visualfries 0.1.902 → 0.1.1095

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.
@@ -76,13 +76,11 @@ export class PixiSplitScreenDisplayObjectHook {
76
76
  // Use sanitized value to prevent XSS
77
77
  ctx.filter = `blur(${sanitizedStrength}px)`;
78
78
  // Get the source element (video/image)
79
- const sourceElement = this.#context.getResource('videoElement');
79
+ const sourceElement = this.#context.getResource('videoElement') || this.#context.getResource('imageElement');
80
80
  if (!sourceElement) {
81
- throw new Error('videoElement not found in resources.');
81
+ // Video or Image element not ready yet - will be called again on next update
82
+ return;
82
83
  }
83
- // const sourceElement = this.#pixiTexture.baseTexture.resource.source as
84
- // | HTMLVideoElement
85
- // | HTMLImageElement;
86
84
  // Draw the original texture with blur
87
85
  ctx.drawImage(sourceElement, 0, 0, this.#bgCanvas.width, this.#bgCanvas.height);
88
86
  }
@@ -171,6 +169,8 @@ export class PixiSplitScreenDisplayObjectHook {
171
169
  this.#drawBlurredBackground();
172
170
  }
173
171
  if (this.#displayObject) {
172
+ // Always re-assert the resource in case the context was cleared or updated
173
+ this.#context.setResource('pixiRenderObject', this.#displayObject);
174
174
  if (this.#displayObject.visible != isActive) {
175
175
  this.#displayObject.visible = isActive;
176
176
  }
@@ -186,10 +186,28 @@ export class PixiSplitScreenDisplayObjectHook {
186
186
  this.#context.setResource('pixiRenderObject', this.#displayObject);
187
187
  }
188
188
  async #handleRefresh() {
189
- await this.#handleDestroy();
190
- this.#displayObject.removeChildren();
191
- this.#initDisplayObject();
192
- await this.#handleUpdate();
189
+ const currentTexture = this.#context.getResource('pixiTexture');
190
+ // Check if texture has changed (e.g., video source change)
191
+ if (currentTexture && currentTexture !== this.#pixiTexture) {
192
+ // Texture changed - need to recreate everything
193
+ await this.#handleDestroy();
194
+ this.#pixiTexture = currentTexture;
195
+ if (this.#displayObject) {
196
+ this.#displayObject.removeChildren();
197
+ this.#initDisplayObject();
198
+ }
199
+ }
200
+ else if (this.#displayObject?.children?.length > 0) {
201
+ // Same texture - just update sprite properties (position, size, etc.)
202
+ // For split screen, we may need to rebuild if effects changed
203
+ // For now, trigger a full rebuild on refresh
204
+ this.#displayObject.removeChildren();
205
+ if (currentTexture) {
206
+ this.#pixiTexture = currentTexture;
207
+ this.#initDisplayObject();
208
+ }
209
+ }
210
+ // If no texture yet, #handleUpdate will handle initial creation
193
211
  }
194
212
  async #handleDestroy() {
195
213
  // remove event listeners from video
@@ -198,7 +216,7 @@ export class PixiSplitScreenDisplayObjectHook {
198
216
  async handle(type, context) {
199
217
  this.#context = context;
200
218
  const data = this.#context.contextData;
201
- if (!data || data.type !== 'VIDEO') {
219
+ if (!data || (data.type !== 'VIDEO' && data.type !== 'IMAGE')) {
202
220
  return;
203
221
  }
204
222
  this.componentElement = data;
@@ -2,26 +2,51 @@ import * as PIXI from 'pixi.js-legacy';
2
2
  import { VideoComponentShape } from '../..';
3
3
  import { z } from 'zod';
4
4
  export class PixiVideoTextureHook {
5
- types = ['update'];
5
+ // Note: 'refresh' is NOT included - timeline position changes don't need texture recreation.
6
+ // The video element persists and the same texture can continue to be used.
7
+ // Only 'refresh:content' (source change) should recreate the texture.
8
+ types = ['update', 'destroy', 'refresh:content'];
6
9
  priority = 1;
7
10
  #context;
8
11
  #videoTexture;
12
+ #videoElement;
9
13
  componentElement;
14
+ #handlers = {
15
+ update: this.#handleUpdate.bind(this),
16
+ destroy: this.#handleDestroy.bind(this),
17
+ 'refresh:content': this.#handleRefreshContent.bind(this)
18
+ };
10
19
  async #handleUpdate() {
20
+ const media = this.#context.getResource('videoElement');
21
+ // If element changed or texture is missing, recreate it
22
+ if (media && (media !== this.#videoElement || !this.#videoTexture)) {
23
+ await this.#handleDestroy();
24
+ const res = new PIXI.VideoResource(media, {
25
+ autoPlay: false,
26
+ updateFPS: 30
27
+ });
28
+ const baseTexture = new PIXI.BaseTexture(res);
29
+ this.#videoTexture = new PIXI.Texture(baseTexture);
30
+ this.#videoElement = media;
31
+ }
32
+ // Always re-assert the resource in case the context was cleared or updated
11
33
  if (this.#videoTexture) {
12
- return;
34
+ this.#context.setResource('pixiTexture', this.#videoTexture);
13
35
  }
14
- const media = this.#context.getResource('videoElement');
15
- if (!media) {
16
- throw new Error('videoElement not found in resources.');
36
+ }
37
+ async #handleDestroy() {
38
+ if (this.#videoTexture) {
39
+ // Destroy the texture and its base texture to free GPU memory
40
+ this.#videoTexture.destroy(true);
41
+ this.#videoTexture = undefined;
42
+ this.#videoElement = undefined;
43
+ this.#context.removeResource('pixiTexture');
17
44
  }
18
- const res = new PIXI.VideoResource(media, {
19
- autoPlay: false,
20
- updateFPS: 30
21
- });
22
- const baseTexture = new PIXI.BaseTexture(res);
23
- this.#videoTexture = new PIXI.Texture(baseTexture);
24
- this.#context.setResource('pixiTexture', this.#videoTexture);
45
+ }
46
+ async #handleRefreshContent() {
47
+ // Only recreate texture when video source changes
48
+ await this.#handleDestroy();
49
+ // Texture will be recreated on next update when videoElement is available
25
50
  }
26
51
  async handle(type, context) {
27
52
  this.#context = context;
@@ -30,6 +55,9 @@ export class PixiVideoTextureHook {
30
55
  return;
31
56
  }
32
57
  this.componentElement = data;
33
- return await this.#handleUpdate();
58
+ const handler = this.#handlers[type];
59
+ if (handler) {
60
+ await handler();
61
+ }
34
62
  }
35
63
  }
@@ -48,7 +48,7 @@ export class ComponentDirector {
48
48
  return this.builder.getComponent();
49
49
  }
50
50
  constructImage() {
51
- this.builder.withImage().withTexture().withDisplayObject();
51
+ this.builder.withImage().withTexture().withSplitScreen();
52
52
  return this.builder.getComponent();
53
53
  }
54
54
  constructGif() {
@@ -0,0 +1,89 @@
1
+ {
2
+ "id": "01_basic_text",
3
+ "version": "2.0",
4
+ "name": "01. Basic Text",
5
+ "settings": {
6
+ "width": 1080,
7
+ "height": 1920,
8
+ "duration": 5,
9
+ "fps": 30,
10
+ "backgroundColor": "#000000"
11
+ },
12
+ "assets": [],
13
+ "layers": [
14
+ {
15
+ "id": "layer-bg",
16
+ "name": "Background Layer",
17
+ "order": 0,
18
+ "visible": true,
19
+ "muted": false,
20
+ "components": [
21
+ {
22
+ "id": "bg-shape",
23
+ "name": "Black Rectangle",
24
+ "type": "SHAPE",
25
+ "timeline": { "startAt": 0, "endAt": 5 },
26
+ "visible": true,
27
+ "order": 0,
28
+ "shape": { "type": "rectangle" },
29
+ "appearance": {
30
+ "x": 0,
31
+ "y": 0,
32
+ "width": 1080,
33
+ "height": 1920,
34
+ "opacity": 1,
35
+ "rotation": 0,
36
+ "scaleX": 1,
37
+ "scaleY": 1,
38
+ "background": "#000000"
39
+ },
40
+ "animations": { "enabled": true, "list": [] },
41
+ "effects": { "enabled": true, "map": {} }
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "id": "layer-text",
47
+ "name": "Text Layer",
48
+ "order": 1,
49
+ "visible": true,
50
+ "muted": false,
51
+ "components": [
52
+ {
53
+ "id": "center-text",
54
+ "name": "Center Text",
55
+ "type": "TEXT",
56
+ "text": "VISUALFRIES",
57
+ "timeline": { "startAt": 0, "endAt": 5 },
58
+ "visible": true,
59
+ "order": 0,
60
+ "appearance": {
61
+ "x": 0,
62
+ "y": 860,
63
+ "width": 1080,
64
+ "height": 200,
65
+ "opacity": 1,
66
+ "rotation": 0,
67
+ "scaleX": 1,
68
+ "scaleY": 1,
69
+ "horizontalAlign": "center",
70
+ "verticalAlign": "center",
71
+ "text": {
72
+ "fontFamily": "Inter",
73
+ "fontSize": 140,
74
+ "fontWeight": "900",
75
+ "lineHeight": { "value": 1, "unit": "em" },
76
+ "color": "#ffffff",
77
+ "textAlign": "center",
78
+ "textTransform": "uppercase"
79
+ }
80
+ },
81
+ "animations": { "enabled": true, "list": [] },
82
+ "effects": { "enabled": true, "map": {} }
83
+ }
84
+ ]
85
+ }
86
+ ],
87
+ "transitions": [],
88
+ "audioTracks": []
89
+ }
@@ -0,0 +1,110 @@
1
+ {
2
+ "id": "02_animated_text",
3
+ "version": "2.0",
4
+ "name": "02. Animated Text",
5
+ "settings": {
6
+ "width": 1080,
7
+ "height": 1920,
8
+ "duration": 5,
9
+ "fps": 30,
10
+ "backgroundColor": "#000000"
11
+ },
12
+ "assets": [],
13
+ "layers": [
14
+ {
15
+ "id": "layer-bg",
16
+ "name": "Background Layer",
17
+ "order": 0,
18
+ "visible": true,
19
+ "muted": false,
20
+ "components": [
21
+ {
22
+ "id": "bg-shape",
23
+ "name": "Black Rectangle",
24
+ "type": "SHAPE",
25
+ "timeline": { "startAt": 0, "endAt": 5 },
26
+ "visible": true,
27
+ "order": 0,
28
+ "shape": { "type": "rectangle" },
29
+ "appearance": {
30
+ "x": 0,
31
+ "y": 0,
32
+ "width": 1080,
33
+ "height": 1920,
34
+ "opacity": 1,
35
+ "rotation": 0,
36
+ "scaleX": 1,
37
+ "scaleY": 1,
38
+ "background": "#000000"
39
+ },
40
+ "animations": { "enabled": true, "list": [] },
41
+ "effects": { "enabled": true, "map": {} }
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "id": "layer-text",
47
+ "name": "Text Layer",
48
+ "order": 1,
49
+ "visible": true,
50
+ "muted": false,
51
+ "components": [
52
+ {
53
+ "id": "animated-text",
54
+ "name": "Animated Text",
55
+ "type": "TEXT",
56
+ "text": "ANIMATION",
57
+ "timeline": { "startAt": 0, "endAt": 5 },
58
+ "visible": true,
59
+ "order": 0,
60
+ "appearance": {
61
+ "x": 0,
62
+ "y": 860,
63
+ "width": 1080,
64
+ "height": 200,
65
+ "opacity": 1,
66
+ "rotation": 0,
67
+ "scaleX": 1,
68
+ "scaleY": 1,
69
+ "horizontalAlign": "center",
70
+ "verticalAlign": "center",
71
+ "text": {
72
+ "fontFamily": "Inter",
73
+ "fontSize": 140,
74
+ "fontWeight": "900",
75
+ "lineHeight": { "value": 1, "unit": "em" },
76
+ "color": "#ffffff",
77
+ "textAlign": "center",
78
+ "textTransform": "uppercase"
79
+ }
80
+ },
81
+ "animations": {
82
+ "enabled": true,
83
+ "list": [
84
+ {
85
+ "id": "intro-anim",
86
+ "name": "Scale & Fade",
87
+ "animation": {
88
+ "id": "intro-reveal",
89
+ "timeline": [
90
+ {
91
+ "tweens": [
92
+ {
93
+ "method": "from",
94
+ "vars": { "duration": 1, "scale": 0, "opacity": 0, "ease": "back.out(1.7)" }
95
+ }
96
+ ]
97
+ }
98
+ ]
99
+ }
100
+ }
101
+ ]
102
+ },
103
+ "effects": { "enabled": true, "map": {} }
104
+ }
105
+ ]
106
+ }
107
+ ],
108
+ "transitions": [],
109
+ "audioTracks": []
110
+ }
@@ -0,0 +1,94 @@
1
+ {
2
+ "id": "03_video_background",
3
+ "version": "2.0",
4
+ "name": "03. Video Background",
5
+ "settings": {
6
+ "width": 1080,
7
+ "height": 1920,
8
+ "duration": 5,
9
+ "fps": 30,
10
+ "backgroundColor": "#000000"
11
+ },
12
+ "assets": [],
13
+ "layers": [
14
+ {
15
+ "id": "layer-video",
16
+ "name": "Video Layer",
17
+ "order": 0,
18
+ "visible": true,
19
+ "muted": true,
20
+ "components": [
21
+ {
22
+ "id": "bunny-video",
23
+ "name": "Background Video",
24
+ "type": "VIDEO",
25
+ "timeline": { "startAt": 0, "endAt": 5 },
26
+ "visible": true,
27
+ "order": 0,
28
+ "source": {
29
+ "url": "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
30
+ "startAt": 0,
31
+ "endAt": 5
32
+ },
33
+ "appearance": {
34
+ "x": 0,
35
+ "y": 0,
36
+ "width": 1080,
37
+ "height": 1920,
38
+ "opacity": 1,
39
+ "rotation": 0,
40
+ "scaleX": 1,
41
+ "scaleY": 1
42
+ },
43
+ "volume": 0,
44
+ "muted": true,
45
+ "animations": { "enabled": true, "list": [] },
46
+ "effects": { "enabled": true, "map": {} }
47
+ }
48
+ ]
49
+ },
50
+ {
51
+ "id": "layer-overlay",
52
+ "name": "Overlay Layer",
53
+ "order": 1,
54
+ "visible": true,
55
+ "muted": false,
56
+ "components": [
57
+ {
58
+ "id": "overlay-text",
59
+ "name": "Overlay Text",
60
+ "type": "TEXT",
61
+ "text": "VIDEO BG",
62
+ "timeline": { "startAt": 0, "endAt": 5 },
63
+ "visible": true,
64
+ "order": 0,
65
+ "appearance": {
66
+ "x": 0,
67
+ "y": 860,
68
+ "width": 1080,
69
+ "height": 200,
70
+ "opacity": 1,
71
+ "rotation": 0,
72
+ "scaleX": 1,
73
+ "scaleY": 1,
74
+ "horizontalAlign": "center",
75
+ "verticalAlign": "center",
76
+ "text": {
77
+ "fontFamily": "Inter",
78
+ "fontSize": 140,
79
+ "fontWeight": "900",
80
+ "lineHeight": { "value": 1, "unit": "em" },
81
+ "color": "#ffffff",
82
+ "textAlign": "center",
83
+ "textTransform": "uppercase"
84
+ }
85
+ },
86
+ "animations": { "enabled": true, "list": [] },
87
+ "effects": { "enabled": true, "map": {} }
88
+ }
89
+ ]
90
+ }
91
+ ],
92
+ "transitions": [],
93
+ "audioTracks": []
94
+ }
@@ -0,0 +1,116 @@
1
+ {
2
+ "id": "04_real_subtitles",
3
+ "version": "2.0",
4
+ "name": "04. Real Subtitles",
5
+ "settings": {
6
+ "width": 1080,
7
+ "height": 1920,
8
+ "duration": 5,
9
+ "fps": 30,
10
+ "backgroundColor": "#000000"
11
+ },
12
+ "assets": [
13
+ {
14
+ "id": "bunny-asset",
15
+ "type": "VIDEO",
16
+ "url": "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
17
+ }
18
+ ],
19
+ "layers": [
20
+ {
21
+ "id": "layer-video",
22
+ "name": "Video Layer",
23
+ "order": 0,
24
+ "visible": true,
25
+ "muted": true,
26
+ "components": [
27
+ {
28
+ "id": "bunny-video",
29
+ "name": "Background Video",
30
+ "type": "VIDEO",
31
+ "timeline": { "startAt": 0, "endAt": 5 },
32
+ "visible": true,
33
+ "order": 0,
34
+ "source": {
35
+ "assetId": "bunny-asset",
36
+ "url": "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
37
+ "startAt": 0,
38
+ "endAt": 5
39
+ },
40
+ "appearance": {
41
+ "x": 0,
42
+ "y": 0,
43
+ "width": 1080,
44
+ "height": 1920,
45
+ "opacity": 1,
46
+ "rotation": 0,
47
+ "scaleX": 1,
48
+ "scaleY": 1
49
+ },
50
+ "volume": 0,
51
+ "muted": true,
52
+ "animations": { "enabled": true, "list": [] },
53
+ "effects": { "enabled": true, "map": {} }
54
+ }
55
+ ]
56
+ },
57
+ {
58
+ "id": "layer-subtitles",
59
+ "name": "Subtitle Layer",
60
+ "order": 1,
61
+ "visible": true,
62
+ "muted": false,
63
+ "components": [
64
+ {
65
+ "id": "real-subtitles",
66
+ "name": "Real Subtitles",
67
+ "type": "SUBTITLES",
68
+ "timeline": { "startAt": 0, "endAt": 5 },
69
+ "visible": true,
70
+ "order": 0,
71
+ "source": {
72
+ "assetId": "bunny-asset",
73
+ "startAt": 0,
74
+ "endAt": 5
75
+ },
76
+ "appearance": {
77
+ "x": 0,
78
+ "y": 1400,
79
+ "width": 1080,
80
+ "height": 300,
81
+ "opacity": 1,
82
+ "rotation": 0,
83
+ "scaleX": 1,
84
+ "scaleY": 1,
85
+ "text": {
86
+ "fontFamily": "Inter",
87
+ "fontSize": 80,
88
+ "fontWeight": "900",
89
+ "color": "#ffffff",
90
+ "textAlign": "center",
91
+ "textTransform": "uppercase",
92
+ "activeWord": {
93
+ "enabled": true,
94
+ "color": "#ffd700",
95
+ "scale": 1.3
96
+ }
97
+ }
98
+ },
99
+ "animations": {
100
+ "enabled": true,
101
+ "list": [
102
+ {
103
+ "id": "words-anim",
104
+ "name": "Words Highlight",
105
+ "animation": "cf-words-highlight"
106
+ }
107
+ ]
108
+ },
109
+ "effects": { "enabled": true, "map": {} }
110
+ }
111
+ ]
112
+ }
113
+ ],
114
+ "transitions": [],
115
+ "audioTracks": []
116
+ }