summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml4
-rw-r--r--assets/css/default.css2
-rw-r--r--assets/css/player.css3
-rw-r--r--assets/js/community.js27
-rw-r--r--assets/js/embed.js26
-rw-r--r--assets/js/handlers.js39
-rw-r--r--assets/js/notifications.js31
-rw-r--r--assets/js/player.js214
-rw-r--r--assets/js/playlist_widget.js23
-rw-r--r--assets/js/silvermine-videojs-quality-selector.min.js4
-rw-r--r--assets/js/subscribe_widget.js48
-rw-r--r--assets/js/themes.js23
-rw-r--r--assets/js/watch.js130
-rw-r--r--assets/js/watched_widget.js19
-rw-r--r--docker/Dockerfile2
-rw-r--r--docker/Dockerfile.arm646
-rw-r--r--locales/ar.json5
-rw-r--r--locales/cs.json67
-rw-r--r--locales/da.json5
-rw-r--r--locales/de.json5
-rw-r--r--locales/el.json4
-rw-r--r--locales/en-US.json2
-rw-r--r--locales/eo.json5
-rw-r--r--locales/es.json17
-rw-r--r--locales/eu.json1
-rw-r--r--locales/fa.json5
-rw-r--r--locales/fi.json15
-rw-r--r--locales/fr.json55
-rw-r--r--locales/he.json4
-rw-r--r--locales/hi.json474
-rw-r--r--locales/hr.json5
-rw-r--r--locales/hu-HU.json30
-rw-r--r--locales/id.json5
-rw-r--r--locales/it.json11
-rw-r--r--locales/ja.json5
-rw-r--r--locales/ko.json5
-rw-r--r--locales/lt.json5
-rw-r--r--locales/nb-NO.json5
-rw-r--r--locales/nl.json88
-rw-r--r--locales/pl.json5
-rw-r--r--locales/pt-BR.json51
-rw-r--r--locales/pt-PT.json5
-rw-r--r--locales/pt.json5
-rw-r--r--locales/ro.json182
-rw-r--r--locales/ru.json23
-rw-r--r--locales/sk.json26
-rw-r--r--locales/sl.json506
-rw-r--r--locales/sq.json5
-rw-r--r--locales/sr.json5
-rw-r--r--locales/sr_Cyrl.json5
-rw-r--r--locales/sv-SE.json5
-rw-r--r--locales/tr.json15
-rw-r--r--locales/uk.json75
-rw-r--r--locales/vi.json5
-rw-r--r--locales/zh-CN.json15
-rw-r--r--locales/zh-TW.json15
-rw-r--r--shard.lock4
-rw-r--r--shard.yml5
-rw-r--r--spec/invidious/search/query_spec.cr200
-rw-r--r--src/ext/kemal_content_for.cr16
-rw-r--r--src/ext/kemal_static_file_handler.cr (renamed from src/invidious/helpers/static_file_handler.cr)0
-rw-r--r--src/invidious.cr6
-rw-r--r--src/invidious/channels/about.cr7
-rw-r--r--src/invidious/comments.cr71
-rw-r--r--src/invidious/frontend/search_filters.cr2
-rw-r--r--src/invidious/helpers/errors.cr2
-rw-r--r--src/invidious/helpers/i18n.cr2
-rw-r--r--src/invidious/helpers/macros.cr12
-rw-r--r--src/invidious/helpers/serialized_yt_data.cr7
-rw-r--r--src/invidious/helpers/utils.cr8
-rw-r--r--src/invidious/routes/api/manifest.cr10
-rw-r--r--src/invidious/routes/feeds.cr1
-rw-r--r--src/invidious/routes/watch.cr17
-rw-r--r--src/invidious/search/query.cr17
-rw-r--r--src/invidious/videos.cr54
-rw-r--r--src/invidious/views/channel.ecr2
-rw-r--r--src/invidious/views/community.ecr2
-rw-r--r--src/invidious/views/components/item.ecr18
-rw-r--r--src/invidious/views/components/player.ecr18
-rw-r--r--src/invidious/views/embed.ecr3
-rw-r--r--src/invidious/views/playlists.ecr2
-rw-r--r--src/invidious/views/watch.ecr9
-rw-r--r--src/invidious/yt_backend/extractors.cr50
-rw-r--r--videojs-dependencies.yml8
84 files changed, 2328 insertions, 597 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index db0987cf..4e68b7f2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,9 +38,9 @@ jobs:
matrix:
stable: [true]
crystal:
- - 1.0.0
- - 1.1.1
- 1.2.2
+ - 1.3.2
+ - 1.4.0
include:
- crystal: nightly
stable: false
diff --git a/assets/css/default.css b/assets/css/default.css
index 49069c92..61b7819f 100644
--- a/assets/css/default.css
+++ b/assets/css/default.css
@@ -291,7 +291,7 @@ input[type="search"]::-webkit-search-cancel-button {
.flexible { display: flex; }
.flex-left { flex: 1 1 100%; flex-wrap: wrap; }
-.flex-right { flex: 1 0 max-content; flex-wrap: nowrap; }
+.flex-right { flex: 1 0 auto; flex-wrap: nowrap; }
p.channel-name { margin: 0; }
p.video-data { margin: 0; font-weight: bold; font-size: 80%; }
diff --git a/assets/css/player.css b/assets/css/player.css
index 120fd2f8..304375b5 100644
--- a/assets/css/player.css
+++ b/assets/css/player.css
@@ -70,6 +70,9 @@
margin-bottom: 2em;
}
+.video-js.player-style-youtube .vjs-progress-control .vjs-progress-holder, .video-js.player-style-youtube .vjs-progress-control {height: 5px;
+margin-bottom: 10px;}
+
ul.vjs-menu-content::-webkit-scrollbar {
display: none;
}
diff --git a/assets/js/community.js b/assets/js/community.js
index 4077f1cd..44066a58 100644
--- a/assets/js/community.js
+++ b/assets/js/community.js
@@ -1,19 +1,20 @@
-var community_data = JSON.parse(document.getElementById('community_data').innerHTML);
+'use strict';
+var community_data = JSON.parse(document.getElementById('community_data').textContent);
String.prototype.supplant = function (o) {
return this.replace(/{([^{}]*)}/g, function (a, b) {
var r = o[b];
return typeof r === 'string' || typeof r === 'number' ? r : a;
});
-}
+};
function hide_youtube_replies(event) {
var target = event.target;
- sub_text = target.getAttribute('data-inner-text');
- inner_text = target.getAttribute('data-sub-text');
+ var sub_text = target.getAttribute('data-inner-text');
+ var inner_text = target.getAttribute('data-sub-text');
- body = target.parentNode.parentNode.children[1];
+ var body = target.parentNode.parentNode.children[1];
body.style.display = 'none';
target.innerHTML = sub_text;
@@ -25,10 +26,10 @@ function hide_youtube_replies(event) {
function show_youtube_replies(event) {
var target = event.target;
- sub_text = target.getAttribute('data-inner-text');
- inner_text = target.getAttribute('data-sub-text');
+ var sub_text = target.getAttribute('data-inner-text');
+ var inner_text = target.getAttribute('data-sub-text');
- body = target.parentNode.parentNode.children[1];
+ var body = target.parentNode.parentNode.children[1];
body.style.display = '';
target.innerHTML = sub_text;
@@ -63,8 +64,8 @@ function get_youtube_replies(target, load_more) {
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status == 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status === 200) {
if (load_more) {
body = body.parentNode.parentNode;
body.removeChild(body.lastElementChild);
@@ -92,12 +93,12 @@ function get_youtube_replies(target, load_more) {
body.innerHTML = fallback;
}
}
- }
+ };
xhr.ontimeout = function () {
- console.log('Pulling comments failed.');
+ console.warn('Pulling comments failed.');
body.innerHTML = fallback;
- }
+ };
xhr.send();
}
diff --git a/assets/js/embed.js b/assets/js/embed.js
index 9d0be0ea..7e9ac605 100644
--- a/assets/js/embed.js
+++ b/assets/js/embed.js
@@ -1,19 +1,21 @@
-var video_data = JSON.parse(document.getElementById('video_data').innerHTML);
+'use strict';
+var video_data = JSON.parse(document.getElementById('video_data').textContent);
function get_playlist(plid, retries) {
- if (retries == undefined) retries = 5;
+ if (retries === undefined) retries = 5;
if (retries <= 0) {
- console.log('Failed to pull playlist');
+ console.warn('Failed to pull playlist');
return;
}
+ var plid_url;
if (plid.startsWith('RD')) {
- var plid_url = '/api/v1/mixes/' + plid +
+ plid_url = '/api/v1/mixes/' + plid +
'?continuation=' + video_data.id +
'&format=html&hl=' + video_data.preferences.locale;
} else {
- var plid_url = '/api/v1/playlists/' + plid +
+ plid_url = '/api/v1/playlists/' + plid +
'?index=' + video_data.index +
'&continuation' + video_data.id +
'&format=html&hl=' + video_data.preferences.locale;
@@ -57,17 +59,17 @@ function get_playlist(plid, retries) {
}
}
}
- }
+ };
xhr.onerror = function () {
- console.log('Pulling playlist failed... ' + retries + '/5');
- setTimeout(function () { get_playlist(plid, retries - 1) }, 1000);
- }
+ console.warn('Pulling playlist failed... ' + retries + '/5');
+ setTimeout(function () { get_playlist(plid, retries - 1); }, 1000);
+ };
xhr.ontimeout = function () {
- console.log('Pulling playlist failed... ' + retries + '/5');
+ console.warn('Pulling playlist failed... ' + retries + '/5');
get_playlist(plid, retries - 1);
- }
+ };
xhr.send();
}
@@ -96,7 +98,7 @@ window.addEventListener('load', function (e) {
}
if (video_data.video_series.length !== 0) {
- url.searchParams.set('playlist', video_data.video_series.join(','))
+ url.searchParams.set('playlist', video_data.video_series.join(','));
}
location.assign(url.pathname + url.search);
diff --git a/assets/js/handlers.js b/assets/js/handlers.js
index 02175957..f6617b60 100644
--- a/assets/js/handlers.js
+++ b/assets/js/handlers.js
@@ -13,20 +13,23 @@
// For dynamically inserted elements
document.addEventListener('click', function (e) {
if (!e || !e.target) { return; }
- e = e.target;
- var handler_name = e.getAttribute('data-onclick');
+
+ var t = e.target;
+ var handler_name = t.getAttribute('data-onclick');
+
switch (handler_name) {
case 'jump_to_time':
- var time = e.getAttribute('data-jump-time');
+ e.preventDefault();
+ var time = t.getAttribute('data-jump-time');
player.currentTime(time);
break;
case 'get_youtube_replies':
- var load_more = e.getAttribute('data-load-more') !== null;
- var load_replies = e.getAttribute('data-load-replies') !== null;
- get_youtube_replies(e, load_more, load_replies);
+ var load_more = t.getAttribute('data-load-more') !== null;
+ var load_replies = t.getAttribute('data-load-replies') !== null;
+ get_youtube_replies(t, load_more, load_replies);
break;
case 'toggle_parent':
- toggle_parent(e);
+ toggle_parent(t);
break;
default:
break;
@@ -76,7 +79,7 @@
});
n2a(document.querySelectorAll('[data-onrange="update_volume_value"]')).forEach(function (e) {
- var cb = function () { update_volume_value(e); }
+ var cb = function () { update_volume_value(e); };
e.oninput = cb;
e.onchange = cb;
});
@@ -102,13 +105,13 @@
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status != 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status !== 200) {
count.innerText = parseInt(count.innerText) + 1;
row.style.display = '';
}
}
- }
+ };
var csrf_token = target.parentNode.querySelector('input[name="csrf_token"]').value;
xhr.send('csrf_token=' + csrf_token);
@@ -131,20 +134,20 @@
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status != 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status !== 200) {
count.innerText = parseInt(count.innerText) + 1;
row.style.display = '';
}
}
- }
+ };
var csrf_token = target.parentNode.querySelector('input[name="csrf_token"]').value;
xhr.send('csrf_token=' + csrf_token);
}
// Handle keypresses
- window.addEventListener('keydown', (event) => {
+ window.addEventListener('keydown', function (event) {
// Ignore modifier keys
if (event.ctrlKey || event.metaKey) return;
@@ -152,14 +155,14 @@
let focused_tag = document.activeElement.tagName.toLowerCase();
const allowed = /^(button|checkbox|file|radio|submit)$/;
- if (focused_tag === "textarea") return;
- if (focused_tag === "input") {
+ if (focused_tag === 'textarea') return;
+ if (focused_tag === 'input') {
let focused_type = document.activeElement.type.toLowerCase();
if (!focused_type.match(allowed)) return;
}
// Focus search bar on '/'
- if (event.key == "/") {
+ if (event.key === '/') {
document.getElementById('searchbox').focus();
event.preventDefault();
}
diff --git a/assets/js/notifications.js b/assets/js/notifications.js
index 3d1ec1ed..ec5f6dd3 100644
--- a/assets/js/notifications.js
+++ b/assets/js/notifications.js
@@ -1,9 +1,10 @@
-var notification_data = JSON.parse(document.getElementById('notification_data').innerHTML);
+'use strict';
+var notification_data = JSON.parse(document.getElementById('notification_data').textContent);
var notifications, delivered;
function get_subscriptions(callback, retries) {
- if (retries == undefined) retries = 5;
+ if (retries === undefined) retries = 5;
if (retries <= 0) {
return;
@@ -17,21 +18,21 @@ function get_subscriptions(callback, retries) {
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
- subscriptions = xhr.response;
+ var subscriptions = xhr.response;
callback(subscriptions);
}
}
- }
+ };
xhr.onerror = function () {
- console.log('Pulling subscriptions failed... ' + retries + '/5');
- setTimeout(function () { get_subscriptions(callback, retries - 1) }, 1000);
- }
+ console.warn('Pulling subscriptions failed... ' + retries + '/5');
+ setTimeout(function () { get_subscriptions(callback, retries - 1); }, 1000);
+ };
xhr.ontimeout = function () {
- console.log('Pulling subscriptions failed... ' + retries + '/5');
+ console.warn('Pulling subscriptions failed... ' + retries + '/5');
get_subscriptions(callback, retries - 1);
- }
+ };
xhr.send();
}
@@ -40,7 +41,7 @@ function create_notification_stream(subscriptions) {
notifications = new SSE(
'/api/v1/auth/notifications?fields=videoId,title,author,authorId,publishedText,published,authorThumbnails,liveNow', {
withCredentials: true,
- payload: 'topics=' + subscriptions.map(function (subscription) { return subscription.authorId }).join(','),
+ payload: 'topics=' + subscriptions.map(function (subscription) { return subscription.authorId; }).join(','),
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
delivered = [];
@@ -53,7 +54,7 @@ function create_notification_stream(subscriptions) {
}
var notification = JSON.parse(event.data);
- console.log('Got notification:', notification);
+ console.info('Got notification:', notification);
if (start_time < notification.published && !delivered.includes(notification.videoId)) {
if (Notification.permission === 'granted') {
@@ -67,7 +68,7 @@ function create_notification_stream(subscriptions) {
system_notification.onclick = function (event) {
window.open('/watch?v=' + event.currentTarget.tag, '_blank');
- }
+ };
}
delivered.push(notification.videoId);
@@ -82,16 +83,16 @@ function create_notification_stream(subscriptions) {
'<i class="icon ion-ios-notifications-outline"></i>';
}
}
- }
+ };
notifications.addEventListener('error', handle_notification_error);
notifications.stream();
}
function handle_notification_error(event) {
- console.log('Something went wrong with notifications, trying to reconnect...');
+ console.warn('Something went wrong with notifications, trying to reconnect...');
notifications = { close: function () { } };
- setTimeout(function () { get_subscriptions(create_notification_stream) }, 1000);
+ setTimeout(function () { get_subscriptions(create_notification_stream); }, 1000);
}
window.addEventListener('load', function (e) {
diff --git a/assets/js/player.js b/assets/js/player.js
index e478fb8f..6ddb1158 100644
--- a/assets/js/player.js
+++ b/assets/js/player.js
@@ -1,5 +1,6 @@
-var player_data = JSON.parse(document.getElementById('player_data').innerHTML);
-var video_data = JSON.parse(document.getElementById('video_data').innerHTML);
+'use strict';
+var player_data = JSON.parse(document.getElementById('player_data').textContent);
+var video_data = JSON.parse(document.getElementById('video_data').textContent);
var options = {
preload: 'auto',
@@ -27,7 +28,7 @@ var options = {
overrideNative: true
}
}
-}
+};
if (player_data.aspect_ratio) {
options.aspectRatio = player_data.aspect_ratio;
@@ -38,7 +39,7 @@ embed_url.searchParams.delete('v');
var short_url = location.origin + '/' + video_data.id + embed_url.search;
embed_url = location.origin + '/embed/' + video_data.id + embed_url.search;
-var save_player_pos_key = "save_player_pos";
+var save_player_pos_key = 'save_player_pos';
videojs.Vhs.xhr.beforeRequest = function(options) {
if (options.uri.indexOf('videoplayback') === -1 && options.uri.indexOf('local=true') === -1) {
@@ -49,6 +50,42 @@ videojs.Vhs.xhr.beforeRequest = function(options) {
var player = videojs('player', options);
+player.on('error', () => {
+ if (video_data.params.quality !== 'dash') {
+ if (!player.currentSrc().includes("local=true") && !video_data.local_disabled) {
+ var currentSources = player.currentSources();
+ for (var i = 0; i < currentSources.length; i++) {
+ currentSources[i]["src"] += "&local=true"
+ }
+ player.src(currentSources)
+ }
+ else if (player.error().code === 2 || player.error().code === 4) {
+ setTimeout(function (event) {
+ console.log('An error occurred in the player, reloading...');
+
+ var currentTime = player.currentTime();
+ var playbackRate = player.playbackRate();
+ var paused = player.paused();
+
+ player.load();
+
+ if (currentTime > 0.5) currentTime -= 0.5;
+
+ player.currentTime(currentTime);
+ player.playbackRate(playbackRate);
+
+ if (!paused) player.play();
+ }, 10000);
+ }
+ }
+});
+
+if (video_data.params.quality == 'dash') {
+ player.reloadSourceOnError({
+ errorInterval: 10
+ });
+}
+
/**
* Function for add time argument to url
* @param {String} url
@@ -75,12 +112,12 @@ var shareOptions = {
description: player_data.description,
image: player_data.thumbnail,
get embedCode() {
- return "<iframe id='ivplayer' width='640' height='360' src='" +
- addCurrentTimeToURL(embed_url) + "' style='border:none;'></iframe>";
+ return '<iframe id="ivplayer" width="640" height="360" src="' +
+ addCurrentTimeToURL(embed_url) + '" style="border:none;"></iframe>';
}
};
-const storage = (() => {
+const storage = (function () {
try { if (localStorage.length !== -1) return localStorage; }
catch (e) { console.info('No storage available: ' + e); }
@@ -101,78 +138,57 @@ if (location.pathname.startsWith('/embed/')) {
// Detection code taken from https://stackoverflow.com/a/20293441
function isMobile() {
- try{ document.createEvent("TouchEvent"); return true; }
+ try{ document.createEvent('TouchEvent'); return true; }
catch(e){ return false; }
}
if (isMobile()) {
player.mobileUi();
- buttons = ["playToggle", "volumePanel", "captionsButton"];
+ var buttons = ['playToggle', 'volumePanel', 'captionsButton'];
- if (video_data.params.quality !== 'dash') buttons.push("qualitySelector")
+ if (video_data.params.quality !== 'dash') buttons.push('qualitySelector');
// Create new control bar object for operation buttons
- const ControlBar = videojs.getComponent("controlBar");
+ const ControlBar = videojs.getComponent('controlBar');
let operations_bar = new ControlBar(player, {
children: [],
playbackRates: [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0]
});
- buttons.slice(1).forEach(child => operations_bar.addChild(child))
+ buttons.slice(1).forEach(function (child) {operations_bar.addChild(child);});
// Remove operation buttons from primary control bar
- primary_control_bar = player.getChild("controlBar");
- buttons.forEach(child => primary_control_bar.removeChild(child));
+ var primary_control_bar = player.getChild('controlBar');
+ buttons.forEach(function (child) {primary_control_bar.removeChild(child);});
- operations_bar_element = operations_bar.el();
- operations_bar_element.className += " mobile-operations-bar"
- player.addChild(operations_bar)
+ var operations_bar_element = operations_bar.el();
+ operations_bar_element.className += ' mobile-operations-bar';
+ player.addChild(operations_bar);
// Playback menu doesn't work when it's initialized outside of the primary control bar
- playback_element = document.getElementsByClassName("vjs-playback-rate")[0]
- operations_bar_element.append(playback_element)
+ var playback_element = document.getElementsByClassName('vjs-playback-rate')[0];
+ operations_bar_element.append(playback_element);
// The share and http source selector element can't be fetched till the players ready.
- player.one("playing", () => {
- share_element = document.getElementsByClassName("vjs-share-control")[0]
- operations_bar_element.append(share_element)
-
- if (video_data.params.quality === 'dash') {
- http_source_selector = document.getElementsByClassName("vjs-http-source-selector vjs-menu-button")[0]
- operations_bar_element.append(http_source_selector)
- }
- })
-}
-
-player.on('error', function (event) {
- if (player.error().code === 2 || player.error().code === 4) {
- setTimeout(function (event) {
- console.log('An error occurred in the player, reloading...');
+ player.one('playing', function () {
+ var share_element = document.getElementsByClassName('vjs-share-control')[0];
+ operations_bar_element.append(share_element);
- var currentTime = player.currentTime();
- var playbackRate = player.playbackRate();
- var paused = player.paused();
-
- player.load();
-
- if (currentTime > 0.5) currentTime -= 0.5;
-
- player.currentTime(currentTime);
- player.playbackRate(playbackRate);
-
- if (!paused) player.play();
- }, 5000);
- }
-});
+ if (video_data.params.quality === 'dash') {
+ var http_source_selector = document.getElementsByClassName('vjs-http-source-selector vjs-menu-button')[0];
+ operations_bar_element.append(http_source_selector);
+ }
+ });
+}
// Enable VR video support
if (!video_data.params.listen && video_data.vr && video_data.params.vr_mode) {
- player.crossOrigin("anonymous")
+ player.crossOrigin('anonymous');
switch (video_data.projection_type) {
- case "EQUIRECTANGULAR":
- player.vr({projection: "equirectangular"});
- default: // Should only be "MESH" but we'll use this as a fallback.
- player.vr({projection: "EAC"});
+ case 'EQUIRECTANGULAR':
+ player.vr({projection: 'equirectangular'});
+ default: // Should only be 'MESH' but we'll use this as a fallback.
+ player.vr({projection: 'EAC'});
}
}
@@ -207,27 +223,27 @@ player.playbackRate(video_data.params.speed);
* @returns cookieValue
*/
function getCookieValue(name) {
- var value = document.cookie.split(";").filter(item => item.includes(name + "="));
+ var value = document.cookie.split(';').filter(function (item) {return item.includes(name + '=');});
- return (value != null && value.length >= 1)
- ? value[0].substring((name + "=").length, value[0].length)
+ return (value.length >= 1)
+ ? value[0].substring((name + '=').length, value[0].length)
: null;
}
/**
- * Method for updating the "PREFS" cookie (or creating it if missing)
+ * Method for updating the 'PREFS' cookie (or creating it if missing)
*
* @param {number} newVolume New volume defined (null if unchanged)
* @param {number} newSpeed New speed defined (null if unchanged)
*/
function updateCookie(newVolume, newSpeed) {
- var volumeValue = newVolume != null ? newVolume : video_data.params.volume;
- var speedValue = newSpeed != null ? newSpeed : video_data.params.speed;
+ var volumeValue = newVolume !== null ? newVolume : video_data.params.volume;
+ var speedValue = newSpeed !== null ? newSpeed : video_data.params.speed;
var cookieValue = getCookieValue('PREFS');
var cookieData;
- if (cookieValue != null) {
+ if (cookieValue !== null) {
var cookieJson = JSON.parse(decodeURIComponent(cookieValue));
cookieJson.volume = volumeValue;
cookieJson.speed = speedValue;
@@ -244,7 +260,7 @@ function updateCookie(newVolume, newSpeed) {
var domainUsed = window.location.hostname;
// Fix for a bug in FF where the leading dot in the FQDN is not ignored
- if (domainUsed.charAt(0) != '.' && !ipRegex.test(domainUsed) && domainUsed != 'localhost')
+ if (domainUsed.charAt(0) !== '.' && !ipRegex.test(domainUsed) && domainUsed !== 'localhost')
domainUsed = '.' + window.location.hostname;
document.cookie = 'PREFS=' + cookieData + '; SameSite=Strict; path=/; domain=' +
@@ -264,7 +280,7 @@ player.on('volumechange', function () {
player.on('waiting', function () {
if (player.playbackRate() > 1 && player.liveTracker.isLive() && player.liveTracker.atLiveEdge()) {
- console.log('Player has caught up to source, resetting playbackRate.')
+ console.info('Player has caught up to source, resetting playbackRate.');
player.playbackRate(1);
}
});
@@ -275,13 +291,13 @@ if (video_data.premiere_timestamp && Math.round(new Date() / 1000) < video_data.
if (video_data.params.save_player_pos) {
const url = new URL(location);
- const hasTimeParam = url.searchParams.has("t");
+ const hasTimeParam = url.searchParams.has('t');
const remeberedTime = get_video_time();
let lastUpdated = 0;
if(!hasTimeParam) set_seconds_after_start(remeberedTime);
- const updateTime = () => {
+ const updateTime = function () {
const raw = player.currentTime();
const time = Math.floor(raw);
@@ -291,7 +307,7 @@ if (video_data.params.save_player_pos) {
}
};
- player.on("timeupdate", updateTime);
+ player.on('timeupdate', updateTime);
}
else remove_all_video_times();
@@ -301,13 +317,13 @@ if (video_data.params.autoplay) {
player.ready(function () {
new Promise(function (resolve, reject) {
- setTimeout(() => resolve(1), 1);
+ setTimeout(function () {resolve(1);}, 1);
}).then(function (result) {
var promise = player.play();
if (promise !== undefined) {
- promise.then(_ => {
- }).catch(error => {
+ promise.then(function () {
+ }).catch(function (error) {
bpb.show();
});
}
@@ -318,16 +334,16 @@ if (video_data.params.autoplay) {
if (!video_data.params.listen && video_data.params.quality === 'dash') {
player.httpSourceSelector();
- if (video_data.params.quality_dash != "auto") {
- player.ready(() => {
- player.on("loadedmetadata", () => {
- const qualityLevels = Array.from(player.qualityLevels()).sort((a, b) => a.height - b.height);
+ if (video_data.params.quality_dash !== 'auto') {
+ player.ready(function () {
+ player.on('loadedmetadata', function () {
+ const qualityLevels = Array.from(player.qualityLevels()).sort(function (a, b) {return a.height - b.height;});
let targetQualityLevel;
switch (video_data.params.quality_dash) {
- case "best":
+ case 'best':
targetQualityLevel = qualityLevels.length - 1;
break;
- case "worst":
+ case 'worst':
targetQualityLevel = 0;
break;
default:
@@ -341,7 +357,7 @@ if (!video_data.params.listen && video_data.params.quality === 'dash') {
}
}
for (let i = 0; i < qualityLevels.length; i++) {
- qualityLevels[i].enabled = (i == targetQualityLevel);
+ qualityLevels[i].enabled = (i === targetQualityLevel);
}
});
});
@@ -375,10 +391,12 @@ if (!video_data.params.listen && video_data.params.annotations) {
}
}
}
- }
+ };
- window.addEventListener('__ar_annotation_click', e => {
- const { url, target, seconds } = e.detail;
+ window.addEventListener('__ar_annotation_click', function (e) {
+ const url = e.detail.url,
+ target = e.detail.target,
+ seconds = e.detail.seconds;
var path = new URL(url);
if (path.href.startsWith('https://www.youtube.com/watch?') && seconds) {
@@ -448,7 +466,7 @@ function get_video_time() {
return timestamp || 0;
}
- catch {
+ catch (e) {
return 0;
}
}
@@ -459,7 +477,7 @@ function set_all_video_times(times) {
try {
storage.setItem(save_player_pos_key, JSON.stringify(times));
} catch (e) {
- console.debug('set_all_video_times: ' + e);
+ console.warn('set_all_video_times: ' + e);
}
} else {
storage.removeItem(save_player_pos_key);
@@ -474,7 +492,7 @@ function get_all_video_times() {
try {
return JSON.parse(raw);
} catch (e) {
- console.debug('get_all_video_times: ' + e);
+ console.warn('get_all_video_times: ' + e);
}
}
}
@@ -568,7 +586,7 @@ function increase_playback_rate(steps) {
player.playbackRate(options.playbackRates[newIndex]);
}
-window.addEventListener('keydown', e => {
+window.addEventListener('keydown', function (e) {
if (e.target.tagName.toLowerCase() === 'input') {
// Ignore input when focus is on certain elements, e.g. form fields.
return;
@@ -687,7 +705,7 @@ window.addEventListener('keydown', e => {
var volumeHover = false;
var volumeSelector = pEl.querySelector('.vjs-volume-menu-button') || pEl.querySelector('.vjs-volume-panel');
- if (volumeSelector != null) {
+ if (volumeSelector !== null) {
volumeSelector.onmouseover = function () { volumeHover = true; };
volumeSelector.onmouseout = function () { volumeHover = false; };
}
@@ -707,9 +725,9 @@ window.addEventListener('keydown', e => {
var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail)));
event.preventDefault();
- if (delta == 1) {
+ if (delta === 1) {
increase_volume(volumeStep);
- } else if (delta == -1) {
+ } else if (delta === -1) {
increase_volume(-volumeStep);
}
}
@@ -718,7 +736,7 @@ window.addEventListener('keydown', e => {
};
player.on('mousewheel', mouseScroll);
- player.on("DOMMouseScroll", mouseScroll);
+ player.on('DOMMouseScroll', mouseScroll);
}());
// Since videojs-share can sometimes be blocked, we defer it until last
@@ -728,13 +746,13 @@ if (player.share) {
// show the preferred caption by default
if (player_data.preferred_caption_found) {
- player.ready(() => {
+ player.ready(function () {
player.textTracks()[1].mode = 'showing';
});
}
// Safari audio double duration fix
-if (navigator.vendor == "Apple Computer, Inc." && video_data.params.listen) {
+if (navigator.vendor === 'Apple Computer, Inc.' && video_data.params.listen) {
player.on('loadedmetadata', function () {
player.on('timeupdate', function () {
if (player.remainingTime() < player.duration() / 2 && player.remainingTime() >= 2) {
@@ -745,18 +763,18 @@ if (navigator.vendor == "Apple Computer, Inc." && video_data.params.listen) {
}
// Watch on Invidious link
-if (window.location.pathname.startsWith("/embed/")) {
+if (window.location.pathname.startsWith('/embed/')) {
const Button = videojs.getComponent('Button');
let watch_on_invidious_button = new Button(player);
// Create hyperlink for current instance
- redirect_element = document.createElement("a");
- redirect_element.setAttribute("href", `http://${window.location.host}/watch?v=${window.location.pathname.replace("/embed/","")}`)
- redirect_element.appendChild(document.createTextNode("Invidious"))
+ var redirect_element = document.createElement('a');
+ redirect_element.setAttribute('href', location.pathname.replace('/embed/', '/watch?v='));
+ redirect_element.appendChild(document.createTextNode('Invidious'));
- watch_on_invidious_button.el().appendChild(redirect_element)
- watch_on_invidious_button.addClass("watch-on-invidious")
+ watch_on_invidious_button.el().appendChild(redirect_element);
+ watch_on_invidious_button.addClass('watch-on-invidious');
- cb = player.getChild('ControlBar')
- cb.addChild(watch_on_invidious_button)
-};
+ var cb = player.getChild('ControlBar');
+ cb.addChild(watch_on_invidious_button);
+}
diff --git a/assets/js/playlist_widget.js b/assets/js/playlist_widget.js
index 0ec27859..c2565874 100644
--- a/assets/js/playlist_widget.js
+++ b/assets/js/playlist_widget.js
@@ -1,4 +1,5 @@
-var playlist_data = JSON.parse(document.getElementById('playlist_data').innerHTML);
+'use strict';
+var playlist_data = JSON.parse(document.getElementById('playlist_data').textContent);
function add_playlist_video(target) {
var select = target.parentNode.children[0].children[1];
@@ -14,12 +15,12 @@ function add_playlist_video(target) {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status == 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status === 200) {
option.innerText = '✓' + option.innerText;
}
}
- }
+ };
xhr.send('csrf_token=' + playlist_data.csrf_token);
}
@@ -38,12 +39,12 @@ function add_playlist_item(target) {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status != 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status !== 200) {
tile.style.display = '';
}
}
- }
+ };
xhr.send('csrf_token=' + playlist_data.csrf_token);
}
@@ -62,12 +63,12 @@ function remove_playlist_item(target) {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status != 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status !== 200) {
tile.style.display = '';
}
}
- }
+ };
xhr.send('csrf_token=' + playlist_data.csrf_token);
-} \ No newline at end of file
+}
diff --git a/assets/js/silvermine-videojs-quality-selector.min.js b/assets/js/silvermine-videojs-quality-selector.min.js
index 88621e8d..1877047d 100644
--- a/assets/js/silvermine-videojs-quality-selector.min.js
+++ b/assets/js/silvermine-videojs-quality-selector.min.js
@@ -1,4 +1,4 @@
-/*! @silvermine/videojs-quality-selector 2020-03-02 v1.1.2-36-g64d620a-dirty */
+/*! @silvermine/videojs-quality-selector 2022-04-13 v1.1.2-43-gaa06e72-dirty */
-!function u(o,c,a){function l(e,n){if(!c[e]){if(!o[e]){var t="function"==typeof require&&require;if(!n&&t)return t(e,!0);if(s)return s(e,!0);var r=new Error("Cannot find module '"+e+"'");throw r.code="MODULE_NOT_FOUND",r}var i=c[e]={exports:{}};o[e][0].call(i.exports,function(n){return l(o[e][1][n]||n)},i,i.exports,u,o,c,a)}return c[e].exports}for(var s="function"==typeof require&&require,n=0;n<a.length;n++)l(a[n]);return l}({1:[function(n,e,t){!function(){var u=!1,o=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;this.Class=function(){},Class.extend=function(n){var i=this.prototype;u=!0;var e=new this;for(var t in u=!1,n)e[t]="function"==typeof n[t]&&"function"==typeof i[t]&&o.test(n[t])?function(t,r){return function(){var n=this._super;this._super=i[t];var e=r.apply(this,arguments);return this._super=n,e}}(t,n[t]):n[t];function r(){!u&&this.init&&this.init.apply(this,arguments)}return((r.prototype=e).constructor=r).extend=arguments.callee,r},e.exports=Class}()},{}],2:[function(n,J,$){(function(V){!function(){function t(){}var n="object"==typeof self&&self.self===self&&self||"object"==typeof V&&V.global===V&&V||this||{},e=n._,r=Array.prototype,o=Object.prototype,f="undefined"!=typeof Symbol?Symbol.prototype:null,i=r.push,a=r.slice,p=o.toString,u=o.hasOwnProperty,c=Array.isArray,l=Object.keys,s=Object.create,h=function(n){return n instanceof h?n:this instanceof h?void(this._wrapped=n):new h(n)};void 0===$||$.nodeType?n._=h:(void 0!==J&&!J.nodeType&&J.exports&&($=J.exports=h),$._=h),h.VERSION="1.9.1";function d(i,u,n){if(void 0===u)return i;switch(null==n?3:n){case 1:return function(n){return i.call(u,n)};case 3:return function(n,e,t){return i.call(u,n,e,t)};case 4:return function(n,e,t,r){return i.call(u,n,e,t,r)}}return function(){return i.apply(u,arguments)}}function v(n,e,t){return h.iteratee!==y?h.iteratee(n,e):null==n?h.identity:h.isFunction(n)?d(n,e,t):h.isObject(n)&&!h.isArray(n)?h.matcher(n):h.property(n)}var y;h.iteratee=y=function(n,e){return v(n,e,1/0)};function g(i,u){return u=null==u?i.length-1:+u,function(){for(var n=Math.max(arguments.length-u,0),e=Array(n),t=0;t<n;t++)e[t]=arguments[t+u];switch(u){case 0:return i.call(this,e);case 1:return i.call(this,arguments[0],e);case 2:return i.call(this,arguments[0],arguments[1],e)}var r=Array(u+1);for(t=0;t<u;t++)r[t]=arguments[t];return r[u]=e,i.apply(this,r)}}function S(n){if(!h.isObject(n))return{};if(s)return s(n);t.prototype=n;var e=new t;return t.prototype=null,e}function m(e){return function(n){return null==n?void 0:n[e]}}function _(n,e){return null!=n&&u.call(n,e)}function b(n,e){for(var t=e.length,r=0;r<t;r++){if(null==n)return;n=n[e[r]]}return t?n:void 0}function x(n){var e=j(n);return"number"==typeof e&&0<=e&&e<=k}var k=Math.pow(2,53)-1,j=m("length");h.each=h.forEach=function(n,e,t){var r,i;if(e=d(e,t),x(n))for(r=0,i=n.length;r<i;r++)e(n[r],r,n);else{var u=h.keys(n);for(r=0,i=u.length;r<i;r++)e(n[u[r]],u[r],n)}return n},h.map=h.collect=function(n,e,t){e=v(e,t);for(var r=!x(n)&&h.keys(n),i=(r||n).length,u=Array(i),o=0;o<i;o++){var c=r?r[o]:o;u[o]=e(n[c],c,n)}return u};function E(a){return function(n,e,t,r){var i=3<=arguments.length;return function(n,e,t,r){var i=!x(n)&&h.keys(n),u=(i||n).length,o=0<a?0:u-1;for(r||(t=n[i?i[o]:o],o+=a);0<=o&&o<u;o+=a){var c=i?i[o]:o;t=e(t,n[c],c,n)}return t}(n,d(e,r,4),t,i)}}h.reduce=h.foldl=h.inject=E(1),h.reduceRight=h.foldr=E(-1),h.find=h.detect=function(n,e,t){var r=(x(n)?h.findIndex:h.findKey)(n,e,t);if(void 0!==r&&-1!==r)return n[r]},h.filter=h.select=function(n,r,e){var i=[];return r=v(r,e),h.each(n,function(n,e,t){r(n,e,t)&&i.push(n)}),i},h.reject=function(n,e,t){return h.filter(n,h.negate(v(e)),t)},h.every=h.all=function(n,e,t){e=v(e,t);for(var r=!x(n)&&h.keys(n),i=(r||n).length,u=0;u<i;u++){var o=r?r[u]:u;if(!e(n[o],o,n))return!1}return!0},h.some=h.any=function(n,e,t){e=v(e,t);for(var r=!x(n)&&h.keys(n),i=(r||n).length,u=0;u<i;u++){var o=r?r[u]:u;if(e(n[o],o,n))return!0}return!1},h.contains=h.includes=h.include=function(n,e,t,r){return x(n)||(n=h.values(n)),"number"==typeof t&&!r||(t=0),0<=h.indexOf(n,e,t)},h.invoke=g(function(n,t,r){var i,u;return h.isFunction(t)?u=t:h.isArray(t)&&(i=t.slice(0,-1),t=t[t.length-1]),h.map(n,function(n){var e=u;if(!e){if(i&&i.length&&(n=b(n,i)),null==n)return;e=n[t]}return null==e?e:e.apply(n,r)})}),h.pluck=function(n,e){return h.map(n,h.property(e))},h.where=function(n,e){return h.filter(n,h.matcher(e))},h.findWhere=function(n,e){return h.find(n,h.matcher(e))},h.max=function(n,r,e){var t,i,u=-1/0,o=-1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var c=0,a=(n=x(n)?n:h.values(n)).length;c<a;c++)null!=(t=n[c])&&u<t&&(u=t);else r=v(r,e),h.each(n,function(n,e,t){i=r(n,e,t),(o<i||i===-1/0&&u===-1/0)&&(u=n,o=i)});return u},h.min=function(n,r,e){var t,i,u=1/0,o=1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var c=0,a=(n=x(n)?n:h.values(n)).length;c<a;c++)null!=(t=n[c])&&t<u&&(u=t);else r=v(r,e),h.each(n,function(n,e,t){((i=r(n,e,t))<o||i===1/0&&u===1/0)&&(u=n,o=i)});return u},h.shuffle=function(n){return h.sample(n,1/0)},h.sample=function(n,e,t){if(null==e||t)return x(n)||(n=h.values(n)),n[h.random(n.length-1)];var r=x(n)?h.clone(n):h.values(n),i=j(r);e=Math.max(Math.min(e,i),0);for(var u=i-1,o=0;o<e;o++){var c=h.random(o,u),a=r[o];r[o]=r[c],r[c]=a}return r.slice(0,e)},h.sortBy=function(n,r,e){var i=0;return r=v(r,e),h.pluck(h.map(n,function(n,e,t){return{value:n,index:i++,criteria:r(n,e,t)}}).sort(function(n,e){var t=n.criteria,r=e.criteria;if(t!==r){if(r<t||void 0===t)return 1;if(t<r||void 0===r)return-1}return n.index-e.index}),"value")};function w(o,e){return function(r,i,n){var u=e?[[],[]]:{};return i=v(i,n),h.each(r,function(n,e){var t=i(n,e,r);o(u,n,t)}),u}}h.groupBy=w(function(n,e,t){_(n,t)?n[t].push(e):n[t]=[e]}),h.indexBy=w(function(n,e,t){n[t]=e}),h.countBy=w(function(n,e,t){_(n,t)?n[t]++:n[t]=1});var A=/[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;h.toArray=function(n){return n?h.isArray(n)?a.call(n):h.isString(n)?n.match(A):x(n)?h.map(n,h.identity):h.values(n):[]},h.size=function(n){return null==n?0:x(n)?n.length:h.keys(n).length},h.partition=w(function(n,e,t){n[t?0:1].push(e)},!0),h.first=h.head=h.take=function(n,e,t){return null==n||n.length<1?null==e?void 0:[]:null==e||t?n[0]:h.initial(n,n.length-e)},h.initial=function(n,e,t){return a.call(n,0,Math.max(0,n.length-(null==e||t?1:e)))},h.last=function(n,e,t){return null==n||n.length<1?null==e?void 0:[]:null==e||t?n[n.length-1]:h.rest(n,Math.max(0,n.length-e))},h.rest=h.tail=h.drop=function(n,e,t){return a.call(n,null==e||t?1:e)},h.compact=function(n){return h.filter(n,Boolean)};var T=function(n,e,t,r){for(var i=(r=r||[]).length,u=0,o=j(n);u<o;u++){var c=n[u];if(x(c)&&(h.isArray(c)||h.isArguments(c)))if(e)for(var a=0,l=c.length;a<l;)r[i++]=c[a++];else T(c,e,t,r),i=r.length;else t||(r[i++]=c)}return r};h.flatten=function(n,e){return T(n,e,!1)},h.without=g(function(n,e){return h.difference(n,e)}),h.uniq=h.unique=function(n,e,t,r){h.isBoolean(e)||(r=t,t=e,e=!1),null!=t&&(t=v(t,r));for(var i=[],u=[],o=0,c=j(n);o<c;o++){var a=n[o],l=t?t(a,o,n):a;e&&!t?(o&&u===l||i.push(a),u=l):t?h.contains(u,l)||(u.push(l),i.push(a)):h.contains(i,a)||i.push(a)}return i},h.union=g(function(n){return h.uniq(T(n,!0,!0))}),h.intersection=function(n){for(var e=[],t=arguments.length,r=0,i=j(n);r<i;r++){var u=n[r];if(!h.contains(e,u)){var o;for(o=1;o<t&&h.contains(arguments[o],u);o++);o===t&&e.push(u)}}return e},h.difference=g(function(n,e){return e=T(e,!0,!0),h.filter(n,function(n){return!h.contains(e,n)})}),h.unzip=function(n){for(var e=n&&h.max(n,j).length||0,t=Array(e),r=0;r<e;r++)t[r]=h.pluck(n,r);return t},h.zip=g(h.unzip),h.object=function(n,e){for(var t={},r=0,i=j(n);r<i;r++)e?t[n[r]]=e[r]:t[n[r][0]]=n[r][1];return t};function O(u){return function(n,e,t){e=v(e,t);for(var r=j(n),i=0<u?0:r-1;0<=i&&i<r;i+=u)if(e(n[i],i,n))return i;return-1}}h.findIndex=O(1),h.findLastIndex=O(-1),h.sortedIndex=function(n,e,t,r){for(var i=(t=v(t,r,1))(e),u=0,o=j(n);u<o;){var c=Math.floor((u+o)/2);t(n[c])<i?u=c+1:o=c}return u};function C(u,o,c){return function(n,e,t){var r=0,i=j(n);if("number"==typeof t)0<u?r=0<=t?t:Math.max(t+i,r):i=0<=t?Math.min(t+1,i):t+i+1;else if(c&&t&&i)return n[t=c(n,e)]===e?t:-1;if(e!=e)return 0<=(t=o(a.call(n,r,i),h.isNaN))?t+r:-1;for(t=0<u?r:i-1;0<=t&&t<i;t+=u)if(n[t]===e)return t;return-1}}h.indexOf=C(1,h.findIndex,h.sortedIndex),h.lastIndexOf=C(-1,h.findLastIndex),h.range=function(n,e,t){null==e&&(e=n||0,n=0),t=t||(e<n?-1:1);for(var r=Math.max(Math.ceil((e-n)/t),0),i=Array(r),u=0;u<r;u++,n+=t)i[u]=n;return i},h.chunk=function(n,e){if(null==e||e<1)return[];for(var t=[],r=0,i=n.length;r<i;)t.push(a.call(n,r,r+=e));return t};function I(n,e,t,r,i){if(!(r instanceof e))return n.apply(t,i);var u=S(n.prototype),o=n.apply(u,i);return h.isObject(o)?o:u}h.bind=g(function(e,t,r){if(!h.isFunction(e))throw new TypeError("Bind must be called on a function");var i=g(function(n){return I(e,i,t,this,r.concat(n))});return i}),h.partial=g(function(i,u){var o=h.partial.placeholder,c=function(){for(var n=0,e=u.length,t=Array(e),r=0;r<e;r++)t[r]=u[r]===o?arguments[n++]:u[r];for(;n<arguments.length;)t.push(arguments[n++]);return I(i,c,this,this,t)};return c}),(h.partial.placeholder=h).bindAll=g(function(n,e){var t=(e=T(e,!1,!1)).length;if(t<1)throw new Error("bindAll must be passed function names");for(;t--;){var r=e[t];n[r]=h.bind(n[r],n)}}),h.memoize=function(r,i){var u=function(n){var e=u.cache,t=""+(i?i.apply(this,arguments):n);return _(e,t)||(e[t]=r.apply(this,arguments)),e[t]};return u.cache={},u},h.delay=g(function(n,e,t){return setTimeout(function(){return n.apply(null,t)},e)}),h.defer=h.partial(h.delay,h,1),h.throttle=function(t,r,i){var u,o,c,a,l=0;i=i||{};function s(){l=!1===i.leading?0:h.now(),u=null,a=t.apply(o,c),u||(o=c=null)}function n(){var n=h.now();l||!1!==i.leading||(l=n);var e=r-(n-l);return o=this,c=arguments,e<=0||r<e?(u&&(clearTimeout(u),u=null),l=n,a=t.apply(o,c),u||(o=c=null)):u||!1===i.trailing||(u=setTimeout(s,e)),a}return n.cancel=function(){clearTimeout(u),l=0,u=o=c=null},n},h.debounce=function(t,r,i){function u(n,e){o=null,e&&(c=t.apply(n,e))}var o,c,n=g(function(n){if(o&&clearTimeout(o),i){var e=!o;o=setTimeout(u,r),e&&(c=t.apply(this,n))}else o=h.delay(u,r,this,n);return c});return n.cancel=function(){clearTimeout(o),o=null},n},h.wrap=function(n,e){return h.partial(e,n)},h.negate=function(n){return function(){return!n.apply(this,arguments)}},h.compose=function(){var t=arguments,r=t.length-1;return function(){for(var n=r,e=t[r].apply(this,arguments);n--;)e=t[n].call(this,e);return e}},h.after=function(n,e){return function(){if(--n<1)return e.apply(this,arguments)}},h.before=function(n,e){var t;return function(){return 0<--n&&(t=e.apply(this,arguments)),n<=1&&(e=null),t}},h.once=h.partial(h.before,2),h.restArguments=g;function F(n,e){var t=M.length,r=n.constructor,i=h.isFunction(r)&&r.prototype||o,u="constructor";for(_(n,u)&&!h.contains(e,u)&&e.push(u);t--;)(u=M[t])in n&&n[u]!==i[u]&&!h.contains(e,u)&&e.push(u)}var q=!{toString:null}.propertyIsEnumerable("toString"),M=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];h.keys=function(n){if(!h.isObject(n))return[];if(l)return l(n);var e=[];for(var t in n)_(n,t)&&e.push(t);return q&&F(n,e),e},h.allKeys=function(n){if(!h.isObject(n))return[];var e=[];for(var t in n)e.push(t);return q&&F(n,e),e},h.values=function(n){for(var e=h.keys(n),t=e.length,r=Array(t),i=0;i<t;i++)r[i]=n[e[i]];return r},h.mapObject=function(n,e,t){e=v(e,t);for(var r=h.keys(n),i=r.length,u={},o=0;o<i;o++){var c=r[o];u[c]=e(n[c],c,n)}return u},h.pairs=function(n){for(var e=h.keys(n),t=e.length,r=Array(t),i=0;i<t;i++)r[i]=[e[i],n[e[i]]];return r},h.invert=function(n){for(var e={},t=h.keys(n),r=0,i=t.length;r<i;r++)e[n[t[r]]]=t[r];return e},h.functions=h.methods=function(n){var e=[];for(var t in n)h.isFunction(n[t])&&e.push(t);return e.sort()};function N(a,l){return function(n){var e=arguments.length;if(l&&(n=Object(n)),e<2||null==n)return n;for(var t=1;t<e;t++)for(var r=arguments[t],i=a(r),u=i.length,o=0;o<u;o++){var c=i[o];l&&void 0!==n[c]||(n[c]=r[c])}return n}}h.extend=N(h.allKeys),h.extendOwn=h.assign=N(h.keys),h.findKey=function(n,e,t){e=v(e,t);for(var r,i=h.keys(n),u=0,o=i.length;u<o;u++)if(e(n[r=i[u]],r,n))return r};function R(n,e,t){return e in t}var Q,L;h.pick=g(function(n,e){var t={},r=e[0];if(null==n)return t;h.isFunction(r)?(1<e.length&&(r=d(r,e[1])),e=h.allKeys(n)):(r=R,e=T(e,!1,!1),n=Object(n));for(var i=0,u=e.length;i<u;i++){var o=e[i],c=n[o];r(c,o,n)&&(t[o]=c)}return t}),h.omit=g(function(n,t){var e,r=t[0];return h.isFunction(r)?(r=h.negate(r),1<t.length&&(e=t[1])):(t=h.map(T(t,!1,!1),String),r=function(n,e){return!h.contains(t,e)}),h.pick(n,r,e)}),h.defaults=N(h.allKeys,!0),h.create=function(n,e){var t=S(n);return e&&h.extendOwn(t,e),t},h.clone=function(n){return h.isObject(n)?h.isArray(n)?n.slice():h.extend({},n):n},h.tap=function(n,e){return e(n),n},h.isMatch=function(n,e){var t=h.keys(e),r=t.length;if(null==n)return!r;for(var i=Object(n),u=0;u<r;u++){var o=t[u];if(e[o]!==i[o]||!(o in i))return!1}return!0},Q=function(n,e,t,r){if(n===e)return 0!==n||1/n==1/e;if(null==n||null==e)return!1;if(n!=n)return e!=e;var i=typeof n;return("function"==i||"object"==i||"object"==typeof e)&&L(n,e,t,r)},L=function(n,e,t,r){n instanceof h&&(n=n._wrapped),e instanceof h&&(e=e._wrapped);var i=p.call(n);if(i!==p.call(e))return!1;switch(i){case"[object RegExp]":case"[object String]":return""+n==""+e;case"[object Number]":return+n!=+n?+e!=+e:0==+n?1/+n==1/e:+n==+e;case"[object Date]":case"[object Boolean]":return+n==+e;case"[object Symbol]":return f.valueOf.call(n)===f.valueOf.call(e)}var u="[object Array]"===i;if(!u){if("object"!=typeof n||"object"!=typeof e)return!1;var o=n.constructor,c=e.constructor;if(o!==c&&!(h.isFunction(o)&&o instanceof o&&h.isFunction(c)&&c instanceof c)&&"constructor"in n&&"constructor"in e)return!1}r=r||[];for(var a=(t=t||[]).length;a--;)if(t[a]===n)return r[a]===e;if(t.push(n),r.push(e),u){if((a=n.length)!==e.length)return!1;for(;a--;)if(!Q(n[a],e[a],t,r))return!1}else{var l,s=h.keys(n);if(a=s.length,h.keys(e).length!==a)return!1;for(;a--;)if(l=s[a],!_(e,l)||!Q(n[l],e[l],t,r))return!1}return t.pop(),r.pop(),!0},h.isEqual=function(n,e){return Q(n,e)},h.isEmpty=function(n){return null==n||(x(n)&&(h.isArray(n)||h.isString(n)||h.isArguments(n))?0===n.length:0===h.keys(n).length)},h.isElement=function(n){return!(!n||1!==n.nodeType)},h.isArray=c||function(n){return"[object Array]"===p.call(n)},h.isObject=function(n){var e=typeof n;return"function"==e||"object"==e&&!!n},h.each(["Arguments","Function","String","Number","Date","RegExp","Error","Symbol","Map","WeakMap","Set","WeakSet"],function(e){h["is"+e]=function(n){return p.call(n)==="[object "+e+"]"}}),h.isArguments(arguments)||(h.isArguments=function(n){return _(n,"callee")});var U=n.document&&n.document.childNodes;"function"!=typeof/./&&"object"!=typeof Int8Array&&"function"!=typeof U&&(h.isFunction=function(n){return"function"==typeof n||!1}),h.isFinite=function(n){return!h.isSymbol(n)&&isFinite(n)&&!isNaN(parseFloat(n))},h.isNaN=function(n){return h.isNumber(n)&&isNaN(n)},h.isBoolean=function(n){return!0===n||!1===n||"[object Boolean]"===p.call(n)},h.isNull=function(n){return null===n},h.isUndefined=function(n){return void 0===n},h.has=function(n,e){if(!h.isArray(e))return _(n,e);for(var t=e.length,r=0;r<t;r++){var i=e[r];if(null==n||!u.call(n,i))return!1;n=n[i]}return!!t},h.noConflict=function(){return n._=e,this},h.identity=function(n){return n},h.constant=function(n){return function(){return n}},h.noop=function(){},h.property=function(e){return h.isArray(e)?function(n){return b(n,e)}:m(e)},h.propertyOf=function(e){return null==e?function(){}:function(n){return h.isArray(n)?b(e,n):e[n]}},h.matcher=h.matches=function(e){return e=h.extendOwn({},e),function(n){return h.isMatch(n,e)}},h.times=function(n,e,t){var r=Array(Math.max(0,n));e=d(e,t,1);for(var i=0;i<n;i++)r[i]=e(i);return r},h.random=function(n,e){return null==e&&(e=n,n=0),n+Math.floor(Math.random()*(e-n+1))},h.now=Date.now||function(){return(new Date).getTime()};function D(e){function t(n){return e[n]}var n="(?:"+h.keys(e).join("|")+")",r=RegExp(n),i=RegExp(n,"g");return function(n){return n=null==n?"":""+n,r.test(n)?n.replace(i,t):n}}var P={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},W=h.invert(P);h.escape=D(P),h.unescape=D(W),h.result=function(n,e,t){h.isArray(e)||(e=[e]);var r=e.length;if(!r)return h.isFunction(t)?t.call(n):t;for(var i=0;i<r;i++){var u=null==n?void 0:n[e[i]];void 0===u&&(u=t,i=r),n=h.isFunction(u)?u.call(n):u}return n};var B=0;h.uniqueId=function(n){var e=++B+"";return n?n+e:e},h.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};function Y(n){return"\\"+K[n]}var z=/(.)^/,K={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},G=/\\|'|\r|\n|\u2028|\u2029/g;h.template=function(u,n,e){!n&&e&&(n=e),n=h.defaults({},n,h.templateSettings);var t,r=RegExp([(n.escape||z).source,(n.interpolate||z).source,(n.evaluate||z).source].join("|")+"|$","g"),o=0,c="__p+='";u.replace(r,function(n,e,t,r,i){return c+=u.slice(o,i).replace(G,Y),o=i+n.length,e?c+="'+\n((__t=("+e+"))==null?'':_.escape(__t))+\n'":t?c+="'+\n((__t=("+t+"))==null?'':__t)+\n'":r&&(c+="';\n"+r+"\n__p+='"),n}),c+="';\n",n.variable||(c="with(obj||{}){\n"+c+"}\n"),c="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+c+"return __p;\n";try{t=new Function(n.variable||"obj","_",c)}catch(n){throw n.source=c,n}function i(n){return t.call(this,n,h)}var a=n.variable||"obj";return i.source="function("+a+"){\n"+c+"}",i},h.chain=function(n){var e=h(n);return e._chain=!0,e};function H(n,e){return n._chain?h(e).chain():e}h.mixin=function(t){return h.each(h.functions(t),function(n){var e=h[n]=t[n];h.prototype[n]=function(){var n=[this._wrapped];return i.apply(n,arguments),H(this,e.apply(h,n))}}),h},h.mixin(h),h.each(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];h.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),"shift"!==e&&"splice"!==e||0!==n.length||delete n[0],H(this,n)}}),h.each(["concat","join","slice"],function(n){var e=r[n];h.prototype[n]=function(){return H(this,e.apply(this._wrapped,arguments))}}),h.prototype.value=function(){return this._wrapped},h.prototype.valueOf=h.prototype.toJSON=h.prototype.value,h.prototype.toString=function(){return String(this._wrapped)},"function"==typeof define&&define.amd&&define("underscore",[],function(){return h})}()}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],3:[function(n,e,t){"use strict";var i=n("underscore"),u=n("../events");e.exports=function(n){var r=n.getComponent("MenuItem");return n.extend(r,{constructor:function(n,e){var t=e.source;if(!i.isObject(t))throw new Error('was not provided a "source" object, but rather: '+typeof t);e=i.extend({selectable:!0,label:t.label},e),r.call(this,n,e),this.source=t},handleClick:function(n){r.prototype.handleClick.call(this,n),this.player().trigger(u.QUALITY_REQUESTED,this.source)}})}},{"../events":5,underscore:2}],4:[function(n,e,t){"use strict";var i=n("underscore"),u=n("../events"),o=n("./QualityOption"),c="vjs-quality-changing";e.exports=function(n){var e,r=n.getComponent("MenuButton"),t=o(n);return e=n.extend(r,{constructor:function(t,n){r.call(this,t,n),t.on(u.QUALITY_REQUESTED,function(n,e){this.setSelectedSource(e),t.addClass(c),t.one("loadeddata",function(){t.removeClass(c)})}.bind(this)),t.on(u.PLAYER_SOURCES_CHANGED,function(){this.update()}.bind(this)),t.on(u.QUALITY_SELECTED,function(n,e){this.setSelectedSource(e)}.bind(this)),t.one("ready",function(){this.selectedSrc=t.src(),this.update()}.bind(this)),this.controlText("Open quality selector menu")},setSelectedSource:function(n){var e=n?n.src:void 0;this.selectedSrc!==e&&(this.selectedSrc=e,i.each(this.items,function(n){n.selected(n.source.src===e)}))},createItems:function(){var e=this.player(),n=e.currentSources();return i.map(n,function(n){return new t(e,{source:n,selected:n.src===this.selectedSrc})}.bind(this))},buildWrapperCSSClass:function(){return"vjs-quality-selector "+r.prototype.buildWrapperCSSClass.call(this)}}),n.registerComponent("QualitySelector",e),e}},{"../events":5,"./QualityOption":3,underscore:2}],5:[function(n,e,t){"use strict";e.exports={QUALITY_REQUESTED:"qualityRequested",QUALITY_SELECTED:"qualitySelected",PLAYER_SOURCES_CHANGED:"playerSourcesChanged"}},{}],6:[function(n,e,t){"use strict";var c=n("underscore"),r=n("./events"),i=n("./components/QualitySelector"),u=n("./middleware/SourceInterceptor"),a=n("./util/SafeSeek");e.exports=function(n){n=n||window.videojs,i(n),u(n),n.hook("setup",function(o){o.on(r.QUALITY_REQUESTED,function(n,e){var t=o.currentSources(),r=o.currentTime(),i=o.playbackRate(),u=o.paused();c.each(t,function(n){n.selected=!1}),c.findWhere(t,{src:e.src}).selected=!0,o._qualitySelectorSafeSeek&&o._qualitySelectorSafeSeek.onQualitySelectionChange(),o.src(t),o.ready(function(){o._qualitySelectorSafeSeek&&!o._qualitySelectorSafeSeek.hasFinished()||(o._qualitySelectorSafeSeek=new a(o,r),o.playbackRate(i)),u||o.play()})})})},e.exports.EVENTS=r},{"./components/QualitySelector":4,"./events":5,"./middleware/SourceInterceptor":7,"./util/SafeSeek":9,underscore:2}],7:[function(n,e,t){"use strict";var u=n("underscore"),o=n("../events");e.exports=function(n){n.use("*",function(i){return{setSource:function(n,e){var t,r=i.currentSources();i._qualitySelectorSafeSeek&&i._qualitySelectorSafeSeek.onPlayerSourcesChange(),u.isEqual(r,i._qualitySelectorPreviousSources)||(i.trigger(o.PLAYER_SOURCES_CHANGED,r),i._qualitySelectorPreviousSources=r),t=u.find(r,function(n){return!0===n.selected||"true"===n.selected||"selected"===n.selected})||n,i.trigger(o.QUALITY_SELECTED,t),e(null,t)}}})}},{"../events":5,underscore:2}],8:[function(n,e,t){"use strict";n("./index")()},{"./index":6}],9:[function(n,e,t){"use strict";var r=n("class.extend");e.exports=r.extend({init:function(n,e){this._player=n,this._seekToTime=e,this._hasFinished=!1,this._keepThisInstanceWhenPlayerSourcesChange=!1,this._seekWhenSafe()},_seekWhenSafe:function(){this._player.readyState()<3?(this._seekFn=this._seek.bind(this),this._player.one("canplay",this._seekFn)):this._seek()},onPlayerSourcesChange:function(){this._keepThisInstanceWhenPlayerSourcesChange?this._keepThisInstanceWhenPlayerSourcesChange=!1:this.cancel()},onQualitySelectionChange:function(){this.hasFinished()||(this._keepThisInstanceWhenPlayerSourcesChange=!0)},_seek:function(){this._player.currentTime(this._seekToTime),this._keepThisInstanceWhenPlayerSourcesChange=!1,this._hasFinished=!0},hasFinished:function(){return this._hasFinished},cancel:function(){this._player.off("canplay",this._seekFn),this._keepThisInstanceWhenPlayerSourcesChange=!1,this._hasFinished=!0}})},{"class.extend":1}]},{},[8]);
+!function u(o,c,a){function l(e,n){if(!c[e]){if(!o[e]){var t="function"==typeof require&&require;if(!n&&t)return t(e,!0);if(s)return s(e,!0);var r=new Error("Cannot find module '"+e+"'");throw r.code="MODULE_NOT_FOUND",r}var i=c[e]={exports:{}};o[e][0].call(i.exports,function(n){return l(o[e][1][n]||n)},i,i.exports,u,o,c,a)}return c[e].exports}for(var s="function"==typeof require&&require,n=0;n<a.length;n++)l(a[n]);return l}({1:[function(n,e,t){!function(){var u=!1,o=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;this.Class=function(){},Class.extend=function(n){var i=this.prototype;u=!0;var e=new this;for(var t in u=!1,n)e[t]="function"==typeof n[t]&&"function"==typeof i[t]&&o.test(n[t])?function(t,r){return function(){var n=this._super;this._super=i[t];var e=r.apply(this,arguments);return this._super=n,e}}(t,n[t]):n[t];function r(){!u&&this.init&&this.init.apply(this,arguments)}return((r.prototype=e).constructor=r).extend=arguments.callee,r},e.exports=Class}()},{}],2:[function(n,J,$){(function(V){!function(){function t(){}var n="object"==typeof self&&self.self===self&&self||"object"==typeof V&&V.global===V&&V||this||{},e=n._,r=Array.prototype,o=Object.prototype,f="undefined"!=typeof Symbol?Symbol.prototype:null,i=r.push,a=r.slice,p=o.toString,u=o.hasOwnProperty,c=Array.isArray,l=Object.keys,s=Object.create,h=function(n){return n instanceof h?n:this instanceof h?void(this._wrapped=n):new h(n)};void 0===$||$.nodeType?n._=h:(void 0!==J&&!J.nodeType&&J.exports&&($=J.exports=h),$._=h),h.VERSION="1.9.1";function d(i,u,n){if(void 0===u)return i;switch(null==n?3:n){case 1:return function(n){return i.call(u,n)};case 3:return function(n,e,t){return i.call(u,n,e,t)};case 4:return function(n,e,t,r){return i.call(u,n,e,t,r)}}return function(){return i.apply(u,arguments)}}function y(n,e,t){return h.iteratee!==v?h.iteratee(n,e):null==n?h.identity:h.isFunction(n)?d(n,e,t):h.isObject(n)&&!h.isArray(n)?h.matcher(n):h.property(n)}var v;h.iteratee=v=function(n,e){return y(n,e,1/0)};function g(i,u){return u=null==u?i.length-1:+u,function(){for(var n=Math.max(arguments.length-u,0),e=Array(n),t=0;t<n;t++)e[t]=arguments[t+u];switch(u){case 0:return i.call(this,e);case 1:return i.call(this,arguments[0],e);case 2:return i.call(this,arguments[0],arguments[1],e)}var r=Array(u+1);for(t=0;t<u;t++)r[t]=arguments[t];return r[u]=e,i.apply(this,r)}}function S(n){if(!h.isObject(n))return{};if(s)return s(n);t.prototype=n;var e=new t;return t.prototype=null,e}function m(e){return function(n){return null==n?void 0:n[e]}}function _(n,e){return null!=n&&u.call(n,e)}function b(n,e){for(var t=e.length,r=0;r<t;r++){if(null==n)return;n=n[e[r]]}return t?n:void 0}function x(n){var e=j(n);return"number"==typeof e&&0<=e&&e<=k}var k=Math.pow(2,53)-1,j=m("length");h.each=h.forEach=function(n,e,t){var r,i;if(e=d(e,t),x(n))for(r=0,i=n.length;r<i;r++)e(n[r],r,n);else{var u=h.keys(n);for(r=0,i=u.length;r<i;r++)e(n[u[r]],u[r],n)}return n},h.map=h.collect=function(n,e,t){e=y(e,t);for(var r=!x(n)&&h.keys(n),i=(r||n).length,u=Array(i),o=0;o<i;o++){var c=r?r[o]:o;u[o]=e(n[c],c,n)}return u};function E(a){return function(n,e,t,r){var i=3<=arguments.length;return function(n,e,t,r){var i=!x(n)&&h.keys(n),u=(i||n).length,o=0<a?0:u-1;for(r||(t=n[i?i[o]:o],o+=a);0<=o&&o<u;o+=a){var c=i?i[o]:o;t=e(t,n[c],c,n)}return t}(n,d(e,r,4),t,i)}}h.reduce=h.foldl=h.inject=E(1),h.reduceRight=h.foldr=E(-1),h.find=h.detect=function(n,e,t){var r=(x(n)?h.findIndex:h.findKey)(n,e,t);if(void 0!==r&&-1!==r)return n[r]},h.filter=h.select=function(n,r,e){var i=[];return r=y(r,e),h.each(n,function(n,e,t){r(n,e,t)&&i.push(n)}),i},h.reject=function(n,e,t){return h.filter(n,h.negate(y(e)),t)},h.every=h.all=function(n,e,t){e=y(e,t);for(var r=!x(n)&&h.keys(n),i=(r||n).length,u=0;u<i;u++){var o=r?r[u]:u;if(!e(n[o],o,n))return!1}return!0},h.some=h.any=function(n,e,t){e=y(e,t);for(var r=!x(n)&&h.keys(n),i=(r||n).length,u=0;u<i;u++){var o=r?r[u]:u;if(e(n[o],o,n))return!0}return!1},h.contains=h.includes=h.include=function(n,e,t,r){return x(n)||(n=h.values(n)),"number"==typeof t&&!r||(t=0),0<=h.indexOf(n,e,t)},h.invoke=g(function(n,t,r){var i,u;return h.isFunction(t)?u=t:h.isArray(t)&&(i=t.slice(0,-1),t=t[t.length-1]),h.map(n,function(n){var e=u;if(!e){if(i&&i.length&&(n=b(n,i)),null==n)return;e=n[t]}return null==e?e:e.apply(n,r)})}),h.pluck=function(n,e){return h.map(n,h.property(e))},h.where=function(n,e){return h.filter(n,h.matcher(e))},h.findWhere=function(n,e){return h.find(n,h.matcher(e))},h.max=function(n,r,e){var t,i,u=-1/0,o=-1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var c=0,a=(n=x(n)?n:h.values(n)).length;c<a;c++)null!=(t=n[c])&&u<t&&(u=t);else r=y(r,e),h.each(n,function(n,e,t){i=r(n,e,t),(o<i||i===-1/0&&u===-1/0)&&(u=n,o=i)});return u},h.min=function(n,r,e){var t,i,u=1/0,o=1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var c=0,a=(n=x(n)?n:h.values(n)).length;c<a;c++)null!=(t=n[c])&&t<u&&(u=t);else r=y(r,e),h.each(n,function(n,e,t){((i=r(n,e,t))<o||i===1/0&&u===1/0)&&(u=n,o=i)});return u},h.shuffle=function(n){return h.sample(n,1/0)},h.sample=function(n,e,t){if(null==e||t)return x(n)||(n=h.values(n)),n[h.random(n.length-1)];var r=x(n)?h.clone(n):h.values(n),i=j(r);e=Math.max(Math.min(e,i),0);for(var u=i-1,o=0;o<e;o++){var c=h.random(o,u),a=r[o];r[o]=r[c],r[c]=a}return r.slice(0,e)},h.sortBy=function(n,r,e){var i=0;return r=y(r,e),h.pluck(h.map(n,function(n,e,t){return{value:n,index:i++,criteria:r(n,e,t)}}).sort(function(n,e){var t=n.criteria,r=e.criteria;if(t!==r){if(r<t||void 0===t)return 1;if(t<r||void 0===r)return-1}return n.index-e.index}),"value")};function w(o,e){return function(r,i,n){var u=e?[[],[]]:{};return i=y(i,n),h.each(r,function(n,e){var t=i(n,e,r);o(u,n,t)}),u}}h.groupBy=w(function(n,e,t){_(n,t)?n[t].push(e):n[t]=[e]}),h.indexBy=w(function(n,e,t){n[t]=e}),h.countBy=w(function(n,e,t){_(n,t)?n[t]++:n[t]=1});var A=/[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;h.toArray=function(n){return n?h.isArray(n)?a.call(n):h.isString(n)?n.match(A):x(n)?h.map(n,h.identity):h.values(n):[]},h.size=function(n){return null==n?0:x(n)?n.length:h.keys(n).length},h.partition=w(function(n,e,t){n[t?0:1].push(e)},!0),h.first=h.head=h.take=function(n,e,t){return null==n||n.length<1?null==e?void 0:[]:null==e||t?n[0]:h.initial(n,n.length-e)},h.initial=function(n,e,t){return a.call(n,0,Math.max(0,n.length-(null==e||t?1:e)))},h.last=function(n,e,t){return null==n||n.length<1?null==e?void 0:[]:null==e||t?n[n.length-1]:h.rest(n,Math.max(0,n.length-e))},h.rest=h.tail=h.drop=function(n,e,t){return a.call(n,null==e||t?1:e)},h.compact=function(n){return h.filter(n,Boolean)};var T=function(n,e,t,r){for(var i=(r=r||[]).length,u=0,o=j(n);u<o;u++){var c=n[u];if(x(c)&&(h.isArray(c)||h.isArguments(c)))if(e)for(var a=0,l=c.length;a<l;)r[i++]=c[a++];else T(c,e,t,r),i=r.length;else t||(r[i++]=c)}return r};h.flatten=function(n,e){return T(n,e,!1)},h.without=g(function(n,e){return h.difference(n,e)}),h.uniq=h.unique=function(n,e,t,r){h.isBoolean(e)||(r=t,t=e,e=!1),null!=t&&(t=y(t,r));for(var i=[],u=[],o=0,c=j(n);o<c;o++){var a=n[o],l=t?t(a,o,n):a;e&&!t?(o&&u===l||i.push(a),u=l):t?h.contains(u,l)||(u.push(l),i.push(a)):h.contains(i,a)||i.push(a)}return i},h.union=g(function(n){return h.uniq(T(n,!0,!0))}),h.intersection=function(n){for(var e=[],t=arguments.length,r=0,i=j(n);r<i;r++){var u=n[r];if(!h.contains(e,u)){var o;for(o=1;o<t&&h.contains(arguments[o],u);o++);o===t&&e.push(u)}}return e},h.difference=g(function(n,e){return e=T(e,!0,!0),h.filter(n,function(n){return!h.contains(e,n)})}),h.unzip=function(n){for(var e=n&&h.max(n,j).length||0,t=Array(e),r=0;r<e;r++)t[r]=h.pluck(n,r);return t},h.zip=g(h.unzip),h.object=function(n,e){for(var t={},r=0,i=j(n);r<i;r++)e?t[n[r]]=e[r]:t[n[r][0]]=n[r][1];return t};function O(u){return function(n,e,t){e=y(e,t);for(var r=j(n),i=0<u?0:r-1;0<=i&&i<r;i+=u)if(e(n[i],i,n))return i;return-1}}h.findIndex=O(1),h.findLastIndex=O(-1),h.sortedIndex=function(n,e,t,r){for(var i=(t=y(t,r,1))(e),u=0,o=j(n);u<o;){var c=Math.floor((u+o)/2);t(n[c])<i?u=c+1:o=c}return u};function C(u,o,c){return function(n,e,t){var r=0,i=j(n);if("number"==typeof t)0<u?r=0<=t?t:Math.max(t+i,r):i=0<=t?Math.min(t+1,i):t+i+1;else if(c&&t&&i)return n[t=c(n,e)]===e?t:-1;if(e!=e)return 0<=(t=o(a.call(n,r,i),h.isNaN))?t+r:-1;for(t=0<u?r:i-1;0<=t&&t<i;t+=u)if(n[t]===e)return t;return-1}}h.indexOf=C(1,h.findIndex,h.sortedIndex),h.lastIndexOf=C(-1,h.findLastIndex),h.range=function(n,e,t){null==e&&(e=n||0,n=0),t=t||(e<n?-1:1);for(var r=Math.max(Math.ceil((e-n)/t),0),i=Array(r),u=0;u<r;u++,n+=t)i[u]=n;return i},h.chunk=function(n,e){if(null==e||e<1)return[];for(var t=[],r=0,i=n.length;r<i;)t.push(a.call(n,r,r+=e));return t};function I(n,e,t,r,i){if(!(r instanceof e))return n.apply(t,i);var u=S(n.prototype),o=n.apply(u,i);return h.isObject(o)?o:u}h.bind=g(function(e,t,r){if(!h.isFunction(e))throw new TypeError("Bind must be called on a function");var i=g(function(n){return I(e,i,t,this,r.concat(n))});return i}),h.partial=g(function(i,u){var o=h.partial.placeholder,c=function(){for(var n=0,e=u.length,t=Array(e),r=0;r<e;r++)t[r]=u[r]===o?arguments[n++]:u[r];for(;n<arguments.length;)t.push(arguments[n++]);return I(i,c,this,this,t)};return c}),(h.partial.placeholder=h).bindAll=g(function(n,e){var t=(e=T(e,!1,!1)).length;if(t<1)throw new Error("bindAll must be passed function names");for(;t--;){var r=e[t];n[r]=h.bind(n[r],n)}}),h.memoize=function(r,i){var u=function(n){var e=u.cache,t=""+(i?i.apply(this,arguments):n);return _(e,t)||(e[t]=r.apply(this,arguments)),e[t]};return u.cache={},u},h.delay=g(function(n,e,t){return setTimeout(function(){return n.apply(null,t)},e)}),h.defer=h.partial(h.delay,h,1),h.throttle=function(t,r,i){var u,o,c,a,l=0;i=i||{};function s(){l=!1===i.leading?0:h.now(),u=null,a=t.apply(o,c),u||(o=c=null)}function n(){var n=h.now();l||!1!==i.leading||(l=n);var e=r-(n-l);return o=this,c=arguments,e<=0||r<e?(u&&(clearTimeout(u),u=null),l=n,a=t.apply(o,c),u||(o=c=null)):u||!1===i.trailing||(u=setTimeout(s,e)),a}return n.cancel=function(){clearTimeout(u),l=0,u=o=c=null},n},h.debounce=function(t,r,i){function u(n,e){o=null,e&&(c=t.apply(n,e))}var o,c,n=g(function(n){if(o&&clearTimeout(o),i){var e=!o;o=setTimeout(u,r),e&&(c=t.apply(this,n))}else o=h.delay(u,r,this,n);return c});return n.cancel=function(){clearTimeout(o),o=null},n},h.wrap=function(n,e){return h.partial(e,n)},h.negate=function(n){return function(){return!n.apply(this,arguments)}},h.compose=function(){var t=arguments,r=t.length-1;return function(){for(var n=r,e=t[r].apply(this,arguments);n--;)e=t[n].call(this,e);return e}},h.after=function(n,e){return function(){if(--n<1)return e.apply(this,arguments)}},h.before=function(n,e){var t;return function(){return 0<--n&&(t=e.apply(this,arguments)),n<=1&&(e=null),t}},h.once=h.partial(h.before,2),h.restArguments=g;function F(n,e){var t=M.length,r=n.constructor,i=h.isFunction(r)&&r.prototype||o,u="constructor";for(_(n,u)&&!h.contains(e,u)&&e.push(u);t--;)(u=M[t])in n&&n[u]!==i[u]&&!h.contains(e,u)&&e.push(u)}var q=!{toString:null}.propertyIsEnumerable("toString"),M=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];h.keys=function(n){if(!h.isObject(n))return[];if(l)return l(n);var e=[];for(var t in n)_(n,t)&&e.push(t);return q&&F(n,e),e},h.allKeys=function(n){if(!h.isObject(n))return[];var e=[];for(var t in n)e.push(t);return q&&F(n,e),e},h.values=function(n){for(var e=h.keys(n),t=e.length,r=Array(t),i=0;i<t;i++)r[i]=n[e[i]];return r},h.mapObject=function(n,e,t){e=y(e,t);for(var r=h.keys(n),i=r.length,u={},o=0;o<i;o++){var c=r[o];u[c]=e(n[c],c,n)}return u},h.pairs=function(n){for(var e=h.keys(n),t=e.length,r=Array(t),i=0;i<t;i++)r[i]=[e[i],n[e[i]]];return r},h.invert=function(n){for(var e={},t=h.keys(n),r=0,i=t.length;r<i;r++)e[n[t[r]]]=t[r];return e},h.functions=h.methods=function(n){var e=[];for(var t in n)h.isFunction(n[t])&&e.push(t);return e.sort()};function N(a,l){return function(n){var e=arguments.length;if(l&&(n=Object(n)),e<2||null==n)return n;for(var t=1;t<e;t++)for(var r=arguments[t],i=a(r),u=i.length,o=0;o<u;o++){var c=i[o];l&&void 0!==n[c]||(n[c]=r[c])}return n}}h.extend=N(h.allKeys),h.extendOwn=h.assign=N(h.keys),h.findKey=function(n,e,t){e=y(e,t);for(var r,i=h.keys(n),u=0,o=i.length;u<o;u++)if(e(n[r=i[u]],r,n))return r};function R(n,e,t){return e in t}var Q,L;h.pick=g(function(n,e){var t={},r=e[0];if(null==n)return t;h.isFunction(r)?(1<e.length&&(r=d(r,e[1])),e=h.allKeys(n)):(r=R,e=T(e,!1,!1),n=Object(n));for(var i=0,u=e.length;i<u;i++){var o=e[i],c=n[o];r(c,o,n)&&(t[o]=c)}return t}),h.omit=g(function(n,t){var e,r=t[0];return h.isFunction(r)?(r=h.negate(r),1<t.length&&(e=t[1])):(t=h.map(T(t,!1,!1),String),r=function(n,e){return!h.contains(t,e)}),h.pick(n,r,e)}),h.defaults=N(h.allKeys,!0),h.create=function(n,e){var t=S(n);return e&&h.extendOwn(t,e),t},h.clone=function(n){return h.isObject(n)?h.isArray(n)?n.slice():h.extend({},n):n},h.tap=function(n,e){return e(n),n},h.isMatch=function(n,e){var t=h.keys(e),r=t.length;if(null==n)return!r;for(var i=Object(n),u=0;u<r;u++){var o=t[u];if(e[o]!==i[o]||!(o in i))return!1}return!0},Q=function(n,e,t,r){if(n===e)return 0!==n||1/n==1/e;if(null==n||null==e)return!1;if(n!=n)return e!=e;var i=typeof n;return("function"==i||"object"==i||"object"==typeof e)&&L(n,e,t,r)},L=function(n,e,t,r){n instanceof h&&(n=n._wrapped),e instanceof h&&(e=e._wrapped);var i=p.call(n);if(i!==p.call(e))return!1;switch(i){case"[object RegExp]":case"[object String]":return""+n==""+e;case"[object Number]":return+n!=+n?+e!=+e:0==+n?1/+n==1/e:+n==+e;case"[object Date]":case"[object Boolean]":return+n==+e;case"[object Symbol]":return f.valueOf.call(n)===f.valueOf.call(e)}var u="[object Array]"===i;if(!u){if("object"!=typeof n||"object"!=typeof e)return!1;var o=n.constructor,c=e.constructor;if(o!==c&&!(h.isFunction(o)&&o instanceof o&&h.isFunction(c)&&c instanceof c)&&"constructor"in n&&"constructor"in e)return!1}r=r||[];for(var a=(t=t||[]).length;a--;)if(t[a]===n)return r[a]===e;if(t.push(n),r.push(e),u){if((a=n.length)!==e.length)return!1;for(;a--;)if(!Q(n[a],e[a],t,r))return!1}else{var l,s=h.keys(n);if(a=s.length,h.keys(e).length!==a)return!1;for(;a--;)if(l=s[a],!_(e,l)||!Q(n[l],e[l],t,r))return!1}return t.pop(),r.pop(),!0},h.isEqual=function(n,e){return Q(n,e)},h.isEmpty=function(n){return null==n||(x(n)&&(h.isArray(n)||h.isString(n)||h.isArguments(n))?0===n.length:0===h.keys(n).length)},h.isElement=function(n){return!(!n||1!==n.nodeType)},h.isArray=c||function(n){return"[object Array]"===p.call(n)},h.isObject=function(n){var e=typeof n;return"function"==e||"object"==e&&!!n},h.each(["Arguments","Function","String","Number","Date","RegExp","Error","Symbol","Map","WeakMap","Set","WeakSet"],function(e){h["is"+e]=function(n){return p.call(n)==="[object "+e+"]"}}),h.isArguments(arguments)||(h.isArguments=function(n){return _(n,"callee")});var U=n.document&&n.document.childNodes;"function"!=typeof/./&&"object"!=typeof Int8Array&&"function"!=typeof U&&(h.isFunction=function(n){return"function"==typeof n||!1}),h.isFinite=function(n){return!h.isSymbol(n)&&isFinite(n)&&!isNaN(parseFloat(n))},h.isNaN=function(n){return h.isNumber(n)&&isNaN(n)},h.isBoolean=function(n){return!0===n||!1===n||"[object Boolean]"===p.call(n)},h.isNull=function(n){return null===n},h.isUndefined=function(n){return void 0===n},h.has=function(n,e){if(!h.isArray(e))return _(n,e);for(var t=e.length,r=0;r<t;r++){var i=e[r];if(null==n||!u.call(n,i))return!1;n=n[i]}return!!t},h.noConflict=function(){return n._=e,this},h.identity=function(n){return n},h.constant=function(n){return function(){return n}},h.noop=function(){},h.property=function(e){return h.isArray(e)?function(n){return b(n,e)}:m(e)},h.propertyOf=function(e){return null==e?function(){}:function(n){return h.isArray(n)?b(e,n):e[n]}},h.matcher=h.matches=function(e){return e=h.extendOwn({},e),function(n){return h.isMatch(n,e)}},h.times=function(n,e,t){var r=Array(Math.max(0,n));e=d(e,t,1);for(var i=0;i<n;i++)r[i]=e(i);return r},h.random=function(n,e){return null==e&&(e=n,n=0),n+Math.floor(Math.random()*(e-n+1))},h.now=Date.now||function(){return(new Date).getTime()};function D(e){function t(n){return e[n]}var n="(?:"+h.keys(e).join("|")+")",r=RegExp(n),i=RegExp(n,"g");return function(n){return n=null==n?"":""+n,r.test(n)?n.replace(i,t):n}}var P={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},W=h.invert(P);h.escape=D(P),h.unescape=D(W),h.result=function(n,e,t){h.isArray(e)||(e=[e]);var r=e.length;if(!r)return h.isFunction(t)?t.call(n):t;for(var i=0;i<r;i++){var u=null==n?void 0:n[e[i]];void 0===u&&(u=t,i=r),n=h.isFunction(u)?u.call(n):u}return n};var B=0;h.uniqueId=function(n){var e=++B+"";return n?n+e:e},h.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};function Y(n){return"\\"+K[n]}var z=/(.)^/,K={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},G=/\\|'|\r|\n|\u2028|\u2029/g;h.template=function(u,n,e){!n&&e&&(n=e),n=h.defaults({},n,h.templateSettings);var t,r=RegExp([(n.escape||z).source,(n.interpolate||z).source,(n.evaluate||z).source].join("|")+"|$","g"),o=0,c="__p+='";u.replace(r,function(n,e,t,r,i){return c+=u.slice(o,i).replace(G,Y),o=i+n.length,e?c+="'+\n((__t=("+e+"))==null?'':_.escape(__t))+\n'":t?c+="'+\n((__t=("+t+"))==null?'':__t)+\n'":r&&(c+="';\n"+r+"\n__p+='"),n}),c+="';\n",n.variable||(c="with(obj||{}){\n"+c+"}\n"),c="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+c+"return __p;\n";try{t=new Function(n.variable||"obj","_",c)}catch(n){throw n.source=c,n}function i(n){return t.call(this,n,h)}var a=n.variable||"obj";return i.source="function("+a+"){\n"+c+"}",i},h.chain=function(n){var e=h(n);return e._chain=!0,e};function H(n,e){return n._chain?h(e).chain():e}h.mixin=function(t){return h.each(h.functions(t),function(n){var e=h[n]=t[n];h.prototype[n]=function(){var n=[this._wrapped];return i.apply(n,arguments),H(this,e.apply(h,n))}}),h},h.mixin(h),h.each(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=r[e];h.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),"shift"!==e&&"splice"!==e||0!==n.length||delete n[0],H(this,n)}}),h.each(["concat","join","slice"],function(n){var e=r[n];h.prototype[n]=function(){return H(this,e.apply(this._wrapped,arguments))}}),h.prototype.value=function(){return this._wrapped},h.prototype.valueOf=h.prototype.toJSON=h.prototype.value,h.prototype.toString=function(){return String(this._wrapped)},"function"==typeof define&&define.amd&&define("underscore",[],function(){return h})}()}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],3:[function(n,e,t){"use strict";var i=n("underscore"),u=n("../events");e.exports=function(n){var r=n.getComponent("MenuItem");return n.extend(r,{constructor:function(n,e){var t=e.source;if(!i.isObject(t))throw new Error('was not provided a "source" object, but rather: '+typeof t);e=i.extend({selectable:!0,label:t.label},e),r.call(this,n,e),this.source=t},handleClick:function(n){r.prototype.handleClick.call(this,n),this.player().trigger(u.QUALITY_REQUESTED,this.source)}})}},{"../events":5,underscore:2}],4:[function(n,e,t){"use strict";var i=n("underscore"),u=n("../events"),o=n("./QualityOption"),c="vjs-quality-changing";e.exports=function(n){var e,r=n.getComponent("MenuButton"),t=o(n);return e=n.extend(r,{constructor:function(t,n){r.call(this,t,n),t.on(u.QUALITY_REQUESTED,function(n,e){this.setSelectedSource(e),t.addClass(c),t.one("loadeddata",function(){t.removeClass(c)})}.bind(this)),t.on(u.PLAYER_SOURCES_CHANGED,function(){this.update()}.bind(this)),t.on(u.QUALITY_SELECTED,function(n,e){this.setSelectedSource(e)}.bind(this)),t.one("ready",function(){this.selectedSrc=t.src(),this.update()}.bind(this)),this.controlText("Open quality selector menu")},setSelectedSource:function(n){var e=n?n.src:void 0;this.selectedSrc!==e&&(this.selectedSrc=e,i.each(this.items,function(n){n.selected(n.source.src===e)}))},createItems:function(){var e=this.player(),n=e.currentSources();return n=n.filter(function(n){return null==n.hidequalityoption}),i.map(n,function(n){return new t(e,{source:n,selected:n.src===this.selectedSrc})}.bind(this))},buildWrapperCSSClass:function(){return"vjs-quality-selector "+r.prototype.buildWrapperCSSClass.call(this)}}),n.registerComponent("QualitySelector",e),e}},{"../events":5,"./QualityOption":3,underscore:2}],5:[function(n,e,t){"use strict";e.exports={QUALITY_REQUESTED:"qualityRequested",QUALITY_SELECTED:"qualitySelected",PLAYER_SOURCES_CHANGED:"playerSourcesChanged"}},{}],6:[function(n,e,t){"use strict";var c=n("underscore"),r=n("./events"),i=n("./components/QualitySelector"),u=n("./middleware/SourceInterceptor"),a=n("./util/SafeSeek");e.exports=function(n){n=n||window.videojs,i(n),u(n),n.hook("setup",function(o){o.on(r.QUALITY_REQUESTED,function(n,e){var t=o.currentSources(),r=o.currentTime(),i=o.playbackRate(),u=o.paused();c.each(t,function(n){n.selected=!1}),c.findWhere(t,{src:e.src}).selected=!0,o._qualitySelectorSafeSeek&&o._qualitySelectorSafeSeek.onQualitySelectionChange(),o.src(t),o.ready(function(){o._qualitySelectorSafeSeek&&!o._qualitySelectorSafeSeek.hasFinished()||(o._qualitySelectorSafeSeek=new a(o,r),o.playbackRate(i)),u||o.play()})})})},e.exports.EVENTS=r},{"./components/QualitySelector":4,"./events":5,"./middleware/SourceInterceptor":7,"./util/SafeSeek":9,underscore:2}],7:[function(n,e,t){"use strict";var u=n("underscore"),o=n("../events");e.exports=function(n){n.use("*",function(i){return{setSource:function(n,e){var t,r=i.currentSources();i._qualitySelectorSafeSeek&&i._qualitySelectorSafeSeek.onPlayerSourcesChange(),u.isEqual(r,i._qualitySelectorPreviousSources)||(i.trigger(o.PLAYER_SOURCES_CHANGED,r),i._qualitySelectorPreviousSources=r),t=u.find(r,function(n){return!0===n.selected||"true"===n.selected||"selected"===n.selected})||n,i.trigger(o.QUALITY_SELECTED,t),e(null,t)}}})}},{"../events":5,underscore:2}],8:[function(n,e,t){"use strict";n("./index")()},{"./index":6}],9:[function(n,e,t){"use strict";var r=n("class.extend");e.exports=r.extend({init:function(n,e){this._player=n,this._seekToTime=e,this._hasFinished=!1,this._keepThisInstanceWhenPlayerSourcesChange=!1,this._seekWhenSafe()},_seekWhenSafe:function(){this._player.readyState()<3?(this._seekFn=this._seek.bind(this),this._player.one("canplay",this._seekFn)):this._seek()},onPlayerSourcesChange:function(){this._keepThisInstanceWhenPlayerSourcesChange?this._keepThisInstanceWhenPlayerSourcesChange=!1:this.cancel()},onQualitySelectionChange:function(){this.hasFinished()||(this._keepThisInstanceWhenPlayerSourcesChange=!0)},_seek:function(){this._player.currentTime(this._seekToTime),this._keepThisInstanceWhenPlayerSourcesChange=!1,this._hasFinished=!0},hasFinished:function(){return this._hasFinished},cancel:function(){this._player.off("canplay",this._seekFn),this._keepThisInstanceWhenPlayerSourcesChange=!1,this._hasFinished=!0}})},{"class.extend":1}]},{},[8]);
//# sourceMappingURL=silvermine-videojs-quality-selector.min.js.map \ No newline at end of file
diff --git a/assets/js/subscribe_widget.js b/assets/js/subscribe_widget.js
index 216c36fe..45ff5706 100644
--- a/assets/js/subscribe_widget.js
+++ b/assets/js/subscribe_widget.js
@@ -1,4 +1,5 @@
-var subscribe_data = JSON.parse(document.getElementById('subscribe_data').innerHTML);
+'use strict';
+var subscribe_data = JSON.parse(document.getElementById('subscribe_data').textContent);
var subscribe_button = document.getElementById('subscribe');
subscribe_button.parentNode['action'] = 'javascript:void(0)';
@@ -9,9 +10,11 @@ if (subscribe_button.getAttribute('data-type') === 'subscribe') {
subscribe_button.onclick = unsubscribe;
}
-function subscribe(retries = 5) {
+function subscribe(retries) {
+ if (retries === undefined) retries = 5;
+
if (retries <= 0) {
- console.log('Failed to subscribe.');
+ console.warn('Failed to subscribe.');
return;
}
@@ -28,30 +31,33 @@ function subscribe(retries = 5) {
subscribe_button.innerHTML = '<b>' + subscribe_data.unsubscribe_text + ' | ' + subscribe_data.sub_count_text + '</b>';
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status != 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status !== 200) {
subscribe_button.onclick = subscribe;
subscribe_button.innerHTML = fallback;
}
}
- }
+ };
xhr.onerror = function () {
- console.log('Subscribing failed... ' + retries + '/5');
- setTimeout(function () { subscribe(retries - 1) }, 1000);
- }
+ console.warn('Subscribing failed... ' + retries + '/5');
+ setTimeout(function () { subscribe(retries - 1); }, 1000);
+ };
xhr.ontimeout = function () {
- console.log('Subscribing failed... ' + retries + '/5');
+ console.warn('Subscribing failed... ' + retries + '/5');
subscribe(retries - 1);
- }
+ };
xhr.send('csrf_token=' + subscribe_data.csrf_token);
}
-function unsubscribe(retries = 5) {
+function unsubscribe(retries) {
+ if (retries === undefined)
+ retries = 5;
+
if (retries <= 0) {
- console.log('Failed to subscribe');
+ console.warn('Failed to subscribe');
return;
}
@@ -68,23 +74,23 @@ function unsubscribe(retries = 5) {
subscribe_button.innerHTML = '<b>' + subscribe_data.subscribe_text + ' | ' + subscribe_data.sub_count_text + '</b>';
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status != 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status !== 200) {
subscribe_button.onclick = unsubscribe;
subscribe_button.innerHTML = fallback;
}
}
- }
+ };
xhr.onerror = function () {
- console.log('Unsubscribing failed... ' + retries + '/5');
- setTimeout(function () { unsubscribe(retries - 1) }, 1000);
- }
+ console.warn('Unsubscribing failed... ' + retries + '/5');
+ setTimeout(function () { unsubscribe(retries - 1); }, 1000);
+ };
xhr.ontimeout = function () {
- console.log('Unsubscribing failed... ' + retries + '/5');
+ console.warn('Unsubscribing failed... ' + retries + '/5');
unsubscribe(retries - 1);
- }
+ };
xhr.send('csrf_token=' + subscribe_data.csrf_token);
}
diff --git a/assets/js/themes.js b/assets/js/themes.js
index 0214a7f0..3f503b38 100644
--- a/assets/js/themes.js
+++ b/assets/js/themes.js
@@ -1,8 +1,9 @@
+'use strict';
var toggle_theme = document.getElementById('toggle_theme');
toggle_theme.href = 'javascript:void(0);';
toggle_theme.addEventListener('click', function () {
- var dark_mode = document.body.classList.contains("light-theme");
+ var dark_mode = document.body.classList.contains('light-theme');
var url = '/toggle_theme?redirect=false';
var xhr = new XMLHttpRequest();
@@ -13,7 +14,7 @@ toggle_theme.addEventListener('click', function () {
set_mode(dark_mode);
try {
window.localStorage.setItem('dark_mode', dark_mode ? 'dark' : 'light');
- } catch {}
+ } catch (e) {}
xhr.send();
});
@@ -29,7 +30,7 @@ window.addEventListener('DOMContentLoaded', function () {
try {
// Update localStorage if dark mode preference changed on preferences page
window.localStorage.setItem('dark_mode', dark_mode);
- } catch {}
+ } catch (e) {}
update_mode(dark_mode);
});
@@ -46,11 +47,11 @@ function scheme_switch (e) {
if (localStorage.getItem('dark_mode')) {
return;
}
- } catch {}
+ } catch (exception) {}
if (e.matches) {
- if (e.media.includes("dark")) {
+ if (e.media.includes('dark')) {
set_mode(true);
- } else if (e.media.includes("light")) {
+ } else if (e.media.includes('light')) {
set_mode(false);
}
}
@@ -77,15 +78,13 @@ function update_mode (mode) {
// If preference for dark mode indicated
set_mode(true);
}
- else if (mode === 'false' /* for backwards compatibility */ || mode === 'light') {
- // If preference for light mode indicated
- set_mode(false);
- }
+ else if (mode === 'false' /* for backwards compatibility */ || mode === 'light') {
+ // If preference for light mode indicated
+ set_mode(false);
+ }
else if (document.getElementById('dark_mode_pref').textContent === '' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
// If no preference indicated here and no preference indicated on the preferences page (backend), but the browser tells us that the operating system has a dark theme
set_mode(true);
}
// else do nothing, falling back to the mode defined by the `dark_mode` preference on the preferences page (backend)
}
-
-
diff --git a/assets/js/watch.js b/assets/js/watch.js
index 1579abf4..29d58be5 100644
--- a/assets/js/watch.js
+++ b/assets/js/watch.js
@@ -1,31 +1,32 @@
-var video_data = JSON.parse(document.getElementById('video_data').innerHTML);
+'use strict';
+var video_data = JSON.parse(document.getElementById('video_data').textContent);
String.prototype.supplant = function (o) {
return this.replace(/{([^{}]*)}/g, function (a, b) {
var r = o[b];
return typeof r === 'string' || typeof r === 'number' ? r : a;
});
-}
+};
function toggle_parent(target) {
- body = target.parentNode.parentNode.children[1];
+ var body = target.parentNode.parentNode.children[1];
if (body.style.display === null || body.style.display === '') {
- target.innerHTML = '[ + ]';
+ target.textContent = '[ + ]';
body.style.display = 'none';
} else {
- target.innerHTML = '[ - ]';
+ target.textContent = '[ − ]';
body.style.display = '';
}
}
function toggle_comments(event) {
var target = event.target;
- body = target.parentNode.parentNode.parentNode.children[1];
+ var body = target.parentNode.parentNode.parentNode.children[1];
if (body.style.display === null || body.style.display === '') {
- target.innerHTML = '[ + ]';
+ target.textContent = '[ + ]';
body.style.display = 'none';
} else {
- target.innerHTML = '[ - ]';
+ target.textContent = '[ − ]';
body.style.display = '';
}
}
@@ -43,13 +44,13 @@ function swap_comments(event) {
function hide_youtube_replies(event) {
var target = event.target;
- sub_text = target.getAttribute('data-inner-text');
- inner_text = target.getAttribute('data-sub-text');
+ var sub_text = target.getAttribute('data-inner-text');
+ var inner_text = target.getAttribute('data-sub-text');
- body = target.parentNode.parentNode.children[1];
+ var body = target.parentNode.parentNode.children[1];
body.style.display = 'none';
- target.innerHTML = sub_text;
+ target.textContent = sub_text;
target.onclick = show_youtube_replies;
target.setAttribute('data-inner-text', inner_text);
target.setAttribute('data-sub-text', sub_text);
@@ -58,13 +59,13 @@ function hide_youtube_replies(event) {
function show_youtube_replies(event) {
var target = event.target;
- sub_text = target.getAttribute('data-inner-text');
- inner_text = target.getAttribute('data-sub-text');
+ var sub_text = target.getAttribute('data-inner-text');
+ var inner_text = target.getAttribute('data-sub-text');
- body = target.parentNode.parentNode.children[1];
+ var body = target.parentNode.parentNode.children[1];
body.style.display = '';
- target.innerHTML = sub_text;
+ target.textContent = sub_text;
target.onclick = hide_youtube_replies;
target.setAttribute('data-inner-text', inner_text);
target.setAttribute('data-sub-text', sub_text);
@@ -116,25 +117,26 @@ function number_with_separator(val) {
}
function get_playlist(plid, retries) {
- if (retries == undefined) retries = 5;
- playlist = document.getElementById('playlist');
+ if (retries === undefined) retries = 5;
+ var playlist = document.getElementById('playlist');
if (retries <= 0) {
- console.log('Failed to pull playlist');
+ console.warn('Failed to pull playlist');
playlist.innerHTML = '';
return;
}
playlist.innerHTML = ' \
<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3> \
- <hr>'
+ <hr>';
+ var plid_url;
if (plid.startsWith('RD')) {
- var plid_url = '/api/v1/mixes/' + plid +
+ plid_url = '/api/v1/mixes/' + plid +
'?continuation=' + video_data.id +
'&format=html&hl=' + video_data.preferences.locale;
} else {
- var plid_url = '/api/v1/playlists/' + plid +
+ plid_url = '/api/v1/playlists/' + plid +
'?index=' + video_data.index +
'&continuation=' + video_data.id +
'&format=html&hl=' + video_data.preferences.locale;
@@ -146,8 +148,8 @@ function get_playlist(plid, retries) {
xhr.open('GET', plid_url, true);
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status == 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status === 200) {
playlist.innerHTML = xhr.response.playlistHtml;
var nextVideo = document.getElementById(xhr.response.nextVideo);
nextVideo.parentNode.parentNode.scrollTop = nextVideo.offsetTop;
@@ -185,35 +187,35 @@ function get_playlist(plid, retries) {
document.getElementById('continue').style.display = '';
}
}
- }
+ };
xhr.onerror = function () {
playlist = document.getElementById('playlist');
playlist.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3><hr>';
- console.log('Pulling playlist timed out... ' + retries + '/5');
- setTimeout(function () { get_playlist(plid, retries - 1) }, 1000);
- }
+ console.warn('Pulling playlist timed out... ' + retries + '/5');
+ setTimeout(function () { get_playlist(plid, retries - 1); }, 1000);
+ };
xhr.ontimeout = function () {
playlist = document.getElementById('playlist');
playlist.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3><hr>';
- console.log('Pulling playlist timed out... ' + retries + '/5');
+ console.warn('Pulling playlist timed out... ' + retries + '/5');
get_playlist(plid, retries - 1);
- }
+ };
xhr.send();
}
function get_reddit_comments(retries) {
- if (retries == undefined) retries = 5;
- comments = document.getElementById('comments');
+ if (retries === undefined) retries = 5;
+ var comments = document.getElementById('comments');
if (retries <= 0) {
- console.log('Failed to pull comments');
+ console.warn('Failed to pull comments');
comments.innerHTML = '';
return;
}
@@ -231,12 +233,12 @@ function get_reddit_comments(retries) {
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status == 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status === 200) {
comments.innerHTML = ' \
<div> \
<h3> \
- <a href="javascript:void(0)">[ - ]</a> \
+ <a href="javascript:void(0)">[ − ]</a> \
{title} \
</h3> \
<p> \
@@ -263,34 +265,34 @@ function get_reddit_comments(retries) {
comments.children[0].children[1].children[0].onclick = swap_comments;
} else {
if (video_data.params.comments[1] === 'youtube') {
- console.log('Pulling comments failed... ' + retries + '/5');
- setTimeout(function () { get_youtube_comments(retries - 1) }, 1000);
+ console.warn('Pulling comments failed... ' + retries + '/5');
+ setTimeout(function () { get_youtube_comments(retries - 1); }, 1000);
} else {
comments.innerHTML = fallback;
}
}
}
- }
+ };
xhr.onerror = function () {
- console.log('Pulling comments failed... ' + retries + '/5');
- setTimeout(function () { get_reddit_comments(retries - 1) }, 1000);
- }
+ console.warn('Pulling comments failed... ' + retries + '/5');
+ setTimeout(function () { get_reddit_comments(retries - 1); }, 1000);
+ };
xhr.ontimeout = function () {
- console.log('Pulling comments failed... ' + retries + '/5');
+ console.warn('Pulling comments failed... ' + retries + '/5');
get_reddit_comments(retries - 1);
- }
+ };
xhr.send();
}
function get_youtube_comments(retries) {
- if (retries == undefined) retries = 5;
- comments = document.getElementById('comments');
+ if (retries === undefined) retries = 5;
+ var comments = document.getElementById('comments');
if (retries <= 0) {
- console.log('Failed to pull comments');
+ console.warn('Failed to pull comments');
comments.innerHTML = '';
return;
}
@@ -309,12 +311,12 @@ function get_youtube_comments(retries) {
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status == 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status === 200) {
comments.innerHTML = ' \
<div> \
<h3> \
- <a href="javascript:void(0)">[ - ]</a> \
+ <a href="javascript:void(0)">[ − ]</a> \
{commentsText} \
</h3> \
<b> \
@@ -336,27 +338,27 @@ function get_youtube_comments(retries) {
comments.children[0].children[1].children[0].onclick = swap_comments;
} else {
if (video_data.params.comments[1] === 'youtube') {
- setTimeout(function () { get_youtube_comments(retries - 1) }, 1000);
+ setTimeout(function () { get_youtube_comments(retries - 1); }, 1000);
} else {
comments.innerHTML = '';
}
}
}
- }
+ };
xhr.onerror = function () {
comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
- console.log('Pulling comments failed... ' + retries + '/5');
- setTimeout(function () { get_youtube_comments(retries - 1) }, 1000);
- }
+ console.warn('Pulling comments failed... ' + retries + '/5');
+ setTimeout(function () { get_youtube_comments(retries - 1); }, 1000);
+ };
xhr.ontimeout = function () {
comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
- console.log('Pulling comments failed... ' + retries + '/5');
+ console.warn('Pulling comments failed... ' + retries + '/5');
get_youtube_comments(retries - 1);
- }
+ };
xhr.send();
}
@@ -373,7 +375,7 @@ function get_youtube_replies(target, load_more, load_replies) {
'?format=html' +
'&hl=' + video_data.preferences.locale +
'&thin_mode=' + video_data.preferences.thin_mode +
- '&continuation=' + continuation
+ '&continuation=' + continuation;
if (load_replies) {
url += '&action=action_get_comment_replies';
}
@@ -383,8 +385,8 @@ function get_youtube_replies(target, load_more, load_replies) {
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status == 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status === 200) {
if (load_more) {
body = body.parentNode.parentNode;
body.removeChild(body.lastElementChild);
@@ -412,12 +414,12 @@ function get_youtube_replies(target, load_more, load_replies) {
body.innerHTML = fallback;
}
}
- }
+ };
xhr.ontimeout = function () {
- console.log('Pulling comments failed.');
+ console.warn('Pulling comments failed.');
body.innerHTML = fallback;
- }
+ };
xhr.send();
}
@@ -461,7 +463,7 @@ window.addEventListener('load', function (e) {
} else if (video_data.params.comments[1] === 'reddit') {
get_reddit_comments();
} else {
- comments = document.getElementById('comments');
+ var comments = document.getElementById('comments');
comments.innerHTML = '';
}
});
diff --git a/assets/js/watched_widget.js b/assets/js/watched_widget.js
index ba741974..87989a79 100644
--- a/assets/js/watched_widget.js
+++ b/assets/js/watched_widget.js
@@ -1,4 +1,5 @@
-var watched_data = JSON.parse(document.getElementById('watched_data').innerHTML);
+'use strict';
+var watched_data = JSON.parse(document.getElementById('watched_data').textContent);
function mark_watched(target) {
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
@@ -13,12 +14,12 @@ function mark_watched(target) {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status != 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status !== 200) {
tile.style.display = '';
}
}
- }
+ };
xhr.send('csrf_token=' + watched_data.csrf_token);
}
@@ -26,7 +27,7 @@ function mark_watched(target) {
function mark_unwatched(target) {
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
tile.style.display = 'none';
- var count = document.getElementById('count')
+ var count = document.getElementById('count');
count.innerText = count.innerText - 1;
var url = '/watch_ajax?action_mark_unwatched=1&redirect=false' +
@@ -38,13 +39,13 @@ function mark_unwatched(target) {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
- if (xhr.readyState == 4) {
- if (xhr.status != 200) {
+ if (xhr.readyState === 4) {
+ if (xhr.status !== 200) {
count.innerText = count.innerText - 1 + 2;
tile.style.display = '';
}
}
- }
+ };
xhr.send('csrf_token=' + watched_data.csrf_token);
-} \ No newline at end of file
+}
diff --git a/docker/Dockerfile b/docker/Dockerfile
index df35a179..1346f6eb 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,4 +1,4 @@
-FROM crystallang/crystal:1.2.2-alpine AS builder
+FROM crystallang/crystal:1.4.1-alpine AS builder
RUN apk add --no-cache sqlite-static yaml-static
ARG release
diff --git a/docker/Dockerfile.arm64 b/docker/Dockerfile.arm64
index 5f4d3793..75cab819 100644
--- a/docker/Dockerfile.arm64
+++ b/docker/Dockerfile.arm64
@@ -1,5 +1,5 @@
-FROM alpine:3.15 AS builder
-RUN apk add --no-cache 'crystal=1.2.2-r0' shards sqlite-static yaml-static yaml-dev libxml2-dev zlib-static openssl-libs-static openssl-dev musl-dev
+FROM alpine:edge AS builder
+RUN apk add --no-cache 'crystal=1.4.1-r0' shards sqlite-static yaml-static yaml-dev libxml2-dev zlib-static openssl-libs-static openssl-dev musl-dev
ARG release
@@ -34,7 +34,7 @@ RUN if [ ${release} == 1 ] ; then \
--link-flags "-lxml2 -llzma"; \
fi
-FROM alpine:3.15
+FROM alpine:edge
RUN apk add --no-cache librsvg ttf-opensans
WORKDIR /invidious
RUN addgroup -g 1000 -S invidious && \
diff --git a/locales/ar.json b/locales/ar.json
index f009fd46..01c9bbb9 100644
--- a/locales/ar.json
+++ b/locales/ar.json
@@ -141,7 +141,6 @@
"Show less": "عرض اقل",
"Watch on YouTube": "مشاهدة الفيديو على اليوتيوب",
"Switch Invidious Instance": "تبديل المثيل Invidious",
- "Broken? Try another Invidious Instance": "معطل؟ جرب مثيل Invidious آخر",
"Hide annotations": "إخفاء الملاحظات في الفيديو",
"Show annotations": "عرض الملاحظات في الفيديو",
"Genre: ": "النوع: ",
@@ -355,7 +354,6 @@
"search_filters_features_option_four_k": "4k",
"search_filters_features_option_location": "الأماكن",
"search_filters_features_option_hdr": "وضع التباين العالي",
- "search_filters_label": "معامل الفرز",
"Current version: ": "الإصدار الحالي: ",
"next_steps_error_message": "بعد ذلك يجب أن تحاول: ",
"next_steps_error_message_refresh": "تحديث",
@@ -459,5 +457,6 @@
"Portuguese (Brazil)": "البرتغالية (البرازيل)",
"Russian (auto-generated)": "الروسية (منشأة تلقائيا)",
"Spanish (Spain)": "الإسبانية (إسبانيا)",
- "crash_page_search_issue": "بحثت عن <a href=\"`x`\"> المشكلات الموجودة على GitHub </a>"
+ "crash_page_search_issue": "بحثت عن <a href=\"`x`\"> المشكلات الموجودة على GitHub </a>",
+ "search_filters_title": "معامل الفرز"
}
diff --git a/locales/cs.json b/locales/cs.json
index f8af17d2..318866b1 100644
--- a/locales/cs.json
+++ b/locales/cs.json
@@ -262,29 +262,28 @@
"Video mode": "Videový režim",
"Videos": "Videa",
"Community": "Komunita",
- "search_filters_sort_option_rating": "hodnocení",
- "search_filters_sort_option_date": "datum",
- "search_filters_sort_option_views": "zhlédnutí",
- "search_filters_duration_label": "délka",
- "search_filters_date_option_hour": "hodina",
- "search_filters_date_option_today": "dnes",
- "search_filters_date_option_week": "týden",
- "search_filters_date_option_month": "měsíc",
- "search_filters_date_option_year": "rok",
- "search_filters_type_option_video": "video",
- "search_filters_type_option_channel": "kanál",
- "search_filters_type_option_playlist": "playlist",
- "search_filters_type_option_movie": "film",
- "search_filters_type_option_show": "zobrazit",
+ "search_filters_sort_option_rating": "Hodnocení",
+ "search_filters_sort_option_date": "Datum nahrání",
+ "search_filters_sort_option_views": "Počet zhlédnutí",
+ "search_filters_duration_label": "Délka",
+ "search_filters_date_option_hour": "Poslední hodina",
+ "search_filters_date_option_today": "Dnes",
+ "search_filters_date_option_week": "Tento týden",
+ "search_filters_date_option_month": "Tento měsíc",
+ "search_filters_date_option_year": "Tento rok",
+ "search_filters_type_option_video": "Video",
+ "search_filters_type_option_channel": "Kanál",
+ "search_filters_type_option_playlist": "Playlist",
+ "search_filters_type_option_movie": "Film",
+ "search_filters_type_option_show": "Seriál",
"search_filters_features_option_hd": "HD",
- "search_filters_features_option_subtitles": "titulky",
+ "search_filters_features_option_subtitles": "Titulky",
"search_filters_features_option_c_commons": "Creative Commons",
"search_filters_features_option_three_d": "3D",
- "search_filters_features_option_live": "živě",
- "search_filters_features_option_four_k": "4k",
- "search_filters_features_option_location": "umístění",
+ "search_filters_features_option_live": "Živě",
+ "search_filters_features_option_four_k": "4K",
+ "search_filters_features_option_location": "Umístění",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "Filtr",
"generic_count_days_0": "{{count}} dnem",
"generic_count_days_1": "{{count}} dny",
"generic_count_days_2": "{{count}} dny",
@@ -374,20 +373,15 @@
"generic_count_minutes_0": "{{count}} minutou",
"generic_count_minutes_1": "{{count}} minutami",
"generic_count_minutes_2": "{{count}} minutami",
- "short": "Krátké (< 4 minuty)",
- "long": "Dlouhé (> 20 minut)",
"footer_documentation": "Dokumentace",
"next_steps_error_message_refresh": "Obnovit stránku",
"Chinese": "Čínština",
- "360": "360°",
"Dutch (auto-generated)": "Nizozemština (automaticky generováno)",
"Erroneous token": "Chybný token",
"tokens_count_0": "{{count}} token",
"tokens_count_1": "{{count}} tokeny",
"tokens_count_2": "{{count}} tokenů",
"Portuguese (Brazil)": "Portugalština (Brazílie)",
- "content_type": "Typ",
- "sort": "Řazení",
"Token is expired, please try again": "Token vypršel, zkuste to prosím znovu",
"English (United States)": "Angličtina (Spojené státy)",
"Cantonese (Hong Kong)": "Kantonština (Hong Kong)",
@@ -401,7 +395,6 @@
"%A %B %-d, %Y": "%A %B %-d, %Y",
"YouTube comment permalink": "Permanentní odkaz YouTube komentáře",
"permalink": "permalink",
- "purchased": "Zakoupeno",
"footer_original_source_code": "Původní zdrojový kód",
"adminprefs_modified_source_code_url_label": "URL repozitáře s upraveným zdrojovým kódem",
"Video unavailable": "Video není dostupné",
@@ -434,11 +427,9 @@
"Erroneous CAPTCHA": "Chybná CAPTCHA",
"Password is a required field": "Heslo je vyžadované pole",
"preferences_automatic_instance_redirect_label": "Automatické přesměrování instance (fallback na redirect.invidious.io): ",
- "Broken? Try another Invidious Instance": "Je něco rozbité? Zkuste jinou instanci Invidious",
"Switch Invidious Instance": "Přepnout instanci Invidious",
"Empty playlist": "Prázdný playlist",
"footer_source_code": "Zdrojový kód",
- "relevance": "Relevantnost",
"View YouTube comments": "Zobrazit YouTube komentáře",
"Blacklisted regions: ": "Oblasti na černé listině: ",
"Wrong username or password": "Nesprávné uživatelské jméno nebo heslo",
@@ -454,7 +445,6 @@
"Deleted or invalid channel": "Smazaný nebo neplatný kanál",
"This channel does not exist.": "Tento kanál neexistuje.",
"Hidden field \"token\" is a required field": "Skryté pole \"token\" je vyžadované",
- "features": "Funkce",
"Wilson score: ": "Skóre Wilson: ",
"Shared `x`": "Zveřejněno `x`",
"Premieres in `x`": "Premiéra za `x`",
@@ -477,5 +467,24 @@
"Erroneous challenge": "Chybná výzva",
"Premieres `x`": "Premiéra `x`",
"CAPTCHA is a required field": "CAPTCHA je vyžadované pole",
- "`x` ago": "Před `x`"
+ "`x` ago": "Před `x`",
+ "search_message_change_filters_or_query": "Zkuste rozšířit vyhledávaný dotaz a/nebo změnit filtry.",
+ "search_filters_date_option_none": "Jakékoli datum",
+ "search_filters_date_label": "Datum nahrání",
+ "search_filters_type_option_all": "Jakýkoli typ",
+ "search_filters_duration_option_none": "Jakákoli délka",
+ "search_filters_type_label": "Typ",
+ "search_filters_duration_option_short": "Krátká (< 4 minuty)",
+ "search_message_no_results": "Nenalezeny žádné výsledky.",
+ "search_filters_title": "Filtry",
+ "search_filters_duration_option_medium": "Střední (4 - 20 minut)",
+ "search_filters_duration_option_long": "Dlouhá (> 20 minut)",
+ "search_message_use_another_instance": " Můžete také <a href=\"`x`\">hledat na jiné instanci</a>.",
+ "search_filters_features_label": "Vlastnosti",
+ "search_filters_features_option_three_sixty": "360°",
+ "search_filters_features_option_vr180": "VR180",
+ "search_filters_features_option_purchased": "Zakoupeno",
+ "search_filters_sort_label": "Řadit dle",
+ "search_filters_sort_option_relevance": "Relevantnost",
+ "search_filters_apply_button": "Použít vybrané filtry"
}
diff --git a/locales/da.json b/locales/da.json
index 9e221145..4816c2c9 100644
--- a/locales/da.json
+++ b/locales/da.json
@@ -215,7 +215,6 @@
"permalink": "permalink",
"search_filters_sort_option_date": "Upload dato",
"search_filters_features_label": "Funktioner",
- "search_filters_label": "Filter",
"Khmer": "Khmer",
"Finnish": "Finsk",
"search_filters_date_option_week": "Denne uge",
@@ -349,7 +348,6 @@
"next_steps_error_message": "Efter det burde du prøve at: ",
"Sinhala": "Singalesisk (Sinhala)",
"Thai": "Thai",
- "Broken? Try another Invidious Instance": "I stykker? Prøv en anden Invidious instans",
"No such user": "Brugeren findes ikke",
"Token is expired, please try again": "Token er udløbet, prøv igen",
"Catalan": "Catalansk",
@@ -461,5 +459,6 @@
"generic_count_weeks_plural": "{{count}} uger",
"crash_page_you_found_a_bug": "Det ser ud til, at du har fundet en fejl i Invidious!",
"crash_page_read_the_faq": "læs <a href=\"`x`\">Ofte stillede spørgsmål (FAQ)</a>",
- "crash_page_search_issue": "søgte efter <a href=\"`x`\">eksisterende problemer på GitHub</a>"
+ "crash_page_search_issue": "søgte efter <a href=\"`x`\">eksisterende problemer på GitHub</a>",
+ "search_filters_title": "Filter"
}
diff --git a/locales/de.json b/locales/de.json
index b46b7ec4..24b83bb3 100644
--- a/locales/de.json
+++ b/locales/de.json
@@ -141,7 +141,6 @@
"Show less": "Weniger anzeigen",
"Watch on YouTube": "Video auf YouTube ansehen",
"Switch Invidious Instance": "Invidious Instanz wechseln",
- "Broken? Try another Invidious Instance": "Kaputt? Versuche eine andere Invidious Instanz",
"Hide annotations": "Anmerkungen ausblenden",
"Show annotations": "Anmerkungen anzeigen",
"Genre: ": "Genre: ",
@@ -355,7 +354,6 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "Standort",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "Filtern",
"Current version: ": "Aktuelle Version: ",
"next_steps_error_message": "Danach folgendes versuchen: ",
"next_steps_error_message_refresh": "Aktualisieren",
@@ -461,5 +459,6 @@
"Chinese (China)": "Chinesisch (China)",
"Chinese (Taiwan)": "Chinesisch (Taiwan)",
"Korean (auto-generated)": "Koreanisch (automatisch generiert)",
- "Portuguese (auto-generated)": "Portugiesisch (automatisch generiert)"
+ "Portuguese (auto-generated)": "Portugiesisch (automatisch generiert)",
+ "search_filters_title": "Filtern"
}
diff --git a/locales/el.json b/locales/el.json
index 29beb75c..048a520b 100644
--- a/locales/el.json
+++ b/locales/el.json
@@ -405,7 +405,6 @@
"search_filters_date_option_month": "Αυτόν τον μήνα",
"Released under the AGPLv3 on Github.": "Κυκλοφορεί υπό την AGPLv3 στο GitHub.",
"search_filters_sort_label": "Ταξινόμηση κατά",
- "search_filters_label": "Φίλτρο",
"search_filters_type_option_movie": "Ταινία",
"footer_modfied_source_code": "Τροποποιημένος πηγαίος κώδικας",
"search_filters_features_label": "Χαρακτηριστικά",
@@ -449,5 +448,6 @@
"videoinfo_youTube_embed_link": "Ενσωμάτωση",
"videoinfo_invidious_embed_link": "Σύνδεσμος Ενσωμάτωσης",
"search_filters_type_option_show": "Μπάρα προόδου διαβάσματος",
- "preferences_watch_history_label": "Ενεργοποίηση ιστορικού παρακολούθησης: "
+ "preferences_watch_history_label": "Ενεργοποίηση ιστορικού παρακολούθησης: ",
+ "search_filters_title": "Φίλτρο"
}
diff --git a/locales/en-US.json b/locales/en-US.json
index c57670fc..7518c3a1 100644
--- a/locales/en-US.json
+++ b/locales/en-US.json
@@ -68,7 +68,7 @@
"preferences_watch_history_label": "Enable watch history: ",
"preferences_speed_label": "Default speed: ",
"preferences_quality_label": "Preferred video quality: ",
- "preferences_quality_option_dash": "DASH (adaptative quality)",
+ "preferences_quality_option_dash": "DASH (adaptive quality)",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Medium",
"preferences_quality_option_small": "Small",
diff --git a/locales/eo.json b/locales/eo.json
index cd3447a7..40ab5f39 100644
--- a/locales/eo.json
+++ b/locales/eo.json
@@ -141,7 +141,6 @@
"Show less": "Montri malpli",
"Watch on YouTube": "Vidi filmeton en JuTubo",
"Switch Invidious Instance": "Ŝanĝi instalaĵon de Indivious",
- "Broken? Try another Invidious Instance": "Ĉu misfunkcio? Provu alian instalaĵon de Indivious",
"Hide annotations": "Kaŝi prinotojn",
"Show annotations": "Montri prinotojn",
"Genre: ": "Ĝenro: ",
@@ -355,7 +354,6 @@
"search_filters_features_option_four_k": "4k",
"search_filters_features_option_location": "loko",
"search_filters_features_option_hdr": "granddinamikgama",
- "search_filters_label": "filtri",
"Current version: ": "Nuna versio: ",
"next_steps_error_message": "Poste, vi provu: ",
"next_steps_error_message_refresh": "Reŝargi",
@@ -369,5 +367,6 @@
"footer_original_source_code": "Originala fontkodo",
"footer_donate_page": "Donaci",
"preferences_region_label": "Lando de la enhavo: ",
- "preferences_quality_dash_label": "Preferata DASH-a videkvalito: "
+ "preferences_quality_dash_label": "Preferata DASH-a videkvalito: ",
+ "search_filters_title": "Filtri"
}
diff --git a/locales/es.json b/locales/es.json
index 28ca0bf5..0958a736 100644
--- a/locales/es.json
+++ b/locales/es.json
@@ -141,7 +141,6 @@
"Show less": "Mostrar menos",
"Watch on YouTube": "Ver el vídeo en YouTube",
"Switch Invidious Instance": "Cambiar Instancia de Invidious",
- "Broken? Try another Invidious Instance": "¿Algún error? Prueba otra instancia de Invidious",
"Hide annotations": "Ocultar anotaciones",
"Show annotations": "Mostrar anotaciones",
"Genre: ": "Género: ",
@@ -355,7 +354,6 @@
"search_filters_features_option_four_k": "4k",
"search_filters_features_option_location": "ubicación",
"search_filters_features_option_hdr": "hdr",
- "search_filters_label": "filtro",
"Current version: ": "Versión actual: ",
"next_steps_error_message": "Después de lo cual deberías intentar: ",
"next_steps_error_message_refresh": "Recargar la página",
@@ -459,5 +457,18 @@
"Korean (auto-generated)": "Coreano (generados automáticamente)",
"Spanish (Mexico)": "Español (Méjico)",
"Spanish (auto-generated)": "Español (generados automáticamente)",
- "preferences_watch_history_label": "Habilitar historial de reproducciones: "
+ "preferences_watch_history_label": "Habilitar historial de reproducciones: ",
+ "search_message_no_results": "No se han encontrado resultados.",
+ "search_message_change_filters_or_query": "Pruebe ampliar la consulta de búsqueda y/o a cambiar los filtros.",
+ "search_filters_title": "Filtros",
+ "search_filters_date_label": "Fecha de subida",
+ "search_filters_date_option_none": "Cualquier fecha",
+ "search_filters_type_option_all": "Cualquier tipo",
+ "search_filters_duration_option_none": "Cualquier duración",
+ "search_filters_features_option_vr180": "VR180",
+ "search_filters_apply_button": "Aplicar filtros seleccionados",
+ "tokens_count": "{{count}} token",
+ "tokens_count_plural": "{{count}} tokens",
+ "search_message_use_another_instance": " También puede <a href=\"`x`\">buscar en otra instancia</a>.",
+ "search_filters_duration_option_medium": "Medio (4 - 20 minutes)"
}
diff --git a/locales/eu.json b/locales/eu.json
index 041e9195..9e093a52 100644
--- a/locales/eu.json
+++ b/locales/eu.json
@@ -243,7 +243,6 @@
"Hidden field \"challenge\" is a required field": "\"challenge\" eremu ezkutua beharrezkoa da",
"German": "Alemaniarra",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Ezin izena eman. Izan leike zure konturako berresteko bi faktoreak piztuta ez daudela.",
- "Broken? Try another Invidious Instance": "Akatsaren bat? Invidiouseko beste adibide bat saiatu",
"View YouTube comments": "YouTubeko iruzkinak ikusi",
"Google verification code": "Googleren berresteko kodea",
"`x` is live": "'x' bizirik darrai",
diff --git a/locales/fa.json b/locales/fa.json
index 26f1b220..5ea976f5 100644
--- a/locales/fa.json
+++ b/locales/fa.json
@@ -148,7 +148,6 @@
"Show less": "نمایش کم‌تر",
"Watch on YouTube": "تماشا در یوتیوب",
"Switch Invidious Instance": "تعویض نمونه اینویدیوس",
- "Broken? Try another Invidious Instance": "کار نمی‌کند؟ نمونه دیگری از اینویدیوس را امتحان کنید",
"Hide annotations": "مخفی کردن حاشیه نویسی ها",
"Show annotations": "نمایش حاشیه نویسی ها",
"Genre: ": "ژانر: ",
@@ -371,7 +370,6 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "مکان",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "پالایه",
"Current version: ": "نسخه فعلی: ",
"next_steps_error_message": "اکنون بایستی یکی از این موارد را امتحان کنید: ",
"next_steps_error_message_refresh": "تازه‌سازی",
@@ -412,5 +410,6 @@
"footer_original_source_code": "کد منبع اصلی",
"search_filters_duration_option_long": "بلند (> 20 دقیقه)",
"adminprefs_modified_source_code_url_label": "URL مخزن کد منبع ویریش شده",
- "search_filters_duration_option_short": "کوتاه (< 4 دقیقه)"
+ "search_filters_duration_option_short": "کوتاه (< 4 دقیقه)",
+ "search_filters_title": "پالایه"
}
diff --git a/locales/fi.json b/locales/fi.json
index ce1fbee7..2aa64ea7 100644
--- a/locales/fi.json
+++ b/locales/fi.json
@@ -140,7 +140,6 @@
"Show less": "Näytä vähemmän",
"Watch on YouTube": "Katso YouTubessa",
"Switch Invidious Instance": "Vaihda Invidious-instanssia",
- "Broken? Try another Invidious Instance": "Rikki? Kokeile toista Invidious-instanssia",
"Hide annotations": "Piilota merkkaukset",
"Show annotations": "Näytä merkkaukset",
"Genre: ": "Genre: ",
@@ -354,7 +353,6 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "Sijainti",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "Suodatin",
"Current version: ": "Tämänhetkinen versio: ",
"next_steps_error_message": "Sinun tulisi kokeilla seuraavia: ",
"next_steps_error_message_refresh": "Päivitä",
@@ -461,5 +459,16 @@
"Spanish (Mexico)": "Espanja (Meksiko)",
"Spanish (Spain)": "Espanja (Espanja)",
"Turkish (auto-generated)": "Turkki (automaattisesti luotu)",
- "Vietnamese (auto-generated)": "Vietnam (automaattisesti luotu)"
+ "Vietnamese (auto-generated)": "Vietnam (automaattisesti luotu)",
+ "search_filters_title": "Suodatin",
+ "search_message_no_results": "Ei tuloksia löydetty.",
+ "search_message_change_filters_or_query": "Yritä hakukyselysi laajentamista ja/tai suodattimien muuttamista.",
+ "search_filters_duration_option_none": "Mikä tahansa kesto",
+ "search_filters_features_option_vr180": "VR180",
+ "search_filters_apply_button": "Ota valitut suodattimet käyttöön",
+ "search_filters_date_label": "Latausaika",
+ "search_filters_duration_option_medium": "Keskipituinen (4 - 20 minuuttia)",
+ "search_message_use_another_instance": " Voit myös <a href=\"`x`\">hakea toisella instanssilla</a>.",
+ "search_filters_date_option_none": "Milloin tahansa",
+ "search_filters_type_option_all": "Mikä tahansa tyyppi"
}
diff --git a/locales/fr.json b/locales/fr.json
index 7684f13e..6fee70f9 100644
--- a/locales/fr.json
+++ b/locales/fr.json
@@ -155,7 +155,6 @@
"Show less": "Afficher moins",
"Watch on YouTube": "Voir la vidéo sur Youtube",
"Switch Invidious Instance": "Changer d'instance",
- "Broken? Try another Invidious Instance": "Instance Invidious défectueuse ? Essayez-en une autre",
"Hide annotations": "Masquer les annotations",
"Show annotations": "Afficher les annotations",
"Genre: ": "Genre : ",
@@ -361,33 +360,32 @@
"Videos": "Vidéos",
"Playlists": "Listes de lecture",
"Community": "Communauté",
- "search_filters_sort_option_relevance": "pertinence",
- "search_filters_sort_option_rating": "évaluation",
- "search_filters_sort_option_date": "date",
- "search_filters_sort_option_views": "nombre de vues",
- "search_filters_type_label": "type",
- "search_filters_duration_label": "durée",
- "search_filters_features_label": "fonctionnalités",
+ "search_filters_sort_option_relevance": "Pertinence",
+ "search_filters_sort_option_rating": "Notation",
+ "search_filters_sort_option_date": "Date d'ajout",
+ "search_filters_sort_option_views": "Nombre de vues",
+ "search_filters_type_label": "Type de contenu",
+ "search_filters_duration_label": "Durée",
+ "search_filters_features_label": "Fonctionnalités",
"search_filters_sort_label": "Trier par",
- "search_filters_date_option_hour": "dernière heure",
- "search_filters_date_option_today": "aujourd'hui",
- "search_filters_date_option_week": "semaine",
- "search_filters_date_option_month": "mois",
- "search_filters_date_option_year": "année",
- "search_filters_type_option_video": "vidéo",
- "search_filters_type_option_channel": "chaîne",
- "search_filters_type_option_playlist": "liste de lecture",
- "search_filters_type_option_movie": "film",
- "search_filters_type_option_show": "émission",
+ "search_filters_date_option_hour": "Dernière heure",
+ "search_filters_date_option_today": "Aujourd'hui",
+ "search_filters_date_option_week": "Cette semaine",
+ "search_filters_date_option_month": "Ce mois-ci",
+ "search_filters_date_option_year": "Cette année",
+ "search_filters_type_option_video": "Vidéo",
+ "search_filters_type_option_channel": "Chaîne",
+ "search_filters_type_option_playlist": "Liste de lecture",
+ "search_filters_type_option_movie": "Film",
+ "search_filters_type_option_show": "Émission",
"search_filters_features_option_hd": "HD",
- "search_filters_features_option_subtitles": "sous-titres / CC",
+ "search_filters_features_option_subtitles": "Sous-titres (CC)",
"search_filters_features_option_c_commons": "Creative Commons",
"search_filters_features_option_three_d": "3D",
- "search_filters_features_option_live": "en direct",
+ "search_filters_features_option_live": "En direct",
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "emplacement",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "filtrer",
"Current version: ": "Version actuelle : ",
"next_steps_error_message": "Vous pouvez essayer de : ",
"next_steps_error_message_refresh": "Rafraîchir la page",
@@ -420,7 +418,7 @@
"videoinfo_started_streaming_x_ago": "En stream depuis `x`",
"videoinfo_watch_on_youTube": "Regarder sur YouTube",
"videoinfo_youTube_embed_link": "Intégrer",
- "search_filters_features_option_purchased": "Acheter",
+ "search_filters_features_option_purchased": "Acheté",
"videoinfo_invidious_embed_link": "Lien intégré",
"download_subtitles": "Sous-titres - `x` (.vtt)",
"user_saved_playlists": "`x` listes de lecture sauvegardées",
@@ -461,5 +459,16 @@
"Vietnamese (auto-generated)": "Vietnamien (auto-généré)",
"Russian (auto-generated)": "Russe (auto-généré)",
"Spanish (Spain)": "Espagnol (Espagne)",
- "preferences_watch_history_label": "Activer l'historique de visionnage : "
+ "preferences_watch_history_label": "Activer l'historique de visionnage : ",
+ "search_filters_title": "Filtres",
+ "search_message_change_filters_or_query": "Essayez d'élargir votre recherche et/ou de changer les filtres.",
+ "search_filters_date_option_none": "Toutes les dates",
+ "search_filters_duration_option_medium": "Moyenne (de 4 à 20 minutes)",
+ "search_filters_apply_button": "Appliquer les filtres",
+ "search_message_no_results": "Aucun résultat.",
+ "search_message_use_another_instance": " Vous pouvez également <a href=\"`x`\">effectuer votre recherche sur une autre instance</a>.",
+ "search_filters_type_option_all": "Tous les types",
+ "search_filters_date_label": "Date d'ajout",
+ "search_filters_features_option_vr180": "VR180",
+ "search_filters_duration_option_none": "Toutes les durées"
}
diff --git a/locales/he.json b/locales/he.json
index fc75b953..384b2657 100644
--- a/locales/he.json
+++ b/locales/he.json
@@ -300,6 +300,6 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "מיקום",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "סינון",
- "Current version: ": "הגרסה הנוכחית: "
+ "Current version: ": "הגרסה הנוכחית: ",
+ "search_filters_title": "סינון"
}
diff --git a/locales/hi.json b/locales/hi.json
new file mode 100644
index 00000000..0fc35b25
--- /dev/null
+++ b/locales/hi.json
@@ -0,0 +1,474 @@
+{
+ "last": "आखिरी",
+ "Yes": "हाँ",
+ "No": "नहीं",
+ "Export subscriptions as OPML (for NewPipe & FreeTube)": "OPML के रूप में सदस्यताएँ निर्यात करें (NewPipe और FreeTube के लिए)",
+ "Log in/register": "लॉग-इन/पंजीकृत करें",
+ "Log in with Google": "Google के साथ लॉग-इन करें",
+ "preferences_autoplay_label": "अपने आप चलाने की सुविधा: ",
+ "preferences_dark_mode_label": "थीम: ",
+ "preferences_default_home_label": "डिफ़ॉल्ट मुखपृष्ठ: ",
+ "Could not fetch comments": "टिप्पणियाँ प्राप्त न की जा सकीं",
+ "comments_points_count": "{{count}} पॉइंट",
+ "comments_points_count_plural": "{{count}} पॉइंट्स",
+ "Subscription manager": "सदस्यता प्रबंधन",
+ "License: ": "लाइसेंस: ",
+ "Wilson score: ": "Wilson स्कोर: ",
+ "Wrong answer": "गलत जवाब",
+ "Erroneous CAPTCHA": "गलत CAPTCHA",
+ "Please log in": "कृपया लॉग-इन करें",
+ "Bosnian": "बोस्नियाई",
+ "Bulgarian": "बुल्गारियाई",
+ "Burmese": "बर्मी",
+ "Chinese (Traditional)": "चीनी (पारंपरिक)",
+ "Kurdish": "कुर्द",
+ "Punjabi": "पंजाबी",
+ "Sinhala": "सिंहली",
+ "Slovak": "स्लोवाक",
+ "generic_count_days": "{{count}} दिन",
+ "generic_count_days_plural": "{{count}} दिन",
+ "generic_count_hours": "{{count}} घंटे",
+ "generic_count_hours_plural": "{{count}} घंटे",
+ "generic_count_minutes": "{{count}} मिनट",
+ "generic_count_minutes_plural": "{{count}} मिनट",
+ "generic_count_seconds": "{{count}} सेकंड",
+ "generic_count_seconds_plural": "{{count}} सेकंड",
+ "generic_playlists_count": "{{count}} प्लेलिस्ट",
+ "generic_playlists_count_plural": "{{count}} प्लेलिस्ट्स",
+ "crash_page_report_issue": "अगर इनमें से कुछ भी काम नहीं करता, कृपया <a href=\"`x`\">GitHub पर एक नया मुद्दा खोल दें</a> (अंग्रेज़ी में) और अपने संदेश में यह टेक्स्ट दर्ज करें (इसे अनुवादित न करें!):",
+ "generic_views_count": "{{count}} बार देखा गया",
+ "generic_views_count_plural": "{{count}} बार देखा गया",
+ "generic_videos_count": "{{count}} वीडियो",
+ "generic_videos_count_plural": "{{count}} वीडियो",
+ "generic_subscribers_count": "{{count}} सदस्य",
+ "generic_subscribers_count_plural": "{{count}} सदस्य",
+ "generic_subscriptions_count": "{{count}} सदस्यता",
+ "generic_subscriptions_count_plural": "{{count}} सदस्यताएँ",
+ "LIVE": "लाइव",
+ "Shared `x` ago": "`x` पहले बाँटा गया",
+ "Unsubscribe": "सदस्यता छोड़ें",
+ "Subscribe": "सदस्यता लें",
+ "View channel on YouTube": "चैनल YouTube पर देखें",
+ "View playlist on YouTube": "प्लेलिस्ट YouTube पर देखें",
+ "newest": "सबसे नया",
+ "oldest": "सबसे पुराना",
+ "popular": "सर्वाधिक लोकप्रिय",
+ "Next page": "अगला पृष्ठ",
+ "Previous page": "पिछला पृष्ठ",
+ "Clear watch history?": "देखने का इतिहास मिटाएँ?",
+ "New password": "नया पासवर्ड",
+ "New passwords must match": "पासवर्ड्स को मेल खाना होगा",
+ "Cannot change password for Google accounts": "Google खातों के लिए पासवर्ड नहीं बदल सकते",
+ "Authorize token?": "टोकन को प्रमाणित करें?",
+ "Authorize token for `x`?": "`x` के लिए टोकन को प्रमाणित करें?",
+ "Import and Export Data": "डेटा को आयात और निर्यात करें",
+ "Import": "आयात करें",
+ "Import Invidious data": "Invidious JSON डेटा आयात करें",
+ "Import YouTube subscriptions": "YouTube/OPML सदस्यताएँ आयात करें",
+ "Import FreeTube subscriptions (.db)": "FreeTube सदस्यताएँ आयात करें (.db)",
+ "Import NewPipe subscriptions (.json)": "NewPipe सदस्यताएँ आयात करें (.json)",
+ "Import NewPipe data (.zip)": "NewPipe डेटा आयात करें (.zip)",
+ "Export": "निर्यात करें",
+ "Export subscriptions as OPML": "OPML के रूप में सदस्यताएँ निर्यात करें",
+ "Export data as JSON": "Invidious डेटा को JSON के रूप में निर्यात करें",
+ "Delete account?": "खाता हटाएँ?",
+ "History": "देखे गए वीडियो",
+ "An alternative front-end to YouTube": "YouTube का एक वैकल्पिक फ्रंट-एंड",
+ "JavaScript license information": "जावास्क्रिप्ट लाइसेंस की जानकारी",
+ "source": "स्रोत",
+ "Log in": "लॉग-इन करें",
+ "User ID": "सदस्य ID",
+ "Password": "पासवर्ड",
+ "Register": "पंजीकृत करें",
+ "E-mail": "ईमेल",
+ "Google verification code": "Google प्रमाणीकरण कोड",
+ "Time (h:mm:ss):": "समय (घं:मिमि:सेसे):",
+ "Text CAPTCHA": "टेक्स्ट CAPTCHA",
+ "Image CAPTCHA": "चित्र CAPTCHA",
+ "Sign In": "साइन इन करें",
+ "Preferences": "प्राथमिकताएँ",
+ "preferences_category_player": "प्लेयर की प्राथमिकताएँ",
+ "preferences_video_loop_label": "हमेशा लूप करें: ",
+ "preferences_continue_label": "डिफ़ॉल्ट से अगला चलाएँ: ",
+ "preferences_continue_autoplay_label": "अगला वीडियो अपने आप चलाएँ: ",
+ "preferences_listen_label": "डिफ़ॉल्ट से सुनें: ",
+ "preferences_local_label": "प्रॉक्सी वीडियो: ",
+ "preferences_watch_history_label": "देखने का इतिहास सक्षम करें: ",
+ "preferences_speed_label": "वीडियो चलाने की डिफ़ॉल्ट रफ़्तार: ",
+ "preferences_quality_label": "वीडियो की प्राथमिक क्वालिटी: ",
+ "preferences_quality_option_dash": "DASH (अनुकूली गुणवत्ता)",
+ "preferences_quality_option_hd720": "HD720",
+ "preferences_quality_option_medium": "मध्यम",
+ "preferences_quality_option_small": "छोटा",
+ "preferences_quality_dash_label": "प्राथमिक DASH वीडियो क्वालिटी: ",
+ "preferences_quality_dash_option_720p": "720p",
+ "preferences_quality_dash_option_auto": "अपने-आप",
+ "preferences_quality_dash_option_best": "सबसे अच्छा",
+ "preferences_quality_dash_option_worst": "सबसे खराब",
+ "preferences_quality_dash_option_4320p": "4320p",
+ "preferences_quality_dash_option_2160p": "2160p",
+ "preferences_quality_dash_option_1440p": "1440p",
+ "preferences_quality_dash_option_1080p": "1080p",
+ "preferences_quality_dash_option_480p": "480p",
+ "preferences_quality_dash_option_360p": "360p",
+ "preferences_quality_dash_option_240p": "240p",
+ "preferences_quality_dash_option_144p": "144p",
+ "preferences_comments_label": "डिफ़ॉल्ट टिप्पणियाँ: ",
+ "preferences_volume_label": "प्लेयर का वॉल्यूम: ",
+ "youtube": "YouTube",
+ "reddit": "Reddit",
+ "invidious": "Invidious",
+ "preferences_captions_label": "डिफ़ॉल्ट कैप्शन: ",
+ "Fallback captions: ": "वैकल्पिक कैप्शन: ",
+ "preferences_related_videos_label": "संबंधित वीडियो दिखाएँ: ",
+ "preferences_annotations_label": "डिफ़ॉल्ट से टिप्पणियाँ दिखाएँ: ",
+ "preferences_extend_desc_label": "अपने आप वीडियो के विवरण का विस्तार करें: ",
+ "preferences_vr_mode_label": "उत्तरदायी 360 डिग्री वीडियो (WebGL की ज़रूरत है): ",
+ "preferences_category_visual": "यथादृश्य प्राथमिकताएँ",
+ "preferences_region_label": "सामग्री का राष्ट्र: ",
+ "preferences_player_style_label": "प्लेयर का स्टाइल: ",
+ "Dark mode: ": "डार्क मोड: ",
+ "dark": "डार्क",
+ "light": "लाइट",
+ "preferences_thin_mode_label": "हल्का मोड: ",
+ "preferences_category_misc": "विविध प्राथमिकताएँ",
+ "preferences_automatic_instance_redirect_label": "अपने आप अनुप्रेषित करें (redirect.invidious.io पर फ़ॉलबैक करें): ",
+ "preferences_category_subscription": "सदस्यताओं की प्राथमिकताएँ",
+ "preferences_annotations_subscribed_label": "सदस्यता लिए गए चैनलों पर डिफ़ॉल्ट से टिप्पणियाँ दिखाएँ? ",
+ "Redirect homepage to feed: ": "फ़ीड पर मुखपृष्ठ को अनुप्रेषित करें: ",
+ "preferences_max_results_label": "फ़ीड में दिखाए जाने वाले वीडियों की संख्या: ",
+ "preferences_sort_label": "वीडियों को इस मानदंड पर छाँटें: ",
+ "published": "प्रकाशित",
+ "published - reverse": "प्रकाशित - उल्टा",
+ "Only show latest video from channel: ": "चैनल से सिर्फ नवीनतम वीडियो ही दिखाएँ: ",
+ "alphabetically": "वर्णक्रमानुसार",
+ "Only show latest unwatched video from channel: ": "चैनल से सिर्फ न देखा गया नवीनतम वीडियो ही दिखाएँ: ",
+ "alphabetically - reverse": "वर्णक्रमानुसार - उल्टा",
+ "channel name": "चैनल का नाम",
+ "channel name - reverse": "चैनल का नाम - उल्टा",
+ "preferences_unseen_only_label": "सिर्फ न देखे गए वीडियो ही दिखाएँ: ",
+ "preferences_notifications_only_label": "सिर्फ सूचनाएँ दिखाएँ (अगर हो तो): ",
+ "Enable web notifications": "वेब सूचनाएँ सक्षम करें",
+ "`x` uploaded a video": "`x` ने वीडियो अपलोड किया",
+ "`x` is live": "`x` लाइव हैं",
+ "preferences_category_data": "डेटा की प्राथमिकताएँ",
+ "Clear watch history": "देखने का इतिहास साफ़ करें",
+ "Import/export data": "डेटा को आयात/निर्यात करें",
+ "Change password": "पासवर्ड बदलें",
+ "Manage subscriptions": "सदस्यताएँ प्रबंधित करें",
+ "Manage tokens": "टोकन प्रबंधित करें",
+ "Watch history": "देखने का इतिहास",
+ "Delete account": "खाता हटाएँ",
+ "preferences_category_admin": "प्रबंधक प्राथमिकताएँ",
+ "preferences_feed_menu_label": "फ़ीड मेन्यू: ",
+ "preferences_show_nick_label": "ऊपर उपनाम दिखाएँ: ",
+ "Top enabled: ": "ऊपर का हिस्सा सक्षम है: ",
+ "CAPTCHA enabled: ": "CAPTCHA सक्षम है: ",
+ "Login enabled: ": "लॉग-इन सक्षम है: ",
+ "Registration enabled: ": "पंजीकरण सक्षम है: ",
+ "Report statistics: ": "सांख्यिकी रिपोर्ट करें: ",
+ "Released under the AGPLv3 on Github.": "GitHub पर AGPLv3 के अंतर्गत प्रकाशित।",
+ "Save preferences": "प्राथमिकताएँ सहेजें",
+ "Token manager": "टोकन प्रबंधन",
+ "Token": "टोकन",
+ "tokens_count": "{{count}} टोकन",
+ "tokens_count_plural": "{{count}} टोकन",
+ "Import/export": "आयात/निर्यात करें",
+ "unsubscribe": "सदस्यता छोड़ें",
+ "revoke": "हटाएँ",
+ "Subscriptions": "सदस्यताएँ",
+ "subscriptions_unseen_notifs_count": "{{count}} अपठित सूचना",
+ "subscriptions_unseen_notifs_count_plural": "{{count}} अपठित सूचना",
+ "search": "खोजें",
+ "Log out": "लॉग-आउट करें",
+ "Source available here.": "स्रोत यहाँ उपलब्ध है।",
+ "View JavaScript license information.": "जावास्क्रिप्ट लाइसेंस की जानकारी देखें।",
+ "View privacy policy.": "निजता नीति देखें।",
+ "Trending": "रुझान में",
+ "Public": "सार्वजनिक",
+ "Unlisted": "सबके लिए उपलब्ध नहीं",
+ "Private": "निजी",
+ "View all playlists": "सभी प्लेलिस्ट देखें",
+ "Create playlist": "प्लेलिस्ट बनाएँ",
+ "Updated `x` ago": "`x` पहले अपडेट किया गया",
+ "Delete playlist `x`?": "प्लेलिस्ट `x` हटाएँ?",
+ "Delete playlist": "प्लेलिस्ट हटाएँ",
+ "Title": "शीर्षक",
+ "Playlist privacy": "प्लेलिस्ट की निजता",
+ "Editing playlist `x`": "प्लेलिस्ट `x` को संपादित किया जा रहा है",
+ "Show more": "अधिक देखें",
+ "Show less": "कम देखें",
+ "Watch on YouTube": "YouTube पर देखें",
+ "Switch Invidious Instance": "Invidious उदाहरण बदलें",
+ "search_message_no_results": "कोई परिणाम नहीं मिला।",
+ "search_message_change_filters_or_query": "अपने खोज क्वेरी को और चौड़ा करें और/या फ़िल्टर बदलें।",
+ "search_message_use_another_instance": " आप <a href=\"`x`\">दूसरे उदाहरण पर भी खोज सकते हैं</a>।",
+ "Hide annotations": "टिप्पणियाँ छिपाएँ",
+ "Show annotations": "टिप्पणियाँ दिखाएँ",
+ "Genre: ": "श्रेणी: ",
+ "Family friendly? ": "परिवार के लिए ठीक है? ",
+ "Engagement: ": "सगाई: ",
+ "Whitelisted regions: ": "स्वीकृत क्षेत्र: ",
+ "Blacklisted regions: ": "अस्वीकृत क्षेत्र: ",
+ "Shared `x`": "`x` बाँटा गया",
+ "Premieres in `x`": "`x` बाद प्रीमियर होगा",
+ "Premieres `x`": "`x` को प्रीमिर होगा",
+ "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "नमस्कार! ऐसा लगता है कि आपका जावास्क्रिप्ट अक्षम है। टिप्पणियाँ देखने के लिए यहाँ क्लिक करें, लेकिन याद रखें कि इन्हें लोड होने में थोड़ा ज़्यादा समय लग सकता है।",
+ "View YouTube comments": "YouTube टिप्पणियाँ देखें",
+ "View more comments on Reddit": "Reddit पर अधिक टिप्पणियाँ देखें",
+ "View `x` comments": {
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` टिप्पणी देखें",
+ "": "`x` टिप्पणियाँ देखें"
+ },
+ "View Reddit comments": "Reddit पर टिप्पणियाँ",
+ "Hide replies": "जवाब छिपाएँ",
+ "Show replies": "जवाब दिखाएँ",
+ "Incorrect password": "गलत पासवर्ड",
+ "Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "लॉग-इन नहीं किया जा सका, सुनिश्चित करें कि दो-कारक प्रमाणीकरण (Authenticator या SMS) सक्षम है।",
+ "Invalid TFA code": "अमान्य TFA कोड",
+ "Login failed. This may be because two-factor authentication is not turned on for your account.": "लॉग-इन नाकाम रहा। ऐसा इसलिए हो सकता है कि दो-कारक प्रमाणीकरण आपके खाते पर सक्षम नहीं है।",
+ "Quota exceeded, try again in a few hours": "कोटा पार हो चुका है, कृपया कुछ घंटों में फिर कोशिश करें",
+ "CAPTCHA is a required field": "CAPTCHA एक ज़रूरी फ़ील्ड है",
+ "User ID is a required field": "सदस्य ID एक ज़रूरी फ़ील्ड है",
+ "Password is a required field": "पासवर्ड एक ज़रूरी फ़ील्ड है",
+ "Wrong username or password": "गलत सदस्यनाम या पासवर्ड",
+ "Please sign in using 'Log in with Google'": "कृपया 'Google के साथ लॉग-इन करें' के साथ साइन-इन करें",
+ "Password cannot be empty": "पासवर्ड खाली नहीं हो सकता",
+ "Password cannot be longer than 55 characters": "पासवर्ड में अधिकतम 55 अक्षर हो सकते हैं",
+ "Invidious Private Feed for `x`": "`x` के लिए Invidious निजी फ़ीड",
+ "channel:`x`": "चैनल:`x`",
+ "Deleted or invalid channel": "हटाया गया या अमान्य चैनल",
+ "This channel does not exist.": "यह चैनल मौजूद नहीं है।",
+ "Could not get channel info.": "चैनल की जानकारी प्राप्त न की जा सकी।",
+ "comments_view_x_replies": "{{count}} टिप्पणी देखें",
+ "comments_view_x_replies_plural": "{{count}} टिप्पणियाँ देखें",
+ "`x` ago": "`x` पहले",
+ "Load more": "अधिक लोड करें",
+ "Could not create mix.": "मिक्स न बनाया जा सका।",
+ "Empty playlist": "खाली प्लेलिस्ट",
+ "Not a playlist.": "यह प्लेलिस्ट नहीं है।",
+ "Playlist does not exist.": "प्लेलिस्ट मौजूद नहीं है।",
+ "Could not pull trending pages.": "रुझान के पृष्ठ प्राप्त न किए जा सके।",
+ "Hidden field \"challenge\" is a required field": "छिपाया गया फ़ील्ड \"चुनौती\" एक आवश्यक फ़ील्ड है",
+ "Hidden field \"token\" is a required field": "छिपाया गया फ़ील्ड \"टोकन\" एक आवश्यक फ़ील्ड है",
+ "Erroneous challenge": "त्रुटिपूर्ण चुनौती",
+ "Erroneous token": "त्रुटिपूर्ण टोकन",
+ "No such user": "यह सदस्य मौजूद नहीं हैं",
+ "Token is expired, please try again": "टोकन की समय-सीमा समाप्त हो चुकी है, कृपया दोबारा कोशिश करें",
+ "English": "अंग्रेज़ी",
+ "English (United Kingdom)": "अंग्रेज़ी (यूनाइटेड किंग्डम)",
+ "English (United States)": "अंग्रेज़ी (संयुक्त राष्ट्र)",
+ "English (auto-generated)": "अंग्रेज़ी (अपने-आप जनरेट हुआ)",
+ "Afrikaans": "अफ़्रीकी",
+ "Albanian": "अल्बानियाई",
+ "Amharic": "अम्हेरी",
+ "Arabic": "अरबी",
+ "Armenian": "आर्मेनियाई",
+ "Belarusian": "बेलारूसी",
+ "Azerbaijani": "अज़रबैजानी",
+ "Bangla": "बंगाली",
+ "Basque": "बास्क",
+ "Cantonese (Hong Kong)": "कैंटोनीज़ (हाँग काँग)",
+ "Catalan": "कातालान",
+ "Cebuano": "सेबुआनो",
+ "Chinese": "चीनी",
+ "Chinese (China)": "चीनी (चीन)",
+ "Chinese (Hong Kong)": "चीनी (हाँग काँग)",
+ "Chinese (Simplified)": "चीनी (सरलीकृत)",
+ "Chinese (Taiwan)": "चीनी (ताइवान)",
+ "Corsican": "कोर्सिकन",
+ "Croatian": "क्रोएशियाई",
+ "Czech": "चेक",
+ "Danish": "डेनिश",
+ "Dutch": "डच",
+ "Dutch (auto-generated)": "डच (अपने-आप जनरेट हुआ)",
+ "Esperanto": "एस्पेरांतो",
+ "Estonian": "एस्टोनियाई",
+ "Filipino": "फ़िलिपीनो",
+ "Finnish": "फ़िनिश",
+ "French": "फ़्रेंच",
+ "French (auto-generated)": "फ़्रेंच (अपने-आप जनरेट हुआ)",
+ "Galician": "गैलिशियन",
+ "Georgian": "जॉर्जियाई",
+ "German": "जर्मन",
+ "German (auto-generated)": "जर्मन (अपने-आप जनरेट हुआ)",
+ "Greek": "यूनानी",
+ "Gujarati": "गुजराती",
+ "Haitian Creole": "हैती क्रियोल",
+ "Hausa": "हौसा",
+ "Hawaiian": "हवाई",
+ "Hebrew": "हीब्रू",
+ "Hindi": "हिन्दी",
+ "Hmong": "हमोंग",
+ "Hungarian": "हंगेरी",
+ "Icelandic": "आइसलैंडिक",
+ "Igbo": "इग्बो",
+ "Indonesian": "इंडोनेशियाई",
+ "Indonesian (auto-generated)": "इंडोनेशियाई (अपने-आप जनरेट हुआ)",
+ "Interlingue": "इंटरलिंगुआ",
+ "Irish": "आयरिश",
+ "Italian": "इतालवी",
+ "Italian (auto-generated)": "इतालवी (अपने-आप जनरेट हुआ)",
+ "Japanese": "जापानी",
+ "Japanese (auto-generated)": "जापानी (अपने-आप जनरेट हुआ)",
+ "Javanese": "जावानीज़",
+ "Kannada": "कन्नड़",
+ "Kazakh": "कज़ाख़",
+ "Khmer": "खमेर",
+ "Korean": "कोरियाई",
+ "Korean (auto-generated)": "कोरियाई (अपने-आप जनरेट हुआ)",
+ "Kyrgyz": "किर्गीज़",
+ "Lao": "लाओ",
+ "Latin": "लैटिन",
+ "Latvian": "लातवियाई",
+ "Lithuanian": "लिथुएनियाई",
+ "Luxembourgish": "लग्ज़मबर्गी",
+ "Macedonian": "मकादूनियाई",
+ "Malagasy": "मालागासी",
+ "Malay": "मलय",
+ "Malayalam": "मलयालम",
+ "Maltese": "माल्टीज़",
+ "Maori": "माओरी",
+ "Marathi": "मराठी",
+ "Mongolian": "मंगोलियाई",
+ "Nepali": "नेपाली",
+ "Norwegian Bokmål": "नॉर्वेजियाई",
+ "Nyanja": "न्यानजा",
+ "Pashto": "पश्तो",
+ "Persian": "फ़ारसी",
+ "Polish": "पोलिश",
+ "Portuguese": "पुर्तगाली",
+ "Portuguese (auto-generated)": "पुर्तगाली (अपने-आप जनरेट हुआ)",
+ "Portuguese (Brazil)": "पुर्तगाली (ब्राज़ील)",
+ "Romanian": "रोमेनियाई",
+ "Russian": "रूसी",
+ "Russian (auto-generated)": "रूसी (अपने-आप जनरेट हुआ)",
+ "Samoan": "सामोन",
+ "Scottish Gaelic": "स्कॉटिश गाएलिक",
+ "Serbian": "सर्बियाई",
+ "Shona": "शोणा",
+ "Sindhi": "सिंधी",
+ "Slovenian": "स्लोवेनियाई",
+ "Somali": "सोमाली",
+ "Southern Sotho": "दक्षिणी सोथो",
+ "Spanish": "स्पेनी",
+ "Spanish (auto-generated)": "स्पेनी (अपने-आप जनरेट हुआ)",
+ "Spanish (Latin America)": "स्पेनी (लातिन अमेरिकी)",
+ "Spanish (Mexico)": "स्पेनी (मेक्सिको)",
+ "Spanish (Spain)": "स्पेनी (स्पेन)",
+ "Sundanese": "सुंडानी",
+ "Swahili": "स्वाहिली",
+ "Swedish": "स्वीडिश",
+ "Tajik": "ताजीक",
+ "Tamil": "तमिल",
+ "Telugu": "तेलुगु",
+ "Thai": "थाई",
+ "Turkish": "तुर्की",
+ "Turkish (auto-generated)": "तुर्की (अपने-आप जनरेट हुआ)",
+ "Ukrainian": "यूक्रेनी",
+ "Urdu": "उर्दू",
+ "Uzbek": "उज़्बेक",
+ "Vietnamese": "वियतनामी",
+ "Vietnamese (auto-generated)": "वियतनामी (अपने-आप जनरेट हुआ)",
+ "Welsh": "Welsh",
+ "Western Frisian": "पश्चिमी फ़्रिसियाई",
+ "Xhosa": "खोसा",
+ "Yiddish": "यहूदी",
+ "generic_count_years": "{{count}} वर्ष",
+ "generic_count_years_plural": "{{count}} वर्ष",
+ "Yoruba": "योरुबा",
+ "generic_count_months": "{{count}} महीने",
+ "generic_count_months_plural": "{{count}} महीने",
+ "Zulu": "ज़ूलू",
+ "generic_count_weeks": "{{count}} हफ़्ते",
+ "generic_count_weeks_plural": "{{count}} हफ़्ते",
+ "Fallback comments: ": "फ़ॉलबैक टिप्पणियाँ: ",
+ "Popular": "प्रसिद्ध",
+ "Search": "खोजें",
+ "Top": "ऊपर",
+ "About": "जानकारी",
+ "Rating: ": "रेटिंग: ",
+ "preferences_locale_label": "भाषा: ",
+ "View as playlist": "प्लेलिस्ट के रूप में देखें",
+ "Default": "डिफ़ॉल्ट",
+ "Download": "डाउनलोड करें",
+ "Download as: ": "इस रूप में डाउनलोड करें: ",
+ "%A %B %-d, %Y": "%A %B %-d, %Y",
+ "Music": "संगीत",
+ "Gaming": "गेमिंग",
+ "News": "समाचार",
+ "Movies": "फ़िल्में",
+ "(edited)": "(संपादित)",
+ "YouTube comment permalink": "YouTube पर टिप्पणी की स्थायी कड़ी",
+ "permalink": "स्थायी कड़ी",
+ "Videos": "वीडियो",
+ "`x` marked it with a ❤": "`x` ने इसे एक ❤ से चिह्नित किया",
+ "Audio mode": "ऑडियो मोड",
+ "Playlists": "प्लेलिस्ट्स",
+ "Video mode": "वीडियो मोड",
+ "Community": "समुदाय",
+ "search_filters_title": "फ़िल्टर",
+ "search_filters_date_label": "अपलोड करने का समय",
+ "search_filters_date_option_none": "कोई भी समय",
+ "search_filters_date_option_week": "इस हफ़्ते",
+ "search_filters_date_option_month": "इस महीने",
+ "search_filters_date_option_hour": "पिछला घंटा",
+ "search_filters_date_option_today": "आज",
+ "search_filters_date_option_year": "इस साल",
+ "search_filters_type_label": "प्रकार",
+ "search_filters_type_option_all": "कोई भी प्रकार",
+ "search_filters_type_option_video": "वीडियो",
+ "search_filters_type_option_channel": "चैनल",
+ "search_filters_sort_option_relevance": "प्रासंगिकता",
+ "search_filters_type_option_playlist": "प्लेलिस्ट",
+ "search_filters_type_option_movie": "फ़िल्म",
+ "search_filters_type_option_show": "शो",
+ "search_filters_duration_label": "अवधि",
+ "search_filters_duration_option_none": "कोई भी अवधि",
+ "search_filters_duration_option_short": "4 मिनट से कम",
+ "search_filters_duration_option_medium": "4 से 20 मिनट तक",
+ "search_filters_duration_option_long": "20 मिनट से ज़्यादा",
+ "search_filters_features_label": "सुविधाएँ",
+ "search_filters_features_option_live": "लाइव",
+ "search_filters_sort_option_rating": "रेटिंग",
+ "search_filters_features_option_four_k": "4K",
+ "search_filters_features_option_hd": "HD",
+ "search_filters_features_option_subtitles": "उपशीर्षक/कैप्शन",
+ "search_filters_features_option_c_commons": "क्रिएटिव कॉमन्स",
+ "search_filters_features_option_three_sixty": "360°",
+ "search_filters_features_option_vr180": "VR180",
+ "search_filters_features_option_three_d": "3D",
+ "search_filters_features_option_hdr": "HDR",
+ "search_filters_features_option_location": "जगह",
+ "search_filters_features_option_purchased": "खरीदा गया",
+ "search_filters_sort_label": "इस क्रम से लगाएँ",
+ "search_filters_sort_option_date": "अपलोड की ताऱीख",
+ "search_filters_sort_option_views": "देखे जाने की संख्या",
+ "search_filters_apply_button": "चयनित फ़िल्टर लागू करें",
+ "footer_documentation": "प्रलेख",
+ "footer_source_code": "स्रोत कोड",
+ "footer_original_source_code": "मूल स्रोत कोड",
+ "footer_modfied_source_code": "बदला गया स्रोत कोड",
+ "Current version: ": "वर्तमान संस्करण: ",
+ "next_steps_error_message": "इसके बाद आपके ये आज़माने चाहिए: ",
+ "next_steps_error_message_refresh": "साफ़ करें",
+ "next_steps_error_message_go_to_youtube": "YouTube पर जाएँ",
+ "footer_donate_page": "दान करें",
+ "adminprefs_modified_source_code_url_label": "बदले गए स्रोत कोड के रिपॉज़िटरी का URL",
+ "none": "कुछ नहीं",
+ "videoinfo_started_streaming_x_ago": "`x` पहले स्ट्रीम करना शुरू किया",
+ "videoinfo_watch_on_youTube": "YouTube पर देखें",
+ "Video unavailable": "वीडियो उपलब्ध नहीं है",
+ "preferences_save_player_pos_label": "यहाँ से चलाना शुरू करें: ",
+ "crash_page_you_found_a_bug": "शायद आपको Invidious में कोई बग नज़र आ गया है!",
+ "videoinfo_youTube_embed_link": "एम्बेड करें",
+ "videoinfo_invidious_embed_link": "एम्बोड करने की कड़ी",
+ "download_subtitles": "उपशीर्षक - `x` (.vtt)",
+ "user_created_playlists": "बनाए गए `x` प्लेलिस्ट्स",
+ "user_saved_playlists": "सहेजे गए `x` प्लेलिस्ट्स",
+ "crash_page_before_reporting": "बग रिपोर्ट करने से पहले:",
+ "crash_page_switch_instance": "<a href=\"`x`\">किसी दूसरे उदाहरण का इस्तेमाल करें</a>",
+ "crash_page_read_the_faq": "<a href=\"`x`\">अक्सर पूछे जाने वाले प्रश्न (FAQ)</a> पढ़ें",
+ "crash_page_refresh": "<a href=\"`x`\">पृष्ठ को एक बार साफ़ करें</a>",
+ "crash_page_search_issue": "<a href=\"`x`\">GitHub पर मौजूदा मुद्दे</a> ढूँढ़ें"
+}
diff --git a/locales/hr.json b/locales/hr.json
index d6c8d6aa..94633aac 100644
--- a/locales/hr.json
+++ b/locales/hr.json
@@ -141,7 +141,6 @@
"Show less": "Pokaži manje",
"Watch on YouTube": "Gledaj na YouTubeu",
"Switch Invidious Instance": "Promijeni Invidious instancu",
- "Broken? Try another Invidious Instance": "Pokvarena? Probaj jednu drugu Invidious instancu",
"Hide annotations": "Sakrij napomene",
"Show annotations": "Prikaži napomene",
"Genre: ": "Žanr: ",
@@ -355,7 +354,6 @@
"search_filters_features_option_four_k": "4k",
"search_filters_features_option_location": "lokacija",
"search_filters_features_option_hdr": "hdr",
- "search_filters_label": "filtar",
"Current version: ": "Trenutačna verzija: ",
"next_steps_error_message": "Nakon toga bi trebali pokušati sljedeće: ",
"next_steps_error_message_refresh": "Aktualiziraj stranicu",
@@ -477,5 +475,6 @@
"Korean (auto-generated)": "Korejski (automatski generiran)",
"Portuguese (auto-generated)": "Portugalski (automatski generiran)",
"Spanish (auto-generated)": "Španjolski (automatski generiran)",
- "preferences_watch_history_label": "Aktiviraj povijest gledanja: "
+ "preferences_watch_history_label": "Aktiviraj povijest gledanja: ",
+ "search_filters_title": "Filtar"
}
diff --git a/locales/hu-HU.json b/locales/hu-HU.json
index 1c1d9598..50e505dc 100644
--- a/locales/hu-HU.json
+++ b/locales/hu-HU.json
@@ -31,15 +31,15 @@
"No": "Nem",
"Import and Export Data": "Adatok importálása és exportálása",
"Import": "Importálás",
- "Import Invidious data": "Az Invidious adatainak importálása",
- "Import YouTube subscriptions": "YouTube-feliratkozások importálása",
+ "Import Invidious data": "Az Invidious JSON-adatainak importálása",
+ "Import YouTube subscriptions": "YouTube- vagy OPML-feliratkozások importálása",
"Import FreeTube subscriptions (.db)": "FreeTube-feliratkozások importálása (.db)",
"Import NewPipe subscriptions (.json)": "NewPipe-feliratkozások importálása (.json)",
"Import NewPipe data (.zip)": "NewPipe adatainak importálása (.zip)",
"Export": "Exportálás",
"Export subscriptions as OPML": "Feliratkozások exportálása OPML-ként",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Feliratkozások exportálása OPML-ként (NewPipe-hoz és FreeTube-hoz)",
- "Export data as JSON": "Adat exportálása JSON-ként",
+ "Export data as JSON": "Az Invidious JSON-adatainak exportálása",
"Delete account?": "Törlésre kerüljön a fiók?",
"History": "Megnézett videók naplója",
"An alternative front-end to YouTube": "Ez az oldal egyike a YouTube alternatív kezelőfelületeinek",
@@ -159,7 +159,7 @@
"Engagement: ": "Visszajelzési mutató: ",
"Whitelisted regions: ": "Engedélyezett régiók: ",
"Blacklisted regions: ": "Tiltott régiók: ",
- "Shared `x`": "`x` napon osztották meg",
+ "Shared `x`": "`x` dátummal osztották meg",
"Premieres in `x`": "`x` később lesz a premierje",
"Premieres `x`": "`x` lesz a premierje",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Helló! Úgy tűnik a JavaScript ki van kapcsolva a böngészőben. Ide kattintva lehet olvasni a hozzászólásokat, de a betöltésük így kicsit több időbe telik.",
@@ -366,13 +366,13 @@
"invidious": "Invidious",
"videoinfo_started_streaming_x_ago": "`x` ezelőtt kezdte streamelni",
"search_filters_sort_option_views": "Mennyien látták",
- "search_filters_features_option_purchased": "Megvásárolva",
- "search_filters_features_option_three_sixty": "360°-os",
+ "search_filters_features_option_purchased": "Megvásárolt",
+ "search_filters_features_option_three_sixty": "360°-os virtuális valóság",
"footer_original_source_code": "Eredeti forráskód",
"none": "egyik sem",
"videoinfo_watch_on_youTube": "YouTube-on megnézni",
"videoinfo_youTube_embed_link": "beágyazva",
- "videoinfo_invidious_embed_link": "Beágyazás linkje",
+ "videoinfo_invidious_embed_link": "Beágyazott hivatkozás",
"download_subtitles": "Felirat – `x` (.vtt)",
"user_created_playlists": "`x` létrehozott lejátszási lista",
"user_saved_playlists": "`x` mentett lejátszási lista",
@@ -389,7 +389,6 @@
"Released under the AGPLv3 on Github.": "AGPLv3 licenc alapján a GitHubon",
"search_filters_features_option_three_d": "3D-ben",
"search_filters_features_option_live": "Élőben",
- "search_filters_label": "Szűrők",
"next_steps_error_message_refresh": "Újratöltés",
"footer_donate_page": "Adakozás",
"footer_source_code": "Forráskód",
@@ -414,7 +413,6 @@
"search_filters_date_option_hour": "Az elmúlt órában",
"search_filters_type_option_movie": "Film",
"search_filters_features_option_hdr": "HDR",
- "Broken? Try another Invidious Instance": "Nem működik? Próbáld meg egy másik Invidious oldallal.",
"search_filters_duration_label": "Játékidő",
"next_steps_error_message": "Az alábbi lehetőségek állnak rendelkezésre: ",
"Xhosa": "xhosza",
@@ -460,5 +458,17 @@
"Italian (auto-generated)": "olasz (automatikusan generált)",
"Dutch (auto-generated)": "holland (automatikusan generált)",
"French (auto-generated)": "francia (automatikusan generált)",
- "Vietnamese (auto-generated)": "vietnámi (automatikusan generált)"
+ "Vietnamese (auto-generated)": "vietnámi (automatikusan generált)",
+ "search_filters_title": "Szűrők",
+ "preferences_watch_history_label": "Megnézett videók naplózása: ",
+ "search_message_no_results": "Nincs találat.",
+ "search_message_change_filters_or_query": "Próbálj meg bővebben rákeresni vagy a szűrőkön állítani.",
+ "search_message_use_another_instance": " Megpróbálhatod <a href=\"`x`\">egy másik</a> Invidious-oldalon is a keresést.",
+ "search_filters_date_label": "Feltöltés ideje",
+ "search_filters_date_option_none": "Mindegy mikor",
+ "search_filters_type_option_all": "Bármilyen",
+ "search_filters_duration_option_none": "Mindegy",
+ "search_filters_duration_option_medium": "Átlagos (4 és 20 perc között)",
+ "search_filters_features_option_vr180": "180°-os virtuális valóság",
+ "search_filters_apply_button": "Keresés a megadott szűrőkkel"
}
diff --git a/locales/id.json b/locales/id.json
index 3053d69c..71b7bdb1 100644
--- a/locales/id.json
+++ b/locales/id.json
@@ -148,7 +148,6 @@
"Show less": "Tampilkan lebih sedikit",
"Watch on YouTube": "Tonton di YouTube",
"Switch Invidious Instance": "Ganti peladen Invidious",
- "Broken? Try another Invidious Instance": "Rusak? Coba peladen Invidious yang lain",
"Hide annotations": "Sembunyikan anotasi",
"Show annotations": "Tampilkan anotasi",
"Genre: ": "Genre: ",
@@ -371,7 +370,6 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "Lokasi",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "Saring",
"Current version: ": "Versi saat ini: ",
"next_steps_error_message": "Setelah itu Anda harus mencoba: ",
"next_steps_error_message_refresh": "Segarkan",
@@ -419,5 +417,6 @@
"crash_page_before_reporting": "Sebelum melaporkan masalah, pastikan anda memiliki:",
"English (United States)": "Inggris (US)",
"preferences_watch_history_label": "Aktifkan riwayat tontonan: ",
- "English (United Kingdom)": "Inggris (UK)"
+ "English (United Kingdom)": "Inggris (UK)",
+ "search_filters_title": "Saring"
}
diff --git a/locales/it.json b/locales/it.json
index 69699f05..7ba5ff2d 100644
--- a/locales/it.json
+++ b/locales/it.json
@@ -372,7 +372,6 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "Posizione",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "Filtra",
"Current version: ": "Versione attuale: ",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_360p": "360p",
@@ -409,22 +408,22 @@
"preferences_automatic_instance_redirect_label": "Reindirizzamento automatico dell'istanza (ripiego su redirect.invidious.io): ",
"Video unavailable": "Video non disponibile",
"preferences_show_nick_label": "Mostra nickname in alto: ",
- "short": "Corto (< 4 minuti)",
"videoinfo_youTube_embed_link": "Incorpora",
"videoinfo_invidious_embed_link": "Incorpora collegamento",
"user_created_playlists": "playlist create da `x`",
"preferences_save_player_pos_label": "Memorizza il minutaggio raggiunto dal video: ",
- "purchased": "Acquistato",
"preferences_quality_option_dash": "DASH (qualità adattiva)",
"preferences_region_label": "Nazione del contenuto: ",
"preferences_category_misc": "Preferenze varie",
- "show": "Serie",
- "long": "Lungo (> 20 minuti)",
"next_steps_error_message": "Dopodiché dovresti provare a: ",
"next_steps_error_message_refresh": "Aggiornare",
"footer_donate_page": "Dona",
"footer_source_code": "Codice sorgente",
"adminprefs_modified_source_code_url_label": "Link per il repository del codice sorgente modificato",
"Show more": "Mostra di più",
- "Broken? Try another Invidious Instance": "Non funzionante? Prova un’altra istanza Invidious"
+ "search_filters_title": "Filtra",
+ "search_filters_type_option_show": "Serie",
+ "search_filters_duration_option_short": "Corto (< 4 minuti)",
+ "search_filters_duration_option_long": "Lungo (> 20 minuti)",
+ "search_filters_features_option_purchased": "Acquistato"
}
diff --git a/locales/ja.json b/locales/ja.json
index 977efd53..20d3c20e 100644
--- a/locales/ja.json
+++ b/locales/ja.json
@@ -148,7 +148,6 @@
"Show less": "表示を減らす",
"Watch on YouTube": "YouTube で視聴",
"Switch Invidious Instance": "Invidiousインスタンスの変更",
- "Broken? Try another Invidious Instance": "壊れる?違うInvidiousインスタンスを試してみる",
"Hide annotations": "アノテーションを隠す",
"Show annotations": "アノテーションを表示",
"Genre: ": "ジャンル: ",
@@ -371,7 +370,6 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "場所",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "フィルタ",
"Current version: ": "現在のバージョン: ",
"next_steps_error_message": "下記のものを試して下さい: ",
"next_steps_error_message_refresh": "再読込",
@@ -434,5 +432,6 @@
"Spanish (Mexico)": "スペイン語 (メキシコ)",
"Spanish (Spain)": "スペイン語 (スペイン)",
"Vietnamese (auto-generated)": "ベトナム語 (自動生成)",
- "360": "360°"
+ "search_filters_title": "フィルタ",
+ "search_filters_features_option_three_sixty": "360°"
}
diff --git a/locales/ko.json b/locales/ko.json
index 616380cf..12c2b31f 100644
--- a/locales/ko.json
+++ b/locales/ko.json
@@ -199,7 +199,6 @@
"search_filters_features_option_three_d": "3D",
"search_filters_features_option_location": "지역",
"search_filters_features_option_four_k": "4K",
- "search_filters_label": "필터",
"search_filters_features_option_hdr": "HDR",
"Current version: ": "현재 버전: ",
"next_steps_error_message_refresh": "새로 고침",
@@ -298,7 +297,6 @@
"Empty playlist": "재생목록 비어 있음",
"Show annotations": "주석 보이기",
"Hide annotations": "주석 숨기기",
- "Broken? Try another Invidious Instance": "안되나요? 다른 Invidious 인스턴스를 시도해보세요",
"Switch Invidious Instance": "Invidious 인스턴스 변경",
"Spanish": "스페인어",
"Southern Sotho": "소토어",
@@ -382,5 +380,6 @@
"footer_source_code": "소스 코드",
"footer_original_source_code": "원본 소스 코드",
"footer_modfied_source_code": "수정된 소스 코드",
- "adminprefs_modified_source_code_url_label": "수정된 소스 코드 저장소의 URL"
+ "adminprefs_modified_source_code_url_label": "수정된 소스 코드 저장소의 URL",
+ "search_filters_title": "필터"
}
diff --git a/locales/lt.json b/locales/lt.json
index 7ecf60cf..607b3705 100644
--- a/locales/lt.json
+++ b/locales/lt.json
@@ -141,7 +141,6 @@
"Show less": "Rodyti mažiau",
"Watch on YouTube": "Žiaurėti Youtube",
"Switch Invidious Instance": "Keisti Invidious šaltinį",
- "Broken? Try another Invidious Instance": "Neveikia? Bandyk kitą Invidious šaltinį",
"Hide annotations": "Slėpti anotacijas",
"Show annotations": "Rodyti anotacijas",
"Genre: ": "Žanras: ",
@@ -355,7 +354,6 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "Vietovė",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "Filtras",
"Current version: ": "Dabartinė versija: ",
"next_steps_error_message": "Po to turėtumėte pabandyti: ",
"next_steps_error_message_refresh": "Atnaujinti",
@@ -372,5 +370,6 @@
"preferences_quality_dash_label": "Pageidaujama DASH vaizdo kokybė: ",
"preferences_quality_dash_option_best": "Geriausia",
"preferences_quality_dash_option_worst": "Blogiausia",
- "preferences_quality_dash_option_auto": "Automatinis"
+ "preferences_quality_dash_option_auto": "Automatinis",
+ "search_filters_title": "Filtras"
}
diff --git a/locales/nb-NO.json b/locales/nb-NO.json
index dee0c94b..8d80c10c 100644
--- a/locales/nb-NO.json
+++ b/locales/nb-NO.json
@@ -141,7 +141,6 @@
"Show less": "Vis mindre",
"Watch on YouTube": "Vis video på YouTube",
"Switch Invidious Instance": "Bytt Invidious-instans",
- "Broken? Try another Invidious Instance": "Knekt? Forsøk en annen Invidious-instans",
"Hide annotations": "Skjul merknader",
"Show annotations": "Vis merknader",
"Genre: ": "Sjanger: ",
@@ -355,7 +354,6 @@
"search_filters_features_option_four_k": "4k",
"search_filters_features_option_location": "sted",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "filtrer",
"Current version: ": "Gjeldende versjon: ",
"next_steps_error_message": "Etterpå bør du prøve dette: ",
"next_steps_error_message_refresh": "Gjenoppfrisk",
@@ -461,5 +459,6 @@
"Portuguese (auto-generated)": "Portugisisk (laget automatisk)",
"Russian (auto-generated)": "Russisk (laget automatisk)",
"Dutch (auto-generated)": "Nederlandsk (laget automatisk)",
- "Turkish (auto-generated)": "Tyrkisk (laget automatisk)"
+ "Turkish (auto-generated)": "Tyrkisk (laget automatisk)",
+ "search_filters_title": "Filtrer"
}
diff --git a/locales/nl.json b/locales/nl.json
index 99ae7f8e..17057553 100644
--- a/locales/nl.json
+++ b/locales/nl.json
@@ -21,15 +21,15 @@
"No": "Nee",
"Import and Export Data": "Gegevens im- en exporteren",
"Import": "Importeren",
- "Import Invidious data": "Invidious-gegevens importeren",
- "Import YouTube subscriptions": "YouTube-abonnementen importeren",
+ "Import Invidious data": "JSON-gegevens Invidious importeren",
+ "Import YouTube subscriptions": "YouTube-/OPML-abonnementen importeren",
"Import FreeTube subscriptions (.db)": "FreeTube-abonnementen importeren (.db)",
"Import NewPipe subscriptions (.json)": "NewPipe-abonnementen importeren (.json)",
"Import NewPipe data (.zip)": "NewPipe-gegevens importeren (.zip)",
"Export": "Exporteren",
"Export subscriptions as OPML": "Abonnementen exporteren als OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Abonnementen exporteren als OPML (voor NewPipe en FreeTube)",
- "Export data as JSON": "Gegevens exporteren als JSON",
+ "Export data as JSON": "Invidious-gegevens naar JSON exporteren",
"Delete account?": "Wilt u uw account verwijderen?",
"History": "Geschiedenis",
"An alternative front-end to YouTube": "Een alternatief front-end voor YouTube",
@@ -66,7 +66,7 @@
"preferences_related_videos_label": "Gerelateerde video's tonen? ",
"preferences_annotations_label": "Standaard annotaties tonen? ",
"preferences_extend_desc_label": "Breid videobeschrijving automatisch uit: ",
- "preferences_vr_mode_label": "Interactieve 360-graden-video's ",
+ "preferences_vr_mode_label": "Interactieve 360-graden-video's (vereist WebGL) ",
"preferences_category_visual": "Visuele instellingen",
"preferences_player_style_label": "Speler vormgeving ",
"Dark mode: ": "Donkere modus: ",
@@ -349,7 +349,6 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "locatie",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "verfijnen",
"Current version: ": "Huidige versie: ",
"Switch Invidious Instance": "Schakel tussen de Invidious Instanties",
"preferences_automatic_instance_redirect_label": "Automatische instantie-omleiding (terugval naar redirect.invidious.io): ",
@@ -366,7 +365,6 @@
"footer_original_source_code": "Originele bron-code",
"footer_modfied_source_code": "Gewijzigde bron-code",
"adminprefs_modified_source_code_url_label": "URL naar gewijzigde bron-code-opslagplaats",
- "Broken? Try another Invidious Instance": "Kapot? Probeer een andere Invidious Instantie",
"next_steps_error_message": "Waarna u moet proberen om: ",
"footer_source_code": "Bron-code",
"search_filters_duration_option_long": "Lang (> 20 minuten)",
@@ -395,8 +393,82 @@
"user_created_playlists": "`x` afspeellijsten aangemaakt",
"user_saved_playlists": "`x` afspeellijsten opgeslagen",
"Video unavailable": "Video onbeschikbaar",
- "preferences_save_player_pos_label": "Huidig afspeeltijdstip opslaan: ",
+ "preferences_save_player_pos_label": "Afspeelpositie opslaan: ",
"none": "geen",
"search_filters_features_option_purchased": "Gekocht",
- "search_filters_features_option_three_sixty": "360º"
+ "search_filters_features_option_three_sixty": "360º",
+ "search_filters_title": "Verfijnen",
+ "generic_count_days": "{{count}} dag",
+ "generic_count_days_plural": "{{count}} dagen",
+ "Chinese (Taiwan)": "Chinees (Taiwan)",
+ "Dutch (auto-generated)": "Nederlands (automatisch gegenereerd)",
+ "tokens_count": "{{count}} token",
+ "tokens_count_plural": "{{count}} tokens",
+ "generic_count_seconds": "{{count}} second",
+ "generic_count_seconds_plural": "{{count}} seconden",
+ "generic_count_weeks": "{{count}} week",
+ "generic_count_weeks_plural": "{{count}} weken",
+ "English (United States)": "Engels (Verenigde Staten)",
+ "generic_views_count": "{{count}} keer bekeken",
+ "generic_views_count_plural": "{{count}} keren bekeken",
+ "generic_videos_count": "{{count}} video",
+ "generic_videos_count_plural": "{{count}} video's",
+ "generic_subscriptions_count": "{{count}} abonnement",
+ "generic_subscriptions_count_plural": "{{count}} abonnementen",
+ "subscriptions_unseen_notifs_count": "{{count}} ongeziene melding",
+ "subscriptions_unseen_notifs_count_plural": "{{count}} ongeziene meldingen",
+ "preferences_watch_history_label": "Kijkgeschiedenis inschakelen: ",
+ "crash_page_switch_instance": "geprobeerd hebt om <a href=\"`x`\">een andere instantie te gebruiken</a>",
+ "Portuguese (auto-generated)": "Portugees (automatisch gegenereerd)",
+ "Russian (auto-generated)": "Russisch (automatisch gegenereerd)",
+ "Vietnamese (auto-generated)": "Vietnamees (automatisch gegenereerd)",
+ "comments_points_count": "{{count}} punt",
+ "comments_points_count_plural": "{{count}} punten",
+ "crash_page_before_reporting": "Voor je een bug rapporteert, kijk even na of je:",
+ "Chinese": "Chinees",
+ "search_filters_features_option_vr180": "VR180",
+ "search_filters_date_label": "Uploaddatum",
+ "Portuguese (Brazil)": "Portugees (Brazilië)",
+ "Interlingue": "Interlingue",
+ "Turkish (auto-generated)": "Turks (automatisch gegenereerd)",
+ "search_filters_date_option_none": "Alle datums",
+ "generic_subscribers_count": "{{count}} abonnee",
+ "generic_subscribers_count_plural": "{{count}} abonnees",
+ "search_message_no_results": "Geen resultaten teruggevonden.",
+ "search_message_change_filters_or_query": "Probeer je zoekopdracht uit te breiden en/of de filters aan te passen.",
+ "English (United Kingdom)": "Engels (Verenigd Koninkrijk)",
+ "German (auto-generated)": "Duits (automatisch gegenereerd)",
+ "Spanish (Mexico)": "Spaans (Mexico)",
+ "Spanish (Spain)": "Spaans (Spanje)",
+ "search_filters_type_option_all": "Alle types",
+ "crash_page_refresh": "geprobeerd hebt om <a href=\"`x`\">de pagina te herladen</a>",
+ "comments_view_x_replies": "{{count}} reactie bekijken",
+ "comments_view_x_replies_plural": "{{count}} reacties bekijken",
+ "generic_count_years": "{{count}} jaar",
+ "generic_count_years_plural": "{{count}} jaren",
+ "generic_count_months": "{{count}} maand",
+ "generic_count_months_plural": "{{count}} maanden",
+ "generic_count_hours": "{{count}} uur",
+ "generic_count_hours_plural": "{{count}} uren",
+ "generic_count_minutes": "{{count}} minuut",
+ "generic_count_minutes_plural": "{{count}} minuten",
+ "French (auto-generated)": "Frans (automatisch gegenereerd)",
+ "generic_playlists_count": "{{count}} afspeellijst",
+ "generic_playlists_count_plural": "{{count}} afspeellijsten",
+ "Chinese (Hong Kong)": "Chinees (Hongkong)",
+ "Korean (auto-generated)": "Koreaans (automatisch gegenereerd)",
+ "search_filters_apply_button": "Geselecteerd filters toepassen",
+ "search_message_use_another_instance": " Je kan ook <a href=\"`x`\">zoeken op een andere instantie</a>.",
+ "Cantonese (Hong Kong)": "Kantonees (Hongkong)",
+ "Chinese (China)": "Chinees (China)",
+ "crash_page_read_the_faq": "de <a href=\"`x`\">veelgestelde vragen (FAQ)</a> gelezen hebt",
+ "crash_page_search_issue": "gezocht hebt op <a href=\"`x`\">bestaande problemen op GitHub</a>",
+ "search_filters_duration_option_none": "Alle lengtes",
+ "Indonesian (auto-generated)": "Indonesisch (automatisch gegenereerd)",
+ "Italian (auto-generated)": "Italiaans (automatisch gegenereerd)",
+ "Japanese (auto-generated)": "Japans (automatisch gegenereerd)",
+ "Spanish (auto-generated)": "Spaans (automatisch gegenereerd)",
+ "crash_page_you_found_a_bug": "Je lijkt een bug in Invidious tegengekomen te zijn!",
+ "search_filters_duration_option_medium": "Gemiddeld (4 - 20 minuten)",
+ "crash_page_report_issue": "Indien het bovenstaande niet hielp, gelieve dan <a href=\"`x`\">een nieuw ticket op GitHub</a> te openen (liefst in het Engels) en neem de volgende tekst op in je bericht (gelieve deze NIET te vertalen):"
}
diff --git a/locales/pl.json b/locales/pl.json
index 6ccb712c..37f951a3 100644
--- a/locales/pl.json
+++ b/locales/pl.json
@@ -140,7 +140,6 @@
"Show less": "Pokaż mniej",
"Watch on YouTube": "Zobacz film na YouTube",
"Switch Invidious Instance": "Przełącz instancję Invidious",
- "Broken? Try another Invidious Instance": "Nie działa? Spróbuj innej instancji Invidious",
"Hide annotations": "Ukryj adnotacje",
"Show annotations": "Pokaż adnotacje",
"Genre: ": "Gatunek: ",
@@ -354,7 +353,6 @@
"search_filters_features_option_four_k": "4k",
"search_filters_features_option_location": "Lokalizacja",
"search_filters_features_option_hdr": "hdr",
- "search_filters_label": "filtr",
"Current version: ": "Aktualna wersja: ",
"next_steps_error_message": "Po czym powinien*ś spróbować: ",
"next_steps_error_message_refresh": "Odśwież",
@@ -476,5 +474,6 @@
"Japanese (auto-generated)": "japoński (wygenerowany automatycznie)",
"Russian (auto-generated)": "rosyjski (wygenerowany automatycznie)",
"Portuguese (auto-generated)": "portugalski (wygenerowany automatycznie)",
- "Portuguese (Brazil)": "portugalski (Brazylia)"
+ "Portuguese (Brazil)": "portugalski (Brazylia)",
+ "search_filters_title": "Filtr"
}
diff --git a/locales/pt-BR.json b/locales/pt-BR.json
index 8f95e2f2..e54e20bd 100644
--- a/locales/pt-BR.json
+++ b/locales/pt-BR.json
@@ -21,15 +21,15 @@
"No": "Não",
"Import and Export Data": "Importar e Exportar Dados",
"Import": "Importar",
- "Import Invidious data": "Importar dados do Invidious",
- "Import YouTube subscriptions": "Importar inscrições do YouTube",
+ "Import Invidious data": "Importar dados em JSON do Invidious",
+ "Import YouTube subscriptions": "Importar inscrições do YouTube/OPML",
"Import FreeTube subscriptions (.db)": "Importar inscrições do FreeTube (.db)",
"Import NewPipe subscriptions (.json)": "Importar inscrições do NewPipe (.json)",
"Import NewPipe data (.zip)": "Importar dados do NewPipe (.zip)",
"Export": "Exportar",
"Export subscriptions as OPML": "Exportar inscrições como OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Exportar inscrições como OPML (para NewPipe e FreeTube)",
- "Export data as JSON": "Exportar dados como JSON",
+ "Export data as JSON": "Exportar dados Invidious como JSON",
"Delete account?": "Excluir conta?",
"History": "Histórico",
"An alternative front-end to YouTube": "Uma interface alternativa para o YouTube",
@@ -66,7 +66,7 @@
"preferences_related_videos_label": "Mostrar vídeos relacionados: ",
"preferences_annotations_label": "Sempre mostrar anotações: ",
"preferences_extend_desc_label": "Estenda automaticamente a descrição do vídeo: ",
- "preferences_vr_mode_label": "Vídeos interativos de 360 graus: ",
+ "preferences_vr_mode_label": "Vídeos interativos de 360 graus (requer WebGL): ",
"preferences_category_visual": "Preferências visuais",
"preferences_player_style_label": "Estilo do tocador: ",
"Dark mode: ": "Modo escuro: ",
@@ -143,7 +143,6 @@
"Show less": "Mostrar menos",
"Watch on YouTube": "Assistir no YouTube",
"Switch Invidious Instance": "Mudar a instância do Invidious",
- "Broken? Try another Invidious Instance": "Quebrou? Tente outra Instância do Invidious",
"Hide annotations": "Ocultar anotações",
"Show annotations": "Mostrar anotações",
"Genre: ": "Gênero: ",
@@ -371,7 +370,6 @@
"search_filters_features_option_four_k": "4k",
"search_filters_features_option_location": "localização",
"search_filters_features_option_hdr": "hdr",
- "search_filters_label": "filtro",
"Current version: ": "Versão atual: ",
"next_steps_error_message": "Depois disso, você deve tentar: ",
"next_steps_error_message_refresh": "Atualizar",
@@ -409,10 +407,10 @@
"crash_page_switch_instance": "tentou <a href=\"`x`\">usar outra instância</a>",
"crash_page_search_issue": "procurou por um <a href=\"`x`\">erro existente no GitHub</a>",
"crash_page_report_issue": "Se nenhuma opção acima ajudou, por favor <a href=\"`x`\">abra um novo problema no Github</a> (preferencialmente em inglês) e inclua o seguinte texto (NÃO traduza):",
- "crash_page_read_the_faq": "leu as <a href=\"`x`\">Perguntas Frequentes (FAQ)</a>",
+ "crash_page_read_the_faq": "leia as <a href=\"`x`\">Perguntas frequentes (FAQ)</a>",
"generic_views_count": "{{count}} visualização",
"generic_views_count_plural": "{{count}} visualizações",
- "preferences_quality_option_dash": "DASH (qualidade adaptiva)",
+ "preferences_quality_option_dash": "DASH (qualidade adaptável)",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_small": "Pequeno",
"preferences_quality_dash_option_auto": "Auto",
@@ -437,5 +435,40 @@
"user_created_playlists": "`x` listas de reprodução criadas",
"user_saved_playlists": "`x` listas de reprodução salvas",
"Video unavailable": "Vídeo indisponível",
- "videoinfo_started_streaming_x_ago": "Iniciou a transmissão a `x`"
+ "videoinfo_started_streaming_x_ago": "Iniciou a transmissão a `x`",
+ "search_filters_title": "Filtro",
+ "preferences_watch_history_label": "Ative o histórico de exibição: ",
+ "search_message_no_results": "Nenhum resultado encontrado.",
+ "search_message_change_filters_or_query": "Tente ampliar sua consulta de pesquisa e/ou alterar os filtros.",
+ "English (United Kingdom)": "Inglês (Reino Unido)",
+ "English (United States)": "Inglês (Estados Unidos)",
+ "German (auto-generated)": "Alemão (gerado automaticamente)",
+ "Chinese": "Chinês",
+ "Chinese (China)": "Chinês (China)",
+ "Cantonese (Hong Kong)": "Cantonês (Hong Kong)",
+ "Interlingue": "Interlíngua",
+ "search_filters_type_option_all": "Qualquer tipo",
+ "search_filters_apply_button": "Aplicar filtros selecionados",
+ "Chinese (Hong Kong)": "Chinês (Hong Kong)",
+ "Chinese (Taiwan)": "Chinês (Taiwan)",
+ "Japanese (auto-generated)": "Japonês (gerado automaticamente)",
+ "Korean (auto-generated)": "Coreano (gerado automaticamente)",
+ "Portuguese (auto-generated)": "Português (gerado automaticamente)",
+ "Portuguese (Brazil)": "Português (Brasil)",
+ "Russian (auto-generated)": "Russo (gerado automaticamente)",
+ "Vietnamese (auto-generated)": "Vietnamita (gerado automaticamente)",
+ "search_filters_date_label": "Data de upload",
+ "search_filters_date_option_none": "Qualquer data",
+ "Dutch (auto-generated)": "Holandês (gerado automaticamente)",
+ "French (auto-generated)": "Francês (gerado automaticamente)",
+ "Indonesian (auto-generated)": "indonésio (gerado automaticamente)",
+ "Italian (auto-generated)": "Italiano (gerado automaticamente)",
+ "Spanish (auto-generated)": "Espanhol (gerado automaticamente)",
+ "Spanish (Mexico)": "Espanhol (México)",
+ "search_filters_duration_option_none": "Qualquer duração",
+ "search_message_use_another_instance": " Você também pode <a href=\"`x`\">pesquisar em outra instância</a>.",
+ "Spanish (Spain)": "Espanhol (Espanha)",
+ "Turkish (auto-generated)": "Turco (gerado automaticamente)",
+ "search_filters_duration_option_medium": "Médio (4 - 20 minutos)",
+ "search_filters_features_option_vr180": "VR180"
}
diff --git a/locales/pt-PT.json b/locales/pt-PT.json
index 93e93d18..a57a2939 100644
--- a/locales/pt-PT.json
+++ b/locales/pt-PT.json
@@ -143,7 +143,6 @@
"Show less": "Mostrar menos",
"Watch on YouTube": "Ver no YouTube",
"Switch Invidious Instance": "Mudar a instância do Invidious",
- "Broken? Try another Invidious Instance": "Falhou? Tente outra Instância do Invidious",
"Hide annotations": "Ocultar anotações",
"Show annotations": "Mostrar anotações",
"Genre: ": "Género: ",
@@ -371,9 +370,9 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "Localização",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "Filtro",
"Current version: ": "Versão atual: ",
"next_steps_error_message": "Pode tentar as seguintes opções: ",
"next_steps_error_message_refresh": "Atualizar",
- "next_steps_error_message_go_to_youtube": "Ir ao YouTube"
+ "next_steps_error_message_go_to_youtube": "Ir ao YouTube",
+ "search_filters_title": "Filtro"
}
diff --git a/locales/pt.json b/locales/pt.json
index b5dbd455..df237649 100644
--- a/locales/pt.json
+++ b/locales/pt.json
@@ -4,7 +4,6 @@
"search_filters_sort_option_date": "Data de envio",
"search_filters_sort_option_rating": "Avaliação",
"search_filters_sort_option_relevance": "Relevância",
- "Broken? Try another Invidious Instance": "Falhou? Tente outra Instância do Invidious",
"Switch Invidious Instance": "Mudar a instância do Invidious",
"Show less": "Mostrar menos",
"Show more": "Mostrar mais",
@@ -17,7 +16,6 @@
"next_steps_error_message_go_to_youtube": "Ir ao YouTube",
"next_steps_error_message": "Pode tentar as seguintes opções: ",
"next_steps_error_message_refresh": "Atualizar",
- "search_filters_label": "Filtro",
"search_filters_features_option_hdr": "HDR",
"search_filters_features_option_location": "Localização",
"search_filters_features_option_four_k": "4K",
@@ -437,5 +435,6 @@
"crash_page_read_the_faq": "leu as <a href=\"`x`\">Perguntas frequentes (FAQ)</a>",
"crash_page_search_issue": "procurou se <a href=\"`x`\">o erro já foi reportado no GitHub</a>",
"crash_page_report_issue": "Se nenhuma opção acima ajudou, por favor <a href=\"`x`\">abra um novo problema no Github</a> (preferencialmente em inglês) e inclua o seguinte texto tal qual (NÃO o traduza):",
- "user_created_playlists": "`x` listas de reprodução criadas"
+ "user_created_playlists": "`x` listas de reprodução criadas",
+ "search_filters_title": "Filtro"
}
diff --git a/locales/ro.json b/locales/ro.json
index 2ea6496b..342f5f37 100644
--- a/locales/ro.json
+++ b/locales/ro.json
@@ -21,7 +21,7 @@
"No": "Nu",
"Import and Export Data": "Importați și Exportați Datele",
"Import": "Importați",
- "Import Invidious data": "Importați Datele de pe Invidious",
+ "Import Invidious data": "Importați datele JSON de pe Invidious",
"Import YouTube subscriptions": "Importați abonamentele de pe YouTube",
"Import FreeTube subscriptions (.db)": "Importați abonamentele de pe FreeTube (.db)",
"Import NewPipe subscriptions (.json)": "Importați abonamentele de pe NewPipe (.json)",
@@ -29,7 +29,7 @@
"Export": "Exportați",
"Export subscriptions as OPML": "Exportați abonamentele în format OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Exportați abonamentele în format OPML (pentru NewPipe și FreeTube)",
- "Export data as JSON": "Exportați datele în format JSON",
+ "Export data as JSON": "Exportați datele Invidious în format JSON",
"Delete account?": "Sunteți siguri că doriți să vă ștergeți contul?",
"History": "Istoric",
"An alternative front-end to YouTube": "O alternativă front-end pentru YouTube",
@@ -155,7 +155,7 @@
"Hide replies": "Ascundeți replicile",
"Show replies": "Afișați replicile",
"Incorrect password": "Parolă incorectă",
- "Quota exceeded, try again in a few hours": "Numărul de tentative de conectare a fost depășit. Va rugăm să încercați din nou în câteva ore.",
+ "Quota exceeded, try again in a few hours": "Numărul de tentative de conectare a fost depășit. Va rugăm să încercați din nou în câteva ore",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Conectare eșuată. Dacă nu reușiți să vă conectați, verificați dacă ați activat autentificarea cu doi factori (Autentificator sau SMS).",
"Invalid TFA code": "Codul de autentificare cu doi factori este invalid",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Conectare eșuată. Acest lucru ar putea fi cauzat de faptul că nu ați activat autentificarea cu doi factori.",
@@ -174,7 +174,7 @@
"Deleted or invalid channel": "Canal șters sau invalid",
"This channel does not exist.": "Acest canal nu există.",
"Could not get channel info.": "Nu am putut primi informații despre acest canal.",
- "Could not fetch comments": "Încărcarea comentariilor a eșuat.",
+ "Could not fetch comments": "Încărcarea comentariilor a eșuat",
"`x` ago": "acum `x`",
"Load more": "Vedeți mai mult",
"Could not create mix.": "Nu am putut crea această listă de redare.",
@@ -187,7 +187,7 @@
"Erroneous challenge": "Challenge invalid",
"Erroneous token": "Token invalid",
"No such user": "Acest utilizator nu există",
- "Token is expired, please try again": "Token-ul este expirat, vă rugăm să reîncercați.",
+ "Token is expired, please try again": "Jetonul a expirat, vă rugăm să încercați din nou",
"English": "Engleză",
"English (auto-generated)": "Engleză (generată automat)",
"Afrikaans": "Afrikaans",
@@ -295,7 +295,7 @@
"Yoruba": "Yoruba",
"Zulu": "Zoulou",
"Fallback comments: ": "Comentarii alternative: ",
- "Popular": "Popular",
+ "Popular": "Populare",
"Top": "Top",
"About": "Despre",
"Rating: ": "Evaluare: ",
@@ -318,5 +318,173 @@
"Videos": "Videoclipuri",
"Playlists": "Liste de redare",
"Community": "Comunitate",
- "Current version: ": "Versiunea actuală: "
+ "Current version: ": "Versiunea actuală: ",
+ "crash_page_read_the_faq": "citit lista <a href=\"`x`\">Întrebărilor Frecvente (FAQ)</a>",
+ "generic_count_days_0": "{{count}} zi",
+ "generic_count_days_1": "{{count}} zile",
+ "generic_count_days_2": "{{count}} de zile",
+ "generic_count_hours_0": "{{count}} oră",
+ "generic_count_hours_1": "{{count}} ore",
+ "generic_count_hours_2": "{{count}} de ore",
+ "generic_count_minutes_0": "{{count}} minut",
+ "generic_count_minutes_1": "{{count}} minute",
+ "generic_count_minutes_2": "{{count}} de minute",
+ "generic_views_count_0": "{{count}} vizionare",
+ "generic_views_count_1": "{{count}} vizionări",
+ "generic_views_count_2": "{{count}} de vizionări",
+ "subscriptions_unseen_notifs_count_0": "{{count}} notificare neverificată",
+ "subscriptions_unseen_notifs_count_1": "{{count}} notificări neverificate",
+ "subscriptions_unseen_notifs_count_2": "{{count}} de notificări neverificate",
+ "crash_page_refresh": "încercat să <a href=\"`x`\">reîmprospătați pagina</a>",
+ "crash_page_switch_instance": "am încercat să <a href=\"`x`\">folosim o altă instanță</a>",
+ "preferences_watch_history_label": "Activează istoricul: ",
+ "invidious": "Invidious",
+ "preferences_vr_mode_label": "Videoclipuri interactive de 360 de grade (necesită WebGL): ",
+ "English (United Kingdom)": "Engleză (Regatul Unit)",
+ "English (United States)": "Engleză (Statele Unite ale Americii)",
+ "Chinese": "Chineză",
+ "Chinese (China)": "Chineză (China)",
+ "Chinese (Hong Kong)": "Chineză (Hong Kong)",
+ "Chinese (Taiwan)": "Chineză (Taiwan)",
+ "Cantonese (Hong Kong)": "Cantoneză (Hong Kong)",
+ "Portuguese (auto-generated)": "Portugheză (generată automat)",
+ "Portuguese (Brazil)": "Portugheză (Brazilia)",
+ "Russian (auto-generated)": "Rusă (generată automat)",
+ "Turkish (auto-generated)": "Turcă (generată automat)",
+ "Vietnamese (auto-generated)": "Vietnameză (generată automat)",
+ "videoinfo_started_streaming_x_ago": "În direct de acum `x`",
+ "preferences_quality_dash_option_2160p": "2160p",
+ "footer_modfied_source_code": "Codul sursă modificat",
+ "preferences_quality_dash_label": "Calitatea video DASH preferată: ",
+ "generic_videos_count_0": "{{count}} videoclip",
+ "generic_videos_count_1": "{{count}} videoclipuri",
+ "generic_videos_count_2": "{{count}} de videoclipuri",
+ "generic_playlists_count_0": "{{count}} playlist",
+ "generic_playlists_count_1": "{{count}} playlisturi",
+ "generic_playlists_count_2": "{{count}} de playlisturi",
+ "tokens_count_0": "{{count}} jeton",
+ "tokens_count_1": "{{count}} jetoane",
+ "tokens_count_2": "{{count}} de jetoane",
+ "comments_points_count_0": "{{count}} punct",
+ "comments_points_count_1": "{{count}} puncte",
+ "comments_points_count_2": "{{count}} de puncte",
+ "Spanish (Spain)": "Spaniolă (Spania)",
+ "Video unavailable": "Videoclip indisponibil",
+ "crash_page_search_issue": "căutat <a href=\"`x`\">sugestiile existente pe GitHub</a>",
+ "Show more": "Afișați mai mult",
+ "Released under the AGPLv3 on Github.": "Lansat sub licența AGPLv3 pe GitHub.",
+ "preferences_quality_option_dash": "DASH (calitate adaptativă)",
+ "preferences_quality_option_hd720": "HD720",
+ "preferences_quality_option_small": "Mică",
+ "preferences_quality_dash_option_1440p": "1440p",
+ "preferences_quality_dash_option_1080p": "1080p",
+ "preferences_category_misc": "Setări diverse",
+ "preferences_automatic_instance_redirect_label": "Redirecționare automată de instanță (trecere prin redirect.invidious.io): ",
+ "preferences_quality_dash_option_480p": "480p",
+ "preferences_quality_option_medium": "Medie",
+ "Switch Invidious Instance": "Schimbă instanța Invidious",
+ "preferences_quality_dash_option_720p": "720p",
+ "preferences_quality_dash_option_auto": "Automatică",
+ "preferences_quality_dash_option_best": "Cea mai bună",
+ "preferences_quality_dash_option_worst": "Cea mai redusă",
+ "preferences_quality_dash_option_4320p": "4320p",
+ "preferences_quality_dash_option_360p": "360p",
+ "preferences_region_label": "Țară de conținut: ",
+ "preferences_extend_desc_label": "Extindeți automat descrierea: ",
+ "preferences_show_nick_label": "Afișați numele de utilizator pe partea de sus: ",
+ "generic_subscribers_count_0": "{{count}} abonat",
+ "generic_subscribers_count_1": "{{count}} abonați",
+ "generic_subscribers_count_2": "{{count}} de abonați",
+ "generic_subscriptions_count_0": "{{count}} abonament",
+ "generic_subscriptions_count_1": "{{count}} abonamente",
+ "generic_subscriptions_count_2": "{{count}} de abonamente",
+ "Search": "Căutați",
+ "search_filters_title": "Filtre",
+ "search_filters_date_label": "Data încărcării",
+ "none": "niciunul",
+ "search_message_use_another_instance": " Puteți <a href=\"`x`\">căuta într-o altă instanță</a>.",
+ "comments_view_x_replies_0": "Afișați {{count}} răspuns",
+ "comments_view_x_replies_1": "Afișați {{count}} răspunsuri",
+ "comments_view_x_replies_2": "Afișați {{count}} de răspunsuri",
+ "search_message_no_results": "Nu s-au găsit rezultate.",
+ "Dutch (auto-generated)": "Olandeză (generată automat)",
+ "Indonesian (auto-generated)": "Indoneziană (generată automat)",
+ "German (auto-generated)": "Germană (generată automat)",
+ "French (auto-generated)": "Franceză (generată automat)",
+ "Interlingue": "Interlingue",
+ "Italian (auto-generated)": "Italiană (generată automat)",
+ "Japanese (auto-generated)": "Japoneză (generată automat)",
+ "Korean (auto-generated)": "Coreeană (generată automat)",
+ "Spanish (auto-generated)": "Spaniolă (generată automat)",
+ "search_filters_date_option_none": "Oricând",
+ "search_filters_date_option_year": "an",
+ "search_filters_type_option_channel": "canal",
+ "Spanish (Mexico)": "Spaniolă (Mexic)",
+ "generic_count_weeks_0": "{{count}} săptămână",
+ "generic_count_weeks_1": "{{count}} săptămâni",
+ "generic_count_weeks_2": "{{count}} de săptămâni",
+ "generic_count_seconds_0": "{{count}} secundă",
+ "generic_count_seconds_1": "{{count}} secunde",
+ "generic_count_seconds_2": "{{count}} de secunde",
+ "search_filters_type_option_video": "videoclip",
+ "generic_count_years_0": "{{count}} an",
+ "generic_count_years_1": "{{count}} ani",
+ "generic_count_years_2": "{{count}} de ani",
+ "generic_count_months_0": "{{count}} lună",
+ "generic_count_months_1": "{{count}} luni",
+ "generic_count_months_2": "{{count}} de luni",
+ "search_filters_duration_label": "durată",
+ "search_filters_date_option_month": "lună",
+ "search_filters_type_label": "Tip",
+ "search_filters_date_option_today": "azi",
+ "search_filters_date_option_week": "săptămână",
+ "search_filters_features_option_vr180": "VR180",
+ "search_filters_type_option_playlist": "playlist",
+ "search_filters_type_option_movie": "film",
+ "search_filters_type_option_show": "emisiune",
+ "search_filters_duration_option_short": "Scurt (< 4 minute)",
+ "search_filters_duration_option_medium": "Medie (4 - 20 de minute)",
+ "search_filters_duration_option_none": "Fără limită",
+ "search_filters_duration_option_long": "Lungă (> 20 de minute)",
+ "search_filters_features_label": "atribute",
+ "search_filters_features_option_live": "în direct",
+ "search_filters_features_option_four_k": "4K",
+ "search_filters_features_option_c_commons": "Creative Commons",
+ "search_filters_features_option_three_sixty": "360°",
+ "search_filters_features_option_three_d": "3D",
+ "search_filters_features_option_subtitles": "subtitrări/CC",
+ "search_filters_features_option_hd": "HD",
+ "search_filters_features_option_hdr": "HDR",
+ "search_filters_features_option_purchased": "Cumpărate",
+ "next_steps_error_message": "După ce ar trebui să încercați să: ",
+ "user_saved_playlists": "`x` playlisturi salvate",
+ "search_filters_features_option_location": "locație",
+ "search_filters_sort_label": "Sortați după",
+ "search_filters_sort_option_relevance": "relevanță",
+ "search_filters_sort_option_rating": "clasificare",
+ "search_filters_sort_option_date": "Data încărcării",
+ "search_filters_sort_option_views": "Numărul de vizionări",
+ "footer_source_code": "Codul sursă",
+ "search_filters_apply_button": "Aplicați filtrele selectate",
+ "footer_original_source_code": "Codul sursă original",
+ "next_steps_error_message_refresh": "Reîmprospătează",
+ "next_steps_error_message_go_to_youtube": "Mergeți pe YouTube",
+ "footer_donate_page": "Donați",
+ "adminprefs_modified_source_code_url_label": "URL către depozitul de cod sursă modificat",
+ "footer_documentation": "Documentație",
+ "videoinfo_youTube_embed_link": "Încorporați",
+ "videoinfo_watch_on_youTube": "Vizionați pe YouTube",
+ "videoinfo_invidious_embed_link": "Link de încorporare",
+ "download_subtitles": "Subtitrări - `x` (.vtt)",
+ "user_created_playlists": "`x` playlisturi create",
+ "preferences_save_player_pos_label": "Salvați poziția de redare: ",
+ "crash_page_you_found_a_bug": "Se pare că ați găsit un bug în aplicația Invidious!",
+ "crash_page_before_reporting": "Înainte de a reporta bugul, asigurați-vă că ați:",
+ "search_filters_date_option_hour": "oră",
+ "search_message_change_filters_or_query": "Încercați să lărgiți căutarea sau să modificați filtrele.",
+ "crash_page_report_issue": "Dacă niciuna dintre sugestiile de mai sus v-a ajutat, vă rugăm să <a href=\"`x`\">postați o nouă sugestie pe GitHub</a> (cel mai bine în engleză), și să includeți următorul text în post (să nu îl traduceți):",
+ "search_filters_type_option_all": "orice tip",
+ "preferences_quality_dash_option_240p": "240p",
+ "preferences_quality_dash_option_144p": "144p",
+ "Show less": "Afișați mai puțin"
}
diff --git a/locales/ru.json b/locales/ru.json
index 6ed6b296..0199f61f 100644
--- a/locales/ru.json
+++ b/locales/ru.json
@@ -5,8 +5,8 @@
"Subscribe": "Подписаться",
"View channel on YouTube": "Смотреть канал на YouTube",
"View playlist on YouTube": "Посмотреть плейлист на YouTube",
- "newest": "самые свежие",
- "oldest": "самые старые",
+ "newest": "сначала новые",
+ "oldest": "сначала старые",
"popular": "популярные",
"last": "недавние",
"Next page": "Следующая страница",
@@ -74,8 +74,8 @@
"dark": "темная",
"light": "светлая",
"preferences_thin_mode_label": "Облегчённое оформление: ",
- "preferences_category_misc": "Прочие предпочтения",
- "preferences_automatic_instance_redirect_label": "Автоматическое перенаправление на зеркало сайта (резервный вариант redirect.invidious.io): ",
+ "preferences_category_misc": "Прочие настройки",
+ "preferences_automatic_instance_redirect_label": "Автоматическое перенаправление на зеркало сайта (переход на redirect.invidious.io): ",
"preferences_category_subscription": "Настройки подписок",
"preferences_annotations_subscribed_label": "Всегда показывать аннотации в видео каналов, на которые вы подписаны? ",
"Redirect homepage to feed: ": "Отображать видео с каналов, на которые вы подписаны, как главную страницу: ",
@@ -141,7 +141,6 @@
"Show less": "Показать меньше",
"Watch on YouTube": "Смотреть на YouTube",
"Switch Invidious Instance": "Сменить экземпляр Invidious",
- "Broken? Try another Invidious Instance": "Сломался? Попробуйте другой экземпляр Invidious",
"Hide annotations": "Скрыть аннотации",
"Show annotations": "Показать аннотации",
"Genre: ": "Жанр: ",
@@ -355,7 +354,6 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "Местоположение",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "Фильтр",
"Current version: ": "Текущая версия: ",
"next_steps_error_message": "После чего следует попробовать: ",
"next_steps_error_message_refresh": "Обновить",
@@ -477,5 +475,16 @@
"Video unavailable": "Видео недоступно",
"preferences_save_player_pos_label": "Запоминать позицию: ",
"preferences_region_label": "Страна: ",
- "preferences_watch_history_label": "Включить историю просмотров "
+ "preferences_watch_history_label": "Включить историю просмотров ",
+ "search_filters_title": "Фильтр",
+ "search_filters_duration_option_none": "Любой длины",
+ "search_filters_type_option_all": "Любого типа",
+ "search_filters_date_option_none": "Любой даты",
+ "search_filters_date_label": "Дата загрузки",
+ "search_message_no_results": "Ничего не найдено.",
+ "search_message_use_another_instance": " Дополнительно вы можете <a href=\"`x`\">поискать на других зеркалах</a>.",
+ "search_filters_features_option_vr180": "VR180",
+ "search_message_change_filters_or_query": "Попробуйте расширить поисковый запрос и изменить фильтры.",
+ "search_filters_duration_option_medium": "Средние (4 - 20 минут)",
+ "search_filters_apply_button": "Применить фильтры"
}
diff --git a/locales/sk.json b/locales/sk.json
index f20ad75a..cdb3a596 100644
--- a/locales/sk.json
+++ b/locales/sk.json
@@ -18,15 +18,15 @@
"No": "Nie",
"Import and Export Data": "Import a Export údajov",
"Import": "Import",
- "Import Invidious data": "Importovať údaje Invidious",
- "Import YouTube subscriptions": "Importovať odbery YouTube",
+ "Import Invidious data": "Importovať JSON údaje Invidious",
+ "Import YouTube subscriptions": "Importovať odbery YouTube/OPML",
"Import FreeTube subscriptions (.db)": "Importovať odbery FreeTube (.db)",
"Import NewPipe subscriptions (.json)": "Importovať odbery NewPipe (.json)",
"Import NewPipe data (.zip)": "Importovať údaje NewPipe (.zip)",
"Export": "Export",
"Export subscriptions as OPML": "Exportovať odbery ako OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Exportovať odbery ako OPML (pre NewPipe a FreeTube)",
- "Export data as JSON": "Export údajov ako JSON",
+ "Export data as JSON": "Exportovať údaje Invidious ako JSON",
"Delete account?": "Zrušiť účet?",
"History": "História",
"An alternative front-end to YouTube": "Alternatívny front-end pre YouTube",
@@ -84,5 +84,23 @@
"preferences_unseen_only_label": "Zobraziť iba neprehrané: ",
"preferences_notifications_only_label": "Zobraziť iba upozornenia (ak existujú): ",
"Enable web notifications": "Povoliť webové upozornenia",
- "`x` uploaded a video": "`x` nahral(a) video"
+ "`x` uploaded a video": "`x` nahral(a) video",
+ "generic_views_count_0": "{{count}} zhliadnutie",
+ "generic_views_count_1": "{{count}} zhliadnutia",
+ "generic_views_count_2": "{{count}} zhliadnutí",
+ "generic_subscribers_count_0": "{{count}} odberateľ",
+ "generic_subscribers_count_1": "{{count}} odberatelia",
+ "generic_subscribers_count_2": "{{count}} odberateľov",
+ "Shared `x` ago": "Zverejnené pred `x`",
+ "generic_playlists_count_0": "{{count}} playlist",
+ "generic_playlists_count_1": "{{count}} playlisty",
+ "generic_playlists_count_2": "{{count}} playlistov",
+ "generic_videos_count_0": "{{count}} video",
+ "generic_videos_count_1": "{{count}} videá",
+ "generic_videos_count_2": "{{count}} videí",
+ "generic_subscriptions_count_0": "{{count}} odber",
+ "generic_subscriptions_count_1": "{{count}} odbery",
+ "generic_subscriptions_count_2": "{{count}} odberov",
+ "Authorize token for `x`?": "Autorizovať token pre `x`?",
+ "View playlist on YouTube": "Zobraziť playlist na YouTube"
}
diff --git a/locales/sl.json b/locales/sl.json
new file mode 100644
index 00000000..791a01c5
--- /dev/null
+++ b/locales/sl.json
@@ -0,0 +1,506 @@
+{
+ "No": "Ne",
+ "Subscribe": "Naroči se",
+ "View playlist on YouTube": "Ogled seznama predvajanja v YouTubu",
+ "last": "zadnji",
+ "Next page": "Naslednja stran",
+ "Previous page": "Prejšnja stran",
+ "Clear watch history?": "Izbrisati zgodovino ogledov?",
+ "New password": "Novo geslo",
+ "New passwords must match": "Nova gesla se morajo ujemati",
+ "Cannot change password for Google accounts": "Ni mogoče spremeniti gesla za račune Google",
+ "Authorize token?": "Naj odobrim žeton?",
+ "Yes": "Da",
+ "Import and Export Data": "Uvoz in izvoz podatkov",
+ "Import": "Uvozi",
+ "Import Invidious data": "Uvozi Invidious JSON podatke",
+ "Import YouTube subscriptions": "Uvozi YouTube/OPML naročnine",
+ "Import FreeTube subscriptions (.db)": "Uvozi FreeTube (.db) naročnine",
+ "Import NewPipe data (.zip)": "Uvozi NewPipe (.zip) podatke",
+ "Export": "Izvozi",
+ "Export subscriptions as OPML": "Izvozi naročnine kot OPML",
+ "Export subscriptions as OPML (for NewPipe & FreeTube)": "Izvozi naročnine kot OPML (za NewPipe in FreeTube)",
+ "Log in": "Prijava",
+ "Log in/register": "Prijava/registracija",
+ "Log in with Google": "Prijavi se z Googlom",
+ "User ID": "ID uporabnika",
+ "Password": "Geslo",
+ "Time (h:mm:ss):": "Čas (h:mm:ss):",
+ "Text CAPTCHA": "Besedilo CAPTCHA",
+ "source": "izvorna koda",
+ "Image CAPTCHA": "Slika CAPTCHA",
+ "Sign In": "Prijavi se",
+ "Register": "Registriraj se",
+ "E-mail": "E-pošta",
+ "Google verification code": "Googlova koda za preverjanje",
+ "Preferences": "Nastavitve",
+ "preferences_video_loop_label": "Vedno v zanki: ",
+ "preferences_autoplay_label": "Samodejno predvajanje: ",
+ "preferences_continue_autoplay_label": "Samodejno predvajanje naslednjega videoposnetka: ",
+ "preferences_listen_label": "Privzeto poslušaj: ",
+ "preferences_local_label": "Proxy za videoposnetke: ",
+ "preferences_speed_label": "Privzeta hitrost: ",
+ "preferences_quality_label": "Prednostna kakovost videoposnetka: ",
+ "preferences_quality_option_hd720": "HD720",
+ "preferences_quality_dash_option_best": "najboljša",
+ "preferences_quality_dash_option_4320p": "4320p",
+ "preferences_quality_dash_option_1440p": "1440p",
+ "preferences_quality_dash_option_1080p": "1080p",
+ "preferences_quality_dash_option_720p": "720p",
+ "preferences_quality_dash_option_480p": "480p",
+ "preferences_quality_dash_option_360p": "360p",
+ "preferences_quality_dash_option_240p": "240p",
+ "preferences_volume_label": "Glasnost predvajalnika: ",
+ "reddit": "Reddit",
+ "preferences_player_style_label": "Slog predvajalnika: ",
+ "dark": "temna",
+ "light": "svetla",
+ "preferences_thin_mode_label": "Tanki način: ",
+ "preferences_automatic_instance_redirect_label": "Samodejna preusmeritev (na redirect.invidious.io): ",
+ "preferences_annotations_subscribed_label": "Privzeto prikazati opombe za naročene kanale? ",
+ "Redirect homepage to feed: ": "Preusmeri domačo stran na vir: ",
+ "preferences_max_results_label": "Število videoposnetkov, prikazanih v viru: ",
+ "preferences_sort_label": "Razvrsti videoposnetke po: ",
+ "published": "datumu objave",
+ "published - reverse": "datumu objave - obratno",
+ "alphabetically": "abecednem vrstnem redu",
+ "alphabetically - reverse": "po abecednem vrstnem redu - obratno",
+ "channel name": "imenu kanala",
+ "channel name - reverse": "imenu kanala - obratno",
+ "Only show latest video from channel: ": "Pokaži samo najnovejši videoposnetek iz kanala: ",
+ "Only show latest unwatched video from channel: ": "Pokaži samo najnovejši še neogledani videoposnetek iz kanala: ",
+ "preferences_unseen_only_label": "Pokaži samo neogledane: ",
+ "preferences_notifications_only_label": "Pokaži samo obvestila (če obstajajo): ",
+ "preferences_category_data": "Nastavitve podatkov",
+ "Clear watch history": "Počisti zgodovino ogledov",
+ "Import/export data": "Uvoz/izvoz podatkov",
+ "Change password": "Spremeni geslo",
+ "Watch history": "Oglej si zgodovino",
+ "Delete account": "Izbriši račun",
+ "preferences_category_admin": "Skrbniške nastavitve",
+ "preferences_default_home_label": "Privzeta domača stran: ",
+ "preferences_feed_menu_label": "Meni vira: ",
+ "Top enabled: ": "Vrh je omogočen: ",
+ "CAPTCHA enabled: ": "CAPTCHA omogočeni: ",
+ "Login enabled: ": "Prijava je omogočena: ",
+ "Registration enabled: ": "Registracija je omogočena: ",
+ "Token manager": "Upravitelj žetonov",
+ "Token": "Žeton",
+ "tokens_count_0": "{{count}} žeton",
+ "tokens_count_1": "{{count}} žetona",
+ "tokens_count_2": "{{count}} žetoni",
+ "tokens_count_3": "{{count}} žetonov",
+ "Import/export": "Uvoz/izvoz",
+ "unsubscribe": "odjava",
+ "revoke": "prekliči",
+ "search": "iskanje",
+ "Log out": "Odjava",
+ "Released under the AGPLv3 on Github.": "Objavljeno pod licenco AGPLv3 na GitHubu.",
+ "Trending": "Trendi",
+ "Private": "Zasebno",
+ "View all playlists": "Oglej si vse sezname predvajanja",
+ "Updated `x` ago": "Posodobljeno pred `x`",
+ "Delete playlist `x`?": "Brisanje seznama predvajanja `x`?",
+ "Delete playlist": "Izbriši seznam predvajanja",
+ "Title": "Naslov",
+ "Playlist privacy": "Zasebnost seznama predvajanja",
+ "Editing playlist `x`": "Urejanje seznama predvajanja `x`",
+ "Show more": "Pokaži več",
+ "Switch Invidious Instance": "Preklopi Invidious instanco",
+ "search_message_change_filters_or_query": "Poskusi razširiti iskalno poizvedbo in/ali spremeniti filtre.",
+ "search_message_use_another_instance": " Lahko tudi <a href=\"`x`\">iščeš v drugi istanci</a>.",
+ "Wilson score: ": "Wilsonov rezultat: ",
+ "Engagement: ": "Sodelovanje: ",
+ "Blacklisted regions: ": "Regije na seznamu nedovoljenih: ",
+ "Shared `x`": "V skupni rabi `x`",
+ "Premieres `x`": "Premiere `x`",
+ "View YouTube comments": "Oglej si YouTube komentarje",
+ "View more comments on Reddit": "Prikaži več komentarjev na Reddit",
+ "View `x` comments": {
+ "([^.,0-9]|^)1([^.,0-9]|$)": "Poglej `x` komentar",
+ "": "Poglej `x` komentarjev"
+ },
+ "Quota exceeded, try again in a few hours": "Kvota je presežena, poskusi znova čez nekaj ur",
+ "Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Ne morem se prijaviti, preveri, ali je vklopljeno dvofaktorsko preverjanje pristnosti (avtentikator ali SMS).",
+ "Please sign in using 'Log in with Google'": "Prijavi se z uporabo »Prijava z Googlom«",
+ "Password cannot be empty": "Geslo ne sme biti prazno",
+ "`x` ago": "`x` nazaj",
+ "Load more": "Naloži več",
+ "comments_points_count_0": "{{count}} točka",
+ "comments_points_count_1": "{{count}} točki",
+ "comments_points_count_2": "{{count}} točke",
+ "comments_points_count_3": "{{count}} točk",
+ "Hidden field \"token\" is a required field": "Skrito polje »žeton« je zahtevano polje",
+ "Erroneous challenge": "Napačen izziv",
+ "English": "angleščina",
+ "English (United States)": "angleščina (Združene države)",
+ "Albanian": "albanščina",
+ "Amharic": "amharščina",
+ "Azerbaijani": "azerbajdžanščina",
+ "Bangla": "bengalščina",
+ "Belarusian": "beloruščina",
+ "Burmese": "birmanščina",
+ "Cebuano": "cebuanščina",
+ "Chinese (Hong Kong)": "kitajščina (Hongkong)",
+ "Chinese (Simplified)": "kitajščina (poenostavljena)",
+ "Chinese (Taiwan)": "kitajščina (Tajvan)",
+ "Corsican": "korzijščina",
+ "Croatian": "hrvaščina",
+ "Danish": "danščina",
+ "Dutch": "nizozemščina",
+ "Estonian": "estonščina",
+ "Filipino": "filipinščina",
+ "Finnish": "finščina",
+ "French": "francoščina",
+ "French (auto-generated)": "francoščina (samodejno ustvarjeno)",
+ "Georgian": "gruzinščina",
+ "German": "nemščina",
+ "Greek": "grščina",
+ "Gujarati": "gudžaratščina",
+ "Haitian Creole": "haitijska kreolščina",
+ "Hausa": "havščina",
+ "Hawaiian": "havajščina",
+ "Hmong": "hmonščina",
+ "Hungarian": "madžarščina",
+ "Icelandic": "islandščina",
+ "Igbo": "igbo",
+ "Interlingue": "interlingua",
+ "Italian (auto-generated)": "italijanščina (samodejno ustvarjeno)",
+ "Japanese": "japonščina",
+ "Japanese (auto-generated)": "japonščina (samodejno ustvarjeno)",
+ "Khmer": "kmerščina",
+ "Korean": "korejščina",
+ "Korean (auto-generated)": "korejščina (samodejno ustvarjeno)",
+ "Kurdish": "kurdščina",
+ "Kannada": "kanadejščina",
+ "Latvian": "latvijščina",
+ "Lithuanian": "litovščina",
+ "Luxembourgish": "luksemburščina",
+ "Macedonian": "makedonščina",
+ "Malagasy": "malgaščina",
+ "Malay": "malajščina",
+ "Nepali": "nepalščina",
+ "Norwegian Bokmål": "norveščina bokmal",
+ "Nyanja": "njanščina",
+ "Punjabi": "pandžabščina",
+ "Romanian": "romunščina",
+ "Russian": "ruščina",
+ "Samoan": "samoanščina",
+ "Scottish Gaelic": "škotska galščina",
+ "Shona": "šonaščina",
+ "Sundanese": "sudanščina",
+ "Thai": "tajščina",
+ "Turkish": "turščina",
+ "Turkish (auto-generated)": "turščina (samodejno ustvarjeno)",
+ "Ukrainian": "ukrajinščina",
+ "Urdu": "urdujščina",
+ "Telugu": "telugu",
+ "Vietnamese": "vietnamščina",
+ "Welsh": "valižanščina",
+ "Western Frisian": "zahodnofrizijščina",
+ "Yiddish": "jidiš",
+ "Yoruba": "joruba",
+ "Xhosa": "xhosa",
+ "generic_count_years_0": "{{count}} leto",
+ "generic_count_years_1": "{{count}} leti",
+ "generic_count_years_2": "{{count}} leta",
+ "generic_count_years_3": "{{count}} let",
+ "generic_count_days_0": "{{count}} dan",
+ "generic_count_days_1": "{{count}} dneva",
+ "generic_count_days_2": "{{count}} dni",
+ "generic_count_days_3": "{{count}} dni",
+ "generic_count_hours_0": "{{count}} ura",
+ "generic_count_hours_1": "{{count}} uri",
+ "generic_count_hours_2": "{{count}} ure",
+ "generic_count_hours_3": "{{count}} ur",
+ "generic_count_minutes_0": "{{count}} minuta",
+ "generic_count_minutes_1": "{{count}} minuti",
+ "generic_count_minutes_2": "{{count}} minute",
+ "generic_count_minutes_3": "{{count}} minut",
+ "Search": "Iskanje",
+ "Top": "Vrh",
+ "About": "O aplikaciji",
+ "%A %B %-d, %Y": "%A %-d %B %Y",
+ "Audio mode": "Avdio način",
+ "Videos": "Videoposnetki",
+ "search_filters_date_label": "Datum nalaganja",
+ "search_filters_date_option_today": "Danes",
+ "search_filters_date_option_week": "Ta teden",
+ "search_filters_type_label": "Vrsta",
+ "search_filters_type_option_all": "Katerakoli vrsta",
+ "search_filters_type_option_playlist": "Seznami predvajanja",
+ "search_filters_features_option_subtitles": "Podnapisi/CC",
+ "search_filters_features_option_location": "Lokacija",
+ "footer_donate_page": "Prispevaj",
+ "footer_documentation": "Dokumentacija",
+ "footer_original_source_code": "Izvirna izvorna koda",
+ "none": "ni",
+ "videoinfo_started_streaming_x_ago": "Začetek pretakanja `x` nazaj",
+ "videoinfo_watch_on_youTube": "Oglej si v YouTubu",
+ "user_saved_playlists": "`x` shranjenih seznamov predvajanja",
+ "Video unavailable": "Video ni na voljo",
+ "preferences_save_player_pos_label": "Shrani položaj predvajanja: ",
+ "crash_page_you_found_a_bug": "Videti je, da si v Invidiousu našel hrošča!",
+ "crash_page_read_the_faq": "prebral/a <a href=\"`x`\">Pogosto zastavljena vprašanja (FAQ)</a>",
+ "generic_videos_count_0": "{{count}} video",
+ "generic_videos_count_1": "{{count}} videa",
+ "generic_videos_count_2": "{{count}} videi",
+ "generic_videos_count_3": "{{count}} videov",
+ "generic_views_count_0": "{{count}} ogled",
+ "generic_views_count_1": "{{count}} ogleda",
+ "generic_views_count_2": "{{count}} ogledi",
+ "generic_views_count_3": "{{count}} ogledov",
+ "generic_playlists_count_0": "{{count}} seznam predvajanja",
+ "generic_playlists_count_1": "{{count}} seznama predvajanja",
+ "generic_playlists_count_2": "{{count}} seznami predvajanja",
+ "generic_playlists_count_3": "{{count}} seznamov predvajanja",
+ "generic_subscribers_count_0": "{{count}} naročnik",
+ "generic_subscribers_count_1": "{{count}} naročnika",
+ "generic_subscribers_count_2": "{{count}} naročniki",
+ "generic_subscribers_count_3": "{{count}} naročnikov",
+ "generic_subscriptions_count_0": "{{count}} naročnina",
+ "generic_subscriptions_count_1": "{{count}} naročnini",
+ "generic_subscriptions_count_2": "{{count}} naročnine",
+ "generic_subscriptions_count_3": "{{count}} naročnin",
+ "LIVE": "V ŽIVO",
+ "Shared `x` ago": "Deljeno pred `x`",
+ "View channel on YouTube": "Ogled kanala v YouTubu",
+ "newest": "najnovejši",
+ "Unsubscribe": "Odjavi se",
+ "Authorize token for `x`?": "Odobriti žeton za `x`?",
+ "Import NewPipe subscriptions (.json)": "Uvozi NewPipe (.json) naročnine",
+ "History": "Zgodovina",
+ "JavaScript license information": "Podatki o licenci JavaScript",
+ "oldest": "najstarejši",
+ "popular": "priljubljen",
+ "Export data as JSON": "Izvozi Invidious podatke kot JSON",
+ "Delete account?": "Izbrisati račun?",
+ "An alternative front-end to YouTube": "Alternativni vmesnik za YouTube",
+ "preferences_category_player": "Nastavitve predvajalnika",
+ "preferences_continue_label": "Privzeto predvajaj naslednjega: ",
+ "preferences_watch_history_label": "Omogoči zgodovino ogledov: ",
+ "preferences_quality_option_medium": "srednja",
+ "preferences_quality_option_dash": "DASH (prilagodljiva kakovost)",
+ "preferences_quality_option_small": "majhna",
+ "preferences_quality_dash_option_worst": "najslabša",
+ "preferences_quality_dash_label": "Prednostna kakovost videoposnetkov DASH: ",
+ "preferences_comments_label": "Privzeti komentarji: ",
+ "preferences_quality_dash_option_auto": "samodejna",
+ "preferences_quality_dash_option_2160p": "2160p",
+ "preferences_quality_dash_option_144p": "144p",
+ "youtube": "YouTube",
+ "invidious": "Invidious",
+ "preferences_vr_mode_label": "Interaktivni videoposnetki na 360 stopinj (zahteva WebGL): ",
+ "preferences_captions_label": "Privzeti napisi: ",
+ "Fallback captions: ": "Pomožni napisi: ",
+ "preferences_extend_desc_label": "Samodejno razširi opis videoposnetka: ",
+ "preferences_related_videos_label": "Prikaži povezane videoposnetke: ",
+ "preferences_annotations_label": "Privzeto prikaži opombe: ",
+ "preferences_category_visual": "Vizualne nastavitve",
+ "preferences_region_label": "Država vsebine: ",
+ "Dark mode: ": "Temni način: ",
+ "preferences_dark_mode_label": "Tema: ",
+ "preferences_category_misc": "Različne nastavitve",
+ "preferences_category_subscription": "Nastavitve naročnine",
+ "Unlisted": "Nerazporejeno",
+ "Enable web notifications": "Omogoči spletna obvestila",
+ "`x` is live": "`x` je v živo",
+ "Manage subscriptions": "Upravljaj naročnine",
+ "Manage tokens": "Upravljaj žetone",
+ "Subscription manager": "Upravitelj naročnin",
+ "`x` uploaded a video": "`x` je naložil/a videoposnetek",
+ "preferences_show_nick_label": "Prikaži vzdevek na vrhu: ",
+ "search_message_no_results": "Ni zadetkov.",
+ "Save preferences": "Shrani nastavitve",
+ "Subscriptions": "Naročnine",
+ "Report statistics: ": "Poročilo o statistiki: ",
+ "subscriptions_unseen_notifs_count_0": "{{count}} neogledano obvestilo",
+ "subscriptions_unseen_notifs_count_1": "{{count}} neogledani obvestili",
+ "subscriptions_unseen_notifs_count_2": "{{count}} neogledana obvestila",
+ "subscriptions_unseen_notifs_count_3": "{{count}} neogledanih obvestil",
+ "View JavaScript license information.": "Oglej si informacije o licenci za JavaScript.",
+ "Show less": "Pokaži manj",
+ "Watch on YouTube": "Oglej si v YouTubu",
+ "Source available here.": "Izvorna koda na voljo tukaj.",
+ "License: ": "Licenca: ",
+ "View privacy policy.": "Oglej si pravilnik o zasebnosti.",
+ "Public": "Javno",
+ "Create playlist": "Ustvari seznam predvajanja",
+ "Hide annotations": "Skrij opombe",
+ "Show annotations": "Pokaži opombe",
+ "Genre: ": "Žanr: ",
+ "Family friendly? ": "Družinam prijazno? ",
+ "Whitelisted regions: ": "Regije na seznamu dovoljenih: ",
+ "Premieres in `x`": "Premiere v `x`",
+ "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Živjo! Izgleda, da imaš izklopljene JavaScripte . Klikni tukaj, če si želiš ogledati komentarje, vendar vedi, da bo lahko nalaganje trajajo nekoliko dlje.",
+ "Show replies": "Pokaži odgovore",
+ "Erroneous CAPTCHA": "Napačna CAPTCHA",
+ "User ID is a required field": "ID uporabnika je obvezno polje",
+ "Password is a required field": "Geslo je obvezno polje",
+ "Wrong username or password": "Napačno uporabniško ime ali geslo",
+ "Password cannot be longer than 55 characters": "Geslo ne sme biti daljše od 55 znakov",
+ "channel:`x`": "kanal: `x`",
+ "Could not fetch comments": "Ni bilo mogoče pridobiti komentarjev",
+ "Could not pull trending pages.": "Ni bilo mogoče povleči trendovskih strani.",
+ "Please log in": "Prosim, prijavi se",
+ "Playlist does not exist.": "Seznam predvajanja ne obstaja.",
+ "Incorrect password": "Napačno geslo",
+ "View Reddit comments": "Oglej si komentarje na Redditu",
+ "This channel does not exist.": "Ta kanal ne obstaja.",
+ "Hide replies": "Skrij odgovore",
+ "Invalid TFA code": "Neveljavna koda TFA",
+ "Login failed. This may be because two-factor authentication is not turned on for your account.": "Prijava ni uspela. To je lahko zato, ker za tvoj račun ni vklopljeno dvofaktorsko preverjanje pristnosti.",
+ "Invidious Private Feed for `x`": "Invidious zasebni vir za `x`",
+ "Deleted or invalid channel": "Izbrisan ali neveljaven kanal",
+ "Empty playlist": "Prazen seznam predvajanja",
+ "No such user": "Ni tega uporabnika",
+ "Token is expired, please try again": "Žeton je potekel, poskusi znova",
+ "English (United Kingdom)": "angleščina (Združeno kraljestvo)",
+ "Wrong answer": "Napačen odgovor",
+ "CAPTCHA is a required field": "CAPTCHA je obvezno polje",
+ "Could not get channel info.": "Ni bilo mogoče dobiti informacij o kanalu.",
+ "comments_view_x_replies_0": "Poglej {{count}} odgovor",
+ "comments_view_x_replies_1": "Poglej {{count}} odgovora",
+ "comments_view_x_replies_2": "Poglej {{count}} odgovore",
+ "comments_view_x_replies_3": "Poglej {{count}} odgovorov",
+ "Could not create mix.": "Ni bilo mogoče ustvariti mixa.",
+ "Not a playlist.": "Ni seznam predvajanja.",
+ "Hidden field \"challenge\" is a required field": "Skrito polje »izziv« je obvezno polje",
+ "Erroneous token": "Napačen žeton",
+ "Afrikaans": "afrikanščina",
+ "Arabic": "arabščina",
+ "Armenian": "armenščina",
+ "English (auto-generated)": "angleščina (samodejno ustvarjeno)",
+ "Bulgarian": "bolgarščina",
+ "Catalan": "katalonščina",
+ "Cantonese (Hong Kong)": "kantonščina (Hongkong)",
+ "Chinese (Traditional)": "kitajščina (tradicionalna)",
+ "Basque": "baskovščina",
+ "Czech": "češčina",
+ "Bosnian": "bosanščina",
+ "Chinese": "kitajščina",
+ "Chinese (China)": "kitajščina (Kitajska)",
+ "Dutch (auto-generated)": "nizozemščina (samodejno ustvarjeno)",
+ "Esperanto": "esperanto",
+ "Galician": "galicijščina",
+ "German (auto-generated)": "nemščina (samodejno ustvarjeno)",
+ "Hebrew": "hebrejščina",
+ "Malayalam": "malajalamščina",
+ "Hindi": "hindijščina",
+ "Indonesian": "indonezijščina",
+ "Kazakh": "kazahstanščina",
+ "Indonesian (auto-generated)": "indonezijščina (samodejno generirano)",
+ "Irish": "irščina",
+ "Persian": "perzijščina",
+ "Slovak": "slovaščina",
+ "Italian": "italijanščina",
+ "Maori": "maorščina",
+ "Portuguese": "portugalščina",
+ "Javanese": "javanščina",
+ "Kyrgyz": "kirgiščina",
+ "Lao": "laoščina",
+ "Latin": "latinščina",
+ "Mongolian": "mongolščina",
+ "Portuguese (auto-generated)": "portugalščina (samodejno ustvarjeno)",
+ "Sindhi": "sindščina",
+ "Maltese": "malteščina",
+ "Marathi": "maratščina",
+ "Pashto": "paštu",
+ "Polish": "poljščina",
+ "Portuguese (Brazil)": "portugalščina (Brazilija)",
+ "Fallback comments: ": "Nadomestni komentarji: ",
+ "Gaming": "Igralništvo",
+ "Russian (auto-generated)": "ruščina (samodejno ustvarjeno)",
+ "Serbian": "srbščina",
+ "Sinhala": "singalščina",
+ "Slovenian": "slovenščina",
+ "Somali": "somalijščina",
+ "Spanish": "španščina",
+ "Southern Sotho": "južni sotho",
+ "Spanish (auto-generated)": "španščina (samodejno ustvarjeno)",
+ "Spanish (Mexico)": "španščina (Mehika)",
+ "Spanish (Latin America)": "španščina (Latinska Amerika)",
+ "Spanish (Spain)": "španščina (Španija)",
+ "Tajik": "tadžiščina",
+ "Tamil": "tamilščina",
+ "generic_count_weeks_0": "{{count}} teden",
+ "generic_count_weeks_1": "{{count}} tedna",
+ "generic_count_weeks_2": "{{count}} tedne",
+ "generic_count_weeks_3": "{{count}} tednov",
+ "Swahili": "svahilščina",
+ "Swedish": "švedščina",
+ "Vietnamese (auto-generated)": "vietnamščina (samodejno ustvarjeno)",
+ "generic_count_months_0": "{{count}} mesec",
+ "generic_count_months_1": "{{count}} meseca",
+ "generic_count_months_2": "{{count}} mesece",
+ "generic_count_months_3": "{{count}} mesecev",
+ "Uzbek": "uzbeščina",
+ "Zulu": "zulujščina",
+ "generic_count_seconds_0": "{{count}} sekunda",
+ "generic_count_seconds_1": "{{count}} sekundi",
+ "generic_count_seconds_2": "{{count}} sekunde",
+ "generic_count_seconds_3": "{{count}} sekund",
+ "Popular": "Priljubljeni",
+ "Music": "Glasba",
+ "Movies": "Filmi",
+ "YouTube comment permalink": "Stalna povezava za komentar na YouTubu",
+ "search_filters_title": "Filtri",
+ "preferences_locale_label": "Jezik: ",
+ "Rating: ": "Ocena: ",
+ "Default": "Privzeto",
+ "News": "Novice",
+ "Download as: ": "Prenesi kot: ",
+ "(edited)": "(urejeno)",
+ "View as playlist": "Poglej kot seznam predvajanja",
+ "Download": "Prenesi",
+ "permalink": "stalna povezava",
+ "`x` marked it with a ❤": "`x` ga je označil/a z ❤",
+ "Community": "Skupnost",
+ "search_filters_features_option_three_sixty": "360°",
+ "Video mode": "Video način",
+ "search_filters_features_option_c_commons": "Creative Commons",
+ "search_filters_features_option_three_d": "3D",
+ "Playlists": "Seznami predvajanja",
+ "search_filters_date_option_none": "Katerikoli datum",
+ "search_filters_date_option_month": "Ta mesec",
+ "search_filters_date_option_year": "Letos",
+ "search_filters_type_option_movie": "Film",
+ "search_filters_duration_option_long": "Dolg (> 20 minut)",
+ "search_filters_features_label": "Lastnosti",
+ "search_filters_features_option_four_k": "4K",
+ "search_filters_features_option_hdr": "HDR",
+ "next_steps_error_message_refresh": "Osveži",
+ "search_filters_date_option_hour": "Zadnja ura",
+ "search_filters_features_option_purchased": "Kupljeno",
+ "search_filters_sort_label": "Razvrsti po",
+ "search_filters_sort_option_views": "številu ogledov",
+ "Current version: ": "Trenutna različica: ",
+ "search_filters_features_option_live": "V živo",
+ "search_filters_features_option_hd": "HD",
+ "search_filters_type_option_channel": "Kanal",
+ "search_filters_type_option_show": "Pokaži",
+ "search_filters_duration_label": "Trajanje",
+ "search_filters_duration_option_none": "Poljubno trajanje",
+ "search_filters_duration_option_short": "Kratek (< 4 minute)",
+ "search_filters_duration_option_medium": "Srednji (4 - 20 minut)",
+ "search_filters_features_option_vr180": "VR180",
+ "search_filters_sort_option_date": "datumu nalaganja",
+ "search_filters_type_option_video": "Videoposnetek",
+ "search_filters_sort_option_relevance": "ustreznosti",
+ "search_filters_sort_option_rating": "oceni",
+ "search_filters_apply_button": "Uporabi izbrane filtre",
+ "next_steps_error_message": "Po tem moraš poskusiti: ",
+ "next_steps_error_message_go_to_youtube": "Pojdi na YouTube",
+ "footer_source_code": "Izvorna koda",
+ "footer_modfied_source_code": "Spremenjena izvorna koda",
+ "user_created_playlists": "`x` ustvarjenih seznamov predvajanja",
+ "adminprefs_modified_source_code_url_label": "URL do shrambe spremenjene izvorne kode",
+ "videoinfo_youTube_embed_link": "Vdelati",
+ "videoinfo_invidious_embed_link": "Povezava za vdelavo",
+ "crash_page_switch_instance": "poskušal/a <a href=\"`x`\">uporabiti drugo instanco</a>",
+ "download_subtitles": "Podnapisi - `x` (.vtt)",
+ "crash_page_refresh": "poskušal/a <a href=\"`x`\">osvežiti stran</a>",
+ "crash_page_before_reporting": "Preden prijaviš napako, se prepričaj, da si:",
+ "crash_page_search_issue": "preiskal/a <a href=\"`x`\">obstoječe težave na GitHubu</a>",
+ "crash_page_report_issue": "Če nič od navedenega ni pomagalo, prosim <a href=\"`x`\">odpri novo težavo v GitHubu</a> (po možnosti v angleščini) in v svoje sporočilo vključi naslednje besedilo (tega besedila NE prevajaj):"
+}
diff --git a/locales/sq.json b/locales/sq.json
index c87c5446..76f1eaa3 100644
--- a/locales/sq.json
+++ b/locales/sq.json
@@ -147,7 +147,6 @@
"Show less": "Shfaq më pak",
"Watch on YouTube": "Shiheni në YouTube",
"Switch Invidious Instance": "Ndërroni Instancë Invidious",
- "Broken? Try another Invidious Instance": "E prishur? Provoni një tjetër Instancë Invidious",
"Hide annotations": "Fshihi shënimet",
"Show annotations": "Shfaq shënime",
"License: ": "Licencë: ",
@@ -371,7 +370,6 @@
"Nepali": "Nepaleze",
"Norwegian Bokmål": "Norvegjishte Bokmål",
"search_filters_features_option_three_sixty": "360°",
- "search_filters_label": "Filtroji",
"Current version: ": "Versioni i tanishëm: ",
"next_steps_error_message": "Pas të cilës duhet të provoni të: ",
"next_steps_error_message_refresh": "Rifreskoje",
@@ -448,5 +446,6 @@
"Import YouTube subscriptions": "Importoni pajtime YouTube/OPML",
"Export data as JSON": "Eksportoji të dhënat Invidious si JSON",
"preferences_vr_mode_label": "Video me ndërveprim 360 gradë (lyp WebGL): ",
- "Shared `x`": "Ndau me të tjerë `x`"
+ "Shared `x`": "Ndau me të tjerë `x`",
+ "search_filters_title": "Filtra"
}
diff --git a/locales/sr.json b/locales/sr.json
index 620c83dc..d2f990ae 100644
--- a/locales/sr.json
+++ b/locales/sr.json
@@ -150,7 +150,6 @@
"search_filters_features_option_c_commons": "Creative Commons (Licenca)",
"search_filters_features_option_three_d": "3D",
"search_filters_features_option_hdr": "Video Visoke Rezolucije",
- "search_filters_label": "Filter",
"next_steps_error_message": "Nakon čega bi trebali probati: ",
"next_steps_error_message_go_to_youtube": "Idi na YouTube",
"footer_documentation": "Dokumentacija",
@@ -226,7 +225,6 @@
"preferences_captions_label": "Podrazumevani titl: ",
"Music": "Muzika",
"search_filters_type_label": "Tip",
- "Broken? Try another Invidious Instance": "Ne funkcioniše ispravno? Probajte drugu Invidious instancu",
"Tamil": "Tamilski",
"Save preferences": "Sačuvaj podešavanja",
"Only show latest unwatched video from channel: ": "Prikaži samo poslednje video klipove koji nisu pogledani sa kanala: ",
@@ -369,5 +367,6 @@
"unsubscribe": "prekini sa praćenjem",
"Blacklisted regions: ": "Zabranjene oblasti: ",
"Polish": "Poljski",
- "Yoruba": "Joruba"
+ "Yoruba": "Joruba",
+ "search_filters_title": "Filter"
}
diff --git a/locales/sr_Cyrl.json b/locales/sr_Cyrl.json
index 6aea400a..c0f1224f 100644
--- a/locales/sr_Cyrl.json
+++ b/locales/sr_Cyrl.json
@@ -189,7 +189,6 @@
"search_filters_features_option_c_commons": "Creative Commons (Лиценца)",
"search_filters_features_option_live": "Уживо",
"search_filters_features_option_location": "Локација",
- "search_filters_label": "Филтер",
"next_steps_error_message": "Након чега би требали пробати: ",
"footer_donate_page": "Донирај",
"footer_documentation": "Документација",
@@ -345,7 +344,6 @@
"search_filters_features_option_subtitles": "Титл/Превод",
"preferences_extend_desc_label": "Аутоматски прикажи цео опис видеа: ",
"Show less": "Прикажи мање",
- "Broken? Try another Invidious Instance": "Не функционише исправно? Пробајте другу Invidious инстанцу",
"Family friendly? ": "Погодно за породицу? ",
"Premieres `x`": "Премерe у `x`",
"Bosnian": "Босански",
@@ -369,5 +367,6 @@
"Hebrew": "Хебрејски",
"Korean": "Корејски",
"Kurdish": "Курдски",
- "Malay": "Малајски"
+ "Malay": "Малајски",
+ "search_filters_title": "Филтер"
}
diff --git a/locales/sv-SE.json b/locales/sv-SE.json
index 44cb92ed..777899d0 100644
--- a/locales/sv-SE.json
+++ b/locales/sv-SE.json
@@ -139,7 +139,6 @@
"Show less": "Visa mindre",
"Watch on YouTube": "Titta på YouTube",
"Switch Invidious Instance": "Byt Invidious Instans",
- "Broken? Try another Invidious Instance": "Trasig? Prova en annan Invidious Instance",
"Hide annotations": "Dölj länkar-i-video",
"Show annotations": "Visa länkar-i-video",
"Genre: ": "Genre: ",
@@ -353,7 +352,6 @@
"search_filters_features_option_four_k": "4k",
"search_filters_features_option_location": "plats",
"search_filters_features_option_hdr": "hdr",
- "search_filters_label": "Filter",
"Current version: ": "Nuvarande version: ",
"next_steps_error_message_refresh": "Uppdatera",
"next_steps_error_message_go_to_youtube": "Gå till Youtube",
@@ -361,5 +359,6 @@
"footer_source_code": "Källkod",
"search_filters_duration_option_long": "Lång (> 20 minuter)",
"footer_documentation": "Dokumentation",
- "search_filters_duration_option_short": "Kort (< 4 minuter)"
+ "search_filters_duration_option_short": "Kort (< 4 minuter)",
+ "search_filters_title": "Filter"
}
diff --git a/locales/tr.json b/locales/tr.json
index 75de6d33..b1991c35 100644
--- a/locales/tr.json
+++ b/locales/tr.json
@@ -141,7 +141,6 @@
"Show less": "Daha az göster",
"Watch on YouTube": "YouTube'da izle",
"Switch Invidious Instance": "Invidious Örneğini Değiştir",
- "Broken? Try another Invidious Instance": "Bozuk mu? Başka bir Invidious örneğini deneyin",
"Hide annotations": "Ek açıklamaları gizle",
"Show annotations": "Ek açıklamaları göster",
"Genre: ": "Tür: ",
@@ -355,7 +354,6 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "Konum",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "Filtrele",
"Current version: ": "Şu anki sürüm: ",
"next_steps_error_message": "Bundan sonra şunları denemelisiniz: ",
"next_steps_error_message_refresh": "Yenile",
@@ -461,5 +459,16 @@
"Portuguese (auto-generated)": "Portekizce (otomatik oluşturuldu)",
"Spanish (Spain)": "İspanyolca (İspanya)",
"Vietnamese (auto-generated)": "Vietnamca (otomatik oluşturuldu)",
- "preferences_watch_history_label": "İzleme geçmişini etkinleştir: "
+ "preferences_watch_history_label": "İzleme geçmişini etkinleştir: ",
+ "search_message_use_another_instance": " Ayrıca <a href=\"`x`\">başka bir örnekte arayabilirsiniz</a>.",
+ "search_filters_type_option_all": "Herhangi bir tür",
+ "search_filters_duration_option_none": "Herhangi bir süre",
+ "search_message_no_results": "Sonuç bulunamadı.",
+ "search_filters_date_label": "Yükleme tarihi",
+ "search_filters_apply_button": "Seçili filtreleri uygula",
+ "search_filters_date_option_none": "Herhangi bir tarih",
+ "search_filters_duration_option_medium": "Orta (4 - 20 dakika)",
+ "search_filters_features_option_vr180": "VR180",
+ "search_filters_title": "Filtreler",
+ "search_message_change_filters_or_query": "Arama sorgunuzu genişletmeyi ve/veya filtreleri değiştirmeyi deneyin."
}
diff --git a/locales/uk.json b/locales/uk.json
index 5b006999..dd03d559 100644
--- a/locales/uk.json
+++ b/locales/uk.json
@@ -355,7 +355,6 @@
"generic_count_hours_0": "{{count}} годину",
"generic_count_hours_1": "{{count}} години",
"generic_count_hours_2": "{{count}} годин",
- "content_type": "Тип",
"crash_page_switch_instance": "спробуйте <a href=\"`x`\">використати інший сервер</a>",
"crash_page_read_the_faq": "прочитайте <a href=\"`x`\">часті питання (ЧаП)</a>",
"crash_page_search_issue": "перегляньте <a href=\"`x`\">наявні обговорення на GitHub</a>",
@@ -386,15 +385,6 @@
"Spanish (auto-generated)": "Іспанська (автогенератор)",
"Spanish (Mexico)": "Іспанська (Мексика)",
"Spanish (Spain)": "Іспанська (Іспанія)",
- "views": "Кількість переглядів",
- "today": "Сьогодні",
- "playlist": "Список відтворення",
- "long": "Довге (понад 20 хвилин)",
- "hd": "HD",
- "creative_commons": "Creative Commons",
- "3d": "3D",
- "hdr": "HDR",
- "360": "360°",
"next_steps_error_message_go_to_youtube": "Перейти до YouTube",
"footer_donate_page": "Пожертвувати",
"footer_documentation": "Документація",
@@ -415,29 +405,11 @@
"preferences_save_player_pos_label": "Зберегти позицію відтворення: ",
"preferences_show_nick_label": "Псевдонім угорі: ",
"Show more": "Докладніше",
- "week": "Цей тиждень",
- "year": "Цей рік",
- "video": "Відео",
- "channel": "Канал",
- "subtitles": "Субтитри",
- "live": "Наживо",
- "4k": "4K",
- "filter": "Фільтр",
"next_steps_error_message": "Після чого спробуйте: ",
"next_steps_error_message_refresh": "Оновити сторінку",
- "relevance": "Доречність",
- "rating": "Рейтинг",
- "duration": "Тривалість",
- "sort": "Порядок",
- "movie": "Фільм",
"Search": "Пошук",
- "location": "Місце",
"preferences_extend_desc_label": "Автоматично розширювати опис відео: ",
- "month": "Цей місяць",
- "features": "Функції",
"preferences_category_misc": "Різноманітні параметри",
- "date": "Дата вивантаження",
- "hour": "Ця година",
"Show less": "Коротше",
"preferences_quality_option_small": "Низька",
"preferences_quality_dash_option_240p": "240p",
@@ -453,18 +425,14 @@
"preferences_automatic_instance_redirect_label": "Автоматична зміна сервера (redirect.invidious.io як резерв): ",
"Switch Invidious Instance": "Інший сервер Invidious",
"preferences_quality_dash_option_480p": "480p",
- "Broken? Try another Invidious Instance": "Не працює? Спробуйте інший сервер Invidious",
"Chinese (Taiwan)": "Китайська (Тайвань)",
"Dutch (auto-generated)": "Нідерландська (автогенератор)",
"Indonesian (auto-generated)": "Індонезійська (автогенератор)",
"Japanese (auto-generated)": "Японська (автогенератор)",
- "show": "Шоу",
"Korean (auto-generated)": "Корейська (автогенератор)",
"generic_count_months_0": "{{count}} місяць",
"generic_count_months_1": "{{count}} місяці",
"generic_count_months_2": "{{count}} місяців",
- "short": "Коротке (до 4 хвилин)",
- "purchased": "Придбання",
"videoinfo_youTube_embed_link": "Вкласти",
"generic_count_minutes_0": "{{count}} хвилину",
"generic_count_minutes_1": "{{count}} хвилини",
@@ -477,5 +445,46 @@
"download_subtitles": "Субтитри — `x` (.vtt)",
"comments_points_count_0": "{{count}} пункт",
"comments_points_count_1": "{{count}} пункти",
- "comments_points_count_2": "{{count}} пунктів"
+ "comments_points_count_2": "{{count}} пунктів",
+ "search_filters_features_option_three_d": "3D",
+ "search_filters_features_option_location": "Геомітка",
+ "search_filters_duration_option_none": "Будь-які",
+ "search_filters_features_option_hd": "HD",
+ "search_message_change_filters_or_query": "Спробуйте ширший запит і/або інші фільтри.",
+ "search_filters_type_option_all": "Будь-що",
+ "search_filters_type_option_movie": "Фільм",
+ "search_filters_type_option_show": "Шоу",
+ "search_filters_duration_label": "Тривалість",
+ "search_filters_duration_option_short": "Короткі (до 4 хвилин)",
+ "search_message_no_results": "Результатів не знайдено.",
+ "search_filters_date_label": "Дата вивантаження",
+ "search_filters_date_option_none": "Будь-яка дата",
+ "search_filters_date_option_today": "Сьогодні",
+ "search_filters_date_option_week": "Цей тиждень",
+ "search_filters_type_label": "Тип",
+ "search_filters_type_option_channel": "Канал",
+ "search_message_use_another_instance": " Можете також <a href=\"`x`\">пошукати іншим сервером</a>.",
+ "search_filters_title": "Фільтри",
+ "search_filters_date_option_hour": "Остання година",
+ "search_filters_date_option_month": "Цей місяць",
+ "search_filters_date_option_year": "Цей рік",
+ "search_filters_type_option_video": "Відео",
+ "search_filters_type_option_playlist": "Добірка",
+ "search_filters_duration_option_medium": "Середні (4–20 хвилин)",
+ "search_filters_duration_option_long": "Довгі (понад 20 хвилин)",
+ "search_filters_features_label": "Особливості",
+ "search_filters_features_option_live": "Наживо",
+ "search_filters_features_option_four_k": "4K",
+ "search_filters_features_option_subtitles": "Субтитри",
+ "search_filters_features_option_c_commons": "Creative Commons",
+ "search_filters_features_option_three_sixty": "360°",
+ "search_filters_features_option_hdr": "HDR",
+ "search_filters_sort_label": "Спершу",
+ "search_filters_sort_option_date": "Нещодавні",
+ "search_filters_apply_button": "Застосувати фільтри",
+ "search_filters_features_option_vr180": "VR180",
+ "search_filters_features_option_purchased": "Придбано",
+ "search_filters_sort_option_relevance": "Відповідні",
+ "search_filters_sort_option_rating": "Рейтингові",
+ "search_filters_sort_option_views": "Популярні"
}
diff --git a/locales/vi.json b/locales/vi.json
index 3112ef4a..709013a2 100644
--- a/locales/vi.json
+++ b/locales/vi.json
@@ -138,7 +138,6 @@
"Show less": "Hiện ít hơn",
"Watch on YouTube": "Xem trên YouTube",
"Switch Invidious Instance": "Chuyển phiên bản Invidious",
- "Broken? Try another Invidious Instance": "Bị hỏng? Hãy thử một Phiên bản Invidious khác",
"Hide annotations": "Ẩn chú thích",
"Show annotations": "Hiển thị chú thích",
"Genre: ": "Thể loại: ",
@@ -341,6 +340,6 @@
"search_filters_features_option_four_k": "4k",
"search_filters_features_option_location": "vị trí",
"search_filters_features_option_hdr": "hdr",
- "search_filters_label": "bộ lọc",
- "Current version: ": "Phiên bản hiện tại: "
+ "Current version: ": "Phiên bản hiện tại: ",
+ "search_filters_title": "bộ lọc"
}
diff --git a/locales/zh-CN.json b/locales/zh-CN.json
index bc1a3e4b..ed180628 100644
--- a/locales/zh-CN.json
+++ b/locales/zh-CN.json
@@ -148,7 +148,6 @@
"Show less": "显示较少",
"Watch on YouTube": "在 YouTube 观看",
"Switch Invidious Instance": "切换 Invidious 实例",
- "Broken? Try another Invidious Instance": "无法正常工作? 尝试另一个 Invidious 实例",
"Hide annotations": "隐藏注释",
"Show annotations": "显示注释",
"Genre: ": "风格: ",
@@ -371,7 +370,6 @@
"search_filters_features_option_four_k": "4k",
"search_filters_features_option_location": "位置",
"search_filters_features_option_hdr": "hdr",
- "search_filters_label": "过滤器",
"Current version: ": "当前版本: ",
"next_steps_error_message": "在此之后你应尝试: ",
"next_steps_error_message_refresh": "刷新",
@@ -445,5 +443,16 @@
"French (auto-generated)": "法语 (自动生成)",
"Turkish (auto-generated)": "土耳其语 (自动生成)",
"Spanish (Spain)": "西班牙语 (西班牙)",
- "preferences_watch_history_label": "启用观看历史: "
+ "preferences_watch_history_label": "启用观看历史: ",
+ "search_message_use_another_instance": " 你也可以 <a href=\"`x`\">在另一实例上搜索</a>。",
+ "search_filters_title": "过滤器",
+ "search_filters_date_label": "上传日期",
+ "search_filters_apply_button": "应用所选过滤器",
+ "search_message_no_results": "没找到结果。",
+ "search_filters_duration_option_medium": "中等(4-20 分钟)",
+ "search_filters_date_option_none": "任意日期",
+ "search_message_change_filters_or_query": "尝试扩大你的搜索查询和/或更改过滤器。",
+ "search_filters_duration_option_none": "任意时长",
+ "search_filters_type_option_all": "任意类型",
+ "search_filters_features_option_vr180": "VR180"
}
diff --git a/locales/zh-TW.json b/locales/zh-TW.json
index 189dba18..4b6fa71b 100644
--- a/locales/zh-TW.json
+++ b/locales/zh-TW.json
@@ -148,7 +148,6 @@
"Show less": "顯示較少",
"Watch on YouTube": "在 YouTube 上觀看",
"Switch Invidious Instance": "切換 Invidious 站台",
- "Broken? Try another Invidious Instance": "故障了嗎?試試看其他 Invidious 站台吧",
"Hide annotations": "隱藏註釋",
"Show annotations": "顯示註釋",
"Genre: ": "風格: ",
@@ -371,7 +370,6 @@
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "位置",
"search_filters_features_option_hdr": "HDR",
- "search_filters_label": "篩選條件",
"Current version: ": "目前版本: ",
"next_steps_error_message": "之後您應該嘗試: ",
"next_steps_error_message_refresh": "重新整理",
@@ -445,5 +443,16 @@
"Portuguese (Brazil)": "葡萄牙語(巴西)",
"Japanese (auto-generated)": "日語(自動產生)",
"Portuguese (auto-generated)": "葡萄牙語(自動產生)",
- "preferences_watch_history_label": "啟用觀看紀錄: "
+ "preferences_watch_history_label": "啟用觀看紀錄: ",
+ "search_message_change_filters_or_query": "嘗試擴大您的查詢字詞與/或變更過濾條件。",
+ "search_filters_apply_button": "套用選定的過濾條件",
+ "search_message_no_results": "找不到結果。",
+ "search_filters_duration_option_none": "任何時長",
+ "search_filters_duration_option_medium": "中等(4到20分鐘)",
+ "search_filters_features_option_vr180": "VR180",
+ "search_message_use_another_instance": " 您也可以<a href=\"`x`\">在其他站台上搜尋</a>。",
+ "search_filters_title": "過濾條件",
+ "search_filters_date_label": "上傳日期",
+ "search_filters_type_option_all": "任何類型",
+ "search_filters_date_option_none": "任何日期"
}
diff --git a/shard.lock b/shard.lock
index be4333c1..cdce1160 100644
--- a/shard.lock
+++ b/shard.lock
@@ -14,11 +14,11 @@ shards:
exception_page:
git: https://github.com/crystal-loot/exception_page.git
- version: 0.2.0
+ version: 0.2.2
kemal:
git: https://github.com/kemalcr/kemal.git
- version: 1.1.0
+ version: 1.1.2
kilt:
git: https://github.com/jeromegn/kilt.git
diff --git a/shard.yml b/shard.yml
index bf382ec3..9c9b0d37 100644
--- a/shard.yml
+++ b/shard.yml
@@ -18,7 +18,10 @@ dependencies:
version: ~> 0.18.0
kemal:
github: kemalcr/kemal
- version: ~> 1.1.0
+ version: ~> 1.1.2
+ kilt:
+ github: jeromegn/kilt
+ version: ~> 0.6.1
protodec:
github: iv-org/protodec
version: ~> 0.1.4
diff --git a/spec/invidious/search/query_spec.cr b/spec/invidious/search/query_spec.cr
new file mode 100644
index 00000000..4853e9e9
--- /dev/null
+++ b/spec/invidious/search/query_spec.cr
@@ -0,0 +1,200 @@
+require "../../../src/invidious/search/filters"
+require "../../../src/invidious/search/query"
+
+require "http/params"
+require "spectator"
+
+Spectator.configure do |config|
+ config.fail_blank
+ config.randomize
+end
+
+Spectator.describe Invidious::Search::Query do
+ describe Type::Regular do
+ # -------------------
+ # Query parsing
+ # -------------------
+
+ it "parses query with URL prameters (q)" do
+ query = described_class.new(
+ HTTP::Params.parse("q=What+is+Love+10+hour&type=video&duration=long"),
+ Invidious::Search::Query::Type::Regular, nil
+ )
+
+ expect(query.type).to eq(Invidious::Search::Query::Type::Regular)
+ expect(query.channel).to be_empty
+ expect(query.text).to eq("What is Love 10 hour")
+
+ expect(query.filters).to eq(
+ Invidious::Search::Filters.new(
+ type: Invidious::Search::Filters::Type::Video,
+ duration: Invidious::Search::Filters::Duration::Long
+ )
+ )
+ end
+
+ it "parses query with URL prameters (search_query)" do
+ query = described_class.new(
+ HTTP::Params.parse("search_query=What+is+Love+10+hour&type=video&duration=long"),
+ Invidious::Search::Query::Type::Regular, nil
+ )
+
+ expect(query.type).to eq(Invidious::Search::Query::Type::Regular)
+ expect(query.channel).to be_empty
+ expect(query.text).to eq("What is Love 10 hour")
+
+ expect(query.filters).to eq(
+ Invidious::Search::Filters.new(
+ type: Invidious::Search::Filters::Type::Video,
+ duration: Invidious::Search::Filters::Duration::Long
+ )
+ )
+ end
+
+ it "parses query with legacy filters (q)" do
+ query = described_class.new(
+ HTTP::Params.parse("q=Nyan+cat+duration:long"),
+ Invidious::Search::Query::Type::Regular, nil
+ )
+
+ expect(query.type).to eq(Invidious::Search::Query::Type::Regular)
+ expect(query.channel).to be_empty
+ expect(query.text).to eq("Nyan cat")
+
+ expect(query.filters).to eq(
+ Invidious::Search::Filters.new(
+ duration: Invidious::Search::Filters::Duration::Long
+ )
+ )
+ end
+
+ it "parses query with legacy filters (search_query)" do
+ query = described_class.new(
+ HTTP::Params.parse("search_query=Nyan+cat+duration:long"),
+ Invidious::Search::Query::Type::Regular, nil
+ )
+
+ expect(query.type).to eq(Invidious::Search::Query::Type::Regular)
+ expect(query.channel).to be_empty
+ expect(query.text).to eq("Nyan cat")
+
+ expect(query.filters).to eq(
+ Invidious::Search::Filters.new(
+ duration: Invidious::Search::Filters::Duration::Long
+ )
+ )
+ end
+
+ it "parses query with both URL params and legacy filters" do
+ query = described_class.new(
+ HTTP::Params.parse("q=Vamos+a+la+playa+duration:long&type=Video&date=year"),
+ Invidious::Search::Query::Type::Regular, nil
+ )
+
+ expect(query.type).to eq(Invidious::Search::Query::Type::Regular)
+ expect(query.channel).to be_empty
+ expect(query.text).to eq("Vamos a la playa duration:long")
+
+ expect(query.filters).to eq(
+ Invidious::Search::Filters.new(
+ type: Invidious::Search::Filters::Type::Video,
+ date: Invidious::Search::Filters::Date::Year
+ )
+ )
+ end
+
+ # -------------------
+ # Type switching
+ # -------------------
+
+ it "switches to channel search (URL param)" do
+ query = described_class.new(
+ HTTP::Params.parse("q=thunderbolt+4&channel=UC0vBXGSyV14uvJ4hECDOl0Q"),
+ Invidious::Search::Query::Type::Regular, nil
+ )
+
+ expect(query.type).to eq(Invidious::Search::Query::Type::Channel)
+ expect(query.channel).to eq("UC0vBXGSyV14uvJ4hECDOl0Q")
+ expect(query.text).to eq("thunderbolt 4")
+ expect(query.filters.default?).to be_true
+ end
+
+ it "switches to channel search (legacy)" do
+ query = described_class.new(
+ HTTP::Params.parse("q=channel%3AUCRPdsCVuH53rcbTcEkuY4uQ+rdna3"),
+ Invidious::Search::Query::Type::Regular, nil
+ )
+
+ expect(query.type).to eq(Invidious::Search::Query::Type::Channel)
+ expect(query.channel).to eq("UCRPdsCVuH53rcbTcEkuY4uQ")
+ expect(query.text).to eq("rdna3")
+ expect(query.filters.default?).to be_true
+ end
+
+ it "switches to subscriptions search" do
+ query = described_class.new(
+ HTTP::Params.parse("q=subscriptions:true+tunak+tunak+tun"),
+ Invidious::Search::Query::Type::Regular, nil
+ )
+
+ expect(query.type).to eq(Invidious::Search::Query::Type::Subscriptions)
+ expect(query.channel).to be_empty
+ expect(query.text).to eq("tunak tunak tun")
+ expect(query.filters.default?).to be_true
+ end
+ end
+
+ describe Type::Channel do
+ it "ignores extra parameters" do
+ query = described_class.new(
+ HTTP::Params.parse("q=Take+on+me+channel%3AUC12345679&type=video&date=year"),
+ Invidious::Search::Query::Type::Channel, nil
+ )
+
+ expect(query.type).to eq(Invidious::Search::Query::Type::Channel)
+ expect(query.channel).to be_empty
+ expect(query.text).to eq("Take on me")
+ expect(query.filters.default?).to be_true
+ end
+ end
+
+ describe Type::Subscriptions do
+ it "works" do
+ query = described_class.new(
+ HTTP::Params.parse("q=Harlem+shake&type=video&date=year"),
+ Invidious::Search::Query::Type::Subscriptions, nil
+ )
+
+ expect(query.type).to eq(Invidious::Search::Query::Type::Subscriptions)
+ expect(query.channel).to be_empty
+ expect(query.text).to eq("Harlem shake")
+
+ expect(query.filters).to eq(
+ Invidious::Search::Filters.new(
+ type: Invidious::Search::Filters::Type::Video,
+ date: Invidious::Search::Filters::Date::Year
+ )
+ )
+ end
+ end
+
+ describe Type::Playlist do
+ it "ignores extra parameters" do
+ query = described_class.new(
+ HTTP::Params.parse("q=Harlem+shake+type:video+date:year&channel=UC12345679"),
+ Invidious::Search::Query::Type::Playlist, nil
+ )
+
+ expect(query.type).to eq(Invidious::Search::Query::Type::Playlist)
+ expect(query.channel).to be_empty
+ expect(query.text).to eq("Harlem shake")
+
+ expect(query.filters).to eq(
+ Invidious::Search::Filters.new(
+ type: Invidious::Search::Filters::Type::Video,
+ date: Invidious::Search::Filters::Date::Year
+ )
+ )
+ end
+ end
+end
diff --git a/src/ext/kemal_content_for.cr b/src/ext/kemal_content_for.cr
new file mode 100644
index 00000000..a4f3fd96
--- /dev/null
+++ b/src/ext/kemal_content_for.cr
@@ -0,0 +1,16 @@
+# Overrides for Kemal's `content_for` macro in order to keep using
+# kilt as it was before Kemal v1.1.1 (Kemal PR #618).
+
+require "kemal"
+require "kilt"
+
+macro content_for(key, file = __FILE__)
+ %proc = ->() {
+ __kilt_io__ = IO::Memory.new
+ {{ yield }}
+ __kilt_io__.to_s
+ }
+
+ CONTENT_FOR_BLOCKS[{{key}}] = Tuple.new {{file}}, %proc
+ nil
+end
diff --git a/src/invidious/helpers/static_file_handler.cr b/src/ext/kemal_static_file_handler.cr
index 6ef2d74c..6ef2d74c 100644
--- a/src/invidious/helpers/static_file_handler.cr
+++ b/src/ext/kemal_static_file_handler.cr
diff --git a/src/invidious.cr b/src/invidious.cr
index 9f3d5d10..dd240852 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -16,7 +16,13 @@
require "digest/md5"
require "file_utils"
+
+# Require kemal, kilt, then our own overrides
require "kemal"
+require "kilt"
+require "./ext/kemal_content_for.cr"
+require "./ext/kemal_static_file_handler.cr"
+
require "athena-negotiation"
require "openssl/hmac"
require "option_parser"
diff --git a/src/invidious/channels/about.cr b/src/invidious/channels/about.cr
index 4f82a0f1..d48fd1fb 100644
--- a/src/invidious/channels/about.cr
+++ b/src/invidious/channels/about.cr
@@ -12,7 +12,8 @@ record AboutChannel,
joined : Time,
is_family_friendly : Bool,
allowed_regions : Array(String),
- tabs : Array(String)
+ tabs : Array(String),
+ verified : Bool
record AboutRelatedChannel,
ucid : String,
@@ -70,6 +71,9 @@ def get_about_info(ucid, locale) : AboutChannel
# if banner.includes? "channels/c4/default_banner"
# banner = nil
# end
+ # author_verified_badges = initdata["header"]?.try &.["c4TabbedHeaderRenderer"]?.try &.["badges"]?
+ author_verified_badge = initdata["header"].dig?("c4TabbedHeaderRenderer", "badges", 0, "metadataBadgeRenderer", "tooltip")
+ author_verified = (author_verified_badge && author_verified_badge == "Verified")
description = initdata["metadata"]["channelMetadataRenderer"]?.try &.["description"]?.try &.as_s? || ""
description_html = HTML.escape(description)
@@ -128,6 +132,7 @@ def get_about_info(ucid, locale) : AboutChannel
is_family_friendly: is_family_friendly,
allowed_regions: allowed_regions,
tabs: tabs,
+ verified: author_verified || false,
)
end
diff --git a/src/invidious/comments.cr b/src/invidious/comments.cr
index ab9fcc8b..1f8de657 100644
--- a/src/invidious/comments.cr
+++ b/src/invidious/comments.cr
@@ -143,9 +143,11 @@ def fetch_youtube_comments(id, cursor, format, locale, thin_mode, region, sort_b
node_comment = node["commentRenderer"]
end
- content_html = node_comment["contentText"]?.try { |t| parse_content(t) } || ""
+ content_html = node_comment["contentText"]?.try { |t| parse_content(t, id) } || ""
author = node_comment["authorText"]?.try &.["simpleText"]? || ""
+ json.field "verified", (node_comment["authorCommentBadge"]? != nil)
+
json.field "author", author
json.field "authorThumbnails" do
json.array do
@@ -329,7 +331,11 @@ def template_youtube_comments(comments, locale, thin_mode, is_replies = false)
end
author_name = HTML.escape(child["author"].as_s)
-
+ if child["verified"]?.try &.as_bool && child["authorIsChannelOwner"]?.try &.as_bool
+ author_name += "&nbsp;<i class=\"icon ion ion-md-checkmark-circle\"></i>"
+ elsif child["verified"]?.try &.as_bool
+ author_name += "&nbsp;<i class=\"icon ion ion-md-checkmark\"></i>"
+ end
html << <<-END_HTML
<div class="pure-g" style="width:100%">
<div class="channel-profile pure-u-4-24 pure-u-md-2-24">
@@ -554,26 +560,19 @@ def fill_links(html, scheme, host)
return html.to_xml(options: XML::SaveOptions::NO_DECL)
end
-def parse_content(content : JSON::Any) : String
+def parse_content(content : JSON::Any, video_id : String? = "") : String
content["simpleText"]?.try &.as_s.rchop('\ufeff').try { |b| HTML.escape(b) }.to_s ||
- content["runs"]?.try &.as_a.try { |r| content_to_comment_html(r).try &.to_s.gsub("\n", "<br>") } || ""
+ content["runs"]?.try &.as_a.try { |r| content_to_comment_html(r, video_id).try &.to_s.gsub("\n", "<br>") } || ""
end
-def content_to_comment_html(content)
- comment_html = content.map do |run|
+def content_to_comment_html(content, video_id : String? = "")
+ html_array = content.map do |run|
text = HTML.escape(run["text"].as_s)
- if run["bold"]?
- text = "<b>#{text}</b>"
- end
-
- if run["italics"]?
- text = "<i>#{text}</i>"
- end
-
if run["navigationEndpoint"]?
if url = run["navigationEndpoint"]["urlEndpoint"]?.try &.["url"].as_s
url = URI.parse(url)
+ displayed_url = text
if url.host == "youtu.be"
url = "/watch?v=#{url.request_target.lstrip('/')}"
@@ -581,31 +580,53 @@ def content_to_comment_html(content)
if url.path == "/redirect"
# Sometimes, links can be corrupted (why?) so make sure to fallback
# nicely. See https://github.com/iv-org/invidious/issues/2682
- url = HTTP::Params.parse(url.query.not_nil!)["q"]? || ""
+ url = url.query_params["q"]? || ""
+ displayed_url = url
else
url = url.request_target
+ displayed_url = "youtube.com#{url}"
end
end
- text = %(<a href="#{url}">#{text}</a>)
+ text = %(<a href="#{url}">#{reduce_uri(displayed_url)}</a>)
elsif watch_endpoint = run["navigationEndpoint"]["watchEndpoint"]?
- length_seconds = watch_endpoint["startTimeSeconds"]?
- video_id = watch_endpoint["videoId"].as_s
-
- if length_seconds && length_seconds.as_i > 0
- text = %(<a href="javascript:void(0)" data-onclick="jump_to_time" data-jump-time="#{length_seconds}">#{text}</a>)
+ start_time = watch_endpoint["startTimeSeconds"]?.try &.as_i
+ link_video_id = watch_endpoint["videoId"].as_s
+
+ url = "/watch?v=#{link_video_id}"
+ url += "&t=#{start_time}" if !start_time.nil?
+
+ # If the current video ID (passed through from the caller function)
+ # is the same as the video ID in the link, add HTML attributes for
+ # the JS handler function that bypasses page reload.
+ #
+ # See: https://github.com/iv-org/invidious/issues/3063
+ if link_video_id == video_id
+ start_time ||= 0
+ text = %(<a href="#{url}" data-onclick="jump_to_time" data-jump-time="#{start_time}">#{reduce_uri(text)}</a>)
else
- text = %(<a href="/watch?v=#{video_id}">#{text}</a>)
+ text = %(<a href="#{url}">#{text}</a>)
end
elsif url = run.dig?("navigationEndpoint", "commandMetadata", "webCommandMetadata", "url").try &.as_s
- text = %(<a href="#{url}">#{text}</a>)
+ if text.starts_with?(/\s?[@#]/)
+ # Handle "pings" in comments and hasthags differently
+ # See:
+ # - https://github.com/iv-org/invidious/issues/3038
+ # - https://github.com/iv-org/invidious/issues/3062
+ text = %(<a href="#{url}">#{text}</a>)
+ else
+ text = %(<a href="#{url}">#{reduce_uri(url)}</a>)
+ end
end
end
+ text = "<b>#{text}</b>" if run["bold"]?
+ text = "<i>#{text}</i>" if run["italics"]?
+
text
- end.join("").delete('\ufeff')
+ end
- return comment_html
+ return html_array.join("").delete('\ufeff')
end
def produce_comment_continuation(video_id, cursor = "", sort_by = "top")
diff --git a/src/invidious/frontend/search_filters.cr b/src/invidious/frontend/search_filters.cr
index 68f27b4f..8ac0af2e 100644
--- a/src/invidious/frontend/search_filters.cr
+++ b/src/invidious/frontend/search_filters.cr
@@ -106,7 +106,7 @@ module Invidious::Frontend::SearchFilters
{% feature = value.underscore %}
str << "\t\t\t\t\t\t<div>"
- str << "<input type='checkbox' name='features' id='filter-features-{{feature}}' value='{{feature}}'"
+ str << "<input type='checkbox' name='features' id='filter-feature-{{feature}}' value='{{feature}}'"
str << " checked" if value.{{feature}}?
str << '>'
diff --git a/src/invidious/helpers/errors.cr b/src/invidious/helpers/errors.cr
index 2eab6263..b80dcdaf 100644
--- a/src/invidious/helpers/errors.cr
+++ b/src/invidious/helpers/errors.cr
@@ -46,7 +46,7 @@ def error_template_helper(env : HTTP::Server::Context, status_code : Int32, exce
TEXT
- issue_template += github_details("Backtrace", HTML.escape(exception.inspect_with_backtrace))
+ issue_template += github_details("Backtrace", exception.inspect_with_backtrace)
# URLs for the error message below
url_faq = "https://github.com/iv-org/documentation/blob/master/docs/faq.md"
diff --git a/src/invidious/helpers/i18n.cr b/src/invidious/helpers/i18n.cr
index 982b97d8..9d3c4e8b 100644
--- a/src/invidious/helpers/i18n.cr
+++ b/src/invidious/helpers/i18n.cr
@@ -14,6 +14,7 @@ LOCALES_LIST = {
"fi" => "Suomi", # Finnish
"fr" => "Français", # French
"he" => "עברית", # Hebrew
+ "hi" => "हिन्दी", # Hindi
"hr" => "Hrvatski", # Croatian
"hu-HU" => "Magyar Nyelv", # Hungarian
"id" => "Bahasa Indonesia", # Indonesian
@@ -30,6 +31,7 @@ LOCALES_LIST = {
"pt-PT" => "Português de Portugal", # Portuguese (Portugal)
"ro" => "Română", # Romanian
"ru" => "Русский", # Russian
+ "sl" => "Slovenščina", # Slovenian
"sq" => "Shqip", # Albanian
"sr" => "Srpski (latinica)", # Serbian (Latin)
"sr_Cyrl" => "Српски (ћирилица)", # Serbian (Cyrillic)
diff --git a/src/invidious/helpers/macros.cr b/src/invidious/helpers/macros.cr
index 75df1612..43e7171b 100644
--- a/src/invidious/helpers/macros.cr
+++ b/src/invidious/helpers/macros.cr
@@ -48,13 +48,19 @@ module JSON::Serializable
end
end
-macro templated(filename, template = "template", navbar_search = true)
+macro templated(_filename, template = "template", navbar_search = true)
navbar_search = {{navbar_search}}
- render "src/invidious/views/#{{{filename}}}.ecr", "src/invidious/views/#{{{template}}}.ecr"
+
+ {{ filename = "src/invidious/views/" + _filename + ".ecr" }}
+ {{ layout = "src/invidious/views/" + template + ".ecr" }}
+
+ __content_filename__ = {{filename}}
+ content = Kilt.render({{filename}})
+ Kilt.render({{layout}})
end
macro rendered(filename)
- render "src/invidious/views/#{{{filename}}}.ecr"
+ Kilt.render("src/invidious/views/#{{{filename}}}.ecr")
end
# Similar to Kemals halt method but works in a
diff --git a/src/invidious/helpers/serialized_yt_data.cr b/src/invidious/helpers/serialized_yt_data.cr
index bfbc237c..3918bd13 100644
--- a/src/invidious/helpers/serialized_yt_data.cr
+++ b/src/invidious/helpers/serialized_yt_data.cr
@@ -12,6 +12,7 @@ struct SearchVideo
property live_now : Bool
property premium : Bool
property premiere_timestamp : Time?
+ property author_verified : Bool
def to_xml(auto_generated, query_params, xml : XML::Builder)
query_params["v"] = self.id
@@ -129,6 +130,7 @@ struct SearchPlaylist
property video_count : Int32
property videos : Array(SearchPlaylistVideo)
property thumbnail : String?
+ property author_verified : Bool
def to_json(locale : String?, json : JSON::Builder)
json.object do
@@ -141,6 +143,8 @@ struct SearchPlaylist
json.field "authorId", self.ucid
json.field "authorUrl", "/channel/#{self.ucid}"
+ json.field "authorVerified", self.author_verified
+
json.field "videoCount", self.video_count
json.field "videos" do
json.array do
@@ -182,6 +186,7 @@ struct SearchChannel
property video_count : Int32
property description_html : String
property auto_generated : Bool
+ property author_verified : Bool
def to_json(locale : String?, json : JSON::Builder)
json.object do
@@ -189,7 +194,7 @@ struct SearchChannel
json.field "author", self.author
json.field "authorId", self.ucid
json.field "authorUrl", "/channel/#{self.ucid}"
-
+ json.field "authorVerified", self.author_verified
json.field "authorThumbnails" do
json.array do
qualities = {32, 48, 76, 100, 176, 512}
diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr
index c1dc17db..8ae5034a 100644
--- a/src/invidious/helpers/utils.cr
+++ b/src/invidious/helpers/utils.cr
@@ -383,3 +383,11 @@ def fetch_random_instance
return filtered_instance_list.sample(1)[0]
end
+
+def reduce_uri(uri : URI | String, max_length : Int32 = 50, suffix : String = "…") : String
+ str = uri.to_s.sub(/^https?:\/\//, "")
+ if str.size > max_length
+ str = "#{str[0, max_length]}#{suffix}"
+ end
+ return str
+end
diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr
index ca429df5..8bc36946 100644
--- a/src/invidious/routes/api/manifest.cr
+++ b/src/invidious/routes/api/manifest.cr
@@ -56,12 +56,15 @@ module Invidious::Routes::API::Manifest
xml.element("Period") do
i = 0
- {"audio/mp4", "audio/webm"}.each do |mime_type|
+ {"audio/mp4"}.each do |mime_type|
mime_streams = audio_streams.select { |stream| stream["mimeType"].as_s.starts_with? mime_type }
next if mime_streams.empty?
xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true) do
mime_streams.each do |fmt|
+ # OTF streams aren't supported yet (See https://github.com/TeamNewPipe/NewPipe/issues/2415)
+ next if !(fmt.has_key?("indexRange") && fmt.has_key?("initRange"))
+
codecs = fmt["mimeType"].as_s.split("codecs=")[1].strip('"')
bandwidth = fmt["bitrate"].as_i
itag = fmt["itag"].as_i
@@ -83,13 +86,16 @@ module Invidious::Routes::API::Manifest
potential_heights = {4320, 2160, 1440, 1080, 720, 480, 360, 240, 144}
- {"video/mp4", "video/webm"}.each do |mime_type|
+ {"video/mp4"}.each do |mime_type|
mime_streams = video_streams.select { |stream| stream["mimeType"].as_s.starts_with? mime_type }
next if mime_streams.empty?
heights = [] of Int32
xml.element("AdaptationSet", id: i, mimeType: mime_type, startWithSAP: 1, subsegmentAlignment: true, scanType: "progressive") do
mime_streams.each do |fmt|
+ # OTF streams aren't supported yet (See https://github.com/TeamNewPipe/NewPipe/issues/2415)
+ next if !(fmt.has_key?("indexRange") && fmt.has_key?("initRange"))
+
codecs = fmt["mimeType"].as_s.split("codecs=")[1].strip('"')
bandwidth = fmt["bitrate"].as_i
itag = fmt["itag"].as_i
diff --git a/src/invidious/routes/feeds.cr b/src/invidious/routes/feeds.cr
index f7f7b426..b5b58399 100644
--- a/src/invidious/routes/feeds.cr
+++ b/src/invidious/routes/feeds.cr
@@ -182,6 +182,7 @@ module Invidious::Routes::Feeds
paid: false,
premium: false,
premiere_timestamp: nil,
+ author_verified: false, # ¯\_(ツ)_/¯
})
end
diff --git a/src/invidious/routes/watch.cr b/src/invidious/routes/watch.cr
index 867ffa6a..75475430 100644
--- a/src/invidious/routes/watch.cr
+++ b/src/invidious/routes/watch.cr
@@ -308,25 +308,26 @@ module Invidious::Routes::Watch
extension = download_widget["ext"].as_s
filename = "#{video_id}-#{title}.#{extension}"
- # Pass form parameters as URL parameters for the handlers of both
- # /latest_version and /api/v1/captions. This avoids an un-necessary
- # redirect and duplicated (and hazardous) sanity checks.
- env.params.query["id"] = video_id
- env.params.query["title"] = filename
-
- # Delete the useless ones
+ # Delete the now useless URL parameters
env.params.body.delete("id")
env.params.body.delete("title")
env.params.body.delete("download_widget")
+ # Pass form parameters as URL parameters for the handlers of both
+ # /latest_version and /api/v1/captions. This avoids an un-necessary
+ # redirect and duplicated (and hazardous) sanity checks.
if label = download_widget["label"]?
# URL params specific to /api/v1/captions/:id
- env.params.query["label"] = URI.encode_www_form(label.as_s, space_to_plus: false)
+ env.params.url["id"] = video_id
+ env.params.query["title"] = filename
+ env.params.query["label"] = URI.decode_www_form(label.as_s)
return Invidious::Routes::API::V1::Videos.captions(env)
elsif itag = download_widget["itag"]?.try &.as_i
# URL params specific to /latest_version
+ env.params.query["id"] = video_id
env.params.query["itag"] = itag.to_s
+ env.params.query["title"] = filename
env.params.query["local"] = "true"
return Invidious::Routes::VideoPlayback.latest_version(env)
diff --git a/src/invidious/search/query.cr b/src/invidious/search/query.cr
index 1c2b37d2..34b36b1d 100644
--- a/src/invidious/search/query.cr
+++ b/src/invidious/search/query.cr
@@ -10,7 +10,7 @@ module Invidious::Search
Playlist # "Add playlist item" search
end
- @type : Type = Type::Regular
+ getter type : Type = Type::Regular
@raw_query : String
@query : String = ""
@@ -63,14 +63,17 @@ module Invidious::Search
# Specific handling
case @type
- when .playlist?, .channel?
- # In "add playlist item" mode, filters are parsed from the query
- # string itself (legacy), and the channel is ignored.
- #
+ when .channel?
# In "channel search" mode, filters are ignored, but we still parse
# the query prevent transmission of legacy filters to youtube.
#
- @filters, @query, @channel, _ = Filters.from_legacy_filters(@raw_query || "")
+ _, _, @query, _ = Filters.from_legacy_filters(@raw_query)
+ #
+ when .playlist?
+ # In "add playlist item" mode, filters are parsed from the query
+ # string itself (legacy), and the channel is ignored.
+ #
+ @filters, _, @query, _ = Filters.from_legacy_filters(@raw_query)
#
when .subscriptions?, .regular?
if params["sp"]?
@@ -84,7 +87,7 @@ module Invidious::Search
if @filters.default? && @raw_query.includes?(':')
# Parse legacy filters from query
- @filters, @query, @channel, subs = Filters.from_legacy_filters(@raw_query || "")
+ @filters, @channel, @query, subs = Filters.from_legacy_filters(@raw_query)
else
@query = @raw_query || ""
end
diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr
index 31ae90c7..f65b05bb 100644
--- a/src/invidious/videos.cr
+++ b/src/invidious/videos.cr
@@ -374,18 +374,25 @@ struct Video
json.array do
self.adaptive_fmts.each do |fmt|
json.object do
- json.field "index", "#{fmt["indexRange"]["start"]}-#{fmt["indexRange"]["end"]}"
- json.field "bitrate", fmt["bitrate"].as_i.to_s
- json.field "init", "#{fmt["initRange"]["start"]}-#{fmt["initRange"]["end"]}"
+ # Only available on regular videos, not livestreams/OTF streams
+ if init_range = fmt["initRange"]?
+ json.field "init", "#{init_range["start"]}-#{init_range["end"]}"
+ end
+ if index_range = fmt["indexRange"]?
+ json.field "index", "#{index_range["start"]}-#{index_range["end"]}"
+ end
+
+ # Not available on MPEG-4 Timed Text (`text/mp4`) streams (livestreams only)
+ json.field "bitrate", fmt["bitrate"].as_i.to_s if fmt["bitrate"]?
+
json.field "url", fmt["url"]
json.field "itag", fmt["itag"].as_i.to_s
json.field "type", fmt["mimeType"]
- json.field "clen", fmt["contentLength"]
+ json.field "clen", fmt["contentLength"]? || "-1"
json.field "lmt", fmt["lastModified"]
json.field "projectionType", fmt["projectionType"]
- fmt_info = itag_to_metadata?(fmt["itag"])
- if fmt_info
+ if fmt_info = itag_to_metadata?(fmt["itag"])
fps = fmt_info["fps"]?.try &.to_i || fmt["fps"]?.try &.as_i || 30
json.field "fps", fps
json.field "container", fmt_info["ext"]
@@ -405,6 +412,19 @@ struct Video
end
end
end
+
+ # Livestream chunk infos
+ json.field "targetDurationSec", fmt["targetDurationSec"].as_i if fmt.has_key?("targetDurationSec")
+ json.field "maxDvrDurationSec", fmt["maxDvrDurationSec"].as_i if fmt.has_key?("maxDvrDurationSec")
+
+ # Audio-related data
+ json.field "audioQuality", fmt["audioQuality"] if fmt.has_key?("audioQuality")
+ json.field "audioSampleRate", fmt["audioSampleRate"].as_s.to_i if fmt.has_key?("audioSampleRate")
+ json.field "audioChannels", fmt["audioChannels"] if fmt.has_key?("audioChannels")
+
+ # Extra misc stuff
+ json.field "colorInfo", fmt["colorInfo"] if fmt.has_key?("colorInfo")
+ json.field "captionTrack", fmt["captionTrack"] if fmt.has_key?("captionTrack")
end
end
end
@@ -593,6 +613,10 @@ struct Video
info["authorThumbnail"]?.try &.as_s || ""
end
+ def author_verified : Bool
+ info["authorVerified"]?.try &.as_bool || false
+ end
+
def sub_count_text : String
info["subCountText"]?.try &.as_s || "-"
end
@@ -612,6 +636,7 @@ struct Video
fmt["url"] = JSON::Any.new("#{fmt["url"]}&host=#{URI.parse(fmt["url"].as_s).host}")
fmt["url"] = JSON::Any.new("#{fmt["url"]}&region=#{self.info["region"]}") if self.info["region"]?
end
+
fmt_stream.sort_by! { |f| f["width"]?.try &.as_i || 0 }
@fmt_stream = fmt_stream
return @fmt_stream.as(Array(Hash(String, JSON::Any)))
@@ -631,9 +656,7 @@ struct Video
fmt["url"] = JSON::Any.new("#{fmt["url"]}&host=#{URI.parse(fmt["url"].as_s).host}")
fmt["url"] = JSON::Any.new("#{fmt["url"]}&region=#{self.info["region"]}") if self.info["region"]?
end
- # See https://github.com/TeamNewPipe/NewPipe/issues/2415
- # Some streams are segmented by URL `sq/` rather than index, for now we just filter them out
- fmt_stream.reject! { |f| !f["indexRange"]? }
+
fmt_stream.sort_by! { |f| f["width"]?.try &.as_i || 0 }
@adaptive_fmts = fmt_stream
return @adaptive_fmts.as(Array(Hash(String, JSON::Any)))
@@ -845,6 +868,12 @@ def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)?
.try &.dig?("runs", 0)
author = channel_info.try &.dig?("text")
+ author_verified_badge = related["ownerBadges"]?.try do |badges_array|
+ badges_array.as_a.find(&.dig("metadataBadgeRenderer", "tooltip").as_s.== "Verified")
+ end
+
+ author_verified = (author_verified_badge && author_verified_badge.size > 0).to_s
+
ucid = channel_info.try { |ci| HelperExtractors.get_browse_id(ci) }
# "4,088,033 views", only available on compact renderer
@@ -868,6 +897,7 @@ def parse_related_video(related : JSON::Any) : Hash(String, JSON::Any)?
"length_seconds" => JSON::Any.new(length || "0"),
"view_count" => JSON::Any.new(view_count || "0"),
"short_view_count" => JSON::Any.new(short_view_count || "0"),
+ "author_verified" => JSON::Any.new(author_verified),
}
end
@@ -1024,7 +1054,7 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_
# Description
description_html = video_secondary_renderer.try &.dig?("description", "runs")
- .try &.as_a.try { |t| content_to_comment_html(t) }
+ .try &.as_a.try { |t| content_to_comment_html(t, video_id) }
params["descriptionHtml"] = JSON::Any.new(description_html || "<p></p>")
@@ -1062,6 +1092,10 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_
author_info = video_secondary_renderer.try &.dig?("owner", "videoOwnerRenderer")
author_thumbnail = author_info.try &.dig?("thumbnail", "thumbnails", 0, "url")
+ author_verified_badge = author_info.try &.dig?("badges", 0, "metadataBadgeRenderer", "tooltip")
+ author_verified = (!author_verified_badge.nil? && author_verified_badge == "Verified")
+ params["authorVerified"] = JSON::Any.new(author_verified)
+
params["authorThumbnail"] = JSON::Any.new(author_thumbnail.try &.as_s || "")
params["subCountText"] = JSON::Any.new(author_info.try &.["subscriberCountText"]?
diff --git a/src/invidious/views/channel.ecr b/src/invidious/views/channel.ecr
index 40b553a9..92f81ee4 100644
--- a/src/invidious/views/channel.ecr
+++ b/src/invidious/views/channel.ecr
@@ -20,7 +20,7 @@
<div class="pure-u-2-3">
<div class="channel-profile">
<img src="/ggpht<%= URI.parse(channel.author_thumbnail).request_target %>">
- <span><%= author %></span>
+ <span><%= author %></span><% if !channel.verified.nil? && channel.verified %>&nbsp;<i class="icon ion ion-md-checkmark-circle"></i><% end %>
</div>
</div>
<div class="pure-u-1-3">
diff --git a/src/invidious/views/community.ecr b/src/invidious/views/community.ecr
index f0add06b..3bc29e55 100644
--- a/src/invidious/views/community.ecr
+++ b/src/invidious/views/community.ecr
@@ -19,7 +19,7 @@
<div class="pure-u-2-3">
<div class="channel-profile">
<img src="/ggpht<%= URI.parse(channel.author_thumbnail).request_target %>">
- <span><%= author %></span>
+ <span><%= author %></span><% if !channel.verified.nil? && channel.verified %>&nbsp;<i class="icon ion ion-md-checkmark-circle"></i><% end %>
</div>
</div>
<div class="pure-u-1-3" style="text-align:right">
diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr
index 5f8bde13..fb7ad1dc 100644
--- a/src/invidious/views/components/item.ecr
+++ b/src/invidious/views/components/item.ecr
@@ -8,7 +8,7 @@
<img loading="lazy" style="width:56.25%" src="/ggpht<%= URI.parse(item.author_thumbnail).request_target.gsub(/=s\d+/, "=s176") %>"/>
</center>
<% end %>
- <p dir="auto"><%= HTML.escape(item.author) %></p>
+ <p dir="auto"><%= HTML.escape(item.author) %><% if !item.author_verified.nil? && item.author_verified %>&nbsp;<i class="icon ion ion-md-checkmark-circle"></i><% end %></p>
</a>
<p><%= translate_count(locale, "generic_subscribers_count", item.subscriber_count, NumberFormatting::Separator) %></p>
<% if !item.auto_generated %><p><%= translate_count(locale, "generic_videos_count", item.video_count, NumberFormatting::Separator) %></p><% end %>
@@ -30,7 +30,7 @@
<p dir="auto"><%= HTML.escape(item.title) %></p>
</a>
<a href="/channel/<%= item.ucid %>">
- <p dir="auto"><b><%= HTML.escape(item.author) %></b></p>
+ <p dir="auto"><b><%= HTML.escape(item.author) %><% if !item.is_a?(InvidiousPlaylist) && !item.author_verified.nil? && item.author_verified %>&nbsp;<i class="icon ion ion-md-checkmark-circle"></i><% end %></b></p>
</a>
<% when MixVideo %>
<a href="/watch?v=<%= item.id %>&list=<%= item.rdid %>">
@@ -52,11 +52,11 @@
<% if !env.get("preferences").as(Preferences).thin_mode %>
<div class="thumbnail">
<img loading="lazy" class="thumbnail" src="/vi/<%= item.id %>/mqdefault.jpg"/>
- <% if plid = env.get?("remove_playlist_items") %>
- <form data-onsubmit="return_false" action="/playlist_ajax?action_remove_video=1&set_video_id=<%= item.index %>&playlist_id=<%= plid %>&referer=<%= env.get("current_page") %>" method="post">
+ <% if plid_form = env.get?("remove_playlist_items") %>
+ <form data-onsubmit="return_false" action="/playlist_ajax?action_remove_video=1&set_video_id=<%= item.index %>&playlist_id=<%= plid_form %>&referer=<%= env.get("current_page") %>" method="post">
<input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>">
<p class="watched">
- <a data-onclick="remove_playlist_item" data-index="<%= item.index %>" data-plid="<%= plid %>" href="javascript:void(0)">
+ <a data-onclick="remove_playlist_item" data-index="<%= item.index %>" data-plid="<%= plid_form %>" href="javascript:void(0)">
<button type="submit" style="all:unset">
<i class="icon ion-md-trash"></i>
</button>
@@ -117,11 +117,11 @@
</a>
</p>
</form>
- <% elsif plid = env.get? "add_playlist_items" %>
- <form data-onsubmit="return_false" action="/playlist_ajax?action_add_video=1&video_id=<%= item.id %>&playlist_id=<%= plid %>&referer=<%= env.get("current_page") %>" method="post">
+ <% elsif plid_form = env.get? "add_playlist_items" %>
+ <form data-onsubmit="return_false" action="/playlist_ajax?action_add_video=1&video_id=<%= item.id %>&playlist_id=<%= plid_form %>&referer=<%= env.get("current_page") %>" method="post">
<input type="hidden" name="csrf_token" value="<%= HTML.escape(env.get?("csrf_token").try &.as(String) || "") %>">
<p class="watched">
- <a data-onclick="add_playlist_item" data-id="<%= item.id %>" data-plid="<%= plid %>" href="javascript:void(0)">
+ <a data-onclick="add_playlist_item" data-id="<%= item.id %>" data-plid="<%= plid_form %>" href="javascript:void(0)">
<button type="submit" style="all:unset">
<i class="icon ion-md-add"></i>
</button>
@@ -142,7 +142,7 @@
<div class="video-card-row flexible">
<div class="flex-left"><a href="/channel/<%= item.ucid %>">
- <p class="channel-name" dir="auto"><%= HTML.escape(item.author) %></p>
+ <p class="channel-name" dir="auto"><%= HTML.escape(item.author) %><% if !item.is_a?(ChannelVideo) && !item.author_verified.nil? && item.author_verified %>&nbsp;<i class="icon ion ion-md-checkmark-circle"></i><% end %></p>
</a></div>
<% endpoint_params = "?v=#{item.id}" %>
diff --git a/src/invidious/views/components/player.ecr b/src/invidious/views/components/player.ecr
index 206ba380..fffefc9a 100644
--- a/src/invidious/views/components/player.ecr
+++ b/src/invidious/views/components/player.ecr
@@ -7,8 +7,19 @@
<source src="<%= URI.parse(hlsvp).request_target %><% if params.local %>?local=true<% end %>" type="application/x-mpegURL" label="livestream">
<% else %>
<% if params.listen %>
- <% audio_streams.each_with_index do |fmt, i| %>
- <source src="/latest_version?id=<%= video.id %>&itag=<%= fmt["itag"] %><% if params.local %>&local=true<% end %>" type='<%= fmt["mimeType"] %>' label="<%= fmt["bitrate"] %>k" selected="<%= i == 0 ? true : false %>">
+ <% audio_streams.each_with_index do |fmt, i|
+ src_url = "/latest_version?id=#{video.id}&itag=#{fmt["itag"]}"
+ src_url += "&local=true" if params.local
+
+ bitrate = fmt["bitrate"]
+ mimetype = HTML.escape(fmt["mimeType"].as_s)
+
+ selected = i == 0 ? true : false
+ %>
+ <source src="<%= src_url %>" type='<%= mimetype %>' label="<%= bitrate %>k" selected="<%= selected %>">
+ <% if !params.local && !CONFIG.disabled?("local") %>
+ <source src="<%= src_url %>&local=true" type='<%= mimetype %>' hidequalityoption="true">
+ <% end %>
<% end %>
<% else %>
<% if params.quality == "dash" %>
@@ -28,6 +39,9 @@
selected = params.quality ? (params.quality == quality) : (i == 0)
%>
<source src="<%= src_url %>" type="<%= mimetype %>" label="<%= quality %>" selected="<%= selected %>">
+ <% if !params.local && !CONFIG.disabled?("local") %>
+ <source src="<%= src_url %>&local=true" type="<%= mimetype %>" hidequalityoption="true">
+ <% end %>
<% end %>
<% end %>
diff --git a/src/invidious/views/embed.ecr b/src/invidious/views/embed.ecr
index 27a8e266..ce5ff7f0 100644
--- a/src/invidious/views/embed.ecr
+++ b/src/invidious/views/embed.ecr
@@ -24,7 +24,8 @@
"video_series" => video_series,
"params" => params,
"preferences" => preferences,
- "premiere_timestamp" => video.premiere_timestamp.try &.to_unix
+ "premiere_timestamp" => video.premiere_timestamp.try &.to_unix,
+ "local_disabled" => CONFIG.disabled?("local")
}.to_pretty_json
%>
</script>
diff --git a/src/invidious/views/playlists.ecr b/src/invidious/views/playlists.ecr
index 12dba088..c8718e7b 100644
--- a/src/invidious/views/playlists.ecr
+++ b/src/invidious/views/playlists.ecr
@@ -19,7 +19,7 @@
<div class="pure-u-2-3">
<div class="channel-profile">
<img src="/ggpht<%= URI.parse(channel.author_thumbnail).request_target %>">
- <span><%= author %></span>
+ <span><%= author %></span><% if !channel.verified.nil? && channel.verified %>&nbsp;<i class="icon ion ion-md-checkmark-circle"></i><% end %>
</div>
</div>
<div class="pure-u-1-3" style="text-align:right">
diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr
index 0e4af3ab..8b6eb903 100644
--- a/src/invidious/views/watch.ecr
+++ b/src/invidious/views/watch.ecr
@@ -64,7 +64,8 @@ we're going to need to do it here in order to allow for translations.
"preferences" => preferences,
"premiere_timestamp" => video.premiere_timestamp.try &.to_unix,
"vr" => video.is_vr,
- "projection_type" => video.projection_type
+ "projection_type" => video.projection_type,
+ "local_disabled" => CONFIG.disabled?("local")
}.to_pretty_json
%>
</script>
@@ -206,7 +207,7 @@ we're going to need to do it here in order to allow for translations.
<% if !video.author_thumbnail.empty? %>
<img src="/ggpht<%= URI.parse(video.author_thumbnail).request_target %>">
<% end %>
- <span id="channel-name"><%= author %></span>
+ <span id="channel-name"><%= author %><% if !video.author_verified.nil? && video.author_verified %>&nbsp;<i class="icon ion ion-md-checkmark-circle"></i><% end %></span>
</div>
</a>
@@ -280,9 +281,9 @@ we're going to need to do it here in order to allow for translations.
<h5 class="pure-g">
<div class="pure-u-14-24">
<% if rv["ucid"]? %>
- <b style="width:100%"><a href="/channel/<%= rv["ucid"] %>"><%= rv["author"]? %></a></b>
+ <b style="width:100%"><a href="/channel/<%= rv["ucid"] %>"><%= rv["author"]? %><% if rv["author_verified"]? == "true" %>&nbsp;<i class="icon ion ion-md-checkmark-circle"></i><% end %></a></b>
<% else %>
- <b style="width:100%"><%= rv["author"]? %></b>
+ <b style="width:100%"><%= rv["author"]? %><% if rv["author_verified"]? == "true" %>&nbsp;<i class="icon ion ion-md-checkmark-circle"></i><% end %></b>
<% end %>
</div>
diff --git a/src/invidious/yt_backend/extractors.cr b/src/invidious/yt_backend/extractors.cr
index ce39bc28..a2ec7d59 100644
--- a/src/invidious/yt_backend/extractors.cr
+++ b/src/invidious/yt_backend/extractors.cr
@@ -69,7 +69,7 @@ private module Parsers
# TODO change default value to nil and typical encoding type to tuple storing type (watchers, views, etc)
# and count
view_count = item_contents.dig?("viewCountText", "simpleText").try &.as_s.gsub(/\D+/, "").to_i64? || 0_i64
- description_html = item_contents["descriptionSnippet"]?.try { |t| parse_content(t) } || ""
+ description_html = item_contents["descriptionSnippet"]?.try { |t| parse_content(t, video_id) } || ""
# The length information generally exist in "lengthText". However, the info can sometimes
# be retrieved from "thumbnailOverlays" (e.g when the video is a "shorts" one).
@@ -102,7 +102,11 @@ private module Parsers
premium = false
premiere_timestamp = item_contents.dig?("upcomingEventData", "startTime").try { |t| Time.unix(t.as_s.to_i64) }
+ author_verified_badge = item_contents["ownerBadges"]?.try do |badges_array|
+ badges_array.as_a.find(&.dig("metadataBadgeRenderer", "tooltip").as_s.== "Verified")
+ end
+ author_verified = (author_verified_badge && author_verified_badge.size > 0)
item_contents["badges"]?.try &.as_a.each do |badge|
b = badge["metadataBadgeRenderer"]
case b["label"].as_s
@@ -129,6 +133,7 @@ private module Parsers
live_now: live_now,
premium: premium,
premiere_timestamp: premiere_timestamp,
+ author_verified: author_verified || false,
})
end
@@ -156,7 +161,11 @@ private module Parsers
private def self.parse(item_contents, author_fallback)
author = extract_text(item_contents["title"]) || author_fallback.name
author_id = item_contents["channelId"]?.try &.as_s || author_fallback.id
+ author_verified_badge = item_contents["ownerBadges"]?.try do |badges_array|
+ badges_array.as_a.find(&.dig("metadataBadgeRenderer", "tooltip").as_s.== "Verified")
+ end
+ author_verified = (author_verified_badge && author_verified_badge.size > 0)
author_thumbnail = HelperExtractors.get_thumbnails(item_contents)
# When public subscriber count is disabled, the subscriberCountText isn't sent by InnerTube.
# Always simpleText
@@ -179,6 +188,7 @@ private module Parsers
video_count: video_count,
description_html: description_html,
auto_generated: auto_generated,
+ author_verified: author_verified || false,
})
end
@@ -206,18 +216,23 @@ private module Parsers
private def self.parse(item_contents, author_fallback)
title = extract_text(item_contents["title"]) || ""
plid = item_contents["playlistId"]?.try &.as_s || ""
+ author_verified_badge = item_contents["ownerBadges"]?.try do |badges_array|
+ badges_array.as_a.find(&.dig("metadataBadgeRenderer", "tooltip").as_s.== "Verified")
+ end
+ author_verified = (author_verified_badge && author_verified_badge.size > 0)
video_count = HelperExtractors.get_video_count(item_contents)
playlist_thumbnail = HelperExtractors.get_thumbnails(item_contents)
SearchPlaylist.new({
- title: title,
- id: plid,
- author: author_fallback.name,
- ucid: author_fallback.id,
- video_count: video_count,
- videos: [] of SearchPlaylistVideo,
- thumbnail: playlist_thumbnail,
+ title: title,
+ id: plid,
+ author: author_fallback.name,
+ ucid: author_fallback.id,
+ video_count: video_count,
+ videos: [] of SearchPlaylistVideo,
+ thumbnail: playlist_thumbnail,
+ author_verified: author_verified || false,
})
end
@@ -251,7 +266,11 @@ private module Parsers
author_info = item_contents.dig?("shortBylineText", "runs", 0)
author = author_info.try &.["text"].as_s || author_fallback.name
author_id = author_info.try { |x| HelperExtractors.get_browse_id(x) } || author_fallback.id
+ author_verified_badge = item_contents["ownerBadges"]?.try do |badges_array|
+ badges_array.as_a.find(&.dig("metadataBadgeRenderer", "tooltip").as_s.== "Verified")
+ end
+ author_verified = (author_verified_badge && author_verified_badge.size > 0)
videos = item_contents["videos"]?.try &.as_a.map do |v|
v = v["childVideoRenderer"]
v_title = v.dig?("title", "simpleText").try &.as_s || ""
@@ -267,13 +286,14 @@ private module Parsers
# TODO: item_contents["publishedTimeText"]?
SearchPlaylist.new({
- title: title,
- id: plid,
- author: author,
- ucid: author_id,
- video_count: video_count,
- videos: videos,
- thumbnail: playlist_thumbnail,
+ title: title,
+ id: plid,
+ author: author,
+ ucid: author_id,
+ video_count: video_count,
+ videos: videos,
+ thumbnail: playlist_thumbnail,
+ author_verified: author_verified || false,
})
end
diff --git a/videojs-dependencies.yml b/videojs-dependencies.yml
index 6de23d25..e9ccc9dd 100644
--- a/videojs-dependencies.yml
+++ b/videojs-dependencies.yml
@@ -1,9 +1,7 @@
-# Due to an firefox issue, we're stuck on 7.11.0. If you're hosting a private instance
-# and you're using a chromium based browser, feel free to bump this to the latest version
-# in order to get support for higher resolutions on more videos.
+# Due to a 'video append of' error (see #3011), we're stuck on 7.12.1.
video.js:
- version: 7.11.0
- shasum: e20747d890716085e7255a90d73c00f32324a224
+ version: 7.12.1
+ shasum: 1d12eeb1f52e3679e8e4c987d9b9eb37e2247fa2
videojs-contrib-quality-levels:
version: 2.1.0