| @@ -1,4 +1,5 @@ | ||
| 1 | +(function() { | |
| 1 | 2 | "use strict"; |
| 2 | 3 | var wp; |
| 3 | 4 | (wp ||= {}).uploadMedia = (() => { |
| 4 | 5 | var __create = Object.create; |
| @@ -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 |
| @@ -146,8 +152,9 @@ | ||
| 146 | 152 | OperationType2["Upload"] = "UPLOAD"; |
| 147 | 153 | OperationType2["ResizeCrop"] = "RESIZE_CROP"; |
| 148 | 154 | OperationType2["Rotate"] = "ROTATE"; |
| 149 | 155 | OperationType2["TranscodeImage"] = "TRANSCODE_IMAGE"; |
| 156 | + OperationType2["TranscodeGif"] = "TRANSCODE_GIF"; | |
| 150 | 157 | OperationType2["ThumbnailGeneration"] = "THUMBNAIL_GENERATION"; |
| 151 | 158 | OperationType2["Finalize"] = "FINALIZE"; |
| 152 | 159 | OperationType2["DetectUltraHdr"] = "DETECT_ULTRAHDR"; |
| 153 | 160 | return OperationType2; |
| @@ -187,9 +194,10 @@ | ||
| 187 | 194 | mediaUpload: noop, |
| 188 | 195 | maxConcurrentUploads: DEFAULT_MAX_CONCURRENT_UPLOADS, |
| 189 | 196 | maxConcurrentImageProcessing: DEFAULT_MAX_CONCURRENT_IMAGE_PROCESSING, |
| 190 | 197 | retry: { ...DEFAULT_RETRY_SETTINGS } |
| 191 | - } | |
| 198 | + }, | |
| 199 | + failureCount: 0 | |
| 192 | 200 | }; |
| 193 | 201 | function reducer(state = DEFAULT_STATE, action = { type: Type.Unknown }) { |
| 194 | 202 | switch (action.type) { |
| 195 | 203 | case Type.PauseQueue: { |
| @@ -228,11 +236,15 @@ | ||
| 228 | 236 | return { |
| 229 | 237 | ...state, |
| 230 | 238 | queue: [...state.queue, action.item] |
| 231 | 239 | }; |
| 232 | - case Type.Cancel: | |
| 240 | + case Type.Cancel: { | |
| 241 | + const cancelled = state.queue.find( | |
| 242 | + (item) => item.id === action.id | |
| 243 | + ); | |
| 233 | 244 | return { |
| 234 | 245 | ...state, |
| 246 | + failureCount: cancelled && !cancelled.parentId ? state.failureCount + 1 : state.failureCount, | |
| 235 | 247 | queue: state.queue.map( |
| 236 | 248 | (item) => item.id === action.id ? { |
| 237 | 249 | ...item, |
| 238 | 250 | error: action.error |
| @@ -238,8 +250,9 @@ | ||
| 238 | 250 | error: action.error |
| 239 | 251 | } : item |
| 240 | 252 | ) |
| 241 | 253 | }; |
| 254 | + } | |
| 242 | 255 | case Type.RetryItem: |
| 243 | 256 | return { |
| 244 | 257 | ...state, |
| 245 | 258 | queue: state.queue.map( |
| @@ -247,9 +260,20 @@ | ||
| 247 | 260 | ...item, |
| 248 | 261 | status: ItemStatus.Processing, |
| 249 | 262 | error: void 0, |
| 250 | 263 | retryCount: (item.retryCount ?? 0) + 1, |
| 251 | - 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 | |
| 252 | 276 | } : item |
| 253 | 277 | ) |
| 254 | 278 | }; |
| 255 | 279 | case Type.ScheduleRetry: |
| @@ -410,15 +434,18 @@ | ||
| 410 | 434 | var private_selectors_exports = {}; |
| 411 | 435 | __export(private_selectors_exports, { |
| 412 | 436 | getActiveImageProcessingCount: () => getActiveImageProcessingCount, |
| 413 | 437 | getActiveUploadCount: () => getActiveUploadCount, |
| 438 | + getActiveVideoProcessingCount: () => getActiveVideoProcessingCount, | |
| 414 | 439 | getAllItems: () => getAllItems, |
| 415 | 440 | getBlobUrls: () => getBlobUrls, |
| 416 | 441 | getFailedItems: () => getFailedItems, |
| 442 | + getFailureCount: () => getFailureCount, | |
| 417 | 443 | getItem: () => getItem, |
| 418 | 444 | getItemProgress: () => getItemProgress, |
| 419 | 445 | getPendingImageProcessing: () => getPendingImageProcessing, |
| 420 | 446 | getPendingUploads: () => getPendingUploads, |
| 447 | + getPendingVideoProcessing: () => getPendingVideoProcessing, | |
| 421 | 448 | hasPendingItemsByParentId: () => hasPendingItemsByParentId, |
| 422 | 449 | isBatchUploaded: () => isBatchUploaded, |
| 423 | 450 | isPaused: () => isPaused |
| 424 | 451 | }); |
| @@ -424,8 +451,11 @@ | ||
| 424 | 451 | }); |
| 425 | 452 | function getAllItems(state) { |
| 426 | 453 | return state.queue; |
| 427 | 454 | } |
| 455 | + function getFailureCount(state) { | |
| 456 | + return state.failureCount; | |
| 457 | + } | |
| 428 | 458 | function getItem(state, id) { |
| 429 | 459 | return state.queue.find((item) => item.id === id); |
| 430 | 460 | } |
| 431 | 461 | function isBatchUploaded(state, batchId) { |
| @@ -455,8 +485,13 @@ | ||
| 455 | 485 | return state.queue.filter( |
| 456 | 486 | (item) => item.currentOperation === OperationType.ResizeCrop || item.currentOperation === OperationType.Rotate |
| 457 | 487 | ).length; |
| 458 | 488 | } |
| 489 | + function getActiveVideoProcessingCount(state) { | |
| 490 | + return state.queue.filter( | |
| 491 | + (item) => item.currentOperation === OperationType.TranscodeGif | |
| 492 | + ).length; | |
| 493 | + } | |
| 459 | 494 | function getPendingImageProcessing(state) { |
| 460 | 495 | return state.queue.filter((item) => { |
| 461 | 496 | const nextOperation = Array.isArray(item.operations?.[0]) ? item.operations[0][0] : item.operations?.[0]; |
| 462 | 497 | return (nextOperation === OperationType.ResizeCrop || nextOperation === OperationType.Rotate) && item.currentOperation !== OperationType.ResizeCrop && item.currentOperation !== OperationType.Rotate; |
| @@ -461,8 +496,14 @@ | ||
| 461 | 496 | const nextOperation = Array.isArray(item.operations?.[0]) ? item.operations[0][0] : item.operations?.[0]; |
| 462 | 497 | return (nextOperation === OperationType.ResizeCrop || nextOperation === OperationType.Rotate) && item.currentOperation !== OperationType.ResizeCrop && item.currentOperation !== OperationType.Rotate; |
| 463 | 498 | }); |
| 464 | 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 | + } | |
| 465 | 506 | function getFailedItems(state) { |
| 466 | 507 | return state.queue.filter((item) => item.error !== void 0); |
| 467 | 508 | } |
| 468 | 509 | function hasPendingItemsByParentId(state, parentId) { |
| @@ -620,8 +661,72 @@ | ||
| 620 | 661 | } |
| 621 | 662 | function getFileBasename(name) { |
| 622 | 663 | return name.includes(".") ? name.split(".").slice(0, -1).join(".") : name; |
| 623 | 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 | + } | |
| 624 | 729 | |
| 625 | 730 | // packages/upload-media/build-module/store/utils/index.mjs |
| 626 | 731 | var vipsModulePromise; |
| 627 | 732 | var vipsModule; |
| @@ -635,9 +740,9 @@ | ||
| 635 | 740 | ); |
| 636 | 741 | } |
| 637 | 742 | return vipsModulePromise; |
| 638 | 743 | } |
| 639 | - async function vipsConvertImageFormat(id, file, type, quality, interlaced) { | |
| 744 | + async function vipsConvertImageFormat(id, file, type, options = {}) { | |
| 640 | 745 | const { vipsConvertImageFormat: convertImageFormat } = await loadVipsModule(); |
| 641 | 746 | const buffer = await convertImageFormat( |
| 642 | 747 | id, |
| 643 | 748 | await file.arrayBuffer(), |
| @@ -642,10 +747,9 @@ | ||
| 642 | 747 | id, |
| 643 | 748 | await file.arrayBuffer(), |
| 644 | 749 | file.type, |
| 645 | 750 | type, |
| 646 | - quality, | |
| 647 | - interlaced | |
| 751 | + options | |
| 648 | 752 | ); |
| 649 | 753 | const ext = type.split("/")[1]; |
| 650 | 754 | const fileName = `${getFileBasename(file.name)}.${ext}`; |
| 651 | 755 | return new File([new Blob([buffer])], fileName, { |
| @@ -663,21 +767,28 @@ | ||
| 663 | 767 | async function vipsGetUltraHdrInfo(buffer) { |
| 664 | 768 | const { vipsGetUltraHdrInfo: getUltraHdrInfo } = await loadVipsModule(); |
| 665 | 769 | return getUltraHdrInfo(buffer); |
| 666 | 770 | } |
| 667 | - async function vipsResizeImage(id, file, resize, smartCrop, addSuffix, signal, scaledSuffix, quality) { | |
| 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; | |
| 668 | 781 | if (signal?.aborted) { |
| 669 | 782 | throw new Error("Operation aborted"); |
| 670 | 783 | } |
| 671 | 784 | const { vipsResizeImage: resizeImage } = await loadVipsModule(); |
| 672 | - const { buffer, width, height, originalWidth, originalHeight } = await resizeImage( | |
| 673 | - id, | |
| 674 | - await file.arrayBuffer(), | |
| 675 | - file.type, | |
| 676 | - resize, | |
| 785 | + const { buffer, width, height, originalWidth, originalHeight } = await resizeImage(id, await file.arrayBuffer(), file.type, resize, { | |
| 677 | 786 | smartCrop, |
| 678 | - quality | |
| 679 | - ); | |
| 787 | + quality, | |
| 788 | + stripMeta, | |
| 789 | + maxBitdepth | |
| 790 | + }); | |
| 680 | 791 | let fileName = file.name; |
| 681 | 792 | const wasResized = originalWidth > width || originalHeight > height; |
| 682 | 793 | if (wasResized) { |
| 683 | 794 | const basename = getFileBasename(file.name); |
| @@ -754,9 +865,162 @@ | ||
| 754 | 865 | completedVipsOperations = 0; |
| 755 | 866 | } |
| 756 | 867 | } |
| 757 | 868 | |
| 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 | + } | |
| 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 | + | |
| 758 | 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 || {}); | |
| 759 | 1023 | var UploadError = class extends Error { |
| 760 | 1024 | code; |
| 761 | 1025 | file; |
| 762 | 1026 | constructor({ code, message, file, cause }) { |
| @@ -780,9 +1044,9 @@ | ||
| 780 | 1044 | return file.type.startsWith(`${allowedType}/`); |
| 781 | 1045 | }); |
| 782 | 1046 | if (file.type && !isAllowedType) { |
| 783 | 1047 | throw new UploadError({ |
| 784 | - code: "MIME_TYPE_NOT_SUPPORTED", | |
| 1048 | + code: ErrorCode.MIME_TYPE_NOT_SUPPORTED, | |
| 785 | 1049 | message: (0, import_i18n2.sprintf)( |
| 786 | 1050 | // translators: %s: file name. |
| 787 | 1051 | (0, import_i18n2.__)("%s: Sorry, this file type is not supported here."), |
| 788 | 1052 | file.name |
| @@ -824,9 +1088,9 @@ | ||
| 824 | 1088 | file.type |
| 825 | 1089 | ); |
| 826 | 1090 | if (file.type && !isAllowedMimeTypeForUser) { |
| 827 | 1091 | throw new UploadError({ |
| 828 | - code: "MIME_TYPE_NOT_ALLOWED_FOR_USER", | |
| 1092 | + code: ErrorCode.MIME_TYPE_NOT_ALLOWED_FOR_USER, | |
| 829 | 1093 | message: (0, import_i18n3.sprintf)( |
| 830 | 1094 | // translators: %s: file name. |
| 831 | 1095 | (0, import_i18n3.__)( |
| 832 | 1096 | "%s: Sorry, you are not allowed to upload this file type." |
| @@ -842,9 +1106,9 @@ | ||
| 842 | 1106 | var import_i18n4 = __toESM(require_i18n(), 1); |
| 843 | 1107 | function validateFileSize(file, maxUploadFileSize) { |
| 844 | 1108 | if (file.size <= 0) { |
| 845 | 1109 | throw new UploadError({ |
| 846 | - code: "EMPTY_FILE", | |
| 1110 | + code: ErrorCode.EMPTY_FILE, | |
| 847 | 1111 | message: (0, import_i18n4.sprintf)( |
| 848 | 1112 | // translators: %s: file name. |
| 849 | 1113 | (0, import_i18n4.__)("%s: This file is empty."), |
| 850 | 1114 | file.name |
| @@ -853,9 +1117,9 @@ | ||
| 853 | 1117 | }); |
| 854 | 1118 | } |
| 855 | 1119 | if (maxUploadFileSize && file.size > maxUploadFileSize) { |
| 856 | 1120 | throw new UploadError({ |
| 857 | - code: "SIZE_ABOVE_LIMIT", | |
| 1121 | + code: ErrorCode.SIZE_ABOVE_LIMIT, | |
| 858 | 1122 | message: (0, import_i18n4.sprintf)( |
| 859 | 1123 | // translators: %s: file name. |
| 860 | 1124 | (0, import_i18n4.__)( |
| 861 | 1125 | "%s: This file exceeds the maximum upload size for this site." |
| @@ -930,9 +1194,12 @@ | ||
| 930 | 1194 | } |
| 931 | 1195 | } |
| 932 | 1196 | } |
| 933 | 1197 | item.abortController?.abort(); |
| 934 | - await vipsCancelOperations(id); | |
| 1198 | + vipsCancelOperations(id).catch(() => { | |
| 1199 | + }); | |
| 1200 | + cancelGifToVideoOperations(id).catch(() => { | |
| 1201 | + }); | |
| 935 | 1202 | if (!silent) { |
| 936 | 1203 | const { onError } = item; |
| 937 | 1204 | onError?.(error ?? new Error("Upload cancelled")); |
| 938 | 1205 | if (!onError && error && !item.parentId) { |
| @@ -937,8 +1204,12 @@ | ||
| 937 | 1204 | onError?.(error ?? new Error("Upload cancelled")); |
| 938 | 1205 | if (!onError && error && !item.parentId) { |
| 939 | 1206 | console.error("Upload cancelled", error); |
| 940 | 1207 | } |
| 1208 | + } else { | |
| 1209 | + debug( | |
| 1210 | + `Item cancelled: ${item.file.name} (item ${id}): ${error instanceof Error ? error.message : error}` | |
| 1211 | + ); | |
| 941 | 1212 | } |
| 942 | 1213 | const { currentOperation, parentId, batchId } = item; |
| 943 | 1214 | dispatch({ |
| 944 | 1215 | type: Type.Cancel, |
| @@ -956,8 +1227,13 @@ | ||
| 956 | 1227 | for (const pending of select2.getPendingUploads()) { |
| 957 | 1228 | dispatch.processItem(pending.id); |
| 958 | 1229 | } |
| 959 | 1230 | } |
| 1231 | + if (currentOperation === OperationType.TranscodeGif) { | |
| 1232 | + for (const pending of select2.getPendingVideoProcessing()) { | |
| 1233 | + dispatch.processItem(pending.id); | |
| 1234 | + } | |
| 1235 | + } | |
| 960 | 1236 | if (currentOperation === OperationType.ResizeCrop || currentOperation === OperationType.Rotate || currentOperation === OperationType.TranscodeImage) { |
| 961 | 1237 | maybeRecycleVipsWorker(select2.getActiveImageProcessingCount()); |
| 962 | 1238 | } |
| 963 | 1239 | if (parentId) { |
| @@ -962,13 +1238,14 @@ | ||
| 962 | 1238 | } |
| 963 | 1239 | if (parentId) { |
| 964 | 1240 | const parentItem = select2.getItem(parentId); |
| 965 | 1241 | if (parentItem) { |
| 1242 | + const isOptionalCompanion = item.additionalData?.image_size === "animated_video" || item.additionalData?.image_size === "animated_video_poster"; | |
| 966 | 1243 | if (select2.hasPendingItemsByParentId(parentId)) { |
| 967 | 1244 | if (parentItem.operations && parentItem.operations.length > 0) { |
| 968 | 1245 | dispatch.processItem(parentId); |
| 969 | 1246 | } |
| 970 | - } else if (parentItem.subSizes && parentItem.subSizes.length > 0) { | |
| 1247 | + } else if (parentItem.subSizes && parentItem.subSizes.length > 0 || isOptionalCompanion) { | |
| 971 | 1248 | if (parentItem.operations && parentItem.operations.length > 0) { |
| 972 | 1249 | dispatch.processItem(parentId); |
| 973 | 1250 | } |
| 974 | 1251 | } else { |
| @@ -977,12 +1254,12 @@ | ||
| 977 | 1254 | if (parentAttachmentId && mediaDelete) { |
| 978 | 1255 | mediaDelete(parentAttachmentId).catch(() => { |
| 979 | 1256 | }); |
| 980 | 1257 | } |
| 981 | - dispatch.cancelItem( | |
| 1258 | + await dispatch.cancelItem( | |
| 982 | 1259 | parentId, |
| 983 | 1260 | new UploadError({ |
| 984 | - code: error instanceof UploadError && error.code || "UPLOAD_ERROR", | |
| 1261 | + code: error instanceof UploadError && error.code || ErrorCode.GENERAL, | |
| 985 | 1262 | message: error?.message || (0, import_i18n5.__)("The image could not be uploaded."), |
| 986 | 1263 | file: parentItem.file, |
| 987 | 1264 | cause: error instanceof Error ? error : void 0 |
| 988 | 1265 | }) |
| @@ -990,8 +1267,9 @@ | ||
| 990 | 1267 | } |
| 991 | 1268 | } |
| 992 | 1269 | } |
| 993 | 1270 | if (batchId && select2.isBatchUploaded(batchId)) { |
| 1271 | + debug(`Batch completed: ${batchId}`); | |
| 994 | 1272 | item.onBatchSuccess?.(); |
| 995 | 1273 | } |
| 996 | 1274 | }; |
| 997 | 1275 | } |
| @@ -1080,8 +1358,9 @@ | ||
| 1080 | 1358 | resumeQueue: () => resumeQueue, |
| 1081 | 1359 | revokeBlobUrls: () => revokeBlobUrls, |
| 1082 | 1360 | rotateItem: () => rotateItem, |
| 1083 | 1361 | sideloadItem: () => sideloadItem, |
| 1362 | + transcodeGifItem: () => transcodeGifItem, | |
| 1084 | 1363 | transcodeImageItem: () => transcodeImageItem, |
| 1085 | 1364 | updateItemProgress: () => updateItemProgress, |
| 1086 | 1365 | updateSettings: () => updateSettings, |
| 1087 | 1366 | uploadItem: () => uploadItem |
| @@ -1086,9 +1365,9 @@ | ||
| 1086 | 1365 | updateSettings: () => updateSettings, |
| 1087 | 1366 | uploadItem: () => uploadItem |
| 1088 | 1367 | }); |
| 1089 | 1368 | var import_blob = __toESM(require_blob(), 1); |
| 1090 | - var import_i18n6 = __toESM(require_i18n(), 1); | |
| 1369 | + var import_i18n7 = __toESM(require_i18n(), 1); | |
| 1091 | 1370 | |
| 1092 | 1371 | // packages/upload-media/build-module/heic-parser.mjs |
| 1093 | 1372 | var Reader = class { |
| 1094 | 1373 | view; |
| @@ -1359,8 +1638,15 @@ | ||
| 1359 | 1638 | return `hvc1.${spacePrefix}${profileIdc}.${compatHex}.${tierChar}${levelIdc}${constraintStr}`; |
| 1360 | 1639 | } |
| 1361 | 1640 | function readItemData(buffer, loc, idatOffset) { |
| 1362 | 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 | + } | |
| 1363 | 1649 | if (loc.extents.length === 1) { |
| 1364 | 1650 | const ext = loc.extents[0]; |
| 1365 | 1651 | const start = baseOffset + ext.offset; |
| 1366 | 1652 | return new Uint8Array(buffer.slice(start, start + ext.length)); |
| @@ -1495,9 +1781,10 @@ | ||
| 1495 | 1781 | tileWidth: width, |
| 1496 | 1782 | tileHeight: height, |
| 1497 | 1783 | outputWidth: width, |
| 1498 | 1784 | outputHeight: height, |
| 1499 | - rotation | |
| 1785 | + rotation, | |
| 1786 | + exifOrientation: rotation === 0 ? getUnappliedExifOrientation(buffer) : 1 | |
| 1500 | 1787 | }; |
| 1501 | 1788 | } |
| 1502 | 1789 | function parseGridImage(r, buffer, gridItemId, locations, allAssoc, properties, irefBox, idatOffset) { |
| 1503 | 1790 | const gridLoc = locations.get(gridItemId); |
| @@ -1588,13 +1875,269 @@ | ||
| 1588 | 1875 | tileWidth, |
| 1589 | 1876 | tileHeight, |
| 1590 | 1877 | outputWidth, |
| 1591 | 1878 | outputHeight, |
| 1592 | - rotation | |
| 1879 | + rotation, | |
| 1880 | + exifOrientation: rotation === 0 ? getUnappliedExifOrientation(buffer) : 1 | |
| 1593 | 1881 | }; |
| 1594 | 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 | + } | |
| 1595 | 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 | + | |
| 1596 | 2136 | // packages/upload-media/build-module/canvas-utils.mjs |
| 2137 | + var IMAGE_DECODER_TIMEOUT = 15e3; | |
| 2138 | + var HeicUnsupportedError = class extends Error { | |
| 2139 | + }; | |
| 1597 | 2140 | async function canvasConvertToJpeg(file, quality = 0.82) { |
| 1598 | 2141 | const baseName = getFileBasename(file.name); |
| 1599 | 2142 | try { |
| 1600 | 2143 | const bitmap = await createImageBitmap(file); |
| @@ -1623,10 +2166,36 @@ | ||
| 1623 | 2166 | const decoder = new ImageDecoder({ |
| 1624 | 2167 | type: file.type, |
| 1625 | 2168 | data: file.stream() |
| 1626 | 2169 | }); |
| 2170 | + let videoFrame; | |
| 2171 | + let timeoutId; | |
| 2172 | + let timedOut = false; | |
| 1627 | 2173 | try { |
| 1628 | - 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) { | |
| 1629 | 2198 | try { |
| 1630 | 2199 | const canvas = new OffscreenCanvas( |
| 1631 | 2200 | videoFrame.displayWidth, |
| 1632 | 2201 | videoFrame.displayHeight |
| @@ -1645,57 +2214,62 @@ | ||
| 1645 | 2214 | }); |
| 1646 | 2215 | } finally { |
| 1647 | 2216 | videoFrame.close(); |
| 1648 | 2217 | } |
| 1649 | - } finally { | |
| 1650 | - decoder.close(); | |
| 1651 | 2218 | } |
| 1652 | 2219 | } |
| 1653 | 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 | + } | |
| 1654 | 2229 | if (typeof VideoDecoder !== "undefined") { |
| 2230 | + let supported = false; | |
| 1655 | 2231 | try { |
| 1656 | - const heicData = parseHeic(await file.arrayBuffer()); | |
| 1657 | 2232 | const support = await VideoDecoder.isConfigSupported({ |
| 1658 | 2233 | codec: heicData.codecString |
| 1659 | 2234 | }); |
| 1660 | - if (support.supported) { | |
| 1661 | - const canvas = new OffscreenCanvas( | |
| 1662 | - heicData.outputWidth, | |
| 1663 | - 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 | |
| 1664 | 2254 | ); |
| 1665 | - const ctx = canvas.getContext("2d"); | |
| 1666 | - if (!ctx) { | |
| 1667 | - throw new Error("Could not get canvas 2d context"); | |
| 2255 | + try { | |
| 2256 | + ctx.drawImage(frame, tile.x, tile.y); | |
| 2257 | + } finally { | |
| 2258 | + frame.close(); | |
| 1668 | 2259 | } |
| 1669 | - for (const tile of heicData.tiles) { | |
| 1670 | - const frame = await decodeHevcFrame( | |
| 1671 | - heicData.codecString, | |
| 1672 | - heicData.description, | |
| 1673 | - heicData.tileWidth, | |
| 1674 | - heicData.tileHeight, | |
| 1675 | - tile.data | |
| 1676 | - ); | |
| 1677 | - try { | |
| 1678 | - ctx.drawImage(frame, tile.x, tile.y); | |
| 1679 | - } finally { | |
| 1680 | - frame.close(); | |
| 1681 | - } | |
| 1682 | - } | |
| 1683 | - const outputCanvas = applyRotation(canvas, heicData.rotation); | |
| 1684 | - const jpegBlob = await outputCanvas.convertToBlob({ | |
| 1685 | - type: "image/jpeg", | |
| 1686 | - quality | |
| 1687 | - }); | |
| 1688 | - return new File([jpegBlob], `${baseName}.jpg`, { | |
| 1689 | - type: "image/jpeg" | |
| 1690 | - }); | |
| 1691 | 2260 | } |
| 1692 | - } 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 | + }); | |
| 1693 | 2269 | } |
| 1694 | 2270 | } |
| 1695 | - throw new Error( | |
| 1696 | - "This browser cannot decode HEIC images. Please use Safari or convert to JPEG before uploading." | |
| 1697 | - ); | |
| 2271 | + throw new HeicUnsupportedError(getHeicUnsupportedMessage()); | |
| 1698 | 2272 | } |
| 1699 | 2273 | function applyRotation(source, rotation) { |
| 1700 | 2274 | if (rotation === 0) { |
| 1701 | 2275 | return source; |
| @@ -1712,8 +2286,45 @@ | ||
| 1712 | 2286 | ctx.rotate(-rotation * Math.PI / 180); |
| 1713 | 2287 | ctx.drawImage(source, -source.width / 2, -source.height / 2); |
| 1714 | 2288 | return rotated; |
| 1715 | 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 | + } | |
| 1716 | 2327 | function decodeHevcFrame(codec, description, width, height, data) { |
| 1717 | 2328 | return new Promise((resolve, reject) => { |
| 1718 | 2329 | const decoder = new VideoDecoder({ |
| 1719 | 2330 | output: (frame) => { |
| @@ -2028,8 +2639,11 @@ | ||
| 2028 | 2639 | const item = select2.getItem(id); |
| 2029 | 2640 | if (!item) { |
| 2030 | 2641 | return; |
| 2031 | 2642 | } |
| 2643 | + if (item.currentOperation) { | |
| 2644 | + return; | |
| 2645 | + } | |
| 2032 | 2646 | const { |
| 2033 | 2647 | attachment, |
| 2034 | 2648 | onChange, |
| 2035 | 2649 | onSuccess, |
| @@ -2052,8 +2666,14 @@ | ||
| 2052 | 2666 | if (activeCount >= settings.maxConcurrentImageProcessing) { |
| 2053 | 2667 | return; |
| 2054 | 2668 | } |
| 2055 | 2669 | } |
| 2670 | + if (operation === OperationType.TranscodeGif) { | |
| 2671 | + const activeCount = select2.getActiveVideoProcessingCount(); | |
| 2672 | + if (activeCount >= 1) { | |
| 2673 | + return; | |
| 2674 | + } | |
| 2675 | + } | |
| 2056 | 2676 | if (attachment) { |
| 2057 | 2677 | const isHeicUrl = attachment.url && /\.hei[cf]$/i.test(attachment.url); |
| 2058 | 2678 | if (!isHeicUrl) { |
| 2059 | 2679 | onChange?.([attachment]); |
| @@ -2097,8 +2717,11 @@ | ||
| 2097 | 2717 | type: Type.OperationStart, |
| 2098 | 2718 | id, |
| 2099 | 2719 | operation |
| 2100 | 2720 | }); |
| 2721 | + debug( | |
| 2722 | + `Starting operation ${operation} for ${item.file.name} (item ${item.id})` | |
| 2723 | + ); | |
| 2101 | 2724 | switch (operation) { |
| 2102 | 2725 | case OperationType.Prepare: |
| 2103 | 2726 | dispatch.prepareItem(item.id); |
| 2104 | 2727 | break; |
| @@ -2119,8 +2742,14 @@ | ||
| 2119 | 2742 | item.id, |
| 2120 | 2743 | operationArgs |
| 2121 | 2744 | ); |
| 2122 | 2745 | break; |
| 2746 | + case OperationType.TranscodeGif: | |
| 2747 | + dispatch.transcodeGifItem( | |
| 2748 | + item.id, | |
| 2749 | + operationArgs | |
| 2750 | + ); | |
| 2751 | + break; | |
| 2123 | 2752 | case OperationType.Upload: |
| 2124 | 2753 | if (item.parentId) { |
| 2125 | 2754 | dispatch.sideloadItem(id); |
| 2126 | 2755 | } else { |
| @@ -2179,8 +2808,9 @@ | ||
| 2179 | 2808 | id |
| 2180 | 2809 | }); |
| 2181 | 2810 | if (select2.getAllItems().length === 0) { |
| 2182 | 2811 | terminateVipsWorker(); |
| 2812 | + terminateVideoConversionWorker(); | |
| 2183 | 2813 | } |
| 2184 | 2814 | }; |
| 2185 | 2815 | } |
| 2186 | 2816 | function finishOperation(id, updates) { |
| @@ -2204,8 +2834,14 @@ | ||
| 2204 | 2834 | for (const pendingItem of pendingItems) { |
| 2205 | 2835 | dispatch.processItem(pendingItem.id); |
| 2206 | 2836 | } |
| 2207 | 2837 | } |
| 2838 | + if (previousOperation === OperationType.TranscodeGif) { | |
| 2839 | + const pendingItems = select2.getPendingVideoProcessing(); | |
| 2840 | + for (const pendingItem of pendingItems) { | |
| 2841 | + dispatch.processItem(pendingItem.id); | |
| 2842 | + } | |
| 2843 | + } | |
| 2208 | 2844 | if (previousOperation === OperationType.ResizeCrop || previousOperation === OperationType.Rotate || previousOperation === OperationType.TranscodeImage) { |
| 2209 | 2845 | maybeRecycleVipsWorker(select2.getActiveImageProcessingCount()); |
| 2210 | 2846 | } |
| 2211 | 2847 | }; |
| @@ -2213,9 +2849,9 @@ | ||
| 2213 | 2849 | var VALID_IMAGE_FORMATS = ["jpeg", "webp", "avif", "png", "gif"]; |
| 2214 | 2850 | function isValidImageFormat(format) { |
| 2215 | 2851 | return VALID_IMAGE_FORMATS.includes(format); |
| 2216 | 2852 | } |
| 2217 | - async function getTranscodeImageOperation(file, outputMimeType, interlaced = false) { | |
| 2853 | + async function getTranscodeImageOperation(file, outputMimeType, interlaced = false, quality = DEFAULT_OUTPUT_QUALITY) { | |
| 2218 | 2854 | if (file.type === "image/png" && outputMimeType === "image/jpeg") { |
| 2219 | 2855 | const blobUrl = (0, import_blob.createBlobURL)(file); |
| 2220 | 2856 | try { |
| 2221 | 2857 | const hasAlpha = await vipsHasTransparency(blobUrl); |
| @@ -2235,9 +2871,9 @@ | ||
| 2235 | 2871 | return [ |
| 2236 | 2872 | OperationType.TranscodeImage, |
| 2237 | 2873 | { |
| 2238 | 2874 | outputFormat: formatPart, |
| 2239 | - outputQuality: DEFAULT_OUTPUT_QUALITY, | |
| 2875 | + outputQuality: quality, | |
| 2240 | 2876 | interlaced |
| 2241 | 2877 | } |
| 2242 | 2878 | ]; |
| 2243 | 2879 | } |
| @@ -2249,14 +2885,52 @@ | ||
| 2249 | 2885 | } |
| 2250 | 2886 | const { file } = item; |
| 2251 | 2887 | const operations = []; |
| 2252 | 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 | + } | |
| 2253 | 2924 | let heicJpeg = null; |
| 2254 | - const isImage = file.type.startsWith("image/"); | |
| 2255 | - const isVipsSupported = CLIENT_SIDE_SUPPORTED_MIME_TYPES.includes( | |
| 2256 | - file.type | |
| 2257 | - ); | |
| 2258 | - 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; | |
| 2259 | 2933 | let tooLargeForClient = false; |
| 2260 | 2934 | if (isImage && isVipsSupported) { |
| 2261 | 2935 | const dimensions = await getImageDimensions(file); |
| 2262 | 2936 | if (dimensions && exceedsClientProcessingMemory(dimensions)) { |
| @@ -2262,9 +2936,9 @@ | ||
| 2262 | 2936 | if (dimensions && exceedsClientProcessingMemory(dimensions)) { |
| 2263 | 2937 | tooLargeForClient = true; |
| 2264 | 2938 | } |
| 2265 | 2939 | } |
| 2266 | - if (file.type === "image/jpeg" && !tooLargeForClient) { | |
| 2940 | + if (file.type === "image/jpeg" && !isHeic && !tooLargeForClient) { | |
| 2267 | 2941 | operations.push(OperationType.DetectUltraHdr); |
| 2268 | 2942 | } |
| 2269 | 2943 | if (isImage && isVipsSupported && !tooLargeForClient) { |
| 2270 | 2944 | operations.push( |
| @@ -2274,18 +2948,22 @@ | ||
| 2274 | 2948 | ); |
| 2275 | 2949 | } else if (isImage && isHeic) { |
| 2276 | 2950 | try { |
| 2277 | 2951 | heicJpeg = await canvasConvertToJpeg( |
| 2278 | - file, | |
| 2952 | + heicFile, | |
| 2279 | 2953 | settings.imageQuality ?? DEFAULT_OUTPUT_QUALITY |
| 2280 | 2954 | ); |
| 2281 | - } catch { | |
| 2955 | + } catch (error) { | |
| 2956 | + const unsupported = error instanceof HeicUnsupportedError; | |
| 2282 | 2957 | dispatch.cancelItem( |
| 2283 | 2958 | id, |
| 2284 | 2959 | new UploadError({ |
| 2285 | - code: "HEIC_DECODE_ERROR", | |
| 2286 | - message: "This browser cannot decode HEIC images and the server does not support them either. Please convert to JPEG before uploading.", | |
| 2287 | - 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 | |
| 2288 | 2966 | }) |
| 2289 | 2967 | ); |
| 2290 | 2968 | return; |
| 2291 | 2969 | } |
| @@ -2307,9 +2985,9 @@ | ||
| 2307 | 2985 | const vipsAvailable = isClientSideMediaSupported(); |
| 2308 | 2986 | updates = { |
| 2309 | 2987 | file: heicJpeg, |
| 2310 | 2988 | sourceFile: heicJpeg, |
| 2311 | - originalHeicFile: item.file, | |
| 2989 | + originalHeicFile: heicFile, | |
| 2312 | 2990 | additionalData: { |
| 2313 | 2991 | ...item.additionalData, |
| 2314 | 2992 | generate_sub_sizes: !vipsAvailable, |
| 2315 | 2993 | convert_format: true |
| @@ -2357,23 +3035,42 @@ | ||
| 2357 | 3035 | const item = select2.getItem(id); |
| 2358 | 3036 | if (!item) { |
| 2359 | 3037 | return; |
| 2360 | 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 | + }; | |
| 2361 | 3057 | select2.getSettings().mediaUpload({ |
| 2362 | 3058 | filesList: [item.file], |
| 2363 | 3059 | additionalData: item.additionalData, |
| 2364 | 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, | |
| 2365 | 3066 | onFileChange: ([attachment]) => { |
| 2366 | 3067 | if (attachment && !(0, import_blob.isBlobURL)(attachment.url)) { |
| 2367 | - dispatch.finishOperation(id, { | |
| 2368 | - attachment | |
| 2369 | - }); | |
| 3068 | + finishUpload(attachment); | |
| 2370 | 3069 | } |
| 2371 | 3070 | }, |
| 2372 | 3071 | onSuccess: ([attachment]) => { |
| 2373 | - dispatch.finishOperation(id, { | |
| 2374 | - attachment | |
| 2375 | - }); | |
| 3072 | + finishUpload(attachment); | |
| 2376 | 3073 | }, |
| 2377 | 3074 | onError: (error) => { |
| 2378 | 3075 | dispatch.cancelItem(id, error); |
| 2379 | 3076 | } |
| @@ -2391,8 +3088,9 @@ | ||
| 2391 | 3088 | if (!mediaSideload) { |
| 2392 | 3089 | dispatch.finishOperation(id, {}); |
| 2393 | 3090 | return; |
| 2394 | 3091 | } |
| 3092 | + const startTime = performance.now(); | |
| 2395 | 3093 | mediaSideload({ |
| 2396 | 3094 | file: item.file, |
| 2397 | 3095 | attachmentId: post, |
| 2398 | 3096 | additionalData, |
| @@ -2397,8 +3095,17 @@ | ||
| 2397 | 3095 | attachmentId: post, |
| 2398 | 3096 | additionalData, |
| 2399 | 3097 | signal: item.abortController?.signal, |
| 2400 | 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 | + }); | |
| 2401 | 3108 | if (item.parentId) { |
| 2402 | 3109 | dispatch({ |
| 2403 | 3110 | type: Type.AccumulateSubSize, |
| 2404 | 3111 | id: item.parentId, |
| @@ -2424,21 +3131,36 @@ | ||
| 2424 | 3131 | file: item.file |
| 2425 | 3132 | }); |
| 2426 | 3133 | return; |
| 2427 | 3134 | } |
| 3135 | + const startTime = performance.now(); | |
| 2428 | 3136 | const addSuffix = Boolean(item.parentId); |
| 2429 | 3137 | const scaledSuffix = Boolean(args.isThresholdResize); |
| 3138 | + const { imageStripMeta, imageMaxBitDepth } = select2.getSettings(); | |
| 2430 | 3139 | try { |
| 2431 | 3140 | const file = await vipsResizeImage( |
| 2432 | 3141 | item.id, |
| 2433 | 3142 | item.file, |
| 2434 | 3143 | args.resize, |
| 2435 | - false, | |
| 2436 | - // smartCrop | |
| 2437 | - addSuffix, | |
| 2438 | - item.abortController?.signal, | |
| 2439 | - 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 | + } | |
| 2440 | 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 | + }); | |
| 2441 | 3163 | const blobUrl = (0, import_blob.createBlobURL)(file); |
| 2442 | 3164 | dispatch({ |
| 2443 | 3165 | type: Type.CacheBlobUrl, |
| 2444 | 3166 | id, |
| @@ -2453,10 +3175,10 @@ | ||
| 2453 | 3175 | } catch (error) { |
| 2454 | 3176 | dispatch.cancelItem( |
| 2455 | 3177 | id, |
| 2456 | 3178 | new UploadError({ |
| 2457 | - code: "IMAGE_TRANSCODING_ERROR", | |
| 2458 | - message: (0, import_i18n6.__)( | |
| 3179 | + code: ErrorCode.IMAGE_TRANSCODING_ERROR, | |
| 3180 | + message: (0, import_i18n7.__)( | |
| 2459 | 3181 | "The web server cannot generate responsive image sizes for this image. Convert it to JPEG or PNG before uploading." |
| 2460 | 3182 | ), |
| 2461 | 3183 | file: item.file, |
| 2462 | 3184 | cause: error instanceof Error ? error : void 0 |
| @@ -2476,8 +3198,9 @@ | ||
| 2476 | 3198 | file: item.file |
| 2477 | 3199 | }); |
| 2478 | 3200 | return; |
| 2479 | 3201 | } |
| 3202 | + const startTime = performance.now(); | |
| 2480 | 3203 | try { |
| 2481 | 3204 | const file = await vipsRotateImage( |
| 2482 | 3205 | item.id, |
| 2483 | 3206 | item.file, |
| @@ -2483,8 +3206,17 @@ | ||
| 2483 | 3206 | item.file, |
| 2484 | 3207 | args.orientation, |
| 2485 | 3208 | item.abortController?.signal |
| 2486 | 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 | + }); | |
| 2487 | 3219 | const blobUrl = (0, import_blob.createBlobURL)(file); |
| 2488 | 3220 | dispatch({ |
| 2489 | 3221 | type: Type.CacheBlobUrl, |
| 2490 | 3222 | id, |
| @@ -2499,10 +3231,10 @@ | ||
| 2499 | 3231 | } catch (error) { |
| 2500 | 3232 | dispatch.cancelItem( |
| 2501 | 3233 | id, |
| 2502 | 3234 | new UploadError({ |
| 2503 | - code: "IMAGE_ROTATION_ERROR", | |
| 2504 | - message: (0, import_i18n6.__)( | |
| 3235 | + code: ErrorCode.IMAGE_ROTATION_ERROR, | |
| 3236 | + message: (0, import_i18n7.__)( | |
| 2505 | 3237 | "The web server cannot generate responsive image sizes for this image. Convert it to JPEG or PNG before uploading." |
| 2506 | 3238 | ), |
| 2507 | 3239 | file: item.file, |
| 2508 | 3240 | cause: error instanceof Error ? error : void 0 |
| @@ -2522,19 +3254,34 @@ | ||
| 2522 | 3254 | file: item.file |
| 2523 | 3255 | }); |
| 2524 | 3256 | return; |
| 2525 | 3257 | } |
| 3258 | + const startTime = performance.now(); | |
| 2526 | 3259 | const outputMimeType = `image/${args.outputFormat}`; |
| 2527 | 3260 | const quality = args.outputQuality ?? DEFAULT_OUTPUT_QUALITY; |
| 2528 | 3261 | const interlaced = args.interlaced ?? false; |
| 3262 | + const { imageStripMeta, imageMaxBitDepth } = select2.getSettings(); | |
| 2529 | 3263 | try { |
| 2530 | 3264 | const file = await vipsConvertImageFormat( |
| 2531 | 3265 | item.id, |
| 2532 | 3266 | item.file, |
| 2533 | 3267 | outputMimeType, |
| 2534 | - quality, | |
| 2535 | - interlaced | |
| 3268 | + { | |
| 3269 | + quality, | |
| 3270 | + interlaced, | |
| 3271 | + stripMeta: imageStripMeta, | |
| 3272 | + maxBitdepth: imageMaxBitDepth | |
| 3273 | + } | |
| 2536 | 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 | + }); | |
| 2537 | 3284 | const blobUrl = (0, import_blob.createBlobURL)(file); |
| 2538 | 3285 | dispatch({ |
| 2539 | 3286 | type: Type.CacheBlobUrl, |
| 2540 | 3287 | id, |
| @@ -2549,9 +3296,9 @@ | ||
| 2549 | 3296 | } catch (error) { |
| 2550 | 3297 | dispatch.cancelItem( |
| 2551 | 3298 | id, |
| 2552 | 3299 | new UploadError({ |
| 2553 | - code: "MEDIA_TRANSCODING_ERROR", | |
| 3300 | + code: ErrorCode.MEDIA_TRANSCODING_ERROR, | |
| 2554 | 3301 | message: "Image could not be transcoded to the target format", |
| 2555 | 3302 | file: item.file, |
| 2556 | 3303 | cause: error instanceof Error ? error : void 0 |
| 2557 | 3304 | }) |
| @@ -2558,8 +3305,91 @@ | ||
| 2558 | 3305 | ); |
| 2559 | 3306 | } |
| 2560 | 3307 | }; |
| 2561 | 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 | + } | |
| 2562 | 3392 | function generateThumbnails(id) { |
| 2563 | 3393 | return async ({ select: select2, dispatch }) => { |
| 2564 | 3394 | const item = select2.getItem(id); |
| 2565 | 3395 | if (!item) { |
| @@ -2577,35 +3407,57 @@ | ||
| 2577 | 3407 | batchId: v4_default(), |
| 2578 | 3408 | parentId: item.id, |
| 2579 | 3409 | additionalData: { |
| 2580 | 3410 | post: attachment.id, |
| 2581 | - image_size: "original-heic", | |
| 3411 | + image_size: "source_original", | |
| 2582 | 3412 | convert_format: false |
| 2583 | 3413 | }, |
| 2584 | 3414 | operations: [OperationType.Upload] |
| 2585 | 3415 | }); |
| 2586 | 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; | |
| 2587 | 3450 | { |
| 2588 | - const needsRotation = attachment.exif_orientation && attachment.exif_orientation !== 1 && !item.file.name.includes("-scaled"); | |
| 2589 | - if (needsRotation && attachment.id) { | |
| 3451 | + const needsRotation = exifOrientation !== 1 && !item.file.name.includes("-scaled"); | |
| 3452 | + if ((needsRotation || needsClientRotation) && attachment.id) { | |
| 2590 | 3453 | try { |
| 2591 | - const rotatedFile = await vipsRotateImage( | |
| 3454 | + rotatedSource = await vipsRotateImage( | |
| 2592 | 3455 | item.id, |
| 2593 | 3456 | item.sourceFile, |
| 2594 | - attachment.exif_orientation, | |
| 3457 | + exifOrientation, | |
| 2595 | 3458 | item.abortController?.signal |
| 2596 | 3459 | ); |
| 2597 | - dispatch.addSideloadItem({ | |
| 2598 | - file: rotatedFile, | |
| 2599 | - batchId: v4_default(), | |
| 2600 | - parentId: item.id, | |
| 2601 | - additionalData: { | |
| 2602 | - post: attachment.id, | |
| 2603 | - image_size: "original", | |
| 2604 | - convert_format: false | |
| 2605 | - }, | |
| 2606 | - operations: [OperationType.Upload] | |
| 2607 | - }); | |
| 2608 | 3460 | } catch { |
| 2609 | 3461 | console.warn( |
| 2610 | 3462 | "Failed to rotate image, continuing with thumbnails" |
| 2611 | 3463 | ); |
| @@ -2610,24 +3462,48 @@ | ||
| 2610 | 3462 | "Failed to rotate image, continuing with thumbnails" |
| 2611 | 3463 | ); |
| 2612 | 3464 | } |
| 2613 | 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 | + } | |
| 2614 | 3479 | } |
| 2615 | 3480 | if (!item.parentId && attachment.missing_image_sizes && attachment.missing_image_sizes.length > 0) { |
| 2616 | 3481 | const allImageSizes = settings.allImageSizes || {}; |
| 2617 | 3482 | const sizesToGenerate = attachment.missing_image_sizes; |
| 2618 | - const thumbnailSource = item.sourceFile; | |
| 3483 | + const thumbnailSource = needsClientRotation && rotatedSource ? rotatedSource : item.sourceFile; | |
| 2619 | 3484 | const file = attachment.filename ? renameFile(thumbnailSource, attachment.filename) : thumbnailSource; |
| 2620 | 3485 | const batchId = v4_default(); |
| 2621 | 3486 | const isUltraHdr = ultraHdrItems.has(item.id); |
| 2622 | 3487 | const outputMimeType = attachment.image_output_format; |
| 2623 | 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 | + }; | |
| 2624 | 3499 | let thumbnailTranscodeOperation = null; |
| 2625 | 3500 | if (!isUltraHdr && outputMimeType) { |
| 2626 | 3501 | thumbnailTranscodeOperation = await getTranscodeImageOperation( |
| 2627 | 3502 | thumbnailSource, |
| 2628 | 3503 | outputMimeType, |
| 2629 | - interlaced | |
| 3504 | + interlaced, | |
| 3505 | + defaultQuality | |
| 2630 | 3506 | ); |
| 2631 | 3507 | } |
| 2632 | 3508 | const dimensionGroups = /* @__PURE__ */ new Map(); |
| 2633 | 3509 | for (const name of sizesToGenerate) { |
| @@ -2647,13 +3523,23 @@ | ||
| 2647 | 3523 | } |
| 2648 | 3524 | } |
| 2649 | 3525 | for (const [, names] of dimensionGroups) { |
| 2650 | 3526 | const imageSize = allImageSizes[names[0]]; |
| 3527 | + const sizeQuality = qualityForSize(names[0]); | |
| 2651 | 3528 | const thumbnailOperations = [ |
| 2652 | - [OperationType.ResizeCrop, { resize: imageSize }] | |
| 3529 | + [ | |
| 3530 | + OperationType.ResizeCrop, | |
| 3531 | + { resize: imageSize, quality: sizeQuality } | |
| 3532 | + ] | |
| 2653 | 3533 | ]; |
| 2654 | 3534 | if (!isUltraHdr && thumbnailTranscodeOperation) { |
| 2655 | - thumbnailOperations.push(thumbnailTranscodeOperation); | |
| 3535 | + thumbnailOperations.push([ | |
| 3536 | + thumbnailTranscodeOperation[0], | |
| 3537 | + { | |
| 3538 | + ...thumbnailTranscodeOperation[1], | |
| 3539 | + outputQuality: sizeQuality | |
| 3540 | + } | |
| 3541 | + ]); | |
| 2656 | 3542 | } |
| 2657 | 3543 | thumbnailOperations.push(OperationType.Upload); |
| 2658 | 3544 | const imageSizeParam = names.length === 1 ? names[0] : names; |
| 2659 | 3545 | dispatch.addSideloadItem({ |
| @@ -2685,9 +3571,10 @@ | ||
| 2685 | 3571 | resize: { |
| 2686 | 3572 | width: bigImageSizeThreshold, |
| 2687 | 3573 | height: bigImageSizeThreshold |
| 2688 | 3574 | }, |
| 2689 | - isThresholdResize: true | |
| 3575 | + isThresholdResize: true, | |
| 3576 | + quality: defaultQuality | |
| 2690 | 3577 | } |
| 2691 | 3578 | ] |
| 2692 | 3579 | ]; |
| 2693 | 3580 | if (!isUltraHdr && thumbnailTranscodeOperation) { |
| @@ -2733,8 +3620,17 @@ | ||
| 2733 | 3620 | updates.attachment = updatedAttachment; |
| 2734 | 3621 | } |
| 2735 | 3622 | } catch (error) { |
| 2736 | 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; | |
| 2737 | 3633 | } |
| 2738 | 3634 | } |
| 2739 | 3635 | dispatch.finishOperation(id, updates); |
| 2740 | 3636 | }; |
| @@ -2840,7 +3736,115 @@ | ||
| 2840 | 3736 | }, [settings, updateSettings2]); |
| 2841 | 3737 | return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children }); |
| 2842 | 3738 | }); |
| 2843 | 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 | + } | |
| 2844 | 3846 | return __toCommonJS(index_exports); |
| 3847 | +})(); | |
| 3848 | +(window.wp ||= {}).uploadMedia = wp.uploadMedia; | |
| 2845 | 3849 | })(); |
| 2846 | 3850 | //# sourceMappingURL=index.js.map |