@meframe/core 0.0.14 → 0.0.15

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.
Files changed (34) hide show
  1. package/dist/cache/CacheManager.d.ts +1 -1
  2. package/dist/cache/CacheManager.d.ts.map +1 -1
  3. package/dist/cache/CacheManager.js +3 -4
  4. package/dist/cache/CacheManager.js.map +1 -1
  5. package/dist/cache/L2Cache.d.ts.map +1 -1
  6. package/dist/cache/L2Cache.js +21 -13
  7. package/dist/cache/L2Cache.js.map +1 -1
  8. package/dist/cache/l1/VideoL1Cache.d.ts +1 -4
  9. package/dist/cache/l1/VideoL1Cache.d.ts.map +1 -1
  10. package/dist/cache/l1/VideoL1Cache.js +12 -33
  11. package/dist/cache/l1/VideoL1Cache.js.map +1 -1
  12. package/dist/model/CompositionModel.d.ts +8 -0
  13. package/dist/model/CompositionModel.d.ts.map +1 -1
  14. package/dist/model/CompositionModel.js +38 -0
  15. package/dist/model/CompositionModel.js.map +1 -1
  16. package/dist/model/patch.js +9 -0
  17. package/dist/model/patch.js.map +1 -1
  18. package/dist/orchestrator/ClipSessionManager.js +1 -1
  19. package/dist/orchestrator/ClipSessionManager.js.map +1 -1
  20. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  21. package/dist/orchestrator/Orchestrator.js +1 -9
  22. package/dist/orchestrator/Orchestrator.js.map +1 -1
  23. package/dist/orchestrator/VideoClipSession.d.ts +0 -1
  24. package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
  25. package/dist/orchestrator/VideoClipSession.js +11 -13
  26. package/dist/orchestrator/VideoClipSession.js.map +1 -1
  27. package/dist/stages/decode/BaseDecoder.d.ts.map +1 -1
  28. package/dist/stages/decode/BaseDecoder.js +1 -1
  29. package/dist/stages/decode/BaseDecoder.js.map +1 -1
  30. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  31. package/dist/stages/load/ResourceLoader.js.map +1 -1
  32. package/dist/workers/BaseDecoder.js +1 -1
  33. package/dist/workers/BaseDecoder.js.map +1 -1
  34. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"CompositionModel.d.ts","sourceRoot":"","sources":["../../src/model/CompositionModel.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,MAAM,EAGP,MAAM,SAAS,CAAC;AAIjB,qBAAa,gBAAgB;IAC3B,SAAgB,OAAO,EAAG,KAAK,CAAU;IACzC,SAAgB,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IAChC,UAAU,EAAG,MAAM,CAAC;IAC3B,SAAgB,WAAW,EAAE,MAAM,CAAC;IACpC,SAAgB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChC,SAAgB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAsB;IAEvD,SAAgB,YAAY,CAAC,EAAE;QAC7B,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IAEF,SAAgB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAElC,IAAI,EAAE,oBAAoB;IA6BtC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI;IAInC,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,GAAG,KAAK,EAAE;IAKpE,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIjC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE;IAoBxD,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE;IAgBtD,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAKpD,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAcpE;;;OAGG;IACH,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;IAsCvF;;;OAGG;IACH,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;IAY/C,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAIxC,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,IAAI;IAOvF,kBAAkB,IAAI,QAAQ,EAAE;IAahC,WAAW,IAAI,MAAM;IAIrB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAUzC,OAAO,CAAC,YAAY;IA0DpB,OAAO,CAAC,oBAAoB;IAqE5B,OAAO,CAAC,cAAc;CAoBvB"}
