summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docker/Dockerfile3
-rw-r--r--docker/Dockerfile.arm643
-rw-r--r--kubernetes/Chart.lock6
-rw-r--r--locales/ar.json10
-rw-r--r--locales/ca.json2
-rw-r--r--locales/cs.json12
-rw-r--r--locales/da.json4
-rw-r--r--locales/de.json4
-rw-r--r--locales/el.json4
-rw-r--r--locales/eo.json10
-rw-r--r--locales/es.json10
-rw-r--r--locales/et.json4
-rw-r--r--locales/fa.json4
-rw-r--r--locales/fi.json4
-rw-r--r--locales/fr.json10
-rw-r--r--locales/he.json4
-rw-r--r--locales/hi.json4
-rw-r--r--locales/hr.json18
-rw-r--r--locales/hu-HU.json4
-rw-r--r--locales/id.json4
-rw-r--r--locales/is.json4
-rw-r--r--locales/it.json10
-rw-r--r--locales/ja.json4
-rw-r--r--locales/ko.json56
-rw-r--r--locales/lt.json4
-rw-r--r--locales/nb-NO.json4
-rw-r--r--locales/nl.json4
-rw-r--r--locales/pl.json10
-rw-r--r--locales/pt-BR.json4
-rw-r--r--locales/pt-PT.json4
-rw-r--r--locales/pt.json4
-rw-r--r--locales/ro.json4
-rw-r--r--locales/ru.json4
-rw-r--r--locales/sl.json4
-rw-r--r--locales/sq.json4
-rw-r--r--locales/sr.json4
-rw-r--r--locales/sr_Cyrl.json4
-rw-r--r--locales/sv-SE.json4
-rw-r--r--locales/tr.json10
-rw-r--r--locales/uk.json44
-rw-r--r--locales/vi.json4
-rw-r--r--locales/zh-CN.json4
-rw-r--r--locales/zh-TW.json10
-rwxr-xr-x[-rw-r--r--]scripts/deploy-database.sh0
-rwxr-xr-x[-rw-r--r--]scripts/fetch-player-dependencies.cr2
-rwxr-xr-x[-rw-r--r--]scripts/install-dependencies.sh0
-rw-r--r--src/invidious.cr1
-rw-r--r--src/invidious/channels/community.cr2
-rw-r--r--src/invidious/helpers/json_filter.cr6
-rw-r--r--src/invidious/helpers/utils.cr4
-rw-r--r--src/invidious/http_server/utils.cr20
-rw-r--r--src/invidious/jsonify/api_v1/video_json.cr11
-rw-r--r--src/invidious/routes/account.cr2
-rw-r--r--src/invidious/routes/api/manifest.cr4
-rw-r--r--src/invidious/routes/api/v1/videos.cr75
-rw-r--r--src/invidious/routes/login.cr6
-rw-r--r--src/invidious/routes/video_playback.cr20
-rw-r--r--src/invidious/routing.cr2
-rw-r--r--src/invidious/videos/caption.cr66
-rw-r--r--src/invidious/videos/parser.cr6
-rw-r--r--src/invidious/yt_backend/extractors.cr16
61 files changed, 377 insertions, 198 deletions
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 34549df1..57864883 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -43,7 +43,7 @@ RUN if [[ "${release}" == 1 && "${disable_quic}" == 1 ]] ; then \
FROM alpine:3.16
-RUN apk add --no-cache librsvg ttf-opensans
+RUN apk add --no-cache librsvg ttf-opensans tini
WORKDIR /invidious
RUN addgroup -g 1000 -S invidious && \
adduser -u 1000 -S invidious -G invidious
@@ -58,4 +58,5 @@ RUN chmod o+rX -R ./assets ./config ./locales
EXPOSE 3000
USER invidious
+ENTRYPOINT ["/sbin/tini", "--"]
CMD [ "/invidious/invidious" ]
diff --git a/docker/Dockerfile.arm64 b/docker/Dockerfile.arm64
index ef3284b1..10135efb 100644
--- a/docker/Dockerfile.arm64
+++ b/docker/Dockerfile.arm64
@@ -42,7 +42,7 @@ RUN if [[ "${release}" == 1 && "${disable_quic}" == 1 ]] ; then \
fi
FROM alpine:3.16
-RUN apk add --no-cache librsvg ttf-opensans
+RUN apk add --no-cache librsvg ttf-opensans tini
WORKDIR /invidious
RUN addgroup -g 1000 -S invidious && \
adduser -u 1000 -S invidious -G invidious
@@ -57,4 +57,5 @@ RUN chmod o+rX -R ./assets ./config ./locales
EXPOSE 3000
USER invidious
+ENTRYPOINT ["/sbin/tini", "--"]
CMD [ "/invidious/invidious" ]
diff --git a/kubernetes/Chart.lock b/kubernetes/Chart.lock
index 37fcdbbd..cc76e920 100644
--- a/kubernetes/Chart.lock
+++ b/kubernetes/Chart.lock
@@ -1,6 +1,6 @@
dependencies:
- name: postgresql
repository: https://charts.bitnami.com/bitnami/
- version: 11.1.3
-digest: sha256:79061645472b6fb342d45e8e5b3aacd018ef5067193e46a060bccdc99fe7f6e1
-generated: "2022-03-02T05:57:20.081432389+13:00"
+ version: 12.1.9
+digest: sha256:71ff342a6c0a98bece3d7fe199983afb2113f8db65a3e3819de875af2c45add7
+generated: "2023-01-20T20:42:32.757707004Z"
diff --git a/locales/ar.json b/locales/ar.json
index 2a746e5d..55dea5f3 100644
--- a/locales/ar.json
+++ b/locales/ar.json
@@ -325,9 +325,9 @@
"`x` marked it with a ❤": "`x` أعجب بهذا",
"Audio mode": "الوضع الصوتي",
"Video mode": "وضع الفيديو",
- "Videos": "الفيديوهات",
+ "channel_tab_videos_label": "الفيديوهات",
"Playlists": "قوائم التشغيل",
- "Community": "المجتمع",
+ "channel_tab_community_label": "المجتمع",
"search_filters_sort_option_relevance": "ملائمة",
"search_filters_sort_option_rating": "تقييم",
"search_filters_sort_option_date": "التاريخ",
@@ -536,5 +536,9 @@
"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": "القنوات"
}
diff --git a/locales/ca.json b/locales/ca.json
index 741414d2..2ba6ae39 100644
--- a/locales/ca.json
+++ b/locales/ca.json
@@ -51,7 +51,7 @@
"Movies": "Películes",
"Download": "Descarrega",
"Download as: ": "Descarrega com: ",
- "Videos": "Vídeos",
+ "channel_tab_videos_label": "Vídeos",
"search_filters_type_label": "Tipus",
"search_filters_duration_label": "Duració",
"search_filters_sort_label": "Ordena per",
diff --git a/locales/cs.json b/locales/cs.json
index 7538365a..7502de0b 100644
--- a/locales/cs.json
+++ b/locales/cs.json
@@ -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",
@@ -260,8 +260,8 @@
"`x` marked it with a ❤": "`x` to označil(a) se ❤",
"Audio mode": "Audiový režim",
"Video mode": "Videový režim",
- "Videos": "Videa",
- "Community": "Komunita",
+ "channel_tab_videos_label": "Videa",
+ "channel_tab_community_label": "Komunita",
"search_filters_sort_option_rating": "Hodnocení",
"search_filters_sort_option_date": "Datum nahrání",
"search_filters_sort_option_views": "Počet zhlédnutí",
@@ -488,5 +488,9 @@
"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"
}
diff --git a/locales/da.json b/locales/da.json
index 4816c2c9..2bee6c80 100644
--- a/locales/da.json
+++ b/locales/da.json
@@ -187,7 +187,7 @@
"Esperanto": "Esperanto",
"Czech": "Tjekkisk",
"Danish": "Dansk",
- "Community": "Samfund",
+ "channel_tab_community_label": "Samfund",
"Afrikaans": "Afrikansk",
"Portuguese": "Portugisisk",
"Ukrainian": "Ukrainsk",
@@ -267,7 +267,7 @@
"search_filters_sort_option_rating": "Bedømmelse",
"Yoruba": "Yoruba",
"Erroneous token": "Fejlagtig token",
- "Videos": "Videoer",
+ "channel_tab_videos_label": "Videoer",
"search_filters_type_option_show": "Vis",
"Luxembourgish": "Luxemboursk",
"Vietnamese": "Vietnamesisk",
diff --git a/locales/de.json b/locales/de.json
index a2070cf5..55c40905 100644
--- a/locales/de.json
+++ b/locales/de.json
@@ -325,9 +325,9 @@
"`x` marked it with a ❤": "`x` markierte es mit einem ❤",
"Audio mode": "Audiomodus",
"Video mode": "Videomodus",
- "Videos": "Videos",
+ "channel_tab_videos_label": "Videos",
"Playlists": "Wiedergabelisten",
- "Community": "Gemeinschaft",
+ "channel_tab_community_label": "Gemeinschaft",
"search_filters_sort_option_relevance": "Relevanz",
"search_filters_sort_option_rating": "Bewertung",
"search_filters_sort_option_date": "Datum",
diff --git a/locales/el.json b/locales/el.json
index d91d64fc..3448a4dc 100644
--- a/locales/el.json
+++ b/locales/el.json
@@ -315,9 +315,9 @@
"`x` marked it with a ❤": "Ο χρηστης `x` έβαλε ❤",
"Audio mode": "Λειτουργία ήχου",
"Video mode": "Λειτουργία βίντεο",
- "Videos": "Βίντεο",
+ "channel_tab_videos_label": "Βίντεο",
"Playlists": "Λίστες Αναπαραγωγής",
- "Community": "Κοινότητα",
+ "channel_tab_community_label": "Κοινότητα",
"Current version: ": "Τρέχουσα έκδοση: ",
"generic_playlists_count": "{{count}} λίστα αναπαραγωγής",
"generic_playlists_count_plural": "{{count}} λίστες αναπαραγωγής",
diff --git a/locales/eo.json b/locales/eo.json
index 5aa2bbc6..56e718f2 100644
--- a/locales/eo.json
+++ b/locales/eo.json
@@ -325,9 +325,9 @@
"`x` marked it with a ❤": "`x` markis ĝin per ❤",
"Audio mode": "Aŭda reĝimo",
"Video mode": "Videa reĝimo",
- "Videos": "Filmetoj",
+ "channel_tab_videos_label": "Videoj",
"Playlists": "Ludlistoj",
- "Community": "Komunumo",
+ "channel_tab_community_label": "Komunumo",
"search_filters_sort_option_relevance": "rilateco",
"search_filters_sort_option_rating": "takso",
"search_filters_sort_option_date": "dato",
@@ -472,5 +472,9 @@
"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"
}
diff --git a/locales/es.json b/locales/es.json
index 8603e9fe..59d6b145 100644
--- a/locales/es.json
+++ b/locales/es.json
@@ -325,9 +325,9 @@
"`x` marked it with a ❤": "`x` lo ha marcado con un ❤",
"Audio mode": "Modo de audio",
"Video mode": "Modo de vídeo",
- "Videos": "Vídeos",
+ "channel_tab_videos_label": "Vídeos",
"Playlists": "Listas de reproducción",
- "Community": "Comunidad",
+ "channel_tab_community_label": "Comunidad",
"search_filters_sort_option_relevance": "relevancia",
"search_filters_sort_option_rating": "valoración",
"search_filters_sort_option_date": "fecha",
@@ -472,5 +472,9 @@
"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 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>",
+ "channel_tab_streams_label": "Directos",
+ "channel_tab_channels_label": "Canales",
+ "channel_tab_shorts_label": "Cortos",
+ "channel_tab_playlists_label": "Listas de reproducción"
}
diff --git a/locales/et.json b/locales/et.json
index 7beb1749..74338aba 100644
--- a/locales/et.json
+++ b/locales/et.json
@@ -296,8 +296,8 @@
"Corsican": "Korsika",
"Javanese": "Jaava",
"Lithuanian": "Leedu",
- "Videos": "Videod",
- "Community": "Kogukond",
+ "channel_tab_videos_label": "Videod",
+ "channel_tab_community_label": "Kogukond",
"CAPTCHA is a required field": "CAPTCHA on kohustuslik väli",
"comments_points_count": "{{count}} punkt",
"comments_points_count_plural": "{{count}} punkti",
diff --git a/locales/fa.json b/locales/fa.json
index 3a8f547f..f2ca2745 100644
--- a/locales/fa.json
+++ b/locales/fa.json
@@ -341,9 +341,9 @@
"`x` marked it with a ❤": "`x` نشان گذاری شده با یک ❤",
"Audio mode": "حالت صدا",
"Video mode": "حالت ویدیو",
- "Videos": "ویدیو ها",
+ "channel_tab_videos_label": "ویدیو ها",
"Playlists": "سیاهه‌های پخش",
- "Community": "اجتماع",
+ "channel_tab_community_label": "اجتماع",
"search_filters_sort_option_relevance": "مرتبط بودن",
"search_filters_sort_option_rating": "امتیاز",
"search_filters_sort_option_date": "تاریخ بارگذاری",
diff --git a/locales/fi.json b/locales/fi.json
index bef9027f..366a2739 100644
--- a/locales/fi.json
+++ b/locales/fi.json
@@ -324,9 +324,9 @@
"`x` marked it with a ❤": "`x` merkkasi ❤:llä",
"Audio mode": "Äänitila",
"Video mode": "Videotila",
- "Videos": "Videot",
+ "channel_tab_videos_label": "Videot",
"Playlists": "Soittolistat",
- "Community": "Yhteisö",
+ "channel_tab_community_label": "Yhteisö",
"search_filters_sort_option_relevance": "Osuvuus",
"search_filters_sort_option_rating": "Arvostelu",
"search_filters_sort_option_date": "Latauspäivämäärä",
diff --git a/locales/fr.json b/locales/fr.json
index 2f384eb1..9d3e117f 100644
--- a/locales/fr.json
+++ b/locales/fr.json
@@ -358,9 +358,9 @@
"`x` marked it with a ❤": "`x` l'a marqué d'un ❤",
"Audio mode": "Mode audio",
"Video mode": "Mode vidéo",
- "Videos": "Vidéos",
+ "channel_tab_videos_label": "Vidéos",
"Playlists": "Listes de lecture",
- "Community": "Communauté",
+ "channel_tab_community_label": "Communauté",
"search_filters_sort_option_relevance": "Pertinence",
"search_filters_sort_option_rating": "Notation",
"search_filters_sort_option_date": "Date d'ajout",
@@ -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"
}
diff --git a/locales/he.json b/locales/he.json
index 384b2657..ab42313b 100644
--- a/locales/he.json
+++ b/locales/he.json
@@ -271,9 +271,9 @@
"`x` marked it with a ❤": "סומנה ב־❤ על ידי `x`",
"Audio mode": "Audio mode",
"Video mode": "Video mode",
- "Videos": "סרטונים",
+ "channel_tab_videos_label": "סרטונים",
"Playlists": "פלייליסטים",
- "Community": "קהילה",
+ "channel_tab_community_label": "קהילה",
"search_filters_sort_option_relevance": "רלוונטיות",
"search_filters_sort_option_rating": "דירוג",
"search_filters_sort_option_date": "תאריך העלאה",
diff --git a/locales/hi.json b/locales/hi.json
index 32ae7823..e576080f 100644
--- a/locales/hi.json
+++ b/locales/hi.json
@@ -401,12 +401,12 @@
"(edited)": "(संपादित)",
"YouTube comment permalink": "YouTube पर टिप्पणी की स्थायी कड़ी",
"permalink": "स्थायी कड़ी",
- "Videos": "वीडियो",
+ "channel_tab_videos_label": "वीडियो",
"`x` marked it with a ❤": "`x` ने इसे एक ❤ से चिह्नित किया",
"Audio mode": "ऑडियो मोड",
"Playlists": "प्लेलिस्ट्स",
"Video mode": "वीडियो मोड",
- "Community": "समुदाय",
+ "channel_tab_community_label": "समुदाय",
"search_filters_title": "फ़िल्टर",
"search_filters_date_label": "अपलोड करने का समय",
"search_filters_date_option_none": "कोई भी समय",
diff --git a/locales/hr.json b/locales/hr.json
index e42cc4f5..7914ab16 100644
--- a/locales/hr.json
+++ b/locales/hr.json
@@ -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",
@@ -325,9 +325,9 @@
"`x` marked it with a ❤": "Označeno sa ❤ od `x`",
"Audio mode": "Audio modus",
"Video mode": "Videomodus",
- "Videos": "Videa",
+ "channel_tab_videos_label": "Videa",
"Playlists": "Zbirke",
- "Community": "Zajednica",
+ "channel_tab_community_label": "Zajednica",
"search_filters_sort_option_relevance": "Značaj",
"search_filters_sort_option_rating": "Ocjena",
"search_filters_sort_option_date": "Datum prijenosa",
@@ -488,5 +488,9 @@
"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"
}
diff --git a/locales/hu-HU.json b/locales/hu-HU.json
index 19ada1d8..f93930e0 100644
--- a/locales/hu-HU.json
+++ b/locales/hu-HU.json
@@ -348,9 +348,9 @@
"`x` marked it with a ❤": "`x` ❤ jelet adott a hozzászóláshoz",
"Audio mode": "Csak hanggal",
"Video mode": "Hanggal és képpel",
- "Videos": "Videói",
+ "channel_tab_videos_label": "Videói",
"Playlists": "Lejátszási listái",
- "Community": "Közösség",
+ "channel_tab_community_label": "Közösség",
"Current version: ": "Jelenlegi verzió: ",
"preferences_quality_option_medium": "Közepes",
"preferences_quality_dash_option_auto": "Automatikus",
diff --git a/locales/id.json b/locales/id.json
index a30f0ad4..51d6d55c 100644
--- a/locales/id.json
+++ b/locales/id.json
@@ -341,9 +341,9 @@
"`x` marked it with a ❤": "`x` telah ditandai dengan ❤",
"Audio mode": "Mode audio",
"Video mode": "Mode video",
- "Videos": "Video",
+ "channel_tab_videos_label": "Video",
"Playlists": "Daftar putar",
- "Community": "Komunitas",
+ "channel_tab_community_label": "Komunitas",
"search_filters_sort_option_relevance": "Relevansi",
"search_filters_sort_option_rating": "Penilaian",
"search_filters_sort_option_date": "Tanggal Unggah",
diff --git a/locales/is.json b/locales/is.json
index 99bd6574..3282eb50 100644
--- a/locales/is.json
+++ b/locales/is.json
@@ -315,9 +315,9 @@
"`x` marked it with a ❤": "`x` merkti það með ❤",
"Audio mode": "Hljóð ham",
"Video mode": "Myndband ham",
- "Videos": "Myndbönd",
+ "channel_tab_videos_label": "Myndbönd",
"Playlists": "Spilunarlistar",
- "Community": "Samfélag",
+ "channel_tab_community_label": "Samfélag",
"Current version: ": "Núverandi útgáfa: ",
"preferences_watch_history_label": "Virkja áhorfssögu: "
}
diff --git a/locales/it.json b/locales/it.json
index c195f3b9..f47b032e 100644
--- a/locales/it.json
+++ b/locales/it.json
@@ -344,9 +344,8 @@
"`x` marked it with a ❤": "`x` l'ha contrassegnato con un ❤",
"Audio mode": "Modalità audio",
"Video mode": "Modalità video",
- "Videos": "Video",
+ "channel_tab_videos_label": "Video",
"Playlists": "Playlist",
- "Community": "Comunità",
"search_filters_sort_option_relevance": "Pertinenza",
"search_filters_sort_option_rating": "Valutazione",
"search_filters_sort_option_date": "Data di caricamento",
@@ -472,5 +471,10 @@
"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à"
}
diff --git a/locales/ja.json b/locales/ja.json
index 4971c472..a392abfe 100644
--- a/locales/ja.json
+++ b/locales/ja.json
@@ -341,9 +341,9 @@
"`x` marked it with a ❤": "`x` が❤を込めてマークしました",
"Audio mode": "オーディオモード",
"Video mode": "ビデオモード",
- "Videos": "動画",
+ "channel_tab_videos_label": "動画",
"Playlists": "プレイリスト",
- "Community": "コミュニティ",
+ "channel_tab_community_label": "コミュニティ",
"search_filters_sort_option_relevance": "関連",
"search_filters_sort_option_rating": "評価",
"search_filters_sort_option_date": "時刻",
diff --git a/locales/ko.json b/locales/ko.json
index 28b518a2..d4f3a711 100644
--- a/locales/ko.json
+++ b/locales/ko.json
@@ -2,7 +2,7 @@
"preferences_sort_label": "동영상 정렬 기준: ",
"preferences_max_results_label": "피드에 표시된 동영상 수: ",
"Redirect homepage to feed: ": "피드로 홈페이지 리디렉션: ",
- "preferences_annotations_subscribed_label": "구독한 채널에 기본적으로 특수효과를 표시하시겠습니까? ",
+ "preferences_annotations_subscribed_label": "구독한 채널에 기본으로 주석 표시: ",
"preferences_category_subscription": "구독 설정",
"preferences_automatic_instance_redirect_label": "자동 인스턴스 리디렉션 (redirect.invidious.io로 대체): ",
"preferences_thin_mode_label": "단순 모드: ",
@@ -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": "기본으로 주석 표시: ",
@@ -26,7 +26,7 @@
"preferences_speed_label": "기본 속도: ",
"preferences_local_label": "비디오를 프록시: ",
"preferences_listen_label": "라디오 모드: ",
- "preferences_continue_autoplay_label": "다음 동영상 자동재생 ",
+ "preferences_continue_autoplay_label": "다음 동영상 자동재생: ",
"preferences_continue_label": "다음 동영상으로 이동: ",
"preferences_autoplay_label": "자동재생: ",
"preferences_video_loop_label": "항상 반복: ",
@@ -37,8 +37,8 @@
"Register": "회원가입",
"Sign In": "로그인",
"preferences_category_misc": "기타 설정",
- "Image CAPTCHA": "이미지 CAPTCHA",
- "Text CAPTCHA": "텍스트 CAPTCHA",
+ "Image CAPTCHA": "이미지 캡차",
+ "Text CAPTCHA": "텍스트 캡차",
"Time (h:mm:ss):": "시각 (h:mm:ss):",
"Password": "비밀번호",
"User ID": "사용자 ID",
@@ -50,15 +50,15 @@
"An alternative front-end to YouTube": "유튜브의 프론트엔드 대안",
"History": "역사",
"Delete account?": "계정을 삭제 하시겠습니까?",
- "Export data as JSON": "데이터를 JSON으로 내보내기",
- "Export subscriptions as OPML (for NewPipe & FreeTube)": "구독을 OPML로 내보내기 (NewPipe 및 FreeTube 용)",
- "Export subscriptions as OPML": "구독을 OPML로 내보내기",
+ "Export data as JSON": "JSON으로 데이터 내보내기",
+ "Export subscriptions as OPML (for NewPipe & FreeTube)": "OPML로 구독 내보내기 (뉴파이프 및 프리튜브)",
+ "Export subscriptions as OPML": "OPML로 구독 내보내기",
"Export": "내보내기",
- "Import NewPipe data (.zip)": "NewPipe 데이터 가져오기 (.zip)",
- "Import NewPipe subscriptions (.json)": "NewPipe 구독을 가져오기 (.json)",
- "Import FreeTube subscriptions (.db)": "FreeTube 구독 가져오기 (.db)",
+ "Import NewPipe data (.zip)": "뉴파이프 데이터 가져오기 (.zip)",
+ "Import NewPipe subscriptions (.json)": "뉴파이프 구독 가져오기 (.json)",
+ "Import FreeTube subscriptions (.db)": "프리튜브 구독 가져오기 (.db)",
"Import YouTube subscriptions": "유튜브 구독 가져오기",
- "Import Invidious data": "인비디어스 JSON 데이터 가져오기",
+ "Import Invidious data": "인비디어스 데이터 가져오기 (.json)",
"Import": "가져오기",
"Import and Export Data": "데이터 가져오기 및 내보내기",
"No": "아니요",
@@ -150,9 +150,9 @@
"Subscription manager": "구독 관리자",
"Save preferences": "설정 저장",
"Report statistics: ": "통계 보고: ",
- "Registration enabled: ": "등록 활성화: ",
+ "Registration enabled: ": "회원가입 활성화: ",
"Login enabled: ": "로그인 활성화: ",
- "CAPTCHA enabled: ": "CAPTCHA 활성화: ",
+ "CAPTCHA enabled: ": "캡차 활성화: ",
"Top enabled: ": "Top 활성화: ",
"preferences_show_nick_label": "상단에 닉네임 표시: ",
"preferences_feed_menu_label": "피드 메뉴: ",
@@ -187,8 +187,8 @@
"Polish": "폴란드어",
"Persian": "페르시아어",
"Pashto": "파슈토어",
- "Nyanja": "체와어",
- "Norwegian Bokmål": "보크몰",
+ "Nyanja": "냔자어",
+ "Norwegian Bokmål": "노르웨이 부크몰어",
"Nepali": "네팔어",
"Mongolian": "몽골어",
"Marathi": "마라티어",
@@ -284,10 +284,10 @@
"Password cannot be empty": "비밀번호는 비워둘 수 없습니다",
"Please sign in using 'Log in with Google'": "'구글로 로그인'을 사용하여 로그인하세요",
"Wrong username or password": "잘못된 사용자 이름 또는 비밀번호",
- "Password is a required field": "비밀번호는 필수 필드입니다",
- "User ID is a required field": "사용자 ID는 필수 필드입니다",
- "CAPTCHA is a required field": "CAPTCHA는 필수 필드입니다",
- "Erroneous CAPTCHA": "잘못된 CAPTCHA",
+ "Password is a required field": "비밀번호는 필수 입력란입니다",
+ "User ID is a required field": "사용자 ID는 필수 입력란입니다",
+ "CAPTCHA is a required field": "캡차는 필수 입력란입니다",
+ "Erroneous CAPTCHA": "잘못된 캡차",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "로그인 실패. 계정에 이중 인증이 설정되어 있지 않기 때문일 수 있습니다.",
"Blacklisted regions: ": "차단된 지역: ",
"Playlists": "재생목록",
@@ -297,7 +297,7 @@
"Empty playlist": "재생목록 비어 있음",
"Show annotations": "주석 보이기",
"Hide annotations": "주석 숨기기",
- "Switch Invidious Instance": "Invidious 인스턴스 변경",
+ "Switch Invidious Instance": "인비디어스 인스턴스 변경",
"Spanish": "스페인어",
"Southern Sotho": "소토어",
"Somali": "소말리어",
@@ -347,8 +347,8 @@
"search_filters_sort_option_date": "업로드 날짜",
"search_filters_sort_option_rating": "평점",
"search_filters_sort_option_relevance": "관련성",
- "Community": "커뮤니티",
- "Videos": "동영상",
+ "channel_tab_community_label": "커뮤니티",
+ "channel_tab_videos_label": "동영상",
"Video mode": "비디오 모드",
"Audio mode": "오디오 모드",
"permalink": "퍼머링크",
@@ -383,7 +383,7 @@
"adminprefs_modified_source_code_url_label": "수정된 소스 코드 저장소의 URL",
"search_filters_title": "필터",
"preferences_quality_dash_option_4320p": "4320p",
- "Popular enabled: ": "인기 급상승 활성화: ",
+ "Popular enabled: ": "인기 활성화: ",
"Dutch (auto-generated)": "네덜란드어 (자동 생성됨)",
"Chinese (Hong Kong)": "중국어 (홍콩)",
"Chinese (Taiwan)": "중국어 (대만)",
@@ -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": "재생목록"
}
diff --git a/locales/lt.json b/locales/lt.json
index 35ababee..9bfcfdba 100644
--- a/locales/lt.json
+++ b/locales/lt.json
@@ -325,9 +325,9 @@
"`x` marked it with a ❤": "`x` pažymėjo tai su ❤",
"Audio mode": "Garso rėžimas",
"Video mode": "Vaizdo rėžimas",
- "Videos": "Vaizdo įrašai",
+ "channel_tab_videos_label": "Vaizdo įrašai",
"Playlists": "Grojaraiščiai",
- "Community": "Bendruomenė",
+ "channel_tab_community_label": "Bendruomenė",
"search_filters_sort_option_relevance": "Aktualumas",
"search_filters_sort_option_rating": "Reitingas",
"search_filters_sort_option_date": "Įkėlimo data",
diff --git a/locales/nb-NO.json b/locales/nb-NO.json
index f4c2021b..d29cca43 100644
--- a/locales/nb-NO.json
+++ b/locales/nb-NO.json
@@ -325,9 +325,9 @@
"`x` marked it with a ❤": "`x` levnet et ❤",
"Audio mode": "Lydmodus",
"Video mode": "Video-modus",
- "Videos": "Videoer",
+ "channel_tab_videos_label": "Videoer",
"Playlists": "Spillelister",
- "Community": "Gemenskap",
+ "channel_tab_community_label": "Gemenskap",
"search_filters_sort_option_relevance": "relevans",
"search_filters_sort_option_rating": "vurdering",
"search_filters_sort_option_date": "dato",
diff --git a/locales/nl.json b/locales/nl.json
index 17057553..dfc68671 100644
--- a/locales/nl.json
+++ b/locales/nl.json
@@ -320,9 +320,9 @@
"`x` marked it with a ❤": "`x` heeft dit gemarkeerd met ❤",
"Audio mode": "Audiomodus",
"Video mode": "Videomodus",
- "Videos": "Video's",
+ "channel_tab_videos_label": "Video's",
"Playlists": "Afspeellijsten",
- "Community": "Gemeenschap",
+ "channel_tab_community_label": "Gemeenschap",
"search_filters_sort_option_relevance": "relevantie",
"search_filters_sort_option_rating": "beoordeling",
"search_filters_sort_option_date": "datum",
diff --git a/locales/pl.json b/locales/pl.json
index f1a07490..b9c2a638 100644
--- a/locales/pl.json
+++ b/locales/pl.json
@@ -324,9 +324,9 @@
"`x` marked it with a ❤": "`x` oznaczonych ❤",
"Audio mode": "Tryb audio",
"Video mode": "Tryb wideo",
- "Videos": "Filmy",
+ "channel_tab_videos_label": "Wideo",
"Playlists": "Playlisty",
- "Community": "Społeczność",
+ "channel_tab_community_label": "Społeczność",
"search_filters_sort_option_relevance": "Trafność",
"search_filters_sort_option_rating": "Ocena",
"search_filters_sort_option_date": "Data przesłania",
@@ -488,5 +488,9 @@
"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"
}
diff --git a/locales/pt-BR.json b/locales/pt-BR.json
index 41b457bb..112ed4b7 100644
--- a/locales/pt-BR.json
+++ b/locales/pt-BR.json
@@ -341,9 +341,9 @@
"`x` marked it with a ❤": "`x` foi marcado como ❤",
"Audio mode": "Modo de áudio",
"Video mode": "Modo de vídeo",
- "Videos": "Vídeos",
+ "channel_tab_videos_label": "Vídeos",
"Playlists": "Listas de reprodução",
- "Community": "Comunidade",
+ "channel_tab_community_label": "Comunidade",
"search_filters_sort_option_relevance": "relevância",
"search_filters_sort_option_rating": "avaliação",
"search_filters_sort_option_date": "data",
diff --git a/locales/pt-PT.json b/locales/pt-PT.json
index 1bee2807..1788deb1 100644
--- a/locales/pt-PT.json
+++ b/locales/pt-PT.json
@@ -341,9 +341,9 @@
"`x` marked it with a ❤": "`x` foi marcado como ❤",
"Audio mode": "Modo de áudio",
"Video mode": "Modo de vídeo",
- "Videos": "Vídeos",
+ "channel_tab_videos_label": "Vídeos",
"Playlists": "Listas de reprodução",
- "Community": "Comunidade",
+ "channel_tab_community_label": "Comunidade",
"search_filters_sort_option_relevance": "Relevância",
"search_filters_sort_option_rating": "Avaliação",
"search_filters_sort_option_date": "Data de envio",
diff --git a/locales/pt.json b/locales/pt.json
index b550bc87..2facba94 100644
--- a/locales/pt.json
+++ b/locales/pt.json
@@ -267,9 +267,9 @@
"Next page": "Próxima página",
"last": "últimos",
"Current version: ": "Versão atual: ",
- "Community": "Comunidade",
+ "channel_tab_community_label": "Comunidade",
"Playlists": "Listas de reprodução",
- "Videos": "Vídeos",
+ "channel_tab_videos_label": "Vídeos",
"Video mode": "Modo de vídeo",
"Audio mode": "Modo de áudio",
"`x` marked it with a ❤": "`x` foi marcado como ❤",
diff --git a/locales/ro.json b/locales/ro.json
index 342f5f37..0f6407d6 100644
--- a/locales/ro.json
+++ b/locales/ro.json
@@ -315,9 +315,9 @@
"`x` marked it with a ❤": "`x` l-a marcat cu o ❤",
"Audio mode": "Mod audio",
"Video mode": "Mod video",
- "Videos": "Videoclipuri",
+ "channel_tab_videos_label": "Videoclipuri",
"Playlists": "Liste de redare",
- "Community": "Comunitate",
+ "channel_tab_community_label": "Comunitate",
"Current version: ": "Versiunea actuală: ",
"crash_page_read_the_faq": "citit lista <a href=\"`x`\">Întrebărilor Frecvente (FAQ)</a>",
"generic_count_days_0": "{{count}} zi",
diff --git a/locales/ru.json b/locales/ru.json
index 93c9cbec..e54937a6 100644
--- a/locales/ru.json
+++ b/locales/ru.json
@@ -325,9 +325,9 @@
"`x` marked it with a ❤": "❤ от автора канала \"`x`\"",
"Audio mode": "Аудио режим",
"Video mode": "Видео режим",
- "Videos": "Видео",
+ "channel_tab_videos_label": "Видео",
"Playlists": "Плейлисты",
- "Community": "Сообщество",
+ "channel_tab_community_label": "Сообщество",
"search_filters_sort_option_relevance": "по актуальности",
"search_filters_sort_option_rating": "по рейтингу",
"search_filters_sort_option_date": "по дате загрузки",
diff --git a/locales/sl.json b/locales/sl.json
index 5994ca1a..f27bb20d 100644
--- a/locales/sl.json
+++ b/locales/sl.json
@@ -222,7 +222,7 @@
"About": "O aplikaciji",
"%A %B %-d, %Y": "%A %-d %B %Y",
"Audio mode": "Avdio način",
- "Videos": "Videoposnetki",
+ "channel_tab_videos_label": "Videoposnetki",
"search_filters_date_label": "Datum nalaganja",
"search_filters_date_option_today": "Danes",
"search_filters_date_option_week": "Ta teden",
@@ -455,7 +455,7 @@
"Download": "Prenesi",
"permalink": "stalna povezava",
"`x` marked it with a ❤": "`x` ga je označil/a z ❤",
- "Community": "Skupnost",
+ "channel_tab_community_label": "Skupnost",
"search_filters_features_option_three_sixty": "360°",
"Video mode": "Video način",
"search_filters_features_option_c_commons": "Creative Commons",
diff --git a/locales/sq.json b/locales/sq.json
index 76dfd1b7..b8651316 100644
--- a/locales/sq.json
+++ b/locales/sq.json
@@ -259,10 +259,10 @@
"YouTube comment permalink": "Permalidhje komenti YouTube",
"Audio mode": "Mënyrë për audion",
"Playlists": "Luajlista",
- "Community": "Bashkësi",
+ "channel_tab_community_label": "Bashkësi",
"search_filters_sort_option_relevance": "Rëndësi",
"Video mode": "Mënyrë video",
- "Videos": "Video",
+ "channel_tab_videos_label": "Video",
"search_filters_sort_option_rating": "Vlerësim",
"search_filters_sort_option_date": "Datë ngarkimi",
"search_filters_sort_option_views": "Numër parjesh",
diff --git a/locales/sr.json b/locales/sr.json
index d2f990ae..fd19c493 100644
--- a/locales/sr.json
+++ b/locales/sr.json
@@ -257,7 +257,7 @@
"preferences_volume_label": "Jačina zvuka: ",
"preferences_locale_label": "Jezik: ",
"adminprefs_modified_source_code_url_label": "URL veza do skladišta sa Izmenjenom Izvornom Kodom",
- "Community": "Zajednica",
+ "channel_tab_community_label": "Zajednica",
"Video mode": "Video mod",
"Fallback captions: ": "Titl u slučaju da glavni nije dostupan: ",
"Private": "Privatno",
@@ -289,7 +289,7 @@
"Erroneous token": "Pogrešan žeton",
"Czech": "Češki",
"Latin": "Latinski",
- "Videos": "Video klipovi",
+ "channel_tab_videos_label": "Video klipovi",
"search_filters_features_option_four_k": "4К",
"footer_donate_page": "Doniraj",
"English": "Engleski",
diff --git a/locales/sr_Cyrl.json b/locales/sr_Cyrl.json
index c0f1224f..bef9915d 100644
--- a/locales/sr_Cyrl.json
+++ b/locales/sr_Cyrl.json
@@ -245,7 +245,7 @@
"(edited)": "(измењено)",
"`x` marked it with a ❤": "`x` је означио/ла ово са ❤",
"Audio mode": "Аудио мод",
- "Videos": "Видео клипови",
+ "channel_tab_videos_label": "Видео клипови",
"search_filters_sort_option_views": "Број прегледа",
"search_filters_features_label": "Карактеристике",
"search_filters_date_option_today": "Данас",
@@ -298,7 +298,7 @@
"Ukrainian": "Украјински",
"permalink": "трајна веза",
"Pashto": "Паштунски",
- "Community": "Заједница",
+ "channel_tab_community_label": "Заједница",
"Sindhi": "Синди",
"Could not fetch comments": "Узимање коментара није успело",
"Bangla": "Бангла/Бенгалски",
diff --git a/locales/sv-SE.json b/locales/sv-SE.json
index 777899d0..39e94fd3 100644
--- a/locales/sv-SE.json
+++ b/locales/sv-SE.json
@@ -323,9 +323,9 @@
"`x` marked it with a ❤": "`x` lämnade ett ❤",
"Audio mode": "Ljudläge",
"Video mode": "Videoläge",
- "Videos": "Videor",
+ "channel_tab_videos_label": "Videor",
"Playlists": "Spellistor",
- "Community": "Gemenskap",
+ "channel_tab_community_label": "Gemenskap",
"search_filters_sort_option_relevance": "Relevans",
"search_filters_sort_option_rating": "Rankning",
"search_filters_sort_option_date": "Datum",
diff --git a/locales/tr.json b/locales/tr.json
index 17db1cf1..76cce15a 100644
--- a/locales/tr.json
+++ b/locales/tr.json
@@ -325,9 +325,9 @@
"`x` marked it with a ❤": "`x` ❤ İle İşaretledi",
"Audio mode": "Ses Modu",
"Video mode": "Video Modu",
- "Videos": "Videolar",
+ "channel_tab_videos_label": "Videolar",
"Playlists": "Oynatma Listeleri",
- "Community": "Topluluk",
+ "channel_tab_community_label": "Topluluk",
"search_filters_sort_option_relevance": "İlgi",
"search_filters_sort_option_rating": "Değerlendirme",
"search_filters_sort_option_date": "Yükleme Tarihi",
@@ -472,5 +472,9 @@
"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"
}
diff --git a/locales/uk.json b/locales/uk.json
index b6994c56..ae2fb5bd 100644
--- a/locales/uk.json
+++ b/locales/uk.json
@@ -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": "Заголовок",
@@ -315,9 +315,9 @@
"`x` marked it with a ❤": "❤ цьому від каналу `x`",
"Audio mode": "Аудіорежим",
"Video mode": "Відеорежим",
- "Videos": "Відео",
+ "channel_tab_videos_label": "Відео",
"Playlists": "Плейлисти",
- "Community": "Спільнота",
+ "channel_tab_community_label": "Спільнота",
"Current version: ": "Поточна версія: ",
"generic_views_count_0": "{{count}} перегляд",
"generic_views_count_1": "{{count}} перегляди",
@@ -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,9 @@
"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": "Канали"
}
diff --git a/locales/vi.json b/locales/vi.json
index 07fcf52f..3f7125c4 100644
--- a/locales/vi.json
+++ b/locales/vi.json
@@ -311,9 +311,9 @@
"`x` marked it with a ❤": "` x` đã đánh dấu nó bằng một ❤",
"Audio mode": "Chế độ âm thanh",
"Video mode": "Chế độ quay",
- "Videos": "Video",
+ "channel_tab_videos_label": "Video",
"Playlists": "Danh sách phát",
- "Community": "Cộng đồng",
+ "channel_tab_community_label": "Cộng đồng",
"search_filters_sort_option_relevance": "liên quan",
"search_filters_sort_option_rating": "Xếp hạng",
"search_filters_sort_option_date": "ngày",
diff --git a/locales/zh-CN.json b/locales/zh-CN.json
index 7e749dc9..385f16bd 100644
--- a/locales/zh-CN.json
+++ b/locales/zh-CN.json
@@ -341,9 +341,9 @@
"`x` marked it with a ❤": "`x` 为此加 ❤",
"Audio mode": "音频模式",
"Video mode": "视频模式",
- "Videos": "视频",
+ "channel_tab_videos_label": "视频",
"Playlists": "播放列表",
- "Community": "社区",
+ "channel_tab_community_label": "社区",
"search_filters_sort_option_relevance": "相关度",
"search_filters_sort_option_rating": "评分",
"search_filters_sort_option_date": "上传日期",
diff --git a/locales/zh-TW.json b/locales/zh-TW.json
index 54933701..3b51721d 100644
--- a/locales/zh-TW.json
+++ b/locales/zh-TW.json
@@ -341,9 +341,9 @@
"`x` marked it with a ❤": "`x` 為此標記 ❤",
"Audio mode": "音訊模式",
"Video mode": "視訊模式",
- "Videos": "影片",
+ "channel_tab_videos_label": "影片",
"Playlists": "播放清單",
- "Community": "社群",
+ "channel_tab_community_label": "社群",
"search_filters_sort_option_relevance": "關聯",
"search_filters_sort_option_rating": "評分",
"search_filters_sort_option_date": "日期",
@@ -456,5 +456,9 @@
"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": "直播"
}
diff --git a/scripts/deploy-database.sh b/scripts/deploy-database.sh
index fa24b8f0..fa24b8f0 100644..100755
--- a/scripts/deploy-database.sh
+++ b/scripts/deploy-database.sh
diff --git a/scripts/fetch-player-dependencies.cr b/scripts/fetch-player-dependencies.cr
index ed658b51..813e4ce4 100644..100755
--- a/scripts/fetch-player-dependencies.cr
+++ b/scripts/fetch-player-dependencies.cr
@@ -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`
diff --git a/scripts/install-dependencies.sh b/scripts/install-dependencies.sh
index 1e67bdaf..1e67bdaf 100644..100755
--- a/scripts/install-dependencies.sh
+++ b/scripts/install-dependencies.sh
diff --git a/src/invidious.cr b/src/invidious.cr
index 5064f0b8..d4f8e0fb 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -34,6 +34,7 @@ require "protodec/utils"
require "./invidious/database/*"
require "./invidious/database/migrations/*"
+require "./invidious/http_server/*"
require "./invidious/helpers/*"
require "./invidious/yt_backend/*"
require "./invidious/frontend/*"
diff --git a/src/invidious/channels/community.cr b/src/invidious/channels/community.cr
index 8e300288..76dff555 100644
--- a/src/invidious/channels/community.cr
+++ b/src/invidious/channels/community.cr
@@ -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
diff --git a/src/invidious/helpers/json_filter.cr b/src/invidious/helpers/json_filter.cr
index b8e8f96d..3f4080ba 100644
--- a/src/invidious/helpers/json_filter.cr
+++ b/src/invidious/helpers/json_filter.cr
@@ -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)
diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr
index ed0cca38..500a2582 100644
--- a/src/invidious/helpers/utils.cr
+++ b/src/invidious/helpers/utils.cr
@@ -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
diff --git a/src/invidious/http_server/utils.cr b/src/invidious/http_server/utils.cr
new file mode 100644
index 00000000..e3f1fa0f
--- /dev/null
+++ b/src/invidious/http_server/utils.cr
@@ -0,0 +1,20 @@
+module Invidious::HttpServer
+ module Utils
+ extend self
+
+ def proxy_video_url(raw_url : String, *, region : String? = nil, absolute : Bool = false)
+ url = URI.parse(raw_url)
+
+ # Add some URL parameters
+ params = url.query_params
+ params["host"] = url.host.not_nil! # Should never be nil, in theory
+ params["region"] = region if !region.nil?
+
+ if absolute
+ return "#{HOST_URL}#{url.request_target}?#{params}"
+ else
+ return "#{url.request_target}?#{params}"
+ end
+ end
+ end
+end
diff --git a/src/invidious/jsonify/api_v1/video_json.cr b/src/invidious/jsonify/api_v1/video_json.cr
index 642789aa..a2b1a35c 100644
--- a/src/invidious/jsonify/api_v1/video_json.cr
+++ b/src/invidious/jsonify/api_v1/video_json.cr
@@ -3,7 +3,7 @@ require "json"
module Invidious::JSONify::APIv1
extend self
- def video(video : Video, json : JSON::Builder, *, locale : String?)
+ def video(video : Video, json : JSON::Builder, *, locale : String?, proxy : Bool = false)
json.object do
json.field "type", video.video_type
@@ -89,7 +89,14 @@ module Invidious::JSONify::APIv1
# Not available on MPEG-4 Timed Text (`text/mp4`) streams (livestreams only)
json.field "bitrate", fmt["bitrate"].as_i.to_s if fmt["bitrate"]?
- json.field "url", fmt["url"]
+ if proxy
+ json.field "url", Invidious::HttpServer::Utils.proxy_video_url(
+ fmt["url"].to_s, absolute: true
+ )
+ else
+ json.field "url", fmt["url"]
+ end
+
json.field "itag", fmt["itag"].as_i.to_s
json.field "type", fmt["mimeType"]
json.field "clen", fmt["contentLength"]? || "-1"
diff --git a/src/invidious/routes/account.cr b/src/invidious/routes/account.cr
index 9bb73136..e6a70ed2 100644
--- a/src/invidious/routes/account.cr
+++ b/src/invidious/routes/account.cr
@@ -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)
diff --git a/src/invidious/routes/api/manifest.cr b/src/invidious/routes/api/manifest.cr
index ae65f10d..662d1002 100644
--- a/src/invidious/routes/api/manifest.cr
+++ b/src/invidious/routes/api/manifest.cr
@@ -29,7 +29,7 @@ module Invidious::Routes::API::Manifest
if local
uri = URI.parse(url)
- url = "#{uri.request_target}host/#{uri.host}/"
+ url = "#{HOST_URL}#{uri.request_target}host/#{uri.host}/"
end
"<BaseURL>#{url}</BaseURL>"
@@ -42,7 +42,7 @@ module Invidious::Routes::API::Manifest
if local
adaptive_fmts.each do |fmt|
- fmt["url"] = JSON::Any.new(URI.parse(fmt["url"].as_s).request_target)
+ fmt["url"] = JSON::Any.new("#{HOST_URL}#{URI.parse(fmt["url"].as_s).request_target}")
end
end
diff --git a/src/invidious/routes/api/v1/videos.cr b/src/invidious/routes/api/v1/videos.cr
index a6b2eb4e..f312211e 100644
--- a/src/invidious/routes/api/v1/videos.cr
+++ b/src/invidious/routes/api/v1/videos.cr
@@ -6,6 +6,7 @@ module Invidious::Routes::API::V1::Videos
id = env.params.url["id"]
region = env.params.query["region"]?
+ proxy = {"1", "true"}.any? &.== env.params.query["local"]?
begin
video = get_video(id, region: region)
@@ -15,7 +16,9 @@ module Invidious::Routes::API::V1::Videos
return error_json(500, ex)
end
- video.to_json(locale, nil)
+ return JSON.build do |json|
+ Invidious::JSONify::APIv1.video(video, json, locale: locale, proxy: proxy)
+ end
end
def self.captions(env)
@@ -90,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
- 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
+ END_VTT
- if caption_nodes.size > i + 1
- end_time = caption_nodes[i + 1]["start"].to_f.seconds
- else
- end_time = start_time + duration
- end
+ 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
- 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')}"
+ if caption_nodes.size > i + 1
+ end_time = caption_nodes[i + 1]["start"].to_f.seconds
+ else
+ end_time = start_time + duration
+ end
- 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
+ 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}
+ str << <<-END_CUE
+ #{start_time} --> #{end_time}
+ #{text}
- END_CUE
+ END_CUE
+ end
end
end
else
@@ -138,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"]?
diff --git a/src/invidious/routes/login.cr b/src/invidious/routes/login.cr
index 99fc13a2..6454131a 100644
--- a/src/invidious/routes/login.cr
+++ b/src/invidious/routes/login.cr
@@ -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
diff --git a/src/invidious/routes/video_playback.cr b/src/invidious/routes/video_playback.cr
index 560f9c19..1e932d11 100644
--- a/src/invidious/routes/video_playback.cr
+++ b/src/invidious/routes/video_playback.cr
@@ -35,6 +35,13 @@ module Invidious::Routes::VideoPlayback
end
end
+ # See: https://github.com/iv-org/invidious/issues/3302
+ range_header = env.request.headers["Range"]?
+ if range_header.nil?
+ range_for_head = query_params["range"]? || "0-640"
+ headers["Range"] = "bytes=#{range_for_head}"
+ end
+
client = make_client(URI.parse(host), region)
response = HTTP::Client::Response.new(500)
error = ""
@@ -70,6 +77,9 @@ module Invidious::Routes::VideoPlayback
end
end
+ # Remove the Range header added previously.
+ headers.delete("Range") if range_header.nil?
+
if response.status_code >= 400
env.response.content_type = "text/plain"
haltf env, response.status_code
@@ -91,14 +101,8 @@ module Invidious::Routes::VideoPlayback
env.response.headers["Access-Control-Allow-Origin"] = "*"
if location = resp.headers["Location"]?
- location = URI.parse(location)
- location = "#{location.request_target}&host=#{location.host}"
-
- if region
- location += "&region=#{region}"
- end
-
- return env.redirect location
+ url = Invidious::HttpServer::Utils.proxy_video_url(location, region: region)
+ return env.redirect url
end
IO.copy(resp.body_io, env.response)
diff --git a/src/invidious/routing.cr b/src/invidious/routing.cr
index 491022a5..157e6de7 100644
--- a/src/invidious/routing.cr
+++ b/src/invidious/routing.cr
@@ -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
diff --git a/src/invidious/videos/caption.cr b/src/invidious/videos/caption.cr
index 4642c1a7..13f81a31 100644
--- a/src/invidious/videos/caption.cr
+++ b/src/invidious/videos/caption.cr
@@ -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 = {
"",
diff --git a/src/invidious/videos/parser.cr b/src/invidious/videos/parser.cr
index 0abac32f..cf43f1be 100644
--- a/src/invidious/videos/parser.cr
+++ b/src/invidious/videos/parser.cr
@@ -66,8 +66,10 @@ def extract_video_info(video_id : String, proxy_region : String? = nil)
reason ||= subreason.try &.[]("runs").as_a.map(&.[]("text")).join("")
reason ||= player_response.dig("playabilityStatus", "reason").as_s
- # Stop here if video is not a scheduled livestream
- if !{"LIVE_STREAM_OFFLINE", "LOGIN_REQUIRED"}.any?(playability_status)
+ # Stop here if video is not a scheduled livestream or
+ # for LOGIN_REQUIRED when videoDetails element is not found because retrying won't help
+ if !{"LIVE_STREAM_OFFLINE", "LOGIN_REQUIRED"}.any?(playability_status) ||
+ playability_status == "LOGIN_REQUIRED" && !player_response.dig?("videoDetails")
return {
"version" => JSON::Any.new(Video::SCHEMA_VERSION.to_i64),
"reason" => JSON::Any.new(reason),
diff --git a/src/invidious/yt_backend/extractors.cr b/src/invidious/yt_backend/extractors.cr
index 65d107b2..b14ad7b9 100644
--- a/src/invidious/yt_backend/extractors.cr
+++ b/src/invidious/yt_backend/extractors.cr
@@ -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