aboutsummaryrefslogtreecommitdiffstats
path: root/youtube/templates
diff options
context:
space:
mode:
Diffstat (limited to 'youtube/templates')
-rw-r--r--youtube/templates/base.html113
-rw-r--r--youtube/templates/common_elements.html152
-rw-r--r--youtube/templates/error.html8
-rw-r--r--youtube/templates/playlist.html106
-rw-r--r--youtube/templates/search.html54
-rw-r--r--youtube/templates/watch.html209
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 (&lt; 4 minutes)</label>
+
+ <input type="radio" id="duration_long" name="duration" value="2">
+ <label for="duration_long">Long (&gt; 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 %}