@@ -61,9 +61,7 @@ type ExternalImageTool =
6161export const IMAGE_REDUCE_QUALITY_STEPS = [ 85 , 75 , 65 , 55 , 45 , 35 ] as const ;
6262export const MAX_IMAGE_INPUT_PIXELS = 25_000_000 ;
6363const IMAGE_PROCESS_TIMEOUT_MS = 20_000 ;
64- const IMAGE_METADATA_TIMEOUT_MS = 10_000 ;
6564const IMAGE_TOOL_MAX_BUFFER = 1024 * 1024 ;
66- const IMAGE_METADATA_MAX_BUFFER = 512 * 1024 ;
6765const MEDIA_UNDERSTANDING_CORE_PLUGIN_ID = "media-understanding-core" ;
6866const MEDIA_UNDERSTANDING_CORE_IMAGE_OPS_ARTIFACT = "image-ops.js" ;
6967
@@ -207,6 +205,7 @@ function isImageBackendUnavailableCause(error: unknown): boolean {
207205 detail . includes ( 'cannot find package "sharp"' ) ||
208206 detail . includes ( "cannot find module 'sharp'" ) ||
209207 detail . includes ( 'cannot find module "sharp"' ) ||
208+ detail . includes ( "support for this compression format has not been built in" ) ||
210209 detail . includes ( "is not available" ) ||
211210 detail . includes ( "command not found" ) ||
212211 detail . includes ( "enoent" )
@@ -223,12 +222,11 @@ async function runWithImageBackends<T>(
223222 return await fn ( backend ) ;
224223 } catch ( error ) {
225224 errors . push ( error ) ;
225+ if ( ! isImageBackendUnavailableCause ( error ) ) {
226+ throw error ;
227+ }
226228 }
227229 }
228- const processingError = errors . find ( ( error ) => ! isImageBackendUnavailableCause ( error ) ) ;
229- if ( processingError ) {
230- throw processingError ;
231- }
232230 throw createImageProcessorUnavailableError ( operation , errors ) ;
233231}
234232
@@ -768,18 +766,6 @@ async function runPowerShellImageScript(
768766 } ) ;
769767}
770768
771- const WINDOWS_NATIVE_METADATA_SCRIPT = `
772- param([string]$InputPath)
773- $ErrorActionPreference = 'Stop'
774- Add-Type -AssemblyName System.Drawing
775- $image = [System.Drawing.Image]::FromFile($InputPath)
776- try {
777- [Console]::Out.WriteLine(('{0} {1}' -f $image.Width, $image.Height))
778- } finally {
779- $image.Dispose()
780- }
781- ` ;
782-
783769const WINDOWS_NATIVE_RESIZE_SCRIPT = `
784770param(
785771 [string]$InputPath,
@@ -858,22 +844,6 @@ try {
858844}
859845` ;
860846
861- async function windowsNativeMetadataFromBuffer ( buffer : Buffer ) : Promise < ImageMetadata | null > {
862- return await withImageTemp ( async ( workspace ) => {
863- const input = await workspace . write ( "in.img" , buffer ) ;
864- const { stdout } = await runPowerShellImageScript (
865- "metadata.ps1" ,
866- WINDOWS_NATIVE_METADATA_SCRIPT ,
867- [ input ] ,
868- ) ;
869- const [ widthRaw , heightRaw ] = stdout . trim ( ) . split ( / \s + / , 2 ) ;
870- return buildImageMetadata (
871- Number . parseInt ( widthRaw ?? "" , 10 ) ,
872- Number . parseInt ( heightRaw ?? "" , 10 ) ,
873- ) ;
874- } ) ;
875- }
876-
877847async function windowsNativeResize (
878848 params : ResizeToJpegParams | ResizeToPngParams ,
879849 format : "jpeg" | "png" ,
@@ -904,51 +874,6 @@ async function runConvertTool(
904874 } ) ;
905875}
906876
907- async function metadataFromIdentifyTool (
908- tool : Extract < ExternalImageTool , { flavor : "magick" | "convert" | "gm" } > ,
909- buffer : Buffer ,
910- ) : Promise < ImageMetadata | null > {
911- return await withImageTemp ( async ( workspace ) => {
912- const input = await workspace . write ( "in.img" , buffer ) ;
913- const command =
914- tool . flavor === "convert"
915- ? resolveSystemBin ( "identify" , { trust : "standard" } )
916- : tool . command ;
917- if ( ! command ) {
918- return null ;
919- }
920- const args = tool . flavor === "magick" ? [ "identify" ] : tool . flavor === "gm" ? [ "identify" ] : [ ] ;
921- const { stdout } = await runExec ( command , [ ...args , "-format" , "%w %h" , input ] , {
922- timeoutMs : IMAGE_METADATA_TIMEOUT_MS ,
923- maxBuffer : IMAGE_METADATA_MAX_BUFFER ,
924- } ) ;
925- const [ widthRaw , heightRaw ] = stdout . trim ( ) . split ( / \s + / , 2 ) ;
926- const width = Number . parseInt ( widthRaw ?? "" , 10 ) ;
927- const height = Number . parseInt ( heightRaw ?? "" , 10 ) ;
928- return buildImageMetadata ( width , height ) ;
929- } ) ;
930- }
931-
932- async function externalMetadataFromBuffer (
933- backend : Exclude < ImageBackend , "sharp" > ,
934- buffer : Buffer ,
935- ) : Promise < ImageMetadata | null > {
936- const tool = resolveImageTool ( backend ) ;
937- if ( ! tool ) {
938- throw new Error ( `Image backend ${ backend } is not available` ) ;
939- }
940- if ( tool . flavor === "sips" ) {
941- return await sipsMetadataFromBuffer ( buffer ) ;
942- }
943- if ( tool . flavor === "powershell" ) {
944- return await windowsNativeMetadataFromBuffer ( buffer ) ;
945- }
946- if ( tool . flavor === "ffmpeg" ) {
947- return null ;
948- }
949- return await metadataFromIdentifyTool ( tool , buffer ) ;
950- }
951-
952877function buildResizeGeometry ( maxSide : number , withoutEnlargement ?: boolean ) : string {
953878 const side = clampInteger ( maxSide , 1 , Number . MAX_SAFE_INTEGER ) ;
954879 return `${ side } x${ side } ${ withoutEnlargement === false ? "" : ">" } ` ;
@@ -1114,34 +1039,6 @@ async function externalResizeToPng(
11141039 } ) ;
11151040}
11161041
1117- async function sipsMetadataFromBuffer ( buffer : Buffer ) : Promise < ImageMetadata | null > {
1118- return await withImageTemp ( async ( workspace ) => {
1119- const input = await workspace . write ( "in.img" , buffer ) ;
1120- const { stdout } = await runExec (
1121- "/usr/bin/sips" ,
1122- [ "-g" , "pixelWidth" , "-g" , "pixelHeight" , input ] ,
1123- {
1124- timeoutMs : IMAGE_METADATA_TIMEOUT_MS ,
1125- maxBuffer : IMAGE_METADATA_MAX_BUFFER ,
1126- } ,
1127- ) ;
1128- const w = stdout . match ( / p i x e l W i d t h : \s * ( [ 0 - 9 ] + ) / ) ;
1129- const h = stdout . match ( / p i x e l H e i g h t : \s * ( [ 0 - 9 ] + ) / ) ;
1130- if ( ! w ?. [ 1 ] || ! h ?. [ 1 ] ) {
1131- return null ;
1132- }
1133- const width = Number . parseInt ( w [ 1 ] , 10 ) ;
1134- const height = Number . parseInt ( h [ 1 ] , 10 ) ;
1135- if ( ! Number . isFinite ( width ) || ! Number . isFinite ( height ) ) {
1136- return null ;
1137- }
1138- if ( width <= 0 || height <= 0 ) {
1139- return null ;
1140- }
1141- return { width, height } ;
1142- } ) ;
1143- }
1144-
11451042async function sipsResizeToJpeg ( params : {
11461043 buffer : Buffer ;
11471044 maxSide : number ;
@@ -1193,13 +1090,15 @@ export async function getImageMetadata(buffer: Buffer): Promise<ImageMetadata |
11931090 }
11941091 }
11951092
1196- return await runWithImageBackends ( "metadata" , async ( backend ) => {
1197- const meta =
1198- backend === "sharp"
1199- ? await ( await loadMediaAttachmentImageOps ( ) ) . getImageMetadata ( buffer )
1200- : await externalMetadataFromBuffer ( backend , buffer ) ;
1093+ const preference = getImageBackendPreference ( ) ;
1094+ if ( preference !== "auto" && preference !== "sharp" ) {
1095+ return null ;
1096+ }
1097+
1098+ return await ( async ( ) => {
1099+ const meta = await ( await loadMediaAttachmentImageOps ( ) ) . getImageMetadata ( buffer ) ;
12011100 return meta ? validateImagePixelLimit ( meta ) : null ;
1202- } ) . catch ( ( ) => null ) ;
1101+ } ) ( ) . catch ( ( ) => null ) ;
12031102}
12041103
12051104/**
0 commit comments