@video-editor/renderer 0.0.1-beta.1 → 0.0.1-beta.11

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/dist/index.js CHANGED
@@ -1,20 +1,20 @@
1
- import { createValidator as se, createResourceManager as ue } from "@video-editor/protocol";
2
- import { toRaw as fe, isRef as le, shallowRef as b, unref as q, ref as L, computed as de, effectScope as pe, watch as A } from "@vue/reactivity";
1
+ import { createValidator as le, createResourceManager as de } from "@video-editor/protocol";
2
+ import { toRaw as pe, isRef as ve, shallowRef as q, unref as L, ref as W, computed as he, effectScope as ye, watch as k } from "@vue/reactivity";
3
3
  import { MP4Clip as _ } from "@webav/av-cliper";
4
- import { file as ve } from "opfs-tools";
5
- import { Application as ye, Sprite as F, Texture as D, Graphics as Y, Container as he } from "pixi.js";
6
- async function me(t) {
7
- const n = new ye();
4
+ import { file as me } from "opfs-tools";
5
+ import { Application as we, Sprite as F, Texture as R, Graphics as B, Container as Te } from "pixi.js";
6
+ async function xe(t) {
7
+ const n = new we();
8
8
  return await n.init({ resizeTo: window, ...t }), n;
9
9
  }
10
- function we(t) {
10
+ function Ie(t) {
11
11
  const n = /* @__PURE__ */ new Set();
12
12
  for (const a of t.tracks)
13
13
  for (const i of a.children)
14
14
  i.url && n.add(i.url);
15
15
  return n;
16
16
  }
17
- function Te(t, n) {
17
+ function ge(t, n) {
18
18
  const a = [];
19
19
  return t.tracks.forEach((i, u) => {
20
20
  i.children.forEach((s, p) => {
@@ -22,189 +22,189 @@ function Te(t, n) {
22
22
  });
23
23
  }), a.sort((i, u) => i.trackIndex === u.trackIndex ? i.childIndex - u.childIndex : i.trackIndex - u.trackIndex);
24
24
  }
25
- function xe(t, n, a, i) {
26
- const u = Me(n);
25
+ function Me(t, n, a, i) {
26
+ const u = Ce(n);
27
27
  t instanceof F ? (t.anchor.set(0.5), t.width = a, t.height = i, t.x = a / 2, t.y = i / 2, t.texture.source?.addEventListener?.("error", () => {
28
- t.texture = D.from(Ie(a, i));
29
- }, { once: !0 })) : t instanceof Y && (t.clear(), t.rect(0, 0, a, i).fill({ color: z("url" in n && typeof n.url == "string" ? n.url : n.segmentType), alpha: B(n) ? u : 0.35 }), t.pivot.set(a / 2, i / 2), t.position.set(a / 2, i / 2)), t.alpha = u;
30
- const s = Ce(n);
28
+ t.texture = R.from(Se(a, i));
29
+ }, { once: !0 })) : t instanceof B && (t.clear(), t.rect(0, 0, a, i).fill({ color: G("url" in n && typeof n.url == "string" ? n.url : n.segmentType), alpha: Q(n) ? u : 0.35 }), t.pivot.set(a / 2, i / 2), t.position.set(a / 2, i / 2)), t.alpha = u;
30
+ const s = Re(n);
31
31
  if (s) {
32
- const [p, g] = s.position ?? [0, 0], [v, m] = s.scale ?? [1, 1], y = s.rotation?.[2] ?? 0;
33
- t.position.set(a / 2 + p * a / 2, i / 2 - g * i / 2), t.scale.set(v, m), t.rotation = y / 180 * Math.PI;
32
+ const [p, I] = s.position ?? [0, 0], [v, y] = s.scale ?? [1, 1], h = s.rotation?.[2] ?? 0;
33
+ t.position.set(a / 2 + p * a / 2, i / 2 - I * i / 2), t.scale.set(v, y), t.rotation = h / 180 * Math.PI;
34
34
  }
35
35
  }
36
36
  function H(t, n) {
37
- const a = new Y();
38
- return a.rect(0, 0, 10, 10).fill({ color: z(n ?? t), alpha: 1 }), a;
37
+ const a = new B();
38
+ return a.rect(0, 0, 10, 10).fill({ color: G(n ?? t), alpha: 1 }), a;
39
39
  }
40
- function Ie(t, n, a) {
40
+ function Se(t, n, a) {
41
41
  const i = `<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${n}"><rect width="100%" height="100%" fill="#0f172a" fill-opacity="0.8"/></svg>`;
42
42
  return `data:image/svg+xml;base64,${btoa(i)}`;
43
43
  }
44
- function z(t) {
44
+ function G(t) {
45
45
  let n = 0;
46
46
  for (let a = 0; a < t.length; a++)
47
47
  n = t.charCodeAt(a) + ((n << 5) - n);
48
48
  return n & 16777215;
49
49
  }
50
- function ge(t) {
50
+ function z(t) {
51
51
  const n = t.tracks.flatMap((a) => a.children.map((i) => i.endTime));
52
52
  return n.length ? Math.max(...n) : 0;
53
53
  }
54
- function W(t, n, a) {
54
+ function J(t, n, a) {
55
55
  return Math.min(Math.max(t, n), a);
56
56
  }
57
- function J(t) {
58
- const n = fe(t);
57
+ function Y(t) {
58
+ const n = pe(t);
59
59
  return JSON.parse(JSON.stringify(n));
60
60
  }
61
- function B(t) {
61
+ function Q(t) {
62
62
  return "opacity" in t;
63
63
  }
64
- function Me(t) {
65
- return B(t) && typeof t.opacity == "number" ? t.opacity : 1;
64
+ function Ce(t) {
65
+ return Q(t) && typeof t.opacity == "number" ? t.opacity : 1;
66
66
  }
67
- function Se(t) {
67
+ function De(t) {
68
68
  return "transform" in t;
69
69
  }
70
- function Ce(t) {
71
- if (Se(t))
70
+ function Re(t) {
71
+ if (De(t))
72
72
  return t.transform;
73
73
  }
74
- const De = "/video-editor-res";
75
- async function ke(t) {
76
- const n = se(), a = le(t.protocol) ? t.protocol : b(t.protocol), i = b(
77
- n.verify(J(q(a)))
78
- ), u = t.app ?? await me(t.appOptions), s = new he();
74
+ const Ae = "/video-editor-res";
75
+ async function $e(t) {
76
+ const n = le(), a = ve(t.protocol) ? t.protocol : q(t.protocol), i = q(
77
+ n.verify(Y(L(a)))
78
+ ), u = t.app ?? await xe(t.appOptions), s = new Te();
79
79
  u.stage.addChild(s);
80
- const p = ue({ dir: t.resourceDir }), g = /* @__PURE__ */ new Set(), v = /* @__PURE__ */ new Map(), m = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map(), f = L(0), x = L(!1), w = de(() => ge(i.value));
81
- let M, C = 0;
82
- async function G(e) {
83
- const { protocol: o, at: r, layer: c } = e, l = Te(o, r), I = e.app.renderer.width, d = e.app.renderer.height, h = [];
84
- for (const { segment: T } of l) {
85
- const P = await e.getDisplay(T);
86
- P && (xe(P, T, I, d), ne(T) && await re(T, r), h.push(P));
80
+ const p = de({ dir: t.resourceDir }), I = /* @__PURE__ */ new Set(), v = /* @__PURE__ */ new Map(), y = /* @__PURE__ */ new Map(), h = /* @__PURE__ */ new Map(), f = W(0), T = W(!1), m = he(() => z(i.value));
81
+ let g, C = 0;
82
+ async function j(e) {
83
+ const { protocol: o, at: r, layer: c } = e, l = ie(o, r), x = ge(o, l), d = e.app.renderer.width, w = e.app.renderer.height, M = [];
84
+ for (const { segment: D } of x) {
85
+ const P = await e.getDisplay(D);
86
+ P && (Me(P, D, d, w), ae(D) && await oe(D, l), M.push(P));
87
87
  }
88
88
  c.removeChildren();
89
- const S = h.filter(Boolean);
89
+ const S = M.filter(Boolean);
90
90
  S.length && c.addChild(...S), e.app.render();
91
91
  }
92
- const O = Re(() => G({
92
+ const O = Ee(() => j({
93
93
  app: u,
94
94
  layer: s,
95
95
  protocol: i.value,
96
96
  at: f.value,
97
- getDisplay: X
98
- })), k = pe();
99
- k.run(() => {
100
- A(
101
- () => q(a),
97
+ getDisplay: ee
98
+ })), N = ye();
99
+ N.run(() => {
100
+ k(
101
+ () => L(a),
102
102
  (e) => {
103
103
  try {
104
- i.value = n.verify(J(e));
104
+ i.value = n.verify(Y(e));
105
105
  } catch (o) {
106
106
  console.error("[renderer] invalid protocol update", o);
107
107
  return;
108
108
  }
109
- N(), Q(i.value), j(i.value), R(), O();
109
+ $(), K(i.value), X(i.value), A(), O();
110
110
  },
111
111
  { deep: !0, immediate: !0 }
112
- ), A(f, () => {
113
- R(), O();
114
- }), A(w, () => R());
112
+ ), k(f, () => {
113
+ A(), O();
114
+ }), k(m, () => A());
115
115
  });
116
- function R() {
117
- const e = w.value;
116
+ function A() {
117
+ const e = m.value;
118
118
  e <= 0 ? f.value = 0 : f.value > e ? f.value = e : f.value < 0 && (f.value = 0);
119
119
  }
120
- function Q(e) {
121
- for (const o of we(e))
122
- g.has(o) || (g.add(o), p.add(o).catch(() => {
120
+ function K(e) {
121
+ for (const o of Ie(e))
122
+ I.has(o) || (I.add(o), p.add(o).catch(() => {
123
123
  }));
124
124
  }
125
- function j(e) {
125
+ function X(e) {
126
126
  const o = new Set(e.tracks.flatMap((r) => r.children.map((c) => c.id)));
127
127
  for (const [r, c] of v)
128
128
  o.has(r) || (c.destroy(), v.delete(r));
129
- for (const [r, c] of y)
130
- o.has(r) || (c.clip.destroy(), y.delete(r));
129
+ for (const [r, c] of h)
130
+ o.has(r) || (c.clip.destroy(), h.delete(r));
131
131
  }
132
- function N() {
132
+ function $() {
133
133
  s.removeChildren();
134
134
  for (const e of v.values())
135
135
  e.destroy();
136
- v.clear(), m.clear();
137
- for (const { clip: e } of y.values())
136
+ v.clear(), y.clear();
137
+ for (const { clip: e } of h.values())
138
138
  e.destroy();
139
- y.clear();
139
+ h.clear();
140
140
  }
141
- function $() {
142
- x.value || (x.value = !0, C = performance.now(), M = requestAnimationFrame(U));
141
+ function U() {
142
+ T.value || (T.value = !0, C = performance.now(), g = requestAnimationFrame(V));
143
143
  }
144
144
  function E() {
145
- x.value = !1, M !== void 0 && cancelAnimationFrame(M), M = void 0;
145
+ T.value = !1, g !== void 0 && cancelAnimationFrame(g), g = void 0;
146
146
  }
147
- function U() {
148
- V(), x.value && (M = requestAnimationFrame(U));
147
+ function V() {
148
+ b(), T.value && (g = requestAnimationFrame(V));
149
149
  }
150
- function V(e) {
151
- if (!x.value && e === void 0)
150
+ function b(e) {
151
+ if (!T.value && e === void 0)
152
152
  return;
153
153
  const o = performance.now(), r = e ?? (C ? o - C : 0);
154
- C = o, r !== 0 && (f.value = W(
154
+ C = o, r !== 0 && (f.value = J(
155
155
  f.value + r,
156
156
  0,
157
- w.value || Number.POSITIVE_INFINITY
158
- ), w.value > 0 && f.value >= w.value && E());
157
+ m.value || Number.POSITIVE_INFINITY
158
+ ), m.value > 0 && f.value >= m.value && E());
159
159
  }
160
- function K(e) {
161
- f.value = W(e, 0, w.value || Number.POSITIVE_INFINITY);
160
+ function Z(e) {
161
+ f.value = J(e, 0, m.value || Number.POSITIVE_INFINITY);
162
162
  }
163
- async function X(e) {
163
+ async function ee(e) {
164
164
  const o = v.get(e.id);
165
165
  if (o)
166
166
  return o;
167
- const r = m.get(e.id);
167
+ const r = y.get(e.id);
168
168
  if (r)
169
169
  return r;
170
- const c = Z(e);
171
- m.set(e.id, c);
170
+ const c = te(e);
171
+ y.set(e.id, c);
172
172
  const l = await c;
173
- return l && v.set(e.id, l), m.delete(e.id), l;
173
+ return l && v.set(e.id, l), y.delete(e.id), l;
174
174
  }
175
- async function Z(e) {
176
- if (e.segmentType === "frames" || e.segmentType === "image") {
175
+ async function te(e) {
176
+ if (e.segmentType === "frames" || e.segmentType === "sticker") {
177
177
  if (!e.url)
178
178
  return H(e.segmentType);
179
179
  if ("type" in e && e.type === "video") {
180
- const r = await te(e);
180
+ const r = await ne(e);
181
181
  if (r)
182
182
  return r;
183
183
  }
184
- const o = await ee(e.url);
184
+ const o = await re(e.url);
185
185
  return o ? new F(o) : H(e.segmentType, e.url);
186
186
  }
187
187
  e.segmentType !== "text" && (e.segmentType === "effect" || e.segmentType);
188
188
  }
189
- async function ee(e) {
189
+ async function re(e) {
190
190
  const o = e.startsWith("data:"), r = /^https?:\/\//.test(e);
191
191
  if (!o && !r)
192
192
  try {
193
193
  await p.add(e);
194
194
  const c = await p.get(e);
195
195
  if (c instanceof HTMLImageElement)
196
- return D.from(c);
196
+ return R.from(c);
197
197
  } catch {
198
198
  }
199
- return await ae(e);
199
+ return await se(e);
200
200
  }
201
- async function te(e) {
202
- const o = y.get(e.id);
201
+ async function ne(e) {
202
+ const o = h.get(e.id);
203
203
  if (o)
204
204
  return o.sprite;
205
205
  let r;
206
206
  try {
207
- await p.add(e.url), r = await oe(e.url);
207
+ await p.add(e.url), r = await ce(e.url);
208
208
  } catch {
209
209
  r = void 0;
210
210
  }
@@ -213,10 +213,10 @@ async function ke(t) {
213
213
  if (r)
214
214
  c = new _(r);
215
215
  else {
216
- const T = await fetch(e.url);
217
- if (!T.body)
216
+ const S = await fetch(e.url);
217
+ if (!S.body)
218
218
  return;
219
- c = new _(T.body);
219
+ c = new _(S.body);
220
220
  }
221
221
  } catch {
222
222
  return;
@@ -227,46 +227,55 @@ async function ke(t) {
227
227
  c.destroy();
228
228
  return;
229
229
  }
230
- const { width: l, height: I } = c.meta, d = document.createElement("canvas");
231
- d.width = l || 1, d.height = I || 1;
232
- const h = D.from(d), S = new F(h);
233
- return y.set(e.id, { clip: c, canvas: d, texture: h, sprite: S, meta: { width: l, height: I } }), S;
230
+ const { width: l, height: x } = c.meta, d = document.createElement("canvas");
231
+ d.width = l || 1, d.height = x || 1;
232
+ const w = R.from(d), M = new F(w);
233
+ return h.set(e.id, { clip: c, canvas: d, texture: w, sprite: M, meta: { width: l, height: x } }), M;
234
234
  }
235
- async function re(e, o) {
236
- const r = y.get(e.id);
235
+ async function oe(e, o) {
236
+ const r = h.get(e.id);
237
237
  if (r)
238
238
  try {
239
- const c = e.fromTime ?? 0, l = Math.max(0, o - e.startTime + c), I = Math.floor(l * 1e3), d = await r.clip.tick(I);
239
+ const c = e.fromTime ?? 0, l = Math.max(0, o - e.startTime + c), x = Math.floor(l * 1e3), d = await r.clip.tick(x);
240
240
  if (d.video) {
241
- const h = r.canvas.getContext("2d");
242
- h && (h.drawImage(d.video, 0, 0, r.canvas.width, r.canvas.height), ie(r.texture)), d.video.close();
241
+ const w = r.canvas.getContext("2d");
242
+ w && (w.drawImage(d.video, 0, 0, r.canvas.width, r.canvas.height), ue(r.texture)), d.video.close();
243
243
  }
244
244
  } catch (c) {
245
245
  console.warn("[renderer] update video frame failed", c);
246
246
  }
247
247
  }
248
- function ne(e) {
248
+ function ae(e) {
249
249
  return e.segmentType === "frames" && e.type === "video" && typeof e.url == "string";
250
250
  }
251
- async function oe(e) {
252
- const o = t.resourceDir ?? De;
251
+ function ie(e, o) {
252
+ const r = z(e);
253
+ if (r <= 0)
254
+ return 0;
255
+ if (o < r)
256
+ return o;
257
+ const c = Math.max(1e3 / Math.max(e.fps || 30, 1), 1);
258
+ return Math.max(r - c, 0);
259
+ }
260
+ async function ce(e) {
261
+ const o = t.resourceDir ?? Ae;
253
262
  try {
254
- const r = ve(`${o}/${e}`);
263
+ const r = me(`${o}/${e}`);
255
264
  if (await r.exists())
256
265
  return r;
257
266
  } catch {
258
267
  return;
259
268
  }
260
269
  }
261
- function ae(e) {
270
+ function se(e) {
262
271
  return new Promise((o) => {
263
272
  const r = new Image();
264
- r.crossOrigin = "anonymous", r.onload = () => o(D.from(r)), r.onerror = () => {
273
+ r.crossOrigin = "anonymous", r.onload = () => o(R.from(r)), r.onerror = () => {
265
274
  console.warn("[renderer] failed to load image", e), o(void 0);
266
275
  }, r.src = e;
267
276
  });
268
277
  }
269
- function ie(e) {
278
+ function ue(e) {
270
279
  const o = e.source;
271
280
  if ("update" in o && typeof o.update == "function") {
272
281
  o.update();
@@ -274,23 +283,23 @@ async function ke(t) {
274
283
  }
275
284
  typeof e.update == "function" && e.update();
276
285
  }
277
- function ce() {
278
- E(), k.stop(), N(), s.destroy({ children: !0 }), v.clear(), m.clear(), g.clear(), t.app || u.destroy();
286
+ function fe() {
287
+ E(), N.stop(), $(), s.destroy({ children: !0 }), v.clear(), y.clear(), I.clear(), t.app || u.destroy();
279
288
  }
280
- return t.autoPlay && $(), {
289
+ return t.autoPlay && U(), {
281
290
  app: u,
282
291
  layer: s,
283
292
  currentTime: f,
284
- duration: w,
285
- isPlaying: x,
286
- play: $,
293
+ duration: m,
294
+ isPlaying: T,
295
+ play: U,
287
296
  pause: E,
288
- tick: V,
289
- seek: K,
290
- destroy: ce
297
+ tick: b,
298
+ seek: Z,
299
+ destroy: fe
291
300
  };
292
301
  }
293
- function Re(t) {
302
+ function Ee(t) {
294
303
  let n = !1, a = !1;
295
304
  return async () => {
296
305
  if (a) {
@@ -305,6 +314,6 @@ function Re(t) {
305
314
  };
306
315
  }
307
316
  export {
308
- ke as createRenderer
317
+ $e as createRenderer
309
318
  };
310
319
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/2d/index.ts","../src/helpers.ts","../src/index.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\nimport type { ApplicationOptions } from 'pixi.js'\nimport { Application } from 'pixi.js'\n\ndeclare global {\n\n // eslint-disable-next-line vars-on-top\n var __PIXI_APP__: Application\n}\n\nexport async function createApp(opts?: Partial<ApplicationOptions>) {\n const app = new Application()\n\n await app.init({ resizeTo: window, ...opts })\n\n if (import.meta.env.DEV) {\n globalThis.__PIXI_APP__ = app\n }\n\n return app\n}\n","import type { ITransform, IVideoProtocol, SegmentUnion } from '@video-editor/shared'\nimport type { PixiDisplayObject } from './types'\nimport { toRaw } from '@vue/reactivity'\nimport { Graphics, Sprite, Texture } from 'pixi.js'\n\nexport function collectResourceUrls(protocol: IVideoProtocol) {\n const urls = new Set<string>()\n for (const track of protocol.tracks) {\n for (const segment of track.children) {\n if (segment.url)\n urls.add(segment.url)\n }\n }\n return urls\n}\n\nexport function collectActiveSegments(protocol: IVideoProtocol, at: number) {\n const active: { segment: SegmentUnion, trackIndex: number, childIndex: number }[] = []\n protocol.tracks.forEach((track, trackIndex) => {\n track.children.forEach((segment, childIndex) => {\n if (segment.startTime <= at && at < segment.endTime)\n active.push({ segment, trackIndex, childIndex })\n })\n })\n\n return active.sort((a, b) => {\n if (a.trackIndex === b.trackIndex)\n return a.childIndex - b.childIndex\n return a.trackIndex - b.trackIndex\n })\n}\n\nexport function applyDisplayProps(display: PixiDisplayObject, segment: SegmentUnion, width: number, height: number) {\n const opacity = readOpacity(segment)\n // size\n if (display instanceof Sprite) {\n display.anchor.set(0.5)\n display.width = width\n display.height = height\n display.x = width / 2\n display.y = height / 2\n const src = display.texture.source as { addEventListener?: (type: string, cb: () => void, opts?: AddEventListenerOptions) => void } | undefined\n src?.addEventListener?.('error', () => {\n // fallback to a colored rect if texture failed\n display.texture = Texture.from(placeholderTexture(width, height))\n }, { once: true })\n }\n else if (display instanceof Graphics) {\n display.clear()\n display\n .rect(0, 0, width, height)\n .fill({ color: stringToColor('url' in segment && typeof segment.url === 'string' ? segment.url : segment.segmentType), alpha: hasOpacity(segment) ? opacity : 0.35 })\n display.pivot.set(width / 2, height / 2)\n display.position.set(width / 2, height / 2)\n }\n\n display.alpha = opacity\n\n // simple 2D transform\n const transform = readTransform(segment)\n if (transform) {\n const [px, py] = transform.position ?? [0, 0]\n const [sx, sy] = transform.scale ?? [1, 1]\n const rotation = transform.rotation?.[2] ?? 0\n\n display.position.set(width / 2 + (px * width) / 2, height / 2 - (py * height) / 2)\n display.scale.set(sx, sy)\n display.rotation = (rotation / 180) * Math.PI\n }\n}\n\nexport function placeholder(key: string, url?: string) {\n const g = new Graphics()\n g.rect(0, 0, 10, 10).fill({ color: stringToColor(url ?? key), alpha: 1 })\n return g\n}\n\nexport function placeholderTexture(width: number, height: number, color?: string) {\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\"><rect width=\"100%\" height=\"100%\" fill=\"${color ?? '#0f172a'}\" fill-opacity=\"0.8\"/></svg>`\n return `data:image/svg+xml;base64,${btoa(svg)}`\n}\n\nexport function stringToColor(key: string) {\n let hash = 0\n for (let i = 0; i < key.length; i++)\n hash = key.charCodeAt(i) + ((hash << 5) - hash)\n return hash & 0x00FFFFFF\n}\n\nexport function computeDuration(protocol: IVideoProtocol) {\n const endTimes = protocol.tracks.flatMap(track => track.children.map(seg => seg.endTime))\n return endTimes.length ? Math.max(...endTimes) : 0\n}\n\nexport function clamp(num: number, min: number, max: number) {\n return Math.min(Math.max(num, min), max)\n}\n\nexport function cloneProtocol(protocol: IVideoProtocol) {\n const raw = toRaw(protocol) as IVideoProtocol\n // use JSON clone to avoid structuredClone errors on proxies (e.g., Vue reactive)\n return JSON.parse(JSON.stringify(raw)) as IVideoProtocol\n}\n\nfunction hasOpacity(segment: SegmentUnion): segment is SegmentUnion & { opacity?: number } {\n return 'opacity' in segment\n}\n\nfunction readOpacity(segment: SegmentUnion) {\n if (hasOpacity(segment) && typeof segment.opacity === 'number')\n return segment.opacity\n return 1\n}\n\nfunction hasTransform(segment: SegmentUnion): segment is SegmentUnion & { transform?: ITransform } {\n return 'transform' in segment\n}\n\nfunction readTransform(segment: SegmentUnion) {\n if (hasTransform(segment))\n return segment.transform\n return undefined\n}\n","import type { IVideoFramesSegment, IVideoProtocol, SegmentUnion } from '@video-editor/shared'\nimport type { ComputedRef, Ref, ShallowRef } from '@vue/reactivity'\nimport type { Application, ApplicationOptions } from 'pixi.js'\nimport type { MaybeRef, PixiDisplayObject } from './types'\nimport { createResourceManager, createValidator } from '@video-editor/protocol'\nimport {\n computed,\n effectScope,\n isRef,\n ref,\n shallowRef,\n unref,\n watch,\n} from '@vue/reactivity'\nimport { MP4Clip } from '@webav/av-cliper'\nimport { file as opfsFile } from 'opfs-tools'\nimport { Container, Sprite, Texture } from 'pixi.js'\nimport { createApp as create2dApp } from './2d'\nimport {\n applyDisplayProps,\n clamp,\n cloneProtocol,\n collectActiveSegments,\n collectResourceUrls,\n computeDuration,\n placeholder,\n} from './helpers'\n\nconst DEFAULT_RES_DIR = '/video-editor-res'\n\nexport interface RendererOptions {\n protocol: MaybeRef<IVideoProtocol>\n app?: Application\n appOptions?: Partial<ApplicationOptions>\n resourceDir?: string\n autoPlay?: boolean\n}\n\nexport interface Renderer {\n app: Application\n layer: Container\n currentTime: Ref<number>\n duration: ComputedRef<number>\n isPlaying: Ref<boolean>\n play: () => void\n pause: () => void\n tick: (deltaMs?: number) => void\n seek: (time: number) => void\n destroy: () => void\n}\n\n/**\n * Create a renderer that reacts to protocol updates and drives playback state.\n * - Pass a reactive `protocol` (Ref/readonly/normal object)\n * - Call `play/pause/seek/tick` to drive the timeline\n * - Rendering updates when `protocol` or `currentTime` changes\n */\nexport async function createRenderer(opts: RendererOptions): Promise<Renderer> {\n const validator = createValidator()\n const protocolInput: Ref<IVideoProtocol> | ShallowRef<IVideoProtocol>\n = isRef(opts.protocol) ? opts.protocol : shallowRef(opts.protocol)\n const validatedProtocol: ShallowRef<IVideoProtocol> = shallowRef(\n validator.verify(cloneProtocol(unref(protocolInput))),\n )\n\n const app = opts.app ?? await create2dApp(opts.appOptions)\n const layer = new Container()\n app.stage.addChild(layer)\n\n const resourceManager = createResourceManager({ dir: opts.resourceDir })\n const resourceWarmUp = new Set<string>()\n const displayCache = new Map<string, PixiDisplayObject>()\n const displayLoading = new Map<string, Promise<PixiDisplayObject | undefined>>()\n const videoEntries = new Map<string, {\n clip: MP4Clip\n canvas: HTMLCanvasElement\n texture: Texture\n sprite: Sprite\n meta?: { width: number, height: number }\n }>()\n\n const currentTime = ref(0)\n const isPlaying = ref(false)\n const duration = computed(() => computeDuration(validatedProtocol.value))\n\n let rafId: number | undefined\n let lastTickAt = 0\n\n interface RenderTask {\n app: Application\n layer: Container\n protocol: IVideoProtocol\n at: number\n getDisplay: (segment: SegmentUnion) => Promise<PixiDisplayObject | undefined>\n }\n\n async function renderScene(task: RenderTask) {\n const { protocol, at, layer } = task\n const active = collectActiveSegments(protocol, at)\n const stageWidth = task.app.renderer.width\n const stageHeight = task.app.renderer.height\n\n const renders: (PixiDisplayObject | undefined)[] = []\n for (const { segment } of active) {\n const display = await task.getDisplay(segment)\n if (!display)\n continue\n applyDisplayProps(display, segment, stageWidth, stageHeight)\n if (isVideoSegment(segment))\n await updateVideoFrame(segment, at)\n renders.push(display)\n }\n\n layer.removeChildren()\n const cleaned = renders.filter(Boolean) as PixiDisplayObject[]\n if (cleaned.length)\n layer.addChild(...cleaned)\n task.app.render()\n }\n\n const queueRender = createRenderQueue(() => renderScene({\n app,\n layer,\n protocol: validatedProtocol.value,\n at: currentTime.value,\n getDisplay: getDisplayForSegment,\n }))\n\n const scope = effectScope()\n scope.run(() => {\n // Sync external protocol mutations into a verified snapshot the renderer can rely on.\n watch(\n () => unref(protocolInput),\n (protocol) => {\n try {\n validatedProtocol.value = validator.verify(cloneProtocol(protocol))\n }\n catch (err) {\n console.error('[renderer] invalid protocol update', err)\n return\n }\n clearDisplays()\n warmUpResources(validatedProtocol.value)\n cleanupCache(validatedProtocol.value)\n clampCurrentTime()\n queueRender()\n },\n { deep: true, immediate: true },\n )\n\n // React to time changes.\n watch(currentTime, () => {\n clampCurrentTime()\n queueRender()\n })\n\n // Keep duration/currentTime in sync with protocol updates.\n watch(duration, () => clampCurrentTime())\n })\n\n function clampCurrentTime() {\n const nextDuration = duration.value\n if (nextDuration <= 0)\n currentTime.value = 0\n else if (currentTime.value > nextDuration)\n currentTime.value = nextDuration\n else if (currentTime.value < 0)\n currentTime.value = 0\n }\n\n function warmUpResources(protocol: IVideoProtocol) {\n for (const url of collectResourceUrls(protocol)) {\n if (resourceWarmUp.has(url))\n continue\n\n resourceWarmUp.add(url)\n resourceManager.add(url).catch(() => {\n // noop – render will fall back to Texture.from(url)\n })\n }\n }\n\n function cleanupCache(protocol: IVideoProtocol) {\n const ids = new Set(protocol.tracks.flatMap(track => track.children.map(seg => seg.id)))\n for (const [id, display] of displayCache) {\n if (ids.has(id))\n continue\n display.destroy()\n displayCache.delete(id)\n }\n for (const [id, entry] of videoEntries) {\n if (ids.has(id))\n continue\n entry.clip.destroy()\n videoEntries.delete(id)\n }\n }\n\n function clearDisplays() {\n layer.removeChildren()\n for (const display of displayCache.values()) {\n display.destroy()\n }\n displayCache.clear()\n displayLoading.clear()\n for (const { clip } of videoEntries.values())\n clip.destroy()\n videoEntries.clear()\n }\n\n function play() {\n if (isPlaying.value)\n return\n isPlaying.value = true\n lastTickAt = performance.now()\n rafId = requestAnimationFrame(loop)\n }\n\n function pause() {\n isPlaying.value = false\n if (rafId !== undefined)\n cancelAnimationFrame(rafId)\n rafId = undefined\n }\n\n function loop() {\n tick()\n if (isPlaying.value)\n rafId = requestAnimationFrame(loop)\n }\n\n function tick(deltaMs?: number) {\n if (!isPlaying.value && deltaMs === undefined)\n return\n\n const now = performance.now()\n const delta = deltaMs ?? (lastTickAt ? now - lastTickAt : 0)\n lastTickAt = now\n\n if (delta === 0)\n return\n\n currentTime.value = clamp(\n currentTime.value + delta,\n 0,\n duration.value || Number.POSITIVE_INFINITY,\n )\n\n if (duration.value > 0 && currentTime.value >= duration.value)\n pause()\n\n // render happens via watch on currentTime\n }\n\n function seek(time: number) {\n currentTime.value = clamp(time, 0, duration.value || Number.POSITIVE_INFINITY)\n }\n\n async function getDisplayForSegment(segment: SegmentUnion) {\n const cached = displayCache.get(segment.id)\n if (cached)\n return cached\n\n const loading = displayLoading.get(segment.id)\n if (loading)\n return loading\n\n const promise = loadDisplay(segment)\n displayLoading.set(segment.id, promise)\n\n const display = await promise\n if (display)\n displayCache.set(segment.id, display)\n\n displayLoading.delete(segment.id)\n return display\n }\n\n async function loadDisplay(segment: SegmentUnion): Promise<PixiDisplayObject | undefined> {\n // prioritize static resources via protocol resource manager\n if (segment.segmentType === 'frames' || segment.segmentType === 'image') {\n if (!segment.url)\n return placeholder(segment.segmentType)\n\n if ('type' in segment && segment.type === 'video') {\n const sprite = await loadVideoSprite(segment)\n if (sprite)\n return sprite\n }\n\n const texture = await loadTexture(segment.url)\n if (texture)\n return new Sprite(texture)\n return placeholder(segment.segmentType, segment.url)\n }\n\n if (segment.segmentType === 'text')\n return undefined\n\n if (segment.segmentType === 'effect' || segment.segmentType === 'filter')\n return undefined\n\n // audio segments do not render visuals\n return undefined\n }\n\n async function loadTexture(url: string) {\n const isDataUrl = url.startsWith('data:')\n const isHttp = /^https?:\\/\\//.test(url)\n\n if (!isDataUrl && !isHttp) {\n try {\n await resourceManager.add(url)\n const res = await resourceManager.get(url)\n if (res instanceof HTMLImageElement)\n return Texture.from(res)\n }\n catch {\n // fall through to direct image load\n }\n }\n\n // load image directly to avoid invalid path issues with http/data URLs\n return await loadImageTexture(url)\n }\n\n async function loadVideoSprite(segment: SegmentUnion & { type: 'video', url: string }): Promise<Sprite | undefined> {\n const existing = videoEntries.get(segment.id)\n if (existing)\n return existing.sprite\n\n let file\n try {\n await resourceManager.add(segment.url)\n file = await getOpfsFile(segment.url)\n }\n catch {\n file = undefined\n }\n let clip: MP4Clip\n try {\n if (file) {\n clip = new MP4Clip(file)\n }\n else {\n const res = await fetch(segment.url)\n if (!res.body)\n return undefined\n clip = new MP4Clip(res.body)\n }\n }\n catch {\n return undefined\n }\n\n try {\n await clip.ready\n }\n catch {\n clip.destroy()\n return undefined\n }\n\n const { width, height } = clip.meta\n const canvas = document.createElement('canvas')\n canvas.width = width || 1\n canvas.height = height || 1\n const texture = Texture.from(canvas)\n const sprite = new Sprite(texture)\n\n videoEntries.set(segment.id, { clip, canvas, texture, sprite, meta: { width, height } })\n return sprite\n }\n\n async function updateVideoFrame(segment: IVideoFramesSegment, at: number) {\n const entry = videoEntries.get(segment.id)\n if (!entry)\n return\n\n try {\n const offsetMs = segment.fromTime ?? 0\n const relativeMs = Math.max(0, at - segment.startTime + offsetMs)\n const relativeUs = Math.floor(relativeMs * 1000)\n const res = await entry.clip.tick(relativeUs)\n if (res.video) {\n const ctx = entry.canvas.getContext('2d')\n if (ctx) {\n ctx.drawImage(res.video, 0, 0, entry.canvas.width, entry.canvas.height)\n refreshCanvasTexture(entry.texture)\n }\n res.video.close()\n }\n }\n catch (err) {\n console.warn('[renderer] update video frame failed', err)\n }\n }\n\n function isVideoSegment(segment: SegmentUnion): segment is IVideoFramesSegment {\n return segment.segmentType === 'frames'\n && segment.type === 'video'\n && typeof segment.url === 'string'\n }\n\n async function getOpfsFile(url: string) {\n const dir = opts.resourceDir ?? DEFAULT_RES_DIR\n try {\n const file = opfsFile(`${dir}/${url}`)\n if (await file.exists())\n return file\n }\n catch {\n return undefined\n }\n return undefined\n }\n\n function loadImageTexture(url: string): Promise<Texture | undefined> {\n return new Promise((resolve) => {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.onload = () => resolve(Texture.from(img))\n img.onerror = () => {\n console.warn('[renderer] failed to load image', url)\n resolve(undefined)\n }\n img.src = url\n })\n }\n\n function refreshCanvasTexture(texture: Texture) {\n const source = texture.source\n if ('update' in source && typeof source.update === 'function') {\n source.update()\n return\n }\n\n if (typeof texture.update === 'function')\n texture.update()\n }\n\n function destroy() {\n pause()\n scope.stop()\n clearDisplays()\n layer.destroy({ children: true })\n displayCache.clear()\n displayLoading.clear()\n resourceWarmUp.clear()\n if (!opts.app)\n app.destroy()\n }\n\n if (opts.autoPlay)\n play()\n\n return {\n app,\n layer,\n currentTime,\n duration,\n isPlaying,\n play,\n pause,\n tick,\n seek,\n destroy,\n }\n}\n\nfunction createRenderQueue(job: () => Promise<void> | void) {\n let queued = false\n let running = false\n\n const run = async () => {\n if (running) {\n queued = true\n return\n }\n running = true\n do {\n queued = false\n await job()\n } while (queued)\n running = false\n }\n\n return run\n}\n"],"names":["createApp","opts","app","Application","collectResourceUrls","protocol","urls","track","segment","collectActiveSegments","at","active","trackIndex","childIndex","a","b","applyDisplayProps","display","width","height","opacity","readOpacity","Sprite","Texture","placeholderTexture","Graphics","stringToColor","hasOpacity","transform","readTransform","px","py","sx","sy","rotation","placeholder","key","url","g","color","svg","hash","i","computeDuration","endTimes","seg","clamp","num","min","max","cloneProtocol","raw","toRaw","hasTransform","DEFAULT_RES_DIR","createRenderer","validator","createValidator","protocolInput","isRef","shallowRef","validatedProtocol","unref","create2dApp","layer","Container","resourceManager","createResourceManager","resourceWarmUp","displayCache","displayLoading","videoEntries","currentTime","ref","isPlaying","duration","computed","rafId","lastTickAt","renderScene","task","stageWidth","stageHeight","renders","isVideoSegment","updateVideoFrame","cleaned","queueRender","createRenderQueue","getDisplayForSegment","scope","effectScope","watch","err","clearDisplays","warmUpResources","cleanupCache","clampCurrentTime","nextDuration","ids","id","entry","clip","play","loop","pause","tick","deltaMs","now","delta","seek","time","cached","loading","promise","loadDisplay","sprite","loadVideoSprite","texture","loadTexture","isDataUrl","isHttp","res","loadImageTexture","existing","file","getOpfsFile","MP4Clip","canvas","offsetMs","relativeMs","relativeUs","ctx","refreshCanvasTexture","dir","opfsFile","resolve","img","source","destroy","job","queued","running"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/2d/index.ts","../src/helpers.ts","../src/index.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\nimport type { ApplicationOptions } from 'pixi.js'\nimport { Application } from 'pixi.js'\n\ndeclare global {\n\n // eslint-disable-next-line vars-on-top\n var __PIXI_APP__: Application\n}\n\nexport async function createApp(opts?: Partial<ApplicationOptions>) {\n const app = new Application()\n\n await app.init({ resizeTo: window, ...opts })\n\n if (import.meta.env.DEV) {\n globalThis.__PIXI_APP__ = app\n }\n\n return app\n}\n","import type { ITransform, IVideoProtocol, SegmentUnion } from '@video-editor/shared'\nimport type { PixiDisplayObject } from './types'\nimport { toRaw } from '@vue/reactivity'\nimport { Graphics, Sprite, Texture } from 'pixi.js'\n\nexport function collectResourceUrls(protocol: IVideoProtocol) {\n const urls = new Set<string>()\n for (const track of protocol.tracks) {\n for (const segment of track.children) {\n if (segment.url)\n urls.add(segment.url)\n }\n }\n return urls\n}\n\nexport function collectActiveSegments(protocol: IVideoProtocol, at: number) {\n const active: { segment: SegmentUnion, trackIndex: number, childIndex: number }[] = []\n protocol.tracks.forEach((track, trackIndex) => {\n track.children.forEach((segment, childIndex) => {\n if (segment.startTime <= at && at < segment.endTime)\n active.push({ segment, trackIndex, childIndex })\n })\n })\n\n return active.sort((a, b) => {\n if (a.trackIndex === b.trackIndex)\n return a.childIndex - b.childIndex\n return a.trackIndex - b.trackIndex\n })\n}\n\nexport function applyDisplayProps(display: PixiDisplayObject, segment: SegmentUnion, width: number, height: number) {\n const opacity = readOpacity(segment)\n // size\n if (display instanceof Sprite) {\n display.anchor.set(0.5)\n display.width = width\n display.height = height\n display.x = width / 2\n display.y = height / 2\n const src = display.texture.source as { addEventListener?: (type: string, cb: () => void, opts?: AddEventListenerOptions) => void } | undefined\n src?.addEventListener?.('error', () => {\n // fallback to a colored rect if texture failed\n display.texture = Texture.from(placeholderTexture(width, height))\n }, { once: true })\n }\n else if (display instanceof Graphics) {\n display.clear()\n display\n .rect(0, 0, width, height)\n .fill({ color: stringToColor('url' in segment && typeof segment.url === 'string' ? segment.url : segment.segmentType), alpha: hasOpacity(segment) ? opacity : 0.35 })\n display.pivot.set(width / 2, height / 2)\n display.position.set(width / 2, height / 2)\n }\n\n display.alpha = opacity\n\n // simple 2D transform\n const transform = readTransform(segment)\n if (transform) {\n const [px, py] = transform.position ?? [0, 0]\n const [sx, sy] = transform.scale ?? [1, 1]\n const rotation = transform.rotation?.[2] ?? 0\n\n display.position.set(width / 2 + (px * width) / 2, height / 2 - (py * height) / 2)\n display.scale.set(sx, sy)\n display.rotation = (rotation / 180) * Math.PI\n }\n}\n\nexport function placeholder(key: string, url?: string) {\n const g = new Graphics()\n g.rect(0, 0, 10, 10).fill({ color: stringToColor(url ?? key), alpha: 1 })\n return g\n}\n\nexport function placeholderTexture(width: number, height: number, color?: string) {\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${width}\" height=\"${height}\"><rect width=\"100%\" height=\"100%\" fill=\"${color ?? '#0f172a'}\" fill-opacity=\"0.8\"/></svg>`\n return `data:image/svg+xml;base64,${btoa(svg)}`\n}\n\nexport function stringToColor(key: string) {\n let hash = 0\n for (let i = 0; i < key.length; i++)\n hash = key.charCodeAt(i) + ((hash << 5) - hash)\n return hash & 0x00FFFFFF\n}\n\nexport function computeDuration(protocol: IVideoProtocol) {\n const endTimes = protocol.tracks.flatMap(track => track.children.map(seg => seg.endTime))\n return endTimes.length ? Math.max(...endTimes) : 0\n}\n\nexport function clamp(num: number, min: number, max: number) {\n return Math.min(Math.max(num, min), max)\n}\n\nexport function cloneProtocol(protocol: IVideoProtocol) {\n const raw = toRaw(protocol) as IVideoProtocol\n // use JSON clone to avoid structuredClone errors on proxies (e.g., Vue reactive)\n return JSON.parse(JSON.stringify(raw)) as IVideoProtocol\n}\n\nfunction hasOpacity(segment: SegmentUnion): segment is SegmentUnion & { opacity?: number } {\n return 'opacity' in segment\n}\n\nfunction readOpacity(segment: SegmentUnion) {\n if (hasOpacity(segment) && typeof segment.opacity === 'number')\n return segment.opacity\n return 1\n}\n\nfunction hasTransform(segment: SegmentUnion): segment is SegmentUnion & { transform?: ITransform } {\n return 'transform' in segment\n}\n\nfunction readTransform(segment: SegmentUnion) {\n if (hasTransform(segment))\n return segment.transform\n return undefined\n}\n","import type { IVideoFramesSegment, IVideoProtocol, SegmentUnion } from '@video-editor/shared'\nimport type { ComputedRef, Ref, ShallowRef } from '@vue/reactivity'\nimport type { Application, ApplicationOptions } from 'pixi.js'\nimport type { MaybeRef, PixiDisplayObject } from './types'\nimport { createResourceManager, createValidator } from '@video-editor/protocol'\nimport {\n computed,\n effectScope,\n isRef,\n ref,\n shallowRef,\n unref,\n watch,\n} from '@vue/reactivity'\nimport { MP4Clip } from '@webav/av-cliper'\nimport { file as opfsFile } from 'opfs-tools'\nimport { Container, Sprite, Texture } from 'pixi.js'\nimport { createApp as create2dApp } from './2d'\nimport {\n applyDisplayProps,\n clamp,\n cloneProtocol,\n collectActiveSegments,\n collectResourceUrls,\n computeDuration,\n placeholder,\n} from './helpers'\n\nconst DEFAULT_RES_DIR = '/video-editor-res'\n\nexport interface RendererOptions {\n protocol: MaybeRef<IVideoProtocol>\n app?: Application\n appOptions?: Partial<ApplicationOptions>\n resourceDir?: string\n autoPlay?: boolean\n}\n\nexport interface Renderer {\n app: Application\n layer: Container\n currentTime: Ref<number>\n duration: ComputedRef<number>\n isPlaying: Ref<boolean>\n play: () => void\n pause: () => void\n tick: (deltaMs?: number) => void\n seek: (time: number) => void\n destroy: () => void\n}\n\n/**\n * Create a renderer that reacts to protocol updates and drives playback state.\n * - Pass a reactive `protocol` (Ref/readonly/normal object)\n * - Call `play/pause/seek/tick` to drive the timeline\n * - Rendering updates when `protocol` or `currentTime` changes\n */\nexport async function createRenderer(opts: RendererOptions): Promise<Renderer> {\n const validator = createValidator()\n const protocolInput: Ref<IVideoProtocol> | ShallowRef<IVideoProtocol>\n = isRef(opts.protocol) ? opts.protocol : shallowRef(opts.protocol)\n const validatedProtocol: ShallowRef<IVideoProtocol> = shallowRef(\n validator.verify(cloneProtocol(unref(protocolInput))),\n )\n\n const app = opts.app ?? await create2dApp(opts.appOptions)\n const layer = new Container()\n app.stage.addChild(layer)\n\n const resourceManager = createResourceManager({ dir: opts.resourceDir })\n const resourceWarmUp = new Set<string>()\n const displayCache = new Map<string, PixiDisplayObject>()\n const displayLoading = new Map<string, Promise<PixiDisplayObject | undefined>>()\n const videoEntries = new Map<string, {\n clip: MP4Clip\n canvas: HTMLCanvasElement\n texture: Texture\n sprite: Sprite\n meta?: { width: number, height: number }\n }>()\n\n const currentTime = ref(0)\n const isPlaying = ref(false)\n const duration = computed(() => computeDuration(validatedProtocol.value))\n\n let rafId: number | undefined\n let lastTickAt = 0\n\n interface RenderTask {\n app: Application\n layer: Container\n protocol: IVideoProtocol\n at: number\n getDisplay: (segment: SegmentUnion) => Promise<PixiDisplayObject | undefined>\n }\n\n async function renderScene(task: RenderTask) {\n const { protocol, at, layer } = task\n const renderAt = normalizeRenderTime(protocol, at)\n const active = collectActiveSegments(protocol, renderAt)\n const stageWidth = task.app.renderer.width\n const stageHeight = task.app.renderer.height\n\n const renders: (PixiDisplayObject | undefined)[] = []\n for (const { segment } of active) {\n const display = await task.getDisplay(segment)\n if (!display)\n continue\n applyDisplayProps(display, segment, stageWidth, stageHeight)\n if (isVideoSegment(segment))\n await updateVideoFrame(segment, renderAt)\n renders.push(display)\n }\n\n layer.removeChildren()\n const cleaned = renders.filter(Boolean) as PixiDisplayObject[]\n if (cleaned.length)\n layer.addChild(...cleaned)\n task.app.render()\n }\n\n const queueRender = createRenderQueue(() => renderScene({\n app,\n layer,\n protocol: validatedProtocol.value,\n at: currentTime.value,\n getDisplay: getDisplayForSegment,\n }))\n\n const scope = effectScope()\n scope.run(() => {\n // Sync external protocol mutations into a verified snapshot the renderer can rely on.\n watch(\n () => unref(protocolInput),\n (protocol) => {\n try {\n validatedProtocol.value = validator.verify(cloneProtocol(protocol))\n }\n catch (err) {\n console.error('[renderer] invalid protocol update', err)\n return\n }\n clearDisplays()\n warmUpResources(validatedProtocol.value)\n cleanupCache(validatedProtocol.value)\n clampCurrentTime()\n queueRender()\n },\n { deep: true, immediate: true },\n )\n\n // React to time changes.\n watch(currentTime, () => {\n clampCurrentTime()\n queueRender()\n })\n\n // Keep duration/currentTime in sync with protocol updates.\n watch(duration, () => clampCurrentTime())\n })\n\n function clampCurrentTime() {\n const nextDuration = duration.value\n if (nextDuration <= 0)\n currentTime.value = 0\n else if (currentTime.value > nextDuration)\n currentTime.value = nextDuration\n else if (currentTime.value < 0)\n currentTime.value = 0\n }\n\n function warmUpResources(protocol: IVideoProtocol) {\n for (const url of collectResourceUrls(protocol)) {\n if (resourceWarmUp.has(url))\n continue\n\n resourceWarmUp.add(url)\n resourceManager.add(url).catch(() => {\n // noop – render will fall back to Texture.from(url)\n })\n }\n }\n\n function cleanupCache(protocol: IVideoProtocol) {\n const ids = new Set(protocol.tracks.flatMap(track => track.children.map(seg => seg.id)))\n for (const [id, display] of displayCache) {\n if (ids.has(id))\n continue\n display.destroy()\n displayCache.delete(id)\n }\n for (const [id, entry] of videoEntries) {\n if (ids.has(id))\n continue\n entry.clip.destroy()\n videoEntries.delete(id)\n }\n }\n\n function clearDisplays() {\n layer.removeChildren()\n for (const display of displayCache.values()) {\n display.destroy()\n }\n displayCache.clear()\n displayLoading.clear()\n for (const { clip } of videoEntries.values())\n clip.destroy()\n videoEntries.clear()\n }\n\n function play() {\n if (isPlaying.value)\n return\n isPlaying.value = true\n lastTickAt = performance.now()\n rafId = requestAnimationFrame(loop)\n }\n\n function pause() {\n isPlaying.value = false\n if (rafId !== undefined)\n cancelAnimationFrame(rafId)\n rafId = undefined\n }\n\n function loop() {\n tick()\n if (isPlaying.value)\n rafId = requestAnimationFrame(loop)\n }\n\n function tick(deltaMs?: number) {\n if (!isPlaying.value && deltaMs === undefined)\n return\n\n const now = performance.now()\n const delta = deltaMs ?? (lastTickAt ? now - lastTickAt : 0)\n lastTickAt = now\n\n if (delta === 0)\n return\n\n currentTime.value = clamp(\n currentTime.value + delta,\n 0,\n duration.value || Number.POSITIVE_INFINITY,\n )\n\n if (duration.value > 0 && currentTime.value >= duration.value)\n pause()\n\n // render happens via watch on currentTime\n }\n\n function seek(time: number) {\n currentTime.value = clamp(time, 0, duration.value || Number.POSITIVE_INFINITY)\n }\n\n async function getDisplayForSegment(segment: SegmentUnion) {\n const cached = displayCache.get(segment.id)\n if (cached)\n return cached\n\n const loading = displayLoading.get(segment.id)\n if (loading)\n return loading\n\n const promise = loadDisplay(segment)\n displayLoading.set(segment.id, promise)\n\n const display = await promise\n if (display)\n displayCache.set(segment.id, display)\n\n displayLoading.delete(segment.id)\n return display\n }\n\n async function loadDisplay(segment: SegmentUnion): Promise<PixiDisplayObject | undefined> {\n // prioritize static resources via protocol resource manager\n if (segment.segmentType === 'frames' || segment.segmentType === 'sticker') {\n if (!segment.url)\n return placeholder(segment.segmentType)\n\n if ('type' in segment && segment.type === 'video') {\n const sprite = await loadVideoSprite(segment)\n if (sprite)\n return sprite\n }\n\n const texture = await loadTexture(segment.url)\n if (texture)\n return new Sprite(texture)\n return placeholder(segment.segmentType, segment.url)\n }\n\n if (segment.segmentType === 'text')\n return undefined\n\n if (segment.segmentType === 'effect' || segment.segmentType === 'filter')\n return undefined\n\n // audio segments do not render visuals\n return undefined\n }\n\n async function loadTexture(url: string) {\n const isDataUrl = url.startsWith('data:')\n const isHttp = /^https?:\\/\\//.test(url)\n\n if (!isDataUrl && !isHttp) {\n try {\n await resourceManager.add(url)\n const res = await resourceManager.get(url)\n if (res instanceof HTMLImageElement)\n return Texture.from(res)\n }\n catch {\n // fall through to direct image load\n }\n }\n\n // load image directly to avoid invalid path issues with http/data URLs\n return await loadImageTexture(url)\n }\n\n async function loadVideoSprite(segment: SegmentUnion & { type: 'video', url: string }): Promise<Sprite | undefined> {\n const existing = videoEntries.get(segment.id)\n if (existing)\n return existing.sprite\n\n let file\n try {\n await resourceManager.add(segment.url)\n file = await getOpfsFile(segment.url)\n }\n catch {\n file = undefined\n }\n let clip: MP4Clip\n try {\n if (file) {\n clip = new MP4Clip(file)\n }\n else {\n const res = await fetch(segment.url)\n if (!res.body)\n return undefined\n clip = new MP4Clip(res.body)\n }\n }\n catch {\n return undefined\n }\n\n try {\n await clip.ready\n }\n catch {\n clip.destroy()\n return undefined\n }\n\n const { width, height } = clip.meta\n const canvas = document.createElement('canvas')\n canvas.width = width || 1\n canvas.height = height || 1\n const texture = Texture.from(canvas)\n const sprite = new Sprite(texture)\n\n videoEntries.set(segment.id, { clip, canvas, texture, sprite, meta: { width, height } })\n return sprite\n }\n\n async function updateVideoFrame(segment: IVideoFramesSegment, at: number) {\n const entry = videoEntries.get(segment.id)\n if (!entry)\n return\n\n try {\n const offsetMs = segment.fromTime ?? 0\n const relativeMs = Math.max(0, at - segment.startTime + offsetMs)\n const relativeUs = Math.floor(relativeMs * 1000)\n const res = await entry.clip.tick(relativeUs)\n if (res.video) {\n const ctx = entry.canvas.getContext('2d')\n if (ctx) {\n ctx.drawImage(res.video, 0, 0, entry.canvas.width, entry.canvas.height)\n refreshCanvasTexture(entry.texture)\n }\n res.video.close()\n }\n }\n catch (err) {\n console.warn('[renderer] update video frame failed', err)\n }\n }\n\n function isVideoSegment(segment: SegmentUnion): segment is IVideoFramesSegment {\n return segment.segmentType === 'frames'\n && segment.type === 'video'\n && typeof segment.url === 'string'\n }\n\n function normalizeRenderTime(protocol: IVideoProtocol, at: number) {\n const total = computeDuration(protocol)\n if (total <= 0)\n return 0\n if (at < total)\n return at\n // Keep the last visible frame when playback reaches the end.\n const frameWindow = Math.max(1000 / Math.max(protocol.fps || 30, 1), 1)\n return Math.max(total - frameWindow, 0)\n }\n\n async function getOpfsFile(url: string) {\n const dir = opts.resourceDir ?? DEFAULT_RES_DIR\n try {\n const file = opfsFile(`${dir}/${url}`)\n if (await file.exists())\n return file\n }\n catch {\n return undefined\n }\n return undefined\n }\n\n function loadImageTexture(url: string): Promise<Texture | undefined> {\n return new Promise((resolve) => {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.onload = () => resolve(Texture.from(img))\n img.onerror = () => {\n console.warn('[renderer] failed to load image', url)\n resolve(undefined)\n }\n img.src = url\n })\n }\n\n function refreshCanvasTexture(texture: Texture) {\n const source = texture.source\n if ('update' in source && typeof source.update === 'function') {\n source.update()\n return\n }\n\n if (typeof texture.update === 'function')\n texture.update()\n }\n\n function destroy() {\n pause()\n scope.stop()\n clearDisplays()\n layer.destroy({ children: true })\n displayCache.clear()\n displayLoading.clear()\n resourceWarmUp.clear()\n if (!opts.app)\n app.destroy()\n }\n\n if (opts.autoPlay)\n play()\n\n return {\n app,\n layer,\n currentTime,\n duration,\n isPlaying,\n play,\n pause,\n tick,\n seek,\n destroy,\n }\n}\n\nfunction createRenderQueue(job: () => Promise<void> | void) {\n let queued = false\n let running = false\n\n const run = async () => {\n if (running) {\n queued = true\n return\n }\n running = true\n do {\n queued = false\n await job()\n } while (queued)\n running = false\n }\n\n return run\n}\n"],"names":["createApp","opts","app","Application","collectResourceUrls","protocol","urls","track","segment","collectActiveSegments","at","active","trackIndex","childIndex","a","b","applyDisplayProps","display","width","height","opacity","readOpacity","Sprite","Texture","placeholderTexture","Graphics","stringToColor","hasOpacity","transform","readTransform","px","py","sx","sy","rotation","placeholder","key","url","g","color","svg","hash","i","computeDuration","endTimes","seg","clamp","num","min","max","cloneProtocol","raw","toRaw","hasTransform","DEFAULT_RES_DIR","createRenderer","validator","createValidator","protocolInput","isRef","shallowRef","validatedProtocol","unref","create2dApp","layer","Container","resourceManager","createResourceManager","resourceWarmUp","displayCache","displayLoading","videoEntries","currentTime","ref","isPlaying","duration","computed","rafId","lastTickAt","renderScene","task","renderAt","normalizeRenderTime","stageWidth","stageHeight","renders","isVideoSegment","updateVideoFrame","cleaned","queueRender","createRenderQueue","getDisplayForSegment","scope","effectScope","watch","err","clearDisplays","warmUpResources","cleanupCache","clampCurrentTime","nextDuration","ids","id","entry","clip","play","loop","pause","tick","deltaMs","now","delta","seek","time","cached","loading","promise","loadDisplay","sprite","loadVideoSprite","texture","loadTexture","isDataUrl","isHttp","res","loadImageTexture","existing","file","getOpfsFile","MP4Clip","canvas","offsetMs","relativeMs","relativeUs","ctx","refreshCanvasTexture","total","frameWindow","dir","opfsFile","resolve","img","source","destroy","job","queued","running"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@video-editor/renderer",
3
3
  "type": "module",
4
- "version": "0.0.1-beta.1",
4
+ "version": "0.0.1-beta.11",
5
5
  "exports": {
6
6
  ".": {
7
7
  "import": "./dist/index.js"
@@ -21,8 +21,8 @@
21
21
  "@webav/av-cliper": "^1.2.7",
22
22
  "opfs-tools": "^0.7.4",
23
23
  "pixi.js": "^8.14.3",
24
- "@video-editor/protocol": "0.0.1-beta.1",
25
- "@video-editor/shared": "0.0.1-beta.1"
24
+ "@video-editor/protocol": "0.0.1-beta.11",
25
+ "@video-editor/shared": "0.0.1-beta.11"
26
26
  },
27
27
  "scripts": {
28
28
  "build": "vite build",