diff options
author | Umimaso <git@umimaso.com> | 2021-06-21 00:56:26 +0100 |
---|---|---|
committer | Jesús <heckyel@hyperbola.info> | 2021-06-23 14:34:55 -0500 |
commit | 2d1794889aa3591b31a62f325a0c11a8553ba2d9 (patch) | |
tree | 8695134d0fcdf2b04e5a21d494640632bb33d9a6 | |
parent | ff7aae05c45ccbde4fd14e6e5e7e3cb4edf8c816 (diff) | |
download | yt-local-2d1794889aa3591b31a62f325a0c11a8553ba2d9.tar.lz yt-local-2d1794889aa3591b31a62f325a0c11a8553ba2d9.tar.xz yt-local-2d1794889aa3591b31a62f325a0c11a8553ba2d9.zip |
feat: autoplay for related videos
Add autoplay support for related videos. Move the playlist autoplay code
into this shared script. Add the SameSite=Strict attribute to the
autoplay cookie due to Firefox soon rejecting cookies which use
SameSite=None without the secure attribute.
Closes: #50
Signed-off-by: Jesús <heckyel@hyperbola.info>
-rw-r--r-- | youtube/static/watch.css | 8 | ||||
-rw-r--r-- | youtube/templates/watch.html | 262 |
2 files changed, 152 insertions, 118 deletions
diff --git a/youtube/static/watch.css b/youtube/static/watch.css index 90c08d5..60abd21 100644 --- a/youtube/static/watch.css +++ b/youtube/static/watch.css @@ -345,6 +345,14 @@ label[for=options-toggle-cbox] { .side-videos { grid-area: side-videos; } +.side-videos .related-autoplay { + list-style: none; + display: grid; + grid-template-columns: repeat(2, auto); + justify-content: start; + grid-column-gap: 0.5rem; +} + /* playlist items */ .side-videos .site-playlist { border-style: solid; diff --git a/youtube/templates/watch.html b/youtube/templates/watch.html index 0b61f92..47880b8 100644 --- a/youtube/templates/watch.html +++ b/youtube/templates/watch.html @@ -150,124 +150,42 @@ <!-- playlist --> {% if playlist %} - <div class="site-playlist"> - <div class="playlist-header"> - <a href="{{ playlist['url'] }}" title="{{ playlist['title'] }}"><h3>{{ playlist['title'] }}</h3></a> - <ul class="playlist-metadata"> - <li>Autoplay: <input type="checkbox" id="autoplay-toggle"></li> - {% if playlist['current_index'] is none %} - <li>[Error!]/{{ playlist['video_count'] }}</li> - {% else %} - <li>{{ playlist['current_index']+1 }}/{{ playlist['video_count'] }}</li> - {% endif %} - <li><a href="{{ playlist['author_url'] }}" title="{{ playlist['author'] }}">{{ playlist['author'] }}</a></li> - </ul> - </div> - <nav class="playlist-videos"> - {% for info in playlist['items'] %} - {# non-lazy load for 5 videos surrounding current video #} - {# for non-js browsers or old such that IntersectionObserver doesn't work #} - {# -10 is sentinel to not load anything if there's no current_index for some reason #} - {% if (playlist.get('current_index', -10) - loop.index0)|abs is lt(5) %} - {{ common_elements.item(info, include_badges=false, lazy_load=false) }} - {% else %} - {{ common_elements.item(info, include_badges=false, lazy_load=true) }} - {% endif %} - {% endfor %} - </nav> - {% if playlist['current_index'] is not none %} - <script> - // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later - (function main() { - // from https://stackoverflow.com/a/6969486 - function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string - } - let playability_error = {{ 'true' if playability_error else 'false' }}; - let playlist_id = {{ playlist['id']|tojson }}; - playlist_id = escapeRegExp(playlist_id); - - // read cookies on whether to autoplay thru playlist - // pain in the ass: - // https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie - let cookieValue = document.cookie.replace(new RegExp( - '(?:(?:^|.*;\\s*)autoplay_' + playlist_id + '\\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 - checkbox.addEventListener( 'change', function() { - if(this.checked) { - autoplayEnabled = 1; - document.cookie = 'autoplay_' + playlist_id + '=1'; - } else { - autoplayEnabled = 0; - document.cookie = 'autoplay_' + playlist_id + '=0'; - } - }); - - const vid = document.getElementById('js-video-player'); - if(!playability_error){ - if(autoplayEnabled){ - vid.play(); - } - } - - let currentIndex = {{ playlist['current_index']|tojson }}; - {% if playlist['current_index']+1 == playlist['items']|length %} - let nextVideoUrl = null; - {% else %} - let nextVideoUrl = {{ (playlist['items'][playlist['current_index']+1]['url'])|tojson }}; - {% endif %} - let nextVideoDelay = 1000; - - // scroll playlist to proper position - let pl = document.querySelector('.playlist-videos'); - // item height + gap == 100 - pl.scrollTop = 100*currentIndex; - - // go to next video when video ends - // https://stackoverflow.com/a/2880950 - if(nextVideoUrl){ - if(playability_error){ - videoEnded(); - } else { - vid.addEventListener('ended', videoEnded, false); - } - function nextVideo(){ - if(autoplayEnabled){ - window.location.href = nextVideoUrl; - } - } - function videoEnded(e) { - window.setTimeout(nextVideo, nextVideoDelay); - } - } - }()); - // @license-end - </script> - {% endif %} - {% if playlist['id'] is not none %} - <script> - // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later - (function main() { + <div class="site-playlist"> + <div class="playlist-header"> + <a href="{{ playlist['url'] }}" title="{{ playlist['title'] }}"><h3>{{ playlist['title'] }}</h3></a> + <ul class="playlist-metadata"> + <li>Autoplay: <input type="checkbox" id="autoplay-toggle"></li> + {% if playlist['current_index'] is none %} + <li>[Error!]/{{ playlist['video_count'] }}</li> + {% else %} + <li>{{ playlist['current_index']+1 }}/{{ playlist['video_count'] }}</li> + {% endif %} + <li><a href="{{ playlist['author_url'] }}" title="{{ playlist['author'] }}">{{ playlist['author'] }}</a></li> + </ul> + </div> + <nav class="playlist-videos"> + {% for info in playlist['items'] %} + {# non-lazy load for 5 videos surrounding current video #} + {# for non-js browsers or old such that IntersectionObserver doesn't work #} + {# -10 is sentinel to not load anything if there's no current_index for some reason #} + {% if (playlist.get('current_index', -10) - loop.index0)|abs is lt(5) %} + {{ common_elements.item(info, include_badges=false, lazy_load=false) }} + {% else %} + {{ common_elements.item(info, include_badges=false, lazy_load=true) }} + {% endif %} + {% endfor %} + </nav> + {% if playlist['id'] is not none %} + <script> + // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later // 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 - let observer = new IntersectionObserver(lazyLoad, { + 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 @@ -287,15 +205,123 @@ }; // Tell our observer to observe all img elements with a "lazy" class - let lazyImages = document.querySelectorAll('img.lazy'); + var lazyImages = document.querySelectorAll('img.lazy'); lazyImages.forEach(img => { observer.observe(img); }); - }()); - // @license-end - </script> - {% endif %} - </div> + // @license-end + </script> + {% endif %} + </div> + {% elif settings.related_videos_mode != 0 %} + <li class="related-autoplay">Autoplay: <input type="checkbox" id="autoplay-toggle"></li> + {% endif %} + + {% if settings.related_videos_mode != 0 or playlist %} + <script> + // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later + var playability_error = {{ 'true' if playability_error else 'false' }}; + {% if playlist and playlist['current_index'] is not none %} + {% set isPlaylist = true %} + {% endif %} + + {% if isPlaylist %} + // from https://stackoverflow.com/a/6969486 + function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string + } + var playlist_id = {{ playlist['id']|tojson }}; + playlist_id = escapeRegExp(playlist_id); + {% endif %} + + // read cookies on whether to autoplay + // pain in the ass: + // https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie + {% if isPlaylist %} + var cookieValue = document.cookie.replace(new RegExp( + '(?:(?:^|.*;\\s*)autoplay_' + playlist_id + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1'); + {% else %} + var cookieValue = document.cookie.replace(new RegExp( + '(?:(?:^|.*;\\s*)autoplay\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1'); + {% endif %} + + var autoplayEnabled = 0; + if(cookieValue.length === 0){ + autoplayEnabled = 0; + } else { + autoplayEnabled = Number(cookieValue); + } + + // check the checkbox if autoplay is on + var checkbox = document.querySelector('#autoplay-toggle'); + if(autoplayEnabled){ + checkbox.checked = true; + } + + // listen for checkbox to turn autoplay on and off + var cookie = 'autoplay' + {% if isPlaylist %} + cookie += '_' + playlist_id; + {% endif %} + 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 + var vid = document.querySelector('video'); + if(autoplayEnabled){ + vid.play(); + } + } + + // determine next video url + {% if isPlaylist %} + var currentIndex = {{ playlist['current_index']|tojson }}; + {% if playlist['current_index']+1 == playlist['items']|length %} + var nextVideoUrl = null; + {% else %} + var nextVideoUrl = {{ (playlist['items'][playlist['current_index']+1]['url'])|tojson }}; + {% endif %} + + // scroll playlist to proper position + // item height + gap == 100 + var pl = document.querySelector('.playlist-videos'); + pl.scrollTop = 100*currentIndex; + {% else %} + {% if related|length == 0 %} + var nextVideoUrl = null; + {% else %} + var nextVideoUrl = {{ (related[0]['url'])|tojson }}; + {% endif %} + {% endif %} + var nextVideoDelay = 1000; + + // go to next video when video ends + // https://stackoverflow.com/a/2880950 + if (nextVideoUrl) { + if(playability_error){ + videoEnded(); + } else { + vid.addEventListener('ended', videoEnded, false); + } + function nextVideo(){ + if(autoplayEnabled){ + window.location.href = nextVideoUrl; + } + } + function videoEnded(e) { + window.setTimeout(nextVideo, nextVideoDelay); + } + } + // @license-end + </script> {% endif %} <!-- /playlist --> |