@editframe/elements 0.38.0 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/canvas/EFCanvas.d.ts +4 -4
  2. package/dist/canvas/EFCanvasItem.d.ts +4 -4
  3. package/dist/canvas/overlays/SelectionOverlay.d.ts +2 -2
  4. package/dist/canvas/overlays/SelectionOverlay.js.map +1 -1
  5. package/dist/elements/EFCaptions.js +1 -1
  6. package/dist/elements/EFCaptions.js.map +1 -1
  7. package/dist/elements/EFImage.js +3 -4
  8. package/dist/elements/EFImage.js.map +1 -1
  9. package/dist/elements/EFMedia/BufferedSeekingInput.js +1 -1
  10. package/dist/elements/EFMedia/CachedFetcher.js +99 -0
  11. package/dist/elements/EFMedia/CachedFetcher.js.map +1 -0
  12. package/dist/elements/EFMedia/MediaEngine.d.ts +19 -0
  13. package/dist/elements/EFMedia/MediaEngine.js +129 -0
  14. package/dist/elements/EFMedia/MediaEngine.js.map +1 -0
  15. package/dist/elements/EFMedia/SegmentIndex.d.ts +32 -0
  16. package/dist/elements/EFMedia/SegmentIndex.js +185 -0
  17. package/dist/elements/EFMedia/SegmentIndex.js.map +1 -0
  18. package/dist/elements/EFMedia/SegmentTransport.d.ts +12 -0
  19. package/dist/elements/EFMedia/SegmentTransport.js +69 -0
  20. package/dist/elements/EFMedia/SegmentTransport.js.map +1 -0
  21. package/dist/elements/EFMedia/TimingModel.d.ts +10 -0
  22. package/dist/elements/EFMedia/TimingModel.js +28 -0
  23. package/dist/elements/EFMedia/TimingModel.js.map +1 -0
  24. package/dist/elements/EFMedia/shared/AudioSpanUtils.js +7 -6
  25. package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
  26. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +13 -34
  27. package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
  28. package/dist/elements/EFMedia.d.ts +2 -1
  29. package/dist/elements/EFMedia.js +14 -31
  30. package/dist/elements/EFMedia.js.map +1 -1
  31. package/dist/elements/EFPanZoom.d.ts +4 -4
  32. package/dist/elements/EFSourceMixin.js +1 -1
  33. package/dist/elements/EFSourceMixin.js.map +1 -1
  34. package/dist/elements/EFSurface.d.ts +4 -4
  35. package/dist/elements/EFTemporal.js +2 -1
  36. package/dist/elements/EFTemporal.js.map +1 -1
  37. package/dist/elements/EFTimegroup.js +2 -1
  38. package/dist/elements/EFTimegroup.js.map +1 -1
  39. package/dist/elements/EFVideo.js +204 -187
  40. package/dist/elements/EFVideo.js.map +1 -1
  41. package/dist/gui/EFActiveRootTemporal.d.ts +4 -4
  42. package/dist/gui/EFConfiguration.d.ts +0 -7
  43. package/dist/gui/EFConfiguration.js +0 -5
  44. package/dist/gui/EFConfiguration.js.map +1 -1
  45. package/dist/gui/EFControls.d.ts +2 -2
  46. package/dist/gui/EFDial.d.ts +4 -4
  47. package/dist/gui/EFFocusOverlay.d.ts +4 -4
  48. package/dist/gui/EFOverlayItem.d.ts +4 -4
  49. package/dist/gui/EFOverlayLayer.d.ts +4 -4
  50. package/dist/gui/EFPause.d.ts +4 -4
  51. package/dist/gui/EFPlay.d.ts +4 -4
  52. package/dist/gui/EFResizableBox.d.ts +4 -4
  53. package/dist/gui/EFScrubber.d.ts +4 -4
  54. package/dist/gui/EFTimeDisplay.d.ts +4 -4
  55. package/dist/gui/EFTimelineRuler.d.ts +4 -4
  56. package/dist/gui/EFToggleLoop.d.ts +4 -4
  57. package/dist/gui/EFTogglePlay.d.ts +4 -4
  58. package/dist/gui/EFTransformHandles.d.ts +4 -4
  59. package/dist/gui/EFWorkbench.d.ts +2 -0
  60. package/dist/gui/EFWorkbench.js +68 -1
  61. package/dist/gui/EFWorkbench.js.map +1 -1
  62. package/dist/gui/PlaybackController.d.ts +2 -0
  63. package/dist/gui/PlaybackController.js +11 -1
  64. package/dist/gui/PlaybackController.js.map +1 -1
  65. package/dist/gui/ef-theme.css +11 -0
  66. package/dist/gui/timeline/EFTimeline.js.map +1 -1
  67. package/dist/gui/timeline/EFTimelineRow.d.ts +2 -2
  68. package/dist/gui/timeline/tracks/AudioTrack.js +28 -30
  69. package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -1
  70. package/dist/gui/timeline/tracks/EFThumbnailStrip.d.ts +1 -0
  71. package/dist/gui/timeline/tracks/EFThumbnailStrip.js +41 -8
  72. package/dist/gui/timeline/tracks/EFThumbnailStrip.js.map +1 -1
  73. package/dist/gui/timeline/tracks/VideoTrack.js +2 -2
  74. package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -1
  75. package/dist/gui/timeline/tracks/waveformUtils.js +19 -19
  76. package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -1
  77. package/dist/gui/tree/EFTree.d.ts +4 -4
  78. package/dist/gui/tree/EFTreeItem.d.ts +4 -4
  79. package/dist/preview/QualityUpgradeScheduler.d.ts +8 -0
  80. package/dist/preview/QualityUpgradeScheduler.js +13 -1
  81. package/dist/preview/QualityUpgradeScheduler.js.map +1 -1
  82. package/dist/preview/renderTimegroupToCanvas.d.ts +144 -0
  83. package/dist/preview/renderTimegroupToCanvas.js +56 -3
  84. package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
  85. package/dist/preview/renderTimegroupToCanvas.types.d.ts +22 -1
  86. package/dist/preview/renderTimegroupToVideo.d.ts +27 -0
  87. package/dist/preview/renderTimegroupToVideo.js +13 -5
  88. package/dist/preview/renderTimegroupToVideo.js.map +1 -1
  89. package/dist/preview/renderVideoToVideo.js +5 -6
  90. package/dist/preview/renderVideoToVideo.js.map +1 -1
  91. package/dist/preview/renderers.d.ts +56 -0
  92. package/dist/preview/renderers.js +13 -1
  93. package/dist/preview/renderers.js.map +1 -1
  94. package/dist/preview/rendering/inlineImages.d.ts +13 -0
  95. package/dist/preview/rendering/inlineImages.js +7 -1
  96. package/dist/preview/rendering/inlineImages.js.map +1 -1
  97. package/dist/preview/rendering/loadImage.d.ts +8 -0
  98. package/dist/render/EFRenderAPI.js.map +1 -1
  99. package/dist/transcoding/types/index.d.ts +6 -94
  100. package/dist/transcoding/utils/UrlGenerator.d.ts +3 -12
  101. package/dist/transcoding/utils/UrlGenerator.js +3 -29
  102. package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
  103. package/package.json +26 -2
  104. package/test/setup.ts +1 -1
  105. package/test/useAssetMSW.ts +0 -100
  106. package/tsdown.config.ts +6 -1
  107. package/dist/elements/EFMedia/AssetMediaEngine.js +0 -284
  108. package/dist/elements/EFMedia/AssetMediaEngine.js.map +0 -1
  109. package/dist/elements/EFMedia/BaseMediaEngine.js +0 -200
  110. package/dist/elements/EFMedia/BaseMediaEngine.js.map +0 -1
  111. package/dist/elements/EFMedia/FileMediaEngine.js +0 -122
  112. package/dist/elements/EFMedia/FileMediaEngine.js.map +0 -1
  113. package/dist/elements/EFMedia/JitMediaEngine.js +0 -157
  114. package/dist/elements/EFMedia/JitMediaEngine.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"EFRenderAPI.js","names":["api: IEFRenderAPI"],"sources":["../../src/render/EFRenderAPI.ts"],"sourcesContent":["/**\n * Window API for programmatic video rendering.\n *\n * Exposes renderTimegroupToVideo for use from Playwright/CLI.\n * Supports streaming output and custom data injection.\n */\n\nimport type { EFTimegroup } from \"../elements/EFTimegroup.js\";\nimport type { EFWorkbench } from \"../gui/EFWorkbench.js\";\nimport { getRenderInfo, type RenderInfo } from \"../getRenderInfo.js\";\n// Import only types - actual function loaded dynamically\nimport type {\n RenderToVideoOptions,\n RenderProgress,\n} from \"../preview/renderTimegroupToVideo.types.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface IEFRenderAPI {\n /**\n * Render with streaming output (calls window.onRenderChunk for each chunk).\n * Use this for CLI/Playwright to avoid memory buffering.\n */\n renderStreaming(options?: RenderToVideoOptions): Promise<void>;\n\n /**\n * Render and return buffer (for shorter videos or in-browser use).\n * Returns the video as Uint8Array.\n */\n render(options?: RenderToVideoOptions): Promise<Uint8Array>;\n\n /**\n * Get render info (dimensions, duration, assets).\n * Same as the exported getRenderInfo function.\n */\n getRenderInfo(): Promise<RenderInfo>;\n\n /**\n * Check if SDK is ready for rendering.\n * Returns true if a root timegroup is found.\n */\n isReady(): boolean;\n}\n\ndeclare global {\n interface Window {\n EF_RENDER?: IEFRenderAPI;\n EF_RENDER_DATA?: Record<string, unknown>;\n onRenderChunk?: (chunk: Uint8Array) => void; // Set by Playwright\n onRenderProgress?: (progress: RenderProgress) => void; // Optional progress callback\n }\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\nfunction findRootTimegroup(): EFTimegroup | null {\n // Try to find timegroup from workbench first\n const workbench = document.querySelector(\n \"ef-workbench\",\n ) as EFWorkbench | null;\n if (workbench) {\n const timegroup = workbench.querySelector(\n \"ef-timegroup\",\n ) as EFTimegroup | null;\n if (timegroup) {\n return timegroup;\n }\n }\n\n // Fallback: find first root timegroup\n const rootTimegroup = document.querySelector(\n \"ef-timegroup\",\n ) as EFTimegroup | null;\n return rootTimegroup;\n}\n\nfunction setWorkbenchRendering(rendering: boolean): void {\n const workbench = document.querySelector(\n \"ef-workbench\",\n ) as EFWorkbench | null;\n if (workbench) {\n workbench.rendering = rendering;\n }\n}\n\nasync function waitForTimegroupDimensions(\n timegroup: EFTimegroup,\n): Promise<void> {\n await Promise.all(\n Array.from(document.styleSheets).map((sheet) => {\n if (sheet.href) {\n const link = Array.from(\n document.querySelectorAll('link[rel=\"stylesheet\"]'),\n ).find((l) => (l as HTMLLinkElement).href === sheet.href);\n if (link && !(link as HTMLLinkElement).sheet) {\n return new Promise((resolve) => {\n link.addEventListener(\"load\", resolve);\n link.addEventListener(\"error\", resolve);\n });\n }\n }\n return Promise.resolve();\n }),\n );\n\n // Force layout immediately after stylesheets load\n void timegroup.offsetHeight;\n\n const rect = timegroup.getBoundingClientRect();\n const hasOffset = timegroup.offsetWidth > 0 && timegroup.offsetHeight > 0;\n const hasRect = rect.width > 0 && rect.height > 0;\n const computedWidth = getComputedStyle(timegroup).width;\n const computedHeight = getComputedStyle(timegroup).height;\n const hasComputed =\n parseFloat(computedWidth) > 0 && parseFloat(computedHeight) > 0;\n\n if (!hasOffset && !hasRect && !hasComputed) {\n throw new Error(\n `Timegroup has no dimensions (${timegroup.offsetWidth}x${timegroup.offsetHeight}). ` +\n `Computed styles: width=${computedWidth}, height=${computedHeight}. ` +\n `Classes: \"${timegroup.className}\". ` +\n `\\n\\nTailwind CSS did not generate styles for these classes. ` +\n `Check that:\\n` +\n `1. Your Tailwind config 'content' array includes the HTML file\\n` +\n `2. Tailwind CSS is properly configured in your project\\n` +\n `3. The dev server successfully compiled CSS (check for Tailwind warnings above)`,\n );\n }\n\n}\n\nconst api: IEFRenderAPI = {\n async renderStreaming(options: RenderToVideoOptions = {}): Promise<void> {\n const timegroup = findRootTimegroup();\n if (!timegroup) {\n throw new Error(\"No ef-timegroup found. Cannot render.\");\n }\n\n // Check if window.onRenderChunk is available\n if (typeof window === \"undefined\" || !window.onRenderChunk) {\n throw new Error(\n \"window.onRenderChunk is not set. \" +\n \"Call page.exposeFunction('onRenderChunk', callback) from Playwright first.\",\n );\n }\n\n // Hide workbench UI during render\n setWorkbenchRendering(true);\n\n try {\n // Wait for timegroup to have dimensions\n await waitForTimegroupDimensions(timegroup);\n\n // Wait for media to be ready\n await timegroup.waitForMediaDurations();\n\n // Create custom writable stream that calls window.onRenderChunk\n const chunkWriter = new WritableStream<Uint8Array>({\n write(chunk: Uint8Array) {\n if (window.onRenderChunk) {\n window.onRenderChunk(chunk);\n }\n },\n });\n\n // Merge progress callback if window.onRenderProgress is set\n const onProgress = options.onProgress || window.onRenderProgress;\n\n // Render with custom stream\n // Dynamic import to avoid loading render utilities during module initialization\n const { renderTimegroupToVideo } =\n await import(\"../preview/renderTimegroupToVideo.js\");\n await renderTimegroupToVideo(timegroup, {\n ...options,\n customWritableStream: chunkWriter,\n onProgress,\n returnBuffer: false,\n });\n } finally {\n // Restore workbench UI\n setWorkbenchRendering(false);\n }\n },\n\n async render(options: RenderToVideoOptions = {}): Promise<Uint8Array> {\n const timegroup = findRootTimegroup();\n if (!timegroup) {\n throw new Error(\"No ef-timegroup found. Cannot render.\");\n }\n\n // Hide workbench UI during render\n setWorkbenchRendering(true);\n\n try {\n // Wait for timegroup to have dimensions\n await waitForTimegroupDimensions(timegroup);\n\n // Wait for media to be ready\n await timegroup.waitForMediaDurations();\n\n // Merge progress callback if window.onRenderProgress is set\n const onProgress = options.onProgress || window.onRenderProgress;\n\n // Dynamic import to avoid loading render utilities during module initialization\n const { renderTimegroupToVideo } =\n await import(\"../preview/renderTimegroupToVideo.js\");\n const buffer = await renderTimegroupToVideo(timegroup, {\n ...options,\n returnBuffer: true,\n onProgress,\n });\n\n if (!buffer) {\n throw new Error(\"Render failed: no buffer returned\");\n }\n\n return buffer;\n } finally {\n // Restore workbench UI\n setWorkbenchRendering(false);\n }\n },\n\n async getRenderInfo(): Promise<RenderInfo> {\n return getRenderInfo();\n },\n\n isReady(): boolean {\n return findRootTimegroup() !== null;\n },\n};\n\n// Export and register on window\nif (typeof window !== \"undefined\") {\n window.EF_RENDER = api;\n}\n\nexport { api as EFRenderAPI };\nexport type { IEFRenderAPI as EFRenderAPIInterface };\n"],"mappings":";;;AA2DA,SAAS,oBAAwC;CAE/C,MAAM,YAAY,SAAS,cACzB,eACD;AACD,KAAI,WAAW;EACb,MAAM,YAAY,UAAU,cAC1B,eACD;AACD,MAAI,UACF,QAAO;;AAQX,QAHsB,SAAS,cAC7B,eACD;;AAIH,SAAS,sBAAsB,WAA0B;CACvD,MAAM,YAAY,SAAS,cACzB,eACD;AACD,KAAI,UACF,WAAU,YAAY;;AAI1B,eAAe,2BACb,WACe;AACf,OAAM,QAAQ,IACZ,MAAM,KAAK,SAAS,YAAY,CAAC,KAAK,UAAU;AAC9C,MAAI,MAAM,MAAM;GACd,MAAM,OAAO,MAAM,KACjB,SAAS,iBAAiB,2BAAyB,CACpD,CAAC,MAAM,MAAO,EAAsB,SAAS,MAAM,KAAK;AACzD,OAAI,QAAQ,CAAE,KAAyB,MACrC,QAAO,IAAI,SAAS,YAAY;AAC9B,SAAK,iBAAiB,QAAQ,QAAQ;AACtC,SAAK,iBAAiB,SAAS,QAAQ;KACvC;;AAGN,SAAO,QAAQ,SAAS;GACxB,CACH;AAGD,CAAK,UAAU;CAEf,MAAM,OAAO,UAAU,uBAAuB;CAC9C,MAAM,YAAY,UAAU,cAAc,KAAK,UAAU,eAAe;CACxE,MAAM,UAAU,KAAK,QAAQ,KAAK,KAAK,SAAS;CAChD,MAAM,gBAAgB,iBAAiB,UAAU,CAAC;CAClD,MAAM,iBAAiB,iBAAiB,UAAU,CAAC;AAInD,KAAI,CAAC,aAAa,CAAC,WAAW,EAF5B,WAAW,cAAc,GAAG,KAAK,WAAW,eAAe,GAAG,GAG9D,OAAM,IAAI,MACR,gCAAgC,UAAU,YAAY,GAAG,UAAU,aAAa,4BACpD,cAAc,WAAW,eAAe,cACrD,UAAU,UAAU,qRAMpC;;AAKL,MAAMA,MAAoB;CACxB,MAAM,gBAAgB,UAAgC,EAAE,EAAiB;EACvE,MAAM,YAAY,mBAAmB;AACrC,MAAI,CAAC,UACH,OAAM,IAAI,MAAM,wCAAwC;AAI1D,MAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAC3C,OAAM,IAAI,MACR,8GAED;AAIH,wBAAsB,KAAK;AAE3B,MAAI;AAEF,SAAM,2BAA2B,UAAU;AAG3C,SAAM,UAAU,uBAAuB;GAGvC,MAAM,cAAc,IAAI,eAA2B,EACjD,MAAM,OAAmB;AACvB,QAAI,OAAO,cACT,QAAO,cAAc,MAAM;MAGhC,CAAC;GAGF,MAAM,aAAa,QAAQ,cAAc,OAAO;GAIhD,MAAM,EAAE,2BACN,MAAM,OAAO;AACf,SAAM,uBAAuB,WAAW;IACtC,GAAG;IACH,sBAAsB;IACtB;IACA,cAAc;IACf,CAAC;YACM;AAER,yBAAsB,MAAM;;;CAIhC,MAAM,OAAO,UAAgC,EAAE,EAAuB;EACpE,MAAM,YAAY,mBAAmB;AACrC,MAAI,CAAC,UACH,OAAM,IAAI,MAAM,wCAAwC;AAI1D,wBAAsB,KAAK;AAE3B,MAAI;AAEF,SAAM,2BAA2B,UAAU;AAG3C,SAAM,UAAU,uBAAuB;GAGvC,MAAM,aAAa,QAAQ,cAAc,OAAO;GAGhD,MAAM,EAAE,2BACN,MAAM,OAAO;GACf,MAAM,SAAS,MAAM,uBAAuB,WAAW;IACrD,GAAG;IACH,cAAc;IACd;IACD,CAAC;AAEF,OAAI,CAAC,OACH,OAAM,IAAI,MAAM,oCAAoC;AAGtD,UAAO;YACC;AAER,yBAAsB,MAAM;;;CAIhC,MAAM,gBAAqC;AACzC,SAAO,eAAe;;CAGxB,UAAmB;AACjB,SAAO,mBAAmB,KAAK;;CAElC;AAGD,IAAI,OAAO,WAAW,YACpB,QAAO,YAAY"}
