@meframe/core 0.3.9 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/Meframe.d.ts +2 -2
  2. package/dist/Meframe.d.ts.map +1 -1
  3. package/dist/Meframe.js +9 -6
  4. package/dist/Meframe.js.map +1 -1
  5. package/dist/controllers/ExportController.d.ts.map +1 -1
  6. package/dist/controllers/ExportController.js +2 -0
  7. package/dist/controllers/ExportController.js.map +1 -1
  8. package/dist/event/events.d.ts +1 -0
  9. package/dist/event/events.d.ts.map +1 -1
  10. package/dist/event/events.js.map +1 -1
  11. package/dist/medeo-fe/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +1 -0
  12. package/dist/{node_modules → medeo-fe/node_modules}/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js +2 -2
  13. package/dist/medeo-fe/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +1 -0
  14. package/dist/orchestrator/ExportScheduler.d.ts.map +1 -1
  15. package/dist/orchestrator/ExportScheduler.js +9 -0
  16. package/dist/orchestrator/ExportScheduler.js.map +1 -1
  17. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  18. package/dist/orchestrator/Orchestrator.js +1 -0
  19. package/dist/orchestrator/Orchestrator.js.map +1 -1
  20. package/dist/stages/encode/BaseEncoder.d.ts.map +1 -1
  21. package/dist/stages/encode/BaseEncoder.js +4 -1
  22. package/dist/stages/encode/BaseEncoder.js.map +1 -1
  23. package/dist/stages/load/ResourceLoader.d.ts +1 -0
  24. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  25. package/dist/stages/load/ResourceLoader.js +20 -0
  26. package/dist/stages/load/ResourceLoader.js.map +1 -1
  27. package/dist/stages/load/StreamFactory.d.ts.map +1 -1
  28. package/dist/stages/load/StreamFactory.js +6 -3
  29. package/dist/stages/load/StreamFactory.js.map +1 -1
  30. package/dist/stages/load/types.d.ts +1 -0
  31. package/dist/stages/load/types.d.ts.map +1 -1
  32. package/dist/stages/mux/MP4Muxer.js +1 -1
  33. package/dist/utils/mp4box.js +1 -1
  34. package/dist/workers/stages/export/{export.worker.DbrPlw6d.js → export.worker.DCStS1mL.js} +5 -2
  35. package/dist/workers/stages/export/export.worker.DCStS1mL.js.map +1 -0
  36. package/dist/workers/worker-manifest.json +1 -1
  37. package/package.json +1 -1
  38. package/dist/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js.map +0 -1
  39. package/dist/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js.map +0 -1
  40. package/dist/workers/stages/export/export.worker.DbrPlw6d.js.map +0 -1
  41. /package/dist/{node_modules → medeo-fe/node_modules}/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js +0 -0
