diff options
Diffstat (limited to 'youtube/templates')
-rw-r--r-- | youtube/templates/base.html | 46 | ||||
-rw-r--r-- | youtube/templates/channel.html | 33 | ||||
-rw-r--r-- | youtube/templates/comments.html | 2 | ||||
-rw-r--r-- | youtube/templates/comments_page.html | 2 | ||||
-rw-r--r-- | youtube/templates/common_elements.html | 29 | ||||
-rw-r--r-- | youtube/templates/embed.html | 57 | ||||
-rw-r--r-- | youtube/templates/error.html | 26 | ||||
-rw-r--r-- | youtube/templates/home.html | 2 | ||||
-rw-r--r-- | youtube/templates/licenses.html | 25 | ||||
-rw-r--r-- | youtube/templates/local_playlist.html | 28 | ||||
-rw-r--r-- | youtube/templates/playlist.html | 6 | ||||
-rw-r--r-- | youtube/templates/search.html | 6 | ||||
-rw-r--r-- | youtube/templates/settings.html | 6 | ||||
-rw-r--r-- | youtube/templates/shared.css | 5 | ||||
-rw-r--r-- | youtube/templates/subscription_manager.html | 25 | ||||
-rw-r--r-- | youtube/templates/subscriptions.html | 4 | ||||
-rw-r--r-- | youtube/templates/subscriptions.xml | 9 | ||||
-rw-r--r-- | youtube/templates/unsubscribe_verify.html | 8 | ||||
-rw-r--r-- | youtube/templates/watch.html | 242 |
19 files changed, 309 insertions, 252 deletions
diff --git a/youtube/templates/base.html b/youtube/templates/base.html index 7b32d76..393cc52 100644 --- a/youtube/templates/base.html +++ b/youtube/templates/base.html @@ -1,32 +1,46 @@ +{% if settings.app_public %} + {% set app_url = settings.app_url|string %} +{% else %} + {% set app_url = settings.app_url|string + ':' + settings.port_number|string %} +{% endif %} <!DOCTYPE html> <html lang="en"> <head> - <meta charset="UTF-8"/> - <meta name="viewport" content="width=device-width, initial-scale=1"/> - <meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval'; media-src 'self' https://*.googlevideo.com; {{ "img-src 'self' https://*.googleusercontent.com https://*.ggpht.com https://*.ytimg.com;" if not settings.proxy_images else "" }}"/> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' 'unsafe-eval'; media-src 'self' blob: {{ app_url }}/* data: https://*.googlevideo.com; {{ "img-src 'self' https://*.googleusercontent.com https://*.ggpht.com https://*.ytimg.com;" if not settings.proxy_images else "" }}"> <title>{{ page_title }}</title> - <link title="Youtube local" href="/youtube.com/opensearch.xml" rel="search" type="application/opensearchdescription+xml"/> - <link href="/youtube.com/static/favicon.ico" type="image/x-icon" rel="icon"/> - <link href="/youtube.com/static/normalize.css" rel="stylesheet"/> - <link href="{{ theme_path }}" rel="stylesheet"/> + <link title="YT Local" href="/youtube.com/opensearch.xml" rel="search" type="application/opensearchdescription+xml"> + <link href="/youtube.com/static/favicon.ico" type="image/x-icon" rel="icon"> + <link href="/youtube.com/static/normalize.css" rel="stylesheet"> + <link href="{{ theme_path }}" rel="stylesheet"> + <link href="/youtube.com/shared.css" rel="stylesheet"> {% block style %} {{ style }} {% endblock %} + + {% if js_data %} + <script> + // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later + data = {{ js_data|tojson }}; + // @license-end + </script> + {% endif %} </head> <body> <header class="header"> <nav class="home"> - <a href="/youtube.com" id="home-link">YouTube Local</a> + <a href="/youtube.com" id="home-link">YT Local</a> </nav> - <form class="form" id="site-search" action="/youtube.com/search"> - <input type="search" name="query" class="search-box" value="{{ search_box_value }}" - {{ "autofocus" if request.path == "/" else "" }} placeholder="Type to search..."> + <form class="form" id="site-search" action="/youtube.com/results"> + <input type="search" name="search_query" class="search-box" value="{{ search_box_value }}" + {{ "autofocus" if (request.path in ("/", "/results") or error_message) else "" }} required placeholder="Type to search..."> <button type="submit" value="Search" class="search-button">Search</button> <!-- options --> <div class="dropdown"> <!-- hidden box --> - <input id="options-toggle-cbox" class="opt-box" role="button" type="checkbox"> + <input id="options-toggle-cbox" class="opt-box" type="checkbox"> <!-- end hidden box --> <label class="dropdown-label" for="options-toggle-cbox">Options</label> <div class="dropdown-content"> @@ -119,7 +133,7 @@ {% if header_playlist_names is defined %} <form class="playlist" id="playlist-edit" action="/youtube.com/edit_playlist" method="post" target="_self"> - <input class="play-box" name="playlist_name" id="playlist-name-selection" list="playlist-options" type="search" placeholder="I added your playlist..."> + <input class="play-box" name="playlist_name" id="playlist-name-selection" list="playlist-options" type="search" placeholder="Add name of your playlist..."> <datalist class="play-hidden" id="playlist-options"> {% for playlist_name in header_playlist_names %} <option value="{{ playlist_name }}">{{ playlist_name }}</option> @@ -127,7 +141,7 @@ </datalist> <button class="play-add" type="submit" id="playlist-add-button" name="action" value="add">+List</button> <div class="play-clean"> - <button type="reset" id="item-selection-reset">Clear selection</button> + <button type="reset" id="item-selection-reset">Clear</button> </div> </form> <script src="/youtube.com/static/js/playlistadd.js"></script> @@ -150,8 +164,8 @@ </div> <div> <p>This site is Free/Libre Software</p> - {% if current_commit and current_version %} - <p>Current version: {{ current_version }}-{{ current_commit }} @ {{ current_branch }}</p> + {% if current_commit != None %} + <p>Current version: {{ current_commit }} @ {{ current_branch }}</p> {% else %} <p>Current version: {{ current_version }}</p> {% endif %} diff --git a/youtube/templates/channel.html b/youtube/templates/channel.html index 294f1df..c43f488 100644 --- a/youtube/templates/channel.html +++ b/youtube/templates/channel.html @@ -1,21 +1,21 @@ {% if current_tab == 'search' %} {% set page_title = search_box_value + ' - Page ' + page_number|string %} {% else %} - {% set page_title = channel_name + ' - Channel' %} + {% set page_title = channel_name|string + ' - Channel' %} {% endif %} {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - <link href="/youtube.com/static/message_box.css" rel="stylesheet"/> - <link href="/youtube.com/static/channel.css" rel="stylesheet"/> + <link href="/youtube.com/static/message_box.css" rel="stylesheet"> + <link href="/youtube.com/static/channel.css" rel="stylesheet"> {% endblock style %} {% block main %} <div class="author-container"> <div class="author"> - <img alt="{{ channel_name }}" src="{{ avatar }}"/> + <img alt="{{ channel_name }}" src="{{ avatar }}"> <h2>{{ channel_name }}</h2> </div> <div class="summary"> @@ -33,7 +33,7 @@ <hr/> <nav class="channel-tabs"> - {% for tab_name in ('Videos', 'Playlists', 'About') %} + {% for tab_name in ('Videos', 'Shorts', 'Streams', 'Playlists', 'About') %} {% if tab_name.lower() == current_tab %} <a class="tab page-button">{{ tab_name }}</a> {% else %} @@ -51,8 +51,11 @@ <ul> {% for (before_text, stat, after_text) in [ ('Joined ', date_joined, ''), - ('', view_count|commatize, ' views'), + ('', approx_view_count, ' views'), ('', approx_subscriber_count, ' subscribers'), + ('', approx_video_count, ' videos'), + ('Country: ', country, ''), + ('Canonical Url: ', canonical_url, ''), ] %} {% if stat %} <li>{{ before_text + stat|string + after_text }}</li> @@ -65,7 +68,11 @@ <hr> <ul> {% for text, url in links %} - <li><a href="{{ url }}">{{ text }}</a></li> + {% if url %} + <li><a href="{{ url }}">{{ text }}</a></li> + {% else %} + <li>{{ text }}</li> + {% endif %} {% endfor %} </ul> </div> @@ -73,8 +80,8 @@ <!-- new--> <div id="links-metadata"> - {% if current_tab == 'videos' %} - {% set sorts = [('1', 'views'), ('2', 'oldest'), ('3', 'newest')] %} + {% if current_tab in ('videos', 'shorts', 'streams') %} + {% set sorts = [('1', 'views'), ('2', 'oldest'), ('3', 'newest'), ('4', 'newest - no shorts'),] %} <div id="number-of-results">{{ number_of_videos }} videos</div> {% elif current_tab == 'playlists' %} {% set sorts = [('2', 'oldest'), ('3', 'newest'), ('4', 'last video added')] %} @@ -110,13 +117,9 @@ <hr/> <footer class="pagination-container"> - {% if current_tab == 'videos' and current_sort.__str__() == '2' %} - <nav class="next-previous-button-row"> - {{ common_elements.next_previous_ctoken_buttons(None, ctoken, channel_url + '/' + current_tab, parameters_dictionary) }} - </nav> - {% elif current_tab == 'videos' %} + {% if current_tab in ('videos', 'shorts', 'streams') %} <nav class="pagination-list"> - {{ common_elements.page_buttons(number_of_pages, channel_url + '/' + current_tab, parameters_dictionary, include_ends=(current_sort.__str__() == '3')) }} + {{ common_elements.page_buttons(number_of_pages, channel_url + '/' + current_tab, parameters_dictionary, include_ends=(current_sort.__str__() in '34')) }} </nav> {% elif current_tab == 'playlists' or current_tab == 'search' %} <nav class="next-previous-button-row"> diff --git a/youtube/templates/comments.html b/youtube/templates/comments.html index 7d0ef85..7bd75e5 100644 --- a/youtube/templates/comments.html +++ b/youtube/templates/comments.html @@ -21,7 +21,7 @@ <span class="comment-text">{{ common_elements.text_runs(comment['text']) }}</span> {% endif %} - <span class="comment-likes">{{ comment['likes_text'] if comment['like_count'] else ''}}</span> + <span class="comment-likes">{{ comment['likes_text'] if comment['approx_like_count'] else ''}}</span> <div class="button-row"> {% if comment['reply_count'] %} {% if settings.use_comments_js and comment['replies_url'] %} diff --git a/youtube/templates/comments_page.html b/youtube/templates/comments_page.html index 09230f5..3764b10 100644 --- a/youtube/templates/comments_page.html +++ b/youtube/templates/comments_page.html @@ -1,4 +1,4 @@ -{% set page_title = ('Replies' if comments_info['is_replies'] else 'Comments page ' + comments_info['page_number']) %} +{% set page_title = ('Replies' if comments_info['is_replies'] else 'Comments page ' + comments_info['page_number']|string) %} {% import "comments.html" as comments with context %} {% if not slim %} diff --git a/youtube/templates/common_elements.html b/youtube/templates/common_elements.html index 94554d4..bacc513 100644 --- a/youtube/templates/common_elements.html +++ b/youtube/templates/common_elements.html @@ -38,10 +38,21 @@ <h4 class="title"><a href="{{ info['url'] }}" title="{{ info['title'] }}">{{ info['title'] }}</a></h4> {% if include_author %} + {% set author_description = info['author'] %} + {% set AUTHOR_DESC_LENGTH = 35 %} + {% if author_description != None %} + {% if author_description|length >= AUTHOR_DESC_LENGTH %} + {% set author_description = author_description[:AUTHOR_DESC_LENGTH].split(' ')[:-1]|join(' ') %} + {% if not author_description[-1] in ['.', '?', ':', '!'] %} + {% set author_more = author_description + '…' %} + {% set author_description = author_more|replace('"','') %} + {% endif %} + {% endif %} + {% endif %} {% if info.get('author_url') %} - <address title="{{ info['author'] }}"><b><a href="{{ info['author_url'] }}">{{ info['author'] }}</a></b></address> + <address title="{{ info['author'] }}"><b><a href="{{ info['author_url'] }}">{{ author_description }}</a></b></address> {% else %} - <address title="{{ info['author'] }}"><b>{{ info['author'] }}</b></address> + <address title="{{ info['author'] }}"><b>{{ author_description }}</b></address> {% endif %} {% endif %} @@ -104,17 +115,18 @@ {% set parameters_dictionary = parameters_dictionary.to_dict() %} {% if current_page != 1 %} - {% set _ = parameters_dictionary.__setitem__('page', current_page - 1) %} - <a class="page-link previous-page" href="{{ url + '?' + parameters_dictionary|urlencode }}">Previous page</a> + {% set _ = parameters_dictionary.__setitem__('page', current_page - 1) %} + <a class="page-link previous-page" href="{{ url + '?' + parameters_dictionary|urlencode }}">Previous page</a> {% endif %} {% if not is_last_page %} - {% set _ = parameters_dictionary.__setitem__('page', current_page + 1) %} - <a class="page-link next-page" href="{{ url + '?' + parameters_dictionary|urlencode }}">Next page</a> + {% set _ = parameters_dictionary.__setitem__('page', current_page + 1) %} + <a class="page-link next-page" href="{{ url + '?' + parameters_dictionary|urlencode }}">Next page</a> {% endif %} +{% endmacro %} - {% macro next_previous_ctoken_buttons(prev_ctoken, next_ctoken, url, parameters_dictionary) %} - {% set parameters_dictionary = parameters_dictionary.to_dict() %} +{% macro next_previous_ctoken_buttons(prev_ctoken, next_ctoken, url, parameters_dictionary) %} + {% set parameters_dictionary = parameters_dictionary.to_dict() %} {% if prev_ctoken %} {% set _ = parameters_dictionary.__setitem__('ctoken', prev_ctoken) %} @@ -125,5 +137,4 @@ {% set _ = parameters_dictionary.__setitem__('ctoken', next_ctoken) %} <a class="page-link next-page" href="{{ url + '?' + parameters_dictionary|urlencode }}">Next page</a> {% endif %} - {% endmacro %} {% endmacro %} diff --git a/youtube/templates/embed.html b/youtube/templates/embed.html index 728791b..85d2d78 100644 --- a/youtube/templates/embed.html +++ b/youtube/templates/embed.html @@ -1,11 +1,16 @@ <!DOCTYPE html> -<html lang="es"> +<html lang="en"> <head> - <meta charset="UTF-8"/> - <meta name="viewport" content="width=device-width, initial-scale=1"/> - <meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline'; media-src 'self' https://*.googlevideo.com; {{ "img-src 'self' https://*.googleusercontent.com https://*.ggpht.com https://*.ytimg.com;" if not settings.proxy_images else "" }}"/> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline'; media-src 'self' https://*.googlevideo.com; {{ "img-src 'self' https://*.googleusercontent.com https://*.ggpht.com https://*.ytimg.com;" if not settings.proxy_images else "" }}"> <title>{{ title }}</title> - <link href="/youtube.com/static/favicon.ico" type="image/x-icon" rel="icon"/> + <link href="/youtube.com/static/favicon.ico" type="image/x-icon" rel="icon"> + {% if settings.use_video_player == 2 %} + <!-- plyr --> + <link href="/youtube.com/static/modules/plyr/plyr.css" rel="stylesheet"> + <!--/ plyr --> + {% endif %} <style> body { margin: 0rem; @@ -15,14 +20,26 @@ width: 100%; height: auto; } + /* Prevent this div from blocking right-click menu for video + e.g. Firefox playback speed options */ + .plyr__poster { + display: none !important; + } </style> + {% if js_data %} + <script> + // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later + data = {{ js_data|tojson }}; + // @license-end + </script> + {% endif %} </head> <body> - <video controls autofocus onmouseleave="{{ title }}" + <video id="js-video-player" controls autofocus onmouseleave="{{ title }}" oncontextmenu="{{ title }}" onmouseenter="{{ title }}" title="{{ title }}"> - {% for video_source in video_sources %} - <source src="{{ video_source['src'] }}" type="{{ video_source['type'] }}"> - {% endfor %} + {% if uni_sources %} + <source src="{{ uni_sources[uni_idx]['url'] }}" type="{{ uni_sources[uni_idx]['type'] }}" data-res="{{ uni_sources[uni_idx]['quality'] }}"> + {% endif %} {% for source in subtitle_sources %} {% if source['on'] %} <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}" default> @@ -31,5 +48,27 @@ {% endif %} {% endfor %} </video> + {% if js_data %} + <script> + // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later + data = {{ js_data|tojson }}; + // @license-end + </script> + {% endif %} + <script> + // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later + let storyboard_url = {{ storyboard_url | tojson }}; + // @license-end + </script> + {% if settings.use_video_player == 2 %} + <!-- plyr --> + <script src="/youtube.com/static/modules/plyr/plyr.min.js" + integrity="sha512-l6ZzdXpfMHRfifqaR79wbYCEWjLDMI9DnROvb+oLkKq6d7MGroGpMbI7HFpicvmAH/2aQO+vJhewq8rhysrImw==" + crossorigin="anonymous"></script> + <script src="/youtube.com/static/js/plyr-start.js"></script> + <!-- /plyr --> + {% elif settings.use_video_player == 1 %} + <script src="/youtube.com/static/js/hotkeys.js"></script> + {% endif %} </body> </html> diff --git a/youtube/templates/error.html b/youtube/templates/error.html index 7256aee..97f8ca9 100644 --- a/youtube/templates/error.html +++ b/youtube/templates/error.html @@ -1,22 +1,36 @@ -{% set page_title = 'Error' %} +{% if error_code %} + {% set page_title = 'Error: ' ~ error_code %} +{% else %} + {% set page_title = 'Error' %} +{% endif %} {% if not slim %} {% extends "base.html" %} {% endif %} -{% block style %} - <link href="/youtube.com/static/home.css" rel="stylesheet"> -{% endblock style %} +{% if traceback %} + {% block style %} + <link href="/youtube.com/static/home.css" rel="stylesheet"> + {% endblock style %} +{% endif %} {% block main %} {% if traceback %} <div class="code-error" id="error-box"> <h1>500 Uncaught exception:</h1> <div class="code-box"><code>{{ traceback }}</code></div> - <p>Please report this issue at <a href="https://libregit.org/heckyel/yt-local/issues" target="_blank" rel="noopener noreferrer">https://libregit.org/heckyel/yt-local/issues</a></p> + <p>Please report this issue at <a href="https://todo.sr.ht/~heckyel/yt-local" target="_blank" rel="noopener noreferrer">https://todo.sr.ht/~heckyel/yt-local</a></p> <p>Remember to include the traceback in your issue and redact any information in it you do not want to share</p> </div> {% else %} - <div id="error-message">{{ error_message }}</div> + <section id="error-message" class="comments-area"> + <div class="comments"> + <div class="comment-container"> + <div class="comment"> + <span class="comment-text">{{ error_message }}</span> + </div> + </div> + </div> + </section> {% endif %} {% endblock %} diff --git a/youtube/templates/home.html b/youtube/templates/home.html index ef1f029..0adac56 100644 --- a/youtube/templates/home.html +++ b/youtube/templates/home.html @@ -1,7 +1,7 @@ {% set page_title = title %} {% extends "base.html" %} {% block style %} - <link href="/youtube.com/static/home.css" rel="stylesheet"/> + <link href="/youtube.com/static/home.css" rel="stylesheet"> {% endblock style %} {% block main %} <ul> diff --git a/youtube/templates/licenses.html b/youtube/templates/licenses.html index dc883a8..dc73bfb 100644 --- a/youtube/templates/licenses.html +++ b/youtube/templates/licenses.html @@ -1,7 +1,7 @@ {% set page_title = title %} {% extends "base.html" %} {% block style %} - <link href="/youtube.com/static/license.css" rel="stylesheet"/> + <link href="/youtube.com/static/license.css" rel="stylesheet"> {% endblock style %} {% block main %} <table id="jslicense-labels1" class="table"> @@ -15,6 +15,11 @@ </thead> <tbody> <tr> + <td data-label="File"><a href="/youtube.com/static/js/av-merge.js">av-merge.js</a></td> + <td data-label="License"><a href="http://www.gnu.org/licenses/agpl-3.0.html">AGPL-3.0 or later</a></td> + <td data-label="Source"><a href="/youtube.com/static/js/av-merge.js">av-merge.js</a></td> + </tr> + <tr> <td data-label="File"><a href="/youtube.com/static/js/comments.js">comments.js</a></td> <td data-label="License"><a href="http://www.gnu.org/licenses/agpl-3.0.html">AGPL-3.0 or later</a></td> <td data-label="Source"><a href="/youtube.com/static/js/comments.js">comments.js</a></td> @@ -35,15 +40,25 @@ <td data-label="Source"><a href="/youtube.com/static/js/playlistadd.js">playlistadd.js</a></td> </tr> <tr> - <td data-label="File"><a href="/youtube.com/static/js/speedyplay.js">speedyplay.js</a></td> + <td data-label="File"><a href="/youtube.com/static/js/plyr-start.js">plyr-start.js</a></td> <td data-label="License"><a href="http://www.gnu.org/licenses/agpl-3.0.html">AGPL-3.0 or later</a></td> - <td data-label="Source"><a href="/youtube.com/static/js/speedyplay.js">speedyplay.js</a></td> + <td data-label="Source"><a href="/youtube.com/static/js/plyr-start.js">plyr-start.js</a></td> + </tr> + <tr> + <td data-label="File"><a href="/youtube.com/static/modules/plyr/plyr.min.js">plyr.min.js</a></td> + <td data-label="License"><a href="https://spdx.org/licenses/MIT.html">Expat</a></td> + <td data-label="Source"><a href="/youtube.com/static/modules/plyr/plyr.js">plyr.js</a></td> </tr> - <tr> + <tr> <td data-label="File"><a href="/youtube.com/static/js/transcript-table.js">transcript-table.js</a></td> <td data-label="License"><a href="http://www.gnu.org/licenses/agpl-3.0.html">AGPL-3.0 or later</a></td> <td data-label="Source"><a href="/youtube.com/static/js/transcript-table.js">transcript-table.js</a></td> - </tr> + </tr> + <tr> + <td data-label="File"><a href="/youtube.com/static/js/watch.js">watch.js</a></td> + <td data-label="License"><a href="http://www.gnu.org/licenses/agpl-3.0.html">AGPL-3.0 or later</a></td> + <td data-label="Source"><a href="/youtube.com/static/js/watch.js">watch.js</a></td> + </tr> </tbody> </table> {% endblock main %} diff --git a/youtube/templates/local_playlist.html b/youtube/templates/local_playlist.html index 765ac66..3286f67 100644 --- a/youtube/templates/local_playlist.html +++ b/youtube/templates/local_playlist.html @@ -2,13 +2,29 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - <link href="/youtube.com/static/message_box.css" rel="stylesheet"/> - <link href="/youtube.com/static/local_playlist.css" rel="stylesheet"/> + <link href="/youtube.com/static/message_box.css" rel="stylesheet"> + <link href="/youtube.com/static/local_playlist.css" rel="stylesheet"> {% endblock style %} {% block main %} <div class="playlist-metadata"> <h2 class="play-title">{{ playlist_name }}</h2> + + <div id="export-options"> + <form id="playlist-export" method="post"> + <select id="export-type" name="export_format"> + <option value="json">JSON</option> + <option value="ids">Video id list (txt)</option> + <option value="urls">Video url list (txt)</option> + </select> + <button type="submit" id="playlist-export-button" name="action" value="export">Export</button> + </form> + </div> + </div> + + <form id="playlist-remove" action="/youtube.com/edit_playlist" method="post" target="_self"></form> + <div class="playlist-metadata" id="video-remove-container"> + <button id="removePlayList" type="submit" name="action" value="remove_playlist" form="playlist-remove" formaction="">Remove playlist</button> <input type="hidden" name="playlist_page" value="{{ playlist_name }}" form="playlist-edit"> <button class="play-action" type="submit" id="playlist-remove-button" name="action" value="remove" form="playlist-edit" formaction="">Remove from playlist</button> </div> @@ -17,6 +33,14 @@ {{ common_elements.item(video_info) }} {% endfor %} </div> + <script> + // @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&dn=gpl-3.0.txt GPL-v3-or-Later + const deletePlayList = document.getElementById('removePlayList'); + deletePlayList.addEventListener('click', (event) => { + return confirm('You are about to permanently delete {{ playlist_name }}\n\nOnce a playlist is permanently deleted, it cannot be recovered.') + }); + // @license-end + </script> <footer class="pagination-container"> <nav class="pagination-list"> {{ common_elements.page_buttons(num_pages, '/https://www.youtube.com/playlists/' + playlist_name, parameters_dictionary) }} diff --git a/youtube/templates/playlist.html b/youtube/templates/playlist.html index c154207..994523e 100644 --- a/youtube/templates/playlist.html +++ b/youtube/templates/playlist.html @@ -2,15 +2,15 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - <link href="/youtube.com/static/message_box.css" rel="stylesheet"/> - <link href="/youtube.com/static/playlist.css" rel="stylesheet"/> + <link href="/youtube.com/static/message_box.css" rel="stylesheet"> + <link href="/youtube.com/static/playlist.css" rel="stylesheet"> {% endblock style %} {% block main %} <div class="playlist-metadata"> <div class="author"> - <img alt="{{ title }}" src="{{ thumbnail }}"/> + <img alt="{{ title }}" src="{{ thumbnail }}"> <h2>{{ title }}</h2> </div> <div class="summary"> diff --git a/youtube/templates/search.html b/youtube/templates/search.html index 4238d70..af87c90 100644 --- a/youtube/templates/search.html +++ b/youtube/templates/search.html @@ -3,8 +3,8 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - <link href="/youtube.com/static/message_box.css" rel="stylesheet"/> - <link href="/youtube.com/static/search.css" rel="stylesheet"/> + <link href="/youtube.com/static/message_box.css" rel="stylesheet"> + <link href="/youtube.com/static/search.css" rel="stylesheet"> {% endblock style %} {% block main %} @@ -28,7 +28,7 @@ <!-- /video item --> <footer class="pagination-container"> <nav class="pagination-list"> - {{ common_elements.page_buttons(estimated_pages, '/https://www.youtube.com/search', parameters_dictionary) }} + {{ common_elements.page_buttons(estimated_pages, '/https://www.youtube.com/results', parameters_dictionary) }} </nav> </footer> {% endblock main %} diff --git a/youtube/templates/settings.html b/youtube/templates/settings.html index 7a7ce9e..a4ebabf 100644 --- a/youtube/templates/settings.html +++ b/youtube/templates/settings.html @@ -1,7 +1,7 @@ {% set page_title = 'Settings' %} {% extends "base.html" %} {% block style %} - <link href="/youtube.com/static/settings.css" rel="stylesheet"/> + <link href="/youtube.com/static/settings.css" rel="stylesheet"> {% endblock style %} {% block main %} @@ -13,9 +13,9 @@ {% if not setting_info.get('hidden', false) %} <li class="setting-item"> {% if 'label' is in(setting_info) %} - <label for="{{ 'setting_' + setting_name }}">{{ setting_info['label'] }}</label> + <label for="{{ 'setting_' + setting_name }}" {% if 'comment' is in(setting_info) %}title="{{ setting_info['comment'] }}" {% endif %}>{{ setting_info['label'] }}</label> {% else %} - <label for="{{ 'setting_' + setting_name }}">{{ setting_name.replace('_', ' ')|capitalize }}</label> + <label for="{{ 'setting_' + setting_name }}" {% if 'comment' is in(setting_info) %}title="{{ setting_info['comment'] }}" {% endif %}>{{ setting_name.replace('_', ' ')|capitalize }}</label> {% endif %} {% if setting_info['type'].__name__ == 'bool' %} diff --git a/youtube/templates/shared.css b/youtube/templates/shared.css new file mode 100644 index 0000000..8f12651 --- /dev/null +++ b/youtube/templates/shared.css @@ -0,0 +1,5 @@ +html { + font-family: {{ font_family }}; + background: var(--background); + color: var(--text); +} diff --git a/youtube/templates/subscription_manager.html b/youtube/templates/subscription_manager.html index 92cd024..96082c3 100644 --- a/youtube/templates/subscription_manager.html +++ b/youtube/templates/subscription_manager.html @@ -1,7 +1,7 @@ {% set page_title = 'Subscription Manager' %} {% extends "base.html" %} {% block style %} - <link href="/youtube.com/static/subscription_manager.css" rel="stylesheet"/> + <link href="/youtube.com/static/subscription_manager.css" rel="stylesheet"> {% endblock style %} @@ -19,14 +19,25 @@ <div class="import-export"> <form class="subscriptions-import-form" enctype="multipart/form-data" action="/youtube.com/import_subscriptions" method="POST"> <h2>Import subscriptions</h2> - <input type="file" id="subscriptions-import" accept="application/json, application/xml, text/x-opml" name="subscriptions_file"> - <input type="submit" value="Import" class="import-submit-button"> + <div class="subscriptions-import-options"> + <input type="file" id="subscriptions-import" accept="application/json, application/xml, text/x-opml, text/csv" name="subscriptions_file" required> + <input type="submit" value="Import"> + </div> </form> - <!--<ul class="subscriptions-export-links"> - <li><a href="/youtube.com/subscriptions.opml">Export subscriptions (OPML)</a></li> - <li><a href="/youtube.com/subscriptions.xml">Export subscriptions (RSS)</a></li> - </ul>--> + <form class="subscriptions-export-form" action="/youtube.com/export_subscriptions" method="POST"> + <h2>Export subscriptions</h2> + <div class="subscriptions-export-options"> + <select id="export-type" name="export_format" title="Export format"> + <option value="json_newpipe">JSON (NewPipe)</option> + <option value="json_google_takeout">JSON (Old Google Takeout Format)</option> + <option value="opml">OPML (RSS, no tags)</option> + </select> + <label for="include-muted">Include muted</label> + <input id="include-muted" type="checkbox" name="include_muted" checked> + <input type="submit" value="Export"> + </div> + </form> </div> <hr> diff --git a/youtube/templates/subscriptions.html b/youtube/templates/subscriptions.html index b528e5c..2823e8d 100644 --- a/youtube/templates/subscriptions.html +++ b/youtube/templates/subscriptions.html @@ -7,8 +7,8 @@ {% import "common_elements.html" as common_elements %} {% block style %} - <link href="/youtube.com/static/message_box.css" rel="stylesheet"/> - <link href="/youtube.com/static/subscription.css" rel="stylesheet"/> + <link href="/youtube.com/static/message_box.css" rel="stylesheet"> + <link href="/youtube.com/static/subscription.css" rel="stylesheet"> {% endblock style %} {% block main %} diff --git a/youtube/templates/subscriptions.xml b/youtube/templates/subscriptions.xml new file mode 100644 index 0000000..5365da1 --- /dev/null +++ b/youtube/templates/subscriptions.xml @@ -0,0 +1,9 @@ +<opml version="1.1"> + <body> + <outline text="YouTube Subscriptions" title="YouTube Subscriptions"> + {% for sub in sub_list %} + <outline text="{{sub['channel_name']}}" title="{{sub['channel_name']}}" type="rss" xmlUrl="https://www.youtube.com/feeds/videos.xml?channel_id={{sub['channel_id']}}" /> + {%- endfor %} + </outline> + </body> +</opml> diff --git a/youtube/templates/unsubscribe_verify.html b/youtube/templates/unsubscribe_verify.html index 98581c0..e899783 100644 --- a/youtube/templates/unsubscribe_verify.html +++ b/youtube/templates/unsubscribe_verify.html @@ -1,17 +1,19 @@ {% set page_title = 'Unsubscribe?' %} {% extends "base.html" %} +{% block style %} + <link href="/youtube.com/static/unsubscribe.css" rel="stylesheet"/> +{% endblock style %} {% block main %} - <span>Are you sure you want to unsubscribe from these channels?</span> + <p>Are you sure you want to unsubscribe from these channels?</p> <form class="subscriptions-import-form" action="/youtube.com/subscription_manager" method="POST"> {% for channel_id, channel_name in unsubscribe_list %} <input type="hidden" name="channel_ids" value="{{ channel_id }}"> {% endfor %} - <input type="hidden" name="action" value="unsubscribe"> <input type="submit" value="Yes, unsubscribe"> </form> - <ul> + <ul class="list-channel"> {% for channel_id, channel_name in unsubscribe_list %} <li><a href="{{ '/https://www.youtube.com/channel/' + channel_id }}" title="{{ channel_name }}">{{ channel_name }}</a></li> {% endfor %} diff --git a/youtube/templates/watch.html b/youtube/templates/watch.html index 7184872..0991457 100644 --- a/youtube/templates/watch.html +++ b/youtube/templates/watch.html @@ -3,8 +3,14 @@ {% import "common_elements.html" as common_elements %} {% import "comments.html" as comments with context %} {% block style %} - <link href="/youtube.com/static/message_box.css" rel="stylesheet"/> - <link href="/youtube.com/static/watch.css" rel="stylesheet"/> + <link href="/youtube.com/static/message_box.css" rel="stylesheet"> + <link href="/youtube.com/static/watch.css" rel="stylesheet"> + {% if settings.use_video_player == 2 %} + <!-- plyr --> + <link href="/youtube.com/static/modules/plyr/plyr.css" rel="stylesheet"> + <link href="/youtube.com/static/modules/plyr/custom_plyr.css" rel="stylesheet"> + <!--/ plyr --> + {% endif %} {% endblock style %} {% block main %} @@ -17,7 +23,7 @@ {% endif %} </span> </div> - {% elif (video_sources.__len__() == 0 or live) and hls_formats.__len__() != 0 %} + {% elif (uni_sources.__len__() == 0 or live) and hls_formats.__len__() != 0 %} <div class="live-url-choices"> <span>Copy a url into your video player:</span> <ol> @@ -28,10 +34,10 @@ </div> {% else %} <figure class="sc-video"> - <video id="js-video-player" playsinline controls> - {% for video_source in video_sources %} - <source src="{{ video_source['src'] }}" type="{{ video_source['type'] }}"> - {% endfor %} + <video id="js-video-player" playsinline controls {{ 'autoplay' if settings.autoplay_videos }}> + {% if uni_sources %} + <source src="{{ uni_sources[uni_idx]['url'] }}" type="{{ uni_sources[uni_idx]['type'] }}" data-res="{{ uni_sources[uni_idx]['quality'] }}"> + {% endif %} {% for source in subtitle_sources %} {% if source['on'] %} @@ -42,12 +48,6 @@ {% endfor %} </video> </figure> - - {% if time_start != 0 %} - <script> - document.getElementById('js-video-player').currentTime = {{ time_start|tojson }}; - </script> - {% endif %} {% endif %} <div class="sc-info"> @@ -72,15 +72,26 @@ <address class="v-uploaded">Uploaded by <a href="{{ uploader_channel_url }}">{{ uploader }}</a></address> <span class="v-views">{{ view_count }} views</span> <time class="v-published" datetime="{{ time_published_utc }}">Published on {{ time_published }}</time> - <span class="v-likes-dislikes">{{ like_count }} likes {{ dislike_count }} dislikes</span> + <span class="v-likes-dislikes">{{ like_count }} likes</span> <div class="external-player-controls"> <input class="speed" id="speed-control" type="text" title="Video speed"> - <script src="/youtube.com/static/js/speedyplay.js"></script> + {% if settings.use_video_player != 2 %} + <select id="quality-select" autocomplete="off"> + {% for src in uni_sources %} + <option value='{"type": "uni", "index": {{ loop.index0 }}}' {{ 'selected' if loop.index0 == uni_idx and not using_pair_sources else '' }} >{{ src['quality_string'] }}</option> + {% endfor %} + {% for src_pair in pair_sources %} + <option value='{"type": "pair", "index": {{ loop.index0}}}' {{ 'selected' if loop.index0 == pair_idx and using_pair_sources else '' }} >{{ src_pair['quality_string'] }}</option> + {% endfor %} + </select> + {% endif %} </div> - <input class="v-checkbox" name="video_info_list" value="{{ video_info }}" form="playlist-edit" type="checkbox"> + <span class="v-direct-link"><a href="https://youtu.be/{{ video_id }}" rel="noopener noreferrer" target="_blank">Direct Link</a></span> + + {% if settings.use_video_download != 0 %} <details class="v-download"> <summary class="download-dropdown-label">Download</summary> <ul class="download-dropdown-content"> @@ -100,6 +111,9 @@ {% endfor %} </ul> </details> + {% else %} + <span class="v-download"></span> + {% endif %} <span class="v-description">{{ common_elements.text_runs(description)|escape|urlize|timestamps|safe }}</span> <div class="v-music-list"> @@ -115,7 +129,11 @@ {% for track in music_list %} <tr> {% for attribute in music_attributes %} - <td>{{ track.get(attribute.lower(), '') }}</td> + {% if attribute.lower() == 'title' and track['url'] is not none %} + <td><a href="{{ track['url'] }}">{{ track.get(attribute.lower(), '') }}</a></td> + {% else %} + <td>{{ track.get(attribute.lower(), '') }}</td> + {% endif %} {% endfor %} </tr> {% endfor %} @@ -143,154 +161,35 @@ <!-- 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 class="site-playlist"> + <div class="playlist-header"> + <a href="{{ playlist['url'] }}" title="{{ playlist['title'] }}"><h3>{{ playlist['title'] }}</h3></a> + <ul class="playlist-metadata"> + <li><label for="playlist-autoplay-toggle">Autoplay: </label><input id="playlist-autoplay-toggle" type="checkbox" class="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> </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() { - // 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, { - // 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 - let lazyImages = document.querySelectorAll('img.lazy'); - lazyImages.forEach(img => { - observer.observe(img); - }); - }()); - // @license-end - </script> - {% endif %} - </div> + {% elif settings.related_videos_mode != 0 %} + <div class="related-autoplay"><label for="related-autoplay-toggle">Autoplay: </label><input id="related-autoplay-toggle" type="checkbox" class="autoplay-toggle"></div> {% endif %} - <!-- /playlist --> {% if subtitle_sources %} <details id="transcript-details"> @@ -328,7 +227,7 @@ <div class="comments-area-outer comments-disabled">Comments disabled</div> {% else %} <details class="comments-area-outer" {{'open' if settings.comments_mode == 1 else ''}}> - <summary>{{ comment_count|commatize }} comment{{'s' if comment_count != 1 else ''}}</summary> + <summary>{{ comment_count|commatize }} comment{{'s' if comment_count != '1' else ''}}</summary> <div class="comments-area-inner comments-area"> {% if comments_info %} {{ comments.video_comments(comments_info) }} @@ -340,14 +239,25 @@ </div> + <script src="/youtube.com/static/js/av-merge.js"></script> + <script src="/youtube.com/static/js/watch.js"></script> <script> // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later - data = {{ js_data|tojson }}; + let storyboard_url = {{ storyboard_url | tojson }}; // @license-end </script> <script src="/youtube.com/static/js/common.js"></script> <script src="/youtube.com/static/js/transcript-table.js"></script> - {% if settings.use_video_hotkeys %} <script src="/youtube.com/static/js/hotkeys.js"></script> {% endif %} + {% if settings.use_video_player == 2 %} + <!-- plyr --> + <script src="/youtube.com/static/modules/plyr/plyr.min.js" + integrity="sha512-l6ZzdXpfMHRfifqaR79wbYCEWjLDMI9DnROvb+oLkKq6d7MGroGpMbI7HFpicvmAH/2aQO+vJhewq8rhysrImw==" + crossorigin="anonymous"></script> + <script src="/youtube.com/static/js/plyr-start.js"></script> + <!-- /plyr --> + {% elif settings.use_video_player == 1 %} + <script src="/youtube.com/static/js/hotkeys.js"></script> + {% endif %} {% if settings.use_comments_js %} <script src="/youtube.com/static/js/comments.js"></script> {% endif %} {% if settings.use_sponsorblock_js %} <script src="/youtube.com/static/js/sponsorblock.js"></script> {% endif %} {% endblock main %} |