diff options
-rw-r--r-- | settings.py | 18 | ||||
-rw-r--r-- | youtube/__init__.py | 17 | ||||
-rw-r--r-- | youtube/local_playlist.py | 1 | ||||
-rw-r--r-- | youtube/static/comments.css | 5 | ||||
-rw-r--r-- | youtube/static/dark_theme.css | 21 | ||||
-rw-r--r-- | youtube/static/gray_theme.css | 9 | ||||
-rw-r--r-- | youtube/static/light_theme.css | 9 | ||||
-rw-r--r-- | youtube/static/shared.css | 491 | ||||
-rw-r--r-- | youtube/templates/base.html | 25 | ||||
-rw-r--r-- | youtube/templates/channel.html | 3 | ||||
-rw-r--r-- | youtube/templates/comments.html | 28 | ||||
-rw-r--r-- | youtube/templates/comments_page.html | 83 | ||||
-rw-r--r-- | youtube/templates/common_elements.html | 140 | ||||
-rw-r--r-- | youtube/templates/delete_comment.html | 8 | ||||
-rw-r--r-- | youtube/templates/home.html | 22 | ||||
-rw-r--r-- | youtube/templates/local_playlist.html | 50 | ||||
-rw-r--r-- | youtube/templates/login.html | 17 | ||||
-rw-r--r-- | youtube/templates/playlist.html | 121 | ||||
-rw-r--r-- | youtube/templates/post_comment.html | 23 | ||||
-rw-r--r-- | youtube/templates/search.html | 34 | ||||
-rw-r--r-- | youtube/templates/watch.html | 439 | ||||
-rw-r--r-- | youtube/watch.py | 25 | ||||
-rw-r--r-- | youtube/yt_data_extract.py | 4 |
23 files changed, 754 insertions, 839 deletions
diff --git a/settings.py b/settings.py index 4aedd19..d02a1fd 100644 --- a/settings.py +++ b/settings.py @@ -66,6 +66,24 @@ For security reasons, enabling this is not recommended.''', 1 to sort by newest''', }), + ('theater_mode', { + 'type': bool, + 'default': True, + 'comment': '', + }), + + ('default_resolution', { + 'type': int, + 'default': 720, + 'comment': '', + }), + + ('theme', { + 'type': int, + 'default': 0, + 'comment': '', + }), + ('gather_googlevideo_domains', { 'type': bool, 'default': False, diff --git a/youtube/__init__.py b/youtube/__init__.py index e620827..38ff7d3 100644 --- a/youtube/__init__.py +++ b/youtube/__init__.py @@ -1,7 +1,22 @@ import flask +import settings yt_app = flask.Flask(__name__) yt_app.url_map.strict_slashes = False @yt_app.route('/') def homepage(): - return flask.render_template('base.html', title="Youtube local") + return flask.render_template('home.html', title="Youtube local") + + +theme_names = { + 0: 'light_theme', + 1: 'gray_theme', + 2: 'dark_theme', +} + +@yt_app.context_processor +def inject_theme_preference(): + return { + 'theme_path': '/youtube.com/static/' + theme_names[settings.theme] + '.css', + } + diff --git a/youtube/local_playlist.py b/youtube/local_playlist.py index bb05d1a..4b92315 100644 --- a/youtube/local_playlist.py +++ b/youtube/local_playlist.py @@ -82,7 +82,6 @@ def get_local_playlist_videos(name, offset=0, amount=50): else: info['thumbnail'] = util.get_thumbnail_url(info['id']) missing_thumbnails.append(info['id']) - info['item_size'] = 'small' info['type'] = 'video' yt_data_extract.add_extra_html_info(info) videos.append(info) diff --git a/youtube/static/comments.css b/youtube/static/comments.css index 4cec3e1..85f0cc1 100644 --- a/youtube/static/comments.css +++ b/youtube/static/comments.css @@ -69,7 +69,7 @@ display:grid; grid-template-columns: auto auto 100px 1fr; grid-template-rows: 0fr 0fr 0fr 0fr; - background-color: #dadada; + background-color: var(--interface-color); justify-content: start; } @@ -102,8 +102,6 @@ grid-column: 3; grid-row: 1; white-space: nowrap; - color: black; - } @@ -126,4 +124,5 @@ .more-comments{ justify-self:center; margin-top:10px; + margin-bottom: 10px; } diff --git a/youtube/static/dark_theme.css b/youtube/static/dark_theme.css new file mode 100644 index 0000000..4f302cb --- /dev/null +++ b/youtube/static/dark_theme.css @@ -0,0 +1,21 @@ +body{ + --interface-color: #333333; + --text-color: #cccccc; + --background-color: #000000; +} + +a:link { + color: #22aaff; +} + +a:visited { + color: ##7755ff; +} + +a:not([href]){ + color: var(--text-color); +} + +.comment .permalink{ + color: #ffffff; +} diff --git a/youtube/static/gray_theme.css b/youtube/static/gray_theme.css new file mode 100644 index 0000000..69cc849 --- /dev/null +++ b/youtube/static/gray_theme.css @@ -0,0 +1,9 @@ +body{ + --interface-color: #dadada; + --text-color: #222222; + --background-color: #bcbcbc; +} + +.comment .permalink{ + color: #000000; +} diff --git a/youtube/static/light_theme.css b/youtube/static/light_theme.css new file mode 100644 index 0000000..05697b9 --- /dev/null +++ b/youtube/static/light_theme.css @@ -0,0 +1,9 @@ +body{ + --interface-color: #ffffff; + --text-color: #222222; + --background-color: #f8f8f8; +} + +.comment .permalink{ + color: #000000; +} diff --git a/youtube/static/shared.css b/youtube/static/shared.css index a360972..a79e42b 100644 --- a/youtube/static/shared.css +++ b/youtube/static/shared.css @@ -4,93 +4,114 @@ h1, h2, h3, h4, h5, h6, div, button{ } +address{ + font-style:normal; +} body{ margin:0; padding: 0; - color:#222; + color:var(--text-color); - background-color:#cccccc; + background-color:var(--background-color); min-height:100vh; - - display:grid; - grid-template-rows: 50px 1fr; + display: flex; + flex-direction: column; } header{ background-color:#333333; + height: 50px; - grid-row: 1; - - display:grid; - grid-template-columns: minmax(0px, 3fr) 640px 40px 500px minmax(0px,2fr); - } - - main{ - grid-row: 2; + display: flex; + justify-content: center; } -address{ - font-style:normal; -} - - - - #site-search{ - grid-column: 2; - display: grid; - grid-template-columns: 1fr auto auto; + #home-link{ + align-self: center; + margin-left:10px; + color: #ffffff; + } - } - #site-search .search-box{ - align-self:center; - height:25px; - border:0; - - grid-column: 1; + #site-search{ + max-width: 600px; + margin-left:10px; + display: flex; + flex-grow: 1; } - #site-search .search-button{ - grid-column: 2; - align-self:center; - height:25px; - border-style:solid; - border-width:1px; - } - #site-search .dropdown{ - margin-left:5px; - grid-column: 3; - align-self:center; - height:25px; - } - #site-search .dropdown button{ + #site-search .search-box{ + align-self:center; + height:25px; + border:0; + + flex-grow: 1; + } + #site-search .search-button{ align-self:center; height:25px; border-style:solid; border-width:1px; } - #site-search .css-sucks{ - width:0px; - height:0px; + #site-search .dropdown{ + margin-left:5px; + align-self:center; + height:25px; } - #site-search .dropdown-content{ - grid-template-columns: auto auto; - white-space: nowrap; + #site-search .dropdown button{ + align-self:center; + height:25px; + + border-style:solid; + border-width:1px; + } + #site-search .css-sucks{ + width:0px; + height:0px; } - #site-search .dropdown-content h3{ - grid-column:1 / span 2; + #site-search .dropdown-content{ + grid-template-columns: auto auto; + white-space: nowrap; } + #site-search .dropdown-content h3{ + grid-column:1 / span 2; + } + + #playlist-edit{ + margin-left: 10px; + align-self: center; + } + #local-playlists{ + margin-right:5px; + color: #ffffff; + } + #playlist-name-selection{ + } + #playlist-add-button{ + padding-left: 10px; + padding-right: 10px; + } + #item-selection-reset{ + padding-left: 10px; + padding-right: 10px; + } + + main{ + flex-grow: 1; + padding-bottom: 20px; + } + .dropdown{ z-index:1; } .dropdown-content{ display:none; - background-color: #e9e9e9; + background-color: var(--interface-color); } .dropdown:hover .dropdown-content{ /* For some reason, if this is just grid, it will insist on being 0px wide just like its 0px by 0px parent */ @@ -98,281 +119,179 @@ address{ display:inline-grid; } - - -#header-right{ - grid-column:4; - - display:grid; - grid-template-columns:auto auto auto 1fr; - grid-template-rows: 1fr; - width: 540px; -} - #playlist-edit{ - display:contents; - } - #local-playlists{ - grid-column: 1; - grid-row:1; - align-self: center; - margin-right:5px; - color: #ffffff; - } - #playlist-name-selection{ - grid-column:2; - grid-row:1; - justify-self:start; - align-self: center; - } - #playlist-add-button{ - grid-column:3; - grid-row:1; - align-self: center; - padding-left: 10px; - padding-right: 10px; - } - #item-selection-reset{ - grid-column:4; - grid-row:1; - align-self: center; - justify-self:start; - padding-left: 10px; - padding-right: 10px; - } - - - .item-list{ display: grid; - grid-auto-rows: 138px; grid-row-gap: 10px; } - .item-list .video-thumbnail-box{ - width:246px; - } - .item-list .playlist-thumbnail-box{ - width:246px; - } .item-grid{ - display:grid; - grid-template-columns: repeat(auto-fill, 400px); - grid-auto-rows: 94px; - grid-row-gap: 10px; + display: flex; + flex-wrap: wrap; } - .item-grid .video-thumbnail-box{ - width:168px; + .item-grid > .playlist-item-box{ + margin-right: 10px; } - .item-grid .playlist-thumbnail-box{ - width:168px; + .item-grid > * { + margin-bottom: 10px; + } + .item-grid .horizontal-item-box .item{ + width:370px; + } + .item-grid .vertical-item-box .item{ } - - -.medium-item-box{ - - display:grid; - grid-template-columns: 1fr 30px; +.item-box{ + display: inline-flex; + flex-direction: row; } -.medium-item{ - background-color:#bcbcbc; - text-decoration:none; - font-size: 12px; - color: #767676; - - display: grid; - align-content: start; - grid-template-columns: auto 1fr auto; - grid-template-rows: auto auto auto auto auto 1fr; +.vertical-item-box{ } - .medium-item .title{ - grid-column:2 / span 2; - grid-row:1; - justify-self:start; - min-width: 0; - max-height:3.6em; - overflow:hidden; - - color: #333; - font-size: 16px; - font-weight: 500; - text-decoration:initial; - } - .medium-item address{ - display:inline; +.horizontal-item-box{ +} + .item{ + background-color:var(--interface-color); + text-decoration:none; + font-size: 12px; + color: #767676; } - /*.medium-item .views{ - grid-column: 3; - grid-row: 2; - justify-self:end; + + .horizontal-item-box .item { + flex-grow: 1; + display: grid; + align-content: start; + grid-template-columns: auto 1fr; + grid-template-rows: auto auto auto auto 1fr; } - .medium-item time{ - grid-column: 2; - grid-row: 3; - justify-self:start; - }*/ - .medium-item .stats{ - grid-column: 2 / span 2; - grid-row: 2; - max-height:2.4em; - overflow:hidden; + .vertical-item-box .item{ + width: 168px; } - .medium-item .stats > *::after{ - content: " | "; + .thumbnail-box{ + font-size: 0px; /* prevent newlines and blank space from creating gaps */ + position: relative; + display: block; } - .medium-item .stats > *:last-child::after{ - content: ""; + .horizontal-item-box .thumbnail-box{ + grid-row: 1 / span 5; + margin-right: 4px; } - - .medium-item .description{ - grid-column: 2 / span 2; - grid-row: 4; - } - .medium-item .badges{ - grid-column: 2 / span 2; - grid-row: 5; - } - /* thumbnail size */ - .medium-item img{ - /*height:138px; - width:246px;*/ - height:100%; - justify-self:center; - } - -.small-item-box{ - color: #767676; - font-size: 12px; + .no-description .thumbnail-box{ + width: 168px; + height:94px; + } + .has-description .thumbnail-box{ + width: 246px; + height:138px; + } + .video-item .thumbnail-info{ + position: absolute; + bottom: 2px; + right: 2px; + opacity: .8; + color: #ffffff; + font-size: 12px; + background-color: #000000; + } + .playlist-item .thumbnail-info{ + position: absolute; + right: 0px; + bottom: 0px; + height: 100%; + width: 50%; + text-align:center; + white-space: pre-line; + opacity: .8; + color: #cfcfcf; + font-size: 12px; + background-color: #000000; + } + .playlist-item .thumbnail-info span{ /* trick to vertically center the text */ + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + } + .thumbnail-img{ /* center it */ + margin: auto; + display: block; + max-height: 100%; + } + .horizontal-item-box .thumbnail-img{ + height: 100%; + } - display:grid; - grid-template-columns: 1fr 30px; - grid-template-rows: 94px; -} + .item .title{ + min-width: 0; + max-height:3.6em; + overflow:hidden; -.small-item{ - background-color:#bcbcbc; - align-content: start; - text-decoration:none; - - display: grid; - grid-template-columns: 168px 1fr; - grid-column-gap: 5px; - grid-template-rows: auto auto auto 1fr; -} - .small-item .title{ - grid-column:2; - grid-row:1; - margin:0; + color: var(--text-color); + font-size: 16px; + font-weight: 500; + text-decoration:initial; + } - color: #333; - font-size: 16px; - font-weight: 500; - text-decoration:initial; - min-width: 0; - justify-self:start; + .stats{ + list-style: none; + padding: 0px; + margin: 0px; + } + .horizontal-stats{ + max-height:2.4em; + overflow:hidden; + } + .horizontal-stats > li{ + display: inline; + } - overflow:hidden; - max-height: 3.3em; - line-height: 1.1em; - } - .small-item address{ - grid-column: 2; - grid-row: 2; - justify-self: start; - } - - .small-item .views{ - grid-column: 2; - grid-row: 3; - justify-self:start; - } - /* thumbnail size */ - .small-item img{ - /*height:94px; - width:168px;*/ - height:100%; - justify-self:center; - } - -.item-checkbox{ - justify-self:start; - align-self:center; - height:30px; - width:30px; - - grid-column: 2; -} + .horizontal-stats > li::after{ + content: " | "; + } + .horizontal-stats > li:last-child::after{ + content: ""; + } -/* ---Thumbnails for videos---- */ -.video-thumbnail-box{ - max-height:100%; + .vertical-stats{ + display: flex; + flex-direction: column; + } + .stats address{ + display: inline; + } + .vertical-stats li{ + max-height: 1.3em; + overflow: hidden; + } - grid-column:1; - grid-row:1 / span 6; - - display:grid; - grid-template-columns: 1fr 0fr; -} - .video-thumbnail-img{ - grid-column:1 / span 2; - grid-row:1; - } - .video-duration{ - grid-column: 2; - grid-row: 1; - align-self: end; - opacity: .8; - color: #ffffff; - font-size: 12px; - background-color: #000000; + .item-checkbox{ + justify-self:start; + align-self:center; + height:30px; + width:30px; + min-width:30px; + margin: 0px; } -/* ---Thumbnails for playlists---- */ -.playlist-thumbnail-box{ - max-height:100%; - - grid-column:1; - grid-row:1 / span 6; - - display:grid; - grid-template-columns: 3fr 2fr; -} - .playlist-thumbnail-img{ - grid-column:1 / span 2; - grid-row:1; - } - .playlist-thumbnail-info{ - grid-column:2; - grid-row:1; - - display: grid; - align-items:center; - - text-align:center; - white-space: pre-line; - opacity: .8; - color: #cfcfcf; - background-color: #000000; - } .page-button-row{ + margin-top: 10px; + margin-bottom: 10px; justify-self:center; + justify-content: center; display: grid; grid-auto-columns: 40px; grid-auto-flow: column; height: 40px; } .page-button{ - background-color: #e9e9e9; + background-color: var(--interface-color); border-style: outset; border-width: 2px; font-weight: bold; text-align: center; } .sort-button{ - background-color: #d0d0d0; + background-color: var(--interface-color); padding: 2px; justify-self: start; } diff --git a/youtube/templates/base.html b/youtube/templates/base.html index 72e3691..9127efa 100644 --- a/youtube/templates/base.html +++ b/youtube/templates/base.html @@ -4,6 +4,7 @@ <meta charset="utf-8"> <title>{{ page_title }}</title> <meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline'; script-src 'none'; media-src 'self' https://*.googlevideo.com"> + <link href="{{ theme_path }}" type="text/css" rel="stylesheet"> <link href="/youtube.com/static/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"> @@ -16,6 +17,7 @@ </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 }}"> <button type="submit" value="Search" class="search-button">Search</button> @@ -91,19 +93,16 @@ </div> </form> - <div id="header-right"> - <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 %} - </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> - </form> - <a href="/youtube.com/playlists" id="local-playlists">Local playlists</a> - </div> + <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 %} + </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> + </form> </header> <main> {% block main %} diff --git a/youtube/templates/channel.html b/youtube/templates/channel.html index 069e33b..ed04988 100644 --- a/youtube/templates/channel.html +++ b/youtube/templates/channel.html @@ -31,7 +31,7 @@ grid-auto-flow: column; justify-content:start; - background-color: #aaaaaa; + background-color: var(--interface-color); padding: 3px; padding-left: 6px; } @@ -44,7 +44,6 @@ padding-top: 8px; padding-bottom: 8px; padding-left: 6px; - background-color: #bababa; margin-bottom: 10px; } #number-of-results{ diff --git a/youtube/templates/comments.html b/youtube/templates/comments.html index 82276b8..20cde4e 100644 --- a/youtube/templates/comments.html +++ b/youtube/templates/comments.html @@ -29,21 +29,19 @@ {% endmacro %} {% macro video_comments(comments_info) %} - <section class="comments-area"> - <div class="comment-links"> - {% for link_text, link_url in comments_info['comment_links'] %} - <a class="sort-button" href="{{ link_url }}">{{ link_text }}</a> - {% endfor %} - </div> - <div class="comments"> - {% for comment in comments_info['comments'] %} - {{ render_comment(comment, comments_info['include_avatars']) }} - {% endfor %} - </div> - {% if 'more_comments_url' is in comments_info %} - <a class="page-button more-comments" href="{{ comments_info['more_comments_url'] }}">More comments</a> - {% endif %} - </section> + <div class="comment-links"> + {% for link_text, link_url in comments_info['comment_links'] %} + <a class="sort-button" href="{{ link_url }}">{{ link_text }}</a> + {% endfor %} + </div> + <div class="comments"> + {% for comment in comments_info['comments'] %} + {{ render_comment(comment, comments_info['include_avatars']) }} + {% endfor %} + </div> + {% if 'more_comments_url' is in comments_info %} + <a class="page-button more-comments" href="{{ comments_info['more_comments_url'] }}">More comments</a> + {% endif %} {% endmacro %} {% macro comment_posting_box(info) %} diff --git a/youtube/templates/comments_page.html b/youtube/templates/comments_page.html index 68c8537..047404a 100644 --- a/youtube/templates/comments_page.html +++ b/youtube/templates/comments_page.html @@ -3,63 +3,46 @@ {% import "comments.html" as comments %} {% block style %} - main{ - display:grid; - grid-template-columns: 3fr 2fr; + .comments-area{ + margin: auto; + width:640px; } - #left{ - background-color:#bcbcbc; - - display: grid; - grid-column: 1; - grid-row: 1; - grid-template-columns: 1fr 640px; - grid-template-rows: 0fr 0fr 0fr; - } - .comments-area{ - grid-column:2; - } - .comment{ - width:640px; - } {% endblock style %} {% block main %} - <div id="left"> - <section class="comments-area"> - {% if not comments_info['is_replies'] %} - <section class="video-metadata"> - <a class="video-metadata-thumbnail-box" href="{{ comments_info['video_url'] }}" title="{{ comments_info['video_title'] }}"> - <img class="video-metadata-thumbnail-img" src="{{ comments_info['video_thumbnail'] }}" height="180px" width="320px"> - </a> - <a class="title" href="{{ comments_info['video_url'] }}" title="{{ comments_info['video_title'] }}">{{ comments_info['video_title'] }}</a> - - <h2>Comments page {{ comments_info['page_number'] }}</h2> - <span>Sorted by {{ comments_info['sort_text'] }}</span> - </section> - {% endif %} - - {{ comments.comment_posting_box(comment_posting_box_info) }} - - {% if not comments_info['is_replies'] %} - <div class="comment-links"> - {% for link_text, link_url in comments_info['comment_links'] %} - <a class="sort-button" href="{{ link_url }}">{{ link_text }}</a> - {% endfor %} - </div> - {% endif %} - - <div class="comments"> - {% for comment in comments_info['comments'] %} - {{ comments.render_comment(comment, comments_info['include_avatars']) }} + <section class="comments-area"> + {% if not comments_info['is_replies'] %} + <section class="video-metadata"> + <a class="video-metadata-thumbnail-box" href="{{ comments_info['video_url'] }}" title="{{ comments_info['video_title'] }}"> + <img class="video-metadata-thumbnail-img" src="{{ comments_info['video_thumbnail'] }}" height="180px" width="320px"> + </a> + <a class="title" href="{{ comments_info['video_url'] }}" title="{{ comments_info['video_title'] }}">{{ comments_info['video_title'] }}</a> + + <h2>Comments page {{ comments_info['page_number'] }}</h2> + <span>Sorted by {{ comments_info['sort_text'] }}</span> + </section> + {% endif %} + + {{ comments.comment_posting_box(comment_posting_box_info) }} + + {% if not comments_info['is_replies'] %} + <div class="comment-links"> + {% for link_text, link_url in comments_info['comment_links'] %} + <a class="sort-button" href="{{ link_url }}">{{ link_text }}</a> {% endfor %} </div> - {% if 'more_comments_url' is in comments_info %} - <a class="page-button more-comments" href="{{ comments_info['more_comments_url'] }}">More comments</a> - {% endif %} - </section> - </div> + {% endif %} + + <div class="comments"> + {% for comment in comments_info['comments'] %} + {{ comments.render_comment(comment, comments_info['include_avatars']) }} + {% endfor %} + </div> + {% if 'more_comments_url' is in comments_info %} + <a class="page-button more-comments" href="{{ comments_info['more_comments_url'] }}">More comments</a> + {% endif %} + </section> {% endblock main %} diff --git a/youtube/templates/common_elements.html b/youtube/templates/common_elements.html index 49e2fad..67655b3 100644 --- a/youtube/templates/common_elements.html +++ b/youtube/templates/common_elements.html @@ -14,121 +14,53 @@ {%- endif -%} {% endmacro %} -{% macro small_item(info, include_author=true) %} - <div class="small-item-box"> - <div class="small-item"> - {% if info['type'] == 'video' %} - <a class="video-thumbnail-box" href="{{ info['url'] }}" title="{{ info['title'] }}"> - <img class="video-thumbnail-img" src="{{ info['thumbnail'] }}"> - <span class="video-duration">{{ info['duration'] }}</span> - </a> - <a class="title" href="{{ info['url'] }}" title="{{ info['title'] }}">{{ info['title'] }}</a> - - <address>{{ info['author'] }}</address> - <span class="views">{{ info['views'] }}</span> - - {% elif info['type'] == 'playlist' %} - <a class="playlist-thumbnail-box" href="{{ info['url'] }}" title="{{ info['title'] }}"> - <img class="playlist-thumbnail-img" src="{{ info['thumbnail'] }}"> - <div class="playlist-thumbnail-info"> - <span>{{ info['size'] }}</span> +{% macro item(info, description=false, horizontal=true, include_author=true) %} + <div class="item-box {{ info['type'] + '-item-box' }} {{'horizontal-item-box' if horizontal else 'vertical-item-box'}} {{'has-description' if description else 'no-description'}}"> + <div class="item {{ info['type'] + '-item' }}"> + <a class="thumbnail-box" href="{{ info['url'] }}" title="{{ info['title'] }}"> + <img class="thumbnail-img" src="{{ info['thumbnail'] }}"> + {% if info['type'] != 'channel' %} + <div class="thumbnail-info"> + <span>{{ info['size'] if info['type'] == 'playlist' else info['duration'] }}</span> </div> - </a> - <a class="title" href="{{ info['url'] }}" title="{{ info['title'] }}">{{ info['title'] }}</a> - - <address>{{ info['author'] }}</address> - {% else %} - Error: unsupported item type - {% endif %} - </div> - {% if info['type'] == 'video' %} - <input class="item-checkbox" type="checkbox" name="video_info_list" value="{{ info['video_info'] }}" form="playlist-edit"> - {% endif %} - </div> -{% endmacro %} - -{% macro get_stats(info, include_author=true) %} - {% if include_author %} - {% if 'author_url' is in(info) %} - <address>By <a href="{{ info['author_url'] }}">{{ info['author'] }}</a></address> - {% else %} - <address><b>{{ info['author'] }}</b></address> - {% endif %} - {% endif %} - {% if 'views' is in(info) %} - <span class="views">{{ info['views'] }}</span> - {% endif %} - {% if 'published' is in(info) %} - <time>{{ info['published'] }}</time> - {% endif %} -{% endmacro %} - - - -{% macro medium_item(info, include_author=true) %} - <div class="medium-item-box"> - <div class="medium-item"> - {% if info['type'] == 'video' %} - <a class="video-thumbnail-box" href="{{ info['url'] }}" title="{{ info['title'] }}"> - <img class="video-thumbnail-img" src="{{ info['thumbnail'] }}"> - <span class="video-duration">{{ info['duration'] }}</span> - </a> - - <a class="title" href="{{ info['url'] }}" title="{{ info['title'] }}">{{ info['title'] }}</a> - - <div class="stats"> - {{ get_stats(info, include_author) }} - </div> - + {% endif %} + </a> + + <div class="title"><a class="title" href="{{ info['url'] }}" title="{{ info['title'] }}">{{ info['title'] }}</a></div> + + <ul class="stats {{'vertical-stats' if horizontal and not description and include_author else 'horizontal-stats'}}"> + {% if info['type'] == 'channel' %} + <li><span>{{ info['subscriber_count'] }} subscribers</span></li> + <li><span>{{ info['size'] }} videos</span></li> + {% else %} + {% if include_author %} + {% if 'author_url' is in(info) %} + <li><address title="{{ info['author'] }}">By <a href="{{ info['author_url'] }}">{{ info['author'] }}</a></address></li> + {% else %} + <li><address title="{{ info['author'] }}"><b>{{ info['author'] }}</b></address></li> + {% endif %} + {% endif %} + {% if 'views' is in(info) %} + <li><span class="views">{{ info['views'] }}</span></li> + {% endif %} + {% if 'published' is in(info) %} + <li><time>{{ info['published'] }}</time></li> + {% endif %} + {% endif %} + </ul> + + {% if description %} <span class="description">{{ text_runs(info.get('description', '')) }}</span> - <span class="badges">{{ info['badges']|join(' | ') }}</span> - {% elif info['type'] == 'playlist' %} - <a class="playlist-thumbnail-box" href="{{ info['url'] }}" title="{{ info['title'] }}"> - <img class="playlist-thumbnail-img" src="{{ info['thumbnail'] }}"> - <div class="playlist-thumbnail-info"> - <span>{{ info['size'] }}</span> - </div> - </a> - - <a class="title" href="{{ info['url'] }}" title="{{ info['title'] }}">{{ info['title'] }}</a> - - <div class="stats"> - {{ get_stats(info, include_author) }} - </div> - {% elif info['type'] == 'channel' %} - <a class="video-thumbnail-box" href="{{ info['url'] }}" title="{{ info['title'] }}"> - <img class="video-thumbnail-img" src="{{ info['thumbnail'] }}"> - </a> - - <a class="title" href="{{ info['url'] }}">{{ info['title'] }}</a> - - <span>{{ info['subscriber_count'] }} subscribers</span> - <span>{{ info['size'] }} videos</span> - - <span class="description">{{ text_runs(info.get('description', '')) }}</span> - {% else %} - Error: unsupported item type {% endif %} + <span class="badges">{{ info['badges']|join(' | ') }}</span> </div> {% if info['type'] == 'video' %} <input class="item-checkbox" type="checkbox" name="video_info_list" value="{{ info['video_info'] }}" form="playlist-edit"> {% endif %} </div> -{% endmacro %} - -{% macro item(info, include_author=true) %} - {% if info['item_size'] == 'small' %} - {{ small_item(info, include_author) }} - {% elif info['item_size'] == 'medium' %} - {{ medium_item(info, include_author) }} - {% else %} - Error: Unknown item size - {% endif %} {% endmacro %} - - {% macro page_buttons(estimated_pages, url, parameters_dictionary) %} {% set current_page = parameters_dictionary.get('page', 1)|int %} {% set parameters_dictionary = parameters_dictionary.to_dict() %} diff --git a/youtube/templates/delete_comment.html b/youtube/templates/delete_comment.html index 71555ee..28c8f2a 100644 --- a/youtube/templates/delete_comment.html +++ b/youtube/templates/delete_comment.html @@ -2,14 +2,10 @@ {% extends "base.html" %} {% block style %} - main{ - display: grid; - grid-template-columns: minmax(0px, 3fr) 640px 40px 500px minmax(0px,2fr); - align-content: start; - } main > div, main > form{ + margin: auto; margin-top:20px; - grid-column:2; + width: 640px; } {% endblock style %} diff --git a/youtube/templates/home.html b/youtube/templates/home.html new file mode 100644 index 0000000..9890f5e --- /dev/null +++ b/youtube/templates/home.html @@ -0,0 +1,22 @@ +{% 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; + } +{% endblock style %} +{% block main %} + <ul> + <li><a href="/youtube.com/playlists">Local playlists</a></li> + <li><a href="/youtube.com/subscriptions">Subscriptions</a></li> + <li><a href="/youtube.com/subscription_manager">Subscription Manager</a></li> + <li><a href="/youtube.com/settings">Settings</a></li> + </ul> +{% endblock main %} diff --git a/youtube/templates/local_playlist.html b/youtube/templates/local_playlist.html index f8e6f01..7ba0642 100644 --- a/youtube/templates/local_playlist.html +++ b/youtube/templates/local_playlist.html @@ -2,59 +2,41 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - main{ - display:grid; - grid-template-columns: 3fr 1fr; + main > *{ + width: 800px; + margin: auto; } - - - #left{ - grid-column: 1; - grid-row: 1; - - display: grid; - grid-template-columns: 1fr 800px auto; - grid-template-rows: 0fr 1fr 0fr; + .playlist-metadata{ + display: flex; + flex-direction: row; + justify-content: space-between; } .playlist-title{ - grid-column:2; } #playlist-remove-button{ - grid-column:3; align-self: center; white-space: nowrap; } #results{ - - grid-row: 2; - grid-column: 2 / span 2; - - display: grid; grid-auto-rows: 0fr; grid-row-gap: 10px; - - } - .page-button-row{ - grid-row: 3; - grid-column: 2; - justify-self: center; } {% endblock style %} {% block main %} - <div id="left"> + <div class="playlist-metadata"> <h2 class="playlist-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> - <div id="results"> - {% 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> </div> + <div id="results"> + {% 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> {% endblock main %} diff --git a/youtube/templates/login.html b/youtube/templates/login.html index 0f09a62..384f1ac 100644 --- a/youtube/templates/login.html +++ b/youtube/templates/login.html @@ -2,16 +2,14 @@ {% extends "base.html" %} {% block style %} - main{ - display: grid; - grid-template-columns: minmax(0px, 3fr) 640px 40px 500px minmax(0px,2fr); - align-content: start; - grid-row-gap: 40px; + main > * { + width: 640px; + margin: auto; } - main form{ + background-color: var(--interface-color); + padding: 10px; margin-top:20px; - grid-column:2; display:grid; justify-items: start; align-content: start; @@ -26,10 +24,9 @@ margin-top:20px; } #tor-note{ - grid-row:2; - grid-column:2; - background-color: #dddddd; + background-color: var(--interface-color); padding: 10px; + margin-top: 40px; } {% endblock style %} diff --git a/youtube/templates/playlist.html b/youtube/templates/playlist.html index 371b51b..ab2640f 100644 --- a/youtube/templates/playlist.html +++ b/youtube/templates/playlist.html @@ -2,72 +2,45 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - main{ - display:grid; - grid-template-columns: 3fr 1fr; + main > * { + width: 800px; + margin:auto; } - - - #left{ - grid-column: 1; - grid-row: 1; - - display: grid; - grid-template-columns: 1fr 800px; - grid-template-rows: 0fr 1fr 0fr; + .playlist-metadata{ + display:grid; + grid-template-columns: 0fr 1fr; } - .playlist-metadata{ + .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; - grid-row:1; - - 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; - } - .page-button-row{ - grid-row: 3; - grid-column: 2; - justify-self: center; + .playlist-stats{ + grid-row:3; + grid-column:2; + } + + .playlist-description{ + grid-row:4; + grid-column:2; + min-width:0px; + white-space: pre-line; } - - - #right{ - grid-column: 2; - grid-row: 1; - } #results{ - - grid-row: 2; - grid-column: 2; margin-top:10px; - + display: grid; grid-auto-rows: 0fr; grid-row-gap: 10px; @@ -76,27 +49,25 @@ {% endblock style %} {% block main %} - <div id="left"> - <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="playlist-stats"> - <div>{{ views }}</div> - <div>{{ size }}</div> - </div> - <div class="playlist-description">{{ common_elements.text_runs(description) }}</div> + <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="playlist-stats"> + <div>{{ views }}</div> + <div>{{ size }}</div> </div> + <div class="playlist-description">{{ common_elements.text_runs(description) }}</div> + </div> - <div id="results"> - {% 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> + <div id="results"> + {% 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 %} diff --git a/youtube/templates/post_comment.html b/youtube/templates/post_comment.html index 67c54f1..ba6a22c 100644 --- a/youtube/templates/post_comment.html +++ b/youtube/templates/post_comment.html @@ -3,27 +3,18 @@ {% import "comments.html" as comments %} {% block style %} - main{ - display: grid; - grid-template-columns: 3fr 2fr; - } - .left{ - display:grid; - grid-template-columns: 1fr 640px; - } - textarea{ - width: 460px; - height: 85px; - } .comment-form{ - grid-column:2; + width: 640px; + margin: auto; justify-content:start; } + textarea{ + width: 460px; + height: 85px; + } {% endblock style %} {% block main %} - <div class="left"> - {{ comments.comment_posting_box(comment_posting_box_info) }} - </div> + {{ comments.comment_posting_box(comment_posting_box_info) }} {% endblock %} diff --git a/youtube/templates/search.html b/youtube/templates/search.html index 782a85e..63f930e 100644 --- a/youtube/templates/search.html +++ b/youtube/templates/search.html @@ -3,31 +3,19 @@ {% extends "base.html" %} {% import "common_elements.html" as common_elements %} {% block style %} - main{ - display:grid; - grid-template-columns: minmax(0px, 1fr) 800px minmax(0px,2fr); - max-width:100vw; - } - - + main > * { + max-width: 800px; + margin: auto; + } + #result-info{ + } #number-of-results{ font-weight:bold; } - #result-info{ - grid-row: 1; - grid-column:2; - align-self:center; - } - .page-button-row{ - grid-column: 2; - justify-self: center; - } - - - .item-list{ - grid-row: 2; - grid-column: 2; - } + .item-list{ + padding-left: 10px; + padding-right: 10px; + } .badge{ background-color:#cccccc; } @@ -45,7 +33,7 @@ </div> <div class="item-list"> {% for info in results %} - {{ common_elements.item(info) }} + {{ common_elements.item(info, description=true) }} {% endfor %} </div> <nav class="page-button-row"> diff --git a/youtube/templates/watch.html b/youtube/templates/watch.html index 82c1a97..14e953b 100644 --- a/youtube/templates/watch.html +++ b/youtube/templates/watch.html @@ -3,96 +3,116 @@ {% import "common_elements.html" as common_elements %} {% import "comments.html" as comments %} {% block style %} - main{ - display:grid; - grid-template-columns: minmax(0px, 3fr) 640px 40px 500px minmax(0px,2fr); - background-color:#cccccc; - } + 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; + } + + {% if theater_mode %} + video{ + grid-column: 1 / span 5; + justify-self: center; + max-width: 100%; + width: {{ theater_video_target_width }}px; + max-height: {{ video_height }}px; + margin-bottom: 10px; + background-color: var(--background-color); + } + .related-videos-outer{ + grid-row: 2 /span 3; + width: 400px; + } + .video-info{ + width: 640px; + } + {% else %} + video{ + height: 360px; + width: 640px; + grid-column: 2; + } + .related-videos-outer{ + 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; + } - #left{ - background-color:#bcbcbc; + .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 > .is-unlisted{ + background-color: var(--interface-color); + justify-self:start; + padding-left:2px; + padding-right:2px; + } + .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 > .download-dropdown{ + grid-column:1 / span 2; + grid-row: 6; + } + .video-info > .checkbox{ + justify-self:end; + align-self: start; + grid-row: 5; + grid-column: 2; + } + .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; + } + + .music-list{ + grid-row:8; + grid-column: 1 / span 2; + background-color: var(--interface-color); } - .full-item{ - display: grid; - grid-column: 2; - grid-template-rows: 0fr 0fr 0fr 0fr 20px 0fr 0fr; - grid-template-columns: 1fr 1fr; - align-content: start; - background-color:#bcbcbc; - } - .full-item > video{ - grid-column: 1 / span 2; - grid-row: 1; - } - .full-item > .title{ - grid-column: 1 / span 2; - grid-row:2; - min-width: 0; - } - .full-item > .is-unlisted{ - background-color: #d0d0d0; - justify-self:start; - padding-left:2px; - padding-right:2px; - } - .full-item > address{ - grid-column: 1; - grid-row: 4; - justify-self: start; - } - .full-item > .views{ - grid-column: 2; - grid-row: 4; - justify-self:end; - } - .full-item > time{ - grid-column: 1; - grid-row: 5; - justify-self:start; - } - .full-item > .likes-dislikes{ - grid-column: 2; - grid-row: 5; - justify-self:end; - } - .full-item > .download-dropdown{ - grid-column:1; - grid-row: 6; - } - .full-item > .checkbox{ - justify-self:end; - - grid-row: 6; - grid-column: 2; - } - .full-item > .description{ - background-color:#d0d0d0; - margin-top:8px; - white-space: pre-wrap; - min-width: 0; - word-wrap: break-word; - grid-column: 1 / span 2; - grid-row: 7; - } - .full-item .music-list{ - grid-row:8; - grid-column: 1 / span 2; - } - - .full-item .comments-area{ - grid-column: 1 / span 2; - grid-row: 9; - margin-top:10px; - } - .comment{ - width:640px; - } - - .music-list{ - background-color: #d0d0d0; - } .music-list table,th,td{ border: 1px solid; } @@ -105,126 +125,161 @@ font-weight:bold; margin-bottom:5px; } + .comments-area-outer{ + grid-column: 2; + grid-row: 3; + margin-top:10px; + } + .comments-area-inner{ + padding-top: 10px; + } + .comment{ + width:640px; + } + .related-videos-outer{ + grid-column: 4; + max-width: 640px; + } + .related-videos-inner{ + padding-top: 10px; + display: grid; + grid-auto-rows: 94px; + grid-row-gap: 10px; + } - #related{ - grid-column: 4; - display: grid; - grid-auto-rows: 90px; - grid-row-gap: 10px; - } - #related .medium-item{ - grid-template-columns: 160px 1fr 0fr; - } + /* 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; + } + .related-videos-outer{ + margin-top: 10px; + grid-column: 2; + grid-row: 3; + width: initial; + } + .comments-area-outer{ + grid-row: 4; + } + } - .download-dropdown{ - z-index:1; - justify-self:start; - min-width:0px; - height:0px; + .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; } - - .download-dropdown-label{ - background-color: #e9e9e9; - border-style: outset; - border-width: 2px; - font-weight: bold; + .format-attributes li{ + white-space: nowrap; + max-height: 1.2em; } - - .download-dropdown-content{ - display:none; - background-color: #e9e9e9; + .format-ext{ + width: 60px; } - .download-dropdown:hover .download-dropdown-content { - display: grid; - grid-auto-rows:30px; - padding-bottom: 50px; + .format-res{ + width:90px; } - .download-dropdown-content a{ - white-space: nowrap; - display:grid; - grid-template-columns: 60px 90px auto; - max-height: 1.2em; - } {% endblock style %} {% block main %} - <div id="left"> - </div> - <article class="full-item"> - - <video width="640" height="360" controls autofocus> -{% 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 %} - - </video> - - <h2 class="title">{{ title }}</h2> -{% if unlisted %} - <span class="is-unlisted">Unlisted</span> -{% endif %} - <address>Uploaded by <a href="{{ uploader_channel_url }}">{{ uploader }}</a></address> - <span class="views">{{ views }} views</span> - - - <time datetime="$upload_date">Published on {{ upload_date }}</time> - <span class="likes-dislikes">{{ likes }} likes {{ dislikes }} dislikes</span> - <div class="download-dropdown"> - <button class="download-dropdown-label">Download</button> - <div class="download-dropdown-content"> -{% for format in download_formats %} - <a href="{{ format['url'] }}"> - <span>{{ format['ext'] }}</span> - <span>{{ format['resolution'] }}</span> - <span>{{ format['note'] }}</span> - </a> -{% endfor %} - </div> - </div> - <input class="checkbox" name="video_info_list" value="{{ video_info }}" form="playlist-edit" type="checkbox"> - - <span class="description">{{ description }}</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 %} - <tr> - {% for attribute in music_attributes %} - <td>{{ track.get(attribute.lower(), '') }}</td> - {% endfor %} - </tr> - {% endfor %} - </table> - {% endif %} - </div> + <video controls autofocus> + {% for video_source in video_sources %} + <source src="{{ video_source['src'] }}" type="{{ video_source['type'] }}"> + {% endfor %} - {% if comments_info %} - {{ comments.video_comments(comments_info) }} - {% endif %} - </article> + {% 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> + + <div class="video-info"> + <h2 class="title">{{ title }}</h2> + {% if unlisted %} + <span class="is-unlisted">Unlisted</span> + {% endif %} + <address>Uploaded by <a href="{{ uploader_channel_url }}">{{ uploader }}</a></address> + <span class="views">{{ views }} views</span> + <time datetime="$upload_date">Published on {{ upload_date }}</time> + <span class="likes-dislikes">{{ likes }} likes {{ dislikes }} dislikes</span> + <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-res">{{ format['resolution'] }}</li> + <li class="format-note">{{ format['note'] }}</li> + </ol> + </a> + </li> + {% endfor %} + </ul> + </details> + <input class="checkbox" name="video_info_list" value="{{ video_info }}" form="playlist-edit" type="checkbox"> - <nav id="related"> - {% for info in related %} - {{ common_elements.item(info) }} + <span class="description">{{ description }}</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 %} + <tr> + {% for attribute in music_attributes %} + <td>{{ track.get(attribute.lower(), '') }}</td> + {% endfor %} + </tr> {% endfor %} - </nav> + </table> + {% endif %} + </div> + </div> + + {% if related_videos_mode != 0 %} + <details class="related-videos-outer" {{'open' if related_videos_mode == 1 else ''}}> + <summary>Related Videos</summary> + <nav class="related-videos-inner"> + {% for info in related %} + {{ common_elements.item(info) }} + {% endfor %} + </nav> + </details> + {% endif %} + {% if comments_mode != 0 %} + <details class="comments-area-outer" {{'open' if comments_mode == 1 else ''}}> + <summary>Comments</summary> + <section class="comments-area-inner comments-area"> + {% if comments_info %} + {{ comments.video_comments(comments_info) }} + {% endif %} + </section> + </details> + {% endif %} {% endblock main %} diff --git a/youtube/watch.py b/youtube/watch.py index 5487dd4..8f83e48 100644 --- a/youtube/watch.py +++ b/youtube/watch.py @@ -36,7 +36,6 @@ def watch_page_related_video_info(item): except KeyError: result['views'] = '' result['thumbnail'] = util.get_thumbnail_url(item['id']) - result['item_size'] = 'small' result['type'] = 'video' return result @@ -47,19 +46,24 @@ def watch_page_related_playlist_info(item): 'id': item['list'], 'first_video_id': item['video_id'], 'thumbnail': util.get_thumbnail_url(item['video_id']), - 'item_size': 'small', 'type': 'playlist', } def get_video_sources(info): video_sources = [] for format in info['formats']: - if format['acodec'] != 'none' and format['vcodec'] != 'none': + if format['acodec'] != 'none' and format['vcodec'] != 'none' and format['height'] <= settings.default_resolution: video_sources.append({ 'src': format['url'], 'type': 'video/' + format['ext'], + 'height': format['height'], + 'width': format['width'], }) + #### order the videos sources so the preferred resolution is first ### + + video_sources.sort(key=lambda source: source['height'], reverse=True) + return video_sources def get_subtitle_sources(info): @@ -193,6 +197,12 @@ def get_watch_page(): 'note': yt_dl_downloader._format_note(format), }) + video_sources = get_video_sources(info) + video_height = video_sources[0]['height'] + + # 1 second per pixel, or the actual video width + theater_video_target_width = max(640, info['duration'], video_sources[0]['width']) + return flask.render_template('watch.html', header_playlist_names = local_playlist.get_playlist_names(), uploader_channel_url = '/' + info['uploader_url'], @@ -202,13 +212,20 @@ def get_watch_page(): dislikes = (lambda x: '{:,}'.format(x) if x is not None else "")(info.get("dislike_count", None)), download_formats = download_formats, video_info = json.dumps(video_info), - video_sources = get_video_sources(info), + video_sources = video_sources, subtitle_sources = get_subtitle_sources(info), related = related_videos, music_list = info['music_list'], music_attributes = get_ordered_music_list_attributes(info['music_list']), comments_info = comments_info, + theater_mode = settings.theater_mode, + related_videos_mode = settings.related_videos_mode, + comments_mode = settings.comments_mode, + + video_height = video_height, + theater_video_target_width = theater_video_target_width, + title = info['title'], uploader = info['uploader'], description = info['description'], diff --git a/youtube/yt_data_extract.py b/youtube/yt_data_extract.py index c236c2f..db28e62 100644 --- a/youtube/yt_data_extract.py +++ b/youtube/yt_data_extract.py @@ -197,10 +197,6 @@ def renderer_info(renderer, additional_info={}): info.update(additional_info) - if type.startswith('compact') or (type.startswith('playlist') and type != 'playlistRenderer'): - info['item_size'] = 'small' - else: - info['item_size'] = 'medium' if type in ('compactVideoRenderer', 'videoRenderer', 'playlistVideoRenderer', 'gridVideoRenderer'): info['type'] = 'video' |