Add initial video player implementation with controls and features
Some checks failed
Build and release container directly from master / release (push) Failing after 34s
Some checks failed
Build and release container directly from master / release (push) Failing after 34s
This commit is contained in:
parent
d1bc15b8bf
commit
f50559ac18
@ -1,6 +1,8 @@
|
||||
'use strict';
|
||||
console.log('[Invidious Debug] player.js start');
|
||||
var player_data = JSON.parse(document.getElementById('player_data').textContent);
|
||||
var video_data = JSON.parse(document.getElementById('video_data').textContent);
|
||||
console.log('[Invidious Debug] video_data.params:', JSON.stringify(video_data.params));
|
||||
|
||||
var options = {
|
||||
liveui: true,
|
||||
@ -50,9 +52,19 @@ videojs.Vhs.xhr.beforeRequest = function(options) {
|
||||
return options;
|
||||
};
|
||||
|
||||
var player = videojs('player', options);
|
||||
console.log('[Invidious Debug] Initializing videojs...');
|
||||
var player;
|
||||
try {
|
||||
player = videojs('player', options);
|
||||
console.log('[Invidious Debug] videojs initialized successfully.');
|
||||
} catch (e) {
|
||||
console.error('[Invidious Debug] videojs initialization FAILED:', e);
|
||||
// Stop further execution if player init fails
|
||||
throw e;
|
||||
}
|
||||
|
||||
player.on('error', function () {
|
||||
console.error('[Invidious Debug] Player error event triggered:', player.error());
|
||||
if (video_data.params.quality === 'dash') return;
|
||||
|
||||
var localNotDisabled = (
|
||||
@ -70,6 +82,7 @@ player.on('error', function () {
|
||||
return source;
|
||||
}));
|
||||
} else if (reloadMakesSense) {
|
||||
console.log('[Invidious Debug] Player error: Attempting reload in 5 seconds.');
|
||||
setTimeout(function () {
|
||||
console.warn('An error occurred in the player, reloading...');
|
||||
|
||||
@ -86,13 +99,21 @@ player.on('error', function () {
|
||||
player.playbackRate(playbackRate);
|
||||
if (!paused) player.play();
|
||||
}, 5000);
|
||||
} else {
|
||||
console.log('[Invidious Debug] Player error: No specific action taken.');
|
||||
}
|
||||
});
|
||||
|
||||
if (video_data.params.quality === 'dash') {
|
||||
player.reloadSourceOnError({
|
||||
errorInterval: 10
|
||||
});
|
||||
console.log('[Invidious Debug] Initializing reloadSourceOnError...');
|
||||
try {
|
||||
player.reloadSourceOnError({
|
||||
errorInterval: 10
|
||||
});
|
||||
console.log('[Invidious Debug] reloadSourceOnError initialized.');
|
||||
} catch (e) {
|
||||
console.error('[Invidious Debug] reloadSourceOnError FAILED:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -162,116 +183,6 @@ player.on('timeupdate', function () {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var shareOptions = {
|
||||
socials: ['fbFeed', 'tw', 'reddit', 'email'],
|
||||
|
||||
get url() {
|
||||
return addCurrentTimeToURL(short_url);
|
||||
},
|
||||
title: player_data.title,
|
||||
description: player_data.description,
|
||||
image: player_data.thumbnail,
|
||||
get embedCode() {
|
||||
// Single quotes inside here required. HTML inserted as is into value attribute of input
|
||||
return "<iframe id='ivplayer' width='640' height='360' src='" +
|
||||
addCurrentTimeToURL(embed_url) + "' style='border:none;'></iframe>";
|
||||
}
|
||||
};
|
||||
|
||||
if (location.pathname.startsWith('/embed/')) {
|
||||
var overlay_content = '<h1><a rel="noopener" target="_blank" href="' + location.origin + '/watch?v=' + video_data.id + '">' + player_data.title + '</a></h1>';
|
||||
player.overlay({
|
||||
overlays: [
|
||||
{ start: 'loadstart', content: overlay_content, end: 'playing', align: 'top'},
|
||||
{ start: 'pause', content: overlay_content, end: 'playing', align: 'top'}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// Detect mobile users and initialize mobileUi for better UX
|
||||
// Detection code taken from https://stackoverflow.com/a/20293441
|
||||
|
||||
function isMobile() {
|
||||
try{ document.createEvent('TouchEvent'); return true; }
|
||||
catch(e){ return false; }
|
||||
}
|
||||
|
||||
if (isMobile()) {
|
||||
player.mobileUi({ touchControls: { seekSeconds: 5 * player.playbackRate() } });
|
||||
|
||||
var buttons = ['playToggle', 'volumePanel', 'captionsButton'];
|
||||
|
||||
if (!video_data.params.listen && video_data.params.quality === 'dash') buttons.push('audioTrackButton');
|
||||
if (video_data.params.listen || video_data.params.quality !== 'dash') buttons.push('qualitySelector');
|
||||
|
||||
// Create new control bar object for operation buttons
|
||||
const ControlBar = videojs.getComponent('controlBar');
|
||||
let operations_bar = new ControlBar(player, {
|
||||
children: [],
|
||||
playbackRates: [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0]
|
||||
});
|
||||
buttons.slice(1).forEach(function (child) {operations_bar.addChild(child);});
|
||||
|
||||
// Remove operation buttons from primary control bar
|
||||
var primary_control_bar = player.getChild('controlBar');
|
||||
buttons.forEach(function (child) {primary_control_bar.removeChild(child);});
|
||||
|
||||
var operations_bar_element = operations_bar.el();
|
||||
operations_bar_element.classList.add('mobile-operations-bar');
|
||||
player.addChild(operations_bar);
|
||||
|
||||
// Playback menu doesn't work when it's initialized outside of the primary control bar
|
||||
var playback_element = document.getElementsByClassName('vjs-playback-rate')[0];
|
||||
operations_bar_element.append(playback_element);
|
||||
|
||||
// The share and http source selector element can't be fetched till the players ready.
|
||||
player.one('playing', function () {
|
||||
var share_element = document.getElementsByClassName('vjs-share-control')[0];
|
||||
operations_bar_element.append(share_element);
|
||||
|
||||
if (!video_data.params.listen && video_data.params.quality === 'dash') {
|
||||
var http_source_selector = document.getElementsByClassName('vjs-http-source-selector vjs-menu-button')[0];
|
||||
operations_bar_element.append(http_source_selector);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Enable VR video support
|
||||
if (!video_data.params.listen && video_data.vr && video_data.params.vr_mode) {
|
||||
player.crossOrigin('anonymous');
|
||||
switch (video_data.projection_type) {
|
||||
case 'EQUIRECTANGULAR':
|
||||
player.vr({projection: 'equirectangular'});
|
||||
default: // Should only be 'MESH' but we'll use this as a fallback.
|
||||
player.vr({projection: 'EAC'});
|
||||
}
|
||||
}
|
||||
|
||||
// Add markers
|
||||
if (video_data.params.video_start > 0 || video_data.params.video_end > 0) {
|
||||
var markers = [{ time: video_data.params.video_start, text: 'Start' }];
|
||||
|
||||
if (video_data.params.video_end < 0) {
|
||||
markers.push({ time: video_data.length_seconds - 0.5, text: 'End' });
|
||||
} else {
|
||||
markers.push({ time: video_data.params.video_end, text: 'End' });
|
||||
}
|
||||
|
||||
player.markers({
|
||||
onMarkerReached: function (marker) {
|
||||
if (marker.text === 'End')
|
||||
player.loop() ? player.markers.prev('Start') : player.pause();
|
||||
},
|
||||
markers: markers
|
||||
});
|
||||
|
||||
player.currentTime(video_data.params.video_start);
|
||||
}
|
||||
|
||||
player.volume(video_data.params.volume / 100);
|
||||
player.playbackRate(video_data.params.speed);
|
||||
|
||||
/**
|
||||
* Method for getting the contents of a cookie
|
||||
*
|
||||
@ -351,16 +262,12 @@ if (video_data.premiere_timestamp && Math.round(new Date() / 1000) < video_data.
|
||||
}
|
||||
|
||||
if (video_data.params.save_player_pos) {
|
||||
const url = new URL(location);
|
||||
const hasTimeParam = url.searchParams.has('t');
|
||||
const rememberedTime = get_video_time();
|
||||
let lastUpdated = 0;
|
||||
|
||||
if(!hasTimeParam) {
|
||||
if (rememberedTime >= video_data.length_seconds - 20)
|
||||
set_seconds_after_start(0);
|
||||
else
|
||||
set_seconds_after_start(rememberedTime);
|
||||
console.log('[Invidious Debug] Setting up player position save/restore...');
|
||||
var lastUpdated = 0;
|
||||
var save_video_time = function(time) {
|
||||
const all_video_times = get_all_video_times();
|
||||
all_video_times[video_data.id] = time;
|
||||
helpers.storage.set(save_player_pos_key, all_video_times);
|
||||
}
|
||||
|
||||
player.on('timeupdate', function () {
|
||||
@ -376,10 +283,12 @@ if (video_data.params.save_player_pos) {
|
||||
else remove_all_video_times();
|
||||
|
||||
if (video_data.params.autoplay) {
|
||||
console.log('[Invidious Debug] Handling autoplay setup...');
|
||||
var bpb = player.getChild('bigPlayButton');
|
||||
bpb.hide();
|
||||
|
||||
player.ready(function () {
|
||||
console.log('[Invidious Debug] Player ready for autoplay.');
|
||||
new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {resolve(1);}, 1);
|
||||
}).then(function (result) {
|
||||
@ -387,53 +296,77 @@ if (video_data.params.autoplay) {
|
||||
|
||||
if (promise !== undefined) {
|
||||
promise.then(function () {
|
||||
console.log('[Invidious Debug] Autoplay successful.');
|
||||
}).catch(function (error) {
|
||||
console.error('[Invidious Debug] Autoplay FAILED:', error);
|
||||
bpb.show();
|
||||
});
|
||||
} else {
|
||||
console.log('[Invidious Debug] Autoplay started (no promise returned).');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (!video_data.params.listen && video_data.params.quality === 'dash') {
|
||||
player.httpSourceSelector();
|
||||
console.log('[Invidious Debug] Initializing httpSourceSelector...');
|
||||
try {
|
||||
player.httpSourceSelector();
|
||||
console.log('[Invidious Debug] httpSourceSelector initialized.');
|
||||
|
||||
if (video_data.params.quality_dash !== 'auto') {
|
||||
player.ready(function () {
|
||||
player.on('loadedmetadata', function () {
|
||||
const qualityLevels = Array.from(player.qualityLevels()).sort(function (a, b) {return a.height - b.height;});
|
||||
let targetQualityLevel;
|
||||
switch (video_data.params.quality_dash) {
|
||||
case 'best':
|
||||
targetQualityLevel = qualityLevels.length - 1;
|
||||
break;
|
||||
case 'worst':
|
||||
targetQualityLevel = 0;
|
||||
break;
|
||||
default:
|
||||
const targetHeight = parseInt(video_data.params.quality_dash);
|
||||
for (let i = 0; i < qualityLevels.length; i++) {
|
||||
if (qualityLevels[i].height <= targetHeight)
|
||||
targetQualityLevel = i;
|
||||
else
|
||||
if (video_data.params.quality_dash !== 'auto') {
|
||||
console.log('[Invidious Debug] Setting DASH quality:', video_data.params.quality_dash);
|
||||
player.ready(function () {
|
||||
player.on('loadedmetadata', function () {
|
||||
try {
|
||||
const qualityLevels = Array.from(player.qualityLevels()).sort(function (a, b) {return a.height - b.height;});
|
||||
let targetQualityLevel;
|
||||
switch (video_data.params.quality_dash) {
|
||||
case 'best':
|
||||
targetQualityLevel = qualityLevels.length - 1;
|
||||
break;
|
||||
case 'worst':
|
||||
targetQualityLevel = 0;
|
||||
break;
|
||||
default:
|
||||
const targetHeight = parseInt(video_data.params.quality_dash);
|
||||
for (let i = 0; i < qualityLevels.length; i++) {
|
||||
if (qualityLevels[i].height <= targetHeight)
|
||||
targetQualityLevel = i;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
qualityLevels.forEach(function (level, index) {
|
||||
level.enabled = (index === targetQualityLevel);
|
||||
qualityLevels.forEach(function (level, index) {
|
||||
level.enabled = (index === targetQualityLevel);
|
||||
});
|
||||
console.log('[Invidious Debug] DASH quality levels set.');
|
||||
} catch (e) {
|
||||
console.error('[Invidious Debug] Error setting DASH quality levels:', e);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error('[Invidious Debug] httpSourceSelector FAILED:', e);
|
||||
}
|
||||
}
|
||||
|
||||
player.vttThumbnails({
|
||||
src: '/api/v1/storyboards/' + video_data.id + '?height=90',
|
||||
showTimestamp: true
|
||||
});
|
||||
console.log('[Invidious Debug] Initializing vttThumbnails...');
|
||||
try {
|
||||
player.vttThumbnails({
|
||||
src: '/api/v1/storyboards/' + video_data.id + '?height=90',
|
||||
showTimestamp: true
|
||||
});
|
||||
console.log('[Invidious Debug] vttThumbnails initialized.');
|
||||
} catch (e) {
|
||||
console.error('[Invidious Debug] vttThumbnails FAILED:', e);
|
||||
}
|
||||
|
||||
// Enable annotations
|
||||
if (!video_data.params.listen && video_data.params.annotations) {
|
||||
console.log('[Invidious Debug] Initializing annotations...');
|
||||
addEventListener('load', function (e) {
|
||||
addEventListener('__ar_annotation_click', function (e) {
|
||||
const url = e.detail.url,
|
||||
@ -468,9 +401,9 @@ if (!video_data.params.listen && video_data.params.annotations) {
|
||||
} else {
|
||||
player.youtubeAnnotationsPlugin({ annotationXml: response, videoContainer: video_container });
|
||||
}
|
||||
console.log('[Invidious Debug] Annotations initialized.');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -732,7 +665,15 @@ addEventListener('keydown', function (e) {
|
||||
}());
|
||||
|
||||
// Since videojs-share can sometimes be blocked, we defer it until last
|
||||
if (player.share) player.share(shareOptions);
|
||||
if (player.share) {
|
||||
console.log('[Invidious Debug] Initializing share plugin...');
|
||||
try {
|
||||
player.share(shareOptions);
|
||||
console.log('[Invidious Debug] Share plugin initialized.');
|
||||
} catch(e) {
|
||||
console.error('[Invidious Debug] Share plugin FAILED:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// show the preferred caption by default
|
||||
if (player_data.preferred_caption_found) {
|
||||
@ -748,6 +689,7 @@ if (player_data.preferred_caption_found) {
|
||||
|
||||
// Safari audio double duration fix
|
||||
if (navigator.vendor === 'Apple Computer, Inc.' && video_data.params.listen) {
|
||||
console.log('[Invidious Debug] Applying Safari listen mode duration fix...');
|
||||
player.on('loadedmetadata', function () {
|
||||
player.on('timeupdate', function () {
|
||||
if (player.remainingTime() < player.duration() / 2 && player.remainingTime() >= 2) {
|
||||
@ -759,6 +701,7 @@ if (navigator.vendor === 'Apple Computer, Inc.' && video_data.params.listen) {
|
||||
|
||||
// Safari screen timeout on looped video playback fix
|
||||
if (navigator.vendor === 'Apple Computer, Inc.' && !video_data.params.listen && video_data.params.video_loop) {
|
||||
console.log('[Invidious Debug] Applying Safari loop mode screen timeout fix...');
|
||||
player.loop(false);
|
||||
player.ready(function () {
|
||||
player.on('ended', function () {
|
||||
@ -770,19 +713,25 @@ if (navigator.vendor === 'Apple Computer, Inc.' && !video_data.params.listen &&
|
||||
|
||||
// Watch on Invidious link
|
||||
if (location.pathname.startsWith('/embed/')) {
|
||||
const Button = videojs.getComponent('Button');
|
||||
let watch_on_invidious_button = new Button(player);
|
||||
console.log('[Invidious Debug] Adding Watch on Invidious button...');
|
||||
try {
|
||||
const Button = videojs.getComponent('Button');
|
||||
let watch_on_invidious_button = new Button(player);
|
||||
|
||||
// Create hyperlink for current instance
|
||||
var redirect_element = document.createElement('a');
|
||||
redirect_element.setAttribute('href', location.pathname.replace('/embed/', '/watch?v='));
|
||||
redirect_element.appendChild(document.createTextNode('Invidious'));
|
||||
// Create hyperlink for current instance
|
||||
var redirect_element = document.createElement('a');
|
||||
redirect_element.setAttribute('href', location.pathname.replace('/embed/', '/watch?v='));
|
||||
redirect_element.appendChild(document.createTextNode('Invidious'));
|
||||
|
||||
watch_on_invidious_button.el().appendChild(redirect_element);
|
||||
watch_on_invidious_button.addClass('watch-on-invidious');
|
||||
watch_on_invidious_button.el().appendChild(redirect_element);
|
||||
watch_on_invidious_button.addClass('watch-on-invidious');
|
||||
|
||||
var cb = player.getChild('ControlBar');
|
||||
cb.addChild(watch_on_invidious_button);
|
||||
var cb = player.getChild('ControlBar');
|
||||
cb.addChild(watch_on_invidious_button);
|
||||
console.log('[Invidious Debug] Watch on Invidious button added.');
|
||||
} catch (e) {
|
||||
console.error('[Invidious Debug] Watch on Invidious button FAILED:', e);
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener('DOMContentLoaded', function () {
|
||||
@ -792,3 +741,5 @@ addEventListener('DOMContentLoaded', function () {
|
||||
changeInstanceLink.href = addCurrentTimeToURL(changeInstanceLink.href);
|
||||
});
|
||||
});
|
||||
|
||||
console.log('[Invidious Debug] player.js finished loading.');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user