mirror of
https://github.com/iv-org/invidious.git
synced 2025-04-20 23:46:26 -04:00
Merge branch 'iv-org:master' into master
This commit is contained in:
commit
6077b858ef
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@ -23,4 +23,4 @@ jobs:
|
||||
stale-pr-label: "stale"
|
||||
ascending: true
|
||||
# Never mark feature requests/enhancements as stale
|
||||
exempt-issue-labels: "feature-request,enhancement"
|
||||
exempt-issue-labels: "feature-request,enhancement,exempt-stale"
|
||||
|
@ -154,6 +154,8 @@ Weblate also allows you to log-in with major SSO providers like Github, Gitlab,
|
||||
- [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
|
||||
|
@ -145,6 +145,24 @@ img.thumbnail {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
div.watched-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(255,255,255,.4);
|
||||
}
|
||||
|
||||
div.watched-indicator {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
height: 4px;
|
||||
width: 100%;
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.length {
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
@ -490,13 +508,14 @@ hr {
|
||||
}
|
||||
|
||||
/* Description Expansion Styling*/
|
||||
#descexpansionbutton {
|
||||
display: none
|
||||
#descexpansionbutton,
|
||||
#music-desc-expansion {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#descexpansionbutton ~ div {
|
||||
overflow: hidden;
|
||||
height: 8.3em;
|
||||
max-height: 8.3em;
|
||||
}
|
||||
|
||||
#descexpansionbutton:checked ~ div {
|
||||
@ -509,7 +528,8 @@ hr {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
label[for="descexpansionbutton"]:hover {
|
||||
label[for="descexpansionbutton"]:hover,
|
||||
label[for="music-desc-expansion"]:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@ -521,14 +541,38 @@ h4,
|
||||
h5,
|
||||
p,
|
||||
#descriptionWrapper,
|
||||
#description-box {
|
||||
unicode-bidi: plaintext;
|
||||
text-align: start;
|
||||
#description-box,
|
||||
#music-description-box {
|
||||
unicode-bidi: plaintext;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
#descriptionWrapper {
|
||||
max-width: 600px;
|
||||
white-space: pre-wrap;
|
||||
max-width: 600px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
#music-description-box {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#music-desc-expansion:checked ~ #music-description-box {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#music-desc-expansion ~ label > h3 > .ion-ios-arrow-up,
|
||||
#music-desc-expansion:checked ~ label > h3 > .ion-ios-arrow-down {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#music-desc-expansion:checked ~ label > h3 > .ion-ios-arrow-up,
|
||||
#music-desc-expansion ~ label > h3 > .ion-ios-arrow-down {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* Select all the music items except the first one */
|
||||
.music-item + .music-item {
|
||||
border-top: 1px solid #ffffff;
|
||||
}
|
||||
|
||||
/* Center the "invidious" logo on the search page */
|
||||
|
24
assets/js/watched_indicator.js
Normal file
24
assets/js/watched_indicator.js
Normal file
@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
var save_player_pos_key = 'save_player_pos';
|
||||
|
||||
function get_all_video_times() {
|
||||
return helpers.storage.get(save_player_pos_key) || {};
|
||||
}
|
||||
|
||||
document.querySelectorAll('.watched-indicator').forEach(function (indicator) {
|
||||
var watched_part = get_all_video_times()[indicator.dataset.id];
|
||||
var total = parseInt(indicator.dataset.length, 10);
|
||||
if (watched_part === undefined) {
|
||||
watched_part = total;
|
||||
}
|
||||
var percentage = Math.round((watched_part / total) * 100);
|
||||
|
||||
if (percentage < 5) {
|
||||
percentage = 5;
|
||||
}
|
||||
if (percentage > 90) {
|
||||
percentage = 100;
|
||||
}
|
||||
|
||||
indicator.style.width = percentage + '%';
|
||||
});
|
1
locales/af.json
Normal file
1
locales/af.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
@ -536,5 +536,12 @@
|
||||
"generic_count_seconds_3": "{{count}} ثوانٍ",
|
||||
"generic_count_seconds_4": "{{count}} ثانية",
|
||||
"generic_count_seconds_5": "{{count}} ثانية",
|
||||
"error_video_not_in_playlist": "الفيديو المطلوب غير موجود في قائمة التشغيل هذه. <a href=\"`x`\"> انقر هنا للحصول على الصفحة الرئيسية لقائمة التشغيل. </a>"
|
||||
"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": "القنوات",
|
||||
"Music in this video": "الموسيقى في هذا الفيديو",
|
||||
"Album: ": "الألبوم: ",
|
||||
"Artist: ": "الفنان: "
|
||||
}
|
||||
|
@ -63,7 +63,7 @@
|
||||
"reddit": "Reddit",
|
||||
"preferences_captions_label": "Výchozí titulky: ",
|
||||
"Fallback captions: ": "Záložní titulky: ",
|
||||
"preferences_related_videos_label": "Zobrazit podobné videa: ",
|
||||
"preferences_related_videos_label": "Zobrazit podobná videa: ",
|
||||
"preferences_annotations_label": "Zobrazovat poznámky ve výchozím nastavení: ",
|
||||
"preferences_extend_desc_label": "Rozšířit automaticky popis u videa: ",
|
||||
"preferences_category_visual": "Nastavení vzhledu",
|
||||
@ -488,5 +488,12 @@
|
||||
"search_filters_sort_option_relevance": "Relevantnost",
|
||||
"search_filters_apply_button": "Použít vybrané filtry",
|
||||
"Popular enabled: ": "Populární povoleno: ",
|
||||
"error_video_not_in_playlist": "Požadované video v tomto playlistu neexistuje. <a href=\"`x`\">Klikněte sem pro navštívení domovské stránky playlistu.</a>"
|
||||
"error_video_not_in_playlist": "Požadované video v tomto playlistu neexistuje. <a href=\"`x`\">Klikněte sem pro navštívení domovské stránky playlistu.</a>",
|
||||
"channel_tab_shorts_label": "Shorts",
|
||||
"channel_tab_playlists_label": "Playlisty",
|
||||
"channel_tab_channels_label": "Kanály",
|
||||
"channel_tab_streams_label": "Živé přenosy",
|
||||
"Music in this video": "Hudba v tomto videu",
|
||||
"Artist: ": "Umělec: ",
|
||||
"Album: ": "Album: "
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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": "Δε βρέθηκαν αποτελέσματα."
|
||||
}
|
||||
|
@ -188,6 +188,9 @@
|
||||
"Engagement: ": "Engagement: ",
|
||||
"Whitelisted regions: ": "Whitelisted regions: ",
|
||||
"Blacklisted regions: ": "Blacklisted regions: ",
|
||||
"Music in this video": "Music in this video",
|
||||
"Artist: ": "Artist: ",
|
||||
"Album: ": "Album: ",
|
||||
"Shared `x`": "Shared `x`",
|
||||
"Premieres in `x`": "Premieres in `x`",
|
||||
"Premieres `x`": "Premieres `x`",
|
||||
@ -451,7 +454,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",
|
||||
|
@ -325,7 +325,7 @@
|
||||
"`x` marked it with a ❤": "`x` markis ĝin per ❤",
|
||||
"Audio mode": "Aŭda reĝimo",
|
||||
"Video mode": "Videa reĝimo",
|
||||
"channel_tab_videos_label": "Filmetoj",
|
||||
"channel_tab_videos_label": "Videoj",
|
||||
"Playlists": "Ludlistoj",
|
||||
"channel_tab_community_label": "Komunumo",
|
||||
"search_filters_sort_option_relevance": "rilateco",
|
||||
@ -472,5 +472,12 @@
|
||||
"generic_subscribers_count_plural": "{{count}} abonantoj",
|
||||
"generic_count_months": "{{count}} monato",
|
||||
"generic_count_months_plural": "{{count}} monatoj",
|
||||
"preferences_save_player_pos_label": "Konservi ludadan pozicion: "
|
||||
"preferences_save_player_pos_label": "Konservi ludadan pozicion: ",
|
||||
"channel_tab_streams_label": "Tujelsendoj",
|
||||
"channel_tab_playlists_label": "Ludlistoj",
|
||||
"channel_tab_channels_label": "Kanaloj",
|
||||
"channel_tab_shorts_label": "Mallongaj",
|
||||
"Music in this video": "Muziko en ĉi tiu video",
|
||||
"Artist: ": "Artisto: ",
|
||||
"Album: ": "Albumo: "
|
||||
}
|
||||
|
111
locales/es.json
111
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,8 @@
|
||||
"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": "{{count}} video",
|
||||
"generic_videos_count_plural": "{{count}} videos",
|
||||
"generic_count_months": "{{count}} mes",
|
||||
"generic_count_months_plural": "{{count}} meses",
|
||||
"comments_points_count": "{{count}} punto",
|
||||
@ -433,7 +434,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,16 +462,22 @@
|
||||
"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 video",
|
||||
"Artist: ": "Artista: ",
|
||||
"Album: ": "Álbum: "
|
||||
}
|
||||
|
@ -408,9 +408,9 @@
|
||||
"preferences_region_label": "کشور محتوا: ",
|
||||
"footer_documentation": "مستندات",
|
||||
"footer_original_source_code": "کد منبع اصلی",
|
||||
"search_filters_duration_option_long": "بلند (> 20 دقیقه)",
|
||||
"search_filters_duration_option_long": "بلند (> ۲۰ دقیقه)",
|
||||
"adminprefs_modified_source_code_url_label": "URL مخزن کد منبع ویریش شده",
|
||||
"search_filters_duration_option_short": "کوتاه (< 4 دقیقه)",
|
||||
"search_filters_duration_option_short": "کوتاه (< ۴ دقیقه)",
|
||||
"search_filters_title": "پالایه",
|
||||
"Chinese (Hong Kong)": "چینی (هنگکنگ)",
|
||||
"Dutch (auto-generated)": "هلندی (تولید خودکار)",
|
||||
@ -424,5 +424,26 @@
|
||||
"search_message_no_results": "نتیجهای یافت نشد.",
|
||||
"search_message_change_filters_or_query": "سعی کنید جستوجوی خود را وسیعتر کنید و/یا فیلترها را تغییر دهید.",
|
||||
"Chinese (China)": "چینی (چین)",
|
||||
"German (auto-generated)": "آلمانی (تولید خودکار)"
|
||||
"German (auto-generated)": "آلمانی (تولید خودکار)",
|
||||
"Japanese (auto-generated)": "ژاپنی (تولید خودکار)",
|
||||
"Korean (auto-generated)": "کرهای (تولید خودکار)",
|
||||
"Portuguese (Brazil)": "پرتغالی (برزیل)",
|
||||
"search_filters_apply_button": "اعمال فیلترهای انتخاب شده",
|
||||
"Italian (auto-generated)": "ایتالیایی (تولید خودکار)",
|
||||
"Vietnamese (auto-generated)": "ویتنامی (تولید خودکار)",
|
||||
"search_filters_type_option_all": "هر نوعی",
|
||||
"search_filters_duration_option_none": "هر مدت زمانی",
|
||||
"search_filters_date_label": "تاریخ بارگذاری",
|
||||
"search_filters_date_option_none": "هر تاریخی",
|
||||
"user_created_playlists": "`x` فهرست پخش ایجاد شد",
|
||||
"Interlingue": "سرخپوستی",
|
||||
"Russian (auto-generated)": "روسی (تولید خودکار)",
|
||||
"Spanish (auto-generated)": "اسپانیایی (تولید خودکار)",
|
||||
"search_filters_duration_option_medium": "متوسط (۴ تا ۲۰ دقیقه)",
|
||||
"Portuguese (auto-generated)": "پرتغالی (تولید خودکار)",
|
||||
"Cantonese (Hong Kong)": "کانتونی (هنگ کنگ)",
|
||||
"Spanish (Spain)": "اسپانیایی (اسپانیا)",
|
||||
"Turkish (auto-generated)": "ترکی (تولید خودکار)",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"Spanish (Mexico)": "اسپانیایی (مکزیک)"
|
||||
}
|
||||
|
@ -472,5 +472,9 @@
|
||||
"search_filters_date_label": "Date d'ajout",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"search_filters_duration_option_none": "Toutes les durées",
|
||||
"error_video_not_in_playlist": "La vidéo demandée n'existe pas dans cette liste de lecture. <a href=\"`x`\">Cliquez ici pour retourner à la liste de lecture.</a>"
|
||||
"error_video_not_in_playlist": "La vidéo demandée n'existe pas dans cette liste de lecture. <a href=\"`x`\">Cliquez ici pour retourner à la liste de lecture.</a>",
|
||||
"channel_tab_shorts_label": "Clips",
|
||||
"channel_tab_streams_label": "En direct",
|
||||
"channel_tab_playlists_label": "Listes de lecture",
|
||||
"channel_tab_channels_label": "Chaînes"
|
||||
}
|
||||
|
@ -470,5 +470,14 @@
|
||||
"crash_page_switch_instance": "<a href=\"`x`\">किसी दूसरे उदाहरण का इस्तेमाल करें</a>",
|
||||
"crash_page_read_the_faq": "<a href=\"`x`\">अक्सर पूछे जाने वाले प्रश्न (FAQ)</a> पढ़ें",
|
||||
"crash_page_refresh": "<a href=\"`x`\">पृष्ठ को एक बार साफ़ करें</a>",
|
||||
"crash_page_search_issue": "<a href=\"`x`\">GitHub पर मौजूदा मुद्दे</a> ढूँढ़ें"
|
||||
"crash_page_search_issue": "<a href=\"`x`\">GitHub पर मौजूदा मुद्दे</a> ढूँढ़ें",
|
||||
"Popular enabled: ": "लोकप्रिय सक्षम: ",
|
||||
"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": "चैनल्स"
|
||||
}
|
||||
|
@ -7,8 +7,8 @@
|
||||
"View playlist on YouTube": "Prikaži zbirku na YouTubeu",
|
||||
"newest": "najnovije",
|
||||
"oldest": "najstarije",
|
||||
"popular": "popularni",
|
||||
"last": "zadnji",
|
||||
"popular": "popularne",
|
||||
"last": "zadnje",
|
||||
"Next page": "Sljedeća stranica",
|
||||
"Previous page": "Prethodna stranica",
|
||||
"Clear watch history?": "Izbrisati povijest gledanja?",
|
||||
@ -43,9 +43,9 @@
|
||||
"Time (h:mm:ss):": "Vrijeme (h:mm:ss):",
|
||||
"Text CAPTCHA": "Tekstualni CAPTCHA",
|
||||
"Image CAPTCHA": "Slikovni CAPTCHA",
|
||||
"Sign In": "Prijava",
|
||||
"Sign In": "Prijavi se",
|
||||
"Register": "Registriraj se",
|
||||
"E-mail": "E-mail",
|
||||
"E-mail": "E-mail adresa",
|
||||
"Google verification code": "Googleov potvrdni kod",
|
||||
"Preferences": "Postavke",
|
||||
"preferences_category_player": "Postavke playera",
|
||||
@ -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)",
|
||||
@ -488,5 +488,12 @@
|
||||
"search_filters_apply_button": "Primijeni odabrane filtre",
|
||||
"search_filters_type_option_all": "Bilo koja vrsta",
|
||||
"Popular enabled: ": "Popularni aktivirani: ",
|
||||
"error_video_not_in_playlist": "Traženi video ne postoji u ovoj zbirci. <a href=\"`x`\">Pritisni ovdje za početnu stranicu zbirke.</a>"
|
||||
"error_video_not_in_playlist": "Traženi video ne postoji u ovoj zbirci. <a href=\"`x`\">Pritisni ovdje za početnu stranicu zbirke.</a>",
|
||||
"channel_tab_streams_label": "Prijenosi uživo",
|
||||
"channel_tab_playlists_label": "Zbirke",
|
||||
"channel_tab_channels_label": "Kanali",
|
||||
"channel_tab_shorts_label": "Kratka videa",
|
||||
"Music in this video": "Glazba u ovom videu",
|
||||
"Album: ": "Album: ",
|
||||
"Artist: ": "Izvođač: "
|
||||
}
|
||||
|
@ -346,7 +346,6 @@
|
||||
"Video mode": "Modalità video",
|
||||
"channel_tab_videos_label": "Video",
|
||||
"Playlists": "Playlist",
|
||||
"channel_tab_community_label": "Comunità",
|
||||
"search_filters_sort_option_relevance": "Pertinenza",
|
||||
"search_filters_sort_option_rating": "Valutazione",
|
||||
"search_filters_sort_option_date": "Data di caricamento",
|
||||
@ -472,5 +471,13 @@
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"search_filters_apply_button": "Applica filtri selezionati",
|
||||
"crash_page_refresh": "provato a <a href=\"`x`\">ricaricare la pagina</a>",
|
||||
"error_video_not_in_playlist": "Il video richiesto non esiste in questa playlist. <a href=\"`x`\">Fai clic qui per la pagina iniziale della playlist.</a>"
|
||||
"error_video_not_in_playlist": "Il video richiesto non esiste in questa playlist. <a href=\"`x`\">Fai clic qui per la pagina iniziale della playlist.</a>",
|
||||
"channel_tab_shorts_label": "Short",
|
||||
"channel_tab_playlists_label": "Playlist",
|
||||
"channel_tab_channels_label": "Canali",
|
||||
"channel_tab_streams_label": "Livestream",
|
||||
"channel_tab_community_label": "Comunità",
|
||||
"Music in this video": "Musica in questo video",
|
||||
"Artist: ": "Artista: ",
|
||||
"Album: ": "Album: "
|
||||
}
|
||||
|
130
locales/ja.json
130
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 でチャンネルを見る",
|
||||
@ -53,34 +53,34 @@
|
||||
"E-mail": "メールアドレス",
|
||||
"Google verification code": "Google 認証コード",
|
||||
"Preferences": "設定",
|
||||
"preferences_category_player": "プレイヤー設定",
|
||||
"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_listen_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": "デフォルトでアノテーションを表示: ",
|
||||
"preferences_extend_desc_label": "動画の説明文を自動的に拡張: ",
|
||||
"preferences_vr_mode_label": "対話的な360°動画 (WebGL が必要): ",
|
||||
"preferences_category_visual": "外観設定",
|
||||
"preferences_player_style_label": "プレイヤースタイル: ",
|
||||
"preferences_player_style_label": "プレイヤーのスタイル: ",
|
||||
"Dark mode: ": "ダークモード: ",
|
||||
"preferences_dark_mode_label": "テーマ: ",
|
||||
"dark": "ダーク",
|
||||
"light": "ライト",
|
||||
"preferences_thin_mode_label": "最小モード: ",
|
||||
"preferences_category_misc": "雑設定",
|
||||
"preferences_automatic_instance_redirect_label": "自動的なインスタンスの移転(redirect.invidious.ioにフォールバック): ",
|
||||
"preferences_category_misc": "ほかの設定",
|
||||
"preferences_automatic_instance_redirect_label": "インスタンスの自動転送 (redirect.invidious.ioにフォールバック): ",
|
||||
"preferences_category_subscription": "登録チャンネル設定",
|
||||
"preferences_annotations_subscribed_label": "デフォルトで登録チャンネルのアノテーションを表示しますか? ",
|
||||
"Redirect homepage to feed: ": "ホームからフィードにリダイレクト: ",
|
||||
@ -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: ": "トップページを有効化: ",
|
||||
@ -117,8 +117,8 @@
|
||||
"Registration enabled: ": "登録を有効化: ",
|
||||
"Report statistics: ": "統計を報告: ",
|
||||
"Save preferences": "設定を保存",
|
||||
"Subscription manager": "登録チャンネルマネージャー",
|
||||
"Token manager": "トークンマネージャー",
|
||||
"Subscription manager": "登録チャンネルの管理",
|
||||
"Token manager": "トークンの管理",
|
||||
"Token": "トークン",
|
||||
"tokens_count_0": "{{count}} 個のトークン",
|
||||
"Import/export": "インポート/エクスポート",
|
||||
@ -128,7 +128,7 @@
|
||||
"subscriptions_unseen_notifs_count_0": "{{count}} 個の未読通知",
|
||||
"search": "検索",
|
||||
"Log out": "ログアウト",
|
||||
"Released under the AGPLv3 on Github.": "GitHub 上で AGPLv3 の元で公開されています。",
|
||||
"Released under the AGPLv3 on Github.": "GitHub 上で AGPLv3 の元で公開",
|
||||
"Source available here.": "ソースはここで閲覧可能です。",
|
||||
"View JavaScript license information.": "JavaScript ライセンス情報",
|
||||
"View privacy policy.": "プライバシーポリシー",
|
||||
@ -136,28 +136,28 @@
|
||||
"Public": "公開",
|
||||
"Unlisted": "限定公開",
|
||||
"Private": "非公開",
|
||||
"View all playlists": "再生リストをすべて見る",
|
||||
"View all playlists": "すべての再生リストを表示",
|
||||
"Updated `x` ago": "`x`前に更新",
|
||||
"Delete playlist `x`?": "再生リスト `x` を削除しますか?",
|
||||
"Delete playlist": "再生リストを削除",
|
||||
"Create playlist": "再生リストを作成",
|
||||
"Title": "タイトル",
|
||||
"Playlist privacy": "再生リストのプライバシー",
|
||||
"Playlist privacy": "再生リストの公開設定",
|
||||
"Editing playlist `x`": "再生リスト `x` を編集中",
|
||||
"Show more": "表示を増やす",
|
||||
"Show less": "表示を減らす",
|
||||
"Show more": "もっと見る",
|
||||
"Show less": "表示を少なく",
|
||||
"Watch on YouTube": "YouTube で視聴",
|
||||
"Switch Invidious Instance": "Invidiousインスタンスの変更",
|
||||
"Switch Invidious Instance": "Invidious インスタンスの変更",
|
||||
"Hide annotations": "アノテーションを隠す",
|
||||
"Show annotations": "アノテーションを表示",
|
||||
"Genre: ": "ジャンル: ",
|
||||
"License: ": "ライセンス: ",
|
||||
"Family friendly? ": "家族向け: ",
|
||||
"Wilson score: ": "ウィルソンスコア: ",
|
||||
"Wilson score: ": "ウィルソン得点区間: ",
|
||||
"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 を無効にしているのかな?ここをクリックしてコメントを見れるけど、読み込みには少し時間がかかることがあるのを覚えておいてね。",
|
||||
@ -181,31 +181,31 @@
|
||||
"User ID is a required field": "ユーザー ID は必須項目です",
|
||||
"Password is a required field": "パスワードは必須項目です",
|
||||
"Wrong username or password": "ユーザー名またはパスワードが間違っています",
|
||||
"Please sign in using 'Log in with Google'": "'Google でログイン' を使用してログインしてください",
|
||||
"Password cannot be empty": "パスワードを空にすることはできません",
|
||||
"Please sign in using 'Log in with Google'": "「Google でログイン」を使用してログインしてください",
|
||||
"Password cannot be empty": "パスワードは空にできません",
|
||||
"Password cannot be longer than 55 characters": "パスワードは55文字より長くできません",
|
||||
"Please log in": "ログインをしてください",
|
||||
"Invidious Private Feed for `x`": "`x` の Invidious プライベートフィード",
|
||||
"Please log in": "ログインしてください",
|
||||
"Invidious Private Feed for `x`": "`x` 個人の Invidious によるフィード",
|
||||
"channel:`x`": "チャンネル:`x`",
|
||||
"Deleted or invalid channel": "削除済みまたは無効なチャンネルです",
|
||||
"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": "もっと読み込む",
|
||||
"comments_points_count_0": "{{count}} ポイント",
|
||||
"Load more": "もっと見る",
|
||||
"comments_points_count_0": "{{count}}点",
|
||||
"Could not create mix.": "ミックスを作成できませんでした。",
|
||||
"Empty playlist": "空の再生リスト",
|
||||
"Not a playlist.": "再生リストではありません。",
|
||||
"Playlist does not exist.": "再生リストが存在しません。",
|
||||
"Could not pull trending pages.": "急上昇ページを取得できませんでした。",
|
||||
"Hidden field \"challenge\" is a required field": "非表示項目 \"challenge\" は必須項目です",
|
||||
"Hidden field \"token\" is a required field": "非表示項目 \"token\" は必須項目です",
|
||||
"Hidden field \"challenge\" is a required field": "非表示項目 challenge は必須項目です",
|
||||
"Hidden field \"token\" is a required field": "非表示項目 token は必須項目です",
|
||||
"Erroneous challenge": "チャレンジが間違っています",
|
||||
"Erroneous token": "トークンが間違っています",
|
||||
"No such user": "ユーザーが存在しません",
|
||||
"Token is expired, please try again": "トークンが期限切れです。再度試してください",
|
||||
"Token is expired, please try again": "トークンが期限切れです。再度お試しください",
|
||||
"English": "英語",
|
||||
"English (auto-generated)": "英語 (自動生成)",
|
||||
"Afrikaans": "アフリカーンス語",
|
||||
@ -313,7 +313,7 @@
|
||||
"Yoruba": "ヨルバ語",
|
||||
"Zulu": "ズール語",
|
||||
"generic_count_years_0": "{{count}}年",
|
||||
"generic_count_months_0": "{{count}}ヶ月",
|
||||
"generic_count_months_0": "{{count}}か月",
|
||||
"generic_count_weeks_0": "{{count}}週",
|
||||
"generic_count_days_0": "{{count}}日",
|
||||
"generic_count_hours_0": "{{count}}時間",
|
||||
@ -338,21 +338,21 @@
|
||||
"(edited)": "(編集済み)",
|
||||
"YouTube comment permalink": "YouTube コメントのパーマリンク",
|
||||
"permalink": "パーマリンク",
|
||||
"`x` marked it with a ❤": "`x` が❤を込めてマークしました",
|
||||
"Audio mode": "オーディオモード",
|
||||
"Video mode": "ビデオモード",
|
||||
"`x` marked it with a ❤": "`x` が❤を送りました",
|
||||
"Audio mode": "音声モード",
|
||||
"Video mode": "動画モード",
|
||||
"channel_tab_videos_label": "動画",
|
||||
"Playlists": "プレイリスト",
|
||||
"Playlists": "再生リスト",
|
||||
"channel_tab_community_label": "コミュニティ",
|
||||
"search_filters_sort_option_relevance": "関連",
|
||||
"search_filters_sort_option_relevance": "関連度",
|
||||
"search_filters_sort_option_rating": "評価",
|
||||
"search_filters_sort_option_date": "時刻",
|
||||
"search_filters_sort_option_date": "アップロード日",
|
||||
"search_filters_sort_option_views": "再生回数",
|
||||
"search_filters_type_label": "コンテンツの種類",
|
||||
"search_filters_type_label": "種類",
|
||||
"search_filters_duration_label": "再生時間",
|
||||
"search_filters_features_label": "機能",
|
||||
"search_filters_features_label": "特徴",
|
||||
"search_filters_sort_label": "順番",
|
||||
"search_filters_date_option_hour": "1時間前",
|
||||
"search_filters_date_option_hour": "1時間以内",
|
||||
"search_filters_date_option_today": "今日",
|
||||
"search_filters_date_option_week": "今週",
|
||||
"search_filters_date_option_month": "今月",
|
||||
@ -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,9 +377,9 @@
|
||||
"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",
|
||||
"footer_original_source_code": "元のソースコード",
|
||||
"footer_modfied_source_code": "改変して使用",
|
||||
"adminprefs_modified_source_code_url_label": "改変されたソースコードのレポジトリのURL",
|
||||
"search_filters_duration_option_long": "20 分以上",
|
||||
"preferences_region_label": "地域: ",
|
||||
"footer_donate_page": "寄付する",
|
||||
@ -406,10 +406,10 @@
|
||||
"preferences_quality_option_dash": "DASH (適応品質)",
|
||||
"preferences_quality_dash_option_worst": "最悪",
|
||||
"preferences_quality_dash_option_best": "最高",
|
||||
"videoinfo_started_streaming_x_ago": "`x`分前に配信を開始",
|
||||
"videoinfo_started_streaming_x_ago": "`x`前に配信を開始",
|
||||
"videoinfo_watch_on_youTube": "YouTube上で見る",
|
||||
"user_created_playlists": "`x`が作成したプレイリスト",
|
||||
"Video unavailable": "ビデオは利用できません",
|
||||
"user_created_playlists": "`x`個の作成した再生リスト",
|
||||
"Video unavailable": "動画は利用できません",
|
||||
"Chinese": "中国語",
|
||||
"Chinese (Taiwan)": "中国語 (台湾)",
|
||||
"Korean (auto-generated)": "韓国語 (自動生成)",
|
||||
@ -434,24 +434,34 @@
|
||||
"Vietnamese (auto-generated)": "ベトナム語 (自動生成)",
|
||||
"search_filters_title": "フィルタ",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"search_message_change_filters_or_query": "別のキーワードを試してみるか、検索フィルタを削除してください",
|
||||
"search_message_no_results": "一致する検索結果はありませんでした",
|
||||
"search_message_change_filters_or_query": "別の検索語句を試したり、検索フィルタを変更してください。",
|
||||
"search_message_no_results": "一致する検索結果はありません。",
|
||||
"English (United States)": "英語 (アメリカ)",
|
||||
"search_filters_date_label": "アップロード日",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"crash_page_switch_instance": "<a href=\"`x`\">別のインスタンスを使用</a>しようとしました",
|
||||
"crash_page_switch_instance": "<a href=\"`x`\">別のインスタンスを使用</a>を試す",
|
||||
"crash_page_read_the_faq": "<a href=\"`x`\">よくある質問 (FAQ)</a> を読む",
|
||||
"Popular enabled: ": "人気動画を有効化 ",
|
||||
"search_message_use_another_instance": " <a href=\"`x`\">別のインスタンスで検索</a>することもできます。",
|
||||
"search_message_use_another_instance": " <a href=\"`x`\">別のインスタンス上でも検索</a>できます。",
|
||||
"search_filters_apply_button": "選択したフィルターを適用",
|
||||
"user_saved_playlists": "`x` 個の保存済みプレイリスト",
|
||||
"user_saved_playlists": "`x` 個の保存した再生リスト",
|
||||
"crash_page_you_found_a_bug": "Invidious でバグを見つけたようです。",
|
||||
"crash_page_refresh": "<a href=\"`x`\">ページを更新</a>しようとしました",
|
||||
"preferences_watch_history_label": "視聴履歴を有効化 ",
|
||||
"search_filters_date_option_none": "任意の日付",
|
||||
"search_filters_type_option_all": "いかなるタイプ",
|
||||
"search_filters_duration_option_none": "任意の期間",
|
||||
"search_filters_duration_option_medium": "ミディアム (4 ~ 20 分)",
|
||||
"crash_page_refresh": "<a href=\"`x`\">ページを更新</a>を試す",
|
||||
"preferences_watch_history_label": "再生履歴を有効化 ",
|
||||
"search_filters_date_option_none": "すべて",
|
||||
"search_filters_type_option_all": "すべての種類",
|
||||
"search_filters_duration_option_none": "すべての長さ",
|
||||
"search_filters_duration_option_medium": "4 ~ 20 分",
|
||||
"preferences_save_player_pos_label": "再生位置を保存: ",
|
||||
"crash_page_before_reporting": "バグを報告する前に、次のことを確認してください。"
|
||||
"crash_page_before_reporting": "バグを報告する前に、次のことを確認してください。",
|
||||
"crash_page_report_issue": "上記が助けにならないなら、<a href=\"`x`\">GitHub</a> に新しい issue を作成し(英語が好ましい)、メッセージに次のテキストを含めてください(テキストは翻訳しない)。",
|
||||
"crash_page_search_issue": "<a href=\"`x`\">GitHub の既存の問題 (issue)</a> を検索",
|
||||
"channel_tab_streams_label": "ライブ",
|
||||
"channel_tab_playlists_label": "再生リスト",
|
||||
"error_video_not_in_playlist": "要求された動画はこの再生リスト内に存在しません。<a href=\"`x`\">再生リストのホームへ。</a>",
|
||||
"channel_tab_shorts_label": "ショート",
|
||||
"channel_tab_channels_label": "チャンネル",
|
||||
"Music in this video": "この動画の音楽",
|
||||
"Artist: ": "アーティスト: ",
|
||||
"Album: ": "アルバム: "
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
"preferences_dark_mode_label": "테마: ",
|
||||
"Dark mode: ": "다크 모드: ",
|
||||
"preferences_player_style_label": "플레이어 스타일: ",
|
||||
"preferences_category_visual": "시각 설정",
|
||||
"preferences_category_visual": "환경 설정",
|
||||
"preferences_vr_mode_label": "VR 영상 활성화(WebGL 필요): ",
|
||||
"preferences_extend_desc_label": "자동으로 비디오 설명을 확장: ",
|
||||
"preferences_annotations_label": "기본으로 주석 표시: ",
|
||||
@ -150,7 +150,7 @@
|
||||
"Subscription manager": "구독 관리자",
|
||||
"Save preferences": "설정 저장",
|
||||
"Report statistics: ": "통계 보고: ",
|
||||
"Registration enabled: ": "등록 활성화: ",
|
||||
"Registration enabled: ": "회원가입 활성화: ",
|
||||
"Login enabled: ": "로그인 활성화: ",
|
||||
"CAPTCHA enabled: ": "캡차 활성화: ",
|
||||
"Top enabled: ": "Top 활성화: ",
|
||||
@ -187,8 +187,8 @@
|
||||
"Polish": "폴란드어",
|
||||
"Persian": "페르시아어",
|
||||
"Pashto": "파슈토어",
|
||||
"Nyanja": "체와어",
|
||||
"Norwegian Bokmål": "보크몰",
|
||||
"Nyanja": "냔자어",
|
||||
"Norwegian Bokmål": "노르웨이 부크몰어",
|
||||
"Nepali": "네팔어",
|
||||
"Mongolian": "몽골어",
|
||||
"Marathi": "마라티어",
|
||||
@ -442,7 +442,7 @@
|
||||
"preferences_save_player_pos_label": "이어서 보기: ",
|
||||
"none": "없음",
|
||||
"videoinfo_started_streaming_x_ago": "`x` 전에 스트리밍을 시작했습니다",
|
||||
"crash_page_you_found_a_bug": "Invidious에서 버그를 찾은 것 같습니다!",
|
||||
"crash_page_you_found_a_bug": "인비디어스에서 버그를 찾은 것 같습니다!",
|
||||
"download_subtitles": "자막 - `x`(.vtt)",
|
||||
"user_saved_playlists": "`x`개의 저장된 재생목록",
|
||||
"crash_page_before_reporting": "버그를 보고하기 전에 다음 사항이 있는지 확인합니다:",
|
||||
@ -456,5 +456,9 @@
|
||||
"crash_page_report_issue": "위의 방법 중 어느 것도 도움이 되지 않았다면, <a href=\"`x`\">깃허브에서 새 이슈를 열고</a>(가능하면 영어로) 메시지에 다음 텍스트를 포함하세요(해당 텍스트를 번역하지 마십시오):",
|
||||
"videoinfo_youTube_embed_link": "임베드",
|
||||
"videoinfo_invidious_embed_link": "임베드 링크",
|
||||
"error_video_not_in_playlist": "요청한 동영상이 이 재생목록에 없습니다. <a href=\"`x`\">재생목록 목록을 보려면 여기를 클릭하십시오.</a>"
|
||||
"error_video_not_in_playlist": "요청한 동영상이 이 재생목록에 없습니다. <a href=\"`x`\">재생목록 목록을 보려면 여기를 클릭하십시오.</a>",
|
||||
"channel_tab_shorts_label": "쇼츠",
|
||||
"channel_tab_streams_label": "실시간 스트리밍",
|
||||
"channel_tab_channels_label": "채널",
|
||||
"channel_tab_playlists_label": "재생목록"
|
||||
}
|
||||
|
@ -67,7 +67,7 @@
|
||||
"preferences_annotations_label": "Domyślnie pokazuj adnotacje: ",
|
||||
"preferences_extend_desc_label": "Automatycznie rozwijaj opisy filmów: ",
|
||||
"preferences_vr_mode_label": "Interaktywne filmy 360 stopni (wymaga WebGL): ",
|
||||
"preferences_category_visual": "Preferencje Wizualne",
|
||||
"preferences_category_visual": "Preferencje wizualne",
|
||||
"preferences_player_style_label": "Styl odtwarzacza: ",
|
||||
"Dark mode: ": "Ciemny motyw: ",
|
||||
"preferences_dark_mode_label": "Motyw: ",
|
||||
@ -324,7 +324,7 @@
|
||||
"`x` marked it with a ❤": "`x` oznaczonych ❤",
|
||||
"Audio mode": "Tryb audio",
|
||||
"Video mode": "Tryb wideo",
|
||||
"channel_tab_videos_label": "Filmy",
|
||||
"channel_tab_videos_label": "Wideo",
|
||||
"Playlists": "Playlisty",
|
||||
"channel_tab_community_label": "Społeczność",
|
||||
"search_filters_sort_option_relevance": "Trafność",
|
||||
@ -443,7 +443,7 @@
|
||||
"user_saved_playlists": "`x` zapisanych playlist",
|
||||
"Video unavailable": "Film niedostępny",
|
||||
"preferences_save_player_pos_label": "Zapisz pozycję odtwarzania: ",
|
||||
"preferences_region_label": "Region zawartości: ",
|
||||
"preferences_region_label": "Kraj treści: ",
|
||||
"Released under the AGPLv3 on Github.": "Wydany na licencji AGPLv3 na GitHub.",
|
||||
"search_filters_duration_option_short": "Krótka (< 4 minut)",
|
||||
"search_filters_duration_option_long": "Długa (> 20 minut)",
|
||||
@ -481,12 +481,19 @@
|
||||
"search_message_no_results": "Nie znaleziono wyników.",
|
||||
"preferences_watch_history_label": "Włącz historię oglądania: ",
|
||||
"search_filters_apply_button": "Zastosuj wybrane filtry",
|
||||
"search_message_change_filters_or_query": "Spróbuj poszerzyć zapytanie i/lub zmienić filtry.",
|
||||
"search_message_change_filters_or_query": "Spróbuj poszerzyć zapytanie wyszukiwania i/lub zmienić filtry.",
|
||||
"search_filters_date_label": "Data przesłania",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"search_filters_date_option_none": "Dowolna data",
|
||||
"search_message_use_another_instance": " Możesz także <a href=\"`x`\">wyszukać w innej instancji</a>.",
|
||||
"search_filters_type_option_all": "Dowolny typ",
|
||||
"search_filters_duration_option_none": "Dowolna długość",
|
||||
"search_filters_duration_option_medium": "Średnia (4-20 minut)"
|
||||
"search_filters_duration_option_medium": "Średnia (4-20 minut)",
|
||||
"channel_tab_streams_label": "Na żywo",
|
||||
"channel_tab_channels_label": "Kanały",
|
||||
"channel_tab_playlists_label": "Playlisty",
|
||||
"channel_tab_shorts_label": "Shorts",
|
||||
"Music in this video": "Muzyka w tym filmie",
|
||||
"Artist: ": "Wykonawca: ",
|
||||
"Album: ": "Album: "
|
||||
}
|
||||
|
@ -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",
|
||||
@ -472,5 +472,12 @@
|
||||
"search_filters_duration_option_medium": "Médio (4 - 20 minutos)",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"Popular enabled: ": "Popular habilitado: ",
|
||||
"error_video_not_in_playlist": "O vídeo solicitado não existe nesta playlist. <a href=\"`x`\">Clique aqui para acessar a página inicial da playlist.</a>"
|
||||
"error_video_not_in_playlist": "O vídeo solicitado não existe nesta playlist. <a href=\"`x`\">Clique aqui para acessar a página inicial da playlist.</a>",
|
||||
"channel_tab_channels_label": "Canais",
|
||||
"channel_tab_playlists_label": "Listas de reprodução",
|
||||
"channel_tab_shorts_label": "Curtos",
|
||||
"channel_tab_streams_label": "Ao Vivo",
|
||||
"Music in this video": "Música neste vídeo",
|
||||
"Artist: ": "Artista: ",
|
||||
"Album: ": "Álbum: "
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -472,5 +472,12 @@
|
||||
"search_filters_type_option_all": "Qualquer tipo",
|
||||
"search_filters_duration_option_none": "Qualquer duração",
|
||||
"Popular enabled: ": "Página \"popular\" ativada: ",
|
||||
"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>",
|
||||
"channel_tab_playlists_label": "Listas de reprodução",
|
||||
"channel_tab_channels_label": "Canais",
|
||||
"channel_tab_shorts_label": "Curtos",
|
||||
"channel_tab_streams_label": "Diretos",
|
||||
"Music in this video": "Música neste vídeo",
|
||||
"Artist: ": "Artista: ",
|
||||
"Album: ": "Álbum: "
|
||||
}
|
||||
|
@ -69,11 +69,11 @@
|
||||
"preferences_vr_mode_label": "Интерактивные 360-градусные видео (необходим WebGL): ",
|
||||
"preferences_category_visual": "Настройки сайта",
|
||||
"preferences_player_style_label": "Стиль проигрывателя: ",
|
||||
"Dark mode: ": "Тёмное оформление: ",
|
||||
"Dark mode: ": "Темное оформление: ",
|
||||
"preferences_dark_mode_label": "Тема: ",
|
||||
"dark": "тёмная",
|
||||
"dark": "темная",
|
||||
"light": "светлая",
|
||||
"preferences_thin_mode_label": "Облегчённое оформление: ",
|
||||
"preferences_thin_mode_label": "Облегченное оформление: ",
|
||||
"preferences_category_misc": "Прочие настройки",
|
||||
"preferences_automatic_instance_redirect_label": "Автоматическая смена зеркала (переход на redirect.invidious.io): ",
|
||||
"preferences_category_subscription": "Настройки подписок",
|
||||
@ -88,7 +88,7 @@
|
||||
"channel name": "по названию канала",
|
||||
"channel name - reverse": "по названию канала в обратном порядке",
|
||||
"Only show latest video from channel: ": "Показывать только последние видео с каналов: ",
|
||||
"Only show latest unwatched video from channel: ": "Показывать только непросмотренные видео с каналов: ",
|
||||
"Only show latest unwatched video from channel: ": "Показывать только последние непросмотренные видео с канала: ",
|
||||
"preferences_unseen_only_label": "Показывать только непросмотренные видео: ",
|
||||
"preferences_notifications_only_label": "Показывать только оповещения, если они есть: ",
|
||||
"Enable web notifications": "Включить уведомления в браузере",
|
||||
@ -147,13 +147,13 @@
|
||||
"License: ": "Лицензия: ",
|
||||
"Family friendly? ": "Семейный просмотр: ",
|
||||
"Wilson score: ": "Оценка Уилсона: ",
|
||||
"Engagement: ": "Вовлечённость: ",
|
||||
"Engagement: ": "Вовлеченность: ",
|
||||
"Whitelisted regions: ": "Доступно в регионах: ",
|
||||
"Blacklisted regions: ": "Недоступно в регионах: ",
|
||||
"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. Нажмите сюда, чтобы увидеть комментарии. Но учтите: они могут загружаться немного медленнее.",
|
||||
"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. Нажмите сюда, чтобы увидеть комментарии. Но учтите: они могут загружаться немного медленнее.",
|
||||
"View YouTube comments": "Показать комментарии с YouTube",
|
||||
"View more comments on Reddit": "Посмотреть больше комментариев на Reddit",
|
||||
"View `x` comments": {
|
||||
@ -180,23 +180,23 @@
|
||||
"Please log in": "Пожалуйста, войдите",
|
||||
"Invidious Private Feed for `x`": "Приватная лента Invidious для `x`",
|
||||
"channel:`x`": "канал: `x`",
|
||||
"Deleted or invalid channel": "Канал удалён или не найден",
|
||||
"Deleted or invalid channel": "Канал удален или не найден",
|
||||
"This channel does not exist.": "Такого канала не существует.",
|
||||
"Could not get channel info.": "Не удаётся получить информацию об этом канале.",
|
||||
"Could not fetch comments": "Не удаётся загрузить комментарии",
|
||||
"Could not get channel info.": "Не удается получить информацию об этом канале.",
|
||||
"Could not fetch comments": "Не удается загрузить комментарии",
|
||||
"`x` ago": "`x` назад",
|
||||
"Load more": "Загрузить ещё",
|
||||
"Load more": "Загрузить еще",
|
||||
"Could not create mix.": "Не удалось создать микс.",
|
||||
"Empty playlist": "Плейлист пуст",
|
||||
"Not a playlist.": "Некорректный плейлист.",
|
||||
"Not a playlist.": "Это не плейлист.",
|
||||
"Playlist does not exist.": "Плейлист не существует.",
|
||||
"Could not pull trending pages.": "Не удаётся загрузить страницы «в тренде».",
|
||||
"Could not pull trending pages.": "Не удается загрузить страницы «в тренде».",
|
||||
"Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле «challenge»",
|
||||
"Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле «токен»",
|
||||
"Erroneous challenge": "Неправильный ответ в «challenge»",
|
||||
"Erroneous token": "Неправильный токен",
|
||||
"No such user": "Пользователь не найден",
|
||||
"Token is expired, please try again": "Срок действия токена истёк, попробуйте позже",
|
||||
"Token is expired, please try again": "Срок действия токена истек, попробуйте позже",
|
||||
"English": "Английский",
|
||||
"English (auto-generated)": "Английский (созданы автоматически)",
|
||||
"Afrikaans": "Африкаанс",
|
||||
@ -379,7 +379,7 @@
|
||||
"Turkish (auto-generated)": "Турецкий (созданы автоматически)",
|
||||
"Vietnamese (auto-generated)": "Вьетнамский (созданы автоматически)",
|
||||
"footer_documentation": "Документация",
|
||||
"adminprefs_modified_source_code_url_label": "Ссылка на нашу ветку репозитория",
|
||||
"adminprefs_modified_source_code_url_label": "URL-адрес репозитория измененного исходного кода",
|
||||
"none": "ничего",
|
||||
"videoinfo_watch_on_youTube": "Смотреть на YouTube",
|
||||
"videoinfo_youTube_embed_link": "Версия для встраивания",
|
||||
@ -453,8 +453,8 @@
|
||||
"Portuguese (Brazil)": "Португальский (Бразилия)",
|
||||
"footer_source_code": "Исходный код",
|
||||
"footer_original_source_code": "Оригинальный исходный код",
|
||||
"footer_modfied_source_code": "Изменённый исходный код",
|
||||
"user_saved_playlists": "`x` сохранённых плейлистов",
|
||||
"footer_modfied_source_code": "Измененный исходный код",
|
||||
"user_saved_playlists": "`x` сохраненных плейлистов",
|
||||
"crash_page_search_issue": "поискали <a href=\"`x`\">похожую проблему на GitHub</a>",
|
||||
"comments_points_count_0": "{{count}} плюс",
|
||||
"comments_points_count_1": "{{count}} плюса",
|
||||
@ -488,5 +488,12 @@
|
||||
"search_filters_duration_option_medium": "Средние (4 - 20 минут)",
|
||||
"search_filters_apply_button": "Применить фильтры",
|
||||
"Popular enabled: ": "Популярное включено: ",
|
||||
"error_video_not_in_playlist": "Запрошенного видео нет в этом плейлисте. <a href=\"`x`\">Нажмите тут, чтобы вернуться к странице плейлиста.</a>"
|
||||
"error_video_not_in_playlist": "Запрошенного видео нет в этом плейлисте. <a href=\"`x`\">Нажмите тут, чтобы вернуться к странице плейлиста.</a>",
|
||||
"channel_tab_playlists_label": "Плейлисты",
|
||||
"channel_tab_channels_label": "Каналы",
|
||||
"channel_tab_streams_label": "Живое вещание",
|
||||
"channel_tab_shorts_label": "Shorts",
|
||||
"Music in this video": "Музыка в этом видео",
|
||||
"Artist: ": "Исполнитель: ",
|
||||
"Album: ": "Альбом: "
|
||||
}
|
||||
|
@ -206,7 +206,7 @@
|
||||
"generic_count_years_2": "{{count}} leti",
|
||||
"generic_count_years_3": "{{count}} leti",
|
||||
"generic_count_days_0": "{{count}} dnevom",
|
||||
"generic_count_days_1": "{{count}} dnevi",
|
||||
"generic_count_days_1": "{{count}} dnevoma",
|
||||
"generic_count_days_2": "{{count}} dnevi",
|
||||
"generic_count_days_3": "{{count}} dnevi",
|
||||
"generic_count_hours_0": "{{count}} uro",
|
||||
@ -246,10 +246,10 @@
|
||||
"generic_videos_count_1": "{{count}} videa",
|
||||
"generic_videos_count_2": "{{count}} videi",
|
||||
"generic_videos_count_3": "{{count}} videov",
|
||||
"generic_views_count_0": "{{count}} ogled",
|
||||
"generic_views_count_1": "{{count}} ogleda",
|
||||
"generic_views_count_2": "{{count}} ogledi",
|
||||
"generic_views_count_3": "{{count}} ogledov",
|
||||
"generic_views_count_0": "Ogledov: {{count}}",
|
||||
"generic_views_count_1": "Ogledov: {{count}}",
|
||||
"generic_views_count_2": "Ogledov: {{count}}",
|
||||
"generic_views_count_3": "Ogledov: {{count}}",
|
||||
"generic_playlists_count_0": "{{count}} seznam predvajanja",
|
||||
"generic_playlists_count_1": "{{count}} seznama predvajanja",
|
||||
"generic_playlists_count_2": "{{count}} seznami predvajanja",
|
||||
@ -495,7 +495,7 @@
|
||||
"footer_modfied_source_code": "Spremenjena izvorna koda",
|
||||
"user_created_playlists": "`x` ustvarjenih seznamov predvajanja",
|
||||
"adminprefs_modified_source_code_url_label": "URL do shrambe spremenjene izvorne kode",
|
||||
"videoinfo_youTube_embed_link": "Vdelati",
|
||||
"videoinfo_youTube_embed_link": "Vdelaj",
|
||||
"videoinfo_invidious_embed_link": "Povezava za vdelavo",
|
||||
"crash_page_switch_instance": "poskušal/a <a href=\"`x`\">uporabiti drugo instanco</a>",
|
||||
"download_subtitles": "Podnapisi - `x` (.vtt)",
|
||||
@ -504,5 +504,12 @@
|
||||
"crash_page_search_issue": "preiskal/a <a href=\"`x`\">obstoječe težave na GitHubu</a>",
|
||||
"crash_page_report_issue": "Če nič od navedenega ni pomagalo, prosim <a href=\"`x`\">odpri novo težavo v GitHubu</a> (po možnosti v angleščini) in v svoje sporočilo vključi naslednje besedilo (tega besedila NE prevajaj):",
|
||||
"Popular enabled: ": "Priljubljeni omogočeni: ",
|
||||
"error_video_not_in_playlist": "Zahtevani videoposnetek ne obstaja na tem seznamu predvajanja. <a href=\"`x`\">Klikni tukaj za domačo stran seznama predvajanja.</a>"
|
||||
"error_video_not_in_playlist": "Zahtevani videoposnetek ne obstaja na tem seznamu predvajanja. <a href=\"`x`\">Klikni tukaj za domačo stran seznama predvajanja.</a>",
|
||||
"channel_tab_playlists_label": "Seznami predvajanja",
|
||||
"channel_tab_shorts_label": "Kratki videoposnetki",
|
||||
"channel_tab_channels_label": "Kanali",
|
||||
"channel_tab_streams_label": "Prenosi v živo",
|
||||
"Artist: ": "Umetnik/ca: ",
|
||||
"Music in this video": "Glasba v tem videoposnetku",
|
||||
"Album: ": "Album: "
|
||||
}
|
||||
|
@ -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ë",
|
||||
@ -463,5 +463,12 @@
|
||||
"search_filters_duration_option_none": "Çfarëdo kohëzgjatjeje",
|
||||
"search_filters_duration_option_medium": "Mesatare (4 - 20 minuta)",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"search_filters_apply_button": "Apliko filtrat e përzgjedhur"
|
||||
"search_filters_apply_button": "Apliko filtrat e përzgjedhur",
|
||||
"channel_tab_playlists_label": "Luajlista",
|
||||
"Artist: ": "Artist: ",
|
||||
"Album: ": "Album: ",
|
||||
"channel_tab_channels_label": "Kanale",
|
||||
"Music in this video": "Muzikë në këtë video",
|
||||
"channel_tab_shorts_label": "Të shkurtra",
|
||||
"channel_tab_streams_label": "Transmetime të drejtpërdrejta"
|
||||
}
|
||||
|
@ -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: ",
|
||||
@ -397,8 +397,8 @@
|
||||
"videoinfo_watch_on_youTube": "YouTube'da İzle",
|
||||
"download_subtitles": "Alt Yazılar - `x` (.vtt)",
|
||||
"preferences_save_player_pos_label": "Oynatma Konumunu Kaydet: ",
|
||||
"generic_views_count": "{{count}} Görüntüleme",
|
||||
"generic_views_count_plural": "{{count}} Görüntüleme",
|
||||
"generic_views_count": "{{count}} Görüntülenme",
|
||||
"generic_views_count_plural": "{{count}} Görüntülenme",
|
||||
"generic_subscribers_count": "{{count}} Abone",
|
||||
"generic_subscribers_count_plural": "{{count}} Abone",
|
||||
"generic_subscriptions_count": "{{count}} Abonelik",
|
||||
@ -472,5 +472,12 @@
|
||||
"search_filters_title": "Filtreler",
|
||||
"search_message_change_filters_or_query": "Arama sorgunuzu genişletmeyi ve/veya filtreleri değiştirmeyi deneyin.",
|
||||
"Popular enabled: ": "Popüler Etkin: ",
|
||||
"error_video_not_in_playlist": "İstenen video bu oynatma listesinde yok. <a href=\"`x`\">Oynatma listesi ana sayfası için buraya tıklayın.</a>"
|
||||
"error_video_not_in_playlist": "İstenen video bu oynatma listesinde yok. <a href=\"`x`\">Oynatma listesi ana sayfası için buraya tıklayın.</a>",
|
||||
"channel_tab_channels_label": "Kanallar",
|
||||
"channel_tab_shorts_label": "Kısa Çekimler",
|
||||
"channel_tab_streams_label": "Canlı Yayınlar",
|
||||
"channel_tab_playlists_label": "Oynatma Listeleri",
|
||||
"Album: ": "Albüm: ",
|
||||
"Music in this video": "Bu videodaki müzik",
|
||||
"Artist: ": "Sanatçı: "
|
||||
}
|
||||
|
@ -54,7 +54,7 @@
|
||||
"preferences_continue_label": "Завжди вмикати наступне відео: ",
|
||||
"preferences_continue_autoplay_label": "Автовідтворення наступного відео: ",
|
||||
"preferences_listen_label": "Режим «тільки звук» як усталений: ",
|
||||
"preferences_local_label": "Програвати відео через проксі? ",
|
||||
"preferences_local_label": "Відтворення відео через проксі: ",
|
||||
"preferences_speed_label": "Усталена швидкість відео: ",
|
||||
"preferences_quality_label": "Пріорітетна якість відео: ",
|
||||
"preferences_volume_label": "Гучність відео: ",
|
||||
@ -63,13 +63,13 @@
|
||||
"reddit": "Reddit",
|
||||
"preferences_captions_label": "Основна мова субтитрів: ",
|
||||
"Fallback captions: ": "Запасна мова субтитрів: ",
|
||||
"preferences_related_videos_label": "Показувати схожі відео? ",
|
||||
"preferences_annotations_label": "Завжди показувати анотації? ",
|
||||
"preferences_related_videos_label": "Показувати схожі відео: ",
|
||||
"preferences_annotations_label": "Завжди показувати анотації: ",
|
||||
"preferences_category_visual": "Налаштування сайту",
|
||||
"preferences_player_style_label": "Стиль програвача: ",
|
||||
"Dark mode: ": "Темне оформлення: ",
|
||||
"Dark mode: ": "Темний режим: ",
|
||||
"preferences_dark_mode_label": "Тема: ",
|
||||
"dark": "темна",
|
||||
"dark": "Темна",
|
||||
"light": "Світла",
|
||||
"preferences_thin_mode_label": "Полегшене оформлення: ",
|
||||
"preferences_category_subscription": "Налаштування підписок",
|
||||
@ -101,11 +101,11 @@
|
||||
"preferences_category_admin": "Адміністраторські налаштування",
|
||||
"preferences_default_home_label": "Усталена домашня сторінка: ",
|
||||
"preferences_feed_menu_label": "Меню потоку з відео: ",
|
||||
"Top enabled: ": "Увімкнути топ відео? ",
|
||||
"CAPTCHA enabled: ": "Увімкнути капчу? ",
|
||||
"Login enabled: ": "Увімкнути авторизацію? ",
|
||||
"Registration enabled: ": "Увімкнути реєстрацію? ",
|
||||
"Report statistics: ": "Повідомляти статистику? ",
|
||||
"Top enabled: ": "Увімкнути топ відео: ",
|
||||
"CAPTCHA enabled: ": "Увімкнути CAPTCHA: ",
|
||||
"Login enabled: ": "Увімкнути вхід: ",
|
||||
"Registration enabled: ": "Увімкнути реєстрацію: ",
|
||||
"Report statistics: ": "Повідомляти статистику: ",
|
||||
"Save preferences": "Зберегти налаштування",
|
||||
"Subscription manager": "Менеджер підписок",
|
||||
"Token manager": "Менеджер токенів",
|
||||
@ -125,7 +125,7 @@
|
||||
"Private": "Особистий",
|
||||
"View all playlists": "Переглянути всі списки відтворення",
|
||||
"Updated `x` ago": "Оновлено `x` тому",
|
||||
"Delete playlist `x`?": "Видалити список відтворення \"x\"?",
|
||||
"Delete playlist `x`?": "Видалити список відтворення `x`?",
|
||||
"Delete playlist": "Видалити список відтворення",
|
||||
"Create playlist": "Створити список відтворення",
|
||||
"Title": "Заголовок",
|
||||
@ -386,12 +386,12 @@
|
||||
"Spanish (Mexico)": "Іспанська (Мексика)",
|
||||
"Spanish (Spain)": "Іспанська (Іспанія)",
|
||||
"next_steps_error_message_go_to_youtube": "Перейти до YouTube",
|
||||
"footer_donate_page": "Пожертвувати",
|
||||
"footer_donate_page": "Підтримати",
|
||||
"footer_documentation": "Документація",
|
||||
"footer_source_code": "Вихідний код",
|
||||
"footer_original_source_code": "Оригінал вихідного коду",
|
||||
"footer_modfied_source_code": "Змінений вихідний код",
|
||||
"adminprefs_modified_source_code_url_label": "URL-адреса репозиторію зміненого вихідного коду",
|
||||
"footer_source_code": "Джерельний код",
|
||||
"footer_original_source_code": "Оригінал джерельного коду",
|
||||
"footer_modfied_source_code": "Змінений джерельний код",
|
||||
"adminprefs_modified_source_code_url_label": "URL-адреса репозиторію зміненого джерельного коду",
|
||||
"none": "нема",
|
||||
"videoinfo_started_streaming_x_ago": "Трансляцію розпочато `x` тому",
|
||||
"crash_page_you_found_a_bug": "Схоже, ви знайшли ваду в Invidious!",
|
||||
@ -408,7 +408,7 @@
|
||||
"next_steps_error_message": "Після чого спробуйте: ",
|
||||
"next_steps_error_message_refresh": "Оновити сторінку",
|
||||
"Search": "Пошук",
|
||||
"preferences_extend_desc_label": "Автоматично розширювати опис відео: ",
|
||||
"preferences_extend_desc_label": "Автоматично розгортати опис відео: ",
|
||||
"preferences_category_misc": "Різноманітні параметри",
|
||||
"Show less": "Коротше",
|
||||
"preferences_quality_option_small": "Низька",
|
||||
@ -488,5 +488,12 @@
|
||||
"search_filters_sort_option_rating": "Рейтингові",
|
||||
"search_filters_sort_option_views": "Популярні",
|
||||
"Popular enabled: ": "Популярне ввімкнено: ",
|
||||
"error_video_not_in_playlist": "Запитуваного відео в цьому списку відтворення не існує. <a href=\"`x`\">Клацніть тут, щоб переглянути домашню сторінку списку відтворення.</a>"
|
||||
"error_video_not_in_playlist": "Запитуваного відео в цьому списку відтворення не існує. <a href=\"`x`\">Клацніть тут, щоб переглянути домашню сторінку списку відтворення.</a>",
|
||||
"channel_tab_shorts_label": "Shorts",
|
||||
"channel_tab_streams_label": "Прямі трансляції",
|
||||
"channel_tab_playlists_label": "Добірки",
|
||||
"channel_tab_channels_label": "Канали",
|
||||
"Music in this video": "Музика в цьому відео",
|
||||
"Artist: ": "Виконавець: ",
|
||||
"Album: ": "Альбом: "
|
||||
}
|
||||
|
@ -456,5 +456,12 @@
|
||||
"search_filters_type_option_all": "任意类型",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"Popular enabled: ": "已启用流行度: ",
|
||||
"error_video_not_in_playlist": "此播放列表中不存在请求的视频。 <a href=\"`x`\">单击析出查看播放列表主页。</a>"
|
||||
"error_video_not_in_playlist": "此播放列表中不存在请求的视频。 <a href=\"`x`\">单击析出查看播放列表主页。</a>",
|
||||
"Music in this video": "此视频中的音乐",
|
||||
"channel_tab_playlists_label": "播放列表",
|
||||
"Artist: ": "艺术家: ",
|
||||
"channel_tab_streams_label": "直播",
|
||||
"Album: ": "专辑: ",
|
||||
"channel_tab_shorts_label": "短视频",
|
||||
"channel_tab_channels_label": "频道"
|
||||
}
|
||||
|
@ -456,5 +456,12 @@
|
||||
"search_filters_type_option_all": "任何類型",
|
||||
"search_filters_date_option_none": "任何日期",
|
||||
"Popular enabled: ": "已啟用人氣: ",
|
||||
"error_video_not_in_playlist": "此播放清單不存在請求的影片。<a href=\"`x`\">點擊此處檢視播放清單首頁。</a>"
|
||||
"error_video_not_in_playlist": "此播放清單不存在請求的影片。<a href=\"`x`\">點擊此處檢視播放清單首頁。</a>",
|
||||
"channel_tab_shorts_label": "短片",
|
||||
"channel_tab_playlists_label": "播放清單",
|
||||
"channel_tab_channels_label": "頻道",
|
||||
"channel_tab_streams_label": "直播",
|
||||
"Artist: ": "藝術家: ",
|
||||
"Album: ": "專輯: ",
|
||||
"Music in this video": "此影片中的音樂"
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ dependencies_to_install.each do |dep|
|
||||
dep = "videojs.markers" if dep == "videojs-markers"
|
||||
|
||||
if File.exists?("#{download_path}/package/dist/#{dep}.css")
|
||||
if minified && File.exists?("#{tmp_dir_path}/#{dep}/package/dist/#{dep}.min.css")
|
||||
if minified && File.exists?("#{download_path}/package/dist/#{dep}.min.css")
|
||||
`mv #{download_path}/package/dist/#{dep}.min.css #{dest_path}/#{dep}.css`
|
||||
else
|
||||
`mv #{download_path}/package/dist/#{dep}.css #{dest_path}/#{dep}.css`
|
||||
|
@ -69,7 +69,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
|
||||
next if !post
|
||||
|
||||
content_html = post["contentText"]?.try { |t| parse_content(t) } || ""
|
||||
author = post["authorText"]?.try &.["simpleText"]? || ""
|
||||
author = post["authorText"]["runs"]?.try &.[0]?.try &.["text"]? || ""
|
||||
|
||||
json.object do
|
||||
json.field "author", author
|
||||
@ -189,6 +189,32 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
|
||||
# when .has_key?("pollRenderer")
|
||||
# attachment = attachment["pollRenderer"]
|
||||
# json.field "type", "poll"
|
||||
when .has_key?("postMultiImageRenderer")
|
||||
attachment = attachment["postMultiImageRenderer"]
|
||||
json.field "type", "multiImage"
|
||||
json.field "images" do
|
||||
json.array do
|
||||
attachment["images"].as_a.each do |image|
|
||||
json.array do
|
||||
thumbnail = image["backstageImageRenderer"]["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")
|
||||
|
||||
qualities = {320, 560, 640, 1280, 2000}
|
||||
|
||||
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
|
||||
else
|
||||
json.field "type", "unknown"
|
||||
json.field "error", "Unrecognized attachment type."
|
||||
|
@ -181,6 +181,8 @@ def fetch_youtube_comments(id, cursor, format, locale, thin_mode, region, sort_b
|
||||
json.field "content", html_to_content(content_html)
|
||||
json.field "contentHtml", content_html
|
||||
|
||||
json.field "isPinned", (node_comment["pinnedCommentBadge"]? != nil)
|
||||
|
||||
json.field "published", published.to_unix
|
||||
json.field "publishedText", translate(locale, "`x` ago", recode_date(published, locale))
|
||||
|
||||
@ -670,6 +672,7 @@ def content_to_comment_html(content, video_id : String? = "")
|
||||
end
|
||||
|
||||
text = "<b>#{text}</b>" if run["bold"]?
|
||||
text = "<s>#{text}</s>" if run["strikethrough"]?
|
||||
text = "<i>#{text}</i>" if run["italics"]?
|
||||
|
||||
text
|
||||
|
@ -20,7 +20,7 @@ module JSONFilter
|
||||
/^\(|\(\(|\/\(/
|
||||
end
|
||||
|
||||
def self.parse_fields(fields_text : String) : Nil
|
||||
def self.parse_fields(fields_text : String, &) : Nil
|
||||
if fields_text.empty?
|
||||
raise FieldsParser::ParseError.new "Fields is empty"
|
||||
end
|
||||
@ -42,7 +42,7 @@ module JSONFilter
|
||||
parse_nest_groups(fields_text) { |nest_list| yield nest_list }
|
||||
end
|
||||
|
||||
def self.parse_single_nests(fields_text : String) : Nil
|
||||
def self.parse_single_nests(fields_text : String, &) : Nil
|
||||
single_nests = remove_nest_groups(fields_text)
|
||||
|
||||
if !single_nests.empty?
|
||||
@ -60,7 +60,7 @@ module JSONFilter
|
||||
end
|
||||
end
|
||||
|
||||
def self.parse_nest_groups(fields_text : String) : Nil
|
||||
def self.parse_nest_groups(fields_text : String, &) : Nil
|
||||
nest_stack = [] of NamedTuple(group_name: String, closing_bracket_index: Int64)
|
||||
bracket_pairs = get_bracket_pairs(fields_text, true)
|
||||
|
||||
|
@ -74,6 +74,7 @@ struct SearchVideo
|
||||
json.field "author", self.author
|
||||
json.field "authorId", self.ucid
|
||||
json.field "authorUrl", "/channel/#{self.ucid}"
|
||||
json.field "authorVerified", self.author_verified
|
||||
|
||||
json.field "videoThumbnails" do
|
||||
Invidious::JSONify::APIv1.thumbnails(json, self.id)
|
||||
|
@ -162,7 +162,7 @@ def number_with_separator(number)
|
||||
end
|
||||
|
||||
def short_text_to_number(short_text : String) : Int64
|
||||
matches = /(?<number>\d+(\.\d+)?)\s?(?<suffix>[mMkKbB])?/.match(short_text)
|
||||
matches = /(?<number>\d+(\.\d+)?)\s?(?<suffix>[mMkKbB]?)/.match(short_text)
|
||||
number = matches.try &.["number"].to_f || 0.0
|
||||
|
||||
case matches.try &.["suffix"].downcase
|
||||
@ -259,7 +259,7 @@ def get_referer(env, fallback = "/", unroll = true)
|
||||
end
|
||||
|
||||
referer = referer.request_target
|
||||
referer = "/" + referer.gsub(/[^\/?@&%=\-_.0-9a-zA-Z]/, "").lstrip("/\\")
|
||||
referer = "/" + referer.gsub(/[^\/?@&%=\-_.:,0-9a-zA-Z]/, "").lstrip("/\\")
|
||||
|
||||
if referer == env.request.path
|
||||
referer = fallback
|
||||
|
@ -203,7 +203,7 @@ module Invidious::Routes::Account
|
||||
referer = get_referer(env)
|
||||
|
||||
if !user
|
||||
return env.redirect referer
|
||||
return env.redirect "/login?referer=#{URI.encode_path_segment(env.request.resource)}"
|
||||
end
|
||||
|
||||
user = user.as(User)
|
||||
@ -262,6 +262,7 @@ module Invidious::Routes::Account
|
||||
end
|
||||
|
||||
query["token"] = access_token
|
||||
query["username"] = URI.encode_path_segment(user.email)
|
||||
url.query = query.to_s
|
||||
|
||||
env.redirect url.to_s
|
||||
|
@ -31,6 +31,88 @@ module Invidious::Routes::API::V1::Authenticated
|
||||
env.response.status_code = 204
|
||||
end
|
||||
|
||||
def self.export_invidious(env)
|
||||
env.response.content_type = "application/json"
|
||||
user = env.get("user").as(User)
|
||||
|
||||
return Invidious::User::Export.to_invidious(user)
|
||||
end
|
||||
|
||||
def self.import_invidious(env)
|
||||
user = env.get("user").as(User)
|
||||
|
||||
begin
|
||||
if body = env.request.body
|
||||
body = env.request.body.not_nil!.gets_to_end
|
||||
else
|
||||
body = "{}"
|
||||
end
|
||||
Invidious::User::Import.from_invidious(user, body)
|
||||
rescue
|
||||
end
|
||||
|
||||
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"
|
||||
|
||||
|
@ -89,6 +89,8 @@ module Invidious::Routes::API::V1::Channels
|
||||
json.field "descriptionHtml", channel.description_html
|
||||
|
||||
json.field "allowedRegions", channel.allowed_regions
|
||||
json.field "tabs", channel.tabs
|
||||
json.field "authorVerified", channel.verified
|
||||
|
||||
json.field "latestVideos" do
|
||||
json.array do
|
||||
|
@ -150,4 +150,31 @@ module Invidious::Routes::API::V1::Misc
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
# resolve channel and clip urls, return the UCID
|
||||
def self.resolve_url(env)
|
||||
env.response.content_type = "application/json"
|
||||
url = env.params.query["url"]?
|
||||
|
||||
return error_json(400, "Missing URL to resolve") if !url
|
||||
|
||||
begin
|
||||
resolved_url = YoutubeAPI.resolve_url(url.as(String))
|
||||
endpoint = resolved_url["endpoint"]
|
||||
pageType = endpoint.dig?("commandMetadata", "webCommandMetadata", "webPageType").try &.as_s || ""
|
||||
if resolved_ucid = endpoint.dig?("watchEndpoint", "videoId")
|
||||
elsif resolved_ucid = endpoint.dig?("browseEndpoint", "browseId")
|
||||
elsif pageType == "WEB_PAGE_TYPE_UNKNOWN"
|
||||
return error_json(400, "Unknown url")
|
||||
end
|
||||
rescue ex
|
||||
return error_json(500, ex)
|
||||
end
|
||||
JSON.build do |json|
|
||||
json.object do
|
||||
json.field "ucid", resolved_ucid.try &.as_s || ""
|
||||
json.field "pageType", pageType
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -93,45 +93,50 @@ module Invidious::Routes::API::V1::Videos
|
||||
# as well as some other markup that makes it cumbersome, so we try to fix that here
|
||||
if caption.name.includes? "auto-generated"
|
||||
caption_xml = YT_POOL.client &.get(url).body
|
||||
caption_xml = XML.parse(caption_xml)
|
||||
|
||||
webvtt = String.build do |str|
|
||||
str << <<-END_VTT
|
||||
WEBVTT
|
||||
Kind: captions
|
||||
Language: #{tlang || caption.language_code}
|
||||
if caption_xml.starts_with?("<?xml")
|
||||
webvtt = caption.timedtext_to_vtt(caption_xml, tlang)
|
||||
else
|
||||
caption_xml = XML.parse(caption_xml)
|
||||
|
||||
webvtt = String.build do |str|
|
||||
str << <<-END_VTT
|
||||
WEBVTT
|
||||
Kind: captions
|
||||
Language: #{tlang || caption.language_code}
|
||||
|
||||
|
||||
END_VTT
|
||||
END_VTT
|
||||
|
||||
caption_nodes = caption_xml.xpath_nodes("//transcript/text")
|
||||
caption_nodes.each_with_index do |node, i|
|
||||
start_time = node["start"].to_f.seconds
|
||||
duration = node["dur"]?.try &.to_f.seconds
|
||||
duration ||= start_time
|
||||
caption_nodes = caption_xml.xpath_nodes("//transcript/text")
|
||||
caption_nodes.each_with_index do |node, i|
|
||||
start_time = node["start"].to_f.seconds
|
||||
duration = node["dur"]?.try &.to_f.seconds
|
||||
duration ||= start_time
|
||||
|
||||
if caption_nodes.size > i + 1
|
||||
end_time = caption_nodes[i + 1]["start"].to_f.seconds
|
||||
else
|
||||
end_time = start_time + duration
|
||||
if caption_nodes.size > i + 1
|
||||
end_time = caption_nodes[i + 1]["start"].to_f.seconds
|
||||
else
|
||||
end_time = start_time + duration
|
||||
end
|
||||
|
||||
start_time = "#{start_time.hours.to_s.rjust(2, '0')}:#{start_time.minutes.to_s.rjust(2, '0')}:#{start_time.seconds.to_s.rjust(2, '0')}.#{start_time.milliseconds.to_s.rjust(3, '0')}"
|
||||
end_time = "#{end_time.hours.to_s.rjust(2, '0')}:#{end_time.minutes.to_s.rjust(2, '0')}:#{end_time.seconds.to_s.rjust(2, '0')}.#{end_time.milliseconds.to_s.rjust(3, '0')}"
|
||||
|
||||
text = HTML.unescape(node.content)
|
||||
text = text.gsub(/<font color="#[a-fA-F0-9]{6}">/, "")
|
||||
text = text.gsub(/<\/font>/, "")
|
||||
if md = text.match(/(?<name>.*) : (?<text>.*)/)
|
||||
text = "<v #{md["name"]}>#{md["text"]}</v>"
|
||||
end
|
||||
|
||||
str << <<-END_CUE
|
||||
#{start_time} --> #{end_time}
|
||||
#{text}
|
||||
|
||||
|
||||
END_CUE
|
||||
end
|
||||
|
||||
start_time = "#{start_time.hours.to_s.rjust(2, '0')}:#{start_time.minutes.to_s.rjust(2, '0')}:#{start_time.seconds.to_s.rjust(2, '0')}.#{start_time.milliseconds.to_s.rjust(3, '0')}"
|
||||
end_time = "#{end_time.hours.to_s.rjust(2, '0')}:#{end_time.minutes.to_s.rjust(2, '0')}:#{end_time.seconds.to_s.rjust(2, '0')}.#{end_time.milliseconds.to_s.rjust(3, '0')}"
|
||||
|
||||
text = HTML.unescape(node.content)
|
||||
text = text.gsub(/<font color="#[a-fA-F0-9]{6}">/, "")
|
||||
text = text.gsub(/<\/font>/, "")
|
||||
if md = text.match(/(?<name>.*) : (?<text>.*)/)
|
||||
text = "<v #{md["name"]}>#{md["text"]}</v>"
|
||||
end
|
||||
|
||||
str << <<-END_CUE
|
||||
#{start_time} --> #{end_time}
|
||||
#{text}
|
||||
|
||||
|
||||
END_CUE
|
||||
end
|
||||
end
|
||||
else
|
||||
@ -141,7 +146,12 @@ module Invidious::Routes::API::V1::Videos
|
||||
#
|
||||
# See: https://github.com/iv-org/invidious/issues/2391
|
||||
webvtt = YT_POOL.client &.get("#{url}&format=vtt").body
|
||||
.gsub(/([0-9:.]{12} --> [0-9:.]{12}).+/, "\\1")
|
||||
if webvtt.starts_with?("<?xml")
|
||||
webvtt = caption.timedtext_to_vtt(webvtt)
|
||||
else
|
||||
webvtt = YT_POOL.client &.get("#{url}&format=vtt").body
|
||||
.gsub(/([0-9:.]{12} --> [0-9:.]{12}).+/, "\\1")
|
||||
end
|
||||
end
|
||||
|
||||
if title = env.params.query["title"]?
|
||||
|
@ -6,14 +6,14 @@ module Invidious::Routes::Login
|
||||
|
||||
user = env.get? "user"
|
||||
|
||||
return env.redirect "/feed/subscriptions" if user
|
||||
referer = get_referer(env, "/feed/subscriptions")
|
||||
|
||||
return env.redirect referer if user
|
||||
|
||||
if !CONFIG.login_enabled
|
||||
return error_template(400, "Login has been disabled by administrator.")
|
||||
end
|
||||
|
||||
referer = get_referer(env, "/feed/subscriptions")
|
||||
|
||||
email = nil
|
||||
password = nil
|
||||
captcha = nil
|
||||
|
@ -104,33 +104,8 @@ module Invidious::Routes::Subscriptions
|
||||
if format == "json"
|
||||
env.response.content_type = "application/json"
|
||||
env.response.headers["content-disposition"] = "attachment"
|
||||
playlists = Invidious::Database::Playlists.select_like_iv(user.email)
|
||||
|
||||
return JSON.build do |json|
|
||||
json.object do
|
||||
json.field "subscriptions", user.subscriptions
|
||||
json.field "watch_history", user.watched
|
||||
json.field "preferences", user.preferences
|
||||
json.field "playlists" do
|
||||
json.array do
|
||||
playlists.each do |playlist|
|
||||
json.object do
|
||||
json.field "title", playlist.title
|
||||
json.field "description", html_to_content(playlist.description_html)
|
||||
json.field "privacy", playlist.privacy.to_s
|
||||
json.field "videos" do
|
||||
json.array do
|
||||
Invidious::Database::PlaylistVideos.select_ids(playlist.id, playlist.index, limit: 500).each do |video_id|
|
||||
json.string video_id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return Invidious::User::Export.to_invidious(user)
|
||||
else
|
||||
env.response.content_type = "application/xml"
|
||||
env.response.headers["content-disposition"] = "attachment"
|
||||
|
@ -132,6 +132,8 @@ module Invidious::Routing
|
||||
get "/c/:user#{path}", Routes::Channels, :brand_redirect
|
||||
# /user/linustechtips | Not always the same as /c/
|
||||
get "/user/:user#{path}", Routes::Channels, :brand_redirect
|
||||
# /@LinusTechTips | Handle
|
||||
get "/@:user#{path}", Routes::Channels, :brand_redirect
|
||||
# /attribution_link?a=anything&u=/channel/UCZYTClx2T1of7BRZ86-8fow
|
||||
get "/attribution_link#{path}", Routes::Channels, :brand_redirect
|
||||
# /profile?user=linustechtips
|
||||
@ -252,6 +254,14 @@ module Invidious::Routing
|
||||
get "/api/v1/auth/preferences", {{namespace}}::Authenticated, :get_preferences
|
||||
post "/api/v1/auth/preferences", {{namespace}}::Authenticated, :set_preferences
|
||||
|
||||
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
|
||||
@ -279,6 +289,7 @@ module Invidious::Routing
|
||||
get "/api/v1/playlists/:plid", {{namespace}}::Misc, :get_playlist
|
||||
get "/api/v1/auth/playlists/:plid", {{namespace}}::Misc, :get_playlist
|
||||
get "/api/v1/mixes/:rdid", {{namespace}}::Misc, :mixes
|
||||
get "/api/v1/resolveurl", {{namespace}}::Misc, :resolve_url
|
||||
{% end %}
|
||||
end
|
||||
end
|
||||
|
@ -4,11 +4,12 @@ def fetch_trending(trending_type, region, locale)
|
||||
|
||||
plid = nil
|
||||
|
||||
if trending_type == "Music"
|
||||
case trending_type.try &.downcase
|
||||
when "music"
|
||||
params = "4gINGgt5dG1hX2NoYXJ0cw%3D%3D"
|
||||
elsif trending_type == "Gaming"
|
||||
when "gaming"
|
||||
params = "4gIcGhpnYW1pbmdfY29ycHVzX21vc3RfcG9wdWxhcg%3D%3D"
|
||||
elsif trending_type == "Movies"
|
||||
when "movies"
|
||||
params = "4gIKGgh0cmFpbGVycw%3D%3D"
|
||||
else # Default
|
||||
params = ""
|
||||
|
35
src/invidious/user/exports.cr
Normal file
35
src/invidious/user/exports.cr
Normal file
@ -0,0 +1,35 @@
|
||||
struct Invidious::User
|
||||
module Export
|
||||
extend self
|
||||
|
||||
def to_invidious(user : User)
|
||||
playlists = Invidious::Database::Playlists.select_like_iv(user.email)
|
||||
|
||||
return JSON.build do |json|
|
||||
json.object do
|
||||
json.field "subscriptions", user.subscriptions
|
||||
json.field "watch_history", user.watched
|
||||
json.field "preferences", user.preferences
|
||||
json.field "playlists" do
|
||||
json.array do
|
||||
playlists.each do |playlist|
|
||||
json.object do
|
||||
json.field "title", playlist.title
|
||||
json.field "description", html_to_content(playlist.description_html)
|
||||
json.field "privacy", playlist.privacy.to_s
|
||||
json.field "videos" do
|
||||
json.array do
|
||||
Invidious::Database::PlaylistVideos.select_ids(playlist.id, playlist.index, limit: CONFIG.playlist_length_limit).each do |video_id|
|
||||
json.string video_id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end # module
|
||||
end
|
@ -247,6 +247,12 @@ struct Video
|
||||
info["reason"]?.try &.as_s
|
||||
end
|
||||
|
||||
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)
|
||||
}
|
||||
end
|
||||
|
||||
# Macros defining getters/setters for various types of data
|
||||
|
||||
private macro getset_string(name)
|
||||
|
@ -31,6 +31,72 @@ module Invidious::Videos
|
||||
return captions_list
|
||||
end
|
||||
|
||||
def timedtext_to_vtt(timedtext : String, tlang = nil) : String
|
||||
# In the future, we could just directly work with the url. This is more of a POC
|
||||
cues = [] of XML::Node
|
||||
tree = XML.parse(timedtext)
|
||||
tree = tree.children.first
|
||||
|
||||
tree.children.each do |item|
|
||||
if item.name == "body"
|
||||
item.children.each do |cue|
|
||||
if cue.name == "p" && !(cue.children.size == 1 && cue.children[0].content == "\n")
|
||||
cues << cue
|
||||
end
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
result = String.build do |result|
|
||||
result << <<-END_VTT
|
||||
WEBVTT
|
||||
Kind: captions
|
||||
Language: #{tlang || @language_code}
|
||||
|
||||
|
||||
END_VTT
|
||||
|
||||
result << "\n\n"
|
||||
|
||||
cues.each_with_index do |node, i|
|
||||
start_time = node["t"].to_f.milliseconds
|
||||
|
||||
duration = node["d"]?.try &.to_f.milliseconds
|
||||
|
||||
duration ||= start_time
|
||||
|
||||
if cues.size > i + 1
|
||||
end_time = cues[i + 1]["t"].to_f.milliseconds
|
||||
else
|
||||
end_time = start_time + duration
|
||||
end
|
||||
|
||||
# start_time
|
||||
result << start_time.hours.to_s.rjust(2, '0')
|
||||
result << ':' << start_time.minutes.to_s.rjust(2, '0')
|
||||
result << ':' << start_time.seconds.to_s.rjust(2, '0')
|
||||
result << '.' << start_time.milliseconds.to_s.rjust(3, '0')
|
||||
|
||||
result << " --> "
|
||||
|
||||
# end_time
|
||||
result << end_time.hours.to_s.rjust(2, '0')
|
||||
result << ':' << end_time.minutes.to_s.rjust(2, '0')
|
||||
result << ':' << end_time.seconds.to_s.rjust(2, '0')
|
||||
result << '.' << end_time.milliseconds.to_s.rjust(3, '0')
|
||||
|
||||
result << "\n"
|
||||
|
||||
node.children.each do |s|
|
||||
result << s.content
|
||||
end
|
||||
result << "\n"
|
||||
result << "\n"
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
# List of all caption languages available on Youtube.
|
||||
LANGUAGES = {
|
||||
"",
|
||||
|
12
src/invidious/videos/music.cr
Normal file
12
src/invidious/videos/music.cr
Normal file
@ -0,0 +1,12 @@
|
||||
require "json"
|
||||
|
||||
struct VideoMusic
|
||||
include JSON::Serializable
|
||||
|
||||
property album : String
|
||||
property artist : String
|
||||
property license : String
|
||||
|
||||
def initialize(@album : String, @artist : String, @license : String)
|
||||
end
|
||||
end
|
@ -311,6 +311,33 @@ def parse_video_info(video_id : String, player_response : Hash(String, JSON::Any
|
||||
end
|
||||
end
|
||||
|
||||
# Music section
|
||||
|
||||
music_list = [] of VideoMusic
|
||||
music_desclist = player_response.dig?(
|
||||
"engagementPanels", 1, "engagementPanelSectionListRenderer",
|
||||
"content", "structuredDescriptionContentRenderer", "items", 2,
|
||||
"videoDescriptionMusicSectionRenderer", "carouselLockups"
|
||||
)
|
||||
|
||||
music_desclist.try &.as_a.each do |music_desc|
|
||||
artist = nil
|
||||
album = nil
|
||||
music_license = nil
|
||||
|
||||
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 == "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)
|
||||
end
|
||||
|
||||
# Author infos
|
||||
|
||||
author = video_details["author"]?.try &.as_s
|
||||
@ -361,6 +388,8 @@ def parse_video_info(video_id : String, player_response : Hash(String, JSON::Any
|
||||
"genre" => JSON::Any.new(genre.try &.as_s || ""),
|
||||
"genreUcid" => JSON::Any.new(genre_ucid.try &.as_s || ""),
|
||||
"license" => JSON::Any.new(license.try &.as_s || ""),
|
||||
# Music section
|
||||
"music" => JSON.parse(music_list.to_json),
|
||||
# Author infos
|
||||
"author" => JSON::Any.new(author || ""),
|
||||
"ucid" => JSON::Any.new(ucid || ""),
|
||||
|
@ -39,6 +39,8 @@
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script src="/js/watched_indicator.js"></script>
|
||||
|
||||
<% if query %>
|
||||
<%- query_encoded = URI.encode_www_form(query.text, space_to_plus: true) -%>
|
||||
<div class="pure-g h-box">
|
||||
|
@ -49,6 +49,8 @@
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script src="/js/watched_indicator.js"></script>
|
||||
|
||||
<div class="pure-g h-box">
|
||||
<div class="pure-u-1 pure-u-md-4-5"></div>
|
||||
<div class="pure-u-1 pure-u-lg-1-5" style="text-align:right">
|
||||
|
@ -1,3 +1,5 @@
|
||||
<% item_watched = !item.is_a?(SearchChannel | SearchPlaylist | InvidiousPlaylist | Category) && env.get?("user").try &.as(User).watched.index(item.id) != nil %>
|
||||
|
||||
<div class="pure-u-1 pure-u-md-1-4">
|
||||
<div class="h-box">
|
||||
<% case item when %>
|
||||
@ -40,6 +42,11 @@
|
||||
<% if item.length_seconds != 0 %>
|
||||
<p class="length"><%= recode_length_seconds(item.length_seconds) %></p>
|
||||
<% end %>
|
||||
|
||||
<% if item_watched %>
|
||||
<div class="watched-overlay"></div>
|
||||
<div class="watched-indicator" data-length="<%= item.length_seconds %>" data-id="<%= item.id %>"></div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<p dir="auto"><%= HTML.escape(item.title) %></p>
|
||||
@ -67,6 +74,11 @@
|
||||
<% elsif item.length_seconds != 0 %>
|
||||
<p class="length"><%= recode_length_seconds(item.length_seconds) %></p>
|
||||
<% end %>
|
||||
|
||||
<% if item_watched %>
|
||||
<div class="watched-overlay"></div>
|
||||
<div class="watched-indicator" data-length="<%= item.length_seconds %>" data-id="<%= item.id %>"></div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<p dir="auto"><%= HTML.escape(item.title) %></p>
|
||||
@ -124,6 +136,11 @@
|
||||
<% elsif item.length_seconds != 0 %>
|
||||
<p class="length"><%= recode_length_seconds(item.length_seconds) %></p>
|
||||
<% end %>
|
||||
|
||||
<% if item_watched %>
|
||||
<div class="watched-overlay"></div>
|
||||
<div class="watched-indicator" data-length="<%= item.length_seconds %>" data-id="<%= item.id %>"></div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<p dir="auto"><%= HTML.escape(item.title) %></p>
|
||||
|
@ -62,6 +62,8 @@
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script src="/js/watched_indicator.js"></script>
|
||||
|
||||
<div class="pure-g h-box">
|
||||
<div class="pure-u-1 pure-u-lg-1-5">
|
||||
<% if page > 1 %>
|
||||
|
@ -32,3 +32,5 @@
|
||||
<%= rendered "components/item" %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script src="/js/watched_indicator.js"></script>
|
||||
|
@ -16,3 +16,5 @@
|
||||
<%= rendered "components/item" %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script src="/js/watched_indicator.js"></script>
|
||||
|
@ -62,6 +62,8 @@
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script src="/js/watched_indicator.js"></script>
|
||||
|
||||
<div class="pure-g h-box">
|
||||
<div class="pure-u-1 pure-u-lg-1-5">
|
||||
<% if page > 1 %>
|
||||
|
@ -45,3 +45,5 @@
|
||||
<%= rendered "components/item" %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script src="/js/watched_indicator.js"></script>
|
||||
|
@ -24,6 +24,8 @@
|
||||
<%- end -%>
|
||||
</div>
|
||||
|
||||
<script src="/js/watched_indicator.js"></script>
|
||||
|
||||
<div class="pure-g h-box">
|
||||
<div class="pure-u-1 pure-u-lg-1-5">
|
||||
<%- if page > 1 -%>
|
||||
|
@ -106,6 +106,8 @@
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script src="/js/watched_indicator.js"></script>
|
||||
|
||||
<div class="pure-g h-box">
|
||||
<div class="pure-u-1 pure-u-lg-1-5">
|
||||
<% if page > 1 %>
|
||||
|
@ -37,6 +37,8 @@
|
||||
</div>
|
||||
<%- end -%>
|
||||
|
||||
<script src="/js/watched_indicator.js"></script>
|
||||
|
||||
<div class="pure-g h-box">
|
||||
<div class="pure-u-1 pure-u-lg-1-5">
|
||||
<%- if query.page > 1 -%>
|
||||
|
@ -235,6 +235,28 @@ we're going to need to do it here in order to allow for translations.
|
||||
|
||||
<hr>
|
||||
|
||||
<% if !video.music.empty? %>
|
||||
<input id="music-desc-expansion" type="checkbox"/>
|
||||
<label for="music-desc-expansion">
|
||||
<h3 id="music-description-title">
|
||||
<%= translate(locale, "Music in this video") %>
|
||||
<span class="icon ion-ios-arrow-up"></span>
|
||||
<span class="icon ion-ios-arrow-down"></span>
|
||||
</h3>
|
||||
</label>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
<% end %>
|
||||
<div id="comments">
|
||||
<% if nojs %>
|
||||
<%= comment_html %>
|
||||
|
@ -172,7 +172,17 @@ private module Parsers
|
||||
# When public subscriber count is disabled, the subscriberCountText isn't sent by InnerTube.
|
||||
# Always simpleText
|
||||
# TODO change default value to nil
|
||||
|
||||
subscriber_count = item_contents.dig?("subscriberCountText", "simpleText")
|
||||
|
||||
# Since youtube added channel handles, `VideoCountText` holds the number of
|
||||
# subscribers and `subscriberCountText` holds the handle, except when the
|
||||
# channel doesn't have a handle (e.g: some topic music channels).
|
||||
# See https://github.com/iv-org/invidious/issues/3394#issuecomment-1321261688
|
||||
if !subscriber_count || !subscriber_count.as_s.includes? " subscriber"
|
||||
subscriber_count = item_contents.dig?("videoCountText", "simpleText")
|
||||
end
|
||||
subscriber_count = subscriber_count
|
||||
.try { |s| short_text_to_number(s.as_s.split(" ")[0]).to_i32 } || 0
|
||||
|
||||
# Auto-generated channels doesn't have videoCountText
|
||||
@ -682,7 +692,11 @@ module HelperExtractors
|
||||
# Returns a 0 when it's unable to do so
|
||||
def self.get_video_count(container : JSON::Any) : Int32
|
||||
if box = container["videoCountText"]?
|
||||
return extract_text(box).try &.gsub(/\D/, "").to_i || 0
|
||||
if (extracted_text = extract_text(box)) && !extracted_text.includes? " subscriber"
|
||||
return extracted_text.gsub(/\D/, "").to_i
|
||||
else
|
||||
return 0
|
||||
end
|
||||
elsif box = container["videoCount"]?
|
||||
return box.as_s.to_i
|
||||
else
|
||||
|
Loading…
x
Reference in New Issue
Block a user