diff options
34 files changed, 730 insertions, 124 deletions
@@ -1,4 +1,4 @@ -/doc/ +/docs/ /dev/ /lib/ /bin/ @@ -149,12 +149,13 @@ Weblate also allows you to log-in with major SSO providers like Github, Gitlab, - [CloudTube](https://sr.ht/~cadence/tube/): A JavaScript-rich alternate YouTube player. - [PeerTubeify](https://gitlab.com/Cha_de_L/peertubeify): On YouTube, displays a link to the same video on PeerTube, if it exists. - [MusicPiped](https://github.com/deep-gaurav/MusicPiped): A material design music player that streams music from YouTube. -- [HoloPlay](https://github.com/stephane-r/HoloPlay): Funny Android application connecting on Invidious API's with search, playlists and favorites. +- [HoloPlay](https://github.com/stephane-r/holoplay-wa): Progressive Web App connecting on Invidious API's with search, playlists and favorites. - [WatchTube](https://github.com/WatchTubeTeam/WatchTube): Powerful YouTube client for Apple Watch. - [Yattee](https://github.com/yattee/yattee): Alternative YouTube frontend for iPhone, iPad, Mac and Apple TV. - [TubiTui](https://codeberg.org/777/TubiTui): A lightweight, libre, TUI-based YouTube client. - [Ytfzf](https://github.com/pystardust/ytfzf): A posix script to find and watch youtube videos from the terminal. (Without API) - [Playlet](https://github.com/iBicha/playlet): Unofficial Youtube client for Roku TV +- [Clipious](https://github.com/lamarios/clipious): Unofficial Invidious client for Android ## Liability diff --git a/assets/css/default.css b/assets/css/default.css index 3deaebe1..f8b1c9f7 100644 --- a/assets/css/default.css +++ b/assets/css/default.css @@ -515,7 +515,7 @@ hr { #descexpansionbutton ~ div { overflow: hidden; - height: 8.3em; + max-height: 8.3em; } #descexpansionbutton:checked ~ div { @@ -583,3 +583,7 @@ p, /* Wider settings name to less word wrap */ .pure-form-aligned .pure-control-group label { width: 19em; } + +.channel-emoji { + margin: 0 2px; +} diff --git a/locales/ar.json b/locales/ar.json index 181ff933..3ce34c2d 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -543,5 +543,7 @@ "channel_tab_channels_label": "القنوات", "Music in this video": "الموسيقى في هذا الفيديو", "Album: ": "الألبوم: ", - "Artist: ": "الفنان: " + "Artist: ": "الفنان: ", + "Song: ": "أغنية: ", + "Channel Sponsor": "راعي القناة" } diff --git a/locales/ca.json b/locales/ca.json index 2ba6ae39..54a0b177 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -75,7 +75,7 @@ "Title": "Títol", "Belarusian": "Bielorús", "Enable web notifications": "Activa notificacions web", - "search": "busca", + "search": "cerca", "Catalan": "Català", "Croatian": "Croat", "preferences_category_admin": "Preferències d'administrador", @@ -99,5 +99,387 @@ "Music": "Música", "search_filters_sort_option_relevance": "Rellevància", "search_filters_date_option_hour": "Última hora", - "search_filters_date_option_today": "Avui" + "search_filters_date_option_today": "Avui", + "preferences_volume_label": "Volum del reproductor: ", + "invidious": "Invidious", + "preferences_quality_dash_option_144p": "144p", + "Turkish (auto-generated)": "Turc (generat automàticament)", + "Urdu": "Urdú", + "Vietnamese (auto-generated)": "Vietnamita (generat automàticament)", + "Welsh": "Gal·lès", + "Yoruba": "Ioruba", + "YouTube comment permalink": "Enllaç permanent de comentari de YouTube", + "Channel Sponsor": "Patrocinador del canal", + "Audio mode": "Mode d'àudio", + "search_filters_date_option_none": "Qualsevol data", + "search_filters_type_option_playlist": "Llista de reproducció", + "search_filters_type_option_movie": "Pel·lícula", + "search_filters_features_option_four_k": "4K", + "search_filters_features_option_subtitles": "Subtítols/CC", + "search_filters_features_option_live": "Directe", + "search_filters_features_option_hd": "HD", + "search_filters_features_option_hdr": "HDR", + "search_filters_features_option_location": "Ubicació", + "search_filters_apply_button": "Aplica els filtres seleccionats", + "videoinfo_started_streaming_x_ago": "Ha començat el directe fa `x`", + "next_steps_error_message_go_to_youtube": "Anar a YouTube", + "footer_donate_page": "Donar", + "footer_original_source_code": "Codi font original", + "videoinfo_watch_on_youTube": "Veure a YouTube", + "user_saved_playlists": "`x` llistes de reproducció guardades", + "adminprefs_modified_source_code_url_label": "URL al repositori de codi font modificat", + "none": "cap", + "footer_modfied_source_code": "Codi font modificat", + "videoinfo_invidious_embed_link": "Incrusta l'enllaç", + "download_subtitles": "Subtítols - `x` (.vtt)", + "user_created_playlists": "`x`llistes de reproducció creades", + "Video unavailable": "Vídeo no disponible", + "channel_tab_channels_label": "Canals", + "channel_tab_playlists_label": "Llistes de reproducció", + "channel_tab_community_label": "Comunitat", + "Invalid TFA code": "Codi TFA no vàlid", + "Czech": "Txec", + "Default": "Per defecte", + "Amharic": "Amàric", + "preferences_automatic_instance_redirect_label": "Redirecció automàtica d'instàncies (retorna a redirect.invidious.io): ", + "Login enabled: ": "Activa inici de sessió: ", + "Registration enabled: ": "Activa registre: ", + "Whitelisted regions: ": "Regions a la llista blanca: ", + "Chinese (Simplified)": "Xinès (Simplificat)", + "Corsican": "Cors", + "Estonian": "Estonià", + "Japanese (auto-generated)": "Japonès (generat automàticament)", + "English (United States)": "Anglès (Estats Units)", + "English (auto-generated)": "Anglès (generat automàticament)", + "Cebuano": "Cebuà", + "Esperanto": "Esperanto", + "Scottish Gaelic": "Gaèlic escocès", + "Playlists": "Llistes de reproducció", + "search_filters_title": "Filtres", + "search_filters_type_option_all": "Qualsevol tipus", + "search_filters_duration_option_none": "Qualsevol duració", + "next_steps_error_message": "Després d'això, hauríeu d'intentar: ", + "next_steps_error_message_refresh": "Recarregar la pàgina", + "crash_page_refresh": "ha intentat <a href=\"`x`\">actualitzar la pàgina</a>", + "crash_page_report_issue": "Si cap de les anteriors no ha ajudat, <a href=\"`x`\">obre un nou issue a GitHub</a> (preferiblement en anglès) i inclou el text següent al missatge (NO tradueixis aquest text):", + "generic_subscriptions_count": "{{count}} subscripció", + "generic_subscriptions_count_plural": "{{count}} subscripcions", + "error_video_not_in_playlist": "El vídeo sol·licitat no existeix en aquesta llista de reproducció. <a href=\"`x`\">Fes clic aquí per a la pàgina d'inici de la llista de reproducció.</a>", + "comments_points_count": "{{count}} punt", + "comments_points_count_plural": "{{count}} punts", + "%A %B %-d, %Y": "%A %B %-d, %Y", + "Create playlist": "Crear llista de reproducció", + "Text CAPTCHA": "Text CAPTCHA", + "Next page": "Pàgina següent", + "preferences_category_visual": "Preferències visuals", + "preferences_unseen_only_label": "Mostra només no vistos: ", + "preferences_listen_label": "Escolta per defecte: ", + "Import": "Importar", + "Token": "Senyal", + "Wilson score: ": "Puntuació de Wilson: ", + "search_filters_date_label": "Data de càrrega", + "search_filters_features_option_three_sixty": "360°", + "source": "font", + "preferences_default_home_label": "Pàgina d'inici per defecte: ", + "preferences_comments_label": "Comentaris per defecte: ", + "`x` uploaded a video": "`x` ha penjat un vídeo", + "Released under the AGPLv3 on Github.": "Publicat sota l'AGPLv3 a GitHub.", + "Token manager": "Gestor de tokens", + "Watch history": "Historial de reproduccions", + "Cannot change password for Google accounts": "No es pot canviar la contrasenya dels comptes de Google", + "Authorize token?": "Autoritzar senyal?", + "Source available here.": "Font disponible aquí.", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Exporta subscripcions com a OPML (per a NewPipe i FreeTube)", + "Log in": "Inicia sessió", + "search_filters_sort_option_date": "Data de càrrega", + "Unlisted": "No llistat", + "View privacy policy.": "Veure política de privadesa.", + "Public": "Públic", + "View all playlists": "Veure totes les llistes de reproducció", + "reddit": "Reddit", + "Manage tokens": "Gestiona senyals", + "Not a playlist.": "No és una llista de reproducció.", + "preferences_local_label": "Vídeos de Proxy: ", + "View channel on YouTube": "Veure canal a Youtube", + "preferences_quality_dash_option_1080p": "1080p", + "Top enabled: ": "Activa top: ", + "Delete playlist `x`?": "Eliminar llista de reproducció `x`?", + "View JavaScript license information.": "Consulta la informació de la llicència de JavaScript.", + "Playlist privacy": "Privacitat de la llista de reproducció", + "search_message_no_results": "No s'han trobat resultats.", + "search_message_use_another_instance": " També es pot <a href=\"`x`\">buscar en una altra instància</a>.", + "Genre: ": "Gènere: ", + "Hidden field \"challenge\" is a required field": "El camp ocult \"repte\" és un camp obligatori", + "Burmese": "Birmà", + "View as playlist": "Mostra com a llista de reproducció", + "preferences_category_subscription": "Preferències de subscripció", + "Music in this video": "Música en aquest vídeo", + "Artist: ": "Artista: ", + "Album: ": "Àlbum: ", + "Shared `x`": "Compartit `x`", + "Premieres `x`": "Estrena `x`", + "View more comments on Reddit": "Veure més comentaris a Reddit", + "View `x` comments": { + "([^.,0-9]|^)1([^.,0-9]|$)": "Veure `x` comentari", + "": "Veure `x` comentaris" + }, + "View Reddit comments": "Veure comentaris de Reddit", + "Incorrect password": "Contrasenya incorrecta", + "Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "No es pot iniciar la sessió, assegureu-vos que l'autenticació de dos factors (Autenticador o SMS) estigui activada.", + "Erroneous CAPTCHA": "CAPTCHA erroni", + "CAPTCHA is a required field": "El CAPTCHA és un camp obligatori", + "Korean (auto-generated)": "Coreà (generat automàticament)", + "Kyrgyz": "Kirguís", + "Latin": "Llatí", + "Malagasy": "Malgaix", + "Maori": "Maori", + "Marathi": "Marathi", + "Norwegian Bokmål": "Bokmål Noruec", + "Nyanja": "Nyanja", + "Portuguese (Brazil)": "Portuguès (Brazil)", + "Punjabi": "Panjabi", + "Russian (auto-generated)": "Rus (generat automàticament)", + "Samoan": "Samoà", + "Somali": "Somali", + "Southern Sotho": "Sesotho", + "Spanish (Mexico)": "Espanyol (Mèxic)", + "Spanish (Spain)": "Espanyol (Espanya)", + "Sundanese": "Sondanès", + "Swahili": "Suahili", + "Tamil": "Tàmil", + "Telugu": "Telugu", + "Zulu": "Zulu", + "generic_count_months": "{{count}} mes", + "generic_count_months_plural": "{{count}} mesos", + "generic_count_weeks": "{{count}} setmana", + "generic_count_weeks_plural": "{{count}} setmanes", + "About": "Sobre", + "`x` marked it with a ❤": "`x`marca'l amb un ❤", + "Video mode": "Mode de vídeo", + "search_filters_features_label": "Característiques", + "search_filters_features_option_c_commons": "Creative Commons", + "search_filters_features_option_vr180": "VR180", + "search_filters_features_option_three_d": "3D", + "search_filters_features_option_purchased": "Comprat", + "Chinese (Hong Kong)": "Xinès (Hong Kong)", + "Chinese (Taiwan)": "Xinès (Taiwan)", + "Hmong": "Hmong", + "Kazakh": "Kazakh", + "Igbo": "Igbo", + "Javanese": "Javanès", + "Indonesian (auto-generated)": "Indonesi (generat automàticament)", + "Interlingue": "Interlingüe", + "Khmer": "Khmer", + "This channel does not exist.": "Aquest canal no existeix.", + "Song: ": "Cançó: ", + "Login failed. This may be because two-factor authentication is not turned on for your account.": "Error a l'iniciar sessió. Això pot ser perquè l'autenticació de dos factors no està activada per al vostre compte.", + "channel:`x`": "canal: `x`", + "Deleted or invalid channel": "Canal suprimit o no vàlid", + "Could not get channel info.": "No s'ha pogut obtenir la informació del canal.", + "Could not pull trending pages.": "No s'han pogut extreure les pàgines de tendència.", + "comments_view_x_replies": "Veure {{count}} resposta", + "comments_view_x_replies_plural": "Veure {{count}} respostes", + "Subscriptions": "Subscripcions", + "generic_count_seconds": "{{count}} segon", + "generic_count_seconds_plural": "{{count}} segons", + "channel_tab_shorts_label": "Vídeos curts", + "preferences_save_player_pos_label": "Desa la posició de reproducció: ", + "crash_page_before_reporting": "Abans d'informar d'un error, assegureu-vos que teniu:", + "crash_page_switch_instance": "ha intentat <a href=\"`x`\">utilitzar una altra instància</a>", + "crash_page_read_the_faq": "heu llegit les <a href=\"`x`\">Preguntes més freqüents (FAQ)</a>", + "crash_page_search_issue": "ha cercat <a href=\"`x`\">problemes existents a GitHub</a>", + "User ID is a required field": "L'identificador d'usuari és un camp obligatori", + "Password is a required field": "La contrasenya és un camp obligatori", + "Wrong username or password": "Nom d'usuari o contrasenya incorrectes", + "Please sign in using 'Log in with Google'": "Si us plau, inicieu la sessió amb 'Inicieu sessió amb Google'", + "Password cannot be longer than 55 characters": "La contrasenya no pot tenir més de 55 caràcters", + "Invidious Private Feed for `x`": "Feed privat Invidious per a `x`", + "generic_views_count": "{{count}} visualització", + "generic_views_count_plural": "{{count}} visualitzacions", + "generic_videos_count": "{{count}} vídeo", + "generic_videos_count_plural": "{{count}} vídeos", + "Token is expired, please try again": "La senyal ha caducat, torna-ho a provar", + "English": "Anglès", + "Kannada": "Kanarès", + "Erroneous token": "Senyal errònia", + "`x` ago": "fa `x`", + "Empty playlist": "Llista de reproducció buida", + "Playlist does not exist.": "La llista de reproducció no existeix.", + "No such user": "No hi ha tal usuari", + "Afrikaans": "Afrikàans", + "Azerbaijani": "Azerbaidjana", + "Cantonese (Hong Kong)": "Cantonès (Hong Kong)", + "Chinese": "Xinès", + "Chinese (China)": "Xinès (Xina)", + "Chinese (Traditional)": "Xinès (Tradicional)", + "Dutch": "Holandès", + "Dutch (auto-generated)": "Holandès (generat automàticament)", + "French (auto-generated)": "Francès (generat automàticament)", + "Georgian": "Georgià", + "German (auto-generated)": "Alemany (generat automàticament)", + "Gujarati": "Gujarati", + "Hawaiian": "Hawaià", + "generic_count_years": "{{count}} any", + "generic_count_years_plural": "{{count}} anys", + "Popular": "Popular", + "Rating: ": "Valoració: ", + "permalink": "enllaç permanent", + "preferences_quality_dash_option_worst": "Pitjor", + "Yiddish": "Ídix", + "preferences_quality_dash_option_auto": "Automàtic", + "Western Frisian": "Frisó occidental", + "Swedish": "Suec", + "Only show latest unwatched video from channel: ": "Mostra només l'últim vídeo no vist del canal: ", + "preferences_continue_label": "Reprodueix el següent per defecte: ", + "Import YouTube subscriptions": "Importar subscripcions de YouTube", + "search_filters_sort_option_rating": "Valoració", + "preferences_thin_mode_label": "Mode prim: ", + "preferences_quality_option_small": "Petit", + "CAPTCHA enabled: ": "activa CAPTCHA: ", + "Import and Export Data": "Importar i exportar dades", + "preferences_quality_dash_option_360p": "360p", + "Popular enabled: ": "Activa popular: ", + "Password": "Contrasenya", + "Blacklisted regions: ": "Regions a la llista negra: ", + "Register": "Registra't", + "Shared `x` ago": "Compartit fa `x`", + "search_filters_sort_option_views": "Recompte de visualitzacions", + "Import Invidious data": "Importa dades JSON d'Invidious", + "preferences_related_videos_label": "Mostra vídeos relacionats: ", + "preferences_show_nick_label": "Mostra l'àlies a la part superior: ", + "Time (h:mm:ss):": "Temps (h:mm:ss):", + "Could not fetch comments": "No s'han pogut obtenir els comentaris", + "New password": "Nova contrasenya", + "preferences_notifications_only_label": "Mostra només notificacions (si n'hi ha): ", + "preferences_annotations_label": "Mostra anotacions per defecte: ", + "Import FreeTube subscriptions (.db)": "Importar subscripcions de FreeTube (.db)", + "Fallback captions: ": "Subtítols alternatius: ", + "Log out": "Tancar sessió", + "preferences_quality_dash_option_2160p": "2160p", + "Unsubscribe": "Cancel·la la subscripció", + "Log in/register": "Inicia sessió/registra't", + "Nepali": "Nepalí", + "Xhosa": "Xosa", + "preferences_captions_label": "Subtítols per defecte: ", + "preferences_autoplay_label": "Reproducció automàtica: ", + "`x` is live": "`x` està en directe", + "Uzbek": "Uzbek", + "Hausa": "Haussa", + "Bosnian": "Bosnià", + "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Hola! Sembla que tens JavaScript desactivat. Feu clic aquí per veure els comentaris, tingueu en compte que poden trigar una mica més a carregar-se.", + "Password cannot be empty": "La contrasenya no pot estar buida", + "preferences_video_loop_label": "Sempre en bucle: ", + "preferences_quality_option_dash": "DASH (qualitat adaptativa)", + "Change password": "Canvia la contrasenya", + "Export data as JSON": "Exporta dades d'Invidious com a JSON", + "Wrong answer": "Resposta incorrecta", + "Clear watch history": "Neteja l'historial de reproduccions", + "Mongolian": "Mongol", + "preferences_quality_dash_option_best": "Millor", + "Authorize token for `x`?": "Autoritzar senyal per a `x`?", + "Report statistics: ": "Estadístiques de l'informe: ", + "Switch Invidious Instance": "Canvia la instància d'Invidious", + "History": "Historial", + "Portuguese (auto-generated)": "Portuguès (generat automàticament)", + "footer_source_code": "Codi font", + "videoinfo_youTube_embed_link": "Insereix", + "generic_count_minutes": "{{count}} minut", + "generic_count_minutes_plural": "{{count}} minuts", + "preferences_category_player": "Preferències del reproductor", + "Sign In": "Inicia Sessió", + "preferences_continue_autoplay_label": "Reprodueix automàticament el següent vídeo: ", + "generic_playlists_count": "{{count}} llista de reproducció", + "generic_playlists_count_plural": "{{count}} llistes de reproducció", + "Delete account?": "Esborrar compte?", + "Please log in": "Si us plau inicieu sessió", + "Import NewPipe data (.zip)": "Importar dades de NewPipe (.zip)", + "Image CAPTCHA": "Imatge CAPTCHA", + "channel_tab_streams_label": "Transmissions en directe", + "preferences_category_misc": "Preferències diverses", + "preferences_annotations_subscribed_label": "Mostra les anotacions per defecte dels canals subscrits? ", + "Tajik": "Tadjik", + "preferences_player_style_label": "Estil del reproductor: ", + "Load more": "Carrega més", + "preferences_vr_mode_label": "Vídeos interactius de 360 graus (requereix WebGL): ", + "Manage subscriptions": "Gestionar les subscripcions", + "preferences_quality_option_medium": "Mitjà", + "Editing playlist `x`": "Editant la llista de reproducció `x`", + "search_filters_duration_option_medium": "Mitjà (4 - 20 minuts)", + "E-mail": "Correu electrònic", + "Spanish (auto-generated)": "Castellà (generat automàticament)", + "Export": "Exportar", + "preferences_quality_dash_option_4320p": "4320p", + "JavaScript license information": "Informació de la llicència de JavaScript", + "Hidden field \"token\" is a required field": "El camp ocult \"senyal\" és un camp obligatori", + "Shona": "Xona", + "Family friendly? ": "Apte per a tots els públics? ", + "preferences_quality_dash_label": "Qualitat de vídeo DASH preferida: ", + "Hindi": "Hindi", + "An alternative front-end to YouTube": "Una interfície alternativa a YouTube", + "Export subscriptions as OPML": "Exporta subscripcions com a OPML", + "Watch on YouTube": "Veure a YouTube", + "Lao": "Laosià", + "search_message_change_filters_or_query": "Proveu d'ampliar la vostra consulta de cerca i/o canviar els filtres.", + "View YouTube comments": "Veure comentaris de YouTube", + "New passwords must match": "Les contrasenyes noves han de coincidir", + "Subscription manager": "Gestor de subscripcions", + "Premieres in `x`": "Estrena en `x`", + "youtube": "YouTube", + "Latvian": "Letó", + "LIVE": "EN VIU", + "Could not create mix.": "No s'ha pogut crear la barreja.", + "preferences_speed_label": "Velocitat per defecte: ", + "preferences_extend_desc_label": "Amplieu automàticament la descripció del vídeo: ", + "popular": "popular", + "Erroneous challenge": "Repte erroni", + "last": "darrer", + "preferences_quality_dash_option_240p": "240p", + "preferences_quality_dash_option_720p": "720p", + "preferences_quality_dash_option_480p": "480p", + "Log in with Google": "Inicia sessió amb Google", + "preferences_quality_dash_option_1440p": "1440p", + "Previous page": "Pàgina anterior", + "Only show latest video from channel: ": "Mostra només l'últim vídeo del canal: ", + "unsubscribe": "cancel·la la subscripció", + "View playlist on YouTube": "Veure llista de reproducció a YouTube", + "Import NewPipe subscriptions (.json)": "Importar subscripcions de NewPipe (.json)", + "crash_page_you_found_a_bug": "Sembla que has trobat un error a Invidious!", + "Subscribe": "Subscriu-me", + "Quota exceeded, try again in a few hours": "S'ha superat la quota, torna-ho a provar d'aquí a unes hores", + "generic_count_days": "{{count}} dia", + "generic_count_days_plural": "{{count}} dies", + "Trending": "Tendència", + "Updated `x` ago": "Actualitzat fa `x`", + "Haitian Creole": "Crioll Haitià", + "preferences_watch_history_label": "Habilita historial de reproduccions: ", + "generic_count_hours": "{{count}} hora", + "generic_count_hours_plural": "{{count}} hores", + "Malayalam": "Maialàiam", + "Clear watch history?": "Neteja historial de reproduccions?", + "Import/export data": "Importa/exporta dades", + "Sinhala": "Singalès", + "Delete playlist": "Eliminar llista de reproducció", + "Bangla": "Bengalí", + "Italian (auto-generated)": "Italià (generat automàticament)", + "License: ": "Llicència: ", + "(edited)": "(editat)", + "Pashto": "Paixtu", + "preferences_dark_mode_label": "Tema: ", + "revoke": "revocar", + "English (United Kingdom)": "Anglès (Regne Unit)", + "preferences_quality_option_hd720": "HD720", + "tokens_count": "{{count}} senyal", + "tokens_count_plural": "{{count}} senyals", + "subscriptions_unseen_notifs_count": "{{count}} notificació no vista", + "subscriptions_unseen_notifs_count_plural": "{{count}} notificacions no vistes", + "generic_subscribers_count": "{{count}} subscriptor", + "generic_subscribers_count_plural": "{{count}} subscriptors", + "Sindhi": "Sindhi", + "Slovenian": "Eslovè", + "preferences_feed_menu_label": "Menú del feed: ", + "Fallback comments: ": "Comentaris alternatius: ", + "Top": "Millors", + "preferences_max_results_label": "Nombre de vídeos mostrats al feed: ", + "Engagement: ": "Atracció: ", + "Redirect homepage to feed: ": "Redirigeix la pàgina d'inici al feed: " } diff --git a/locales/cs.json b/locales/cs.json index 51db1550..4611c4fd 100644 --- a/locales/cs.json +++ b/locales/cs.json @@ -495,5 +495,7 @@ "channel_tab_streams_label": "Živé přenosy", "Music in this video": "Hudba v tomto videu", "Artist: ": "Umělec: ", - "Album: ": "Album: " + "Album: ": "Album: ", + "Channel Sponsor": "Sponzor kanálu", + "Song: ": "Skladba: " } diff --git a/locales/de.json b/locales/de.json index 55c40905..c2941d6d 100644 --- a/locales/de.json +++ b/locales/de.json @@ -472,5 +472,12 @@ "search_filters_duration_option_none": "Beliebige Länge", "search_filters_date_label": "Upload-Datum", "search_filters_date_option_none": "Beliebiges Datum", - "error_video_not_in_playlist": "Das angeforderte Video existiert nicht in dieser Wiedergabeliste. <a href=\"`x`\">Klicken Sie hier, um zur Startseite der Wiedergabeliste zu gelangen.</a>" + "error_video_not_in_playlist": "Das angeforderte Video existiert nicht in dieser Wiedergabeliste. <a href=\"`x`\">Klicken Sie hier, um zur Startseite der Wiedergabeliste zu gelangen.</a>", + "channel_tab_shorts_label": "Shorts", + "channel_tab_streams_label": "Livestreams", + "Music in this video": "Musik in diesem Video", + "Artist: ": "Künstler: ", + "Album: ": "Album: ", + "channel_tab_playlists_label": "Wiedergabelisten", + "channel_tab_channels_label": "Kanäle" } diff --git a/locales/el.json b/locales/el.json index 3448a4dc..8d0c84dd 100644 --- a/locales/el.json +++ b/locales/el.json @@ -366,7 +366,7 @@ "preferences_quality_option_hd720": "HD720", "preferences_quality_option_medium": "Μεσαία", "preferences_quality_option_small": "Μικρό", - "preferences_quality_option_dash": "DASH (προσαρμοστική ποιότητα)", + "preferences_quality_option_dash": "DASH (προσαρμόσιμη ποιότητα)", "preferences_quality_dash_option_4320p": "4320p", "preferences_quality_dash_option_720p": "720p", "invidious": "Invidious", @@ -450,5 +450,5 @@ "search_filters_type_option_show": "Μπάρα προόδου διαβάσματος", "preferences_watch_history_label": "Ενεργοποίηση ιστορικού παρακολούθησης: ", "search_filters_title": "Φίλτρο", - "search_message_no_results": "Δεν" + "search_message_no_results": "Δε βρέθηκαν αποτελέσματα." } diff --git a/locales/en-US.json b/locales/en-US.json index fbcc1341..a3c195ff 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -191,6 +191,7 @@ "Blacklisted regions: ": "Blacklisted regions: ", "Music in this video": "Music in this video", "Artist: ": "Artist: ", + "Song: ": "Song: ", "Album: ": "Album: ", "Shared `x`": "Shared `x`", "Premieres in `x`": "Premieres in `x`", @@ -406,6 +407,7 @@ "YouTube comment permalink": "YouTube comment permalink", "permalink": "permalink", "`x` marked it with a ❤": "`x` marked it with a ❤", + "Channel Sponsor": "Channel Sponsor", "Audio mode": "Audio mode", "Video mode": "Video mode", "Playlists": "Playlists", @@ -455,7 +457,7 @@ "footer_documentation": "Documentation", "footer_source_code": "Source code", "footer_original_source_code": "Original source code", - "footer_modfied_source_code": "Modified Source code", + "footer_modfied_source_code": "Modified source code", "adminprefs_modified_source_code_url_label": "URL to modified source code repository", "none": "none", "videoinfo_started_streaming_x_ago": "Started streaming `x` ago", diff --git a/locales/es.json b/locales/es.json index 6cf721f3..bb082c06 100644 --- a/locales/es.json +++ b/locales/es.json @@ -52,21 +52,21 @@ "preferences_video_loop_label": "Repetir siempre: ", "preferences_autoplay_label": "Reproducción automática: ", "preferences_continue_label": "Reproducir siguiente por defecto: ", - "preferences_continue_autoplay_label": "Reproducir automáticamente el vídeo siguiente: ", + "preferences_continue_autoplay_label": "Reproducir automáticamente el video siguiente: ", "preferences_listen_label": "Activar el sonido por defecto: ", - "preferences_local_label": "¿Usar un proxy para los vídeos? ", + "preferences_local_label": "¿Usar un proxy para los videos? ", "preferences_speed_label": "Velocidad por defecto: ", - "preferences_quality_label": "Calidad de vídeo preferida: ", + "preferences_quality_label": "Calidad de video preferida: ", "preferences_volume_label": "Volumen del reproductor: ", "preferences_comments_label": "Comentarios por defecto: ", "youtube": "YouTube", "reddit": "Reddit", "preferences_captions_label": "Subtítulos por defecto: ", "Fallback captions: ": "Subtítulos alternativos: ", - "preferences_related_videos_label": "¿Mostrar vídeos relacionados? ", + "preferences_related_videos_label": "¿Mostrar videos relacionados? ", "preferences_annotations_label": "¿Mostrar anotaciones por defecto? ", - "preferences_extend_desc_label": "Extender automáticamente la descripción del vídeo: ", - "preferences_vr_mode_label": "Vídeos interactivos de 360 grados (necesita WebGL): ", + "preferences_extend_desc_label": "Extender automáticamente la descripción del video: ", + "preferences_vr_mode_label": "Videos interactivos de 360 grados (necesita WebGL): ", "preferences_category_visual": "Preferencias visuales", "preferences_player_style_label": "Estilo de reproductor: ", "Dark mode: ": "Modo oscuro: ", @@ -79,16 +79,16 @@ "preferences_category_subscription": "Preferencias de la suscripción", "preferences_annotations_subscribed_label": "¿Mostrar anotaciones por defecto para los canales suscritos? ", "Redirect homepage to feed: ": "Redirigir la página de inicio a la fuente: ", - "preferences_max_results_label": "Número de vídeos mostrados en la fuente: ", - "preferences_sort_label": "Ordenar los vídeos por: ", + "preferences_max_results_label": "Número de videos mostrados en la fuente: ", + "preferences_sort_label": "Ordenar los videos por: ", "published": "fecha de publicación", "published - reverse": "fecha de publicación: orden inverso", "alphabetically": "alfabéticamente", "alphabetically - reverse": "alfabéticamente: orden inverso", "channel name": "nombre del canal", "channel name - reverse": "nombre del canal: orden inverso", - "Only show latest video from channel: ": "Mostrar solo el último vídeo del canal: ", - "Only show latest unwatched video from channel: ": "Mostrar solo el último vídeo sin ver del canal: ", + "Only show latest video from channel: ": "Mostrar solo el último video del canal: ", + "Only show latest unwatched video from channel: ": "Mostrar solo el último video sin ver del canal: ", "preferences_unseen_only_label": "Mostrar solo los no vistos: ", "preferences_notifications_only_label": "Mostrar solo notificaciones (si hay alguna): ", "Enable web notifications": "Habilitar notificaciones web", @@ -139,7 +139,7 @@ "Editing playlist `x`": "Editando la lista de reproducción 'x'", "Show more": "Mostrar más", "Show less": "Mostrar menos", - "Watch on YouTube": "Ver el vídeo en YouTube", + "Watch on YouTube": "Ver en YouTube", "Switch Invidious Instance": "Cambiar Instancia de Invidious", "Hide annotations": "Ocultar anotaciones", "Show annotations": "Mostrar anotaciones", @@ -153,7 +153,7 @@ "Shared `x`": "Compartido `x`", "Premieres in `x`": "Se estrena en `x`", "Premieres `x`": "Estrenos `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.": "¡Hola! Parece que tiene JavaScript desactivado. Haga clic aquí para ver los comentarios, pero tenga en cuenta que pueden tardar un poco más en cargarse.", + "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "¡Hola! Parece que tienes JavaScript desactivado. Haz clic aquí para ver los comentarios, pero tengas en cuenta que pueden tardar un poco más en cargarse.", "View YouTube comments": "Ver los comentarios de YouTube", "View more comments on Reddit": "Ver más comentarios en Reddit", "View `x` comments": { @@ -164,7 +164,7 @@ "Hide replies": "Ocultar las respuestas", "Show replies": "Mostrar las respuestas", "Incorrect password": "Contraseña incorrecta", - "Quota exceeded, try again in a few hours": "Cuota excedida, pruebe otra vez en unas horas", + "Quota exceeded, try again in a few hours": "Cuota excedida, prueba otra vez en unas horas", "Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "No se puede iniciar sesión, asegúrese de que la autentificación de dos factores (autentificador o SMS) esté habilitada.", "Invalid TFA code": "Código TFA no válido", "Login failed. This may be because two-factor authentication is not turned on for your account.": "Error de inicio de sesion. Puede deberse a que la autentificación de dos factores no está habilitada en su cuenta.", @@ -176,7 +176,7 @@ "Wrong username or password": "Nombre o contraseña incorrecto", "Please sign in using 'Log in with Google'": "Inicie sesión con «Iniciar sesión con Google»", "Password cannot be empty": "La contraseña no puede estar en blanco", - "Password cannot be longer than 55 characters": "La contraseña no puede tener más de 55 caracteres", + "Password cannot be longer than 55 characters": "La contraseña no debe tener más de 55 caracteres", "Please log in": "Inicie sesión, por favor", "Invidious Private Feed for `x`": "Fuente privada de Invidious para `x`", "channel:`x`": "canal: `x`", @@ -198,7 +198,7 @@ "No such user": "Usuario no existe", "Token is expired, please try again": "El símbolo ha caducado, inténtelo de nuevo", "English": "Inglés", - "English (auto-generated)": "Inglés (generados automáticamente)", + "English (auto-generated)": "Inglés (generado automáticamente)", "Afrikaans": "Afrikáans", "Albanian": "Albanés", "Amharic": "Amárico", @@ -324,50 +324,51 @@ "permalink": "enlace permanente", "`x` marked it with a ❤": "`x` lo ha marcado con un ❤", "Audio mode": "Modo de audio", - "Video mode": "Modo de vídeo", - "channel_tab_videos_label": "Vídeos", + "Video mode": "Modo de video", + "channel_tab_videos_label": "Videos", "Playlists": "Listas de reproducción", "channel_tab_community_label": "Comunidad", - "search_filters_sort_option_relevance": "relevancia", - "search_filters_sort_option_rating": "valoración", - "search_filters_sort_option_date": "fecha", - "search_filters_sort_option_views": "visualizaciones", - "search_filters_type_label": "content_type", + "search_filters_sort_option_relevance": "Relevancia", + "search_filters_sort_option_rating": "Valoración", + "search_filters_sort_option_date": "Fecha de subida", + "search_filters_sort_option_views": "Visualizaciones", + "search_filters_type_label": "tipo de contenido", "search_filters_duration_label": "duración", "search_filters_features_label": "funcionalidades", "search_filters_sort_label": "ordenar", - "search_filters_date_option_hour": "hora", - "search_filters_date_option_today": "hoy", - "search_filters_date_option_week": "semana", - "search_filters_date_option_month": "mes", - "search_filters_date_option_year": "año", - "search_filters_type_option_video": "vídeo", - "search_filters_type_option_channel": "canal", - "search_filters_type_option_playlist": "lista de reproducción", - "search_filters_type_option_movie": "película", - "search_filters_type_option_show": "programa", - "search_filters_features_option_hd": "hd", - "search_filters_features_option_subtitles": "subtítulos", - "search_filters_features_option_c_commons": "creative_commons", - "search_filters_features_option_three_d": "3d", - "search_filters_features_option_live": "directo", - "search_filters_features_option_four_k": "4k", - "search_filters_features_option_location": "ubicación", - "search_filters_features_option_hdr": "hdr", + "search_filters_date_option_hour": "Última hora", + "search_filters_date_option_today": "Hoy", + "search_filters_date_option_week": "Esta semana", + "search_filters_date_option_month": "Este mes", + "search_filters_date_option_year": "Este año", + "search_filters_type_option_video": "Video", + "search_filters_type_option_channel": "Canal", + "search_filters_type_option_playlist": "Lista de reproducción", + "search_filters_type_option_movie": "Película", + "search_filters_type_option_show": "Programa", + "search_filters_features_option_hd": "HD", + "search_filters_features_option_subtitles": "Subtítulos", + "search_filters_features_option_c_commons": "Creative Commons", + "search_filters_features_option_three_d": "3D", + "search_filters_features_option_live": "En directo", + "search_filters_features_option_four_k": "4K", + "search_filters_features_option_location": "Ubicación", + "search_filters_features_option_hdr": "HDR", "Current version: ": "Versión actual: ", "next_steps_error_message": "Después de lo cual debes intentar: ", "next_steps_error_message_refresh": "Recargar la página", "next_steps_error_message_go_to_youtube": "Ir a YouTube", - "search_filters_duration_option_short": "Corto (< 4 minutos)", - "search_filters_duration_option_long": "Largo (> 20 minutos)", + "search_filters_duration_option_short": "Menos de 4 minutos", + "search_filters_duration_option_medium": "De 4 a 20 minutos", + "search_filters_duration_option_long": "Más de 20 minutos", "footer_documentation": "Documentación", "footer_original_source_code": "Código fuente original", - "adminprefs_modified_source_code_url_label": "URL al repositorio de código fuente modificado", + "adminprefs_modified_source_code_url_label": "Enlace al repositorio de código fuente modificado", "footer_source_code": "Código fuente", "footer_modfied_source_code": "Código fuente modificado", "footer_donate_page": "Donar", "preferences_region_label": "País del contenido: ", - "preferences_quality_dash_label": "Calidad de vídeo DASH preferida: ", + "preferences_quality_dash_label": "Calidad de video DASH preferida: ", "preferences_quality_option_hd720": "HD720", "preferences_quality_option_medium": "Intermedia", "preferences_quality_dash_option_auto": "Automática", @@ -376,7 +377,7 @@ "download_subtitles": "Subtítulos- `x` (.vtt)", "user_created_playlists": "`x` listas de reproducción creadas", "user_saved_playlists": "`x` listas de reproducción guardadas", - "Video unavailable": "Vídeo no disponible", + "Video unavailable": "Video no disponible", "videoinfo_youTube_embed_link": "Insertar", "preferences_quality_dash_option_2160p": "2160p", "preferences_quality_dash_option_4320p": "4320p", @@ -413,8 +414,9 @@ "generic_count_weeks_plural": "{{count}} semanas", "generic_playlists_count": "{{count}} lista de reproducción", "generic_playlists_count_plural": "{{count}} listas de reproducción", - "generic_videos_count": "{{count}} vídeo", - "generic_videos_count_plural": "{{count}} vídeos", + "generic_videos_count_0": "{{count}} video", + "generic_videos_count_1": "{{count}} videos", + "generic_videos_count_2": "{{count}} videos", "generic_count_months": "{{count}} mes", "generic_count_months_plural": "{{count}} meses", "comments_points_count": "{{count}} punto", @@ -433,7 +435,7 @@ "crash_page_search_issue": "buscado <a href=\"`x`\">problemas existentes en GitHub</a>", "crash_page_you_found_a_bug": "¡Parece que has encontrado un error en Invidious!", "crash_page_refresh": "probado a <a href=\"`x`\">recargar la página</a>", - "crash_page_report_issue": "Si nada de lo anterior ha sido de ayuda, por favor, <a href=\"`x`\">abre una nueva incidencia en GitHub</a> (preferiblemente en inglés) e incluye el siguiente texto en tu mensaje (NO traduzcas este texto):", + "crash_page_report_issue": "Si nada de lo anterior ha sido de ayuda, por favor, <a href=\"`x`\">abre una nueva incidencia en GitHub</a> (preferiblemente en inglés) e incluye verbatim el siguiente texto en tu mensaje:", "English (United States)": "Inglés (Estados Unidos)", "Cantonese (Hong Kong)": "Cantonés (Hong Kong)", "Dutch (auto-generated)": "Neerlandés (generados automáticamente)", @@ -461,23 +463,24 @@ "search_message_no_results": "No se han encontrado resultados.", "search_message_change_filters_or_query": "Pruebe ampliar la consulta de búsqueda y/o a cambiar los filtros.", "search_filters_title": "Filtros", - "search_filters_date_label": "Fecha de subida", + "search_filters_date_label": "fecha de subida", "search_filters_date_option_none": "Cualquier fecha", "search_filters_type_option_all": "Cualquier tipo", "search_filters_duration_option_none": "Cualquier duración", "search_filters_features_option_vr180": "VR180", - "search_filters_apply_button": "Aplicar filtros seleccionados", + "search_filters_apply_button": "Aplicar filtros", "tokens_count": "{{count}} ficha", "tokens_count_plural": "{{count}} fichas", "search_message_use_another_instance": " También puede <a href=\"`x`\">buscar en otra instancia</a>.", - "search_filters_duration_option_medium": "Medio (4 - 20 minutes)", "Popular enabled: ": "¿Habilitar la sección popular? ", - "error_video_not_in_playlist": "El vídeo solicitado no existe en esta lista de reproducción. <a href=\"`x`\">Haga clic aquí para acceder a la página de inicio de la lista de reproducción.</a>", + "error_video_not_in_playlist": "El video que solicitaste no existe en esta lista de reproducción. <a href=\"`x`\">Haz clic aquí para acceder a la página de inicio de la lista de reproducción.</a>", "channel_tab_streams_label": "Directos", "channel_tab_channels_label": "Canales", "channel_tab_shorts_label": "Cortos", "channel_tab_playlists_label": "Listas de reproducción", - "Music in this video": "Música en este vídeo", + "Music in this video": "Música en este video", "Artist: ": "Artista: ", - "Album: ": "Álbum: " + "Album: ": "Álbum: ", + "Song: ": "Canción: ", + "Channel Sponsor": "Patrocinador del canal" } diff --git a/locales/fa.json b/locales/fa.json index fe72a1e8..56685f64 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -26,15 +26,15 @@ "No": "خیر", "Import and Export Data": "درونبرد و برونبرد داده", "Import": "درونبرد", - "Import Invidious data": "درونبرد داده اینویدیوس", - "Import YouTube subscriptions": "درونبرد اشتراکهای یوتیوب", + "Import Invidious data": "وارد کردن داده JSON اینویدیوس", + "Import YouTube subscriptions": "وارد کردن اشتراک OPML/ یوتیوب", "Import FreeTube subscriptions (.db)": "درونبرد اشتراکهای فریتیوب (.db)", "Import NewPipe subscriptions (.json)": "درونبرد اشتراکهای نیوپایپ (.json)", "Import NewPipe data (.zip)": "درونبرد داده نیوپایپ (.zip)", "Export": "برونبرد", "Export subscriptions as OPML": "برونبرد اشتراکها در قالب OPML", "Export subscriptions as OPML (for NewPipe & FreeTube)": "برونبرد اشتراکها در قالب OPML (برای نیوپایپ و فریتیوب)", - "Export data as JSON": "برونبرد داده در قالب JSON", + "Export data as JSON": "گرفتن(خارج کردن) اطلاعات اینویدیوس با فرمت JSON", "Delete account?": "حذف حساب کاربری؟", "History": "تاریخچه", "An alternative front-end to YouTube": "یک پیشانه جایگزین برای یوتیوب", @@ -71,7 +71,7 @@ "preferences_related_videos_label": "نمایش ویدیو های مرتبط: ", "preferences_annotations_label": "نمایش حاشیه نویسی ها به طور پیشفرض: ", "preferences_extend_desc_label": "گسترش خودکار توضیحات ویدئو: ", - "preferences_vr_mode_label": "ویدئوها ۳۶۰ درجه تعاملی: ", + "preferences_vr_mode_label": "ویدئوها ۳۶۰ درجه تعاملی(نیازمند WebGL): ", "preferences_category_visual": "ترجیحات بصری", "preferences_player_style_label": "حالت پخش کننده: ", "Dark mode: ": "حالت تاریک: ", @@ -80,7 +80,7 @@ "light": "روشن", "preferences_thin_mode_label": "حالت نازک: ", "preferences_category_misc": "ترجیحات متفرقه", - "preferences_automatic_instance_redirect_label": "هدایت خودکار نمونه (به طور پیشفرض به redirect.invidious.io): ", + "preferences_automatic_instance_redirect_label": "هدایت خودکار نمونه (انتقال به redirect.invidious.io): ", "preferences_category_subscription": "ترجیحات اشتراک", "preferences_annotations_subscribed_label": "نمایش حاشیه نویسی ها به طور پیشفرض برای کانال های مشترک شده: ", "Redirect homepage to feed: ": "تغییر مسیر صفحه خانه به خوراک: ", @@ -157,7 +157,7 @@ "Engagement: ": "نامزدی: ", "Whitelisted regions: ": "مناطق لیست سفید: ", "Blacklisted regions: ": "مناطق لیست سیاه: ", - "Shared `x`": "به اشتراک گذاشته شده `x`", + "Shared `x`": "`x` به اشتراک گذاشته شد", "Premieres in `x`": "برای اولین بار در `x`", "Premieres `x`": "برای اولین بار `x`", "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "سلام! مثل اینکه تو جاوا اسکریپت رو خاموش کرده ای. اینجا کلیک کن تا نظرات را ببینی، این رو یادت باشه که ممکنه بارگذاری اونها کمی طول بکشه.", @@ -375,7 +375,7 @@ "next_steps_error_message_refresh": "تازهسازی", "next_steps_error_message_go_to_youtube": "رفتن به یوتیوب", "preferences_quality_option_hd720": "HD720", - "preferences_quality_option_dash": "DASH (کیفیت قابل تطبیق)", + "preferences_quality_option_dash": "DASH (کیفیت تطبیفی)", "preferences_quality_option_medium": "میانه", "preferences_quality_option_small": "پایین", "preferences_quality_dash_option_auto": "خودکار", @@ -445,5 +445,10 @@ "Spanish (Spain)": "اسپانیایی (اسپانیا)", "Turkish (auto-generated)": "ترکی (تولید خودکار)", "search_filters_features_option_vr180": "VR180", - "Spanish (Mexico)": "اسپانیایی (مکزیک)" + "Spanish (Mexico)": "اسپانیایی (مکزیک)", + "Popular enabled: ": "محبوب ها فعال شد: ", + "Music in this video": "آهنگ در این ویدیو", + "Artist: ": "هنرمند: ", + "Album: ": "آلبوم: ", + "Song: ": "آهنگ: " } diff --git a/locales/hi.json b/locales/hi.json index 54e0fe84..41335266 100644 --- a/locales/hi.json +++ b/locales/hi.json @@ -472,5 +472,12 @@ "crash_page_refresh": "<a href=\"`x`\">पृष्ठ को एक बार साफ़ करें</a>", "crash_page_search_issue": "<a href=\"`x`\">GitHub पर मौजूदा मुद्दे</a> ढूँढ़ें", "Popular enabled: ": "लोकप्रिय सक्षम: ", - "Artist: ": "कलाकार: " + "Artist: ": "कलाकार: ", + "Music in this video": "इस वीडियो में संगीत", + "Album: ": "एल्बम: ", + "error_video_not_in_playlist": "अनुरोधित वीडियो इस प्लेलिस्ट में मौजूद नहीं है। <a href=\"`x`\">प्लेलिस्ट के मुखपृष्ठ पर जाने के लिए यहाँ क्लिक करें।</a>", + "channel_tab_shorts_label": "शॉर्ट्स", + "channel_tab_streams_label": "लाइवस्ट्रीम्स", + "channel_tab_playlists_label": "प्लेलिस्ट्स", + "channel_tab_channels_label": "चैनल्स" } diff --git a/locales/hr.json b/locales/hr.json index 72cd6a8e..ade732ad 100644 --- a/locales/hr.json +++ b/locales/hr.json @@ -359,13 +359,13 @@ "next_steps_error_message_refresh": "Aktualiziraj stranicu", "next_steps_error_message_go_to_youtube": "Idi na YouTube", "footer_donate_page": "Doniraj", - "adminprefs_modified_source_code_url_label": "URL do repozitorija izmijenjenog izvornog koda", + "adminprefs_modified_source_code_url_label": "URL do repozitorija prilagođenog izvornog koda", "search_filters_duration_option_short": "Kratko (< 4 minute)", "search_filters_duration_option_long": "Dugo (> 20 minute)", "footer_source_code": "Izvorni kod", - "footer_modfied_source_code": "Izmijenjeni izvorni kod", + "footer_modfied_source_code": "Prilagođen izvorni kod", "footer_documentation": "Dokumentacija", - "footer_original_source_code": "Izvoran izvorni kod", + "footer_original_source_code": "Prvobitan izvorni kod", "preferences_region_label": "Zemlja sadržaja: ", "preferences_quality_dash_label": "Preferirana DASH videokvaliteta: ", "preferences_quality_option_dash": "DASH (adaptativna kvaliteta)", @@ -495,5 +495,7 @@ "channel_tab_shorts_label": "Kratka videa", "Music in this video": "Glazba u ovom videu", "Album: ": "Album: ", - "Artist: ": "Izvođač: " + "Artist: ": "Izvođač: ", + "Channel Sponsor": "Sponzor kanala", + "Song: ": "Pjesma: " } diff --git a/locales/ja.json b/locales/ja.json index 3ad4b494..8a4537d4 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -5,7 +5,7 @@ "generic_subscribers_count_0": "{{count}} 人の登録者", "generic_subscriptions_count_0": "{{count}} 個の登録チャンネル", "LIVE": "ライブ", - "Shared `x` ago": "`x`前に共有", + "Shared `x` ago": "`x`前に公開", "Unsubscribe": "登録解除", "Subscribe": "登録", "View channel on YouTube": "YouTube でチャンネルを見る", @@ -56,17 +56,17 @@ "preferences_category_player": "プレイヤーの設定", "preferences_video_loop_label": "常にループ: ", "preferences_autoplay_label": "自動再生: ", - "preferences_continue_label": "デフォルトで次を再生: ", + "preferences_continue_label": "次の動画を再生: ", "preferences_continue_autoplay_label": "次の動画を自動再生: ", "preferences_listen_label": "デフォルトで音声モードを使用: ", - "preferences_local_label": "動画をプロキシーに通す: ", - "preferences_speed_label": "デフォルトの再生速度: ", + "preferences_local_label": "動画視聴にプロキシーを経由: ", + "preferences_speed_label": "標準の再生速度: ", "preferences_quality_label": "優先する画質: ", "preferences_volume_label": "プレイヤーの音量: ", "preferences_comments_label": "デフォルトのコメント: ", "youtube": "YouTube", "reddit": "Reddit", - "preferences_captions_label": "デフォルトの字幕: ", + "preferences_captions_label": "優先する字幕: ", "Fallback captions: ": "フォールバック時の字幕: ", "preferences_related_videos_label": "関連動画を表示: ", "preferences_annotations_label": "デフォルトでアノテーションを表示: ", @@ -108,7 +108,7 @@ "Watch history": "再生履歴", "Delete account": "アカウントを削除", "preferences_category_admin": "管理者設定", - "preferences_default_home_label": "デフォルトのホーム: ", + "preferences_default_home_label": "ホームに表示するページ: ", "preferences_feed_menu_label": "フィードメニュー: ", "preferences_show_nick_label": "ニックネームを一番上に表示する: ", "Top enabled: ": "トップページを有効化: ", @@ -157,7 +157,7 @@ "Engagement: ": "エンゲージメント: ", "Whitelisted regions: ": "ホワイトリストの地域: ", "Blacklisted regions: ": "ブラックリストの地域: ", - "Shared `x`": "`x`に共有", + "Shared `x`": "公開日 `x`", "Premieres in `x`": "`x`後にプレミア公開", "Premieres `x`": "`x`にプレミア公開", "Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "やあ!君は JavaScript を無効にしているのかな?ここをクリックしてコメントを見れるけど、読み込みには少し時間がかかることがあるのを覚えておいてね。", @@ -191,9 +191,9 @@ "This channel does not exist.": "このチャンネルは存在しません。", "Could not get channel info.": "チャンネル情報を取得できませんでした。", "Could not fetch comments": "コメントを取得できませんでした", - "comments_view_x_replies_0": "{{count}} 件の返信を見る", + "comments_view_x_replies_0": "{{count}}件の返信を表示", "`x` ago": "`x`前", - "Load more": "もっと読み込む", + "Load more": "もっと見る", "comments_points_count_0": "{{count}}点", "Could not create mix.": "ミックスを作成できませんでした。", "Empty playlist": "空の再生リスト", @@ -366,7 +366,7 @@ "search_filters_features_option_subtitles": "字幕", "search_filters_features_option_c_commons": "クリエイティブ・コモンズ", "search_filters_features_option_three_d": "3D", - "search_filters_features_option_live": "生配信", + "search_filters_features_option_live": "ライブ", "search_filters_features_option_four_k": "4K", "search_filters_features_option_location": "場所", "search_filters_features_option_hdr": "HDR", @@ -377,8 +377,8 @@ "search_filters_duration_option_short": "4 分未満", "footer_documentation": "文書", "footer_source_code": "ソースコード", - "footer_original_source_code": "ソースコード (元)", - "footer_modfied_source_code": "ソースコード (改変)", + "footer_original_source_code": "元のソースコード", + "footer_modfied_source_code": "改変して使用", "adminprefs_modified_source_code_url_label": "改変されたソースコードのレポジトリのURL", "search_filters_duration_option_long": "20 分以上", "preferences_region_label": "地域: ", @@ -445,7 +445,7 @@ "search_message_use_another_instance": " <a href=\"`x`\">別のインスタンス上でも検索</a>できます。", "search_filters_apply_button": "選択したフィルターを適用", "user_saved_playlists": "`x` 個の保存した再生リスト", - "crash_page_you_found_a_bug": "Invidious でバグを見つけたようです。", + "crash_page_you_found_a_bug": "Invidious のバグのようです!", "crash_page_refresh": "<a href=\"`x`\">ページを更新</a>を試す", "preferences_watch_history_label": "再生履歴を有効化 ", "search_filters_date_option_none": "すべて", @@ -463,5 +463,7 @@ "channel_tab_channels_label": "チャンネル", "Music in this video": "この動画の音楽", "Artist: ": "アーティスト: ", - "Album: ": "アルバム: " + "Album: ": "アルバム: ", + "Song: ": "曲: ", + "Channel Sponsor": "チャンネルのスポンサー" } diff --git a/locales/pl.json b/locales/pl.json index 2dd3ed87..3ca78e43 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -495,5 +495,7 @@ "channel_tab_shorts_label": "Shorts", "Music in this video": "Muzyka w tym filmie", "Artist: ": "Wykonawca: ", - "Album: ": "Album: " + "Album: ": "Album: ", + "Song: ": "Piosenka: ", + "Channel Sponsor": "Sponsor kanału" } diff --git a/locales/pt-BR.json b/locales/pt-BR.json index afd31ede..ec00d46e 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -381,7 +381,7 @@ "footer_documentation": "Documentação", "footer_source_code": "Código fonte", "footer_original_source_code": "Código fonte original", - "footer_modfied_source_code": "Código Fonte Modificado", + "footer_modfied_source_code": "Código-fonte modificado", "preferences_quality_dash_label": "Qualidade de vídeo do painel preferida: ", "preferences_region_label": "País do conteúdo: ", "preferences_quality_dash_option_4320p": "4320p", @@ -476,5 +476,8 @@ "channel_tab_channels_label": "Canais", "channel_tab_playlists_label": "Listas de reprodução", "channel_tab_shorts_label": "Curtos", - "channel_tab_streams_label": "Ao Vivo" + "channel_tab_streams_label": "Ao Vivo", + "Music in this video": "Música neste vídeo", + "Artist: ": "Artista: ", + "Album: ": "Álbum: " } diff --git a/locales/pt-PT.json b/locales/pt-PT.json index 1788deb1..43834d70 100644 --- a/locales/pt-PT.json +++ b/locales/pt-PT.json @@ -472,5 +472,12 @@ "search_message_change_filters_or_query": "Tente alargar os termos genéricos da pesquisa e/ou alterar os filtros.", "crash_page_refresh": "tentou <a href=\"`x`\">recarregar a página</a>", "crash_page_switch_instance": "tentou <a href=\"`x`\">usar outra instância</a>", - "error_video_not_in_playlist": "O vídeo pedido não existe nesta lista de reprodução. <a href=\"`x`\">Clique aqui para a página inicial da lista de reprodução.</a>" + "error_video_not_in_playlist": "O vídeo pedido não existe nesta lista de reprodução. <a href=\"`x`\">Clique aqui para a página inicial da lista de reprodução.</a>", + "Artist: ": "Artista: ", + "Album: ": "Álbum: ", + "channel_tab_streams_label": "Diretos", + "channel_tab_playlists_label": "Listas de reprodução", + "channel_tab_channels_label": "Canais", + "Music in this video": "Música neste vídeo", + "channel_tab_shorts_label": "Curtos" } diff --git a/locales/pt.json b/locales/pt.json index b6b6c110..310381ae 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -479,5 +479,7 @@ "channel_tab_streams_label": "Diretos", "Music in this video": "Música neste vídeo", "Artist: ": "Artista: ", - "Album: ": "Álbum: " + "Album: ": "Álbum: ", + "Song: ": "Canção: ", + "Channel Sponsor": "Patrocinador do canal" } diff --git a/locales/sq.json b/locales/sq.json index 15025750..7f29a035 100644 --- a/locales/sq.json +++ b/locales/sq.json @@ -286,7 +286,7 @@ "search_filters_type_option_show": "Shfaqe", "search_filters_duration_option_short": "E shkurtër (< 4 minuta)", "search_filters_features_option_purchased": "Të blera", - "footer_modfied_source_code": "Kod Burim i ndryshuar", + "footer_modfied_source_code": "Kod burim i ndryshuar", "adminprefs_modified_source_code_url_label": "URL e depos së ndryshuar të kodit burim", "none": "asnjë", "videoinfo_started_streaming_x_ago": "Filloi transmetimin `x` më parë", @@ -468,5 +468,7 @@ "Artist: ": "Artist: ", "Album: ": "Album: ", "channel_tab_channels_label": "Kanale", - "Music in this video": "Muzikë në këtë video" + "Music in this video": "Muzikë në këtë video", + "channel_tab_shorts_label": "Të shkurtra", + "channel_tab_streams_label": "Transmetime të drejtpërdrejta" } diff --git a/locales/tr.json b/locales/tr.json index d98e2038..6e0bc175 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -363,7 +363,7 @@ "footer_documentation": "Belgelendirme", "footer_source_code": "Kaynak Kodları", "footer_original_source_code": "Orijinal Kaynak Kodları", - "footer_modfied_source_code": "Değiştirilmiş Kaynak Kodları", + "footer_modfied_source_code": "Değiştirilmiş kaynak kodları", "adminprefs_modified_source_code_url_label": "Değiştirilmiş Kaynak Kodları Deposunun URL'si", "footer_donate_page": "Bağış Yap", "preferences_region_label": "İçerik Ülkesi: ", @@ -479,5 +479,7 @@ "channel_tab_playlists_label": "Oynatma Listeleri", "Album: ": "Albüm: ", "Music in this video": "Bu videodaki müzik", - "Artist: ": "Sanatçı: " + "Artist: ": "Sanatçı: ", + "Channel Sponsor": "Kanal Sponsoru", + "Song: ": "Şarkı: " } diff --git a/locales/uk.json b/locales/uk.json index b44d237f..4d748e7f 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -495,5 +495,7 @@ "channel_tab_channels_label": "Канали", "Music in this video": "Музика в цьому відео", "Artist: ": "Виконавець: ", - "Album: ": "Альбом: " + "Album: ": "Альбом: ", + "Song: ": "Пісня: ", + "Channel Sponsor": "Спонсор каналу" } diff --git a/locales/zh-CN.json b/locales/zh-CN.json index aff6dd3e..f202cf88 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -463,5 +463,7 @@ "channel_tab_streams_label": "直播", "Album: ": "专辑: ", "channel_tab_shorts_label": "短视频", - "channel_tab_channels_label": "频道" + "channel_tab_channels_label": "频道", + "Song: ": "歌曲: ", + "Channel Sponsor": "频道赞助者" } diff --git a/locales/zh-TW.json b/locales/zh-TW.json index 8aa9869a..54090d3d 100644 --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -463,5 +463,7 @@ "channel_tab_streams_label": "直播", "Artist: ": "藝術家: ", "Album: ": "專輯: ", - "Music in this video": "此影片中的音樂" + "Music in this video": "此影片中的音樂", + "Channel Sponsor": "頻道贊助者", + "Song: ": "歌曲: " } diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr index 13af2d8b..ce34ff82 100644 --- a/src/invidious/channels/community.cr +++ b/src/invidious/channels/community.cr @@ -1,3 +1,5 @@ +private IMAGE_QUALITIES = {320, 560, 640, 1280, 2000} + # TODO: Add "sort_by" def fetch_channel_community(ucid, continuation, locale, format, thin_mode) response = YT_POOL.client &.get("/channel/#{ucid}/community?gl=US&hl=en") @@ -108,6 +110,8 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) like_count = post["actionButtons"]["commentActionButtonsRenderer"]["likeButton"]["toggleButtonRenderer"]["accessibilityData"]["accessibilityData"]["label"] .try &.as_s.gsub(/\D/, "").to_i? || 0 + reply_count = short_text_to_number(post.dig?("actionButtons", "commentActionButtonsRenderer", "replyButton", "buttonRenderer", "text", "simpleText").try &.as_s || "0") + json.field "content", html_to_content(content_html) json.field "contentHtml", content_html @@ -115,6 +119,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) json.field "publishedText", translate(locale, "`x` ago", recode_date(published, locale)) json.field "likeCount", like_count + json.field "replyCount", reply_count json.field "commentId", post["postId"]? || post["commentId"]? || "" json.field "authorIsChannelOwner", post["authorEndpoint"]["browseEndpoint"]["browseId"] == ucid @@ -174,9 +179,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) aspect_ratio = (width.to_f / height.to_f) url = thumbnail["url"].as_s.gsub(/=w\d+-h\d+(-p)?(-nd)?(-df)?(-rwa)?/, "=s640") - qualities = {320, 560, 640, 1280, 2000} - - qualities.each do |quality| + IMAGE_QUALITIES.each do |quality| json.object do json.field "url", url.gsub(/=s\d+/, "=s#{quality}") json.field "width", quality @@ -185,10 +188,39 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) end end end - # TODO - # when .has_key?("pollRenderer") - # attachment = attachment["pollRenderer"] - # json.field "type", "poll" + when .has_key?("pollRenderer") + attachment = attachment["pollRenderer"] + json.field "type", "poll" + json.field "totalVotes", short_text_to_number(attachment["totalVotes"]["simpleText"].as_s.split(" ")[0]) + json.field "choices" do + json.array do + attachment["choices"].as_a.each do |choice| + json.object do + json.field "text", choice.dig("text", "runs", 0, "text").as_s + # A choice can have an image associated with it. + # Ex post: https://www.youtube.com/post/UgkxD4XavXUD4NQiddJXXdohbwOwcVqrH9Re + if choice["image"]? + thumbnail = choice["image"]["thumbnails"][0].as_h + width = thumbnail["width"].as_i + height = thumbnail["height"].as_i + aspect_ratio = (width.to_f / height.to_f) + url = thumbnail["url"].as_s.gsub(/=w\d+-h\d+(-p)?(-nd)?(-df)?(-rwa)?/, "=s640") + json.field "image" do + json.array do + IMAGE_QUALITIES.each do |quality| + json.object do + json.field "url", url.gsub(/=s\d+/, "=s#{quality}") + json.field "width", quality + json.field "height", (quality / aspect_ratio).ceil.to_i + end + end + end + end + end + end + end + end + end when .has_key?("postMultiImageRenderer") attachment = attachment["postMultiImageRenderer"] json.field "type", "multiImage" @@ -202,9 +234,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode) aspect_ratio = (width.to_f / height.to_f) url = thumbnail["url"].as_s.gsub(/=w\d+-h\d+(-p)?(-nd)?(-df)?(-rwa)?/, "=s640") - qualities = {320, 560, 640, 1280, 2000} - - qualities.each do |quality| + IMAGE_QUALITIES.each do |quality| json.object do json.field "url", url.gsub(/=s\d+/, "=s#{quality}") json.field "width", quality diff --git a/src/invidious/comments.cr b/src/invidious/comments.cr index 357a461c..b15d63d4 100644 --- a/src/invidious/comments.cr +++ b/src/invidious/comments.cr @@ -182,7 +182,11 @@ def fetch_youtube_comments(id, cursor, format, locale, thin_mode, region, sort_b json.field "contentHtml", content_html json.field "isPinned", (node_comment["pinnedCommentBadge"]? != nil) - + json.field "isSponsor", (node_comment["sponsorCommentBadge"]? != nil) + if node_comment["sponsorCommentBadge"]? + # Sponsor icon thumbnails always have one object and there's only ever the url property in it + json.field "sponsorIconUrl", node_comment.dig("sponsorCommentBadge", "sponsorCommentBadgeRenderer", "customBadge", "thumbnails", 0, "url").to_s + end json.field "published", published.to_unix json.field "publishedText", translate(locale, "`x` ago", recode_date(published, locale)) @@ -324,11 +328,21 @@ def template_youtube_comments(comments, locale, thin_mode, is_replies = false) end author_name = HTML.escape(child["author"].as_s) + sponsor_icon = "" if child["verified"]?.try &.as_bool && child["authorIsChannelOwner"]?.try &.as_bool author_name += " <i class=\"icon ion ion-md-checkmark-circle\"></i>" elsif child["verified"]?.try &.as_bool author_name += " <i class=\"icon ion ion-md-checkmark\"></i>" end + + if child["isSponsor"]?.try &.as_bool + sponsor_icon = String.build do |str| + str << %(<img alt="" ) + str << %(src="/ggpht) << URI.parse(child["sponsorIconUrl"].as_s).request_target << "\" " + str << %(title=") << translate(locale, "Channel Sponsor") << "\" " + str << %(width="16" height="16" />) + end + end html << <<-END_HTML <div class="pure-g" style="width:100%"> <div class="channel-profile pure-u-4-24 pure-u-md-2-24"> @@ -339,6 +353,7 @@ def template_youtube_comments(comments, locale, thin_mode, is_replies = false) <b> <a class="#{child["authorIsChannelOwner"] == true ? "channel-owner" : ""}" href="#{child["authorUrl"]}">#{author_name}</a> </b> + #{sponsor_icon} <p style="white-space:pre-wrap">#{child["contentHtml"]}</p> END_HTML @@ -675,6 +690,27 @@ def content_to_comment_html(content, video_id : String? = "") text = "<s>#{text}</s>" if run["strikethrough"]? text = "<i>#{text}</i>" if run["italics"]? + # check for custom emojis + if run["emoji"]? + if run["emoji"]["isCustomEmoji"]?.try &.as_bool + if emojiImage = run.dig?("emoji", "image") + emojiAlt = emojiImage.dig?("accessibility", "accessibilityData", "label").try &.as_s || text + emojiThumb = emojiImage["thumbnails"][0] + text = String.build do |str| + str << %(<img alt=") << emojiAlt << "\" " + str << %(src="/ggpht) << URI.parse(emojiThumb["url"].as_s).request_target << "\" " + str << %(title=") << emojiAlt << "\" " + str << %(width=") << emojiThumb["width"] << "\" " + str << %(height=") << emojiThumb["height"] << "\" " + str << %(class="channel-emoji"/>) + end + else + # Hide deleted channel emoji + text = "" + end + end + end + text end diff --git a/src/invidious/jsonify/api_v1/video_json.cr b/src/invidious/jsonify/api_v1/video_json.cr index a2b1a35c..fe4b5223 100644 --- a/src/invidious/jsonify/api_v1/video_json.cr +++ b/src/invidious/jsonify/api_v1/video_json.cr @@ -197,6 +197,21 @@ module Invidious::JSONify::APIv1 end end + if !video.music.empty? + json.field "musicTracks" do + json.array do + video.music.each do |music| + json.object do + json.field "song", music.song + json.field "artist", music.artist + json.field "album", music.album + json.field "license", music.license + end + end + end + end + end + json.field "recommendedVideos" do json.array do video.related_videos.each do |rv| diff --git a/src/invidious/routes/account.cr b/src/invidious/routes/account.cr index 8f69df94..5aa4452c 100644 --- a/src/invidious/routes/account.cr +++ b/src/invidious/routes/account.cr @@ -262,7 +262,7 @@ module Invidious::Routes::Account end query["token"] = access_token - query["username"] = user.email + query["username"] = URI.encode_path_segment(user.email) url.query = query.to_s env.redirect url.to_s diff --git a/src/invidious/routes/api/v1/authenticated.cr b/src/invidious/routes/api/v1/authenticated.cr index 6b935312..ce2ee812 100644 --- a/src/invidious/routes/api/v1/authenticated.cr +++ b/src/invidious/routes/api/v1/authenticated.cr @@ -54,6 +54,65 @@ module Invidious::Routes::API::V1::Authenticated env.response.status_code = 204 end + def self.get_history(env) + env.response.content_type = "application/json" + user = env.get("user").as(User) + + page = env.params.query["page"]?.try &.to_i?.try &.clamp(0, Int32::MAX) + page ||= 1 + + max_results = env.params.query["max_results"]?.try &.to_i?.try &.clamp(0, MAX_ITEMS_PER_PAGE) + max_results ||= user.preferences.max_results + max_results ||= CONFIG.default_user_preferences.max_results + + start_index = (page - 1) * max_results + if user.watched[start_index]? + watched = user.watched.reverse[start_index, max_results] + end + watched ||= [] of String + + return watched.to_json + end + + def self.mark_watched(env) + user = env.get("user").as(User) + + if !user.preferences.watch_history + return error_json(409, "Watch history is disabled in preferences.") + end + + id = env.params.url["id"] + if !id.match(/^[a-zA-Z0-9_-]{11}$/) + return error_json(400, "Invalid video id.") + end + + Invidious::Database::Users.mark_watched(user, id) + env.response.status_code = 204 + end + + def self.mark_unwatched(env) + user = env.get("user").as(User) + + if !user.preferences.watch_history + return error_json(409, "Watch history is disabled in preferences.") + end + + id = env.params.url["id"] + if !id.match(/^[a-zA-Z0-9_-]{11}$/) + return error_json(400, "Invalid video id.") + end + + Invidious::Database::Users.mark_unwatched(user, id) + env.response.status_code = 204 + end + + def self.clear_history(env) + user = env.get("user").as(User) + + Invidious::Database::Users.clear_watch_history(user) + env.response.status_code = 204 + end + def self.feed(env) env.response.content_type = "application/json" diff --git a/src/invidious/routes/video_playback.cr b/src/invidious/routes/video_playback.cr index 1e932d11..9641e01a 100644 --- a/src/invidious/routes/video_playback.cr +++ b/src/invidious/routes/video_playback.cr @@ -256,7 +256,7 @@ module Invidious::Routes::VideoPlayback return error_template(400, "Invalid video ID") end - if itag.nil? || itag <= 0 || itag >= 1000 + if !itag.nil? && (itag <= 0 || itag >= 1000) return error_template(400, "Invalid itag") end @@ -277,7 +277,11 @@ module Invidious::Routes::VideoPlayback return error_template(500, ex) end - fmt = video.fmt_stream.find(nil) { |f| f["itag"].as_i == itag } || video.adaptive_fmts.find(nil) { |f| f["itag"].as_i == itag } + if itag.nil? + fmt = video.fmt_stream[-1]? + else + fmt = video.fmt_stream.find(nil) { |f| f["itag"].as_i == itag } || video.adaptive_fmts.find(nil) { |f| f["itag"].as_i == itag } + end url = fmt.try &.["url"]?.try &.as_s if !url diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr index dca2f117..9e2ade3d 100644 --- a/src/invidious/routing.cr +++ b/src/invidious/routing.cr @@ -257,6 +257,11 @@ module Invidious::Routing get "/api/v1/auth/export/invidious", {{namespace}}::Authenticated, :export_invidious post "/api/v1/auth/import/invidious", {{namespace}}::Authenticated, :import_invidious + get "/api/v1/auth/history", {{namespace}}::Authenticated, :get_history + post "/api/v1/auth/history/:id", {{namespace}}::Authenticated, :mark_watched + delete "/api/v1/auth/history/:id", {{namespace}}::Authenticated, :mark_unwatched + delete "/api/v1/auth/history", {{namespace}}::Authenticated, :clear_history + get "/api/v1/auth/feed", {{namespace}}::Authenticated, :feed get "/api/v1/auth/subscriptions", {{namespace}}::Authenticated, :get_subscriptions diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr index 436ac82d..0038a97a 100644 --- a/src/invidious/videos.cr +++ b/src/invidious/videos.cr @@ -249,7 +249,12 @@ struct Video def music : Array(VideoMusic) info["music"].as_a.map { |music_json| - VideoMusic.new(music_json["album"].as_s, music_json["artist"].as_s, music_json["license"].as_s) + VideoMusic.new( + music_json["song"].as_s, + music_json["album"].as_s, + music_json["artist"].as_s, + music_json["license"].as_s + ) } end diff --git a/src/invidious/videos/music.cr b/src/invidious/videos/music.cr index 402ae46f..08d88a3e 100644 --- a/src/invidious/videos/music.cr +++ b/src/invidious/videos/music.cr @@ -3,10 +3,11 @@ require "json" struct VideoMusic include JSON::Serializable + property song : String property album : String property artist : String property license : String - def initialize(@album : String, @artist : String, @license : String) + def initialize(@song : String, @album : String, @artist : String, @license : String) end end diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr index efc4b2e5..608ae99d 100644 --- a/src/invidious/videos/parser.cr +++ b/src/invidious/videos/parser.cr @@ -327,17 +327,25 @@ def parse_video_info(video_id : String, player_response : Hash(String, JSON::Any album = nil music_license = nil + # Used when the video has multiple songs + if song_title = music_desc.dig?("carouselLockupRenderer", "videoLockup", "compactVideoRenderer", "title") + # "simpleText" for plain text / "runs" when song has a link + song = song_title["simpleText"]? || song_title.dig("runs", 0, "text") + end + music_desc.dig?("carouselLockupRenderer", "infoRows").try &.as_a.each do |desc| desc_title = extract_text(desc.dig?("infoRowRenderer", "title")) if desc_title == "ARTIST" artist = extract_text(desc.dig?("infoRowRenderer", "defaultMetadata")) + elsif desc_title == "SONG" + song = extract_text(desc.dig?("infoRowRenderer", "defaultMetadata")) elsif desc_title == "ALBUM" album = extract_text(desc.dig?("infoRowRenderer", "defaultMetadata")) elsif desc_title == "LICENSES" music_license = extract_text(desc.dig?("infoRowRenderer", "expandedMetadata")) end end - music_list << VideoMusic.new(album.to_s, artist.to_s, music_license.to_s) + music_list << VideoMusic.new(song.to_s, album.to_s, artist.to_s, music_license.to_s) end # Author infos diff --git a/src/invidious/views/watch.ecr b/src/invidious/views/watch.ecr index 1fc79495..a3ec94e8 100644 --- a/src/invidious/views/watch.ecr +++ b/src/invidious/views/watch.ecr @@ -252,9 +252,9 @@ we're going to need to do it here in order to allow for translations. <div id="music-description-box"> <% video.music.each do |music| %> <div class="music-item"> - <p id="music-artist"><%= translate(locale, "Artist: ") %><%= music.artist %></p> - <p id="music-album"><%= translate(locale, "Album: ") %><%= music.album %></p> - <p id="music-license"><%= translate(locale, "License: ") %><%= music.license %></p> + <p class="music-song"><%= translate(locale, "Song: ") %><%= music.song %></p> + <p class="music-artist"><%= translate(locale, "Artist: ") %><%= music.artist %></p> + <p class="music-album"><%= translate(locale, "Album: ") %><%= music.album %></p> </div> <% end %> </div> |
