demuxe 0.3.0-beta.3

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 (124) hide show
  1. package/LICENSE +352 -0
  2. package/README.md +75 -0
  3. package/bin/demuxe.mjs +30 -0
  4. package/docs/API-MIGRATION.md +54 -0
  5. package/docs/BETA.md +98 -0
  6. package/docs/BRANDING-MIGRATION.md +77 -0
  7. package/docs/COMPATIBILITY-EXPANSION.md +181 -0
  8. package/docs/LICENSING.md +83 -0
  9. package/docs/OPTIMIZATION-COMPLETION.md +511 -0
  10. package/docs/OPTIMIZATION-FLAC.md +178 -0
  11. package/docs/OPTIMIZATION-INTEGRATION.md +221 -0
  12. package/docs/OPTIMIZATION-REVIEW-FIXES.md +78 -0
  13. package/docs/PLAYER-COMPONENT.md +247 -0
  14. package/docs/PUBLIC-API-VALIDATION.md +163 -0
  15. package/docs/PUBLIC-API.md +245 -0
  16. package/docs/RELEASE.md +181 -0
  17. package/docs/RUNTIME-ASSETS.md +127 -0
  18. package/engine-build.json +8781 -0
  19. package/examples/custom-controls.html +14 -0
  20. package/examples/player-element.html +5 -0
  21. package/fixtures/DejaVuSans.ttf +0 -0
  22. package/fixtures/FONT-LICENSE.txt +187 -0
  23. package/index.d.ts +1 -0
  24. package/index.js +1 -0
  25. package/package.json +46 -0
  26. package/player.d.ts +1 -0
  27. package/player.js +1 -0
  28. package/release-manifest.json +541 -0
  29. package/sources.lock.json +96 -0
  30. package/third_party/notices/dav1d/COPYING +23 -0
  31. package/third_party/notices/emscripten/LICENSE +102 -0
  32. package/third_party/notices/emscripten/system/lib/compiler-rt/LICENSE.TXT +311 -0
  33. package/third_party/notices/emscripten/system/lib/libc/musl/COPYRIGHT +193 -0
  34. package/third_party/notices/emscripten/system/lib/libcxx/LICENSE.TXT +311 -0
  35. package/third_party/notices/emscripten/system/lib/libcxxabi/LICENSE.TXT +311 -0
  36. package/third_party/notices/emsdk/LICENSE +27 -0
  37. package/third_party/notices/ffmpeg/COPYING.GPLv2 +339 -0
  38. package/third_party/notices/ffmpeg/COPYING.GPLv3 +674 -0
  39. package/third_party/notices/ffmpeg/COPYING.LGPLv2.1 +502 -0
  40. package/third_party/notices/ffmpeg/LICENSE.md +129 -0
  41. package/third_party/notices/ffmpeg-adaptation/COPYING.LGPLv2.1 +502 -0
  42. package/third_party/notices/ffmpeg-adaptation/LICENSE.md +127 -0
  43. package/third_party/notices/freetype/LICENSE.TXT +46 -0
  44. package/third_party/notices/freetype/docs/FTL.TXT +169 -0
  45. package/third_party/notices/freetype/docs/GPLv2.TXT +340 -0
  46. package/third_party/notices/fribidi/COPYING +504 -0
  47. package/third_party/notices/harfbuzz/COPYING +42 -0
  48. package/third_party/notices/libass/COPYING +15 -0
  49. package/third_party/notices/libplacebo/LICENSE +458 -0
  50. package/third_party/notices/libxml2/Copyright +23 -0
  51. package/third_party/notices/libxml2/dict.c.notice +17 -0
  52. package/third_party/notices/libxml2/list.c.notice +16 -0
  53. package/third_party/notices/mpv/Copyright +78 -0
  54. package/third_party/notices/mpv/LICENSE.GPL +338 -0
  55. package/third_party/notices/mpv/LICENSE.LGPL +501 -0
  56. package/third_party/notices/vulkan-headers/LICENSE.txt +202 -0
  57. package/third_party/notices/zimg/COPYING +14 -0
  58. package/third_party/notices/zlib/LICENSE +22 -0
  59. package/third_party/notices.json +166 -0
  60. package/toolchain.lock.json +204 -0
  61. package/web/audio-worklet.js +45 -0
  62. package/web/cheap-mp4-probe.js +82 -0
  63. package/web/engine-adaptation/manifest.json +75 -0
  64. package/web/engine-adaptation/remux.mjs +2 -0
  65. package/web/engine-adaptation/remux.wasm +0 -0
  66. package/web/engine-ass/manifest.json +55 -0
  67. package/web/engine-ass/subtitles.mjs +2 -0
  68. package/web/engine-ass/subtitles.wasm +0 -0
  69. package/web/engine-hybrid/player.mjs +2 -0
  70. package/web/engine-hybrid/player.wasm +0 -0
  71. package/web/engine-remux/remux.mjs +2 -0
  72. package/web/engine-remux/remux.wasm +0 -0
  73. package/web/engine-software-full/player.mjs +2 -0
  74. package/web/engine-software-full/player.wasm +0 -0
  75. package/web/file-reader.js +34 -0
  76. package/web/filter-retained-engine-worker.js +292 -0
  77. package/web/generated/index.d.ts +6 -0
  78. package/web/generated/index.js +4 -0
  79. package/web/generated/internal/assets.d.ts +2 -0
  80. package/web/generated/internal/assets.js +7 -0
  81. package/web/generated/internal/backend.d.ts +27 -0
  82. package/web/generated/internal/backend.js +1 -0
  83. package/web/generated/internal/errors.d.ts +13 -0
  84. package/web/generated/internal/errors.js +54 -0
  85. package/web/generated/internal/native-ass.d.ts +38 -0
  86. package/web/generated/internal/native-ass.js +206 -0
  87. package/web/generated/internal/native-player.d.ts +95 -0
  88. package/web/generated/internal/native-player.js +502 -0
  89. package/web/generated/internal/playback-plans.d.ts +526 -0
  90. package/web/generated/internal/playback-plans.js +110 -0
  91. package/web/generated/internal/selection.d.ts +45 -0
  92. package/web/generated/internal/selection.js +45 -0
  93. package/web/generated/internal/state.d.ts +8 -0
  94. package/web/generated/internal/state.js +56 -0
  95. package/web/generated/internal/wasm-player.d.ts +127 -0
  96. package/web/generated/internal/wasm-player.js +372 -0
  97. package/web/generated/player/index.d.ts +198 -0
  98. package/web/generated/player/index.js +873 -0
  99. package/web/generated/player/interaction.d.ts +6 -0
  100. package/web/generated/player/interaction.js +14 -0
  101. package/web/generated/player/styles.d.ts +1 -0
  102. package/web/generated/player/styles.js +86 -0
  103. package/web/generated/types.d.ts +227 -0
  104. package/web/generated/types.js +2 -0
  105. package/web/generated/unified-player.d.ts +129 -0
  106. package/web/generated/unified-player.js +1074 -0
  107. package/web/io-worker.js +65 -0
  108. package/web/native-ass-worker.js +26 -0
  109. package/web/native-remux-player.js +301 -0
  110. package/web/native-remux-source-worker.js +25 -0
  111. package/web/native-remux-worker.js +46 -0
  112. package/web/range-reader.js +130 -0
  113. package/web/remux-packaging.js +11 -0
  114. package/web/resource-loader.js +173 -0
  115. package/web/retained-decoder-worker.js +200 -0
  116. package/web/retained-video.js +15 -0
  117. package/web/segmented-subtitles.js +37 -0
  118. package/web/software-full-engine-worker.js +234 -0
  119. package/web/source-probe.js +28 -0
  120. package/web/split-mp4.js +159 -0
  121. package/web/streaming-manifest.js +136 -0
  122. package/web/subtitle-overlay.js +26 -0
  123. package/web/video-codec-config.js +59 -0
  124. package/web/vod-manifest.js +54 -0