1
+ {"version":3,"file":"EFRenderAPI.js","names":["api: IEFRenderAPI"],"sources":["../../src/render/EFRenderAPI.ts"],"sourcesContent":["/**\n * Window API for programmatic video rendering.\n *\n * Exposes renderTimegroupToVideo for use from Playwright/CLI.\n * Supports streaming output and custom data injection.\n */\n\nimport type { EFTimegroup } from \"../elements/EFTimegroup.js\";\nimport type { EFWorkbench } from \"../gui/EFWorkbench.js\";\nimport { getRenderInfo, type RenderInfo } from \"../getRenderInfo.js\";\n// Import only types - actual function loaded dynamically\nimport type {\n RenderToVideoOptions,\n RenderProgress,\n} from \"../preview/renderTimegroupToVideo.types.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface IEFRenderAPI {\n /**\n * Render with streaming output (calls window.onRenderChunk for each chunk).\n * Use this for CLI/Playwright to avoid memory buffering.\n */\n renderStreaming(options?: RenderToVideoOptions): Promise<void>;\n\n /**\n * Render and return buffer (for shorter videos or in-browser use).\n * Returns the video as Uint8Array.\n */\n render(options?: RenderToVideoOptions): Promise<Uint8Array>;\n\n /**\n * Get render info (dimensions, duration, assets).\n * Same as the exported getRenderInfo function.\n */\n getRenderInfo(): Promise<RenderInfo>;\n\n /**\n * Check if SDK is ready for rendering.\n * Returns true if a root timegroup is found.\n */\n isReady(): boolean;\n}\n\ndeclare global {\n interface Window {\n EF_RENDER?: IEFRenderAPI;\n EF_RENDER_DATA?: Record<string, unknown>;\n onRenderChunk?: (chunk: Uint8Array) => void; // Set by Playwright\n onRenderProgress?: (progress: RenderProgress) => void; // Optional progress callback\n }\n}\n\n// ============================================================================\n// Implementation\n// ============================================================================\n\nfunction findRootTimegroup(): EFTimegroup | null {\n // Try to find timegroup from workbench first\n const workbench = document.querySelector(\n \"ef-workbench\",\n ) as EFWorkbench | null;\n if (workbench) {\n const timegroup = workbench.querySelector(\n \"ef-timegroup\",\n ) as EFTimegroup | null;\n if (timegroup) {\n return timegroup;\n }\n }\n\n // Fallback: find first root timegroup\n const rootTimegroup = document.querySelector(\n \"ef-timegroup\",\n ) as EFTimegroup | null;\n return rootTimegroup;\n}\n\nfunction setWorkbenchRendering(rendering: boolean): void {\n const workbench = document.querySelector(\n \"ef-workbench\",\n ) as EFWorkbench | null;\n if (workbench) {\n workbench.rendering = rendering;\n }\n}\n\nasync function waitForTimegroupDimensions(\n timegroup: EFTimegroup,\n): Promise<void> {\n await Promise.all(\n Array.from(document.styleSheets).map((sheet) => {\n if (sheet.href) {\n const link = Array.from(\n document.querySelectorAll('link[rel=\"stylesheet\"]'),\n ).find((l) => (l as HTMLLinkElement).href === sheet.href);\n if (link && !(link as HTMLLinkElement).sheet) {\n return new Promise((resolve) => {\n link.addEventListener(\"load\", resolve);\n link.addEventListener(\"error\", resolve);\n });\n }\n }\n return Promise.resolve();\n }),\n );\n\n // Force layout immediately after stylesheets load\n void timegroup.offsetHeight;\n\n const rect = timegroup.getBoundingClientRect();\n const hasOffset = timegroup.offsetWidth > 0 && timegroup.offsetHeight > 0;\n const hasRect = rect.width > 0 && rect.height > 0;\n const computedWidth = getComputedStyle(timegroup).width;\n const computedHeight = getComputedStyle(timegroup).height;\n const hasComputed =\n parseFloat(computedWidth) > 0 && parseFloat(computedHeight) > 0;\n\n if (!hasOffset && !hasRect && !hasComputed) {\n throw new Error(\n `Timegroup has no dimensions (${timegroup.offsetWidth}x${timegroup.offsetHeight}). ` +\n `Computed styles: width=${computedWidth}, height=${computedHeight}. ` +\n `Classes: \"${timegroup.className}\". ` +\n `\\n\\nTailwind CSS did not generate styles for these classes. ` +\n `Check that:\\n` +\n `1. Your Tailwind config 'content' array includes the HTML file\\n` +\n `2. Tailwind CSS is properly configured in your project\\n` +\n `3. The dev server successfully compiled CSS (check for Tailwind warnings above)`,\n );\n }\n}\n\nconst api: IEFRenderAPI = {\n async renderStreaming(options: RenderToVideoOptions = {}): Promise<void> {\n const timegroup = findRootTimegroup();\n if (!timegroup) {\n throw new Error(\"No ef-timegroup found. Cannot render.\");\n }\n\n // Check if window.onRenderChunk is available\n if (typeof window === \"undefined\" || !window.onRenderChunk) {\n throw new Error(\n \"window.onRenderChunk is not set. \" +\n \"Call page.exposeFunction('onRenderChunk', callback) from Playwright first.\",\n );\n }\n\n // Hide workbench UI during render\n setWorkbenchRendering(true);\n\n try {\n // Wait for timegroup to have dimensions\n await waitForTimegroupDimensions(timegroup);\n\n // Wait for media to be ready\n await timegroup.waitForMediaDurations();\n\n // Create custom writable stream that calls window.onRenderChunk\n const chunkWriter = new WritableStream<Uint8Array>({\n write(chunk: Uint8Array) {\n if (window.onRenderChunk) {\n window.onRenderChunk(chunk);\n }\n },\n });\n\n // Merge progress callback if window.onRenderProgress is set\n const onProgress = options.onProgress || window.onRenderProgress;\n\n // Render with custom stream\n // Dynamic import to avoid loading render utilities during module initialization\n const { renderTimegroupToVideo } =\n await import(\"../preview/renderTimegroupToVideo.js\");\n await renderTimegroupToVideo(timegroup, {\n ...options,\n customWritableStream: chunkWriter,\n onProgress,\n returnBuffer: false,\n });\n } finally {\n // Restore workbench UI\n setWorkbenchRendering(false);\n }\n },\n\n async render(options: RenderToVideoOptions = {}): Promise<Uint8Array> {\n const timegroup = findRootTimegroup();\n if (!timegroup) {\n throw new Error(\"No ef-timegroup found. Cannot render.\");\n }\n\n // Hide workbench UI during render\n setWorkbenchRendering(true);\n\n try {\n // Wait for timegroup to have dimensions\n await waitForTimegroupDimensions(timegroup);\n\n // Wait for media to be ready\n await timegroup.waitForMediaDurations();\n\n // Merge progress callback if window.onRenderProgress is set\n const onProgress = options.onProgress || window.onRenderProgress;\n\n // Dynamic import to avoid loading render utilities during module initialization\n const { renderTimegroupToVideo } =\n await import(\"../preview/renderTimegroupToVideo.js\");\n const buffer = await renderTimegroupToVideo(timegroup, {\n ...options,\n returnBuffer: true,\n onProgress,\n });\n\n if (!buffer) {\n throw new Error(\"Render failed: no buffer returned\");\n }\n\n return buffer;\n } finally {\n // Restore workbench UI\n setWorkbenchRendering(false);\n }\n },\n\n async getRenderInfo(): Promise<RenderInfo> {\n return getRenderInfo();\n },\n\n isReady(): boolean {\n return findRootTimegroup() !== null;\n },\n};\n\n// Export and register on window\nif (typeof window !== \"undefined\") {\n window.EF_RENDER = api;\n}\n\nexport { api as EFRenderAPI };\nexport type { IEFRenderAPI as EFRenderAPIInterface };\n"],"mappings":";;;AA2DA,SAAS,oBAAwC;CAE/C,MAAM,YAAY,SAAS,cACzB,eACD;AACD,KAAI,WAAW;EACb,MAAM,YAAY,UAAU,cAC1B,eACD;AACD,MAAI,UACF,QAAO;;AAQX,QAHsB,SAAS,cAC7B,eACD;;AAIH,SAAS,sBAAsB,WAA0B;CACvD,MAAM,YAAY,SAAS,cACzB,eACD;AACD,KAAI,UACF,WAAU,YAAY;;AAI1B,eAAe,2BACb,WACe;AACf,OAAM,QAAQ,IACZ,MAAM,KAAK,SAAS,YAAY,CAAC,KAAK,UAAU;AAC9C,MAAI,MAAM,MAAM;GACd,MAAM,OAAO,MAAM,KACjB,SAAS,iBAAiB,2BAAyB,CACpD,CAAC,MAAM,MAAO,EAAsB,SAAS,MAAM,KAAK;AACzD,OAAI,QAAQ,CAAE,KAAyB,MACrC,QAAO,IAAI,SAAS,YAAY;AAC9B,SAAK,iBAAiB,QAAQ,QAAQ;AACtC,SAAK,iBAAiB,SAAS,QAAQ;KACvC;;AAGN,SAAO,QAAQ,SAAS;GACxB,CACH;AAGD,CAAK,UAAU;CAEf,MAAM,OAAO,UAAU,uBAAuB;CAC9C,MAAM,YAAY,UAAU,cAAc,KAAK,UAAU,eAAe;CACxE,MAAM,UAAU,KAAK,QAAQ,KAAK,KAAK,SAAS;CAChD,MAAM,gBAAgB,iBAAiB,UAAU,CAAC;CAClD,MAAM,iBAAiB,iBAAiB,UAAU,CAAC;AAInD,KAAI,CAAC,aAAa,CAAC,WAAW,EAF5B,WAAW,cAAc,GAAG,KAAK,WAAW,eAAe,GAAG,GAG9D,OAAM,IAAI,MACR,gCAAgC,UAAU,YAAY,GAAG,UAAU,aAAa,4BACpD,cAAc,WAAW,eAAe,cACrD,UAAU,UAAU,qRAMpC;;AAIL,MAAMA,MAAoB;CACxB,MAAM,gBAAgB,UAAgC,EAAE,EAAiB;EACvE,MAAM,YAAY,mBAAmB;AACrC,MAAI,CAAC,UACH,OAAM,IAAI,MAAM,wCAAwC;AAI1D,MAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAC3C,OAAM,IAAI,MACR,8GAED;AAIH,wBAAsB,KAAK;AAE3B,MAAI;AAEF,SAAM,2BAA2B,UAAU;AAG3C,SAAM,UAAU,uBAAuB;GAGvC,MAAM,cAAc,IAAI,eAA2B,EACjD,MAAM,OAAmB;AACvB,QAAI,OAAO,cACT,QAAO,cAAc,MAAM;MAGhC,CAAC;GAGF,MAAM,aAAa,QAAQ,cAAc,OAAO;GAIhD,MAAM,EAAE,2BACN,MAAM,OAAO;AACf,SAAM,uBAAuB,WAAW;IACtC,GAAG;IACH,sBAAsB;IACtB;IACA,cAAc;IACf,CAAC;YACM;AAER,yBAAsB,MAAM;;;CAIhC,MAAM,OAAO,UAAgC,EAAE,EAAuB;EACpE,MAAM,YAAY,mBAAmB;AACrC,MAAI,CAAC,UACH,OAAM,IAAI,MAAM,wCAAwC;AAI1D,wBAAsB,KAAK;AAE3B,MAAI;AAEF,SAAM,2BAA2B,UAAU;AAG3C,SAAM,UAAU,uBAAuB;GAGvC,MAAM,aAAa,QAAQ,cAAc,OAAO;GAGhD,MAAM,EAAE,2BACN,MAAM,OAAO;GACf,MAAM,SAAS,MAAM,uBAAuB,WAAW;IACrD,GAAG;IACH,cAAc;IACd;IACD,CAAC;AAEF,OAAI,CAAC,OACH,OAAM,IAAI,MAAM,oCAAoC;AAGtD,UAAO;YACC;AAER,yBAAsB,MAAM;;;CAIhC,MAAM,gBAAqC;AACzC,SAAO,eAAe;;CAGxB,UAAmB;AACjB,SAAO,mBAAmB,KAAK;;CAElC;AAGD,IAAI,OAAO,WAAW,YACpB,QAAO,YAAY"}
@@ -1,93 +1,10 @@
1
+ import { SegmentIndex, SegmentTimeRange, TrackRef, TrackRole, TrackSet } from "../../elements/EFMedia/SegmentIndex.js";
2
+ import { SegmentTransport } from "../../elements/EFMedia/SegmentTransport.js";
3
+ import { TimingModel } from "../../elements/EFMedia/TimingModel.js";
4
+ import { MediaEngine } from "../../elements/EFMedia/MediaEngine.js";
5
+
1
6
  //#region src/transcoding/types/index.d.ts
