@remotion/studio-shared 4.0.471 → 4.0.473

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
1
  import type { AudioCodec, ChromeMode, Codec, ColorSpace, LogLevel, PixelFormat, StillImageFormat, VideoImageFormat, X264Preset } from '@remotion/renderer';
2
2
  import type { HardwareAccelerationOption } from '@remotion/renderer/client';
3
- import type { _InternalTypes, CannotUpdateSequenceReason, CanUpdateEffectPropsResponse, CanUpdateSequencePropsResponseFalse, CanUpdateSequencePropsResponseTrue, CanUpdateSequencePropStatus, SequenceNodePath, SequencePropsSubscriptionKey, SequenceSchema } from 'remotion';
3
+ import type { _InternalTypes, CannotUpdateSequenceReason, CanUpdateEffectPropsResponse, CanUpdateSequencePropsResponseFalse, CanUpdateSequencePropsResponseTrue, CanUpdateSequencePropStatus, ExtrapolateType, SequenceNodePath, SequencePropsSubscriptionKey, SequenceSchema } from 'remotion';
4
4
  import type { RecastCodemod, VisualControlChange } from './codemods';
5
+ import type { EffectClipboardParam, EffectClipboardPasteType, EffectClipboardSnapshot } from './effect-clipboard-data';
5
6
  import type { PackageManager } from './package-manager';
6
7
  import type { ProjectInfo } from './project-info';
7
8
  import type { CompletedClientRender, RequiredChromiumOptions } from './render-job';
@@ -163,6 +164,13 @@ export type DeleteStaticFileResponse = {
163
164
  success: boolean;
164
165
  existed: boolean;
165
166
  };
