myetv-player 1.7.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/css/myetv-player.css +36 -0
- package/css/myetv-player.min.css +1 -1
- package/dist/myetv-player.js +24 -7
- package/dist/myetv-player.min.js +24 -7
- package/package.json +2 -1
- package/plugins/autosub/myetv-autosub-plugin.js +323 -114
- package/plugins/autosub/readme.md +109 -13
package/README.md
CHANGED
|
@@ -143,6 +143,7 @@ const player = new MYETVvideoplayer('my-video', {
|
|
|
143
143
|
| `brandLogoUrl` | string | `''` | Brand logo url in the controlbar (png, jpg, gif) - image height 44px - image width 120px |
|
|
144
144
|
| `brandLogoLinkUrl` | string | `''` | Optional URL to open in a new page when clicking the brand logo in the controlbar
|
|
145
145
|
| `brandLogoTooltipText` | string | `''` | Optional Custom tooltip of the brand logo (the default is the url of the brand logo, if present)
|
|
146
|
+
| `loadingLogo` | string | `''` | Optional Custom image logo url to show it inside the loading spinner
|
|
146
147
|
| `watermarkUrl` | string | `''` | Optional URL of the image watermark over the video, reccomended dimension: width: 180px, height: 100px
|
|
147
148
|
| `watermarkLink` | string | `''` | Optional URL to open in a new page when clicking the watermark logo in the video
|
|
148
149
|
| `watermarkPosition` | string | `''` | Optional where to show the watermark logo in the video (values are: top-left, top-right, bottom-left, bottom-right)
|
package/css/myetv-player.css
CHANGED
|
@@ -388,6 +388,42 @@ body {
|
|
|
388
388
|
animation: spin 1s linear infinite;
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
.loading-spinner-wrap {
|
|
392
|
+
position: relative;
|
|
393
|
+
width: 56px;
|
|
394
|
+
height: 56px;
|
|
395
|
+
display: flex;
|
|
396
|
+
align-items: center;
|
|
397
|
+
justify-content: center;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.loading-spinner-wrap .loading-spinner {
|
|
401
|
+
position: absolute;
|
|
402
|
+
top: 0;
|
|
403
|
+
left: 0;
|
|
404
|
+
width: 100%;
|
|
405
|
+
height: 100%;
|
|
406
|
+
border-radius: 50%;
|
|
407
|
+
border: 3px solid rgba(255, 255, 255, 0.2);
|
|
408
|
+
border-top: 3px solid var(--player-primary-color);
|
|
409
|
+
animation: spin 1s linear infinite;
|
|
410
|
+
box-sizing: border-box;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.loading-spinner-logo {
|
|
414
|
+
position: relative;
|
|
415
|
+
z-index: 1;
|
|
416
|
+
max-width: 34px;
|
|
417
|
+
max-height: 34px;
|
|
418
|
+
width: auto;
|
|
419
|
+
height: auto;
|
|
420
|
+
border-radius: 0;
|
|
421
|
+
object-fit: contain;
|
|
422
|
+
display: block;
|
|
423
|
+
flex-shrink: 0;
|
|
424
|
+
pointer-events: none;
|
|
425
|
+
}
|
|
426
|
+
|
|
391
427
|
@keyframes spin {
|
|
392
428
|
0% {
|
|
393
429
|
transform: rotate(0deg);
|
package/css/myetv-player.min.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--player-primary-color: goldenrod;--player-primary-hover: #daa520;--player-primary-dark: #b8860b;--player-button-color: white;--player-button-hover: rgba(255, 255, 255, 0.1);--player-button-active: rgba(255, 255, 255, 0.2);--player-text-color: white;--player-text-secondary: rgba(255, 255, 255, 0.8);--player-bg-primary: #000;--player-bg-controls: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.8) 100%);--player-bg-title-overlay: linear-gradient(180deg, rgba(0, 0, 0, 0.8) 0%, transparent 100%);--player-bg-menu: rgba(20, 20, 20, 0.95);--player-bg-loading: rgba(0, 0, 0, 0.7);--player-border-radius: 12px;--player-controls-padding: 20px 15px 15px;--player-title-overlay-padding: 15px 20px 25px;--player-button-padding: 8px;--player-icon-size: 20px;--player-progress-height: 6px;--player-progress-bg: rgba(255, 255, 255, 0.2);--player-progress-buffer: rgba(255, 255, 255, 0.3);--player-progress-handle-size: 16px;--player-volume-height: 4px;--player-volume-bg: rgba(255, 255, 255, 0.2);--player-volume-handle-size: 14px;--player-volume-fill: 100%;--player-transition-fast: 0.2s ease;--player-transition-normal: 0.3s ease;--player-shadow-main: 0 8px 32px rgba(0, 0, 0, 0.3);--player-shadow-menu: 0 4px 16px rgba(0, 0, 0, 0.2);--player-shadow-tooltip: 0 3px 12px rgba(0, 0, 0, 0.4)}*{box-sizing:border-box}body{margin:0;padding:20px;background:#1a1a1a;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif}.video-container{max-width:1200px;margin:0 auto;background:var(--player-bg-primary);border-radius:var(--player-border-radius);box-shadow:var(--player-shadow-main);position:relative}.video-container:fullscreen,.video-container:-webkit-full-screen,.video-container:-moz-full-screen{width:100vw;height:100vh;border-radius:0}.video-wrapper{position:relative;width:100%;background:var(--player-bg-primary);overflow:visible !important}.video-wrapper.player-initialized .video-player{visibility:visible;opacity:1;transition:opacity .3s ease;pointer-events:auto}.video-wrapper.player-initialized .initial-loading{opacity:0;visibility:hidden;transition:opacity .3s ease,visibility .3s ease;transition-delay:.2s;display:none;transition-delay:.5s}.video-wrapper:not(.has-controls) .video-watermark.hide-on-autohide{visibility:hidden;opacity:0}.hidden{display:none !important}.player-theme-dark{--player-bg-primary: #1a1a1a;--player-bg-controls: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.9) 100%);--player-bg-title-overlay: linear-gradient(180deg, rgba(30, 30, 30, 0.9) 0%, transparent 100%);--player-bg-menu: rgba(30, 30, 30, 0.95)}.video-player{width:100%;height:auto;display:block;min-height:300px;-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden;will-change:transform;visibility:visible;opacity:1;position:relative;z-index:1;transition:none}.video-wrapper.player-initialized .video-player{visibility:visible;opacity:1;transition:opacity .3s ease;pointer-events:auto}.video-player::-webkit-media-controls-panel,.video-player::-webkit-media-controls-play-button,.video-player::-webkit-media-controls-start-playback-button,.video-player::-webkit-media-controls-timeline,.video-player::-webkit-media-controls-current-time-display,.video-player::-webkit-media-controls-time-remaining-display,.video-player::-webkit-media-controls-mute-button,.video-player::-webkit-media-controls-toggle-closed-captions-button,.video-player::-webkit-media-controls-volume-slider,.video-player::-webkit-media-controls-fullscreen-button,.video-player::-webkit-media-controls-seek-back-button,.video-player::-webkit-media-controls-seek-forward-button,.video-player::-webkit-media-controls-rewind-button,.video-player::-webkit-media-controls-return-to-realtime-button,.video-player::-webkit-media-controls-overlay-play-button{display:none !important;visibility:hidden !important;opacity:0 !important}.video-player::-moz-media-controls{display:none !important}.initial-loading{position:absolute;top:0;left:0;width:100%;height:100%;background:var(--player-bg-primary);display:flex;align-items:center;justify-content:center;z-index:20}.video-wrapper.player-initialized .initial-loading{opacity:0;visibility:hidden;transition:opacity .3s ease,visibility .3s ease;transition-delay:.2s}.video-wrapper.player-initialized .initial-loading{display:none;transition-delay:.5s}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:var(--player-bg-loading);display:flex;align-items:center;justify-content:center;opacity:0;visibility:hidden;transition:opacity var(--player-transition-normal);z-index:15}.loading-overlay.active{opacity:1;visibility:visible}.loading-spinner{width:50px;height:50px;border:3px solid hsla(0,0%,100%,.3);border-top:3px solid var(--player-primary-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.video-player::cue{background:rgba(0,0,0,.8);color:#fff;font-size:18px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;font-weight:500;text-shadow:2px 2px 4px rgba(0,0,0,.8);padding:8px 12px;border-radius:6px;line-height:1.4}.video-player::cue(.highlight){background:var(--player-primary-color);color:#000}.video-player::cue(b){font-weight:700}.video-player::cue(i){font-style:italic}.video-poster-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-size:cover;background-position:center;background-repeat:no-repeat;z-index:1;cursor:pointer;opacity:0;visibility:hidden;transition:opacity .3s ease,visibility .3s ease;pointer-events:none}.video-poster-overlay.visible{opacity:1;visibility:visible;pointer-events:auto}.video-poster-overlay.hidden{opacity:0;visibility:hidden;pointer-events:none}.video-poster-overlay.visible:hover{opacity:.95}.video-poster-overlay::after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:80px;height:80px;background:rgba(0,0,0,.7);border-radius:50%;border:3px solid var(--player-primary-color);opacity:0;transition:opacity .3s ease,transform .3s ease,border-color .3s ease;pointer-events:none}.video-poster-overlay.visible:hover::after{opacity:1;transform:translate(-50%, -50%) scale(1.1);border-color:var(--player-primary-hover);box-shadow:0 0 20px var(--player-primary-color)}.video-poster-overlay::before{content:"";position:absolute;top:50%;left:50%;transform:translate(-40%, -50%);width:0;height:0;border-style:solid;border-width:15px 0 15px 25px;border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) var(--player-primary-color);z-index:2;opacity:0;transition:opacity .3s ease,border-color .3s ease;pointer-events:none}.video-poster-overlay.visible:hover::before{opacity:1;border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) var(--player-primary-hover)}.initial-loading{position:absolute;top:0;left:0;width:100%;height:100%;background:var(--player-bg-primary);display:flex;align-items:center;justify-content:center;z-index:20;flex-direction:column;gap:15px}.video-wrapper.player-initialized .initial-loading{opacity:0;visibility:hidden;transition:opacity .3s ease,visibility .3s ease;transition-delay:.2s}.video-wrapper.player-initialized .initial-loading{display:none;transition-delay:.5s}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:var(--player-bg-loading);display:flex;align-items:center;justify-content:center;opacity:0;visibility:hidden;transition:opacity var(--player-transition-normal);z-index:15;flex-direction:column;gap:15px}.loading-overlay.active{opacity:1;visibility:visible}.loading-spinner{width:50px;height:50px;border:3px solid hsla(0,0%,100%,.3);border-top:3px solid var(--player-primary-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.loading-text{color:var(--player-text-color);font-size:14px;font-weight:500;text-shadow:0 2px 4px rgba(0,0,0,.5);letter-spacing:.5px;margin-top:10px;text-align:center}.title-overlay{position:absolute;top:0;left:0;right:0;background:var(--player-bg-title-overlay);padding:var(--player-title-overlay-padding);opacity:0;transform:translateY(-100%);transition:all var(--player-transition-normal);z-index:15;pointer-events:none}.title-overlay.show{opacity:1;transform:translateY(0)}.title-overlay.show.persistent{opacity:1;transform:translateY(0)}.title-text{color:var(--player-text-color);font-size:18px;font-weight:600;line-height:1.3;margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:0 2px 8px rgba(0,0,0,.7);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.player-top-bar{position:absolute;top:0;left:0;right:0;display:flex;justify-content:space-between;align-items:flex-start;padding:15px 20px;background:linear-gradient(to bottom, rgba(0, 0, 0, var(--player-topbar-opacity, 0.95)) 0%, rgba(0, 0, 0, calc(var(--player-topbar-opacity, 0.95) * 0.68)) 50%, rgba(0, 0, 0, 0) 100%);backdrop-filter:blur(5px);z-index:20;opacity:0;transform:translateY(-100%);transition:all .3s ease;pointer-events:none}.player-top-bar.no-title-background{background:rgba(0,0,0,0);backdrop-filter:none}.player-top-bar .top-bar-title{flex:1;margin-right:20px;pointer-events:none;min-width:0;max-width:calc(100% - 80px)}.player-top-bar .top-bar-title .video-title{color:#fff;font-size:18px;font-weight:600;margin:0 0 4px 0;text-shadow:0 2px 4px rgba(0,0,0,.8);line-height:1.3;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.player-top-bar .top-bar-title .video-subtitle{color:hsla(0,0%,100%,.8);font-size:14px;text-shadow:0 1px 3px rgba(0,0,0,.8);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.player-top-bar .top-bar-spacer{flex:1}.player-top-bar .settings-control{pointer-events:all;position:relative;flex-shrink:0}.player-top-bar .settings-control .settings-btn{background:rgba(0,0,0,.6);backdrop-filter:blur(10px);padding:10px;border-radius:50%;border:1px solid hsla(0,0%,100%,.15);transition:all .3s ease;cursor:pointer}.player-top-bar .settings-control .settings-btn:hover,.player-top-bar .settings-control .settings-btn.active{background:rgba(0,0,0,.9);border-color:hsla(0,0%,100%,.3);transform:rotate(90deg)}.player-top-bar .settings-control .settings-btn .icon svg{display:block}.player-top-bar .settings-control .settings-menu{position:absolute;top:calc(100% + 10px);right:0;min-width:240px;max-width:320px;background:rgba(28,28,28,.98);backdrop-filter:blur(20px);border-radius:8px;border:1px solid hsla(0,0%,100%,.1);box-shadow:0 8px 32px rgba(0,0,0,.6);opacity:0;visibility:hidden;transform:translateY(-10px);transition:all .3s ease;max-height:600px !important;min-height:200px;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:hsla(0,0%,100%,.3) rgba(0,0,0,0);display:flex;flex-direction:column}.player-top-bar .settings-control .settings-menu::-webkit-scrollbar{width:6px}.player-top-bar .settings-control .settings-menu::-webkit-scrollbar-track{background:hsla(0,0%,100%,.05);border-radius:3px}.player-top-bar .settings-control .settings-menu::-webkit-scrollbar-thumb{background:hsla(0,0%,100%,.3);border-radius:3px}.player-top-bar .settings-control .settings-menu::-webkit-scrollbar-thumb:hover{background:hsla(0,0%,100%,.5)}.player-top-bar .settings-control .settings-menu.active{opacity:1;visibility:visible;transform:translateY(0)}.settings-option[data-action=moreinfo]{order:-1;border-bottom:1px solid hsla(0,0%,100%,.1);padding-bottom:12px;margin-bottom:8px}.settings-expandable-wrapper{border-bottom:1px solid hsla(0,0%,100%,.05)}.settings-expandable-wrapper:last-child{border-bottom:none}.settings-option{padding:12px 16px;cursor:pointer;transition:background .2s ease;display:flex;justify-content:space-between;align-items:center;color:#fff;user-select:none}.settings-option:hover{background:hsla(0,0%,100%,.1)}.settings-option .settings-option-label{flex:1;color:#fff;font-size:14px;font-weight:400}.settings-option .settings-option-label strong{font-weight:600;margin-left:4px}.settings-option .expand-arrow{color:hsla(0,0%,100%,.6);font-size:12px;transition:transform .3s ease;margin-left:10px;line-height:1}.settings-expandable-content{background:rgba(0,0,0,.3);max-height:250px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:hsla(0,0%,100%,.2) rgba(0,0,0,0)}.settings-expandable-content::-webkit-scrollbar{width:4px}.settings-expandable-content::-webkit-scrollbar-thumb{background:hsla(0,0%,100%,.2);border-radius:2px}.settings-expandable-content::-webkit-scrollbar-thumb:hover{background:hsla(0,0%,100%,.3)}.settings-suboption{padding:10px 20px 10px 32px;cursor:pointer;transition:background .2s ease;color:hsla(0,0%,100%,.8);font-size:13px;user-select:none}.settings-suboption:hover{background:hsla(0,0%,100%,.08);color:hsla(0,0%,100%,.95)}.settings-suboption.active{background:hsla(0,0%,100%,.15);color:#fff;font-weight:500;position:relative}.settings-suboption.active::before{content:"✓";position:absolute;left:12px;color:var(--player-primary-color, #ff0000);font-weight:700}body .video-wrapper:hover .player-top-bar,body .video-wrapper .player-top-bar{opacity:0 !important;transform:translateY(-100%) !important;transition:all .3s ease !important}body .video-wrapper.has-controls .player-top-bar{opacity:1 !important;transform:translateY(0) !important}body .video-wrapper .player-top-bar.persistent{opacity:1 !important;transform:translateY(0) !important}@media(max-width: 768px){.player-top-bar{padding:12px 15px}.player-top-bar .top-bar-title{margin-right:15px;max-width:calc(100% - 70px)}.player-top-bar .top-bar-title .video-title{font-size:16px}.player-top-bar .top-bar-title .video-subtitle{font-size:13px}.player-top-bar .settings-control .settings-btn{padding:8px}.player-top-bar .settings-control .settings-btn .icon svg{width:18px;height:18px}.player-top-bar .settings-control .settings-menu{min-width:200px;max-height:400px}}@media(max-width: 480px){.player-top-bar{padding:10px 12px}.player-top-bar .top-bar-title{margin-right:10px;max-width:calc(100% - 60px)}.player-top-bar .top-bar-title .video-title{font-size:14px}.player-top-bar .top-bar-title .video-subtitle{font-size:12px}.player-top-bar .settings-control .settings-btn{padding:6px}.player-top-bar .settings-control .settings-btn .icon svg{width:16px;height:16px}.player-top-bar .settings-control .settings-menu{min-width:180px;max-height:300px}}.video-wrapper:fullscreen .player-top-bar,.video-wrapper:-webkit-full-screen .player-top-bar,.video-wrapper:-moz-full-screen .player-top-bar{padding:20px 30px}.video-wrapper:fullscreen .player-top-bar .top-bar-title .video-title,.video-wrapper:-webkit-full-screen .player-top-bar .top-bar-title .video-title,.video-wrapper:-moz-full-screen .player-top-bar .top-bar-title .video-title{font-size:22px}.video-wrapper:fullscreen .player-top-bar .settings-control .settings-btn,.video-wrapper:-webkit-full-screen .player-top-bar .settings-control .settings-btn,.video-wrapper:-moz-full-screen .player-top-bar .settings-control .settings-btn{padding:12px}.title-text{color:var(--player-text-color);font-size:18px;font-weight:600;line-height:1.3;margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:0 2px 8px rgba(0,0,0,.7);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.subtitle-text{color:var(--player-text-color);font-size:14px;font-weight:400;line-height:1.3;margin:5px 0 0 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:0 2px 8px rgba(0,0,0,.7);opacity:.9;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.chapter-name{font-size:13px;font-weight:500;color:hsla(0,0%,100%,.9);margin-top:6px;max-width:400px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:opacity .3s}.controls{position:absolute;bottom:0;left:0;right:0;background:rgba(0, 0, 0, var(--control-bar-opacity, 0.95));padding:var(--player-controls-padding);opacity:0;transform:translateY(100%);transition:all var(--player-transition-normal);z-index:10;min-height:70px !important;box-sizing:border-box}.controls.show{opacity:1;transform:translateY(0);position:absolute !important;bottom:0 !important;z-index:20 !important}.play-icon svg,.pause-icon svg,.volume-icon svg,.mute-icon svg,.playlist-prev-btn .icon svg,.playlist-next-btn .icon svg,.subtitles-btn .icon svg,.fullscreen-icon svg,.exit-fullscreen-icon svg,.pip-icon svg,.pip-exit-icon svg{width:16px;height:16px;display:block}.controls-main{display:flex;justify-content:space-between;align-items:center;width:100%;min-height:44px !important;flex-shrink:0}.controls-left,.controls-right{display:flex;align-items:center;gap:8px;flex-shrink:1;min-width:0}.control-btn{background:none;border:none;color:var(--player-button-color);cursor:pointer;padding:var(--player-button-padding);border-radius:6px;display:flex;align-items:center;justify-content:center;gap:5px;transition:all var(--player-transition-fast);font-size:14px;font-weight:500;position:relative;flex-shrink:1;min-width:0;white-space:nowrap;vertical-align:middle}.control-btn:hover{background:var(--player-button-hover);transform:scale(1.05)}.control-btn:active{transform:scale(0.95);background:var(--player-button-active)}.subtitles-btn{position:relative}.quality-btn{min-height:36px;padding:6px 8px}.quality-btn-text{display:flex;flex-direction:column;align-items:center;line-height:1}.selected-quality{font-size:14px;font-weight:500;color:var(--player-button-color)}.current-quality{font-size:10px;font-weight:400;color:var(--player-text-secondary);opacity:.8;margin-top:2px;line-height:1}.time-display{color:var(--player-text-color);font-size:14px;font-weight:500;display:flex;flex-direction:column;align-items:center;justify-content:center;line-height:1.1;gap:0;font-variant-numeric:tabular-nums;flex-shrink:2;min-width:0;margin:0 5px}.time-display .duration{font-size:10px;color:var(--player-text-secondary);opacity:.8;font-weight:400}.icon{width:var(--player-icon-size);height:var(--player-icon-size);display:flex;align-items:center;justify-content:center;font-size:16px}.hidden{display:none !important}.controls-right .brand-logo{height:44px;max-width:120px;object-fit:contain;margin-right:10px;pointer-events:auto;opacity:.8;transition:opacity var(--player-transition-fast);order:-1;flex-shrink:1}.controls-right .brand-logo:hover{opacity:1}.controls-right .brand-logo-link{order:-1;margin-right:10px;display:inline-block;text-decoration:none}.controls-right .brand-logo-link .brand-logo{margin-right:0}.video-wrapper.hide-cursor{cursor:none !important}.video-wrapper.hide-cursor .controls{cursor:default !important}.video-wrapper.hide-cursor .control-btn{cursor:pointer !important}.video-wrapper.hide-cursor iframe{cursor:auto !important;pointer-events:auto !important}.play-from-start-btn .restart-icon{display:inline-flex;align-items:center;justify-content:center}.control-btn .icon{display:inline-flex;align-items:center;justify-content:center}.moreinfo-modal-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.85);display:flex;align-items:center;justify-content:center;z-index:10000;backdrop-filter:blur(5px)}.moreinfo-modal-content{background:linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);border-radius:12px;max-width:600px;width:90%;max-height:80%;display:flex;flex-direction:column;box-shadow:0 10px 40px rgba(0,0,0,.5);border:1px solid hsla(0,0%,100%,.1)}.moreinfo-modal-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;border-bottom:1px solid hsla(0,0%,100%,.1)}.moreinfo-modal-title{margin:0;font-size:20px;font-weight:600;color:#fff;flex:1}.moreinfo-modal-close{background:rgba(0,0,0,0);border:none;color:#fff;font-size:32px;cursor:pointer;padding:0;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:background-color .2s ease;line-height:1}.moreinfo-modal-close:hover{background:hsla(0,0%,100%,.1)}.moreinfo-modal-body{padding:24px;overflow-y:auto;overflow-x:hidden;flex:1;color:#e0e0e0;font-size:14px;line-height:1.6}.moreinfo-modal-body::-webkit-scrollbar{width:8px}.moreinfo-modal-body::-webkit-scrollbar-track{background:hsla(0,0%,100%,.05);border-radius:4px}.moreinfo-modal-body::-webkit-scrollbar-thumb{background:hsla(0,0%,100%,.2);border-radius:4px}.moreinfo-modal-body::-webkit-scrollbar-thumb:hover{background:hsla(0,0%,100%,.3)}.moreinfo-modal-body{scrollbar-width:thin;scrollbar-color:hsla(0,0%,100%,.2) hsla(0,0%,100%,.05)}@media(max-width: 600px){.moreinfo-modal-content{width:95%;max-height:85%}.moreinfo-modal-header{padding:16px 20px}.moreinfo-modal-title{font-size:18px}.moreinfo-modal-body{padding:20px;font-size:13px}}.progress-container{width:100%;height:var(--player-progress-height);background:var(--player-progress-bg);border-radius:calc(var(--player-progress-height)/2);margin-bottom:15px;position:relative;cursor:pointer}.progress-bar{width:100%;height:100%;position:relative;border-radius:calc(var(--player-progress-height)/2);overflow:hidden}.progress-buffer{height:100%;background:var(--player-progress-buffer);width:0%;border-radius:calc(var(--player-progress-height)/2);transition:width var(--player-transition-fast)}.progress-filled{position:absolute;top:0;left:0;height:100%;background:var(--player-primary-color);width:0%;border-radius:calc(var(--player-progress-height)/2);transition:width .1s ease}.progress-handle{position:absolute;top:50%;transform:translate(-50%, -50%);width:var(--player-progress-handle-size);height:var(--player-progress-handle-size);background:var(--player-primary-color);border-radius:50%;opacity:1;transition:opacity var(--player-transition-fast);z-index:2;left:0%;box-shadow:0 2px 8px rgba(0,0,0,.3);pointer-events:all;touch-action:none}.progress-handle::before{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:44px;height:44px;border-radius:50%}.progress-container:hover .progress-handle{opacity:1}.progress-container:hover .progress-filled{background:var(--player-primary-hover)}.seek-tooltip{position:absolute;bottom:100%;left:0;background:rgba(0,0,0,.9);color:#fff;padding:6px 10px;border-radius:6px;font-size:12px;font-weight:500;white-space:nowrap;opacity:0;visibility:hidden;transform:translateX(-50%) translateY(-8px);transition:all .15s ease;z-index:1000;box-shadow:var(--player-shadow-tooltip);font-variant-numeric:tabular-nums;backdrop-filter:blur(8px);border:1px solid hsla(0,0%,100%,.1)}.seek-tooltip::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);width:0;height:0;border-left:5px solid rgba(0,0,0,0);border-right:5px solid rgba(0,0,0,0);border-top:5px solid rgba(0,0,0,.9)}.seek-tooltip.visible{opacity:1;visibility:visible;transform:translateX(-50%) translateY(-4px)}.chapter-markers-container{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:3}.chapter-marker{position:absolute;top:0;height:100%;width:3px;background:var(--player-primary-color);opacity:.7;cursor:pointer;pointer-events:auto;transition:all var(--player-transition-fast);border-radius:2px;transform:translateX(-50%)}.chapter-marker:hover{opacity:1;width:4px;height:120%;top:-10%;box-shadow:0 0 8px var(--player-primary-color)}.chapter-marker.active{background:var(--player-primary-hover);opacity:1;width:4px}.chapter-tooltip{position:absolute;bottom:100%;left:0;background:rgba(0,0,0,.95);backdrop-filter:blur(10px);border-radius:8px;padding:0;margin-bottom:12px;opacity:0;visibility:hidden;transition:all .2s ease;transform:translateX(-50%) translateY(-8px);z-index:1000;box-shadow:var(--player-shadow-tooltip);border:1px solid hsla(0,0%,100%,.15);min-width:200px;max-width:300px;overflow:hidden;pointer-events:none}.chapter-tooltip::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);width:0;height:0;border-left:6px solid rgba(0,0,0,0);border-right:6px solid rgba(0,0,0,0);border-top:6px solid rgba(0,0,0,.95)}.chapter-tooltip-image{width:100%;height:150px;background-size:cover;background-position:center;background-repeat:no-repeat;display:none;border-bottom:1px solid hsla(0,0%,100%,.1)}.chapter-tooltip-title{padding:10px 12px 6px;color:var(--player-text-color);font-size:14px;font-weight:600;line-height:1.3;word-wrap:break-word}.chapter-tooltip-time{padding:0 12px 10px;color:var(--player-text-secondary);font-size:12px;font-weight:400;font-variant-numeric:tabular-nums}.progress-handle-circle{border-radius:50%}.progress-handle-square{border-radius:2px}.progress-handle-diamond{border-radius:2px;transform:translate(-50%, -50%) rotate(45deg)}.progress-handle-arrow{border-radius:0;clip-path:polygon(0% 50%, 60% 0%, 60% 35%, 100% 35%, 100% 65%, 60% 65%, 60% 100%)}.progress-handle-triangle{border-radius:0;clip-path:polygon(50% 0%, 0% 100%, 100% 100%)}.progress-handle-heart{border-radius:0}.progress-handle-heart::before{content:"❤";font-size:12px;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}.progress-handle-star{border-radius:0;clip-path:polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)}.progress-handle-none{opacity:0 !important}.progress-handle::after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:44px;height:44px;border-radius:50%}.progress-container.seeking .progress-bar{height:calc(var(--player-progress-height)*2);transition:height .15s ease}.progress-container.seeking .progress-handle{transform:translate(-50%, -50%) scale(1.4);transition:transform .15s ease}@media(hover: hover)and (pointer: fine){.progress-container:hover .progress-bar{height:calc(var(--player-progress-height)*1.3);transition:height .15s ease}}.chapter-segment{box-sizing:border-box}.chapter-marker:hover{background:rgba(0,0,0,.9) !important}.chapter-tooltip{animation:fadeIn .15s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateX(-50%) translateY(-5px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}.progress-container:hover .chapter-segment{background:hsla(0,0%,100%,.4) !important}.chapter-tooltip{animation:fadeIn .15s ease-in-out}.chapter-tooltip .chapter-tooltip-content{display:flex;flex-direction:column}.chapter-tooltip .chapter-tooltip-image{background-size:cover;background-position:center;background-repeat:no-repeat;border-radius:3px}.chapter-tooltip .chapter-tooltip-title{line-height:1.3}.chapter-tooltip .chapter-tooltip-time{opacity:.8}@keyframes fadeIn{from{opacity:0;transform:translateX(-50%) translateY(-5px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}.chapter-tooltip-hover .chapter-tooltip-content{display:flex;flex-direction:column;gap:6px}.chapter-tooltip-hover .chapter-tooltip-image{width:100%;aspect-ratio:16/9;background-size:cover;background-position:center;background-repeat:no-repeat;border-radius:3px;max-width:180px}.chapter-tooltip-hover .chapter-tooltip-title{font-size:13px;font-weight:600;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chapter-tooltip-hover .chapter-tooltip-time{font-size:12px;font-weight:400;color:hsla(0,0%,100%,.8);max-width:180px}@media(max-width: 1200px){.chapter-tooltip-hover .chapter-tooltip-image{max-width:150px}.chapter-tooltip-hover .chapter-tooltip-title,.chapter-tooltip-hover .chapter-tooltip-time{max-width:150px}}@media(max-width: 768px){.chapter-tooltip-hover .chapter-tooltip-image{max-width:100px}.chapter-tooltip-hover .chapter-tooltip-title,.chapter-tooltip-hover .chapter-tooltip-time{max-width:100px}}@media(max-width: 480px){.chapter-tooltip-hover .chapter-tooltip-image{max-width:80px}.chapter-tooltip-hover .chapter-tooltip-title{font-size:11px;max-width:80px}.chapter-tooltip-hover .chapter-tooltip-time{font-size:10px;max-width:80px}}.volume-container{display:flex;align-items:center;gap:8px;position:relative;flex-shrink:2;min-width:0}.volume-slider{width:60px;height:var(--player-volume-height);background:var(--player-volume-bg);border-radius:calc(var(--player-volume-height)/2);outline:none;cursor:pointer;-webkit-appearance:none;transition:all var(--player-transition-fast)}.volume-tooltip{position:absolute;bottom:210%;transition:opacity .15s ease,transform .15s ease;left:0;transform:translateX(-50%);background:rgba(0,0,0,.9);color:#fff;padding:6px 10px;border-radius:6px;font-size:12px;font-weight:500;white-space:nowrap;opacity:0;visibility:hidden;z-index:1000;box-shadow:var(--player-shadow-tooltip);pointer-events:none;backdrop-filter:blur(8px);border:1px solid hsla(0,0%,100%,.1)}.volume-tooltip::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);width:0;height:0;border-left:6px solid rgba(0,0,0,0);border-right:6px solid rgba(0,0,0,0);border-top:6px solid rgba(0,0,0,.9)}.volume-container:hover .volume-tooltip,.volume-tooltip.visible{opacity:1;visibility:visible}.volume-slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--player-volume-handle-size);height:var(--player-volume-handle-size);border-radius:50%;background:var(--player-primary-dark);cursor:pointer;transition:all var(--player-transition-fast);box-shadow:0 2px 6px rgba(0,0,0,.2);margin-top:calc((var(--player-volume-height) - var(--player-volume-handle-size))/2);transform:translateY(0)}.volume-slider::-webkit-slider-thumb:hover{transform:translateY(0) scale(1.2);background:var(--player-primary-color)}.volume-slider::-moz-range-thumb{width:var(--player-volume-handle-size);height:var(--player-volume-handle-size);border-radius:50%;background:var(--player-primary-dark);cursor:pointer;border:none;box-shadow:0 2px 6px rgba(0,0,0,.2);transition:all var(--player-transition-fast);margin-top:0;transform:translateY(calc((var(--player-volume-height) - var(--player-volume-handle-size)) / 2))}.volume-slider::-moz-range-thumb:hover{background:var(--player-primary-color);transform:translateY(calc((var(--player-volume-height) - var(--player-volume-handle-size)) / 2)) scale(1.1)}.volume-slider::-webkit-slider-runnable-track{height:var(--player-volume-height);background:linear-gradient(to right, var(--player-primary-dark) 0%, var(--player-primary-dark) var(--player-volume-fill), var(--player-volume-bg) var(--player-volume-fill), var(--player-volume-bg) 100%);border-radius:calc(var(--player-volume-height)/2);transition:background var(--player-transition-fast);margin:0;border:none}.volume-slider::-moz-range-track{height:var(--player-volume-height);background:linear-gradient(to right, var(--player-primary-dark) 0%, var(--player-primary-dark) var(--player-volume-fill), var(--player-volume-bg) var(--player-volume-fill), var(--player-volume-bg) 100%);border-radius:calc(var(--player-volume-height)/2);border:none;transition:background var(--player-transition-fast);margin:0}.quality-control{position:relative}.subtitles-control{display:none !important}.speed-control{display:none !important}.speed-menu,.quality-menu,.subtitles-menu{position:absolute;bottom:100%;right:0;background:var(--player-bg-menu);backdrop-filter:blur(10px);border-radius:8px;padding:8px 0;margin-bottom:10px;opacity:0;visibility:hidden;transition:all var(--player-transition-fast);min-width:140px;border:1px solid hsla(0,0%,100%,.1);z-index:100;box-shadow:var(--player-shadow-menu)}.speed-menu.active,.quality-menu.active,.subtitles-menu.active{opacity:1 !important;visibility:visible !important;pointer-events:all !important}.speed-option,.quality-option,.subtitles-option{padding:8px 16px;color:var(--player-text-color);cursor:pointer;transition:all var(--player-transition-fast);font-size:14px;display:flex;align-items:center;justify-content:space-between}.speed-option:hover,.quality-option:hover,.subtitles-option:hover{background:hsla(0,0%,100%,.1);color:var(--player-primary-color)}.speed-option.active,.quality-option.active,.subtitles-option.active{color:var(--player-primary-color);font-weight:600;background:hsla(0,0%,100%,.05)}.subtitles-option.selected,.subtitles-option.active{color:var(--player-primary-color);background:hsla(0,0%,100%,.1);position:relative}.subtitles-option.selected::after,.subtitles-option.active::after{content:"✓";position:absolute;right:10px;font-weight:bold}.quality-option.selected{color:var(--player-primary-color);font-weight:600}.quality-option.selected::after{content:"Selected";font-size:12px;color:var(--player-primary-color);font-weight:400;margin-left:8px}.quality-option.playing{background:hsla(0,0%,100%,.05)}.quality-option.playing::after{content:"Playing";font-size:12px;color:#4caf50;font-weight:400;margin-left:8px}.quality-option.selected.playing::after{content:"Active";font-size:12px;color:var(--player-primary-color);font-weight:500;margin-left:8px}.subtitles-option.active::after{content:"✓";font-size:12px;color:var(--player-primary-color)}.settings-control{position:relative;display:block !important}.settings-btn{background:none;border:none;color:var(--player-button-color);cursor:pointer;padding:var(--player-button-padding);border-radius:6px;display:flex;align-items:center;gap:5px;transition:all var(--player-transition-fast);font-size:14px;font-weight:500;position:relative;flex-shrink:0;min-width:0;white-space:nowrap}.settings-btn:hover{background:var(--player-button-hover);transform:scale(1.05)}.settings-btn:active{transform:scale(0.95);background:var(--player-button-active)}.settings-menu{position:absolute;bottom:100%;right:0;background:var(--player-bg-menu);backdrop-filter:blur(10px);border-radius:8px;padding:8px 0;margin-bottom:10px;opacity:0;visibility:hidden;transition:all var(--player-transition-fast);min-width:180px;border:1px solid hsla(0,0%,100%,.1);z-index:100;box-shadow:var(--player-shadow-menu);max-height:200px;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:var(--player-primary-color) hsla(0,0%,100%,.05)}.settings-menu.active{opacity:1 !important;visibility:visible !important;pointer-events:all !important}.settings-menu::-webkit-scrollbar{width:8px}.settings-menu::-webkit-scrollbar-track{background:hsla(0,0%,100%,.05);border-radius:10px}.settings-menu::-webkit-scrollbar-thumb{background:var(--player-primary-color);border-radius:10px;border:2px solid rgba(0,0,0,0);background-clip:content-box}.settings-menu::-webkit-scrollbar-thumb:hover{background:var(--player-primary-hover, var(--player-primary-color));background-clip:content-box}.settings-option{padding:8px 16px;color:var(--player-text-color);cursor:pointer;transition:all var(--player-transition-fast);font-size:14px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid hsla(0,0%,100%,.05);position:relative}.settings-option:last-child{border-bottom:none}.settings-option:hover{background:hsla(0,0%,100%,.1);color:var(--player-primary-color)}.settings-option-label{display:flex;align-items:center;gap:8px;flex:1}.settings-option-value{font-size:12px;color:var(--player-text-secondary);opacity:.8}.settings-expandable-wrapper{position:relative;display:block}.settings-option.expandable-trigger{display:flex;justify-content:space-between;align-items:center;cursor:pointer}.settings-option.expandable-trigger .settings-option-label{font-size:14px}.settings-option.expandable-trigger .expand-arrow{font-size:12px;transition:transform .2s ease;margin-left:8px}.settings-option.expandable-trigger:hover{background:hsla(0,0%,100%,.1)}.settings-expandable-content{padding-left:15px;margin-top:4px;display:none;background:rgba(0,0,0,.3);border-left:3px solid var(--player-primary-color)}.settings-expandable-content.active{display:block}.settings-suboption{padding:8px 12px;cursor:pointer;color:#fff;font-size:13px;white-space:normal;word-wrap:break-word;opacity:.8;transition:opacity .2s;display:flex;align-items:center;justify-content:space-between}.settings-suboption:hover{opacity:1;background:hsla(0,0%,100%,.1)}.settings-suboption.active{opacity:1;font-weight:bold;color:var(--player-primary-color)}.settings-suboption.active::after{content:"✓";font-size:12px}@media(max-width: 768px){.settings-menu>.settings-option{font-size:13px}.settings-suboption{font-size:12px;padding:7px 10px}}@media(max-width: 600px){.settings-menu>.settings-option{font-size:12px}.settings-suboption{font-size:11px;padding:6px 8px}}@media(max-width: 450px){.settings-menu>.settings-option{font-size:11px}.settings-suboption{font-size:10px;padding:5px 6px}}@media(max-width: 350px){.settings-control{display:block !important}.pip-btn{display:none !important}}.audio-player{width:320px;height:80px}.audio-player video{display:none !important}.audio-player .controls-wrapper{height:60px}.audio-player .audio-wave-canvas{display:block;width:100%;height:60px;background-color:#222;border-radius:4px;margin-top:5px}.player-theme-blue{--player-primary-color: #2196F3;--player-primary-hover: #1976D2;--player-primary-dark: #1565C0}.player-theme-green{--player-primary-color: #4CAF50;--player-primary-hover: #45a049;--player-primary-dark: #388e3c}.player-theme-red{--player-primary-color: #f44336;--player-primary-hover: #d32f2f;--player-primary-dark: #c62828}.video-watermark{position:absolute;z-index:15;pointer-events:auto;opacity:.7;transition:opacity .3s ease,visibility .3s ease,bottom .3s ease}.video-watermark{visibility:visible;opacity:.7}.video-wrapper:not(.has-controls) .video-watermark.hide-on-autohide{visibility:hidden;opacity:0}.video-wrapper.has-controls .video-watermark{visibility:visible;opacity:.7}.video-watermark:hover{opacity:1}.video-watermark img{display:block;max-width:150px;max-height:80px;width:auto;height:auto;object-fit:contain}.video-watermark.watermark-topleft{top:15px;left:15px}.video-watermark.watermark-topright{top:15px;right:15px}.video-watermark.watermark-bottomleft{bottom:calc(var(--player-controls-height, 70px) + 15px);left:15px}.video-watermark.watermark-bottomright{bottom:calc(var(--player-controls-height, 70px) + 15px);right:15px}.video-wrapper:not(.has-controls) .video-watermark.watermark-bottomleft:not(.hide-on-autohide){bottom:15px}.video-wrapper:not(.has-controls) .video-watermark.watermark-bottomright:not(.hide-on-autohide){bottom:15px}.video-wrapper.has-controls .video-watermark.watermark-bottomleft,.video-wrapper.has-controls .video-watermark.watermark-bottomright{bottom:calc(var(--player-controls-height, 70px) + 15px)}@media(max-width: 768px){.video-watermark img{max-width:100px;max-height:50px}.video-watermark.watermark-topleft,.video-watermark.watermark-topright{top:10px}.video-watermark.watermark-topleft,.video-watermark.watermark-bottomleft{left:10px}.video-watermark.watermark-topright,.video-watermark.watermark-bottomright{right:10px}.video-watermark.watermark-bottomleft,.video-watermark.watermark-bottomright{bottom:calc(var(--player-controls-height, 60px) + 10px)}.video-wrapper:not(.has-controls) .video-watermark.watermark-bottomleft:not(.hide-on-autohide),.video-wrapper:not(.has-controls) .video-watermark.watermark-bottomright:not(.hide-on-autohide){bottom:10px}}@media(max-width: 480px){.video-watermark.watermark-bottomleft,.video-watermark.watermark-bottomright{bottom:calc(var(--player-controls-height, 55px) + 10px)}.video-wrapper:not(.has-controls) .video-watermark.watermark-bottomleft:not(.hide-on-autohide),.video-wrapper:not(.has-controls) .video-watermark.watermark-bottomright:not(.hide-on-autohide){bottom:8px}}.video-watermark[style*="cursor: pointer"]{cursor:pointer !important}@media(max-width: 768px){.controls-left,.controls-right{gap:8px}.volume-slider{width:50px}.time-display{font-size:12px}.icon{font-size:14px}.control-btn{padding:6px}.quality-btn{min-height:32px;padding:4px 6px}.selected-quality{font-size:12px}.current-quality{font-size:9px}.seek-tooltip{font-size:11px;padding:4px 8px}.title-overlay{padding:12px 15px 20px}.title-text{font-size:16px}.video-player::cue{font-size:16px;padding:6px 10px}.controls-right .brand-logo{height:36px;max-width:100px;margin-right:8px}}@media(max-width: 480px){.controls-left,.controls-right{gap:6px}.progress-container{margin-bottom:10px}.controls-main{padding-top:6px}.volume-container{flex-shrink:3}.volume-slider{width:35px}.quality-btn{min-height:28px;padding:3px 5px}.selected-quality{font-size:11px}.current-quality{font-size:8px}.seek-tooltip{font-size:10px;padding:3px 6px}.title-overlay{padding:10px 12px 18px}.title-text{font-size:14px}.video-player::cue{font-size:14px;padding:4px 8px}.controls-right .brand-logo{height:28px;max-width:80px;margin-right:5px}}@media(max-width: 350px){.controls-left,.controls-right{gap:4px}.control-btn{padding:4px}.icon{font-size:12px}.quality-btn{min-height:24px;padding:2px 4px}.selected-quality{font-size:10px}.current-quality{font-size:7px}.controls-right .brand-logo{height:22px;max-width:50px;margin-right:3px}.volume-slider{width:30px}.settings-menu{min-width:160px;font-size:12px}.settings-option{padding:6px 12px;font-size:12px}.settings-submenu{min-width:130px}.settings-suboption{padding:6px 12px;font-size:11px}}@media(max-width: 280px){.controls-left,.controls-right{gap:3px}.control-btn{padding:3px}.icon{font-size:10px}.quality-btn{min-height:20px;padding:1px 3px}.selected-quality{font-size:9px}.current-quality{font-size:6px}.controls-right .brand-logo{height:18px;max-width:40px;margin-right:2px}.volume-slider{width:25px}.settings-menu{min-width:140px;font-size:11px}.settings-option{padding:5px 10px;font-size:11px}.settings-submenu{min-width:120px}.settings-suboption{padding:5px 10px;font-size:10px}}@media(max-width: 600px){.controls-main{overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.controls-main::-webkit-scrollbar{display:none}.controls-left,.controls-right{flex-wrap:nowrap;white-space:nowrap;flex-shrink:1;min-width:fit-content}}.controls-right .playlist-prev-btn,.controls-right .playlist-next-btn{display:none}.controls-right .playlist-prev-btn.playlist-active,.controls-right .playlist-next-btn.playlist-active{display:flex}.playlist-prev-btn .icon::before{content:"⏮"}.playlist-next-btn .icon::before{content:"⏭"}.playlist-prev-btn:disabled,.playlist-next-btn:disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.playlist-prev-btn:disabled .icon,.playlist-next-btn:disabled .icon{opacity:.5}@media(max-width: 768px){.playlist-prev-btn .icon::before,.playlist-next-btn .icon::before{font-size:16px}}@media(max-width: 480px){.playlist-prev-btn .icon::before,.playlist-next-btn .icon::before{font-size:14px}}.video-container:fullscreen,.video-container:-webkit-full-screen,.video-container:-moz-full-screen{width:100vw;height:100vh;border-radius:0}@keyframes qualityChange{0%{opacity:.7}50%{opacity:.3}100%{opacity:1}}.quality-changing{animation:qualityChange .5s ease-in-out}.control-btn:focus{outline:2px solid var(--player-primary-color);outline-offset:2px}.volume-slider:focus{outline:2px solid var(--player-primary-color);outline-offset:2px}.player-large-controls{--player-icon-size: 24px;--player-button-padding: 12px;--player-progress-height: 8px;--player-progress-handle-size: 20px;--player-title-overlay-padding: 18px 24px 30px}.player-compact-controls{--player-icon-size: 16px;--player-button-padding: 4px;--player-controls-padding: 15px 10px 10px;--player-title-overlay-padding: 12px 16px 20px}@-moz-document url-prefix(){.volume-slider::-moz-range-thumb{transform:translateY(calc((var(--player-volume-height) - var(--player-volume-handle-size)) / 2 - 2px))}.volume-slider::-moz-range-thumb:hover{transform:translateY(calc((var(--player-volume-height) - var(--player-volume-handle-size)) / 2 - 2px)) scale(1.1)}.volume-slider::-moz-range-track{height:var(--player-volume-height);background:linear-gradient(to right, var(--player-primary-dark) 0%, var(--player-primary-dark) var(--player-volume-fill), var(--player-volume-bg) var(--player-volume-fill), var(--player-volume-bg) 100%);border-radius:calc(var(--player-volume-height)/2);border:none;transition:background var(--player-transition-fast);margin:0}}@supports(-moz-appearance: none){.volume-slider{margin-top:-1px}.volume-slider::-moz-range-thumb{transform:translateY(calc((var(--player-volume-height) - var(--player-volume-handle-size)) / 2 - 1.5px))}.volume-slider::-moz-range-thumb:hover{transform:translateY(calc((var(--player-volume-height) - var(--player-volume-handle-size)) / 2 - 1.5px)) scale(1.1)}}@-moz-document url-prefix(){.volume-container{position:relative;top:2px !important}.volume-slider::-moz-range-thumb{margin-top:-6px !important;transform:translateY(-2px) !important}.volume-slider::-moz-range-thumb:hover{transform:translateY(-2px) scale(1.1) !important}}.video-player{object-position:center center}.resolution-normal .video-player{object-fit:contain;object-position:center center}.resolution-4-3 .video-player{object-fit:fill;aspect-ratio:4/3}.resolution-4-3 .video-wrapper{aspect-ratio:4/3}.resolution-16-9 .video-player{object-fit:fill;aspect-ratio:16/9}.resolution-16-9 .video-wrapper{aspect-ratio:16/9}.resolution-stretched .video-player{object-fit:fill;width:100%;height:100%}.resolution-stretched .video-wrapper{height:auto;min-height:300px}.resolution-fit-to-screen .video-player{object-fit:cover;object-position:center center;width:100%;height:100%}.resolution-fit-to-screen .video-wrapper{height:100vh;max-height:100vh}.resolution-scale-to-fit .video-player{object-fit:contain;object-position:center center;width:100%;height:100%;max-width:100vw;max-height:100vh}.resolution-scale-to-fit .video-wrapper{display:flex;align-items:center;justify-content:center;height:70vh;min-height:400px;background:var(--player-bg-primary, #000)}@media(orientation: portrait){.resolution-scale-to-fit .video-wrapper{height:50vh;min-height:350px}}@media(orientation: landscape){.resolution-scale-to-fit .video-wrapper{height:80vh;min-height:450px}}@media(max-width: 768px){.resolution-fit-to-screen .video-wrapper{height:50vh;min-height:250px}.resolution-4-3 .video-wrapper,.resolution-16-9 .video-wrapper{min-height:200px}.resolution-scale-to-fit .video-wrapper{height:45vh;min-height:300px}}@media(max-width: 480px){.resolution-fit-to-screen .video-wrapper{height:40vh;min-height:200px}.resolution-4-3 .video-wrapper,.resolution-16-9 .video-wrapper{min-height:180px}.resolution-scale-to-fit .video-wrapper{height:40vh;min-height:250px}}.video-player{transition:object-fit .3s ease,aspect-ratio .3s ease}.video-wrapper{transition:aspect-ratio .3s ease,height .3s ease}@supports not (aspect-ratio: 1){.resolution-4-3 .video-wrapper{padding-bottom:75%;height:0;position:relative}.resolution-4-3 .video-player{position:absolute;top:0;left:0;width:100%;height:100%}.resolution-16-9 .video-wrapper{padding-bottom:56.25%;height:0;position:relative}.resolution-16-9 .video-player{position:absolute;top:0;left:0;width:100%;height:100%}}.quality-changing .video-player{filter:brightness(0.7)}.resolution-debug .video-wrapper::before{content:"Resolution: " attr(data-resolution);position:absolute;top:10px;left:10px;background:rgba(0,0,0,.7);color:#fff;padding:5px 10px;border-radius:4px;font-size:12px;z-index:1000;pointer-events:none}.controls,.controls-main,.controls-left,.controls-right{overflow:visible !important}.controls-left,.controls-right{flex-wrap:nowrap !important;white-space:nowrap !important}.control-btn{min-width:0 !important;white-space:nowrap !important}video::cue{background-color:rgba(0,0,0,.8);color:#fff;font-family:Arial,Helvetica,sans-serif;font-size:18px;font-weight:normal;line-height:1.2;text-shadow:1px 1px 1px rgba(0,0,0,.8);padding:4px 8px;border-radius:4px;white-space:pre-line}video::-webkit-media-text-track-display{color:#fff;font-family:Arial,Helvetica,sans-serif;background-color:rgba(0,0,0,.8);border-radius:4px;padding:4px 8px;font-size:18px;text-shadow:1px 1px 1px rgba(0,0,0,.8)}.custom-subtitle-overlay{font-size:clamp(12px,4vw,18px)}@media(max-width: 768px){.custom-subtitle-overlay{font-size:16px !important;bottom:70px !important;max-width:85% !important;padding:6px 12px !important;line-height:1.2 !important}}@media(max-width: 480px){.custom-subtitle-overlay{font-size:14px !important;bottom:60px !important;max-width:90% !important;padding:5px 10px !important;line-height:1.15 !important}}@media(max-width: 360px){.custom-subtitle-overlay{font-size:12px !important;bottom:50px !important;max-width:95% !important;padding:4px 8px !important}}@media(max-height: 500px)and (orientation: landscape){.custom-subtitle-overlay{font-size:13px !important;bottom:45px !important;max-width:85% !important;padding:4px 10px !important}}.speed-menu,.quality-menu,.subtitles-menu{max-height:200px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--player-primary-color) hsla(0,0%,100%,.1)}.speed-menu::-webkit-scrollbar,.quality-menu::-webkit-scrollbar,.subtitles-menu::-webkit-scrollbar{width:6px}.speed-menu::-webkit-scrollbar-track,.quality-menu::-webkit-scrollbar-track,.subtitles-menu::-webkit-scrollbar-track{background:hsla(0,0%,100%,.1);border-radius:3px}.speed-menu::-webkit-scrollbar-thumb,.quality-menu::-webkit-scrollbar-thumb,.subtitles-menu::-webkit-scrollbar-thumb{background:var(--player-primary-color);border-radius:3px}.speed-menu::-webkit-scrollbar-thumb:hover,.quality-menu::-webkit-scrollbar-thumb:hover,.subtitles-menu::-webkit-scrollbar-thumb:hover{background:var(--player-primary-hover)}@media(max-height: 400px){.speed-menu,.quality-menu,.subtitles-menu{max-height:150px}}@media(max-height: 300px){.speed-menu,.quality-menu,.subtitles-menu{max-height:120px}}.settings-submenu{max-height:180px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--player-primary-color) hsla(0,0%,100%,.1)}.settings-submenu::-webkit-scrollbar{width:6px}.settings-submenu::-webkit-scrollbar-track{background:hsla(0,0%,100%,.1);border-radius:3px}.settings-submenu::-webkit-scrollbar-thumb{background:var(--player-primary-color);border-radius:3px}.settings-submenu::-webkit-scrollbar-thumb:hover{background:var(--player-primary-hover)}@media(max-width: 350px){.settings-submenu{max-height:140px}}@media(max-height: 400px){.settings-submenu{max-height:120px}}.volume-container{position:relative;display:flex;align-items:center;gap:var(--player-controls-gap)}.volume-container[data-mobile-slider=show] .volume-slider{width:80px;height:var(--player-volume-height);background:var(--player-volume-bg);border-radius:calc(var(--player-volume-height)/2);transition:all .3s ease}@media(max-width: 1200px){.volume-container[data-mobile-slider=show] .volume-slider{width:70px}}@media(max-width: 900px){.volume-container[data-mobile-slider=show] .volume-slider{width:60px}}@media(max-width: 768px){.volume-container[data-mobile-slider=show] .volume-slider{width:50px}}@media(max-width: 600px){.volume-container[data-mobile-slider=show] .volume-slider{width:40px}}@media(max-width: 550px){.volume-container[data-mobile-slider=show] .volume-tooltip{display:none !important}.volume-container[data-mobile-slider=show]{pointer-events:auto !important;position:relative}.mute-btn{position:relative;z-index:100;pointer-events:auto !important}.volume-container[data-mobile-slider=show] .volume-slider{position:absolute;opacity:0;visibility:hidden;pointer-events:none;width:0;height:0;transform:translateX(-100%);transition:opacity 0s ease,visibility 0s ease 2s,width 0s ease 2s}.controls-left:hover .volume-container[data-mobile-slider=show] .volume-slider,.mute-btn:hover~.volume-container[data-mobile-slider=show] .volume-slider,.volume-container[data-mobile-slider=show]:hover .volume-slider,.volume-slider:hover{position:absolute;opacity:1;visibility:visible;pointer-events:auto !important;width:90px !important;height:auto;bottom:auto;top:50%;left:5px;transform:translateY(-50%);z-index:19;background:rgba(0,0,0,.92) !important;border:1px solid hsla(0,0%,100%,.15);border-radius:8px;padding:10px 14px;box-shadow:0 4px 16px rgba(0,0,0,.6);backdrop-filter:blur(10px);transition:opacity .2s ease,visibility 0s ease,width .2s ease}.controls-left:has(.volume-container[data-mobile-slider=show]):hover{pointer-events:auto !important}.controls-left:hover .volume-slider::-webkit-slider-runnable-track,.volume-container[data-mobile-slider=show]:hover .volume-slider::-webkit-slider-runnable-track,.volume-slider:hover::-webkit-slider-runnable-track{width:60px;height:4px !important;background:linear-gradient(to right, var(--player-primary-color) 0%, var(--player-primary-color) var(--player-volume-fill, 50%), rgba(255, 255, 255, 0.4) var(--player-volume-fill, 50%), rgba(255, 255, 255, 0.4) 100%) !important;border-radius:2px}.controls-left:hover .volume-slider::-webkit-slider-thumb,.volume-container[data-mobile-slider=show]:hover .volume-slider::-webkit-slider-thumb,.volume-slider:hover::-webkit-slider-thumb{opacity:1 !important;visibility:visible !important;-webkit-appearance:none;width:14px;height:14px;border-radius:50%;background:#fff;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,.5);margin-top:-5px}}.chapter-markers-container{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:3}.chapter-marker{position:absolute;top:0;height:100%;width:3px;background:var(--player-primary-color);opacity:.7;cursor:pointer;pointer-events:auto;transition:all var(--player-transition-fast);border-radius:2px;transform:translateX(-50%)}.chapter-marker:hover{opacity:1;width:4px;height:120%;top:-10%;box-shadow:0 0 8px var(--player-primary-color)}.chapter-marker.active{background:var(--player-primary-hover);opacity:1;width:4px}.chapter-tooltip{position:absolute;bottom:100%;left:0;background:rgba(0,0,0,.95);backdrop-filter:blur(10px);border-radius:8px;padding:0;margin-bottom:12px;opacity:0;visibility:hidden;transition:all .2s ease;transform:translateX(-50%) translateY(-8px);z-index:1000;box-shadow:var(--player-shadow-tooltip);border:1px solid hsla(0,0%,100%,.15);min-width:200px;max-width:300px;overflow:hidden;pointer-events:none}.chapter-tooltip::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);width:0;height:0;border-left:6px solid rgba(0,0,0,0);border-right:6px solid rgba(0,0,0,0);border-top:6px solid rgba(0,0,0,.95)}.chapter-tooltip-image{width:100%;height:150px;background-size:cover;background-position:center;background-repeat:no-repeat;display:none;border-bottom:1px solid hsla(0,0%,100%,.1)}.chapter-tooltip-title{padding:10px 12px 6px;color:var(--player-text-color);font-size:14px;font-weight:600;line-height:1.3;word-wrap:break-word}.chapter-tooltip-time{padding:0 12px 10px;color:var(--player-text-secondary);font-size:12px;font-weight:400;font-variant-numeric:tabular-nums}@media(max-width: 480px){.chapter-marker{width:2px}.chapter-marker:hover{width:3px}.chapter-tooltip{min-width:160px;max-width:250px}.chapter-tooltip-image{height:100px}}.video-poster-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-size:cover;background-position:center;background-repeat:no-repeat;z-index:1;cursor:pointer;opacity:0;visibility:hidden;transition:opacity .3s ease,visibility .3s ease;pointer-events:none}.video-poster-overlay.visible{opacity:1;visibility:visible;pointer-events:auto}.video-poster-overlay.hidden{opacity:0;visibility:hidden;pointer-events:none}.video-poster-overlay.visible:hover{opacity:.95}.video-poster-overlay::after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:80px;height:80px;background:rgba(0,0,0,.7);border-radius:50%;border:3px solid var(--player-primary-color);opacity:0;transition:opacity .3s ease,transform .3s ease,border-color .3s ease;pointer-events:none}.video-poster-overlay.visible:hover::after{opacity:1;transform:translate(-50%, -50%) scale(1.1);border-color:var(--player-primary-hover);box-shadow:0 0 20px var(--player-primary-color)}.video-poster-overlay::before{content:"";position:absolute;top:50%;left:50%;transform:translate(-40%, -50%);width:0;height:0;border-style:solid;border-width:15px 0 15px 25px;border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) var(--player-primary-color);z-index:2;opacity:0;transition:opacity .3s ease,border-color .3s ease;pointer-events:none}.video-poster-overlay.visible:hover::before{opacity:1;border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) var(--player-primary-hover)}@media(max-width: 768px){.video-poster-overlay::after{width:60px;height:60px}.video-poster-overlay::before{border-width:12px 0 12px 20px}}@media(max-width: 480px){.video-poster-overlay::after{width:50px;height:50px}.video-poster-overlay::before{border-width:10px 0 10px 16px}}.video-poster-overlay.hidden{transition:opacity .5s ease,visibility 0s ease .5s}.player-theme-blue .video-poster-overlay::after{border-color:#2196f3}.player-theme-blue .video-poster-overlay.visible:hover::after{border-color:#1976d2;box-shadow:0 0 20px #2196f3}.player-theme-blue .video-poster-overlay::before{border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) #2196f3}.player-theme-blue .video-poster-overlay.visible:hover::before{border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) #1976d2}.player-theme-green .video-poster-overlay::after{border-color:#4caf50}.player-theme-green .video-poster-overlay.visible:hover::after{border-color:#45a049;box-shadow:0 0 20px #4caf50}.player-theme-green .video-poster-overlay::before{border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) #4caf50}.player-theme-green .video-poster-overlay.visible:hover::before{border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) #45a049}.player-theme-red .video-poster-overlay::after{border-color:#f44336}.player-theme-red .video-poster-overlay.visible:hover::after{border-color:#d32f2f;box-shadow:0 0 20px #f44336}.player-theme-red .video-poster-overlay::before{border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) #f44336}.player-theme-red .video-poster-overlay.visible:hover::before{border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) #d32f2f}.player-theme-dark .video-poster-overlay::after{background:rgba(0,0,0,.85)}@media(max-height: 400px){.video-player{min-height:200px}.controls{min-height:50px !important;padding:10px 10px 8px !important}.progress-container{margin-bottom:8px}.controls-main{min-height:32px !important}}@media(max-height: 330px){.video-player{min-height:150px}.controls{min-height:45px !important;padding:8px 8px 6px !important}.progress-container{margin-bottom:6px;height:4px}.controls-main{min-height:28px !important}.control-btn{padding:4px !important}.icon{font-size:14px !important}.time-display{font-size:11px !important}}@media(max-height: 250px){.video-player{min-height:120px}.controls{min-height:40px !important;padding:6px 8px 5px !important}.progress-container{margin-bottom:4px;height:3px}.controls-main{min-height:24px !important}.control-btn{padding:2px !important}.icon{font-size:12px !important}.time-display{font-size:10px !important}.quality-btn{min-height:20px !important;padding:2px 4px !important}.selected-quality{font-size:9px !important}.current-quality{display:none}.volume-slider{width:40px !important}}.video-container,.video-wrapper{overflow:visible !important}.controls.show{position:absolute !important;bottom:0 !important;overflow:visible !important}
|
|
1
|
+
:root{--player-primary-color: goldenrod;--player-primary-hover: #daa520;--player-primary-dark: #b8860b;--player-button-color: white;--player-button-hover: rgba(255, 255, 255, 0.1);--player-button-active: rgba(255, 255, 255, 0.2);--player-text-color: white;--player-text-secondary: rgba(255, 255, 255, 0.8);--player-bg-primary: #000;--player-bg-controls: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.8) 100%);--player-bg-title-overlay: linear-gradient(180deg, rgba(0, 0, 0, 0.8) 0%, transparent 100%);--player-bg-menu: rgba(20, 20, 20, 0.95);--player-bg-loading: rgba(0, 0, 0, 0.7);--player-border-radius: 12px;--player-controls-padding: 20px 15px 15px;--player-title-overlay-padding: 15px 20px 25px;--player-button-padding: 8px;--player-icon-size: 20px;--player-progress-height: 6px;--player-progress-bg: rgba(255, 255, 255, 0.2);--player-progress-buffer: rgba(255, 255, 255, 0.3);--player-progress-handle-size: 16px;--player-volume-height: 4px;--player-volume-bg: rgba(255, 255, 255, 0.2);--player-volume-handle-size: 14px;--player-volume-fill: 100%;--player-transition-fast: 0.2s ease;--player-transition-normal: 0.3s ease;--player-shadow-main: 0 8px 32px rgba(0, 0, 0, 0.3);--player-shadow-menu: 0 4px 16px rgba(0, 0, 0, 0.2);--player-shadow-tooltip: 0 3px 12px rgba(0, 0, 0, 0.4)}*{box-sizing:border-box}body{margin:0;padding:20px;background:#1a1a1a;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif}.video-container{max-width:1200px;margin:0 auto;background:var(--player-bg-primary);border-radius:var(--player-border-radius);box-shadow:var(--player-shadow-main);position:relative}.video-container:fullscreen,.video-container:-webkit-full-screen,.video-container:-moz-full-screen{width:100vw;height:100vh;border-radius:0}.video-wrapper{position:relative;width:100%;background:var(--player-bg-primary);overflow:visible !important}.video-wrapper.player-initialized .video-player{visibility:visible;opacity:1;transition:opacity .3s ease;pointer-events:auto}.video-wrapper.player-initialized .initial-loading{opacity:0;visibility:hidden;transition:opacity .3s ease,visibility .3s ease;transition-delay:.2s;display:none;transition-delay:.5s}.video-wrapper:not(.has-controls) .video-watermark.hide-on-autohide{visibility:hidden;opacity:0}.hidden{display:none !important}.player-theme-dark{--player-bg-primary: #1a1a1a;--player-bg-controls: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.9) 100%);--player-bg-title-overlay: linear-gradient(180deg, rgba(30, 30, 30, 0.9) 0%, transparent 100%);--player-bg-menu: rgba(30, 30, 30, 0.95)}.video-player{width:100%;height:auto;display:block;min-height:300px;-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden;will-change:transform;visibility:visible;opacity:1;position:relative;z-index:1;transition:none}.video-wrapper.player-initialized .video-player{visibility:visible;opacity:1;transition:opacity .3s ease;pointer-events:auto}.video-player::-webkit-media-controls-panel,.video-player::-webkit-media-controls-play-button,.video-player::-webkit-media-controls-start-playback-button,.video-player::-webkit-media-controls-timeline,.video-player::-webkit-media-controls-current-time-display,.video-player::-webkit-media-controls-time-remaining-display,.video-player::-webkit-media-controls-mute-button,.video-player::-webkit-media-controls-toggle-closed-captions-button,.video-player::-webkit-media-controls-volume-slider,.video-player::-webkit-media-controls-fullscreen-button,.video-player::-webkit-media-controls-seek-back-button,.video-player::-webkit-media-controls-seek-forward-button,.video-player::-webkit-media-controls-rewind-button,.video-player::-webkit-media-controls-return-to-realtime-button,.video-player::-webkit-media-controls-overlay-play-button{display:none !important;visibility:hidden !important;opacity:0 !important}.video-player::-moz-media-controls{display:none !important}.initial-loading{position:absolute;top:0;left:0;width:100%;height:100%;background:var(--player-bg-primary);display:flex;align-items:center;justify-content:center;z-index:20}.video-wrapper.player-initialized .initial-loading{opacity:0;visibility:hidden;transition:opacity .3s ease,visibility .3s ease;transition-delay:.2s}.video-wrapper.player-initialized .initial-loading{display:none;transition-delay:.5s}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:var(--player-bg-loading);display:flex;align-items:center;justify-content:center;opacity:0;visibility:hidden;transition:opacity var(--player-transition-normal);z-index:15}.loading-overlay.active{opacity:1;visibility:visible}.loading-spinner{width:50px;height:50px;border:3px solid hsla(0,0%,100%,.3);border-top:3px solid var(--player-primary-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.video-player::cue{background:rgba(0,0,0,.8);color:#fff;font-size:18px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;font-weight:500;text-shadow:2px 2px 4px rgba(0,0,0,.8);padding:8px 12px;border-radius:6px;line-height:1.4}.video-player::cue(.highlight){background:var(--player-primary-color);color:#000}.video-player::cue(b){font-weight:700}.video-player::cue(i){font-style:italic}.video-poster-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-size:cover;background-position:center;background-repeat:no-repeat;z-index:1;cursor:pointer;opacity:0;visibility:hidden;transition:opacity .3s ease,visibility .3s ease;pointer-events:none}.video-poster-overlay.visible{opacity:1;visibility:visible;pointer-events:auto}.video-poster-overlay.hidden{opacity:0;visibility:hidden;pointer-events:none}.video-poster-overlay.visible:hover{opacity:.95}.video-poster-overlay::after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:80px;height:80px;background:rgba(0,0,0,.7);border-radius:50%;border:3px solid var(--player-primary-color);opacity:0;transition:opacity .3s ease,transform .3s ease,border-color .3s ease;pointer-events:none}.video-poster-overlay.visible:hover::after{opacity:1;transform:translate(-50%, -50%) scale(1.1);border-color:var(--player-primary-hover);box-shadow:0 0 20px var(--player-primary-color)}.video-poster-overlay::before{content:"";position:absolute;top:50%;left:50%;transform:translate(-40%, -50%);width:0;height:0;border-style:solid;border-width:15px 0 15px 25px;border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) var(--player-primary-color);z-index:2;opacity:0;transition:opacity .3s ease,border-color .3s ease;pointer-events:none}.video-poster-overlay.visible:hover::before{opacity:1;border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) var(--player-primary-hover)}.initial-loading{position:absolute;top:0;left:0;width:100%;height:100%;background:var(--player-bg-primary);display:flex;align-items:center;justify-content:center;z-index:20;flex-direction:column;gap:15px}.video-wrapper.player-initialized .initial-loading{opacity:0;visibility:hidden;transition:opacity .3s ease,visibility .3s ease;transition-delay:.2s}.video-wrapper.player-initialized .initial-loading{display:none;transition-delay:.5s}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:var(--player-bg-loading);display:flex;align-items:center;justify-content:center;opacity:0;visibility:hidden;transition:opacity var(--player-transition-normal);z-index:15;flex-direction:column;gap:15px}.loading-overlay.active{opacity:1;visibility:visible}.loading-spinner{width:50px;height:50px;border:3px solid hsla(0,0%,100%,.3);border-top:3px solid var(--player-primary-color);border-radius:50%;animation:spin 1s linear infinite}.loading-spinner-wrap{position:relative;width:56px;height:56px;display:flex;align-items:center;justify-content:center}.loading-spinner-wrap .loading-spinner{position:absolute;top:0;left:0;width:100%;height:100%;border-radius:50%;border:3px solid hsla(0,0%,100%,.2);border-top:3px solid var(--player-primary-color);animation:spin 1s linear infinite;box-sizing:border-box}.loading-spinner-logo{position:relative;z-index:1;max-width:34px;max-height:34px;width:auto;height:auto;border-radius:0;object-fit:contain;display:block;flex-shrink:0;pointer-events:none}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.loading-text{color:var(--player-text-color);font-size:14px;font-weight:500;text-shadow:0 2px 4px rgba(0,0,0,.5);letter-spacing:.5px;margin-top:10px;text-align:center}.title-overlay{position:absolute;top:0;left:0;right:0;background:var(--player-bg-title-overlay);padding:var(--player-title-overlay-padding);opacity:0;transform:translateY(-100%);transition:all var(--player-transition-normal);z-index:15;pointer-events:none}.title-overlay.show{opacity:1;transform:translateY(0)}.title-overlay.show.persistent{opacity:1;transform:translateY(0)}.title-text{color:var(--player-text-color);font-size:18px;font-weight:600;line-height:1.3;margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:0 2px 8px rgba(0,0,0,.7);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.player-top-bar{position:absolute;top:0;left:0;right:0;display:flex;justify-content:space-between;align-items:flex-start;padding:15px 20px;background:linear-gradient(to bottom, rgba(0, 0, 0, var(--player-topbar-opacity, 0.95)) 0%, rgba(0, 0, 0, calc(var(--player-topbar-opacity, 0.95) * 0.68)) 50%, rgba(0, 0, 0, 0) 100%);backdrop-filter:blur(5px);z-index:20;opacity:0;transform:translateY(-100%);transition:all .3s ease;pointer-events:none}.player-top-bar.no-title-background{background:rgba(0,0,0,0);backdrop-filter:none}.player-top-bar .top-bar-title{flex:1;margin-right:20px;pointer-events:none;min-width:0;max-width:calc(100% - 80px)}.player-top-bar .top-bar-title .video-title{color:#fff;font-size:18px;font-weight:600;margin:0 0 4px 0;text-shadow:0 2px 4px rgba(0,0,0,.8);line-height:1.3;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.player-top-bar .top-bar-title .video-subtitle{color:hsla(0,0%,100%,.8);font-size:14px;text-shadow:0 1px 3px rgba(0,0,0,.8);max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}.player-top-bar .top-bar-spacer{flex:1}.player-top-bar .settings-control{pointer-events:all;position:relative;flex-shrink:0}.player-top-bar .settings-control .settings-btn{background:rgba(0,0,0,.6);backdrop-filter:blur(10px);padding:10px;border-radius:50%;border:1px solid hsla(0,0%,100%,.15);transition:all .3s ease;cursor:pointer}.player-top-bar .settings-control .settings-btn:hover,.player-top-bar .settings-control .settings-btn.active{background:rgba(0,0,0,.9);border-color:hsla(0,0%,100%,.3);transform:rotate(90deg)}.player-top-bar .settings-control .settings-btn .icon svg{display:block}.player-top-bar .settings-control .settings-menu{position:absolute;top:calc(100% + 10px);right:0;min-width:240px;max-width:320px;background:rgba(28,28,28,.98);backdrop-filter:blur(20px);border-radius:8px;border:1px solid hsla(0,0%,100%,.1);box-shadow:0 8px 32px rgba(0,0,0,.6);opacity:0;visibility:hidden;transform:translateY(-10px);transition:all .3s ease;max-height:600px !important;min-height:200px;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:hsla(0,0%,100%,.3) rgba(0,0,0,0);display:flex;flex-direction:column}.player-top-bar .settings-control .settings-menu::-webkit-scrollbar{width:6px}.player-top-bar .settings-control .settings-menu::-webkit-scrollbar-track{background:hsla(0,0%,100%,.05);border-radius:3px}.player-top-bar .settings-control .settings-menu::-webkit-scrollbar-thumb{background:hsla(0,0%,100%,.3);border-radius:3px}.player-top-bar .settings-control .settings-menu::-webkit-scrollbar-thumb:hover{background:hsla(0,0%,100%,.5)}.player-top-bar .settings-control .settings-menu.active{opacity:1;visibility:visible;transform:translateY(0)}.settings-option[data-action=moreinfo]{order:-1;border-bottom:1px solid hsla(0,0%,100%,.1);padding-bottom:12px;margin-bottom:8px}.settings-expandable-wrapper{border-bottom:1px solid hsla(0,0%,100%,.05)}.settings-expandable-wrapper:last-child{border-bottom:none}.settings-option{padding:12px 16px;cursor:pointer;transition:background .2s ease;display:flex;justify-content:space-between;align-items:center;color:#fff;user-select:none}.settings-option:hover{background:hsla(0,0%,100%,.1)}.settings-option .settings-option-label{flex:1;color:#fff;font-size:14px;font-weight:400}.settings-option .settings-option-label strong{font-weight:600;margin-left:4px}.settings-option .expand-arrow{color:hsla(0,0%,100%,.6);font-size:12px;transition:transform .3s ease;margin-left:10px;line-height:1}.settings-expandable-content{background:rgba(0,0,0,.3);max-height:250px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:hsla(0,0%,100%,.2) rgba(0,0,0,0)}.settings-expandable-content::-webkit-scrollbar{width:4px}.settings-expandable-content::-webkit-scrollbar-thumb{background:hsla(0,0%,100%,.2);border-radius:2px}.settings-expandable-content::-webkit-scrollbar-thumb:hover{background:hsla(0,0%,100%,.3)}.settings-suboption{padding:10px 20px 10px 32px;cursor:pointer;transition:background .2s ease;color:hsla(0,0%,100%,.8);font-size:13px;user-select:none}.settings-suboption:hover{background:hsla(0,0%,100%,.08);color:hsla(0,0%,100%,.95)}.settings-suboption.active{background:hsla(0,0%,100%,.15);color:#fff;font-weight:500;position:relative}.settings-suboption.active::before{content:"✓";position:absolute;left:12px;color:var(--player-primary-color, #ff0000);font-weight:700}body .video-wrapper:hover .player-top-bar,body .video-wrapper .player-top-bar{opacity:0 !important;transform:translateY(-100%) !important;transition:all .3s ease !important}body .video-wrapper.has-controls .player-top-bar{opacity:1 !important;transform:translateY(0) !important}body .video-wrapper .player-top-bar.persistent{opacity:1 !important;transform:translateY(0) !important}@media(max-width: 768px){.player-top-bar{padding:12px 15px}.player-top-bar .top-bar-title{margin-right:15px;max-width:calc(100% - 70px)}.player-top-bar .top-bar-title .video-title{font-size:16px}.player-top-bar .top-bar-title .video-subtitle{font-size:13px}.player-top-bar .settings-control .settings-btn{padding:8px}.player-top-bar .settings-control .settings-btn .icon svg{width:18px;height:18px}.player-top-bar .settings-control .settings-menu{min-width:200px;max-height:400px}}@media(max-width: 480px){.player-top-bar{padding:10px 12px}.player-top-bar .top-bar-title{margin-right:10px;max-width:calc(100% - 60px)}.player-top-bar .top-bar-title .video-title{font-size:14px}.player-top-bar .top-bar-title .video-subtitle{font-size:12px}.player-top-bar .settings-control .settings-btn{padding:6px}.player-top-bar .settings-control .settings-btn .icon svg{width:16px;height:16px}.player-top-bar .settings-control .settings-menu{min-width:180px;max-height:300px}}.video-wrapper:fullscreen .player-top-bar,.video-wrapper:-webkit-full-screen .player-top-bar,.video-wrapper:-moz-full-screen .player-top-bar{padding:20px 30px}.video-wrapper:fullscreen .player-top-bar .top-bar-title .video-title,.video-wrapper:-webkit-full-screen .player-top-bar .top-bar-title .video-title,.video-wrapper:-moz-full-screen .player-top-bar .top-bar-title .video-title{font-size:22px}.video-wrapper:fullscreen .player-top-bar .settings-control .settings-btn,.video-wrapper:-webkit-full-screen .player-top-bar .settings-control .settings-btn,.video-wrapper:-moz-full-screen .player-top-bar .settings-control .settings-btn{padding:12px}.title-text{color:var(--player-text-color);font-size:18px;font-weight:600;line-height:1.3;margin:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:0 2px 8px rgba(0,0,0,.7);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.subtitle-text{color:var(--player-text-color);font-size:14px;font-weight:400;line-height:1.3;margin:5px 0 0 0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:0 2px 8px rgba(0,0,0,.7);opacity:.9;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.chapter-name{font-size:13px;font-weight:500;color:hsla(0,0%,100%,.9);margin-top:6px;max-width:400px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:opacity .3s}.controls{position:absolute;bottom:0;left:0;right:0;background:rgba(0, 0, 0, var(--control-bar-opacity, 0.95));padding:var(--player-controls-padding);opacity:0;transform:translateY(100%);transition:all var(--player-transition-normal);z-index:10;min-height:70px !important;box-sizing:border-box}.controls.show{opacity:1;transform:translateY(0);position:absolute !important;bottom:0 !important;z-index:20 !important}.play-icon svg,.pause-icon svg,.volume-icon svg,.mute-icon svg,.playlist-prev-btn .icon svg,.playlist-next-btn .icon svg,.subtitles-btn .icon svg,.fullscreen-icon svg,.exit-fullscreen-icon svg,.pip-icon svg,.pip-exit-icon svg{width:16px;height:16px;display:block}.controls-main{display:flex;justify-content:space-between;align-items:center;width:100%;min-height:44px !important;flex-shrink:0}.controls-left,.controls-right{display:flex;align-items:center;gap:8px;flex-shrink:1;min-width:0}.control-btn{background:none;border:none;color:var(--player-button-color);cursor:pointer;padding:var(--player-button-padding);border-radius:6px;display:flex;align-items:center;justify-content:center;gap:5px;transition:all var(--player-transition-fast);font-size:14px;font-weight:500;position:relative;flex-shrink:1;min-width:0;white-space:nowrap;vertical-align:middle}.control-btn:hover{background:var(--player-button-hover);transform:scale(1.05)}.control-btn:active{transform:scale(0.95);background:var(--player-button-active)}.subtitles-btn{position:relative}.quality-btn{min-height:36px;padding:6px 8px}.quality-btn-text{display:flex;flex-direction:column;align-items:center;line-height:1}.selected-quality{font-size:14px;font-weight:500;color:var(--player-button-color)}.current-quality{font-size:10px;font-weight:400;color:var(--player-text-secondary);opacity:.8;margin-top:2px;line-height:1}.time-display{color:var(--player-text-color);font-size:14px;font-weight:500;display:flex;flex-direction:column;align-items:center;justify-content:center;line-height:1.1;gap:0;font-variant-numeric:tabular-nums;flex-shrink:2;min-width:0;margin:0 5px}.time-display .duration{font-size:10px;color:var(--player-text-secondary);opacity:.8;font-weight:400}.icon{width:var(--player-icon-size);height:var(--player-icon-size);display:flex;align-items:center;justify-content:center;font-size:16px}.hidden{display:none !important}.controls-right .brand-logo{height:44px;max-width:120px;object-fit:contain;margin-right:10px;pointer-events:auto;opacity:.8;transition:opacity var(--player-transition-fast);order:-1;flex-shrink:1}.controls-right .brand-logo:hover{opacity:1}.controls-right .brand-logo-link{order:-1;margin-right:10px;display:inline-block;text-decoration:none}.controls-right .brand-logo-link .brand-logo{margin-right:0}.video-wrapper.hide-cursor{cursor:none !important}.video-wrapper.hide-cursor .controls{cursor:default !important}.video-wrapper.hide-cursor .control-btn{cursor:pointer !important}.video-wrapper.hide-cursor iframe{cursor:auto !important;pointer-events:auto !important}.play-from-start-btn .restart-icon{display:inline-flex;align-items:center;justify-content:center}.control-btn .icon{display:inline-flex;align-items:center;justify-content:center}.moreinfo-modal-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.85);display:flex;align-items:center;justify-content:center;z-index:10000;backdrop-filter:blur(5px)}.moreinfo-modal-content{background:linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);border-radius:12px;max-width:600px;width:90%;max-height:80%;display:flex;flex-direction:column;box-shadow:0 10px 40px rgba(0,0,0,.5);border:1px solid hsla(0,0%,100%,.1)}.moreinfo-modal-header{display:flex;justify-content:space-between;align-items:center;padding:20px 24px;border-bottom:1px solid hsla(0,0%,100%,.1)}.moreinfo-modal-title{margin:0;font-size:20px;font-weight:600;color:#fff;flex:1}.moreinfo-modal-close{background:rgba(0,0,0,0);border:none;color:#fff;font-size:32px;cursor:pointer;padding:0;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:background-color .2s ease;line-height:1}.moreinfo-modal-close:hover{background:hsla(0,0%,100%,.1)}.moreinfo-modal-body{padding:24px;overflow-y:auto;overflow-x:hidden;flex:1;color:#e0e0e0;font-size:14px;line-height:1.6}.moreinfo-modal-body::-webkit-scrollbar{width:8px}.moreinfo-modal-body::-webkit-scrollbar-track{background:hsla(0,0%,100%,.05);border-radius:4px}.moreinfo-modal-body::-webkit-scrollbar-thumb{background:hsla(0,0%,100%,.2);border-radius:4px}.moreinfo-modal-body::-webkit-scrollbar-thumb:hover{background:hsla(0,0%,100%,.3)}.moreinfo-modal-body{scrollbar-width:thin;scrollbar-color:hsla(0,0%,100%,.2) hsla(0,0%,100%,.05)}@media(max-width: 600px){.moreinfo-modal-content{width:95%;max-height:85%}.moreinfo-modal-header{padding:16px 20px}.moreinfo-modal-title{font-size:18px}.moreinfo-modal-body{padding:20px;font-size:13px}}.progress-container{width:100%;height:var(--player-progress-height);background:var(--player-progress-bg);border-radius:calc(var(--player-progress-height)/2);margin-bottom:15px;position:relative;cursor:pointer}.progress-bar{width:100%;height:100%;position:relative;border-radius:calc(var(--player-progress-height)/2);overflow:hidden}.progress-buffer{height:100%;background:var(--player-progress-buffer);width:0%;border-radius:calc(var(--player-progress-height)/2);transition:width var(--player-transition-fast)}.progress-filled{position:absolute;top:0;left:0;height:100%;background:var(--player-primary-color);width:0%;border-radius:calc(var(--player-progress-height)/2);transition:width .1s ease}.progress-handle{position:absolute;top:50%;transform:translate(-50%, -50%);width:var(--player-progress-handle-size);height:var(--player-progress-handle-size);background:var(--player-primary-color);border-radius:50%;opacity:1;transition:opacity var(--player-transition-fast);z-index:2;left:0%;box-shadow:0 2px 8px rgba(0,0,0,.3);pointer-events:all;touch-action:none}.progress-handle::before{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:44px;height:44px;border-radius:50%}.progress-container:hover .progress-handle{opacity:1}.progress-container:hover .progress-filled{background:var(--player-primary-hover)}.seek-tooltip{position:absolute;bottom:100%;left:0;background:rgba(0,0,0,.9);color:#fff;padding:6px 10px;border-radius:6px;font-size:12px;font-weight:500;white-space:nowrap;opacity:0;visibility:hidden;transform:translateX(-50%) translateY(-8px);transition:all .15s ease;z-index:1000;box-shadow:var(--player-shadow-tooltip);font-variant-numeric:tabular-nums;backdrop-filter:blur(8px);border:1px solid hsla(0,0%,100%,.1)}.seek-tooltip::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);width:0;height:0;border-left:5px solid rgba(0,0,0,0);border-right:5px solid rgba(0,0,0,0);border-top:5px solid rgba(0,0,0,.9)}.seek-tooltip.visible{opacity:1;visibility:visible;transform:translateX(-50%) translateY(-4px)}.chapter-markers-container{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:3}.chapter-marker{position:absolute;top:0;height:100%;width:3px;background:var(--player-primary-color);opacity:.7;cursor:pointer;pointer-events:auto;transition:all var(--player-transition-fast);border-radius:2px;transform:translateX(-50%)}.chapter-marker:hover{opacity:1;width:4px;height:120%;top:-10%;box-shadow:0 0 8px var(--player-primary-color)}.chapter-marker.active{background:var(--player-primary-hover);opacity:1;width:4px}.chapter-tooltip{position:absolute;bottom:100%;left:0;background:rgba(0,0,0,.95);backdrop-filter:blur(10px);border-radius:8px;padding:0;margin-bottom:12px;opacity:0;visibility:hidden;transition:all .2s ease;transform:translateX(-50%) translateY(-8px);z-index:1000;box-shadow:var(--player-shadow-tooltip);border:1px solid hsla(0,0%,100%,.15);min-width:200px;max-width:300px;overflow:hidden;pointer-events:none}.chapter-tooltip::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);width:0;height:0;border-left:6px solid rgba(0,0,0,0);border-right:6px solid rgba(0,0,0,0);border-top:6px solid rgba(0,0,0,.95)}.chapter-tooltip-image{width:100%;height:150px;background-size:cover;background-position:center;background-repeat:no-repeat;display:none;border-bottom:1px solid hsla(0,0%,100%,.1)}.chapter-tooltip-title{padding:10px 12px 6px;color:var(--player-text-color);font-size:14px;font-weight:600;line-height:1.3;word-wrap:break-word}.chapter-tooltip-time{padding:0 12px 10px;color:var(--player-text-secondary);font-size:12px;font-weight:400;font-variant-numeric:tabular-nums}.progress-handle-circle{border-radius:50%}.progress-handle-square{border-radius:2px}.progress-handle-diamond{border-radius:2px;transform:translate(-50%, -50%) rotate(45deg)}.progress-handle-arrow{border-radius:0;clip-path:polygon(0% 50%, 60% 0%, 60% 35%, 100% 35%, 100% 65%, 60% 65%, 60% 100%)}.progress-handle-triangle{border-radius:0;clip-path:polygon(50% 0%, 0% 100%, 100% 100%)}.progress-handle-heart{border-radius:0}.progress-handle-heart::before{content:"❤";font-size:12px;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}.progress-handle-star{border-radius:0;clip-path:polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)}.progress-handle-none{opacity:0 !important}.progress-handle::after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:44px;height:44px;border-radius:50%}.progress-container.seeking .progress-bar{height:calc(var(--player-progress-height)*2);transition:height .15s ease}.progress-container.seeking .progress-handle{transform:translate(-50%, -50%) scale(1.4);transition:transform .15s ease}@media(hover: hover)and (pointer: fine){.progress-container:hover .progress-bar{height:calc(var(--player-progress-height)*1.3);transition:height .15s ease}}.chapter-segment{box-sizing:border-box}.chapter-marker:hover{background:rgba(0,0,0,.9) !important}.chapter-tooltip{animation:fadeIn .15s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateX(-50%) translateY(-5px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}.progress-container:hover .chapter-segment{background:hsla(0,0%,100%,.4) !important}.chapter-tooltip{animation:fadeIn .15s ease-in-out}.chapter-tooltip .chapter-tooltip-content{display:flex;flex-direction:column}.chapter-tooltip .chapter-tooltip-image{background-size:cover;background-position:center;background-repeat:no-repeat;border-radius:3px}.chapter-tooltip .chapter-tooltip-title{line-height:1.3}.chapter-tooltip .chapter-tooltip-time{opacity:.8}@keyframes fadeIn{from{opacity:0;transform:translateX(-50%) translateY(-5px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}.chapter-tooltip-hover .chapter-tooltip-content{display:flex;flex-direction:column;gap:6px}.chapter-tooltip-hover .chapter-tooltip-image{width:100%;aspect-ratio:16/9;background-size:cover;background-position:center;background-repeat:no-repeat;border-radius:3px;max-width:180px}.chapter-tooltip-hover .chapter-tooltip-title{font-size:13px;font-weight:600;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chapter-tooltip-hover .chapter-tooltip-time{font-size:12px;font-weight:400;color:hsla(0,0%,100%,.8);max-width:180px}@media(max-width: 1200px){.chapter-tooltip-hover .chapter-tooltip-image{max-width:150px}.chapter-tooltip-hover .chapter-tooltip-title,.chapter-tooltip-hover .chapter-tooltip-time{max-width:150px}}@media(max-width: 768px){.chapter-tooltip-hover .chapter-tooltip-image{max-width:100px}.chapter-tooltip-hover .chapter-tooltip-title,.chapter-tooltip-hover .chapter-tooltip-time{max-width:100px}}@media(max-width: 480px){.chapter-tooltip-hover .chapter-tooltip-image{max-width:80px}.chapter-tooltip-hover .chapter-tooltip-title{font-size:11px;max-width:80px}.chapter-tooltip-hover .chapter-tooltip-time{font-size:10px;max-width:80px}}.volume-container{display:flex;align-items:center;gap:8px;position:relative;flex-shrink:2;min-width:0}.volume-slider{width:60px;height:var(--player-volume-height);background:var(--player-volume-bg);border-radius:calc(var(--player-volume-height)/2);outline:none;cursor:pointer;-webkit-appearance:none;transition:all var(--player-transition-fast)}.volume-tooltip{position:absolute;bottom:210%;transition:opacity .15s ease,transform .15s ease;left:0;transform:translateX(-50%);background:rgba(0,0,0,.9);color:#fff;padding:6px 10px;border-radius:6px;font-size:12px;font-weight:500;white-space:nowrap;opacity:0;visibility:hidden;z-index:1000;box-shadow:var(--player-shadow-tooltip);pointer-events:none;backdrop-filter:blur(8px);border:1px solid hsla(0,0%,100%,.1)}.volume-tooltip::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);width:0;height:0;border-left:6px solid rgba(0,0,0,0);border-right:6px solid rgba(0,0,0,0);border-top:6px solid rgba(0,0,0,.9)}.volume-container:hover .volume-tooltip,.volume-tooltip.visible{opacity:1;visibility:visible}.volume-slider::-webkit-slider-thumb{-webkit-appearance:none;width:var(--player-volume-handle-size);height:var(--player-volume-handle-size);border-radius:50%;background:var(--player-primary-dark);cursor:pointer;transition:all var(--player-transition-fast);box-shadow:0 2px 6px rgba(0,0,0,.2);margin-top:calc((var(--player-volume-height) - var(--player-volume-handle-size))/2);transform:translateY(0)}.volume-slider::-webkit-slider-thumb:hover{transform:translateY(0) scale(1.2);background:var(--player-primary-color)}.volume-slider::-moz-range-thumb{width:var(--player-volume-handle-size);height:var(--player-volume-handle-size);border-radius:50%;background:var(--player-primary-dark);cursor:pointer;border:none;box-shadow:0 2px 6px rgba(0,0,0,.2);transition:all var(--player-transition-fast);margin-top:0;transform:translateY(calc((var(--player-volume-height) - var(--player-volume-handle-size)) / 2))}.volume-slider::-moz-range-thumb:hover{background:var(--player-primary-color);transform:translateY(calc((var(--player-volume-height) - var(--player-volume-handle-size)) / 2)) scale(1.1)}.volume-slider::-webkit-slider-runnable-track{height:var(--player-volume-height);background:linear-gradient(to right, var(--player-primary-dark) 0%, var(--player-primary-dark) var(--player-volume-fill), var(--player-volume-bg) var(--player-volume-fill), var(--player-volume-bg) 100%);border-radius:calc(var(--player-volume-height)/2);transition:background var(--player-transition-fast);margin:0;border:none}.volume-slider::-moz-range-track{height:var(--player-volume-height);background:linear-gradient(to right, var(--player-primary-dark) 0%, var(--player-primary-dark) var(--player-volume-fill), var(--player-volume-bg) var(--player-volume-fill), var(--player-volume-bg) 100%);border-radius:calc(var(--player-volume-height)/2);border:none;transition:background var(--player-transition-fast);margin:0}.quality-control{position:relative}.subtitles-control{display:none !important}.speed-control{display:none !important}.speed-menu,.quality-menu,.subtitles-menu{position:absolute;bottom:100%;right:0;background:var(--player-bg-menu);backdrop-filter:blur(10px);border-radius:8px;padding:8px 0;margin-bottom:10px;opacity:0;visibility:hidden;transition:all var(--player-transition-fast);min-width:140px;border:1px solid hsla(0,0%,100%,.1);z-index:100;box-shadow:var(--player-shadow-menu)}.speed-menu.active,.quality-menu.active,.subtitles-menu.active{opacity:1 !important;visibility:visible !important;pointer-events:all !important}.speed-option,.quality-option,.subtitles-option{padding:8px 16px;color:var(--player-text-color);cursor:pointer;transition:all var(--player-transition-fast);font-size:14px;display:flex;align-items:center;justify-content:space-between}.speed-option:hover,.quality-option:hover,.subtitles-option:hover{background:hsla(0,0%,100%,.1);color:var(--player-primary-color)}.speed-option.active,.quality-option.active,.subtitles-option.active{color:var(--player-primary-color);font-weight:600;background:hsla(0,0%,100%,.05)}.subtitles-option.selected,.subtitles-option.active{color:var(--player-primary-color);background:hsla(0,0%,100%,.1);position:relative}.subtitles-option.selected::after,.subtitles-option.active::after{content:"✓";position:absolute;right:10px;font-weight:bold}.quality-option.selected{color:var(--player-primary-color);font-weight:600}.quality-option.selected::after{content:"Selected";font-size:12px;color:var(--player-primary-color);font-weight:400;margin-left:8px}.quality-option.playing{background:hsla(0,0%,100%,.05)}.quality-option.playing::after{content:"Playing";font-size:12px;color:#4caf50;font-weight:400;margin-left:8px}.quality-option.selected.playing::after{content:"Active";font-size:12px;color:var(--player-primary-color);font-weight:500;margin-left:8px}.subtitles-option.active::after{content:"✓";font-size:12px;color:var(--player-primary-color)}.settings-control{position:relative;display:block !important}.settings-btn{background:none;border:none;color:var(--player-button-color);cursor:pointer;padding:var(--player-button-padding);border-radius:6px;display:flex;align-items:center;gap:5px;transition:all var(--player-transition-fast);font-size:14px;font-weight:500;position:relative;flex-shrink:0;min-width:0;white-space:nowrap}.settings-btn:hover{background:var(--player-button-hover);transform:scale(1.05)}.settings-btn:active{transform:scale(0.95);background:var(--player-button-active)}.settings-menu{position:absolute;bottom:100%;right:0;background:var(--player-bg-menu);backdrop-filter:blur(10px);border-radius:8px;padding:8px 0;margin-bottom:10px;opacity:0;visibility:hidden;transition:all var(--player-transition-fast);min-width:180px;border:1px solid hsla(0,0%,100%,.1);z-index:100;box-shadow:var(--player-shadow-menu);max-height:200px;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;scrollbar-color:var(--player-primary-color) hsla(0,0%,100%,.05)}.settings-menu.active{opacity:1 !important;visibility:visible !important;pointer-events:all !important}.settings-menu::-webkit-scrollbar{width:8px}.settings-menu::-webkit-scrollbar-track{background:hsla(0,0%,100%,.05);border-radius:10px}.settings-menu::-webkit-scrollbar-thumb{background:var(--player-primary-color);border-radius:10px;border:2px solid rgba(0,0,0,0);background-clip:content-box}.settings-menu::-webkit-scrollbar-thumb:hover{background:var(--player-primary-hover, var(--player-primary-color));background-clip:content-box}.settings-option{padding:8px 16px;color:var(--player-text-color);cursor:pointer;transition:all var(--player-transition-fast);font-size:14px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid hsla(0,0%,100%,.05);position:relative}.settings-option:last-child{border-bottom:none}.settings-option:hover{background:hsla(0,0%,100%,.1);color:var(--player-primary-color)}.settings-option-label{display:flex;align-items:center;gap:8px;flex:1}.settings-option-value{font-size:12px;color:var(--player-text-secondary);opacity:.8}.settings-expandable-wrapper{position:relative;display:block}.settings-option.expandable-trigger{display:flex;justify-content:space-between;align-items:center;cursor:pointer}.settings-option.expandable-trigger .settings-option-label{font-size:14px}.settings-option.expandable-trigger .expand-arrow{font-size:12px;transition:transform .2s ease;margin-left:8px}.settings-option.expandable-trigger:hover{background:hsla(0,0%,100%,.1)}.settings-expandable-content{padding-left:15px;margin-top:4px;display:none;background:rgba(0,0,0,.3);border-left:3px solid var(--player-primary-color)}.settings-expandable-content.active{display:block}.settings-suboption{padding:8px 12px;cursor:pointer;color:#fff;font-size:13px;white-space:normal;word-wrap:break-word;opacity:.8;transition:opacity .2s;display:flex;align-items:center;justify-content:space-between}.settings-suboption:hover{opacity:1;background:hsla(0,0%,100%,.1)}.settings-suboption.active{opacity:1;font-weight:bold;color:var(--player-primary-color)}.settings-suboption.active::after{content:"✓";font-size:12px}@media(max-width: 768px){.settings-menu>.settings-option{font-size:13px}.settings-suboption{font-size:12px;padding:7px 10px}}@media(max-width: 600px){.settings-menu>.settings-option{font-size:12px}.settings-suboption{font-size:11px;padding:6px 8px}}@media(max-width: 450px){.settings-menu>.settings-option{font-size:11px}.settings-suboption{font-size:10px;padding:5px 6px}}@media(max-width: 350px){.settings-control{display:block !important}.pip-btn{display:none !important}}.audio-player{width:320px;height:80px}.audio-player video{display:none !important}.audio-player .controls-wrapper{height:60px}.audio-player .audio-wave-canvas{display:block;width:100%;height:60px;background-color:#222;border-radius:4px;margin-top:5px}.player-theme-blue{--player-primary-color: #2196F3;--player-primary-hover: #1976D2;--player-primary-dark: #1565C0}.player-theme-green{--player-primary-color: #4CAF50;--player-primary-hover: #45a049;--player-primary-dark: #388e3c}.player-theme-red{--player-primary-color: #f44336;--player-primary-hover: #d32f2f;--player-primary-dark: #c62828}.video-watermark{position:absolute;z-index:15;pointer-events:auto;opacity:.7;transition:opacity .3s ease,visibility .3s ease,bottom .3s ease}.video-watermark{visibility:visible;opacity:.7}.video-wrapper:not(.has-controls) .video-watermark.hide-on-autohide{visibility:hidden;opacity:0}.video-wrapper.has-controls .video-watermark{visibility:visible;opacity:.7}.video-watermark:hover{opacity:1}.video-watermark img{display:block;max-width:150px;max-height:80px;width:auto;height:auto;object-fit:contain}.video-watermark.watermark-topleft{top:15px;left:15px}.video-watermark.watermark-topright{top:15px;right:15px}.video-watermark.watermark-bottomleft{bottom:calc(var(--player-controls-height, 70px) + 15px);left:15px}.video-watermark.watermark-bottomright{bottom:calc(var(--player-controls-height, 70px) + 15px);right:15px}.video-wrapper:not(.has-controls) .video-watermark.watermark-bottomleft:not(.hide-on-autohide){bottom:15px}.video-wrapper:not(.has-controls) .video-watermark.watermark-bottomright:not(.hide-on-autohide){bottom:15px}.video-wrapper.has-controls .video-watermark.watermark-bottomleft,.video-wrapper.has-controls .video-watermark.watermark-bottomright{bottom:calc(var(--player-controls-height, 70px) + 15px)}@media(max-width: 768px){.video-watermark img{max-width:100px;max-height:50px}.video-watermark.watermark-topleft,.video-watermark.watermark-topright{top:10px}.video-watermark.watermark-topleft,.video-watermark.watermark-bottomleft{left:10px}.video-watermark.watermark-topright,.video-watermark.watermark-bottomright{right:10px}.video-watermark.watermark-bottomleft,.video-watermark.watermark-bottomright{bottom:calc(var(--player-controls-height, 60px) + 10px)}.video-wrapper:not(.has-controls) .video-watermark.watermark-bottomleft:not(.hide-on-autohide),.video-wrapper:not(.has-controls) .video-watermark.watermark-bottomright:not(.hide-on-autohide){bottom:10px}}@media(max-width: 480px){.video-watermark.watermark-bottomleft,.video-watermark.watermark-bottomright{bottom:calc(var(--player-controls-height, 55px) + 10px)}.video-wrapper:not(.has-controls) .video-watermark.watermark-bottomleft:not(.hide-on-autohide),.video-wrapper:not(.has-controls) .video-watermark.watermark-bottomright:not(.hide-on-autohide){bottom:8px}}.video-watermark[style*="cursor: pointer"]{cursor:pointer !important}@media(max-width: 768px){.controls-left,.controls-right{gap:8px}.volume-slider{width:50px}.time-display{font-size:12px}.icon{font-size:14px}.control-btn{padding:6px}.quality-btn{min-height:32px;padding:4px 6px}.selected-quality{font-size:12px}.current-quality{font-size:9px}.seek-tooltip{font-size:11px;padding:4px 8px}.title-overlay{padding:12px 15px 20px}.title-text{font-size:16px}.video-player::cue{font-size:16px;padding:6px 10px}.controls-right .brand-logo{height:36px;max-width:100px;margin-right:8px}}@media(max-width: 480px){.controls-left,.controls-right{gap:6px}.progress-container{margin-bottom:10px}.controls-main{padding-top:6px}.volume-container{flex-shrink:3}.volume-slider{width:35px}.quality-btn{min-height:28px;padding:3px 5px}.selected-quality{font-size:11px}.current-quality{font-size:8px}.seek-tooltip{font-size:10px;padding:3px 6px}.title-overlay{padding:10px 12px 18px}.title-text{font-size:14px}.video-player::cue{font-size:14px;padding:4px 8px}.controls-right .brand-logo{height:28px;max-width:80px;margin-right:5px}}@media(max-width: 350px){.controls-left,.controls-right{gap:4px}.control-btn{padding:4px}.icon{font-size:12px}.quality-btn{min-height:24px;padding:2px 4px}.selected-quality{font-size:10px}.current-quality{font-size:7px}.controls-right .brand-logo{height:22px;max-width:50px;margin-right:3px}.volume-slider{width:30px}.settings-menu{min-width:160px;font-size:12px}.settings-option{padding:6px 12px;font-size:12px}.settings-submenu{min-width:130px}.settings-suboption{padding:6px 12px;font-size:11px}}@media(max-width: 280px){.controls-left,.controls-right{gap:3px}.control-btn{padding:3px}.icon{font-size:10px}.quality-btn{min-height:20px;padding:1px 3px}.selected-quality{font-size:9px}.current-quality{font-size:6px}.controls-right .brand-logo{height:18px;max-width:40px;margin-right:2px}.volume-slider{width:25px}.settings-menu{min-width:140px;font-size:11px}.settings-option{padding:5px 10px;font-size:11px}.settings-submenu{min-width:120px}.settings-suboption{padding:5px 10px;font-size:10px}}@media(max-width: 600px){.controls-main{overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}.controls-main::-webkit-scrollbar{display:none}.controls-left,.controls-right{flex-wrap:nowrap;white-space:nowrap;flex-shrink:1;min-width:fit-content}}.controls-right .playlist-prev-btn,.controls-right .playlist-next-btn{display:none}.controls-right .playlist-prev-btn.playlist-active,.controls-right .playlist-next-btn.playlist-active{display:flex}.playlist-prev-btn .icon::before{content:"⏮"}.playlist-next-btn .icon::before{content:"⏭"}.playlist-prev-btn:disabled,.playlist-next-btn:disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.playlist-prev-btn:disabled .icon,.playlist-next-btn:disabled .icon{opacity:.5}@media(max-width: 768px){.playlist-prev-btn .icon::before,.playlist-next-btn .icon::before{font-size:16px}}@media(max-width: 480px){.playlist-prev-btn .icon::before,.playlist-next-btn .icon::before{font-size:14px}}.video-container:fullscreen,.video-container:-webkit-full-screen,.video-container:-moz-full-screen{width:100vw;height:100vh;border-radius:0}@keyframes qualityChange{0%{opacity:.7}50%{opacity:.3}100%{opacity:1}}.quality-changing{animation:qualityChange .5s ease-in-out}.control-btn:focus{outline:2px solid var(--player-primary-color);outline-offset:2px}.volume-slider:focus{outline:2px solid var(--player-primary-color);outline-offset:2px}.player-large-controls{--player-icon-size: 24px;--player-button-padding: 12px;--player-progress-height: 8px;--player-progress-handle-size: 20px;--player-title-overlay-padding: 18px 24px 30px}.player-compact-controls{--player-icon-size: 16px;--player-button-padding: 4px;--player-controls-padding: 15px 10px 10px;--player-title-overlay-padding: 12px 16px 20px}@-moz-document url-prefix(){.volume-slider::-moz-range-thumb{transform:translateY(calc((var(--player-volume-height) - var(--player-volume-handle-size)) / 2 - 2px))}.volume-slider::-moz-range-thumb:hover{transform:translateY(calc((var(--player-volume-height) - var(--player-volume-handle-size)) / 2 - 2px)) scale(1.1)}.volume-slider::-moz-range-track{height:var(--player-volume-height);background:linear-gradient(to right, var(--player-primary-dark) 0%, var(--player-primary-dark) var(--player-volume-fill), var(--player-volume-bg) var(--player-volume-fill), var(--player-volume-bg) 100%);border-radius:calc(var(--player-volume-height)/2);border:none;transition:background var(--player-transition-fast);margin:0}}@supports(-moz-appearance: none){.volume-slider{margin-top:-1px}.volume-slider::-moz-range-thumb{transform:translateY(calc((var(--player-volume-height) - var(--player-volume-handle-size)) / 2 - 1.5px))}.volume-slider::-moz-range-thumb:hover{transform:translateY(calc((var(--player-volume-height) - var(--player-volume-handle-size)) / 2 - 1.5px)) scale(1.1)}}@-moz-document url-prefix(){.volume-container{position:relative;top:2px !important}.volume-slider::-moz-range-thumb{margin-top:-6px !important;transform:translateY(-2px) !important}.volume-slider::-moz-range-thumb:hover{transform:translateY(-2px) scale(1.1) !important}}.video-player{object-position:center center}.resolution-normal .video-player{object-fit:contain;object-position:center center}.resolution-4-3 .video-player{object-fit:fill;aspect-ratio:4/3}.resolution-4-3 .video-wrapper{aspect-ratio:4/3}.resolution-16-9 .video-player{object-fit:fill;aspect-ratio:16/9}.resolution-16-9 .video-wrapper{aspect-ratio:16/9}.resolution-stretched .video-player{object-fit:fill;width:100%;height:100%}.resolution-stretched .video-wrapper{height:auto;min-height:300px}.resolution-fit-to-screen .video-player{object-fit:cover;object-position:center center;width:100%;height:100%}.resolution-fit-to-screen .video-wrapper{height:100vh;max-height:100vh}.resolution-scale-to-fit .video-player{object-fit:contain;object-position:center center;width:100%;height:100%;max-width:100vw;max-height:100vh}.resolution-scale-to-fit .video-wrapper{display:flex;align-items:center;justify-content:center;height:70vh;min-height:400px;background:var(--player-bg-primary, #000)}@media(orientation: portrait){.resolution-scale-to-fit .video-wrapper{height:50vh;min-height:350px}}@media(orientation: landscape){.resolution-scale-to-fit .video-wrapper{height:80vh;min-height:450px}}@media(max-width: 768px){.resolution-fit-to-screen .video-wrapper{height:50vh;min-height:250px}.resolution-4-3 .video-wrapper,.resolution-16-9 .video-wrapper{min-height:200px}.resolution-scale-to-fit .video-wrapper{height:45vh;min-height:300px}}@media(max-width: 480px){.resolution-fit-to-screen .video-wrapper{height:40vh;min-height:200px}.resolution-4-3 .video-wrapper,.resolution-16-9 .video-wrapper{min-height:180px}.resolution-scale-to-fit .video-wrapper{height:40vh;min-height:250px}}.video-player{transition:object-fit .3s ease,aspect-ratio .3s ease}.video-wrapper{transition:aspect-ratio .3s ease,height .3s ease}@supports not (aspect-ratio: 1){.resolution-4-3 .video-wrapper{padding-bottom:75%;height:0;position:relative}.resolution-4-3 .video-player{position:absolute;top:0;left:0;width:100%;height:100%}.resolution-16-9 .video-wrapper{padding-bottom:56.25%;height:0;position:relative}.resolution-16-9 .video-player{position:absolute;top:0;left:0;width:100%;height:100%}}.quality-changing .video-player{filter:brightness(0.7)}.resolution-debug .video-wrapper::before{content:"Resolution: " attr(data-resolution);position:absolute;top:10px;left:10px;background:rgba(0,0,0,.7);color:#fff;padding:5px 10px;border-radius:4px;font-size:12px;z-index:1000;pointer-events:none}.controls,.controls-main,.controls-left,.controls-right{overflow:visible !important}.controls-left,.controls-right{flex-wrap:nowrap !important;white-space:nowrap !important}.control-btn{min-width:0 !important;white-space:nowrap !important}video::cue{background-color:rgba(0,0,0,.8);color:#fff;font-family:Arial,Helvetica,sans-serif;font-size:18px;font-weight:normal;line-height:1.2;text-shadow:1px 1px 1px rgba(0,0,0,.8);padding:4px 8px;border-radius:4px;white-space:pre-line}video::-webkit-media-text-track-display{color:#fff;font-family:Arial,Helvetica,sans-serif;background-color:rgba(0,0,0,.8);border-radius:4px;padding:4px 8px;font-size:18px;text-shadow:1px 1px 1px rgba(0,0,0,.8)}.custom-subtitle-overlay{font-size:clamp(12px,4vw,18px)}@media(max-width: 768px){.custom-subtitle-overlay{font-size:16px !important;bottom:70px !important;max-width:85% !important;padding:6px 12px !important;line-height:1.2 !important}}@media(max-width: 480px){.custom-subtitle-overlay{font-size:14px !important;bottom:60px !important;max-width:90% !important;padding:5px 10px !important;line-height:1.15 !important}}@media(max-width: 360px){.custom-subtitle-overlay{font-size:12px !important;bottom:50px !important;max-width:95% !important;padding:4px 8px !important}}@media(max-height: 500px)and (orientation: landscape){.custom-subtitle-overlay{font-size:13px !important;bottom:45px !important;max-width:85% !important;padding:4px 10px !important}}.speed-menu,.quality-menu,.subtitles-menu{max-height:200px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--player-primary-color) hsla(0,0%,100%,.1)}.speed-menu::-webkit-scrollbar,.quality-menu::-webkit-scrollbar,.subtitles-menu::-webkit-scrollbar{width:6px}.speed-menu::-webkit-scrollbar-track,.quality-menu::-webkit-scrollbar-track,.subtitles-menu::-webkit-scrollbar-track{background:hsla(0,0%,100%,.1);border-radius:3px}.speed-menu::-webkit-scrollbar-thumb,.quality-menu::-webkit-scrollbar-thumb,.subtitles-menu::-webkit-scrollbar-thumb{background:var(--player-primary-color);border-radius:3px}.speed-menu::-webkit-scrollbar-thumb:hover,.quality-menu::-webkit-scrollbar-thumb:hover,.subtitles-menu::-webkit-scrollbar-thumb:hover{background:var(--player-primary-hover)}@media(max-height: 400px){.speed-menu,.quality-menu,.subtitles-menu{max-height:150px}}@media(max-height: 300px){.speed-menu,.quality-menu,.subtitles-menu{max-height:120px}}.settings-submenu{max-height:180px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--player-primary-color) hsla(0,0%,100%,.1)}.settings-submenu::-webkit-scrollbar{width:6px}.settings-submenu::-webkit-scrollbar-track{background:hsla(0,0%,100%,.1);border-radius:3px}.settings-submenu::-webkit-scrollbar-thumb{background:var(--player-primary-color);border-radius:3px}.settings-submenu::-webkit-scrollbar-thumb:hover{background:var(--player-primary-hover)}@media(max-width: 350px){.settings-submenu{max-height:140px}}@media(max-height: 400px){.settings-submenu{max-height:120px}}.volume-container{position:relative;display:flex;align-items:center;gap:var(--player-controls-gap)}.volume-container[data-mobile-slider=show] .volume-slider{width:80px;height:var(--player-volume-height);background:var(--player-volume-bg);border-radius:calc(var(--player-volume-height)/2);transition:all .3s ease}@media(max-width: 1200px){.volume-container[data-mobile-slider=show] .volume-slider{width:70px}}@media(max-width: 900px){.volume-container[data-mobile-slider=show] .volume-slider{width:60px}}@media(max-width: 768px){.volume-container[data-mobile-slider=show] .volume-slider{width:50px}}@media(max-width: 600px){.volume-container[data-mobile-slider=show] .volume-slider{width:40px}}@media(max-width: 550px){.volume-container[data-mobile-slider=show] .volume-tooltip{display:none !important}.volume-container[data-mobile-slider=show]{pointer-events:auto !important;position:relative}.mute-btn{position:relative;z-index:100;pointer-events:auto !important}.volume-container[data-mobile-slider=show] .volume-slider{position:absolute;opacity:0;visibility:hidden;pointer-events:none;width:0;height:0;transform:translateX(-100%);transition:opacity 0s ease,visibility 0s ease 2s,width 0s ease 2s}.controls-left:hover .volume-container[data-mobile-slider=show] .volume-slider,.mute-btn:hover~.volume-container[data-mobile-slider=show] .volume-slider,.volume-container[data-mobile-slider=show]:hover .volume-slider,.volume-slider:hover{position:absolute;opacity:1;visibility:visible;pointer-events:auto !important;width:90px !important;height:auto;bottom:auto;top:50%;left:5px;transform:translateY(-50%);z-index:19;background:rgba(0,0,0,.92) !important;border:1px solid hsla(0,0%,100%,.15);border-radius:8px;padding:10px 14px;box-shadow:0 4px 16px rgba(0,0,0,.6);backdrop-filter:blur(10px);transition:opacity .2s ease,visibility 0s ease,width .2s ease}.controls-left:has(.volume-container[data-mobile-slider=show]):hover{pointer-events:auto !important}.controls-left:hover .volume-slider::-webkit-slider-runnable-track,.volume-container[data-mobile-slider=show]:hover .volume-slider::-webkit-slider-runnable-track,.volume-slider:hover::-webkit-slider-runnable-track{width:60px;height:4px !important;background:linear-gradient(to right, var(--player-primary-color) 0%, var(--player-primary-color) var(--player-volume-fill, 50%), rgba(255, 255, 255, 0.4) var(--player-volume-fill, 50%), rgba(255, 255, 255, 0.4) 100%) !important;border-radius:2px}.controls-left:hover .volume-slider::-webkit-slider-thumb,.volume-container[data-mobile-slider=show]:hover .volume-slider::-webkit-slider-thumb,.volume-slider:hover::-webkit-slider-thumb{opacity:1 !important;visibility:visible !important;-webkit-appearance:none;width:14px;height:14px;border-radius:50%;background:#fff;cursor:pointer;box-shadow:0 2px 6px rgba(0,0,0,.5);margin-top:-5px}}.chapter-markers-container{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:3}.chapter-marker{position:absolute;top:0;height:100%;width:3px;background:var(--player-primary-color);opacity:.7;cursor:pointer;pointer-events:auto;transition:all var(--player-transition-fast);border-radius:2px;transform:translateX(-50%)}.chapter-marker:hover{opacity:1;width:4px;height:120%;top:-10%;box-shadow:0 0 8px var(--player-primary-color)}.chapter-marker.active{background:var(--player-primary-hover);opacity:1;width:4px}.chapter-tooltip{position:absolute;bottom:100%;left:0;background:rgba(0,0,0,.95);backdrop-filter:blur(10px);border-radius:8px;padding:0;margin-bottom:12px;opacity:0;visibility:hidden;transition:all .2s ease;transform:translateX(-50%) translateY(-8px);z-index:1000;box-shadow:var(--player-shadow-tooltip);border:1px solid hsla(0,0%,100%,.15);min-width:200px;max-width:300px;overflow:hidden;pointer-events:none}.chapter-tooltip::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);width:0;height:0;border-left:6px solid rgba(0,0,0,0);border-right:6px solid rgba(0,0,0,0);border-top:6px solid rgba(0,0,0,.95)}.chapter-tooltip-image{width:100%;height:150px;background-size:cover;background-position:center;background-repeat:no-repeat;display:none;border-bottom:1px solid hsla(0,0%,100%,.1)}.chapter-tooltip-title{padding:10px 12px 6px;color:var(--player-text-color);font-size:14px;font-weight:600;line-height:1.3;word-wrap:break-word}.chapter-tooltip-time{padding:0 12px 10px;color:var(--player-text-secondary);font-size:12px;font-weight:400;font-variant-numeric:tabular-nums}@media(max-width: 480px){.chapter-marker{width:2px}.chapter-marker:hover{width:3px}.chapter-tooltip{min-width:160px;max-width:250px}.chapter-tooltip-image{height:100px}}.video-poster-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background-size:cover;background-position:center;background-repeat:no-repeat;z-index:1;cursor:pointer;opacity:0;visibility:hidden;transition:opacity .3s ease,visibility .3s ease;pointer-events:none}.video-poster-overlay.visible{opacity:1;visibility:visible;pointer-events:auto}.video-poster-overlay.hidden{opacity:0;visibility:hidden;pointer-events:none}.video-poster-overlay.visible:hover{opacity:.95}.video-poster-overlay::after{content:"";position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:80px;height:80px;background:rgba(0,0,0,.7);border-radius:50%;border:3px solid var(--player-primary-color);opacity:0;transition:opacity .3s ease,transform .3s ease,border-color .3s ease;pointer-events:none}.video-poster-overlay.visible:hover::after{opacity:1;transform:translate(-50%, -50%) scale(1.1);border-color:var(--player-primary-hover);box-shadow:0 0 20px var(--player-primary-color)}.video-poster-overlay::before{content:"";position:absolute;top:50%;left:50%;transform:translate(-40%, -50%);width:0;height:0;border-style:solid;border-width:15px 0 15px 25px;border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) var(--player-primary-color);z-index:2;opacity:0;transition:opacity .3s ease,border-color .3s ease;pointer-events:none}.video-poster-overlay.visible:hover::before{opacity:1;border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) var(--player-primary-hover)}@media(max-width: 768px){.video-poster-overlay::after{width:60px;height:60px}.video-poster-overlay::before{border-width:12px 0 12px 20px}}@media(max-width: 480px){.video-poster-overlay::after{width:50px;height:50px}.video-poster-overlay::before{border-width:10px 0 10px 16px}}.video-poster-overlay.hidden{transition:opacity .5s ease,visibility 0s ease .5s}.player-theme-blue .video-poster-overlay::after{border-color:#2196f3}.player-theme-blue .video-poster-overlay.visible:hover::after{border-color:#1976d2;box-shadow:0 0 20px #2196f3}.player-theme-blue .video-poster-overlay::before{border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) #2196f3}.player-theme-blue .video-poster-overlay.visible:hover::before{border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) #1976d2}.player-theme-green .video-poster-overlay::after{border-color:#4caf50}.player-theme-green .video-poster-overlay.visible:hover::after{border-color:#45a049;box-shadow:0 0 20px #4caf50}.player-theme-green .video-poster-overlay::before{border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) #4caf50}.player-theme-green .video-poster-overlay.visible:hover::before{border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) #45a049}.player-theme-red .video-poster-overlay::after{border-color:#f44336}.player-theme-red .video-poster-overlay.visible:hover::after{border-color:#d32f2f;box-shadow:0 0 20px #f44336}.player-theme-red .video-poster-overlay::before{border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) #f44336}.player-theme-red .video-poster-overlay.visible:hover::before{border-color:rgba(0,0,0,0) rgba(0,0,0,0) rgba(0,0,0,0) #d32f2f}.player-theme-dark .video-poster-overlay::after{background:rgba(0,0,0,.85)}@media(max-height: 400px){.video-player{min-height:200px}.controls{min-height:50px !important;padding:10px 10px 8px !important}.progress-container{margin-bottom:8px}.controls-main{min-height:32px !important}}@media(max-height: 330px){.video-player{min-height:150px}.controls{min-height:45px !important;padding:8px 8px 6px !important}.progress-container{margin-bottom:6px;height:4px}.controls-main{min-height:28px !important}.control-btn{padding:4px !important}.icon{font-size:14px !important}.time-display{font-size:11px !important}}@media(max-height: 250px){.video-player{min-height:120px}.controls{min-height:40px !important;padding:6px 8px 5px !important}.progress-container{margin-bottom:4px;height:3px}.controls-main{min-height:24px !important}.control-btn{padding:2px !important}.icon{font-size:12px !important}.time-display{font-size:10px !important}.quality-btn{min-height:20px !important;padding:2px 4px !important}.selected-quality{font-size:9px !important}.current-quality{display:none}.volume-slider{width:40px !important}}.video-container,.video-wrapper{overflow:visible !important}.controls.show{position:absolute !important;bottom:0 !important;overflow:visible !important}
|
package/dist/myetv-player.js
CHANGED
|
@@ -700,6 +700,7 @@ constructor(videoElement, options = {}) {
|
|
|
700
700
|
brandLogoUrl: '', // URL for brand logo image
|
|
701
701
|
brandLogoLinkUrl: '', // Optional URL to open when clicking the logo
|
|
702
702
|
brandLogoTooltipText: '', // Tooltip text for brand logo
|
|
703
|
+
loadingLogo: null, // URL image to show inside the loading circle
|
|
703
704
|
playlistEnabled: true, // Enable/disable playlist detection
|
|
704
705
|
playlistAutoPlay: true, // Auto-play next video when current ends
|
|
705
706
|
playlistLoop: false, // Loop playlist when reaching the end
|
|
@@ -1337,24 +1338,40 @@ createPlayerStructure() {
|
|
|
1337
1338
|
createInitialLoading() {
|
|
1338
1339
|
const initialLoader = document.createElement('div');
|
|
1339
1340
|
initialLoader.className = 'initial-loading';
|
|
1340
|
-
initialLoader.innerHTML =
|
|
1341
|
+
initialLoader.innerHTML = `
|
|
1342
|
+
<div class="loading-spinner-wrap">
|
|
1343
|
+
<div class="loading-spinner"></div>
|
|
1344
|
+
${this.options.loadingLogo
|
|
1345
|
+
? `<img class="loading-spinner-logo" src="${this.options.loadingLogo}" alt="" />`
|
|
1346
|
+
: ''}
|
|
1347
|
+
</div>
|
|
1348
|
+
<div class="loading-text"></div>
|
|
1349
|
+
`;
|
|
1341
1350
|
this.container.appendChild(initialLoader);
|
|
1342
1351
|
this.initialLoading = initialLoader;
|
|
1343
1352
|
}
|
|
1344
1353
|
|
|
1345
|
-
collectVideoQualities() {
|
|
1346
|
-
if (this.options.debug) console.log('📁 Video qualities will be loaded with restored sources');
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
1354
|
createLoadingOverlay() {
|
|
1350
1355
|
const overlay = document.createElement('div');
|
|
1351
1356
|
overlay.className = 'loading-overlay';
|
|
1352
|
-
overlay.id =
|
|
1353
|
-
overlay.innerHTML =
|
|
1357
|
+
overlay.id = `loadingOverlay-${this.getUniqueId()}`;
|
|
1358
|
+
overlay.innerHTML = `
|
|
1359
|
+
<div class="loading-spinner-wrap">
|
|
1360
|
+
<div class="loading-spinner"></div>
|
|
1361
|
+
${this.options.loadingLogo
|
|
1362
|
+
? `<img class="loading-spinner-logo" src="${this.options.loadingLogo}" alt="" />`
|
|
1363
|
+
: ''}
|
|
1364
|
+
</div>
|
|
1365
|
+
<div class="loading-text"></div>
|
|
1366
|
+
`;
|
|
1354
1367
|
this.container.appendChild(overlay);
|
|
1355
1368
|
this.loadingOverlay = overlay;
|
|
1356
1369
|
}
|
|
1357
1370
|
|
|
1371
|
+
collectVideoQualities() {
|
|
1372
|
+
if (this.options.debug) console.log('📁 Video qualities will be loaded with restored sources');
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1358
1375
|
updateTooltips() {
|
|
1359
1376
|
if (!this.controls) return;
|
|
1360
1377
|
|
package/dist/myetv-player.min.js
CHANGED
|
@@ -655,6 +655,7 @@ constructor(videoElement, options = {}) {
|
|
|
655
655
|
brandLogoUrl: '', // URL for brand logo image
|
|
656
656
|
brandLogoLinkUrl: '', // Optional URL to open when clicking the logo
|
|
657
657
|
brandLogoTooltipText: '', // Tooltip text for brand logo
|
|
658
|
+
loadingLogo: null, // URL image to show inside the loading circle
|
|
658
659
|
playlistEnabled: true, // Enable/disable playlist detection
|
|
659
660
|
playlistAutoPlay: true, // Auto-play next video when current ends
|
|
660
661
|
playlistLoop: false, // Loop playlist when reaching the end
|
|
@@ -1251,24 +1252,40 @@ createPlayerStructure() {
|
|
|
1251
1252
|
createInitialLoading() {
|
|
1252
1253
|
const initialLoader = document.createElement('div');
|
|
1253
1254
|
initialLoader.className = 'initial-loading';
|
|
1254
|
-
initialLoader.innerHTML =
|
|
1255
|
+
initialLoader.innerHTML = `
|
|
1256
|
+
<div class="loading-spinner-wrap">
|
|
1257
|
+
<div class="loading-spinner"></div>
|
|
1258
|
+
${this.options.loadingLogo
|
|
1259
|
+
? `<img class="loading-spinner-logo" src="${this.options.loadingLogo}" alt="" />`
|
|
1260
|
+
: ''}
|
|
1261
|
+
</div>
|
|
1262
|
+
<div class="loading-text"></div>
|
|
1263
|
+
`;
|
|
1255
1264
|
this.container.appendChild(initialLoader);
|
|
1256
1265
|
this.initialLoading = initialLoader;
|
|
1257
1266
|
}
|
|
1258
1267
|
|
|
1259
|
-
collectVideoQualities() {
|
|
1260
|
-
if (this.options.debug) console.log('📁 Video qualities will be loaded with restored sources');
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
1268
|
createLoadingOverlay() {
|
|
1264
1269
|
const overlay = document.createElement('div');
|
|
1265
1270
|
overlay.className = 'loading-overlay';
|
|
1266
|
-
overlay.id =
|
|
1267
|
-
overlay.innerHTML =
|
|
1271
|
+
overlay.id = `loadingOverlay-${this.getUniqueId()}`;
|
|
1272
|
+
overlay.innerHTML = `
|
|
1273
|
+
<div class="loading-spinner-wrap">
|
|
1274
|
+
<div class="loading-spinner"></div>
|
|
1275
|
+
${this.options.loadingLogo
|
|
1276
|
+
? `<img class="loading-spinner-logo" src="${this.options.loadingLogo}" alt="" />`
|
|
1277
|
+
: ''}
|
|
1278
|
+
</div>
|
|
1279
|
+
<div class="loading-text"></div>
|
|
1280
|
+
`;
|
|
1268
1281
|
this.container.appendChild(overlay);
|
|
1269
1282
|
this.loadingOverlay = overlay;
|
|
1270
1283
|
}
|
|
1271
1284
|
|
|
1285
|
+
collectVideoQualities() {
|
|
1286
|
+
if (this.options.debug) console.log('📁 Video qualities will be loaded with restored sources');
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1272
1289
|
updateTooltips() {
|
|
1273
1290
|
if (!this.controls) return;
|
|
1274
1291
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "myetv-player",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "MYETV Video Player - Modular HTML5 video player with plugin support for YouTube, Vimeo, Twitch, Facebook, Cloudflare Stream and streaming protocols (HLS/DASH)",
|
|
5
5
|
"main": "dist/myetv-player.js",
|
|
6
6
|
"files": [
|
|
@@ -71,3 +71,4 @@
|
|
|
71
71
|
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
|
|
@@ -220,7 +220,16 @@
|
|
|
220
220
|
`;
|
|
221
221
|
|
|
222
222
|
const ICON_CC = `<svg viewBox="0 0 24 24"><path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-9 8H9.5v-.5h-2v3h2V14H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V14H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"/></svg>`;
|
|
223
|
-
const ICON_LOADING = `<svg viewBox="0 0 24 24"
|
|
223
|
+
const ICON_LOADING = `<svg viewBox="0 0 24 24">
|
|
224
|
+
<!-- cerchio rotante -->
|
|
225
|
+
<path d="M12 2A10 10 0 0 1 22 12" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none">
|
|
226
|
+
<animateTransform attributeName="transform" type="rotate" from="0 12 12" to="360 12 12" dur="1s" repeatCount="indefinite"/>
|
|
227
|
+
</path>
|
|
228
|
+
<!-- icona CC fissa al centro, scala ridotta -->
|
|
229
|
+
<g transform="translate(4, 5) scale(0.65)">
|
|
230
|
+
<path d="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-9 8H9.5v-.5h-2v3h2V14H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V14H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z"/>
|
|
231
|
+
</g>
|
|
232
|
+
</svg>`;
|
|
224
233
|
|
|
225
234
|
const WHISPER_MODELS = {
|
|
226
235
|
tiny: 'Xenova/whisper-tiny',
|
|
@@ -289,6 +298,7 @@
|
|
|
289
298
|
cacheEnabled: true,
|
|
290
299
|
idCache: null,
|
|
291
300
|
autoTranslation: null, // ISO 2-letter code, e.g. 'en', 'it'
|
|
301
|
+
translationEngine: null, // null = MyMemory (default), oppure { type: 'libretranslate', url: 'https://...', apiKey: '' }
|
|
292
302
|
}, options);
|
|
293
303
|
|
|
294
304
|
this.isGenerating = false;
|
|
@@ -326,26 +336,31 @@
|
|
|
326
336
|
}
|
|
327
337
|
|
|
328
338
|
_scheduleAutoTranslation() {
|
|
329
|
-
// Light polling: waits until this.subtitles is populated (from cache or transcription),
|
|
330
|
-
// then triggers translation automatically
|
|
331
339
|
const lang = this.opts.autoTranslation.toLowerCase().trim().slice(0, 2);
|
|
332
|
-
let tries = 0;
|
|
333
340
|
|
|
341
|
+
// Set translateLang immediately so chunk_done incremental translation kicks in right away
|
|
342
|
+
this.translateLang = lang;
|
|
343
|
+
|
|
344
|
+
let tries = 0;
|
|
334
345
|
const check = setInterval(() => {
|
|
335
346
|
tries++;
|
|
336
|
-
|
|
347
|
+
// Wait until transcription is fully complete, then do a full translation pass
|
|
348
|
+
// to catch any chunks that may have been missed or arrived out of order
|
|
349
|
+
if (this.subtitles.length > 0 && !this.isGenerating) {
|
|
337
350
|
clearInterval(check);
|
|
338
|
-
if (this.player.options.debug)
|
|
339
|
-
|
|
351
|
+
if (this.player.options.debug) console.log('[AutoSub] autoTranslation final pass, lang:', lang);
|
|
352
|
+
// Reset and retranslate everything cleanly at the end
|
|
353
|
+
this.subtitlesTrans = [];
|
|
354
|
+
this._transCache = {};
|
|
340
355
|
this._setTranslationLang(lang);
|
|
341
356
|
this._updateMenu();
|
|
342
357
|
return;
|
|
343
358
|
}
|
|
344
|
-
// Stop waiting after 5 minutes (enough for any transcription, even large videos with slow models)
|
|
345
359
|
if (tries > 300) clearInterval(check);
|
|
346
360
|
}, 1000);
|
|
347
361
|
}
|
|
348
362
|
|
|
363
|
+
|
|
349
364
|
async handleButtonClick() {
|
|
350
365
|
// If cache is enabled and no subtitles are loaded yet, try loading from cache first
|
|
351
366
|
if (this.subtitles.length === 0 && !this.isGenerating) {
|
|
@@ -370,40 +385,71 @@
|
|
|
370
385
|
async generate() {
|
|
371
386
|
if (this.isGenerating) return;
|
|
372
387
|
|
|
373
|
-
|
|
388
|
+
let partialCache = null;
|
|
389
|
+
|
|
374
390
|
if (this.subtitles.length === 0) {
|
|
375
391
|
const cached = this._loadFromCache();
|
|
376
392
|
if (cached) {
|
|
377
|
-
this.subtitles = cached;
|
|
393
|
+
this.subtitles = cached.subtitles;
|
|
378
394
|
this.subVisible = true;
|
|
379
395
|
this._startDisplay();
|
|
380
396
|
this._setBtnActive(true);
|
|
381
397
|
this._updateMenu();
|
|
398
|
+
|
|
399
|
+
if (cached.chunksCompleted === null || cached.chunksCompleted >= cached.totalChunks) {
|
|
400
|
+
if (this.player.options.debug) console.log('[AutoSub] Full cache hit, skipping transcription');
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
partialCache = cached; // Pass to transcribeChunksStreaming
|
|
382
405
|
if (this.player.options.debug)
|
|
383
|
-
console.log('[AutoSub]
|
|
384
|
-
return;
|
|
406
|
+
console.log('[AutoSub] Partial cache found — resuming from chunk', cached.chunksCompleted, 'of', cached.totalChunks);
|
|
385
407
|
}
|
|
386
408
|
}
|
|
387
409
|
|
|
388
410
|
this.isGenerating = true;
|
|
389
411
|
this._setBtnGenerating(true);
|
|
390
412
|
this._updatePanel('Extracting audio...', 5);
|
|
391
|
-
|
|
392
413
|
try {
|
|
393
|
-
|
|
394
|
-
this.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
this.
|
|
414
|
+
this.isGenerating = true;
|
|
415
|
+
this._setBtnGenerating(true);
|
|
416
|
+
this._updatePanel('Extracting audio...', 5);
|
|
417
|
+
try {
|
|
418
|
+
const audioData = await this._extractAudio();
|
|
419
|
+
this._updatePanel('Downloading Transformers.js bundle...', 30);
|
|
420
|
+
await this._ensureBundle();
|
|
421
|
+
this._updatePanel('Preparing Whisper worker...', 38);
|
|
422
|
+
await this._transcribeChunksStreaming(audioData, partialCache);
|
|
423
|
+
|
|
424
|
+
// Sort subtitles by start time — partial cache + new chunks may be unsorted
|
|
425
|
+
this.subtitles.sort((a, b) => a.start - b.start);
|
|
426
|
+
|
|
427
|
+
// Mark cache as complete
|
|
428
|
+
this._saveToCache(this.subtitles, null, null, this._lastDetectedLang || null);
|
|
429
|
+
this._updatePanel('Subtitles ready!', 100);
|
|
402
430
|
this._setBtnActive(true);
|
|
403
431
|
this._updateMenu();
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
432
|
+
|
|
433
|
+
// Re-trigger translation if one was active, using the now-complete subtitles
|
|
434
|
+
if (this.translateLang && this.translateLang !== 'off') {
|
|
435
|
+
const lang = this.translateLang;
|
|
436
|
+
this.translateLang = 'off'; // reset so setTranslationLang doesn't skip
|
|
437
|
+
this.subtitlesTrans = [];
|
|
438
|
+
this._transCache = {};
|
|
439
|
+
await this._setTranslationLang(lang); // re-translate with full subtitles
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
setTimeout(() => { this._closePanel(); }, 800);
|
|
443
|
+
|
|
444
|
+
if (this.player.options.debug) console.log('[AutoSub] Generation complete:', this.subtitles.length, 'segments');
|
|
445
|
+
} catch (err) {
|
|
446
|
+
if (this.player.options.debug) console.error('[AutoSub] Generation error:', err);
|
|
447
|
+
this._updatePanel('Error: ' + (err.message || String(err)), 0);
|
|
448
|
+
} finally {
|
|
449
|
+
this.isGenerating = false;
|
|
450
|
+
this._setBtnGenerating(false);
|
|
451
|
+
}
|
|
452
|
+
|
|
407
453
|
} catch (err) {
|
|
408
454
|
if (this.player.options.debug) console.error('[AutoSub] Generation error:', err);
|
|
409
455
|
this._updatePanel('Error: ' + (err.message || String(err)), 0);
|
|
@@ -742,11 +788,12 @@
|
|
|
742
788
|
|
|
743
789
|
const genLabel = isGenerating ? '⏳ Generating...' : hasSubs ? '🔄 Regenerate transcription' : '🎙️ Generate subtitles';
|
|
744
790
|
const itemGen = this._menuItem(genLabel, false, isGenerating, () => {
|
|
791
|
+
this._deleteCache();
|
|
745
792
|
this.subtitles = [];
|
|
746
793
|
this.subtitlesTrans = [];
|
|
747
|
-
this.
|
|
748
|
-
this.
|
|
749
|
-
this.
|
|
794
|
+
this.transCache = {};
|
|
795
|
+
this.closeMenuNow();
|
|
796
|
+
this._generate();
|
|
750
797
|
});
|
|
751
798
|
|
|
752
799
|
const itemPanel = this._menuItem('📊 Show progress', false, !isGenerating, () => {
|
|
@@ -831,13 +878,21 @@
|
|
|
831
878
|
|
|
832
879
|
async _setTranslationLang(langCode) {
|
|
833
880
|
this.translateLang = langCode;
|
|
834
|
-
|
|
835
881
|
if (langCode === 'off') {
|
|
836
882
|
this.subtitlesTrans = [];
|
|
837
883
|
if (this.player.options.debug) console.log('[AutoSub] Translation disabled');
|
|
838
884
|
return;
|
|
839
885
|
}
|
|
840
886
|
|
|
887
|
+
// Skip if source language equals target language
|
|
888
|
+
const sourceLang = this._resolveLanguage(this.opts.language);
|
|
889
|
+
if (sourceLang && sourceLang === langCode.slice(0, 2)) {
|
|
890
|
+
if (this.player.options.debug) console.log('[AutoSub] Source = target lang, skipping translation');
|
|
891
|
+
this.subtitlesTrans = [];
|
|
892
|
+
this.translateLang = 'off';
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
|
|
841
896
|
// Already cached in memory?
|
|
842
897
|
if (this._transCache[langCode]) {
|
|
843
898
|
this.subtitlesTrans = this._transCache[langCode];
|
|
@@ -863,61 +918,138 @@
|
|
|
863
918
|
}
|
|
864
919
|
}
|
|
865
920
|
|
|
921
|
+
async _translateText(text, sourceLang, targetLang) {
|
|
922
|
+
const engine = this.opts.translationEngine;
|
|
923
|
+
|
|
924
|
+
// ── MyMemory (default) ──────────────────────────────────────────────
|
|
925
|
+
if (!engine || engine.type === 'mymemory') {
|
|
926
|
+
const url = `https://api.mymemory.translated.net/get?q=${encodeURIComponent(text)}&langpair=${sourceLang}|${targetLang}`;
|
|
927
|
+
const resp = await fetch(url);
|
|
928
|
+
const json = await resp.json();
|
|
929
|
+
if (json.responseStatus && json.responseStatus !== 200) {
|
|
930
|
+
throw new Error('MyMemory error: ' + json.responseDetails + ' (status ' + json.responseStatus + ')');
|
|
931
|
+
}
|
|
932
|
+
return json.responseData?.translatedText ?? text;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// ── LibreTranslate ──────────────────────────────────────────────────
|
|
936
|
+
if (engine.type === 'libretranslate') {
|
|
937
|
+
const body = { q: text, source: sourceLang, target: targetLang, format: 'text' };
|
|
938
|
+
if (engine.apiKey) body.api_key = engine.apiKey;
|
|
939
|
+
const resp = await fetch(engine.url.replace(/\/$/, '') + '/translate', {
|
|
940
|
+
method: 'POST',
|
|
941
|
+
headers: { 'Content-Type': 'application/json' },
|
|
942
|
+
body: JSON.stringify(body)
|
|
943
|
+
});
|
|
944
|
+
if (!resp.ok) throw new Error('LibreTranslate HTTP ' + resp.status);
|
|
945
|
+
const json = await resp.json();
|
|
946
|
+
if (json.error) throw new Error('LibreTranslate: ' + json.error);
|
|
947
|
+
return json.translatedText ?? text;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// ── MarianMT (Argos Translate API / custom REST endpoint) ───────────
|
|
951
|
+
if (engine.type === 'marianmt') {
|
|
952
|
+
const resp = await fetch(engine.url.replace(/\/$/, '') + '/translate', {
|
|
953
|
+
method: 'POST',
|
|
954
|
+
headers: { 'Content-Type': 'application/json' },
|
|
955
|
+
body: JSON.stringify({ q: text, source: sourceLang, target: targetLang })
|
|
956
|
+
});
|
|
957
|
+
if (!resp.ok) throw new Error('MarianMT HTTP ' + resp.status);
|
|
958
|
+
const json = await resp.json();
|
|
959
|
+
return json.translatedText ?? json.translation ?? text;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
throw new Error('Unknown translation engine: ' + engine.type);
|
|
963
|
+
}
|
|
964
|
+
|
|
866
965
|
async _translateBatch(segments, targetLang) {
|
|
966
|
+
const SEP = ' ||| ';
|
|
867
967
|
const BATCH_CHARS = 400;
|
|
868
|
-
const SEP = '\n||||\n';
|
|
869
968
|
|
|
870
|
-
//
|
|
871
|
-
// 1. Use the language set in plugin options (if provided)
|
|
872
|
-
// 2. Otherwise try to detect it from the first segment via MyMemory
|
|
873
|
-
// 3. Absolute fallback: 'en'
|
|
969
|
+
// Detect source language
|
|
874
970
|
let sourceLang = null;
|
|
875
|
-
|
|
876
971
|
if (this.opts.language) {
|
|
877
|
-
// Normalize: 'italian' → 'it', 'it' → 'it'
|
|
878
972
|
const lower = this.opts.language.toLowerCase().trim();
|
|
879
|
-
// Reverse lookup in LANGUAGE_MAP (value → key)
|
|
880
973
|
const found = Object.entries(LANGUAGE_MAP).find(([k, v]) => k === lower || v === lower);
|
|
881
|
-
sourceLang = found ? found[0] : lower.slice(0, 2);
|
|
974
|
+
sourceLang = found ? found[0] : lower.slice(0, 2);
|
|
882
975
|
}
|
|
883
|
-
|
|
884
976
|
if (!sourceLang && segments.length > 0) {
|
|
885
|
-
// Try to detect language from the first segment via MyMemory
|
|
886
977
|
try {
|
|
887
978
|
const sample = segments[0].text.slice(0, 100);
|
|
888
979
|
const detectUrl = `https://api.mymemory.translated.net/get?q=${encodeURIComponent(sample)}&langpair=en|en`;
|
|
889
980
|
const resp = await fetch(detectUrl);
|
|
890
981
|
const json = await resp.json();
|
|
891
|
-
// MyMemory returns the detected language in responseData
|
|
892
982
|
const detected = json.responseData?.detectedLanguage;
|
|
893
|
-
if (
|
|
894
|
-
|
|
895
|
-
}
|
|
896
|
-
} catch (_) { }
|
|
983
|
+
if (/[a-z]{2}-[A-Z]{2}/.test(detected)) sourceLang = detected.slice(0, 2);
|
|
984
|
+
} catch { }
|
|
897
985
|
}
|
|
986
|
+
if (!sourceLang) sourceLang = 'en';
|
|
898
987
|
|
|
899
|
-
|
|
988
|
+
// Normalize target to 2-letter code
|
|
989
|
+
const normTarget = LANGUAGE_MAP[targetLang]
|
|
990
|
+
? targetLang
|
|
991
|
+
: (Object.entries(LANGUAGE_MAP).find(([k, v]) => v === targetLang)?.[0] ?? targetLang.slice(0, 2));
|
|
900
992
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
if (this.player.options.debug)
|
|
904
|
-
console.log('[AutoSub] Source === target lang, skipping translation');
|
|
993
|
+
if (sourceLang === normTarget) {
|
|
994
|
+
if (this.player.options.debug) console.log('[AutoSub] Source = target lang, skipping translation');
|
|
905
995
|
return segments.map(s => ({ ...s }));
|
|
906
996
|
}
|
|
907
997
|
|
|
908
|
-
if (this.player.options.debug)
|
|
909
|
-
console.log('[AutoSub] Translating', sourceLang, '→', targetLang);
|
|
998
|
+
if (this.player.options.debug) console.log('[AutoSub] Translating', sourceLang, '→', normTarget);
|
|
910
999
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1000
|
+
const engine = this.opts.translationEngine;
|
|
1001
|
+
|
|
1002
|
+
// ── LibreTranslate: array nativo, mapping 1:1 garantito ──────────
|
|
1003
|
+
if (engine?.type === 'libretranslate') {
|
|
1004
|
+
const BATCH_SIZE = 50;
|
|
1005
|
+
const result = [];
|
|
1006
|
+
const url = `${engine.url.replace(/\/$/, '')}/translate`;
|
|
915
1007
|
|
|
1008
|
+
for (let i = 0; i < segments.length; i += BATCH_SIZE) {
|
|
1009
|
+
const batch = segments.slice(i, i + BATCH_SIZE);
|
|
1010
|
+
try {
|
|
1011
|
+
const body = {
|
|
1012
|
+
q: batch.map(s => s.text), // array → risposta è array nella stessa posizione
|
|
1013
|
+
source: sourceLang,
|
|
1014
|
+
target: normTarget,
|
|
1015
|
+
format: 'text'
|
|
1016
|
+
};
|
|
1017
|
+
if (engine.apiKey) body.api_key = engine.apiKey;
|
|
1018
|
+
|
|
1019
|
+
const resp = await fetch(url, {
|
|
1020
|
+
method: 'POST',
|
|
1021
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1022
|
+
body: JSON.stringify(body)
|
|
1023
|
+
});
|
|
1024
|
+
if (!resp.ok) throw new Error(`LibreTranslate HTTP ${resp.status}`);
|
|
1025
|
+
const json = await resp.json();
|
|
1026
|
+
if (json.error) throw new Error(`LibreTranslate: ${json.error}`);
|
|
1027
|
+
|
|
1028
|
+
const translations = Array.isArray(json.translatedText)
|
|
1029
|
+
? json.translatedText
|
|
1030
|
+
: [json.translatedText];
|
|
1031
|
+
|
|
1032
|
+
batch.forEach((seg, j) => result.push({
|
|
1033
|
+
start: seg.start,
|
|
1034
|
+
end: seg.end,
|
|
1035
|
+
text: translations[j] ?? seg.text
|
|
1036
|
+
}));
|
|
1037
|
+
} catch (err) {
|
|
1038
|
+
if (this.player.options.debug) console.warn('[AutoSub] LibreTranslate batch error', err.message);
|
|
1039
|
+
batch.forEach(seg => result.push({ ...seg }));
|
|
1040
|
+
}
|
|
1041
|
+
if (i + BATCH_SIZE < segments.length)
|
|
1042
|
+
await new Promise(r => setTimeout(r, 100));
|
|
1043
|
+
}
|
|
1044
|
+
return result;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// ── MyMemory fallback ────────────────────────────────────────────
|
|
1048
|
+
const batches = [];
|
|
1049
|
+
let current = [], charCount = 0;
|
|
916
1050
|
for (const seg of segments) {
|
|
917
1051
|
if (charCount + seg.text.length > BATCH_CHARS && current.length > 0) {
|
|
918
|
-
batches.push(current);
|
|
919
|
-
current = [];
|
|
920
|
-
charCount = 0;
|
|
1052
|
+
batches.push(current); current = []; charCount = 0;
|
|
921
1053
|
}
|
|
922
1054
|
current.push(seg);
|
|
923
1055
|
charCount += seg.text.length;
|
|
@@ -927,31 +1059,22 @@
|
|
|
927
1059
|
const result = [];
|
|
928
1060
|
for (const batch of batches) {
|
|
929
1061
|
const sourceText = batch.map(s => s.text).join(SEP);
|
|
930
|
-
const url = `https://api.mymemory.translated.net/get?q=${encodeURIComponent(sourceText)}&langpair=${sourceLang}|${targetLang}`;
|
|
931
1062
|
try {
|
|
932
|
-
const
|
|
933
|
-
const
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
const translated = json.responseData?.translatedText || sourceText;
|
|
943
|
-
const parts = translated.split('||||').map(s => s.trim().replace(/^\n+|\n+$/g, ''));
|
|
944
|
-
batch.forEach((seg, i) => {
|
|
945
|
-
result.push({ start: seg.start, end: seg.end, text: parts[i] || seg.text });
|
|
946
|
-
});
|
|
947
|
-
await new Promise(r => setTimeout(r, 120)); // rate-limit delay between batches
|
|
948
|
-
} catch (_) {
|
|
949
|
-
batch.forEach(seg => result.push({ ...seg })); // fallback: keep original text
|
|
1063
|
+
const translated = await this._translateText(sourceText, sourceLang, normTarget);
|
|
1064
|
+
const parts = translated.split(SEP).map(s => s.trim().replace(/&/g, '&'));
|
|
1065
|
+
batch.forEach((seg, i) => result.push({
|
|
1066
|
+
start: seg.start, end: seg.end, text: parts[i] ?? seg.text
|
|
1067
|
+
}));
|
|
1068
|
+
} catch (err) {
|
|
1069
|
+
if (this.player.options.debug) console.warn('[AutoSub] Translation batch error', err.message);
|
|
1070
|
+
batch.forEach(seg => result.push({ ...seg }));
|
|
950
1071
|
}
|
|
1072
|
+
await new Promise(r => setTimeout(r, 120));
|
|
951
1073
|
}
|
|
952
1074
|
return result;
|
|
953
1075
|
}
|
|
954
1076
|
|
|
1077
|
+
|
|
955
1078
|
// ─────────────────────────────────────────────────────────────────────────
|
|
956
1079
|
// PRIVATE — DISPLAY
|
|
957
1080
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -1417,19 +1540,18 @@ self.onmessage = async function(e) {
|
|
|
1417
1540
|
}
|
|
1418
1541
|
if (type === 'transcribe') {
|
|
1419
1542
|
try {
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
result
|
|
1426
|
-
chunkIndex: payload.chunkIndex, totalChunks: payload.totalChunks,
|
|
1427
|
-
isLast: payload.isLast
|
|
1428
|
-
}});
|
|
1543
|
+
const audio = new Float32Array(payload.audio);
|
|
1544
|
+
const opts = { return_timestamps: true, task: 'transcribe', chunk_length_s: payload.chunkSec };
|
|
1545
|
+
if (payload.lang) opts.language = payload.lang;
|
|
1546
|
+
const result = await transcriber(audio, opts);
|
|
1547
|
+
// Include detected language in the response so the main thread can reuse it
|
|
1548
|
+
const detectedLang = result.language || payload.lang || null;
|
|
1549
|
+
self.postMessage({ type: 'chunk_done', payload: { result, timeOffset: payload.timeOffset, chunkIndex: payload.chunkIndex, totalChunks: payload.totalChunks, isLast: payload.isLast, detectedLang } });
|
|
1429
1550
|
} catch(err) {
|
|
1430
|
-
|
|
1551
|
+
self.postMessage({ type: 'error', payload: err.message || String(err) });
|
|
1431
1552
|
}
|
|
1432
|
-
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1433
1555
|
};
|
|
1434
1556
|
`;
|
|
1435
1557
|
|
|
@@ -1455,7 +1577,7 @@ self.onmessage = async function(e) {
|
|
|
1455
1577
|
);
|
|
1456
1578
|
}
|
|
1457
1579
|
|
|
1458
|
-
async _transcribeChunksStreaming(audioData) {
|
|
1580
|
+
async _transcribeChunksStreaming(audioData, partialCache = null) {
|
|
1459
1581
|
|
|
1460
1582
|
// ── CAPTURE STREAM MODE (HLS or DASH fallback) ───────────────────────
|
|
1461
1583
|
if (audioData?._captureMode) {
|
|
@@ -1547,13 +1669,21 @@ self.onmessage = async function(e) {
|
|
|
1547
1669
|
|
|
1548
1670
|
if (type === 'chunk_done') {
|
|
1549
1671
|
const { result, timeOffset, isLast } = payload;
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1672
|
+
|
|
1673
|
+
let rawChunks = (result.chunks || [])
|
|
1674
|
+
.filter(c => c.text?.trim())
|
|
1675
|
+
.map(c => {
|
|
1676
|
+
const ts0 = (c.timestamp?.[0] != null) ? c.timestamp[0] : 0;
|
|
1677
|
+
const ts1 = (c.timestamp?.[1] != null) ? c.timestamp[1] : ts0 + 5;
|
|
1678
|
+
return { text: c.text.trim(), start: ts0 + timeOffset, end: ts1 + timeOffset };
|
|
1679
|
+
});
|
|
1680
|
+
|
|
1681
|
+
if (rawChunks.length === 0 && result.text?.trim()) {
|
|
1682
|
+
rawChunks = [{ text: result.text.trim(), start: timeOffset, end: timeOffset + 30 }];
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1555
1685
|
this.subtitles.push(...this._normalizeChunks(rawChunks));
|
|
1556
|
-
if (this.opts.cacheEnabled) this._saveToCache(this.subtitles);
|
|
1686
|
+
if (this.opts.cacheEnabled) this._saveToCache(this.subtitles, null, null);
|
|
1557
1687
|
|
|
1558
1688
|
activeJob = false;
|
|
1559
1689
|
|
|
@@ -1593,8 +1723,17 @@ self.onmessage = async function(e) {
|
|
|
1593
1723
|
const chunkSize = SAMPLE_RATE * CHUNK_SEC;
|
|
1594
1724
|
const totalChunks = Math.ceil(float32Audio.length / chunkSize);
|
|
1595
1725
|
const lang = this._resolveLanguage(this.opts.language);
|
|
1726
|
+
let detectedLang = lang || partialCache?.detectedLang || null;
|
|
1596
1727
|
const modelId = WHISPER_MODELS[this.opts.modelSize] || WHISPER_MODELS.base;
|
|
1597
1728
|
|
|
1729
|
+
// Use partialCache passed from generate() — no second localStorage read
|
|
1730
|
+
const startChunk = (partialCache?.chunksCompleted != null && partialCache.chunksCompleted < totalChunks)
|
|
1731
|
+
? partialCache.chunksCompleted
|
|
1732
|
+
: 0;
|
|
1733
|
+
|
|
1734
|
+
if (this.player.options.debug && startChunk > 0)
|
|
1735
|
+
console.log('[AutoSub] Resuming transcription from chunk', startChunk + 1, 'of', totalChunks);
|
|
1736
|
+
|
|
1598
1737
|
if (!this.subVisible) { this.subVisible = true; this._startDisplay(); }
|
|
1599
1738
|
|
|
1600
1739
|
const worker = this._createTranscriberWorker();
|
|
@@ -1619,28 +1758,73 @@ self.onmessage = async function(e) {
|
|
|
1619
1758
|
if (type === 'ready') {
|
|
1620
1759
|
URL.revokeObjectURL(worker._bundleURL);
|
|
1621
1760
|
URL.revokeObjectURL(worker._workerURL);
|
|
1622
|
-
this.
|
|
1623
|
-
this.
|
|
1761
|
+
if (this.player.options.debug) console.log('[AutoSub] Worker ready — sending chunk', startChunk + 1, 'of', totalChunks);
|
|
1762
|
+
this._updatePanel('Transcribing chunk ' + (startChunk + 1) + ' of ' + totalChunks + '...', 64);
|
|
1763
|
+
this._sendChunk(worker, float32Audio, startChunk, chunkSize, totalChunks, detectedLang, CHUNK_SEC);
|
|
1624
1764
|
}
|
|
1625
1765
|
|
|
1626
1766
|
if (type === 'chunk_done') {
|
|
1627
1767
|
const { result, timeOffset, chunkIndex } = payload;
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1768
|
+
|
|
1769
|
+
// On first processed chunk, save the detected language and reuse it for all subsequent chunks
|
|
1770
|
+
if (chunkIndex === startChunk && payload.detectedLang && !detectedLang) {
|
|
1771
|
+
detectedLang = payload.detectedLang;
|
|
1772
|
+
if (this.player.options.debug) console.log('[AutoSub] Language auto-detected:', detectedLang);
|
|
1773
|
+
}
|
|
1774
|
+
this._saveToCache(this.subtitles, chunkIndex + 1, totalChunks, detectedLang);
|
|
1775
|
+
|
|
1776
|
+
let rawChunks = (result.chunks || [])
|
|
1777
|
+
.filter(c => c.text?.trim())
|
|
1778
|
+
.map(c => {
|
|
1779
|
+
const ts0 = (c.timestamp?.[0] != null) ? c.timestamp[0] : 0;
|
|
1780
|
+
const ts1 = (c.timestamp?.[1] != null) ? c.timestamp[1] : ts0 + 5;
|
|
1781
|
+
return { text: c.text.trim(), start: ts0 + timeOffset, end: ts1 + timeOffset };
|
|
1782
|
+
});
|
|
1783
|
+
|
|
1784
|
+
if (rawChunks.length === 0 && result.text?.trim()) {
|
|
1785
|
+
rawChunks = [{ text: result.text.trim(), start: timeOffset, end: timeOffset + CHUNK_SEC }];
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
const normalized = this._normalizeChunks(rawChunks);
|
|
1789
|
+
this.subtitles.push(...normalized);
|
|
1790
|
+
|
|
1791
|
+
// Save partial progress after every chunk
|
|
1792
|
+
this._saveToCache(this.subtitles, chunkIndex + 1, totalChunks, detectedLang);
|
|
1793
|
+
|
|
1794
|
+
// If translation is active, translate this chunk immediately and append
|
|
1795
|
+
if (this.translateLang && this.translateLang !== 'off' && normalized.length > 0) {
|
|
1796
|
+
const currentLang = this.translateLang;
|
|
1797
|
+
this._translateBatch(normalized, currentLang).then(translated => {
|
|
1798
|
+
// Only append if translation lang hasn't changed in the meantime
|
|
1799
|
+
if (this.translateLang === currentLang) {
|
|
1800
|
+
this.subtitlesTrans.push(...translated);
|
|
1801
|
+
this.subtitlesTrans.sort((a, b) => a.start - b.start);
|
|
1802
|
+
}
|
|
1803
|
+
}).catch(() => {
|
|
1804
|
+
// On error push originals so display doesn't go blank
|
|
1805
|
+
if (this.translateLang === currentLang) {
|
|
1806
|
+
this.subtitlesTrans.push(...normalized);
|
|
1807
|
+
this.subtitlesTrans.sort((a, b) => a.start - b.start);
|
|
1808
|
+
}
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1635
1812
|
|
|
1636
1813
|
const next = chunkIndex + 1;
|
|
1637
1814
|
if (next < totalChunks) {
|
|
1638
1815
|
const pct = 64 + Math.round((next / totalChunks) * 33);
|
|
1639
1816
|
this._updatePanel('Transcribing chunk ' + (next + 1) + ' of ' + totalChunks + '...', pct);
|
|
1640
|
-
|
|
1817
|
+
// Use detectedLang instead of lang so all chunks use the same detected language
|
|
1818
|
+
this._sendChunk(worker, float32Audio, next, chunkSize, totalChunks, detectedLang, CHUNK_SEC);
|
|
1641
1819
|
} else {
|
|
1642
|
-
|
|
1820
|
+
if (this.player.options.debug) console.log('[AutoSub] All chunks done, subtitles:', this.subtitles.length);
|
|
1821
|
+
worker.terminate(); this._worker = null;
|
|
1822
|
+
resolve();
|
|
1823
|
+
// generate() chiamerà _saveToCache(subtitles, null, null, detectedLang)
|
|
1824
|
+
// ma detectedLang non è accessibile lì — salvalo su this
|
|
1825
|
+
this._lastDetectedLang = detectedLang;
|
|
1643
1826
|
}
|
|
1827
|
+
|
|
1644
1828
|
}
|
|
1645
1829
|
|
|
1646
1830
|
if (type === 'error') {
|
|
@@ -1652,6 +1836,7 @@ self.onmessage = async function(e) {
|
|
|
1652
1836
|
};
|
|
1653
1837
|
|
|
1654
1838
|
worker.postMessage({ type: 'init', payload: { modelId } });
|
|
1839
|
+
if (this.player.options.debug) console.log('[AutoSub] Init sent (normal), modelId:', modelId, 'totalChunks:', totalChunks, 'startChunk:', startChunk);
|
|
1655
1840
|
});
|
|
1656
1841
|
}
|
|
1657
1842
|
|
|
@@ -1720,18 +1905,42 @@ self.onmessage = async function(e) {
|
|
|
1720
1905
|
if (!raw) return null;
|
|
1721
1906
|
const parsed = JSON.parse(raw);
|
|
1722
1907
|
if (!Array.isArray(parsed.subtitles) || !parsed.subtitles.length) return null;
|
|
1723
|
-
return
|
|
1724
|
-
|
|
1908
|
+
return {
|
|
1909
|
+
subtitles: parsed.subtitles,
|
|
1910
|
+
chunksCompleted: parsed.chunksCompleted ?? null,
|
|
1911
|
+
totalChunks: parsed.totalChunks ?? null,
|
|
1912
|
+
detectedLang: parsed.detectedLang ?? null // ← aggiunto
|
|
1913
|
+
};
|
|
1914
|
+
} catch {
|
|
1915
|
+
return null;
|
|
1916
|
+
}
|
|
1725
1917
|
}
|
|
1726
1918
|
|
|
1727
|
-
_saveToCache(subtitles) {
|
|
1919
|
+
_saveToCache(subtitles, chunksCompleted = null, totalChunks = null, detectedLang = null) {
|
|
1728
1920
|
if (!this.opts.cacheEnabled || !subtitles.length) return;
|
|
1729
1921
|
try {
|
|
1730
1922
|
const key = this._getCacheKey();
|
|
1731
|
-
const data = JSON.stringify({
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1923
|
+
const data = JSON.stringify({
|
|
1924
|
+
subtitles,
|
|
1925
|
+
chunksCompleted,
|
|
1926
|
+
totalChunks,
|
|
1927
|
+
detectedLang,
|
|
1928
|
+
savedAt: Date.now()
|
|
1929
|
+
});
|
|
1930
|
+
try {
|
|
1931
|
+
localStorage.setItem(key, data);
|
|
1932
|
+
} catch {
|
|
1933
|
+
this._evictOldestCache();
|
|
1934
|
+
try { localStorage.setItem(key, data); } catch { /* storage full */ }
|
|
1935
|
+
}
|
|
1936
|
+
} catch { /* ignore */ }
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
_deleteCache() {
|
|
1940
|
+
if (!this.opts.cacheEnabled) return;
|
|
1941
|
+
try {
|
|
1942
|
+
localStorage.removeItem(this._getCacheKey());
|
|
1943
|
+
} catch { }
|
|
1735
1944
|
}
|
|
1736
1945
|
|
|
1737
1946
|
_evictOldestCache() {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
- 🧠 **100% client-side** — transcription runs in a Web Worker, never leaves the browser
|
|
10
10
|
- ⚡ **Streaming transcription** — subtitles appear chunk by chunk while the model processes audio
|
|
11
|
-
- 🌍 **Auto-translation** — translate subtitles
|
|
11
|
+
- 🌍 **Auto-translation** — translate subtitles via [MyMemory API](https://mymemory.translated.net/) (free, no key) or self-hosted [LibreTranslate](https://libretranslate.com/) / MarianMT (no limits)
|
|
12
12
|
- 🖱️ **Draggable subtitles** — reposition subtitles anywhere on the video (mouse & touch)
|
|
13
13
|
- 📱 **Responsive text** — font size adapts automatically to screen size via `clamp()`
|
|
14
14
|
- 💾 **localStorage cache** — transcriptions are cached and reloaded instantly on next visit
|
|
@@ -62,7 +62,7 @@ npm install myetv-autosub-plugin
|
|
|
62
62
|
|
|
63
63
|
player.on('playerready', () => {
|
|
64
64
|
player.usePlugin('autoSubtitles', {
|
|
65
|
-
language: 'en',
|
|
65
|
+
language: 'en', // source language of the video
|
|
66
66
|
modelSize: 'tiny', // whisper model size
|
|
67
67
|
position: 'topbar', // button position
|
|
68
68
|
idCache: 'video-123', // stable cache key (recommended)
|
|
@@ -82,6 +82,7 @@ npm install myetv-autosub-plugin
|
|
|
82
82
|
| `modelSize` | `string` | `'base'` | Whisper model size: `'tiny'` (~39 MB), `'base'` (~74 MB), `'small'` (~244 MB). Larger = more accurate but slower. |
|
|
83
83
|
| `autoGenerate` | `boolean` | `false` | Automatically start transcription and show subtitles when the video player is loaded. |
|
|
84
84
|
| `autoTranslation` | `string` | `null` | Automatically translate subtitles into this language on load (ISO 2-letter code, e.g. `'en'`, `'it'`, `'fr'`). Requires subtitles to be ready first. |
|
|
85
|
+
| `translationEngine` | `object` | `null` | Translation engine configuration. If `null`, uses MyMemory (free, limited). See [Translation Engines](#-translation-engines) below. |
|
|
85
86
|
| `showButton` | `boolean` | `true` | Show the CC button in the player interface. |
|
|
86
87
|
| `position` | `string` | `'topbar'` | Button position: `'right'` or `'left'` (control bar) or `'topbar'` (next to the ⚙️ settings icon). |
|
|
87
88
|
| `subtitleStyle` | `object` | `{}` | Custom inline CSS style object applied to the subtitle text element. |
|
|
@@ -89,6 +90,86 @@ npm install myetv-autosub-plugin
|
|
|
89
90
|
| `idCache` | `string` | `null` | Stable unique key used for the localStorage cache entry (recommended: your video's database ID). Falls back to a hash of the video URL if not set. |
|
|
90
91
|
|
|
91
92
|
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 🌐 Translation Engines
|
|
96
|
+
|
|
97
|
+
The plugin supports three translation backends. By default it uses **MyMemory** (no configuration needed).
|
|
98
|
+
|
|
99
|
+
### Comparison
|
|
100
|
+
|
|
101
|
+
| Engine | Limit | Privacy | Setup |
|
|
102
|
+
| :-- | :-- | :-- | :-- |
|
|
103
|
+
| **MyMemory** (default) | ⚠️ ~5.000 chars/day per IP | ☁️ External API | None |
|
|
104
|
+
| **LibreTranslate** (self-hosted) | ✅ No limits | 🔒 Your server | Easy |
|
|
105
|
+
| **MarianMT** (self-hosted) | ✅ No limits | 🔒 Your server | Medium |
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
### MyMemory (default)
|
|
111
|
+
|
|
112
|
+
Free public API, no configuration required. Limit is approximately **5.000 characters/day per IP**. Suitable for low-traffic or development use.
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// Default — no translationEngine needed
|
|
116
|
+
player.usePlugin('autoSubtitles', {
|
|
117
|
+
autoTranslation: 'it',
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
### LibreTranslate (recommended for production)
|
|
125
|
+
|
|
126
|
+
Self-hosted, no limits, supports **native array batch translation** — each subtitle segment is translated individually with guaranteed 1:1 index mapping, eliminating any desync issues.
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
player.usePlugin('autoSubtitles', {
|
|
130
|
+
autoTranslation: 'it',
|
|
131
|
+
translationEngine: {
|
|
132
|
+
type: 'libretranslate',
|
|
133
|
+
url: 'https://your-libretranslate-server.com',
|
|
134
|
+
apiKey: 'your-api-key', // optional, required if --api-keys is enabled
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Self-hosting with Docker:**
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
docker run -p 5000:5000 libretranslate/libretranslate --disable-web-ui --api-keys
|
|
143
|
+
```
|
|
144
|
+
LibreTranslate: [https://github.com/LibreTranslate/LibreTranslate](https://github.com/LibreTranslate/LibreTranslate)
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### MarianMT
|
|
148
|
+
|
|
149
|
+
Custom self-hosted REST endpoint compatible with MarianMT or Argos Translate.
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
player.usePlugin('autoSubtitles', {
|
|
153
|
+
autoTranslation: 'it',
|
|
154
|
+
translationEngine: {
|
|
155
|
+
type: 'marianmt',
|
|
156
|
+
url: 'https://your-marianmt-server.com',
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Expected request/response format:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
// POST /translate
|
|
165
|
+
// Request
|
|
166
|
+
{ "q": "Hello world", "source": "en", "target": "it" }
|
|
167
|
+
|
|
168
|
+
// Response
|
|
169
|
+
{ "translatedText": "Ciao mondo" }
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
|
|
92
173
|
---
|
|
93
174
|
|
|
94
175
|
## 🎛️ Subtitle Menu
|
|
@@ -101,10 +182,10 @@ Clicking the **CC button** opens a context menu with three sections:
|
|
|
101
182
|
- **✕ Disable** — hide subtitles
|
|
102
183
|
|
|
103
184
|
|
|
104
|
-
###
|
|
185
|
+
### Management
|
|
105
186
|
|
|
106
187
|
- **🎙️ Generate subtitles** — start transcription (first time)
|
|
107
|
-
- **🔄 Regenerate
|
|
188
|
+
- **🔄 Regenerate transcription** — force re-transcription (discards cache)
|
|
108
189
|
- **📊 Show progress** — open the transcription progress panel (visible while generating)
|
|
109
190
|
|
|
110
191
|
|
|
@@ -159,9 +240,15 @@ If your site uses a Content Security Policy, add the following:
|
|
|
159
240
|
Content-Security-Policy:
|
|
160
241
|
worker-src blob:;
|
|
161
242
|
script-src blob: https://cdn.jsdelivr.net;
|
|
162
|
-
connect-src https://cdn.jsdelivr.net
|
|
243
|
+
connect-src https://cdn.jsdelivr.net
|
|
244
|
+
https://huggingface.co
|
|
245
|
+
https://api.mymemory.translated.net
|
|
246
|
+
https://your-libretranslate-server.com;
|
|
163
247
|
```
|
|
164
|
-
|
|
248
|
+
|
|
249
|
+
> If using LibreTranslate or MarianMT, replace `https://api.mymemory.translated.net` with your server URL (or add both if using MyMemory as fallback).
|
|
250
|
+
|
|
251
|
+
Use `crossorigin="anonymous"` on the `<video>` element.
|
|
165
252
|
|
|
166
253
|
---
|
|
167
254
|
|
|
@@ -190,13 +277,18 @@ Hover over the subtitle text to reveal the **⠿ sposta** drag handle. Click and
|
|
|
190
277
|
```javascript
|
|
191
278
|
player.on('playerready', () => {
|
|
192
279
|
player.usePlugin('autoSubtitles', {
|
|
193
|
-
language: 'english',
|
|
194
|
-
modelSize: 'base',
|
|
195
|
-
autoGenerate: true,
|
|
196
|
-
autoTranslation: '
|
|
197
|
-
position: 'topbar',
|
|
198
|
-
cacheEnabled: true,
|
|
199
|
-
idCache: `video-${videoId}`,
|
|
280
|
+
language: 'english', // language of the audio: "english" or "en" (if empty auto-detect)
|
|
281
|
+
modelSize: 'base', // AI model: 'tiny', 'base', 'small'
|
|
282
|
+
autoGenerate: true, // start transcription automatically
|
|
283
|
+
autoTranslation: 'it', // auto-translate to Italian when ready
|
|
284
|
+
position: 'topbar', // CC button position: 'left', 'right', 'topbar'
|
|
285
|
+
cacheEnabled: true,
|
|
286
|
+
idCache: `video-${videoId}`,
|
|
287
|
+
translationEngine: {
|
|
288
|
+
type: 'libretranslate',
|
|
289
|
+
url: 'https://your-libretranslate-server.com',
|
|
290
|
+
apiKey: 'your-api-key',
|
|
291
|
+
},
|
|
200
292
|
subtitleStyle: {
|
|
201
293
|
fontSize: '1.2em',
|
|
202
294
|
color: '#ffe066',
|
|
@@ -237,6 +329,10 @@ Whisper ASR — chunks of 30s each
|
|
|
237
329
|
│ (subtitles stream in live)
|
|
238
330
|
▼
|
|
239
331
|
Save to localStorage cache
|
|
332
|
+
│
|
|
333
|
+
▼
|
|
334
|
+
Translate via selected engine
|
|
335
|
+
(MyMemory / LibreTranslate / MarianMT)
|
|
240
336
|
```
|
|
241
337
|
|
|
242
338
|
|