Minor formatting changes

pull/530/head
Omar Roth 6 years ago
parent 22b9bbe702
commit 1a9360ca75
No known key found for this signature in database
GPG Key ID: B8254FB7EC3D37F2

@ -309,7 +309,7 @@ def template_youtube_comments(comments, locale, thin_mode)
html += <<-END_HTML html += <<-END_HTML
<div class="pure-g"> <div class="pure-g">
<div class="pure-u-4-24 pure-u-md-2-24"> <div class="pure-u-4-24 pure-u-md-2-24">
<img style="width:90%; padding-right:1em; padding-top:1em;" src="#{author_thumbnail}"> <img style="width:90%;padding-right:1em;padding-top:1em" src="#{author_thumbnail}">
</div> </div>
<div class="pure-u-20-24 pure-u-md-22-24"> <div class="pure-u-20-24 pure-u-md-22-24">
<p> <p>

@ -7,7 +7,7 @@
<div class="pure-u-2-3"> <div class="pure-u-2-3">
<h3><%= author %></h3> <h3><%= author %></h3>
</div> </div>
<div class="pure-u-1-3" style="text-align:right;"> <div class="pure-u-1-3" style="text-align:right">
<h3> <h3>
<a href="/feed/channel/<%= ucid %>"><i class="icon ion-logo-rss"></i></a> <a href="/feed/channel/<%= ucid %>"><i class="icon ion-logo-rss"></i></a>
</h3> </h3>
@ -35,10 +35,9 @@
<% end %> <% end %>
</div> </div>
</div> </div>
<div class="pure-u-1-3"></div>
<div class="pure-u-1-3"> <div class="pure-u-1-3">
</div> <div class="pure-g" style="text-align:right">
<div class="pure-u-1-3">
<div class="pure-g" style="text-align:right;">
<% sort_options.each do |sort| %> <% sort_options.each do |sort| %>
<div class="pure-u-1 pure-md-1-3"> <div class="pure-u-1 pure-md-1-3">
<% if sort_by == sort %> <% if sort_by == sort %>
@ -75,7 +74,7 @@
<% end %> <% end %>
</div> </div>
<div class="pure-u-1 pure-u-lg-3-5"></div> <div class="pure-u-1 pure-u-lg-3-5"></div>
<div style="text-align:right" class="pure-u-1 pure-u-lg-1-5"> <div class="pure-u-1 pure-u-lg-1-5" style="text-align:right">
<% if count == 60 %> <% if count == 60 %>
<a href="/channel/<%= ucid %>?page=<%= page + 1 %><% if sort_by != "newest" %>&sort_by=<%= sort_by %><% end %>"> <a href="/channel/<%= ucid %>?page=<%= page + 1 %><% if sort_by != "newest" %>&sort_by=<%= sort_by %><% end %>">
<%= translate(locale, "Next page") %> <%= translate(locale, "Next page") %>

@ -8,7 +8,7 @@
<% end %> <% end %>
<% feed_menu.each do |feed| %> <% feed_menu.each do |feed| %>
<div class="pure-u-1-2 pure-u-md-1-<%= feed_menu.size %>"> <div class="pure-u-1-2 pure-u-md-1-<%= feed_menu.size %>">
<a href="/feed/<%= feed.downcase %>" style="text-align:center;" class="pure-menu-heading"> <a href="/feed/<%= feed.downcase %>" class="pure-menu-heading" style="text-align:center">
<%= translate(locale, feed) %> <%= translate(locale, feed) %>
</a> </a>
</div> </div>

@ -2,11 +2,10 @@
<div class="h-box"> <div class="h-box">
<% case item when %> <% case item when %>
<% when SearchChannel %> <% when SearchChannel %>
<a style="width:100%;" href="/channel/<%= item.ucid %>"> <a style="width:100%" href="/channel/<%= item.ucid %>">
<% if env.get("preferences").as(Preferences).thin_mode %> <% if !env.get("preferences").as(Preferences).thin_mode %>
<% else %>
<center> <center>
<img style="width:56.25%;" src="/ggpht<%= URI.parse(item.author_thumbnail).full_path %>"/> <img style="width:56.25%" src="/ggpht<%= URI.parse(item.author_thumbnail).full_path %>"/>
</center> </center>
<% end %> <% end %>
<p><%= item.author %></p> <p><%= item.author %></p>
@ -20,9 +19,9 @@
<% else %> <% else %>
<% url = "/playlist?list=#{item.id}" %> <% url = "/playlist?list=#{item.id}" %>
<% end %> <% end %>
<a style="width:100%;" href="<%= url %>">
<% if env.get("preferences").as(Preferences).thin_mode %> <a style="width:100%" href="<%= url %>">
<% else %> <% if !env.get("preferences").as(Preferences).thin_mode %>
<div class="thumbnail"> <div class="thumbnail">
<img class="thumbnail" src="/vi/<%= item.thumbnail_id %>/mqdefault.jpg"/> <img class="thumbnail" src="/vi/<%= item.thumbnail_id %>/mqdefault.jpg"/>
<p class="length"><%= number_with_separator(item.video_count) %> videos</p> <p class="length"><%= number_with_separator(item.video_count) %> videos</p>
@ -31,12 +30,13 @@
<p><%= item.title %></p> <p><%= item.title %></p>
</a> </a>
<p> <p>
<b><a style="width:100%;" href="/channel/<%= item.ucid %>"><%= item.author %></a></b> <b>
<a style="width:100%" href="/channel/<%= item.ucid %>"><%= item.author %></a>
</b>
</p> </p>
<% when MixVideo %> <% when MixVideo %>
<a style="width:100%;" href="/watch?v=<%= item.id %>&list=<%= item.mixes[0] %>"> <a style="width:100%" href="/watch?v=<%= item.id %>&list=<%= item.mixes[0] %>">
<% if env.get("preferences").as(Preferences).thin_mode %> <% if !env.get("preferences").as(Preferences).thin_mode %>
<% else %>
<div class="thumbnail"> <div class="thumbnail">
<img class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/> <img class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/>
<% if item.length_seconds != 0 %> <% if item.length_seconds != 0 %>
@ -47,12 +47,13 @@
<p><%= item.title %></p> <p><%= item.title %></p>
</a> </a>
<p> <p>
<b><a style="width:100%;" href="/channel/<%= item.ucid %>"><%= item.author %></a></b> <b>
<a style="width:100%" href="/channel/<%= item.ucid %>"><%= item.author %></a>
</b>
</p> </p>
<% when PlaylistVideo %> <% when PlaylistVideo %>
<a style="width:100%;" href="/watch?v=<%= item.id %>&list=<%= item.playlists[0] %>"> <a style="width:100%" href="/watch?v=<%= item.id %>&list=<%= item.playlists[0] %>">
<% if env.get("preferences").as(Preferences).thin_mode %> <% if !env.get("preferences").as(Preferences).thin_mode %>
<% else %>
<div class="thumbnail"> <div class="thumbnail">
<img class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/> <img class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/>
<% if item.responds_to?(:live_now) && item.live_now %> <% if item.responds_to?(:live_now) && item.live_now %>
@ -65,7 +66,9 @@
<p><%= item.title %></p> <p><%= item.title %></p>
</a> </a>
<p> <p>
<b><a style="width:100%;" href="/channel/<%= item.ucid %>"><%= item.author %></a></b> <b>
<a style="width:100%" href="/channel/<%= item.ucid %>"><%= item.author %></a>
</b>
</p> </p>
<h5 class="pure-g"> <h5 class="pure-g">
@ -82,13 +85,12 @@
</div> </div>
</h5> </h5>
<% else %> <% else %>
<% if env.get("preferences").as(Preferences).thin_mode %> <% if !env.get("preferences").as(Preferences).thin_mode %>
<% else %> <a style="width:100%" href="/watch?v=<%= item.id %>">
<a style="width:100%;" href="/watch?v=<%= item.id %>">
<div class="thumbnail"> <div class="thumbnail">
<img class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/> <img class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/>
<% if env.get? "show_watched" %> <% if env.get? "show_watched" %>
<form onsubmit="return false;" action="/watch_ajax?action_mark_watched=1&id=<%= item.id %>&referer=<%= env.get("current_page") %>" method="post"> <form onsubmit="return false" action="/watch_ajax?action_mark_watched=1&id=<%= item.id %>&referer=<%= env.get("current_page") %>" method="post">
<input type="hidden" name="csrf_token" value="<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>"> <input type="hidden" name="csrf_token" value="<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>">
<p class="watched"> <p class="watched">
<a onclick="mark_watched(this)" data-id="<%= item.id %>" href="#"> <a onclick="mark_watched(this)" data-id="<%= item.id %>" href="#">
@ -102,6 +104,7 @@
</p> </p>
</form> </form>
<% end %> <% end %>
<% if item.responds_to?(:live_now) && item.live_now %> <% if item.responds_to?(:live_now) && item.live_now %>
<p class="length"><i class="icon ion-ios-play-circle"></i> <%= translate(locale, "LIVE") %></p> <p class="length"><i class="icon ion-ios-play-circle"></i> <%= translate(locale, "LIVE") %></p>
<% elsif item.length_seconds != 0 %> <% elsif item.length_seconds != 0 %>
@ -112,7 +115,9 @@
<% end %> <% end %>
<p><a href="/watch?v=<%= item.id %>"><%= item.title %></a></p> <p><a href="/watch?v=<%= item.id %>"><%= item.title %></a></p>
<p> <p>
<b><a style="width:100%;" href="/channel/<%= item.ucid %>"><%= item.author %></a></b> <b>
<a style="width:100%" href="/channel/<%= item.ucid %>"><%= item.author %></a>
</b>
</p> </p>
<h5 class="pure-g"> <h5 class="pure-g">

