@video-editor/protocol 0.0.1-beta.3 → 0.0.1-beta.31

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.
package/README.md CHANGED
@@ -1,3 +1,94 @@
1
- # @video-editor/shared
1
+ # @video-editor/protocol
2
2
 
3
- video editor libs common type
3
+ Video editor protocol management, validation, and resource utilities.
4
+
5
+ ## Features
6
+
7
+ - **Protocol Management**: Create and manage video editor protocols with undo/redo
8
+ - **Validation**: JSON Schema validation for all segment types
9
+ - **Resource Management**: OPFS-based caching for video/audio resources
10
+ - **Waveform Extraction**: Extract and cache audio waveforms for visualization
11
+ - **Thumbnail Generation**: Extract video thumbnails with caching
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pnpm add @video-editor/protocol
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ### Audio Waveform Extraction
22
+
23
+ ```typescript
24
+ import { extractWaveform, peaksToSvgPath } from '@video-editor/protocol'
25
+
26
+ // Extract waveform from audio URL
27
+ const waveform = await extractWaveform('/audio.mp3', { samples: 1000 })
28
+
29
+ // Generate SVG path for rendering
30
+ const svgPath = peaksToSvgPath(waveform.peaks, 800, 100)
31
+
32
+ console.log(waveform.peaks) // number[] - normalized 0-1
33
+ console.log(waveform.duration) // number - seconds
34
+ ```
35
+
36
+ ### Protocol Management
37
+
38
+ ```typescript
39
+ import { createVideoProtocolManager } from '@video-editor/protocol'
40
+ import type { IVideoProtocol } from '@video-editor/protocol'
41
+
42
+ const protocol: IVideoProtocol = { /* ... */ }
43
+ const manager = createVideoProtocolManager(protocol)
44
+
45
+ // Add segment
46
+ manager.addSegment({
47
+ id: 'audio-1',
48
+ segmentType: 'audio',
49
+ url: '/audio.mp3',
50
+ startTime: 0,
51
+ endTime: 3000,
52
+ })
53
+
54
+ // Undo/Redo
55
+ manager.undo()
56
+ manager.redo()
57
+ ```
58
+
59
+ ### Resource Caching
60
+
61
+ ```typescript
62
+ import { createResourceManager } from '@video-editor/protocol'
63
+
64
+ const resourceManager = createResourceManager()
65
+
66
+ // Add resource to OPFS cache
67
+ await resourceManager.add('/video-editor-res/audio.mp3', audioBlob)
68
+
69
+ // Read from cache
70
+ const cached = await resourceManager.read('/video-editor-res/audio.mp3')
71
+ ```
72
+
73
+ ## Documentation
74
+
75
+ See [WAVEFORM.md](../../WAVEFORM.md) for detailed waveform API documentation.
76
+
77
+ ## Exports
78
+
79
+ ### Waveform
80
+ - `extractWaveform(url, options)` - Extract waveform from URL
81
+ - `extractWaveformFromBuffer(buffer, cacheKey, options)` - Extract from ArrayBuffer
82
+ - `clearWaveformCache(url?)` - Clear waveform cache
83
+ - `peaksToSvgPath(peaks, width, height)` - Generate SVG path
84
+ - `peaksToBars(peaks, width)` - Generate bar data
85
+
86
+ ### Protocol
87
+ - `createVideoProtocolManager(protocol)` - Create protocol manager
88
+ - `createValidator()` - Create protocol validator
89
+ - `parse(protocolString)` - Parse and validate protocol JSON
90
+
91
+ ### Resources
92
+ - `createResourceManager(dir?)` - Create resource manager
93
+ - `generateThumbnails(url, options)` - Generate video thumbnails
94
+ - `getMp4Meta(url)` - Extract MP4 metadata
package/dist/index.d.ts CHANGED
@@ -6,8 +6,8 @@ import { IEffectSegment } from '@video-editor/shared';
6
6
  import { IFillMode } from '@video-editor/shared';
7
7
  import { IFilterSegment } from '@video-editor/shared';
8
8
  import { IFramesSegmentUnion } from '@video-editor/shared';
9
- import { IImageSegment } from '@video-editor/shared';
10
9
  import { ISamples } from './fetch';
10
+ import { IStickerSegment } from '@video-editor/shared';
11
11
  import { ITextSegment } from '@video-editor/shared';
