diff --git a/assets/hashtag.svg b/assets/hashtag.svg new file mode 100644 index 00000000..55109825 --- /dev/null +++ b/assets/hashtag.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/locales/ar.json b/locales/ar.json index c137d1a3..877fb9ff 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -540,5 +540,13 @@ "Channel Sponsor": "راعي القناة", "Standard YouTube license": "ترخيص YouTube القياسي", "Download is disabled": "تم تعطيل التحميلات", - "Import YouTube playlist (.csv)": "استيراد قائمة تشغيل YouTube (.csv)" + "Import YouTube playlist (.csv)": "استيراد قائمة تشغيل YouTube (.csv)", + "generic_button_save": "حفظ", + "generic_button_delete": "حذف", + "generic_button_edit": "تحرير", + "generic_button_cancel": "الغاء", + "generic_button_rss": "RSS", + "channel_tab_releases_label": "الإصدارات", + "playlist_button_add_items": "إضافة مقاطع فيديو", + "channel_tab_podcasts_label": "البودكاست" } diff --git a/locales/az.json b/locales/az.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/locales/az.json @@ -0,0 +1 @@ +{} diff --git a/locales/cs.json b/locales/cs.json index 73ed960d..b2cce0bd 100644 --- a/locales/cs.json +++ b/locales/cs.json @@ -492,5 +492,13 @@ "Song: ": "Skladba: ", "Standard YouTube license": "Standardní licence YouTube", "Download is disabled": "Stahování je zakázáno", - "Import YouTube playlist (.csv)": "Importovat YouTube playlist (.csv)" + "Import YouTube playlist (.csv)": "Importovat YouTube playlist (.csv)", + "generic_button_save": "Uložit", + "generic_button_delete": "Odstranit", + "generic_button_cancel": "Zrušit", + "channel_tab_podcasts_label": "Podcasty", + "channel_tab_releases_label": "Vydání", + "generic_button_edit": "Upravit", + "generic_button_rss": "RSS", + "playlist_button_add_items": "Přidat videa" } diff --git a/locales/de.json b/locales/de.json index 66f2ae6f..6ceaa44b 100644 --- a/locales/de.json +++ b/locales/de.json @@ -476,5 +476,11 @@ "Standard YouTube license": "Standard YouTube-Lizenz", "Song: ": "Musik: ", "Download is disabled": "Herunterladen ist deaktiviert", - "Import YouTube playlist (.csv)": "YouTube Playlist Importieren (.csv)" + "Import YouTube playlist (.csv)": "YouTube Playlist Importieren (.csv)", + "generic_button_delete": "Löschen", + "generic_button_edit": "Bearbeiten", + "generic_button_save": "Speichern", + "generic_button_cancel": "Abbrechen", + "generic_button_rss": "RSS", + "playlist_button_add_items": "Videos hinzufügen" } diff --git a/locales/en-US.json b/locales/en-US.json index 74f43d90..06d095dc 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -1,4 +1,6 @@ { + "generic_channels_count": "{{count}} channel", + "generic_channels_count_plural": "{{count}} channels", "generic_views_count": "{{count}} view", "generic_views_count_plural": "{{count}} views", "generic_videos_count": "{{count}} video", diff --git a/locales/eo.json b/locales/eo.json index a4b46bef..6d1b0bc1 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -154,7 +154,7 @@ "View YouTube comments": "Vidi komentojn de JuTubo", "View more comments on Reddit": "Vidi pli komentoj en Reddit", "View `x` comments": { - "([^.,0-9]|^)1([^.,0-9]|$)": "Vidi `x` komentojn", + "([^.,0-9]|^)1([^.,0-9]|$)": "Vidi `x` komenton", "": "Vidi `x` komentojn" }, "View Reddit comments": "Vidi komentojn de Reddit", @@ -447,8 +447,8 @@ "French (auto-generated)": "Franca (aŭtomate generita)", "Spanish (Mexico)": "Hispana (Meksiko)", "Spanish (auto-generated)": "Hispana (aŭtomate generita)", - "generic_count_days": "{{count}} jaro", - "generic_count_days_plural": "{{count}} jaroj", + "generic_count_days": "{{count}} tago", + "generic_count_days_plural": "{{count}} tagoj", "search_filters_type_option_all": "Ajna speco", "search_filters_duration_option_none": "Ajna daŭro", "search_filters_apply_button": "Uzi elektitajn filtrilojn", @@ -476,5 +476,13 @@ "Song: ": "Muzikaĵo: ", "Standard YouTube license": "Implicita YouTube-licenco", "Download is disabled": "Elŝuto estas malebligita", - "Import YouTube playlist (.csv)": "Importi YouTube-ludliston (.csv)" + "Import YouTube playlist (.csv)": "Importi YouTube-ludliston (.csv)", + "generic_button_edit": "Redakti", + "playlist_button_add_items": "Aldoni videojn", + "generic_button_rss": "RSS", + "generic_button_delete": "Forigi", + "channel_tab_podcasts_label": "Podkastoj", + "generic_button_cancel": "Nuligi", + "channel_tab_releases_label": "Eldonoj", + "generic_button_save": "Konservi" } diff --git a/locales/es.json b/locales/es.json index b3103a25..b4a56030 100644 --- a/locales/es.json +++ b/locales/es.json @@ -113,7 +113,7 @@ "Token manager": "Gestor de tokens", "Token": "Ficha", "Import/export": "Importar/Exportar", - "unsubscribe": "Desuscribirse", + "unsubscribe": "desuscribirse", "revoke": "revocar", "Subscriptions": "Suscripciones", "search": "buscar", @@ -154,7 +154,7 @@ "View YouTube comments": "Ver los comentarios de YouTube", "View more comments on Reddit": "Ver más comentarios en Reddit", "View `x` comments": { - "([^.,0-9]|^)1([^.,0-9]|$)": "Ver `x` comentarios", + "([^.,0-9]|^)1([^.,0-9]|$)": "Ver `x` comentario", "": "Ver `x` comentarios" }, "View Reddit comments": "Ver los comentarios de Reddit", @@ -476,5 +476,13 @@ "Channel Sponsor": "Patrocinador del canal", "Standard YouTube license": "Licencia de YouTube estándar", "Download is disabled": "La descarga está deshabilitada", - "Import YouTube playlist (.csv)": "Importar lista de reproducción de YouTube (.csv)" + "Import YouTube playlist (.csv)": "Importar lista de reproducción de YouTube (.csv)", + "playlist_button_add_items": "Añadir vídeos", + "generic_button_edit": "Editar", + "generic_button_save": "Guardar", + "generic_button_delete": "Borrar", + "generic_button_cancel": "Cancelar", + "generic_button_rss": "RSS", + "channel_tab_podcasts_label": "Podcasts", + "channel_tab_releases_label": "Publicaciones" } diff --git a/locales/fr.json b/locales/fr.json index 2eb4dd2b..7fea8f14 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -1,4 +1,6 @@ { + "generic_channels_count": "{{count}} chaîne", + "generic_channels_count_plural": "{{count}} chaînes", "generic_views_count": "{{count}} vue", "generic_views_count_plural": "{{count}} vues", "generic_videos_count": "{{count}} vidéo", @@ -55,10 +57,10 @@ "Password": "Mot de passe", "Time (h:mm:ss):": "Heure (h:mm:ss) :", "Text CAPTCHA": "CAPTCHA textuel", - "Image CAPTCHA": "CAPTCHA graphique", - "Sign In": "Se connecter", + "Image CAPTCHA": "CAPTCHA pictural", + "Sign In": "S'identifier", "Register": "S'inscrire", - "E-mail": "E-mail", + "E-mail": "Courriel", "Preferences": "Préférences", "preferences_category_player": "Préférences du lecteur", "preferences_video_loop_label": "Lire en boucle : ", @@ -128,8 +130,8 @@ "Subscription manager": "Gestionnaire d'abonnement", "Token manager": "Gestionnaire de token", "Token": "Token", - "tokens_count": "{{count}} token", - "tokens_count_plural": "{{count}} tokens", + "tokens_count": "{{count}} jeton", + "tokens_count_plural": "{{count}} jetons", "Import/export": "Importer/Exporter", "unsubscribe": "se désabonner", "revoke": "révoquer", @@ -482,5 +484,7 @@ "Music in this video": "Musique dans cette vidéo", "Channel Sponsor": "Soutien de la chaîne", "Download is disabled": "Le téléchargement est désactivé", - "Import YouTube playlist (.csv)": "Importer des listes de lecture de Youtube (.csv)" + "Import YouTube playlist (.csv)": "Importer des listes de lecture de Youtube (.csv)", + "channel_tab_releases_label": "Parutions", + "channel_tab_podcasts_label": "Émissions audio" } diff --git a/locales/hi.json b/locales/hi.json index dcb7294d..21807c50 100644 --- a/locales/hi.json +++ b/locales/hi.json @@ -471,5 +471,18 @@ "channel_tab_shorts_label": "शॉर्ट्स", "channel_tab_streams_label": "लाइवस्ट्रीम्स", "channel_tab_playlists_label": "प्लेलिस्ट्स", - "channel_tab_channels_label": "चैनल्स" + "channel_tab_channels_label": "चैनल्स", + "generic_button_save": "सहेजें", + "generic_button_cancel": "रद्द करें", + "generic_button_rss": "आरएसएस", + "generic_button_edit": "संपादित करें", + "generic_button_delete": "मिटाएं", + "playlist_button_add_items": "वीडियो जोड़ें", + "Song: ": "गाना: ", + "channel_tab_podcasts_label": "पाॅडकास्ट", + "channel_tab_releases_label": "रिलीज़ेस्", + "Import YouTube playlist (.csv)": "YouTube प्लेलिस्ट (.csv) आयात करें", + "Standard YouTube license": "मानक यूट्यूब लाइसेंस", + "Channel Sponsor": "चैनल प्रायोजक", + "Download is disabled": "डाउनलोड करना अक्षम है" } diff --git a/locales/hr.json b/locales/hr.json index 0549fa70..ba3dd5e5 100644 --- a/locales/hr.json +++ b/locales/hr.json @@ -492,5 +492,13 @@ "Song: ": "Pjesma: ", "Standard YouTube license": "Standardna YouTube licenca", "Download is disabled": "Preuzimanje je deaktivirano", - "Import YouTube playlist (.csv)": "Uvezi YouTube zbirku (.csv)" + "Import YouTube playlist (.csv)": "Uvezi YouTube zbirku (.csv)", + "generic_button_delete": "Izbriši", + "playlist_button_add_items": "Dodaj videa", + "channel_tab_podcasts_label": "Podcasti", + "generic_button_edit": "Uredi", + "generic_button_save": "Spremi", + "generic_button_cancel": "Odustani", + "generic_button_rss": "RSS", + "channel_tab_releases_label": "Izdanja" } diff --git a/locales/it.json b/locales/it.json index a3d0f5da..894eb97f 100644 --- a/locales/it.json +++ b/locales/it.json @@ -13,7 +13,7 @@ "View playlist on YouTube": "Vedi playlist su YouTube", "newest": "più recente", "oldest": "più vecchio", - "popular": "Tendenze", + "popular": "popolare", "last": "ultimo", "Next page": "Pagina successiva", "Previous page": "Pagina precedente", @@ -467,7 +467,7 @@ "channel_tab_shorts_label": "Short", "channel_tab_playlists_label": "Playlist", "channel_tab_channels_label": "Canali", - "channel_tab_streams_label": "Livestream", + "channel_tab_streams_label": "Trasmissioni in diretta", "channel_tab_community_label": "Comunità", "Music in this video": "Musica in questo video", "Artist: ": "Artista: ", @@ -476,5 +476,13 @@ "Song: ": "Canzone: ", "Standard YouTube license": "Licenza standard di YouTube", "Channel Sponsor": "Sponsor del canale", - "Import YouTube playlist (.csv)": "Importa playlist di YouTube (.csv)" + "Import YouTube playlist (.csv)": "Importa playlist di YouTube (.csv)", + "generic_button_edit": "Modifica", + "generic_button_cancel": "Annulla", + "generic_button_rss": "RSS", + "channel_tab_releases_label": "Pubblicazioni", + "generic_button_delete": "Elimina", + "generic_button_save": "Salva", + "playlist_button_add_items": "Aggiungi video", + "channel_tab_podcasts_label": "Podcast" } diff --git a/locales/ja.json b/locales/ja.json index 8adcbf6a..6fc02e2d 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -81,7 +81,7 @@ "preferences_category_subscription": "登録チャンネル設定", "preferences_annotations_subscribed_label": "最初から登録チャンネルのアノテーションを表示 ", "Redirect homepage to feed: ": "ホームからフィードにリダイレクト: ", - "preferences_max_results_label": "フィードに表示する動画の量: ", + "preferences_max_results_label": "フィードに表示する動画数: ", "preferences_sort_label": "動画を並び替え: ", "published": "投稿日", "published - reverse": "投稿日 - 逆順", @@ -366,13 +366,13 @@ "next_steps_error_message": "下記のものを試して下さい: ", "next_steps_error_message_refresh": "再読込", "next_steps_error_message_go_to_youtube": "YouTubeへ", - "search_filters_duration_option_short": "4 分未満", + "search_filters_duration_option_short": "4分未満", "footer_documentation": "説明書", "footer_source_code": "ソースコード", "footer_original_source_code": "元のソースコード", "footer_modfied_source_code": "改変して使用", "adminprefs_modified_source_code_url_label": "改変されたソースコードのレポジトリのURL", - "search_filters_duration_option_long": "20 分以上", + "search_filters_duration_option_long": "20分以上", "preferences_region_label": "地域: ", "footer_donate_page": "寄付する", "preferences_quality_dash_label": "優先するDASH画質: ", @@ -443,7 +443,7 @@ "search_filters_date_option_none": "すべて", "search_filters_type_option_all": "すべての種類", "search_filters_duration_option_none": "すべての長さ", - "search_filters_duration_option_medium": "4 ~ 20 分", + "search_filters_duration_option_medium": "4 ~ 20分", "preferences_save_player_pos_label": "再生位置を保存: ", "crash_page_before_reporting": "バグを報告する前に、次のことを確認してください。", "crash_page_report_issue": "上記が助けにならないなら、GitHub に新しい issue を作成し(英語が好ましい)、メッセージに次のテキストを含めてください(テキストは翻訳しない)。", @@ -460,5 +460,13 @@ "Channel Sponsor": "チャンネルのスポンサー", "Standard YouTube license": "標準 Youtube ライセンス", "Download is disabled": "ダウンロード: このインスタンスでは未対応", - "Import YouTube playlist (.csv)": "YouTube 再生リストをインポート (.csv)" + "Import YouTube playlist (.csv)": "YouTube 再生リストをインポート (.csv)", + "generic_button_delete": "削除", + "generic_button_cancel": "キャンセル", + "channel_tab_podcasts_label": "ポッドキャスト", + "channel_tab_releases_label": "リリース", + "generic_button_edit": "編集", + "generic_button_save": "保存", + "generic_button_rss": "RSS", + "playlist_button_add_items": "動画を追加" } diff --git a/locales/ko.json b/locales/ko.json index 9c8db5a1..e02a8316 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -460,5 +460,13 @@ "Music in this video": "동영상 속 음악", "Artist: ": "아티스트: ", "Download is disabled": "다운로드가 비활성화 되어있음", - "Import YouTube playlist (.csv)": "유튜브 플레이리스트 가져오기 (.csv)" + "Import YouTube playlist (.csv)": "유튜브 플레이리스트 가져오기 (.csv)", + "playlist_button_add_items": "동영상 추가", + "channel_tab_podcasts_label": "팟캐스트", + "generic_button_delete": "삭제", + "generic_button_edit": "편집", + "generic_button_save": "저장", + "generic_button_cancel": "취소", + "generic_button_rss": "RSS", + "channel_tab_releases_label": "출시" } diff --git a/locales/la.json b/locales/la.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/locales/la.json @@ -0,0 +1 @@ +{} diff --git a/locales/nb-NO.json b/locales/nb-NO.json index 1e0e9e77..216b559f 100644 --- a/locales/nb-NO.json +++ b/locales/nb-NO.json @@ -154,7 +154,7 @@ "View YouTube comments": "Vis YouTube-kommentarer", "View more comments on Reddit": "Vis flere kommenterer på Reddit", "View `x` comments": { - "([^.,0-9]|^)1([^.,0-9]|$)": "Vis `x` kommentarer", + "([^.,0-9]|^)1([^.,0-9]|$)": "Vis `x` kommentar", "": "Vis `x` kommentarer" }, "View Reddit comments": "Vis Reddit-kommentarer", @@ -476,5 +476,13 @@ "Album: ": "Album: ", "Download is disabled": "Nedlasting er avskrudd", "Channel Sponsor": "Kanalsponsor", - "Import YouTube playlist (.csv)": "Importer YouTube-spilleliste (.csv)" + "Import YouTube playlist (.csv)": "Importer YouTube-spilleliste (.csv)", + "channel_tab_podcasts_label": "Podkaster", + "channel_tab_releases_label": "Utgaver", + "generic_button_delete": "Slett", + "generic_button_edit": "Endre", + "generic_button_save": "Lagre", + "generic_button_cancel": "Avbryt", + "generic_button_rss": "RSS", + "playlist_button_add_items": "Legg til videoer" } diff --git a/locales/or.json b/locales/or.json index 0967ef42..948610f1 100644 --- a/locales/or.json +++ b/locales/or.json @@ -1 +1,29 @@ -{} +{ + "preferences_quality_dash_option_720p": "୭୨୦ପି", + "preferences_quality_dash_option_4320p": "୪୩୨୦ପି", + "preferences_quality_dash_option_240p": "୨୪୦ପି", + "preferences_quality_dash_option_2160p": "୨୧୬୦ପି", + "preferences_quality_dash_option_144p": "୧୪୪ପି", + "reddit": "Reddit", + "preferences_quality_dash_option_480p": "୪୮୦ପି", + "preferences_dark_mode_label": "ଥିମ୍: ", + "dark": "ଗାଢ଼", + "published": "ପ୍ରକାଶିତ", + "generic_videos_count": "{{count}}ଟିଏ ଵିଡ଼ିଓ", + "generic_videos_count_plural": "{{count}}ଟି ଵିଡ଼ିଓ", + "generic_button_edit": "ସମ୍ପାଦନା", + "light": "ହାଲୁକା", + "last": "ଗତ", + "New password": "ନୂଆ ପାସ୍‌ୱର୍ଡ଼", + "preferences_quality_dash_option_1440p": "୧୪୪୦ପି", + "preferences_quality_dash_option_360p": "୩୬୦ପି", + "preferences_quality_option_medium": "ମଧ୍ୟମ", + "preferences_quality_dash_option_1080p": "୧୦୮୦ପି", + "youtube": "YouTube", + "preferences_quality_option_hd720": "HD୭୨୦", + "invidious": "Invidious", + "generic_playlists_count": "{{count}}ଟିଏ ଚାଳନାତାଲିକା", + "generic_playlists_count_plural": "{{count}}ଟି ଚାଳନାତାଲିକା", + "Yes": "ହଁ", + "No": "ନାହିଁ" +} diff --git a/locales/pl.json b/locales/pl.json index e237db8b..f1924c8a 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -148,12 +148,12 @@ "Blacklisted regions: ": "Niedostępny na obszarach: ", "Shared `x`": "Udostępniono `x`", "Premieres in `x`": "Publikacja za `x`", - "Premieres `x`": "Publikacja za `x`", + "Premieres `x`": "Publikacja `x`", "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "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", "View `x` comments": { - "([^.,0-9]|^)1([^.,0-9]|$)": "Wyświetl `x` komentarzy", + "([^.,0-9]|^)1([^.,0-9]|$)": "Wyświetl `x` komentarz", "": "Wyświetl `x` komentarzy" }, "View Reddit comments": "Wyświetl komentarze z Redditta", @@ -492,5 +492,13 @@ "Song: ": "Piosenka: ", "Channel Sponsor": "Sponsor kanału", "Standard YouTube license": "Standardowa licencja YouTube", - "Import YouTube playlist (.csv)": "Importuj playlistę YouTube (.csv)" + "Import YouTube playlist (.csv)": "Importuj playlistę YouTube (.csv)", + "generic_button_edit": "Edytuj", + "generic_button_cancel": "Anuluj", + "generic_button_rss": "RSS", + "channel_tab_podcasts_label": "Podkasty", + "channel_tab_releases_label": "Wydania", + "generic_button_delete": "Usuń", + "generic_button_save": "Zapisz", + "playlist_button_add_items": "Dodaj filmy" } diff --git a/locales/pt-BR.json b/locales/pt-BR.json index 81290398..68a6e3ab 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -475,6 +475,14 @@ "Standard YouTube license": "Licença padrão do YouTube", "Song: ": "Música: ", "Channel Sponsor": "Patrocinador do Canal", - "Download is disabled": "Download está desativado", - "Import YouTube playlist (.csv)": "Importar lista de reprodução do YouTube (.csv)" + "Download is disabled": "Download está desabilitado", + "Import YouTube playlist (.csv)": "Importar lista de reprodução do YouTube (.csv)", + "generic_button_delete": "Apagar", + "generic_button_save": "Salvar", + "generic_button_edit": "Editar", + "playlist_button_add_items": "Adicionar vídeos", + "channel_tab_releases_label": "Lançamentos", + "channel_tab_podcasts_label": "Podcasts", + "generic_button_cancel": "Cancelar", + "generic_button_rss": "RSS" } diff --git a/locales/pt.json b/locales/pt.json index dfa411c3..e7cc4810 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -19,7 +19,7 @@ "search_filters_features_option_hdr": "HDR", "search_filters_features_option_location": "Localização", "search_filters_features_option_four_k": "4K", - "search_filters_features_option_live": "Em direto", + "search_filters_features_option_live": "Ao Vivo", "search_filters_features_option_three_d": "3D", "search_filters_features_option_c_commons": "Creative Commons", "search_filters_features_option_subtitles": "Legendas", @@ -365,7 +365,7 @@ "Subscribe": "Subscrever", "Unsubscribe": "Anular subscrição", "Shared `x` ago": "Partilhado `x` atrás", - "LIVE": "Em direto", + "LIVE": "AO VIVO", "search_filters_duration_option_short": "Curto (< 4 minutos)", "search_filters_duration_option_long": "Longo (> 20 minutos)", "footer_source_code": "Código-fonte", @@ -476,5 +476,13 @@ "Channel Sponsor": "Patrocinador do canal", "Standard YouTube license": "Licença padrão do YouTube", "Download is disabled": "A descarga está desativada", - "Import YouTube playlist (.csv)": "Importar lista de reprodução do YouTube (.csv)" + "Import YouTube playlist (.csv)": "Importar lista de reprodução do YouTube (.csv)", + "generic_button_delete": "Deletar", + "generic_button_edit": "Editar", + "generic_button_rss": "RSS", + "channel_tab_podcasts_label": "Podcasts", + "channel_tab_releases_label": "Lançamentos", + "generic_button_save": "Salvar", + "generic_button_cancel": "Cancelar", + "playlist_button_add_items": "Adicionar vídeos" } diff --git a/locales/ru.json b/locales/ru.json index a93207ad..5325a9b6 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -492,5 +492,13 @@ "Standard YouTube license": "Стандартная лицензия YouTube", "Channel Sponsor": "Спонсор канала", "Download is disabled": "Загрузка отключена", - "Import YouTube playlist (.csv)": "Импорт плейлиста YouTube (.csv)" + "Import YouTube playlist (.csv)": "Импорт плейлиста YouTube (.csv)", + "channel_tab_releases_label": "Релизы", + "generic_button_delete": "Удалить", + "generic_button_edit": "Редактировать", + "generic_button_save": "Сохранить", + "generic_button_cancel": "Отменить", + "generic_button_rss": "RSS", + "playlist_button_add_items": "Добавить видео", + "channel_tab_podcasts_label": "Подкасты" } diff --git a/locales/si.json b/locales/si.json index 19f34fac..4637cbd2 100644 --- a/locales/si.json +++ b/locales/si.json @@ -89,7 +89,7 @@ "preferences_quality_option_hd720": "HD720", "preferences_quality_dash_option_auto": "ස්වයංක්‍රීය", "preferences_quality_option_small": "කුඩා", - "preferences_quality_dash_option_best": "උසස්", + "preferences_quality_dash_option_best": "හොඳම", "preferences_quality_dash_option_2160p": "2160p", "preferences_quality_dash_option_1440p": "1440p", "preferences_quality_dash_option_720p": "720p", @@ -119,5 +119,9 @@ "Only show latest unwatched video from channel: ": "නාලිකාවේ නවතම නැරඹන නොලද වීඩියෝව පමණක් පෙන්වන්න: ", "preferences_category_data": "දත්ත මනාප", "Clear watch history": "නැරඹුම් ඉතිහාසය මකාදැමීම", - "Subscriptions": "දායකත්ව" + "Subscriptions": "දායකත්ව", + "generic_button_rss": "RSS", + "generic_button_save": "සුරකින්න", + "generic_button_cancel": "අවලංගු කරන්න", + "preferences_quality_dash_option_worst": "නරකම" } diff --git a/locales/sk.json b/locales/sk.json index 7346dc58..8add0f57 100644 --- a/locales/sk.json +++ b/locales/sk.json @@ -9,7 +9,7 @@ "last": "posledné", "Next page": "Ďalšia strana", "Previous page": "Predchádzajúca strana", - "Clear watch history?": "Vymazať históriu sledovania?", + "Clear watch history?": "Vymazať históriu pozerania?", "New password": "Nové heslo", "New passwords must match": "Nové heslá sa musia zhodovať", "Authorize token?": "Autorizovať token?", @@ -99,5 +99,23 @@ "generic_subscriptions_count_1": "{{count}} odbery", "generic_subscriptions_count_2": "{{count}} odberov", "Authorize token for `x`?": "Autorizovať token pre `x`?", - "View playlist on YouTube": "Zobraziť playlist na YouTube" + "View playlist on YouTube": "Zobraziť playlist na YouTube", + "preferences_quality_dash_option_best": "Najlepšia", + "preferences_quality_dash_option_worst": "Najhoršia", + "preferences_quality_dash_option_1440p": "1440p", + "preferences_quality_dash_option_720p": "720p", + "preferences_quality_option_hd720": "HD720", + "preferences_quality_dash_label": "Preferovaná video kvalita DASH: ", + "preferences_quality_option_dash": "DASH (adaptívna kvalita)", + "preferences_quality_option_small": "Malá", + "preferences_watch_history_label": "Zapnúť históriu pozerania: ", + "preferences_quality_dash_option_240p": "240p", + "preferences_quality_dash_option_1080p": "1080p", + "preferences_quality_dash_option_480p": "480p", + "preferences_quality_dash_option_auto": "Auto", + "preferences_quality_dash_option_144p": "144p", + "preferences_quality_dash_option_2160p": "2160p", + "invidious": "Invidious", + "preferences_quality_dash_option_4320p": "4320p", + "preferences_quality_dash_option_360p": "360p" } diff --git a/locales/sl.json b/locales/sl.json index 45f63c6b..fec1cb62 100644 --- a/locales/sl.json +++ b/locales/sl.json @@ -222,7 +222,7 @@ "search_filters_date_option_week": "Ta teden", "search_filters_type_label": "Vrsta", "search_filters_type_option_all": "Katerakoli vrsta", - "search_filters_type_option_playlist": "Seznami predvajanja", + "search_filters_type_option_playlist": "Seznam predvajanja", "search_filters_features_option_subtitles": "Podnapisi/CC", "search_filters_features_option_location": "Lokacija", "footer_donate_page": "Prispevaj", @@ -508,5 +508,13 @@ "Standard YouTube license": "Standardna licenca YouTube", "Channel Sponsor": "Sponzor kanala", "Download is disabled": "Prenos je onemogočen", - "Import YouTube playlist (.csv)": "Uvoz seznama predvajanja YouTube (.csv)" + "Import YouTube playlist (.csv)": "Uvoz seznama predvajanja YouTube (.csv)", + "generic_button_delete": "Izbriši", + "generic_button_edit": "Uredi", + "generic_button_save": "Shrani", + "generic_button_cancel": "Prekliči", + "generic_button_rss": "RSS", + "playlist_button_add_items": "Dodaj videoposnetke", + "channel_tab_podcasts_label": "Poddaje", + "channel_tab_releases_label": "Izdaje" } diff --git a/locales/tr.json b/locales/tr.json index 22732a51..7f3f2de8 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -476,5 +476,13 @@ "Song: ": "Şarkı: ", "Standard YouTube license": "Standart YouTube lisansı", "Download is disabled": "İndirme devre dışı", - "Import YouTube playlist (.csv)": "YouTube Oynatma Listesini İçe Aktar (.csv)" + "Import YouTube playlist (.csv)": "YouTube Oynatma Listesini İçe Aktar (.csv)", + "generic_button_delete": "Sil", + "generic_button_edit": "Düzenle", + "generic_button_save": "Kaydet", + "generic_button_cancel": "İptal", + "generic_button_rss": "RSS", + "channel_tab_releases_label": "Yayınlar", + "playlist_button_add_items": "Video ekle", + "channel_tab_podcasts_label": "Podcast'ler" } diff --git a/locales/uk.json b/locales/uk.json index 308b10ca..4d8f06a5 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -492,5 +492,13 @@ "Channel Sponsor": "Спонсор каналу", "Standard YouTube license": "Стандартна ліцензія YouTube", "Download is disabled": "Завантаження вимкнено", - "Import YouTube playlist (.csv)": "Імпорт списку відтворення YouTube (.csv)" + "Import YouTube playlist (.csv)": "Імпорт списку відтворення YouTube (.csv)", + "channel_tab_podcasts_label": "Подкасти", + "playlist_button_add_items": "Додати відео", + "generic_button_cancel": "Скасувати", + "generic_button_rss": "RSS", + "channel_tab_releases_label": "Випуски", + "generic_button_delete": "Видалити", + "generic_button_edit": "Змінити", + "generic_button_save": "Зберегти" } diff --git a/locales/vi.json b/locales/vi.json index d79c684c..9cb87d3e 100644 --- a/locales/vi.json +++ b/locales/vi.json @@ -2,7 +2,7 @@ "generic_videos_count_0": "{{count}} video", "generic_subscribers_count_0": "{{count}} người theo dõi", "LIVE": "TRỰC TIẾP", - "Shared `x` ago": "Đã chia sẻ` x` trước", + "Shared `x` ago": "Đã chia sẻ `x` trước", "Unsubscribe": "Hủy theo dõi", "Subscribe": "Theo dõi", "View channel on YouTube": "Xem kênh trên YouTube", @@ -71,7 +71,7 @@ "Dark mode: ": "Chế độ tối: ", "preferences_dark_mode_label": "Chủ đề: ", "dark": "tối", - "light": "ánh sáng", + "light": "sáng", "preferences_thin_mode_label": "Chế độ mỏng: ", "preferences_category_misc": "Tùy chọn khác", "preferences_automatic_instance_redirect_label": "Tự động chuyển hướng phiên bản (dự phòng về redirect.invidious.io): ", @@ -120,7 +120,7 @@ "View JavaScript license information.": "Xem thông tin giấy phép JavaScript.", "View privacy policy.": "Xem chính sách bảo mật.", "Trending": "Xu hướng", - "Public": "Công cộng", + "Public": "Công khai", "Unlisted": "Không hiển thị", "Private": "Riêng tư", "View all playlists": "Xem tất cả danh sách phát", @@ -182,17 +182,17 @@ "Amharic": "Amharic", "Arabic": "Tiếng Ả Rập", "Armenian": "Tiếng Armenia", - "Azerbaijani": "Azerbaijan", - "Bangla": "Bangla", + "Azerbaijani": "Tiếng Azerbaijan", + "Bangla": "Tiếng Bengal", "Basque": "Tiếng Basque", - "Belarusian": "Người Belarus", + "Belarusian": "Tiếng Belarus", "Bosnian": "Tiếng Bosnia", "Bulgarian": "Tiếng Bungari", "Burmese": "Tiếng Miến Điện", "Catalan": "Tiếng Catalan", "Cebuano": "Cebuano", "Chinese (Simplified)": "Tiếng Trung (Giản thể)", - "Chinese (Traditional)": "Truyền thống Trung Hoa)", + "Chinese (Traditional)": "Tiếng Trung (Phồn thể)", "Corsican": "Corsican", "Croatian": "Tiếng Croatia", "Czech": "Tiếng Séc", @@ -219,22 +219,22 @@ "Igbo": "Igbo", "Indonesian": "Tiếng Indonesia", "Irish": "Tiếng Ailen", - "Italian": "Người Ý", + "Italian": "Tiếng Ý", "Japanese": "Tiếng Nhật", "Javanese": "Tiếng Java", "Kannada": "Tiếng Kannada", "Kazakh": "Tiếng Kazakh", "Khmer": "Tiếng Khmer", - "Korean": "Hàn Quốc", + "Korean": "Tiếng Hàn", "Kurdish": "Tiếng Kurd", - "Kyrgyz": "Kyrgyz", - "Lao": "Lào", - "Latin": "Latin", + "Kyrgyz": "Tiếng Kyrgyz", + "Lao": "Tiếng Lào", + "Latin": "Tiếng Latin", "Latvian": "Tiếng Latvia", "Lithuanian": "Tiếng Litva", "Luxembourgish": "Tiếng Luxembourg", - "Macedonian": "Người Macedonian", - "Malagasy": "Malagasy", + "Macedonian": "Tiếng Macedonian", + "Malagasy": "Tiếng Malagasy", "Malay": "Tiếng Mã Lai", "Malayalam": "Tiếng Malayalam", "Maltese": "Cây nho", @@ -364,7 +364,7 @@ "Import/export": "Xuất/nhập dữ liệu", "preferences_quality_dash_option_4320p": "4320p", "preferences_quality_option_dash": "DASH (tự tối ưu chất lượng)", - "generic_subscriptions_count_0": "{{count}} thuê bao", + "generic_subscriptions_count_0": "{{count}} người đăng kí", "preferences_quality_dash_option_1440p": "1440p", "preferences_quality_dash_option_480p": "480p", "preferences_quality_dash_option_2160p": "2160p", @@ -383,5 +383,9 @@ "Standard YouTube license": "Giấy phép YouTube thông thường", "Album: ": "Album: ", "preferences_save_player_pos_label": "Lưu vị trí xem cuối cùng ", - "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Xin chào! Có vẻ như bạn đã tắt JavaScript. Bấm vào đây để xem bình luận, lưu ý rằng thời gian tải có thể lâu hơn." + "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Xin chào! Có vẻ như bạn đã tắt JavaScript. Bấm vào đây để xem bình luận, lưu ý rằng thời gian tải có thể lâu hơn.", + "Chinese (China)": "Tiếng Trung (Trung Quốc)", + "generic_button_cancel": "Hủy", + "Chinese": "Tiếng Trung", + "generic_button_delete": "Xóa" } diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 58b834fa..62f45a29 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -460,5 +460,13 @@ "Channel Sponsor": "频道赞助者", "Standard YouTube license": "标准 YouTube 许可证", "Download is disabled": "已禁用下载", - "Import YouTube playlist (.csv)": "导入 YouTube 播放列表(.csv)" + "Import YouTube playlist (.csv)": "导入 YouTube 播放列表(.csv)", + "generic_button_cancel": "取消", + "playlist_button_add_items": "添加视频", + "generic_button_delete": "删除", + "channel_tab_podcasts_label": "播客", + "generic_button_edit": "编辑", + "generic_button_save": "保存", + "generic_button_rss": "RSS", + "channel_tab_releases_label": "公告" } diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 7da2d762..da81922b 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -460,5 +460,13 @@ "Song: ": "歌曲: ", "Standard YouTube license": "標準 YouTube 授權條款", "Download is disabled": "已停用下載", - "Import YouTube playlist (.csv)": "匯入 YouTube 播放清單 (.csv)" + "Import YouTube playlist (.csv)": "匯入 YouTube 播放清單 (.csv)", + "generic_button_cancel": "取消", + "generic_button_edit": "編輯", + "generic_button_save": "儲存", + "generic_button_rss": "RSS", + "generic_button_delete": "刪除", + "playlist_button_add_items": "新增影片", + "channel_tab_podcasts_label": "Podcast", + "channel_tab_releases_label": "發布" } diff --git a/src/invidious/helpers/serialized_yt_data.cr b/src/invidious/helpers/serialized_yt_data.cr index 7c12ad0e..e0bd7279 100644 --- a/src/invidious/helpers/serialized_yt_data.cr +++ b/src/invidious/helpers/serialized_yt_data.cr @@ -232,6 +232,25 @@ struct SearchChannel end end +struct SearchHashtag + include DB::Serializable + + property title : String + property url : String + property video_count : Int64 + property channel_count : Int64 + + def to_json(locale : String?, json : JSON::Builder) + json.object do + json.field "type", "hashtag" + json.field "title", self.title + json.field "url", self.url + json.field "videoCount", self.video_count + json.field "channelCount", self.channel_count + end + end +end + class Category include DB::Serializable @@ -274,4 +293,4 @@ struct Continuation end end -alias SearchItem = SearchVideo | SearchChannel | SearchPlaylist | Category +alias SearchItem = SearchVideo | SearchChannel | SearchPlaylist | SearchHashtag | Category diff --git a/src/invidious/routes/errors.cr b/src/invidious/routes/errors.cr index b138b562..1e9ab44e 100644 --- a/src/invidious/routes/errors.cr +++ b/src/invidious/routes/errors.cr @@ -1,5 +1,10 @@ module Invidious::Routes::ErrorRoutes def self.error_404(env) + # Workaround for #3117 + if HOST_URL.empty? && env.request.path.starts_with?("/v1/storyboards/sb") + return env.redirect "#{env.request.path[15..]}?#{env.params.query}" + end + if md = env.request.path.match(/^\/(?([a-zA-Z0-9_-]{11})|(\w+))$/) item = md["id"] diff --git a/src/invidious/user/imports.cr b/src/invidious/user/imports.cr index 0a2fe1e2..86d0ce6e 100644 --- a/src/invidious/user/imports.cr +++ b/src/invidious/user/imports.cr @@ -133,7 +133,7 @@ struct Invidious::User next if !video_id begin - video = get_video(video_id) + video = get_video(video_id, false) rescue ex next end diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index 2a09d187..06ff96b1 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -55,9 +55,7 @@ def extract_video_info(video_id : String, proxy_region : String? = nil) client_config = YoutubeAPI::ClientConfig.new(proxy_region: proxy_region) # Fetch data from the player endpoint - # CgIQBg is a workaround for streaming URLs that returns a 403. - # See https://github.com/iv-org/invidious/issues/4027#issuecomment-1666944520 - player_response = YoutubeAPI.player(video_id: video_id, params: "CgIQBg", client_config: client_config) + player_response = YoutubeAPI.player(video_id: video_id, params: "", client_config: client_config) playability_status = player_response.dig?("playabilityStatus", "status").try &.as_s @@ -120,6 +118,9 @@ def extract_video_info(video_id : String, proxy_region : String? = nil) # Replace player response and reset reason if !new_player_response.nil? + # Preserve storyboard data before replacement + new_player_response["storyboards"] = player_response["storyboards"] if player_response["storyboards"]? + player_response = new_player_response params.delete("reason") end diff --git a/src/invidious/views/components/item.ecr b/src/invidious/views/components/item.ecr index 7ffd2d93..c29ec47b 100644 --- a/src/invidious/views/components/item.ecr +++ b/src/invidious/views/components/item.ecr @@ -1,6 +1,6 @@ <%- thin_mode = env.get("preferences").as(Preferences).thin_mode - item_watched = !item.is_a?(SearchChannel | SearchPlaylist | InvidiousPlaylist | Category) && env.get?("user").try &.as(User).watched.index(item.id) != nil + item_watched = !item.is_a?(SearchChannel | SearchHashtag | SearchPlaylist | InvidiousPlaylist | Category) && env.get?("user").try &.as(User).watched.index(item.id) != nil author_verified = item.responds_to?(:author_verified) && item.author_verified -%> @@ -29,6 +29,30 @@

