summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile118
-rw-r--r--README.md23
-rw-r--r--locales/ca.json103
-rw-r--r--locales/fr.json12
-rw-r--r--locales/id.json70
-rw-r--r--locales/pt-BR.json4
-rw-r--r--locales/sr.json371
-rw-r--r--spec/helpers_spec.cr19
-rw-r--r--src/invidious/comments.cr41
-rw-r--r--src/invidious/routes/video_playback.cr2
-rw-r--r--src/invidious/trending.cr10
-rw-r--r--src/invidious/videos.cr12
12 files changed, 662 insertions, 123 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..7271d9bd
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,118 @@
+# -----------------------
+# Compilation options
+# -----------------------
+
+RELEASE := 1
+STATIC := 0
+
+DISABLE_LSQUIC := 0
+NO_DBG_SYMBOLS := 0
+
+
+FLAGS ?=
+
+
+ifeq ($(RELEASE), 1)
+ FLAGS += --release
+endif
+
+ifeq ($(STATIC), 1)
+ FLAGS += --static
+endif
+
+
+ifeq ($(NO_DBG_SYMBOLS), 1)
+ FLAGS += --no-debug
+else
+ FLAGS += --debug
+endif
+
+ifeq ($(DISABLE_LSQUIC), 1)
+ FLAGS += -Ddisable_lsquic
+endif
+
+
+# -----------------------
+# Main
+# -----------------------
+
+all: invidious
+
+get-libs:
+ shards install --production
+
+# TODO: add support for ARM64 via cross-compilation
+invidious: get-libs
+ crystal build src/invidious.cr $(FLAGS) --progress --stats --error-trace
+
+
+run: invidious
+ ./invidious
+
+
+# -----------------------
+# Development
+# -----------------------
+
+
+format:
+ crystal tool format
+
+test:
+ crystal spec
+
+verify:
+ crystal build src/invidious.cr --no-codegen --progress --stats --error-trace
+
+
+# -----------------------
+# (Un)Install
+# -----------------------
+
+# TODO
+
+
+# -----------------------
+# Cleaning
+# -----------------------
+
+clean:
+ rm invidious
+
+distclean: clean
+ rm -rf libs
+
+
+# -----------------------
+# Help page
+# -----------------------
+
+help:
+ echo "Targets available in this Makefile:"
+ echo ""
+ echo "get-libs Fetch Crystal libraries"
+ echo "invidious Build Invidious"
+ echo "run Launch Invidious"
+ echo ""
+ echo "format Run the Crystal formatter"
+ echo "test Run tests"
+ echo "verify Just make sure that the code compiles, but without"
+ echo " generating any binaries. Useful to search for errors"
+ echo ""
+ echo "clean Remove build artifacts"
+ echo "distclean Remove build artifacts and libraries"
+ echo ""
+ echo ""
+ echo "Build options available for this Makefile:"
+ echo ""
+ echo "RELEASE Make a release build (Default: 1)"
+ echo "STATIC Link librariess tatically (Default: 1)"
+ echo ""
+ echo "DISABLE_LSQUIC Don't use lsquic (Default: 0)"
+ echo "NO_DBG_SYMBOLS Strip debug symbols (Default: 0)"
+
+
+
+# No targets generates an output named after themselves
+.PHONY: all get-libs build amd64 run
+.PHONY: format test verify clean distclean help
diff --git a/README.md b/README.md
index 8beb4380..29b8397a 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,9 @@
<a href="https://github.com/iv-org/invidious/actions">
<img alt="Build Status" src="https://github.com/iv-org/invidious/workflows/Invidious%20CI/badge.svg">
</a>
+ <a href="https://github.com/iv-org/invidious/commits/master">
+ <img alt="GitHub commits" src="https://img.shields.io/github/commit-activity/y/iv-org/invidious?color=red&label=commits">
+ </a>
<a href="https://github.com/iv-org/invidious/issues">
<img alt="GitHub issues" src="https://img.shields.io/github/issues/iv-org/invidious?color=important">
</a>
@@ -17,19 +20,22 @@
<a href="https://hosted.weblate.org/engage/invidious/">
<img alt="Translation Status" src="https://hosted.weblate.org/widgets/invidious/-/translations/svg-badge.svg">
</a>
+
<a href="https://github.com/humanetech-community/awesome-humane-tech">
<img alt="Awesome Humane Tech" src="https://raw.githubusercontent.com/humanetech-community/awesome-humane-tech/main/humane-tech-badge.svg?sanitize=true">
</a>
<h3>An open source alternative front-end to YouTube</h3>
+ <a href="https://invidious.io/">Website</a>
+ &nbsp;•&nbsp;
<a href="https://instances.invidious.io/">Instances list</a>
&nbsp;•&nbsp;
- <a href="#documentation">Documentation</a>
+ <a href="https://docs.invidious.io/">Documentation</a>
&nbsp;•&nbsp;
<a href="#contribute">Contribute</a>
&nbsp;•&nbsp;
- <a href="#donate">Donate</a>
+ <a href="https://invidious.io/donate/">Donate</a>
<h5>Chat with us:</h5>
<a href="https://matrix.to/#/#invidious:matrix.org">
@@ -133,18 +139,7 @@ Weblate also allows you to log-in with major SSO providers like Github, Gitlab,
- [CloudTube](https://sr.ht/~cadence/tube/): A JavaScript-rich alternate YouTube player.
- [PeerTubeify](https://gitlab.com/Cha_deL/peertubeify): On YouTube, displays a link to the same video on PeerTube, if it exists.
- [MusicPiped](https://github.com/deep-gaurav/MusicPiped): A material design music player that streams music from YouTube.
-- [HoloPlay](https://github.com/stephane-r/HoloPlay): Funny Android application connecting on Invidious API's with search, playlists and favoris.
-
-
-## Donate
-
-Bitcoin (BTC): [bc1qfhe7rq3lqzuayzjxzyt9waz9ytrs09kla3tsgr](bitcoin:bc1qfhe7rq3lqzuayzjxzyt9waz9ytrs09kla3tsgr)
-
-Monero (XMR): [41nMCtek197boJtiUvGnTFYMatrLEpnpkQDmUECqx5Es2uX3sTKKWVhSL76suXsG3LXqkEJBrCZBgPTwJrDp1FrZJfycGPR](monero:41nMCtek197boJtiUvGnTFYMatrLEpnpkQDmUECqx5Es2uX3sTKKWVhSL76suXsG3LXqkEJBrCZBgPTwJrDp1FrZJfycGPR)
-
-Ethereum (ETH): [0xD1F7E3Bfb19Ee5a52baED396Ad34717aF18d995B](ethereum:0xD1F7E3Bfb19Ee5a52baED396Ad34717aF18d995B)
-
-Litecoin (LTC): [ltc1q8787aq2xrseq5yx52axx8c4fqks88zj5vr0zx9](litecoin:ltc1q8787aq2xrseq5yx52axx8c4fqks88zj5vr0zx9)
+- [HoloPlay](https://github.com/stephane-r/HoloPlay): Funny Android application connecting on Invidious API's with search, playlists and favorites.
## Liability
diff --git a/locales/ca.json b/locales/ca.json
new file mode 100644
index 00000000..1fa7cc1f
--- /dev/null
+++ b/locales/ca.json
@@ -0,0 +1,103 @@
+{
+ "oldest": "més antic",
+ "Yes": "Sí",
+ "preferences_quality_label": "Qualitat de vídeo preferida: ",
+ "newest": "més nou",
+ "No": "No",
+ "Google verification code": "Codi de verificació de Google",
+ "User ID": "ID d'usuari",
+ "Preferences": "Preferències",
+ "Dark mode: ": "Mode fosc: ",
+ "dark": "fosc",
+ "light": "clar",
+ "published": "publicat",
+ "published - reverse": "publicat - invers",
+ "alphabetically": "alfabèticament",
+ "alphabetically - reverse": "alfabèticament - invers",
+ "channel name - reverse": "nom del canal - invers",
+ "preferences_category_data": "Preferències de dades",
+ "Delete account": "Elimina compte",
+ "Save preferences": "Guarda preferències",
+ "Private": "Privat",
+ "Show more": "Mostra'n més",
+ "Show less": "Mostra'n menys",
+ "Hide replies": "Amaga respostes",
+ "Arabic": "Àrab",
+ "Armenian": "Armeni",
+ "Basque": "Basc",
+ "Filipino": "Filipí",
+ "Finnish": "Finès",
+ "German": "Alemany",
+ "Greek": "Grec",
+ "Hungarian": "Hongarès",
+ "Icelandic": "Islandès",
+ "Italian": "Italià",
+ "Japanese": "Japonès",
+ "Korean": "Coreà",
+ "Kurdish": "Kurd",
+ "Lithuanian": "Lituà",
+ "Luxembourgish": "Luxemburguès",
+ "Macedonian": "Macedoni",
+ "Polish": "Polonès",
+ "Portuguese": "Portuguès",
+ "Romanian": "Romanès",
+ "Russian": "Rus",
+ "Serbian": "Serbi",
+ "Spanish (Latin America)": "Castellà (Amèrica llatina)",
+ "Turkish": "Turc",
+ "Ukrainian": "Ucraïnès",
+ "preferences_locale_label": "Idioma: ",
+ "Gaming": "Jocs",
+ "Movies": "Películes",
+ "Download": "Descarrega",
+ "Download as: ": "Descarrega com: ",
+ "Videos": "Vídeos",
+ "content_type": "Tipus",
+ "duration": "Duració",
+ "sort": "Ordena per",
+ "week": "Aquesta setmana",
+ "month": "Aquest mes",
+ "year": "Aquest any",
+ "video": "Vídeo",
+ "channel": "Canal",
+ "short": "Curt (< 4 minuts)",
+ "long": "Llarg (> 20 minuts)",
+ "Current version: ": "Versió actual: ",
+ "Malay": "Malai",
+ "Persian": "Persa",
+ "Slovak": "Eslovac",
+ "Search": "Busca",
+ "Show annotations": "Mostra anotacions",
+ "preferences_region_label": "País del contingut: ",
+ "preferences_sort_label": "Ordena vídeos per: ",
+ "Import/export": "Importa/exporta",
+ "channel name": "nom del canal",
+ "Title": "Títol",
+ "Belarusian": "Bielorús",
+ "Enable web notifications": "Activa notificacions web",
+ "search": "busca",
+ "Catalan": "Català",
+ "Croatian": "Croat",
+ "preferences_category_admin": "Preferències d'administrador",
+ "Hide annotations": "Amaga anotacions",
+ "Show replies": "Mostra respostes",
+ "Bulgarian": "Búlgar",
+ "Albanian": "Albanès",
+ "French": "Francès",
+ "Irish": "Irlandès",
+ "Maltese": "Maltès",
+ "Danish": "Danès",
+ "Galician": "Gallec",
+ "Hebrew": "Hebreu",
+ "Indonesian": "Indonesi",
+ "Spanish": "Castellà",
+ "Vietnamese": "Vietnamita",
+ "News": "Notícies",
+ "show": "Mostra",
+ "footer_documentation": "Documentació",
+ "Thai": "Tailandès",
+ "Music": "Música",
+ "relevance": "Rellevància",
+ "hour": "Última hora",
+ "today": "Avui"
+}
diff --git a/locales/fr.json b/locales/fr.json
index 2332594a..00971576 100644
--- a/locales/fr.json
+++ b/locales/fr.json
@@ -423,5 +423,15 @@
"Current version: ": "Version actuelle : ",
"next_steps_error_message": "Vous pouvez essayer de : ",
"next_steps_error_message_refresh": "Rafraîchir la page",
- "next_steps_error_message_go_to_youtube": "Aller sur YouTube"
+ "next_steps_error_message_go_to_youtube": "Aller sur YouTube",
+ "preferences_quality_dash_label": "Qualité préférée de la vidéo du tableau de bord : ",
+ "footer_source_code": "Code source",
+ "preferences_region_label": "Pays du contenu : ",
+ "footer_donate_page": "Faire un don",
+ "footer_modfied_source_code": "Code source modifié",
+ "short": "Courte (< 4 minutes)",
+ "long": "Longue (> 20 minutes)",
+ "adminprefs_modified_source_code_url_label": "URL du dépôt du code source modifié",
+ "footer_documentation": "Documentation",
+ "footer_original_source_code": "Code source original"
}
diff --git a/locales/id.json b/locales/id.json
index ca4c1e0f..054a2557 100644
--- a/locales/id.json
+++ b/locales/id.json
@@ -14,7 +14,7 @@
"LIVE": "SIARAN LANGSUNG",
"Shared `x` ago": "Dibagikan `x` yang lalu",
"Unsubscribe": "Batal Langganan",
- "Subscribe": "Berangganan",
+ "Subscribe": "Berlangganan",
"View channel on YouTube": "Lihat kanal di YouTube",
"View playlist on YouTube": "Lihat daftar putar di YouTube",
"newest": "terbaru",
@@ -209,7 +209,7 @@
"Please log in": "Harap masuk",
"Invidious Private Feed for `x`": "Umpan pribadi Invidious untuk`x`",
"channel:`x`": "kanal:`x`",
- "Deleted or invalid channel": "Kanal terhapus atau invalid",
+ "Deleted or invalid channel": "Kanal terhapus atau tidak valid",
"This channel does not exist.": "Kanal ini tidak ada.",
"Could not get channel info.": "Tidak bisa mendapatkan info kanal.",
"Could not fetch comments": "Tidak dapat memuat komentar",
@@ -220,8 +220,8 @@
"`x` ago": "`x` lalu",
"Load more": "Muat lebih banyak",
"`x` points": {
- "([^.,0-9]|^)1([^.,0-9]|$)": "`x` titik",
- "": "`x` titik"
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` poin",
+ "": "`x` poin"
},
"Could not create mix.": "Tidak dapat membuat mix.",
"Empty playlist": "Daftar putar kosong",
@@ -372,10 +372,10 @@
"Popular": "Populer",
"Search": "Cari",
"Top": "Teratas",
- "About": "Ihwal",
- "Rating: ": "Peringkat: ",
+ "About": "Tentang",
+ "Rating: ": "Rating: ",
"preferences_locale_label": "Bahasa: ",
- "View as playlist": "Tampilkan sebagai daftar putar",
+ "View as playlist": "Lihat sebagai daftar putar",
"Default": "Baku",
"Music": "Musik",
"Gaming": "Permainan",
@@ -385,7 +385,7 @@
"Download as: ": "Unduh sebagai: ",
"%A %B %-d, %Y": "%A %B %-d, %Y",
"(edited)": "(disunting)",
- "YouTube comment permalink": "Komentar YouTube permalink",
+ "YouTube comment permalink": "Tautan permanen komentar YouTube",
"permalink": "tautan permanen",
"`x` marked it with a ❤": "`x` telah ditandai dengan ❤",
"Audio mode": "Mode audio",
@@ -393,33 +393,33 @@
"Videos": "Video",
"Playlists": "Daftar putar",
"Community": "Komunitas",
- "relevance": "Relevan",
- "rating": "peringkat",
- "date": "tanggal",
- "views": "ditonton",
- "content_type": "tipe_konten",
- "duration": "durasi",
- "features": "fitur",
- "sort": "urut",
- "hour": "jam",
- "today": "hari ini",
- "week": "minggu",
- "month": "bulan",
- "year": "tahun",
- "video": "video",
- "channel": "kanal",
- "playlist": "daftar putar",
- "movie": "film",
- "show": "tampilkan",
- "hd": "hd",
- "subtitles": "subtitel",
- "creative_commons": "creative_commons",
- "3d": "3d",
- "live": "siaran langsung",
- "4k": "4k",
- "location": "lokasi",
- "hdr": "hdr",
- "filter": "saring",
+ "relevance": "Relevansi",
+ "rating": "Rating",
+ "date": "Tanggal unggah",
+ "views": "Jumlah ditonton",
+ "content_type": "Tipe",
+ "duration": "Durasi",
+ "features": "Fitur",
+ "sort": "Urut Berdasarkan",
+ "hour": "Jam Terakhir",
+ "today": "Hari Ini",
+ "week": "Pekan Ini",
+ "month": "Bulan Ini",
+ "year": "Tahun Ini",
+ "video": "Video",
+ "channel": "Kanal",
+ "playlist": "Daftar Putar",
+ "movie": "Film",
+ "show": "Pertunjukan/Acara",
+ "hd": "HD",
+ "subtitles": "Takarir",
+ "creative_commons": "Creative Commons",
+ "3d": "3D",
+ "live": "Siaran Langsung",
+ "4k": "4K",
+ "location": "Lokasi",
+ "hdr": "HDR",
+ "filter": "Saring",
"Current version: ": "Versi saat ini: ",
"next_steps_error_message": "Setelah itu Anda harus mencoba: ",
"next_steps_error_message_refresh": "Segarkan",
diff --git a/locales/pt-BR.json b/locales/pt-BR.json
index 072deff2..6baa2c0d 100644
--- a/locales/pt-BR.json
+++ b/locales/pt-BR.json
@@ -431,5 +431,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: "
}
diff --git a/locales/sr.json b/locales/sr.json
index 0082bd66..e0713b43 100644
--- a/locales/sr.json
+++ b/locales/sr.json
@@ -19,7 +19,7 @@
"View playlist on YouTube": "Погледај списак извођења на YouTube-у",
"newest": "најновије",
"oldest": "најстарије",
- "popular": "гласовито",
+ "popular": "популарно",
"last": "последње",
"Next page": "Следећа страница",
"Previous page": "Претходна страница",
@@ -66,5 +66,372 @@
"preferences_continue_label": "Увек подразумевано пуштај следеће: ",
"preferences_continue_autoplay_label": "Самопуштање следећег видео записа: ",
"preferences_listen_label": "Увек подразумевано укључен само звук: ",
- "preferences_local_label": "Приказ видео записа преко посредника: "
+ "preferences_local_label": "Приказ видео записа преко посредника: ",
+ "Playlist privacy": "Подешавања приватности плеј листе",
+ "Editing playlist `x`": "Измена плеј листе `x`",
+ "Please sign in using 'Log in with Google'": "Молимо Вас да се пријавите помоћу 'Log in with Google'",
+ "Playlist does not exist.": "Непостојећа плеј листа.",
+ "Erroneous challenge": "Погрешан изазов",
+ "Maltese": "Малтешки",
+ "Download": "Преузми",
+ "Download as: ": "Преузми као: ",
+ "Quota exceeded, try again in a few hours": "Квота је премашена, молимо Вас да покушате за пар сати",
+ "Bangla": "Бангла/Бенгалски",
+ "preferences_quality_dash_label": "Преферирани квалитет dash видео формата: ",
+ "Token manager": "Меначер токена",
+ "Token": "Токен",
+ "`x` tokens": {
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` токен",
+ "": "`x` токена"
+ },
+ "Import/export": "Увези/Извези",
+ "revoke": "опозови",
+ "`x` unseen notifications": {
+ "": "`x` непрегледаних обавештења",
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` непрегледаних обавештења"
+ },
+ "search": "претрага",
+ "Log out": "Одјава",
+ "Source available here.": "Изворни код.",
+ "Trending": "У тренду",
+ "Updated `x` ago": "Ажурирано пре `x`",
+ "Delete playlist `x`?": "Обриши плеј листу `x`?",
+ "Create playlist": "Направи плеј листу",
+ "Show less": "Прикажи мање",
+ "Switch Invidious Instance": "Промени Invidious инстанцу",
+ "Hide annotations": "Сакриј напомене",
+ "User ID is a required field": "Кориснички ID је обавезно поље",
+ "Wrong username or password": "Погрешно корисничко име или лозинка",
+ "Please log in": "Молимо Вас да се пријавите",
+ "channel:`x`": "канал:`x`",
+ "Could not fetch comments": "Узимање коментара није успело",
+ "`x` points": {
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` поен",
+ "": "`x` поена"
+ },
+ "Could not create mix.": "Прављење микса није успело.",
+ "Empty playlist": "Празна плеј листа",
+ "Not a playlist.": "Није плеј листа.",
+ "Could not pull trending pages.": "Учитавање 'У току' странције није успело.",
+ "Token is expired, please try again": "Токен је истекао, молимо Вас да покушате поново",
+ "English (auto-generated)": "Енглески (аутоматски генерисано)",
+ "Afrikaans": "Африканс",
+ "Albanian": "Албански",
+ "Armenian": "Јерменски",
+ "Azerbaijani": "Азербејџански",
+ "Basque": "Баскијски",
+ "Bosnian": "Српски (Босна и Херцеговина)",
+ "Bulgarian": "Бугарски",
+ "Burmese": "Бурмански",
+ "Catalan": "Каталонски",
+ "Cebuano": "Cebuano",
+ "Chinese (Traditional)": "Кинески (Традиционални)",
+ "Corsican": "Корзикански",
+ "Danish": "Дански",
+ "Kannada": "Канада (Језик)",
+ "Kazakh": "Казашки",
+ "Russian": "Руски",
+ "Scottish Gaelic": "Шкотски Гелски",
+ "Sinhala": "Синхалешки",
+ "Slovak": "Словачки",
+ "Spanish": "Шпански",
+ "Spanish (Latin America)": "Шпански (Јужна Америка)",
+ "Sundanese": "Сундски",
+ "Swedish": "Шведски",
+ "Tajik": "Таџички",
+ "Telugu": "Телугу",
+ "Turkish": "Турски",
+ "Ukrainian": "Украјински",
+ "Urdu": "Урду",
+ "Uzbek": "Узбечки",
+ "Vietnamese": "Вијетнамски",
+ "`x` minutes": {
+ "": "`x` минута",
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` минут"
+ },
+ "`x` seconds": {
+ "": "`x` секунди",
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` секунда"
+ },
+ "Rating: ": "Оцена/е: ",
+ "View as playlist": "Погледај као плеј листу",
+ "Default": "Подразумеван/о",
+ "Gaming": "Гејминг",
+ "Movies": "Филмови",
+ "%A %B %-d, %Y": "%A %B %-d, %Y",
+ "(edited)": "(измењено)",
+ "YouTube comment permalink": "YouTube коментар трајна веза",
+ "Audio mode": "Аудио мод",
+ "Playlists": "Плеј листе",
+ "relevance": "Ревелантно",
+ "rating": "Оцене",
+ "date": "Датум отпремања",
+ "views": "Број прегледа",
+ "`x` marked it with a ❤": "`x` је означио/ла ово са ❤",
+ "duration": "Трајање",
+ "features": "Карактеристике",
+ "hour": "Последњи сат",
+ "week": "Ове недеље",
+ "month": "Овај месец",
+ "year": "Ове године",
+ "video": "Видео",
+ "playlist": "Плеј листа",
+ "movie": "Филм",
+ "long": "Дуг (> 20 минута)",
+ "hd": "HD",
+ "creative_commons": "Creative Commons (Лиценца)",
+ "3d": "3Д",
+ "hdr": "Видео високе резолуције",
+ "filter": "Филтер",
+ "next_steps_error_message": "Можете урадити нешта од следећег: ",
+ "next_steps_error_message_go_to_youtube": "Иди на YouTube",
+ "footer_documentation": "Документација",
+ "preferences_region_label": "Држава порекла садржаја: ",
+ "preferences_player_style_label": "Стил плејера: ",
+ "preferences_dark_mode_label": "Изглед/Тема: ",
+ "light": "светла",
+ "preferences_thin_mode_label": "Компактни режим: ",
+ "preferences_category_misc": "Остала подешавања",
+ "preferences_automatic_instance_redirect_label": "Аутоматско пребацивање на другу инстанцу у случају отказивања (пређи на redirect.invidious.io): ",
+ "alphabetically - reverse": "по алфабету - обрнуто",
+ "Enable web notifications": "Омогући обавештења у веб претраживачу",
+ "`x` is live": "`x` преноси уживо",
+ "Manage tokens": "Управљај токенима",
+ "Watch history": "Историја гледања",
+ "preferences_feed_menu_label": "Feed мени: ",
+ "preferences_show_nick_label": "Прикажи надимке на врху: ",
+ "CAPTCHA enabled: ": "CAPTCHA омогућена: ",
+ "Registration enabled: ": "Регистрација омогућена: ",
+ "Subscription manager": "Менаџер претплата",
+ "Wilson score: ": "Wilson скор: ",
+ "Engagement: ": "Ангажовање: ",
+ "Whitelisted regions: ": "Дозвољени региони: ",
+ "Shared `x`": "Подељено `x`",
+ "`x` views": {
+ "": "`x` прегледа",
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`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. Кликните овде да видите коментаре, имајте на уму да ово може да потраје дуже док се не учитају.",
+ "View `x` comments": {
+ "([^.,0-9]|^)1([^.,0-9]|$)": "Прикажи `x` коментар",
+ "": "Прикажи `x` коментара"
+ },
+ "View Reddit comments": "Прикажи Reddit коментаре",
+ "Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Неуспешна пријава, проверите да ли сте упалили двофакторну аутентикацију (Аутентикатор или СМС).",
+ "CAPTCHA is a required field": "CAPTCHA је обавезно поље",
+ "Croatian": "Хрватски",
+ "Estonian": "Естонски",
+ "Filipino": "Филипино",
+ "French": "Француски",
+ "Galician": "Галицијски",
+ "German": "Немачки",
+ "Greek": "Грчки",
+ "Hausa": "Хауса",
+ "Italian": "Италијански",
+ "Khmer": "Кмерски",
+ "Kurdish": "Курдски",
+ "Kyrgyz": "Киргиски",
+ "Latvian": "Летонски",
+ "Lithuanian": "Литвански",
+ "Macedonian": "Македонски",
+ "Malagasy": "Малгашки",
+ "Malay": "Малајски",
+ "Marathi": "Маратхи",
+ "Mongolian": "Монголски",
+ "Norwegian Bokmål": "Норвешки Бокмал",
+ "Nyanja": "Чева",
+ "Pashto": "Паштунски",
+ "Persian": "Персијски",
+ "Punjabi": "Пунџаби",
+ "Romanian": "Румунски",
+ "Welsh": "Велшки",
+ "Western Frisian": "Западнофризијски",
+ "`x` years": {
+ "": "`x` година",
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` година"
+ },
+ "`x` weeks": {
+ "": "`x` недеља",
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` недеља"
+ },
+ "`x` days": {
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` дан",
+ "": "`x` дана"
+ },
+ "`x` hours": {
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` сат",
+ "": "`x` сати"
+ },
+ "Fallback comments: ": "Коментари у случају отказивања: ",
+ "Popular": "Популарно",
+ "Search": "Претрага",
+ "About": "О програму",
+ "footer_source_code": "Изворни Код",
+ "footer_original_source_code": "Оригинални изворни код",
+ "preferences_related_videos_label": "Прикажи сличне видео клипове: ",
+ "preferences_annotations_label": "Прикажи напомене подразумевано: ",
+ "preferences_extend_desc_label": "Аутоматски прикажи цео опис видеа: ",
+ "preferences_vr_mode_label": "Интерактивни видео клипови у 360 степени: ",
+ "preferences_category_visual": "Визуелне преференце",
+ "preferences_captions_label": "Подразумевани титл: ",
+ "Music": "Музика",
+ "content_type": "Тип",
+ "Broken? Try another Invidious Instance": "Не функционише исправно? Пробајте другу Invidious инстанцу",
+ "Tamil": "Тамилски",
+ "Save preferences": "Сачувај подешавања",
+ "Only show latest unwatched video from channel: ": "Прикажи само последње видео клипове који нису погледани са канала: ",
+ "Xhosa": "Коса (Језик)",
+ "channel": "Канал",
+ "Hungarian": "Мађарски",
+ "Maori": "Маори (Језик)",
+ "Manage subscriptions": "Управљај претплатама",
+ "Hindi": "Хинди",
+ "`x` ago": "пре `x`",
+ "Import/export data": "Увези/Извези податке",
+ "`x` uploaded a video": "`x` је отпремио/ла видео клип",
+ "View `x` replies": {
+ "([^.,0-9]|^)1([^.,0-9]|$)": "Погледај `x` одоговор",
+ "": "Погледај `x` одоговора"
+ },
+ "Delete account": "Обриши налог",
+ "preferences_default_home_label": "Подразумевана почетна страница: ",
+ "Serbian": "Српски",
+ "License: ": "Лиценца: ",
+ "live": "Уживо",
+ "Report statistics: ": "Извештавај о статистици: ",
+ "Only show latest video from channel: ": "Приказуј последње видео клипове само са канала: ",
+ "channel name - reverse": "име канала - обрнуто",
+ "Could not get channel info.": "Узимање података о каналу није успело.",
+ "View privacy policy.": "Погледај политику приватности.",
+ "Change password": "Промени лозинку",
+ "Malayalam": "Малајалам",
+ "View more comments on Reddit": "Прикажи више коментара на Reddit-у",
+ "Portuguese": "Португалски",
+ "View YouTube comments": "Прикажи YouTube коментаре",
+ "published - reverse": "објављено - обрнуто",
+ "Dutch": "Белгијски",
+ "preferences_volume_label": "Јачина звука: ",
+ "preferences_locale_label": "Језик: ",
+ "`x` subscriptions": {
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` пратиоц",
+ "": "`x` пратилаца"
+ },
+ "adminprefs_modified_source_code_url_label": "URL веза до репозиторијума са измењеним изворним кодом",
+ "Community": "Заједница",
+ "Video mode": "Видео мод",
+ "Fallback captions: ": "Титл у случају да главни није доступан: ",
+ "Private": "Приватно",
+ "alphabetically": "по алфабету",
+ "No such user": "Непостојећи корисник",
+ "Subscriptions": "Праћења",
+ "today": "Данас",
+ "Finnish": "Фински",
+ "Lao": "Лаоски",
+ "Login enabled: ": "Пријава омогућена: ",
+ "Shona": "Шона",
+ "location": "Локација",
+ "Load more": "Учитај више",
+ "Released under the AGPLv3 on Github.": "Избачено под лиценцом AGPLv3 на Github-у.",
+ "Slovenian": "Словеначки",
+ "View JavaScript license information.": "Погледај информације лиценце везане за JavaScript.",
+ "Chinese (Simplified)": "Кинески (Поједностављени)",
+ "preferences_comments_label": "Подразумевани коментари: ",
+ "Incorrect password": "Нетачна лозинка",
+ "Show replies": "Прикажи одговоре",
+ "Invidious Private Feed for `x`": "Invidious приватни Feed за `x`",
+ "Watch on YouTube": "Гледај на YouTube-у",
+ "Wrong answer": "Погрешан одговор",
+ "preferences_quality_label": "Преферирани видео квалитет: ",
+ "Hide replies": "Сакриј одговоре",
+ "Invalid TFA code": "Неважећи TFA код",
+ "Login failed. This may be because two-factor authentication is not turned on for your account.": "Неуспешна пријава! Ово се можда дешава јер двофакторна аутентикација није омогућена на Вашем налогу.",
+ "Erroneous CAPTCHA": "Погрешна CAPTCHA",
+ "Erroneous token": "Погрешан токен",
+ "Czech": "Чешки",
+ "Latin": "Латински",
+ "Videos": "Видео клипови",
+ "4k": "4К",
+ "footer_donate_page": "Донирај",
+ "English": "Енглески",
+ "Arabic": "Арапски",
+ "Unlisted": "Ненаведено",
+ "Hidden field \"challenge\" is a required field": "Сакривено \"challenge\" поље је обавезно",
+ "Hidden field \"token\" is a required field": "Сакривено \"token\" поље је обавезно",
+ "Georgian": "Грузијски",
+ "Hawaiian": "Хавајски",
+ "Hebrew": "Хебрејски",
+ "Icelandic": "Исландски",
+ "Igbo": "Игбо",
+ "Japanese": "Јапански",
+ "Javanese": "Јавански",
+ "Sindhi": "Синди",
+ "Swahili": "Свахили",
+ "Yiddish": "Јидиш",
+ "Zulu": "Зулу",
+ "subtitles": "Титл",
+ "Password cannot be longer than 55 characters": "Лозинка не може бити дужа од 55 карактера",
+ "This channel does not exist.": "Овај канал не постоји.",
+ "Belarusian": "Белоруски",
+ "Gujarati": "Гуџарати",
+ "Haitian Creole": "Хаићански Креолски",
+ "Somali": "Сомалијски",
+ "Top": "Врх",
+ "footer_modfied_source_code": "Измењени изворни код",
+ "preferences_category_subscription": "Подешавања праћења",
+ "preferences_annotations_subscribed_label": "Подразумевано приказати напомене за канале које пратите? ",
+ "preferences_max_results_label": "Број видео клипова приказаних у листи (feed-у): ",
+ "preferences_sort_label": "Сортирај видео клипове по: ",
+ "preferences_unseen_only_label": "Прикажи само видео клипове који нису погледани: ",
+ "preferences_notifications_only_label": "Прикажи само обавештења (ако их уопште има): ",
+ "preferences_category_data": "Подешавања података",
+ "Clear watch history": "Обриши историју гледања",
+ "preferences_category_admin": "Администраторска подешавања",
+ "published": "објављено",
+ "sort": "Сортирај према",
+ "show": "Шоу",
+ "short": "Кратак (< 4 минута)",
+ "Current version: ": "Тренутна верзија: ",
+ "Top enabled: ": "Врх омогућен: ",
+ "Public": "Јавно",
+ "Delete playlist": "Обриши плеј листу",
+ "Title": "Наслов",
+ "Show annotations": "Прикажи напомене",
+ "Password cannot be empty": "Лозинка не може бити празна",
+ "Deleted or invalid channel": "Обрисан или непостојећи канал",
+ "Esperanto": "Есперанто",
+ "Hmong": "Хмонг",
+ "Luxembourgish": "Луксембуршки",
+ "Nepali": "Непалски",
+ "Samoan": "Самоански",
+ "News": "Вести",
+ "permalink": "трајна веза",
+ "Password is a required field": "Лозинка је обавезно поље",
+ "Amharic": "Амхарски",
+ "Indonesian": "Индонежански",
+ "Irish": "Ирски",
+ "Korean": "Корејски",
+ "Southern Sotho": "Сото",
+ "Thai": "Тајски",
+ "`x` months": {
+ "([^.,0-9]|^)1([^.,0-9]|$)": "`x` месец",
+ "": "`x` месеци"
+ },
+ "preferences_speed_label": "Подразумевана брзина: ",
+ "Dark mode: ": "Тамни режим: ",
+ "dark": "тамна",
+ "Redirect homepage to feed: ": "Пребаци са почетне странице на листу (feed): ",
+ "channel name": "име канала",
+ "View all playlists": "Прегледај све плеј листе",
+ "Show more": "Прикажи више",
+ "Genre: ": "Жанр: ",
+ "Family friendly? ": "Погодно за породицу? ",
+ "next_steps_error_message_refresh": "Освежи страницу",
+ "youtube": "YouTube",
+ "reddit": "Reddit",
+ "unsubscribe": "прекини са праћењем",
+ "Blacklisted regions: ": "Блокирани региони: ",
+ "Polish": "Пољски",
+ "Yoruba": "Јоруба"
}
diff --git a/spec/helpers_spec.cr b/spec/helpers_spec.cr
index 002c8bdd..4215b2bd 100644
--- a/spec/helpers_spec.cr
+++ b/spec/helpers_spec.cr
@@ -77,16 +77,6 @@ describe "Helper" do
end
end
- describe "#produce_comment_reply_continuation" do
- it "correctly produces a continuation token for replies to a given comment" do
- produce_comment_reply_continuation("cIHQWOoJeag", "UCq6VFHwMzcMXbuKyG7SQYIg", "Ugx1IP_wGVv3WtGWcdV4AaABAg").should eq("EiYSC2NJSFFXT29KZWFnwAEByAEB4AEBogINKP___________wFAABgGMk0aSxIaVWd4MUlQX3dHVnYzV3RHV2NkVjRBYUFCQWciAggAKhhVQ3E2VkZId016Y01YYnVLeUc3U1FZSWcyC2NJSFFXT29KZWFnQAFICg%3D%3D")
-
- produce_comment_reply_continuation("cIHQWOoJeag", "UCq6VFHwMzcMXbuKyG7SQYIg", "Ugza62y_TlmTu9o2RfF4AaABAg").should eq("EiYSC2NJSFFXT29KZWFnwAEByAEB4AEBogINKP___________wFAABgGMk0aSxIaVWd6YTYyeV9UbG1UdTlvMlJmRjRBYUFCQWciAggAKhhVQ3E2VkZId016Y01YYnVLeUc3U1FZSWcyC2NJSFFXT29KZWFnQAFICg%3D%3D")
-
- produce_comment_reply_continuation("_cE8xSu6swE", "UC1AZY74-dGVPe6bfxFwwEMg", "UgyBUaRGHB9Jmt1dsUZ4AaABAg").should eq("EiYSC19jRTh4U3U2c3dFwAEByAEB4AEBogINKP___________wFAABgGMk0aSxIaVWd5QlVhUkdIQjlKbXQxZHNVWjRBYUFCQWciAggAKhhVQzFBWlk3NC1kR1ZQZTZiZnhGd3dFTWcyC19jRTh4U3U2c3dFQAFICg%3D%3D")
- end
- end
-
describe "#produce_channel_community_continuation" do
it "correctly produces a continuation token for a channel community" do
produce_channel_community_continuation("UCCj956IF62FbT7Gouszaj9w", "Egljb21tdW5pdHm4").should eq("4qmFsgIsEhhVQ0NqOTU2SUY2MkZiVDdHb3VzemFqOXcaEEVnbGpiMjF0ZFc1cGRIbTQ%3D")
@@ -107,15 +97,6 @@ describe "Helper" do
end
end
- describe "#extract_plid" do
- it "correctly extracts playlist ID from trending URL" do
- extract_plid("/feed/trending?bp=4gIuCggvbS8wNHJsZhIiUExGZ3F1TG5MNTlhbVBud2pLbmNhZUp3MDYzZlU1M3Q0cA%3D%3D").should eq("PLFgquLnL59amPnwjKncaeJw063fU53t4p")
- extract_plid("/feed/trending?bp=4gIvCgkvbS8wYnp2bTISIlBMaUN2Vkp6QnVwS2tDaFNnUDdGWFhDclo2aEp4NmtlTm0%3D").should eq("PLiCvVJzBupKkChSgP7FXXCrZ6hJx6keNm")
- extract_plid("/feed/trending?bp=4gIuCggvbS8wNWpoZxIiUEwzWlE1Q3BOdWxRbUtPUDNJekdsYWN0V1c4dklYX0hFUA%3D%3D").should eq("PL3ZQ5CpNulQmKOP3IzGlactWW8vIX_HEP")
- extract_plid("/feed/trending?bp=4gIuCggvbS8wMnZ4bhIiUEx6akZiYUZ6c21NUnFhdEJnVTdPeGNGTkZhQ2hqTkVERA%3D%3D").should eq("PLzjFbaFzsmMRqatBgU7OxcFNFaChjNEDD")
- end
- end
-
describe "#sign_token" do
it "correctly signs a given hash" do
token = {
diff --git a/src/invidious/comments.cr b/src/invidious/comments.cr
index ffdce000..12a80bc4 100644
--- a/src/invidious/comments.cr
+++ b/src/invidious/comments.cr
@@ -60,8 +60,6 @@ def fetch_youtube_comments(id, cursor, format, locale, thin_mode, region, sort_b
case cursor
when nil, ""
ctoken = produce_comment_continuation(id, cursor: "", sort_by: sort_by)
- # when .starts_with? "Ug"
- # ctoken = produce_comment_reply_continuation(id, video.ucid, cursor)
when .starts_with? "ADSJ"
ctoken = produce_comment_continuation(id, cursor: cursor, sort_by: sort_by)
else
@@ -575,7 +573,9 @@ def content_to_comment_html(content)
url = "/watch?v=#{url.request_target.lstrip('/')}"
elsif url.host.nil? || url.host.not_nil!.ends_with?("youtube.com")
if url.path == "/redirect"
- url = HTTP::Params.parse(url.query.not_nil!)["q"]
+ # Sometimes, links can be corrupted (why?) so make sure to fallback
+ # nicely. See https://github.com/iv-org/invidious/issues/2682
+ url = HTTP::Params.parse(url.query.not_nil!)["q"]? || ""
else
url = url.request_target
end
@@ -645,38 +645,3 @@ def produce_comment_continuation(video_id, cursor = "", sort_by = "top")
return continuation
end
-
-def produce_comment_reply_continuation(video_id, ucid, comment_id)
- object = {
- "2:embedded" => {
- "2:string" => video_id,
- "24:varint" => 1_i64,
- "25:varint" => 1_i64,
- "28:varint" => 1_i64,
- "36:embedded" => {
- "5:varint" => -1_i64,
- "8:varint" => 0_i64,
- },
- },
- "3:varint" => 6_i64,
- "6:embedded" => {
- "3:embedded" => {
- "2:string" => comment_id,
- "4:embedded" => {
- "1:varint" => 0_i64,
- },
- "5:string" => ucid,
- "6:string" => video_id,
- "8:varint" => 1_i64,
- "9:varint" => 10_i64,
- },
- },
- }
-
- continuation = object.try { |i| Protodec::Any.cast_json(i) }
- .try { |i| Protodec::Any.from_json(i) }
- .try { |i| Base64.urlsafe_encode(i) }
- .try { |i| URI.encode_www_form(i) }
-
- return continuation
-end
diff --git a/src/invidious/routes/video_playback.cr b/src/invidious/routes/video_playback.cr
index 5c64f669..394b1592 100644
--- a/src/invidious/routes/video_playback.cr
+++ b/src/invidious/routes/video_playback.cr
@@ -240,7 +240,7 @@ module Invidious::Routes::VideoPlayback
download_widget = JSON.parse(env.params.query["download_widget"])
id = download_widget["id"].as_s
- title = download_widget["title"].as_s
+ title = URI.decode_www_form(download_widget["title"].as_s)
if label = download_widget["label"]?
return env.redirect "/api/v1/captions/#{id}?label=#{label}&title=#{title}"
diff --git a/src/invidious/trending.cr b/src/invidious/trending.cr
index 25bab4d2..1f957081 100644
--- a/src/invidious/trending.cr
+++ b/src/invidious/trending.cr
@@ -20,13 +20,3 @@ def fetch_trending(trending_type, region, locale)
return {trending, plid}
end
-
-def extract_plid(url)
- return url.try { |i| URI.parse(i).query }
- .try { |i| HTTP::Params.parse(i)["bp"] }
- .try { |i| URI.decode_www_form(i) }
- .try { |i| Base64.decode(i) }
- .try { |i| IO::Memory.new(i) }
- .try { |i| Protodec::Any.parse(i) }
- .try &.["44:0:embedded"]?.try &.["2:1:string"]?.try &.as_s
-end
diff --git a/src/invidious/videos.cr b/src/invidious/videos.cr
index e20fb386..00fea49f 100644
--- a/src/invidious/videos.cr
+++ b/src/invidious/videos.cr
@@ -857,8 +857,16 @@ def extract_video_info(video_id : String, proxy_region : String? = nil, context_
else
client_config.client_type = YoutubeAPI::ClientType::Android
end
- stream_data = YoutubeAPI.player(video_id: video_id, params: "", client_config: client_config)
- params["streamingData"] = stream_data["streamingData"]? || JSON::Any.new("")
+ android_player = YoutubeAPI.player(video_id: video_id, params: "", client_config: client_config)
+
+ # Sometime, the video is available from the web client, but not on Android, so check
+ # that here, and fallback to the streaming data from the web client if needed.
+ # See: https://github.com/iv-org/invidious/issues/2549
+ if android_player["playabilityStatus"]["status"] == "OK"
+ params["streamingData"] = android_player["streamingData"]? || JSON::Any.new("")
+ else
+ params["streamingData"] = player_response["streamingData"]? || JSON::Any.new("")
+ end
end
{"captions", "microformat", "playabilityStatus", "storyboards", "videoDetails"}.each do |f|