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 b36f8c37..55592d2c 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -275,6 +275,6 @@ "Gaming": "الألعاب", "News": "الأخبار", "Movies": "الأفلام", - "Download as: ": "تحميل كـ", + "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..7f77e317 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 @@ -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/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 @@ -<% if env.get?("user") && env.get("user").as(User).preferences.quality == "dash" %> +<% if params[:quality] == "dash" %> 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 @@

<%= translate(locale, "Watch video on Youtube") %>

+ +
+
+ + +
+ + + + +
+

<%= number_with_separator(video.views) %>

<%= number_with_separator(video.likes) %>

<%= number_with_separator(video.dislikes) %>

@@ -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 = ' \

\
' @@ -323,15 +358,22 @@ function get_playlist() { comments = document.getElementById("playlist"); comments.innerHTML = '


'; - 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 = '

'; @@ -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 = '

'; @@ -438,7 +487,7 @@ function get_youtube_comments() { comments.innerHTML = '

'; - get_youtube_comments(); + get_youtube_comments(timeouts + 1); }; }