2
7
 
3
- type RenditionId = "high" | "medium" | "low" | "audio" | "scrub";
4
- interface AudioRendition {
5
- id?: RenditionId;
6
- trackId: number | undefined;
7
- src: string;
8
- segmentDurationMs?: number;
9
- segmentDurationsMs?: number[];
10
- startTimeOffsetMs?: number;
11
- }
12
- interface VideoRendition {
13
- id?: RenditionId;
14
- trackId: number | undefined;
15
- src: string;
16
- segmentDurationMs?: number;
17
- segmentDurationsMs?: number[];
18
- startTimeOffsetMs?: number;
19
- }
20
- /**
21
- * Union type representing either an audio or video rendition.
22
- * Used in methods that can work with either type of media rendition.
23
- */
24
- type MediaRendition = AudioRendition | VideoRendition;
25
- interface MediaEngine {
26
- durationMs: number;
27
- src: string;
28
- audioRendition?: AudioRendition;
29
- videoRendition?: VideoRendition;
30
- templates: {
31
- initSegment: string;
32
- mediaSegment: string;
33
- };
34
- fetchInitSegment: (rendition: {
35
- id?: RenditionId;
36
- trackId: number | undefined;
37
- src: string;
38
- }, signal: AbortSignal) => Promise<ArrayBuffer>;
39
- fetchMediaSegment: (segmentId: number, rendition: {
40
- id?: RenditionId;
41
- trackId: number | undefined;
42
- src: string;
43
- }, signal: AbortSignal) => Promise<ArrayBuffer>;
44
- computeSegmentId: (desiredSeekTimeMs: number, rendition: MediaRendition) => number | undefined;
45
- /**
46
- * Get the video rendition if available, otherwise return undefined.
47
- * Callers should handle undefined appropriately.
48
- */
49
- getVideoRendition: () => VideoRendition | undefined;
50
- /**
51
- * Get the audio rendition if available, otherwise return undefined.
52
- * Callers should handle undefined appropriately.
53
- */
54
- getAudioRendition: () => AudioRendition | undefined;
55
- /**
56
- * Check if a segment is cached for a given rendition
57
- */
58
- isSegmentCached: (segmentId: number, rendition: AudioRendition | VideoRendition) => boolean;
59
- /**
60
- * Get scrub video rendition if available, otherwise return undefined
61
- * Each engine implements this based on their capabilities:
62
- * - JitMediaEngine: looks for "scrub" rendition in manifest
63
- * - AssetMediaEngine: returns regular video rendition (no separate scrub)
64
- */
65
- getScrubVideoRendition(): VideoRendition | undefined;
66
- /**
67
- * Calculate audio segments needed for a time range
68
- * Each media engine implements this based on their segment structure
69
- */
70
- calculateAudioSegmentRange: (fromMs: number, toMs: number, rendition: AudioRendition, durationMs: number) => SegmentTimeRange[];
71
- /**
72
- * Get buffer configuration for this media engine
73
- * Returns preferred buffer settings that may override host defaults
74
- */
75
- getBufferConfig: () => {
76
- videoBufferDurationMs: number;
77
- audioBufferDurationMs: number;
78
- maxVideoBufferFetches: number;
79
- maxAudioBufferFetches: number;
80
- bufferThresholdMs: number;
81
- };
82
- /**
83
- * Extract thumbnail canvases at multiple timestamps efficiently
84
- * Uses scrub rendition and batches by segment for optimal performance
85
- * Returns thumbnail objects in same order as input timestamps
86
- * Returns null for any timestamps that fail to extract
87
- * @param signal - Optional AbortSignal to cancel in-flight requests when element is disconnected
88
- */
89
- extractThumbnails(timestamps: number[], signal?: AbortSignal): Promise<(ThumbnailResult | null)[]>;
90
- }
91
8
  interface ThumbnailResult {
92
9
  timestamp: number;
93
10
  thumbnail: HTMLCanvasElement | OffscreenCanvas;
@@ -97,11 +14,6 @@ interface AudioSpan {
97
14
  endMs: number;
98
15
  blob: Blob;
99
16
  }
100
- interface SegmentTimeRange {
101
- segmentId: number;
102
- startMs: number;
103
- endMs: number;
104
- }
105
17
  //#endregion
106
- export { AudioSpan, MediaEngine, RenditionId };
18
+ export { AudioSpan, ThumbnailResult };
107
19
  //# sourceMappingURL=index.d.ts.map
@@ -1,7 +1,7 @@
1
- import { MediaEngine, RenditionId } from "../types/index.js";
2
-
3
1
  //#region src/transcoding/utils/UrlGenerator.d.ts
4
-
2
+ /**
3
+ * URL generation utilities for transcoding endpoints
4
+ */
5
5
  declare class UrlGenerator {
6
6
  private baseUrl;
7
7
  constructor(baseUrl: () => string);
@@ -9,10 +9,6 @@ declare class UrlGenerator {
9
9
  * Get the base URL for constructing absolute URLs
10
10
  */
11
11
  getBaseUrl(): string;
12
- /**
13
- * Generate video segment URL
14
- */
15
- generateSegmentUrl(segmentId: "init" | number, renditionId: RenditionId, metadata: MediaEngine): string;
16
12
  /**
17
13
  * Generate init segment URL
18
14
  */
@@ -21,11 +17,6 @@ declare class UrlGenerator {
21
17
  * Generate manifest URL
22
18
  */
23
19
  generateManifestUrl(mediaUrl: string): string;
24
- /**
25
- * Generate track fragment index URL using production API format
26
- * @deprecated Use MD5-based URL generation in AssetMediaEngine.fetch() instead
27
- */
28
- generateTrackFragmentIndexUrl(mediaUrl: string): string;
29
20
  /**
30
21
  * Generate quality presets URL
31
22
  */
@@ -1,4 +1,7 @@
1
1
  //#region src/transcoding/utils/UrlGenerator.ts
2
+ /**
3
+ * URL generation utilities for transcoding endpoints
4
+ */
2
5
  var UrlGenerator = class {
3
6
  constructor(baseUrl) {
4
7
  this.baseUrl = baseUrl;
@@ -10,26 +13,6 @@ var UrlGenerator = class {
10
13
  return this.baseUrl();
11
14
  }
12
15
  /**
13
- * Generate video segment URL
14
- */
15
- generateSegmentUrl(segmentId, renditionId, metadata) {
16
- const audioRendition = metadata.audioRendition;
17
- const videoRendition = metadata.videoRendition;
18
- let rendition;
19
- if (renditionId === "audio") rendition = audioRendition;
20
- else rendition = videoRendition;
21
- if (!rendition) {
22
- console.error("Rendition not found", {
23
- renditionId,
24
- hasAudio: !!audioRendition,
25
- hasVideo: !!videoRendition,
26
- metadata
27
- });
28
- throw new Error(`Rendition ${renditionId} not found (hasAudio=${!!audioRendition}, hasVideo=${!!videoRendition})`);
29
- }
30
- return (segmentId === "init" ? metadata.templates.initSegment : metadata.templates.mediaSegment).replace("{rendition}", renditionId).replace("{segmentId}", segmentId.toString()).replace("{src}", metadata.src).replace("{trackId}", rendition.trackId?.toString() ?? "");
31
- }
32
- /**
33
16
  * Generate init segment URL
34
17
  */
35
18
  generateInitSegmentUrl(mediaUrl, rendition) {
@@ -42,15 +25,6 @@ var UrlGenerator = class {
42
25
  return `${this.baseUrl()}/api/v1/transcode/manifest.json?url=${encodeURIComponent(mediaUrl)}`;
43
26
  }
44
27
  /**
45
- * Generate track fragment index URL using production API format
46
- * @deprecated Use MD5-based URL generation in AssetMediaEngine.fetch() instead
47
- */
48
- generateTrackFragmentIndexUrl(mediaUrl) {
49
- let normalizedSrc = mediaUrl.startsWith("/") ? mediaUrl.slice(1) : mediaUrl;
50
- normalizedSrc = normalizedSrc.replace(/^\/+/, "");
51
- return `@ef-track-fragment-index/${normalizedSrc}`;
52
- }
53
- /**
54
28
  * Generate quality presets URL
55
29
  */
56
30
  generatePresetsUrl() {
@@ -1 +1 @@
1
- {"version":3,"file":"UrlGenerator.js","names":["baseUrl: () => string"],"sources":["../../../src/transcoding/utils/UrlGenerator.ts"],"sourcesContent":["/**\n * URL generation utilities for transcoding endpoints\n */\n\nimport type { RenditionId } from \"../types/index.js\";\nimport type { MediaEngine } from \"../types/index.ts\";\n\nexport class UrlGenerator {\n constructor(private baseUrl: () => string) {}\n\n /**\n * Get the base URL for constructing absolute URLs\n */\n getBaseUrl(): string {\n return this.baseUrl();\n }\n\n /**\n * Generate video segment URL\n */\n generateSegmentUrl(\n segmentId: \"init\" | number,\n renditionId: RenditionId,\n metadata: MediaEngine,\n ): string {\n // Determine which rendition to use based on renditionId\n // Audio renditions: \"audio\"\n // Video renditions: \"high\", \"scrub\", \"low\", \"medium\", etc.\n const audioRendition = metadata.audioRendition;\n const videoRendition = metadata.videoRendition;\n\n let rendition;\n if (renditionId === \"audio\") {\n rendition = audioRendition;\n } else {\n // For all other rendition IDs (high, scrub, low, medium), use video rendition\n rendition = videoRendition;\n }\n\n if (!rendition) {\n console.error(\"Rendition not found\", {\n renditionId,\n hasAudio: !!audioRendition,\n hasVideo: !!videoRendition,\n metadata,\n });\n throw new Error(\n `Rendition ${renditionId} not found (hasAudio=${!!audioRendition}, hasVideo=${!!videoRendition})`,\n );\n }\n\n const template =\n segmentId === \"init\"\n ? metadata.templates.initSegment\n : metadata.templates.mediaSegment;\n return template\n .replace(\"{rendition}\", renditionId)\n .replace(\"{segmentId}\", segmentId.toString())\n .replace(\"{src}\", metadata.src)\n .replace(\"{trackId}\", rendition.trackId?.toString() ?? \"\");\n }\n\n /**\n * Generate init segment URL\n */\n generateInitSegmentUrl(mediaUrl: string, rendition: string): string {\n return `${this.baseUrl()}/api/v1/transcode/${rendition}/init.mp4?url=${encodeURIComponent(mediaUrl)}`;\n }\n\n /**\n * Generate manifest URL\n */\n generateManifestUrl(mediaUrl: string): string {\n return `${this.baseUrl()}/api/v1/transcode/manifest.json?url=${encodeURIComponent(mediaUrl)}`;\n }\n\n /**\n * Generate track fragment index URL using production API format\n * @deprecated Use MD5-based URL generation in AssetMediaEngine.fetch() instead\n */\n generateTrackFragmentIndexUrl(mediaUrl: string): string {\n // Normalize the path: remove leading slash and any double slashes\n let normalizedSrc = mediaUrl.startsWith(\"/\") ? mediaUrl.slice(1) : mediaUrl;\n // Remove any remaining leading slashes (handles cases like \"//assets/video.mp4\")\n normalizedSrc = normalizedSrc.replace(/^\\/+/, \"\");\n // Legacy format - kept for backward compatibility but should not be used\n return `@ef-track-fragment-index/${normalizedSrc}`;\n }\n\n /**\n * Generate quality presets URL\n */\n generatePresetsUrl(): string {\n return `${this.baseUrl()}/api/v1/transcode/presets`;\n }\n}\n"],"mappings":";AAOA,IAAa,eAAb,MAA0B;CACxB,YAAY,AAAQA,SAAuB;EAAvB;;;;;CAKpB,aAAqB;AACnB,SAAO,KAAK,SAAS;;;;;CAMvB,mBACE,WACA,aACA,UACQ;EAIR,MAAM,iBAAiB,SAAS;EAChC,MAAM,iBAAiB,SAAS;EAEhC,IAAI;AACJ,MAAI,gBAAgB,QAClB,aAAY;MAGZ,aAAY;AAGd,MAAI,CAAC,WAAW;AACd,WAAQ,MAAM,uBAAuB;IACnC;IACA,UAAU,CAAC,CAAC;IACZ,UAAU,CAAC,CAAC;IACZ;IACD,CAAC;AACF,SAAM,IAAI,MACR,aAAa,YAAY,uBAAuB,CAAC,CAAC,eAAe,aAAa,CAAC,CAAC,eAAe,GAChG;;AAOH,UAHE,cAAc,SACV,SAAS,UAAU,cACnB,SAAS,UAAU,cAEtB,QAAQ,eAAe,YAAY,CACnC,QAAQ,eAAe,UAAU,UAAU,CAAC,CAC5C,QAAQ,SAAS,SAAS,IAAI,CAC9B,QAAQ,aAAa,UAAU,SAAS,UAAU,IAAI,GAAG;;;;;CAM9D,uBAAuB,UAAkB,WAA2B;AAClE,SAAO,GAAG,KAAK,SAAS,CAAC,oBAAoB,UAAU,gBAAgB,mBAAmB,SAAS;;;;;CAMrG,oBAAoB,UAA0B;AAC5C,SAAO,GAAG,KAAK,SAAS,CAAC,sCAAsC,mBAAmB,SAAS;;;;;;CAO7F,8BAA8B,UAA0B;EAEtD,IAAI,gBAAgB,SAAS,WAAW,IAAI,GAAG,SAAS,MAAM,EAAE,GAAG;AAEnE,kBAAgB,cAAc,QAAQ,QAAQ,GAAG;AAEjD,SAAO,4BAA4B;;;;;CAMrC,qBAA6B;AAC3B,SAAO,GAAG,KAAK,SAAS,CAAC"}
1
+ {"version":3,"file":"UrlGenerator.js","names":["baseUrl: () => string"],"sources":["../../../src/transcoding/utils/UrlGenerator.ts"],"sourcesContent":["/**\n * URL generation utilities for transcoding endpoints\n */\n\nexport class UrlGenerator {\n constructor(private baseUrl: () => string) {}\n\n /**\n * Get the base URL for constructing absolute URLs\n */\n getBaseUrl(): string {\n return this.baseUrl();\n }\n\n /**\n * Generate init segment URL\n */\n generateInitSegmentUrl(mediaUrl: string, rendition: string): string {\n return `${this.baseUrl()}/api/v1/transcode/${rendition}/init.mp4?url=${encodeURIComponent(mediaUrl)}`;\n }\n\n /**\n * Generate manifest URL\n */\n generateManifestUrl(mediaUrl: string): string {\n return `${this.baseUrl()}/api/v1/transcode/manifest.json?url=${encodeURIComponent(mediaUrl)}`;\n }\n\n /**\n * Generate quality presets URL\n */\n generatePresetsUrl(): string {\n return `${this.baseUrl()}/api/v1/transcode/presets`;\n }\n}\n"],"mappings":";;;;AAIA,IAAa,eAAb,MAA0B;CACxB,YAAY,AAAQA,SAAuB;EAAvB;;;;;CAKpB,aAAqB;AACnB,SAAO,KAAK,SAAS;;;;;CAMvB,uBAAuB,UAAkB,WAA2B;AAClE,SAAO,GAAG,KAAK,SAAS,CAAC,oBAAoB,UAAU,gBAAgB,mBAAmB,SAAS;;;;;CAMrG,oBAAoB,UAA0B;AAC5C,SAAO,GAAG,KAAK,SAAS,CAAC,sCAAsC,mBAAmB,SAAS;;;;;CAM7F,qBAA6B;AAC3B,SAAO,GAAG,KAAK,SAAS,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@editframe/elements",
3
- "version": "0.38.0",
3
+ "version": "0.39.0",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,7 +18,7 @@
18
18
  "license": "UNLICENSED",
19
19
  "dependencies": {
20
20
  "@bramus/style-observer": "^1.3.0",
21
- "@editframe/assets": "0.38.0",
21
+ "@editframe/assets": "0.39.0",
22
22
  "@lit/context": "^1.1.6",
23
23
  "@opentelemetry/api": "^1.9.0",
24
24
  "@opentelemetry/context-zone": "^1.26.0",
@@ -55,6 +55,18 @@
55
55
  "default": "./dist/node.js"
56
56
  }
57
57
  },
58
+ "./preview/renderTimegroupToCanvas": {
59
+ "import": {
60
+ "types": "./dist/preview/renderTimegroupToCanvas.d.ts",
61
+ "default": "./dist/preview/renderTimegroupToCanvas.js"
62
+ }
63
+ },
64
+ "./preview/renderTimegroupToVideo": {
65
+ "import": {
66
+ "types": "./dist/preview/renderTimegroupToVideo.d.ts",
67
+ "default": "./dist/preview/renderTimegroupToVideo.js"
68
+ }
69
+ },
58
70
  "./package.json": "./package.json",
59
71
  "./styles.css": "./dist/style.css",
60
72
  "./types.json": "./types.json"
@@ -79,6 +91,18 @@
79
91
  "default": "./dist/node.js"
80
92
  }
81
93
  },
94
+ "./preview/renderTimegroupToVideo": {
95
+ "import": {
96
+ "types": "./dist/preview/renderTimegroupToVideo.d.ts",
97
+ "default": "./dist/preview/renderTimegroupToVideo.js"
98
+ }
99
+ },
100
+ "./preview/renderTimegroupToCanvas": {
101
+ "import": {
102
+ "types": "./dist/preview/renderTimegroupToCanvas.d.ts",
103
+ "default": "./dist/preview/renderTimegroupToCanvas.js"
104
+ }
105
+ },
82
106
  "./package.json": "./package.json",
83
107
  "./styles.css": "./dist/style.css",
84
108
  "./theme.css": "./dist/gui/ef-theme.css",
package/test/setup.ts CHANGED
@@ -7,7 +7,7 @@ import { beforeEach, afterAll } from "vitest";
7
7
  import {
8
8
  globalRequestDeduplicator,
9
9
  mediaCache,
10
- } from "../src/elements/EFMedia/BaseMediaEngine.js";
10
+ } from "../src/elements/EFMedia/CachedFetcher.js";
11
11
  import { globalURLTokenDeduplicator } from "../src/transcoding/cache/URLTokenDeduplicator.js";
12
12
  import { TEST_SERVER_PORT } from "./constants.js";
13
13
 
@@ -1,106 +1,6 @@
1
- /**
2
- * Asset-specific MSW handlers for testing
3
- * Provides pre-configured handlers for asset fragment indexes and track data
4
- */
5
-
6
1
  import { HttpResponse, http } from "msw";
7
2
 
8
- /**
9
- * Asset MSW handlers that redirect requests to real test assets
10
- * Use with MSW worker.use() to proxy asset requests to /test-assets/asset-mode/
11
- */
12
3
  export const assetMSWHandlers = [
13
- // Fragment index handler - rewrite to test asset
14
- http.get("/@ef-track-fragment-index/*", async () => {
15
- const response = await fetch("/asset-mode/index.json");
16
- const data = await response.json();
17
- return HttpResponse.json(data);
18
- }),
19
-
20
- // Track data handler - rewrite to test asset with proper range support
21
- http.get("/@ef-track/*", async ({ request }) => {
22
- const url = new URL(request.url);
23
- const trackId = url.searchParams.get("trackId");
24
- if (!trackId) {
25
- return new HttpResponse(null, { status: 400 });
26
- }
27
-
28
- const rangeHeader = request.headers.get("range");
29
- const response = await fetch(`/asset-mode/track-${trackId}.mp4`, {
30
- headers: {
31
- ...(rangeHeader && {
32
- range: rangeHeader,
33
- }),
34
- },
35
- });
36
-
37
- const contentRangeHeader = response.headers.get("Content-Range");
38
- return new HttpResponse(await response.arrayBuffer(), {
39
- status: response.status,
40
- headers: {
41
- "Content-Type": "video/mp4",
42
- "Accept-Ranges": "bytes",
43
- ...(contentRangeHeader && {
44
- "Content-Range": contentRangeHeader,
45
- }),
46
- },
47
- });
48
- }),
49
-
50
- // Asset ID API handlers - these are needed when tests set assetId properties
51
- http.get("/api/v1/isobmff_files/:assetId/index", async () => {
52
- const mockIndex = {
53
- 0: {
54
- duration: 10000,
55
- timescale: 1000,
56
- fragments: [
57
- {
58
- offset: 0,
59
- size: 1024,
60
- timestamp: 0,
61
- duration: 10000,
62
- },
63
- ],
64
- },
65
- };
66
-
67
- return HttpResponse.json(mockIndex, {
68
- headers: {
69
- "Content-Type": "application/json",
70
- },
71
- });
72
- }),
73
-
74
- http.get("/api/v1/isobmff_tracks/:assetId/:trackId", async ({ request }) => {
75
- // Check if this is a range request
76
- const rangeHeader = request.headers.get("range");
77
-
78
- if (rangeHeader) {
79
- // Return a mock MP4 segment with proper range headers
80
- const mockData = new ArrayBuffer(1024); // 1KB mock data
81
- return new HttpResponse(mockData, {
82
- status: 206,
83
- headers: {
84
- "Content-Type": "video/mp4",
85
- "Accept-Ranges": "bytes",
86
- "Content-Range": rangeHeader,
87
- "Content-Length": "1024",
88
- },
89
- });
90
- }
91
-
92
- // Return the full mock track
93
- const mockData = new ArrayBuffer(1024);
94
- return new HttpResponse(mockData, {
95
- status: 200,
96
- headers: {
97
- "Content-Type": "video/mp4",
98
- "Accept-Ranges": "bytes",
99
- "Content-Length": "1024",
100
- },
101
- });
102
- }),
103
-
104
4
  http.get("/api/v1/files/:id/index", async () => {
105
5
  const mockIndex = {
106
6
  0: {
package/tsdown.config.ts CHANGED
@@ -48,7 +48,12 @@ const inlineCssPlugin = (): Plugin => ({
48
48
 
49
49
  export default defineConfig(
50
50
  createTsdownConfig({
51
- entry: ["src/index.ts", "src/node.ts"],
51
+ entry: [
52
+ "src/index.ts",
53
+ "src/node.ts",
54
+ "src/preview/renderTimegroupToVideo.ts",
55
+ "src/preview/renderTimegroupToCanvas.ts",
56
+ ],
52
57
  plugins: [inlineCssPlugin()],
53
58
  external: [/@editframe\/assets/],
54
59
  publint: false, // Disabled because CSS is built after tsdown