Add initial video player implementation with controls and features
Some checks failed
Build and release container directly from master / release (push) Failing after 34s

This commit is contained in:
knight 2025-05-05 10:56:06 -04:00
parent d1bc15b8bf
commit f50559ac18

View File

@ -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.');