@ -94,7 +94,7 @@ var player = videojs("player", options, function() {
} }
} }
}, },
// Go backward 5 seconds // Go backward 10 seconds
backward: { backward: {
key: function(e) { key: function(e) {
return e.which === 74; return e.which === 74;
@ -103,7 +103,7 @@ var player = videojs("player", options, function() {
player.currentTime(player.currentTime() - 10); player.currentTime(player.currentTime() - 10);
} }
}, },
// Go forward 5 seconds // Go forward 10 seconds
forward: { forward: {
key: function(e) { key: function(e) {
return e.which === 76; return e.which === 76;
@ -141,16 +141,18 @@ var player = videojs("player", options, function() {
player.on('error', function(event) { player.on('error', function(event) {
if (player.error().code === 2 || player.error().code === 4) { if (player.error().code === 2 || player.error().code === 4) {
setInterval(setTimeout(function (event) { setInterval(setTimeout(function (event) {
console.log("An error occured in the player, reloading..."); console.log('An error occured in the player, reloading...');
var currentTime = player.currentTime(); var currentTime = player.currentTime();
var playbackRate = player.playbackRate(); var playbackRate = player.playbackRate();
var paused = player.paused(); var paused = player.paused();
player.load(); player.load();
if (currentTime > 0.5) { if (currentTime > 0.5) {
currentTime -= 0.5; currentTime -= 0.5;
} }
player.currentTime(currentTime); player.currentTime(currentTime);
player.playbackRate(playbackRate); player.playbackRate(playbackRate);
@ -164,20 +166,20 @@ player.on('error', function(event) {
<% if params.video_start > 0 || params.video_end > 0 %> <% if params.video_start > 0 || params.video_end > 0 %>
player.markers({ player.markers({
onMarkerReached: function(marker) { onMarkerReached: function(marker) {
if (marker.text === "End") { if (marker.text === 'End') {
if (player.loop()) { if (player.loop()) {
player.markers.prev("Start"); player.markers.prev('Start');
} else { } else {
player.pause(); player.pause();
} }
} }
}, },
markers: [ markers: [
{ time: <%= params.video_start %>, text: "Start" }, { time: <%= params.video_start %>, text: 'Start' },
<% if params.video_end < 0 %> <% if params.video_end < 0 %>
{ time: <%= video.info["length_seconds"].to_f - 0.5 %>, text: "End" } { time: <%= video.info["length_seconds"].to_f - 0.5 %>, text: 'End' }
<% else %> <% else %>
{ time: <%= params.video_end %>, text: "End" } { time: <%= params.video_end %>, text: 'End' }
<% end %> <% end %>
] ]
}); });
@ -216,17 +218,17 @@ player.httpSourceSelector();
<% end %> <% end %>
<% if !params.listen && params.annotations %> <% if !params.listen && params.annotations %>
var video_container = document.getElementById("player"); var video_container = document.getElementById('player');
let xhr = new XMLHttpRequest(); let xhr = new XMLHttpRequest();
xhr.responseType = "text"; xhr.responseType = 'text';
xhr.timeout = 60000; xhr.timeout = 60000;
xhr.open("GET", "/api/v1/annotations/<%= video.id %>", true); xhr.open('GET', '/api/v1/annotations/<%= video.id %>', true);
xhr.send(); xhr.send();
xhr.onreadystatechange = function () { xhr.onreadystatechange = function () {
if (xhr.readyState === 4) { if (xhr.readyState === 4) {
if (xhr.status === 200) { if (xhr.status === 200) {
videojs.registerPlugin("youtubeAnnotationsPlugin", youtubeAnnotationsPlugin); videojs.registerPlugin('youtubeAnnotationsPlugin', youtubeAnnotationsPlugin);
if (!player.paused()) { if (!player.paused()) {
player.youtubeAnnotationsPlugin({annotationXml: xhr.response, videoContainer: video_container}); player.youtubeAnnotationsPlugin({annotationXml: xhr.response, videoContainer: video_container});
} else { } else {
@ -238,22 +240,20 @@ xhr.onreadystatechange = function () {
} }
}; };
window.addEventListener("__ar_annotation_click", e => { window.addEventListener('__ar_annotation_click', e => {
const { url, target, seconds } = e.detail; const { url, target, seconds } = e.detail;
var path = new URL(url); var path = new URL(url);
if (path.href.startsWith("https://www.youtube.com/watch?") && seconds) { if (path.href.startsWith('https://www.youtube.com/watch?') && seconds) {
path.search += "&t=" + seconds; path.search += '&t=' + seconds;
} }
path = path.pathname + path.search; path = path.pathname + path.search;
if (target === "current") { if (target === 'current') {
window.location.href = path; window.location.href = path;
} } else if (target === 'new') {
else if (target === "new") { window.open(path, '_blank');
window.open(path, "_blank");
} }
}); });
<% end %> <% end %>

@ -8,10 +8,12 @@
<script src="/js/videojs.hotkeys.min.js"></script> <script src="/js/videojs.hotkeys.min.js"></script>
<script src="/js/videojs-markers.min.js"></script> <script src="/js/videojs-markers.min.js"></script>
<script src="/js/videojs-share.min.js"></script> <script src="/js/videojs-share.min.js"></script>
<% if params.annotations %> <% if params.annotations %>
<link rel="stylesheet" href="/css/videojs-youtube-annotations.min.css"> <link rel="stylesheet" href="/css/videojs-youtube-annotations.min.css">
<script src="/js/videojs-youtube-annotations.min.js"></script> <script src="/js/videojs-youtube-annotations.min.js"></script>
<% end %> <% end %>
<% if params.listen || params.quality != "dash" %> <% if params.listen || params.quality != "dash" %>
<link rel="stylesheet" href="/css/quality-selector.css"> <link rel="stylesheet" href="/css/quality-selector.css">
<script src="/js/silvermine-videojs-quality-selector.min.js"></script> <script src="/js/silvermine-videojs-quality-selector.min.js"></script>

@ -1,7 +1,7 @@
<% if user %> <% if user %>
<% if subscriptions.includes? ucid %> <% if subscriptions.includes? ucid %>
<p> <p>
<form onsubmit="return false;" action="/subscription_ajax?action_remove_subscriptions=1&c=<%= ucid %>&referer=<%= env.get("current_page") %>" method="post"> <form onsubmit="return false" action="/subscription_ajax?action_remove_subscriptions=1&c=<%= ucid %>&referer=<%= env.get("current_page") %>" method="post">
<input type="hidden" name="csrf_token" value="<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>"> <input type="hidden" name="csrf_token" value="<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>">
<a id="subscribe" onclick="unsubscribe()" class="pure-button pure-button-primary" href="#"> <a id="subscribe" onclick="unsubscribe()" class="pure-button pure-button-primary" href="#">
<b><input style="all:unset" type="submit" value="<%= translate(locale, "Unsubscribe") %> | <%= sub_count_text %>"></b> <b><input style="all:unset" type="submit" value="<%= translate(locale, "Unsubscribe") %> | <%= sub_count_text %>"></b>
@ -10,7 +10,7 @@
</p> </p>
<% else %> <% else %>
<p> <p>
<form onsubmit="return false;" action="/subscription_ajax?action_create_subscription_to_channel=1&c=<%= ucid %>&referer=<%= env.get("current_page") %>" method="post"> <form onsubmit="return false" action="/subscription_ajax?action_create_subscription_to_channel=1&c=<%= ucid %>&referer=<%= env.get("current_page") %>" method="post">
<input type="hidden" name="csrf_token" value="<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>"> <input type="hidden" name="csrf_token" value="<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>">
<a id="subscribe" onclick="subscribe()" class="pure-button pure-button-primary" href="#"> <a id="subscribe" onclick="subscribe()" class="pure-button pure-button-primary" href="#">
<b><input style="all:unset" type="submit" value="<%= translate(locale, "Subscribe") %> | <%= sub_count_text %>"></b> <b><input style="all:unset" type="submit" value="<%= translate(locale, "Subscribe") %> | <%= sub_count_text %>"></b>

@ -1,27 +1,29 @@
subscribe_button = document.getElementById("subscribe"); subscribe_button = document.getElementById('subscribe');
if (subscribe_button.getAttribute('onclick')) { if (subscribe_button.getAttribute('onclick')) {
subscribe_button["href"] = "javascript:void(0)"; subscribe_button['href'] = 'javascript:void(0)';
} }
function subscribe(timeouts = 0) { function subscribe(timeouts = 0) {
subscribe_button = document.getElementById("subscribe"); subscribe_button = document.getElementById('subscribe');
if (timeouts > 10) { if (timeouts > 10) {
console.log("Failed to subscribe."); console.log('Failed to subscribe.');
return; return;
} }
var url = "/subscription_ajax?action_create_subscription_to_channel=1&redirect=false&c=<%= ucid %>&referer=<%= env.get("current_page") %>"; var url = '/subscription_ajax?action_create_subscription_to_channel=1&redirect=false' +
'&c=<%= ucid %>&referer=<%= env.get("current_page") %>';
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.responseType = "json"; xhr.responseType = 'json';
xhr.timeout = 20000; xhr.timeout = 20000;
xhr.open("POST", url, true); xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("csrf_token=<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>"); xhr.send('csrf_token=<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>');
var fallback = subscribe_button.innerHTML; var fallback = subscribe_button.innerHTML;
subscribe_button.onclick = unsubscribe; subscribe_button.onclick = unsubscribe;
subscribe_button.innerHTML = '<b><%= translate(locale, "Unsubscribe").gsub("'", "\\'") %> | <%= sub_count_text %></b>' subscribe_button.innerHTML = '<b><%= translate(locale, "Unsubscribe").gsub("'", "\\'") %> | <%= sub_count_text %></b>';
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
@ -33,31 +35,31 @@ function subscribe(timeouts = 0) {
} }
xhr.ontimeout = function() { xhr.ontimeout = function() {
console.log("Subscribing timed out."); console.log('Subscribing timed out.');
subscribe(timeouts + 1); subscribe(timeouts + 1);
}; };
} }
function unsubscribe(timeouts = 0) { function unsubscribe(timeouts = 0) {
subscribe_button = document.getElementById("subscribe"); subscribe_button = document.getElementById('subscribe');
if (timeouts > 10) { if (timeouts > 10) {
console.log("Failed to subscribe"); console.log('Failed to subscribe');
return; return;
} }
var url = "/subscription_ajax?action_remove_subscriptions=1&redirect=false&c=<%= ucid %>&referer=<%= env.get("current_page") %>"; var url = '/subscription_ajax?action_remove_subscriptions=1&redirect=false' +
'&c=<%= ucid %>&referer=<%= env.get("current_page") %>';
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.responseType = "json"; xhr.responseType = 'json';
xhr.timeout = 20000; xhr.timeout = 20000;
xhr.open("POST", url, true); xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send("csrf_token=<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>"); xhr.send('csrf_token=<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>');
var fallback = subscribe_button.innerHTML; var fallback = subscribe_button.innerHTML;
subscribe_button.onclick = subscribe; subscribe_button.onclick = subscribe;
subscribe_button.innerHTML = '<b><%= translate(locale, "Subscribe").gsub("'", "\\'") %> | <%= sub_count_text %></b>' subscribe_button.innerHTML = '<b><%= translate(locale, "Subscribe").gsub("'", "\\'") %> | <%= sub_count_text %></b>';
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
@ -69,8 +71,7 @@ function unsubscribe(timeouts = 0) {
} }
xhr.ontimeout = function() { xhr.ontimeout = function() {
console.log("Unsubscribing timed out."); console.log('Unsubscribing timed out.');
unsubscribe(timeouts + 1); unsubscribe(timeouts + 1);
}; };
} }

@ -27,24 +27,26 @@
<script> <script>
<% if plid %> <% if plid %>
function get_playlist(timeouts = 0) { function get_playlist(plid, timeouts = 0) {
if (timeouts > 10) { if (timeouts > 10) {
console.log("Failed to pull playlist"); console.log('Failed to pull playlist');
return; return;
} }
var plid = "<%= plid %>" if (plid.startsWith('RD')) {
var plid_url = '/api/v1/mixes/' + plid +
if (plid.startsWith("RD")) { '?continuation=<%= video.id %>' +
var plid_url = "/api/v1/mixes/<%= plid %>?continuation=<%= video.id %>&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; '&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>';
} else { } else {
var plid_url = "/api/v1/playlists/<%= plid %>?continuation=<%= video.id %>&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; var plid_url = '/api/v1/playlists/' + plid +
'?continuation=<%= video.id %>' +
'&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>';
} }
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.responseType = "json"; xhr.responseType = 'json';
xhr.timeout = 20000; xhr.timeout = 20000;
xhr.open("GET", plid_url, true); xhr.open('GET', plid_url, true);
xhr.send(); xhr.send();
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
@ -52,18 +54,18 @@ function get_playlist(timeouts = 0) {
if (xhr.status == 200) { if (xhr.status == 200) {
if (xhr.response.nextVideo) { if (xhr.response.nextVideo) {
player.on('ended', function() { player.on('ended', function() {
location.assign("/embed/" location.assign('/watch?v=' + xhr.response.nextVideo +
+ xhr.response.nextVideo '&list=' + plid +
+ "?list=<%= plid %>"
<% if params.listen != preferences.listen %> <% if params.listen != preferences.listen %>
+ "&listen=<%= params.listen %>" '&listen=<%= params.listen %>' +
<% end %> <% end %>
<% if params.autoplay || params.continue_autoplay %> <% if params.autoplay || params.continue_autoplay %>
+ "&autoplay=1" '&autoplay=1' +
<% end %> <% end %>
<% if params.speed != preferences.speed %> <% if params.speed != preferences.speed %>
+ "&speed=<%= params.speed %>" '&speed=<%= params.speed %>' +
<% end %> <% end %>
''
); );
}); });
} }
@ -72,28 +74,28 @@ function get_playlist(timeouts = 0) {
}; };
xhr.ontimeout = function() { xhr.ontimeout = function() {
console.log("Pulling playlist timed out."); console.log('Pulling playlist timed out.');
get_playlist(timeouts + 1); get_playlist(plid, timeouts + 1);
}; };
} }
get_playlist(); get_playlist('<%= plid %>');
<% elsif video_series %> <% elsif video_series %>
player.on('ended', function() { player.on('ended', function() {
location.assign("/embed/" location.assign('/embed/<%= video_series.shift %>' +
+ "<%= video_series.shift %>"
<% if !video_series.empty? %> <% if !video_series.empty? %>
+ "?playlist=<%= video_series.join(",") %>" '?playlist=<%= video_series.join(",") %>' +
<% end %> <% end %>
<% if params.listen != preferences.listen %> <% if params.listen != preferences.listen %>
+ "&listen=<%= params.listen %>" '&listen=<%= params.listen %>' +
<% end %> <% end %>
<% if params.autoplay || params.continue_autoplay %> <% if params.autoplay || params.continue_autoplay %>
+ "&autoplay=1" '&autoplay=1' +
<% end %> <% end %>
<% if params.speed != preferences.speed %> <% if params.speed != preferences.speed %>
+ "&speed=<%= params.speed %>" '&speed=<%= params.speed %>' +
<% end %> <% end %>
''
); );
}); });
<% end %> <% end %>

@ -6,12 +6,12 @@
<div class="pure-u-1-3"> <div class="pure-u-1-3">
<h3><%= translate(locale, "`x` videos", %(<span id="count">#{user.watched.size}</span>)) %></h3> <h3><%= translate(locale, "`x` videos", %(<span id="count">#{user.watched.size}</span>)) %></h3>
</div> </div>
<div class="pure-u-1-3" style="text-align:center;"> <div class="pure-u-1-3" style="text-align:center">
<h3> <h3>
<a href="/feed/subscriptions"><%= translate(locale, "`x` subscriptions", %(<span id="count">#{user.subscriptions.size}</span>)) %></a> <a href="/feed/subscriptions"><%= translate(locale, "`x` subscriptions", %(<span id="count">#{user.subscriptions.size}</span>)) %></a>
</h3> </h3>
</div> </div>
<div class="pure-u-1-3" style="text-align:right;"> <div class="pure-u-1-3" style="text-align:right">
<h3> <h3>
<a href="/clear_watch_history"><%= translate(locale, "Clear watch history") %></a> <a href="/clear_watch_history"><%= translate(locale, "Clear watch history") %></a>
</h3> </h3>
@ -23,9 +23,8 @@
<% slice.each do |item| %> <% slice.each do |item| %>
<div class="pure-u-1 pure-u-md-1-4"> <div class="pure-u-1 pure-u-md-1-4">
<div class="h-box"> <div class="h-box">
<a style="width:100%;" href="/watch?v=<%= item %>"> <a style="width:100%" href="/watch?v=<%= item %>">
<% if env.get("preferences").as(Preferences).thin_mode %> <% if !env.get("preferences").as(Preferences).thin_mode %>
<% else %>
<div class="thumbnail"> <div class="thumbnail">
<img class="thumbnail" src="/vi/<%= item %>/mqdefault.jpg"/> <img class="thumbnail" src="/vi/<%= item %>/mqdefault.jpg"/>
<form onsubmit="return false;" action="/watch_ajax?action_mark_unwatched=1&id=<%= item %>&referer=<%= env.get("current_page") %>" method="post"> <form onsubmit="return false;" action="/watch_ajax?action_mark_unwatched=1&id=<%= item %>&referer=<%= env.get("current_page") %>" method="post">
@ -52,22 +51,23 @@
function mark_unwatched(target) { function mark_unwatched(target) {
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode; var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
tile.style.display = "none"; tile.style.display = "none";
var count = document.getElementById("count") var count = document.getElementById('count')
count.innerText = count.innerText - 1; count.innerText = count.innerText - 1;
var url = "/watch_ajax?action_mark_unwatched=1&redirect=false&id=" + target.getAttribute("data-id"); var url = '/watch_ajax?action_mark_unwatched=1&redirect=false' +
'&id=' + target.getAttribute('data-id');
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.responseType = "json"; xhr.responseType = 'json';
xhr.timeout = 20000; xhr.timeout = 20000;
xhr.open("POST", url, true); xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send("csrf_token=<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>"); xhr.send('csrf_token=<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>');
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
if (xhr.status != 200) { if (xhr.status != 200) {
count.innerText = count.innerText - 1 + 2; count.innerText = count.innerText - 1 + 2;
tile.style.display = ""; tile.style.display = '';
} }
} }
} }
@ -83,7 +83,7 @@ function mark_unwatched(target) {
<% end %> <% end %>
</div> </div>
<div class="pure-u-1 pure-u-lg-3-5"></div> <div class="pure-u-1 pure-u-lg-3-5"></div>
<div style="text-align:right;" class="pure-u-1 pure-u-lg-1-5"> <div class="pure-u-1 pure-u-lg-1-5" style="text-align:right">
<% if watched.size >= limit %> <% if watched.size >= limit %>
<a href="/feed/history?page=<%= page + 1 %>"> <a href="/feed/history?page=<%= page + 1 %>">
<%= translate(locale, "Next page") %> <%= translate(locale, "Next page") %>

@ -18,7 +18,9 @@
</a> </a>
</div> </div>
</div> </div>
<hr> <hr>
<% if account_type == "invidious" %> <% if account_type == "invidious" %>
<form class="pure-form pure-form-stacked" action="/login?referer=<%= URI.escape(referer) %>&type=invidious" method="post"> <form class="pure-form pure-form-stacked" action="/login?referer=<%= URI.escape(referer) %>&type=invidious" method="post">
<fieldset> <fieldset>

@ -6,7 +6,7 @@
<div class="pure-u-2-3"> <div class="pure-u-2-3">
<h3><%= mix.title %></h3> <h3><%= mix.title %></h3>
</div> </div>
<div class="pure-u-1-3" style="text-align:right;"> <div class="pure-u-1-3" style="text-align:right">
<h3> <h3>
<a href="/feed/playlist/<%= mix.id %>"><i class="icon ion-logo-rss"></i></a> <a href="/feed/playlist/<%= mix.id %>"><i class="icon ion-logo-rss"></i></a>
</h3> </h3>

@ -7,12 +7,13 @@
<div class="pure-u-2-3"> <div class="pure-u-2-3">
<h3><%= playlist.title %></h3> <h3><%= playlist.title %></h3>
</div> </div>
<div class="pure-u-1-3" style="text-align:right;"> <div class="pure-u-1-3" style="text-align:right">
<h3> <h3>
<a href="/feed/playlist/<%= plid %>"><i class="icon ion-logo-rss"></i></a> <a href="/feed/playlist/<%= plid %>"><i class="icon ion-logo-rss"></i></a>
</h3> </h3>
</div> </div>
</div> </div>
<div class="pure-g h-box"> <div class="pure-g h-box">
<div class="pure-u-1 pure-u-md-1-4"> <div class="pure-u-1 pure-u-md-1-4">
<a href="/channel/<%= playlist.ucid %>"> <a href="/channel/<%= playlist.ucid %>">
@ -42,7 +43,7 @@
<% end %> <% end %>
</div> </div>
<div class="pure-u-1 pure-u-lg-3-5"></div> <div class="pure-u-1 pure-u-lg-3-5"></div>
<div style="text-align:right;" class="pure-u-1 pure-u-lg-1-5"> <div class="pure-u-1 pure-u-lg-1-5" style="text-align:right">
<% if videos.size == 100 %> <% if videos.size == 100 %>
<a href="/playlist?list=<%= playlist.id %>&page=<%= page + 1 %>"> <a href="/playlist?list=<%= playlist.id %>&page=<%= page + 1 %>">
<%= translate(locale, "Next page") %> <%= translate(locale, "Next page") %>

@ -6,7 +6,7 @@
<div class="pure-u-2-3"> <div class="pure-u-2-3">
<h3><%= author %></h3> <h3><%= author %></h3>
</div> </div>
<div class="pure-u-1-3" style="text-align:right;"> <div class="pure-u-1-3" style="text-align:right">
<h3> <h3>
<a href="/feed/channel/<%= ucid %>"><i class="icon ion-logo-rss"></i></a> <a href="/feed/channel/<%= ucid %>"><i class="icon ion-logo-rss"></i></a>
</h3> </h3>
@ -32,10 +32,9 @@
<% end %> <% end %>
</div> </div>
</div> </div>
<div class="pure-u-1-3"></div>
<div class="pure-u-1-3"> <div class="pure-u-1-3">
</div> <div class="pure-g" style="text-align:right">
<div class="pure-u-1-3">
<div class="pure-g" style="text-align:right;">
<% {"last", "oldest", "newest"}.each do |sort| %> <% {"last", "oldest", "newest"}.each do |sort| %>
<div class="pure-u-1 pure-md-1-3"> <div class="pure-u-1 pure-md-1-3">
<% if sort_by == sort %> <% if sort_by == sort %>
@ -65,7 +64,7 @@
<div class="pure-g h-box"> <div class="pure-g h-box">
<div class="pure-u-1 pure-u-md-4-5"></div> <div class="pure-u-1 pure-u-md-4-5"></div>
<div style="text-align:right;" class="pure-u-1 pure-u-lg-1-5"> <div class="pure-u-1 pure-u-lg-1-5" style="text-align:right">
<% if items.size >= 28 %> <% if items.size >= 28 %>
<a href="/channel/<%= ucid %>/playlists?continuation=<%= continuation %><% if sort_by != "last" %>&sort_by=<%= sort_by %><% end %>"> <a href="/channel/<%= ucid %>/playlists?continuation=<%= continuation %><% if sort_by != "last" %>&sort_by=<%= sort_by %><% end %>">
<%= translate(locale, "Next page") %> <%= translate(locale, "Next page") %>

@ -1,6 +1,12 @@
<% content_for "header" do %> <% content_for "header" do %>
<meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>"> <meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>">
<title><% if config.default_home != "Popular" %><%= translate(locale, "Popular") %> - <% end %>Invidious</title> <title>
<% if config.default_home != "Popular" %>
<%= translate(locale, "Popular") %> - Invidious
<% else %>
Invidious
<% end %>
</title>
<% end %> <% end %>
<%= rendered "components/feed_menu" %> <%= rendered "components/feed_menu" %>

@ -19,7 +19,7 @@
<% end %> <% end %>
</div> </div>
<div class="pure-u-1 pure-u-lg-3-5"></div> <div class="pure-u-1 pure-u-lg-3-5"></div>
<div style="text-align:right;" class="pure-u-1 pure-u-lg-1-5"> <div class="pure-u-1 pure-u-lg-1-5" style="text-align:right">
<% if count >= 20 %> <% if count >= 20 %>
<a href="/search?q=<%= HTML.escape(query.not_nil!) %>&page=<%= page + 1 %>"> <a href="/search?q=<%= HTML.escape(query.not_nil!) %>&page=<%= page + 1 %>">
<%= translate(locale, "Next page") %> <%= translate(locale, "Next page") %>

@ -5,17 +5,23 @@
<div class="pure-g h-box"> <div class="pure-g h-box">
<div class="pure-u-1-3"> <div class="pure-u-1-3">
<h3> <h3>
<a href="/feed/subscriptions"><%= translate(locale, "`x` subscriptions", %(<span id="count">#{subscriptions.size}</span>)) %></a> <a href="/feed/subscriptions">
<%= translate(locale, "`x` subscriptions", %(<span id="count">#{subscriptions.size}</span>)) %>
</a>
</h3> </h3>
</div> </div>
<div class="pure-u-1-3" style="text-align:center"> <div class="pure-u-1-3" style="text-align:center">
<h3> <h3>
<a href="/feed/history"><%= translate(locale, "Watch history") %></a> <a href="/feed/history">
<%= translate(locale, "Watch history") %>
</a>
</h3> </h3>
</div> </div>
<div class="pure-u-1-3" style="text-align:right"> <div class="pure-u-1-3" style="text-align:right">
<h3> <h3>
<a href="/data_control?referer=<%= referer %>"><%= translate(locale, "Import/export") %></a> <a href="/data_control?referer=<%= referer %>">
<%= translate(locale, "Import/export") %>
</a>
</h3> </h3>
</div> </div>
</div> </div>
@ -50,23 +56,25 @@
<script> <script>
function remove_subscription(target) { function remove_subscription(target) {
var row = target.parentNode.parentNode.parentNode.parentNode.parentNode; var row = target.parentNode.parentNode.parentNode.parentNode.parentNode;
row.style.display = "none"; row.style.display = 'none';
var count = document.getElementById("count") var count = document.getElementById('count');
count.innerText = count.innerText - 1; count.innerText = count.innerText - 1;
var url = "/subscription_ajax?action_remove_subscriptions=1&redirect=false&referer=<%= env.get("current_page") %>&c=" + target.getAttribute("data-ucid"); var url = '/subscription_ajax?action_remove_subscriptions=1&redirect=false' +
'&referer=<%= env.get("current_page") %>' +
'&c=' + target.getAttribute('data-ucid');
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.responseType = "json"; xhr.responseType = 'json';
xhr.timeout = 20000; xhr.timeout = 20000;
xhr.open("POST", url, true); xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send("csrf_token=<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>"); xhr.send('csrf_token=<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>');
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
if (xhr.status != 200) { if (xhr.status != 200) {
count.innerText = count.innerText - 1 + 2; count.innerText = parseInt(count.innerText) + 1;
row.style.display = ""; row.style.display = '';
} }
} }
} }

@ -11,19 +11,21 @@
<a href="/subscription_manager"><%= translate(locale, "Manage subscriptions") %></a> <a href="/subscription_manager"><%= translate(locale, "Manage subscriptions") %></a>
</h3> </h3>
</div> </div>
<div class="pure-u-1-3" style="text-align:center;"> <div class="pure-u-1-3" style="text-align:center">
<h3> <h3>
<a href="/feed/history"><%= translate(locale, "Watch history") %></a> <a href="/feed/history"><%= translate(locale, "Watch history") %></a>
</h3> </h3>
</div> </div>
<div class="pure-u-1-3" style="text-align:right;"> <div class="pure-u-1-3" style="text-align:right">
<h3> <h3>
<a href="/feed/private?token=<%= token %>"><i class="icon ion-logo-rss"></i></a> <a href="/feed/private?token=<%= token %>"><i class="icon ion-logo-rss"></i></a>
</h3> </h3>
</div> </div>
</div> </div>
<center><%= translate(locale, "`x` unseen notifications", "#{notifications.size}") %></center> <center>
<%= translate(locale, "`x` unseen notifications", "#{notifications.size}") %>
</center>
<% if !notifications.empty? %> <% if !notifications.empty? %>
<div class="h-box"> <div class="h-box">
@ -54,20 +56,21 @@
<script> <script>
function mark_watched(target) { function mark_watched(target) {
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode; var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
tile.style.display = "none"; tile.style.display = 'none';
var url = "/watch_ajax?action_mark_watched=1&redirect=false&id=" + target.getAttribute("data-id"); var url = '/watch_ajax?action_mark_watched=1&redirect=false' +
'&id=' + target.getAttribute('data-id');
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.responseType = "json"; xhr.responseType = 'json';
xhr.timeout = 20000; xhr.timeout = 20000;
xhr.open("POST", url, true); xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send("csrf_token=<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>"); xhr.send('csrf_token=<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>');
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
if (xhr.status != 200) { if (xhr.status != 200) {
tile.style.display = ""; tile.style.display = '';
} }
} }
} }
@ -83,7 +86,7 @@ function mark_watched(target) {
<% end %> <% end %>
</div> </div>
<div class="pure-u-1 pure-u-lg-3-5"></div> <div class="pure-u-1 pure-u-lg-3-5"></div>
<div style="text-align:right;" class="pure-u-1 pure-u-lg-1-5"> <div class="pure-u-1 pure-u-lg-1-5" style="text-align:right">
<% if (videos.size + notifications.size) == max_results %> <% if (videos.size + notifications.size) == max_results %>
<a href="/feed/subscriptions?max_results=<%= max_results %>&page=<%= page + 1 %>"> <a href="/feed/subscriptions?max_results=<%= max_results %>&page=<%= page + 1 %>">
<%= translate(locale, "Next page") %> <%= translate(locale, "Next page") %>

@ -38,7 +38,7 @@
<div class="pure-u-1 pure-u-md-12-24 searchbar"> <div class="pure-u-1 pure-u-md-12-24 searchbar">
<form class="pure-form" action="/search" method="get"> <form class="pure-form" action="/search" method="get">
<fieldset> <fieldset>
<input type="search" style="width:100%;" name="q" placeholder="<%= translate(locale, "search") %>" value="<%= env.get?("search").try {|x| HTML.escape(x.as(String)) } || env.params.query["q"]?.try {|x| HTML.escape(x)} %>"> <input type="search" style="width:100%" name="q" placeholder="<%= translate(locale, "search") %>" value="<%= env.get?("search").try {|x| HTML.escape(x.as(String)) } || env.params.query["q"]?.try {|x| HTML.escape(x)} %>">
</fieldset> </fieldset>
</form> </form>
</div> </div>
@ -101,12 +101,15 @@
<% end %> <% end %>
</div> </div>
</div> </div>
<% if CONFIG.banner %> <% if CONFIG.banner %>
<div class="h-box"> <div class="h-box">
<h3><%= CONFIG.banner %></h3> <h3><%= CONFIG.banner %></h3>
</div> </div>
<% end %> <% end %>
<%= content %> <%= content %>
<div class="footer"> <div class="footer">
<div class="pure-g"> <div class="pure-g">
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 pure-u-md-1-3">
@ -116,10 +119,12 @@
</div> </div>
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-bitcoin"></i> <i class="icon ion-logo-bitcoin"></i>
BTC: 356DpZyMXu6rYd55Yqzjs29n79kGKWcYrY</div> BTC: 356DpZyMXu6rYd55Yqzjs29n79kGKWcYrY
</div>
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-bitcoin"></i> <i class="icon ion-logo-bitcoin"></i>
BCH: qq4ptclkzej5eza6a50et5ggc58hxsq5aylqut2npk</div> BCH: qq4ptclkzej5eza6a50et5ggc58hxsq5aylqut2npk
</div>
<div class="pure-u-1 pure-u-md-1-3"> <div class="pure-u-1 pure-u-md-1-3">
<i class="icon ion-logo-usd"></i> <i class="icon ion-logo-usd"></i>
<a href="https://liberapay.com/omarroth">Liberapay</a> <a href="https://liberapay.com/omarroth">Liberapay</a>
@ -141,7 +146,8 @@
<i class="icon ion-logo-github"></i> <i class="icon ion-logo-github"></i>
<%= translate(locale, "Current version: ") %> <%= CURRENT_VERSION %>-<%= CURRENT_COMMIT %> <%= translate(locale, "Current version: ") %> <%= CURRENT_VERSION %>-<%= CURRENT_COMMIT %>
<i class="icon ion-logo-github"></i> <i class="icon ion-logo-github"></i>
<%= CURRENT_BRANCH %></div> <%= CURRENT_BRANCH %>
</div>
</div> </div>
</div> </div>
</div> </div>

@ -48,23 +48,25 @@
<script> <script>
function revoke_token(target) { function revoke_token(target) {
var row = target.parentNode.parentNode.parentNode.parentNode.parentNode; var row = target.parentNode.parentNode.parentNode.parentNode.parentNode;
row.style.display = "none"; row.style.display = 'none';
var count = document.getElementById("count") var count = document.getElementById('count');
count.innerText = count.innerText - 1; count.innerText = count.innerText - 1;
var url = "/token_ajax?action_revoke_token=1&redirect=false&referer=<%= env.get("current_page") %>&session=" + target.getAttribute("data-session"); var url = '/token_ajax?action_revoke_token=1&redirect=false' +
'&referer=<%= env.get("current_page") %>' +
'&session=' + target.getAttribute('data-session');
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.responseType = "json"; xhr.responseType = 'json';
xhr.timeout = 20000; xhr.timeout = 20000;
xhr.open("POST", url, true); xhr.open('POST', url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send("csrf_token=<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>"); xhr.send('csrf_token=<%= URI.escape(env.get?("csrf_token").try &.as(String) || "") %>');
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
if (xhr.readyState == 4) { if (xhr.readyState == 4) {
if (xhr.status != 200) { if (xhr.status != 200) {
count.innerText = count.innerText - 1 + 2; count.innerText = parseInt(count.innerText) + 1;
row.style.display = ""; row.style.display = '';
} }
} }
} }

@ -1,6 +1,12 @@
<% content_for "header" do %> <% content_for "header" do %>
<meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>"> <meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>">
<title><% if config.default_home != "Top" %><%= translate(locale, "Top") %> - <% end %>Invidious</title> <title>
<% if config.default_home != "Top" %>
<%= translate(locale, "Top") %> - Invidious
<% else %>
Invidious
<% end %>
</title>
<% end %> <% end %>
<%= rendered "components/feed_menu" %> <%= rendered "components/feed_menu" %>

@ -1,6 +1,12 @@
<% content_for "header" do %> <% content_for "header" do %>
<meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>"> <meta name="description" content="<%= translate(locale, "An alternative front-end to YouTube") %>">
<title><% if config.default_home != "Trending" %><%= translate(locale, "Trending") %> - <% end %>Invidious</title> <title>
<% if config.default_home != "Trending" %>
<%= translate(locale, "Trending") %> - Invidious
<% else %>
Invidious
<% end %>
</title>
<% end %> <% end %>
<%= rendered "components/feed_menu" %> <%= rendered "components/feed_menu" %>
@ -8,11 +14,13 @@
<div class="pure-g h-box"> <div class="pure-g h-box">
<div style="align-self:flex-end" class="pure-u-2-3"> <div style="align-self:flex-end" class="pure-u-2-3">
<% if plid %> <% if plid %>
<a href="/playlist?list=<%= plid %>"><%= translate(locale, "View as playlist") %></a> <a href="/playlist?list=<%= plid %>">
<%= translate(locale, "View as playlist") %>
</a>
<% end %> <% end %>
</div> </div>
<div class="pure-u-1-3"> <div class="pure-u-1-3">
<div class="pure-g" style="text-align:right;"> <div class="pure-g" style="text-align:right">
<% {"Default", "Music", "Gaming", "News", "Movies"}.each do |option| %> <% {"Default", "Music", "Gaming", "News", "Movies"}.each do |option| %>
<div class="pure-u-1 pure-md-1-3"> <div class="pure-u-1 pure-md-1-3">
<% if trending_type == option %> <% if trending_type == option %>

@ -44,18 +44,26 @@
</a> </a>
<% end %> <% end %>
</h1> </h1>
<% if !video.is_listed %> <% if !video.is_listed %>
<h3><i class="icon ion-ios-lock"></i> <%= translate(locale, "Unlisted") %></h3> <h3>
<i class="icon ion-ios-lock"></i> <%= translate(locale, "Unlisted") %>
</h3>
<% end %> <% end %>
<% if !reason.empty? %> <% if !reason.empty? %>
<h3><%= reason %></h3> <h3>
<%= reason %>
</h3>
<% end %> <% end %>
</div> </div>
<div class="pure-g"> <div class="pure-g">
<div class="pure-u-1 pure-u-lg-1-5"> <div class="pure-u-1 pure-u-lg-1-5">
<div class="h-box"> <div class="h-box">
<p><a href="https://www.youtube.com/watch?v=<%= video.id %>"><%= translate(locale, "Watch on YouTube") %></a></p> <p>
<a href="https://www.youtube.com/watch?v=<%= video.id %>"><%= translate(locale, "Watch on YouTube") %></a>
</p>
<p> <p>
<% if params.annotations %> <% if params.annotations %>
<a href="/watch?<%= env.params.query %>&iv_load_policy=3"> <a href="/watch?<%= env.params.query %>&iv_load_policy=3">
@ -69,7 +77,7 @@
</p> </p>
<% if CONFIG.dmca_content.includes? video.id %> <% if CONFIG.dmca_content.includes? video.id %>
<p>Download is disabled.</p> <p><%= translate(locale, "Download is disabled.") %></p>
<% else %> <% else %>
<form class="pure-form pure-form-stacked" action="/latest_version" method="get" rel="noopener" target="_blank"> <form class="pure-form pure-form-stacked" action="/latest_version" method="get" rel="noopener" target="_blank">
<div class="pure-control-group"> <div class="pure-control-group">
@ -107,7 +115,7 @@
<p><i class="icon ion-ios-eye"></i> <%= number_with_separator(video.views) %></p> <p><i class="icon ion-ios-eye"></i> <%= number_with_separator(video.views) %></p>
<p><i class="icon ion-ios-thumbs-up"></i> <%= number_with_separator(video.likes) %></p> <p><i class="icon ion-ios-thumbs-up"></i> <%= number_with_separator(video.likes) %></p>
<p><i class="icon ion-ios-thumbs-down"></i> <%= number_with_separator(video.dislikes) %></p> <p><i class="icon ion-ios-thumbs-down"></i> <%= number_with_separator(video.dislikes) %></p>
<p id="Genre"><%= translate(locale, "Genre: ") %> <p id="genre"><%= translate(locale, "Genre: ") %>
<% if video.genre_url.empty? %> <% if video.genre_url.empty? %>
<%= video.genre %> <%= video.genre %>
<% else %> <% else %>
@ -115,14 +123,14 @@
<% end %> <% end %>
</p> </p>
<% if !video.license.empty? %> <% if !video.license.empty? %>
<p id="License"><%= translate(locale, "License: ") %><%= video.license %></p> <p id="license"><%= translate(locale, "License: ") %><%= video.license %></p>
<% end %> <% end %>
<p id="FamilyFriendly"><%= translate(locale, "Family friendly? ") %><%= translate_bool(locale, video.is_family_friendly) %></p> <p id="family_friendly"><%= translate(locale, "Family friendly? ") %><%= translate_bool(locale, video.is_family_friendly) %></p>
<p id="Wilson"><%= translate(locale, "Wilson score: ") %><%= video.wilson_score.round(4) %></p> <p id="wilson"><%= translate(locale, "Wilson score: ") %><%= video.wilson_score.round(4) %></p>
<p id="Rating"><%= translate(locale, "Rating: ") %><%= rating.round(4) %> / 5</p> <p id="rating"><%= translate(locale, "Rating: ") %><%= rating.round(4) %> / 5</p>
<p id="Engagement"><%= translate(locale, "Engagement: ") %><%= engagement.round(2) %>%</p> <p id="engagement"><%= translate(locale, "Engagement: ") %><%= engagement.round(2) %>%</p>
<% if video.allowed_regions.size != REGIONS.size %> <% if video.allowed_regions.size != REGIONS.size %>
<p id="AllowedRegions"> <p id="allowed_regions">
<% if video.allowed_regions.size < REGIONS.size / 2 %> <% if video.allowed_regions.size < REGIONS.size / 2 %>
<%= translate(locale, "Whitelisted regions: ") %><%= video.allowed_regions.join(", ") %> <%= translate(locale, "Whitelisted regions: ") %><%= video.allowed_regions.join(", ") %>
<% else %> <% else %>
@ -140,17 +148,22 @@
<h3><%= video.author %></h3> <h3><%= video.author %></h3>
</a> </a>
</p> </p>
<% ucid = video.ucid %> <% ucid = video.ucid %>
<% author = video.author %> <% author = video.author %>
<% sub_count_text = video.sub_count_text %> <% sub_count_text = video.sub_count_text %>
<%= rendered "components/subscribe_widget" %> <%= rendered "components/subscribe_widget" %>
<p> <p>
<b><%= translate(locale, "Shared `x`", video.published.to_s("%B %-d, %Y")) %></b> <b><%= translate(locale, "Shared `x`", video.published.to_s("%B %-d, %Y")) %></b>
</p> </p>
<div> <div>
<%= video.description %> <%= video.description %>
</div> </div>
<hr> <hr>
<div id="comments"> <div id="comments">
<% if nojs %> <% if nojs %>
<%= comment_html %> <%= comment_html %>
@ -164,16 +177,15 @@
</div> </div>
</div> </div>
</div> </div>
<% if params.related_videos || plid %> <% if params.related_videos || plid %>
<div class="pure-u-1 pure-u-lg-1-5"> <div class="pure-u-1 pure-u-lg-1-5">
<% if plid %> <% if plid %>
<div id="playlist" class="h-box"> <div id="playlist" class="h-box"></div>
</div>
<% end %> <% end %>
<% if params.related_videos %> <% if params.related_videos %>
<div class="h-box"> <div class="h-box">
<% if !rvs.empty? %> <% if !rvs.empty? %>
<div <% if plid %>style="display:none"<% end %>> <div <% if plid %>style="display:none"<% end %>>
<div class="pure-control-group"> <div class="pure-control-group">
@ -187,8 +199,7 @@
<% rvs.each do |rv| %> <% rvs.each do |rv| %>
<% if rv["id"]? %> <% if rv["id"]? %>
<a href="/watch?v=<%= rv["id"] %>"> <a href="/watch?v=<%= rv["id"] %>">
<% if env.get("preferences").as(Preferences).thin_mode %> <% if !env.get("preferences").as(Preferences).thin_mode %>
<% else %>
<div class="thumbnail"> <div class="thumbnail">
<img class="thumbnail" src="/vi/<%= rv["id"] %>/mqdefault.jpg"> <img class="thumbnail" src="/vi/<%= rv["id"] %>/mqdefault.jpg">
<p class="length"><%= recode_length_seconds(rv["length_seconds"]?.try &.to_i? || 0) %></p> <p class="length"><%= recode_length_seconds(rv["length_seconds"]?.try &.to_i? || 0) %></p>
@ -218,18 +229,19 @@
<script> <script>
<% if !rvs.empty? && !plid && params.continue %> <% if !rvs.empty? && !plid && params.continue %>
player.on('ended', function() { player.on('ended', function() {
location.assign("/watch?v=" location.assign('/watch?v=' +
+ "<%= rvs.select { |rv| rv["id"]? }[0]?.try &.["id"] %>" '<%= rvs.select { |rv| rv["id"]? }[0]?.try &.["id"] %>' +
+ "&continue=1" '&continue=1' +
<% if params.listen != preferences.listen %> <% if params.listen != preferences.listen %>
+ "&listen=<%= params.listen %>" '&listen=<%= params.listen %>' +
<% end %> <% end %>
<% if params.autoplay || params.continue_autoplay %> <% if params.autoplay || params.continue_autoplay %>
+ "&autoplay=1" '&autoplay=1' +
<% end %> <% end %>
<% if params.speed != preferences.speed %> <% if params.speed != preferences.speed %>
+ "&speed=<%= params.speed %>" '&speed=<%= params.speed %>' +
<% end %> <% end %>
''
); );
}); });
<% end %> <% end %>
@ -237,18 +249,19 @@ player.on('ended', function() {
function continue_autoplay(target) { function continue_autoplay(target) {
if (target.checked) { if (target.checked) {
player.on('ended', function() { player.on('ended', function() {
location.assign("/watch?v=" location.assign('/watch?v=' +
+ "<%= rvs.select { |rv| rv["id"]? }[0]?.try &.["id"] %>" '<%= rvs.select { |rv| rv["id"]? }[0]?.try &.["id"] %>' +
+ "&continue=1" '&continue=1' +
<% if params.listen != preferences.listen %> <% if params.listen != preferences.listen %>
+ "&listen=<%= params.listen %>" '&listen=<%= params.listen %>' +
<% end %> <% end %>
<% if params.autoplay || params.continue_autoplay %> <% if params.autoplay || params.continue_autoplay %>
+ "&autoplay=1" '&autoplay=1' +
<% end %> <% end %>
<% if params.speed != preferences.speed %> <% if params.speed != preferences.speed %>
+ "&speed=<%= params.speed %>" '&speed=<%= params.speed %>' +
<% end %> <% end %>
''
); );
}); });
} else { } else {
@ -269,12 +282,12 @@ function number_with_separator(val) {
<%= rendered "components/subscribe_widget_script" %> <%= rendered "components/subscribe_widget_script" %>
<% if plid %> <% if plid %>
function get_playlist(timeouts = 0) { function get_playlist(plid, timeouts = 0) {
playlist = document.getElementById("playlist"); playlist = document.getElementById('playlist');
if (timeouts > 10) { if (timeouts > 10) {
console.log("Failed to pull playlist"); console.log('Failed to pull playlist');
playlist.innerHTML = ""; playlist.innerHTML = '';
return; return;
} }
@ -282,18 +295,20 @@ function get_playlist(timeouts = 0) {
<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3> \ <h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3> \
<hr>' <hr>'
var plid = "<%= plid %>" if (plid.startsWith('RD')) {
var plid_url = '/api/v1/mixes/' + plid +
if (plid.startsWith("RD")) { '?continuation=<%= video.id %>' +
var plid_url = "/api/v1/mixes/<%= plid %>?continuation=<%= video.id %>&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; '&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>';
} else { } else {
var plid_url = "/api/v1/playlists/<%= plid %>?continuation=<%= video.id %>&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; var plid_url = '/api/v1/playlists/' + plid +
'?continuation=<%= video.id %>' +
'&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>';
} }
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.responseType = "json"; xhr.responseType = 'json';
xhr.timeout = 20000; xhr.timeout = 20000;
xhr.open("GET", plid_url, true); xhr.open('GET', plid_url, true);
xhr.send(); xhr.send();
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
@ -303,47 +318,46 @@ function get_playlist(timeouts = 0) {
if (xhr.response.nextVideo) { if (xhr.response.nextVideo) {
player.on('ended', function() { player.on('ended', function() {
location.assign("/watch?v=" location.assign('/watch?v=' + xhr.response.nextVideo +
+ xhr.response.nextVideo '&list=' + plid +
+ "&list=<%= plid %>"
<% if params.listen != preferences.listen %> <% if params.listen != preferences.listen %>
+ "&listen=<%= params.listen %>" '&listen=<%= params.listen %>' +
<% end %> <% end %>
<% if params.autoplay || params.continue_autoplay %> <% if params.autoplay || params.continue_autoplay %>
+ "&autoplay=1" '&autoplay=1' +
<% end %> <% end %>
<% if params.speed != preferences.speed %> <% if params.speed != preferences.speed %>
+ "&speed=<%= params.speed %>" '&speed=<%= params.speed %>' +
<% end %> <% end %>
''
); );
}); });
} }
} else { } else {
playlist.innerHTML = ""; playlist.innerHTML = '';
document.getElementById('continue').style.display = ""; document.getElementById('continue').style.display = '';
} }
} }
}; };
xhr.ontimeout = function() { xhr.ontimeout = function() {
console.log("Pulling playlist timed out."); console.log('Pulling playlist timed out.');
playlist = document.getElementById('playlist');
playlist = document.getElementById("playlist");
playlist.innerHTML = playlist.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3><hr>'; '<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3><hr>';
get_playlist(timeouts + 1); get_playlist(plid, timeouts + 1);
}; };
} }
get_playlist(); get_playlist('<%= plid %>');
<% end %> <% end %>
function get_reddit_comments(timeouts = 0) { function get_reddit_comments(timeouts = 0) {
comments = document.getElementById("comments"); comments = document.getElementById('comments');
if (timeouts > 10) { if (timeouts > 10) {
console.log("Failed to pull comments"); console.log('Failed to pull comments');
comments.innerHTML = ""; comments.innerHTML = '';
return; return;
} }
@ -351,11 +365,13 @@ function get_reddit_comments(timeouts = 0) {
comments.innerHTML = comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>'; '<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
var url = "/api/v1/comments/<%= video.id %>?source=reddit&format=html&hl=<%= env.get("preferences").as(Preferences).locale %>"; var url = '/api/v1/comments/<%= video.id %>' +
'?source=reddit&format=html' +
'&hl=<%= env.get("preferences").as(Preferences).locale %>';
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.responseType = "json"; xhr.responseType = 'json';
xhr.timeout = 20000; xhr.timeout = 20000;
xhr.open("GET", url, true); xhr.open('GET', url, true);
xhr.send(); xhr.send();
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
@ -395,18 +411,17 @@ function get_reddit_comments(timeouts = 0) {
}; };
xhr.ontimeout = function() { xhr.ontimeout = function() {
console.log("Pulling comments timed out."); console.log('Pulling comments timed out.');
get_reddit_comments(timeouts + 1); get_reddit_comments(timeouts + 1);
}; };
} }
function get_youtube_comments(timeouts = 0) { function get_youtube_comments(timeouts = 0) {
comments = document.getElementById("comments"); comments = document.getElementById('comments');
if (timeouts > 10) { if (timeouts > 10) {
console.log("Failed to pull comments"); console.log('Failed to pull comments');
comments.innerHTML = ""; comments.innerHTML = '';
return; return;
} }
@ -414,11 +429,14 @@ function get_youtube_comments(timeouts = 0) {
comments.innerHTML = comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>'; '<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
var url = "/api/v1/comments/<%= video.id %>?format=html&hl=<%= env.get("preferences").as(Preferences).locale %>&thin_mode=<%= env.get("preferences").as(Preferences).thin_mode %>"; var url = '/api/v1/comments/<%= video.id %>' +
'?format=html' +
'&hl=<%= env.get("preferences").as(Preferences).locale %>' +
'&thin_mode=<%= env.get("preferences").as(Preferences).thin_mode %>';
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.responseType = "json"; xhr.responseType = 'json';
xhr.timeout = 20000; xhr.timeout = 20000;
xhr.open("GET", url, true); xhr.open('GET', url, true);
xhr.send(); xhr.send();
xhr.onreadystatechange = function() { xhr.onreadystatechange = function() {
@ -449,15 +467,14 @@ function get_youtube_comments(timeouts = 0) {
<% if preferences && preferences.comments[1] == "youtube" %> <% if preferences && preferences.comments[1] == "youtube" %>
get_youtube_comments(timeouts + 1); get_youtube_comments(timeouts + 1);
<% else %> <% else %>
comments.innerHTML = ""; comments.innerHTML = '';
<% end %> <% end %>
} }
} }
}; };
xhr.ontimeout = function() { xhr.ontimeout = function() {
console.log("Pulling comments timed out."); console.log('Pulling comments timed out.');
comments.innerHTML = comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>'; '<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
get_youtube_comments(timeouts + 1); get_youtube_comments(timeouts + 1);
@ -472,8 +489,11 @@ function get_youtube_replies(target, load_more) {
body.innerHTML = body.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>'; '<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
var url = '/api/v1/comments/<%= video.id %>?format=html&hl=<%= env.get("preferences").as(Preferences).locale %>&thin_mode=<%= env.get("preferences").as(Preferences).thin_mode %>&continuation=' + var url = '/api/v1/comments/<%= video.id %>' +
continuation; '?format=html' +
'&hl=<%= env.get("preferences").as(Preferences).locale %>' +
'&thin_mode=<%= env.get("preferences").as(Preferences).thin_mode %>' +
'&continuation=' + continuation;
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.responseType = 'json'; xhr.responseType = 'json';
xhr.timeout = 20000; xhr.timeout = 20000;
@ -504,7 +524,6 @@ function get_youtube_replies(target, load_more) {
xhr.ontimeout = function() { xhr.ontimeout = function() {
console.log('Pulling comments timed out.'); console.log('Pulling comments timed out.');
body.innerHTML = fallback; body.innerHTML = fallback;
}; };
} }
@ -520,8 +539,8 @@ function get_youtube_replies(target, load_more) {
<% elsif preferences.comments[1] == "reddit" %> <% elsif preferences.comments[1] == "reddit" %>
get_reddit_comments(); get_reddit_comments();
<% else %> <% else %>
comments = document.getElementById("comments"); comments = document.getElementById('comments');
comments.innerHTML = ""; comments.innerHTML = '';
<% end %> <% end %>
<% end %> <% end %>
<% else %> <% else %>

Loading…
Cancel
Save