| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 (function() { 2 "use strict"; 3 var wp; 4 (wp ||= {}).uploadMedia = (() => { 5 var __create = Object.create; 6 var __defProp = Object.defineProperty; 7 var __getOwnPropDesc = Object.getOwnPropertyDescriptor; 8 var __getOwnPropNames = Object.getOwnPropertyNames; 9 var __getProtoOf = Object.getPrototypeOf; 10 var __hasOwnProp = Object.prototype.hasOwnProperty; 11 var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { 12 get: (a, b) => (typeof require !== "undefined" ? require : a)[b] 13 }) : x)(function(x) { 14 if (typeof require !== "undefined") return require.apply(this, arguments); 15 throw Error('Dynamic require of "' + x + '" is not supported'); 16 }); 17 var __commonJS = (cb, mod) => function __require2() { 18 return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; 19 }; 20 var __export = (target, all) => { 21 for (var name in all) 22 __defProp(target, name, { get: all[name], enumerable: true }); 23 }; 24 var __copyProps = (to, from, except, desc) => { 25 if (from && typeof from === "object" || typeof from === "function") { 26 for (let key of __getOwnPropNames(from)) 27 if (!__hasOwnProp.call(to, key) && key !== except) 28 __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); 29 } 30 return to; 31 }; 32 var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( 33 // If the importer is in node compatibility mode or this is not an ESM 34 // file that has been converted to a CommonJS file using a Babel- 35 // compatible transform (i.e. "__esModule" has not been set), then set 36 // "default" to the CommonJS "module.exports" for node compatibility. 37 isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, 38 mod 39 )); 40 var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); 41 42 // package-external:@wordpress/data 43 var require_data = __commonJS({ 44 "package-external:@wordpress/data"(exports, module) { 45 module.exports = window.wp.data; 46 } 47 }); 48 49 // package-external:@wordpress/i18n 50 var require_i18n = __commonJS({ 51 "package-external:@wordpress/i18n"(exports, module) { 52 module.exports = window.wp.i18n; 53 } 54 }); 55 56 // package-external:@wordpress/url 57 var require_url = __commonJS({ 58 "package-external:@wordpress/url"(exports, module) { 59 module.exports = window.wp.url; 60 } 61 }); 62 63 // package-external:@wordpress/blob 64 var require_blob = __commonJS({ 65 "package-external:@wordpress/blob"(exports, module) { 66 module.exports = window.wp.blob; 67 } 68 }); 69 70 // package-external:@wordpress/private-apis 71 var require_private_apis = __commonJS({ 72 "package-external:@wordpress/private-apis"(exports, module) { 73 module.exports = window.wp.privateApis; 74 } 75 }); 76 77 // package-external:@wordpress/element 78 var require_element = __commonJS({ 79 "package-external:@wordpress/element"(exports, module) { 80 module.exports = window.wp.element; 81 } 82 }); 83 84 // package-external:@wordpress/compose 85 var require_compose = __commonJS({ 86 "package-external:@wordpress/compose"(exports, module) { 87 module.exports = window.wp.compose; 88 } 89 }); 90 91 // vendor-external:react/jsx-runtime 92 var require_jsx_runtime = __commonJS({ 93 "vendor-external:react/jsx-runtime"(exports, module) { 94 module.exports = window.ReactJSXRuntime; 95 } 96 }); 97 98 // packages/upload-media/build-module/index.mjs 99 var index_exports = {}; 100 __export(index_exports, { 101 ErrorCode: () => ErrorCode, 102 MediaUploadProvider: () => provider_default, 103 UploadError: () => UploadError, 104 clearFeatureDetectionCache: () => clearFeatureDetectionCache, 105 detectClientSideMediaSupport: () => detectClientSideMediaSupport, 106 getErrorMessage: () => getErrorMessage, 107 getHeicConversionAdvice: () => getHeicConversionAdvice, 108 getHeicUnsupportedMessage: () => getHeicUnsupportedMessage, 109 isClientSideMediaSupported: () => isClientSideMediaSupported, 110 isHeicCanvasSupported: () => isHeicCanvasSupported, 111 isHeicFile: () => isHeicFile, 112 store: () => store 113 }); 114 115 // packages/upload-media/build-module/store/index.mjs 116 var import_data = __toESM(require_data(), 1); 117 118 // packages/upload-media/build-module/store/types.mjs 119 var Type = /* @__PURE__ */ ((Type2) => { 120 Type2["Unknown"] = "REDUX_UNKNOWN"; 121 Type2["Add"] = "ADD_ITEM"; 122 Type2["Prepare"] = "PREPARE_ITEM"; 123 Type2["Cancel"] = "CANCEL_ITEM"; 124 Type2["Remove"] = "REMOVE_ITEM"; 125 Type2["RetryItem"] = "RETRY_ITEM"; 126 Type2["ScheduleRetry"] = "SCHEDULE_RETRY"; 127 Type2["PauseItem"] = "PAUSE_ITEM"; 128 Type2["ResumeItem"] = "RESUME_ITEM"; 129 Type2["PauseQueue"] = "PAUSE_QUEUE"; 130 Type2["ResumeQueue"] = "RESUME_QUEUE"; 131 Type2["OperationStart"] = "OPERATION_START"; 132 Type2["OperationFinish"] = "OPERATION_FINISH"; 133 Type2["AddOperations"] = "ADD_OPERATIONS"; 134 Type2["CacheBlobUrl"] = "CACHE_BLOB_URL"; 135 Type2["RevokeBlobUrls"] = "REVOKE_BLOB_URLS"; 136 Type2["UpdateProgress"] = "UPDATE_PROGRESS"; 137 Type2["AccumulateSubSize"] = "ACCUMULATE_SUB_SIZE"; 138 Type2["UpdateSettings"] = "UPDATE_SETTINGS"; 139 return Type2; 140 })(Type || {}); 141 var ItemStatus = /* @__PURE__ */ ((ItemStatus2) => { 142 ItemStatus2["Queued"] = "QUEUED"; 143 ItemStatus2["Processing"] = "PROCESSING"; 144 ItemStatus2["Paused"] = "PAUSED"; 145 ItemStatus2["PendingRetry"] = "PENDING_RETRY"; 146 ItemStatus2["Uploaded"] = "UPLOADED"; 147 ItemStatus2["Error"] = "ERROR"; 148 return ItemStatus2; 149 })(ItemStatus || {}); 150 var OperationType = /* @__PURE__ */ ((OperationType2) => { 151 OperationType2["Prepare"] = "PREPARE"; 152 OperationType2["Upload"] = "UPLOAD"; 153 OperationType2["ResizeCrop"] = "RESIZE_CROP"; 154 OperationType2["Rotate"] = "ROTATE"; 155 OperationType2["TranscodeImage"] = "TRANSCODE_IMAGE"; 156 OperationType2["TranscodeGif"] = "TRANSCODE_GIF"; 157 OperationType2["ThumbnailGeneration"] = "THUMBNAIL_GENERATION"; 158 OperationType2["Finalize"] = "FINALIZE"; 159 OperationType2["DetectUltraHdr"] = "DETECT_ULTRAHDR"; 160 return OperationType2; 161 })(OperationType || {}); 162 163 // packages/upload-media/build-module/store/constants.mjs 164 var STORE_NAME = "core/upload-media"; 165 var DEFAULT_MAX_CONCURRENT_UPLOADS = 5; 166 var DEFAULT_MAX_CONCURRENT_IMAGE_PROCESSING = 2; 167 var DEFAULT_RETRY_SETTINGS = { 168 maxRetryAttempts: 3, 169 initialRetryDelayMs: 1e3, 170 maxRetryDelayMs: 3e4, 171 backoffMultiplier: 2, 172 retryJitter: 0.1 173 }; 174 var CLIENT_SIDE_SUPPORTED_MIME_TYPES = [ 175 "image/jpeg", 176 "image/png", 177 "image/gif", 178 "image/webp", 179 "image/avif" 180 ]; 181 var HEIC_MIME_TYPES = [ 182 "image/heic", 183 "image/heif" 184 ]; 185 186 // packages/upload-media/build-module/store/reducer.mjs 187 var noop = () => { 188 }; 189 var DEFAULT_STATE = { 190 queue: [], 191 queueStatus: "active", 192 blobUrls: {}, 193 settings: { 194 mediaUpload: noop, 195 maxConcurrentUploads: DEFAULT_MAX_CONCURRENT_UPLOADS, 196 maxConcurrentImageProcessing: DEFAULT_MAX_CONCURRENT_IMAGE_PROCESSING, 197 retry: { ...DEFAULT_RETRY_SETTINGS } 198 }, 199 failureCount: 0 200 }; 201 function reducer(state = DEFAULT_STATE, action = { type: Type.Unknown }) { 202 switch (action.type) { 203 case Type.PauseQueue: { 204 return { 205 ...state, 206 queueStatus: "paused" 207 }; 208 } 209 case Type.ResumeQueue: { 210 return { 211 ...state, 212 queueStatus: "active" 213 }; 214 } 215 case Type.PauseItem: 216 return { 217 ...state, 218 queue: state.queue.map( 219 (item) => item.id === action.id ? { 220 ...item, 221 status: ItemStatus.Paused 222 } : item 223 ) 224 }; 225 case Type.ResumeItem: 226 return { 227 ...state, 228 queue: state.queue.map( 229 (item) => item.id === action.id ? { 230 ...item, 231 status: ItemStatus.Processing 232 } : item 233 ) 234 }; 235 case Type.Add: 236 return { 237 ...state, 238 queue: [...state.queue, action.item] 239 }; 240 case Type.Cancel: { 241 const cancelled = state.queue.find( 242 (item) => item.id === action.id 243 ); 244 return { 245 ...state, 246 failureCount: cancelled && !cancelled.parentId ? state.failureCount + 1 : state.failureCount, 247 queue: state.queue.map( 248 (item) => item.id === action.id ? { 249 ...item, 250 error: action.error 251 } : item 252 ) 253 }; 254 } 255 case Type.RetryItem: 256 return { 257 ...state, 258 queue: state.queue.map( 259 (item) => item.id === action.id ? { 260 ...item, 261 status: ItemStatus.Processing, 262 error: void 0, 263 retryCount: (item.retryCount ?? 0) + 1, 264 abortController: new AbortController(), 265 /* 266 * The failed operation is still recorded on 267 * the item: nothing finishes it when it 268 * fails, and while the item waits out the 269 * backoff that is what keeps it out of the 270 * concurrency pools. Clear it now that the 271 * item is about to run again, so processItem 272 * does not mistake it for an operation still 273 * in flight and skip the retry. 274 */ 275 currentOperation: void 0 276 } : item 277 ) 278 }; 279 case Type.ScheduleRetry: 280 return { 281 ...state, 282 queue: state.queue.map( 283 (item) => item.id === action.id ? { 284 ...item, 285 status: ItemStatus.PendingRetry, 286 error: action.error, 287 retryCount: action.retryCount, 288 nextRetryTimestamp: action.nextRetryTimestamp 289 } : item 290 ) 291 }; 292 case Type.Remove: 293 return { 294 ...state, 295 queue: state.queue.filter((item) => item.id !== action.id) 296 }; 297 case Type.OperationStart: { 298 return { 299 ...state, 300 queue: state.queue.map( 301 (item) => item.id === action.id ? { 302 ...item, 303 currentOperation: action.operation 304 } : item 305 ) 306 }; 307 } 308 case Type.AddOperations: 309 return { 310 ...state, 311 queue: state.queue.map((item) => { 312 if (item.id !== action.id) { 313 return item; 314 } 315 return { 316 ...item, 317 operations: [ 318 ...item.operations || [], 319 ...action.operations 320 ] 321 }; 322 }) 323 }; 324 case Type.OperationFinish: 325 return { 326 ...state, 327 queue: state.queue.map((item) => { 328 if (item.id !== action.id) { 329 return item; 330 } 331 const operations = item.operations ? item.operations.slice(1) : []; 332 const attachment = item.attachment || action.item.attachment ? { 333 ...item.attachment, 334 ...action.item.attachment 335 } : void 0; 336 return { 337 ...item, 338 currentOperation: void 0, 339 operations, 340 ...action.item, 341 attachment, 342 additionalData: { 343 ...item.additionalData, 344 ...action.item.additionalData 345 } 346 }; 347 }) 348 }; 349 case Type.CacheBlobUrl: { 350 const blobUrls = state.blobUrls[action.id] || []; 351 return { 352 ...state, 353 blobUrls: { 354 ...state.blobUrls, 355 [action.id]: [...blobUrls, action.blobUrl] 356 } 357 }; 358 } 359 case Type.RevokeBlobUrls: { 360 const newBlobUrls = { ...state.blobUrls }; 361 delete newBlobUrls[action.id]; 362 return { 363 ...state, 364 blobUrls: newBlobUrls 365 }; 366 } 367 case Type.UpdateProgress: 368 return { 369 ...state, 370 queue: state.queue.map( 371 (item) => item.id === action.id ? { 372 ...item, 373 progress: action.progress 374 } : item 375 ) 376 }; 377 case Type.AccumulateSubSize: 378 return { 379 ...state, 380 queue: state.queue.map( 381 (item) => item.id === action.id ? { 382 ...item, 383 subSizes: [ 384 ...item.subSizes || [], 385 action.subSize 386 ] 387 } : item 388 ) 389 }; 390 case Type.UpdateSettings: { 391 return { 392 ...state, 393 settings: { 394 ...state.settings, 395 ...action.settings 396 } 397 }; 398 } 399 } 400 return state; 401 } 402 var reducer_default = reducer; 403 404 // packages/upload-media/build-module/store/selectors.mjs 405 var selectors_exports = {}; 406 __export(selectors_exports, { 407 getItems: () => getItems, 408 getSettings: () => getSettings, 409 isUploading: () => isUploading, 410 isUploadingById: () => isUploadingById, 411 isUploadingByUrl: () => isUploadingByUrl 412 }); 413 function getItems(state) { 414 return state.queue; 415 } 416 function isUploading(state) { 417 return state.queue.length >= 1; 418 } 419 function isUploadingByUrl(state, url) { 420 return state.queue.some( 421 (item) => item.attachment?.url === url || item.sourceUrl === url 422 ); 423 } 424 function isUploadingById(state, attachmentId) { 425 return state.queue.some( 426 (item) => item.attachment?.id === attachmentId || item.sourceAttachmentId === attachmentId 427 ); 428 } 429 function getSettings(state) { 430 return state.settings; 431 } 432 433 // packages/upload-media/build-module/store/private-selectors.mjs 434 var private_selectors_exports = {}; 435 __export(private_selectors_exports, { 436 getActiveImageProcessingCount: () => getActiveImageProcessingCount, 437 getActiveUploadCount: () => getActiveUploadCount, 438 getActiveVideoProcessingCount: () => getActiveVideoProcessingCount, 439 getAllItems: () => getAllItems, 440 getBlobUrls: () => getBlobUrls, 441 getFailedItems: () => getFailedItems, 442 getFailureCount: () => getFailureCount, 443 getItem: () => getItem, 444 getItemProgress: () => getItemProgress, 445 getPendingImageProcessing: () => getPendingImageProcessing, 446 getPendingUploads: () => getPendingUploads, 447 getPendingVideoProcessing: () => getPendingVideoProcessing, 448 hasPendingItemsByParentId: () => hasPendingItemsByParentId, 449 isBatchUploaded: () => isBatchUploaded, 450 isPaused: () => isPaused 451 }); 452 function getAllItems(state) { 453 return state.queue; 454 } 455 function getFailureCount(state) { 456 return state.failureCount; 457 } 458 function getItem(state, id) { 459 return state.queue.find((item) => item.id === id); 460 } 461 function isBatchUploaded(state, batchId) { 462 const batchItems = state.queue.filter( 463 (item) => batchId === item.batchId 464 ); 465 return batchItems.length === 0; 466 } 467 function isPaused(state) { 468 return state.queueStatus === "paused"; 469 } 470 function getBlobUrls(state, id) { 471 return state.blobUrls[id] || []; 472 } 473 function getActiveUploadCount(state) { 474 return state.queue.filter( 475 (item) => item.currentOperation === OperationType.Upload 476 ).length; 477 } 478 function getPendingUploads(state) { 479 return state.queue.filter((item) => { 480 const nextOperation = Array.isArray(item.operations?.[0]) ? item.operations[0][0] : item.operations?.[0]; 481 return nextOperation === OperationType.Upload && item.currentOperation !== OperationType.Upload; 482 }); 483 } 484 function getActiveImageProcessingCount(state) { 485 return state.queue.filter( 486 (item) => item.currentOperation === OperationType.ResizeCrop || item.currentOperation === OperationType.Rotate 487 ).length; 488 } 489 function getActiveVideoProcessingCount(state) { 490 return state.queue.filter( 491 (item) => item.currentOperation === OperationType.TranscodeGif 492 ).length; 493 } 494 function getPendingImageProcessing(state) { 495 return state.queue.filter((item) => { 496 const nextOperation = Array.isArray(item.operations?.[0]) ? item.operations[0][0] : item.operations?.[0]; 497 return (nextOperation === OperationType.ResizeCrop || nextOperation === OperationType.Rotate) && item.currentOperation !== OperationType.ResizeCrop && item.currentOperation !== OperationType.Rotate; 498 }); 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 } 506 function getFailedItems(state) { 507 return state.queue.filter((item) => item.error !== void 0); 508 } 509 function hasPendingItemsByParentId(state, parentId) { 510 return state.queue.some((item) => item.parentId === parentId); 511 } 512 function getItemProgress(state, id) { 513 const item = state.queue.find((i) => i.id === id); 514 return item?.progress; 515 } 516 517 // packages/upload-media/build-module/store/actions.mjs 518 var actions_exports = {}; 519 __export(actions_exports, { 520 addItems: () => addItems, 521 cancelItem: () => cancelItem, 522 executeRetry: () => executeRetry, 523 retryItem: () => retryItem, 524 scheduleRetry: () => scheduleRetry 525 }); 526 527 // node_modules/uuid/dist/stringify.js 528 var byteToHex = []; 529 for (let i = 0; i < 256; ++i) { 530 byteToHex.push((i + 256).toString(16).slice(1)); 531 } 532 function unsafeStringify(arr, offset = 0) { 533 return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); 534 } 535 536 // node_modules/uuid/dist/rng.js 537 var rnds8 = new Uint8Array(16); 538 function rng() { 539 return crypto.getRandomValues(rnds8); 540 } 541 542 // node_modules/uuid/dist/v4.js 543 function v4(options, buf, offset) { 544 if (!buf && !options && crypto.randomUUID) { 545 return crypto.randomUUID(); 546 } 547 return _v4(options, buf, offset); 548 } 549 function _v4(options, buf, offset) { 550 options = options || {}; 551 const rnds = options.random ?? options.rng?.() ?? rng(); 552 if (rnds.length < 16) { 553 throw new Error("Random bytes length must be >= 16"); 554 } 555 rnds[6] = rnds[6] & 15 | 64; 556 rnds[8] = rnds[8] & 63 | 128; 557 if (buf) { 558 offset = offset || 0; 559 if (offset < 0 || offset + 16 > buf.length) { 560 throw new RangeError(`UUID byte range $offset}:$offset + 15} is out of buffer bounds`); 561 } 562 for (let i = 0; i < 16; ++i) { 563 buf[offset + i] = rnds[i]; 564 } 565 return buf; 566 } 567 return unsafeStringify(rnds); 568 } 569 var v4_default = v4; 570 571 // packages/upload-media/build-module/store/actions.mjs 572 var import_i18n5 = __toESM(require_i18n(), 1); 573 574 // packages/upload-media/build-module/store/utils/retry.mjs 575 function calculateRetryDelay(options) { 576 const { attempt, initialDelay, maxDelay, multiplier, jitter } = options; 577 const exponentialDelay = initialDelay * Math.pow(multiplier, attempt - 1); 578 const cappedDelay = Math.min(exponentialDelay, maxDelay); 579 const jitterFactor = 1 + (Math.random() * 2 - 1) * jitter; 580 return Math.floor(cappedDelay * jitterFactor); 581 } 582 var RETRYABLE_MESSAGE_PATTERNS = [ 583 /network/i, 584 /timeout/i, 585 /ECONNRESET/i, 586 /fetch failed/i, 587 /connection/i, 588 /socket/i, 589 /ETIMEDOUT/i, 590 /ENOTFOUND/i, 591 /Could not get a valid response/i, 592 /Failed to fetch/i, 593 /Load failed/i 594 ]; 595 function shouldRetryError(error, retryCount, maxRetries) { 596 if (retryCount >= maxRetries) { 597 return false; 598 } 599 const message = typeof error === "string" ? error : error?.message || ""; 600 return RETRYABLE_MESSAGE_PATTERNS.some( 601 (pattern) => pattern.test(message) 602 ); 603 } 604 var retryTimers = /* @__PURE__ */ new Map(); 605 function clearRetryTimer(id) { 606 const pendingTimer = retryTimers.get(id); 607 if (pendingTimer !== void 0) { 608 clearTimeout(pendingTimer); 609 retryTimers.delete(id); 610 } 611 } 612 613 // packages/upload-media/build-module/image-file.mjs 614 var ImageFile = class extends File { 615 width = 0; 616 height = 0; 617 originalWidth = 0; 618 originalHeight = 0; 619 get wasResized() { 620 return (this.originalWidth || 0) > this.width || (this.originalHeight || 0) > this.height; 621 } 622 constructor(file, width, height, originalWidth, originalHeight) { 623 super([file], file.name, { 624 type: file.type, 625 lastModified: file.lastModified 626 }); 627 this.width = width; 628 this.height = height; 629 this.originalWidth = originalWidth; 630 this.originalHeight = originalHeight; 631 } 632 }; 633 634 // packages/upload-media/build-module/utils.mjs 635 var import_url = __toESM(require_url(), 1); 636 var import_i18n = __toESM(require_i18n(), 1); 637 function convertBlobToFile(fileOrBlob) { 638 if (fileOrBlob instanceof File) { 639 return fileOrBlob; 640 } 641 if ("name" in fileOrBlob && typeof fileOrBlob.name === "string") { 642 return new File([fileOrBlob], fileOrBlob.name, { 643 type: fileOrBlob.type, 644 lastModified: fileOrBlob.lastModified 645 }); 646 } 647 const ext = fileOrBlob.type.split("/")[1]; 648 const mediaType = "application/pdf" === fileOrBlob.type ? "document" : fileOrBlob.type.split("/")[0]; 649 return new File([fileOrBlob], `$mediaType}.$ext}`, { 650 type: fileOrBlob.type 651 }); 652 } 653 function renameFile(file, name) { 654 return new File([file], name, { 655 type: file.type, 656 lastModified: file.lastModified 657 }); 658 } 659 function cloneFile(file) { 660 return renameFile(file, file.name); 661 } 662 function getFileBasename(name) { 663 return name.includes(".") ? name.split(".").slice(0, -1).join(".") : name; 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 } 729 730 // packages/upload-media/build-module/store/utils/index.mjs 731 var vipsModulePromise; 732 var vipsModule; 733 function loadVipsModule() { 734 if (!vipsModulePromise) { 735 vipsModulePromise = import("@wordpress/vips/worker").then( 736 (mod) => { 737 vipsModule = mod; 738 return mod; 739 } 740 ); 741 } 742 return vipsModulePromise; 743 } 744 async function vipsConvertImageFormat(id, file, type, options = {}) { 745 const { vipsConvertImageFormat: convertImageFormat } = await loadVipsModule(); 746 const buffer = await convertImageFormat( 747 id, 748 await file.arrayBuffer(), 749 file.type, 750 type, 751 options 752 ); 753 const ext = type.split("/")[1]; 754 const fileName = `$getFileBasename(file.name)}.$ext}`; 755 return new File([new Blob([buffer])], fileName, { 756 type 757 }); 758 } 759 async function vipsHasTransparency(url) { 760 const { vipsHasTransparency: hasTransparency } = await loadVipsModule(); 761 const response = await fetch(url); 762 if (!response.ok) { 763 throw new Error(`Failed to fetch image: $response.status}`); 764 } 765 return hasTransparency(await response.arrayBuffer()); 766 } 767 async function vipsGetUltraHdrInfo(buffer) { 768 const { vipsGetUltraHdrInfo: getUltraHdrInfo } = await loadVipsModule(); 769 return getUltraHdrInfo(buffer); 770 } 771 async function vipsResizeImage(id, file, resize, options = {}) { 772 const { 773 smartCrop = false, 774 addSuffix = false, 775 signal, 776 scaledSuffix, 777 quality, 778 stripMeta, 779 maxBitdepth 780 } = options; 781 if (signal?.aborted) { 782 throw new Error("Operation aborted"); 783 } 784 const { vipsResizeImage: resizeImage } = await loadVipsModule(); 785 const { buffer, width, height, originalWidth, originalHeight } = await resizeImage(id, await file.arrayBuffer(), file.type, resize, { 786 smartCrop, 787 quality, 788 stripMeta, 789 maxBitdepth 790 }); 791 let fileName = file.name; 792 const wasResized = originalWidth > width || originalHeight > height; 793 if (wasResized) { 794 const basename = getFileBasename(file.name); 795 if (scaledSuffix) { 796 fileName = file.name.replace(basename, `$basename}-scaled`); 797 } else if (addSuffix) { 798 fileName = file.name.replace( 799 basename, 800 `$basename}-$width}x$height}` 801 ); 802 } 803 } 804 const resultFile = new ImageFile( 805 new File( 806 [new Blob([buffer], { type: file.type })], 807 fileName, 808 { 809 type: file.type 810 } 811 ), 812 width, 813 height, 814 originalWidth, 815 originalHeight 816 ); 817 return resultFile; 818 } 819 async function vipsRotateImage(id, file, orientation, signal) { 820 if (signal?.aborted) { 821 throw new Error("Operation aborted"); 822 } 823 if (orientation === 1) { 824 return file; 825 } 826 const { vipsRotateImage: rotateImage } = await loadVipsModule(); 827 const { buffer, width, height } = await rotateImage( 828 id, 829 await file.arrayBuffer(), 830 file.type, 831 orientation 832 ); 833 const basename = getFileBasename(file.name); 834 const fileName = file.name.replace(basename, `$basename}-rotated`); 835 const resultFile = new ImageFile( 836 new File( 837 [new Blob([buffer], { type: file.type })], 838 fileName, 839 { 840 type: file.type 841 } 842 ), 843 width, 844 height 845 ); 846 return resultFile; 847 } 848 async function vipsCancelOperations(id) { 849 if (!vipsModule) { 850 return false; 851 } 852 return vipsModule.vipsCancelOperations(id); 853 } 854 function terminateVipsWorker() { 855 if (vipsModule) { 856 vipsModule.terminateVipsWorker(); 857 } 858 } 859 var completedVipsOperations = 0; 860 var MAX_VIPS_OPS_BEFORE_RECYCLE = 50; 861 function maybeRecycleVipsWorker(activeImageProcessingCount) { 862 completedVipsOperations++; 863 if (completedVipsOperations >= MAX_VIPS_OPS_BEFORE_RECYCLE && activeImageProcessingCount === 0) { 864 terminateVipsWorker(); 865 completedVipsOperations = 0; 866 } 867 } 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 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 || {}); 1023 var UploadError = class extends Error { 1024 code; 1025 file; 1026 constructor({ code, message, file, cause }) { 1027 super(message, { cause }); 1028 Object.setPrototypeOf(this, new.target.prototype); 1029 this.code = code; 1030 this.file = file; 1031 } 1032 }; 1033 1034 // packages/upload-media/build-module/validate-mime-type.mjs 1035 var import_i18n2 = __toESM(require_i18n(), 1); 1036 function validateMimeType(file, allowedTypes) { 1037 if (!allowedTypes) { 1038 return; 1039 } 1040 const isAllowedType = allowedTypes.some((allowedType) => { 1041 if (allowedType.includes("/")) { 1042 return allowedType === file.type; 1043 } 1044 return file.type.startsWith(`$allowedType}/`); 1045 }); 1046 if (file.type && !isAllowedType) { 1047 throw new UploadError({ 1048 code: ErrorCode.MIME_TYPE_NOT_SUPPORTED, 1049 message: (0, import_i18n2.sprintf)( 1050 // translators: %s: file name. 1051 (0, import_i18n2.__)("%s: Sorry, this file type is not supported here."), 1052 file.name 1053 ), 1054 file 1055 }); 1056 } 1057 } 1058 1059 // packages/upload-media/build-module/validate-mime-type-for-user.mjs 1060 var import_i18n3 = __toESM(require_i18n(), 1); 1061 1062 // packages/upload-media/build-module/get-mime-types-array.mjs 1063 function getMimeTypesArray(wpMimeTypesObject) { 1064 if (!wpMimeTypesObject) { 1065 return null; 1066 } 1067 return Object.entries(wpMimeTypesObject).flatMap( 1068 ([extensionsString, mime]) => { 1069 const [type] = mime.split("/"); 1070 const extensions = extensionsString.split("|"); 1071 return [ 1072 mime, 1073 ...extensions.map( 1074 (extension) => `$type}/$extension}` 1075 ) 1076 ]; 1077 } 1078 ); 1079 } 1080 1081 // packages/upload-media/build-module/validate-mime-type-for-user.mjs 1082 function validateMimeTypeForUser(file, wpAllowedMimeTypes) { 1083 const allowedMimeTypesForUser = getMimeTypesArray(wpAllowedMimeTypes); 1084 if (!allowedMimeTypesForUser) { 1085 return; 1086 } 1087 const isAllowedMimeTypeForUser = allowedMimeTypesForUser.includes( 1088 file.type 1089 ); 1090 if (file.type && !isAllowedMimeTypeForUser) { 1091 throw new UploadError({ 1092 code: ErrorCode.MIME_TYPE_NOT_ALLOWED_FOR_USER, 1093 message: (0, import_i18n3.sprintf)( 1094 // translators: %s: file name. 1095 (0, import_i18n3.__)( 1096 "%s: Sorry, you are not allowed to upload this file type." 1097 ), 1098 file.name 1099 ), 1100 file 1101 }); 1102 } 1103 } 1104 1105 // packages/upload-media/build-module/validate-file-size.mjs 1106 var import_i18n4 = __toESM(require_i18n(), 1); 1107 function validateFileSize(file, maxUploadFileSize) { 1108 if (file.size <= 0) { 1109 throw new UploadError({ 1110 code: ErrorCode.EMPTY_FILE, 1111 message: (0, import_i18n4.sprintf)( 1112 // translators: %s: file name. 1113 (0, import_i18n4.__)("%s: This file is empty."), 1114 file.name 1115 ), 1116 file 1117 }); 1118 } 1119 if (maxUploadFileSize && file.size > maxUploadFileSize) { 1120 throw new UploadError({ 1121 code: ErrorCode.SIZE_ABOVE_LIMIT, 1122 message: (0, import_i18n4.sprintf)( 1123 // translators: %s: file name. 1124 (0, import_i18n4.__)( 1125 "%s: This file exceeds the maximum upload size for this site." 1126 ), 1127 file.name 1128 ), 1129 file 1130 }); 1131 } 1132 } 1133 1134 // packages/upload-media/build-module/store/actions.mjs 1135 function addItems({ 1136 files, 1137 onChange, 1138 onSuccess, 1139 onError, 1140 onBatchSuccess, 1141 additionalData, 1142 allowedTypes 1143 }) { 1144 return async ({ select: select2, dispatch }) => { 1145 const batchId = v4_default(); 1146 for (const file of files) { 1147 try { 1148 validateMimeType(file, allowedTypes); 1149 validateMimeTypeForUser( 1150 file, 1151 select2.getSettings().allowedMimeTypes 1152 ); 1153 } catch (error) { 1154 onError?.(error); 1155 continue; 1156 } 1157 try { 1158 validateFileSize( 1159 file, 1160 select2.getSettings().maxUploadFileSize 1161 ); 1162 } catch (error) { 1163 onError?.(error); 1164 continue; 1165 } 1166 dispatch.addItem({ 1167 file, 1168 batchId, 1169 onChange, 1170 onSuccess, 1171 onBatchSuccess, 1172 onError, 1173 additionalData 1174 }); 1175 } 1176 }; 1177 } 1178 function cancelItem(id, error, silent = false) { 1179 return async ({ select: select2, dispatch }) => { 1180 const item = select2.getItem(id); 1181 if (!item) { 1182 return; 1183 } 1184 clearRetryTimer(id); 1185 if (!silent && error && !item.parentId && !item.attachment?.id) { 1186 const settings = select2.getSettings(); 1187 const retrySettings = settings.retry; 1188 if (retrySettings) { 1189 const retryCount = item.retryCount ?? 0; 1190 const maxRetries = retrySettings.maxRetryAttempts; 1191 if (shouldRetryError(error, retryCount, maxRetries)) { 1192 dispatch.scheduleRetry(id, error); 1193 return; 1194 } 1195 } 1196 } 1197 item.abortController?.abort(); 1198 vipsCancelOperations(id).catch(() => { 1199 }); 1200 cancelGifToVideoOperations(id).catch(() => { 1201 }); 1202 if (!silent) { 1203 const { onError } = item; 1204 onError?.(error ?? new Error("Upload cancelled")); 1205 if (!onError && error && !item.parentId) { 1206 console.error("Upload cancelled", error); 1207 } 1208 } else { 1209 debug( 1210 `Item cancelled: $item.file.name} (item $id}): $error instanceof Error ? error.message : error}` 1211 ); 1212 } 1213 const { currentOperation, parentId, batchId } = item; 1214 dispatch({ 1215 type: Type.Cancel, 1216 id, 1217 error 1218 }); 1219 dispatch.removeItem(id); 1220 dispatch.revokeBlobUrls(id); 1221 if (currentOperation === OperationType.ResizeCrop || currentOperation === OperationType.Rotate) { 1222 for (const pending of select2.getPendingImageProcessing()) { 1223 dispatch.processItem(pending.id); 1224 } 1225 } 1226 if (currentOperation === OperationType.Upload) { 1227 for (const pending of select2.getPendingUploads()) { 1228 dispatch.processItem(pending.id); 1229 } 1230 } 1231 if (currentOperation === OperationType.TranscodeGif) { 1232 for (const pending of select2.getPendingVideoProcessing()) { 1233 dispatch.processItem(pending.id); 1234 } 1235 } 1236 if (currentOperation === OperationType.ResizeCrop || currentOperation === OperationType.Rotate || currentOperation === OperationType.TranscodeImage) { 1237 maybeRecycleVipsWorker(select2.getActiveImageProcessingCount()); 1238 } 1239 if (parentId) { 1240 const parentItem = select2.getItem(parentId); 1241 if (parentItem) { 1242 const isOptionalCompanion = item.additionalData?.image_size === "animated_video" || item.additionalData?.image_size === "animated_video_poster"; 1243 if (select2.hasPendingItemsByParentId(parentId)) { 1244 if (parentItem.operations && parentItem.operations.length > 0) { 1245 dispatch.processItem(parentId); 1246 } 1247 } else if (parentItem.subSizes && parentItem.subSizes.length > 0 || isOptionalCompanion) { 1248 if (parentItem.operations && parentItem.operations.length > 0) { 1249 dispatch.processItem(parentId); 1250 } 1251 } else { 1252 const parentAttachmentId = parentItem.attachment?.id; 1253 const { mediaDelete } = select2.getSettings(); 1254 if (parentAttachmentId && mediaDelete) { 1255 mediaDelete(parentAttachmentId).catch(() => { 1256 }); 1257 } 1258 await dispatch.cancelItem( 1259 parentId, 1260 new UploadError({ 1261 code: error instanceof UploadError && error.code || ErrorCode.GENERAL, 1262 message: error?.message || (0, import_i18n5.__)("The image could not be uploaded."), 1263 file: parentItem.file, 1264 cause: error instanceof Error ? error : void 0 1265 }) 1266 ); 1267 } 1268 } 1269 } 1270 if (batchId && select2.isBatchUploaded(batchId)) { 1271 debug(`Batch completed: $batchId}`); 1272 item.onBatchSuccess?.(); 1273 } 1274 }; 1275 } 1276 function retryItem(id) { 1277 return async ({ select: select2, dispatch }) => { 1278 const item = select2.getItem(id); 1279 if (!item) { 1280 return; 1281 } 1282 if (!item.error) { 1283 return; 1284 } 1285 dispatch({ 1286 type: Type.RetryItem, 1287 id 1288 }); 1289 dispatch.processItem(id); 1290 }; 1291 } 1292 function scheduleRetry(id, error) { 1293 return async ({ select: select2, dispatch }) => { 1294 const item = select2.getItem(id); 1295 if (!item) { 1296 return; 1297 } 1298 const settings = select2.getSettings(); 1299 const retrySettings = settings.retry; 1300 if (!retrySettings) { 1301 return; 1302 } 1303 const currentRetryCount = item.retryCount ?? 0; 1304 const delay = calculateRetryDelay({ 1305 attempt: currentRetryCount + 1, 1306 initialDelay: retrySettings.initialRetryDelayMs, 1307 maxDelay: retrySettings.maxRetryDelayMs, 1308 multiplier: retrySettings.backoffMultiplier, 1309 jitter: retrySettings.retryJitter 1310 }); 1311 const timerId = setTimeout(() => { 1312 retryTimers.delete(id); 1313 dispatch.executeRetry(id); 1314 }, delay); 1315 retryTimers.set(id, timerId); 1316 dispatch({ 1317 type: Type.ScheduleRetry, 1318 id, 1319 error, 1320 retryCount: currentRetryCount, 1321 nextRetryTimestamp: Date.now() + delay 1322 }); 1323 }; 1324 } 1325 function executeRetry(id) { 1326 return async ({ select: select2, dispatch }) => { 1327 const item = select2.getItem(id); 1328 if (!item || item.status !== ItemStatus.PendingRetry) { 1329 return; 1330 } 1331 if (select2.isPaused()) { 1332 return; 1333 } 1334 dispatch({ 1335 type: Type.RetryItem, 1336 id 1337 }); 1338 dispatch.processItem(id); 1339 }; 1340 } 1341 1342 // packages/upload-media/build-module/store/private-actions.mjs 1343 var private_actions_exports = {}; 1344 __export(private_actions_exports, { 1345 addItem: () => addItem, 1346 addSideloadItem: () => addSideloadItem, 1347 detectUltraHdr: () => detectUltraHdr, 1348 finalizeItem: () => finalizeItem, 1349 finishOperation: () => finishOperation, 1350 generateThumbnails: () => generateThumbnails, 1351 getTranscodeImageOperation: () => getTranscodeImageOperation, 1352 pauseItem: () => pauseItem, 1353 pauseQueue: () => pauseQueue, 1354 prepareItem: () => prepareItem, 1355 processItem: () => processItem, 1356 removeItem: () => removeItem, 1357 resizeCropItem: () => resizeCropItem, 1358 resumeQueue: () => resumeQueue, 1359 revokeBlobUrls: () => revokeBlobUrls, 1360 rotateItem: () => rotateItem, 1361 sideloadItem: () => sideloadItem, 1362 transcodeGifItem: () => transcodeGifItem, 1363 transcodeImageItem: () => transcodeImageItem, 1364 updateItemProgress: () => updateItemProgress, 1365 updateSettings: () => updateSettings, 1366 uploadItem: () => uploadItem 1367 }); 1368 var import_blob = __toESM(require_blob(), 1); 1369 var import_i18n7 = __toESM(require_i18n(), 1); 1370 1371 // packages/upload-media/build-module/heic-parser.mjs 1372 var Reader = class { 1373 view; 1374 buffer; 1375 pos; 1376 constructor(buffer, offset = 0) { 1377 this.buffer = buffer; 1378 this.view = new DataView(buffer); 1379 this.pos = offset; 1380 } 1381 u8() { 1382 const v = this.view.getUint8(this.pos); 1383 this.pos += 1; 1384 return v; 1385 } 1386 u16() { 1387 const v = this.view.getUint16(this.pos); 1388 this.pos += 2; 1389 return v; 1390 } 1391 u32() { 1392 const v = this.view.getUint32(this.pos); 1393 this.pos += 4; 1394 return v; 1395 } 1396 u64() { 1397 const hi = this.view.getUint32(this.pos); 1398 const lo = this.view.getUint32(this.pos + 4); 1399 this.pos += 8; 1400 return hi * 4294967296 + lo; 1401 } 1402 /** 1403 * Read a variable-width unsigned integer (0, 4 or 8 bytes). 1404 * 1405 * @param size Byte width to read (0, 4, or 8). 1406 */ 1407 uN(size) { 1408 if (size === 0) { 1409 return 0; 1410 } 1411 if (size === 4) { 1412 return this.u32(); 1413 } 1414 if (size === 8) { 1415 return this.u64(); 1416 } 1417 throw new Error(`Unsupported uint size: $size}`); 1418 } 1419 str(len) { 1420 let s = ""; 1421 for (let i = 0; i < len; i++) { 1422 s += String.fromCharCode(this.view.getUint8(this.pos + i)); 1423 } 1424 this.pos += len; 1425 return s; 1426 } 1427 bytes(len) { 1428 const b = new Uint8Array(this.buffer, this.pos, len); 1429 this.pos += len; 1430 return new Uint8Array(b); 1431 } 1432 }; 1433 function readBox(r) { 1434 if (r.pos + 8 > r.view.byteLength) { 1435 return null; 1436 } 1437 const offset = r.pos; 1438 let size = r.u32(); 1439 const type = r.str(4); 1440 let headerSize = 8; 1441 if (size === 1) { 1442 size = r.u64(); 1443 headerSize = 16; 1444 } else if (size === 0) { 1445 size = r.view.byteLength - offset; 1446 } 1447 return { type, offset, size, headerSize }; 1448 } 1449 function findBoxes(r, start, end) { 1450 const boxes = []; 1451 r.pos = start; 1452 while (r.pos < end) { 1453 const box = readBox(r); 1454 if (!box || box.size < 8) { 1455 break; 1456 } 1457 boxes.push(box); 1458 r.pos = box.offset + box.size; 1459 } 1460 return boxes; 1461 } 1462 function findBox(r, start, end, type) { 1463 r.pos = start; 1464 while (r.pos < end) { 1465 const box = readBox(r); 1466 if (!box || box.size < 8) { 1467 break; 1468 } 1469 if (box.type === type) { 1470 return box; 1471 } 1472 r.pos = box.offset + box.size; 1473 } 1474 return void 0; 1475 } 1476 function parsePitm(r, box) { 1477 r.pos = box.offset + box.headerSize; 1478 const version = r.u8(); 1479 r.pos += 3; 1480 return version === 0 ? r.u16() : r.u32(); 1481 } 1482 function parseIloc(r, box) { 1483 r.pos = box.offset + box.headerSize; 1484 const version = r.u8(); 1485 r.pos += 3; 1486 const byte1 = r.u8(); 1487 const offsetSize = byte1 >> 4 & 15; 1488 const lengthSize = byte1 & 15; 1489 const byte2 = r.u8(); 1490 const baseOffsetSize = byte2 >> 4 & 15; 1491 const indexSize = version >= 1 ? byte2 & 15 : 0; 1492 const itemCount = version < 2 ? r.u16() : r.u32(); 1493 const items = /* @__PURE__ */ new Map(); 1494 for (let i = 0; i < itemCount; i++) { 1495 const itemId = version < 2 ? r.u16() : r.u32(); 1496 let constructionMethod = 0; 1497 if (version === 1 || version === 2) { 1498 const cm = r.u16(); 1499 constructionMethod = cm & 15; 1500 } 1501 r.u16(); 1502 const baseOffset = r.uN(baseOffsetSize); 1503 const extentCount = r.u16(); 1504 const extents = []; 1505 for (let j = 0; j < extentCount; j++) { 1506 if (version >= 1) { 1507 r.uN(indexSize); 1508 } 1509 const extOffset = r.uN(offsetSize); 1510 const extLength = r.uN(lengthSize); 1511 extents.push({ 1512 offset: baseOffset + extOffset, 1513 length: extLength 1514 }); 1515 } 1516 items.set(itemId, { constructionMethod, extents }); 1517 } 1518 return items; 1519 } 1520 function parseIpma(r, box) { 1521 r.pos = box.offset + box.headerSize; 1522 const vf = r.u32(); 1523 const version = vf >>> 24; 1524 const flags = vf & 16777215; 1525 const largeIndex = (flags & 1) !== 0; 1526 const entryCount = r.u32(); 1527 const associations = /* @__PURE__ */ new Map(); 1528 for (let i = 0; i < entryCount; i++) { 1529 const itemId = version < 1 ? r.u16() : r.u32(); 1530 const assocCount = r.u8(); 1531 const indices = []; 1532 for (let j = 0; j < assocCount; j++) { 1533 if (largeIndex) { 1534 indices.push(r.u16() & 32767); 1535 } else { 1536 indices.push(r.u8() & 127); 1537 } 1538 } 1539 associations.set(itemId, indices); 1540 } 1541 return associations; 1542 } 1543 function parseIspe(r, box) { 1544 r.pos = box.offset + box.headerSize + 4; 1545 return { width: r.u32(), height: r.u32() }; 1546 } 1547 function parseIrot(r, box) { 1548 r.pos = box.offset + box.headerSize; 1549 return (r.u8() & 3) * 90; 1550 } 1551 function parseIinf(r, box) { 1552 r.pos = box.offset + box.headerSize; 1553 const version = r.u8(); 1554 r.pos += 3; 1555 const entryCount = version === 0 ? r.u16() : r.u32(); 1556 const itemTypes = /* @__PURE__ */ new Map(); 1557 const entriesStart = r.pos; 1558 const boxEnd = box.offset + box.size; 1559 const infeBoxes = findBoxes(r, entriesStart, boxEnd); 1560 for (let i = 0; i < Math.min(entryCount, infeBoxes.length); i++) { 1561 const infe = infeBoxes[i]; 1562 if (infe.type !== "infe") { 1563 continue; 1564 } 1565 r.pos = infe.offset + infe.headerSize; 1566 const infeVersion = r.u8(); 1567 r.pos += 3; 1568 if (infeVersion >= 2) { 1569 const itemId = infeVersion === 2 ? r.u16() : r.u32(); 1570 r.u16(); 1571 const itemType = r.str(4); 1572 itemTypes.set(itemId, itemType); 1573 } 1574 } 1575 return itemTypes; 1576 } 1577 function parseIref(r, box, refType) { 1578 r.pos = box.offset + box.headerSize; 1579 const version = r.u8(); 1580 r.pos += 3; 1581 const refs = /* @__PURE__ */ new Map(); 1582 const boxEnd = box.offset + box.size; 1583 while (r.pos < boxEnd) { 1584 const refBox = readBox(r); 1585 if (!refBox || refBox.size < 8) { 1586 break; 1587 } 1588 r.pos = refBox.offset + refBox.headerSize; 1589 const fromId = version === 0 ? r.u16() : r.u32(); 1590 const refCount = r.u16(); 1591 const toIds = []; 1592 for (let i = 0; i < refCount; i++) { 1593 toIds.push(version === 0 ? r.u16() : r.u32()); 1594 } 1595 if (refBox.type === refType) { 1596 refs.set(fromId, toIds); 1597 } 1598 r.pos = refBox.offset + refBox.size; 1599 } 1600 return refs; 1601 } 1602 function reverseBits32(n) { 1603 n = n >>> 1 & 1431655765 | (n & 1431655765) << 1; 1604 n = n >>> 2 & 858993459 | (n & 858993459) << 2; 1605 n = n >>> 4 & 252645135 | (n & 252645135) << 4; 1606 n = n >>> 8 & 16711935 | (n & 16711935) << 8; 1607 n = n >>> 16 | n << 16; 1608 return n >>> 0; 1609 } 1610 function buildCodecString(r, recordOffset) { 1611 r.pos = recordOffset; 1612 r.u8(); 1613 const byte1 = r.u8(); 1614 const profileSpace = byte1 >> 6 & 3; 1615 const tierFlag = byte1 >> 5 & 1; 1616 const profileIdc = byte1 & 31; 1617 const compatFlags = r.u32(); 1618 const constraintBytes = r.bytes(6); 1619 const levelIdc = r.u8(); 1620 const spacePrefix = profileSpace > 0 ? String.fromCharCode(64 + profileSpace) : ""; 1621 const compatHex = reverseBits32(compatFlags).toString(16).toUpperCase(); 1622 const tierChar = tierFlag ? "H" : "L"; 1623 let lastNonZero = -1; 1624 for (let i = 5; i >= 0; i--) { 1625 if (constraintBytes[i] !== 0) { 1626 lastNonZero = i; 1627 break; 1628 } 1629 } 1630 let constraintStr = ""; 1631 if (lastNonZero >= 0) { 1632 const parts = []; 1633 for (let i = 0; i <= lastNonZero; i++) { 1634 parts.push(constraintBytes[i].toString(16).toUpperCase()); 1635 } 1636 constraintStr = "." + parts.join("."); 1637 } 1638 return `hvc1.$spacePrefix}$profileIdc}.$compatHex}.$tierChar}$levelIdc}$constraintStr}`; 1639 } 1640 function readItemData(buffer, loc, idatOffset) { 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 } 1649 if (loc.extents.length === 1) { 1650 const ext = loc.extents[0]; 1651 const start = baseOffset + ext.offset; 1652 return new Uint8Array(buffer.slice(start, start + ext.length)); 1653 } 1654 let totalLength = 0; 1655 for (const ext of loc.extents) { 1656 totalLength += ext.length; 1657 } 1658 const data = new Uint8Array(totalLength); 1659 let pos = 0; 1660 for (const ext of loc.extents) { 1661 const start = baseOffset + ext.offset; 1662 data.set( 1663 new Uint8Array(buffer.slice(start, start + ext.length)), 1664 pos 1665 ); 1666 pos += ext.length; 1667 } 1668 return data; 1669 } 1670 function findHvcProperties(propIndices, properties) { 1671 let hvcCBox; 1672 let ispeBox; 1673 let irotBox; 1674 for (const idx of propIndices) { 1675 if (idx < 1 || idx > properties.length) { 1676 continue; 1677 } 1678 const prop = properties[idx - 1]; 1679 if (prop.type === "hvcC" && !hvcCBox) { 1680 hvcCBox = prop; 1681 } 1682 if (prop.type === "ispe" && !ispeBox) { 1683 ispeBox = prop; 1684 } 1685 if (prop.type === "irot" && !irotBox) { 1686 irotBox = prop; 1687 } 1688 } 1689 if (!hvcCBox) { 1690 throw new Error("No HEVC configuration (hvcC) found"); 1691 } 1692 if (!ispeBox) { 1693 throw new Error("No image dimensions (ispe) found"); 1694 } 1695 return { hvcCBox, ispeBox, irotBox }; 1696 } 1697 function parseHeic(buffer) { 1698 const r = new Reader(buffer); 1699 const fileEnd = buffer.byteLength; 1700 const metaBox = findBox(r, 0, fileEnd, "meta"); 1701 if (!metaBox) { 1702 throw new Error("No meta box found in HEIC file"); 1703 } 1704 const metaChildStart = metaBox.offset + metaBox.headerSize + 4; 1705 const metaEnd = metaBox.offset + metaBox.size; 1706 const children = findBoxes(r, metaChildStart, metaEnd); 1707 const pitmBox = children.find((b) => b.type === "pitm"); 1708 const ilocBox = children.find((b) => b.type === "iloc"); 1709 const iprpBox = children.find((b) => b.type === "iprp"); 1710 const iinfBox = children.find((b) => b.type === "iinf"); 1711 const irefBox = children.find((b) => b.type === "iref"); 1712 const idatBox = children.find((b) => b.type === "idat"); 1713 const idatOffset = idatBox ? idatBox.offset + idatBox.headerSize : 0; 1714 if (!pitmBox || !ilocBox || !iprpBox) { 1715 throw new Error("Missing required boxes (pitm, iloc, iprp) in HEIC"); 1716 } 1717 const primaryId = parsePitm(r, pitmBox); 1718 const locations = parseIloc(r, ilocBox); 1719 const iprpStart = iprpBox.offset + iprpBox.headerSize; 1720 const iprpEnd = iprpBox.offset + iprpBox.size; 1721 const iprpChildren = findBoxes(r, iprpStart, iprpEnd); 1722 const ipcoBox = iprpChildren.find((b) => b.type === "ipco"); 1723 const ipmaBox = iprpChildren.find((b) => b.type === "ipma"); 1724 if (!ipcoBox || !ipmaBox) { 1725 throw new Error("Missing ipco or ipma in HEIC properties"); 1726 } 1727 const allAssoc = parseIpma(r, ipmaBox); 1728 const ipcoStart = ipcoBox.offset + ipcoBox.headerSize; 1729 const ipcoEnd = ipcoBox.offset + ipcoBox.size; 1730 const properties = findBoxes(r, ipcoStart, ipcoEnd); 1731 let primaryItemType = "hvc1"; 1732 if (iinfBox) { 1733 const itemTypes = parseIinf(r, iinfBox); 1734 const t = itemTypes.get(primaryId); 1735 if (t) { 1736 primaryItemType = t; 1737 } 1738 } 1739 if (primaryItemType === "grid") { 1740 return parseGridImage( 1741 r, 1742 buffer, 1743 primaryId, 1744 locations, 1745 allAssoc, 1746 properties, 1747 irefBox, 1748 idatOffset 1749 ); 1750 } 1751 const primaryLoc = locations.get(primaryId); 1752 if (!primaryLoc || primaryLoc.extents.length === 0) { 1753 throw new Error(`No location data for primary item $primaryId}`); 1754 } 1755 const primaryPropIndices = allAssoc.get(primaryId); 1756 if (!primaryPropIndices || primaryPropIndices.length === 0) { 1757 throw new Error("No property associations for primary item"); 1758 } 1759 const { hvcCBox, ispeBox, irotBox } = findHvcProperties( 1760 primaryPropIndices, 1761 properties 1762 ); 1763 const hvcCDataStart = hvcCBox.offset + hvcCBox.headerSize; 1764 const hvcCDataSize = hvcCBox.size - hvcCBox.headerSize; 1765 const description = new Uint8Array( 1766 buffer.slice(hvcCDataStart, hvcCDataStart + hvcCDataSize) 1767 ); 1768 const codecString = buildCodecString(r, hvcCDataStart); 1769 const { width, height } = parseIspe(r, ispeBox); 1770 const rotation = irotBox ? parseIrot(r, irotBox) : 0; 1771 return { 1772 codecString, 1773 description, 1774 tiles: [ 1775 { 1776 data: readItemData(buffer, primaryLoc, idatOffset), 1777 x: 0, 1778 y: 0 1779 } 1780 ], 1781 tileWidth: width, 1782 tileHeight: height, 1783 outputWidth: width, 1784 outputHeight: height, 1785 rotation, 1786 exifOrientation: rotation === 0 ? getUnappliedExifOrientation(buffer) : 1 1787 }; 1788 } 1789 function parseGridImage(r, buffer, gridItemId, locations, allAssoc, properties, irefBox, idatOffset) { 1790 const gridLoc = locations.get(gridItemId); 1791 if (!gridLoc || gridLoc.extents.length === 0) { 1792 throw new Error("No location data for grid item"); 1793 } 1794 const gridData = readItemData(buffer, gridLoc, idatOffset); 1795 const largeFields = gridData.length > 1 && (gridData[1] & 1) !== 0; 1796 const minGridSize = largeFields ? 12 : 8; 1797 if (gridData.length < minGridSize) { 1798 throw new Error( 1799 `Grid descriptor too short: $gridData.length} bytes` 1800 ); 1801 } 1802 const rows = gridData[2] + 1; 1803 const columns = gridData[3] + 1; 1804 const gv = new DataView(gridData.buffer, gridData.byteOffset); 1805 let outputWidth; 1806 let outputHeight; 1807 if (largeFields) { 1808 outputWidth = gv.getUint32(4); 1809 outputHeight = gv.getUint32(8); 1810 } else { 1811 outputWidth = gv.getUint16(4); 1812 outputHeight = gv.getUint16(6); 1813 } 1814 if (!irefBox) { 1815 throw new Error("Grid image requires iref box"); 1816 } 1817 const dimgRefs = parseIref(r, irefBox, "dimg"); 1818 const tileItemIds = dimgRefs.get(gridItemId); 1819 if (!tileItemIds || tileItemIds.length === 0) { 1820 throw new Error("No tile references found for grid item"); 1821 } 1822 const expectedTiles = rows * columns; 1823 if (tileItemIds.length < expectedTiles) { 1824 throw new Error( 1825 `Grid expects $expectedTiles} tiles but found $tileItemIds.length}` 1826 ); 1827 } 1828 const firstTileProps = allAssoc.get(tileItemIds[0]); 1829 if (!firstTileProps || firstTileProps.length === 0) { 1830 throw new Error("No property associations for tile item"); 1831 } 1832 const { hvcCBox, ispeBox } = findHvcProperties( 1833 firstTileProps, 1834 properties 1835 ); 1836 const gridProps = allAssoc.get(gridItemId) || []; 1837 let irotBox; 1838 for (const idx of gridProps) { 1839 if (idx >= 1 && idx <= properties.length) { 1840 const prop = properties[idx - 1]; 1841 if (prop.type === "irot") { 1842 irotBox = prop; 1843 break; 1844 } 1845 } 1846 } 1847 const hvcCDataStart = hvcCBox.offset + hvcCBox.headerSize; 1848 const hvcCDataSize = hvcCBox.size - hvcCBox.headerSize; 1849 const description = new Uint8Array( 1850 buffer.slice(hvcCDataStart, hvcCDataStart + hvcCDataSize) 1851 ); 1852 const codecString = buildCodecString(r, hvcCDataStart); 1853 const { width: tileWidth, height: tileHeight } = parseIspe(r, ispeBox); 1854 const tiles = []; 1855 for (let row = 0; row < rows; row++) { 1856 for (let col = 0; col < columns; col++) { 1857 const tileIdx = row * columns + col; 1858 const tileId = tileItemIds[tileIdx]; 1859 const tileLoc = locations.get(tileId); 1860 if (!tileLoc || tileLoc.extents.length === 0) { 1861 throw new Error(`No location data for tile item $tileId}`); 1862 } 1863 tiles.push({ 1864 data: readItemData(buffer, tileLoc, idatOffset), 1865 x: col * tileWidth, 1866 y: row * tileHeight 1867 }); 1868 } 1869 } 1870 const rotation = irotBox ? parseIrot(r, irotBox) : 0; 1871 return { 1872 codecString, 1873 description, 1874 tiles, 1875 tileWidth, 1876 tileHeight, 1877 outputWidth, 1878 outputHeight, 1879 rotation, 1880 exifOrientation: rotation === 0 ? getUnappliedExifOrientation(buffer) : 1 1881 }; 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 } 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 2136 // packages/upload-media/build-module/canvas-utils.mjs 2137 var IMAGE_DECODER_TIMEOUT = 15e3; 2138 var HeicUnsupportedError = class extends Error { 2139 }; 2140 async function canvasConvertToJpeg(file, quality = 0.82) { 2141 const baseName = getFileBasename(file.name); 2142 try { 2143 const bitmap = await createImageBitmap(file); 2144 try { 2145 const canvas = new OffscreenCanvas(bitmap.width, bitmap.height); 2146 const ctx = canvas.getContext("2d"); 2147 if (!ctx) { 2148 throw new Error("Could not get canvas 2d context"); 2149 } 2150 ctx.drawImage(bitmap, 0, 0); 2151 const jpegBlob = await canvas.convertToBlob({ 2152 type: "image/jpeg", 2153 quality 2154 }); 2155 return new File([jpegBlob], `$baseName}.jpg`, { 2156 type: "image/jpeg" 2157 }); 2158 } finally { 2159 bitmap.close(); 2160 } 2161 } catch { 2162 } 2163 if (typeof ImageDecoder !== "undefined") { 2164 const supported = await ImageDecoder.isTypeSupported(file.type); 2165 if (supported) { 2166 const decoder = new ImageDecoder({ 2167 type: file.type, 2168 data: file.stream() 2169 }); 2170 let videoFrame; 2171 let timeoutId; 2172 let timedOut = false; 2173 try { 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) { 2198 try { 2199 const canvas = new OffscreenCanvas( 2200 videoFrame.displayWidth, 2201 videoFrame.displayHeight 2202 ); 2203 const ctx = canvas.getContext("2d"); 2204 if (!ctx) { 2205 throw new Error("Could not get canvas 2d context"); 2206 } 2207 ctx.drawImage(videoFrame, 0, 0); 2208 const jpegBlob = await canvas.convertToBlob({ 2209 type: "image/jpeg", 2210 quality 2211 }); 2212 return new File([jpegBlob], `$baseName}.jpg`, { 2213 type: "image/jpeg" 2214 }); 2215 } finally { 2216 videoFrame.close(); 2217 } 2218 } 2219 } 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 } 2229 if (typeof VideoDecoder !== "undefined") { 2230 let supported = false; 2231 try { 2232 const support = await VideoDecoder.isConfigSupported({ 2233 codec: heicData.codecString 2234 }); 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 2254 ); 2255 try { 2256 ctx.drawImage(frame, tile.x, tile.y); 2257 } finally { 2258 frame.close(); 2259 } 2260 } 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 }); 2269 } 2270 } 2271 throw new HeicUnsupportedError(getHeicUnsupportedMessage()); 2272 } 2273 function applyRotation(source, rotation) { 2274 if (rotation === 0) { 2275 return source; 2276 } 2277 const swap = rotation === 90 || rotation === 270; 2278 const w = swap ? source.height : source.width; 2279 const h = swap ? source.width : source.height; 2280 const rotated = new OffscreenCanvas(w, h); 2281 const ctx = rotated.getContext("2d"); 2282 if (!ctx) { 2283 return source; 2284 } 2285 ctx.translate(w / 2, h / 2); 2286 ctx.rotate(-rotation * Math.PI / 180); 2287 ctx.drawImage(source, -source.width / 2, -source.height / 2); 2288 return rotated; 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 } 2327 function decodeHevcFrame(codec, description, width, height, data) { 2328 return new Promise((resolve, reject) => { 2329 const decoder = new VideoDecoder({ 2330 output: (frame) => { 2331 decoder.close(); 2332 resolve(frame); 2333 }, 2334 error: (e) => { 2335 if (decoder.state !== "closed") { 2336 decoder.close(); 2337 } 2338 reject(e); 2339 } 2340 }); 2341 decoder.configure({ 2342 codec, 2343 codedWidth: width, 2344 codedHeight: height, 2345 description 2346 }); 2347 decoder.decode( 2348 new EncodedVideoChunk({ 2349 type: "key", 2350 timestamp: 0, 2351 data 2352 }) 2353 ); 2354 decoder.flush().catch((e) => { 2355 if (decoder.state !== "closed") { 2356 decoder.close(); 2357 } 2358 reject(e); 2359 }); 2360 }); 2361 } 2362 2363 // packages/upload-media/build-module/feature-detection.mjs 2364 var cachedResult = null; 2365 function detectClientSideMediaSupport() { 2366 if (cachedResult !== null) { 2367 return cachedResult; 2368 } 2369 if (typeof WebAssembly === "undefined") { 2370 cachedResult = { 2371 supported: false, 2372 reason: "WebAssembly is not supported in this browser." 2373 }; 2374 return cachedResult; 2375 } 2376 if (typeof SharedArrayBuffer === "undefined") { 2377 cachedResult = { 2378 supported: false, 2379 reason: "SharedArrayBuffer is not available. This may be due to missing cross-origin isolation headers." 2380 }; 2381 return cachedResult; 2382 } 2383 if (typeof Worker === "undefined") { 2384 cachedResult = { 2385 supported: false, 2386 reason: "Web Workers are not supported in this browser." 2387 }; 2388 return cachedResult; 2389 } 2390 if (typeof navigator !== "undefined" && "deviceMemory" in navigator && navigator.deviceMemory <= 2) { 2391 cachedResult = { 2392 supported: false, 2393 reason: "Device has insufficient memory for client-side media processing." 2394 }; 2395 return cachedResult; 2396 } 2397 if (typeof navigator !== "undefined" && "hardwareConcurrency" in navigator && navigator.hardwareConcurrency < 2) { 2398 cachedResult = { 2399 supported: false, 2400 reason: "Device has insufficient CPU cores for client-side media processing." 2401 }; 2402 return cachedResult; 2403 } 2404 if (typeof navigator !== "undefined") { 2405 const connection = navigator.connection; 2406 if (connection) { 2407 if (connection.saveData) { 2408 cachedResult = { 2409 supported: false, 2410 reason: "Data saver mode is enabled." 2411 }; 2412 return cachedResult; 2413 } 2414 if (connection.effectiveType === "slow-2g" || connection.effectiveType === "2g") { 2415 cachedResult = { 2416 supported: false, 2417 reason: "Network connection is too slow for client-side media processing." 2418 }; 2419 return cachedResult; 2420 } 2421 } 2422 } 2423 if (typeof window !== "undefined") { 2424 try { 2425 const testBlob = new Blob([""], { 2426 type: "application/javascript" 2427 }); 2428 const testUrl = URL.createObjectURL(testBlob); 2429 try { 2430 const testWorker = new Worker(testUrl); 2431 testWorker.terminate(); 2432 } finally { 2433 URL.revokeObjectURL(testUrl); 2434 } 2435 } catch { 2436 cachedResult = { 2437 supported: false, 2438 reason: "The site's Content Security Policy (CSP) does not allow blob: workers. The worker-src directive must include blob: to enable client-side media processing." 2439 }; 2440 return cachedResult; 2441 } 2442 } 2443 cachedResult = { supported: true }; 2444 return cachedResult; 2445 } 2446 function isClientSideMediaSupported() { 2447 return detectClientSideMediaSupport().supported; 2448 } 2449 function isHeicCanvasSupported() { 2450 return typeof createImageBitmap !== "undefined" && typeof OffscreenCanvas !== "undefined"; 2451 } 2452 function clearFeatureDetectionCache() { 2453 cachedResult = null; 2454 } 2455 var BYTES_PER_PIXEL = 4; 2456 var INTERLACED_MEMORY_BUDGET = 0.5 * 1024 * 1024 * 1024; 2457 var BASELINE_MEMORY_BUDGET = 0.9 * 1024 * 1024 * 1024; 2458 function exceedsClientProcessingMemory(dimensions) { 2459 const { width, height, interlaced } = dimensions; 2460 const estimatedBytes = width * height * BYTES_PER_PIXEL; 2461 const budget = interlaced ? INTERLACED_MEMORY_BUDGET : BASELINE_MEMORY_BUDGET; 2462 return estimatedBytes > budget; 2463 } 2464 2465 // packages/upload-media/build-module/get-image-dimensions.mjs 2466 var MAX_HEADER_BYTES = 512 * 1024; 2467 function parseJpeg(view) { 2468 let offset = 2; 2469 while (offset < view.byteLength) { 2470 if (view.getUint8(offset) !== 255) { 2471 return null; 2472 } 2473 while (offset < view.byteLength && view.getUint8(offset) === 255) { 2474 offset++; 2475 } 2476 if (offset >= view.byteLength) { 2477 return null; 2478 } 2479 const marker = view.getUint8(offset); 2480 offset++; 2481 if (marker === 1 || marker >= 208 && marker <= 217) { 2482 continue; 2483 } 2484 if (marker === 218) { 2485 return null; 2486 } 2487 if (offset + 2 > view.byteLength) { 2488 return null; 2489 } 2490 const segmentLength = view.getUint16(offset); 2491 if (segmentLength < 2) { 2492 return null; 2493 } 2494 const isStartOfFrame = marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204; 2495 if (isStartOfFrame) { 2496 if (offset + 7 > view.byteLength) { 2497 return null; 2498 } 2499 const height = view.getUint16(offset + 3); 2500 const width = view.getUint16(offset + 5); 2501 const interlaced = marker === 194 || marker === 198 || marker === 202 || marker === 206; 2502 return { width, height, interlaced }; 2503 } 2504 offset += segmentLength; 2505 } 2506 return null; 2507 } 2508 function parsePng(view) { 2509 if (view.byteLength < 29) { 2510 return null; 2511 } 2512 const isIhdr = view.getUint8(12) === 73 && // I 2513 view.getUint8(13) === 72 && // H 2514 view.getUint8(14) === 68 && // D 2515 view.getUint8(15) === 82; 2516 if (!isIhdr) { 2517 return null; 2518 } 2519 const width = view.getUint32(16); 2520 const height = view.getUint32(20); 2521 const interlaceMethod = view.getUint8(28); 2522 return { width, height, interlaced: interlaceMethod !== 0 }; 2523 } 2524 async function getImageDimensions(file) { 2525 try { 2526 const headerBytes = Math.min(file.size, MAX_HEADER_BYTES); 2527 const buffer = await file.slice(0, headerBytes).arrayBuffer(); 2528 const view = new DataView(buffer); 2529 if (view.byteLength >= 3 && view.getUint16(0) === 65496) { 2530 return parseJpeg(view); 2531 } 2532 if (view.byteLength >= 8 && view.getUint32(0) === 2303741511 && view.getUint32(4) === 218765834) { 2533 return parsePng(view); 2534 } 2535 return null; 2536 } catch { 2537 return null; 2538 } 2539 } 2540 2541 // packages/upload-media/build-module/stub-file.mjs 2542 var StubFile = class extends File { 2543 constructor(fileName = "stub-file") { 2544 super([], fileName); 2545 } 2546 }; 2547 2548 // packages/upload-media/build-module/store/private-actions.mjs 2549 var DEFAULT_OUTPUT_QUALITY = 0.82; 2550 var ultraHdrItems = /* @__PURE__ */ new Set(); 2551 function addItem({ 2552 file: fileOrBlob, 2553 batchId, 2554 onChange, 2555 onSuccess, 2556 onBatchSuccess, 2557 onError, 2558 additionalData = {}, 2559 sourceUrl, 2560 sourceAttachmentId, 2561 abortController, 2562 operations 2563 }) { 2564 return async ({ dispatch }) => { 2565 const itemId = v4_default(); 2566 const file = convertBlobToFile(fileOrBlob); 2567 let blobUrl; 2568 if (!(file instanceof StubFile)) { 2569 blobUrl = (0, import_blob.createBlobURL)(file); 2570 dispatch({ 2571 type: Type.CacheBlobUrl, 2572 id: itemId, 2573 blobUrl 2574 }); 2575 } 2576 dispatch({ 2577 type: Type.Add, 2578 item: { 2579 id: itemId, 2580 batchId, 2581 status: ItemStatus.Processing, 2582 sourceFile: cloneFile(file), 2583 file, 2584 attachment: { 2585 url: blobUrl 2586 }, 2587 additionalData: { 2588 generate_sub_sizes: false, 2589 ...additionalData 2590 }, 2591 onChange, 2592 onSuccess, 2593 onBatchSuccess, 2594 onError, 2595 sourceUrl, 2596 sourceAttachmentId, 2597 abortController: abortController || new AbortController(), 2598 operations: Array.isArray(operations) ? operations : [OperationType.Prepare] 2599 } 2600 }); 2601 dispatch.processItem(itemId); 2602 }; 2603 } 2604 function addSideloadItem({ 2605 file, 2606 onChange, 2607 additionalData, 2608 operations, 2609 batchId, 2610 parentId 2611 }) { 2612 return ({ dispatch }) => { 2613 const itemId = v4_default(); 2614 dispatch({ 2615 type: Type.Add, 2616 item: { 2617 id: itemId, 2618 batchId, 2619 status: ItemStatus.Processing, 2620 sourceFile: cloneFile(file), 2621 file, 2622 onChange, 2623 additionalData: { 2624 ...additionalData 2625 }, 2626 parentId, 2627 operations: Array.isArray(operations) ? operations : [OperationType.Prepare], 2628 abortController: new AbortController() 2629 } 2630 }); 2631 dispatch.processItem(itemId); 2632 }; 2633 } 2634 function processItem(id) { 2635 return async ({ select: select2, dispatch }) => { 2636 if (select2.isPaused()) { 2637 return; 2638 } 2639 const item = select2.getItem(id); 2640 if (!item) { 2641 return; 2642 } 2643 if (item.currentOperation) { 2644 return; 2645 } 2646 const { 2647 attachment, 2648 onChange, 2649 onSuccess, 2650 onBatchSuccess, 2651 batchId, 2652 parentId 2653 } = item; 2654 const operation = Array.isArray(item.operations?.[0]) ? item.operations[0][0] : item.operations?.[0]; 2655 const operationArgs = Array.isArray(item.operations?.[0]) ? item.operations[0][1] : void 0; 2656 if (operation === OperationType.Upload) { 2657 const settings = select2.getSettings(); 2658 const activeCount = select2.getActiveUploadCount(); 2659 if (activeCount >= settings.maxConcurrentUploads) { 2660 return; 2661 } 2662 } 2663 if (operation === OperationType.ResizeCrop || operation === OperationType.Rotate) { 2664 const settings = select2.getSettings(); 2665 const activeCount = select2.getActiveImageProcessingCount(); 2666 if (activeCount >= settings.maxConcurrentImageProcessing) { 2667 return; 2668 } 2669 } 2670 if (operation === OperationType.TranscodeGif) { 2671 const activeCount = select2.getActiveVideoProcessingCount(); 2672 if (activeCount >= 1) { 2673 return; 2674 } 2675 } 2676 if (attachment) { 2677 const isHeicUrl = attachment.url && /\.hei[cf]$/i.test(attachment.url); 2678 if (!isHeicUrl) { 2679 onChange?.([attachment]); 2680 } 2681 } 2682 if (!operation) { 2683 if (parentId || !parentId && !select2.hasPendingItemsByParentId(id)) { 2684 if (attachment) { 2685 onSuccess?.([attachment]); 2686 } 2687 dispatch.removeItem(id); 2688 dispatch.revokeBlobUrls(id); 2689 if (batchId && select2.isBatchUploaded(batchId)) { 2690 onBatchSuccess?.(); 2691 } 2692 } 2693 if (parentId && batchId && select2.isBatchUploaded(batchId)) { 2694 const parentItem = select2.getItem(parentId); 2695 if (!parentItem) { 2696 return; 2697 } 2698 if (parentItem.operations && parentItem.operations.length > 0) { 2699 dispatch.processItem(parentId); 2700 return; 2701 } 2702 if (attachment) { 2703 parentItem.onSuccess?.([attachment]); 2704 } 2705 dispatch.removeItem(parentId); 2706 dispatch.revokeBlobUrls(parentId); 2707 if (parentItem.batchId && select2.isBatchUploaded(parentItem.batchId)) { 2708 parentItem.onBatchSuccess?.(); 2709 } 2710 } 2711 return; 2712 } 2713 if (operation === OperationType.Finalize && select2.hasPendingItemsByParentId(id)) { 2714 return; 2715 } 2716 dispatch({ 2717 type: Type.OperationStart, 2718 id, 2719 operation 2720 }); 2721 debug( 2722 `Starting operation $operation} for $item.file.name} (item $item.id})` 2723 ); 2724 switch (operation) { 2725 case OperationType.Prepare: 2726 dispatch.prepareItem(item.id); 2727 break; 2728 case OperationType.ResizeCrop: 2729 dispatch.resizeCropItem( 2730 item.id, 2731 operationArgs 2732 ); 2733 break; 2734 case OperationType.Rotate: 2735 dispatch.rotateItem( 2736 item.id, 2737 operationArgs 2738 ); 2739 break; 2740 case OperationType.TranscodeImage: 2741 dispatch.transcodeImageItem( 2742 item.id, 2743 operationArgs 2744 ); 2745 break; 2746 case OperationType.TranscodeGif: 2747 dispatch.transcodeGifItem( 2748 item.id, 2749 operationArgs 2750 ); 2751 break; 2752 case OperationType.Upload: 2753 if (item.parentId) { 2754 dispatch.sideloadItem(id); 2755 } else { 2756 dispatch.uploadItem(id); 2757 } 2758 break; 2759 case OperationType.ThumbnailGeneration: 2760 dispatch.generateThumbnails(id); 2761 break; 2762 case OperationType.Finalize: 2763 dispatch.finalizeItem(id); 2764 break; 2765 case OperationType.DetectUltraHdr: 2766 dispatch.detectUltraHdr(id); 2767 break; 2768 } 2769 }; 2770 } 2771 function pauseQueue() { 2772 return { 2773 type: Type.PauseQueue 2774 }; 2775 } 2776 function resumeQueue() { 2777 return async ({ select: select2, dispatch }) => { 2778 dispatch({ 2779 type: Type.ResumeQueue 2780 }); 2781 for (const item of select2.getAllItems()) { 2782 if (item.status === ItemStatus.PendingRetry) { 2783 dispatch.executeRetry(item.id); 2784 } else { 2785 dispatch.processItem(item.id); 2786 } 2787 } 2788 }; 2789 } 2790 function pauseItem(id) { 2791 return async ({ dispatch }) => { 2792 dispatch({ 2793 type: Type.PauseItem, 2794 id 2795 }); 2796 }; 2797 } 2798 function removeItem(id) { 2799 return async ({ select: select2, dispatch }) => { 2800 const item = select2.getItem(id); 2801 if (!item) { 2802 return; 2803 } 2804 ultraHdrItems.delete(id); 2805 clearRetryTimer(id); 2806 dispatch({ 2807 type: Type.Remove, 2808 id 2809 }); 2810 if (select2.getAllItems().length === 0) { 2811 terminateVipsWorker(); 2812 terminateVideoConversionWorker(); 2813 } 2814 }; 2815 } 2816 function finishOperation(id, updates) { 2817 return async ({ select: select2, dispatch }) => { 2818 const item = select2.getItem(id); 2819 const previousOperation = item?.currentOperation; 2820 dispatch({ 2821 type: Type.OperationFinish, 2822 id, 2823 item: updates 2824 }); 2825 dispatch.processItem(id); 2826 if (previousOperation === OperationType.Upload) { 2827 const pendingUploads = select2.getPendingUploads(); 2828 for (const pendingItem of pendingUploads) { 2829 dispatch.processItem(pendingItem.id); 2830 } 2831 } 2832 if (previousOperation === OperationType.ResizeCrop || previousOperation === OperationType.Rotate) { 2833 const pendingItems = select2.getPendingImageProcessing(); 2834 for (const pendingItem of pendingItems) { 2835 dispatch.processItem(pendingItem.id); 2836 } 2837 } 2838 if (previousOperation === OperationType.TranscodeGif) { 2839 const pendingItems = select2.getPendingVideoProcessing(); 2840 for (const pendingItem of pendingItems) { 2841 dispatch.processItem(pendingItem.id); 2842 } 2843 } 2844 if (previousOperation === OperationType.ResizeCrop || previousOperation === OperationType.Rotate || previousOperation === OperationType.TranscodeImage) { 2845 maybeRecycleVipsWorker(select2.getActiveImageProcessingCount()); 2846 } 2847 }; 2848 } 2849 var VALID_IMAGE_FORMATS = ["jpeg", "webp", "avif", "png", "gif"]; 2850 function isValidImageFormat(format) { 2851 return VALID_IMAGE_FORMATS.includes(format); 2852 } 2853 async function getTranscodeImageOperation(file, outputMimeType, interlaced = false, quality = DEFAULT_OUTPUT_QUALITY) { 2854 if (file.type === "image/png" && outputMimeType === "image/jpeg") { 2855 const blobUrl = (0, import_blob.createBlobURL)(file); 2856 try { 2857 const hasAlpha = await vipsHasTransparency(blobUrl); 2858 if (hasAlpha) { 2859 return null; 2860 } 2861 } catch { 2862 return null; 2863 } finally { 2864 (0, import_blob.revokeBlobURL)(blobUrl); 2865 } 2866 } 2867 const formatPart = outputMimeType.split("/")[1]; 2868 if (!isValidImageFormat(formatPart)) { 2869 return null; 2870 } 2871 return [ 2872 OperationType.TranscodeImage, 2873 { 2874 outputFormat: formatPart, 2875 outputQuality: quality, 2876 interlaced 2877 } 2878 ]; 2879 } 2880 function prepareItem(id) { 2881 return async ({ select: select2, dispatch }) => { 2882 const item = select2.getItem(id); 2883 if (!item) { 2884 return; 2885 } 2886 const { file } = item; 2887 const operations = []; 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 } 2924 let heicJpeg = null; 2925 const isHeic = await isHeicFile(file); 2926 const isMisnamedHeic = isHeic && !HEIC_MIME_TYPES.includes(file.type); 2927 const isImage = file.type.startsWith("image/") || isHeic; 2928 const isVipsSupported = !isHeic && CLIENT_SIDE_SUPPORTED_MIME_TYPES.includes(file.type); 2929 const heicFile = isMisnamedHeic ? new File([file], `$getFileBasename(file.name)}.heic`, { 2930 type: HEIC_MIME_TYPES[0], 2931 lastModified: file.lastModified 2932 }) : file; 2933 let tooLargeForClient = false; 2934 if (isImage && isVipsSupported) { 2935 const dimensions = await getImageDimensions(file); 2936 if (dimensions && exceedsClientProcessingMemory(dimensions)) { 2937 tooLargeForClient = true; 2938 } 2939 } 2940 if (file.type === "image/jpeg" && !isHeic && !tooLargeForClient) { 2941 operations.push(OperationType.DetectUltraHdr); 2942 } 2943 if (isImage && isVipsSupported && !tooLargeForClient) { 2944 operations.push( 2945 OperationType.Upload, 2946 OperationType.ThumbnailGeneration, 2947 OperationType.Finalize 2948 ); 2949 } else if (isImage && isHeic) { 2950 try { 2951 heicJpeg = await canvasConvertToJpeg( 2952 heicFile, 2953 settings.imageQuality ?? DEFAULT_OUTPUT_QUALITY 2954 ); 2955 } catch (error) { 2956 const unsupported = error instanceof HeicUnsupportedError; 2957 dispatch.cancelItem( 2958 id, 2959 new UploadError({ 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 2966 }) 2967 ); 2968 return; 2969 } 2970 operations.push( 2971 OperationType.Upload, 2972 OperationType.ThumbnailGeneration, 2973 OperationType.Finalize 2974 ); 2975 } else { 2976 operations.push(OperationType.Upload); 2977 } 2978 dispatch({ 2979 type: Type.AddOperations, 2980 id, 2981 operations 2982 }); 2983 let updates; 2984 if (isHeic && heicJpeg) { 2985 const vipsAvailable = isClientSideMediaSupported(); 2986 updates = { 2987 file: heicJpeg, 2988 sourceFile: heicJpeg, 2989 originalHeicFile: heicFile, 2990 additionalData: { 2991 ...item.additionalData, 2992 generate_sub_sizes: !vipsAvailable, 2993 convert_format: true 2994 } 2995 }; 2996 } else if (!isVipsSupported || !isImage || tooLargeForClient) { 2997 updates = { 2998 additionalData: { 2999 ...item.additionalData, 3000 generate_sub_sizes: true, 3001 convert_format: true 3002 } 3003 }; 3004 } else { 3005 updates = { 3006 additionalData: { 3007 ...item.additionalData, 3008 generate_sub_sizes: false 3009 } 3010 }; 3011 } 3012 dispatch.finishOperation(id, updates); 3013 }; 3014 } 3015 function detectUltraHdr(id) { 3016 return async ({ select: select2, dispatch }) => { 3017 const item = select2.getItem(id); 3018 if (!item) { 3019 return; 3020 } 3021 let info; 3022 try { 3023 const buffer = await item.file.arrayBuffer(); 3024 info = await vipsGetUltraHdrInfo(buffer); 3025 } catch { 3026 } 3027 if (info) { 3028 ultraHdrItems.add(id); 3029 } 3030 dispatch.finishOperation(id, {}); 3031 }; 3032 } 3033 function uploadItem(id) { 3034 return async ({ select: select2, dispatch }) => { 3035 const item = select2.getItem(id); 3036 if (!item) { 3037 return; 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 }; 3057 select2.getSettings().mediaUpload({ 3058 filesList: [item.file], 3059 additionalData: item.additionalData, 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, 3066 onFileChange: ([attachment]) => { 3067 if (attachment && !(0, import_blob.isBlobURL)(attachment.url)) { 3068 finishUpload(attachment); 3069 } 3070 }, 3071 onSuccess: ([attachment]) => { 3072 finishUpload(attachment); 3073 }, 3074 onError: (error) => { 3075 dispatch.cancelItem(id, error); 3076 } 3077 }); 3078 }; 3079 } 3080 function sideloadItem(id) { 3081 return async ({ select: select2, dispatch }) => { 3082 const item = select2.getItem(id); 3083 if (!item) { 3084 return; 3085 } 3086 const { post, ...additionalData } = item.additionalData; 3087 const mediaSideload = select2.getSettings().mediaSideload; 3088 if (!mediaSideload) { 3089 dispatch.finishOperation(id, {}); 3090 return; 3091 } 3092 const startTime = performance.now(); 3093 mediaSideload({ 3094 file: item.file, 3095 attachmentId: post, 3096 additionalData, 3097 signal: item.abortController?.signal, 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 }); 3108 if (item.parentId) { 3109 dispatch({ 3110 type: Type.AccumulateSubSize, 3111 id: item.parentId, 3112 subSize 3113 }); 3114 } 3115 dispatch.finishOperation(id, {}); 3116 }, 3117 onError: (error) => { 3118 dispatch.cancelItem(id, error); 3119 } 3120 }); 3121 }; 3122 } 3123 function resizeCropItem(id, args) { 3124 return async ({ select: select2, dispatch }) => { 3125 const item = select2.getItem(id); 3126 if (!item) { 3127 return; 3128 } 3129 if (!args?.resize) { 3130 dispatch.finishOperation(id, { 3131 file: item.file 3132 }); 3133 return; 3134 } 3135 const startTime = performance.now(); 3136 const addSuffix = Boolean(item.parentId); 3137 const scaledSuffix = Boolean(args.isThresholdResize); 3138 const { imageStripMeta, imageMaxBitDepth } = select2.getSettings(); 3139 try { 3140 const file = await vipsResizeImage( 3141 item.id, 3142 item.file, 3143 args.resize, 3144 { 3145 smartCrop: false, 3146 addSuffix, 3147 signal: item.abortController?.signal, 3148 scaledSuffix, 3149 quality: args.quality, 3150 stripMeta: imageStripMeta, 3151 maxBitdepth: imageMaxBitDepth 3152 } 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 }); 3163 const blobUrl = (0, import_blob.createBlobURL)(file); 3164 dispatch({ 3165 type: Type.CacheBlobUrl, 3166 id, 3167 blobUrl 3168 }); 3169 dispatch.finishOperation(id, { 3170 file, 3171 attachment: { 3172 url: blobUrl 3173 } 3174 }); 3175 } catch (error) { 3176 dispatch.cancelItem( 3177 id, 3178 new UploadError({ 3179 code: ErrorCode.IMAGE_TRANSCODING_ERROR, 3180 message: (0, import_i18n7.__)( 3181 "The web server cannot generate responsive image sizes for this image. Convert it to JPEG or PNG before uploading." 3182 ), 3183 file: item.file, 3184 cause: error instanceof Error ? error : void 0 3185 }) 3186 ); 3187 } 3188 }; 3189 } 3190 function rotateItem(id, args) { 3191 return async ({ select: select2, dispatch }) => { 3192 const item = select2.getItem(id); 3193 if (!item) { 3194 return; 3195 } 3196 if (!args?.orientation || args.orientation === 1) { 3197 dispatch.finishOperation(id, { 3198 file: item.file 3199 }); 3200 return; 3201 } 3202 const startTime = performance.now(); 3203 try { 3204 const file = await vipsRotateImage( 3205 item.id, 3206 item.file, 3207 args.orientation, 3208 item.abortController?.signal 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 }); 3219 const blobUrl = (0, import_blob.createBlobURL)(file); 3220 dispatch({ 3221 type: Type.CacheBlobUrl, 3222 id, 3223 blobUrl 3224 }); 3225 dispatch.finishOperation(id, { 3226 file, 3227 attachment: { 3228 url: blobUrl 3229 } 3230 }); 3231 } catch (error) { 3232 dispatch.cancelItem( 3233 id, 3234 new UploadError({ 3235 code: ErrorCode.IMAGE_ROTATION_ERROR, 3236 message: (0, import_i18n7.__)( 3237 "The web server cannot generate responsive image sizes for this image. Convert it to JPEG or PNG before uploading." 3238 ), 3239 file: item.file, 3240 cause: error instanceof Error ? error : void 0 3241 }) 3242 ); 3243 } 3244 }; 3245 } 3246 function transcodeImageItem(id, args) { 3247 return async ({ select: select2, dispatch }) => { 3248 const item = select2.getItem(id); 3249 if (!item) { 3250 return; 3251 } 3252 if (!args?.outputFormat) { 3253 dispatch.finishOperation(id, { 3254 file: item.file 3255 }); 3256 return; 3257 } 3258 const startTime = performance.now(); 3259 const outputMimeType = `image/$args.outputFormat}`; 3260 const quality = args.outputQuality ?? DEFAULT_OUTPUT_QUALITY; 3261 const interlaced = args.interlaced ?? false; 3262 const { imageStripMeta, imageMaxBitDepth } = select2.getSettings(); 3263 try { 3264 const file = await vipsConvertImageFormat( 3265 item.id, 3266 item.file, 3267 outputMimeType, 3268 { 3269 quality, 3270 interlaced, 3271 stripMeta: imageStripMeta, 3272 maxBitdepth: imageMaxBitDepth 3273 } 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 }); 3284 const blobUrl = (0, import_blob.createBlobURL)(file); 3285 dispatch({ 3286 type: Type.CacheBlobUrl, 3287 id, 3288 blobUrl 3289 }); 3290 dispatch.finishOperation(id, { 3291 file, 3292 attachment: { 3293 url: blobUrl 3294 } 3295 }); 3296 } catch (error) { 3297 dispatch.cancelItem( 3298 id, 3299 new UploadError({ 3300 code: ErrorCode.MEDIA_TRANSCODING_ERROR, 3301 message: "Image could not be transcoded to the target format", 3302 file: item.file, 3303 cause: error instanceof Error ? error : void 0 3304 }) 3305 ); 3306 } 3307 }; 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 } 3392 function generateThumbnails(id) { 3393 return async ({ select: select2, dispatch }) => { 3394 const item = select2.getItem(id); 3395 if (!item) { 3396 return; 3397 } 3398 if (!item.attachment) { 3399 dispatch.finishOperation(id, {}); 3400 return; 3401 } 3402 const attachment = item.attachment; 3403 const settings = select2.getSettings(); 3404 if (item.originalHeicFile && attachment.id) { 3405 dispatch.addSideloadItem({ 3406 file: item.originalHeicFile, 3407 batchId: v4_default(), 3408 parentId: item.id, 3409 additionalData: { 3410 post: attachment.id, 3411 image_size: "source_original", 3412 convert_format: false 3413 }, 3414 operations: [OperationType.Upload] 3415 }); 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; 3450 { 3451 const needsRotation = exifOrientation !== 1 && !item.file.name.includes("-scaled"); 3452 if ((needsRotation || needsClientRotation) && attachment.id) { 3453 try { 3454 rotatedSource = await vipsRotateImage( 3455 item.id, 3456 item.sourceFile, 3457 exifOrientation, 3458 item.abortController?.signal 3459 ); 3460 } catch { 3461 console.warn( 3462 "Failed to rotate image, continuing with thumbnails" 3463 ); 3464 } 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 } 3479 } 3480 if (!item.parentId && attachment.missing_image_sizes && attachment.missing_image_sizes.length > 0) { 3481 const allImageSizes = settings.allImageSizes || {}; 3482 const sizesToGenerate = attachment.missing_image_sizes; 3483 const thumbnailSource = needsClientRotation && rotatedSource ? rotatedSource : item.sourceFile; 3484 const file = attachment.filename ? renameFile(thumbnailSource, attachment.filename) : thumbnailSource; 3485 const batchId = v4_default(); 3486 const isUltraHdr = ultraHdrItems.has(item.id); 3487 const outputMimeType = attachment.image_output_format; 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 }; 3499 let thumbnailTranscodeOperation = null; 3500 if (!isUltraHdr && outputMimeType) { 3501 thumbnailTranscodeOperation = await getTranscodeImageOperation( 3502 thumbnailSource, 3503 outputMimeType, 3504 interlaced, 3505 defaultQuality 3506 ); 3507 } 3508 const dimensionGroups = /* @__PURE__ */ new Map(); 3509 for (const name of sizesToGenerate) { 3510 const imageSize = allImageSizes[name]; 3511 if (!imageSize) { 3512 console.warn( 3513 `Image size "$name}" not found in configuration` 3514 ); 3515 continue; 3516 } 3517 const key = `$imageSize.width}x$imageSize.height}x$imageSize.crop}`; 3518 const group = dimensionGroups.get(key); 3519 if (group) { 3520 group.push(name); 3521 } else { 3522 dimensionGroups.set(key, [name]); 3523 } 3524 } 3525 for (const [, names] of dimensionGroups) { 3526 const imageSize = allImageSizes[names[0]]; 3527 const sizeQuality = qualityForSize(names[0]); 3528 const thumbnailOperations = [ 3529 [ 3530 OperationType.ResizeCrop, 3531 { resize: imageSize, quality: sizeQuality } 3532 ] 3533 ]; 3534 if (!isUltraHdr && thumbnailTranscodeOperation) { 3535 thumbnailOperations.push([ 3536 thumbnailTranscodeOperation[0], 3537 { 3538 ...thumbnailTranscodeOperation[1], 3539 outputQuality: sizeQuality 3540 } 3541 ]); 3542 } 3543 thumbnailOperations.push(OperationType.Upload); 3544 const imageSizeParam = names.length === 1 ? names[0] : names; 3545 dispatch.addSideloadItem({ 3546 file, 3547 batchId, 3548 parentId: item.id, 3549 additionalData: { 3550 // Sideloading does not use the parent post ID but the 3551 // attachment ID as the image sizes need to be added to it. 3552 post: attachment.id, 3553 image_size: imageSizeParam, 3554 convert_format: false 3555 }, 3556 operations: thumbnailOperations 3557 }); 3558 } 3559 { 3560 const { bigImageSizeThreshold } = settings; 3561 if (bigImageSizeThreshold && attachment.id) { 3562 const bitmap = await createImageBitmap(thumbnailSource); 3563 const needsScaling = bitmap.width > bigImageSizeThreshold || bitmap.height > bigImageSizeThreshold; 3564 bitmap.close(); 3565 if (needsScaling) { 3566 const sourceForScaled = attachment.filename ? renameFile(thumbnailSource, attachment.filename) : thumbnailSource; 3567 const scaledOperations = [ 3568 [ 3569 OperationType.ResizeCrop, 3570 { 3571 resize: { 3572 width: bigImageSizeThreshold, 3573 height: bigImageSizeThreshold 3574 }, 3575 isThresholdResize: true, 3576 quality: defaultQuality 3577 } 3578 ] 3579 ]; 3580 if (!isUltraHdr && thumbnailTranscodeOperation) { 3581 scaledOperations.push( 3582 thumbnailTranscodeOperation 3583 ); 3584 } 3585 scaledOperations.push(OperationType.Upload); 3586 dispatch.addSideloadItem({ 3587 file: sourceForScaled, 3588 batchId, 3589 parentId: item.id, 3590 additionalData: { 3591 post: attachment.id, 3592 image_size: "scaled", 3593 convert_format: false 3594 }, 3595 operations: scaledOperations 3596 }); 3597 } 3598 } 3599 } 3600 } 3601 dispatch.finishOperation(id, {}); 3602 }; 3603 } 3604 function finalizeItem(id) { 3605 return async ({ select: select2, dispatch }) => { 3606 const item = select2.getItem(id); 3607 if (!item) { 3608 return; 3609 } 3610 const attachment = item.attachment; 3611 const { mediaFinalize } = select2.getSettings(); 3612 const updates = {}; 3613 if (attachment?.id && mediaFinalize) { 3614 try { 3615 const updatedAttachment = await mediaFinalize( 3616 attachment.id, 3617 item.subSizes || [] 3618 ); 3619 if (updatedAttachment) { 3620 updates.attachment = updatedAttachment; 3621 } 3622 } catch (error) { 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; 3633 } 3634 } 3635 dispatch.finishOperation(id, updates); 3636 }; 3637 } 3638 function revokeBlobUrls(id) { 3639 return async ({ select: select2, dispatch }) => { 3640 const blobUrls = select2.getBlobUrls(id); 3641 for (const blobUrl of blobUrls) { 3642 (0, import_blob.revokeBlobURL)(blobUrl); 3643 } 3644 dispatch({ 3645 type: Type.RevokeBlobUrls, 3646 id 3647 }); 3648 }; 3649 } 3650 function updateItemProgress(id, progress) { 3651 return async ({ dispatch }) => { 3652 dispatch({ 3653 type: Type.UpdateProgress, 3654 id, 3655 progress 3656 }); 3657 }; 3658 } 3659 function updateSettings(settings) { 3660 return { 3661 type: Type.UpdateSettings, 3662 settings 3663 }; 3664 } 3665 3666 // packages/upload-media/build-module/lock-unlock.mjs 3667 var import_private_apis = __toESM(require_private_apis(), 1); 3668 var { lock, unlock } = (0, import_private_apis.__dangerousOptInToUnstableAPIsOnlyForCoreModules)( 3669 "I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.", 3670 "@wordpress/upload-media" 3671 ); 3672 3673 // packages/upload-media/build-module/store/index.mjs 3674 var storeConfig = { 3675 reducer: reducer_default, 3676 selectors: selectors_exports, 3677 actions: actions_exports 3678 }; 3679 var store = (0, import_data.createReduxStore)(STORE_NAME, { 3680 reducer: reducer_default, 3681 selectors: selectors_exports, 3682 actions: actions_exports 3683 }); 3684 if (!(0, import_data.select)(store)) { 3685 (0, import_data.register)(store); 3686 } 3687 unlock(store).registerPrivateActions(private_actions_exports); 3688 unlock(store).registerPrivateSelectors(private_selectors_exports); 3689 3690 // packages/upload-media/build-module/components/provider/index.mjs 3691 var import_element2 = __toESM(require_element(), 1); 3692 var import_data3 = __toESM(require_data(), 1); 3693 3694 // packages/upload-media/build-module/components/provider/with-registry-provider.mjs 3695 var import_element = __toESM(require_element(), 1); 3696 var import_data2 = __toESM(require_data(), 1); 3697 var import_compose = __toESM(require_compose(), 1); 3698 var import_jsx_runtime = __toESM(require_jsx_runtime(), 1); 3699 function getSubRegistry(subRegistries, registry, useSubRegistry) { 3700 if (!useSubRegistry) { 3701 return registry; 3702 } 3703 let subRegistry = subRegistries.get(registry); 3704 if (!subRegistry) { 3705 subRegistry = (0, import_data2.createRegistry)({}, registry); 3706 subRegistry.registerStore(STORE_NAME, storeConfig); 3707 subRegistries.set(registry, subRegistry); 3708 } 3709 return subRegistry; 3710 } 3711 var withRegistryProvider = (0, import_compose.createHigherOrderComponent)( 3712 (WrappedComponent) => ({ useSubRegistry = true, ...props }) => { 3713 const registry = (0, import_data2.useRegistry)(); 3714 const [subRegistries] = (0, import_element.useState)(() => /* @__PURE__ */ new WeakMap()); 3715 const subRegistry = getSubRegistry( 3716 subRegistries, 3717 registry, 3718 useSubRegistry 3719 ); 3720 if (subRegistry === registry) { 3721 return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WrappedComponent, { registry, ...props }); 3722 } 3723 return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_data2.RegistryProvider, { value: subRegistry, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WrappedComponent, { registry: subRegistry, ...props }) }); 3724 }, 3725 "withRegistryProvider" 3726 ); 3727 var with_registry_provider_default = withRegistryProvider; 3728 3729 // packages/upload-media/build-module/components/provider/index.mjs 3730 var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1); 3731 var MediaUploadProvider = with_registry_provider_default((props) => { 3732 const { children, settings } = props; 3733 const { updateSettings: updateSettings2 } = unlock((0, import_data3.useDispatch)(store)); 3734 (0, import_element2.useEffect)(() => { 3735 updateSettings2(settings); 3736 }, [settings, updateSettings2]); 3737 return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children }); 3738 }); 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 } 3846 return __toCommonJS(index_exports); 3847 })(); 3848 (window.wp ||= {}).uploadMedia = wp.uploadMedia; 3849 })();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Mon Oct 12 08:20:33 2026 | Cross-referenced by PHPXref |