12
12
  import { ITrackType } from '@video-editor/shared';
13
13
  import { ITransition } from '@video-editor/shared';
@@ -20,10 +20,17 @@ import { SegmentUnion } from '@video-editor/shared';
20
20
  import { TrackTypeMapSegment } from '@video-editor/shared';
21
21
  import { TrackUnion } from '@video-editor/shared';
22
22
 
23
+ /**
24
+ * Clear waveform cache for a specific URL or all
25
+ */
26
+ export declare function clearWaveformCache(url?: string): void;
27
+
23
28
  export declare function createResourceManager(opts?: {
24
29
  dir?: string;
25
30
  }): {
26
- add: (url: string) => Promise<void>;
31
+ add: (url: string, opts?: {
32
+ body?: ReadableStream<BufferSource>;
33
+ }) => Promise<void>;
27
34
  get: (url: string) => Promise<void | HTMLImageElement | ISamples[]>;
28
35
  remove: (url: string) => Promise<void>;
29
36
  clear: () => Promise<void>;
@@ -36,7 +43,7 @@ export declare function createValidator(): {
36
43
  };
37
44
  verifyFramesSegment: (o: object) => IFramesSegmentUnion;
38
45
  verifyTextSegment: (o: object) => ITextSegment;
39
- verifyPhotoSegment: (o: object) => IImageSegment;
46
+ verifyStickerSegment: (o: object) => IStickerSegment;
40
47
  verifyAudioSegment: (o: object) => IAudioSegment;
41
48
  verifyEffectSegment: (o: object) => IEffectSegment;
42
49
  verifyFilterSegment: (o: object) => IFilterSegment;
@@ -45,10 +52,15 @@ export declare function createValidator(): {
45
52
  };
46
53
  verifySegment: (o: object & {
47
54
  segmentType: ITrackType;
48
- }) => IFramesSegmentUnion | IAudioSegment | IEffectSegment | IFilterSegment | IImageSegment | ITextSegment;
55
+ }) => IFramesSegmentUnion | IAudioSegment | IEffectSegment | IFilterSegment | IStickerSegment | ITextSegment;
49
56
  };
50
57
 
51
- export declare function createVideoProtocolManager(protocol: IVideoProtocol): {
58
+ export declare function createVideoProtocolManager(protocol: IVideoProtocol, options?: {
59
+ idFactory?: {
60
+ segment?: () => string;
61
+ track?: () => string;
62
+ };
63
+ }): {
52
64
  videoBasicInfo: {
53
65
  version: `${number}.${number}.${number}`;
54
66
  width: number;
@@ -214,6 +226,9 @@ export declare function createVideoProtocolManager(protocol: IVideoProtocol): {
214
226
  readonly [x: string]: never;
215
227
  } | null | undefined;
216
228
  })[];
229
+ readonly extra?: {
230
+ readonly [x: string]: never;
231
+ } | null | undefined;
217
232
  readonly isMain?: boolean | undefined;
218
233
  }[];
219
234
  text: readonly {
@@ -270,12 +285,15 @@ export declare function createVideoProtocolManager(protocol: IVideoProtocol): {
270
285
  readonly [x: string]: never;
271
286
  } | null | undefined;
272
287
  }[];
288
+ readonly extra?: {
289
+ readonly [x: string]: never;
290
+ } | null | undefined;
273
291
  }[];
274
- image: readonly {
292
+ sticker: readonly {
275
293
  readonly trackId: string;
276
- readonly trackType: "image";
294
+ readonly trackType: "sticker";
277
295
  readonly children: readonly {
278
- readonly segmentType: "image";
296
+ readonly segmentType: "sticker";
279
297
  readonly format: "img" | "gif";
280
298
  readonly url: string;
281
299
  readonly fillMode?: IFillMode | undefined;
@@ -312,6 +330,9 @@ export declare function createVideoProtocolManager(protocol: IVideoProtocol): {
312
330
  readonly [x: string]: never;
313
331
  } | null | undefined;
314
332
  }[];
333
+ readonly extra?: {
334
+ readonly [x: string]: never;
335
+ } | null | undefined;
315
336
  }[];
316
337
  audio: readonly {
317
338
  readonly trackId: string;
@@ -331,6 +352,9 @@ export declare function createVideoProtocolManager(protocol: IVideoProtocol): {
331
352
  readonly [x: string]: never;
332
353
  } | null | undefined;
333
354
  }[];
355
+ readonly extra?: {
356
+ readonly [x: string]: never;
357
+ } | null | undefined;
334
358
  }[];
