Custom video player
This commit is contained in:
+159
-374
@@ -1,30 +1,17 @@
|
||||
// Plyr Player
|
||||
import Plyr from 'plyr';
|
||||
import 'plyr/dist/plyr.css';
|
||||
|
||||
// Vidstack Player
|
||||
// HStream Custom Video Player
|
||||
import 'vidstack/player/styles/default/theme.css';
|
||||
import 'vidstack/player/styles/default/layouts/video.css';
|
||||
import { VidstackPlayer, VidstackPlayerLayout } from 'vidstack/global/player';
|
||||
|
||||
// Dash Support
|
||||
import * as dashjs from 'dashjs';
|
||||
|
||||
// Subtitle Support
|
||||
import SubtitlesOctopus from '@jellyfin/libass-wasm';
|
||||
|
||||
// Custom JS
|
||||
import { initMobileWidescreen } from './player-mobile';
|
||||
import { mobileDoubleClick } from './player-mobile'
|
||||
import { HStreamPlayer } from './player/player-core';
|
||||
import { initMobileWidescreen, initMobileDoubleTap, isMobile } from './player/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 { addVideoTracks, addSubtitleTracks } from './player/player-data';
|
||||
import { isIOS } from './detect-ios';
|
||||
import { startEngagementTracking, stopEngagementTracking } from './player-engagement';
|
||||
import { renderHeatmap } from './player-heatmap';
|
||||
import { startEngagementTracking, stopEngagementTracking } from './player/player-engagement';
|
||||
import { renderHeatmap } from './player/player-heatmap';
|
||||
|
||||
// Variables
|
||||
var player = null;
|
||||
var av1Supported = (!!document.createElement('video').canPlayType('video/webm; codecs="av01.0.05M.08, opus"'));
|
||||
var dashSupported = dashjs.supportsMediaSource();
|
||||
@@ -35,23 +22,23 @@ var captions = true;
|
||||
var lastTime = 0.0;
|
||||
var streamServer = '';
|
||||
var streamServers = [];
|
||||
var fallbackServers = [];
|
||||
var streamServerIndex = 0;
|
||||
var streamServerCount = 0;
|
||||
var ambientMode = true;
|
||||
var serverFallback = false;
|
||||
var saveInterval;
|
||||
var watchTracked = false;
|
||||
var subtitleInstance = null;
|
||||
|
||||
// Track that the user watched at least 10 seconds of the video
|
||||
function trackWatchTime() {
|
||||
if (watchTracked) return;
|
||||
var video = document.getElementsByTagName('video')[0];
|
||||
if (video && video.currentTime >= 10) {
|
||||
var videoEl = document.getElementsByTagName('video')[0];
|
||||
if (videoEl && videoEl.currentTime >= 10) {
|
||||
watchTracked = true;
|
||||
var episodeId = document.getElementById('e_id').value;
|
||||
window.axios.post('/watched/track', {
|
||||
episode_id: episodeId
|
||||
}).then(function (response) {
|
||||
}).then(function () {
|
||||
console.log('Watch tracked for episode ' + episodeId);
|
||||
}).catch(function (error) {
|
||||
console.error('Failed to track watch: ' + error);
|
||||
@@ -59,119 +46,24 @@ function trackWatchTime() {
|
||||
}
|
||||
}
|
||||
|
||||
var subtitleInstance = null;
|
||||
|
||||
var controls = [
|
||||
'play-large', // The large play button in the center
|
||||
'play', // Play/pause playback
|
||||
'progress', // The progress bar and scrubber for playback and buffering
|
||||
'current-time', // The current time of playback
|
||||
'duration', // The full duration of the media
|
||||
'mute', // Toggle mute
|
||||
'volume', // Volume control
|
||||
'captions', // Toggle captions
|
||||
'settings', // Settings menu
|
||||
'fullscreen', // Toggle fullscreen
|
||||
];
|
||||
|
||||
// Load Volume from LocalStorage
|
||||
if (localStorage.hstreamVolume) {
|
||||
volume = parseFloat(localStorage.getItem('hstreamVolume')).toFixed(2);
|
||||
volume = parseFloat(localStorage.getItem('hstreamVolume'));
|
||||
if (!isNaN(volume)) volume = Math.max(0, Math.min(1, volume));
|
||||
console.log('Loaded Audio Volume from Local Storage: ' + volume);
|
||||
}
|
||||
|
||||
// Load Captions from LocalStorage
|
||||
if (localStorage.hstreamCaptions) {
|
||||
captions = (localStorage.getItem('hstreamCaptions') == 'true');
|
||||
captions = (localStorage.getItem('hstreamCaptions') === 'true');
|
||||
console.log('Loaded Captions Status from Local Storage: ' + captions);
|
||||
}
|
||||
|
||||
// Load Muted from LocalStorage
|
||||
if (localStorage.hstreamCaptions) {
|
||||
muted = (localStorage.getItem('hstreamMuted') == 'true');
|
||||
if (localStorage.hstreamMuted) {
|
||||
muted = (localStorage.getItem('hstreamMuted') === 'true');
|
||||
console.log('Loaded Muted Status from Local Storage: ' + muted);
|
||||
}
|
||||
|
||||
// Asia Server Fallback
|
||||
if (localStorage.hstreamServerFallback) {
|
||||
serverFallback = (localStorage.getItem('hstreamServerFallback') == 'true');
|
||||
console.log('Loaded Server Fallback Status from Local Storage: ' + serverFallback);
|
||||
}
|
||||
|
||||
// Alert User when AV1 is not supported
|
||||
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);
|
||||
// Expose player and dash so they can be used from the console
|
||||
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();
|
||||
document.getElementById('av1-unsupported').classList.remove('hidden');
|
||||
}
|
||||
|
||||
function initSubtitles(lang) {
|
||||
@@ -179,32 +71,28 @@ function initSubtitles(lang) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dispose old instance
|
||||
if (subtitleInstance != null && subtitleInstance instanceof SubtitlesOctopus) {
|
||||
if (subtitleInstance !== null && subtitleInstance instanceof SubtitlesOctopus) {
|
||||
subtitleInstance.dispose();
|
||||
}
|
||||
|
||||
let newSubUrl = streamServer + '/' + apiResponse.stream_url + '/';
|
||||
var newSubUrl = streamServer + '/' + apiResponse.stream_url + '/';
|
||||
|
||||
if (lang != 'en') {
|
||||
if (lang !== 'en') {
|
||||
newSubUrl += 'autotrans/' + lang + '.ass';
|
||||
}
|
||||
else {
|
||||
newSubUrl += 'eng.ass'
|
||||
} else {
|
||||
newSubUrl += 'eng.ass';
|
||||
}
|
||||
|
||||
let subFont = '/fonts/Figtree-ExtraBold.woff2';
|
||||
// Hindi font
|
||||
if (lang == 'hi') {
|
||||
var subFont = '/fonts/Figtree-ExtraBold.woff2';
|
||||
if (lang === 'hi') {
|
||||
subFont = '/fonts/Hind-SemiBold.ttf';
|
||||
}
|
||||
|
||||
// Subtitles
|
||||
var options = {
|
||||
video: document.getElementsByTagName('video')[0], // HTML5 video element
|
||||
subUrl: newSubUrl, // Link to subtitles
|
||||
workerUrl: '/build/js/subtitles-octopus-worker.js', // Link to WebAssembly-based file "libassjs-worker.js"
|
||||
legacyWorkerUrl: '/build/js/subtitles-octopus-worker-legacy.js', // Link to non-WebAssembly worker
|
||||
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',
|
||||
};
|
||||
@@ -212,237 +100,146 @@ function initSubtitles(lang) {
|
||||
subtitleInstance = new SubtitlesOctopus(options);
|
||||
}
|
||||
|
||||
function initPlayerQualityChange(data) {
|
||||
if (dashSupported && !apiResponse.legacy) {
|
||||
player.on('qualitychange', function () {
|
||||
initDash(data);
|
||||
});
|
||||
initDash(data);
|
||||
}
|
||||
}
|
||||
|
||||
function initDash(data) {
|
||||
var videoEl = document.querySelector('video');
|
||||
var quality = player.quality;
|
||||
|
||||
data.forEach(function (el) {
|
||||
if (el.mode === 'mpd' && el.size === quality) {
|
||||
var dash = dashjs.MediaPlayer().create();
|
||||
dash.initialize(videoEl, el.src, true);
|
||||
window.dash = dash;
|
||||
player.dash = dash;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initPlayer() {
|
||||
player = new Plyr('#player', {
|
||||
controls,
|
||||
quality: {
|
||||
default: 720,
|
||||
options: [2161, 2160, 1081, 1080, 720]
|
||||
var videoEl = document.querySelector('#player');
|
||||
var container = videoEl.parentElement;
|
||||
|
||||
var data = addVideoTracks(streamServer, apiResponse, av1Supported, dashSupported);
|
||||
var subtitleTracks = addSubtitleTracks(streamServer, apiResponse);
|
||||
var vttThumbsUrl = streamServer + '/' + apiResponse.stream_url + '/thumbs.vtt';
|
||||
|
||||
player = new HStreamPlayer({
|
||||
container: container,
|
||||
video: videoEl,
|
||||
apiResponse: apiResponse,
|
||||
streamServer: streamServer,
|
||||
streamServers: streamServers,
|
||||
fallbackServers: fallbackServers,
|
||||
streamServerIndex: streamServerIndex,
|
||||
av1Supported: av1Supported,
|
||||
dashSupported: dashSupported,
|
||||
poster: apiResponse.poster,
|
||||
title: apiResponse.title,
|
||||
data: data,
|
||||
subtitleTracks: subtitleTracks,
|
||||
volume: volume,
|
||||
muted: muted,
|
||||
captionsActive: captions,
|
||||
captionLanguage: 'en',
|
||||
ambientMode: ambientMode,
|
||||
isMobile: isMobile(),
|
||||
quality: parseInt(localStorage.getItem('hstreamQuality')) || 1080,
|
||||
lastTime: lastTime,
|
||||
subtitleInstance: subtitleInstance,
|
||||
onEnded: function () {
|
||||
playNextPlaylistVideo();
|
||||
},
|
||||
i18n: {
|
||||
qualityLabel: {
|
||||
2161: "2160p48",
|
||||
2160: "2160p",
|
||||
1081: "1080p48",
|
||||
1080: "1080p",
|
||||
720: "720p"
|
||||
},
|
||||
qualityBadge: {
|
||||
2161: "UHD@48",
|
||||
1081: "FHD@48",
|
||||
1080: "FHD",
|
||||
},
|
||||
onTimeUpdate: function () {
|
||||
trackWatchTime();
|
||||
},
|
||||
onQualityChange: function (size) {
|
||||
if (dashSupported && !apiResponse.legacy) {
|
||||
initDash(data);
|
||||
}
|
||||
},
|
||||
onVolumeChange: function () {
|
||||
localStorage.setItem('hstreamVolume', player.volume.toString());
|
||||
localStorage.setItem('hstreamMuted', player.muted.toString());
|
||||
},
|
||||
onCaptionsToggle: function (active) {
|
||||
localStorage.setItem('hstreamCaptions', active.toString());
|
||||
if (subtitleInstance && subtitleInstance.canvas) {
|
||||
subtitleInstance.canvas.style.visibility = active ? 'visible' : 'hidden';
|
||||
}
|
||||
var libassParent = document.querySelector('.libassjs-canvas-parent');
|
||||
if (libassParent) {
|
||||
libassParent.style.visibility = active ? 'visible' : 'hidden';
|
||||
}
|
||||
},
|
||||
onLanguageChange: function (lang) {
|
||||
initSubtitles(lang);
|
||||
},
|
||||
onServerChange: function (index) {
|
||||
streamServerIndex = index;
|
||||
var allServers = streamServers.concat(fallbackServers);
|
||||
streamServer = allServers[streamServerIndex];
|
||||
console.log('Selected Server: ' + streamServer);
|
||||
|
||||
if (player) {
|
||||
clearInterval(saveInterval);
|
||||
stopEngagementTracking();
|
||||
player.destroy();
|
||||
}
|
||||
initPlayer();
|
||||
},
|
||||
fullscreen: { enabled: true, fallback: true, iosNative: true }
|
||||
});
|
||||
|
||||
// Player Track Data
|
||||
var data = addVideoTracks(streamServer, apiResponse, av1Supported, dashSupported);
|
||||
window.player = player;
|
||||
|
||||
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.languages = ['en'];
|
||||
player.captions.language = 'en';
|
||||
player.captions.active = captions;
|
||||
if (!isMobile()) {
|
||||
player.initThumbnails(vttThumbsUrl);
|
||||
}
|
||||
|
||||
if (dashSupported && !apiResponse.legacy) {
|
||||
player.on('qualitychange', () => {
|
||||
initDash(data, player);
|
||||
});
|
||||
|
||||
initDash(data, player);
|
||||
initDash(data);
|
||||
}
|
||||
|
||||
// Ambient Mode
|
||||
let canvas = document.getElementById("ambientVideo"), ctx = canvas.getContext("2d"), video = document.getElementsByTagName('video')[0];
|
||||
setCanvasDimension(canvas, video);
|
||||
paintStaticVideo(ctx, video);
|
||||
initMobileWidescreen(container, videoEl);
|
||||
initMobileDoubleTap(container, videoEl, player);
|
||||
|
||||
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);
|
||||
var episodeId = document.getElementById('e_id').value;
|
||||
player.initHeatmap(episodeId);
|
||||
|
||||
if (localStorage.ambientMode == 'false') {
|
||||
toggleAmbientMode();
|
||||
}
|
||||
|
||||
// Server select (Asia)
|
||||
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.")
|
||||
}
|
||||
|
||||
// Start engagement heatmap tracking
|
||||
const episodeId = document.getElementById('e_id').value;
|
||||
videoEl.addEventListener('play', function onFirstPlay() {
|
||||
videoEl.removeEventListener('play', onFirstPlay);
|
||||
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); // drawing at 30fps
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
// Track watch time after 10 seconds of playback
|
||||
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();
|
||||
|
||||
// Start time
|
||||
setTimeout(function () {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const time = parseInt(params.get("t"));
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var time = parseInt(params.get('t'));
|
||||
if (!isNaN(time)) {
|
||||
player.currentTime = time;
|
||||
console.log("Skipping to " + time)
|
||||
console.log('Skipping to ' + time);
|
||||
}
|
||||
if (lastTime > 0) {
|
||||
player.currentTime = lastTime;
|
||||
console.log("Skipping to " + lastTime)
|
||||
console.log('Skipping to ' + lastTime);
|
||||
}
|
||||
}, 500);
|
||||
|
||||
player.on('ready', () => {
|
||||
mobileDoubleClick(player);
|
||||
|
||||
// Load engagement heatmap once video duration is known
|
||||
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);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Server Select
|
||||
// I hate this...
|
||||
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));
|
||||
}
|
||||
|
||||
// Event Listeners
|
||||
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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Periodically save last timestamp
|
||||
saveInterval = setInterval(function () {
|
||||
lastTime = player.currentTime;
|
||||
console.log("Last Player Position: " + lastTime);
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
async function initVidstackPlayer() {
|
||||
const videoSource = streamServer + '/' + apiResponse.stream_url + '/x264.720p.mp4';
|
||||
const videoThumbs = streamServer + '/' + apiResponse.stream_url + '/thumbs.vtt';
|
||||
const videoCaption = streamServer + '/' + apiResponse.stream_url + '/eng.vtt';
|
||||
var videoSource = streamServer + '/' + apiResponse.stream_url + '/x264.720p.mp4';
|
||||
var videoThumbs = streamServer + '/' + apiResponse.stream_url + '/thumbs.vtt';
|
||||
var videoCaption = streamServer + '/' + apiResponse.stream_url + '/eng.vtt';
|
||||
|
||||
player = await VidstackPlayer.create({
|
||||
target: '#player',
|
||||
@@ -464,57 +261,45 @@ async function initVidstackPlayer() {
|
||||
]
|
||||
});
|
||||
|
||||
// Ambient Mode
|
||||
let canvas = document.getElementById("ambientVideo"), ctx = canvas.getContext("2d"), video = document.getElementsByTagName('video')[0];
|
||||
setCanvasDimension(canvas, video);
|
||||
paintStaticVideo(ctx, video);
|
||||
window.player = player;
|
||||
|
||||
player.addEventListener('play', () => {
|
||||
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); // drawing at 30fps
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
// Track watch time after 10 seconds of playback
|
||||
player.addEventListener('time-update', () => {
|
||||
player.addEventListener('time-update', function () {
|
||||
trackWatchTime();
|
||||
});
|
||||
}
|
||||
|
||||
// Get Data from API
|
||||
window.axios.post('/player/api', {
|
||||
episode_id: document.getElementById('e_id').value
|
||||
}).then(function (response) {
|
||||
if (response.status == 200) {
|
||||
if (response.status === 200) {
|
||||
apiResponse = response.data;
|
||||
streamServers = apiResponse.stream_domains;
|
||||
streamServers = apiResponse.stream_domains || [];
|
||||
fallbackServers = apiResponse.asia_stream_domains || [];
|
||||
|
||||
if (serverFallback) {
|
||||
streamServers = 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;
|
||||
streamServerIndex = Math.floor(Math.random() * streamServerCount);
|
||||
streamServer = streamServers[streamServerIndex];
|
||||
streamServerCount = streamServers.length + fallbackServers.length;
|
||||
console.log('Selected Server: ' + streamServer + ' with Index: ' + streamServerIndex);
|
||||
|
||||
if (!isIOS()) {
|
||||
initPlayer();
|
||||
}
|
||||
else {
|
||||
console.log("Detected Apple Shit. Using different player.")
|
||||
} else {
|
||||
console.log('Detected Apple device. Using Vidstack fallback player.');
|
||||
initVidstackPlayer();
|
||||
}
|
||||
|
||||
}
|
||||
}).catch(function (error) {
|
||||
var alert = document.getElementById("player-alert");
|
||||
alert.innerText = 'The player encountered a problem: ' + error;
|
||||
alert.classList.remove("hidden");
|
||||
var alert = document.getElementById('player-alert');
|
||||
if (alert) {
|
||||
alert.innerText = 'The player encountered a problem: ' + error;
|
||||
alert.classList.remove('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user