@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.
- package/dist/canvas/EFCanvas.d.ts +4 -4
- package/dist/canvas/EFCanvasItem.d.ts +4 -4
- package/dist/canvas/overlays/SelectionOverlay.d.ts +2 -2
- package/dist/canvas/overlays/SelectionOverlay.js.map +1 -1
- package/dist/elements/EFCaptions.js +1 -1
- package/dist/elements/EFCaptions.js.map +1 -1
- package/dist/elements/EFImage.js +3 -4
- package/dist/elements/EFImage.js.map +1 -1
- package/dist/elements/EFMedia/BufferedSeekingInput.js +1 -1
- package/dist/elements/EFMedia/CachedFetcher.js +99 -0
- package/dist/elements/EFMedia/CachedFetcher.js.map +1 -0
- package/dist/elements/EFMedia/MediaEngine.d.ts +19 -0
- package/dist/elements/EFMedia/MediaEngine.js +129 -0
- package/dist/elements/EFMedia/MediaEngine.js.map +1 -0
- package/dist/elements/EFMedia/SegmentIndex.d.ts +32 -0
- package/dist/elements/EFMedia/SegmentIndex.js +185 -0
- package/dist/elements/EFMedia/SegmentIndex.js.map +1 -0
- package/dist/elements/EFMedia/SegmentTransport.d.ts +12 -0
- package/dist/elements/EFMedia/SegmentTransport.js +69 -0
- package/dist/elements/EFMedia/SegmentTransport.js.map +1 -0
- package/dist/elements/EFMedia/TimingModel.d.ts +10 -0
- package/dist/elements/EFMedia/TimingModel.js +28 -0
- package/dist/elements/EFMedia/TimingModel.js.map +1 -0
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +7 -6
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +13 -34
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
- package/dist/elements/EFMedia.d.ts +2 -1
- package/dist/elements/EFMedia.js +14 -31
- package/dist/elements/EFMedia.js.map +1 -1
- package/dist/elements/EFPanZoom.d.ts +4 -4
- package/dist/elements/EFSourceMixin.js +1 -1
- package/dist/elements/EFSourceMixin.js.map +1 -1
- package/dist/elements/EFSurface.d.ts +4 -4
- package/dist/elements/EFTemporal.js +2 -1
- package/dist/elements/EFTemporal.js.map +1 -1
- package/dist/elements/EFTimegroup.js +2 -1
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/dist/elements/EFVideo.js +204 -187
- package/dist/elements/EFVideo.js.map +1 -1
- package/dist/gui/EFActiveRootTemporal.d.ts +4 -4
- package/dist/gui/EFConfiguration.d.ts +0 -7
- package/dist/gui/EFConfiguration.js +0 -5
- package/dist/gui/EFConfiguration.js.map +1 -1
- package/dist/gui/EFControls.d.ts +2 -2
- package/dist/gui/EFDial.d.ts +4 -4
- package/dist/gui/EFFocusOverlay.d.ts +4 -4
- package/dist/gui/EFOverlayItem.d.ts +4 -4
- package/dist/gui/EFOverlayLayer.d.ts +4 -4
- package/dist/gui/EFPause.d.ts +4 -4
- package/dist/gui/EFPlay.d.ts +4 -4
- package/dist/gui/EFResizableBox.d.ts +4 -4
- package/dist/gui/EFScrubber.d.ts +4 -4
- package/dist/gui/EFTimeDisplay.d.ts +4 -4
- package/dist/gui/EFTimelineRuler.d.ts +4 -4
- package/dist/gui/EFToggleLoop.d.ts +4 -4
- package/dist/gui/EFTogglePlay.d.ts +4 -4
- package/dist/gui/EFTransformHandles.d.ts +4 -4
- package/dist/gui/EFWorkbench.d.ts +2 -0
- package/dist/gui/EFWorkbench.js +68 -1
- package/dist/gui/EFWorkbench.js.map +1 -1
- package/dist/gui/PlaybackController.d.ts +2 -0
- package/dist/gui/PlaybackController.js +11 -1
- package/dist/gui/PlaybackController.js.map +1 -1
- package/dist/gui/ef-theme.css +11 -0
- package/dist/gui/timeline/EFTimeline.js.map +1 -1
- package/dist/gui/timeline/EFTimelineRow.d.ts +2 -2
- package/dist/gui/timeline/tracks/AudioTrack.js +28 -30
- package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/EFThumbnailStrip.d.ts +1 -0
- package/dist/gui/timeline/tracks/EFThumbnailStrip.js +41 -8
- package/dist/gui/timeline/tracks/EFThumbnailStrip.js.map +1 -1
- package/dist/gui/timeline/tracks/VideoTrack.js +2 -2
- package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -1
- package/dist/gui/timeline/tracks/waveformUtils.js +19 -19
- package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -1
- package/dist/gui/tree/EFTree.d.ts +4 -4
- package/dist/gui/tree/EFTreeItem.d.ts +4 -4
- package/dist/preview/QualityUpgradeScheduler.d.ts +8 -0
- package/dist/preview/QualityUpgradeScheduler.js +13 -1
- package/dist/preview/QualityUpgradeScheduler.js.map +1 -1
- package/dist/preview/renderTimegroupToCanvas.d.ts +144 -0
- package/dist/preview/renderTimegroupToCanvas.js +56 -3
- package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
- package/dist/preview/renderTimegroupToCanvas.types.d.ts +22 -1
- package/dist/preview/renderTimegroupToVideo.d.ts +27 -0
- package/dist/preview/renderTimegroupToVideo.js +13 -5
- package/dist/preview/renderTimegroupToVideo.js.map +1 -1
- package/dist/preview/renderVideoToVideo.js +5 -6
- package/dist/preview/renderVideoToVideo.js.map +1 -1
- package/dist/preview/renderers.d.ts +56 -0
- package/dist/preview/renderers.js +13 -1
- package/dist/preview/renderers.js.map +1 -1
- package/dist/preview/rendering/inlineImages.d.ts +13 -0
- package/dist/preview/rendering/inlineImages.js +7 -1
- package/dist/preview/rendering/inlineImages.js.map +1 -1
- package/dist/preview/rendering/loadImage.d.ts +8 -0
- package/dist/render/EFRenderAPI.js.map +1 -1
- package/dist/transcoding/types/index.d.ts +6 -94
- package/dist/transcoding/utils/UrlGenerator.d.ts +3 -12
- package/dist/transcoding/utils/UrlGenerator.js +3 -29
- package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
- package/package.json +26 -2
- package/test/setup.ts +1 -1
- package/test/useAssetMSW.ts +0 -100
- package/tsdown.config.ts +6 -1
- package/dist/elements/EFMedia/AssetMediaEngine.js +0 -284
- package/dist/elements/EFMedia/AssetMediaEngine.js.map +0 -1
- package/dist/elements/EFMedia/BaseMediaEngine.js +0 -200
- package/dist/elements/EFMedia/BaseMediaEngine.js.map +0 -1
- package/dist/elements/EFMedia/FileMediaEngine.js +0 -122
- package/dist/elements/EFMedia/FileMediaEngine.js.map +0 -1
- package/dist/elements/EFMedia/JitMediaEngine.js +0 -157
- 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
|
|
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,
|
|
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\
|
|
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.
|
|
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.
|
|
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/
|
|
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
|
|
package/test/useAssetMSW.ts
CHANGED
|
@@ -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: [
|
|
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
|