Compare commits

..

751 Commits

Author SHA1 Message Date
Émilien (perso) 93559cbdd5
Merge pull request #3914 from eltociear/patch-1
Fix typo in jobs.cr
1 year ago
Ikko Eltociear Ashimine 495ccdc221
Fix typo in jobs.cr
follwing -> following
1 year ago
Samantaz Fox e6f5fcbc4b
User: Strip empty new lines before parsing CSV (#3895) 1 year ago
Samantaz Fox df6b51f9c6
Comments: Fix display of youtube links (#3911) 1 year ago
lamemakes 7a569d81ca Updated comment link returns 1 year ago
Émilien (perso) 875b8ea0c2
Merge pull request #3903 from unixfox/temporary-message-video-not-available 1 year ago
Emilien Devos 8e4833d21a temp explanation about video not available issue 1 year ago
Samantaz Fox c3a3f98014
User: Remove broken Google login (#3893) 1 year ago
Samantaz Fox b06c87ff8d
User: Remove broken Google login (various comments) 1 year ago
Samantaz Fox 69f23d95b8
User: Remove broken Google login (various functions) 1 year ago
Samantaz Fox 3444117818
User: Remove broken Google login (various constants) 1 year ago
Samantaz Fox 39ff94362e
User: Remove broken Google login (feeds route) 1 year ago
Samantaz Fox 11ab6ffb32
User: Remove broken Google login (notifications route) 1 year ago
Samantaz Fox 9dd4195dd0
User: Remove broken Google login (subscribe route) 1 year ago
Samantaz Fox fcbd5106c3
User: Remove broken Google login (password change route) 1 year ago
Samantaz Fox 836898754e
User: Remove broken Google login (before_all route) 1 year ago
Samantaz Fox d3b04ac68c
User: Remove broken Google login (dedicated captcha route) 1 year ago
Samantaz Fox b2b61ab0a9
User: Remove broken Google login (login route) 1 year ago
Samantaz Fox 62bd895562
User: Remove broken Google login (HTML form) 1 year ago
Samantaz Fox 8d2ab70cbc
User: Remove broken Google login (localized strings) 1 year ago
Samantaz Fox 3024424ea2
Translations update from Hosted Weblate (#3824) 1 year ago
Samantaz Fox 5af87f97a3
Fix broken Italian locale (i18next v3->v4 mixup) 1 year ago
Samantaz Fox 96238d719d
Fix broken Spanish locale (i18next v3->v4 mixup) 1 year ago
Daniele Tricoli 52c317f235
Update Italian translation 1 year ago
maboroshin f954483eac
Update Japanese translation 1 year ago
Translator a4ca460651
Update French translation 1 year ago
maboroshin 37bab74085
Update Japanese translation 1 year ago
Nicolas Dommanget-Muller 50d6a2afb9
Update French translation 1 year ago
Translator daccbc2abb
Update French translation 1 year ago
04f7rx0n6 d250b4132b
Update Russian translation 1 year ago
joaooliva 3690631cdd
Update Portuguese (Brazil) translation 1 year ago
xrfmkrh 3b6474d72b
Update Korean translation 1 year ago
maboroshin fd3e2aa868
Update Japanese translation 1 year ago
gallegonovato 14a5751a47
Update Spanish translation 1 year ago
Andrey fda8d2d4d3
Update Russian translation 1 year ago
Samantaz Fox 46ea22f75c
Watch: Load watch page data for premieres (#3866) 1 year ago
Samantaz Fox 68c26563fc
Comments: Don't break JavaScript when loading more (#3870) 1 year ago
TheFrenchGhosty 9cec83c1ff
Merge pull request #3897 from IceTheDev2/patch-1
Consistent formatting in the README
1 year ago
IceTheDev2 281c8ecbf5
Update README.md 1 year ago
Samantaz Fox 1b942f4f0a
User: Strip empty new lines before parsing CSV 1 year ago
Samantaz Fox e7bed765fe
Chore: Bump various constants (#3843) 1 year ago
Samantaz Fox 7556cb69f2
YT API: Bump WEB/MWEB client versions 1 year ago
Samantaz Fox b5e30d66d4
YT API: Bump Android app version 1 year ago
Samantaz Fox d9521c82cf
YT API: Bump iOS app version 1 year ago
Samantaz Fox 505a1566d1
Misc: Update User-Agent string 1 year ago
Émilien Devos (perso) 19192b8be1
Merge pull request #3874 from SamantazFox/api-only-makefile 1 year ago
Samantaz Fox 867d488931
Makefile: Add API_ONLY variable 1 year ago
Chunky programmer 45cc835694 Comments: Don't break JavaScript when loading more 1 year ago
Chunky programmer 233bd3f593 Watch: Load watch page data for premieres 1 year ago
Émilien Devos (perso) 545a5937d8
Only close at 90% similarity 1 year ago
Émilien Devos (perso) 35694cc7e3
Warn about hmac key deadline (#3855) 1 year ago
Emilien Devos 372192eabc warn about hmac key deadline 1 year ago
Émilien Devos (perso) bc06c2fc27
Better message for auto close 1 year ago
Émilien Devos (perso) 7ea6ec1f52
add one return line for the reply message 1 year ago
Emilien Devos 042ad1f266 auto close duplicated issues 1 year ago
Samantaz Fox bbf16279bb
Search: Allow browser suggestions (#3724) 1 year ago
Samantaz Fox 16ac3be85b
Playlists: Fix duplicated videos across pages (#3807) 1 year ago
Samantaz Fox 21f0b90354
Utils: Add support for short "x ago" forms (#3825) 1 year ago
Samantaz Fox 928ea75dbc
Search: Parse playlists when searching a channel (#3804) 1 year ago
Samantaz Fox 4414c9df70
specc: Add tests for 'decode_date' 1 year ago
Samantaz Fox 898066407d
Utils: Update 'decode_date' to take into account short "x ago" forms 1 year ago
Samantaz Fox 381a0e326d
Comments: Cleanup code (#3784) 1 year ago
Samantaz Fox 193c510c65
Spec: Update require to point to new files 1 year ago
Samantaz Fox f0c8477905
Comments: Move content-related functions to their own file 1 year ago
Samantaz Fox 4379a3d873
Comments: Move ctoken functions to youtube.cr 1 year ago
Samantaz Fox df85265453
Comments: Move link utility functions to own file + module 1 year ago
Samantaz Fox de78848039
Comments: Move 'template_reddit' function to own file + module 1 year ago
Samantaz Fox e10f6b6626
Comments: Move 'template_youtube' function to own file + module 1 year ago
Samantaz Fox 634e913da9
Comments: Move 'fetch_reddit' function to own file + module 1 year ago
Samantaz Fox 1b25737b01
Comments: Move 'fetch_youtube' function to own file + module 1 year ago
Samantaz Fox 8dd1824869
Comments: Move reddit type definitions to their own file 1 year ago
Samantaz Fox c7876d564f
Comments: add 'require' statement for a dedicated folder 1 year ago
Samantaz Fox 5d176ad6de
Translations update from Hosted Weblate (#3797) 1 year ago
Samantaz Fox 4b29f8254a
Fix broken Spanish locale (i18next v3->v4 mixup) 1 year ago
Fjuro c9eafb250f
Update Czech translation 1 year ago
Milo Ivir fe97b3d761
Update Croatian translation 1 year ago
maboroshin ed2d16c91d
Update Japanese translation 1 year ago
Jeff Huang a727bb037f
Update Chinese (Traditional) translation 1 year ago
Oğuz Ersen f0f6cb0d83
Update Turkish translation 1 year ago
Eric e8df08e41e
Update Chinese (Simplified) translation 1 year ago
Ihor Hordiichuk fd06656d86
Update Ukrainian translation 1 year ago
Jorge Maldonado Ventura ea6db9c58a
Update Esperanto translation 1 year ago
Jorge Maldonado Ventura 184bd3204f
Update Spanish translation 1 year ago
atilluF f0120bece1
Update Italian translation 1 year ago
Rex_sa 7e3c685cd6
Update Arabic translation 1 year ago
Matthaiks 67a79faaeb
Update Polish translation 1 year ago
Ashirg-ch 11d45adcdc
Update German translation 1 year ago
joaooliva f2cc97b290
Update Portuguese (Brazil) translation 1 year ago
Alex e656714542
Update German translation 1 year ago
maboroshin a79b7ef170
Update Japanese translation 1 year ago
gallegonovato ef4ff4e4b2
Update Spanish translation 1 year ago
Samantaz Fox 9c0c39baed
Community: Fix position of the "creator heart" (#3817) 1 year ago
Samantaz Fox 6440ae0b5c
Community: Fix position of the "creator heart" (broken by #3783) 2 years ago
Samantaz Fox e238c08be5
Search: Populate search bar with ChannelId (#3805) 2 years ago
Samantaz Fox 8d434ac06a
User: Allow CSV Youtube playlists to be imported (#3595) 2 years ago
Samantaz Fox 1333e6db26
API: Add hashtag endpoint (#3692) 2 years ago
ChunkyProgrammer 8bd2e60abc
Use string interpolation instead of concatenation
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
chunky programmer c713c32ceb Fix issue where playlists will refetch the same videos 2 years ago
chunky programmer 12b4dd9191 Populate search bar with ChannelId 2 years ago
chunky programmer b2a0e6f1ff Parse playlists when searching a channel 2 years ago
chunky programmer d728599251 add page parameter 2 years ago
ChunkyProgrammer d6fb5c03b7 add hashtag endpoint 2 years ago
Samantaz Fox 3a54e9556b
Hashtag: Fix continuation token (#3803) 2 years ago
Samantaz Fox 6755e31b72
Fix hashtag continuation token 2 years ago
Samantaz Fox 10fee9da61
Community: Fix live video + parse playlist attachments (#3767) 2 years ago
Samantaz Fox b420de6977
Subscriptions: Fix Nil assertion failed (#3793) 2 years ago
Samantaz Fox febd14f703
Community: Minor HTML/CSS fixes (#3783) 2 years ago
Samantaz Fox 92f6a4d546
Translations update from Hosted Weblate (#3780) 2 years ago
Samantaz Fox 544fc9f92e
Fix broken Spanish locale (i18next v3->v4 mixup) 2 years ago
Samantaz Fox c385a944e6
Subscriptions: Fix casing of XML tag names 2 years ago
Samantaz Fox ce1fb8d08c
Use XML.parse instead of XML.parse_html
Due to recent changes to libxml2 (between 2.9.14 and 2.10.4,
See https://gitlab.gnome.org/GNOME/libxml2/-/issues/508), the
HTML parser doesn't take into account the namespaces (xmlns).

Because HTML shouldn't contain namespaces anyway, there is no
reason for use to keep using it. But switching to the XML
parser means that we have to pass the namespaces to every
single 'xpath_node(s)' method for it to be able to properly
navigate the XML structure.
2 years ago
gallegonovato 56ebb477ca
Update Spanish translation 2 years ago
xrfmkrh cca8bcf2a8
Update Korean translation 2 years ago
Fjuro f3d9db10a2
Update Czech translation 2 years ago
Émilien Devos (perso) 46a9ce811a
Document save playback position (#3790) 2 years ago
Émilien Devos (perso) 36f7c99cfb
Update config.example.yml
Document save playback position in the config.example.yml
2 years ago
Samantaz Fox 720789b622
HTML: wrap comments metadata in a paragraph 2 years ago
Samantaz Fox ce2649420f
CSS: Fix iframe attachment size in community posts 2 years ago
Samantaz Fox 7aac401407
CSS: limit width of the comments in community tab 2 years ago
ChunkyProgrammer 2d5145614b
Fix unknown type attachment
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Samantaz Fox 1eb1bae370
Fix watch history order (#3653) 2 years ago
Samantaz Fox 5017176e39
Trending: fix mistakes from #3773 (#3781) 2 years ago
Samantaz Fox efda154ec8
Allow channel urls to be displayed in video's description (#3759) 2 years ago
Samantaz Fox c1fb320094
Channels: Fix index out of bounds error (#3737) 2 years ago
Samantaz Fox 90914343ec
Trending: de-duplicate results 2 years ago
Samantaz Fox 384a8e200c
Trending: fix mistakes from #3773 2 years ago
ChunkyProgrammer 28584f22c5 Fix index out of bounds error 2 years ago
chunky programmer 1b10446e5e move url parsing to utils method 2 years ago
chunky programmer d420741cc1 Allow channel urls to be displayed in YT description 2 years ago
chunky programmer f298e225a1 fix live video attachments, parse playlists 2 years ago
Samantaz Fox 97e3938f5f
Translations update from Hosted Weblate (#3708) 2 years ago
Samantaz Fox deed4d10f2
Fix broken Spanish/Italian locales (i18next v3->v4 mixup) 2 years ago
Артём Котлубай 70a79f343d
Update Russian translation 2 years ago
Артём Котлубай e6471feadc
Update Russian translation 2 years ago
John Donne 49e04192c0
Update French translation 2 years ago
Nicolas Dommanget-Muller 1f12323ee6
Update French translation 2 years ago
John Donne 732fb7c499
Update French translation 2 years ago
AHOHNMYC 14053821ac
Update Russian translation 2 years ago
SC 346f32855a
Update Portuguese translation 2 years ago
Ernestas 7d48b96173
Update Lithuanian translation 2 years ago
atilluF b9932b113b
Update Italian translation 2 years ago
Andrey 72f83d4aa2
Update Russian translation 2 years ago
Jeff Huang 919997e41c
Update Chinese (Traditional) translation 2 years ago
Damjan Gerl 6667bdcd92
Update Slovenian translation 2 years ago
victor dargallo cb0e837a5e
Update Catalan translation 2 years ago
Fjuro e6ba3e3dab
Update Czech translation 2 years ago
Milo Ivir f81bc96da0
Update Croatian translation 2 years ago
abyan akhtar 4c541489dd
Update Indonesian translation 2 years ago
Jorge Maldonado Ventura c60c14851b
Update Esperanto translation 2 years ago
Rex_sa d857ee5a7c
Update Arabic translation 2 years ago
Oğuz Ersen 657486c19a
Update Turkish translation 2 years ago
Eric 9d52ddbf8d
Update Chinese (Simplified) translation 2 years ago
Ihor Hordiichuk d8337252a8
Update Ukrainian translation 2 years ago
gallegonovato 66e671237f
Update Spanish translation 2 years ago
victor dargallo d5a516d76c
Update Catalan translation 2 years ago
maboroshin 231fb3481e
Update Japanese translation 2 years ago
Damjan Gerl 7b4e3639cf
Update Slovenian translation 2 years ago
victor dargallo 778edf63cb
Update Catalan translation 2 years ago
SC fe1648e72e
Update Portuguese translation 2 years ago
Fjuro 1825b8edb3
Update Czech translation 2 years ago
Milo Ivir a3e587657f
Update Croatian translation 2 years ago
Parsa 4078fc5818
Update Persian translation 2 years ago
Jeff Huang a9fcfcf7c9
Update Chinese (Traditional) translation 2 years ago
Oğuz Ersen 4aa2c406ff
Update Turkish translation 2 years ago
Eric f46cc98654
Update Chinese (Simplified) translation 2 years ago
Ihor Hordiichuk ec1d6ee851
Update Ukrainian translation 2 years ago
Jorge Maldonado Ventura 9eafbbdcbb
Update Esperanto translation 2 years ago
Jorge Maldonado Ventura 5c24bf1322
Update Spanish translation 2 years ago
gallegonovato b97b5b5859
Update Spanish translation 2 years ago
Rex_sa d139334376
Update Arabic translation 2 years ago
Matthaiks 155f5fef97
Update Polish translation 2 years ago
Ashirg-ch 17ecdbaf7d
Update German translation 2 years ago
AHOHNMYC 67859113fd
Update Russian translation 2 years ago
Samantaz Fox eefc8bbbdd
Search/Trending: Fix duplicated results (#3773) 2 years ago
Samantaz Fox ff5e42d836
Channels: Fix fetching more community posts (#3761) 2 years ago
Samantaz Fox 3cfbc19ccc
Extractors: Add utility function to extract items from categories 2 years ago
Samantaz Fox 7afa03d821
Search: Don't extract items from categories too 2 years ago
Samantaz Fox 0107b774f2
Trending: Don't extract items from categories 2 years ago
Samantaz Fox 9dfa268204
CI: bump crystal version in test matrix (#3769) 2 years ago
Samantaz Fox e24feab1f7
CI: Remove crystal 1.3.2 2 years ago
Samantaz Fox d1b51e57a2
CI: Add crystal 1.7.3 and 1.8.1 2 years ago
chunky programmer 5517a4eadb fix fetching community continuations 2 years ago
Samantaz Fox 961cae2b9a
Improve accessibility (#3710) 2 years ago
Samantaz Fox adc605024f
Videos: Add support for attributed descriptions (#3701) 2 years ago
Samantaz Fox 9a765418d1
Update specs 2 years ago
Samantaz Fox b3c0afef02
Videos: fix description text offset when emojis are present 2 years ago
Émilien Devos (perso) 525e4bd67a
Merge pull request #3700 from ChunkyProgrammer/fix-shorts
Fix parsing shorts on channel page
2 years ago
Gavin c421f1f205
Merge branch 'iv-org:master' into youtube-playlist-import 2 years ago
thtmnisamnstr fffdaa1410 Updated csv reading as per feedback and ran
Signed-off-by: thtmnisamnstr <gavinj1984@gmail.com>
2 years ago
raphj 600da635b7
Allow browser suggestions for search (#3704) 2 years ago
ChunkyProgrammer e3c1cb3ec9 fix view count extraction 2 years ago
ChunkyProgrammer dc929be198 Merge branch 'master' into fix-shorts 2 years ago
ChunkyProgrammer 1da00bade3 implement code suggestions
Co-Authored-By: Samantaz Fox <coding@samantaz.fr>
2 years ago
Samantaz Fox 8db2a93827
Search: Fix channel search (#3699) 2 years ago
Samantaz Fox c0eab2b1f6
CSS: fix long description overflow (#3711) 2 years ago
Samantaz Fox 9e82e6fc1b
Handle auto theme correctly with the manual toggle (#3521) 2 years ago
Samantaz Fox ef6eea3a65
Videos: ignore empty music entries (#3709) 2 years ago
Samantaz Fox d526094380
download_widget: Add missing translation key (#3713) 2 years ago
Samantaz Fox 562d75a47b
Quick fix for channel videos page bug (#3718) 2 years ago
Emilien Devos e0600f4553 quick fix for channel videos page 2 years ago
Jarek Baran 0fe1b1ec19 download_widget: Add missing translation key 2 years ago
techmetx11 73d2ed6f77
Optimize some redundant stuff 2 years ago
Lennart Bernhardt f83f0d2561
remove fixed height from description 2 years ago
Lennart Bernhardt 1d187bcf17
fix long description overflow 2 years ago
ChunkyProgrammer a3da03bee9 improve accessibility 2 years ago
ChunkyProgrammer f840addd93 Fix error when song title is missing from the track 2 years ago
techmetx11 7755ed4ac8
Fix regexs 2 years ago
techmetx11 49ddf8b6bd
Added attributed description support 2 years ago
ChunkyProgrammer 5767344746 Fix parsing shorts on channel page 2 years ago
ChunkyProgrammer 3492485789 Fix channel search 2 years ago
Samantaz Fox 8a44bd11d2
Videos: Fix 0 views and empty license field (#3557) 2 years ago
Samantaz Fox c0410602e7
Translations update from Hosted Weblate (#3693) 2 years ago
Samantaz Fox 4ae158ef6d
Videos: Add back support for views on livestreams 2 years ago
Samantaz Fox 1f3317e257
Update video spec 2 years ago
victor dargallo 08cbd44b57
Update Catalan translation 2 years ago
Oğuz Ersen 224fbcd2b1
Update Turkish translation 2 years ago
victor dargallo c188dec4fa
Update Catalan translation 2 years ago
SC 3aa6a0c4f0
Update Portuguese translation 2 years ago
Fjuro ce1f61d185
Update Czech translation 2 years ago
Milo Ivir c1e45cb84a
Update Croatian translation 2 years ago
HamidReza Shareghzade defec2e8fb
Update Persian translation 2 years ago
maboroshin ded28b80d3
Update Japanese translation 2 years ago
Jeff Huang dd6c9dbc65
Update Chinese (Traditional) translation 2 years ago
Eric 46a7be89a7
Update Chinese (Simplified) translation 2 years ago
Ihor Hordiichuk 72656e802e
Update Ukrainian translation 2 years ago
gallegonovato 60e3f8aec0
Update Spanish translation 2 years ago
Rex_sa aad166c96a
Update Arabic translation 2 years ago
Matthaiks a0bdcc2964
Update Polish translation 2 years ago
Mateusz Bączek e1a25a184a
Add the docs/ folder to gitignore (#3694) 2 years ago
Samantaz Fox 26ea676b8d
Community: Restore thumbnail qualities array (#3691) 2 years ago
Samantaz Fox b66a5c40a9
Community: Restore thumbnail qualities array 2 years ago
Émilien Devos (perso) a6d21cb211
Merge pull request #3627 from iBicha/feature/latest-version-optional-itag
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Stéphane 712aea0831
chore: update HoloPlay app on README (#3690) 2 years ago
Samantaz Fox 6837e42928
Video page: add song title + remove song license on music videos (#3680) 2 years ago
ChunkyProgrammer ffcc837c2a remove music license 2 years ago
Samantaz Fox b4806e7ba9
Comments: Add support for custom emojis + member info (#3636) 2 years ago
Samantaz Fox 5c633ad1da
Community: parse reply count and polls (#3635) 2 years ago
Brahim Hadriche 3848c3f53f
Update src/invidious/routes/video_playback.cr
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
thtmnisamnstr 3341929060 removed unnecessary conditionals and uninitialized variable declarations
Signed-off-by: thtmnisamnstr <gavinj1984@gmail.com>
2 years ago
ChunkyProgrammer a781cf3734 readd try as bool for isSponsor key 2 years ago
Samantaz Fox d79d6f38b2
Fix description boxes smaller than 3 lines (#3668) 2 years ago
Samantaz Fox f012d70e47
Translations update from Hosted Weblate (#3673) 2 years ago
Samantaz Fox 01e00a588b
Update Spanish localisation further (#3676) 2 years ago
Samantaz Fox 35ac26bd61
Add History API (#3654) 2 years ago
ChunkyProgrammer e3081ef1a9
Apply style change suggestions
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Brahim Hadriche 0b17f68eba Fix input validation 2 years ago
ChunkyProgrammer 742c951bc9 support videos with multiple songs 2 years ago
ChunkyProgrammer d8e23d34b6 add song title for music tracks 2 years ago
maboroshin 548a0f26ef
Update Japanese translation 2 years ago
VisualPlugin 9325fa79ae
Update es.json 2 years ago
Felipe Nogueira 1f607273a8
Update Portuguese (Brazil) translation 2 years ago
fresh 3c3d9ebf84
Update Greek translation 2 years ago
Émilien Devos (perso) 6b01629c5d
Merge pull request #3671 from lamarios/master
Add new android client to list of projects using invidious
2 years ago
Brahim Hadriche 025e755542 Use single db call 2 years ago
Paul Fauchon a3ecd46b01
add new Android client to list of projects using invidious 2 years ago
Paul Fauchon f6c6c9e5ec
Merge pull request #1 from iv-org/master
rebase
2 years ago
Samantaz Fox bff5c8d9a1
Translations update from Hosted Weblate (#3657) 2 years ago
amogusussy 03542f2f5d
Fix empty description boxes.
If a video has no description, (without this commit) the description box will still take up 8.3em, even if there's no content in it.

This fixes that issue.
2 years ago
Brahim Hadriche a5cc66e060 Fix id check 2 years ago
Brahim Hadriche 38f6d08be6 Validate id, avoid db call if not needed 2 years ago
Brahim Hadriche 8c0efb3ea9 validate video id 2 years ago
ChunkyProgrammer 60b7c8015c add channel emoji css class 2 years ago
Samantaz Fox 406d74d0b6
Update Spanish translation 2 years ago
ChunkyProgrammer 4a14713462 use dig, create private image quality constant
Co-Authored-By: Samantaz Fox <coding@samantaz.fr>
2 years ago
Brahim Hadriche 27bf4d02a1 PR nursing 2 years ago
Besnik Bleta 2974ed348c
Update Albanian translation 2 years ago
Milo Ivir fdf162e318
Update Croatian translation 2 years ago
maboroshin 24ac873532
Update Japanese translation 2 years ago
Oğuz Ersen 0efb56238f
Update Turkish translation 2 years ago
gallegonovato eb3af9d4f1
Update Spanish translation 2 years ago
Ashirg-ch 23f1f8bde3
Update German translation 2 years ago
Ashirg-ch 3ddcfea8fa
Update English (United States) translation 2 years ago
techmetx11 4ac263f1df
Replace == with empty? 2 years ago
techmetx11 8eca5b270e
Video: Fix 0 views, and empty license field 2 years ago
thtmnisamnstr b3eea6ab3e improved import algorithm, fixed a referer issue from the playlists page after deleting a playlist
Signed-off-by: thtmnisamnstr <gavinj1984@gmail.com>
2 years ago
Saurmandal 7e0210d090
Update Hindi translation 2 years ago
ssantos 596a16c085
Update Portuguese (Portugal) translation 2 years ago
André Marcelo Alvarenga 57e4312d9f
Update Portuguese (Brazil) translation 2 years ago
Émilien Devos (perso) 0995e0447c
Merge pull request #3656 from iBicha/feature/auth-token-username
encode username on callback
2 years ago
Brahim Hadriche 6ee51f460a encode username on callback 2 years ago
Brahim Hadriche 15e9510ab2 Check preferences before marking video as watched 2 years ago
Brahim Hadriche 7b124eec64 Add History API 2 years ago
Brahim Hadriche 20289a4d01 Fix order for import 2 years ago
Brahim Hadriche 8445d3ae12 Fix watch history order 2 years ago
Samantaz Fox b287ff2126
Translations update from Hosted Weblate (#3639) 2 years ago
Andrey 64780ce1da
Update Russian translation 2 years ago
Raman 8046316f20
Update Hindi translation 2 years ago
Samantaz Fox 4bbeb4a4c8
Add watch indicators (#3383) 2 years ago
ChunkyProgrammer b5eb6016bb add spaces at end of attribute 2 years ago
Wes van der Vleuten bde21d527f Fixed console error 2 years ago
Émilien Devos (perso) b287ddc52a
Allow to set a label for exempting from staling (#3651) 2 years ago
ChunkyProgrammer bc5d81fe60 use string builder to create images
change member to sponsor
2 years ago
Samantaz Fox cbbec00e1c
Auth: Add username to token callback (#3603) 2 years ago
Samantaz Fox ba217c9174
API: Add endpoints for import/export (#3617) 2 years ago
Samantaz Fox 217b740e01
API: Add endpoint to resolve youtube urls (#3612) 2 years ago
Émilien Devos (perso) d6bf9e9bcf
Merge pull request #3622 from ChunkyProgrammer/downcase-trending-type-param 2 years ago
Brahim Hadriche a95f82e44b
Add Playlet to "Projects using Invidious" (#3640) 2 years ago
ChunkyProgrammer 76ad4e8026 show member icon, hide deleted emojis, fix non-custom emojis 2 years ago
ChunkyProgrammer d03a62641f Add support for custom emojis in comments 2 years ago
ChunkyProgrammer 4731480821 parse votes as number
Co-Authored-By: syeopite <70992037+syeopite@users.noreply.github.com>
2 years ago
ChunkyProgrammer aecbafbc7b Community: parse replyCount 2 years ago
ChunkyProgrammer 8384fa94c2 Community: Parse polls 2 years ago
Samantaz Fox 7993784701
Translations update from Hosted Weblate (#3596) 2 years ago
AHOHNMYC 9c400fd455
Update Russian translation 2 years ago
SC e4d14481c5
Update Portuguese translation 2 years ago
Marsel J. Jonker cb7c4a8220
Add Afrikaans translation 2 years ago
Damjan Gerl c5d1344511
Update Slovenian translation 2 years ago
Besnik Bleta 299eb9207b
Update Albanian translation 2 years ago
Fjuro f2390ed052
Update Czech translation 2 years ago
Milo Ivir 256b518469
Update Croatian translation 2 years ago
maboroshin 58688a6311
Update Japanese translation 2 years ago
Jeff Huang fc5092c399
Update Chinese (Traditional) translation 2 years ago
Oğuz Ersen 591f816781
Update Turkish translation 2 years ago
Eric db6d3d2191
Update Chinese (Simplified) translation 2 years ago
Ihor Hordiichuk 054686e557
Update Ukrainian translation 2 years ago
Jorge Maldonado Ventura c1c6f67ad3
Update Esperanto translation 2 years ago
Jorge Maldonado Ventura c82272155e
Update Spanish translation 2 years ago
atilluF 4ca23f2d51
Update Italian translation 2 years ago
Rex_sa 45c99190b2
Update Arabic translation 2 years ago
Matthaiks 7ae9dabe3c
Update Polish translation 2 years ago
Damjan Gerl 5534cd87f8
Update Slovenian translation 2 years ago
Goudarz Jafari eb7588f1a0
Update Persian translation 2 years ago
Mateus 20dc0a9e26
Update Portuguese (Brazil) translation 2 years ago
maboroshin f4de962dc2
Update Japanese translation 2 years ago
eightyy8 b2f93dc89c
Update Russian translation 2 years ago
SC bd00b4c730
Update Portuguese translation 2 years ago
AHOHNMYC 4830656484
Update Russian translation 2 years ago
Samantaz Fox f9c2412010
YT Comments: Revert isPinned to a nil check (#3631) 2 years ago
Samantaz Fox 87342e4efd
Comments: Revert "isPinned" to a nil check
"pinnedCommentBadge" is not a boolean, but a complex structure.
This commit fixes a wrong assumption I had during the rewiew of
https://github.com/iv-org/invidious/pull/3626
2 years ago
ChunkyProgrammer 838cbeffcc Use case statement for trending_type
Co-Authored-By: Samantaz Fox <coding@samantaz.fr>
2 years ago
Samantaz Fox 27ff8d7c33
API: add authorVerified where possible + provide channel tabs list (#3625) 2 years ago
Samantaz Fox feeb872791
YT Comments: parse isPinned and strikethroughs (#3626) 2 years ago
thtmnisamnstr 6f01d6eacf ran crystal tool format. it should fix some CI issues
Signed-off-by: thtmnisamnstr <gavinj1984@gmail.com>
2 years ago
ChunkyProgrammer e0c70d34cc
Make sure pinnedCommentBadge isn't equal to false
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Brahim Hadriche d57d278f32 Make itag optional under /latest_version 2 years ago
ChunkyProgrammer b893bdac0d parse isPinned, add support for strikethrough 2 years ago
ChunkyProgrammer 97825be10c add missing authorVerified to api 2 years ago
ChunkyProgrammer 28424d0e88 Ignore casing for trending type in api 2 years ago
Brahim Hadriche c37d8e3664 Use CONFIG.playlist_length_limit when exporting playlists 2 years ago
Brahim Hadriche 47a5b98e25 Remove unused db call 2 years ago
Brahim Hadriche 2606decd21 Refactor export function 2 years ago
Brahim Hadriche b2589c74be Add API for import/export 2 years ago
ChunkyProgrammer c162c7ff3f add pageType 2 years ago
Samantaz Fox d6dd341594
API: Parse multiimage community posts (#3599) 2 years ago
Samantaz Fox c7f34042a2
Video: Add support for the music description section (#3559) 2 years ago
Samantaz Fox 6c687a3cac
Login redirect to referer on logged-in user (#3556) 2 years ago
ChunkyProgrammer bf5175d1e9 Feat: Add api endpoint to resolve youtube urls 2 years ago
Brahim Hadriche e7a9aeff95 Add username to auth token callback 2 years ago
ChunkyProgrammer 785fe52674 API: Parse multiimage community posts 2 years ago
Gavin Johnson 72d0c9e409 removed comments
Signed-off-by: Gavin Johnson <gavinj1984@gmail.com>
2 years ago
Gavin Johnson 5c7bda66ae removed comments
Signed-off-by: Gavin Johnson <gavinj1984@gmail.com>
2 years ago
Gavin Johnson 96344f28b4 added youtube playlist import functionality. fixes issue #2114
Signed-off-by: Gavin Johnson <gavinj1984@gmail.com>
2 years ago
Samantaz Fox 3b8e6c6040
Add support for channel handles (#3591) 2 years ago
Macic 13bf4e9e00
Support handles 2 years ago
Samantaz Fox 2a803dc067
Fix displaying author name for community posts (#3590) 2 years ago
ChunkyProgrammer c2957dbce4 fix displaying author name #1612 2 years ago
Samantaz Fox 9a9f8231e8
Translations update from Hosted Weblate (#3575) 2 years ago
Hosted Weblate ad3c721af7
Update Czech translation
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
Signed-off-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Hosted Weblate 9b9fde1054
Update Chinese (Traditional) translation
Co-authored-by: Jeff Huang <s8321414@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
Signed-off-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Hosted Weblate e66e463156
Update Croatian translation
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
Signed-off-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Hosted Weblate 5c024c677b
Update Ukrainian translation
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
Signed-off-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Hosted Weblate 68caf355af
Update Esperanto translation
Co-authored-by: Jorge Maldonado Ventura <jorgesumle@freakspot.net>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
Signed-off-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Hosted Weblate 32bc44e83b
Update Spanish translation
Co-authored-by: Jorge Maldonado Ventura <jorgesumle@freakspot.net>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
Signed-off-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Hosted Weblate 8cc0f9faf0
Update Italian translation
Update Italian translation

Co-authored-by: atilluF <atilluF@users.noreply.hosted.weblate.org>
Co-authored-by: Translator-3000 <weblate.m1d0h@8shield.net>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
Signed-off-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Hosted Weblate 75d136ce77
Update French translation
Co-authored-by: slundi <slundi@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
Signed-off-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Hosted Weblate dd1ffb9283
Update Arabic translation
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
Signed-off-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Hosted Weblate b3a605c574
Update Polish translation
Co-authored-by: Matthaiks <kitynska@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
Signed-off-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Hosted Weblate f5b3cee263
Update Korean translation
Update Korean translation

Co-authored-by: xrfmkrh <rF3nMd7sRKezjF2vcEQo@protonmail.com>
Co-authored-by: 이정희 <daemul72@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
Signed-off-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Hosted Weblate 24f1d82919
Update Turkish translation
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
Signed-off-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Samantaz Fox dbee027ed9
Search: Fix ArithmeticOverflow + wrong subs count issues (#3535) 2 years ago
Samantaz Fox 624425cfa8
Add support for timedtext captions (#3533) 2 years ago
techmetx11 caf9520c86
Major improvements 2 years ago
Wes van der Vleuten 420e12bb8b Merge remote-tracking branch 'upstream/master' 2 years ago
Wes van der Vleuten 7fd205179b Added suggestions 2 years ago
Wes van der Vleuten 4aa696fa6e
Update assets/js/watched_widget.js with suggestion of AHOHNMYC
Co-authored-by: AHOHNMYC <24810600+AHOHNMYC@users.noreply.github.com>
2 years ago
Samantaz Fox 4e3884cae7
Use the right source path when copying .min.css files (#3588) 2 years ago
Samantaz Fox ebc02d0be3
Formatting fix for Crystal nightly (#3586) 2 years ago
Samantaz Fox f47d4f88cc
Deps: Use the right source path when copying .min.css files
Thanks to therealresonix for the catch!
2 years ago
Samantaz Fox cf93c94fc4
Formatting fix for Crystal nightly
Changes added by https://github.com/crystal-lang/crystal/pull/12951
2 years ago
Émilien Devos 030070f1eb
Merge pull request #3585 from hippogriffin/postgresql-12-chart-lock
Update chart.lock
2 years ago
hippogriffin 3509999892 update chart.lock 2 years ago
Brahim Hadriche f6a4d04070 Redirect auth token to login 2 years ago
Brahim Hadriche 0e22a0c21a Merge branch 'master' into feature/login-redirect 2 years ago
Samantaz Fox 0e68756758
Make DASH absolute urls when local (#3554) 2 years ago
Samantaz Fox a7b2df31f0
API: make /api/v1/videos respect the 'local' parameter (#3567) 2 years ago
Samantaz Fox ce07f2cd4a
Video proxy: always include the 'range' header (#3570) 2 years ago
DUOLabs333 ff66cec920 Remove debug print 2 years ago
DUO Labs 67ace4fd9d
Some indention changes
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
DUOLabs333 86333cd434 Formatting 2 years ago
Gavin Johnson 855202e40e added youtube playlist import; initial commit
Signed-off-by: Gavin Johnson <gavinj1984@gmail.com>
2 years ago
DUOLabs333 8dcc98b3b9 If videCountText lists the number of subscribers, then don't use it in get_video_count 2 years ago
Brahim Hadriche 910809f1eb Handle case with included manifest 2 years ago
techmetx11 fe5b81f2c3
Add support for multiple songs 2 years ago
Samantaz Fox ea0d1b6f7b
Make shell scripts executable (chmod 755) (#3571) 2 years ago
Émilien Devos c8fecffbbe
Merge pull request #3458 from Craeckie/master
Docker: Added tini for proper signal forwarding
2 years ago
Samantaz Fox 215446e638
Docker: Also add tini to ARM64 dockerfile 2 years ago
Samantaz Fox b779445836
Translations update from Hosted Weblate (#3564) 2 years ago
Hosted Weblate c02ae66bb1
Update Korean translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: xrfmkrh <rF3nMd7sRKezjF2vcEQo@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/invidious/translations/
Translation: Invidious/Invidious Translations
2 years ago
Samantaz Fox d1bf36bd2b
Update translation keys for new channels tabs (#3568) 2 years ago
Samantaz Fox aacf83c06e
locales: Update translation keys for Videos/Community tabs 2 years ago
Samantaz Fox 1af846e58c
API: make /api/v1/videos respect the 'local' parameter 2 years ago
Émilien Devos c012aac997
Don't continue when LOGIN_REQUIRED and no videoDetails element (#3563) 2 years ago
Émilien Devos d6087fac47 Don't continue when LOGIN_REQUIRED and no videoDetails 2 years ago
Samantaz Fox 4ee483282e
Video proxy: always include the 'range' header 2 years ago
Samantaz Fox 04b97ec261
make shell scripts executable (chmod 755) 2 years ago
techmetx11 1b5fbfc13e
Video: Add support for the music section 2 years ago
Brahim Hadriche 01acb9bfbf Login redirect to referer on logged-in user 2 years ago
Brahim Hadriche 1fb0a49592 Make DASH absolute urls when local 2 years ago
DUOLabs333 4b2d942024 Convert tabs to spaces 2 years ago
Samantaz Fox 05258d56bd
Add support for the new channel layout - part 2 (#3419) 2 years ago
marc 692166bd64
Update chart dependency for postgresql
* Update Chart.yaml

Set postgresql Chart version to 12.1.6. Bitnami deleted 11.1.3.

* Force postgresql image tag version

Co-authored-by: Émilien Devos <contact@emiliendevos.be>
2 years ago
DUOLabs333 456e91426a Formatting 2 years ago
DUOLabs333 4fc1b8ae86 Remove superfluous 'end' 2 years ago
DUOLabs333 32471382c4 Different cosmetic fixes 2 years ago
Samantaz Fox 927c37ce3e
CSS: display pointer cursor when hovering the "show more/less" toggle (#3518) 2 years ago
Samantaz Fox a37522a03d
Implement workaround for broken shorts objects 2 years ago
Brackets ed8f02ef01
Update default.css
pointer on hover on label for descexpansionbutton
2 years ago
DUO Labs 8d08cfe30f
Add comments to src/invidious/yt_backend/extractors.cr
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Samantaz Fox 049bfab438
Translations update from Hosted Weblate (#3370) 2 years ago
Hosted Weblate 7f0f40f811
Update Korean translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: xrfmkrh <rF3nMd7sRKezjF2vcEQo@protonmail.com>
2 years ago
Hosted Weblate 62b8f8ac80
Update Persian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Parsa Abbasi <parsa.abbasi1996@gmail.com>
2 years ago
Hosted Weblate 16140f8b3f
Update Portuguese (Portugal) translation
Update Portuguese (Portugal) translation

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Samantaz Fox <translator-weblate@samantaz.fr>
Co-authored-by: ssantos <ssantos@web.de>
2 years ago
Hosted Weblate e0275d0908
Update Japanese translation
Co-authored-by: 田島翔太 <taziman.taziyuki@gmail.com>
2 years ago
Hosted Weblate a57770eb1f
Update Turkish translation
Co-authored-by: Fatih K <freepremiumidiot46@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate 233de2eff9
Update Esperanto translation
Update Esperanto translation

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jorge Maldonado Ventura <jorgesumle@freakspot.net>
2 years ago
Hosted Weblate 9c9d71d41a
Update German translation
Co-authored-by: DarkMoonExpeditionRobot <DarkMoonExpeditionRobot@proton.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate 6b2fff83b5
Update Albanian translation
Co-authored-by: Besnik Bleta <besnik@programeshqip.org>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate 23b229ebb7
Update Finnish translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: tomechio <tomasz@salamon.fi>
2 years ago
Hosted Weblate 72aa5c94af
Update Portuguese (Brazil) translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Vinicius <rodriguessv30@gmail.com>
2 years ago
Hosted Weblate 4d6ff3a3c6
Update Hungarian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kroca Karoly <mechatroniccroco@gmail.com>
2 years ago
Hosted Weblate e2864a5ba1
Update Italian translation
Co-authored-by: atilluF <atilluF@users.noreply.hosted.weblate.org>
2 years ago
Hosted Weblate a36363198c
Update Arabic translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Mohamed-Touhami MAHDI <dragnucs@legtux.org>
2 years ago
Hosted Weblate e2ce9c2cee
Add Odia translation
Co-authored-by: GET100PERCENT <eraofphysics@yahoo.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Émilien Devos 98301a2237
Add ability to disable all user notifications (#3473) 2 years ago
DUOLabs333 0d3610f63d Change regex used in short_text_to_number 2 years ago
DUOLabs333 85dd3533bb Fix for the ArithmeticOverflow Problem 2 years ago
DUOLabs333 76758baab8 Removed unneccesary String::Builder and removed cues that was just a blank line 2 years ago
DUOLabs333 9d83e2da4e Add newline 2 years ago
DUOLabs333 45b8f6d0cd More linting 2 years ago
DUOLabs333 b49ed65a07 Linting 2 years ago
DUOLabs333 8df1c3bb57 Add support for timedtext captions 2 years ago
confused_alex 865704dc7b
Fixed dead link (#3526) 2 years ago
shironeko 1aaf290814 handle auto theme correctly with the manual toggle
If the user used the manual toggle, they will not be able to get back to
auto since it will force set to light theme. This should fix that.
2 years ago
brackets0 4659e27b56 fix: on hover btn #descexpansionbutton cursor to pointer 2 years ago
Samantaz Fox f9eb839c7a
channel: remove dead playlists code 2 years ago
Samantaz Fox 69b8e0919f
api: Add support for the "featured channels" endpoint 2 years ago
Samantaz Fox 4e3a930626
frontend: Add support for the "featured channels" page 2 years ago
Samantaz Fox b6a4de66a5
frontend: Unify the various channel pages 2 years ago
Samantaz Fox 40c666cab2
api: Add support for shorts and livestreams 2 years ago
Samantaz Fox 6c9754e663
frontend: Add support for shorts and livestreams 2 years ago
Samantaz Fox 5d6abd5301
extractors: Fix ReelItemRendererParser 2 years ago
Samantaz Fox 52ef89f02d
channel: Add support for shorts and livestreams (backend only) 2 years ago
Samantaz Fox 2903e896ec
channel: use YT API + extractors to fetch videos 2 years ago
Samantaz Fox c5ee2bfc0f
channel: use YT API to fetch playlist items 2 years ago
Samantaz Fox 8e8ca4fcc5
Prepare to create a 'Channel' module 2 years ago
Samantaz Fox ce7db8d2cb
extractors: Add continuation token parser 2 years ago
Samantaz Fox bdc51cd20f
extractors: separate 'extract' and 'parse' logic 2 years ago
Samantaz Fox 9588fcb5d1
frontend: remove paging on channel videos 2 years ago
Samantaz Fox fbcce57ce2
channel: use extractor utils to parse tabs (+ code cleaning) 2 years ago
Samantaz Fox 99bf519781
shards: Bump protodec to v0.1.5 2 years ago
dev 1f6c234259 added tini for proper signal forwarding 2 years ago
Samantaz Fox 5160d8bae3
Fix values for dark_mode in config.example.yml (#3426) 2 years ago
PrivacyDevel 4fc5d43374
Update config.example.yml 2 years ago
PrivacyDevel 9656067296
Add "light" and "auto" as accepted values for dark_mode 2 years ago
PrivacyDevel 9eb2ad367e
Correct config.example.yml dark_mode comment 2 years ago
Samantaz Fox 09b9b758de
Add param 8AEB for getting youtube stories (#3258) 2 years ago
Samantaz Fox a46404bf78
CI: Use Crystal 1.6.2 in test matrix (#3400) 2 years ago
Samantaz Fox c142703453
Fix parsing error in search results (#3418) 2 years ago
Samantaz Fox f44506b7e0
yt api: bump web client version 2 years ago
Samantaz Fox afc0ec3c30
search: Fix short text parsing 2 years ago
Samantaz Fox 1bb8f2815d
CI: Use Crystal 1.6.2 in test matrix 2 years ago
Samantaz Fox 516efd2df3
Cleanup videos (#3238) 2 years ago
Samantaz Fox 47cc26cb3c
videos: fix 'Arithmetic overflow' error 2 years ago
Samantaz Fox cc5c83333f
videos: improve fetching of streaming data 2 years ago
Wes van der Vleuten d3d9cfdd0d Cleanup 2 years ago
Wes van der Vleuten c03f92baf7 Fixed watch indicator when position is not saved 2 years ago
Wes van der Vleuten 5bcb5f3175 Removed console.log 2 years ago
Wes van der Vleuten c95ee10d69 Added parital watch indicator on more locations 2 years ago
Wes van der Vleuten f604c1c68b Fixed thumbnails with darkreader, Added watched indicator in more locations 2 years ago
Wes van der Vleuten 7b57381773 Added watch indicator 2 years ago
Wes van der Vleuten 437f42250e
Watched marker 2 years ago
Samantaz Fox 09942dee66
Add support for the new channel layout - part 1 (#3374) 2 years ago
Samantaz Fox 9da1827e95
Dirty fix to get back the channel videos 2 years ago
Samantaz Fox 758b7df400
dockerfile: Pass '-Ddisable_quic' to 'crystal build' (#3376)
+ use alpine 3.16 as a base like the crystal team
2 years ago
Samantaz Fox 46a63e6150
extractors: Add support for reelItemRenderer 2 years ago
Samantaz Fox f267394bbe
extractors: Add support for richGridRenderer 2 years ago
Samantaz Fox 2acff70811
videos: handle different JSON structs being present in cache 2 years ago
Samantaz Fox db91d3af66
videos: Fix some bugs 2 years ago
Samantaz Fox 83795c245a
videos: Support the new like button's structure 2 years ago
Samantaz Fox d659a451d6
videos: remove unused 'VideoRedirect' exception 2 years ago
Samantaz Fox 87a5d70062
videos: move API's JSON structure to a dedicated module 2 years ago
Samantaz Fox ae03ed7bf7
videos: move player/next parsing code to a dedicated file 2 years ago
Samantaz Fox e23ceb6ae9
videos: Fix extraction code according to tests 2 years ago
Samantaz Fox 33150f5de3
spec: Add test cases for regular videos extraction 2 years ago
Samantaz Fox 7df0cfcbed
Videos: fix 'views' parsing for livestreams 2 years ago
Samantaz Fox 907ddfa06a
spec: Add tests for recent changes 2 years ago
Samantaz Fox 6aaea7fafa
Videos: parse data during first fetching
There will be less data to be stores in the DB cache
2 years ago
Samantaz Fox cd03fa06ae
videos: move 'Caption' and associated global/functions to a separate file 2 years ago
Samantaz Fox 9baaef412f
videos: move 'VideoPreferences' and its associated function to a separate file
This will require some rework later.
2 years ago
Samantaz Fox 88141c459c
videos: move formats structure to a separate file/module 2 years ago
Samantaz Fox 6250039405
videos: move regions list to a dedicated file 2 years ago
Samantaz Fox 84cd4d6a5b
Makefile: disable QUIC by default (#3367) 2 years ago
Samantaz Fox a1c6159e6f
Translations update from Hosted Weblate (#3365) 2 years ago
Samantaz Fox b7555343a0
Add missing languages to the locales list (#3369) 2 years ago
Samantaz Fox 4055c3bec8
i18n: Add Bengali, Catalan, Basque, Sinhala and Slovak
Add languages even if translation is <= 25%
2 years ago
Samantaz Fox c5303d55e5
Update CI (#3368) 2 years ago
Samantaz Fox 8096c2d81d
CI: bump install-crystal action to v1.7.0 2 years ago
Samantaz Fox 4e1f5c8357
CI: bump Crystal versions 2 years ago
Hosted Weblate bba693e2af
Update Korean translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: xrfmkrh <rF3nMd7sRKezjF2vcEQo@protonmail.com>
2 years ago
Hosted Weblate 127bfd5023
Update Esperanto translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jorge Maldonado Ventura <jorgesumle@freakspot.net>
2 years ago
Hosted Weblate 2edfe4a463
Update Lithuanian translation
Update Lithuanian translation

Co-authored-by: Gediminas Murauskas <muziejusinfo@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate 4b1ef90d96
Update Norwegian Bokmål translation
Co-authored-by: Petter Reinholdtsen <pere-weblate@hungry.com>
2 years ago
Émilien Devos 0c7919f3d9
Dont use quay for the postgresql bitnami image 2 years ago
Samantaz Fox 72cf49eda1
Remove twitter:site meta tag from watch view (#3361) 2 years ago
Samantaz Fox e2ab488e7f
Add meta tags to channels (#3347) 2 years ago
thecashewtrader 1e96206b0b Remove twitter:site meta tag from watch view 2 years ago
thecashewtrader 6f301db11c Remove twitter:site meta tag from channel view 2 years ago
Samantaz Fox 12db1be87b
Translations update from Hosted Weblate (#3333) 2 years ago
Samantaz Fox cdb370f56b
Add channel name to embeds (#3338) 2 years ago
Hosted Weblate ae4f67f39c
Update Korean translation
Update Korean translation

Update Korean translation

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: xrfmkrh <rF3nMd7sRKezjF2vcEQo@protonmail.com>
Co-authored-by: 이정희 <daemul72@gmail.com>
2 years ago
Hosted Weblate fcd29a4143
Update Lithuanian translation
Co-authored-by: Gediminas Murauskas <muziejusinfo@gmail.com>
2 years ago
Hosted Weblate fa544c158a
Update Vietnamese translation
Co-authored-by: HexagonCDN <ctyducliem2006@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate 7f3509aa36
Update Spanish translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
2 years ago
thecashewtrader a1e0a6b499 Add meta tags to channels 2 years ago
thecashewtrader 6ea3673cf0 Move uploader channel name to `og:site_name` 2 years ago
Samantaz Fox 3b39b8c772
Add table cleaning job (#3294) 2 years ago
thecashewtrader ffb42a9b23 Add channel name to embeds 2 years ago
Samantaz Fox 6707368f19
Translations update from Hosted Weblate (#3324) 2 years ago
Hosted Weblate 1e186257da
Update Korean translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: xrfmkrh <rF3nMd7sRKezjF2vcEQo@protonmail.com>
2 years ago
Hosted Weblate d85fcc4e7c
Update French translation
Update French translation

Co-authored-by: Grandasse <grandassestl@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Samantaz Fox <translator-weblate@samantaz.fr>
2 years ago
Hosted Weblate 3e13d83ced
Update Polish translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Matthaiks <kitynska@gmail.com>
2 years ago
Hosted Weblate 14de6a5658
Update Portuguese translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: SC <lalocas@protonmail.com>
2 years ago
Hosted Weblate 6100d5f12d
Update Russian translation
Co-authored-by: AHOHNMYC <lqwh2h2cwa@protonmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Samantaz Fox dcfa0687f4
Allow to set max playlist length via a config variable (#3283) 2 years ago
Samantaz Fox a01433960d
Never mark feature requests/enhancements as stale (#3331) 2 years ago
Benjamin Loison 18a7ebe3a5
Correct `peertubeify` URL in `README.md` (#3325) 2 years ago
Samantaz Fox 7069969198
Never mark feature requests/enhancements as stale 2 years ago
Jakub Filo 7c45026383 Fix playlist limit 2 years ago
Samantaz Fox cf12e9dec1
Fix the video title overlay's colors in embed (#3308) 2 years ago
Samantaz Fox 221d472127
Replicate headers and params made by yt apps (#3290) 2 years ago
Samantaz Fox 376ed3f4d3
css: Fix the video title overlay's colors in embed 2 years ago
Samantaz Fox 7df176d750
Keep listen mode after related video click (#3160) 2 years ago
Samantaz Fox 0fa3250f02
Translations update from Hosted Weblate (#3301) 2 years ago
Hosted Weblate 3b439a8fb7
Update Korean translation
Co-authored-by: PiQuark6046 <piquark6046@protonmail.com>
2 years ago
Hosted Weblate 3a56ed19fe
Update Czech translation
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate 53662b8400
Update Indonesian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Neko Nekowazarashi <kodra@nekoweb.my.id>
2 years ago
Hosted Weblate eac37f1bd4
Update Turkish translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
2 years ago
Hosted Weblate 1ac5081090
Update Chinese (Simplified) translation
Co-authored-by: Eric <hamburger1024@firemail.cc>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate e3de6a4138
Update Slovenian translation
Co-authored-by: Damjan Gerl <damjan@damjan.net>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate fc96ecaa66
Update Croatian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Milo Ivir <mail@milotype.de>
2 years ago
Hosted Weblate 5ca34f3eb5
Update Chinese (Traditional) translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jeff Huang <s8321414@gmail.com>
2 years ago
Hosted Weblate dcabce50c0
Update Ukrainian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
2 years ago
Hosted Weblate 5b0a4a8db4
Update Spanish translation
Co-authored-by: Ángel Fernández Sánchez <angelfx19@gmail.com>
2 years ago
Hosted Weblate b5a2c67d16
Update Italian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: atilluF <atilluf@outlook.com>
2 years ago
Hosted Weblate f911871990
Update Arabic translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
2 years ago
Chris Helder c3de622493
Fix page shift on search bar focus (#3304) 2 years ago
Samantaz Fox 53fb6ad039
Fix replies count (#3299) 2 years ago
Samantaz Fox 8ab339396a
Message when the video doesn't exist in playlist (#3243) 2 years ago
Samantaz Fox 5048a89b9b
Add/Change Javascript license notice (#3262) 2 years ago
Samantaz Fox feb38f891b
Translations update from Hosted Weblate (#3271) 2 years ago
Emilien Devos c658fd27cc better spoof requests 2 years ago
Emilien Devos 260bab598e reword error messages 2 years ago
Emilien Devos 6f3b4fbaaf fix replies count 2 years ago
Samantaz Fox 1e7d330350
Handle long usernames gracefully (#3286) 2 years ago
Émilien Devos 31244cbcc8 replicate headers and params made by yt apps 2 years ago
Andrei E 508a5761a1 Handle long usernames gracefully 2 years ago
Jakub Filo 4818b89ab1 Allow to set maximum custom playlist length via a config variable. 2 years ago
Hosted Weblate 4e44123abc
Update French translation
Co-authored-by: Chance Ducharme <avrilgazeau@hotmail.ca>
2 years ago
Hosted Weblate 689365d713
Update Spanish translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: nyoooooooooooooooom <p77q1nqi@anonaddy.me>
2 years ago
Émilien Devos a7d9df5516
add check video id for android client too (#3280) 2 years ago
Samantaz Fox 16b23efb4f
Throw error if the returned videoID is different (#3278) 2 years ago
Émilien Devos 389e49183c throw error if the videoID returned is different 2 years ago
Emilien Devos ca4c2115ee Message when the video doesn't exist in playlist 2 years ago
Samantaz Fox 4c1a5f84fa
Fix prefs cookies in player (#3276) 2 years ago
Samantaz Fox b0bb156918
Adapt StaticFileHandler for Crystal 1.6 (#3269) 2 years ago
Samantaz Fox 5565204273
StaticFileHandler: use HTTP::Status rather than integers 2 years ago
Samantaz Fox d950a0ef5d
StaticFileHandler: Adapt for Crystal 1.6
See:
 - https://github.com/crystal-lang/crystal/pull/12310
 - https://github.com/kemalcr/kemal/pull/644
2 years ago
Samantaz Fox b2c0f7efc3
Fix missing hash key: "toggleButtonRenderer" (issue #3260) 2 years ago
Samantaz Fox 9e58bc19c4
Fix #3265 2 years ago
Samantaz Fox 0d7e2afba4
Merge pull request #3261 from SamantazFox/routing-cleanup 2 years ago
Samantaz Fox e0d063d306
Merge pull request #3197 from weblate/weblate-invidious-translations 2 years ago
CalculationPaper c847d6d370
Update licenses.ecr
Oh, it's handlers not helpers.
2 years ago
CalculationPaper bbf66c9b72
Add/Change Javascript license notice 2 years ago
Hosted Weblate ed0ad587dc
Update Indonesian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: uwu as a service <okayokokay@protonmail.com>
2 years ago
Hosted Weblate 56fe591eee
Update Portuguese (Portugal) translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Tmpod <tmpod@pm.me>
2 years ago
Hosted Weblate 7b9693bca4
Update German translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Pixelcode <pixelcode@dismail.de>
2 years ago
Hosted Weblate fd0417b14c
Update Greek translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: THANOS SIOURDAKIS <siourdakisthanos@gmail.com>
2 years ago
Hosted Weblate 89c12f2585
Update Italian translation
Co-authored-by: atilluF <atilluf@outlook.com>
2 years ago
Hosted Weblate 5c71adb137
Update Norwegian Bokmål translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Petter Reinholdtsen <pere-weblate@hungry.com>
2 years ago
Hosted Weblate 4c23062d1e
Update Arabic translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
2 years ago
Hosted Weblate 190b45086c
Update Russian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Егор Ермаков <eg.ermakov2016@yandex.ru>
2 years ago
Hosted Weblate 008983c8e3
Update Sinhala translation
Update Sinhala translation

Add Sinhala translation

Co-authored-by: DilshanH <ntpghiran@hotmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Samantaz Fox cb8a375c5e
routing: Directly call Kemal's add_route function 2 years ago
Samantaz Fox 848a60aa9b
routes: remove useless 'locale' variable in error 505 handler 2 years ago
Samantaz Fox 88ea794fdb
routes: move error 404 logic to its own module 2 years ago
Samantaz Fox 870350fd61
routes: move before_all logic to its own module 2 years ago
Samantaz Fox 1e25894f7e
routing: move the remaining routes registration to a wrapper function 2 years ago
Samantaz Fox 223e74569a
routing: move feed routes registration to Invidious::Routing 2 years ago
Samantaz Fox 0a4d793556
routing: move search routes registration to Invidious::Routing 2 years ago
Samantaz Fox 5503914abe
routing: move playlist routes registration to Invidious::Routing 2 years ago
Samantaz Fox 906466d7fb
routing: move watch/embed routes registration to Invidious::Routing 2 years ago
Samantaz Fox e2532de766
routing: move image proxy routes registration to Invidious::Routing 2 years ago
Samantaz Fox 3ac4390d11
routing: move channel routes registration to Invidious::Routing 2 years ago
Samantaz Fox 389ae7a573
routing: register playback routes with a function, rather than a macro 2 years ago
Samantaz Fox 176247091d
routing: register API routes with a function, rather than a macro 2 years ago
Samantaz Fox e22cc73f32
routing: register user routes with a function, rather than a macro 2 years ago
Samantaz Fox c23ad25899
routing: remove HEAD from HTTP methods
Kemal automatically creates an associated HEAD route for all GET routes
2 years ago
Émilien Devos 7f2ec18372
Add param 8AEB for getting youtube stories 2 years ago
Samantaz Fox 9cc0418769
Merge pull request #3249 from iv-org/improve-extractor 2 years ago
Samantaz Fox 925a2c8e77
Merge pull request #3253 from amarakon/master 2 years ago
Samantaz Fox 390734d86e
Merge pull request #3255 from iv-org/fix-content-not-available 2 years ago
Emilien Devos 218f7be1a7 For android client send sdk version to youtube 2 years ago
Emilien Devos 246955b68a if case for sectionListRenderer 2 years ago
amarakon d24506baed Add Ytfzf to projects using Invidious 2 years ago
Samantaz Fox b0be2237fa
Merge pull request #3248 from iv-org/fix-cookies 2 years ago
Samantaz Fox 945394fb1a
Merge pull request #3252 from SamantazFox/bump-ytapi-clients 2 years ago
Samantaz Fox 19886f71f5
Merge pull request #3250 from iv-org/fix-cookies 2 years ago
Samantaz Fox 23855c09dc
Remove 'screen' where not required 2 years ago
Samantaz Fox 618ab01cd7
Add TVHtml5 client 2 years ago
Samantaz Fox 349d90b60e
Add IOS clients 2 years ago
Samantaz Fox 9e7c2dcdbb
Move the default API key to a constant for clarity 2 years ago
Samantaz Fox f353589a53
Bump web clients versions 2 years ago
Samantaz Fox fc97929dee
Bump android app version 2 years ago
Émilien Devos 3d77642a1e
Disable decrypt_polling by default + add comment (#3244) 2 years ago
Emilien Devos b55c1a35bf Set cookies to Lax 2 years ago
Mateusz Jabłoński 5df700a56e Added image tag to RSS channel for favicon rendering
https://validator.w3.org/feed/docs/rss2.html#ltimagegtSubelementOfLtchannelgt
2 years ago
Emilien Devos 0c64a86ebe crystal 1.5.0 to CI and update crystal version 2 years ago
Émilien Devos 644ba46945
Remove mentions that decrypt_polling is broken
And add notice about bandwidth usage, related to https://github.com/iv-org/invidious/issues/3234
2 years ago
Samantaz Fox 4ab54f284c
Merge pull request #3214 from SamantazFox/fix-scheduled-streams 2 years ago
Émilien Devos 210c2a8855
Fix updated sources not returned inside map func
This fix the issue reported in https://github.com/iv-org/invidious/issues/2055#issuecomment-1192894698
2 years ago
Samantaz Fox 0ed4f1a9a4
Add unit tests for scheduled livestreams 2 years ago
Samantaz Fox 7e648840a1
Move InfoException to exceptions.cr 2 years ago
Émilien Devos 5e090778ae
Use alpine 3.16 for crystal 1.4.1
Until crystal 1.5 has been tested.
2 years ago
Samantaz Fox 049ed114fd
Separate video data fetching from parsing in videos.cr 2 years ago
Samantaz Fox 88007a08f2
Merge pull request #3205 from iv-org/escape-username 2 years ago
Émilien Devos 6c4ed282bb HTML escape username 2 years ago
Samantaz Fox c8765385df
Fetch data from next endpoint for scheduled streams 2 years ago
Samantaz Fox 0ed22c0be0
Merge pull request #3140 from AHOHNMYC/css-ie11-fixes 2 years ago
Samantaz Fox 3ffef4b9fb
Merge pull request #3133 from AHOHNMYC/widen-settings-name 2 years ago
Samantaz Fox ceeebceb3a
Merge pull request #3199 from AHOHNMYC/fix-js-in-embed 2 years ago
AHOHNMYC 0338b26d5c
Include `_helpers.js` in embedded view 2 years ago
PrivateGER 6577cc0c8c
Fix a dead link to Docker install documentation (#3198) 2 years ago
Émilien Devos 586000ca3d add more explanation about checking the player dependencies 2 years ago
Samantaz Fox abc81ebd08
Merge pull request #3165 from SamantazFox/small-fixes-06-2022 2 years ago
Samantaz Fox cd6c73e487
Merge pull request #3162 from 138138138/138138138-audio-quality-1 2 years ago
Samantaz Fox 69ad57338f
Mention why we use multiple AdaptationSet for audio 2 years ago
138138138 cbcf31a4f9
Skip OTF streams in DASH audio
Skip OTF streams, prevent creating empty AdaptationSet in DASH audio
2 years ago
138138138 b19beac5b4
Update src/invidious/views/components/player.ecr
better syntax

Co-authored-by: Samantaz Fox <coding@samantaz.fr>
2 years ago
Samantaz Fox dc6d088e30
Merge pull request #3158 from 138138138/138138138-mobileui-forward-rate 2 years ago
Samantaz Fox b0ad27af23
Merge pull request #3151 from weblate/weblate-invidious-translations 2 years ago
Hosted Weblate 5f23c6358a
Update Czech translation
Update Czech translation

Co-authored-by: Fjuro <fjuro@seznam.cz>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate da776c935f
Update Indonesian translation
Update Indonesian translation

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: liimee <alt3753.7@gmail.com>
Co-authored-by: uwu as a service <okayokokay@protonmail.com>
2 years ago
Hosted Weblate 0a315783ef
Update Portuguese (Portugal) translation
Update Portuguese (Portugal) translation

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Tmpod <tmpod@pm.me>
2 years ago
Hosted Weblate 65061b0514
Update Japanese translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: uwu as a service <okayokokay@protonmail.com>
2 years ago
Hosted Weblate 063e5e359e
Update Turkish translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
2 years ago
Hosted Weblate f460afca35
Update Chinese (Simplified) translation
Co-authored-by: Eric <alchemillatruth@purelymail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate 66a08ace1d
Update Slovenian translation
Co-authored-by: Damjan Gerl <damjan@damjan.net>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate 68e65e968a
Update Portuguese translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: SC <lalocas@protonmail.com>
2 years ago
Hosted Weblate 1ba0ab982b
Update Croatian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Milo Ivir <mail@milotype.de>
2 years ago
Hosted Weblate 8752b8bb3f
Update Finnish translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Markus Mikkonen <markus.mikkonen@outlook.com>
2 years ago
Hosted Weblate 168f86ef89
Update Portuguese (Brazil) translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: The Cats <philosoph@snopyta.org>
2 years ago
Hosted Weblate 85927853f9
Update Chinese (Traditional) translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jeff Huang <s8321414@gmail.com>
2 years ago
Hosted Weblate 57f60bf173
Update Ukrainian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
2 years ago
Hosted Weblate d16c3ed40a
Update Italian translation
Update Italian translation

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Pietro Cappuccino <p.cappuccino@tiscali.it>
Co-authored-by: ㅤAbsurdUsername <sgso@pm.me>
2 years ago
Hosted Weblate e90f4a2cbf
Update Norwegian Bokmål translation
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate d00839ec68
Update Russian translation
Update Russian translation

Update Russian translation

Update Russian translation

Co-authored-by: AHOHNMYC <lqwh2h2cwa@protonmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Егор Ермаков <eg.ermakov2016@yandex.ru>
2 years ago
Samantaz Fox ea35d92493
Merge pull request #3157 from 138138138/138138138-fix-captions 2 years ago
Samantaz Fox beb9894c47
Merge pull request #3124 from iv-org/add-404-status-code 2 years ago
Samantaz Fox f7b1dcc271
Don't treat LIVE_STREAM_OFFLINE playability status as an error (fixes #3155) 2 years ago
Samantaz Fox eb226e1dcf
Remove all backend code related to dislikes 2 years ago
Samantaz Fox 8332ad0f16
Fix syntax errors in shell scripts 2 years ago
Samantaz Fox 06af5a004e
Remove useless link in item forms (buttons on thumbnail) 2 years ago
Samantaz Fox ce32873ef8
Remove item (video/channel/mix) thumbnail from keyboard navigation tree 2 years ago
Samantaz Fox 99bc230fe6
Fix missing hash key: "availableCountries" (Closes #3047) 2 years ago
Samantaz Fox 0e3820b634
Add #to_http_params method to Query (Fixes #3148) 2 years ago
Samantaz Fox eba84dcd78
Merge pull request #3183 from 11Tuvork28/fix-selected-key-missing 2 years ago
11tuvork28 864f27ef72 switched to extract_selected_tab for the community tab 2 years ago
11tuvork28 a8b72d8342 Fixed community tab 2 years ago
11tuvork28 15d2cfba90 Fix `Missing hash key: "selected" (KeyError)` 2 years ago
11tuvork28 2851d993ad updated comment to represent current structure 2 years ago
138138138 3f1d88282e
Update some comments 2 years ago
138138138 cc9ce916c6
Update MobileUi 2 years ago
138138138 c7d468578f
Update MobileUi 2 years ago
138138138 e0f6988eb5
DASH Default to high quality m4a 2 years ago
138138138 09ff370ddc
Change player.css order 2 years ago
138138138 32ecf30c82
Add audioTrackButton 2 years ago
138138138 a62adccd3d
change lang to label
lang has to be BCP 47 standard. Using label also can let video.js know there are 2 audio tracks.
2 years ago
138138138 c75bf35f59
Update DASH format to serve 2 audio to player
player.audioTracks() can successfully show
tracks_: Array(2)
2 years ago
138138138 3013782b7b
formatting 2 years ago
138138138 81abebd144
Highest quality m4a on audio only mode as default
Audio mode will automatically select highest quality m4a as default.
2 years ago
138138138 140b6c1227
DASH playback force highest quality m4a
Since VideoJS is unable to handle adaptive audio quality, the best audo quality is forced for every video quality.
2 years ago
138138138 ac685f65e9
Fix captions
textTracks 0 in DASH mode shows debug messages.
Use textTracks 1 in DASH mode, and textTracks 0 in non-DASH mode and audio mode.
2 years ago
138138138 de74056925
Keep listen mode after related video click
When clicking the related videos, listen mode will be kept by passing listen=true/listen=false on the URL.
2 years ago
138138138 f6b1cbd5d0
Player MobileUi fast forward/backward rate
The fast forward/backward seconds will be adjusted according to playback rate (same as YouTube app behavior).
5 seconds is used when the playback rate is 1x. Previously it was 10 seconds. I believe most of the users watch videos at 2x, so the change will not be obvious.
2 years ago
138138138 7db6e43e3f
Fix captions
Captions should automatically show according to preferences.
2 years ago
Émilien Devos 6c73614a47
Bump revision for crystal alpine package on Docker 2 years ago
Samantaz Fox b5c54b4e41
Merge pull request #3137 from SamantazFox/add-hashtags
Add hashtags
2 years ago
Samantaz Fox 8f1c84e6d4
Merge pull request #3119 from GauthierPLM/popular-enabled
Add "Popular Enabled: " string to localisation
2 years ago
Samantaz Fox d1df4af734
Merge pull request #3118 from weblate/weblate-invidious-translations
Translations update from Hosted Weblate
2 years ago
Samantaz Fox 23cd04fe88
Merge pull request #3116 from DoodlesEpic/fix-reddit-comments-nojs-ytkids
Fix document is empty error on yt kids video when reddit comments are enabled
2 years ago
Samantaz Fox dbc7c97e0b
Merge pull request #3103 from SamantazFox/add-utility-scripts
Add utility scripts
2 years ago
Samantaz Fox 2313ca8f72
Merge pull request #3084 from AHOHNMYC/js-helpers-polyfills
JS refactoring part 2: helper functions, poyfills
2 years ago
Hosted Weblate 9418ba1687
Update Bengali translation
Update Bengali translation

Add Bengali translation

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Oymate <dhruboadittya96@gmail.com>
2 years ago
Hosted Weblate 233491940c
Update Indonesian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: I. Musthafa <i.musthafa66@gmail.com>
2 years ago
Hosted Weblate 7708e7ab08
Update Slovenian translation
Update Slovenian translation

Co-authored-by: Damjan Gerl <damjan@damjan.net>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Hosted Weblate 600bd38630
Update Portuguese translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: SC <lalocas@protonmail.com>
2 years ago
Hosted Weblate f7290dfcb6
Update Croatian translation
Update Croatian translation

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Milo Ivir <mail@milotype.de>
2 years ago
Hosted Weblate e22f7583eb
Update Ukrainian translation
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
2 years ago
Hosted Weblate c0e85f5687
Update Russian translation
Co-authored-by: AHOHNMYC <lqwh2h2cwa@protonmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
2 years ago
Samantaz Fox 3593f67eb6
Fix: related videos is a Hash(String, String) 2 years ago
Samantaz Fox 1b251264a6
Pull submodules during CI 2 years ago
Samantaz Fox 93c1a1d42e
Add mocks as a submodule 2 years ago
Samantaz Fox d7f6b6b018
Fix CI: support reloadContinuationItemsCommand containers 2 years ago
Samantaz Fox fd99f20404
Fix CI: use be_close() with 1s delta for Time comparisons 2 years ago
Samantaz Fox 2b1e1b11a3
Fix CI: support BADGE_STYLE_TYPE_VERIFIED_ARTIST 2 years ago
Samantaz Fox 96ac7f9f35
Add hashtag extractor spec 2 years ago
Samantaz Fox 33da64a669
Add support for hashtags 2 years ago
meow 38eb4ccbc4 CSS. Small IE11 fixes 2 years ago
meow a57414307e CSS. Small IE11 fixes 2 years ago
Mohammed Anas 7ad111e2f6
Update actions used in GH workflows (#3138) 2 years ago
meow a402128a7d Move `_helpers.js` include from various .ecr's into `template.ecr` `head` tag 2 years ago
meow d3ab4a5145 JS. Trailing spaces removed 2 years ago
Émilien Devos 4ae77bcef9
Remove rating display from the frontend 2 years ago
Émilien Devos e84416e56d
Remove dislikes icon (#3092) 2 years ago
TheFrenchGhosty 59ccc9d73e
Merge pull request #3134 from lhc-sudo/patch-1
Add TubiTui to Projects Using Invidious section of README.md
2 years ago
777 1533a28817
Add TubiTui to Projects Using Invidious section 2 years ago
meow 7e4840867e CSS. Wider settings name to less word wrap 2 years ago
meow f2f3f045e5 fix time adding dirung redirection 2 years ago
meow b12149bafd Save time during redirection on another instance 2 years ago
TheFrenchGhosty 307c1b0b62
Merge pull request #3128 from arekf/patch-1
Add Yattee to README
3 years ago
TheFrenchGhosty b201745988
Markdown enhancement 3 years ago
Arkadiusz Fal 352266481e
Add Yattee to README 3 years ago
Gauthier POGAM--LE MONTAGNER 958867e92b
Fix wrong french translation
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
3 years ago
Gauthier POGAM--LE MONTAGNER b50de2f2ed Add "Popular Enabled: " string to localisation 3 years ago
DoodlesEpic ad37db4c82 Fix document is empty error on yt kids video when reddit comments are enabled 3 years ago
Samantaz Fox 1f359f5a13
Print some helpful notice for PostgreSQL configuration 3 years ago
Samantaz Fox fe53b5503c
Add a script to start postgres and create user/DB 3 years ago
Samantaz Fox d66ef8fe22
Add a script to install dependencies 3 years ago
meow b729597728 comment changed
extra spaces removed
3 years ago
meow b72b917af2 handled invalid values in storage
partial rewrite notifications.js
innerText to textContent
fixed bug with clamping
3 years ago
AHOHNMYC 319bbd2f81
JS code minor formatting
Co-authored-by: Samantaz Fox <coding@samantaz.fr>
3 years ago
meow 1097648f0a Fix HTML validation. This is how browser really split tags 3 years ago
meow 17e6213448 Less player reload timeout 3 years ago
meow 2ea423032e Share video regression. Single quotes are required 3 years ago
meow 2dead1a19b JS theme switching simplified 3 years ago
meow e18b10297b JS fixes: recursion in themes, keys for frame walking, JSON XHR and details-summary in IE11 3 years ago
meow fd66084388 js code rewrite. Themes rewritten, bugs fixed 3 years ago
meow f06d5b973b jsdoc type fix 3 years ago
meow fd890f9c0a fix helpers storage 3 years ago
meow 835237382f fix helpers 3 years ago
meow 7dd699370f js code rewrite. Created _helpers.js with XHR and storage wrapper 3 years ago

@ -0,0 +1,37 @@
name: Close duplicates
on:
issues:
types: [opened]
jobs:
run:
runs-on: ubuntu-latest
permissions: write-all
steps:
- uses: iv-org/close-potential-duplicates@v1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Issue title filter work with anymatch https://www.npmjs.com/package/anymatch.
# Any matched issue will stop detection immediately.
# You can specify multi filters in each line.
filter: ''
# Exclude keywords in title before detecting.
exclude: ''
# Label to set, when potential duplicates are detected.
label: duplicate
# Get issues with state to compare. Supported state: 'all', 'closed', 'open'.
state: open
# If similarity is higher than this threshold([0,1]), issue will be marked as duplicate.
threshold: 0.9
# Reactions to be add to comment when potential duplicates are detected.
# Available reactions: "-1", "+1", "confused", "laugh", "heart", "hooray", "rocket", "eyes"
reactions: ''
close: true
# Comment to post when potential duplicates are detected.
comment: |
Hello, your issue is a duplicate of this/these issue(s): {{#issues}}
- #{{ number }} [accuracy: {{ accuracy }}%]
{{/issues}}
If this is a mistake please explain why and ping @\unixfox, @\SamantazFox and @\TheFrenchGhosty.
Please refrain from opening new issues, it won't help in solving your problem.

@ -38,23 +38,27 @@ jobs:
matrix:
stable: [true]
crystal:
- 1.2.2
- 1.3.2
- 1.4.0
- 1.4.1
- 1.5.1
- 1.6.2
- 1.7.3
- 1.8.1
include:
- crystal: nightly
stable: false
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: true
- name: Install Crystal
uses: crystal-lang/install-crystal@v1.5.3
uses: crystal-lang/install-crystal@v1.7.0
with:
crystal: ${{ matrix.crystal }}
- name: Cache Shards
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ./lib
key: shards-${{ hashFiles('shard.lock') }}
@ -84,7 +88,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Build Docker
run: docker-compose build --build-arg release=0
@ -100,18 +104,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v2
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Build Docker ARM64 image
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile.arm64

@ -22,12 +22,12 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install Crystal
uses: oprypin/install-crystal@v1.2.4
uses: crystal-lang/install-crystal@v1.6.0
with:
crystal: 1.2.2
crystal: 1.5.0
- name: Run lint
run: |
@ -38,23 +38,23 @@ jobs:
fi
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@v2
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@v2
- name: Login to registry
uses: docker/login-action@v1
uses: docker/login-action@v2
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
- name: Build and push Docker AMD64 image for Push Event
- name: Build and push Docker AMD64 image without QUIC for Push Event
if: github.ref == 'refs/heads/master'
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile
@ -62,11 +62,13 @@ jobs:
labels: quay.expires-after=12w
push: true
tags: quay.io/invidious/invidious:${{ github.sha }},quay.io/invidious/invidious:latest
build-args: release=1
build-args: |
"release=1"
"disable_quic=1"
- name: Build and push Docker ARM64 image for Push Event
- name: Build and push Docker ARM64 image without QUIC for Push Event
if: github.ref == 'refs/heads/master'
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile.arm64
@ -74,4 +76,30 @@ jobs:
labels: quay.expires-after=12w
push: true
tags: quay.io/invidious/invidious:${{ github.sha }}-arm64,quay.io/invidious/invidious:latest-arm64
build-args: |
"release=1"
"disable_quic=1"
- name: Build and push Docker AMD64 image with QUIC for Push Event
if: github.ref == 'refs/heads/master'
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile
platforms: linux/amd64
labels: quay.expires-after=12w
push: true
tags: quay.io/invidious/invidious:${{ github.sha }}-quic,quay.io/invidious/invidious:latest-quic
build-args: release=1
- name: Build and push Docker ARM64 image with QUIC for Push Event
if: github.ref == 'refs/heads/master'
uses: docker/build-push-action@v3
with:
context: .
file: docker/Dockerfile.arm64
platforms: linux/arm64/v8
labels: quay.expires-after=12w
push: true
tags: quay.io/invidious/invidious:${{ github.sha }}-arm64-quic,quay.io/invidious/invidious:latest-arm64-quic
build-args: release=1

@ -10,7 +10,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 365
@ -22,3 +22,5 @@ jobs:
stale-issue-label: "stale"
stale-pr-label: "stale"
ascending: true
# Never mark feature requests/enhancements as stale
exempt-issue-labels: "feature-request,enhancement,exempt-stale"

2
.gitignore vendored

@ -1,4 +1,4 @@
/doc/
/docs/
/dev/
/lib/
/bin/

3
.gitmodules vendored

@ -0,0 +1,3 @@
[submodule "mocks"]
path = mocks
url = ../mocks

@ -5,7 +5,7 @@
RELEASE := 1
STATIC := 0
DISABLE_QUIC := 0
DISABLE_QUIC := 1
NO_DBG_SYMBOLS := 0
@ -31,6 +31,10 @@ ifeq ($(DISABLE_QUIC), 1)
FLAGS += -Ddisable_quic
endif
ifeq ($(API_ONLY), 1)
FLAGS += -Dapi_only
endif
# -----------------------
# Main
@ -109,6 +113,7 @@ help:
@echo " RELEASE Make a release build (Default: 1)"
@echo " STATIC Link libraries statically (Default: 0)"
@echo ""
@echo " API_ONLY Build invidious without a GUI (Default: 0)"
@echo " DISABLE_QUIC Disable support for QUIC (Default: 0)"
@echo " NO_DBG_SYMBOLS Strip debug symbols (Default: 0)"

@ -147,10 +147,15 @@ Weblate also allows you to log-in with major SSO providers like Github, Gitlab,
- [FreeTube](https://github.com/FreeTubeApp/FreeTube): A libre software YouTube app for privacy.
- [CloudTube](https://sr.ht/~cadence/tube/): A JavaScript-rich alternate YouTube player.
- [PeerTubeify](https://gitlab.com/Cha_deL/peertubeify): On YouTube, displays a link to the same video on PeerTube, if it exists.
- [PeerTubeify](https://gitlab.com/Cha_de_L/peertubeify): On YouTube, displays a link to the same video on PeerTube, if it exists.
- [MusicPiped](https://github.com/deep-gaurav/MusicPiped): A material design music player that streams music from YouTube.
- [HoloPlay](https://github.com/stephane-r/HoloPlay): Funny Android application connecting on Invidious API's with search, playlists and favorites.
- [HoloPlay](https://github.com/stephane-r/holoplay-wa): Progressive Web App connecting on Invidious API's with search, playlists and favorites.
- [WatchTube](https://github.com/WatchTubeTeam/WatchTube): Powerful YouTube client for Apple Watch.
- [Yattee](https://github.com/yattee/yattee): Alternative YouTube frontend for iPhone, iPad, Mac and Apple TV.
- [TubiTui](https://codeberg.org/777/TubiTui): A lightweight, libre, TUI-based YouTube client.
- [Ytfzf](https://github.com/pystardust/ytfzf): A posix script to find and watch youtube videos from the terminal. (Without API).
- [Playlet](https://github.com/iBicha/playlet): Unofficial Youtube client for Roku TV.
- [Clipious](https://github.com/lamarios/clipious): Unofficial Invidious client for Android.
## Liability

@ -46,6 +46,7 @@ body a.channel-owner {
}
.creator-heart {
display: inline-block;
position: relative;
width: 16px;
height: 16px;
@ -66,6 +67,7 @@ body a.channel-owner {
}
.creator-heart-small-container {
display: block;
position: relative;
width: 13px;
height: 13px;
@ -119,13 +121,16 @@ body a.pure-button {
button.pure-button-primary,
body a.pure-button-primary,
.channel-owner:hover {
.channel-owner:hover,
.channel-owner:focus {
background-color: #a0a0a0;
color: rgba(35, 35, 35, 1);
}
button.pure-button-primary:hover,
body a.pure-button-primary:hover {
body a.pure-button-primary:hover,
button.pure-button-primary:focus,
body a.pure-button-primary:focus {
background-color: rgba(0, 182, 240, 1);
color: #fff;
}
@ -145,6 +150,24 @@ img.thumbnail {
object-fit: cover;
}
div.watched-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255,255,255,.4);
}
div.watched-indicator {
position: absolute;
left: 0;
bottom: 0;
height: 4px;
width: 100%;
background-color: red;
}
.length {
z-index: 100;
position: absolute;
@ -204,17 +227,20 @@ img.thumbnail {
margin: 1px;
border: 1px solid;
border-color: #0000 #0000 #CCC #0000;
border-color: rgba(0,0,0,0);
border-bottom-color: #CCC;
border-radius: 0;
box-shadow: none;
appearance: none;
-webkit-appearance: none;
}
.searchbar input[type="search"]:focus {
margin: 0 0 0.5px 0;
margin: 0;
border: 2px solid;
border-color: #0000 #0000 #FED #0000;
border-color: rgba(0,0,0,0);
border-bottom-color: #FED;
}
/* https://stackoverflow.com/a/55170420 */
@ -234,7 +260,7 @@ input[type="search"]::-webkit-search-cancel-button {
}
.user-field div {
width: initial;
width: auto;
}
.user-field div:not(:last-child) {
@ -297,6 +323,30 @@ p.channel-name { margin: 0; }
p.video-data { margin: 0; font-weight: bold; font-size: 80%; }
/*
* Comments & community posts
*/
#comments {
max-width: 800px;
margin: auto;
}
.video-iframe-wrapper {
position: relative;
height: 0;
padding-bottom: 56.25%;
}
.video-iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: none;
}
/*
* Footer
*/
@ -345,11 +395,14 @@ span > select {
.light-theme a:hover,
.light-theme a:active,
.light-theme summary:hover {
.light-theme summary:hover,
.light-theme a:focus,
.light-theme summary:focus {
color: #075A9E !important;
}
.light-theme a.pure-button-primary:hover {
.light-theme a.pure-button-primary:hover,
.light-theme a.pure-button-primary:focus {
color: #fff !important;
}
@ -372,11 +425,14 @@ span > select {
@media (prefers-color-scheme: light) {
.no-theme a:hover,
.no-theme a:active,
.no-theme summary:hover {
.no-theme summary:hover,
.no-theme a:focus,
.no-theme summary:focus {
color: #075A9E !important;
}
.no-theme a.pure-button-primary:hover {
.no-theme a.pure-button-primary:hover,
.no-theme a.pure-button-primary:focus {
color: #fff !important;
}
@ -403,7 +459,9 @@ span > select {
.dark-theme a:hover,
.dark-theme a:active,
.dark-theme summary:hover {
.dark-theme summary:hover,
.dark-theme a:focus,
.dark-theme summary:focus {
color: rgb(0, 182, 240);
}
@ -442,7 +500,8 @@ body.dark-theme {
@media (prefers-color-scheme: dark) {
.no-theme a:hover,
.no-theme a:active {
.no-theme a:active,
.no-theme a:focus {
color: rgb(0, 182, 240);
}
@ -488,13 +547,17 @@ hr {
}
/* Description Expansion Styling*/
#descexpansionbutton {
display: none
#descexpansionbutton,
#music-desc-expansion {
display: none;
}
#descexpansionbutton ~ div {
overflow: hidden;
height: 8.3em;
}
#descexpansionbutton:not(:checked) ~ div {
max-height: 8.3em;
}
#descexpansionbutton:checked ~ div {
@ -507,6 +570,11 @@ hr {
margin-top: 20px;
}
label[for="descexpansionbutton"]:hover,
label[for="music-desc-expansion"]:hover {
cursor: pointer;
}
/* Bidi (bidirectional text) support */
h1,
h2,
@ -515,7 +583,8 @@ h4,
h5,
p,
#descriptionWrapper,
#description-box {
#description-box,
#music-description-box {
unicode-bidi: plaintext;
text-align: start;
}
@ -525,5 +594,38 @@ p,
white-space: pre-wrap;
}
#music-description-box {
display: none;
}
#music-desc-expansion:checked ~ #music-description-box {
display: block;
}
#music-desc-expansion ~ label > h3 > .ion-ios-arrow-up,
#music-desc-expansion:checked ~ label > h3 > .ion-ios-arrow-down {
display: none;
}
#music-desc-expansion:checked ~ label > h3 > .ion-ios-arrow-up,
#music-desc-expansion ~ label > h3 > .ion-ios-arrow-down {
display: inline;
}
/* Select all the music items except the first one */
.music-item + .music-item {
border-top: 1px solid #ffffff;
}
/* Center the "invidious" logo on the search page */
#logo > h1 { text-align: center; }
/* IE11 fixes */
:-ms-input-placeholder { color: #888; }
/* Wider settings name to less word wrap */
.pure-form-aligned .pure-control-group label { width: 19em; }
.channel-emoji {
margin: 0 2px;
}

@ -21,6 +21,7 @@
color: white;
}
.watch-on-invidious > a:hover {
.watch-on-invidious > a:hover,
.watch-on-invidious > a:focus {
color: rgba(0, 182, 240, 1);;
}

@ -101,21 +101,25 @@ ul.vjs-menu-content::-webkit-scrollbar {
order: 2;
}
.vjs-audio-button {
order: 3;
}
.vjs-quality-selector,
.video-js .vjs-http-source-selector {
order: 3;
order: 4;
}
.vjs-playback-rate {
order: 4;
order: 5;
}
.vjs-share-control {
order: 5;
order: 6;
}
.vjs-fullscreen-control {
order: 6;
order: 7;
}
.vjs-playback-rate > .vjs-menu {
@ -171,11 +175,14 @@ ul.vjs-menu-content::-webkit-scrollbar {
.video-js.player-style-invidious .vjs-play-progress {
background-color: rgba(0, 182, 240, 1);
}
vjs-menu-content
/* Overlay */
.video-js .vjs-overlay {
background-color: rgba(35, 35, 35, 0.75);
color: rgba(255, 255, 255, 1);
background-color: rgba(35, 35, 35, 0.75) !important;
}
.video-js .vjs-overlay * {
color: rgba(255, 255, 255, 1) !important;
text-align: center;
}
/* ProgressBar marker */

@ -68,7 +68,10 @@ fieldset, legend {
.filter-options label { margin: 0 10px; }
#filters-apply { text-align: end; }
#filters-apply {
text-align: right; /* IE11 only */
text-align: end; /* Override for compatible browsers */
}
/* Error message */

@ -0,0 +1,254 @@
'use strict';
// Contains only auxiliary methods
// May be included and executed unlimited number of times without any consequences
// Polyfills for IE11
Array.prototype.find = Array.prototype.find || function (condition) {
return this.filter(condition)[0];
};
Array.from = Array.from || function (source) {
return Array.prototype.slice.call(source);
};
NodeList.prototype.forEach = NodeList.prototype.forEach || function (callback) {
Array.from(this).forEach(callback);
};
String.prototype.includes = String.prototype.includes || function (searchString) {
return this.indexOf(searchString) >= 0;
};
String.prototype.startsWith = String.prototype.startsWith || function (prefix) {
return this.substr(0, prefix.length) === prefix;
};
Math.sign = Math.sign || function(x) {
x = +x;
if (!x) return x; // 0 and NaN
return x > 0 ? 1 : -1;
};
if (!window.hasOwnProperty('HTMLDetailsElement') && !window.hasOwnProperty('mockHTMLDetailsElement')) {
window.mockHTMLDetailsElement = true;
const style = 'details:not([open]) > :not(summary) {display: none}';
document.head.appendChild(document.createElement('style')).textContent = style;
addEventListener('click', function (e) {
if (e.target.nodeName !== 'SUMMARY') return;
const details = e.target.parentElement;
if (details.hasAttribute('open'))
details.removeAttribute('open');
else
details.setAttribute('open', '');
});
}
// Monstrous global variable for handy code
// Includes: clamp, xhr, storage.{get,set,remove}
window.helpers = window.helpers || {
/**
* https://en.wikipedia.org/wiki/Clamping_(graphics)
* @param {Number} num Source number
* @param {Number} min Low border
* @param {Number} max High border
* @returns {Number} Clamped value
*/
clamp: function (num, min, max) {
if (max < min) {
var t = max; max = min; min = t; // swap max and min
}
if (max < num)
return max;
if (min > num)
return min;
return num;
},
/** @private */
_xhr: function (method, url, options, callbacks) {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
// Default options
xhr.responseType = 'json';
xhr.timeout = 10000;
// Default options redefining
if (options.responseType)
xhr.responseType = options.responseType;
if (options.timeout)
xhr.timeout = options.timeout;
if (method === 'POST')
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// better than onreadystatechange because of 404 codes https://stackoverflow.com/a/36182963
xhr.onloadend = function () {
if (xhr.status === 200) {
if (callbacks.on200) {
// fix for IE11. It doesn't convert response to JSON
if (xhr.responseType === '' && typeof(xhr.response) === 'string')
callbacks.on200(JSON.parse(xhr.response));
else
callbacks.on200(xhr.response);
}
} else {
// handled by onerror
if (xhr.status === 0) return;
if (callbacks.onNon200)
callbacks.onNon200(xhr);
}
};
xhr.ontimeout = function () {
if (callbacks.onTimeout)
callbacks.onTimeout(xhr);
};
xhr.onerror = function () {
if (callbacks.onError)
callbacks.onError(xhr);
};
if (options.payload)
xhr.send(options.payload);
else
xhr.send();
},
/** @private */
_xhrRetry: function(method, url, options, callbacks) {
if (options.retries <= 0) {
console.warn('Failed to pull', options.entity_name);
if (callbacks.onTotalFail)
callbacks.onTotalFail();
return;
}
helpers._xhr(method, url, options, callbacks);
},
/**
* @callback callbackXhrOn200
* @param {Object} response - xhr.response
*/
/**
* @callback callbackXhrError
* @param {XMLHttpRequest} xhr
*/
/**
* @param {'GET'|'POST'} method - 'GET' or 'POST'
* @param {String} url - URL to send request to
* @param {Object} options - other XHR options
* @param {XMLHttpRequestBodyInit} [options.payload=null] - payload for POST-requests
* @param {'arraybuffer'|'blob'|'document'|'json'|'text'} [options.responseType=json]
* @param {Number} [options.timeout=10000]
* @param {Number} [options.retries=1]
* @param {String} [options.entity_name='unknown'] - string to log
* @param {Number} [options.retry_timeout=1000]
* @param {Object} callbacks - functions to execute on events fired
* @param {callbackXhrOn200} [callbacks.on200]
* @param {callbackXhrError} [callbacks.onNon200]
* @param {callbackXhrError} [callbacks.onTimeout]
* @param {callbackXhrError} [callbacks.onError]
* @param {callbackXhrError} [callbacks.onTotalFail] - if failed after all retries
*/
xhr: function(method, url, options, callbacks) {
if (!options.retries || options.retries <= 1) {
helpers._xhr(method, url, options, callbacks);
return;
}
if (!options.entity_name) options.entity_name = 'unknown';
if (!options.retry_timeout) options.retry_timeout = 1000;
const retries_total = options.retries;
let currentTry = 1;
const retry = function () {
console.warn('Pulling ' + options.entity_name + ' failed... ' + (currentTry++) + '/' + retries_total);
setTimeout(function () {
options.retries--;
helpers._xhrRetry(method, url, options, callbacks);
}, options.retry_timeout);
};
// Pack retry() call into error handlers
callbacks._onError = callbacks.onError;
callbacks.onError = function (xhr) {
if (callbacks._onError)
callbacks._onError(xhr);
retry();
};
callbacks._onTimeout = callbacks.onTimeout;
callbacks.onTimeout = function (xhr) {
if (callbacks._onTimeout)
callbacks._onTimeout(xhr);
retry();
};
helpers._xhrRetry(method, url, options, callbacks);
},
/**
* @typedef {Object} invidiousStorage
* @property {(key:String) => Object} get
* @property {(key:String, value:Object)} set
* @property {(key:String)} remove
*/
/**
* Universal storage, stores and returns JS objects. Uses inside localStorage or cookies
* @type {invidiousStorage}
*/
storage: (function () {
// access to localStorage throws exception in Tor Browser, so try is needed
let localStorageIsUsable = false;
try{localStorageIsUsable = !!localStorage.setItem;}catch(e){}
if (localStorageIsUsable) {
return {
get: function (key) {
let storageItem = localStorage.getItem(key)
if (!storageItem) return;
try {
return JSON.parse(decodeURIComponent(storageItem));
} catch(e) {
// Erase non parsable value
helpers.storage.remove(key);
}
},
set: function (key, value) {
let encoded_value = encodeURIComponent(JSON.stringify(value))
localStorage.setItem(key, encoded_value);
},
remove: function (key) { localStorage.removeItem(key); }
};
}
// TODO: fire 'storage' event for cookies
console.info('Storage: localStorage is disabled or unaccessible. Cookies used as fallback');
return {
get: function (key) {
const cookiePrefix = key + '=';
function findCallback(cookie) {return cookie.startsWith(cookiePrefix);}
const matchedCookie = document.cookie.split('; ').find(findCallback);
if (matchedCookie) {
const cookieBody = matchedCookie.replace(cookiePrefix, '');
if (cookieBody.length === 0) return;
try {
return JSON.parse(decodeURIComponent(cookieBody));
} catch(e) {
// Erase non parsable value
helpers.storage.remove(key);
}
}
},
set: function (key, value) {
const cookie_data = encodeURIComponent(JSON.stringify(value));
// Set expiration in 2 year
const date = new Date();
date.setFullYear(date.getFullYear()+2);
document.cookie = key + '=' + cookie_data + '; expires=' + date.toGMTString();
},
remove: function (key) {
document.cookie = key + '=; Max-Age=0';
}
};
})()
};

@ -1,13 +1,6 @@
'use strict';
var community_data = JSON.parse(document.getElementById('community_data').textContent);
String.prototype.supplant = function (o) {
return this.replace(/{([^{}]*)}/g, function (a, b) {
var r = o[b];
return typeof r === 'string' || typeof r === 'number' ? r : a;
});
};
function hide_youtube_replies(event) {
var target = event.target;
@ -38,13 +31,6 @@ function show_youtube_replies(event) {
target.setAttribute('data-sub-text', sub_text);
}
function number_with_separator(val) {
while (/(\d+)(\d{3})/.test(val.toString())) {
val = val.toString().replace(/(\d+)(\d{3})/, '$1' + ',' + '$2');
}
return val;
}
function get_youtube_replies(target, load_more) {
var continuation = target.getAttribute('data-continuation');
@ -58,18 +44,13 @@ function get_youtube_replies(target, load_more) {
'&hl=' + community_data.preferences.locale +
'&thin_mode=' + community_data.preferences.thin_mode +
'&continuation=' + continuation;
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
helpers.xhr('GET', url, {}, {
on200: function (response) {
if (load_more) {
body = body.parentNode.parentNode;
body.removeChild(body.lastElementChild);
body.innerHTML += xhr.response.contentHtml;
body.innerHTML += response.contentHtml;
} else {
body.removeChild(body.lastElementChild);
@ -81,24 +62,21 @@ function get_youtube_replies(target, load_more) {
a.onclick = hide_youtube_replies;
a.setAttribute('data-sub-text', community_data.hide_replies_text);
a.setAttribute('data-inner-text', community_data.show_replies_text);
a.innerText = community_data.hide_replies_text;
a.textContent = community_data.hide_replies_text;
var div = document.createElement('div');
div.innerHTML = xhr.response.contentHtml;
div.innerHTML = response.contentHtml;
body.appendChild(p);
body.appendChild(div);
}
} else {
},
onNon200: function (xhr) {
body.innerHTML = fallback;
}
}
};
xhr.ontimeout = function () {
console.warn('Pulling comments failed.');
},
onTimeout: function (xhr) {
console.warn('Pulling comments failed');
body.innerHTML = fallback;
};
xhr.send();
}
});
}

@ -1,14 +1,7 @@
'use strict';
var video_data = JSON.parse(document.getElementById('video_data').textContent);
function get_playlist(plid, retries) {
if (retries === undefined) retries = 5;
if (retries <= 0) {
console.warn('Failed to pull playlist');
return;
}
function get_playlist(plid) {
var plid_url;
if (plid.startsWith('RD')) {
plid_url = '/api/v1/mixes/' + plid +
@ -21,85 +14,49 @@ function get_playlist(plid, retries) {
'&format=html&hl=' + video_data.preferences.locale;
}
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', plid_url, true);
helpers.xhr('GET', plid_url, {retries: 5, entity_name: 'playlist'}, {
on200: function (response) {
if (!response.nextVideo)
return;
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
if (xhr.response.nextVideo) {
player.on('ended', function () {
var url = new URL('https://example.com/embed/' + xhr.response.nextVideo);
var url = new URL('https://example.com/embed/' + response.nextVideo);
url.searchParams.set('list', plid);
if (!plid.startsWith('RD')) {
url.searchParams.set('index', xhr.response.index);
}
if (video_data.params.autoplay || video_data.params.continue_autoplay) {
if (!plid.startsWith('RD'))
url.searchParams.set('index', response.index);
if (video_data.params.autoplay || video_data.params.continue_autoplay)
url.searchParams.set('autoplay', '1');
}
if (video_data.params.listen !== video_data.preferences.listen) {
if (video_data.params.listen !== video_data.preferences.listen)
url.searchParams.set('listen', video_data.params.listen);
}
if (video_data.params.speed !== video_data.preferences.speed) {
if (video_data.params.speed !== video_data.preferences.speed)
url.searchParams.set('speed', video_data.params.speed);
}
if (video_data.params.local !== video_data.preferences.local) {
if (video_data.params.local !== video_data.preferences.local)
url.searchParams.set('local', video_data.params.local);
}
location.assign(url.pathname + url.search);
});
}
}
}
};
xhr.onerror = function () {
console.warn('Pulling playlist failed... ' + retries + '/5');
setTimeout(function () { get_playlist(plid, retries - 1); }, 1000);
};
xhr.ontimeout = function () {
console.warn('Pulling playlist failed... ' + retries + '/5');
get_playlist(plid, retries - 1);
};
xhr.send();
});
}
window.addEventListener('load', function (e) {
addEventListener('load', function (e) {
if (video_data.plid) {
get_playlist(video_data.plid);
} else if (video_data.video_series) {
player.on('ended', function () {
var url = new URL('https://example.com/embed/' + video_data.video_series.shift());
if (video_data.params.autoplay || video_data.params.continue_autoplay) {
if (video_data.params.autoplay || video_data.params.continue_autoplay)
url.searchParams.set('autoplay', '1');
}
if (video_data.params.listen !== video_data.preferences.listen) {
if (video_data.params.listen !== video_data.preferences.listen)
url.searchParams.set('listen', video_data.params.listen);
}
if (video_data.params.speed !== video_data.preferences.speed) {
if (video_data.params.speed !== video_data.preferences.speed)
url.searchParams.set('speed', video_data.params.speed);
}
if (video_data.params.local !== video_data.preferences.local) {
if (video_data.params.local !== video_data.preferences.local)
url.searchParams.set('local', video_data.params.local);
}
if (video_data.video_series.length !== 0) {
if (video_data.video_series.length !== 0)
url.searchParams.set('playlist', video_data.video_series.join(','));
}
location.assign(url.pathname + url.search);
});

@ -1,8 +1,6 @@
'use strict';
(function () {
var n2a = function (n) { return Array.prototype.slice.call(n); };
var video_player = document.getElementById('player_html5_api');
if (video_player) {
video_player.onmouseenter = function () { video_player['data-title'] = video_player['title']; video_player['title'] = ''; };
@ -11,8 +9,8 @@
}
// For dynamically inserted elements
document.addEventListener('click', function (e) {
if (!e || !e.target) { return; }
addEventListener('click', function (e) {
if (!e || !e.target) return;
var t = e.target;
var handler_name = t.getAttribute('data-onclick');
@ -29,6 +27,7 @@
get_youtube_replies(t, load_more, load_replies);
break;
case 'toggle_parent':
e.preventDefault();
toggle_parent(t);
break;
default:
@ -36,118 +35,98 @@
}
});
n2a(document.querySelectorAll('[data-mouse="switch_classes"]')).forEach(function (e) {
var classes = e.getAttribute('data-switch-classes').split(',');
var ec = classes[0];
var lc = classes[1];
var onoff = function (on, off) {
var cs = e.getAttribute('class');
cs = cs.split(off).join(on);
e.setAttribute('class', cs);
};
e.onmouseenter = function () { onoff(ec, lc); };
e.onmouseleave = function () { onoff(lc, ec); };
document.querySelectorAll('[data-mouse="switch_classes"]').forEach(function (el) {
var classes = el.getAttribute('data-switch-classes').split(',');
var classOnEnter = classes[0];
var classOnLeave = classes[1];
function toggle_classes(toAdd, toRemove) {
el.classList.add(toAdd);
el.classList.remove(toRemove);
}
el.onmouseenter = function () { toggle_classes(classOnEnter, classOnLeave); };
el.onmouseleave = function () { toggle_classes(classOnLeave, classOnEnter); };
});
n2a(document.querySelectorAll('[data-onsubmit="return_false"]')).forEach(function (e) {
e.onsubmit = function () { return false; };
document.querySelectorAll('[data-onsubmit="return_false"]').forEach(function (el) {
el.onsubmit = function () { return false; };
});
n2a(document.querySelectorAll('[data-onclick="mark_watched"]')).forEach(function (e) {
e.onclick = function () { mark_watched(e); };
document.querySelectorAll('[data-onclick="mark_watched"]').forEach(function (el) {
el.onclick = function () { mark_watched(el); };
});
n2a(document.querySelectorAll('[data-onclick="mark_unwatched"]')).forEach(function (e) {
e.onclick = function () { mark_unwatched(e); };
document.querySelectorAll('[data-onclick="mark_unwatched"]').forEach(function (el) {
el.onclick = function () { mark_unwatched(el); };
});
n2a(document.querySelectorAll('[data-onclick="add_playlist_video"]')).forEach(function (e) {
e.onclick = function () { add_playlist_video(e); };
document.querySelectorAll('[data-onclick="add_playlist_video"]').forEach(function (el) {
el.onclick = function () { add_playlist_video(el); };
});
n2a(document.querySelectorAll('[data-onclick="add_playlist_item"]')).forEach(function (e) {
e.onclick = function () { add_playlist_item(e); };
document.querySelectorAll('[data-onclick="add_playlist_item"]').forEach(function (el) {
el.onclick = function () { add_playlist_item(el); };
});
n2a(document.querySelectorAll('[data-onclick="remove_playlist_item"]')).forEach(function (e) {
e.onclick = function () { remove_playlist_item(e); };
document.querySelectorAll('[data-onclick="remove_playlist_item"]').forEach(function (el) {
el.onclick = function () { remove_playlist_item(el); };
});
n2a(document.querySelectorAll('[data-onclick="revoke_token"]')).forEach(function (e) {
e.onclick = function () { revoke_token(e); };
document.querySelectorAll('[data-onclick="revoke_token"]').forEach(function (el) {
el.onclick = function () { revoke_token(el); };
});
n2a(document.querySelectorAll('[data-onclick="remove_subscription"]')).forEach(function (e) {
e.onclick = function () { remove_subscription(e); };
document.querySelectorAll('[data-onclick="remove_subscription"]').forEach(function (el) {
el.onclick = function () { remove_subscription(el); };
});
n2a(document.querySelectorAll('[data-onclick="notification_requestPermission"]')).forEach(function (e) {
e.onclick = function () { Notification.requestPermission(); };
document.querySelectorAll('[data-onclick="notification_requestPermission"]').forEach(function (el) {
el.onclick = function () { Notification.requestPermission(); };
});
n2a(document.querySelectorAll('[data-onrange="update_volume_value"]')).forEach(function (e) {
var cb = function () { update_volume_value(e); };
e.oninput = cb;
e.onchange = cb;
document.querySelectorAll('[data-onrange="update_volume_value"]').forEach(function (el) {
function update_volume_value() {
document.getElementById('volume-value').textContent = el.value;
}
el.oninput = update_volume_value;
el.onchange = update_volume_value;
});
function update_volume_value(element) {
document.getElementById('volume-value').innerText = element.value;
}
function revoke_token(target) {
var row = target.parentNode.parentNode.parentNode.parentNode.parentNode;
row.style.display = 'none';
var count = document.getElementById('count');
count.innerText = count.innerText - 1;
count.textContent--;
var referer = window.encodeURIComponent(document.location.href);
var url = '/token_ajax?action_revoke_token=1&redirect=false' +
'&referer=' + referer +
'&referer=' + encodeURIComponent(location.href) +
'&session=' + target.getAttribute('data-session');
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status !== 200) {
count.innerText = parseInt(count.innerText) + 1;
var payload = 'csrf_token=' + target.parentNode.querySelector('input[name="csrf_token"]').value;
helpers.xhr('POST', url, {payload: payload}, {
onNon200: function (xhr) {
count.textContent++;
row.style.display = '';
}
}
};
var csrf_token = target.parentNode.querySelector('input[name="csrf_token"]').value;
xhr.send('csrf_token=' + csrf_token);
});
}
function remove_subscription(target) {
var row = target.parentNode.parentNode.parentNode.parentNode.parentNode;
row.style.display = 'none';
var count = document.getElementById('count');
count.innerText = count.innerText - 1;
count.textContent--;
var referer = window.encodeURIComponent(document.location.href);
var url = '/subscription_ajax?action_remove_subscriptions=1&redirect=false' +
'&referer=' + referer +
'&referer=' + encodeURIComponent(location.href) +
'&c=' + target.getAttribute('data-ucid');
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status !== 200) {
count.innerText = parseInt(count.innerText) + 1;
var payload = 'csrf_token=' + target.parentNode.querySelector('input[name="csrf_token"]').value;
helpers.xhr('POST', url, {payload: payload}, {
onNon200: function (xhr) {
count.textContent++;
row.style.display = '';
}
}
};
var csrf_token = target.parentNode.querySelector('input[name="csrf_token"]').value;
xhr.send('csrf_token=' + csrf_token);
});
}
// Handle keypresses
window.addEventListener('keydown', function (event) {
addEventListener('keydown', function (event) {
// Ignore modifier keys
if (event.ctrlKey || event.metaKey) return;
@ -158,7 +137,7 @@
if (focused_tag === 'textarea') return;
if (focused_tag === 'input') {
let focused_type = document.activeElement.type.toLowerCase();
if (!focused_type.match(allowed)) return;
if (!allowed.test(focused_type)) return;
}
// Focus search bar on '/'

@ -1,43 +1,26 @@
'use strict';
var notification_data = JSON.parse(document.getElementById('notification_data').textContent);
var notifications, delivered;
function get_subscriptions(callback, retries) {
if (retries === undefined) retries = 5;
if (retries <= 0) {
return;
}
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', '/api/v1/auth/subscriptions?fields=authorId', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var subscriptions = xhr.response;
callback(subscriptions);
}
}
};
xhr.onerror = function () {
console.warn('Pulling subscriptions failed... ' + retries + '/5');
setTimeout(function () { get_subscriptions(callback, retries - 1); }, 1000);
};
/** Boolean meaning 'some tab have stream' */
const STORAGE_KEY_STREAM = 'stream';
/** Number of notifications. May be increased or reset */
const STORAGE_KEY_NOTIF_COUNT = 'notification_count';
xhr.ontimeout = function () {
console.warn('Pulling subscriptions failed... ' + retries + '/5');
get_subscriptions(callback, retries - 1);
};
xhr.send();
var notifications, delivered;
var notifications_mock = { close: function () { } };
function get_subscriptions() {
helpers.xhr('GET', '/api/v1/auth/subscriptions?fields=authorId', {
retries: 5,
entity_name: 'subscriptions'
}, {
on200: create_notification_stream
});
}
function create_notification_stream(subscriptions) {
// sse.js can't be replaced to EventSource in place as it lack support of payload and headers
// see https://developer.mozilla.org/en-US/docs/Web/API/EventSource/EventSource
notifications = new SSE(
'/api/v1/auth/notifications?fields=videoId,title,author,authorId,publishedText,published,authorThumbnails,liveNow', {
withCredentials: true,
@ -49,96 +32,100 @@ function create_notification_stream(subscriptions) {
var start_time = Math.round(new Date() / 1000);
notifications.onmessage = function (event) {
if (!event.id) {
return;
}
if (!event.id) return;
var notification = JSON.parse(event.data);
console.info('Got notification:', notification);
if (start_time < notification.published && !delivered.includes(notification.videoId)) {
if (Notification.permission === 'granted') {
var system_notification =
new Notification((notification.liveNow ? notification_data.live_now_text : notification_data.upload_text).replace('`x`', notification.author), {
// Ignore not actual and delivered notifications
if (start_time > notification.published || delivered.includes(notification.videoId)) return;
delivered.push(notification.videoId);
let notification_count = helpers.storage.get(STORAGE_KEY_NOTIF_COUNT) || 0;
notification_count++;
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
update_ticker_count();
// permission for notifications handled on settings page. JS handler is in handlers.js
if (window.Notification && Notification.permission === 'granted') {
var notification_text = notification.liveNow ? notification_data.live_now_text : notification_data.upload_text;
notification_text = notification_text.replace('`x`', notification.author);
var system_notification = new Notification(notification_text, {
body: notification.title,
icon: '/ggpht' + new URL(notification.authorThumbnails[2].url).pathname,
img: '/ggpht' + new URL(notification.authorThumbnails[4].url).pathname,
tag: notification.videoId
img: '/ggpht' + new URL(notification.authorThumbnails[4].url).pathname
});
system_notification.onclick = function (event) {
window.open('/watch?v=' + event.currentTarget.tag, '_blank');
system_notification.onclick = function (e) {
open('/watch?v=' + notification.videoId, '_blank');
};
}
};
delivered.push(notification.videoId);
localStorage.setItem('notification_count', parseInt(localStorage.getItem('notification_count') || '0') + 1);
notifications.addEventListener('error', function (e) {
console.warn('Something went wrong with notifications, trying to reconnect...');
notifications = notifications_mock;
setTimeout(get_subscriptions, 1000);
});
notifications.stream();
}
function update_ticker_count() {
var notification_ticker = document.getElementById('notification_ticker');
if (parseInt(localStorage.getItem('notification_count')) > 0) {
const notification_count = helpers.storage.get(STORAGE_KEY_STREAM);
if (notification_count > 0) {
notification_ticker.innerHTML =
'<span id="notification_count">' + localStorage.getItem('notification_count') + '</span> <i class="icon ion-ios-notifications"></i>';
'<span id="notification_count">' + notification_count + '</span> <i class="icon ion-ios-notifications"></i>';
} else {
notification_ticker.innerHTML =
'<i class="icon ion-ios-notifications-outline"></i>';
}
}
};
notifications.addEventListener('error', handle_notification_error);
notifications.stream();
}
function handle_notification_error(event) {
console.warn('Something went wrong with notifications, trying to reconnect...');
notifications = { close: function () { } };
setTimeout(function () { get_subscriptions(create_notification_stream); }, 1000);
function start_stream_if_needed() {
// random wait for other tabs set 'stream' flag
setTimeout(function () {
if (!helpers.storage.get(STORAGE_KEY_STREAM)) {
// if no one set 'stream', set it by yourself and start stream
helpers.storage.set(STORAGE_KEY_STREAM, true);
notifications = notifications_mock;
get_subscriptions();
}
}, Math.random() * 1000 + 50); // [0.050 .. 1.050) second
}
window.addEventListener('load', function (e) {
localStorage.setItem('notification_count', document.getElementById('notification_count') ? document.getElementById('notification_count').innerText : '0');
if (localStorage.getItem('stream')) {
localStorage.removeItem('stream');
} else {
setTimeout(function () {
if (!localStorage.getItem('stream')) {
notifications = { close: function () { } };
localStorage.setItem('stream', true);
get_subscriptions(create_notification_stream);
}
}, Math.random() * 1000 + 50);
}
addEventListener('storage', function (e) {
if (e.key === STORAGE_KEY_NOTIF_COUNT)
update_ticker_count();
window.addEventListener('storage', function (e) {
if (e.key === 'stream' && !e.newValue) {
// if 'stream' key was removed
if (e.key === STORAGE_KEY_STREAM && !helpers.storage.get(STORAGE_KEY_STREAM)) {
if (notifications) {
localStorage.setItem('stream', true);
// restore it if we have active stream
helpers.storage.set(STORAGE_KEY_STREAM, true);
} else {
setTimeout(function () {
if (!localStorage.getItem('stream')) {
notifications = { close: function () { } };
localStorage.setItem('stream', true);
get_subscriptions(create_notification_stream);
start_stream_if_needed();
}
}, Math.random() * 1000 + 50);
}
} else if (e.key === 'notification_count') {
var notification_ticker = document.getElementById('notification_ticker');
});
if (parseInt(e.newValue) > 0) {
notification_ticker.innerHTML =
'<span id="notification_count">' + e.newValue + '</span> <i class="icon ion-ios-notifications"></i>';
} else {
notification_ticker.innerHTML =
'<i class="icon ion-ios-notifications-outline"></i>';
}
}
});
addEventListener('load', function () {
var notification_count_el = document.getElementById('notification_count');
var notification_count = notification_count_el ? parseInt(notification_count_el.textContent) : 0;
helpers.storage.set(STORAGE_KEY_NOTIF_COUNT, notification_count);
if (helpers.storage.get(STORAGE_KEY_STREAM))
helpers.storage.remove(STORAGE_KEY_STREAM);
start_stream_if_needed();
});
window.addEventListener('unload', function (e) {
if (notifications) {
localStorage.removeItem('stream');
}
addEventListener('unload', function () {
// let chance to other tabs to be a streamer via firing 'storage' event
if (notifications) helpers.storage.remove(STORAGE_KEY_STREAM);
});

@ -17,6 +17,7 @@ var options = {
'remainingTimeDisplay',
'Spacer',
'captionsButton',
'audioTrackButton',
'qualitySelector',
'playbackRateMenuButton',
'fullscreenToggle'
@ -42,27 +43,38 @@ embed_url = location.origin + '/embed/' + video_data.id + embed_url.search;
var save_player_pos_key = 'save_player_pos';
videojs.Vhs.xhr.beforeRequest = function(options) {
if (options.uri.indexOf('videoplayback') === -1 && options.uri.indexOf('local=true') === -1) {
options.uri = options.uri + '?local=true';
// set local if requested not videoplayback
if (!options.uri.includes('videoplayback')) {
if (!options.uri.includes('local=true'))
options.uri += '?local=true';
}
return options;
};
var player = videojs('player', options);
player.on('error', () => {
if (video_data.params.quality !== 'dash') {
if (!player.currentSrc().includes("local=true") && !video_data.local_disabled) {
var currentSources = player.currentSources();
for (var i = 0; i < currentSources.length; i++) {
currentSources[i]["src"] += "&local=true"
}
player.src(currentSources)
}
else if (player.error().code === 2 || player.error().code === 4) {
setTimeout(function (event) {
console.log('An error occurred in the player, reloading...');
player.on('error', function () {
if (video_data.params.quality === 'dash') return;
var localNotDisabled = (
!player.currentSrc().includes('local=true') && !video_data.local_disabled
);
var reloadMakesSense = (
player.error().code === MediaError.MEDIA_ERR_NETWORK ||
player.error().code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED
);
if (localNotDisabled) {
// add local=true to all current sources
player.src(player.currentSources().map(function (source) {
source.src += '&local=true';
return source;
}));
} else if (reloadMakesSense) {
setTimeout(function () {
console.warn('An error occurred in the player, reloading...');
// After load() all parameters are reset. Save them
var currentTime = player.currentTime();
var playbackRate = player.playbackRate();
var paused = player.paused();
@ -73,14 +85,12 @@ player.on('error', () => {
player.currentTime(currentTime);
player.playbackRate(playbackRate);
if (!paused) player.play();
}, 10000);
}
}, 5000);
}
});
if (video_data.params.quality == 'dash') {
if (video_data.params.quality === 'dash') {
player.reloadSourceOnError({
errorInterval: 10
});
@ -89,7 +99,7 @@ if (video_data.params.quality == 'dash') {
/**
* Function for add time argument to url
* @param {String} url
* @returns urlWithTimeArg
* @returns {URL} urlWithTimeArg
*/
function addCurrentTimeToURL(url) {
var urlUsed = new URL(url);
@ -112,18 +122,12 @@ var shareOptions = {
description: player_data.description,
image: player_data.thumbnail,
get embedCode() {
return '<iframe id="ivplayer" width="640" height="360" src="' +
addCurrentTimeToURL(embed_url) + '" style="border:none;"></iframe>';
// Single quotes inside here required. HTML inserted as is into value attribute of input
return "<iframe id='ivplayer' width='640' height='360' src='" +
addCurrentTimeToURL(embed_url) + "' style='border:none;'></iframe>";
}
};
const storage = (function () {
try { if (localStorage.length !== -1) return localStorage; }
catch (e) { console.info('No storage available: ' + e); }
return undefined;
})();
if (location.pathname.startsWith('/embed/')) {
var overlay_content = '<h1><a rel="noopener" target="_blank" href="' + location.origin + '/watch?v=' + video_data.id + '">' + player_data.title + '</a></h1>';
player.overlay({
@ -143,11 +147,12 @@ function isMobile() {
}
if (isMobile()) {
player.mobileUi();
player.mobileUi({ touchControls: { seekSeconds: 5 * player.playbackRate() } });
var buttons = ['playToggle', 'volumePanel', 'captionsButton'];
if (video_data.params.quality !== 'dash') buttons.push('qualitySelector');
if (!video_data.params.listen && video_data.params.quality === 'dash') buttons.push('audioTrackButton');
if (video_data.params.listen || video_data.params.quality !== 'dash') buttons.push('qualitySelector');
// Create new control bar object for operation buttons
const ControlBar = videojs.getComponent('controlBar');
@ -162,7 +167,7 @@ if (isMobile()) {
buttons.forEach(function (child) {primary_control_bar.removeChild(child);});
var operations_bar_element = operations_bar.el();
operations_bar_element.className += ' mobile-operations-bar';
operations_bar_element.classList.add('mobile-operations-bar');
player.addChild(operations_bar);
// Playback menu doesn't work when it's initialized outside of the primary control bar
@ -174,7 +179,7 @@ if (isMobile()) {
var share_element = document.getElementsByClassName('vjs-share-control')[0];
operations_bar_element.append(share_element);
if (video_data.params.quality === 'dash') {
if (!video_data.params.listen && video_data.params.quality === 'dash') {
var http_source_selector = document.getElementsByClassName('vjs-http-source-selector vjs-menu-button')[0];
operations_bar_element.append(http_source_selector);
}
@ -220,14 +225,14 @@ player.playbackRate(video_data.params.speed);
* Method for getting the contents of a cookie
*
* @param {String} name Name of cookie
* @returns cookieValue
* @returns {String|null} cookieValue
*/
function getCookieValue(name) {
var value = document.cookie.split(';').filter(function (item) {return item.includes(name + '=');});
return (value.length >= 1)
? value[0].substring((name + '=').length, value[0].length)
: null;
var cookiePrefix = name + '=';
var matchedCookie = document.cookie.split(';').find(function (item) {return item.includes(cookiePrefix);});
if (matchedCookie)
return matchedCookie.replace(cookiePrefix, '');
return null;
}
/**
@ -254,17 +259,19 @@ function updateCookie(newVolume, newSpeed) {
// Set expiration in 2 year
var date = new Date();
date.setTime(date.getTime() + 63115200);
date.setFullYear(date.getFullYear() + 2);
var ipRegex = /^((\d+\.){3}\d+|[A-Fa-f0-9]*:[A-Fa-f0-9:]*:[A-Fa-f0-9:]+)$/;
var domainUsed = window.location.hostname;
var ipRegex = /^((\d+\.){3}\d+|[\dA-Fa-f]*:[\d:A-Fa-f]*:[\d:A-Fa-f]+)$/;
var domainUsed = location.hostname;
// Fix for a bug in FF where the leading dot in the FQDN is not ignored
if (domainUsed.charAt(0) !== '.' && !ipRegex.test(domainUsed) && domainUsed !== 'localhost')
domainUsed = '.' + window.location.hostname;
domainUsed = '.' + location.hostname;
document.cookie = 'PREFS=' + cookieData + '; SameSite=Strict; path=/; domain=' +
domainUsed + '; expires=' + date.toGMTString() + ';';
var secure = location.protocol.startsWith("https") ? " Secure;" : "";
document.cookie = 'PREFS=' + cookieData + '; SameSite=Lax; path=/; domain=' +
domainUsed + '; expires=' + date.toGMTString() + ';' + secure;
video_data.params.volume = volumeValue;
video_data.params.speed = speedValue;
@ -272,6 +279,9 @@ function updateCookie(newVolume, newSpeed) {
player.on('ratechange', function () {
updateCookie(null, player.playbackRate());
if (isMobile()) {
player.mobileUi({ touchControls: { seekSeconds: 5 * player.playbackRate() } });
}
});
player.on('volumechange', function () {
@ -280,7 +290,7 @@ player.on('volumechange', function () {
player.on('waiting', function () {
if (player.playbackRate() > 1 && player.liveTracker.isLive() && player.liveTracker.atLiveEdge()) {
console.info('Player has caught up to source, resetting playbackRate.');
console.info('Player has caught up to source, resetting playbackRate');
player.playbackRate(1);
}
});
@ -292,12 +302,12 @@ if (video_data.premiere_timestamp && Math.round(new Date() / 1000) < video_data.
if (video_data.params.save_player_pos) {
const url = new URL(location);
const hasTimeParam = url.searchParams.has('t');
const remeberedTime = get_video_time();
const rememberedTime = get_video_time();
let lastUpdated = 0;
if(!hasTimeParam) set_seconds_after_start(remeberedTime);
if(!hasTimeParam) set_seconds_after_start(rememberedTime);
const updateTime = function () {
player.on('timeupdate', function () {
const raw = player.currentTime();
const time = Math.floor(raw);
@ -305,9 +315,7 @@ if (video_data.params.save_player_pos) {
save_video_time(time);
lastUpdated = time;
}
};
player.on('timeupdate', updateTime);
});
}
else remove_all_video_times();
@ -347,53 +355,31 @@ if (!video_data.params.listen && video_data.params.quality === 'dash') {
targetQualityLevel = 0;
break;
default:
const targetHeight = Number.parseInt(video_data.params.quality_dash, 10);
const targetHeight = parseInt(video_data.params.quality_dash);
for (let i = 0; i < qualityLevels.length; i++) {
if (qualityLevels[i].height <= targetHeight) {
if (qualityLevels[i].height <= targetHeight)
targetQualityLevel = i;
} else {
else
break;
}
}
}
for (let i = 0; i < qualityLevels.length; i++) {
qualityLevels[i].enabled = (i === targetQualityLevel);
}
qualityLevels.forEach(function (level, index) {
level.enabled = (index === targetQualityLevel);
});
});
});
}
}
player.vttThumbnails({
src: location.origin + '/api/v1/storyboards/' + video_data.id + '?height=90',
src: '/api/v1/storyboards/' + video_data.id + '?height=90',
showTimestamp: true
});
// Enable annotations
if (!video_data.params.listen && video_data.params.annotations) {
window.addEventListener('load', function (e) {
var video_container = document.getElementById('player');
let xhr = new XMLHttpRequest();
xhr.responseType = 'text';
xhr.timeout = 60000;
xhr.open('GET', '/api/v1/annotations/' + video_data.id, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
videojs.registerPlugin('youtubeAnnotationsPlugin', youtubeAnnotationsPlugin);
if (!player.paused()) {
player.youtubeAnnotationsPlugin({ annotationXml: xhr.response, videoContainer: video_container });
} else {
player.one('play', function (event) {
player.youtubeAnnotationsPlugin({ annotationXml: xhr.response, videoContainer: video_container });
});
}
}
}
};
window.addEventListener('__ar_annotation_click', function (e) {
addEventListener('load', function (e) {
addEventListener('__ar_annotation_click', function (e) {
const url = e.detail.url,
target = e.detail.target,
seconds = e.detail.seconds;
@ -406,41 +392,48 @@ if (!video_data.params.listen && video_data.params.annotations) {
path = path.pathname + path.search;
if (target === 'current') {
window.location.href = path;
location.href = path;
} else if (target === 'new') {
window.open(path, '_blank');
open(path, '_blank');
}
});
helpers.xhr('GET', '/api/v1/annotations/' + video_data.id, {
responseType: 'text',
timeout: 60000
}, {
on200: function (response) {
var video_container = document.getElementById('player');
videojs.registerPlugin('youtubeAnnotationsPlugin', youtubeAnnotationsPlugin);
if (player.paused()) {
player.one('play', function (event) {
player.youtubeAnnotationsPlugin({ annotationXml: response, videoContainer: video_container });
});
} else {
player.youtubeAnnotationsPlugin({ annotationXml: response, videoContainer: video_container });
}
}
});
xhr.send();
});
}
function increase_volume(delta) {
function change_volume(delta) {
const curVolume = player.volume();
let newVolume = curVolume + delta;
if (newVolume > 1) {
newVolume = 1;
} else if (newVolume < 0) {
newVolume = 0;
}
newVolume = helpers.clamp(newVolume, 0, 1);
player.volume(newVolume);
}
function toggle_muted() {
const isMuted = player.muted();
player.muted(!isMuted);
player.muted(!player.muted());
}
function skip_seconds(delta) {
const duration = player.duration();
const curTime = player.currentTime();
let newTime = curTime + delta;
if (newTime > duration) {
newTime = duration;
} else if (newTime < 0) {
newTime = 0;
}
newTime = helpers.clamp(newTime, 0, duration);
player.currentTime(newTime);
}
@ -450,57 +443,21 @@ function set_seconds_after_start(delta) {
}
function save_video_time(seconds) {
const videoId = video_data.id;
const all_video_times = get_all_video_times();
all_video_times[videoId] = seconds;
set_all_video_times(all_video_times);
all_video_times[video_data.id] = seconds;
helpers.storage.set(save_player_pos_key, all_video_times);
}
function get_video_time() {
try {
const videoId = video_data.id;
const all_video_times = get_all_video_times();
const timestamp = all_video_times[videoId];
return timestamp || 0;
}
catch (e) {
return 0;
}
}
function set_all_video_times(times) {
if (storage) {
if (times) {
try {
storage.setItem(save_player_pos_key, JSON.stringify(times));
} catch (e) {
console.warn('set_all_video_times: ' + e);
}
} else {
storage.removeItem(save_player_pos_key);
}
}
return get_all_video_times()[video_data.id] || 0;
}
function get_all_video_times() {
if (storage) {
const raw = storage.getItem(save_player_pos_key);
if (raw !== null) {
try {
return JSON.parse(raw);
} catch (e) {
console.warn('get_all_video_times: ' + e);
}
}
}
return {};
return helpers.storage.get(save_player_pos_key) || {};
}
function remove_all_video_times() {
set_all_video_times(null);
helpers.storage.remove(save_player_pos_key);
}
function set_time_percent(percent) {
@ -516,21 +473,23 @@ function toggle_play() { player.paused() ? play() : pause(); }
const toggle_captions = (function () {
let toggledTrack = null;
const onChange = function (e) {
function bindChange(onOrOff) {
player.textTracks()[onOrOff]('change', function (e) {
toggledTrack = null;
};
const bindChange = function (onOrOff) {
player.textTracks()[onOrOff]('change', onChange);
};
});
}
// Wrapper function to ignore our own emitted events and only listen
// to events emitted by Video.js on click on the captions menu items.
const setMode = function (track, mode) {
function setMode(track, mode) {
bindChange('off');
track.mode = mode;
window.setTimeout(function () {
setTimeout(function () {
bindChange('on');
}, 0);
};
}
bindChange('on');
return function () {
if (toggledTrack !== null) {
@ -578,15 +537,11 @@ function increase_playback_rate(steps) {
const maxIndex = options.playbackRates.length - 1;
const curIndex = options.playbackRates.indexOf(player.playbackRate());
let newIndex = curIndex + steps;
if (newIndex > maxIndex) {
newIndex = maxIndex;
} else if (newIndex < 0) {
newIndex = 0;
}
newIndex = helpers.clamp(newIndex, 0, maxIndex);
player.playbackRate(options.playbackRates[newIndex]);
}
window.addEventListener('keydown', function (e) {
addEventListener('keydown', function (e) {
if (e.target.tagName.toLowerCase() === 'input') {
// Ignore input when focus is on certain elements, e.g. form fields.
return;
@ -619,10 +574,10 @@ window.addEventListener('keydown', function (e) {
case 'MediaStop': action = stop; break;
case 'ArrowUp':
if (isPlayerFocused) action = increase_volume.bind(this, 0.1);
if (isPlayerFocused) action = change_volume.bind(this, 0.1);
break;
case 'ArrowDown':
if (isPlayerFocused) action = increase_volume.bind(this, -0.1);
if (isPlayerFocused) action = change_volume.bind(this, -0.1);
break;
case 'm':
@ -673,12 +628,11 @@ window.addEventListener('keydown', function (e) {
// TODO: Add support to play back previous video.
break;
case '.':
// TODO: Add support for next-frame-stepping.
break;
case ',':
// TODO: Add support for previous-frame-stepping.
break;
// TODO: More precise step. Now FPS is taken equal to 29.97
// Common FPS: https://forum.videohelp.com/threads/81868#post323588
// Possible solution is new HTMLVideoElement.requestVideoFrameCallback() https://wicg.github.io/video-rvfc/
case ',': action = function () { pause(); skip_seconds(-1/29.97); }; break;
case '.': action = function () { pause(); skip_seconds( 1/29.97); }; break;
case '>': action = increase_playback_rate.bind(this, 1); break;
case '<': action = increase_playback_rate.bind(this, -1); break;
@ -697,10 +651,6 @@ window.addEventListener('keydown', function (e) {
// Add support for controlling the player volume by scrolling over it. Adapted from
// https://github.com/ctd1500/videojs-hotkeys/blob/bb4a158b2e214ccab87c2e7b95f42bc45c6bfd87/videojs.hotkeys.js#L292-L328
(function () {
const volumeStep = 0.05;
const enableVolumeScroll = true;
const enableHoverScroll = true;
const doc = document;
const pEl = document.getElementById('player');
var volumeHover = false;
@ -710,44 +660,33 @@ window.addEventListener('keydown', function (e) {
volumeSelector.onmouseout = function () { volumeHover = false; };
}
var mouseScroll = function mouseScroll(event) {
var activeEl = doc.activeElement;
if (enableHoverScroll) {
// If we leave this undefined then it can match non-existent elements below
activeEl = 0;
}
function mouseScroll(event) {
// When controls are disabled, hotkeys will be disabled as well
if (player.controls()) {
if (volumeHover) {
if (enableVolumeScroll) {
event = window.event || event;
var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail)));
if (!player.controls() || !volumeHover) return;
event.preventDefault();
var wheelMove = event.wheelDelta || -event.detail;
var volumeSign = Math.sign(wheelMove);
if (delta === 1) {
increase_volume(volumeStep);
} else if (delta === -1) {
increase_volume(-volumeStep);
}
change_volume(volumeSign * 0.05); // decrease/increase by 5%
}
}
}
};
player.on('mousewheel', mouseScroll);
player.on('DOMMouseScroll', mouseScroll);
}());
// Since videojs-share can sometimes be blocked, we defer it until last
if (player.share) {
player.share(shareOptions);
}
if (player.share) player.share(shareOptions);
// show the preferred caption by default
if (player_data.preferred_caption_found) {
player.ready(function () {
if (!video_data.params.listen && video_data.params.quality === 'dash') {
// play.textTracks()[0] on DASH mode is showing some debug messages
player.textTracks()[1].mode = 'showing';
} else {
player.textTracks()[0].mode = 'showing';
}
});
}
@ -763,7 +702,7 @@ if (navigator.vendor === 'Apple Computer, Inc.' && video_data.params.listen) {
}
// Watch on Invidious link
if (window.location.pathname.startsWith('/embed/')) {
if (location.pathname.startsWith('/embed/')) {
const Button = videojs.getComponent('Button');
let watch_on_invidious_button = new Button(player);
@ -778,3 +717,11 @@ if (window.location.pathname.startsWith('/embed/')) {
var cb = player.getChild('ControlBar');
cb.addChild(watch_on_invidious_button);
}
addEventListener('DOMContentLoaded', function () {
// Save time during redirection on another instance
const changeInstanceLink = document.querySelector('#watch-on-another-invidious-instance > a');
if (changeInstanceLink) changeInstanceLink.addEventListener('click', function () {
changeInstanceLink.href = addCurrentTimeToURL(changeInstanceLink.href);
});
});

@ -1,5 +1,6 @@
'use strict';
var playlist_data = JSON.parse(document.getElementById('playlist_data').textContent);
var payload = 'csrf_token=' + playlist_data.csrf_token;
function add_playlist_video(target) {
var select = target.parentNode.children[0].children[1];
@ -8,21 +9,12 @@ function add_playlist_video(target) {
var url = '/playlist_ajax?action_add_video=1&redirect=false' +
'&video_id=' + target.getAttribute('data-id') +
'&playlist_id=' + option.getAttribute('data-plid');
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
option.innerText = '✓' + option.innerText;
helpers.xhr('POST', url, {payload: payload}, {
on200: function (response) {
option.textContent = '✓' + option.textContent;
}
}
};
xhr.send('csrf_token=' + playlist_data.csrf_token);
});
}
function add_playlist_item(target) {
@ -32,21 +24,12 @@ function add_playlist_item(target) {
var url = '/playlist_ajax?action_add_video=1&redirect=false' +
'&video_id=' + target.getAttribute('data-id') +
'&playlist_id=' + target.getAttribute('data-plid');
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status !== 200) {
helpers.xhr('POST', url, {payload: payload}, {
onNon200: function (xhr) {
tile.style.display = '';
}
}
};
xhr.send('csrf_token=' + playlist_data.csrf_token);
});
}
function remove_playlist_item(target) {
@ -56,19 +39,10 @@ function remove_playlist_item(target) {
var url = '/playlist_ajax?action_remove_video=1&redirect=false' +
'&set_video_id=' + target.getAttribute('data-index') +
'&playlist_id=' + target.getAttribute('data-plid');
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status !== 200) {
helpers.xhr('POST', url, {payload: payload}, {
onNon200: function (xhr) {
tile.style.display = '';
}
}
};
xhr.send('csrf_token=' + playlist_data.csrf_token);
});
}

@ -1,8 +1,9 @@
'use strict';
var subscribe_data = JSON.parse(document.getElementById('subscribe_data').textContent);
var payload = 'csrf_token=' + subscribe_data.csrf_token;
var subscribe_button = document.getElementById('subscribe');
subscribe_button.parentNode['action'] = 'javascript:void(0)';
subscribe_button.parentNode.action = 'javascript:void(0)';
if (subscribe_button.getAttribute('data-type') === 'subscribe') {
subscribe_button.onclick = subscribe;
@ -10,87 +11,34 @@ if (subscribe_button.getAttribute('data-type') === 'subscribe') {
subscribe_button.onclick = unsubscribe;
}
function subscribe(retries) {
if (retries === undefined) retries = 5;
if (retries <= 0) {
console.warn('Failed to subscribe.');
return;
}
var url = '/subscription_ajax?action_create_subscription_to_channel=1&redirect=false' +
'&c=' + subscribe_data.ucid;
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
function subscribe() {
var fallback = subscribe_button.innerHTML;
subscribe_button.onclick = unsubscribe;
subscribe_button.innerHTML = '<b>' + subscribe_data.unsubscribe_text + ' | ' + subscribe_data.sub_count_text + '</b>';
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status !== 200) {
var url = '/subscription_ajax?action_create_subscription_to_channel=1&redirect=false' +
'&c=' + subscribe_data.ucid;
helpers.xhr('POST', url, {payload: payload, retries: 5, entity_name: 'subscribe request'}, {
onNon200: function (xhr) {
subscribe_button.onclick = subscribe;
subscribe_button.innerHTML = fallback;
}
}
};
xhr.onerror = function () {
console.warn('Subscribing failed... ' + retries + '/5');
setTimeout(function () { subscribe(retries - 1); }, 1000);
};
xhr.ontimeout = function () {
console.warn('Subscribing failed... ' + retries + '/5');
subscribe(retries - 1);
};
xhr.send('csrf_token=' + subscribe_data.csrf_token);
});
}
function unsubscribe(retries) {
if (retries === undefined)
retries = 5;
if (retries <= 0) {
console.warn('Failed to subscribe');
return;
}
var url = '/subscription_ajax?action_remove_subscriptions=1&redirect=false' +
'&c=' + subscribe_data.ucid;
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
function unsubscribe() {
var fallback = subscribe_button.innerHTML;
subscribe_button.onclick = subscribe;
subscribe_button.innerHTML = '<b>' + subscribe_data.subscribe_text + ' | ' + subscribe_data.sub_count_text + '</b>';
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status !== 200) {
var url = '/subscription_ajax?action_remove_subscriptions=1&redirect=false' +
'&c=' + subscribe_data.ucid;
helpers.xhr('POST', url, {payload: payload, retries: 5, entity_name: 'unsubscribe request'}, {
onNon200: function (xhr) {
subscribe_button.onclick = unsubscribe;
subscribe_button.innerHTML = fallback;
}
}
};
xhr.onerror = function () {
console.warn('Unsubscribing failed... ' + retries + '/5');
setTimeout(function () { unsubscribe(retries - 1); }, 1000);
};
xhr.ontimeout = function () {
console.warn('Unsubscribing failed... ' + retries + '/5');
unsubscribe(retries - 1);
};
xhr.send('csrf_token=' + subscribe_data.csrf_token);
});
}

@ -1,90 +1,46 @@
'use strict';
var toggle_theme = document.getElementById('toggle_theme');
toggle_theme.href = 'javascript:void(0);';
toggle_theme.href = 'javascript:void(0)';
toggle_theme.addEventListener('click', function () {
var dark_mode = document.body.classList.contains('light-theme');
var url = '/toggle_theme?redirect=false';
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', url, true);
set_mode(dark_mode);
try {
window.localStorage.setItem('dark_mode', dark_mode ? 'dark' : 'light');
} catch (e) {}
xhr.send();
});
window.addEventListener('storage', function (e) {
if (e.key === 'dark_mode') {
update_mode(e.newValue);
}
});
const STORAGE_KEY_THEME = 'dark_mode';
const THEME_DARK = 'dark';
const THEME_LIGHT = 'light';
window.addEventListener('DOMContentLoaded', function () {
const dark_mode = document.getElementById('dark_mode_pref').textContent;
try {
// Update localStorage if dark mode preference changed on preferences page
window.localStorage.setItem('dark_mode', dark_mode);
} catch (e) {}
update_mode(dark_mode);
// TODO: theme state controlled by system
toggle_theme.addEventListener('click', function () {
const isDarkTheme = helpers.storage.get(STORAGE_KEY_THEME) === THEME_DARK;
const newTheme = isDarkTheme ? THEME_LIGHT : THEME_DARK;
setTheme(newTheme);
helpers.storage.set(STORAGE_KEY_THEME, newTheme);
helpers.xhr('GET', '/toggle_theme?redirect=false', {}, {});
});
var darkScheme = window.matchMedia('(prefers-color-scheme: dark)');
var lightScheme = window.matchMedia('(prefers-color-scheme: light)');
darkScheme.addListener(scheme_switch);
lightScheme.addListener(scheme_switch);
function scheme_switch (e) {
// ignore this method if we have a preference set
try {
if (localStorage.getItem('dark_mode')) {
return;
}
} catch (exception) {}
if (e.matches) {
if (e.media.includes('dark')) {
set_mode(true);
} else if (e.media.includes('light')) {
set_mode(false);
}
}
}
function set_mode (bool) {
if (bool) {
// dark
toggle_theme.children[0].setAttribute('class', 'icon ion-ios-sunny');
document.body.classList.remove('no-theme');
document.body.classList.remove('light-theme');
document.body.classList.add('dark-theme');
/** @param {THEME_DARK|THEME_LIGHT} theme */
function setTheme(theme) {
// By default body element has .no-theme class that uses OS theme via CSS @media rules
// It rewrites using hard className below
if (theme === THEME_DARK) {
toggle_theme.children[0].className = 'icon ion-ios-sunny';
document.body.className = 'dark-theme';
} else if (theme === THEME_LIGHT) {
toggle_theme.children[0].className = 'icon ion-ios-moon';
document.body.className = 'light-theme';
} else {
// light
toggle_theme.children[0].setAttribute('class', 'icon ion-ios-moon');
document.body.classList.remove('no-theme');
document.body.classList.remove('dark-theme');
document.body.classList.add('light-theme');
document.body.className = 'no-theme';
}
}
function update_mode (mode) {
if (mode === 'true' /* for backwards compatibility */ || mode === 'dark') {
// If preference for dark mode indicated
set_mode(true);
}
else if (mode === 'false' /* for backwards compatibility */ || mode === 'light') {
// If preference for light mode indicated
set_mode(false);
}
else if (document.getElementById('dark_mode_pref').textContent === '' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
// If no preference indicated here and no preference indicated on the preferences page (backend), but the browser tells us that the operating system has a dark theme
set_mode(true);
// Handles theme change event caused by other tab
addEventListener('storage', function (e) {
if (e.key === STORAGE_KEY_THEME)
setTheme(helpers.storage.get(STORAGE_KEY_THEME));
});
// Set theme from preferences on page load
addEventListener('DOMContentLoaded', function () {
const prefTheme = document.getElementById('dark_mode_pref').textContent;
if (prefTheme) {
setTheme(prefTheme);
helpers.storage.set(STORAGE_KEY_THEME, prefTheme);
}
// else do nothing, falling back to the mode defined by the `dark_mode` preference on the preferences page (backend)
}
});

@ -1,5 +1,7 @@
'use strict';
var video_data = JSON.parse(document.getElementById('video_data').textContent);
var spinnerHTML = '<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
var spinnerHTMLwithHR = spinnerHTML + '<hr>';
String.prototype.supplant = function (o) {
return this.replace(/{([^{}]*)}/g, function (a, b) {
@ -10,24 +12,24 @@ String.prototype.supplant = function (o) {
function toggle_parent(target) {
var body = target.parentNode.parentNode.children[1];
if (body.style.display === null || body.style.display === '') {
target.textContent = '[ + ]';
body.style.display = 'none';
} else {
if (body.style.display === 'none') {
target.textContent = '[ ]';
body.style.display = '';
} else {
target.textContent = '[ + ]';
body.style.display = 'none';
}
}
function toggle_comments(event) {
var target = event.target;
var body = target.parentNode.parentNode.parentNode.children[1];
if (body.style.display === null || body.style.display === '') {
target.textContent = '[ + ]';
body.style.display = 'none';
} else {
if (body.style.display === 'none') {
target.textContent = '[ ]';
body.style.display = '';
} else {
target.textContent = '[ + ]';
body.style.display = 'none';
}
}
@ -79,56 +81,31 @@ if (continue_button) {
function next_video() {
var url = new URL('https://example.com/watch?v=' + video_data.next_video);
if (video_data.params.autoplay || video_data.params.continue_autoplay) {
if (video_data.params.autoplay || video_data.params.continue_autoplay)
url.searchParams.set('autoplay', '1');
}
if (video_data.params.listen !== video_data.preferences.listen) {
if (video_data.params.listen !== video_data.preferences.listen)
url.searchParams.set('listen', video_data.params.listen);
}
if (video_data.params.speed !== video_data.preferences.speed) {
if (video_data.params.speed !== video_data.preferences.speed)
url.searchParams.set('speed', video_data.params.speed);
}
if (video_data.params.local !== video_data.preferences.local) {
if (video_data.params.local !== video_data.preferences.local)
url.searchParams.set('local', video_data.params.local);
}
url.searchParams.set('continue', '1');
location.assign(url.pathname + url.search);
}
function continue_autoplay(event) {
if (event.target.checked) {
player.on('ended', function () {
next_video();
});
player.on('ended', next_video);
} else {
player.off('ended');
}
}
function number_with_separator(val) {
while (/(\d+)(\d{3})/.test(val.toString())) {
val = val.toString().replace(/(\d+)(\d{3})/, '$1' + ',' + '$2');
}
return val;
}
function get_playlist(plid, retries) {
if (retries === undefined) retries = 5;
function get_playlist(plid) {
var playlist = document.getElementById('playlist');
if (retries <= 0) {
console.warn('Failed to pull playlist');
playlist.innerHTML = '';
return;
}
playlist.innerHTML = ' \
<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3> \
<hr>';
playlist.innerHTML = spinnerHTMLwithHR;
var plid_url;
if (plid.startsWith('RD')) {
@ -142,99 +119,62 @@ function get_playlist(plid, retries) {
'&format=html&hl=' + video_data.preferences.locale;
}
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', plid_url, true);
helpers.xhr('GET', plid_url, {retries: 5, entity_name: 'playlist'}, {
on200: function (response) {
playlist.innerHTML = response.playlistHtml;
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
playlist.innerHTML = xhr.response.playlistHtml;
var nextVideo = document.getElementById(xhr.response.nextVideo);
if (!response.nextVideo) return;
var nextVideo = document.getElementById(response.nextVideo);
nextVideo.parentNode.parentNode.scrollTop = nextVideo.offsetTop;
if (xhr.response.nextVideo) {
player.on('ended', function () {
var url = new URL('https://example.com/watch?v=' + xhr.response.nextVideo);
var url = new URL('https://example.com/watch?v=' + response.nextVideo);
url.searchParams.set('list', plid);
if (!plid.startsWith('RD')) {
url.searchParams.set('index', xhr.response.index);
}
if (video_data.params.autoplay || video_data.params.continue_autoplay) {
if (!plid.startsWith('RD'))
url.searchParams.set('index', response.index);
if (video_data.params.autoplay || video_data.params.continue_autoplay)
url.searchParams.set('autoplay', '1');
}
if (video_data.params.listen !== video_data.preferences.listen) {
if (video_data.params.listen !== video_data.preferences.listen)
url.searchParams.set('listen', video_data.params.listen);
}
if (video_data.params.speed !== video_data.preferences.speed) {
if (video_data.params.speed !== video_data.preferences.speed)
url.searchParams.set('speed', video_data.params.speed);
}
if (video_data.params.local !== video_data.preferences.local) {
if (video_data.params.local !== video_data.preferences.local)
url.searchParams.set('local', video_data.params.local);
}
location.assign(url.pathname + url.search);
});
}
} else {
},
onNon200: function (xhr) {
playlist.innerHTML = '';
document.getElementById('continue').style.display = '';
},
onError: function (xhr) {
playlist.innerHTML = spinnerHTMLwithHR;
},
onTimeout: function (xhr) {
playlist.innerHTML = spinnerHTMLwithHR;
}
}
};
xhr.onerror = function () {
playlist = document.getElementById('playlist');
playlist.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3><hr>';
console.warn('Pulling playlist timed out... ' + retries + '/5');
setTimeout(function () { get_playlist(plid, retries - 1); }, 1000);
};
xhr.ontimeout = function () {
playlist = document.getElementById('playlist');
playlist.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3><hr>';
console.warn('Pulling playlist timed out... ' + retries + '/5');
get_playlist(plid, retries - 1);
};
xhr.send();
});
}
function get_reddit_comments(retries) {
if (retries === undefined) retries = 5;
function get_reddit_comments() {
var comments = document.getElementById('comments');
if (retries <= 0) {
console.warn('Failed to pull comments');
comments.innerHTML = '';
return;
}
var fallback = comments.innerHTML;
comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
comments.innerHTML = spinnerHTML;
var url = '/api/v1/comments/' + video_data.id +
'?source=reddit&format=html' +
'&hl=' + video_data.preferences.locale;
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var onNon200 = function (xhr) { comments.innerHTML = fallback; };
if (video_data.params.comments[1] === 'youtube')
onNon200 = function (xhr) {};
helpers.xhr('GET', url, {retries: 5, entity_name: ''}, {
on200: function (response) {
comments.innerHTML = ' \
<div> \
<h3> \
@ -254,65 +194,37 @@ function get_reddit_comments(retries) {
</div> \
<div>{contentHtml}</div> \
<hr>'.supplant({
title: xhr.response.title,
title: response.title,
youtubeCommentsText: video_data.youtube_comments_text,
redditPermalinkText: video_data.reddit_permalink_text,
permalink: xhr.response.permalink,
contentHtml: xhr.response.contentHtml
permalink: response.permalink,
contentHtml: response.contentHtml
});
comments.children[0].children[0].children[0].onclick = toggle_comments;
comments.children[0].children[1].children[0].onclick = swap_comments;
} else {
if (video_data.params.comments[1] === 'youtube') {
console.warn('Pulling comments failed... ' + retries + '/5');
setTimeout(function () { get_youtube_comments(retries - 1); }, 1000);
} else {
comments.innerHTML = fallback;
}
}
}
};
xhr.onerror = function () {
console.warn('Pulling comments failed... ' + retries + '/5');
setTimeout(function () { get_reddit_comments(retries - 1); }, 1000);
};
xhr.ontimeout = function () {
console.warn('Pulling comments failed... ' + retries + '/5');
get_reddit_comments(retries - 1);
};
xhr.send();
},
onNon200: onNon200, // declared above
});
}
function get_youtube_comments(retries) {
if (retries === undefined) retries = 5;
function get_youtube_comments() {
var comments = document.getElementById('comments');
if (retries <= 0) {
console.warn('Failed to pull comments');
comments.innerHTML = '';
return;
}
var fallback = comments.innerHTML;
comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
comments.innerHTML = spinnerHTML;
var url = '/api/v1/comments/' + video_data.id +
'?format=html' +
'&hl=' + video_data.preferences.locale +
'&thin_mode=' + video_data.preferences.thin_mode;
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var onNon200 = function (xhr) { comments.innerHTML = fallback; };
if (video_data.params.comments[1] === 'youtube')
onNon200 = function (xhr) {};
helpers.xhr('GET', url, {retries: 5, entity_name: 'comments'}, {
on200: function (response) {
comments.innerHTML = ' \
<div> \
<h3> \
@ -327,40 +239,28 @@ function get_youtube_comments(retries) {
</div> \
<div>{contentHtml}</div> \
<hr>'.supplant({
contentHtml: xhr.response.contentHtml,
contentHtml: response.contentHtml,
redditComments: video_data.reddit_comments_text,
commentsText: video_data.comments_text.supplant(
{ commentCount: number_with_separator(xhr.response.commentCount) }
)
commentsText: video_data.comments_text.supplant({
// toLocaleString correctly splits number with local thousands separator. e.g.:
// '1,234,567.89' for user with English locale
// '1 234 567,89' for user with Russian locale
// '1.234.567,89' for user with Portuguese locale
commentCount: response.commentCount.toLocaleString()
})
});
comments.children[0].children[0].children[0].onclick = toggle_comments;
comments.children[0].children[1].children[0].onclick = swap_comments;
} else {
if (video_data.params.comments[1] === 'youtube') {
setTimeout(function () { get_youtube_comments(retries - 1); }, 1000);
} else {
comments.innerHTML = '';
}
}
},
onNon200: onNon200, // declared above
onError: function (xhr) {
comments.innerHTML = spinnerHTML;
},
onTimeout: function (xhr) {
comments.innerHTML = spinnerHTML;
}
};
xhr.onerror = function () {
comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
console.warn('Pulling comments failed... ' + retries + '/5');
setTimeout(function () { get_youtube_comments(retries - 1); }, 1000);
};
xhr.ontimeout = function () {
comments.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
console.warn('Pulling comments failed... ' + retries + '/5');
get_youtube_comments(retries - 1);
};
xhr.send();
});
}
function get_youtube_replies(target, load_more, load_replies) {
@ -368,29 +268,21 @@ function get_youtube_replies(target, load_more, load_replies) {
var body = target.parentNode.parentNode;
var fallback = body.innerHTML;
body.innerHTML =
'<h3 style="text-align:center"><div class="loading"><i class="icon ion-ios-refresh"></i></div></h3>';
body.innerHTML = spinnerHTML;
var url = '/api/v1/comments/' + video_data.id +
'?format=html' +
'&hl=' + video_data.preferences.locale +
'&thin_mode=' + video_data.preferences.thin_mode +
'&continuation=' + continuation;
if (load_replies) {
url += '&action=action_get_comment_replies';
}
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
if (load_replies) url += '&action=action_get_comment_replies';
helpers.xhr('GET', url, {}, {
on200: function (response) {
if (load_more) {
body = body.parentNode.parentNode;
body.removeChild(body.lastElementChild);
body.innerHTML += xhr.response.contentHtml;
body.insertAdjacentHTML('beforeend', response.contentHtml);
} else {
body.removeChild(body.lastElementChild);
@ -402,57 +294,46 @@ function get_youtube_replies(target, load_more, load_replies) {
a.onclick = hide_youtube_replies;
a.setAttribute('data-sub-text', video_data.hide_replies_text);
a.setAttribute('data-inner-text', video_data.show_replies_text);
a.innerText = video_data.hide_replies_text;
a.textContent = video_data.hide_replies_text;
var div = document.createElement('div');
div.innerHTML = xhr.response.contentHtml;
div.innerHTML = response.contentHtml;
body.appendChild(p);
body.appendChild(div);
}
} else {
},
onNon200: function (xhr) {
body.innerHTML = fallback;
}
}
};
xhr.ontimeout = function () {
console.warn('Pulling comments failed.');
},
onTimeout: function (xhr) {
console.warn('Pulling comments failed');
body.innerHTML = fallback;
};
xhr.send();
}
});
}
if (video_data.play_next) {
player.on('ended', function () {
var url = new URL('https://example.com/watch?v=' + video_data.next_video);
if (video_data.params.autoplay || video_data.params.continue_autoplay) {
if (video_data.params.autoplay || video_data.params.continue_autoplay)
url.searchParams.set('autoplay', '1');
}
if (video_data.params.listen !== video_data.preferences.listen) {
if (video_data.params.listen !== video_data.preferences.listen)
url.searchParams.set('listen', video_data.params.listen);
}
if (video_data.params.speed !== video_data.preferences.speed) {
if (video_data.params.speed !== video_data.preferences.speed)
url.searchParams.set('speed', video_data.params.speed);
}
if (video_data.params.local !== video_data.preferences.local) {
if (video_data.params.local !== video_data.preferences.local)
url.searchParams.set('local', video_data.params.local);
}
url.searchParams.set('continue', '1');
location.assign(url.pathname + url.search);
});
}
window.addEventListener('load', function (e) {
if (video_data.plid) {
addEventListener('load', function (e) {
if (video_data.plid)
get_playlist(video_data.plid);
}
if (video_data.params.comments[0] === 'youtube') {
get_youtube_comments();

@ -0,0 +1,24 @@
'use strict';
var save_player_pos_key = 'save_player_pos';
function get_all_video_times() {
return helpers.storage.get(save_player_pos_key) || {};
}
document.querySelectorAll('.watched-indicator').forEach(function (indicator) {
var watched_part = get_all_video_times()[indicator.dataset.id];
var total = parseInt(indicator.dataset.length, 10);
if (watched_part === undefined) {
watched_part = total;
}
var percentage = Math.round((watched_part / total) * 100);
if (percentage < 5) {
percentage = 5;
}
if (percentage > 90) {
percentage = 100;
}
indicator.style.width = percentage + '%';
});

@ -1,5 +1,6 @@
'use strict';
var watched_data = JSON.parse(document.getElementById('watched_data').textContent);
var payload = 'csrf_token=' + watched_data.csrf_token;
function mark_watched(target) {
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
@ -7,45 +8,27 @@ function mark_watched(target) {
var url = '/watch_ajax?action_mark_watched=1&redirect=false' +
'&id=' + target.getAttribute('data-id');
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status !== 200) {
helpers.xhr('POST', url, {payload: payload}, {
onNon200: function (xhr) {
tile.style.display = '';
}
}
};
xhr.send('csrf_token=' + watched_data.csrf_token);
});
}
function mark_unwatched(target) {
var tile = target.parentNode.parentNode.parentNode.parentNode.parentNode;
tile.style.display = 'none';
var count = document.getElementById('count');
count.innerText = count.innerText - 1;
count.textContent--;
var url = '/watch_ajax?action_mark_unwatched=1&redirect=false' +
'&id=' + target.getAttribute('data-id');
var xhr = new XMLHttpRequest();
xhr.responseType = 'json';
xhr.timeout = 10000;
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status !== 200) {
count.innerText = count.innerText - 1 + 2;
helpers.xhr('POST', url, {payload: payload}, {
onNon200: function (xhr) {
count.textContent++;
tile.style.display = '';
}
}
};
xhr.send('csrf_token=' + watched_data.csrf_token);
});
}

@ -255,8 +255,7 @@ https_only: false
#registration_enabled: true
##
## Allow/Forbid users to log-in. This setting affects the ability
## to connect with BOTH Google and Invidious (local) accounts.
## Allow/Forbid users to log-in.
##
## Accepted values: true, false
## Default: true
@ -295,6 +294,17 @@ https_only: false
##
#admins: [""]
##
## Enable/Disable the user notifications for all users
##
## Note: On large instances, it is recommended to set this option to 'false'
## in order to reduce the amount of data written to the database, and hence
## improve the overall performance of the instance.
##
## Accepted values: true, false
## Default: true
##
#enable_user_notifications: true
# -----------------------------
# Background jobs
@ -304,10 +314,8 @@ https_only: false
## Number of threads to use when crawling channel videos (during
## subscriptions update).
##
## Notes:
## - Setting this to 0 will disable the channel videos crawl job.
## - This setting is overridden if "-c THREADS" or
## "--channel-threads=THREADS" are passed on the command line.
## Notes: This setting is overridden if either "-c THREADS" or
## "--channel-threads=THREADS" is passed on the command line.
##
## Accepted values: a positive integer
## Default: 1
@ -335,10 +343,8 @@ full_refresh: false
##
## Number of threads to use when updating RSS feeds.
##
## Notes:
## - Setting this to 0 will disable the channel videos crawl job.
## - This setting is overridden if "-f THREADS" or
## "--feed-threads=THREADS" are passed on the command line.
## Notes: This setting is overridden if either "-f THREADS" or
## "--feed-threads=THREADS" is passed on the command line.
##
## Accepted values: a positive integer
## Default: 1
@ -349,13 +355,49 @@ feed_threads: 1
## Enable/Disable the polling job that keeps the decryption
## function (for "secured" videos) up to date.
##
## Note: This part of the code is currently broken, so changing
## Note: This part of the code generate a small amount of data every minute.
## This may not be desired if you have bandwidth limits set by your ISP.
##
## Note 2: This part of the code is currently broken, so changing
## this setting has no impact.
##
## Accepted values: true, false
## Default: true
## Default: false
##
#decrypt_polling: true
#decrypt_polling: false
jobs:
## Options for the database cleaning job
clear_expired_items:
## Enable/Disable job
##
## Accepted values: true, false
## Default: true
##
enable: true
## Options for the channels updater job
refresh_channels:
## Enable/Disable job
##
## Accepted values: true, false
## Default: true
##
enable: true
## Options for the RSS feeds updater job
refresh_feeds:
## Enable/Disable job
##
## Accepted values: true, false
## Default: true
##
enable: true
# -----------------------------
@ -450,7 +492,13 @@ feed_threads: 1
##
#modified_source_code_url: ""
##
## Maximum custom playlist length limit.
##
## Accepted values: Integer
## Default: 500
##
#playlist_length_limit: 500
#########################################
#
@ -575,10 +623,10 @@ default_user_preferences:
##
## Enable/Disable dark mode.
##
## Accepted values: true, false
## Default: <none>
## Accepted values: "dark", "light", "auto"
## Default: "auto"
##
#dark_mode:
#dark_mode: "auto"
##
## Enable/Disable thin mode (no video thumbnails).
@ -769,6 +817,16 @@ default_user_preferences:
##
#vr_mode: true
##
## Save the playback position
## Allow to continue watching at the previous position when
## watching the same video.
##
## Accepted values: true, false
## Default: false
##
#save_player_pos: false
# -----------------------------
# Subscription feed
# -----------------------------

@ -2,7 +2,7 @@
# Using it will build an image from the locally cloned repository.
#
# If you want to use Invidious in production, see the docker-compose.yml file provided
# in the installation documentation: https://docs.invidious.io/Installation.md
# in the installation documentation: https://docs.invidious.io/installation/
version: "3"
services:

@ -2,6 +2,7 @@ FROM crystallang/crystal:1.4.1-alpine AS builder
RUN apk add --no-cache sqlite-static yaml-static
ARG release
ARG disable_quic
WORKDIR /invidious
COPY ./shard.yml ./shard.yml
@ -23,7 +24,13 @@ COPY ./videojs-dependencies.yml ./videojs-dependencies.yml
RUN crystal spec --warnings all \
--link-flags "-lxml2 -llzma"
RUN if [ "${release}" == 1 ] ; then \
RUN if [[ "${release}" == 1 && "${disable_quic}" == 1 ]] ; then \
crystal build ./src/invidious.cr \
--release \
-Ddisable_quic \
--static --warnings all \
--link-flags "-lxml2 -llzma"; \
elif [[ "${release}" == 1 ]] ; then \
crystal build ./src/invidious.cr \
--release \
--static --warnings all \
@ -35,8 +42,8 @@ RUN if [ "${release}" == 1 ] ; then \
fi
FROM alpine:latest
RUN apk add --no-cache librsvg ttf-opensans
FROM alpine:3.16
RUN apk add --no-cache librsvg ttf-opensans tini
WORKDIR /invidious
RUN addgroup -g 1000 -S invidious && \
adduser -u 1000 -S invidious -G invidious
@ -51,4 +58,5 @@ RUN chmod o+rX -R ./assets ./config ./locales
EXPOSE 3000
USER invidious
ENTRYPOINT ["/sbin/tini", "--"]
CMD [ "/invidious/invidious" ]

@ -1,7 +1,8 @@
FROM alpine:edge AS builder
FROM alpine:3.16 AS builder
RUN apk add --no-cache 'crystal=1.4.1-r0' shards sqlite-static yaml-static yaml-dev libxml2-dev zlib-static openssl-libs-static openssl-dev musl-dev
ARG release
ARG disable_quic
WORKDIR /invidious
COPY ./shard.yml ./shard.yml
@ -23,7 +24,13 @@ COPY ./videojs-dependencies.yml ./videojs-dependencies.yml
RUN crystal spec --warnings all \
--link-flags "-lxml2 -llzma"
RUN if [ ${release} == 1 ] ; then \
RUN if [[ "${release}" == 1 && "${disable_quic}" == 1 ]] ; then \
crystal build ./src/invidious.cr \
--release \
-Ddisable_quic \
--static --warnings all \
--link-flags "-lxml2 -llzma"; \
elif [[ "${release}" == 1 ]] ; then \
crystal build ./src/invidious.cr \
--release \
--static --warnings all \
@ -34,8 +41,8 @@ RUN if [ ${release} == 1 ] ; then \
--link-flags "-lxml2 -llzma"; \
fi
FROM alpine:edge
RUN apk add --no-cache librsvg ttf-opensans
FROM alpine:3.16
RUN apk add --no-cache librsvg ttf-opensans tini
WORKDIR /invidious
RUN addgroup -g 1000 -S invidious && \
adduser -u 1000 -S invidious -G invidious
@ -50,4 +57,5 @@ RUN chmod o+rX -R ./assets ./config ./locales
EXPOSE 3000
USER invidious
ENTRYPOINT ["/sbin/tini", "--"]
CMD [ "/invidious/invidious" ]

@ -1,6 +1,6 @@
dependencies:
- name: postgresql
repository: https://charts.bitnami.com/bitnami/
version: 11.1.3
digest: sha256:79061645472b6fb342d45e8e5b3aacd018ef5067193e46a060bccdc99fe7f6e1
generated: "2022-03-02T05:57:20.081432389+13:00"
version: 12.1.9
digest: sha256:71ff342a6c0a98bece3d7fe199983afb2113f8db65a3e3819de875af2c45add7
generated: "2023-01-20T20:42:32.757707004Z"

@ -17,6 +17,6 @@ maintainers:
email: mail@leonklingele.de
dependencies:
- name: postgresql
version: ~11.1.3
version: ~12.1.6
repository: "https://charts.bitnami.com/bitnami/"
engine: gotpl

@ -35,7 +35,7 @@ securityContext:
# See https://github.com/bitnami/charts/tree/master/bitnami/postgresql
postgresql:
image:
registry: quay.io
tag: 13
auth:
username: kemal
password: kemal

@ -1,11 +1,11 @@
{
"LIVE": "مُباشِر",
"Shared `x` ago": "تمَّ رفع المقطع المرئيّ مُنذ `x`",
"Shared `x` ago": "تمَّ الرفع مُنذ `x`",
"Unsubscribe": "إلغاء الاشتراك",
"Subscribe": "الإشتراك",
"View channel on YouTube": "زيارة القناة على موقع يوتيوب",
"View playlist on YouTube": "عرض قائمة التشغيل على اليوتيوب",
"newest": "الأجدد",
"Subscribe": "الاشتراك",
"View channel on YouTube": "زيارة القناة على يوتيوب",
"View playlist on YouTube": "عرض قائمة التشغيل على يوتيوب",
"newest": "الأحدث",
"oldest": "الأقدم",
"popular": "الأكثر شعبية",
"last": "الأخيرة",
@ -14,7 +14,6 @@
"Clear watch history?": "هل تريد محو سجل المشاهدة؟",
"New password": "كلمة مرور جديدة",
"New passwords must match": "يَجبُ أن تكون كلمتا المرور متطابقتين",
"Cannot change password for Google accounts": "لا يُمكن تغيير كلمة المرور لِحسابات جوجل",
"Authorize token?": "رمز التفويض؟",
"Authorize token for `x`?": "السماح بالرمز المميز ل 'x'؟",
"Yes": "نعم",
@ -37,7 +36,6 @@
"source": "المصدر",
"Log in": "تسجيل الدخول",
"Log in/register": "تسجيل الدخول \\ إنشاء حساب",
"Log in with Google": "تسجيل الدخول باستخدام جوجل",
"User ID": "مُعرِّف المُستخدم",
"Password": "كلمة المرور",
"Time (h:mm:ss):": "الوقت (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "تسجيل الدخول",
"Register": "التسجيل",
"E-mail": "البريد الإلكتروني",
"Google verification code": "رمز تحقق جوجل",
"Preferences": "الإعدادات",
"preferences_category_player": "إعدادات المُشغِّل",
"preferences_video_loop_label": "كرر المقطع المرئيّ دائما: ",
@ -96,8 +93,8 @@
"`x` is live": "`x` في بث مباشر",
"preferences_category_data": "إعدادات التفضيلات",
"Clear watch history": "حذف سجل المشاهدة",
"Import/export data": ضافة\\استخراج البيانات",
"Change password": "غير كلمة السر",
"Import/export data": ستيراد و تصدير البيانات",
"Change password": "تغير كلمة السر",
"Manage subscriptions": "إدارة الاشتراكات",
"Manage tokens": "إدارة الرموز",
"Watch history": "سجل المشاهدة",
@ -137,7 +134,7 @@
"Title": "العنوان",
"Playlist privacy": "إعدادات الخصوصية",
"Editing playlist `x`": "تعديل قائمة التشغيل `x`",
"Show more": "إظهار المزيد",
"Show more": "عرض المزيد",
"Show less": "عرض اقل",
"Watch on YouTube": "مشاهدة الفيديو على اليوتيوب",
"Switch Invidious Instance": "تبديل المثيل Invidious",
@ -147,34 +144,29 @@
"License: ": "التراخيص: ",
"Family friendly? ": "محتوى عائلي؟ ",
"Wilson score: ": "درجة ويلسون: ",
"Engagement: ": "نسبة المشاركة: ",
"Engagement: ": "نسبة التفاعل: ",
"Whitelisted regions: ": "الدول المسموح فيها هذا الفيديو: ",
"Blacklisted regions: ": "الدول المحظور فيها هذا الفيديو: ",
"Shared `x`": "شارك منذ `x`",
"Shared `x`": "تمت المشاركة في `x`",
"Premieres in `x`": "يعرض فى `x`",
"Premieres `x`": "يعرض `x`",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "أهلًا! يبدو أن جافاسكريبت معطلٌ لديك. اضغط هنا لعرض التعليقات، وَضَع في اعتبارك أنها ستأخذ وقتًا أطول للتحميل.",
"View YouTube comments": "عرض تعليقات اليوتيوب",
"View more comments on Reddit": "عرض المزيد من التعليقات على\\من موقع Reddit",
"View more comments on Reddit": "عرض المزيد من التعليقات على\\من موقع ريديت",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "عرض `x` تعليقات",
"": "عرض `x` تعليقات"
},
"View Reddit comments": "عرض تعليقات ريدإت Reddit",
"View Reddit comments": "عرض تعليقات ريديت",
"Hide replies": "إخفاء الردود",
"Show replies": "عرض الردود",
"Incorrect password": "كلمة السر غير صحيحة",
"Quota exceeded, try again in a few hours": "تم تجاوز عدد المرات المسموح بها، حاول مجددًا بعد بضع ساعات",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "غير قادر على تسجيل الدخول، تأكد من تشغيل المصادقة الثنائية 2FA.",
"Invalid TFA code": "كود مصادقة ثنائية 2FA غير صحيح",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "فشل تسجيل الدخول. قد يكون هذا بسبب أن المصادقة الثنائية 2FA معطلة في حسابك.",
"Wrong answer": "إجابة خاطئة",
"Erroneous CAPTCHA": "الكابتشا CAPTCHA غير صاحلة",
"CAPTCHA is a required field": "مكان الكابتشا CAPTCHA مطلوب",
"User ID is a required field": "مكان اسم المستخدم مطلوب",
"Password is a required field": "مكان كلمة السر مطلوب",
"Wrong username or password": "اسم المستخدم او كلمة السر غير صحيح",
"Please sign in using 'Log in with Google'": "الرجاء تسجيل الدخول باستخدام \"تسجيل الدخول باستخدام Google\"",
"Password cannot be empty": "لا يمكن أن تكون كلمة السر فارغة",
"Password cannot be longer than 55 characters": "يجب أن لا تتعدى كلمة السر 55 حرفًا",
"Please log in": "الرجاء تسجيل الدخول",
@ -182,20 +174,20 @@
"channel:`x`": "قناة:`x`",
"Deleted or invalid channel": "قناة ممسوحة او غير صالحة",
"This channel does not exist.": "هذه القناة غير موجودة.",
"Could not get channel info.": "لم يستطع الحصول على معلومات القناة.",
"Could not fetch comments": م يتمكن من إحضار التعليقات",
"Could not get channel info.": "لم يتمكن الحصول على معلومات القناة.",
"Could not fetch comments": ا يتمكن إحضار التعليقات",
"`x` ago": "`x` منذ",
"Load more": "عرض المزيد",
"Load more": "تحميل المزيد",
"Could not create mix.": "تعذر إنشاء مزيج.",
"Empty playlist": "قائمة التشغيل فارغة",
"Not a playlist.": "قائمة التشغيل غير صالحة.",
"Playlist does not exist.": "قائمة التشغيل غير موجودة.",
"Could not pull trending pages.": م يستطع عرض الصفحات الراجئة.",
"Hidden field \"challenge\" is a required field": "مكان مخفي \"تحدي\" مكان مطلوب",
"Hidden field \"token\" is a required field": "مكان مخفي \"رمز\" مكان مطلوب",
"Erroneous challenge": "تحدي غير صالح",
"Could not pull trending pages.": ا يتمكن عرض الصفحات الراجئة.",
"Hidden field \"challenge\" is a required field": "الحقل المخفي \"تحدي\" حقل مطلوب",
"Hidden field \"token\" is a required field": "الحقل المخفي \"رمز\" حقل مطلوب",
"Erroneous challenge": "تحدي خاطئ",
"Erroneous token": "رمز مميز خاطئ",
"No such user": "مستخدم غير صالح",
"No such user": "مستخدم غير موجود",
"Token is expired, please try again": "الرمز منتهى الصلاحية، الرجاء المحاولة مرة اخرى",
"English": "إنجليزي",
"English (auto-generated)": "إنجليزي (تم إنشائه تلقائيًا)",
@ -325,15 +317,15 @@
"`x` marked it with a ❤": "`x` أعجب بهذا",
"Audio mode": "الوضع الصوتي",
"Video mode": "وضع الفيديو",
"Videos": "الفيديوهات",
"channel_tab_videos_label": "الفيديوهات",
"Playlists": "قوائم التشغيل",
"Community": "المجتمع",
"search_filters_sort_option_relevance": "ملاؤم",
"channel_tab_community_label": "المجتمع",
"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_duration_label": "المدة",
"search_filters_features_label": "الميزات",
"search_filters_sort_label": "فرز",
"search_filters_date_option_hour": "آخر ساعة",
@ -351,8 +343,8 @@
"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_four_k": "4K",
"search_filters_features_option_location": "المكان",
"search_filters_features_option_hdr": "وضع التباين العالي",
"Current version: ": "الإصدار الحالي: ",
"next_steps_error_message": "بعد ذلك يجب أن تحاول: ",
@ -360,15 +352,15 @@
"next_steps_error_message_go_to_youtube": "انتقل إلى يوتيوب",
"search_filters_duration_option_short": "قصير (< 4 دقائق)",
"search_filters_duration_option_long": "طويل (> 20 دقيقة)",
"footer_source_code": "شفرة المصدر",
"footer_original_source_code": "كود المصدر الأصلي",
"footer_modfied_source_code": "شفرة المصدر المعدلة",
"adminprefs_modified_source_code_url_label": "URL إلى مستودع التعليمات البرمجية المصدرية المعدلة",
"footer_source_code": "الكود المصدر",
"footer_original_source_code": "الكود المصدر الأصلي",
"footer_modfied_source_code": "الكود المصدر المعدل",
"adminprefs_modified_source_code_url_label": "URL إلى مستودع الكود المصدر المعدل",
"footer_documentation": "التوثيق",
"footer_donate_page": "تبرّع",
"preferences_region_label": "بلد المحتوى: ",
"preferences_quality_dash_label": "جودة فيديو DASH المفضلة: ",
"preferences_quality_option_dash": "DASH (جودة تكييفية)",
"preferences_quality_option_dash": "DASH (الجودة التلقائية)",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "متوسطة",
"preferences_quality_option_small": "صغيرة",
@ -398,31 +390,31 @@
"invidious": "الخيالي",
"preferences_save_player_pos_label": "حفظ موضع التشغيل: ",
"crash_page_you_found_a_bug": "يبدو أنك قد وجدت خطأً برمجيًّا في Invidious!",
"generic_videos_count_0": "لا فيديوهات",
"generic_videos_count_0": "لا يوجد فيديوهات",
"generic_videos_count_1": "فيديو واحد",
"generic_videos_count_2": "فيديوهين",
"generic_videos_count_3": "{{count}} فيديوهات",
"generic_videos_count_4": "{{count}} فيديو",
"generic_videos_count_5": "{{count}} فيديو",
"generic_subscribers_count_0": "لا مشتركين",
"generic_subscribers_count_0": "لا يوجد مشترك",
"generic_subscribers_count_1": "مشترك واحد",
"generic_subscribers_count_2": "مشتركان",
"generic_subscribers_count_3": "{{count}} مشتركين",
"generic_subscribers_count_4": "{{count}} مشترك",
"generic_subscribers_count_5": "{{count}} مشترك",
"generic_views_count_0": "لا مشاهدات",
"generic_views_count_0": "لا يوجد مشاهدة",
"generic_views_count_1": "مشاهدة واحدة",
"generic_views_count_2": "مشاهدتان",
"generic_views_count_3": "{{count}} مشاهدات",
"generic_views_count_4": "{{count}} مشاهدة",
"generic_views_count_5": "{{count}} مشاهدة",
"generic_subscriptions_count_0": "لا اشتراكات",
"generic_subscriptions_count_0": "لا يوجد اشتراك",
"generic_subscriptions_count_1": "اشتراك واحد",
"generic_subscriptions_count_2": "اشتراكان",
"generic_subscriptions_count_3": "{{count}} اشتراكات",
"generic_subscriptions_count_4": "{{count}} اشتراك",
"generic_subscriptions_count_5": "{{count}} اشتراك",
"generic_playlists_count_0": "لا قوائم تشغيل",
"generic_playlists_count_0": "لا يوجد قوائم تشغيل",
"generic_playlists_count_1": "قائمة تشغيل واحدة",
"generic_playlists_count_2": "قائمتا تشغيل",
"generic_playlists_count_3": "{{count}} قوائم تشغيل",
@ -459,5 +451,94 @@
"Spanish (Spain)": "الإسبانية (إسبانيا)",
"crash_page_search_issue": "بحثت عن <a href=\"`x`\"> المشكلات الموجودة على GitHub </a>",
"search_filters_title": "معامل الفرز",
"search_message_no_results": "لا توجد نتائج."
"search_message_no_results": "لا توجد نتائج.",
"search_message_change_filters_or_query": "حاول توسيع استعلام البحث و / أو تغيير عوامل التصفية.",
"search_filters_date_label": "تاريخ الرفع",
"generic_count_weeks_0": "{{count}} أسبوع",
"generic_count_weeks_1": "أسبوع واحد",
"generic_count_weeks_2": "أسبوعين",
"generic_count_weeks_3": "{{count}} أسابيع",
"generic_count_weeks_4": "{{count}} أسبوع",
"generic_count_weeks_5": "{{count}} أسبوع",
"Popular enabled: ": "تم تمكين الشعبية: ",
"search_filters_duration_option_medium": "متوسط (4-20 دقيقة)",
"search_filters_date_option_none": "أي تاريخ",
"search_filters_type_option_all": "أي نوع",
"search_filters_features_option_vr180": "VR180",
"generic_count_minutes_0": "{{count}} دقيقة",
"generic_count_minutes_1": "دقيقة واحدة",
"generic_count_minutes_2": "دقيقتين",
"generic_count_minutes_3": "{{count}} دقائق",
"generic_count_minutes_4": "{{count}} دقيقة",
"generic_count_minutes_5": "{{count}} دقيقة",
"generic_count_hours_0": "{{count}} ساعة",
"generic_count_hours_1": "ساعة واحدة",
"generic_count_hours_2": "ساعتين",
"generic_count_hours_3": "{{count}} ساعات",
"generic_count_hours_4": "{{count}} ساعة",
"generic_count_hours_5": "{{count}} ساعة",
"comments_view_x_replies_0": "عرض رد {{count}}",
"comments_view_x_replies_1": "عرض رد {{count}}",
"comments_view_x_replies_2": "عرض رد {{count}}",
"comments_view_x_replies_3": "عرض رد {{count}}",
"comments_view_x_replies_4": "عرض الردود {{count}}",
"comments_view_x_replies_5": "عرض رد {{count}}",
"search_message_use_another_instance": " يمكنك أيضًا البحث عن <a href=\"`x`\"> في مثيل آخر </a>.",
"comments_points_count_0": "{{count}} نقطة",
"comments_points_count_1": "نقطة واحدة",
"comments_points_count_2": "نقطتان",
"comments_points_count_3": "{{count}} نقط",
"comments_points_count_4": "{{count}} نقطة",
"comments_points_count_5": "{{count}} نقطة",
"generic_count_years_0": "{{count}} السنة",
"generic_count_years_1": "{{count}} السنة",
"generic_count_years_2": "{{count}} السنة",
"generic_count_years_3": "{{count}} السنة",
"generic_count_years_4": "{{count}} سنوات",
"generic_count_years_5": "{{count}} السنة",
"tokens_count_0": "الرمز المميز {{count}}",
"tokens_count_1": "الرمز المميز {{count}}",
"tokens_count_2": "الرمز المميز {{count}}",
"tokens_count_3": "الرمز المميز {{count}}",
"tokens_count_4": "الرموز المميزة {{count}}",
"tokens_count_5": "الرمز المميز {{count}}",
"search_filters_apply_button": "تطبيق الفلاتر المحددة",
"search_filters_duration_option_none": "أي مدة",
"subscriptions_unseen_notifs_count_0": "{{count}} إشعار جديد",
"subscriptions_unseen_notifs_count_1": "إشعار واحد جديد",
"subscriptions_unseen_notifs_count_2": "إشعارين جديدين",
"subscriptions_unseen_notifs_count_3": "{{count}} إشعارات جديدة",
"subscriptions_unseen_notifs_count_4": "{{count}} إشعارا جديد",
"subscriptions_unseen_notifs_count_5": "{{count}} إشعار جديد",
"generic_count_days_0": "{{count}} يوم",
"generic_count_days_1": "يوم واحد",
"generic_count_days_2": "يومين",
"generic_count_days_3": "{{count}} أيام",
"generic_count_days_4": "{{count}} يوم",
"generic_count_days_5": "{{count}} يوم",
"generic_count_months_0": "{{count}} شهر",
"generic_count_months_1": "{{count}} شهر",
"generic_count_months_2": "{{count}} شهر",
"generic_count_months_3": "{{count}} شهر",
"generic_count_months_4": "{{count}} شهور",
"generic_count_months_5": "{{count}} شهر",
"generic_count_seconds_0": "{{count}} ثانية",
"generic_count_seconds_1": "ثانية واحدة",
"generic_count_seconds_2": "ثانيتين",
"generic_count_seconds_3": "{{count}} ثوانٍ",
"generic_count_seconds_4": "{{count}} ثانية",
"generic_count_seconds_5": "{{count}} ثانية",
"error_video_not_in_playlist": "الفيديو المطلوب غير موجود في قائمة التشغيل هذه. <a href=\"`x`\"> انقر هنا للحصول على الصفحة الرئيسية لقائمة التشغيل. </a>",
"channel_tab_shorts_label": "الفيديوهات القصيرة",
"channel_tab_streams_label": "البث المباشر",
"channel_tab_playlists_label": "قوائم التشغيل",
"channel_tab_channels_label": "القنوات",
"Music in this video": "الموسيقى في هذا الفيديو",
"Album: ": "الألبوم: ",
"Artist: ": "الفنان: ",
"Song: ": "أغنية: ",
"Channel Sponsor": "راعي القناة",
"Standard YouTube license": "ترخيص YouTube القياسي",
"Download is disabled": "تم تعطيل التحميلات",
"Import YouTube playlist (.csv)": "استيراد قائمة تشغيل YouTube (.csv)"
}

@ -0,0 +1,94 @@
{
"Subscribe": "সাবস্ক্রাইব",
"View channel on YouTube": "ইউটিউবে চ্যানেল দেখুন",
"View playlist on YouTube": "ইউটিউবে প্লেলিস্ট দেখুন",
"newest": "সর্ব-নতুন",
"oldest": "পুরানতম",
"popular": "জনপ্রিয়",
"last": "শেষটা",
"Next page": "পরের পৃষ্ঠা",
"Previous page": "আগের পৃষ্ঠা",
"Clear watch history?": "দেখার ইতিহাস সাফ করবেন?",
"New password": "নতুন পাসওয়ার্ড",
"New passwords must match": "নতুন পাসওয়ার্ড অবশ্যই মিলতে হবে",
"Authorize token?": "টোকেন অনুমোদন করবেন?",
"Authorize token for `x`?": "`x` -এর জন্য টোকেন অনুমোদন?",
"Yes": "হ্যাঁ",
"No": "না",
"Import and Export Data": "তথ্য আমদানি ও রপ্তানি",
"Import": "আমদানি",
"Import Invidious data": "ইনভিডিয়াস তথ্য আমদানি",
"Import YouTube subscriptions": "ইউটিউব সাবস্ক্রিপশন আনুন",
"Import FreeTube subscriptions (.db)": "ফ্রিটিউব সাবস্ক্রিপশন (.db) আনুন",
"Import NewPipe subscriptions (.json)": "নতুন পাইপ সাবস্ক্রিপশন আনুন (.json)",
"Import NewPipe data (.zip)": "নিউপাইপ তথ্য আনুন (.zip)",
"Export": "তথ্য বের করুন",
"Export subscriptions as OPML": "সাবস্ক্রিপশন OPML হিসাবে আনুন",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "OPML-এ সাবস্ক্রিপশন বের করুন(নিউ পাইপ এবং ফ্রিউটিউব এর জন্য)",
"Export data as JSON": "JSON হিসাবে তথ্য বের করুন",
"Delete account?": "অ্যাকাউন্ট মুছে ফেলবেন?",
"History": "ইতিহাস",
"An alternative front-end to YouTube": "ইউটিউবের একটি বিকল্পস্বরূপ সম্মুখ-প্রান্ত",
"JavaScript license information": "জাভাস্ক্রিপ্ট লাইসেন্সের তথ্য",
"source": "সূত্র",
"Log in": "লগ ইন",
"Log in/register": "লগ ইন/রেজিস্টার",
"User ID": "ইউজার আইডি",
"Password": "পাসওয়ার্ড",
"Time (h:mm:ss):": "সময় (ঘণ্টা:মিনিট:সেকেন্ড):",
"Text CAPTCHA": "টেক্সট ক্যাপচা",
"Image CAPTCHA": "চিত্র ক্যাপচা",
"Sign In": "সাইন ইন",
"Register": "নিবন্ধন",
"E-mail": "ই-মেইল",
"Preferences": "পছন্দসমূহ",
"preferences_category_player": "প্লেয়ারের পছন্দসমূহ",
"preferences_video_loop_label": "সর্বদা লুপ: ",
"preferences_autoplay_label": "স্বয়ংক্রিয় চালু: ",
"preferences_continue_label": "ডিফল্টভাবে পরবর্তী চালাও: ",
"preferences_continue_autoplay_label": "পরবর্তী ভিডিও স্বয়ংক্রিয়ভাবে চালাও: ",
"preferences_listen_label": "সহজাতভাবে শোনো: ",
"preferences_local_label": "ভিডিও প্রক্সি করো: ",
"preferences_speed_label": "সহজাত গতি: ",
"preferences_quality_label": "পছন্দের ভিডিও মান: ",
"preferences_volume_label": "প্লেয়ার শব্দের মাত্রা: ",
"LIVE": "লাইভ",
"Shared `x` ago": "`x` আগে শেয়ার করা হয়েছে",
"Unsubscribe": "আনসাবস্ক্রাইব",
"generic_views_count": "{{count}}জন দেখেছে",
"generic_views_count_plural": "{{count}}জন দেখেছে",
"generic_videos_count": "{{count}}টি ভিডিও",
"generic_videos_count_plural": "{{count}}টি ভিডিও",
"generic_subscribers_count": "{{count}}জন অনুসরণকারী",
"generic_subscribers_count_plural": "{{count}}জন অনুসরণকারী",
"preferences_watch_history_label": "দেখার ইতিহাস চালু করো: ",
"preferences_quality_option_dash": "ড্যাশ (সময়োপযোগী মান)",
"preferences_quality_dash_option_auto": "স্বয়ংক্রিয়",
"preferences_quality_dash_option_best": "সেরা",
"preferences_quality_dash_option_worst": "মন্দতম",
"preferences_quality_dash_option_4320p": "৪৩২০পি",
"preferences_quality_dash_option_2160p": "২১৬০পি",
"preferences_quality_dash_option_1440p": "১৪৪০পি",
"preferences_quality_dash_option_480p": "৪৮০পি",
"preferences_quality_dash_option_360p": "৩৬০পি",
"preferences_quality_dash_option_240p": "২৪০পি",
"preferences_quality_dash_option_144p": "১৪৪পি",
"preferences_comments_label": "সহজাত মন্তব্য: ",
"youtube": "ইউটিউব",
"Fallback captions: ": "বিকল্প উপাখ্যান: ",
"preferences_related_videos_label": "সম্পর্কিত ভিডিও দেখাও: ",
"preferences_annotations_label": "সহজাতভাবে টীকা দেখাও ",
"preferences_quality_option_hd720": "উচ্চ৭২০",
"preferences_quality_dash_label": "পছন্দের ড্যাশ ভিডিও মান: ",
"preferences_captions_label": "সহজাত উপাখ্যান: ",
"generic_playlists_count": "{{count}}টি চালুতালিকা",
"generic_playlists_count_plural": "{{count}}টি চালুতালিকা",
"reddit": "রেডিট",
"invidious": "ইনভিডিয়াস",
"generic_subscriptions_count": "{{count}}টি অনুসরণ",
"generic_subscriptions_count_plural": "{{count}}টি অনুসরণ",
"preferences_quality_option_medium": "মধ্যম",
"preferences_quality_option_small": "ছোট",
"preferences_quality_dash_option_1080p": "১০৮০পি",
"preferences_quality_dash_option_720p": "৭২০পি"
}

@ -14,7 +14,6 @@
"Clear watch history?": "দেখার ইতিহাস সাফ করবেন?",
"New password": "নতুন পাসওয়ার্ড",
"New passwords must match": "নতুন পাসওয়ার্ড অবশ্যই মিলতে হবে",
"Cannot change password for Google accounts": "গুগল অ্যাকাউন্টগুলোর জন্য পাসওয়ার্ড পরিবর্তন করা যায় না",
"Authorize token?": "টোকেন অনুমোদন করবেন?",
"Authorize token for `x`?": "`x` -এর জন্য টোকেন অনুমোদন?",
"Yes": "হ্যাঁ",
@ -37,7 +36,6 @@
"source": "সূত্র",
"Log in": "লগ ইন",
"Log in/register": "লগ ইন/রেজিস্টার",
"Log in with Google": "গুগল দিয়ে লগ ইন করুন",
"User ID": "ইউজার আইডি",
"Password": "পাসওয়ার্ড",
"Time (h:mm:ss):": "সময় (ঘণ্টা:মিনিট:সেকেন্ড):",
@ -46,7 +44,6 @@
"Sign In": "সাইন ইন",
"Register": "নিবন্ধন",
"E-mail": "ই-মেইল",
"Google verification code": "গুগল যাচাইকরণ কোড",
"Preferences": "পছন্দসমূহ",
"preferences_category_player": "প্লেয়ারের পছন্দসমূহ",
"preferences_video_loop_label": "সর্বদা লুপ: ",

@ -4,7 +4,6 @@
"preferences_quality_label": "Qualitat de vídeo preferida: ",
"newest": "més nou",
"No": "No",
"Google verification code": "Codi de verificació de Google",
"User ID": "ID d'usuari",
"Preferences": "Preferències",
"Dark mode: ": "Mode fosc: ",
@ -51,7 +50,7 @@
"Movies": "Películes",
"Download": "Descarrega",
"Download as: ": "Descarrega com: ",
"Videos": "Vídeos",
"channel_tab_videos_label": "Vídeos",
"search_filters_type_label": "Tipus",
"search_filters_duration_label": "Duració",
"search_filters_sort_label": "Ordena per",
@ -66,7 +65,7 @@
"Malay": "Malai",
"Persian": "Persa",
"Slovak": "Eslovac",
"Search": "Busca",
"Search": "Cerca",
"Show annotations": "Mostra anotacions",
"preferences_region_label": "País del contingut: ",
"preferences_sort_label": "Ordena vídeos per: ",
@ -75,7 +74,7 @@
"Title": "Títol",
"Belarusian": "Bielorús",
"Enable web notifications": "Activa notificacions web",
"search": "busca",
"search": "Cerca",
"Catalan": "Català",
"Croatian": "Croat",
"preferences_category_admin": "Preferències d'administrador",
@ -99,5 +98,382 @@
"Music": "Música",
"search_filters_sort_option_relevance": "Rellevància",
"search_filters_date_option_hour": "Última hora",
"search_filters_date_option_today": "Avui"
"search_filters_date_option_today": "Avui",
"preferences_volume_label": "Volum del reproductor: ",
"invidious": "Invidious",
"preferences_quality_dash_option_144p": "144p",
"Turkish (auto-generated)": "Turc (generat automàticament)",
"Urdu": "Urdú",
"Vietnamese (auto-generated)": "Vietnamita (generat automàticament)",
"Welsh": "Gal·lès",
"Yoruba": "Ioruba",
"YouTube comment permalink": "Enllaç permanent de comentari de YouTube",
"Channel Sponsor": "Patrocinador del canal",
"Audio mode": "Mode d'àudio",
"search_filters_date_option_none": "Qualsevol data",
"search_filters_type_option_playlist": "Llista de reproducció",
"search_filters_type_option_movie": "Pel·lícula",
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_subtitles": "Subtítols/CC",
"search_filters_features_option_live": "Directe",
"search_filters_features_option_hd": "HD",
"search_filters_features_option_hdr": "HDR",
"search_filters_features_option_location": "Ubicació",
"search_filters_apply_button": "Aplica els filtres seleccionats",
"videoinfo_started_streaming_x_ago": "Ha començat el directe fa `x`",
"next_steps_error_message_go_to_youtube": "Vés a YouTube",
"footer_donate_page": "Feu un donatiu",
"footer_original_source_code": "Codi font original",
"videoinfo_watch_on_youTube": "Veure a YouTube",
"user_saved_playlists": "`x` llistes de reproducció guardades",
"adminprefs_modified_source_code_url_label": "URL al repositori de codi font modificat",
"none": "cap",
"footer_modfied_source_code": "Codi font modificat",
"videoinfo_invidious_embed_link": "Incrusta l'enllaç",
"download_subtitles": "Subtítols - `x` (.vtt)",
"user_created_playlists": "`x`llistes de reproducció creades",
"Video unavailable": "Vídeo no disponible",
"channel_tab_channels_label": "Canals",
"channel_tab_playlists_label": "Llistes de reproducció",
"channel_tab_community_label": "Comunitat",
"Czech": "Txec",
"Default": "Per defecte",
"Amharic": "Amàric",
"preferences_automatic_instance_redirect_label": "Redirecció automàtica d'instàncies (retorna a redirect.invidious.io): ",
"Login enabled: ": "Activa inici de sessió: ",
"Registration enabled: ": "Activa registre: ",
"Whitelisted regions: ": "Regions a la llista blanca: ",
"Chinese (Simplified)": "Xinès (Simplificat)",
"Corsican": "Cors",
"Estonian": "Estonià",
"Japanese (auto-generated)": "Japonès (generat automàticament)",
"English (United States)": "Anglès (Estats Units)",
"English (auto-generated)": "Anglès (generat automàticament)",
"Cebuano": "Cebuà",
"Esperanto": "Esperanto",
"Scottish Gaelic": "Gaèlic escocès",
"Playlists": "Llistes de reproducció",
"search_filters_title": "Filtres",
"search_filters_type_option_all": "Qualsevol tipus",
"search_filters_duration_option_none": "Qualsevol duració",
"next_steps_error_message": "Després d'això, hauríeu d'intentar: ",
"next_steps_error_message_refresh": "Recarregar la pàgina",
"crash_page_refresh": "ha intentat <a href=\"`x`\">actualitzar la pàgina</a>",
"crash_page_report_issue": "Si cap de les anteriors no ha ajudat, <a href=\"`x`\">obre un nou issue a GitHub</a> (preferiblement en anglès) i inclou el text següent al missatge (NO tradueixis aquest text):",
"generic_subscriptions_count": "{{count}} subscripció",
"generic_subscriptions_count_plural": "{{count}} subscripcions",
"error_video_not_in_playlist": "El vídeo sol·licitat no existeix en aquesta llista de reproducció. <a href=\"`x`\">Feu clic aquí per a la pàgina d'inici de la llista de reproducció.</a>",
"comments_points_count": "{{count}} punt",
"comments_points_count_plural": "{{count}} punts",
"%A %B %-d, %Y": "%A %B %-d, %Y",
"Create playlist": "Crear llista de reproducció",
"Text CAPTCHA": "Text CAPTCHA",
"Next page": "Pàgina següent",
"preferences_category_visual": "Preferències visuals",
"preferences_unseen_only_label": "Mostra només no vistos: ",
"preferences_listen_label": "Escolta per defecte: ",
"Import": "Importar",
"Token": "Testimoni",
"Wilson score: ": "Puntuació de Wilson: ",
"search_filters_date_label": "Data de càrrega",
"search_filters_features_option_three_sixty": "360°",
"source": "font",
"preferences_default_home_label": "Pàgina d'inici per defecte: ",
"preferences_comments_label": "Comentaris per defecte: ",
"`x` uploaded a video": "`x` ha penjat un vídeo",
"Released under the AGPLv3 on Github.": "Publicat sota l'AGPLv3 a GitHub.",
"Token manager": "Gestor de testimonis",
"Watch history": "Historial de reproduccions",
"Authorize token?": "Autoritzar testimoni?",
"Source available here.": "Font disponible aquí.",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Exporta subscripcions com a OPML (per a NewPipe i FreeTube)",
"Log in": "Inicia sessió",
"search_filters_sort_option_date": "Data de càrrega",
"Unlisted": "No llistat",
"View privacy policy.": "Veure política de privadesa.",
"Public": "Públic",
"View all playlists": "Veure totes les llistes de reproducció",
"reddit": "Reddit",
"Manage tokens": "Gestiona testimonis",
"Not a playlist.": "No és una llista de reproducció.",
"preferences_local_label": "Vídeos de Proxy: ",
"View channel on YouTube": "Veure canal a Youtube",
"preferences_quality_dash_option_1080p": "1080p",
"Top enabled: ": "Activa top: ",
"Delete playlist `x`?": "Eliminar llista de reproducció `x`?",
"View JavaScript license information.": "Consulta la informació de la llicència de JavaScript.",
"Playlist privacy": "Privacitat de la llista de reproducció",
"search_message_no_results": "No s'han trobat resultats.",
"search_message_use_another_instance": " També es pot <a href=\"`x`\">buscar en una altra instància</a>.",
"Genre: ": "Gènere: ",
"Hidden field \"challenge\" is a required field": "El camp ocult \"repte\" és un camp obligatori",
"Burmese": "Birmà",
"View as playlist": "Mostra com a llista de reproducció",
"preferences_category_subscription": "Preferències de subscripció",
"Music in this video": "Música en aquest vídeo",
"Artist: ": "Artista: ",
"Album: ": "Àlbum: ",
"Shared `x`": "Compartit `x`",
"Premieres `x`": "Estrena `x`",
"View more comments on Reddit": "Veure més comentaris a Reddit",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "Veure `x` comentari",
"": "Veure `x` comentaris"
},
"View Reddit comments": "Veure comentaris de Reddit",
"Incorrect password": "Contrasenya incorrecta",
"Erroneous CAPTCHA": "CAPTCHA erroni",
"CAPTCHA is a required field": "El CAPTCHA és un camp obligatori",
"Korean (auto-generated)": "Coreà (generat automàticament)",
"Kyrgyz": "Kirguís",
"Latin": "Llatí",
"Malagasy": "Malgaix",
"Maori": "Maori",
"Marathi": "Marathi",
"Norwegian Bokmål": "Bokmål Noruec",
"Nyanja": "Nyanja",
"Portuguese (Brazil)": "Portuguès (Brazil)",
"Punjabi": "Panjabi",
"Russian (auto-generated)": "Rus (generat automàticament)",
"Samoan": "Samoà",
"Somali": "Somali",
"Southern Sotho": "Sesotho",
"Spanish (Mexico)": "Espanyol (Mèxic)",
"Spanish (Spain)": "Espanyol (Espanya)",
"Sundanese": "Sondanès",
"Swahili": "Suahili",
"Tamil": "Tàmil",
"Telugu": "Telugu",
"Zulu": "Zulu",
"generic_count_months": "{{count}} mes",
"generic_count_months_plural": "{{count}} mesos",
"generic_count_weeks": "{{count}} setmana",
"generic_count_weeks_plural": "{{count}} setmanes",
"About": "Sobre",
"`x` marked it with a ❤": "`x`marca'l amb un ❤",
"Video mode": "Mode de vídeo",
"search_filters_features_label": "Característiques",
"search_filters_features_option_c_commons": "Creative Commons",
"search_filters_features_option_vr180": "VR180",
"search_filters_features_option_three_d": "3D",
"search_filters_features_option_purchased": "Comprat",
"Chinese (Hong Kong)": "Xinès (Hong Kong)",
"Chinese (Taiwan)": "Xinès (Taiwan)",
"Hmong": "Hmong",
"Kazakh": "Kazakh",
"Igbo": "Igbo",
"Javanese": "Javanès",
"Indonesian (auto-generated)": "Indonesi (generat automàticament)",
"Interlingue": "Interlingüe",
"Khmer": "Khmer",
"This channel does not exist.": "Aquest canal no existeix.",
"Song: ": "Cançó: ",
"channel:`x`": "canal: `x`",
"Deleted or invalid channel": "Canal suprimit o no vàlid",
"Could not get channel info.": "No s'ha pogut obtenir la informació del canal.",
"Could not pull trending pages.": "No s'han pogut extreure les pàgines de tendència.",
"comments_view_x_replies": "Veure {{count}} resposta",
"comments_view_x_replies_plural": "Veure {{count}} respostes",
"Subscriptions": "Subscripcions",
"generic_count_seconds": "{{count}} segon",
"generic_count_seconds_plural": "{{count}} segons",
"channel_tab_shorts_label": "Vídeos curts",
"preferences_save_player_pos_label": "Desa la posició de reproducció: ",
"crash_page_before_reporting": "Abans d'informar d'un error, assegureu-vos que teniu:",
"crash_page_switch_instance": "ha intentat <a href=\"`x`\">utilitzar una altra instància</a>",
"crash_page_read_the_faq": "heu llegit les <a href=\"`x`\">Preguntes més freqüents (FAQ)</a>",
"crash_page_search_issue": "ha cercat <a href=\"`x`\">problemes existents a GitHub</a>",
"User ID is a required field": "L'identificador d'usuari és un camp obligatori",
"Password is a required field": "La contrasenya és un camp obligatori",
"Wrong username or password": "Nom d'usuari o contrasenya incorrectes",
"Password cannot be longer than 55 characters": "La contrasenya no pot tenir més de 55 caràcters",
"Invidious Private Feed for `x`": "Feed privat Invidious per a `x`",
"generic_views_count": "{{count}} visualització",
"generic_views_count_plural": "{{count}} visualitzacions",
"generic_videos_count": "{{count}} vídeo",
"generic_videos_count_plural": "{{count}} vídeos",
"Token is expired, please try again": "El testimoni ha caducat, torna-ho a provar",
"English": "Anglès",
"Kannada": "Kanarès",
"Erroneous token": "Testimoni erroni",
"`x` ago": "fa `x`",
"Empty playlist": "Llista de reproducció buida",
"Playlist does not exist.": "La llista de reproducció no existeix.",
"No such user": "No hi ha tal usuari",
"Afrikaans": "Afrikàans",
"Azerbaijani": "Azerbaidjana",
"Cantonese (Hong Kong)": "Cantonès (Hong Kong)",
"Chinese": "Xinès",
"Chinese (China)": "Xinès (Xina)",
"Chinese (Traditional)": "Xinès (Tradicional)",
"Dutch": "Holandès",
"Dutch (auto-generated)": "Holandès (generat automàticament)",
"French (auto-generated)": "Francès (generat automàticament)",
"Georgian": "Georgià",
"German (auto-generated)": "Alemany (generat automàticament)",
"Gujarati": "Gujarati",
"Hawaiian": "Hawaià",
"generic_count_years": "{{count}} any",
"generic_count_years_plural": "{{count}} anys",
"Popular": "Popular",
"Rating: ": "Valoració: ",
"permalink": "enllaç permanent",
"preferences_quality_dash_option_worst": "Pitjor",
"Yiddish": "Ídix",
"preferences_quality_dash_option_auto": "Automàtic",
"Western Frisian": "Frisó occidental",
"Swedish": "Suec",
"Only show latest unwatched video from channel: ": "Mostra només l'últim vídeo no vist del canal: ",
"preferences_continue_label": "Reprodueix el següent per defecte: ",
"Import YouTube subscriptions": "Importar subscripcions de YouTube",
"search_filters_sort_option_rating": "Valoració",
"preferences_thin_mode_label": "Mode prim: ",
"preferences_quality_option_small": "Petit",
"CAPTCHA enabled: ": "activa CAPTCHA: ",
"Import and Export Data": "Importar i exportar dades",
"preferences_quality_dash_option_360p": "360p",
"Popular enabled: ": "Activa popular: ",
"Password": "Contrasenya",
"Blacklisted regions: ": "Regions a la llista negra: ",
"Register": "Registra't",
"Shared `x` ago": "Compartit fa `x`",
"search_filters_sort_option_views": "Recompte de visualitzacions",
"Import Invidious data": "Importa dades JSON d'Invidious",
"preferences_related_videos_label": "Mostra vídeos relacionats: ",
"preferences_show_nick_label": "Mostra l'àlies a la part superior: ",
"Time (h:mm:ss):": "Temps (h:mm:ss):",
"Could not fetch comments": "No s'han pogut obtenir els comentaris",
"New password": "Nova contrasenya",
"preferences_notifications_only_label": "Mostra només notificacions (si n'hi ha): ",
"preferences_annotations_label": "Mostra anotacions per defecte: ",
"Import FreeTube subscriptions (.db)": "Importar subscripcions de FreeTube (.db)",
"Fallback captions: ": "Subtítols alternatius: ",
"Log out": "Tancar sessió",
"preferences_quality_dash_option_2160p": "2160p",
"Unsubscribe": "Cancel·la la subscripció",
"Log in/register": "Inicia sessió/registra't",
"Nepali": "Nepalí",
"Xhosa": "Xosa",
"preferences_captions_label": "Subtítols per defecte: ",
"preferences_autoplay_label": "Reproducció automàtica: ",
"`x` is live": "`x` està en directe",
"Uzbek": "Uzbek",
"Hausa": "Haussa",
"Bosnian": "Bosnià",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Hola! Sembla que tens JavaScript desactivat. Feu clic aquí per veure els comentaris, tingueu en compte que poden trigar una mica més a carregar-se.",
"Password cannot be empty": "La contrasenya no pot estar buida",
"preferences_video_loop_label": "Sempre en bucle: ",
"preferences_quality_option_dash": "DASH (qualitat adaptativa)",
"Change password": "Canvia la contrasenya",
"Export data as JSON": "Exporta dades d'Invidious com a JSON",
"Wrong answer": "Resposta incorrecta",
"Clear watch history": "Neteja l'historial de reproduccions",
"Mongolian": "Mongol",
"preferences_quality_dash_option_best": "Millor",
"Authorize token for `x`?": "Autoritzar testimoni per a `x`?",
"Report statistics: ": "Estadístiques de l'informe: ",
"Switch Invidious Instance": "Canvia la instància d'Invidious",
"History": "Historial",
"Portuguese (auto-generated)": "Portuguès (generat automàticament)",
"footer_source_code": "Codi font",
"videoinfo_youTube_embed_link": "Insereix",
"generic_count_minutes": "{{count}} minut",
"generic_count_minutes_plural": "{{count}} minuts",
"preferences_category_player": "Preferències del reproductor",
"Sign In": "Inicia Sessió",
"preferences_continue_autoplay_label": "Reprodueix automàticament el següent vídeo: ",
"generic_playlists_count": "{{count}} llista de reproducció",
"generic_playlists_count_plural": "{{count}} llistes de reproducció",
"Delete account?": "Esborrar compte?",
"Please log in": "Si us plau inicieu sessió",
"Import NewPipe data (.zip)": "Importar dades de NewPipe (.zip)",
"Image CAPTCHA": "Imatge CAPTCHA",
"channel_tab_streams_label": "Transmissions en directe",
"preferences_category_misc": "Preferències diverses",
"preferences_annotations_subscribed_label": "Mostra les anotacions per defecte dels canals subscrits? ",
"Tajik": "Tadjik",
"preferences_player_style_label": "Estil del reproductor: ",
"Load more": "Carrega més",
"preferences_vr_mode_label": "Vídeos interactius de 360 graus (requereix WebGL): ",
"Manage subscriptions": "Gestionar les subscripcions",
"preferences_quality_option_medium": "Mitjà",
"Editing playlist `x`": "Editant la llista de reproducció `x`",
"search_filters_duration_option_medium": "Mitjà (4 - 20 minuts)",
"E-mail": "Correu electrònic",
"Spanish (auto-generated)": "Castellà (generat automàticament)",
"Export": "Exportar",
"preferences_quality_dash_option_4320p": "4320p",
"JavaScript license information": "Informació de la llicència de JavaScript",
"Hidden field \"token\" is a required field": "El camp ocult \"testimoni\" és un camp obligatori",
"Shona": "Xona",
"Family friendly? ": "Apte per a tots els públics? ",
"preferences_quality_dash_label": "Qualitat de vídeo DASH preferida: ",
"Hindi": "Hindi",
"An alternative front-end to YouTube": "Una interfície alternativa a YouTube",
"Export subscriptions as OPML": "Exporta subscripcions com a OPML",
"Watch on YouTube": "Veure a YouTube",
"Lao": "Laosià",
"search_message_change_filters_or_query": "Proveu d'ampliar la vostra consulta de cerca i/o canviar els filtres.",
"View YouTube comments": "Veure comentaris de YouTube",
"New passwords must match": "Les contrasenyes noves han de coincidir",
"Subscription manager": "Gestor de subscripcions",
"Premieres in `x`": "Estrena en `x`",
"youtube": "YouTube",
"Latvian": "Letó",
"LIVE": "EN VIU",
"Could not create mix.": "No s'ha pogut crear la barreja.",
"preferences_speed_label": "Velocitat per defecte: ",
"preferences_extend_desc_label": "Amplieu automàticament la descripció del vídeo: ",
"popular": "popular",
"Erroneous challenge": "Repte erroni",
"last": "darrer",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_dash_option_1440p": "1440p",
"Previous page": "Pàgina anterior",
"Only show latest video from channel: ": "Mostra només l'últim vídeo del canal: ",
"unsubscribe": "cancel·la la subscripció",
"View playlist on YouTube": "Veure llista de reproducció a YouTube",
"Import NewPipe subscriptions (.json)": "Importar subscripcions de NewPipe (.json)",
"crash_page_you_found_a_bug": "Heu trobat un error a Invidious!",
"Subscribe": "Subscriu-me",
"generic_count_days": "{{count}} dia",
"generic_count_days_plural": "{{count}} dies",
"Trending": "Tendència",
"Updated `x` ago": "Actualitzat fa `x`",
"Haitian Creole": "Crioll Haitià",
"preferences_watch_history_label": "Habilita historial de reproduccions: ",
"generic_count_hours": "{{count}} hora",
"generic_count_hours_plural": "{{count}} hores",
"Malayalam": "Maialàiam",
"Clear watch history?": "Neteja historial de reproduccions?",
"Import/export data": "Importa/exporta dades",
"Sinhala": "Singalès",
"Delete playlist": "Eliminar llista de reproducció",
"Bangla": "Bengalí",
"Italian (auto-generated)": "Italià (generat automàticament)",
"License: ": "Llicència: ",
"(edited)": "(editat)",
"Pashto": "Paixtu",
"preferences_dark_mode_label": "Tema: ",
"revoke": "revocar",
"English (United Kingdom)": "Anglès (Regne Unit)",
"preferences_quality_option_hd720": "HD720",
"tokens_count": "{{count}} testimoni",
"tokens_count_plural": "{{count}} testimonis",
"subscriptions_unseen_notifs_count": "{{count}} notificació no vista",
"subscriptions_unseen_notifs_count_plural": "{{count}} notificacions no vistes",
"generic_subscribers_count": "{{count}} subscriptor",
"generic_subscribers_count_plural": "{{count}} subscriptors",
"Sindhi": "Sindhi",
"Slovenian": "Eslovè",
"preferences_feed_menu_label": "Menú del feed: ",
"Fallback comments: ": "Comentaris alternatius: ",
"Top": "Millors",
"preferences_max_results_label": "Nombre de vídeos mostrats al feed: ",
"Engagement: ": "Atracció: ",
"Redirect homepage to feed: ": "Redirigeix la pàgina d'inici al feed: ",
"Standard YouTube license": "Llicència estàndard de YouTube",
"Download is disabled": "Les baixades s'han inhabilitat"
}

@ -13,8 +13,7 @@
"Previous page": "Předchozí strana",
"Clear watch history?": "Smazat historii?",
"New password": "Nové heslo",
"New passwords must match": "Hesla se musí schodovat",
"Cannot change password for Google accounts": "Nelze změnit heslo pro účty Google",
"New passwords must match": "Hesla se musí shodovat",
"Authorize token?": "Autorizovat token?",
"Authorize token for `x`?": "Autorizovat token pro `x`?",
"Yes": "Ano",
@ -37,7 +36,6 @@
"source": "zdrojový kód",
"Log in": "Přihlásit se",
"Log in/register": "Přihlásit se/vytvořit účet",
"Log in with Google": "Přihlásit se s Googlem",
"User ID": "ID uživatele",
"Password": "Heslo",
"Time (h:mm:ss):": "Čas (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Přihlásit se",
"Register": "Vytvořit účet",
"E-mail": "E-mail",
"Google verification code": "Verifikační číslo Google",
"Preferences": "Nastavení",
"preferences_category_player": "Nastavení přehravače",
"preferences_video_loop_label": "Vždy opakovat: ",
@ -63,7 +60,7 @@
"reddit": "Reddit",
"preferences_captions_label": "Výchozí titulky: ",
"Fallback captions: ": "Záložní titulky: ",
"preferences_related_videos_label": "Zobrazit podobné videa: ",
"preferences_related_videos_label": "Zobrazit podobná videa: ",
"preferences_annotations_label": "Zobrazovat poznámky ve výchozím nastavení: ",
"preferences_extend_desc_label": "Rozšířit automaticky popis u videa: ",
"preferences_category_visual": "Nastavení vzhledu",
@ -88,7 +85,7 @@
"Only show latest unwatched video from channel: ": "Zobrazit jen nejnovější nezhlédnuté video z daného kanálu: ",
"preferences_unseen_only_label": "Zobrazit jen již nezhlédnuté: ",
"preferences_notifications_only_label": "Zobrazit pouze upozornění (pokud nějaká jsou): ",
"Enable web notifications": "Povolit webové upozornění",
"Enable web notifications": "Povolit webová upozornění",
"`x` uploaded a video": "`x` nahrál(a) video",
"`x` is live": "`x` je živě",
"preferences_category_data": "Nastavení dat",
@ -260,8 +257,8 @@
"`x` marked it with a ❤": "`x` to označil(a) se ❤",
"Audio mode": "Audiový režim",
"Video mode": "Videový režim",
"Videos": "Videa",
"Community": "Komunita",
"channel_tab_videos_label": "Videa",
"channel_tab_community_label": "Komunita",
"search_filters_sort_option_rating": "Hodnocení",
"search_filters_sort_option_date": "Datum nahrání",
"search_filters_sort_option_views": "Počet zhlédnutí",
@ -335,7 +332,6 @@
"preferences_quality_dash_option_1440p": "1440p",
"invidious": "Invidious",
"View more comments on Reddit": "Zobrazit více komentářů na Redditu",
"Invalid TFA code": "Nesprávný TFA kód",
"generic_playlists_count_0": "{{count}} playlist",
"generic_playlists_count_1": "{{count}} playlisty",
"generic_playlists_count_2": "{{count}} playlistů",
@ -349,7 +345,6 @@
"subscriptions_unseen_notifs_count_1": "{{count}} nezobrazená oznámení",
"subscriptions_unseen_notifs_count_2": "{{count}} nezobrazených oznámení",
"Show replies": "Zobrazit odpovědi",
"Quota exceeded, try again in a few hours": "Kvóta překročena, zkuste to znovu za pár hodin",
"Password cannot be longer than 55 characters": "Heslo nesmí být delší než 55 znaků",
"comments_view_x_replies_0": "Zobrazit {{count}} odpověď",
"comments_view_x_replies_1": "Zobrazit {{count}} odpovědi",
@ -433,7 +428,6 @@
"View YouTube comments": "Zobrazit YouTube komentáře",
"Blacklisted regions: ": "Oblasti na černé listině: ",
"Wrong username or password": "Nesprávné uživatelské jméno nebo heslo",
"Please sign in using 'Log in with Google'": "Přihlaste se prosím pomocí Googlu",
"Password cannot be empty": "Heslo nemůže být prázné",
"preferences_category_misc": "Různá nastavení",
"preferences_show_nick_label": "Zobrazit přezdívku na vrchu: ",
@ -452,8 +446,6 @@
"([^.,0-9]|^)1([^.,0-9]|$)": "Zobrazit `x` komentář",
"": "Zobrazit `x` komentářů"
},
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Nepodařilo se přihlásit, ujistěte se, že je povoleno dvoufázové ověřování (autentifikátor nebo SMS).",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Přihlášení selhalo. Toto se může stát, když není na vašem účtu povolené dvoufázové ověřování.",
"Could not get channel info.": "Nepodařilo se získat informace o kanálu.",
"Could not fetch comments": "Nepodařilo se získat komentáře",
"Could not create mix.": "Nepodařilo se vytvořit mix.",
@ -486,5 +478,19 @@
"search_filters_features_option_purchased": "Zakoupeno",
"search_filters_sort_label": "Řadit dle",
"search_filters_sort_option_relevance": "Relevantnost",
"search_filters_apply_button": "Použít vybrané filtry"
"search_filters_apply_button": "Použít vybrané filtry",
"Popular enabled: ": "Populární povoleno: ",
"error_video_not_in_playlist": "Požadované video v tomto playlistu neexistuje. <a href=\"`x`\">Klikněte sem pro navštívení domovské stránky playlistu.</a>",
"channel_tab_shorts_label": "Shorts",
"channel_tab_playlists_label": "Playlisty",
"channel_tab_channels_label": "Kanály",
"channel_tab_streams_label": "Živé přenosy",
"Music in this video": "Hudba v tomto videu",
"Artist: ": "Umělec: ",
"Album: ": "Album: ",
"Channel Sponsor": "Sponzor kanálu",
"Song: ": "Skladba: ",
"Standard YouTube license": "Standardní licence YouTube",
"Download is disabled": "Stahování je zakázáno",
"Import YouTube playlist (.csv)": "Importovat YouTube playlist (.csv)"
}

@ -14,7 +14,6 @@
"Clear watch history?": "Ryd afspilningshistorik?",
"New password": "Nyt kodeord",
"New passwords must match": "Nye kodeord skal matche",
"Cannot change password for Google accounts": "Kan ikke skifte kodeord til Google-konti",
"Authorize token?": "Godkend token?",
"Authorize token for `x`?": "Godkend token til `x`?",
"Yes": "Ja",
@ -37,7 +36,6 @@
"source": "kilde",
"Log in": "Log på",
"Log in/register": "Log på/registrer",
"Log in with Google": "Log på med Google",
"User ID": "Bruger ID",
"Password": "Kodeord",
"Time (h:mm:ss):": "Tid (t:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Log ind",
"Register": "Registrer",
"E-mail": "E-mail",
"Google verification code": "Google-verifikationskode",
"Preferences": "Præferencer",
"preferences_category_player": "Afspillerindstillinger",
"preferences_video_loop_label": "Altid gentag: ",
@ -159,17 +156,12 @@
"Hide replies": "Skjul svar",
"Show replies": "Vis svar",
"Incorrect password": "Forkert adgangskode",
"Quota exceeded, try again in a few hours": "Kvota overskredet, prøv igen om et par timer",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Login fejlet, tjek at totrinsbekræftelse (Authenticator eller SMS) er slået til.",
"Invalid TFA code": "Ugyldig TFA kode",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Login fejlede. Dette kan skyldes, at to-faktor autentificering ikke er aktiveret for din konto.",
"Wrong answer": "Forkert svar",
"Erroneous CAPTCHA": "Fejlagtig CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA er et obligatorisk felt",
"User ID is a required field": "Bruger ID er et krævet felt",
"Password is a required field": "Adgangskode er et obligatorisk felt",
"Wrong username or password": "Forkert brugernavn eller adgangskode",
"Please sign in using 'Log in with Google'": "Log ind via 'Log ind med Google'",
"Password cannot be empty": "Adgangskoden må ikke være tom",
"Password cannot be longer than 55 characters": "Adgangskoden må ikke være længere end 55 tegn",
"Please log in": "Venligst log ind",
@ -187,7 +179,7 @@
"Esperanto": "Esperanto",
"Czech": "Tjekkisk",
"Danish": "Dansk",
"Community": "Samfund",
"channel_tab_community_label": "Samfund",
"Afrikaans": "Afrikansk",
"Portuguese": "Portugisisk",
"Ukrainian": "Ukrainsk",
@ -267,7 +259,7 @@
"search_filters_sort_option_rating": "Bedømmelse",
"Yoruba": "Yoruba",
"Erroneous token": "Fejlagtig token",
"Videos": "Videoer",
"channel_tab_videos_label": "Videoer",
"search_filters_type_option_show": "Vis",
"Luxembourgish": "Luxemboursk",
"Vietnamese": "Vietnamesisk",

@ -14,7 +14,6 @@
"Clear watch history?": "Verlauf löschen?",
"New password": "Neues Passwort",
"New passwords must match": "Neue Passwörter müssen übereinstimmen",
"Cannot change password for Google accounts": "Ich kann das Passwort deines Google Kontos nicht ändern",
"Authorize token?": "Token autorisieren?",
"Authorize token for `x`?": "Token für `x` autorisieren?",
"Yes": "Ja",
@ -37,7 +36,6 @@
"source": "Quelle",
"Log in": "Anmelden",
"Log in/register": "Anmelden/registrieren",
"Log in with Google": "Mit Google anmelden",
"User ID": "Benutzer-ID",
"Password": "Passwort",
"Time (h:mm:ss):": "Zeit (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Anmelden",
"Register": "Registrieren",
"E-mail": "E-Mail",
"Google verification code": "Google-Bestätigungscode",
"Preferences": "Einstellungen",
"preferences_category_player": "Wiedergabeeinstellungen",
"preferences_video_loop_label": "Immer wiederholen: ",
@ -164,17 +161,12 @@
"Hide replies": "Antworten verstecken",
"Show replies": "Antworten anzeigen",
"Incorrect password": "Falsches Passwort",
"Quota exceeded, try again in a few hours": "Kontingent überschritten, versuche es in ein paar Stunden erneut",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Anmeldung nicht möglich, stellen Sie sicher, dass die Zwei-Faktor-Authentisierung (Authenticator oder SMS) aktiviert ist.",
"Invalid TFA code": "Ungültiger TFA Code",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Die Anmeldung ist fehlgeschlagen. Dies kann daran liegen, dass die Zwei-Faktor-Authentisierung für Ihr Konto nicht aktiviert ist.",
"Wrong answer": "Ungültige Antwort",
"Erroneous CAPTCHA": "Ungültiges CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA ist eine erforderliche Eingabe",
"User ID is a required field": "Benutzer ID ist eine erforderliche Eingabe",
"Password is a required field": "Passwort ist eine erforderliche Eingabe",
"Wrong username or password": "Ungültiger Benutzername oder Passwort",
"Please sign in using 'Log in with Google'": "Bitte melden Sie sich mit „Mit Google anmelden“ an",
"Password cannot be empty": "Passwort darf nicht leer sein",
"Password cannot be longer than 55 characters": "Passwort darf nicht länger als 55 Zeichen sein",
"Please log in": "Bitte anmelden",
@ -325,9 +317,9 @@
"`x` marked it with a ❤": "`x` markierte es mit einem ❤",
"Audio mode": "Audiomodus",
"Video mode": "Videomodus",
"Videos": "Videos",
"channel_tab_videos_label": "Videos",
"Playlists": "Wiedergabelisten",
"Community": "Gemeinschaft",
"channel_tab_community_label": "Gemeinschaft",
"search_filters_sort_option_relevance": "Relevanz",
"search_filters_sort_option_rating": "Bewertung",
"search_filters_sort_option_date": "Datum",
@ -367,7 +359,7 @@
"adminprefs_modified_source_code_url_label": "URL zum Repositorie des modifizierten Quellcodes",
"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_dash": "DASH (adaptive Qualität)",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Mittel",
"preferences_quality_option_small": "Niedrig",
@ -433,7 +425,7 @@
"comments_points_count_plural": "{{count}} Punkte",
"crash_page_you_found_a_bug": "Anscheinend haben Sie einen Fehler in Invidious gefunden!",
"generic_count_months": "{{count}} Monat",
"generic_count_months_plural": "{{count}} Monate",
"generic_count_months_plural": "{{count}} Monaten",
"Cantonese (Hong Kong)": "Kantonesisch (Hong Kong)",
"Chinese (Hong Kong)": "Chinesisch (Hong Kong)",
"generic_playlists_count": "{{count}} Wiedergabeliste",
@ -460,5 +452,28 @@
"Chinese (Taiwan)": "Chinesisch (Taiwan)",
"Korean (auto-generated)": "Koreanisch (automatisch generiert)",
"Portuguese (auto-generated)": "Portugiesisch (automatisch generiert)",
"search_filters_title": "Filtern"
"search_filters_title": "Filtern",
"search_message_change_filters_or_query": "Versuchen Sie, Ihre Suchanfrage zu erweitern und/oder die Filter zu ändern.",
"search_message_use_another_instance": " Sie können auch <a href=\"`x`\">auf einer anderen Instanz suchen</a>.",
"Popular enabled: ": "„Beliebt“-Seite aktiviert: ",
"search_message_no_results": "Keine Ergebnisse gefunden.",
"search_filters_duration_option_medium": "Mittel (4 - 20 Minuten)",
"search_filters_features_option_vr180": "VR180",
"search_filters_type_option_all": "Beliebiger Typ",
"search_filters_apply_button": "Ausgewählte Filter anwenden",
"search_filters_duration_option_none": "Beliebige Länge",
"search_filters_date_label": "Upload-Datum",
"search_filters_date_option_none": "Beliebiges Datum",
"error_video_not_in_playlist": "Das angeforderte Video existiert nicht in dieser Wiedergabeliste. <a href=\"`x`\">Klicken Sie hier, um zur Startseite der Wiedergabeliste zu gelangen.</a>",
"channel_tab_shorts_label": "Shorts",
"channel_tab_streams_label": "Livestreams",
"Music in this video": "Musik in diesem Video",
"Artist: ": "Künstler: ",
"Album: ": "Album: ",
"channel_tab_playlists_label": "Wiedergabelisten",
"channel_tab_channels_label": "Kanäle",
"Channel Sponsor": "Kanalsponsor",
"Standard YouTube license": "Standard YouTube-Lizenz",
"Song: ": "Musik: ",
"Download is disabled": "Herunterladen ist deaktiviert"
}

@ -14,7 +14,6 @@
"Clear watch history?": "Διαγραφή ιστορικού προβολής;",
"New password": "Νέος κωδικός πρόσβασης",
"New passwords must match": "Οι νέοι κωδικοί πρόσβασης πρέπει να ταιριάζουν",
"Cannot change password for Google accounts": "Δεν επιτρέπεται η αλλαγή κωδικού πρόσβασης λογαριασμών Google",
"Authorize token?": "Εξουσιοδότηση διασύνδεσης;",
"Authorize token for `x`?": "Εξουσιοδότηση διασύνδεσης με `x`;",
"Yes": "Ναι",
@ -37,7 +36,6 @@
"source": "πηγή",
"Log in": "Σύνδεση",
"Log in/register": "Σύνδεση/εγγραφή",
"Log in with Google": "Σύνδεση με Google",
"User ID": "Ταυτότητα χρήστη",
"Password": "Κωδικός πρόσβασης",
"Time (h:mm:ss):": "Ώρα (ω:λλ:δδ):",
@ -46,7 +44,6 @@
"Sign In": "Σύνδεση",
"Register": "Εγγραφή",
"E-mail": "Ηλεκτρονικό ταχυδρομείο",
"Google verification code": "Κωδικός επαλήθευσης Google",
"Preferences": "Προτιμήσεις",
"preferences_category_player": "Προτιμήσεις αναπαραγωγής",
"preferences_video_loop_label": "Αυτόματη επανάληψη: ",
@ -155,17 +152,12 @@
"Hide replies": "Απόκρυψη απαντήσεων",
"Show replies": "Προβολή απαντήσεων",
"Incorrect password": "Λανθασμένος κωδικός πρόσβασης",
"Quota exceeded, try again in a few hours": "Έχετε υπερβεί το όριο προσπαθειών, δοκιμάστε ξανα σε λίγες ώρες",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Αδυναμία σύνδεσης, βεβαιωθείτε πως ο έλεγχος ταυτότητας δύο παραγόντων (με Authenticator ή SMS) είναι ενεργοποιημένος.",
"Invalid TFA code": "Μη έγκυρος κωδικός ελέγχου ταυτότητας δύο παραγόντων",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Αποτυχία σύνδεσης. Ίσως ευθύνεται η έλλειψη ελέγχου ταυτότητας δύο παραγόντων για το λογαριασμό σας.",
"Wrong answer": "Λανθασμένη απάντηση",
"Erroneous CAPTCHA": "Λανθασμένο CAPTCHA",
"CAPTCHA is a required field": "Το CAPTCHA είναι απαιτούμενο πεδίο",
"User ID is a required field": "Η ταυτότητα χρήστη είναι απαιτούμενο πεδίο",
"Password is a required field": "Ο κωδικός πρόσβασης είναι απαιτούμενο πεδίο",
"Wrong username or password": "Λανθασμένο όνομα χρήστη ή κωδικός πρόσβασης",
"Please sign in using 'Log in with Google'": "Συνδεθείτε με την επιλογή 'Σύνδεση με Google'",
"Password cannot be empty": "Ο κωδικός πρόσβασης δεν γίνεται να είναι κενός",
"Password cannot be longer than 55 characters": "Ο κωδικός πρόσβασης δεν γίνεται να υπερβαίνει τους 55 χαρακτήρες",
"Please log in": "Συνδεθείτε",
@ -315,9 +307,9 @@
"`x` marked it with a ❤": "Ο χρηστης `x` έβαλε ❤",
"Audio mode": "Λειτουργία ήχου",
"Video mode": "Λειτουργία βίντεο",
"Videos": "Βίντεο",
"channel_tab_videos_label": "Βίντεο",
"Playlists": "Λίστες Αναπαραγωγής",
"Community": "Κοινότητα",
"channel_tab_community_label": "Κοινότητα",
"Current version: ": "Τρέχουσα έκδοση: ",
"generic_playlists_count": "{{count}} λίστα αναπαραγωγής",
"generic_playlists_count_plural": "{{count}} λίστες αναπαραγωγής",
@ -366,7 +358,7 @@
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Μεσαία",
"preferences_quality_option_small": "Μικρό",
"preferences_quality_option_dash": "DASH (προσαρμοστική ποιότητα)",
"preferences_quality_option_dash": "DASH (προσαρμόσιμη ποιότητα)",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_720p": "720p",
"invidious": "Invidious",
@ -449,5 +441,6 @@
"videoinfo_invidious_embed_link": "Σύνδεσμος Ενσωμάτωσης",
"search_filters_type_option_show": "Μπάρα προόδου διαβάσματος",
"preferences_watch_history_label": "Ενεργοποίηση ιστορικού παρακολούθησης: ",
"search_filters_title": "Φίλτρο"
"search_filters_title": "Φίλτρο",
"search_message_no_results": "Δε βρέθηκαν αποτελέσματα."
}

@ -24,7 +24,6 @@
"Clear watch history?": "Clear watch history?",
"New password": "New password",
"New passwords must match": "New passwords must match",
"Cannot change password for Google accounts": "Cannot change password for Google accounts",
"Authorize token?": "Authorize token?",
"Authorize token for `x`?": "Authorize token for `x`?",
"Yes": "Yes",
@ -33,6 +32,7 @@
"Import": "Import",
"Import Invidious data": "Import Invidious JSON data",
"Import YouTube subscriptions": "Import YouTube/OPML subscriptions",
"Import YouTube playlist (.csv)": "Import YouTube playlist (.csv)",
"Import FreeTube subscriptions (.db)": "Import FreeTube subscriptions (.db)",
"Import NewPipe subscriptions (.json)": "Import NewPipe subscriptions (.json)",
"Import NewPipe data (.zip)": "Import NewPipe data (.zip)",
@ -47,7 +47,6 @@
"source": "source",
"Log in": "Log in",
"Log in/register": "Log in/register",
"Log in with Google": "Log in with Google",
"User ID": "User ID",
"Password": "Password",
"Time (h:mm:ss):": "Time (h:mm:ss):",
@ -56,7 +55,6 @@
"Sign In": "Sign In",
"Register": "Register",
"E-mail": "E-mail",
"Google verification code": "Google verification code",
"Preferences": "Preferences",
"preferences_category_player": "Player preferences",
"preferences_video_loop_label": "Always loop: ",
@ -136,6 +134,7 @@
"preferences_default_home_label": "Default homepage: ",
"preferences_feed_menu_label": "Feed menu: ",
"preferences_show_nick_label": "Show nickname on top: ",
"Popular enabled: ": "Popular enabled: ",
"Top enabled: ": "Top enabled: ",
"CAPTCHA enabled: ": "CAPTCHA enabled: ",
"Login enabled: ": "Login enabled: ",
@ -182,11 +181,16 @@
"Show annotations": "Show annotations",
"Genre: ": "Genre: ",
"License: ": "License: ",
"Standard YouTube license": "Standard YouTube license",
"Family friendly? ": "Family friendly? ",
"Wilson score: ": "Wilson score: ",
"Engagement: ": "Engagement: ",
"Whitelisted regions: ": "Whitelisted regions: ",
"Blacklisted regions: ": "Blacklisted regions: ",
"Music in this video": "Music in this video",
"Artist: ": "Artist: ",
"Song: ": "Song: ",
"Album: ": "Album: ",
"Shared `x`": "Shared `x`",
"Premieres in `x`": "Premieres in `x`",
"Premieres `x`": "Premieres `x`",
@ -201,17 +205,12 @@
"Hide replies": "Hide replies",
"Show replies": "Show replies",
"Incorrect password": "Incorrect password",
"Quota exceeded, try again in a few hours": "Quota exceeded, try again in a few hours",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.",
"Invalid TFA code": "Invalid TFA code",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Login failed. This may be because two-factor authentication is not turned on for your account.",
"Wrong answer": "Wrong answer",
"Erroneous CAPTCHA": "Erroneous CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA is a required field",
"User ID is a required field": "User ID is a required field",
"Password is a required field": "Password is a required field",
"Wrong username or password": "Wrong username or password",
"Please sign in using 'Log in with Google'": "Please sign in using 'Log in with Google'",
"Password cannot be empty": "Password cannot be empty",
"Password cannot be longer than 55 characters": "Password cannot be longer than 55 characters",
"Please log in": "Please log in",
@ -396,16 +395,16 @@
"Movies": "Movies",
"Download": "Download",
"Download as: ": "Download as: ",
"Download is disabled": "Download is disabled",
"%A %B %-d, %Y": "%A %B %-d, %Y",
"(edited)": "(edited)",
"YouTube comment permalink": "YouTube comment permalink",
"permalink": "permalink",
"`x` marked it with a ❤": "`x` marked it with a ❤",
"Channel Sponsor": "Channel Sponsor",
"Audio mode": "Audio mode",
"Video mode": "Video mode",
"Videos": "Videos",
"Playlists": "Playlists",
"Community": "Community",
"search_filters_title": "Filters",
"search_filters_date_label": "Upload date",
"search_filters_date_option_none": "Any date",
@ -452,7 +451,7 @@
"footer_documentation": "Documentation",
"footer_source_code": "Source code",
"footer_original_source_code": "Original source code",
"footer_modfied_source_code": "Modified Source code",
"footer_modfied_source_code": "Modified source code",
"adminprefs_modified_source_code_url_label": "URL to modified source code repository",
"none": "none",
"videoinfo_started_streaming_x_ago": "Started streaming `x` ago",
@ -470,5 +469,12 @@
"crash_page_switch_instance": "tried to <a href=\"`x`\">use another instance</a>",
"crash_page_read_the_faq": "read the <a href=\"`x`\">Frequently Asked Questions (FAQ)</a>",
"crash_page_search_issue": "searched for <a href=\"`x`\">existing issues on GitHub</a>",
"crash_page_report_issue": "If none of the above helped, please <a href=\"`x`\">open a new issue on GitHub</a> (preferably in English) and include the following text in your message (do NOT translate that text):"
"crash_page_report_issue": "If none of the above helped, please <a href=\"`x`\">open a new issue on GitHub</a> (preferably in English) and include the following text in your message (do NOT translate that text):",
"error_video_not_in_playlist": "The requested video doesn't exist in this playlist. <a href=\"`x`\">Click here for the playlist home page.</a>",
"channel_tab_videos_label": "Videos",
"channel_tab_shorts_label": "Shorts",
"channel_tab_streams_label": "Livestreams",
"channel_tab_playlists_label": "Playlists",
"channel_tab_community_label": "Community",
"channel_tab_channels_label": "Channels"
}

@ -5,8 +5,8 @@
"Subscribe": "Abonu",
"View channel on YouTube": "Vidu kanalon en JuTubo",
"View playlist on YouTube": "Vidu ludliston en JuTubo",
"newest": "pli novaj",
"oldest": "pli malnovaj",
"newest": "plej novaj",
"oldest": "plej malnovaj",
"popular": "popularaj",
"last": "lasta",
"Next page": "Sekva paĝo",
@ -14,22 +14,21 @@
"Clear watch history?": "Ĉu forigi vidohistorion?",
"New password": "Nova pasvorto",
"New passwords must match": "Novaj pasvortoj devas kongrui",
"Cannot change password for Google accounts": "Ne eblas ŝanĝi pasvorton por kontoj de Google",
"Authorize token?": "Ĉu rajtigi ĵetonon?",
"Authorize token for `x`?": "Ĉu rajtigi ĵetonon por `x`?",
"Yes": "Jes",
"No": "Ne",
"Import and Export Data": "Importi kaj Eksporti Datumojn",
"Import": "Importi",
"Import Invidious data": "Importi datumojn de Invidious",
"Import YouTube subscriptions": "Importi abonojn de JuTubo",
"Import Invidious data": "Importi JSON-datumojn de Invidious",
"Import YouTube subscriptions": "Importi abonojn de YouTube/OPML",
"Import FreeTube subscriptions (.db)": "Importi abonojn de FreeTube (.db)",
"Import NewPipe subscriptions (.json)": "Importi abonojn de NewPipe (.json)",
"Import NewPipe data (.zip)": "Importi datumojn de NewPipe (.zip)",
"Export": "Eksporti",
"Export subscriptions as OPML": "Eksporti abonojn kiel OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksporti abonojn kiel OPML (por NewPipe kaj FreeTube)",
"Export data as JSON": "Eksporti datumojn kiel JSON",
"Export data as JSON": "Eksporti Invidious-datumojn kiel JSON",
"Delete account?": "Ĉu forigi konton?",
"History": "Historio",
"An alternative front-end to YouTube": "Alternativa fasado al JuTubo",
@ -37,7 +36,6 @@
"source": "fonto",
"Log in": "Ensaluti",
"Log in/register": "Ensaluti/Registriĝi",
"Log in with Google": "Ensaluti al Google",
"User ID": "Uzula identigilo",
"Password": "Pasvorto",
"Time (h:mm:ss):": "Horo (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Ensaluti",
"Register": "Registriĝi",
"E-mail": "Retpoŝto",
"Google verification code": "Kontrolkodo de Google",
"Preferences": "Agordoj",
"preferences_category_player": "Spektilaj agordoj",
"preferences_video_loop_label": "Ĉiam ripeti: ",
@ -66,7 +63,7 @@
"preferences_related_videos_label": "Ĉu montri rilatajn filmetojn? ",
"preferences_annotations_label": "Ĉu montri prinotojn defaŭlte? ",
"preferences_extend_desc_label": "Aŭtomate etendi priskribon de filmeto: ",
"preferences_vr_mode_label": "Interagaj 360-gradaj filmetoj: ",
"preferences_vr_mode_label": "Interagaj 360-gradaj filmoj (postulas WebGL-n): ",
"preferences_category_visual": "Vidaj preferoj",
"preferences_player_style_label": "Ludila stilo: ",
"Dark mode: ": "Malhela reĝimo: ",
@ -75,7 +72,7 @@
"light": "hela",
"preferences_thin_mode_label": "Maldika reĝimo: ",
"preferences_category_misc": "Aliaj agordoj",
"preferences_automatic_instance_redirect_label": "Aŭtomata alidirektado de instalaĵo (retropaŝo al redirect.invidious.io): ",
"preferences_automatic_instance_redirect_label": "Aŭtomata alidirektado de nodo (retropaŝo al redirect.invidious.io): ",
"preferences_category_subscription": "Abonaj agordoj",
"preferences_annotations_subscribed_label": "Ĉu montri prinotojn defaŭlte por abonitaj kanaloj? ",
"Redirect homepage to feed: ": "Alidirekti hejmpâgon al fluo: ",
@ -140,7 +137,7 @@
"Show more": "Montri pli",
"Show less": "Montri malpli",
"Watch on YouTube": "Vidi filmeton en JuTubo",
"Switch Invidious Instance": "Ŝanĝi instalaĵon de Indivious",
"Switch Invidious Instance": "Ŝanĝi nodon de Indivious",
"Hide annotations": "Kaŝi prinotojn",
"Show annotations": "Montri prinotojn",
"Genre: ": "Ĝenro: ",
@ -164,17 +161,12 @@
"Hide replies": "Kaŝi respondojn",
"Show replies": "Montri respondojn",
"Incorrect password": "Malbona pasvorto",
"Quota exceeded, try again in a few hours": "Kvoto transpasita, provu denove post iuj horoj",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Ne povas ensaluti, certigu, ke dufaktora aŭtentigo (Authenticator aŭ SMS) estas ebligita.",
"Invalid TFA code": "Nevalida TFA-kodo",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Ensalutado fiaskis. Eble ĉar la dufaktora aŭtentigo estas malebligita en via konto.",
"Wrong answer": "Nevalida respondo",
"Erroneous CAPTCHA": "Nevalida CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA estas deviga kampo",
"User ID is a required field": "Uzula identigilo estas deviga kampo",
"Password is a required field": "Pasvorto estas deviga kampo",
"Wrong username or password": "Nevalida uzantnomo aŭ pasvorto",
"Please sign in using 'Log in with Google'": "Bonvolu ensaluti per 'Ensaluti per Google'",
"Password cannot be empty": "Pasvorto ne povas esti malplena",
"Password cannot be longer than 55 characters": "Pasvorto ne povas esti pli longa ol 55 signoj",
"Please log in": "Bonvolu ensaluti",
@ -325,9 +317,9 @@
"`x` marked it with a ❤": "`x` markis ĝin per ❤",
"Audio mode": "Aŭda reĝimo",
"Video mode": "Videa reĝimo",
"Videos": "Filmetoj",
"channel_tab_videos_label": "Videoj",
"Playlists": "Ludlistoj",
"Community": "Komunumo",
"channel_tab_community_label": "Komunumo",
"search_filters_sort_option_relevance": "rilateco",
"search_filters_sort_option_rating": "takso",
"search_filters_sort_option_date": "dato",
@ -368,5 +360,121 @@
"footer_donate_page": "Donaci",
"preferences_region_label": "Lando de la enhavo: ",
"preferences_quality_dash_label": "Preferata DASH-a videkvalito: ",
"search_filters_title": "Filtri"
"search_filters_title": "Filtri",
"preferences_quality_dash_option_best": "Plej bona",
"preferences_quality_dash_option_worst": "Malplej bona",
"Popular enabled: ": "Populara sekcio ebligita: ",
"search_message_no_results": "Neniu rezulto trovita.",
"search_message_use_another_instance": " Vi ankaŭ povas <a href=\"`x`\">serĉi en alia nodo</a>.",
"tokens_count": "{{count}} ĵetono",
"tokens_count_plural": "{{count}} ĵetonoj",
"subscriptions_unseen_notifs_count": "{{count}} nevidita sciigo",
"subscriptions_unseen_notifs_count_plural": "{{count}} neviditaj sciigoj",
"Indonesian (auto-generated)": "Indonezia (aŭtomate generita)",
"Interlingue": "Interlingvo",
"Italian (auto-generated)": "Itala (aŭtomate generita)",
"Korean (auto-generated)": "Korea (aŭtomate generita)",
"Portuguese (Brazil)": "Portugala (Brazilo)",
"Portuguese (auto-generated)": "Portugala (aŭtomate generita)",
"Russian (auto-generated)": "Rusa (aŭtomate generita)",
"Spanish (Spain)": "Hispana (Hispanio)",
"generic_count_years": "{{count}} jaro",
"generic_count_years_plural": "{{count}} jaroj",
"Turkish (auto-generated)": "Turka (aŭtomate generita)",
"Vietnamese (auto-generated)": "Vjetnama (aŭtomate generita)",
"generic_count_hours": "{{count}} horo",
"generic_count_hours_plural": "{{count}} horoj",
"generic_count_minutes": "{{count}} minuto",
"generic_count_minutes_plural": "{{count}} minutoj",
"search_filters_date_label": "Alŝutdato",
"search_filters_date_option_none": "Ajna dato",
"search_filters_duration_option_medium": "Meza (4 - 20 minutoj)",
"search_filters_features_option_three_sixty": "360º",
"search_filters_features_option_vr180": "VR180",
"user_created_playlists": "`x`kreitaj ludlistoj",
"user_saved_playlists": "`x`konservitaj ludlistoj",
"crash_page_switch_instance": "klopodis <a href=\"`x`\">uzi alian nodon</a>",
"crash_page_read_the_faq": "legis la <a href=\"`x`\">oftajn demandojn</a>",
"error_video_not_in_playlist": "La petita video ne ekzistas en ĉi tiu ludlisto. <a href=\"`x`\">Alklaku ĉi tie por iri al la ludlista hejmpaĝo.</a>",
"crash_page_search_issue": "serĉis por <a href=\"`x`\">ekzistantaj problemoj en GitHub</a>",
"generic_count_seconds": "{{count}} sekundo",
"generic_count_seconds_plural": "{{count}} sekundoj",
"preferences_quality_dash_option_144p": "144p",
"comments_view_x_replies": "Vidi {{count}} respondon",
"comments_view_x_replies_plural": "Vidi {{count}} respondojn",
"preferences_quality_dash_option_360p": "360p",
"invidious": "Invidious",
"Chinese (Taiwan)": "Ĉina (Tajvano)",
"English (United Kingdom)": "Angla (Britio)",
"search_filters_features_option_purchased": "Aĉetita",
"Japanese (auto-generated)": "Japana (aŭtomate generita)",
"search_message_change_filters_or_query": "Provu vastigi vian serĉpeton kaj/aŭ ŝanĝi la filtrilojn.",
"preferences_quality_dash_option_1080p": "1080p",
"generic_count_weeks": "{{count}} semajno",
"generic_count_weeks_plural": "{{count}} semajnoj",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_auto": "Aŭtomate",
"preferences_quality_dash_option_2160p": "2160p",
"English (United States)": "Angla (Usono)",
"Chinese": "Ĉina",
"videoinfo_watch_on_youTube": "Vidi en YouTube",
"crash_page_you_found_a_bug": "Ŝajnas, ke vi trovis eraron en Invidious!",
"comments_points_count": "{{count}} poento",
"comments_points_count_plural": "{{count}} poentoj",
"Cantonese (Hong Kong)": "Kantona (Honkongo)",
"preferences_watch_history_label": "Ebligi vidohistorion: ",
"preferences_quality_option_small": "Eta",
"generic_playlists_count": "{{count}} ludlisto",
"generic_playlists_count_plural": "{{count}} ludlistoj",
"videoinfo_youTube_embed_link": "Enigi",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Meza",
"generic_subscriptions_count": "{{count}} abono",
"generic_subscriptions_count_plural": "{{count}} abonoj",
"videoinfo_started_streaming_x_ago": "Komercis elsendi antaŭ `x`",
"download_subtitles": "Subtitoloj - `x` (.vtt)",
"videoinfo_invidious_embed_link": "Enigi Ligilon",
"crash_page_report_issue": "Se neniu el la antaŭaj agoj helpis, bonvolu <a href=\"`x`\">estigi novan problemon en GitHub</a> (prefere angle) kaj inkludi la jenan tekston en via mesaĝo (NE traduku tiun tekston):",
"preferences_quality_option_dash": "DASH (adapta kvalito)",
"Chinese (Hong Kong)": "Ĉina (Honkongo)",
"Chinese (China)": "Ĉina (Ĉinio)",
"Dutch (auto-generated)": "Nederlanda (aŭtomate generita)",
"German (auto-generated)": "Germana (aŭtomate generita)",
"French (auto-generated)": "Franca (aŭtomate generita)",
"Spanish (Mexico)": "Hispana (Meksiko)",
"Spanish (auto-generated)": "Hispana (aŭtomate generita)",
"generic_count_days": "{{count}} jaro",
"generic_count_days_plural": "{{count}} jaroj",
"search_filters_type_option_all": "Ajna speco",
"search_filters_duration_option_none": "Ajna daŭro",
"search_filters_apply_button": "Uzi elektitajn filtrilojn",
"none": "neniu",
"Video unavailable": "Nedisponebla video",
"crash_page_before_reporting": "Antaŭ ol informi pri eraro certigu, ke vi:",
"crash_page_refresh": "klopodis <a href=\"`x`\">reŝarĝi la paĝon</a>",
"generic_views_count": "{{count}} spekto",
"generic_views_count_plural": "{{count}} spektoj",
"generic_videos_count": "{{count}} video",
"generic_videos_count_plural": "{{count}} videoj",
"generic_subscribers_count": "{{count}} abonanto",
"generic_subscribers_count_plural": "{{count}} abonantoj",
"generic_count_months": "{{count}} monato",
"generic_count_months_plural": "{{count}} monatoj",
"preferences_save_player_pos_label": "Konservi ludadan pozicion: ",
"channel_tab_streams_label": "Tujelsendoj",
"channel_tab_playlists_label": "Ludlistoj",
"channel_tab_channels_label": "Kanaloj",
"channel_tab_shorts_label": "Mallongaj",
"Music in this video": "Muziko en ĉi tiu video",
"Artist: ": "Artisto: ",
"Album: ": "Albumo: ",
"Channel Sponsor": "Kanala sponsoro",
"Song: ": "Muzikaĵo: ",
"Standard YouTube license": "Implicita YouTube-licenco",
"Download is disabled": "Elŝuto estas malebligita",
"Import YouTube playlist (.csv)": "Importi YouTube-ludliston (.csv)"
}

@ -14,7 +14,6 @@
"Clear watch history?": "¿Quiere borrar el historial de reproducción?",
"New password": "Nueva contraseña",
"New passwords must match": "Las nuevas contraseñas deben coincidir",
"Cannot change password for Google accounts": "No se puede cambiar la contraseña de la cuenta de Google",
"Authorize token?": "¿Autorizar el token?",
"Authorize token for `x`?": "¿Autorizar el token para `x`?",
"Yes": "Sí",
@ -37,7 +36,6 @@
"source": "código fuente",
"Log in": "Iniciar sesión",
"Log in/register": "Iniciar sesión/Registrarse",
"Log in with Google": "Iniciar sesión en Google",
"User ID": "Nombre",
"Password": "Contraseña",
"Time (h:mm:ss):": "Hora (h:mm:ss):",
@ -46,27 +44,26 @@
"Sign In": "Iniciar sesión",
"Register": "Registrarse",
"E-mail": "Correo",
"Google verification code": "Código de verificación de Google",
"Preferences": "Preferencias",
"preferences_category_player": "Preferencias del reproductor",
"preferences_video_loop_label": "Repetir siempre: ",
"preferences_autoplay_label": "Reproducción automática: ",
"preferences_continue_label": "Reproducir siguiente por defecto: ",
"preferences_continue_autoplay_label": "Reproducir automáticamente el vídeo siguiente: ",
"preferences_continue_autoplay_label": "Reproducir automáticamente el video siguiente: ",
"preferences_listen_label": "Activar el sonido por defecto: ",
"preferences_local_label": "¿Usar un proxy para los vídeos? ",
"preferences_local_label": "¿Usar un proxy para los videos? ",
"preferences_speed_label": "Velocidad por defecto: ",
"preferences_quality_label": "Calidad de vídeo preferida: ",
"preferences_quality_label": "Calidad de video preferida: ",
"preferences_volume_label": "Volumen del reproductor: ",
"preferences_comments_label": "Comentarios por defecto: ",
"youtube": "YouTube",
"reddit": "Reddit",
"preferences_captions_label": "Subtítulos por defecto: ",
"Fallback captions: ": "Subtítulos alternativos: ",
"preferences_related_videos_label": "¿Mostrar vídeos relacionados? ",
"preferences_related_videos_label": "¿Mostrar videos relacionados? ",
"preferences_annotations_label": "¿Mostrar anotaciones por defecto? ",
"preferences_extend_desc_label": "Extender automáticamente la descripción del vídeo: ",
"preferences_vr_mode_label": "Vídeos interactivos de 360 grados (necesita WebGL): ",
"preferences_extend_desc_label": "Extender automáticamente la descripción del video: ",
"preferences_vr_mode_label": "Videos interactivos de 360 grados (necesita WebGL): ",
"preferences_category_visual": "Preferencias visuales",
"preferences_player_style_label": "Estilo de reproductor: ",
"Dark mode: ": "Modo oscuro: ",
@ -79,16 +76,16 @@
"preferences_category_subscription": "Preferencias de la suscripción",
"preferences_annotations_subscribed_label": "¿Mostrar anotaciones por defecto para los canales suscritos? ",
"Redirect homepage to feed: ": "Redirigir la página de inicio a la fuente: ",
"preferences_max_results_label": "Número de vídeos mostrados en la fuente: ",
"preferences_sort_label": "Ordenar los vídeos por: ",
"preferences_max_results_label": "Número de videos mostrados en la fuente: ",
"preferences_sort_label": "Ordenar los videos por: ",
"published": "fecha de publicación",
"published - reverse": "fecha de publicación: orden inverso",
"alphabetically": "alfabéticamente",
"alphabetically - reverse": "alfabéticamente: orden inverso",
"channel name": "nombre del canal",
"channel name - reverse": "nombre del canal: orden inverso",
"Only show latest video from channel: ": "Mostrar solo el último vídeo del canal: ",
"Only show latest unwatched video from channel: ": "Mostrar solo el último vídeo sin ver del canal: ",
"Only show latest video from channel: ": "Mostrar solo el último video del canal: ",
"Only show latest unwatched video from channel: ": "Mostrar solo el último video sin ver del canal: ",
"preferences_unseen_only_label": "Mostrar solo los no vistos: ",
"preferences_notifications_only_label": "Mostrar solo notificaciones (si hay alguna): ",
"Enable web notifications": "Habilitar notificaciones web",
@ -114,7 +111,7 @@
"Save preferences": "Guardar las preferencias",
"Subscription manager": "Gestor de suscripciones",
"Token manager": "Gestor de tokens",
"Token": "Token",
"Token": "Ficha",
"Import/export": "Importar/Exportar",
"unsubscribe": "Desuscribirse",
"revoke": "revocar",
@ -139,7 +136,7 @@
"Editing playlist `x`": "Editando la lista de reproducción 'x'",
"Show more": "Mostrar más",
"Show less": "Mostrar menos",
"Watch on YouTube": "Ver el vídeo en YouTube",
"Watch on YouTube": "Ver en YouTube",
"Switch Invidious Instance": "Cambiar Instancia de Invidious",
"Hide annotations": "Ocultar anotaciones",
"Show annotations": "Mostrar anotaciones",
@ -153,7 +150,7 @@
"Shared `x`": "Compartido `x`",
"Premieres in `x`": "Se estrena en `x`",
"Premieres `x`": "Estrenos `x`",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "¡Hola! Parece que tiene JavaScript desactivado. Haga clic aquí para ver los comentarios, pero tenga en cuenta que pueden tardar un poco más en cargarse.",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "¡Hola! Parece que tienes JavaScript desactivado. Haz clic aquí para ver los comentarios, pero tengas en cuenta que pueden tardar un poco más en cargarse.",
"View YouTube comments": "Ver los comentarios de YouTube",
"View more comments on Reddit": "Ver más comentarios en Reddit",
"View `x` comments": {
@ -164,19 +161,14 @@
"Hide replies": "Ocultar las respuestas",
"Show replies": "Mostrar las respuestas",
"Incorrect password": "Contraseña incorrecta",
"Quota exceeded, try again in a few hours": "Cuota excedida, pruebe otra vez en unas horas",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "No se puede iniciar sesión, asegúrese de que la autentificación de dos factores (autentificador o SMS) esté habilitada.",
"Invalid TFA code": "Código TFA no válido",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Error de inicio de sesion. Puede deberse a que la autentificación de dos factores no está habilitada en su cuenta.",
"Wrong answer": "Respuesta no válida",
"Erroneous CAPTCHA": "CAPTCHA no válido",
"CAPTCHA is a required field": "El CAPTCHA es un campo obligatorio",
"User ID is a required field": "El nombre es un campo obligatorio",
"Password is a required field": "La contraseña es un campo obligatorio",
"Wrong username or password": "Nombre o contraseña incorrecto",
"Please sign in using 'Log in with Google'": "Inicie sesión con «Iniciar sesión con Google»",
"Password cannot be empty": "La contraseña no puede estar en blanco",
"Password cannot be longer than 55 characters": "La contraseña no puede tener más de 55 caracteres",
"Password cannot be longer than 55 characters": "La contraseña no debe tener más de 55 caracteres",
"Please log in": "Inicie sesión, por favor",
"Invidious Private Feed for `x`": "Fuente privada de Invidious para `x`",
"channel:`x`": "canal: `x`",
@ -198,7 +190,7 @@
"No such user": "Usuario no existe",
"Token is expired, please try again": "El símbolo ha caducado, inténtelo de nuevo",
"English": "Inglés",
"English (auto-generated)": "Inglés (generados automáticamente)",
"English (auto-generated)": "Inglés (generado automáticamente)",
"Afrikaans": "Afrikáans",
"Albanian": "Albanés",
"Amharic": "Amárico",
@ -324,50 +316,51 @@
"permalink": "enlace permanente",
"`x` marked it with a ❤": "`x` lo ha marcado con un ❤",
"Audio mode": "Modo de audio",
"Video mode": "Modo de vídeo",
"Videos": "Vídeos",
"Video mode": "Modo de video",
"channel_tab_videos_label": "Videos",
"Playlists": "Listas de reproducción",
"Community": "Comunidad",
"search_filters_sort_option_relevance": "relevancia",
"search_filters_sort_option_rating": "valoración",
"search_filters_sort_option_date": "fecha",
"search_filters_sort_option_views": "visualizaciones",
"search_filters_type_label": "content_type",
"channel_tab_community_label": "Comunidad",
"search_filters_sort_option_relevance": "Relevancia",
"search_filters_sort_option_rating": "Valoración",
"search_filters_sort_option_date": "Fecha de subida",
"search_filters_sort_option_views": "Visualizaciones",
"search_filters_type_label": "tipo de contenido",
"search_filters_duration_label": "duración",
"search_filters_features_label": "funcionalidades",
"search_filters_sort_label": "ordenar",
"search_filters_date_option_hour": "hora",
"search_filters_date_option_today": "hoy",
"search_filters_date_option_week": "semana",
"search_filters_date_option_month": "mes",
"search_filters_date_option_year": "año",
"search_filters_type_option_video": "deo",
"search_filters_type_option_channel": "canal",
"search_filters_type_option_playlist": "lista de reproducción",
"search_filters_type_option_movie": "película",
"search_filters_type_option_show": "programa",
"search_filters_features_option_hd": "hd",
"search_filters_features_option_subtitles": "subtítulos",
"search_filters_features_option_c_commons": "creative_commons",
"search_filters_features_option_three_d": "3d",
"search_filters_features_option_live": "directo",
"search_filters_features_option_four_k": "4k",
"search_filters_features_option_location": "ubicación",
"search_filters_features_option_hdr": "hdr",
"search_filters_date_option_hour": "Última hora",
"search_filters_date_option_today": "Hoy",
"search_filters_date_option_week": "Esta semana",
"search_filters_date_option_month": "Este mes",
"search_filters_date_option_year": "Este año",
"search_filters_type_option_video": "Video",
"search_filters_type_option_channel": "Canal",
"search_filters_type_option_playlist": "Lista de reproducción",
"search_filters_type_option_movie": "Película",
"search_filters_type_option_show": "Programa",
"search_filters_features_option_hd": "HD",
"search_filters_features_option_subtitles": "Subtítulos",
"search_filters_features_option_c_commons": "Creative Commons",
"search_filters_features_option_three_d": "3D",
"search_filters_features_option_live": "En directo",
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "Ubicación",
"search_filters_features_option_hdr": "HDR",
"Current version: ": "Versión actual: ",
"next_steps_error_message": "Después de lo cual deberías intentar: ",
"next_steps_error_message": "Después de lo cual debes intentar: ",
"next_steps_error_message_refresh": "Recargar la página",
"next_steps_error_message_go_to_youtube": "Ir a YouTube",
"search_filters_duration_option_short": "Corto (< 4 minutos)",
"search_filters_duration_option_long": "Largo (> 20 minutos)",
"search_filters_duration_option_short": "Menos de 4 minutos",
"search_filters_duration_option_medium": "De 4 a 20 minutos",
"search_filters_duration_option_long": "Más de 20 minutos",
"footer_documentation": "Documentación",
"footer_original_source_code": "Código fuente original",
"adminprefs_modified_source_code_url_label": "URL al repositorio de código fuente modificado",
"adminprefs_modified_source_code_url_label": "Enlace al repositorio de código fuente modificado",
"footer_source_code": "Código fuente",
"footer_modfied_source_code": "Código fuente modificado",
"footer_donate_page": "Donar",
"preferences_region_label": "País del contenido: ",
"preferences_quality_dash_label": "Calidad de vídeo DASH preferida: ",
"preferences_quality_dash_label": "Calidad de video DASH preferida: ",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Intermedia",
"preferences_quality_dash_option_auto": "Automática",
@ -376,7 +369,7 @@
"download_subtitles": "Subtítulos- `x` (.vtt)",
"user_created_playlists": "`x` listas de reproducción creadas",
"user_saved_playlists": "`x` listas de reproducción guardadas",
"Video unavailable": "Vídeo no disponible",
"Video unavailable": "Video no disponible",
"videoinfo_youTube_embed_link": "Insertar",
"preferences_quality_dash_option_2160p": "2160p",
"preferences_quality_dash_option_4320p": "4320p",
@ -412,9 +405,9 @@
"generic_count_weeks": "{{count}} semana",
"generic_count_weeks_plural": "{{count}} semanas",
"generic_playlists_count": "{{count}} lista de reproducción",
"generic_playlists_count_plural": "{{count}} listas de reproducción",
"generic_videos_count": "{{count}} vídeo",
"generic_videos_count_plural": "{{count}} vídeos",
"generic_playlists_count_plural": "{{count}} listas de reproducciones",
"generic_videos_count": "{{count}} video",
"generic_videos_count_plural": "{{count}} video",
"generic_count_months": "{{count}} mes",
"generic_count_months_plural": "{{count}} meses",
"comments_points_count": "{{count}} punto",
@ -433,7 +426,7 @@
"crash_page_search_issue": "buscado <a href=\"`x`\">problemas existentes en GitHub</a>",
"crash_page_you_found_a_bug": "¡Parece que has encontrado un error en Invidious!",
"crash_page_refresh": "probado a <a href=\"`x`\">recargar la página</a>",
"crash_page_report_issue": "Si nada de lo anterior ha sido de ayuda, por favor, <a href=\"`x`\">abre una nueva incidencia en GitHub</a> (preferiblemente en inglés) e incluye el siguiente texto en tu mensaje (NO traduzcas este texto):",
"crash_page_report_issue": "Si nada de lo anterior ha sido de ayuda, por favor, <a href=\"`x`\">abre una nueva incidencia en GitHub</a> (preferiblemente en inglés) e incluye verbatim el siguiente texto en tu mensaje:",
"English (United States)": "Inglés (Estados Unidos)",
"Cantonese (Hong Kong)": "Cantonés (Hong Kong)",
"Dutch (auto-generated)": "Neerlandés (generados automáticamente)",
@ -461,14 +454,27 @@
"search_message_no_results": "No se han encontrado resultados.",
"search_message_change_filters_or_query": "Pruebe ampliar la consulta de búsqueda y/o a cambiar los filtros.",
"search_filters_title": "Filtros",
"search_filters_date_label": "Fecha de subida",
"search_filters_date_label": "fecha de subida",
"search_filters_date_option_none": "Cualquier fecha",
"search_filters_type_option_all": "Cualquier tipo",
"search_filters_duration_option_none": "Cualquier duración",
"search_filters_features_option_vr180": "VR180",
"search_filters_apply_button": "Aplicar filtros seleccionados",
"search_filters_apply_button": "Aplicar filtros",
"tokens_count": "{{count}} token",
"tokens_count_plural": "{{count}} tokens",
"search_message_use_another_instance": " También puede <a href=\"`x`\">buscar en otra instancia</a>.",
"search_filters_duration_option_medium": "Medio (4 - 20 minutes)"
"Popular enabled: ": "¿Habilitar la sección popular? ",
"error_video_not_in_playlist": "El video que solicitaste no existe en esta lista de reproducción. <a href=\"`x`\">Haz clic aquí para acceder a la página de inicio de la lista de reproducción.</a>",
"channel_tab_streams_label": "Directos",
"channel_tab_channels_label": "Canales",
"channel_tab_shorts_label": "Cortos",
"channel_tab_playlists_label": "Listas de reproducción",
"Music in this video": "Música en este video",
"Artist: ": "Artista: ",
"Album: ": "Álbum: ",
"Song: ": "Canción: ",
"Channel Sponsor": "Patrocinador del canal",
"Standard YouTube license": "Licencia de YouTube estándar",
"Download is disabled": "La descarga está deshabilitada",
"Import YouTube playlist (.csv)": "Importar lista de reproducción de YouTube (.csv)"
}

@ -25,7 +25,6 @@
"Clear watch history?": "Kustuta vaatamiste ajalugu?",
"New password": "Uus salasõna",
"New passwords must match": "Uued salasõnad peavad ühtima",
"Cannot change password for Google accounts": "Google'i kasutaja salasõna ei saa muuta",
"Import and Export Data": "Impordi ja ekspordi andmed",
"Import": "Impordi",
"Import YouTube subscriptions": "Impordi tellimused Youtube'ist/OPML-ist",
@ -38,7 +37,6 @@
"History": "Ajalugu",
"JavaScript license information": "JavaScripti litsentsi info",
"source": "allikas",
"Log in with Google": "Logi sisse Google'iga",
"User ID": "Kasutada ID",
"Password": "Salasõna",
"Time (h:mm:ss):": "Aeg (h:mm:ss):",
@ -118,12 +116,10 @@
"Hide replies": "Peida vastused",
"Show replies": "Näita vastuseid",
"Incorrect password": "Vale salasõna",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Sisselogimine ei õnnestunud. Asi võib olla selles, et",
"Wrong answer": "Vale vastus",
"User ID is a required field": "Kasutaja ID on kohustuslik väli",
"Password is a required field": "Salasõna on kohustuslik väli",
"Wrong username or password": "Vale kasutajanimi või salasõna",
"Please sign in using 'Log in with Google'": "Palun kasutage 'Logi sisse Google'iga'",
"Password cannot be longer than 55 characters": "Salasõna ei tohi olla pikem kui 55 tähemärki",
"Password cannot be empty": "Salasõna ei tohi olla tühi",
"Please log in": "Palun logige sisse",
@ -290,14 +286,12 @@
"": "Vaata `x` kommentaare"
},
"Khmer": "Khmeeri",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Sisselogimine ei õnnestunud. Kontrollige, kas two-factor authentication (Authenticator või SMS) on sisselülitatud.",
"Invalid TFA code": "Vale TFA-kood",
"Bosnian": "Bosnia",
"Corsican": "Korsika",
"Javanese": "Jaava",
"Lithuanian": "Leedu",
"Videos": "Videod",
"Community": "Kogukond",
"channel_tab_videos_label": "Videod",
"channel_tab_community_label": "Kogukond",
"CAPTCHA is a required field": "CAPTCHA on kohustuslik väli",
"comments_points_count": "{{count}} punkt",
"comments_points_count_plural": "{{count}} punkti",

@ -14,7 +14,6 @@
"Clear watch history?": "Garbitu ikusitakoen historia?",
"New password": "Pasahitz berria",
"New passwords must match": "Pasahitza berriek bat egin behar dute",
"Cannot change password for Google accounts": "Ezin da pasahitza aldatu Google kontuetan",
"Authorize token?": "Baimendu tokena?",
"Yes": "Bai",
"No": "Ez",
@ -36,7 +35,6 @@
"source": "iturburua",
"Log in": "Saioa hasi",
"Log in/register": "Hasi saioa / Eman izena",
"Log in with Google": "Hasi saioa Googlekin",
"User ID": "Erabiltzaile IDa",
"Password": "Pasahitza",
"Time (h:mm:ss):": "Denbora (h:mm:ss):",
@ -93,7 +91,6 @@
"Import/export data": "Inportatu/exportatu data",
"Create playlist": "Zerrenda sortu",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Aditu! JavaScript itzalita dakazula ematen du. Hemen sakatu iruzkinak ikusteko. Denbora luza leikeela kontuan hartu.",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Ezinezkoa izena eman. Ziurtatu berresteko bi faktoreak (Authenticator edo SMS) piztuta daudela.",
"generic_views_count": "{{count}}ikusia",
"generic_views_count_plural": "{{count}}ikusiak",
"generic_playlists_count": "{{count}}zerrenda",
@ -136,7 +133,6 @@
"License: ": "Lizentzia: ",
"Family friendly? ": "Adeikorra familiarekin? ",
"Wilson score: ": "Wilsonen puntuazioa: ",
"Quota exceeded, try again in a few hours": "Kuota gaindituta, ordu batzuren bueltan berriro saiatu",
"comments_view_x_replies": "{{count}} erantzuna ikusi",
"comments_view_x_replies_plural": "{{count}} erantzunak ikusi",
"Catalan": "Katalaniera",
@ -204,7 +200,6 @@
"preferences_category_data": "Dataren lehentasunak",
"preferences_default_home_label": "Homepage lehenetsia: ",
"preferences_automatic_instance_redirect_label": "berbideratze adibide automatikoa (atzera egin berbideratzeko: invidious.io) ",
"Please sign in using 'Log in with Google'": "'Log in Googlerekin' erabili",
"`x` uploaded a video": "' x'(e)k bideo bat igo du",
"published - reverse": "argitaratuta - alderantziz",
"Could not get channel info.": "Kanalaren adierazpena ezin lortu.",
@ -220,7 +215,6 @@
"Premieres in `x`": "'x'eko estrenaldiak",
"Delete playlist `x`?": "'x' zerrenda ezabatu nahi?",
"Token is expired, please try again": "Token kadukatua, saiatu berriro",
"Invalid TFA code": "TFA kodea ez da zuzena",
"CAPTCHA enabled: ": "CAPTCHA gaitu: ",
"Released under the AGPLv3 on Github.": "GitHubeko AGPLv3pean argitaratuta.",
"channel:`x`": "Kanal: 'x'",
@ -242,9 +236,7 @@
"preferences_category_subscription": "Harpidetzaren lehentasunak",
"Hidden field \"challenge\" is a required field": "\"challenge\" eremu ezkutua beharrezkoa da",
"German": "Alemaniarra",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Ezin izena eman. Izan leike zure konturako berresteko bi faktoreak piztuta ez daudela.",
"View YouTube comments": "YouTubeko iruzkinak ikusi",
"Google verification code": "Googleren berresteko kodea",
"`x` is live": "'x' bizirik darrai",
"Password cannot be empty": "Pasahitza ezin da hutsik utzi",
"preferences_video_loop_label": "Beti begiztatu: ",

@ -19,22 +19,21 @@
"Clear watch history?": "پاک کردن تاریخچه نمایش؟",
"New password": "گذرواژه تازه",
"New passwords must match": "گذارواژه های تازه باید باهم همخوانی داشته باشند",
"Cannot change password for Google accounts": "نمیتوان گذرواژه را برای حساب های کاربری گوگل تغییر داد",
"Authorize token?": "توکن دسترسی؟",
"Authorize token for `x`?": "توکن دسترسی برای `x`؟",
"Yes": "بله",
"No": "خیر",
"Import and Export Data": "درون‌برد و برون‌برد داده",
"Import": "درون‌برد",
"Import Invidious data": "درون‌برد داده اینویدیوس",
"Import YouTube subscriptions": "درون‌برد اشتراک‌های یوتیوب",
"Import Invidious data": "وارد کردن داده JSON اینویدیوس",
"Import YouTube subscriptions": "وارد کردن اشتراک OPML/ یوتیوب",
"Import FreeTube subscriptions (.db)": "درون‌برد اشتراک‌های فری‌تیوب (.db)",
"Import NewPipe subscriptions (.json)": "درون‌برد اشتراک‌های نیوپایپ (.json)",
"Import NewPipe data (.zip)": "درون‌برد داده نیوپایپ (.zip)",
"Export": "برون‌برد",
"Export subscriptions as OPML": "برون‌برد اشتراک‌ها در قالب OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "برون‌برد اشتراک‌ها در قالب OPML (برای نیوپایپ و فری‌تیوب)",
"Export data as JSON": "برون‌برد داده در قالب JSON",
"Export data as JSON": "گرفتن(خارج کردن) اطلاعات اینویدیوس با فرمت JSON",
"Delete account?": "حذف حساب کاربری؟",
"History": "تاریخچه",
"An alternative front-end to YouTube": "یک پیشانه جایگزین برای یوتیوب",
@ -42,7 +41,6 @@
"source": "منبع",
"Log in": "ورود",
"Log in/register": "ورود/ثبت نام",
"Log in with Google": "ورود با گوگل",
"User ID": "شناسه کاربری",
"Password": "گذرواژه",
"Time (h:mm:ss):": "زمان (h:mm:ss):",
@ -51,7 +49,6 @@
"Sign In": "ورود",
"Register": "ثبت نام",
"E-mail": "ایمیل",
"Google verification code": "کد تایید گوگل",
"Preferences": "ترجیحات",
"preferences_category_player": "ترجیحات نمایش‌دهنده",
"preferences_video_loop_label": "همواره ویدئو را بازپخش کن ",
@ -71,7 +68,7 @@
"preferences_related_videos_label": "نمایش ویدیو های مرتبط: ",
"preferences_annotations_label": "نمایش حاشیه نویسی ها به طور پیشفرض: ",
"preferences_extend_desc_label": "گسترش خودکار توضیحات ویدئو: ",
"preferences_vr_mode_label": "ویدئوها ۳۶۰ درجه تعاملی: ",
"preferences_vr_mode_label": "ویدئوها ۳۶۰ درجه تعاملی(نیازمند WebGL): ",
"preferences_category_visual": "ترجیحات بصری",
"preferences_player_style_label": "حالت پخش کننده: ",
"Dark mode: ": "حالت تاریک: ",
@ -80,7 +77,7 @@
"light": "روشن",
"preferences_thin_mode_label": "حالت نازک: ",
"preferences_category_misc": "ترجیحات متفرقه",
"preferences_automatic_instance_redirect_label": "هدایت خودکار نمونه (به طور پیش‌فرض به redirect.invidious.io): ",
"preferences_automatic_instance_redirect_label": "هدایت خودکار نمونه (انتقال به redirect.invidious.io): ",
"preferences_category_subscription": "ترجیحات اشتراک",
"preferences_annotations_subscribed_label": "نمایش حاشیه نویسی ها به طور پیشفرض برای کانال های مشترک شده: ",
"Redirect homepage to feed: ": "تغییر مسیر صفحه خانه به خوراک: ",
@ -157,7 +154,7 @@
"Engagement: ": "نامزدی: ",
"Whitelisted regions: ": "مناطق لیست سفید: ",
"Blacklisted regions: ": "مناطق لیست سیاه: ",
"Shared `x`": "به اشتراک گذاشته شده `x`",
"Shared `x`": "`x` به اشتراک گذاشته شد",
"Premieres in `x`": "برای اولین بار در `x`",
"Premieres `x`": "برای اولین بار `x`",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "سلام! مثل اینکه تو جاوا اسکریپت رو خاموش کرده ای. اینجا کلیک کن تا نظرات را ببینی، این رو یادت باشه که ممکنه بارگذاری اونها کمی طول بکشه.",
@ -171,17 +168,12 @@
"Hide replies": "مخفی کردن پاسخ ها",
"Show replies": "نمایش پاسخ ها",
"Incorrect password": "گذرواژه نا درست",
"Quota exceeded, try again in a few hours": "سهمیه بیشتر شده است، چند ساعت بعد دوباره تلاش کنید",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "قادر به ورود نیستید، مطمئن شوید احراز تایید-دو‌مرحله (Authenticator یا پیام‌کوتاه) خاموش باشد.",
"Invalid TFA code": "کد TFA نادرست است",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "ورود با خطا مواجه شد. این ممکن است به خاطر احراز تایید-دو‌مرحله باشد که برای حساب کاربری شما فعال نشده است.",
"Wrong answer": "پاسخ غلط",
"Erroneous CAPTCHA": "CAPTCHA نا درست",
"CAPTCHA is a required field": "CAPTCHA یک فیلد ضروری است",
"User ID is a required field": "شناسه کاربری یک فیلد ضروری است",
"Password is a required field": "گذرواژه یک فیلد ضروری است",
"Wrong username or password": "نام کاربری یا گذرواژه غلط است",
"Please sign in using 'Log in with Google'": "لطفا با استفاده از 'ورود توسط گوگل' وارد شوید",
"Password cannot be empty": "گذرواژه نمیتواند خالی باشد",
"Password cannot be longer than 55 characters": "گذر واژه نمیتواند از ۵۵ کاراکتر بیشتر باشد",
"Please log in": "لطفا وارد شوید",
@ -341,9 +333,9 @@
"`x` marked it with a ❤": "`x` نشان گذاری شده با یک ❤",
"Audio mode": "حالت صدا",
"Video mode": "حالت ویدیو",
"Videos": "ویدیو ها",
"channel_tab_videos_label": "ویدیو ها",
"Playlists": "سیاهه‌های پخش",
"Community": "اجتماع",
"channel_tab_community_label": "اجتماع",
"search_filters_sort_option_relevance": "مرتبط بودن",
"search_filters_sort_option_rating": "امتیاز",
"search_filters_sort_option_date": "تاریخ بارگذاری",
@ -375,7 +367,7 @@
"next_steps_error_message_refresh": "تازه‌سازی",
"next_steps_error_message_go_to_youtube": "رفتن به یوتیوب",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_dash": "DASH (کیفیت قابل تطبیق)",
"preferences_quality_option_dash": "DASH (کیفیت تطبیفی)",
"preferences_quality_option_medium": "میانه",
"preferences_quality_option_small": "پایین",
"preferences_quality_dash_option_auto": "خودکار",
@ -408,8 +400,50 @@
"preferences_region_label": "کشور محتوا: ",
"footer_documentation": "مستندات",
"footer_original_source_code": "کد منبع اصلی",
"search_filters_duration_option_long": "بلند (> 20 دقیقه)",
"search_filters_duration_option_long": "بلند (> ۲۰ دقیقه)",
"adminprefs_modified_source_code_url_label": "URL مخزن کد منبع ویریش شده",
"search_filters_duration_option_short": "کوتاه (< 4 دقیقه)",
"search_filters_title": "پالایه"
"search_filters_duration_option_short": "کوتاه (< ۴ دقیقه)",
"search_filters_title": "پالایه",
"Chinese (Hong Kong)": "چینی (هنگ‌کنگ)",
"Dutch (auto-generated)": "هلندی (تولید خودکار)",
"preferences_watch_history_label": "فعال‌سازی تاریخچه‌ی پخش ",
"Indonesian (auto-generated)": "اندونزیایی (تولید خودکار)",
"English (United States)": "انگلیسی (ایالات متحده)",
"Chinese": "چینی",
"Chinese (Taiwan)": "چینی (تایوان)",
"French (auto-generated)": "فرانسوی (تولید خودکار)",
"English (United Kingdom)": "انگلیسی (ایالات بریتانیا)",
"search_message_no_results": "نتیجه‌ای یافت نشد.",
"search_message_change_filters_or_query": "سعی کنید جست‌و‌جوی خود را وسیع‌تر کنید و/یا فیلترها را تغییر دهید.",
"Chinese (China)": "چینی (چین)",
"German (auto-generated)": "آلمانی (تولید خودکار)",
"Japanese (auto-generated)": "ژاپنی (تولید خودکار)",
"Korean (auto-generated)": "کره‌ای (تولید خودکار)",
"Portuguese (Brazil)": "پرتغالی (برزیل)",
"search_filters_apply_button": "اعمال فیلترهای انتخاب شده",
"Italian (auto-generated)": "ایتالیایی (تولید خودکار)",
"Vietnamese (auto-generated)": "ویتنامی (تولید خودکار)",
"search_filters_type_option_all": "هر نوعی",
"search_filters_duration_option_none": "هر مدت زمانی",
"search_filters_date_label": "تاریخ بارگذاری",
"search_filters_date_option_none": "هر تاریخی",
"user_created_playlists": "`x` فهرست پخش ایجاد شد",
"Interlingue": "سرخپوستی",
"Russian (auto-generated)": "روسی (تولید خودکار)",
"Spanish (auto-generated)": "اسپانیایی (تولید خودکار)",
"search_filters_duration_option_medium": "متوسط (۴ تا ۲۰ دقیقه)",
"Portuguese (auto-generated)": "پرتغالی (تولید خودکار)",
"Cantonese (Hong Kong)": "کانتونی (هنگ کنگ)",
"Spanish (Spain)": "اسپانیایی (اسپانیا)",
"Turkish (auto-generated)": "ترکی (تولید خودکار)",
"search_filters_features_option_vr180": "VR180",
"Spanish (Mexico)": "اسپانیایی (مکزیک)",
"Popular enabled: ": "محبوب ها فعال شد: ",
"Music in this video": "آهنگ در این ویدیو",
"Artist: ": "هنرمند: ",
"Album: ": "آلبوم: ",
"Song: ": "آهنگ: ",
"Channel Sponsor": "اسپانسر کانال",
"Standard YouTube license": "پروانه استاندارد YouTube",
"search_message_use_another_instance": " شما همچنین می‌توانید <a href=\"`x`\">در نمونه دیگر هم جستجو کنید</a>."
}

@ -14,7 +14,6 @@
"Clear watch history?": "Tyhjennä katseluhistoria?",
"New password": "Uusi salasana",
"New passwords must match": "Uusien salasanojen täytyy täsmätä",
"Cannot change password for Google accounts": "Google-tilien salasanaa ei voi vaihtaa",
"Authorize token?": "Valuutetaanko tunnus?",
"Authorize token for `x`?": "Valtuutetaanko tunnus `x`:lle?",
"Yes": "Kyllä",
@ -37,7 +36,6 @@
"source": "lähde",
"Log in": "Kirjaudu sisään",
"Log in/register": "Kirjaudu sisään/rekisteröidy",
"Log in with Google": "Kirjaudu sisään Googlella",
"User ID": "Käyttäjätunnus",
"Password": "Salasana",
"Time (h:mm:ss):": "Aika (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Kirjaudu sisään",
"Register": "Rekisteröidy",
"E-mail": "Sähköposti",
"Google verification code": "Google-vahvistuskoodi",
"Preferences": "Asetukset",
"preferences_category_player": "Soittimen asetukset",
"preferences_video_loop_label": "Toista jatkuvasti aina: ",
@ -163,17 +160,12 @@
"Hide replies": "Piilota vastaukset",
"Show replies": "Näytä vastaukset",
"Incorrect password": "Väärä salasana",
"Quota exceeded, try again in a few hours": "Kiintiö ylitetty, yritä parin tunnin kuluttua uudestaan",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Sisäänkirjautuminen epäonnistui. Varmista, että kaksivaiheinen tunnistautuminen (Authenticator tai tekstiviesti) on käytössä.",
"Invalid TFA code": "Virheellinen turvakoodi",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Sisäänkirjautuminen epäonnistui. Tämä voi johtua siitä, että kaksivaiheinen tunnistautuminen on pois käytöstä tunnuksellasi.",
"Wrong answer": "Väärä vastaus",
"Erroneous CAPTCHA": "Virheellinen CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA-kenttä vaaditaan",
"User ID is a required field": "Käyttäjätunnus vaaditaan",
"Password is a required field": "Salasana vaaditaan",
"Wrong username or password": "Väärä käyttäjänimi tai salasana",
"Please sign in using 'Log in with Google'": "Ole hyvä ja kirjaudu sisään Google-tunnuksella",
"Password cannot be empty": "Salasana ei voi olla tyhjä",
"Password cannot be longer than 55 characters": "Salasana ei voi olla yli 55 merkkiä pitkä",
"Please log in": "Kirjaudu sisään, ole hyvä",
@ -324,9 +316,9 @@
"`x` marked it with a ❤": "`x` merkkasi ❤:llä",
"Audio mode": "Äänitila",
"Video mode": "Videotila",
"Videos": "Videot",
"channel_tab_videos_label": "Videot",
"Playlists": "Soittolistat",
"Community": "Yhteisö",
"channel_tab_community_label": "Yhteisö",
"search_filters_sort_option_relevance": "Osuvuus",
"search_filters_sort_option_rating": "Arvostelu",
"search_filters_sort_option_date": "Latauspäivämäärä",
@ -470,5 +462,7 @@
"search_filters_duration_option_medium": "Keskipituinen (4 - 20 minuuttia)",
"search_message_use_another_instance": " Voit myös <a href=\"`x`\">hakea toisella instanssilla</a>.",
"search_filters_date_option_none": "Milloin tahansa",
"search_filters_type_option_all": "Mikä tahansa tyyppi"
"search_filters_type_option_all": "Mikä tahansa tyyppi",
"Popular enabled: ": "Suosittu käytössä: ",
"error_video_not_in_playlist": "Pyydettyä videota ei löydy tästä soittolistasta. <a href=\"`x`\">Klikkaa tähän päästäksesi soittolistan etusivulle.</a>"
}

@ -24,7 +24,6 @@
"Clear watch history?": "Êtes-vous sûr de vouloir supprimer l'historique des vidéos regardées ?",
"New password": "Nouveau mot de passe",
"New passwords must match": "Les nouveaux mots de passe doivent correspondre",
"Cannot change password for Google accounts": "Le mot de passe d'un compte Google ne peut pas être changé depuis Invidious",
"Authorize token?": "Autoriser le token ?",
"Authorize token for `x`?": "Autoriser le token pour `x` ?",
"Yes": "Oui",
@ -47,7 +46,6 @@
"source": "source",
"Log in": "Se connecter",
"Log in/register": "Se connecter/S'inscrire",
"Log in with Google": "Se connecter avec Google",
"User ID": "Identifiant utilisateur",
"Password": "Mot de passe",
"Time (h:mm:ss):": "Heure (h:mm:ss) :",
@ -56,7 +54,6 @@
"Sign In": "Se connecter",
"Register": "S'inscrire",
"E-mail": "E-mail",
"Google verification code": "Code de vérification Google",
"Preferences": "Préférences",
"preferences_category_player": "Préférences du lecteur",
"preferences_video_loop_label": "Lire en boucle : ",
@ -116,6 +113,7 @@
"preferences_default_home_label": "Page d'accueil par défaut : ",
"preferences_feed_menu_label": "Préferences des abonnements : ",
"preferences_show_nick_label": "Afficher le nom d'utilisateur en haut à droite : ",
"Popular enabled: ": "Page \"populaire\" activée: ",
"Top enabled: ": "Top activé : ",
"CAPTCHA enabled: ": "CAPTCHA activé : ",
"Login enabled: ": "Autoriser l'ouverture de sessions utilisateur : ",
@ -178,17 +176,12 @@
"Hide replies": "Masquer les réponses",
"Show replies": "Afficher les réponses",
"Incorrect password": "Mot de passe incorrect",
"Quota exceeded, try again in a few hours": "Nombre de tentatives de connexion dépassé, réessayez dans quelques heures",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Impossible de se connecter, si après plusieurs tentative vous ne parvenez toujours pas à vous connecter, assurez-vous que l'authentification à deux facteurs (Authenticator ou SMS) est activée.",
"Invalid TFA code": "Code d'authentification à deux facteurs invalide",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "La connexion a échoué. Cela peut être dû au fait que l'authentification à deux facteurs n'est pas activée sur votre compte.",
"Wrong answer": "Réponse invalide",
"Erroneous CAPTCHA": "CAPTCHA invalide",
"CAPTCHA is a required field": "Veuillez entrer un CAPTCHA",
"User ID is a required field": "Veuillez entrer un Identifiant Utilisateur",
"Password is a required field": "Veuillez entrer un Mot de passe",
"Wrong username or password": "Nom d'utilisateur ou mot de passe invalide",
"Please sign in using 'Log in with Google'": "Veuillez vous connecter en utilisant \"Se connecter avec Google\"",
"Password cannot be empty": "Le mot de passe ne peut pas être vide",
"Password cannot be longer than 55 characters": "Le mot de passe ne doit pas comporter plus de 55 caractères",
"Please log in": "Veuillez vous connecter",
@ -357,9 +350,9 @@
"`x` marked it with a ❤": "`x` l'a marqué d'un ❤",
"Audio mode": "Mode audio",
"Video mode": "Mode vidéo",
"Videos": "Vidéos",
"channel_tab_videos_label": "Vidéos",
"Playlists": "Listes de lecture",
"Community": "Communauté",
"channel_tab_community_label": "Communauté",
"search_filters_sort_option_relevance": "Pertinence",
"search_filters_sort_option_rating": "Notation",
"search_filters_sort_option_date": "Date d'ajout",
@ -470,5 +463,18 @@
"search_filters_type_option_all": "Tous les types",
"search_filters_date_label": "Date d'ajout",
"search_filters_features_option_vr180": "VR180",
"search_filters_duration_option_none": "Toutes les durées"
"search_filters_duration_option_none": "Toutes les durées",
"error_video_not_in_playlist": "La vidéo demandée n'existe pas dans cette liste de lecture. <a href=\"`x`\">Cliquez ici pour retourner à la liste de lecture.</a>",
"channel_tab_shorts_label": "Vidéos courtes",
"channel_tab_streams_label": "Vidéos en direct",
"channel_tab_playlists_label": "Listes de lecture",
"channel_tab_channels_label": "Chaînes",
"Song: ": "Chanson : ",
"Artist: ": "Artiste : ",
"Album: ": "Album : ",
"Standard YouTube license": "Licence YouTube Standard",
"Music in this video": "Musique dans cette vidéo",
"Channel Sponsor": "Soutien de la chaîne",
"Download is disabled": "Le téléchargement est désactivé",
"Import YouTube playlist (.csv)": "Importer des listes de lecture de Youtube (.csv)"
}

@ -14,7 +14,6 @@
"Clear watch history?": "לנקות את היסטוריית הצפייה?",
"New password": "סיסמה חדשה",
"New passwords must match": "על הסיסמאות החדשות להתאים",
"Cannot change password for Google accounts": "לא ניתן לשנות את הסיסמה לחשבונות Google",
"Authorize token?": "לאשר את האסימון?",
"Authorize token for `x`?": "האם לאשר את האסימון עבור `x`?",
"Yes": "כן",
@ -37,7 +36,6 @@
"source": "source",
"Log in": "כניסה",
"Log in/register": "כניסה/הרשמה",
"Log in with Google": "כניסה עם Google",
"User ID": "שם משתמש",
"Password": "סיסמה",
"Time (h:mm:ss):": "זמן (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "התחברות",
"Register": "הרשמה",
"E-mail": "דוא״ל",
"Google verification code": "קוד האימות של Google",
"Preferences": "העדפות",
"preferences_category_player": "העדפות הנגן",
"preferences_autoplay_label": "ניגון אוטומטי: ",
@ -137,7 +134,6 @@
"User ID is a required field": "חובה למלא את שדה שם המשתמש",
"Password is a required field": "חובה למלא את שדה הסיסמה",
"Wrong username or password": "שם משתמש שגוי או סיסמה שגויה",
"Please sign in using 'Log in with Google'": "נא להתחבר בעזרת \"התחברות עם Google\"",
"Password cannot be longer than 55 characters": "על אורך הסיסמה להיות 55 תווים לכל היותר",
"Please log in": "נא להתחבר",
"channel:`x`": "ערוץ:`x`",
@ -271,9 +267,9 @@
"`x` marked it with a ❤": "סומנה ב־❤ על ידי `x`",
"Audio mode": "Audio mode",
"Video mode": "Video mode",
"Videos": "סרטונים",
"channel_tab_videos_label": "סרטונים",
"Playlists": "פלייליסטים",
"Community": "קהילה",
"channel_tab_community_label": "קהילה",
"search_filters_sort_option_relevance": "רלוונטיות",
"search_filters_sort_option_rating": "דירוג",
"search_filters_sort_option_date": "תאריך העלאה",

@ -4,7 +4,6 @@
"No": "नहीं",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "OPML के रूप में सदस्यताएँ निर्यात करें (NewPipe और FreeTube के लिए)",
"Log in/register": "लॉग-इन/पंजीकृत करें",
"Log in with Google": "Google के साथ लॉग-इन करें",
"preferences_autoplay_label": "अपने आप चलाने की सुविधा: ",
"preferences_dark_mode_label": "थीम: ",
"preferences_default_home_label": "डिफ़ॉल्ट मुखपृष्ठ: ",
@ -58,7 +57,6 @@
"Clear watch history?": "देखने का इतिहास मिटाएँ?",
"New password": "नया पासवर्ड",
"New passwords must match": "पासवर्ड्स को मेल खाना होगा",
"Cannot change password for Google accounts": "Google खातों के लिए पासवर्ड नहीं बदल सकते",
"Authorize token?": "टोकन को प्रमाणित करें?",
"Authorize token for `x`?": "`x` के लिए टोकन को प्रमाणित करें?",
"Import and Export Data": "डेटा को आयात और निर्यात करें",
@ -81,7 +79,6 @@
"Password": "पासवर्ड",
"Register": "पंजीकृत करें",
"E-mail": "ईमेल",
"Google verification code": "Google प्रमाणीकरण कोड",
"Time (h:mm:ss):": "समय (घं:मिमि:सेसे):",
"Text CAPTCHA": "टेक्स्ट CAPTCHA",
"Image CAPTCHA": "चित्र CAPTCHA",
@ -224,15 +221,10 @@
"Hide replies": "जवाब छिपाएँ",
"Show replies": "जवाब दिखाएँ",
"Incorrect password": "गलत पासवर्ड",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "लॉग-इन नहीं किया जा सका, सुनिश्चित करें कि दो-कारक प्रमाणीकरण (Authenticator या SMS) सक्षम है।",
"Invalid TFA code": "अमान्य TFA कोड",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "लॉग-इन नाकाम रहा। ऐसा इसलिए हो सकता है कि दो-कारक प्रमाणीकरण आपके खाते पर सक्षम नहीं है।",
"Quota exceeded, try again in a few hours": "कोटा पार हो चुका है, कृपया कुछ घंटों में फिर कोशिश करें",
"CAPTCHA is a required field": "CAPTCHA एक ज़रूरी फ़ील्ड है",
"User ID is a required field": "सदस्य ID एक ज़रूरी फ़ील्ड है",
"Password is a required field": "पासवर्ड एक ज़रूरी फ़ील्ड है",
"Wrong username or password": "गलत सदस्यनाम या पासवर्ड",
"Please sign in using 'Log in with Google'": "कृपया 'Google के साथ लॉग-इन करें' के साथ साइन-इन करें",
"Password cannot be empty": "पासवर्ड खाली नहीं हो सकता",
"Password cannot be longer than 55 characters": "पासवर्ड में अधिकतम 55 अक्षर हो सकते हैं",
"Invidious Private Feed for `x`": "`x` के लिए Invidious निजी फ़ीड",
@ -401,12 +393,12 @@
"(edited)": "(संपादित)",
"YouTube comment permalink": "YouTube पर टिप्पणी की स्थायी कड़ी",
"permalink": "स्थायी कड़ी",
"Videos": "वीडियो",
"channel_tab_videos_label": "वीडियो",
"`x` marked it with a ❤": "`x` ने इसे एक ❤ से चिह्नित किया",
"Audio mode": "ऑडियो मोड",
"Playlists": "प्लेलिस्ट्स",
"Video mode": "वीडियो मोड",
"Community": "समुदाय",
"channel_tab_community_label": "समुदाय",
"search_filters_title": "फ़िल्टर",
"search_filters_date_label": "अपलोड करने का समय",
"search_filters_date_option_none": "कोई भी समय",
@ -470,5 +462,14 @@
"crash_page_switch_instance": "<a href=\"`x`\">किसी दूसरे उदाहरण का इस्तेमाल करें</a>",
"crash_page_read_the_faq": "<a href=\"`x`\">अक्सर पूछे जाने वाले प्रश्न (FAQ)</a> पढ़ें",
"crash_page_refresh": "<a href=\"`x`\">पृष्ठ को एक बार साफ़ करें</a>",
"crash_page_search_issue": "<a href=\"`x`\">GitHub पर मौजूदा मुद्दे</a> ढूँढ़ें"
"crash_page_search_issue": "<a href=\"`x`\">GitHub पर मौजूदा मुद्दे</a> ढूँढ़ें",
"Popular enabled: ": "लोकप्रिय सक्षम: ",
"Artist: ": "कलाकार: ",
"Music in this video": "इस वीडियो में संगीत",
"Album: ": "एल्बम: ",
"error_video_not_in_playlist": "अनुरोधित वीडियो इस प्लेलिस्ट में मौजूद नहीं है। <a href=\"`x`\">प्लेलिस्ट के मुखपृष्ठ पर जाने के लिए यहाँ क्लिक करें।</a>",
"channel_tab_shorts_label": "शॉर्ट्स",
"channel_tab_streams_label": "लाइवस्ट्रीम्स",
"channel_tab_playlists_label": "प्लेलिस्ट्स",
"channel_tab_channels_label": "चैनल्स"
}

@ -7,14 +7,13 @@
"View playlist on YouTube": "Prikaži zbirku na YouTubeu",
"newest": "najnovije",
"oldest": "najstarije",
"popular": "popularni",
"last": "zadnji",
"popular": "popularne",
"last": "zadnje",
"Next page": "Sljedeća stranica",
"Previous page": "Prethodna stranica",
"Clear watch history?": "Izbrisati povijest gledanja?",
"New password": "Nova lozinka",
"New passwords must match": "Nove lozinke se moraju poklapati",
"Cannot change password for Google accounts": "Nije moguće promijeniti lozinku za Google račune",
"Authorize token?": "Autorizirati token?",
"Authorize token for `x`?": "Autorizirati token za `x`?",
"Yes": "Da",
@ -37,16 +36,14 @@
"source": "izvor",
"Log in": "Prijavi se",
"Log in/register": "Prijavi se/registriraj se",
"Log in with Google": "Prijavi se pomoću Googlea",
"User ID": "Korisnički ID",
"Password": "Lozinka",
"Time (h:mm:ss):": "Vrijeme (h:mm:ss):",
"Text CAPTCHA": "Tekstualni CAPTCHA",
"Image CAPTCHA": "Slikovni CAPTCHA",
"Sign In": "Prijava",
"Sign In": "Prijavi se",
"Register": "Registriraj se",
"E-mail": "E-mail",
"Google verification code": "Googleov potvrdni kod",
"E-mail": "E-mail adresa",
"Preferences": "Postavke",
"preferences_category_player": "Postavke playera",
"preferences_video_loop_label": "Uvijek ponavljaj: ",
@ -107,7 +104,7 @@
"preferences_feed_menu_label": "Izbornik za feedove: ",
"preferences_show_nick_label": "Prikaži nadimak na vrhu: ",
"Top enabled: ": "Najbolji aktivirani: ",
"CAPTCHA enabled: ": "Aktivirani CAPTCHA: ",
"CAPTCHA enabled: ": "CAPTCHA aktiviran: ",
"Login enabled: ": "Prijava aktivirana: ",
"Registration enabled: ": "Registracija aktivirana: ",
"Report statistics: ": "Izvještaj o statistici: ",
@ -137,8 +134,8 @@
"Title": "Naslov",
"Playlist privacy": "Privatnost zbirke",
"Editing playlist `x`": "Uređivanje zbirke `x`",
"Show more": "Pokaži više",
"Show less": "Pokaži manje",
"Show more": "Prikaži više",
"Show less": "Prikaži manje",
"Watch on YouTube": "Gledaj na YouTubeu",
"Switch Invidious Instance": "Promijeni Invidious instancu",
"Hide annotations": "Sakrij napomene",
@ -164,17 +161,12 @@
"Hide replies": "Sakrij odgovore",
"Show replies": "Prikaži odgovore",
"Incorrect password": "Neispravna lozinka",
"Quota exceeded, try again in a few hours": "Kvota je prekoračena. Pokušaj ponovo za par sati",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Prijava neuspjela. Provjeri da je dvofaktorska autentifikacija uključena (Authenticator ili SMS).",
"Invalid TFA code": "Neispravan TFA kod",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Prijava neuspjela. Možda zato što za tvoj račun nije uključena dvofaktorska autentifikacija.",
"Wrong answer": "Krivi odgovor",
"Erroneous CAPTCHA": "Neispravan CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA je obavezno polje",
"User ID is a required field": "Korisnički ID je obavezno polje",
"Password is a required field": "Polje lozinke je obavezno polje",
"Wrong username or password": "Krivo korisničko ime ili lozinka",
"Please sign in using 'Log in with Google'": "Za prijavu koristi „Prijavi se pomoću Googlea”",
"Password cannot be empty": "Polje lozinke ne smije ostati prazno",
"Password cannot be longer than 55 characters": "Lozinka ne može biti duža od 55 znakova",
"Please log in": "Prijavi se",
@ -318,54 +310,54 @@
"Movies": "Filmovi",
"Download": "Preuzmi",
"Download as: ": "Preuzmi kao: ",
"%A %B %-d, %Y": "%A, %-d. %B %Y",
"%A %B %-d, %Y": "%A, %-d. %B %Y.",
"(edited)": "(uređeno)",
"YouTube comment permalink": "Stalna poveznica YouTube komentara",
"permalink": "stalna poveznica",
"`x` marked it with a ❤": "Označeno sa ❤ od `x`",
"Audio mode": "Audio modus",
"Video mode": "Videomodus",
"Videos": "Videa",
"channel_tab_videos_label": "Videa",
"Playlists": "Zbirke",
"Community": "Zajednica",
"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",
"channel_tab_community_label": "Zajednica",
"search_filters_sort_option_relevance": "Značaj",
"search_filters_sort_option_rating": "Ocjena",
"search_filters_sort_option_date": "Datum prijenosa",
"search_filters_sort_option_views": "Broj gledanja",
"search_filters_type_label": "Vrsta",
"search_filters_duration_label": "Trajanje",
"search_filters_features_label": "Funkcije",
"search_filters_sort_label": "Redoslijed",
"search_filters_date_option_hour": "Zadnjih sat vremena",
"search_filters_date_option_today": "Danas",
"search_filters_date_option_week": "Ovaj tjedan",
"search_filters_date_option_month": "Ovaj mjesec",
"search_filters_date_option_year": "Ova 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_type_option_movie": "Film",
"search_filters_type_option_show": "Emisija",
"search_filters_features_option_hd": "HD",
"search_filters_features_option_subtitles": "Titlovi/CC",
"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_features_option_location": "Lokacija",
"search_filters_features_option_hdr": "HDR",
"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",
"search_filters_duration_option_short": "Kratki (< 4 minute)",
"search_filters_duration_option_long": "Dugi (> 20 minute)",
"adminprefs_modified_source_code_url_label": "URL do repozitorija prilagođenog izvornog koda",
"search_filters_duration_option_short": "Kratko (< 4 minute)",
"search_filters_duration_option_long": "Dugo (> 20 minute)",
"footer_source_code": "Izvorni kod",
"footer_modfied_source_code": "Izmijenjeni izvorni kod",
"footer_modfied_source_code": "Prilagođen izvorni kod",
"footer_documentation": "Dokumentacija",
"footer_original_source_code": "Izvoran izvorni kod",
"footer_original_source_code": "Prvobitan izvorni kod",
"preferences_region_label": "Zemlja sadržaja: ",
"preferences_quality_dash_label": "Preferirana DASH videokvaliteta: ",
"preferences_quality_option_dash": "DASH (adaptativna kvaliteta)",
@ -384,8 +376,8 @@
"search_filters_features_option_three_sixty": "360 °",
"none": "bez",
"videoinfo_youTube_embed_link": "Ugradi",
"user_created_playlists": "`x` stvorene zbirke",
"user_saved_playlists": "`x` spremljene zbirke",
"user_created_playlists": "`x` je stvorio/la zbirke",
"user_saved_playlists": "`x` je spremio/la zbirke",
"Video unavailable": "Video nedostupan",
"preferences_save_player_pos_label": "Spremi mjesto reprodukcije: ",
"videoinfo_watch_on_youTube": "Gledaj na YouTubeu",
@ -432,7 +424,7 @@
"generic_subscriptions_count_2": "{{count}} pretplata",
"generic_playlists_count_0": "{{count}} zbirka",
"generic_playlists_count_1": "{{count}} zbirke",
"generic_playlists_count_2": "{{count}} zbirka",
"generic_playlists_count_2": "{{count}} zbiraka",
"generic_videos_count_0": "{{count}} video",
"generic_videos_count_1": "{{count}} videa",
"generic_videos_count_2": "{{count}} videa",
@ -476,5 +468,29 @@
"Portuguese (auto-generated)": "Portugalski (automatski generiran)",
"Spanish (auto-generated)": "Španjolski (automatski generiran)",
"preferences_watch_history_label": "Aktiviraj povijest gledanja: ",
"search_filters_title": "Filtar"
"search_filters_title": "Filtri",
"search_filters_date_option_none": "Bilo koji datum",
"search_filters_date_label": "Datum prijenosa",
"search_message_no_results": "Nema rezultata.",
"search_message_use_another_instance": " Također možeš <a href=\"`x`\">tražiti na jednoj drugoj instanci</a>.",
"search_message_change_filters_or_query": "Pokušaj proširiti upit za pretragu i/ili promijeni filtre.",
"search_filters_features_option_vr180": "VR180",
"search_filters_duration_option_none": "Bilo koje duljine",
"search_filters_duration_option_medium": "Srednje (4 20 minuta)",
"search_filters_apply_button": "Primijeni odabrane filtre",
"search_filters_type_option_all": "Bilo koja vrsta",
"Popular enabled: ": "Popularni aktivirani: ",
"error_video_not_in_playlist": "Traženi video ne postoji u ovoj zbirci. <a href=\"`x`\">Pritisni ovdje za početnu stranicu zbirke.</a>",
"channel_tab_streams_label": "Prijenosi uživo",
"channel_tab_playlists_label": "Zbirke",
"channel_tab_channels_label": "Kanali",
"channel_tab_shorts_label": "Kratka videa",
"Music in this video": "Glazba u ovom videu",
"Album: ": "Album: ",
"Artist: ": "Izvođač: ",
"Channel Sponsor": "Sponzor kanala",
"Song: ": "Pjesma: ",
"Standard YouTube license": "Standardna YouTube licenca",
"Download is disabled": "Preuzimanje je deaktivirano",
"Import YouTube playlist (.csv)": "Uvezi YouTube zbirku (.csv)"
}

@ -24,7 +24,6 @@
"Clear watch history?": "Törölve legyen a megnézett videók naplója?",
"New password": "Új jelszó",
"New passwords must match": "Az új jelszavaknak egyezniük kell.",
"Cannot change password for Google accounts": "A Google-fiók jelszavát nem lehet megváltoztatni.",
"Authorize token?": "Engedélyezve legyen a token?",
"Authorize token for `x`?": "Engedélyezve legyen a token erre? „`x`”",
"Yes": "Igen",
@ -47,7 +46,6 @@
"source": "forrás",
"Log in": "Bejelentkezés",
"Log in/register": "Bejelentkezés/Regisztrálás",
"Log in with Google": "Bejelentkezés Google-fiókkal",
"User ID": "Felhasználói azonosító",
"Password": "Jelszó",
"Time (h:mm:ss):": "A pontos idő (ó:pp:mm):",
@ -56,7 +54,6 @@
"Sign In": "Bejelentkezés",
"Register": "Regisztrálás",
"E-mail": "E-mail-cím",
"Google verification code": "A Google ellenőrző kódja",
"Preferences": "Beállítások",
"preferences_category_player": "Lejátszó beállításai",
"preferences_video_loop_label": "Videó állandó ismétlése: ",
@ -173,16 +170,12 @@
"Hide replies": "Válaszok elrejtése",
"Show replies": "Válaszok mutatása",
"Incorrect password": "A jelszó nem megfelelő",
"Quota exceeded, try again in a few hours": "A kvótát meghaladták. Néhány órával később próbáld meg újból betölteni.",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Nem sikerült bejelentkezni. A kétlépcsős (hitelesítő vagy szöveges üzenet általi) hitelesítésnek bekapcsolva kell lennie.",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Nem sikerült bejelentkezni. Ennek oka lehet, hogy a kétlépcsős hitelesítés nincs bekapcsolva a fiók beállításaiban.",
"Wrong answer": "Nem jól válaszoltál.",
"Erroneous CAPTCHA": "A CAPTCHA hibás.",
"CAPTCHA is a required field": "A CAPTCHA-mezőt ki kell tölteni.",
"User ID is a required field": "A felhasználói azonosítót meg kell adni.",
"Password is a required field": "Meg kell adni egy jelszót.",
"Wrong username or password": "Vagy a felhasználói név, vagy pedig a jelszó nem megfelelő.",
"Please sign in using 'Log in with Google'": "A „Bejelentkezés Google-el” gombbal jelentkezz be.",
"Password cannot be empty": "A jelszót nem lehet kihagyni.",
"Password cannot be longer than 55 characters": "A jelszó nem lehet hosszabb 55 karakternél.",
"Please log in": "Kérjük, jelentkezz be.",
@ -348,9 +341,9 @@
"`x` marked it with a ❤": "`x` ❤jelet adott a hozzászóláshoz",
"Audio mode": "Csak hanggal",
"Video mode": "Hanggal és képpel",
"Videos": "Videói",
"channel_tab_videos_label": "Videói",
"Playlists": "Lejátszási listái",
"Community": "Közösség",
"channel_tab_community_label": "Közösség",
"Current version: ": "Jelenlegi verzió: ",
"preferences_quality_option_medium": "Közepes",
"preferences_quality_dash_option_auto": "Automatikus",
@ -419,7 +412,6 @@
"Switch Invidious Instance": "Váltás másik Invidious-oldalra",
"Urdu": "urdu",
"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ó",
"search_filters_features_option_hd": "HD",
"next_steps_error_message_go_to_youtube": "Ugrás a YouTube-ra",
@ -470,5 +462,7 @@
"search_filters_duration_option_none": "Mindegy",
"search_filters_duration_option_medium": "Átlagos (4 és 20 perc között)",
"search_filters_features_option_vr180": "180°-os virtuális valóság",
"search_filters_apply_button": "Keresés a megadott szűrőkkel"
"search_filters_apply_button": "Keresés a megadott szűrőkkel",
"Popular enabled: ": "Népszerű engedélyezve ",
"error_video_not_in_playlist": "A lejátszási listában keresett videó nem létezik. <a href=\"`x`\">Kattintson ide a lejátszási listához jutáshoz.</a>"
}

@ -19,7 +19,6 @@
"Clear watch history?": "Bersihkan riwayat tontonan?",
"New password": "Kata sandi baru",
"New passwords must match": "Kata sandi baru harus cocok",
"Cannot change password for Google accounts": "Tidak dapat mengganti kata sandi untuk akun Google",
"Authorize token?": "Otorisasi token?",
"Authorize token for `x`?": "Otorisasi token untuk `x`?",
"Yes": "Ya",
@ -42,7 +41,6 @@
"source": "sumber",
"Log in": "Masuk",
"Log in/register": "Masuk/Daftar",
"Log in with Google": "Masuk dengan Google",
"User ID": "ID Pengguna",
"Password": "Kata Sandi",
"Time (h:mm:ss):": "Waktu (j:mm:dd):",
@ -51,7 +49,6 @@
"Sign In": "Masuk",
"Register": "Daftar",
"E-mail": "Surel",
"Google verification code": "Kode verifikasi Google",
"Preferences": "Preferensi",
"preferences_category_player": "Preferensi pemutar",
"preferences_video_loop_label": "Selalu ulangi: ",
@ -126,7 +123,7 @@
"revoke": "cabut",
"Subscriptions": "Langganan",
"subscriptions_unseen_notifs_count_0": "{{count}} pemberitahuan belum dilihat",
"search": "cari",
"search": "Telusuri",
"Log out": "Keluar",
"Released under the AGPLv3 on Github.": "Dirilis di bawah AGPLv3 di GitHub.",
"Source available here.": "Sumber tersedia di sini.",
@ -171,17 +168,12 @@
"Hide replies": "Sembunyikan balasan",
"Show replies": "Lihat balasan",
"Incorrect password": "Kata sandi salah",
"Quota exceeded, try again in a few hours": "Kuota penuh, coba lagi dalam beberapa jam",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Tidak dapat masuk, pastikan autentikasi dua-faktor (autentikator atau SMS) sudah nyala.",
"Invalid TFA code": "Kode TFA tidak valid",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Gagal masuk. Ini mungkin disebabkan autentikasi dua-faktor tidak dinyalakan untuk akun Anda.",
"Wrong answer": "Jawaban salah",
"Erroneous CAPTCHA": "CAPTCHA salah",
"CAPTCHA is a required field": "CAPTCHA perlu diisi",
"User ID is a required field": "ID pengguna perlu diisi",
"Password is a required field": "Kata sandi perlu diisi",
"Wrong username or password": "Nama pengguna atau kata sandi salah",
"Please sign in using 'Log in with Google'": "Harap masuk menggunakan 'Masuk dengan Google'",
"Password cannot be empty": "Kata sandi tidak boleh kosong",
"Password cannot be longer than 55 characters": "Kata sandi tidak boleh lebih dari 55 karakter",
"Please log in": "Harap masuk",
@ -341,12 +333,12 @@
"`x` marked it with a ❤": "`x` telah ditandai dengan ❤",
"Audio mode": "Mode audio",
"Video mode": "Mode video",
"Videos": "Video",
"channel_tab_videos_label": "Video",
"Playlists": "Daftar putar",
"Community": "Komunitas",
"channel_tab_community_label": "Komunitas",
"search_filters_sort_option_relevance": "Relevansi",
"search_filters_sort_option_rating": "Penilaian",
"search_filters_sort_option_date": "Tanggal unggah",
"search_filters_sort_option_date": "Tanggal Unggah",
"search_filters_sort_option_views": "Jumlah ditonton",
"search_filters_type_label": "Tipe",
"search_filters_duration_label": "Durasi",
@ -418,5 +410,41 @@
"English (United States)": "Inggris (US)",
"preferences_watch_history_label": "Aktifkan riwayat tontonan: ",
"English (United Kingdom)": "Inggris (UK)",
"search_filters_title": "Saring"
"search_filters_title": "Saring",
"search_message_no_results": "Tidak ada hasil yang ditemukan.",
"search_message_change_filters_or_query": "Coba perbanyak kueri pencarian dan/atau ubah filter Anda.",
"search_message_use_another_instance": " Anda juga bisa <a href=\"`x`\">mencari di peladen lain</a>.",
"Indonesian (auto-generated)": "Indonesia (dibuat secara otomatis)",
"Japanese (auto-generated)": "Jepang (dibuat secara otomatis)",
"Korean (auto-generated)": "Korea (dibuat secara otomatis)",
"Portuguese (Brazil)": "Portugis (Brasil)",
"Russian (auto-generated)": "Rusia (dibuat secara otomatis)",
"Spanish (Mexico)": "Spanyol (Meksiko)",
"Spanish (Spain)": "Spanyol (Spanyol)",
"Vietnamese (auto-generated)": "Vietnam (dibuat secara otomatis)",
"search_filters_features_option_vr180": "VR180",
"Spanish (auto-generated)": "Spanyol (dibuat secara otomatis)",
"Chinese": "Bahasa Cina",
"Chinese (Taiwan)": "Bahasa Cina (Taiwan)",
"Chinese (Hong Kong)": "Bahasa Cina (Hong Kong)",
"Chinese (China)": "Bahasa Cina (China)",
"French (auto-generated)": "Perancis (dibuat secara otomatis)",
"German (auto-generated)": "Jerman (dibuat secara otomatis)",
"Italian (auto-generated)": "Italia (dibuat secara otomatis)",
"Portuguese (auto-generated)": "Portugis (dibuat secara otomatis)",
"Turkish (auto-generated)": "Turki (dibuat secara otomatis)",
"search_filters_date_label": "Tanggal unggah",
"search_filters_type_option_all": "Segala jenis",
"search_filters_apply_button": "Terapkan saringan yang dipilih",
"Dutch (auto-generated)": "Belanda (dihasilkan secara otomatis)",
"search_filters_date_option_none": "Tanggal berapa pun",
"search_filters_duration_option_none": "Durasi berapa pun",
"search_filters_duration_option_medium": "Sedang (4 - 20 menit)",
"Cantonese (Hong Kong)": "Bahasa Kanton (Hong Kong)",
"crash_page_refresh": "mencoba untuk <a href=\"`x`\">memuat ulang halaman</a>",
"crash_page_switch_instance": "mencoba untuk <a href=\"`x`\">menggunakan peladen lainnya</a>",
"crash_page_read_the_faq": "baca <a href=\"`x`\">Soal Sering Ditanya (SSD/FAQ)</a>",
"crash_page_search_issue": "mencari <a href=\"`x`\">isu yang ada di GitHub</a>",
"crash_page_report_issue": "Jika yang di atas tidak membantu, <a href=\"`x`\">buka isu baru di GitHub</a> (sebaiknya dalam bahasa Inggris) dan sertakan teks berikut dalam pesan Anda (JANGAN terjemahkan teks tersebut):",
"Popular enabled: ": "Populer diaktifkan: "
}

@ -14,7 +14,6 @@
"Clear watch history?": "Hreinsa áhorfssögu?",
"New password": "Nýtt lykilorð",
"New passwords must match": "Nýtt lykilorð verður að passa",
"Cannot change password for Google accounts": "Ekki er hægt að breyta lykilorði fyrir Google reikninga",
"Authorize token?": "Leyfa tákn?",
"Authorize token for `x`?": "Leyfa tákn fyrir `x`?",
"Yes": "Já",
@ -37,7 +36,6 @@
"source": "uppspretta",
"Log in": "Skrá inn",
"Log in/register": "Innskráning/nýskráning",
"Log in with Google": "Skrá inn með Google",
"User ID": "Notandakenni",
"Password": "Lykilorð",
"Time (h:mm:ss):": "Tími (h:mm: ss):",
@ -46,7 +44,6 @@
"Sign In": "Skrá inn",
"Register": "Nýskrá",
"E-mail": "Tölvupóstur",
"Google verification code": "Google staðfestingarkóði",
"Preferences": "Kjörstillingar",
"preferences_category_player": "Kjörstillingar spilara",
"preferences_video_loop_label": "Alltaf lykkja: ",
@ -155,17 +152,12 @@
"Hide replies": "Fela svör",
"Show replies": "Sýna svör",
"Incorrect password": "Rangt lykilorð",
"Quota exceeded, try again in a few hours": "Kvóti fór yfir, reyndu aftur eftir nokkrar klukkustundir",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Ekki er hægt að skrá þig inn, vertu viss um að tvíþætt staðfesting (Authenticator eða SMS) sé kveikt á.",
"Invalid TFA code": "Ógildur TFA kóði",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Innskráning mistókst. Þetta gæti verið vegna þess að tvíþátta staðfesting er ekki kveikt á reikningnum þínum.",
"Wrong answer": "Rangt svar",
"Erroneous CAPTCHA": "Rangt CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA er nauðsynlegur reitur",
"User ID is a required field": "Notandakenni er nauðsynlegur reitur",
"Password is a required field": "Lykilorð er nauðsynlegur reitur",
"Wrong username or password": "Rangt notandanafn eða lykilorð",
"Please sign in using 'Log in with Google'": "Vinsamlegast skráðu þig inn með því að nota 'Innskráning með Google'",
"Password cannot be empty": "Lykilorð má ekki vera autt",
"Password cannot be longer than 55 characters": "Lykilorð má ekki vera lengra en 55 stafir",
"Please log in": "Vinsamlegast skráðu þig inn",
@ -315,9 +307,9 @@
"`x` marked it with a ❤": "`x` merkti það með ❤",
"Audio mode": "Hljóð ham",
"Video mode": "Myndband ham",
"Videos": "Myndbönd",
"channel_tab_videos_label": "Myndbönd",
"Playlists": "Spilunarlistar",
"Community": "Samfélag",
"channel_tab_community_label": "Samfélag",
"Current version: ": "Núverandi útgáfa: ",
"preferences_watch_history_label": "Virkja áhorfssögu: "
}

@ -14,13 +14,12 @@
"newest": "più recente",
"oldest": "più vecchio",
"popular": "Tendenze",
"last": "durare",
"last": "ultimo",
"Next page": "Pagina successiva",
"Previous page": "Pagina precedente",
"Clear watch history?": "Eliminare la cronologia dei video guardati?",
"New password": "Nuova password",
"New passwords must match": "Le nuove password devono corrispondere",
"Cannot change password for Google accounts": "Non è possibile modificare la password per gli account Google",
"Authorize token?": "Autorizzare gettone?",
"Authorize token for `x`?": "Autorizzare gettone per `x`?",
"Yes": "Sì",
@ -28,7 +27,7 @@
"Import and Export Data": "Importazione ed esportazione dati",
"Import": "Importa",
"Import Invidious data": "Importa dati Invidious in formato JSON",
"Import YouTube subscriptions": "Importa le iscrizioni da YouTube",
"Import YouTube subscriptions": "Importa le iscrizioni da YouTube/OPML",
"Import FreeTube subscriptions (.db)": "Importa le iscrizioni da FreeTube (.db)",
"Import NewPipe subscriptions (.json)": "Importa le iscrizioni da NewPipe (.json)",
"Import NewPipe data (.zip)": "Importa i dati di NewPipe (.zip)",
@ -43,7 +42,6 @@
"source": "sorgente",
"Log in": "Accedi",
"Log in/register": "Accedi/Registrati",
"Log in with Google": "Accedi con Google",
"User ID": "ID utente",
"Password": "Password",
"Time (h:mm:ss):": "Orario (h:mm:ss):",
@ -52,7 +50,6 @@
"Sign In": "Accedi",
"Register": "Registrati",
"E-mail": "E-mail",
"Google verification code": "Codice di verifica Google",
"Preferences": "Preferenze",
"preferences_category_player": "Preferenze del riproduttore",
"preferences_video_loop_label": "Ripeti sempre: ",
@ -158,7 +155,7 @@
"generic_views_count_plural": "{{count}} visualizzazioni",
"Premieres in `x`": "In anteprima in `x`",
"Premieres `x`": "In anteprima `x`",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Ciao! Sembra che tu abbia disattivato JavaScript. Clicca qui per visualizzare i commenti. Considera che potrebbe volerci più tempo.",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Ciao, Sembra che tu abbia disattivato JavaScript. Clicca qui per visualizzare i commenti, ma considera che il caricamento potrebbe richiedere più tempo.",
"View YouTube comments": "Visualizza i commenti da YouTube",
"View more comments on Reddit": "Visualizza più commenti su Reddit",
"View `x` comments": {
@ -169,17 +166,12 @@
"Hide replies": "Nascondi le risposte",
"Show replies": "Mostra le risposte",
"Incorrect password": "Password sbagliata",
"Quota exceeded, try again in a few hours": "Limite superato, prova di nuovo fra qualche ora",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Impossibile autenticarsi, controlla che l'autenticazione in due passaggi (Authenticator o SMS) sia attiva.",
"Invalid TFA code": "Codice di autenticazione a due fattori non valido",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Login fallito. L'errore potrebbe essere causato dal fatto che la verifica in due passaggi non è attiva sul tuo account.",
"Wrong answer": "Risposta errata",
"Erroneous CAPTCHA": "CAPTCHA errato",
"CAPTCHA is a required field": "Il CAPTCHA è un campo obbligatorio",
"User ID is a required field": "L'ID utente è obbligatorio",
"Password is a required field": "La password è un campo obbligatorio",
"Wrong username or password": "Nome utente o password errati",
"Please sign in using 'Log in with Google'": "Per favore accedi con «Entra con Google»",
"Password cannot be empty": "La password non può essere vuota",
"Password cannot be longer than 55 characters": "La password non può contenere più di 55 caratteri",
"Please log in": "Per favore, accedi",
@ -212,7 +204,7 @@
"Azerbaijani": "Azero",
"Bangla": "Bengalese",
"Basque": "Basco",
"Belarusian": "Biellorusso",
"Belarusian": "Bielorusso",
"Bosnian": "Bosniaco",
"Bulgarian": "Bulgaro",
"Burmese": "Birmano",
@ -238,10 +230,10 @@
"Haitian Creole": "Creolo haitiano",
"Hausa": "Lingua hausa",
"Hawaiian": "Hawaiano",
"Hebrew": "Ebreo",
"Hebrew": "Ebraico",
"Hindi": "Hindi",
"Hmong": "Hmong",
"Hungarian": "Ungarese",
"Hungarian": "Ungherese",
"Icelandic": "Islandese",
"Igbo": "Igbo",
"Indonesian": "Indonesiano",
@ -254,7 +246,7 @@
"Khmer": "Khmer",
"Korean": "Coreano",
"Kurdish": "Curdo",
"Kyrgyz": "Kirghize",
"Kyrgyz": "Kirghiso",
"Lao": "Lao",
"Latin": "Latino",
"Latvian": "Lettone",
@ -269,7 +261,7 @@
"Marathi": "Marathi",
"Mongolian": "Mongolo",
"Nepali": "Nepalese",
"Norwegian Bokmål": "Norvegese",
"Norwegian Bokmål": "Norvegese bokmål",
"Nyanja": "Nyanja",
"Pashto": "Pashtu",
"Persian": "Persiano",
@ -278,7 +270,7 @@
"Punjabi": "Punjabi",
"Romanian": "Rumeno",
"Russian": "Russo",
"Samoan": "Samoan",
"Samoan": "Samoano",
"Scottish Gaelic": "Gaelico scozzese",
"Serbian": "Serbo",
"Shona": "Shona",
@ -290,18 +282,18 @@
"Southern Sotho": "Sotho del Sud",
"Spanish": "Spagnolo",
"Spanish (Latin America)": "Spagnolo (America latina)",
"Sundanese": "Sudanese",
"Sundanese": "Sundanese",
"Swahili": "Swahili",
"Swedish": "Svedese",
"Tajik": "Tajik",
"Tajik": "Tagico",
"Tamil": "Tamil",
"Telugu": "Telugu",
"Thai": "Thaï",
"Thai": "Thailandese",
"Turkish": "Turco",
"Ukrainian": "Ucraino",
"Urdu": "Urdu",
"Uzbek": "Uzbeco",
"Vietnamese": "Vietnamese",
"Vietnamese": "Vietnamita",
"Welsh": "Gallese",
"Western Frisian": "Frisone occidentale",
"Xhosa": "Xhosa",
@ -340,13 +332,12 @@
"%A %B %-d, %Y": "%A %-d %B %Y",
"(edited)": "(modificato)",
"YouTube comment permalink": "Link permanente al commento di YouTube",
"permalink": "permalink",
"permalink": "perma-collegamento",
"`x` marked it with a ❤": "`x` l'ha contrassegnato con un ❤",
"Audio mode": "Modalità audio",
"Video mode": "Modalità video",
"Videos": "Video",
"channel_tab_videos_label": "Video",
"Playlists": "Playlist",
"Community": "Comunità",
"search_filters_sort_option_relevance": "Pertinenza",
"search_filters_sort_option_rating": "Valutazione",
"search_filters_sort_option_date": "Data di caricamento",
@ -364,7 +355,7 @@
"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_hd": "HD",
"search_filters_features_option_subtitles": "Sottotitoli / CC",
"search_filters_features_option_c_commons": "Creative Commons",
"search_filters_features_option_three_d": "3D",
@ -383,9 +374,9 @@
"preferences_quality_dash_option_4320p": "4320p",
"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.",
"Released under the AGPLv3 on Github.": "Pubblicato su GitHub con licenza AGPLv3.",
"preferences_quality_option_medium": "Media",
"preferences_quality_option_small": "Piccola",
"preferences_quality_option_small": "Limitata",
"preferences_quality_dash_option_best": "Migliore",
"preferences_quality_dash_option_worst": "Peggiore",
"invidious": "Invidious",
@ -393,7 +384,7 @@
"preferences_quality_option_hd720": "HD720",
"preferences_quality_dash_option_auto": "Automatica",
"videoinfo_watch_on_youTube": "Guarda su YouTube",
"preferences_extend_desc_label": "Espandi automaticamente la descrizione del video: ",
"preferences_extend_desc_label": "Estendi automaticamente la descrizione del video: ",
"preferences_vr_mode_label": "Video interattivi a 360 gradi: ",
"Show less": "Mostra di meno",
"Switch Invidious Instance": "Cambia istanza Invidious",
@ -425,5 +416,65 @@
"search_filters_type_option_show": "Serie",
"search_filters_duration_option_short": "Corto (< 4 minuti)",
"search_filters_duration_option_long": "Lungo (> 20 minuti)",
"search_filters_features_option_purchased": "Acquistato"
"search_filters_features_option_purchased": "Acquistato",
"comments_view_x_replies": "Vedi {{count}} risposta",
"comments_view_x_replies_plural": "Vedi {{count}} risposte",
"comments_points_count": "{{count}} punto",
"comments_points_count_plural": "{{count}} punti",
"Portuguese (auto-generated)": "Portoghese (generati automaticamente)",
"crash_page_you_found_a_bug": "Sembra che tu abbia trovato un bug in Invidious!",
"crash_page_switch_instance": "provato a <a href=\"`x`\">usare un'altra istanza</a>",
"crash_page_before_reporting": "Prima di segnalare un bug, assicurati di aver:",
"crash_page_read_the_faq": "letto le <a href=\"`x`\">domande più frequenti (FAQ)</a>",
"crash_page_search_issue": "cercato tra <a href=\"`x`\"> i problemi esistenti su GitHub</a>",
"crash_page_report_issue": "Se niente di tutto ciò ha aiutato, per favore <a href=\"`x`\">apri un nuovo problema su GitHub</a> (preferibilmente in inglese) e includi il seguente testo nel tuo messaggio (NON tradurre il testo):",
"Popular enabled: ": "Popolare attivato: ",
"English (United Kingdom)": "Inglese (Regno Unito)",
"Portuguese (Brazil)": "Portoghese (Brasile)",
"preferences_watch_history_label": "Attiva cronologia di riproduzione: ",
"French (auto-generated)": "Francese (generati automaticamente)",
"search_message_use_another_instance": " Puoi anche <a href=\"`x`\">cercare in un'altra istanza</a>.",
"search_message_no_results": "Nessun risultato trovato.",
"search_message_change_filters_or_query": "Prova ad ampliare la ricerca e/o modificare i filtri.",
"English (United States)": "Inglese (Stati Uniti)",
"Cantonese (Hong Kong)": "Cantonese (Hong Kong)",
"Chinese": "Cinese",
"Chinese (China)": "Cinese (Cina)",
"Chinese (Hong Kong)": "Cinese (Hong Kong)",
"Chinese (Taiwan)": "Cinese (Taiwan)",
"Dutch (auto-generated)": "Olandese (generati automaticamente)",
"German (auto-generated)": "Tedesco (generati automaticamente)",
"Indonesian (auto-generated)": "Indonesiano (generati automaticamente)",
"Interlingue": "Interlingua",
"Italian (auto-generated)": "Italiano (generati automaticamente)",
"Japanese (auto-generated)": "Giapponese (generati automaticamente)",
"Korean (auto-generated)": "Coreano (generati automaticamente)",
"Russian (auto-generated)": "Russo (generati automaticamente)",
"Spanish (auto-generated)": "Spagnolo (generati automaticamente)",
"Spanish (Mexico)": "Spagnolo (Messico)",
"Spanish (Spain)": "Spagnolo (Spagna)",
"Turkish (auto-generated)": "Turco (auto-generato)",
"Vietnamese (auto-generated)": "Vietnamita (auto-generato)",
"search_filters_date_label": "Data caricamento",
"search_filters_date_option_none": "Qualunque data",
"search_filters_type_option_all": "Qualunque tipo",
"search_filters_duration_option_none": "Qualunque durata",
"search_filters_duration_option_medium": "Media (4 - 20 minuti)",
"search_filters_features_option_vr180": "VR180",
"search_filters_apply_button": "Applica filtri selezionati",
"crash_page_refresh": "provato a <a href=\"`x`\">ricaricare la pagina</a>",
"error_video_not_in_playlist": "Il video richiesto non esiste in questa playlist. <a href=\"`x`\">Fai clic qui per la pagina iniziale della playlist.</a>",
"channel_tab_shorts_label": "Short",
"channel_tab_playlists_label": "Playlist",
"channel_tab_channels_label": "Canali",
"channel_tab_streams_label": "Livestream",
"channel_tab_community_label": "Comunità",
"Music in this video": "Musica in questo video",
"Artist: ": "Artista: ",
"Album: ": "Album: ",
"Download is disabled": "Il download è disabilitato",
"Song: ": "Canzone: ",
"Standard YouTube license": "Licenza standard di YouTube",
"Channel Sponsor": "Sponsor del canale",
"Import YouTube playlist (.csv)": "Importa playlist di YouTube (.csv)"
}

@ -1,15 +1,15 @@
{
"generic_views_count_0": "{{count}} 回視聴",
"generic_videos_count_0": "{{count}}の動画",
"generic_playlists_count_0": "{{count}} 個の再生リスト",
"generic_videos_count_0": "{{count}}の動画",
"generic_playlists_count_0": "{{count}}個の再生リスト",
"generic_subscribers_count_0": "{{count}} 人の登録者",
"generic_subscriptions_count_0": "{{count}} 個の登録チャンネル",
"generic_subscriptions_count_0": "{{count}}個の登録チャンネル",
"LIVE": "ライブ",
"Shared `x` ago": "`x`前に共有",
"Shared `x` ago": "`x`前に公開",
"Unsubscribe": "登録解除",
"Subscribe": "登録",
"View channel on YouTube": "YouTube でチャンネルを見る",
"View playlist on YouTube": "YouTube で再生リストを見る",
"View channel on YouTube": "YouTube でチャンネルを表示",
"View playlist on YouTube": "YouTube で再生リストを表示",
"newest": "新しい順",
"oldest": "古い順",
"popular": "人気順",
@ -19,7 +19,6 @@
"Clear watch history?": "再生履歴を削除しますか?",
"New password": "新しいパスワード",
"New passwords must match": "新しいパスワードが一致していません",
"Cannot change password for Google accounts": "Google アカウントのパスワードは変更できません",
"Authorize token?": "トークンを認証しますか?",
"Authorize token for `x`?": "トークン `x` を認証しますか?",
"Yes": "はい",
@ -42,7 +41,6 @@
"source": "ソース",
"Log in": "ログイン",
"Log in/register": "ログイン/登録",
"Log in with Google": "Google でログイン",
"User ID": "ユーザー ID",
"Password": "パスワード",
"Time (h:mm:ss):": "時間 (時:分分:秒秒):",
@ -51,38 +49,37 @@
"Sign In": "サインイン",
"Register": "登録",
"E-mail": "メールアドレス",
"Google verification code": "Google 認証コード",
"Preferences": "設定",
"preferences_category_player": "プレイヤー設定",
"preferences_category_player": "プレイヤー設定",
"preferences_video_loop_label": "常にループ: ",
"preferences_autoplay_label": "自動再生: ",
"preferences_continue_label": "デフォルトで次を再生: ",
"preferences_continue_label": "の動画を再生をオン: ",
"preferences_continue_autoplay_label": "次の動画を自動再生: ",
"preferences_listen_label": "デフォルトでオーディオモードを使用: ",
"preferences_local_label": "動画をプロキシーに通す: ",
"preferences_speed_label": "デフォルトの再生速度: ",
"preferences_listen_label": "音声モードを使用: ",
"preferences_local_label": "動画視聴にプロキシを経由: ",
"preferences_speed_label": "再生速度の初期値: ",
"preferences_quality_label": "優先する画質: ",
"preferences_volume_label": "プレイヤーの音量: ",
"preferences_comments_label": "デフォルトのコメント: ",
"youtube": "YouTube",
"reddit": "Reddit",
"preferences_captions_label": "デフォルトの字幕: ",
"preferences_captions_label": "優先する字幕: ",
"Fallback captions: ": "フォールバック時の字幕: ",
"preferences_related_videos_label": "関連動画を表示: ",
"preferences_annotations_label": "デフォルトでアノテーションを表示: ",
"preferences_annotations_label": "最初からアノテーションを表示: ",
"preferences_extend_desc_label": "動画の説明文を自動的に拡張: ",
"preferences_vr_mode_label": "対話的な360°動画 (WebGL が必要): ",
"preferences_category_visual": "外観設定",
"preferences_player_style_label": "プレイヤースタイル: ",
"preferences_player_style_label": "プレイヤースタイル: ",
"Dark mode: ": "ダークモード: ",
"preferences_dark_mode_label": "テーマ: ",
"dark": "ダーク",
"light": "ライト",
"preferences_thin_mode_label": "最小モード: ",
"preferences_category_misc": "設定",
"preferences_automatic_instance_redirect_label": "自動的なインスタンスの移転redirect.invidious.ioにフォールバック ",
"preferences_category_misc": "ほかの設定",
"preferences_automatic_instance_redirect_label": "インスタンスの自動転送 (redirect.invidious.ioにフォールバック): ",
"preferences_category_subscription": "登録チャンネル設定",
"preferences_annotations_subscribed_label": "デフォルトで登録チャンネルのアノテーションを表示しますか? ",
"preferences_annotations_subscribed_label": "最初から登録チャンネルのアノテーションを表示 ",
"Redirect homepage to feed: ": "ホームからフィードにリダイレクト: ",
"preferences_max_results_label": "フィードに表示する動画の量: ",
"preferences_sort_label": "動画を並び替え: ",
@ -108,104 +105,99 @@
"Watch history": "再生履歴",
"Delete account": "アカウントを削除",
"preferences_category_admin": "管理者設定",
"preferences_default_home_label": "デフォルトのホーム: ",
"preferences_feed_menu_label": "フィードメニュー: ",
"preferences_show_nick_label": "ニックネームを一番上に表示する: ",
"preferences_default_home_label": "ホームに表示するページ: ",
"preferences_feed_menu_label": "フィードメニュー: ",
"preferences_show_nick_label": "ログイン名を上部に表示: ",
"Top enabled: ": "トップページを有効化: ",
"CAPTCHA enabled: ": "CAPTCHA を有効化: ",
"Login enabled: ": "ログインを有効化: ",
"Registration enabled: ": "登録を有効化: ",
"Report statistics: ": "統計を報告: ",
"Save preferences": "設定を保存",
"Subscription manager": "登録チャンネルマネージャー",
"Token manager": "トークンマネージャー",
"Subscription manager": "登録チャンネルの管理",
"Token manager": "トークンの管理",
"Token": "トークン",
"tokens_count_0": "{{count}} 個のトークン",
"tokens_count_0": "{{count}}個のトークン",
"Import/export": "インポート/エクスポート",
"unsubscribe": "登録解除",
"revoke": "取り消す",
"Subscriptions": "登録チャンネル",
"subscriptions_unseen_notifs_count_0": "{{count}}の未読通知",
"subscriptions_unseen_notifs_count_0": "{{count}}の未読通知",
"search": "検索",
"Log out": "ログアウト",
"Released under the AGPLv3 on Github.": "GitHub 上で AGPLv3 の元で公開されています。",
"Released under the AGPLv3 on Github.": "GitHub 上で AGPLv3 の元で公開",
"Source available here.": "ソースはここで閲覧可能です。",
"View JavaScript license information.": "JavaScript ライセンス情報",
"View privacy policy.": "プライバシーポリシー",
"View privacy policy.": "個人情報保護方針",
"Trending": "急上昇",
"Public": "公開",
"Unlisted": "限定公開",
"Private": "非公開",
"View all playlists": "再生リストをすべて見る",
"View all playlists": "すべての再生リストを表示",
"Updated `x` ago": "`x`前に更新",
"Delete playlist `x`?": "再生リスト `x` を削除しますか?",
"Delete playlist": "再生リストを削除",
"Create playlist": "再生リストを作成",
"Title": "タイトル",
"Playlist privacy": "再生リストのプライバシー",
"Playlist privacy": "再生リストの公開状態",
"Editing playlist `x`": "再生リスト `x` を編集中",
"Show more": "表示を増やす",
"Show less": "表示を減らす",
"Watch on YouTube": "YouTube で視聴",
"Switch Invidious Instance": "Invidiousインスタンスの変更",
"Show more": "もっと見る",
"Show less": "表示を少なく",
"Watch on YouTube": "YouTubeで視聴",
"Switch Invidious Instance": "Invidious インスタンスの変更",
"Hide annotations": "アノテーションを隠す",
"Show annotations": "アノテーションを表示",
"Genre: ": "ジャンル: ",
"License: ": "ライセンス: ",
"Family friendly? ": "家族向け: ",
"Wilson score: ": "ウィルソンスコア: ",
"Wilson score: ": "ウィルソン得点区間: ",
"Engagement: ": "エンゲージメント: ",
"Whitelisted regions: ": "ホワイトリストの地域: ",
"Blacklisted regions: ": "ブラックリストの地域: ",
"Shared `x`": "`x`に共有",
"Shared `x`": "公開日 `x`",
"Premieres in `x`": "`x`後にプレミア公開",
"Premieres `x`": "`x`にプレミア公開",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "やあ!君は JavaScript を無効にしているのかな?ここをクリックしてコメントを見れるけど、読み込みには少し時間がかかることがあるのを覚えておいてね。",
"View YouTube comments": "YouTube のコメントを見る",
"View YouTube comments": "YouTube のコメントを表示",
"View more comments on Reddit": "Reddit でコメントをもっと見る",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` 件のコメントを見る",
"": "`x` 件のコメントを見る"
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` 件のコメントを表示",
"": "`x` 件のコメントを表示"
},
"View Reddit comments": "Reddit のコメントを見る",
"View Reddit comments": "Reddit のコメントを表示",
"Hide replies": "返信を非表示",
"Show replies": "返信を表示",
"Incorrect password": "パスワードが間違っています",
"Quota exceeded, try again in a few hours": "試行を制限中です。数時間後にやり直してください",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "ログインできませんでした。2段階認証 (認証アプリまたは SMS) が有効になっていることを確認してください。",
"Invalid TFA code": "TFA (2段階認証) コードが無効です",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "ログインに失敗しました。あなたのアカウントで2段階認証が有効になっていない可能性があります。",
"Wrong answer": "回答が間違っています",
"Erroneous CAPTCHA": "CAPTCHA が間違っています",
"CAPTCHA is a required field": "CAPTCHA は必須項目です",
"User ID is a required field": "ユーザー ID は必須項目です",
"Password is a required field": "パスワードは必須項目です",
"Wrong username or password": "ユーザー名またはパスワードが間違っています",
"Please sign in using 'Log in with Google'": "'Google でログイン' を使用してログインしてください",
"Password cannot be empty": "パスワードを空にすることはできません",
"Password cannot be empty": "パスワードは空にできません",
"Password cannot be longer than 55 characters": "パスワードは55文字より長くできません",
"Please log in": "ログインしてください",
"Invidious Private Feed for `x`": "`x` の Invidious プライベートフィード",
"Please log in": "ログインしてください",
"Invidious Private Feed for `x`": "`x` 個人の Invidious によるフィード",
"channel:`x`": "チャンネル:`x`",
"Deleted or invalid channel": "削除済みまたは無効なチャンネルです",
"This channel does not exist.": "このチャンネルは存在しません。",
"Could not get channel info.": "チャンネル情報を取得できませんでした。",
"Could not fetch comments": "コメントを取得できませんでした",
"comments_view_x_replies_0": "{{count}} 件の返信を見る",
"comments_view_x_replies_0": "{{count}}件の返信を表示",
"`x` ago": "`x`前",
"Load more": "もっと読み込む",
"comments_points_count_0": "{{count}} ポイント",
"Load more": "もっと見る",
"comments_points_count_0": "{{count}}",
"Could not create mix.": "ミックスを作成できませんでした。",
"Empty playlist": "空の再生リスト",
"Not a playlist.": "再生リストではありません。",
"Playlist does not exist.": "再生リストが存在しません。",
"Could not pull trending pages.": "急上昇ページを取得できませんでした。",
"Hidden field \"challenge\" is a required field": "非表示項目 \"challenge\" は必須項目です",
"Hidden field \"token\" is a required field": "非表示項目 \"token\" は必須項目です",
"Hidden field \"challenge\" is a required field": "非表示項目 challenge は必須項目です",
"Hidden field \"token\" is a required field": "非表示項目 token は必須項目です",
"Erroneous challenge": "チャレンジが間違っています",
"Erroneous token": "トークンが間違っています",
"No such user": "ユーザーが存在しません",
"Token is expired, please try again": "トークンが期限切れです。再度試しください",
"Token is expired, please try again": "トークンが期限切れです。再度試しください",
"English": "英語",
"English (auto-generated)": "英語 (自動生成)",
"Afrikaans": "アフリカーンス語",
@ -313,8 +305,8 @@
"Yoruba": "ヨルバ語",
"Zulu": "ズール語",
"generic_count_years_0": "{{count}}年",
"generic_count_months_0": "{{count}}月",
"generic_count_weeks_0": "{{count}}週",
"generic_count_months_0": "{{count}}月",
"generic_count_weeks_0": "{{count}}週",
"generic_count_days_0": "{{count}}日",
"generic_count_hours_0": "{{count}}時間",
"generic_count_minutes_0": "{{count}}分",
@ -326,8 +318,8 @@
"About": "このサービスについて",
"Rating: ": "評価: ",
"preferences_locale_label": "言語: ",
"View as playlist": "再生リストで見る",
"Default": "デフォルト",
"View as playlist": "再生リストとして閲覧",
"Default": "標準",
"Music": "音楽",
"Gaming": "ゲーム",
"News": "ニュース",
@ -338,21 +330,21 @@
"(edited)": "(編集済み)",
"YouTube comment permalink": "YouTube コメントのパーマリンク",
"permalink": "パーマリンク",
"`x` marked it with a ❤": "`x` が❤を込めてマークしました",
"Audio mode": "オーディオモード",
"Video mode": "ビデオモード",
"Videos": "動画",
"Playlists": "プレイリスト",
"Community": "コミュニティ",
"search_filters_sort_option_relevance": "関連",
"`x` marked it with a ❤": "`x` が❤を送りました",
"Audio mode": "音声モード",
"Video mode": "動画モード",
"channel_tab_videos_label": "動画",
"Playlists": "再生リスト",
"channel_tab_community_label": "コミュニティ",
"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_sort_option_date": "アップロード日",
"search_filters_sort_option_views": "視聴回数",
"search_filters_type_label": "種類",
"search_filters_duration_label": "再生時間",
"search_filters_features_label": "機能",
"search_filters_features_label": "特徴",
"search_filters_sort_label": "順番",
"search_filters_date_option_hour": "1時間前",
"search_filters_date_option_hour": "1時間以内",
"search_filters_date_option_today": "今日",
"search_filters_date_option_week": "今週",
"search_filters_date_option_month": "今月",
@ -366,7 +358,7 @@
"search_filters_features_option_subtitles": "字幕",
"search_filters_features_option_c_commons": "クリエイティブ・コモンズ",
"search_filters_features_option_three_d": "3D",
"search_filters_features_option_live": "生配信",
"search_filters_features_option_live": "ライブ",
"search_filters_features_option_four_k": "4K",
"search_filters_features_option_location": "場所",
"search_filters_features_option_hdr": "HDR",
@ -375,15 +367,15 @@
"next_steps_error_message_refresh": "再読込",
"next_steps_error_message_go_to_youtube": "YouTubeへ",
"search_filters_duration_option_short": "4 分未満",
"footer_documentation": "書",
"footer_documentation": "説明書",
"footer_source_code": "ソースコード",
"footer_original_source_code": "ソースコード(元)",
"footer_modfied_source_code": "ソースコード(編集)",
"adminprefs_modified_source_code_url_label": "編集したソースコードのレポジトリーURL",
"footer_original_source_code": "元のソースコード",
"footer_modfied_source_code": "改変して使用",
"adminprefs_modified_source_code_url_label": "改変されたソースコードのレポジトリのURL",
"search_filters_duration_option_long": "20 分以上",
"preferences_region_label": "地域: ",
"footer_donate_page": "寄付する",
"preferences_quality_dash_label": "優先するDash画質 : ",
"preferences_quality_dash_label": "優先するDASH画質: ",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_144p": "144p",
@ -403,13 +395,13 @@
"none": "なし",
"download_subtitles": "字幕 - `x` (.vtt)",
"search_filters_features_option_purchased": "購入済み",
"preferences_quality_option_dash": "DASH (適切な品質)",
"preferences_quality_option_dash": "DASH (適応的画質)",
"preferences_quality_dash_option_worst": "最悪",
"preferences_quality_dash_option_best": "最高",
"videoinfo_started_streaming_x_ago": "`x`前に配信を開始",
"videoinfo_watch_on_youTube": "YouTube上で見る",
"user_created_playlists": "`x`が作成したプレイリスト",
"Video unavailable": "ビデオは利用できません",
"videoinfo_started_streaming_x_ago": "`x`前に配信を開始",
"videoinfo_watch_on_youTube": "YouTubeで視聴",
"user_created_playlists": "`x`個の作成した再生リスト",
"Video unavailable": "動画は利用できません",
"Chinese": "中国語",
"Chinese (Taiwan)": "中国語 (台湾)",
"Korean (auto-generated)": "韓国語 (自動生成)",
@ -433,5 +425,40 @@
"Spanish (Spain)": "スペイン語 (スペイン)",
"Vietnamese (auto-generated)": "ベトナム語 (自動生成)",
"search_filters_title": "フィルタ",
"search_filters_features_option_three_sixty": "360°"
"search_filters_features_option_three_sixty": "360°",
"search_message_change_filters_or_query": "別の検索語句を試したり、検索フィルタを変更してください。",
"search_message_no_results": "一致する検索結果はありません。",
"English (United States)": "英語 (アメリカ)",
"search_filters_date_label": "アップロード日",
"search_filters_features_option_vr180": "VR180",
"crash_page_switch_instance": "<a href=\"`x`\">別のインスタンスを使用</a>を試す",
"crash_page_read_the_faq": "<a href=\"`x`\">よくある質問 (FAQ)</a> を読む",
"Popular enabled: ": "人気動画を有効化 ",
"search_message_use_another_instance": " <a href=\"`x`\">別のインスタンス上での検索</a>も可能です。",
"search_filters_apply_button": "選択したフィルターを適用",
"user_saved_playlists": "`x`個の保存済みの再生リスト",
"crash_page_you_found_a_bug": "Invidious のバグのようです!",
"crash_page_refresh": "<a href=\"`x`\">ページを更新</a>を試す",
"preferences_watch_history_label": "再生履歴を有効化 ",
"search_filters_date_option_none": "すべて",
"search_filters_type_option_all": "すべての種類",
"search_filters_duration_option_none": "すべての長さ",
"search_filters_duration_option_medium": "4 20 分",
"preferences_save_player_pos_label": "再生位置を保存: ",
"crash_page_before_reporting": "バグを報告する前に、次のことを確認してください。",
"crash_page_report_issue": "上記が助けにならないなら、<a href=\"`x`\">GitHub</a> に新しい issue を作成し(英語が好ましい)、メッセージに次のテキストを含めてください(テキストは翻訳しない)。",
"crash_page_search_issue": "<a href=\"`x`\">GitHub の既存の問題 (issue)</a> を検索",
"channel_tab_streams_label": "ライブ",
"channel_tab_playlists_label": "再生リスト",
"error_video_not_in_playlist": "要求された動画はこの再生リスト内に存在しません。<a href=\"`x`\">再生リストのホームへ。</a>",
"channel_tab_shorts_label": "ショート",
"channel_tab_channels_label": "チャンネル",
"Music in this video": "この動画の音楽",
"Artist: ": "アーティスト: ",
"Album: ": "アルバム: ",
"Song: ": "曲: ",
"Channel Sponsor": "チャンネルのスポンサー",
"Standard YouTube license": "標準 Youtube ライセンス",
"Download is disabled": "ダウンロード: このインスタンスでは未対応",
"Import YouTube playlist (.csv)": "YouTube 再生リストをインポート (.csv)"
}

@ -2,7 +2,7 @@
"preferences_sort_label": "동영상 정렬 기준: ",
"preferences_max_results_label": "피드에 표시된 동영상 수: ",
"Redirect homepage to feed: ": "피드로 홈페이지 리디렉션: ",
"preferences_annotations_subscribed_label": "구독한 채널에 기본적으로 특수효과를 표시하시겠습니까? ",
"preferences_annotations_subscribed_label": "구독한 채널에 기본으로 주석 표시: ",
"preferences_category_subscription": "구독 설정",
"preferences_automatic_instance_redirect_label": "자동 인스턴스 리디렉션 (redirect.invidious.io로 대체): ",
"preferences_thin_mode_label": "단순 모드: ",
@ -11,73 +11,70 @@
"preferences_dark_mode_label": "테마: ",
"Dark mode: ": "다크 모드: ",
"preferences_player_style_label": "플레이어 스타일: ",
"preferences_category_visual": "시각 설정",
"preferences_vr_mode_label": "인터랙티브 360도 비디오: ",
"preferences_extend_desc_label": "자동으로 비디오 설명 확장: ",
"preferences_annotations_label": "기본으로 주석 표시: ",
"preferences_category_visual": "환경 설정",
"preferences_vr_mode_label": "VR 영상 활성화(WebGL 필요): ",
"preferences_extend_desc_label": "자동으로 비디오 설명 확장: ",
"preferences_annotations_label": "기본으로 주석 표시: ",
"preferences_related_videos_label": "관련 동영상 보기: ",
"Fallback captions: ": "대체 자막: ",
"preferences_captions_label": "기본 자막: ",
"reddit": "Reddit",
"youtube": "YouTube",
"reddit": "레딧",
"youtube": "유튜브",
"preferences_comments_label": "기본 댓글: ",
"preferences_volume_label": "플레이어 볼륨: ",
"preferences_quality_label": "선호하는 비디오 품질: ",
"preferences_speed_label": "기본 속도: ",
"preferences_local_label": "비디오를 프록시: ",
"preferences_listen_label": "기본적으로 듣기: ",
"preferences_continue_autoplay_label": "다음 동영상 자동재생 ",
"preferences_continue_label": "기본적으로 다음 재생: ",
"preferences_listen_label": "라디오 모드: ",
"preferences_continue_autoplay_label": "다음 동영상 자동재생: ",
"preferences_continue_label": "다음 동영상으로 이동: ",
"preferences_autoplay_label": "자동재생: ",
"preferences_video_loop_label": "항상 반복: ",
"preferences_category_player": "플레이어 설정",
"Preferences": "설정",
"Google verification code": "구글 인증 코드",
"E-mail": "이메일",
"Register": "회원가입",
"Sign In": "로그인",
"preferences_category_misc": "기타 설정",
"Image CAPTCHA": "이미지 CAPTCHA",
"Text CAPTCHA": "텍스트 CAPTCHA",
"Image CAPTCHA": "이미지 캡차",
"Text CAPTCHA": "텍스트 캡차",
"Time (h:mm:ss):": "시각 (h:mm:ss):",
"Password": "비밀번호",
"User ID": "사용자 ID",
"Log in with Google": "구글로 로그인",
"Log in/register": "로그인/회원가입",
"Log in": "로그인",
"source": "출처",
"JavaScript license information": "JavaScript 라이선스 정보",
"An alternative front-end to YouTube": "YouTube의 대안 프론트엔드",
"JavaScript license information": "자바스크립트 라이선스 정보",
"An alternative front-end to YouTube": "유튜브의 프론트엔드 대안",
"History": "역사",
"Delete account?": "계정을 삭제 하시겠습니까?",
"Export data as JSON": "데이터를 JSON으로 내보내기",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "구독을 OPML로 내보내기 (NewPipe 및 FreeTube 용)",
"Export subscriptions as OPML": "구독을 OPML로 내보내기",
"Export data as JSON": "JSON으로 데이터 내보내기",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "OPML로 구독 내보내기 (뉴파이프 및 프리튜브)",
"Export subscriptions as OPML": "OPML로 구독 내보내기",
"Export": "내보내기",
"Import NewPipe data (.zip)": "NewPipe 데이터 가져오기 (.zip)",
"Import NewPipe subscriptions (.json)": "NewPipe 구독을 가져오기 (.json)",
"Import FreeTube subscriptions (.db)": "FreeTube 구독 가져오기 (.db)",
"Import YouTube subscriptions": "YouTube 구독 가져오기",
"Import Invidious data": "Invidious 데이터 가져오기",
"Import NewPipe data (.zip)": "뉴파이프 데이터 가져오기 (.zip)",
"Import NewPipe subscriptions (.json)": "뉴파이프 구독 가져오기 (.json)",
"Import FreeTube subscriptions (.db)": "프리튜브 구독 가져오기 (.db)",
"Import YouTube subscriptions": "유튜브 구독 가져오기",
"Import Invidious data": "인비디어스 데이터 가져오기 (.json)",
"Import": "가져오기",
"Import and Export Data": "데이터 가져오기 및 내보내기",
"No": "아니요",
"Yes": "예",
"Authorize token for `x`?": "`x` 에 대한 토큰을 승인하시겠습니까?",
"Authorize token?": "토큰을 승인하시겠습니까?",
"Cannot change password for Google accounts": "Google 계정의 비밀번호를 변경할 수 없습니다",
"New passwords must match": "새 비밀번호는 일치해야 합니다",
"New password": "새 비밀번호",
"Clear watch history?": "재생 기록을 삭제 하시겠습니까?",
"Previous page": "이전 페이지",
"Next page": "다음 페이지",
"last": "마지막",
"Shared `x` ago": "`x` 전에 공유",
"Shared `x` ago": "`x` 전",
"popular": "인기",
"oldest": "오래된순",
"newest": "최신순",
"View playlist on YouTube": "YouTube에서 재생목록 보기",
"View channel on YouTube": "YouTube에서 채널 보기",
"View playlist on YouTube": "유튜브에서 재생목록 보기",
"View channel on YouTube": "유튜브에서 채널 보기",
"Subscribe": "구독",
"Unsubscribe": "구독 취소",
"LIVE": "실시간",
@ -91,7 +88,7 @@
"Japanese": "일본어",
"Greek": "그리스어",
"German": "독일어",
"Chinese (Traditional)": "중국어 (정자)",
"Chinese (Traditional)": "중국어 (정자)",
"Chinese (Simplified)": "중국어 (간체자)",
"French": "프랑스어",
"Finnish": "핀란드어",
@ -112,7 +109,6 @@
"This channel does not exist.": "이 채널은 존재하지 않습니다.",
"Deleted or invalid channel": "삭제되었거나 더 이상 존재하지 않는 채널",
"channel:`x`": "채널:`x`",
"Invalid TFA code": "유효하지 않은 TFA 코드",
"Show replies": "댓글 보기",
"Hide replies": "댓글 숨기기",
"Incorrect password": "잘못된 비밀번호",
@ -120,7 +116,7 @@
"Genre: ": "장르: ",
"Editing playlist `x`": "재생목록 `x` 수정하기",
"Playlist privacy": "재생목록 공개 범위",
"Watch on YouTube": "YouTube에서 보기",
"Watch on YouTube": "유튜브에서 보기",
"Show less": "간략히",
"Show more": "더보기",
"Title": "제목",
@ -129,13 +125,13 @@
"Delete playlist": "재생목록 삭제",
"Delete playlist `x`?": "재생목록 `x` 를 삭제 하시겠습니까?",
"Updated `x` ago": "`x` 전에 업데이트됨",
"Released under the AGPLv3 on Github.": "GitHub에 AGPLv3 으로 배포됩니다.",
"Released under the AGPLv3 on Github.": "깃허브에 AGPLv3 으로 배포됩니다.",
"View all playlists": "모든 재생목록 보기",
"Private": "비공개",
"Unlisted": "목록에 없음",
"Public": "공개",
"View privacy policy.": "개인정보 처리방침 보기.",
"View JavaScript license information.": "JavaScript 라이센스 정보 보기.",
"View JavaScript license information.": "자바스크립트 라이선스 정보 보기.",
"Source available here.": "소스는 여기에서 사용할 수 있습니다.",
"Log out": "로그아웃",
"search": "검색",
@ -150,9 +146,9 @@
"Subscription manager": "구독 관리자",
"Save preferences": "설정 저장",
"Report statistics: ": "통계 보고: ",
"Registration enabled: ": "등록 활성화: ",
"Registration enabled: ": "회원가입 활성화: ",
"Login enabled: ": "로그인 활성화: ",
"CAPTCHA enabled: ": "CAPTCHA 활성화: ",
"CAPTCHA enabled: ": "캡차 활성화: ",
"Top enabled: ": "Top 활성화: ",
"preferences_show_nick_label": "상단에 닉네임 표시: ",
"preferences_feed_menu_label": "피드 메뉴: ",
@ -183,12 +179,12 @@
"Russian": "러시아어",
"Romanian": "루마니아어",
"Punjabi": "펀자브어",
"Portuguese": "포르투갈어(포어)",
"Portuguese": "포르투갈어",
"Polish": "폴란드어",
"Persian": "페르시아어(파사어)",
"Persian": "페르시아어",
"Pashto": "파슈토어",
"Nyanja": "체와어",
"Norwegian Bokmål": "보크몰",
"Nyanja": "냔자어",
"Norwegian Bokmål": "노르웨이 부크몰어",
"Nepali": "네팔어",
"Mongolian": "몽골어",
"Marathi": "마라티어",
@ -202,7 +198,7 @@
"search_filters_features_option_hdr": "HDR",
"Current version: ": "현재 버전: ",
"next_steps_error_message_refresh": "새로 고침",
"next_steps_error_message_go_to_youtube": "YouTube로 가기",
"next_steps_error_message_go_to_youtube": "유튜브로 가기",
"search_filters_features_option_subtitles": "자막",
"`x` marked it with a ❤": "`x`님의 ❤",
"Download as: ": "다음으로 다운로드: ",
@ -225,7 +221,7 @@
"Kazakh": "카자흐어",
"Kannada": "칸나다어",
"Javanese": "자바어",
"Italian": "이탈리아어(이태리어)",
"Italian": "이탈리아어",
"Irish": "아일랜드어",
"Indonesian": "인도네시아어",
"Igbo": "이보어",
@ -245,18 +241,17 @@
"Could not create mix.": "믹스를 생성할 수 없습니다.",
"`x` ago": "`x` 전",
"comments_view_x_replies_0": "답글 {{count}}개 보기",
"View Reddit comments": "Reddit의 댓글 보기",
"View Reddit comments": "레딧 댓글 보기",
"Engagement: ": "약속: ",
"Wilson score: ": "Wilson Score: ",
"Family friendly? ": "가족 친화적입니까? ",
"Quota exceeded, try again in a few hours": "한도량을 초과했습니다. 몇 시간 후에 다시 시도하세요",
"Family friendly? ": "전연령 영상입니까? ",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` 개의 댓글 보기",
"": "`x` 개의 댓글 보기"
"([^.,0-9]|^)1([^.,0-9]|$)": "`x`개의 댓글 보기",
"": "`x`개의 댓글 보기"
},
"Haitian Creole": "아이티 크레올어",
"Gujarati": "구자라트어",
"Esperanto": "에스페란토(에스페란토어)",
"Esperanto": "에스페란토",
"Georgian": "조지아어",
"Galician": "갈리시아어",
"Filipino": "타갈로그어(필리핀어)",
@ -272,23 +267,20 @@
"Bulgarian": "불가리아어",
"Bosnian": "보스니아어",
"Belarusian": "벨라루스어",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "로그인할 수 없습니다. 이중 인증(Authenticator 또는 SMS)이 켜져 있는지 확인하세요.",
"View more comments on Reddit": "Reddit에서 더 많은 댓글 보기",
"View YouTube comments": "YouTube 댓글 보기",
"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`",
"View more comments on Reddit": "레딧에서 더 많은 댓글 보기",
"View YouTube comments": "유튜브 댓글 보기",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "자바스크립트가 꺼져 있는 것 같습니다! 댓글을 보려면 여기를 클릭하세요. 댓글을 로드하는 데 시간이 조금 더 걸릴 수 있습니다.",
"Shared `x`": "`x` 업로드",
"Whitelisted regions: ": "차단되지 않은 지역: ",
"search_filters_sort_option_views": "조회수",
"Please log in": "로그인하세요",
"Password cannot be longer than 55 characters": "비밀번호는 55자 이하여야 합니다",
"Password cannot be empty": "비밀번호는 비워둘 수 없습니다",
"Please sign in using 'Log in with Google'": "'Google로 로그인'을 사용하여 로그인하세요",
"Wrong username or password": "잘못된 사용자 이름 또는 비밀번호",
"Password is a required field": "비밀번호는 필수 필드입니다",
"User ID is a required field": "사용자 ID는 필수 필드입니다",
"CAPTCHA is a required field": "CAPTCHA는 필수 필드입니다",
"Erroneous CAPTCHA": "잘못된 CAPTCHA",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "로그인 실패. 계정에 이중 인증이 설정되어 있지 않기 때문일 수 있습니다.",
"Password is a required field": "비밀번호는 필수 입력란입니다",
"User ID is a required field": "사용자 ID는 필수 입력란입니다",
"CAPTCHA is a required field": "캡차는 필수 입력란입니다",
"Erroneous CAPTCHA": "잘못된 캡차",
"Blacklisted regions: ": "차단된 지역: ",
"Playlists": "재생목록",
"View as playlist": "재생목록으로 보기",
@ -297,7 +289,7 @@
"Empty playlist": "재생목록 비어 있음",
"Show annotations": "주석 보이기",
"Hide annotations": "주석 숨기기",
"Switch Invidious Instance": "Invidious 인스턴스 변경",
"Switch Invidious Instance": "인비디어스 인스턴스 변경",
"Spanish": "스페인어",
"Southern Sotho": "소토어",
"Somali": "소말리어",
@ -312,13 +304,13 @@
"Fallback comments: ": "대체 댓글: ",
"Swahili": "스와힐리어",
"Sundanese": "순다어",
"generic_count_years_0": "{{count}} 년",
"generic_count_months_0": "{{count}} 월",
"generic_count_weeks_0": "{{count}} 주",
"generic_count_days_0": "{{count}} 일",
"generic_count_hours_0": "{{count}} 시",
"generic_count_minutes_0": "{{count}} 분",
"generic_count_seconds_0": "{{count}} 초",
"generic_count_years_0": "{{count}}년",
"generic_count_months_0": "{{count}}월",
"generic_count_weeks_0": "{{count}}주",
"generic_count_days_0": "{{count}}일",
"generic_count_hours_0": "{{count}}",
"generic_count_minutes_0": "{{count}}분",
"generic_count_seconds_0": "{{count}}초",
"Zulu": "줄루어",
"Yoruba": "요루바어",
"Yiddish": "이디시어",
@ -337,9 +329,9 @@
"Swedish": "스웨덴어",
"Spanish (Latin America)": "스페인어 (라틴 아메리카)",
"comments_points_count_0": "{{count}} 포인트",
"Invidious Private Feed for `x`": "`x` 에 대한 Invidious 비공개 피드",
"Invidious Private Feed for `x`": "`x` 에 대한 인비디어스 비공개 피드",
"Premieres `x`": "최초 공개 `x`",
"Premieres in `x`": "`x` 최초 공개",
"Premieres in `x`": "`x` 최초 공개",
"next_steps_error_message": "다음 방법을 시도해 보세요: ",
"search_filters_features_option_c_commons": "크리에이티브 커먼즈",
"search_filters_duration_label": "길이",
@ -347,12 +339,12 @@
"search_filters_sort_option_date": "업로드 날짜",
"search_filters_sort_option_rating": "평점",
"search_filters_sort_option_relevance": "관련성",
"Community": "커뮤니티",
"Videos": "동영상",
"channel_tab_community_label": "커뮤니티",
"channel_tab_videos_label": "동영상",
"Video mode": "비디오 모드",
"Audio mode": "오디오 모드",
"permalink": "퍼머링크",
"YouTube comment permalink": "YouTube 댓글 퍼머링크",
"YouTube comment permalink": "유튜브 댓글 퍼머링크",
"(edited)": "(수정됨)",
"%A %B %-d, %Y": "%A %B %-d, %Y",
"Movies": "영화",
@ -374,12 +366,99 @@
"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분 초과",
"search_filters_duration_option_short": "짧음 (4분 미만)",
"search_filters_duration_option_long": "김 (20분 초과)",
"footer_documentation": "문서",
"footer_source_code": "소스 코드",
"footer_original_source_code": "원본 소스 코드",
"footer_modfied_source_code": "수정된 소스 코드",
"adminprefs_modified_source_code_url_label": "수정된 소스 코드 저장소의 URL",
"search_filters_title": "필터"
"search_filters_title": "필터",
"preferences_quality_dash_option_4320p": "4320p",
"Popular enabled: ": "인기 활성화: ",
"Dutch (auto-generated)": "네덜란드어 (자동 생성됨)",
"Chinese (Hong Kong)": "중국어 (홍콩)",
"Chinese (Taiwan)": "중국어 (대만)",
"German (auto-generated)": "독일어 (자동 생성됨)",
"Interlingue": "Interlingue",
"search_filters_date_label": "업로드 날짜",
"search_filters_date_option_none": "모든 날짜",
"search_filters_duration_option_none": "모든 기간",
"search_filters_features_option_three_sixty": "360°",
"search_filters_features_option_purchased": "구입한 항목",
"search_filters_apply_button": "선택한 필터 적용하기",
"preferences_quality_dash_option_240p": "240p",
"preferences_region_label": "국가: ",
"preferences_quality_dash_option_1440p": "1440p",
"French (auto-generated)": "프랑스어 (자동 생성됨)",
"Indonesian (auto-generated)": "인도네시아어 (자동 생성됨)",
"Turkish (auto-generated)": "터키어 (자동 생성됨)",
"Vietnamese (auto-generated)": "베트남어 (자동 생성됨)",
"preferences_quality_dash_option_2160p": "2160p",
"Italian (auto-generated)": "이탈리아어 (자동 생성됨)",
"preferences_quality_option_medium": "보통",
"preferences_quality_dash_option_720p": "720p",
"search_filters_duration_option_medium": "중간 (4 - 20분)",
"preferences_quality_dash_option_best": "최고",
"Portuguese (auto-generated)": "포르투갈어 (자동 생성됨)",
"Spanish (Spain)": "스페인어 (스페인)",
"preferences_quality_dash_label": "선호하는 DASH 비디오 품질: ",
"preferences_quality_option_hd720": "HD720",
"Spanish (auto-generated)": "스페인어 (자동 생성됨)",
"preferences_quality_dash_option_1080p": "1080p",
"preferences_quality_dash_option_worst": "최저",
"preferences_watch_history_label": "시청 기록 저장: ",
"invidious": "인비디어스",
"preferences_quality_option_small": "낮음",
"preferences_quality_dash_option_auto": "자동",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_dash_option_144p": "144p",
"English (United Kingdom)": "영어 (영국)",
"search_filters_features_option_vr180": "VR180",
"Cantonese (Hong Kong)": "광동어 (홍콩)",
"Portuguese (Brazil)": "포르투갈어 (브라질)",
"search_message_no_results": "결과가 없습니다.",
"search_message_change_filters_or_query": "필터를 변경하시거나 검색어를 넓게 시도해보세요.",
"search_message_use_another_instance": " 당신은 <a href=\"`x`\">다른 인스턴스에서 검색</a>할 수도 있습니다.",
"English (United States)": "영어 (미국)",
"Chinese": "중국어",
"Chinese (China)": "중국어 (중국)",
"Japanese (auto-generated)": "일본어 (자동 생성됨)",
"Korean (auto-generated)": "한국어 (자동 생성됨)",
"Russian (auto-generated)": "러시아어 (자동 생성됨)",
"Spanish (Mexico)": "스페인어 (멕시코)",
"search_filters_type_option_all": "모든 유형",
"footer_donate_page": "기부하기",
"preferences_quality_option_dash": "DASH (다양한 화질)",
"preferences_quality_dash_option_360p": "360p",
"preferences_save_player_pos_label": "이어서 보기: ",
"none": "없음",
"videoinfo_started_streaming_x_ago": "`x` 전에 스트리밍을 시작했습니다",
"crash_page_you_found_a_bug": "인비디어스에서 버그를 찾은 것 같습니다!",
"download_subtitles": "자막 - `x`(.vtt)",
"user_saved_playlists": "`x`개의 저장된 재생목록",
"crash_page_before_reporting": "버그를 보고하기 전에 다음 사항이 있는지 확인합니다:",
"crash_page_search_issue": "<a href=\"`x`\">깃허브에서 기존 이슈</a>를 검색했습니다",
"Video unavailable": "비디오를 사용할 수 없음",
"crash_page_refresh": "<a href=\"`x`\">페이지를 새로고침</a>하려고 했습니다",
"videoinfo_watch_on_youTube": "유튜브에서 보기",
"crash_page_switch_instance": "<a href=\"`x`\">다른 인스턴스를 사용</a>하려고 했습니다",
"crash_page_read_the_faq": "<a href=\"`x`\">자주 묻는 질문(FAQ)</a> 읽기",
"user_created_playlists": "`x`개의 생성된 재생목록",
"crash_page_report_issue": "위의 방법 중 어느 것도 도움이 되지 않았다면, <a href=\"`x`\">깃허브에서 새 이슈를 열고</a>(가능하면 영어로) 메시지에 다음 텍스트를 포함하세요(해당 텍스트를 번역하지 마십시오):",
"videoinfo_youTube_embed_link": "임베드",
"videoinfo_invidious_embed_link": "임베드 링크",
"error_video_not_in_playlist": "요청한 동영상이 이 재생목록에 없습니다. <a href=\"`x`\">재생목록 목록을 보려면 여기를 클릭하십시오.</a>",
"channel_tab_shorts_label": "쇼츠",
"channel_tab_streams_label": "실시간 스트리밍",
"channel_tab_channels_label": "채널",
"channel_tab_playlists_label": "재생목록",
"Standard YouTube license": "표준 유튜브 라이선스",
"Song: ": "제목: ",
"Channel Sponsor": "채널 스폰서",
"Album: ": "앨범: ",
"Music in this video": "동영상 속 음악",
"Artist: ": "아티스트: ",
"Download is disabled": "다운로드가 비활성화 되어있음",
"Import YouTube playlist (.csv)": "유튜브 플레이리스트 가져오기 (.csv)"
}

@ -14,22 +14,21 @@
"Clear watch history?": "Išvalyti žiūrėjimo istoriją?",
"New password": "Naujas slaptažodis",
"New passwords must match": "Naujas slaptažodis turi sutapti",
"Cannot change password for Google accounts": "Negalima pakeisti Google paskyros slaptažodžio",
"Authorize token?": "Autorizuoti žetoną?",
"Authorize token for `x`?": "Autorizuoti žetoną `x`?",
"Yes": "Taip",
"No": "Ne",
"Import and Export Data": "Importuoti ir eksportuoti duomenis",
"Import": "Importuoti",
"Import Invidious data": "Importuoti Invidious duomenis",
"Import YouTube subscriptions": "Importuoti YouTube prenumeratas",
"Import Invidious data": "Importuoti Invidious JSON duomenis",
"Import YouTube subscriptions": "Importuoti YouTube/OPML prenumeratas",
"Import FreeTube subscriptions (.db)": "Importuoti FreeTube prenumeratas (.db)",
"Import NewPipe subscriptions (.json)": "Importuoti NewPipe prenumeratas (.json)",
"Import NewPipe data (.zip)": "Importuoti NewPipe duomenis (.zip)",
"Export": "Eksportuoti",
"Export subscriptions as OPML": "Eksportuoti prenumeratas kaip OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Eksportuoti prenumeratas kaip OPML (skirta NewPipe & FreeTube)",
"Export data as JSON": "Eksportuoti duomenis kaip JSON",
"Export data as JSON": "Eksportuoti Invidious duomenis kaip JSON",
"Delete account?": "Ištrinti paskyrą?",
"History": "Istorija",
"An alternative front-end to YouTube": "Alternatyvus YouTube žiūrėjimo būdas",
@ -37,7 +36,6 @@
"source": "šaltinis",
"Log in": "Prisijungti",
"Log in/register": "Prisijungti/ registruotis",
"Log in with Google": "Prisijungti naudojantis Google",
"User ID": "Naudotojo ID",
"Password": "Slaptažodis",
"Time (h:mm:ss):": "Laikas (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Prisijungti",
"Register": "Registruotis",
"E-mail": "El. paštas",
"Google verification code": "Google patvirtinimo kodas",
"Preferences": "Pasirinktys",
"preferences_category_player": "Grotuvo pasirinktys",
"preferences_video_loop_label": "Visada kartoti: ",
@ -66,7 +63,7 @@
"preferences_related_videos_label": "Rodyti susijusius vaizdo įrašus: ",
"preferences_annotations_label": "Rodyti anotacijas pagal nutylėjimą: ",
"preferences_extend_desc_label": "Automatiškai išplėsti vaizdo įrašo aprašymą: ",
"preferences_vr_mode_label": "Interaktyvūs 360 laipsnių vaizdo įrašai: ",
"preferences_vr_mode_label": "Interaktyvūs 360 laipsnių vaizdo įrašai (reikalingas WebGL): ",
"preferences_category_visual": "Vizualinės nuostatos",
"preferences_player_style_label": "Vaizdo grotuvo stilius: ",
"Dark mode: ": "Tamsus rėžimas: ",
@ -153,7 +150,7 @@
"Shared `x`": "Pasidalino `x`",
"Premieres in `x`": "Premjera už `x`",
"Premieres `x`": "Premjera`x`",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Sveiki! Atrodo, kad turite išjungę \"JavaScript\". Spauskite čia norėdami peržiūrėti komentarus, turėkite omenyje, kad jų įkėlimas gali užtrukti.",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Sveiki! Panašu, kad turite išjungę „JavaScript“. Spustelėkite čia norėdami peržiūrėti komentarus, atminkite, kad jų įkėlimas gali užtrukti šiek tiek ilgiau.",
"View YouTube comments": "Žiūrėti YouTube komentarus",
"View more comments on Reddit": "Žiūrėti daugiau komentarų Reddit",
"View `x` comments": {
@ -164,17 +161,12 @@
"Hide replies": "Slėpti atsakymus",
"Show replies": "Rodyti atsakymus",
"Incorrect password": "Slaptažodis neteisingas",
"Quota exceeded, try again in a few hours": "Viršyta kvota, bandykite dar kartą po keleto valandų",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Nepavyko prisijungti, įsitikinkite, kad yra įjungta dviejų etapų autentifikacija (Autentifikatorius arba SMS).",
"Invalid TFA code": "Neteisingas TFA kodas",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Prisijungimas nepavyko. Tai gali būti todėl, kad jūsų paskyroje nėra įjungta dviejų etapų autentifikacija.",
"Wrong answer": "Atsakymas neteisingas",
"Erroneous CAPTCHA": "Klaidinga CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA yra reikalinga šiam laukeliui",
"User ID is a required field": "Vartotojo ID yra reikalingas šiam laukeliui",
"Password is a required field": "Slaptažodis yra reikalingas šiam laukeliui",
"Wrong username or password": "Neteisingas vartotojo vardas arba slaptažodis",
"Please sign in using 'Log in with Google'": "Prašome prisijungti naudojant \"Prisijungti su\" Google \"",
"Password cannot be empty": "Slaptažodžio laukelis negali būti tuščias",
"Password cannot be longer than 55 characters": "Slaptažodis negali būti ilgesnis nei 55 simboliai",
"Please log in": "Prašome prisijungti",
@ -325,9 +317,9 @@
"`x` marked it with a ❤": "`x` pažymėjo tai su ❤",
"Audio mode": "Garso rėžimas",
"Video mode": "Vaizdo rėžimas",
"Videos": "Vaizdo įrašai",
"channel_tab_videos_label": "Vaizdo įrašai",
"Playlists": "Grojaraiščiai",
"Community": "Bendruomenė",
"channel_tab_community_label": "Bendruomenė",
"search_filters_sort_option_relevance": "Aktualumas",
"search_filters_sort_option_rating": "Reitingas",
"search_filters_sort_option_date": "Įkėlimo data",
@ -371,5 +363,123 @@
"preferences_quality_dash_option_best": "Geriausia",
"preferences_quality_dash_option_worst": "Blogiausia",
"preferences_quality_dash_option_auto": "Automatinis",
"search_filters_title": "Filtras"
"search_filters_title": "Filtras",
"generic_videos_count_0": "{{count}} vaizdo įrašas",
"generic_videos_count_1": "{{count}} vaizdo įrašai",
"generic_videos_count_2": "{{count}} vaizdo įrašų",
"generic_subscribers_count_0": "{{count}} prenumeratorius",
"generic_subscribers_count_1": "{{count}} prenumeratoriai",
"generic_subscribers_count_2": "{{count}} prenumeratorių",
"generic_subscriptions_count_0": "{{count}} prenumerata",
"generic_subscriptions_count_1": "{{count}} prenumeratos",
"generic_subscriptions_count_2": "{{count}} prenumeratų",
"preferences_watch_history_label": "Įgalinti žiūrėjimo istoriją: ",
"preferences_quality_dash_option_1080p": "1080p",
"invidious": "Invidious",
"preferences_quality_dash_option_720p": "720p",
"generic_playlists_count_0": "{{count}} grojaraštis",
"generic_playlists_count_1": "{{count}} grojaraščiai",
"generic_playlists_count_2": "{{count}} grojaraščių",
"preferences_quality_option_medium": "Vidutinė",
"preferences_quality_option_small": "Maža",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_2160p": "2160p",
"preferences_quality_dash_option_144p": "144p",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_dash_option_360p": "360p",
"preferences_quality_option_dash": "DASH (prisitaikanti kokybė)",
"generic_views_count_0": "{{count}} peržiūra",
"generic_views_count_1": "{{count}} peržiūros",
"generic_views_count_2": "{{count}} peržiūrų",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_dash_option_240p": "240p",
"none": "nėra",
"search_filters_type_option_all": "Bet koks tipas",
"videoinfo_started_streaming_x_ago": "Pradėjo transliuoti prieš `x`",
"crash_page_switch_instance": "pabandėte <a href=\"`x`\">naudoti kitą perdavimo šaltinį</a>",
"search_filters_duration_option_none": "Bet kokia trukmė",
"search_filters_duration_option_medium": "Vidutinio ilgumo (4 - 20 minučių)",
"search_filters_features_option_vr180": "VR180",
"crash_page_before_reporting": "Prieš pranešdami apie klaidą įsitikinkite, kad:",
"crash_page_read_the_faq": "perskaitėte <a href=\"`x`\">Dažniausiai užduodamus klausimus (DUK)</a>",
"crash_page_search_issue": "ieškojote <a href=\"`x`\"> esamų problemų GitHub</a>",
"error_video_not_in_playlist": "Prašomo vaizdo įrašo šiame grojaraštyje nėra. <a href=\"`x`\">Spustelėkite čia, kad pamatytumėte grojaraščio pagrindinį puslapį.</a>",
"crash_page_report_issue": "Jei nė vienas iš pirmiau pateiktų būdų nepadėjo, prašome <a href=\"`x`\">atidaryti naują problemą GitHub</a> (pageidautina anglų kalba) ir į savo pranešimą įtraukti šį tekstą (NEVERSKITE šio teksto):",
"subscriptions_unseen_notifs_count_0": "{{count}} nematytas pranešimas",
"subscriptions_unseen_notifs_count_1": "{{count}} nematyti pranešimai",
"subscriptions_unseen_notifs_count_2": "{{count}} nematytų pranešimų",
"Vietnamese (auto-generated)": "Vietnamiečių kalba (automatiškai sugeneruota)",
"Dutch (auto-generated)": "Olandų kalba (automatiškai sugeneruota)",
"generic_count_weeks_0": "{{count}} savaitę",
"generic_count_weeks_1": "{{count}} savaitės",
"generic_count_weeks_2": "{{count}} savaičių",
"Interlingue": "Interlingue",
"Italian (auto-generated)": "Italų kalba (automatiškai sugeneruota)",
"Japanese (auto-generated)": "Japonų kalba (automatiškai sugeneruota)",
"Korean (auto-generated)": "Korėjiečių kalba (automatiškai sugeneruota)",
"generic_count_months_0": "{{count}} mėnesį",
"generic_count_months_1": "{{count}} mėnesius",
"generic_count_months_2": "{{count}} mėnesių",
"generic_count_days_0": "{{count}} dieną",
"generic_count_days_1": "{{count}} dienas",
"generic_count_days_2": "{{count}} dienų",
"generic_count_hours_0": "{{count}} valandą",
"generic_count_hours_1": "{{count}} valandas",
"generic_count_hours_2": "{{count}} valandų",
"generic_count_seconds_0": "{{count}} sekundę",
"generic_count_seconds_1": "{{count}} sekundes",
"generic_count_seconds_2": "{{count}} sekundžių",
"generic_count_minutes_0": "{{count}} minutę",
"generic_count_minutes_1": "{{count}} minutes",
"generic_count_minutes_2": "{{count}} minučių",
"generic_count_years_0": "{{count}} metus",
"generic_count_years_1": "{{count}} metus",
"generic_count_years_2": "{{count}} metų",
"Popular enabled: ": "Populiarūs įgalinti: ",
"Portuguese (auto-generated)": "Portugalų kalba (automatiškai sugeneruota)",
"videoinfo_watch_on_youTube": "Žiaurėti Youtube",
"Chinese (China)": "Kinų kalba (Kinija)",
"crash_page_you_found_a_bug": "Atrodo, kad radote \"Invidious\" klaidą!",
"search_filters_features_option_three_sixty": "360°",
"English (United Kingdom)": "Anglų kalba (Jungtinė Karalystė)",
"Chinese (Hong Kong)": "Kinų kalba (Honkongas)",
"search_message_change_filters_or_query": "Pabandykite išplėsti paieškos užklausą ir (arba) pakeisti filtrus.",
"English (United States)": "Anglų kalba (Jungtinės Amerikos Valstijos)",
"Chinese (Taiwan)": "Kinų kalba (Taivanas)",
"search_message_use_another_instance": " Taip pat galite <a href=\"`x`\">ieškoti kitame perdavimo šaltinyje</a>.",
"tokens_count_0": "{{count}} žetonas",
"tokens_count_1": "{{count}} žetonai",
"tokens_count_2": "{{count}} žetonų",
"search_message_no_results": "Rezultatų nerasta.",
"comments_view_x_replies_0": "Žiūrėti {{count}} atsakymą",
"comments_view_x_replies_1": "Žiūrėti {{count}} atsakymus",
"comments_view_x_replies_2": "Žiūrėti {{count}} atsakymų",
"comments_points_count_0": "{{count}} taškas",
"comments_points_count_1": "{{count}} taškai",
"comments_points_count_2": "{{count}} taškų",
"Cantonese (Hong Kong)": "Kantono kalba (Honkongas)",
"Chinese": "Kinų",
"French (auto-generated)": "Prancūzų kalba (automatiškai sugeneruota)",
"German (auto-generated)": "Vokiečių kalba (automatiškai sugeneruota)",
"Indonesian (auto-generated)": "Indoneziečių kalba (automatiškai sugeneruota)",
"Portuguese (Brazil)": "Portugalų kalba (Brazilija)",
"Russian (auto-generated)": "Rusų kalba (automatiškai sugeneruota)",
"Spanish (Mexico)": "Ispanų kalba (Meksika)",
"Spanish (auto-generated)": "Ispanų kalba (automatiškai sugeneruota)",
"Spanish (Spain)": "Ispanų kalba (Ispanija)",
"Turkish (auto-generated)": "Turkų kalba (automatiškai sugeneruota)",
"search_filters_date_label": "Įkėlimo data",
"search_filters_date_option_none": "Bet kokia data",
"search_filters_features_option_purchased": "Įsigyta",
"search_filters_apply_button": "Taikyti pasirinktus filtrus",
"download_subtitles": "Subtitrai - `x` (.vtt)",
"user_created_playlists": "`x` sukurti grojaraščiai",
"user_saved_playlists": "`x` išsaugoti grojaraščiai",
"Video unavailable": "Vaizdo įrašas nepasiekiamas",
"preferences_save_player_pos_label": "Išsaugoti atkūrimo padėtį: ",
"videoinfo_youTube_embed_link": "Įterpti",
"videoinfo_invidious_embed_link": "Įterpti nuorodą",
"crash_page_refresh": "pabandėte <a href=\"`x`\">atnaujinti puslapį</a>",
"Album: ": "Albumas "
}

@ -14,7 +14,6 @@
"Clear watch history?": "Tøm visningshistorikk?",
"New password": "Nytt passord",
"New passwords must match": "Nye passordfelter må stemme overens",
"Cannot change password for Google accounts": "Kan ikke endre passord for Google-kontoer",
"Authorize token?": "Identitetsbekreft symbol?",
"Authorize token for `x`?": "Identitetsbekreft symbol for `x`?",
"Yes": "Ja",
@ -37,7 +36,6 @@
"source": "kilde",
"Log in": "Logg inn",
"Log in/register": "Logg inn/registrer",
"Log in with Google": "Logg inn med Google",
"User ID": "Bruker-ID",
"Password": "Passord",
"Time (h:mm:ss):": "Tid (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Innlogging",
"Register": "Registrer",
"E-mail": "E-post",
"Google verification code": "Google-bekreftelseskode",
"Preferences": "Innstillinger",
"preferences_category_player": "Avspillerinnstillinger",
"preferences_video_loop_label": "Alltid gjenta: ",
@ -164,17 +161,12 @@
"Hide replies": "Skjul svar",
"Show replies": "Vis svar",
"Incorrect password": "Feil passord",
"Quota exceeded, try again in a few hours": "Kvote overskredet, prøv igjen om et par timer",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Kunne ikke logge inn, forsikre deg om at tofaktor-identitetsbekreftelse (Authenticator eller SMS) er skrudd på.",
"Invalid TFA code": "Ugyldig tofaktorkode",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Innlogging mislyktes. Dette kan være fordi tofaktor-identitetsbekreftelse er skrudd av på kontoen din.",
"Wrong answer": "Ugyldig svar",
"Erroneous CAPTCHA": "Ugyldig CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA er et påkrevd felt",
"User ID is a required field": "Bruker-ID er et påkrevd felt",
"Password is a required field": "Passord er et påkrevd felt",
"Wrong username or password": "Ugyldig brukernavn eller passord",
"Please sign in using 'Log in with Google'": "Logg inn ved bruk av \"Google-innlogging\"",
"Password cannot be empty": "Passordet kan ikke være tomt",
"Password cannot be longer than 55 characters": "Passordet kan ikke være lengre enn 55 tegn",
"Please log in": "Logg inn",
@ -325,9 +317,9 @@
"`x` marked it with a ❤": "`x` levnet et ❤",
"Audio mode": "Lydmodus",
"Video mode": "Video-modus",
"Videos": "Videoer",
"channel_tab_videos_label": "Videoer",
"Playlists": "Spillelister",
"Community": "Gemenskap",
"channel_tab_community_label": "Gemenskap",
"search_filters_sort_option_relevance": "relevans",
"search_filters_sort_option_rating": "vurdering",
"search_filters_sort_option_date": "dato",
@ -460,5 +452,17 @@
"Russian (auto-generated)": "Russisk (laget automatisk)",
"Dutch (auto-generated)": "Nederlandsk (laget automatisk)",
"Turkish (auto-generated)": "Tyrkisk (laget automatisk)",
"search_filters_title": "Filtrer"
"search_filters_title": "Filtrer",
"Popular enabled: ": "Populære aktiv: ",
"search_message_change_filters_or_query": "Prøv ett mindre snevert søk og/eller endre filterne.",
"search_filters_duration_option_medium": "Middels (420 minutter)",
"search_message_no_results": "Resultatløst.",
"search_filters_type_option_all": "Alle typer",
"search_filters_duration_option_none": "Enhver varighet",
"search_message_use_another_instance": " Du kan også <a href=\"`x`\">søke på en annen instans</a>.",
"search_filters_date_label": "Opplastningsdato",
"search_filters_apply_button": "Bruk valgte filtre",
"search_filters_date_option_none": "Siden begynnelsen",
"search_filters_features_option_vr180": "VR180",
"error_video_not_in_playlist": "Forespurt video finnes ikke i denne spillelisten. <a href=\"`x`\">Trykk her for spillelistens hjemmeside.</a>"
}

@ -14,7 +14,6 @@
"Clear watch history?": "Wil je de kijkgeschiedenis wissen?",
"New password": "Nieuw wachtwoord",
"New passwords must match": "De nieuwe wachtwoorden moeten overeenkomen",
"Cannot change password for Google accounts": "Kan het wachtwoord van Google-accounts niet wijzigen",
"Authorize token?": "Wil je de toegangssleutel machtigen?",
"Authorize token for `x`?": "Wil je de toegangssleutel machtigen voor `x`?",
"Yes": "Ja",
@ -37,7 +36,6 @@
"source": "bron",
"Log in": "Inloggen",
"Log in/register": "Inloggen/Registreren",
"Log in with Google": "Inloggen met Google",
"User ID": "Gebruikers-id",
"Password": "Wachtwoord",
"Time (h:mm:ss):": "Tijd (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Inloggen",
"Register": "Registreren",
"E-mail": "E-mailadres",
"Google verification code": "Google-verificatiecode",
"Preferences": "Instellingen",
"preferences_category_player": "Spelerinstellingen",
"preferences_video_loop_label": "Altijd herhalen: ",
@ -159,17 +156,12 @@
"Hide replies": "Antwoorden verbergen",
"Show replies": "Antwoorden tonen",
"Incorrect password": "Wachtwoord is onjuist",
"Quota exceeded, try again in a few hours": "Quota overschreden; probeer het over een paar uur opnieuw",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Kan niet inloggen. Zorg ervoor dat authenticatie in twee stappen (Authenticator of sms) is ingeschakeld.",
"Invalid TFA code": "Onjuiste TFA-code",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Inloggen mislukt. Wellicht is authenticatie in twee stappen niet ingeschakeld op je account.",
"Wrong answer": "Onjuist antwoord",
"Erroneous CAPTCHA": "Onjuiste CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA is vereist",
"User ID is a required field": "Gebruikers-id is vereist",
"Password is a required field": "Wachtwoord is vereist",
"Wrong username or password": "Onjuiste gebruikersnaam of wachtwoord",
"Please sign in using 'Log in with Google'": "Log in via 'Inloggen met Google'",
"Password cannot be empty": "Het wachtwoordveld mag niet leeg zijn",
"Password cannot be longer than 55 characters": "Het wachtwoord mag niet langer dan 55 tekens zijn",
"Please log in": "Log in",
@ -320,9 +312,9 @@
"`x` marked it with a ❤": "`x` heeft dit gemarkeerd met ❤",
"Audio mode": "Audiomodus",
"Video mode": "Videomodus",
"Videos": "Video's",
"channel_tab_videos_label": "Video's",
"Playlists": "Afspeellijsten",
"Community": "Gemeenschap",
"channel_tab_community_label": "Gemeenschap",
"search_filters_sort_option_relevance": "relevantie",
"search_filters_sort_option_rating": "beoordeling",
"search_filters_sort_option_date": "datum",

@ -14,7 +14,6 @@
"Clear watch history?": "Wyczyścić historię?",
"New password": "Nowe hasło",
"New passwords must match": "Nowe hasła muszą być identyczne",
"Cannot change password for Google accounts": "Nie można zmienić hasła do konta Google",
"Authorize token?": "Autoryzować token?",
"Authorize token for `x`?": "Autoryzować token dla `x`?",
"Yes": "Tak",
@ -37,7 +36,6 @@
"source": "źródło",
"Log in": "Zaloguj",
"Log in/register": "Zaloguj/Zarejestruj",
"Log in with Google": "Zaloguj do Google",
"User ID": "ID użytkownika",
"Password": "Hasło",
"Time (h:mm:ss):": "Godzina (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Zaloguj się",
"Register": "Zarejestruj się",
"E-mail": "E-mail",
"Google verification code": "Kod weryfikacyjny Google",
"Preferences": "Preferencje",
"preferences_category_player": "Ustawienia odtwarzacza",
"preferences_video_loop_label": "Zawsze zapętlaj: ",
@ -54,7 +51,7 @@
"preferences_continue_label": "Domyślnie odtwarzaj następny: ",
"preferences_continue_autoplay_label": "Odtwórz następny film: ",
"preferences_listen_label": "Tryb dźwiękowy: ",
"preferences_local_label": "Filmy przez proxy? ",
"preferences_local_label": "Wideo przez proxy? ",
"preferences_speed_label": "Domyślna prędkość: ",
"preferences_quality_label": "Preferowana jakość filmów: ",
"preferences_volume_label": "Głośność odtwarzacza: ",
@ -67,7 +64,7 @@
"preferences_annotations_label": "Domyślnie pokazuj adnotacje: ",
"preferences_extend_desc_label": "Automatycznie rozwijaj opisy filmów: ",
"preferences_vr_mode_label": "Interaktywne filmy 360 stopni (wymaga WebGL): ",
"preferences_category_visual": "Preferencje Wizualne",
"preferences_category_visual": "Preferencje wizualne",
"preferences_player_style_label": "Styl odtwarzacza: ",
"Dark mode: ": "Ciemny motyw: ",
"preferences_dark_mode_label": "Motyw: ",
@ -112,7 +109,7 @@
"Registration enabled: ": "Rejestracja włączona? ",
"Report statistics: ": "Raportować statystyki? ",
"Save preferences": "Zapisz preferencje",
"Subscription manager": "Manager subskrybcji",
"Subscription manager": "Menedżer subskrypcji",
"Token manager": "Menedżer tokenów",
"Token": "Token",
"Import/export": "Import/Eksport",
@ -163,17 +160,12 @@
"Hide replies": "Ukryj odpowiedzi",
"Show replies": "Pokaż odpowiedzi",
"Incorrect password": "Niepoprawne hasło",
"Quota exceeded, try again in a few hours": "Przekroczony limit zapytań, spróbuj ponownie za kilka godzin",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Nie udało się zalogować, upewnij się, że dwuetapowe uwierzytelnianie (Autentykator lub SMS) jest aktywne.",
"Invalid TFA code": "Niepoprawny kod TFA",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Nie udało się zalogować. To może być spowodowane wyłączoną dwustopniową autoryzacją na twoim koncie.",
"Wrong answer": "Niepoprawna odpowiedź",
"Erroneous CAPTCHA": "CAPTCHA wykonane błędnie",
"CAPTCHA is a required field": "CAPTCHA jest polem wymaganym",
"User ID is a required field": "ID użytkownika jest polem wymaganym",
"Password is a required field": "Hasło jest polem wymaganym",
"Wrong username or password": "Niepoprawny login lub hasło",
"Please sign in using 'Log in with Google'": "Zaloguj się używając \"Zaloguj się przez Google\"",
"Password cannot be empty": "Hasło nie może być puste",
"Password cannot be longer than 55 characters": "Hasło nie może być dłuższe niż 55 znaków",
"Please log in": "Proszę się zalogować",
@ -283,7 +275,7 @@
"Somali": "somalijski",
"Southern Sotho": "sotho południowy",
"Spanish": "hiszpański",
"Spanish (Latin America)": "hiszpański (ameryka łacińska)",
"Spanish (Latin America)": "hiszpański (Ameryka Łacińska)",
"Sundanese": "sundajski",
"Swahili": "suahili",
"Swedish": "szwedzki",
@ -317,6 +309,7 @@
"Movies": "Filmy",
"Download": "Pobierz",
"Download as: ": "Pobierz jako: ",
"Download is disabled": "Pobieranie jest wyłączone",
"%A %B %-d, %Y": "%A, %-d %B %Y",
"(edited)": "(edytowany)",
"YouTube comment permalink": "Odnośnik bezpośredni do komentarza na YouTube",
@ -324,37 +317,37 @@
"`x` marked it with a ❤": "`x` oznaczonych ❤",
"Audio mode": "Tryb audio",
"Video mode": "Tryb wideo",
"Videos": "Filmy",
"channel_tab_videos_label": "Wideo",
"Playlists": "Playlisty",
"Community": "Społeczność",
"channel_tab_community_label": "Społeczność",
"search_filters_sort_option_relevance": "Trafność",
"search_filters_sort_option_rating": "Ocena",
"search_filters_sort_option_date": "data",
"search_filters_sort_option_date": "Data przesłania",
"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_sort_label": "Sortuj wg",
"search_filters_date_option_hour": "Ostatnia godzina",
"search_filters_date_option_today": "Dzisiaj",
"search_filters_date_option_week": "W tym tygodniu",
"search_filters_date_option_month": "W tym miesiącu",
"search_filters_date_option_year": "W tym roku",
"search_filters_type_option_video": "Wideo",
"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/CC",
"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_four_k": "4K",
"search_filters_features_option_location": "Lokalizacja",
"search_filters_features_option_hdr": "hdr",
"search_filters_features_option_hdr": "HDR",
"Current version: ": "Aktualna wersja: ",
"next_steps_error_message": "Po czym powinien*ś spróbować: ",
"next_steps_error_message": "Po czym należy spróbować: ",
"next_steps_error_message_refresh": "Odśwież",
"next_steps_error_message_go_to_youtube": "Przejdź do YouTube",
"invidious": "Invidious",
@ -397,11 +390,11 @@
"generic_count_seconds_0": "{{count}} sekunda",
"generic_count_seconds_1": "{{count}} sekundy",
"generic_count_seconds_2": "{{count}} sekund",
"crash_page_you_found_a_bug": "Wygląda na to że udało ci się znaleźć błąd w Invidious!",
"crash_page_you_found_a_bug": "Wygląda na to, że udało ci się znaleźć błąd w Invidious!",
"crash_page_refresh": "próbowano <a href=\"`x`\">odświeżyć stronę</a>",
"crash_page_switch_instance": "spróbowano <a href=\"`x`\"> użyć innej instancji</a>",
"crash_page_read_the_faq": "przeczytaj <a href=\"`x`\"> Często Zadawane Pytania (FAQ)</a>",
"crash_page_search_issue": "próbowano poszukać <a href=\"`x`\"> istniejących zgłoszeń na GitHub'ie</a>",
"crash_page_switch_instance": "próbowano <a href=\"`x`\">użyć innej instancji</a>",
"crash_page_read_the_faq": "przeczytaj <a href=\"`x`\">Najczęściej zadawane pytania (FAQ)</a>",
"crash_page_search_issue": "próbowano poszukać <a href=\"`x`\">istniejących zgłoszeń na GitHubie</a>",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_144p": "144p",
@ -418,12 +411,12 @@
"generic_count_years_0": "{{count}} rok",
"generic_count_years_1": "{{count}} lata",
"generic_count_years_2": "{{count}} lat",
"crash_page_before_reporting": "Przed zgłoszeniem błędu, upewnij się że masz:",
"crash_page_report_issue": "Jeżeli nic z powyższych opcji nie pomogło, proszę <a href=\"`x`\"> otworzyć nowe zgłoszenie na GitHub'ie</a> (najlepiej po Angielsku) i dodać poniższy tekst w twojej wiadomości (NIE tłumacz tego tekstu):",
"crash_page_before_reporting": "Przed zgłoszeniem błędu, upewnij się, że masz:",
"crash_page_report_issue": "Jeżeli nic z powyższych opcji nie pomogło, proszę <a href=\"`x`\">otworzyć nowe zgłoszenie na GitHubie</a> (najlepiej po angielsku) i dodać poniższy tekst w twojej wiadomości (NIE tłumacz tego tekstu):",
"preferences_quality_dash_option_auto": "Automatyczna",
"preferences_quality_dash_option_best": "Najlepsza",
"preferences_quality_dash_option_worst": "Najgorsza",
"preferences_quality_option_dash": "DASH (jakość adaptywna)",
"preferences_quality_option_dash": "DASH (jakość adaptacyjna)",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Średnia",
"preferences_quality_option_small": "Mała",
@ -443,21 +436,21 @@
"user_saved_playlists": "`x` zapisanych playlist",
"Video unavailable": "Film niedostępny",
"preferences_save_player_pos_label": "Zapisz pozycję odtwarzania: ",
"preferences_region_label": "Region zawartości: ",
"preferences_region_label": "Kraj treści: ",
"Released under the AGPLv3 on Github.": "Wydany na licencji AGPLv3 na GitHub.",
"search_filters_duration_option_short": "Krótkie (< 4 minutes)",
"search_filters_duration_option_long": "Długie (> 20 minutes)",
"search_filters_duration_option_short": "Krótka (< 4 minut)",
"search_filters_duration_option_long": "Długa (> 20 minut)",
"footer_documentation": "Dokumentacja",
"footer_source_code": "Kod źródłowy",
"footer_modfied_source_code": "Zmodyfikowany Kod źródłowy",
"footer_modfied_source_code": "Zmodyfikowany kod źródłowy",
"footer_original_source_code": "Oryginalny kod źródłowy",
"adminprefs_modified_source_code_url_label": "Adres URL do repozytorium z zmodyfikowanym kodem źródłowym",
"adminprefs_modified_source_code_url_label": "Adres URL do repozytorium ze zmodyfikowanym kodem źródłowym",
"English (United Kingdom)": "angielski (Wielka Brytania)",
"English (United States)": "angielski (Stany Zjednoczone)",
"Cantonese (Hong Kong)": "kantoński (Hong Kong)",
"Cantonese (Hong Kong)": "kantoński (Hongkong)",
"Chinese": "chiński",
"Chinese (China)": "chiński (Chiny)",
"Chinese (Hong Kong)": "chiński (Hong Kong)",
"Chinese (Hong Kong)": "chiński (Hongkong)",
"Chinese (Taiwan)": "chiński (Tajwan)",
"Dutch (auto-generated)": "niderlandzki (wygenerowany automatycznie)",
"French (auto-generated)": "francuski (wygenerowany automatycznie)",
@ -475,5 +468,29 @@
"Russian (auto-generated)": "rosyjski (wygenerowany automatycznie)",
"Portuguese (auto-generated)": "portugalski (wygenerowany automatycznie)",
"Portuguese (Brazil)": "portugalski (Brazylia)",
"search_filters_title": "Filtr"
"search_filters_title": "Filtr",
"error_video_not_in_playlist": "Żądany film nie istnieje na tej playliście. <a href=\"`x`\">Kliknij tutaj, aby przejść do strony głównej playlisty.</a>",
"Popular enabled: ": "Popularne włączone: ",
"search_message_no_results": "Nie znaleziono wyników.",
"preferences_watch_history_label": "Włącz historię oglądania: ",
"search_filters_apply_button": "Zastosuj wybrane filtry",
"search_message_change_filters_or_query": "Spróbuj poszerzyć zapytanie wyszukiwania i/lub zmienić filtry.",
"search_filters_date_label": "Data przesłania",
"search_filters_features_option_vr180": "VR180",
"search_filters_date_option_none": "Dowolna data",
"search_message_use_another_instance": " Możesz także <a href=\"`x`\">wyszukać w innej instancji</a>.",
"search_filters_type_option_all": "Dowolny typ",
"search_filters_duration_option_none": "Dowolna długość",
"search_filters_duration_option_medium": "Średnia (4-20 minut)",
"channel_tab_streams_label": "Na żywo",
"channel_tab_channels_label": "Kanały",
"channel_tab_playlists_label": "Playlisty",
"channel_tab_shorts_label": "Shorts",
"Music in this video": "Muzyka w tym filmie",
"Artist: ": "Wykonawca: ",
"Album: ": "Album: ",
"Song: ": "Piosenka: ",
"Channel Sponsor": "Sponsor kanału",
"Standard YouTube license": "Standardowa licencja YouTube",
"Import YouTube playlist (.csv)": "Importuj playlistę YouTube (.csv)"
}

@ -14,7 +14,6 @@
"Clear watch history?": "Limpar histórico de reprodução?",
"New password": "Nova senha",
"New passwords must match": "Nova senha deve ser igual",
"Cannot change password for Google accounts": "Não é possível alterar sua senha de contas do Google",
"Authorize token?": "Autorizar o token?",
"Authorize token for `x`?": "Autorizar o token para `x`?",
"Yes": "Sim",
@ -37,7 +36,6 @@
"source": "código-fonte",
"Log in": "Entrar",
"Log in/register": "Entrar/Registrar",
"Log in with Google": "Entrar com conta Google",
"User ID": "Usuário",
"Password": "Senha",
"Time (h:mm:ss):": "Hora (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Entrar",
"Register": "Registrar",
"E-mail": "E-mail",
"Google verification code": "Código de verificação do Google",
"Preferences": "Preferências",
"preferences_category_player": "Preferências do reprodutor",
"preferences_video_loop_label": "Repetir sempre: ",
@ -166,17 +163,12 @@
"Hide replies": "Ocultar respostas",
"Show replies": "Mostrar respostas",
"Incorrect password": "Senha incorreta",
"Quota exceeded, try again in a few hours": "Cota excedida, tente novamente em algumas horas",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Não foi possível fazer login, sua autenticação em dois passos (app autenticador ou sms) deve estar ativada.",
"Invalid TFA code": "Código TFA inválido",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Falha no login. Isso pode acontecer porque a autenticação em dois passos está desativada para sua conta.",
"Wrong answer": "Resposta incorreta",
"Erroneous CAPTCHA": "CAPTCHA inválido",
"CAPTCHA is a required field": "O CAPTCHA é um campo obrigatório",
"User ID is a required field": "O nome de usuário é um campo obrigatório",
"Password is a required field": "A senha é um campo obrigatório",
"Wrong username or password": "Nome de usuário ou senha inválidos",
"Please sign in using 'Log in with Google'": "Por favor, entre usando 'Entrar com conta Google'",
"Password cannot be empty": "A senha não pode ficar em branco",
"Password cannot be longer than 55 characters": "A senha não pode ter mais que 55 caracteres",
"Please log in": "Por favor, inicie sua sessão",
@ -341,9 +333,9 @@
"`x` marked it with a ❤": "`x` foi marcado como ❤",
"Audio mode": "Modo de áudio",
"Video mode": "Modo de vídeo",
"Videos": "Vídeos",
"channel_tab_videos_label": "Vídeos",
"Playlists": "Listas de reprodução",
"Community": "Comunidade",
"channel_tab_community_label": "Comunidade",
"search_filters_sort_option_relevance": "relevância",
"search_filters_sort_option_rating": "avaliação",
"search_filters_sort_option_date": "data",
@ -381,7 +373,7 @@
"footer_documentation": "Documentação",
"footer_source_code": "Código fonte",
"footer_original_source_code": "Código fonte original",
"footer_modfied_source_code": "Código Fonte Modificado",
"footer_modfied_source_code": "Código-fonte modificado",
"preferences_quality_dash_label": "Qualidade de vídeo do painel preferida: ",
"preferences_region_label": "País do conteúdo: ",
"preferences_quality_dash_option_4320p": "4320p",
@ -470,5 +462,19 @@
"Spanish (Spain)": "Espanhol (Espanha)",
"Turkish (auto-generated)": "Turco (gerado automaticamente)",
"search_filters_duration_option_medium": "Médio (4 - 20 minutos)",
"search_filters_features_option_vr180": "VR180"
"search_filters_features_option_vr180": "VR180",
"Popular enabled: ": "Popular habilitado: ",
"error_video_not_in_playlist": "O vídeo solicitado não existe nesta playlist. <a href=\"`x`\">Clique aqui para acessar a página inicial da playlist.</a>",
"channel_tab_channels_label": "Canais",
"channel_tab_playlists_label": "Listas de reprodução",
"channel_tab_shorts_label": "Curtos",
"channel_tab_streams_label": "Ao Vivo",
"Music in this video": "Música neste vídeo",
"Artist: ": "Artista: ",
"Album: ": "Álbum: ",
"Standard YouTube license": "Licença padrão do YouTube",
"Song: ": "Música: ",
"Channel Sponsor": "Patrocinador do Canal",
"Download is disabled": "Download está desativado",
"Import YouTube playlist (.csv)": "Importar lista de reprodução do YouTube (.csv)"
}

@ -14,22 +14,21 @@
"Clear watch history?": "Limpar histórico de reprodução?",
"New password": "Nova palavra-chave",
"New passwords must match": "As novas palavra-chaves devem corresponder",
"Cannot change password for Google accounts": "Não é possível alterar a palavra-chave para contas do Google",
"Authorize token?": "Autorizar token?",
"Authorize token for `x`?": "Autorizar token para `x`?",
"Yes": "Sim",
"No": "Não",
"Import and Export Data": "Importar e exportar dados",
"Import": "Importar",
"Import Invidious data": "Importar dados do Invidious",
"Import YouTube subscriptions": "Importar subscrições do YouTube",
"Import Invidious data": "Importar dados JSON do Invidious",
"Import YouTube subscriptions": "Importar subscrições do YouTube/OPML",
"Import FreeTube subscriptions (.db)": "Importar subscrições do FreeTube (.db)",
"Import NewPipe subscriptions (.json)": "Importar subscrições do NewPipe (.json)",
"Import NewPipe data (.zip)": "Importar dados do NewPipe (.zip)",
"Export": "Exportar",
"Export subscriptions as OPML": "Exportar subscrições como OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Exportar subscrições como OPML (para NewPipe e FreeTube)",
"Export data as JSON": "Exportar dados como JSON",
"Export data as JSON": "Exportar dados Invidious como JSON",
"Delete account?": "Eliminar conta?",
"History": "Histórico",
"An alternative front-end to YouTube": "Uma interface alternativa ao YouTube",
@ -37,7 +36,6 @@
"source": "código-fonte",
"Log in": "Iniciar sessão",
"Log in/register": "Iniciar sessão/registar",
"Log in with Google": "Iniciar sessão com o Google",
"User ID": "Utilizador",
"Password": "Palavra-chave",
"Time (h:mm:ss):": "Tempo (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Iniciar sessão",
"Register": "Registar",
"E-mail": "E-mail",
"Google verification code": "Código de verificação do Google",
"Preferences": "Preferências",
"preferences_category_player": "Preferências do reprodutor",
"preferences_video_loop_label": "Repetir sempre: ",
@ -60,13 +57,13 @@
"preferences_volume_label": "Volume da reprodução: ",
"preferences_comments_label": "Preferência dos comentários: ",
"youtube": "YouTube",
"reddit": "reddit",
"reddit": "Reddit",
"preferences_captions_label": "Legendas predefinidas: ",
"Fallback captions: ": "Legendas alternativas: ",
"preferences_related_videos_label": "Mostrar vídeos relacionados: ",
"preferences_annotations_label": "Mostrar anotações sempre: ",
"preferences_extend_desc_label": "Estender automaticamente a descrição do vídeo: ",
"preferences_vr_mode_label": "Vídeos interativos de 360 graus: ",
"preferences_vr_mode_label": "Vídeos interativos de 360 graus (necessita de WebGL): ",
"preferences_category_visual": "Preferências visuais",
"preferences_player_style_label": "Estilo do reprodutor: ",
"Dark mode: ": "Modo escuro: ",
@ -166,17 +163,12 @@
"Hide replies": "Ocultar respostas",
"Show replies": "Mostrar respostas",
"Incorrect password": "Palavra-chave incorreta",
"Quota exceeded, try again in a few hours": "Cota excedida. Tente novamente dentro de algumas horas",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Não é possível iniciar a sessão, certifique-se que a autenticação de dois fatores (Autenticador ou SMS) está ativada.",
"Invalid TFA code": "Código TFA inválido",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Falhou o início de sessão. Isto pode ser devido a não ter ativado na sua conta a autenticação de dois fatores (2FA).",
"Wrong answer": "Resposta errada",
"Erroneous CAPTCHA": "CAPTCHA inválido",
"CAPTCHA is a required field": "CAPTCHA é um campo obrigatório",
"User ID is a required field": "O nome de utilizador é um campo obrigatório",
"Password is a required field": "Palavra-chave é um campo obrigatório",
"Wrong username or password": "Nome de utilizador ou palavra-chave incorreto",
"Please sign in using 'Log in with Google'": "Por favor, inicie sessão usando 'Iniciar sessão com o Google'",
"Password cannot be empty": "A palavra-chave não pode estar vazia",
"Password cannot be longer than 55 characters": "A palavra-chave não pode ser superior a 55 caracteres",
"Please log in": "Por favor, inicie sessão",
@ -341,9 +333,9 @@
"`x` marked it with a ❤": "`x` foi marcado como ❤",
"Audio mode": "Modo de áudio",
"Video mode": "Modo de vídeo",
"Videos": "Vídeos",
"channel_tab_videos_label": "Vídeos",
"Playlists": "Listas de reprodução",
"Community": "Comunidade",
"channel_tab_community_label": "Comunidade",
"search_filters_sort_option_relevance": "Relevância",
"search_filters_sort_option_rating": "Avaliação",
"search_filters_sort_option_date": "Data de envio",
@ -374,5 +366,110 @@
"next_steps_error_message": "Pode tentar as seguintes opções: ",
"next_steps_error_message_refresh": "Atualizar",
"next_steps_error_message_go_to_youtube": "Ir ao YouTube",
"search_filters_title": "Filtro"
"search_filters_title": "Filtro",
"generic_videos_count": "{{count}} vídeo",
"generic_videos_count_plural": "{{count}} vídeos",
"generic_playlists_count": "{{count}} lista de reprodução",
"generic_playlists_count_plural": "{{count}} listas de reprodução",
"generic_subscriptions_count": "{{count}} inscrição",
"generic_subscriptions_count_plural": "{{count}} inscrições",
"generic_views_count": "{{count}} visualização",
"generic_views_count_plural": "{{count}} visualizações",
"generic_subscribers_count": "{{count}} inscrito",
"generic_subscribers_count_plural": "{{count}} inscritos",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_label": "Qualidade de vídeo DASH preferida: ",
"preferences_quality_dash_option_2160p": "2160p",
"subscriptions_unseen_notifs_count": "{{count}} notificação não vista",
"subscriptions_unseen_notifs_count_plural": "{{count}} notificações não vistas",
"Popular enabled: ": "Página \"popular\" ativada: ",
"search_message_no_results": "Nenhum resultado encontrado.",
"preferences_quality_dash_option_auto": "Automático",
"preferences_region_label": "País do conteúdo: ",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_720p": "720p",
"preferences_watch_history_label": "Ativar histórico de reprodução: ",
"preferences_quality_dash_option_best": "Melhor",
"preferences_quality_dash_option_worst": "Pior",
"preferences_quality_dash_option_144p": "144p",
"invidious": "Invidious",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_dash": "DASH (qualidade adaptativa)",
"preferences_quality_option_medium": "Média",
"preferences_quality_option_small": "Baixa",
"preferences_quality_dash_option_1080p": "1080p",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_dash_option_360p": "360p",
"preferences_quality_dash_option_240p": "240p",
"Video unavailable": "Vídeo não disponível",
"Russian (auto-generated)": "Russo (gerado automaticamente)",
"comments_view_x_replies": "Ver {{count}} resposta",
"comments_view_x_replies_plural": "Ver {{count}} respostas",
"comments_points_count": "{{count}} ponto",
"comments_points_count_plural": "{{count}} pontos",
"English (United Kingdom)": "Inglês (Reino Unido)",
"Chinese (Hong Kong)": "Chinês (Hong Kong)",
"Chinese (Taiwan)": "Chinês (Taiwan)",
"Dutch (auto-generated)": "Holandês (gerado automaticamente)",
"French (auto-generated)": "Francês (gerado automaticamente)",
"German (auto-generated)": "Alemão (gerado automaticamente)",
"Indonesian (auto-generated)": "Indonésio (gerado automaticamente)",
"Interlingue": "Interlíngua",
"Italian (auto-generated)": "Italiano (gerado automaticamente)",
"Japanese (auto-generated)": "Japonês (gerado automaticamente)",
"Korean (auto-generated)": "Coreano (gerado automaticamente)",
"Portuguese (auto-generated)": "Português (gerado automaticamente)",
"Portuguese (Brazil)": "Português (Brasil)",
"Spanish (Spain)": "Espanhol (Espanha)",
"Vietnamese (auto-generated)": "Vietnamita (gerado automaticamente)",
"search_filters_type_option_all": "Qualquer tipo",
"search_filters_duration_option_none": "Qualquer duração",
"search_filters_duration_option_short": "Curto (< 4 minutos)",
"search_filters_duration_option_medium": "Médio (4 - 20 minutos)",
"search_filters_duration_option_long": "Longo (> 20 minutos)",
"search_filters_features_option_purchased": "Comprado",
"search_filters_apply_button": "Aplicar filtros selecionados",
"videoinfo_watch_on_youTube": "Ver no YouTube",
"videoinfo_youTube_embed_link": "Incorporar",
"adminprefs_modified_source_code_url_label": "URL do repositório do código-fonte alterado",
"videoinfo_invidious_embed_link": "Incorporar hiperligação",
"none": "nenhum",
"videoinfo_started_streaming_x_ago": "Iniciou a transmissão há `x`",
"download_subtitles": "Legendas - `x` (.vtt)",
"user_created_playlists": "`x` listas de reprodução criadas",
"user_saved_playlists": "`x` listas de reprodução guardadas",
"preferences_save_player_pos_label": "Guardar a posição de reprodução atual do vídeo: ",
"Turkish (auto-generated)": "Turco (gerado automaticamente)",
"Cantonese (Hong Kong)": "Cantonês (Hong Kong)",
"Chinese (China)": "Chinês (China)",
"Spanish (auto-generated)": "Espanhol (gerado automaticamente)",
"Spanish (Mexico)": "Espanhol (México)",
"English (United States)": "Inglês (Estados Unidos)",
"footer_donate_page": "Doar",
"footer_documentation": "Documentação",
"footer_source_code": "Código-fonte",
"footer_original_source_code": "Código-fonte original",
"footer_modfied_source_code": "Código-fonte alterado",
"Chinese": "Chinês",
"search_filters_date_label": "Data de publicação",
"search_filters_date_option_none": "Qualquer data",
"search_filters_features_option_three_sixty": "360°",
"search_filters_features_option_vr180": "VR180",
"search_message_use_another_instance": " Também pode <a href=\"`x`\">pesquisar noutra instância</a>.",
"crash_page_you_found_a_bug": "Parece que encontrou um erro no Invidious!",
"crash_page_before_reporting": "Antes de reportar um erro, verifique se:",
"crash_page_read_the_faq": "leia as <a href=\"`x`\">Perguntas frequentes (FAQ)</a>",
"crash_page_search_issue": "procurou se <a href=\"`x`\">o erro já foi reportado no GitHub</a>",
"crash_page_report_issue": "Se nenhuma opção acima ajudou, por favor <a href=\"`x`\">abra um novo problema no Github</a> (preferencialmente em inglês) e inclua o seguinte texto tal qual (NÃO o traduza):",
"search_message_change_filters_or_query": "Tente alargar os termos genéricos da pesquisa e/ou alterar os filtros.",
"crash_page_refresh": "tentou <a href=\"`x`\">recarregar a página</a>",
"crash_page_switch_instance": "tentou <a href=\"`x`\">usar outra instância</a>",
"error_video_not_in_playlist": "O vídeo pedido não existe nesta lista de reprodução. <a href=\"`x`\">Clique aqui para a página inicial da lista de reprodução.</a>",
"Artist: ": "Artista: ",
"Album: ": "Álbum: ",
"channel_tab_streams_label": "Diretos",
"channel_tab_playlists_label": "Listas de reprodução",
"channel_tab_channels_label": "Canais",
"Music in this video": "Música neste vídeo",
"channel_tab_shorts_label": "Curtos"
}

@ -11,7 +11,7 @@
"preferences_show_nick_label": "Mostrar nome de utilizador em cima: ",
"preferences_automatic_instance_redirect_label": "Redirecionamento de instância automática (solução de último recurso para redirect.invidious.io): ",
"preferences_category_misc": "Preferências diversas",
"preferences_vr_mode_label": "Vídeos interativos de 360 graus: ",
"preferences_vr_mode_label": "Vídeos interativos de 360 graus (necessita de WebGL): ",
"preferences_extend_desc_label": "Estender automaticamente a descrição do vídeo: ",
"next_steps_error_message_go_to_youtube": "Ir ao YouTube",
"next_steps_error_message": "Pode tentar as seguintes opções: ",
@ -63,8 +63,6 @@
"Could not pull trending pages.": "Não foi possível obter as páginas de tendências.",
"Could not create mix.": "Não foi possível criar a mistura.",
"Deleted or invalid channel": "Canal eliminado ou inválido",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Falhou o início de sessão. Isto pode ser devido a não ter ativado na sua conta a autenticação de dois fatores (2FA).",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Não é possível iniciar a sessão, certifique-se que a autenticação de dois fatores (Autenticador ou SMS) está ativada.",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Olá! Parece que o JavaScript está desativado. Clique aqui para ver os comentários, entretanto eles podem levar mais tempo para carregar.",
"Delete playlist": "Eliminar lista de reprodução",
"Delete playlist `x`?": "Eliminar a lista de reprodução 'x'?",
@ -81,7 +79,6 @@
"Log in/register": "Iniciar sessão/registar",
"Delete account?": "Eliminar conta?",
"Import and Export Data": "Importar e exportar dados",
"Cannot change password for Google accounts": "Não é possível alterar a palavra-chave para contas do Google",
"Filipino": "Filipino",
"Estonian": "Estónio",
"Esperanto": "Esperanto",
@ -125,15 +122,12 @@
"Please log in": "Por favor, inicie sessão",
"Password cannot be longer than 55 characters": "A palavra-chave não pode ser superior a 55 caracteres",
"Password cannot be empty": "A palavra-chave não pode estar vazia",
"Please sign in using 'Log in with Google'": "Por favor, inicie sessão usando 'Iniciar sessão com o Google'",
"Wrong username or password": "Nome de utilizador ou palavra-chave incorreto",
"Password is a required field": "Palavra-chave é um campo obrigatório",
"User ID is a required field": "O nome de utilizador é um campo obrigatório",
"CAPTCHA is a required field": "CAPTCHA é um campo obrigatório",
"Erroneous CAPTCHA": "CAPTCHA inválido",
"Wrong answer": "Resposta errada",
"Invalid TFA code": "Código TFA inválido",
"Quota exceeded, try again in a few hours": "Cota excedida. Tente novamente dentro de algumas horas",
"Incorrect password": "Palavra-chave incorreta",
"Show replies": "Mostrar respostas",
"Hide replies": "Ocultar respostas",
@ -232,7 +226,6 @@
"preferences_video_loop_label": "Repetir sempre: ",
"preferences_category_player": "Preferências do reprodutor",
"Preferences": "Preferências",
"Google verification code": "Código de verificação do Google",
"E-mail": "E-mail",
"Register": "Registar",
"Image CAPTCHA": "Imagem CAPTCHA",
@ -240,21 +233,20 @@
"Time (h:mm:ss):": "Tempo (h:mm:ss):",
"Password": "Palavra-chave",
"User ID": "Utilizador",
"Log in with Google": "Iniciar sessão com o Google",
"Log in": "Iniciar sessão",
"source": "código-fonte",
"JavaScript license information": "Informação de licença do JavaScript",
"An alternative front-end to YouTube": "Uma interface alternativa ao YouTube",
"History": "Histórico",
"Export data as JSON": "Exportar dados como JSON",
"Export data as JSON": "Exportar dados Invidious como JSON",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Exportar subscrições como OPML (para NewPipe e FreeTube)",
"Export subscriptions as OPML": "Exportar subscrições como OPML",
"Export": "Exportar",
"Import NewPipe data (.zip)": "Importar dados do NewPipe (.zip)",
"Import NewPipe subscriptions (.json)": "Importar subscrições do NewPipe (.json)",
"Import FreeTube subscriptions (.db)": "Importar subscrições do FreeTube (.db)",
"Import YouTube subscriptions": "Importar subscrições do YouTube",
"Import Invidious data": "Importar dados do Invidious",
"Import YouTube subscriptions": "Importar subscrições do YouTube/OPML",
"Import Invidious data": "Importar dados JSON do Invidious",
"Import": "Importar",
"No": "Não",
"Yes": "Sim",
@ -267,9 +259,9 @@
"Next page": "Próxima página",
"last": "últimos",
"Current version: ": "Versão atual: ",
"Community": "Comunidade",
"channel_tab_community_label": "Comunidade",
"Playlists": "Listas de reprodução",
"Videos": "Vídeos",
"channel_tab_videos_label": "Vídeos",
"Video mode": "Modo de vídeo",
"Audio mode": "Modo de áudio",
"`x` marked it with a ❤": "`x` foi marcado como ❤",
@ -432,9 +424,56 @@
"crash_page_before_reporting": "Antes de reportar um erro, verifique se:",
"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_read_the_faq": "leu as <a href=\"`x`\">Perguntas frequentes (FAQ)</a>",
"crash_page_read_the_faq": "leia as <a href=\"`x`\">Perguntas frequentes (FAQ)</a>",
"crash_page_search_issue": "procurou se <a href=\"`x`\">o erro já foi reportado no GitHub</a>",
"crash_page_report_issue": "Se nenhuma opção acima ajudou, por favor <a href=\"`x`\">abra um novo problema no Github</a> (preferencialmente em inglês) e inclua o seguinte texto tal qual (NÃO o traduza):",
"user_created_playlists": "`x` listas de reprodução criadas",
"search_filters_title": "Filtro"
"search_filters_title": "Filtro",
"Chinese (Taiwan)": "Chinês (Taiwan)",
"search_message_no_results": "Nenhum resultado encontrado.",
"search_message_change_filters_or_query": "Tente alargar os termos genéricos da pesquisa e/ou alterar os filtros.",
"search_message_use_another_instance": " Também pode <a href=\"`x`\">pesquisar noutra instância</a>.",
"English (United Kingdom)": "Inglês (Reino Unido)",
"English (United States)": "Inglês (Estados Unidos)",
"Cantonese (Hong Kong)": "Cantonês (Hong Kong)",
"Chinese": "Chinês",
"Chinese (Hong Kong)": "Chinês (Hong Kong)",
"Dutch (auto-generated)": "Holandês (gerado automaticamente)",
"French (auto-generated)": "Francês (gerado automaticamente)",
"German (auto-generated)": "Alemão (gerado automaticamente)",
"Indonesian (auto-generated)": "Indonésio (gerado automaticamente)",
"Interlingue": "Interlíngua",
"Italian (auto-generated)": "Italiano (gerado automaticamente)",
"Japanese (auto-generated)": "Japonês (gerado automaticamente)",
"Korean (auto-generated)": "Coreano (gerado automaticamente)",
"Portuguese (auto-generated)": "Português (gerado automaticamente)",
"Portuguese (Brazil)": "Português (Brasil)",
"Turkish (auto-generated)": "Turco (gerado automaticamente)",
"Vietnamese (auto-generated)": "Vietnamita (gerado automaticamente)",
"search_filters_duration_option_medium": "Médio (4 - 20 minutos)",
"search_filters_features_option_vr180": "VR180",
"search_filters_apply_button": "Aplicar filtros selecionados",
"Spanish (auto-generated)": "Espanhol (gerado automaticamente)",
"Spanish (Mexico)": "Espanhol (México)",
"preferences_watch_history_label": "Ativar histórico de reprodução: ",
"Chinese (China)": "Chinês (China)",
"Russian (auto-generated)": "Russo (gerado automaticamente)",
"Spanish (Spain)": "Espanhol (Espanha)",
"search_filters_date_label": "Data de publicação",
"search_filters_date_option_none": "Qualquer data",
"search_filters_type_option_all": "Qualquer tipo",
"search_filters_duration_option_none": "Qualquer duração",
"Popular enabled: ": "Página \"popular\" ativada: ",
"error_video_not_in_playlist": "O vídeo pedido não existe nesta lista de reprodução. <a href=\"`x`\">Clique aqui para a página inicial da lista de reprodução.</a>",
"channel_tab_playlists_label": "Listas de reprodução",
"channel_tab_channels_label": "Canais",
"channel_tab_shorts_label": "Curtos",
"channel_tab_streams_label": "Diretos",
"Music in this video": "Música neste vídeo",
"Artist: ": "Artista: ",
"Album: ": "Álbum: ",
"Song: ": "Canção: ",
"Channel Sponsor": "Patrocinador do canal",
"Standard YouTube license": "Licença padrão do YouTube",
"Download is disabled": "A descarga está desativada"
}

@ -14,7 +14,6 @@
"Clear watch history?": "Doriți să ștergeți istoricul?",
"New password": "Parola nouă",
"New passwords must match": "Câmpurile \"Parolă nouă\" trebuie să fie identice",
"Cannot change password for Google accounts": "Parola pentru un cont Google nu poate fi schimbată de pe Invidious",
"Authorize token?": "Autorizați token-ul?",
"Authorize token for `x`?": "Autorizați token-ul pentru `x` ?",
"Yes": "Da",
@ -37,7 +36,6 @@
"source": "sursă",
"Log in": "Conectați-vă",
"Log in/register": "Conectați-vă/Creați-vă un cont",
"Log in with Google": "Conectați-vă cu Google",
"User ID": "ID Utilizator",
"Password": "Parolă",
"Time (h:mm:ss):": "Ora (h:mm:ss) :",
@ -46,7 +44,6 @@
"Sign In": "Conectați-vă",
"Register": "Înregistrați-vă",
"E-mail": "E-mail",
"Google verification code": "Cod de verificare Google",
"Preferences": "Preferințe",
"preferences_category_player": "Setări de redare",
"preferences_video_loop_label": "Reluați videoclipul la nesfârșit: ",
@ -155,17 +152,12 @@
"Hide replies": "Ascundeți replicile",
"Show replies": "Afișați replicile",
"Incorrect password": "Parolă incorectă",
"Quota exceeded, try again in a few hours": "Numărul de tentative de conectare a fost depășit. Va rugăm să încercați din nou în câteva ore",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Conectare eșuată. Dacă nu reușiți să vă conectați, verificați dacă ați activat autentificarea cu doi factori (Autentificator sau SMS).",
"Invalid TFA code": "Codul de autentificare cu doi factori este invalid",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Conectare eșuată. Acest lucru ar putea fi cauzat de faptul că nu ați activat autentificarea cu doi factori.",
"Wrong answer": "Răspuns invalid",
"Erroneous CAPTCHA": "CAPTCHA invalid",
"CAPTCHA is a required field": "Câmpul CAPTCHA este obligatoriu",
"User ID is a required field": "Câmpul ID Utilizator este obligatoriu",
"Password is a required field": "Câmpul Parolă este obligatoriu",
"Wrong username or password": "Nume de utilizator sau parolă invalidă",
"Please sign in using 'Log in with Google'": "Vă rog conectați-vă folosind \"Conectați-vă cu Google\"",
"Password cannot be empty": "Parola nu poate fi goală",
"Password cannot be longer than 55 characters": "Parola nu poate să conțină mai mult de 55 de caractere",
"Please log in": "Vă rog conectați-vă",
@ -315,9 +307,9 @@
"`x` marked it with a ❤": "`x` l-a marcat cu o ❤",
"Audio mode": "Mod audio",
"Video mode": "Mod video",
"Videos": "Videoclipuri",
"channel_tab_videos_label": "Videoclipuri",
"Playlists": "Liste de redare",
"Community": "Comunitate",
"channel_tab_community_label": "Comunitate",
"Current version: ": "Versiunea actuală: ",
"crash_page_read_the_faq": "citit lista <a href=\"`x`\">Întrebărilor Frecvente (FAQ)</a>",
"generic_count_days_0": "{{count}} zi",

@ -4,7 +4,7 @@
"Unsubscribe": "Отписаться",
"Subscribe": "Подписаться",
"View channel on YouTube": "Смотреть канал на YouTube",
"View playlist on YouTube": осмотреть плейлист на YouTube",
"View playlist on YouTube": росмотреть подборку на ютубе",
"newest": "сначала новые",
"oldest": "сначала старые",
"popular": "популярные",
@ -14,7 +14,6 @@
"Clear watch history?": "Очистить историю просмотров?",
"New password": "Новый пароль",
"New passwords must match": "Новые пароли не совпадают",
"Cannot change password for Google accounts": "Изменить пароль аккаунта Google невозможно",
"Authorize token?": "Авторизовать токен?",
"Authorize token for `x`?": "Авторизовать токен для `x`?",
"Yes": "Да",
@ -30,28 +29,26 @@
"Export subscriptions as OPML": "Экспортировать подписки в формате OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Экспортировать подписки в формате OPML (для NewPipe и FreeTube)",
"Export data as JSON": "Экспортировать данные Invidious в формате JSON",
"Delete account?": "Удалить аккаунт?",
"Delete account?": "Удалить учётку?",
"History": "История",
"An alternative front-end to YouTube": "Альтернативный фронтенд для YouTube",
"JavaScript license information": "Информация о лицензиях JavaScript",
"source": "источник",
"Log in": "Войти",
"Log in/register": "Войти или зарегистрироваться",
"Log in with Google": "Войти через Google",
"User ID": "ID пользователя",
"User ID": "ИД пользователя",
"Password": "Пароль",
"Time (h:mm:ss):": "Время (ч:мм:сс):",
"Text CAPTCHA": "Текст капчи",
"Image CAPTCHA": "Изображение капчи",
"Text CAPTCHA": "Текстовая капча (англ.)",
"Image CAPTCHA": "Капча-картинка",
"Sign In": "Войти",
"Register": "Зарегистрироваться",
"E-mail": "Электронная почта",
"Google verification code": "Код подтверждения Google",
"E-mail": "Эл. почта",
"Preferences": "Настройки",
"preferences_category_player": "Настройки проигрывателя",
"preferences_video_loop_label": "Всегда повторять: ",
"preferences_autoplay_label": "Автовоспроизведение: ",
"preferences_continue_label": "Всегда включать следующее видео? ",
"preferences_continue_label": "Переходить к следующему видео? ",
"preferences_continue_autoplay_label": "Автопроигрывание следующего видео: ",
"preferences_listen_label": "Режим «только аудио» по умолчанию: ",
"preferences_local_label": "Проигрывать видео через прокси? ",
@ -71,15 +68,15 @@
"preferences_player_style_label": "Стиль проигрывателя: ",
"Dark mode: ": "Тёмное оформление: ",
"preferences_dark_mode_label": "Тема: ",
"dark": емная",
"dark": ёмная",
"light": "светлая",
"preferences_thin_mode_label": "Облегчённое оформление: ",
"preferences_category_misc": "Прочие настройки",
"preferences_automatic_instance_redirect_label": "Автоматическое перенаправление на зеркало сайта (переход на redirect.invidious.io): ",
"preferences_automatic_instance_redirect_label": "Автоматическая смена зеркала (переход на redirect.invidious.io): ",
"preferences_category_subscription": "Настройки подписок",
"preferences_annotations_subscribed_label": "Всегда показывать аннотации в видео каналов, на которые вы подписаны? ",
"Redirect homepage to feed: ": "Отображать видео с каналов, на которые вы подписаны, как главную страницу: ",
"preferences_max_results_label": "Число видео, на которые вы подписаны, в ленте: ",
"preferences_annotations_subscribed_label": "Всегда показывать аннотации на каналах из ваших подписок? ",
"Redirect homepage to feed: ": "Показывать подписки на главной странице: ",
"preferences_max_results_label": "Число видео в ленте: ",
"preferences_sort_label": "Сортировать видео: ",
"published": "по дате публикации",
"published - reverse": "по дате публикации в обратном порядке",
@ -88,7 +85,7 @@
"channel name": "по названию канала",
"channel name - reverse": "по названию канала в обратном порядке",
"Only show latest video from channel: ": "Показывать только последние видео с каналов: ",
"Only show latest unwatched video from channel: ": "Показывать только непросмотренные видео с каналов: ",
"Only show latest unwatched video from channel: ": "Показывать только последние непросмотренные видео с канала: ",
"preferences_unseen_only_label": "Показывать только непросмотренные видео: ",
"preferences_notifications_only_label": "Показывать только оповещения, если они есть: ",
"Enable web notifications": "Включить уведомления в браузере",
@ -96,24 +93,24 @@
"`x` is live": "`x` в прямом эфире",
"preferences_category_data": "Настройки данных",
"Clear watch history": "Очистить историю просмотров",
"Import/export data": "Импорткспорт данных",
"Import/export data": "Импорт и экспорт данных",
"Change password": "Изменить пароль",
"Manage subscriptions": "Управлять подписками",
"Manage tokens": "Управлять токенами",
"Manage subscriptions": "Управление подписками",
"Manage tokens": "Управление токенами",
"Watch history": "История просмотров",
"Delete account": "Удалить аккаунт",
"preferences_category_admin": "Администраторские настройки",
"preferences_category_admin": "Настройки администратора",
"preferences_default_home_label": "Главная страница по умолчанию: ",
"preferences_feed_menu_label": "Меню ленты видео: ",
"preferences_show_nick_label": "Показать ник вверху: ",
"Top enabled: ": "Включить топ видео? ",
"CAPTCHA enabled: ": "Включить капчу? ",
"Login enabled: ": "Включить авторизацию? ",
"Login enabled: ": "Включить авторизацию: ",
"Registration enabled: ": "Включить регистрацию? ",
"Report statistics: ": "Сообщать статистику? ",
"Save preferences": "Сохранить настройки",
"Subscription manager": "Менеджер подписок",
"Token manager": "Менеджер токенов",
"Subscription manager": "Управление подписками",
"Token manager": "Управление токенами",
"Token": "Токен",
"Import/export": "Импорт и экспорт",
"unsubscribe": "отписаться",
@ -122,59 +119,54 @@
"search": "поиск",
"Log out": "Выйти",
"Released under the AGPLv3 on Github.": "Выпущено под лицензией AGPLv3 на GitHub.",
"Source available here.": "Исходный код доступен здесь.",
"View JavaScript license information.": "Посмотреть информацию по лицензии JavaScript.",
"View privacy policy.": "Посмотреть политику конфиденциальности.",
"Source available here.": "Исходный код.",
"View JavaScript license information.": "Информация о лицензиях JavaScript.",
"View privacy policy.": "Политика конфиденциальности.",
"Trending": "В тренде",
"Public": "Публичный",
"Unlisted": "Нет в списке",
"Private": "Приватный",
"View all playlists": осмотреть все плейлисты",
"View all playlists": росмотреть все подборки",
"Updated `x` ago": "Обновлено `x` назад",
"Delete playlist `x`?": "Удалить плейлист `x`?",
"Delete playlist": "Удалить плейлист",
"Create playlist": "Создать плейлист",
"Delete playlist `x`?": "Удалить подборку `x`?",
"Delete playlist": "Удалить подборку",
"Create playlist": "Создать подборку",
"Title": "Заголовок",
"Playlist privacy": "Конфиденциальность плейлиста",
"Editing playlist `x`": "Редактирование плейлиста `x`",
"Show more": "Показать больше",
"Show less": "Показать меньше",
"Playlist privacy": "Видимость подборки",
"Editing playlist `x`": "Изменение подборки `x`",
"Show more": "Развернуть",
"Show less": "Свернуть",
"Watch on YouTube": "Смотреть на YouTube",
"Switch Invidious Instance": "Сменить экземпляр Invidious",
"Switch Invidious Instance": "Сменить зеркало Invidious",
"Hide annotations": "Скрыть аннотации",
"Show annotations": "Показать аннотации",
"Genre: ": "Жанр: ",
"License: ": "Лицензия: ",
"Family friendly? ": "Семейный просмотр: ",
"Wilson score: ": "Рейтинг Уилсона: ",
"Wilson score: ": "Оценка Уилсона: ",
"Engagement: ": "Вовлечённость: ",
"Whitelisted regions: ": "Доступно в регионах: ",
"Blacklisted regions: ": "Недоступно в регионах: ",
"Shared `x`": "Опубликовано `x`",
"Premieres in `x`": "Премьера через `x`",
"Premieres `x`": "Премьера `x`",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Похоже, у вас отключён JavaScript. Чтобы увидить комментарии, нажмите сюда, но учтите: они могут загружаться немного медленнее.",
"View YouTube comments": "Смотреть комментарии с YouTube",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Похоже, у вас отключён JavaScript. Нажмите сюда, чтобы увидеть комментарии. Но учтите: они могут загружаться немного медленнее.",
"View YouTube comments": "Показать комментарии с YouTube",
"View more comments on Reddit": "Посмотреть больше комментариев на Reddit",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "Показать `x` комментариев",
"": "Показать `x` комментариев"
"([^.,0-9]|^)1([^.,0-9]|$)": "Показано `x` комментариев",
"": "Показано `x` комментариев"
},
"View Reddit comments": "Смотреть комментарии с Reddit",
"Hide replies": "Скрыть ответы",
"Show replies": "Показать ответы",
"Incorrect password": "Неправильный пароль",
"Quota exceeded, try again in a few hours": "Лимит превышен, попробуйте снова через несколько часов",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Войти не удаётся. Проверьте, не включена ли двухфакторная аутентификация (по коду или смс).",
"Invalid TFA code": "Неправильный код двухфакторной аутентификации",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Не удаётся войти. Это может быть из-за того, что в вашем аккаунте не включена двухфакторная аутентификация.",
"Wrong answer": "Неправильный ответ",
"Erroneous CAPTCHA": "Неправильная капча",
"CAPTCHA is a required field": "Необходимо пройти капчу",
"User ID is a required field": "Необходимо ввести ID пользователя",
"CAPTCHA is a required field": "Необходимо решить капчу",
"User ID is a required field": "Необходимо ввести идентификатор пользователя",
"Password is a required field": "Необходимо ввести пароль",
"Wrong username or password": "Неправильный логин или пароль",
"Please sign in using 'Log in with Google'": "Пожалуйста, нажмите «Войти через Google»",
"Password cannot be empty": "Пароль не может быть пустым",
"Password cannot be longer than 55 characters": "Пароль не может быть длиннее 55 символов",
"Please log in": "Пожалуйста, войдите",
@ -185,17 +177,17 @@
"Could not get channel info.": "Не удаётся получить информацию об этом канале.",
"Could not fetch comments": "Не удаётся загрузить комментарии",
"`x` ago": "`x` назад",
"Load more": "Загрузить больше",
"Could not create mix.": "Не удаётся создать микс.",
"Empty playlist": лейлист пуст",
"Not a playlist.": "Некорректный плейлист.",
"Playlist does not exist.": лейлист не существует.",
"Load more": "Загрузить ещё",
"Could not create mix.": "Не удалось создать микс.",
"Empty playlist": одборка пуста",
"Not a playlist.": "Это не подборка.",
"Playlist does not exist.": одборка не существует.",
"Could not pull trending pages.": "Не удаётся загрузить страницы «в тренде».",
"Hidden field \"challenge\" is a required field": "Необходимо заполнить скрытое поле «challenge»",
"Hidden field \"token\" is a required field": "Необходимо заполнить скрытое поле «токен»",
"Erroneous challenge": "Неправильный ответ в «challenge»",
"Erroneous token": "Неправильный токен",
"No such user": "Недопустимое имя пользователя",
"No such user": "Пользователь не найден",
"Token is expired, please try again": "Срок действия токена истёк, попробуйте позже",
"English": "Английский",
"English (auto-generated)": "Английский (созданы автоматически)",
@ -213,13 +205,13 @@
"Burmese": "Бирманский",
"Catalan": "Каталонский",
"Cebuano": "Себуанский",
"Chinese (Simplified)": "Китайский (упрощенный)",
"Chinese (Simplified)": "Китайский (упрощённый)",
"Chinese (Traditional)": "Китайский (традиционный)",
"Corsican": "Корсиканский",
"Croatian": "Хорватский",
"Czech": "Чешский",
"Danish": "Датский",
"Dutch": "Нидерландский",
"Dutch": "Голландский",
"Esperanto": "Эсперанто",
"Estonian": "Эстонский",
"Filipino": "Филиппинский",
@ -229,8 +221,8 @@
"Georgian": "Грузинский",
"German": "Немецкий",
"Greek": "Греческий",
"Gujarati": "Гуджаратский",
"Haitian Creole": "Гаит. креольский",
"Gujarati": "Гуджарати",
"Haitian Creole": "Гаитянский креольский",
"Hausa": "Хауса",
"Hawaiian": "Гавайский",
"Hebrew": "Иврит",
@ -251,7 +243,7 @@
"Kurdish": "Курдский",
"Kyrgyz": "Киргизский",
"Lao": "Лаосский",
"Latin": "Латинский",
"Latin": "Латынь",
"Latvian": "Латышский",
"Lithuanian": "Литовский",
"Luxembourgish": "Люксембургский",
@ -262,9 +254,9 @@
"Maltese": "Мальтийский",
"Maori": "Маори",
"Marathi": "Маратхи",
"Mongolian": "Монгольская",
"Mongolian": "Монгольский",
"Nepali": "Непальский",
"Norwegian Bokmål": "Норвежский",
"Norwegian Bokmål": "Норвежский букмол",
"Nyanja": "Ньянджа",
"Pashto": "Пушту",
"Persian": "Персидский",
@ -299,7 +291,7 @@
"Vietnamese": "Вьетнамский",
"Welsh": "Валлийский",
"Western Frisian": "Западнофризский",
"Xhosa": "Коса",
"Xhosa": "Коса (кхоса)",
"Yiddish": "Идиш",
"Yoruba": "Йоруба",
"Zulu": "Зулусский",
@ -310,8 +302,8 @@
"About": "О сайте",
"Rating: ": "Рейтинг: ",
"preferences_locale_label": "Язык: ",
"View as playlist": "Смотреть как плейлист",
"Default": "По-умолчанию",
"View as playlist": "Смотреть как подборку",
"Default": "По умолчанию",
"Music": "Музыка",
"Gaming": "Игры",
"News": "Новости",
@ -325,17 +317,17 @@
"`x` marked it with a ❤": "❤ от автора канала \"`x`\"",
"Audio mode": "Аудио режим",
"Video mode": "Видео режим",
"Videos": "Видео",
"Playlists": лейлисты",
"Community": "Сообщество",
"search_filters_sort_option_relevance": "Актуальность",
"search_filters_sort_option_rating": "Рейтинг",
"search_filters_sort_option_date": "Дата загрузки",
"search_filters_sort_option_views": "Просмотры",
"channel_tab_videos_label": "Видео",
"Playlists": одборки",
"channel_tab_community_label": "Сообщество",
"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_features_label": "Дополнительно",
"search_filters_sort_label": "Сортировать",
"search_filters_date_option_hour": "Последний час",
"search_filters_date_option_today": "Сегодня",
"search_filters_date_option_week": "Эта неделя",
@ -343,9 +335,9 @@
"search_filters_date_option_year": "Этот год",
"search_filters_type_option_video": "Видео",
"search_filters_type_option_channel": "Канал",
"search_filters_type_option_playlist": лейлист",
"search_filters_type_option_playlist": одборка",
"search_filters_type_option_movie": "Фильм",
"search_filters_type_option_show": "Показать",
"search_filters_type_option_show": "Сериал",
"search_filters_features_option_hd": "HD",
"search_filters_features_option_subtitles": "Субтитры",
"search_filters_features_option_c_commons": "Creative Commons",
@ -368,34 +360,34 @@
"English (United States)": "Английский (США)",
"Cantonese (Hong Kong)": "Кантонский (Гонконг)",
"Chinese (Taiwan)": "Китайский (Тайвань)",
"Dutch (auto-generated)": "Голландский (автоматический)",
"German (auto-generated)": "Немецкий (автоматический)",
"Indonesian (auto-generated)": "Индонезийский (автоматический)",
"Italian (auto-generated)": "Итальянский (автоматический)",
"Dutch (auto-generated)": "Голландский (созданы автоматически)",
"German (auto-generated)": "Немецкий (созданы автоматически)",
"Indonesian (auto-generated)": "Индонезийский (созданы автоматически)",
"Italian (auto-generated)": "Итальянский (созданы автоматически)",
"Interlingue": "Окциденталь",
"Russian (auto-generated)": "Русский (автоматический)",
"Spanish (auto-generated)": "Испанский (автоматический)",
"Russian (auto-generated)": "Русский (созданы автоматически)",
"Spanish (auto-generated)": "Испанский (созданы автоматически)",
"Spanish (Spain)": "Испанский (Испания)",
"Turkish (auto-generated)": "Турецкий (автоматический)",
"Vietnamese (auto-generated)": "Вьетнамский (автоматический)",
"Turkish (auto-generated)": "Турецкий (созданы автоматически)",
"Vietnamese (auto-generated)": "Вьетнамский (созданы автоматически)",
"footer_documentation": "Документация",
"adminprefs_modified_source_code_url_label": "Ссылка на нашу ветку репозитория",
"adminprefs_modified_source_code_url_label": "Ссылка на репозиторий с измененными исходными кодами",
"none": "ничего",
"videoinfo_watch_on_youTube": "Смотреть на YouTube",
"videoinfo_youTube_embed_link": "Встраиваемый элемент",
"videoinfo_invidious_embed_link": "Встраиваемая ссылка",
"videoinfo_youTube_embed_link": "Версия для встраивания",
"videoinfo_invidious_embed_link": "Ссылка для встраивания",
"download_subtitles": "Субтитры - `x` (.vtt)",
"user_created_playlists": "`x` созданных плейлистов",
"crash_page_you_found_a_bug": "Похоже вы нашли баг в Invidious!",
"user_created_playlists": "`x` созданных подборок",
"crash_page_you_found_a_bug": "Похоже, вы нашли ошибку в Invidious!",
"crash_page_before_reporting": "Прежде чем сообщать об ошибке, убедитесь, что вы:",
"crash_page_refresh": "пробовали <a href=\"`x`\"> перезагрузить страницу</a>",
"crash_page_report_issue": "Если ни один вариант не помог, пожалуйста <a href=\"`x`\">откройте новую проблему на GitHub</a> (желательно на английском) и приложите следующий текст к вашему сообщению (НЕ переводите его):",
"crash_page_report_issue": "Если ни один вариант не помог, пожалуйста <a href=\"`x`\">откройте новую проблему на GitHub</a> (на английском, пжлста) и приложите следующий текст к вашему сообщению (НЕ переводите его):",
"generic_videos_count_0": "{{count}} видео",
"generic_videos_count_1": "{{count}} видео",
"generic_videos_count_2": "{{count}} видео",
"generic_playlists_count_0": "{{count}} плейлист",
"generic_playlists_count_1": "{{count}} плейлиста",
"generic_playlists_count_2": "{{count}} плейлистов",
"generic_playlists_count_0": "{{count}} подборка",
"generic_playlists_count_1": "{{count}} подборки",
"generic_playlists_count_2": "{{count}} подборок",
"tokens_count_0": "{{count}} токен",
"tokens_count_1": "{{count}} токена",
"tokens_count_2": "{{count}} токенов",
@ -417,8 +409,8 @@
"generic_views_count_0": "{{count}} просмотр",
"generic_views_count_1": "{{count}} просмотра",
"generic_views_count_2": "{{count}} просмотров",
"French (auto-generated)": "Французский (автоматический)",
"Portuguese (auto-generated)": "Португальский (автоматический)",
"French (auto-generated)": "Французский (созданы автоматически)",
"Portuguese (auto-generated)": "Португальский (созданы автоматически)",
"generic_count_days_0": "{{count}} день",
"generic_count_days_1": "{{count}} дня",
"generic_count_days_2": "{{count}} дней",
@ -438,12 +430,12 @@
"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>",
"crash_page_read_the_faq": "прочли ответы на <a href=\"`x`\">Частые Вопросы (ЧаВо)</a>",
"Chinese": "Китайский",
"Chinese (Hong Kong)": "Китайский (Гонконг)",
"Japanese (auto-generated)": "Японский (автоматический)",
"Japanese (auto-generated)": "Японский (созданы автоматически)",
"Chinese (China)": "Китайский (Китай)",
"Korean (auto-generated)": "Корейский (автоматический)",
"Korean (auto-generated)": "Корейский (созданы автоматически)",
"generic_count_months_0": "{{count}} месяц",
"generic_count_months_1": "{{count}} месяца",
"generic_count_months_2": "{{count}} месяцев",
@ -454,8 +446,8 @@
"footer_source_code": "Исходный код",
"footer_original_source_code": "Оригинальный исходный код",
"footer_modfied_source_code": "Изменённый исходный код",
"user_saved_playlists": "`x` сохранённых плейлистов",
"crash_page_search_issue": "искали <a href=\"`x`\">похожую проблему на GitHub</a>",
"user_saved_playlists": "`x` сохранённых подборок",
"crash_page_search_issue": "поискали <a href=\"`x`\">похожую проблему на GitHub</a>",
"comments_points_count_0": "{{count}} плюс",
"comments_points_count_1": "{{count}} плюса",
"comments_points_count_2": "{{count}} плюсов",
@ -464,7 +456,7 @@
"preferences_quality_option_dash": "DASH (автоматическое качество)",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_option_medium": "Среднее",
"preferences_quality_dash_label": "Предпочтительное автоматическое качество видео: ",
"preferences_quality_dash_label": "Предпочтительное качество для DASH: ",
"preferences_quality_dash_option_worst": "Очень низкое",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_2160p": "2160p",
@ -475,16 +467,30 @@
"Video unavailable": "Видео недоступно",
"preferences_save_player_pos_label": "Запоминать позицию: ",
"preferences_region_label": "Страна: ",
"preferences_watch_history_label": "Включить историю просмотров ",
"preferences_watch_history_label": "Включить историю просмотров: ",
"search_filters_title": "Фильтр",
"search_filters_duration_option_none": "Любой длины",
"search_filters_type_option_all": "Любого типа",
"search_filters_date_option_none": "Любой даты",
"search_filters_date_option_none": "Любая дата",
"search_filters_date_label": "Дата загрузки",
"search_message_no_results": "Ничего не найдено.",
"search_message_use_another_instance": " Дополнительно вы можете <a href=\"`x`\">поискать на других зеркалах</a>.",
"search_filters_features_option_vr180": "VR180",
"search_message_change_filters_or_query": "Попробуйте расширить поисковый запрос и изменить фильтры.",
"search_message_change_filters_or_query": "Попробуйте расширить поисковый запрос или изменить фильтры.",
"search_filters_duration_option_medium": "Средние (4 - 20 минут)",
"search_filters_apply_button": "Применить фильтры"
"search_filters_apply_button": "Применить фильтры",
"Popular enabled: ": "Популярное включено: ",
"error_video_not_in_playlist": "Запрошенного видео нет в этой подборке. <a href=\"`x`\">Нажмите тут, чтобы вернуться к странице подборки.</a>",
"channel_tab_playlists_label": "Подборки",
"channel_tab_channels_label": "Каналы",
"channel_tab_streams_label": "Стримы",
"channel_tab_shorts_label": "Shorts",
"Music in this video": "Музыка в этом видео",
"Artist: ": "Исполнитель: ",
"Album: ": "Альбом: ",
"Song: ": "Композиция: ",
"Standard YouTube license": "Стандартная лицензия YouTube",
"Channel Sponsor": "Спонсор канала",
"Download is disabled": "Загрузка отключена",
"Import YouTube playlist (.csv)": "Импорт плейлиста YouTube (.csv)"
}

@ -0,0 +1,123 @@
{
"generic_views_count": "බැලීම් {{count}}",
"generic_views_count_plural": "බැලීම් {{count}}",
"generic_videos_count": "{{count}} වීඩියෝව",
"generic_videos_count_plural": "වීඩියෝ {{count}}",
"generic_subscribers_count": "ග්‍රාහකයන් {{count}}",
"generic_subscribers_count_plural": "ග්‍රාහකයන් {{count}}",
"generic_subscriptions_count": "දායකත්ව {{count}}",
"generic_subscriptions_count_plural": "දායකත්ව {{count}}",
"Shared `x` ago": "`x` පෙර බෙදා ගන්නා ලදී",
"Unsubscribe": "දායක නොවන්න",
"View playlist on YouTube": "YouTube හි ධාවන ලැයිස්තුව බලන්න",
"newest": "අලුත්ම",
"oldest": "පැරණිතම",
"popular": "ජනප්‍රිය",
"last": "අවසන්",
"Authorize token?": "ටෝකනය අනුමත කරනවා ද?",
"Authorize token for `x`?": "`x` සඳහා ටෝකනය අනුමත කරනවා ද?",
"Yes": "ඔව්",
"Import and Export Data": "දත්ත ආනයනය සහ අපනයනය කිරීම",
"Import": "ආනයන",
"Import Invidious data": "Invidious JSON දත්ත ආයාත කරන්න",
"Import FreeTube subscriptions (.db)": "FreeTube දායකත්වයන් (.db) ආයාත කරන්න",
"Import NewPipe subscriptions (.json)": "NewPipe දායකත්වයන් (.json) ආයාත කරන්න",
"Import NewPipe data (.zip)": "NewPipe දත්ත (.zip) ආයාත කරන්න",
"Export": "අපනයන",
"Export data as JSON": "Invidious දත්ත JSON ලෙස අපනයනය කරන්න",
"Delete account?": "ගිණුම මකාදමනවා ද?",
"History": "ඉතිහාසය",
"An alternative front-end to YouTube": "YouTube සඳහා විකල්ප ඉදිරිපස අන්තයක්",
"source": "මූලාශ්‍රය",
"Log in/register": "පුරන්න/ලියාපදිංචිවන්න",
"Password": "මුරපදය",
"Time (h:mm:ss):": "වේලාව (h:mm:ss):",
"Sign In": "පුරන්න",
"Preferences": "මනාපයන්",
"preferences_category_player": "වීඩියෝ ධාවක මනාපයන්",
"preferences_video_loop_label": "නැවත නැවතත්: ",
"preferences_autoplay_label": "ස්වයංක්‍රීය වාදනය: ",
"preferences_continue_label": "මීලඟට වාදනය කරන්න: ",
"preferences_continue_autoplay_label": "මීළඟ වීඩියෝව ස්වයංක්‍රීයව ධාවනය කරන්න: ",
"preferences_local_label": "Proxy වීඩියෝ: ",
"preferences_watch_history_label": "නැරඹුම් ඉතිහාසය සබල කරන්න: ",
"preferences_speed_label": "පෙරනිමි වේගය: ",
"preferences_quality_option_dash": "DASH (අනුවර්තිත ගුණත්වය)",
"preferences_quality_option_medium": "මධ්‍යස්ථ",
"preferences_quality_dash_label": "කැමති DASH වීඩියෝ ගුණත්වය: ",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_1080p": "1080p",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_dash_option_360p": "360p",
"preferences_quality_dash_option_144p": "144p",
"preferences_volume_label": "ධාවකයේ හඬ: ",
"preferences_comments_label": "පෙරනිමි අදහස්: ",
"youtube": "YouTube",
"reddit": "Reddit",
"invidious": "Invidious",
"preferences_captions_label": "පෙරනිමි උපසිරැසි: ",
"preferences_related_videos_label": "අදාළ වීඩියෝ පෙන්වන්න: ",
"preferences_annotations_label": "අනුසටහන් පෙන්වන්න: ",
"preferences_vr_mode_label": "අන්තර්ක්‍රියාකාරී අංශක 360 වීඩියෝ (WebGL අවශ්‍යයි): ",
"preferences_region_label": "අන්තර්ගත රට: ",
"preferences_player_style_label": "වීඩියෝ ධාවක විලාසය: ",
"Dark mode: ": "අඳුරු මාදිලිය: ",
"preferences_dark_mode_label": "තේමාව: ",
"light": "ආලෝකමත්",
"generic_playlists_count": "{{count}} ධාවන ලැයිස්තුව",
"generic_playlists_count_plural": "ධාවන ලැයිස්තු {{count}}",
"LIVE": "සජීව",
"Subscribe": "දායක වන්න",
"View channel on YouTube": "YouTube හි නාලිකාව බලන්න",
"Next page": "ඊළඟ පිටුව",
"Previous page": "පෙර පිටුව",
"Clear watch history?": "නැරඹුම් ඉතිහාසය මකාදමනවා ද?",
"No": "නැත",
"Log in": "පුරන්න",
"New password": "නව මුරපදය",
"Import YouTube subscriptions": "YouTube/OPML දායකත්වයන් ආයාත කරන්න",
"Register": "ලියාපදිංචිවන්න",
"New passwords must match": "නව මුරපද ගැලපිය යුතුය",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "OPML ලෙස දායකත්වයන් අපනයනය කරන්න (NewPipe සහ FreeTube සඳහා)",
"Export subscriptions as OPML": "දායකත්වයන් OPML ලෙස අපනයනය කරන්න",
"JavaScript license information": "JavaScript බලපත්‍ර තොරතුරු",
"User ID": "පරිශීලක කේතය",
"Text CAPTCHA": "CAPTCHA පෙල",
"Image CAPTCHA": "CAPTCHA රූපය",
"E-mail": "විද්‍යුත් තැපෑල",
"preferences_quality_label": "කැමති වීඩියෝ ගුණත්වය: ",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_dash_option_auto": "ස්වයංක්‍රීය",
"preferences_quality_option_small": "කුඩා",
"preferences_quality_dash_option_best": "උසස්",
"preferences_quality_dash_option_2160p": "2160p",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_240p": "240p",
"preferences_extend_desc_label": "වීඩියෝ විස්තරය ස්වයංක්‍රීයව දිගහරින්න: ",
"preferences_category_visual": "දෘශ්‍ය මනාපයන්",
"dark": "අඳුරු",
"preferences_category_misc": "විවිධ මනාප",
"preferences_category_subscription": "දායකත්ව මනාප",
"Redirect homepage to feed: ": "මුල් පිටුව පෝෂණය වෙත හරවා යවන්න: ",
"preferences_max_results_label": "සංග්‍රහයේ පෙන්වන වීඩියෝ ගණන: ",
"preferences_sort_label": "වීඩියෝ වර්ග කරන්න: ",
"alphabetically": "අකාරාදී ලෙස",
"alphabetically - reverse": "අකාරාදී - ආපසු",
"channel name": "නාලිකාවේ නම",
"Only show latest video from channel: ": "නාලිකාවේ නවතම වීඩියෝව පමණක් පෙන්වන්න: ",
"preferences_unseen_only_label": "නොබැලූ පමණක් පෙන්වන්න: ",
"Enable web notifications": "වෙබ් දැනුම්දීම් සබල කරන්න",
"Import/export data": "දත්ත ආනයනය / අපනයනය",
"Change password": "මුරපදය වෙනස් කරන්න",
"Manage subscriptions": "දායකත්ව කළමනාකරණය",
"Manage tokens": "ටෝකන කළමනාකරණය",
"Watch history": "නැරඹුම් ඉතිහාසය",
"Save preferences": "මනාප සුරකින්න",
"Token": "ටෝකනය",
"View privacy policy.": "රහස්‍යතා ප්‍රතිපත්තිය බලන්න.",
"Only show latest unwatched video from channel: ": "නාලිකාවේ නවතම නැරඹන නොලද වීඩියෝව පමණක් පෙන්වන්න: ",
"preferences_category_data": "දත්ත මනාප",
"Clear watch history": "නැරඹුම් ඉතිහාසය මකාදැමීම",
"Subscriptions": "දායකත්ව"
}

@ -12,7 +12,6 @@
"Clear watch history?": "Vymazať históriu sledovania?",
"New password": "Nové heslo",
"New passwords must match": "Nové heslá sa musia zhodovať",
"Cannot change password for Google accounts": "Heslo pre účty Google sa nedá zmeniť",
"Authorize token?": "Autorizovať token?",
"Yes": "Áno",
"No": "Nie",
@ -34,7 +33,6 @@
"source": "zdroj",
"Log in": "Prihlásiť sa",
"Log in/register": "Prihlásiť sa/Registrovať",
"Log in with Google": "Prihlásiť sa pomocou účtu Google",
"User ID": "ID používateľa",
"Password": "Heslo",
"Time (h:mm:ss):": "Čas (h:mm:ss):",
@ -43,7 +41,6 @@
"Sign In": "Prihlásiť sa",
"Register": "Registrovať",
"E-mail": "E-mail",
"Google verification code": "Overovací kód Google",
"Preferences": "Nastavenia",
"preferences_category_player": "Nastavenia prehrávača",
"preferences_video_loop_label": "Vždy opakovať: ",

@ -8,7 +8,6 @@
"Clear watch history?": "Izbrisati zgodovino ogledov?",
"New password": "Novo geslo",
"New passwords must match": "Nova gesla se morajo ujemati",
"Cannot change password for Google accounts": "Ni mogoče spremeniti gesla za račune Google",
"Authorize token?": "Naj odobrim žeton?",
"Yes": "Da",
"Import and Export Data": "Uvoz in izvoz podatkov",
@ -22,7 +21,6 @@
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Izvozi naročnine kot OPML (za NewPipe in FreeTube)",
"Log in": "Prijava",
"Log in/register": "Prijava/registracija",
"Log in with Google": "Prijavi se z Googlom",
"User ID": "ID uporabnika",
"Password": "Geslo",
"Time (h:mm:ss):": "Čas (h:mm:ss):",
@ -32,7 +30,6 @@
"Sign In": "Prijavi se",
"Register": "Registriraj se",
"E-mail": "E-pošta",
"Google verification code": "Googlova koda za preverjanje",
"Preferences": "Nastavitve",
"preferences_video_loop_label": "Vedno v zanki: ",
"preferences_autoplay_label": "Samodejno predvajanje: ",
@ -80,7 +77,7 @@
"preferences_category_admin": "Skrbniške nastavitve",
"preferences_default_home_label": "Privzeta domača stran: ",
"preferences_feed_menu_label": "Meni vira: ",
"Top enabled: ": "Vrh je omogočen: ",
"Top enabled: ": "Vrh omogočen: ",
"CAPTCHA enabled: ": "CAPTCHA omogočeni: ",
"Login enabled: ": "Prijava je omogočena: ",
"Registration enabled: ": "Registracija je omogočena: ",
@ -112,7 +109,7 @@
"Wilson score: ": "Wilsonov rezultat: ",
"Engagement: ": "Sodelovanje: ",
"Blacklisted regions: ": "Regije na seznamu nedovoljenih: ",
"Shared `x`": "V skupni rabi `x`",
"Shared `x`": "V skupni rabi od: `x`",
"Premieres `x`": "Premiere `x`",
"View YouTube comments": "Oglej si YouTube komentarje",
"View more comments on Reddit": "Prikaži več komentarjev na Reddit",
@ -120,9 +117,6 @@
"([^.,0-9]|^)1([^.,0-9]|$)": "Poglej `x` komentar",
"": "Poglej `x` komentarjev"
},
"Quota exceeded, try again in a few hours": "Kvota je presežena, poskusi znova čez nekaj ur",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Ne morem se prijaviti, preveri, ali je vklopljeno dvofaktorsko preverjanje pristnosti (avtentikator ali SMS).",
"Please sign in using 'Log in with Google'": "Prijavi se z uporabo »Prijava z Googlom«",
"Password cannot be empty": "Geslo ne sme biti prazno",
"`x` ago": "`x` nazaj",
"Load more": "Naloži več",
@ -201,28 +195,28 @@
"Yiddish": "jidiš",
"Yoruba": "joruba",
"Xhosa": "xhosa",
"generic_count_years_0": "{{count}} leto",
"generic_count_years_0": "{{count}} letom",
"generic_count_years_1": "{{count}} leti",
"generic_count_years_2": "{{count}} leta",
"generic_count_years_3": "{{count}} let",
"generic_count_days_0": "{{count}} dan",
"generic_count_days_1": "{{count}} dneva",
"generic_count_days_2": "{{count}} dni",
"generic_count_days_3": "{{count}} dni",
"generic_count_hours_0": "{{count}} ura",
"generic_count_hours_1": "{{count}} uri",
"generic_count_hours_2": "{{count}} ure",
"generic_count_hours_3": "{{count}} ur",
"generic_count_minutes_0": "{{count}} minuta",
"generic_count_minutes_1": "{{count}} minuti",
"generic_count_minutes_2": "{{count}} minute",
"generic_count_minutes_3": "{{count}} minut",
"generic_count_years_2": "{{count}} leti",
"generic_count_years_3": "{{count}} leti",
"generic_count_days_0": "{{count}} dnevom",
"generic_count_days_1": "{{count}} dnevoma",
"generic_count_days_2": "{{count}} dnevi",
"generic_count_days_3": "{{count}} dnevi",
"generic_count_hours_0": "{{count}} uro",
"generic_count_hours_1": "{{count}} urami",
"generic_count_hours_2": "{{count}} urami",
"generic_count_hours_3": "{{count}} urami",
"generic_count_minutes_0": "{{count}} minuto",
"generic_count_minutes_1": "{{count}} minutami",
"generic_count_minutes_2": "{{count}} minutami",
"generic_count_minutes_3": "{{count}} minutami",
"Search": "Iskanje",
"Top": "Vrh",
"About": "O aplikaciji",
"%A %B %-d, %Y": "%A %-d %B %Y",
"Audio mode": "Avdio način",
"Videos": "Videoposnetki",
"channel_tab_videos_label": "Videoposnetki",
"search_filters_date_label": "Datum nalaganja",
"search_filters_date_option_today": "Danes",
"search_filters_date_option_week": "Ta teden",
@ -246,10 +240,10 @@
"generic_videos_count_1": "{{count}} videa",
"generic_videos_count_2": "{{count}} videi",
"generic_videos_count_3": "{{count}} videov",
"generic_views_count_0": "{{count}} ogled",
"generic_views_count_1": "{{count}} ogleda",
"generic_views_count_2": "{{count}} ogledi",
"generic_views_count_3": "{{count}} ogledov",
"generic_views_count_0": "Ogledov: {{count}}",
"generic_views_count_1": "Ogledov: {{count}}",
"generic_views_count_2": "Ogledov: {{count}}",
"generic_views_count_3": "Ogledov: {{count}}",
"generic_playlists_count_0": "{{count}} seznam predvajanja",
"generic_playlists_count_1": "{{count}} seznama predvajanja",
"generic_playlists_count_2": "{{count}} seznami predvajanja",
@ -348,8 +342,6 @@
"View Reddit comments": "Oglej si komentarje na Redditu",
"This channel does not exist.": "Ta kanal ne obstaja.",
"Hide replies": "Skrij odgovore",
"Invalid TFA code": "Neveljavna koda TFA",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Prijava ni uspela. To je lahko zato, ker za tvoj račun ni vklopljeno dvofaktorsko preverjanje pristnosti.",
"Invidious Private Feed for `x`": "Invidious zasebni vir za `x`",
"Deleted or invalid channel": "Izbrisan ali neveljaven kanal",
"Empty playlist": "Prazen seznam predvajanja",
@ -423,23 +415,23 @@
"Spanish (Spain)": "španščina (Španija)",
"Tajik": "tadžiščina",
"Tamil": "tamilščina",
"generic_count_weeks_0": "{{count}} teden",
"generic_count_weeks_1": "{{count}} tedna",
"generic_count_weeks_2": "{{count}} tedne",
"generic_count_weeks_3": "{{count}} tednov",
"generic_count_weeks_0": "{{count}} tednom",
"generic_count_weeks_1": "{{count}} tedni",
"generic_count_weeks_2": "{{count}} tedni",
"generic_count_weeks_3": "{{count}} tedni",
"Swahili": "svahilščina",
"Swedish": "švedščina",
"Vietnamese (auto-generated)": "vietnamščina (samodejno ustvarjeno)",
"generic_count_months_0": "{{count}} mesec",
"generic_count_months_1": "{{count}} meseca",
"generic_count_months_2": "{{count}} mesece",
"generic_count_months_3": "{{count}} mesecev",
"generic_count_months_0": "{{count}} mesecem",
"generic_count_months_1": "{{count}} meseci",
"generic_count_months_2": "{{count}} meseci",
"generic_count_months_3": "{{count}} meseci",
"Uzbek": "uzbeščina",
"Zulu": "zulujščina",
"generic_count_seconds_0": "{{count}} sekunda",
"generic_count_seconds_1": "{{count}} sekundi",
"generic_count_seconds_2": "{{count}} sekunde",
"generic_count_seconds_3": "{{count}} sekund",
"generic_count_seconds_0": "{{count}} sekundo",
"generic_count_seconds_1": "{{count}} sekundami",
"generic_count_seconds_2": "{{count}} sekundami",
"generic_count_seconds_3": "{{count}} sekundami",
"Popular": "Priljubljeni",
"Music": "Glasba",
"Movies": "Filmi",
@ -455,7 +447,7 @@
"Download": "Prenesi",
"permalink": "stalna povezava",
"`x` marked it with a ❤": "`x` ga je označil/a z ❤",
"Community": "Skupnost",
"channel_tab_community_label": "Skupnost",
"search_filters_features_option_three_sixty": "360°",
"Video mode": "Video način",
"search_filters_features_option_c_commons": "Creative Commons",
@ -495,12 +487,25 @@
"footer_modfied_source_code": "Spremenjena izvorna koda",
"user_created_playlists": "`x` ustvarjenih seznamov predvajanja",
"adminprefs_modified_source_code_url_label": "URL do shrambe spremenjene izvorne kode",
"videoinfo_youTube_embed_link": "Vdelati",
"videoinfo_youTube_embed_link": "Vdelaj",
"videoinfo_invidious_embed_link": "Povezava za vdelavo",
"crash_page_switch_instance": "poskušal/a <a href=\"`x`\">uporabiti drugo instanco</a>",
"download_subtitles": "Podnapisi - `x` (.vtt)",
"crash_page_refresh": "poskušal/a <a href=\"`x`\">osvežiti stran</a>",
"crash_page_before_reporting": "Preden prijaviš napako, se prepričaj, da si:",
"crash_page_search_issue": "preiskal/a <a href=\"`x`\">obstoječe težave na GitHubu</a>",
"crash_page_report_issue": "Če nič od navedenega ni pomagalo, prosim <a href=\"`x`\">odpri novo težavo v GitHubu</a> (po možnosti v angleščini) in v svoje sporočilo vključi naslednje besedilo (tega besedila NE prevajaj):"
"crash_page_report_issue": "Če nič od navedenega ni pomagalo, prosim <a href=\"`x`\">odpri novo težavo v GitHubu</a> (po možnosti v angleščini) in v svoje sporočilo vključi naslednje besedilo (tega besedila NE prevajaj):",
"Popular enabled: ": "Priljubljeni omogočeni: ",
"error_video_not_in_playlist": "Zahtevani videoposnetek ne obstaja na tem seznamu predvajanja. <a href=\"`x`\">Klikni tukaj za domačo stran seznama predvajanja.</a>",
"channel_tab_playlists_label": "Seznami predvajanja",
"channel_tab_shorts_label": "Kratki videoposnetki",
"channel_tab_channels_label": "Kanali",
"channel_tab_streams_label": "Prenosi v živo",
"Artist: ": "Umetnik/ca: ",
"Music in this video": "Glasba v tem videoposnetku",
"Album: ": "Album: ",
"Song: ": "Pesem: ",
"Standard YouTube license": "Standardna licenca YouTube",
"Channel Sponsor": "Sponzor kanala",
"Download is disabled": "Prenos je onemogočen"
}

@ -35,12 +35,10 @@
"videoinfo_youTube_embed_link": "Trupëzojeni",
"videoinfo_invidious_embed_link": "Lidhje Trupëzimi",
"oldest": "më të vjetrat",
"Cannot change password for Google accounts": "Smund të ndryshojë fjalëkalimin për llogari Google",
"New passwords must match": "Fjalëkalimet e rinj duhet të përputhen me njëri-tjetrin",
"Authorize token?": "Të autorizohet token-i?",
"Authorize token for `x`?": "Të autorizohet token-i për `x`?",
"Log in/register": "Hyni/regjistrohuni",
"Log in with Google": "Hyni me Google",
"User ID": "ID Përdoruesi",
"Password": "Fjalëkalim",
"Time (h:mm:ss):": "Kohë (h:mm:ss):",
@ -156,19 +154,14 @@
"Whitelisted regions: ": "Rajone të lejuara: ",
"Premieres `x`": "Premiera `x`",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Njatjeta! Duket sikur keni JavaScript-in të çaktivizuar. Klikoni këtu që të shihni komentet, mbani parasysh se mund të duhet pak më tepër kohë që të ngarkohen.",
"Quota exceeded, try again in a few hours": "Janë tejkaluar kuotat, riprovoni pas pak orësh",
"Blacklisted regions: ": "Rajone të palejuara: ",
"Premieres in `x`": "Premiera në `x`",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Sarrihet të bëhet hyrja, sigurohuni se mirëfilltësimi dyfaktorësh (me Mirëfilltësues apo SMS) është i aktivizuar.",
"Wrong answer": "Përgjigje e gabuar",
"Invalid TFA code": "Kod MDF i pavlefshëm",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Dështoi hyrja. Kjo mund të vijë ngaqë për llogarinë tuaj sështë aktivizuar mirëfilltësimi dyfaktorësh.",
"Erroneous CAPTCHA": "CAPTCHA e gabuar",
"CAPTCHA is a required field": "CAPTCHA është fushë e domosdoshme",
"User ID is a required field": "ID-ja e përdoruesit është fushë e domosdoshme",
"Password is a required field": "Fusha e fjalëkalimit është e domosdoshme",
"Wrong username or password": "Emër përdoruesi ose fjalëkalim i gabuar",
"Please sign in using 'Log in with Google'": "Ju lutemi, bëni hyrjen duke përdorur “Bëni hyrjen me Google”",
"Password cannot be empty": "Fjalëkalimi smund të jetë i zbrazët",
"Password cannot be longer than 55 characters": "Fjalëkalimi smund të jetë më i gjatë se 55 shenja",
"Please log in": "Ju lutemi, bëni hyrjen",
@ -259,10 +252,10 @@
"YouTube comment permalink": "Permalidhje komenti YouTube",
"Audio mode": "Mënyrë për audion",
"Playlists": "Luajlista",
"Community": "Bashkësi",
"channel_tab_community_label": "Bashkësi",
"search_filters_sort_option_relevance": "Rëndësi",
"Video mode": "Mënyrë video",
"Videos": "Video",
"channel_tab_videos_label": "Video",
"search_filters_sort_option_rating": "Vlerësim",
"search_filters_sort_option_date": "Datë ngarkimi",
"search_filters_sort_option_views": "Numër parjesh",
@ -286,7 +279,7 @@
"search_filters_type_option_show": "Shfaqe",
"search_filters_duration_option_short": "E shkurtër (< 4 minuta)",
"search_filters_features_option_purchased": "Të blera",
"footer_modfied_source_code": "Kod Burim i ndryshuar",
"footer_modfied_source_code": "Kod burim i ndryshuar",
"adminprefs_modified_source_code_url_label": "URL e depos së ndryshuar të kodit burim",
"none": "asnjë",
"videoinfo_started_streaming_x_ago": "Filloi transmetimin `x` më parë",
@ -303,7 +296,6 @@
"Previous page": "Faqja e mëparshme",
"Clear watch history?": "Të spastrohet historiku i parjeve?",
"New password": "Fjalëkalim i ri",
"Google verification code": "Kod verifikimi Google",
"preferences_related_videos_label": "Shfaq video të afërta: ",
"preferences_annotations_label": "Si parazgjedhje, shfaqi shënimet: ",
"preferences_show_nick_label": "Shfaqe nofkën në krye: ",
@ -446,6 +438,29 @@
"Import YouTube subscriptions": "Importoni pajtime YouTube/OPML",
"Export data as JSON": "Eksportoji të dhënat Invidious si JSON",
"preferences_vr_mode_label": "Video me ndërveprim 360 gradë (lyp WebGL): ",
"Shared `x`": "Ndau me të tjerë `x`",
"search_filters_title": "Filtra"
"Shared `x`": "Ndarë me të tjerë më `x`",
"search_filters_title": "Filtra",
"Popular enabled: ": "Me populloret të aktivizuara: ",
"error_video_not_in_playlist": "Videoja e kërkuar sekziston në këtë luajlistë. <a href=\"`x`\">Klikoni këtu për faqen hyrëse të luajlistës.</a>",
"search_message_use_another_instance": " Mundeni edhe të <a href=\"`x`\">kërkoni në një instancë tjetër</a>.",
"search_filters_date_label": "Datë ngarkimi",
"preferences_watch_history_label": "Aktivizo historik parjesh: ",
"Top enabled: ": "Me kryesueset të aktivizuara: ",
"preferences_video_loop_label": "Përsërite gjithmonë: ",
"search_message_no_results": "Su gjetën përfundime.",
"Could not pull trending pages.": "Su morën dot faqet në modë.",
"search_filters_date_option_none": "Çfarëdo date",
"search_message_change_filters_or_query": "Provoni të zgjeroni kërkesën tuaj të kërkimit dhe/ose të ndryshoni filtrat.",
"search_filters_type_option_all": "Çfarëdo lloji",
"search_filters_duration_option_none": "Çfarëdo kohëzgjatjeje",
"search_filters_duration_option_medium": "Mesatare (4 - 20 minuta)",
"search_filters_features_option_vr180": "VR180",
"search_filters_apply_button": "Apliko filtrat e përzgjedhur",
"channel_tab_playlists_label": "Luajlista",
"Artist: ": "Artist: ",
"Album: ": "Album: ",
"channel_tab_channels_label": "Kanale",
"Music in this video": "Muzikë në këtë video",
"channel_tab_shorts_label": "Të shkurtra",
"channel_tab_streams_label": "Transmetime të drejtpërdrejta"
}

@ -14,7 +14,6 @@
"Clear watch history?": "Izbrisati povest pregledanja?",
"New password": "Nova lozinka",
"New passwords must match": "Nove lozinke moraju biti istovetne",
"Cannot change password for Google accounts": "Nije moguće promeniti lozinku za Google naloge",
"Authorize token?": "Ovlasti žeton?",
"Authorize token for `x`?": "Ovlasti žeton za `x`?",
"Yes": "Da",
@ -37,7 +36,6 @@
"source": "izvor",
"Log in": "Prijavi se",
"Log in/register": "Prijavi se/Otvori nalog",
"Log in with Google": "Prijavi se pomoću Google-a",
"User ID": "Korisnički ID",
"Password": "Lozinka",
"Time (h:mm:ss):": "Vreme (č:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Prijava",
"Register": "Otvori nalog",
"E-mail": "E-pošta",
"Google verification code": "Google-ova overna koda",
"Preferences": "Podešavanja",
"preferences_category_player": "Podešavanja reproduktora",
"preferences_video_loop_label": "Uvek ponavljaj: ",
@ -57,13 +54,11 @@
"preferences_local_label": "Prikaz video zapisa preko posrednika: ",
"Playlist privacy": "Podešavanja privatnosti plej liste",
"Editing playlist `x`": "Izmena plej liste `x`",
"Please sign in using 'Log in with Google'": "Molimo Vas da se prijavite pomoću 'Log in with Google'",
"Playlist does not exist.": "Nepostojeća plej lista.",
"Erroneous challenge": "Pogrešan izazov",
"Maltese": "Malteški",
"Download": "Preuzmi",
"Download as: ": "Preuzmi kao: ",
"Quota exceeded, try again in a few hours": "Kvota je premašena, molimo vas da pokušate ponovo za par sati",
"Bangla": "Bangla/Bengalski",
"preferences_quality_dash_label": "Preferirani kvalitet DASH video formata: ",
"Token manager": "Upravljanje žetonima",
@ -182,7 +177,6 @@
"": "Prikaži `x` komentara"
},
"View Reddit comments": "Prikaži Reddit komentare",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Neuspešna prijava, proverite da li ste upalili dvofaktornu autentikaciju (Autentikator ili SMS).",
"CAPTCHA is a required field": "CAPTCHA je obavezno polje",
"Croatian": "Hrvatski",
"Estonian": "Estonski",
@ -257,7 +251,7 @@
"preferences_volume_label": "Jačina zvuka: ",
"preferences_locale_label": "Jezik: ",
"adminprefs_modified_source_code_url_label": "URL veza do skladišta sa Izmenjenom Izvornom Kodom",
"Community": "Zajednica",
"channel_tab_community_label": "Zajednica",
"Video mode": "Video mod",
"Fallback captions: ": "Titl u slučaju da glavni nije dostupan: ",
"Private": "Privatno",
@ -283,13 +277,11 @@
"Wrong answer": "Pogrešan odgovor",
"preferences_quality_label": "Preferirani video kvalitet: ",
"Hide replies": "Sakrij odgovore",
"Invalid TFA code": "Nevažeća TFA koda",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Neuspešna prijava! Ovo se možda dešava jer dvofaktorna autentikacija nije omogućena na vašem nalogu.",
"Erroneous CAPTCHA": "Pogrešna CAPTCHA",
"Erroneous token": "Pogrešan žeton",
"Czech": "Češki",
"Latin": "Latinski",
"Videos": "Video klipovi",
"channel_tab_videos_label": "Video klipovi",
"search_filters_features_option_four_k": "4К",
"footer_donate_page": "Doniraj",
"English": "Engleski",

@ -14,7 +14,6 @@
"Clear watch history?": "Избрисати повест прегледања?",
"New password": "Нова лозинка",
"New passwords must match": "Нове лозинке морају бити истоветне",
"Cannot change password for Google accounts": "Није могуће променити лозинку за Google налоге",
"Authorize token?": "Овласти жетон?",
"Authorize token for `x`?": "Овласти жетон за `x`?",
"Yes": "Да",
@ -37,7 +36,6 @@
"source": "извор",
"Log in": "Пријави се",
"Log in/register": "Пријави се/Отворите налог",
"Log in with Google": "Пријави се помоћу Google-а",
"User ID": "Кориснички ИД",
"Password": "Лозинка",
"Time (h:mm:ss):": "Време (ч:мм:сс):",
@ -46,7 +44,6 @@
"Sign In": "Пријава",
"Register": "Отвори налог",
"E-mail": "Е-пошта",
"Google verification code": "Google-ова оверна кода",
"Preferences": "Подешавања",
"preferences_category_player": "Подешавања репродуктора",
"preferences_video_loop_label": "Увек понављај: ",
@ -150,8 +147,6 @@
"Burmese": "Бурмански",
"preferences_quality_dash_label": "Преферирани квалитет DASH видео формата: ",
"Erroneous token": "Погрешан жетон",
"Quota exceeded, try again in a few hours": "Квота је премашена, молимо вас да покушате поново за пар сати",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Неуспешна пријава, проверите да ли сте упалили двофакторну аутентикацију (Аутентикатор или СМС).",
"CAPTCHA is a required field": "CAPTCHA је обавезно поље",
"No such user": "Непостојећи корисник",
"Chinese (Traditional)": "Кинески (Традиционални)",
@ -164,7 +159,6 @@
"preferences_show_nick_label": "Прикажи надимке на врху: ",
"Report statistics: ": "Извештавај о статистици: ",
"Show more": "Прикажи више",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Неуспешна пријава! Ово се можда дешава јер двофакторна аутентикација није омогућена на vашем налогу.",
"Wrong answer": "Погрешан одговор",
"Hidden field \"token\" is a required field": "Сакривено \"token\" поље је обавезно",
"English": "Енглески",
@ -198,7 +192,6 @@
"User ID is a required field": "Кориснички ИД је обавезно поље",
"Password is a required field": "Лозинка је обавезно поље",
"Wrong username or password": "Погрешно корисничко име или лозинка",
"Please sign in using 'Log in with Google'": "Молимо Вас да се пријавите помоћу 'Log in with Google'",
"Password cannot be empty": "Лозинка не може бити празна",
"Password cannot be longer than 55 characters": "Лозинка не може бити дужа од 55 карактера",
"Invidious Private Feed for `x`": "Инвидиоус Приватни Довод за `x`",
@ -245,7 +238,7 @@
"(edited)": "(измењено)",
"`x` marked it with a ❤": "`x` је означио/ла ово са ❤",
"Audio mode": "Аудио мод",
"Videos": "Видео клипови",
"channel_tab_videos_label": "Видео клипови",
"search_filters_sort_option_views": "Број прегледа",
"search_filters_features_label": "Карактеристике",
"search_filters_date_option_today": "Данас",
@ -298,7 +291,7 @@
"Ukrainian": "Украјински",
"permalink": "трајна веза",
"Pashto": "Паштунски",
"Community": "Заједница",
"channel_tab_community_label": "Заједница",
"Sindhi": "Синди",
"Could not fetch comments": "Узимање коментара није успело",
"Bangla": "Бангла/Бенгалски",
@ -324,7 +317,6 @@
"Released under the AGPLv3 on Github.": "Избачено под лиценцом AGPLv3 на GitHub-у.",
"Afrikaans": "Африканс",
"preferences_automatic_instance_redirect_label": "Аутоматско пребацивање на другу инстанцу у случају отказивања (пречи ће назад на редирецт.инвидиоус.ио): ",
"Invalid TFA code": "Неважећа TFA кода",
"Please log in": "Молимо вас да се пријавите",
"English (auto-generated)": "Енглески (аутоматски генерисано)",
"Hindi": "Хинди",

@ -14,7 +14,6 @@
"Clear watch history?": "Töm visningshistorik?",
"New password": "Nytt lösenord",
"New passwords must match": "Nya lösenord måste stämma överens",
"Cannot change password for Google accounts": "Kan inte ändra lösenord på Google-konton",
"Authorize token?": "Auktorisera åtkomsttoken?",
"Authorize token for `x`?": "Auktorisera åtkomsttoken för `x`?",
"Yes": "Ja",
@ -37,7 +36,6 @@
"source": "källa",
"Log in": "Logga in",
"Log in/register": "Logga in/registrera",
"Log in with Google": "Logga in med Google",
"User ID": "Användar-ID",
"Password": "Lösenord",
"Time (h:mm:ss):": "Tid (h:mm:ss):",
@ -46,7 +44,6 @@
"Sign In": "Inloggning",
"Register": "Registrera",
"E-mail": "E-post",
"Google verification code": "Google-bekräftelsekod",
"Preferences": "Inställningar",
"preferences_category_player": "Spelarinställningar",
"preferences_video_loop_label": "Loopa alltid: ",
@ -162,17 +159,12 @@
"Hide replies": "Dölj svar",
"Show replies": "Visa svar",
"Incorrect password": "Fel lösenord",
"Quota exceeded, try again in a few hours": "Kvoten överskriden, försök igen om ett par timmar",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Kunde inte logga in, försäkra dig om att tvåfaktors-autentisering (Authenticator eller SMS) är påslagen.",
"Invalid TFA code": "Ogiltig tvåfaktor-kod",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Inloggning misslyckades. Detta kan vara för att tvåfaktors-autentisering inte är påslaget på ditt konto.",
"Wrong answer": "Fel svar",
"Erroneous CAPTCHA": "Ogiltig CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA är ett obligatoriskt fält",
"User ID is a required field": "Användar-ID är ett obligatoriskt fält",
"Password is a required field": "Lösenord är ett obligatoriskt fält",
"Wrong username or password": "Ogiltigt användarnamn eller lösenord",
"Please sign in using 'Log in with Google'": "Logga in genom \"Google-inloggning\"",
"Password cannot be empty": "Lösenordet kan inte vara tomt",
"Password cannot be longer than 55 characters": "Lösenordet kan inte vara längre än 55 tecken",
"Please log in": "Logga in",
@ -323,9 +315,9 @@
"`x` marked it with a ❤": "`x` lämnade ett ❤",
"Audio mode": "Ljudläge",
"Video mode": "Videoläge",
"Videos": "Videor",
"channel_tab_videos_label": "Videor",
"Playlists": "Spellistor",
"Community": "Gemenskap",
"channel_tab_community_label": "Gemenskap",
"search_filters_sort_option_relevance": "Relevans",
"search_filters_sort_option_rating": "Rankning",
"search_filters_sort_option_date": "Datum",

@ -1,126 +1,123 @@
{
"LIVE": "CANLI",
"Shared `x` ago": "`x` önce paylaşıldı",
"Unsubscribe": "Abonelikten çık",
"Subscribe": "Abone ol",
"View channel on YouTube": "Kanalı YouTube'da görüntüle",
"View playlist on YouTube": "Oynatma listesini YouTube'da görüntüle",
"newest": "en yeni",
"oldest": "en eski",
"popular": "popüler",
"last": "son",
"Next page": "Sonraki sayfa",
"Previous page": "Önceki sayfa",
"Shared `x` ago": "`x` Önce Paylaşıldı",
"Unsubscribe": "Abonelikten Çık",
"Subscribe": "Abone Ol",
"View channel on YouTube": "Kanalı YouTube'da Görüntüle",
"View playlist on YouTube": "Oynatma Listesini YouTube'da Görüntüle",
"newest": "En Yeni",
"oldest": "En Eski",
"popular": "Popüler",
"last": "Son",
"Next page": "Sonraki Sayfa",
"Previous page": "Önceki Sayfa",
"Clear watch history?": "İzleme geçmişi temizlensin mi?",
"New password": "Yeni parola",
"New passwords must match": "Yeni parolalar eşleşmek zorunda",
"Cannot change password for Google accounts": "Google hesapları için parola değiştirilemez",
"New password": "Yeni Parola",
"New passwords must match": "Yeni Parolalar Eşleşmek Zorunda",
"Authorize token?": "Belirteç yetkilendirilsin mi?",
"Authorize token for `x`?": "`x` için belirteç yetkilendirilsin mi?",
"Yes": "Evet",
"No": "Hayır",
"Import and Export Data": "Verileri İçe ve Dışa Aktar",
"Import": "İçe aktar",
"Import Invidious data": "İnvidious JSON verilerini içe aktar",
"Import YouTube subscriptions": "YouTube/OPML aboneliklerini içe aktar",
"Import FreeTube subscriptions (.db)": "FreeTube aboneliklerini içe aktar (.db)",
"Import NewPipe subscriptions (.json)": "NewPipe aboneliklerini içe aktar (.json)",
"Import NewPipe data (.zip)": "NewPipe verilerini içe aktar (.zip)",
"Export": "Dışa aktar",
"Export subscriptions as OPML": "Abonelikleri OPML olarak dışa aktar",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Abonelikleri OPML olarak dışa aktar (NewPipe ve FreeTube için)",
"Export data as JSON": "Invidious verilerini JSON olarak dışa aktar",
"Import": "İçe Aktar",
"Import Invidious data": "Invidious JSON Verilerini İçe Aktar",
"Import YouTube subscriptions": "YouTube/OPML Aboneliklerini İçe Aktar",
"Import FreeTube subscriptions (.db)": "FreeTube Aboneliklerini İçe Aktar (.db)",
"Import NewPipe subscriptions (.json)": "NewPipe Aboneliklerini İçe Aktar (.json)",
"Import NewPipe data (.zip)": "NewPipe Verilerini İçe Aktar (.zip)",
"Export": "Dışa Aktar",
"Export subscriptions as OPML": "Abonelikleri OPML Olarak Dışa Aktar",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Abonelikleri OPML Olarak Dışa Aktar (NewPipe ve FreeTube İçin)",
"Export data as JSON": "İnvidious Verilerini JSON Olarak Dışa Aktar",
"Delete account?": "Hesap silinsin mi?",
"History": "Geçmiş",
"An alternative front-end to YouTube": "YouTube için alternatif bir ön-yüz",
"JavaScript license information": "JavaScript lisans bilgileri",
"source": "kaynak",
"Log in": "Oturum aç",
"Log in/register": "Oturum aç/kayıt ol",
"Log in with Google": "Google ile oturum aç",
"User ID": "Kullanıcı kimliği",
"An alternative front-end to YouTube": "YouTube İçin Alternatif Bir Ön-Yüz",
"JavaScript license information": "JavaScript Lisans Bilgileri",
"source": "Kaynak",
"Log in": "Oturum Aç",
"Log in/register": "Oturum Aç/Kayıt Ol",
"User ID": "Kullanıcı Kimliği",
"Password": "Parola",
"Time (h:mm:ss):": "Zaman (h:mm:ss):",
"Text CAPTCHA": "Metin CAPTCHA",
"Image CAPTCHA": "Resim CAPTCHA",
"Sign In": "Oturum Aç",
"Register": "Kayıt Ol",
"E-mail": "E-posta",
"Google verification code": "Google doğrulama kodu",
"E-mail": "E-Posta",
"Preferences": "Tercihler",
"preferences_category_player": "Oynatıcı tercihleri",
"preferences_video_loop_label": "Sürekli döngü: ",
"preferences_autoplay_label": "Otomatik oynat: ",
"preferences_continue_label": "Öntanımlı olarak sonrakini oynat: ",
"preferences_continue_autoplay_label": "Sonraki videoyu otomatik oynat: ",
"preferences_listen_label": "Öntanımlı olarak dinle: ",
"preferences_local_label": "Videoları proxy'le: ",
"preferences_speed_label": "Öntanımlı hız: ",
"preferences_quality_label": "Tercih edilen video kalitesi: ",
"preferences_volume_label": "Oynatıcı ses seviyesi: ",
"preferences_comments_label": "Öntanımlı yorumlar: ",
"preferences_category_player": "Oynatıcı Tercihleri",
"preferences_video_loop_label": "Sürekli Döngü: ",
"preferences_autoplay_label": "Otomatik Oynat: ",
"preferences_continue_label": "Öntanımlı Olarak Sonrakini Oynat: ",
"preferences_continue_autoplay_label": "Sonraki Videoyu Otomatik Oynat: ",
"preferences_listen_label": "Öntanımlı Olarak Dinle: ",
"preferences_local_label": "Videolara Proxy Uygula: ",
"preferences_speed_label": "Öntanımlı Hız: ",
"preferences_quality_label": "Tercih Edilen Video Kalitesi: ",
"preferences_volume_label": "Oynatıcı Ses Seviyesi: ",
"preferences_comments_label": "Öntanımlı Yorumlar: ",
"youtube": "YouTube",
"reddit": "Reddit",
"preferences_captions_label": "Öntanımlı altyazılar: ",
"Fallback captions: ": "Yedek altyazılar: ",
"preferences_related_videos_label": "İlgili videoları göster: ",
"preferences_annotations_label": "Öntanımlı olarak ek açıklamaları göster: ",
"preferences_extend_desc_label": "Video ıklamasını otomatik olarak genişlet: ",
"preferences_vr_mode_label": "Etkileşimli 360 derece videolar (WebGL gerektirir): ",
"preferences_category_visual": "Görsel tercihler",
"preferences_player_style_label": "Oynatıcı biçimi: ",
"Dark mode: ": "Karanlık mod: ",
"preferences_captions_label": "Öntanımlı Altyazılar: ",
"Fallback captions: ": "Yedek Altyazılar: ",
"preferences_related_videos_label": "İlgili Videoları Göster: ",
"preferences_annotations_label": "Öntanımlı Olarak Ek Açıklamaları Göster: ",
"preferences_extend_desc_label": "Video ıklamasını Otomatik Olarak Genişlet: ",
"preferences_vr_mode_label": "Etkileşimli 360 Derece Videolar (WebGL Gerektirir): ",
"preferences_category_visual": "Görsel Tercihler",
"preferences_player_style_label": "Oynatıcı Biçimi: ",
"Dark mode: ": "Koyu Mod: ",
"preferences_dark_mode_label": "Tema: ",
"dark": "karanlık",
"light": "aydınlık",
"preferences_thin_mode_label": "İnce mod: ",
"preferences_category_misc": "Çeşitli tercihler",
"preferences_automatic_instance_redirect_label": "Otomatik örnek yeniden yönlendirmesi (yedek: redirect.invidious.io): ",
"preferences_category_subscription": "Abonelik tercihleri",
"preferences_annotations_subscribed_label": "Abone olunan kanallar için ek açıklamaları öntanımlı olarak göster: ",
"Redirect homepage to feed: ": "Ana sayfayı akışa yönlendir: ",
"preferences_max_results_label": "Akışta gösterilen video sayısı: ",
"preferences_sort_label": "Videoları sıralama kriteri: ",
"published": "yayınlandı",
"published - reverse": "yayınlandı - ters",
"alphabetically": "alfabetik olarak",
"alphabetically - reverse": "alfabetik olarak - ters",
"channel name": "kanal adı",
"channel name - reverse": "kanal adı - ters",
"Only show latest video from channel: ": "Sadece kanaldaki en son videoyu göster: ",
"Only show latest unwatched video from channel: ": "Sadece kanaldaki en son izlenmemiş videoyu göster: ",
"preferences_unseen_only_label": "Sadece izlenmemişleri göster: ",
"preferences_notifications_only_label": "Sadece bildirimleri göster (eğer varsa): ",
"Enable web notifications": "Ağ bildirimlerini etkinleştir",
"`x` uploaded a video": "`x` bir video yükledi",
"`x` is live": "`x` canlı yayında",
"preferences_category_data": "Veri tercihleri",
"Clear watch history": "İzleme geçmişini temizle",
"Import/export data": "Verileri içe/dışa aktar",
"Change password": "Parolayı değiştir",
"Manage subscriptions": "Abonelikleri yönet",
"Manage tokens": "Belirteçleri yönet",
"Watch history": "İzleme geçmişi",
"Delete account": "Hesap silme",
"preferences_category_admin": "Yönetici tercihleri",
"preferences_default_home_label": "Öntanımlı ana sayfa: ",
"preferences_feed_menu_label": "Akış menüsü: ",
"preferences_show_nick_label": "Takma adı üstte göster: ",
"Top enabled: ": "Top etkin: ",
"CAPTCHA enabled: ": "CAPTCHA etkin: ",
"Login enabled: ": "Oturum açma etkin: ",
"Registration enabled: ": "Kayıt olma etkin: ",
"Report statistics: ": "Rapor istatistikleri: ",
"Save preferences": "Tercihleri kaydet",
"Subscription manager": "Abonelik yöneticisi",
"Token manager": "Belirteç yöneticisi",
"dark": "Koyu",
"light": "ık",
"preferences_thin_mode_label": "İnce Mod: ",
"preferences_category_misc": "Çeşitli Tercihler",
"preferences_automatic_instance_redirect_label": "Otomatik Örnek Yeniden Yönlendirmesi (Yedek: redirect.invidious.io): ",
"preferences_category_subscription": "Abonelik Tercihleri",
"preferences_annotations_subscribed_label": "Abone Olunan Kanallar İçin Ek Açıklamaları Öntanımlı Olarak Göster: ",
"Redirect homepage to feed: ": "Ana Sayfayı Akışa Yönlendir: ",
"preferences_max_results_label": "Akışta Gösterilen Video Sayısı: ",
"preferences_sort_label": "Videoları Sıralama Kriteri: ",
"published": "Yayınlandı",
"published - reverse": "Yayınlandı - Ters",
"alphabetically": "Alfabetik Olarak",
"alphabetically - reverse": "Alfabetik Olarak - Ters",
"channel name": "Kanal Adı",
"channel name - reverse": "Kanal Adı - Ters",
"Only show latest video from channel: ": "Sadece Kanaldaki En Son Videoyu Göster: ",
"Only show latest unwatched video from channel: ": "Sadece Kanaldaki En Son İzlenmemiş Videoyu Göster: ",
"preferences_unseen_only_label": "Sadece İzlenmemişleri Göster: ",
"preferences_notifications_only_label": "Sadece Bildirimleri Göster (Eğer Varsa): ",
"Enable web notifications": "Ağ Bildirimlerini Etkinleştir",
"`x` uploaded a video": "`x` Bir Video Yükledi",
"`x` is live": "`x` Canlı Yayında",
"preferences_category_data": "Veri Tercihleri",
"Clear watch history": "İzleme Geçmişini Temizle",
"Import/export data": "Verileri İçe/Dışa Aktar",
"Change password": "Parolayı Değiştir",
"Manage subscriptions": "Abonelikleri Yönet",
"Manage tokens": "Belirteçleri Yönet",
"Watch history": "İzleme Geçmişi",
"Delete account": "Hesap Silme",
"preferences_category_admin": "Yönetici Tercihleri",
"preferences_default_home_label": "Öntanımlı Ana Sayfa: ",
"preferences_feed_menu_label": "Akış Menüsü: ",
"preferences_show_nick_label": "Takma Adı Üstte Göster: ",
"Top enabled: ": "Top Etkin: ",
"CAPTCHA enabled: ": "CAPTCHA Etkin: ",
"Login enabled: ": "Oturum Açma Etkin: ",
"Registration enabled: ": "Kayıt Olma Etkin: ",
"Report statistics: ": "Rapor İstatistikleri: ",
"Save preferences": "Tercihleri Kaydet",
"Subscription manager": "Abonelik Yöneticisi",
"Token manager": "Belirteç Yöneticisi",
"Token": "Belirteç",
"Import/export": "İçe/dışa aktar",
"unsubscribe": "abonelikten çık",
"revoke": "geri al",
"Import/export": "İçe/Dışa Aktar",
"unsubscribe": "Abonelikten Çık",
"revoke": "Geri Al",
"Subscriptions": "Abonelikler",
"search": "ara",
"Log out": ıkış yap",
"search": "Ara",
"Log out": ıkış Yap",
"Released under the AGPLv3 on Github.": "GitHub'da AGPLv3 altında yayınlandı.",
"Source available here.": "Kaynak kodları burada bulunabilir.",
"View JavaScript license information.": "JavaScript lisans bilgilerini görüntüle.",
@ -129,76 +126,71 @@
"Public": "Genel",
"Unlisted": "Listelenmemiş",
"Private": "Özel",
"View all playlists": "Tüm oynatma listelerini görüntüle",
"Updated `x` ago": "`x` önce güncellendi",
"View all playlists": "Tüm Oynatma Listelerini Görüntüle",
"Updated `x` ago": "`x` Önce Güncellendi",
"Delete playlist `x`?": "`x` oynatma listesi silinsin mi?",
"Delete playlist": "Oynatma listesini sil",
"Create playlist": "Oynatma listesi oluştur",
"Delete playlist": "Oynatma Listesini Sil",
"Create playlist": "Oynatma Listesi Oluştur",
"Title": "Başlık",
"Playlist privacy": "Oynatma listesi gizliliği",
"Editing playlist `x`": "`x` oynatma listesi düzenleniyor",
"Show more": "Daha fazla göster",
"Show less": "Daha az göster",
"Watch on YouTube": "YouTube'da izle",
"Playlist privacy": "Oynatma Listesi Gizliliği",
"Editing playlist `x`": "`x` Oynatma Listesi Düzenleniyor",
"Show more": "Daha Fazla Göster",
"Show less": "Daha Az Göster",
"Watch on YouTube": "YouTube'da İzle",
"Switch Invidious Instance": "Invidious Örneğini Değiştir",
"Hide annotations": "Ek ıklamaları gizle",
"Show annotations": "Ek ıklamaları göster",
"Hide annotations": "Ek ıklamaları Gizle",
"Show annotations": "Ek ıklamaları Göster",
"Genre: ": "Tür: ",
"License: ": "Lisans: ",
"Family friendly? ": "Aile için uygun mu? ",
"Wilson score: ": "Wilson puanı: ",
"Engagement: ": "İzleyenlerin oy verme oranı: ",
"Whitelisted regions: ": "Beyaz listeye alınan bölgeler: ",
"Blacklisted regions: ": "Kara listeye alınan bölgeler: ",
"Shared `x`": "`x` paylaşıldı",
"Premieres in `x`": "`x`içinde ilk gösterim",
"Premieres `x`": "`x` ilk gösterim",
"Wilson score: ": "Wilson Puanı: ",
"Engagement: ": "İzleyenlerin Oy Verme Oranı: ",
"Whitelisted regions: ": "Beyaz Listeye Alınan Bölgeler: ",
"Blacklisted regions: ": "Kara Listeye Alınan Bölgeler: ",
"Shared `x`": "`x` Paylaşıldı",
"Premieres in `x`": "`x`İçinde İlk Gösterim",
"Premieres `x`": "`x` İlk Gösterim",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Merhaba! JavaScript'i kapatmış gibi görünüyorsun. Yorumları görüntülemek için buraya tıkla, yüklenmelerinin biraz uzun sürebileceğini unutma.",
"View YouTube comments": "YouTube yorumlarını görüntüle",
"View more comments on Reddit": "Reddit'te daha fazla yorum görüntüle",
"View YouTube comments": "YouTube Yorumlarını Görüntüle",
"View more comments on Reddit": "Reddit'te Daha Fazla Yorum Görüntüle",
"View `x` comments": {
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` yorumu görüntüle",
"": "`x` yorumu görüntüle"
"([^.,0-9]|^)1([^.,0-9]|$)": "`x` Yorumu Görüntüle",
"": "`x` Yorumu Görüntüle"
},
"View Reddit comments": "Reddit yorumlarını görüntüle",
"Hide replies": "Cevapları gizle",
"Show replies": "Cevapları göster",
"Incorrect password": "Yanlış parola",
"Quota exceeded, try again in a few hours": "Kota aşıldı, birkaç saat içinde tekrar deneyin",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Oturum açılamadı, iki faktörlü kimlik doğrulamanın (Authenticator ya da SMS) açık olduğundan emin olun.",
"Invalid TFA code": "Geçersiz TFA kodu",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Giriş başarısız. Bunun nedeni, hesabınız için iki faktörlü kimlik doğrulamanın açık olmaması olabilir.",
"Wrong answer": "Yanlış cevap",
"View Reddit comments": "Reddit Yorumlarını Görüntüle",
"Hide replies": "Cevapları Gizle",
"Show replies": "Cevapları Göster",
"Incorrect password": "Yanlış Parola",
"Wrong answer": "Yanlış Cevap",
"Erroneous CAPTCHA": "Hatalı CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA zorunlu bir alandır",
"User ID is a required field": "Kullanıcı kimliği zorunlu bir alandır",
"Password is a required field": "Parola zorunlu bir alandır",
"Wrong username or password": "Yanlış kullanıcı adı ya da parola",
"Please sign in using 'Log in with Google'": "Lütfen 'Google ile giriş yap' seçeneğini kullanarak oturum açın",
"Password cannot be empty": "Parola boş olamaz",
"Password cannot be longer than 55 characters": "Parola 55 karakterden uzun olamaz",
"Please log in": "Lütfen oturum açın",
"Invidious Private Feed for `x`": "`x` için İnvidious Özel Akışı",
"channel:`x`": "kanal:`x`",
"Deleted or invalid channel": "Silinmiş ya da geçersiz kanal",
"CAPTCHA is a required field": "CAPTCHA Zorunlu Bir Alandır",
"User ID is a required field": "Kullanıcı Kimliği Zorunlu Bir Alandır",
"Password is a required field": "Parola Zorunlu Bir Alandır",
"Wrong username or password": "Yanlış Kullanıcı Adı ya da Parola",
"Password cannot be empty": "Parola Boş Olamaz",
"Password cannot be longer than 55 characters": "Parola 55 Karakterden Uzun Olamaz",
"Please log in": "Lütfen Oturum Açın",
"Invidious Private Feed for `x`": "`x` İçin Invidious Özel Akışı",
"channel:`x`": "Kanal:`x`",
"Deleted or invalid channel": "Silinmiş ya da Geçersiz Kanal",
"This channel does not exist.": "Bu kanal mevcut değil.",
"Could not get channel info.": "Kanal bilgisi alınamadı.",
"Could not fetch comments": "Yorumlar alınamadı",
"`x` ago": "`x` önce",
"Load more": "Daha fazla yükle",
"Could not fetch comments": "Yorumlar Alınamadı",
"`x` ago": "`x` Önce",
"Load more": "Daha Fazla Yükle",
"Could not create mix.": "Mix oluşturulamadı.",
"Empty playlist": "Boş oynatma listesi",
"Empty playlist": "Boş Oynatma Listesi",
"Not a playlist.": "Oynatma listesi değil.",
"Playlist does not exist.": "Oynatma listesi mevcut değil.",
"Could not pull trending pages.": "Trend sayfaları alınamıyor.",
"Hidden field \"challenge\" is a required field": "Gizli alan \"challenge\" zorunlu bir alandır",
"Hidden field \"token\" is a required field": "\"belirteç\" gizli alanı zorunlu bir alandır",
"Erroneous challenge": "Hatalı challenge",
"Erroneous token": "Hatalı belirteç",
"No such user": "Böyle bir kullanıcı yok",
"Token is expired, please try again": "Belirtecin süresi doldu, lütfen tekrar deneyin",
"Hidden field \"challenge\" is a required field": "Gizli Alan \"Challenge\" Zorunlu Bir Alandır",
"Hidden field \"token\" is a required field": "\"Belirteç\" Gizli Alanı Zorunlu Bir Alandır",
"Erroneous challenge": "Hatalı Challenge",
"Erroneous token": "Hatalı Belirteç",
"No such user": "Böyle Bir Kullanıcı Yok",
"Token is expired, please try again": "Belirtecin Süresi Doldu, Lütfen Tekrar Deneyin",
"English": "İngilizce",
"English (auto-generated)": "İngilizce (otomatik oluşturuldu)",
"English (auto-generated)": "İngilizce (Otomatik Oluşturuldu)",
"Afrikaans": "Afrikanca",
"Albanian": "Arnavutça",
"Amharic": "Amharca",
@ -230,9 +222,9 @@
"German": "Almanca",
"Greek": "Yunanca",
"Gujarati": "Guceratça",
"Haitian Creole": "Haiti Creole dili",
"Haitian Creole": "Haiti Creole Dili",
"Hausa": "Hausaca",
"Hawaiian": "Hawaii dili",
"Hawaiian": "Hawaii Dili",
"Hebrew": "İbranice",
"Hindi": "Hintçe",
"Hmong": "Hmong",
@ -244,7 +236,7 @@
"Italian": "İtalyanca",
"Japanese": "Japonca",
"Javanese": "Cava dili",
"Kannada": "Kannada dili",
"Kannada": "Kannada Dili",
"Kazakh": "Kazakça",
"Khmer": "Kmerce",
"Korean": "Korece",
@ -258,10 +250,10 @@
"Macedonian": "Makedonca",
"Malagasy": "Malgaşça",
"Malay": "Malayca",
"Malayalam": "Malayalam dili",
"Malayalam": "Malayalam Dili",
"Maltese": "Maltaca",
"Maori": "Maori dili",
"Marathi": "Marati dili",
"Maori": "Maori Dili",
"Marathi": "Marati Dili",
"Mongolian": "Moğolca",
"Nepali": "Nepalce",
"Norwegian Bokmål": "Norveççe Bokmål",
@ -270,19 +262,19 @@
"Persian": "Farsça",
"Polish": "Lehçe",
"Portuguese": "Portekizce",
"Punjabi": "Pencap dili",
"Punjabi": "Pencap Dili",
"Romanian": "Rumence",
"Russian": "Rusça",
"Samoan": "Samoa dili",
"Samoan": "Samoa Dili",
"Scottish Gaelic": "İskoç Galcesi",
"Serbian": "Sırpça",
"Shona": "Şona dili",
"Shona": "Şona Dili",
"Sindhi": "Sintçe",
"Sinhala": "Seylanca",
"Slovak": "Slovakça",
"Slovenian": "Slovence",
"Somali": "Somalice",
"Southern Sotho": "Güney Sotho dili",
"Southern Sotho": "Güney Sotho Dili",
"Spanish": "İspanyolca",
"Spanish (Latin America)": "İspanyolca (Latin Amerika)",
"Sundanese": "Sundaca",
@ -290,7 +282,7 @@
"Swedish": "İsveççe",
"Tajik": "Tacikçe",
"Tamil": "Tamilce",
"Telugu": "Telugu dili",
"Telugu": "Telugu Dili",
"Thai": "Tayca",
"Turkish": "Türkçe",
"Ukrainian": "Ukraynaca",
@ -299,176 +291,190 @@
"Vietnamese": "Vietnamca",
"Welsh": "Galce",
"Western Frisian": "Batı Frizcesi",
"Xhosa": "Xhosa dili",
"Xhosa": "Xhosa Dili",
"Yiddish": "Yiddiş",
"Yoruba": "Yoruba dili",
"Yoruba": "Yoruba Dili",
"Zulu": "Zuluca",
"Fallback comments: ": "Yedek yorumlar: ",
"Fallback comments: ": "Yedek Yorumlar: ",
"Popular": "Popüler",
"Search": "Ara",
"Top": "Enler",
"About": "Hakkında",
"Rating: ": "Değerlendirme: ",
"preferences_locale_label": "Dil: ",
"View as playlist": "Oynatma listesi olarak görüntüle",
"View as playlist": "Oynatma Listesi Olarak Görüntüle",
"Default": "Öntanımlı",
"Music": "Müzik",
"Gaming": "Oyun",
"News": "Haberler",
"Movies": "Filmler",
"Download": "İndir",
"Download as: ": "Şu şekilde indir: ",
"Download as: ": "Şu Şekilde İndir: ",
"%A %B %-d, %Y": "%A %B %-d, %Y",
"(edited)": "(düzenlendi)",
"YouTube comment permalink": "YouTube yorumu kalıcı linki",
"permalink": "kalıcı link",
"`x` marked it with a ❤": "`x` ❤ ile işaretledi",
"Audio mode": "Ses modu",
"Video mode": "Video modu",
"Videos": "Videolar",
"Playlists": "Oynatma listeleri",
"Community": "Topluluk",
"(edited)": "(Düzenlendi)",
"YouTube comment permalink": "YouTube Yorumu Kalıcı Linki",
"permalink": "Kalıcı Link",
"`x` marked it with a ❤": "`x` ❤ İle İşaretledi",
"Audio mode": "Ses Modu",
"Video mode": "Video Modu",
"channel_tab_videos_label": "Videolar",
"Playlists": "Oynatma Listeleri",
"channel_tab_community_label": "Topluluk",
"search_filters_sort_option_relevance": "İlgi",
"search_filters_sort_option_rating": "Değerlendirme",
"search_filters_sort_option_date": "Yükleme tarihi",
"search_filters_sort_option_views": "Görüntüleme sayısı",
"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_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_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_subtitles": "Alt Yazılar",
"search_filters_features_option_c_commons": "Yaratıcı",
"search_filters_features_option_three_d": "3D",
"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",
"Current version: ": "Şu anki sürüm: ",
"next_steps_error_message": "Bundan sonra şunları denemelisiniz: ",
"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",
"search_filters_duration_option_short": "Kısa (4 dakikadan az)",
"search_filters_duration_option_long": "Uzun (20 dakikadan fazla)",
"next_steps_error_message_go_to_youtube": "YouTube'a Git",
"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ı",
"footer_source_code": "Kaynak Kodları",
"footer_original_source_code": "Orijinal Kaynak Kodları",
"footer_modfied_source_code": "Değiştirilmiş kaynak kodları",
"adminprefs_modified_source_code_url_label": "Değiştirilmiş kaynak kodları deposunun URL'si",
"footer_donate_page": "Bağış yap",
"preferences_region_label": "İçerik ülkesi: ",
"preferences_quality_dash_label": "Tercih edilen DASH video kalitesi: ",
"adminprefs_modified_source_code_url_label": "Değiştirilmiş Kaynak Kodları Deposunun URL'si",
"footer_donate_page": "Bağış Yap",
"preferences_region_label": "İçerik Ülkesi: ",
"preferences_quality_dash_label": "Tercih Edilen DASH Video Kalitesi: ",
"preferences_quality_option_hd720": "HD720",
"preferences_quality_dash_option_best": "En iyi",
"preferences_quality_dash_option_worst": "En kötü",
"preferences_quality_dash_option_4320p": "4320p",
"preferences_quality_dash_option_2160p": "2160p",
"preferences_quality_dash_option_480p": "480p",
"preferences_quality_dash_option_360p": "360p",
"preferences_quality_dash_option_240p": "240p",
"preferences_quality_dash_option_144p": "144p",
"preferences_quality_dash_option_best": "En İyi",
"preferences_quality_dash_option_worst": "En Kötü",
"preferences_quality_dash_option_4320p": "4320P",
"preferences_quality_dash_option_2160p": "2160P",
"preferences_quality_dash_option_480p": "480P",
"preferences_quality_dash_option_360p": "360P",
"preferences_quality_dash_option_240p": "240P",
"preferences_quality_dash_option_144p": "144P",
"invidious": "Invidious",
"none": "yok",
"videoinfo_started_streaming_x_ago": "`x` önce yayına başladı",
"videoinfo_youTube_embed_link": "Göm",
"videoinfo_invidious_embed_link": "Bağlantıyı Göm",
"user_created_playlists": "`x` oluşturulan oynatma listeleri",
"user_saved_playlists": "`x` kaydedilen oynatma listeleri",
"none": "Yok",
"videoinfo_started_streaming_x_ago": "`x` Önce Yayına Başladı",
"videoinfo_youTube_embed_link": "Entegre Et",
"videoinfo_invidious_embed_link": "Bağlantıyı Entegre Et",
"user_created_playlists": "`x` Oluşturulan Oynatma Listeleri",
"user_saved_playlists": "`x` Kaydedilen Oynatma Listeleri",
"preferences_quality_option_small": "Küçük",
"preferences_quality_dash_option_720p": "720p",
"preferences_quality_dash_option_720p": "720P",
"preferences_quality_option_medium": "Orta",
"preferences_quality_dash_option_1440p": "1440p",
"preferences_quality_dash_option_1080p": "1080p",
"Video unavailable": "Video kullanılamıyor",
"preferences_quality_option_dash": "DASH (uyarlanabilir kalite)",
"preferences_quality_dash_option_1440p": "1440P",
"preferences_quality_dash_option_1080p": "1080P",
"Video unavailable": "Video Kullanılamıyor",
"preferences_quality_option_dash": "DASH (Uyarlanabilir Kalite)",
"preferences_quality_dash_option_auto": "Otomatik",
"search_filters_features_option_purchased": "Satın alınan",
"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: ",
"generic_views_count": "{{count}} görüntüleme",
"generic_views_count_plural": "{{count}} görüntüleme",
"generic_subscribers_count": "{{count}} abone",
"generic_subscribers_count_plural": "{{count}} abone",
"generic_subscriptions_count": "{{count}} abonelik",
"generic_subscriptions_count_plural": "{{count}} abonelik",
"subscriptions_unseen_notifs_count": "{{count}} okunmamış bildirim",
"subscriptions_unseen_notifs_count_plural": "{{count}} okunmamış bildirim",
"comments_points_count": "{{count}} puan",
"comments_points_count_plural": "{{count}} puan",
"generic_count_hours": "{{count}} saat",
"generic_count_hours_plural": "{{count}} saat",
"generic_count_minutes": "{{count}} dakika",
"generic_count_minutes_plural": "{{count}} dakika",
"generic_count_seconds": "{{count}} saniye",
"generic_count_seconds_plural": "{{count}} saniye",
"generic_playlists_count": "{{count}} oynatma listesi",
"generic_playlists_count_plural": "{{count}} oynatma listesi",
"tokens_count": "{{count}} belirteç",
"tokens_count_plural": "{{count}} belirteç",
"comments_view_x_replies": "{{count}} yanıtı görüntüle",
"comments_view_x_replies_plural": "{{count}} yanıtı görüntüle",
"generic_count_years": "{{count}} yıl",
"generic_count_years_plural": "{{count}} yıl",
"generic_count_months": "{{count}} ay",
"generic_count_months_plural": "{{count}} ay",
"generic_count_days": "{{count}} gün",
"generic_count_days_plural": "{{count}} gün",
"generic_videos_count": "{{count}} video",
"generic_videos_count_plural": "{{count}} video",
"generic_count_weeks": "{{count}} hafta",
"generic_count_weeks_plural": "{{count}} hafta",
"videoinfo_watch_on_youTube": "YouTube'da İzle",
"download_subtitles": "Alt Yazılar - `x` (.vtt)",
"preferences_save_player_pos_label": "Oynatma Konumunu Kaydet: ",
"generic_views_count": "{{count}} Görüntülenme",
"generic_views_count_plural": "{{count}} Görüntülenme",
"generic_subscribers_count": "{{count}} Abone",
"generic_subscribers_count_plural": "{{count}} Abone",
"generic_subscriptions_count": "{{count}} Abonelik",
"generic_subscriptions_count_plural": "{{count}} Abonelik",
"subscriptions_unseen_notifs_count": "{{count}} Okunmamış Bildirim",
"subscriptions_unseen_notifs_count_plural": "{{count}} Okunmamış Bildirim",
"comments_points_count": "{{count}} Puan",
"comments_points_count_plural": "{{count}} Puan",
"generic_count_hours": "{{count}} Saat",
"generic_count_hours_plural": "{{count}} Saat",
"generic_count_minutes": "{{count}} Dakika",
"generic_count_minutes_plural": "{{count}} Dakika",
"generic_count_seconds": "{{count}} Saniye",
"generic_count_seconds_plural": "{{count}} Saniye",
"generic_playlists_count": "{{count}} Oynatma Listesi",
"generic_playlists_count_plural": "{{count}} Oynatma Listesi",
"tokens_count": "{{count}} Belirteç",
"tokens_count_plural": "{{count}} Belirteç",
"comments_view_x_replies": "{{count}} Yanıtı Görüntüle",
"comments_view_x_replies_plural": "{{count}} Yanıtı Görüntüle",
"generic_count_years": "{{count}} Yıl",
"generic_count_years_plural": "{{count}} Yıl",
"generic_count_months": "{{count}} Ay",
"generic_count_months_plural": "{{count}} Ay",
"generic_count_days": "{{count}} Gün",
"generic_count_days_plural": "{{count}} Gün",
"generic_videos_count": "{{count}} Video",
"generic_videos_count_plural": "{{count}} Video",
"generic_count_weeks": "{{count}} Hafta",
"generic_count_weeks_plural": "{{count}} Hafta",
"crash_page_you_found_a_bug": "Görünüşe göre Invidious'ta bir hata buldunuz!",
"crash_page_before_reporting": "Bir hatayı bildirmeden önce, şunları yaptığınızdan emin olun:",
"crash_page_refresh": "<a href=\"`x`\">sayfayı yenilemeye</a> çalıştınız",
"crash_page_switch_instance": "<a href=\"`x`\">başka bir örnek kullanmaya</a> çalıştınız",
"crash_page_read_the_faq": "<a href=\"`x`\">Sık Sorulan Soruları (SSS)</a> okudunuz",
"crash_page_search_issue": "<a href=\"`x`\">GitHub'daki sorunlarda</a> aradınız",
"crash_page_report_issue": "Yukarıdakilerin hiçbiri yardımcı olmadıysa, lütfen <a href=\"`x`\">GitHub'da yeni bir sorun açın</a> (tercihen İngilizce) ve mesajınıza aşağıdaki metni ekleyin (bu metni ÇEVİRMEYİN):",
"crash_page_refresh": "<a href=\"`x`\">Sayfayı Yenilemeye</a> Çalıştınız",
"crash_page_switch_instance": "<a href=\"`x`\">Başka Bir Örnek Kullanmaya</a> Çalıştınız",
"crash_page_read_the_faq": "<a href=\"`x`\">Sık Sorulan Soruları (SSS)</a> Okudunuz",
"crash_page_search_issue": "<a href=\"`x`\">GitHub'daki Sorunlarda</a> Aradınız",
"crash_page_report_issue": "Yukarıdakilerin hiçbiri yardımcı olmadıysa, lütfen <a href=\"`x`\">GitHub'da yeni bir sorun açın</a> (Tercihen İngilizce) ve mesajınıza aşağıdaki metni ekleyin (Bu metni ÇEVİRMEYİN):",
"English (United Kingdom)": "İngilizce (Birleşik Krallık)",
"Chinese": "Çince",
"Interlingue": "İnterlingue",
"Italian (auto-generated)": "İtalyanca (otomatik oluşturuldu)",
"Japanese (auto-generated)": "Japonca (otomatik oluşturuldu)",
"Italian (auto-generated)": "İtalyanca (Otomatik Oluşturuldu)",
"Japanese (auto-generated)": "Japonca (Otomatik Oluşturuldu)",
"Portuguese (Brazil)": "Portekizce (Brezilya)",
"Russian (auto-generated)": "Rusça (otomatik oluşturuldu)",
"Spanish (auto-generated)": "İspanyolca (otomatik oluşturuldu)",
"Russian (auto-generated)": "Rusça (Otomatik Oluşturuldu)",
"Spanish (auto-generated)": "İspanyolca (Otomatik Oluşturuldu)",
"Spanish (Mexico)": "İspanyolca (Meksika)",
"English (United States)": "İngilizce (ABD)",
"Cantonese (Hong Kong)": "Kantonca (Hong Kong)",
"Chinese (Taiwan)": "Çince (Tayvan)",
"Dutch (auto-generated)": "Felemenkçe (otomatik oluşturuldu)",
"Indonesian (auto-generated)": "Endonezyaca (otomatik oluşturuldu)",
"Dutch (auto-generated)": "Felemenkçe (Otomatik Oluşturuldu)",
"Indonesian (auto-generated)": "Endonezyaca (Otomatik Oluşturuldu)",
"Chinese (Hong Kong)": "Çince (Hong Kong)",
"French (auto-generated)": "Fransızca (otomatik oluşturuldu)",
"Korean (auto-generated)": "Korece (otomatik oluşturuldu)",
"Turkish (auto-generated)": "Türkçe (otomatik oluşturuldu)",
"French (auto-generated)": "Fransızca (Otomatik Oluşturuldu)",
"Korean (auto-generated)": "Korece (Otomatik Oluşturuldu)",
"Turkish (auto-generated)": "Türkçe (Otomatik Oluşturuldu)",
"Chinese (China)": "Çince (Çin)",
"German (auto-generated)": "Almanca (otomatik oluşturuldu)",
"Portuguese (auto-generated)": "Portekizce (otomatik oluşturuldu)",
"German (auto-generated)": "Almanca (Otomatik Oluşturuldu)",
"Portuguese (auto-generated)": "Portekizce (Otomatik Oluşturuldu)",
"Spanish (Spain)": "İspanyolca (İspanya)",
"Vietnamese (auto-generated)": "Vietnamca (otomatik oluşturuldu)",
"preferences_watch_history_label": "İzleme geçmişini etkinleştir: ",
"Vietnamese (auto-generated)": "Vietnamca (Otomatik Oluşturuldu)",
"preferences_watch_history_label": "İzleme Geçmişini Etkinleştir: ",
"search_message_use_another_instance": " Ayrıca <a href=\"`x`\">başka bir örnekte arayabilirsiniz</a>.",
"search_filters_type_option_all": "Herhangi bir tür",
"search_filters_duration_option_none": "Herhangi bir süre",
"search_filters_type_option_all": "Herhangi Bir Tür",
"search_filters_duration_option_none": "Herhangi Bir Süre",
"search_message_no_results": "Sonuç bulunamadı.",
"search_filters_date_label": "Yükleme tarihi",
"search_filters_apply_button": "Seçili filtreleri uygula",
"search_filters_date_option_none": "Herhangi bir tarih",
"search_filters_duration_option_medium": "Orta (4 - 20 dakika)",
"search_filters_date_label": "Yükleme Tarihi",
"search_filters_apply_button": "Seçili Filtreleri Uygula",
"search_filters_date_option_none": "Herhangi Bir Tarih",
"search_filters_duration_option_medium": "Orta (4 - 20 Dakika)",
"search_filters_features_option_vr180": "VR180",
"search_filters_title": "Filtreler",
"search_message_change_filters_or_query": "Arama sorgunuzu genişletmeyi ve/veya filtreleri değiştirmeyi deneyin."
"search_message_change_filters_or_query": "Arama sorgunuzu genişletmeyi ve/veya filtreleri değiştirmeyi deneyin.",
"Popular enabled: ": "Popüler Etkin: ",
"error_video_not_in_playlist": "İstenen video bu oynatma listesinde yok. <a href=\"`x`\">Oynatma listesi ana sayfası için buraya tıklayın.</a>",
"channel_tab_channels_label": "Kanallar",
"channel_tab_shorts_label": "Kısa Çekimler",
"channel_tab_streams_label": "Canlı Yayınlar",
"channel_tab_playlists_label": "Oynatma Listeleri",
"Album: ": "Albüm: ",
"Music in this video": "Bu videodaki müzik",
"Artist: ": "Sanatçı: ",
"Channel Sponsor": "Kanal Sponsoru",
"Song: ": "Şarkı: ",
"Standard YouTube license": "Standart YouTube lisansı",
"Download is disabled": "İndirme devre dışı",
"Import YouTube playlist (.csv)": "YouTube Oynatma Listesini İçe Aktar (.csv)"
}

@ -1,6 +1,6 @@
{
"LIVE": "ПРЯМИЙ ЕФІР",
"Shared `x` ago": "Розміщено `x` назад",
"LIVE": "НАЖИВО",
"Shared `x` ago": "Розміщено `x` тому",
"Unsubscribe": "Відписатися",
"Subscribe": "Підписатися",
"View channel on YouTube": "Подивитися канал на YouTube",
@ -14,7 +14,6 @@
"Clear watch history?": "Очистити історію переглядів?",
"New password": "Новий пароль",
"New passwords must match": "Нові паролі не співпадають",
"Cannot change password for Google accounts": "Змінити пароль обліківки Google неможливо",
"Authorize token?": "Авторизувати токен?",
"Authorize token for `x`?": "Авторизувати токен для `x`?",
"Yes": "Так",
@ -30,23 +29,21 @@
"Export subscriptions as OPML": "Експортувати підписки у форматі OPML",
"Export subscriptions as OPML (for NewPipe & FreeTube)": "Експортувати підписки у форматі OPML (для NewPipe та FreeTube)",
"Export data as JSON": "Експортувати дані Invidious у форматі JSON",
"Delete account?": "Видалити обліківку?",
"Delete account?": "Видалити обліковий запис?",
"History": "Історія",
"An alternative front-end to YouTube": "Альтернативний фронтенд до YouTube",
"JavaScript license information": "Інформація щодо ліцензій JavaScript",
"source": "джерело",
"Log in": "Увійти",
"Log in/register": "Увійти або зареєструватися",
"Log in with Google": "Увійти через Google",
"User ID": "ID користувача",
"Password": "Пароль",
"Time (h:mm:ss):": "Час (г:мм:сс):",
"Text CAPTCHA": "Текст капчі",
"Image CAPTCHA": "Зображення капчі",
"Time (h:mm:ss):": "Час (г:хх:сс):",
"Text CAPTCHA": "Текст CAPTCHA",
"Image CAPTCHA": "Зображення CAPTCHA",
"Sign In": "Увійти",
"Register": "Зареєструватися",
"E-mail": "Електронна пошта",
"Google verification code": "Код підтвердження Google",
"Preferences": "Налаштування",
"preferences_category_player": "Налаштування програвача",
"preferences_video_loop_label": "Завжди повторювати: ",
@ -54,7 +51,7 @@
"preferences_continue_label": "Завжди вмикати наступне відео: ",
"preferences_continue_autoplay_label": "Автовідтворення наступного відео: ",
"preferences_listen_label": "Режим «тільки звук» як усталений: ",
"preferences_local_label": "Програвати відео через проксі? ",
"preferences_local_label": "Відтворення відео через проксі: ",
"preferences_speed_label": "Усталена швидкість відео: ",
"preferences_quality_label": "Пріорітетна якість відео: ",
"preferences_volume_label": "Гучність відео: ",
@ -63,13 +60,13 @@
"reddit": "Reddit",
"preferences_captions_label": "Основна мова субтитрів: ",
"Fallback captions: ": "Запасна мова субтитрів: ",
"preferences_related_videos_label": "Показувати схожі відео? ",
"preferences_annotations_label": "Завжди показувати анотації? ",
"preferences_related_videos_label": "Показувати схожі відео: ",
"preferences_annotations_label": "Завжди показувати анотації: ",
"preferences_category_visual": "Налаштування сайту",
"preferences_player_style_label": "Стиль програвача: ",
"Dark mode: ": "Темне оформлення: ",
"Dark mode: ": "Темний режим: ",
"preferences_dark_mode_label": "Тема: ",
"dark": "темна",
"dark": "Темна",
"light": "Світла",
"preferences_thin_mode_label": "Полегшене оформлення: ",
"preferences_category_subscription": "Налаштування підписок",
@ -101,11 +98,11 @@
"preferences_category_admin": "Адміністраторські налаштування",
"preferences_default_home_label": "Усталена домашня сторінка: ",
"preferences_feed_menu_label": "Меню потоку з відео: ",
"Top enabled: ": "Увімкнути топ відео? ",
"CAPTCHA enabled: ": "Увімкнути капчу? ",
"Login enabled: ": "Увімкнути авторизацію? ",
"Registration enabled: ": "Увімкнути реєстрацію? ",
"Report statistics: ": "Повідомляти статистику? ",
"Top enabled: ": "Увімкнути топ відео: ",
"CAPTCHA enabled: ": "Увімкнути CAPTCHA: ",
"Login enabled: ": "Увімкнути вхід: ",
"Registration enabled: ": "Увімкнути реєстрацію: ",
"Report statistics: ": "Повідомляти статистику: ",
"Save preferences": "Зберегти налаштування",
"Subscription manager": "Менеджер підписок",
"Token manager": "Менеджер токенів",
@ -125,7 +122,7 @@
"Private": "Особистий",
"View all playlists": "Переглянути всі списки відтворення",
"Updated `x` ago": "Оновлено `x` тому",
"Delete playlist `x`?": "Видалити список відтворення \"x\"?",
"Delete playlist `x`?": "Видалити список відтворення `x`?",
"Delete playlist": "Видалити список відтворення",
"Create playlist": "Створити список відтворення",
"Title": "Заголовок",
@ -142,7 +139,7 @@
"Whitelisted regions: ": "Доступно у регіонах: ",
"Blacklisted regions: ": "Недоступно у регіонах: ",
"Shared `x`": "Розміщено `x`",
"Premieres in `x`": "Прем’єра через `x`",
"Premieres in `x`": "Прем’єра за `x`",
"Premieres `x`": "Прем’єра `x`",
"Hi! Looks like you have JavaScript turned off. Click here to view comments, keep in mind they may take a bit longer to load.": "Схоже, у вас відключений JavaScript. Щоб побачити коментарі, натисніть сюда, але майте на увазі, що вони можуть завантажуватися трохи довше.",
"View YouTube comments": "Переглянути коментарі з YouTube",
@ -155,21 +152,16 @@
"Hide replies": "Сховати відповіді",
"Show replies": "Показати відповіді",
"Incorrect password": "Неправильний пароль",
"Quota exceeded, try again in a few hours": "Ліміт перевищено, спробуйте знову за декілька годин",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Не вдається увійти. Перевірте, чи не ввімкнена двофакторна аутентифікація (за кодом чи смс).",
"Invalid TFA code": "Неправильний код двофакторної аутентифікації",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Не вдається увійти. Це може бути через те, що у вашій обліківці не ввімкнена двофакторна аутентифікація.",
"Wrong answer": "Неправильна відповідь",
"Erroneous CAPTCHA": "Неправильна капча",
"CAPTCHA is a required field": "Необхідно пройти капчу",
"CAPTCHA is a required field": "Необхідно пройти CAPTCHA",
"User ID is a required field": "Необхідно ввести ID користувача",
"Password is a required field": "Необхідно ввести пароль",
"Wrong username or password": "Неправильний логін чи пароль",
"Please sign in using 'Log in with Google'": "Будь ласка, натисніть «Увійти через Google»",
"Password cannot be empty": "Пароль не може бути порожнім",
"Password cannot be longer than 55 characters": "Пароль не може бути довшим за 55 знаків",
"Please log in": "Будь ласка, увійдіть",
"Invidious Private Feed for `x`": "Приватний поток відео Invidious для `x`",
"Invidious Private Feed for `x`": "Приватний потік відео Invidious для `x`",
"channel:`x`": "канал: `x`",
"Deleted or invalid channel": "Канал видалено або не знайдено",
"This channel does not exist.": "Такого каналу не існує.",
@ -315,9 +307,9 @@
"`x` marked it with a ❤": "❤ цьому від каналу `x`",
"Audio mode": "Аудіорежим",
"Video mode": "Відеорежим",
"Videos": "Відео",
"channel_tab_videos_label": "Відео",
"Playlists": "Плейлисти",
"Community": "Спільнота",
"channel_tab_community_label": "Спільнота",
"Current version: ": "Поточна версія: ",
"generic_views_count_0": "{{count}} перегляд",
"generic_views_count_1": "{{count}} перегляди",
@ -386,12 +378,12 @@
"Spanish (Mexico)": "Іспанська (Мексика)",
"Spanish (Spain)": "Іспанська (Іспанія)",
"next_steps_error_message_go_to_youtube": "Перейти до YouTube",
"footer_donate_page": ожертвувати",
"footer_donate_page": ідтримати",
"footer_documentation": "Документація",
"footer_source_code": "Вихідний код",
"footer_original_source_code": "Оригінал вихідного коду",
"footer_modfied_source_code": "Змінений вихідний код",
"adminprefs_modified_source_code_url_label": "URL-адреса репозиторію зміненого вихідного коду",
"footer_source_code": "Джерельний код",
"footer_original_source_code": "Оригінал джерельного коду",
"footer_modfied_source_code": "Змінений джерельний код",
"adminprefs_modified_source_code_url_label": "URL-адреса репозиторію зміненого джерельного коду",
"none": "нема",
"videoinfo_started_streaming_x_ago": "Трансляцію розпочато `x` тому",
"crash_page_you_found_a_bug": "Схоже, ви знайшли ваду в Invidious!",
@ -408,7 +400,7 @@
"next_steps_error_message": "Після чого спробуйте: ",
"next_steps_error_message_refresh": "Оновити сторінку",
"Search": "Пошук",
"preferences_extend_desc_label": "Автоматично розширювати опис відео: ",
"preferences_extend_desc_label": "Автоматично розгортати опис відео: ",
"preferences_category_misc": "Різноманітні параметри",
"Show less": "Коротше",
"preferences_quality_option_small": "Низька",
@ -486,5 +478,19 @@
"search_filters_features_option_purchased": "Придбано",
"search_filters_sort_option_relevance": "Відповідні",
"search_filters_sort_option_rating": "Рейтингові",
"search_filters_sort_option_views": "Популярні"
"search_filters_sort_option_views": "Популярні",
"Popular enabled: ": "Популярне ввімкнено: ",
"error_video_not_in_playlist": "Запитуваного відео в цьому списку відтворення не існує. <a href=\"`x`\">Клацніть тут, щоб переглянути домашню сторінку списку відтворення.</a>",
"channel_tab_shorts_label": "Shorts",
"channel_tab_streams_label": "Прямі трансляції",
"channel_tab_playlists_label": "Добірки",
"channel_tab_channels_label": "Канали",
"Music in this video": "Музика в цьому відео",
"Artist: ": "Виконавець: ",
"Album: ": "Альбом: ",
"Song: ": "Пісня: ",
"Channel Sponsor": "Спонсор каналу",
"Standard YouTube license": "Стандартна ліцензія YouTube",
"Download is disabled": "Завантаження вимкнено",
"Import YouTube playlist (.csv)": "Імпорт списку відтворення YouTube (.csv)"
}

@ -16,7 +16,6 @@
"Clear watch history?": "Xóa lịch sử xem?",
"New password": "Mật khẩu mới",
"New passwords must match": "Mật khẩu mới phải khớp",
"Cannot change password for Google accounts": "Không thể thay đổi mật khẩu cho tài khoản Google",
"Authorize token?": "Cấp phép mã thông báo?",
"Authorize token for `x`?": "Cấp phép mã thông báo cho` x`?",
"Yes": "Đúng",
@ -39,7 +38,6 @@
"source": "nguồn",
"Log in": "Đăng nhập",
"Log in/register": "Đăng nhập / đăng ký",
"Log in with Google": "Đăng nhập bằng Google",
"User ID": "Tên người dùng",
"Password": "Mật khẩu",
"Time (h:mm:ss):": "Thời gian (h: mm: ss):",
@ -48,7 +46,6 @@
"Sign In": "Đăng nhập",
"Register": "Đăng ký",
"E-mail": "E-mail",
"Google verification code": "Mã xác minh của Google",
"Preferences": "Sở thích",
"preferences_category_player": "Tùy chọn người chơi",
"preferences_video_loop_label": "Luôn lặp lại: ",
@ -152,17 +149,12 @@
"Hide replies": "Ẩn câu trả lời",
"Show replies": "Hiển thị câu trả lời",
"Incorrect password": "Mật khẩu không đúng",
"Quota exceeded, try again in a few hours": "Đã vượt quá hạn ngạch, hãy thử lại sau vài giờ nữa",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "Không thể đăng nhập, hãy đảm bảo rằng xác thực hai yếu tố (Authenticator hoặc SMS) được bật.",
"Invalid TFA code": "Mã TFA không hợp lệ",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "Đăng nhập không thành công. Điều này có thể là do xác thực hai yếu tố chưa được bật cho tài khoản của bạn.",
"Wrong answer": "Câu trả lời sai",
"Erroneous CAPTCHA": "CAPTCHA bị lỗi",
"CAPTCHA is a required field": "CAPTCHA là trường bắt buộc",
"User ID is a required field": "User ID là trường bắt buộc",
"Password is a required field": "Mật khẩu là trường bắt buộc",
"Wrong username or password": "Tên người dùng hoặc mật khẩu sai",
"Please sign in using 'Log in with Google'": "Vui lòng đăng nhập bằng 'Đăng nhập bằng Google'",
"Password cannot be empty": "Mật khẩu không được để trống",
"Password cannot be longer than 55 characters": "Mật khẩu không được dài hơn 55 ký tự",
"Please log in": "Xin vui lòng đăng nhập",
@ -311,9 +303,9 @@
"`x` marked it with a ❤": "` x` đã đánh dấu nó bằng một ❤",
"Audio mode": "Chế độ âm thanh",
"Video mode": "Chế độ quay",
"Videos": "Video",
"channel_tab_videos_label": "Video",
"Playlists": "Danh sách phát",
"Community": "Cộng đồng",
"channel_tab_community_label": "Cộng đồng",
"search_filters_sort_option_relevance": "liên quan",
"search_filters_sort_option_rating": "Xếp hạng",
"search_filters_sort_option_date": "ngày",
@ -341,5 +333,10 @@
"search_filters_features_option_location": "vị trí",
"search_filters_features_option_hdr": "hdr",
"Current version: ": "Phiên bản hiện tại: ",
"search_filters_title": "bộ lọc"
"search_filters_title": "bộ lọc",
"generic_playlists_count": "{{count}} danh sách phát",
"generic_views_count": "{{count}} lượt xem",
"View `x` comments": {
"": "Xem `x` bình luận"
}
}

@ -19,7 +19,6 @@
"Clear watch history?": "清除观看历史?",
"New password": "新密码",
"New passwords must match": "新密码必须匹配",
"Cannot change password for Google accounts": "无法为 Google 账户更改密码",
"Authorize token?": "授权令牌?",
"Authorize token for `x`?": "`x` 的授权令牌?",
"Yes": "是",
@ -42,7 +41,6 @@
"source": "source",
"Log in": "登录",
"Log in/register": "登录/注册",
"Log in with Google": "使用 Google 账户登录",
"User ID": "用户 ID",
"Password": "密码",
"Time (h:mm:ss):": "时间 (h:mm:ss):",
@ -51,7 +49,6 @@
"Sign In": "登录",
"Register": "注册",
"E-mail": "E-mail",
"Google verification code": "Google 验证代码",
"Preferences": "偏好设置",
"preferences_category_player": "播放器偏好设置",
"preferences_video_loop_label": "始终循环: ",
@ -171,17 +168,12 @@
"Hide replies": "隐藏回复",
"Show replies": "显示回复",
"Incorrect password": "密码错误",
"Quota exceeded, try again in a few hours": "已超出限额,请于几小时后重试",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "无法登录。请确认你的短信或验证器的二步验证已打开。",
"Invalid TFA code": "无效的二步验证码",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "登录失败。可能是因为二步验证未打开。",
"Wrong answer": "错误的回复",
"Erroneous CAPTCHA": "验证码错误",
"CAPTCHA is a required field": "验证码必填",
"User ID is a required field": "用户名必填",
"Password is a required field": "密码必填",
"Wrong username or password": "用户名或密码错误",
"Please sign in using 'Log in with Google'": "请通过谷歌账户登录",
"Password cannot be empty": "密码不能为空",
"Password cannot be longer than 55 characters": "密码长度不能大于 55",
"Please log in": "请登录",
@ -341,9 +333,9 @@
"`x` marked it with a ❤": "`x` 为此加 ❤",
"Audio mode": "音频模式",
"Video mode": "视频模式",
"Videos": "视频",
"channel_tab_videos_label": "视频",
"Playlists": "播放列表",
"Community": "社区",
"channel_tab_community_label": "社区",
"search_filters_sort_option_relevance": "相关度",
"search_filters_sort_option_rating": "评分",
"search_filters_sort_option_date": "上传日期",
@ -454,5 +446,19 @@
"search_message_change_filters_or_query": "尝试扩大你的搜索查询和/或更改过滤器。",
"search_filters_duration_option_none": "任意时长",
"search_filters_type_option_all": "任意类型",
"search_filters_features_option_vr180": "VR180"
"search_filters_features_option_vr180": "VR180",
"Popular enabled: ": "已启用流行度: ",
"error_video_not_in_playlist": "此播放列表中不存在请求的视频。 <a href=\"`x`\">单击析出查看播放列表主页。</a>",
"Music in this video": "此视频中的音乐",
"channel_tab_playlists_label": "播放列表",
"Artist: ": "艺术家: ",
"channel_tab_streams_label": "直播",
"Album: ": "专辑: ",
"channel_tab_shorts_label": "短视频",
"channel_tab_channels_label": "频道",
"Song: ": "歌曲: ",
"Channel Sponsor": "频道赞助者",
"Standard YouTube license": "标准 YouTube 许可证",
"Download is disabled": "已禁用下载",
"Import YouTube playlist (.csv)": "导入 YouTube 播放列表(.csv"
}

@ -19,7 +19,6 @@
"Clear watch history?": "清除觀看歷史?",
"New password": "新密碼",
"New passwords must match": "新密碼必須符合",
"Cannot change password for Google accounts": "無法變更 Google 帳號的密碼",
"Authorize token?": "授權 token",
"Authorize token for `x`?": "`x` 的授權 token",
"Yes": "是",
@ -42,7 +41,6 @@
"source": "來源",
"Log in": "登入",
"Log in/register": "登入/註冊",
"Log in with Google": "使用 Google 登入",
"User ID": "使用者 ID",
"Password": "密碼",
"Time (h:mm:ss):": "時間 (h:mm:ss):",
@ -51,7 +49,6 @@
"Sign In": "登入",
"Register": "註冊",
"E-mail": "電子郵件",
"Google verification code": "Google 驗證碼",
"Preferences": "偏好設定",
"preferences_category_player": "播放器偏好設定",
"preferences_video_loop_label": "總是循環播放: ",
@ -171,17 +168,12 @@
"Hide replies": "隱藏回覆",
"Show replies": "顯示回覆",
"Incorrect password": "不正確的密碼",
"Quota exceeded, try again in a few hours": "超過限額,請在幾個小時後再試一次",
"Unable to log in, make sure two-factor authentication (Authenticator or SMS) is turned on.": "無法登入,請確定雙因素驗證(驗證器或簡訊)已開啟。",
"Invalid TFA code": "無效的 TFA 代碼",
"Login failed. This may be because two-factor authentication is not turned on for your account.": "登入失敗。這可能是因為您的帳號未開啟雙因素驗證的關係。",
"Wrong answer": "錯誤的答案",
"Erroneous CAPTCHA": "錯誤的 CAPTCHA",
"CAPTCHA is a required field": "CAPTCHA 為必填欄位",
"User ID is a required field": "使用者 ID 為必填欄位",
"Password is a required field": "密碼為必填欄位",
"Wrong username or password": "錯誤的使用者名稱或密碼",
"Please sign in using 'Log in with Google'": "請使用「以 Google 登入」來登入",
"Password cannot be empty": "密碼不能為空",
"Password cannot be longer than 55 characters": "密碼不能長於55個字元",
"Please log in": "請登入",
@ -341,9 +333,9 @@
"`x` marked it with a ❤": "`x` 為此標記 ❤",
"Audio mode": "音訊模式",
"Video mode": "視訊模式",
"Videos": "影片",
"channel_tab_videos_label": "影片",
"Playlists": "播放清單",
"Community": "社群",
"channel_tab_community_label": "社群",
"search_filters_sort_option_relevance": "關聯",
"search_filters_sort_option_rating": "評分",
"search_filters_sort_option_date": "日期",
@ -454,5 +446,19 @@
"search_filters_title": "過濾條件",
"search_filters_date_label": "上傳日期",
"search_filters_type_option_all": "任何類型",
"search_filters_date_option_none": "任何日期"
"search_filters_date_option_none": "任何日期",
"Popular enabled: ": "已啟用人氣: ",
"error_video_not_in_playlist": "此播放清單不存在請求的影片。<a href=\"`x`\">點擊此處檢視播放清單首頁。</a>",
"channel_tab_shorts_label": "短片",
"channel_tab_playlists_label": "播放清單",
"channel_tab_channels_label": "頻道",
"channel_tab_streams_label": "直播",
"Artist: ": "藝術家: ",
"Album: ": "專輯: ",
"Music in this video": "此影片中的音樂",
"Channel Sponsor": "頻道贊助者",
"Song: ": "歌曲: ",
"Standard YouTube license": "標準 YouTube 授權條款",
"Download is disabled": "已停用下載",
"Import YouTube playlist (.csv)": "匯入 YouTube 播放清單 (.csv)"
}

@ -0,0 +1 @@
Subproject commit 11ec372f72747c09d48ffef04843f72be67d5b54

@ -0,0 +1,60 @@
#!/bin/sh
#
# Parameters
#
interactive=true
if [ "$1" = "--no-interactive" ]; then
interactive=false
fi
#
# Enable and start Postgres
#
sudo systemctl start postgresql.service
sudo systemctl enable postgresql.service
#
# Create databse and user
#
if [ "$interactive" = "true" ]; then
sudo -u postgres -- createuser -P kemal
sudo -u postgres -- createdb -O kemal invidious
else
# Generate a DB password
if [ -z "$POSTGRES_PASS" ]; then
echo "Generating database password"
POSTGRES_PASS=$(tr -dc 'A-Za-z0-9.;!?{[()]}\\/' < /dev/urandom | head -c16)
fi
# hostname:port:database:username:password
echo "Writing .pgpass"
echo "127.0.0.1:*:invidious:kemal:${POSTGRES_PASS}" > "$HOME/.pgpass"
sudo -u postgres -- psql -c "CREATE USER kemal WITH PASSWORD '$POSTGRES_PASS';"
sudo -u postgres -- psql -c "CREATE DATABASE invidious WITH OWNER kemal;"
sudo -u postgres -- psql -c "GRANT ALL ON DATABASE invidious TO kemal;"
fi
#
# Instructions for modification of pg_hba.conf
#
if [ "$interactive" = "true" ]; then
echo
echo "-------------"
echo " NOTICE "
echo "-------------"
echo
echo "Make sure that your postgreSQL's pg_hba.conf file contains the follwong"
echo "lines before previous 'host' configurations:"
echo
echo "host invidious kemal 127.0.0.1/32 md5"
echo "host invidious kemal ::1/128 md5"
echo
fi

@ -129,7 +129,7 @@ dependencies_to_install.each do |dep|
dep = "videojs.markers" if dep == "videojs-markers"
if File.exists?("#{download_path}/package/dist/#{dep}.css")
if minified && File.exists?("#{tmp_dir_path}/#{dep}/package/dist/#{dep}.min.css")
if minified && File.exists?("#{download_path}/package/dist/#{dep}.min.css")
`mv #{download_path}/package/dist/#{dep}.min.css #{dest_path}/#{dep}.css`
else
`mv #{download_path}/package/dist/#{dep}.css #{dest_path}/#{dep}.css`

@ -0,0 +1,174 @@
#!/bin/sh
#
# Script that installs the various dependencies of invidious
#
# Dependencies:
# - crystal => Language in which Invidious is developed
# - postgres => Database server
# - git => required to clone Invidious
# - librsvg2-bin => For login captcha (provides 'rsvg-convert')
#
# - libssl-dev => Used by Crystal's SSL module (standard library)
# - libxml2-dev => Used by Crystal's XML module (standard library)
# - libyaml-dev => Used by Crystal's YAML module (standard library)
# - libgmp-dev => Used by Crystal's BigNumbers module (standard library)
# - libevent-dev => Used by crystal's internal scheduler (?)
# - libpcre3-dev => Used by Crystal's regex engine (?)
#
# - libsqlite3-dev => Used to open .db files from NewPipe exports
# - zlib1g-dev => TBD
# - libreadline-dev => TBD
#
#
# Tested on:
# - OpenSUSE Leap 15.3
#
# Load system details
#
if [ -e /etc/os-release ]; then
. /etc/os-release
elif [ -e /usr/lib/os-release ]; then
. /usr/lib/os-release
else
echo "Unsupported Linux system"
exit 2
fi
#
# Some variables
#
repo_base_url="https://download.opensuse.org/repositories/devel:/languages:/crystal/"
repo_end_url="devel:languages:crystal.repo"
apt_gpg_key="/usr/share/keyrings/crystal.gpg"
apt_list_file="/etc/apt/sources.list.d/crystal.list"
yum_repo_file="/etc/yum.repos.d/crystal.repo"
#
# Major install functions
#
make_repo_url() {
echo "${repo_base_url}/${1}/${repo_end_url}"
}
install_apt() {
repo="$1"
echo "Adding Crystal repository"
curl -fsSL "${repo_base_url}/${repo}/Release.key" \
| gpg --dearmor \
| sudo tee "${apt_gpg_key}" > /dev/null
echo "deb [signed-by=${apt_gpg_key}] ${repo_base_url}/${repo}/ /" \
| sudo tee "$apt_list_file"
sudo apt-get update
sudo apt-get install --yes --no-install-recommends \
libssl-dev libxml2-dev libyaml-dev libgmp-dev libevent-dev \
libpcre3-dev libreadline-dev libsqlite3-dev zlib1g-dev \
crystal postgresql-13 git librsvg2-bin make
}
install_yum() {
repo=$(make_repo_url "$1")
echo "Adding Crystal repository"
cat << END | sudo tee "${yum_repo_file}" > /dev/null
[crystal]
name=Crystal
type=rpm-md
baseurl=${repo}/
gpgcheck=1
gpgkey=${repo}/repodata/repomd.xml.key
enabled=1
END
sudo yum -y install \
openssl-devel libxml2-devel libyaml-devel gmp-devel \
readline-devel sqlite-devel \
crystal postgresql postgresql-server git librsvg2-tools make
}
install_pacman() {
# TODO: find an alternative to --no-confirm?
sudo pacman -S --no-confirm \
base-devel librsvg postgresql crystal
}
install_zypper()
{
repo=$(make_repo_url "$1")
echo "Adding Crystal repository"
sudo zypper --non-interactive addrepo -f "$repo"
sudo zypper --non-interactive --gpg-auto-import-keys install --no-recommends \
libopenssl-devel libxml2-devel libyaml-devel gmp-devel libevent-devel \
pcre-devel readline-devel sqlite3-devel zlib-devel \
crystal postgresql postgresql-server git rsvg-convert make
}
#
# System-specific logic
#
case "$ID" in
archlinux) install_pacman;;
centos) install_dnf "CentOS_${VERSION_ID}";;
debian)
case "$VERSION_CODENAME" in
sid) install_apt "Debian_Unstable";;
bookworm) install_apt "Debian_Testing";;
*) install_apt "Debian_${VERSION_ID}";;
esac
;;
fedora)
if [ "$VERSION" == *"Prerelease"* ]; then
install_dnf "Fedora_Rawhide"
else
install_dnf "Fedora_${VERSION}"
fi
;;
opensuse-leap) install_zypper "openSUSE_Leap_${VERSION}";;
opensuse-tumbleweed) install_zypper "openSUSE_Tumbleweed";;
rhel) install_dnf "RHEL_${VERSION_ID}";;
ubuntu)
# Small workaround for recently released 22.04
case "$VERSION_ID" in
22.04) install_apt "xUbuntu_21.04";;
*) install_apt "xUbuntu_${VERSION_ID}";;
esac
;;
*)
# Try to match on ID_LIKE instead
# Not guaranteed to 100% work
case "$ID_LIKE" in
archlinux) install_pacman;;
centos) install_dnf "CentOS_${VERSION_ID}";;
debian) install_apt "Debian_${VERSION_ID}";;
*)
echo "Error: distribution ${CODENAME} is not supported"
echo "Please install dependencies manually"
exit 2
;;
esac
;;
esac

@ -34,7 +34,7 @@ shards:
protodec:
git: https://github.com/iv-org/protodec.git
version: 0.1.4
version: 0.1.5
radix:
git: https://github.com/luislavena/radix.git

@ -24,7 +24,7 @@ dependencies:
version: ~> 0.6.1
protodec:
github: iv-org/protodec
version: ~> 0.1.4
version: ~> 0.1.5
lsquic:
github: iv-org/lsquic.cr
version: ~> 2.18.1-2

@ -0,0 +1,109 @@
require "../parsers_helper.cr"
Spectator.describe Invidious::Hashtag do
it "parses richItemRenderer containers (test 1)" do
# Enable mock
test_content = load_mock("hashtag/martingarrix_page1")
videos, _ = extract_items(test_content)
expect(typeof(videos)).to eq(Array(SearchItem))
expect(videos.size).to eq(60)
#
# Random video check 1
#
expect(typeof(videos[11])).to eq(SearchItem)
video_11 = videos[11].as(SearchVideo)
expect(video_11.id).to eq("06eSsOWcKYA")
expect(video_11.title).to eq("Martin Garrix - Live @ Tomorrowland 2018")
expect(video_11.ucid).to eq("UC5H_KXkPbEsGs0tFt8R35mA")
expect(video_11.author).to eq("Martin Garrix")
expect(video_11.author_verified).to be_true
expect(video_11.published).to be_close(Time.utc - 3.years, 1.second)
expect(video_11.length_seconds).to eq((56.minutes + 41.seconds).total_seconds.to_i32)
expect(video_11.views).to eq(40_504_893)
expect(video_11.live_now).to be_false
expect(video_11.premium).to be_false
expect(video_11.premiere_timestamp).to be_nil
#
# Random video check 2
#
expect(typeof(videos[35])).to eq(SearchItem)
video_35 = videos[35].as(SearchVideo)
expect(video_35.id).to eq("b9HpOAYjY9I")
expect(video_35.title).to eq("Martin Garrix feat. Mike Yung - Dreamer (Official Video)")
expect(video_35.ucid).to eq("UC5H_KXkPbEsGs0tFt8R35mA")
expect(video_35.author).to eq("Martin Garrix")
expect(video_35.author_verified).to be_true
expect(video_35.published).to be_close(Time.utc - 3.years, 1.second)
expect(video_35.length_seconds).to eq((3.minutes + 14.seconds).total_seconds.to_i32)
expect(video_35.views).to eq(30_790_049)
expect(video_35.live_now).to be_false
expect(video_35.premium).to be_false
expect(video_35.premiere_timestamp).to be_nil
end
it "parses richItemRenderer containers (test 2)" do
# Enable mock
test_content = load_mock("hashtag/martingarrix_page2")
videos, _ = extract_items(test_content)
expect(typeof(videos)).to eq(Array(SearchItem))
expect(videos.size).to eq(60)
#
# Random video check 1
#
expect(typeof(videos[41])).to eq(SearchItem)
video_41 = videos[41].as(SearchVideo)
expect(video_41.id).to eq("qhstH17zAjs")
expect(video_41.title).to eq("Martin Garrix Radio - Episode 391")
expect(video_41.ucid).to eq("UC5H_KXkPbEsGs0tFt8R35mA")
expect(video_41.author).to eq("Martin Garrix")
expect(video_41.author_verified).to be_true
expect(video_41.published).to be_close(Time.utc - 2.months, 1.second)
expect(video_41.length_seconds).to eq((1.hour).total_seconds.to_i32)
expect(video_41.views).to eq(63_240)
expect(video_41.live_now).to be_false
expect(video_41.premium).to be_false
expect(video_41.premiere_timestamp).to be_nil
#
# Random video check 2
#
expect(typeof(videos[48])).to eq(SearchItem)
video_48 = videos[48].as(SearchVideo)
expect(video_48.id).to eq("lqGvW0NIfdc")
expect(video_48.title).to eq("Martin Garrix SENTIO Full Album Mix by Sakul")
expect(video_48.ucid).to eq("UC3833PXeLTS6yRpwGMQpp4Q")
expect(video_48.author).to eq("SAKUL")
expect(video_48.author_verified).to be_false
expect(video_48.published).to be_close(Time.utc - 3.weeks, 1.second)
expect(video_48.length_seconds).to eq((35.minutes + 46.seconds).total_seconds.to_i32)
expect(video_48.views).to eq(68_704)
expect(video_48.live_now).to be_false
expect(video_48.premium).to be_false
expect(video_48.premiere_timestamp).to be_nil
end
end

@ -5,13 +5,13 @@ CONFIG = Config.from_yaml(File.open("config/config.example.yml"))
Spectator.describe "Helper" do
describe "#produce_channel_videos_url" do
it "correctly produces url for requesting page `x` of a channel's videos" do
expect(produce_channel_videos_url(ucid: "UCXuqSBlHAE6Xw-yeJA0Tunw")).to eq("/browse_ajax?continuation=4qmFsgI8EhhVQ1h1cVNCbEhBRTZYdy15ZUpBMFR1bncaIEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0V4&gl=US&hl=en")
# expect(produce_channel_videos_url(ucid: "UCXuqSBlHAE6Xw-yeJA0Tunw")).to eq("/browse_ajax?continuation=4qmFsgI8EhhVQ1h1cVNCbEhBRTZYdy15ZUpBMFR1bncaIEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0V4&gl=US&hl=en")
#
# expect(produce_channel_videos_url(ucid: "UCXuqSBlHAE6Xw-yeJA0Tunw", sort_by: "popular")).to eq("/browse_ajax?continuation=4qmFsgJAEhhVQ1h1cVNCbEhBRTZYdy15ZUpBMFR1bncaJEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0V4R0FFPQ%3D%3D&gl=US&hl=en")
expect(produce_channel_videos_url(ucid: "UCXuqSBlHAE6Xw-yeJA0Tunw", sort_by: "popular")).to eq("/browse_ajax?continuation=4qmFsgJAEhhVQ1h1cVNCbEhBRTZYdy15ZUpBMFR1bncaJEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0V4R0FFPQ%3D%3D&gl=US&hl=en")
# expect(produce_channel_videos_url(ucid: "UCXuqSBlHAE6Xw-yeJA0Tunw", page: 20)).to eq("/browse_ajax?continuation=4qmFsgJAEhhVQ1h1cVNCbEhBRTZYdy15ZUpBMFR1bncaJEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0l5TUE9PQ%3D%3D&gl=US&hl=en")
expect(produce_channel_videos_url(ucid: "UCXuqSBlHAE6Xw-yeJA0Tunw", page: 20)).to eq("/browse_ajax?continuation=4qmFsgJAEhhVQ1h1cVNCbEhBRTZYdy15ZUpBMFR1bncaJEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0l5TUE9PQ%3D%3D&gl=US&hl=en")
expect(produce_channel_videos_url(ucid: "UC-9-kyTW8ZkZNDHQJ6FgpwQ", page: 20, sort_by: "popular")).to eq("/browse_ajax?continuation=4qmFsgJAEhhVQy05LWt5VFc4WmtaTkRIUUo2Rmdwd1EaJEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0l5TUJnQg%3D%3D&gl=US&hl=en")
# expect(produce_channel_videos_url(ucid: "UC-9-kyTW8ZkZNDHQJ6FgpwQ", page: 20, sort_by: "popular")).to eq("/browse_ajax?continuation=4qmFsgJAEhhVQy05LWt5VFc4WmtaTkRIUUo2Rmdwd1EaJEVnWjJhV1JsYjNNd0FqZ0JZQUZxQUxnQkFDQUFlZ0l5TUJnQg%3D%3D&gl=US&hl=en")
end
end
@ -23,24 +23,6 @@ Spectator.describe "Helper" do
end
end
describe "#produce_channel_playlists_url" do
it "correctly produces a /browse_ajax URL with the given UCID and cursor" do
expect(produce_channel_playlists_url("UCCj956IF62FbT7Gouszaj9w", "AIOkY9EQpi_gyn1_QrFuZ1reN81_MMmI1YmlBblw8j7JHItEFG5h7qcJTNd4W9x5Quk_CVZ028gW")).to eq("/browse_ajax?continuation=4qmFsgLNARIYVUNDajk1NklGNjJGYlQ3R291c3phajl3GrABRWdsd2JHRjViR2x6ZEhNd0FqZ0JZQUZxQUxnQkFIcG1VVlZzVUdFeGF6VlNWa1ozWVZZNWJtVlhOSGhZTVVaNVVtNVdZVTFZU214VWFtZDRXREF4VG1KVmEzaFhWekZ6VVcxS2MyUjZhSEZPTUhCSlUxaFNSbEpyWXpGaFJHUjRXVEJ3VlZSdFVUQldlbXcwVGxaR01XRXhPVVJXYkc5M1RXcG9ibFozSUFFWUF3PT0%3D&gl=US&hl=en")
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")
expect(produce_comment_continuation("_cE8xSu6swE", "ADSJ_i1yz21HI4xrtsYXVC-2_kfZ6kx1yjYQumXAAxqH3CAd7ZxKxfLdZS1__fqhCtOASRbbpSBGH_tH1J96Dxux-Qfjk-lUbupMqv08Q3aHzGu7p70VoUMHhI2-GoJpnbpmcOxkGzeIuenRS_ym2Y8fkDowhqLPFgsS0n4djnZ2UmC17F3Ch3N1S1UYf1ZVOc991qOC1iW9kJDzyvRQTWCPsJUPneSaAKW-Rr97pdesOkR4i8cNvHZRnQKe2HEfsvlJOb2C3lF1dJBfJeNfnQYeh5hv6_fZN7bt3-JL1Xk3Qc9NXNxmmbDpwAC_yFR8dthFfUJdyIO9Nu1D79MLYeR-H5HxqUJokkJiGIz4lTE_CXXbhAI")).to eq("EkMSC19jRTh4U3U2c3dFyAEA4AEBogINKP___________wFAAMICHQgEGhdodHRwczovL3d3dy55b3V0dWJlLmNvbSIAGAYyiQMK8wJBRFNKX2kxeXoyMUhJNHhydHNZWFZDLTJfa2ZaNmt4MXlqWVF1bVhBQXhxSDNDQWQ3WnhLeGZMZFpTMV9fZnFoQ3RPQVNSYmJwU0JHSF90SDFKOTZEeHV4LVFmamstbFVidXBNcXYwOFEzYUh6R3U3cDcwVm9VTUhoSTItR29KcG5icG1jT3hrR3plSXVlblJTX3ltMlk4ZmtEb3docUxQRmdzUzBuNGRqbloyVW1DMTdGM0NoM04xUzFVWWYxWlZPYzk5MXFPQzFpVzlrSkR6eXZSUVRXQ1BzSlVQbmVTYUFLVy1Scjk3cGRlc09rUjRpOGNOdkhaUm5RS2UySEVmc3ZsSk9iMkMzbEYxZEpCZkplTmZuUVllaDVodjZfZlpON2J0My1KTDFYazNRYzlOWE54bW1iRHB3QUNfeUZSOGR0aEZmVUpkeUlPOU51MUQ3OU1MWWVSLUg1SHhxVUpva2tKaUdJejRsVEVfQ1hYYmhBSSIPIgtfY0U4eFN1NnN3RTAAKBQ%3D")
expect(produce_comment_continuation("29-q7YnyUmY", "")).to eq("EkMSCzI5LXE3WW55VW1ZyAEA4AEBogINKP___________wFAAMICHQgEGhdodHRwczovL3d3dy55b3V0dWJlLmNvbSIAGAYyFQoAIg8iCzI5LXE3WW55VW1ZMAAoFA%3D%3D")
expect(produce_comment_continuation("CvFH_6DNRCY", "")).to eq("EkMSC0N2RkhfNkROUkNZyAEA4AEBogINKP___________wFAAMICHQgEGhdodHRwczovL3d3dy55b3V0dWJlLmNvbSIAGAYyFQoAIg8iC0N2RkhfNkROUkNZMAAoFA%3D%3D")
end
end
describe "#produce_channel_community_continuation" do
it "correctly produces a continuation token for a channel community" do
expect(produce_channel_community_continuation("UCCj956IF62FbT7Gouszaj9w", "Egljb21tdW5pdHm4")).to eq("4qmFsgIsEhhVQ0NqOTU2SUY2MkZiVDdHb3VzemFqOXcaEEVnbGpiMjF0ZFc1cGRIbTQ%3D")

@ -197,4 +197,46 @@ Spectator.describe Invidious::Search::Query do
)
end
end
describe "#to_http_params" do
it "formats regular search" do
query = described_class.new(
HTTP::Params.parse("q=The+Simpsons+hiding+in+bush&duration=short"),
Invidious::Search::Query::Type::Regular, nil
)
params = query.to_http_params
expect(params).to have_key("duration")
expect(params["duration"]?).to eq("short")
expect(params).to have_key("q")
expect(params["q"]?).to eq("The Simpsons hiding in bush")
# Check if there aren't other parameters
params.delete("duration")
params.delete("q")
expect(params).to be_empty
end
it "formats channel search" do
query = described_class.new(
HTTP::Params.parse("q=channel:UC2DjFE7Xf11URZqWBigcVOQ%20multimeter"),
Invidious::Search::Query::Type::Regular, nil
)
params = query.to_http_params
expect(params).to have_key("channel")
expect(params["channel"]?).to eq("UC2DjFE7Xf11URZqWBigcVOQ")
expect(params).to have_key("q")
expect(params["q"]?).to eq("multimeter")
# Check if there aren't other parameters
params.delete("channel")
params.delete("q")
expect(params).to be_empty
end
end
end

@ -0,0 +1,46 @@
require "../spec_helper"
Spectator.describe "Utils" do
describe "decode_date" do
it "parses short dates (en-US)" do
expect(decode_date("1s ago")).to be_close(Time.utc - 1.second, 500.milliseconds)
expect(decode_date("2min ago")).to be_close(Time.utc - 2.minutes, 500.milliseconds)
expect(decode_date("3h ago")).to be_close(Time.utc - 3.hours, 500.milliseconds)
expect(decode_date("4d ago")).to be_close(Time.utc - 4.days, 500.milliseconds)
expect(decode_date("5w ago")).to be_close(Time.utc - 5.weeks, 500.milliseconds)
expect(decode_date("6mo ago")).to be_close(Time.utc - 6.months, 500.milliseconds)
expect(decode_date("7y ago")).to be_close(Time.utc - 7.years, 500.milliseconds)
end
it "parses short dates (en-GB)" do
expect(decode_date("55s ago")).to be_close(Time.utc - 55.seconds, 500.milliseconds)
expect(decode_date("44min ago")).to be_close(Time.utc - 44.minutes, 500.milliseconds)
expect(decode_date("22hr ago")).to be_close(Time.utc - 22.hours, 500.milliseconds)
expect(decode_date("1day ago")).to be_close(Time.utc - 1.day, 500.milliseconds)
expect(decode_date("2days ago")).to be_close(Time.utc - 2.days, 500.milliseconds)
expect(decode_date("3wk ago")).to be_close(Time.utc - 3.weeks, 500.milliseconds)
expect(decode_date("11mo ago")).to be_close(Time.utc - 11.months, 500.milliseconds)
expect(decode_date("11yr ago")).to be_close(Time.utc - 11.years, 500.milliseconds)
end
it "parses long forms (singular)" do
expect(decode_date("1 second ago")).to be_close(Time.utc - 1.second, 500.milliseconds)
expect(decode_date("1 minute ago")).to be_close(Time.utc - 1.minute, 500.milliseconds)
expect(decode_date("1 hour ago")).to be_close(Time.utc - 1.hour, 500.milliseconds)
expect(decode_date("1 day ago")).to be_close(Time.utc - 1.day, 500.milliseconds)
expect(decode_date("1 week ago")).to be_close(Time.utc - 1.week, 500.milliseconds)
expect(decode_date("1 month ago")).to be_close(Time.utc - 1.month, 500.milliseconds)
expect(decode_date("1 year ago")).to be_close(Time.utc - 1.year, 500.milliseconds)
end
it "parses long forms (plural)" do
expect(decode_date("5 seconds ago")).to be_close(Time.utc - 5.seconds, 500.milliseconds)
expect(decode_date("17 minutes ago")).to be_close(Time.utc - 17.minutes, 500.milliseconds)
expect(decode_date("23 hours ago")).to be_close(Time.utc - 23.hours, 500.milliseconds)
expect(decode_date("3 days ago")).to be_close(Time.utc - 3.days, 500.milliseconds)
expect(decode_date("2 weeks ago")).to be_close(Time.utc - 2.weeks, 500.milliseconds)
expect(decode_date("9 months ago")).to be_close(Time.utc - 9.months, 500.milliseconds)
expect(decode_date("8 years ago")).to be_close(Time.utc - 8.years, 500.milliseconds)
end
end
end

@ -0,0 +1,166 @@
require "../../parsers_helper.cr"
Spectator.describe "parse_video_info" do
it "parses a regular video" do
# Enable mock
_player = load_mock("video/regular_mrbeast.player")
_next = load_mock("video/regular_mrbeast.next")
raw_data = _player.merge!(_next)
info = parse_video_info("2isYuQZMbdU", raw_data)
# Some basic verifications
expect(typeof(info)).to eq(Hash(String, JSON::Any))
expect(info["videoType"].as_s).to eq("Video")
# Basic video infos
expect(info["title"].as_s).to eq("I Gave My 100,000,000th Subscriber An Island")
expect(info["views"].as_i).to eq(126_573_823)
expect(info["likes"].as_i).to eq(5_157_654)
# For some reason the video length from VideoDetails and the
# one from microformat differs by 1s...
expect(info["lengthSeconds"].as_i).to be_between(930_i64, 931_i64)
expect(info["published"].as_s).to eq("2022-08-04T00:00:00Z")
# Extra video infos
expect(info["allowedRegions"].as_a).to_not be_empty
expect(info["allowedRegions"].as_a.size).to eq(249)
expect(info["allowedRegions"].as_a).to contain(
"AD", "BA", "BB", "BW", "BY", "EG", "GG", "HN", "NP", "NR", "TR",
"TT", "TV", "TW", "TZ", "VA", "VC", "VE", "VG", "VI", "VN", "VU",
"WF", "WS", "YE", "YT", "ZA", "ZM", "ZW"
)
expect(info["keywords"].as_a).to be_empty
expect(info["allowRatings"].as_bool).to be_true
expect(info["isFamilyFriendly"].as_bool).to be_true
expect(info["isListed"].as_bool).to be_true
expect(info["isUpcoming"].as_bool).to be_false
# Related videos
expect(info["relatedVideos"].as_a.size).to eq(20)
expect(info["relatedVideos"][0]["id"]).to eq("Hwybp38GnZw")
expect(info["relatedVideos"][0]["title"]).to eq("I Built Willy Wonka's Chocolate Factory!")
expect(info["relatedVideos"][0]["author"]).to eq("MrBeast")
expect(info["relatedVideos"][0]["ucid"]).to eq("UCX6OQ3DkcsbYNE6H8uQQuVA")
expect(info["relatedVideos"][0]["view_count"]).to eq("179877630")
expect(info["relatedVideos"][0]["short_view_count"]).to eq("179M")
expect(info["relatedVideos"][0]["author_verified"]).to eq("true")
# Description
description = "🚀Launch a store on Shopify, Ill buy from 100 random stores that do ▸ "
expect(info["description"].as_s).to start_with(description)
expect(info["shortDescription"].as_s).to start_with(description)
expect(info["descriptionHtml"].as_s).to start_with(description)
# Video metadata
expect(info["genre"].as_s).to eq("Entertainment")
expect(info["genreUcid"].as_s).to be_empty
expect(info["license"].as_s).to be_empty
# Author infos
expect(info["author"].as_s).to eq("MrBeast")
expect(info["ucid"].as_s).to eq("UCX6OQ3DkcsbYNE6H8uQQuVA")
expect(info["authorThumbnail"].as_s).to eq(
"https://yt3.ggpht.com/ytc/AL5GRJVuqw82ERvHzsmBxL7avr1dpBtsVIXcEzBPZaloFg=s48-c-k-c0x00ffffff-no-rj"
)
expect(info["authorVerified"].as_bool).to be_true
expect(info["subCountText"].as_s).to eq("143M")
end
it "parses a regular video with no descrition/comments" do
# Enable mock
_player = load_mock("video/regular_no-description.player")
_next = load_mock("video/regular_no-description.next")
raw_data = _player.merge!(_next)
info = parse_video_info("iuevw6218F0", raw_data)
# Some basic verifications
expect(typeof(info)).to eq(Hash(String, JSON::Any))
expect(info["videoType"].as_s).to eq("Video")
# Basic video infos
expect(info["title"].as_s).to eq("Chris Rea - Auberge")
expect(info["views"].as_i).to eq(10_943_126)
expect(info["likes"].as_i).to eq(0)
expect(info["lengthSeconds"].as_i).to eq(283_i64)
expect(info["published"].as_s).to eq("2012-05-21T00:00:00Z")
# Extra video infos
expect(info["allowedRegions"].as_a).to_not be_empty
expect(info["allowedRegions"].as_a.size).to eq(249)
expect(info["allowedRegions"].as_a).to contain(
"AD", "BA", "BB", "BW", "BY", "EG", "GG", "HN", "NP", "NR", "TR",
"TT", "TV", "TW", "TZ", "VA", "VC", "VE", "VG", "VI", "VN", "VU",
"WF", "WS", "YE", "YT", "ZA", "ZM", "ZW"
)
expect(info["keywords"].as_a).to_not be_empty
expect(info["keywords"].as_a.size).to eq(4)
expect(info["keywords"].as_a).to contain_exactly(
"Chris",
"Rea",
"Auberge",
"1991"
).in_any_order
expect(info["allowRatings"].as_bool).to be_true
expect(info["isFamilyFriendly"].as_bool).to be_true
expect(info["isListed"].as_bool).to be_true
expect(info["isUpcoming"].as_bool).to be_false
# Related videos
expect(info["relatedVideos"].as_a.size).to eq(19)
expect(info["relatedVideos"][0]["id"]).to eq("Ww3KeZ2_Yv4")
expect(info["relatedVideos"][0]["title"]).to eq("Chris Rea")
expect(info["relatedVideos"][0]["author"]).to eq("PanMusic")
expect(info["relatedVideos"][0]["ucid"]).to eq("UCsKAPSuh1iNbLWUga_igPyA")
expect(info["relatedVideos"][0]["view_count"]).to eq("31581")
expect(info["relatedVideos"][0]["short_view_count"]).to eq("31K")
expect(info["relatedVideos"][0]["author_verified"]).to eq("false")
# Description
expect(info["description"].as_s).to eq(" ")
expect(info["shortDescription"].as_s).to be_empty
expect(info["descriptionHtml"].as_s).to eq("")
# Video metadata
expect(info["genre"].as_s).to eq("Music")
expect(info["genreUcid"].as_s).to be_empty
expect(info["license"].as_s).to be_empty
# Author infos
expect(info["author"].as_s).to eq("ChrisReaOfficial")
expect(info["ucid"].as_s).to eq("UC_5q6nWPbD30-y6oiWF_oNA")
expect(info["authorThumbnail"].as_s).to be_empty
expect(info["authorVerified"].as_bool).to be_false
expect(info["subCountText"].as_s).to eq("-")
end
end

@ -0,0 +1,111 @@
require "../../parsers_helper.cr"
Spectator.describe "parse_video_info" do
it "parses scheduled livestreams data" do
# Enable mock
_player = load_mock("video/scheduled_live_PBD-Podcast.player")
_next = load_mock("video/scheduled_live_PBD-Podcast.next")
raw_data = _player.merge!(_next)
info = parse_video_info("N-yVic7BbY0", raw_data)
# Some basic verifications
expect(typeof(info)).to eq(Hash(String, JSON::Any))
expect(info["videoType"].as_s).to eq("Scheduled")
# Basic video infos
expect(info["title"].as_s).to eq("Home Team | PBD Podcast | Ep. 241")
expect(info["views"].as_i).to eq(6)
expect(info["likes"].as_i).to eq(7)
expect(info["lengthSeconds"].as_i).to eq(0_i64)
expect(info["published"].as_s).to eq("2023-02-28T14:00:00Z") # Unix 1677592800
# Extra video infos
expect(info["allowedRegions"].as_a).to_not be_empty
expect(info["allowedRegions"].as_a.size).to eq(249)
expect(info["allowedRegions"].as_a).to contain(
"AD", "AR", "BA", "BT", "CZ", "FO", "GL", "IO", "KE", "KH", "LS",
"LT", "MP", "NO", "PR", "RO", "SE", "SK", "SS", "SX", "SZ", "ZW"
)
expect(info["keywords"].as_a).to_not be_empty
expect(info["keywords"].as_a.size).to eq(25)
expect(info["keywords"].as_a).to contain_exactly(
"Patrick Bet-David",
"Valeutainment",
"The BetDavid Podcast",
"The BetDavid Show",
"Betdavid",
"PBD",
"BetDavid show",
"Betdavid podcast",
"podcast betdavid",
"podcast patrick",
"patrick bet david podcast",
"Valuetainment podcast",
"Entrepreneurs",
"Entrepreneurship",
"Entrepreneur Motivation",
"Entrepreneur Advice",
"Startup Entrepreneurs",
"valuetainment",
"patrick bet david",
"PBD podcast",
"Betdavid show",
"Betdavid Podcast",
"Podcast Betdavid",
"Show Betdavid",
"PBDPodcast"
).in_any_order
expect(info["allowRatings"].as_bool).to be_true
expect(info["isFamilyFriendly"].as_bool).to be_true
expect(info["isListed"].as_bool).to be_true
expect(info["isUpcoming"].as_bool).to be_true
# Related videos
expect(info["relatedVideos"].as_a.size).to eq(20)
expect(info["relatedVideos"][0]["id"]).to eq("j7jPzzjbVuk")
expect(info["relatedVideos"][0]["author"]).to eq("Democracy Now!")
expect(info["relatedVideos"][0]["ucid"]).to eq("UCzuqE7-t13O4NIDYJfakrhw")
expect(info["relatedVideos"][0]["view_count"]).to eq("7576")
expect(info["relatedVideos"][0]["short_view_count"]).to eq("7.5K")
expect(info["relatedVideos"][0]["author_verified"]).to eq("true")
# Description
description_start_text = "PBD Podcast Episode 241. The home team is ready and at it again with the latest news, interesting topics and trending conversations on topics that matter. Try our sponsor Aura for 14 days free - https://aura.com/pbd"
expect(info["description"].as_s).to start_with(description_start_text)
expect(info["shortDescription"].as_s).to start_with(description_start_text)
# TODO: Update mocks right before the start of PDB podcast, either on friday or saturday (time unknown)
# expect(info["descriptionHtml"].as_s).to start_with(
# "PBD Podcast Episode 241. The home team is ready and at it again with the latest news, interesting topics and trending conversations on topics that matter. Try our sponsor Aura for 14 days free - <a href=\"https://aura.com/pbd\">aura.com/pbd</a>"
# )
# Video metadata
expect(info["genre"].as_s).to eq("Entertainment")
expect(info["genreUcid"].as_s).to be_empty
expect(info["license"].as_s).to be_empty
# Author infos
expect(info["author"].as_s).to eq("PBD Podcast")
expect(info["ucid"].as_s).to eq("UCGX7nGXpz-CmO_Arg-cgJ7A")
expect(info["authorThumbnail"].as_s).to eq(
"https://yt3.ggpht.com/61ArDiQshJrvSXcGLhpFfIO3hlMabe2fksitcf6oGob0Mdr5gztdkXxRljICUodL4iuTSrtxW4A=s48-c-k-c0x00ffffff-no-rj"
)
expect(info["authorVerified"].as_bool).to be_false
expect(info["subCountText"].as_s).to eq("594K")
end
end

@ -0,0 +1,35 @@
require "db"
require "json"
require "kemal"
require "protodec/utils"
require "spectator"
require "../src/invidious/exceptions"
require "../src/invidious/helpers/macros"
require "../src/invidious/helpers/logger"
require "../src/invidious/helpers/utils"
require "../src/invidious/videos"
require "../src/invidious/videos/*"
require "../src/invidious/comments/content"
require "../src/invidious/helpers/serialized_yt_data"
require "../src/invidious/yt_backend/extractors"
require "../src/invidious/yt_backend/extractors_utils"
OUTPUT = File.open(File::NULL, "w")
LOGGER = Invidious::LogHandler.new(OUTPUT, LogLevel::Off)
def load_mock(file) : Hash(String, JSON::Any)
file = File.join(__DIR__, "..", "mocks", file + ".json")
content = File.read(file)
return JSON.parse(content).as_h
end
Spectator.configure do |config|
config.fail_blank
config.randomize
end

@ -5,8 +5,8 @@ require "protodec/utils"
require "yaml"
require "../src/invidious/helpers/*"
require "../src/invidious/channels/*"
require "../src/invidious/videos/caption"
require "../src/invidious/videos"
require "../src/invidious/comments"
require "../src/invidious/playlists"
require "../src/invidious/search/ctoken"
require "../src/invidious/trending"

@ -111,7 +111,7 @@ module Kemal
if @fallthrough
call_next(context)
else
context.response.status_code = 405
context.response.status = HTTP::Status::METHOD_NOT_ALLOWED
context.response.headers.add("Allow", "GET, HEAD")
end
return
@ -124,7 +124,7 @@ module Kemal
# File path cannot contains '\0' (NUL) because all filesystem I know
# don't accept '\0' character as file name.
if request_path.includes? '\0'
context.response.status_code = 400
context.response.status = HTTP::Status::BAD_REQUEST
return
end
@ -143,13 +143,15 @@ module Kemal
add_cache_headers(context.response.headers, last_modified)
if cache_request?(context, last_modified)
context.response.status_code = 304
context.response.status = HTTP::Status::NOT_MODIFIED
return
end
send_file(context, file_path, file[:data], file[:filestat])
else
is_dir = Dir.exists? file_path
file_info = File.info?(file_path)
is_dir = file_info.try &.directory? || false
is_file = file_info.try &.file? || false
if request_path != expanded_path
redirect_to context, expanded_path
@ -157,19 +159,21 @@ module Kemal
redirect_to context, expanded_path + '/'
end
if Dir.exists?(file_path)
return call_next(context) if file_info.nil?
if is_dir
if config.is_a?(Hash) && config["dir_listing"] == true
context.response.content_type = "text/html"
directory_listing(context.response, request_path, file_path)
else
call_next(context)
end
elsif File.exists?(file_path)
last_modified = modification_time(file_path)
elsif is_file
last_modified = file_info.modification_time
add_cache_headers(context.response.headers, last_modified)
if cache_request?(context, last_modified)
context.response.status_code = 304
context.response.status = HTTP::Status::NOT_MODIFIED
return
end
@ -177,14 +181,12 @@ module Kemal
data = Bytes.new(size)
File.open(file_path, &.read(data))
filestat = File.info(file_path)
@cached_files[file_path] = {data: data, filestat: filestat}
send_file(context, file_path, data, filestat)
@cached_files[file_path] = {data: data, filestat: file_info}
send_file(context, file_path, data, file_info)
else
send_file(context, file_path)
end
else
else # Not a normal file (FIFO/device/socket)
call_next(context)
end
end

@ -34,23 +34,35 @@ require "protodec/utils"
require "./invidious/database/*"
require "./invidious/database/migrations/*"
require "./invidious/http_server/*"
require "./invidious/helpers/*"
require "./invidious/yt_backend/*"
require "./invidious/frontend/*"
require "./invidious/videos/*"
require "./invidious/jsonify/**"
require "./invidious/*"
require "./invidious/comments/*"
require "./invidious/channels/*"
require "./invidious/user/*"
require "./invidious/search/*"
require "./invidious/routes/**"
require "./invidious/jobs/**"
# Declare the base namespace for invidious
module Invidious
end
# Simple alias to make code easier to read
alias IV = Invidious
CONFIG = Config.load
HMAC_KEY_CONFIGURED = CONFIG.hmac_key != nil
HMAC_KEY = CONFIG.hmac_key || Random::Secure.hex(32)
PG_DB = DB.open CONFIG.database_url
ARCHIVE_URL = URI.parse("https://archive.org")
LOGIN_URL = URI.parse("https://accounts.google.com")
PUBSUB_URL = URI.parse("https://pubsubhubbub.appspot.com")
REDDIT_URL = URI.parse("https://www.reddit.com")
YT_URL = URI.parse("https://www.youtube.com")
@ -133,12 +145,13 @@ Invidious::Database.check_integrity(CONFIG)
# Running the script by itself would show some colorful feedback while this doesn't.
# Perhaps we should just move the script to runtime in order to get that feedback?
{% puts "\nChecking player dependencies...\n" %}
{% puts "\nChecking player dependencies, this may take more than 20 minutes... If it is stuck, check your internet connection.\n" %}
{% if flag?(:minified_player_dependencies) %}
{% puts run("../scripts/fetch-player-dependencies.cr", "--minified").stringify %}
{% else %}
{% puts run("../scripts/fetch-player-dependencies.cr").stringify %}
{% end %}
{% puts "\nDone checking player dependencies, now compiling Invidious...\n" %}
{% end %}
# Start jobs
@ -168,313 +181,30 @@ if CONFIG.popular_enabled
Invidious::Jobs.register Invidious::Jobs::PullPopularVideosJob.new(PG_DB)
end
CONNECTION_CHANNEL = Channel({Bool, Channel(PQ::Notification)}).new(32)
CONNECTION_CHANNEL = ::Channel({Bool, ::Channel(PQ::Notification)}).new(32)
Invidious::Jobs.register Invidious::Jobs::NotificationJob.new(CONNECTION_CHANNEL, CONFIG.database_url)
Invidious::Jobs.register Invidious::Jobs::ClearExpiredItemsJob.new
Invidious::Jobs.start_all
def popular_videos
Invidious::Jobs::PullPopularVideosJob::POPULAR_VIDEOS.get
end
before_all do |env|
preferences = Preferences.from_json("{}")
begin
if prefs_cookie = env.request.cookies["PREFS"]?
preferences = Preferences.from_json(URI.decode_www_form(prefs_cookie.value))
else
if language_header = env.request.headers["Accept-Language"]?
if language = ANG.language_negotiator.best(language_header, LOCALES.keys)
preferences.locale = language.header
end
end
end
rescue
preferences = Preferences.from_json("{}")
end
env.set "preferences", preferences
env.response.headers["X-XSS-Protection"] = "1; mode=block"
env.response.headers["X-Content-Type-Options"] = "nosniff"
# Allow media resources to be loaded from google servers
# TODO: check if *.youtube.com can be removed
if CONFIG.disabled?("local") || !preferences.local
extra_media_csp = " https://*.googlevideo.com:443 https://*.youtube.com:443"
else
extra_media_csp = ""
end
# Only allow the pages at /embed/* to be embedded
if env.request.resource.starts_with?("/embed")
frame_ancestors = "'self' http: https:"
else
frame_ancestors = "'none'"
end
# TODO: Remove style-src's 'unsafe-inline', requires to remove all
# inline styles (<style> [..] </style>, style=" [..] ")
env.response.headers["Content-Security-Policy"] = {
"default-src 'none'",
"script-src 'self'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data:",
"font-src 'self' data:",
"connect-src 'self'",
"manifest-src 'self'",
"media-src 'self' blob:" + extra_media_csp,
"child-src 'self' blob:",
"frame-src 'self'",
"frame-ancestors " + frame_ancestors,
}.join("; ")
env.response.headers["Referrer-Policy"] = "same-origin"
# Ask the chrom*-based browsers to disable FLoC
# See: https://blog.runcloud.io/google-floc/
env.response.headers["Permissions-Policy"] = "interest-cohort=()"
if (Kemal.config.ssl || CONFIG.https_only) && CONFIG.hsts
env.response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload"
end
next if {
"/sb/",
"/vi/",
"/s_p/",
"/yts/",
"/ggpht/",
"/api/manifest/",
"/videoplayback",
"/latest_version",
"/download",
}.any? { |r| env.request.resource.starts_with? r }
if env.request.cookies.has_key? "SID"
sid = env.request.cookies["SID"].value
if sid.starts_with? "v1:"
raise "Cannot use token as SID"
end
# Invidious users only have SID
if !env.request.cookies.has_key? "SSID"
if email = Invidious::Database::SessionIDs.select_email(sid)
user = Invidious::Database::Users.select!(email: email)
csrf_token = generate_response(sid, {
":authorize_token",
":playlist_ajax",
":signout",
":subscription_ajax",
":token_ajax",
":watch_ajax",
}, HMAC_KEY, 1.week)
preferences = user.preferences
env.set "preferences", preferences
env.set "sid", sid
env.set "csrf_token", csrf_token
env.set "user", user
end
else
headers = HTTP::Headers.new
headers["Cookie"] = env.request.headers["Cookie"]
begin
user, sid = get_user(sid, headers, false)
csrf_token = generate_response(sid, {
":authorize_token",
":playlist_ajax",
":signout",
":subscription_ajax",
":token_ajax",
":watch_ajax",
}, HMAC_KEY, 1.week)
preferences = user.preferences
env.set "preferences", preferences
env.set "sid", sid
env.set "csrf_token", csrf_token
env.set "user", user
rescue ex
end
end
end
dark_mode = convert_theme(env.params.query["dark_mode"]?) || preferences.dark_mode.to_s
thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode.to_s
thin_mode = thin_mode == "true"
locale = env.params.query["hl"]? || preferences.locale
preferences.dark_mode = dark_mode
preferences.thin_mode = thin_mode
preferences.locale = locale
env.set "preferences", preferences
# Routing
current_page = env.request.path
if env.request.query
query = HTTP::Params.parse(env.request.query.not_nil!)
if query["referer"]?
query["referer"] = get_referer(env, "/")
end
current_page += "?#{query}"
end
env.set "current_page", URI.encode_www_form(current_page)
before_all do |env|
Invidious::Routes::BeforeAll.handle(env)
end
{% unless flag?(:api_only) %}
Invidious::Routing.get "/", Invidious::Routes::Misc, :home
Invidious::Routing.get "/privacy", Invidious::Routes::Misc, :privacy
Invidious::Routing.get "/licenses", Invidious::Routes::Misc, :licenses
Invidious::Routing.get "/channel/:ucid", Invidious::Routes::Channels, :home
Invidious::Routing.get "/channel/:ucid/home", Invidious::Routes::Channels, :home
Invidious::Routing.get "/channel/:ucid/videos", Invidious::Routes::Channels, :videos
Invidious::Routing.get "/channel/:ucid/playlists", Invidious::Routes::Channels, :playlists
Invidious::Routing.get "/channel/:ucid/community", Invidious::Routes::Channels, :community
Invidious::Routing.get "/channel/:ucid/about", Invidious::Routes::Channels, :about
Invidious::Routing.get "/channel/:ucid/live", Invidious::Routes::Channels, :live
Invidious::Routing.get "/user/:user/live", Invidious::Routes::Channels, :live
Invidious::Routing.get "/c/:user/live", Invidious::Routes::Channels, :live
["", "/videos", "/playlists", "/community", "/about"].each do |path|
# /c/LinusTechTips
Invidious::Routing.get "/c/:user#{path}", Invidious::Routes::Channels, :brand_redirect
# /user/linustechtips | Not always the same as /c/
Invidious::Routing.get "/user/:user#{path}", Invidious::Routes::Channels, :brand_redirect
# /attribution_link?a=anything&u=/channel/UCZYTClx2T1of7BRZ86-8fow
Invidious::Routing.get "/attribution_link#{path}", Invidious::Routes::Channels, :brand_redirect
# /profile?user=linustechtips
Invidious::Routing.get "/profile/#{path}", Invidious::Routes::Channels, :profile
end
Invidious::Routing.get "/watch", Invidious::Routes::Watch, :handle
Invidious::Routing.post "/watch_ajax", Invidious::Routes::Watch, :mark_watched
Invidious::Routing.get "/watch/:id", Invidious::Routes::Watch, :redirect
Invidious::Routing.get "/shorts/:id", Invidious::Routes::Watch, :redirect
Invidious::Routing.get "/clip/:clip", Invidious::Routes::Watch, :clip
Invidious::Routing.get "/w/:id", Invidious::Routes::Watch, :redirect
Invidious::Routing.get "/v/:id", Invidious::Routes::Watch, :redirect
Invidious::Routing.get "/e/:id", Invidious::Routes::Watch, :redirect
Invidious::Routing.get "/redirect", Invidious::Routes::Misc, :cross_instance_redirect
Invidious::Routing.post "/download", Invidious::Routes::Watch, :download
Invidious::Routing.get "/embed/", Invidious::Routes::Embed, :redirect
Invidious::Routing.get "/embed/:id", Invidious::Routes::Embed, :show
Invidious::Routing.get "/create_playlist", Invidious::Routes::Playlists, :new
Invidious::Routing.post "/create_playlist", Invidious::Routes::Playlists, :create
Invidious::Routing.get "/subscribe_playlist", Invidious::Routes::Playlists, :subscribe
Invidious::Routing.get "/delete_playlist", Invidious::Routes::Playlists, :delete_page
Invidious::Routing.post "/delete_playlist", Invidious::Routes::Playlists, :delete
Invidious::Routing.get "/edit_playlist", Invidious::Routes::Playlists, :edit
Invidious::Routing.post "/edit_playlist", Invidious::Routes::Playlists, :update
Invidious::Routing.get "/add_playlist_items", Invidious::Routes::Playlists, :add_playlist_items_page
Invidious::Routing.post "/playlist_ajax", Invidious::Routes::Playlists, :playlist_ajax
Invidious::Routing.get "/playlist", Invidious::Routes::Playlists, :show
Invidious::Routing.get "/mix", Invidious::Routes::Playlists, :mix
Invidious::Routing.get "/watch_videos", Invidious::Routes::Playlists, :watch_videos
Invidious::Routing.get "/opensearch.xml", Invidious::Routes::Search, :opensearch
Invidious::Routing.get "/results", Invidious::Routes::Search, :results
Invidious::Routing.get "/search", Invidious::Routes::Search, :search
# User routes
define_user_routes()
# Feeds
Invidious::Routing.get "/view_all_playlists", Invidious::Routes::Feeds, :view_all_playlists_redirect
Invidious::Routing.get "/feed/playlists", Invidious::Routes::Feeds, :playlists
Invidious::Routing.get "/feed/popular", Invidious::Routes::Feeds, :popular
Invidious::Routing.get "/feed/trending", Invidious::Routes::Feeds, :trending
Invidious::Routing.get "/feed/subscriptions", Invidious::Routes::Feeds, :subscriptions
Invidious::Routing.get "/feed/history", Invidious::Routes::Feeds, :history
# RSS Feeds
Invidious::Routing.get "/feed/channel/:ucid", Invidious::Routes::Feeds, :rss_channel
Invidious::Routing.get "/feed/private", Invidious::Routes::Feeds, :rss_private
Invidious::Routing.get "/feed/playlist/:plid", Invidious::Routes::Feeds, :rss_playlist
Invidious::Routing.get "/feeds/videos.xml", Invidious::Routes::Feeds, :rss_videos
# Support push notifications via PubSubHubbub
Invidious::Routing.get "/feed/webhook/:token", Invidious::Routes::Feeds, :push_notifications_get
Invidious::Routing.post "/feed/webhook/:token", Invidious::Routes::Feeds, :push_notifications_post
Invidious::Routing.get "/modify_notifications", Invidious::Routes::Notifications, :modify
Invidious::Routing.post "/subscription_ajax", Invidious::Routes::Subscriptions, :toggle_subscription
Invidious::Routing.get "/subscription_manager", Invidious::Routes::Subscriptions, :subscription_manager
{% end %}
Invidious::Routing.get "/ggpht/*", Invidious::Routes::Images, :ggpht
Invidious::Routing.options "/sb/:authority/:id/:storyboard/:index", Invidious::Routes::Images, :options_storyboard
Invidious::Routing.get "/sb/:authority/:id/:storyboard/:index", Invidious::Routes::Images, :get_storyboard
Invidious::Routing.get "/s_p/:id/:name", Invidious::Routes::Images, :s_p_image
Invidious::Routing.get "/yts/img/:name", Invidious::Routes::Images, :yts_image
Invidious::Routing.get "/vi/:id/:name", Invidious::Routes::Images, :thumbnails
# API routes (macro)
define_v1_api_routes()
# Video playback (macros)
define_api_manifest_routes()
define_video_playback_routes()
Invidious::Routing.register_all
error 404 do |env|
if md = env.request.path.match(/^\/(?<id>([a-zA-Z0-9_-]{11})|(\w+))$/)
item = md["id"]
# Check if item is branding URL e.g. https://youtube.com/gaming
response = YT_POOL.client &.get("/#{item}")
if response.status_code == 301
response = YT_POOL.client &.get(URI.parse(response.headers["Location"]).request_target)
end
if response.body.empty?
env.response.headers["Location"] = "/"
halt env, status_code: 302
end
html = XML.parse_html(response.body)
ucid = html.xpath_node(%q(//link[@rel="canonical"])).try &.["href"].split("/")[-1]
if ucid
env.response.headers["Location"] = "/channel/#{ucid}"
halt env, status_code: 302
end
params = [] of String
env.params.query.each do |k, v|
params << "#{k}=#{v}"
end
params = params.join("&")
url = "/watch?v=#{item}"
if !params.empty?
url += "&#{params}"
end
# Check if item is video ID
if item.match(/^[a-zA-Z0-9_-]{11}$/) && YT_POOL.client &.head("/watch?v=#{item}").status_code != 404
env.response.headers["Location"] = url
halt env, status_code: 302
end
end
env.response.headers["Location"] = "/"
halt env, status_code: 302
Invidious::Routes::ErrorRoutes.error_404(env)
end
error 500 do |env, ex|
locale = env.get("preferences").as(Preferences).locale
error_template(500, ex)
end
@ -482,6 +212,8 @@ static_headers do |response|
response.headers.add("Cache-Control", "max-age=2629800")
end
# Init Kemal
public_folder "assets"
Kemal.config.powered_by_header = false
@ -498,6 +230,10 @@ Kemal.config.host_binding = Kemal.config.host_binding != "0.0.0.0" ? Kemal.confi
Kemal.config.port = Kemal.config.port != 3000 ? Kemal.config.port : CONFIG.port
Kemal.config.app_name = "Invidious"
if !HMAC_KEY_CONFIGURED
LOGGER.warn("Please configure hmac_key by July 1st, see more here: https://github.com/iv-org/invidious/issues/3854")
end
# Use in kemal's production mode.
# Users can also set the KEMAL_ENV environmental variable for this to be set automatically.
{% if flag?(:release) || flag?(:production) %}

@ -16,12 +16,6 @@ record AboutChannel,
tabs : Array(String),
verified : Bool
record AboutRelatedChannel,
ucid : String,
author : String,
author_url : String,
author_thumbnail : String
def get_about_info(ucid, locale) : AboutChannel
begin
# "EgVhYm91dA==" is the base64-encoded protobuf object {"2:string":"about"}
@ -59,13 +53,11 @@ def get_about_info(ucid, locale) : AboutChannel
banner = banners.try &.[-1]?.try &.["url"].as_s?
description_node = initdata["header"]["interactiveTabbedHeaderRenderer"]["description"]
is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool
allowed_regions = initdata["microformat"]["microformatDataRenderer"]["availableCountries"].as_a.map(&.as_s)
else
author = initdata["metadata"]["channelMetadataRenderer"]["title"].as_s
author_url = initdata["metadata"]["channelMetadataRenderer"]["channelUrl"].as_s
author_thumbnail = initdata["metadata"]["channelMetadataRenderer"]["avatar"]["thumbnails"][0]["url"].as_s
author_verified = has_verified_badge?(initdata.dig?("header", "c4TabbedHeaderRenderer", "badges"))
ucid = initdata["metadata"]["channelMetadataRenderer"]["externalId"].as_s
@ -76,18 +68,19 @@ def get_about_info(ucid, locale) : AboutChannel
# if banner.includes? "channels/c4/default_banner"
# banner = nil
# end
# author_verified_badges = initdata["header"]?.try &.["c4TabbedHeaderRenderer"]?.try &.["badges"]?
author_verified_badge = initdata["header"].dig?("c4TabbedHeaderRenderer", "badges", 0, "metadataBadgeRenderer", "tooltip")
author_verified = (author_verified_badge && author_verified_badge == "Verified")
description_node = initdata["metadata"]["channelMetadataRenderer"]?.try &.["description"]?
end
is_family_friendly = initdata["microformat"]["microformatDataRenderer"]["familySafe"].as_bool
allowed_regions = initdata["microformat"]["microformatDataRenderer"]["availableCountries"].as_a.map(&.as_s)
end
allowed_regions = initdata
.dig?("microformat", "microformatDataRenderer", "availableCountries")
.try &.as_a.map(&.as_s) || [] of String
description = !description_node.nil? ? description_node.as_s : ""
description_html = HTML.escape(description)
if !description_node.nil?
if description_node.as_h?.nil?
description_node = text_to_parsed_content(description_node.as_s)
@ -101,38 +94,51 @@ def get_about_info(ucid, locale) : AboutChannel
total_views = 0_i64
joined = Time.unix(0)
tabs = [] of String
tab_names = [] of String
tabs_json = initdata["contents"]["twoColumnBrowseResultsRenderer"]["tabs"]?.try &.as_a?
if !tabs_json.nil?
# Retrieve information from the tabs array. The index we are looking for varies between channels.
tabs_json.each do |node|
# Try to find the about section which is located in only one of the tabs.
channel_about_meta = node["tabRenderer"]?.try &.["content"]?.try &.["sectionListRenderer"]?
.try &.["contents"]?.try &.[0]?.try &.["itemSectionRenderer"]?.try &.["contents"]?
.try &.[0]?.try &.["channelAboutFullMetadataRenderer"]?
if tabs_json = initdata["contents"]["twoColumnBrowseResultsRenderer"]["tabs"]?
# Get the name of the tabs available on this channel
tab_names = tabs_json.as_a.compact_map do |entry|
name = entry.dig?("tabRenderer", "title").try &.as_s.downcase
# This is a small fix to not add extra code on the HTML side
# I.e, the URL for the "live" tab is .../streams, so use "streams"
# everywhere for the sake of simplicity
(name == "live") ? "streams" : name
end
# Get the currently active tab ("About")
about_tab = extract_selected_tab(tabs_json)
# Try to find the about metadata section
channel_about_meta = about_tab.dig?(
"content",
"sectionListRenderer", "contents", 0,
"itemSectionRenderer", "contents", 0,
"channelAboutFullMetadataRenderer"
)
if !channel_about_meta.nil?
total_views = channel_about_meta["viewCountText"]?.try &.["simpleText"]?.try &.as_s.gsub(/\D/, "").to_i64? || 0_i64
total_views = channel_about_meta.dig?("viewCountText", "simpleText").try &.as_s.gsub(/\D/, "").to_i64? || 0_i64
# The joined text is split to several sub strings. The reduce joins those strings before parsing the date.
joined = channel_about_meta["joinedDateText"]?.try &.["runs"]?.try &.as_a.reduce("") { |acc, nd| acc + nd["text"].as_s }
joined = extract_text(channel_about_meta["joinedDateText"]?)
.try { |text| Time.parse(text, "Joined %b %-d, %Y", Time::Location.local) } || Time.unix(0)
# Normal Auto-generated channels
# https://support.google.com/youtube/answer/2579942
# For auto-generated channels, channel_about_meta only has ["description"]["simpleText"] and ["primaryLinks"][0]["title"]["simpleText"]
if (channel_about_meta["primaryLinks"]?.try &.size || 0) == 1 && (channel_about_meta["primaryLinks"][0]?) &&
(channel_about_meta["primaryLinks"][0]["title"]?.try &.["simpleText"]?.try &.as_s? || "") == "Auto-generated by YouTube"
auto_generated = true
end
end
# For auto-generated channels, channel_about_meta only has
# ["description"]["simpleText"] and ["primaryLinks"][0]["title"]["simpleText"]
auto_generated = (
(channel_about_meta["primaryLinks"]?.try &.size) == 1 && \
extract_text(channel_about_meta.dig?("primaryLinks", 0, "title")) == "Auto-generated by YouTube"
)
end
tabs = tabs_json.reject { |node| node["tabRenderer"]?.nil? }.map(&.["tabRenderer"]["title"].as_s.downcase)
end
sub_count = initdata["header"]["c4TabbedHeaderRenderer"]?.try &.["subscriberCountText"]?.try &.["simpleText"]?.try &.as_s?
.try { |text| short_text_to_number(text.split(" ")[0]) } || 0
sub_count = initdata
.dig?("header", "c4TabbedHeaderRenderer", "subscriberCountText", "simpleText").try &.as_s?
.try { |text| short_text_to_number(text.split(" ")[0]).to_i32 } || 0
AboutChannel.new(
ucid: ucid,
@ -148,46 +154,20 @@ def get_about_info(ucid, locale) : AboutChannel
joined: joined,
is_family_friendly: is_family_friendly,
allowed_regions: allowed_regions,
tabs: tabs,
tabs: tab_names,
verified: author_verified || false,
)
end
def fetch_related_channels(about_channel : AboutChannel) : Array(AboutRelatedChannel)
def fetch_related_channels(about_channel : AboutChannel, continuation : String? = nil) : {Array(SearchChannel), String?}
if continuation.nil?
# params is {"2:string":"channels"} encoded
channels = YoutubeAPI.browse(browse_id: about_channel.ucid, params: "EghjaGFubmVscw%3D%3D")
tabs = channels.dig?("contents", "twoColumnBrowseResultsRenderer", "tabs").try(&.as_a?) || [] of JSON::Any
tab = tabs.find(&.dig?("tabRenderer", "title").try(&.as_s?).try(&.== "Channels"))
return [] of AboutRelatedChannel if tab.nil?
items = tab.dig?(
"tabRenderer", "content",
"sectionListRenderer", "contents", 0,
"itemSectionRenderer", "contents", 0,
"gridRenderer", "items"
).try &.as_a?
related = [] of AboutRelatedChannel
return related if (items.nil? || items.empty?)
items.each do |item|
renderer = item["gridChannelRenderer"]?
next if !renderer
related_id = renderer.dig("channelId").as_s
related_title = renderer.dig("title", "simpleText").as_s
related_author_url = renderer.dig("navigationEndpoint", "browseEndpoint", "canonicalBaseUrl").as_s
related_author_thumbnail = HelperExtractors.get_thumbnails(renderer)
related << AboutRelatedChannel.new(
ucid: related_id,
author: related_title,
author_url: related_author_url,
author_thumbnail: related_author_thumbnail,
)
initial_data = YoutubeAPI.browse(browse_id: about_channel.ucid, params: "EghjaGFubmVscw%3D%3D")
else
initial_data = YoutubeAPI.browse(continuation)
end
return related
items, continuation = extract_items(initial_data)
return items.select(SearchChannel), continuation
end

@ -29,7 +29,7 @@ struct ChannelVideo
json.field "title", self.title
json.field "videoId", self.id
json.field "videoThumbnails" do
generate_thumbnails(json, self.id)
Invidious::JSONify::APIv1.thumbnails(json, self.id)
end
json.field "lengthSeconds", self.length_seconds
@ -159,12 +159,18 @@ def fetch_channel(ucid, pull_all_videos : Bool)
LOGGER.debug("fetch_channel: #{ucid}")
LOGGER.trace("fetch_channel: #{ucid} : pull_all_videos = #{pull_all_videos}")
namespaces = {
"yt" => "http://www.youtube.com/xml/schemas/2015",
"media" => "http://search.yahoo.com/mrss/",
"default" => "http://www.w3.org/2005/Atom",
}
LOGGER.trace("fetch_channel: #{ucid} : Downloading RSS feed")
rss = YT_POOL.client &.get("/feeds/videos.xml?channel_id=#{ucid}").body
LOGGER.trace("fetch_channel: #{ucid} : Parsing RSS feed")
rss = XML.parse_html(rss)
rss = XML.parse(rss)
author = rss.xpath_node(%q(//feed/title))
author = rss.xpath_node("//default:feed/default:title", namespaces)
if !author
raise InfoException.new("Deleted or invalid channel")
end
@ -180,24 +186,39 @@ def fetch_channel(ucid, pull_all_videos : Bool)
LOGGER.trace("fetch_channel: #{ucid} : author = #{author}, auto_generated = #{auto_generated}")
page = 1
channel = InvidiousChannel.new({
id: ucid,
author: author,
updated: Time.utc,
deleted: false,
subscribed: nil,
})
LOGGER.trace("fetch_channel: #{ucid} : Downloading channel videos page")
initial_data = get_channel_videos_response(ucid, page, auto_generated: auto_generated)
videos = extract_videos(initial_data, author, ucid)
videos, continuation = IV::Channel::Tabs.get_videos(channel)
LOGGER.trace("fetch_channel: #{ucid} : Extracting videos from channel RSS feed")
rss.xpath_nodes("//feed/entry").each do |entry|
video_id = entry.xpath_node("videoid").not_nil!.content
title = entry.xpath_node("title").not_nil!.content
published = Time.parse_rfc3339(entry.xpath_node("published").not_nil!.content)
updated = Time.parse_rfc3339(entry.xpath_node("updated").not_nil!.content)
author = entry.xpath_node("author/name").not_nil!.content
ucid = entry.xpath_node("channelid").not_nil!.content
views = entry.xpath_node("group/community/statistics").try &.["views"]?.try &.to_i64?
views ||= 0_i64
channel_video = videos.select { |video| video.id == video_id }[0]?
rss.xpath_nodes("//default:feed/default:entry", namespaces).each do |entry|
video_id = entry.xpath_node("yt:videoId", namespaces).not_nil!.content
title = entry.xpath_node("default:title", namespaces).not_nil!.content
published = Time.parse_rfc3339(
entry.xpath_node("default:published", namespaces).not_nil!.content
)
updated = Time.parse_rfc3339(
entry.xpath_node("default:updated", namespaces).not_nil!.content
)
author = entry.xpath_node("default:author/default:name", namespaces).not_nil!.content
ucid = entry.xpath_node("yt:channelId", namespaces).not_nil!.content
views = entry
.xpath_node("media:group/media:community/media:statistics", namespaces)
.try &.["views"]?.try &.to_i64? || 0_i64
channel_video = videos
.select(SearchVideo)
.select(&.id.== video_id)[0]?
length_seconds = channel_video.try &.length_seconds
length_seconds ||= 0
@ -228,23 +249,25 @@ def fetch_channel(ucid, pull_all_videos : Bool)
if was_insert
LOGGER.trace("fetch_channel: #{ucid} : video #{video_id} : Inserted, updating subscriptions")
if CONFIG.enable_user_notifications
Invidious::Database::Users.add_notification(video)
else
Invidious::Database::Users.feed_needs_update(video)
end
else
LOGGER.trace("fetch_channel: #{ucid} : video #{video_id} : Updated")
end
end
if pull_all_videos
page += 1
ids = [] of String
loop do
initial_data = get_channel_videos_response(ucid, page, auto_generated: auto_generated)
videos = extract_videos(initial_data, author, ucid)
# Keep fetching videos using the continuation token retrieved earlier
videos, continuation = IV::Channel::Tabs.get_videos(channel, continuation: continuation)
count = videos.size
videos = videos.map { |video| ChannelVideo.new({
count = 0
videos.select(SearchVideo).each do |video|
count += 1
video = ChannelVideo.new({
id: video.id,
title: video.title,
published: video.published,
@ -255,31 +278,27 @@ def fetch_channel(ucid, pull_all_videos : Bool)
live_now: video.live_now,
premiere_timestamp: video.premiere_timestamp,
views: video.views,
}) }
videos.each do |video|
ids << video.id
})
# We are notified of Red videos elsewhere (PubSub), which includes a correct published date,
# so since they don't provide a published date here we can safely ignore them.
if Time.utc - video.published > 1.minute
was_insert = Invidious::Database::ChannelVideos.insert(video)
Invidious::Database::Users.add_notification(video) if was_insert
if was_insert
if CONFIG.enable_user_notifications
Invidious::Database::Users.add_notification(video)
else
Invidious::Database::Users.feed_needs_update(video)
end
end
end
end
break if count < 25
page += 1
sleep 500.milliseconds
end
end
channel = InvidiousChannel.new({
id: ucid,
author: author,
updated: Time.utc,
deleted: false,
subscribed: nil,
})
channel.updated = Time.utc
return channel
end

@ -1,3 +1,5 @@
private IMAGE_QUALITIES = {320, 560, 640, 1280, 2000}
# TODO: Add "sort_by"
def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
response = YT_POOL.client &.get("/channel/#{ucid}/community?gl=US&hl=en")
@ -13,13 +15,11 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
if !continuation || continuation.empty?
initial_data = extract_initial_data(response.body)
body = initial_data["contents"]?.try &.["twoColumnBrowseResultsRenderer"]["tabs"].as_a.select { |tab| tab["tabRenderer"]?.try &.["selected"].as_bool.== true }[0]?
body = extract_selected_tab(initial_data["contents"]["twoColumnBrowseResultsRenderer"]["tabs"])["content"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"]
if !body
raise InfoException.new("Could not extract community tab.")
end
body = body["tabRenderer"]["content"]["sectionListRenderer"]["contents"][0]["itemSectionRenderer"]
else
continuation = produce_channel_community_continuation(ucid, continuation)
@ -31,18 +31,16 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
session_token: session_token,
}
response = YT_POOL.client &.post("/comment_service_ajax?action_get_comments=1&ctoken=#{continuation}&continuation=#{continuation}&hl=en&gl=US", headers, form: post_req)
body = JSON.parse(response.body)
body = YoutubeAPI.browse(continuation)
body = body["response"]["continuationContents"]["itemSectionContinuation"]? ||
body["response"]["continuationContents"]["backstageCommentsContinuation"]?
body = body.dig?("continuationContents", "itemSectionContinuation") ||
body.dig?("continuationContents", "backstageCommentsContinuation")
if !body
raise InfoException.new("Could not extract continuation.")
end
end
continuation = body["continuations"]?.try &.[0]["nextContinuationData"]["continuation"].as_s
posts = body["contents"].as_a
if message = posts[0]["messageRenderer"]?
@ -71,7 +69,7 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
next if !post
content_html = post["contentText"]?.try { |t| parse_content(t) } || ""
author = post["authorText"]?.try &.["simpleText"]? || ""
author = post["authorText"]["runs"]?.try &.[0]?.try &.["text"]? || ""
json.object do
json.field "author", author
@ -110,6 +108,8 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
like_count = post["actionButtons"]["commentActionButtonsRenderer"]["likeButton"]["toggleButtonRenderer"]["accessibilityData"]["accessibilityData"]["label"]
.try &.as_s.gsub(/\D/, "").to_i? || 0
reply_count = short_text_to_number(post.dig?("actionButtons", "commentActionButtonsRenderer", "replyButton", "buttonRenderer", "text", "simpleText").try &.as_s || "0")
json.field "content", html_to_content(content_html)
json.field "contentHtml", content_html
@ -117,54 +117,19 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
json.field "publishedText", translate(locale, "`x` ago", recode_date(published, locale))
json.field "likeCount", like_count
json.field "replyCount", reply_count
json.field "commentId", post["postId"]? || post["commentId"]? || ""
json.field "authorIsChannelOwner", post["authorEndpoint"]["browseEndpoint"]["browseId"] == ucid
if attachment = post["backstageAttachment"]?
json.field "attachment" do
json.object do
case attachment.as_h
when .has_key?("videoRenderer")
attachment = attachment["videoRenderer"]
json.field "type", "video"
if !attachment["videoId"]?
error_message = (attachment["title"]["simpleText"]? ||
attachment["title"]["runs"]?.try &.[0]?.try &.["text"]?)
json.field "error", error_message
else
video_id = attachment["videoId"].as_s
video_title = attachment["title"]["simpleText"]? || attachment["title"]["runs"]?.try &.[0]?.try &.["text"]?
json.field "title", video_title
json.field "videoId", video_id
json.field "videoThumbnails" do
generate_thumbnails(json, video_id)
end
json.field "lengthSeconds", decode_length_seconds(attachment["lengthText"]["simpleText"].as_s)
author_info = attachment["ownerText"]["runs"][0].as_h
json.field "author", author_info["text"].as_s
json.field "authorId", author_info["navigationEndpoint"]["browseEndpoint"]["browseId"]
json.field "authorUrl", author_info["navigationEndpoint"]["commandMetadata"]["webCommandMetadata"]["url"]
# TODO: json.field "authorThumbnails", "channelThumbnailSupportedRenderers"
# TODO: json.field "authorVerified", "ownerBadges"
published = decode_date(attachment["publishedTimeText"]["simpleText"].as_s)
json.field "published", published.to_unix
json.field "publishedText", translate(locale, "`x` ago", recode_date(published, locale))
view_count = attachment["viewCountText"]?.try &.["simpleText"].as_s.gsub(/\D/, "").to_i64? || 0_i64
json.field "viewCount", view_count
json.field "viewCountText", translate_count(locale, "generic_views_count", view_count, NumberFormatting::Short)
end
parse_item(attachment)
.as(SearchVideo)
.to_json(locale, json)
when .has_key?("backstageImageRenderer")
json.object do
attachment = attachment["backstageImageRenderer"]
json.field "type", "image"
@ -176,9 +141,66 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
aspect_ratio = (width.to_f / height.to_f)
url = thumbnail["url"].as_s.gsub(/=w\d+-h\d+(-p)?(-nd)?(-df)?(-rwa)?/, "=s640")
qualities = {320, 560, 640, 1280, 2000}
IMAGE_QUALITIES.each do |quality|
json.object do
json.field "url", url.gsub(/=s\d+/, "=s#{quality}")
json.field "width", quality
json.field "height", (quality / aspect_ratio).ceil.to_i
end
end
end
end
end
when .has_key?("pollRenderer")
json.object do
attachment = attachment["pollRenderer"]
json.field "type", "poll"
json.field "totalVotes", short_text_to_number(attachment["totalVotes"]["simpleText"].as_s.split(" ")[0])
json.field "choices" do
json.array do
attachment["choices"].as_a.each do |choice|
json.object do
json.field "text", choice.dig("text", "runs", 0, "text").as_s
# A choice can have an image associated with it.
# Ex post: https://www.youtube.com/post/UgkxD4XavXUD4NQiddJXXdohbwOwcVqrH9Re
if choice["image"]?
thumbnail = choice["image"]["thumbnails"][0].as_h
width = thumbnail["width"].as_i
height = thumbnail["height"].as_i
aspect_ratio = (width.to_f / height.to_f)
url = thumbnail["url"].as_s.gsub(/=w\d+-h\d+(-p)?(-nd)?(-df)?(-rwa)?/, "=s640")
json.field "image" do
json.array do
IMAGE_QUALITIES.each do |quality|
json.object do
json.field "url", url.gsub(/=s\d+/, "=s#{quality}")
json.field "width", quality
json.field "height", (quality / aspect_ratio).ceil.to_i
end
end
end
end
end
end
end
end
end
end
when .has_key?("postMultiImageRenderer")
json.object do
attachment = attachment["postMultiImageRenderer"]
json.field "type", "multiImage"
json.field "images" do
json.array do
attachment["images"].as_a.each do |image|
json.array do
thumbnail = image["backstageImageRenderer"]["image"]["thumbnails"][0].as_h
width = thumbnail["width"].as_i
height = thumbnail["height"].as_i
aspect_ratio = (width.to_f / height.to_f)
url = thumbnail["url"].as_s.gsub(/=w\d+-h\d+(-p)?(-nd)?(-df)?(-rwa)?/, "=s640")
qualities.each do |quality|
IMAGE_QUALITIES.each do |quality|
json.object do
json.field "url", url.gsub(/=s\d+/, "=s#{quality}")
json.field "width", quality
@ -187,11 +209,15 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
end
end
end
# TODO
# when .has_key?("pollRenderer")
# attachment = attachment["pollRenderer"]
# json.field "type", "poll"
end
end
end
when .has_key?("playlistRenderer")
parse_item(attachment)
.as(SearchPlaylist)
.to_json(locale, json)
else
json.object do
json.field "type", "unknown"
json.field "error", "Unrecognized attachment type."
end
@ -216,17 +242,15 @@ def fetch_channel_community(ucid, continuation, locale, format, thin_mode)
end
end
end
if body["continuations"]?
continuation = body["continuations"][0]["nextContinuationData"]["continuation"].as_s
json.field "continuation", extract_channel_community_cursor(continuation)
if cont = posts.dig?(-1, "continuationItemRenderer", "continuationEndpoint", "continuationCommand", "token")
json.field "continuation", extract_channel_community_cursor(cont.as_s)
end
end
end
if format == "html"
response = JSON.parse(response)
content_html = template_youtube_comments(response, locale, thin_mode)
content_html = IV::Frontend::Comments.template_youtube(response, locale, thin_mode)
response = JSON.build do |json|
json.object do

@ -1,93 +1,28 @@
def fetch_channel_playlists(ucid, author, continuation, sort_by)
if continuation
response_json = YoutubeAPI.browse(continuation)
continuation_items = response_json["onResponseReceivedActions"]?
.try &.[0]["appendContinuationItemsAction"]["continuationItems"]
return [] of SearchItem, nil if !continuation_items
items = [] of SearchItem
continuation_items.as_a.select(&.as_h.has_key?("gridPlaylistRenderer")).each { |item|
extract_item(item, author, ucid).try { |t| items << t }
}
continuation = continuation_items.as_a.last["continuationItemRenderer"]?
.try &.["continuationEndpoint"]["continuationCommand"]["token"].as_s
initial_data = YoutubeAPI.browse(continuation)
else
url = "/channel/#{ucid}/playlists?flow=list&view=1"
params =
case sort_by
when "last", "last_added"
#
# Equivalent to "&sort=lad"
# {"2:string": "playlists", "3:varint": 4, "4:varint": 1, "6:varint": 1}
"EglwbGF5bGlzdHMYBCABMAE%3D"
when "oldest", "oldest_created"
url += "&sort=da"
# formerly "&sort=da"
# Not available anymore :c or maybe ??
# {"2:string": "playlists", "3:varint": 2, "4:varint": 1, "6:varint": 1}
"EglwbGF5bGlzdHMYAiABMAE%3D"
# {"2:string": "playlists", "3:varint": 1, "4:varint": 1, "6:varint": 1}
# "EglwbGF5bGlzdHMYASABMAE%3D"
when "newest", "newest_created"
url += "&sort=dd"
else nil # Ignore
end
response = YT_POOL.client &.get(url)
initial_data = extract_initial_data(response.body)
return [] of SearchItem, nil if !initial_data
items = extract_items(initial_data, author, ucid)
continuation = response.body.match(/"token":"(?<continuation>[^"]+)"/).try &.["continuation"]?
# Formerly "&sort=dd"
# {"2:string": "playlists", "3:varint": 3, "4:varint": 1, "6:varint": 1}
"EglwbGF5bGlzdHMYAyABMAE%3D"
end
return items, continuation
end
# ## NOTE: DEPRECATED
# Reason -> Unstable
# The Protobuf object must be provided with an id of the last playlist from the current "page"
# in order to fetch the next one accurately
# (if the id isn't included, entries shift around erratically between pages,
# leading to repetitions and skip overs)
#
# Since it's impossible to produce the appropriate Protobuf without an id being provided by the user,
# it's better to stick to continuation tokens provided by the first request and onward
def produce_channel_playlists_url(ucid, cursor, sort = "newest", auto_generated = false)
object = {
"80226972:embedded" => {
"2:string" => ucid,
"3:base64" => {
"2:string" => "playlists",
"6:varint" => 2_i64,
"7:varint" => 1_i64,
"12:varint" => 1_i64,
"13:string" => "",
"23:varint" => 0_i64,
},
},
}
if cursor
cursor = Base64.urlsafe_encode(cursor, false) if !auto_generated
object["80226972:embedded"]["3:base64"].as(Hash)["15:string"] = cursor
initial_data = YoutubeAPI.browse(ucid, params: params || "")
end
if auto_generated
object["80226972:embedded"]["3:base64"].as(Hash)["4:varint"] = 0x32_i64
else
object["80226972:embedded"]["3:base64"].as(Hash)["4:varint"] = 1_i64
case sort
when "oldest", "oldest_created"
object["80226972:embedded"]["3:base64"].as(Hash)["3:varint"] = 2_i64
when "newest", "newest_created"
object["80226972:embedded"]["3:base64"].as(Hash)["3:varint"] = 3_i64
when "last", "last_added"
object["80226972:embedded"]["3:base64"].as(Hash)["3:varint"] = 4_i64
else nil # Ignore
end
end
object["80226972:embedded"]["3:string"] = Base64.urlsafe_encode(Protodec::Any.from_json(Protodec::Any.cast_json(object["80226972:embedded"]["3:base64"])))
object["80226972:embedded"].delete("3:base64")
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 "/browse_ajax?continuation=#{continuation}&gl=US&hl=en"
return extract_items(initial_data, author, ucid)
end

@ -1,53 +1,58 @@
def produce_channel_videos_continuation(ucid, page = 1, auto_generated = nil, sort_by = "newest", v2 = false)
object = {
"80226972:embedded" => {
"2:string" => ucid,
"3:base64" => {
"2:string" => "videos",
"6:varint" => 2_i64,
"7:varint" => 1_i64,
"12:varint" => 1_i64,
"13:string" => "",
"23:varint" => 0_i64,
},
object_inner_2 = {
"2:0:embedded" => {
"1:0:varint" => 0_i64,
},
"5:varint" => 50_i64,
"6:varint" => 1_i64,
"7:varint" => (page * 30).to_i64,
"9:varint" => 1_i64,
"10:varint" => 0_i64,
}
if !v2
if auto_generated
seed = Time.unix(1525757349)
until seed >= Time.utc
seed += 1.month
end
timestamp = seed - (page - 1).months
object_inner_2_encoded = object_inner_2
.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) }
object["80226972:embedded"]["3:base64"].as(Hash)["4:varint"] = 0x36_i64
object["80226972:embedded"]["3:base64"].as(Hash)["15:string"] = "#{timestamp.to_unix}"
else
object["80226972:embedded"]["3:base64"].as(Hash)["4:varint"] = 0_i64
object["80226972:embedded"]["3:base64"].as(Hash)["15:string"] = "#{page}"
sort_by_numerical =
case sort_by
when "newest" then 1_i64
when "popular" then 2_i64
when "oldest" then 3_i64 # Broken as of 10/2022 :c
else 1_i64 # Fallback to "newest"
end
else
object["80226972:embedded"]["3:base64"].as(Hash)["4:varint"] = 0_i64
object["80226972:embedded"]["3:base64"].as(Hash)["61:string"] = Base64.urlsafe_encode(Protodec::Any.from_json(Protodec::Any.cast_json({
"1:string" => Base64.urlsafe_encode(Protodec::Any.from_json(Protodec::Any.cast_json({
"1:varint" => 30_i64 * (page - 1),
}))),
})))
end
object_inner_1 = {
"110:embedded" => {
"3:embedded" => {
"15:embedded" => {
"1:embedded" => {
"1:string" => object_inner_2_encoded,
},
"2:embedded" => {
"1:string" => "00000000-0000-0000-0000-000000000000",
},
"3:varint" => sort_by_numerical,
},
},
},
}
case sort_by
when "newest"
when "popular"
object["80226972:embedded"]["3:base64"].as(Hash)["3:varint"] = 0x01_i64
when "oldest"
object["80226972:embedded"]["3:base64"].as(Hash)["3:varint"] = 0x02_i64
else nil # Ignore
end
object_inner_1_encoded = object_inner_1
.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) }
object["80226972:embedded"]["3:string"] = Base64.urlsafe_encode(Protodec::Any.from_json(Protodec::Any.cast_json(object["80226972:embedded"]["3:base64"])))
object["80226972:embedded"].delete("3:base64")
object = {
"80226972:embedded" => {
"2:string" => ucid,
"3:string" => object_inner_1_encoded,
"35:string" => "browse-feed#{ucid}videos102",
},
}
continuation = object.try { |i| Protodec::Any.cast_json(i) }
.try { |i| Protodec::Any.from_json(i) }
@ -57,33 +62,115 @@ def produce_channel_videos_continuation(ucid, page = 1, auto_generated = nil, so
return continuation
end
def get_channel_videos_response(ucid, page = 1, auto_generated = nil, sort_by = "newest")
continuation = produce_channel_videos_continuation(ucid, page,
auto_generated: auto_generated, sort_by: sort_by, v2: true)
return YoutubeAPI.browse(continuation)
# Used in bypass_captcha_job.cr
def produce_channel_videos_url(ucid, page = 1, auto_generated = nil, sort_by = "newest", v2 = false)
continuation = produce_channel_videos_continuation(ucid, page, auto_generated, sort_by, v2)
return "/browse_ajax?continuation=#{continuation}&gl=US&hl=en"
end
def get_60_videos(ucid, author, page, auto_generated, sort_by = "newest")
videos = [] of SearchVideo
module Invidious::Channel::Tabs
extend self
# -------------------
# Regular videos
# -------------------
2.times do |i|
initial_data = get_channel_videos_response(ucid, page * 2 + (i - 1), auto_generated: auto_generated, sort_by: sort_by)
videos.concat extract_videos(initial_data, author, ucid)
def make_initial_video_ctoken(ucid, sort_by) : String
return produce_channel_videos_continuation(ucid, sort_by: sort_by)
end
return videos.size, videos
end
# Wrapper for AboutChannel, as we still need to call get_videos with
# an author name and ucid directly (e.g in RSS feeds).
# TODO: figure out how to get rid of that
def get_videos(channel : AboutChannel, *, continuation : String? = nil, sort_by = "newest")
return get_videos(
channel.author, channel.ucid,
continuation: continuation, sort_by: sort_by
)
end
def get_latest_videos(ucid)
initial_data = get_channel_videos_response(ucid)
author = initial_data["metadata"]?.try &.["channelMetadataRenderer"]?.try &.["title"]?.try &.as_s
# Wrapper for InvidiousChannel, as we still need to call get_videos with
# an author name and ucid directly (e.g in RSS feeds).
# TODO: figure out how to get rid of that
def get_videos(channel : InvidiousChannel, *, continuation : String? = nil, sort_by = "newest")
return get_videos(
channel.author, channel.id,
continuation: continuation, sort_by: sort_by
)
end
return extract_videos(initial_data, author, ucid)
end
def get_videos(author : String, ucid : String, *, continuation : String? = nil, sort_by = "newest")
continuation ||= make_initial_video_ctoken(ucid, sort_by)
initial_data = YoutubeAPI.browse(continuation: continuation)
# Used in bypass_captcha_job.cr
def produce_channel_videos_url(ucid, page = 1, auto_generated = nil, sort_by = "newest", v2 = false)
continuation = produce_channel_videos_continuation(ucid, page, auto_generated, sort_by, v2)
return "/browse_ajax?continuation=#{continuation}&gl=US&hl=en"
return extract_items(initial_data, author, ucid)
end
def get_60_videos(channel : AboutChannel, *, continuation : String? = nil, sort_by = "newest")
if continuation.nil?
# Fetch the first "page" of video
items, next_continuation = get_videos(channel, sort_by: sort_by)
else
# Fetch a "page" of videos using the given continuation token
items, next_continuation = get_videos(channel, continuation: continuation)
end
# If there is more to load, then load a second "page"
# and replace the previous continuation token
if !next_continuation.nil?
items_2, next_continuation = get_videos(channel, continuation: next_continuation)
items.concat items_2
end
return items, next_continuation
end
# -------------------
# Shorts
# -------------------
def get_shorts(channel : AboutChannel, continuation : String? = nil)
if continuation.nil?
# EgZzaG9ydHPyBgUKA5oBAA%3D%3D is the protobuf object to load "shorts"
# TODO: try to extract the continuation tokens that allows other sorting options
initial_data = YoutubeAPI.browse(channel.ucid, params: "EgZzaG9ydHPyBgUKA5oBAA%3D%3D")
else
initial_data = YoutubeAPI.browse(continuation: continuation)
end
return extract_items(initial_data, channel.author, channel.ucid)
end
# -------------------
# Livestreams
# -------------------
def get_livestreams(channel : AboutChannel, continuation : String? = nil)
if continuation.nil?
# EgdzdHJlYW1z8gYECgJ6AA%3D%3D is the protobuf object to load "streams"
initial_data = YoutubeAPI.browse(channel.ucid, params: "EgdzdHJlYW1z8gYECgJ6AA%3D%3D")
else
initial_data = YoutubeAPI.browse(continuation: continuation)
end
return extract_items(initial_data, channel.author, channel.ucid)
end
def get_60_livestreams(channel : AboutChannel, continuation : String? = nil)
if continuation.nil?
# Fetch the first "page" of streams
items, next_continuation = get_livestreams(channel)
else
# Fetch a "page" of streams using the given continuation token
items, next_continuation = get_livestreams(channel, continuation: continuation)
end
# If there is more to load, then load a second "page"
# and replace the previous continuation token
if !next_continuation.nil?
items_2, next_continuation = get_livestreams(channel, continuation: next_continuation)
items.concat items_2
end
return items, next_continuation
end
end

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save