diff options
Diffstat (limited to 'youtube/templates')
-rw-r--r-- | youtube/templates/base.html | 113 | ||||
-rw-r--r-- | youtube/templates/common_elements.html | 152 | ||||
-rw-r--r-- | youtube/templates/error.html | 8 | ||||
-rw-r--r-- | youtube/templates/playlist.html | 106 | ||||
-rw-r--r-- | youtube/templates/search.html | 54 | ||||
-rw-r--r-- | youtube/templates/watch.html | 209 |
6 files changed, 642 insertions, 0 deletions
diff --git a/youtube/templates/base.html b/youtube/templates/base.html new file mode 100644 index 0000000..eafd369 --- /dev/null +++ b/youtube/templates/base.html @@ -0,0 +1,113 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>{% block page_title %}{{ title }}{% endblock %}</title> + <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/common_elements.html b/youtube/templates/common_elements.html new file mode 100644 index 0000000..b140332 --- /dev/null +++ b/youtube/templates/common_elements.html @@ -0,0 +1,152 @@ +{% 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) %} + <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) %} + {% 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 %} + {% 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) %} + <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) }} + </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) }} + </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'] }}</span> + <span>{{ info['size'] }}</span> + + <span class="description">{{ text_runs(info['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) %} + {% if info['item_size'] == 'small' %} + {{ small_item(info) }} + {% elif info['item_size'] == 'medium' %} + {{ medium_item(info) }} + {% 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/error.html b/youtube/templates/error.html new file mode 100644 index 0000000..1f33c44 --- /dev/null +++ b/youtube/templates/error.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} + +{% block page_title %}Error{% endblock %} + +{% block main %} + {{ error_message }} +{% endblock %} + diff --git a/youtube/templates/playlist.html b/youtube/templates/playlist.html new file mode 100644 index 0000000..09e382b --- /dev/null +++ b/youtube/templates/playlist.html @@ -0,0 +1,106 @@ +{% extends "base.html" %} +{% block page_title %}{{ title + ' - Page ' + parameters_dictionary.get('page', '1') }}{% endblock %} +{% 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">{{ 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/search.html b/youtube/templates/search.html new file mode 100644 index 0000000..1086cfd --- /dev/null +++ b/youtube/templates/search.html @@ -0,0 +1,54 @@ +{% set search_box_value = query %} +{% extends "base.html" %} +{% block page_title %}{{ query + ' - Search' }}{% endblock %} +{% 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/watch.html b/youtube/templates/watch.html new file mode 100644 index 0000000..122958c --- /dev/null +++ b/youtube/templates/watch.html @@ -0,0 +1,209 @@ +{% extends "base.html" %} +{% import "common_elements.html" as common_elements %} +{% block page_title %}{{ title }}{% endblock %} +{% 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"> +{{ music_list|safe }} + </div> +{{ comments|safe }} + </article> + + + + + <nav id="related"> + {% for info in related %} + {{ common_elements.item(info) }} + {% endfor %} + </nav> + +{% endblock main %} |