aboutsummaryrefslogtreecommitdiffstats
path: root/youtube/static/js/watch.js
diff options
context:
space:
mode:
Diffstat (limited to 'youtube/static/js/watch.js')
-rw-r--r--youtube/static/js/watch.js201
1 files changed, 201 insertions, 0 deletions
diff --git a/youtube/static/js/watch.js b/youtube/static/js/watch.js
new file mode 100644
index 0000000..38aab3f
--- /dev/null
+++ b/youtube/static/js/watch.js
@@ -0,0 +1,201 @@
+var video = document.getElementById('js-video-player');
+
+function changeQuality(selection) {
+ var currentVideoTime = video.currentTime;
+ var videoPaused = video.paused;
+ var videoSpeed = video.playbackRate;
+ var videoSource;
+ if (avMerge)
+ avMerge.close();
+ if (selection.type == 'uni'){
+ videoSource = data['uni_sources'][selection.index];
+ video.src = videoSource.url;
+ } else {
+ let srcPair = data['pair_sources'][selection.index];
+ videoSource = srcPair[0];
+ avMerge = new AVMerge(video, srcPair, currentVideoTime);
+ }
+ video.currentTime = currentVideoTime;
+ if (!videoPaused){
+ video.play();
+ }
+ video.playbackRate = videoSpeed;
+}
+
+// Initialize av-merge
+var avMerge;
+if (data.using_pair_sources) {
+ var srcPair = data['pair_sources'][data['pair_idx']];
+ var videoSource = srcPair[0];
+ // Do it dynamically rather than as the default in jinja
+ // in case javascript is disabled
+ avMerge = new AVMerge(video, srcPair, 0);
+}
+
+// Quality selector
+document.getElementById('quality-select').addEventListener(
+ 'change', function(e) {
+ changeQuality(JSON.parse(this.value))
+ }
+);
+
+// Set up video start time from &t parameter
+if (data.time_start != 0 && video)
+ video.currentTime = data.time_start;
+
+// External video speed control
+var speedInput = document.getElementById('speed-control');
+speedInput.addEventListener('keyup', (event) => {
+ if (event.key === 'Enter') {
+ var speed = parseFloat(speedInput.value);
+ if(!isNaN(speed)){
+ video.playbackRate = speed;
+ }
+ }
+});
+
+
+// Playlist lazy image loading
+if (data.playlist && data.playlist['id'] !== null) {
+ // lazy load playlist images
+ // copied almost verbatim from
+ // https://css-tricks.com/tips-for-rolling-your-own-lazy-loading/
+ // IntersectionObserver isn't supported in pre-quantum
+ // firefox versions, but the alternative of making it
+ // manually is a performance drain, so oh well
+ var observer = new IntersectionObserver(lazyLoad, {
+
+ // where in relation to the edge of the viewport, we are observing
+ rootMargin: "100px",
+
+ // how much of the element needs to have intersected
+ // in order to fire our loading function
+ threshold: 1.0
+
+ });
+
+ function lazyLoad(elements) {
+ elements.forEach(item => {
+ if (item.intersectionRatio > 0) {
+
+ // set the src attribute to trigger a load
+ item.target.src = item.target.dataset.src;
+
+ // stop observing this element. Our work here is done!
+ observer.unobserve(item.target);
+ };
+ });
+ };
+
+ // Tell our observer to observe all img elements with a "lazy" class
+ var lazyImages = document.querySelectorAll('img.lazy');
+ lazyImages.forEach(img => {
+ observer.observe(img);
+ });
+}
+
+
+// Autoplay
+if (data.settings.related_videos_mode !== 0 || data.playlist !== null) {
+ let playability_error = !!data.playability_error;
+ let isPlaylist = false;
+ if (data.playlist !== null && data.playlist['current_index'] !== null)
+ isPlaylist = true;
+
+ // read cookies on whether to autoplay
+ // https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie
+ let cookieValue;
+ let playlist_id;
+ if (isPlaylist) {
+ // from https://stackoverflow.com/a/6969486
+ function escapeRegExp(string) {
+ // $& means the whole matched string
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ }
+ playlist_id = data.playlist['id'];
+ playlist_id = escapeRegExp(playlist_id);
+
+ cookieValue = document.cookie.replace(new RegExp(
+ '(?:(?:^|.*;\\s*)autoplay_'
+ + playlist_id + '\\s*\\=\\s*([^;]*).*$)|^.*$'
+ ), '$1');
+ } else {
+ cookieValue = document.cookie.replace(new RegExp(
+ '(?:(?:^|.*;\\s*)autoplay\\s*\\=\\s*([^;]*).*$)|^.*$'
+ ),'$1');
+ }
+
+ let autoplayEnabled = 0;
+ if(cookieValue.length === 0){
+ autoplayEnabled = 0;
+ } else {
+ autoplayEnabled = Number(cookieValue);
+ }
+
+ // check the checkbox if autoplay is on
+ let checkbox = document.querySelector('.autoplay-toggle');
+ if(autoplayEnabled){
+ checkbox.checked = true;
+ }
+
+ // listen for checkbox to turn autoplay on and off
+ let cookie = 'autoplay'
+ if (isPlaylist)
+ cookie += '_' + playlist_id;
+
+ checkbox.addEventListener( 'change', function() {
+ if(this.checked) {
+ autoplayEnabled = 1;
+ document.cookie = cookie + '=1; SameSite=Strict';
+ } else {
+ autoplayEnabled = 0;
+ document.cookie = cookie + '=0; SameSite=Strict';
+ }
+ });
+
+ if(!playability_error){
+ // play the video if autoplay is on
+ if(autoplayEnabled){
+ video.play();
+ }
+ }
+
+ // determine next video url
+ let nextVideoUrl;
+ if (isPlaylist) {
+ let currentIndex = data.playlist['current_index'];
+ if (data.playlist['current_index']+1 == data.playlist['items'].length)
+ nextVideoUrl = null;
+ else
+ nextVideoUrl = data.playlist['items'][data.playlist['current_index']+1]['url'];
+
+ // scroll playlist to proper position
+ // item height + gap == 100
+ let pl = document.querySelector('.playlist-videos');
+ pl.scrollTop = 100*currentIndex;
+ } else {
+ if (data.related.length === 0)
+ nextVideoUrl = null;
+ else
+ nextVideoUrl = data.related[0]['url'];
+ }
+ let nextVideoDelay = 1000;
+
+ // go to next video when video ends
+ // https://stackoverflow.com/a/2880950
+ if (nextVideoUrl) {
+ if(playability_error){
+ videoEnded();
+ } else {
+ video.addEventListener('ended', videoEnded, false);
+ }
+ function nextVideo(){
+ if(autoplayEnabled){
+ window.location.href = nextVideoUrl;
+ }
+ }
+ function videoEnded(e) {
+ window.setTimeout(nextVideo, nextVideoDelay);
+ }
+ }
+}