From f50559ac184cc8ba98e8c025c3b21fb7e0fcad17 Mon Sep 17 00:00:00 2001 From: knight Date: Mon, 5 May 2025 10:56:06 -0400 Subject: [PATCH] Add initial video player implementation with controls and features --- assets/js/player.js | 277 ++++++++++++++++++-------------------------- 1 file changed, 114 insertions(+), 163 deletions(-) diff --git a/assets/js/player.js b/assets/js/player.js index f32c9b56..37917147 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -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 ""; - } -}; - -if (location.pathname.startsWith('/embed/')) { - var overlay_content = '

' + player_data.title + '

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