diff options
| author | Bobby <[email protected]> | 2024-12-24 06:50:59 -0500 |
|---|---|---|
| committer | Bobby <[email protected]> | 2024-12-24 06:50:59 -0500 |
| commit | 6df9f0dc40501e8f55bcc883dfe5be65e60d3c3d (patch) | |
| tree | 273148d564c11ae46e9f96c0231ce57427f5591f /static/js/libs/videoPlayer.js | |
| parent | 4feba2452a151ed999d52d4a0d53b0b0584bf70e (diff) | |
| download | thatcomputerscientist-6df9f0dc40501e8f55bcc883dfe5be65e60d3c3d.tar.xz thatcomputerscientist-6df9f0dc40501e8f55bcc883dfe5be65e60d3c3d.zip | |
bucket load of things
Diffstat (limited to 'static/js/libs/videoPlayer.js')
| -rw-r--r-- | static/js/libs/videoPlayer.js | 159 |
1 files changed, 147 insertions, 12 deletions
diff --git a/static/js/libs/videoPlayer.js b/static/js/libs/videoPlayer.js index 30febb29..076d2aed 100644 --- a/static/js/libs/videoPlayer.js +++ b/static/js/libs/videoPlayer.js @@ -81,6 +81,11 @@ class VideoPlayer { } }; + static STORAGE_KEYS = { + VOLUME: 'videoplayer_volume', + QUALITY: 'videoplayer_quality' + }; + constructor(config = {}) { this.config = this.mergeConfig(VideoPlayer.defaultConfig, config); this.initializeElements(); @@ -89,6 +94,8 @@ class VideoPlayer { this.setupSubtitles(); this.setupFullscreenHandling(); this.setupVideoInteractions(); + this.setupBufferingIndicator(); + this.loadVolume(); if (this.config.keyboard.enabled) { this.setupKeyboardControls(); @@ -176,6 +183,7 @@ class VideoPlayer { setupSource() { const { url, type } = this.config.source; + if (!url) return; if (type === 'hls') { if (!Hls.isSupported()) return; @@ -237,7 +245,9 @@ class VideoPlayer { setupMenuEvents() { const closeMenus = (except) => { Object.entries(this.elements.menus).forEach(([key, menu]) => { - if (key !== except) menu.classList.remove('show'); + try { + if (key !== except) menu.classList.remove('show'); + } catch (e) { } }); }; @@ -261,12 +271,135 @@ class VideoPlayer { // Close menus when clicking outside document.addEventListener('click', (e) => { - if (!e.target.closest('.quality-control')) this.elements.menus.quality.classList.remove('show'); - if (!e.target.closest('.sub-dub-control')) this.elements.menus.subDub.classList.remove('show'); - if (!e.target.closest('#ccBtn')) this.elements.menus.caption.classList.remove('show'); + try { + if (!e.target.closest('.quality-control')) this.elements.menus.quality.classList.remove('show'); + } catch (e) { } + + try { + if (!e.target.closest('.sub-dub-control')) this.elements.menus.subDub.classList.remove('show'); + } catch (e) { } + + try { + if (!e.target.closest('#ccBtn')) this.elements.menus.caption.classList.remove('show'); + } catch (e) { } }); } + saveVolume(volume) { + try { + localStorage.setItem(VideoPlayer.STORAGE_KEYS.VOLUME, volume); + } catch (e) { } + } + + loadVolume() { + try { + const savedVolume = localStorage.getItem(VideoPlayer.STORAGE_KEYS.VOLUME); + if (savedVolume !== null) { + this.updateVolume(parseFloat(savedVolume)); + } + } catch (e) { } + } + + saveQuality(quality) { + try { + localStorage.setItem(VideoPlayer.STORAGE_KEYS.QUALITY, quality); + } catch (e) { } + } + + loadQuality() { + try { + const savedQuality = localStorage.getItem(VideoPlayer.STORAGE_KEYS.QUALITY); + if (savedQuality !== null && this.hls) { + this.hls.currentLevel = parseInt(savedQuality); + // Update quality button text + const qualityButton = this.elements.controls.quality; + const qualityText = savedQuality === '-1' ? 'Auto' : + `${this.hls.levels[savedQuality].height}p`; + qualityButton.innerHTML = this.getQualityButtonHTML(qualityText); + } + } catch (e) { } + } + + getQualityButtonHTML(text) { + return `<svg class="win98-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="2" stroke="currentColor"> + <path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" /> + <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /> + </svg> + <svg class="win98-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="2" stroke="currentColor"> + <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 15 12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" /> + </svg>${text}`; + } + + setupBufferingIndicator() { + const playerContainer = this.elements.video.closest('.win98-player-content'); + let loadingTimeout; + + const isBuffering = () => { + const video = this.elements.video; + + // If seeking or initial load + if (video.seeking || video.readyState < 3) return true; + + // If playing but not enough data + if (!video.paused && video.currentTime > 0) { + // Check if we have data for the current position + for (let i = 0; i < video.buffered.length; i++) { + if (video.currentTime >= video.buffered.start(i) && + video.currentTime <= video.buffered.end(i)) { + // Check if we have enough buffer ahead + const aheadBuffer = video.buffered.end(i) - video.currentTime; + if (aheadBuffer < 0.5) return true; // Less than 0.5 seconds ahead + return false; + } + } + return true; // Current time not in any buffer range + } + return false; + }; + + const showLoading = () => { + if (loadingTimeout) clearTimeout(loadingTimeout); + loadingTimeout = setTimeout(() => { + if (isBuffering()) { + playerContainer.classList.add('video-loading'); + } + }, 100); + }; + + const hideLoading = () => { + if (loadingTimeout) clearTimeout(loadingTimeout); + loadingTimeout = setTimeout(() => { + if (!isBuffering()) { + playerContainer.classList.remove('video-loading'); + } + }, 100); + }; + + // Video events + this.elements.video.addEventListener('waiting', showLoading); + this.elements.video.addEventListener('canplay', hideLoading); + this.elements.video.addEventListener('playing', hideLoading); + this.elements.video.addEventListener('progress', showLoading); // Check on data load + this.elements.video.addEventListener('timeupdate', () => { + if (isBuffering()) showLoading(); + else hideLoading(); + }); + this.elements.video.addEventListener('seeked', hideLoading); + this.elements.video.addEventListener('stalled', showLoading); + + // HLS specific events + if (this.hls) { + this.hls.on(Hls.Events.FRAG_LOADING, showLoading); + this.hls.on(Hls.Events.FRAG_BUFFERED, hideLoading); + this.hls.on(Hls.Events.ERROR, showLoading); + } + + // Initial loading state + if (!this.elements.video.readyState || this.elements.video.readyState < 3) { + playerContainer.classList.add('video-loading'); + } + } + setupQualityMenu(levels) { this.elements.menus.quality.innerHTML = ` <button data-quality="-1">Auto</button> @@ -279,16 +412,14 @@ class VideoPlayer { if (e.target.tagName === 'BUTTON') { const quality = parseInt(e.target.dataset.quality); this.hls.currentLevel = quality; - this.elements.controls.quality.innerHTML = `<svg class="win98-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="2" stroke="currentColor"> - <path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z" /> - <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /> - </svg> - <svg class="win98-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke-width="2" stroke="currentColor"> - <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 15 12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" /> - </svg>${e.target.textContent}`; + this.elements.controls.quality.innerHTML = this.getQualityButtonHTML(e.target.textContent); this.elements.menus.quality.classList.remove('show'); + this.saveQuality(quality); // Save quality setting } }); + + // Load saved quality after menu setup + this.loadQuality(); } setupSubtitles() { @@ -536,7 +667,10 @@ class VideoPlayer { updateTimeDisplay() { const progress = (this.elements.video.currentTime / this.elements.video.duration) * 100; - this.elements.displays.timeCurrent.textContent = this.formatTime(this.elements.video.currentTime); + // If there is a single digit in minutes, add a leading zero + const currentTime = this.formatTime(this.elements.video.currentTime); + const singleDigitMinutes = currentTime.length === 4 && currentTime[1] === ':'; + this.elements.displays.timeCurrent.textContent = singleDigitMinutes ? `0${currentTime}` : currentTime; this.updateSeekDisplay(progress); } @@ -559,6 +693,7 @@ class VideoPlayer { this.updateVolumeSliders(volume); this.updateMuteButton(volume > 0); if (this.elements.video.muted && volume > 0) this.elements.video.muted = false; + this.saveVolume(volume); // Save volume setting } updateVolumeSliders(volume) { |
