PluginProbe
Gutenberg / 24.1.0
Gutenberg v24.1.0
24.1.0 24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 All 404 releases
← All changes | build/scripts/upload-media/index.js +103 -12 24.0.0 → 24.1.0 View file →
@@ -107,8 +107,9 @@
107 107 getHeicConversionAdvice: () => getHeicConversionAdvice,
108 108 getHeicUnsupportedMessage: () => getHeicUnsupportedMessage,
109 109 isClientSideMediaSupported: () => isClientSideMediaSupported,
110 110 isHeicCanvasSupported: () => isHeicCanvasSupported,
111 + isHeicFile: () => isHeicFile,
111 112 store: () => store
112 113 });
113 114
114 115 // packages/upload-media/build-module/store/index.mjs
@@ -259,9 +260,20 @@
259 260 ...item,
260 261 status: ItemStatus.Processing,
261 262 error: void 0,
262 263 retryCount: (item.retryCount ?? 0) + 1,
263 - abortController: new AbortController()
264 + abortController: new AbortController(),
265 + /*
266 + * The failed operation is still recorded on
267 + * the item: nothing finishes it when it
268 + * fails, and while the item waits out the
269 + * backoff that is what keeps it out of the
270 + * concurrency pools. Clear it now that the
271 + * item is about to run again, so processItem
272 + * does not mistake it for an operation still
273 + * in flight and skip the retry.
274 + */
275 + currentOperation: void 0
264 276 } : item
265 277 )
266 278 };
267 279 case Type.ScheduleRetry:
@@ -665,8 +677,56 @@
665 677 }
666 678 }
667 679 return false;
668 680 }
681 + var FILE_TYPE_BOX_BYTES = 64;
682 + var HEIC_BRANDS = [
683 + "heic",
684 + "heix",
685 + "heim",
686 + "heis",
687 + "hevc",
688 + "hevx",
689 + "hevm",
690 + "hevs",
691 + "mif1",
692 + "msf1"
693 + ];
694 + var AVIF_BRANDS = ["avif", "avis"];
695 + function isHeicBuffer(buffer) {
696 + const view = new Uint8Array(buffer);
697 + const readBrand = (offset) => String.fromCharCode(...view.subarray(offset, offset + 4));
698 + if (view.length < 12 || readBrand(4) !== "ftyp") {
699 + return false;
700 + }
701 + const boxEnd = Math.min(
702 + view.length,
703 + new DataView(buffer).getUint32(0)
704 + );
705 + const brands = [readBrand(8)];
706 + for (let offset = 16; offset + 4 <= boxEnd; offset += 4) {
707 + brands.push(readBrand(offset));
708 + }
709 + if (brands.some((brand) => AVIF_BRANDS.includes(brand))) {
710 + return false;
711 + }
712 + return brands.some((brand) => HEIC_BRANDS.includes(brand));
713 + }
714 + async function isHeicFile(file) {
715 + if (HEIC_MIME_TYPES.includes(file.type)) {
716 + return true;
717 + }
718 + if (file.type && !file.type.startsWith("image/")) {
719 + return false;
720 + }
721 + try {
722 + return isHeicBuffer(
723 + await file.slice(0, FILE_TYPE_BOX_BYTES).arrayBuffer()
724 + );
725 + } catch {
726 + return false;
727 + }
728 + }
669 729
670 730 // packages/upload-media/build-module/store/utils/index.mjs
671 731 var vipsModulePromise;
672 732 var vipsModule;
@@ -2073,8 +2133,9 @@
2073 2133 return `${cause} ${getHeicConversionAdvice()}`;
2074 2134 }
2075 2135
2076 2136 // packages/upload-media/build-module/canvas-utils.mjs
2137 + var IMAGE_DECODER_TIMEOUT = 15e3;
2077 2138 var HeicUnsupportedError = class extends Error {
2078 2139 };
2079 2140 async function canvasConvertToJpeg(file, quality = 0.82) {
2080 2141 const baseName = getFileBasename(file.name);
@@ -2105,10 +2166,36 @@
2105 2166 const decoder = new ImageDecoder({
2106 2167 type: file.type,
2107 2168 data: file.stream()
2108 2169 });
2170 + let videoFrame;
2171 + let timeoutId;
2172 + let timedOut = false;
2109 2173 try {
2110 - const { image: videoFrame } = await decoder.decode();
2174 + const decoded = await Promise.race([
2175 + decoder.decode(),
2176 + new Promise((_resolve, reject) => {
2177 + timeoutId = setTimeout(() => {
2178 + timedOut = true;
2179 + decoder.close();
2180 + reject(
2181 + new Error("ImageDecoder decode timed out")
2182 + );
2183 + }, IMAGE_DECODER_TIMEOUT);
2184 + })
2185 + ]);
2186 + videoFrame = decoded.image;
2187 + } catch (error) {
2188 + if (!timedOut) {
2189 + throw error;
2190 + }
2191 + } finally {
2192 + clearTimeout(timeoutId);
2193 + if (!timedOut) {
2194 + decoder.close();
2195 + }
2196 + }
2197 + if (videoFrame) {
2111 2198 try {
2112 2199 const canvas = new OffscreenCanvas(
2113 2200 videoFrame.displayWidth,
2114 2201 videoFrame.displayHeight
@@ -2127,10 +2214,8 @@
2127 2214 });
2128 2215 } finally {
2129 2216 videoFrame.close();
2130 2217 }
2131 - } finally {
2132 - decoder.close();
2133 2218 }
2134 2219 }
2135 2220 }
2136 2221 let heicData;
@@ -2554,8 +2639,11 @@
2554 2639 const item = select2.getItem(id);
2555 2640 if (!item) {
2556 2641 return;
2557 2642 }
2643 + if (item.currentOperation) {
2644 + return;
2645 + }
2558 2646 const {
2559 2647 attachment,
2560 2648 onChange,
2561 2649 onSuccess,
@@ -2833,13 +2921,16 @@
2833 2921 }
2834 2922 }
2835 2923 }
2836 2924 let heicJpeg = null;
2837 - const isImage = file.type.startsWith("image/");
2838 - const isVipsSupported = CLIENT_SIDE_SUPPORTED_MIME_TYPES.includes(
2839 - file.type
2840 - );
2841 - const isHeic = HEIC_MIME_TYPES.includes(file.type);
2925 + const isHeic = await isHeicFile(file);
2926 + const isMisnamedHeic = isHeic && !HEIC_MIME_TYPES.includes(file.type);
2927 + const isImage = file.type.startsWith("image/") || isHeic;
2928 + const isVipsSupported = !isHeic && CLIENT_SIDE_SUPPORTED_MIME_TYPES.includes(file.type);
2929 + const heicFile = isMisnamedHeic ? new File([file], `${getFileBasename(file.name)}.heic`, {
2930 + type: HEIC_MIME_TYPES[0],
2931 + lastModified: file.lastModified
2932 + }) : file;
2842 2933 let tooLargeForClient = false;
2843 2934 if (isImage && isVipsSupported) {
2844 2935 const dimensions = await getImageDimensions(file);
2845 2936 if (dimensions && exceedsClientProcessingMemory(dimensions)) {
@@ -2845,9 +2936,9 @@
2845 2936 if (dimensions && exceedsClientProcessingMemory(dimensions)) {
2846 2937 tooLargeForClient = true;
2847 2938 }
2848 2939 }
2849 - if (file.type === "image/jpeg" && !tooLargeForClient) {
2940 + if (file.type === "image/jpeg" && !isHeic && !tooLargeForClient) {
2850 2941 operations.push(OperationType.DetectUltraHdr);
2851 2942 }
2852 2943 if (isImage && isVipsSupported && !tooLargeForClient) {
2853 2944 operations.push(
@@ -2857,9 +2948,9 @@
2857 2948 );
2858 2949 } else if (isImage && isHeic) {
2859 2950 try {
2860 2951 heicJpeg = await canvasConvertToJpeg(
2861 - file,
2952 + heicFile,
2862 2953 settings.imageQuality ?? DEFAULT_OUTPUT_QUALITY
2863 2954 );
2864 2955 } catch (error) {
2865 2956 const unsupported = error instanceof HeicUnsupportedError;
@@ -2894,9 +2985,9 @@
2894 2985 const vipsAvailable = isClientSideMediaSupported();
2895 2986 updates = {
2896 2987 file: heicJpeg,
2897 2988 sourceFile: heicJpeg,
2898 - originalHeicFile: item.file,
2989 + originalHeicFile: heicFile,
2899 2990 additionalData: {
2900 2991 ...item.additionalData,
2901 2992 generate_sub_sizes: !vipsAvailable,
2902 2993 convert_format: true