@@ -12,6 +12,7 @@ class ResourceLoader {
12
12
  taskManager;
13
13
  streamFactory;
14
14
  eventBus;
15
+ onProgress;
15
16
  onStateChange;
16
17
  blobCache = /* @__PURE__ */ new Map();
17
18
  parsingIndexes = /* @__PURE__ */ new Set();
@@ -27,6 +28,7 @@ class ResourceLoader {
27
28
  this.taskManager = new TaskManager(maxConcurrent, preloadConcurrency);
28
29
  this.streamFactory = new StreamFactory(options.onProgress, config);
29
30
  this.eventBus = options.eventBus;
31
+ this.onProgress = options.onProgress;
30
32
  this.onStateChange = options.onStateChange;
31
33
  this.cacheManager = options.cacheManager;
32
34
  }
@@ -213,6 +215,15 @@ class ResourceLoader {
213
215
  if (!cached) {
214
216
  throw new ResourceCorruptedError(resourceId, "File not found in OPFS or is empty");
215
217
  }
218
+ this.onProgress?.({
219
+ resourceId,
220
+ cached: true,
221
+ bytesLoaded: 0,
222
+ totalBytes: 0,
223
+ percentage: 0,
224
+ speed: 0,
225
+ estimatedTimeRemaining: 0
226
+ });
216
227
  const stream = await this.createOPFSReadStream(resourceId);
217
228
  const parseTask = {
218
229
  resourceId,
@@ -223,6 +234,15 @@ class ResourceLoader {
223
234
  isPreload: false
224
235
  };
225
236
  await this.parseIndexFromStream(parseTask, stream);
237
+ this.onProgress?.({
238
+ resourceId,
239
+ cached: true,
240
+ bytesLoaded: 0,
241
+ totalBytes: 0,
242
+ percentage: 100,
243
+ speed: 0,
244
+ estimatedTimeRemaining: 0
245
+ });
226
246
  } catch (error) {
227
247
  if (error instanceof EmptyStreamError || error instanceof ResourceCorruptedError || error instanceof MP4MoovNotFoundError) {
228
248
  console.warn(`[ResourceLoader] Corrupted cache detected for ${resourceId}, clearing...`);
@@ -1 +1 @@
1
- {"version":3,"file":"ResourceLoader.js","sources":["../../../src/stages/load/ResourceLoader.ts"],"sourcesContent":["import { type Resource, type CompositionModel, hasResourceId } from '../../model';\nimport type { ResourceLoadOptions, LoadTask, ResourceLoaderOptions } from './types';\nimport { TaskManager } from './TaskManager';\nimport { StreamFactory } from './StreamFactory';\nimport { EventPayloadMap, MeframeEvent } from '../../event/events';\nimport { EventBus } from '../../event/EventBus';\nimport { createImageBitmapFromBlob } from '../../utils/image-utils';\nimport { MP4IndexParser, type MP4ParseResult } from '../demux/MP4IndexParser';\nimport { MP3FrameParser } from '../demux/MP3FrameParser';\nimport type { CacheManager } from '../../cache/CacheManager';\nimport { MP4MoovNotFoundError, NotMP4Error, toResourceErrorInfo } from '../../utils/errors';\nimport {\n EmptyStreamError,\n ResourceCorruptedError,\n OPFSQuotaExceededError,\n isDOMException,\n} from '../../utils/errors';\n\nexport class ResourceConflictError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ResourceConflictError';\n }\n}\n\nexport class ResourceLoader {\n private cacheManager: CacheManager;\n private model?: CompositionModel;\n private taskManager: TaskManager;\n private streamFactory: StreamFactory;\n private eventBus?: EventBus<EventPayloadMap>;\n private onStateChange?: (resourceId: string, state: Resource['state']) => void;\n private blobCache = new Map<string, Blob>();\n private parsingIndexes = new Set<string>(); // Track in-progress index parsing\n private writingResources = new Set<string>(); // Track in-progress OPFS writes\n\n // Preloading state\n private isPreloadingEnabled = true;\n\n constructor(options: ResourceLoaderOptions) {\n const config = options.config || {};\n const maxConcurrent = config.maxConcurrent ?? 3;\n const preloadConcurrency = config.preloadConcurrency ?? 2;\n\n this.taskManager = new TaskManager(maxConcurrent, preloadConcurrency);\n this.streamFactory = new StreamFactory(options.onProgress, config);\n this.eventBus = options.eventBus;\n this.onStateChange = options.onStateChange;\n this.cacheManager = options.cacheManager;\n }\n\n async setModel(model: CompositionModel): Promise<void> {\n // Resource IDs are globally stable across models (same id implies same uri/content),\n // so in-flight load tasks remain valid across model switching.\n this.model = model;\n const mainTrack = model.tracks.find((track) => track.id === (model.mainTrackId || 'main'));\n if (mainTrack?.clips?.[0] && hasResourceId(mainTrack.clips[0])) {\n try {\n await this.load(mainTrack.clips[0].resourceId, {\n isPreload: false,\n clipId: mainTrack.clips[0].id,\n trackId: mainTrack.id,\n });\n } catch (error) {\n // Do not block model setup on permanent media/container mismatch.\n if (error instanceof NotMP4Error || error instanceof MP4MoovNotFoundError) {\n // Resource state is already set to 'error' in startLoad().\n } else {\n throw error;\n }\n }\n }\n this.startPreloading();\n }\n\n setPreloadingEnabled(enabled: boolean): void {\n this.isPreloadingEnabled = enabled;\n if (enabled) {\n this.startPreloading();\n }\n }\n\n startPreloading(): void {\n if (!this.model || !this.isPreloadingEnabled) return;\n\n // Collect all video/audio tracks for horizontal loading\n const tracks = this.model.tracks.filter(\n (track) => track.kind === 'video' || track.kind === 'audio'\n );\n if (tracks.length === 0) return;\n\n // Find maximum clip count across all tracks\n const maxClipCount = Math.max(...tracks.map((track) => track.clips.length));\n\n // Horizontal loading: load clip[0] from all tracks, then clip[1], etc.\n for (let clipIndex = 0; clipIndex < maxClipCount; clipIndex++) {\n for (const track of tracks) {\n const clip = track.clips[clipIndex];\n if (!clip || !hasResourceId(clip)) continue;\n\n const resource = this.model.getResource(clip.resourceId);\n if (!resource) continue;\n\n // Skip if already ready, loading, or error\n if (\n resource.state === 'ready' ||\n resource.state === 'loading' ||\n resource.state === 'error'\n ) {\n continue;\n }\n\n // Preload is best-effort; always swallow errors to avoid unhandled promise rejections.\n void this.load(resource.id, { isPreload: true }).catch(() => {});\n }\n }\n }\n\n private enqueueLoad(\n resource: Resource,\n isPreload: boolean = false,\n sessionId?: string,\n clipId?: string,\n trackId?: string\n ): LoadTask {\n // Check if task is already active\n const existingTask = this.taskManager.getActiveTask(resource.id);\n if (existingTask) {\n // Upgrade preload task to foreground if needed (go through TaskManager to keep counters correct)\n this.taskManager.enqueue(resource, isPreload, sessionId, clipId, trackId);\n return existingTask;\n }\n\n // Create new task and enqueue\n const task = this.taskManager.enqueue(resource, isPreload, sessionId, clipId, trackId);\n this.processQueue();\n return task;\n }\n\n private processQueue(): void {\n while (this.taskManager.canProcess) {\n const task = this.taskManager.getNextTask();\n if (!task) break;\n this.startLoad(task);\n }\n }\n\n /**\n * Load video resource (download + cache or read from cache)\n */\n private async loadVideoResource(task: LoadTask): Promise<void> {\n const cached = await this.cacheManager.hasResourceInCache(task.resourceId);\n\n if (cached) {\n // Resource already in OPFS - ensure index is parsed.\n // Important: OPFS files can disappear (browser eviction / manual clear / partial write).\n // If ensureIndexParsed fails with a corruption-related error, self-heal by re-downloading.\n try {\n await this.ensureIndexParsed(task.resourceId);\n } catch (error) {\n if (\n error instanceof ResourceCorruptedError ||\n error instanceof EmptyStreamError ||\n error instanceof MP4MoovNotFoundError\n ) {\n // ensureIndexParsed() already clears corrupted OPFS + mp4Index cache where applicable.\n // Re-download to OPFS and rebuild index cache.\n await this.loadWithOPFSCache(task);\n return;\n }\n throw error;\n }\n\n // Note: Export now uses IndexedVideoSource directly from OPFS\n // No need to transfer stream to worker anymore\n } else {\n // Not cached - download and cache to OPFS\n await this.loadWithOPFSCache(task);\n }\n }\n\n /**\n * Load audio resource (download + parse or reuse cache)\n */\n private async loadAudioResource(task: LoadTask): Promise<void> {\n if (!this.cacheManager.audioSampleCache.has(task.resourceId)) {\n // Not cached - download and parse\n await this.loadAndParseAudioFile(task);\n }\n // If already cached, do nothing (reuse existing cache)\n }\n\n /**\n * Ensure image blob is cached (download + cache or reuse cache).\n *\n * Important: preload should NOT decode ImageBitmap.\n * createImageBitmap can be slow / flaky under heavy main-thread load (playback + export),\n * and decoding isn't required for caching or for later loadImage()/getImageBitmap() calls.\n */\n private async ensureImageBlob(task: LoadTask): Promise<void> {\n // Check cache first\n let blob = this.blobCache.get(task.resourceId);\n\n if (!blob) {\n // Not cached: download and cache\n if (task.controller) {\n blob = await this.fetchBlob(task.resource.uri, task.controller.signal);\n this.blobCache.set(task.resourceId, blob);\n } else {\n return;\n }\n }\n }\n\n /**\n * Get cached ImageBitmap (for already loaded resources)\n * Used by VideoClipSession to batch transfer attachments\n */\n async getImageBitmap(resourceId: string): Promise<ImageBitmap | null> {\n const blob = this.blobCache.get(resourceId);\n if (!blob) return null;\n return await createImageBitmapFromBlob(blob);\n }\n\n /**\n * Load text resource (json/text)\n */\n private async loadTextResource(task: LoadTask): Promise<void> {\n if (task.controller) {\n await this.fetchBlob(task.resource.uri, task.controller.signal);\n }\n }\n\n /**\n * Start loading a resource\n * Handles state management (loading → ready/error) for all resource types\n */\n private async startLoad(task: LoadTask): Promise<void> {\n this.taskManager.activateTask(task);\n let loadError: Error | undefined;\n\n try {\n this.updateResourceState(task.resourceId, 'loading');\n task.controller = new AbortController();\n\n // Route to specific handlers based on resource type\n switch (task.resource.type) {\n case 'image': {\n // Preload path: only cache blob, avoid createImageBitmap during export/playback concurrency.\n await this.ensureImageBlob(task);\n break;\n }\n\n case 'video':\n await this.loadVideoResource(task);\n break;\n\n case 'audio':\n await this.loadAudioResource(task);\n break;\n\n case 'json':\n case 'text':\n await this.loadTextResource(task);\n break;\n }\n\n // Unified state update for all resource types\n this.updateResourceState(task.resourceId, 'ready');\n } catch (error) {\n task.error = error as Error;\n loadError = error as Error;\n this.updateResourceState(task.resourceId, 'error', toResourceErrorInfo(error));\n } finally {\n this.taskManager.completeTask(task.resourceId, loadError);\n this.processQueue();\n }\n }\n\n /**\n * Ensure MP4 index is parsed for a cached resource\n */\n async ensureIndexParsed(resourceId: string): Promise<void> {\n // Check if index already exists\n if (this.cacheManager.mp4IndexCache.has(resourceId)) {\n return;\n }\n\n // Wait for OPFS write to complete\n while (this.writingResources.has(resourceId)) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n // Check if already parsing (avoid duplicate parsing for same resource)\n if (this.parsingIndexes.has(resourceId)) {\n // Wait for the in-progress parsing to complete\n while (this.parsingIndexes.has(resourceId)) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n return;\n }\n\n // Mark as parsing\n this.parsingIndexes.add(resourceId);\n\n try {\n // Validate file exists and has content\n const cached = await this.cacheManager.hasResourceInCache(resourceId);\n if (!cached) {\n throw new ResourceCorruptedError(resourceId, 'File not found in OPFS or is empty');\n }\n\n // Parse from OPFS file\n const stream = await this.createOPFSReadStream(resourceId);\n // Create minimal task for parsing (no clipId since this is a background index parse)\n const parseTask: LoadTask = {\n resourceId,\n resource: { id: resourceId, type: 'video', uri: '' },\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n isPreload: false,\n };\n await this.parseIndexFromStream(parseTask, stream);\n } catch (error) {\n // Handle empty stream error by clearing corrupted cache\n if (\n error instanceof EmptyStreamError ||\n error instanceof ResourceCorruptedError ||\n error instanceof MP4MoovNotFoundError\n ) {\n console.warn(`[ResourceLoader] Corrupted cache detected for ${resourceId}, clearing...`);\n try {\n await this.cacheManager.resourceCache.deleteResource(resourceId);\n this.cacheManager.mp4IndexCache.delete(resourceId);\n } catch (deleteError) {\n console.error(`[ResourceLoader] Failed to clear corrupted cache:`, deleteError);\n }\n }\n throw error;\n } finally {\n // Remove from parsing set\n this.parsingIndexes.delete(resourceId);\n }\n }\n\n /**\n * Create ReadableStream from OPFS file\n * Reuses OPFSManager's underlying file access\n */\n private async createOPFSReadStream(resourceId: string): Promise<ReadableStream<Uint8Array>> {\n const opfsManager = this.cacheManager.resourceCache.opfsManager;\n const projectId = this.cacheManager.resourceCache.projectId;\n\n try {\n // Get file handle from OPFS\n const dir = await opfsManager.getProjectDir(projectId, 'resource');\n const fileName = `${resourceId}.mp4`;\n const fileHandle = await dir.getFileHandle(fileName, { create: false });\n const file = await fileHandle.getFile();\n\n // Return native stream\n return file.stream();\n } catch (error) {\n if (isDOMException(error, 'NotFoundError')) {\n throw new ResourceCorruptedError(resourceId, 'File not found in OPFS');\n }\n throw error;\n }\n }\n\n /**\n * Load resource and cache to OPFS + parse moov + extract first frame\n *\n * Strategy: Parallel tee() approach for fast first frame\n * - One stream writes to OPFS (background)\n * - Another stream parses moov and extracts first GOP\n * - First frame is decoded and cached immediately\n */\n private async loadWithOPFSCache(task: LoadTask): Promise<void> {\n const stream = await this.streamFactory.createRegularStream(task);\n if (!stream) {\n throw new Error(`Failed to create stream for ${task.resourceId}`);\n }\n\n // Export mode: only 2-way split (OPFS + parsing)\n // IndexedVideoSource will read directly from OPFS later\n const [opfsStream, parseStream] = stream.tee();\n\n // Parallel execution: write to OPFS and parse index\n await Promise.all([\n this.writeToOPFS(task.resourceId, opfsStream, task),\n this.parseIndexFromStream(task, parseStream),\n ]);\n }\n\n /**\n * Write resource stream to OPFS with retry on quota exceeded\n * If quota is exceeded and old projects are evicted, fetches a fresh stream and retries\n */\n private async writeToOPFS(\n resourceId: string,\n stream: ReadableStream<Uint8Array>,\n task?: LoadTask\n ): Promise<void> {\n this.writingResources.add(resourceId);\n try {\n await this.cacheManager.resourceCache.writeResource(resourceId, stream);\n } catch (error) {\n if (error instanceof OPFSQuotaExceededError && error.retryable && task) {\n console.log(\n `[ResourceLoader] OPFS quota exceeded for ${resourceId}, retrying with fresh stream...`\n );\n\n // Create fresh stream for retry\n const retryStream = await this.streamFactory.createRegularStream(task);\n if (!retryStream) {\n throw new Error(`Failed to create retry stream for ${resourceId}`);\n }\n\n // Retry write with fresh stream\n await this.cacheManager.resourceCache.writeResource(resourceId, retryStream);\n } else {\n throw error;\n }\n } finally {\n this.writingResources.delete(resourceId);\n }\n }\n\n /**\n * Load and parse audio file (MP3/WAV) in main thread\n * Extract EncodedAudioChunk and cache to AudioSampleCache\n * Aligned with video audio track extraction (unified architecture)\n */\n private async loadAndParseAudioFile(task: LoadTask): Promise<void> {\n const { resourceId } = task;\n\n try {\n // TODO: Streaming download and parse?\n const blob = await this.fetchBlob(task.resource.uri, task.controller!.signal);\n\n // Convert blob to ArrayBuffer\n const arrayBuffer = await blob.arrayBuffer();\n const uint8Array = new Uint8Array(arrayBuffer);\n\n // Parse MP3 frames using MP3FrameParser\n const parser = new MP3FrameParser();\n const { frames, config } = parser.push(uint8Array);\n const remainingFrames = parser.flush();\n const allFrames = [...frames, ...remainingFrames];\n\n if (!config) {\n throw new Error(`Failed to parse audio config for ${resourceId}`);\n }\n\n // Convert MP3Frame to EncodedAudioChunk\n const audioChunks: EncodedAudioChunk[] = allFrames.map((frame) => {\n return new EncodedAudioChunk({\n type: 'key', // MP3 frames are all key frames\n timestamp: frame.timestampUs,\n duration: frame.durationUs,\n data: frame.data,\n });\n });\n\n // Build AudioDecoderConfig from MP3Config\n const audioConfig: AudioDecoderConfig = {\n codec: 'mp3',\n sampleRate: config.sampleRate,\n numberOfChannels: config.channels,\n };\n\n // Cache to AudioSampleCache\n this.cacheManager.audioSampleCache.set(resourceId, audioChunks, audioConfig);\n } catch (error) {\n console.error(`[ResourceLoader] Failed to parse audio file ${resourceId}:`, error);\n throw error;\n }\n }\n\n /**\n * Parse moov from stream and cache index + audio samples + decode first frame\n */\n private async parseIndexFromStream(\n task: LoadTask,\n stream: ReadableStream<Uint8Array>\n ): Promise<void> {\n const { resourceId, clipId } = task;\n\n try {\n const parser = new MP4IndexParser();\n\n // Only enable first GOP extraction for clips that start at time 0 (cover clips)\n const shouldExtractFirstGOP = clipId ? this.shouldExtractCover(clipId) : false;\n\n const result: MP4ParseResult = await parser.parseFromStream(stream, {\n resourceId: resourceId,\n onFirstFrameReady: shouldExtractFirstGOP\n ? async (index, chunks) => {\n // Set resourceId on index\n index.resourceId = resourceId;\n\n // Cache index immediately\n this.cacheManager.mp4IndexCache.set(resourceId, index);\n\n // Emit event with chunks for Orchestrator to handle\n this.eventBus?.emit(MeframeEvent.ResourceFirstFrameReady, {\n resourceId,\n clipId: clipId!,\n index,\n chunks,\n });\n }\n : undefined,\n });\n\n result.index.resourceId = resourceId;\n\n // Cache index (if not already cached by onFirstFrameReady)\n if (!this.cacheManager.mp4IndexCache.has(resourceId)) {\n this.cacheManager.mp4IndexCache.set(resourceId, result.index);\n }\n\n // Cache audio samples if present\n if (result.audioSamples && result.audioMetadata) {\n this.cacheManager.audioSampleCache.set(\n resourceId,\n result.audioSamples,\n result.audioMetadata\n );\n }\n } catch (error) {\n if (error instanceof NotMP4Error) {\n console.error(\n `[ResourceLoader] Resource ${resourceId} is not an MP4 file: ${error.detectedContainer}`\n );\n } else if (error instanceof MP4MoovNotFoundError) {\n console.error(\n `[ResourceLoader] MP4 moov box not found for ${resourceId} after reading ${error.bytesRead} bytes`\n );\n } else {\n console.error(`[ResourceLoader] Failed to parse MP4 index for ${resourceId}:`, error);\n }\n // Rethrow error to ensure resource is marked as failed\n // In the new architecture (Window Cache + AudioSampleCache), index parsing is critical.\n throw error;\n }\n }\n\n async loadImage(resource: Resource): Promise<ImageBitmap> {\n // If we already have the blob in memory, decoding to ImageBitmap is NOT resource loading.\n // Do not flip resource.state to 'loading' during playback; it will cause buffering oscillation\n // because PlaybackController gates on resource.state === 'ready'.\n const existingBlob = this.blobCache.get(resource.id);\n if (existingBlob) {\n const imageBitmap = await createImageBitmapFromBlob(existingBlob);\n return imageBitmap;\n }\n\n const task: LoadTask = {\n resourceId: resource.id,\n resource: resource,\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n isPreload: false,\n controller: new AbortController(),\n };\n\n // loadImage() bypasses startLoad(), so it must manage resource state by itself.\n let loadError: Error | undefined;\n try {\n this.updateResourceState(resource.id, 'loading');\n // Ensure blob exists, then decode to ImageBitmap.\n await this.ensureImageBlob(task);\n const blob = this.blobCache.get(resource.id);\n if (!blob) throw new Error(`Failed to load image ${resource.id}`);\n const imageBitmap = await createImageBitmapFromBlob(blob);\n this.updateResourceState(resource.id, 'ready');\n return imageBitmap;\n } catch (error) {\n loadError = error as Error;\n this.updateResourceState(resource.id, 'error', toResourceErrorInfo(error));\n throw loadError;\n }\n }\n\n /**\n * Fetch resource as blob (for images, json, etc.)\n */\n private async fetchBlob(uri: string, signal: AbortSignal): Promise<Blob> {\n const response = await fetch(uri, { signal });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return response.blob();\n }\n\n private updateResourceState(\n resourceId: string,\n state: Resource['state'],\n error?: Resource['error']\n ): void {\n const resource = this.model?.resources.get(resourceId);\n if (resource) {\n const oldState = resource.state;\n resource.state = state;\n if (state === 'error') {\n resource.error = error;\n } else {\n // Clear stale error info on non-error transitions (new attempt / recovered)\n resource.error = undefined;\n }\n this.eventBus?.emit(MeframeEvent.ResourceStageChange, {\n type: MeframeEvent.ResourceStageChange,\n resourceId,\n oldState,\n newState: state,\n });\n }\n\n this.onStateChange?.(resourceId, state);\n }\n\n /**\n * Fetch a resource and wait for loading + parsing to complete\n *\n * Returns a Promise that resolves when:\n * - Resource is fully loaded, parsed, and cached (state='ready')\n * - Or rejects if loading/parsing fails\n *\n * Promise lifecycle:\n * 1. enqueueLoad() creates LoadTask with promise/resolve/reject (or reuses existing)\n * 2. processQueue() → startLoad() executes async in background\n * 3. startLoad() completes → finally → completeTask() → task.resolve()/reject()\n */\n async load(resourceId?: string, options?: ResourceLoadOptions): Promise<void> {\n if (!resourceId) {\n return;\n }\n\n const resource = this.model?.getResource(resourceId);\n if (!resource) {\n const m = this.model;\n console.warn('[ResourceLoader] Resource not found in model:', {\n resourceId,\n options: {\n isPreload: options?.isPreload ?? false,\n sessionId: options?.sessionId,\n clipId: options?.clipId,\n trackId: options?.trackId,\n isMainTrack: options?.isMainTrack,\n },\n model: m\n ? {\n fps: m.fps,\n durationUs: m.durationUs,\n mainTrackId: m.mainTrackId,\n trackCount: m.tracks.length,\n resourcesSize: m.resources.size,\n }\n : null,\n });\n return;\n }\n\n // Terminal error: do not retry automatically. Upstream can decide how to recover (e.g. replace URI).\n if (resource.state === 'error' && resource.error?.terminal === true) {\n return;\n }\n\n const isPreload = options?.isPreload ?? false;\n\n // First check: if resource is already ready, nothing to do\n if (resource.state === 'ready') {\n // NOTE: resource.state is runtime-only and can be stale relative to OPFS/index.\n // During export (or after browser storage eviction), the OPFS file may disappear while\n // the model still marks the resource as 'ready'. For video resources we must validate.\n if (resource.type === 'video') {\n const hasIndex = this.cacheManager.mp4IndexCache.has(resourceId);\n if (hasIndex) {\n const cached = await this.cacheManager.hasResourceInCache(resourceId);\n if (cached) {\n return;\n }\n }\n\n // Force a repair load (OPFS/index missing). Mark back to pending for correctness.\n this.updateResourceState(resourceId, 'pending');\n // Fall through to enqueue a load task.\n } else {\n return;\n }\n }\n\n // Second check: if resource is being loaded\n if (resource.state === 'loading') {\n const existingTask = this.taskManager.getActiveTask(resourceId);\n if (existingTask) {\n // Upgrade preload -> foreground through TaskManager to keep counters correct\n // (do NOT mutate existingTask.isPreload here).\n this.taskManager.enqueue(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // If sessionId matches or no sessionId required, reuse existing task\n if (!options?.sessionId || existingTask.sessionId === options.sessionId) {\n return existingTask.promise;\n }\n }\n }\n\n // Third path: check if already has active task\n const existingTask = this.taskManager.getActiveTask(resourceId);\n if (existingTask) {\n // Upgrade preload -> foreground through TaskManager to keep counters correct\n // (do NOT mutate existingTask.isPreload here).\n this.taskManager.enqueue(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // Reuse existing task\n return existingTask.promise;\n }\n\n // Create new task\n const task = this.enqueueLoad(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // Wait for task completion\n return task.promise;\n }\n\n cancel(resourceId: string): void {\n this.taskManager.cancelTask(resourceId);\n this.processQueue();\n }\n\n /**\n * Check if a resource is currently being loaded\n */\n isResourceLoading(resourceId: string): boolean {\n return this.taskManager.hasActiveTask(resourceId);\n }\n\n pause(resourceId: string): void {\n const task = this.taskManager.getActiveTask(resourceId);\n if (task) {\n task.controller?.abort();\n }\n }\n\n async resume(resourceId: string, options?: ResourceLoadOptions): Promise<void> {\n const resource = this.model?.getResource(resourceId);\n if (!resource) {\n throw new Error(`Resource ${resourceId} not found`);\n }\n\n const pausedTask = this.taskManager.getActiveTask(resourceId);\n\n if (pausedTask?.pausedAt !== undefined) {\n this.enqueueLoad(\n resource,\n options?.isPreload ?? false,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n } else {\n await this.load(resourceId, options);\n }\n }\n\n get activeTasks(): Map<string, LoadTask> {\n return this.taskManager.activeTasks;\n }\n\n get taskQueue(): LoadTask[] {\n return this.taskManager.taskQueue;\n }\n\n dispose(): void {\n this.taskManager.clear();\n this.blobCache.clear();\n }\n\n /**\n * Check if a clip needs cover extraction (first GOP decode)\n * Only clips starting at time 0 need fast cover rendering\n */\n private shouldExtractCover(clipId: string): boolean {\n if (!this.model) return false;\n\n const clip = this.model.findClip(clipId);\n return clip?.startUs === 0;\n }\n}\n"],"names":["existingTask"],"mappings":"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"}
1
+ {"version":3,"file":"ResourceLoader.js","sources":["../../../src/stages/load/ResourceLoader.ts"],"sourcesContent":["import { type Resource, type CompositionModel, hasResourceId } from '../../model';\nimport type { ResourceLoadOptions, LoadTask, LoadProgress, ResourceLoaderOptions } from './types';\nimport { TaskManager } from './TaskManager';\nimport { StreamFactory } from './StreamFactory';\nimport { EventPayloadMap, MeframeEvent } from '../../event/events';\nimport { EventBus } from '../../event/EventBus';\nimport { createImageBitmapFromBlob } from '../../utils/image-utils';\nimport { MP4IndexParser, type MP4ParseResult } from '../demux/MP4IndexParser';\nimport { MP3FrameParser } from '../demux/MP3FrameParser';\nimport type { CacheManager } from '../../cache/CacheManager';\nimport { MP4MoovNotFoundError, NotMP4Error, toResourceErrorInfo } from '../../utils/errors';\nimport {\n EmptyStreamError,\n ResourceCorruptedError,\n OPFSQuotaExceededError,\n isDOMException,\n} from '../../utils/errors';\n\nexport class ResourceConflictError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ResourceConflictError';\n }\n}\n\nexport class ResourceLoader {\n private cacheManager: CacheManager;\n private model?: CompositionModel;\n private taskManager: TaskManager;\n private streamFactory: StreamFactory;\n private eventBus?: EventBus<EventPayloadMap>;\n private onProgress?: (progress: LoadProgress) => void;\n private onStateChange?: (resourceId: string, state: Resource['state']) => void;\n private blobCache = new Map<string, Blob>();\n private parsingIndexes = new Set<string>(); // Track in-progress index parsing\n private writingResources = new Set<string>(); // Track in-progress OPFS writes\n\n // Preloading state\n private isPreloadingEnabled = true;\n\n constructor(options: ResourceLoaderOptions) {\n const config = options.config || {};\n const maxConcurrent = config.maxConcurrent ?? 3;\n const preloadConcurrency = config.preloadConcurrency ?? 2;\n\n this.taskManager = new TaskManager(maxConcurrent, preloadConcurrency);\n this.streamFactory = new StreamFactory(options.onProgress, config);\n this.eventBus = options.eventBus;\n this.onProgress = options.onProgress;\n this.onStateChange = options.onStateChange;\n this.cacheManager = options.cacheManager;\n }\n\n async setModel(model: CompositionModel): Promise<void> {\n // Resource IDs are globally stable across models (same id implies same uri/content),\n // so in-flight load tasks remain valid across model switching.\n this.model = model;\n const mainTrack = model.tracks.find((track) => track.id === (model.mainTrackId || 'main'));\n if (mainTrack?.clips?.[0] && hasResourceId(mainTrack.clips[0])) {\n try {\n await this.load(mainTrack.clips[0].resourceId, {\n isPreload: false,\n clipId: mainTrack.clips[0].id,\n trackId: mainTrack.id,\n });\n } catch (error) {\n // Do not block model setup on permanent media/container mismatch.\n if (error instanceof NotMP4Error || error instanceof MP4MoovNotFoundError) {\n // Resource state is already set to 'error' in startLoad().\n } else {\n throw error;\n }\n }\n }\n this.startPreloading();\n }\n\n setPreloadingEnabled(enabled: boolean): void {\n this.isPreloadingEnabled = enabled;\n if (enabled) {\n this.startPreloading();\n }\n }\n\n startPreloading(): void {\n if (!this.model || !this.isPreloadingEnabled) return;\n\n // Collect all video/audio tracks for horizontal loading\n const tracks = this.model.tracks.filter(\n (track) => track.kind === 'video' || track.kind === 'audio'\n );\n if (tracks.length === 0) return;\n\n // Find maximum clip count across all tracks\n const maxClipCount = Math.max(...tracks.map((track) => track.clips.length));\n\n // Horizontal loading: load clip[0] from all tracks, then clip[1], etc.\n for (let clipIndex = 0; clipIndex < maxClipCount; clipIndex++) {\n for (const track of tracks) {\n const clip = track.clips[clipIndex];\n if (!clip || !hasResourceId(clip)) continue;\n\n const resource = this.model.getResource(clip.resourceId);\n if (!resource) continue;\n\n // Skip if already ready, loading, or error\n if (\n resource.state === 'ready' ||\n resource.state === 'loading' ||\n resource.state === 'error'\n ) {\n continue;\n }\n\n // Preload is best-effort; always swallow errors to avoid unhandled promise rejections.\n void this.load(resource.id, { isPreload: true }).catch(() => {});\n }\n }\n }\n\n private enqueueLoad(\n resource: Resource,\n isPreload: boolean = false,\n sessionId?: string,\n clipId?: string,\n trackId?: string\n ): LoadTask {\n // Check if task is already active\n const existingTask = this.taskManager.getActiveTask(resource.id);\n if (existingTask) {\n // Upgrade preload task to foreground if needed (go through TaskManager to keep counters correct)\n this.taskManager.enqueue(resource, isPreload, sessionId, clipId, trackId);\n return existingTask;\n }\n\n // Create new task and enqueue\n const task = this.taskManager.enqueue(resource, isPreload, sessionId, clipId, trackId);\n this.processQueue();\n return task;\n }\n\n private processQueue(): void {\n while (this.taskManager.canProcess) {\n const task = this.taskManager.getNextTask();\n if (!task) break;\n this.startLoad(task);\n }\n }\n\n /**\n * Load video resource (download + cache or read from cache)\n */\n private async loadVideoResource(task: LoadTask): Promise<void> {\n const cached = await this.cacheManager.hasResourceInCache(task.resourceId);\n\n if (cached) {\n // Resource already in OPFS - ensure index is parsed.\n // Important: OPFS files can disappear (browser eviction / manual clear / partial write).\n // If ensureIndexParsed fails with a corruption-related error, self-heal by re-downloading.\n try {\n await this.ensureIndexParsed(task.resourceId);\n } catch (error) {\n if (\n error instanceof ResourceCorruptedError ||\n error instanceof EmptyStreamError ||\n error instanceof MP4MoovNotFoundError\n ) {\n // ensureIndexParsed() already clears corrupted OPFS + mp4Index cache where applicable.\n // Re-download to OPFS and rebuild index cache.\n await this.loadWithOPFSCache(task);\n return;\n }\n throw error;\n }\n\n // Note: Export now uses IndexedVideoSource directly from OPFS\n // No need to transfer stream to worker anymore\n } else {\n // Not cached - download and cache to OPFS\n await this.loadWithOPFSCache(task);\n }\n }\n\n /**\n * Load audio resource (download + parse or reuse cache)\n */\n private async loadAudioResource(task: LoadTask): Promise<void> {\n if (!this.cacheManager.audioSampleCache.has(task.resourceId)) {\n // Not cached - download and parse\n await this.loadAndParseAudioFile(task);\n }\n // If already cached, do nothing (reuse existing cache)\n }\n\n /**\n * Ensure image blob is cached (download + cache or reuse cache).\n *\n * Important: preload should NOT decode ImageBitmap.\n * createImageBitmap can be slow / flaky under heavy main-thread load (playback + export),\n * and decoding isn't required for caching or for later loadImage()/getImageBitmap() calls.\n */\n private async ensureImageBlob(task: LoadTask): Promise<void> {\n // Check cache first\n let blob = this.blobCache.get(task.resourceId);\n\n if (!blob) {\n // Not cached: download and cache\n if (task.controller) {\n blob = await this.fetchBlob(task.resource.uri, task.controller.signal);\n this.blobCache.set(task.resourceId, blob);\n } else {\n return;\n }\n }\n }\n\n /**\n * Get cached ImageBitmap (for already loaded resources)\n * Used by VideoClipSession to batch transfer attachments\n */\n async getImageBitmap(resourceId: string): Promise<ImageBitmap | null> {\n const blob = this.blobCache.get(resourceId);\n if (!blob) return null;\n return await createImageBitmapFromBlob(blob);\n }\n\n /**\n * Load text resource (json/text)\n */\n private async loadTextResource(task: LoadTask): Promise<void> {\n if (task.controller) {\n await this.fetchBlob(task.resource.uri, task.controller.signal);\n }\n }\n\n /**\n * Start loading a resource\n * Handles state management (loading → ready/error) for all resource types\n */\n private async startLoad(task: LoadTask): Promise<void> {\n this.taskManager.activateTask(task);\n let loadError: Error | undefined;\n\n try {\n this.updateResourceState(task.resourceId, 'loading');\n task.controller = new AbortController();\n\n // Route to specific handlers based on resource type\n switch (task.resource.type) {\n case 'image': {\n // Preload path: only cache blob, avoid createImageBitmap during export/playback concurrency.\n await this.ensureImageBlob(task);\n break;\n }\n\n case 'video':\n await this.loadVideoResource(task);\n break;\n\n case 'audio':\n await this.loadAudioResource(task);\n break;\n\n case 'json':\n case 'text':\n await this.loadTextResource(task);\n break;\n }\n\n // Unified state update for all resource types\n this.updateResourceState(task.resourceId, 'ready');\n } catch (error) {\n task.error = error as Error;\n loadError = error as Error;\n this.updateResourceState(task.resourceId, 'error', toResourceErrorInfo(error));\n } finally {\n this.taskManager.completeTask(task.resourceId, loadError);\n this.processQueue();\n }\n }\n\n /**\n * Ensure MP4 index is parsed for a cached resource\n */\n async ensureIndexParsed(resourceId: string): Promise<void> {\n // Check if index already exists\n if (this.cacheManager.mp4IndexCache.has(resourceId)) {\n return;\n }\n\n // Wait for OPFS write to complete\n while (this.writingResources.has(resourceId)) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n // Check if already parsing (avoid duplicate parsing for same resource)\n if (this.parsingIndexes.has(resourceId)) {\n // Wait for the in-progress parsing to complete\n while (this.parsingIndexes.has(resourceId)) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n return;\n }\n\n // Mark as parsing\n this.parsingIndexes.add(resourceId);\n\n try {\n // Validate file exists and has content\n const cached = await this.cacheManager.hasResourceInCache(resourceId);\n if (!cached) {\n throw new ResourceCorruptedError(resourceId, 'File not found in OPFS or is empty');\n }\n\n this.onProgress?.({\n resourceId,\n cached: true,\n bytesLoaded: 0,\n totalBytes: 0,\n percentage: 0,\n speed: 0,\n estimatedTimeRemaining: 0,\n });\n // Parse from OPFS file\n const stream = await this.createOPFSReadStream(resourceId);\n // Create minimal task for parsing (no clipId since this is a background index parse)\n const parseTask: LoadTask = {\n resourceId,\n resource: { id: resourceId, type: 'video', uri: '' },\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n isPreload: false,\n };\n await this.parseIndexFromStream(parseTask, stream);\n\n this.onProgress?.({\n resourceId,\n cached: true,\n bytesLoaded: 0,\n totalBytes: 0,\n percentage: 100,\n speed: 0,\n estimatedTimeRemaining: 0,\n });\n } catch (error) {\n // Handle empty stream error by clearing corrupted cache\n if (\n error instanceof EmptyStreamError ||\n error instanceof ResourceCorruptedError ||\n error instanceof MP4MoovNotFoundError\n ) {\n console.warn(`[ResourceLoader] Corrupted cache detected for ${resourceId}, clearing...`);\n try {\n await this.cacheManager.resourceCache.deleteResource(resourceId);\n this.cacheManager.mp4IndexCache.delete(resourceId);\n } catch (deleteError) {\n console.error(`[ResourceLoader] Failed to clear corrupted cache:`, deleteError);\n }\n }\n throw error;\n } finally {\n // Remove from parsing set\n this.parsingIndexes.delete(resourceId);\n }\n }\n\n /**\n * Create ReadableStream from OPFS file\n * Reuses OPFSManager's underlying file access\n */\n private async createOPFSReadStream(resourceId: string): Promise<ReadableStream<Uint8Array>> {\n const opfsManager = this.cacheManager.resourceCache.opfsManager;\n const projectId = this.cacheManager.resourceCache.projectId;\n\n try {\n // Get file handle from OPFS\n const dir = await opfsManager.getProjectDir(projectId, 'resource');\n const fileName = `${resourceId}.mp4`;\n const fileHandle = await dir.getFileHandle(fileName, { create: false });\n const file = await fileHandle.getFile();\n\n // Return native stream\n return file.stream();\n } catch (error) {\n if (isDOMException(error, 'NotFoundError')) {\n throw new ResourceCorruptedError(resourceId, 'File not found in OPFS');\n }\n throw error;\n }\n }\n\n /**\n * Load resource and cache to OPFS + parse moov + extract first frame\n *\n * Strategy: Parallel tee() approach for fast first frame\n * - One stream writes to OPFS (background)\n * - Another stream parses moov and extracts first GOP\n * - First frame is decoded and cached immediately\n */\n private async loadWithOPFSCache(task: LoadTask): Promise<void> {\n const stream = await this.streamFactory.createRegularStream(task);\n if (!stream) {\n throw new Error(`Failed to create stream for ${task.resourceId}`);\n }\n\n // Export mode: only 2-way split (OPFS + parsing)\n // IndexedVideoSource will read directly from OPFS later\n const [opfsStream, parseStream] = stream.tee();\n\n // Parallel execution: write to OPFS and parse index\n await Promise.all([\n this.writeToOPFS(task.resourceId, opfsStream, task),\n this.parseIndexFromStream(task, parseStream),\n ]);\n }\n\n /**\n * Write resource stream to OPFS with retry on quota exceeded\n * If quota is exceeded and old projects are evicted, fetches a fresh stream and retries\n */\n private async writeToOPFS(\n resourceId: string,\n stream: ReadableStream<Uint8Array>,\n task?: LoadTask\n ): Promise<void> {\n this.writingResources.add(resourceId);\n try {\n await this.cacheManager.resourceCache.writeResource(resourceId, stream);\n } catch (error) {\n if (error instanceof OPFSQuotaExceededError && error.retryable && task) {\n console.log(\n `[ResourceLoader] OPFS quota exceeded for ${resourceId}, retrying with fresh stream...`\n );\n\n // Create fresh stream for retry\n const retryStream = await this.streamFactory.createRegularStream(task);\n if (!retryStream) {\n throw new Error(`Failed to create retry stream for ${resourceId}`);\n }\n\n // Retry write with fresh stream\n await this.cacheManager.resourceCache.writeResource(resourceId, retryStream);\n } else {\n throw error;\n }\n } finally {\n this.writingResources.delete(resourceId);\n }\n }\n\n /**\n * Load and parse audio file (MP3/WAV) in main thread\n * Extract EncodedAudioChunk and cache to AudioSampleCache\n * Aligned with video audio track extraction (unified architecture)\n */\n private async loadAndParseAudioFile(task: LoadTask): Promise<void> {\n const { resourceId } = task;\n\n try {\n // TODO: Streaming download and parse?\n const blob = await this.fetchBlob(task.resource.uri, task.controller!.signal);\n\n // Convert blob to ArrayBuffer\n const arrayBuffer = await blob.arrayBuffer();\n const uint8Array = new Uint8Array(arrayBuffer);\n\n // Parse MP3 frames using MP3FrameParser\n const parser = new MP3FrameParser();\n const { frames, config } = parser.push(uint8Array);\n const remainingFrames = parser.flush();\n const allFrames = [...frames, ...remainingFrames];\n\n if (!config) {\n throw new Error(`Failed to parse audio config for ${resourceId}`);\n }\n\n // Convert MP3Frame to EncodedAudioChunk\n const audioChunks: EncodedAudioChunk[] = allFrames.map((frame) => {\n return new EncodedAudioChunk({\n type: 'key', // MP3 frames are all key frames\n timestamp: frame.timestampUs,\n duration: frame.durationUs,\n data: frame.data,\n });\n });\n\n // Build AudioDecoderConfig from MP3Config\n const audioConfig: AudioDecoderConfig = {\n codec: 'mp3',\n sampleRate: config.sampleRate,\n numberOfChannels: config.channels,\n };\n\n // Cache to AudioSampleCache\n this.cacheManager.audioSampleCache.set(resourceId, audioChunks, audioConfig);\n } catch (error) {\n console.error(`[ResourceLoader] Failed to parse audio file ${resourceId}:`, error);\n throw error;\n }\n }\n\n /**\n * Parse moov from stream and cache index + audio samples + decode first frame\n */\n private async parseIndexFromStream(\n task: LoadTask,\n stream: ReadableStream<Uint8Array>\n ): Promise<void> {\n const { resourceId, clipId } = task;\n\n try {\n const parser = new MP4IndexParser();\n\n // Only enable first GOP extraction for clips that start at time 0 (cover clips)\n const shouldExtractFirstGOP = clipId ? this.shouldExtractCover(clipId) : false;\n\n const result: MP4ParseResult = await parser.parseFromStream(stream, {\n resourceId: resourceId,\n onFirstFrameReady: shouldExtractFirstGOP\n ? async (index, chunks) => {\n // Set resourceId on index\n index.resourceId = resourceId;\n\n // Cache index immediately\n this.cacheManager.mp4IndexCache.set(resourceId, index);\n\n // Emit event with chunks for Orchestrator to handle\n this.eventBus?.emit(MeframeEvent.ResourceFirstFrameReady, {\n resourceId,\n clipId: clipId!,\n index,\n chunks,\n });\n }\n : undefined,\n });\n\n result.index.resourceId = resourceId;\n\n // Cache index (if not already cached by onFirstFrameReady)\n if (!this.cacheManager.mp4IndexCache.has(resourceId)) {\n this.cacheManager.mp4IndexCache.set(resourceId, result.index);\n }\n\n // Cache audio samples if present\n if (result.audioSamples && result.audioMetadata) {\n this.cacheManager.audioSampleCache.set(\n resourceId,\n result.audioSamples,\n result.audioMetadata\n );\n }\n } catch (error) {\n if (error instanceof NotMP4Error) {\n console.error(\n `[ResourceLoader] Resource ${resourceId} is not an MP4 file: ${error.detectedContainer}`\n );\n } else if (error instanceof MP4MoovNotFoundError) {\n console.error(\n `[ResourceLoader] MP4 moov box not found for ${resourceId} after reading ${error.bytesRead} bytes`\n );\n } else {\n console.error(`[ResourceLoader] Failed to parse MP4 index for ${resourceId}:`, error);\n }\n // Rethrow error to ensure resource is marked as failed\n // In the new architecture (Window Cache + AudioSampleCache), index parsing is critical.\n throw error;\n }\n }\n\n async loadImage(resource: Resource): Promise<ImageBitmap> {\n // If we already have the blob in memory, decoding to ImageBitmap is NOT resource loading.\n // Do not flip resource.state to 'loading' during playback; it will cause buffering oscillation\n // because PlaybackController gates on resource.state === 'ready'.\n const existingBlob = this.blobCache.get(resource.id);\n if (existingBlob) {\n const imageBitmap = await createImageBitmapFromBlob(existingBlob);\n return imageBitmap;\n }\n\n const task: LoadTask = {\n resourceId: resource.id,\n resource: resource,\n bytesLoaded: 0,\n totalBytes: 0,\n startTime: Date.now(),\n isPreload: false,\n controller: new AbortController(),\n };\n\n // loadImage() bypasses startLoad(), so it must manage resource state by itself.\n let loadError: Error | undefined;\n try {\n this.updateResourceState(resource.id, 'loading');\n // Ensure blob exists, then decode to ImageBitmap.\n await this.ensureImageBlob(task);\n const blob = this.blobCache.get(resource.id);\n if (!blob) throw new Error(`Failed to load image ${resource.id}`);\n const imageBitmap = await createImageBitmapFromBlob(blob);\n this.updateResourceState(resource.id, 'ready');\n return imageBitmap;\n } catch (error) {\n loadError = error as Error;\n this.updateResourceState(resource.id, 'error', toResourceErrorInfo(error));\n throw loadError;\n }\n }\n\n /**\n * Fetch resource as blob (for images, json, etc.)\n */\n private async fetchBlob(uri: string, signal: AbortSignal): Promise<Blob> {\n const response = await fetch(uri, { signal });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return response.blob();\n }\n\n private updateResourceState(\n resourceId: string,\n state: Resource['state'],\n error?: Resource['error']\n ): void {\n const resource = this.model?.resources.get(resourceId);\n if (resource) {\n const oldState = resource.state;\n resource.state = state;\n if (state === 'error') {\n resource.error = error;\n } else {\n // Clear stale error info on non-error transitions (new attempt / recovered)\n resource.error = undefined;\n }\n this.eventBus?.emit(MeframeEvent.ResourceStageChange, {\n type: MeframeEvent.ResourceStageChange,\n resourceId,\n oldState,\n newState: state,\n });\n }\n\n this.onStateChange?.(resourceId, state);\n }\n\n /**\n * Fetch a resource and wait for loading + parsing to complete\n *\n * Returns a Promise that resolves when:\n * - Resource is fully loaded, parsed, and cached (state='ready')\n * - Or rejects if loading/parsing fails\n *\n * Promise lifecycle:\n * 1. enqueueLoad() creates LoadTask with promise/resolve/reject (or reuses existing)\n * 2. processQueue() → startLoad() executes async in background\n * 3. startLoad() completes → finally → completeTask() → task.resolve()/reject()\n */\n async load(resourceId?: string, options?: ResourceLoadOptions): Promise<void> {\n if (!resourceId) {\n return;\n }\n\n const resource = this.model?.getResource(resourceId);\n if (!resource) {\n const m = this.model;\n console.warn('[ResourceLoader] Resource not found in model:', {\n resourceId,\n options: {\n isPreload: options?.isPreload ?? false,\n sessionId: options?.sessionId,\n clipId: options?.clipId,\n trackId: options?.trackId,\n isMainTrack: options?.isMainTrack,\n },\n model: m\n ? {\n fps: m.fps,\n durationUs: m.durationUs,\n mainTrackId: m.mainTrackId,\n trackCount: m.tracks.length,\n resourcesSize: m.resources.size,\n }\n : null,\n });\n return;\n }\n\n // Terminal error: do not retry automatically. Upstream can decide how to recover (e.g. replace URI).\n if (resource.state === 'error' && resource.error?.terminal === true) {\n return;\n }\n\n const isPreload = options?.isPreload ?? false;\n\n // First check: if resource is already ready, nothing to do\n if (resource.state === 'ready') {\n // NOTE: resource.state is runtime-only and can be stale relative to OPFS/index.\n // During export (or after browser storage eviction), the OPFS file may disappear while\n // the model still marks the resource as 'ready'. For video resources we must validate.\n if (resource.type === 'video') {\n const hasIndex = this.cacheManager.mp4IndexCache.has(resourceId);\n if (hasIndex) {\n const cached = await this.cacheManager.hasResourceInCache(resourceId);\n if (cached) {\n return;\n }\n }\n\n // Force a repair load (OPFS/index missing). Mark back to pending for correctness.\n this.updateResourceState(resourceId, 'pending');\n // Fall through to enqueue a load task.\n } else {\n return;\n }\n }\n\n // Second check: if resource is being loaded\n if (resource.state === 'loading') {\n const existingTask = this.taskManager.getActiveTask(resourceId);\n if (existingTask) {\n // Upgrade preload -> foreground through TaskManager to keep counters correct\n // (do NOT mutate existingTask.isPreload here).\n this.taskManager.enqueue(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // If sessionId matches or no sessionId required, reuse existing task\n if (!options?.sessionId || existingTask.sessionId === options.sessionId) {\n return existingTask.promise;\n }\n }\n }\n\n // Third path: check if already has active task\n const existingTask = this.taskManager.getActiveTask(resourceId);\n if (existingTask) {\n // Upgrade preload -> foreground through TaskManager to keep counters correct\n // (do NOT mutate existingTask.isPreload here).\n this.taskManager.enqueue(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // Reuse existing task\n return existingTask.promise;\n }\n\n // Create new task\n const task = this.enqueueLoad(\n resource,\n isPreload,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n\n // Wait for task completion\n return task.promise;\n }\n\n cancel(resourceId: string): void {\n this.taskManager.cancelTask(resourceId);\n this.processQueue();\n }\n\n /**\n * Check if a resource is currently being loaded\n */\n isResourceLoading(resourceId: string): boolean {\n return this.taskManager.hasActiveTask(resourceId);\n }\n\n pause(resourceId: string): void {\n const task = this.taskManager.getActiveTask(resourceId);\n if (task) {\n task.controller?.abort();\n }\n }\n\n async resume(resourceId: string, options?: ResourceLoadOptions): Promise<void> {\n const resource = this.model?.getResource(resourceId);\n if (!resource) {\n throw new Error(`Resource ${resourceId} not found`);\n }\n\n const pausedTask = this.taskManager.getActiveTask(resourceId);\n\n if (pausedTask?.pausedAt !== undefined) {\n this.enqueueLoad(\n resource,\n options?.isPreload ?? false,\n options?.sessionId,\n options?.clipId,\n options?.trackId\n );\n } else {\n await this.load(resourceId, options);\n }\n }\n\n get activeTasks(): Map<string, LoadTask> {\n return this.taskManager.activeTasks;\n }\n\n get taskQueue(): LoadTask[] {\n return this.taskManager.taskQueue;\n }\n\n dispose(): void {\n this.taskManager.clear();\n this.blobCache.clear();\n }\n\n /**\n * Check if a clip needs cover extraction (first GOP decode)\n * Only clips starting at time 0 need fast cover rendering\n */\n private shouldExtractCover(clipId: string): boolean {\n if (!this.model) return false;\n\n const clip = this.model.findClip(clipId);\n return clip?.startUs === 0;\n }\n}\n"],"names":["existingTask"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"StreamFactory.d.ts","sourceRoot":"","sources":["../../../src/stages/load/StreamFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEpF;;GAEG;AACH,qBAAa,aAAa;IAatB,OAAO,CAAC,UAAU,CAAC;IAXrB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAe;IAC9D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAe;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAK;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAO;IAElD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAG1B,UAAU,CAAC,GAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,aAAA,EACrD,MAAM,CAAC,EAAE,YAAY;IASvB;;OAEG;IACG,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAwBrF;;OAEG;IACG,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IAuBrF;;OAEG;IACH,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC;IA8DjE;;OAEG;IACH,gBAAgB,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC;IAmChG;;OAEG;YACW,cAAc;IAkC5B;;OAEG;IACH,OAAO,CAAC,cAAc;CAkBvB"}
1
+ {"version":3,"file":"StreamFactory.d.ts","sourceRoot":"","sources":["../../../src/stages/load/StreamFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEpF;;GAEG;AACH,qBAAa,aAAa;IAatB,OAAO,CAAC,UAAU,CAAC;IAXrB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAe;IAC9D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAe;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAK;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAO;IAElD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAG1B,UAAU,CAAC,GAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,aAAA,EACrD,MAAM,CAAC,EAAE,YAAY;IASvB;;OAEG;IACG,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAwBrF;;OAEG;IACG,mBAAmB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IAuBrF;;OAEG;IACH,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC;IA+DjE;;OAEG;IACH,gBAAgB,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,GAAG,cAAc,CAAC,UAAU,CAAC;IAoChG;;OAEG;YACW,cAAc;IAkC5B;;OAEG;IACH,OAAO,CAAC,cAAc;CAwBvB"}
@@ -98,7 +98,8 @@ class StreamFactory {
98
98
  controller.enqueue(uint8Array);
99
99
  bytesReceived += chunk.byteLength;
100
100
  task.bytesLoaded = bytesReceived;
101
- this.reportProgress(task, bytesReceived, totalSize);
101
+ let cached = false;
102
+ this.reportProgress(task, cached, bytesReceived, totalSize);
102
103
  } catch (error) {
103
104
  if (error.name === "AbortError") {
104
105
  task.pausedAt = bytesReceived;
@@ -134,7 +135,8 @@ class StreamFactory {
134
135
  }
135
136
  bytesReceived += value.byteLength;
136
137
  task.bytesLoaded = bytesReceived;
137
- this.reportProgress(task, bytesReceived, task.totalBytes);
138
+ let cached = false;
139
+ this.reportProgress(task, cached, bytesReceived, task.totalBytes);
138
140
  controller.enqueue(value);
139
141
  } catch (error) {
140
142
  controller.error(error);
@@ -180,12 +182,13 @@ class StreamFactory {
180
182
  /**
181
183
  * Report progress
182
184
  */
183
- reportProgress(task, bytesLoaded, totalBytes) {
185
+ reportProgress(task, cached, bytesLoaded, totalBytes) {
184
186
  if (!this.onProgress || !task.resource.id) return;
185
187
  const elapsedSeconds = (Date.now() - task.startTime) / 1e3;
186
188
  const speed = bytesLoaded / elapsedSeconds;
187
189
  const progress = {
188
190
  resourceId: task.resource.id,
191
+ cached,
189
192
  bytesLoaded,
190
193
  totalBytes,
191
194
  percentage: totalBytes > 0 ? bytesLoaded / totalBytes * 100 : 0,
@@ -1 +1 @@
1
- {"version":3,"file":"StreamFactory.js","sources":["../../../src/stages/load/StreamFactory.ts"],"sourcesContent":["import type { LoadTask, LoadProgress, StreamMetadata, LoaderConfig } from './types';\n\n/**\n * Factory for creating various types of streams with backpressure configuration\n */\nexport class StreamFactory {\n // Default values as fallback\n private static readonly DEFAULT_HIGH_WATER_MARK = 1024 * 1024; // 1MB\n private static readonly DEFAULT_CHUNK_SIZE = 1024 * 1024; // 1MB\n private static readonly DEFAULT_RETRY_COUNT = 3;\n private static readonly DEFAULT_RETRY_DELAY = 500; // ms\n\n private readonly highWaterMark: number;\n private readonly chunkSize: number;\n private readonly retryCount: number;\n private readonly retryDelay: number;\n\n constructor(\n private onProgress?: (progress: LoadProgress) => void,\n config?: LoaderConfig\n ) {\n // Use provided config with local defaults as fallback\n this.highWaterMark = config?.highWaterMark ?? StreamFactory.DEFAULT_HIGH_WATER_MARK;\n this.chunkSize = config?.chunkSize ?? StreamFactory.DEFAULT_CHUNK_SIZE;\n this.retryCount = config?.retryCount ?? StreamFactory.DEFAULT_RETRY_COUNT;\n this.retryDelay = config?.retryDelay ?? StreamFactory.DEFAULT_RETRY_DELAY;\n }\n\n /**\n * Fetch metadata for a resource\n */\n async fetchMetadata(url: string, signal: AbortSignal): Promise<StreamMetadata | null> {\n try {\n const response = await fetch(url, {\n method: 'HEAD',\n signal,\n });\n\n if (!response.ok) {\n return null;\n }\n\n return {\n url,\n contentLength: Number(response.headers.get('content-length') || 0),\n acceptRanges: response.headers.get('accept-ranges') === 'bytes',\n contentType: response.headers.get('content-type') || undefined,\n lastModified: response.headers.get('last-modified') || undefined,\n etag: response.headers.get('etag') || undefined,\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Create a regular (non-resumable) stream\n */\n async createRegularStream(task: LoadTask): Promise<ReadableStream<Uint8Array> | null> {\n const response = await this.fetchWithRetry(task.resource.uri, {\n method: 'GET', // Explicitly specify GET (vs HEAD)\n signal: task.controller!.signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n // Return null if body is empty (e.g., from cached HEAD response)\n // ResourceLoader should retry with forceFresh in this case\n if (!response.body) {\n console.warn(\n `[StreamFactory] Response body is null for ${task.resource.uri}, possibly cached HEAD response`\n );\n return null;\n }\n\n task.totalBytes = Number(response.headers.get('content-length') || 0);\n return this.wrapWithProgress(response.body, task);\n }\n\n /**\n * Create a resumable stream with Range support\n */\n createResumableStream(task: LoadTask): ReadableStream<Uint8Array> {\n let bytesReceived = task.pausedAt || 0;\n const totalSize = task.metadata?.contentLength || 0;\n\n return new ReadableStream<Uint8Array>(\n {\n pull: async (controller) => {\n // Backpressure: wait for downstream to consume\n if (controller.desiredSize !== null && controller.desiredSize <= 0) {\n return;\n }\n\n if (bytesReceived >= totalSize) {\n controller.close();\n return;\n }\n\n const end = Math.min(bytesReceived + this.chunkSize - 1, totalSize - 1);\n\n try {\n const response = await fetch(task.resource.uri, {\n method: 'GET',\n signal: task.controller!.signal,\n headers: { Range: `bytes=${bytesReceived}-${end}` },\n // Use default cache for Range requests\n });\n\n if (!response.ok && response.status !== 206) {\n throw new Error(`Range request failed: ${response.status}`);\n }\n\n if (!response.body) {\n throw new Error(`Response body is null in Range request for ${task.resource.uri}`);\n }\n\n const chunk = await response.arrayBuffer();\n const uint8Array = new Uint8Array(chunk);\n\n controller.enqueue(uint8Array);\n bytesReceived += chunk.byteLength;\n task.bytesLoaded = bytesReceived;\n\n this.reportProgress(task, bytesReceived, totalSize);\n } catch (error: any) {\n if (error.name === 'AbortError') {\n task.pausedAt = bytesReceived;\n }\n controller.error(error);\n }\n },\n\n cancel: () => {\n task.pausedAt = bytesReceived;\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: (chunk) => chunk.byteLength,\n }\n );\n }\n\n /**\n * Wrap stream with progress tracking\n */\n wrapWithProgress(stream: ReadableStream<Uint8Array>, task: LoadTask): ReadableStream<Uint8Array> {\n const reader = stream.getReader();\n let bytesReceived = 0;\n\n return new ReadableStream<Uint8Array>(\n {\n pull: async (controller) => {\n while (true) {\n try {\n const { done, value } = await reader.read();\n if (done) {\n controller.close();\n break;\n }\n\n bytesReceived += value.byteLength;\n task.bytesLoaded = bytesReceived;\n this.reportProgress(task, bytesReceived, task.totalBytes);\n controller.enqueue(value);\n } catch (error) {\n controller.error(error);\n break;\n }\n }\n },\n\n cancel: () => reader.cancel(),\n },\n {\n highWaterMark: this.highWaterMark,\n size: (chunk) => chunk.byteLength,\n }\n );\n }\n\n /**\n * Fetch with retry logic\n */\n private async fetchWithRetry(url: string, init: RequestInit): Promise<Response> {\n let lastError: Error | null = null;\n\n for (let i = 0; i <= this.retryCount; i++) {\n try {\n const response = await fetch(url, init);\n if (response.ok || response.status === 206) {\n return response;\n }\n\n // Don't retry on client errors (4xx)\n if (response.status >= 400 && response.status < 500) {\n return response;\n }\n\n lastError = new Error(`HTTP ${response.status}: ${response.statusText}`);\n } catch (error: any) {\n lastError = error;\n\n // Don't retry on abort\n if (error.name === 'AbortError') {\n throw error;\n }\n }\n\n // Exponential backoff\n if (i < this.retryCount) {\n await new Promise((resolve) => setTimeout(resolve, this.retryDelay * Math.pow(2, i)));\n }\n }\n\n throw lastError || new Error('Failed to fetch after retries');\n }\n\n /**\n * Report progress\n */\n private reportProgress(task: LoadTask, bytesLoaded: number, totalBytes: number): void {\n if (!this.onProgress || !task.resource.id) return;\n\n const elapsedSeconds = (Date.now() - task.startTime) / 1000;\n const speed = bytesLoaded / elapsedSeconds;\n\n const progress: LoadProgress = {\n resourceId: task.resource.id,\n bytesLoaded,\n totalBytes,\n percentage: totalBytes > 0 ? (bytesLoaded / totalBytes) * 100 : 0,\n speed,\n estimatedTimeRemaining:\n speed > 0 && totalBytes > 0 ? ((totalBytes - bytesLoaded) / speed) * 1000 : 0,\n };\n\n this.onProgress(progress);\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StreamFactory.js","sources":["../../../src/stages/load/StreamFactory.ts"],"sourcesContent":["import type { LoadTask, LoadProgress, StreamMetadata, LoaderConfig } from './types';\n\n/**\n * Factory for creating various types of streams with backpressure configuration\n */\nexport class StreamFactory {\n // Default values as fallback\n private static readonly DEFAULT_HIGH_WATER_MARK = 1024 * 1024; // 1MB\n private static readonly DEFAULT_CHUNK_SIZE = 1024 * 1024; // 1MB\n private static readonly DEFAULT_RETRY_COUNT = 3;\n private static readonly DEFAULT_RETRY_DELAY = 500; // ms\n\n private readonly highWaterMark: number;\n private readonly chunkSize: number;\n private readonly retryCount: number;\n private readonly retryDelay: number;\n\n constructor(\n private onProgress?: (progress: LoadProgress) => void,\n config?: LoaderConfig\n ) {\n // Use provided config with local defaults as fallback\n this.highWaterMark = config?.highWaterMark ?? StreamFactory.DEFAULT_HIGH_WATER_MARK;\n this.chunkSize = config?.chunkSize ?? StreamFactory.DEFAULT_CHUNK_SIZE;\n this.retryCount = config?.retryCount ?? StreamFactory.DEFAULT_RETRY_COUNT;\n this.retryDelay = config?.retryDelay ?? StreamFactory.DEFAULT_RETRY_DELAY;\n }\n\n /**\n * Fetch metadata for a resource\n */\n async fetchMetadata(url: string, signal: AbortSignal): Promise<StreamMetadata | null> {\n try {\n const response = await fetch(url, {\n method: 'HEAD',\n signal,\n });\n\n if (!response.ok) {\n return null;\n }\n\n return {\n url,\n contentLength: Number(response.headers.get('content-length') || 0),\n acceptRanges: response.headers.get('accept-ranges') === 'bytes',\n contentType: response.headers.get('content-type') || undefined,\n lastModified: response.headers.get('last-modified') || undefined,\n etag: response.headers.get('etag') || undefined,\n };\n } catch {\n return null;\n }\n }\n\n /**\n * Create a regular (non-resumable) stream\n */\n async createRegularStream(task: LoadTask): Promise<ReadableStream<Uint8Array> | null> {\n const response = await this.fetchWithRetry(task.resource.uri, {\n method: 'GET', // Explicitly specify GET (vs HEAD)\n signal: task.controller!.signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n // Return null if body is empty (e.g., from cached HEAD response)\n // ResourceLoader should retry with forceFresh in this case\n if (!response.body) {\n console.warn(\n `[StreamFactory] Response body is null for ${task.resource.uri}, possibly cached HEAD response`\n );\n return null;\n }\n\n task.totalBytes = Number(response.headers.get('content-length') || 0);\n return this.wrapWithProgress(response.body, task);\n }\n\n /**\n * Create a resumable stream with Range support\n */\n createResumableStream(task: LoadTask): ReadableStream<Uint8Array> {\n let bytesReceived = task.pausedAt || 0;\n const totalSize = task.metadata?.contentLength || 0;\n\n return new ReadableStream<Uint8Array>(\n {\n pull: async (controller) => {\n // Backpressure: wait for downstream to consume\n if (controller.desiredSize !== null && controller.desiredSize <= 0) {\n return;\n }\n\n if (bytesReceived >= totalSize) {\n controller.close();\n return;\n }\n\n const end = Math.min(bytesReceived + this.chunkSize - 1, totalSize - 1);\n\n try {\n const response = await fetch(task.resource.uri, {\n method: 'GET',\n signal: task.controller!.signal,\n headers: { Range: `bytes=${bytesReceived}-${end}` },\n // Use default cache for Range requests\n });\n\n if (!response.ok && response.status !== 206) {\n throw new Error(`Range request failed: ${response.status}`);\n }\n\n if (!response.body) {\n throw new Error(`Response body is null in Range request for ${task.resource.uri}`);\n }\n\n const chunk = await response.arrayBuffer();\n const uint8Array = new Uint8Array(chunk);\n\n controller.enqueue(uint8Array);\n bytesReceived += chunk.byteLength;\n task.bytesLoaded = bytesReceived;\n\n let cached = false;\n this.reportProgress(task, cached, bytesReceived, totalSize);\n } catch (error: any) {\n if (error.name === 'AbortError') {\n task.pausedAt = bytesReceived;\n }\n controller.error(error);\n }\n },\n\n cancel: () => {\n task.pausedAt = bytesReceived;\n },\n },\n {\n highWaterMark: this.highWaterMark,\n size: (chunk) => chunk.byteLength,\n }\n );\n }\n\n /**\n * Wrap stream with progress tracking\n */\n wrapWithProgress(stream: ReadableStream<Uint8Array>, task: LoadTask): ReadableStream<Uint8Array> {\n const reader = stream.getReader();\n let bytesReceived = 0;\n\n return new ReadableStream<Uint8Array>(\n {\n pull: async (controller) => {\n while (true) {\n try {\n const { done, value } = await reader.read();\n if (done) {\n controller.close();\n break;\n }\n\n bytesReceived += value.byteLength;\n task.bytesLoaded = bytesReceived;\n let cached = false;\n this.reportProgress(task, cached, bytesReceived, task.totalBytes);\n controller.enqueue(value);\n } catch (error) {\n controller.error(error);\n break;\n }\n }\n },\n\n cancel: () => reader.cancel(),\n },\n {\n highWaterMark: this.highWaterMark,\n size: (chunk) => chunk.byteLength,\n }\n );\n }\n\n /**\n * Fetch with retry logic\n */\n private async fetchWithRetry(url: string, init: RequestInit): Promise<Response> {\n let lastError: Error | null = null;\n\n for (let i = 0; i <= this.retryCount; i++) {\n try {\n const response = await fetch(url, init);\n if (response.ok || response.status === 206) {\n return response;\n }\n\n // Don't retry on client errors (4xx)\n if (response.status >= 400 && response.status < 500) {\n return response;\n }\n\n lastError = new Error(`HTTP ${response.status}: ${response.statusText}`);\n } catch (error: any) {\n lastError = error;\n\n // Don't retry on abort\n if (error.name === 'AbortError') {\n throw error;\n }\n }\n\n // Exponential backoff\n if (i < this.retryCount) {\n await new Promise((resolve) => setTimeout(resolve, this.retryDelay * Math.pow(2, i)));\n }\n }\n\n throw lastError || new Error('Failed to fetch after retries');\n }\n\n /**\n * Report progress\n */\n private reportProgress(\n task: LoadTask,\n cached: boolean,\n bytesLoaded: number,\n totalBytes: number\n ): void {\n if (!this.onProgress || !task.resource.id) return;\n\n const elapsedSeconds = (Date.now() - task.startTime) / 1000;\n const speed = bytesLoaded / elapsedSeconds;\n\n const progress: LoadProgress = {\n resourceId: task.resource.id,\n cached,\n bytesLoaded,\n totalBytes,\n percentage: totalBytes > 0 ? (bytesLoaded / totalBytes) * 100 : 0,\n speed,\n estimatedTimeRemaining:\n speed > 0 && totalBytes > 0 ? ((totalBytes - bytesLoaded) / speed) * 1000 : 0,\n };\n\n this.onProgress(progress);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -34,6 +34,7 @@ export interface ClipLoadOptions {
34
34
  }
35
35
  export interface LoadProgress {
36
36
  resourceId: string;
37
+ cached: boolean;
37
38
  bytesLoaded: number;
38
39
  totalBytes: number;
39
40
  percentage: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/stages/load/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/stages/load/types.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { StreamTarget, ArrayBufferTarget, Muxer } from "../../node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js";
1
+ import { StreamTarget, ArrayBufferTarget, Muxer } from "../../medeo-fe/node_modules/.pnpm/mp4-muxer@5.2.2/node_modules/mp4-muxer/build/mp4-muxer.js";
2
2
  import { checkBrowserCompatibility } from "../../utils/platform-utils.js";
3
3
  class MP4Muxer {
4
4
  muxer;
@@ -1,4 +1,4 @@
1
- import * as mp4box_all from "../node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js";
1
+ import * as mp4box_all from "../medeo-fe/node_modules/.pnpm/mp4box@0.5.4/node_modules/mp4box/dist/mp4box.all.js";
2
2
  const lib = mp4box_all;
3
3
  const MP4Box = lib.default && typeof lib.default.createFile === "function" ? lib.default : lib;
4
4
  if (typeof MP4Box.createFile !== "function") {
@@ -2322,7 +2322,7 @@ class BaseEncoder {
2322
2322
  return JSON.stringify(a) === JSON.stringify(b);
2323
2323
  }
2324
2324
  async initialize() {
2325
- console.info("Encoder is initializing...");
2325
+ console.info("Encoder is initializing, config:", this.config);
2326
2326
  if (this.encoder?.state === "configured") {
2327
2327
  console.warn("Encoder is already initialized, skipping initialization");
2328
2328
  return;
@@ -2439,6 +2439,7 @@ class BaseEncoder {
2439
2439
  },
2440
2440
  transform: async (input) => {
2441
2441
  if (!this.isReady) {
2442
+ console.warn("Encoder is not ready, initializing 1...");
2442
2443
  await this.initialize();
2443
2444
  }
2444
2445
  if (this.encoder && this.encoder.encodeQueueSize >= this.encodeQueueThreshold) {
@@ -2454,9 +2455,11 @@ class BaseEncoder {
2454
2455
  });
2455
2456
  }
2456
2457
  if (!this.isReady) {
2458
+ console.warn("Encoder is not ready, initializing 2...");
2457
2459
  await this.initialize();
2458
2460
  }
2459
2461
  if (!this.encoder || this.encoder.state !== "configured") {
2462
+ console.error("Encoder not configured, throwing error");
2460
2463
  throw new Error("Encoder not configured");
2461
2464
  }
2462
2465
  const frame = input.frame || input;
@@ -3240,4 +3243,4 @@ const export_worker = null;
3240
3243
  export {
3241
3244
  export_worker as default
3242
3245
  };
3243
- //# sourceMappingURL=export.worker.DbrPlw6d.js.map
3246
+ //# sourceMappingURL=export.worker.DCStS1mL.js.map