summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--assets/css/default.css21
-rw-r--r--assets/js/watch.js56
-rw-r--r--locales/ar.json4
-rw-r--r--locales/de.json549
-rw-r--r--locales/en-US.json9
-rw-r--r--locales/fr.json9
-rw-r--r--locales/nb_NO.json537
-rw-r--r--locales/nl.json9
-rw-r--r--locales/pl.json9
-rw-r--r--locales/ru.json549
-rw-r--r--src/invidious.cr28
-rw-r--r--src/invidious/helpers/helpers.cr22
-rw-r--r--src/invidious/helpers/logger.cr35
-rw-r--r--src/invidious/jobs.cr16
-rw-r--r--src/invidious/views/components/player_sources.ecr2
-rw-r--r--src/invidious/views/watch.ecr61
16 files changed, 1070 insertions, 846 deletions
diff --git a/assets/css/default.css b/assets/css/default.css
index 8bb3f273..4f0db4cf 100644
--- a/assets/css/default.css
+++ b/assets/css/default.css
@@ -262,8 +262,23 @@ img.thumbnail {
#player-container {
position: relative;
- padding-bottom: 56.25%;
- margin-left: 1em;
- margin-right: 1em;
+ padding-bottom: 55.25%;
+ margin-left: 2em;
+ margin-right: 2em;
height: 0;
}
+
+#progress-container {
+ width: 100%;
+ border-radius: 2px;
+ background: #aaa;
+}
+
+#download-progress {
+ width: 0%;
+ border-radius: 2px;
+ height: 10px;
+ background-color: #0078e7;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
diff --git a/assets/js/watch.js b/assets/js/watch.js
index 99500686..81fa6288 100644
--- a/assets/js/watch.js
+++ b/assets/js/watch.js
@@ -50,3 +50,59 @@ function hide_youtube_replies(target) {
target.innerHTML = "Show replies";
target.setAttribute("onclick", "show_youtube_replies(this)");
}
+
+function download_video(target) {
+ var title = target.getAttribute("data-title");
+ var children = document.getElementById("download_widget").children;
+ var progress = document.getElementById("download-progress");
+ var url = "";
+
+ document.getElementById("progress-container").style.display = "";
+
+ for (i = 0; i < children.length; i++) {
+ if (children[i].selected) {
+ url = children[i].getAttribute("data-url");
+ }
+ }
+
+ url = "/videoplayback" + url.split("/videoplayback")[1];
+
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", url);
+ xhr.responseType = "arraybuffer";
+
+ xhr.onprogress = function(event) {
+ if (event.lengthComputable) {
+ progress.style.width = "" + (event.loaded / event.total)*100 + "%";
+ }
+ };
+
+ xhr.onload = function(event) {
+ if (event.currentTarget.status != 200) {
+ console.log("Downloading " + title + " failed.")
+ document.getElementById("progress-container").style.display = "none";
+ progress.style.width = "0%";
+
+ return;
+ }
+
+ var data = new Blob([xhr.response], {'type' : 'video/mp4'});
+ var videoFile = window.URL.createObjectURL(data);
+
+ var link = document.createElement('a');
+ link.href = videoFile;
+ link.setAttribute('download', title);
+ document.body.appendChild(link);
+
+ window.requestAnimationFrame(function() {
+ var event = new MouseEvent('click');
+ link.dispatchEvent(event);
+ document.body.removeChild(link);
+ });
+
+ document.getElementById("progress-container").style.display = "none";
+ progress.style.width = "0%";
+ };
+
+ xhr.send(null);
+} \ No newline at end of file
diff --git a/locales/ar.json b/locales/ar.json
index 268be9df..55592d2c 100644
--- a/locales/ar.json
+++ b/locales/ar.json
@@ -274,5 +274,7 @@
"Music": "الاغانى",
"Gaming": "الألعاب",
"News": "الأخبار",
- "Movies": "الأفلام"
+ "Movies": "الأفلام",
+ "Download as:": "تحميل كـ",
+ "Download": "تحميل"
}
diff --git a/locales/de.json b/locales/de.json
index 039b1564..e176da76 100644
--- a/locales/de.json
+++ b/locales/de.json
@@ -1,273 +1,280 @@
{
- "`x` subscribers": "`x` Abonnenten",
- "`x` videos": "`x` Videos",
- "LIVE": "LIVE",
- "Shared `x` ago": "Vor `x` geteilt",
- "Unsubscribe": "Abbestellen",
- "Subscribe": "Abonnieren",
- "Login to subscribe to `x`": "Einloggen um `x` zu abonnieren",
- "View channel on YouTube": "Kanal auf YouTube anzeigen",
- "newest": "neueste",
- "oldest": "älteste",
- "popular": "beliebt",
- "Preview page": "Vorschau Seite",
- "Next page": "Nächste Seite",
- "Clear watch history?": "Verlauf löschen?",
- "Yes": "Ja",
- "No": "Nein",
- "Import and Export Data": "Import und Export Daten",
- "Import": "Importieren",
- "Import Invidious data": "Invidious Daten importieren",
- "Import YouTube subscriptions": "YouTube Abonnements importieren",
- "Import FreeTube subscriptions (.db)": "FreeTube Abonnements importieren (.db)",
- "Import NewPipe subscriptions (.json)": "NewPipe Abonnements importieren (.json)",
- "Import NewPipe data (.zip)": "NewPipe Daten importieren (.zip)",
- "Export": "Exportieren",
- "Export subscriptions as OPML": "Abonnements als OPML exportieren",
- "Export subscriptions as OPML (for NewPipe & FreeTube)": "Abonnements als OPML exportieren (für NewPipe & FreeTube)",
- "Export data as JSON": "Daten als JSON exportieren",
- "Delete account?": "Account löschen?",
- "History": "Verlauf",
- "Previous page": "Vorherige Seite",
- "An alternative front-end to YouTube": "Eine alternative Oberfläche für YouTube",
- "JavaScript license information": "JavaScript Lizenzinformationen",
- "source": "Quelle",
- "Login": "Einloggen",
- "Login/Register": "Einloggen/Registrieren",
- "Login to Google": "In Google einloggen",
- "User ID:": "Benutzer ID:",
- "Password:": "Passwort:",
- "Time (h:mm:ss):": "Zeit (h:mm:ss):",
- "Text CAPTCHA": "Text CAPTCHA",
- "Image CAPTCHA": "Image CAPTCHA",
- "Sign In": "Einloggen",
- "Register": "Registrieren",
- "Email:": "Email:",
- "Google verification code:": "Google Bestätigungscode:",
- "Preferences": "Einstellungen",
- "Player preferences": "Playereinstellungen",
- "Always loop: ": "Immer wiederholen: ",
- "Autoplay: ": "Automatisch abspielen: ",
- "Autoplay next video: ": "nächstes Video automatisch abspielen: ",
- "Listen by default: ": "Nur Ton als Standard: ",
- "Default speed: ": "Standardgeschwindigkeit: ",
- "Preferred video quality: ": "Bevorzugte Videoqualität: ",
- "Player volume: ": "Playerlautstärke: ",
- "Default comments: ": "Standardkommentare: ",
- "youtube": "youtube",
- "reddit": "reddit",
- "Default captions: ": "Standarduntertitel: ",
- "Fallback captions: ": "Ersatzuntertitel: ",
- "Show related videos? ": "Ähnliche Videos anzeigen? ",
- "Visual preferences": "Anzeigeeinstellungen",
- "Dark mode: ": "Nachtmodus: ",
- "Thin mode: ": "Schlanker Modus: ",
- "Subscription preferences": "Abonnementeinstellungen",
- "Redirect homepage to feed: ": "Startseite zu Feed umleiten: ",
- "Number of videos shown in feed: ": "Anzahl von Videos die im Feed angezeigt werden: ",
- "Sort videos by: ": "Videos sortieren nach: ",
- "published": "veröffentlicht",
- "published - reverse": "veröffentlicht - invertiert",
- "alphabetically": "alphabetisch",
- "alphabetically - reverse": "alphabetisch - invertiert",
- "channel name": "Kanalname",
- "channel name - reverse": "Kanalname - invertiert",
- "Only show latest video from channel: ": "Nur neueste Videos des Kanals anzeigen: ",
- "Only show latest unwatched video from channel: ": "Nur neueste ungesehene Videos des Kanals anzeigen: ",
- "Only show unwatched: ": "Nur ungesehene anzeigen: ",
- "Only show notifications (if there are any): ": "Nur Benachrichtigungen anzeigen (wenn es welche gibt): ",
- "Data preferences": "Dateneinstellungen",
- "Clear watch history": "Verlauf löschen",
- "Import/Export data": "Daten im- exportieren",
- "Manage subscriptions": "Abonnements verwalten",
- "Watch history": "Verlauf",
- "Delete account": "Account löschen",
- "Save preferences": "Einstellungen speichern",
- "Subscription manager": "Abonnementverwaltung",
- "`x` subscriptions": "`x` Abonnements",
- "Import/Export": "Importieren/Exportieren",
- "unsubscribe": "abbestellen",
- "Subscriptions": "Abonnements",
- "`x` unseen notifications": "`x` ungesehene Benachrichtigungen",
- "search": "Suchen",
- "Sign out": "Abmelden",
- "Released under the AGPLv3 by Omar Roth.": "Veröffentlicht unter AGPLv3 von Omar Roth.",
- "Source available here.": "Quellcode verfügbar hier.",
- "Liberapay: ": "Liberapay: ",
- "Patreon: ": "Patreon: ",
- "BTC: ": "BTC: ",
- "BCH: ": "BCH: ",
- "View JavaScript license information.": "Javascript Lizenzinformationen anzeigen.",
- "Trending": "Trending",
- "Watch video on Youtube": "Video auf YouTube ansehen",
- "Genre: ": "Genre: ",
- "License: ": "Lizenz: ",
- "Family friendly? ": "Familienfreundlich? ",
- "Wilson score: ": "Wilson-Score: ",
- "Engagement: ": "Engagement: ",
- "Whitelisted regions: ": "Erlaubte Regionen: ",
- "Blacklisted regions: ": "Unerlaubte Regionen: ",
- "Shared `x`": "Geteilt `x`",
- "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hallo! Anscheinend haben Sie JavaScript deaktiviert. Klicken Sie hier um Kommentare anzuzeigen, beachten sie dass es etwas länger dauern kann um sie zu laden.",
- "View YouTube comments": "YouTube Kommentare anzeigen",
- "View more comments on Reddit": "Mehr Kommentare auf Reddit anzeigen",
- "View `x` comments": "`x` Kommentare anzeigen",
- "View Reddit comments": "Reddit Kommentare anzeigen",
- "Hide replies": "Antworten verstecken",
- "Show replies": "Antworten anzeigen",
- "Incorrect password": "Falsches Passwort",
- "Quota exceeded, try again in a few hours": "Kontingent überschritten, versuche es in ein paar Stunden erneut",
- "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Login nicht möglich, stellen Sie sicher dass two-factor Authentifikation (Authentifizierung oder SMS) aktiviert ist.",
- "Invalid TFA code": "Ungültiger TFA Code",
- "Login failed. This may be because two-factor authentication is not enabled on your account.": "Login fehlgeschlagen. Das kann daran liegen dass two-factor Authentifizierung in ihrem Account nicht aktiviert ist.",
- "Invalid answer": "Ungültige Antwort",
- "Invalid CAPTCHA": "Ungültiges CAPTCHA",
- "CAPTCHA is a required field": "CAPTCHA ist eine erforderliche Eingabe",
- "User ID is a required field": "Benutzer ID ist eine erforderliche Eingabe",
- "Password is a required field": "Passwort ist eine erforderliche Eingabe",
- "Invalid username or password": "Ungültiger Benutzername oder Passwort",
- "Please sign in using 'Sign in with Google'": "Bitte melden sie sich mit 'Mit Google anmelden' an",
- "Password cannot be empty": "Passwort darf nicht leer sein",
- "Password cannot be longer than 55 characters": "Passwort darf nicht länger als 55 Zeichen sein",
- "Please sign in": "Bitte anmelden",
- "Invidious Private Feed for `x`": "Invidious Persönlicher Feed für `x`",
- "channel:`x`": "Kanal:`x`",
- "Deleted or invalid channel": "Gelöschter oder ungültiger Kanal",
- "This channel does not exist.": "Dieser Kanal existiert nicht.",
- "Could not get channel info.": "Kanalinformationen konnten nicht geladen werden.",
- "Could not fetch comments": "Kommentare konnten nicht geladen werden",
- "View `x` replies": "Zeige `x` Antworten",
- "`x` ago": "vor `x`",
- "Load more": "Mehr laden",
- "`x` points": "`x` Punkte",
- "Could not create mix.": "Mix konnte nicht erstellt werden.",
- "Playlist is empty": "Playlist ist leer",
- "Invalid playlist.": "Ungültige Playlist.",
- "Playlist does not exist.": "Playlist existiert nicht.",
- "Could not pull trending pages.": "Trending Seiten konnten nicht geladen werden.",
- "Hidden field \"challenge\" is a required field": "Verstecktes Feld \"challenge\" ist eine erforderliche Eingabe",
- "Hidden field \"token\" is a required field": "Verstecktes Feld \"token\" ist eine erforderliche Eingabe",
- "Invalid challenge": "Ungültiger Test",
- "Invalid token": "Ungöltige Marke",
- "Invalid user": "Ungültiger Benutzer",
- "Token is expired, please try again": "Marke ist abgelaufen, bitte erneut versuchen",
- "English": "Englisch",
- "English (auto-generated)": "Englisch (automatisch erzeugt)",
- "Afrikaans": "Afrikaans",
- "Albanian": "Albanisch",
- "Amharic": "Amharisch",
- "Arabic": "Arabisch",
- "Armenian": "Armenisch",
- "Azerbaijani": "Aserbaidschanisch",
- "Bangla": "Bengalisch",
- "Basque": "Baskisch",
- "Belarusian": "Weißrussisch",
- "Bosnian": "Bosnisch",
- "Bulgarian": "Bulgarisch",
- "Burmese": "Burmesisch",
- "Catalan": "Katalanisch",
- "Cebuano": "Cebuano",
- "Chinese (Simplified)": "Chinesisch (vereinfacht)",
- "Chinese (Traditional)": "Chinesisch (traditionell)",
- "Corsican": "Korsisch",
- "Croatian": "Kroatisch",
- "Czech": "Tschechisch",
- "Danish": "Dänisch",
- "Dutch": "Niederländisch",
- "Esperanto": "Esperanto",
- "Estonian": "Estnisch",
- "Filipino": "Philippinisch",
- "Finnish": "Finnisch",
- "French": "Französisch",
- "Galician": "Galizisch",
- "Georgian": "Georgisch",
- "German": "Deutsch",
- "Greek": "Griechisch",
- "Gujarati": "Gujarati",
- "Haitian Creole": "Haitianisches Kreolisch",
- "Hausa": "Hausa",
- "Hawaiian": "Hawaiianisch",
- "Hebrew": "Hebräisch",
- "Hindi": "Hindi",
- "Hmong": "Hmong",
- "Hungarian": "Ungarisch",
- "Icelandic": "Isländisch",
- "Igbo": "Igbo",
- "Indonesian": "Indonesisch",
- "Irish": "Irisch",
- "Italian": "Italienisch",
- "Japanese": "Japanisch",
- "Javanese": "Javanisch",
- "Kannada": "Kannada",
- "Kazakh": "Kasachisch",
- "Khmer": "Khmer",
- "Korean": "Koreanisch",
- "Kurdish": "Kurdisch",
- "Kyrgyz": "Kirgisisch",
- "Lao": "Laotisch",
- "Latin": "Lateinisch",
- "Latvian": "Lettisch",
- "Lithuanian": "Litauisch",
- "Luxembourgish": "Luxemburgisch",
- "Macedonian": "Mazedonisch",
- "Malagasy": "Madagassisch",
- "Malay": "Malaiisch",
- "Malayalam": "Malayalam",
- "Maltese": "Maltesisch",
- "Maori": "Maori",
- "Marathi": "Marathi",
- "Mongolian": "Mongolisch",
- "Nepali": "Nepalesisch",
- "Norwegian": "Norwegisch",
- "Nyanja": "Nyanja",
- "Pashto": "Paschtunisch",
- "Persian": "Persisch",
- "Polish": "Polnisch",
- "Portuguese": "Portugiesisch",
- "Punjabi": "Pandschabi",
- "Romanian": "Rumänisch",
- "Russian": "Russisch",
- "Samoan": "Samoanisch",
- "Scottish Gaelic": "Schottisches Gälisch",
- "Serbian": "Serbisch",
- "Shona": "Schona",
- "Sindhi": "Sindhi",
- "Sinhala": "Singhalesisch",
- "Slovak": "Slowakisch",
- "Slovenian": "Slowenisch",
- "Somali": "Somali",
- "Southern Sotho": "Südliches Sotho",
- "Spanish": "Spanisch",
- "Spanish (Latin America)": "Spanisch (Lateinamerika)",
- "Sundanese": "Sundanesisch",
- "Swahili": "Suaheli",
- "Swedish": "Schwedisch",
- "Tajik": "Tadschikisch",
- "Tamil": "Tamilisch",
- "Telugu": "Telugu",
- "Thai": "Thailändisch",
- "Turkish": "Türkisch",
- "Ukrainian": "Ukrainisch",
- "Urdu": "Urdu",
- "Uzbek": "Usbekisch",
- "Vietnamese": "Vietnamesisch",
- "Welsh": "Walisisch",
- "Western Frisian": "Westfriesisch",
- "Xhosa": "Xhosa",
- "Yiddish": "Jiddisch",
- "Yoruba": "Joruba",
- "Zulu": "Zulu",
- "`x` years": "`x` Jahre",
- "`x` months": "`x` Monate",
- "`x` weeks": "`x` Wochen",
- "`x` days": "`x` Tage",
- "`x` hours": "`x` Stunden",
- "`x` minutes": "`x` Minuten",
- "`x` seconds": "`x` Sekunden",
- "Fallback comments: ": "",
- "Popular": "Populär",
- "Top": "",
- "About": "Über",
- "Rating: ": "Bewertung: ",
- "Language: ": "Sprache: "
+ "`x` subscribers": "`x` Abonnenten",
+ "`x` videos": "`x` Videos",
+ "LIVE": "LIVE",
+ "Shared `x` ago": "Vor `x` geteilt",
+ "Unsubscribe": "Abbestellen",
+ "Subscribe": "Abonnieren",
+ "Login to subscribe to `x`": "Einloggen um `x` zu abonnieren",
+ "View channel on YouTube": "Kanal auf YouTube anzeigen",
+ "newest": "neueste",
+ "oldest": "älteste",
+ "popular": "beliebt",
+ "Preview page": "Vorschau Seite",
+ "Next page": "Nächste Seite",
+ "Clear watch history?": "Verlauf löschen?",
+ "Yes": "Ja",
+ "No": "Nein",
+ "Import and Export Data": "Import und Export Daten",
+ "Import": "Importieren",
+ "Import Invidious data": "Invidious Daten importieren",
+ "Import YouTube subscriptions": "YouTube Abonnements importieren",
+ "Import FreeTube subscriptions (.db)": "FreeTube Abonnements importieren (.db)",
+ "Import NewPipe subscriptions (.json)": "NewPipe Abonnements importieren (.json)",
+ "Import NewPipe data (.zip)": "NewPipe Daten importieren (.zip)",
+ "Export": "Exportieren",
+ "Export subscriptions as OPML": "Abonnements als OPML exportieren",
+ "Export subscriptions as OPML (for NewPipe & FreeTube)": "Abonnements als OPML exportieren (für NewPipe & FreeTube)",
+ "Export data as JSON": "Daten als JSON exportieren",
+ "Delete account?": "Account löschen?",
+ "History": "Verlauf",
+ "Previous page": "Vorherige Seite",
+ "An alternative front-end to YouTube": "Eine alternative Oberfläche für YouTube",
+ "JavaScript license information": "JavaScript Lizenzinformationen",
+ "source": "Quelle",
+ "Login": "Einloggen",
+ "Login/Register": "Einloggen/Registrieren",
+ "Login to Google": "In Google einloggen",
+ "User ID:": "Benutzer ID:",
+ "Password:": "Passwort:",
+ "Time (h:mm:ss):": "Zeit (h:mm:ss):",
+ "Text CAPTCHA": "Text CAPTCHA",
+ "Image CAPTCHA": "Image CAPTCHA",
+ "Sign In": "Einloggen",
+ "Register": "Registrieren",
+ "Email:": "Email:",
+ "Google verification code:": "Google Bestätigungscode:",
+ "Preferences": "Einstellungen",
+ "Player preferences": "Playereinstellungen",
+ "Always loop: ": "Immer wiederholen: ",
+ "Autoplay: ": "Automatisch abspielen: ",
+ "Autoplay next video: ": "nächstes Video automatisch abspielen: ",
+ "Listen by default: ": "Nur Ton als Standard: ",
+ "Default speed: ": "Standardgeschwindigkeit: ",
+ "Preferred video quality: ": "Bevorzugte Videoqualität: ",
+ "Player volume: ": "Playerlautstärke: ",
+ "Default comments: ": "Standardkommentare: ",
+ "youtube": "youtube",
+ "reddit": "reddit",
+ "Default captions: ": "Standarduntertitel: ",
+ "Fallback captions: ": "Ersatzuntertitel: ",
+ "Show related videos? ": "Ähnliche Videos anzeigen? ",
+ "Visual preferences": "Anzeigeeinstellungen",
+ "Dark mode: ": "Nachtmodus: ",
+ "Thin mode: ": "Schlanker Modus: ",
+ "Subscription preferences": "Abonnementeinstellungen",
+ "Redirect homepage to feed: ": "Startseite zu Feed umleiten: ",
+ "Number of videos shown in feed: ": "Anzahl von Videos die im Feed angezeigt werden: ",
+ "Sort videos by: ": "Videos sortieren nach: ",
+ "published": "veröffentlicht",
+ "published - reverse": "veröffentlicht - invertiert",
+ "alphabetically": "alphabetisch",
+ "alphabetically - reverse": "alphabetisch - invertiert",
+ "channel name": "Kanalname",
+ "channel name - reverse": "Kanalname - invertiert",
+ "Only show latest video from channel: ": "Nur neueste Videos des Kanals anzeigen: ",
+ "Only show latest unwatched video from channel: ": "Nur neueste ungesehene Videos des Kanals anzeigen: ",
+ "Only show unwatched: ": "Nur ungesehene anzeigen: ",
+ "Only show notifications (if there are any): ": "Nur Benachrichtigungen anzeigen (wenn es welche gibt): ",
+ "Data preferences": "Dateneinstellungen",
+ "Clear watch history": "Verlauf löschen",
+ "Import/Export data": "Daten im- exportieren",
+ "Manage subscriptions": "Abonnements verwalten",
+ "Watch history": "Verlauf",
+ "Delete account": "Account löschen",
+ "Save preferences": "Einstellungen speichern",
+ "Subscription manager": "Abonnementverwaltung",
+ "`x` subscriptions": "`x` Abonnements",
+ "Import/Export": "Importieren/Exportieren",
+ "unsubscribe": "abbestellen",
+ "Subscriptions": "Abonnements",
+ "`x` unseen notifications": "`x` ungesehene Benachrichtigungen",
+ "search": "Suchen",
+ "Sign out": "Abmelden",
+ "Released under the AGPLv3 by Omar Roth.": "Veröffentlicht unter AGPLv3 von Omar Roth.",
+ "Source available here.": "Quellcode verfügbar hier.",
+ "Liberapay: ": "Liberapay: ",
+ "Patreon: ": "Patreon: ",
+ "BTC: ": "BTC: ",
+ "BCH: ": "BCH: ",
+ "View JavaScript license information.": "Javascript Lizenzinformationen anzeigen.",
+ "Trending": "Trending",
+ "Watch video on Youtube": "Video auf YouTube ansehen",
+ "Genre: ": "Genre: ",
+ "License: ": "Lizenz: ",
+ "Family friendly? ": "Familienfreundlich? ",
+ "Wilson score: ": "Wilson-Score: ",
+ "Engagement: ": "Engagement: ",
+ "Whitelisted regions: ": "Erlaubte Regionen: ",
+ "Blacklisted regions: ": "Unerlaubte Regionen: ",
+ "Shared `x`": "Geteilt `x`",
+ "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hallo! Anscheinend haben Sie JavaScript deaktiviert. Klicken Sie hier um Kommentare anzuzeigen, beachten sie dass es etwas länger dauern kann um sie zu laden.",
+ "View YouTube comments": "YouTube Kommentare anzeigen",
+ "View more comments on Reddit": "Mehr Kommentare auf Reddit anzeigen",
+ "View `x` comments": "`x` Kommentare anzeigen",
+ "View Reddit comments": "Reddit Kommentare anzeigen",
+ "Hide replies": "Antworten verstecken",
+ "Show replies": "Antworten anzeigen",
+ "Incorrect password": "Falsches Passwort",
+ "Quota exceeded, try again in a few hours": "Kontingent überschritten, versuche es in ein paar Stunden erneut",
+ "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Login nicht möglich, stellen Sie sicher dass two-factor Authentifikation (Authentifizierung oder SMS) aktiviert ist.",
+ "Invalid TFA code": "Ungültiger TFA Code",
+ "Login failed. This may be because two-factor authentication is not enabled on your account.": "Login fehlgeschlagen. Das kann daran liegen dass two-factor Authentifizierung in ihrem Account nicht aktiviert ist.",
+ "Invalid answer": "Ungültige Antwort",
+ "Invalid CAPTCHA": "Ungültiges CAPTCHA",
+ "CAPTCHA is a required field": "CAPTCHA ist eine erforderliche Eingabe",
+ "User ID is a required field": "Benutzer ID ist eine erforderliche Eingabe",
+ "Password is a required field": "Passwort ist eine erforderliche Eingabe",
+ "Invalid username or password": "Ungültiger Benutzername oder Passwort",
+ "Please sign in using 'Sign in with Google'": "Bitte melden sie sich mit 'Mit Google anmelden' an",
+ "Password cannot be empty": "Passwort darf nicht leer sein",
+ "Password cannot be longer than 55 characters": "Passwort darf nicht länger als 55 Zeichen sein",
+ "Please sign in": "Bitte anmelden",
+ "Invidious Private Feed for `x`": "Invidious Persönlicher Feed für `x`",
+ "channel:`x`": "Kanal:`x`",
+ "Deleted or invalid channel": "Gelöschter oder ungültiger Kanal",
+ "This channel does not exist.": "Dieser Kanal existiert nicht.",
+ "Could not get channel info.": "Kanalinformationen konnten nicht geladen werden.",
+ "Could not fetch comments": "Kommentare konnten nicht geladen werden",
+ "View `x` replies": "Zeige `x` Antworten",
+ "`x` ago": "vor `x`",
+ "Load more": "Mehr laden",
+ "`x` points": "`x` Punkte",
+ "Could not create mix.": "Mix konnte nicht erstellt werden.",
+ "Playlist is empty": "Playlist ist leer",
+ "Invalid playlist.": "Ungültige Playlist.",
+ "Playlist does not exist.": "Playlist existiert nicht.",
+ "Could not pull trending pages.": "Trending Seiten konnten nicht geladen werden.",
+ "Hidden field \"challenge\" is a required field": "Verstecktes Feld \"challenge\" ist eine erforderliche Eingabe",
+ "Hidden field \"token\" is a required field": "Verstecktes Feld \"token\" ist eine erforderliche Eingabe",
+ "Invalid challenge": "Ungültiger Test",
+ "Invalid token": "Ungöltige Marke",
+ "Invalid user": "Ungültiger Benutzer",
+ "Token is expired, please try again": "Marke ist abgelaufen, bitte erneut versuchen",
+ "English": "Englisch",
+ "English (auto-generated)": "Englisch (automatisch erzeugt)",
+ "Afrikaans": "Afrikaans",
+ "Albanian": "Albanisch",
+ "Amharic": "Amharisch",
+ "Arabic": "Arabisch",
+ "Armenian": "Armenisch",
+ "Azerbaijani": "Aserbaidschanisch",
+ "Bangla": "Bengalisch",
+ "Basque": "Baskisch",
+ "Belarusian": "Weißrussisch",
+ "Bosnian": "Bosnisch",
+ "Bulgarian": "Bulgarisch",
+ "Burmese": "Burmesisch",
+ "Catalan": "Katalanisch",
+ "Cebuano": "Cebuano",
+ "Chinese (Simplified)": "Chinesisch (vereinfacht)",
+ "Chinese (Traditional)": "Chinesisch (traditionell)",
+ "Corsican": "Korsisch",
+ "Croatian": "Kroatisch",
+ "Czech": "Tschechisch",
+ "Danish": "Dänisch",
+ "Dutch": "Niederländisch",
+ "Esperanto": "Esperanto",
+ "Estonian": "Estnisch",
+ "Filipino": "Philippinisch",
+ "Finnish": "Finnisch",
+ "French": "Französisch",
+ "Galician": "Galizisch",
+ "Georgian": "Georgisch",
+ "German": "Deutsch",
+ "Greek": "Griechisch",
+ "Gujarati": "Gujarati",
+ "Haitian Creole": "Haitianisches Kreolisch",
+ "Hausa": "Hausa",
+ "Hawaiian": "Hawaiianisch",
+ "Hebrew": "Hebräisch",
+ "Hindi": "Hindi",
+ "Hmong": "Hmong",
+ "Hungarian": "Ungarisch",
+ "Icelandic": "Isländisch",
+ "Igbo": "Igbo",
+ "Indonesian": "Indonesisch",
+ "Irish": "Irisch",
+ "Italian": "Italienisch",
+ "Japanese": "Japanisch",
+ "Javanese": "Javanisch",
+ "Kannada": "Kannada",
+ "Kazakh": "Kasachisch",
+ "Khmer": "Khmer",
+ "Korean": "Koreanisch",
+ "Kurdish": "Kurdisch",
+ "Kyrgyz": "Kirgisisch",
+ "Lao": "Laotisch",
+ "Latin": "Lateinisch",
+ "Latvian": "Lettisch",
+ "Lithuanian": "Litauisch",
+ "Luxembourgish": "Luxemburgisch",
+ "Macedonian": "Mazedonisch",
+ "Malagasy": "Madagassisch",
+ "Malay": "Malaiisch",
+ "Malayalam": "Malayalam",
+ "Maltese": "Maltesisch",
+ "Maori": "Maori",
+ "Marathi": "Marathi",
+ "Mongolian": "Mongolisch",
+ "Nepali": "Nepalesisch",
+ "Norwegian": "Norwegisch",
+ "Nyanja": "Nyanja",
+ "Pashto": "Paschtunisch",
+ "Persian": "Persisch",
+ "Polish": "Polnisch",
+ "Portuguese": "Portugiesisch",
+ "Punjabi": "Pandschabi",
+ "Romanian": "Rumänisch",
+ "Russian": "Russisch",
+ "Samoan": "Samoanisch",
+ "Scottish Gaelic": "Schottisches Gälisch",
+ "Serbian": "Serbisch",
+ "Shona": "Schona",
+ "Sindhi": "Sindhi",
+ "Sinhala": "Singhalesisch",
+ "Slovak": "Slowakisch",
+ "Slovenian": "Slowenisch",
+ "Somali": "Somali",
+ "Southern Sotho": "Südliches Sotho",
+ "Spanish": "Spanisch",
+ "Spanish (Latin America)": "Spanisch (Lateinamerika)",
+ "Sundanese": "Sundanesisch",
+ "Swahili": "Suaheli",
+ "Swedish": "Schwedisch",
+ "Tajik": "Tadschikisch",
+ "Tamil": "Tamilisch",
+ "Telugu": "Telugu",
+ "Thai": "Thailändisch",
+ "Turkish": "Türkisch",
+ "Ukrainian": "Ukrainisch",
+ "Urdu": "Urdu",
+ "Uzbek": "Usbekisch",
+ "Vietnamese": "Vietnamesisch",
+ "Welsh": "Walisisch",
+ "Western Frisian": "Westfriesisch",
+ "Xhosa": "Xhosa",
+ "Yiddish": "Jiddisch",
+ "Yoruba": "Joruba",
+ "Zulu": "Zulu",
+ "`x` years": "`x` Jahre",
+ "`x` months": "`x` Monate",
+ "`x` weeks": "`x` Wochen",
+ "`x` days": "`x` Tage",
+ "`x` hours": "`x` Stunden",
+ "`x` minutes": "`x` Minuten",
+ "`x` seconds": "`x` Sekunden",
+ "Fallback comments: ": "",
+ "Popular": "Populär",
+ "Top": "",
+ "About": "Über",
+ "Rating: ": "Bewertung: ",
+ "Language: ": "Sprache: ",
+ "Default": "",
+ "Music": "",
+ "Gaming": "",
+ "News": "",
+ "Movies": "",
+ "Download": "",
+ "Download as: ": ""
}
diff --git a/locales/en-US.json b/locales/en-US.json
index 10e04598..ccc01458 100644
--- a/locales/en-US.json
+++ b/locales/en-US.json
@@ -263,5 +263,12 @@
"Top": "Top",
"About": "About",
"Rating: ": "Rating: ",
- "Language: ": "Language: "
+ "Language: ": "Language: ",
+ "Default": "Default",
+ "Music": "Music",
+ "Gaming": "Gaming",
+ "News": "News",
+ "Movies": "Movies",
+ "Download": "Download",
+ "Download as: ": "Download as: "
}
diff --git a/locales/fr.json b/locales/fr.json
index 4f9e3441..8d81d6cc 100644
--- a/locales/fr.json
+++ b/locales/fr.json
@@ -263,5 +263,12 @@
"Top": "Haut",
"About": "Sur",
"Rating: ": "Évaluation: ",
- "Language: ": "Langue: "
+ "Language: ": "Langue: ",
+ "Default": "",
+ "Music": "",
+ "Gaming": "",
+ "News": "",
+ "Movies": "",
+ "Download": "",
+ "Download as: ": ""
}
diff --git a/locales/nb_NO.json b/locales/nb_NO.json
index a64e61b7..7fe3e6a8 100644
--- a/locales/nb_NO.json
+++ b/locales/nb_NO.json
@@ -1,267 +1,274 @@
{
- "`x` subscribers": "`x` abonnenter",
- "`x` videos": "`x` videoer",
- "LIVE": "SANNTIDSVISNING",
- "Shared `x` ago": "Delt for `x` siden",
- "Unsubscribe": "Opphev abonnement",
- "Subscribe": "Abonner",
- "Login to subscribe to `x`": "Logg inn for å abonnere på `x`",
- "View channel on YouTube": "Vis kanal på YouTube",
- "newest": "nyeste",
- "oldest": "eldste",
- "popular": "populært",
- "Preview page": "Forhåndsvis side",
- "Next page": "Neste side",
- "Clear watch history?": "Tøm visningshistorikk?",
- "Yes": "Ja",
- "No": "Nei",
- "Import and Export Data": "Importer- og eksporter data",
- "Import": "Importer",
- "Import Invidious data": "Importer Invidious-data",
- "Import YouTube subscriptions": "Importer YouTube-abonnenter",
- "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)",
- "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)",
- "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)",
- "Export": "Eksporter",
- "Export subscriptions as OPML": "Eksporter abonnenter som OPML",
- "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)",
- "Export data as JSON": "Eksporter data som JSON",
- "Delete account?": "Slett konto?",
- "History": "Historikk",
- "Previous page": "Forrige side",
- "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube",
- "JavaScript license information": "JavaScript-lisensinformasjon",
- "source": "kilde",
- "Login": "Logg inn",
- "Login/Register": "Logg inn/registrer",
- "Login to Google": "Logg inn med Google",
- "User ID:": "Bruker-ID:",
- "Password:": "Passord:",
- "Time (h:mm:ss):": "Tid (h:mm:ss):",
- "Text CAPTCHA": "Tekst-CAPTCHA",
- "Image CAPTCHA": "Bilde-CAPTCHA",
- "Sign In": "Innlogging",
- "Register": "Registrer",
- "Email:": "E-post:",
- "Google verification code:": "Google-bekreftelseskode:",
- "Preferences": "Innstillinger",
- "Player preferences": "Avspillerinnstillinger",
- "Always loop: ": "Alltid gjenta: ",
- "Autoplay: ": "Autoavspilling: ",
- "Autoplay next video: ": "Autospill neste video: ",
- "Listen by default: ": "Lytt som forvalg: ",
- "Default speed: ": "Forvalgt hastighet: ",
- "Preferred video quality: ": "Foretrukket videokvalitet: ",
- "Player volume: ": "Avspillerlydstyrke: ",
- "Default comments: ": "Forvalgte kommentarer: ",
- "Default captions: ": "Forvalgte undertitler: ",
- "Fallback captions: ": "Tilbakefallsundertitler: ",
- "Show related videos? ": "Vis relaterte videoer? ",
- "Visual preferences": "Visuelle innstillinger",
- "Dark mode: ": "Mørk drakt: ",
- "Thin mode: ": "Tynt modus: ",
- "Subscription preferences": "Abonnementsinnstillinger",
- "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ",
- "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ",
- "Sort videos by: ": "Sorter videoer etter: ",
- "published": "publisert",
- "published - reverse": "publisert - motsatt",
- "alphabetically": "alfabetisk",
- "alphabetically - reverse": "alfabetisk - motsatt",
- "channel name": "kanalnavn",
- "channel name - reverse": "kanalnavn - motsatt",
- "Only show latest video from channel: ": "Kun vis siste video fra kanal: ",
- "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ",
- "Only show unwatched: ": "Kun vis usette: ",
- "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ",
- "Data preferences": "Datainnstillinger",
- "Clear watch history": "Tøm visningshistorikk",
- "Import/Export data": "Importer/eksporter data",
- "Manage subscriptions": "Behandle abonnementer",
- "Watch history": "Visningshistorikk",
- "Delete account": "Slett konto",
- "Save preferences": "Lagre innstillinger",
- "Subscription manager": "Abonnementsbehandler",
- "`x` subscriptions": "`x` abonnementer",
- "Import/Export": "Importer/eksporter",
- "unsubscribe": "opphev abonnement",
- "Subscriptions": "Abonnement",
- "`x` unseen notifications": "`x` usette merknader",
- "search": "søk",
- "Sign out": "Logg ut",
- "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.",
- "Source available here.": "Kildekode tilgjengelig her.",
- "View JavaScript license information.": "Vis JavaScript-lisensinfo.",
- "Trending": "Trendsettende",
- "Watch video on Youtube": "Vis video på YouTube",
- "Genre: ": "Sjanger: ",
- "License: ": "Lisens: ",
- "Family friendly? ": "Familievennlig? ",
- "Wilson score: ": "Wilson-poengsum: ",
- "Engagement: ": "Engasjement: ",
- "Whitelisted regions: ": "Hvitlistede regioner: ",
- "Blacklisted regions: ": "Svartelistede regioner: ",
- "Shared `x`": "Delt `x`",
- "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.",
- "View YouTube comments": "Vis YouTube-kommentarer",
- "View more comments on Reddit": "Vis flere kommenterer på Reddit",
- "View `x` comments": "Vis `x` kommentarer",
- "View Reddit comments": "Vis Reddit-kommentarer",
- "Hide replies": "Skjul svar",
- "Show replies": "Vis svar",
- "Incorrect password": "Feil passord",
- "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer",
- "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.",
- "Invalid TFA code": "Ugyldig tofaktorkode",
- "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.",
- "Invalid answer": "Ugyldig svar",
- "Invalid CAPTCHA": "Ugyldig CAPTCHA",
- "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt",
- "User ID is a required field": "Bruker-ID er et påkrevd felt",
- "Password is a required field": "Passord er et påkrevd felt",
- "Invalid username or password": "Ugyldig brukernavn eller passord",
- "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"",
- "Password cannot be empty": "Passordet kan ikke være tomt",
- "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn",
- "Please sign in": "Logg inn",
- "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`",
- "channel:`x`": "kanal `x`",
- "Deleted or invalid channel": "Slettet eller ugyldig kanal",
- "This channel does not exist.": "Denne kanalen finnes ikke.",
- "Could not get channel info.": "Kunne ikke innhente kanalinfo.",
- "Could not fetch comments": "Kunne ikke hente kommentarer",
- "View `x` replies": "Vis `x` svar",
- "`x` ago": "`x` siden",
- "Load more": "Last inn flere",
- "`x` points": "`x` poeng",
- "Could not create mix.": "Kunne ikke opprette miks.",
- "Playlist is empty": "Spillelisten er tom",
- "Invalid playlist.": "Ugyldig spilleliste.",
- "Playlist does not exist.": "Spillelisten finnes ikke.",
- "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.",
- "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt",
- "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt",
- "Invalid challenge": "Ugyldig utfordring",
- "Invalid token": "Ugyldig symbol",
- "Invalid user": "Ugyldig bruker",
- "Token is expired, please try again": "Symbol utløpt, prøv igjen",
- "English": "Engelsk",
- "English (auto-generated)": "Engelsk (auto-generert)",
- "Afrikaans": "",
- "Albanian": "Albansk",
- "Amharic": "",
- "Arabic": "Arabisk",
- "Armenian": "Armensk",
- "Azerbaijani": "",
- "Bangla": "",
- "Basque": "",
- "Belarusian": "Hviterussisk",
- "Bosnian": "Bosnisk",
- "Bulgarian": "Bulgarsk",
- "Burmese": "Burmesisk",
- "Catalan": "Katalansk",
- "Cebuano": "",
- "Chinese (Simplified)": "",
- "Chinese (Traditional)": "",
- "Corsican": "",
- "Croatian": "",
- "Czech": "Tsjekkisk",
- "Danish": "Dansk",
- "Dutch": "",
- "Esperanto": "Esperanto",
- "Estonian": "",
- "Filipino": "",
- "Finnish": "Finsk",
- "French": "Fransk",
- "Galician": "",
- "Georgian": "",
- "German": "",
- "Greek": "",
- "Gujarati": "",
- "Haitian Creole": "",
- "Hausa": "",
- "Hawaiian": "",
- "Hebrew": "",
- "Hindi": "",
- "Hmong": "",
- "Hungarian": "Ungarsk",
- "Icelandic": "Islandsk",
- "Igbo": "",
- "Indonesian": "Indonesisk",
- "Irish": "Irsk",
- "Italian": "Italiensk",
- "Japanese": "Japansk",
- "Javanese": "",
- "Kannada": "",
- "Kazakh": "",
- "Khmer": "",
- "Korean": "",
- "Kurdish": "",
- "Kyrgyz": "",
- "Lao": "",
- "Latin": "",
- "Latvian": "",
- "Lithuanian": "",
- "Luxembourgish": "",
- "Macedonian": "",
- "Malagasy": "",
- "Malay": "",
- "Malayalam": "",
- "Maltese": "",
- "Maori": "",
- "Marathi": "",
- "Mongolian": "",
- "Nepali": "",
- "Norwegian": "Norsk bokmål",
- "Nyanja": "",
- "Pashto": "",
- "Persian": "",
- "Polish": "",
- "Portuguese": "",
- "Punjabi": "",
- "Romanian": "",
- "Russian": "Russisk",
- "Samoan": "",
- "Scottish Gaelic": "",
- "Serbian": "Serbisk",
- "Shona": "",
- "Sindhi": "",
- "Sinhala": "",
- "Slovak": "Slovakisk",
- "Slovenian": "Slovensk",
- "Somali": "Somali",
- "Southern Sotho": "",
- "Spanish": "Spansk",
- "Spanish (Latin America)": "",
- "Sundanese": "",
- "Swahili": "",
- "Swedish": "Svensk",
- "Tajik": "",
- "Tamil": "",
- "Telugu": "",
- "Thai": "",
- "Turkish": "Tyrkisk",
- "Ukrainian": "Ukrainsk",
- "Urdu": "",
- "Uzbek": "",
- "Vietnamese": "Vietnamesisk",
- "Welsh": "",
- "Western Frisian": "",
- "Xhosa": "",
- "Yiddish": "",
- "Yoruba": "",
- "Zulu": "",
- "`x` years": "`x` år",
- "`x` months": "`x` måneder",
- "`x` weeks": "`x` uker",
- "`x` days": "`x` dager",
- "`x` hours": "`x` timer",
- "`x` minutes": "`x` minutter",
- "`x` seconds": "`x` sekunder",
- "Fallback comments: ": "Tilbakefallskommentarer: ",
- "Popular": "Pupulært",
- "Top": "Topp",
- "About": "Om",
- "Rating: ": "Vurdering: ",
- "Language: ": "Språk: "
+ "`x` subscribers": "`x` abonnenter",
+ "`x` videos": "`x` videoer",
+ "LIVE": "SANNTIDSVISNING",
+ "Shared `x` ago": "Delt for `x` siden",
+ "Unsubscribe": "Opphev abonnement",
+ "Subscribe": "Abonner",
+ "Login to subscribe to `x`": "Logg inn for å abonnere på `x`",
+ "View channel on YouTube": "Vis kanal på YouTube",
+ "newest": "nyeste",
+ "oldest": "eldste",
+ "popular": "populært",
+ "Preview page": "Forhåndsvis side",
+ "Next page": "Neste side",
+ "Clear watch history?": "Tøm visningshistorikk?",
+ "Yes": "Ja",
+ "No": "Nei",
+ "Import and Export Data": "Importer- og eksporter data",
+ "Import": "Importer",
+ "Import Invidious data": "Importer Invidious-data",
+ "Import YouTube subscriptions": "Importer YouTube-abonnenter",
+ "Import FreeTube subscriptions (.db)": "Importer FreeTube-abonnenter (.db)",
+ "Import NewPipe subscriptions (.json)": "Importer NewPipe-abonnenter (.json)",
+ "Import NewPipe data (.zip)": "Importer NewPipe-data (.zip)",
+ "Export": "Eksporter",
+ "Export subscriptions as OPML": "Eksporter abonnenter som OPML",
+ "Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporter abonnenter som OPML (for NewPipe og FreeTube)",
+ "Export data as JSON": "Eksporter data som JSON",
+ "Delete account?": "Slett konto?",
+ "History": "Historikk",
+ "Previous page": "Forrige side",
+ "An alternative front-end to YouTube": "En alternativ grenseflate for YouTube",
+ "JavaScript license information": "JavaScript-lisensinformasjon",
+ "source": "kilde",
+ "Login": "Logg inn",
+ "Login/Register": "Logg inn/registrer",
+ "Login to Google": "Logg inn med Google",
+ "User ID:": "Bruker-ID:",
+ "Password:": "Passord:",
+ "Time (h:mm:ss):": "Tid (h:mm:ss):",
+ "Text CAPTCHA": "Tekst-CAPTCHA",
+ "Image CAPTCHA": "Bilde-CAPTCHA",
+ "Sign In": "Innlogging",
+ "Register": "Registrer",
+ "Email:": "E-post:",
+ "Google verification code:": "Google-bekreftelseskode:",
+ "Preferences": "Innstillinger",
+ "Player preferences": "Avspillerinnstillinger",
+ "Always loop: ": "Alltid gjenta: ",
+ "Autoplay: ": "Autoavspilling: ",
+ "Autoplay next video: ": "Autospill neste video: ",
+ "Listen by default: ": "Lytt som forvalg: ",
+ "Default speed: ": "Forvalgt hastighet: ",
+ "Preferred video quality: ": "Foretrukket videokvalitet: ",
+ "Player volume: ": "Avspillerlydstyrke: ",
+ "Default comments: ": "Forvalgte kommentarer: ",
+ "Default captions: ": "Forvalgte undertitler: ",
+ "Fallback captions: ": "Tilbakefallsundertitler: ",
+ "Show related videos? ": "Vis relaterte videoer? ",
+ "Visual preferences": "Visuelle innstillinger",
+ "Dark mode: ": "Mørk drakt: ",
+ "Thin mode: ": "Tynt modus: ",
+ "Subscription preferences": "Abonnementsinnstillinger",
+ "Redirect homepage to feed: ": "Videresend hjemmeside til flyt: ",
+ "Number of videos shown in feed: ": "Antall videoer å vise i flyt: ",
+ "Sort videos by: ": "Sorter videoer etter: ",
+ "published": "publisert",
+ "published - reverse": "publisert - motsatt",
+ "alphabetically": "alfabetisk",
+ "alphabetically - reverse": "alfabetisk - motsatt",
+ "channel name": "kanalnavn",
+ "channel name - reverse": "kanalnavn - motsatt",
+ "Only show latest video from channel: ": "Kun vis siste video fra kanal: ",
+ "Only show latest unwatched video from channel: ": "Kun vis siste usette video fra kanal: ",
+ "Only show unwatched: ": "Kun vis usette: ",
+ "Only show notifications (if there are any): ": "Kun vis merknader (hvis det er noen): ",
+ "Data preferences": "Datainnstillinger",
+ "Clear watch history": "Tøm visningshistorikk",
+ "Import/Export data": "Importer/eksporter data",
+ "Manage subscriptions": "Behandle abonnementer",
+ "Watch history": "Visningshistorikk",
+ "Delete account": "Slett konto",
+ "Save preferences": "Lagre innstillinger",
+ "Subscription manager": "Abonnementsbehandler",
+ "`x` subscriptions": "`x` abonnementer",
+ "Import/Export": "Importer/eksporter",
+ "unsubscribe": "opphev abonnement",
+ "Subscriptions": "Abonnement",
+ "`x` unseen notifications": "`x` usette merknader",
+ "search": "søk",
+ "Sign out": "Logg ut",
+ "Released under the AGPLv3 by Omar Roth.": "Utgitt med AGPLv3+lisens av Omar Roth.",
+ "Source available here.": "Kildekode tilgjengelig her.",
+ "View JavaScript license information.": "Vis JavaScript-lisensinfo.",
+ "Trending": "Trendsettende",
+ "Watch video on Youtube": "Vis video på YouTube",
+ "Genre: ": "Sjanger: ",
+ "License: ": "Lisens: ",
+ "Family friendly? ": "Familievennlig? ",
+ "Wilson score: ": "Wilson-poengsum: ",
+ "Engagement: ": "Engasjement: ",
+ "Whitelisted regions: ": "Hvitlistede regioner: ",
+ "Blacklisted regions: ": "Svartelistede regioner: ",
+ "Shared `x`": "Delt `x`",
+ "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hei. Det ser ut til at du har JavaScript avslått. Klikk her for å vise kommentarer, ha i minnet at innlasting tar lengre tid.",
+ "View YouTube comments": "Vis YouTube-kommentarer",
+ "View more comments on Reddit": "Vis flere kommenterer på Reddit",
+ "View `x` comments": "Vis `x` kommentarer",
+ "View Reddit comments": "Vis Reddit-kommentarer",
+ "Hide replies": "Skjul svar",
+ "Show replies": "Vis svar",
+ "Incorrect password": "Feil passord",
+ "Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer",
+ "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.",
+ "Invalid TFA code": "Ugyldig tofaktorkode",
+ "Login failed. This may be because two-factor authentication is not enabled on your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.",
+ "Invalid answer": "Ugyldig svar",
+ "Invalid CAPTCHA": "Ugyldig CAPTCHA",
+ "CAPTCHA is a required field": "CAPTCHA er et påkrevd felt",
+ "User ID is a required field": "Bruker-ID er et påkrevd felt",
+ "Password is a required field": "Passord er et påkrevd felt",
+ "Invalid username or password": "Ugyldig brukernavn eller passord",
+ "Please sign in using 'Sign in with Google'": "Logg inn ved bruk av \"Google-innlogging\"",
+ "Password cannot be empty": "Passordet kan ikke være tomt",
+ "Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn",
+ "Please sign in": "Logg inn",
+ "Invidious Private Feed for `x`": "Ugyldig privat flyt for `x`",
+ "channel:`x`": "kanal `x`",
+ "Deleted or invalid channel": "Slettet eller ugyldig kanal",
+ "This channel does not exist.": "Denne kanalen finnes ikke.",
+ "Could not get channel info.": "Kunne ikke innhente kanalinfo.",
+ "Could not fetch comments": "Kunne ikke hente kommentarer",
+ "View `x` replies": "Vis `x` svar",
+ "`x` ago": "`x` siden",
+ "Load more": "Last inn flere",
+ "`x` points": "`x` poeng",
+ "Could not create mix.": "Kunne ikke opprette miks.",
+ "Playlist is empty": "Spillelisten er tom",
+ "Invalid playlist.": "Ugyldig spilleliste.",
+ "Playlist does not exist.": "Spillelisten finnes ikke.",
+ "Could not pull trending pages.": "Kunne ikke hente trendsettende sider.",
+ "Hidden field \"challenge\" is a required field": "Skjult felt \"utfordring\" er et påkrevd felt",
+ "Hidden field \"token\" is a required field": "Skjult felt \"symbol\" er et påkrevd felt",
+ "Invalid challenge": "Ugyldig utfordring",
+ "Invalid token": "Ugyldig symbol",
+ "Invalid user": "Ugyldig bruker",
+ "Token is expired, please try again": "Symbol utløpt, prøv igjen",
+ "English": "Engelsk",
+ "English (auto-generated)": "Engelsk (auto-generert)",
+ "Afrikaans": "",
+ "Albanian": "Albansk",
+ "Amharic": "",
+ "Arabic": "Arabisk",
+ "Armenian": "Armensk",
+ "Azerbaijani": "",
+ "Bangla": "",
+ "Basque": "",
+ "Belarusian": "Hviterussisk",
+ "Bosnian": "Bosnisk",
+ "Bulgarian": "Bulgarsk",
+ "Burmese": "Burmesisk",
+ "Catalan": "Katalansk",
+ "Cebuano": "",
+ "Chinese (Simplified)": "",
+ "Chinese (Traditional)": "",
+ "Corsican": "",
+ "Croatian": "",
+ "Czech": "Tsjekkisk",
+ "Danish": "Dansk",
+ "Dutch": "",
+ "Esperanto": "Esperanto",
+ "Estonian": "",
+ "Filipino": "",
+ "Finnish": "Finsk",
+ "French": "Fransk",
+ "Galician": "",
+ "Georgian": "",
+ "German": "",
+ "Greek": "",
+ "Gujarati": "",
+ "Haitian Creole": "",
+ "Hausa": "",
+ "Hawaiian": "",
+ "Hebrew": "",
+ "Hindi": "",
+ "Hmong": "",
+ "Hungarian": "Ungarsk",
+ "Icelandic": "Islandsk",
+ "Igbo": "",
+ "Indonesian": "Indonesisk",
+ "Irish": "Irsk",
+ "Italian": "Italiensk",
+ "Japanese": "Japansk",
+ "Javanese": "",
+ "Kannada": "",
+ "Kazakh": "",
+ "Khmer": "",
+ "Korean": "",
+ "Kurdish": "",
+ "Kyrgyz": "",
+ "Lao": "",
+ "Latin": "",
+ "Latvian": "",
+ "Lithuanian": "",
+ "Luxembourgish": "",
+ "Macedonian": "",
+ "Malagasy": "",
+ "Malay": "",
+ "Malayalam": "",
+ "Maltese": "",
+ "Maori": "",
+ "Marathi": "",
+ "Mongolian": "",
+ "Nepali": "",
+ "Norwegian": "Norsk bokmål",
+ "Nyanja": "",
+ "Pashto": "",
+ "Persian": "",
+ "Polish": "",
+ "Portuguese": "",
+ "Punjabi": "",
+ "Romanian": "",
+ "Russian": "Russisk",
+ "Samoan": "",
+ "Scottish Gaelic": "",
+ "Serbian": "Serbisk",
+ "Shona": "",
+ "Sindhi": "",
+ "Sinhala": "",
+ "Slovak": "Slovakisk",
+ "Slovenian": "Slovensk",
+ "Somali": "Somali",
+ "Southern Sotho": "",
+ "Spanish": "Spansk",
+ "Spanish (Latin America)": "",
+ "Sundanese": "",
+ "Swahili": "",
+ "Swedish": "Svensk",
+ "Tajik": "",
+ "Tamil": "",
+ "Telugu": "",
+ "Thai": "",
+ "Turkish": "Tyrkisk",
+ "Ukrainian": "Ukrainsk",
+ "Urdu": "",
+ "Uzbek": "",
+ "Vietnamese": "Vietnamesisk",
+ "Welsh": "",
+ "Western Frisian": "",
+ "Xhosa": "",
+ "Yiddish": "",
+ "Yoruba": "",
+ "Zulu": "",
+ "`x` years": "`x` år",
+ "`x` months": "`x` måneder",
+ "`x` weeks": "`x` uker",
+ "`x` days": "`x` dager",
+ "`x` hours": "`x` timer",
+ "`x` minutes": "`x` minutter",
+ "`x` seconds": "`x` sekunder",
+ "Fallback comments: ": "Tilbakefallskommentarer: ",
+ "Popular": "Pupulært",
+ "Top": "Topp",
+ "About": "Om",
+ "Rating: ": "Vurdering: ",
+ "Language: ": "Språk: ",
+ "Default": "",
+ "Music": "",
+ "Gaming": "",
+ "News": "",
+ "Movies": "",
+ "Download": "",
+ "Download as: ": ""
}
diff --git a/locales/nl.json b/locales/nl.json
index b7317e5f..de681e71 100644
--- a/locales/nl.json
+++ b/locales/nl.json
@@ -263,5 +263,12 @@
"Top": "",
"About": "",
"Rating: ": "",
- "Language: ": ""
+ "Language: ": "",
+ "Default": "",
+ "Music": "",
+ "Gaming": "",
+ "News": "",
+ "Movies": "",
+ "Download": "",
+ "Download as: ": ""
}
diff --git a/locales/pl.json b/locales/pl.json
index d0686c72..497e098d 100644
--- a/locales/pl.json
+++ b/locales/pl.json
@@ -263,5 +263,12 @@
"Top": "",
"About": "",
"Rating: ": "",
- "Language: ": ""
+ "Language: ": "",
+ "Default": "",
+ "Music": "",
+ "Gaming": "",
+ "News": "",
+ "Movies": "",
+ "Download": "",
+ "Download as: ": ""
}
diff --git a/locales/ru.json b/locales/ru.json
index e7b659b7..c55c63d8 100644
--- a/locales/ru.json
+++ b/locales/ru.json
@@ -1,273 +1,280 @@
{
- "`x` subscribers": "`x` подписчиков",
- "`x` videos": "`x` видео",
- "LIVE": "ПРЯМОЙ ЭФИР",
- "Shared `x` ago": "Опубликовано `x` назад",
- "Unsubscribe": "Отписаться",
- "Subscribe": "Подписаться",
- "Login to subscribe to `x`": "Войти, чтобы подписаться на `x`",
- "View channel on YouTube": "Канал на YouTube",
- "newest": "новые",
- "oldest": "старые",
- "popular": "популярные",
- "Preview page": "Предварительный просмотр",
- "Next page": "Следующая страница",
- "Clear watch history?": "Очистить историю просмотров?",
- "Yes": "Да",
- "No": "Нет",
- "Import and Export Data": "Импорт и экспорт данных",
- "Import": "Импорт",
- "Import Invidious data": "Импортировать данные Invidious",
- "Import YouTube subscriptions": "Импортировать YouTube подписки",
- "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 subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в OPML (для NewPipe и FreeTube)",
- "Export data as JSON": "Экспортировать данные в JSON",
- "Delete account?": "Удалить аккаунт?",
- "History": "История",
- "Previous page": "Предыдущая страница",
- "An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube",
- "JavaScript license information": "Лицензии JavaScript",
- "source": "источник",
- "Login": "Войти",
- "Login/Register": "Войти/Регистрация",
- "Login to Google": "Войти через Google",
- "User ID:": "ID пользователя:",
- "Password:": "Пароль:",
- "Time (h:mm:ss):": "Время (ч:мм:сс):",
- "Text CAPTCHA": "Текст капчи",
- "Image CAPTCHA": "Изображение капчи",
- "Sign In": "Войти",
- "Register": "Регистрация",
- "Email:": "Эл. почта:",
- "Google verification code:": "Код подтверждения Google:",
- "Preferences": "Настройки",
- "Player preferences": "Настройки проигрывателя",
- "Always loop: ": "Всегда повторять: ",
- "Autoplay: ": "Автовоспроизведение: ",
- "Autoplay next video: ": "Автовоспроизведение следующего видео: ",
- "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ",
- "Default speed: ": "Скорость по-умолчанию: ",
- "Preferred video quality: ": "Предпочтительное качество видео: ",
- "Player volume: ": "Громкость воспроизведения: ",
- "Default comments: ": "Источник комментариев: ",
- "youtube": "YouTube",
- "reddit": "Reddit",
- "Default captions: ": "Субтитры по-умолчанию: ",
- "Fallback captions: ": "Резервные субтитры: ",
- "Show related videos? ": "Показывать похожие видео? ",
- "Visual preferences": "Визуальные настройки",
- "Dark mode: ": "Темная тема: ",
- "Thin mode: ": "Облегченный режим: ",
- "Subscription preferences": "Настройки подписок",
- "Redirect homepage to feed: ": "Отображать ленту вместо главной страницы: ",
- "Number of videos shown in feed: ": "Число видео в ленте: ",
- "Sort videos by: ": "Сортировать видео по: ",
- "published": "дате публикации",
- "published - reverse": "дате - обратный порядок",
- "alphabetically": "алфавиту",
- "alphabetically - reverse": "алфавиту - обратный порядок",
- "channel name": "имени канала",
- "channel name - reverse": "имени канала - обратный порядок",
- "Only show latest video from channel: ": "Отображать только последние видео с каждого канала: ",
- "Only show latest unwatched video from channel: ": "Отображать только непросмотренные видео с каждого канала: ",
- "Only show unwatched: ": "Отображать только непросмотренные видео: ",
- "Only show notifications (if there are any): ": "Отображать только оповещения (если есть): ",
- "Data preferences": "Настройки данных",
- "Clear watch history": "Очистить историю просмотра",
- "Import/Export data": "Импорт/Экспорт данных",
- "Manage subscriptions": "Управление подписками",
- "Watch history": "История просмотров",
- "Delete account": "Удалить аккаунт",
- "Save preferences": "Сохранить настройки",
- "Subscription manager": "Менеджер подписок",
- "`x` subscriptions": "`x` подписок",
- "Import/Export": "Импорт/Экспорт",
- "unsubscribe": "отписаться",
- "Subscriptions": "Подписки",
- "`x` unseen notifications": "`x` новых оповещений",
- "search": "поиск",
- "Sign out": "Выйти",
- "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.",
- "Source available here.": "Исходный код доступен здесь.",
- "Liberapay: ": "Liberapay: ",
- "Patreon: ": "Patreon: ",
- "BTC: ": "BTC: ",
- "BCH: ": "BCH: ",
- "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.",
- "Trending": "В тренде",
- "Watch video on Youtube": "Смотреть на YouTube",
- "Genre: ": "Жанр: ",
- "License: ": "Лицензия: ",
- "Family friendly? ": "Семейный просмотр: ",
- "Wilson score: ": "Рейтинг Вильсона: ",
- "Engagement: ": "Вовлеченность: ",
- "Whitelisted regions: ": "Доступно для: ",
- "Blacklisted regions: ": "Недоступно для: ",
- "Shared `x`": "Опубликовано `x`",
- "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Похоже, что у Вас отключен JavaScript. Нажмите сюда, чтобы увидеть комментарии (учтите, что они могут загружаться дольше).",
- "View YouTube comments": "Смотреть комментарии с YouTube",
- "View more comments on Reddit": "Больше комментариев на Reddit",
- "View `x` comments": "Показать `x` комментариев",
- "View Reddit comments": "Смотреть комментарии с Reddit",
- "Hide replies": "Скрыть ответы",
- "Show replies": "Показать ответы",
- "Incorrect password": "Неправильный пароль",
- "Quota exceeded, try again in a few hours": "Превышена квота, попробуйте снова через несколько часов",
- "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Вход не выполнен, проверьте, не включена ли двухфакторная аутентификация.",
- "Invalid TFA code": "Неправильный TFA код",
- "Login failed. This may be because two-factor authentication is not enabled on your account.": "Не удалось войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.",
- "Invalid answer": "Неверный ответ",
- "Invalid CAPTCHA": "Неверная капча",
- "CAPTCHA is a required field": "Необходимо ввести капчу",
- "User ID is a required field": "Необходимо ввести идентификатор пользователя",
- "Password is a required field": "Необходимо ввести пароль",
- "Invalid username or password": "Недопустимый пароль или имя пользователя",
- "Please sign in using 'Sign in with Google'": "Пожалуйста войдите через Google",
- "Password cannot be empty": "Пароль не может быть пустым",
- "Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов",
- "Please sign in": "Пожалуйста, войдите",
- "Invidious Private Feed for `x`": "Приватная лента Invidious для `x`",
- "channel:`x`": "канал: `x`",
- "Deleted or invalid channel": "Канал удален или не найден",
- "This channel does not exist.": "Такой канал не существует.",
- "Could not get channel info.": "Невозможно получить информацию о канале.",
- "Could not fetch comments": "Невозможно получить комментарии",
- "View `x` replies": "Показать `x` ответов",
- "`x` ago": "`x` назад",
- "Load more": "Загрузить больше",
- "`x` points": "`x` очков",
- "Could not create mix.": "Невозможно создать \"микс\".",
- "Playlist is empty": "Плейлист пуст",
- "Invalid playlist.": "Некорректный плейлист.",
- "Playlist does not exist.": "Плейлист не существует.",
- "Could not pull trending pages.": "Невозможно получить страницы \"в тренде\".",
- "Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле \"challenge\"",
- "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле \"токен\"",
- "Invalid challenge": "Неправильный ответ в \"challenge\"",
- "Invalid token": "Неправильный токен",
- "Invalid user": "Недопустимое имя пользователя",
- "Token is expired, please try again": "Срок действия токена истек, попробуйте позже",
- "English": "Английский",
- "English (auto-generated)": "Английский (созданы автоматически)",
- "Afrikaans": "",
- "Albanian": "",
- "Amharic": "",
- "Arabic": "",
- "Armenian": "",
- "Azerbaijani": "",
- "Bangla": "",
- "Basque": "",
- "Belarusian": "",
- "Bosnian": "",
- "Bulgarian": "",
- "Burmese": "",
- "Catalan": "",
- "Cebuano": "",
- "Chinese (Simplified)": "",
- "Chinese (Traditional)": "",
- "Corsican": "",
- "Croatian": "",
- "Czech": "",
- "Danish": "",
- "Dutch": "",
- "Esperanto": "",
- "Estonian": "",
- "Filipino": "",
- "Finnish": "",
- "French": "",
- "Galician": "",
- "Georgian": "",
- "German": "",
- "Greek": "",
- "Gujarati": "",
- "Haitian Creole": "",
- "Hausa": "",
- "Hawaiian": "",
- "Hebrew": "",
- "Hindi": "",
- "Hmong": "",
- "Hungarian": "",
- "Icelandic": "",
- "Igbo": "",
- "Indonesian": "",
- "Irish": "",
- "Italian": "",
- "Japanese": "",
- "Javanese": "",
- "Kannada": "",
- "Kazakh": "",
- "Khmer": "",
- "Korean": "",
- "Kurdish": "",
- "Kyrgyz": "",
- "Lao": "",
- "Latin": "",
- "Latvian": "",
- "Lithuanian": "",
- "Luxembourgish": "",
- "Macedonian": "",
- "Malagasy": "",
- "Malay": "",
- "Malayalam": "",
- "Maltese": "",
- "Maori": "",
- "Marathi": "",
- "Mongolian": "",
- "Nepali": "",
- "Norwegian": "",
- "Nyanja": "",
- "Pashto": "",
- "Persian": "",
- "Polish": "",
- "Portuguese": "",
- "Punjabi": "",
- "Romanian": "",
- "Russian": "",
- "Samoan": "",
- "Scottish Gaelic": "",
- "Serbian": "",
- "Shona": "",
- "Sindhi": "",
- "Sinhala": "",
- "Slovak": "",
- "Slovenian": "",
- "Somali": "",
- "Southern Sotho": "",
- "Spanish": "",
- "Spanish (Latin America)": "",
- "Sundanese": "",
- "Swahili": "",
- "Swedish": "",
- "Tajik": "",
- "Tamil": "",
- "Telugu": "",
- "Thai": "",
- "Turkish": "",
- "Ukrainian": "",
- "Urdu": "",
- "Uzbek": "",
- "Vietnamese": "",
- "Welsh": "",
- "Western Frisian": "",
- "Xhosa": "",
- "Yiddish": "",
- "Yoruba": "",
- "Zulu": "",
- "`x` years": "`x` лет",
- "`x` months": "`x` месяцев",
- "`x` weeks": "`x` недель",
- "`x` days": "`x` дней",
- "`x` hours": "`x` часов",
- "`x` minutes": "`x` минут",
- "`x` seconds": "`x` секунд",
- "Fallback comments: ": "Резервные комментарии: ",
- "Popular": "Популярное",
- "Top": "Топ",
- "About": "О сайте",
- "Rating: ": "Рейтинг: ",
- "Language: ": "Язык: "
+ "`x` subscribers": "`x` подписчиков",
+ "`x` videos": "`x` видео",
+ "LIVE": "ПРЯМОЙ ЭФИР",
+ "Shared `x` ago": "Опубликовано `x` назад",
+ "Unsubscribe": "Отписаться",
+ "Subscribe": "Подписаться",
+ "Login to subscribe to `x`": "Войти, чтобы подписаться на `x`",
+ "View channel on YouTube": "Канал на YouTube",
+ "newest": "новые",
+ "oldest": "старые",
+ "popular": "популярные",
+ "Preview page": "Предварительный просмотр",
+ "Next page": "Следующая страница",
+ "Clear watch history?": "Очистить историю просмотров?",
+ "Yes": "Да",
+ "No": "Нет",
+ "Import and Export Data": "Импорт и экспорт данных",
+ "Import": "Импорт",
+ "Import Invidious data": "Импортировать данные Invidious",
+ "Import YouTube subscriptions": "Импортировать YouTube подписки",
+ "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 subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в OPML (для NewPipe и FreeTube)",
+ "Export data as JSON": "Экспортировать данные в JSON",
+ "Delete account?": "Удалить аккаунт?",
+ "History": "История",
+ "Previous page": "Предыдущая страница",
+ "An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube",
+ "JavaScript license information": "Лицензии JavaScript",
+ "source": "источник",
+ "Login": "Войти",
+ "Login/Register": "Войти/Регистрация",
+ "Login to Google": "Войти через Google",
+ "User ID:": "ID пользователя:",
+ "Password:": "Пароль:",
+ "Time (h:mm:ss):": "Время (ч:мм:сс):",
+ "Text CAPTCHA": "Текст капчи",
+ "Image CAPTCHA": "Изображение капчи",
+ "Sign In": "Войти",
+ "Register": "Регистрация",
+ "Email:": "Эл. почта:",
+ "Google verification code:": "Код подтверждения Google:",
+ "Preferences": "Настройки",
+ "Player preferences": "Настройки проигрывателя",
+ "Always loop: ": "Всегда повторять: ",
+ "Autoplay: ": "Автовоспроизведение: ",
+ "Autoplay next video: ": "Автовоспроизведение следующего видео: ",
+ "Listen by default: ": "Режим \"только аудио\" по-умолчанию: ",
+ "Default speed: ": "Скорость по-умолчанию: ",
+ "Preferred video quality: ": "Предпочтительное качество видео: ",
+ "Player volume: ": "Громкость воспроизведения: ",
+ "Default comments: ": "Источник комментариев: ",
+ "youtube": "YouTube",
+ "reddit": "Reddit",
+ "Default captions: ": "Субтитры по-умолчанию: ",
+ "Fallback captions: ": "Резервные субтитры: ",
+ "Show related videos? ": "Показывать похожие видео? ",
+ "Visual preferences": "Визуальные настройки",
+ "Dark mode: ": "Темная тема: ",
+ "Thin mode: ": "Облегченный режим: ",
+ "Subscription preferences": "Настройки подписок",
+ "Redirect homepage to feed: ": "Отображать ленту вместо главной страницы: ",
+ "Number of videos shown in feed: ": "Число видео в ленте: ",
+ "Sort videos by: ": "Сортировать видео по: ",
+ "published": "дате публикации",
+ "published - reverse": "дате - обратный порядок",
+ "alphabetically": "алфавиту",
+ "alphabetically - reverse": "алфавиту - обратный порядок",
+ "channel name": "имени канала",
+ "channel name - reverse": "имени канала - обратный порядок",
+ "Only show latest video from channel: ": "Отображать только последние видео с каждого канала: ",
+ "Only show latest unwatched video from channel: ": "Отображать только непросмотренные видео с каждого канала: ",
+ "Only show unwatched: ": "Отображать только непросмотренные видео: ",
+ "Only show notifications (if there are any): ": "Отображать только оповещения (если есть): ",
+ "Data preferences": "Настройки данных",
+ "Clear watch history": "Очистить историю просмотра",
+ "Import/Export data": "Импорт/Экспорт данных",
+ "Manage subscriptions": "Управление подписками",
+ "Watch history": "История просмотров",
+ "Delete account": "Удалить аккаунт",
+ "Save preferences": "Сохранить настройки",
+ "Subscription manager": "Менеджер подписок",
+ "`x` subscriptions": "`x` подписок",
+ "Import/Export": "Импорт/Экспорт",
+ "unsubscribe": "отписаться",
+ "Subscriptions": "Подписки",
+ "`x` unseen notifications": "`x` новых оповещений",
+ "search": "поиск",
+ "Sign out": "Выйти",
+ "Released under the AGPLv3 by Omar Roth.": "Распространяется Omar Roth по AGPLv3.",
+ "Source available here.": "Исходный код доступен здесь.",
+ "Liberapay: ": "Liberapay: ",
+ "Patreon: ": "Patreon: ",
+ "BTC: ": "BTC: ",
+ "BCH: ": "BCH: ",
+ "View JavaScript license information.": "Посмотреть лицензии JavaScript кода.",
+ "Trending": "В тренде",
+ "Watch video on Youtube": "Смотреть на YouTube",
+ "Genre: ": "Жанр: ",
+ "License: ": "Лицензия: ",
+ "Family friendly? ": "Семейный просмотр: ",
+ "Wilson score: ": "Рейтинг Вильсона: ",
+ "Engagement: ": "Вовлеченность: ",
+ "Whitelisted regions: ": "Доступно для: ",
+ "Blacklisted regions: ": "Недоступно для: ",
+ "Shared `x`": "Опубликовано `x`",
+ "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Похоже, что у Вас отключен JavaScript. Нажмите сюда, чтобы увидеть комментарии (учтите, что они могут загружаться дольше).",
+ "View YouTube comments": "Смотреть комментарии с YouTube",
+ "View more comments on Reddit": "Больше комментариев на Reddit",
+ "View `x` comments": "Показать `x` комментариев",
+ "View Reddit comments": "Смотреть комментарии с Reddit",
+ "Hide replies": "Скрыть ответы",
+ "Show replies": "Показать ответы",
+ "Incorrect password": "Неправильный пароль",
+ "Quota exceeded, try again in a few hours": "Превышена квота, попробуйте снова через несколько часов",
+ "Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Вход не выполнен, проверьте, не включена ли двухфакторная аутентификация.",
+ "Invalid TFA code": "Неправильный TFA код",
+ "Login failed. This may be because two-factor authentication is not enabled on your account.": "Не удалось войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.",
+ "Invalid answer": "Неверный ответ",
+ "Invalid CAPTCHA": "Неверная капча",
+ "CAPTCHA is a required field": "Необходимо ввести капчу",
+ "User ID is a required field": "Необходимо ввести идентификатор пользователя",
+ "Password is a required field": "Необходимо ввести пароль",
+ "Invalid username or password": "Недопустимый пароль или имя пользователя",
+ "Please sign in using 'Sign in with Google'": "Пожалуйста войдите через Google",
+ "Password cannot be empty": "Пароль не может быть пустым",
+ "Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов",
+ "Please sign in": "Пожалуйста, войдите",
+ "Invidious Private Feed for `x`": "Приватная лента Invidious для `x`",
+ "channel:`x`": "канал: `x`",
+ "Deleted or invalid channel": "Канал удален или не найден",
+ "This channel does not exist.": "Такой канал не существует.",
+ "Could not get channel info.": "Невозможно получить информацию о канале.",
+ "Could not fetch comments": "Невозможно получить комментарии",
+ "View `x` replies": "Показать `x` ответов",
+ "`x` ago": "`x` назад",
+ "Load more": "Загрузить больше",
+ "`x` points": "`x` очков",
+ "Could not create mix.": "Невозможно создать \"микс\".",
+ "Playlist is empty": "Плейлист пуст",
+ "Invalid playlist.": "Некорректный плейлист.",
+ "Playlist does not exist.": "Плейлист не существует.",
+ "Could not pull trending pages.": "Невозможно получить страницы \"в тренде\".",
+ "Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле \"challenge\"",
+ "Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле \"токен\"",
+ "Invalid challenge": "Неправильный ответ в \"challenge\"",
+ "Invalid token": "Неправильный токен",
+ "Invalid user": "Недопустимое имя пользователя",
+ "Token is expired, please try again": "Срок действия токена истек, попробуйте позже",
+ "English": "Английский",
+ "English (auto-generated)": "Английский (созданы автоматически)",
+ "Afrikaans": "",
+ "Albanian": "",
+ "Amharic": "",
+ "Arabic": "",
+ "Armenian": "",
+ "Azerbaijani": "",
+ "Bangla": "",
+ "Basque": "",
+ "Belarusian": "",
+ "Bosnian": "",
+ "Bulgarian": "",
+ "Burmese": "",
+ "Catalan": "",
+ "Cebuano": "",
+ "Chinese (Simplified)": "",
+ "Chinese (Traditional)": "",
+ "Corsican": "",
+ "Croatian": "",
+ "Czech": "",
+ "Danish": "",
+ "Dutch": "",
+ "Esperanto": "",
+ "Estonian": "",
+ "Filipino": "",
+ "Finnish": "",
+ "French": "",
+ "Galician": "",
+ "Georgian": "",
+ "German": "",
+ "Greek": "",
+ "Gujarati": "",
+ "Haitian Creole": "",
+ "Hausa": "",
+ "Hawaiian": "",
+ "Hebrew": "",
+ "Hindi": "",
+ "Hmong": "",
+ "Hungarian": "",
+ "Icelandic": "",
+ "Igbo": "",
+ "Indonesian": "",
+ "Irish": "",
+ "Italian": "",
+ "Japanese": "",
+ "Javanese": "",
+ "Kannada": "",
+ "Kazakh": "",
+ "Khmer": "",
+ "Korean": "",
+ "Kurdish": "",
+ "Kyrgyz": "",
+ "Lao": "",
+ "Latin": "",
+ "Latvian": "",
+ "Lithuanian": "",
+ "Luxembourgish": "",
+ "Macedonian": "",
+ "Malagasy": "",
+ "Malay": "",
+ "Malayalam": "",
+ "Maltese": "",
+ "Maori": "",
+ "Marathi": "",
+ "Mongolian": "",
+ "Nepali": "",
+ "Norwegian": "",
+ "Nyanja": "",
+ "Pashto": "",
+ "Persian": "",
+ "Polish": "",
+ "Portuguese": "",
+ "Punjabi": "",
+ "Romanian": "",
+ "Russian": "",
+ "Samoan": "",
+ "Scottish Gaelic": "",
+ "Serbian": "",
+ "Shona": "",
+ "Sindhi": "",
+ "Sinhala": "",
+ "Slovak": "",
+ "Slovenian": "",
+ "Somali": "",
+ "Southern Sotho": "",
+ "Spanish": "",
+ "Spanish (Latin America)": "",
+ "Sundanese": "",
+ "Swahili": "",
+ "Swedish": "",
+ "Tajik": "",
+ "Tamil": "",
+ "Telugu": "",
+ "Thai": "",
+ "Turkish": "",
+ "Ukrainian": "",
+ "Urdu": "",
+ "Uzbek": "",
+ "Vietnamese": "",
+ "Welsh": "",
+ "Western Frisian": "",
+ "Xhosa": "",
+ "Yiddish": "",
+ "Yoruba": "",
+ "Zulu": "",
+ "`x` years": "`x` лет",
+ "`x` months": "`x` месяцев",
+ "`x` weeks": "`x` недель",
+ "`x` days": "`x` дней",
+ "`x` hours": "`x` часов",
+ "`x` minutes": "`x` минут",
+ "`x` seconds": "`x` секунд",
+ "Fallback comments: ": "Резервные комментарии: ",
+ "Popular": "Популярное",
+ "Top": "Топ",
+ "About": "О сайте",
+ "Rating: ": "Рейтинг: ",
+ "Language: ": "Язык: ",
+ "Default": "",
+ "Music": "",
+ "Gaming": "",
+ "News": "",
+ "Movies": "",
+ "Download": "",
+ "Download as: ": ""
}
diff --git a/src/invidious.cr b/src/invidious.cr
index 4bde39ef..1a96b50a 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -16,6 +16,7 @@
require "detect_language"
require "digest/md5"
+require "file_utils"
require "kemal"
require "openssl/hmac"
require "option_parser"
@@ -35,6 +36,8 @@ channel_threads = CONFIG.channel_threads
feed_threads = CONFIG.feed_threads
video_threads = CONFIG.video_threads
+logger = Invidious::LogHandler.new
+
Kemal.config.extra_options do |parser|
parser.banner = "Usage: invidious [arguments]"
parser.on("-t THREADS", "--crawl-threads=THREADS", "Number of threads for crawling YouTube (default: #{crawl_threads})") do |number|
@@ -69,6 +72,10 @@ Kemal.config.extra_options do |parser|
exit
end
end
+ parser.on("-o OUTPUT", "--output=OUTPUT", "Redirect output (default: STDOUT)") do |output|
+ FileUtils.mkdir_p(File.dirname(output))
+ logger = Invidious::LogHandler.new(File.open(output, mode: "a"))
+ end
end
Kemal::CLI.new
@@ -101,17 +108,17 @@ LOCALES = {
crawl_threads.times do
spawn do
- crawl_videos(PG_DB)
+ crawl_videos(PG_DB, logger)
end
end
-refresh_channels(PG_DB, channel_threads, CONFIG.full_refresh)
+refresh_channels(PG_DB, logger, channel_threads, CONFIG.full_refresh)
-refresh_feeds(PG_DB, feed_threads)
+refresh_feeds(PG_DB, logger, feed_threads)
video_threads.times do |i|
spawn do
- refresh_videos(PG_DB)
+ refresh_videos(PG_DB, logger)
end
end
@@ -295,7 +302,7 @@ get "/watch" do |env|
next env.redirect "/watch?v=#{ex.message}"
rescue ex
error_message = ex.message
- STDOUT << id << " : " << ex.message << "\n"
+ logger.write("#{id} : #{ex.message}\n")
next templated "error"
end
@@ -2135,6 +2142,16 @@ get "/c/:user" do |env|
env.redirect anchor["href"]
end
+# Legacy endpoint for /user/:username
+get "/profile" do |env|
+ user = env.params.query["user"]?
+ if !user
+ env.redirect "/"
+ else
+ env.redirect "/user/#{user}"
+ end
+end
+
get "/user/:user" do |env|
user = env.params.url["user"]
env.redirect "/channel/#{user}"
@@ -3849,4 +3866,5 @@ add_handler FilteredCompressHandler.new
add_handler DenyFrame.new
add_context_storage_type(User)
+Kemal.config.logger = logger
Kemal.run
diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr
index d5e233b0..91a80203 100644
--- a/src/invidious/helpers/helpers.cr
+++ b/src/invidious/helpers/helpers.cr
@@ -1,21 +1,21 @@
class Config
YAML.mapping({
- crawl_threads: Int32,
- channel_threads: Int32,
- feed_threads: Int32,
- video_threads: Int32,
- db: NamedTuple(
- user: String,
+ crawl_threads: Int32, # Number of threads to use for finding new videos from YouTube (used to populate "top" page)
+ channel_threads: Int32, # Number of threads to use for crawling videos from channels (for updating subscriptions)
+ feed_threads: Int32, # Number of threads to use for updating feeds
+ video_threads: Int32, # Number of threads to use for updating videos in cache (mostly non-functional)
+ db: NamedTuple( # Database configuration
+user: String,
password: String,
host: String,
port: Int32,
dbname: String,
),
- dl_api_key: String?,
- https_only: Bool?,
- hmac_key: String?,
- full_refresh: Bool,
- domain: String,
+ dl_api_key: String?, # DetectLanguage API Key (used to filter non-English results from "top" page), mostly non-functional
+ https_only: Bool?, # Used to tell Invidious it is behind a proxy, so links to resources should be https://
+ hmac_key: String?, # HMAC signing key for CSRF tokens
+ full_refresh: Bool, # Used for crawling channels: threads should check all videos uploaded by a channel
+ domain: String, # Domain to be used for links to resources on the site where an absolute URL is required
})
end
diff --git a/src/invidious/helpers/logger.cr b/src/invidious/helpers/logger.cr
new file mode 100644
index 00000000..5bb1eb40
--- /dev/null
+++ b/src/invidious/helpers/logger.cr
@@ -0,0 +1,35 @@
+require "logger"
+
+class Invidious::LogHandler < Kemal::BaseLogHandler
+ def initialize(@io : IO = STDOUT)
+ end
+
+ def call(context : HTTP::Server::Context)
+ time = Time.now
+ call_next(context)
+ elapsed_text = elapsed_text(Time.now - time)
+
+ @io << time << ' ' << context.response.status_code << ' ' << context.request.method << ' ' << context.request.resource << ' ' << elapsed_text << '\n'
+
+ if @io.is_a? File
+ @io.flush
+ end
+
+ context
+ end
+
+ def write(message : String)
+ @io << message
+
+ if @io.is_a? File
+ @io.flush
+ end
+ end
+
+ private def elapsed_text(elapsed)
+ millis = elapsed.total_milliseconds
+ return "#{millis.round(2)}ms" if millis >= 1
+
+ "#{(millis * 1000).round(2)}µs"
+ end
+end
diff --git a/src/invidious/jobs.cr b/src/invidious/jobs.cr
index df02c7fb..f6e2d8fe 100644
--- a/src/invidious/jobs.cr
+++ b/src/invidious/jobs.cr
@@ -1,4 +1,4 @@
-def crawl_videos(db)
+def crawl_videos(db, logger)
ids = Deque(String).new
random = Random.new
@@ -21,7 +21,7 @@ def crawl_videos(db)
id = ids[0]
video = get_video(id, db)
rescue ex
- STDOUT << id << " : " << ex.message << "\n"
+ logger.write("#{id} : #{ex.message}\n")
next
ensure
ids.delete(id)
@@ -46,7 +46,7 @@ def crawl_videos(db)
end
end
-def refresh_channels(db, max_threads = 1, full_refresh = false)
+def refresh_channels(db, logger, max_threads = 1, full_refresh = false)
max_channel = Channel(Int32).new
spawn do
@@ -73,7 +73,7 @@ def refresh_channels(db, max_threads = 1, full_refresh = false)
db.exec("UPDATE channels SET updated = $1, author = $2 WHERE id = $3", Time.now, channel.author, id)
rescue ex
- STDOUT << id << " : " << ex.message << "\n"
+ logger.write("#{id} : #{ex.message}\n")
end
active_channel.send(true)
@@ -86,7 +86,7 @@ def refresh_channels(db, max_threads = 1, full_refresh = false)
max_channel.send(max_threads)
end
-def refresh_videos(db)
+def refresh_videos(db, logger)
loop do
db.query("SELECT id FROM videos ORDER BY updated") do |rs|
rs.each do
@@ -94,7 +94,7 @@ def refresh_videos(db)
id = rs.read(String)
video = get_video(id, db)
rescue ex
- STDOUT << id << " : " << ex.message << "\n"
+ logger.write("#{id} : #{ex.message}\n")
next
end
end
@@ -104,7 +104,7 @@ def refresh_videos(db)
end
end
-def refresh_feeds(db, max_threads = 1)
+def refresh_feeds(db, logger, max_threads = 1)
max_channel = Channel(Int32).new
spawn do
@@ -129,7 +129,7 @@ def refresh_feeds(db, max_threads = 1)
begin
db.exec("REFRESH MATERIALIZED VIEW #{view_name}")
rescue ex
- STDOUT << "REFRESH " << email << " : " << ex.message << "\n"
+ logger.write("REFRESH #{email} : #{ex.message}\n")
end
active_channel.send(true)
diff --git a/src/invidious/views/components/player_sources.ecr b/src/invidious/views/components/player_sources.ecr
index 3afce6cb..aed606af 100644
--- a/src/invidious/views/components/player_sources.ecr
+++ b/src/invidious/views/components/player_sources.ecr
@@ -8,7 +8,7 @@
<script src="/js/videojs-markers.min.js"></script>
<script src="/js/videojs-share.min.js"></script>
<script src="/js/videojs-http-streaming.min.js"></script>
-<% if env.get?("user") && env.get("user").as(User).preferences.quality == "dash" %>
+<% if params[:quality] == "dash" %>
<script src="/js/dash.mediaplayer.min.js"></script>
<script src="/js/videojs-dash.min.js"></script>
<script src="/js/videojs-contrib-quality-levels.min.js"></script>
diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr
index 832d13d1..d5ab5411 100644
--- a/src/invidious/views/watch.ecr
+++ b/src/invidious/views/watch.ecr
@@ -53,6 +53,34 @@
<div class="pure-u-1 pure-u-md-1-5">
<div class="h-box">
<p><a href="https://www.youtube.com/watch?v=<%= video.id %>"><%= translate(locale, "Watch video on Youtube") %></a></p>
+
+ <form class="pure-form pure-form-stacked">
+ <div class="pure-control-group">
+ <label for="download_widget"><%= translate(locale, "Download as: ") %></label>
+ <select style="width:100%" name="download_widget" id="download_widget">
+ <% video_streams.each do |option| %>
+ <option data-url="<%= option["url"] %>"><%= option["quality_label"] %> - <%= option["type"].split(";")[0] %> @ <%= option["fps"] %>fps - video only</option>
+ <% end %>
+ <% audio_streams.each do |option| %>
+ <option data-url="<%= option["url"] %>"><%= option["type"].split(";")[0] %> @ <%= option["bitrate"] %>k - audio only</option>
+ <% end %>
+ <% fmt_stream.each do |option| %>
+ <option data-url="<%= option["url"] %>"><%= itag_to_metadata?(option["itag"]).try &.["height"]? || "~240" %>p - <%= option["type"].split(";")[0] %></option>
+ <% end %>
+ </select>
+ </div>
+
+ <div id="progress-container" style="width:100%; display:none">
+ <div id="download-progress">
+ </div>
+ </div>
+
+ <button type="button" data-title="<%= video.title.dump_unquoted %>-<%= video.id %>.mp4" onclick="download_video(this)"
+ class="pure-button pure-button-primary">
+ <%= translate(locale, "Download") %>
+ </button>
+ </form>
+
<p><i class="icon ion-ios-eye"></i> <%= number_with_separator(video.views) %></p>
<p><i class="icon ion-ios-thumbs-up"></i> <%= number_with_separator(video.likes) %></p>
<p><i class="icon ion-ios-thumbs-down"></i> <%= number_with_separator(video.dislikes) %></p>
@@ -268,8 +296,15 @@ function unsubscribe() {
}
<% if plid %>
-function get_playlist() {
+function get_playlist(timeouts = 0) {
playlist = document.getElementById("playlist");
+
+ if (timeouts > 10) {
+ console.log("Failed to pull playlist");
+ playlist.innerHTML = "";
+ return;
+ }
+
playlist.innerHTML = ' \
<h3><center class="loading"><i class="icon ion-ios-refresh"></i></center></h3> \
<hr>'
@@ -323,15 +358,22 @@ function get_playlist() {
comments = document.getElementById("playlist");
comments.innerHTML =
'<h3><center class="loading"><i class="icon ion-ios-refresh"></i></center></h3><hr>';
- get_playlist();
+ get_playlist(timeouts + 1);
};
}
get_playlist();
<% end %>
-function get_reddit_comments() {
+function get_reddit_comments(timeouts = 0) {
comments = document.getElementById("comments");
+
+ if (timeouts > 10) {
+ console.log("Failed to pull comments");
+ comments.innerHTML = "";
+ return;
+ }
+
var fallback = comments.innerHTML;
comments.innerHTML =
'<h3><center class="loading"><i class="icon ion-ios-refresh"></i></center></h3>';
@@ -382,12 +424,19 @@ function get_reddit_comments() {
xhr.ontimeout = function() {
console.log("Pulling comments timed out.");
- get_reddit_comments();
+ get_reddit_comments(timeouts + 1);
};
}
-function get_youtube_comments() {
+function get_youtube_comments(timeouts = 0) {
comments = document.getElementById("comments");
+
+ if (timeouts > 10) {
+ console.log("Failed to pull comments");
+ comments.innerHTML = "";
+ return;
+ }
+
var fallback = comments.innerHTML;
comments.innerHTML =
'<h3><center class="loading"><i class="icon ion-ios-refresh"></i></center></h3>';
@@ -438,7 +487,7 @@ function get_youtube_comments() {
comments.innerHTML =
'<h3><center class="loading"><i class="icon ion-ios-refresh"></i></center></h3>';
- get_youtube_comments();
+ get_youtube_comments(timeouts + 1);
};
}