aboutsummaryrefslogtreecommitdiffstats
path: root/youtube
diff options
context:
space:
mode:
authorUmimaso <git@umimaso.com>2021-06-21 00:56:26 +0100
committerJesús <heckyel@hyperbola.info>2021-06-23 14:34:55 -0500
commit2d1794889aa3591b31a62f325a0c11a8553ba2d9 (patch)
tree8695134d0fcdf2b04e5a21d494640632bb33d9a6 /youtube
parentff7aae05c45ccbde4fd14e6e5e7e3cb4edf8c816 (diff)
downloadyt-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>
Diffstat (limited to 'youtube')
-rw-r--r--youtube/static/watch.css8
-rw-r--r--youtube/templates/watch.html262
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 -->