What caues the failure to unblock a custom JavaScript video player by using the seeker? [closed]

1 day ago 1
ARTICLE AD BOX

I have been working on a JavaScript video player.

document.addEventListener('DOMContentLoaded', () => { String.prototype.timeFormat = function() { let sec = parseInt(this, 10); let h = Math.floor(sec / 3600); let m = Math.floor((sec % 3600) / 60); let s = sec % 60; if (h < 10) h = '0' + h; if (m < 10) m = '0' + m; if (s < 10) s = '0' + s; return h > 0 ? `${h}:${m}:${s}` : `${m}:${s}`; }; function toggleFullScreen(elem) { if (!document.fullscreenElement) { elem.requestFullscreen?.() || elem.webkitRequestFullscreen?.() || elem.msRequestFullscreen?.(); } else { document.exitFullscreen?.() || document.webkitExitFullscreen?.() || document.msExitFullscreen?.(); } } document.querySelectorAll('.video-container').forEach(container => { const video = container.querySelector('video'); const poster = container.querySelector('.poster'); const spinner = container.querySelector('.loading-spinner'); const playBtn = container.querySelector('input[name="play-pause"]'); const progressBar = container.querySelector('.progress-bar'); const progress = container.querySelector('.progress'); const currentTimeEl = container.querySelector('.current-time'); const durationEl = container.querySelector('.duration'); const volumeSlider = container.querySelector('.volume-slider'); const muteToggle = container.querySelector('.mute-toggle'); const muteCheckbox = muteToggle.querySelector('input[type="checkbox"]'); const rateDisplay = container.querySelector('.rate_display'); const fullscreenBtn = container.querySelector('input[name="screen-toggler"]'); const rateOptions = container.querySelectorAll('.playback-rate ul li'); let manualPaused = false; let isTryingToPlay = false; function showSpinner() { spinner.style.opacity = 1; } function hideSpinner() { spinner.style.opacity = 0; } function updatePlayPause() { if (video.paused) { playBtn.value = 'Play'; playBtn.classList.add('play'); playBtn.classList.remove('pause'); } else { playBtn.value = 'Pause'; playBtn.classList.remove('play'); playBtn.classList.add('pause'); } } function updateProgress() { const dur = isFinite(video.duration) ? video.duration : 0; const cur = isFinite(video.currentTime) ? video.currentTime : 0; const percent = dur > 0 ? (cur / dur) * 100 : 0; progress.style.width = percent + '%'; currentTimeEl.textContent = Math.round(cur).toString().timeFormat(); durationEl.textContent = dur > 0 ? Math.round(dur).toString().timeFormat() : '--:--'; } async function attemptPlay() { if (manualPaused || isTryingToPlay) return; isTryingToPlay = true; showSpinner(); try { await video.play(); } catch {} hideSpinner(); isTryingToPlay = false; updatePlayPause(); } playBtn.addEventListener('click', () => { manualPaused = !video.paused; if (!manualPaused) { poster.style.display = 'none'; attemptPlay(); } else { video.pause(); } }); volumeSlider.addEventListener('input', () => { video.volume = volumeSlider.value; video.muted = video.volume === 0; muteCheckbox.checked = video.muted; muteToggle.classList.toggle('muted', video.muted); }); muteToggle.addEventListener('click', () => { video.muted = muteCheckbox.checked; muteToggle.classList.toggle('muted', video.muted); }); progressBar.addEventListener('click', e => { const rect = progressBar.getBoundingClientRect(); const dur = isFinite(video.duration) && video.duration > 0 ? video.duration : 100; video.currentTime = ((e.clientX - rect.left) / rect.width) * dur; updateProgress(); attemptPlay(); }); fullscreenBtn.addEventListener('click', () => { container.classList.toggle('fullscreen'); fullscreenBtn.classList.toggle('exit'); toggleFullScreen(document.body); }); document.addEventListener('fullscreenchange', () => { if (!document.fullscreenElement) { container.classList.remove('fullscreen'); fullscreenBtn.classList.remove('exit'); } }); rateOptions.forEach(opt => { opt.addEventListener('click', () => { video.playbackRate = parseFloat(opt.dataset.rate); rateDisplay.textContent = video.playbackRate === 1 ? 'Normal' : video.playbackRate + 'x'; }); }); video.addEventListener('loadedmetadata', updateProgress); video.addEventListener('timeupdate', updateProgress); video.addEventListener('waiting', showSpinner); video.addEventListener('stalled', showSpinner); video.addEventListener('seeking', showSpinner); video.addEventListener('playing', () => { hideSpinner(); updatePlayPause(); }); video.addEventListener('canplay', () => { hideSpinner(); updatePlayPause(); }); video.addEventListener('pause', updatePlayPause); video.addEventListener('ended', updatePlayPause); video.addEventListener('seeked', () => { updateProgress(); if (!manualPaused) attemptPlay(); updatePlayPause(); }); }); }); .video-container { font-family: Arial, sans-serif; max-width: 960px; margin: 0 auto; position: relative; overflow: hidden; aspect-ratio: 16 / 9; } .video-container video, .video-container .poster { width: 100%; height: 100%; object-fit: cover; display: block; } /* Video loading indicator (spinner) */ @keyframes spin { to { transform: rotate(360deg) } } .loading-spinner { position: absolute; top: 0; bottom: 0; left: 0; right: 0; background: rgba(0, 0, 0, 0.6); display: flex; align-items: center; justify-content: center; z-index: 5; pointer-events: none; opacity: 0; transition: opacity 0.2s ease; pointer-events: none; } .video-container.loading .loading-spinner { opacity: 1; } .spinner { width: 48px; height: 48px; border: 4px solid rgba(255, 255, 255, 0.3); border-top-color: #fff; border-radius: 50%; animation: spin 0.9s linear infinite; } .video-container:hover .controls-wrapper { bottom: 0; } .video-container.fullscreen { max-width: none; position: fixed; top: 0; bottom: 0; left: 0; right: 0; z-index: 1021; } .video-container.fullscreen video { position: absolute; top: 50%; -webkit-transform: translateY(-50%); -ms-transform: translateY(-50%); transform: translateY(-50%); } .controls-wrapper { position: absolute; height: 50px; bottom: -57px; left: 0; right: 0; z-index: 10; -webkit-transition: all 0.25s ease-in-out; -moz-transition: all 0.25s ease-in-out; transition: all 0.25s ease-in-out; } .controls-wrapper * { cursor: pointer; } .controls-wrapper span { cursor: default; color: #fff; } .progress-bar { height: 5px; width: 100%; background: #777; cursor: pointer; } .progress { height: 5px; width: 0%; max-width: 100%; background: #c62129; } .video-controls { list-style-type: none; margin: 0; padding: 0; display: flex; justify-content: space-between; align-items: center; height: 45px; background: linear-gradient(to top, rgba(0, 0, 0, 0.4) 0, rgba(0, 0, 0, 0) 100%); } .video-controls>li { font-size: 12px; margin: 0 5px; } .video-controls li.fullscreen-container { margin: 0 10px 0 auto; } .video-controls li input { display: inline-block; padding: 0 2px; float: left; height: 24px; line-height: 24px; cursor: pointer; width: 20px; overflow: hidden; line-height: 999px; margin: 0; border: none; background: transparent; outline: none; background: url("https://i.sstatic.net/Da9PZ5i4.png") no-repeat top left; width: 20px; height: 20px; } .video-controls li span { display: inline-block; padding: 0 2px; float: left; height: 24px; line-height: 24px; } .video-controls li a { display: inline-block; padding: 0 2px; float: left; height: 24px; line-height: 24px; cursor: pointer; width: 20px; overflow: hidden; line-height: 999px; margin: 0; border: none; background: transparent; outline: none; text-decoration: none; background: url("https://i.sstatic.net/Da9PZ5i4.png") no-repeat top left; width: 20px; height: 20px; } .video-controls li input[type="checkbox"] { opacity: 0; } .video-controls li input.volume-slider { width: 100px; background: #c62129; height: 5px; } .video-controls li input.play { background-position: 0 0; } .video-controls li input.pause { background-position: 0 -30px; } .video-controls li .previous { background-position: 0 -60px; } .video-controls li .next { background-position: 0 -90px; } .video-controls li input.toggle-fullscreen { background-position: 0 -180px; } .video-controls li input.toggle-fullscreen.exit { background-position: 0 -210px; } .video-controls input[type="range"] { -webkit-appearance: none; height: 4px; background: #c62129; opacity: 0.8; padding: 0; margin: 0; outline: none; overflow: visible; } .video-controls input[type="range"]:focus { -webkit-appearance: none; height: 4px; background: #c62129; opacity: 0.8; padding: 0; margin: 0; outline: none; overflow: visible; } .video-controls input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; height: 10px; width: 10px; border-radius: 50%; background: #fff; cursor: pointer; margin-top: -1px; } .video-controls input[type="range"]::-moz-range-thumb { -webkit-appearance: none; height: 10px; width: 10px; border-radius: 50%; background: #fff; cursor: pointer; margin-top: -2px; } .video-controls li.mute-toggle { background: url("https://i.sstatic.net/Da9PZ5i4.png") no-repeat top left; width: 20px; height: 20px; } .video-controls li.unmuted { background-position: 0 -120px; } .video-controls li.muted { background-position: 0 -150px; } .playback-rate { position: relative; } .playback-rate span { width: 55px; text-align: center; color: #1c232c; padding: 5px; border-radius: 2px; background: rgba(255, 255, 255, 0.7); cursor: pointer; } .playback-rate .piker { cursor: pointer; transition: opacity 1s ease-in-out; display: none; position: absolute; opacity: 0; bottom: 0; padding-bottom: 30px; width: 55px; } .playback-rate:hover .piker { display: block; opacity: 1; text-align: center; } .playback-rate ul { padding: 0; margin: 0; overflow: hidden; border-radius: 2px; background: rgba(0, 0, 0, 0.75); box-shadow: 0 1px 1px 0px rgba(0, 0, 0, 0.12); } .playback-rate ul li { color: #fff; text-align: right; margin: 0; display: block; height: 20px; line-height: 20px; padding: 0 5px; } .playback-rate ul li:hover { background: rgba(255, 255, 255, 0.1); } <div class="video-container mb-3"> <img src="https://fastly.picsum.photos/id/866/1200/800.jpg?hmac=cMZ0zQRwnCFQskIqM7bmp90xVm9-k6lHUAFEJZp3mw4" class="poster" /> <div class="loading-spinner"> <div class="spinner"></div> </div> <video src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" type="video/mp4" ></video> <div class="controls-wrapper"> <div class="progress-bar"> <div class="progress"></div> </div> <ul class="video-controls"> <li> <input type="button" name="play-pause" value="Play" class="play" /> </li> <li> <a href="https://larablog.com/show/enhancing-female-pleasure-techniques-for-deeper-intimacy" class="previous" title="Previous video: Whatever" > Previous </a> </li> <li></li> <li class="mute-toggle unmuted"> <input type="checkbox" name="mute" /> </li> <li> <input type="range" min="0" max="1" step="0.01" class="volume-slider" /> </li> <li class="timer"> <span class="current-time"></span><span>/</span><span class="duration"></span> </li> <li class="playback-rate"> <span class="rate_display">Normal</span> <div class="piker"> <ul class="dropdown-content" id="rate_selector"> <li data-rate="0.5">0.5x</li> <li data-rate="0.75">0.75x</li> <li data-rate="1">Normal</li> <li data-rate="1.125">1.125x</li> <li data-rate="1.5">1.5x</li> <li data-rate="2">2x</li> </ul> </div> </li> <li class="fullscreen-container"> <input type="button" name="screen-toggler" value="Fullscreen" class="toggle-fullscreen" /> </li> </ul> </div> </div>

The problem I am faced with is this: when the video is very big (100 MB or more), it gets blocked, and trying to unblock it by seeking an already buffered point on the timeline fails to unblock it.

What causes this bug? What is a reliable way to make it possible for the user to unblock a video by navigating back on the timeline?

Read Entire Article