335
359
  effect: readonly {
336
360
  readonly trackId: string;
@@ -347,6 +371,9 @@ export declare function createVideoProtocolManager(protocol: IVideoProtocol): {
347
371
  readonly [x: string]: never;
348
372
  } | null | undefined;
349
373
  }[];
374
+ readonly extra?: {
375
+ readonly [x: string]: never;
376
+ } | null | undefined;
350
377
  }[];
351
378
  filter: readonly {
352
379
  readonly trackId: string;
@@ -364,19 +391,79 @@ export declare function createVideoProtocolManager(protocol: IVideoProtocol): {
364
391
  readonly [x: string]: never;
365
392
  } | null | undefined;
366
393
  }[];
394
+ readonly extra?: {
395
+ readonly [x: string]: never;
396
+ } | null | undefined;
367
397
  }[];
368
398
  }>;
369
399
  segmentMap: ComputedRef<Record<string, DeepReadonly<SegmentUnion | undefined>>>;
400
+ protocol: ComputedRef<IVideoProtocol>;
370
401
  getSegment: <T extends ITrackType>(id: SegmentUnion["id"], type?: T) => DeepReadonly<TrackTypeMapSegment[T]> | undefined;
371
- addSegment: (segment: PartialByKeys<TrackTypeMapSegment[ITrackType], "id">) => string;
372
- removeSegment: (id: SegmentUnion["id"]) => boolean;
402
+ addSegment: (segment: PartialByKeys<TrackTypeMapSegment[ITrackType], "id">, trackId?: string) => {
403
+ id: string;
404
+ affectedSegments: SegmentUnion[];
405
+ affectedTracks: TrackUnion[];
406
+ createdTracks: TrackUnion[];
407
+ removedTrackIds: string[];
408
+ };
409
+ removeSegment: (id: SegmentUnion["id"]) => {
410
+ success: boolean;
411
+ affectedSegments: SegmentUnion[];
412
+ affectedTracks: TrackUnion[];
413
+ createdTracks: TrackUnion[];
414
+ removedTrackIds: string[];
415
+ };
373
416
  updateSegment: <T extends ITrackType>(updater: (segment: TrackTypeMapSegment[T]) => void, id?: string, type?: T) => void;
417
+ moveSegment: (moveOptions: {
418
+ segmentId: string;
419
+ sourceTrackId: string;
420
+ targetTrackId?: string;
421
+ startTime: number;
422
+ endTime: number;
423
+ isNewTrack?: boolean;
424
+ newTrackInsertIndex?: number;
425
+ newTrackId?: string;
426
+ }) => {
427
+ success: boolean;
428
+ affectedSegments: SegmentUnion[];
429
+ affectedTracks: TrackUnion[];
430
+ createdTracks: TrackUnion[];
431
+ removedTrackIds: string[];
432
+ };
433
+ resizeSegment: (options: {
434
+ segmentId: string;
435
+ trackId: string;
436
+ startTime: number;
437
+ endTime: number;
438
+ }) => {
439
+ success: boolean;
440
+ affectedSegments: SegmentUnion[];
441
+ affectedTracks: TrackUnion[];
442
+ createdTracks: TrackUnion[];
443
+ removedTrackIds: string[];
444
+ };
374
445
  exportProtocol: () => IVideoProtocol;
375
446
  addTransition: (transition: ITransition, addTime?: number) => boolean;
376
447
  removeTransition: (segmentId: string) => boolean;
377
448
  updateTransition: (segmentId: string, updater: (transition: ITransition) => void) => boolean;
378
- undo: () => void;
379
- redo: () => void;
449
+ replaceTrackId: (oldTrackId: string, newTrackId: string) => boolean;
450
+ replaceSegmentId: (oldSegmentId: string, newSegmentId: string) => boolean;
451
+ undo: () => {
452
+ success: boolean;
453
+ affectedSegments: SegmentUnion[];
454
+ affectedTracks: TrackUnion[];
455
+ createdTracks: TrackUnion[];
456
+ removedTrackIds: string[];
457
+ removedSegmentIds: string[];
458
+ };
459
+ redo: () => {
460
+ success: boolean;
461
+ affectedSegments: SegmentUnion[];
462
+ affectedTracks: TrackUnion[];
463
+ createdTracks: TrackUnion[];
464
+ removedTrackIds: string[];
465
+ removedSegmentIds: string[];
466
+ };
380
467
  redoCount: ComputedRef<number>;
