@video-editor/protocol 0.0.1-beta.3 → 0.0.1-beta.30
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 +93 -2
- package/dist/index.d.ts +160 -12
- package/dist/index.js +7980 -7900
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -1,3 +1,94 @@
|
|
|
1
|
-
# @video-editor/
|
|
1
|
+
# @video-editor/protocol
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
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
|
-
|
|
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 |
|
|
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
|
-
|
|
292
|
+
sticker: readonly {
|
|
275
293
|
readonly trackId: string;
|
|
276
|
-
readonly trackType: "
|
|
294
|
+
readonly trackType: "sticker";
|
|
277
295
|
readonly children: readonly {
|
|
278
|
-
readonly segmentType: "
|
|
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"
|
|
372
|
-
|
|
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
|
-
|
|
379
|
-
|
|
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 { }
|