<%= translate_count(locale, "generic_subscribers_count", item.subscriber_count, NumberFormatting::Separator) %>

<% if !item.auto_generated %>

<%= translate_count(locale, "generic_videos_count", item.video_count, NumberFormatting::Separator) %>

<% end %>
<%= item.description_html %>
+ <% when SearchHashtag %> + <% if !thin_mode %> + +
+
+ <%- else -%> +
+ <% end %> + +
+ +
+ +
+ <%- if item.video_count != 0 -%> +

<%= translate_count(locale, "generic_videos_count", item.video_count, NumberFormatting::Separator) %>

+ <%- end -%> +
+ +
+ <%- if item.channel_count != 0 -%> +

<%= translate_count(locale, "generic_channels_count", item.channel_count, NumberFormatting::Separator) %>

+ <%- end -%> +
<% when SearchPlaylist, InvidiousPlaylist %> <%- if item.id.starts_with? "RD" diff --git a/src/invidious/yt_backend/extractors.cr b/src/invidious/yt_backend/extractors.cr index 8cf59d50..aaf7772e 100644 --- a/src/invidious/yt_backend/extractors.cr +++ b/src/invidious/yt_backend/extractors.cr @@ -11,15 +11,16 @@ private ITEM_CONTAINER_EXTRACTOR = { } private ITEM_PARSERS = { + Parsers::RichItemRendererParser, Parsers::VideoRendererParser, Parsers::ChannelRendererParser, Parsers::GridPlaylistRendererParser, Parsers::PlaylistRendererParser, Parsers::CategoryRendererParser, - Parsers::RichItemRendererParser, Parsers::ReelItemRendererParser, Parsers::ItemSectionRendererParser, Parsers::ContinuationItemRendererParser, + Parsers::HashtagRendererParser, } private alias InitialData = Hash(String, JSON::Any) @@ -210,6 +211,56 @@ private module Parsers end end + # Parses an Innertube `hashtagTileRenderer` into a `SearchHashtag`. + # Returns `nil` when the given object is not a `hashtagTileRenderer`. + # + # A `hashtagTileRenderer` is a kind of search result. + # It can be found when searching for any hashtag (e.g "#hi" or "#shorts") + module HashtagRendererParser + def self.process(item : JSON::Any, author_fallback : AuthorFallback) + if item_contents = item["hashtagTileRenderer"]? + return self.parse(item_contents) + end + end + + private def self.parse(item_contents) + title = extract_text(item_contents["hashtag"]).not_nil! # E.g "#hi" + + # E.g "/hashtag/hi" + url = item_contents.dig?("onTapCommand", "commandMetadata", "webCommandMetadata", "url").try &.as_s + url ||= URI.encode_path("/hashtag/#{title.lchop('#')}") + + video_count_txt = extract_text(item_contents["hashtagVideoCount"]?) # E.g "203K videos" + channel_count_txt = extract_text(item_contents["hashtagChannelCount"]?) # E.g "81K channels" + + # Fallback for video/channel counts + if channel_count_txt.nil? || video_count_txt.nil? + # E.g: "203K videos • 81K channels" + info_text = extract_text(item_contents["hashtagInfoText"]?).try &.split(" • ") + + if info_text && info_text.size == 2 + video_count_txt ||= info_text[0] + channel_count_txt ||= info_text[1] + end + end + + return SearchHashtag.new({ + title: title, + url: url, + video_count: short_text_to_number(video_count_txt || ""), + channel_count: short_text_to_number(channel_count_txt || ""), + }) + rescue ex + LOGGER.debug("HashtagRendererParser: Failed to extract renderer.") + LOGGER.debug("HashtagRendererParser: Got exception: #{ex.message}") + return nil + end + + def self.parser_name + return {{@type.name}} + end + end + # Parses a InnerTube gridPlaylistRenderer into a SearchPlaylist. Returns nil when the given object isn't a gridPlaylistRenderer # # A gridPlaylistRenderer renders a playlist, that is located in a grid, to click on within the YouTube and Invidious UI.