| @@ -1,4 +1,5 @@ | ||
| 1 | +(function() { | |
| 1 | 2 | "use strict"; |
| 2 | 3 | var wp; |
| 3 | 4 | (wp ||= {}).uploadMedia = (() => { |
| 4 | 5 | var __create = Object.create; |
| @@ -102,10 +103,13 @@ | ||
| 102 | 103 | UploadError: () => UploadError, |
| 103 | 104 | clearFeatureDetectionCache: () => clearFeatureDetectionCache, |
| 104 | 105 | detectClientSideMediaSupport: () => detectClientSideMediaSupport, |
| 105 | 106 | getErrorMessage: () => getErrorMessage, |
| 107 | + getHeicConversionAdvice: () => getHeicConversionAdvice, | |
| 108 | + getHeicUnsupportedMessage: () => getHeicUnsupportedMessage, | |
| 106 | 109 | isClientSideMediaSupported: () => isClientSideMediaSupported, |
| 107 | 110 | isHeicCanvasSupported: () => isHeicCanvasSupported, |
| 111 | + isHeicFile: () => isHeicFile, | |
| 108 | 112 | store: () => store |
| 109 | 113 | }); |
| 110 | 114 | |
| 111 | 115 | // packages/upload-media/build-module/store/index.mjs |
| @@ -190,9 +194,10 @@ | ||
| 190 | 194 | mediaUpload: noop, |
| 191 | 195 | maxConcurrentUploads: DEFAULT_MAX_CONCURRENT_UPLOADS, |
| 192 | 196 | maxConcurrentImageProcessing: DEFAULT_MAX_CONCURRENT_IMAGE_PROCESSING, |
| 193 | 197 | retry: { ...DEFAULT_RETRY_SETTINGS } |
| 194 | - } | |
| 198 | + }, | |
| 199 | + failureCount: 0 | |
| 195 | 200 | }; |
| 196 | 201 | function reducer(state = DEFAULT_STATE, action = { type: Type.Unknown }) { |
| 197 | 202 | switch (action.type) { |
| 198 | 203 | case Type.PauseQueue: { |
| @@ -231,11 +236,15 @@ | ||
| 231 | 236 | return { |
| 232 | 237 | ...state, |
| 233 | 238 | queue: [...state.queue, action.item] |
| 234 | 239 | }; |
| 235 | - case Type.Cancel: | |
| 240 | + case Type.Cancel: { | |
| 241 | + const cancelled = state.queue.find( | |
| 242 | + (item) => item.id === action.id | |
| 243 | + ); | |
| 236 | 244 | return { |
| 237 | 245 | ...state, |
| 246 | + failureCount: cancelled && !cancelled.parentId ? state.failureCount + 1 : state.failureCount, | |
| 238 | 247 | queue: state.queue.map( |
| 239 | 248 | (item) => item.id === action.id ? { |
| 240 | 249 | ...item, |
| 241 | 250 | error: action.error |
| @@ -241,8 +250,9 @@ | ||
| 241 | 250 | error: action.error |
| 242 | 251 | } : item |
| 243 | 252 | ) |
| 244 | 253 | }; |
| 254 | + } | |
| 245 | 255 | case Type.RetryItem: |
| 246 | 256 | return { |
| 247 | 257 | ...state, |
| 248 | 258 | queue: state.queue.map( |
| @@ -250,9 +260,20 @@ | ||
| 250 | 260 | ...item, |
| 251 | 261 | status: ItemStatus.Processing, |
| 252 | 262 | error: void 0, |
| 253 | 263 | retryCount: (item.retryCount ?? 0) + 1, |
| 254 | - 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 | |
| 255 | 276 | } : item |
| 256 | 277 | ) |
| 257 | 278 | }; |
| 258 | 279 | case Type.ScheduleRetry: |
| @@ -417,8 +438,9 @@ | ||
| 417 | 438 | getActiveVideoProcessingCount: () => getActiveVideoProcessingCount, |
| 418 | 439 | getAllItems: () => getAllItems, |
| 419 | 440 | getBlobUrls: () => getBlobUrls, |
| 420 | 441 | getFailedItems: () => getFailedItems, |
| 442 | + getFailureCount: () => getFailureCount, | |
| 421 | 443 | getItem: () => getItem, |
| 422 | 444 | getItemProgress: () => getItemProgress, |
| 423 | 445 | getPendingImageProcessing: () => getPendingImageProcessing, |
| 424 | 446 | getPendingUploads: () => getPendingUploads, |
| @@ -429,8 +451,11 @@ | ||
| 429 | 451 | }); |
| 430 | 452 | function getAllItems(state) { |
| 431 | 453 | return state.queue; |
| 432 | 454 | } |
| 455 | + function getFailureCount(state) { | |
| 456 | + return state.failureCount; | |
| 457 | + } | |
| 433 | 458 | function getItem(state, id) { |
| 434 | 459 | return state.queue.find((item) => item.id === id); |
| 435 | 460 | } |
| 436 | 461 | function isBatchUploaded(state, batchId) { |
| @@ -652,8 +677,56 @@ | ||
| 652 | 677 | } |
| 653 | 678 | } |
| 654 | 679 | return false; |
| 655 | 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 | + } | |
| 656 | 729 | |
| 657 | 730 | // packages/upload-media/build-module/store/utils/index.mjs |
| 658 | 731 | var vipsModulePromise; |
| 659 | 732 | var vipsModule; |
| @@ -942,8 +1015,9 @@ | ||
| 942 | 1015 | ErrorCode2["IMAGE_TRANSCODING_ERROR"] = "IMAGE_TRANSCODING_ERROR"; |
| 943 | 1016 | ErrorCode2["IMAGE_ROTATION_ERROR"] = "IMAGE_ROTATION_ERROR"; |
| 944 | 1017 | ErrorCode2["MEDIA_TRANSCODING_ERROR"] = "MEDIA_TRANSCODING_ERROR"; |
| 945 | 1018 | ErrorCode2["GIF_TRANSCODING_ERROR"] = "GIF_TRANSCODING_ERROR"; |
| 1019 | + ErrorCode2["MEDIA_FINALIZE_ERROR"] = "MEDIA_FINALIZE_ERROR"; | |
| 946 | 1020 | ErrorCode2["GENERAL"] = "GENERAL"; |
| 947 | 1021 | return ErrorCode2; |
| 948 | 1022 | })(ErrorCode || {}); |
| 949 | 1023 | var UploadError = class extends Error { |
| @@ -1291,9 +1365,9 @@ | ||
| 1291 | 1365 | updateSettings: () => updateSettings, |
| 1292 | 1366 | uploadItem: () => uploadItem |
| 1293 | 1367 | }); |
| 1294 | 1368 | var import_blob = __toESM(require_blob(), 1); |
| 1295 | - var import_i18n6 = __toESM(require_i18n(), 1); | |
| 1369 | + var import_i18n7 = __toESM(require_i18n(), 1); | |
| 1296 | 1370 | |
| 1297 | 1371 | // packages/upload-media/build-module/heic-parser.mjs |
| 1298 | 1372 | var Reader = class { |
| 1299 | 1373 | view; |
| @@ -1564,8 +1638,15 @@ | ||
| 1564 | 1638 | return `hvc1.${spacePrefix}${profileIdc}.${compatHex}.${tierChar}${levelIdc}${constraintStr}`; |
| 1565 | 1639 | } |
| 1566 | 1640 | function readItemData(buffer, loc, idatOffset) { |
| 1567 | 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 | + } | |
| 1568 | 1649 | if (loc.extents.length === 1) { |
| 1569 | 1650 | const ext = loc.extents[0]; |
| 1570 | 1651 | const start = baseOffset + ext.offset; |
| 1571 | 1652 | return new Uint8Array(buffer.slice(start, start + ext.length)); |
| @@ -1919,9 +2000,144 @@ | ||
| 1919 | 2000 | } |
| 1920 | 2001 | return parseExifOrientation(buffer); |
| 1921 | 2002 | } |
| 1922 | 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 | + | |
| 1923 | 2136 | // packages/upload-media/build-module/canvas-utils.mjs |
| 2137 | + var IMAGE_DECODER_TIMEOUT = 15e3; | |
| 2138 | + var HeicUnsupportedError = class extends Error { | |
| 2139 | + }; | |
| 1924 | 2140 | async function canvasConvertToJpeg(file, quality = 0.82) { |
| 1925 | 2141 | const baseName = getFileBasename(file.name); |
| 1926 | 2142 | try { |
| 1927 | 2143 | const bitmap = await createImageBitmap(file); |
| @@ -1950,10 +2166,36 @@ | ||
| 1950 | 2166 | const decoder = new ImageDecoder({ |
| 1951 | 2167 | type: file.type, |
| 1952 | 2168 | data: file.stream() |
| 1953 | 2169 | }); |
| 2170 | + let videoFrame; | |
| 2171 | + let timeoutId; | |
| 2172 | + let timedOut = false; | |
| 1954 | 2173 | try { |
| 1955 | - 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) { | |
| 1956 | 2198 | try { |
| 1957 | 2199 | const canvas = new OffscreenCanvas( |
| 1958 | 2200 | videoFrame.displayWidth, |
| 1959 | 2201 | videoFrame.displayHeight |
| @@ -1972,60 +2214,62 @@ | ||
| 1972 | 2214 | }); |
| 1973 | 2215 | } finally { |
| 1974 | 2216 | videoFrame.close(); |
| 1975 | 2217 | } |
| 1976 | - } finally { | |
| 1977 | - decoder.close(); | |
| 1978 | 2218 | } |
| 1979 | 2219 | } |
| 1980 | 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 | + } | |
| 1981 | 2229 | if (typeof VideoDecoder !== "undefined") { |
| 2230 | + let supported = false; | |
| 1982 | 2231 | try { |
| 1983 | - const heicData = parseHeic(await file.arrayBuffer()); | |
| 1984 | 2232 | const support = await VideoDecoder.isConfigSupported({ |
| 1985 | 2233 | codec: heicData.codecString |
| 1986 | 2234 | }); |
| 1987 | - if (support.supported) { | |
| 1988 | - const canvas = new OffscreenCanvas( | |
| 1989 | - heicData.outputWidth, | |
| 1990 | - 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 | |
| 1991 | 2254 | ); |
| 1992 | - const ctx = canvas.getContext("2d"); | |
| 1993 | - if (!ctx) { | |
| 1994 | - throw new Error("Could not get canvas 2d context"); | |
| 2255 | + try { | |
| 2256 | + ctx.drawImage(frame, tile.x, tile.y); | |
| 2257 | + } finally { | |
| 2258 | + frame.close(); | |
| 1995 | 2259 | } |
| 1996 | - for (const tile of heicData.tiles) { | |
| 1997 | - const frame = await decodeHevcFrame( | |
| 1998 | - heicData.codecString, | |
| 1999 | - heicData.description, | |
| 2000 | - heicData.tileWidth, | |
| 2001 | - heicData.tileHeight, | |
| 2002 | - tile.data | |
| 2003 | - ); | |
| 2004 | - try { | |
| 2005 | - ctx.drawImage(frame, tile.x, tile.y); | |
| 2006 | - } finally { | |
| 2007 | - frame.close(); | |
| 2008 | - } | |
| 2009 | - } | |
| 2010 | - const outputCanvas = heicData.rotation !== 0 ? applyRotation(canvas, heicData.rotation) : applyExifOrientation( | |
| 2011 | - canvas, | |
| 2012 | - heicData.exifOrientation | |
| 2013 | - ); | |
| 2014 | - const jpegBlob = await outputCanvas.convertToBlob({ | |
| 2015 | - type: "image/jpeg", | |
| 2016 | - quality | |
| 2017 | - }); | |
| 2018 | - return new File([jpegBlob], `${baseName}.jpg`, { | |
| 2019 | - type: "image/jpeg" | |
| 2020 | - }); | |
| 2021 | 2260 | } |
| 2022 | - } 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 | + }); | |
| 2023 | 2269 | } |
| 2024 | 2270 | } |
| 2025 | - throw new Error( | |
| 2026 | - "This browser cannot decode HEIC images. Please use Safari or convert to JPEG before uploading." | |
| 2027 | - ); | |
| 2271 | + throw new HeicUnsupportedError(getHeicUnsupportedMessage()); | |
| 2028 | 2272 | } |
| 2029 | 2273 | function applyRotation(source, rotation) { |
| 2030 | 2274 | if (rotation === 0) { |
| 2031 | 2275 | return source; |
| @@ -2395,8 +2639,11 @@ | ||
| 2395 | 2639 | const item = select2.getItem(id); |
| 2396 | 2640 | if (!item) { |
| 2397 | 2641 | return; |
| 2398 | 2642 | } |
| 2643 | + if (item.currentOperation) { | |
| 2644 | + return; | |
| 2645 | + } | |
| 2399 | 2646 | const { |
| 2400 | 2647 | attachment, |
| 2401 | 2648 | onChange, |
| 2402 | 2649 | onSuccess, |
| @@ -2674,13 +2921,16 @@ | ||
| 2674 | 2921 | } |
| 2675 | 2922 | } |
| 2676 | 2923 | } |
| 2677 | 2924 | let heicJpeg = null; |
| 2678 | - const isImage = file.type.startsWith("image/"); | |
| 2679 | - const isVipsSupported = CLIENT_SIDE_SUPPORTED_MIME_TYPES.includes( | |
| 2680 | - file.type | |
| 2681 | - ); | |
| 2682 | - 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; | |
| 2683 | 2933 | let tooLargeForClient = false; |
| 2684 | 2934 | if (isImage && isVipsSupported) { |
| 2685 | 2935 | const dimensions = await getImageDimensions(file); |
| 2686 | 2936 | if (dimensions && exceedsClientProcessingMemory(dimensions)) { |
| @@ -2686,9 +2936,9 @@ | ||
| 2686 | 2936 | if (dimensions && exceedsClientProcessingMemory(dimensions)) { |
| 2687 | 2937 | tooLargeForClient = true; |
| 2688 | 2938 | } |
| 2689 | 2939 | } |
| 2690 | - if (file.type === "image/jpeg" && !tooLargeForClient) { | |
| 2940 | + if (file.type === "image/jpeg" && !isHeic && !tooLargeForClient) { | |
| 2691 | 2941 | operations.push(OperationType.DetectUltraHdr); |
| 2692 | 2942 | } |
| 2693 | 2943 | if (isImage && isVipsSupported && !tooLargeForClient) { |
| 2694 | 2944 | operations.push( |
| @@ -2698,18 +2948,22 @@ | ||
| 2698 | 2948 | ); |
| 2699 | 2949 | } else if (isImage && isHeic) { |
| 2700 | 2950 | try { |
| 2701 | 2951 | heicJpeg = await canvasConvertToJpeg( |
| 2702 | - file, | |
| 2952 | + heicFile, | |
| 2703 | 2953 | settings.imageQuality ?? DEFAULT_OUTPUT_QUALITY |
| 2704 | 2954 | ); |
| 2705 | - } catch { | |
| 2955 | + } catch (error) { | |
| 2956 | + const unsupported = error instanceof HeicUnsupportedError; | |
| 2706 | 2957 | dispatch.cancelItem( |
| 2707 | 2958 | id, |
| 2708 | 2959 | new UploadError({ |
| 2709 | - code: ErrorCode.HEIC_DECODE_ERROR, | |
| 2710 | - message: "This browser cannot decode HEIC images and the server does not support them either. Please convert to JPEG before uploading.", | |
| 2711 | - 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 | |
| 2712 | 2966 | }) |
| 2713 | 2967 | ); |
| 2714 | 2968 | return; |
| 2715 | 2969 | } |
| @@ -2731,9 +2985,9 @@ | ||
| 2731 | 2985 | const vipsAvailable = isClientSideMediaSupported(); |
| 2732 | 2986 | updates = { |
| 2733 | 2987 | file: heicJpeg, |
| 2734 | 2988 | sourceFile: heicJpeg, |
| 2735 | - originalHeicFile: item.file, | |
| 2989 | + originalHeicFile: heicFile, | |
| 2736 | 2990 | additionalData: { |
| 2737 | 2991 | ...item.additionalData, |
| 2738 | 2992 | generate_sub_sizes: !vipsAvailable, |
| 2739 | 2993 | convert_format: true |
| @@ -2922,9 +3176,9 @@ | ||
| 2922 | 3176 | dispatch.cancelItem( |
| 2923 | 3177 | id, |
| 2924 | 3178 | new UploadError({ |
| 2925 | 3179 | code: ErrorCode.IMAGE_TRANSCODING_ERROR, |
| 2926 | - message: (0, import_i18n6.__)( | |
| 3180 | + message: (0, import_i18n7.__)( | |
| 2927 | 3181 | "The web server cannot generate responsive image sizes for this image. Convert it to JPEG or PNG before uploading." |
| 2928 | 3182 | ), |
| 2929 | 3183 | file: item.file, |
| 2930 | 3184 | cause: error instanceof Error ? error : void 0 |
| @@ -2978,9 +3232,9 @@ | ||
| 2978 | 3232 | dispatch.cancelItem( |
| 2979 | 3233 | id, |
| 2980 | 3234 | new UploadError({ |
| 2981 | 3235 | code: ErrorCode.IMAGE_ROTATION_ERROR, |
| 2982 | - message: (0, import_i18n6.__)( | |
| 3236 | + message: (0, import_i18n7.__)( | |
| 2983 | 3237 | "The web server cannot generate responsive image sizes for this image. Convert it to JPEG or PNG before uploading." |
| 2984 | 3238 | ), |
| 2985 | 3239 | file: item.file, |
| 2986 | 3240 | cause: error instanceof Error ? error : void 0 |
| @@ -3366,8 +3620,17 @@ | ||
| 3366 | 3620 | updates.attachment = updatedAttachment; |
| 3367 | 3621 | } |
| 3368 | 3622 | } catch (error) { |
| 3369 | 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; | |
| 3370 | 3633 | } |
| 3371 | 3634 | } |
| 3372 | 3635 | dispatch.finishOperation(id, updates); |
| 3373 | 3636 | }; |
| @@ -3475,102 +3738,113 @@ | ||
| 3475 | 3738 | }); |
| 3476 | 3739 | var provider_default = MediaUploadProvider; |
| 3477 | 3740 | |
| 3478 | 3741 | // packages/upload-media/build-module/error-messages.mjs |
| 3479 | - var import_i18n7 = __toESM(require_i18n(), 1); | |
| 3742 | + var import_i18n8 = __toESM(require_i18n(), 1); | |
| 3480 | 3743 | function getErrorMessage(code, fileName) { |
| 3481 | 3744 | const messages = { |
| 3482 | 3745 | [ErrorCode.EMPTY_FILE]: { |
| 3483 | - title: (0, import_i18n7.__)("Empty file"), | |
| 3484 | - description: (0, import_i18n7.sprintf)( | |
| 3746 | + title: (0, import_i18n8.__)("Empty file"), | |
| 3747 | + description: (0, import_i18n8.sprintf)( | |
| 3485 | 3748 | /* translators: %s: file name */ |
| 3486 | - (0, import_i18n7.__)('"%s" is empty.'), | |
| 3749 | + (0, import_i18n8.__)('"%s" is empty.'), | |
| 3487 | 3750 | fileName |
| 3488 | 3751 | ), |
| 3489 | - action: (0, import_i18n7.__)("Please choose a different file.") | |
| 3752 | + action: (0, import_i18n8.__)("Please choose a different file.") | |
| 3490 | 3753 | }, |
| 3491 | 3754 | [ErrorCode.SIZE_ABOVE_LIMIT]: { |
| 3492 | - title: (0, import_i18n7.__)("File too large"), | |
| 3493 | - description: (0, import_i18n7.sprintf)( | |
| 3755 | + title: (0, import_i18n8.__)("File too large"), | |
| 3756 | + description: (0, import_i18n8.sprintf)( | |
| 3494 | 3757 | /* translators: %s: file name */ |
| 3495 | - (0, import_i18n7.__)('"%s" exceeds the maximum upload size.'), | |
| 3758 | + (0, import_i18n8.__)('"%s" exceeds the maximum upload size.'), | |
| 3496 | 3759 | fileName |
| 3497 | 3760 | ), |
| 3498 | - action: (0, import_i18n7.__)("Please reduce the file size and try again.") | |
| 3761 | + action: (0, import_i18n8.__)("Please reduce the file size and try again.") | |
| 3499 | 3762 | }, |
| 3500 | 3763 | [ErrorCode.MIME_TYPE_NOT_SUPPORTED]: { |
| 3501 | - title: (0, import_i18n7.__)("Unsupported file type"), | |
| 3502 | - description: (0, import_i18n7.sprintf)( | |
| 3764 | + title: (0, import_i18n8.__)("Unsupported file type"), | |
| 3765 | + description: (0, import_i18n8.sprintf)( | |
| 3503 | 3766 | /* translators: %s: file name */ |
| 3504 | - (0, import_i18n7.__)('"%s" is not a supported file type.'), | |
| 3767 | + (0, import_i18n8.__)('"%s" is not a supported file type.'), | |
| 3505 | 3768 | fileName |
| 3506 | 3769 | ), |
| 3507 | - action: (0, import_i18n7.__)("Please upload a different file format.") | |
| 3770 | + action: (0, import_i18n8.__)("Please upload a different file format.") | |
| 3508 | 3771 | }, |
| 3509 | 3772 | [ErrorCode.MIME_TYPE_NOT_ALLOWED_FOR_USER]: { |
| 3510 | - title: (0, import_i18n7.__)("File type not allowed"), | |
| 3511 | - description: (0, import_i18n7.sprintf)( | |
| 3773 | + title: (0, import_i18n8.__)("File type not allowed"), | |
| 3774 | + description: (0, import_i18n8.sprintf)( | |
| 3512 | 3775 | /* translators: %s: file name */ |
| 3513 | - (0, import_i18n7.__)('You are not allowed to upload "%s".'), | |
| 3776 | + (0, import_i18n8.__)('You are not allowed to upload "%s".'), | |
| 3514 | 3777 | fileName |
| 3515 | 3778 | ), |
| 3516 | - action: (0, import_i18n7.__)("Please contact your site administrator.") | |
| 3779 | + action: (0, import_i18n8.__)("Please contact your site administrator.") | |
| 3517 | 3780 | }, |
| 3518 | 3781 | [ErrorCode.HEIC_DECODE_ERROR]: { |
| 3519 | - title: (0, import_i18n7.__)("HEIC decode failed"), | |
| 3520 | - description: (0, import_i18n7.sprintf)( | |
| 3782 | + title: (0, import_i18n8.__)("HEIC decode failed"), | |
| 3783 | + description: (0, import_i18n8.sprintf)( | |
| 3521 | 3784 | /* translators: %s: file name */ |
| 3522 | - (0, import_i18n7.__)('Failed to decode HEIC file "%s".'), | |
| 3785 | + (0, import_i18n8.__)('Failed to decode HEIC file "%s".'), | |
| 3523 | 3786 | fileName |
| 3524 | 3787 | ), |
| 3525 | - action: (0, import_i18n7.__)("Try converting the image to JPEG or PNG first.") | |
| 3788 | + action: getHeicConversionAdvice() | |
| 3526 | 3789 | }, |
| 3527 | 3790 | [ErrorCode.IMAGE_TRANSCODING_ERROR]: { |
| 3528 | - title: (0, import_i18n7.__)("Image processing failed"), | |
| 3529 | - description: (0, import_i18n7.sprintf)( | |
| 3791 | + title: (0, import_i18n8.__)("Image processing failed"), | |
| 3792 | + description: (0, import_i18n8.sprintf)( | |
| 3530 | 3793 | /* translators: %s: file name */ |
| 3531 | - (0, import_i18n7.__)('Failed to process "%s".'), | |
| 3794 | + (0, import_i18n8.__)('Failed to process "%s".'), | |
| 3532 | 3795 | fileName |
| 3533 | 3796 | ), |
| 3534 | - action: (0, import_i18n7.__)("The image may be corrupted. Try a different file.") | |
| 3797 | + action: (0, import_i18n8.__)("The image may be corrupted. Try a different file.") | |
| 3535 | 3798 | }, |
| 3536 | 3799 | [ErrorCode.IMAGE_ROTATION_ERROR]: { |
| 3537 | - title: (0, import_i18n7.__)("Image rotation failed"), | |
| 3538 | - description: (0, import_i18n7.sprintf)( | |
| 3800 | + title: (0, import_i18n8.__)("Image rotation failed"), | |
| 3801 | + description: (0, import_i18n8.sprintf)( | |
| 3539 | 3802 | /* translators: %s: file name */ |
| 3540 | - (0, import_i18n7.__)('Failed to rotate "%s".'), | |
| 3803 | + (0, import_i18n8.__)('Failed to rotate "%s".'), | |
| 3541 | 3804 | fileName |
| 3542 | 3805 | ), |
| 3543 | - action: (0, import_i18n7.__)("The image may be corrupted. Try a different file.") | |
| 3806 | + action: (0, import_i18n8.__)("The image may be corrupted. Try a different file.") | |
| 3544 | 3807 | }, |
| 3545 | 3808 | [ErrorCode.MEDIA_TRANSCODING_ERROR]: { |
| 3546 | - title: (0, import_i18n7.__)("Media processing failed"), | |
| 3547 | - description: (0, import_i18n7.sprintf)( | |
| 3809 | + title: (0, import_i18n8.__)("Media processing failed"), | |
| 3810 | + description: (0, import_i18n8.sprintf)( | |
| 3548 | 3811 | /* translators: %s: file name */ |
| 3549 | - (0, import_i18n7.__)('Failed to convert "%s" to the target format.'), | |
| 3812 | + (0, import_i18n8.__)('Failed to convert "%s" to the target format.'), | |
| 3550 | 3813 | fileName |
| 3551 | 3814 | ), |
| 3552 | - action: (0, import_i18n7.__)("The file may be corrupted. Try a different file.") | |
| 3815 | + action: (0, import_i18n8.__)("The file may be corrupted. Try a different file.") | |
| 3553 | 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 | + }, | |
| 3554 | 3826 | [ErrorCode.GENERAL]: { |
| 3555 | - title: (0, import_i18n7.__)("Upload failed"), | |
| 3556 | - description: (0, import_i18n7.sprintf)( | |
| 3827 | + title: (0, import_i18n8.__)("Upload failed"), | |
| 3828 | + description: (0, import_i18n8.sprintf)( | |
| 3557 | 3829 | /* translators: %s: file name */ |
| 3558 | - (0, import_i18n7.__)('Failed to upload "%s".'), | |
| 3830 | + (0, import_i18n8.__)('Failed to upload "%s".'), | |
| 3559 | 3831 | fileName |
| 3560 | 3832 | ), |
| 3561 | - action: (0, import_i18n7.__)("Please try again.") | |
| 3833 | + action: (0, import_i18n8.__)("Please try again.") | |
| 3562 | 3834 | } |
| 3563 | 3835 | }; |
| 3564 | 3836 | return messages[code] || { |
| 3565 | - title: (0, import_i18n7.__)("Upload failed"), | |
| 3566 | - description: (0, import_i18n7.sprintf)( | |
| 3837 | + title: (0, import_i18n8.__)("Upload failed"), | |
| 3838 | + description: (0, import_i18n8.sprintf)( | |
| 3567 | 3839 | /* translators: %s: file name */ |
| 3568 | - (0, import_i18n7.__)('Failed to upload "%s".'), | |
| 3840 | + (0, import_i18n8.__)('Failed to upload "%s".'), | |
| 3569 | 3841 | fileName |
| 3570 | 3842 | ), |
| 3571 | - action: (0, import_i18n7.__)("Please try again.") | |
| 3843 | + action: (0, import_i18n8.__)("Please try again.") | |
| 3572 | 3844 | }; |
| 3573 | 3845 | } |
| 3574 | 3846 | return __toCommonJS(index_exports); |
| 3847 | +})(); | |
| 3848 | +(window.wp ||= {}).uploadMedia = wp.uploadMedia; | |
| 3575 | 3849 | })(); |
| 3576 | 3850 | //# sourceMappingURL=index.js.map |