mirror of
https://github.com/iv-org/invidious.git
synced 2024-11-22 05:27:21 +00:00
Merge pull request #2956 from SamantazFox/search-filters
Overhaul search filters
This commit is contained in:
commit
4900ce24fa
@ -77,10 +77,6 @@ Metrics/CyclomaticComplexity:
|
||||
# process_video_params(query, preferences) => [20/10]
|
||||
- src/invidious/videos.cr
|
||||
|
||||
# produce_search_params(page, sort, ...) => [29/10]
|
||||
# process_search_query(query, page, ...) => [14/10]
|
||||
- src/invidious/search.cr
|
||||
|
||||
|
||||
|
||||
#src/invidious/playlists.cr:327:5
|
||||
|
@ -15,6 +15,11 @@ body {
|
||||
background-color: rgb(255, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.underlined {
|
||||
border-bottom: 1px solid;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.channel-profile > * {
|
||||
font-size: 1.17em;
|
||||
font-weight: bold;
|
||||
@ -475,30 +480,6 @@ body.dark-theme {
|
||||
}
|
||||
}
|
||||
|
||||
#filters {
|
||||
display: inline;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#filters > div {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#filters > summary {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#filters > summary::before {
|
||||
content: "[ + ]";
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
#filters[open] > summary::before {
|
||||
content: "[ - ]";
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
/*With commit d9528f5 all contents of the page is now within a flexbox. However,
|
||||
the hr element is rendered improperly within one.
|
||||
See https://stackoverflow.com/a/34372979 for more info */
|
||||
|
117
assets/css/search.css
Normal file
117
assets/css/search.css
Normal file
@ -0,0 +1,117 @@
|
||||
summary {
|
||||
/* This should hide the marker */
|
||||
display: block;
|
||||
|
||||
font-size: 1.17em;
|
||||
font-weight: bold;
|
||||
margin: 0 auto 10px auto;
|
||||
}
|
||||
|
||||
summary::-webkit-details-marker,
|
||||
summary::marker { display: none; }
|
||||
|
||||
summary:before {
|
||||
border-radius: 5px;
|
||||
content: "[ + ]";
|
||||
margin: -2px 10px 0 10px;
|
||||
padding: 1px 0 3px 0;
|
||||
text-align: center;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
details[open] > summary:before { content: "[ ‒ ]"; }
|
||||
|
||||
|
||||
#filters-box {
|
||||
padding: 10px 20px 20px 10px;
|
||||
margin: 10px 15px;
|
||||
}
|
||||
#filters-flex {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
align-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
|
||||
fieldset, legend {
|
||||
display: contents !important;
|
||||
border: none !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
.filter-column {
|
||||
display: inline-block;
|
||||
display: inline-flex;
|
||||
width: max-content;
|
||||
min-width: max-content;
|
||||
max-width: 16em;
|
||||
margin: 15px;
|
||||
flex-grow: 2;
|
||||
flex-basis: auto;
|
||||
flex-direction: column;
|
||||
}
|
||||
.filter-name, .filter-options {
|
||||
display: block;
|
||||
padding: 5px 10px;
|
||||
margin: 0;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.filter-options div { margin: 6px 0; }
|
||||
.filter-options div * { vertical-align: middle; }
|
||||
.filter-options label { margin: 0 10px; }
|
||||
|
||||
|
||||
#filters-apply { text-align: end; }
|
||||
|
||||
/* Error message */
|
||||
|
||||
.no-results-error {
|
||||
text-align: center;
|
||||
line-height: 180%;
|
||||
font-size: 110%;
|
||||
padding: 15px 15px 125px 15px;
|
||||
}
|
||||
|
||||
/* Responsive rules */
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
summary { font-size: 1.30em; }
|
||||
#filters-box {
|
||||
margin: 10px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
#filters-apply {
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Light theme */
|
||||
|
||||
.light-theme #filters-box {
|
||||
background: #dfdfdf;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.no-theme #filters-box {
|
||||
background: #dfdfdf;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark theme */
|
||||
|
||||
.dark-theme #filters-box {
|
||||
background: #373737;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.no-theme #filters-box {
|
||||
background: #373737;
|
||||
}
|
||||
}
|
@ -329,39 +329,39 @@
|
||||
"Videos": "الفيديوهات",
|
||||
"Playlists": "قوائم التشغيل",
|
||||
"Community": "المجتمع",
|
||||
"relevance": "ملاؤم",
|
||||
"rating": "تقييم",
|
||||
"date": "التاريخ",
|
||||
"views": "مشاهدات",
|
||||
"content_type": "نوع المحتوى",
|
||||
"duration": "المدة الزمنية",
|
||||
"features": "الميزات",
|
||||
"sort": "فرز",
|
||||
"hour": "آخر ساعة",
|
||||
"today": "اليوم",
|
||||
"week": "هذا الأسبوع",
|
||||
"month": "هذا الشهر",
|
||||
"year": "هذه السنة",
|
||||
"video": "فيديو",
|
||||
"channel": "قناة",
|
||||
"playlist": "قائمة التشغيل",
|
||||
"movie": "فيلم",
|
||||
"show": "عرض",
|
||||
"hd": "عالية الدقة",
|
||||
"subtitles": "ترجمات",
|
||||
"creative_commons": "المشاع الإبداعي",
|
||||
"3d": "ثلاثي الأبعاد",
|
||||
"live": "مباشر",
|
||||
"4k": "4k",
|
||||
"location": "الأماكن",
|
||||
"hdr": "وضع التباين العالي",
|
||||
"filter": "معامل الفرز",
|
||||
"search_filters_sort_option_relevance": "ملاؤم",
|
||||
"search_filters_sort_option_rating": "تقييم",
|
||||
"search_filters_sort_option_date": "التاريخ",
|
||||
"search_filters_sort_option_views": "مشاهدات",
|
||||
"search_filters_type_label": "نوع المحتوى",
|
||||
"search_filters_duration_label": "المدة الزمنية",
|
||||
"search_filters_features_label": "الميزات",
|
||||
"search_filters_sort_label": "فرز",
|
||||
"search_filters_date_option_hour": "آخر ساعة",
|
||||
"search_filters_date_option_today": "اليوم",
|
||||
"search_filters_date_option_week": "هذا الأسبوع",
|
||||
"search_filters_date_option_month": "هذا الشهر",
|
||||
"search_filters_date_option_year": "هذه السنة",
|
||||
"search_filters_type_option_video": "فيديو",
|
||||
"search_filters_type_option_channel": "قناة",
|
||||
"search_filters_type_option_playlist": "قائمة التشغيل",
|
||||
"search_filters_type_option_movie": "فيلم",
|
||||
"search_filters_type_option_show": "عرض",
|
||||
"search_filters_features_option_hd": "عالية الدقة",
|
||||
"search_filters_features_option_subtitles": "ترجمات",
|
||||
"search_filters_features_option_c_commons": "المشاع الإبداعي",
|
||||
"search_filters_features_option_three_d": "ثلاثي الأبعاد",
|
||||
"search_filters_features_option_live": "مباشر",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "الأماكن",
|
||||
"search_filters_features_option_hdr": "وضع التباين العالي",
|
||||
"search_filters_label": "معامل الفرز",
|
||||
"Current version: ": "الإصدار الحالي: ",
|
||||
"next_steps_error_message": "بعد ذلك يجب أن تحاول: ",
|
||||
"next_steps_error_message_refresh": "تحديث",
|
||||
"next_steps_error_message_go_to_youtube": "انتقل إلى يوتيوب",
|
||||
"short": "قصير (< 4 دقائق)",
|
||||
"long": "طويل (> 20 دقيقة)",
|
||||
"search_filters_duration_option_short": "قصير (< 4 دقائق)",
|
||||
"search_filters_duration_option_long": "طويل (> 20 دقيقة)",
|
||||
"footer_source_code": "شفرة المصدر",
|
||||
"footer_original_source_code": "كود المصدر الأصلي",
|
||||
"footer_modfied_source_code": "شفرة المصدر المعدلة",
|
||||
@ -386,7 +386,7 @@
|
||||
"preferences_quality_dash_option_360p": "360p",
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"purchased": "تم شراؤها",
|
||||
"search_filters_features_option_purchased": "تم شراؤها",
|
||||
"none": "لاشيء",
|
||||
"videoinfo_started_streaming_x_ago": "بدأ البث منذ `x`",
|
||||
"videoinfo_watch_on_youTube": "مشاهدة على يوتيوب",
|
||||
@ -395,7 +395,7 @@
|
||||
"user_created_playlists": "'x' إنشاء قوائم التشغيل",
|
||||
"user_saved_playlists": "قوائم التشغيل المحفوظة 'x'",
|
||||
"Video unavailable": "الفيديو غير متوفر",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"download_subtitles": "ترجمات - 'x' (.vtt)",
|
||||
"invidious": "الخيالي",
|
||||
"preferences_save_player_pos_label": "حفظ موضع التشغيل: ",
|
||||
|
@ -52,16 +52,16 @@
|
||||
"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)",
|
||||
"search_filters_type_label": "Tipus",
|
||||
"search_filters_duration_label": "Duració",
|
||||
"search_filters_sort_label": "Ordena per",
|
||||
"search_filters_date_option_week": "Aquesta setmana",
|
||||
"search_filters_date_option_month": "Aquest mes",
|
||||
"search_filters_date_option_year": "Aquest any",
|
||||
"search_filters_type_option_video": "Vídeo",
|
||||
"search_filters_type_option_channel": "Canal",
|
||||
"search_filters_duration_option_short": "Curt (< 4 minuts)",
|
||||
"search_filters_duration_option_long": "Llarg (> 20 minuts)",
|
||||
"Current version: ": "Versió actual: ",
|
||||
"Malay": "Malai",
|
||||
"Persian": "Persa",
|
||||
@ -93,11 +93,11 @@
|
||||
"Spanish": "Castellà",
|
||||
"Vietnamese": "Vietnamita",
|
||||
"News": "Notícies",
|
||||
"show": "Mostra",
|
||||
"search_filters_type_option_show": "Mostra",
|
||||
"footer_documentation": "Documentació",
|
||||
"Thai": "Tailandès",
|
||||
"Music": "Música",
|
||||
"relevance": "Rellevància",
|
||||
"hour": "Última hora",
|
||||
"today": "Avui"
|
||||
"search_filters_sort_option_relevance": "Rellevància",
|
||||
"search_filters_date_option_hour": "Última hora",
|
||||
"search_filters_date_option_today": "Avui"
|
||||
}
|
||||
|
@ -262,29 +262,29 @@
|
||||
"Video mode": "Videový režim",
|
||||
"Videos": "Videa",
|
||||
"Community": "Komunita",
|
||||
"rating": "Hodnocení",
|
||||
"date": "Datum zveřejnění",
|
||||
"views": "Počet zhlédnutí",
|
||||
"duration": "Délka",
|
||||
"hour": "Před hodinou",
|
||||
"today": "Dnes",
|
||||
"week": "Tento týden",
|
||||
"month": "Tento měsíc",
|
||||
"year": "Tento rok",
|
||||
"video": "Video",
|
||||
"channel": "Kanál",
|
||||
"playlist": "Playlist",
|
||||
"movie": "Film",
|
||||
"show": "Show",
|
||||
"hd": "HD",
|
||||
"subtitles": "Titulky",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "Živě",
|
||||
"4k": "4K",
|
||||
"location": "Umístění",
|
||||
"hdr": "HDR",
|
||||
"filter": "Filtr",
|
||||
"search_filters_sort_option_rating": "hodnocení",
|
||||
"search_filters_sort_option_date": "datum",
|
||||
"search_filters_sort_option_views": "zhlédnutí",
|
||||
"search_filters_duration_label": "délka",
|
||||
"search_filters_date_option_hour": "hodina",
|
||||
"search_filters_date_option_today": "dnes",
|
||||
"search_filters_date_option_week": "týden",
|
||||
"search_filters_date_option_month": "měsíc",
|
||||
"search_filters_date_option_year": "rok",
|
||||
"search_filters_type_option_video": "video",
|
||||
"search_filters_type_option_channel": "kanál",
|
||||
"search_filters_type_option_playlist": "playlist",
|
||||
"search_filters_type_option_movie": "film",
|
||||
"search_filters_type_option_show": "zobrazit",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "titulky",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "živě",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "umístění",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "Filtr",
|
||||
"generic_count_days_0": "{{count}} dnem",
|
||||
"generic_count_days_1": "{{count}} dny",
|
||||
"generic_count_days_2": "{{count}} dny",
|
||||
|
@ -202,7 +202,7 @@
|
||||
"Hidden field \"challenge\" is a required field": "Det skjulte felt \"challenge\" er et påkrævet felt",
|
||||
"Albanian": "Albansk",
|
||||
"preferences_quality_dash_label": "Fortrukket DASH video kvalitet: ",
|
||||
"live": "Direkte",
|
||||
"search_filters_features_option_live": "Direkte",
|
||||
"Lao": "Lao-tse",
|
||||
"Filipino": "Filippinsk",
|
||||
"Greek": "Græsk",
|
||||
@ -213,23 +213,23 @@
|
||||
"preferences_locale_label": "Sprog: ",
|
||||
"News": "Nyheder",
|
||||
"permalink": "permalink",
|
||||
"date": "Upload dato",
|
||||
"features": "Funktioner",
|
||||
"filter": "Filter",
|
||||
"search_filters_sort_option_date": "Upload dato",
|
||||
"search_filters_features_label": "Funktioner",
|
||||
"search_filters_label": "Filter",
|
||||
"Khmer": "Khmer",
|
||||
"Finnish": "Finsk",
|
||||
"week": "Denne uge",
|
||||
"search_filters_date_option_week": "Denne uge",
|
||||
"Korean": "Koreansk",
|
||||
"Telugu": "Telugu",
|
||||
"Malayalam": "Malayalam",
|
||||
"View as playlist": "Se som spilleliste",
|
||||
"Hungarian": "Ungarsk",
|
||||
"Welsh": "Walisisk",
|
||||
"subtitles": "Undertekster/CC",
|
||||
"search_filters_features_option_subtitles": "Undertekster/CC",
|
||||
"Bosnian": "Bosnisk",
|
||||
"Yiddish": "Jiddisch",
|
||||
"Belarusian": "Belarussisk",
|
||||
"today": "I dag",
|
||||
"search_filters_date_option_today": "I dag",
|
||||
"Shona": "Shona",
|
||||
"Slovenian": "Slovensk",
|
||||
"Gaming": "Gaming",
|
||||
@ -248,33 +248,33 @@
|
||||
"footer_modfied_source_code": "Modificeret Kildekode",
|
||||
"Released under the AGPLv3 on Github.": "Udgivet under AGPLv3 på Github.",
|
||||
"Tajik": "Tadsjikisk",
|
||||
"month": "Denne måned",
|
||||
"search_filters_date_option_month": "Denne måned",
|
||||
"Hebrew": "Hebraisk",
|
||||
"Kannada": "Kannada",
|
||||
"Current version: ": "Nuværende version: ",
|
||||
"Amharic": "Amharisk",
|
||||
"Swedish": "Svensk",
|
||||
"Corsican": "Korsikansk",
|
||||
"movie": "Film",
|
||||
"search_filters_type_option_movie": "Film",
|
||||
"Could not pull trending pages.": "Kunne ikke hente trending sider.",
|
||||
"English": "Engelsk",
|
||||
"hd": "HD",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"Hausa": "Islandsk",
|
||||
"year": "Dette år",
|
||||
"search_filters_date_option_year": "Dette år",
|
||||
"Japanese": "Japansk",
|
||||
"content_type": "Type",
|
||||
"search_filters_type_label": "Type",
|
||||
"Icelandic": "Islandsk",
|
||||
"Basque": "Baskisk",
|
||||
"rating": "Bedømmelse",
|
||||
"search_filters_sort_option_rating": "Bedømmelse",
|
||||
"Yoruba": "Yoruba",
|
||||
"Erroneous token": "Fejlagtig token",
|
||||
"Videos": "Videoer",
|
||||
"show": "Vis",
|
||||
"search_filters_type_option_show": "Vis",
|
||||
"Luxembourgish": "Luxemboursk",
|
||||
"Vietnamese": "Vietnamesisk",
|
||||
"Latvian": "Lettisk",
|
||||
"Indonesian": "Indonesisk",
|
||||
"duration": "Varighed",
|
||||
"search_filters_duration_label": "Varighed",
|
||||
"footer_original_source_code": "Original kildekode",
|
||||
"Search": "Søg",
|
||||
"Serbian": "Serbisk",
|
||||
@ -289,8 +289,8 @@
|
||||
"Rating: ": "Bedømmelse: ",
|
||||
"Movies": "Film",
|
||||
"YouTube comment permalink": "Youtube kommentarer permalink",
|
||||
"location": "Lokation",
|
||||
"hdr": "HDR",
|
||||
"search_filters_features_option_location": "Lokation",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"Cebuano": "Cebuano (Sugbuanon)",
|
||||
"Nyanja": "Nyanja",
|
||||
"Chinese (Simplified)": "Kinesisk (forenklet)",
|
||||
@ -306,11 +306,11 @@
|
||||
"German": "Tysk",
|
||||
"Maori": "Maori",
|
||||
"Slovak": "Slovakisk",
|
||||
"relevance": "Relevans",
|
||||
"hour": "Sidste time",
|
||||
"playlist": "Spilleliste",
|
||||
"long": "Lang (> 20 minutter)",
|
||||
"creative_commons": "Creative Commons",
|
||||
"search_filters_sort_option_relevance": "Relevans",
|
||||
"search_filters_date_option_hour": "Sidste time",
|
||||
"search_filters_type_option_playlist": "Spilleliste",
|
||||
"search_filters_duration_option_long": "Lang (> 20 minutter)",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"Marathi": "Marathi",
|
||||
"Sindhi": "Sindhi",
|
||||
"preferences_category_misc": "Diverse indstillinger",
|
||||
@ -327,8 +327,8 @@
|
||||
"Western Frisian": "Vestfrisisk",
|
||||
"Top": "Top",
|
||||
"Music": "Musik",
|
||||
"views": "Antal visninger",
|
||||
"sort": "Sorter efter",
|
||||
"search_filters_sort_option_views": "Antal visninger",
|
||||
"search_filters_sort_label": "Sorter efter",
|
||||
"Zulu": "Zulu",
|
||||
"Invidious Private Feed for `x`": "Invidious Privat Feed til `x`",
|
||||
"English (auto-generated)": "Engelsk (autogenereret)",
|
||||
@ -359,16 +359,16 @@
|
||||
"Scottish Gaelic": "Skotsk Gælisk",
|
||||
"Default": "Standard",
|
||||
"Video mode": "Videotilstand",
|
||||
"short": "Kort (< 4 minutter)",
|
||||
"search_filters_duration_option_short": "Kort (< 4 minutter)",
|
||||
"Hidden field \"token\" is a required field": "Det skjulte felt \"token\" er et påkrævet felt",
|
||||
"Azerbaijani": "Aserbajdsjansk",
|
||||
"Georgian": "Georgisk",
|
||||
"Italian": "Italiensk",
|
||||
"Audio mode": "Lydtilstand",
|
||||
"video": "Video",
|
||||
"channel": "Kanal",
|
||||
"3d": "3D",
|
||||
"4k": "4K",
|
||||
"search_filters_type_option_video": "Video",
|
||||
"search_filters_type_option_channel": "Kanal",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"Hmong": "Hmong",
|
||||
"preferences_quality_option_medium": "Medium",
|
||||
"preferences_quality_option_small": "Lille",
|
||||
@ -381,8 +381,8 @@
|
||||
"preferences_quality_dash_option_360p": "360p",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"invidious": "Invidious",
|
||||
"purchased": "Købt",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_purchased": "Købt",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"none": "ingen",
|
||||
"videoinfo_started_streaming_x_ago": "Streamen blev startet for `x`siden",
|
||||
"videoinfo_watch_on_youTube": "Se på YouTube",
|
||||
|
@ -329,45 +329,45 @@
|
||||
"Videos": "Videos",
|
||||
"Playlists": "Wiedergabelisten",
|
||||
"Community": "Gemeinschaft",
|
||||
"relevance": "Relevanz",
|
||||
"rating": "Bewertung",
|
||||
"date": "Datum",
|
||||
"views": "Aufrufe",
|
||||
"content_type": "Inhaltstyp",
|
||||
"duration": "Dauer",
|
||||
"features": "Eigenschaften",
|
||||
"sort": "sortieren",
|
||||
"hour": "Letzte Stunde",
|
||||
"today": "Heute",
|
||||
"week": "Diese Woche",
|
||||
"month": "Diesen Monat",
|
||||
"year": "Dieses Jahr",
|
||||
"video": "Video",
|
||||
"channel": "Kanal",
|
||||
"playlist": "Wiedergabeliste",
|
||||
"movie": "Film",
|
||||
"show": "anzeigen",
|
||||
"hd": "HD",
|
||||
"subtitles": "Untertitel / CC",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "Live",
|
||||
"4k": "4K",
|
||||
"location": "Standort",
|
||||
"hdr": "HDR",
|
||||
"filter": "Filtern",
|
||||
"search_filters_sort_option_relevance": "Relevanz",
|
||||
"search_filters_sort_option_rating": "Bewertung",
|
||||
"search_filters_sort_option_date": "Datum",
|
||||
"search_filters_sort_option_views": "Aufrufe",
|
||||
"search_filters_type_label": "Inhaltstyp",
|
||||
"search_filters_duration_label": "Dauer",
|
||||
"search_filters_features_label": "Eigenschaften",
|
||||
"search_filters_sort_label": "sortieren",
|
||||
"search_filters_date_option_hour": "Letzte Stunde",
|
||||
"search_filters_date_option_today": "Heute",
|
||||
"search_filters_date_option_week": "Diese Woche",
|
||||
"search_filters_date_option_month": "Diesen Monat",
|
||||
"search_filters_date_option_year": "Dieses Jahr",
|
||||
"search_filters_type_option_video": "Video",
|
||||
"search_filters_type_option_channel": "Kanal",
|
||||
"search_filters_type_option_playlist": "Wiedergabeliste",
|
||||
"search_filters_type_option_movie": "Film",
|
||||
"search_filters_type_option_show": "Anzeigen",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "Untertitel / CC",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "Live",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "Standort",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "Filtern",
|
||||
"Current version: ": "Aktuelle Version: ",
|
||||
"next_steps_error_message": "Danach folgendes versuchen: ",
|
||||
"next_steps_error_message_refresh": "Aktualisieren",
|
||||
"next_steps_error_message_go_to_youtube": "Zu YouTube gehen",
|
||||
"footer_donate_page": "Spende",
|
||||
"long": "Lang (> 20 Minuten)",
|
||||
"search_filters_duration_option_long": "Lang (> 20 Minuten)",
|
||||
"footer_original_source_code": "Original Quellcode",
|
||||
"footer_modfied_source_code": "Modifizierter Quellcode",
|
||||
"footer_documentation": "Dokumentation",
|
||||
"footer_source_code": "Quellcode",
|
||||
"adminprefs_modified_source_code_url_label": "URL zum Repositorie des modifizierten Quellcodes",
|
||||
"short": "Kurz (< 4 Minuten)",
|
||||
"search_filters_duration_option_short": "Kurz (< 4 Minuten)",
|
||||
"preferences_region_label": "Land der Inhalte: ",
|
||||
"preferences_quality_option_dash": "DASH (automatische Qualität)",
|
||||
"preferences_quality_option_hd720": "HD720",
|
||||
@ -389,12 +389,12 @@
|
||||
"user_created_playlists": "`x` Wiedergabelisten erstellt",
|
||||
"user_saved_playlists": "`x` Wiedergabelisten gespeichert",
|
||||
"preferences_save_player_pos_label": "Aktuelle Position im Video speichern: ",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"preferences_quality_dash_option_best": "Höchste",
|
||||
"preferences_quality_dash_option_worst": "Niedrigste",
|
||||
"preferences_quality_dash_option_1440p": "1440p",
|
||||
"videoinfo_youTube_embed_link": "Eingebettet",
|
||||
"purchased": "Gekauft",
|
||||
"search_filters_features_option_purchased": "Gekauft",
|
||||
"none": "keine",
|
||||
"videoinfo_started_streaming_x_ago": "Stream begann vor `x`",
|
||||
"videoinfo_watch_on_youTube": "Auf YouTube ansehen",
|
||||
|
@ -373,51 +373,51 @@
|
||||
"preferences_region_label": "Χώρα περιεχομένου: ",
|
||||
"preferences_category_misc": "Διάφορες προτιμήσεις",
|
||||
"Show more": "Εμφάνιση περισσότερων",
|
||||
"today": "Σήμερα",
|
||||
"360": "360°",
|
||||
"search_filters_date_option_today": "Σήμερα",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"videoinfo_started_streaming_x_ago": "Ξεκίνησε η ροή `x` πριν από",
|
||||
"videoinfo_watch_on_youTube": "Παρακολουθήστε στο YouTube",
|
||||
"download_subtitles": "Υπότιτλοι - `x` (.vtt)",
|
||||
"user_created_playlists": "`x` δημιουργημένες λίστες αναπαραγωγής",
|
||||
"user_saved_playlists": "`x` αποθηκευμένες λίστες αναπαραγωγής",
|
||||
"rating": "Αξιολόγηση",
|
||||
"relevance": "Συνάφεια",
|
||||
"purchased": "Αγορασμένο",
|
||||
"date": "Ημερομηνία μεταφόρτωσης",
|
||||
"content_type": "Τύπος",
|
||||
"duration": "Διάρκεια",
|
||||
"week": "Αυτή την εβδομάδα",
|
||||
"year": "Φέτος",
|
||||
"channel": "Κανάλι",
|
||||
"playlist": "Λίστα αναπαραγωγής",
|
||||
"long": "Μεγάλο (> 20 λεπτά)",
|
||||
"hd": "HD",
|
||||
"location": "Τοποθεσία",
|
||||
"3d": "3D",
|
||||
"search_filters_sort_option_rating": "Αξιολόγηση",
|
||||
"search_filters_sort_option_relevance": "Συνάφεια",
|
||||
"search_filters_features_option_purchased": "Αγορασμένο",
|
||||
"search_filters_sort_option_date": "Ημερομηνία μεταφόρτωσης",
|
||||
"search_filters_type_label": "Τύπος",
|
||||
"search_filters_duration_label": "Διάρκεια",
|
||||
"search_filters_date_option_week": "Αυτή την εβδομάδα",
|
||||
"search_filters_date_option_year": "Φέτος",
|
||||
"search_filters_type_option_channel": "Κανάλι",
|
||||
"search_filters_type_option_playlist": "Λίστα αναπαραγωγής",
|
||||
"search_filters_duration_option_long": "Μεγάλο (> 20 λεπτά)",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_location": "Τοποθεσία",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"next_steps_error_message": "Μετά από αυτό θα πρέπει να προσπαθήσετε να: ",
|
||||
"next_steps_error_message_go_to_youtube": "Μεταβείτε στο YouTube",
|
||||
"footer_donate_page": "Δωρεά",
|
||||
"footer_original_source_code": "Πρωτότυπος πηγαίος κώδικας",
|
||||
"preferences_show_nick_label": "Εμφάνιση ψευδώνυμου στην κορυφή: ",
|
||||
"hour": "Τελευταία ώρα",
|
||||
"search_filters_date_option_hour": "Τελευταία ώρα",
|
||||
"adminprefs_modified_source_code_url_label": "URL σε αποθετήριο τροποποιημένου πηγαίου κώδικα",
|
||||
"subtitles": "Υπότιτλοι/CC",
|
||||
"month": "Αυτόν τον μήνα",
|
||||
"search_filters_features_option_subtitles": "Υπότιτλοι/CC",
|
||||
"search_filters_date_option_month": "Αυτόν τον μήνα",
|
||||
"Released under the AGPLv3 on Github.": "Κυκλοφορεί υπό την AGPLv3 στο Github.",
|
||||
"sort": "Ταξινόμηση κατά",
|
||||
"filter": "Φίλτρο",
|
||||
"movie": "Ταινία",
|
||||
"search_filters_sort_label": "Ταξινόμηση κατά",
|
||||
"search_filters_label": "Φίλτρο",
|
||||
"search_filters_type_option_movie": "Ταινία",
|
||||
"footer_modfied_source_code": "Τροποποιημένος πηγαίος κώδικας",
|
||||
"features": "Χαρακτηριστικά",
|
||||
"4k": "4K",
|
||||
"search_filters_features_label": "Χαρακτηριστικά",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"footer_documentation": "Τεκμηρίωση",
|
||||
"short": "Σύντομο (< 4 λεπτά)",
|
||||
"search_filters_duration_option_short": "Σύντομο (< 4 λεπτά)",
|
||||
"next_steps_error_message_refresh": "Ανανέωση",
|
||||
"video": "Βίντεο",
|
||||
"live": "Ζωντανά",
|
||||
"creative_commons": "Creative Commons",
|
||||
"search_filters_type_option_video": "Βίντεο",
|
||||
"search_filters_features_option_live": "Ζωντανά",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"Search": "Αναζήτηση",
|
||||
"hdr": "HDR",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"preferences_extend_desc_label": "Αυτόματη επέκταση της περιγραφής του βίντεο: ",
|
||||
"preferences_vr_mode_label": "Διαδραστικά βίντεο 360 μοιρών (απαιτεί WebGL): ",
|
||||
"Show less": "Εμφάνιση λιγότερων",
|
||||
@ -448,6 +448,6 @@
|
||||
"none": "κανένα",
|
||||
"videoinfo_youTube_embed_link": "Ενσωμάτωση",
|
||||
"videoinfo_invidious_embed_link": "Σύνδεσμος Ενσωμάτωσης",
|
||||
"show": "Μπάρα προόδου διαβάσματος",
|
||||
"search_filters_type_option_show": "Μπάρα προόδου διαβάσματος",
|
||||
"preferences_watch_history_label": "Ενεργοποίηση ιστορικού παρακολούθησης: "
|
||||
}
|
||||
|
@ -175,7 +175,9 @@
|
||||
"Show less": "Show less",
|
||||
"Watch on YouTube": "Watch on YouTube",
|
||||
"Switch Invidious Instance": "Switch Invidious Instance",
|
||||
"Broken? Try another Invidious Instance": "Broken? Try another Invidious Instance",
|
||||
"search_message_no_results": "No results found.",
|
||||
"search_message_change_filters_or_query": "Try widening your search query and/or changing the filters.",
|
||||
"search_message_use_another_instance": " You can also <a href=\"`x`\">search on another instance</a>.",
|
||||
"Hide annotations": "Hide annotations",
|
||||
"Show annotations": "Show annotations",
|
||||
"Genre: ": "Genre: ",
|
||||
@ -404,37 +406,44 @@
|
||||
"Videos": "Videos",
|
||||
"Playlists": "Playlists",
|
||||
"Community": "Community",
|
||||
"relevance": "Relevance",
|
||||
"rating": "Rating",
|
||||
"date": "Upload date",
|
||||
"views": "View count",
|
||||
"content_type": "Type",
|
||||
"duration": "Duration",
|
||||
"features": "Features",
|
||||
"sort": "Sort By",
|
||||
"hour": "Last Hour",
|
||||
"today": "Today",
|
||||
"week": "This week",
|
||||
"month": "This month",
|
||||
"year": "This year",
|
||||
"video": "Video",
|
||||
"channel": "Channel",
|
||||
"playlist": "Playlist",
|
||||
"movie": "Movie",
|
||||
"show": "Show",
|
||||
"short": "Short (< 4 minutes)",
|
||||
"long": "Long (> 20 minutes)",
|
||||
"hd": "HD",
|
||||
"subtitles": "Subtitles/CC",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "Live",
|
||||
"4k": "4K",
|
||||
"location": "Location",
|
||||
"hdr": "HDR",
|
||||
"purchased": "Purchased",
|
||||
"360": "360°",
|
||||
"filter": "Filter",
|
||||
"search_filters_title": "Filters",
|
||||
"search_filters_date_label": "Upload date",
|
||||
"search_filters_date_option_none": "Any date",
|
||||
"search_filters_date_option_hour": "Last Hour",
|
||||
"search_filters_date_option_today": "Today",
|
||||
"search_filters_date_option_week": "This week",
|
||||
"search_filters_date_option_month": "This month",
|
||||
"search_filters_date_option_year": "This year",
|
||||
"search_filters_type_label": "Type",
|
||||
"search_filters_type_option_all": "Any type",
|
||||
"search_filters_type_option_video": "Video",
|
||||
"search_filters_type_option_channel": "Channel",
|
||||
"search_filters_type_option_playlist": "Playlist",
|
||||
"search_filters_type_option_movie": "Movie",
|
||||
"search_filters_type_option_show": "Show",
|
||||
"search_filters_duration_label": "Duration",
|
||||
"search_filters_duration_option_none": "Any duration",
|
||||
"search_filters_duration_option_short": "Short (< 4 minutes)",
|
||||
"search_filters_duration_option_medium": "Medium (4 - 20 minutes)",
|
||||
"search_filters_duration_option_long": "Long (> 20 minutes)",
|
||||
"search_filters_features_label": "Features",
|
||||
"search_filters_features_option_live": "Live",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "Subtitles/CC",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"search_filters_features_option_vr180": "VR180",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_features_option_location": "Location",
|
||||
"search_filters_features_option_purchased": "Purchased",
|
||||
"search_filters_sort_label": "Sort By",
|
||||
"search_filters_sort_option_relevance": "Relevance",
|
||||
"search_filters_sort_option_rating": "Rating",
|
||||
"search_filters_sort_option_date": "Upload Date",
|
||||
"search_filters_sort_option_views": "View count",
|
||||
"search_filters_apply_button": "Apply selected filters",
|
||||
"Current version: ": "Current version: ",
|
||||
"next_steps_error_message": "After which you should try to: ",
|
||||
"next_steps_error_message_refresh": "Refresh",
|
||||
|
@ -329,39 +329,39 @@
|
||||
"Videos": "Filmetoj",
|
||||
"Playlists": "Ludlistoj",
|
||||
"Community": "Komunumo",
|
||||
"relevance": "rilateco",
|
||||
"rating": "takso",
|
||||
"date": "dato",
|
||||
"views": "vidoj",
|
||||
"content_type": "enhavtipo",
|
||||
"duration": "daŭro",
|
||||
"features": "trajtoj",
|
||||
"sort": "ordigi",
|
||||
"hour": "horo",
|
||||
"today": "hodiaŭ",
|
||||
"week": "semajno",
|
||||
"month": "monato",
|
||||
"year": "jaro",
|
||||
"video": "filmeto",
|
||||
"channel": "kanalo",
|
||||
"playlist": "ludlisto",
|
||||
"movie": "filmo",
|
||||
"show": "spektaĵo",
|
||||
"hd": "altdistingiva",
|
||||
"subtitles": "subtekstoj",
|
||||
"creative_commons": "Krea Komunaĵo",
|
||||
"3d": "3D",
|
||||
"live": "nuna",
|
||||
"4k": "4k",
|
||||
"location": "loko",
|
||||
"hdr": "granddinamikgama",
|
||||
"filter": "filtri",
|
||||
"search_filters_sort_option_relevance": "rilateco",
|
||||
"search_filters_sort_option_rating": "takso",
|
||||
"search_filters_sort_option_date": "dato",
|
||||
"search_filters_sort_option_views": "vidoj",
|
||||
"search_filters_type_label": "enhavtipo",
|
||||
"search_filters_duration_label": "daŭro",
|
||||
"search_filters_features_label": "trajtoj",
|
||||
"search_filters_sort_label": "ordigi",
|
||||
"search_filters_date_option_hour": "horo",
|
||||
"search_filters_date_option_today": "hodiaŭ",
|
||||
"search_filters_date_option_week": "semajno",
|
||||
"search_filters_date_option_month": "monato",
|
||||
"search_filters_date_option_year": "jaro",
|
||||
"search_filters_type_option_video": "filmeto",
|
||||
"search_filters_type_option_channel": "kanalo",
|
||||
"search_filters_type_option_playlist": "ludlisto",
|
||||
"search_filters_type_option_movie": "filmo",
|
||||
"search_filters_type_option_show": "spektaĵo",
|
||||
"search_filters_features_option_hd": "altdistingiva",
|
||||
"search_filters_features_option_subtitles": "subtekstoj",
|
||||
"search_filters_features_option_c_commons": "Krea Komunaĵo",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "nuna",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "loko",
|
||||
"search_filters_features_option_hdr": "granddinamikgama",
|
||||
"search_filters_label": "filtri",
|
||||
"Current version: ": "Nuna versio: ",
|
||||
"next_steps_error_message": "Poste, vi provu: ",
|
||||
"next_steps_error_message_refresh": "Reŝargi",
|
||||
"next_steps_error_message_go_to_youtube": "Iri al JuTubo",
|
||||
"long": "Longa (> 20 minutos)",
|
||||
"short": "Mallonga (< 4 minutos)",
|
||||
"search_filters_duration_option_long": "Longa (> 20 minutos)",
|
||||
"search_filters_duration_option_short": "Mallonga (< 4 minutos)",
|
||||
"footer_documentation": "Dokumentaro",
|
||||
"footer_source_code": "Fontkodo",
|
||||
"adminprefs_modified_source_code_url_label": "URL al modifita deponejo de fontkodo",
|
||||
|
@ -329,39 +329,39 @@
|
||||
"Videos": "Vídeos",
|
||||
"Playlists": "Listas de reproducción",
|
||||
"Community": "Comunidad",
|
||||
"relevance": "relevancia",
|
||||
"rating": "valoración",
|
||||
"date": "fecha",
|
||||
"views": "visualizaciones",
|
||||
"content_type": "content_type",
|
||||
"duration": "duración",
|
||||
"features": "funcionalidades",
|
||||
"sort": "ordenar",
|
||||
"hour": "hora",
|
||||
"today": "hoy",
|
||||
"week": "semana",
|
||||
"month": "mes",
|
||||
"year": "año",
|
||||
"video": "vídeo",
|
||||
"channel": "canal",
|
||||
"playlist": "lista de reproducción",
|
||||
"movie": "película",
|
||||
"show": "programa",
|
||||
"hd": "hd",
|
||||
"subtitles": "subtítulos",
|
||||
"creative_commons": "creative_commons",
|
||||
"3d": "3d",
|
||||
"live": "directo",
|
||||
"4k": "4k",
|
||||
"location": "ubicación",
|
||||
"hdr": "hdr",
|
||||
"filter": "filtro",
|
||||
"search_filters_sort_option_relevance": "relevancia",
|
||||
"search_filters_sort_option_rating": "valoración",
|
||||
"search_filters_sort_option_date": "fecha",
|
||||
"search_filters_sort_option_views": "visualizaciones",
|
||||
"search_filters_type_label": "content_type",
|
||||
"search_filters_duration_label": "duración",
|
||||
"search_filters_features_label": "funcionalidades",
|
||||
"search_filters_sort_label": "ordenar",
|
||||
"search_filters_date_option_hour": "hora",
|
||||
"search_filters_date_option_today": "hoy",
|
||||
"search_filters_date_option_week": "semana",
|
||||
"search_filters_date_option_month": "mes",
|
||||
"search_filters_date_option_year": "año",
|
||||
"search_filters_type_option_video": "vídeo",
|
||||
"search_filters_type_option_channel": "canal",
|
||||
"search_filters_type_option_playlist": "lista de reproducción",
|
||||
"search_filters_type_option_movie": "película",
|
||||
"search_filters_type_option_show": "programa",
|
||||
"search_filters_features_option_hd": "hd",
|
||||
"search_filters_features_option_subtitles": "subtítulos",
|
||||
"search_filters_features_option_c_commons": "creative_commons",
|
||||
"search_filters_features_option_three_d": "3d",
|
||||
"search_filters_features_option_live": "directo",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "ubicación",
|
||||
"search_filters_features_option_hdr": "hdr",
|
||||
"search_filters_label": "filtro",
|
||||
"Current version: ": "Versión actual: ",
|
||||
"next_steps_error_message": "Después de lo cual deberías intentar: ",
|
||||
"next_steps_error_message_refresh": "Recargar la página",
|
||||
"next_steps_error_message_go_to_youtube": "Ir a YouTube",
|
||||
"short": "Corto (< 4 minutos)",
|
||||
"long": "Largo (> 20 minutos)",
|
||||
"search_filters_duration_option_short": "Corto (< 4 minutos)",
|
||||
"search_filters_duration_option_long": "Largo (> 20 minutos)",
|
||||
"footer_documentation": "Documentación",
|
||||
"footer_original_source_code": "Código fuente original",
|
||||
"adminprefs_modified_source_code_url_label": "URL al repositorio de código fuente modificado",
|
||||
@ -395,8 +395,8 @@
|
||||
"preferences_quality_dash_option_worst": "La peor",
|
||||
"videoinfo_invidious_embed_link": "Enlace para Insertar",
|
||||
"preferences_quality_dash_option_1080p": "1080p",
|
||||
"purchased": "Comprado",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_purchased": "Comprado",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"videoinfo_watch_on_youTube": "Ver en YouTube",
|
||||
"preferences_save_player_pos_label": "Guardar posición de reproducción: ",
|
||||
"generic_views_count": "{{count}} visualización",
|
||||
|
@ -345,33 +345,33 @@
|
||||
"Videos": "ویدیو ها",
|
||||
"Playlists": "سیاهههای پخش",
|
||||
"Community": "اجتماع",
|
||||
"relevance": "مرتبط بودن",
|
||||
"rating": "امتیاز",
|
||||
"date": "تاریخ بارگذاری",
|
||||
"views": "تعداد بازدید",
|
||||
"content_type": "نوع",
|
||||
"duration": "مدت",
|
||||
"features": "ویژگیها",
|
||||
"sort": "به ترتیب",
|
||||
"hour": "یک ساعت گذشته",
|
||||
"today": "امروز",
|
||||
"week": "این هفته",
|
||||
"month": "این ماه",
|
||||
"year": "امسال",
|
||||
"video": "ویدئو",
|
||||
"channel": "کانال",
|
||||
"playlist": "سیاههٔ پخش",
|
||||
"movie": "فیلم",
|
||||
"show": "نمایش",
|
||||
"hd": "HD",
|
||||
"subtitles": "زیرنویس",
|
||||
"creative_commons": "کریتیو کامونز",
|
||||
"3d": "سهبعدی",
|
||||
"live": "زنده",
|
||||
"4k": "4K",
|
||||
"location": "مکان",
|
||||
"hdr": "HDR",
|
||||
"filter": "پالایه",
|
||||
"search_filters_sort_option_relevance": "مرتبط بودن",
|
||||
"search_filters_sort_option_rating": "امتیاز",
|
||||
"search_filters_sort_option_date": "تاریخ بارگذاری",
|
||||
"search_filters_sort_option_views": "تعداد بازدید",
|
||||
"search_filters_type_label": "نوع",
|
||||
"search_filters_duration_label": "مدت",
|
||||
"search_filters_features_label": "ویژگیها",
|
||||
"search_filters_sort_label": "به ترتیب",
|
||||
"search_filters_date_option_hour": "یک ساعت گذشته",
|
||||
"search_filters_date_option_today": "امروز",
|
||||
"search_filters_date_option_week": "این هفته",
|
||||
"search_filters_date_option_month": "این ماه",
|
||||
"search_filters_date_option_year": "امسال",
|
||||
"search_filters_type_option_video": "ویدئو",
|
||||
"search_filters_type_option_channel": "کانال",
|
||||
"search_filters_type_option_playlist": "سیاههٔ پخش",
|
||||
"search_filters_type_option_movie": "فیلم",
|
||||
"search_filters_type_option_show": "نمایش",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "زیرنویس",
|
||||
"search_filters_features_option_c_commons": "کریتیو کامونز",
|
||||
"search_filters_features_option_three_d": "سهبعدی",
|
||||
"search_filters_features_option_live": "زنده",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "مکان",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "پالایه",
|
||||
"Current version: ": "نسخه فعلی: ",
|
||||
"next_steps_error_message": "اکنون بایستی یکی از این موارد را امتحان کنید: ",
|
||||
"next_steps_error_message_refresh": "تازهسازی",
|
||||
@ -393,7 +393,7 @@
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"invidious": "اینویدیوس",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"footer_donate_page": "کمک مالی",
|
||||
"footer_source_code": "کد منبع",
|
||||
"footer_modfied_source_code": "کد منبع ویرایش شده",
|
||||
@ -405,12 +405,12 @@
|
||||
"download_subtitles": "زیرنویسها - `x` (.vtt)",
|
||||
"Video unavailable": "ویدئو دردسترس نیست",
|
||||
"preferences_save_player_pos_label": "ذخیره زمان کنونی ویدئو: ",
|
||||
"purchased": "خریداری شده",
|
||||
"search_filters_features_option_purchased": "خریداری شده",
|
||||
"preferences_quality_dash_label": "کیفیت ترجیحی ویدئو DASH: ",
|
||||
"preferences_region_label": "کشور محتوا: ",
|
||||
"footer_documentation": "مستندات",
|
||||
"footer_original_source_code": "کد منبع اصلی",
|
||||
"long": "بلند (> 20 دقیقه)",
|
||||
"search_filters_duration_option_long": "بلند (> 20 دقیقه)",
|
||||
"adminprefs_modified_source_code_url_label": "URL مخزن کد منبع ویریش شده",
|
||||
"short": "کوتاه (< 4 دقیقه)"
|
||||
"search_filters_duration_option_short": "کوتاه (< 4 دقیقه)"
|
||||
}
|
||||
|
@ -328,33 +328,33 @@
|
||||
"Videos": "Videot",
|
||||
"Playlists": "Soittolistat",
|
||||
"Community": "Yhteisö",
|
||||
"relevance": "Osuvuus",
|
||||
"rating": "Arvostelu",
|
||||
"date": "Latauspäivämäärä",
|
||||
"views": "Katselukerrat",
|
||||
"content_type": "Tyyppi",
|
||||
"duration": "Kesto",
|
||||
"features": "Ominaisuudet",
|
||||
"sort": "Luokittele",
|
||||
"hour": "Viimeisin tunti",
|
||||
"today": "Tänään",
|
||||
"week": "Tämä viikko",
|
||||
"month": "Tämä kuukausi",
|
||||
"year": "Tämä vuosi",
|
||||
"video": "Video",
|
||||
"channel": "Kanava",
|
||||
"playlist": "Soittolista",
|
||||
"movie": "Elokuva",
|
||||
"show": "Ohjelma",
|
||||
"hd": "HD",
|
||||
"subtitles": "Tekstitys/CC",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "Suora lähetys",
|
||||
"4k": "4K",
|
||||
"location": "Sijainti",
|
||||
"hdr": "HDR",
|
||||
"filter": "Suodatin",
|
||||
"search_filters_sort_option_relevance": "Osuvuus",
|
||||
"search_filters_sort_option_rating": "Arvostelu",
|
||||
"search_filters_sort_option_date": "Latauspäivämäärä",
|
||||
"search_filters_sort_option_views": "Katselukerrat",
|
||||
"search_filters_type_label": "Tyyppi",
|
||||
"search_filters_duration_label": "Kesto",
|
||||
"search_filters_features_label": "Ominaisuudet",
|
||||
"search_filters_sort_label": "Luokittele",
|
||||
"search_filters_date_option_hour": "Viimeisin tunti",
|
||||
"search_filters_date_option_today": "Tänään",
|
||||
"search_filters_date_option_week": "Tämä viikko",
|
||||
"search_filters_date_option_month": "Tämä kuukausi",
|
||||
"search_filters_date_option_year": "Tämä vuosi",
|
||||
"search_filters_type_option_video": "Video",
|
||||
"search_filters_type_option_channel": "Kanava",
|
||||
"search_filters_type_option_playlist": "Soittolista",
|
||||
"search_filters_type_option_movie": "Elokuva",
|
||||
"search_filters_type_option_show": "Ohjelma",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "Tekstitys/CC",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "Suora lähetys",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "Sijainti",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "Suodatin",
|
||||
"Current version: ": "Tämänhetkinen versio: ",
|
||||
"next_steps_error_message": "Sinun tulisi kokeilla seuraavia: ",
|
||||
"next_steps_error_message_refresh": "Päivitä",
|
||||
@ -423,8 +423,8 @@
|
||||
"preferences_quality_dash_label": "Haluttava DASH-videolaatu: ",
|
||||
"generic_count_years": "{{count}} vuosi",
|
||||
"generic_count_years_plural": "{{count}} vuotta",
|
||||
"purchased": "Ostettu",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_purchased": "Ostettu",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"videoinfo_watch_on_youTube": "Katso YouTubessa",
|
||||
"none": "ei mikään",
|
||||
"videoinfo_started_streaming_x_ago": "Striimaaminen aloitettu `x` sitten",
|
||||
@ -433,8 +433,8 @@
|
||||
"footer_source_code": "Lähdekoodi",
|
||||
"adminprefs_modified_source_code_url_label": "URL muokattuun lähdekoodirepositoryyn",
|
||||
"Released under the AGPLv3 on Github.": "Julkaistu AGPLv3-lisenssin alla GitHubissa.",
|
||||
"short": "Lyhyt (< 4 minuuttia)",
|
||||
"long": "Pitkä (> 20 minuuttia)",
|
||||
"search_filters_duration_option_short": "Lyhyt (< 4 minuuttia)",
|
||||
"search_filters_duration_option_long": "Pitkä (> 20 minuuttia)",
|
||||
"footer_documentation": "Dokumentaatio",
|
||||
"footer_original_source_code": "Alkuperäinen lähdekoodi",
|
||||
"footer_modfied_source_code": "Muokattu lähdekoodi",
|
||||
|
@ -361,33 +361,33 @@
|
||||
"Videos": "Vidéos",
|
||||
"Playlists": "Listes de lecture",
|
||||
"Community": "Communauté",
|
||||
"relevance": "pertinence",
|
||||
"rating": "évaluation",
|
||||
"date": "date",
|
||||
"views": "nombre de vues",
|
||||
"content_type": "type",
|
||||
"duration": "durée",
|
||||
"features": "fonctionnalités",
|
||||
"sort": "Trier par",
|
||||
"hour": "dernière heure",
|
||||
"today": "aujourd'hui",
|
||||
"week": "semaine",
|
||||
"month": "mois",
|
||||
"year": "année",
|
||||
"video": "vidéo",
|
||||
"channel": "chaîne",
|
||||
"playlist": "liste de lecture",
|
||||
"movie": "film",
|
||||
"show": "émission",
|
||||
"hd": "HD",
|
||||
"subtitles": "sous-titres / CC",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "en direct",
|
||||
"4k": "4K",
|
||||
"location": "emplacement",
|
||||
"hdr": "HDR",
|
||||
"filter": "filtrer",
|
||||
"search_filters_sort_option_relevance": "pertinence",
|
||||
"search_filters_sort_option_rating": "évaluation",
|
||||
"search_filters_sort_option_date": "date",
|
||||
"search_filters_sort_option_views": "nombre de vues",
|
||||
"search_filters_type_label": "type",
|
||||
"search_filters_duration_label": "durée",
|
||||
"search_filters_features_label": "fonctionnalités",
|
||||
"search_filters_sort_label": "Trier par",
|
||||
"search_filters_date_option_hour": "dernière heure",
|
||||
"search_filters_date_option_today": "aujourd'hui",
|
||||
"search_filters_date_option_week": "semaine",
|
||||
"search_filters_date_option_month": "mois",
|
||||
"search_filters_date_option_year": "année",
|
||||
"search_filters_type_option_video": "vidéo",
|
||||
"search_filters_type_option_channel": "chaîne",
|
||||
"search_filters_type_option_playlist": "liste de lecture",
|
||||
"search_filters_type_option_movie": "film",
|
||||
"search_filters_type_option_show": "émission",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "sous-titres / CC",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "en direct",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "emplacement",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "filtrer",
|
||||
"Current version: ": "Version actuelle : ",
|
||||
"next_steps_error_message": "Vous pouvez essayer de : ",
|
||||
"next_steps_error_message_refresh": "Rafraîchir la page",
|
||||
@ -397,8 +397,8 @@
|
||||
"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)",
|
||||
"search_filters_duration_option_short": "Courte (< 4 minutes)",
|
||||
"search_filters_duration_option_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",
|
||||
@ -415,12 +415,12 @@
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"invidious": "Invidious",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"none": "aucun",
|
||||
"videoinfo_started_streaming_x_ago": "En stream depuis `x`",
|
||||
"videoinfo_watch_on_youTube": "Regarder sur YouTube",
|
||||
"videoinfo_youTube_embed_link": "Intégrer",
|
||||
"purchased": "Acheter",
|
||||
"search_filters_features_option_purchased": "Acheter",
|
||||
"videoinfo_invidious_embed_link": "Lien intégré",
|
||||
"download_subtitles": "Sous-titres - `x` (.vtt)",
|
||||
"user_saved_playlists": "`x` listes de lecture sauvegardées",
|
||||
|
@ -274,32 +274,32 @@
|
||||
"Videos": "סרטונים",
|
||||
"Playlists": "פלייליסטים",
|
||||
"Community": "קהילה",
|
||||
"relevance": "רלוונטיות",
|
||||
"rating": "דירוג",
|
||||
"date": "תאריך העלאה",
|
||||
"views": "מספר צפיות",
|
||||
"content_type": "סוג",
|
||||
"duration": "משך זמן",
|
||||
"features": "תכונות",
|
||||
"sort": "מיון לפי",
|
||||
"hour": "השעה האחרונה",
|
||||
"today": "היום",
|
||||
"week": "השבוע",
|
||||
"month": "החודש",
|
||||
"year": "השנה",
|
||||
"video": "סרטון",
|
||||
"channel": "ערוץ",
|
||||
"playlist": "פלייליסט",
|
||||
"movie": "סרט",
|
||||
"show": "תכנית טלוויזיה",
|
||||
"hd": "HD",
|
||||
"subtitles": "כתוביות",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "Live",
|
||||
"4k": "4K",
|
||||
"location": "מיקום",
|
||||
"hdr": "HDR",
|
||||
"filter": "סינון",
|
||||
"search_filters_sort_option_relevance": "רלוונטיות",
|
||||
"search_filters_sort_option_rating": "דירוג",
|
||||
"search_filters_sort_option_date": "תאריך העלאה",
|
||||
"search_filters_sort_option_views": "מספר צפיות",
|
||||
"search_filters_type_label": "סוג",
|
||||
"search_filters_duration_label": "משך זמן",
|
||||
"search_filters_features_label": "תכונות",
|
||||
"search_filters_sort_label": "מיון לפי",
|
||||
"search_filters_date_option_hour": "השעה האחרונה",
|
||||
"search_filters_date_option_today": "היום",
|
||||
"search_filters_date_option_week": "השבוע",
|
||||
"search_filters_date_option_month": "החודש",
|
||||
"search_filters_date_option_year": "השנה",
|
||||
"search_filters_type_option_video": "סרטון",
|
||||
"search_filters_type_option_channel": "ערוץ",
|
||||
"search_filters_type_option_playlist": "פלייליסט",
|
||||
"search_filters_type_option_movie": "סרט",
|
||||
"search_filters_type_option_show": "תכנית טלוויזיה",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "כתוביות",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "Live",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "מיקום",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "סינון",
|
||||
"Current version: ": "הגרסה הנוכחית: "
|
||||
}
|
||||
|
@ -329,41 +329,41 @@
|
||||
"Videos": "Videa",
|
||||
"Playlists": "Zbirke",
|
||||
"Community": "Zajednica",
|
||||
"relevance": "značaj",
|
||||
"rating": "ocjena",
|
||||
"date": "datum",
|
||||
"views": "prikazi",
|
||||
"content_type": "vrsta_sadržaja",
|
||||
"duration": "trajanje",
|
||||
"features": "funkcije",
|
||||
"sort": "redoslijed",
|
||||
"hour": "sat",
|
||||
"today": "danas",
|
||||
"week": "tjedan",
|
||||
"month": "mjesec",
|
||||
"year": "godina",
|
||||
"video": "video",
|
||||
"channel": "kanal",
|
||||
"playlist": "Zbirka",
|
||||
"movie": "film",
|
||||
"show": "emisija",
|
||||
"hd": "hd",
|
||||
"subtitles": "titlovi",
|
||||
"creative_commons": "creative_commons",
|
||||
"3d": "3d",
|
||||
"live": "uživo",
|
||||
"4k": "4k",
|
||||
"location": "lokacija",
|
||||
"hdr": "hdr",
|
||||
"filter": "filtar",
|
||||
"search_filters_sort_option_relevance": "značaj",
|
||||
"search_filters_sort_option_rating": "ocjena",
|
||||
"search_filters_sort_option_date": "datum",
|
||||
"search_filters_sort_option_views": "prikazi",
|
||||
"search_filters_type_label": "vrsta_sadržaja",
|
||||
"search_filters_duration_label": "trajanje",
|
||||
"search_filters_features_label": "funkcije",
|
||||
"search_filters_sort_label": "redoslijed",
|
||||
"search_filters_date_option_hour": "sat",
|
||||
"search_filters_date_option_today": "danas",
|
||||
"search_filters_date_option_week": "tjedan",
|
||||
"search_filters_date_option_month": "mjesec",
|
||||
"search_filters_date_option_year": "godina",
|
||||
"search_filters_type_option_video": "video",
|
||||
"search_filters_type_option_channel": "kanal",
|
||||
"search_filters_type_option_playlist": "Zbirka",
|
||||
"search_filters_type_option_movie": "film",
|
||||
"search_filters_type_option_show": "emisija",
|
||||
"search_filters_features_option_hd": "hd",
|
||||
"search_filters_features_option_subtitles": "titlovi",
|
||||
"search_filters_features_option_c_commons": "creative_commons",
|
||||
"search_filters_features_option_three_d": "3d",
|
||||
"search_filters_features_option_live": "uživo",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "lokacija",
|
||||
"search_filters_features_option_hdr": "hdr",
|
||||
"search_filters_label": "filtar",
|
||||
"Current version: ": "Trenutačna verzija: ",
|
||||
"next_steps_error_message": "Nakon toga bi trebali pokušati sljedeće: ",
|
||||
"next_steps_error_message_refresh": "Aktualiziraj stranicu",
|
||||
"next_steps_error_message_go_to_youtube": "Idi na YouTube",
|
||||
"footer_donate_page": "Doniraj",
|
||||
"adminprefs_modified_source_code_url_label": "URL do repozitorija izmijenjenog izvornog koda",
|
||||
"short": "Kratki (< 4 minute)",
|
||||
"long": "Dugi (> 20 minute)",
|
||||
"search_filters_duration_option_short": "Kratki (< 4 minute)",
|
||||
"search_filters_duration_option_long": "Dugi (> 20 minute)",
|
||||
"footer_source_code": "Izvorni kod",
|
||||
"footer_modfied_source_code": "Izmijenjeni izvorni kod",
|
||||
"footer_documentation": "Dokumentacija",
|
||||
@ -382,8 +382,8 @@
|
||||
"preferences_quality_dash_option_240p": "240 p",
|
||||
"preferences_quality_dash_option_144p": "144 p",
|
||||
"invidious": "Invidious",
|
||||
"purchased": "Kupljeno",
|
||||
"360": "360 °",
|
||||
"search_filters_features_option_purchased": "Kupljeno",
|
||||
"search_filters_features_option_three_sixty": "360 °",
|
||||
"none": "bez",
|
||||
"videoinfo_youTube_embed_link": "Ugradi",
|
||||
"user_created_playlists": "`x` stvorene zbirke",
|
||||
|
@ -365,9 +365,9 @@
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"invidious": "Invidious",
|
||||
"videoinfo_started_streaming_x_ago": "`x` ezelőtt kezdte streamelni",
|
||||
"views": "Mennyien látták",
|
||||
"purchased": "Megvásárolva",
|
||||
"360": "360°-os",
|
||||
"search_filters_sort_option_views": "Mennyien látták",
|
||||
"search_filters_features_option_purchased": "Megvásárolva",
|
||||
"search_filters_features_option_three_sixty": "360°-os",
|
||||
"footer_original_source_code": "Eredeti forráskód",
|
||||
"none": "egyik sem",
|
||||
"videoinfo_watch_on_youTube": "YouTube-on megnézni",
|
||||
@ -382,14 +382,14 @@
|
||||
"preferences_quality_dash_option_1440p": "1440p",
|
||||
"preferences_quality_dash_label": "DASH-videó minősége: ",
|
||||
"preferences_quality_option_small": "Rossz",
|
||||
"date": "Feltöltés dátuma",
|
||||
"search_filters_sort_option_date": "Feltöltés dátuma",
|
||||
"Video unavailable": "A videó nem érhető el",
|
||||
"preferences_save_player_pos_label": "A videó folytatása onnan, ahol félbe lett hagyva: ",
|
||||
"preferences_show_nick_label": "Becenév mutatása felül: ",
|
||||
"Released under the AGPLv3 on Github.": "AGPLv3 licenc alapján a GitHubon",
|
||||
"3d": "3D-ben",
|
||||
"live": "Élőben",
|
||||
"filter": "Szűrők",
|
||||
"search_filters_features_option_three_d": "3D-ben",
|
||||
"search_filters_features_option_live": "Élőben",
|
||||
"search_filters_label": "Szűrők",
|
||||
"next_steps_error_message_refresh": "Újratöltés",
|
||||
"footer_donate_page": "Adakozás",
|
||||
"footer_source_code": "Forráskód",
|
||||
@ -397,40 +397,40 @@
|
||||
"adminprefs_modified_source_code_url_label": "A módosított forráskód repositoryjának URL-je:",
|
||||
"preferences_automatic_instance_redirect_label": "Váltáskor másik Invidious oldal automatikus betöltése (redirect.invidious.io töltődik, ha nem működne): ",
|
||||
"preferences_region_label": "Ország tartalmainak mutatása: ",
|
||||
"relevance": "Relevancia",
|
||||
"rating": "Pontszám",
|
||||
"content_type": "Típus",
|
||||
"today": "Mai napon",
|
||||
"channel": "Csatorna",
|
||||
"video": "Videó",
|
||||
"playlist": "Lejátszási lista",
|
||||
"creative_commons": "Creative Commons",
|
||||
"features": "Jellemzők",
|
||||
"sort": "Rendezés módja",
|
||||
"search_filters_sort_option_relevance": "Relevancia",
|
||||
"search_filters_sort_option_rating": "Pontszám",
|
||||
"search_filters_type_label": "Típus",
|
||||
"search_filters_date_option_today": "Mai napon",
|
||||
"search_filters_type_option_channel": "Csatorna",
|
||||
"search_filters_type_option_video": "Videó",
|
||||
"search_filters_type_option_playlist": "Lejátszási lista",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_label": "Jellemzők",
|
||||
"search_filters_sort_label": "Rendezés módja",
|
||||
"preferences_category_misc": "További beállítások",
|
||||
"%A %B %-d, %Y": "%Y. %B %-d %A",
|
||||
"long": "Hosszú (20 percnél hosszabb)",
|
||||
"year": "Ebben az évben",
|
||||
"hour": "Az elmúlt órában",
|
||||
"movie": "Film",
|
||||
"hdr": "HDR",
|
||||
"search_filters_duration_option_long": "Hosszú (20 percnél hosszabb)",
|
||||
"search_filters_date_option_year": "Ebben az évben",
|
||||
"search_filters_date_option_hour": "Az elmúlt órában",
|
||||
"search_filters_type_option_movie": "Film",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"Broken? Try another Invidious Instance": "Nem működik? Próbáld meg egy másik Invidious oldallal.",
|
||||
"duration": "Játékidő",
|
||||
"search_filters_duration_label": "Játékidő",
|
||||
"next_steps_error_message": "Az alábbi lehetőségek állnak rendelkezésre: ",
|
||||
"Xhosa": "xhosza",
|
||||
"Switch Invidious Instance": "Váltás másik Invidious-oldalra",
|
||||
"Urdu": "urdu",
|
||||
"week": "Ezen a héten",
|
||||
"search_filters_date_option_week": "Ezen a héten",
|
||||
"Invalid TFA code": "A kétlépéses hitelesítés kódja nem megfelelő",
|
||||
"footer_documentation": "Dokumentáció",
|
||||
"hd": "HD",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"next_steps_error_message_go_to_youtube": "Ugrás a YouTube-ra",
|
||||
"show": "Műsor",
|
||||
"4k": "4K",
|
||||
"short": "Rövid (4 percnél nem több)",
|
||||
"month": "Ebben a hónapban",
|
||||
"subtitles": "Felirattal",
|
||||
"location": "Közelben",
|
||||
"search_filters_type_option_show": "Műsor",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_duration_option_short": "Rövid (4 percnél nem több)",
|
||||
"search_filters_date_option_month": "Ebben a hónapban",
|
||||
"search_filters_features_option_subtitles": "Felirattal",
|
||||
"search_filters_features_option_location": "Közelben",
|
||||
"crash_page_you_found_a_bug": "Úgy néz ki, találtál egy hibát az Invidiousban.",
|
||||
"crash_page_before_reporting": "Mielőtt jelentenéd a hibát:",
|
||||
"crash_page_read_the_faq": "olvasd el a <a href=\"`x`\">Gyakran Ismételt Kérdéseket (GYIK)</a>",
|
||||
|
@ -345,33 +345,33 @@
|
||||
"Videos": "Video",
|
||||
"Playlists": "Daftar putar",
|
||||
"Community": "Komunitas",
|
||||
"relevance": "Relevansi",
|
||||
"rating": "Penilaian",
|
||||
"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",
|
||||
"search_filters_sort_option_relevance": "Relevansi",
|
||||
"search_filters_sort_option_rating": "Penilaian",
|
||||
"search_filters_sort_option_date": "Tanggal unggah",
|
||||
"search_filters_sort_option_views": "Jumlah ditonton",
|
||||
"search_filters_type_label": "Tipe",
|
||||
"search_filters_duration_label": "Durasi",
|
||||
"search_filters_features_label": "Fitur",
|
||||
"search_filters_sort_label": "Urut Berdasarkan",
|
||||
"search_filters_date_option_hour": "Jam Terakhir",
|
||||
"search_filters_date_option_today": "Hari Ini",
|
||||
"search_filters_date_option_week": "Pekan Ini",
|
||||
"search_filters_date_option_month": "Bulan Ini",
|
||||
"search_filters_date_option_year": "Tahun Ini",
|
||||
"search_filters_type_option_video": "Video",
|
||||
"search_filters_type_option_channel": "Kanal",
|
||||
"search_filters_type_option_playlist": "Daftar Putar",
|
||||
"search_filters_type_option_movie": "Film",
|
||||
"search_filters_type_option_show": "Pertunjukan/Acara",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "Takarir",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "Siaran Langsung",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "Lokasi",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "Saring",
|
||||
"Current version: ": "Versi saat ini: ",
|
||||
"next_steps_error_message": "Setelah itu Anda harus mencoba: ",
|
||||
"next_steps_error_message_refresh": "Segarkan",
|
||||
@ -380,8 +380,8 @@
|
||||
"adminprefs_modified_source_code_url_label": "URL ke repositori kode sumber yang dimodifikasi",
|
||||
"footer_source_code": "Kode sumber",
|
||||
"footer_original_source_code": "Kode sumber yang asli",
|
||||
"short": "Pendek (< 4 menit)",
|
||||
"long": "Panjang (> 20 menit)",
|
||||
"search_filters_duration_option_short": "Pendek (< 4 menit)",
|
||||
"search_filters_duration_option_long": "Panjang (> 20 menit)",
|
||||
"footer_modfied_source_code": "Kode sumber yang dimodifikasi",
|
||||
"footer_documentation": "Dokumentasi",
|
||||
"preferences_region_label": "Konten dari negara: ",
|
||||
@ -398,8 +398,8 @@
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"invidious": "Invidious",
|
||||
"purchased": "Dibeli",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_purchased": "Dibeli",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"none": "tidak ada",
|
||||
"videoinfo_watch_on_youTube": "Tonton di YouTube",
|
||||
"videoinfo_youTube_embed_link": "Tersemat",
|
||||
|
@ -347,32 +347,32 @@
|
||||
"Videos": "Video",
|
||||
"Playlists": "Playlist",
|
||||
"Community": "Comunità",
|
||||
"relevance": "Pertinenza",
|
||||
"rating": "Valutazione",
|
||||
"date": "Data di caricamento",
|
||||
"views": "Numero di visualizzazioni",
|
||||
"content_type": "Tipo",
|
||||
"duration": "Durata",
|
||||
"features": "Caratteristiche",
|
||||
"sort": "Ordina per",
|
||||
"hour": "Ultima ora",
|
||||
"today": "Oggi",
|
||||
"week": "Questa settimana",
|
||||
"month": "Questo mese",
|
||||
"year": "Quest'anno",
|
||||
"video": "Video",
|
||||
"channel": "Canale",
|
||||
"playlist": "Playlist",
|
||||
"movie": "Film",
|
||||
"hd": "AD",
|
||||
"subtitles": "Sottotitoli / CC",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "In diretta",
|
||||
"4k": "4K",
|
||||
"location": "Posizione",
|
||||
"hdr": "HDR",
|
||||
"filter": "Filtra",
|
||||
"search_filters_sort_option_relevance": "Pertinenza",
|
||||
"search_filters_sort_option_rating": "Valutazione",
|
||||
"search_filters_sort_option_date": "Data di caricamento",
|
||||
"search_filters_sort_option_views": "Numero di visualizzazioni",
|
||||
"search_filters_type_label": "Tipo",
|
||||
"search_filters_duration_label": "Durata",
|
||||
"search_filters_features_label": "Caratteristiche",
|
||||
"search_filters_sort_label": "Ordina per",
|
||||
"search_filters_date_option_hour": "Ultima ora",
|
||||
"search_filters_date_option_today": "Oggi",
|
||||
"search_filters_date_option_week": "Questa settimana",
|
||||
"search_filters_date_option_month": "Questo mese",
|
||||
"search_filters_date_option_year": "Quest'anno",
|
||||
"search_filters_type_option_video": "Video",
|
||||
"search_filters_type_option_channel": "Canale",
|
||||
"search_filters_type_option_playlist": "Playlist",
|
||||
"search_filters_type_option_movie": "Film",
|
||||
"search_filters_features_option_hd": "AD",
|
||||
"search_filters_features_option_subtitles": "Sottotitoli / CC",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "In diretta",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "Posizione",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "Filtra",
|
||||
"Current version: ": "Versione attuale: ",
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"preferences_quality_dash_option_360p": "360p",
|
||||
@ -382,7 +382,7 @@
|
||||
"preferences_quality_dash_option_1440p": "1440p",
|
||||
"preferences_quality_dash_option_2160p": "2160p",
|
||||
"preferences_quality_dash_option_4320p": "4320p",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"Released under the AGPLv3 on Github.": "Rilasciato su Github con licenza AGPLv3.",
|
||||
"preferences_quality_option_medium": "Media",
|
||||
|
@ -345,44 +345,44 @@
|
||||
"Videos": "動画",
|
||||
"Playlists": "プレイリスト",
|
||||
"Community": "コミュニティ",
|
||||
"relevance": "関連",
|
||||
"rating": "評価",
|
||||
"date": "時刻",
|
||||
"views": "再生回数",
|
||||
"content_type": "コンテンツの種類",
|
||||
"duration": "再生時間",
|
||||
"features": "機能",
|
||||
"sort": "順番",
|
||||
"hour": "1時間前",
|
||||
"today": "今日",
|
||||
"week": "今週",
|
||||
"month": "今月",
|
||||
"year": "今年",
|
||||
"video": "動画",
|
||||
"channel": "チャンネル",
|
||||
"playlist": "再生リスト",
|
||||
"movie": "映画",
|
||||
"show": "番組",
|
||||
"hd": "HD",
|
||||
"subtitles": "字幕",
|
||||
"creative_commons": "クリエイティブ・コモンズ",
|
||||
"3d": "3D",
|
||||
"live": "生配信",
|
||||
"4k": "4K",
|
||||
"location": "場所",
|
||||
"hdr": "HDR",
|
||||
"filter": "フィルタ",
|
||||
"search_filters_sort_option_relevance": "関連",
|
||||
"search_filters_sort_option_rating": "評価",
|
||||
"search_filters_sort_option_date": "時刻",
|
||||
"search_filters_sort_option_views": "再生回数",
|
||||
"search_filters_type_label": "コンテンツの種類",
|
||||
"search_filters_duration_label": "再生時間",
|
||||
"search_filters_features_label": "機能",
|
||||
"search_filters_sort_label": "順番",
|
||||
"search_filters_date_option_hour": "1時間前",
|
||||
"search_filters_date_option_today": "今日",
|
||||
"search_filters_date_option_week": "今週",
|
||||
"search_filters_date_option_month": "今月",
|
||||
"search_filters_date_option_year": "今年",
|
||||
"search_filters_type_option_video": "動画",
|
||||
"search_filters_type_option_channel": "チャンネル",
|
||||
"search_filters_type_option_playlist": "再生リスト",
|
||||
"search_filters_type_option_movie": "映画",
|
||||
"search_filters_type_option_show": "番組",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "字幕",
|
||||
"search_filters_features_option_c_commons": "クリエイティブ・コモンズ",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "生配信",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "場所",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "フィルタ",
|
||||
"Current version: ": "現在のバージョン: ",
|
||||
"next_steps_error_message": "下記のものを試して下さい: ",
|
||||
"next_steps_error_message_refresh": "再読込",
|
||||
"next_steps_error_message_go_to_youtube": "YouTubeへ",
|
||||
"short": "4 分未満",
|
||||
"search_filters_duration_option_short": "4 分未満",
|
||||
"footer_documentation": "文書",
|
||||
"footer_source_code": "ソースコード",
|
||||
"footer_original_source_code": "ソースコード(元)",
|
||||
"footer_modfied_source_code": "ソースコード(編集)",
|
||||
"adminprefs_modified_source_code_url_label": "編集したソースコードのレポジトリーURL",
|
||||
"long": "20 分以上",
|
||||
"search_filters_duration_option_long": "20 分以上",
|
||||
"preferences_region_label": "地域: ",
|
||||
"footer_donate_page": "寄付する",
|
||||
"preferences_quality_dash_label": "優先するDash画質 : ",
|
||||
@ -404,7 +404,7 @@
|
||||
"videoinfo_invidious_embed_link": "埋め込みリンク",
|
||||
"none": "なし",
|
||||
"download_subtitles": "字幕 - `x` (.vtt)",
|
||||
"purchased": "購入済み",
|
||||
"search_filters_features_option_purchased": "購入済み",
|
||||
"preferences_quality_option_dash": "DASH (適切な品質)",
|
||||
"preferences_quality_dash_option_worst": "最悪",
|
||||
"preferences_quality_dash_option_best": "最高",
|
||||
|
@ -86,7 +86,7 @@
|
||||
"generic_playlists_count_0": "{{count}} 재생목록",
|
||||
"generic_subscribers_count_0": "{{count}} 구독자",
|
||||
"generic_subscriptions_count_0": "{{count}} 구독",
|
||||
"playlist": "재생목록",
|
||||
"search_filters_type_option_playlist": "재생목록",
|
||||
"Korean": "한국어",
|
||||
"Japanese": "일본어",
|
||||
"Greek": "그리스어",
|
||||
@ -195,16 +195,16 @@
|
||||
"Maori": "마오리어",
|
||||
"Maltese": "몰타어",
|
||||
"Wrong answer": "잘못된 답변",
|
||||
"live": "실시간",
|
||||
"3d": "3D",
|
||||
"location": "지역",
|
||||
"4k": "4K",
|
||||
"filter": "필터",
|
||||
"hdr": "HDR",
|
||||
"search_filters_features_option_live": "실시간",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_location": "지역",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_label": "필터",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"Current version: ": "현재 버전: ",
|
||||
"next_steps_error_message_refresh": "새로 고침",
|
||||
"next_steps_error_message_go_to_youtube": "YouTube로 가기",
|
||||
"subtitles": "자막",
|
||||
"search_filters_features_option_subtitles": "자막",
|
||||
"`x` marked it with a ❤": "`x`님의 ❤",
|
||||
"Download as: ": "다음으로 다운로드: ",
|
||||
"Download": "다운로드",
|
||||
@ -219,7 +219,7 @@
|
||||
"Latvian": "라트비아어",
|
||||
"Latin": "라틴어",
|
||||
"Lao": "라오어",
|
||||
"channel": "채널",
|
||||
"search_filters_type_option_channel": "채널",
|
||||
"Kyrgyz": "키르기스어",
|
||||
"Kurdish": "쿠르드어",
|
||||
"Khmer": "크메르어",
|
||||
@ -279,7 +279,7 @@
|
||||
"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가 꺼져 있는 것 같습니다! 댓글을 보려면 여기를 클릭하세요. 댓글을 로드하는 데 시간이 조금 더 걸릴 수 있습니다.",
|
||||
"Shared `x`": "공유된 `x`",
|
||||
"Whitelisted regions: ": "차단되지 않은 지역: ",
|
||||
"views": "조회수",
|
||||
"search_filters_sort_option_views": "조회수",
|
||||
"Please log in": "로그인하세요",
|
||||
"Password cannot be longer than 55 characters": "비밀번호는 55자 이하여야 합니다",
|
||||
"Password cannot be empty": "비밀번호는 비워둘 수 없습니다",
|
||||
@ -343,12 +343,12 @@
|
||||
"Premieres `x`": "최초 공개 `x`",
|
||||
"Premieres in `x`": "`x` 에 최초 공개",
|
||||
"next_steps_error_message": "다음 방법을 시도해 보세요: ",
|
||||
"creative_commons": "크리에이티브 커먼즈",
|
||||
"duration": "길이",
|
||||
"content_type": "구분",
|
||||
"date": "업로드 날짜",
|
||||
"rating": "평점",
|
||||
"relevance": "관련성",
|
||||
"search_filters_features_option_c_commons": "크리에이티브 커먼즈",
|
||||
"search_filters_duration_label": "길이",
|
||||
"search_filters_type_label": "구분",
|
||||
"search_filters_sort_option_date": "업로드 날짜",
|
||||
"search_filters_sort_option_rating": "평점",
|
||||
"search_filters_sort_option_relevance": "관련성",
|
||||
"Community": "커뮤니티",
|
||||
"Videos": "동영상",
|
||||
"Video mode": "비디오 모드",
|
||||
@ -365,19 +365,19 @@
|
||||
"Rating: ": "평점: ",
|
||||
"About": "정보",
|
||||
"Top": "최고",
|
||||
"hd": "HD",
|
||||
"show": "쇼",
|
||||
"movie": "영화",
|
||||
"video": "동영상",
|
||||
"year": "올해",
|
||||
"month": "이번 달",
|
||||
"week": "이번 주",
|
||||
"today": "오늘",
|
||||
"hour": "지난 1시간",
|
||||
"sort": "정렬기준",
|
||||
"features": "기능별",
|
||||
"short": "4분 미만",
|
||||
"long": "20분 초과",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_type_option_show": "쇼",
|
||||
"search_filters_type_option_movie": "영화",
|
||||
"search_filters_type_option_video": "동영상",
|
||||
"search_filters_date_option_year": "올해",
|
||||
"search_filters_date_option_month": "이번 달",
|
||||
"search_filters_date_option_week": "이번 주",
|
||||
"search_filters_date_option_today": "오늘",
|
||||
"search_filters_date_option_hour": "지난 1시간",
|
||||
"search_filters_sort_label": "정렬기준",
|
||||
"search_filters_features_label": "기능별",
|
||||
"search_filters_duration_option_short": "4분 미만",
|
||||
"search_filters_duration_option_long": "20분 초과",
|
||||
"footer_documentation": "문서",
|
||||
"footer_source_code": "소스 코드",
|
||||
"footer_original_source_code": "원본 소스 코드",
|
||||
|
@ -329,39 +329,39 @@
|
||||
"Videos": "Vaizdo įrašai",
|
||||
"Playlists": "Grojaraiščiai",
|
||||
"Community": "Bendruomenė",
|
||||
"relevance": "Aktualumas",
|
||||
"rating": "Reitingas",
|
||||
"date": "Įkėlimo data",
|
||||
"views": "Peržiūrų skaičius",
|
||||
"content_type": "Tipas",
|
||||
"duration": "Trukmė",
|
||||
"features": "Funkcijos",
|
||||
"sort": "Rūšiuoti pagal",
|
||||
"hour": "Per paskutinę valandą",
|
||||
"today": "Šiandien",
|
||||
"week": "Šią savaitę",
|
||||
"month": "Šį mėnesį",
|
||||
"year": "Šiais metais",
|
||||
"video": "Vaizdo įrašas",
|
||||
"channel": "Kanalas",
|
||||
"playlist": "Grojaraštis",
|
||||
"movie": "Filmas",
|
||||
"show": "Serialas",
|
||||
"hd": "HD",
|
||||
"subtitles": "Subtitrai/CC",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "Tiesiogiai",
|
||||
"4k": "4K",
|
||||
"location": "Vietovė",
|
||||
"hdr": "HDR",
|
||||
"filter": "Filtras",
|
||||
"search_filters_sort_option_relevance": "Aktualumas",
|
||||
"search_filters_sort_option_rating": "Reitingas",
|
||||
"search_filters_sort_option_date": "Įkėlimo data",
|
||||
"search_filters_sort_option_views": "Peržiūrų skaičius",
|
||||
"search_filters_type_label": "Tipas",
|
||||
"search_filters_duration_label": "Trukmė",
|
||||
"search_filters_features_label": "Funkcijos",
|
||||
"search_filters_sort_label": "Rūšiuoti pagal",
|
||||
"search_filters_date_option_hour": "Per paskutinę valandą",
|
||||
"search_filters_date_option_today": "Šiandien",
|
||||
"search_filters_date_option_week": "Šią savaitę",
|
||||
"search_filters_date_option_month": "Šį mėnesį",
|
||||
"search_filters_date_option_year": "Šiais metais",
|
||||
"search_filters_type_option_video": "Vaizdo įrašas",
|
||||
"search_filters_type_option_channel": "Kanalas",
|
||||
"search_filters_type_option_playlist": "Grojaraštis",
|
||||
"search_filters_type_option_movie": "Filmas",
|
||||
"search_filters_type_option_show": "Serialas",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "Subtitrai/CC",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "Tiesiogiai",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "Vietovė",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "Filtras",
|
||||
"Current version: ": "Dabartinė versija: ",
|
||||
"next_steps_error_message": "Po to turėtumėte pabandyti: ",
|
||||
"next_steps_error_message_refresh": "Atnaujinti",
|
||||
"next_steps_error_message_go_to_youtube": "Eiti į YouTube",
|
||||
"short": "Trumpas (< 4 minučių)",
|
||||
"long": "Ilgas (> 20 minučių)",
|
||||
"search_filters_duration_option_short": "Trumpas (< 4 minučių)",
|
||||
"search_filters_duration_option_long": "Ilgas (> 20 minučių)",
|
||||
"footer_documentation": "Dokumentacija",
|
||||
"footer_source_code": "Pirminis kodas",
|
||||
"footer_original_source_code": "Pradinis pirminis kodas",
|
||||
|
@ -329,40 +329,40 @@
|
||||
"Videos": "Videoer",
|
||||
"Playlists": "Spillelister",
|
||||
"Community": "Gemenskap",
|
||||
"relevance": "relevans",
|
||||
"rating": "vurdering",
|
||||
"date": "dato",
|
||||
"views": "visninger",
|
||||
"content_type": "innholdstype",
|
||||
"duration": "varighet",
|
||||
"features": "funksjoner",
|
||||
"sort": "sorter",
|
||||
"hour": "time",
|
||||
"today": "i dag",
|
||||
"week": "uke",
|
||||
"month": "måned",
|
||||
"year": "år",
|
||||
"video": "video",
|
||||
"channel": "kanal",
|
||||
"playlist": "spilleliste",
|
||||
"movie": "film",
|
||||
"show": "vis",
|
||||
"hd": "HD",
|
||||
"subtitles": "undertekster",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "direkte",
|
||||
"4k": "4k",
|
||||
"location": "sted",
|
||||
"hdr": "HDR",
|
||||
"filter": "filtrer",
|
||||
"search_filters_sort_option_relevance": "relevans",
|
||||
"search_filters_sort_option_rating": "vurdering",
|
||||
"search_filters_sort_option_date": "dato",
|
||||
"search_filters_sort_option_views": "visninger",
|
||||
"search_filters_type_label": "innholdstype",
|
||||
"search_filters_duration_label": "varighet",
|
||||
"search_filters_features_label": "funksjoner",
|
||||
"search_filters_sort_label": "sorter",
|
||||
"search_filters_date_option_hour": "time",
|
||||
"search_filters_date_option_today": "i dag",
|
||||
"search_filters_date_option_week": "uke",
|
||||
"search_filters_date_option_month": "måned",
|
||||
"search_filters_date_option_year": "år",
|
||||
"search_filters_type_option_video": "video",
|
||||
"search_filters_type_option_channel": "kanal",
|
||||
"search_filters_type_option_playlist": "spilleliste",
|
||||
"search_filters_type_option_movie": "film",
|
||||
"search_filters_type_option_show": "vis",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "undertekster",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "direkte",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "sted",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "filtrer",
|
||||
"Current version: ": "Gjeldende versjon: ",
|
||||
"next_steps_error_message": "Etterpå bør du prøve dette: ",
|
||||
"next_steps_error_message_refresh": "Gjenoppfrisk",
|
||||
"next_steps_error_message_go_to_youtube": "Gå til YouTube",
|
||||
"long": "Lang (> 20 minutter)",
|
||||
"search_filters_duration_option_long": "Lang (> 20 minutter)",
|
||||
"footer_donate_page": "Doner",
|
||||
"short": "Kort (< 4 minutter)",
|
||||
"search_filters_duration_option_short": "Kort (< 4 minutter)",
|
||||
"footer_documentation": "Dokumentasjon",
|
||||
"footer_source_code": "Kildekode",
|
||||
"footer_original_source_code": "Opprinnelig kildekode",
|
||||
@ -384,8 +384,8 @@
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"invidious": "Invidious",
|
||||
"purchased": "Kjøpt",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_purchased": "Kjøpt",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"none": "intet",
|
||||
"videoinfo_watch_on_youTube": "Se på YouTube",
|
||||
"videoinfo_youTube_embed_link": "Bak inn",
|
||||
|
@ -323,33 +323,33 @@
|
||||
"Videos": "Video's",
|
||||
"Playlists": "Afspeellijsten",
|
||||
"Community": "Gemeenschap",
|
||||
"relevance": "relevantie",
|
||||
"rating": "beoordeling",
|
||||
"date": "datum",
|
||||
"views": "keren bekeken",
|
||||
"content_type": "Type inhoud",
|
||||
"duration": "duur",
|
||||
"features": "eigenschappen",
|
||||
"sort": "sorteren",
|
||||
"hour": "uur",
|
||||
"today": "vandaag",
|
||||
"week": "week",
|
||||
"month": "maand",
|
||||
"year": "jaar",
|
||||
"video": "video",
|
||||
"channel": "kanaal",
|
||||
"playlist": "afspeellijst",
|
||||
"movie": "film",
|
||||
"show": "show",
|
||||
"hd": "HD",
|
||||
"subtitles": "ondertitels",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "Live",
|
||||
"4k": "4K",
|
||||
"location": "locatie",
|
||||
"hdr": "HDR",
|
||||
"filter": "verfijnen",
|
||||
"search_filters_sort_option_relevance": "relevantie",
|
||||
"search_filters_sort_option_rating": "beoordeling",
|
||||
"search_filters_sort_option_date": "datum",
|
||||
"search_filters_sort_option_views": "keren bekeken",
|
||||
"search_filters_type_label": "Type inhoud",
|
||||
"search_filters_duration_label": "duur",
|
||||
"search_filters_features_label": "eigenschappen",
|
||||
"search_filters_sort_label": "sorteren",
|
||||
"search_filters_date_option_hour": "uur",
|
||||
"search_filters_date_option_today": "vandaag",
|
||||
"search_filters_date_option_week": "week",
|
||||
"search_filters_date_option_month": "maand",
|
||||
"search_filters_date_option_year": "jaar",
|
||||
"search_filters_type_option_video": "video",
|
||||
"search_filters_type_option_channel": "kanaal",
|
||||
"search_filters_type_option_playlist": "afspeellijst",
|
||||
"search_filters_type_option_movie": "film",
|
||||
"search_filters_type_option_show": "show",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "ondertitels",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "Live",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "locatie",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "verfijnen",
|
||||
"Current version: ": "Huidige versie: ",
|
||||
"Switch Invidious Instance": "Schakel tussen de Invidious Instanties",
|
||||
"preferences_automatic_instance_redirect_label": "Automatische instantie-omleiding (terugval naar redirect.invidious.io): ",
|
||||
@ -358,7 +358,7 @@
|
||||
"preferences_category_misc": "Diverse voorkeuren",
|
||||
"preferences_show_nick_label": "Toon bijnaam bovenaan: ",
|
||||
"Released under the AGPLv3 on Github.": "Uitgebracht onder de AGPLv3 op Github.",
|
||||
"short": "Kort (<4 minuten)",
|
||||
"search_filters_duration_option_short": "Kort (<4 minuten)",
|
||||
"next_steps_error_message_refresh": "Vernieuwen",
|
||||
"next_steps_error_message_go_to_youtube": "Ga naar YouTube",
|
||||
"footer_donate_page": "Doneren",
|
||||
@ -369,7 +369,7 @@
|
||||
"Broken? Try another Invidious Instance": "Kapot? Probeer een andere Invidious Instantie",
|
||||
"next_steps_error_message": "Waarna u moet proberen om: ",
|
||||
"footer_source_code": "Bron-code",
|
||||
"long": "Lang (> 20 minuten)",
|
||||
"search_filters_duration_option_long": "Lang (> 20 minuten)",
|
||||
"preferences_quality_option_dash": "DASH (adaptieve kwaliteit)",
|
||||
"preferences_quality_option_hd720": "HD720",
|
||||
"preferences_quality_option_medium": "Gemiddeld",
|
||||
@ -397,6 +397,6 @@
|
||||
"Video unavailable": "Video onbeschikbaar",
|
||||
"preferences_save_player_pos_label": "Huidig afspeeltijdstip opslaan: ",
|
||||
"none": "geen",
|
||||
"purchased": "Gekocht",
|
||||
"360": "360º"
|
||||
"search_filters_features_option_purchased": "Gekocht",
|
||||
"search_filters_features_option_three_sixty": "360º"
|
||||
}
|
||||
|
@ -328,33 +328,33 @@
|
||||
"Videos": "Filmy",
|
||||
"Playlists": "Playlisty",
|
||||
"Community": "Społeczność",
|
||||
"relevance": "Trafność",
|
||||
"rating": "Ocena",
|
||||
"date": "data",
|
||||
"views": "Liczba wyświetleń",
|
||||
"content_type": "Typ",
|
||||
"duration": "Długość",
|
||||
"features": "Funkcje",
|
||||
"sort": "sortuj",
|
||||
"hour": "godzina",
|
||||
"today": "dzisiaj",
|
||||
"week": "tydzień",
|
||||
"month": "miesiąc",
|
||||
"year": "rok",
|
||||
"video": "Film",
|
||||
"channel": "kanał",
|
||||
"playlist": "playlista",
|
||||
"movie": "film",
|
||||
"show": "pokaż",
|
||||
"hd": "hd",
|
||||
"subtitles": "napisy",
|
||||
"creative_commons": "creative_commons",
|
||||
"3d": "3d",
|
||||
"live": "Na żywo",
|
||||
"4k": "4k",
|
||||
"location": "Lokalizacja",
|
||||
"hdr": "hdr",
|
||||
"filter": "filtr",
|
||||
"search_filters_sort_option_relevance": "Trafność",
|
||||
"search_filters_sort_option_rating": "Ocena",
|
||||
"search_filters_sort_option_date": "data",
|
||||
"search_filters_sort_option_views": "Liczba wyświetleń",
|
||||
"search_filters_type_label": "Typ",
|
||||
"search_filters_duration_label": "Długość",
|
||||
"search_filters_features_label": "Funkcje",
|
||||
"search_filters_sort_label": "sortuj",
|
||||
"search_filters_date_option_hour": "godzina",
|
||||
"search_filters_date_option_today": "dzisiaj",
|
||||
"search_filters_date_option_week": "tydzień",
|
||||
"search_filters_date_option_month": "miesiąc",
|
||||
"search_filters_date_option_year": "rok",
|
||||
"search_filters_type_option_video": "Film",
|
||||
"search_filters_type_option_channel": "kanał",
|
||||
"search_filters_type_option_playlist": "playlista",
|
||||
"search_filters_type_option_movie": "film",
|
||||
"search_filters_type_option_show": "pokaż",
|
||||
"search_filters_features_option_hd": "hd",
|
||||
"search_filters_features_option_subtitles": "napisy",
|
||||
"search_filters_features_option_c_commons": "creative_commons",
|
||||
"search_filters_features_option_three_d": "3d",
|
||||
"search_filters_features_option_live": "Na żywo",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "Lokalizacja",
|
||||
"search_filters_features_option_hdr": "hdr",
|
||||
"search_filters_label": "filtr",
|
||||
"Current version: ": "Aktualna wersja: ",
|
||||
"next_steps_error_message": "Po czym powinien*ś spróbować: ",
|
||||
"next_steps_error_message_refresh": "Odśwież",
|
||||
@ -432,8 +432,8 @@
|
||||
"preferences_quality_dash_label": "Preferowana jakość filmu DASH: ",
|
||||
"preferences_quality_dash_option_4320p": "4320p",
|
||||
"preferences_quality_dash_option_2160p": "2160p",
|
||||
"purchased": "Zakupione",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_purchased": "Zakupione",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"footer_donate_page": "Dotacja",
|
||||
"none": "żadne",
|
||||
"videoinfo_started_streaming_x_ago": "Transmisja rozpoczęta `x` temu",
|
||||
@ -447,8 +447,8 @@
|
||||
"preferences_save_player_pos_label": "Zapisz pozycję odtwarzania: ",
|
||||
"preferences_region_label": "Region zawartości: ",
|
||||
"Released under the AGPLv3 on Github.": "Wydany na licencji AGPLv3 na Github.",
|
||||
"short": "Krótkie (< 4 minutes)",
|
||||
"long": "Długie (> 20 minutes)",
|
||||
"search_filters_duration_option_short": "Krótkie (< 4 minutes)",
|
||||
"search_filters_duration_option_long": "Długie (> 20 minutes)",
|
||||
"footer_documentation": "Dokumentacja",
|
||||
"footer_source_code": "Kod źródłowy",
|
||||
"footer_modfied_source_code": "Zmodyfikowany Kod źródłowy",
|
||||
|
@ -345,41 +345,41 @@
|
||||
"Videos": "Vídeos",
|
||||
"Playlists": "Listas de reprodução",
|
||||
"Community": "Comunidade",
|
||||
"relevance": "relevância",
|
||||
"rating": "avaliação",
|
||||
"date": "data",
|
||||
"views": "visualizações",
|
||||
"content_type": "content_type",
|
||||
"duration": "duração",
|
||||
"features": "recursos",
|
||||
"sort": "ordenar",
|
||||
"hour": "hora",
|
||||
"today": "hoje",
|
||||
"week": "semana",
|
||||
"month": "mês",
|
||||
"year": "ano",
|
||||
"video": "vídeo",
|
||||
"channel": "Canal",
|
||||
"playlist": "playlist",
|
||||
"movie": "filme",
|
||||
"show": "show",
|
||||
"hd": "hd",
|
||||
"subtitles": "legendas",
|
||||
"creative_commons": "creative_commons",
|
||||
"3d": "3d",
|
||||
"live": "ao vivo",
|
||||
"4k": "4k",
|
||||
"location": "localização",
|
||||
"hdr": "hdr",
|
||||
"filter": "filtro",
|
||||
"search_filters_sort_option_relevance": "relevância",
|
||||
"search_filters_sort_option_rating": "avaliação",
|
||||
"search_filters_sort_option_date": "data",
|
||||
"search_filters_sort_option_views": "visualizações",
|
||||
"search_filters_type_label": "content_type",
|
||||
"search_filters_duration_label": "duração",
|
||||
"search_filters_features_label": "recursos",
|
||||
"search_filters_sort_label": "ordenar",
|
||||
"search_filters_date_option_hour": "hora",
|
||||
"search_filters_date_option_today": "hoje",
|
||||
"search_filters_date_option_week": "semana",
|
||||
"search_filters_date_option_month": "mês",
|
||||
"search_filters_date_option_year": "ano",
|
||||
"search_filters_type_option_video": "vídeo",
|
||||
"search_filters_type_option_channel": "Canal",
|
||||
"search_filters_type_option_playlist": "playlist",
|
||||
"search_filters_type_option_movie": "filme",
|
||||
"search_filters_type_option_show": "show",
|
||||
"search_filters_features_option_hd": "hd",
|
||||
"search_filters_features_option_subtitles": "legendas",
|
||||
"search_filters_features_option_c_commons": "creative_commons",
|
||||
"search_filters_features_option_three_d": "3d",
|
||||
"search_filters_features_option_live": "ao vivo",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "localização",
|
||||
"search_filters_features_option_hdr": "hdr",
|
||||
"search_filters_label": "filtro",
|
||||
"Current version: ": "Versão atual: ",
|
||||
"next_steps_error_message": "Depois disso, você deve tentar: ",
|
||||
"next_steps_error_message_refresh": "Atualizar",
|
||||
"next_steps_error_message_go_to_youtube": "Ir para o YouTube",
|
||||
"footer_donate_page": "Doe",
|
||||
"adminprefs_modified_source_code_url_label": "URL para repositório de código fonte modificado",
|
||||
"long": "Longo (> 20 minutos)",
|
||||
"short": "Curto (< 4 minutos)",
|
||||
"search_filters_duration_option_long": "Longo (> 20 minutos)",
|
||||
"search_filters_duration_option_short": "Curto (< 4 minutos)",
|
||||
"footer_documentation": "Documentação",
|
||||
"footer_source_code": "Código fonte",
|
||||
"footer_original_source_code": "Código fonte original",
|
||||
@ -404,7 +404,7 @@
|
||||
"crash_page_you_found_a_bug": "Parece que você encontrou um erro no Invidious!",
|
||||
"crash_page_before_reporting": "Antes de reportar um erro, verifique se você:",
|
||||
"preferences_save_player_pos_label": "Salvar a posição de reprodução: ",
|
||||
"purchased": "Comprado",
|
||||
"search_filters_features_option_purchased": "Comprado",
|
||||
"crash_page_refresh": "tentou <a href=\"`x`\">recarregar a página</a>",
|
||||
"crash_page_switch_instance": "tentou <a href=\"`x`\">usar outra instância</a>",
|
||||
"crash_page_search_issue": "procurou por um <a href=\"`x`\">erro existente no Github</a>",
|
||||
@ -428,7 +428,7 @@
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"invidious": "Invidious",
|
||||
"preferences_quality_option_medium": "Médio",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"none": "none",
|
||||
"videoinfo_watch_on_youTube": "Assistir no YouTube",
|
||||
"videoinfo_youTube_embed_link": "Embutir",
|
||||
|
@ -345,33 +345,33 @@
|
||||
"Videos": "Vídeos",
|
||||
"Playlists": "Listas de reprodução",
|
||||
"Community": "Comunidade",
|
||||
"relevance": "Relevância",
|
||||
"rating": "Avaliação",
|
||||
"date": "Data de envio",
|
||||
"views": "Visualizações",
|
||||
"content_type": "Tipo",
|
||||
"duration": "Duração",
|
||||
"features": "Funcionalidades",
|
||||
"sort": "Ordenar por",
|
||||
"hour": "Última hora",
|
||||
"today": "Hoje",
|
||||
"week": "Esta semana",
|
||||
"month": "Este mês",
|
||||
"year": "Este ano",
|
||||
"video": "Vídeo",
|
||||
"channel": "Canal",
|
||||
"playlist": "Lista de reprodução",
|
||||
"movie": "Filme",
|
||||
"show": "Espetáculo",
|
||||
"hd": "HD",
|
||||
"subtitles": "Legendas",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "Em direto",
|
||||
"4k": "4K",
|
||||
"location": "Localização",
|
||||
"hdr": "HDR",
|
||||
"filter": "Filtro",
|
||||
"search_filters_sort_option_relevance": "Relevância",
|
||||
"search_filters_sort_option_rating": "Avaliação",
|
||||
"search_filters_sort_option_date": "Data de envio",
|
||||
"search_filters_sort_option_views": "Visualizações",
|
||||
"search_filters_type_label": "Tipo",
|
||||
"search_filters_duration_label": "Duração",
|
||||
"search_filters_features_label": "Funcionalidades",
|
||||
"search_filters_sort_label": "Ordenar por",
|
||||
"search_filters_date_option_hour": "Última hora",
|
||||
"search_filters_date_option_today": "Hoje",
|
||||
"search_filters_date_option_week": "Esta semana",
|
||||
"search_filters_date_option_month": "Este mês",
|
||||
"search_filters_date_option_year": "Este ano",
|
||||
"search_filters_type_option_video": "Vídeo",
|
||||
"search_filters_type_option_channel": "Canal",
|
||||
"search_filters_type_option_playlist": "Lista de reprodução",
|
||||
"search_filters_type_option_movie": "Filme",
|
||||
"search_filters_type_option_show": "Espetáculo",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "Legendas",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "Em direto",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "Localização",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "Filtro",
|
||||
"Current version: ": "Versão atual: ",
|
||||
"next_steps_error_message": "Pode tentar as seguintes opções: ",
|
||||
"next_steps_error_message_refresh": "Atualizar",
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"show": "Espetáculo",
|
||||
"views": "Visualizações",
|
||||
"date": "Data de envio",
|
||||
"rating": "Avaliação",
|
||||
"relevance": "Relevância",
|
||||
"search_filters_type_option_show": "Espetáculo",
|
||||
"search_filters_sort_option_views": "Visualizações",
|
||||
"search_filters_sort_option_date": "Data de envio",
|
||||
"search_filters_sort_option_rating": "Avaliação",
|
||||
"search_filters_sort_option_relevance": "Relevância",
|
||||
"Broken? Try another Invidious Instance": "Falhou? Tente outra Instância do Invidious",
|
||||
"Switch Invidious Instance": "Mudar a instância do Invidious",
|
||||
"Show less": "Mostrar menos",
|
||||
@ -17,28 +17,28 @@
|
||||
"next_steps_error_message_go_to_youtube": "Ir ao YouTube",
|
||||
"next_steps_error_message": "Pode tentar as seguintes opções: ",
|
||||
"next_steps_error_message_refresh": "Atualizar",
|
||||
"filter": "Filtro",
|
||||
"hdr": "HDR",
|
||||
"location": "Localização",
|
||||
"4k": "4K",
|
||||
"live": "Em direto",
|
||||
"3d": "3D",
|
||||
"creative_commons": "Creative Commons",
|
||||
"subtitles": "Legendas",
|
||||
"hd": "HD",
|
||||
"movie": "Filme",
|
||||
"playlist": "Lista de reprodução",
|
||||
"channel": "Canal",
|
||||
"video": "Vídeo",
|
||||
"year": "Este ano",
|
||||
"month": "Este mês",
|
||||
"week": "Esta semana",
|
||||
"today": "Hoje",
|
||||
"hour": "Última hora",
|
||||
"sort": "Ordenar por",
|
||||
"features": "Funcionalidades",
|
||||
"duration": "Duração",
|
||||
"content_type": "Tipo",
|
||||
"search_filters_label": "Filtro",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_features_option_location": "Localização",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_live": "Em direto",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_subtitles": "Legendas",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_type_option_movie": "Filme",
|
||||
"search_filters_type_option_playlist": "Lista de reprodução",
|
||||
"search_filters_type_option_channel": "Canal",
|
||||
"search_filters_type_option_video": "Vídeo",
|
||||
"search_filters_date_option_year": "Este ano",
|
||||
"search_filters_date_option_month": "Este mês",
|
||||
"search_filters_date_option_week": "Esta semana",
|
||||
"search_filters_date_option_today": "Hoje",
|
||||
"search_filters_date_option_hour": "Última hora",
|
||||
"search_filters_sort_label": "Ordenar por",
|
||||
"search_filters_features_label": "Funcionalidades",
|
||||
"search_filters_duration_label": "Duração",
|
||||
"search_filters_type_label": "Tipo",
|
||||
"permalink": "hiperligação permanente",
|
||||
"YouTube comment permalink": "Hiperligação permanente do comentário no YouTube",
|
||||
"Download as: ": "Descarregar como: ",
|
||||
@ -376,8 +376,8 @@
|
||||
"Unsubscribe": "Anular subscrição",
|
||||
"Shared `x` ago": "Partilhado `x` atrás",
|
||||
"LIVE": "Em direto",
|
||||
"short": "Curto (< 4 minutos)",
|
||||
"long": "Longo (> 20 minutos)",
|
||||
"search_filters_duration_option_short": "Curto (< 4 minutos)",
|
||||
"search_filters_duration_option_long": "Longo (> 20 minutos)",
|
||||
"footer_source_code": "Código-fonte",
|
||||
"footer_original_source_code": "Código-fonte original",
|
||||
"adminprefs_modified_source_code_url_label": "URL do repositório do código-fonte alterado",
|
||||
@ -397,8 +397,8 @@
|
||||
"preferences_quality_dash_option_360p": "360p",
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"purchased": "Comprado",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_purchased": "Comprado",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"videoinfo_invidious_embed_link": "Incorporar hiperligação",
|
||||
"Video unavailable": "Vídeo não disponível",
|
||||
"invidious": "Invidious",
|
||||
|
@ -329,39 +329,39 @@
|
||||
"Videos": "Видео",
|
||||
"Playlists": "Плейлисты",
|
||||
"Community": "Сообщество",
|
||||
"relevance": "Актуальность",
|
||||
"rating": "Рейтинг",
|
||||
"date": "Дата загрузки",
|
||||
"views": "Просмотры",
|
||||
"content_type": "Тип",
|
||||
"duration": "Длительность",
|
||||
"features": "Функции",
|
||||
"sort": "Сортировать по",
|
||||
"hour": "Последний час",
|
||||
"today": "Сегодня",
|
||||
"week": "Эта неделя",
|
||||
"month": "Этот месяц",
|
||||
"year": "Этот год",
|
||||
"video": "Видео",
|
||||
"channel": "Канал",
|
||||
"playlist": "Плейлист",
|
||||
"movie": "Фильм",
|
||||
"show": "Показать",
|
||||
"hd": "HD",
|
||||
"subtitles": "Субтитры",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "Прямой эфир",
|
||||
"4k": "4K",
|
||||
"location": "Местоположение",
|
||||
"hdr": "HDR",
|
||||
"filter": "Фильтр",
|
||||
"search_filters_sort_option_relevance": "Актуальность",
|
||||
"search_filters_sort_option_rating": "Рейтинг",
|
||||
"search_filters_sort_option_date": "Дата загрузки",
|
||||
"search_filters_sort_option_views": "Просмотры",
|
||||
"search_filters_type_label": "Тип",
|
||||
"search_filters_duration_label": "Длительность",
|
||||
"search_filters_features_label": "Функции",
|
||||
"search_filters_sort_label": "Сортировать по",
|
||||
"search_filters_date_option_hour": "Последний час",
|
||||
"search_filters_date_option_today": "Сегодня",
|
||||
"search_filters_date_option_week": "Эта неделя",
|
||||
"search_filters_date_option_month": "Этот месяц",
|
||||
"search_filters_date_option_year": "Этот год",
|
||||
"search_filters_type_option_video": "Видео",
|
||||
"search_filters_type_option_channel": "Канал",
|
||||
"search_filters_type_option_playlist": "Плейлист",
|
||||
"search_filters_type_option_movie": "Фильм",
|
||||
"search_filters_type_option_show": "Показать",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "Субтитры",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "Прямой эфир",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "Местоположение",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "Фильтр",
|
||||
"Current version: ": "Текущая версия: ",
|
||||
"next_steps_error_message": "После чего следует попробовать: ",
|
||||
"next_steps_error_message_refresh": "Обновить",
|
||||
"next_steps_error_message_go_to_youtube": "Перейти на YouTube",
|
||||
"short": "Короткие (< 4 минут)",
|
||||
"long": "Длинные (> 20 минут)",
|
||||
"search_filters_duration_option_short": "Короткие (< 4 минут)",
|
||||
"search_filters_duration_option_long": "Длинные (> 20 минут)",
|
||||
"preferences_quality_dash_option_best": "Наилучшее",
|
||||
"generic_count_weeks_0": "{{count}} неделя",
|
||||
"generic_count_weeks_1": "{{count}} недели",
|
||||
@ -437,7 +437,7 @@
|
||||
"generic_count_seconds_0": "{{count}} секунда",
|
||||
"generic_count_seconds_1": "{{count}} секунды",
|
||||
"generic_count_seconds_2": "{{count}} секунд",
|
||||
"purchased": "Приобретено",
|
||||
"search_filters_features_option_purchased": "Приобретено",
|
||||
"videoinfo_started_streaming_x_ago": "Трансляция началась `x` назад",
|
||||
"crash_page_switch_instance": "пробовали <a href=\"`x`\">использовать другое зеркало</a>",
|
||||
"crash_page_read_the_faq": "прочли <a href=\"`x`\">Частые Вопросы (ЧаВо)</a>",
|
||||
@ -473,7 +473,7 @@
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"invidious": "Invidious",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"Video unavailable": "Видео недоступно",
|
||||
"preferences_save_player_pos_label": "Запоминать позицию: ",
|
||||
"preferences_region_label": "Страна: ",
|
||||
|
@ -26,11 +26,11 @@
|
||||
"Tamil": "Tamilisht",
|
||||
"Telugu": "Telugu",
|
||||
"Vietnamese": "Vietnamisht",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3D",
|
||||
"live": "Drejtpërsëdrejti",
|
||||
"4k": "4K",
|
||||
"location": "Vendndodhja",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "Drejtpërsëdrejti",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "Vendndodhja",
|
||||
"videoinfo_watch_on_youTube": "Shiheni në YouTube",
|
||||
"videoinfo_youTube_embed_link": "Trupëzojeni",
|
||||
"videoinfo_invidious_embed_link": "Lidhje Trupëzimi",
|
||||
@ -261,32 +261,32 @@
|
||||
"Audio mode": "Mënyrë për audion",
|
||||
"Playlists": "Luajlista",
|
||||
"Community": "Bashkësi",
|
||||
"relevance": "Rëndësi",
|
||||
"search_filters_sort_option_relevance": "Rëndësi",
|
||||
"Video mode": "Mënyrë video",
|
||||
"Videos": "Video",
|
||||
"rating": "Vlerësim",
|
||||
"date": "Datë ngarkimi",
|
||||
"views": "Numër parjesh",
|
||||
"content_type": "Lloj",
|
||||
"duration": "Kohëzgjatje",
|
||||
"features": "Veçori",
|
||||
"sort": "Renditi Sipas",
|
||||
"hour": "Orën e Fundit",
|
||||
"today": "Sot",
|
||||
"long": "E gjatë (> 20 minuta)",
|
||||
"hd": "HD",
|
||||
"subtitles": "Titra/CC",
|
||||
"hdr": "HDR",
|
||||
"week": "Këtë javë",
|
||||
"month": "Këtë muaj",
|
||||
"year": "Këtë vit",
|
||||
"video": "Video",
|
||||
"channel": "Kanal",
|
||||
"playlist": "Luajlistë",
|
||||
"movie": "Film",
|
||||
"show": "Shfaqe",
|
||||
"short": "E shkurtër (< 4 minuta)",
|
||||
"purchased": "Të blera",
|
||||
"search_filters_sort_option_rating": "Vlerësim",
|
||||
"search_filters_sort_option_date": "Datë ngarkimi",
|
||||
"search_filters_sort_option_views": "Numër parjesh",
|
||||
"search_filters_type_label": "Lloj",
|
||||
"search_filters_duration_label": "Kohëzgjatje",
|
||||
"search_filters_features_label": "Veçori",
|
||||
"search_filters_sort_label": "Renditi Sipas",
|
||||
"search_filters_date_option_hour": "Orën e Fundit",
|
||||
"search_filters_date_option_today": "Sot",
|
||||
"search_filters_duration_option_long": "E gjatë (> 20 minuta)",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "Titra/CC",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_date_option_week": "Këtë javë",
|
||||
"search_filters_date_option_month": "Këtë muaj",
|
||||
"search_filters_date_option_year": "Këtë vit",
|
||||
"search_filters_type_option_video": "Video",
|
||||
"search_filters_type_option_channel": "Kanal",
|
||||
"search_filters_type_option_playlist": "Luajlistë",
|
||||
"search_filters_type_option_movie": "Film",
|
||||
"search_filters_type_option_show": "Shfaqe",
|
||||
"search_filters_duration_option_short": "E shkurtër (< 4 minuta)",
|
||||
"search_filters_features_option_purchased": "Të blera",
|
||||
"footer_modfied_source_code": "Kod Burim i ndryshuar",
|
||||
"adminprefs_modified_source_code_url_label": "URL e depos së ndryshuar të kodit burim",
|
||||
"none": "asnjë",
|
||||
@ -370,8 +370,8 @@
|
||||
"Mongolian": "Mongolisht",
|
||||
"Nepali": "Nepaleze",
|
||||
"Norwegian Bokmål": "Norvegjishte Bokmål",
|
||||
"360": "360°",
|
||||
"filter": "Filtroji",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"search_filters_label": "Filtroji",
|
||||
"Current version: ": "Versioni i tanishëm: ",
|
||||
"next_steps_error_message": "Pas të cilës duhet të provoni të: ",
|
||||
"next_steps_error_message_refresh": "Rifreskoje",
|
||||
|
@ -131,26 +131,26 @@
|
||||
"YouTube comment permalink": "YouTube komentar trajna veza",
|
||||
"Audio mode": "Audio mod",
|
||||
"Playlists": "Plej liste",
|
||||
"relevance": "Relevantnost",
|
||||
"rating": "Ocene",
|
||||
"date": "Datum otpremanja",
|
||||
"views": "Broj pregleda",
|
||||
"search_filters_sort_option_relevance": "Relevantnost",
|
||||
"search_filters_sort_option_rating": "Ocene",
|
||||
"search_filters_sort_option_date": "Datum otpremanja",
|
||||
"search_filters_sort_option_views": "Broj pregleda",
|
||||
"`x` marked it with a ❤": "`x` je označio/la ovo sa ❤",
|
||||
"duration": "Trajanje",
|
||||
"features": "Karakteristike",
|
||||
"hour": "Poslednji sat",
|
||||
"week": "Ove sedmice",
|
||||
"month": "Ovaj mesec",
|
||||
"year": "Ove godine",
|
||||
"video": "Video",
|
||||
"playlist": "Plej lista",
|
||||
"movie": "Film",
|
||||
"long": "Dugo (> 20 minuta)",
|
||||
"hd": "HD",
|
||||
"creative_commons": "Creative Commons (Licenca)",
|
||||
"3d": "3D",
|
||||
"hdr": "Video Visoke Rezolucije",
|
||||
"filter": "Filter",
|
||||
"search_filters_duration_label": "Trajanje",
|
||||
"search_filters_features_label": "Karakteristike",
|
||||
"search_filters_date_option_hour": "Poslednji sat",
|
||||
"search_filters_date_option_week": "Ove sedmice",
|
||||
"search_filters_date_option_month": "Ovaj mesec",
|
||||
"search_filters_date_option_year": "Ove godine",
|
||||
"search_filters_type_option_video": "Video",
|
||||
"search_filters_type_option_playlist": "Plej lista",
|
||||
"search_filters_type_option_movie": "Film",
|
||||
"search_filters_duration_option_long": "Dugo (> 20 minuta)",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_c_commons": "Creative Commons (Licenca)",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_hdr": "Video Visoke Rezolucije",
|
||||
"search_filters_label": "Filter",
|
||||
"next_steps_error_message": "Nakon čega bi trebali probati: ",
|
||||
"next_steps_error_message_go_to_youtube": "Idi na YouTube",
|
||||
"footer_documentation": "Dokumentacija",
|
||||
@ -225,13 +225,13 @@
|
||||
"preferences_category_visual": "Vizuelne preference",
|
||||
"preferences_captions_label": "Podrazumevani titl: ",
|
||||
"Music": "Muzika",
|
||||
"content_type": "Tip",
|
||||
"search_filters_type_label": "Tip",
|
||||
"Broken? Try another Invidious Instance": "Ne funkcioniše ispravno? Probajte drugu Invidious instancu",
|
||||
"Tamil": "Tamilski",
|
||||
"Save preferences": "Sačuvaj podešavanja",
|
||||
"Only show latest unwatched video from channel: ": "Prikaži samo poslednje video klipove koji nisu pogledani sa kanala: ",
|
||||
"Xhosa": "Kosa (Jezik)",
|
||||
"channel": "Kanal",
|
||||
"search_filters_type_option_channel": "Kanal",
|
||||
"Hungarian": "Mađarski",
|
||||
"Maori": "Maori (Jezik)",
|
||||
"Manage subscriptions": "Upravljaj zapisima",
|
||||
@ -243,7 +243,7 @@
|
||||
"preferences_default_home_label": "Podrazumevana početna stranica: ",
|
||||
"Serbian": "Srpski",
|
||||
"License: ": "Licenca: ",
|
||||
"live": "Uživo",
|
||||
"search_filters_features_option_live": "Uživo",
|
||||
"Report statistics: ": "Izveštavaj o statistici: ",
|
||||
"Only show latest video from channel: ": "Prikazuj poslednje video klipove samo sa kanala: ",
|
||||
"channel name - reverse": "ime kanala - obrnuto",
|
||||
@ -266,12 +266,12 @@
|
||||
"alphabetically": "po alfabetu",
|
||||
"No such user": "Nepostojeći korisnik",
|
||||
"Subscriptions": "Praćenja",
|
||||
"today": "Danas",
|
||||
"search_filters_date_option_today": "Danas",
|
||||
"Finnish": "Finski",
|
||||
"Lao": "Laoski",
|
||||
"Login enabled: ": "Prijava omogućena: ",
|
||||
"Shona": "Šona",
|
||||
"location": "Lokacija",
|
||||
"search_filters_features_option_location": "Lokacija",
|
||||
"Load more": "Učitaj više",
|
||||
"Released under the AGPLv3 on Github.": "Izbačeno pod licencom AGPLv3 na Github-u.",
|
||||
"Slovenian": "Slovenački",
|
||||
@ -292,7 +292,7 @@
|
||||
"Czech": "Češki",
|
||||
"Latin": "Latinski",
|
||||
"Videos": "Video klipovi",
|
||||
"4k": "4К",
|
||||
"search_filters_features_option_four_k": "4К",
|
||||
"footer_donate_page": "Doniraj",
|
||||
"English": "Engleski",
|
||||
"Arabic": "Arapski",
|
||||
@ -310,7 +310,7 @@
|
||||
"Swahili": "Svahili",
|
||||
"Yiddish": "Jidiš",
|
||||
"Zulu": "Zulu",
|
||||
"subtitles": "Titl/Prevod",
|
||||
"search_filters_features_option_subtitles": "Titl/Prevod",
|
||||
"Password cannot be longer than 55 characters": "Lozinka ne može biti duža od 55 karaktera",
|
||||
"This channel does not exist.": "Ovaj kanal ne postoji.",
|
||||
"Belarusian": "Beloruski",
|
||||
@ -329,9 +329,9 @@
|
||||
"Clear watch history": "Obriši istoriju gledanja",
|
||||
"preferences_category_admin": "Administratorska podešavanja",
|
||||
"published": "objavljeno",
|
||||
"sort": "Poredaj prema",
|
||||
"show": "Emisija",
|
||||
"short": "Kratko (< 4 minute)",
|
||||
"search_filters_sort_label": "Poredaj prema",
|
||||
"search_filters_type_option_show": "Emisija",
|
||||
"search_filters_duration_option_short": "Kratko (< 4 minute)",
|
||||
"Current version: ": "Trenutna verzija: ",
|
||||
"Top enabled: ": "Vrh omogućen: ",
|
||||
"Public": "Javno",
|
||||
|
@ -182,14 +182,14 @@
|
||||
"Georgian": "Грузијски",
|
||||
"Greek": "Грчки",
|
||||
"Hausa": "Хауса",
|
||||
"video": "Видео",
|
||||
"playlist": "Плеј листа",
|
||||
"movie": "Филм",
|
||||
"long": "Дуго (> 20 минута)",
|
||||
"creative_commons": "Creative Commons (Лиценца)",
|
||||
"live": "Уживо",
|
||||
"location": "Локација",
|
||||
"filter": "Филтер",
|
||||
"search_filters_type_option_video": "Видео",
|
||||
"search_filters_type_option_playlist": "Плеј листа",
|
||||
"search_filters_type_option_movie": "Филм",
|
||||
"search_filters_duration_option_long": "Дуго (> 20 минута)",
|
||||
"search_filters_features_option_c_commons": "Creative Commons (Лиценца)",
|
||||
"search_filters_features_option_live": "Уживо",
|
||||
"search_filters_features_option_location": "Локација",
|
||||
"search_filters_label": "Филтер",
|
||||
"next_steps_error_message": "Након чега би требали пробати: ",
|
||||
"footer_donate_page": "Донирај",
|
||||
"footer_documentation": "Документација",
|
||||
@ -247,9 +247,9 @@
|
||||
"`x` marked it with a ❤": "`x` је означио/ла ово са ❤",
|
||||
"Audio mode": "Аудио мод",
|
||||
"Videos": "Видео клипови",
|
||||
"views": "Број прегледа",
|
||||
"features": "Карактеристике",
|
||||
"today": "Данас",
|
||||
"search_filters_sort_option_views": "Број прегледа",
|
||||
"search_filters_features_label": "Карактеристике",
|
||||
"search_filters_date_option_today": "Данас",
|
||||
"%A %B %-d, %Y": "%A %B %-d, %Y",
|
||||
"preferences_locale_label": "Језик: ",
|
||||
"Persian": "Перзијски",
|
||||
@ -257,7 +257,7 @@
|
||||
"": "Прикажи `x` коментара",
|
||||
"([^.,0-9]|^)1([^.,0-9]|$)": "Прикажи `x` коментар"
|
||||
},
|
||||
"channel": "Канал",
|
||||
"search_filters_type_option_channel": "Канал",
|
||||
"Haitian Creole": "Хаићански Креолски",
|
||||
"Armenian": "Јерменски",
|
||||
"next_steps_error_message_go_to_youtube": "Иди на YouTube",
|
||||
@ -265,10 +265,10 @@
|
||||
"preferences_vr_mode_label": "Интерактивни видео клипови у 360 степени: ",
|
||||
"Switch Invidious Instance": "Промени Invidious инстанцу",
|
||||
"Portuguese": "Португалски",
|
||||
"week": "Ове седмице",
|
||||
"show": "Емисија",
|
||||
"search_filters_date_option_week": "Ове седмице",
|
||||
"search_filters_type_option_show": "Емисија",
|
||||
"Fallback comments: ": "Коментари у случају отказивања: ",
|
||||
"hdr": "Видео Високе Резолуције",
|
||||
"search_filters_features_option_hdr": "Видео Високе Резолуције",
|
||||
"About": "О програму",
|
||||
"Kazakh": "Казашки",
|
||||
"Shared `x`": "Подељено `x`",
|
||||
@ -277,7 +277,7 @@
|
||||
"Erroneous challenge": "Погрешан изазов",
|
||||
"Danish": "Дански",
|
||||
"Could not get channel info.": "Узимање података о каналу није успело.",
|
||||
"hd": "HD",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"Slovenian": "Словеначки",
|
||||
"Load more": "Учитај више",
|
||||
"German": "Немачки",
|
||||
@ -288,12 +288,12 @@
|
||||
"Southern Sotho": "Јужни Сото",
|
||||
"Popular": "Популарно",
|
||||
"Gujarati": "Гуџарати",
|
||||
"year": "Ове године",
|
||||
"search_filters_date_option_year": "Ове године",
|
||||
"Irish": "Ирски",
|
||||
"YouTube comment permalink": "YouTube коментар трајна веза",
|
||||
"Malagasy": "Малгашки",
|
||||
"Token is expired, please try again": "Жетон је истекао, молимо вас да покушате поново",
|
||||
"short": "Кратко (< 4 минуте)",
|
||||
"search_filters_duration_option_short": "Кратко (< 4 минуте)",
|
||||
"Samoan": "Самоански",
|
||||
"Tamil": "Тамилски",
|
||||
"Ukrainian": "Украјински",
|
||||
@ -307,20 +307,20 @@
|
||||
"Lithuanian": "Литвански",
|
||||
"Icelandic": "Исландски",
|
||||
"Thai": "Тајски",
|
||||
"month": "Овај месец",
|
||||
"content_type": "Тип",
|
||||
"hour": "Последњи сат",
|
||||
"search_filters_date_option_month": "Овај месец",
|
||||
"search_filters_type_label": "Тип",
|
||||
"search_filters_date_option_hour": "Последњи сат",
|
||||
"Spanish": "Шпански",
|
||||
"date": "Датум отпремања",
|
||||
"search_filters_sort_option_date": "Датум отпремања",
|
||||
"View as playlist": "Погледај као плеј листу",
|
||||
"relevance": "Релевантност",
|
||||
"search_filters_sort_option_relevance": "Релевантност",
|
||||
"Estonian": "Естонски",
|
||||
"Sinhala": "Синхалешки",
|
||||
"Corsican": "Корзикански",
|
||||
"Filipino": "Филипино",
|
||||
"Gaming": "Игрице",
|
||||
"Movies": "Филмови",
|
||||
"rating": "Оцене",
|
||||
"search_filters_sort_option_rating": "Оцене",
|
||||
"Top enabled: ": "Врх омогућен: ",
|
||||
"Released under the AGPLv3 on Github.": "Избачено под лиценцом AGPLv3 на Github-у.",
|
||||
"Afrikaans": "Африканс",
|
||||
@ -340,9 +340,9 @@
|
||||
"Swedish": "Шведски",
|
||||
"Music": "Музика",
|
||||
"Download as: ": "Преузми као: ",
|
||||
"duration": "Трајање",
|
||||
"sort": "Поредај према",
|
||||
"subtitles": "Титл/Превод",
|
||||
"search_filters_duration_label": "Трајање",
|
||||
"search_filters_sort_label": "Поредај према",
|
||||
"search_filters_features_option_subtitles": "Титл/Превод",
|
||||
"preferences_extend_desc_label": "Аутоматски прикажи цео опис видеа: ",
|
||||
"Show less": "Прикажи мање",
|
||||
"Broken? Try another Invidious Instance": "Не функционише исправно? Пробајте другу Invidious инстанцу",
|
||||
@ -359,8 +359,8 @@
|
||||
"Top": "Врх",
|
||||
"Video mode": "Видео мод",
|
||||
"footer_source_code": "Изворна Кода",
|
||||
"3d": "3D",
|
||||
"4k": "4K",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"Erroneous CAPTCHA": "Погрешна CAPTCHA",
|
||||
"`x` ago": "пре `x`",
|
||||
"Arabic": "Арапски",
|
||||
|
@ -327,39 +327,39 @@
|
||||
"Videos": "Videor",
|
||||
"Playlists": "Spellistor",
|
||||
"Community": "Gemenskap",
|
||||
"relevance": "Relevans",
|
||||
"rating": "Rankning",
|
||||
"date": "datum",
|
||||
"views": "visningar",
|
||||
"content_type": "Typ",
|
||||
"duration": "Varaktighet",
|
||||
"features": "Funktioner",
|
||||
"sort": "Sortera efter",
|
||||
"hour": "timme",
|
||||
"today": "idag",
|
||||
"week": "vecka",
|
||||
"month": "månad",
|
||||
"year": "år",
|
||||
"video": "video",
|
||||
"channel": "kanal",
|
||||
"playlist": "spellista",
|
||||
"movie": "film",
|
||||
"show": "tv-serie",
|
||||
"hd": "hd",
|
||||
"subtitles": "undertexter",
|
||||
"creative_commons": "creative_commons",
|
||||
"3d": "3d",
|
||||
"live": "live",
|
||||
"4k": "4k",
|
||||
"location": "plats",
|
||||
"hdr": "hdr",
|
||||
"filter": "Filter",
|
||||
"search_filters_sort_option_relevance": "Relevans",
|
||||
"search_filters_sort_option_rating": "Rankning",
|
||||
"search_filters_sort_option_date": "Datum",
|
||||
"search_filters_sort_option_views": "Visningar",
|
||||
"search_filters_type_label": "Typ",
|
||||
"search_filters_duration_label": "Varaktighet",
|
||||
"search_filters_features_label": "Funktioner",
|
||||
"search_filters_sort_label": "Sortera efter",
|
||||
"search_filters_date_option_hour": "timme",
|
||||
"search_filters_date_option_today": "idag",
|
||||
"search_filters_date_option_week": "vecka",
|
||||
"search_filters_date_option_month": "månad",
|
||||
"search_filters_date_option_year": "år",
|
||||
"search_filters_type_option_video": "video",
|
||||
"search_filters_type_option_channel": "kanal",
|
||||
"search_filters_type_option_playlist": "spellista",
|
||||
"search_filters_type_option_movie": "film",
|
||||
"search_filters_type_option_show": "tv-serie",
|
||||
"search_filters_features_option_hd": "hd",
|
||||
"search_filters_features_option_subtitles": "undertexter",
|
||||
"search_filters_features_option_c_commons": "creative_commons",
|
||||
"search_filters_features_option_three_d": "3d",
|
||||
"search_filters_features_option_live": "live",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "plats",
|
||||
"search_filters_features_option_hdr": "hdr",
|
||||
"search_filters_label": "Filter",
|
||||
"Current version: ": "Nuvarande version: ",
|
||||
"next_steps_error_message_refresh": "Uppdatera",
|
||||
"next_steps_error_message_go_to_youtube": "Gå till Youtube",
|
||||
"Released under the AGPLv3 on Github.": "Publicerad under AGPLv3 på Github.",
|
||||
"footer_source_code": "Källkod",
|
||||
"long": "Lång (> 20 minuter)",
|
||||
"search_filters_duration_option_long": "Lång (> 20 minuter)",
|
||||
"footer_documentation": "Dokumentation",
|
||||
"short": "Kort (< 4 minuter)"
|
||||
"search_filters_duration_option_short": "Kort (< 4 minuter)"
|
||||
}
|
||||
|
@ -329,39 +329,39 @@
|
||||
"Videos": "Videolar",
|
||||
"Playlists": "Oynatma listeleri",
|
||||
"Community": "Topluluk",
|
||||
"relevance": "İlgi",
|
||||
"rating": "Değerlendirme",
|
||||
"date": "Yükleme tarihi",
|
||||
"views": "Görüntüleme sayısı",
|
||||
"content_type": "Tür",
|
||||
"duration": "Süre",
|
||||
"features": "Özellikler",
|
||||
"sort": "Sıralama Ölçütü",
|
||||
"hour": "Son Saat",
|
||||
"today": "Bugün",
|
||||
"week": "Bu hafta",
|
||||
"month": "Bu ay",
|
||||
"year": "Bu yıl",
|
||||
"video": "Video",
|
||||
"channel": "Kanal",
|
||||
"playlist": "Oynatma listesi",
|
||||
"movie": "Film",
|
||||
"show": "Gösteri",
|
||||
"hd": "HD",
|
||||
"subtitles": "Alt yazılar",
|
||||
"creative_commons": "Creative Commons",
|
||||
"3d": "3B",
|
||||
"live": "Canlı",
|
||||
"4k": "4K",
|
||||
"location": "Konum",
|
||||
"hdr": "HDR",
|
||||
"filter": "Filtrele",
|
||||
"search_filters_sort_option_relevance": "İlgi",
|
||||
"search_filters_sort_option_rating": "Değerlendirme",
|
||||
"search_filters_sort_option_date": "Yükleme tarihi",
|
||||
"search_filters_sort_option_views": "Görüntüleme sayısı",
|
||||
"search_filters_type_label": "Tür",
|
||||
"search_filters_duration_label": "Süre",
|
||||
"search_filters_features_label": "Özellikler",
|
||||
"search_filters_sort_label": "Sıralama Ölçütü",
|
||||
"search_filters_date_option_hour": "Son Saat",
|
||||
"search_filters_date_option_today": "Bugün",
|
||||
"search_filters_date_option_week": "Bu hafta",
|
||||
"search_filters_date_option_month": "Bu ay",
|
||||
"search_filters_date_option_year": "Bu yıl",
|
||||
"search_filters_type_option_video": "Video",
|
||||
"search_filters_type_option_channel": "Kanal",
|
||||
"search_filters_type_option_playlist": "Oynatma listesi",
|
||||
"search_filters_type_option_movie": "Film",
|
||||
"search_filters_type_option_show": "Gösteri",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "Alt yazılar",
|
||||
"search_filters_features_option_c_commons": "Creative Commons",
|
||||
"search_filters_features_option_three_d": "3B",
|
||||
"search_filters_features_option_live": "Canlı",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "Konum",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "Filtrele",
|
||||
"Current version: ": "Şu anki sürüm: ",
|
||||
"next_steps_error_message": "Bundan sonra şunları denemelisiniz: ",
|
||||
"next_steps_error_message_refresh": "Yenile",
|
||||
"next_steps_error_message_go_to_youtube": "YouTube'a git",
|
||||
"short": "Kısa (4 dakikadan az)",
|
||||
"long": "Uzun (20 dakikadan fazla)",
|
||||
"search_filters_duration_option_short": "Kısa (4 dakikadan az)",
|
||||
"search_filters_duration_option_long": "Uzun (20 dakikadan fazla)",
|
||||
"footer_documentation": "Belgelendirme",
|
||||
"footer_source_code": "Kaynak kodları",
|
||||
"footer_original_source_code": "Orijinal kaynak kodları",
|
||||
@ -394,8 +394,8 @@
|
||||
"Video unavailable": "Video kullanılamıyor",
|
||||
"preferences_quality_option_dash": "DASH (uyarlanabilir kalite)",
|
||||
"preferences_quality_dash_option_auto": "Otomatik",
|
||||
"purchased": "Satın alınan",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_purchased": "Satın alınan",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"videoinfo_watch_on_youTube": "YouTube'da izle",
|
||||
"download_subtitles": "Alt yazılar - `x` (.vtt)",
|
||||
"preferences_save_player_pos_label": "Oynatma konumunu kaydet: ",
|
||||
|
@ -315,32 +315,32 @@
|
||||
"Videos": "Video",
|
||||
"Playlists": "Danh sách phát",
|
||||
"Community": "Cộng đồng",
|
||||
"relevance": "liên quan",
|
||||
"rating": "Xếp hạng",
|
||||
"date": "ngày",
|
||||
"views": "lượt xem",
|
||||
"content_type": "content_type",
|
||||
"duration": "thời lượng",
|
||||
"features": "đặc trưng",
|
||||
"sort": "sắp xếp",
|
||||
"hour": "giờ",
|
||||
"today": "hôm nay",
|
||||
"week": "tuần",
|
||||
"month": "tháng",
|
||||
"year": "năm",
|
||||
"video": "video",
|
||||
"channel": "kênh",
|
||||
"playlist": "danh sách phát",
|
||||
"movie": "bộ phim",
|
||||
"show": "chỉ",
|
||||
"hd": "hd",
|
||||
"subtitles": "phụ đề",
|
||||
"creative_commons": "Commons sáng tạo",
|
||||
"3d": "3d",
|
||||
"live": "trực tiếp",
|
||||
"4k": "4k",
|
||||
"location": "vị trí",
|
||||
"hdr": "hdr",
|
||||
"filter": "bộ lọc",
|
||||
"search_filters_sort_option_relevance": "liên quan",
|
||||
"search_filters_sort_option_rating": "Xếp hạng",
|
||||
"search_filters_sort_option_date": "ngày",
|
||||
"search_filters_sort_option_views": "lượt xem",
|
||||
"search_filters_type_label": "content_type",
|
||||
"search_filters_duration_label": "thời lượng",
|
||||
"search_filters_features_label": "đặc trưng",
|
||||
"search_filters_sort_label": "sắp xếp",
|
||||
"search_filters_date_option_hour": "giờ",
|
||||
"search_filters_date_option_today": "hôm nay",
|
||||
"search_filters_date_option_week": "tuần",
|
||||
"search_filters_date_option_month": "tháng",
|
||||
"search_filters_date_option_year": "năm",
|
||||
"search_filters_type_option_video": "video",
|
||||
"search_filters_type_option_channel": "kênh",
|
||||
"search_filters_type_option_playlist": "danh sách phát",
|
||||
"search_filters_type_option_movie": "bộ phim",
|
||||
"search_filters_type_option_show": "chỉ",
|
||||
"search_filters_features_option_hd": "hd",
|
||||
"search_filters_features_option_subtitles": "phụ đề",
|
||||
"search_filters_features_option_c_commons": "Commons sáng tạo",
|
||||
"search_filters_features_option_three_d": "3d",
|
||||
"search_filters_features_option_live": "trực tiếp",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "vị trí",
|
||||
"search_filters_features_option_hdr": "hdr",
|
||||
"search_filters_label": "bộ lọc",
|
||||
"Current version: ": "Phiên bản hiện tại: "
|
||||
}
|
||||
|
@ -345,39 +345,39 @@
|
||||
"Videos": "视频",
|
||||
"Playlists": "播放列表",
|
||||
"Community": "社区",
|
||||
"relevance": "相关度",
|
||||
"rating": "评分",
|
||||
"date": "上传日期",
|
||||
"views": "观看次数",
|
||||
"content_type": "类型",
|
||||
"duration": "持续时间",
|
||||
"features": "功能",
|
||||
"sort": "排序依据",
|
||||
"hour": "上个小时",
|
||||
"today": "今日",
|
||||
"week": "本周",
|
||||
"month": "本月",
|
||||
"year": "今年",
|
||||
"video": "视频",
|
||||
"channel": "频道",
|
||||
"playlist": "播放列表",
|
||||
"movie": "电影",
|
||||
"show": "真人秀",
|
||||
"hd": "高清",
|
||||
"subtitles": "字幕",
|
||||
"creative_commons": "creative_commons 许可",
|
||||
"3d": "3d",
|
||||
"live": "直播",
|
||||
"4k": "4k",
|
||||
"location": "位置",
|
||||
"hdr": "hdr",
|
||||
"filter": "过滤器",
|
||||
"search_filters_sort_option_relevance": "相关度",
|
||||
"search_filters_sort_option_rating": "评分",
|
||||
"search_filters_sort_option_date": "上传日期",
|
||||
"search_filters_sort_option_views": "观看次数",
|
||||
"search_filters_type_label": "类型",
|
||||
"search_filters_duration_label": "持续时间",
|
||||
"search_filters_features_label": "功能",
|
||||
"search_filters_sort_label": "排序依据",
|
||||
"search_filters_date_option_hour": "上个小时",
|
||||
"search_filters_date_option_today": "今日",
|
||||
"search_filters_date_option_week": "本周",
|
||||
"search_filters_date_option_month": "本月",
|
||||
"search_filters_date_option_year": "今年",
|
||||
"search_filters_type_option_video": "视频",
|
||||
"search_filters_type_option_channel": "频道",
|
||||
"search_filters_type_option_playlist": "播放列表",
|
||||
"search_filters_type_option_movie": "电影",
|
||||
"search_filters_type_option_show": "真人秀",
|
||||
"search_filters_features_option_hd": "高清",
|
||||
"search_filters_features_option_subtitles": "字幕",
|
||||
"search_filters_features_option_c_commons": "creative_commons 许可",
|
||||
"search_filters_features_option_three_d": "3d",
|
||||
"search_filters_features_option_live": "直播",
|
||||
"search_filters_features_option_four_k": "4k",
|
||||
"search_filters_features_option_location": "位置",
|
||||
"search_filters_features_option_hdr": "hdr",
|
||||
"search_filters_label": "过滤器",
|
||||
"Current version: ": "当前版本: ",
|
||||
"next_steps_error_message": "在此之后你应尝试: ",
|
||||
"next_steps_error_message_refresh": "刷新",
|
||||
"next_steps_error_message_go_to_youtube": "转到 YouTube",
|
||||
"short": "短(少于4分钟)",
|
||||
"long": "长(多于 20 分钟)",
|
||||
"search_filters_duration_option_short": "短(少于4分钟)",
|
||||
"search_filters_duration_option_long": "长(多于 20 分钟)",
|
||||
"footer_documentation": "文档",
|
||||
"footer_source_code": "源代码",
|
||||
"footer_modfied_source_code": "修改的源代码",
|
||||
@ -418,8 +418,8 @@
|
||||
"user_created_playlists": "`x` 创建了播放列表",
|
||||
"user_saved_playlists": "`x` 保存了播放列表",
|
||||
"Video unavailable": "视频不可用",
|
||||
"purchased": "已购买",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_purchased": "已购买",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"none": "无",
|
||||
"preferences_save_player_pos_label": "保存播放位置: ",
|
||||
"Spanish (Mexico)": "西班牙语 (墨西哥)",
|
||||
|
@ -345,39 +345,39 @@
|
||||
"Videos": "影片",
|
||||
"Playlists": "播放清單",
|
||||
"Community": "社群",
|
||||
"relevance": "關聯",
|
||||
"rating": "評分",
|
||||
"date": "日期",
|
||||
"views": "檢視",
|
||||
"content_type": "內容類型",
|
||||
"duration": "時長",
|
||||
"features": "特色",
|
||||
"sort": "排序",
|
||||
"hour": "小時",
|
||||
"today": "今天",
|
||||
"week": "週",
|
||||
"month": "月",
|
||||
"year": "年",
|
||||
"video": "影片",
|
||||
"channel": "頻道",
|
||||
"playlist": "播放清單",
|
||||
"movie": "電影",
|
||||
"show": "秀",
|
||||
"hd": "HD",
|
||||
"subtitles": "字幕",
|
||||
"creative_commons": "創用 CC",
|
||||
"3d": "3D",
|
||||
"live": "直播",
|
||||
"4k": "4K",
|
||||
"location": "位置",
|
||||
"hdr": "HDR",
|
||||
"filter": "篩選條件",
|
||||
"search_filters_sort_option_relevance": "關聯",
|
||||
"search_filters_sort_option_rating": "評分",
|
||||
"search_filters_sort_option_date": "日期",
|
||||
"search_filters_sort_option_views": "檢視",
|
||||
"search_filters_type_label": "內容類型",
|
||||
"search_filters_duration_label": "時長",
|
||||
"search_filters_features_label": "特色",
|
||||
"search_filters_sort_label": "排序",
|
||||
"search_filters_date_option_hour": "小時",
|
||||
"search_filters_date_option_today": "今天",
|
||||
"search_filters_date_option_week": "週",
|
||||
"search_filters_date_option_month": "月",
|
||||
"search_filters_date_option_year": "年",
|
||||
"search_filters_type_option_video": "影片",
|
||||
"search_filters_type_option_channel": "頻道",
|
||||
"search_filters_type_option_playlist": "播放清單",
|
||||
"search_filters_type_option_movie": "電影",
|
||||
"search_filters_type_option_show": "秀",
|
||||
"search_filters_features_option_hd": "HD",
|
||||
"search_filters_features_option_subtitles": "字幕",
|
||||
"search_filters_features_option_c_commons": "創用 CC",
|
||||
"search_filters_features_option_three_d": "3D",
|
||||
"search_filters_features_option_live": "直播",
|
||||
"search_filters_features_option_four_k": "4K",
|
||||
"search_filters_features_option_location": "位置",
|
||||
"search_filters_features_option_hdr": "HDR",
|
||||
"search_filters_label": "篩選條件",
|
||||
"Current version: ": "目前版本: ",
|
||||
"next_steps_error_message": "之後您應該嘗試: ",
|
||||
"next_steps_error_message_refresh": "重新整理",
|
||||
"next_steps_error_message_go_to_youtube": "到 YouTube",
|
||||
"short": "短(小於4分鐘)",
|
||||
"long": "長(多於20分鐘)",
|
||||
"search_filters_duration_option_short": "短(小於4分鐘)",
|
||||
"search_filters_duration_option_long": "長(多於20分鐘)",
|
||||
"footer_documentation": "文件",
|
||||
"footer_source_code": "原始碼",
|
||||
"footer_original_source_code": "原本的原始碼",
|
||||
@ -398,8 +398,8 @@
|
||||
"preferences_quality_dash_option_240p": "240p",
|
||||
"preferences_quality_dash_option_144p": "144p",
|
||||
"invidious": "Invidious",
|
||||
"purchased": "已購買",
|
||||
"360": "360°",
|
||||
"search_filters_features_option_purchased": "已購買",
|
||||
"search_filters_features_option_three_sixty": "360°",
|
||||
"none": "無",
|
||||
"videoinfo_started_streaming_x_ago": "`x` 前開始串流",
|
||||
"videoinfo_watch_on_youTube": "在 YouTube 上觀看",
|
||||
|
@ -29,20 +29,6 @@ Spectator.describe "Helper" do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#produce_search_params" do
|
||||
it "correctly produces token for searching with specified filters" do
|
||||
expect(produce_search_params).to eq("CAASAhABSAA%3D")
|
||||
|
||||
expect(produce_search_params(sort: "upload_date", content_type: "video")).to eq("CAISAhABSAA%3D")
|
||||
|
||||
expect(produce_search_params(content_type: "playlist")).to eq("CAASAhADSAA%3D")
|
||||
|
||||
expect(produce_search_params(sort: "date", content_type: "video", features: ["hd", "cc", "purchased", "hdr"])).to eq("CAISCxABIAEwAUgByAEBSAA%3D")
|
||||
|
||||
expect(produce_search_params(content_type: "channel")).to eq("CAASAhACSAA%3D")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#produce_comment_continuation" do
|
||||
it "correctly produces a continuation token for comments" do
|
||||
expect(produce_comment_continuation("_cE8xSu6swE", "ADSJ_i2qvJeFtL0htmS5_K5Ctj3eGFVBMWL9Wd42o3kmUL6_mAzdLp85-liQZL0mYr_16BhaggUqX652Sv9JqV6VXinShSP-ZT6rL4NolPBaPXVtJsO5_rA_qE3GubAuLFw9uzIIXU2-HnpXbdgPLWTFavfX206hqWmmpHwUOrmxQV_OX6tYkM3ux3rPAKCDrT8eWL7MU3bLiNcnbgkW8o0h8KYLL_8BPa8LcHbTv8pAoNkjerlX1x7K4pqxaXPoyz89qNlnh6rRx6AXgAzzoHH1dmcyQ8CIBeOHg-m4i8ZxdX4dP88XWrIFg-jJGhpGP8JUMDgZgavxVx225hUEYZMyrLGler5em4FgbG62YWC51moLDLeYEA")).to eq("EkMSC19jRTh4U3U2c3dFyAEA4AEBogINKP___________wFAAMICHQgEGhdodHRwczovL3d3dy55b3V0dWJlLmNvbSIAGAYyjAMK9gJBRFNKX2kycXZKZUZ0TDBodG1TNV9LNUN0ajNlR0ZWQk1XTDlXZDQybzNrbVVMNl9tQXpkTHA4NS1saVFaTDBtWXJfMTZCaGFnZ1VxWDY1MlN2OUpxVjZWWGluU2hTUC1aVDZyTDROb2xQQmFQWFZ0SnNPNV9yQV9xRTNHdWJBdUxGdzl1eklJWFUyLUhucFhiZGdQTFdURmF2ZlgyMDZocVdtbXBId1VPcm14UVZfT1g2dFlrTTN1eDNyUEFLQ0RyVDhlV0w3TVUzYkxpTmNuYmdrVzhvMGg4S1lMTF84QlBhOExjSGJUdjhwQW9Oa2plcmxYMXg3SzRwcXhhWFBveXo4OXFObG5oNnJSeDZBWGdBenpvSEgxZG1jeVE4Q0lCZU9IZy1tNGk4WnhkWDRkUDg4WFdySUZnLWpKR2hwR1A4SlVNRGdaZ2F2eFZ4MjI1aFVFWVpNeXJMR2xlcjVlbTRGZ2JHNjJZV0M1MW1vTERMZVlFQSIPIgtfY0U4eFN1NnN3RTAAKBQ%3D")
|
||||
|
371
spec/invidious/search/iv_filters_spec.cr
Normal file
371
spec/invidious/search/iv_filters_spec.cr
Normal file
@ -0,0 +1,371 @@
|
||||
require "../../../src/invidious/search/filters"
|
||||
|
||||
require "http/params"
|
||||
require "spectator"
|
||||
|
||||
Spectator.configure do |config|
|
||||
config.fail_blank
|
||||
config.randomize
|
||||
end
|
||||
|
||||
FEATURES_TEXT = {
|
||||
Invidious::Search::Filters::Features::Live => "live",
|
||||
Invidious::Search::Filters::Features::FourK => "4k",
|
||||
Invidious::Search::Filters::Features::HD => "hd",
|
||||
Invidious::Search::Filters::Features::Subtitles => "subtitles",
|
||||
Invidious::Search::Filters::Features::CCommons => "commons",
|
||||
Invidious::Search::Filters::Features::ThreeSixty => "360",
|
||||
Invidious::Search::Filters::Features::VR180 => "vr180",
|
||||
Invidious::Search::Filters::Features::ThreeD => "3d",
|
||||
Invidious::Search::Filters::Features::HDR => "hdr",
|
||||
Invidious::Search::Filters::Features::Location => "location",
|
||||
Invidious::Search::Filters::Features::Purchased => "purchased",
|
||||
}
|
||||
|
||||
Spectator.describe Invidious::Search::Filters do
|
||||
# -------------------
|
||||
# Decode (legacy)
|
||||
# -------------------
|
||||
|
||||
describe "#from_legacy_filters" do
|
||||
it "Decodes channel: filter" do
|
||||
query = "test channel:UC123456 request"
|
||||
|
||||
fltr, chan, qury, subs = described_class.from_legacy_filters(query)
|
||||
|
||||
expect(fltr).to eq(described_class.new)
|
||||
expect(chan).to eq("UC123456")
|
||||
expect(qury).to eq("test request")
|
||||
expect(subs).to be_false
|
||||
end
|
||||
|
||||
it "Decodes user: filter" do
|
||||
query = "user:LinusTechTips broke something (again)"
|
||||
|
||||
fltr, chan, qury, subs = described_class.from_legacy_filters(query)
|
||||
|
||||
expect(fltr).to eq(described_class.new)
|
||||
expect(chan).to eq("LinusTechTips")
|
||||
expect(qury).to eq("broke something (again)")
|
||||
expect(subs).to be_false
|
||||
end
|
||||
|
||||
it "Decodes type: filter" do
|
||||
Invidious::Search::Filters::Type.each do |value|
|
||||
query = "Eiffel 65 - Blue [1 Hour] type:#{value}"
|
||||
|
||||
fltr, chan, qury, subs = described_class.from_legacy_filters(query)
|
||||
|
||||
expect(fltr).to eq(described_class.new(type: value))
|
||||
expect(chan).to eq("")
|
||||
expect(qury).to eq("Eiffel 65 - Blue [1 Hour]")
|
||||
expect(subs).to be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "Decodes content_type: filter" do
|
||||
Invidious::Search::Filters::Type.each do |value|
|
||||
query = "I like to watch content_type:#{value}"
|
||||
|
||||
fltr, chan, qury, subs = described_class.from_legacy_filters(query)
|
||||
|
||||
expect(fltr).to eq(described_class.new(type: value))
|
||||
expect(chan).to eq("")
|
||||
expect(qury).to eq("I like to watch")
|
||||
expect(subs).to be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "Decodes date: filter" do
|
||||
Invidious::Search::Filters::Date.each do |value|
|
||||
query = "This date:#{value} is old!"
|
||||
|
||||
fltr, chan, qury, subs = described_class.from_legacy_filters(query)
|
||||
|
||||
expect(fltr).to eq(described_class.new(date: value))
|
||||
expect(chan).to eq("")
|
||||
expect(qury).to eq("This is old!")
|
||||
expect(subs).to be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "Decodes duration: filter" do
|
||||
Invidious::Search::Filters::Duration.each do |value|
|
||||
query = "This duration:#{value} is old!"
|
||||
|
||||
fltr, chan, qury, subs = described_class.from_legacy_filters(query)
|
||||
|
||||
expect(fltr).to eq(described_class.new(duration: value))
|
||||
expect(chan).to eq("")
|
||||
expect(qury).to eq("This is old!")
|
||||
expect(subs).to be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "Decodes feature: filter" do
|
||||
Invidious::Search::Filters::Features.each do |value|
|
||||
string = FEATURES_TEXT[value]
|
||||
query = "I like my precious feature:#{string} ^^"
|
||||
|
||||
fltr, chan, qury, subs = described_class.from_legacy_filters(query)
|
||||
|
||||
expect(fltr).to eq(described_class.new(features: value))
|
||||
expect(chan).to eq("")
|
||||
expect(qury).to eq("I like my precious ^^")
|
||||
expect(subs).to be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "Decodes features: filter" do
|
||||
query = "This search has many features:vr180,cc,hdr :o"
|
||||
|
||||
fltr, chan, qury, subs = described_class.from_legacy_filters(query)
|
||||
|
||||
features = Invidious::Search::Filters::Features.flags(HDR, VR180, CCommons)
|
||||
|
||||
expect(fltr).to eq(described_class.new(features: features))
|
||||
expect(chan).to eq("")
|
||||
expect(qury).to eq("This search has many :o")
|
||||
expect(subs).to be_false
|
||||
end
|
||||
|
||||
it "Decodes sort: filter" do
|
||||
Invidious::Search::Filters::Sort.each do |value|
|
||||
query = "Computer? sort:#{value} my files!"
|
||||
|
||||
fltr, chan, qury, subs = described_class.from_legacy_filters(query)
|
||||
|
||||
expect(fltr).to eq(described_class.new(sort: value))
|
||||
expect(chan).to eq("")
|
||||
expect(qury).to eq("Computer? my files!")
|
||||
expect(subs).to be_false
|
||||
end
|
||||
end
|
||||
|
||||
it "Decodes subscriptions: filter" do
|
||||
query = "enable subscriptions:true"
|
||||
|
||||
fltr, chan, qury, subs = described_class.from_legacy_filters(query)
|
||||
|
||||
expect(fltr).to eq(described_class.new)
|
||||
expect(chan).to eq("")
|
||||
expect(qury).to eq("enable")
|
||||
expect(subs).to be_true
|
||||
end
|
||||
|
||||
it "Ignores junk data" do
|
||||
query = "duration:I sort:like type:cleaning features:stuff date:up!"
|
||||
|
||||
fltr, chan, qury, subs = described_class.from_legacy_filters(query)
|
||||
|
||||
expect(fltr).to eq(described_class.new)
|
||||
expect(chan).to eq("")
|
||||
expect(qury).to eq("")
|
||||
expect(subs).to be_false
|
||||
end
|
||||
|
||||
it "Keeps unknown keys" do
|
||||
query = "to:be or:not to:be"
|
||||
|
||||
fltr, chan, qury, subs = described_class.from_legacy_filters(query)
|
||||
|
||||
expect(fltr).to eq(described_class.new)
|
||||
expect(chan).to eq("")
|
||||
expect(qury).to eq("to:be or:not to:be")
|
||||
expect(subs).to be_false
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------
|
||||
# Decode (URL)
|
||||
# -------------------
|
||||
|
||||
describe "#from_iv_params" do
|
||||
it "Decodes type= filter" do
|
||||
Invidious::Search::Filters::Type.each do |value|
|
||||
params = HTTP::Params.parse("type=#{value}")
|
||||
|
||||
expect(described_class.from_iv_params(params))
|
||||
.to eq(described_class.new(type: value))
|
||||
end
|
||||
end
|
||||
|
||||
it "Decodes date= filter" do
|
||||
Invidious::Search::Filters::Date.each do |value|
|
||||
params = HTTP::Params.parse("date=#{value}")
|
||||
|
||||
expect(described_class.from_iv_params(params))
|
||||
.to eq(described_class.new(date: value))
|
||||
end
|
||||
end
|
||||
|
||||
it "Decodes duration= filter" do
|
||||
Invidious::Search::Filters::Duration.each do |value|
|
||||
params = HTTP::Params.parse("duration=#{value}")
|
||||
|
||||
expect(described_class.from_iv_params(params))
|
||||
.to eq(described_class.new(duration: value))
|
||||
end
|
||||
end
|
||||
|
||||
it "Decodes features= filter (single)" do
|
||||
Invidious::Search::Filters::Features.each do |value|
|
||||
string = described_class.format_features(value)
|
||||
params = HTTP::Params.parse("features=#{string}")
|
||||
|
||||
expect(described_class.from_iv_params(params))
|
||||
.to eq(described_class.new(features: value))
|
||||
end
|
||||
end
|
||||
|
||||
it "Decodes features= filter (multiple - comma separated)" do
|
||||
features = Invidious::Search::Filters::Features.flags(HDR, VR180, CCommons)
|
||||
params = HTTP::Params.parse("features=vr180%2Ccc%2Chdr") # %2C is a comma
|
||||
|
||||
expect(described_class.from_iv_params(params))
|
||||
.to eq(described_class.new(features: features))
|
||||
end
|
||||
|
||||
it "Decodes features= filter (multiple - URL parameters)" do
|
||||
features = Invidious::Search::Filters::Features.flags(ThreeSixty, HD, FourK)
|
||||
params = HTTP::Params.parse("features=4k&features=360&features=hd")
|
||||
|
||||
expect(described_class.from_iv_params(params))
|
||||
.to eq(described_class.new(features: features))
|
||||
end
|
||||
|
||||
it "Decodes sort= filter" do
|
||||
Invidious::Search::Filters::Sort.each do |value|
|
||||
params = HTTP::Params.parse("sort=#{value}")
|
||||
|
||||
expect(described_class.from_iv_params(params))
|
||||
.to eq(described_class.new(sort: value))
|
||||
end
|
||||
end
|
||||
|
||||
it "Ignores junk data" do
|
||||
params = HTTP::Params.parse("foo=bar&sort=views&answer=42&type=channel")
|
||||
|
||||
expect(described_class.from_iv_params(params)).to eq(
|
||||
described_class.new(
|
||||
sort: Invidious::Search::Filters::Sort::Views,
|
||||
type: Invidious::Search::Filters::Type::Channel
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------
|
||||
# Encode (URL)
|
||||
# -------------------
|
||||
|
||||
describe "#to_iv_params" do
|
||||
it "Encodes date filter" do
|
||||
Invidious::Search::Filters::Date.each do |value|
|
||||
filters = described_class.new(date: value)
|
||||
params = filters.to_iv_params
|
||||
|
||||
if value.none?
|
||||
expect("#{params}").to eq("")
|
||||
else
|
||||
expect("#{params}").to eq("date=#{value.to_s.underscore}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "Encodes type filter" do
|
||||
Invidious::Search::Filters::Type.each do |value|
|
||||
filters = described_class.new(type: value)
|
||||
params = filters.to_iv_params
|
||||
|
||||
if value.all?
|
||||
expect("#{params}").to eq("")
|
||||
else
|
||||
expect("#{params}").to eq("type=#{value.to_s.underscore}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "Encodes duration filter" do
|
||||
Invidious::Search::Filters::Duration.each do |value|
|
||||
filters = described_class.new(duration: value)
|
||||
params = filters.to_iv_params
|
||||
|
||||
if value.none?
|
||||
expect("#{params}").to eq("")
|
||||
else
|
||||
expect("#{params}").to eq("duration=#{value.to_s.underscore}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "Encodes features filter (single)" do
|
||||
Invidious::Search::Filters::Features.each do |value|
|
||||
string = described_class.format_features(value)
|
||||
filters = described_class.new(features: value)
|
||||
|
||||
expect("#{filters.to_iv_params}")
|
||||
.to eq("features=" + FEATURES_TEXT[value])
|
||||
end
|
||||
end
|
||||
|
||||
it "Encodes features filter (multiple)" do
|
||||
features = Invidious::Search::Filters::Features.flags(Subtitles, Live, ThreeSixty)
|
||||
filters = described_class.new(features: features)
|
||||
|
||||
expect("#{filters.to_iv_params}")
|
||||
.to eq("features=live%2Csubtitles%2C360") # %2C is a comma
|
||||
end
|
||||
|
||||
it "Encodes sort filter" do
|
||||
Invidious::Search::Filters::Sort.each do |value|
|
||||
filters = described_class.new(sort: value)
|
||||
params = filters.to_iv_params
|
||||
|
||||
if value.relevance?
|
||||
expect("#{params}").to eq("")
|
||||
else
|
||||
expect("#{params}").to eq("sort=#{value.to_s.underscore}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "Encodes multiple filters" do
|
||||
filters = described_class.new(
|
||||
date: Invidious::Search::Filters::Date::Today,
|
||||
duration: Invidious::Search::Filters::Duration::Medium,
|
||||
features: Invidious::Search::Filters::Features.flags(Location, Purchased),
|
||||
sort: Invidious::Search::Filters::Sort::Relevance
|
||||
)
|
||||
|
||||
params = filters.to_iv_params
|
||||
|
||||
# Check the `date` param
|
||||
expect(params).to have_key("date")
|
||||
expect(params.fetch_all("date")).to contain_exactly("today")
|
||||
|
||||
# Check the `type` param
|
||||
expect(params).to_not have_key("type")
|
||||
expect(params["type"]?).to be_nil
|
||||
|
||||
# Check the `duration` param
|
||||
expect(params).to have_key("duration")
|
||||
expect(params.fetch_all("duration")).to contain_exactly("medium")
|
||||
|
||||
# Check the `features` param
|
||||
expect(params).to have_key("features")
|
||||
expect(params.fetch_all("features")).to contain_exactly("location,purchased")
|
||||
|
||||
# Check the `sort` param
|
||||
expect(params).to_not have_key("sort")
|
||||
expect(params["sort"]?).to be_nil
|
||||
|
||||
# Check if there aren't other parameters
|
||||
params.delete("date")
|
||||
params.delete("duration")
|
||||
params.delete("features")
|
||||
|
||||
expect(params).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
143
spec/invidious/search/yt_filters_spec.cr
Normal file
143
spec/invidious/search/yt_filters_spec.cr
Normal file
@ -0,0 +1,143 @@
|
||||
require "../../../src/invidious/search/filters"
|
||||
|
||||
require "http/params"
|
||||
require "spectator"
|
||||
|
||||
Spectator.configure do |config|
|
||||
config.fail_blank
|
||||
config.randomize
|
||||
end
|
||||
|
||||
# Encoded filter values are extracted from the search
|
||||
# page of Youtube with any browser devtools HTML inspector.
|
||||
|
||||
DATE_FILTERS = {
|
||||
Invidious::Search::Filters::Date::Hour => "EgIIAQ%3D%3D",
|
||||
Invidious::Search::Filters::Date::Today => "EgIIAg%3D%3D",
|
||||
Invidious::Search::Filters::Date::Week => "EgIIAw%3D%3D",
|
||||
Invidious::Search::Filters::Date::Month => "EgIIBA%3D%3D",
|
||||
Invidious::Search::Filters::Date::Year => "EgIIBQ%3D%3D",
|
||||
}
|
||||
|
||||
TYPE_FILTERS = {
|
||||
Invidious::Search::Filters::Type::Video => "EgIQAQ%3D%3D",
|
||||
Invidious::Search::Filters::Type::Channel => "EgIQAg%3D%3D",
|
||||
Invidious::Search::Filters::Type::Playlist => "EgIQAw%3D%3D",
|
||||
Invidious::Search::Filters::Type::Movie => "EgIQBA%3D%3D",
|
||||
}
|
||||
|
||||
DURATION_FILTERS = {
|
||||
Invidious::Search::Filters::Duration::Short => "EgIYAQ%3D%3D",
|
||||
Invidious::Search::Filters::Duration::Medium => "EgIYAw%3D%3D",
|
||||
Invidious::Search::Filters::Duration::Long => "EgIYAg%3D%3D",
|
||||
}
|
||||
|
||||
FEATURE_FILTERS = {
|
||||
Invidious::Search::Filters::Features::Live => "EgJAAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::FourK => "EgJwAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::HD => "EgIgAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::Subtitles => "EgIoAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::CCommons => "EgIwAQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::ThreeSixty => "EgJ4AQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::VR180 => "EgPQAQE%3D",
|
||||
Invidious::Search::Filters::Features::ThreeD => "EgI4AQ%3D%3D",
|
||||
Invidious::Search::Filters::Features::HDR => "EgPIAQE%3D",
|
||||
Invidious::Search::Filters::Features::Location => "EgO4AQE%3D",
|
||||
Invidious::Search::Filters::Features::Purchased => "EgJIAQ%3D%3D",
|
||||
}
|
||||
|
||||
SORT_FILTERS = {
|
||||
Invidious::Search::Filters::Sort::Relevance => "",
|
||||
Invidious::Search::Filters::Sort::Date => "CAI%3D",
|
||||
Invidious::Search::Filters::Sort::Views => "CAM%3D",
|
||||
Invidious::Search::Filters::Sort::Rating => "CAE%3D",
|
||||
}
|
||||
|
||||
Spectator.describe Invidious::Search::Filters do
|
||||
# -------------------
|
||||
# Encode YT params
|
||||
# -------------------
|
||||
|
||||
describe "#to_yt_params" do
|
||||
sample DATE_FILTERS do |value, result|
|
||||
it "Encodes upload date filter '#{value}'" do
|
||||
expect(described_class.new(date: value).to_yt_params).to eq(result)
|
||||
end
|
||||
end
|
||||
|
||||
sample TYPE_FILTERS do |value, result|
|
||||
it "Encodes content type filter '#{value}'" do
|
||||
expect(described_class.new(type: value).to_yt_params).to eq(result)
|
||||
end
|
||||
end
|
||||
|
||||
sample DURATION_FILTERS do |value, result|
|
||||
it "Encodes duration filter '#{value}'" do
|
||||
expect(described_class.new(duration: value).to_yt_params).to eq(result)
|
||||
end
|
||||
end
|
||||
|
||||
sample FEATURE_FILTERS do |value, result|
|
||||
it "Encodes feature filter '#{value}'" do
|
||||
expect(described_class.new(features: value).to_yt_params).to eq(result)
|
||||
end
|
||||
end
|
||||
|
||||
sample SORT_FILTERS do |value, result|
|
||||
it "Encodes sort filter '#{value}'" do
|
||||
expect(described_class.new(sort: value).to_yt_params).to eq(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------
|
||||
# Decode YT params
|
||||
# -------------------
|
||||
|
||||
describe "#from_yt_params" do
|
||||
sample DATE_FILTERS do |value, encoded|
|
||||
it "Decodes upload date filter '#{value}'" do
|
||||
params = HTTP::Params.parse("sp=#{encoded}")
|
||||
|
||||
expect(described_class.from_yt_params(params))
|
||||
.to eq(described_class.new(date: value))
|
||||
end
|
||||
end
|
||||
|
||||
sample TYPE_FILTERS do |value, encoded|
|
||||
it "Decodes content type filter '#{value}'" do
|
||||
params = HTTP::Params.parse("sp=#{encoded}")
|
||||
|
||||
expect(described_class.from_yt_params(params))
|
||||
.to eq(described_class.new(type: value))
|
||||
end
|
||||
end
|
||||
|
||||
sample DURATION_FILTERS do |value, encoded|
|
||||
it "Decodes duration filter '#{value}'" do
|
||||
params = HTTP::Params.parse("sp=#{encoded}")
|
||||
|
||||
expect(described_class.from_yt_params(params))
|
||||
.to eq(described_class.new(duration: value))
|
||||
end
|
||||
end
|
||||
|
||||
sample FEATURE_FILTERS do |value, encoded|
|
||||
it "Decodes feature filter '#{value}'" do
|
||||
params = HTTP::Params.parse("sp=#{encoded}")
|
||||
|
||||
expect(described_class.from_yt_params(params))
|
||||
.to eq(described_class.new(features: value))
|
||||
end
|
||||
end
|
||||
|
||||
sample SORT_FILTERS do |value, encoded|
|
||||
it "Decodes sort filter '#{value}'" do
|
||||
params = HTTP::Params.parse("sp=#{encoded}")
|
||||
|
||||
expect(described_class.from_yt_params(params))
|
||||
.to eq(described_class.new(sort: value))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -8,7 +8,7 @@ require "../src/invidious/channels/*"
|
||||
require "../src/invidious/videos"
|
||||
require "../src/invidious/comments"
|
||||
require "../src/invidious/playlists"
|
||||
require "../src/invidious/search"
|
||||
require "../src/invidious/search/ctoken"
|
||||
require "../src/invidious/trending"
|
||||
require "spectator"
|
||||
|
||||
|
@ -35,6 +35,7 @@ require "./invidious/frontend/*"
|
||||
require "./invidious/*"
|
||||
require "./invidious/channels/*"
|
||||
require "./invidious/user/*"
|
||||
require "./invidious/search/*"
|
||||
require "./invidious/routes/**"
|
||||
require "./invidious/jobs/**"
|
||||
|
||||
|
@ -1,3 +1,11 @@
|
||||
# Exception used to hold the bogus UCID during a channel search.
|
||||
class ChannelSearchException < InfoException
|
||||
getter channel : String
|
||||
|
||||
def initialize(@channel)
|
||||
end
|
||||
end
|
||||
|
||||
# Exception used to hold the name of the missing item
|
||||
# Should be used in all parsing functions
|
||||
class BrokenTubeException < Exception
|
||||
|
14
src/invidious/frontend/misc.cr
Normal file
14
src/invidious/frontend/misc.cr
Normal file
@ -0,0 +1,14 @@
|
||||
module Invidious::Frontend::Misc
|
||||
extend self
|
||||
|
||||
def redirect_url(env : HTTP::Server::Context)
|
||||
prefs = env.get("preferences").as(Preferences)
|
||||
|
||||
if prefs.automatic_instance_redirect
|
||||
current_page = env.get?("current_page").as(String)
|
||||
redirect_url = "/redirect?referer=#{current_page}"
|
||||
else
|
||||
redirect_url = "https://redirect.invidious.io#{env.request.resource}"
|
||||
end
|
||||
end
|
||||
end
|
135
src/invidious/frontend/search_filters.cr
Normal file
135
src/invidious/frontend/search_filters.cr
Normal file
@ -0,0 +1,135 @@
|
||||
module Invidious::Frontend::SearchFilters
|
||||
extend self
|
||||
|
||||
# Generate the search filters collapsable widget.
|
||||
def generate(filters : Search::Filters, query : String, page : Int, locale : String) : String
|
||||
return String.build(8000) do |str|
|
||||
str << "<div id='filters'>\n"
|
||||
str << "\t<details id='filters-collapse'>"
|
||||
str << "\t\t<summary>" << translate(locale, "search_filters_title") << "</summary>\n"
|
||||
|
||||
str << "\t\t<div id='filters-box'><form action='/search' method='get'>\n"
|
||||
|
||||
str << "\t\t\t<input type='hidden' name='q' value='" << HTML.escape(query) << "'>\n"
|
||||
str << "\t\t\t<input type='hidden' name='page' value='" << page << "'>\n"
|
||||
|
||||
str << "\t\t\t<div id='filters-flex'>"
|
||||
|
||||
filter_wrapper(date)
|
||||
filter_wrapper(type)
|
||||
filter_wrapper(duration)
|
||||
filter_wrapper(features)
|
||||
filter_wrapper(sort)
|
||||
|
||||
str << "\t\t\t</div>\n"
|
||||
|
||||
str << "\t\t\t<div id='filters-apply'>"
|
||||
str << "<button type='submit' class=\"pure-button pure-button-primary\">"
|
||||
str << translate(locale, "search_filters_apply_button")
|
||||
str << "</button></div>\n"
|
||||
|
||||
str << "\t\t</form></div>\n"
|
||||
|
||||
str << "\t</details>\n"
|
||||
str << "</div>\n"
|
||||
end
|
||||
end
|
||||
|
||||
# Generate wrapper HTML (`<div>`, filter name, etc...) around the
|
||||
# `<input>` elements of a search filter
|
||||
macro filter_wrapper(name)
|
||||
str << "\t\t\t\t<div class=\"filter-column\"><fieldset>\n"
|
||||
|
||||
str << "\t\t\t\t\t<legend><div class=\"filter-name underlined\">"
|
||||
str << translate(locale, "search_filters_{{name}}_label")
|
||||
str << "</div></legend>\n"
|
||||
|
||||
str << "\t\t\t\t\t<div class=\"filter-options\">\n"
|
||||
make_{{name}}_filter_options(str, filters.{{name}}, locale)
|
||||
str << "\t\t\t\t\t</div>"
|
||||
|
||||
str << "\t\t\t\t</fieldset></div>\n"
|
||||
end
|
||||
|
||||
# Generates the HTML for the list of radio buttons of the "date" search filter
|
||||
def make_date_filter_options(str : String::Builder, value : Search::Filters::Date, locale : String)
|
||||
{% for value in Invidious::Search::Filters::Date.constants %}
|
||||
{% date = value.underscore %}
|
||||
|
||||
str << "\t\t\t\t\t\t<div>"
|
||||
str << "<input type='radio' name='date' id='filter-date-{{date}}' value='{{date}}'"
|
||||
str << " checked" if value.{{date}}?
|
||||
str << '>'
|
||||
|
||||
str << "<label for='filter-date-{{date}}'>"
|
||||
str << translate(locale, "search_filters_date_option_{{date}}")
|
||||
str << "</label></div>\n"
|
||||
{% end %}
|
||||
end
|
||||
|
||||
# Generates the HTML for the list of radio buttons of the "type" search filter
|
||||
def make_type_filter_options(str : String::Builder, value : Search::Filters::Type, locale : String)
|
||||
{% for value in Invidious::Search::Filters::Type.constants %}
|
||||
{% type = value.underscore %}
|
||||
|
||||
str << "\t\t\t\t\t\t<div>"
|
||||
str << "<input type='radio' name='type' id='filter-type-{{type}}' value='{{type}}'"
|
||||
str << " checked" if value.{{type}}?
|
||||
str << '>'
|
||||
|
||||
str << "<label for='filter-type-{{type}}'>"
|
||||
str << translate(locale, "search_filters_type_option_{{type}}")
|
||||
str << "</label></div>\n"
|
||||
{% end %}
|
||||
end
|
||||
|
||||
# Generates the HTML for the list of radio buttons of the "duration" search filter
|
||||
def make_duration_filter_options(str : String::Builder, value : Search::Filters::Duration, locale : String)
|
||||
{% for value in Invidious::Search::Filters::Duration.constants %}
|
||||
{% duration = value.underscore %}
|
||||
|
||||
str << "\t\t\t\t\t\t<div>"
|
||||
str << "<input type='radio' name='duration' id='filter-duration-{{duration}}' value='{{duration}}'"
|
||||
str << " checked" if value.{{duration}}?
|
||||
str << '>'
|
||||
|
||||
str << "<label for='filter-duration-{{duration}}'>"
|
||||
str << translate(locale, "search_filters_duration_option_{{duration}}")
|
||||
str << "</label></div>\n"
|
||||
{% end %}
|
||||
end
|
||||
|
||||
# Generates the HTML for the list of checkboxes of the "features" search filter
|
||||
def make_features_filter_options(str : String::Builder, value : Search::Filters::Features, locale : String)
|
||||
{% for value in Invidious::Search::Filters::Features.constants %}
|
||||
{% if value.stringify != "All" && value.stringify != "None" %}
|
||||
{% feature = value.underscore %}
|
||||
|
||||
str << "\t\t\t\t\t\t<div>"
|
||||
str << "<input type='checkbox' name='features' id='filter-features-{{feature}}' value='{{feature}}'"
|
||||
str << " checked" if value.{{feature}}?
|
||||
str << '>'
|
||||
|
||||
str << "<label for='filter-feature-{{feature}}'>"
|
||||
str << translate(locale, "search_filters_features_option_{{feature}}")
|
||||
str << "</label></div>\n"
|
||||
{% end %}
|
||||
{% end %}
|
||||
end
|
||||
|
||||
# Generates the HTML for the list of radio buttons of the "sort" search filter
|
||||
def make_sort_filter_options(str : String::Builder, value : Search::Filters::Sort, locale : String)
|
||||
{% for value in Invidious::Search::Filters::Sort.constants %}
|
||||
{% sort = value.underscore %}
|
||||
|
||||
str << "\t\t\t\t\t\t<div>"
|
||||
str << "<input type='radio' name='sort' id='filter-sort-{{sort}}' value='{{sort}}'"
|
||||
str << " checked" if value.{{sort}}?
|
||||
str << '>'
|
||||
|
||||
str << "<label for='filter-sort-{{sort}}'>"
|
||||
str << translate(locale, "search_filters_sort_option_{{sort}}")
|
||||
str << "</label></div>\n"
|
||||
{% end %}
|
||||
end
|
||||
end
|
@ -251,18 +251,22 @@ module Invidious::Routes::API::V1::Channels
|
||||
|
||||
def self.search(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
region = env.params.query["region"]?
|
||||
|
||||
env.response.content_type = "application/json"
|
||||
|
||||
ucid = env.params.url["ucid"]
|
||||
query = Invidious::Search::Query.new(env.params.query, :channel, region)
|
||||
|
||||
query = env.params.query["q"]?
|
||||
query ||= ""
|
||||
# Required because we can't (yet) pass multiple parameter to the
|
||||
# `Search::Query` initializer (in this case, an URL segment)
|
||||
query.channel = env.params.url["ucid"]
|
||||
|
||||
page = env.params.query["page"]?.try &.to_i?
|
||||
page ||= 1
|
||||
begin
|
||||
search_results = query.process
|
||||
rescue ex
|
||||
return error_json(400, ex)
|
||||
end
|
||||
|
||||
search_results = channel_search(query, page, ucid)
|
||||
JSON.build do |json|
|
||||
json.array do
|
||||
search_results.each do |item|
|
||||
|
@ -5,34 +5,14 @@ module Invidious::Routes::API::V1::Search
|
||||
|
||||
env.response.content_type = "application/json"
|
||||
|
||||
query = env.params.query["q"]?
|
||||
query ||= ""
|
||||
|
||||
page = env.params.query["page"]?.try &.to_i?
|
||||
page ||= 1
|
||||
|
||||
sort_by = env.params.query["sort_by"]?.try &.downcase
|
||||
sort_by ||= "relevance"
|
||||
|
||||
date = env.params.query["date"]?.try &.downcase
|
||||
date ||= ""
|
||||
|
||||
duration = env.params.query["duration"]?.try &.downcase
|
||||
duration ||= ""
|
||||
|
||||
features = env.params.query["features"]?.try &.split(",").map(&.downcase)
|
||||
features ||= [] of String
|
||||
|
||||
content_type = env.params.query["type"]?.try &.downcase
|
||||
content_type ||= "video"
|
||||
query = Invidious::Search::Query.new(env.params.query, :regular, region)
|
||||
|
||||
begin
|
||||
search_params = produce_search_params(page, sort_by, date, content_type, duration, features)
|
||||
search_results = query.process
|
||||
rescue ex
|
||||
return error_json(400, ex)
|
||||
end
|
||||
|
||||
search_results = search(query, search_params, region)
|
||||
JSON.build do |json|
|
||||
json.array do
|
||||
search_results.each do |item|
|
||||
|
@ -212,7 +212,10 @@ module Invidious::Routes::Playlists
|
||||
end
|
||||
|
||||
def self.add_playlist_items_page(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
prefs = env.get("preferences").as(Preferences)
|
||||
locale = prefs.locale
|
||||
|
||||
region = env.params.query["region"]? || prefs.region
|
||||
|
||||
user = env.get? "user"
|
||||
sid = env.get? "sid"
|
||||
@ -236,15 +239,10 @@ module Invidious::Routes::Playlists
|
||||
return env.redirect referer
|
||||
end
|
||||
|
||||
query = env.params.query["q"]?
|
||||
if query
|
||||
begin
|
||||
search_query, items, operators = process_search_query(query, page, user, region: nil)
|
||||
videos = items.select(SearchVideo).map(&.as(SearchVideo))
|
||||
rescue ex
|
||||
videos = [] of SearchVideo
|
||||
end
|
||||
else
|
||||
begin
|
||||
query = Invidious::Search::Query.new(env.params.query, :playlist, region)
|
||||
videos = query.process.select(SearchVideo).map(&.as(SearchVideo))
|
||||
rescue ex
|
||||
videos = [] of SearchVideo
|
||||
end
|
||||
|
||||
|
@ -37,37 +37,29 @@ module Invidious::Routes::Search
|
||||
end
|
||||
|
||||
def self.search(env)
|
||||
locale = env.get("preferences").as(Preferences).locale
|
||||
region = env.params.query["region"]?
|
||||
prefs = env.get("preferences").as(Preferences)
|
||||
locale = prefs.locale
|
||||
|
||||
query = env.params.query["search_query"]?
|
||||
query ||= env.params.query["q"]?
|
||||
region = env.params.query["region"]? || prefs.region
|
||||
|
||||
if !query || query.empty?
|
||||
query = Invidious::Search::Query.new(env.params.query, :regular, region)
|
||||
|
||||
if query.empty?
|
||||
# Display the full page search box implemented in #1977
|
||||
env.set "search", ""
|
||||
templated "search_homepage", navbar_search: false
|
||||
else
|
||||
page = env.params.query["page"]?.try &.to_i?
|
||||
page ||= 1
|
||||
|
||||
user = env.get? "user"
|
||||
|
||||
begin
|
||||
search_query, videos, operators = process_search_query(query, page, user, region: region)
|
||||
videos = query.process
|
||||
rescue ex : ChannelSearchException
|
||||
return error_template(404, "Unable to find channel with id of '#{HTML.escape(ex.channel)}'. Are you sure that's an actual channel id? It should look like 'UC4QobU6STFB0P71PMvOGN5A'.")
|
||||
rescue ex
|
||||
return error_template(500, ex)
|
||||
end
|
||||
|
||||
operator_hash = {} of String => String
|
||||
operators.each do |operator|
|
||||
key, value = operator.downcase.split(":")
|
||||
operator_hash[key] = value
|
||||
end
|
||||
|
||||
env.set "search", query
|
||||
env.set "search", query.text
|
||||
templated "search"
|
||||
end
|
||||
end
|
||||
|
@ -1,254 +0,0 @@
|
||||
class ChannelSearchException < InfoException
|
||||
getter channel : String
|
||||
|
||||
def initialize(@channel)
|
||||
end
|
||||
end
|
||||
|
||||
def channel_search(query, page, channel) : Array(SearchItem)
|
||||
response = YT_POOL.client &.get("/channel/#{channel}")
|
||||
|
||||
if response.status_code == 404
|
||||
response = YT_POOL.client &.get("/user/#{channel}")
|
||||
response = YT_POOL.client &.get("/c/#{channel}") if response.status_code == 404
|
||||
initial_data = extract_initial_data(response.body)
|
||||
ucid = initial_data.dig?("header", "c4TabbedHeaderRenderer", "channelId").try(&.as_s?)
|
||||
raise ChannelSearchException.new(channel) if !ucid
|
||||
else
|
||||
ucid = channel
|
||||
end
|
||||
|
||||
continuation = produce_channel_search_continuation(ucid, query, page)
|
||||
response_json = YoutubeAPI.browse(continuation)
|
||||
|
||||
continuation_items = response_json["onResponseReceivedActions"]?
|
||||
.try &.[0]["appendContinuationItemsAction"]["continuationItems"]
|
||||
|
||||
return [] of SearchItem if !continuation_items
|
||||
|
||||
items = [] of SearchItem
|
||||
continuation_items.as_a.select(&.as_h.has_key?("itemSectionRenderer")).each do |item|
|
||||
extract_item(item["itemSectionRenderer"]["contents"].as_a[0]).try { |t| items << t }
|
||||
end
|
||||
|
||||
return items
|
||||
end
|
||||
|
||||
def search(query, search_params = produce_search_params(content_type: "all"), region = nil) : Array(SearchItem)
|
||||
return [] of SearchItem if query.empty?
|
||||
|
||||
client_config = YoutubeAPI::ClientConfig.new(region: region)
|
||||
initial_data = YoutubeAPI.search(query, search_params, client_config: client_config)
|
||||
|
||||
return extract_items(initial_data)
|
||||
end
|
||||
|
||||
def produce_search_params(page = 1, sort : String = "relevance", date : String = "", content_type : String = "",
|
||||
duration : String = "", features : Array(String) = [] of String)
|
||||
object = {
|
||||
"1:varint" => 0_i64,
|
||||
"2:embedded" => {} of String => Int64,
|
||||
"9:varint" => ((page - 1) * 20).to_i64,
|
||||
}
|
||||
|
||||
case sort
|
||||
when "relevance"
|
||||
object["1:varint"] = 0_i64
|
||||
when "rating"
|
||||
object["1:varint"] = 1_i64
|
||||
when "upload_date", "date"
|
||||
object["1:varint"] = 2_i64
|
||||
when "view_count", "views"
|
||||
object["1:varint"] = 3_i64
|
||||
else
|
||||
raise "No sort #{sort}"
|
||||
end
|
||||
|
||||
case date
|
||||
when "hour"
|
||||
object["2:embedded"].as(Hash)["1:varint"] = 1_i64
|
||||
when "today"
|
||||
object["2:embedded"].as(Hash)["1:varint"] = 2_i64
|
||||
when "week"
|
||||
object["2:embedded"].as(Hash)["1:varint"] = 3_i64
|
||||
when "month"
|
||||
object["2:embedded"].as(Hash)["1:varint"] = 4_i64
|
||||
when "year"
|
||||
object["2:embedded"].as(Hash)["1:varint"] = 5_i64
|
||||
else nil # Ignore
|
||||
end
|
||||
|
||||
case content_type
|
||||
when "video"
|
||||
object["2:embedded"].as(Hash)["2:varint"] = 1_i64
|
||||
when "channel"
|
||||
object["2:embedded"].as(Hash)["2:varint"] = 2_i64
|
||||
when "playlist"
|
||||
object["2:embedded"].as(Hash)["2:varint"] = 3_i64
|
||||
when "movie"
|
||||
object["2:embedded"].as(Hash)["2:varint"] = 4_i64
|
||||
when "show"
|
||||
object["2:embedded"].as(Hash)["2:varint"] = 5_i64
|
||||
when "all"
|
||||
#
|
||||
else
|
||||
object["2:embedded"].as(Hash)["2:varint"] = 1_i64
|
||||
end
|
||||
|
||||
case duration
|
||||
when "short"
|
||||
object["2:embedded"].as(Hash)["3:varint"] = 1_i64
|
||||
when "long"
|
||||
object["2:embedded"].as(Hash)["3:varint"] = 2_i64
|
||||
else nil # Ignore
|
||||
end
|
||||
|
||||
features.each do |feature|
|
||||
case feature
|
||||
when "hd"
|
||||
object["2:embedded"].as(Hash)["4:varint"] = 1_i64
|
||||
when "subtitles"
|
||||
object["2:embedded"].as(Hash)["5:varint"] = 1_i64
|
||||
when "creative_commons", "cc"
|
||||
object["2:embedded"].as(Hash)["6:varint"] = 1_i64
|
||||
when "3d"
|
||||
object["2:embedded"].as(Hash)["7:varint"] = 1_i64
|
||||
when "live", "livestream"
|
||||
object["2:embedded"].as(Hash)["8:varint"] = 1_i64
|
||||
when "purchased"
|
||||
object["2:embedded"].as(Hash)["9:varint"] = 1_i64
|
||||
when "4k"
|
||||
object["2:embedded"].as(Hash)["14:varint"] = 1_i64
|
||||
when "360"
|
||||
object["2:embedded"].as(Hash)["15:varint"] = 1_i64
|
||||
when "location"
|
||||
object["2:embedded"].as(Hash)["23:varint"] = 1_i64
|
||||
when "hdr"
|
||||
object["2:embedded"].as(Hash)["25:varint"] = 1_i64
|
||||
else nil # Ignore
|
||||
end
|
||||
end
|
||||
|
||||
if object["2:embedded"].as(Hash).empty?
|
||||
object.delete("2:embedded")
|
||||
end
|
||||
|
||||
params = 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 params
|
||||
end
|
||||
|
||||
def produce_channel_search_continuation(ucid, query, page)
|
||||
if page <= 1
|
||||
idx = 0_i64
|
||||
else
|
||||
idx = 30_i64 * (page - 1)
|
||||
end
|
||||
|
||||
object = {
|
||||
"80226972:embedded" => {
|
||||
"2:string" => ucid,
|
||||
"3:base64" => {
|
||||
"2:string" => "search",
|
||||
"6:varint" => 1_i64,
|
||||
"7:varint" => 1_i64,
|
||||
"12:varint" => 1_i64,
|
||||
"15:base64" => {
|
||||
"3:varint" => idx,
|
||||
},
|
||||
"23:varint" => 0_i64,
|
||||
},
|
||||
"11:string" => query,
|
||||
"35:string" => "browse-feed#{ucid}search",
|
||||
},
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
def process_search_query(query, page, user, region)
|
||||
if user
|
||||
user = user.as(Invidious::User)
|
||||
view_name = "subscriptions_#{sha256(user.email)}"
|
||||
end
|
||||
|
||||
channel = nil
|
||||
content_type = "all"
|
||||
date = ""
|
||||
duration = ""
|
||||
features = [] of String
|
||||
sort = "relevance"
|
||||
subscriptions = nil
|
||||
|
||||
operators = query.split(" ").select(&.match(/\w+:[\w,]+/))
|
||||
operators.each do |operator|
|
||||
key, value = operator.downcase.split(":")
|
||||
|
||||
case key
|
||||
when "channel", "user"
|
||||
channel = operator.split(":")[-1]
|
||||
when "content_type", "type"
|
||||
content_type = value
|
||||
when "date"
|
||||
date = value
|
||||
when "duration"
|
||||
duration = value
|
||||
when "feature", "features"
|
||||
features = value.split(",")
|
||||
when "sort"
|
||||
sort = value
|
||||
when "subscriptions"
|
||||
subscriptions = value == "true"
|
||||
else
|
||||
operators.delete(operator)
|
||||
end
|
||||
end
|
||||
|
||||
search_query = (query.split(" ") - operators).join(" ")
|
||||
|
||||
if channel
|
||||
items = channel_search(search_query, page, channel)
|
||||
elsif subscriptions
|
||||
if view_name
|
||||
items = PG_DB.query_all("SELECT id,title,published,updated,ucid,author,length_seconds FROM (
|
||||
SELECT *,
|
||||
to_tsvector(#{view_name}.title) ||
|
||||
to_tsvector(#{view_name}.author)
|
||||
as document
|
||||
FROM #{view_name}
|
||||
) v_search WHERE v_search.document @@ plainto_tsquery($1) LIMIT 20 OFFSET $2;", search_query, (page - 1) * 20, as: ChannelVideo)
|
||||
else
|
||||
items = [] of ChannelVideo
|
||||
end
|
||||
else
|
||||
search_params = produce_search_params(page: page, sort: sort, date: date, content_type: content_type,
|
||||
duration: duration, features: features)
|
||||
|
||||
items = search(search_query, search_params, region)
|
||||
end
|
||||
|
||||
# Light processing to flatten search results out of Categories.
|
||||
# They should ideally be supported in the future.
|
||||
items_without_category = [] of SearchItem | ChannelVideo
|
||||
items.each do |i|
|
||||
if i.is_a? Category
|
||||
i.contents.each do |nest_i|
|
||||
if !nest_i.is_a? Video
|
||||
items_without_category << nest_i
|
||||
end
|
||||
end
|
||||
else
|
||||
items_without_category << i
|
||||
end
|
||||
end
|
||||
|
||||
{search_query, items_without_category, operators}
|
||||
end
|
32
src/invidious/search/ctoken.cr
Normal file
32
src/invidious/search/ctoken.cr
Normal file
@ -0,0 +1,32 @@
|
||||
def produce_channel_search_continuation(ucid, query, page)
|
||||
if page <= 1
|
||||
idx = 0_i64
|
||||
else
|
||||
idx = 30_i64 * (page - 1)
|
||||
end
|
||||
|
||||
object = {
|
||||
"80226972:embedded" => {
|
||||
"2:string" => ucid,
|
||||
"3:base64" => {
|
||||
"2:string" => "search",
|
||||
"6:varint" => 1_i64,
|
||||
"7:varint" => 1_i64,
|
||||
"12:varint" => 1_i64,
|
||||
"15:base64" => {
|
||||
"3:varint" => idx,
|
||||
},
|
||||
"23:varint" => 0_i64,
|
||||
},
|
||||
"11:string" => query,
|
||||
"35:string" => "browse-feed#{ucid}search",
|
||||
},
|
||||
}
|
||||
|
||||
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
|
376
src/invidious/search/filters.cr
Normal file
376
src/invidious/search/filters.cr
Normal file
@ -0,0 +1,376 @@
|
||||
require "protodec/utils"
|
||||
require "http/params"
|
||||
|
||||
module Invidious::Search
|
||||
struct Filters
|
||||
# Values correspond to { "2:embedded": { "1:varint": <X> }}
|
||||
# except for "None" which is only used by us (= nothing selected)
|
||||
enum Date
|
||||
None = 0
|
||||
Hour = 1
|
||||
Today = 2
|
||||
Week = 3
|
||||
Month = 4
|
||||
Year = 5
|
||||
end
|
||||
|
||||
# Values correspond to { "2:embedded": { "2:varint": <X> }}
|
||||
# except for "All" which is only used by us (= nothing selected)
|
||||
enum Type
|
||||
All = 0
|
||||
Video = 1
|
||||
Channel = 2
|
||||
Playlist = 3
|
||||
Movie = 4
|
||||
|
||||
# Has it been removed?
|
||||
# (Not available on youtube's UI)
|
||||
Show = 5
|
||||
end
|
||||
|
||||
# Values correspond to { "2:embedded": { "3:varint": <X> }}
|
||||
# except for "None" which is only used by us (= nothing selected)
|
||||
enum Duration
|
||||
None = 0
|
||||
Short = 1 # "Under 4 minutes"
|
||||
Long = 2 # "Over 20 minutes"
|
||||
Medium = 3 # "4 - 20 minutes"
|
||||
end
|
||||
|
||||
# Note: flag enums automatically generate
|
||||
# "none" and "all" members
|
||||
@[Flags]
|
||||
enum Features
|
||||
Live
|
||||
FourK # "4K"
|
||||
HD
|
||||
Subtitles # "Subtitles/CC"
|
||||
CCommons # "Creative Commons"
|
||||
ThreeSixty # "360°"
|
||||
VR180
|
||||
ThreeD # "3D"
|
||||
HDR
|
||||
Location
|
||||
Purchased
|
||||
end
|
||||
|
||||
# Values correspond to { "1:varint": <X> }
|
||||
enum Sort
|
||||
Relevance = 0
|
||||
Rating = 1
|
||||
Date = 2
|
||||
Views = 3
|
||||
end
|
||||
|
||||
# Parameters are sorted as on Youtube
|
||||
property date : Date
|
||||
property type : Type
|
||||
property duration : Duration
|
||||
property features : Features
|
||||
property sort : Sort
|
||||
|
||||
def initialize(
|
||||
*, # All parameters must be named
|
||||
@date : Date = Date::None,
|
||||
@type : Type = Type::All,
|
||||
@duration : Duration = Duration::None,
|
||||
@features : Features = Features::None,
|
||||
@sort : Sort = Sort::Relevance
|
||||
)
|
||||
end
|
||||
|
||||
def default? : Bool
|
||||
return @date.none? && @type.all? && @duration.none? && \
|
||||
@features.none? && @sort.relevance?
|
||||
end
|
||||
|
||||
# -------------------
|
||||
# Invidious params
|
||||
# -------------------
|
||||
|
||||
def self.parse_features(raw : Array(String)) : Features
|
||||
# Initialize return variable
|
||||
features = Features.new(0)
|
||||
|
||||
raw.each do |ft|
|
||||
case ft.downcase
|
||||
when "live", "livestream"
|
||||
features = features | Features::Live
|
||||
when "4k" then features = features | Features::FourK
|
||||
when "hd" then features = features | Features::HD
|
||||
when "subtitles" then features = features | Features::Subtitles
|
||||
when "creative_commons", "commons", "cc"
|
||||
features = features | Features::CCommons
|
||||
when "360" then features = features | Features::ThreeSixty
|
||||
when "vr180" then features = features | Features::VR180
|
||||
when "3d" then features = features | Features::ThreeD
|
||||
when "hdr" then features = features | Features::HDR
|
||||
when "location" then features = features | Features::Location
|
||||
when "purchased" then features = features | Features::Purchased
|
||||
end
|
||||
end
|
||||
|
||||
return features
|
||||
end
|
||||
|
||||
def self.format_features(features : Features) : String
|
||||
# Directly return an empty string if there are no features
|
||||
return "" if features.none?
|
||||
|
||||
# Initialize return variable
|
||||
str = [] of String
|
||||
|
||||
str << "live" if features.live?
|
||||
str << "4k" if features.four_k?
|
||||
str << "hd" if features.hd?
|
||||
str << "subtitles" if features.subtitles?
|
||||
str << "commons" if features.c_commons?
|
||||
str << "360" if features.three_sixty?
|
||||
str << "vr180" if features.vr180?
|
||||
str << "3d" if features.three_d?
|
||||
str << "hdr" if features.hdr?
|
||||
str << "location" if features.location?
|
||||
str << "purchased" if features.purchased?
|
||||
|
||||
return str.join(',')
|
||||
end
|
||||
|
||||
def self.from_legacy_filters(str : String) : {Filters, String, String, Bool}
|
||||
# Split search query on spaces
|
||||
members = str.split(' ')
|
||||
|
||||
# Output variables
|
||||
channel = ""
|
||||
filters = Filters.new
|
||||
subscriptions = false
|
||||
|
||||
# Array to hold the non-filter members
|
||||
query = [] of String
|
||||
|
||||
# Parse!
|
||||
members.each do |substr|
|
||||
# Separator operators
|
||||
operators = substr.split(':')
|
||||
|
||||
case operators[0]
|
||||
when "user", "channel"
|
||||
next if operators.size != 2
|
||||
channel = operators[1]
|
||||
#
|
||||
when "type", "content_type"
|
||||
next if operators.size != 2
|
||||
type = Type.parse?(operators[1])
|
||||
filters.type = type if !type.nil?
|
||||
#
|
||||
when "date"
|
||||
next if operators.size != 2
|
||||
date = Date.parse?(operators[1])
|
||||
filters.date = date if !date.nil?
|
||||
#
|
||||
when "duration"
|
||||
next if operators.size != 2
|
||||
duration = Duration.parse?(operators[1])
|
||||
filters.duration = duration if !duration.nil?
|
||||
#
|
||||
when "feature", "features"
|
||||
next if operators.size != 2
|
||||
features = parse_features(operators[1].split(','))
|
||||
filters.features = features if !features.nil?
|
||||
#
|
||||
when "sort"
|
||||
next if operators.size != 2
|
||||
sort = Sort.parse?(operators[1])
|
||||
filters.sort = sort if !sort.nil?
|
||||
#
|
||||
when "subscriptions"
|
||||
next if operators.size != 2
|
||||
subscriptions = {"true", "on", "yes", "1"}.any?(&.== operators[1])
|
||||
#
|
||||
else
|
||||
query << substr
|
||||
end
|
||||
end
|
||||
|
||||
# Re-assemble query (without filters)
|
||||
cleaned_query = query.join(' ')
|
||||
|
||||
return {filters, channel, cleaned_query, subscriptions}
|
||||
end
|
||||
|
||||
def self.from_iv_params(params : HTTP::Params) : Filters
|
||||
# Temporary variables
|
||||
filters = Filters.new
|
||||
|
||||
if type = params["type"]?
|
||||
filters.type = Type.parse?(type) || Type::All
|
||||
params.delete("type")
|
||||
end
|
||||
|
||||
if date = params["date"]?
|
||||
filters.date = Date.parse?(date) || Date::None
|
||||
params.delete("date")
|
||||
end
|
||||
|
||||
if duration = params["duration"]?
|
||||
filters.duration = Duration.parse?(duration) || Duration::None
|
||||
params.delete("duration")
|
||||
end
|
||||
|
||||
features = params.fetch_all("features")
|
||||
if !features.empty?
|
||||
# Un-array input so it can be treated as a comma-separated list
|
||||
features = features[0].split(',') if features.size == 1
|
||||
|
||||
filters.features = parse_features(features) || Features::None
|
||||
params.delete_all("features")
|
||||
end
|
||||
|
||||
if sort = params["sort"]?
|
||||
filters.sort = Sort.parse?(sort) || Sort::Relevance
|
||||
params.delete("sort")
|
||||
end
|
||||
|
||||
return filters
|
||||
end
|
||||
|
||||
def to_iv_params : HTTP::Params
|
||||
# Temporary variables
|
||||
raw_params = {} of String => Array(String)
|
||||
|
||||
raw_params["date"] = [@date.to_s.underscore] if !@date.none?
|
||||
raw_params["type"] = [@type.to_s.underscore] if !@type.all?
|
||||
raw_params["sort"] = [@sort.to_s.underscore] if !@sort.relevance?
|
||||
|
||||
if !@duration.none?
|
||||
raw_params["duration"] = [@duration.to_s.underscore]
|
||||
end
|
||||
|
||||
if !@features.none?
|
||||
raw_params["features"] = [Filters.format_features(@features)]
|
||||
end
|
||||
|
||||
return HTTP::Params.new(raw_params)
|
||||
end
|
||||
|
||||
# -------------------
|
||||
# Youtube params
|
||||
# -------------------
|
||||
|
||||
# Produce the youtube search parameters for the
|
||||
# innertube API (base64-encoded protobuf object).
|
||||
def to_yt_params(page : Int = 1) : String
|
||||
# Initialize the embedded protobuf object
|
||||
embedded = {} of String => Int64
|
||||
|
||||
# Add these field only if associated parameter is selected
|
||||
embedded["1:varint"] = @date.to_i64 if !@date.none?
|
||||
embedded["2:varint"] = @type.to_i64 if !@type.all?
|
||||
embedded["3:varint"] = @duration.to_i64 if !@duration.none?
|
||||
|
||||
if !@features.none?
|
||||
# All features have a value of "1" when enabled, and
|
||||
# the field is omitted when the feature is no selected.
|
||||
embedded["4:varint"] = 1_i64 if @features.includes?(Features::HD)
|
||||
embedded["5:varint"] = 1_i64 if @features.includes?(Features::Subtitles)
|
||||
embedded["6:varint"] = 1_i64 if @features.includes?(Features::CCommons)
|
||||
embedded["7:varint"] = 1_i64 if @features.includes?(Features::ThreeD)
|
||||
embedded["8:varint"] = 1_i64 if @features.includes?(Features::Live)
|
||||
embedded["9:varint"] = 1_i64 if @features.includes?(Features::Purchased)
|
||||
embedded["14:varint"] = 1_i64 if @features.includes?(Features::FourK)
|
||||
embedded["15:varint"] = 1_i64 if @features.includes?(Features::ThreeSixty)
|
||||
embedded["23:varint"] = 1_i64 if @features.includes?(Features::Location)
|
||||
embedded["25:varint"] = 1_i64 if @features.includes?(Features::HDR)
|
||||
embedded["26:varint"] = 1_i64 if @features.includes?(Features::VR180)
|
||||
end
|
||||
|
||||
# Initialize an empty protobuf object
|
||||
object = {} of String => (Int64 | String | Hash(String, Int64))
|
||||
|
||||
# As usual, everything can be omitted if it has no value
|
||||
object["2:embedded"] = embedded if !embedded.empty?
|
||||
|
||||
# Default sort is "relevance", so when this option is selected,
|
||||
# the associated field can be omitted.
|
||||
if !@sort.relevance?
|
||||
object["1:varint"] = @sort.to_i64
|
||||
end
|
||||
|
||||
# Add page number (if provided)
|
||||
if page > 1
|
||||
object["9:varint"] = ((page - 1) * 20).to_i64
|
||||
end
|
||||
|
||||
# If the object is empty, return an empty string,
|
||||
# otherwise encode to protobuf then to base64
|
||||
return "" if object.empty?
|
||||
|
||||
return 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) }
|
||||
end
|
||||
|
||||
# Function to parse the `sp` URL parameter from Youtube
|
||||
# search page. It's a base64-encoded protobuf object.
|
||||
def self.from_yt_params(params : HTTP::Params) : Filters
|
||||
# Initialize output variable
|
||||
filters = Filters.new
|
||||
|
||||
# Get parameter, and check emptyness
|
||||
search_params = params["sp"]?
|
||||
|
||||
if search_params.nil? || search_params.empty?
|
||||
return filters
|
||||
end
|
||||
|
||||
# Decode protobuf object
|
||||
object = search_params
|
||||
.try { |i| URI.decode_www_form(i) }
|
||||
.try { |i| Base64.decode(i) }
|
||||
.try { |i| IO::Memory.new(i) }
|
||||
.try { |i| Protodec::Any.parse(i) }
|
||||
|
||||
# Parse items from embedded object
|
||||
if embedded = object["2:0:embedded"]?
|
||||
# All the following fields (date, type, duration) are optional.
|
||||
if date = embedded["1:0:varint"]?
|
||||
filters.date = Date.from_value?(date.as_i) || Date::None
|
||||
end
|
||||
|
||||
if type = embedded["2:0:varint"]?
|
||||
filters.type = Type.from_value?(type.as_i) || Type::All
|
||||
end
|
||||
|
||||
if duration = embedded["3:0:varint"]?
|
||||
filters.duration = Duration.from_value?(duration.as_i) || Duration::None
|
||||
end
|
||||
|
||||
# All features should have a value of "1" when enabled, and
|
||||
# the field should be omitted when the feature is no selected.
|
||||
features = 0
|
||||
features += (embedded["4:0:varint"]?.try &.as_i == 1_i64) ? Features::HD.value : 0
|
||||
features += (embedded["5:0:varint"]?.try &.as_i == 1_i64) ? Features::Subtitles.value : 0
|
||||
features += (embedded["6:0:varint"]?.try &.as_i == 1_i64) ? Features::CCommons.value : 0
|
||||
features += (embedded["7:0:varint"]?.try &.as_i == 1_i64) ? Features::ThreeD.value : 0
|
||||
features += (embedded["8:0:varint"]?.try &.as_i == 1_i64) ? Features::Live.value : 0
|
||||
features += (embedded["9:0:varint"]?.try &.as_i == 1_i64) ? Features::Purchased.value : 0
|
||||
features += (embedded["14:0:varint"]?.try &.as_i == 1_i64) ? Features::FourK.value : 0
|
||||
features += (embedded["15:0:varint"]?.try &.as_i == 1_i64) ? Features::ThreeSixty.value : 0
|
||||
features += (embedded["23:0:varint"]?.try &.as_i == 1_i64) ? Features::Location.value : 0
|
||||
features += (embedded["25:0:varint"]?.try &.as_i == 1_i64) ? Features::HDR.value : 0
|
||||
features += (embedded["26:0:varint"]?.try &.as_i == 1_i64) ? Features::VR180.value : 0
|
||||
|
||||
filters.features = Features.from_value?(features) || Features::None
|
||||
end
|
||||
|
||||
if sort = object["1:0:varint"]?
|
||||
filters.sort = Sort.from_value?(sort.as_i) || Sort::Relevance
|
||||
end
|
||||
|
||||
# Remove URL parameter and return result
|
||||
params.delete("sp")
|
||||
return filters
|
||||
end
|
||||
end
|
||||
end
|
64
src/invidious/search/processors.cr
Normal file
64
src/invidious/search/processors.cr
Normal file
@ -0,0 +1,64 @@
|
||||
module Invidious::Search
|
||||
module Processors
|
||||
extend self
|
||||
|
||||
# Regular search (`/search` endpoint)
|
||||
def regular(query : Query) : Array(SearchItem)
|
||||
search_params = query.filters.to_yt_params(page: query.page)
|
||||
|
||||
client_config = YoutubeAPI::ClientConfig.new(region: query.region)
|
||||
initial_data = YoutubeAPI.search(query.text, search_params, client_config: client_config)
|
||||
|
||||
return extract_items(initial_data)
|
||||
end
|
||||
|
||||
# Search a youtube channel
|
||||
# TODO: clean code, and rely more on YoutubeAPI
|
||||
def channel(query : Query) : Array(SearchItem)
|
||||
response = YT_POOL.client &.get("/channel/#{query.channel}")
|
||||
|
||||
if response.status_code == 404
|
||||
response = YT_POOL.client &.get("/user/#{query.channel}")
|
||||
response = YT_POOL.client &.get("/c/#{query.channel}") if response.status_code == 404
|
||||
initial_data = extract_initial_data(response.body)
|
||||
ucid = initial_data.dig?("header", "c4TabbedHeaderRenderer", "channelId").try(&.as_s?)
|
||||
raise ChannelSearchException.new(query.channel) if !ucid
|
||||
else
|
||||
ucid = query.channel
|
||||
end
|
||||
|
||||
continuation = produce_channel_search_continuation(ucid, query.text, query.page)
|
||||
response_json = YoutubeAPI.browse(continuation)
|
||||
|
||||
continuation_items = response_json["onResponseReceivedActions"]?
|
||||
.try &.[0]["appendContinuationItemsAction"]["continuationItems"]
|
||||
|
||||
return [] of SearchItem if !continuation_items
|
||||
|
||||
items = [] of SearchItem
|
||||
continuation_items.as_a.select(&.as_h.has_key?("itemSectionRenderer")).each do |item|
|
||||
extract_item(item["itemSectionRenderer"]["contents"].as_a[0]).try { |t| items << t }
|
||||
end
|
||||
|
||||
return items
|
||||
end
|
||||
|
||||
# Search inside of user subscriptions
|
||||
def subscriptions(query : Query, user : Invidious::User) : Array(ChannelVideo)
|
||||
view_name = "subscriptions_#{sha256(user.email)}"
|
||||
|
||||
return PG_DB.query_all("
|
||||
SELECT id,title,published,updated,ucid,author,length_seconds
|
||||
FROM (
|
||||
SELECT *,
|
||||
to_tsvector(#{view_name}.title) ||
|
||||
to_tsvector(#{view_name}.author)
|
||||
as document
|
||||
FROM #{view_name}
|
||||
) v_search WHERE v_search.document @@ plainto_tsquery($1) LIMIT 20 OFFSET $2;",
|
||||
query.text, (query.page - 1) * 20,
|
||||
as: ChannelVideo
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
148
src/invidious/search/query.cr
Normal file
148
src/invidious/search/query.cr
Normal file
@ -0,0 +1,148 @@
|
||||
module Invidious::Search
|
||||
class Query
|
||||
enum Type
|
||||
# Types related to YouTube
|
||||
Regular # Youtube search page
|
||||
Channel # Youtube channel search box
|
||||
|
||||
# Types specific to Invidious
|
||||
Subscriptions # Search user subscriptions
|
||||
Playlist # "Add playlist item" search
|
||||
end
|
||||
|
||||
@type : Type = Type::Regular
|
||||
|
||||
@raw_query : String
|
||||
@query : String = ""
|
||||
|
||||
property filters : Filters = Filters.new
|
||||
property page : Int32
|
||||
property region : String?
|
||||
property channel : String = ""
|
||||
|
||||
# Return true if @raw_query is either `nil` or empty
|
||||
private def empty_raw_query?
|
||||
return @raw_query.empty?
|
||||
end
|
||||
|
||||
# Same as `empty_raw_query?`, but named for external use
|
||||
def empty?
|
||||
return self.empty_raw_query?
|
||||
end
|
||||
|
||||
# Getter for the query string.
|
||||
# It is named `text` to reduce confusion (`search_query.text` makes more
|
||||
# sense than `search_query.query`)
|
||||
def text
|
||||
return @query
|
||||
end
|
||||
|
||||
# Initialize a new search query.
|
||||
# Parameters are used to get the query string, the page number
|
||||
# and the search filters (if any). Type tells this function
|
||||
# where it is being called from (See `Type` above).
|
||||
def initialize(
|
||||
params : HTTP::Params,
|
||||
@type : Type = Type::Regular,
|
||||
@region : String? = nil
|
||||
)
|
||||
# Get the raw search query string (common to all search types). In
|
||||
# Regular search mode, also look for the `search_query` URL parameter
|
||||
if @type.regular?
|
||||
@raw_query = params["q"]? || params["search_query"]? || ""
|
||||
else
|
||||
@raw_query = params["q"]? || ""
|
||||
end
|
||||
|
||||
# Get the page number (also common to all search types)
|
||||
@page = params["page"]?.try &.to_i? || 1
|
||||
|
||||
# Stop here is raw query in empty
|
||||
# NOTE: maybe raise in the future?
|
||||
return if self.empty_raw_query?
|
||||
|
||||
# Specific handling
|
||||
case @type
|
||||
when .playlist?, .channel?
|
||||
# In "add playlist item" mode, filters are parsed from the query
|
||||
# string itself (legacy), and the channel is ignored.
|
||||
#
|
||||
# In "channel search" mode, filters are ignored, but we still parse
|
||||
# the query prevent transmission of legacy filters to youtube.
|
||||
#
|
||||
@filters, @query, @channel, _ = Filters.from_legacy_filters(@raw_query || "")
|
||||
#
|
||||
when .subscriptions?, .regular?
|
||||
if params["sp"]?
|
||||
# Parse the `sp` URL parameter (youtube compatibility)
|
||||
@filters = Filters.from_yt_params(params)
|
||||
@query = @raw_query || ""
|
||||
else
|
||||
# Parse invidious URL parameters (sort, date, etc...)
|
||||
@filters = Filters.from_iv_params(params)
|
||||
@channel = params["channel"]? || ""
|
||||
|
||||
if @filters.default? && @raw_query.includes?(':')
|
||||
# Parse legacy filters from query
|
||||
@filters, @query, @channel, subs = Filters.from_legacy_filters(@raw_query || "")
|
||||
else
|
||||
@query = @raw_query || ""
|
||||
end
|
||||
|
||||
if !@channel.empty?
|
||||
# Switch to channel search mode (filters will be ignored)
|
||||
@type = Type::Channel
|
||||
elsif subs
|
||||
# Switch to subscriptions search mode
|
||||
@type = Type::Subscriptions
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Run the search query using the corresponding search processor.
|
||||
# Returns either the results or an empty array of `SearchItem`.
|
||||
def process(user : Invidious::User? = nil) : Array(SearchItem) | Array(ChannelVideo)
|
||||
items = [] of SearchItem
|
||||
|
||||
# Don't bother going further if search query is empty
|
||||
return items if self.empty_raw_query?
|
||||
|
||||
case @type
|
||||
when .regular?, .playlist?
|
||||
items = unnest_items(Processors.regular(self))
|
||||
#
|
||||
when .channel?
|
||||
items = Processors.channel(self)
|
||||
#
|
||||
when .subscriptions?
|
||||
if user
|
||||
items = Processors.subscriptions(self, user.as(Invidious::User))
|
||||
end
|
||||
end
|
||||
|
||||
return items
|
||||
end
|
||||
|
||||
# TODO: clean code
|
||||
private def unnest_items(all_items) : Array(SearchItem)
|
||||
items = [] of SearchItem
|
||||
|
||||
# Light processing to flatten search results out of Categories.
|
||||
# They should ideally be supported in the future.
|
||||
all_items.each do |i|
|
||||
if i.is_a? Category
|
||||
i.contents.each do |nest_i|
|
||||
if !nest_i.is_a? Video
|
||||
items << nest_i
|
||||
end
|
||||
end
|
||||
else
|
||||
items << i
|
||||
end
|
||||
end
|
||||
|
||||
return items
|
||||
end
|
||||
end
|
||||
end
|
@ -11,7 +11,9 @@
|
||||
<legend><a href="/playlist?list=<%= playlist.id %>"><%= translate(locale, "Editing playlist `x`", %|"#{HTML.escape(playlist.title)}"|) %></a></legend>
|
||||
|
||||
<fieldset>
|
||||
<input class="pure-input-1" type="search" name="q" <% if query %>value="<%= HTML.escape(query) %>"<% else %>placeholder="<%= translate(locale, "Search for videos") %>"<% end %>>
|
||||
<input class="pure-input-1" type="search" name="q"
|
||||
<% if query %>value="<%= HTML.escape(query.text) %>"<% end %>
|
||||
placeholder="<%= translate(locale, "Search for videos") %>">
|
||||
<input type="hidden" name="list" value="<%= plid %>">
|
||||
</fieldset>
|
||||
</form>
|
||||
@ -38,10 +40,11 @@
|
||||
</div>
|
||||
|
||||
<% if query %>
|
||||
<%- query_encoded = URI.encode_www_form(query.text, space_to_plus: true) -%>
|
||||
<div class="pure-g h-box">
|
||||
<div class="pure-u-1 pure-u-lg-1-5">
|
||||
<% if page > 1 %>
|
||||
<a href="/add_playlist_items?list=<%= plid %>&q=<%= URI.encode_www_form(query.not_nil!) %>&page=<%= page - 1 %>">
|
||||
<% if query.page > 1 %>
|
||||
<a href="/add_playlist_items?list=<%= plid %>&q=<%= query_encoded %>&page=<%= page - 1 %>">
|
||||
<%= translate(locale, "Previous page") %>
|
||||
</a>
|
||||
<% end %>
|
||||
@ -49,7 +52,7 @@
|
||||
<div class="pure-u-1 pure-u-lg-3-5"></div>
|
||||
<div class="pure-u-1 pure-u-lg-1-5" style="text-align:right">
|
||||
<% if videos.size >= 20 %>
|
||||
<a href="/add_playlist_items?list=<%= plid %>&q=<%= URI.encode_www_form(query.not_nil!) %>&page=<%= page + 1 %>">
|
||||
<a href="/add_playlist_items?list=<%= plid %>&q=<%= query_encoded %>&page=<%= page + 1 %>">
|
||||
<%= translate(locale, "Next page") %>
|
||||
</a>
|
||||
<% end %>
|
||||
|
@ -1,147 +1,62 @@
|
||||
<% content_for "header" do %>
|
||||
<title><%= search_query.not_nil!.size > 30 ? HTML.escape(query.not_nil![0,30].rstrip(".") + "...") : HTML.escape(query.not_nil!) %> - Invidious</title>
|
||||
<title><%= query.text.size > 30 ? HTML.escape(query.text[0,30].rstrip(".")) + "…" : HTML.escape(query.text) %> - Invidious</title>
|
||||
<link rel="stylesheet" href="/css/search.css?v=<%= ASSET_COMMIT %>">
|
||||
<% end %>
|
||||
|
||||
<% search_query_encoded = env.get?("search").try { |x| URI.encode_www_form(x.as(String), space_to_plus: true) } %>
|
||||
<%-
|
||||
search_query_encoded = URI.encode_www_form(query.text, space_to_plus: true)
|
||||
filter_params = query.filters.to_iv_params
|
||||
|
||||
url_prev_page = "/search?q=#{search_query_encoded}&#{filter_params}&page=#{query.page - 1}"
|
||||
url_next_page = "/search?q=#{search_query_encoded}&#{filter_params}&page=#{query.page + 1}"
|
||||
|
||||
redirect_url = Invidious::Frontend::Misc.redirect_url(env)
|
||||
-%>
|
||||
|
||||
<!-- Search redirection and filtering UI -->
|
||||
<% if videos.size == 0 %>
|
||||
<h3 style="text-align: center">
|
||||
<a href="/redirect?referer=<%= env.get?("current_page") %>"><%= translate(locale, "Broken? Try another Invidious Instance!") %></a>
|
||||
</h3>
|
||||
<% else %>
|
||||
<details id="filters">
|
||||
<summary>
|
||||
<h3 style="display:inline"> <%= translate(locale, "filter") %> </h3>
|
||||
</summary>
|
||||
<div id="filters" class="pure-g h-box">
|
||||
<div class="pure-u-1-3 pure-u-md-1-5">
|
||||
<b><%= translate(locale, "date") %></b>
|
||||
<hr/>
|
||||
<% ["hour", "today", "week", "month", "year"].each do |date| %>
|
||||
<div class="pure-u-1 pure-md-1-5">
|
||||
<% if operator_hash.fetch("date", "all") == date %>
|
||||
<b><%= translate(locale, date) %></b>
|
||||
<% else %>
|
||||
<a href="/search?q=<%= URI.encode_www_form(query.not_nil!.gsub(/ ?date:[a-z]+/, "") + " date:" + date) %>&page=<%= page %>">
|
||||
<%= translate(locale, date) %>
|
||||
</a>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="pure-u-1-3 pure-u-md-1-5">
|
||||
<b><%= translate(locale, "content_type") %></b>
|
||||
<hr/>
|
||||
<% ["video", "channel", "playlist", "movie", "show"].each do |content_type| %>
|
||||
<div class="pure-u-1 pure-md-1-5">
|
||||
<% if operator_hash.fetch("content_type", "all") == content_type %>
|
||||
<b><%= translate(locale, content_type) %></b>
|
||||
<% else %>
|
||||
<a href="/search?q=<%= URI.encode_www_form(query.not_nil!.gsub(/ ?content_type:[a-z]+/, "") + " content_type:" + content_type) %>&page=<%= page %>">
|
||||
<%= translate(locale, content_type) %>
|
||||
</a>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="pure-u-1-3 pure-u-md-1-5">
|
||||
<b><%= translate(locale, "duration") %></b>
|
||||
<hr/>
|
||||
<% ["short", "long"].each do |duration| %>
|
||||
<div class="pure-u-1 pure-md-1-5">
|
||||
<% if operator_hash.fetch("duration", "all") == duration %>
|
||||
<b><%= translate(locale, duration) %></b>
|
||||
<% else %>
|
||||
<a href="/search?q=<%= URI.encode_www_form(query.not_nil!.gsub(/ ?duration:[a-z]+/, "") + " duration:" + duration) %>&page=<%= page %>">
|
||||
<%= translate(locale, duration) %>
|
||||
</a>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="pure-u-1-3 pure-u-md-1-5">
|
||||
<b><%= translate(locale, "features") %></b>
|
||||
<hr/>
|
||||
<% ["hd", "subtitles", "creative_commons", "3d", "live", "purchased", "4k", "360", "location", "hdr"].each do |feature| %>
|
||||
<div class="pure-u-1 pure-md-1-5">
|
||||
<% if operator_hash.fetch("features", "all").includes?(feature) %>
|
||||
<b><%= translate(locale, feature) %></b>
|
||||
<% elsif operator_hash.has_key?("features") %>
|
||||
<a href="/search?q=<%= URI.encode_www_form(query.not_nil!.gsub(/features:/, "features:" + feature + ",")) %>&page=<%= page %>">
|
||||
<%= translate(locale, feature) %>
|
||||
</a>
|
||||
<% else %>
|
||||
<a href="/search?q=<%= URI.encode_www_form(query.not_nil! + " features:" + feature) %>&page=<%= page %>">
|
||||
<%= translate(locale, feature) %>
|
||||
</a>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="pure-u-1-3 pure-u-md-1-5">
|
||||
<b><%= translate(locale, "sort") %></b>
|
||||
<hr/>
|
||||
<% ["relevance", "rating", "date", "views"].each do |sort| %>
|
||||
<div class="pure-u-1 pure-md-1-5">
|
||||
<% if operator_hash.fetch("sort", "relevance") == sort %>
|
||||
<b><%= translate(locale, sort) %></b>
|
||||
<% else %>
|
||||
<a href="/search?q=<%= URI.encode_www_form(query.not_nil!.gsub(/ ?sort:[a-z]+/, "") + " sort:" + sort) %>&page=<%= page %>">
|
||||
<%= translate(locale, sort) %>
|
||||
</a>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<% end %>
|
||||
|
||||
<% if videos.size == 0 %>
|
||||
<hr style="margin: 0;"/>
|
||||
<% else %>
|
||||
<hr/>
|
||||
<% end %>
|
||||
<%= Invidious::Frontend::SearchFilters.generate(query.filters, query.text, query.page, locale) %>
|
||||
<hr/>
|
||||
|
||||
<div class="pure-g h-box v-box">
|
||||
<div class="pure-u-1 pure-u-lg-1-5">
|
||||
<% if page > 1 %>
|
||||
<a href="/search?q=<%= search_query_encoded %>&page=<%= page - 1 %>">
|
||||
<%= translate(locale, "Previous page") %>
|
||||
</a>
|
||||
<% end %>
|
||||
<%- if query.page > 1 -%>
|
||||
<a href="<%= url_prev_page %>"><%= translate(locale, "Previous page") %></a>
|
||||
<%- end -%>
|
||||
</div>
|
||||
<div class="pure-u-1 pure-u-lg-3-5"></div>
|
||||
<div class="pure-u-1 pure-u-lg-1-5" style="text-align:right">
|
||||
<% if videos.size >= 20 %>
|
||||
<a href="/search?q=<%= search_query_encoded %>&page=<%= page + 1 %>">
|
||||
<%= translate(locale, "Next page") %>
|
||||
</a>
|
||||
<% end %>
|
||||
<%- if videos.size >= 20 -%>
|
||||
<a href="<%= url_next_page %>"><%= translate(locale, "Next page") %></a>
|
||||
<%- end -%>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-g">
|
||||
<% videos.each do |item| %>
|
||||
<%= rendered "components/item" %>
|
||||
<% end %>
|
||||
<%- if videos.empty? -%>
|
||||
<div class="h-box no-results-error">
|
||||
<div>
|
||||
<%= translate(locale, "search_message_no_results") %><br/><br/>
|
||||
<%= translate(locale, "search_message_change_filters_or_query") %><br/><br/>
|
||||
<%= translate(locale, "search_message_use_another_instance", redirect_url) %>
|
||||
</div>
|
||||
</div>
|
||||
<%- else -%>
|
||||
<div class="pure-g">
|
||||
<%- videos.each do |item| -%>
|
||||
<%= rendered "components/item" %>
|
||||
<%- end -%>
|
||||
</div>
|
||||
<%- end -%>
|
||||
|
||||
<div class="pure-g h-box">
|
||||
<div class="pure-u-1 pure-u-lg-1-5">
|
||||
<% if page > 1 %>
|
||||
<a href="/search?q=<%= search_query_encoded %>&page=<%= page - 1 %>">
|
||||
<%= translate(locale, "Previous page") %>
|
||||
</a>
|
||||
<% end %>
|
||||
<%- if query.page > 1 -%>
|
||||
<a href="<%= url_prev_page %>"><%= translate(locale, "Previous page") %></a>
|
||||
<%- end -%>
|
||||
</div>
|
||||
<div class="pure-u-1 pure-u-lg-3-5"></div>
|
||||
<div class="pure-u-1 pure-u-lg-1-5" style="text-align:right">
|
||||
<% if videos.size >= 20 %>
|
||||
<a href="/search?q=<%= search_query_encoded %>&page=<%= page + 1 %>">
|
||||
<%= translate(locale, "Next page") %>
|
||||
</a>
|
||||
<% end %>
|
||||
<%- if videos.size >= 20 -%>
|
||||
<a href="<%= url_next_page %>"><%= translate(locale, "Next page") %></a>
|
||||
<%- end -%>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user