forked from midou/invidious
9b75f79fb5
This change is required to make the overlay buttons functional (add to and delete from playlist, mark as watched, etc.)
356 lines
17 KiB
Plaintext
356 lines
17 KiB
Plaintext
<% ucid = video.ucid %>
|
|
<% title = HTML.escape(video.title) %>
|
|
<% author = HTML.escape(video.author) %>
|
|
|
|
|
|
<% content_for "header" do %>
|
|
<meta name="thumbnail" content="<%= thumbnail %>">
|
|
<meta name="description" content="<%= HTML.escape(video.short_description) %>">
|
|
<meta name="keywords" content="<%= video.keywords.join(",") %>">
|
|
<meta property="og:site_name" content="<%= author %> | Invidious">
|
|
<meta property="og:url" content="<%= HOST_URL %>/watch?v=<%= video.id %>">
|
|
<meta property="og:title" content="<%= title %>">
|
|
<meta property="og:image" content="/vi/<%= video.id %>/maxres.jpg">
|
|
<meta property="og:description" content="<%= HTML.escape(video.short_description) %>">
|
|
<meta property="og:type" content="video.other">
|
|
<meta property="og:video:url" content="<%= HOST_URL %>/embed/<%= video.id %>">
|
|
<meta property="og:video:secure_url" content="<%= HOST_URL %>/embed/<%= video.id %>">
|
|
<meta property="og:video:type" content="text/html">
|
|
<meta property="og:video:width" content="1280">
|
|
<meta property="og:video:height" content="720">
|
|
<meta name="twitter:card" content="player">
|
|
<meta name="twitter:url" content="<%= HOST_URL %>/watch?v=<%= video.id %>">
|
|
<meta name="twitter:title" content="<%= title %>">
|
|
<meta name="twitter:description" content="<%= HTML.escape(video.short_description) %>">
|
|
<meta name="twitter:image" content="<%= HOST_URL %>/vi/<%= video.id %>/maxres.jpg">
|
|
<meta name="twitter:player" content="<%= HOST_URL %>/embed/<%= video.id %>">
|
|
<meta name="twitter:player:width" content="1280">
|
|
<meta name="twitter:player:height" content="720">
|
|
<link rel="alternate" href="https://www.youtube.com/watch?v=<%= video.id %>">
|
|
<%= rendered "components/player_sources" %>
|
|
<title><%= title %> - Invidious</title>
|
|
|
|
<!-- Description expansion also updates the 'Show more' button to 'Show less' so
|
|
we're going to need to do it here in order to allow for translations.
|
|
-->
|
|
<style>
|
|
#descexpansionbutton ~ label > a::after {
|
|
content: "<%= translate(locale, "Show more") %>"
|
|
}
|
|
|
|
#descexpansionbutton:checked ~ label > a::after {
|
|
content: "<%= translate(locale, "Show less") %>"
|
|
}
|
|
</style>
|
|
<% end %>
|
|
|
|
<script id="video_data" type="application/json">
|
|
<%=
|
|
{
|
|
"id" => video.id,
|
|
"index" => continuation,
|
|
"plid" => plid,
|
|
"length_seconds" => video.length_seconds.to_f,
|
|
"play_next" => !video.related_videos.empty? && !plid && params.continue,
|
|
"next_video" => video.related_videos.select { |rv| rv["id"]? }[0]?.try &.["id"],
|
|
"youtube_comments_text" => HTML.escape(translate(locale, "View YouTube comments")),
|
|
"reddit_comments_text" => HTML.escape(translate(locale, "View Reddit comments")),
|
|
"reddit_permalink_text" => HTML.escape(translate(locale, "View more comments on Reddit")),
|
|
"comments_text" => HTML.escape(translate(locale, "View `x` comments", "{commentCount}")),
|
|
"hide_replies_text" => HTML.escape(translate(locale, "Hide replies")),
|
|
"show_replies_text" => HTML.escape(translate(locale, "Show replies")),
|
|
"params" => params,
|
|
"preferences" => preferences,
|
|
"premiere_timestamp" => video.premiere_timestamp.try &.to_unix,
|
|
"vr" => video.is_vr,
|
|
"projection_type" => video.projection_type,
|
|
"local_disabled" => CONFIG.disabled?("local")
|
|
}.to_pretty_json
|
|
%>
|
|
</script>
|
|
|
|
<div id="player-container" class="h-box">
|
|
<%= rendered "components/player" %>
|
|
</div>
|
|
|
|
<div class="h-box">
|
|
<h1>
|
|
<%= title %>
|
|
<% if params.listen %>
|
|
<a title="<%=translate(locale, "Video mode")%>" href="/watch?<%= env.params.query %>&listen=0">
|
|
<i class="icon ion-ios-videocam"></i>
|
|
</a>
|
|
<% else %>
|
|
<a title="<%=translate(locale, "Audio mode")%>" href="/watch?<%= env.params.query %>&listen=1">
|
|
<i class="icon ion-md-headset"></i>
|
|
</a>
|
|
<% end %>
|
|
</h1>
|
|
|
|
<% if !video.is_listed %>
|
|
<h3>
|
|
<i class="icon ion-ios-unlock"></i> <%= translate(locale, "Unlisted") %>
|
|
</h3>
|
|
<% end %>
|
|
|
|
<% if video.reason %>
|
|
<h3>
|
|
<%= video.reason %>
|
|
</h3>
|
|
<% elsif video.premiere_timestamp.try &.> Time.utc %>
|
|
<h3>
|
|
<%= video.premiere_timestamp.try { |t| translate(locale, "Premieres in `x`", recode_date((t - Time.utc).ago, locale)) } %>
|
|
</h3>
|
|
<% elsif video.live_now %>
|
|
<h3>
|
|
<%= video.premiere_timestamp.try { |t| translate(locale, "videoinfo_started_streaming_x_ago", recode_date((Time.utc - t).ago, locale)) } %>
|
|
</h3>
|
|
<% end %>
|
|
</div>
|
|
|
|
<div class="pure-g">
|
|
<div class="pure-u-1 pure-u-lg-1-5">
|
|
<div class="h-box">
|
|
<span id="watch-on-youtube">
|
|
<a href="https://www.youtube.com/watch?v=<%= video.id %>"><%= translate(locale, "videoinfo_watch_on_youTube") %></a>
|
|
(<a href="https://www.youtube.com/embed/<%= video.id %>"><%= translate(locale, "videoinfo_youTube_embed_link") %></a>)
|
|
</span>
|
|
<p id="watch-on-another-invidious-instance">
|
|
<% if env.get("preferences").as(Preferences).automatic_instance_redirect%>
|
|
<a href="/redirect?referer=<%= env.get?("current_page") %>"><%= translate(locale, "Switch Invidious Instance") %></a>
|
|
<% else %>
|
|
<a href="https://redirect.invidious.io<%= env.request.resource %>"><%= translate(locale, "Switch Invidious Instance") %></a>
|
|
<% end %>
|
|
</p>
|
|
<p id="embed-link">
|
|
<a href="<%= embed_link %>"><%= translate(locale, "videoinfo_invidious_embed_link") %></a>
|
|
</p>
|
|
<p id="annotations">
|
|
<% if params.annotations %>
|
|
<a href="/watch?<%= env.params.query %>&iv_load_policy=3">
|
|
<%= translate(locale, "Hide annotations") %>
|
|
</a>
|
|
<% else %>
|
|
<a href="/watch?<%= env.params.query %>&iv_load_policy=1">
|
|
<%=translate(locale, "Show annotations")%>
|
|
</a>
|
|
<% end %>
|
|
</p>
|
|
|
|
<% if user %>
|
|
<% playlists = Invidious::Database::Playlists.select_user_created_playlists(user.email) %>
|
|
<% if !playlists.empty? %>
|
|
<form data-onsubmit="return_false" class="pure-form pure-form-stacked" action="/playlist_ajax" method="post" target="_blank">
|
|
<div class="pure-control-group">
|
|
<label for="playlist_id"><%= translate(locale, "Add to playlist: ") %></label>
|
|
<select style="width:100%" name="playlist_id" id="playlist_id">
|
|
<% playlists.each do |plid, playlist_title| %>
|
|
<option data-plid="<%= plid %>" value="<%= plid %>"><%= HTML.escape(playlist_title) %></option>
|
|
<% end %>
|
|
</select>
|
|
</div>
|
|
|
|
<input type="hidden" name="csrf_token" value="<%= URI.encode_www_form(env.get?("csrf_token").try &.as(String) || "") %>">
|
|
<input type="hidden" name="action_add_video" value="1">
|
|
<input type="hidden" name="video_id" value="<%= video.id %>">
|
|
<button data-onclick="add_playlist_video" data-id="<%= video.id %>" type="submit" class="pure-button pure-button-primary">
|
|
<b><%= translate(locale, "Add to playlist") %></b>
|
|
</button>
|
|
</form>
|
|
<script id="playlist_data" type="application/json">
|
|
<%=
|
|
{
|
|
"csrf_token" => URI.encode_www_form(env.get?("csrf_token").try &.as(String) || "")
|
|
}.to_pretty_json
|
|
%>
|
|
</script>
|
|
<script src="/js/playlist_widget.js?v=<%= Time.utc.to_unix_ms %>"></script>
|
|
<% end %>
|
|
<% end %>
|
|
|
|
<%= Invidious::Frontend::WatchPage.download_widget(locale, video, video_assets) %>
|
|
|
|
<p id="views"><i class="icon ion-ios-eye"></i> <%= number_with_separator(video.views) %></p>
|
|
<p id="likes"><i class="icon ion-ios-thumbs-up"></i> <%= number_with_separator(video.likes) %></p>
|
|
<p id="dislikes" style="display: none; visibility: hidden;"></p>
|
|
<p id="genre"><%= translate(locale, "Genre: ") %>
|
|
<% if !video.genre_url %>
|
|
<%= video.genre %>
|
|
<% else %>
|
|
<a href="<%= video.genre_url %>"><%= video.genre %></a>
|
|
<% end %>
|
|
</p>
|
|
<% if video.license %>
|
|
<% if video.license.empty? %>
|
|
<p id="license"><%= translate(locale, "License: ") %><%= translate(locale, "Standard YouTube license") %></p>
|
|
<% else %>
|
|
<p id="license"><%= translate(locale, "License: ") %><%= video.license %></p>
|
|
<% end %>
|
|
<% end %>
|
|
<p id="family_friendly"><%= translate(locale, "Family friendly? ") %><%= translate_bool(locale, video.is_family_friendly) %></p>
|
|
<p id="wilson" style="display: none; visibility: hidden;"></p>
|
|
<p id="rating" style="display: none; visibility: hidden;"></p>
|
|
<p id="engagement" style="display: none; visibility: hidden;"></p>
|
|
<% if video.allowed_regions.size != REGIONS.size %>
|
|
<p id="allowed_regions">
|
|
<% if video.allowed_regions.size < REGIONS.size // 2 %>
|
|
<%= translate(locale, "Whitelisted regions: ") %><%= video.allowed_regions.join(", ") %>
|
|
<% else %>
|
|
<%= translate(locale, "Blacklisted regions: ") %><%= (REGIONS.to_a - video.allowed_regions).join(", ") %>
|
|
<% end %>
|
|
</p>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pure-u-1 <% if params.related_videos || plid %>pure-u-lg-3-5<% else %>pure-u-md-4-5<% end %>">
|
|
|
|
<div class="pure-g h-box flexible title">
|
|
<div class="pure-u-1-2 flex-left flexible">
|
|
<a href="/channel/<%= video.ucid %>">
|
|
<div class="channel-profile">
|
|
<% if !video.author_thumbnail.empty? %>
|
|
<img src="/ggpht<%= URI.parse(video.author_thumbnail).request_target %>" alt="" />
|
|
<% end %>
|
|
<span id="channel-name"><%= author %><% if !video.author_verified.nil? && video.author_verified %> <i class="icon ion ion-md-checkmark-circle"></i><% end %></span>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<div class="pure-u-1-2 flex-right flexible button-container">
|
|
<div class="pure-u">
|
|
<% sub_count_text = video.sub_count_text %>
|
|
<%= rendered "components/subscribe_widget" %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="h-box">
|
|
<p id="published-date">
|
|
<% if video.premiere_timestamp.try &.> Time.utc %>
|
|
<b><%= video.premiere_timestamp.try { |t| translate(locale, "Premieres `x`", t.to_s("%B %-d, %R UTC")) } %></b>
|
|
<% else %>
|
|
<b><%= translate(locale, "Shared `x`", video.published.to_s("%B %-d, %Y")) %></b>
|
|
<% end %>
|
|
</p>
|
|
|
|
<div id="description-box"> <!-- Description -->
|
|
<% if video.description.size < 200 || params.extend_desc %>
|
|
<div id="descriptionWrapper"><%= video.description_html %></div>
|
|
<% else %>
|
|
<input id="descexpansionbutton" type="checkbox"/>
|
|
<div id="descriptionWrapper"><%= video.description_html %></div>
|
|
<label for="descexpansionbutton">
|
|
<a></a>
|
|
</label>
|
|
<% end %>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<% if !video.music.empty? %>
|
|
<input id="music-desc-expansion" type="checkbox"/>
|
|
<label for="music-desc-expansion">
|
|
<h3 id="music-description-title">
|
|
<%= translate(locale, "Music in this video") %>
|
|
<span class="icon ion-ios-arrow-up"></span>
|
|
<span class="icon ion-ios-arrow-down"></span>
|
|
</h3>
|
|
</label>
|
|
|
|
<div id="music-description-box">
|
|
<% video.music.each do |music| %>
|
|
<div class="music-item">
|
|
<p class="music-song"><%= translate(locale, "Song: ") %><%= music.song %></p>
|
|
<p class="music-artist"><%= translate(locale, "Artist: ") %><%= music.artist %></p>
|
|
<p class="music-album"><%= translate(locale, "Album: ") %><%= music.album %></p>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
<hr>
|
|
|
|
<% end %>
|
|
<div id="comments">
|
|
<% if nojs %>
|
|
<%= comment_html %>
|
|
<% else %>
|
|
<noscript>
|
|
<a href="/watch?<%= env.params.query %>&nojs=1">
|
|
<%= translate(locale, "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.") %>
|
|
</a>
|
|
</noscript>
|
|
<% end %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<% if params.related_videos || plid %>
|
|
<div class="pure-u-1 pure-u-lg-1-5">
|
|
<% if plid %>
|
|
<div id="playlist" class="h-box"></div>
|
|
<% end %>
|
|
|
|
<% if params.related_videos %>
|
|
<div class="h-box">
|
|
<% if !video.related_videos.empty? %>
|
|
<div <% if plid %>style="display:none"<% end %>>
|
|
<div class="pure-control-group">
|
|
<label for="continue"><%= translate(locale, "preferences_continue_label") %></label>
|
|
<input name="continue" id="continue" type="checkbox" <% if params.continue %>checked<% end %>>
|
|
</div>
|
|
<hr>
|
|
</div>
|
|
<% end %>
|
|
|
|
<% video.related_videos.each do |rv| %>
|
|
<% if rv["id"]? %>
|
|
<div class="pure-u-1">
|
|
|
|
<div class="thumbnail">
|
|
<%- if !env.get("preferences").as(Preferences).thin_mode -%>
|
|
<a tabindex="-1" href="/watch?v=<%= rv["id"] %>&listen=<%= params.listen %>">
|
|
<img loading="lazy" class="thumbnail" src="/vi/<%= rv["id"] %>/mqdefault.jpg" alt="" />
|
|
</a>
|
|
<%- else -%>
|
|
<div class="thumbnail-placeholder"></div>
|
|
<%- end -%>
|
|
|
|
<div class="bottom-right-overlay">
|
|
<%- if (length_seconds = rv["length_seconds"]?.try &.to_i?) && length_seconds != 0 -%>
|
|
<p class="length"><%= recode_length_seconds(length_seconds) %></p>
|
|
<%- end -%>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="video-card-row">
|
|
<a href="/watch?v=<%= rv["id"] %>&listen=<%= params.listen %>"><p dir="auto"><%= HTML.escape(rv["title"]) %></p></a>
|
|
</div>
|
|
|
|
<h5 class="pure-g">
|
|
<div class="pure-u-14-24">
|
|
<% if rv["ucid"]? %>
|
|
<b style="width:100%"><a href="/channel/<%= rv["ucid"] %>"><%= rv["author"]? %><% if rv["author_verified"]? == "true" %> <i class="icon ion ion-md-checkmark-circle"></i><% end %></a></b>
|
|
<% else %>
|
|
<b style="width:100%"><%= rv["author"]? %><% if rv["author_verified"]? == "true" %> <i class="icon ion ion-md-checkmark-circle"></i><% end %></b>
|
|
<% end %>
|
|
</div>
|
|
|
|
<div class="pure-u-10-24" style="text-align:right">
|
|
<b class="width:100%"><%=
|
|
views = rv["view_count"]?.try &.to_i?
|
|
views ||= rv["view_count_short"]?.try { |x| short_text_to_number(x) }
|
|
translate_count(locale, "generic_views_count", views || 0, NumberFormatting::Short)
|
|
%></b>
|
|
</div>
|
|
</h5>
|
|
|
|
</div>
|
|
<% end %>
|
|
<% end %>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
<script src="/js/watch.js?v=<%= ASSET_COMMIT %>"></script>
|