1
+ {"version":3,"file":"CompositionModel.d.ts","sourceRoot":"","sources":["../../src/model/CompositionModel.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,KAAK,EACL,IAAI,EACJ,QAAQ,EACR,MAAM,EAGP,MAAM,SAAS,CAAC;AAIjB,qBAAa,gBAAgB;IAC3B,SAAgB,OAAO,EAAG,KAAK,CAAU;IACzC,SAAgB,GAAG,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;IAChC,UAAU,EAAG,MAAM,CAAC;IAC3B,SAAgB,WAAW,EAAE,MAAM,CAAC;IACpC,SAAgB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChC,SAAgB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEjD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAsB;IAEvD,SAAgB,YAAY,CAAC,EAAE;QAC7B,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;IAEF,SAAgB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAElC,IAAI,EAAE,oBAAoB;IA6BtC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI;IAInC,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,GAAG,KAAK,EAAE;IAKpE,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIjC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE;IAoBxD,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE;IAgBtD,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAKpD,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAcpE;;;OAGG;IACH,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;IAsCvF;;;OAGG;IACH,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;IAY/C,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAIxC,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,IAAI;IAOvF,kBAAkB,IAAI,QAAQ,EAAE;IAahC,WAAW,IAAI,MAAM;IAIrB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAUzC,OAAO,CAAC,YAAY;IA0DpB;;OAEG;IACH,uBAAuB,CACrB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,aAAa,EAAE,MAAM,GAAG,SAAS,GAChC,IAAI;IA2BP;;OAEG;IACH,mBAAmB,IAAI,IAAI;IAY3B,OAAO,CAAC,oBAAoB;IAqE5B,OAAO,CAAC,cAAc;CAoBvB"}
@@ -207,6 +207,44 @@ ${errors.map((e) => `${e.path}: ${e.message}`).join("\n")}`
207
207
  }
208
208
  this.durationUs = maxEndUs;
209
209
  }
210
+ /**
211
+ * Incrementally update resource index when clip's resourceId changes
212
+ */
213
+ updateClipResourceIndex(clipId, oldResourceId, newResourceId) {
214
+ if (oldResourceId) {
215
+ const oldResource = this.resources.get(oldResourceId);
216
+ if (oldResource?.clipIds) {
217
+ oldResource.clipIds = oldResource.clipIds.filter((id) => id !== clipId);
218
+ }
219
+ const oldCount = this.resourceRefCount.get(oldResourceId) || 0;
220
+ this.resourceRefCount.set(oldResourceId, Math.max(0, oldCount - 1));
221
+ }
222
+ if (newResourceId) {
223
+ const newResource = this.resources.get(newResourceId);
224
+ if (newResource) {
225
+ if (!newResource.clipIds) {
226
+ newResource.clipIds = [];
227
+ }
228
+ if (!newResource.clipIds.includes(clipId)) {
229
+ newResource.clipIds.push(clipId);
230
+ }
231
+ }
232
+ const newCount = this.resourceRefCount.get(newResourceId) || 0;
233
+ this.resourceRefCount.set(newResourceId, newCount + 1);
234
+ }
235
+ }
236
+ /**
237
+ * Recalculate total duration based on main track
238
+ */
239
+ recalculateDuration() {
240
+ const mainTrack = this.findTrack(this.mainTrackId);
241
+ if (!mainTrack || mainTrack.clips.length === 0) {
242
+ this.durationUs = 0;
243
+ return;
244
+ }
245
+ const lastClip = mainTrack.clips[mainTrack.clips.length - 1];
246
+ this.durationUs = (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);
247
+ }
210
248
  sinkAttachmentTracks() {
211
249
  const mainTrack = this.tracks.find((t) => t.id === this.mainTrackId);
212
250
  if (!mainTrack) return;
@@ -1 +1 @@
1
- {"version":3,"file":"CompositionModel.js","sources":["../../src/model/CompositionModel.ts"],"sourcesContent":["import {\n CompositionModelData,\n Track,\n Clip,\n Resource,\n TimeUs,\n AnimationEffect,\n hasResourceId,\n} from './types';\nimport { binarySearchRange, binarySearchOverlapping } from '../utils/binary-search';\nimport { validateCompositionStructure } from './validation';\n\nexport class CompositionModel {\n public readonly version = '1.0' as const;\n public readonly fps: 24 | 25 | 30 | 60;\n public durationUs!: TimeUs; // Assigned in buildIndexes()\n public readonly mainTrackId: string;\n public readonly tracks: Track[];\n public readonly resources: Map<string, Resource>;\n\n private readonly trackMap: Map<string, Track>;\n private readonly clipMap: Map<string, Clip>;\n private readonly resourceRefCount: Map<string, number>;\n\n public readonly renderConfig?: {\n width: number;\n height: number;\n backgroundColor?: string;\n };\n\n public readonly ext?: Record<string, unknown>;\n\n constructor(data: CompositionModelData) {\n const errors = validateCompositionStructure(data);\n if (errors.length > 0) {\n throw new Error(\n `Validation failed:\\n${errors.map((e) => `${e.path}: ${e.message}`).join('\\n')}`\n );\n }\n\n this.fps = data.fps;\n this.mainTrackId = data.mainTrackId ?? 'main';\n this.tracks = data.tracks;\n this.resources = new Map(Object.entries(data.resources));\n this.renderConfig = data.renderConfig;\n this.ext = data.ext;\n\n // Build indexes\n this.trackMap = new Map();\n this.clipMap = new Map();\n this.resourceRefCount = new Map();\n\n this.buildIndexes();\n\n console.log(\n 'CompositionModel constructed',\n this.tracks.find((t) => t.id === this.mainTrackId)?.clips[0]\n );\n }\n\n // Track operations\n findTrack(id: string): Track | null {\n return this.trackMap.get(id) || null;\n }\n\n getTracksByKind(kind: 'video' | 'audio' | 'caption' | 'fx'): Track[] {\n return this.tracks.filter((track) => track.kind === kind);\n }\n\n // Clip operations with binary search optimization\n findClip(id: string): Clip | null {\n return this.clipMap.get(id) || null;\n }\n\n getClipsAtTime(timeUs: TimeUs, trackId?: string): Clip[] {\n const tracks = trackId ? [this.findTrack(trackId)] : this.tracks;\n const clips: Clip[] = [];\n\n for (const track of tracks) {\n if (!track) continue;\n // Use binary search for single point lookup\n const clip = binarySearchRange(track.clips, timeUs, (entry) => ({\n start: entry.startUs,\n end: entry.startUs + entry.durationUs,\n }));\n\n if (clip) {\n clips.push(clip);\n }\n }\n\n return clips;\n }\n\n getActiveClips(startUs: TimeUs, endUs: TimeUs): Clip[] {\n const clips: Clip[] = [];\n\n for (const track of this.tracks) {\n // Use binary search for range overlap\n const overlappingClips = binarySearchOverlapping(track.clips, startUs, endUs, (clip) => ({\n start: clip.startUs,\n end: clip.startUs + clip.durationUs,\n }));\n\n clips.push(...overlappingClips);\n }\n\n return clips;\n }\n\n getClipIdsByResourceId(resourceId: string): string[] {\n const resource = this.resources.get(resourceId);\n return resource?.clipIds || [];\n }\n\n getClipIdAtTime(trackId: string, timeUs: TimeUs): string | undefined {\n const track = this.findTrack(trackId);\n if (!track) {\n return undefined;\n }\n\n const clip = binarySearchRange(track.clips, timeUs, (entry) => ({\n start: entry.startUs,\n end: entry.startUs + entry.durationUs,\n }));\n\n return clip?.id;\n }\n\n /**\n * Get neighboring clips (Prev/Current/Next) at a specific time for video tracks\n * Returns prev, current, and next clip IDs\n */\n getNeighboringClips(timeUs: TimeUs): { prev?: string; current?: string; next?: string } {\n const videoTracks = this.getTracksByKind('video');\n const result: { prev?: string; current?: string; next?: string } = {};\n\n for (const track of videoTracks) {\n const clips = track.clips;\n\n for (let i = 0; i < clips.length; i++) {\n const clip = clips[i];\n if (!clip) continue;\n\n const clipEndUs = clip.startUs + clip.durationUs;\n\n if (clip.startUs <= timeUs && timeUs < clipEndUs) {\n if (!result.current) {\n result.current = clip.id;\n }\n\n if (i > 0 && !result.prev) {\n const prevClip = clips[i - 1];\n if (prevClip) {\n result.prev = prevClip.id;\n }\n }\n\n if (i < clips.length - 1 && !result.next) {\n const nextClip = clips[i + 1];\n if (nextClip) {\n result.next = nextClip.id;\n }\n }\n }\n }\n }\n\n return result;\n }\n\n /**\n * Get all clip IDs that should be cached using 2-Clip strategy\n * Returns array of unique clip IDs (Prev/Current/Next)\n */\n getClipsToCacheAtTime(timeUs: TimeUs): string[] {\n const { current, next } = this.getNeighboringClips(timeUs);\n const clipIds: string[] = [];\n\n if (current) clipIds.push(current);\n if (next) clipIds.push(next);\n // if (prev) clipIds.push(prev);\n\n return Array.from(new Set(clipIds));\n }\n\n // Resource operations\n getResource(id: string): Resource | null {\n return this.resources.get(id) || null;\n }\n\n updateResourceState(id: string, state: 'pending' | 'loading' | 'ready' | 'error'): void {\n const resource = this.resources.get(id);\n if (resource) {\n resource.state = state;\n }\n }\n\n getUnusedResources(): Resource[] {\n const unused: Resource[] = [];\n\n for (const [id, resource] of this.resources) {\n if (!this.resourceRefCount.has(id) || this.resourceRefCount.get(id) === 0) {\n unused.push(resource);\n }\n }\n\n return unused;\n }\n\n // Time operations\n getDuration(): TimeUs {\n return this.durationUs;\n }\n\n getTrackDuration(trackId: string): TimeUs {\n const track = this.findTrack(trackId);\n if (!track || track.clips.length === 0) return 0;\n\n // Since clips are sorted, last clip determines duration\n const lastClip = track.clips[track.clips.length - 1];\n return (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);\n }\n\n // Private methods\n private buildIndexes(): void {\n // Clear existing indexes\n this.trackMap.clear();\n this.clipMap.clear();\n this.resourceRefCount.clear();\n\n // Step 1: Sink attachment tracks to main track first\n this.sinkAttachmentTracks();\n\n let maxEndUs = 0;\n\n // Step 2: Build all indexes in one pass (track, clip, resource)\n for (const track of this.tracks) {\n this.trackMap.set(track.id, track);\n\n for (const clip of track.clips) {\n (clip as Clip).trackId = track.id;\n (clip as Clip).trackKind = track.kind;\n this.clipMap.set(clip.id, clip);\n\n // Main track resource index (only for clips with resourceId)\n if (hasResourceId(clip)) {\n const resource = this.resources.get(clip.resourceId);\n if (resource) {\n resource.clipIds = [...(resource.clipIds || []), clip.id];\n }\n const count = this.resourceRefCount.get(clip.resourceId) || 0;\n this.resourceRefCount.set(clip.resourceId, count + 1);\n }\n\n // Attachment resource indexes (attachments are already sunk)\n const attachments = clip.attachments ?? [];\n for (const attachment of attachments) {\n const attachmentResourceId = attachment.data?.resourceId;\n if (attachmentResourceId && typeof attachmentResourceId === 'string') {\n const attachmentResource = this.resources.get(attachmentResourceId);\n if (attachmentResource) {\n const clipIds = attachmentResource.clipIds || [];\n if (!clipIds.includes(clip.id)) {\n attachmentResource.clipIds = [...clipIds, clip.id];\n }\n }\n const attachmentCount = this.resourceRefCount.get(attachmentResourceId) || 0;\n this.resourceRefCount.set(attachmentResourceId, attachmentCount + 1);\n }\n }\n\n // Calculate max end time\n const clipEndUs = clip.startUs + clip.durationUs;\n if (clipEndUs > maxEndUs) {\n maxEndUs = clipEndUs;\n }\n }\n }\n\n this.durationUs = maxEndUs;\n }\n\n private sinkAttachmentTracks(): void {\n const mainTrack = this.tracks.find((t) => t.id === this.mainTrackId);\n if (!mainTrack) return;\n\n // Find all tracks to sink: non-main, non-audio tracks (includes caption, overlay, etc.)\n const attachmentTracks = this.tracks.filter(\n (t) => t.kind !== 'audio' && t.id !== this.mainTrackId\n );\n\n for (const attachmentTrack of attachmentTracks) {\n for (const attachmentClip of attachmentTrack.clips) {\n // Get purpose: use metadata.purpose, or infer from track kind\n let purpose: 'caption' | 'overlay' | 'mask' =\n attachmentClip.metadata?.purpose ??\n (attachmentTrack.kind === 'caption' ? 'caption' : 'overlay');\n\n // Ensure purpose is valid\n if (purpose !== 'caption' && purpose !== 'overlay' && purpose !== 'mask') {\n purpose = 'overlay';\n }\n\n for (const mainClip of mainTrack.clips) {\n const overlap = this.getTimeOverlap(attachmentClip, mainClip);\n if (!overlap) continue;\n\n if (!mainClip.attachments) {\n mainClip.attachments = [];\n }\n\n // Extract animation effect\n const animationEffect = attachmentClip.effects?.find(\n (e) => e.effectType === 'animation'\n ) as AnimationEffect | undefined;\n\n // Extract target dimensions from metadata\n const targetWidth = attachmentClip.metadata?.targetWidth as number | undefined;\n const targetHeight = attachmentClip.metadata?.targetHeight as number | undefined;\n\n const attachmentData: Record<string, unknown> = {\n animation: animationEffect?.params,\n overlayClipStartUs: attachmentClip.startUs,\n mainClipStartUs: mainClip.startUs,\n trackIndex: this.tracks.indexOf(attachmentTrack),\n ...(targetWidth !== undefined && { targetWidth }),\n ...(targetHeight !== undefined && { targetHeight }),\n };\n\n // Add resourceId if exists (trackKind not yet set, check field directly)\n if ('resourceId' in attachmentClip && attachmentClip.resourceId) {\n attachmentData.resourceId = attachmentClip.resourceId;\n }\n\n // Add text if exists (trackKind not yet set, check field directly)\n if ('text' in attachmentClip && attachmentClip.text) {\n attachmentData.text = attachmentClip.text;\n }\n\n mainClip.attachments.push({\n id: `${purpose}-${attachmentClip.id}-${mainClip.id}`,\n kind: purpose,\n startUs: overlap.clipRelativeStart,\n durationUs: overlap.duration,\n data: attachmentData,\n });\n }\n }\n }\n }\n\n private getTimeOverlap(\n attachmentClip: Clip,\n mainClip: Clip\n ): { clipRelativeStart: number; duration: number } | null {\n const attachmentStart = attachmentClip.startUs;\n const attachmentEnd = attachmentClip.startUs + attachmentClip.durationUs;\n const mainStart = mainClip.startUs;\n const mainEnd = mainClip.startUs + mainClip.durationUs;\n\n if (attachmentEnd <= mainStart || attachmentStart >= mainEnd) {\n return null;\n }\n\n const overlapStart = Math.max(attachmentStart, mainStart);\n const overlapEnd = Math.min(attachmentEnd, mainEnd);\n const duration = overlapEnd - overlapStart;\n const clipRelativeStart = overlapStart - mainStart;\n\n return { clipRelativeStart, duration };\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"CompositionModel.js","sources":["../../src/model/CompositionModel.ts"],"sourcesContent":["import {\n CompositionModelData,\n Track,\n Clip,\n Resource,\n TimeUs,\n AnimationEffect,\n hasResourceId,\n} from './types';\nimport { binarySearchRange, binarySearchOverlapping } from '../utils/binary-search';\nimport { validateCompositionStructure } from './validation';\n\nexport class CompositionModel {\n public readonly version = '1.0' as const;\n public readonly fps: 24 | 25 | 30 | 60;\n public durationUs!: TimeUs; // Assigned in buildIndexes()\n public readonly mainTrackId: string;\n public readonly tracks: Track[];\n public readonly resources: Map<string, Resource>;\n\n private readonly trackMap: Map<string, Track>;\n private readonly clipMap: Map<string, Clip>;\n private readonly resourceRefCount: Map<string, number>;\n\n public readonly renderConfig?: {\n width: number;\n height: number;\n backgroundColor?: string;\n };\n\n public readonly ext?: Record<string, unknown>;\n\n constructor(data: CompositionModelData) {\n const errors = validateCompositionStructure(data);\n if (errors.length > 0) {\n throw new Error(\n `Validation failed:\\n${errors.map((e) => `${e.path}: ${e.message}`).join('\\n')}`\n );\n }\n\n this.fps = data.fps;\n this.mainTrackId = data.mainTrackId ?? 'main';\n this.tracks = data.tracks;\n this.resources = new Map(Object.entries(data.resources));\n this.renderConfig = data.renderConfig;\n this.ext = data.ext;\n\n // Build indexes\n this.trackMap = new Map();\n this.clipMap = new Map();\n this.resourceRefCount = new Map();\n\n this.buildIndexes();\n\n console.log(\n 'CompositionModel constructed',\n this.tracks.find((t) => t.id === this.mainTrackId)?.clips[0]\n );\n }\n\n // Track operations\n findTrack(id: string): Track | null {\n return this.trackMap.get(id) || null;\n }\n\n getTracksByKind(kind: 'video' | 'audio' | 'caption' | 'fx'): Track[] {\n return this.tracks.filter((track) => track.kind === kind);\n }\n\n // Clip operations with binary search optimization\n findClip(id: string): Clip | null {\n return this.clipMap.get(id) || null;\n }\n\n getClipsAtTime(timeUs: TimeUs, trackId?: string): Clip[] {\n const tracks = trackId ? [this.findTrack(trackId)] : this.tracks;\n const clips: Clip[] = [];\n\n for (const track of tracks) {\n if (!track) continue;\n // Use binary search for single point lookup\n const clip = binarySearchRange(track.clips, timeUs, (entry) => ({\n start: entry.startUs,\n end: entry.startUs + entry.durationUs,\n }));\n\n if (clip) {\n clips.push(clip);\n }\n }\n\n return clips;\n }\n\n getActiveClips(startUs: TimeUs, endUs: TimeUs): Clip[] {\n const clips: Clip[] = [];\n\n for (const track of this.tracks) {\n // Use binary search for range overlap\n const overlappingClips = binarySearchOverlapping(track.clips, startUs, endUs, (clip) => ({\n start: clip.startUs,\n end: clip.startUs + clip.durationUs,\n }));\n\n clips.push(...overlappingClips);\n }\n\n return clips;\n }\n\n getClipIdsByResourceId(resourceId: string): string[] {\n const resource = this.resources.get(resourceId);\n return resource?.clipIds || [];\n }\n\n getClipIdAtTime(trackId: string, timeUs: TimeUs): string | undefined {\n const track = this.findTrack(trackId);\n if (!track) {\n return undefined;\n }\n\n const clip = binarySearchRange(track.clips, timeUs, (entry) => ({\n start: entry.startUs,\n end: entry.startUs + entry.durationUs,\n }));\n\n return clip?.id;\n }\n\n /**\n * Get neighboring clips (Prev/Current/Next) at a specific time for video tracks\n * Returns prev, current, and next clip IDs\n */\n getNeighboringClips(timeUs: TimeUs): { prev?: string; current?: string; next?: string } {\n const videoTracks = this.getTracksByKind('video');\n const result: { prev?: string; current?: string; next?: string } = {};\n\n for (const track of videoTracks) {\n const clips = track.clips;\n\n for (let i = 0; i < clips.length; i++) {\n const clip = clips[i];\n if (!clip) continue;\n\n const clipEndUs = clip.startUs + clip.durationUs;\n\n if (clip.startUs <= timeUs && timeUs < clipEndUs) {\n if (!result.current) {\n result.current = clip.id;\n }\n\n if (i > 0 && !result.prev) {\n const prevClip = clips[i - 1];\n if (prevClip) {\n result.prev = prevClip.id;\n }\n }\n\n if (i < clips.length - 1 && !result.next) {\n const nextClip = clips[i + 1];\n if (nextClip) {\n result.next = nextClip.id;\n }\n }\n }\n }\n }\n\n return result;\n }\n\n /**\n * Get all clip IDs that should be cached using 2-Clip strategy\n * Returns array of unique clip IDs (Prev/Current/Next)\n */\n getClipsToCacheAtTime(timeUs: TimeUs): string[] {\n const { current, next } = this.getNeighboringClips(timeUs);\n const clipIds: string[] = [];\n\n if (current) clipIds.push(current);\n if (next) clipIds.push(next);\n // if (prev) clipIds.push(prev);\n\n return Array.from(new Set(clipIds));\n }\n\n // Resource operations\n getResource(id: string): Resource | null {\n return this.resources.get(id) || null;\n }\n\n updateResourceState(id: string, state: 'pending' | 'loading' | 'ready' | 'error'): void {\n const resource = this.resources.get(id);\n if (resource) {\n resource.state = state;\n }\n }\n\n getUnusedResources(): Resource[] {\n const unused: Resource[] = [];\n\n for (const [id, resource] of this.resources) {\n if (!this.resourceRefCount.has(id) || this.resourceRefCount.get(id) === 0) {\n unused.push(resource);\n }\n }\n\n return unused;\n }\n\n // Time operations\n getDuration(): TimeUs {\n return this.durationUs;\n }\n\n getTrackDuration(trackId: string): TimeUs {\n const track = this.findTrack(trackId);\n if (!track || track.clips.length === 0) return 0;\n\n // Since clips are sorted, last clip determines duration\n const lastClip = track.clips[track.clips.length - 1];\n return (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);\n }\n\n // Private methods\n private buildIndexes(): void {\n // Clear existing indexes\n this.trackMap.clear();\n this.clipMap.clear();\n this.resourceRefCount.clear();\n\n // Step 1: Sink attachment tracks to main track first\n this.sinkAttachmentTracks();\n\n let maxEndUs = 0;\n\n // Step 2: Build all indexes in one pass (track, clip, resource)\n for (const track of this.tracks) {\n this.trackMap.set(track.id, track);\n\n for (const clip of track.clips) {\n (clip as Clip).trackId = track.id;\n (clip as Clip).trackKind = track.kind;\n this.clipMap.set(clip.id, clip);\n\n // Main track resource index (only for clips with resourceId)\n if (hasResourceId(clip)) {\n const resource = this.resources.get(clip.resourceId);\n if (resource) {\n resource.clipIds = [...(resource.clipIds || []), clip.id];\n }\n const count = this.resourceRefCount.get(clip.resourceId) || 0;\n this.resourceRefCount.set(clip.resourceId, count + 1);\n }\n\n // Attachment resource indexes (attachments are already sunk)\n const attachments = clip.attachments ?? [];\n for (const attachment of attachments) {\n const attachmentResourceId = attachment.data?.resourceId;\n if (attachmentResourceId && typeof attachmentResourceId === 'string') {\n const attachmentResource = this.resources.get(attachmentResourceId);\n if (attachmentResource) {\n const clipIds = attachmentResource.clipIds || [];\n if (!clipIds.includes(clip.id)) {\n attachmentResource.clipIds = [...clipIds, clip.id];\n }\n }\n const attachmentCount = this.resourceRefCount.get(attachmentResourceId) || 0;\n this.resourceRefCount.set(attachmentResourceId, attachmentCount + 1);\n }\n }\n\n // Calculate max end time\n const clipEndUs = clip.startUs + clip.durationUs;\n if (clipEndUs > maxEndUs) {\n maxEndUs = clipEndUs;\n }\n }\n }\n\n this.durationUs = maxEndUs;\n }\n\n /**\n * Incrementally update resource index when clip's resourceId changes\n */\n updateClipResourceIndex(\n clipId: string,\n oldResourceId: string | undefined,\n newResourceId: string | undefined\n ): void {\n // Remove from old resource\n if (oldResourceId) {\n const oldResource = this.resources.get(oldResourceId);\n if (oldResource?.clipIds) {\n oldResource.clipIds = oldResource.clipIds.filter((id) => id !== clipId);\n }\n const oldCount = this.resourceRefCount.get(oldResourceId) || 0;\n this.resourceRefCount.set(oldResourceId, Math.max(0, oldCount - 1));\n }\n\n // Add to new resource\n if (newResourceId) {\n const newResource = this.resources.get(newResourceId);\n if (newResource) {\n if (!newResource.clipIds) {\n newResource.clipIds = [];\n }\n if (!newResource.clipIds.includes(clipId)) {\n newResource.clipIds.push(clipId);\n }\n }\n const newCount = this.resourceRefCount.get(newResourceId) || 0;\n this.resourceRefCount.set(newResourceId, newCount + 1);\n }\n }\n\n /**\n * Recalculate total duration based on main track\n */\n recalculateDuration(): void {\n const mainTrack = this.findTrack(this.mainTrackId);\n if (!mainTrack || mainTrack.clips.length === 0) {\n this.durationUs = 0;\n return;\n }\n\n // Since clips are sorted, last clip determines duration\n const lastClip = mainTrack.clips[mainTrack.clips.length - 1];\n this.durationUs = (lastClip?.startUs ?? 0) + (lastClip?.durationUs ?? 0);\n }\n\n private sinkAttachmentTracks(): void {\n const mainTrack = this.tracks.find((t) => t.id === this.mainTrackId);\n if (!mainTrack) return;\n\n // Find all tracks to sink: non-main, non-audio tracks (includes caption, overlay, etc.)\n const attachmentTracks = this.tracks.filter(\n (t) => t.kind !== 'audio' && t.id !== this.mainTrackId\n );\n\n for (const attachmentTrack of attachmentTracks) {\n for (const attachmentClip of attachmentTrack.clips) {\n // Get purpose: use metadata.purpose, or infer from track kind\n let purpose: 'caption' | 'overlay' | 'mask' =\n attachmentClip.metadata?.purpose ??\n (attachmentTrack.kind === 'caption' ? 'caption' : 'overlay');\n\n // Ensure purpose is valid\n if (purpose !== 'caption' && purpose !== 'overlay' && purpose !== 'mask') {\n purpose = 'overlay';\n }\n\n for (const mainClip of mainTrack.clips) {\n const overlap = this.getTimeOverlap(attachmentClip, mainClip);\n if (!overlap) continue;\n\n if (!mainClip.attachments) {\n mainClip.attachments = [];\n }\n\n // Extract animation effect\n const animationEffect = attachmentClip.effects?.find(\n (e) => e.effectType === 'animation'\n ) as AnimationEffect | undefined;\n\n // Extract target dimensions from metadata\n const targetWidth = attachmentClip.metadata?.targetWidth as number | undefined;\n const targetHeight = attachmentClip.metadata?.targetHeight as number | undefined;\n\n const attachmentData: Record<string, unknown> = {\n animation: animationEffect?.params,\n overlayClipStartUs: attachmentClip.startUs,\n mainClipStartUs: mainClip.startUs,\n trackIndex: this.tracks.indexOf(attachmentTrack),\n ...(targetWidth !== undefined && { targetWidth }),\n ...(targetHeight !== undefined && { targetHeight }),\n };\n\n // Add resourceId if exists (trackKind not yet set, check field directly)\n if ('resourceId' in attachmentClip && attachmentClip.resourceId) {\n attachmentData.resourceId = attachmentClip.resourceId;\n }\n\n // Add text if exists (trackKind not yet set, check field directly)\n if ('text' in attachmentClip && attachmentClip.text) {\n attachmentData.text = attachmentClip.text;\n }\n\n mainClip.attachments.push({\n id: `${purpose}-${attachmentClip.id}-${mainClip.id}`,\n kind: purpose,\n startUs: overlap.clipRelativeStart,\n durationUs: overlap.duration,\n data: attachmentData,\n });\n }\n }\n }\n }\n\n private getTimeOverlap(\n attachmentClip: Clip,\n mainClip: Clip\n ): { clipRelativeStart: number; duration: number } | null {\n const attachmentStart = attachmentClip.startUs;\n const attachmentEnd = attachmentClip.startUs + attachmentClip.durationUs;\n const mainStart = mainClip.startUs;\n const mainEnd = mainClip.startUs + mainClip.durationUs;\n\n if (attachmentEnd <= mainStart || attachmentStart >= mainEnd) {\n return null;\n }\n\n const overlapStart = Math.max(attachmentStart, mainStart);\n const overlapEnd = Math.min(attachmentEnd, mainEnd);\n const duration = overlapEnd - overlapStart;\n const clipRelativeStart = overlapStart - mainStart;\n\n return { clipRelativeStart, duration };\n }\n}\n"],"names":[],"mappings":"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"}
@@ -238,6 +238,7 @@ function removeClip(model, op) {
238
238
  function updateClip(model, op) {
239
239
  const clip = model.findClip(op.clipId);
240
240
  if (!clip || !op.clip) return;
241
+ const oldResourceId = hasResourceId(clip) ? clip.resourceId : void 0;
241
242
  Object.assign(clip, op.clip);
242
243
  if (op.clip.startUs !== void 0) {
243
244
  const track = model.findTrack(op.trackId);
@@ -245,6 +246,14 @@ function updateClip(model, op) {
245
246
  track.clips.sort((a, b) => a.startUs - b.startUs);
246
247
  }
247
248
  }
249
+ const newResourceId = "resourceId" in op.clip ? op.clip.resourceId : void 0;
250
+ if (newResourceId !== void 0 && newResourceId !== oldResourceId) {
251
+ clip.oldResourceId = oldResourceId;
252
+ model.updateClipResourceIndex(op.clipId, oldResourceId, newResourceId);
253
+ }
254
+ if (op.clip.startUs !== void 0 || op.clip.durationUs !== void 0) {
255
+ model.recalculateDuration();
256
+ }
248
257
  }
249
258
  function moveClip(model, op) {
250
259
  if (!op.clipId || !op.targetTrackId) return;
@@ -1 +1 @@
1
- {"version":3,"file":"patch.js","sources":["../../src/model/patch.ts"],"sourcesContent":["import { CompositionModel } from './CompositionModel';\nimport {\n CompositionPatch,\n PatchOperation,\n TrackOperation,\n ClipOperation,\n ResourceOperation,\n AttachmentOperation,\n TransitionOperation,\n EffectOperation,\n hasResourceId,\n Track,\n Clip,\n Resource,\n Attachment,\n Transition,\n Effect,\n} from './types';\n\n/**\n * Apply patch to model and return affected clip IDs\n * Simplified for 2-Clip strategy - no complex time range calculation needed\n */\nexport function applyPatch(model: CompositionModel, patch: CompositionPatch): Set<string> {\n const affectedClips = new Set<string>();\n\n // Apply all operations and collect affected clips\n for (const op of patch.operations) {\n applyOperation(model, op);\n collectAffectedClips(model, op, affectedClips);\n }\n\n return affectedClips;\n}\n\n/**\n * Collect clips affected by a patch operation\n */\nfunction collectAffectedClips(\n model: CompositionModel,\n op: PatchOperation,\n affectedClips: Set<string>\n): void {\n switch (op.type) {\n // Track operations affect all clips in track\n case 'addTrack':\n case 'removeTrack':\n case 'updateTrack': {\n const trackOp = op as TrackOperation;\n if (trackOp.trackId) {\n const track = model.findTrack(trackOp.trackId);\n if (track) {\n track.clips.forEach((clip) => affectedClips.add(clip.id));\n }\n }\n break;\n }\n\n // Clip operations\n case 'addClip':\n case 'removeClip':\n case 'updateClip':\n case 'moveClip': {\n const clipOp = op as ClipOperation;\n if (clipOp.clipId) {\n affectedClips.add(clipOp.clipId);\n }\n break;\n }\n\n // Resource changes affect all clips using that resource\n case 'addResource':\n case 'updateResource':\n case 'removeResource': {\n const resourceOp = op as ResourceOperation;\n if (resourceOp.resourceId) {\n for (const track of model.tracks) {\n for (const clip of track.clips) {\n if (hasResourceId(clip) && clip.resourceId === resourceOp.resourceId) {\n affectedClips.add(clip.id);\n }\n }\n }\n }\n break;\n }\n\n // Attachment operations\n case 'addAttachment':\n case 'updateAttachment':\n case 'removeAttachment': {\n const attachmentOp = op as AttachmentOperation;\n if (attachmentOp.clipId) {\n affectedClips.add(attachmentOp.clipId);\n }\n break;\n }\n\n // Transition operations\n case 'addTransition':\n case 'updateTransition':\n case 'removeTransition': {\n const transitionOp = op as TransitionOperation;\n if (transitionOp.clipId) {\n affectedClips.add(transitionOp.clipId);\n }\n break;\n }\n\n // Effect operations\n case 'addEffect':\n case 'updateEffect':\n case 'removeEffect': {\n const effectOp = op as EffectOperation;\n if (effectOp.targetType === 'track' && effectOp.targetId) {\n const track = model.findTrack(effectOp.targetId);\n if (track) {\n track.clips.forEach((clip) => affectedClips.add(clip.id));\n }\n } else if (effectOp.targetType === 'clip' && effectOp.targetId) {\n affectedClips.add(effectOp.targetId);\n }\n break;\n }\n }\n}\n\nfunction applyOperation(model: CompositionModel, op: PatchOperation): void {\n switch (op.type) {\n // Track operations\n case 'addTrack':\n addTrack(model, op as TrackOperation);\n break;\n case 'removeTrack':\n removeTrack(model, op as TrackOperation);\n break;\n case 'updateTrack':\n updateTrack(model, op as TrackOperation);\n break;\n\n // Clip operations\n case 'addClip':\n addClip(model, op as ClipOperation);\n break;\n case 'removeClip':\n removeClip(model, op as ClipOperation);\n break;\n case 'updateClip':\n updateClip(model, op as ClipOperation);\n break;\n case 'moveClip':\n moveClip(model, op as ClipOperation);\n break;\n\n // Resource operations\n case 'addResource':\n addResource(model, op as ResourceOperation);\n break;\n case 'updateResource':\n updateResource(model, op as ResourceOperation);\n break;\n case 'removeResource':\n removeResource(model, op as ResourceOperation);\n break;\n\n // Attachment operations\n case 'addAttachment':\n addAttachment(model, op as AttachmentOperation);\n break;\n case 'updateAttachment':\n updateAttachment(model, op as AttachmentOperation);\n break;\n case 'removeAttachment':\n removeAttachment(model, op as AttachmentOperation);\n break;\n\n // Transition operations\n case 'addTransition':\n case 'updateTransition':\n case 'removeTransition':\n handleTransition(model, op as TransitionOperation);\n break;\n\n // Effect operations\n case 'addEffect':\n case 'updateEffect':\n case 'removeEffect':\n handleEffect(model, op as EffectOperation);\n break;\n\n default:\n break;\n }\n}\n\n// Track operations\nfunction addTrack(model: CompositionModel, op: TrackOperation): void {\n if (!op.track) return;\n\n const newTrack: Track = {\n id: op.track.id || `track_${Date.now()}`,\n kind: op.track.kind || 'video',\n clips: op.track.clips || [],\n effects: op.track.effects,\n duckingRules: op.track.duckingRules,\n };\n\n model.tracks.push(newTrack);\n rebuildIndexes(model);\n}\n\nfunction removeTrack(model: CompositionModel, op: TrackOperation): void {\n if (!op.trackId) return;\n\n const index = model.tracks.findIndex((t) => t.id === op.trackId);\n if (index === -1) return;\n\n model.tracks.splice(index, 1);\n rebuildIndexes(model);\n}\n\nfunction updateTrack(model: CompositionModel, op: TrackOperation): void {\n if (!op.trackId || !op.track) return;\n\n const track = model.findTrack(op.trackId);\n if (!track) return;\n\n Object.assign(track, op.track);\n}\n\n// Clip operations\nfunction addClip(model: CompositionModel, op: ClipOperation): void {\n const track = model.findTrack(op.trackId);\n if (!track || !op.clip) return;\n\n let newClip: Clip;\n\n // Create clip based on track kind\n const partialClip = op.clip as any;\n if (track.kind === 'video') {\n newClip = {\n id: op.clip.id || `clip_${Date.now()}`,\n trackKind: 'video',\n resourceId: partialClip.resourceId!,\n startUs: op.clip.startUs!,\n durationUs: op.clip.durationUs!,\n trimStartUs: op.clip.trimStartUs,\n trimEndUs: op.clip.trimEndUs,\n effects: op.clip.effects,\n attachments: op.clip.attachments,\n transitionIn: op.clip.transitionIn,\n transitionOut: op.clip.transitionOut,\n metadata: op.clip.metadata,\n };\n } else if (track.kind === 'audio') {\n newClip = {\n id: op.clip.id || `clip_${Date.now()}`,\n trackKind: 'audio',\n resourceId: partialClip.resourceId!,\n startUs: op.clip.startUs!,\n durationUs: op.clip.durationUs!,\n trimStartUs: op.clip.trimStartUs,\n trimEndUs: op.clip.trimEndUs,\n effects: op.clip.effects,\n attachments: op.clip.attachments,\n transitionIn: op.clip.transitionIn,\n transitionOut: op.clip.transitionOut,\n metadata: op.clip.metadata,\n };\n } else if (track.kind === 'caption') {\n newClip = {\n id: op.clip.id || `clip_${Date.now()}`,\n trackKind: 'caption',\n text: partialClip.text || '',\n startUs: op.clip.startUs!,\n durationUs: op.clip.durationUs!,\n effects: op.clip.effects,\n attachments: op.clip.attachments,\n transitionIn: op.clip.transitionIn,\n transitionOut: op.clip.transitionOut,\n metadata: op.clip.metadata,\n };\n } else {\n // fx clip\n newClip = {\n id: op.clip.id || `clip_${Date.now()}`,\n trackKind: 'fx',\n startUs: op.clip.startUs!,\n durationUs: op.clip.durationUs!,\n effects: op.clip.effects,\n attachments: op.clip.attachments,\n transitionIn: op.clip.transitionIn,\n transitionOut: op.clip.transitionOut,\n metadata: op.clip.metadata,\n };\n }\n\n // Insert clip in sorted position\n const insertIndex = track.clips.findIndex((c) => c.startUs > newClip.startUs);\n if (insertIndex === -1) {\n track.clips.push(newClip);\n } else {\n track.clips.splice(insertIndex, 0, newClip);\n }\n\n rebuildIndexes(model);\n}\n\nfunction removeClip(model: CompositionModel, op: ClipOperation): void {\n const track = model.findTrack(op.trackId);\n if (!track || !op.clipId) return;\n\n const clipIndex = track.clips.findIndex((c) => c.id === op.clipId);\n if (clipIndex === -1) return;\n\n track.clips.splice(clipIndex, 1);\n rebuildIndexes(model);\n}\n\nfunction updateClip(model: CompositionModel, op: ClipOperation): void {\n const clip = model.findClip(op.clipId!);\n if (!clip || !op.clip) return;\n\n Object.assign(clip, op.clip);\n\n // Re-sort if startUs changed\n if (op.clip.startUs !== undefined) {\n const track = model.findTrack(op.trackId);\n if (track) {\n track.clips.sort((a, b) => a.startUs - b.startUs);\n }\n }\n}\n\nfunction moveClip(model: CompositionModel, op: ClipOperation): void {\n if (!op.clipId || !op.targetTrackId) return;\n\n const sourceTrack = model.findTrack(op.trackId);\n const targetTrack = model.findTrack(op.targetTrackId);\n const clip = model.findClip(op.clipId);\n\n if (!sourceTrack || !targetTrack || !clip) return;\n\n // Remove from source track\n const clipIndex = sourceTrack.clips.findIndex((c) => c.id === op.clipId);\n if (clipIndex === -1) return;\n\n sourceTrack.clips.splice(clipIndex, 1);\n\n // Update position if specified\n if (op.targetStartUs !== undefined) {\n clip.startUs = op.targetStartUs;\n }\n\n // Add to target track in sorted position\n const insertIndex = targetTrack.clips.findIndex((c) => c.startUs > clip.startUs);\n if (insertIndex === -1) {\n targetTrack.clips.push(clip);\n } else {\n targetTrack.clips.splice(insertIndex, 0, clip);\n }\n\n rebuildIndexes(model);\n}\n\n// Resource operations\nfunction addResource(model: CompositionModel, op: ResourceOperation): void {\n if (!op.resource) return;\n\n const newResource: Resource = {\n id: op.resourceId,\n type: op.resource.type!,\n uri: op.resource.uri!,\n metadata: op.resource.metadata,\n state: op.resource.state || 'pending',\n };\n\n model.resources.set(op.resourceId, newResource);\n}\n\nfunction updateResource(model: CompositionModel, op: ResourceOperation): void {\n const resource = model.getResource(op.resourceId);\n if (!resource || !op.resource) return;\n\n Object.assign(resource, op.resource);\n}\n\nfunction removeResource(model: CompositionModel, op: ResourceOperation): void {\n if (!model.resources.has(op.resourceId)) return;\n\n model.resources.delete(op.resourceId);\n}\n\n// Attachment operations\nfunction addAttachment(model: CompositionModel, op: AttachmentOperation): void {\n const clip = model.findClip(op.clipId);\n if (!clip || !op.attachment) return;\n\n if (!clip.attachments) {\n clip.attachments = [];\n }\n\n const newAttachment: Attachment = {\n id: op.attachment.id || `attachment_${Date.now()}`,\n kind: op.attachment.kind!,\n startUs: op.attachment.startUs!,\n durationUs: op.attachment.durationUs!,\n data: op.attachment.data!,\n };\n\n clip.attachments.push(newAttachment);\n}\n\nfunction updateAttachment(model: CompositionModel, op: AttachmentOperation): void {\n const clip = model.findClip(op.clipId);\n if (!clip || !clip.attachments || !op.attachmentId || !op.attachment) return;\n\n const attachment = clip.attachments.find((a) => a.id === op.attachmentId);\n if (!attachment) return;\n\n Object.assign(attachment, op.attachment);\n}\n\nfunction removeAttachment(model: CompositionModel, op: AttachmentOperation): void {\n const clip = model.findClip(op.clipId);\n if (!clip || !clip.attachments || !op.attachmentId) return;\n\n const attachmentIndex = clip.attachments.findIndex((a) => a.id === op.attachmentId);\n if (attachmentIndex === -1) return;\n\n clip.attachments.splice(attachmentIndex, 1);\n}\n\n// Transition operations\nfunction handleTransition(model: CompositionModel, op: TransitionOperation): void {\n const clip = model.findClip(op.clipId);\n if (!clip) return;\n\n if (op.position === 'in') {\n if (op.type === 'removeTransition') {\n clip.transitionIn = undefined;\n } else {\n clip.transitionIn = op.transition as Transition;\n }\n } else {\n if (op.type === 'removeTransition') {\n clip.transitionOut = undefined;\n } else {\n clip.transitionOut = op.transition as Transition;\n }\n }\n}\n\n// Effect operations\nfunction handleEffect(model: CompositionModel, op: EffectOperation): void {\n if (op.targetType === 'track') {\n const track = model.findTrack(op.targetId);\n if (!track) return;\n\n if (!track.effects) track.effects = [];\n\n if (op.type === 'addEffect' && op.effect) {\n track.effects.push(op.effect as Effect);\n } else if (op.type === 'removeEffect' && op.effectId) {\n const index = track.effects.findIndex((e) => e.id === op.effectId);\n if (index !== -1) track.effects.splice(index, 1);\n } else if (op.type === 'updateEffect' && op.effectId && op.effect) {\n const effect = track.effects.find((e) => e.id === op.effectId);\n if (effect) Object.assign(effect, op.effect);\n }\n } else {\n const clip = model.findClip(op.targetId);\n if (!clip) return;\n\n if (!clip.effects) clip.effects = [];\n\n if (op.type === 'addEffect' && op.effect) {\n clip.effects.push(op.effect as Effect);\n } else if (op.type === 'removeEffect' && op.effectId) {\n const index = clip.effects.findIndex((e) => e.id === op.effectId);\n if (index !== -1) clip.effects.splice(index, 1);\n } else if (op.type === 'updateEffect' && op.effectId && op.effect) {\n const effect = clip.effects.find((e) => e.id === op.effectId);\n if (effect) Object.assign(effect, op.effect);\n }\n }\n}\n\n// Helper functions\nfunction rebuildIndexes(model: CompositionModel): void {\n // Trigger index rebuild in CompositionModel (buildIndexes also calculates durationUs)\n (model as any).buildIndexes();\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"patch.js","sources":["../../src/model/patch.ts"],"sourcesContent":["import { CompositionModel } from './CompositionModel';\nimport {\n CompositionPatch,\n PatchOperation,\n TrackOperation,\n ClipOperation,\n ResourceOperation,\n AttachmentOperation,\n TransitionOperation,\n EffectOperation,\n hasResourceId,\n Track,\n Clip,\n Resource,\n Attachment,\n Transition,\n Effect,\n} from './types';\n\n/**\n * Apply patch to model and return affected clip IDs\n * Simplified for 2-Clip strategy - no complex time range calculation needed\n */\nexport function applyPatch(model: CompositionModel, patch: CompositionPatch): Set<string> {\n const affectedClips = new Set<string>();\n\n // Apply all operations and collect affected clips\n for (const op of patch.operations) {\n applyOperation(model, op);\n collectAffectedClips(model, op, affectedClips);\n }\n\n return affectedClips;\n}\n\n/**\n * Collect clips affected by a patch operation\n */\nfunction collectAffectedClips(\n model: CompositionModel,\n op: PatchOperation,\n affectedClips: Set<string>\n): void {\n switch (op.type) {\n // Track operations affect all clips in track\n case 'addTrack':\n case 'removeTrack':\n case 'updateTrack': {\n const trackOp = op as TrackOperation;\n if (trackOp.trackId) {\n const track = model.findTrack(trackOp.trackId);\n if (track) {\n track.clips.forEach((clip) => affectedClips.add(clip.id));\n }\n }\n break;\n }\n\n // Clip operations\n case 'addClip':\n case 'removeClip':\n case 'updateClip':\n case 'moveClip': {\n const clipOp = op as ClipOperation;\n if (clipOp.clipId) {\n affectedClips.add(clipOp.clipId);\n }\n break;\n }\n\n // Resource changes affect all clips using that resource\n case 'addResource':\n case 'updateResource':\n case 'removeResource': {\n const resourceOp = op as ResourceOperation;\n if (resourceOp.resourceId) {\n for (const track of model.tracks) {\n for (const clip of track.clips) {\n if (hasResourceId(clip) && clip.resourceId === resourceOp.resourceId) {\n affectedClips.add(clip.id);\n }\n }\n }\n }\n break;\n }\n\n // Attachment operations\n case 'addAttachment':\n case 'updateAttachment':\n case 'removeAttachment': {\n const attachmentOp = op as AttachmentOperation;\n if (attachmentOp.clipId) {\n affectedClips.add(attachmentOp.clipId);\n }\n break;\n }\n\n // Transition operations\n case 'addTransition':\n case 'updateTransition':\n case 'removeTransition': {\n const transitionOp = op as TransitionOperation;\n if (transitionOp.clipId) {\n affectedClips.add(transitionOp.clipId);\n }\n break;\n }\n\n // Effect operations\n case 'addEffect':\n case 'updateEffect':\n case 'removeEffect': {\n const effectOp = op as EffectOperation;\n if (effectOp.targetType === 'track' && effectOp.targetId) {\n const track = model.findTrack(effectOp.targetId);\n if (track) {\n track.clips.forEach((clip) => affectedClips.add(clip.id));\n }\n } else if (effectOp.targetType === 'clip' && effectOp.targetId) {\n affectedClips.add(effectOp.targetId);\n }\n break;\n }\n }\n}\n\nfunction applyOperation(model: CompositionModel, op: PatchOperation): void {\n switch (op.type) {\n // Track operations\n case 'addTrack':\n addTrack(model, op as TrackOperation);\n break;\n case 'removeTrack':\n removeTrack(model, op as TrackOperation);\n break;\n case 'updateTrack':\n updateTrack(model, op as TrackOperation);\n break;\n\n // Clip operations\n case 'addClip':\n addClip(model, op as ClipOperation);\n break;\n case 'removeClip':\n removeClip(model, op as ClipOperation);\n break;\n case 'updateClip':\n updateClip(model, op as ClipOperation);\n break;\n case 'moveClip':\n moveClip(model, op as ClipOperation);\n break;\n\n // Resource operations\n case 'addResource':\n addResource(model, op as ResourceOperation);\n break;\n case 'updateResource':\n updateResource(model, op as ResourceOperation);\n break;\n case 'removeResource':\n removeResource(model, op as ResourceOperation);\n break;\n\n // Attachment operations\n case 'addAttachment':\n addAttachment(model, op as AttachmentOperation);\n break;\n case 'updateAttachment':\n updateAttachment(model, op as AttachmentOperation);\n break;\n case 'removeAttachment':\n removeAttachment(model, op as AttachmentOperation);\n break;\n\n // Transition operations\n case 'addTransition':\n case 'updateTransition':\n case 'removeTransition':\n handleTransition(model, op as TransitionOperation);\n break;\n\n // Effect operations\n case 'addEffect':\n case 'updateEffect':\n case 'removeEffect':\n handleEffect(model, op as EffectOperation);\n break;\n\n default:\n break;\n }\n}\n\n// Track operations\nfunction addTrack(model: CompositionModel, op: TrackOperation): void {\n if (!op.track) return;\n\n const newTrack: Track = {\n id: op.track.id || `track_${Date.now()}`,\n kind: op.track.kind || 'video',\n clips: op.track.clips || [],\n effects: op.track.effects,\n duckingRules: op.track.duckingRules,\n };\n\n model.tracks.push(newTrack);\n rebuildIndexes(model);\n}\n\nfunction removeTrack(model: CompositionModel, op: TrackOperation): void {\n if (!op.trackId) return;\n\n const index = model.tracks.findIndex((t) => t.id === op.trackId);\n if (index === -1) return;\n\n model.tracks.splice(index, 1);\n rebuildIndexes(model);\n}\n\nfunction updateTrack(model: CompositionModel, op: TrackOperation): void {\n if (!op.trackId || !op.track) return;\n\n const track = model.findTrack(op.trackId);\n if (!track) return;\n\n Object.assign(track, op.track);\n}\n\n// Clip operations\nfunction addClip(model: CompositionModel, op: ClipOperation): void {\n const track = model.findTrack(op.trackId);\n if (!track || !op.clip) return;\n\n let newClip: Clip;\n\n // Create clip based on track kind\n const partialClip = op.clip as any;\n if (track.kind === 'video') {\n newClip = {\n id: op.clip.id || `clip_${Date.now()}`,\n trackKind: 'video',\n resourceId: partialClip.resourceId!,\n startUs: op.clip.startUs!,\n durationUs: op.clip.durationUs!,\n trimStartUs: op.clip.trimStartUs,\n trimEndUs: op.clip.trimEndUs,\n effects: op.clip.effects,\n attachments: op.clip.attachments,\n transitionIn: op.clip.transitionIn,\n transitionOut: op.clip.transitionOut,\n metadata: op.clip.metadata,\n };\n } else if (track.kind === 'audio') {\n newClip = {\n id: op.clip.id || `clip_${Date.now()}`,\n trackKind: 'audio',\n resourceId: partialClip.resourceId!,\n startUs: op.clip.startUs!,\n durationUs: op.clip.durationUs!,\n trimStartUs: op.clip.trimStartUs,\n trimEndUs: op.clip.trimEndUs,\n effects: op.clip.effects,\n attachments: op.clip.attachments,\n transitionIn: op.clip.transitionIn,\n transitionOut: op.clip.transitionOut,\n metadata: op.clip.metadata,\n };\n } else if (track.kind === 'caption') {\n newClip = {\n id: op.clip.id || `clip_${Date.now()}`,\n trackKind: 'caption',\n text: partialClip.text || '',\n startUs: op.clip.startUs!,\n durationUs: op.clip.durationUs!,\n effects: op.clip.effects,\n attachments: op.clip.attachments,\n transitionIn: op.clip.transitionIn,\n transitionOut: op.clip.transitionOut,\n metadata: op.clip.metadata,\n };\n } else {\n // fx clip\n newClip = {\n id: op.clip.id || `clip_${Date.now()}`,\n trackKind: 'fx',\n startUs: op.clip.startUs!,\n durationUs: op.clip.durationUs!,\n effects: op.clip.effects,\n attachments: op.clip.attachments,\n transitionIn: op.clip.transitionIn,\n transitionOut: op.clip.transitionOut,\n metadata: op.clip.metadata,\n };\n }\n\n // Insert clip in sorted position\n const insertIndex = track.clips.findIndex((c) => c.startUs > newClip.startUs);\n if (insertIndex === -1) {\n track.clips.push(newClip);\n } else {\n track.clips.splice(insertIndex, 0, newClip);\n }\n\n rebuildIndexes(model);\n}\n\nfunction removeClip(model: CompositionModel, op: ClipOperation): void {\n const track = model.findTrack(op.trackId);\n if (!track || !op.clipId) return;\n\n const clipIndex = track.clips.findIndex((c) => c.id === op.clipId);\n if (clipIndex === -1) return;\n\n track.clips.splice(clipIndex, 1);\n rebuildIndexes(model);\n}\n\nfunction updateClip(model: CompositionModel, op: ClipOperation): void {\n const clip = model.findClip(op.clipId!);\n if (!clip || !op.clip) return;\n\n // Save old resourceId before update\n const oldResourceId = hasResourceId(clip) ? clip.resourceId : undefined;\n\n Object.assign(clip, op.clip);\n\n // Re-sort if startUs changed\n if (op.clip.startUs !== undefined) {\n const track = model.findTrack(op.trackId);\n if (track) {\n track.clips.sort((a, b) => a.startUs - b.startUs);\n }\n }\n\n // Incrementally update resource index if resourceId changed\n const newResourceId = 'resourceId' in op.clip ? op.clip.resourceId : undefined;\n if (newResourceId !== undefined && newResourceId !== oldResourceId) {\n (clip as any).oldResourceId = oldResourceId;\n model.updateClipResourceIndex(op.clipId!, oldResourceId, newResourceId);\n }\n\n // Recalculate duration if time-related fields changed\n if (op.clip.startUs !== undefined || op.clip.durationUs !== undefined) {\n model.recalculateDuration();\n }\n}\n\nfunction moveClip(model: CompositionModel, op: ClipOperation): void {\n if (!op.clipId || !op.targetTrackId) return;\n\n const sourceTrack = model.findTrack(op.trackId);\n const targetTrack = model.findTrack(op.targetTrackId);\n const clip = model.findClip(op.clipId);\n\n if (!sourceTrack || !targetTrack || !clip) return;\n\n // Remove from source track\n const clipIndex = sourceTrack.clips.findIndex((c) => c.id === op.clipId);\n if (clipIndex === -1) return;\n\n sourceTrack.clips.splice(clipIndex, 1);\n\n // Update position if specified\n if (op.targetStartUs !== undefined) {\n clip.startUs = op.targetStartUs;\n }\n\n // Add to target track in sorted position\n const insertIndex = targetTrack.clips.findIndex((c) => c.startUs > clip.startUs);\n if (insertIndex === -1) {\n targetTrack.clips.push(clip);\n } else {\n targetTrack.clips.splice(insertIndex, 0, clip);\n }\n\n rebuildIndexes(model);\n}\n\n// Resource operations\nfunction addResource(model: CompositionModel, op: ResourceOperation): void {\n if (!op.resource) return;\n\n const newResource: Resource = {\n id: op.resourceId,\n type: op.resource.type!,\n uri: op.resource.uri!,\n metadata: op.resource.metadata,\n state: op.resource.state || 'pending',\n };\n\n model.resources.set(op.resourceId, newResource);\n}\n\nfunction updateResource(model: CompositionModel, op: ResourceOperation): void {\n const resource = model.getResource(op.resourceId);\n if (!resource || !op.resource) return;\n\n Object.assign(resource, op.resource);\n}\n\nfunction removeResource(model: CompositionModel, op: ResourceOperation): void {\n if (!model.resources.has(op.resourceId)) return;\n\n model.resources.delete(op.resourceId);\n}\n\n// Attachment operations\nfunction addAttachment(model: CompositionModel, op: AttachmentOperation): void {\n const clip = model.findClip(op.clipId);\n if (!clip || !op.attachment) return;\n\n if (!clip.attachments) {\n clip.attachments = [];\n }\n\n const newAttachment: Attachment = {\n id: op.attachment.id || `attachment_${Date.now()}`,\n kind: op.attachment.kind!,\n startUs: op.attachment.startUs!,\n durationUs: op.attachment.durationUs!,\n data: op.attachment.data!,\n };\n\n clip.attachments.push(newAttachment);\n}\n\nfunction updateAttachment(model: CompositionModel, op: AttachmentOperation): void {\n const clip = model.findClip(op.clipId);\n if (!clip || !clip.attachments || !op.attachmentId || !op.attachment) return;\n\n const attachment = clip.attachments.find((a) => a.id === op.attachmentId);\n if (!attachment) return;\n\n Object.assign(attachment, op.attachment);\n}\n\nfunction removeAttachment(model: CompositionModel, op: AttachmentOperation): void {\n const clip = model.findClip(op.clipId);\n if (!clip || !clip.attachments || !op.attachmentId) return;\n\n const attachmentIndex = clip.attachments.findIndex((a) => a.id === op.attachmentId);\n if (attachmentIndex === -1) return;\n\n clip.attachments.splice(attachmentIndex, 1);\n}\n\n// Transition operations\nfunction handleTransition(model: CompositionModel, op: TransitionOperation): void {\n const clip = model.findClip(op.clipId);\n if (!clip) return;\n\n if (op.position === 'in') {\n if (op.type === 'removeTransition') {\n clip.transitionIn = undefined;\n } else {\n clip.transitionIn = op.transition as Transition;\n }\n } else {\n if (op.type === 'removeTransition') {\n clip.transitionOut = undefined;\n } else {\n clip.transitionOut = op.transition as Transition;\n }\n }\n}\n\n// Effect operations\nfunction handleEffect(model: CompositionModel, op: EffectOperation): void {\n if (op.targetType === 'track') {\n const track = model.findTrack(op.targetId);\n if (!track) return;\n\n if (!track.effects) track.effects = [];\n\n if (op.type === 'addEffect' && op.effect) {\n track.effects.push(op.effect as Effect);\n } else if (op.type === 'removeEffect' && op.effectId) {\n const index = track.effects.findIndex((e) => e.id === op.effectId);\n if (index !== -1) track.effects.splice(index, 1);\n } else if (op.type === 'updateEffect' && op.effectId && op.effect) {\n const effect = track.effects.find((e) => e.id === op.effectId);\n if (effect) Object.assign(effect, op.effect);\n }\n } else {\n const clip = model.findClip(op.targetId);\n if (!clip) return;\n\n if (!clip.effects) clip.effects = [];\n\n if (op.type === 'addEffect' && op.effect) {\n clip.effects.push(op.effect as Effect);\n } else if (op.type === 'removeEffect' && op.effectId) {\n const index = clip.effects.findIndex((e) => e.id === op.effectId);\n if (index !== -1) clip.effects.splice(index, 1);\n } else if (op.type === 'updateEffect' && op.effectId && op.effect) {\n const effect = clip.effects.find((e) => e.id === op.effectId);\n if (effect) Object.assign(effect, op.effect);\n }\n }\n}\n\n// Helper functions\nfunction rebuildIndexes(model: CompositionModel): void {\n // Trigger index rebuild in CompositionModel (buildIndexes also calculates durationUs)\n (model as any).buildIndexes();\n}\n"],"names":[],"mappings":"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"}
@@ -19,7 +19,7 @@ class ClipSessionManager {
19
19
  for (const [clipId, entry] of this.entries) {
20
20
  if (!targetSet.has(clipId) && entry.state === "active") {
21
21
  await this.deactivateClip(clipId);
22
- this.cacheManager.evictClip(clipId);
22
+ this.cacheManager.invalidateClipInL1(clipId);
23
23
  }
24
24
  }
25
25
  this.activeSet = targetSet;
@@ -1 +1 @@
1
- {"version":3,"file":"ClipSessionManager.js","sources":["../../src/orchestrator/ClipSessionManager.ts"],"sourcesContent":["/**\n * ClipSessionManager: Manages VideoClipSession lifecycle with 2-Clip strategy\n *\n * Responsibilities:\n * - Maintains Prev/Current/Next clip sessions\n * - Controls concurrent clip activation (max 2)\n * - Evicts clips outside the active window\n */\n\nimport type { VideoClipSession } from './VideoClipSession';\nimport { ClipUpdateResult } from './CompositionPlanner';\n\nimport type { CacheManager } from '../cache/CacheManager';\n\nexport interface ClipSessionFactory {\n createSession(clipId: string): Promise<VideoClipSession>;\n}\n\nexport type ClipSessionState = 'idle' | 'activating' | 'active' | 'failed';\n\ninterface SessionEntry {\n clipId: string;\n session?: VideoClipSession;\n state: ClipSessionState;\n activationPromise?: Promise<void>;\n lastAccess: number;\n retryCount: number;\n}\n\n/**\n * ClipSessionManager manages VideoClipSession instances using 2-Clip strategy\n *\n * At any time, maintains:\n * - Current clip (being played)\n * - Next clip (for forward playback)\n */\nexport class ClipSessionManager {\n private entries = new Map<string, SessionEntry>();\n private activeSet = new Set<string>();\n private inflightCount = 0;\n private readonly maxConcurrent: number;\n private readonly factory: ClipSessionFactory;\n private readonly cacheManager: CacheManager;\n\n constructor(config: {\n maxConcurrent?: number;\n factory: ClipSessionFactory;\n cacheManager: CacheManager;\n }) {\n this.maxConcurrent = config.maxConcurrent ?? 4;\n this.factory = config.factory;\n this.cacheManager = config.cacheManager;\n }\n\n /**\n * Ensure clips are active (Current, Next)\n * Deactivates clips outside the set\n */\n async ensureClips(clipIds: string[]): Promise<void> {\n const targetSet = new Set(clipIds);\n\n // Deactivate clips not in target set\n for (const [clipId, entry] of this.entries) {\n if (!targetSet.has(clipId) && entry.state === 'active') {\n await this.deactivateClip(clipId);\n this.cacheManager.evictClip(clipId);\n }\n }\n\n // Update active set\n this.activeSet = targetSet;\n\n // Activate missing clips\n const toActivate: string[] = [];\n for (const clipId of clipIds) {\n const entry = this.entries.get(clipId);\n if (!entry || entry.state === 'idle' || entry.state === 'failed') {\n toActivate.push(clipId);\n }\n }\n // Start activation with concurrency control\n for (const clipId of toActivate) {\n await this.enqueueActivation(clipId);\n }\n }\n\n async handlePlannerUpdate(clipId: string, update: ClipUpdateResult): Promise<void> {\n const session = this.getSession(clipId);\n if (!session) return;\n await session.handlePlannerUpdate(update);\n }\n\n /**\n * Get session for a clip\n */\n getSession(clipId: string): VideoClipSession | undefined {\n return this.entries.get(clipId)?.session;\n }\n\n /**\n * Check if a clip is active\n */\n isClipActive(clipId: string): boolean {\n const entry = this.entries.get(clipId);\n return entry?.state === 'active' || false;\n }\n\n /**\n * Get clip state\n */\n getClipState(clipId: string): ClipSessionState {\n return this.entries.get(clipId)?.state ?? 'idle';\n }\n\n /**\n * Get metrics for monitoring\n */\n getMetrics() {\n const states = { idle: 0, activating: 0, active: 0, failed: 0 };\n for (const entry of this.entries.values()) {\n states[entry.state]++;\n }\n\n return {\n total: this.entries.size,\n inflight: this.inflightCount,\n activeSessions: this.activeSet.size,\n ...states,\n };\n }\n\n /**\n * Clear all sessions\n */\n async clear(): Promise<void> {\n for (const clipId of this.entries.keys()) {\n await this.deactivateClip(clipId);\n }\n this.entries.clear();\n this.activeSet.clear();\n }\n\n /**\n * Dispose and cleanup\n */\n async dispose(): Promise<void> {\n await this.clear();\n }\n\n private async enqueueActivation(clipId: string): Promise<void> {\n let entry = this.entries.get(clipId);\n\n if (!entry) {\n entry = {\n clipId,\n state: 'idle',\n lastAccess: Date.now(),\n retryCount: 0,\n };\n this.entries.set(clipId, entry);\n }\n\n // Skip if already activating or active\n if (entry.state === 'activating' || entry.state === 'active') {\n return entry.activationPromise;\n }\n\n // Wait if max concurrent reached\n while (this.inflightCount >= this.maxConcurrent) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n entry.state = 'activating';\n entry.lastAccess = Date.now();\n this.inflightCount++;\n\n const activationPromise = this.activateClip(entry);\n entry.activationPromise = activationPromise;\n\n return activationPromise;\n }\n\n private async activateClip(entry: SessionEntry): Promise<void> {\n try {\n const session = await this.factory.createSession(entry.clipId);\n await session.activate();\n entry.session = session;\n entry.state = 'active';\n entry.retryCount = 0;\n } catch (error) {\n console.error(`[ClipSessionManager] Failed to activate clip ${entry.clipId}:`, error);\n entry.state = 'failed';\n entry.retryCount++;\n\n // Retry with exponential backoff (max 3 retries)\n if (entry.retryCount < 3) {\n const delay = Math.min(1000 * Math.pow(2, entry.retryCount), 5000);\n setTimeout(() => {\n if (this.activeSet.has(entry.clipId)) {\n entry.state = 'idle';\n void this.enqueueActivation(entry.clipId);\n }\n }, delay);\n }\n } finally {\n this.inflightCount--;\n entry.activationPromise = undefined;\n }\n }\n\n private async deactivateClip(clipId: string): Promise<void> {\n const entry = this.entries.get(clipId);\n if (!entry) return;\n\n if (entry.session) {\n await entry.session.dispose();\n entry.session = undefined;\n }\n\n entry.state = 'idle';\n this.entries.delete(clipId);\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ClipSessionManager.js","sources":["../../src/orchestrator/ClipSessionManager.ts"],"sourcesContent":["/**\n * ClipSessionManager: Manages VideoClipSession lifecycle with 2-Clip strategy\n *\n * Responsibilities:\n * - Maintains Prev/Current/Next clip sessions\n * - Controls concurrent clip activation (max 2)\n * - Evicts clips outside the active window\n */\n\nimport type { VideoClipSession } from './VideoClipSession';\nimport { ClipUpdateResult } from './CompositionPlanner';\n\nimport type { CacheManager } from '../cache/CacheManager';\n\nexport interface ClipSessionFactory {\n createSession(clipId: string): Promise<VideoClipSession>;\n}\n\nexport type ClipSessionState = 'idle' | 'activating' | 'active' | 'failed';\n\ninterface SessionEntry {\n clipId: string;\n session?: VideoClipSession;\n state: ClipSessionState;\n activationPromise?: Promise<void>;\n lastAccess: number;\n retryCount: number;\n}\n\n/**\n * ClipSessionManager manages VideoClipSession instances using 2-Clip strategy\n *\n * At any time, maintains:\n * - Current clip (being played)\n * - Next clip (for forward playback)\n */\nexport class ClipSessionManager {\n private entries = new Map<string, SessionEntry>();\n private activeSet = new Set<string>();\n private inflightCount = 0;\n private readonly maxConcurrent: number;\n private readonly factory: ClipSessionFactory;\n private readonly cacheManager: CacheManager;\n\n constructor(config: {\n maxConcurrent?: number;\n factory: ClipSessionFactory;\n cacheManager: CacheManager;\n }) {\n this.maxConcurrent = config.maxConcurrent ?? 4;\n this.factory = config.factory;\n this.cacheManager = config.cacheManager;\n }\n\n /**\n * Ensure clips are active (Current, Next)\n * Deactivates clips outside the set\n */\n async ensureClips(clipIds: string[]): Promise<void> {\n const targetSet = new Set(clipIds);\n\n // Deactivate clips not in target set\n for (const [clipId, entry] of this.entries) {\n if (!targetSet.has(clipId) && entry.state === 'active') {\n await this.deactivateClip(clipId);\n this.cacheManager.invalidateClipInL1(clipId);\n }\n }\n\n // Update active set\n this.activeSet = targetSet;\n\n // Activate missing clips\n const toActivate: string[] = [];\n for (const clipId of clipIds) {\n const entry = this.entries.get(clipId);\n if (!entry || entry.state === 'idle' || entry.state === 'failed') {\n toActivate.push(clipId);\n }\n }\n // Start activation with concurrency control\n for (const clipId of toActivate) {\n await this.enqueueActivation(clipId);\n }\n }\n\n async handlePlannerUpdate(clipId: string, update: ClipUpdateResult): Promise<void> {\n const session = this.getSession(clipId);\n if (!session) return;\n await session.handlePlannerUpdate(update);\n }\n\n /**\n * Get session for a clip\n */\n getSession(clipId: string): VideoClipSession | undefined {\n return this.entries.get(clipId)?.session;\n }\n\n /**\n * Check if a clip is active\n */\n isClipActive(clipId: string): boolean {\n const entry = this.entries.get(clipId);\n return entry?.state === 'active' || false;\n }\n\n /**\n * Get clip state\n */\n getClipState(clipId: string): ClipSessionState {\n return this.entries.get(clipId)?.state ?? 'idle';\n }\n\n /**\n * Get metrics for monitoring\n */\n getMetrics() {\n const states = { idle: 0, activating: 0, active: 0, failed: 0 };\n for (const entry of this.entries.values()) {\n states[entry.state]++;\n }\n\n return {\n total: this.entries.size,\n inflight: this.inflightCount,\n activeSessions: this.activeSet.size,\n ...states,\n };\n }\n\n /**\n * Clear all sessions\n */\n async clear(): Promise<void> {\n for (const clipId of this.entries.keys()) {\n await this.deactivateClip(clipId);\n }\n this.entries.clear();\n this.activeSet.clear();\n }\n\n /**\n * Dispose and cleanup\n */\n async dispose(): Promise<void> {\n await this.clear();\n }\n\n private async enqueueActivation(clipId: string): Promise<void> {\n let entry = this.entries.get(clipId);\n\n if (!entry) {\n entry = {\n clipId,\n state: 'idle',\n lastAccess: Date.now(),\n retryCount: 0,\n };\n this.entries.set(clipId, entry);\n }\n\n // Skip if already activating or active\n if (entry.state === 'activating' || entry.state === 'active') {\n return entry.activationPromise;\n }\n\n // Wait if max concurrent reached\n while (this.inflightCount >= this.maxConcurrent) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n entry.state = 'activating';\n entry.lastAccess = Date.now();\n this.inflightCount++;\n\n const activationPromise = this.activateClip(entry);\n entry.activationPromise = activationPromise;\n\n return activationPromise;\n }\n\n private async activateClip(entry: SessionEntry): Promise<void> {\n try {\n const session = await this.factory.createSession(entry.clipId);\n await session.activate();\n entry.session = session;\n entry.state = 'active';\n entry.retryCount = 0;\n } catch (error) {\n console.error(`[ClipSessionManager] Failed to activate clip ${entry.clipId}:`, error);\n entry.state = 'failed';\n entry.retryCount++;\n\n // Retry with exponential backoff (max 3 retries)\n if (entry.retryCount < 3) {\n const delay = Math.min(1000 * Math.pow(2, entry.retryCount), 5000);\n setTimeout(() => {\n if (this.activeSet.has(entry.clipId)) {\n entry.state = 'idle';\n void this.enqueueActivation(entry.clipId);\n }\n }, delay);\n }\n } finally {\n this.inflightCount--;\n entry.activationPromise = undefined;\n }\n }\n\n private async deactivateClip(clipId: string): Promise<void> {\n const entry = this.entries.get(clipId);\n if (!entry) return;\n\n if (entry.session) {\n await entry.session.dispose();\n entry.session = undefined;\n }\n\n entry.state = 'idle';\n this.entries.delete(clipId);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"Orchestrator.d.ts","sourceRoot":"","sources":["../../src/orchestrator/Orchestrator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Orchestrator.d.ts","sourceRoot":"","sources":["../../src/orchestrator/Orchestrator.ts"],"names":[],"mappings":"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"}
@@ -142,20 +142,12 @@ class Orchestrator {
142
142
  operations: patch.operations.length,
143
143
  affectedClips: Array.from(affectedClipIds)
144
144
  });
145
- const activeClipIds = new Set(
146
- Array.from(this.clipSessionManager["entries"].keys()).filter(
147
- (clipId) => this.clipSessionManager.isClipActive(clipId)
148
- )
149
- );
150
145
  for (const update of clipUpdates) {
151
146
  if (update.type === "remove") {
152
147
  this.activeClips.delete(update.clipId);
153
- this.cacheManager.evictClip(update.clipId);
154
148
  }
149
+ this.cacheManager.invalidateClip(update.clipId);
155
150
  await this.clipSessionManager.handlePlannerUpdate(update.clipId, update);
156
- if (activeClipIds.has(update.clipId) && update.type !== "remove") {
157
- this.cacheManager.evictClip(update.clipId);
158
- }
159
151
  }
160
152
  }
161
153
  handleResourceStateChange(resourceId, state) {
@@ -1 +1 @@
1
- {"version":3,"file":"Orchestrator.js","sources":["../../src/orchestrator/Orchestrator.ts"],"sourcesContent":["import { EventBus } from '../event/EventBus';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { applyPatch as applyModelPatch } from '../model/patch';\nimport { ResourceConflictError, ResourceLoader } from '../stages/load/ResourceLoader';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ConfigLoader } from '../config/ConfigLoader';\nimport type { IOrchestrator, OrchestratorConfig, RenderFrameOptions } from './types';\nimport { WorkerStatus, WorkerType } from '../worker/types';\nimport { CompositionModel, CompositionPatch, Resource, TimeUs, RcFrame, Clip } from '../model';\nimport { hasResourceId } from '../model/types';\nimport { MeframeEvent, type EventPayloadMap } from '../event/events';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { VideoClipSession } from './VideoClipSession';\nimport { ClipSessionManager } from './ClipSessionManager';\nimport { GlobalAudioSession } from '../stages/compose/GlobalAudioSession';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { ExportOptions } from '@/types';\nimport { VideoChunkDecoder } from '../stages/decode/VideoChunkDecoder';\nimport { quantizeTimestampToFrame } from '../utils/time-utils';\n\nexport class Orchestrator implements IOrchestrator {\n workers: WorkerPool;\n eventBus: EventBus<EventPayloadMap>;\n compositionModel: CompositionModel | null = null;\n resourceLoader: ResourceLoader;\n cacheManager: CacheManager;\n planner: CompositionPlanner;\n audioSession: GlobalAudioSession;\n muxManager: MuxManager;\n\n private activeClips = new Set<string>();\n private isInitialized = false;\n private config = ConfigLoader.getInstance().getConfig();\n private clipSessionManager: ClipSessionManager;\n private currentClipId: string | null = null;\n private ensureCacheDebounceTimer: number | null = null;\n private readonly ensureCacheDebounceDelay = 150;\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n constructor(config: OrchestratorConfig) {\n // Use provided eventBus or create a new one\n this.eventBus = config.eventBus || new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n\n // Initialize config first\n this.config = ConfigLoader.getInstance().getConfig();\n\n const workerConfigs = this.buildWorkerConfigs();\n\n // Initialize WorkerPool with worker path from config\n this.workers = new WorkerPool({\n eventBus: this.eventBus,\n workerConfigs,\n workerPath: config.workerPath,\n workerExtension: config.workerExtension,\n });\n\n this.resourceLoader = new ResourceLoader({\n orchestrator: this as any,\n eventBus: this.eventBus,\n config: {\n maxConcurrent: config.maxWorkers || (this.config.load as any)?.retry?.maxAttempts || 4,\n },\n onStateChange: (resourceId, state) => this.handleResourceStateChange(resourceId, state),\n });\n\n this.planner = new CompositionPlanner();\n\n const cacheConfig = config.cacheConfig || this.config.cache;\n this.cacheManager = new CacheManager(\n {\n l1: {\n maxMemoryMB:\n (cacheConfig as any)?.l1Size || (cacheConfig as any)?.l1?.maxMemoryMB || 1024,\n maxGOPs: (this.config.decode as any)?.video?.maxGOPs || 4,\n },\n l2: {\n maxSizeMB: (cacheConfig as any)?.l2Size || (cacheConfig as any)?.l2?.maxSizeMB || 2048,\n projectId: 'default',\n },\n },\n this.eventBus\n );\n\n this.clipSessionManager = new ClipSessionManager({\n maxConcurrent: 2,\n factory: {\n createSession: (clipId) => this.createSession(clipId),\n },\n cacheManager: this.cacheManager,\n });\n\n this.audioSession = new GlobalAudioSession({\n cacheManager: this.cacheManager,\n workers: this.workers,\n resourceLoader: this.resourceLoader,\n eventBus: this.eventBus,\n getModel: () => this.compositionModel,\n buildWorkerConfigs: () => this.buildWorkerConfigs(),\n });\n\n this.muxManager = new MuxManager(\n this.cacheManager,\n this.audioSession,\n this.config.encode.audio as AudioEncoderConfig\n );\n }\n\n get workerStatus(): WorkerStatus {\n const status = this.workers.status;\n const result: WorkerStatus = {} as WorkerStatus;\n\n const workerTypes: WorkerType[] = [\n 'videoDemux',\n 'audioDemux',\n 'videoDecode',\n 'audioDecode',\n 'videoCompose',\n 'audioCompose',\n 'videoEncode',\n ];\n\n for (const type of workerTypes) {\n result[type] = status[type] || {\n state: 'idle',\n taskCount: 0,\n };\n }\n\n return result;\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n\n await this.cacheManager.init();\n\n this.isInitialized = true;\n }\n\n // Event methods - forward to eventBus\n on<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.on(event, handler);\n }\n\n off<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.off(event, handler);\n }\n\n once<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.once(event, handler);\n }\n\n async setCompositionModel(model: CompositionModel): Promise<void> {\n this.compositionModel = model;\n this.planner.setModel(model);\n this.currentClipId = null;\n\n this.eventBus.emit(MeframeEvent.ModelSet, model);\n\n this.eventBus.emit(MeframeEvent.CompositionUpdated, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce((acc: number, track: any) => acc + track.clips.length, 0),\n durationUs: model.durationUs,\n });\n\n await this.audioSession.activateAllAudioClips();\n }\n\n async applyPatch(patch: CompositionPatch): Promise<void> {\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n // Apply patch and get affected clip IDs (simplified for 2-Clip strategy)\n const affectedClipIds = applyModelPatch(this.compositionModel, patch);\n const clipUpdates = this.planner.applyPatch(patch, affectedClipIds);\n\n this.eventBus.emit(MeframeEvent.PatchApplied, {\n operations: patch.operations.length,\n affectedClips: Array.from(affectedClipIds),\n });\n\n // Get currently active clips in the 2-Clip window\n const activeClipIds = new Set(\n Array.from(this.clipSessionManager['entries'].keys()).filter((clipId) =>\n this.clipSessionManager.isClipActive(clipId)\n )\n );\n\n // Process clip updates\n for (const update of clipUpdates) {\n if (update.type === 'remove') {\n this.activeClips.delete(update.clipId);\n this.cacheManager.evictClip(update.clipId);\n }\n\n // Notify ClipSessionManager to handle the update\n // This will install new instructions or restart pipeline as needed\n await this.clipSessionManager.handlePlannerUpdate(update.clipId, update);\n\n // Only evict cache for active clips (in current 2-Clip window)\n // Non-active clips will be regenerated when they enter the window\n if (activeClipIds.has(update.clipId) && update.type !== 'remove') {\n this.cacheManager.evictClip(update.clipId);\n }\n }\n }\n\n private handleResourceStateChange(resourceId: string, state: Resource['state']): void {\n if (!this.compositionModel) {\n return;\n }\n\n this.compositionModel.updateResourceState(resourceId, state ?? 'pending');\n\n if (state !== 'ready') {\n return;\n }\n\n const resource = this.compositionModel.getResource(resourceId);\n if (!resource) {\n return;\n }\n\n // Main video/audio resources: data will flow naturally into pipeline\n if (resource.type === 'video' || resource.type === 'audio') {\n return;\n }\n\n // Attachment resources (fonts, images): update instructions for active clips\n const clipIds = this.compositionModel.getClipIdsByResourceId(resourceId);\n for (const clipId of clipIds) {\n // Only update active clips (in 2-Clip window)\n if (!this.clipSessionManager.isClipActive(clipId)) {\n continue;\n }\n\n const clip = this.compositionModel.findClip(clipId);\n if (!clip) {\n continue;\n }\n\n // Rebuild instructions with updated resource status\n const instructions = this.planner.getInstructions(clipId);\n if (!instructions) {\n continue;\n }\n\n // Send updated instructions to worker (no pipeline restart needed)\n const session = this.clipSessionManager.getSession(clipId);\n const visualWorker = session?.visualWorkerHandle;\n if (visualWorker) {\n visualWorker.send('install_instructions', instructions);\n }\n }\n }\n\n async restartWorker(type: WorkerType, clipId?: string): Promise<void> {\n const clipLocalTypes: WorkerType[] = [\n 'videoDemux',\n 'audioDemux',\n 'videoDecode',\n 'audioDecode',\n 'videoCompose',\n 'videoEncode',\n ];\n\n if (clipLocalTypes.includes(type) && !clipId) {\n throw new Error(`clipId required for restarting ${type} worker`);\n }\n\n this.workers.terminate(type, clipId);\n const worker = await this.workers.getOrCreate(type, clipId);\n\n this.eventBus.emit(MeframeEvent.WorkerRestarted, {\n type,\n workerId: worker.getWorkerId(),\n reason: 'Manual restart',\n });\n\n if (clipId) {\n const session = this.clipSessionManager.getSession(clipId);\n if (session) {\n await session.activate();\n }\n }\n }\n\n async renderFrame(timeUs: TimeUs, options?: RenderFrameOptions): Promise<RcFrame | null> {\n const signal = options?.signal;\n const immediate = options?.immediate ?? true;\n\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n // Detect clip change and proactively ensure cache for prev/current/next\n if (this.currentClipId !== clip.id) {\n this.currentClipId = clip.id;\n void this.ensureClipCache(timeUs, immediate);\n }\n\n // Calculate clip-relative time for cache lookup (global time - clip start time)\n let relativeTimeUs = options?.relativeTimeUs ?? timeUs - clip.startUs;\n\n // Quantize to frame boundary to handle timestamp precision issues\n relativeTimeUs = quantizeTimestampToFrame(relativeTimeUs, 0, this.compositionModel.fps);\n\n // Clamp to clip duration to handle edge cases where quantization pushes beyond clip end\n // This can happen when clip duration is not exactly aligned to frame boundaries\n relativeTimeUs = Math.min(relativeTimeUs, clip.durationUs - 1);\n\n const cachedFrame = this.cacheManager.getFrame(relativeTimeUs, clip.id);\n if (cachedFrame) {\n this.eventBus.emit(MeframeEvent.CacheHit, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n return cachedFrame;\n }\n\n this.eventBus.emit(MeframeEvent.CacheMiss, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n\n if (signal?.aborted) {\n throw new DOMException('Render aborted', 'AbortError');\n }\n\n // L1 miss - try decode from L2\n const l2Frame = await this.decodeFromL2(relativeTimeUs, clip);\n if (l2Frame) {\n return l2Frame;\n }\n\n return null;\n }\n\n private async decodeFromL2(timeUs: TimeUs, clip: Clip): Promise<RcFrame | null> {\n const { id, trackId, startUs } = clip;\n const [chunkStream, metadata] = await Promise.all([\n this.cacheManager.l2Cache.createReadStream(id, 'video'),\n this.cacheManager.l2Cache.getClipMetadata(id, 'video'),\n ]);\n\n if (!chunkStream || !metadata?.codec) {\n return null;\n }\n\n const decoder = new VideoChunkDecoder(`l2-temp-${id}`, {\n codec: metadata.codec,\n width: metadata.codedWidth,\n height: metadata.codedHeight,\n description: metadata.description,\n hardwareAcceleration: metadata.hardwareAcceleration || 'no-preference',\n });\n\n try {\n const decodeStream = chunkStream.pipeThrough(decoder.createStream());\n\n let targetFrame: RcFrame | null = null;\n\n await this.cacheManager.receiveComposedFrames(decodeStream, {\n clipId: id,\n trackId: trackId || this.compositionModel?.mainTrackId || 'main',\n fps: this.compositionModel?.fps ?? 30,\n clipStartUs: startUs,\n onFrame: (info) => {\n if (info.timeUs === timeUs) {\n targetFrame = this.cacheManager.getFrame(timeUs, id);\n }\n },\n });\n\n return targetFrame;\n } finally {\n await decoder.close();\n }\n }\n\n /**\n * Ensure clips are cached using 2-Clip strategy\n * Debounced to avoid excessive session activation during fast seek\n * @param timeUs - Target time for cache window\n * @param immediate - Skip debounce if true (used for initial load)\n */\n async ensureClipCache(timeUs: TimeUs, immediate = false): Promise<void> {\n const executeCache = async (): Promise<void> => {\n if (!this.compositionModel) return;\n\n const clipIds = this.compositionModel.getClipsToCacheAtTime(timeUs);\n if (clipIds.length === 0) return;\n await this.clipSessionManager.ensureClips(clipIds);\n };\n\n if (this.ensureCacheDebounceTimer !== null) {\n clearTimeout(this.ensureCacheDebounceTimer);\n this.ensureCacheDebounceTimer = null;\n }\n\n if (immediate) {\n return executeCache();\n }\n\n return new Promise((resolve) => {\n this.ensureCacheDebounceTimer = setTimeout(async () => {\n this.ensureCacheDebounceTimer = null;\n await executeCache();\n resolve();\n }, this.ensureCacheDebounceDelay) as unknown as number;\n });\n }\n\n /**\n * Wait for clip cache to be ready for playback\n * Returns true if minimum cache is ready, false if timeout\n */\n async waitForClipReady(\n timeUs: TimeUs,\n options?: { minFrameCount?: number; timeoutMs?: number }\n ): Promise<boolean> {\n if (!this.compositionModel) {\n return false;\n }\n\n const clips = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId);\n if (clips.length === 0) {\n return true;\n }\n\n const currentClip = clips[0];\n if (!currentClip) {\n return true;\n }\n\n // For buffering scenario, we just need ANY frames in the clip\n // Don't restrict to frames after a specific startTimeUs, as the pipeline\n // might be rendering from the beginning while playback is in the middle\n return this.cacheManager.waitForClipReady(currentClip.id, {\n minFrameCount: options?.minFrameCount ?? 5,\n timeoutMs: options?.timeoutMs ?? 5_000,\n // Don't pass startTimeUs - count all frames in the clip\n });\n }\n\n /**\n * Render a clip completely for L2 cache (bypass ClipSessionManager)\n * Returns a promise that resolves when encoding is complete\n */\n async renderClipForL2(clipId: string): Promise<boolean> {\n const sessionId = `${clipId}#l2`;\n let session: VideoClipSession | null = null;\n\n return new Promise<boolean>((resolve, reject) => {\n this.createSession(sessionId, {\n forL2Only: true,\n clipId: clipId,\n onL2Complete: () => {\n resolve(true);\n },\n onL2Error: (error) => {\n console.error('[Orchestrator] L2 rendering failed for', clipId, error);\n reject(error);\n },\n })\n .then((s) => {\n session = s;\n return session.activate();\n })\n .catch(async (error) => {\n // Clean up partial session on any error to avoid worker state pollution\n if (session) {\n await session.dispose();\n }\n\n if (error instanceof ResourceConflictError) {\n resolve(false);\n } else {\n reject(error);\n }\n });\n });\n }\n\n /**\n * Create a new session for a clip\n */\n private async createSession(\n sessionId: string,\n options?: {\n forL2Only?: boolean;\n clipId?: string;\n onL2Complete?: () => void;\n onL2Error?: (error: Error) => void;\n }\n ): Promise<VideoClipSession> {\n const clipId = options?.clipId ?? sessionId;\n const clip = this.compositionModel?.findClip(clipId);\n if (!clip) {\n throw new Error(`Clip ${clipId} not found`);\n }\n\n const session = await VideoClipSession.create({\n clipId,\n sessionId,\n forL2Only: options?.forL2Only ?? false,\n planner: this.planner,\n workerPool: this.workers,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel!,\n workerConfigs: this.buildWorkerConfigs(),\n resourceLoader: this.resourceLoader,\n callbacks: {\n onComposedStreamReady: (stream, fps) => {\n if (options?.forL2Only) {\n // L2 channel: don't need L1, cancel stream\n stream.cancel();\n } else {\n // Preview channel: fill L1\n this.cacheManager.receiveComposedFrames(stream, {\n clipId: clipId,\n trackId: this.compositionModel!.mainTrackId,\n fps,\n clipStartUs: clip.startUs,\n onFrame: () => {},\n });\n }\n },\n onEncodedStreamReady: (stream, track) => {\n if (options?.forL2Only) {\n // L2 channel: write to L2 using clipId, notify on complete\n this.cacheManager.receiveEncodedChunks(stream, clipId, track, {\n onComplete: () => {\n session.dispose();\n // Only notify completion for video track (audio is optional)\n if (track === 'video' && options.onL2Complete) {\n options.onL2Complete();\n }\n },\n onError: (error) => {\n console.error(\n `[Orchestrator] L2 encode stream error for ${clipId} ${track}:`,\n error\n );\n session.dispose();\n if (options.onL2Error) {\n options.onL2Error(error);\n }\n },\n });\n } else {\n // Preview channel: don't write to L2, cancel stream\n stream.cancel();\n }\n },\n onPipelineReady: async (attachmentResourceIds?: string[]) => {\n // 1. Load main track resource\n const clip = this.compositionModel?.findClip(clipId);\n if (clip && hasResourceId(clip)) {\n await this.resourceLoader.fetch(clip.resourceId, {\n priority: options?.forL2Only ? 'low' : 'high',\n sessionId: sessionId,\n trackId: clip.trackId,\n isMainTrack: true,\n });\n }\n\n // 2. Load attachment resources (global shared Blob cache)\n if (attachmentResourceIds && attachmentResourceIds.length > 0) {\n for (const resourceId of attachmentResourceIds) {\n await this.resourceLoader.fetch(resourceId, {\n priority: 'normal',\n sessionId: sessionId,\n isMainTrack: false,\n });\n }\n }\n },\n },\n });\n\n this.activeClips.add(sessionId);\n return session;\n }\n\n async dispose(): Promise<void> {\n if (this.ensureCacheDebounceTimer !== null) {\n clearTimeout(this.ensureCacheDebounceTimer);\n this.ensureCacheDebounceTimer = null;\n }\n\n this.resourceLoader.dispose();\n await this.clipSessionManager.dispose();\n await this.cacheManager.clear();\n\n this.currentClipId = null;\n this.activeClips.clear();\n\n this.workers.terminateAll();\n this.compositionModel = null;\n this.eventBus.dispose();\n }\n\n private buildWorkerConfigs(): Record<WorkerType, any> {\n const config = this.config as any;\n const defaultCanvasWidth = config.global?.defaultCanvasWidth ?? 720;\n const defaultCanvasHeight = config.global?.defaultCanvasHeight ?? 1280;\n const defaultFps = config.global?.defaultFps ?? 30;\n return {\n videoDemux: {\n highWaterMark: config.demux?.backpressure?.highWaterMark ?? 10,\n },\n audioDemux: {\n highWaterMark: config.demux?.backpressure?.highWaterMark ?? 10,\n },\n videoDecode: config.decode?.video,\n audioDecode: config.decode?.audio,\n videoCompose: {\n width: config.compose?.canvas?.width ?? defaultCanvasWidth,\n height: config.compose?.canvas?.height ?? defaultCanvasHeight,\n fps: config.global?.defaultFps ?? defaultFps,\n backgroundColor: config.compose?.canvas?.backgroundColor ?? '#000000',\n enableSmoothing: config.compose?.visual?.enableSmoothing ?? true,\n enableHardwareAcceleration: config.compose?.visual?.enableHardwareAcceleration ?? true,\n },\n audioCompose: {\n ducking: config.compose?.audio?.ducking,\n mixing: config.compose?.audio?.mixing,\n },\n videoEncode: {\n codec: 'avc1.42002A',\n width: config.compose?.canvas?.width || defaultCanvasWidth,\n height: config.compose?.canvas?.height || defaultCanvasHeight,\n bitrate: config.encode?.video?.bitrateKbps\n ? config.encode.video.bitrateKbps * 1000\n : 12_000_000,\n framerate: config.encode?.video?.framerate || defaultFps,\n latencyMode: 'quality',\n bitrateMode: 'variable',\n hardwareAcceleration: 'no-preference',\n ...(config.encode?.video as any),\n },\n };\n }\n\n async export(model: CompositionModel, options: ExportOptions): Promise<Blob> {\n return this.muxManager.export(model, options);\n }\n}\n"],"names":["applyModelPatch","clip"],"mappings":"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"}
1
+ {"version":3,"file":"Orchestrator.js","sources":["../../src/orchestrator/Orchestrator.ts"],"sourcesContent":["import { EventBus } from '../event/EventBus';\nimport { WorkerPool } from '../worker/WorkerPool';\nimport { applyPatch as applyModelPatch } from '../model/patch';\nimport { ResourceConflictError, ResourceLoader } from '../stages/load/ResourceLoader';\nimport { CacheManager } from '../cache/CacheManager';\nimport { ConfigLoader } from '../config/ConfigLoader';\nimport type { IOrchestrator, OrchestratorConfig, RenderFrameOptions } from './types';\nimport { WorkerStatus, WorkerType } from '../worker/types';\nimport { CompositionModel, CompositionPatch, Resource, TimeUs, RcFrame, Clip } from '../model';\nimport { hasResourceId } from '../model/types';\nimport { MeframeEvent, type EventPayloadMap } from '../event/events';\nimport { CompositionPlanner } from './CompositionPlanner';\nimport { VideoClipSession } from './VideoClipSession';\nimport { ClipSessionManager } from './ClipSessionManager';\nimport { GlobalAudioSession } from '../stages/compose/GlobalAudioSession';\nimport { MuxManager } from '../stages/mux/MuxManager';\nimport { ExportOptions } from '@/types';\nimport { VideoChunkDecoder } from '../stages/decode/VideoChunkDecoder';\nimport { quantizeTimestampToFrame } from '../utils/time-utils';\n\nexport class Orchestrator implements IOrchestrator {\n workers: WorkerPool;\n eventBus: EventBus<EventPayloadMap>;\n compositionModel: CompositionModel | null = null;\n resourceLoader: ResourceLoader;\n cacheManager: CacheManager;\n planner: CompositionPlanner;\n audioSession: GlobalAudioSession;\n muxManager: MuxManager;\n\n private activeClips = new Set<string>();\n private isInitialized = false;\n private config = ConfigLoader.getInstance().getConfig();\n private clipSessionManager: ClipSessionManager;\n private currentClipId: string | null = null;\n private ensureCacheDebounceTimer: number | null = null;\n private readonly ensureCacheDebounceDelay = 150;\n readonly events: Pick<EventBus<EventPayloadMap>, 'on' | 'off' | 'once'>;\n\n constructor(config: OrchestratorConfig) {\n // Use provided eventBus or create a new one\n this.eventBus = config.eventBus || new EventBus<EventPayloadMap>();\n this.events = this.eventBus.asReadonly();\n\n // Initialize config first\n this.config = ConfigLoader.getInstance().getConfig();\n\n const workerConfigs = this.buildWorkerConfigs();\n\n // Initialize WorkerPool with worker path from config\n this.workers = new WorkerPool({\n eventBus: this.eventBus,\n workerConfigs,\n workerPath: config.workerPath,\n workerExtension: config.workerExtension,\n });\n\n this.resourceLoader = new ResourceLoader({\n orchestrator: this as any,\n eventBus: this.eventBus,\n config: {\n maxConcurrent: config.maxWorkers || (this.config.load as any)?.retry?.maxAttempts || 4,\n },\n onStateChange: (resourceId, state) => this.handleResourceStateChange(resourceId, state),\n });\n\n this.planner = new CompositionPlanner();\n\n const cacheConfig = config.cacheConfig || this.config.cache;\n this.cacheManager = new CacheManager(\n {\n l1: {\n maxMemoryMB:\n (cacheConfig as any)?.l1Size || (cacheConfig as any)?.l1?.maxMemoryMB || 1024,\n maxGOPs: (this.config.decode as any)?.video?.maxGOPs || 4,\n },\n l2: {\n maxSizeMB: (cacheConfig as any)?.l2Size || (cacheConfig as any)?.l2?.maxSizeMB || 2048,\n projectId: 'default',\n },\n },\n this.eventBus\n );\n\n this.clipSessionManager = new ClipSessionManager({\n maxConcurrent: 2,\n factory: {\n createSession: (clipId) => this.createSession(clipId),\n },\n cacheManager: this.cacheManager,\n });\n\n this.audioSession = new GlobalAudioSession({\n cacheManager: this.cacheManager,\n workers: this.workers,\n resourceLoader: this.resourceLoader,\n eventBus: this.eventBus,\n getModel: () => this.compositionModel,\n buildWorkerConfigs: () => this.buildWorkerConfigs(),\n });\n\n this.muxManager = new MuxManager(\n this.cacheManager,\n this.audioSession,\n this.config.encode.audio as AudioEncoderConfig\n );\n }\n\n get workerStatus(): WorkerStatus {\n const status = this.workers.status;\n const result: WorkerStatus = {} as WorkerStatus;\n\n const workerTypes: WorkerType[] = [\n 'videoDemux',\n 'audioDemux',\n 'videoDecode',\n 'audioDecode',\n 'videoCompose',\n 'audioCompose',\n 'videoEncode',\n ];\n\n for (const type of workerTypes) {\n result[type] = status[type] || {\n state: 'idle',\n taskCount: 0,\n };\n }\n\n return result;\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n\n await this.cacheManager.init();\n\n this.isInitialized = true;\n }\n\n // Event methods - forward to eventBus\n on<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.on(event, handler);\n }\n\n off<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.off(event, handler);\n }\n\n once<K extends keyof EventPayloadMap>(\n event: K,\n handler: (payload: EventPayloadMap[K]) => void\n ): void {\n this.eventBus.once(event, handler);\n }\n\n async setCompositionModel(model: CompositionModel): Promise<void> {\n this.compositionModel = model;\n this.planner.setModel(model);\n this.currentClipId = null;\n\n this.eventBus.emit(MeframeEvent.ModelSet, model);\n\n this.eventBus.emit(MeframeEvent.CompositionUpdated, {\n trackCount: model.tracks.length,\n clipCount: model.tracks.reduce((acc: number, track: any) => acc + track.clips.length, 0),\n durationUs: model.durationUs,\n });\n\n await this.audioSession.activateAllAudioClips();\n }\n\n async applyPatch(patch: CompositionPatch): Promise<void> {\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n // Apply patch and get affected clip IDs (simplified for 2-Clip strategy)\n const affectedClipIds = applyModelPatch(this.compositionModel, patch);\n const clipUpdates = this.planner.applyPatch(patch, affectedClipIds);\n\n this.eventBus.emit(MeframeEvent.PatchApplied, {\n operations: patch.operations.length,\n affectedClips: Array.from(affectedClipIds),\n });\n\n // Process clip updates\n for (const update of clipUpdates) {\n if (update.type === 'remove') {\n this.activeClips.delete(update.clipId);\n }\n\n this.cacheManager.invalidateClip(update.clipId);\n await this.clipSessionManager.handlePlannerUpdate(update.clipId, update);\n }\n }\n\n private handleResourceStateChange(resourceId: string, state: Resource['state']): void {\n if (!this.compositionModel) {\n return;\n }\n\n this.compositionModel.updateResourceState(resourceId, state ?? 'pending');\n\n if (state !== 'ready') {\n return;\n }\n\n const resource = this.compositionModel.getResource(resourceId);\n if (!resource) {\n return;\n }\n\n // Main video/audio resources: data will flow naturally into pipeline\n if (resource.type === 'video' || resource.type === 'audio') {\n return;\n }\n\n // Attachment resources (fonts, images): update instructions for active clips\n const clipIds = this.compositionModel.getClipIdsByResourceId(resourceId);\n for (const clipId of clipIds) {\n // Only update active clips (in 2-Clip window)\n if (!this.clipSessionManager.isClipActive(clipId)) {\n continue;\n }\n\n const clip = this.compositionModel.findClip(clipId);\n if (!clip) {\n continue;\n }\n\n // Rebuild instructions with updated resource status\n const instructions = this.planner.getInstructions(clipId);\n if (!instructions) {\n continue;\n }\n\n // Send updated instructions to worker (no pipeline restart needed)\n const session = this.clipSessionManager.getSession(clipId);\n const visualWorker = session?.visualWorkerHandle;\n if (visualWorker) {\n visualWorker.send('install_instructions', instructions);\n }\n }\n }\n\n async restartWorker(type: WorkerType, clipId?: string): Promise<void> {\n const clipLocalTypes: WorkerType[] = [\n 'videoDemux',\n 'audioDemux',\n 'videoDecode',\n 'audioDecode',\n 'videoCompose',\n 'videoEncode',\n ];\n\n if (clipLocalTypes.includes(type) && !clipId) {\n throw new Error(`clipId required for restarting ${type} worker`);\n }\n\n this.workers.terminate(type, clipId);\n const worker = await this.workers.getOrCreate(type, clipId);\n\n this.eventBus.emit(MeframeEvent.WorkerRestarted, {\n type,\n workerId: worker.getWorkerId(),\n reason: 'Manual restart',\n });\n\n if (clipId) {\n const session = this.clipSessionManager.getSession(clipId);\n if (session) {\n await session.activate();\n }\n }\n }\n\n async renderFrame(timeUs: TimeUs, options?: RenderFrameOptions): Promise<RcFrame | null> {\n const signal = options?.signal;\n const immediate = options?.immediate ?? true;\n\n if (!this.compositionModel) {\n throw new Error('No composition model set');\n }\n\n const clip = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId)[0];\n if (!clip) {\n return null;\n }\n\n // Detect clip change and proactively ensure cache for prev/current/next\n if (this.currentClipId !== clip.id) {\n this.currentClipId = clip.id;\n void this.ensureClipCache(timeUs, immediate);\n }\n\n // Calculate clip-relative time for cache lookup (global time - clip start time)\n let relativeTimeUs = options?.relativeTimeUs ?? timeUs - clip.startUs;\n\n // Quantize to frame boundary to handle timestamp precision issues\n relativeTimeUs = quantizeTimestampToFrame(relativeTimeUs, 0, this.compositionModel.fps);\n\n // Clamp to clip duration to handle edge cases where quantization pushes beyond clip end\n // This can happen when clip duration is not exactly aligned to frame boundaries\n relativeTimeUs = Math.min(relativeTimeUs, clip.durationUs - 1);\n\n const cachedFrame = this.cacheManager.getFrame(relativeTimeUs, clip.id);\n if (cachedFrame) {\n this.eventBus.emit(MeframeEvent.CacheHit, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n return cachedFrame;\n }\n\n this.eventBus.emit(MeframeEvent.CacheMiss, {\n timeUs,\n level: 'L1',\n key: `${clip.id}-${relativeTimeUs}`,\n });\n\n if (signal?.aborted) {\n throw new DOMException('Render aborted', 'AbortError');\n }\n\n // L1 miss - try decode from L2\n const l2Frame = await this.decodeFromL2(relativeTimeUs, clip);\n if (l2Frame) {\n return l2Frame;\n }\n\n return null;\n }\n\n private async decodeFromL2(timeUs: TimeUs, clip: Clip): Promise<RcFrame | null> {\n const { id, trackId, startUs } = clip;\n const [chunkStream, metadata] = await Promise.all([\n this.cacheManager.l2Cache.createReadStream(id, 'video'),\n this.cacheManager.l2Cache.getClipMetadata(id, 'video'),\n ]);\n\n if (!chunkStream || !metadata?.codec) {\n return null;\n }\n\n const decoder = new VideoChunkDecoder(`l2-temp-${id}`, {\n codec: metadata.codec,\n width: metadata.codedWidth,\n height: metadata.codedHeight,\n description: metadata.description,\n hardwareAcceleration: metadata.hardwareAcceleration || 'no-preference',\n });\n\n try {\n const decodeStream = chunkStream.pipeThrough(decoder.createStream());\n\n let targetFrame: RcFrame | null = null;\n\n await this.cacheManager.receiveComposedFrames(decodeStream, {\n clipId: id,\n trackId: trackId || this.compositionModel?.mainTrackId || 'main',\n fps: this.compositionModel?.fps ?? 30,\n clipStartUs: startUs,\n onFrame: (info) => {\n if (info.timeUs === timeUs) {\n targetFrame = this.cacheManager.getFrame(timeUs, id);\n }\n },\n });\n\n return targetFrame;\n } finally {\n await decoder.close();\n }\n }\n\n /**\n * Ensure clips are cached using 2-Clip strategy\n * Debounced to avoid excessive session activation during fast seek\n * @param timeUs - Target time for cache window\n * @param immediate - Skip debounce if true (used for initial load)\n */\n async ensureClipCache(timeUs: TimeUs, immediate = false): Promise<void> {\n const executeCache = async (): Promise<void> => {\n if (!this.compositionModel) return;\n\n const clipIds = this.compositionModel.getClipsToCacheAtTime(timeUs);\n if (clipIds.length === 0) return;\n await this.clipSessionManager.ensureClips(clipIds);\n };\n\n if (this.ensureCacheDebounceTimer !== null) {\n clearTimeout(this.ensureCacheDebounceTimer);\n this.ensureCacheDebounceTimer = null;\n }\n\n if (immediate) {\n return executeCache();\n }\n\n return new Promise((resolve) => {\n this.ensureCacheDebounceTimer = setTimeout(async () => {\n this.ensureCacheDebounceTimer = null;\n await executeCache();\n resolve();\n }, this.ensureCacheDebounceDelay) as unknown as number;\n });\n }\n\n /**\n * Wait for clip cache to be ready for playback\n * Returns true if minimum cache is ready, false if timeout\n */\n async waitForClipReady(\n timeUs: TimeUs,\n options?: { minFrameCount?: number; timeoutMs?: number }\n ): Promise<boolean> {\n if (!this.compositionModel) {\n return false;\n }\n\n const clips = this.compositionModel.getClipsAtTime(timeUs, this.compositionModel.mainTrackId);\n if (clips.length === 0) {\n return true;\n }\n\n const currentClip = clips[0];\n if (!currentClip) {\n return true;\n }\n\n // For buffering scenario, we just need ANY frames in the clip\n // Don't restrict to frames after a specific startTimeUs, as the pipeline\n // might be rendering from the beginning while playback is in the middle\n return this.cacheManager.waitForClipReady(currentClip.id, {\n minFrameCount: options?.minFrameCount ?? 5,\n timeoutMs: options?.timeoutMs ?? 5_000,\n // Don't pass startTimeUs - count all frames in the clip\n });\n }\n\n /**\n * Render a clip completely for L2 cache (bypass ClipSessionManager)\n * Returns a promise that resolves when encoding is complete\n */\n async renderClipForL2(clipId: string): Promise<boolean> {\n const sessionId = `${clipId}#l2`;\n let session: VideoClipSession | null = null;\n\n return new Promise<boolean>((resolve, reject) => {\n this.createSession(sessionId, {\n forL2Only: true,\n clipId: clipId,\n onL2Complete: () => {\n resolve(true);\n },\n onL2Error: (error) => {\n console.error('[Orchestrator] L2 rendering failed for', clipId, error);\n reject(error);\n },\n })\n .then((s) => {\n session = s;\n return session.activate();\n })\n .catch(async (error) => {\n // Clean up partial session on any error to avoid worker state pollution\n if (session) {\n await session.dispose();\n }\n\n if (error instanceof ResourceConflictError) {\n resolve(false);\n } else {\n reject(error);\n }\n });\n });\n }\n\n /**\n * Create a new session for a clip\n */\n private async createSession(\n sessionId: string,\n options?: {\n forL2Only?: boolean;\n clipId?: string;\n onL2Complete?: () => void;\n onL2Error?: (error: Error) => void;\n }\n ): Promise<VideoClipSession> {\n const clipId = options?.clipId ?? sessionId;\n const clip = this.compositionModel?.findClip(clipId);\n if (!clip) {\n throw new Error(`Clip ${clipId} not found`);\n }\n\n const session = await VideoClipSession.create({\n clipId,\n sessionId,\n forL2Only: options?.forL2Only ?? false,\n planner: this.planner,\n workerPool: this.workers,\n cacheManager: this.cacheManager,\n compositionModel: this.compositionModel!,\n workerConfigs: this.buildWorkerConfigs(),\n resourceLoader: this.resourceLoader,\n callbacks: {\n onComposedStreamReady: (stream, fps) => {\n if (options?.forL2Only) {\n // L2 channel: don't need L1, cancel stream\n stream.cancel();\n } else {\n // Preview channel: fill L1\n this.cacheManager.receiveComposedFrames(stream, {\n clipId: clipId,\n trackId: this.compositionModel!.mainTrackId,\n fps,\n clipStartUs: clip.startUs,\n onFrame: () => {},\n });\n }\n },\n onEncodedStreamReady: (stream, track) => {\n if (options?.forL2Only) {\n // L2 channel: write to L2 using clipId, notify on complete\n this.cacheManager.receiveEncodedChunks(stream, clipId, track, {\n onComplete: () => {\n session.dispose();\n // Only notify completion for video track (audio is optional)\n if (track === 'video' && options.onL2Complete) {\n options.onL2Complete();\n }\n },\n onError: (error) => {\n console.error(\n `[Orchestrator] L2 encode stream error for ${clipId} ${track}:`,\n error\n );\n session.dispose();\n if (options.onL2Error) {\n options.onL2Error(error);\n }\n },\n });\n } else {\n // Preview channel: don't write to L2, cancel stream\n stream.cancel();\n }\n },\n onPipelineReady: async (attachmentResourceIds?: string[]) => {\n // 1. Load main track resource\n const clip = this.compositionModel?.findClip(clipId);\n if (clip && hasResourceId(clip)) {\n await this.resourceLoader.fetch(clip.resourceId, {\n priority: options?.forL2Only ? 'low' : 'high',\n sessionId: sessionId,\n trackId: clip.trackId,\n isMainTrack: true,\n });\n }\n\n // 2. Load attachment resources (global shared Blob cache)\n if (attachmentResourceIds && attachmentResourceIds.length > 0) {\n for (const resourceId of attachmentResourceIds) {\n await this.resourceLoader.fetch(resourceId, {\n priority: 'normal',\n sessionId: sessionId,\n isMainTrack: false,\n });\n }\n }\n },\n },\n });\n\n this.activeClips.add(sessionId);\n return session;\n }\n\n async dispose(): Promise<void> {\n if (this.ensureCacheDebounceTimer !== null) {\n clearTimeout(this.ensureCacheDebounceTimer);\n this.ensureCacheDebounceTimer = null;\n }\n\n this.resourceLoader.dispose();\n await this.clipSessionManager.dispose();\n await this.cacheManager.clear();\n\n this.currentClipId = null;\n this.activeClips.clear();\n\n this.workers.terminateAll();\n this.compositionModel = null;\n this.eventBus.dispose();\n }\n\n private buildWorkerConfigs(): Record<WorkerType, any> {\n const config = this.config as any;\n const defaultCanvasWidth = config.global?.defaultCanvasWidth ?? 720;\n const defaultCanvasHeight = config.global?.defaultCanvasHeight ?? 1280;\n const defaultFps = config.global?.defaultFps ?? 30;\n return {\n videoDemux: {\n highWaterMark: config.demux?.backpressure?.highWaterMark ?? 10,\n },\n audioDemux: {\n highWaterMark: config.demux?.backpressure?.highWaterMark ?? 10,\n },\n videoDecode: config.decode?.video,\n audioDecode: config.decode?.audio,\n videoCompose: {\n width: config.compose?.canvas?.width ?? defaultCanvasWidth,\n height: config.compose?.canvas?.height ?? defaultCanvasHeight,\n fps: config.global?.defaultFps ?? defaultFps,\n backgroundColor: config.compose?.canvas?.backgroundColor ?? '#000000',\n enableSmoothing: config.compose?.visual?.enableSmoothing ?? true,\n enableHardwareAcceleration: config.compose?.visual?.enableHardwareAcceleration ?? true,\n },\n audioCompose: {\n ducking: config.compose?.audio?.ducking,\n mixing: config.compose?.audio?.mixing,\n },\n videoEncode: {\n codec: 'avc1.42002A',\n width: config.compose?.canvas?.width || defaultCanvasWidth,\n height: config.compose?.canvas?.height || defaultCanvasHeight,\n bitrate: config.encode?.video?.bitrateKbps\n ? config.encode.video.bitrateKbps * 1000\n : 12_000_000,\n framerate: config.encode?.video?.framerate || defaultFps,\n latencyMode: 'quality',\n bitrateMode: 'variable',\n hardwareAcceleration: 'no-preference',\n ...(config.encode?.video as any),\n },\n };\n }\n\n async export(model: CompositionModel, options: ExportOptions): Promise<Blob> {\n return this.muxManager.export(model, options);\n }\n}\n"],"names":["applyModelPatch","clip"],"mappings":"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"}
@@ -66,7 +66,6 @@ export declare class VideoClipSession {
66
66
  private acquireWorkers;
67
67
  private connectVideoPipeline;
68
68
  private installInstructions;
69
- private restartPipeline;
70
69
  private releasePipeline;
71
70
  invalidateClipCache(): Promise<void>;
72
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"VideoClipSession.d.ts","sourceRoot":"","sources":["../../src/orchestrator/VideoClipSession.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAQ,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAGpE,UAAU,yBAAyB;IACjC,qBAAqB,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7E,oBAAoB,CAClB,MAAM,EAAE,cAAc,CAAC;QAAE,KAAK,EAAE,iBAAiB,CAAC;QAAC,QAAQ,EAAE,yBAAyB,CAAA;KAAE,CAAC,EACzF,KAAK,EAAE,OAAO,GAAG,OAAO,GACvB,IAAI,CAAC;IACR,gBAAgB,CAAC,IAAI,IAAI,CAAC;IAC1B,eAAe,CAAC,CAAC,qBAAqB,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnE;AAED,UAAU,sBAAsB;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACvC,SAAS,EAAE,yBAAyB,CAAC;IACrC,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAQD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;IACpD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0B;IACxD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAiB;IAEjD,OAAO,CAAC,kBAAkB,CAAmC;IAC7D,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,qBAAqB,CAA+B;IAC5D,OAAO,CAAC,qBAAqB,CAA+B;IAC5D,OAAO,CAAC,oBAAoB,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAS;WAEd,MAAM,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI9E,OAAO;IAaD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAiCzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOxB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BlE,IAAI,kBAAkB,IAAI,UAAU,GAAG,IAAI,CAE1C;IAED,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,+BAA+B;YAkBzB,kBAAkB;YAQlB,kBAAkB;YAKlB,oBAAoB;YA+DpB,kBAAkB;YAKlB,cAAc;YA8Dd,oBAAoB;YA4GpB,mBAAmB;YAYnB,eAAe;YAgBf,eAAe;IA4DvB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;CAG3C"}
1
+ {"version":3,"file":"VideoClipSession.d.ts","sourceRoot":"","sources":["../../src/orchestrator/VideoClipSession.ts"],"names":[],"mappings":"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"}