Compare commits
4 Commits
2e3918def4
...
f7c37df755
| Author | SHA1 | Date | |
|---|---|---|---|
| f7c37df755 | |||
| 6a9d3b25bf | |||
| 75b98de746 | |||
| eaf48276f0 |
Generated
+10
-10
@@ -13,7 +13,7 @@
|
|||||||
"chart.js": "^4.5.0",
|
"chart.js": "^4.5.0",
|
||||||
"dashjs": "^5.0.0",
|
"dashjs": "^5.0.0",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
"plyr": "^3.7.8",
|
"plyr": "^3.8.4",
|
||||||
"tw-elements": "^1.1.0",
|
"tw-elements": "^1.1.0",
|
||||||
"vidstack": "^1.12.13"
|
"vidstack": "^1.12.13"
|
||||||
},
|
},
|
||||||
@@ -3132,6 +3132,15 @@
|
|||||||
"postcss": "^8.0.9"
|
"postcss": "^8.0.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tw-elements/node_modules/yaml": {
|
||||||
|
"version": "1.10.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
|
||||||
|
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/unplugin": {
|
"node_modules/unplugin": {
|
||||||
"version": "1.16.1",
|
"version": "1.16.1",
|
||||||
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz",
|
||||||
@@ -3349,15 +3358,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||||
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
|
||||||
"node_modules/yaml": {
|
|
||||||
"version": "1.10.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
|
|
||||||
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
|
|
||||||
"license": "ISC",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@
|
|||||||
"chart.js": "^4.5.0",
|
"chart.js": "^4.5.0",
|
||||||
"dashjs": "^5.0.0",
|
"dashjs": "^5.0.0",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
"plyr": "^3.7.8",
|
"plyr": "^3.8.4",
|
||||||
"tw-elements": "^1.1.0",
|
"tw-elements": "^1.1.0",
|
||||||
"vidstack": "^1.12.13"
|
"vidstack": "^1.12.13"
|
||||||
}
|
}
|
||||||
|
|||||||
+156
-6
@@ -190,7 +190,7 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 100%;
|
bottom: 100%;
|
||||||
height: 48px;
|
height: 32px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
}
|
}
|
||||||
@@ -439,13 +439,12 @@
|
|||||||
.hstream-player__menu-container {
|
.hstream-player__menu-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 12px;
|
right: 12px;
|
||||||
bottom: 100%;
|
bottom: 80px;
|
||||||
margin-bottom: 2px;
|
|
||||||
min-width: 220px;
|
min-width: 220px;
|
||||||
max-width: 280px;
|
max-width: 280px;
|
||||||
background: rgba(20, 20, 20, 0.95);
|
background: rgba(20, 20, 20, 0.70);
|
||||||
backdrop-filter: blur(16px);
|
backdrop-filter: blur(12px);
|
||||||
-webkit-backdrop-filter: blur(16px);
|
-webkit-backdrop-filter: blur(12px);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -646,6 +645,88 @@
|
|||||||
to { transform: rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- Player Switcher Toggle ---- */
|
||||||
|
.player-switcher {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: rgba(10, 10, 10, 0.65);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
border-radius: 9999px;
|
||||||
|
padding: 6px 4px 6px 14px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (hover: hover) {
|
||||||
|
.aspect-\[16\/9\]:hover .player-switcher {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-switcher--visible {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-switcher__label {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-switcher__btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 6px 14px;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 9999px;
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
font-family: 'Figtree', sans-serif;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-switcher__btn:hover {
|
||||||
|
background: rgba(198, 30, 84, 0.2);
|
||||||
|
border-color: rgba(198, 30, 84, 0.4);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-switcher__btn i {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-switcher__btn--active {
|
||||||
|
background: rgba(198, 30, 84, 0.25);
|
||||||
|
border-color: rgba(198, 30, 84, 0.5);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-switcher__dot {
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #c61e54;
|
||||||
|
box-shadow: 0 0 6px rgba(198, 30, 84, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Fullscreen (remove rounded borders) ---- */
|
/* ---- Fullscreen (remove rounded borders) ---- */
|
||||||
.hstream-player:fullscreen,
|
.hstream-player:fullscreen,
|
||||||
.hstream-player:-webkit-full-screen {
|
.hstream-player:-webkit-full-screen {
|
||||||
@@ -794,3 +875,72 @@
|
|||||||
height: 36px;
|
height: 36px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
Plyr Player Overrides — pink accent, rounded corners, skip overlay
|
||||||
|
============================================================ */
|
||||||
|
.plyr--full-ui input[type='range'] {
|
||||||
|
color: var(--plyr-range-fill-background, var(--plyr-color-main, var(--plyr-color-main, #c61e54))) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plyr__control--overlaid {
|
||||||
|
background: var(--plyr-video-control-background-hover, var(--plyr-color-main, var(--plyr-color-main, #c61e54))) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plyr--video .plyr__control.plyr__tab-focus,
|
||||||
|
.plyr--video .plyr__control:hover,
|
||||||
|
.plyr--video .plyr__control[aria-expanded='true'] {
|
||||||
|
background: var(--plyr-video-control-background-hover, var(--plyr-color-main, var(--plyr-color-main, #c61e54))) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plyr__menu__container .plyr__control[role='menuitemradio'][aria-checked='true']::before {
|
||||||
|
background: var(--plyr-control-toggle-checked-background, var(--plyr-color-main, var(--plyr-color-main, #c61e54))) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plyr--full-ui {
|
||||||
|
border-radius: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Plyr Engagement Heatmap, overlays the progress bar track */
|
||||||
|
.plyr__progress__heatmap {
|
||||||
|
position: absolute;
|
||||||
|
left: -6px;
|
||||||
|
right: -6px;
|
||||||
|
bottom: 40%;
|
||||||
|
height: 48px;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plyr__progress__heatmap-canvas {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Plyr Mobile Double-tap Skip Overlay */
|
||||||
|
#plyr__time_skip {
|
||||||
|
background: #c61e54;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: #fff;
|
||||||
|
left: 50%;
|
||||||
|
min-width: 60px;
|
||||||
|
width: min-content;
|
||||||
|
max-width: 100px;
|
||||||
|
max-height: 90px;
|
||||||
|
opacity: 0;
|
||||||
|
display: table-cell;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
padding-top: 15px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transition: 1s;
|
||||||
|
z-index: 3;
|
||||||
|
pointer-events: none;
|
||||||
|
box-shadow: 0px 0px 45px #000000;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,436 @@
|
|||||||
|
// Plyr Fallback Player
|
||||||
|
import Plyr from 'plyr';
|
||||||
|
import 'plyr/dist/plyr.css';
|
||||||
|
|
||||||
|
import * as dashjs from 'dashjs';
|
||||||
|
import SubtitlesOctopus from '@jellyfin/libass-wasm';
|
||||||
|
|
||||||
|
import { initMobileWidescreen } from './player-mobile';
|
||||||
|
import { mobileDoubleClick } from './player-mobile';
|
||||||
|
import { playNextPlaylistVideo } from './playlist';
|
||||||
|
import { addVideoTracks } from './player-data';
|
||||||
|
import { addSubtitleTracks } from './player-data';
|
||||||
|
import { serverSelectMenuItem, serverSelectSubmenu, serverSelectMenuClickToggle } from './player-server-select';
|
||||||
|
import { isIOS } from './detect-ios';
|
||||||
|
import { startEngagementTracking, stopEngagementTracking } from './player/player-engagement';
|
||||||
|
import { renderHeatmap } from './player/player-heatmap';
|
||||||
|
|
||||||
|
var player = null;
|
||||||
|
var av1Supported = (!!document.createElement('video').canPlayType('video/webm; codecs="av01.0.05M.08, opus"'));
|
||||||
|
var dashSupported = dashjs.supportsMediaSource();
|
||||||
|
var apiResponse = {};
|
||||||
|
var volume = 0.5;
|
||||||
|
var muted = false;
|
||||||
|
var captions = true;
|
||||||
|
var lastTime = 0.0;
|
||||||
|
var streamServer = '';
|
||||||
|
var streamServers = [];
|
||||||
|
var streamServerIndex = 0;
|
||||||
|
var streamServerCount = 0;
|
||||||
|
var ambientMode = true;
|
||||||
|
var serverFallback = false;
|
||||||
|
var saveInterval;
|
||||||
|
var watchTracked = false;
|
||||||
|
var subtitleInstance = null;
|
||||||
|
|
||||||
|
function trackWatchTime() {
|
||||||
|
if (watchTracked) return;
|
||||||
|
var video = document.getElementsByTagName('video')[0];
|
||||||
|
if (video && video.currentTime >= 10) {
|
||||||
|
watchTracked = true;
|
||||||
|
var episodeId = document.getElementById('e_id').value;
|
||||||
|
window.axios.post('/watched/track', {
|
||||||
|
episode_id: episodeId
|
||||||
|
}).then(function () {
|
||||||
|
console.log('Watch tracked for episode ' + episodeId);
|
||||||
|
}).catch(function (error) {
|
||||||
|
console.error('Failed to track watch: ' + error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var controls = [
|
||||||
|
'play-large',
|
||||||
|
'play',
|
||||||
|
'progress',
|
||||||
|
'current-time',
|
||||||
|
'duration',
|
||||||
|
'mute',
|
||||||
|
'volume',
|
||||||
|
'captions',
|
||||||
|
'settings',
|
||||||
|
'fullscreen',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (localStorage.hstreamVolume) {
|
||||||
|
volume = parseFloat(localStorage.getItem('hstreamVolume')).toFixed(2);
|
||||||
|
console.log('Loaded Audio Volume from Local Storage: ' + volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localStorage.hstreamCaptions) {
|
||||||
|
captions = (localStorage.getItem('hstreamCaptions') == 'true');
|
||||||
|
console.log('Loaded Captions Status from Local Storage: ' + captions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localStorage.hstreamMuted) {
|
||||||
|
muted = (localStorage.getItem('hstreamMuted') == 'true');
|
||||||
|
console.log('Loaded Muted Status from Local Storage: ' + muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localStorage.hstreamServerFallback) {
|
||||||
|
serverFallback = (localStorage.getItem('hstreamServerFallback') == 'true');
|
||||||
|
console.log('Loaded Server Fallback Status from Local Storage: ' + serverFallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!av1Supported) {
|
||||||
|
document.getElementById('av1-unsupported').classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
function initDash(data, player) {
|
||||||
|
const video = document.querySelector('video');
|
||||||
|
|
||||||
|
data.forEach(function (el) {
|
||||||
|
if (el.mode === 'mpd' && el.size === player.config.quality.selected) {
|
||||||
|
const dash = dashjs.MediaPlayer().create();
|
||||||
|
dash.initialize(video, el.src, true);
|
||||||
|
window.player = player;
|
||||||
|
window.dash = dash;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCanvasDimension(canvas, video) {
|
||||||
|
canvas.height = video.offsetHeight;
|
||||||
|
canvas.width = video.offsetWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function paintStaticVideo(ctx, video) {
|
||||||
|
if (localStorage.theme == 'light') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ambientMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx.drawImage(video, 0, 0, video.offsetWidth, video.offsetHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleAmbientMode() {
|
||||||
|
let canvas = document.getElementById('ambientVideo'), ctx = canvas.getContext('2d'), video = document.getElementsByTagName('video')[0];
|
||||||
|
if (ambientMode) {
|
||||||
|
ambientMode = false;
|
||||||
|
localStorage.ambientMode = 'false';
|
||||||
|
setCanvasDimension(canvas, video);
|
||||||
|
document.getElementById('ambient-mode-toggle').innerHTML = '<span>Ambient Mode<span class="plyr__menu__value">Off</span></span>';
|
||||||
|
} else {
|
||||||
|
ambientMode = true;
|
||||||
|
localStorage.ambientMode = 'true';
|
||||||
|
setCanvasDimension(canvas, video);
|
||||||
|
paintStaticVideo(ctx, video);
|
||||||
|
document.getElementById('ambient-mode-toggle').innerHTML = '<span>Ambient Mode<span class="plyr__menu__value">On</span></span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleAsiaServer() {
|
||||||
|
if (serverFallback) {
|
||||||
|
serverFallback = false;
|
||||||
|
localStorage.hstreamServerFallback = 'false';
|
||||||
|
document.getElementById('server-fallback-toggle').innerHTML = '<span>Fallback Server<span class="plyr__menu__value">Off</span></span>';
|
||||||
|
streamServers = apiResponse.stream_domains;
|
||||||
|
} else {
|
||||||
|
serverFallback = true;
|
||||||
|
localStorage.hstreamServerFallback = 'true';
|
||||||
|
document.getElementById('server-fallback-toggle').innerHTML = '<span>Fallback Server<span class="plyr__menu__value">On</span></span>';
|
||||||
|
streamServers = apiResponse.asia_stream_domains;
|
||||||
|
}
|
||||||
|
|
||||||
|
streamServerCount = streamServers.length;
|
||||||
|
streamServerIndex = Math.floor(Math.random() * streamServerCount);
|
||||||
|
streamServer = streamServers[streamServerIndex];
|
||||||
|
console.log('Selected Server: ' + streamServer);
|
||||||
|
|
||||||
|
if (player) {
|
||||||
|
clearInterval(saveInterval);
|
||||||
|
stopEngagementTracking();
|
||||||
|
player.destroy();
|
||||||
|
}
|
||||||
|
initPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSubtitles(lang) {
|
||||||
|
if (isIOS()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subtitleInstance != null && subtitleInstance instanceof SubtitlesOctopus) {
|
||||||
|
subtitleInstance.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
let newSubUrl = streamServer + '/' + apiResponse.stream_url + '/';
|
||||||
|
|
||||||
|
if (lang != 'en') {
|
||||||
|
newSubUrl += 'autotrans/' + lang + '.ass';
|
||||||
|
} else {
|
||||||
|
newSubUrl += 'eng.ass';
|
||||||
|
}
|
||||||
|
|
||||||
|
let subFont = '/fonts/Figtree-ExtraBold.woff2';
|
||||||
|
if (lang == 'hi') {
|
||||||
|
subFont = '/fonts/Hind-SemiBold.ttf';
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
video: document.getElementsByTagName('video')[0],
|
||||||
|
subUrl: newSubUrl,
|
||||||
|
workerUrl: '/build/js/subtitles-octopus-worker.js',
|
||||||
|
legacyWorkerUrl: '/build/js/subtitles-octopus-worker-legacy.js',
|
||||||
|
fonts: [subFont],
|
||||||
|
renderMode: 'wasm-blend',
|
||||||
|
};
|
||||||
|
|
||||||
|
subtitleInstance = new SubtitlesOctopus(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initPlayer() {
|
||||||
|
player = new Plyr('#player', {
|
||||||
|
controls,
|
||||||
|
quality: {
|
||||||
|
default: 720,
|
||||||
|
options: [2161, 2160, 1081, 1080, 720]
|
||||||
|
},
|
||||||
|
i18n: {
|
||||||
|
qualityLabel: {
|
||||||
|
2161: '2160p48',
|
||||||
|
2160: '2160p',
|
||||||
|
1081: '1080p48',
|
||||||
|
1080: '1080p',
|
||||||
|
720: '720p'
|
||||||
|
},
|
||||||
|
qualityBadge: {
|
||||||
|
2161: 'UHD@48',
|
||||||
|
1081: 'FHD@48',
|
||||||
|
1080: 'FHD',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fullscreen: { enabled: true, fallback: true, iosNative: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
var data = addVideoTracks(streamServer, apiResponse, av1Supported, dashSupported);
|
||||||
|
|
||||||
|
player.source = {
|
||||||
|
type: 'video',
|
||||||
|
title: apiResponse.title,
|
||||||
|
poster: apiResponse.poster,
|
||||||
|
previewThumbnails: {
|
||||||
|
enabled: true,
|
||||||
|
src: streamServer + '/' + apiResponse.stream_url + '/thumbs.vtt',
|
||||||
|
},
|
||||||
|
sources: data,
|
||||||
|
tracks: addSubtitleTracks(streamServer, apiResponse)
|
||||||
|
};
|
||||||
|
|
||||||
|
player.volume = volume;
|
||||||
|
player.muted = muted;
|
||||||
|
player.captions.language = 'en';
|
||||||
|
player.captions.active = captions;
|
||||||
|
|
||||||
|
if (dashSupported && !apiResponse.legacy) {
|
||||||
|
player.on('qualitychange', () => {
|
||||||
|
initDash(data, player);
|
||||||
|
});
|
||||||
|
|
||||||
|
initDash(data, player);
|
||||||
|
}
|
||||||
|
|
||||||
|
let canvas = document.getElementById('ambientVideo'), ctx = canvas.getContext('2d'), video = document.getElementsByTagName('video')[0];
|
||||||
|
setCanvasDimension(canvas, video);
|
||||||
|
paintStaticVideo(ctx, video);
|
||||||
|
|
||||||
|
var allItems = document.getElementsByClassName('plyr__control--forward');
|
||||||
|
var lastItem = allItems[allItems.length - 1];
|
||||||
|
lastItem.insertAdjacentHTML('afterend', '<button id="ambient-mode-toggle" type="button" class="plyr__control" role="menuitem" aria-haspopup="true"><span>Ambient Mode<span class="plyr__menu__value">On</span></span></button>');
|
||||||
|
document.getElementById('ambient-mode-toggle').addEventListener('click', toggleAmbientMode);
|
||||||
|
|
||||||
|
if (localStorage.ambientMode == 'false') {
|
||||||
|
toggleAmbientMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastItem = allItems[allItems.length - 1];
|
||||||
|
let value = 'Off';
|
||||||
|
if (serverFallback) { value = 'On'; }
|
||||||
|
lastItem.insertAdjacentHTML('afterend', '<button id="server-fallback-toggle" type="button" class="plyr__control" role="menuitem" aria-haspopup="true"><span>Fallback Server<span class="plyr__menu__value">' + value + '</span></span></button>');
|
||||||
|
document.getElementById('server-fallback-toggle').addEventListener('click', toggleAsiaServer);
|
||||||
|
|
||||||
|
var clickedPlay = false;
|
||||||
|
|
||||||
|
player.on('play', () => {
|
||||||
|
if (!clickedPlay) {
|
||||||
|
player.stop();
|
||||||
|
console.log('Stopped video, because user didn\'t click play.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const episodeId = document.getElementById('e_id').value;
|
||||||
|
startEngagementTracking(episodeId);
|
||||||
|
|
||||||
|
setCanvasDimension(canvas, video);
|
||||||
|
console.log('Play => Function Loop()');
|
||||||
|
var $this = video;
|
||||||
|
(function loop() {
|
||||||
|
if (!player.paused && !player.ended && localStorage.theme == 'dark' && ambientMode) {
|
||||||
|
ctx.drawImage($this, 0, 0, $this.offsetWidth, $this.offsetHeight);
|
||||||
|
setTimeout(loop, 24000 / 1001);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
|
||||||
|
player.on('seeked', () => {
|
||||||
|
paintStaticVideo(ctx, video);
|
||||||
|
if (player.currentTime > 0) {
|
||||||
|
lastTime = player.currentTime;
|
||||||
|
}
|
||||||
|
console.log('Seeked => paintStaticVideo() at ' + player.currentTime);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
setCanvasDimension(canvas, video);
|
||||||
|
if (player.paused) {
|
||||||
|
paintStaticVideo(ctx, video);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
player.on('captionsenabled', () => {
|
||||||
|
document.getElementsByClassName('libassjs-canvas-parent')[0].style.visibility = 'visible';
|
||||||
|
localStorage.setItem('hstreamCaptions', 'true');
|
||||||
|
console.log('Set Captions Status to Local Storage: true');
|
||||||
|
});
|
||||||
|
|
||||||
|
player.on('captionsdisabled', () => {
|
||||||
|
document.getElementsByClassName('libassjs-canvas-parent')[0].style.visibility = 'hidden';
|
||||||
|
localStorage.setItem('hstreamCaptions', 'false');
|
||||||
|
console.log('Set Captions Status to Local Storage: false');
|
||||||
|
});
|
||||||
|
|
||||||
|
player.on('volumechange', () => {
|
||||||
|
console.log('Saving Audio Volume to Local Storage: ' + player.volume);
|
||||||
|
localStorage.setItem('hstreamVolume', player.volume.toString());
|
||||||
|
console.log('Saving Audio Muted to Local Storage: ' + player.muted.toString());
|
||||||
|
localStorage.setItem('hstreamMuted', player.muted.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
player.on('ended', () => {
|
||||||
|
playNextPlaylistVideo();
|
||||||
|
});
|
||||||
|
|
||||||
|
player.on('timeupdate', () => {
|
||||||
|
trackWatchTime();
|
||||||
|
});
|
||||||
|
|
||||||
|
player.on('languagechange', (event) => {
|
||||||
|
let lang = event.detail.plyr.captions.language;
|
||||||
|
|
||||||
|
console.log('Subtitle Event ' + lang);
|
||||||
|
initSubtitles(lang);
|
||||||
|
});
|
||||||
|
|
||||||
|
function playerPlayTemp() {
|
||||||
|
clickedPlay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-plyr="play"]').forEach(play =>
|
||||||
|
play.addEventListener('click', playerPlayTemp)
|
||||||
|
);
|
||||||
|
|
||||||
|
document.getElementsByClassName('plyr--video')[0].addEventListener('click', playerPlayTemp);
|
||||||
|
|
||||||
|
initMobileWidescreen();
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const time = parseInt(params.get('t'));
|
||||||
|
if (!isNaN(time)) {
|
||||||
|
player.currentTime = time;
|
||||||
|
console.log('Skipping to ' + time);
|
||||||
|
}
|
||||||
|
if (lastTime > 0) {
|
||||||
|
player.currentTime = lastTime;
|
||||||
|
console.log('Skipping to ' + lastTime);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
player.on('ready', () => {
|
||||||
|
mobileDoubleClick(player);
|
||||||
|
|
||||||
|
const video = document.querySelector('video');
|
||||||
|
const episodeId = document.getElementById('e_id').value;
|
||||||
|
if (video && video.duration) {
|
||||||
|
renderHeatmap(episodeId, video.duration);
|
||||||
|
} else if (video) {
|
||||||
|
video.addEventListener('loadedmetadata', function onMeta() {
|
||||||
|
video.removeEventListener('loadedmetadata', onMeta);
|
||||||
|
renderHeatmap(episodeId, video.duration);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var settingElements = document.getElementsByClassName('plyr__control--forward');
|
||||||
|
if (settingElements.length == 3) {
|
||||||
|
settingElements[2].insertAdjacentHTML('afterend', serverSelectMenuItem(streamServerIndex));
|
||||||
|
|
||||||
|
var settingNodes = document.getElementsByClassName('plyr__menu__container')[0].childNodes[0].childNodes;
|
||||||
|
if (settingNodes.length == 4) {
|
||||||
|
document.getElementsByClassName('plyr__menu__container')[0].childNodes[0].childNodes[3].insertAdjacentHTML('afterend', serverSelectSubmenu(streamServerIndex, streamServerCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('server-select').addEventListener('click', serverSelectMenuClickToggle);
|
||||||
|
document.getElementById('server-select-list-back-btn').addEventListener('click', serverSelectMenuClickToggle);
|
||||||
|
let serverSelects = document.getElementsByClassName('change_server');
|
||||||
|
for (let i = 0; i < serverSelects.length; i++) {
|
||||||
|
serverSelects[i].addEventListener('click', function () {
|
||||||
|
streamServerIndex = Number(this.value);
|
||||||
|
streamServer = streamServers[streamServerIndex];
|
||||||
|
console.log('Selected Server: ' + streamServer);
|
||||||
|
|
||||||
|
if (player) {
|
||||||
|
clearInterval(saveInterval);
|
||||||
|
stopEngagementTracking();
|
||||||
|
player.destroy();
|
||||||
|
}
|
||||||
|
initPlayer();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveInterval = setInterval(function () {
|
||||||
|
lastTime = player.currentTime;
|
||||||
|
console.log('Last Player Position: ' + lastTime);
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initPlyrPlayer(episodeId) {
|
||||||
|
window.axios.post('/player/api', {
|
||||||
|
episode_id: episodeId
|
||||||
|
}).then(function (response) {
|
||||||
|
if (response.status == 200) {
|
||||||
|
apiResponse = response.data;
|
||||||
|
streamServers = apiResponse.stream_domains;
|
||||||
|
|
||||||
|
if (serverFallback) {
|
||||||
|
streamServers = apiResponse.asia_stream_domains;
|
||||||
|
}
|
||||||
|
|
||||||
|
streamServerCount = streamServers.length;
|
||||||
|
streamServerIndex = Math.floor(Math.random() * streamServerCount);
|
||||||
|
streamServer = streamServers[streamServerIndex];
|
||||||
|
console.log('Selected Server: ' + streamServer + ' with Index: ' + streamServerIndex);
|
||||||
|
|
||||||
|
initPlayer();
|
||||||
|
}
|
||||||
|
}).catch(function (error) {
|
||||||
|
var alert = document.getElementById('player-alert');
|
||||||
|
if (alert) {
|
||||||
|
alert.innerText = 'The player encountered a problem: ' + error;
|
||||||
|
alert.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return player;
|
||||||
|
}
|
||||||
+51
-32
@@ -181,6 +181,9 @@ function initPlayer() {
|
|||||||
},
|
},
|
||||||
onLanguageChange: function (lang) {
|
onLanguageChange: function (lang) {
|
||||||
initSubtitles(lang);
|
initSubtitles(lang);
|
||||||
|
if (player) {
|
||||||
|
player.setSubtitleInstance(subtitleInstance);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onServerChange: function (index) {
|
onServerChange: function (index) {
|
||||||
streamServerIndex = index;
|
streamServerIndex = index;
|
||||||
@@ -199,6 +202,11 @@ function initPlayer() {
|
|||||||
|
|
||||||
window.player = player;
|
window.player = player;
|
||||||
|
|
||||||
|
if (player.captionsActive) {
|
||||||
|
initSubtitles(player.captionLanguage);
|
||||||
|
player.setSubtitleInstance(subtitleInstance);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isMobile()) {
|
if (!isMobile()) {
|
||||||
player.initThumbnails(vttThumbsUrl);
|
player.initThumbnails(vttThumbsUrl);
|
||||||
}
|
}
|
||||||
@@ -268,38 +276,49 @@ async function initVidstackPlayer() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.axios.post('/player/api', {
|
window.setPlayerPreference = function(pref) {
|
||||||
episode_id: document.getElementById('e_id').value
|
localStorage.setItem('hstreamPlayerPreference', pref);
|
||||||
}).then(function (response) {
|
window.location.reload();
|
||||||
if (response.status === 200) {
|
};
|
||||||
apiResponse = response.data;
|
|
||||||
streamServers = apiResponse.stream_domains || [];
|
|
||||||
fallbackServers = apiResponse.asia_stream_domains || [];
|
|
||||||
|
|
||||||
const cdnCount = streamServers.length;
|
const playerPreference = localStorage.getItem('hstreamPlayerPreference') || 'hstream';
|
||||||
if (cdnCount > 0) {
|
|
||||||
streamServerIndex = Math.floor(Math.random() * cdnCount);
|
if (playerPreference === 'plyr' && !isIOS()) {
|
||||||
streamServer = streamServers[streamServerIndex];
|
import('./player-plyr.js').then(m => m.initPlyrPlayer(document.getElementById('e_id').value));
|
||||||
} else {
|
} else {
|
||||||
const fallbackCount = fallbackServers.length;
|
window.axios.post('/player/api', {
|
||||||
streamServerIndex = Math.floor(Math.random() * fallbackCount);
|
episode_id: document.getElementById('e_id').value
|
||||||
streamServer = fallbackServers[streamServerIndex];
|
}).then(function (response) {
|
||||||
|
if (response.status === 200) {
|
||||||
|
apiResponse = response.data;
|
||||||
|
streamServers = apiResponse.stream_domains || [];
|
||||||
|
fallbackServers = apiResponse.asia_stream_domains || [];
|
||||||
|
|
||||||
|
const cdnCount = streamServers.length;
|
||||||
|
if (cdnCount > 0) {
|
||||||
|
streamServerIndex = Math.floor(Math.random() * cdnCount);
|
||||||
|
streamServer = streamServers[streamServerIndex];
|
||||||
|
} else {
|
||||||
|
const fallbackCount = fallbackServers.length;
|
||||||
|
streamServerIndex = Math.floor(Math.random() * fallbackCount);
|
||||||
|
streamServer = fallbackServers[streamServerIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
streamServerCount = streamServers.length + fallbackServers.length;
|
||||||
|
console.log('Selected Server: ' + streamServer + ' with Index: ' + streamServerIndex);
|
||||||
|
|
||||||
|
if (!isIOS()) {
|
||||||
|
initPlayer();
|
||||||
|
} else {
|
||||||
|
console.log('Detected Apple device. Using Vidstack fallback player.');
|
||||||
|
initVidstackPlayer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}).catch(function (error) {
|
||||||
streamServerCount = streamServers.length + fallbackServers.length;
|
var alert = document.getElementById('player-alert');
|
||||||
console.log('Selected Server: ' + streamServer + ' with Index: ' + streamServerIndex);
|
if (alert) {
|
||||||
|
alert.innerText = 'The player encountered a problem: ' + error;
|
||||||
if (!isIOS()) {
|
alert.classList.remove('hidden');
|
||||||
initPlayer();
|
|
||||||
} else {
|
|
||||||
console.log('Detected Apple device. Using Vidstack fallback player.');
|
|
||||||
initVidstackPlayer();
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}).catch(function (error) {
|
}
|
||||||
var alert = document.getElementById('player-alert');
|
|
||||||
if (alert) {
|
|
||||||
alert.innerText = 'The player encountered a problem: ' + error;
|
|
||||||
alert.classList.remove('hidden');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ export class HStreamPlayer {
|
|||||||
|
|
||||||
// Build settings menu
|
// Build settings menu
|
||||||
this._buildSettingsMenu();
|
this._buildSettingsMenu();
|
||||||
this._controlsEl.appendChild(this._menuContainer);
|
this.container.appendChild(this._menuContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createButton(action, iconClass, label) {
|
_createButton(action, iconClass, label) {
|
||||||
@@ -272,7 +272,7 @@ export class HStreamPlayer {
|
|||||||
mainPanel.appendChild(addItem('Quality', this._qualityLabel(this._quality), 'quality'));
|
mainPanel.appendChild(addItem('Quality', this._qualityLabel(this._quality), 'quality'));
|
||||||
mainPanel.appendChild(addItem('Playback Speed', this._speedLabel(this.video.playbackRate), 'speed'));
|
mainPanel.appendChild(addItem('Playback Speed', this._speedLabel(this.video.playbackRate), 'speed'));
|
||||||
|
|
||||||
if (this._subtitleTracks.length > 1) {
|
if (this._subtitleTracks.length >= 1) {
|
||||||
mainPanel.appendChild(addItem('Subtitles', this._captionLabel(), 'captions'));
|
mainPanel.appendChild(addItem('Subtitles', this._captionLabel(), 'captions'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,6 +436,7 @@ export class HStreamPlayer {
|
|||||||
}
|
}
|
||||||
item.type = 'button';
|
item.type = 'button';
|
||||||
item.setAttribute('role', 'menuitemradio');
|
item.setAttribute('role', 'menuitemradio');
|
||||||
|
item.setAttribute('data-srclang', track.srclang);
|
||||||
item.innerHTML = `<span>${track.label}</span><span class="hstream-player__menu-item-radio"></span>`;
|
item.innerHTML = `<span>${track.label}</span><span class="hstream-player__menu-item-radio"></span>`;
|
||||||
item.addEventListener('click', (e) => {
|
item.addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -953,21 +954,21 @@ export class HStreamPlayer {
|
|||||||
toggleCaptions() {
|
toggleCaptions() {
|
||||||
this._captionsActive = !this._captionsActive;
|
this._captionsActive = !this._captionsActive;
|
||||||
|
|
||||||
if (this.subtitleInstance && this.subtitleInstance.canvas) {
|
|
||||||
this.subtitleInstance.canvas.style.visibility = this._captionsActive ? 'visible' : 'hidden';
|
|
||||||
}
|
|
||||||
const libassParent = document.querySelector('.libassjs-canvas-parent');
|
|
||||||
if (libassParent) {
|
|
||||||
libassParent.style.visibility = this._captionsActive ? 'visible' : 'hidden';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._captionsActive) {
|
if (this._captionsActive) {
|
||||||
this._captionsBtn.classList.add('hstream-player__button--active');
|
this._captionsBtn.classList.add('hstream-player__button--active');
|
||||||
} else {
|
} else {
|
||||||
this._captionsBtn.classList.remove('hstream-player__button--active');
|
this._captionsBtn.classList.remove('hstream-player__button--active');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var libassParent = document.querySelector('.libassjs-canvas-parent');
|
||||||
|
if (libassParent) {
|
||||||
|
libassParent.style.visibility = this._captionsActive ? 'visible' : 'hidden';
|
||||||
|
} else if (this._captionsActive && this._onLanguageChange) {
|
||||||
|
this._onLanguageChange(this._captionLanguage);
|
||||||
|
}
|
||||||
|
|
||||||
localStorage.setItem('hstreamCaptions', this._captionsActive.toString());
|
localStorage.setItem('hstreamCaptions', this._captionsActive.toString());
|
||||||
|
this._updateCaptionsPanel();
|
||||||
if (this._onCaptionsToggle) {
|
if (this._onCaptionsToggle) {
|
||||||
this._onCaptionsToggle(this._captionsActive);
|
this._onCaptionsToggle(this._captionsActive);
|
||||||
}
|
}
|
||||||
@@ -983,6 +984,7 @@ export class HStreamPlayer {
|
|||||||
if (this._onLanguageChange) {
|
if (this._onLanguageChange) {
|
||||||
this._onLanguageChange(lang);
|
this._onLanguageChange(lang);
|
||||||
}
|
}
|
||||||
|
this._updateCaptionsPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
get captionLanguage() {
|
get captionLanguage() {
|
||||||
@@ -1062,6 +1064,43 @@ export class HStreamPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_updateCaptionsPanel() {
|
||||||
|
var captionsPanel = this._menuContainer.querySelector('[data-panel="captions"]');
|
||||||
|
if (!captionsPanel) return;
|
||||||
|
|
||||||
|
captionsPanel.querySelectorAll('.hstream-player__menu-item').forEach(function (item) {
|
||||||
|
item.classList.remove('hstream-player__menu-item--checked');
|
||||||
|
item.setAttribute('aria-checked', 'false');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this._captionsActive) {
|
||||||
|
var offItem = captionsPanel.querySelector('.hstream-player__menu-item:not([data-srclang])');
|
||||||
|
if (offItem) {
|
||||||
|
offItem.classList.add('hstream-player__menu-item--checked');
|
||||||
|
offItem.setAttribute('aria-checked', 'true');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var langItem = captionsPanel.querySelector('.hstream-player__menu-item[data-srclang="' + this._captionLanguage + '"]');
|
||||||
|
if (langItem) {
|
||||||
|
langItem.classList.add('hstream-player__menu-item--checked');
|
||||||
|
langItem.setAttribute('aria-checked', 'true');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mainPanel = this._menuContainer.querySelector('[data-panel="main"]');
|
||||||
|
if (mainPanel) {
|
||||||
|
var items = mainPanel.querySelectorAll('.hstream-player__menu-item');
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
var text = items[i].textContent.trim();
|
||||||
|
if (text.startsWith('Subtitles')) {
|
||||||
|
var valueEl = items[i].querySelector('.hstream-player__menu-value');
|
||||||
|
if (valueEl) valueEl.textContent = this._captionLabel();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get quality() {
|
get quality() {
|
||||||
return this._quality;
|
return this._quality;
|
||||||
}
|
}
|
||||||
@@ -1070,6 +1109,10 @@ export class HStreamPlayer {
|
|||||||
return !!document.fullscreenElement;
|
return !!document.fullscreenElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setSubtitleInstance(instance) {
|
||||||
|
this.subtitleInstance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
toggleAmbientMode() {
|
toggleAmbientMode() {
|
||||||
this._ambientMode = !this._ambientMode;
|
this._ambientMode = !this._ambientMode;
|
||||||
localStorage.setItem('ambientMode', this._ambientMode.toString());
|
localStorage.setItem('ambientMode', this._ambientMode.toString());
|
||||||
@@ -1202,7 +1245,7 @@ export class HStreamPlayer {
|
|||||||
if (this._captionsActive) {
|
if (this._captionsActive) {
|
||||||
this._captionsBtn.classList.add('hstream-player__button--active');
|
this._captionsBtn.classList.add('hstream-player__button--active');
|
||||||
}
|
}
|
||||||
if (this._subtitleTracks.length > 1) {
|
if (this._subtitleTracks.length >= 1) {
|
||||||
this._captionsBtn.style.display = '';
|
this._captionsBtn.style.display = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,42 @@
|
|||||||
@endif
|
@endif
|
||||||
<canvas id="ambientVideo" class="decoy"></canvas>
|
<canvas id="ambientVideo" class="decoy"></canvas>
|
||||||
<div class="relative w-full aspect-[16/9]">
|
<div class="relative w-full aspect-[16/9]">
|
||||||
|
<div class="player-switcher" id="player-switcher">
|
||||||
|
<span class="player-switcher__label">Player</span>
|
||||||
|
<button type="button" class="player-switcher__btn" id="plyr-toggle-btn"
|
||||||
|
onclick="window.setPlayerPreference('plyr')" title="Switch to Plyr player">
|
||||||
|
<span class="player-switcher__dot"></span> Plyr
|
||||||
|
</button>
|
||||||
|
<button type="button" class="player-switcher__btn player-switcher__btn--active" id="hstream-toggle-btn"
|
||||||
|
onclick="window.setPlayerPreference('hstream')" title="Switch to HStream player">
|
||||||
|
<span class="player-switcher__dot"></span> HStream
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<video id="player" playsinline crossorigin class="absolute inset-0 w-full h-full"></video>
|
<video id="player" playsinline crossorigin class="absolute inset-0 w-full h-full"></video>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var pref = localStorage.getItem('hstreamPlayerPreference') || 'hstream';
|
||||||
|
var hstreamBtn = document.getElementById('hstream-toggle-btn');
|
||||||
|
var plyrBtn = document.getElementById('plyr-toggle-btn');
|
||||||
|
if (pref === 'plyr') {
|
||||||
|
hstreamBtn.classList.remove('player-switcher__btn--active');
|
||||||
|
plyrBtn.classList.add('player-switcher__btn--active');
|
||||||
|
}
|
||||||
|
|
||||||
|
var switcher = document.getElementById('player-switcher');
|
||||||
|
var container = switcher.parentElement;
|
||||||
|
var hideTimer = null;
|
||||||
|
|
||||||
|
function showSwitcher() {
|
||||||
|
switcher.classList.add('player-switcher--visible');
|
||||||
|
clearTimeout(hideTimer);
|
||||||
|
hideTimer = setTimeout(function () {
|
||||||
|
switcher.classList.remove('player-switcher--visible');
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
container.addEventListener('pointerdown', showSwitcher);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export default defineConfig({
|
|||||||
'resources/js/modals-playlist.js',
|
'resources/js/modals-playlist.js',
|
||||||
'resources/js/theme.js',
|
'resources/js/theme.js',
|
||||||
'resources/js/player.js',
|
'resources/js/player.js',
|
||||||
|
'resources/js/player-plyr.js',
|
||||||
'resources/js/playlist.js',
|
'resources/js/playlist.js',
|
||||||
'resources/js/upload.js',
|
'resources/js/upload.js',
|
||||||
'resources/js/user-blacklist.js',
|
'resources/js/user-blacklist.js',
|
||||||
|
|||||||
Reference in New Issue
Block a user