PluginProbe
Gutenberg / trunk
Gutenberg vtrunk
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 +1487 -167 23.1.1 → trunk View file →
@@ -1,4 +1,5 @@
1 +(function() {
1 2 "use strict";
2 3 var wp;
3 4 (wp ||= {}).uploadMedia = (() => {
4 5 var __create = Object.create;
@@ -44,8 +45,15 @@
44 45 module.exports = window.wp.data;
45 46 }
46 47 });
47 48
49 + // package-external:@wordpress/i18n
50 + var require_i18n = __commonJS({
51 + "package-external:@wordpress/i18n"(exports, module) {
52 + module.exports = window.wp.i18n;
53 + }
54 + });
55 +
48 56 // package-external:@wordpress/url
49 57 var require_url = __commonJS({
50 58 "package-external:@wordpress/url"(exports, module) {
51 59 module.exports = window.wp.url;
@@ -51,15 +59,8 @@
51 59 module.exports = window.wp.url;
52 60 }
53 61 });
54 62
55 - // package-external:@wordpress/i18n
56 - var require_i18n = __commonJS({
57 - "package-external:@wordpress/i18n"(exports, module) {
58 - module.exports = window.wp.i18n;
59 - }
60 - });
61 -
62 63 // package-external:@wordpress/blob
63 64 var require_blob = __commonJS({
64 65 "package-external:@wordpress/blob"(exports, module) {
65 66 module.exports = window.wp.blob;
@@ -96,14 +97,19 @@
96 97
97 98 // packages/upload-media/build-module/index.mjs
98 99 var index_exports = {};
99 100 __export(index_exports, {
101 + ErrorCode: () => ErrorCode,
100 102 MediaUploadProvider: () => provider_default,
101 103 UploadError: () => UploadError,
102 104 clearFeatureDetectionCache: () => clearFeatureDetectionCache,
103 105 detectClientSideMediaSupport: () => detectClientSideMediaSupport,
106 + getErrorMessage: () => getErrorMessage,
107 + getHeicConversionAdvice: () => getHeicConversionAdvice,
108 + getHeicUnsupportedMessage: () => getHeicUnsupportedMessage,
104 109 isClientSideMediaSupported: () => isClientSideMediaSupported,
105 110 isHeicCanvasSupported: () => isHeicCanvasSupported,
111 + isHeicFile: () => isHeicFile,
106 112 store: () => store
107 113 });
108 114
109 115 // packages/upload-media/build-module/store/index.mjs
@@ -116,8 +122,9 @@
116 122 Type2["Prepare"] = "PREPARE_ITEM";
117 123 Type2["Cancel"] = "CANCEL_ITEM";
118 124 Type2["Remove"] = "REMOVE_ITEM";
119 125 Type2["RetryItem"] = "RETRY_ITEM";
126 + Type2["ScheduleRetry"] = "SCHEDULE_RETRY";
120 127 Type2["PauseItem"] = "PAUSE_ITEM";
121 128 Type2["ResumeItem"] = "RESUME_ITEM";
122 129 Type2["PauseQueue"] = "PAUSE_QUEUE";
123 130 Type2["ResumeQueue"] = "RESUME_QUEUE";
@@ -134,8 +141,9 @@
134 141 var ItemStatus = /* @__PURE__ */ ((ItemStatus2) => {
135 142 ItemStatus2["Queued"] = "QUEUED";
136 143 ItemStatus2["Processing"] = "PROCESSING";
137 144 ItemStatus2["Paused"] = "PAUSED";
145 + ItemStatus2["PendingRetry"] = "PENDING_RETRY";
138 146 ItemStatus2["Uploaded"] = "UPLOADED";
139 147 ItemStatus2["Error"] = "ERROR";
140 148 return ItemStatus2;
141 149 })(ItemStatus || {});
@@ -144,10 +152,12 @@
144 152 OperationType2["Upload"] = "UPLOAD";
145 153 OperationType2["ResizeCrop"] = "RESIZE_CROP";
146 154 OperationType2["Rotate"] = "ROTATE";
147 155 OperationType2["TranscodeImage"] = "TRANSCODE_IMAGE";
156 + OperationType2["TranscodeGif"] = "TRANSCODE_GIF";
148 157 OperationType2["ThumbnailGeneration"] = "THUMBNAIL_GENERATION";
149 158 OperationType2["Finalize"] = "FINALIZE";
159 + OperationType2["DetectUltraHdr"] = "DETECT_ULTRAHDR";
150 160 return OperationType2;
151 161 })(OperationType || {});
152 162
153 163 // packages/upload-media/build-module/store/constants.mjs
@@ -153,8 +163,15 @@
153 163 // packages/upload-media/build-module/store/constants.mjs
154 164 var STORE_NAME = "core/upload-media";
155 165 var DEFAULT_MAX_CONCURRENT_UPLOADS = 5;
156 166 var DEFAULT_MAX_CONCURRENT_IMAGE_PROCESSING = 2;
167 + var DEFAULT_RETRY_SETTINGS = {
168 + maxRetryAttempts: 3,
169 + initialRetryDelayMs: 1e3,
170 + maxRetryDelayMs: 3e4,
171 + backoffMultiplier: 2,
172 + retryJitter: 0.1
173 + };
157 174 var CLIENT_SIDE_SUPPORTED_MIME_TYPES = [
158 175 "image/jpeg",
159 176 "image/png",
160 177 "image/gif",
@@ -175,10 +192,12 @@
175 192 blobUrls: {},
176 193 settings: {
177 194 mediaUpload: noop,
178 195 maxConcurrentUploads: DEFAULT_MAX_CONCURRENT_UPLOADS,
179 - maxConcurrentImageProcessing: DEFAULT_MAX_CONCURRENT_IMAGE_PROCESSING
180 - }
196 + maxConcurrentImageProcessing: DEFAULT_MAX_CONCURRENT_IMAGE_PROCESSING,
197 + retry: { ...DEFAULT_RETRY_SETTINGS }
198 + },
199 + failureCount: 0
181 200 };
182 201 function reducer(state = DEFAULT_STATE, action = { type: Type.Unknown }) {
183 202 switch (action.type) {
184 203 case Type.PauseQueue: {
@@ -217,11 +236,15 @@
217 236 return {
218 237 ...state,
219 238 queue: [...state.queue, action.item]
220 239 };
221 - case Type.Cancel:
240 + case Type.Cancel: {
241 + const cancelled = state.queue.find(
242 + (item) => item.id === action.id
243 + );
222 244 return {
223 245 ...state,
246 + failureCount: cancelled && !cancelled.parentId ? state.failureCount + 1 : state.failureCount,
224 247 queue: state.queue.map(
225 248 (item) => item.id === action.id ? {
226 249 ...item,
227 250 error: action.error
@@ -227,8 +250,9 @@
227 250 error: action.error
228 251 } : item
229 252 )
230 253 };
254 + }
231 255 case Type.RetryItem:
232 256 return {
233 257 ...state,
234 258 queue: state.queue.map(
@@ -235,12 +259,37 @@
235 259 (item) => item.id === action.id ? {
236 260 ...item,
237 261 status: ItemStatus.Processing,
238 262 error: void 0,
239 - retryCount: (item.retryCount ?? 0) + 1
263 + retryCount: (item.retryCount ?? 0) + 1,
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
240 276 } : item
241 277 )
242 278 };
279 + case Type.ScheduleRetry:
280 + return {
281 + ...state,
282 + queue: state.queue.map(
283 + (item) => item.id === action.id ? {
284 + ...item,
285 + status: ItemStatus.PendingRetry,
286 + error: action.error,
287 + retryCount: action.retryCount,
288 + nextRetryTimestamp: action.nextRetryTimestamp
289 + } : item
290 + )
291 + };
243 292 case Type.Remove:
244 293 return {
245 294 ...state,
246 295 queue: state.queue.filter((item) => item.id !== action.id)
@@ -385,15 +434,18 @@
385 434 var private_selectors_exports = {};
386 435 __export(private_selectors_exports, {
387 436 getActiveImageProcessingCount: () => getActiveImageProcessingCount,
388 437 getActiveUploadCount: () => getActiveUploadCount,
438 + getActiveVideoProcessingCount: () => getActiveVideoProcessingCount,
389 439 getAllItems: () => getAllItems,
390 440 getBlobUrls: () => getBlobUrls,
391 441 getFailedItems: () => getFailedItems,
442 + getFailureCount: () => getFailureCount,
392 443 getItem: () => getItem,
393 444 getItemProgress: () => getItemProgress,
394 445 getPendingImageProcessing: () => getPendingImageProcessing,
395 446 getPendingUploads: () => getPendingUploads,
447 + getPendingVideoProcessing: () => getPendingVideoProcessing,
396 448 hasPendingItemsByParentId: () => hasPendingItemsByParentId,
397 449 isBatchUploaded: () => isBatchUploaded,
398 450 isPaused: () => isPaused
399 451 });
@@ -399,8 +451,11 @@
399 451 });
400 452 function getAllItems(state) {
401 453 return state.queue;
402 454 }
455 + function getFailureCount(state) {
456 + return state.failureCount;
457 + }
403 458 function getItem(state, id) {
404 459 return state.queue.find((item) => item.id === id);
405 460 }
406 461 function isBatchUploaded(state, batchId) {
@@ -430,8 +485,13 @@
430 485 return state.queue.filter(
431 486 (item) => item.currentOperation === OperationType.ResizeCrop || item.currentOperation === OperationType.Rotate
432 487 ).length;
433 488 }
489 + function getActiveVideoProcessingCount(state) {
490 + return state.queue.filter(
491 + (item) => item.currentOperation === OperationType.TranscodeGif
492 + ).length;
493 + }
434 494 function getPendingImageProcessing(state) {
435 495 return state.queue.filter((item) => {
436 496 const nextOperation = Array.isArray(item.operations?.[0]) ? item.operations[0][0] : item.operations?.[0];
437 497 return (nextOperation === OperationType.ResizeCrop || nextOperation === OperationType.Rotate) && item.currentOperation !== OperationType.ResizeCrop && item.currentOperation !== OperationType.Rotate;
@@ -436,8 +496,14 @@
436 496 const nextOperation = Array.isArray(item.operations?.[0]) ? item.operations[0][0] : item.operations?.[0];
437 497 return (nextOperation === OperationType.ResizeCrop || nextOperation === OperationType.Rotate) && item.currentOperation !== OperationType.ResizeCrop && item.currentOperation !== OperationType.Rotate;
438 498 });
439 499 }
500 + function getPendingVideoProcessing(state) {
501 + return state.queue.filter((item) => {
502 + const nextOperation = Array.isArray(item.operations?.[0]) ? item.operations[0][0] : item.operations?.[0];
503 + return nextOperation === OperationType.TranscodeGif && item.currentOperation !== OperationType.TranscodeGif;
504 + });
505 + }
440 506 function getFailedItems(state) {
441 507 return state.queue.filter((item) => item.error !== void 0);
442 508 }
443 509 function hasPendingItemsByParentId(state, parentId) {
@@ -452,50 +518,48 @@
452 518 var actions_exports = {};
453 519 __export(actions_exports, {
454 520 addItems: () => addItems,
455 521 cancelItem: () => cancelItem,
456 - retryItem: () => retryItem
522 + executeRetry: () => executeRetry,
523 + retryItem: () => retryItem,
524 + scheduleRetry: () => scheduleRetry
457 525 });
458 526
459 - // node_modules/uuid/dist/esm-browser/rng.js
460 - var getRandomValues;
461 - var rnds8 = new Uint8Array(16);
462 - function rng() {
463 - if (!getRandomValues) {
464 - getRandomValues = typeof crypto !== "undefined" && crypto.getRandomValues && crypto.getRandomValues.bind(crypto);
465 - if (!getRandomValues) {
466 - throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");
467 - }
468 - }
469 - return getRandomValues(rnds8);
470 - }
471 -
472 - // node_modules/uuid/dist/esm-browser/stringify.js
527 + // node_modules/uuid/dist/stringify.js
473 528 var byteToHex = [];
474 529 for (let i = 0; i < 256; ++i) {
475 530 byteToHex.push((i + 256).toString(16).slice(1));
476 531 }
477 532 function unsafeStringify(arr, offset = 0) {
478 - return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
533 + return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
479 534 }
480 535
481 - // node_modules/uuid/dist/esm-browser/native.js
482 - var randomUUID = typeof crypto !== "undefined" && crypto.randomUUID && crypto.randomUUID.bind(crypto);
483 - var native_default = {
484 - randomUUID
485 - };
536 + // node_modules/uuid/dist/rng.js
537 + var rnds8 = new Uint8Array(16);
538 + function rng() {
539 + return crypto.getRandomValues(rnds8);
540 + }
486 541
487 - // node_modules/uuid/dist/esm-browser/v4.js
542 + // node_modules/uuid/dist/v4.js
488 543 function v4(options, buf, offset) {
489 - if (native_default.randomUUID && !buf && !options) {
490 - return native_default.randomUUID();
544 + if (!buf && !options && crypto.randomUUID) {
545 + return crypto.randomUUID();
491 546 }
547 + return _v4(options, buf, offset);
548 + }
549 + function _v4(options, buf, offset) {
492 550 options = options || {};
493 - const rnds = options.random || (options.rng || rng)();
551 + const rnds = options.random ?? options.rng?.() ?? rng();
552 + if (rnds.length < 16) {
553 + throw new Error("Random bytes length must be >= 16");
554 + }
494 555 rnds[6] = rnds[6] & 15 | 64;
495 556 rnds[8] = rnds[8] & 63 | 128;
496 557 if (buf) {
497 558 offset = offset || 0;
559 + if (offset < 0 || offset + 16 > buf.length) {
560 + throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
561 + }
498 562 for (let i = 0; i < 16; ++i) {
499 563 buf[offset + i] = rnds[i];
500 564 }
501 565 return buf;
@@ -503,8 +567,50 @@
503 567 return unsafeStringify(rnds);
504 568 }
505 569 var v4_default = v4;
506 570
571 + // packages/upload-media/build-module/store/actions.mjs
572 + var import_i18n5 = __toESM(require_i18n(), 1);
573 +
574 + // packages/upload-media/build-module/store/utils/retry.mjs
575 + function calculateRetryDelay(options) {
576 + const { attempt, initialDelay, maxDelay, multiplier, jitter } = options;
577 + const exponentialDelay = initialDelay * Math.pow(multiplier, attempt - 1);
578 + const cappedDelay = Math.min(exponentialDelay, maxDelay);
579 + const jitterFactor = 1 + (Math.random() * 2 - 1) * jitter;
580 + return Math.floor(cappedDelay * jitterFactor);
581 + }
582 + var RETRYABLE_MESSAGE_PATTERNS = [
583 + /network/i,
584 + /timeout/i,
585 + /ECONNRESET/i,
586 + /fetch failed/i,
587 + /connection/i,
588 + /socket/i,
589 + /ETIMEDOUT/i,
590 + /ENOTFOUND/i,
591 + /Could not get a valid response/i,
592 + /Failed to fetch/i,
593 + /Load failed/i
594 + ];
595 + function shouldRetryError(error, retryCount, maxRetries) {
596 + if (retryCount >= maxRetries) {
597 + return false;
598 + }
599 + const message = typeof error === "string" ? error : error?.message || "";
600 + return RETRYABLE_MESSAGE_PATTERNS.some(
601 + (pattern) => pattern.test(message)
602 + );
603 + }
604 + var retryTimers = /* @__PURE__ */ new Map();
605 + function clearRetryTimer(id) {
606 + const pendingTimer = retryTimers.get(id);
607 + if (pendingTimer !== void 0) {
608 + clearTimeout(pendingTimer);
609 + retryTimers.delete(id);
610 + }
611 + }
612 +
507 613 // packages/upload-media/build-module/image-file.mjs
508 614 var ImageFile = class extends File {
509 615 width = 0;
510 616 height = 0;
@@ -555,8 +661,72 @@
555 661 }
556 662 function getFileBasename(name) {
557 663 return name.includes(".") ? name.split(".").slice(0, -1).join(".") : name;
558 664 }
665 + function isAnimatedGif(buffer) {
666 + const view = new Uint8Array(buffer);
667 + if (view.length < 4 || view[0] !== 71 || view[1] !== 73 || view[2] !== 70 || view[3] !== 56) {
668 + return false;
669 + }
670 + let frameCount = 0;
671 + for (let i = 0; i < view.length - 2; i++) {
672 + if (view[i] === 0 && view[i + 1] === 33 && view[i + 2] === 249) {
673 + frameCount++;
674 + if (frameCount > 1) {
675 + return true;
676 + }
677 + }
678 + }
679 + return false;
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 + }
559 729
560 730 // packages/upload-media/build-module/store/utils/index.mjs
561 731 var vipsModulePromise;
562 732 var vipsModule;
@@ -570,9 +740,9 @@
570 740 );
571 741 }
572 742 return vipsModulePromise;
573 743 }
574 - async function vipsConvertImageFormat(id, file, type, quality, interlaced) {
744 + async function vipsConvertImageFormat(id, file, type, options = {}) {
575 745 const { vipsConvertImageFormat: convertImageFormat } = await loadVipsModule();
576 746 const buffer = await convertImageFormat(
577 747 id,
578 748 await file.arrayBuffer(),
@@ -577,10 +747,9 @@
577 747 id,
578 748 await file.arrayBuffer(),
579 749 file.type,
580 750 type,
581 - quality,
582 - interlaced
751 + options
583 752 );
584 753 const ext = type.split("/")[1];
585 754 const fileName = `${getFileBasename(file.name)}.${ext}`;
586 755 return new File([new Blob([buffer])], fileName, {
@@ -594,21 +763,32 @@
594 763 throw new Error(`Failed to fetch image: ${response.status}`);
595 764 }
596 765 return hasTransparency(await response.arrayBuffer());
597 766 }
598 - async function vipsResizeImage(id, file, resize, smartCrop, addSuffix, signal, scaledSuffix, quality) {
767 + async function vipsGetUltraHdrInfo(buffer) {
768 + const { vipsGetUltraHdrInfo: getUltraHdrInfo } = await loadVipsModule();
769 + return getUltraHdrInfo(buffer);
770 + }
771 + async function vipsResizeImage(id, file, resize, options = {}) {
772 + const {
773 + smartCrop = false,
774 + addSuffix = false,
775 + signal,
776 + scaledSuffix,
777 + quality,
778 + stripMeta,
779 + maxBitdepth
780 + } = options;
599 781 if (signal?.aborted) {
600 782 throw new Error("Operation aborted");
601 783 }
602 784 const { vipsResizeImage: resizeImage } = await loadVipsModule();
603 - const { buffer, width, height, originalWidth, originalHeight } = await resizeImage(
604 - id,
605 - await file.arrayBuffer(),
606 - file.type,
607 - resize,
785 + const { buffer, width, height, originalWidth, originalHeight } = await resizeImage(id, await file.arrayBuffer(), file.type, resize, {
608 786 smartCrop,
609 - quality
610 - );
787 + quality,
788 + stripMeta,
789 + maxBitdepth
790 + });
611 791 let fileName = file.name;
612 792 const wasResized = originalWidth > width || originalHeight > height;
613 793 if (wasResized) {
614 794 const basename = getFileBasename(file.name);
@@ -675,13 +855,172 @@
675 855 if (vipsModule) {
676 856 vipsModule.terminateVipsWorker();
677 857 }
678 858 }
859 + var completedVipsOperations = 0;
860 + var MAX_VIPS_OPS_BEFORE_RECYCLE = 50;
861 + function maybeRecycleVipsWorker(activeImageProcessingCount) {
862 + completedVipsOperations++;
863 + if (completedVipsOperations >= MAX_VIPS_OPS_BEFORE_RECYCLE && activeImageProcessingCount === 0) {
864 + terminateVipsWorker();
865 + completedVipsOperations = 0;
866 + }
867 + }
679 868
680 - // packages/upload-media/build-module/validate-mime-type.mjs
681 - var import_i18n2 = __toESM(require_i18n(), 1);
869 + // packages/upload-media/build-module/store/utils/video-conversion.mjs
870 + var UNSUPPORTED_ERROR_PREFIX = "Unsupported";
871 + var SIZE_LIMIT_ERROR_PREFIX = `${UNSUPPORTED_ERROR_PREFIX}: GIF exceeds maximum conversion size`;
872 + var CONVERSION_TIMEOUT_ERROR_PREFIX = "GIF to video conversion timed out";
873 + var DEFAULT_CONVERSION_TIMEOUT = 3e4;
874 + function isUnsupportedConversionError(error) {
875 + return error instanceof Error && error.message.startsWith(UNSUPPORTED_ERROR_PREFIX);
876 + }
877 + function isSizeLimitConversionError(error) {
878 + return error instanceof Error && error.message.startsWith(SIZE_LIMIT_ERROR_PREFIX);
879 + }
880 + function isConversionTimeoutError(error) {
881 + return error instanceof Error && error.message.startsWith(CONVERSION_TIMEOUT_ERROR_PREFIX);
882 + }
883 + var videoConversionModulePromise;
884 + var videoConversionModule;
885 + function loadVideoConversionModule() {
886 + if (!videoConversionModulePromise) {
887 + videoConversionModulePromise = import("@wordpress/video-conversion/worker").then((mod) => {
888 + videoConversionModule = mod;
889 + return mod;
890 + }).catch((error) => {
891 + videoConversionModulePromise = void 0;
892 + throw error;
893 + });
894 + }
895 + return videoConversionModulePromise;
896 + }
897 + async function convertGifToVideo(id, file, outputMimeType, {
898 + maxDimensions,
899 + timeout = DEFAULT_CONVERSION_TIMEOUT,
900 + maxTotalPixels
901 + } = {}) {
902 + const mod = await loadVideoConversionModule();
903 + const conversion = mod.convertGifToVideo(
904 + id,
905 + file,
906 + outputMimeType,
907 + maxDimensions,
908 + maxTotalPixels
909 + );
910 + let buffer;
911 + if (timeout > 0) {
912 + let timer;
913 + try {
914 + buffer = await Promise.race([
915 + conversion,
916 + new Promise((_, reject) => {
917 + timer = setTimeout(() => {
918 + reject(
919 + new Error(
920 + `${CONVERSION_TIMEOUT_ERROR_PREFIX} after ${timeout}ms`
921 + )
922 + );
923 + }, timeout);
924 + })
925 + ]);
926 + } catch (error) {
927 + if (isConversionTimeoutError(error)) {
928 + conversion.catch(() => {
929 + });
930 + mod.cancelGifToVideoOperations(id).catch(() => {
931 + });
932 + }
933 + throw error;
934 + } finally {
935 + clearTimeout(timer);
936 + }
937 + } else {
938 + buffer = await conversion;
939 + }
940 + const ext = outputMimeType === "video/webm" ? "webm" : "mp4";
941 + const fileName = `${getFileBasename(file.name)}.${ext}`;
942 + return new File(
943 + [new Blob([buffer], { type: outputMimeType })],
944 + fileName,
945 + { type: outputMimeType }
946 + );
947 + }
948 + async function cancelGifToVideoOperations(id) {
949 + const mod = videoConversionModule ?? (videoConversionModulePromise ? await videoConversionModulePromise.catch(() => void 0) : void 0);
950 + if (!mod) {
951 + return false;
952 + }
953 + return mod.cancelGifToVideoOperations(id);
954 + }
955 + function terminateVideoConversionWorker() {
956 + if (videoConversionModule) {
957 + videoConversionModule.terminateVideoConversionWorker();
958 + return;
959 + }
960 + if (videoConversionModulePromise) {
961 + void videoConversionModulePromise.then((mod) => mod.terminateVideoConversionWorker()).catch(() => {
962 + });
963 + }
964 + }
682 965
966 + // packages/upload-media/build-module/store/utils/debug-logger.mjs
967 + function isDebugEnabled() {
968 + return true;
969 + }
970 + function debug(message, ...args) {
971 + if (!isDebugEnabled()) {
972 + return;
973 + }
974 + console.debug(`[upload-media] ${message}`, ...args);
975 + }
976 + function measure(options) {
977 + if (!isDebugEnabled()) {
978 + return;
979 + }
980 + const {
981 + measureName,
982 + startTime,
983 + endTime = performance.now(),
984 + tooltipText,
985 + properties
986 + } = options;
987 + const detail = {
988 + devtools: {
989 + dataType: "track-entry",
990 + track: "Upload Media",
991 + tooltipText,
992 + properties: properties?.map(([key, value]) => ({
993 + key,
994 + value
995 + }))
996 + }
997 + };
998 + try {
999 + performance.measure(measureName, {
1000 + start: startTime,
1001 + end: endTime,
1002 + detail
1003 + });
1004 + } catch {
1005 + }
1006 + }
1007 +
683 1008 // packages/upload-media/build-module/upload-error.mjs
1009 + var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
1010 + ErrorCode2["EMPTY_FILE"] = "EMPTY_FILE";
1011 + ErrorCode2["SIZE_ABOVE_LIMIT"] = "SIZE_ABOVE_LIMIT";
1012 + ErrorCode2["MIME_TYPE_NOT_SUPPORTED"] = "MIME_TYPE_NOT_SUPPORTED";
1013 + ErrorCode2["MIME_TYPE_NOT_ALLOWED_FOR_USER"] = "MIME_TYPE_NOT_ALLOWED_FOR_USER";
1014 + ErrorCode2["HEIC_DECODE_ERROR"] = "HEIC_DECODE_ERROR";
1015 + ErrorCode2["IMAGE_TRANSCODING_ERROR"] = "IMAGE_TRANSCODING_ERROR";
1016 + ErrorCode2["IMAGE_ROTATION_ERROR"] = "IMAGE_ROTATION_ERROR";
1017 + ErrorCode2["MEDIA_TRANSCODING_ERROR"] = "MEDIA_TRANSCODING_ERROR";
1018 + ErrorCode2["GIF_TRANSCODING_ERROR"] = "GIF_TRANSCODING_ERROR";
1019 + ErrorCode2["MEDIA_FINALIZE_ERROR"] = "MEDIA_FINALIZE_ERROR";
1020 + ErrorCode2["GENERAL"] = "GENERAL";
1021 + return ErrorCode2;
1022 + })(ErrorCode || {});
684 1023 var UploadError = class extends Error {
685 1024 code;
686 1025 file;
687 1026 constructor({ code, message, file, cause }) {
@@ -692,8 +1031,9 @@
692 1031 }
693 1032 };
694 1033
695 1034 // packages/upload-media/build-module/validate-mime-type.mjs
1035 + var import_i18n2 = __toESM(require_i18n(), 1);
696 1036 function validateMimeType(file, allowedTypes) {
697 1037 if (!allowedTypes) {
698 1038 return;
699 1039 }
@@ -704,9 +1044,9 @@
704 1044 return file.type.startsWith(`${allowedType}/`);
705 1045 });
706 1046 if (file.type && !isAllowedType) {
707 1047 throw new UploadError({
708 - code: "MIME_TYPE_NOT_SUPPORTED",
1048 + code: ErrorCode.MIME_TYPE_NOT_SUPPORTED,
709 1049 message: (0, import_i18n2.sprintf)(
710 1050 // translators: %s: file name.
711 1051 (0, import_i18n2.__)("%s: Sorry, this file type is not supported here."),
712 1052 file.name
@@ -748,9 +1088,9 @@
748 1088 file.type
749 1089 );
750 1090 if (file.type && !isAllowedMimeTypeForUser) {
751 1091 throw new UploadError({
752 - code: "MIME_TYPE_NOT_ALLOWED_FOR_USER",
1092 + code: ErrorCode.MIME_TYPE_NOT_ALLOWED_FOR_USER,
753 1093 message: (0, import_i18n3.sprintf)(
754 1094 // translators: %s: file name.
755 1095 (0, import_i18n3.__)(
756 1096 "%s: Sorry, you are not allowed to upload this file type."
@@ -766,9 +1106,9 @@
766 1106 var import_i18n4 = __toESM(require_i18n(), 1);
767 1107 function validateFileSize(file, maxUploadFileSize) {
768 1108 if (file.size <= 0) {
769 1109 throw new UploadError({
770 - code: "EMPTY_FILE",
1110 + code: ErrorCode.EMPTY_FILE,
771 1111 message: (0, import_i18n4.sprintf)(
772 1112 // translators: %s: file name.
773 1113 (0, import_i18n4.__)("%s: This file is empty."),
774 1114 file.name
@@ -777,9 +1117,9 @@
777 1117 });
778 1118 }
779 1119 if (maxUploadFileSize && file.size > maxUploadFileSize) {
780 1120 throw new UploadError({
781 - code: "SIZE_ABOVE_LIMIT",
1121 + code: ErrorCode.SIZE_ABOVE_LIMIT,
782 1122 message: (0, import_i18n4.sprintf)(
783 1123 // translators: %s: file name.
784 1124 (0, import_i18n4.__)(
785 1125 "%s: This file exceeds the maximum upload size for this site."
@@ -840,17 +1180,38 @@
840 1180 const item = select2.getItem(id);
841 1181 if (!item) {
842 1182 return;
843 1183 }
1184 + clearRetryTimer(id);
1185 + if (!silent && error && !item.parentId && !item.attachment?.id) {
1186 + const settings = select2.getSettings();
1187 + const retrySettings = settings.retry;
1188 + if (retrySettings) {
1189 + const retryCount = item.retryCount ?? 0;
1190 + const maxRetries = retrySettings.maxRetryAttempts;
1191 + if (shouldRetryError(error, retryCount, maxRetries)) {
1192 + dispatch.scheduleRetry(id, error);
1193 + return;
1194 + }
1195 + }
1196 + }
844 1197 item.abortController?.abort();
845 - await vipsCancelOperations(id);
1198 + vipsCancelOperations(id).catch(() => {
1199 + });
1200 + cancelGifToVideoOperations(id).catch(() => {
1201 + });
846 1202 if (!silent) {
847 1203 const { onError } = item;
848 1204 onError?.(error ?? new Error("Upload cancelled"));
849 - if (!onError && error) {
1205 + if (!onError && error && !item.parentId) {
850 1206 console.error("Upload cancelled", error);
851 1207 }
1208 + } else {
1209 + debug(
1210 + `Item cancelled: ${item.file.name} (item ${id}): ${error instanceof Error ? error.message : error}`
1211 + );
852 1212 }
1213 + const { currentOperation, parentId, batchId } = item;
853 1214 dispatch({
854 1215 type: Type.Cancel,
855 1216 id,
856 1217 error
@@ -856,9 +1217,59 @@
856 1217 error
857 1218 });
858 1219 dispatch.removeItem(id);
859 1220 dispatch.revokeBlobUrls(id);
860 - if (item.batchId && select2.isBatchUploaded(item.batchId)) {
1221 + if (currentOperation === OperationType.ResizeCrop || currentOperation === OperationType.Rotate) {
1222 + for (const pending of select2.getPendingImageProcessing()) {
1223 + dispatch.processItem(pending.id);
1224 + }
1225 + }
1226 + if (currentOperation === OperationType.Upload) {
1227 + for (const pending of select2.getPendingUploads()) {
1228 + dispatch.processItem(pending.id);
1229 + }
1230 + }
1231 + if (currentOperation === OperationType.TranscodeGif) {
1232 + for (const pending of select2.getPendingVideoProcessing()) {
1233 + dispatch.processItem(pending.id);
1234 + }
1235 + }
1236 + if (currentOperation === OperationType.ResizeCrop || currentOperation === OperationType.Rotate || currentOperation === OperationType.TranscodeImage) {
1237 + maybeRecycleVipsWorker(select2.getActiveImageProcessingCount());
1238 + }
1239 + if (parentId) {
1240 + const parentItem = select2.getItem(parentId);
1241 + if (parentItem) {
1242 + const isOptionalCompanion = item.additionalData?.image_size === "animated_video" || item.additionalData?.image_size === "animated_video_poster";
1243 + if (select2.hasPendingItemsByParentId(parentId)) {
1244 + if (parentItem.operations && parentItem.operations.length > 0) {
1245 + dispatch.processItem(parentId);
1246 + }
1247 + } else if (parentItem.subSizes && parentItem.subSizes.length > 0 || isOptionalCompanion) {
1248 + if (parentItem.operations && parentItem.operations.length > 0) {
1249 + dispatch.processItem(parentId);
1250 + }
1251 + } else {
1252 + const parentAttachmentId = parentItem.attachment?.id;
1253 + const { mediaDelete } = select2.getSettings();
1254 + if (parentAttachmentId && mediaDelete) {
1255 + mediaDelete(parentAttachmentId).catch(() => {
1256 + });
1257 + }
1258 + await dispatch.cancelItem(
1259 + parentId,
1260 + new UploadError({
1261 + code: error instanceof UploadError && error.code || ErrorCode.GENERAL,
1262 + message: error?.message || (0, import_i18n5.__)("The image could not be uploaded."),
1263 + file: parentItem.file,
1264 + cause: error instanceof Error ? error : void 0
1265 + })
1266 + );
1267 + }
1268 + }
1269 + }
1270 + if (batchId && select2.isBatchUploaded(batchId)) {
1271 + debug(`Batch completed: ${batchId}`);
861 1272 item.onBatchSuccess?.();
862 1273 }
863 1274 };
864 1275 }
@@ -877,8 +1288,57 @@
877 1288 });
878 1289 dispatch.processItem(id);
879 1290 };
880 1291 }
1292 + function scheduleRetry(id, error) {
1293 + return async ({ select: select2, dispatch }) => {
1294 + const item = select2.getItem(id);
1295 + if (!item) {
1296 + return;
1297 + }
1298 + const settings = select2.getSettings();
1299 + const retrySettings = settings.retry;
1300 + if (!retrySettings) {
1301 + return;
1302 + }
1303 + const currentRetryCount = item.retryCount ?? 0;
1304 + const delay = calculateRetryDelay({
1305 + attempt: currentRetryCount + 1,
1306 + initialDelay: retrySettings.initialRetryDelayMs,
1307 + maxDelay: retrySettings.maxRetryDelayMs,
1308 + multiplier: retrySettings.backoffMultiplier,
1309 + jitter: retrySettings.retryJitter
1310 + });
1311 + const timerId = setTimeout(() => {
1312 + retryTimers.delete(id);
1313 + dispatch.executeRetry(id);
1314 + }, delay);
1315 + retryTimers.set(id, timerId);
1316 + dispatch({
1317 + type: Type.ScheduleRetry,
1318 + id,
1319 + error,
1320 + retryCount: currentRetryCount,
1321 + nextRetryTimestamp: Date.now() + delay
1322 + });
1323 + };
1324 + }
1325 + function executeRetry(id) {
1326 + return async ({ select: select2, dispatch }) => {
1327 + const item = select2.getItem(id);
1328 + if (!item || item.status !== ItemStatus.PendingRetry) {
1329 + return;
1330 + }
1331 + if (select2.isPaused()) {
1332 + return;
1333 + }
1334 + dispatch({
1335 + type: Type.RetryItem,
1336 + id
1337 + });
1338 + dispatch.processItem(id);
1339 + };
1340 + }
881 1341
882 1342 // packages/upload-media/build-module/store/private-actions.mjs
883 1343 var private_actions_exports = {};
884 1344 __export(private_actions_exports, {
@@ -883,8 +1343,9 @@
883 1343 var private_actions_exports = {};
884 1344 __export(private_actions_exports, {
885 1345 addItem: () => addItem,
886 1346 addSideloadItem: () => addSideloadItem,
1347 + detectUltraHdr: () => detectUltraHdr,
887 1348 finalizeItem: () => finalizeItem,
888 1349 finishOperation: () => finishOperation,
889 1350 generateThumbnails: () => generateThumbnails,
890 1351 getTranscodeImageOperation: () => getTranscodeImageOperation,
@@ -897,8 +1358,9 @@
897 1358 resumeQueue: () => resumeQueue,
898 1359 revokeBlobUrls: () => revokeBlobUrls,
899 1360 rotateItem: () => rotateItem,
900 1361 sideloadItem: () => sideloadItem,
1362 + transcodeGifItem: () => transcodeGifItem,
901 1363 transcodeImageItem: () => transcodeImageItem,
902 1364 updateItemProgress: () => updateItemProgress,
903 1365 updateSettings: () => updateSettings,
904 1366 uploadItem: () => uploadItem
@@ -903,8 +1365,9 @@
903 1365 updateSettings: () => updateSettings,
904 1366 uploadItem: () => uploadItem
905 1367 });
906 1368 var import_blob = __toESM(require_blob(), 1);
1369 + var import_i18n7 = __toESM(require_i18n(), 1);
907 1370
908 1371 // packages/upload-media/build-module/heic-parser.mjs
909 1372 var Reader = class {
910 1373 view;
@@ -1175,8 +1638,15 @@
1175 1638 return `hvc1.${spacePrefix}${profileIdc}.${compatHex}.${tierChar}${levelIdc}${constraintStr}`;
1176 1639 }
1177 1640 function readItemData(buffer, loc, idatOffset) {
1178 1641 const baseOffset = loc.constructionMethod === 1 ? idatOffset : 0;
1642 + for (const ext of loc.extents) {
1643 + if (baseOffset + ext.offset + ext.length > buffer.byteLength) {
1644 + throw new Error(
1645 + "HEIC item data extends past the end of the file"
1646 + );
1647 + }
1648 + }
1179 1649 if (loc.extents.length === 1) {
1180 1650 const ext = loc.extents[0];
1181 1651 const start = baseOffset + ext.offset;
1182 1652 return new Uint8Array(buffer.slice(start, start + ext.length));
@@ -1311,9 +1781,10 @@
1311 1781 tileWidth: width,
1312 1782 tileHeight: height,
1313 1783 outputWidth: width,
1314 1784 outputHeight: height,
1315 - rotation
1785 + rotation,
1786 + exifOrientation: rotation === 0 ? getUnappliedExifOrientation(buffer) : 1
1316 1787 };
1317 1788 }
1318 1789 function parseGridImage(r, buffer, gridItemId, locations, allAssoc, properties, irefBox, idatOffset) {
1319 1790 const gridLoc = locations.get(gridItemId);
@@ -1404,13 +1875,269 @@
1404 1875 tileWidth,
1405 1876 tileHeight,
1406 1877 outputWidth,
1407 1878 outputHeight,
1408 - rotation
1879 + rotation,
1880 + exifOrientation: rotation === 0 ? getUnappliedExifOrientation(buffer) : 1
1409 1881 };
1410 1882 }
1883 + function readTiffOrientation(payload) {
1884 + if (payload.length < 8) {
1885 + return 1;
1886 + }
1887 + const view = new DataView(
1888 + payload.buffer,
1889 + payload.byteOffset,
1890 + payload.byteLength
1891 + );
1892 + let tiffStart = 0;
1893 + const firstWord = view.getUint16(0);
1894 + if (firstWord !== 18761 && firstWord !== 19789) {
1895 + tiffStart = view.getUint32(0) + 4;
1896 + }
1897 + if (tiffStart + 8 > payload.length) {
1898 + return 1;
1899 + }
1900 + const byteOrder = view.getUint16(tiffStart);
1901 + let little;
1902 + if (byteOrder === 18761) {
1903 + little = true;
1904 + } else if (byteOrder === 19789) {
1905 + little = false;
1906 + } else {
1907 + return 1;
1908 + }
1909 + const ifd0 = tiffStart + view.getUint32(tiffStart + 4, little);
1910 + if (ifd0 + 2 > payload.length) {
1911 + return 1;
1912 + }
1913 + const entryCount = view.getUint16(ifd0, little);
1914 + for (let i = 0; i < entryCount; i++) {
1915 + const entry = ifd0 + 2 + i * 12;
1916 + if (entry + 12 > payload.length) {
1917 + break;
1918 + }
1919 + if (view.getUint16(entry, little) === 274) {
1920 + const value = view.getUint16(entry + 8, little);
1921 + return value >= 1 && value <= 8 ? value : 1;
1922 + }
1923 + }
1924 + return 1;
1925 + }
1926 + function findMeta(r) {
1927 + const metaBox = findBox(r, 0, r.view.byteLength, "meta");
1928 + if (!metaBox) {
1929 + return null;
1930 + }
1931 + const start = metaBox.offset + metaBox.headerSize + 4;
1932 + const end = metaBox.offset + metaBox.size;
1933 + return { box: metaBox, children: findBoxes(r, start, end) };
1934 + }
1935 + function parseExifOrientation(buffer) {
1936 + try {
1937 + const r = new Reader(buffer);
1938 + const meta = findMeta(r);
1939 + if (!meta) {
1940 + return 1;
1941 + }
1942 + const iinfBox = meta.children.find((b) => b.type === "iinf");
1943 + const ilocBox = meta.children.find((b) => b.type === "iloc");
1944 + if (!iinfBox || !ilocBox) {
1945 + return 1;
1946 + }
1947 + const itemTypes = parseIinf(r, iinfBox);
1948 + let exifItemId;
1949 + for (const [id, type] of itemTypes) {
1950 + if (type === "Exif") {
1951 + exifItemId = id;
1952 + break;
1953 + }
1954 + }
1955 + if (exifItemId === void 0) {
1956 + return 1;
1957 + }
1958 + const loc = parseIloc(r, ilocBox).get(exifItemId);
1959 + if (!loc || loc.extents.length === 0) {
1960 + return 1;
1961 + }
1962 + const idatBox = meta.children.find((b) => b.type === "idat");
1963 + const idatOffset = idatBox ? idatBox.offset + idatBox.headerSize : 0;
1964 + return readTiffOrientation(readItemData(buffer, loc, idatOffset));
1965 + } catch {
1966 + return 1;
1967 + }
1968 + }
1969 + function hasNativeTransform(r) {
1970 + const meta = findMeta(r);
1971 + if (!meta) {
1972 + return false;
1973 + }
1974 + const iprp = meta.children.find((b) => b.type === "iprp");
1975 + if (!iprp) {
1976 + return false;
1977 + }
1978 + const ipco = findBox(
1979 + r,
1980 + iprp.offset + iprp.headerSize,
1981 + iprp.offset + iprp.size,
1982 + "ipco"
1983 + );
1984 + if (!ipco) {
1985 + return false;
1986 + }
1987 + const start = ipco.offset + ipco.headerSize;
1988 + const end = ipco.offset + ipco.size;
1989 + return Boolean(
1990 + findBox(r, start, end, "irot") || findBox(r, start, end, "imir")
1991 + );
1992 + }
1993 + function getUnappliedExifOrientation(buffer) {
1994 + try {
1995 + if (hasNativeTransform(new Reader(buffer))) {
1996 + return 1;
1997 + }
1998 + } catch {
1999 + return 1;
2000 + }
2001 + return parseExifOrientation(buffer);
2002 + }
1411 2003
2004 + // packages/upload-media/build-module/heic-support.mjs
2005 + var import_i18n6 = __toESM(require_i18n(), 1);
2006 + function detectBrowserFamily() {
2007 + if (typeof navigator === "undefined") {
2008 + return null;
2009 + }
2010 + const brands = navigator.userAgentData?.brands;
2011 + if (brands?.length) {
2012 + if (brands.some(({ brand }) => /Chromium/i.test(brand))) {
2013 + return "chromium";
2014 + }
2015 + }
2016 + const userAgent = navigator.userAgent || "";
2017 + if (/Firefox|FxiOS/i.test(userAgent)) {
2018 + return "firefox";
2019 + }
2020 + if (/Chrome|Chromium|CriOS|Edg|OPR/i.test(userAgent)) {
2021 + return "chromium";
2022 + }
2023 + if (/Safari/i.test(userAgent)) {
2024 + return "safari";
2025 + }
2026 + return null;
2027 + }
2028 + function detectPlatform() {
2029 + if (typeof navigator === "undefined") {
2030 + return "other";
2031 + }
2032 + const platform = navigator.userAgentData?.platform || navigator.userAgent || "";
2033 + if (/iPhone|iPad|iPod|iOS/i.test(platform)) {
2034 + return "other";
2035 + }
2036 + if (/macOS|Mac OS X|Macintosh/i.test(platform)) {
2037 + return "macos";
2038 + }
2039 + if (/Windows/i.test(platform)) {
2040 + return "windows";
2041 + }
2042 + if (/Android|CrOS|Chrome OS/i.test(platform)) {
2043 + return "other";
2044 + }
2045 + if (/Linux|X11/i.test(platform)) {
2046 + return "linux";
2047 + }
2048 + return "other";
2049 + }
2050 + function getBrowserName() {
2051 + if (detectBrowserFamily() === "chromium") {
2052 + const brands = navigator.userAgentData?.brands ?? [];
2053 + const userAgent = navigator.userAgent || "";
2054 + if (brands.some(({ brand }) => /Edge/i.test(brand)) || /Edg\//i.test(userAgent)) {
2055 + return "Edge";
2056 + }
2057 + if (brands.some(({ brand }) => /Opera/i.test(brand)) || /OPR\//i.test(userAgent)) {
2058 + return "Opera";
2059 + }
2060 + return "Chrome";
2061 + }
2062 + switch (detectBrowserFamily()) {
2063 + case "firefox":
2064 + return "Firefox";
2065 + case "safari":
2066 + return "Safari";
2067 + default:
2068 + return null;
2069 + }
2070 + }
2071 + function getHeicConversionAdvice() {
2072 + const platform = detectPlatform();
2073 + const family = detectBrowserFamily();
2074 + if (platform === "macos") {
2075 + if (family === "chromium") {
2076 + return (0, import_i18n6.__)(
2077 + "Safari usually can, or you can upload a JPEG instead."
2078 + );
2079 + }
2080 + if (family !== "safari") {
2081 + return (0, import_i18n6.__)(
2082 + "Safari or Chrome usually can, or you can upload a JPEG instead."
2083 + );
2084 + }
2085 + }
2086 + if (platform === "windows" && family === "firefox") {
2087 + return (0, import_i18n6.__)("Chrome or Edge might, or you can upload a JPEG instead.");
2088 + }
2089 + return (0, import_i18n6.__)("You can upload a JPEG instead.");
2090 + }
2091 + function getHeicUnsupportedMessage() {
2092 + const platform = detectPlatform();
2093 + const family = detectBrowserFamily();
2094 + const browserName = getBrowserName();
2095 + let cause;
2096 + if (platform === "linux") {
2097 + cause = (0, import_i18n6.__)(
2098 + "HEIC photos can't be decoded on Linux, so unfortunately we couldn't convert this one."
2099 + );
2100 + } else if (family === "firefox") {
2101 + cause = (0, import_i18n6.__)(
2102 + "Firefox can't read HEIC photos, so we couldn't convert this one."
2103 + );
2104 + } else if (browserName && platform === "windows") {
2105 + cause = (0, import_i18n6.sprintf)(
2106 + /* translators: %s: browser name, e.g. "Chrome". */
2107 + (0, import_i18n6.__)(
2108 + "%s couldn't decode HEIC on this PC, so we couldn't convert this one."
2109 + ),
2110 + browserName
2111 + );
2112 + } else if (browserName && platform === "macos") {
2113 + cause = (0, import_i18n6.sprintf)(
2114 + /* translators: %s: browser name, e.g. "Safari". */
2115 + (0, import_i18n6.__)(
2116 + "%s couldn't decode HEIC on this Mac, so we couldn't convert this one."
2117 + ),
2118 + browserName
2119 + );
2120 + } else if (browserName) {
2121 + cause = (0, import_i18n6.sprintf)(
2122 + /* translators: %s: browser name, e.g. "Chrome". */
2123 + (0, import_i18n6.__)(
2124 + "%s couldn't decode HEIC on this device, so we couldn't convert this one."
2125 + ),
2126 + browserName
2127 + );
2128 + } else {
2129 + cause = (0, import_i18n6.__)(
2130 + "This browser can't read HEIC photos, so we couldn't convert this one."
2131 + );
2132 + }
2133 + return `${cause} ${getHeicConversionAdvice()}`;
2134 + }
2135 +
1412 2136 // packages/upload-media/build-module/canvas-utils.mjs
2137 + var IMAGE_DECODER_TIMEOUT = 15e3;
2138 + var HeicUnsupportedError = class extends Error {
2139 + };
1413 2140 async function canvasConvertToJpeg(file, quality = 0.82) {
1414 2141 const baseName = getFileBasename(file.name);
1415 2142 try {
1416 2143 const bitmap = await createImageBitmap(file);
@@ -1439,10 +2166,36 @@
1439 2166 const decoder = new ImageDecoder({
1440 2167 type: file.type,
1441 2168 data: file.stream()
1442 2169 });
2170 + let videoFrame;
2171 + let timeoutId;
2172 + let timedOut = false;
1443 2173 try {
1444 - 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) {
1445 2198 try {
1446 2199 const canvas = new OffscreenCanvas(
1447 2200 videoFrame.displayWidth,
1448 2201 videoFrame.displayHeight
@@ -1461,57 +2214,62 @@
1461 2214 });
1462 2215 } finally {
1463 2216 videoFrame.close();
1464 2217 }
1465 - } finally {
1466 - decoder.close();
1467 2218 }
1468 2219 }
1469 2220 }
2221 + let heicData;
2222 + try {
2223 + heicData = parseHeic(await file.arrayBuffer());
2224 + } catch (error) {
2225 + throw new Error("The HEIC file could not be parsed", {
2226 + cause: error
2227 + });
2228 + }
1470 2229 if (typeof VideoDecoder !== "undefined") {
2230 + let supported = false;
1471 2231 try {
1472 - const heicData = parseHeic(await file.arrayBuffer());
1473 2232 const support = await VideoDecoder.isConfigSupported({
1474 2233 codec: heicData.codecString
1475 2234 });
1476 - if (support.supported) {
1477 - const canvas = new OffscreenCanvas(
1478 - heicData.outputWidth,
1479 - heicData.outputHeight
2235 + supported = support.supported === true;
2236 + } catch {
2237 + }
2238 + if (supported) {
2239 + const canvas = new OffscreenCanvas(
2240 + heicData.outputWidth,
2241 + heicData.outputHeight
2242 + );
2243 + const ctx = canvas.getContext("2d");
2244 + if (!ctx) {
2245 + throw new Error("Could not get canvas 2d context");
2246 + }
2247 + for (const tile of heicData.tiles) {
2248 + const frame = await decodeHevcFrame(
2249 + heicData.codecString,
2250 + heicData.description,
2251 + heicData.tileWidth,
2252 + heicData.tileHeight,
2253 + tile.data
1480 2254 );
1481 - const ctx = canvas.getContext("2d");
1482 - if (!ctx) {
1483 - throw new Error("Could not get canvas 2d context");
2255 + try {
2256 + ctx.drawImage(frame, tile.x, tile.y);
2257 + } finally {
2258 + frame.close();
1484 2259 }
1485 - for (const tile of heicData.tiles) {
1486 - const frame = await decodeHevcFrame(
1487 - heicData.codecString,
1488 - heicData.description,
1489 - heicData.tileWidth,
1490 - heicData.tileHeight,
1491 - tile.data
1492 - );
1493 - try {
1494 - ctx.drawImage(frame, tile.x, tile.y);
1495 - } finally {
1496 - frame.close();
1497 - }
1498 - }
1499 - const outputCanvas = applyRotation(canvas, heicData.rotation);
1500 - const jpegBlob = await outputCanvas.convertToBlob({
1501 - type: "image/jpeg",
1502 - quality
1503 - });
1504 - return new File([jpegBlob], `${baseName}.jpg`, {
1505 - type: "image/jpeg"
1506 - });
1507 2260 }
1508 - } catch {
2261 + const outputCanvas = heicData.rotation !== 0 ? applyRotation(canvas, heicData.rotation) : applyExifOrientation(canvas, heicData.exifOrientation);
2262 + const jpegBlob = await outputCanvas.convertToBlob({
2263 + type: "image/jpeg",
2264 + quality
2265 + });
2266 + return new File([jpegBlob], `${baseName}.jpg`, {
2267 + type: "image/jpeg"
2268 + });
1509 2269 }
1510 2270 }
1511 - throw new Error(
1512 - "This browser cannot decode HEIC images. Please use Safari or convert to JPEG before uploading."
1513 - );
2271 + throw new HeicUnsupportedError(getHeicUnsupportedMessage());
1514 2272 }
1515 2273 function applyRotation(source, rotation) {
1516 2274 if (rotation === 0) {
1517 2275 return source;
@@ -1528,8 +2286,45 @@
1528 2286 ctx.rotate(-rotation * Math.PI / 180);
1529 2287 ctx.drawImage(source, -source.width / 2, -source.height / 2);
1530 2288 return rotated;
1531 2289 }
2290 + function applyExifOrientation(source, orientation) {
2291 + if (orientation <= 1 || orientation > 8) {
2292 + return source;
2293 + }
2294 + const { width: sw, height: sh } = source;
2295 + const swap = orientation >= 5;
2296 + const out = new OffscreenCanvas(swap ? sh : sw, swap ? sw : sh);
2297 + const ctx = out.getContext("2d");
2298 + if (!ctx) {
2299 + return source;
2300 + }
2301 + switch (orientation) {
2302 + case 2:
2303 + ctx.transform(-1, 0, 0, 1, sw, 0);
2304 + break;
2305 + case 3:
2306 + ctx.transform(-1, 0, 0, -1, sw, sh);
2307 + break;
2308 + case 4:
2309 + ctx.transform(1, 0, 0, -1, 0, sh);
2310 + break;
2311 + case 5:
2312 + ctx.transform(0, 1, 1, 0, 0, 0);
2313 + break;
2314 + case 6:
2315 + ctx.transform(0, 1, -1, 0, sh, 0);
2316 + break;
2317 + case 7:
2318 + ctx.transform(0, -1, -1, 0, sh, sw);
2319 + break;
2320 + case 8:
2321 + ctx.transform(0, -1, 1, 0, 0, sw);
2322 + break;
2323 + }
2324 + ctx.drawImage(source, 0, 0);
2325 + return out;
2326 + }
1532 2327 function decodeHevcFrame(codec, description, width, height, data) {
1533 2328 return new Promise((resolve, reject) => {
1534 2329 const decoder = new VideoDecoder({
1535 2330 output: (frame) => {
@@ -1656,9 +2451,94 @@
1656 2451 }
1657 2452 function clearFeatureDetectionCache() {
1658 2453 cachedResult = null;
1659 2454 }
2455 + var BYTES_PER_PIXEL = 4;
2456 + var INTERLACED_MEMORY_BUDGET = 0.5 * 1024 * 1024 * 1024;
2457 + var BASELINE_MEMORY_BUDGET = 0.9 * 1024 * 1024 * 1024;
2458 + function exceedsClientProcessingMemory(dimensions) {
2459 + const { width, height, interlaced } = dimensions;
2460 + const estimatedBytes = width * height * BYTES_PER_PIXEL;
2461 + const budget = interlaced ? INTERLACED_MEMORY_BUDGET : BASELINE_MEMORY_BUDGET;
2462 + return estimatedBytes > budget;
2463 + }
1660 2464
2465 + // packages/upload-media/build-module/get-image-dimensions.mjs
2466 + var MAX_HEADER_BYTES = 512 * 1024;
2467 + function parseJpeg(view) {
2468 + let offset = 2;
2469 + while (offset < view.byteLength) {
2470 + if (view.getUint8(offset) !== 255) {
2471 + return null;
2472 + }
2473 + while (offset < view.byteLength && view.getUint8(offset) === 255) {
2474 + offset++;
2475 + }
2476 + if (offset >= view.byteLength) {
2477 + return null;
2478 + }
2479 + const marker = view.getUint8(offset);
2480 + offset++;
2481 + if (marker === 1 || marker >= 208 && marker <= 217) {
2482 + continue;
2483 + }
2484 + if (marker === 218) {
2485 + return null;
2486 + }
2487 + if (offset + 2 > view.byteLength) {
2488 + return null;
2489 + }
2490 + const segmentLength = view.getUint16(offset);
2491 + if (segmentLength < 2) {
2492 + return null;
2493 + }
2494 + const isStartOfFrame = marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204;
2495 + if (isStartOfFrame) {
2496 + if (offset + 7 > view.byteLength) {
2497 + return null;
2498 + }
2499 + const height = view.getUint16(offset + 3);
2500 + const width = view.getUint16(offset + 5);
2501 + const interlaced = marker === 194 || marker === 198 || marker === 202 || marker === 206;
2502 + return { width, height, interlaced };
2503 + }
2504 + offset += segmentLength;
2505 + }
2506 + return null;
2507 + }
2508 + function parsePng(view) {
2509 + if (view.byteLength < 29) {
2510 + return null;
2511 + }
2512 + const isIhdr = view.getUint8(12) === 73 && // I
2513 + view.getUint8(13) === 72 && // H
2514 + view.getUint8(14) === 68 && // D
2515 + view.getUint8(15) === 82;
2516 + if (!isIhdr) {
2517 + return null;
2518 + }
2519 + const width = view.getUint32(16);
2520 + const height = view.getUint32(20);
2521 + const interlaceMethod = view.getUint8(28);
2522 + return { width, height, interlaced: interlaceMethod !== 0 };
2523 + }
2524 + async function getImageDimensions(file) {
2525 + try {
2526 + const headerBytes = Math.min(file.size, MAX_HEADER_BYTES);
2527 + const buffer = await file.slice(0, headerBytes).arrayBuffer();
2528 + const view = new DataView(buffer);
2529 + if (view.byteLength >= 3 && view.getUint16(0) === 65496) {
2530 + return parseJpeg(view);
2531 + }
2532 + if (view.byteLength >= 8 && view.getUint32(0) === 2303741511 && view.getUint32(4) === 218765834) {
2533 + return parsePng(view);
2534 + }
2535 + return null;
2536 + } catch {
2537 + return null;
2538 + }
2539 + }
2540 +
1661 2541 // packages/upload-media/build-module/stub-file.mjs
1662 2542 var StubFile = class extends File {
1663 2543 constructor(fileName = "stub-file") {
1664 2544 super([], fileName);
@@ -1666,8 +2546,9 @@
1666 2546 };
1667 2547
1668 2548 // packages/upload-media/build-module/store/private-actions.mjs
1669 2549 var DEFAULT_OUTPUT_QUALITY = 0.82;
2550 + var ultraHdrItems = /* @__PURE__ */ new Set();
1670 2551 function addItem({
1671 2552 file: fileOrBlob,
1672 2553 batchId,
1673 2554 onChange,
@@ -1758,8 +2639,11 @@
1758 2639 const item = select2.getItem(id);
1759 2640 if (!item) {
1760 2641 return;
1761 2642 }
2643 + if (item.currentOperation) {
2644 + return;
2645 + }
1762 2646 const {
1763 2647 attachment,
1764 2648 onChange,
1765 2649 onSuccess,
@@ -1782,8 +2666,14 @@
1782 2666 if (activeCount >= settings.maxConcurrentImageProcessing) {
1783 2667 return;
1784 2668 }
1785 2669 }
2670 + if (operation === OperationType.TranscodeGif) {
2671 + const activeCount = select2.getActiveVideoProcessingCount();
2672 + if (activeCount >= 1) {
2673 + return;
2674 + }
2675 + }
1786 2676 if (attachment) {
1787 2677 const isHeicUrl = attachment.url && /\.hei[cf]$/i.test(attachment.url);
1788 2678 if (!isHeicUrl) {
1789 2679 onChange?.([attachment]);
@@ -1827,8 +2717,11 @@
1827 2717 type: Type.OperationStart,
1828 2718 id,
1829 2719 operation
1830 2720 });
2721 + debug(
2722 + `Starting operation ${operation} for ${item.file.name} (item ${item.id})`
2723 + );
1831 2724 switch (operation) {
1832 2725 case OperationType.Prepare:
1833 2726 dispatch.prepareItem(item.id);
1834 2727 break;
@@ -1849,8 +2742,14 @@
1849 2742 item.id,
1850 2743 operationArgs
1851 2744 );
1852 2745 break;
2746 + case OperationType.TranscodeGif:
2747 + dispatch.transcodeGifItem(
2748 + item.id,
2749 + operationArgs
2750 + );
2751 + break;
1853 2752 case OperationType.Upload:
1854 2753 if (item.parentId) {
1855 2754 dispatch.sideloadItem(id);
1856 2755 } else {
@@ -1862,8 +2761,11 @@
1862 2761 break;
1863 2762 case OperationType.Finalize:
1864 2763 dispatch.finalizeItem(id);
1865 2764 break;
2765 + case OperationType.DetectUltraHdr:
2766 + dispatch.detectUltraHdr(id);
2767 + break;
1866 2768 }
1867 2769 };
1868 2770 }
1869 2771 function pauseQueue() {
@@ -1876,9 +2778,13 @@
1876 2778 dispatch({
1877 2779 type: Type.ResumeQueue
1878 2780 });
1879 2781 for (const item of select2.getAllItems()) {
1880 - dispatch.processItem(item.id);
2782 + if (item.status === ItemStatus.PendingRetry) {
2783 + dispatch.executeRetry(item.id);
2784 + } else {
2785 + dispatch.processItem(item.id);
2786 + }
1881 2787 }
1882 2788 };
1883 2789 }
1884 2790 function pauseItem(id) {
@@ -1894,8 +2800,10 @@
1894 2800 const item = select2.getItem(id);
1895 2801 if (!item) {
1896 2802 return;
1897 2803 }
2804 + ultraHdrItems.delete(id);
2805 + clearRetryTimer(id);
1898 2806 dispatch({
1899 2807 type: Type.Remove,
1900 2808 id
1901 2809 });
@@ -1900,8 +2808,9 @@
1900 2808 id
1901 2809 });
1902 2810 if (select2.getAllItems().length === 0) {
1903 2811 terminateVipsWorker();
2812 + terminateVideoConversionWorker();
1904 2813 }
1905 2814 };
1906 2815 }
1907 2816 function finishOperation(id, updates) {
@@ -1925,8 +2834,17 @@
1925 2834 for (const pendingItem of pendingItems) {
1926 2835 dispatch.processItem(pendingItem.id);
1927 2836 }
1928 2837 }
2838 + if (previousOperation === OperationType.TranscodeGif) {
2839 + const pendingItems = select2.getPendingVideoProcessing();
2840 + for (const pendingItem of pendingItems) {
2841 + dispatch.processItem(pendingItem.id);
2842 + }
2843 + }
2844 + if (previousOperation === OperationType.ResizeCrop || previousOperation === OperationType.Rotate || previousOperation === OperationType.TranscodeImage) {
2845 + maybeRecycleVipsWorker(select2.getActiveImageProcessingCount());
2846 + }
1929 2847 };
1930 2848 }
1931 2849 var VALID_IMAGE_FORMATS = ["jpeg", "webp", "avif", "png", "gif"];
1932 2850 function isValidImageFormat(format) {
@@ -1931,9 +2849,9 @@
1931 2849 var VALID_IMAGE_FORMATS = ["jpeg", "webp", "avif", "png", "gif"];
1932 2850 function isValidImageFormat(format) {
1933 2851 return VALID_IMAGE_FORMATS.includes(format);
1934 2852 }
1935 - async function getTranscodeImageOperation(file, outputMimeType, interlaced = false) {
2853 + async function getTranscodeImageOperation(file, outputMimeType, interlaced = false, quality = DEFAULT_OUTPUT_QUALITY) {
1936 2854 if (file.type === "image/png" && outputMimeType === "image/jpeg") {
1937 2855 const blobUrl = (0, import_blob.createBlobURL)(file);
1938 2856 try {
1939 2857 const hasAlpha = await vipsHasTransparency(blobUrl);
@@ -1953,9 +2871,9 @@
1953 2871 return [
1954 2872 OperationType.TranscodeImage,
1955 2873 {
1956 2874 outputFormat: formatPart,
1957 - outputQuality: DEFAULT_OUTPUT_QUALITY,
2875 + outputQuality: quality,
1958 2876 interlaced
1959 2877 }
1960 2878 ];
1961 2879 }
@@ -1967,28 +2885,63 @@
1967 2885 }
1968 2886 const { file } = item;
1969 2887 const operations = [];
1970 2888 const settings = select2.getSettings();
2889 + if (file.type === "image/gif" && settings.gifConvert !== false && typeof ImageDecoder !== "undefined" && typeof VideoEncoder !== "undefined") {
2890 + let isAnimated = false;
2891 + try {
2892 + isAnimated = isAnimatedGif(await file.arrayBuffer());
2893 + } catch {
2894 + isAnimated = false;
2895 + }
2896 + if (isAnimated) {
2897 + let hasTransparency = false;
2898 + const blobUrl = (0, import_blob.createBlobURL)(file);
2899 + try {
2900 + hasTransparency = await vipsHasTransparency(blobUrl);
2901 + } catch {
2902 + hasTransparency = true;
2903 + } finally {
2904 + (0, import_blob.revokeBlobURL)(blobUrl);
2905 + }
2906 + if (!hasTransparency) {
2907 + operations.push(
2908 + OperationType.Upload,
2909 + OperationType.ThumbnailGeneration,
2910 + OperationType.Finalize
2911 + );
2912 + dispatch({
2913 + type: Type.AddOperations,
2914 + id,
2915 + operations
2916 + });
2917 + dispatch.finishOperation(id, {
2918 + animatedGifFile: item.file
2919 + });
2920 + return;
2921 + }
2922 + }
2923 + }
1971 2924 let heicJpeg = null;
1972 - const isImage = file.type.startsWith("image/");
1973 - const isVipsSupported = CLIENT_SIDE_SUPPORTED_MIME_TYPES.includes(
1974 - file.type
1975 - );
1976 - 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;
2933 + let tooLargeForClient = false;
1977 2934 if (isImage && isVipsSupported) {
1978 - const { bigImageSizeThreshold } = settings;
1979 - if (bigImageSizeThreshold) {
1980 - operations.push([
1981 - OperationType.ResizeCrop,
1982 - {
1983 - resize: {
1984 - width: bigImageSizeThreshold,
1985 - height: bigImageSizeThreshold
1986 - },
1987 - isThresholdResize: true
1988 - }
1989 - ]);
2935 + const dimensions = await getImageDimensions(file);
2936 + if (dimensions && exceedsClientProcessingMemory(dimensions)) {
2937 + tooLargeForClient = true;
1990 2938 }
2939 + }
2940 + if (file.type === "image/jpeg" && !isHeic && !tooLargeForClient) {
2941 + operations.push(OperationType.DetectUltraHdr);
2942 + }
2943 + if (isImage && isVipsSupported && !tooLargeForClient) {
1991 2944 operations.push(
1992 2945 OperationType.Upload,
1993 2946 OperationType.ThumbnailGeneration,
1994 2947 OperationType.Finalize
@@ -1995,18 +2948,22 @@
1995 2948 );
1996 2949 } else if (isImage && isHeic) {
1997 2950 try {
1998 2951 heicJpeg = await canvasConvertToJpeg(
1999 - file,
2952 + heicFile,
2000 2953 settings.imageQuality ?? DEFAULT_OUTPUT_QUALITY
2001 2954 );
2002 - } catch {
2955 + } catch (error) {
2956 + const unsupported = error instanceof HeicUnsupportedError;
2003 2957 dispatch.cancelItem(
2004 2958 id,
2005 2959 new UploadError({
2006 - code: "HEIC_DECODE_ERROR",
2007 - message: "This browser cannot decode HEIC images and the server does not support them either. Please convert to JPEG before uploading.",
2008 - file
2960 + code: unsupported ? ErrorCode.HEIC_DECODE_ERROR : ErrorCode.IMAGE_TRANSCODING_ERROR,
2961 + message: unsupported ? getHeicUnsupportedMessage() : (0, import_i18n7.__)(
2962 + "This HEIC image could not be converted. Try converting it to JPEG before uploading."
2963 + ),
2964 + file,
2965 + cause: error instanceof Error ? error : void 0
2009 2966 })
2010 2967 );
2011 2968 return;
2012 2969 }
@@ -2022,15 +2979,15 @@
2022 2979 type: Type.AddOperations,
2023 2980 id,
2024 2981 operations
2025 2982 });
2026 - let updates = {};
2983 + let updates;
2027 2984 if (isHeic && heicJpeg) {
2028 2985 const vipsAvailable = isClientSideMediaSupported();
2029 2986 updates = {
2030 2987 file: heicJpeg,
2031 2988 sourceFile: heicJpeg,
2032 - originalHeicFile: item.file,
2989 + originalHeicFile: heicFile,
2033 2990 additionalData: {
2034 2991 ...item.additionalData,
2035 2992 generate_sub_sizes: !vipsAvailable,
2036 2993 convert_format: true
@@ -2035,9 +2992,9 @@
2035 2992 generate_sub_sizes: !vipsAvailable,
2036 2993 convert_format: true
2037 2994 }
2038 2995 };
2039 - } else if (!isVipsSupported || !isImage) {
2996 + } else if (!isVipsSupported || !isImage || tooLargeForClient) {
2040 2997 updates = {
2041 2998 additionalData: {
2042 2999 ...item.additionalData,
2043 3000 generate_sub_sizes: true,
@@ -2043,12 +3000,37 @@
2043 3000 generate_sub_sizes: true,
2044 3001 convert_format: true
2045 3002 }
2046 3003 };
3004 + } else {
3005 + updates = {
3006 + additionalData: {
3007 + ...item.additionalData,
3008 + generate_sub_sizes: false
3009 + }
3010 + };
2047 3011 }
2048 3012 dispatch.finishOperation(id, updates);
2049 3013 };
2050 3014 }
3015 + function detectUltraHdr(id) {
3016 + return async ({ select: select2, dispatch }) => {
3017 + const item = select2.getItem(id);
3018 + if (!item) {
3019 + return;
3020 + }
3021 + let info;
3022 + try {
3023 + const buffer = await item.file.arrayBuffer();
3024 + info = await vipsGetUltraHdrInfo(buffer);
3025 + } catch {
3026 + }
3027 + if (info) {
3028 + ultraHdrItems.add(id);
3029 + }
3030 + dispatch.finishOperation(id, {});
3031 + };
3032 + }
2051 3033 function uploadItem(id) {
2052 3034 return async ({ select: select2, dispatch }) => {
2053 3035 const item = select2.getItem(id);
2054 3036 if (!item) {
@@ -2053,23 +3035,42 @@
2053 3035 const item = select2.getItem(id);
2054 3036 if (!item) {
2055 3037 return;
2056 3038 }
3039 + const startTime = performance.now();
3040 + let finished = false;
3041 + const finishUpload = (attachment) => {
3042 + if (finished) {
3043 + return;
3044 + }
3045 + finished = true;
3046 + measure({
3047 + measureName: `Upload ${item.file.name}`,
3048 + startTime,
3049 + tooltipText: item.file.name,
3050 + properties: [
3051 + ["Item ID", item.id],
3052 + ["File name", item.file.name]
3053 + ]
3054 + });
3055 + dispatch.finishOperation(id, { attachment });
3056 + };
2057 3057 select2.getSettings().mediaUpload({
2058 3058 filesList: [item.file],
2059 3059 additionalData: item.additionalData,
2060 3060 signal: item.abortController?.signal,
3061 + // The queue's own items drive upload progress UI and save
3062 + // locking; without this, consumers that track uploads themselves
3063 + // (e.g. the editor's progress snackbar) would count this file a
3064 + // second time.
3065 + isTransportOnly: true,
2061 3066 onFileChange: ([attachment]) => {
2062 3067 if (attachment && !(0, import_blob.isBlobURL)(attachment.url)) {
2063 - dispatch.finishOperation(id, {
2064 - attachment
2065 - });
3068 + finishUpload(attachment);
2066 3069 }
2067 3070 },
2068 3071 onSuccess: ([attachment]) => {
2069 - dispatch.finishOperation(id, {
2070 - attachment
2071 - });
3072 + finishUpload(attachment);
2072 3073 },
2073 3074 onError: (error) => {
2074 3075 dispatch.cancelItem(id, error);
2075 3076 }
@@ -2087,8 +3088,9 @@
2087 3088 if (!mediaSideload) {
2088 3089 dispatch.finishOperation(id, {});
2089 3090 return;
2090 3091 }
3092 + const startTime = performance.now();
2091 3093 mediaSideload({
2092 3094 file: item.file,
2093 3095 attachmentId: post,
2094 3096 additionalData,
@@ -2093,8 +3095,17 @@
2093 3095 attachmentId: post,
2094 3096 additionalData,
2095 3097 signal: item.abortController?.signal,
2096 3098 onSuccess: (subSize) => {
3099 + measure({
3100 + measureName: `Sideload ${item.file.name}`,
3101 + startTime,
3102 + tooltipText: item.file.name,
3103 + properties: [
3104 + ["Item ID", item.id],
3105 + ["File name", item.file.name]
3106 + ]
3107 + });
2097 3108 if (item.parentId) {
2098 3109 dispatch({
2099 3110 type: Type.AccumulateSubSize,
2100 3111 id: item.parentId,
@@ -2120,21 +3131,36 @@
2120 3131 file: item.file
2121 3132 });
2122 3133 return;
2123 3134 }
3135 + const startTime = performance.now();
2124 3136 const addSuffix = Boolean(item.parentId);
2125 3137 const scaledSuffix = Boolean(args.isThresholdResize);
3138 + const { imageStripMeta, imageMaxBitDepth } = select2.getSettings();
2126 3139 try {
2127 3140 const file = await vipsResizeImage(
2128 3141 item.id,
2129 3142 item.file,
2130 3143 args.resize,
2131 - false,
2132 - // smartCrop
2133 - addSuffix,
2134 - item.abortController?.signal,
2135 - scaledSuffix
3144 + {
3145 + smartCrop: false,
3146 + addSuffix,
3147 + signal: item.abortController?.signal,
3148 + scaledSuffix,
3149 + quality: args.quality,
3150 + stripMeta: imageStripMeta,
3151 + maxBitdepth: imageMaxBitDepth
3152 + }
2136 3153 );
3154 + measure({
3155 + measureName: `ResizeCrop ${item.file.name}`,
3156 + startTime,
3157 + tooltipText: item.file.name,
3158 + properties: [
3159 + ["Item ID", item.id],
3160 + ["File name", item.file.name]
3161 + ]
3162 + });
2137 3163 const blobUrl = (0, import_blob.createBlobURL)(file);
2138 3164 dispatch({
2139 3165 type: Type.CacheBlobUrl,
2140 3166 id,
@@ -2149,10 +3175,12 @@
2149 3175 } catch (error) {
2150 3176 dispatch.cancelItem(
2151 3177 id,
2152 3178 new UploadError({
2153 - code: "IMAGE_TRANSCODING_ERROR",
2154 - message: "File could not be uploaded",
3179 + code: ErrorCode.IMAGE_TRANSCODING_ERROR,
3180 + message: (0, import_i18n7.__)(
3181 + "The web server cannot generate responsive image sizes for this image. Convert it to JPEG or PNG before uploading."
3182 + ),
2155 3183 file: item.file,
2156 3184 cause: error instanceof Error ? error : void 0
2157 3185 })
2158 3186 );
@@ -2170,8 +3198,9 @@
2170 3198 file: item.file
2171 3199 });
2172 3200 return;
2173 3201 }
3202 + const startTime = performance.now();
2174 3203 try {
2175 3204 const file = await vipsRotateImage(
2176 3205 item.id,
2177 3206 item.file,
@@ -2177,8 +3206,17 @@
2177 3206 item.file,
2178 3207 args.orientation,
2179 3208 item.abortController?.signal
2180 3209 );
3210 + measure({
3211 + measureName: `Rotate ${item.file.name}`,
3212 + startTime,
3213 + tooltipText: item.file.name,
3214 + properties: [
3215 + ["Item ID", item.id],
3216 + ["File name", item.file.name]
3217 + ]
3218 + });
2181 3219 const blobUrl = (0, import_blob.createBlobURL)(file);
2182 3220 dispatch({
2183 3221 type: Type.CacheBlobUrl,
2184 3222 id,
@@ -2193,10 +3231,12 @@
2193 3231 } catch (error) {
2194 3232 dispatch.cancelItem(
2195 3233 id,
2196 3234 new UploadError({
2197 - code: "IMAGE_ROTATION_ERROR",
2198 - message: "Image could not be rotated",
3235 + code: ErrorCode.IMAGE_ROTATION_ERROR,
3236 + message: (0, import_i18n7.__)(
3237 + "The web server cannot generate responsive image sizes for this image. Convert it to JPEG or PNG before uploading."
3238 + ),
2199 3239 file: item.file,
2200 3240 cause: error instanceof Error ? error : void 0
2201 3241 })
2202 3242 );
@@ -2214,19 +3254,34 @@
2214 3254 file: item.file
2215 3255 });
2216 3256 return;
2217 3257 }
3258 + const startTime = performance.now();
2218 3259 const outputMimeType = `image/${args.outputFormat}`;
2219 3260 const quality = args.outputQuality ?? DEFAULT_OUTPUT_QUALITY;
2220 3261 const interlaced = args.interlaced ?? false;
3262 + const { imageStripMeta, imageMaxBitDepth } = select2.getSettings();
2221 3263 try {
2222 3264 const file = await vipsConvertImageFormat(
2223 3265 item.id,
2224 3266 item.file,
2225 3267 outputMimeType,
2226 - quality,
2227 - interlaced
3268 + {
3269 + quality,
3270 + interlaced,
3271 + stripMeta: imageStripMeta,
3272 + maxBitdepth: imageMaxBitDepth
3273 + }
2228 3274 );
3275 + measure({
3276 + measureName: `Transcode ${item.file.name}`,
3277 + startTime,
3278 + tooltipText: item.file.name,
3279 + properties: [
3280 + ["Item ID", item.id],
3281 + ["File name", item.file.name]
3282 + ]
3283 + });
2229 3284 const blobUrl = (0, import_blob.createBlobURL)(file);
2230 3285 dispatch({
2231 3286 type: Type.CacheBlobUrl,
2232 3287 id,
@@ -2241,9 +3296,9 @@
2241 3296 } catch (error) {
2242 3297 dispatch.cancelItem(
2243 3298 id,
2244 3299 new UploadError({
2245 - code: "MEDIA_TRANSCODING_ERROR",
3300 + code: ErrorCode.MEDIA_TRANSCODING_ERROR,
2246 3301 message: "Image could not be transcoded to the target format",
2247 3302 file: item.file,
2248 3303 cause: error instanceof Error ? error : void 0
2249 3304 })
@@ -2250,8 +3305,91 @@
2250 3305 );
2251 3306 }
2252 3307 };
2253 3308 }
3309 + function transcodeGifItem(id, args) {
3310 + return async ({ select: select2, dispatch }) => {
3311 + const item = select2.getItem(id);
3312 + if (!item) {
3313 + return;
3314 + }
3315 + const outputFormat = args?.outputFormat ?? "mp4";
3316 + const outputMimeType = `video/${outputFormat}`;
3317 + const gifFile = item.file;
3318 + try {
3319 + const file = await convertGifToVideo(
3320 + item.id,
3321 + gifFile,
3322 + outputMimeType,
3323 + {
3324 + timeout: args?.timeout,
3325 + maxTotalPixels: args?.maxTotalPixels
3326 + }
3327 + );
3328 + dispatch.finishOperation(id, { file });
3329 + dispatch.addSideloadItem({
3330 + file: gifFile,
3331 + batchId: v4_default(),
3332 + parentId: item.parentId,
3333 + additionalData: {
3334 + post: item.additionalData?.post,
3335 + image_size: "animated_video_poster",
3336 + convert_format: false
3337 + },
3338 + operations: [
3339 + [
3340 + OperationType.TranscodeImage,
3341 + {
3342 + outputFormat: "jpeg",
3343 + outputQuality: DEFAULT_OUTPUT_QUALITY,
3344 + interlaced: false
3345 + }
3346 + ],
3347 + OperationType.Upload
3348 + ]
3349 + });
3350 + } catch (error) {
3351 + if (isUnsupportedConversionError(error)) {
3352 + if (isSizeLimitConversionError(error)) {
3353 + debug(
3354 + `Skipping GIF to video conversion: ${error instanceof Error ? error.message : error}`
3355 + );
3356 + }
3357 + dispatch.cancelItem(
3358 + id,
3359 + new Error("Animated GIF conversion unsupported"),
3360 + true
3361 + );
3362 + return;
3363 + }
3364 + if (isConversionTimeoutError(error)) {
3365 + debug(
3366 + `GIF to video conversion timed out; keeping the original GIF only: ${error instanceof Error ? error.message : error}`
3367 + );
3368 + dispatch.cancelItem(
3369 + id,
3370 + new Error("Animated GIF conversion timed out"),
3371 + true
3372 + );
3373 + return;
3374 + }
3375 + console.error(
3376 + "[video-conversion] GIF to video conversion failed:",
3377 + error
3378 + );
3379 + dispatch.cancelItem(
3380 + id,
3381 + new UploadError({
3382 + code: ErrorCode.GIF_TRANSCODING_ERROR,
3383 + message: "Animated GIF could not be converted to video",
3384 + file: item.file,
3385 + cause: error instanceof Error ? error : void 0
3386 + }),
3387 + true
3388 + );
3389 + }
3390 + };
3391 + }
2254 3392 function generateThumbnails(id) {
2255 3393 return async ({ select: select2, dispatch }) => {
2256 3394 const item = select2.getItem(id);
2257 3395 if (!item) {
@@ -2269,35 +3407,57 @@
2269 3407 batchId: v4_default(),
2270 3408 parentId: item.id,
2271 3409 additionalData: {
2272 3410 post: attachment.id,
2273 - image_size: "original-heic",
3411 + image_size: "source_original",
2274 3412 convert_format: false
2275 3413 },
2276 3414 operations: [OperationType.Upload]
2277 3415 });
2278 3416 }
3417 + if (item.animatedGifFile && attachment.id) {
3418 + const outputFormat = settings.videoOutputFormat === "video/webm" ? "webm" : "mp4";
3419 + dispatch.addSideloadItem({
3420 + file: item.animatedGifFile,
3421 + batchId: v4_default(),
3422 + parentId: item.id,
3423 + additionalData: {
3424 + post: attachment.id,
3425 + image_size: "animated_video",
3426 + convert_format: false
3427 + },
3428 + operations: [
3429 + [
3430 + OperationType.TranscodeGif,
3431 + {
3432 + outputFormat
3433 + }
3434 + ],
3435 + OperationType.Upload
3436 + ]
3437 + });
3438 + }
3439 + let exifOrientation = attachment.exif_orientation || 1;
3440 + const sourceType = item.sourceFile.type;
3441 + const isHeifFamily = sourceType === "image/avif" || sourceType === "image/heif";
3442 + let needsClientRotation = false;
3443 + if (isHeifFamily) {
3444 + exifOrientation = getUnappliedExifOrientation(
3445 + await item.sourceFile.arrayBuffer()
3446 + );
3447 + needsClientRotation = exifOrientation !== 1;
3448 + }
3449 + let rotatedSource;
2279 3450 {
2280 - const needsRotation = attachment.exif_orientation && attachment.exif_orientation !== 1 && !item.file.name.includes("-scaled");
2281 - if (needsRotation && attachment.id) {
3451 + const needsRotation = exifOrientation !== 1 && !item.file.name.includes("-scaled");
3452 + if ((needsRotation || needsClientRotation) && attachment.id) {
2282 3453 try {
2283 - const rotatedFile = await vipsRotateImage(
3454 + rotatedSource = await vipsRotateImage(
2284 3455 item.id,
2285 3456 item.sourceFile,
2286 - attachment.exif_orientation,
3457 + exifOrientation,
2287 3458 item.abortController?.signal
2288 3459 );
2289 - dispatch.addSideloadItem({
2290 - file: rotatedFile,
2291 - batchId: v4_default(),
2292 - parentId: item.id,
2293 - additionalData: {
2294 - post: attachment.id,
2295 - image_size: "original",
2296 - convert_format: false
2297 - },
2298 - operations: [OperationType.Upload]
2299 - });
2300 3460 } catch {
2301 3461 console.warn(
2302 3462 "Failed to rotate image, continuing with thumbnails"
2303 3463 );
@@ -2302,23 +3462,48 @@
2302 3462 "Failed to rotate image, continuing with thumbnails"
2303 3463 );
2304 3464 }
2305 3465 }
3466 + if (needsRotation && rotatedSource && attachment.id) {
3467 + dispatch.addSideloadItem({
3468 + file: rotatedSource,
3469 + batchId: v4_default(),
3470 + parentId: item.id,
3471 + additionalData: {
3472 + post: attachment.id,
3473 + image_size: "original",
3474 + convert_format: false
3475 + },
3476 + operations: [OperationType.Upload]
3477 + });
3478 + }
2306 3479 }
2307 3480 if (!item.parentId && attachment.missing_image_sizes && attachment.missing_image_sizes.length > 0) {
2308 3481 const allImageSizes = settings.allImageSizes || {};
2309 3482 const sizesToGenerate = attachment.missing_image_sizes;
2310 - const thumbnailSource = item.sourceFile;
3483 + const thumbnailSource = needsClientRotation && rotatedSource ? rotatedSource : item.sourceFile;
2311 3484 const file = attachment.filename ? renameFile(thumbnailSource, attachment.filename) : thumbnailSource;
2312 3485 const batchId = v4_default();
3486 + const isUltraHdr = ultraHdrItems.has(item.id);
2313 3487 const outputMimeType = attachment.image_output_format;
2314 3488 const interlaced = attachment.image_save_progressive ?? false;
3489 + const imageQuality = attachment.image_quality;
3490 + const fallbackQuality = settings.imageQuality ?? DEFAULT_OUTPUT_QUALITY;
3491 + const defaultQuality = typeof imageQuality?.default === "number" ? imageQuality.default / 100 : fallbackQuality;
3492 + const qualityForSize = (sizeName) => {
3493 + const sized = imageQuality?.sizes?.[sizeName];
3494 + if (typeof sized === "number") {
3495 + return sized / 100;
3496 + }
3497 + return defaultQuality;
3498 + };
2315 3499 let thumbnailTranscodeOperation = null;
2316 - if (outputMimeType) {
3500 + if (!isUltraHdr && outputMimeType) {
2317 3501 thumbnailTranscodeOperation = await getTranscodeImageOperation(
2318 3502 thumbnailSource,
2319 3503 outputMimeType,
2320 - interlaced
3504 + interlaced,
3505 + defaultQuality
2321 3506 );
2322 3507 }
2323 3508 const dimensionGroups = /* @__PURE__ */ new Map();
2324 3509 for (const name of sizesToGenerate) {
@@ -2338,13 +3523,23 @@
2338 3523 }
2339 3524 }
2340 3525 for (const [, names] of dimensionGroups) {
2341 3526 const imageSize = allImageSizes[names[0]];
3527 + const sizeQuality = qualityForSize(names[0]);
2342 3528 const thumbnailOperations = [
2343 - [OperationType.ResizeCrop, { resize: imageSize }]
3529 + [
3530 + OperationType.ResizeCrop,
3531 + { resize: imageSize, quality: sizeQuality }
3532 + ]
2344 3533 ];
2345 - if (thumbnailTranscodeOperation) {
2346 - thumbnailOperations.push(thumbnailTranscodeOperation);
3534 + if (!isUltraHdr && thumbnailTranscodeOperation) {
3535 + thumbnailOperations.push([
3536 + thumbnailTranscodeOperation[0],
3537 + {
3538 + ...thumbnailTranscodeOperation[1],
3539 + outputQuality: sizeQuality
3540 + }
3541 + ]);
2347 3542 }
2348 3543 thumbnailOperations.push(OperationType.Upload);
2349 3544 const imageSizeParam = names.length === 1 ? names[0] : names;
2350 3545 dispatch.addSideloadItem({
@@ -2376,13 +3571,14 @@
2376 3571 resize: {
2377 3572 width: bigImageSizeThreshold,
2378 3573 height: bigImageSizeThreshold
2379 3574 },
2380 - isThresholdResize: true
3575 + isThresholdResize: true,
3576 + quality: defaultQuality
2381 3577 }
2382 3578 ]
2383 3579 ];
2384 - if (thumbnailTranscodeOperation) {
3580 + if (!isUltraHdr && thumbnailTranscodeOperation) {
2385 3581 scaledOperations.push(
2386 3582 thumbnailTranscodeOperation
2387 3583 );
2388 3584 }
@@ -2412,16 +3608,32 @@
2412 3608 return;
2413 3609 }
2414 3610 const attachment = item.attachment;
2415 3611 const { mediaFinalize } = select2.getSettings();
3612 + const updates = {};
2416 3613 if (attachment?.id && mediaFinalize) {
2417 3614 try {
2418 - await mediaFinalize(attachment.id, item.subSizes || []);
3615 + const updatedAttachment = await mediaFinalize(
3616 + attachment.id,
3617 + item.subSizes || []
3618 + );
3619 + if (updatedAttachment) {
3620 + updates.attachment = updatedAttachment;
3621 + }
2419 3622 } catch (error) {
2420 3623 console.warn("Media finalization failed:", error);
3624 + dispatch.cancelItem(
3625 + id,
3626 + new UploadError({
3627 + code: ErrorCode.MEDIA_FINALIZE_ERROR,
3628 + message: (0, import_i18n7.__)("Could not finalize the upload."),
3629 + file: item.file
3630 + })
3631 + );
3632 + return;
2421 3633 }
2422 3634 }
2423 - dispatch.finishOperation(id, {});
3635 + dispatch.finishOperation(id, updates);
2424 3636 };
2425 3637 }
2426 3638 function revokeBlobUrls(id) {
2427 3639 return async ({ select: select2, dispatch }) => {
@@ -2524,7 +3736,115 @@
2524 3736 }, [settings, updateSettings2]);
2525 3737 return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children });
2526 3738 });
2527 3739 var provider_default = MediaUploadProvider;
3740 +
3741 + // packages/upload-media/build-module/error-messages.mjs
3742 + var import_i18n8 = __toESM(require_i18n(), 1);
3743 + function getErrorMessage(code, fileName) {
3744 + const messages = {
3745 + [ErrorCode.EMPTY_FILE]: {
3746 + title: (0, import_i18n8.__)("Empty file"),
3747 + description: (0, import_i18n8.sprintf)(
3748 + /* translators: %s: file name */
3749 + (0, import_i18n8.__)('"%s" is empty.'),
3750 + fileName
3751 + ),
3752 + action: (0, import_i18n8.__)("Please choose a different file.")
3753 + },
3754 + [ErrorCode.SIZE_ABOVE_LIMIT]: {
3755 + title: (0, import_i18n8.__)("File too large"),
3756 + description: (0, import_i18n8.sprintf)(
3757 + /* translators: %s: file name */
3758 + (0, import_i18n8.__)('"%s" exceeds the maximum upload size.'),
3759 + fileName
3760 + ),
3761 + action: (0, import_i18n8.__)("Please reduce the file size and try again.")
3762 + },
3763 + [ErrorCode.MIME_TYPE_NOT_SUPPORTED]: {
3764 + title: (0, import_i18n8.__)("Unsupported file type"),
3765 + description: (0, import_i18n8.sprintf)(
3766 + /* translators: %s: file name */
3767 + (0, import_i18n8.__)('"%s" is not a supported file type.'),
3768 + fileName
3769 + ),
3770 + action: (0, import_i18n8.__)("Please upload a different file format.")
3771 + },
3772 + [ErrorCode.MIME_TYPE_NOT_ALLOWED_FOR_USER]: {
3773 + title: (0, import_i18n8.__)("File type not allowed"),
3774 + description: (0, import_i18n8.sprintf)(
3775 + /* translators: %s: file name */
3776 + (0, import_i18n8.__)('You are not allowed to upload "%s".'),
3777 + fileName
3778 + ),
3779 + action: (0, import_i18n8.__)("Please contact your site administrator.")
3780 + },
3781 + [ErrorCode.HEIC_DECODE_ERROR]: {
3782 + title: (0, import_i18n8.__)("HEIC decode failed"),
3783 + description: (0, import_i18n8.sprintf)(
3784 + /* translators: %s: file name */
3785 + (0, import_i18n8.__)('Failed to decode HEIC file "%s".'),
3786 + fileName
3787 + ),
3788 + action: getHeicConversionAdvice()
3789 + },
3790 + [ErrorCode.IMAGE_TRANSCODING_ERROR]: {
3791 + title: (0, import_i18n8.__)("Image processing failed"),
3792 + description: (0, import_i18n8.sprintf)(
3793 + /* translators: %s: file name */
3794 + (0, import_i18n8.__)('Failed to process "%s".'),
3795 + fileName
3796 + ),
3797 + action: (0, import_i18n8.__)("The image may be corrupted. Try a different file.")
3798 + },
3799 + [ErrorCode.IMAGE_ROTATION_ERROR]: {
3800 + title: (0, import_i18n8.__)("Image rotation failed"),
3801 + description: (0, import_i18n8.sprintf)(
3802 + /* translators: %s: file name */
3803 + (0, import_i18n8.__)('Failed to rotate "%s".'),
3804 + fileName
3805 + ),
3806 + action: (0, import_i18n8.__)("The image may be corrupted. Try a different file.")
3807 + },
3808 + [ErrorCode.MEDIA_TRANSCODING_ERROR]: {
3809 + title: (0, import_i18n8.__)("Media processing failed"),
3810 + description: (0, import_i18n8.sprintf)(
3811 + /* translators: %s: file name */
3812 + (0, import_i18n8.__)('Failed to convert "%s" to the target format.'),
3813 + fileName
3814 + ),
3815 + action: (0, import_i18n8.__)("The file may be corrupted. Try a different file.")
3816 + },
3817 + [ErrorCode.MEDIA_FINALIZE_ERROR]: {
3818 + title: (0, import_i18n8.__)("Upload failed"),
3819 + description: (0, import_i18n8.sprintf)(
3820 + /* translators: %s: file name */
3821 + (0, import_i18n8.__)('Could not finalize the upload of "%s".'),
3822 + fileName
3823 + ),
3824 + action: (0, import_i18n8.__)("Please try again.")
3825 + },
3826 + [ErrorCode.GENERAL]: {
3827 + title: (0, import_i18n8.__)("Upload failed"),
3828 + description: (0, import_i18n8.sprintf)(
3829 + /* translators: %s: file name */
3830 + (0, import_i18n8.__)('Failed to upload "%s".'),
3831 + fileName
3832 + ),
3833 + action: (0, import_i18n8.__)("Please try again.")
3834 + }
3835 + };
3836 + return messages[code] || {
3837 + title: (0, import_i18n8.__)("Upload failed"),
3838 + description: (0, import_i18n8.sprintf)(
3839 + /* translators: %s: file name */
3840 + (0, import_i18n8.__)('Failed to upload "%s".'),
3841 + fileName
3842 + ),
3843 + action: (0, import_i18n8.__)("Please try again.")
3844 + };
3845 + }
2528 3846 return __toCommonJS(index_exports);
3847 +})();
3848 +(window.wp ||= {}).uploadMedia = wp.uploadMedia;
2529 3849 })();
2530 3850 //# sourceMappingURL=index.js.map