@@ -0,0 +1,6 @@
1
+ export declare function formatTime(value: number): string;
2
+ export declare function outputDimensions(ratio: number): {
3
+ width: number;
4
+ height: number;
5
+ };
6
+ export declare function shortcut(event: KeyboardEvent, spaceControlsPlayback?: boolean): string | null;
@@ -0,0 +1,14 @@
1
+ export function formatTime(value) {
2
+ const n = Math.max(0, Math.floor(value)), seconds = String(n % 60).padStart(2, '0'), minutes = String(Math.floor(n / 60) % 60).padStart(2, '0');
3
+ return n >= 3600 ? `${Math.floor(n / 3600)}:${minutes}:${seconds}` : `${Math.floor(n / 60)}:${seconds}`;
4
+ }
5
+ export function outputDimensions(ratio) { const width = Math.min(1920, 1080 * ratio); return { width: Math.max(1, Math.round(width)), height: Math.max(1, Math.round(width / ratio)) }; }
6
+ export function shortcut(event, spaceControlsPlayback = false) {
7
+ if (event.altKey || event.ctrlKey || event.metaKey || event.isComposing || event.defaultPrevented)
8
+ return null;
9
+ if (event.composedPath().some(node => node instanceof HTMLElement && (node.matches('input,select,textarea,[role="slider"]') || node.isContentEditable)))
10
+ return null;
11
+ if ((event.key === 'Enter' || event.key === ' ' && !spaceControlsPlayback) && event.composedPath().some(node => node instanceof HTMLElement && node.matches('button,a,summary,[role="button"]')))
12
+ return null;
13
+ return event.key.toLowerCase();
14
+ }
@@ -0,0 +1 @@
1
+ export declare const styles = "\n.queue-navigation{display:flex;align-items:center;gap:2px;flex:none;margin-left:auto;font-size:10px;font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--demuxe-muted-foreground)}\n.queue-navigation .icon-button{width:30px;min-width:30px;flex-basis:30px;padding:7px}.queue-navigation svg{width:16px;height:16px}\n.times:has(.queue-navigation:not([hidden])){gap:4px}.times:has(.queue-navigation:not([hidden]))>.space{display:none}\n.queue-section{margin-top:16px;padding-top:10px;border-top:1px solid var(--demuxe-border)}\n.queue-header{display:flex;align-items:center;justify-content:space-between;gap:8px}.queue-header button{font-size:10px;padding:6px 8px}\n#queue-list{padding:0;margin:8px 0 0;list-style:none;max-height:min(240px,30vh);overflow:auto;overscroll-behavior:contain}#queue-list li{display:flex;gap:4px;align-items:center}\n#queue-list .queue-item{flex:1;min-width:0;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;background:transparent;border:0;padding:8px}\n#queue-list .queue-item[aria-current=true]{color:var(--demuxe-accent);font-weight:650;background:var(--demuxe-control-background)}\n#queue-list .queue-remove{flex:none;background:transparent;border:0;font-size:19px;padding:4px 10px}\n@media(pointer:coarse){.queue-navigation .icon-button{width:44px;min-width:44px;flex-basis:44px}}\n\n:host{display:block;contain:layout;container-type:inline-size;container-name:player;--demuxe-background:#101114;--demuxe-foreground:#f6f3ed;--demuxe-accent:#f3ead8;--demuxe-border:#ffffff24;--demuxe-radius:14px;--demuxe-stage-background:#08090c;--demuxe-muted-foreground:#b8bbc3;--demuxe-panel-background:#151619;--demuxe-control-background:#25262b;--demuxe-overlay-background:#30343b70;color:var(--demuxe-foreground);font:14px/1.5 system-ui,sans-serif;color-scheme:dark}\n*{box-sizing:border-box}[hidden]{display:none!important}button,input,select{font:inherit;color:inherit}button,select{cursor:pointer}button{min-height:44px;border:1px solid var(--demuxe-border);border-radius:8px;background:color-mix(in srgb,var(--demuxe-foreground) 3%,transparent);padding:8px 14px}button:hover{background:color-mix(in srgb,var(--demuxe-foreground) 9%,transparent)}button:disabled,input:disabled,select:disabled{opacity:.4;cursor:default}button:focus-visible,input:focus-visible,select:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--demuxe-accent);outline-offset:4px}input[type=file]{width:100%;min-width:0;font-size:12px}input[type=range]{accent-color:var(--demuxe-accent);cursor:pointer;margin:0;min-height:32px}\n.shell{position:relative;isolation:isolate;background:var(--demuxe-background);border:1px solid var(--demuxe-border);border-radius:var(--demuxe-radius);overflow:clip}.stage{position:relative;width:100%;min-width:0;display:grid;place-items:center;background:var(--demuxe-stage-background);min-height:240px;aspect-ratio:var(--media-aspect,16/9);max-height:80vh;overflow:hidden}.surface,.poster{position:absolute;inset:0;width:100%;height:100%;display:grid;place-items:center}.surface .demuxe-player{width:100%;height:100%;display:grid;place-items:center}.surface video,.surface canvas,.poster{width:100%;height:100%;max-width:100%;max-height:100%;object-fit:contain}\n.topbar{position:absolute;inset:0 0 auto;z-index:2;display:flex;align-items:center;gap:6px;padding:clamp(8px,1.3cqw,16px) clamp(12px,2.2cqw,26px) 24px;background:linear-gradient(var(--demuxe-overlay-background),transparent);pointer-events:none}.topbar>*{pointer-events:auto}.player-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:1;font-size:clamp(12px,1.3cqw,15px);font-weight:450;letter-spacing:.1px}.space{flex:1;min-width:0}.icon-button{display:inline-grid;place-items:center;flex:0 0 36px;width:36px;height:36px;min-width:36px;min-height:36px;padding:8px;border:0;border-radius:50%;background:transparent;transition:background .15s,transform .15s}.icon-button:hover{background:color-mix(in srgb,var(--demuxe-foreground) 8%,transparent)}.icon-button svg{width:20px;height:20px;display:block;pointer-events:none;filter:drop-shadow(0 1px 4px #0008)}.icon-button[aria-expanded=true],.icon-button[aria-pressed=true]{color:var(--demuxe-accent);background:color-mix(in srgb,var(--demuxe-foreground) 7%,transparent)}\n.transport{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:2;display:flex;align-items:center;gap:clamp(16px,3cqw,36px)}.transport .play{width:clamp(56px,8cqw,88px);height:clamp(56px,8cqw,88px);flex-basis:clamp(56px,8cqw,88px);border:0;border-radius:8px;background:transparent;backdrop-filter:none}.transport .icon-button:hover{background:transparent;transform:scale(1.06)}.transport .play svg{width:clamp(44px,6.5cqw,72px);height:clamp(44px,6.5cqw,72px)}.transport #back,.transport #forward{width:60px;height:60px;flex-basis:60px;padding:10px}.transport #back svg,.transport #forward svg{width:clamp(28px,3cqw,36px);height:clamp(28px,3cqw,36px);stroke-width:1.5}\n.controls{position:absolute;z-index:2;inset:auto 0 0;padding:20px clamp(12px,2.3cqw,28px) clamp(8px,1.2cqw,14px);background:linear-gradient(transparent,var(--demuxe-overlay-background));pointer-events:none}.controls>*{pointer-events:auto}.row{display:flex;align-items:center;gap:4px;margin-bottom:0}.volume{width:clamp(42px,5cqw,60px)}.times{display:flex;justify-content:space-between;gap:20px}.time{font-size:clamp(10px,1.1cqw,12px);font-variant-numeric:tabular-nums;text-shadow:0 1px 4px #0008}.timeline{width:100%;display:block;appearance:none;background:transparent;padding:0;height:28px;--progress:0%}.timeline::-webkit-slider-runnable-track{height:3px;border-radius:3px;background:linear-gradient(to right,var(--demuxe-foreground) var(--progress),transparent var(--progress)),var(--buffered,linear-gradient(transparent,transparent)),color-mix(in srgb,var(--demuxe-foreground) 13%,transparent)}.timeline::-webkit-slider-thumb{appearance:none;width:12px;height:12px;border-radius:50%;background:var(--demuxe-accent);margin-top:-4.5px}.timeline::-moz-range-track{height:3px;background:var(--buffered,linear-gradient(transparent,transparent)),color-mix(in srgb,var(--demuxe-foreground) 13%,transparent);border-radius:3px}.timeline::-moz-range-progress{height:3px;background:var(--demuxe-foreground)}.timeline::-moz-range-thumb{width:12px;height:12px;border:0;border-radius:50%;background:var(--demuxe-accent)}\n.settings{position:absolute;z-index:4;right:clamp(12px,2.2cqw,26px);bottom:106px;width:260px;max-width:calc(100% - 24px);max-height:calc(100% - 124px);padding:6px 14px 10px;font-size:12px;background:var(--demuxe-panel-background);border:1px solid color-mix(in srgb,var(--demuxe-foreground) 8%,transparent);border-radius:10px;box-shadow:0 16px 40px #0007;color:var(--demuxe-foreground);overflow:auto}.settings header{display:flex;align-items:center;justify-content:space-between;gap:8px;position:sticky;top:-6px;background:var(--demuxe-panel-background);border-radius:0;border-bottom:1px solid color-mix(in srgb,var(--demuxe-foreground) 5%,transparent);z-index:1}.settings header strong{font-size:12px;font-weight:550;letter-spacing:.1px}.settings header .icon-button{margin-right:-10px}.settings label{display:grid;gap:8px;margin:12px 0}.settings .setting-row{display:flex;align-items:center;justify-content:space-between;gap:14px;min-height:44px;margin:0;border-bottom:1px solid color-mix(in srgb,var(--demuxe-foreground) 3%,transparent)}.setting-row span{flex-shrink:0}.settings select{color:var(--demuxe-muted-foreground);min-height:44px;width:100%;min-width:0;border:0;border-radius:5px;background:transparent;padding:8px}.setting-row select{width:auto;max-width:60%;text-align:right;text-overflow:ellipsis}.media-picker{display:grid;gap:8px;margin:12px 0}.media-picker button{justify-self:start}.media-picker #current-source{color:var(--demuxe-muted-foreground);overflow-wrap:anywhere;min-width:0}\n.settings .subtitle-picker{font-size:12px;color:var(--demuxe-muted-foreground);padding-top:6px}.settings input[type=url]{width:100%;min-width:0;background:var(--demuxe-control-background);border:1px solid color-mix(in srgb,var(--demuxe-foreground) 7%,transparent);border-radius:6px;padding:10px}.settings .check{display:flex;align-items:center;gap:8px}.source-menu{top:76px;bottom:auto;max-height:calc(100% - 100px);width:320px}.settings form{border-top:1px solid color-mix(in srgb,var(--demuxe-foreground) 9%,transparent);margin-top:18px;padding-top:4px}.help{font-size:11px;color:var(--demuxe-muted-foreground);line-height:1.7;margin:14px 0 0}\n.notice{position:absolute;z-index:5;top:78px;left:20px;right:20px;display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--demuxe-overlay-background);border:1px solid var(--demuxe-border);border-radius:10px;overflow-wrap:anywhere}.notice span{flex:1}.status{position:absolute;z-index:2;bottom:132px;left:24px;max-width:calc(100% - 48px);padding:6px 10px;background:var(--demuxe-overlay-background);border-radius:6px;font-size:12px;pointer-events:none}.status:empty{display:none}.busy{position:absolute;top:80px;left:26px;z-index:2;padding:6px 12px;background:var(--demuxe-overlay-background);border-radius:20px;font-size:12px}\n.empty{position:relative;text-align:center;max-width:100%;padding:80px 24px}.empty strong{display:block;font-size:25px;font-weight:450;letter-spacing:-.5px}.empty p{color:var(--demuxe-muted-foreground);margin:8px 0 24px}.emblem{display:grid;place-items:center;width:80px;height:80px;margin:0 auto 24px;border:1px solid color-mix(in srgb,var(--demuxe-foreground) 38%,transparent);border-radius:50%;font-size:32px}.empty button{background:var(--demuxe-foreground);color:var(--demuxe-background);border:0;padding:10px 24px}.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}\n.topbar,.controls,.transport{transition:opacity .22s}.idle .topbar,.idle .controls,.idle .transport{opacity:0;pointer-events:none}.idle .topbar *,.idle .controls *,.idle .transport *{pointer-events:none}.idle{cursor:none}.shell:not(.idle):has(:focus-visible) .topbar,.shell:not(.idle):has(:focus-visible) .controls,.shell:not(.idle):has(:focus-visible) .transport{opacity:1}.shell:not(.idle):has(:focus-visible) .topbar *,.shell:not(.idle):has(:focus-visible) .controls *,.shell:not(.idle):has(:focus-visible) .transport *{pointer-events:auto}\n:host(:fullscreen){background:var(--demuxe-stage-background);width:100%;height:100%}:host(:fullscreen) .shell{height:100%;border:0;border-radius:0}:host(:fullscreen) .stage{height:100%;width:100%;max-height:none;min-height:0;aspect-ratio:auto}\n@container player (max-width:600px){.stage{min-height:240px;max-height:none}.topbar{padding:6px 10px 18px}.controls{padding:12px 12px 8px}.settings{right:10px;bottom:94px;max-height:calc(100% - 108px);width:240px}.source-menu{top:50px;bottom:auto;max-height:calc(100% - 62px)}.menu-open .transport{opacity:0;visibility:hidden}.empty{padding:48px 16px}.empty strong{font-size:18px}.empty p{font-size:12px;margin:6px 0 12px}.emblem{width:44px;height:44px;font-size:23px;margin-bottom:12px}.status{bottom:100px;left:12px}.busy{top:50px;left:12px}.volume{width:42px}}\n@media(pointer:coarse){.icon-button{width:44px;height:44px;min-width:44px;min-height:44px;flex-basis:44px}.settings{bottom:114px;max-height:calc(100% - 126px)}.source-menu{bottom:auto;max-height:calc(100% - 80px)}}\n.settings button,.empty button{background:var(--demuxe-control-background);color:var(--demuxe-foreground);border:1px solid color-mix(in srgb,var(--demuxe-foreground) 9%,transparent);border-radius:7px;box-shadow:0 1px 2px #0003;font-weight:500}.settings button:hover,.empty button:hover{background:var(--demuxe-control-background);border-color:color-mix(in srgb,var(--demuxe-foreground) 15%,transparent)}.settings .icon-button{background:transparent;border:0;box-shadow:none;border-radius:6px;color:var(--demuxe-muted-foreground)}.settings .icon-button:hover{background:color-mix(in srgb,var(--demuxe-foreground) 3%,transparent);color:var(--demuxe-foreground)}.settings .icon-button svg{width:17px;height:17px;filter:none}.settings select:hover:not(:disabled){background:color-mix(in srgb,var(--demuxe-foreground) 2%,transparent);color:var(--demuxe-foreground)}.settings option{background:var(--demuxe-panel-background);color:var(--demuxe-foreground)}.settings input::file-selector-button{font:inherit;color:var(--demuxe-foreground);background:var(--demuxe-control-background);border:1px solid color-mix(in srgb,var(--demuxe-foreground) 8%,transparent);border-radius:6px;padding:8px 10px;margin-right:8px;cursor:pointer}.topbar .icon-button:hover,.row .icon-button:hover{background:var(--demuxe-control-background)}.row .icon-button[aria-expanded=true],.row .icon-button[aria-pressed=true]{background:var(--demuxe-control-background);color:var(--demuxe-foreground)}\n/* One quiet, distinctive entry point into the empty player. */\n.empty #open{display:inline-flex;align-items:center;gap:16px;min-height:62px;padding:12px 22px;background:linear-gradient(135deg,color-mix(in srgb,var(--demuxe-foreground) 3%,transparent),color-mix(in srgb,var(--demuxe-foreground) 1%,transparent));border:1px solid color-mix(in srgb,var(--demuxe-foreground) 9%,transparent);border-radius:18px;box-shadow:inset 0 1px 0 color-mix(in srgb,var(--demuxe-foreground) 3%,transparent);color:var(--demuxe-foreground);font-size:14px;font-weight:450;letter-spacing:.2px;transition:border-color .2s,background .2s,box-shadow .2s}.empty #open .open-folder{width:28px;height:28px;color:var(--demuxe-foreground)}.empty #open .open-arrow{width:18px;height:18px;color:var(--demuxe-muted-foreground);margin-left:10px;transition:transform .2s,color .2s}.empty #open:hover{background:linear-gradient(135deg,color-mix(in srgb,var(--demuxe-foreground) 6%,transparent),color-mix(in srgb,var(--demuxe-foreground) 2%,transparent));border-color:color-mix(in srgb,var(--demuxe-foreground) 19%,transparent);box-shadow:inset 0 1px 0 color-mix(in srgb,var(--demuxe-foreground) 6%,transparent),0 8px 30px #0002;color:var(--demuxe-foreground)}.empty #open:hover .open-arrow{transform:translateX(3px);color:var(--demuxe-foreground)}.empty #open:active{transform:translateY(1px)}\n.diagnostics-overlay{position:absolute;z-index:3;left:clamp(12px,2.2cqw,26px);top:62px;max-width:calc(100% - 48px);max-height:calc(100% - 156px);margin:0;padding:10px 12px;overflow:auto;overscroll-behavior:contain;scrollbar-width:thin;white-space:pre-wrap;overflow-wrap:anywhere;font:11px/1.65 ui-monospace,SFMono-Regular,monospace;color:var(--demuxe-foreground);background:var(--demuxe-overlay-background);border:1px solid color-mix(in srgb,var(--demuxe-foreground) 9%,transparent);border-radius:6px;text-shadow:0 1px 3px #000c;pointer-events:auto}\n@container player (max-width:600px){.diagnostics-overlay{top:52px;max-height:calc(100% - 120px);font-size:9px;line-height:1.45;padding:6px 8px}}\n/* Top actions stay unboxed; motion and stroke carry interaction feedback. */\n.topbar .icon-button,.topbar .icon-button:hover,.topbar .icon-button[aria-expanded=true],.topbar .icon-button[aria-pressed=true]{background:transparent;box-shadow:none}\n.topbar .icon-button{color:var(--demuxe-muted-foreground);transition:color .16s,translate .2s var(--motion-ease),scale .18s var(--motion-ease)}\n.topbar .icon-button:hover:not(:disabled){color:var(--demuxe-foreground);translate:0 -2px;scale:1.08}.topbar .icon-button:active:not(:disabled){translate:0 0;scale:.94}\n.topbar .icon-button[aria-expanded=true],.topbar .icon-button[aria-pressed=true]{color:var(--demuxe-accent)}\n.topbar .icon-button[aria-expanded=true] svg,.topbar .icon-button[aria-pressed=true] svg{stroke-width:2.5;filter:drop-shadow(0 0 5px color-mix(in srgb,var(--demuxe-accent) 20%,transparent))}\n/* Top-right menus align with their triggers; volume sits under the timeline. */\n.settings{top:64px;bottom:auto;max-height:calc(100% - 80px);transform-origin:top right;--menu-offset:-6px}.settings:not([hidden]){transform-origin:top right}.source-menu{top:64px}.controls .times{align-items:center;gap:12px}.controls .row{margin:0}\n@container player (max-width:600px){.transport{top:42%}.settings,.source-menu{top:50px;bottom:auto;max-height:calc(100% - 62px)}}\n/* Show only the existing timeline briefly after a hidden-control seek. */\n.idle.seek-preview .controls{opacity:1;translate:0 0}.idle .controls .row,.idle .controls slot{visibility:hidden}.idle.seek-preview .controls,.idle.seek-preview .controls *{pointer-events:none}\n/* A slim volume track inside the existing pointer target. */\n.volume{appearance:none;background:transparent;height:32px;padding:0;--volume-progress:100%}\n.volume::-webkit-slider-runnable-track{height:2px;border-radius:2px;background:linear-gradient(to right,var(--demuxe-foreground) var(--volume-progress),color-mix(in srgb,var(--demuxe-foreground) 19%,transparent) var(--volume-progress))}\n.volume::-webkit-slider-thumb{appearance:none;width:8px;height:8px;margin-top:-3px;border:0;border-radius:50%;background:var(--demuxe-foreground);transition:scale .16s,box-shadow .16s}\n.volume::-moz-range-track{height:2px;border-radius:2px;background:color-mix(in srgb,var(--demuxe-foreground) 19%,transparent)}.volume::-moz-range-progress{height:2px;border-radius:2px;background:var(--demuxe-foreground)}.volume::-moz-range-thumb{width:8px;height:8px;border:0;border-radius:50%;background:var(--demuxe-foreground);transition:scale .16s,box-shadow .16s}\n.volume:hover::-webkit-slider-thumb,.volume:focus-visible::-webkit-slider-thumb{scale:1.25;box-shadow:0 0 0 3px color-mix(in srgb,var(--demuxe-foreground) 6%,transparent)}.volume:hover::-moz-range-thumb,.volume:focus-visible::-moz-range-thumb{scale:1.25;box-shadow:0 0 0 3px color-mix(in srgb,var(--demuxe-foreground) 6%,transparent)}\n@media(forced-colors:active){.volume{appearance:auto}}\n/* Motion stays on overlays so playback geometry never shifts. */\n:host{--motion-ease:cubic-bezier(.22,1,.36,1)}\n.topbar,.controls,.transport{transition:opacity .24s ease,translate .32s var(--motion-ease),scale .32s var(--motion-ease)}\n.idle .topbar{translate:0 -6px}.idle .controls{translate:0 8px}.idle .transport{scale:.96}\n.shell:not(.idle):has(:focus-visible) .topbar,.shell:not(.idle):has(:focus-visible) .controls{translate:0 0}.shell:not(.idle):has(:focus-visible) .transport{scale:1}\nbutton{transition:background .16s,color .16s,border-color .16s,scale .18s var(--motion-ease)}button:active:not(:disabled){scale:.94}\n.icon-button svg{transition:transform .28s var(--motion-ease)}#settings-toggle[aria-expanded=true] svg{transform:rotate(35deg)}\n.settings:not([hidden]){transform-origin:top right}.settings.source-menu{transform-origin:top right;--menu-offset:-6px}\n\n\n.timeline::-webkit-slider-thumb{transition:scale .16s var(--motion-ease),box-shadow .16s}.timeline:hover:not(:disabled)::-webkit-slider-thumb,.timeline:focus-visible::-webkit-slider-thumb{scale:1.2;box-shadow:0 0 0 4px color-mix(in srgb,var(--demuxe-foreground) 8%,transparent)}.timeline:active:not(:disabled)::-webkit-slider-thumb{scale:1.35}\n.timeline::-moz-range-thumb{transition:scale .16s var(--motion-ease),box-shadow .16s}.timeline:hover:not(:disabled)::-moz-range-thumb,.timeline:focus-visible::-moz-range-thumb{scale:1.2;box-shadow:0 0 0 4px color-mix(in srgb,var(--demuxe-foreground) 8%,transparent)}.timeline:active:not(:disabled)::-moz-range-thumb{scale:1.35}\n.empty #open{transition:border-color .2s,background .2s,box-shadow .2s,scale .18s var(--motion-ease)}.empty #open .open-folder{transition:transform .25s var(--motion-ease)}.empty #open:hover .open-folder{transform:translateY(-2px) rotate(-5deg)}\n.busy{display:flex;align-items:center;gap:8px}.busy::before{content:\"\";width:10px;height:10px;flex:none;border:1.5px solid color-mix(in srgb,var(--demuxe-foreground) 19%,transparent);border-top-color:currentColor;border-radius:50%;animation:busy-turn .8s linear infinite}@keyframes busy-turn{to{transform:rotate(360deg)}}\n/* Waiting feedback stays visible independently of playback chrome. */\n.buffering-indicator{position:absolute;z-index:3;left:50%;top:50%;translate:-50% -50%;pointer-events:none;animation:buffer-appear .18s ease-out .15s backwards}\n.buffering-indicator span{display:block;width:clamp(32px,4.5cqw,48px);height:clamp(32px,4.5cqw,48px);border:2px solid color-mix(in srgb,var(--demuxe-foreground) 13%,transparent);border-top-color:var(--demuxe-accent);border-right-color:color-mix(in srgb,var(--demuxe-accent) 60%,transparent);border-radius:50%;filter:drop-shadow(0 1px 4px #0008);animation:busy-turn .85s linear infinite}\n.buffering .transport #play svg{opacity:0}.transport #play svg{transition:opacity .18s}\n@keyframes buffer-appear{from{opacity:0;scale:.9}to{opacity:1;scale:1}}\n@container player (max-width:600px){.buffering-indicator{top:42%}}\n@media(forced-colors:active){.buffering-indicator span{border-color:GrayText;border-top-color:Highlight}}\n/* Short stagger on reveal, coordinated fade on dismissal. */\n.topbar .icon-button{transition:opacity .18s ease,translate .24s var(--motion-ease),scale .2s var(--motion-ease),color .16s;transition-delay:var(--icon-delay,0ms)}\n#diagnostics-toggle{--icon-delay:0ms}#open-menu{--icon-delay:25ms}#settings-toggle{--icon-delay:50ms}#fullscreen{--icon-delay:75ms}\n.idle .topbar .icon-button{opacity:0;translate:0 -4px;scale:.92;transition-delay:0ms}\n.transport .icon-button{transition:opacity .18s ease,translate .26s var(--motion-ease),scale .2s var(--motion-ease),transform .15s}\n.transport #back{transition-delay:25ms}.transport #forward{transition-delay:50ms}.idle .transport .icon-button{opacity:0;translate:0 5px;scale:.94;transition-delay:0ms}\n.controls .timeline,.controls .times{transition:opacity .18s ease,translate .26s var(--motion-ease)}.controls .times{transition-delay:35ms}\n.idle:not(.seek-preview) .controls .timeline,.idle:not(.seek-preview) .controls .times{opacity:0;translate:0 4px;transition-delay:0ms}\n.settings,.diagnostics-overlay,.empty,.notice{opacity:1;translate:0 0;scale:1;transition:opacity .18s ease,translate .24s var(--motion-ease),scale .24s var(--motion-ease),display .24s allow-discrete}\n.settings[hidden],.diagnostics-overlay[hidden],.empty[hidden],.notice[hidden]{opacity:0;translate:0 -5px;scale:.985;pointer-events:none}\n.settings[hidden] *,.empty[hidden] *,.notice[hidden] *{pointer-events:none}\n@starting-style{.settings:not([hidden]),.diagnostics-overlay:not([hidden]),.empty:not([hidden]),.notice:not([hidden]){opacity:0;translate:0 -5px;scale:.985}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{scroll-behavior:auto;transition:none!important;animation:none!important}}\n\n@media(forced-colors:active){button,select,.shell,.settings{border:1px solid ButtonText}.transport .play{border-color:ButtonText}.stage{background:Canvas}.settings,.source-menu{background:Canvas}.timeline{appearance:auto;accent-color:Highlight}.icon-button,.time,.empty,.help{color:CanvasText}}\n";
@@ -0,0 +1,86 @@
1
+ export const styles = `
2
+ .queue-navigation{display:flex;align-items:center;gap:2px;flex:none;margin-left:auto;font-size:10px;font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--demuxe-muted-foreground)}
3
+ .queue-navigation .icon-button{width:30px;min-width:30px;flex-basis:30px;padding:7px}.queue-navigation svg{width:16px;height:16px}
4
+ .times:has(.queue-navigation:not([hidden])){gap:4px}.times:has(.queue-navigation:not([hidden]))>.space{display:none}
5
+ .queue-section{margin-top:16px;padding-top:10px;border-top:1px solid var(--demuxe-border)}
6
+ .queue-header{display:flex;align-items:center;justify-content:space-between;gap:8px}.queue-header button{font-size:10px;padding:6px 8px}
7
+ #queue-list{padding:0;margin:8px 0 0;list-style:none;max-height:min(240px,30vh);overflow:auto;overscroll-behavior:contain}#queue-list li{display:flex;gap:4px;align-items:center}
8
+ #queue-list .queue-item{flex:1;min-width:0;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;background:transparent;border:0;padding:8px}
9
+ #queue-list .queue-item[aria-current=true]{color:var(--demuxe-accent);font-weight:650;background:var(--demuxe-control-background)}
10
+ #queue-list .queue-remove{flex:none;background:transparent;border:0;font-size:19px;padding:4px 10px}
11
+ @media(pointer:coarse){.queue-navigation .icon-button{width:44px;min-width:44px;flex-basis:44px}}
12
+
13
+ :host{display:block;contain:layout;container-type:inline-size;container-name:player;--demuxe-background:#101114;--demuxe-foreground:#f6f3ed;--demuxe-accent:#f3ead8;--demuxe-border:#ffffff24;--demuxe-radius:14px;--demuxe-stage-background:#08090c;--demuxe-muted-foreground:#b8bbc3;--demuxe-panel-background:#151619;--demuxe-control-background:#25262b;--demuxe-overlay-background:#30343b70;color:var(--demuxe-foreground);font:14px/1.5 system-ui,sans-serif;color-scheme:dark}
14
+ *{box-sizing:border-box}[hidden]{display:none!important}button,input,select{font:inherit;color:inherit}button,select{cursor:pointer}button{min-height:44px;border:1px solid var(--demuxe-border);border-radius:8px;background:color-mix(in srgb,var(--demuxe-foreground) 3%,transparent);padding:8px 14px}button:hover{background:color-mix(in srgb,var(--demuxe-foreground) 9%,transparent)}button:disabled,input:disabled,select:disabled{opacity:.4;cursor:default}button:focus-visible,input:focus-visible,select:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--demuxe-accent);outline-offset:4px}input[type=file]{width:100%;min-width:0;font-size:12px}input[type=range]{accent-color:var(--demuxe-accent);cursor:pointer;margin:0;min-height:32px}
15
+ .shell{position:relative;isolation:isolate;background:var(--demuxe-background);border:1px solid var(--demuxe-border);border-radius:var(--demuxe-radius);overflow:clip}.stage{position:relative;width:100%;min-width:0;display:grid;place-items:center;background:var(--demuxe-stage-background);min-height:240px;aspect-ratio:var(--media-aspect,16/9);max-height:80vh;overflow:hidden}.surface,.poster{position:absolute;inset:0;width:100%;height:100%;display:grid;place-items:center}.surface .demuxe-player{width:100%;height:100%;display:grid;place-items:center}.surface video,.surface canvas,.poster{width:100%;height:100%;max-width:100%;max-height:100%;object-fit:contain}
16
+ .topbar{position:absolute;inset:0 0 auto;z-index:2;display:flex;align-items:center;gap:6px;padding:clamp(8px,1.3cqw,16px) clamp(12px,2.2cqw,26px) 24px;background:linear-gradient(var(--demuxe-overlay-background),transparent);pointer-events:none}.topbar>*{pointer-events:auto}.player-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:1;font-size:clamp(12px,1.3cqw,15px);font-weight:450;letter-spacing:.1px}.space{flex:1;min-width:0}.icon-button{display:inline-grid;place-items:center;flex:0 0 36px;width:36px;height:36px;min-width:36px;min-height:36px;padding:8px;border:0;border-radius:50%;background:transparent;transition:background .15s,transform .15s}.icon-button:hover{background:color-mix(in srgb,var(--demuxe-foreground) 8%,transparent)}.icon-button svg{width:20px;height:20px;display:block;pointer-events:none;filter:drop-shadow(0 1px 4px #0008)}.icon-button[aria-expanded=true],.icon-button[aria-pressed=true]{color:var(--demuxe-accent);background:color-mix(in srgb,var(--demuxe-foreground) 7%,transparent)}
17
+ .transport{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:2;display:flex;align-items:center;gap:clamp(16px,3cqw,36px)}.transport .play{width:clamp(56px,8cqw,88px);height:clamp(56px,8cqw,88px);flex-basis:clamp(56px,8cqw,88px);border:0;border-radius:8px;background:transparent;backdrop-filter:none}.transport .icon-button:hover{background:transparent;transform:scale(1.06)}.transport .play svg{width:clamp(44px,6.5cqw,72px);height:clamp(44px,6.5cqw,72px)}.transport #back,.transport #forward{width:60px;height:60px;flex-basis:60px;padding:10px}.transport #back svg,.transport #forward svg{width:clamp(28px,3cqw,36px);height:clamp(28px,3cqw,36px);stroke-width:1.5}
18
+ .controls{position:absolute;z-index:2;inset:auto 0 0;padding:20px clamp(12px,2.3cqw,28px) clamp(8px,1.2cqw,14px);background:linear-gradient(transparent,var(--demuxe-overlay-background));pointer-events:none}.controls>*{pointer-events:auto}.row{display:flex;align-items:center;gap:4px;margin-bottom:0}.volume{width:clamp(42px,5cqw,60px)}.times{display:flex;justify-content:space-between;gap:20px}.time{font-size:clamp(10px,1.1cqw,12px);font-variant-numeric:tabular-nums;text-shadow:0 1px 4px #0008}.timeline{width:100%;display:block;appearance:none;background:transparent;padding:0;height:28px;--progress:0%}.timeline::-webkit-slider-runnable-track{height:3px;border-radius:3px;background:linear-gradient(to right,var(--demuxe-foreground) var(--progress),transparent var(--progress)),var(--buffered,linear-gradient(transparent,transparent)),color-mix(in srgb,var(--demuxe-foreground) 13%,transparent)}.timeline::-webkit-slider-thumb{appearance:none;width:12px;height:12px;border-radius:50%;background:var(--demuxe-accent);margin-top:-4.5px}.timeline::-moz-range-track{height:3px;background:var(--buffered,linear-gradient(transparent,transparent)),color-mix(in srgb,var(--demuxe-foreground) 13%,transparent);border-radius:3px}.timeline::-moz-range-progress{height:3px;background:var(--demuxe-foreground)}.timeline::-moz-range-thumb{width:12px;height:12px;border:0;border-radius:50%;background:var(--demuxe-accent)}
19
+ .settings{position:absolute;z-index:4;right:clamp(12px,2.2cqw,26px);bottom:106px;width:260px;max-width:calc(100% - 24px);max-height:calc(100% - 124px);padding:6px 14px 10px;font-size:12px;background:var(--demuxe-panel-background);border:1px solid color-mix(in srgb,var(--demuxe-foreground) 8%,transparent);border-radius:10px;box-shadow:0 16px 40px #0007;color:var(--demuxe-foreground);overflow:auto}.settings header{display:flex;align-items:center;justify-content:space-between;gap:8px;position:sticky;top:-6px;background:var(--demuxe-panel-background);border-radius:0;border-bottom:1px solid color-mix(in srgb,var(--demuxe-foreground) 5%,transparent);z-index:1}.settings header strong{font-size:12px;font-weight:550;letter-spacing:.1px}.settings header .icon-button{margin-right:-10px}.settings label{display:grid;gap:8px;margin:12px 0}.settings .setting-row{display:flex;align-items:center;justify-content:space-between;gap:14px;min-height:44px;margin:0;border-bottom:1px solid color-mix(in srgb,var(--demuxe-foreground) 3%,transparent)}.setting-row span{flex-shrink:0}.settings select{color:var(--demuxe-muted-foreground);min-height:44px;width:100%;min-width:0;border:0;border-radius:5px;background:transparent;padding:8px}.setting-row select{width:auto;max-width:60%;text-align:right;text-overflow:ellipsis}.media-picker{display:grid;gap:8px;margin:12px 0}.media-picker button{justify-self:start}.media-picker #current-source{color:var(--demuxe-muted-foreground);overflow-wrap:anywhere;min-width:0}
20
+ .settings .subtitle-picker{font-size:12px;color:var(--demuxe-muted-foreground);padding-top:6px}.settings input[type=url]{width:100%;min-width:0;background:var(--demuxe-control-background);border:1px solid color-mix(in srgb,var(--demuxe-foreground) 7%,transparent);border-radius:6px;padding:10px}.settings .check{display:flex;align-items:center;gap:8px}.source-menu{top:76px;bottom:auto;max-height:calc(100% - 100px);width:320px}.settings form{border-top:1px solid color-mix(in srgb,var(--demuxe-foreground) 9%,transparent);margin-top:18px;padding-top:4px}.help{font-size:11px;color:var(--demuxe-muted-foreground);line-height:1.7;margin:14px 0 0}
21
+ .notice{position:absolute;z-index:5;top:78px;left:20px;right:20px;display:flex;align-items:center;gap:12px;padding:12px 16px;background:var(--demuxe-overlay-background);border:1px solid var(--demuxe-border);border-radius:10px;overflow-wrap:anywhere}.notice span{flex:1}.status{position:absolute;z-index:2;bottom:132px;left:24px;max-width:calc(100% - 48px);padding:6px 10px;background:var(--demuxe-overlay-background);border-radius:6px;font-size:12px;pointer-events:none}.status:empty{display:none}.busy{position:absolute;top:80px;left:26px;z-index:2;padding:6px 12px;background:var(--demuxe-overlay-background);border-radius:20px;font-size:12px}
22
+ .empty{position:relative;text-align:center;max-width:100%;padding:80px 24px}.empty strong{display:block;font-size:25px;font-weight:450;letter-spacing:-.5px}.empty p{color:var(--demuxe-muted-foreground);margin:8px 0 24px}.emblem{display:grid;place-items:center;width:80px;height:80px;margin:0 auto 24px;border:1px solid color-mix(in srgb,var(--demuxe-foreground) 38%,transparent);border-radius:50%;font-size:32px}.empty button{background:var(--demuxe-foreground);color:var(--demuxe-background);border:0;padding:10px 24px}.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
23
+ .topbar,.controls,.transport{transition:opacity .22s}.idle .topbar,.idle .controls,.idle .transport{opacity:0;pointer-events:none}.idle .topbar *,.idle .controls *,.idle .transport *{pointer-events:none}.idle{cursor:none}.shell:not(.idle):has(:focus-visible) .topbar,.shell:not(.idle):has(:focus-visible) .controls,.shell:not(.idle):has(:focus-visible) .transport{opacity:1}.shell:not(.idle):has(:focus-visible) .topbar *,.shell:not(.idle):has(:focus-visible) .controls *,.shell:not(.idle):has(:focus-visible) .transport *{pointer-events:auto}
24
+ :host(:fullscreen){background:var(--demuxe-stage-background);width:100%;height:100%}:host(:fullscreen) .shell{height:100%;border:0;border-radius:0}:host(:fullscreen) .stage{height:100%;width:100%;max-height:none;min-height:0;aspect-ratio:auto}
25
+ @container player (max-width:600px){.stage{min-height:240px;max-height:none}.topbar{padding:6px 10px 18px}.controls{padding:12px 12px 8px}.settings{right:10px;bottom:94px;max-height:calc(100% - 108px);width:240px}.source-menu{top:50px;bottom:auto;max-height:calc(100% - 62px)}.menu-open .transport{opacity:0;visibility:hidden}.empty{padding:48px 16px}.empty strong{font-size:18px}.empty p{font-size:12px;margin:6px 0 12px}.emblem{width:44px;height:44px;font-size:23px;margin-bottom:12px}.status{bottom:100px;left:12px}.busy{top:50px;left:12px}.volume{width:42px}}
26
+ @media(pointer:coarse){.icon-button{width:44px;height:44px;min-width:44px;min-height:44px;flex-basis:44px}.settings{bottom:114px;max-height:calc(100% - 126px)}.source-menu{bottom:auto;max-height:calc(100% - 80px)}}
27
+ .settings button,.empty button{background:var(--demuxe-control-background);color:var(--demuxe-foreground);border:1px solid color-mix(in srgb,var(--demuxe-foreground) 9%,transparent);border-radius:7px;box-shadow:0 1px 2px #0003;font-weight:500}.settings button:hover,.empty button:hover{background:var(--demuxe-control-background);border-color:color-mix(in srgb,var(--demuxe-foreground) 15%,transparent)}.settings .icon-button{background:transparent;border:0;box-shadow:none;border-radius:6px;color:var(--demuxe-muted-foreground)}.settings .icon-button:hover{background:color-mix(in srgb,var(--demuxe-foreground) 3%,transparent);color:var(--demuxe-foreground)}.settings .icon-button svg{width:17px;height:17px;filter:none}.settings select:hover:not(:disabled){background:color-mix(in srgb,var(--demuxe-foreground) 2%,transparent);color:var(--demuxe-foreground)}.settings option{background:var(--demuxe-panel-background);color:var(--demuxe-foreground)}.settings input::file-selector-button{font:inherit;color:var(--demuxe-foreground);background:var(--demuxe-control-background);border:1px solid color-mix(in srgb,var(--demuxe-foreground) 8%,transparent);border-radius:6px;padding:8px 10px;margin-right:8px;cursor:pointer}.topbar .icon-button:hover,.row .icon-button:hover{background:var(--demuxe-control-background)}.row .icon-button[aria-expanded=true],.row .icon-button[aria-pressed=true]{background:var(--demuxe-control-background);color:var(--demuxe-foreground)}
28
+ /* One quiet, distinctive entry point into the empty player. */
29
+ .empty #open{display:inline-flex;align-items:center;gap:16px;min-height:62px;padding:12px 22px;background:linear-gradient(135deg,color-mix(in srgb,var(--demuxe-foreground) 3%,transparent),color-mix(in srgb,var(--demuxe-foreground) 1%,transparent));border:1px solid color-mix(in srgb,var(--demuxe-foreground) 9%,transparent);border-radius:18px;box-shadow:inset 0 1px 0 color-mix(in srgb,var(--demuxe-foreground) 3%,transparent);color:var(--demuxe-foreground);font-size:14px;font-weight:450;letter-spacing:.2px;transition:border-color .2s,background .2s,box-shadow .2s}.empty #open .open-folder{width:28px;height:28px;color:var(--demuxe-foreground)}.empty #open .open-arrow{width:18px;height:18px;color:var(--demuxe-muted-foreground);margin-left:10px;transition:transform .2s,color .2s}.empty #open:hover{background:linear-gradient(135deg,color-mix(in srgb,var(--demuxe-foreground) 6%,transparent),color-mix(in srgb,var(--demuxe-foreground) 2%,transparent));border-color:color-mix(in srgb,var(--demuxe-foreground) 19%,transparent);box-shadow:inset 0 1px 0 color-mix(in srgb,var(--demuxe-foreground) 6%,transparent),0 8px 30px #0002;color:var(--demuxe-foreground)}.empty #open:hover .open-arrow{transform:translateX(3px);color:var(--demuxe-foreground)}.empty #open:active{transform:translateY(1px)}
30
+ .diagnostics-overlay{position:absolute;z-index:3;left:clamp(12px,2.2cqw,26px);top:62px;max-width:calc(100% - 48px);max-height:calc(100% - 156px);margin:0;padding:10px 12px;overflow:auto;overscroll-behavior:contain;scrollbar-width:thin;white-space:pre-wrap;overflow-wrap:anywhere;font:11px/1.65 ui-monospace,SFMono-Regular,monospace;color:var(--demuxe-foreground);background:var(--demuxe-overlay-background);border:1px solid color-mix(in srgb,var(--demuxe-foreground) 9%,transparent);border-radius:6px;text-shadow:0 1px 3px #000c;pointer-events:auto}
31
+ @container player (max-width:600px){.diagnostics-overlay{top:52px;max-height:calc(100% - 120px);font-size:9px;line-height:1.45;padding:6px 8px}}
32
+ /* Top actions stay unboxed; motion and stroke carry interaction feedback. */
33
+ .topbar .icon-button,.topbar .icon-button:hover,.topbar .icon-button[aria-expanded=true],.topbar .icon-button[aria-pressed=true]{background:transparent;box-shadow:none}
34
+ .topbar .icon-button{color:var(--demuxe-muted-foreground);transition:color .16s,translate .2s var(--motion-ease),scale .18s var(--motion-ease)}
35
+ .topbar .icon-button:hover:not(:disabled){color:var(--demuxe-foreground);translate:0 -2px;scale:1.08}.topbar .icon-button:active:not(:disabled){translate:0 0;scale:.94}
36
+ .topbar .icon-button[aria-expanded=true],.topbar .icon-button[aria-pressed=true]{color:var(--demuxe-accent)}
37
+ .topbar .icon-button[aria-expanded=true] svg,.topbar .icon-button[aria-pressed=true] svg{stroke-width:2.5;filter:drop-shadow(0 0 5px color-mix(in srgb,var(--demuxe-accent) 20%,transparent))}
38
+ /* Top-right menus align with their triggers; volume sits under the timeline. */
39
+ .settings{top:64px;bottom:auto;max-height:calc(100% - 80px);transform-origin:top right;--menu-offset:-6px}.settings:not([hidden]){transform-origin:top right}.source-menu{top:64px}.controls .times{align-items:center;gap:12px}.controls .row{margin:0}
40
+ @container player (max-width:600px){.transport{top:42%}.settings,.source-menu{top:50px;bottom:auto;max-height:calc(100% - 62px)}}
41
+ /* Show only the existing timeline briefly after a hidden-control seek. */
42
+ .idle.seek-preview .controls{opacity:1;translate:0 0}.idle .controls .row,.idle .controls slot{visibility:hidden}.idle.seek-preview .controls,.idle.seek-preview .controls *{pointer-events:none}
43
+ /* A slim volume track inside the existing pointer target. */
44
+ .volume{appearance:none;background:transparent;height:32px;padding:0;--volume-progress:100%}
45
+ .volume::-webkit-slider-runnable-track{height:2px;border-radius:2px;background:linear-gradient(to right,var(--demuxe-foreground) var(--volume-progress),color-mix(in srgb,var(--demuxe-foreground) 19%,transparent) var(--volume-progress))}
46
+ .volume::-webkit-slider-thumb{appearance:none;width:8px;height:8px;margin-top:-3px;border:0;border-radius:50%;background:var(--demuxe-foreground);transition:scale .16s,box-shadow .16s}
47
+ .volume::-moz-range-track{height:2px;border-radius:2px;background:color-mix(in srgb,var(--demuxe-foreground) 19%,transparent)}.volume::-moz-range-progress{height:2px;border-radius:2px;background:var(--demuxe-foreground)}.volume::-moz-range-thumb{width:8px;height:8px;border:0;border-radius:50%;background:var(--demuxe-foreground);transition:scale .16s,box-shadow .16s}
48
+ .volume:hover::-webkit-slider-thumb,.volume:focus-visible::-webkit-slider-thumb{scale:1.25;box-shadow:0 0 0 3px color-mix(in srgb,var(--demuxe-foreground) 6%,transparent)}.volume:hover::-moz-range-thumb,.volume:focus-visible::-moz-range-thumb{scale:1.25;box-shadow:0 0 0 3px color-mix(in srgb,var(--demuxe-foreground) 6%,transparent)}
49
+ @media(forced-colors:active){.volume{appearance:auto}}
50
+ /* Motion stays on overlays so playback geometry never shifts. */
51
+ :host{--motion-ease:cubic-bezier(.22,1,.36,1)}
52
+ .topbar,.controls,.transport{transition:opacity .24s ease,translate .32s var(--motion-ease),scale .32s var(--motion-ease)}
53
+ .idle .topbar{translate:0 -6px}.idle .controls{translate:0 8px}.idle .transport{scale:.96}
54
+ .shell:not(.idle):has(:focus-visible) .topbar,.shell:not(.idle):has(:focus-visible) .controls{translate:0 0}.shell:not(.idle):has(:focus-visible) .transport{scale:1}
55
+ button{transition:background .16s,color .16s,border-color .16s,scale .18s var(--motion-ease)}button:active:not(:disabled){scale:.94}
56
+ .icon-button svg{transition:transform .28s var(--motion-ease)}#settings-toggle[aria-expanded=true] svg{transform:rotate(35deg)}
57
+ .settings:not([hidden]){transform-origin:top right}.settings.source-menu{transform-origin:top right;--menu-offset:-6px}
58
+
59
+
60
+ .timeline::-webkit-slider-thumb{transition:scale .16s var(--motion-ease),box-shadow .16s}.timeline:hover:not(:disabled)::-webkit-slider-thumb,.timeline:focus-visible::-webkit-slider-thumb{scale:1.2;box-shadow:0 0 0 4px color-mix(in srgb,var(--demuxe-foreground) 8%,transparent)}.timeline:active:not(:disabled)::-webkit-slider-thumb{scale:1.35}
61
+ .timeline::-moz-range-thumb{transition:scale .16s var(--motion-ease),box-shadow .16s}.timeline:hover:not(:disabled)::-moz-range-thumb,.timeline:focus-visible::-moz-range-thumb{scale:1.2;box-shadow:0 0 0 4px color-mix(in srgb,var(--demuxe-foreground) 8%,transparent)}.timeline:active:not(:disabled)::-moz-range-thumb{scale:1.35}
62
+ .empty #open{transition:border-color .2s,background .2s,box-shadow .2s,scale .18s var(--motion-ease)}.empty #open .open-folder{transition:transform .25s var(--motion-ease)}.empty #open:hover .open-folder{transform:translateY(-2px) rotate(-5deg)}
63
+ .busy{display:flex;align-items:center;gap:8px}.busy::before{content:"";width:10px;height:10px;flex:none;border:1.5px solid color-mix(in srgb,var(--demuxe-foreground) 19%,transparent);border-top-color:currentColor;border-radius:50%;animation:busy-turn .8s linear infinite}@keyframes busy-turn{to{transform:rotate(360deg)}}
64
+ /* Waiting feedback stays visible independently of playback chrome. */
65
+ .buffering-indicator{position:absolute;z-index:3;left:50%;top:50%;translate:-50% -50%;pointer-events:none;animation:buffer-appear .18s ease-out .15s backwards}
66
+ .buffering-indicator span{display:block;width:clamp(32px,4.5cqw,48px);height:clamp(32px,4.5cqw,48px);border:2px solid color-mix(in srgb,var(--demuxe-foreground) 13%,transparent);border-top-color:var(--demuxe-accent);border-right-color:color-mix(in srgb,var(--demuxe-accent) 60%,transparent);border-radius:50%;filter:drop-shadow(0 1px 4px #0008);animation:busy-turn .85s linear infinite}
67
+ .buffering .transport #play svg{opacity:0}.transport #play svg{transition:opacity .18s}
68
+ @keyframes buffer-appear{from{opacity:0;scale:.9}to{opacity:1;scale:1}}
69
+ @container player (max-width:600px){.buffering-indicator{top:42%}}
70
+ @media(forced-colors:active){.buffering-indicator span{border-color:GrayText;border-top-color:Highlight}}
71
+ /* Short stagger on reveal, coordinated fade on dismissal. */
72
+ .topbar .icon-button{transition:opacity .18s ease,translate .24s var(--motion-ease),scale .2s var(--motion-ease),color .16s;transition-delay:var(--icon-delay,0ms)}
73
+ #diagnostics-toggle{--icon-delay:0ms}#open-menu{--icon-delay:25ms}#settings-toggle{--icon-delay:50ms}#fullscreen{--icon-delay:75ms}
74
+ .idle .topbar .icon-button{opacity:0;translate:0 -4px;scale:.92;transition-delay:0ms}
75
+ .transport .icon-button{transition:opacity .18s ease,translate .26s var(--motion-ease),scale .2s var(--motion-ease),transform .15s}
76
+ .transport #back{transition-delay:25ms}.transport #forward{transition-delay:50ms}.idle .transport .icon-button{opacity:0;translate:0 5px;scale:.94;transition-delay:0ms}
77
+ .controls .timeline,.controls .times{transition:opacity .18s ease,translate .26s var(--motion-ease)}.controls .times{transition-delay:35ms}
78
+ .idle:not(.seek-preview) .controls .timeline,.idle:not(.seek-preview) .controls .times{opacity:0;translate:0 4px;transition-delay:0ms}
79
+ .settings,.diagnostics-overlay,.empty,.notice{opacity:1;translate:0 0;scale:1;transition:opacity .18s ease,translate .24s var(--motion-ease),scale .24s var(--motion-ease),display .24s allow-discrete}
80
+ .settings[hidden],.diagnostics-overlay[hidden],.empty[hidden],.notice[hidden]{opacity:0;translate:0 -5px;scale:.985;pointer-events:none}
81
+ .settings[hidden] *,.empty[hidden] *,.notice[hidden] *{pointer-events:none}
82
+ @starting-style{.settings:not([hidden]),.diagnostics-overlay:not([hidden]),.empty:not([hidden]),.notice:not([hidden]){opacity:0;translate:0 -5px;scale:.985}}
83
+ @media(prefers-reduced-motion:reduce){*,*::before,*::after{scroll-behavior:auto;transition:none!important;animation:none!important}}
84
+
85
+ @media(forced-colors:active){button,select,.shell,.settings{border:1px solid ButtonText}.transport .play{border-color:ButtonText}.stage{background:Canvas}.settings,.source-menu{background:Canvas}.timeline{appearance:auto;accent-color:Highlight}.icon-button,.time,.empty,.help{color:CanvasText}}
86
+ `;
@@ -0,0 +1,227 @@
1
+ export declare const PLAYBACK_MODES: readonly ["native", "hybrid", "software"];
2
+ export type PlaybackMode = typeof PLAYBACK_MODES[number];
3
+ export type TrackType = 'audio' | 'sub';
4
+ export type MediaInputOptions = {
5
+ demuxer?: string;
6
+ };
7
+ /** Select one rendition for the session; this is not automatic bitrate switching. */
8
+ export type StreamingOptions = {
9
+ maxBandwidth?: number;
10
+ representation?: string;
11
+ live?: boolean;
12
+ };
13
+ export type RemoteSource = MediaInputOptions & {
14
+ streaming?: StreamingOptions;
15
+ url: string;
16
+ format?: 'file' | 'hls' | 'dash';
17
+ headers?: Record<string, string>;
18
+ credentials?: RequestCredentials;
19
+ allowedOrigins?: string[];
20
+ immutable?: boolean;
21
+ refreshAuthorization?: (resource?: {
22
+ url: string;
23
+ }) => Promise<{
24
+ url?: string;
25
+ headers?: Record<string, string>;
26
+ }>;
27
+ };
28
+ export type TextTrackSource = {
29
+ src: string;
30
+ label: string;
31
+ language?: string;
32
+ default?: boolean;
33
+ };
34
+ export type SubtitleOptions = {
35
+ label?: string;
36
+ language?: string;
37
+ select?: boolean;
38
+ };
39
+ export type SubtitleAsset = {
40
+ bytes: ArrayBuffer;
41
+ format: 'srt' | 'ass' | 'ssa' | 'vtt';
42
+ label: string;
43
+ language?: string;
44
+ select: boolean;
45
+ };
46
+ export type FontAsset = {
47
+ name: string;
48
+ bytes: ArrayBuffer;
49
+ };
50
+ export type AudioOutput = 'stereo' | '5.1' | '7.1' | 'auto';
51
+ export type ToneMapping = 'off' | 'hdr-to-sdr';
52
+ /** Software decode pixels (up to 4K), and mpv's individual FFmpeg allocation cap. */
53
+ export type ResourceLimits = {
54
+ maxDecodePixels?: number;
55
+ maxAllocationBytes?: number;
56
+ };
57
+ export type PlayerOptions = {
58
+ /** Package runtime root; includes web/ and fixtures/. Same-origin only. */
59
+ assetBase?: string;
60
+ audioOutput?: AudioOutput;
61
+ audioFallback?: 'stereo' | 'reject';
62
+ toneMapping?: ToneMapping;
63
+ resourceLimits?: ResourceLimits;
64
+ mode?: PlaybackMode;
65
+ /** Defaults to true when mode is omitted. Explicit modes remain pinned. */
66
+ automaticSelection?: boolean;
67
+ /** Internal Native packaging plan; "never" disables the packet-copy fallback. */
68
+ /** Opt-in retained-session seeks; broad automatic admission remains gated. */
69
+ experimentalBufferedNativeSeeks?: boolean;
70
+ /** Explicit Native trials only; copy original audio first, then qualified integer FLAC. */
71
+ experimentalAudioAdaptation?: 'flac' | 'opus';
72
+ /** Permit automatic FLAC for the qualified local-file subset; never permits lossy conversion. */
73
+ automaticAudioAdaptation?: 'lossless';
74
+ /** Explicit lossy permission; does not authorize resampling or downmixing. */
75
+ allowLossyAudio?: boolean;
76
+ /** External ASS/SSA overlay on qualified Native presentations; no embedded extraction. */
77
+ experimentalNativeASS?: boolean;
78
+ nativeRemux?: 'auto' | 'never' | 'always';
79
+ /** Optional Software presenter; RGB remains the default. */
80
+ softwarePresenter?: 'rgb' | 'experimental-yuv';
81
+ width?: number;
82
+ height?: number;
83
+ videoFilters?: string;
84
+ /** Experimental scalar attenuation, 0..1; 1 avoids Native Web Audio allocation. */
85
+ audioGain?: number;
86
+ audioFilters?: string;
87
+ /** Opt-in scalar audio filtering on Hybrid; other filters retain Software routing. */
88
+ experimentalHybridAudioFilters?: boolean;
89
+ };
90
+ export type Capabilities = {
91
+ videoFilters: boolean;
92
+ audioFilters: boolean;
93
+ mpvSubtitles: boolean;
94
+ externalTextTracks: boolean;
95
+ externalSubtitles: boolean;
96
+ customFonts: boolean;
97
+ customRequestHeaders: boolean;
98
+ };
99
+ export type PlaybackEvent = {
100
+ event: string;
101
+ name?: string;
102
+ data?: unknown;
103
+ [key: string]: unknown;
104
+ };
105
+ export type Diagnostics = {
106
+ planAdmission?: Array<{
107
+ id: string;
108
+ mode: PlaybackMode;
109
+ eligible: boolean;
110
+ code?: string;
111
+ reason?: string;
112
+ }>;
113
+ plan?: {
114
+ id: string;
115
+ mode: PlaybackMode;
116
+ video: string;
117
+ audio: string;
118
+ qualification: string;
119
+ };
120
+ mode: PlaybackMode;
121
+ switching: boolean;
122
+ videoFilters: string;
123
+ audioFilters: string;
124
+ audioGain?: number;
125
+ toneMapping?: ToneMapping;
126
+ resourceLimits?: ResourceLimits;
127
+ selection?: {
128
+ automatic: boolean;
129
+ attempts: Array<{
130
+ mode: PlaybackMode | 'probe';
131
+ outcome: 'skipped' | 'failed' | 'selected';
132
+ reason: string;
133
+ }>;
134
+ };
135
+ backend?: Record<string, unknown>;
136
+ };
137
+ export type OpenOptions = MediaInputOptions & {
138
+ signal?: AbortSignal;
139
+ };
140
+ export type MediaSourceInput = File | ArrayBuffer | string | URL | RemoteSource;
141
+ export type OperationKind = 'opening' | 'seeking' | 'switching' | 'closing';
142
+ export type PendingOperation = Readonly<{
143
+ id: number;
144
+ kind: OperationKind;
145
+ }>;
146
+ export type PlayerErrorCode = 'INVALID_ARGUMENT' | 'ABORTED' | 'AUTOPLAY_BLOCKED' | 'SOURCE_PERMISSION' | 'SOURCE_CHANGED' | 'NETWORK_TIMEOUT' | 'UNSUPPORTED_MEDIA' | 'UNSUPPORTED_TIMELINE' | 'UNSUPPORTED_FEATURE' | 'ASSET_LOAD_FAILED' | 'ISOLATION_REQUIRED' | 'DECODE_FAILED';
147
+ export type SessionError = Readonly<{
148
+ code: PlayerErrorCode;
149
+ message: string;
150
+ operationId: number | null;
151
+ operation: OperationKind | null;
152
+ scope: 'operation' | 'session';
153
+ retryable: boolean;
154
+ }>;
155
+ export type TimeRange = Readonly<{
156
+ start: number;
157
+ end: number;
158
+ }>;
159
+ export type FeatureAvailability = Readonly<{
160
+ availability: 'available';
161
+ } | {
162
+ availability: 'switch';
163
+ mode: PlaybackMode;
164
+ reason: string;
165
+ } | {
166
+ availability: 'unavailable';
167
+ reason: string;
168
+ } | {
169
+ availability: 'unknown';
170
+ reason: string;
171
+ }>;
172
+ export type FeatureName = 'seek' | 'audioTracks' | 'subtitleTracks' | 'externalSubtitles' | 'customFonts' | 'videoFilters' | 'audioFilters' | 'audioGain';
173
+ export type PlayerCapabilities = Readonly<Capabilities & {
174
+ deployment: Readonly<{
175
+ isolated: boolean;
176
+ webCodecs: boolean;
177
+ mediaSource: boolean;
178
+ }>;
179
+ features: Readonly<Record<FeatureName, FeatureAvailability>>;
180
+ }>;
181
+ export type MediaTrack = Readonly<{
182
+ id: string;
183
+ type: 'audio' | 'subtitle' | 'video';
184
+ label: string;
185
+ language: string | null;
186
+ codec: string | null;
187
+ selected: boolean;
188
+ external: boolean;
189
+ }>;
190
+ export type MediaInfo = Readonly<{
191
+ displayWidth: number | null;
192
+ displayHeight: number | null;
193
+ aspectRatio: number | null;
194
+ rotation: number | null;
195
+ video: MediaTrack | null;
196
+ audio: MediaTrack | null;
197
+ subtitle: MediaTrack | null;
198
+ }>;
199
+ export type PlayerState = Readonly<{
200
+ status: 'idle' | 'paused' | 'playing' | 'buffering' | 'ended' | 'error';
201
+ playbackIntent: 'play' | 'pause';
202
+ pendingOperation: PendingOperation | null;
203
+ sourceId: number | null;
204
+ currentTime: number;
205
+ duration: number | null;
206
+ streamType: 'unknown' | 'vod' | 'live';
207
+ subtitlesVisible: boolean;
208
+ volume: number;
209
+ muted: boolean;
210
+ playbackRate: number;
211
+ activeMode: PlaybackMode | null;
212
+ automaticSelection: boolean;
213
+ buffered: readonly TimeRange[] | null;
214
+ seekable: readonly TimeRange[] | null;
215
+ audioTracks: readonly MediaTrack[];
216
+ subtitleTracks: readonly MediaTrack[];
217
+ mediaInfo: MediaInfo;
218
+ capabilities: PlayerCapabilities;
219
+ error: SessionError | null;
220
+ }>;
221
+ export declare const PLAYER_EVENTS: readonly ["play", "playing", "pause", "waiting", "ended", "timeupdate", "durationchange", "seeking", "seeked", "volumechange", "ratechange", "trackschange", "capabilitieschange", "sourcechange", "statechange", "error"];
222
+ export type PlayerEventName = typeof PLAYER_EVENTS[number];
223
+ export type PlayerEventMap = {
224
+ [K in Exclude<PlayerEventName, 'error'>]: CustomEvent<PlayerState>;
225
+ } & {
226
+ error: CustomEvent<SessionError>;
227
+ };
@@ -0,0 +1,2 @@
1
+ export const PLAYBACK_MODES = Object.freeze(['native', 'hybrid', 'software']);
2
+ export const PLAYER_EVENTS = Object.freeze(['play', 'playing', 'pause', 'waiting', 'ended', 'timeupdate', 'durationchange', 'seeking', 'seeked', 'volumechange', 'ratechange', 'trackschange', 'capabilitieschange', 'sourcechange', 'statechange', 'error']);
@@ -0,0 +1,129 @@
1
+ import type { PlayerState, PlayerEventMap, PlayerCapabilities, OpenOptions, MediaSourceInput } from './types.js';
2
+ import type { ToneMapping, SubtitleOptions, PlaybackMode, PlayerOptions, RemoteSource, TextTrackSource, Diagnostics, TrackType } from './types.js';
3
+ /** Three explicit playback modes. Mode/filter changes reopen transactionally. */
4
+ export declare class Player extends EventTarget {
5
+ readonly ready: Promise<void>;
6
+ private assetBase;
7
+ private snapshot;
8
+ private subscribers;
9
+ private publishQueued;
10
+ private sourceSerial;
11
+ private publicSelections;
12
+ private operationSerial;
13
+ private operationEpoch;
14
+ private activeOperation?;
15
+ private pendingOperation;
16
+ private sessionError;
17
+ private observedPlaying;
18
+ private observedWaiting;
19
+ private muted;
20
+ private closing?;
21
+ private currentMode;
22
+ private automatic;
23
+ private attempts;
24
+ private inspection?;
25
+ private recovering;
26
+ private lifetime;
27
+ private recoveredSessions;
28
+ private audioAdaptation?;
29
+ private automaticLossless;
30
+ private losslessInspection?;
31
+ private bufferedNativeSeeks;
32
+ private hybridAudioFilters;
33
+ private nativeASS;
34
+ private allowLossy;
35
+ private planDecisions;
36
+ private admissionContext;
37
+ private nativeRemux;
38
+ private softwarePresenter;
39
+ private settings;
40
+ private audioOutput;
41
+ private audioFallback;
42
+ private toneMapping;
43
+ private resourceLimits;
44
+ private fonts;
45
+ private subtitleAssets;
46
+ private root;
47
+ private width;
48
+ private height;
49
+ private current?;
50
+ private candidate?;
51
+ private source?;
52
+ private nativeTracks;
53
+ private queue;
54
+ private queued;
55
+ private destroyed;
56
+ private destruction?;
57
+ private busy;
58
+ private empty;
59
+ private monitor?;
60
+ constructor(container: HTMLElement, options?: PlayerOptions);
61
+ get state(): PlayerState;
62
+ get mediaInfo(): Readonly<{
63
+ displayWidth: number | null;
64
+ displayHeight: number | null;
65
+ aspectRatio: number | null;
66
+ rotation: number | null;
67
+ video: import("./types.js").MediaTrack | null;
68
+ audio: import("./types.js").MediaTrack | null;
69
+ subtitle: import("./types.js").MediaTrack | null;
70
+ }>;
71
+ subscribe(listener: (state: PlayerState) => void): () => void;
72
+ addEventListener<K extends keyof PlayerEventMap>(type: K, listener: ((this: Player, ev: PlayerEventMap[K]) => any) | null, options?: boolean | AddEventListenerOptions): void;
73
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions): void;
74
+ removeEventListener<K extends keyof PlayerEventMap>(type: K, listener: ((this: Player, ev: PlayerEventMap[K]) => any) | null, options?: boolean | EventListenerOptions): void;
75
+ removeEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions): void;
76
+ private schedulePublish;
77
+ private publish;
78
+ private featureCapabilities;
79
+ get mode(): "native" | "hybrid" | "software";
80
+ get automaticSelection(): boolean;
81
+ get surface(): HTMLCanvasElement | HTMLVideoElement | undefined;
82
+ get properties(): ReadonlyMap<string, unknown>;
83
+ get capabilities(): PlayerCapabilities;
84
+ private get legacyCapabilities();
85
+ get diagnostics(): Diagnostics;
86
+ audioDiagnostics(): object | undefined;
87
+ private emit;
88
+ private assertOperation;
89
+ private validateFilters;
90
+ private enqueue;
91
+ private interruptible;
92
+ private dispose;
93
+ private create;
94
+ private settled;
95
+ private admissible;
96
+ private replace;
97
+ private record;
98
+ private select;
99
+ private recover;
100
+ setAutomaticSelection(enabled?: boolean): Promise<void>;
101
+ open(input: MediaSourceInput, options?: OpenOptions): Promise<void>;
102
+ openRemote(source: RemoteSource, options?: OpenOptions): Promise<void>;
103
+ setMode(mode: PlaybackMode): Promise<void>;
104
+ private filters;
105
+ setVideoFilters(value: string): Promise<void>;
106
+ setAudioFilters(value: string): Promise<void>;
107
+ setAudioGain(value: number): Promise<void>;
108
+ private setting;
109
+ play(): Promise<void>;
110
+ pause(): Promise<void>;
111
+ seek(seconds: number): Promise<void>;
112
+ volume(value: number): Promise<void>;
113
+ setVolume(value: number): Promise<void>;
114
+ setMuted(value: boolean): Promise<void>;
115
+ setPlaybackRate(value: number): Promise<void>;
116
+ selectAudioTrack(id: string | null): Promise<void>;
117
+ selectSubtitleTrack(id: string | null): Promise<void>;
118
+ private selectPublicTrack;
119
+ rate(value: number): Promise<void>;
120
+ selectTrack(type: TrackType, id: string): Promise<void>;
121
+ subtitleVisible(visible: boolean): Promise<void>;
122
+ addSubtitle(file: File, options?: SubtitleOptions): Promise<void>;
123
+ addFont(file: File): Promise<void>;
124
+ setToneMapping(value: ToneMapping): Promise<void>;
125
+ addTextTrack(track: TextTrackSource): Promise<void>;
126
+ resize(width: number, height: number): void;
127
+ close(): Promise<void>;
128
+ destroy(): Promise<void>;
129
+ }