mirror of
https://github.com/iv-org/invidious.git
synced 2025-07-31 01:38:31 +00:00
Merge branch 'master' into api-only
This commit is contained in:
commit
3d76898bb9
@ -10,7 +10,7 @@
|
||||
"newest": "الأجدد",
|
||||
"oldest": "الأقدم",
|
||||
"popular": "الاكثر شعبية",
|
||||
"last": "",
|
||||
"last": "اخر الفيديوهات المعدلة",
|
||||
"Next page": "الصفحة الثانية",
|
||||
"Previous page": "الصفحة السابقة",
|
||||
"Clear watch history?": "مسح السجل ؟",
|
||||
@ -50,7 +50,7 @@
|
||||
"Autoplay: ": "تشغيل تلقائى: ",
|
||||
"Autoplay next video: ": "شغل الفيديو التالى تلقائى: ",
|
||||
"Listen by default: ": "تشغيل النسخة السمعية تلقائى: ",
|
||||
"Proxy videos? ": "",
|
||||
"Proxy videos? ": "عرض الفيديوهات عن طريق الوكيل(proxy) ؟",
|
||||
"Default speed: ": "السرعة الإفتراضية: ",
|
||||
"Preferred video quality: ": "الجودة المفضلة للفيديوهات: ",
|
||||
"Player volume: ": "صوت المشغل: ",
|
||||
@ -83,14 +83,14 @@
|
||||
"Manage subscriptions": "إدارة المشتركين",
|
||||
"Watch history": "سجل المشاهدة",
|
||||
"Delete account": "حذف الحساب",
|
||||
"Administrator preferences": "",
|
||||
"Default homepage: ": "",
|
||||
"Feed menu: ": "",
|
||||
"Top enabled? ": "",
|
||||
"CAPTCHA enabled? ": "",
|
||||
"Login enabled? ": "",
|
||||
"Registration enabled? ": "",
|
||||
"Report statistics? ": "",
|
||||
"Administrator preferences": "إعدادات المدير",
|
||||
"Default homepage: ": "الصفحة الرئيسية الافتراضية ",
|
||||
"Feed menu: ": "قائمة التغذية",
|
||||
"Top enabled? ": "تفعيل 'الأفضل' ؟ ",
|
||||
"CAPTCHA enabled? ": "تفعيل الكابتشا ؟",
|
||||
"Login enabled? ": "تفعيل تسجيل الدخول ؟",
|
||||
"Registration enabled? ": "تفعيل التسجيل ؟",
|
||||
"Report statistics? ": "إبلاغ الإحصائيات",
|
||||
"Save preferences": "حفظ التفضيلات",
|
||||
"Subscription manager": "مدير الإشتراكات",
|
||||
"`x` subscriptions": "`x` مشتركين",
|
||||
@ -103,8 +103,9 @@
|
||||
"Released under the AGPLv3 by Omar Roth.": "تم الإنشاء تحت AGPLv3 بواسطة عمر روث.",
|
||||
"Source available here.": "الأكواد متوفرة هنا.",
|
||||
"View JavaScript license information.": "مشاهدة معلومات حول تراخيص الجافاسكريبت.",
|
||||
"View privacy policy.": "",
|
||||
"View privacy policy.": "عرض سياسة الخصوصية",
|
||||
"Trending": "الشائع",
|
||||
"Unlisted": "غير مصنف",
|
||||
"Watch video on Youtube": "مشاهدة الفيديو على اليوتيوب",
|
||||
"Genre: ": "النوع: ",
|
||||
"License: ": "التراخيص: ",
|
||||
@ -114,6 +115,7 @@
|
||||
"Whitelisted regions: ": "الدول المسموح فيها هذا الفيديو: ",
|
||||
"Blacklisted regions: ": "الدول الحظور فيها هذا الفيديو: ",
|
||||
"Shared `x`": "شارك منذ `x`",
|
||||
"Premieres in `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.": "اهلا! يبدو ان الجافاسكريبت معطلة. اضغط هنا لعرض التعليقات, ضع فى إعتبارك انها ستأخذ وقت اطول للعرض.",
|
||||
"View YouTube comments": "عرض تعليقات اليوتيوب",
|
||||
"View more comments on Reddit": "عرض المزيد من التعليقات على\\من موقع Reddit",
|
||||
|
@ -105,6 +105,7 @@
|
||||
"View JavaScript license information.": "Javascript Lizenzinformationen anzeigen.",
|
||||
"View privacy policy.": "",
|
||||
"Trending": "Trending",
|
||||
"Unlisted": "",
|
||||
"Watch video on Youtube": "Video auf YouTube ansehen",
|
||||
"Genre: ": "Genre: ",
|
||||
"License: ": "Lizenz: ",
|
||||
@ -114,6 +115,7 @@
|
||||
"Whitelisted regions: ": "Erlaubte Regionen: ",
|
||||
"Blacklisted regions: ": "Unerlaubte Regionen: ",
|
||||
"Shared `x`": "Geteilt `x`",
|
||||
"Premieres in `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",
|
||||
|
@ -103,6 +103,7 @@
|
||||
"View JavaScript license information.": "View JavaScript license information.",
|
||||
"View privacy policy.": "View privacy policy.",
|
||||
"Trending": "Trending",
|
||||
"Unlisted": "",
|
||||
"Watch video on Youtube": "Watch video on Youtube",
|
||||
"Genre: ": "Genre: ",
|
||||
"License: ": "License: ",
|
||||
@ -112,6 +113,7 @@
|
||||
"Whitelisted regions: ": "Whitelisted regions: ",
|
||||
"Blacklisted regions: ": "Blacklisted regions: ",
|
||||
"Shared `x`": "Shared `x`",
|
||||
"Premieres in `x`": "",
|
||||
"Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.",
|
||||
"View YouTube comments": "View YouTube comments",
|
||||
"View more comments on Reddit": "View more comments on Reddit",
|
||||
|
@ -102,6 +102,7 @@
|
||||
"Source available here.": "",
|
||||
"View JavaScript license information.": "",
|
||||
"View privacy policy.": "",
|
||||
"Unlisted": "",
|
||||
"Trending": "",
|
||||
"Watch video on Youtube": "",
|
||||
"Genre: ": "",
|
||||
@ -112,6 +113,7 @@
|
||||
"Whitelisted regions: ": "",
|
||||
"Blacklisted regions: ": "",
|
||||
"Shared `x`": "",
|
||||
"Premieres in `x`": "",
|
||||
"Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "",
|
||||
"View YouTube comments": "",
|
||||
"View more comments on Reddit": "",
|
||||
|
@ -2,7 +2,7 @@
|
||||
"`x` subscribers": "`x` abonnés",
|
||||
"`x` videos": "`x` vidéos",
|
||||
"LIVE": "EN DIRECT",
|
||||
"Shared `x` ago": "Partagé il y a `x`",
|
||||
"Shared `x` ago": "Ajoutée il y a `x`",
|
||||
"Unsubscribe": "Se désabonner",
|
||||
"Subscribe": "S'abonner",
|
||||
"Login to subscribe to `x`": "Vous devez vous connecter pour vous abonner à `x`",
|
||||
@ -16,7 +16,7 @@
|
||||
"Clear watch history?": "Êtes-vous sûr de vouloir supprimer l'historique des vidéos regardées ?",
|
||||
"Yes": "Oui",
|
||||
"No": "Non",
|
||||
"Import and Export Data": "Importer et Exporter les Données",
|
||||
"Import and Export Data": "Importer et exporter des données",
|
||||
"Import": "Importer",
|
||||
"Import Invidious data": "Importer des données Invidious",
|
||||
"Import YouTube subscriptions": "Importer des abonnements YouTube",
|
||||
@ -32,32 +32,32 @@
|
||||
"An alternative front-end to YouTube": "Un front-end alternatif à YouTube",
|
||||
"JavaScript license information": "Informations sur les licences JavaScript",
|
||||
"source": "source",
|
||||
"Login": "Connexion",
|
||||
"Login/Register": "Connexion/S'inscrire",
|
||||
"Login to Google": "Se connecter à Google",
|
||||
"Login": "Se connecter",
|
||||
"Login/Register": "Se connecter/Créer un compte",
|
||||
"Login to Google": "Se connecter avec Google",
|
||||
"User ID:": "Identifiant utilisateur :",
|
||||
"Password:": "Mot de passe :",
|
||||
"Time (h:mm:ss):": "Heure (h:mm:ss) :",
|
||||
"Text CAPTCHA": "CAPTCHA Texte",
|
||||
"Image CAPTCHA": "CAPTCHA Image",
|
||||
"Sign In": "S'identifier",
|
||||
"Sign In": "Se connecter",
|
||||
"Register": "S'inscrire",
|
||||
"Email:": "E-mail :",
|
||||
"Google verification code:": "Code de vérification Google :",
|
||||
"Preferences": "Préférences",
|
||||
"Player preferences": "Préférences du Lecteur",
|
||||
"Player preferences": "Préférences du lecteur",
|
||||
"Always loop: ": "Lire en boucle : ",
|
||||
"Autoplay: ": "Lire Automatiquement : ",
|
||||
"Autoplay: ": "Lire automatiquement : ",
|
||||
"Autoplay next video: ": "Lire automatiquement la vidéo suivante : ",
|
||||
"Listen by default: ": "Audio Uniquement par défaut : ",
|
||||
"Proxy videos? ": "Souhaitez vous charger les vidéos à travers un proxy ?",
|
||||
"Listen by default: ": "Audio uniquement : ",
|
||||
"Proxy videos? ": "Charger les vidéos à travers un proxy ? ",
|
||||
"Default speed: ": "Vitesse par défaut : ",
|
||||
"Preferred video quality: ": "Qualité vidéo souhaitée : ",
|
||||
"Player volume: ": "Volume du lecteur : ",
|
||||
"Default comments: ": "Source des Commentaires : ",
|
||||
"Default captions: ": "Sous-titres principal : ",
|
||||
"Fallback captions: ": "Sous-titres secondaire : ",
|
||||
"Show related videos? ": "Voir les vidéos liées à ce sujet ? ",
|
||||
"Default comments: ": "Source des commentaires : ",
|
||||
"Default captions: ": "Sous-titres par défaut : ",
|
||||
"Fallback captions: ": "Fallback captions: ",
|
||||
"Show related videos? ": "Voir les vidéos liées ? ",
|
||||
"Visual preferences": "Préférences du site",
|
||||
"Dark mode: ": "Mode Sombre : ",
|
||||
"Thin mode: ": "Mode Simplifié : ",
|
||||
@ -82,13 +82,13 @@
|
||||
"Watch history": "Historique de visionnage",
|
||||
"Delete account": "Supprimer votre compte",
|
||||
"Administrator preferences": "Préferences d'Administrateur",
|
||||
"Default homepage: ": "Page d'accueil par defaut :",
|
||||
"Feed menu: ": "Menu des Flux :",
|
||||
"Top enabled? ": "Top activé ?",
|
||||
"CAPTCHA enabled? ": "CAPTCHA activé ?",
|
||||
"Login enabled? ": "Connexion activé ?",
|
||||
"Registration enabled? ": "Inscription activé ?",
|
||||
"Report statistics? ": "Telemetrie activé ?",
|
||||
"Default homepage: ": "Page d'accueil par défaut : ",
|
||||
"Feed menu: ": "Menu des Flux : ",
|
||||
"Top enabled? ": "Top activé ? ",
|
||||
"CAPTCHA enabled? ": "CAPTCHA activé ? ",
|
||||
"Login enabled? ": "Connexion activé ? ",
|
||||
"Registration enabled? ": "Inscription activée ? ",
|
||||
"Report statistics? ": "Télémétrie activé ? ",
|
||||
"Save preferences": "Enregistrer les préférences",
|
||||
"Subscription manager": "Gestionnaire d'abonnement",
|
||||
"`x` subscriptions": "`x` abonnements",
|
||||
@ -103,16 +103,18 @@
|
||||
"View JavaScript license information.": "Voir les informations des licences JavaScript.",
|
||||
"View privacy policy.": "Politique de confidentialité",
|
||||
"Trending": "Tendances",
|
||||
"Unlisted": "Non répertoriée",
|
||||
"Watch video on Youtube": "Voir la vidéo sur Youtube",
|
||||
"Genre: ": "Genre : ",
|
||||
"License: ": "Licence : ",
|
||||
"Family friendly? ": "Tout Public ? ",
|
||||
"Wilson score: ": "Score de Wilson : ",
|
||||
"Engagement: ": "Poucentage de spectateur aillant aimé Liker ou Disliker la vidéo : ",
|
||||
"Engagement: ": "Poucentage de spectateur aillant aimé Like ou Dislike la vidéo : ",
|
||||
"Whitelisted regions: ": "Régions en liste blanche : ",
|
||||
"Blacklisted regions: ": "Régions sur liste noire : ",
|
||||
"Shared `x`": "Partagée `x`",
|
||||
"Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Il semblerait que JavaScript sois désactivé. Cliquez ici pour voir les commentaires. Gardez à l'esprit que le chargement peut prendre plus de temps.",
|
||||
"Shared `x`": "Ajoutée le `x`",
|
||||
"Premieres in `x`": "Première dans `x`",
|
||||
"Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Il semblerait que JavaScript soit désactivé. Cliquez ici pour voir les commentaires sans. Gardez à l'esprit que le chargement peut prendre plus de temps.",
|
||||
"View YouTube comments": "Voir les commentaires YouTube",
|
||||
"View more comments on Reddit": "Voir plus de commentaires sur Reddit",
|
||||
"View `x` comments": "Voir `x` commentaires",
|
||||
@ -124,13 +126,13 @@
|
||||
"Unable to login, make sure two-factor authentication (Authenticator or SMS) is enabled.": "Si vous ne parvenez pas à vous connecter, assurez-vous que l'authentification à deux facteurs (Authenticator ou SMS) est activée.",
|
||||
"Invalid TFA code": "Code d'authentification à deux facteurs invalide",
|
||||
"Login failed. This may be because two-factor authentication is not enabled on your account.": "La connexion a échoué. Cela peut être dû au fait que l'authentification à deux facteurs n'est pas activée sur votre compte.",
|
||||
"Invalid answer": "Réponse non valide",
|
||||
"Invalid answer": "Réponse invalide",
|
||||
"Invalid CAPTCHA": "CAPTCHA invalide",
|
||||
"CAPTCHA is a required field": "Veuillez rentrez un CAPTCHA",
|
||||
"User ID is a required field": "Veuillez rentrez un Identifiant Utilisateur",
|
||||
"Password is a required field": "Veuillez rentrez un Mot de passe",
|
||||
"CAPTCHA is a required field": "Veuillez entrer un CAPTCHA",
|
||||
"User ID is a required field": "Veuillez entrer un Identifiant Utilisateur",
|
||||
"Password is a required field": "Veuillez entrer un Mot de passe",
|
||||
"Invalid username or password": "Nom d'utilisateur ou mot de passe invalide",
|
||||
"Please sign in using 'Sign in with Google'": "Veuillez vous connecter en utilisant \"S'identifier avec Google\"",
|
||||
"Please sign in using 'Sign in with Google'": "Veuillez vous connecter en utilisant \"Se connecter avec Google\"",
|
||||
"Password cannot be empty": "Le mot de passe ne peut pas être vide",
|
||||
"Password cannot be longer than 55 characters": "Le mot de passe ne doit pas comporter plus de 55 caractères",
|
||||
"Please sign in": "Veuillez vous connecter",
|
||||
@ -268,7 +270,7 @@
|
||||
"`x` hours": "`x` heures",
|
||||
"`x` minutes": "`x` minutes",
|
||||
"`x` seconds": "`x` secondes",
|
||||
"Fallback comments: ": "Commentaires secondaires : ",
|
||||
"Fallback comments: ": "Fallback comments: ",
|
||||
"Popular": "Populaire",
|
||||
"Top": "Top",
|
||||
"About": "A Propos",
|
||||
@ -289,5 +291,5 @@
|
||||
"Video mode": "Mode Vidéo",
|
||||
"Videos": "Vidéos",
|
||||
"Playlists": "Liste de lecture",
|
||||
"Current version: ": "Version actuelle :"
|
||||
"Current version: ": "Version :"
|
||||
}
|
||||
|
@ -103,6 +103,7 @@
|
||||
"View JavaScript license information.": "Guarda le informazioni di licenza del codice JavaScript.",
|
||||
"View privacy policy.": "",
|
||||
"Trending": "Tendenze",
|
||||
"Unlisted": "",
|
||||
"Watch video on Youtube": "Guarda il video su YouTube",
|
||||
"Genre: ": "Genere: ",
|
||||
"License: ": "Licenza: ",
|
||||
@ -112,6 +113,7 @@
|
||||
"Whitelisted regions: ": "Regioni nella lista bianca: ",
|
||||
"Blacklisted regions: ": "Regioni nella lista nera: ",
|
||||
"Shared `x`": "Condiviso `x`",
|
||||
"Premieres in `x`": "",
|
||||
"Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Ciao! Sembra che tu abbia disattivato JavaScript. Clicca qui per visualizzare i commenti. Considera che potrebbe volerci più tempo.",
|
||||
"View YouTube comments": "Visualizza i commenti da YouTube",
|
||||
"View more comments on Reddit": "Visualizza più commenti su Reddit",
|
||||
|
@ -103,6 +103,7 @@
|
||||
"View JavaScript license information.": "Vis JavaScript-lisensinfo.",
|
||||
"View privacy policy.": "",
|
||||
"Trending": "Trendsettende",
|
||||
"Unlisted": "",
|
||||
"Watch video on Youtube": "Vis video på YouTube",
|
||||
"Genre: ": "Sjanger: ",
|
||||
"License: ": "Lisens: ",
|
||||
@ -112,6 +113,7 @@
|
||||
"Whitelisted regions: ": "Hvitlistede regioner: ",
|
||||
"Blacklisted regions: ": "Svartelistede regioner: ",
|
||||
"Shared `x`": "Delt `x`",
|
||||
"Premieres in `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",
|
||||
|
@ -103,6 +103,7 @@
|
||||
"View JavaScript license information.": "Bekijk JavaScript licentie informatie.",
|
||||
"View privacy policy.": "",
|
||||
"Trending": "Trending",
|
||||
"Unlisted": "",
|
||||
"Watch video on Youtube": "Bekijk video op Youtube",
|
||||
"Genre: ": "Genre: ",
|
||||
"License: ": "Licentie: ",
|
||||
@ -112,6 +113,7 @@
|
||||
"Whitelisted regions: ": "Toegestane regio's: ",
|
||||
"Blacklisted regions: ": "Geblokkeerde regio's: ",
|
||||
"Shared `x`": "`x` gedeeld",
|
||||
"Premieres in `x`": "",
|
||||
"Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Hoi! Het lijkt erop dat je JavaScript uit hebt staan. Klik hier om de reacties te bekijken, hou er rekening mee dat het wat langer duurt om te laden.",
|
||||
"View YouTube comments": "Bekijk YouTube reacties",
|
||||
"View more comments on Reddit": "Bekijk meer reacties op Reddit",
|
||||
|
@ -10,7 +10,7 @@
|
||||
"newest": "najnowsze",
|
||||
"oldest": "najstarsze",
|
||||
"popular": "popularne",
|
||||
"last": "",
|
||||
"last": "ostatnie",
|
||||
"Next page": "Następna strona",
|
||||
"Previous page": "Poprzednia strona",
|
||||
"Clear watch history?": "Wyczyścić historię?",
|
||||
@ -50,7 +50,7 @@
|
||||
"Autoplay: ": "Autoodtwarzanie: ",
|
||||
"Autoplay next video: ": "Odtwórz następny film: ",
|
||||
"Listen by default: ": "Tryb dźwiękowy: ",
|
||||
"Proxy videos? ": "",
|
||||
"Proxy videos? ": "Filmy przez proxy? ",
|
||||
"Default speed: ": "Domyślna prędkość: ",
|
||||
"Preferred video quality: ": "Preferowana jakość filmów: ",
|
||||
"Player volume: ": "Głośność odtwarzacza: ",
|
||||
@ -101,8 +101,9 @@
|
||||
"Released under the AGPLv3 by Omar Roth.": "Wydano na licencji AGPLv3 przez Omar Roth.",
|
||||
"Source available here.": "Kod źródłowy dostępny tutaj.",
|
||||
"View JavaScript license information.": "Wyświetl informację o licencji JavaScript.",
|
||||
"View privacy policy.": "",
|
||||
"View privacy policy.": "Polityka prywatności.",
|
||||
"Trending": "Na czasie",
|
||||
"Unlisted": "",
|
||||
"Watch video on Youtube": "Zobacz film na YouTube",
|
||||
"Genre: ": "Gatunek: ",
|
||||
"License: ": "Licencja: ",
|
||||
@ -112,6 +113,7 @@
|
||||
"Whitelisted regions: ": "Dostępny na obszarach: ",
|
||||
"Blacklisted regions: ": "Niedostępny na obszarach: ",
|
||||
"Shared `x`": "Udostępniono `x`",
|
||||
"Premieres in `x`": "",
|
||||
"Hi! Looks like you have JavaScript disabled. Click here to view comments, keep in mind it may take a bit longer to load.": "Cześć! Wygląda na to, że masz wyłączoną obsługę JavaScriptu. Kliknij tutaj, żeby zobaczyć komentarze. Pamiętaj, że wczytywanie może potrwać dłużej.",
|
||||
"View YouTube comments": "Wyświetl komentarze z YouTube",
|
||||
"View more comments on Reddit": "Wyświetl więcej komentarzy na Reddicie",
|
||||
@ -270,7 +272,7 @@
|
||||
"`x` seconds": "`x` sekund",
|
||||
"Fallback comments: ": "Zastępcze komentarze: ",
|
||||
"Popular": "Popularne",
|
||||
"Top": "Na czasie",
|
||||
"Top": "Najczęściej oglądane",
|
||||
"About": "Informacje",
|
||||
"Rating: ": "Ocena: ",
|
||||
"Language: ": "Język: ",
|
||||
|
@ -105,6 +105,7 @@
|
||||
"View JavaScript license information.": "Посмотреть лицензии JavaScript кода.",
|
||||
"View privacy policy.": "См. политику конфиденциальности.",
|
||||
"Trending": "В тренде",
|
||||
"Unlisted": "",
|
||||
"Watch video on Youtube": "Смотреть на YouTube",
|
||||
"Genre: ": "Жанр: ",
|
||||
"License: ": "Лицензия: ",
|
||||
@ -114,6 +115,7 @@
|
||||
"Whitelisted regions: ": "Доступно для: ",
|
||||
"Blacklisted regions: ": "Недоступно для: ",
|
||||
"Shared `x`": "Опубликовано `x`",
|
||||
"Premieres in `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",
|
||||
|
@ -242,6 +242,9 @@ get "/api/v1/comments/:id" do |env|
|
||||
source = env.params.query["source"]?
|
||||
source ||= "youtube"
|
||||
|
||||
thin_mode = env.params.query["thin_mode"]?
|
||||
thin_mode = thin_mode == "true"
|
||||
|
||||
format = env.params.query["format"]?
|
||||
format ||= "json"
|
||||
|
||||
@ -249,7 +252,7 @@ get "/api/v1/comments/:id" do |env|
|
||||
|
||||
if source == "youtube"
|
||||
begin
|
||||
comments = fetch_youtube_comments(id, continuation, proxies, format, locale, region)
|
||||
comments = fetch_youtube_comments(id, continuation, proxies, format, locale, thin_mode, region)
|
||||
rescue ex
|
||||
error_message = {"error" => ex.message}.to_json
|
||||
env.response.status_code = 500
|
||||
@ -1688,6 +1691,11 @@ get "/videoplayback" do |env|
|
||||
fvip = query_params["fvip"]? || "3"
|
||||
mns = query_params["mn"].split(",")
|
||||
|
||||
if query_params["region"]?
|
||||
region = query_params["region"]
|
||||
query_params.delete("region")
|
||||
end
|
||||
|
||||
if query_params["host"]? && !query_params["host"].empty?
|
||||
host = "https://#{query_params["host"]}"
|
||||
query_params.delete("host")
|
||||
@ -1704,8 +1712,6 @@ get "/videoplayback" do |env|
|
||||
end
|
||||
end
|
||||
|
||||
region = query_params["region"]?
|
||||
|
||||
response = HTTP::Client::Response.new(403)
|
||||
5.times do
|
||||
begin
|
||||
@ -1725,9 +1731,12 @@ get "/videoplayback" do |env|
|
||||
|
||||
if response.headers["Location"]?
|
||||
url = URI.parse(response.headers["Location"])
|
||||
host = url.host
|
||||
env.response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
|
||||
url = url.full_path
|
||||
url += "&host=#{host}"
|
||||
|
||||
if region
|
||||
url += "®ion=#{region}"
|
||||
end
|
||||
@ -1744,15 +1753,30 @@ get "/videoplayback" do |env|
|
||||
client.get(url, headers) do |response|
|
||||
env.response.status_code = response.status_code
|
||||
|
||||
if title = env.params.query["title"]?
|
||||
# https://blog.fastmail.com/2011/06/24/download-non-english-filenames/
|
||||
env.response.headers["Content-Disposition"] = "attachment; filename=\"#{URI.escape(title)}\"; filename*=UTF-8''#{URI.escape(title)}"
|
||||
end
|
||||
|
||||
response.headers.each do |key, value|
|
||||
env.response.headers[key] = value
|
||||
end
|
||||
|
||||
if response.headers["Location"]?
|
||||
url = URI.parse(response.headers["Location"])
|
||||
host = url.host
|
||||
env.response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
|
||||
url = url.full_path
|
||||
url += "&host=#{host}"
|
||||
|
||||
if region
|
||||
url += "®ion=#{region}"
|
||||
end
|
||||
|
||||
next env.redirect url
|
||||
end
|
||||
|
||||
if title = query_params["title"]?
|
||||
# https://blog.fastmail.com/2011/06/24/download-non-english-filenames/
|
||||
env.response.headers["Content-Disposition"] = "attachment; filename=\"#{URI.escape(title)}\"; filename*=UTF-8''#{URI.escape(title)}"
|
||||
end
|
||||
|
||||
env.response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
|
||||
begin
|
||||
|
@ -1,4 +1,4 @@
|
||||
class InvidiousChannel
|
||||
struct InvidiousChannel
|
||||
add_mapping({
|
||||
id: String,
|
||||
author: String,
|
||||
@ -8,7 +8,7 @@ class InvidiousChannel
|
||||
})
|
||||
end
|
||||
|
||||
class ChannelVideo
|
||||
struct ChannelVideo
|
||||
add_mapping({
|
||||
id: String,
|
||||
title: String,
|
||||
|
@ -29,7 +29,7 @@ class RedditComment
|
||||
})
|
||||
end
|
||||
|
||||
class RedditLink
|
||||
struct RedditLink
|
||||
JSON.mapping({
|
||||
author: String,
|
||||
score: Int32,
|
||||
@ -41,7 +41,7 @@ class RedditLink
|
||||
})
|
||||
end
|
||||
|
||||
class RedditMore
|
||||
struct RedditMore
|
||||
JSON.mapping({
|
||||
children: Array(String),
|
||||
count: Int32,
|
||||
@ -56,7 +56,7 @@ class RedditListing
|
||||
})
|
||||
end
|
||||
|
||||
def fetch_youtube_comments(id, continuation, proxies, format, locale, region)
|
||||
def fetch_youtube_comments(id, continuation, proxies, format, locale, thin_mode, region)
|
||||
video = fetch_video(id, proxies, region: region)
|
||||
|
||||
session_token = video.info["session_token"]?
|
||||
@ -232,7 +232,7 @@ def fetch_youtube_comments(id, continuation, proxies, format, locale, region)
|
||||
|
||||
if format == "html"
|
||||
comments = JSON.parse(comments)
|
||||
content_html = template_youtube_comments(comments, locale)
|
||||
content_html = template_youtube_comments(comments, locale, thin_mode)
|
||||
|
||||
comments = JSON.build do |json|
|
||||
json.object do
|
||||
@ -278,7 +278,7 @@ def fetch_reddit_comments(id)
|
||||
return comments, thread
|
||||
end
|
||||
|
||||
def template_youtube_comments(comments, locale)
|
||||
def template_youtube_comments(comments, locale, thin_mode)
|
||||
html = ""
|
||||
|
||||
root = comments["comments"].as_a
|
||||
@ -297,7 +297,11 @@ def template_youtube_comments(comments, locale)
|
||||
END_HTML
|
||||
end
|
||||
|
||||
if !thin_mode
|
||||
author_thumbnail = "/ggpht#{URI.parse(child["authorThumbnails"][-1]["url"].as_s).full_path}"
|
||||
else
|
||||
author_thumbnail = ""
|
||||
end
|
||||
|
||||
html += <<-END_HTML
|
||||
<div class="pure-g">
|
||||
@ -318,7 +322,12 @@ def template_youtube_comments(comments, locale)
|
||||
END_HTML
|
||||
|
||||
if child["creatorHeart"]?
|
||||
if !thin_mode
|
||||
creator_thumbnail = "/ggpht#{URI.parse(child["creatorHeart"]["creatorThumbnail"].as_s).full_path}"
|
||||
else
|
||||
creator_thumbnail = ""
|
||||
end
|
||||
|
||||
html += <<-END_HTML
|
||||
<span class="creator-heart-container" title="#{translate(locale, "`x` marked it with a ❤", child["creatorHeart"]["creatorName"].as_s)}">
|
||||
<div class="creator-heart">
|
||||
|
@ -1,4 +1,4 @@
|
||||
class Config
|
||||
struct Config
|
||||
YAML.mapping({
|
||||
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
|
||||
|
@ -9,6 +9,17 @@ macro add_mapping(mapping)
|
||||
DB.mapping({{mapping}})
|
||||
end
|
||||
|
||||
macro json_mapping(mapping)
|
||||
def initialize({{*mapping.keys.map { |id| "@#{id}".id }}})
|
||||
end
|
||||
|
||||
def to_a
|
||||
return [{{*mapping.keys.map { |id| "@#{id}".id }}}]
|
||||
end
|
||||
|
||||
JSON.mapping({{mapping}})
|
||||
end
|
||||
|
||||
macro templated(filename, template = "template")
|
||||
render "src/invidious/views/#{{{filename}}}.ecr", "src/invidious/views/#{{{template}}}.ecr"
|
||||
end
|
||||
|
@ -106,7 +106,7 @@ def subscribe_to_feeds(db, logger, key, config)
|
||||
if config.use_pubsub_feeds
|
||||
spawn do
|
||||
loop do
|
||||
db.query_all("SELECT id FROM channels WHERE CURRENT_TIMESTAMP - subscribed > '4 days'") do |rs|
|
||||
db.query_all("SELECT id FROM channels WHERE CURRENT_TIMESTAMP - subscribed > '4 days' OR subscribed IS NULL") do |rs|
|
||||
rs.each do
|
||||
ucid = rs.read(String)
|
||||
response = subscribe_pubsub(ucid, key, config)
|
||||
|
@ -1,4 +1,4 @@
|
||||
class MixVideo
|
||||
struct MixVideo
|
||||
add_mapping({
|
||||
title: String,
|
||||
id: String,
|
||||
@ -10,7 +10,7 @@ class MixVideo
|
||||
})
|
||||
end
|
||||
|
||||
class Mix
|
||||
struct Mix
|
||||
add_mapping({
|
||||
title: String,
|
||||
id: String,
|
||||
|
@ -1,4 +1,4 @@
|
||||
class PlaylistVideo
|
||||
struct PlaylistVideo
|
||||
add_mapping({
|
||||
title: String,
|
||||
id: String,
|
||||
@ -12,7 +12,7 @@ class PlaylistVideo
|
||||
})
|
||||
end
|
||||
|
||||
class Playlist
|
||||
struct Playlist
|
||||
add_mapping({
|
||||
title: String,
|
||||
id: String,
|
||||
|
@ -1,4 +1,4 @@
|
||||
class SearchVideo
|
||||
struct SearchVideo
|
||||
add_mapping({
|
||||
title: String,
|
||||
id: String,
|
||||
@ -16,7 +16,7 @@ class SearchVideo
|
||||
})
|
||||
end
|
||||
|
||||
class SearchPlaylistVideo
|
||||
struct SearchPlaylistVideo
|
||||
add_mapping({
|
||||
title: String,
|
||||
id: String,
|
||||
@ -24,7 +24,7 @@ class SearchPlaylistVideo
|
||||
})
|
||||
end
|
||||
|
||||
class SearchPlaylist
|
||||
struct SearchPlaylist
|
||||
add_mapping({
|
||||
title: String,
|
||||
id: String,
|
||||
@ -36,7 +36,7 @@ class SearchPlaylist
|
||||
})
|
||||
end
|
||||
|
||||
class SearchChannel
|
||||
struct SearchChannel
|
||||
add_mapping({
|
||||
author: String,
|
||||
ucid: String,
|
||||
|
@ -1,12 +1,12 @@
|
||||
require "crypto/bcrypt/password"
|
||||
|
||||
class User
|
||||
struct User
|
||||
module PreferencesConverter
|
||||
def self.from_rs(rs)
|
||||
begin
|
||||
Preferences.from_json(rs.read(String))
|
||||
rescue ex
|
||||
DEFAULT_USER_PREFERENCES
|
||||
Preferences.from_json("{}")
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -18,7 +18,6 @@ class User
|
||||
email: String,
|
||||
preferences: {
|
||||
type: Preferences,
|
||||
default: DEFAULT_USER_PREFERENCES,
|
||||
converter: PreferencesConverter,
|
||||
},
|
||||
password: String?,
|
||||
@ -27,30 +26,30 @@ class User
|
||||
})
|
||||
end
|
||||
|
||||
DEFAULT_USER_PREFERENCES = Preferences.from_json({
|
||||
"video_loop" => false,
|
||||
"autoplay" => false,
|
||||
"continue" => false,
|
||||
"local" => false,
|
||||
"listen" => false,
|
||||
"speed" => 1.0,
|
||||
"quality" => "hd720",
|
||||
"volume" => 100,
|
||||
"comments" => ["youtube", ""],
|
||||
"captions" => ["", "", ""],
|
||||
"related_videos" => true,
|
||||
"redirect_feed" => false,
|
||||
"locale" => "en-US",
|
||||
"dark_mode" => false,
|
||||
"thin_mode" => false,
|
||||
"max_results" => 40,
|
||||
"sort" => "published",
|
||||
"latest_only" => false,
|
||||
"unseen_only" => false,
|
||||
"notifications_only" => false,
|
||||
}.to_json)
|
||||
DEFAULT_USER_PREFERENCES = Preferences.new(
|
||||
video_loop: false,
|
||||
autoplay: false,
|
||||
continue: false,
|
||||
local: false,
|
||||
listen: false,
|
||||
speed: 1.0_f32,
|
||||
quality: "hd720",
|
||||
volume: 100,
|
||||
comments: ["youtube", ""],
|
||||
captions: ["", "", ""],
|
||||
related_videos: true,
|
||||
redirect_feed: false,
|
||||
locale: "en-US",
|
||||
dark_mode: false,
|
||||
thin_mode: false,
|
||||
max_results: 40,
|
||||
sort: "published",
|
||||
latest_only: false,
|
||||
unseen_only: false,
|
||||
notifications_only: false,
|
||||
)
|
||||
|
||||
class Preferences
|
||||
struct Preferences
|
||||
module StringToArray
|
||||
def self.to_json(value : Array(String), json : JSON::Builder)
|
||||
json.array do
|
||||
@ -74,58 +73,27 @@ class Preferences
|
||||
end
|
||||
end
|
||||
|
||||
JSON.mapping({
|
||||
video_loop: Bool,
|
||||
autoplay: Bool,
|
||||
continue: {
|
||||
type: Bool,
|
||||
default: DEFAULT_USER_PREFERENCES.continue,
|
||||
},
|
||||
local: {
|
||||
type: Bool,
|
||||
default: DEFAULT_USER_PREFERENCES.local,
|
||||
},
|
||||
listen: {
|
||||
type: Bool,
|
||||
default: DEFAULT_USER_PREFERENCES.listen,
|
||||
},
|
||||
speed: Float32,
|
||||
quality: String,
|
||||
volume: Int32,
|
||||
comments: {
|
||||
type: Array(String),
|
||||
default: DEFAULT_USER_PREFERENCES.comments,
|
||||
converter: StringToArray,
|
||||
},
|
||||
captions: {
|
||||
type: Array(String),
|
||||
default: DEFAULT_USER_PREFERENCES.captions,
|
||||
},
|
||||
redirect_feed: {
|
||||
type: Bool,
|
||||
default: DEFAULT_USER_PREFERENCES.redirect_feed,
|
||||
},
|
||||
related_videos: {
|
||||
type: Bool,
|
||||
default: DEFAULT_USER_PREFERENCES.related_videos,
|
||||
},
|
||||
dark_mode: Bool,
|
||||
thin_mode: {
|
||||
type: Bool,
|
||||
default: DEFAULT_USER_PREFERENCES.thin_mode,
|
||||
},
|
||||
max_results: Int32,
|
||||
sort: String,
|
||||
latest_only: Bool,
|
||||
unseen_only: Bool,
|
||||
notifications_only: {
|
||||
type: Bool,
|
||||
default: DEFAULT_USER_PREFERENCES.notifications_only,
|
||||
},
|
||||
locale: {
|
||||
type: String,
|
||||
default: DEFAULT_USER_PREFERENCES.locale,
|
||||
},
|
||||
json_mapping({
|
||||
video_loop: {type: Bool, default: DEFAULT_USER_PREFERENCES.video_loop},
|
||||
autoplay: {type: Bool, default: DEFAULT_USER_PREFERENCES.autoplay},
|
||||
continue: {type: Bool, default: DEFAULT_USER_PREFERENCES.continue},
|
||||
local: {type: Bool, default: DEFAULT_USER_PREFERENCES.local},
|
||||
listen: {type: Bool, default: DEFAULT_USER_PREFERENCES.listen},
|
||||
speed: {type: Float32, default: DEFAULT_USER_PREFERENCES.speed},
|
||||
quality: {type: String, default: DEFAULT_USER_PREFERENCES.quality},
|
||||
volume: {type: Int32, default: DEFAULT_USER_PREFERENCES.volume},
|
||||
comments: {type: Array(String), default: DEFAULT_USER_PREFERENCES.comments, converter: StringToArray},
|
||||
captions: {type: Array(String), default: DEFAULT_USER_PREFERENCES.captions, converter: StringToArray},
|
||||
redirect_feed: {type: Bool, default: DEFAULT_USER_PREFERENCES.redirect_feed},
|
||||
related_videos: {type: Bool, default: DEFAULT_USER_PREFERENCES.related_videos},
|
||||
dark_mode: {type: Bool, default: DEFAULT_USER_PREFERENCES.dark_mode},
|
||||
thin_mode: {type: Bool, default: DEFAULT_USER_PREFERENCES.thin_mode},
|
||||
max_results: {type: Int32, default: DEFAULT_USER_PREFERENCES.max_results},
|
||||
sort: {type: String, default: DEFAULT_USER_PREFERENCES.sort},
|
||||
latest_only: {type: Bool, default: DEFAULT_USER_PREFERENCES.latest_only},
|
||||
unseen_only: {type: Bool, default: DEFAULT_USER_PREFERENCES.unseen_only},
|
||||
notifications_only: {type: Bool, default: DEFAULT_USER_PREFERENCES.notifications_only},
|
||||
locale: {type: String, default: DEFAULT_USER_PREFERENCES.locale},
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -241,7 +241,7 @@ VIDEO_FORMATS = {
|
||||
"251" => {"ext" => "webm", "format" => "DASH audio", "acodec" => "opus", "abr" => 160},
|
||||
}
|
||||
|
||||
class Video
|
||||
struct Video
|
||||
property player_json : JSON::Any?
|
||||
|
||||
module HTTPParamConverter
|
||||
@ -251,7 +251,7 @@ class Video
|
||||
end
|
||||
|
||||
def allow_ratings
|
||||
allow_ratings = player_response["videoDetails"].try &.["allowRatings"]?.try &.as_bool
|
||||
allow_ratings = player_response["videoDetails"]?.try &.["allowRatings"]?.try &.as_bool
|
||||
|
||||
if allow_ratings.nil?
|
||||
return true
|
||||
@ -271,7 +271,7 @@ class Video
|
||||
end
|
||||
|
||||
def is_listed
|
||||
is_listed = player_response["videoDetails"].try &.["isCrawlable"]?.try &.as_bool
|
||||
is_listed = player_response["videoDetails"]?.try &.["isCrawlable"]?.try &.as_bool
|
||||
|
||||
if is_listed.nil?
|
||||
return true
|
||||
@ -281,7 +281,7 @@ class Video
|
||||
end
|
||||
|
||||
def is_upcoming
|
||||
is_upcoming = player_response["videoDetails"].try &.["isUpcoming"]?.try &.as_bool
|
||||
is_upcoming = player_response["videoDetails"]?.try &.["isUpcoming"]?.try &.as_bool
|
||||
|
||||
if is_upcoming.nil?
|
||||
return false
|
||||
@ -297,7 +297,7 @@ class Video
|
||||
.try &.["liveStreamabilityRenderer"]?
|
||||
.try &.["offlineSlate"]?
|
||||
.try &.["liveStreamOfflineSlateRenderer"]?
|
||||
.try &.["scheduledStartTime"].as_s.to_i64
|
||||
.try &.["scheduledStartTime"]?.try &.as_s.to_i64
|
||||
end
|
||||
|
||||
if premiere_timestamp
|
||||
@ -549,7 +549,7 @@ class Video
|
||||
})
|
||||
end
|
||||
|
||||
class Caption
|
||||
struct Caption
|
||||
JSON.mapping(
|
||||
name: CaptionName,
|
||||
baseUrl: String,
|
||||
@ -557,7 +557,7 @@ class Caption
|
||||
)
|
||||
end
|
||||
|
||||
class CaptionName
|
||||
struct CaptionName
|
||||
JSON.mapping(
|
||||
simpleText: String,
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user