diff options
Diffstat (limited to 'youtube/templates')
-rw-r--r-- | youtube/templates/base.html | 397 | ||||
-rw-r--r-- | youtube/templates/channel.html | 143 | ||||
-rw-r--r-- | youtube/templates/comments.html | 21 | ||||
-rw-r--r-- | youtube/templates/comments_page.html | 10 | ||||
-rw-r--r-- | youtube/templates/common_elements.html | 67 | ||||
-rw-r--r-- | youtube/templates/error.html | 7 | ||||
-rw-r--r-- | youtube/templates/home.html | 11 | ||||
-rw-r--r-- | youtube/templates/local_playlist.html | 36 | ||||
-rw-r--r-- | youtube/templates/local_playlists_list.html | 21 | ||||
-rw-r--r-- | youtube/templates/playlist.html | 76 | ||||
-rw-r--r-- | youtube/templates/search.html | 61 | ||||
-rw-r--r-- | youtube/templates/settings.html | 79 | ||||
-rw-r--r-- | youtube/templates/subscription_manager.html | 78 | ||||
-rw-r--r-- | youtube/templates/subscriptions.html | 81 | ||||
-rw-r--r-- | youtube/templates/watch.html | 905 |
15 files changed, 668 insertions, 1325 deletions
diff --git a/youtube/templates/base.html b/youtube/templates/base.html index 3d9f1e9..4d03c05 100644 --- a/youtube/templates/base.html +++ b/youtube/templates/base.html @@ -1,203 +1,238 @@ <!DOCTYPE html> <html> <head> - <meta charset="utf-8"> + <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>{{ page_title }}</title> - <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 "" }}"> - <link href="{{ theme_path }}" type="text/css" rel="stylesheet"> - <link href="/youtube.com/shared.css" type="text/css" rel="stylesheet"> - <link href="/youtube.com/static/comments.css" type="text/css" rel="stylesheet"> - <link href="/youtube.com/static/favicon.ico" type="image/x-icon" rel="icon"> - <link title="Youtube local" href="/youtube.com/opensearch.xml" rel="search" type="application/opensearchdescription+xml"> - <style type="text/css"> -{% block style %} -{{ style }} -{% endblock %} - </style> + <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"/> + {% block style %} + {{ style }} + {% endblock %} </head> - <body> - <header> - <a href="/youtube.com" id="home-link">Home</a> - <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 "" }}> - <button type="submit" value="Search" class="search-button">Search</button> - <div class="dropdown"> - <button class="dropdown-label">Options</button> - <div class="css-sucks"> - <div class="dropdown-content"> - <h3>Sort by</h3> - <input type="radio" id="sort_relevance" name="sort" value="0"> - <label for="sort_relevance">Relevance</label> - - <input type="radio" id="sort_upload_date" name="sort" value="2"> - <label for="sort_upload_date">Upload date</label> - - <input type="radio" id="sort_view_count" name="sort" value="3"> - <label for="sort_view_count">View count</label> - - <input type="radio" id="sort_rating" name="sort" value="1"> - <label for="sort_rating">Rating</label> - - - <h3>Upload date</h3> - <input type="radio" id="time_any" name="time" value="0"> - <label for="time_any">Any</label> - - <input type="radio" id="time_last_hour" name="time" value="1"> - <label for="time_last_hour">Last hour</label> - - <input type="radio" id="time_today" name="time" value="2"> - <label for="time_today">Today</label> - - <input type="radio" id="time_this_week" name="time" value="3"> - <label for="time_this_week">This week</label> - - <input type="radio" id="time_this_month" name="time" value="4"> - <label for="time_this_month">This month</label> - - <input type="radio" id="time_this_year" name="time" value="5"> - <label for="time_this_year">This year</label> - <h3>Type</h3> - <input type="radio" id="type_any" name="type" value="0"> - <label for="type_any">Any</label> - - <input type="radio" id="type_video" name="type" value="1"> - <label for="type_video">Video</label> - - <input type="radio" id="type_channel" name="type" value="2"> - <label for="type_channel">Channel</label> - - <input type="radio" id="type_playlist" name="type" value="3"> - <label for="type_playlist">Playlist</label> - - <input type="radio" id="type_movie" name="type" value="4"> - <label for="type_movie">Movie</label> - - <input type="radio" id="type_show" name="type" value="5"> - <label for="type_show">Show</label> - - - <h3>Duration</h3> - <input type="radio" id="duration_any" name="duration" value="0"> - <label for="duration_any">Any</label> + <body> + <header class="header"> + <nav class="home"> + <a href="/youtube.com" id="home-link">YouTube 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..."> + <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"> + <!-- end hidden box --> + <label class="dropdown-label" for="options-toggle-cbox">Options</label> + <div class="dropdown-content"> + <h3>Sort by</h3> + <div class="option"> + <input type="radio" id="sort_relevance" name="sort" value="0"> + <label for="sort_relevance">Relevance</label> + </div> + <div class="option"> + <input type="radio" id="sort_upload_date" name="sort" value="2"> + <label for="sort_upload_date">Upload date</label> + </div> + <div class="option"> + <input type="radio" id="sort_view_count" name="sort" value="3"> + <label for="sort_view_count">View count</label> + </div> + <div class="option"> + <input type="radio" id="sort_rating" name="sort" value="1"> + <label for="sort_rating">Rating</label> + </div> - <input type="radio" id="duration_short" name="duration" value="1"> - <label for="duration_short">Short (< 4 minutes)</label> + <h3>Upload date</h3> + <div class="option"> + <input type="radio" id="time_any" name="time" value="0"> + <label for="time_any">Any</label> + </div> + <div class="option"> + <input type="radio" id="time_last_hour" name="time" value="1"> + <label for="time_last_hour">Last hour</label> + </div> + <div class="option"> + <input type="radio" id="time_today" name="time" value="2"> + <label for="time_today">Today</label> + </div> + <div class="option"> + <input type="radio" id="time_this_week" name="time" value="3"> + <label for="time_this_week">This week</label> + </div> + <div class="option"> + <input type="radio" id="time_this_month" name="time" value="4"> + <label for="time_this_month">This month</label> + </div> + <div class="option"> + <input type="radio" id="time_this_year" name="time" value="5"> + <label for="time_this_year">This year</label> + </div> - <input type="radio" id="duration_long" name="duration" value="2"> - <label for="duration_long">Long (> 20 minutes)</label> + <h3>Type</h3> + <div class="option"> + <input type="radio" id="type_any" name="type" value="0"> + <label for="type_any">Any</label> + </div> + <div class="option"> + <input type="radio" id="type_video" name="type" value="1"> + <label for="type_video">Video</label> + </div> + <div class="option"> + <input type="radio" id="type_channel" name="type" value="2"> + <label for="type_channel">Channel</label> + </div> + <div class="option"> + <input type="radio" id="type_playlist" name="type" value="3"> + <label for="type_playlist">Playlist</label> + </div> + <div class="option"> + <input type="radio" id="type_movie" name="type" value="4"> + <label for="type_movie">Movie</label> + </div> + <div class="option"> + <input type="radio" id="type_show" name="type" value="5"> + <label for="type_show">Show</label> + </div> - </div> + <h3>Duration</h3> + <div class="option"> + <input type="radio" id="duration_any" name="duration" value="0"> + <label for="duration_any">Any</label> + </div> + <div class="option"> + <input type="radio" id="duration_short" name="duration" value="1"> + <label for="duration_short">Short (< 4 minutes)</label> + </div> + <div class="option"> + <input type="radio" id="duration_long" name="duration" value="2"> + <label for="duration_long">Long (> 20 minutes)</label> </div> </div> - </form> + </div> + </form> {% if header_playlist_names is defined %} - <form id="playlist-edit" action="/youtube.com/edit_playlist" method="post" target="_self"> - <input name="playlist_name" id="playlist-name-selection" list="playlist-options" type="text"> - <datalist id="playlist-options"> - {% for playlist_name in header_playlist_names %} - <option value="{{ playlist_name }}">{{ playlist_name }}</option> - {% endfor %} + <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..."> + <datalist class="play-hidden" id="playlist-options"> + {% for playlist_name in header_playlist_names %} + <option value="{{ playlist_name }}">{{ playlist_name }}</option> + {% endfor %} </datalist> - <button type="submit" id="playlist-add-button" name="action" value="add">Add to playlist</button> - <button type="reset" id="item-selection-reset">Clear selection</button> + <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> + </div> </form> + <script> - /* Takes control of the form if javascript is enabled, so that adding stuff to a playlist will not cause things to stop loading, and will display a status message. If javascript is disabled, the form will still work using regular HTML methods, but causes things on the page (such as the video) to stop loading. */ - var playlistAddForm = document.getElementById('playlist-edit'); - - function setStyle(element, property, value){ - element.style[property] = value; - } - function removeMessage(messageBox){ - messageBox.parentNode.removeChild(messageBox); - } - - function displayMessage(text, error=false){ - let currentMessageBox = document.getElementById('message-box'); - if(currentMessageBox !== null){ - currentMessageBox.parentNode.removeChild(currentMessageBox); - } - let messageBox = document.createElement('div'); - if(error){ - messageBox.setAttribute('role', 'alert'); - } else { - messageBox.setAttribute('role', 'status'); - } - messageBox.setAttribute('id', 'message-box'); - let textNode = document.createTextNode(text); - messageBox.appendChild(textNode); - document.querySelector('main').appendChild(messageBox); - let currentstyle = window.getComputedStyle(messageBox); - let removalDelay; - if(error){ - removalDelay = 5000; - } else { - removalDelay = 1500; - } - window.setTimeout(setStyle, 20, messageBox, 'opacity', 1); - window.setTimeout(setStyle, removalDelay, messageBox, 'opacity', 0); - window.setTimeout(removeMessage, removalDelay+300, messageBox); - } - // https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript - function sendData(event){ - var clicked_button = document.activeElement; - if(clicked_button === null || clicked_button.getAttribute('type') !== 'submit' || clicked_button.parentElement != event.target){ - console.log('ERROR: clicked_button not valid'); - return; - } - if(clicked_button.getAttribute('value') !== 'add'){ - return; // video(s) are being removed from playlist, just let it refresh the page - } - event.preventDefault(); - var XHR = new XMLHttpRequest(); - var FD = new FormData(playlistAddForm); - - if(FD.getAll('video_info_list').length === 0){ - displayMessage('Error: No videos selected', true); - return; - } - - if(FD.get('playlist_name') === ""){ - displayMessage('Error: No playlist selected', true); - return; - } - - // https://stackoverflow.com/questions/48322876/formdata-doesnt-include-value-of-buttons - FD.append('action', 'add'); - - XHR.addEventListener('load', function(event){ - if(event.target.status == 204){ - displayMessage('Added videos to playlist "' + FD.get('playlist_name') + '"'); - } else { - displayMessage('Error adding videos to playlist: ' + event.target.status.toString(), true); - } - }); - - XHR.addEventListener('error', function(event){ - if(event.target.status == 0){ - displayMessage('XHR failed: Check that XHR requests are allowed', true); - } else { - displayMessage('XHR failed: Unknown error', true); - } - }); - - XHR.open('POST', playlistAddForm.getAttribute('action')); - XHR.send(FD); - } - - playlistAddForm.addEventListener('submit', sendData); + (function main() { + /* Takes control of the form if javascript is enabled, so that adding stuff to a playlist will not cause things to stop loading, and will display a status message. If javascript is disabled, the form will still work using regular HTML methods, but causes things on the page (such as the video) to stop loading. */ + const playlistAddForm = document.getElementById('playlist-edit'); + + function setStyle(element, property, value){ + element.style[property] = value; + } + function removeMessage(messageBox){ + messageBox.parentNode.removeChild(messageBox); + } + + function displayMessage(text, error=false){ + let currentMessageBox = document.getElementById('message-box'); + if(currentMessageBox !== null){ + currentMessageBox.parentNode.removeChild(currentMessageBox); + } + let messageBox = document.createElement('div'); + if(error){ + messageBox.setAttribute('role', 'alert'); + } else { + messageBox.setAttribute('role', 'status'); + } + messageBox.setAttribute('id', 'message-box'); + let textNode = document.createTextNode(text); + messageBox.appendChild(textNode); + document.querySelector('main').appendChild(messageBox); + let currentstyle = window.getComputedStyle(messageBox); + let removalDelay; + if(error){ + removalDelay = 5000; + } else { + removalDelay = 1500; + } + window.setTimeout(setStyle, 20, messageBox, 'opacity', 1); + window.setTimeout(setStyle, removalDelay, messageBox, 'opacity', 0); + window.setTimeout(removeMessage, removalDelay+300, messageBox); + } + // https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript + function sendData(event){ + var clicked_button = document.activeElement; + if(clicked_button === null || clicked_button.getAttribute('type') !== 'submit' || clicked_button.parentElement != event.target){ + console.log('ERROR: clicked_button not valid'); + return; + } + if(clicked_button.getAttribute('value') !== 'add'){ + return; // video(s) are being removed from playlist, just let it refresh the page + } + event.preventDefault(); + var XHR = new XMLHttpRequest(); + var FD = new FormData(playlistAddForm); + + if(FD.getAll('video_info_list').length === 0){ + displayMessage('Error: No videos selected', true); + return; + } + + if(FD.get('playlist_name') === ""){ + displayMessage('Error: No playlist selected', true); + return; + } + + // https://stackoverflow.com/questions/48322876/formdata-doesnt-include-value-of-buttons + FD.append('action', 'add'); + + XHR.addEventListener('load', function(event){ + if(event.target.status == 204){ + displayMessage('Added videos to playlist "' + FD.get('playlist_name') + '"'); + } else { + displayMessage('Error adding videos to playlist: ' + event.target.status.toString(), true); + } + }); + + XHR.addEventListener('error', function(event){ + if(event.target.status == 0){ + displayMessage('XHR failed: Check that XHR requests are allowed', true); + } else { + displayMessage('XHR failed: Unknown error', true); + } + }); + + XHR.open('POST', playlistAddForm.getAttribute('action')); + XHR.send(FD); + } + + playlistAddForm.addEventListener('submit', sendData); + }()); </script> {% endif %} + </header> - <main> -{% block main %} -{{ main }} -{% endblock %} + <main class="main"> + + {% block main %} + {{ main }} + {% endblock %} + </main> + <footer class="footer"> + <p>This site is Free/Libre Software</p> + <p>Current version: 3304bab @ master</p> + </footer> </body> + </html> diff --git a/youtube/templates/channel.html b/youtube/templates/channel.html index dddbff1..25b0971 100644 --- a/youtube/templates/channel.html +++ b/youtube/templates/channel.html @@ -7,114 +7,30 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - main{ - display:grid; -{% if current_tab == 'about' %} - grid-template-rows: 0fr 0fr 1fr; - grid-template-columns: 0fr 1fr; -{% else %} - grid-template-rows: repeat(5, 0fr); - grid-template-columns: auto 1fr; -{% endif %} - } - main .avatar{ - grid-row:1; - grid-column:1; - height:200px; - width:200px; - } - main .summary{ - grid-row:1; - grid-column:2; - margin-left: 5px; - } - .summary subscribe-unsubscribe, .summary short-description{ - margin-top: 10px; - } - main .channel-tabs{ - grid-row:2; - grid-column: 1 / span 2; - - display:grid; - grid-auto-flow: column; - justify-content:start; - - background-color: var(--interface-color); - padding: 3px; - padding-left: 6px; - } - #links-metadata{ - display: grid; - grid-auto-flow: column; - grid-column-gap: 10px; - grid-column: 1/span 2; - justify-content: start; - padding-top: 8px; - padding-bottom: 8px; - padding-left: 6px; - margin-bottom: 10px; - } - #number-of-results{ - font-weight:bold; - } - .content{ - grid-row: 4; - grid-column: 1 / span 2; - } - .search-content{ - width: 800px; - margin-left: 10px; - } - .item-grid{ - padding-left: 20px; - } - .item-list{ - width:800px; - margin: auto; - } - .page-button-row{ - margin-left: auto; - margin-right: auto; - } - .next-previous-button-row{ - margin-left: auto; - margin-right: auto; - } - .tab{ - padding: 5px 75px; - } - .channel-info{ - grid-row: 3; - grid-column: 1 / span 3; - } - .channel-info ul{ - padding-left: 40px; - } - .channel-info h3{ - margin-left: 40px; - } - .channel-info .description{ - white-space: pre-wrap; - min-width: 0; - margin-left: 40px; - } - .medium-item img{ - max-width: 168px; - } + <link href="/youtube.com/static/channel.css" rel="stylesheet"> {% endblock style %} {% block main %} - <img class="avatar" src="{{ avatar }}"> - <div class="summary"> - <h2 class="title">{{ channel_name }}</h2> - <p class="short-description">{{ short_description }}</p> - <form method="POST" action="/youtube.com/subscriptions" class="subscribe-unsubscribe"> - <input type="submit" value="{{ 'Unsubscribe' if subscribed else 'Subscribe' }}"> - <input type="hidden" name="channel_id" value="{{ channel_id }}"> - <input type="hidden" name="channel_name" value="{{ channel_name }}"> - <input type="hidden" name="action" value="{{ 'unsubscribe' if subscribed else 'subscribe' }}"> - </form> + + <div class="author-container"> + <div class="author"> + <img alt="{{ channel_name }}" src="{{ avatar }}"/> + <h2>{{ channel_name }}</h2> + </div> + <div class="summary"> + <p>{{ short_description }}</p> + </div> + <div class="subscribe"> + <form method="POST" action="/youtube.com/subscriptions" class="subscribe-unsubscribe"> + <input class="btn-subscribe" type="submit" value="{{ 'Unsubscribe' if subscribed else 'Subscribe' }}"> + <input type="hidden" name="channel_id" value="{{ channel_id }}"> + <input type="hidden" name="channel_name" value="{{ channel_name }}"> + <input type="hidden" name="action" value="{{ 'unsubscribe' if subscribed else 'subscribe' }}"> + </form> + </div> </div> + <hr/> + <nav class="channel-tabs"> {% for tab_name in ('Videos', 'Playlists', 'About') %} {% if tab_name.lower() == current_tab %} @@ -153,8 +69,9 @@ </ul> </div> {% else %} - <div class="content {{ current_tab + '-content'}}"> - <div id="links-metadata"> + + <!-- new--> + <div id="links-metadata"> {% if current_tab == 'videos' %} {% set sorts = [('1', 'views'), ('2', 'oldest'), ('3', 'newest')] %} <div id="number-of-results">{{ number_of_videos }} videos</div> @@ -177,16 +94,18 @@ <a class="sort-button" href="{{ channel_url + '/' + current_tab + '?sort=' + sort_number }}">{{ 'Sort by ' + sort_name }}</a> {% endif %} {% endfor %} - </div> + </div> - <nav class="{{ 'item-list' if current_tab == 'search' else 'item-grid' }}"> + <div class="video-container {{ current_tab + '-content'}}"> {% for item_info in items %} {{ common_elements.item(item_info, include_author=false) }} {% endfor %} - </nav> + </div> + <hr/> + <footer class="pagination-container"> {% if current_tab == 'videos' %} - <nav class="page-button-row"> + <nav class="pagination-list"> {{ common_elements.page_buttons(number_of_pages, channel_url + '/' + current_tab, parameters_dictionary) }} </nav> {% elif current_tab == 'search' %} @@ -194,6 +113,8 @@ {{ common_elements.next_previous_buttons(is_last_page, channel_url + '/' + current_tab, parameters_dictionary) }} </nav> {% endif %} - </div> + </footer> + <!-- /new--> {% endif %} + {% endblock main %} diff --git a/youtube/templates/comments.html b/youtube/templates/comments.html index ad5ab02..a128b44 100644 --- a/youtube/templates/comments.html +++ b/youtube/templates/comments.html @@ -5,23 +5,24 @@ <div class="comment"> <a class="author-avatar" href="{{ comment['author_url'] }}" title="{{ comment['author'] }}"> {% if include_avatar %} - <img class="author-avatar-img" src="{{ comment['author_avatar'] }}"> + <img class="author-avatar-img" alt="{{ comment['author'] }}" src="{{ comment['author_avatar'] }}"> {% endif %} </a> - <address> + <address class="author-name"> <a class="author" href="{{ comment['author_url'] }}" title="{{ comment['author'] }}">{{ comment['author'] }}</a> </address> <a class="permalink" href="{{ comment['permalink'] }}" title="permalink"> - <time datetime="">{{ comment['time_published'] }}</time> + <time datetime="2012-01-14T15:23:44+02:00">{{ comment['time_published'] }}</time> </a> + {% if timestamp_links %} - <span class="text">{{ common_elements.text_runs(comment['text'])|timestamps|safe }}</span> + <span class="comment-text">{{ common_elements.text_runs(comment['text'])|timestamps|safe }}</span> {% else %} - <span class="text">{{ common_elements.text_runs(comment['text']) }}</span> + <span class="comment-text">{{ common_elements.text_runs(comment['text']) }}</span> {% endif %} - <span class="likes">{{ comment['likes_text'] if comment['like_count'] else ''}}</span> - <div class="bottom-row"> + <span class="comment-likes">{{ comment['likes_text'] if comment['like_count'] else ''}}</span> + <div class="button-row"> {% if settings.use_comments_js and comment['reply_count'] %} <details class="replies" src="{{ comment['replies_url'] }}"> <summary>{{ comment['view_replies_text'] }}</summary> @@ -33,7 +34,6 @@ {% endif %} </div> </div> - </div> {% endmacro %} @@ -57,8 +57,5 @@ <a class="page-button more-comments" href="{{ comments_info['more_comments_url'] }}">More comments</a> {% endif %} {% endif %} -{% endmacro %} - - - +{% endmacro %} diff --git a/youtube/templates/comments_page.html b/youtube/templates/comments_page.html index 2d914af..09230f5 100644 --- a/youtube/templates/comments_page.html +++ b/youtube/templates/comments_page.html @@ -3,16 +3,11 @@ {% if not slim %} {% extends "base.html" %} - {% block style %} - .comments-area{ - margin: auto; - width:640px; - } + <link href="/youtube.com/static/comments.css" rel="stylesheet"> {% endblock style %} {% endif %} - {% block main %} <section class="comments-area"> {% if not comments_info['is_replies'] %} @@ -27,7 +22,6 @@ </section> {% endif %} - {% if not comments_info['is_replies'] %} <div class="comment-links"> {% for link_text, link_url in comments_info['comment_links'] %} @@ -51,5 +45,3 @@ <script src="/youtube.com/static/js/comments.js"></script> {% endif %} {% endblock main %} - - diff --git a/youtube/templates/common_elements.html b/youtube/templates/common_elements.html index 0587ce3..fcd4125 100644 --- a/youtube/templates/common_elements.html +++ b/youtube/templates/common_elements.html @@ -15,60 +15,55 @@ {% endmacro %} {% macro item(info, description=false, horizontal=true, include_author=true, include_badges=true, lazy_load=false) %} - <div class="item-box {{ info['type'] + '-item-box' }} {{'horizontal-item-box' if horizontal else 'vertical-item-box'}} {{'has-description' if description else 'no-description'}}"> + <article class="item-box"> {% if info['error'] %} {{ info['error'] }} {% else %} - <div class="item {{ info['type'] + '-item' }}"> + <div class="item-video {{ info['type'] + '-item' }}"> <a class="thumbnail-box" href="{{ info['url'] }}" title="{{ info['title'] }}"> - {% if lazy_load %} - <img class="thumbnail-img lazy" data-src="{{ info['thumbnail'] }}"> - {% else %} - <img class="thumbnail-img" src="{{ info['thumbnail'] }}"> - {% endif %} - {% if info['type'] != 'channel' %} - <div class="thumbnail-info"> - <span>{{ (info['video_count']|commatize + ' videos') if info['type'] == 'playlist' else info['duration'] }}</span> - </div> - {% endif %} - </a> + <div class="thumbnail {% if info['type'] == 'channel' %} channel {% endif %}"> + {% if lazy_load %} + <img class="thumbnail-img lazy" alt="thumbnail" data-src="{{ info['thumbnail'] }}"> + {% elif info['type'] == 'channel' %} + <img class="thumbnail-img channel" alt="thumbnail" src="{{ info['thumbnail'] }}"> + {% else %} + <img class="thumbnail-img" alt="thumbnail" src="{{ info['thumbnail'] }}"> + {% endif %} - <div class="title"><a class="title" href="{{ info['url'] }}" title="{{ info['title'] }}">{{ info['title'] }}</a></div> + {% if info['type'] != 'channel' %} + <p class="length">{{ (info['video_count']|commatize + ' videos') if info['type'] == 'playlist' else info['duration'] }}</p> + {% endif %} + </div> + </a> + <h4 class="title"><a href="{{ info['url'] }}" title="{{ info['title'] }}">{{ info['title'] }}</a></h4> {% if include_author %} {% if info.get('author_url') %} - <address title="{{ info['author'] }}">By <a href="{{ info['author_url'] }}">{{ info['author'] }}</a></address> + <address title="{{ info['author'] }}"><b><a href="{{ info['author_url'] }}">{{ info['author'] }}</a></b></address> {% else %} <address title="{{ info['author'] }}"><b>{{ info['author'] }}</b></address> {% endif %} {% endif %} - <ul class="stats {{'horizontal-stats' if horizontal else 'vertical-stats'}}"> + + <div class="stats {{'horizontal-stats' if horizontal else 'vertical-stats'}}"> {% if info['type'] == 'channel' %} - <li><span>{{ info['approx_subscriber_count'] }} subscribers</span></li> - <li><span>{{ info['video_count']|commatize }} videos</span></li> + <div>{{ info['approx_subscriber_count'] }} subscribers</div> + <div>{{ info['video_count']|commatize }} videos</div> {% else %} - {% if info.get('approx_view_count') %} - <li><span class="views">{{ info['approx_view_count'] }} views</span></li> - {% endif %} {% if info.get('time_published') %} - <li><time>{{ info['time_published'] }}</time></li> + <time>{{ info['time_published'] }}</time> + {% endif %} + {% if info.get('approx_view_count') %} + <div class="views">{{ info['approx_view_count'] }} views</div> {% endif %} {% endif %} - </ul> - - {% if description %} - <span class="description">{{ text_runs(info.get('description', '')) }}</span> - {% endif %} - {% if include_badges %} - <span class="badges">{{ info['badges']|join(' | ') }}</span> - {% endif %} + </div> </div> {% if info['type'] == 'video' %} <input class="item-checkbox" type="checkbox" name="video_info_list" value="{{ info['video_info'] }}" form="playlist-edit"> {% endif %} {% endif %} - </div> - + </article> {% endmacro %} {% macro page_buttons(estimated_pages, url, parameters_dictionary) %} @@ -84,12 +79,12 @@ {% for page in range(page_start, page_end+1) %} {% if page == current_page %} - <div class="page-button">{{ page }}</div> + <div class="page-link is-current">{{ page }}</div> {% else %} {# IMPORTANT: Jinja SUCKS #} {# https://stackoverflow.com/questions/36886650/how-to-add-a-new-entry-into-a-dictionary-object-while-using-jinja2 #} {% set _ = parameters_dictionary.__setitem__('page', page) %} - <a class="page-button" href="{{ url + '?' + parameters_dictionary|urlencode }}">{{ page }}</a> + <a class="page-link" href="{{ url + '?' + parameters_dictionary|urlencode }}">{{ page }}</a> {% endif %} {% endfor %} @@ -101,11 +96,11 @@ {% if current_page != 1 %} {% set _ = parameters_dictionary.__setitem__('page', current_page - 1) %} - <a class="page-button previous-page" href="{{ url + '?' + parameters_dictionary|urlencode }}">Previous page</a> + <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-button next-page" href="{{ url + '?' + parameters_dictionary|urlencode }}">Next page</a> + <a class="page-link next-page" href="{{ url + '?' + parameters_dictionary|urlencode }}">Next page</a> {% endif %} {% endmacro %} diff --git a/youtube/templates/error.html b/youtube/templates/error.html index c3f58b0..e02ecd9 100644 --- a/youtube/templates/error.html +++ b/youtube/templates/error.html @@ -4,9 +4,13 @@ {% extends "base.html" %} {% endif %} +{% block style %} + <link href="/youtube.com/static/home.css" rel="stylesheet"> +{% endblock style %} + {% block main %} {% if traceback %} - <div id="error-box"> + <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://github.com/user234683/youtube-local/issues" target="_blank">https://github.com/user234683/youtube-local/issues</a></p> @@ -16,4 +20,3 @@ <div id="error-message">{{ error_message }}</div> {% endif %} {% endblock %} - diff --git a/youtube/templates/home.html b/youtube/templates/home.html index 9890f5e..0adac56 100644 --- a/youtube/templates/home.html +++ b/youtube/templates/home.html @@ -1,16 +1,7 @@ {% set page_title = title %} {% extends "base.html" %} {% block style %} - ul { - background-color: var(--interface-color); - padding: 20px; - width: 400px; - margin: auto; - margin-top: 20px; - } - li { - margin-bottom: 10px; - } + <link href="/youtube.com/static/home.css" rel="stylesheet"> {% endblock style %} {% block main %} <ul> diff --git a/youtube/templates/local_playlist.html b/youtube/templates/local_playlist.html index 803c4dc..bbe5edd 100644 --- a/youtube/templates/local_playlist.html +++ b/youtube/templates/local_playlist.html @@ -2,41 +2,23 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - main > *{ - width: 800px; - margin: auto; - } - - .playlist-metadata{ - display: flex; - flex-direction: row; - justify-content: space-between; - } - .playlist-title{ - } - #playlist-remove-button{ - align-self: center; - white-space: nowrap; - } - #results{ - display: grid; - grid-auto-rows: 0fr; - grid-row-gap: 10px; - } + <link href="/youtube.com/static/local_playlist.css" rel="stylesheet"> {% endblock style %} {% block main %} <div class="playlist-metadata"> - <h2 class="playlist-title">{{ playlist_name }}</h2> + <h2 class="play-title">{{ playlist_name }}</h2> <input type="hidden" name="playlist_page" value="{{ playlist_name }}" form="playlist-edit"> - <button type="submit" id="playlist-remove-button" name="action" value="remove" form="playlist-edit" formaction="">Remove from playlist</button> + <button class="play-action" type="submit" id="playlist-remove-button" name="action" value="remove" form="playlist-edit" formaction="">Remove from playlist</button> </div> - <div id="results"> + <div id="results" class="video-container"> {% for video_info in videos %} {{ common_elements.item(video_info) }} {% endfor %} </div> - <nav class="page-button-row"> - {{ common_elements.page_buttons(num_pages, '/https://www.youtube.com/playlists/' + playlist_name, parameters_dictionary) }} - </nav> + <footer class="pagination-container"> + <nav class="pagination-list"> + {{ common_elements.page_buttons(num_pages, '/https://www.youtube.com/playlists/' + playlist_name, parameters_dictionary) }} + </nav> + </footer> {% endblock main %} diff --git a/youtube/templates/local_playlists_list.html b/youtube/templates/local_playlists_list.html index a9fccff..f400808 100644 --- a/youtube/templates/local_playlists_list.html +++ b/youtube/templates/local_playlists_list.html @@ -2,20 +2,7 @@ {% extends "base.html" %} {% block style %} -main{ - display: flex; - justify-content: center; -} - ul{ - background-color: var(--interface-color); - margin-top: 20px; - padding: 20px; - min-width: 400px; - align-self: start; - } - li{ - margin-bottom: 10px; - } + <link href="/youtube.com/static/home.css" rel="stylesheet"> {% endblock style %} {% block main %} @@ -25,9 +12,3 @@ main{ {% endfor %} </ul> {% endblock main %} - - - - - - diff --git a/youtube/templates/playlist.html b/youtube/templates/playlist.html index af86c82..8c88cc7 100644 --- a/youtube/templates/playlist.html +++ b/youtube/templates/playlist.html @@ -2,77 +2,39 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - main > * { - width: 800px; - margin:auto; - } - - .playlist-metadata{ - display:grid; - grid-template-columns: 0fr 1fr; - } - .playlist-thumbnail{ - grid-row: 1 / span 5; - grid-column:1; - justify-self:start; - width:250px; - margin-right: 10px; - } - .playlist-title{ - grid-row: 1; - grid-column:2; - } - .playlist-author{ - grid-row:2; - grid-column:2; - } - .playlist-stats{ - grid-row:3; - grid-column:2; - } - - .playlist-description{ - grid-row:4; - grid-column:2; - min-width:0px; - white-space: pre-line; - } - - #results{ - margin-top:10px; - - display: grid; - grid-auto-rows: 0fr; - grid-row-gap: 10px; - - } + <link href="/youtube.com/static/playlist.css" rel="stylesheet"> {% endblock style %} {% block main %} + <div class="playlist-metadata"> - <img class="playlist-thumbnail" src="{{ thumbnail }}"> - <h2 class="playlist-title">{{ title }}</h2> - <a class="playlist-author" href="{{ author_url }}">{{ author }}</a> + <div class="author"> + <img alt="{{ title }}" src="{{ thumbnail }}"/> + <h2>{{ title }}</h2> + </div> + <div class="summary"> + <a class="playlist-author" href="{{ author_url }}">{{ author }}</a> + </div> <div class="playlist-stats"> <div>{{ video_count|commatize }} videos</div> <div>{{ view_count|commatize }} views</div> <div>Last updated {{ time_published }}</div> </div> - <div class="playlist-description">{{ common_elements.text_runs(description) }}</div> </div> + <hr/> - <div id="results"> + + <div id="results" class="video-container"> {% for info in video_list %} {{ common_elements.item(info) }} {% endfor %} </div> - <nav class="page-button-row"> - {{ common_elements.page_buttons(num_pages, '/https://www.youtube.com/playlist', parameters_dictionary) }} - </nav> -{% endblock main %} - - - - + <hr/> + <footer class="pagination-container"> + <nav class="pagination-list"> + {{ common_elements.page_buttons(num_pages, '/https://www.youtube.com/playlist', parameters_dictionary) }} + </nav> + </footer> +{% endblock main %} diff --git a/youtube/templates/search.html b/youtube/templates/search.html index 8b803e7..e742099 100644 --- a/youtube/templates/search.html +++ b/youtube/templates/search.html @@ -3,44 +3,31 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - main > * { - max-width: 800px; - margin: auto; - } - #result-info{ - margin-top: 10px; - margin-bottom: 10px; - padding-left: 10px; - padding-right: 10px; - } - #number-of-results{ - font-weight:bold; - } - .item-list{ - padding-left: 10px; - padding-right: 10px; - } - .badge{ - background-color:#cccccc; - } + <link href="/youtube.com/static/search.css" rel="stylesheet"> {% endblock style %} {% block main %} - <div id="result-info"> - <div id="number-of-results">Approximately {{ '{:,}'.format(estimated_results) }} results ({{ '{:,}'.format(estimated_pages) }} pages)</div> -{% if corrections['type'] == 'showing_results_for' %} - <div>Showing results for <a>{{ common_elements.text_runs(corrections['corrected_query_text']) }}</a></div> - <div>Search instead for <a href="{{ corrections['original_query_url'] }}">{{ corrections['original_query_text'] }}</a></div> -{% elif corrections['type'] == 'did_you_mean' %} - <div>Did you mean <a href="{{ corrections['corrected_query_url'] }}">{{ common_elements.text_runs(corrections['corrected_query_text']) }}</a></div> -{% endif %} - </div> - <div class="item-list"> - {% for info in results %} - {{ common_elements.item(info, description=true) }} - {% endfor %} - </div> - <nav class="page-button-row"> - {{ common_elements.page_buttons(estimated_pages, '/https://www.youtube.com/search', parameters_dictionary) }} - </nav> + <div class="result-info" id="result-info"> + <div id="number-of-results">Approximately {{ '{:,}'.format(estimated_results) }} results ({{ '{:,}'.format(estimated_pages) }} pages)</div> + {% if corrections['type'] == 'showing_results_for' %} + <div>Showing results for <a>{{ common_elements.text_runs(corrections['corrected_query_text']) }}</a></div> + <div>Search instead for <a href="{{ corrections['original_query_url'] }}">{{ corrections['original_query_text'] }}</a></div> + {% elif corrections['type'] == 'did_you_mean' %} + <div>Did you mean <a href="{{ corrections['corrected_query_url'] }}">{{ common_elements.text_runs(corrections['corrected_query_text']) }}</a></div> + {% endif %} + </div> + + <!-- video item --> + <div class="video-container"> + {% for info in results %} + {{ common_elements.item(info, description=true) }} + {% endfor %} + </div> + <hr/> + <!-- /video item --> + <footer class="pagination-container"> + <nav class="pagination-list"> + {{ common_elements.page_buttons(estimated_pages, '/https://www.youtube.com/search', parameters_dictionary) }} + </nav> + </footer> {% endblock main %} diff --git a/youtube/templates/settings.html b/youtube/templates/settings.html index 19817dd..5385c68 100644 --- a/youtube/templates/settings.html +++ b/youtube/templates/settings.html @@ -1,28 +1,7 @@ {% set page_title = 'Settings' %} {% extends "base.html" %} -{% import "common_elements.html" as common_elements %} {% block style %} - .settings-form { - margin: auto; - width: 600px; - margin-top:10px; - padding: 10px; - display: block; - background-color: var(--interface-color); - } - .settings-list{ - list-style: none; - padding: 0px; - } - .setting-item{ - margin-bottom: 10px; - padding: 5px; - } - .setting-item label{ - display: inline-block; - width: 250px; - } - + <link href="/youtube.com/static/settings.css" rel="stylesheet"> {% endblock style %} {% block main %} @@ -30,37 +9,37 @@ {% for categ in categories %} <h2>{{ categ|capitalize }}</h2> <ul class="settings-list"> - {% for setting_name, setting_info, value in settings_by_category[categ] %} - {% 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> - {% else %} - <label for="{{ 'setting_' + setting_name }}">{{ setting_name.replace('_', ' ')|capitalize }}</label> - {% endif %} - - {% if setting_info['type'].__name__ == 'bool' %} - <input type="checkbox" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" {{ 'checked' if value else '' }}> - {% elif setting_info['type'].__name__ == 'int' %} - {% if 'options' is in(setting_info) %} - <select id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}"> - {% for option in setting_info['options'] %} - <option value="{{ option[0] }}" {{ 'selected' if option[0] == value else '' }}>{{ option[1] }}</option> - {% endfor %} - </select> + {% for setting_name, setting_info, value in settings_by_category[categ] %} + {% 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> {% else %} - <input type="number" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" value="{{ value }}" step="1"> + <label for="{{ 'setting_' + setting_name }}">{{ setting_name.replace('_', ' ')|capitalize }}</label> {% endif %} - {% elif setting_info['type'].__name__ == 'float' %} - {% elif setting_info['type'].__name__ == 'str' %} - <input type="text" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" value="{{ value }}"> - {% else %} - <span>Error: Unknown setting type: setting_info['type'].__name__</span> - {% endif %} - </li> - {% endif %} - {% endfor %} + {% if setting_info['type'].__name__ == 'bool' %} + <input type="checkbox" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" {{ 'checked' if value else '' }}> + {% elif setting_info['type'].__name__ == 'int' %} + {% if 'options' is in(setting_info) %} + <select id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}"> + {% for option in setting_info['options'] %} + <option value="{{ option[0] }}" {{ 'selected' if option[0] == value else '' }}>{{ option[1] }}</option> + {% endfor %} + </select> + {% else %} + <input type="number" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" value="{{ value }}" step="1"> + {% endif %} + {% elif setting_info['type'].__name__ == 'float' %} + + {% elif setting_info['type'].__name__ == 'str' %} + <input type="text" id="{{ 'setting_' + setting_name }}" name="{{ setting_name }}" value="{{ value }}"> + {% else %} + <span>Error: Unknown setting type: setting_info['type'].__name__</span> + {% endif %} + </li> + {% endif %} + {% endfor %} </ul> {% endfor %} <input type="submit" value="Save settings"> diff --git a/youtube/templates/subscription_manager.html b/youtube/templates/subscription_manager.html index 4d798e0..3b8a5c0 100644 --- a/youtube/templates/subscription_manager.html +++ b/youtube/templates/subscription_manager.html @@ -1,81 +1,7 @@ {% set page_title = 'Subscription Manager' %} {% extends "base.html" %} {% block style %} - .import-export{ - display: flex; - flex-direction: row; - padding-left: 10px; - padding-top: 10px; - } - .subscriptions-import-form{ - background-color: var(--interface-color); - display: flex; - flex-direction: column; - align-items: flex-start; - max-width: 300px; - padding:10px; - } - .subscriptions-import-form h2{ - font-size: 1.25rem; - margin-bottom: 10px; - } - - .import-submit-button{ - margin-top:15px; - align-self: flex-end; - } - - - .subscriptions-export-links{ - margin: 0px 0px 0px 20px; - background-color: var(--interface-color); - list-style: none; - max-width: 300px; - padding:10px; - } - - .sub-list-controls{ - background-color: var(--interface-color); - padding:10px; - } - - - .tag-group-list{ - list-style: none; - margin-left: 10px; - margin-right: 10px; - padding: 0px; - } - .tag-group{ - border-style: solid; - margin-bottom: 10px; - } - - .sub-list{ - list-style: none; - padding:10px; - column-width: 300px; - column-gap: 40px; - } - .sub-list-item{ - display:flex; - margin-bottom: 10px; - break-inside:avoid; - } - .sub-list-item:not(.muted){ - background-color: var(--interface-color); - } - .tag-list{ - margin-left:15px; - font-weight:bold; - } - .sub-list-item-name{ - margin-left:15px; - } - .sub-list-checkbox{ - height: 1.5em; - min-width: 1.5em; // need min-width otherwise browser doesn't respect the width and squishes the checkbox down when there's too many tags - } + <link href="/youtube.com/static/subscription_manager.css" rel="stylesheet"> {% endblock style %} @@ -89,8 +15,6 @@ {% endfor %} {% endmacro %} - - {% block main %} <div class="import-export"> <form class="subscriptions-import-form" enctype="multipart/form-data" action="/youtube.com/import_subscriptions" method="POST"> diff --git a/youtube/templates/subscriptions.html b/youtube/templates/subscriptions.html index e2fb25c..9e48c6b 100644 --- a/youtube/templates/subscriptions.html +++ b/youtube/templates/subscriptions.html @@ -7,79 +7,21 @@ {% import "common_elements.html" as common_elements %} {% block style %} - main{ - display:flex; - flex-direction: row; - } - .video-section{ - flex-grow: 1; - padding-left: 10px; - padding-top: 10px; - } - .current-tag{ - margin-bottom:10px; - } - .video-section .page-button-row{ - justify-content: center; - } - .subscriptions-sidebar{ - flex-basis: 300px; - background-color: var(--interface-color); - border-left: 2px; - } - .sidebar-links{ - display:flex; - justify-content: space-between; - padding-left:10px; - padding-right: 10px; - } - - .sidebar-list{ - list-style: none; - padding-left:10px; - padding-right: 10px; - } - .sidebar-list-item{ - display:flex; - justify-content: space-between; - margin-bottom: 5px; - } - .sub-refresh-list .sidebar-item-name{ - text-overflow: clip; - white-space: nowrap; - overflow: hidden; - max-width: 200px; - } + <link href="/youtube.com/static/subscription.css" rel="stylesheet"> {% endblock style %} {% block main %} - <div class="video-section"> - {% if current_tag %} - <h2 class="current-tag">{{ current_tag }}</h2> - {% endif %} - <nav class="item-grid"> - {% for video_info in videos %} - {{ common_elements.item(video_info) }} - {% endfor %} - </nav> - - <nav class="page-button-row"> - {{ common_elements.page_buttons(num_pages, '/youtube.com/subscriptions', parameters_dictionary) }} - </nav> - </div> <div class="subscriptions-sidebar"> <div class="sidebar-links"> - <a href="/youtube.com/subscription_manager" class="sub-manager-link">Subscription Manager</a> - <form method="POST" class="refresh-all"> + <a class="sidebar-title" href="/youtube.com/subscription_manager" class="sub-manager-link">Subscription Manager</a> + <form class="sidebar-action" method="POST" class="refresh-all"> <input type="submit" value="Check All"> <input type="hidden" name="action" value="refresh"> <input type="hidden" name="type" value="all"> </form> </div> - <hr> - <ol class="sidebar-list tags"> {% if current_tag %} <li class="sidebar-list-item"> @@ -105,7 +47,6 @@ </ol> <hr> - <ol class="sidebar-list sub-refresh-list"> {% for subscription in subscription_list %} <li class="sidebar-list-item {{ 'muted' if subscription['muted'] else '' }}"> @@ -119,7 +60,23 @@ </li> {% endfor %} </ol> + </div> + + {% if current_tag %} + <h2 class="current-tag">{{ current_tag }}</h2> + {% endif %} + <div class="video-container"> + {% for video_info in videos %} + {{ common_elements.item(video_info) }} + {% endfor %} </div> + <hr/> + + <footer class="pagination-container"> + <nav class="pagination-list"> + {{ common_elements.page_buttons(num_pages, '/youtube.com/subscriptions', parameters_dictionary) }} + </nav> + </footer> {% endblock main %} diff --git a/youtube/templates/watch.html b/youtube/templates/watch.html index 36b2198..44fc5a7 100644 --- a/youtube/templates/watch.html +++ b/youtube/templates/watch.html @@ -3,378 +3,17 @@ {% import "common_elements.html" as common_elements %} {% import "comments.html" as comments with context %} {% block style %} - details > summary{ - background-color: var(--interface-color); - border-style: outset; - border-width: 2px; - font-weight: bold; - padding-bottom: 2px; - } - details > summary:hover{ - text-decoration: underline; - } - - .playability-error{ - height: 360px; - width: 640px; - grid-column: 2; - background-color: var(--video-background-color); - text-align:center; - } - .playability-error span{ - position: relative; - top: 50%; - transform: translate(-50%, -50%); - } - - .live-url-choices{ - height: 360px; - width: 640px; - grid-column: 2; - background-color: var(--video-background-color); - padding: 25px 0px 0px 25px; - } - .live-url-choices ol{ - list-style: none; - padding:0px; - margin:0px; - margin-top: 15px; - } - .live-url-choices input{ - width: 400px; - } - .url-choice-label{ - display: inline-block; - width: 150px; - } - - {% if settings.theater_mode %} - /* This is the constant aspect ratio trick - Percentages in padding-top declarations are based on the width of the - parent element. We can use this trick to achieve a constant aspect ratio - for video-container-inner by setting height to 0. - - So the video height will decrease if the browser window is narrow, - but it will keep same aspect ratio. Must use absolute positioning on - video to keep it inside its container since the container's height is 0. - - However, because we widen the video player longer than the video's - intrinsic width for long video to make scrubbing easier, we can't use - the aspect ratio to set the height. The height needs to be the - intrinsic height in these cases. So we use a media query so aspect - ratio trick is only used if page width is less than or equal to - intrinsic video width. - */ - #video-container{ - grid-column: 1 / span 5; - justify-self: center; - max-width: 100%; - width: {{ theater_video_target_width }}px; - margin-bottom: 10px; - } - #video-container-inner{ - height: {{ video_height}}px; - position: relative; - } - @media(max-width:{{ video_width }}px){ - #video-container-inner{ - padding-top: calc(100%*{{video_height}}/{{video_width}}); - height: 0px; - } - } - video{ - background-color: var(--video-background-color); - position: absolute; - top: 0px; - left: 0px; - width: 100%; - height: 100%; - } - .side-videos{ - grid-row: 2 /span 3; - width: 400px; - } - .video-info{ - width: 640px; - } - {% else %} - #video-container{ - grid-column: 2; - } - #video-container, #video-container-inner, video{ - height: 360px; - width: 640px; - } - .side-videos{ - grid-row: 1 /span 4; - } - {% endif %} - - main{ - display:grid; - grid-template-columns: 1fr 640px 40px 400px 1fr; - grid-template-rows: auto auto auto auto; - align-content: start; - } - - .video-info{ - grid-column: 2; - grid-row: 2; - display: grid; - grid-template-rows: 0fr 0fr 0fr 20px 0fr 0fr; - grid-template-columns: 1fr 1fr; - align-content: start; - } - .video-info > .title{ - grid-column: 1 / span 2; - min-width: 0; - } - .video-info > .labels{ - justify-self:start; - list-style: none; - padding: 0px; - margin: 5px 0px; - } - .video-info > .labels:empty{ - margin: 0px; - } - .labels > li{ - display: inline; - margin-right:5px; - background-color: var(--interface-color); - padding: 2px 5px; - border-style: solid; - border-width: 1px; - } - .video-info > address{ - grid-column: 1; - grid-row: 3; - justify-self: start; - } - .video-info > .views{ - grid-column: 2; - grid-row: 3; - justify-self:end; - } - .video-info > time{ - grid-column: 1; - grid-row: 4; - justify-self:start; - } - .video-info > .likes-dislikes{ - grid-column: 2; - grid-row: 4; - justify-self:end; - } - .video-info > .external-player-controls{ - justify-self: start; - grid-row: 5; - grid-column: 1; - margin-bottom: 8px; - } - #speed-control{ - width: 65px; - text-align: center; - background-color: var(--interface-color); - color: var(--text-color); - } - .video-info > .checkbox{ - justify-self:end; - align-self: start; - - grid-row: 5; - grid-column: 2; - } - .video-info > .download-dropdown{ - grid-column:1 / span 2; - grid-row: 6; - } - .video-info > .description{ - background-color:var(--interface-color); - margin-top:8px; - white-space: pre-wrap; - min-width: 0; - word-wrap: break-word; - grid-column: 1 / span 2; - grid-row: 7; - padding: 5px; - } - - .music-list{ - grid-row:8; - grid-column: 1 / span 2; - background-color: var(--interface-color); - padding-bottom: 7px; - } - .music-list table,th,td{ - border: 1px solid; - } - .music-list th,td{ - padding-left:4px; - padding-right:5px; - } - .music-list caption{ - text-align:left; - font-weight:bold; - margin-bottom:5px; - } - .more-info{ - grid-row: 9; - grid-column: 1 / span 2; - background-color: var(--interface-color); - } - .more-info > summary{ - font-weight: normal; - border-width: 1px 0px; - border-style: solid; - } - .more-info-content{ - padding: 5px; - } - .more-info-content p{ - margin: 8px 0px; - } - .comments-area-outer{ - grid-column: 2; - grid-row: 3; - margin-top:10px; - } - .comments-disabled{ - background-color: var(--interface-color); - padding: 5px; - font-weight: bold; - } - .comments-area-inner{ - padding-top: 10px; - } - .comment{ - width:640px; - } - - .side-videos{ - grid-column: 4; - max-width: 640px; - } - #transcript-details{ - margin-bottom: 10px; - } - table#transcript-table { - border-collapse: collapse; - width: 100%; - } - table#transcript-table td, th { - border: 1px solid #dddddd; - } - div#transcript-div { - background-color: var(--interface-color); - padding: 5px; - } - .playlist{ - border-style: solid; - border-width: 2px; - border-color: lightgray; - margin-bottom: 10px; - } - .playlist-header{ - background-color: var(--interface-color); - padding: 3px; - border-bottom-style: solid; - border-bottom-width: 2px; - border-bottom-color: lightgray; - } - .playlist-header h3{ - margin: 2px; - } - .playlist-metadata{ - list-style: none; - padding: 0px; - margin: 0px; - } - .playlist-metadata li{ - display: inline; - margin: 2px; - } - .playlist-videos{ - height: 300px; - overflow-y: scroll; - display: grid; - grid-auto-rows: 90px; - grid-row-gap: 10px; - padding-top: 10px; - } - .related-videos-inner{ - padding-top: 10px; - display: grid; - grid-auto-rows: 90px; - grid-row-gap: 10px; - } - .thumbnail-box{ /* overides rule in shared.css */ - height: 90px !important; - width: 120px !important; - } - - /* Put related vids below videos when window is too small */ - /* 1100px instead of 1080 because W3C is full of idiots who include scrollbar width */ - @media (max-width:1100px){ - main{ - grid-template-columns: 1fr 640px 40px 1fr; - } - .side-videos{ - margin-top: 10px; - grid-column: 2; - grid-row: 3; - width: initial; - } - .comments-area-outer{ - grid-row: 4; - } - } - - .download-dropdown-content{ - background-color: var(--interface-color); - padding: 10px; - list-style: none; - margin: 0px; - } - li.download-format{ - margin-bottom: 7px; - } - .format-attributes{ - list-style: none; - padding: 0px; - margin: 0px; - display: flex; - flex-direction: row; - } - .format-attributes li{ - white-space: nowrap; - max-height: 1.2em; - } - .format-ext{ - width: 60px; - } - .format-video-quality{ - width: 140px; - } - .format-audio-quality{ - width: 120px; - } - .format-file-size{ - width: 80px; - } - .format-codecs{ - width: 120px; - } + <link href="/youtube.com/static/watch.css" rel="stylesheet"> {% endblock style %} {% block main %} {% if playability_error %} <div class="playability-error"> <span>{{ 'Error: ' + playability_error }} - {% if invidious_reload_button %} - <a href="{{ video_url }}&use_invidious=0"><br> -Reload without invidious (for usage of new identity button).</a> - {% endif %} + {% if invidious_reload_button %} + <a href="{{ video_url }}&use_invidious=0"><br> + Reload without invidious (for usage of new identity button).</a> + {% endif %} </span> </div> {% elif (video_sources.__len__() == 0 or live) and hls_formats.__len__() != 0 %} @@ -387,143 +26,137 @@ Reload without invidious (for usage of new identity button).</a> </ol> </div> {% else %} - <div id="video-container"> - <div id="video-container-inner"> - <video controls autofocus class="video" height="{{ video_height }}px"> - {% for video_source in video_sources %} - <source src="{{ video_source['src'] }}" type="{{ video_source['type'] }}"> - {% endfor %} + <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 %} - {% for source in subtitle_sources %} - {% if source['on'] %} - <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}" default> - {% else %} - <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}"> - {% endif %} - {% endfor %} + {% for source in subtitle_sources %} + {% if source['on'] %} + <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}" default> + {% else %} + <track label="{{ source['label'] }}" src="{{ source['url'] }}" kind="subtitles" srclang="{{ source['srclang'] }}"> + {% endif %} + {% endfor %} + </video> + </figure> - </video> - </div> - </div> {% if time_start != 0 %} <script> - document.querySelector('video').currentTime = {{ time_start|tojson }}; + document.querySelector('js-video-player').currentTime = {{ time_start|tojson }}; </script> {% endif %} {% endif %} - <div class="video-info"> - <h2 class="title">{{ title }}</h2> - <ul class="labels"> - {%- if unlisted -%} - <li class="is-unlisted">Unlisted</li> - {%- endif -%} - {%- if age_restricted -%} - <li class="age-restricted">Age-restricted</li> - {%- endif -%} - {%- if limited_state -%} - <li>Limited state</li> - {%- endif -%} - {%- if live -%} - <li>Live</li> - {%- endif -%} - </ul> - <address>Uploaded by <a href="{{ uploader_channel_url }}">{{ uploader }}</a></address> - <span class="views">{{ view_count }} views</span> - - - <time datetime="$upload_date">Published on {{ time_published }}</time> - <span class="likes-dislikes">{{ like_count }} likes {{ dislike_count }} dislikes</span> - - <div class="external-player-controls"> - <input id="speed-control" type="text"> - <script> - var video = document.querySelector('video'); - var speedInput = document.querySelector('#speed-control'); - speedInput.addEventListener('keyup', (event) => { - if (event.key === 'Enter') { - var speed = parseFloat(speedInput.value); - if(!isNaN(speed)){ - video.playbackRate = speed; - } - } - }); - </script> - </div> - <input class="checkbox" name="video_info_list" value="{{ video_info }}" form="playlist-edit" type="checkbox"> - - <details class="download-dropdown"> - <summary class="download-dropdown-label">Download</summary> - <ul class="download-dropdown-content"> - {% for format in download_formats %} - <li class="download-format"> - <a class="download-link" href="{{ format['url'] }}"> - <ol class="format-attributes"> - <li class="format-ext">{{ format['ext'] }}</li> - <li class="format-video-quality">{{ format['video_quality'] }}</li> - <li class="format-audio-quality">{{ format['audio_quality'] }}</li> - <li class="format-file-size">{{ format['file_size'] }}</li> - <li class="format-codecs">{{ format['codecs'] }}</li> - </ol> - </a> - </li> - {% endfor %} - {% for download in other_downloads %} - <li class="download-format"> - <a href="{{ download['url'] }}"> - <ol class="format-attributes"> - <li class="format-ext">{{ download['ext'] }}</li> - <li class="format-label">{{ download['label'] }}</li> - </ol> - </a> - </li> - {% endfor %} + <div class="sc-info"> + <div class="video-info"> + <h1 class="v-title">{{ title }}</h1> + + <ul class="labels"> + {%- if unlisted -%} + <li class="is-unlisted">Unlisted</li> + {%- endif -%} + {%- if age_restricted -%} + <li class="age-restricted">Age-restricted</li> + {%- endif -%} + {%- if limited_state -%} + <li>Limited state</li> + {%- endif -%} + {%- if live -%} + <li>Live</li> + {%- endif -%} </ul> - </details> + <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="$upload_date">Published on {{ time_published }}</time> + <span class="v-likes-dislikes">{{ like_count }} likes {{ dislike_count }} dislikes</span> + + <div class="external-player-controls"> + <input class="speed" id="speed-control" type="text"> + <script> + (function main() { + 'use strict'; + const video = document.getElementById('js-video-player'); + const speedInput = document.getElementById('speed-control'); + speedInput.addEventListener('keyup', (event) => { + if (event.key === 'Enter') { + let speed = parseFloat(speedInput.value); + if(!isNaN(speed)){ + video.playbackRate = speed; + } + } + }); + }()); + </script> + </div> - <span class="description">{{ common_elements.text_runs(description)|escape|urlize|timestamps|safe }}</span> - <div class="music-list"> - {% if music_list.__len__() != 0 %} - <hr> - <table> - <caption>Music</caption> - <tr> - {% for attribute in music_attributes %} - <th>{{ attribute }}</th> - {% endfor %} - </tr> - {% for track in music_list %} + <input class="v-checkbox" name="video_info_list" value="{{ video_info }}" form="playlist-edit" type="checkbox"> + + <details class="v-download"> + <summary class="download-dropdown-label">Download</summary> + <ul class="download-dropdown-content"> + {% for format in download_formats %} + <li class="download-format"> + <a class="download-link" href="{{ format['url'] }}" download="{{ title }}.{{ format['ext'] }}"> + {{ format['ext'] }} {{ format['video_quality'] }} {{ format['audio_quality'] }} {{ format['file_size'] }} {{ format['codecs'] }} + </a> + </li> + {% endfor %} + {% for download in other_downloads %} + <li class="download-format"> + <a href="{{ download['url'] }}" download> + {{ download['ext'] }} {{ download['label'] }} + </a> + </li> + {% endfor %} + </ul> + </details> + <span class="v-description">{{ common_elements.text_runs(description)|escape|urlize|timestamps|safe }}</span> + + <div class="v-music-list"> + {% if music_list.__len__() != 0 %} + <hr> + <table> + <caption>Music</caption> <tr> {% for attribute in music_attributes %} - <td>{{ track.get(attribute.lower(), '') }}</td> + <th>{{ attribute }}</th> {% endfor %} </tr> - {% endfor %} - </table> - {% endif %} - </div> - <details class="more-info"> - <summary>More info</summary> - <div class="more-info-content"> - <p>Tor exit node: {{ ip_address }}</p> - {% if invidious_used %} - <p>Used Invidious as fallback.</p> - {% endif %} - <p class="allowed-countries">Allowed countries: {{ allowed_countries|join(', ') }}</p> - - {% if settings.use_sponsorblock_js %} - <ul class="more-actions"> - <li><label><input type=checkbox id=skip_sponsors checked>skip sponsors</label> <span id=skip_n></span> - </ul> + {% for track in music_list %} + <tr> + {% for attribute in music_attributes %} + <td>{{ track.get(attribute.lower(), '') }}</td> + {% endfor %} + </tr> + {% endfor %} + </table> {% endif %} </div> - </details> - </div> + <details class="v-more-info"> + <summary>More info</summary> + <div class="more-info-content"> + <p>Tor exit node: {{ ip_address }}</p> + {% if invidious_used %} + <p>Used Invidious as fallback.</p> + {% endif %} + <p class="allowed-countries">Allowed countries: {{ allowed_countries|join(', ') }}</p> + {% if settings.use_sponsorblock_js %} + <ul class="more-actions"> + <li><label><input type=checkbox id=skip_sponsors checked>skip sponsors</label> <span id=skip_n></span> + </ul> + {% endif %} + </div> + </details> + </div> + + <div class="side-videos"> - <div class="side-videos"> - {% if playlist %} - <div class="playlist"> + <!-- 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"> @@ -550,167 +183,171 @@ Reload without invidious (for usage of new identity button).</a> </nav> {% if playlist['current_index'] is not none %} <script> - // from https://stackoverflow.com/a/6969486 - function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string - } - var playability_error = {{ 'true' if playability_error else 'false' }}; - var 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 - var cookieValue = document.cookie.replace(new RegExp( - '(?:(?:^|.*;\\s*)autoplay_' + playlist_id + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1'); - 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 - checkbox.addEventListener( 'change', function() { - if(this.checked) { - autoplayEnabled = 1; - document.cookie = 'autoplay_' + playlist_id + '=1'; - } else { - autoplayEnabled = 0; - document.cookie = 'autoplay_' + playlist_id + '=0'; - } - }); - - if(!playability_error){ - // play the video if autoplay is on - var vid = document.querySelector('video'); - if(autoplayEnabled){ - vid.play(); - } - } - - 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 %} - var nextVideoDelay = 1000; - - // scroll playlist to proper position - var 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); - } - } + (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); + } + } + }()); </script> {% endif %} {% if playlist['id'] is not none %} <script> - // 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); - }); + (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); + }); + }()); </script> {% endif %} </div> - {% endif %} + {% endif %} + <!-- /playlist --> + + {% if subtitle_sources %} + <details id="transcript-details"> + <summary>Transcript</summary> + <div id="transcript-div"> + <select id="select-tt"> + {% for source in subtitle_sources %} + <option>{{ source['label'] }}</option> + {% endfor %} + </select> + <label for="transcript-use-table">Table view</label> + <input id="transcript-use-table" type="checkbox"> + <table id="transcript-table"></table> + </div> + </details> + {% endif %} + - {% if subtitle_sources %} - <details id="transcript-details"> - <summary>Transcript</summary> - <div id="transcript-div"> - <select id="select-tt"> - {% for source in subtitle_sources %} - <option>{{ source['label'] }}</option> + {% if settings.related_videos_mode != 0 %} + <details class="related-videos-outer" {{'open' if settings.related_videos_mode == 1 else ''}}> + <summary>Related Videos</summary> + <nav class="related-videos-inner"> + {% for info in related %} + {{ common_elements.item(info, include_badges=false) }} {% endfor %} - </select> - <label for="transcript-use-table">Table view</label> - <input type="checkbox" id="transcript-use-table"> - <table id="transcript-table"></table> - </div> - </details> - {% endif %} + </nav> + </details> + {% endif %} - {% if settings.related_videos_mode != 0 %} - <details class="related-videos-outer" {{'open' if settings.related_videos_mode == 1 else ''}}> - <summary>Related Videos</summary> - <nav class="related-videos-inner"> - {% for info in related %} - {{ common_elements.item(info, include_badges=false) }} - {% endfor %} - </nav> - </details> - {% endif %} - </div> + </div> - {% if settings.comments_mode != 0 %} - {% if comments_disabled %} - <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> - <section class="comments-area-inner comments-area"> - {% if comments_info %} - {{ comments.video_comments(comments_info) }} - {% endif %} - </section> - </details> + <!-- comments --> + {% if settings.comments_mode != 0 %} + {% if comments_disabled %} + <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> + <div class="comments-area-inner comments-area"> + {% if comments_info %} + {{ comments.video_comments(comments_info) }} + {% endif %} + </div> + </details> + {% endif %} {% endif %} - {% endif %} + + </div> <script> data = {{ js_data|tojson }} </script> <script src="/youtube.com/static/js/common.js"></script> |