diff options
Diffstat (limited to 'youtube/templates')
-rw-r--r-- | youtube/templates/base.html | 114 | ||||
-rw-r--r-- | youtube/templates/channel.html | 160 | ||||
-rw-r--r-- | youtube/templates/comments.html | 70 | ||||
-rw-r--r-- | youtube/templates/comments_page.html | 65 | ||||
-rw-r--r-- | youtube/templates/common_elements.html | 154 | ||||
-rw-r--r-- | youtube/templates/delete_comment.html | 25 | ||||
-rw-r--r-- | youtube/templates/error.html | 7 | ||||
-rw-r--r-- | youtube/templates/local_playlist.html | 60 | ||||
-rw-r--r-- | youtube/templates/local_playlists_list.html | 16 | ||||
-rw-r--r-- | youtube/templates/login.html | 60 | ||||
-rw-r--r-- | youtube/templates/playlist.html | 106 | ||||
-rw-r--r-- | youtube/templates/post_comment.html | 29 | ||||
-rw-r--r-- | youtube/templates/search.html | 54 | ||||
-rw-r--r-- | youtube/templates/status.html | 7 | ||||
-rw-r--r-- | youtube/templates/watch.html | 230 |
15 files changed, 1157 insertions, 0 deletions
diff --git a/youtube/templates/base.html b/youtube/templates/base.html new file mode 100644 index 0000000..72e3691 --- /dev/null +++ b/youtube/templates/base.html @@ -0,0 +1,114 @@ +<!DOCTYPE html> +<html> + <head> + <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="/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"> + <link title="Youtube local" href="/youtube.com/opensearch.xml" rel="search" type="application/opensearchdescription+xml"> + <style type="text/css"> +{% block style %} +{{ style }} +{% endblock %} + </style> + </head> + <body> + <header> + <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> + <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> + + <input type="radio" id="duration_short" name="duration" value="1"> + <label for="duration_short">Short (< 4 minutes)</label> + + <input type="radio" id="duration_long" name="duration" value="2"> + <label for="duration_long">Long (> 20 minutes)</label> + + </div> + </div> + </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> + </header> + <main> +{% block main %} +{{ main }} +{% endblock %} + </main> + </body> +</html> diff --git a/youtube/templates/channel.html b/youtube/templates/channel.html new file mode 100644 index 0000000..069e33b --- /dev/null +++ b/youtube/templates/channel.html @@ -0,0 +1,160 @@ +{% set page_title = channel_name + ' - Channel' %} +{% 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; + } + main .channel-tabs{ + grid-row:2; + grid-column: 1 / span 2; + + display:grid; + grid-auto-flow: column; + justify-content:start; + + background-color: #aaaaaa; + 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; + background-color: #bababa; + margin-bottom: 10px; + } + #number-of-results{ + font-weight:bold; + } + .item-grid{ + padding-left: 20px; + grid-row:4; + grid-column: 1 / span 2; + } + .item-list{ + width:1000px; + grid-column: 1 / span 2; + } + .page-button-row{ + grid-column: 1 / span 2; + } + .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; + } +{% 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> + </div> + <nav class="channel-tabs"> + {% for tab_name in ('Videos', 'Playlists', 'About') %} + {% if tab_name.lower() == current_tab %} + <a class="tab page-button">{{ tab_name }}</a> + {% else %} + <a class="tab page-button" href="{{ channel_url + '/' + tab_name.lower() }}">{{ tab_name }}</a> + {% endif %} + {% endfor %} + + <form class="channel-search" action="{{ channel_url + '/search' }}"> + <input type="search" name="query" class="search-box" value="{{ search_box_value }}"> + <button type="submit" value="Search" class="search-button">Search</button> + </form> + </nav> + {% if current_tab == 'about' %} + <div class="channel-info"> + <ul> + {% for stat in stats %} + <li>{{ stat }}</li> + {% endfor %} + </ul> + <hr> + <h3>Description</h3> + <div class="description">{{ common_elements.text_runs(description) }}</div> + <hr> + <ul> + {% for text, url in links %} + <li><a href="{{ url }}">{{ text }}</a></li> + {% endfor %} + </ul> + </div> + {% else %} + <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> + {% elif current_tab == 'playlists' %} + {% set sorts = [('2', 'oldest'), ('3', 'newest'), ('4', 'last video added')] %} + {% else %} + {% set sorts = [] %} + {% endif %} + + {% for sort_number, sort_name in sorts %} + {% if sort_number == current_sort.__str__() %} + <a class="sort-button">{{ 'Sorted by ' + sort_name }}</a> + {% else %} + <a class="sort-button" href="{{ channel_url + '/' + current_tab + '?sort=' + sort_number }}">{{ 'Sort by ' + sort_name }}</a> + {% endif %} + {% endfor %} + </div> + + {% if current_tab != 'about' %} + <nav class="{{ 'item-list' if current_tab == 'search' else 'item-grid' }}"> + {% for item_info in items %} + {{ common_elements.item(item_info, include_author=false) }} + {% endfor %} + </nav> + + {% if current_tab != 'playlists' %} + <nav class="page-button-row"> + {{ common_elements.page_buttons(number_of_pages, channel_url + '/' + current_tab, parameters_dictionary) }} + </nav> + {% endif %} + {% endif %} + + {% endif %} +{% endblock main %} diff --git a/youtube/templates/comments.html b/youtube/templates/comments.html new file mode 100644 index 0000000..82276b8 --- /dev/null +++ b/youtube/templates/comments.html @@ -0,0 +1,70 @@ +{% import "common_elements.html" as common_elements %} + +{% macro render_comment(comment, include_avatar) %} + <div class="comment-container"> + <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'] }}"> + {% endif %} + </a> + <address> + <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['published'] }}</time> + </a> + <span class="text">{{ common_elements.text_runs(comment['text']) }}</span> + + <span class="likes">{{ comment['likes_text'] if comment['likes'] else ''}}</span> + <div class="bottom-row"> + <a href="{{ comment['replies_url'] }}" class="replies">{{ comment['view_replies_text'] }}</a> + {% if 'delete_url' is in comment %} + <a href="{{ comment['delete_url'] }}" target="_blank">Delete</a> + {% endif %} + </div> + </div> + + </div> +{% 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> +{% endmacro %} + +{% macro comment_posting_box(info) %} + <form action="{{ info['form_action'] }}" method="post" class="comment-form"> + <div id="comment-account-options"> + <label for="account-selection">Account:</label> + <select id="account-selection" name="channel_id"> + {% for account in info['accounts'] %} + <option value="{{ account[0] }}">{{ account[1] }}</option> + {% endfor %} + </select> + <a href="/https://youtube.com/login" target="_blank">Add account</a> + </div> + <textarea name="comment_text"></textarea> + {% if info['include_video_id_input'] %} + <input type="hidden" name="video_id" value="{{ info['video_id'] }}"> + {% endif %} + <button type="submit" class="post-comment-button">{{ 'Post reply' if info['replying'] else 'Post comment' }}</button> + </form> +{% endmacro %} + + + + diff --git a/youtube/templates/comments_page.html b/youtube/templates/comments_page.html new file mode 100644 index 0000000..68c8537 --- /dev/null +++ b/youtube/templates/comments_page.html @@ -0,0 +1,65 @@ +{% set page_title = ('Replies' if comments_info['is_replies'] else 'Comments page ' + comments_info['page_number']) %} +{% extends "base.html" %} +{% import "comments.html" as comments %} + +{% block style %} + main{ + display:grid; + grid-template-columns: 3fr 2fr; + } + #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']) }} + {% 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> +{% endblock main %} + + diff --git a/youtube/templates/common_elements.html b/youtube/templates/common_elements.html new file mode 100644 index 0000000..49e2fad --- /dev/null +++ b/youtube/templates/common_elements.html @@ -0,0 +1,154 @@ +{% macro text_runs(runs) %} + {%- if runs[0] is mapping -%} + {%- for text_run in runs -%} + {%- if text_run.get("bold", false) -%} + <b>{{ text_run["text"] }}</b> + {%- elif text_run.get('italics', false) -%} + <i>{{ text_run["text"] }}</i> + {%- else -%} + {{ text_run["text"] }} + {%- endif -%} + {%- endfor -%} + {%- else -%} + {{ runs }} + {%- 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> + </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> + + <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 %} + </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() %} + {% if current_page is le(5) %} + {% set page_start = 1 %} + {% set page_end = [9, estimated_pages]|min %} + {% else %} + {% set page_start = current_page - 4 %} + {% set page_end = [current_page + 4, estimated_pages]|min %} + {% endif %} + + {% for page in range(page_start, page_end+1) %} + {% if page == current_page %} + <div class="page-button">{{ 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> + {% endif %} + {% endfor %} + +{% endmacro %} diff --git a/youtube/templates/delete_comment.html b/youtube/templates/delete_comment.html new file mode 100644 index 0000000..71555ee --- /dev/null +++ b/youtube/templates/delete_comment.html @@ -0,0 +1,25 @@ +{% set page_title = 'Delete comment?' %} +{% 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-top:20px; + grid-column:2; + } +{% endblock style %} + +{% block main %} + <div>Are you sure you want to delete this comment?</div> + <form action="" method="POST"> + {% for parameter_name, parameter_value in parameters %} + <input type="hidden" name="{{ parameter_name }}" value="{{ parameter_value }}"> + {% endfor %} + <input type="submit" value="Yes, delete it"> + </form> +{% endblock %} + diff --git a/youtube/templates/error.html b/youtube/templates/error.html new file mode 100644 index 0000000..e77c92c --- /dev/null +++ b/youtube/templates/error.html @@ -0,0 +1,7 @@ +{% set page_title = 'Error' %} +{% extends "base.html" %} + +{% block main %} + {{ error_message }} +{% endblock %} + diff --git a/youtube/templates/local_playlist.html b/youtube/templates/local_playlist.html new file mode 100644 index 0000000..f8e6f01 --- /dev/null +++ b/youtube/templates/local_playlist.html @@ -0,0 +1,60 @@ +{% set page_title = playlist_name + ' - Local playlist' %} +{% extends "base.html" %} +{% import "common_elements.html" as common_elements %} +{% block style %} + main{ + display:grid; + grid-template-columns: 3fr 1fr; + } + + + + #left{ + grid-column: 1; + grid-row: 1; + + display: grid; + grid-template-columns: 1fr 800px auto; + grid-template-rows: 0fr 1fr 0fr; + } + .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"> + <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> +{% endblock main %} diff --git a/youtube/templates/local_playlists_list.html b/youtube/templates/local_playlists_list.html new file mode 100644 index 0000000..9b5f510 --- /dev/null +++ b/youtube/templates/local_playlists_list.html @@ -0,0 +1,16 @@ +{% set page_title = 'Local playlists' %} +{% extends "base.html" %} + +{% block main %} + <ul> + {% for playlist_name, playlist_url in playlists %} + <li><a href="{{ playlist_url }}">{{ playlist_name }}</a></li> + {% endfor %} + </ul> +{% endblock main %} + + + + + + diff --git a/youtube/templates/login.html b/youtube/templates/login.html new file mode 100644 index 0000000..0f09a62 --- /dev/null +++ b/youtube/templates/login.html @@ -0,0 +1,60 @@ +{% set page_title = 'Login' %} +{% 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 form{ + margin-top:20px; + grid-column:2; + display:grid; + justify-items: start; + align-content: start; + grid-row-gap: 10px; + } + + #username, #password{ + grid-column:2; + width: 250px; + } + #add-account-button{ + margin-top:20px; + } + #tor-note{ + grid-row:2; + grid-column:2; + background-color: #dddddd; + padding: 10px; + } +{% endblock style %} + +{% block main %} + <form action="" method="POST"> + <div class="form-field"> + <label for="username">Username:</label> + <input type="text" id="username" name="username"> + </div> + <div class="form-field"> + <label for="password">Password:</label> + <input type="password" id="password" name="password"> + </div> + <div id="save-account-checkbox"> + <input type="checkbox" id="save-account" name="save" checked> + <label for="save-account">Save account info to disk (password will not be saved, only the login cookie)</label> + </div> + <div> + <input type="checkbox" id="use-tor" name="use_tor"> + <label for="use-tor">Use Tor when logging in (WARNING: This will lock your Google account under normal circumstances, see note below)</label> + </div> + <input type="submit" value="Add account" id="add-account-button"> + </form> + <div id="tor-note"><b>Note on using Tor to log in</b><br> +Using Tor to log in should only be done if the account was created using a proxy/VPN/Tor to begin with and hasn't been logged in using your IP. Otherwise, it's pointless since Google already knows who the account belongs to. When logging into a google account, it must be logged in using an IP address geographically close to the area where the account was created or where it is logged into regularly. If the account was created using an IP address in America and is logged into from an IP in Russia, Google will block the Russian IP from logging in, assume someone knows your password, lock the account, and make you change your password. If creating an account using Tor, you must remember the IP (or geographic region) it was created in, and only log in using that geographic region for the exit node. This can be accomplished by <a href="https://tor.stackexchange.com/questions/733/can-i-exit-from-a-specific-country-or-node">putting the desired IP in the torrc file</a> to force Tor to use that exit node. Using the login cookie to post comments through Tor is perfectly safe, however. + </div> +{% endblock main %} + diff --git a/youtube/templates/playlist.html b/youtube/templates/playlist.html new file mode 100644 index 0000000..371b51b --- /dev/null +++ b/youtube/templates/playlist.html @@ -0,0 +1,106 @@ +{% set page_title = title + ' - Page ' + parameters_dictionary.get('page', '1') %} +{% extends "base.html" %} +{% import "common_elements.html" as common_elements %} +{% block style %} + main{ + display:grid; + grid-template-columns: 3fr 1fr; + } + + + + #left{ + grid-column: 1; + grid-row: 1; + + display: grid; + grid-template-columns: 1fr 800px; + grid-template-rows: 0fr 1fr 0fr; + } + .playlist-metadata{ + 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; + } + + + #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; + + } +{% 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> + + <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> +{% endblock main %} + + + + + + diff --git a/youtube/templates/post_comment.html b/youtube/templates/post_comment.html new file mode 100644 index 0000000..67c54f1 --- /dev/null +++ b/youtube/templates/post_comment.html @@ -0,0 +1,29 @@ +{% set page_title = 'Post reply' if replying else 'Post comment' %} +{% extends "base.html" %} +{% 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; + justify-content:start; + } +{% endblock style %} + +{% block main %} + <div class="left"> + {{ comments.comment_posting_box(comment_posting_box_info) }} + </div> +{% endblock %} + diff --git a/youtube/templates/search.html b/youtube/templates/search.html new file mode 100644 index 0000000..782a85e --- /dev/null +++ b/youtube/templates/search.html @@ -0,0 +1,54 @@ +{% set search_box_value = query %} +{% set page_title = query + ' - Search' %} +{% 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; + } + + + #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; + } + .badge{ + background-color:#cccccc; + } +{% 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>{{ corrections['corrected_query']|safe }}</a></div> + <div>Search instead for <a href="{{ corrections['original_query_url'] }}">{{ corrections['original_query'] }}</a></div> +{% elif corrections['type'] == 'did_you_mean' %} + <div>Did you mean <a href="{{ corrections['corrected_query_url'] }}">{{ corrections['corrected_query']|safe }}</a></div> +{% endif %} + </div> + <div class="item-list"> + {% for info in results %} + {{ common_elements.item(info) }} + {% endfor %} + </div> + <nav class="page-button-row"> + {{ common_elements.page_buttons(estimated_pages, '/https://www.youtube.com/search', parameters_dictionary) }} + </nav> +{% endblock main %} diff --git a/youtube/templates/status.html b/youtube/templates/status.html new file mode 100644 index 0000000..901aa5b --- /dev/null +++ b/youtube/templates/status.html @@ -0,0 +1,7 @@ +{% set page_title = (title if (title is defined) else 'Status') %} +{% extends "base.html" %} + +{% block main %} + {{ message }} +{% endblock %} + diff --git a/youtube/templates/watch.html b/youtube/templates/watch.html new file mode 100644 index 0000000..82c1a97 --- /dev/null +++ b/youtube/templates/watch.html @@ -0,0 +1,230 @@ +{% set page_title = title %} +{% extends "base.html" %} +{% 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; + } + + #left{ + background-color:#bcbcbc; + grid-column: 1; + + } + .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; + } + .music-list th,td{ + padding-left:4px; + padding-right:5px; + } + .music-list caption{ + text-align:left; + font-weight:bold; + margin-bottom:5px; + } + + #related{ + grid-column: 4; + display: grid; + grid-auto-rows: 90px; + grid-row-gap: 10px; + } + #related .medium-item{ + grid-template-columns: 160px 1fr 0fr; + } + + .download-dropdown{ + z-index:1; + justify-self:start; + min-width:0px; + height:0px; + } + + .download-dropdown-label{ + background-color: #e9e9e9; + border-style: outset; + border-width: 2px; + font-weight: bold; + } + + .download-dropdown-content{ + display:none; + background-color: #e9e9e9; + } + .download-dropdown:hover .download-dropdown-content { + display: grid; + grid-auto-rows:30px; + padding-bottom: 50px; + } + .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> + + {% if comments_info %} + {{ comments.video_comments(comments_info) }} + {% endif %} + </article> + + + + + <nav id="related"> + {% for info in related %} + {{ common_elements.item(info) }} + {% endfor %} + </nav> + +{% endblock main %} |