]> git.gir.st - subscriptionfeed.git/blob - app/youtube/templates/watch.html.j2
proxy and fixup subtitles
[subscriptionfeed.git] / app / youtube / templates / watch.html.j2
1 <!DOCTYPE html>
2 <title>{{ title }} &mdash; {{ author }}</title>
3 <link rel="shortcut icon" href='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300" fill="%23fff"><circle r="150" cx="150" cy="150" fill="%23f00"/><path d="m120 97v106l93-53"/></svg>'>
4 <link rel="stylesheet" href="/static/style.css">
5
6 {% import 'macros.imp.j2' as macros with context %}
7
8 {{ macros.headerbar() }}
9
10 {% include 'messages.inc.j2' %}
11
12 <article>
13 {% if video_url %}
14 <div class="aspect-ratio main-video" style="--aspect-ratio:{{ aspectr }}">
15 <video controls poster="{{ poster }}">
16 <source src="{{ video_url }}">
17 {% set cc_default = False %}
18 {% for cc in subtitles %}
19 <track label="{{ cc.name }}" kind="subtitles" srclang="{{ cc.code }}" src="{{ url_for('youtube.timedtext') }}?{{ cc.query }}" {{ 'default' if cc_default and not loop.counter }}>
20 {% endfor %}
21 </video>
22
23 <script>
24 "use strict";
25 //todo: should only be done after user is made aware of video id getting sent to 3rd party (wait for k-anon)
26 //window.addEventListener("load", () => load_sponsorblock("{{ video_id }}"));
27 document.addEventListener('DOMContentLoaded', ()=>{ let check = document.querySelector("#skip_sponsors");
28 check.addEventListener("change", () => {if (check.checked) load_sponsorblock("{{ video_id }}")});});
29 function load_sponsorblock(video_id){
30 fetch(`https://sponsor.ajay.app/api/skipSegments?videoID=${video_id}`)
31 .then(response => response.json())
32 .then(data => {
33 for (const segment of data) {
34 const [start, stop] = segment.segment;
35 if (segment.category != "sponsor") continue;
36 document.querySelector('.main-video video')
37 .addEventListener("timeupdate", function() {
38 if (document.querySelector("#skip_sponsors").checked &&
39 this.currentTime >= start && this.currentTime < stop-1) {
40 this.currentTime = stop;
41 }
42 });
43 }
44 });
45 }
46 </script>
47
48 </div>
49 {% else %}{#TODO: this'll break livestreams #}
50 <img src="{{ poster }}" style="width:100%;object-fit:cover;height:calc(100% / {{ aspectr }});">
51 {% endif %}
52
53 {% if video_error %}
54 <div class="video_error">
55 {{ errdetails }} Watch on <a href="{{ invidious_url }}">Invidious</a> or <a href="https://www.youtube.com/watch?v={{ video_id }}">Youtube</a>
56 </div>
57 {% endif %}
58
59 <h1>{{ title | e }}<br>
60 <small><a href="/channel/{{ channel_id }}">{{ author | e }}</a></small></h1>
61
62 <details><summary>Description</summary>
63 <p style="white-space:pre-wrap">{{ description | e }}
64 <hr></details>
65
66 <details><summary>Metadata</summary>
67 <dl>
68 <dt>Duration
69 {% set h = length // (60*60) %}
70 {% set m = length // 60 % 60 %}
71 {% set s = length % 60 %}
72 <dd>{{ h~':' if h }}{{ '%02d' % m }}:{{ '%02d' % s }}
73 <dt>Views
74 <dd>{{ '{0:,}'.format(views | int)|replace(",","&hairsp;") }}
75 <dt>Published
76 <dd>{{ published.split('T')[0] }}
77 <dt>Rating
78 <dd>{{ rating | round(1) }}/5
79 <dt>Visibility
80 <dd>{{ 'unlisted' if unlisted else 'public' }}
81 {% if blacklisted|length == 0 %}
82 <dt>Available in
83 <dd>all regions
84 {% elif whitelisted|length == 0 %}
85 <dt>Blacklisted in
86 <dd>all regions
87 {% elif blacklisted|length > whitelisted|length %}
88 <dt>Available in
89 <dd>{{ whitelisted | join(', ') }}
90 {% else %}
91 <dt>Blacklisted in
92 <dd>{{ blacklisted | join(', ') }}
93 {% endif %}
94 </dl>
95 <hr></details>
96
97 <details><summary>More Actions</summary>
98 <ul>
99 <li><label><input type=checkbox id=skip_sponsors>skip sponsors</label>
100 {# TODO: make checked by default (need to inform the user about potential privacy concerns) #}
101 <noscript><br>Note: requires javascript</noscript>
102 {% if current_user.name == "girst" %}{# TODO: doesn't work if video not already crawled!#}
103 <li><form method=post action="/feed/subscriptions">
104 <input type="hidden" name="{{ 'un' if pinned }}pin" value="{{ post_id }}">
105 <input class="emoji" type=submit value="&#x1f4cc;&#xFE0F;{{ '&#8416;' if pinned }}" title="{{ 'unpin' if pinned else 'pin to top' }}">{#TODO: 'pinned' undefined!#}
106 </form>
107 {% endif %}
108 <li><a href="/watch?v={{ video_id }}&show=raw">show raw video</a>
109 <li><a href="/watch?v={{ video_id }}&show=json">view json metadata</a>
110 <li><a href="https://invidio.us/watch?v={{ video_id }}">watch on invidious</a>
111 <li><a href="https://youtu.be/{{ video_id }}">watch on youtube</a>
112 </ul>
113 <hr></details>
114
115 <!-- not implemented warning:--><div style="background:red">{%for x in (infocards+endcards)|selectattr('content.error')%}{{x.content.error}}{%endfor%}</div>
116 <details><summary>Info- and Endcards</summary>
117 <div class="cards">
118 {% for card in all_cards %}
119 {% set c = card.content %}
120 {% if card.type == 'VIDEO' %}
121 {% call macros.card(c.video_id, c.title, c.length) %}
122 <span class=channel>{{ c.author }}&nbsp;</span>
123 {% endcall %}
124 {% elif card.type == 'CHANNEL' and c.channel_id != channel_id %}
125 {% call macros.card_generic("/channel/"~c.channel_id, c.icons[250] if 'icons' in c else '', c.title) %}
126 <span class=channel>Channel</span>
127 <span class=advanced></span>
128 {% endcall %}
129 {% elif card.type == 'PLAYLIST' %}
130 {% call macros.card_generic("/playlist?list="~c.playlist_id, "https://i.ytimg.com/vi/"~c.video_id~"/mqdefault.jpg", c.title) %}
131 <span class=channel>{{ c.author }}</span>
132 <span class=advanced>{{ c.n_videos }} videos</span>
133 {% endcall %}
134 {% elif card.type == 'WEBSITE' %}
135 {% call macros.card_generic(c.url, c.icons[250] if 'icons' in c else '', c.title) %}
136 <span class=channel>{{ c.domain }}</span>
137 {% endcall %}
138 {% endif %}
139 {% endfor %}
140 </div>
141 <hr></details>
142 </article>
Imprint / Impressum