PluginProbe
Gutenberg / 24.1.0
Gutenberg v24.1.0
24.1.0 24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 All 404 releases
gutenberg / build / scripts / upload-media / index.js

index.js in Gutenberg 24.1.0, at build/scripts/upload-media/index.js

3,851 lines 119.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 })();
3850 //# sourceMappingURL=index.js.map
3851