openvideo 0.0.1
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/LICENSE +63 -0
- package/dist/SharedSystems-pXK0PjzC.js +2691 -0
- package/dist/WebGLRenderer-B_y7ugdK.js +2639 -0
- package/dist/WebGPURenderer-ByeYcldK.js +1655 -0
- package/dist/browserAll-lTqv9nwW.js +1876 -0
- package/dist/clips/audio-clip.d.ts +128 -0
- package/dist/clips/base-clip.d.ts +116 -0
- package/dist/clips/caption-clip.d.ts +392 -0
- package/dist/clips/effect-clip.d.ts +37 -0
- package/dist/clips/iclip.d.ts +178 -0
- package/dist/clips/image-clip.d.ts +115 -0
- package/dist/clips/index.d.ts +14 -0
- package/dist/clips/placeholder-clip.d.ts +15 -0
- package/dist/clips/text-clip.d.ts +266 -0
- package/dist/clips/transition-clip.d.ts +45 -0
- package/dist/clips/video-clip.d.ts +238 -0
- package/dist/colorToUniform-C2jGzNe1.js +97 -0
- package/dist/compositor.d.ts +107 -0
- package/dist/effect/effect.d.ts +6 -0
- package/dist/effect/glsl/custom-glsl.d.ts +1017 -0
- package/dist/effect/glsl/gl-effect.d.ts +16 -0
- package/dist/effect/types.d.ts +24 -0
- package/dist/effect/vertex.d.ts +1 -0
- package/dist/event-emitter.d.ts +55 -0
- package/dist/index-CIRqJXOw.js +41882 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.es.js +28 -0
- package/dist/index.umd.js +7741 -0
- package/dist/json-serialization.d.ts +222 -0
- package/dist/mp4-utils/index.d.ts +31 -0
- package/dist/mp4-utils/mp4box-utils.d.ts +44 -0
- package/dist/mp4-utils/sample-transform.d.ts +26 -0
- package/dist/sprite/base-sprite.d.ts +183 -0
- package/dist/sprite/pixi-sprite-renderer.d.ts +49 -0
- package/dist/studio/history-manager.d.ts +46 -0
- package/dist/studio/resource-manager.d.ts +53 -0
- package/dist/studio/resource-manager.spec.d.ts +1 -0
- package/dist/studio/selection-manager.d.ts +50 -0
- package/dist/studio/timeline-model.d.ts +110 -0
- package/dist/studio/transport.d.ts +44 -0
- package/dist/studio.d.ts +380 -0
- package/dist/studio.spec.d.ts +1 -0
- package/dist/transfomer/parts/handle.d.ts +17 -0
- package/dist/transfomer/parts/snapping.d.ts +41 -0
- package/dist/transfomer/parts/wireframe.d.ts +5 -0
- package/dist/transfomer/transformer.d.ts +31 -0
- package/dist/transition/fragment.d.ts +1 -0
- package/dist/transition/glsl/custom-glsl.d.ts +231 -0
- package/dist/transition/glsl/gl-transition.d.ts +18 -0
- package/dist/transition/transition.d.ts +6 -0
- package/dist/transition/types.d.ts +29 -0
- package/dist/transition/uniforms.d.ts +35 -0
- package/dist/transition/vertex.d.ts +1 -0
- package/dist/utils/asset-manager.d.ts +6 -0
- package/dist/utils/audio-codec-detector.d.ts +28 -0
- package/dist/utils/audio.d.ts +82 -0
- package/dist/utils/chromakey.d.ts +24 -0
- package/dist/utils/color.d.ts +9 -0
- package/dist/utils/common.d.ts +7 -0
- package/dist/utils/dom.d.ts +48 -0
- package/dist/utils/fonts.d.ts +16 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/log.d.ts +27 -0
- package/dist/utils/srt-parser.d.ts +15 -0
- package/dist/utils/stream-utils.d.ts +9 -0
- package/dist/utils/video.d.ts +16 -0
- package/dist/utils/worker-timer.d.ts +1 -0
- package/dist/webworkerAll-BsrA7oC4.js +2670 -0
- package/package.json +63 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { BaseClip } from './base-clip';
|
|
2
|
+
import { IClip, IPlaybackCapable } from './iclip';
|
|
3
|
+
import { AudioJSON } from '../json-serialization';
|
|
4
|
+
interface IAudioOpts {
|
|
5
|
+
loop?: boolean;
|
|
6
|
+
volume?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Audio clip providing audio data for creating and editing audio/video
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Load audio clip asynchronously
|
|
13
|
+
* const audioClip = await Audio.fromUrl('path/to/audio.mp3', {
|
|
14
|
+
* loop: true,
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // Traditional approach (for advanced use)
|
|
19
|
+
* new Audio((await fetch('<mp3 url>')).body, {
|
|
20
|
+
* loop: true,
|
|
21
|
+
* }),
|
|
22
|
+
*/
|
|
23
|
+
export declare class Audio extends BaseClip implements IPlaybackCapable {
|
|
24
|
+
readonly type = "Audio";
|
|
25
|
+
static ctx: AudioContext | null;
|
|
26
|
+
ready: IClip['ready'];
|
|
27
|
+
private _meta;
|
|
28
|
+
/**
|
|
29
|
+
* Audio metadata
|
|
30
|
+
*
|
|
31
|
+
* ⚠️ Note, these are converted (normalized) metadata, not original audio metadata
|
|
32
|
+
*/
|
|
33
|
+
get meta(): {
|
|
34
|
+
sampleRate: number;
|
|
35
|
+
chanCount: number;
|
|
36
|
+
duration: number;
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
};
|
|
40
|
+
private chan0Buf;
|
|
41
|
+
private chan1Buf;
|
|
42
|
+
/**
|
|
43
|
+
* Get complete PCM data from audio clip
|
|
44
|
+
*/
|
|
45
|
+
getPCMData(): Float32Array[];
|
|
46
|
+
private opts;
|
|
47
|
+
/**
|
|
48
|
+
* Whether to loop the audio (hybrid JSON structure)
|
|
49
|
+
*/
|
|
50
|
+
loop: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Load an audio clip from a URL
|
|
53
|
+
* @param url Audio URL
|
|
54
|
+
* @param opts Audio configuration (loop, volume)
|
|
55
|
+
* @returns Promise that resolves to an audio clip
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* const audioClip = await Audio.fromUrl('path/to/audio.mp3', {
|
|
59
|
+
* loop: true,
|
|
60
|
+
* volume: 0.8,
|
|
61
|
+
* });
|
|
62
|
+
*/
|
|
63
|
+
static fromUrl(url: string, opts?: IAudioOpts): Promise<Audio>;
|
|
64
|
+
/**
|
|
65
|
+
* Create an Audio instance from a JSON object (fabric.js pattern)
|
|
66
|
+
* @param json The JSON object representing the clip
|
|
67
|
+
* @returns Promise that resolves to an Audio instance
|
|
68
|
+
*/
|
|
69
|
+
static fromObject(json: AudioJSON): Promise<Audio>;
|
|
70
|
+
/**
|
|
71
|
+
*
|
|
72
|
+
* @param dataSource Audio file stream
|
|
73
|
+
* @param opts Audio configuration, controls volume and whether to loop
|
|
74
|
+
*/
|
|
75
|
+
constructor(dataSource: ReadableStream<Uint8Array> | Float32Array[], opts?: IAudioOpts, src?: string);
|
|
76
|
+
private init;
|
|
77
|
+
/**
|
|
78
|
+
* Intercept data returned by {@link Audio.tick} method for secondary processing of audio data
|
|
79
|
+
* @param time Time when tick was called
|
|
80
|
+
* @param tickRet Data returned by tick
|
|
81
|
+
*
|
|
82
|
+
*/
|
|
83
|
+
tickInterceptor: <T extends Awaited<ReturnType<Audio['tick']>>>(time: number, tickRet: T) => Promise<T>;
|
|
84
|
+
private timestamp;
|
|
85
|
+
private frameOffset;
|
|
86
|
+
/**
|
|
87
|
+
* Return audio PCM data corresponding to the time difference between last and current moments
|
|
88
|
+
*
|
|
89
|
+
* If the difference exceeds 3s or current time is less than last time, reset state
|
|
90
|
+
* @example
|
|
91
|
+
* tick(0) // => []
|
|
92
|
+
* tick(1e6) // => [leftChanPCM(1s), rightChanPCM(1s)]
|
|
93
|
+
*
|
|
94
|
+
*/
|
|
95
|
+
tick(time: number): Promise<{
|
|
96
|
+
audio: Float32Array[];
|
|
97
|
+
state: 'success' | 'done';
|
|
98
|
+
}>;
|
|
99
|
+
/**
|
|
100
|
+
* Split at specified time, return two audio clips before and after
|
|
101
|
+
* @param time Time in microseconds
|
|
102
|
+
*/
|
|
103
|
+
split(time: number): Promise<[this, this]>;
|
|
104
|
+
clone(): Promise<this>;
|
|
105
|
+
/**
|
|
106
|
+
* Destroy instance and release resources
|
|
107
|
+
*/
|
|
108
|
+
destroy(): void;
|
|
109
|
+
toJSON(main?: boolean): AudioJSON;
|
|
110
|
+
static concatAudio: typeof concatAudioClip;
|
|
111
|
+
/**
|
|
112
|
+
* Create HTMLAudioElement for playback
|
|
113
|
+
*/
|
|
114
|
+
createPlaybackElement(): Promise<{
|
|
115
|
+
element: HTMLAudioElement;
|
|
116
|
+
objectUrl?: string;
|
|
117
|
+
}>;
|
|
118
|
+
play(element: HTMLVideoElement | HTMLAudioElement, timeSeconds: number): Promise<void>;
|
|
119
|
+
pause(element: HTMLVideoElement | HTMLAudioElement): void;
|
|
120
|
+
seek(element: HTMLVideoElement | HTMLAudioElement, timeSeconds: number): Promise<void>;
|
|
121
|
+
syncPlayback(element: HTMLVideoElement | HTMLAudioElement, isPlaying: boolean, timeSeconds: number): void;
|
|
122
|
+
cleanupPlayback(element: HTMLVideoElement | HTMLAudioElement, objectUrl?: string): void;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Concatenate multiple AudioClips
|
|
126
|
+
*/
|
|
127
|
+
export declare function concatAudioClip(clips: Audio[], opts?: IAudioOpts): Promise<Audio>;
|
|
128
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { BaseSprite, BaseSpriteEvents } from '../sprite/base-sprite';
|
|
2
|
+
import { IClip, IClipMeta, ITransitionInfo } from './iclip';
|
|
3
|
+
import { ClipJSON } from '../json-serialization';
|
|
4
|
+
/**
|
|
5
|
+
* Base class for all clips that extends BaseSprite
|
|
6
|
+
* Provides common functionality for sprite operations (position, animation, timing)
|
|
7
|
+
* and frame management
|
|
8
|
+
*/
|
|
9
|
+
export declare abstract class BaseClip<T extends BaseSpriteEvents = BaseSpriteEvents> extends BaseSprite<T> implements IClip<T> {
|
|
10
|
+
abstract readonly type: string;
|
|
11
|
+
private lastVf;
|
|
12
|
+
protected destroyed: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Source URL or identifier for this clip
|
|
15
|
+
* Used for serialization and reloading from JSON
|
|
16
|
+
*/
|
|
17
|
+
src: string;
|
|
18
|
+
/**
|
|
19
|
+
* Transition info (optional)
|
|
20
|
+
*/
|
|
21
|
+
transition?: ITransitionInfo;
|
|
22
|
+
abstract tick(time: number): Promise<{
|
|
23
|
+
video?: VideoFrame | ImageBitmap | null;
|
|
24
|
+
audio?: Float32Array[];
|
|
25
|
+
state: 'done' | 'success';
|
|
26
|
+
}>;
|
|
27
|
+
ready: Promise<IClipMeta>;
|
|
28
|
+
abstract readonly meta: IClipMeta;
|
|
29
|
+
abstract clone(): Promise<this>;
|
|
30
|
+
abstract split?(time: number): Promise<[this, this]>;
|
|
31
|
+
constructor();
|
|
32
|
+
/**
|
|
33
|
+
* Get video frame and audio at specified time without rendering to canvas
|
|
34
|
+
* Useful for Pixi.js rendering where canvas context is not needed
|
|
35
|
+
* @param time Specified time in microseconds
|
|
36
|
+
*/
|
|
37
|
+
getFrame(time: number): Promise<{
|
|
38
|
+
video: ImageBitmap | null;
|
|
39
|
+
audio: Float32Array[];
|
|
40
|
+
done: boolean;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Draw image at specified time to canvas context and return corresponding audio data
|
|
44
|
+
* @param time Specified time in microseconds
|
|
45
|
+
*/
|
|
46
|
+
offscreenRender(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, time: number): Promise<{
|
|
47
|
+
audio: Float32Array[];
|
|
48
|
+
done: boolean;
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Set clip properties (position, size, display timeline)
|
|
52
|
+
* @param props Properties to set
|
|
53
|
+
* @param fps Optional FPS for frame-to-time conversion (default: 30)
|
|
54
|
+
* @returns this for method chaining
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* // Using frames (will be converted to microseconds)
|
|
58
|
+
* clip.set({
|
|
59
|
+
* display: {
|
|
60
|
+
* from: 150, // frames
|
|
61
|
+
* to: 450, // frames (10 seconds at 30fps)
|
|
62
|
+
* },
|
|
63
|
+
* }, 30);
|
|
64
|
+
*
|
|
65
|
+
* // Using microseconds directly
|
|
66
|
+
* clip.set({
|
|
67
|
+
* display: {
|
|
68
|
+
* from: 5000000, // microseconds
|
|
69
|
+
* to: 15000000, // microseconds
|
|
70
|
+
* },
|
|
71
|
+
* });
|
|
72
|
+
*/
|
|
73
|
+
set(props: {
|
|
74
|
+
display?: {
|
|
75
|
+
from?: number;
|
|
76
|
+
to?: number;
|
|
77
|
+
};
|
|
78
|
+
x?: number;
|
|
79
|
+
y?: number;
|
|
80
|
+
width?: number;
|
|
81
|
+
height?: number;
|
|
82
|
+
duration?: number;
|
|
83
|
+
}, fps?: number): this;
|
|
84
|
+
/**
|
|
85
|
+
* Base implementation of toJSON that handles common clip properties
|
|
86
|
+
* Subclasses should override to add their specific options
|
|
87
|
+
* @param main Whether this is the main clip (for Compositor)
|
|
88
|
+
*/
|
|
89
|
+
toJSON(main?: boolean): ClipJSON;
|
|
90
|
+
/**
|
|
91
|
+
* Get the list of visible transformer handles for this clip type
|
|
92
|
+
* Default implementation returns all handles
|
|
93
|
+
* Override in subclasses to customize handle visibility (e.g., TextClip)
|
|
94
|
+
*/
|
|
95
|
+
getVisibleHandles(): Array<'tl' | 'tr' | 'bl' | 'br' | 'ml' | 'mr' | 'mt' | 'mb' | 'rot'>;
|
|
96
|
+
/**
|
|
97
|
+
* Scale clip to fit within the scene dimensions while maintaining aspect ratio
|
|
98
|
+
* @param sceneWidth Scene width
|
|
99
|
+
* @param sceneHeight Scene height
|
|
100
|
+
*/
|
|
101
|
+
scaleToFit(sceneWidth: number, sceneHeight: number): Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Scale clip to fill the scene dimensions while maintaining aspect ratio
|
|
104
|
+
* May crop parts of the clip.
|
|
105
|
+
* @param sceneWidth Scene width
|
|
106
|
+
* @param sceneHeight Scene height
|
|
107
|
+
*/
|
|
108
|
+
scaleToFill(sceneWidth: number, sceneHeight: number): Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* Center the clip within the scene dimensions
|
|
111
|
+
* @param sceneWidth Scene width
|
|
112
|
+
* @param sceneHeight Scene height
|
|
113
|
+
*/
|
|
114
|
+
centerInScene(sceneWidth: number, sceneHeight: number): void;
|
|
115
|
+
destroy(): void;
|
|
116
|
+
}
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import { BaseClip } from './base-clip';
|
|
2
|
+
import { IClip } from './iclip';
|
|
3
|
+
import { CaptionJSON } from '../json-serialization';
|
|
4
|
+
import { Application, Texture } from 'pixi.js';
|
|
5
|
+
import { BaseSpriteEvents } from '../sprite/base-sprite';
|
|
6
|
+
export interface ICaptionStyle {
|
|
7
|
+
fontSize?: number;
|
|
8
|
+
fontFamily?: string;
|
|
9
|
+
fontWeight?: string | number;
|
|
10
|
+
fontStyle?: string;
|
|
11
|
+
color?: ICaptionOpts['fill'];
|
|
12
|
+
align?: ICaptionOpts['align'];
|
|
13
|
+
textCase?: ICaptionOpts['textCase'];
|
|
14
|
+
verticalAlign?: ICaptionOpts['verticalAlign'];
|
|
15
|
+
wordsPerLine?: ICaptionOpts['wordsPerLine'];
|
|
16
|
+
stroke?: {
|
|
17
|
+
color: string | number;
|
|
18
|
+
width: number;
|
|
19
|
+
};
|
|
20
|
+
shadow?: {
|
|
21
|
+
color: string | number;
|
|
22
|
+
alpha: number;
|
|
23
|
+
blur: number;
|
|
24
|
+
distance: number;
|
|
25
|
+
angle: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface ICaptionEvents extends BaseSpriteEvents {
|
|
29
|
+
propsChange: Partial<{
|
|
30
|
+
left: number;
|
|
31
|
+
top: number;
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
angle: number;
|
|
35
|
+
zIndex: number;
|
|
36
|
+
opacity: number;
|
|
37
|
+
volume: number;
|
|
38
|
+
text: string;
|
|
39
|
+
words: ICaptionOpts['words'];
|
|
40
|
+
fill: ICaptionOpts['fill'];
|
|
41
|
+
align: ICaptionOpts['align'];
|
|
42
|
+
textCase: ICaptionOpts['textCase'];
|
|
43
|
+
stroke: ICaptionOpts['stroke'];
|
|
44
|
+
dropShadow: ICaptionOpts['dropShadow'];
|
|
45
|
+
caption: ICaptionOpts['caption'];
|
|
46
|
+
wordsPerLine: ICaptionOpts['wordsPerLine'];
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
export interface ICaptionOpts {
|
|
50
|
+
/**
|
|
51
|
+
* Font size in pixels
|
|
52
|
+
* @default 30
|
|
53
|
+
*/
|
|
54
|
+
fontSize?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Font family
|
|
57
|
+
* @default 'Arial'
|
|
58
|
+
*/
|
|
59
|
+
fontFamily?: string;
|
|
60
|
+
fontUrl?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Font weight (e.g., 'normal', 'bold', '400', '700')
|
|
63
|
+
* @default 'normal'
|
|
64
|
+
*/
|
|
65
|
+
fontWeight?: string | number;
|
|
66
|
+
/**
|
|
67
|
+
* Font style (e.g., 'normal', 'italic')
|
|
68
|
+
* @default 'normal'
|
|
69
|
+
*/
|
|
70
|
+
fontStyle?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Text color (hex string, color name, or gradient object)
|
|
73
|
+
* @default '#ffffff'
|
|
74
|
+
*/
|
|
75
|
+
fill?: string | number | {
|
|
76
|
+
type: 'gradient';
|
|
77
|
+
x0: number;
|
|
78
|
+
y0: number;
|
|
79
|
+
x1: number;
|
|
80
|
+
y1: number;
|
|
81
|
+
colors: Array<{
|
|
82
|
+
ratio: number;
|
|
83
|
+
color: string | number;
|
|
84
|
+
}>;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Caption data (matches caption object in JSON)
|
|
88
|
+
*/
|
|
89
|
+
caption?: {
|
|
90
|
+
words?: Array<{
|
|
91
|
+
text: string;
|
|
92
|
+
from: number;
|
|
93
|
+
to: number;
|
|
94
|
+
isKeyWord: boolean;
|
|
95
|
+
paragraphIndex?: number;
|
|
96
|
+
}>;
|
|
97
|
+
colors?: {
|
|
98
|
+
appeared?: string;
|
|
99
|
+
active?: string;
|
|
100
|
+
activeFill?: string;
|
|
101
|
+
background?: string;
|
|
102
|
+
keyword?: string;
|
|
103
|
+
};
|
|
104
|
+
preserveKeywordColor?: boolean;
|
|
105
|
+
positioning?: {
|
|
106
|
+
videoWidth?: number;
|
|
107
|
+
videoHeight?: number;
|
|
108
|
+
bottomOffset?: number;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* @deprecated Use caption.words instead
|
|
113
|
+
*/
|
|
114
|
+
words?: Array<{
|
|
115
|
+
text: string;
|
|
116
|
+
from: number;
|
|
117
|
+
to: number;
|
|
118
|
+
isKeyWord: boolean;
|
|
119
|
+
paragraphIndex?: number;
|
|
120
|
+
}>;
|
|
121
|
+
/**
|
|
122
|
+
* @deprecated Use caption.colors instead
|
|
123
|
+
*/
|
|
124
|
+
colors?: {
|
|
125
|
+
appeared?: string;
|
|
126
|
+
active?: string;
|
|
127
|
+
activeFill?: string;
|
|
128
|
+
background?: string;
|
|
129
|
+
keyword?: string;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* @deprecated Use caption.preserveKeywordColor instead
|
|
133
|
+
*/
|
|
134
|
+
preserveKeywordColor?: boolean;
|
|
135
|
+
/**
|
|
136
|
+
* @deprecated Use caption.positioning.videoWidth instead
|
|
137
|
+
*/
|
|
138
|
+
videoWidth?: number;
|
|
139
|
+
/**
|
|
140
|
+
* @deprecated Use caption.positioning.videoHeight instead
|
|
141
|
+
*/
|
|
142
|
+
videoHeight?: number;
|
|
143
|
+
/**
|
|
144
|
+
* @deprecated Use caption.positioning.bottomOffset instead
|
|
145
|
+
*/
|
|
146
|
+
bottomOffset?: number;
|
|
147
|
+
/**
|
|
148
|
+
* Stroke color (hex string or color name) or stroke object with advanced options
|
|
149
|
+
*/
|
|
150
|
+
stroke?: string | number | {
|
|
151
|
+
color: string | number;
|
|
152
|
+
width: number;
|
|
153
|
+
join?: 'miter' | 'round' | 'bevel';
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Stroke width in pixels (used when stroke is a simple color)
|
|
157
|
+
* @default 0
|
|
158
|
+
*/
|
|
159
|
+
strokeWidth?: number;
|
|
160
|
+
/**
|
|
161
|
+
* Text alignment ('left', 'center', 'right')
|
|
162
|
+
* @default 'center'
|
|
163
|
+
*/
|
|
164
|
+
align?: 'left' | 'center' | 'right';
|
|
165
|
+
/**
|
|
166
|
+
* Drop shadow configuration
|
|
167
|
+
*/
|
|
168
|
+
dropShadow?: {
|
|
169
|
+
color?: string | number;
|
|
170
|
+
alpha?: number;
|
|
171
|
+
blur?: number;
|
|
172
|
+
angle?: number;
|
|
173
|
+
distance?: number;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Word wrap width (0 = no wrap)
|
|
177
|
+
* @default 0
|
|
178
|
+
*/
|
|
179
|
+
wordWrapWidth?: number;
|
|
180
|
+
/**
|
|
181
|
+
* Word wrap mode ('break-word' or 'normal')
|
|
182
|
+
* @default 'break-word'
|
|
183
|
+
*/
|
|
184
|
+
wordWrapMode?: 'break-word' | 'normal';
|
|
185
|
+
/**
|
|
186
|
+
* Whether to enable word wrap
|
|
187
|
+
* @default true
|
|
188
|
+
*/
|
|
189
|
+
wordWrap?: boolean;
|
|
190
|
+
/**
|
|
191
|
+
* Vertical alignment ('top', 'center', 'bottom')
|
|
192
|
+
* @default 'bottom'
|
|
193
|
+
*/
|
|
194
|
+
verticalAlign?: 'top' | 'center' | 'bottom';
|
|
195
|
+
/**
|
|
196
|
+
* Line height (multiplier)
|
|
197
|
+
* @default 1
|
|
198
|
+
*/
|
|
199
|
+
lineHeight?: number;
|
|
200
|
+
/**
|
|
201
|
+
* Letter spacing in pixels
|
|
202
|
+
* @default 0
|
|
203
|
+
*/
|
|
204
|
+
letterSpacing?: number;
|
|
205
|
+
/**
|
|
206
|
+
* Text case transformation
|
|
207
|
+
* @default 'none'
|
|
208
|
+
*/
|
|
209
|
+
textCase?: 'none' | 'uppercase' | 'lowercase' | 'title';
|
|
210
|
+
/**
|
|
211
|
+
* Media ID to which the captions were applied
|
|
212
|
+
*/
|
|
213
|
+
mediaId?: string;
|
|
214
|
+
/**
|
|
215
|
+
* Internal flag to skip automatic positioning
|
|
216
|
+
*/
|
|
217
|
+
initialLayoutApplied?: boolean;
|
|
218
|
+
/**
|
|
219
|
+
* Words per line mode ('single' or 'multiple')
|
|
220
|
+
* @default 'multiple'
|
|
221
|
+
*/
|
|
222
|
+
wordsPerLine?: 'single' | 'multiple';
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Caption clip using Canvas 2D for rendering
|
|
226
|
+
* Each instance represents a single caption segment
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* const captionClip = new CaptionClip('Hello World', {
|
|
230
|
+
* fontSize: 44,
|
|
231
|
+
* fontFamily: 'Arial',
|
|
232
|
+
* fill: '#ffffff',
|
|
233
|
+
* videoWidth: 1280,
|
|
234
|
+
* videoHeight: 720,
|
|
235
|
+
* });
|
|
236
|
+
* captionClip.display.from = 0;
|
|
237
|
+
* captionClip.duration = 3e6; // 3 seconds
|
|
238
|
+
*/
|
|
239
|
+
export declare class Caption extends BaseClip<ICaptionEvents> implements IClip {
|
|
240
|
+
readonly type = "Caption";
|
|
241
|
+
ready: IClip['ready'];
|
|
242
|
+
private _meta;
|
|
243
|
+
get meta(): {
|
|
244
|
+
duration: number;
|
|
245
|
+
width: number;
|
|
246
|
+
height: number;
|
|
247
|
+
};
|
|
248
|
+
private _visualPaddingX;
|
|
249
|
+
private _visualPaddingY;
|
|
250
|
+
private _lastTickTime;
|
|
251
|
+
get width(): number;
|
|
252
|
+
set width(v: number);
|
|
253
|
+
get height(): number;
|
|
254
|
+
set height(v: number);
|
|
255
|
+
get left(): number;
|
|
256
|
+
set left(v: number);
|
|
257
|
+
get top(): number;
|
|
258
|
+
set top(v: number);
|
|
259
|
+
private _initialLayoutApplied;
|
|
260
|
+
private _isXPositionedManually;
|
|
261
|
+
private _isWidthConstrained;
|
|
262
|
+
private _lastContentWidth;
|
|
263
|
+
private _lastContentHeight;
|
|
264
|
+
private _lastProcessedText;
|
|
265
|
+
private _text;
|
|
266
|
+
/**
|
|
267
|
+
* Caption text content (hybrid JSON structure)
|
|
268
|
+
*/
|
|
269
|
+
get text(): string;
|
|
270
|
+
set text(v: string);
|
|
271
|
+
get style(): ICaptionStyle;
|
|
272
|
+
set style(v: Partial<ICaptionOpts> | ICaptionStyle);
|
|
273
|
+
get wordsPerLine(): 'single' | 'multiple';
|
|
274
|
+
set wordsPerLine(v: 'single' | 'multiple');
|
|
275
|
+
get fontFamily(): string;
|
|
276
|
+
set fontFamily(v: string);
|
|
277
|
+
get fontUrl(): string;
|
|
278
|
+
set fontUrl(v: string);
|
|
279
|
+
get fontSize(): number;
|
|
280
|
+
set fontSize(v: number);
|
|
281
|
+
get fontWeight(): string | number;
|
|
282
|
+
set fontWeight(v: string | number);
|
|
283
|
+
get fontStyle(): 'normal' | 'italic' | 'oblique';
|
|
284
|
+
set fontStyle(v: 'normal' | 'italic' | 'oblique');
|
|
285
|
+
get fill(): ICaptionOpts['fill'];
|
|
286
|
+
set fill(v: ICaptionOpts['fill']);
|
|
287
|
+
get align(): 'left' | 'center' | 'right';
|
|
288
|
+
set align(v: 'left' | 'center' | 'right');
|
|
289
|
+
get stroke(): ICaptionOpts['stroke'];
|
|
290
|
+
set stroke(v: ICaptionOpts['stroke']);
|
|
291
|
+
get strokeWidth(): number;
|
|
292
|
+
set strokeWidth(v: number);
|
|
293
|
+
get dropShadow(): ICaptionOpts['dropShadow'];
|
|
294
|
+
set dropShadow(v: ICaptionOpts['dropShadow']);
|
|
295
|
+
get caption(): ICaptionOpts['caption'];
|
|
296
|
+
set caption(v: ICaptionOpts['caption']);
|
|
297
|
+
/**
|
|
298
|
+
* Bottom offset from video bottom (hybrid JSON structure)
|
|
299
|
+
*/
|
|
300
|
+
bottomOffset?: number;
|
|
301
|
+
/**
|
|
302
|
+
* Text case proxy
|
|
303
|
+
*/
|
|
304
|
+
get textCase(): string;
|
|
305
|
+
set textCase(v: 'none' | 'uppercase' | 'lowercase' | 'title');
|
|
306
|
+
/**
|
|
307
|
+
* Unique identifier for this clip instance
|
|
308
|
+
*/
|
|
309
|
+
id: string;
|
|
310
|
+
/**
|
|
311
|
+
* Media ID of the source clip
|
|
312
|
+
*/
|
|
313
|
+
get mediaId(): string | undefined;
|
|
314
|
+
set mediaId(v: string | undefined);
|
|
315
|
+
/**
|
|
316
|
+
* Array of effects to be applied to this clip
|
|
317
|
+
* Each effect specifies key, startTime, duration, and optional targets
|
|
318
|
+
*/
|
|
319
|
+
effects: Array<{
|
|
320
|
+
id: string;
|
|
321
|
+
key: string;
|
|
322
|
+
startTime: number;
|
|
323
|
+
duration: number;
|
|
324
|
+
}>;
|
|
325
|
+
/**
|
|
326
|
+
* Words getter for the clip
|
|
327
|
+
*/
|
|
328
|
+
get words(): ICaptionOpts["words"];
|
|
329
|
+
/**
|
|
330
|
+
* Words setter that triggers re-render and ensures consistency
|
|
331
|
+
*/
|
|
332
|
+
set words(v: ICaptionOpts['words']);
|
|
333
|
+
private opts;
|
|
334
|
+
private pixiTextContainer;
|
|
335
|
+
private renderTexture;
|
|
336
|
+
private wordTexts;
|
|
337
|
+
private textStyle;
|
|
338
|
+
private textStyleBase;
|
|
339
|
+
private _refreshing;
|
|
340
|
+
private _needsRefresh;
|
|
341
|
+
private externalRenderer;
|
|
342
|
+
private pixiApp;
|
|
343
|
+
private originalOpts;
|
|
344
|
+
constructor(text: string, opts?: ICaptionOpts, renderer?: Application['renderer']);
|
|
345
|
+
/**
|
|
346
|
+
* Update text styling options and refresh the caption rendering
|
|
347
|
+
*/
|
|
348
|
+
updateStyle(opts: Partial<ICaptionOpts>): Promise<void>;
|
|
349
|
+
private refreshCaptions;
|
|
350
|
+
private lastLoggedTime;
|
|
351
|
+
updateState(currentTime: number): void;
|
|
352
|
+
/**
|
|
353
|
+
* Get the PixiJS Texture (RenderTexture) for optimized rendering in Studio
|
|
354
|
+
*/
|
|
355
|
+
getTexture(): Promise<Texture | null>;
|
|
356
|
+
offscreenRender(ctx: CanvasRenderingContext2D, time: number): Promise<{
|
|
357
|
+
audio: Float32Array[];
|
|
358
|
+
done: boolean;
|
|
359
|
+
}>;
|
|
360
|
+
/**
|
|
361
|
+
* Set an external renderer (e.g., from Studio) to avoid creating our own Pixi App
|
|
362
|
+
*/
|
|
363
|
+
setRenderer(renderer: Application['renderer']): void;
|
|
364
|
+
private getRenderer;
|
|
365
|
+
tick(time: number): Promise<{
|
|
366
|
+
video: ImageBitmap;
|
|
367
|
+
state: 'success';
|
|
368
|
+
}>;
|
|
369
|
+
split(_time: number): Promise<[this, this]>;
|
|
370
|
+
addEffect(effect: {
|
|
371
|
+
id: string;
|
|
372
|
+
key: string;
|
|
373
|
+
startTime: number;
|
|
374
|
+
duration: number;
|
|
375
|
+
}): void;
|
|
376
|
+
editEffect(effectId: string, newEffectData: Partial<{
|
|
377
|
+
key: string;
|
|
378
|
+
startTime: number;
|
|
379
|
+
duration: number;
|
|
380
|
+
}>): void;
|
|
381
|
+
removeEffect(effectId: string): void;
|
|
382
|
+
clone(): Promise<this>;
|
|
383
|
+
destroy(): void;
|
|
384
|
+
toJSON(main?: boolean): CaptionJSON;
|
|
385
|
+
/**
|
|
386
|
+
* Create a Caption instance from a JSON object (fabric.js pattern)
|
|
387
|
+
* @param json The JSON object representing the clip
|
|
388
|
+
* @returns Promise that resolves to a Caption instance
|
|
389
|
+
*/
|
|
390
|
+
static fromObject(json: CaptionJSON): Promise<Caption>;
|
|
391
|
+
getVisibleHandles(): Array<'tl' | 'tr' | 'bl' | 'br' | 'ml' | 'mr' | 'mt' | 'mb' | 'rot'>;
|
|
392
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { BaseClip } from './base-clip';
|
|
2
|
+
import { IClip } from './iclip';
|
|
3
|
+
import { EffectKey } from '../effect/glsl/gl-effect';
|
|
4
|
+
export declare class Effect extends BaseClip {
|
|
5
|
+
readonly type = "Effect";
|
|
6
|
+
ready: IClip['ready'];
|
|
7
|
+
private _meta;
|
|
8
|
+
get meta(): {
|
|
9
|
+
duration: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Unique identifier for this clip instance
|
|
15
|
+
*/
|
|
16
|
+
id: string;
|
|
17
|
+
/**
|
|
18
|
+
* The effect configuration
|
|
19
|
+
*/
|
|
20
|
+
effect: {
|
|
21
|
+
id: string;
|
|
22
|
+
key: EffectKey;
|
|
23
|
+
name: string;
|
|
24
|
+
};
|
|
25
|
+
constructor(effectKey: EffectKey);
|
|
26
|
+
clone(): Promise<this>;
|
|
27
|
+
tick(_time: number): Promise<{
|
|
28
|
+
video: ImageBitmap | undefined;
|
|
29
|
+
state: 'success';
|
|
30
|
+
}>;
|
|
31
|
+
split(_time: number): Promise<[this, this]>;
|
|
32
|
+
toJSON(main?: boolean): any;
|
|
33
|
+
/**
|
|
34
|
+
* Create an Effect instance from a JSON object
|
|
35
|
+
*/
|
|
36
|
+
static fromObject(json: any): Promise<Effect>;
|
|
37
|
+
}
|