167
+ export type RenameStaticFileRequest = {
168
+ oldRelativePath: string;
169
+ newRelativePath: string;
170
+ };
171
+ export type RenameStaticFileResponse = {
172
+ success: boolean;
173
+ };
166
174
  export type CanUpdateDefaultPropsResponse = {
167
175
  canUpdate: true;
168
176
  currentDefaultProps: Record<string, unknown>;
@@ -219,8 +227,8 @@ export type SaveSequencePropEdit = {
219
227
  export type SaveSequencePropsRequest = {
220
228
  edits: SaveSequencePropEdit[];
221
229
  clientId: string;
222
- undoLabel: string | null;
223
- redoLabel: string | null;
230
+ undoLabel: string;
231
+ redoLabel: string;
224
232
  };
225
233
  export type SaveSequencePropsResult = {
226
234
  fileName: string;
@@ -235,16 +243,22 @@ export type SaveSequencePropsResponse = {
235
243
  canUpdate: false;
236
244
  reason: CannotUpdateSequenceReason;
237
245
  };
238
- export type SaveEffectPropsRequest = {
246
+ type SaveEffectPropsRequestBase = {
239
247
  fileName: string;
240
248
  sequenceNodePath: SequencePropsSubscriptionKey;
241
249
  effectIndex: number;
242
250
  key: string;
243
- value: string;
244
251
  defaultValue: string | null;
245
252
  schema: SequenceSchema;
246
253
  clientId: string;
247
254
  };
255
+ export type SaveEffectPropsRequest = (SaveEffectPropsRequestBase & {
256
+ type: 'value';
257
+ value: string;
258
+ }) | (SaveEffectPropsRequestBase & {
259
+ type: 'effect-param';
260
+ effectParam: EffectClipboardParam;
261
+ });
248
262
  export type SaveEffectPropsResponse = CanUpdateEffectPropsResponse;
249
263
  export type AddEffectRequest = {
250
264
  fileName: string;
@@ -275,6 +289,21 @@ export type ReorderEffectResponse = {
275
289
  reason: string;
276
290
  stack: string;
277
291
  };
292
+ export type ReorderSequencePosition = 'before' | 'after';
293
+ export type ReorderSequenceRequest = {
294
+ fileName: string;
295
+ sourceNodePath: SequencePropsSubscriptionKey;
296
+ targetNodePath: SequencePropsSubscriptionKey;
297
+ position: ReorderSequencePosition;
298
+ clientId: string;
299
+ };
300
+ export type ReorderSequenceResponse = {
301
+ success: true;
302
+ } | {
303
+ success: false;
304
+ reason: string;
305
+ stack: string;
306
+ };
278
307
  export type DeleteSequenceKeyframe = {
279
308
  fileName: string;
280
309
  nodePath: SequencePropsSubscriptionKey;
@@ -282,6 +311,14 @@ export type DeleteSequenceKeyframe = {
282
311
  frame: number;
283
312
  schema: SequenceSchema;
284
313
  };
314
+ export type MoveSequenceKeyframe = {
315
+ fileName: string;
316
+ nodePath: SequencePropsSubscriptionKey;
317
+ key: string;
318
+ fromFrame: number;
319
+ toFrame: number;
320
+ schema: SequenceSchema;
321
+ };
285
322
  export type AddSequenceKeyframeRequest = {
286
323
  fileName: string;
287
324
  nodePath: SequencePropsSubscriptionKey;
@@ -300,6 +337,15 @@ export type DeleteEffectKeyframe = {
300
337
  frame: number;
301
338
  schema: SequenceSchema;
302
339
  };
340
+ export type MoveEffectKeyframe = {
341
+ fileName: string;
342
+ sequenceNodePath: SequencePropsSubscriptionKey;
343
+ effectIndex: number;
344
+ key: string;
345
+ fromFrame: number;
346
+ toFrame: number;
347
+ schema: SequenceSchema;
348
+ };
303
349
  export type DeleteKeyframesRequest = {
304
350
  sequenceKeyframes: DeleteSequenceKeyframe[];
305
351
  effectKeyframes: DeleteEffectKeyframe[];
@@ -308,6 +354,14 @@ export type DeleteKeyframesRequest = {
308
354
  export type DeleteKeyframesResponse = {
309
355
  success: true;
310
356
  };
357
+ export type MoveKeyframesRequest = {
358
+ sequenceKeyframes: MoveSequenceKeyframe[];
359
+ effectKeyframes: MoveEffectKeyframe[];
360
+ clientId: string;
361
+ };
362
+ export type MoveKeyframesResponse = {
363
+ success: true;
364
+ };
311
365
  export type AddEffectKeyframeRequest = {
312
366
  fileName: string;
313
367
  sequenceNodePath: SequencePropsSubscriptionKey;
@@ -319,6 +373,32 @@ export type AddEffectKeyframeRequest = {
319
373
  clientId: string;
320
374
  };
321
375
  export type AddEffectKeyframeResponse = SaveEffectPropsResponse;
376
+ export type KeyframeSettings = {
377
+ clamping: {
378
+ left: ExtrapolateType;
379
+ right: ExtrapolateType;
380
+ } | undefined;
381
+ posterize: number | undefined;
382
+ };
383
+ export type UpdateSequenceKeyframeSettingsRequest = {
384
+ fileName: string;
385
+ nodePath: SequencePropsSubscriptionKey;
386
+ key: string;
387
+ settings: KeyframeSettings;
388
+ schema: SequenceSchema;
389
+ clientId: string;
390
+ };
391
+ export type UpdateSequenceKeyframeSettingsResponse = SaveSequencePropsResponse;
392
+ export type UpdateEffectKeyframeSettingsRequest = {
393
+ fileName: string;
394
+ sequenceNodePath: SequencePropsSubscriptionKey;
395
+ effectIndex: number;
396
+ key: string;
397
+ settings: KeyframeSettings;
398
+ schema: SequenceSchema;
399
+ clientId: string;
400
+ };
401
+ export type UpdateEffectKeyframeSettingsResponse = SaveEffectPropsResponse;
322
402
  type BaseDeleteEffectRequestItem = {
323
403
  fileName: string;
324
404
  sequenceNodePath: SequencePropsSubscriptionKey;
@@ -337,6 +417,20 @@ export type DeleteEffectResponse = {
337
417
  reason: string;
338
418
  stack: string;
339
419
  };
420
+ export type PasteEffectsRequest = {
421
+ targetFileName: string;
422
+ targetSequenceNodePath: SequencePropsSubscriptionKey;
423
+ type: EffectClipboardPasteType;
424
+ effects: EffectClipboardSnapshot[];
425
+ clientId: string;
426
+ };
427
+ export type PasteEffectsResponse = {
428
+ success: true;
429
+ } | {
430
+ success: false;
431
+ reason: string;
432
+ stack: string;
433
+ };
340
434
  export type DeleteJsxNodeRequestItem = {
341
435
  fileName: string;
342
436
  nodePath: SequenceNodePath;
@@ -366,6 +460,14 @@ export type InsertableCompositionElement = {
366
460
  type: 'solid';
367
461
  width: number;
368
462
  height: number;
463
+ } | {
464
+ type: 'asset';
465
+ assetType: 'image' | 'video' | 'gif' | 'audio';
466
+ src: string;
467
+ dimensions: {
468
+ width: number;
469
+ height: number;
470
+ } | null;
369
471
  };
370
472
  export type InsertJsxElementRequest = {
371
473
  compositionFile: string;
@@ -379,6 +481,15 @@ export type InsertJsxElementResponse = {
379
481
  reason: string;
380
482
  stack: string;
381
483
  };
484
+ export type DownloadRemoteAssetRequest = {
485
+ url: string;
486
+ };
487
+ export type DownloadRemoteAssetResponse = {
488
+ assetPath: string;
489
+ sizeInBytes: number;
490
+ created: boolean;
491
+ element: InsertableCompositionElement;
492
+ };
382
493
  export type UpdateAvailableRequest = {};
383
494
  export type UpdateAvailableResponse = {
384
495
  currentVersion: string;
@@ -411,6 +522,13 @@ export type RedoResponse = {
411
522
  success: false;
412
523
  reason: string;
413
524
  };
525
+ export type LogStudioErrorRequest = {
526
+ name: string | null;
527
+ message: string;
528
+ stack: string | null;
529
+ symbolicatedStackFrames: SymbolicatedStackFrame[] | null;
530
+ };
531
+ export type LogStudioErrorResponse = {};
414
532
  export type ApiRoutes = {
415
533
  '/api/composition-component-info': ReqAndRes<CompositionComponentInfoRequest, CompositionComponentInfoResponse>;
416
534
  '/api/cancel': ReqAndRes<CancelRenderRequest, CancelRenderResponse>;
@@ -434,20 +552,28 @@ export type ApiRoutes = {
434
552
  '/api/save-effect-props': ReqAndRes<SaveEffectPropsRequest, SaveEffectPropsResponse>;
435
553
  '/api/add-effect': ReqAndRes<AddEffectRequest, AddEffectResponse>;
436
554
  '/api/reorder-effect': ReqAndRes<ReorderEffectRequest, ReorderEffectResponse>;
555
+ '/api/reorder-sequence': ReqAndRes<ReorderSequenceRequest, ReorderSequenceResponse>;
437
556
  '/api/delete-keyframes': ReqAndRes<DeleteKeyframesRequest, DeleteKeyframesResponse>;
557
+ '/api/move-keyframes': ReqAndRes<MoveKeyframesRequest, MoveKeyframesResponse>;
438
558
  '/api/add-sequence-keyframe': ReqAndRes<AddSequenceKeyframeRequest, AddSequenceKeyframeResponse>;
439
559
  '/api/add-effect-keyframe': ReqAndRes<AddEffectKeyframeRequest, AddEffectKeyframeResponse>;
560
+ '/api/update-sequence-keyframe-settings': ReqAndRes<UpdateSequenceKeyframeSettingsRequest, UpdateSequenceKeyframeSettingsResponse>;
561
+ '/api/update-effect-keyframe-settings': ReqAndRes<UpdateEffectKeyframeSettingsRequest, UpdateEffectKeyframeSettingsResponse>;
440
562
  '/api/delete-effect': ReqAndRes<DeleteEffectRequest, DeleteEffectResponse>;
563
+ '/api/paste-effects': ReqAndRes<PasteEffectsRequest, PasteEffectsResponse>;
441
564
  '/api/delete-jsx-node': ReqAndRes<DeleteJsxNodeRequest, DeleteJsxNodeResponse>;
442
565
  '/api/duplicate-jsx-node': ReqAndRes<DuplicateJsxNodeRequest, DuplicateJsxNodeResponse>;
443
566
  '/api/insert-jsx-element': ReqAndRes<InsertJsxElementRequest, InsertJsxElementResponse>;
567
+ '/api/download-remote-asset': ReqAndRes<DownloadRemoteAssetRequest, DownloadRemoteAssetResponse>;
444
568
  '/api/update-available': ReqAndRes<UpdateAvailableRequest, UpdateAvailableResponse>;
445
569
  '/api/apply-codemod': ReqAndRes<ApplyCodemodRequest, ApplyCodemodResponse>;
446
570
  '/api/project-info': ReqAndRes<ProjectInfoRequest, ProjectInfoResponse>;
447
571
  '/api/delete-static-file': ReqAndRes<DeleteStaticFileRequest, DeleteStaticFileResponse>;
572
+ '/api/rename-static-file': ReqAndRes<RenameStaticFileRequest, RenameStaticFileResponse>;
448
573
  '/api/restart-studio': ReqAndRes<RestartStudioRequest, RestartStudioResponse>;
449
574
  '/api/install-package': ReqAndRes<InstallPackageRequest, InstallPackageResponse>;
450
575
  '/api/undo': ReqAndRes<UndoRequest, UndoResponse>;
451
576
  '/api/redo': ReqAndRes<RedoRequest, RedoResponse>;
577
+ '/api/log-studio-error': ReqAndRes<LogStudioErrorRequest, LogStudioErrorResponse>;
452
578
  };
453
579
  export {};
@@ -0,0 +1,8 @@
1
+ export declare const ASSET_DRAG_MIME_TYPE = "application/vnd.remotion.asset+json";
2
+ export type AssetDragData = {
3
+ type: 'remotion-asset';
4
+ version: 1;
5
+ assetPath: string;
6
+ };
7
+ export declare const makeAssetDragData: (assetPath: string) => AssetDragData;
8
+ export declare const parseAssetDragData: (value: string) => AssetDragData | null;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseAssetDragData = exports.makeAssetDragData = exports.ASSET_DRAG_MIME_TYPE = void 0;
4
+ exports.ASSET_DRAG_MIME_TYPE = 'application/vnd.remotion.asset+json';
5
+ const isRecord = (value) => {
6
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
7
+ };
8
+ const makeAssetDragData = (assetPath) => {
9
+ return {
10
+ type: 'remotion-asset',
11
+ version: 1,
12
+ assetPath,
13
+ };
14
+ };
15
+ exports.makeAssetDragData = makeAssetDragData;
16
+ const parseAssetDragData = (value) => {
17
+ try {
18
+ const parsed = JSON.parse(value);
19
+ if (!isRecord(parsed)) {
20
+ return null;
21
+ }
22
+ if (parsed.type !== 'remotion-asset' || parsed.version !== 1) {
23
+ return null;
24
+ }
25
+ if (typeof parsed.assetPath !== 'string' || parsed.assetPath.length === 0) {
26
+ return null;
27
+ }
28
+ return {
29
+ type: 'remotion-asset',
30
+ version: 1,
31
+ assetPath: parsed.assetPath,
32
+ };
33
+ }
34
+ catch (_a) {
35
+ return null;
36
+ }
37
+ };
38
+ exports.parseAssetDragData = parseAssetDragData;
@@ -25,4 +25,13 @@ export type RecastCodemod = {
25
25
  } | {
26
26
  type: 'delete-composition';
27
27
  idToDelete: string;
28
+ } | {
29
+ type: 'rename-folder';
30
+ folderName: string;
31
+ parentName: string | null;
32
+ newName: string;
33
+ } | {
34
+ type: 'delete-folder';
35
+ folderName: string;
36
+ parentName: string | null;
28
37
  } | ApplyVisualControlCodemod;
@@ -0,0 +1,71 @@
1
+ export type FileDimensions = {
2
+ readonly width: number;
3
+ readonly height: number;
4
+ };
5
+ export declare const matchesPattern: (pattern: Uint8Array<ArrayBufferLike>) => (data: Uint8Array<ArrayBufferLike>) => boolean;
6
+ export declare const isRiffAvi: (data: Uint8Array<ArrayBufferLike>) => boolean;
7
+ export declare const isRiffWave: (data: Uint8Array<ArrayBufferLike>) => boolean;
8
+ export declare const isWebm: (data: Uint8Array<ArrayBufferLike>) => boolean;
9
+ export declare const isIsoBaseMedia: (data: Uint8Array<ArrayBufferLike>) => boolean;
10
+ export declare const isTransportStream: (data: Uint8Array<ArrayBufferLike>) => boolean;
11
+ export declare const isMp3: (data: Uint8Array<ArrayBufferLike>) => boolean;
12
+ export declare const isAac: (data: Uint8Array<ArrayBufferLike>) => boolean;
13
+ export declare const isFlac: (data: Uint8Array<ArrayBufferLike>) => boolean;
14
+ export declare const isM3u: (data: Uint8Array<ArrayBufferLike>) => boolean;
15
+ export type RiffType = {
16
+ type: 'riff';
17
+ };
18
+ export type WebmType = {
19
+ type: 'webm';
20
+ };
21
+ export type IsoBaseMediaType = {
22
+ type: 'iso-base-media';
23
+ };
24
+ export type TransportStreamType = {
25
+ type: 'transport-stream';
26
+ };
27
+ export type Mp3Type = {
28
+ type: 'mp3';
29
+ };
30
+ export type AacType = {
31
+ type: 'aac';
32
+ };
33
+ export type WavType = {
34
+ type: 'wav';
35
+ };
36
+ export type GifType = {
37
+ type: 'gif';
38
+ dimensions: FileDimensions | null;
39
+ };
40
+ export type FlacType = {
41
+ type: 'flac';
42
+ };
43
+ export type M3uType = {
44
+ type: 'm3u';
45
+ };
46
+ export type PngType = {
47
+ type: 'png';
48
+ dimensions: FileDimensions | null;
49
+ };
50
+ export type JpegType = {
51
+ type: 'jpeg';
52
+ dimensions: FileDimensions | null;
53
+ };
54
+ export type WebpType = {
55
+ type: 'webp';
56
+ dimensions: FileDimensions | null;
57
+ };
58
+ export type BmpType = {
59
+ type: 'bmp';
60
+ dimensions: FileDimensions | null;
61
+ };
62
+ export type PdfType = {
63
+ type: 'pdf';
64
+ };
65
+ export type UnknownType = {
66
+ type: 'unknown';
67
+ };
68
+ export type FileType = JpegType | WebpType | RiffType | WebmType | WavType | PdfType | AacType | IsoBaseMediaType | TransportStreamType | Mp3Type | GifType | PngType | BmpType | AacType | FlacType | M3uType | UnknownType;
69
+ export type ImageFileType = JpegType | WebpType | GifType | PngType | BmpType;
70
+ export declare const isImageFileType: (fileType: FileType) => fileType is ImageFileType;
71
+ export declare const detectFileType: (data: Uint8Array<ArrayBufferLike>) => FileType;
@@ -0,0 +1,295 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectFileType = exports.isImageFileType = exports.isM3u = exports.isFlac = exports.isAac = exports.isMp3 = exports.isTransportStream = exports.isIsoBaseMedia = exports.isWebm = exports.isRiffWave = exports.isRiffAvi = exports.matchesPattern = void 0;
4
+ const webmPattern = new Uint8Array([0x1a, 0x45, 0xdf, 0xa3]);
5
+ const matchesPattern = (pattern) => {
6
+ return (data) => {
7
+ return pattern.every((value, index) => data[index] === value);
8
+ };
9
+ };
10
+ exports.matchesPattern = matchesPattern;
11
+ const isRiffAvi = (data) => {
12
+ const riffPattern = new Uint8Array([0x52, 0x49, 0x46, 0x46]);
13
+ if (!(0, exports.matchesPattern)(riffPattern)(data.subarray(0, 4))) {
14
+ return false;
15
+ }
16
+ const fileType = data.subarray(8, 12);
17
+ const aviPattern = new Uint8Array([0x41, 0x56, 0x49, 0x20]);
18
+ return (0, exports.matchesPattern)(aviPattern)(fileType);
19
+ };
20
+ exports.isRiffAvi = isRiffAvi;
21
+ const isRiffWave = (data) => {
22
+ const riffPattern = new Uint8Array([0x52, 0x49, 0x46, 0x46]);
23
+ if (!(0, exports.matchesPattern)(riffPattern)(data.subarray(0, 4))) {
24
+ return false;
25
+ }
26
+ const fileType = data.subarray(8, 12);
27
+ const wavePattern = new Uint8Array([0x57, 0x41, 0x56, 0x45]);
28
+ return (0, exports.matchesPattern)(wavePattern)(fileType);
29
+ };
30
+ exports.isRiffWave = isRiffWave;
31
+ const isWebm = (data) => {
32
+ return (0, exports.matchesPattern)(webmPattern)(data.subarray(0, 4));
33
+ };
34
+ exports.isWebm = isWebm;
35
+ const isIsoBaseMedia = (data) => {
36
+ const isoBaseMediaMp4Pattern = new TextEncoder().encode('ftyp');
37
+ return (0, exports.matchesPattern)(isoBaseMediaMp4Pattern)(data.subarray(4, 8));
38
+ };
39
+ exports.isIsoBaseMedia = isIsoBaseMedia;
40
+ const isTransportStream = (data) => {
41
+ return data[0] === 0x47 && data[188] === 0x47;
42
+ };
43
+ exports.isTransportStream = isTransportStream;
44
+ const isMp3 = (data) => {
45
+ const mpegPattern = new Uint8Array([0xff, 0xf3]);
46
+ const mpegPattern2 = new Uint8Array([0xff, 0xfb]);
47
+ const id3v4Pattern = new Uint8Array([0x49, 0x44, 0x33, 4]);
48
+ const id3v3Pattern = new Uint8Array([0x49, 0x44, 0x33, 3]);
49
+ const id3v2Pattern = new Uint8Array([0x49, 0x44, 0x33, 2]);
50
+ const subarray = data.subarray(0, 4);
51
+ return ((0, exports.matchesPattern)(mpegPattern)(subarray) ||
52
+ (0, exports.matchesPattern)(mpegPattern2)(subarray) ||
53
+ (0, exports.matchesPattern)(id3v4Pattern)(subarray) ||
54
+ (0, exports.matchesPattern)(id3v3Pattern)(subarray) ||
55
+ (0, exports.matchesPattern)(id3v2Pattern)(subarray));
56
+ };
57
+ exports.isMp3 = isMp3;
58
+ const isAac = (data) => {
59
+ const aacPattern = new Uint8Array([0xff, 0xf1]);
60
+ return (0, exports.matchesPattern)(aacPattern)(data.subarray(0, 2));
61
+ };
62
+ exports.isAac = isAac;
63
+ const isFlac = (data) => {
64
+ const flacPattern = new Uint8Array([0x66, 0x4c, 0x61, 0x43]);
65
+ return (0, exports.matchesPattern)(flacPattern)(data.subarray(0, 4));
66
+ };
67
+ exports.isFlac = isFlac;
68
+ const isM3u = (data) => {
69
+ return new TextDecoder('utf-8').decode(data.slice(0, 7)) === '#EXTM3U';
70
+ };
71
+ exports.isM3u = isM3u;
72
+ const getPngDimensions = (pngData) => {
73
+ if (pngData.length < 24) {
74
+ return null;
75
+ }
76
+ const view = new DataView(pngData.buffer, pngData.byteOffset);
77
+ const pngSignature = [137, 80, 78, 71, 13, 10, 26, 10];
78
+ for (let i = 0; i < 8; i++) {
79
+ if (pngData[i] !== pngSignature[i]) {
80
+ return null;
81
+ }
82
+ }
83
+ return {
84
+ width: view.getUint32(16, false),
85
+ height: view.getUint32(20, false),
86
+ };
87
+ };
88
+ const isPng = (data) => {
89
+ const pngPattern = new Uint8Array([0x89, 0x50, 0x4e, 0x47]);
90
+ if ((0, exports.matchesPattern)(pngPattern)(data.subarray(0, 4))) {
91
+ const png = getPngDimensions(data);
92
+ return { dimensions: png, type: 'png' };
93
+ }
94
+ return null;
95
+ };
96
+ const getJpegDimensions = (data) => {
97
+ let offset = 0;
98
+ const readUint16BE = (o) => {
99
+ return (data[o] << 8) | data[o + 1];
100
+ };
101
+ if (data.length < 4 || readUint16BE(offset) !== 0xffd8) {
102
+ return null;
103
+ }
104
+ offset += 2;
105
+ while (offset + 3 < data.length) {
106
+ if (data[offset] === 0xff) {
107
+ const marker = data[offset + 1];
108
+ if (marker === 0xc0 || marker === 0xc2) {
109
+ if (offset + 8 >= data.length) {
110
+ return null;
111
+ }
112
+ const height = readUint16BE(offset + 5);
113
+ const width = readUint16BE(offset + 7);
114
+ return { width, height };
115
+ }
116
+ const length = readUint16BE(offset + 2);
117
+ if (length <= 0) {
118
+ return null;
119
+ }
120
+ offset += length + 2;
121
+ }
122
+ else {
123
+ offset++;
124
+ }
125
+ }
126
+ return null;
127
+ };
128
+ const isJpeg = (data) => {
129
+ const jpegPattern = new Uint8Array([0xff, 0xd8]);
130
+ const jpeg = (0, exports.matchesPattern)(jpegPattern)(data.subarray(0, 2));
131
+ if (!jpeg) {
132
+ return null;
133
+ }
134
+ const dim = getJpegDimensions(data);
135
+ return { dimensions: dim, type: 'jpeg' };
136
+ };
137
+ const getGifDimensions = (data) => {
138
+ if (data.length < 10) {
139
+ return null;
140
+ }
141
+ const view = new DataView(data.buffer, data.byteOffset);
142
+ const width = view.getUint16(6, true);
143
+ const height = view.getUint16(8, true);
144
+ return { width, height };
145
+ };
146
+ const isGif = (data) => {
147
+ const gifPattern = new Uint8Array([0x47, 0x49, 0x46, 0x38]);
148
+ if ((0, exports.matchesPattern)(gifPattern)(data.subarray(0, 4))) {
149
+ return { type: 'gif', dimensions: getGifDimensions(data) };
150
+ }
151
+ return null;
152
+ };
153
+ const getWebPDimensions = (bytes) => {
154
+ if (bytes.length < 30) {
155
+ return null;
156
+ }
157
+ if (bytes[0] !== 0x52 ||
158
+ bytes[1] !== 0x49 ||
159
+ bytes[2] !== 0x46 ||
160
+ bytes[3] !== 0x46 ||
161
+ bytes[8] !== 0x57 ||
162
+ bytes[9] !== 0x45 ||
163
+ bytes[10] !== 0x42 ||
164
+ bytes[11] !== 0x50) {
165
+ return null;
166
+ }
167
+ if (bytes[12] === 0x56 && bytes[13] === 0x50 && bytes[14] === 0x38) {
168
+ if (bytes[15] === 0x20) {
169
+ return {
170
+ width: bytes[26] | ((bytes[27] << 8) & 0x3fff),
171
+ height: bytes[28] | ((bytes[29] << 8) & 0x3fff),
172
+ };
173
+ }
174
+ }
175
+ if (bytes[12] === 0x56 &&
176
+ bytes[13] === 0x50 &&
177
+ bytes[14] === 0x38 &&
178
+ bytes[15] === 0x4c) {
179
+ return {
180
+ width: 1 + (bytes[21] | ((bytes[22] & 0x3f) << 8)),
181
+ height: 1 +
182
+ (((bytes[22] & 0xc0) >> 6) |
183
+ (bytes[23] << 2) |
184
+ ((bytes[24] & 0x0f) << 10)),
185
+ };
186
+ }
187
+ if (bytes[12] === 0x56 &&
188
+ bytes[13] === 0x50 &&
189
+ bytes[14] === 0x38 &&
190
+ bytes[15] === 0x58) {
191
+ return {
192
+ width: 1 + (bytes[24] | (bytes[25] << 8) | (bytes[26] << 16)),
193
+ height: 1 + (bytes[27] | (bytes[28] << 8) | (bytes[29] << 16)),
194
+ };
195
+ }
196
+ return null;
197
+ };
198
+ const isWebp = (data) => {
199
+ const webpPattern = new Uint8Array([0x52, 0x49, 0x46, 0x46]);
200
+ if ((0, exports.matchesPattern)(webpPattern)(data.subarray(0, 4))) {
201
+ return {
202
+ type: 'webp',
203
+ dimensions: getWebPDimensions(data),
204
+ };
205
+ }
206
+ return null;
207
+ };
208
+ const getBmpDimensions = (bmpData) => {
209
+ if (bmpData.length < 26) {
210
+ return null;
211
+ }
212
+ const view = new DataView(bmpData.buffer, bmpData.byteOffset);
213
+ return {
214
+ width: view.getUint32(18, true),
215
+ height: Math.abs(view.getInt32(22, true)),
216
+ };
217
+ };
218
+ const isBmp = (data) => {
219
+ const bmpPattern = new Uint8Array([0x42, 0x4d]);
220
+ if ((0, exports.matchesPattern)(bmpPattern)(data.subarray(0, 2))) {
221
+ const bmp = getBmpDimensions(data);
222
+ return { dimensions: bmp, type: 'bmp' };
223
+ }
224
+ return null;
225
+ };
226
+ const isPdf = (data) => {
227
+ if (data.length < 4) {
228
+ return null;
229
+ }
230
+ const pdfPattern = new Uint8Array([0x25, 0x50, 0x44, 0x46]);
231
+ return (0, exports.matchesPattern)(pdfPattern)(data.subarray(0, 4)) ? { type: 'pdf' } : null;
232
+ };
233
+ const isImageFileType = (fileType) => {
234
+ return (fileType.type === 'jpeg' ||
235
+ fileType.type === 'webp' ||
236
+ fileType.type === 'gif' ||
237
+ fileType.type === 'png' ||
238
+ fileType.type === 'bmp');
239
+ };
240
+ exports.isImageFileType = isImageFileType;
241
+ const detectFileType = (data) => {
242
+ if ((0, exports.isRiffWave)(data)) {
243
+ return { type: 'wav' };
244
+ }
245
+ if ((0, exports.isRiffAvi)(data)) {
246
+ return { type: 'riff' };
247
+ }
248
+ if ((0, exports.isAac)(data)) {
249
+ return { type: 'aac' };
250
+ }
251
+ if ((0, exports.isFlac)(data)) {
252
+ return { type: 'flac' };
253
+ }
254
+ if ((0, exports.isM3u)(data)) {
255
+ return { type: 'm3u' };
256
+ }
257
+ const webp = isWebp(data);
258
+ if (webp) {
259
+ return webp;
260
+ }
261
+ if ((0, exports.isWebm)(data)) {
262
+ return { type: 'webm' };
263
+ }
264
+ if ((0, exports.isIsoBaseMedia)(data)) {
265
+ return { type: 'iso-base-media' };
266
+ }
267
+ if ((0, exports.isTransportStream)(data)) {
268
+ return { type: 'transport-stream' };
269
+ }
270
+ if ((0, exports.isMp3)(data)) {
271
+ return { type: 'mp3' };
272
+ }
273
+ const gif = isGif(data);
274
+ if (gif) {
275
+ return gif;
276
+ }
277
+ const png = isPng(data);
278
+ if (png) {
279
+ return png;
280
+ }
281
+ const pdf = isPdf(data);
282
+ if (pdf) {
283
+ return pdf;
284
+ }
285
+ const bmp = isBmp(data);
286
+ if (bmp) {
287
+ return bmp;
288
+ }
289
+ const jpeg = isJpeg(data);
290
+ if (jpeg) {
291
+ return jpeg;
292
+ }
293
+ return { type: 'unknown' };
294
+ };
295
+ exports.detectFileType = detectFileType;