381
468
  undoCount: ComputedRef<number>;
382
469
  };
@@ -387,6 +474,17 @@ export declare const DUPLICATE_SEGMENT_ID = "duplicate segment id";
387
474
 
388
475
  export declare const DUPLICATE_TRACK_ID = "duplicate track id";
389
476
 
477
+ /**
478
+ * Extract waveform peaks from an audio URL
479
+ * Will try to read from OPFS cache first, then fall back to fetch
480
+ */
481
+ export declare function extractWaveform(url: string, options?: WaveformOptions): Promise<WaveformData>;
482
+
483
+ /**
484
+ * Extract waveform from an ArrayBuffer directly (no fetch needed)
485
+ */
486
+ export declare function extractWaveformFromBuffer(arrayBuffer: ArrayBuffer, cacheKey: string, options?: Omit<WaveformOptions, 'resourceDir'>): Promise<WaveformData>;
487
+
390
488
  export declare function fileTo(type: IResType): typeof fileToImage | typeof fileToMP4 | (() => Promise<void>) | (() => Promise<void>) | (() => Promise<void>);
391
489
 
392
490
  declare function fileToImage(file: OPFSToolFile): Promise<HTMLImageElement | undefined>;
@@ -421,6 +519,17 @@ declare interface GenerateThumbnailsOptions {
421
519
  resourceDir?: string;
422
520
  }
423
521
 
522
+ export declare function getMp4Meta(url: string, options?: {
523
+ resourceDir?: string;
524
+ }): Promise<Mp4Meta>;
525
+
526
+ /**
527
+ * Convert a user-provided URL into a stable, OPFS-safe key.
528
+ * - Removes query/hash so signed URLs map to a single cached entry.
529
+ * - Avoids `://` and other path-unfriendly sequences by encoding segments.
530
+ */
531
+ export declare function getResourceKey(url: string): string;
532
+
424
533
  export declare function getResourceType(url: string): Promise<{
425
534
  type: IResType;
426
535
  totalSize: number;
@@ -446,6 +555,15 @@ export { ITrackType }
446
555
 
447
556
  export { IVideoProtocol }
448
557
 
558
+ declare interface Mp4Meta {
559
+ durationUs: number;
560
+ durationMs: number;
561
+ width: number;
562
+ height: number;
563
+ audioSampleRate: number;
564
+ audioChanCount: number;
565
+ }
566
+
449
567
  declare type OPFSToolFile = ReturnType<typeof file>;
450
568
 
451
569
  export declare const parse: parseFn;
@@ -454,6 +572,19 @@ declare type parseFn = (videoProtocolStr: string) => object;
454
572
 
455
573
  declare type PartialByKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
456
574
 
575
+ /**
576
+ * Generate waveform bars data for canvas/div rendering
577
+ */
578
+ export declare function peaksToBars(peaks: number[], containerWidth: number): Array<{
579
+ x: number;
580
+ height: number;
581
+ }>;
582
+
583
+ /**
584
+ * Create a simple waveform SVG path from peaks
585
+ */
586
+ export declare function peaksToSvgPath(peaks: number[], width: number, height: number): string;
587
+
457
588
  export { SegmentUnion }
458
589
 
459
590
  declare interface Thumbnail {
@@ -467,4 +598,21 @@ export { TrackUnion }
467
598
 
468
599
  export declare function vFetch(url: string, init?: RequestInit): Promise<Response>;
469
600
 
601
+ /**
602
+ * Extract waveform peaks from audio for visualization
603
+ */
604
+ export declare interface WaveformData {
605
+ peaks: number[];
606
+ duration: number;
607
+ }
608
+
609
+ export declare interface WaveformOptions {
610
+ /** Number of samples to extract (default: 100) */
611
+ samples?: number;
612
+ /** Channel to use: 0 = left, 1 = right, 'mix' = average (default: 'mix') */
613
+ channel?: number | 'mix';
614
+ /** Resource directory for OPFS cache (default: DEFAULT_RESOURCE_DIR) */
615
+ resourceDir?: string;
616
+ }
617
+
470
618
  export { }