visualfries 0.1.0
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 +21 -0
- package/README.md +213 -0
- package/dist/DIContainer.d.ts +4 -0
- package/dist/DIContainer.js +145 -0
- package/dist/SceneBuilder.svelte.d.ts +8574 -0
- package/dist/SceneBuilder.svelte.js +409 -0
- package/dist/adapters/subtitleHelpers.d.ts +2 -0
- package/dist/adapters/subtitleHelpers.js +187 -0
- package/dist/animations/AnimationContext.d.ts +17 -0
- package/dist/animations/AnimationContext.js +72 -0
- package/dist/animations/AnimationPresetsRegister.d.ts +362 -0
- package/dist/animations/AnimationPresetsRegister.js +20 -0
- package/dist/animations/AnimationSetup.d.ts +8 -0
- package/dist/animations/AnimationSetup.js +30 -0
- package/dist/animations/SplitTextCache.d.ts +28 -0
- package/dist/animations/SplitTextCache.js +68 -0
- package/dist/animations/animationBuilder.d.ts +31 -0
- package/dist/animations/animationBuilder.js +255 -0
- package/dist/animations/animationPreset.d.ts +7 -0
- package/dist/animations/animationPreset.js +31 -0
- package/dist/animations/builders/AnimationPresetFactory.d.ts +43 -0
- package/dist/animations/builders/AnimationPresetFactory.js +139 -0
- package/dist/animations/builders/LineHighlighterAnimationBuilder.d.ts +16 -0
- package/dist/animations/builders/LineHighlighterAnimationBuilder.js +183 -0
- package/dist/animations/builders/WordHighlighterAnimationBuilder.d.ts +15 -0
- package/dist/animations/builders/WordHighlighterAnimationBuilder.js +180 -0
- package/dist/animations/engines/AnimationEngineAdaptor.d.ts +107 -0
- package/dist/animations/engines/AnimationEngineAdaptor.js +1 -0
- package/dist/animations/engines/GSAPEngineAdaptor.d.ts +21 -0
- package/dist/animations/engines/GSAPEngineAdaptor.js +145 -0
- package/dist/animations/presets/index.d.ts +2 -0
- package/dist/animations/presets/index.js +3 -0
- package/dist/animations/presets/lines.d.ts +52 -0
- package/dist/animations/presets/lines.js +547 -0
- package/dist/animations/presets/words.d.ts +31 -0
- package/dist/animations/presets/words.js +268 -0
- package/dist/animations/transformers/AnimationReferenceTransformer.d.ts +9 -0
- package/dist/animations/transformers/AnimationReferenceTransformer.js +114 -0
- package/dist/builders/PixiComponentBuilder.d.ts +63 -0
- package/dist/builders/PixiComponentBuilder.js +112 -0
- package/dist/builders/_ComponentState.svelte.d.ts +795 -0
- package/dist/builders/_ComponentState.svelte.js +203 -0
- package/dist/builders/html/HtmlBuilder.d.ts +66 -0
- package/dist/builders/html/HtmlBuilder.js +171 -0
- package/dist/builders/html/HtmlBuilderFactory.d.ts +27 -0
- package/dist/builders/html/HtmlBuilderFactory.js +30 -0
- package/dist/builders/html/StyleBuilder.d.ts +13 -0
- package/dist/builders/html/StyleBuilder.js +133 -0
- package/dist/builders/html/StyleProcessor.d.ts +9 -0
- package/dist/builders/html/StyleProcessor.js +1 -0
- package/dist/builders/html/TextComponentHtmlBuilder.d.ts +16 -0
- package/dist/builders/html/TextComponentHtmlBuilder.js +93 -0
- package/dist/builders/html/TextShadowBuilder.d.ts +60 -0
- package/dist/builders/html/TextShadowBuilder.js +227 -0
- package/dist/builders/html/processors/AppearanceStyleProcessor.d.ts +5 -0
- package/dist/builders/html/processors/AppearanceStyleProcessor.js +57 -0
- package/dist/builders/html/processors/TextAppearanceStyleProcessor.d.ts +5 -0
- package/dist/builders/html/processors/TextAppearanceStyleProcessor.js +37 -0
- package/dist/builders/html/processors/TextEffectsStyleProcessor.d.ts +6 -0
- package/dist/builders/html/processors/TextEffectsStyleProcessor.js +68 -0
- package/dist/commands/Command.d.ts +6 -0
- package/dist/commands/Command.js +1 -0
- package/dist/commands/CommandRunner.d.ts +28 -0
- package/dist/commands/CommandRunner.js +81 -0
- package/dist/commands/CommandTypes.d.ts +11 -0
- package/dist/commands/CommandTypes.js +13 -0
- package/dist/commands/PauseCommand.d.ts +4 -0
- package/dist/commands/PauseCommand.js +5 -0
- package/dist/commands/PlayCommand.d.ts +4 -0
- package/dist/commands/PlayCommand.js +6 -0
- package/dist/commands/RenderCommand.d.ts +15 -0
- package/dist/commands/RenderCommand.js +18 -0
- package/dist/commands/RenderFrameCommand.d.ts +17 -0
- package/dist/commands/RenderFrameCommand.js +93 -0
- package/dist/commands/ReplaceSourceOnTimeCommand.d.ts +4 -0
- package/dist/commands/ReplaceSourceOnTimeCommand.js +22 -0
- package/dist/commands/SeekCommand.d.ts +15 -0
- package/dist/commands/SeekCommand.js +39 -0
- package/dist/commands/UpdateComponentCommand.d.ts +4 -0
- package/dist/commands/UpdateComponentCommand.js +17 -0
- package/dist/components/AnimatedGIF.d.ts +201 -0
- package/dist/components/AnimatedGIF.js +391 -0
- package/dist/components/Component.svelte.d.ts +33 -0
- package/dist/components/Component.svelte.js +152 -0
- package/dist/components/ComponentContext.svelte.d.ts +33 -0
- package/dist/components/ComponentContext.svelte.js +105 -0
- package/dist/components/hooks/AnimationHook.d.ts +25 -0
- package/dist/components/hooks/AnimationHook.js +180 -0
- package/dist/components/hooks/CanvasShapeHook.d.ts +12 -0
- package/dist/components/hooks/CanvasShapeHook.js +229 -0
- package/dist/components/hooks/HtmlAnimationHook.d.ts +8 -0
- package/dist/components/hooks/HtmlAnimationHook.js +70 -0
- package/dist/components/hooks/HtmlTextHook.d.ts +16 -0
- package/dist/components/hooks/HtmlTextHook.js +102 -0
- package/dist/components/hooks/HtmlToCanvasHook.d.ts +16 -0
- package/dist/components/hooks/HtmlToCanvasHook.js +148 -0
- package/dist/components/hooks/ImageHook.d.ts +10 -0
- package/dist/components/hooks/ImageHook.js +45 -0
- package/dist/components/hooks/MediaHook.d.ts +15 -0
- package/dist/components/hooks/MediaHook.js +252 -0
- package/dist/components/hooks/MediaSeekingHook.d.ts +12 -0
- package/dist/components/hooks/MediaSeekingHook.js +204 -0
- package/dist/components/hooks/PixiDisplayObjectHook.d.ts +15 -0
- package/dist/components/hooks/PixiDisplayObjectHook.js +77 -0
- package/dist/components/hooks/PixiGifHook.d.ts +15 -0
- package/dist/components/hooks/PixiGifHook.js +97 -0
- package/dist/components/hooks/PixiProgressShapeHook.d.ts +12 -0
- package/dist/components/hooks/PixiProgressShapeHook.js +128 -0
- package/dist/components/hooks/PixiSplitScreenDisplayObjectHook.d.ts +21 -0
- package/dist/components/hooks/PixiSplitScreenDisplayObjectHook.js +210 -0
- package/dist/components/hooks/PixiTextureHook.d.ts +7 -0
- package/dist/components/hooks/PixiTextureHook.js +29 -0
- package/dist/components/hooks/PixiVideoTextureHook.d.ts +10 -0
- package/dist/components/hooks/PixiVideoTextureHook.js +35 -0
- package/dist/components/hooks/SubtitlesHook.d.ts +88 -0
- package/dist/components/hooks/SubtitlesHook.js +199 -0
- package/dist/components/hooks/VerifyGifHook.d.ts +7 -0
- package/dist/components/hooks/VerifyGifHook.js +27 -0
- package/dist/components/hooks/VerifyImageHook.d.ts +7 -0
- package/dist/components/hooks/VerifyImageHook.js +27 -0
- package/dist/components/hooks/VerifyMediaHook.d.ts +7 -0
- package/dist/components/hooks/VerifyMediaHook.js +21 -0
- package/dist/components/hooks/shapes/progress/CustomProgressRenderer.d.ts +8 -0
- package/dist/components/hooks/shapes/progress/CustomProgressRenderer.js +53 -0
- package/dist/components/hooks/shapes/progress/DoubleProgressRenderer.d.ts +8 -0
- package/dist/components/hooks/shapes/progress/DoubleProgressRenderer.js +69 -0
- package/dist/components/hooks/shapes/progress/LinearProgressRenderer.d.ts +8 -0
- package/dist/components/hooks/shapes/progress/LinearProgressRenderer.js +60 -0
- package/dist/components/hooks/shapes/progress/PerimeterProgressRenderer.d.ts +9 -0
- package/dist/components/hooks/shapes/progress/PerimeterProgressRenderer.js +213 -0
- package/dist/components/hooks/shapes/progress/ProgressRenderer.d.ts +17 -0
- package/dist/components/hooks/shapes/progress/ProgressRenderer.js +75 -0
- package/dist/components/hooks/shapes/progress/RadialProgressRenderer.d.ts +8 -0
- package/dist/components/hooks/shapes/progress/RadialProgressRenderer.js +50 -0
- package/dist/components/hooks/shapes/progress/index.d.ts +6 -0
- package/dist/components/hooks/shapes/progress/index.js +6 -0
- package/dist/composers/componentComposer.d.ts +55 -0
- package/dist/composers/componentComposer.js +118 -0
- package/dist/composers/layerComposer.d.ts +46 -0
- package/dist/composers/layerComposer.js +79 -0
- package/dist/composers/sceneComposer.d.ts +48 -0
- package/dist/composers/sceneComposer.js +92 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.js +14 -0
- package/dist/directors/ComponentDirector.d.ts +20 -0
- package/dist/directors/ComponentDirector.js +86 -0
- package/dist/factories/SceneBuilderFactory.d.ts +15 -0
- package/dist/factories/SceneBuilderFactory.js +51 -0
- package/dist/fonts/GoogleFontsProvider.d.ts +12 -0
- package/dist/fonts/GoogleFontsProvider.js +125 -0
- package/dist/fonts/fontLoader.d.ts +15 -0
- package/dist/fonts/fontLoader.js +41 -0
- package/dist/fonts/types.d.ts +1 -0
- package/dist/fonts/types.js +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +14 -0
- package/dist/layers/Layer.svelte.d.ts +8492 -0
- package/dist/layers/Layer.svelte.js +125 -0
- package/dist/managers/AppManager.svelte.d.ts +23 -0
- package/dist/managers/AppManager.svelte.js +89 -0
- package/dist/managers/ComponentsManager.svelte.d.ts +49 -0
- package/dist/managers/ComponentsManager.svelte.js +247 -0
- package/dist/managers/DomManager.d.ts +18 -0
- package/dist/managers/DomManager.js +73 -0
- package/dist/managers/EventManager.d.ts +7 -0
- package/dist/managers/EventManager.js +22 -0
- package/dist/managers/LayersManager.svelte.d.ts +8499 -0
- package/dist/managers/LayersManager.svelte.js +176 -0
- package/dist/managers/MediaManager.d.ts +32 -0
- package/dist/managers/MediaManager.js +243 -0
- package/dist/managers/RenderManager.d.ts +23 -0
- package/dist/managers/RenderManager.js +59 -0
- package/dist/managers/StateManager.svelte.d.ts +8746 -0
- package/dist/managers/StateManager.svelte.js +272 -0
- package/dist/managers/SubtitlesManager.svelte.d.ts +261 -0
- package/dist/managers/SubtitlesManager.svelte.js +1385 -0
- package/dist/managers/TimeManager.svelte.d.ts +6 -0
- package/dist/managers/TimeManager.svelte.js +18 -0
- package/dist/managers/TimelineManager.svelte.d.ts +25 -0
- package/dist/managers/TimelineManager.svelte.js +152 -0
- package/dist/registers.d.ts +12 -0
- package/dist/registers.js +29 -0
- package/dist/schemas/runtime/index.d.ts +3 -0
- package/dist/schemas/runtime/index.js +4 -0
- package/dist/schemas/runtime/types.d.ts +323 -0
- package/dist/schemas/runtime/types.js +12 -0
- package/dist/schemas/scene/animations.d.ts +89738 -0
- package/dist/schemas/scene/animations.js +211 -0
- package/dist/schemas/scene/components.js +515 -0
- package/dist/schemas/scene/core.js +160 -0
- package/dist/schemas/scene/index.d.ts +22 -0
- package/dist/schemas/scene/index.js +10 -0
- package/dist/schemas/scene/properties.d.ts +914 -0
- package/dist/schemas/scene/properties.js +398 -0
- package/dist/schemas/scene/subtitles.d.ts +1141 -0
- package/dist/schemas/scene/subtitles.js +111 -0
- package/dist/schemas/scene/utils.d.ts +1 -0
- package/dist/schemas/scene/utils.js +5 -0
- package/dist/seeds/SeedFactory.d.ts +59 -0
- package/dist/seeds/SeedFactory.js +99 -0
- package/dist/seeds/index.d.ts +8 -0
- package/dist/seeds/index.js +8 -0
- package/dist/transformers/ColorTransformer.d.ts +5 -0
- package/dist/transformers/ColorTransformer.js +67 -0
- package/dist/transformers/PixiColorTransformer.d.ts +22 -0
- package/dist/transformers/PixiColorTransformer.js +104 -0
- package/dist/utils/canvas.d.ts +6 -0
- package/dist/utils/canvas.js +18 -0
- package/dist/utils/document.d.ts +2 -0
- package/dist/utils/document.js +36 -0
- package/dist/utils/emoji.d.ts +10 -0
- package/dist/utils/emoji.js +51 -0
- package/dist/utils/html.d.ts +4 -0
- package/dist/utils/html.js +45 -0
- package/dist/utils/svgGenerator.d.ts +20 -0
- package/dist/utils/svgGenerator.js +103 -0
- package/dist/utils/utils.d.ts +5 -0
- package/dist/utils/utils.js +125 -0
- package/package.json +96 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { AppManager } from './AppManager.svelte.js';
|
|
2
|
+
import { Layer } from '../layers/Layer.svelte.js';
|
|
3
|
+
import { asValue } from 'awilix/browser';
|
|
4
|
+
export class LayersManager {
|
|
5
|
+
layers = new Map();
|
|
6
|
+
isBuilding = false;
|
|
7
|
+
appManager;
|
|
8
|
+
container; // Container
|
|
9
|
+
constructor(cradle) {
|
|
10
|
+
this.container = cradle.container;
|
|
11
|
+
}
|
|
12
|
+
setAppManager(appManager) {
|
|
13
|
+
this.appManager = appManager;
|
|
14
|
+
}
|
|
15
|
+
getAll() {
|
|
16
|
+
const layers = Array.from(this.layers.values()).sort((a, b) => b.order - a.order);
|
|
17
|
+
return layers;
|
|
18
|
+
}
|
|
19
|
+
update(layerId, data) {
|
|
20
|
+
const layer = this.get(layerId);
|
|
21
|
+
if (layer) {
|
|
22
|
+
layer.update(data);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
get(layerId) {
|
|
26
|
+
return this.layers.get(layerId);
|
|
27
|
+
}
|
|
28
|
+
getData() {
|
|
29
|
+
return this.getAll().map((layer) => layer.getData());
|
|
30
|
+
}
|
|
31
|
+
delete(layerId) {
|
|
32
|
+
const layer = this.layers.get(layerId);
|
|
33
|
+
if (layer) {
|
|
34
|
+
try {
|
|
35
|
+
if (layer.displayObject && this.appManager?.stage) {
|
|
36
|
+
this.appManager.stage.removeChild(layer.displayObject);
|
|
37
|
+
}
|
|
38
|
+
layer.destroy?.();
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error('Error during layer cleanup:', error);
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
this.layers.delete(layerId);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async create(layerData) {
|
|
49
|
+
if (this.isBuilding) {
|
|
50
|
+
throw new Error('Attempted to create multiple layers simultaneously. Construct one at the time.');
|
|
51
|
+
}
|
|
52
|
+
let layer = null;
|
|
53
|
+
this.isBuilding = true;
|
|
54
|
+
try {
|
|
55
|
+
// Register component-specific data in the scope
|
|
56
|
+
this.container.register({
|
|
57
|
+
layerData: asValue(layerData)
|
|
58
|
+
});
|
|
59
|
+
layer = this.container.resolve('layer');
|
|
60
|
+
if (!layer) {
|
|
61
|
+
throw new Error('Layer not found');
|
|
62
|
+
}
|
|
63
|
+
await layer.build();
|
|
64
|
+
this.layers.set(layer.id, layer);
|
|
65
|
+
if (layer.displayObject) {
|
|
66
|
+
this.appManager.stage.addChild(layer.displayObject);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
console.error(err);
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
this.isBuilding = false;
|
|
74
|
+
}
|
|
75
|
+
return layer;
|
|
76
|
+
}
|
|
77
|
+
setOrder(id, order) {
|
|
78
|
+
const layer = this.layers.get(id);
|
|
79
|
+
if (!layer)
|
|
80
|
+
return;
|
|
81
|
+
if (layer.order === order) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
layer.setOrder(order);
|
|
85
|
+
this.appManager.stage.sortChildren();
|
|
86
|
+
}
|
|
87
|
+
moveUp(id) {
|
|
88
|
+
this.#reorderLayer(id, (sortedLayers, index) => {
|
|
89
|
+
if (index >= sortedLayers.length - 1)
|
|
90
|
+
return false;
|
|
91
|
+
const currentOrder = sortedLayers[index].order;
|
|
92
|
+
const aboveOrder = sortedLayers[index + 1].order;
|
|
93
|
+
sortedLayers[index].setOrder(aboveOrder);
|
|
94
|
+
sortedLayers[index + 1].setOrder(currentOrder);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
moveDown(id) {
|
|
98
|
+
this.#reorderLayer(id, (sortedLayers, index) => {
|
|
99
|
+
if (index <= 0)
|
|
100
|
+
return false;
|
|
101
|
+
const currentOrder = sortedLayers[index].order;
|
|
102
|
+
const belowOrder = sortedLayers[index - 1].order;
|
|
103
|
+
sortedLayers[index].setOrder(belowOrder);
|
|
104
|
+
sortedLayers[index - 1].setOrder(currentOrder);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
moveToTop(id) {
|
|
108
|
+
this.#reorderLayer(id, (sortedLayers, index) => {
|
|
109
|
+
if (index === sortedLayers.length - 1)
|
|
110
|
+
return false;
|
|
111
|
+
const topOrder = sortedLayers[sortedLayers.length - 1].order;
|
|
112
|
+
sortedLayers[index].setOrder(topOrder + 1);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
moveToBottom(id) {
|
|
116
|
+
this.#reorderLayer(id, (sortedLayers, index) => {
|
|
117
|
+
if (index === 0)
|
|
118
|
+
return false;
|
|
119
|
+
const bottomOrder = sortedLayers[0].order;
|
|
120
|
+
sortedLayers[index].setOrder(bottomOrder - 1);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
moveAfter(id, targetId) {
|
|
124
|
+
this.#reorderLayer(id, (sortedLayers, index) => {
|
|
125
|
+
const targetIndex = sortedLayers.findIndex((l) => l.id === targetId);
|
|
126
|
+
if (targetIndex === -1 || targetIndex === index)
|
|
127
|
+
return false;
|
|
128
|
+
const targetOrder = sortedLayers[targetIndex].order;
|
|
129
|
+
const newOrder = targetIndex > 0 ? (targetOrder + sortedLayers[targetIndex - 1].order) / 2 : targetOrder - 1;
|
|
130
|
+
sortedLayers[index].setOrder(newOrder);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
moveBefore(id, targetId) {
|
|
134
|
+
this.#reorderLayer(id, (sortedLayers, index) => {
|
|
135
|
+
const targetIndex = sortedLayers.findIndex((l) => l.id === targetId);
|
|
136
|
+
if (targetIndex === -1 || targetIndex === index)
|
|
137
|
+
return false;
|
|
138
|
+
const targetOrder = sortedLayers[targetIndex].order;
|
|
139
|
+
const newOrder = targetIndex < sortedLayers.length - 1
|
|
140
|
+
? (targetOrder + sortedLayers[targetIndex + 1].order) / 2
|
|
141
|
+
: targetOrder + 1;
|
|
142
|
+
sortedLayers[index].setOrder(newOrder);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
#reorderLayer(id, reorderFn) {
|
|
146
|
+
const sortedLayers = this.getAll();
|
|
147
|
+
const index = sortedLayers.findIndex((l) => l.id === id);
|
|
148
|
+
if (index === -1)
|
|
149
|
+
return;
|
|
150
|
+
const result = reorderFn(sortedLayers, index);
|
|
151
|
+
if (result !== false) {
|
|
152
|
+
this.appManager.stage.sortChildren();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
bulkUpdate(updates) {
|
|
156
|
+
updates.forEach(({ id, data }) => this.update(id, data));
|
|
157
|
+
}
|
|
158
|
+
bulkDelete(ids) {
|
|
159
|
+
ids.forEach((id) => this.delete(id));
|
|
160
|
+
}
|
|
161
|
+
hide(id) {
|
|
162
|
+
this.update(id, { visible: false });
|
|
163
|
+
}
|
|
164
|
+
show(id) {
|
|
165
|
+
this.update(id, { visible: true });
|
|
166
|
+
}
|
|
167
|
+
toggle(id) {
|
|
168
|
+
const layer = this.get(id);
|
|
169
|
+
if (layer) {
|
|
170
|
+
this.update(id, { visible: !layer.visible });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
filter(predicate) {
|
|
174
|
+
return this.getAll().filter(predicate);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
type MediaType = 'video' | 'image' | 'audio';
|
|
2
|
+
interface VideoOptions {
|
|
3
|
+
muted: boolean;
|
|
4
|
+
fps?: number;
|
|
5
|
+
startAt?: number;
|
|
6
|
+
endAt?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class MediaManager {
|
|
9
|
+
private videoElements;
|
|
10
|
+
private audioElements;
|
|
11
|
+
private imageElements;
|
|
12
|
+
private mediaUsageCount;
|
|
13
|
+
private videoControllers;
|
|
14
|
+
private audioControllers;
|
|
15
|
+
getMediaElement(mediaPath: string, type: MediaType, options?: VideoOptions): Promise<HTMLImageElement | HTMLVideoElement | HTMLAudioElement>;
|
|
16
|
+
private getKey;
|
|
17
|
+
private incrementUsageCount;
|
|
18
|
+
setVideoController(videoPath: string, componentId: string | undefined): void;
|
|
19
|
+
getVideoController(videoPath: string): string | undefined;
|
|
20
|
+
setMediaController(mediaPath: string, componentId: string | undefined, type: 'audio' | 'video'): void;
|
|
21
|
+
getMediaController(videoPath: string, type: 'audio' | 'video'): string | undefined;
|
|
22
|
+
private getImageElement;
|
|
23
|
+
private getVideoElement;
|
|
24
|
+
private getAudioElement;
|
|
25
|
+
stopMediaElements(): void;
|
|
26
|
+
cloneVideoElement(originalVideo: HTMLVideoElement): HTMLVideoElement;
|
|
27
|
+
releaseMediaElement(mediaPath: string, type: MediaType): void;
|
|
28
|
+
seekVideo(mediaPath: string, time: number): Promise<void>;
|
|
29
|
+
updateVideoElement(mediaPath: string): HTMLVideoElement | undefined;
|
|
30
|
+
destroy(): void;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import Hls from 'hls.js';
|
|
2
|
+
export class MediaManager {
|
|
3
|
+
videoElements = new Map();
|
|
4
|
+
audioElements = new Map();
|
|
5
|
+
imageElements = new Map();
|
|
6
|
+
mediaUsageCount = new Map();
|
|
7
|
+
videoControllers = new Map();
|
|
8
|
+
audioControllers = new Map();
|
|
9
|
+
async getMediaElement(mediaPath, type, options) {
|
|
10
|
+
const key = this.getKey(mediaPath, type);
|
|
11
|
+
this.incrementUsageCount(key);
|
|
12
|
+
if (type === 'image') {
|
|
13
|
+
return this.getImageElement(mediaPath);
|
|
14
|
+
}
|
|
15
|
+
else if (type === 'audio') {
|
|
16
|
+
return this.getAudioElement(mediaPath, options);
|
|
17
|
+
}
|
|
18
|
+
return this.getVideoElement(mediaPath, options);
|
|
19
|
+
}
|
|
20
|
+
getKey(mediaPath, type) {
|
|
21
|
+
return `${mediaPath}-${type}`;
|
|
22
|
+
}
|
|
23
|
+
incrementUsageCount(key) {
|
|
24
|
+
const count = this.mediaUsageCount.get(key) || 0;
|
|
25
|
+
this.mediaUsageCount.set(key, count + 1);
|
|
26
|
+
}
|
|
27
|
+
setVideoController(videoPath, componentId) {
|
|
28
|
+
this.videoControllers.set(videoPath, componentId);
|
|
29
|
+
}
|
|
30
|
+
getVideoController(videoPath) {
|
|
31
|
+
return this.videoControllers.get(videoPath);
|
|
32
|
+
}
|
|
33
|
+
setMediaController(mediaPath, componentId, type) {
|
|
34
|
+
if (type === 'video') {
|
|
35
|
+
this.videoControllers.set(mediaPath, componentId);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.audioControllers.set(mediaPath, componentId);
|
|
39
|
+
}
|
|
40
|
+
getMediaController(videoPath, type) {
|
|
41
|
+
if (type === 'video') {
|
|
42
|
+
return this.videoControllers.get(videoPath);
|
|
43
|
+
}
|
|
44
|
+
return this.audioControllers.get(videoPath);
|
|
45
|
+
}
|
|
46
|
+
async getImageElement(mediaPath) {
|
|
47
|
+
if (!this.imageElements.has(mediaPath)) {
|
|
48
|
+
const img = document.createElement('img');
|
|
49
|
+
img.src = mediaPath;
|
|
50
|
+
// Check if image is already loaded/cached
|
|
51
|
+
if (img.complete && img.naturalWidth > 0) {
|
|
52
|
+
this.imageElements.set(mediaPath, img);
|
|
53
|
+
return img;
|
|
54
|
+
}
|
|
55
|
+
// Check if image failed to load
|
|
56
|
+
if (img.complete && img.naturalWidth === 0) {
|
|
57
|
+
throw new Error(`Failed to load image: ${mediaPath}`);
|
|
58
|
+
}
|
|
59
|
+
// Image is still loading, attach handlers
|
|
60
|
+
await new Promise((resolve, reject) => {
|
|
61
|
+
img.onload = resolve;
|
|
62
|
+
img.onerror = () => reject(new Error(`Failed to load image: ${mediaPath}`));
|
|
63
|
+
});
|
|
64
|
+
this.imageElements.set(mediaPath, img);
|
|
65
|
+
}
|
|
66
|
+
return this.imageElements.get(mediaPath);
|
|
67
|
+
}
|
|
68
|
+
async getVideoElement(mediaPath, options) {
|
|
69
|
+
if (!this.videoElements.has(mediaPath)) {
|
|
70
|
+
const video = document.createElement('video');
|
|
71
|
+
// video.src = mediaPath;
|
|
72
|
+
video.crossOrigin = 'anonymous';
|
|
73
|
+
video.muted = options?.muted ?? true;
|
|
74
|
+
video.playsInline = true;
|
|
75
|
+
video.loop = false;
|
|
76
|
+
video.autoplay = false;
|
|
77
|
+
if (options?.startAt !== undefined) {
|
|
78
|
+
video.currentTime = options.startAt;
|
|
79
|
+
}
|
|
80
|
+
if (mediaPath.includes('.m3u8')) {
|
|
81
|
+
if (Hls.isSupported()) {
|
|
82
|
+
const hlsConfig = {
|
|
83
|
+
startPosition: options?.startAt ?? -1
|
|
84
|
+
};
|
|
85
|
+
video.preload = 'none';
|
|
86
|
+
const hls = new Hls(hlsConfig);
|
|
87
|
+
hls.loadSource(mediaPath);
|
|
88
|
+
hls.attachMedia(video);
|
|
89
|
+
if (mediaPath.includes('mux.com')) {
|
|
90
|
+
// this.muxMonitorHlsVideo(video);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
|
94
|
+
video.preload = 'none';
|
|
95
|
+
video.src = mediaPath;
|
|
96
|
+
//
|
|
97
|
+
// If no native HLS support, check if HLS.js is supported
|
|
98
|
+
//
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
video.preload = 'auto';
|
|
103
|
+
video.src = mediaPath;
|
|
104
|
+
}
|
|
105
|
+
await new Promise((resolve, reject) => {
|
|
106
|
+
video.oncanplay = resolve;
|
|
107
|
+
video.onerror = () => reject(new Error(`Failed to load video: ${mediaPath}`));
|
|
108
|
+
});
|
|
109
|
+
this.videoElements.set(mediaPath, video);
|
|
110
|
+
}
|
|
111
|
+
return this.videoElements.get(mediaPath);
|
|
112
|
+
}
|
|
113
|
+
async getAudioElement(mediaPath, options) {
|
|
114
|
+
if (!this.audioElements.has(mediaPath)) {
|
|
115
|
+
const audio = document.createElement('audio');
|
|
116
|
+
audio.src = mediaPath;
|
|
117
|
+
audio.crossOrigin = 'anonymous';
|
|
118
|
+
audio.preload = 'auto';
|
|
119
|
+
audio.loop = false;
|
|
120
|
+
audio.autoplay = false;
|
|
121
|
+
if (options?.startAt !== undefined) {
|
|
122
|
+
audio.currentTime = options.startAt;
|
|
123
|
+
}
|
|
124
|
+
await new Promise((resolve) => {
|
|
125
|
+
audio.onloadedmetadata = resolve;
|
|
126
|
+
});
|
|
127
|
+
this.audioElements.set(mediaPath, audio);
|
|
128
|
+
}
|
|
129
|
+
return this.audioElements.get(mediaPath);
|
|
130
|
+
}
|
|
131
|
+
stopMediaElements() {
|
|
132
|
+
// Stop all videos
|
|
133
|
+
for (const video of this.videoElements.values()) {
|
|
134
|
+
if (!video.paused) {
|
|
135
|
+
video.pause();
|
|
136
|
+
video.currentTime = 0;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Stop all audio
|
|
140
|
+
for (const audio of this.audioElements.values()) {
|
|
141
|
+
if (!audio.paused) {
|
|
142
|
+
audio.pause();
|
|
143
|
+
audio.currentTime = 0;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
cloneVideoElement(originalVideo) {
|
|
148
|
+
const clonedVideo = document.createElement('video');
|
|
149
|
+
// Copy attributes
|
|
150
|
+
clonedVideo.muted = originalVideo.muted;
|
|
151
|
+
clonedVideo.playsInline = originalVideo.playsInline;
|
|
152
|
+
clonedVideo.loop = originalVideo.loop;
|
|
153
|
+
clonedVideo.autoplay = originalVideo.autoplay;
|
|
154
|
+
clonedVideo.crossOrigin = originalVideo.crossOrigin;
|
|
155
|
+
// Instead of setting src, we'll use srcObject to reference the original media source
|
|
156
|
+
if (originalVideo.srcObject) {
|
|
157
|
+
clonedVideo.srcObject = originalVideo.srcObject;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// If srcObject is not available, fall back to src
|
|
161
|
+
clonedVideo.src = originalVideo.src;
|
|
162
|
+
}
|
|
163
|
+
// Copy current state
|
|
164
|
+
clonedVideo.currentTime = originalVideo.currentTime;
|
|
165
|
+
clonedVideo.playbackRate = originalVideo.playbackRate;
|
|
166
|
+
clonedVideo.volume = originalVideo.volume;
|
|
167
|
+
// If the original was playing, start playing the clone
|
|
168
|
+
if (!originalVideo.paused) {
|
|
169
|
+
clonedVideo.play().catch((e) => console.error('Error playing cloned video:', e));
|
|
170
|
+
}
|
|
171
|
+
return clonedVideo;
|
|
172
|
+
}
|
|
173
|
+
releaseMediaElement(mediaPath, type) {
|
|
174
|
+
const key = this.getKey(mediaPath, type);
|
|
175
|
+
const count = this.mediaUsageCount.get(key);
|
|
176
|
+
if (count > 1) {
|
|
177
|
+
this.mediaUsageCount.set(key, count - 1);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
this.mediaUsageCount.delete(key);
|
|
181
|
+
if (type === 'video') {
|
|
182
|
+
const videoElement = this.videoElements.get(mediaPath);
|
|
183
|
+
if (videoElement) {
|
|
184
|
+
videoElement.src = '';
|
|
185
|
+
videoElement.load(); // This frees up memory
|
|
186
|
+
this.videoElements.delete(mediaPath);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else if (type === 'audio') {
|
|
190
|
+
const audioElement = this.audioElements.get(mediaPath);
|
|
191
|
+
if (audioElement) {
|
|
192
|
+
audioElement.pause();
|
|
193
|
+
audioElement.currentTime = 0;
|
|
194
|
+
audioElement.src = '';
|
|
195
|
+
audioElement.load(); // This frees up memory
|
|
196
|
+
this.audioElements.delete(mediaPath);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else if (type === 'image') {
|
|
200
|
+
this.imageElements.delete(mediaPath);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async seekVideo(mediaPath, time) {
|
|
205
|
+
const videoElement = this.videoElements.get(mediaPath);
|
|
206
|
+
if (videoElement) {
|
|
207
|
+
return new Promise((resolve) => {
|
|
208
|
+
const onSeeked = () => {
|
|
209
|
+
videoElement.removeEventListener('seeked', onSeeked);
|
|
210
|
+
resolve();
|
|
211
|
+
};
|
|
212
|
+
videoElement.addEventListener('seeked', onSeeked);
|
|
213
|
+
videoElement.currentTime = time;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return Promise.resolve();
|
|
217
|
+
}
|
|
218
|
+
updateVideoElement(mediaPath) {
|
|
219
|
+
return this.videoElements.get(mediaPath);
|
|
220
|
+
}
|
|
221
|
+
destroy() {
|
|
222
|
+
// Release all video elements
|
|
223
|
+
for (const videoElement of this.videoElements.values()) {
|
|
224
|
+
videoElement.pause();
|
|
225
|
+
videoElement.src = '';
|
|
226
|
+
videoElement.load();
|
|
227
|
+
}
|
|
228
|
+
this.videoElements.clear();
|
|
229
|
+
// Release all audio elements
|
|
230
|
+
for (const audioElement of this.audioElements.values()) {
|
|
231
|
+
audioElement.pause();
|
|
232
|
+
audioElement.src = '';
|
|
233
|
+
audioElement.load();
|
|
234
|
+
}
|
|
235
|
+
this.audioElements.clear();
|
|
236
|
+
// Clear image elements
|
|
237
|
+
this.imageElements.clear();
|
|
238
|
+
// Clear usage counts
|
|
239
|
+
this.mediaUsageCount.clear();
|
|
240
|
+
// Clear video controllers
|
|
241
|
+
this.videoControllers.clear();
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { StateManager } from './StateManager.svelte.js';
|
|
2
|
+
import { EventManager } from './EventManager.js';
|
|
3
|
+
import type { ResourceManager, IComponent, ComponentData } from '..';
|
|
4
|
+
import type { AppearanceInput } from '..';
|
|
5
|
+
import { AppManager } from './AppManager.svelte.js';
|
|
6
|
+
export declare class RenderManager {
|
|
7
|
+
private state;
|
|
8
|
+
private componentsManager;
|
|
9
|
+
private eventManager;
|
|
10
|
+
private appManager;
|
|
11
|
+
private lastActiveById;
|
|
12
|
+
private lastRenderTime;
|
|
13
|
+
constructor(cradle: {
|
|
14
|
+
stateManager: StateManager;
|
|
15
|
+
componentsManager: ResourceManager<IComponent, ComponentData, AppearanceInput>;
|
|
16
|
+
eventManager: EventManager;
|
|
17
|
+
appManager: AppManager;
|
|
18
|
+
});
|
|
19
|
+
private initializeEventListeners;
|
|
20
|
+
private handleBeforeRender;
|
|
21
|
+
render(): Promise<void>;
|
|
22
|
+
destroy(): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { StateManager } from './StateManager.svelte.js';
|
|
2
|
+
import { EventManager } from './EventManager.js';
|
|
3
|
+
import { AppManager } from './AppManager.svelte.js';
|
|
4
|
+
export class RenderManager {
|
|
5
|
+
state;
|
|
6
|
+
componentsManager;
|
|
7
|
+
eventManager;
|
|
8
|
+
appManager;
|
|
9
|
+
// Track last visibility to ensure we run one more update to hide components that just became invisible
|
|
10
|
+
lastActiveById = new Map();
|
|
11
|
+
lastRenderTime = -1;
|
|
12
|
+
constructor(cradle) {
|
|
13
|
+
this.state = cradle.stateManager;
|
|
14
|
+
this.componentsManager = cradle.componentsManager;
|
|
15
|
+
this.eventManager = cradle.eventManager;
|
|
16
|
+
this.appManager = cradle.appManager;
|
|
17
|
+
this.initializeEventListeners();
|
|
18
|
+
}
|
|
19
|
+
initializeEventListeners() {
|
|
20
|
+
this.eventManager.on('beforerender', this.handleBeforeRender.bind(this));
|
|
21
|
+
this.eventManager.on('timeupdate', this.render.bind(this));
|
|
22
|
+
this.eventManager.on('rerender', this.render.bind(this));
|
|
23
|
+
this.eventManager.on('changestate', (event) => {
|
|
24
|
+
if (event.detail.state !== 'playing') {
|
|
25
|
+
this.render();
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async handleBeforeRender() { }
|
|
30
|
+
async render() {
|
|
31
|
+
const components = this.componentsManager.getAll();
|
|
32
|
+
const currentTime = this.state.currentTime;
|
|
33
|
+
const entries = components.map((component) => {
|
|
34
|
+
const startAt = component.props.timeline.startAt ?? 0;
|
|
35
|
+
const endAt = component.props.timeline.endAt ?? this.state.duration;
|
|
36
|
+
const isVisibleByTime = currentTime >= startAt && currentTime <= endAt;
|
|
37
|
+
const isExplicitlyVisible = component.props.visible !== false;
|
|
38
|
+
const shouldBeVisible = (isVisibleByTime && isExplicitlyVisible) || component.type === 'VIDEO';
|
|
39
|
+
const wasVisible = this.lastActiveById.get(component.id) === true;
|
|
40
|
+
return { component, shouldBeVisible, wasVisible };
|
|
41
|
+
});
|
|
42
|
+
const toUpdate = entries
|
|
43
|
+
.filter((e) => e.shouldBeVisible || e.wasVisible)
|
|
44
|
+
.map((e) => e.component);
|
|
45
|
+
await Promise.all(toUpdate.map((component) => component.update()));
|
|
46
|
+
// Update visibility cache after updates
|
|
47
|
+
for (const e of entries) {
|
|
48
|
+
this.lastActiveById.set(e.component.id, e.shouldBeVisible);
|
|
49
|
+
}
|
|
50
|
+
this.appManager.render();
|
|
51
|
+
this.lastRenderTime = currentTime;
|
|
52
|
+
}
|
|
53
|
+
destroy() {
|
|
54
|
+
this.eventManager.removeEventListener('beforerender', this.handleBeforeRender);
|
|
55
|
+
this.eventManager.removeEventListener('timeupdate', this.render);
|
|
56
|
+
this.eventManager.removeEventListener('rerender', this.render);
|
|
57
|
+
this.eventManager.removeEventListener('changestate', this.render);
|
|
58
|
+
}
|
|
59
|
+
}
|