Compare commits

..

191 Commits

Author SHA1 Message Date
William Oldham
51e9c4d758 Add import and export functions for settings to JSON 2024-03-16 17:57:41 +00:00
William Oldham
bcfadc8f60 Add zod - for validation of uploaded settings 2024-03-16 17:51:10 +00:00
William Oldham
a642abc783 Add methods to directly set progress and bookmark items 2024-03-16 17:50:24 +00:00
William Oldham
558c6431fd Merge remote-tracking branch 'origin/dev' into settings-migration 2024-03-16 15:22:29 +00:00
William Oldham
2a0e46a97d Fix #1016 - Capitalisation of zh-Hant broke traditional Chinese 2024-03-16 12:54:59 +00:00
William Oldham
227defd713 Merge pull request #1017 from qtchaos/fix/setPositionState
Add check for setPositionState to avoid TypeError
2024-03-16 08:36:09 +00:00
William Oldham
f1a8ff4bf8 Merge pull request #1021 from Honkertonken/fix-vercel
Fix mixed-routing-properties error
2024-03-16 08:35:22 +00:00
Honkertonken
162da3b22b Update vercel.json 2024-03-16 13:12:34 +05:30
William Oldham
35113ed522 Merge pull request #1019 from movie-web/feature/vercel-headers
Add vercel.json headers
2024-03-15 22:38:28 +00:00
William Oldham
94a003bce6 Add vercel.json headers 2024-03-15 22:25:34 +00:00
qtchaos
e0fec7ffa3 fix: add check for setPositionState to avoid TypeError 2024-03-15 17:30:37 +02:00
Jorrin
6f3c700dcb Merge pull request #1015 from movie-web/dev
Version 4.6.2
2024-03-14 23:52:56 +01:00
Jorrin
c3fec6c522 Merge pull request #1014 from gh-movie-web/weblate-movie-web-website
Translations update from movie-web weblate
2024-03-14 23:36:54 +01:00
jan Kukisulasu
2ac0f2304f Translated using Weblate (Toki Pona)
Currently translated at 82.9% (272 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/tok/
Author: jan Kukisulasu <iam.mcken@gmail.com>
2024-03-14 22:27:48 +00:00
Jamie Poznanski
524e3f7358 Translated using Weblate (Polish)
Currently translated at 97.2% (319 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/pl/
Author: Jamie Poznanski <enby_jamie@users.noreply.weblate.movie-web.app>
2024-03-14 22:27:48 +00:00
Jorrin
135feab14c bump providers and version 2024-03-14 23:27:42 +01:00
Jorrin
41949b0ab3 Merge pull request #1007 from ssssobek/embedded-subtitles-support
Add embedded subtitles support
2024-03-14 21:21:20 +01:00
ssssobek
9f025bd12b Move filterDuplicateCaptionCues to a different file 2024-03-14 20:34:32 +01:00
ssssobek
810a12a097 Apply requested changes 2024-03-14 01:01:24 +01:00
ssssobek
c1f9382f50 Add embedded subtitles support 2024-03-12 23:45:34 +01:00
Jorrin
8ccca76573 Set default onboarding to true 2024-03-11 23:48:23 +01:00
William Oldham
fc76a84bc8 Set default extension links 2024-03-11 22:41:56 +00:00
William Oldham
fe9c2591e1 Merge pull request #1004 from movie-web/dev
Version 4.6.1: Fix subtitle settings and add translations
2024-03-11 20:51:24 +00:00
William Oldham
28cb9243bc Merge branch 'master' into dev 2024-03-11 20:36:26 +00:00
William Oldham
ee047327a1 Start adding migration pages 2024-03-11 20:35:21 +00:00
William Oldham
8e73751f48 Translate onboarding "or" text 2024-03-11 20:35:21 +00:00
William Oldham
8420bedb84 Create migration hook to register and import data 2024-03-11 20:35:21 +00:00
William Oldham
ba2f3fd359 Add method to get keys from seed directly 2024-03-11 20:35:21 +00:00
William Oldham
852e6ff324 Make VerticleLine a general component 2024-03-11 20:35:21 +00:00
William Oldham
7d0563b236 Merge pull request #1002 from gh-movie-web/weblate-movie-web-website
Translations update from movie-web weblate
2024-03-11 20:12:55 +00:00
Mehdi
85ddf7680f Translated using Weblate (Persian)
Currently translated at 100.0% (328 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-03-11 19:27:04 +00:00
Denys Savin
808191c42e Translated using Weblate (Ukrainian)
Currently translated at 100.0% (328 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/uk/
Author: Denys Savin <dxnsav@gmail.com>
2024-03-11 19:27:03 +00:00
blikje
ae90d3d873 Translated using Weblate (Dutch)
Currently translated at 100.0% (328 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/nl/
Author: blikje <Blikje7up@gmail.com>
2024-03-11 19:27:03 +00:00
Jamie Poznanski
cff06d7591 Translated using Weblate (Italian)
Currently translated at 100.0% (328 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/it/
Author: Jamie Poznanski <enby_jamie@users.noreply.weblate.movie-web.app>
2024-03-11 19:27:03 +00:00
Origaming
83853a3814 Translated using Weblate (French)
Currently translated at 100.0% (328 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/fr/
Author: Origaming <mabarleycorne@gmail.com>
2024-03-11 19:27:03 +00:00
teaishealthy
da2472ac5d Translated using Weblate (German)
Currently translated at 100.0% (328 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/de/
Author: teaishealthy <teaishealthy@protonmail.com>
2024-03-11 19:27:03 +00:00
thehairy
0aac489417 Translated using Weblate (German)
Currently translated at 100.0% (328 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/de/
Author: thehairy <soeren.stabenow@outlook.com>
2024-03-11 19:27:03 +00:00
Ninguart
c928a8093d Translated using Weblate (Czech)
Currently translated at 98.4% (323 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/cs/
Author: Ninguart <ninguart@users.noreply.weblate.movie-web.app>
2024-03-11 19:27:03 +00:00
Alja Slenc
11df7450b0 Translated using Weblate (Slovenian)
Currently translated at 100.0% (328 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/sl/
Author: Alja Slenc <alja.slenc@gmail.com>
2024-03-11 18:55:52 +00:00
Alex
8fd409fa1f Translated using Weblate (Russian)
Currently translated at 100.0% (328 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/ru/
Author: Alex <aslanych99@mail.ru>
2024-03-11 18:55:52 +00:00
Raymond Nee
5d25977ba8 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (328 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/zh_Hans/
Author: Raymond Nee <monstorix@outlook.com>
2024-03-11 18:55:52 +00:00
William Oldham
0e2e7abf53 Bump version 2024-03-11 18:55:27 +00:00
William Oldham
c470c34ea8 Merge pull request #999 from qtchaos/fix/subtitles
Update maximums of subtitle settings and add padding
2024-03-11 18:53:26 +00:00
William Oldham
d33faf370f Bump padding to pb-5 to match top of card 2024-03-11 18:48:52 +00:00
William Oldham
64c0a6b7a8 Use deep merge on store to ensure settings are defaulted correctly 2024-03-11 18:46:09 +00:00
qtchaos
7c5c5daf5c fix: add minimums & maximums for other settings 2024-03-11 17:59:55 +02:00
qtchaos
35c9c85b86 fix: increase maximum subtitle size, set background blur max to 100% and increase padding on the bottom of captions view 2024-03-11 17:46:47 +02:00
William Oldham
59958487d2 Merge pull request #973 from movie-web/dev
Version 4.6
2024-03-10 23:19:06 +00:00
William Oldham
1fd3e37fa4 Merge branch 'master' into dev 2024-03-10 23:16:25 +00:00
William Oldham
d5ac1d5db2 Merge pull request #976 from gh-movie-web/weblate-movie-web-website
Translations update from movie-web weblate
2024-03-10 23:16:12 +00:00
admin
cfaf2130e4 Deleted translation using Weblate (Albanian)
Author: admin <admin@movie-web.app>
2024-03-10 23:14:11 +00:00
chaos
57a747099e Translated using Weblate (Estonian)
Currently translated at 100.0% (328 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/et/
Author: chaos <chaos@users.noreply.weblate.movie-web.app>
2024-03-10 23:09:21 +00:00
Jimin
3cab6ab3c3 Translated using Weblate (Korean)
Currently translated at 99.6% (326 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/ko/
Author: Jimin <jiminlee102@gmail.com>
2024-03-10 23:09:21 +00:00
Alex
dd0b9f60c9 Translated using Weblate (Russian)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/ru/
Author: Alex <aslanych99@mail.ru>
2024-03-10 23:09:21 +00:00
Matias Bubello
5754215725 Translated using Weblate (Spanish)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/es/
Author: Matias Bubello <matt.bubello@gmail.com>
2024-03-10 23:09:21 +00:00
aryiu
7ffb904f9e Translated using Weblate (Valencian)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/ca@valencia/
Author: aryiu <aryiu@users.noreply.weblate.476328473.xyz>
2024-03-10 23:09:21 +00:00
chaos
90e1d3c369 Translated using Weblate (Estonian)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/et/
Author: chaos <chaos@users.noreply.weblate.movie-web.app>
2024-03-10 23:09:21 +00:00
Juled Zaganjori
27297227e6 Added translation using Weblate (Albanian)
Author: Juled Zaganjori <zaganjorijuled@gmail.com>
2024-03-10 23:09:21 +00:00
William Oldham
9cb6147017 Bump version to 4.6 2024-03-10 23:09:09 +00:00
chaos
3d333dcb03 Merge pull request #993 from ssssobek/dev
Add blurred background option for subtitles
2024-03-11 01:05:50 +02:00
ssssobek
67af11fd85 Set the default background blur to 50% 2024-03-10 23:56:06 +01:00
ssssobek
ca180ab9ea Go back to using percentages instead of pixels 2024-03-10 23:10:25 +01:00
ssssobek
e09c04b57b Add blurred backgroud for subtitles 2024-03-10 20:14:48 +01:00
William Oldham
9409922efd Merge pull request #721 from qtchaos/mediasession
Implement MediaSession support
2024-03-07 15:54:57 +00:00
William Oldham
425c7beeea Merge branch 'dev' into mediasession 2024-03-07 15:52:35 +00:00
William Oldham
fcee7001ee Merge pull request #985 from zisra/x-button
Make x-button more visible
2024-03-06 17:06:40 +00:00
4a5d537679 Revert all color changes 2024-03-06 11:01:46 -06:00
a652be9a86 Fix 2024-03-06 10:59:29 -06:00
7159b76344 Add onboarding styles to default theme 2024-03-06 10:58:38 -06:00
b74a4cd4c6 Make x-button more visible 2024-03-06 10:56:06 -06:00
qtchaos
256f9f9df9 feat: add season/episode to the start of title 2024-03-05 00:33:31 +02:00
William Oldham
18e9bdbfc5 Merge branch 'dev' into mediasession 2024-03-04 21:42:40 +00:00
William Oldham
89bc201b73 Merge pull request #967 from MovieWebIPFS/patch-1
feat: allow setting 'base' via VITE_BASE_URL
2024-03-04 20:38:28 +00:00
MovieWebIPFS
de2e3e6aed feat: allow setting 'base' via VITE_BASE_URL
this sets the default to `/` which results in this patch not breaking behavior. This is being used to set base as a relative path to enable hosting movie-web at any non-root location
2024-03-04 20:36:39 +00:00
William Oldham
27aff99969 Merge pull request #975 from qtchaos/fix/button-navigate
When navigating to own pages, use `useNavigate()`
2024-03-03 23:20:51 +00:00
qtchaos
f33bc583ea fix: when navigating to own pages, use useNavigate() 2024-03-03 22:47:51 +02:00
William Oldham
bada1d12cf Add Catalan (Valencia) 2024-03-03 19:13:30 +00:00
William Oldham
cf3d9fdc21 Merge pull request #964 from gh-movie-web/weblate-movie-web-website
Translations update from movie-web weblate
2024-03-03 18:50:27 +00:00
Mehdi
8a973b1d89 Translated using Weblate (Persian)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-03-03 18:48:11 +00:00
aryiu
078777f952 Translated using Weblate (Valencian)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/ca@valencia/
Author: aryiu <aryiu@users.noreply.weblate.movie-web.app>
2024-03-03 18:48:11 +00:00
aryiu
dc4ce9b91f Translated using Weblate (Catalan)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/ca/
Author: aryiu <aryiu@users.noreply.weblate.movie-web.app>
2024-03-03 18:48:11 +00:00
Matic Bončina
56413183b6 Translated using Weblate (Slovenian)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/sl/
Author: Matic Bončina <github@express.ninja>
2024-03-03 18:48:11 +00:00
aryiu
f89759e9b9 Translated using Weblate (Catalan)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/ca/
Author: aryiu <aryiu@users.noreply.weblate.movie-web.app>
2024-03-03 18:48:11 +00:00
Fluhfi
9c03cef941 Translated using Weblate (Punjabi)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/pa/
Author: Fluhfi <aseemsharma2992@gmail.com>
2024-03-03 18:48:11 +00:00
Mehdi
49fe07b208 Translated using Weblate (Persian)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-03-03 18:48:11 +00:00
aryiu
5e0b434ea7 Added translation using Weblate (Valencian)
Author: aryiu <aryiu@users.noreply.weblate.movie-web.app>
2024-03-03 18:48:11 +00:00
NidaleNieve
e24697f723 Translated using Weblate (Icelandic)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/is/
Author: NidaleNieve <nidale16@outlook.com>
2024-03-03 18:48:11 +00:00
Mehdi
00700408fb Translated using Weblate (Persian)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-03-03 18:48:11 +00:00
Aayush Shah
2a3ae861cc Translated using Weblate (Hindi)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/hi/
Author: Aayush Shah <shahaayush999@gmail.com>
2024-03-03 18:48:11 +00:00
n1ck
2804b2addd Translated using Weblate (Spanish)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/es/
Author: n1ck <n1ck.lim@proton.me>
2024-03-03 18:48:11 +00:00
Aayush Shah
dcfbf6b266 Translated using Weblate (Nepali)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/ne/
Author: Aayush Shah <shahaayush999@gmail.com>
2024-03-03 18:48:11 +00:00
Raymond Nee
7f859e0bfd Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/zh_Hans/
Author: Raymond Nee <monstorix@outlook.com>
2024-03-03 18:48:11 +00:00
blikje
05741ed632 Translated using Weblate (Dutch)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/nl/
Author: blikje <Blikje7up@gmail.com>
2024-03-03 18:48:11 +00:00
Jamie Poznanski
dc04390172 Translated using Weblate (Italian)
Currently translated at 100.0% (327 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/it/
Author: Jamie Poznanski <enby_jamie@users.noreply.weblate.movie-web.app>
2024-03-03 18:48:11 +00:00
EPScorp
228ac6fd6c Translated using Weblate (French)
Currently translated at 98.1% (321 of 327 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/fr/
Author: EPScorp <robotvolant168@gmail.com>
2024-03-03 18:48:11 +00:00
William Oldham
22cb2a259d Merge pull request #972 from movie-web/config-improvements
Config improvements
2024-03-03 18:48:05 +00:00
William Oldham
fa8548d3c2 Default config.js to not have TMDB set 2024-03-03 18:37:57 +00:00
William Oldham
404d3b885f Handle nullability of config fields 2024-03-03 18:37:43 +00:00
William Oldham
b560445659 Return null values where appr and handle the env being blank 2024-03-03 18:37:28 +00:00
William Oldham
e555354e17 If TMDB key is empty, don't attempt request 2024-03-03 18:36:12 +00:00
William Oldham
95a75f81b1 Merge pull request #961 from binaryoverload/dev
Add vercel config to properly support non-hash router
2024-03-01 19:30:11 +00:00
William Oldham
818e159586 Create vercel.json 2024-03-01 17:17:09 +00:00
William Oldham
a0067cb2f2 Merge pull request #955 from movie-web/dev
Version 4.5.1
2024-02-29 23:31:00 +00:00
William Oldham
9658c61cd0 Merge branch 'dev' of https://github.com/movie-web/movie-web into dev 2024-02-29 23:27:52 +00:00
William Oldham
34cb2bfb36 Bump version to 4.5.1 2024-02-29 23:27:50 +00:00
William Oldham
0f1aab605d Merge branch 'master' into dev 2024-02-29 23:19:07 +00:00
William Oldham
6388ebb8ef Update providers to 2.2.2 2024-02-29 23:18:15 +00:00
William Oldham
5e334246f4 Fix linting 2024-02-29 23:17:52 +00:00
William Oldham
10fa48ba04 Merge pull request #958 from movie-web/965-backend-https-default
Automatically prefix backend url with https:// if not provided
2024-02-29 22:01:49 +00:00
William Oldham
9fc3e9082f feat: automatically prefix backend url with https:// if not provided 2024-02-29 22:00:07 +00:00
William Oldham
d4463cbccd Merge pull request #907 from gh-movie-web/weblate-movie-web-website
Translations update from movie-web weblate
2024-02-29 18:55:40 +00:00
Mehdi
ffb468a450 Translated using Weblate (Persian)
Currently translated at 100.0% (325 of 325 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-02-29 18:51:21 +00:00
Matic Bončina
ceb0c1e35a Translated using Weblate (Slovenian)
Currently translated at 100.0% (325 of 325 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/sl/
Author: Matic Bončina <github@express.ninja>
2024-02-29 18:51:21 +00:00
Matic Boncina
96c5a3387d Translated using Weblate (Slovenian)
Currently translated at 100.0% (325 of 325 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/sl/
Author: Matic Boncina <476328473@express.ninja>
2024-02-29 18:51:21 +00:00
Aayush Shah
dec42deaef Translated using Weblate (Nepali)
Currently translated at 100.0% (325 of 325 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/ne/
Author: Aayush Shah <shahaayush999@gmail.com>
2024-02-29 18:51:21 +00:00
Jamie Poznanski
866142ac57 Translated using Weblate (Italian)
Currently translated at 100.0% (325 of 325 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/it/
Author: Jamie Poznanski <enby_jamie@users.noreply.weblate.movie-web.app>
2024-02-29 18:51:21 +00:00
Raymond Nee
204ec6421d Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (325 of 325 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/zh_Hans/
Author: Raymond Nee <monstorix@outlook.com>
2024-02-29 18:51:21 +00:00
Weblate
3cbffb3981 Update translation files
Updated by "Cleanup translation files" hook in Weblate.

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/
2024-02-29 18:51:21 +00:00
Mehdi
f0e3262a71 Translated using Weblate (Persian)
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-02-29 18:51:21 +00:00
Ninguart
5aecb988de Translated using Weblate (Czech)
Currently translated at 98.4% (316 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/cs/
Author: Ninguart <ninguart@users.noreply.weblate.movie-web.app>
2024-02-29 18:51:21 +00:00
Vijay
e34995fa98 Translated using Weblate (Tamil)
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/ta/
Author: Vijay <vcmvijay@gmail.com>
2024-02-29 18:51:21 +00:00
Mehdi
1c887b132d Translated using Weblate (Persian)
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-02-29 18:51:21 +00:00
Vijay
76cd8847d3 Translated using Weblate (Tamil)
Currently translated at 23.0% (74 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/ta/
Author: Vijay <vcmvijay@gmail.com>
2024-02-29 18:51:21 +00:00
Jamie Poznanski
78d1c4f740 Translated using Weblate (Italian)
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/it/
Author: Jamie Poznanski <enby_jamie@users.noreply.weblate.movie-web.app>
2024-02-29 18:51:21 +00:00
Mehdi
5ebff1276c Translated using Weblate (Persian)
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-02-29 18:51:21 +00:00
Mehdi
e10463d60a Translated using Weblate (Persian)
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-02-29 18:51:21 +00:00
Raymond Nee
65e6db761d Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/zh_Hans/
Author: Raymond Nee <monstorix@outlook.com>
2024-02-29 18:51:21 +00:00
Stella Vanella
89929be12c Translated using Weblate (Vietnamese)
Currently translated at 34.2% (110 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/vi/
Author: Stella Vanella <stellavu2018@gmail.com>
2024-02-29 18:51:21 +00:00
aryiu
a3b2b741c8 Translated using Weblate (Catalan)
Currently translated at 87.5% (281 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/ca/
Author: aryiu <aryiu@users.noreply.weblate.movie-web.app>
2024-02-29 18:51:21 +00:00
Alex
d28c89eb83 Translated using Weblate (Russian)
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/ru/
Author: Alex <aslanych99@mail.ru>
2024-02-29 18:51:21 +00:00
Tauan
2dc7affd94 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/pt_BR/
Author: Tauan <matheus-tauan@hotmail.com>
2024-02-29 18:51:21 +00:00
Alex
ad745eb532 Translated using Weblate (Spanish)
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/es/
Author: Alex <alejandroreizq@duck.com>
2024-02-29 18:51:21 +00:00
Raymond Nee
fc4920699b Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/zh_Hans/
Author: Raymond Nee <monstorix@outlook.com>
2024-02-29 18:51:21 +00:00
Jamie Poznanski
c71c0f6ae7 Translated using Weblate (Italian)
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/it/
Author: Jamie Poznanski <enby_jamie@users.noreply.weblate.movie-web.app>
2024-02-29 18:51:21 +00:00
Guillaume
5804838f5e Translated using Weblate (French)
Currently translated at 100.0% (321 of 321 strings)

Translation: movie-web/website
Translate-URL: https://weblate.movie-web.app/projects/movie-web/website/fr/
Author: Guillaume <guillaumepeeklo@gmail.com>
2024-02-29 18:51:21 +00:00
chaos
cbb699b767 Merge pull request #947 from movie-web/fix/#905
Extension: Prepare stream for all domains in a hls playlist
2024-02-28 16:58:36 +02:00
chaos
4ec89456ac Merge branch 'dev' into fix/#905 2024-02-28 16:50:46 +02:00
William Oldham
acec24b9b5 Merge pull request #949 from movie-web/feature/#944
Add dismissable banners
2024-02-28 11:31:32 +00:00
Jorrin
5a78e48dfe add dismissable banners 2024-02-28 00:48:49 +01:00
Jorrin
d20ab4be08 double map into one 2024-02-27 23:30:00 +01:00
Jorrin
7af58dd9e8 prepare stream for all domains in a hls playlist 2024-02-27 23:19:40 +01:00
William Oldham
8643b7c584 Merge pull request #946 from Audiosutras/dev
Update Dockerfile and add compose.yaml file
2024-02-27 20:10:20 +00:00
Audiosutras
7b1330f664 Update docker-compose.yaml
Co-authored-by: Timon Klinkert <83671398+DenuxPlays@users.noreply.github.com>
2024-02-27 15:05:22 -05:00
Christopher Dixon
916d15d2f0 add commented out build args to docker-compose.yaml 2024-02-27 15:02:45 -05:00
Christopher Dixon
d92a4de948 peer review feedback 2024-02-27 14:55:05 -05:00
Christopher Dixon
2cfa5acb35 update Dockerfile & add compose.yaml file 2024-02-27 14:38:48 -05:00
William Oldham
1acefd75fa Merge pull request #929 from movie-web/dev
Version 4.5.0
2024-02-26 21:39:25 +00:00
William Oldham
86a3136fd0 Bump version 2024-02-26 21:34:51 +00:00
William Oldham
7c4c02dd83 Fix theme preview 2024-02-26 21:27:45 +00:00
William Oldham
261ef5e6c5 Use quick links 2024-02-26 21:27:31 +00:00
William Oldham
843ec84936 Merge pull request #922 from marcoslor/868-add-appearance-preview
Add appearance preview
2024-02-26 21:13:06 +00:00
William Oldham
ba4713b130 Merge pull request #943 from qtchaos/scroll-volume
Allow users to change volume with scroll wheel
2024-02-26 20:56:30 +00:00
qtchaos
67c86a270e feat: make volume scrollable with your mouse 2024-02-26 22:39:32 +02:00
William Oldham
c1731fdbc9 Merge pull request #942 from qtchaos/google-analytics
Make Google Analytics configurable with VITE_GA_ID
2024-02-26 18:35:05 +00:00
qtchaos
0e72829dd7 feat: make Google Analytics configurable with VITE_GA_ID 2024-02-26 19:40:35 +02:00
William Oldham
02fceb7f8e Merge pull request #941 from qtchaos/fix-download
Fix media download button redirecting to incorrect URL on main tab
2024-02-26 15:09:35 +00:00
qtchaos
5321afe2cd chore: remove useNavigate import 2024-02-26 17:06:35 +02:00
qtchaos
e8d8c16d41 fix: use window.open instead of react-router on href within Button 2024-02-26 16:56:46 +02:00
William Oldham
d1c58041c4 Merge pull request #940 from qtchaos/remove-default-setup
Disable default setup option if no proxies are set, remove extension help once it succeeds
2024-02-26 12:12:03 +00:00
qtchaos
f93e9288b5 feat: remove default VITE_CORS_PROXY_URL 2024-02-26 14:06:33 +02:00
qtchaos
1df0ac000d feat: disable default setup option if no proxies set, remove extension help on OK 2024-02-26 13:48:45 +02:00
William Oldham
0a86c59cfb Merge pull request #939 from qtchaos/admin-update
Update the admin page to run workers in parallel
2024-02-25 22:20:15 +00:00
William Oldham
9e4241e464 Merge pull request #938 from qtchaos/remove-domain
Remove references to official domain
2024-02-25 22:19:14 +00:00
qtchaos
bf6424f75d feat: run worker tests in parallel with 5 sec cooldown 2024-02-25 22:04:47 +02:00
qtchaos
130b4f5cc3 feat: disable creating account when server is not set 2024-02-25 21:31:08 +02:00
qtchaos
fcf42a4e8a fix: change useBackendUrl to possibly be undefined, add checks to avoid useless requests to nonexistent backend 2024-02-24 02:08:01 +02:00
qtchaos
b18269b40e chore: Remove references to official domain 2024-02-23 16:26:17 +02:00
Marcos Rios
ac9d347a26 Remove nonsense ref 2024-02-22 23:55:07 -03:00
William Oldham
c6fe62ae8a Merge branch 'master' into dev 2024-02-21 19:09:16 +00:00
William Oldham
87c6be9a61 Merge pull request #915 from movie-web/fix/#904
route subtitles through extension if installed
2024-02-21 19:07:23 +00:00
William Oldham
f4514b283c Merge branch 'dev' into fix/#904 2024-02-21 19:05:29 +00:00
William Oldham
48a37112c6 Merge pull request #920 from vijaysingh2219/dev
Add dynamic and engaging search bar placeholders
2024-02-21 18:11:06 +00:00
William Oldham
ac119a42a0 Merge branch 'dev' into dev 2024-02-21 18:08:17 +00:00
William Oldham
12274db582 Merge pull request #928 from movie-web/fix/#927
Fix #927 and add build arg for Docker PWA
2024-02-21 18:04:50 +00:00
William Oldham
98d06a6051 Fix #927 and add build arg for Docker PWA 2024-02-21 17:54:01 +00:00
Vijay
446be58478 Fix ESLint warning in HeroPart.tsx 2024-02-20 16:18:36 +05:30
Vijay
60d5c5d05c Updated the extra search placeholder options
- Reduced the number of options for search placeholder
2024-02-20 15:29:44 +05:30
Vijay
b1741a3264 Updated 'placeholder' key structure across all locale files 2024-02-19 18:21:34 +05:30
Vijay
f5c1d6eb86 Add dynamic and engaging search bar placeholders
Enhance search bar experience with 15 dynamic placeholders for the movie web app. Provides a mix of engaging questions and suggestions, promoting user interaction and a personalized feel. Shuffled for a random appearance, contributing to a more dynamic user experience.
2024-02-17 23:31:55 +05:30
Jorrin
121a71449f route subtitles through extension if installed 2024-02-15 14:17:57 +01:00
William Oldham
07b5c95607 Merge pull request #909 from movie-web/dev
Version 4.4.2: Fix extension compatibility
2024-02-14 10:20:59 +00:00
William Oldham
eca8e1cff9 Merge pull request #897 from movie-web/dev
Version 4.4.1 - Fix HLS i18n key
2024-02-11 13:51:34 +00:00
chaos
b129181b6c Merge branch 'dev' into mediasession 2024-02-09 15:52:31 +00:00
qtchaos
761e952ce2 fix: update if conditions to allow for updates after changing episodes 2024-02-09 17:51:47 +02:00
mrjvs
a2a3066bc7 Merge branch 'dev' into mediasession 2024-01-31 16:56:36 +01:00
chaos
224d823eb6 Merge branch 'dev' into mediasession 2024-01-11 22:25:07 +02:00
qtchaos
c6f359d4ea Remove unused import in MediaSession.ts 2024-01-06 19:31:05 +02:00
chaos
b952d0a4d0 Merge branch 'dev' into mediasession 2024-01-06 18:44:37 +02:00
qtchaos
fb68efa522 Rework MediaSession to be less bad 2024-01-06 18:43:42 +02:00
mrjvs
949cc216c7 Merge branch 'dev' into mediasession 2024-01-06 15:31:01 +01:00
qtchaos
00066ba788 somewhat ok mediasession implementation 2024-01-05 21:19:59 +02:00
119 changed files with 3394 additions and 516 deletions

View File

@@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at reported to the community leaders responsible for enforcement at
codeofconduct@movie-web.app. our [Discord](https://movie-web.github.io/links/discord).
All complaints will be reviewed and investigated promptly and fairly. All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the All community leaders are obligated to respect the privacy and security of the

View File

@@ -1,6 +1,6 @@
# Contributing Guidelines for movie-web # Contributing Guidelines for movie-web
Thank you for investing your time in contributing to our project! Your contribution will be reflected on [movie-web.app](https://movie-web.app). Thank you for investing your time in contributing to our project! Your contribution will be reflected on all of the community hosted instances that are on the latest version.
Please read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable. Please read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community approachable and respectable.
@@ -33,7 +33,7 @@ There are two places where to request features or report bugs:
### Discord Server ### Discord Server
If you do not have a GitHub account or want to discuss a feature or bug with us before making an issue, you can join our Discord server. If you do not have a GitHub account or want to discuss a feature or bug with us before making an issue, you can join our Discord server.
<a href="https://discord.movie-web.app"><img src="https://discord.com/api/guilds/871713465100816424/widget.png?style=banner2" alt="Discord Server"></a> <a href="https://movie-web.github.io/links/discord"><img src="https://discord.com/api/guilds/871713465100816424/widget.png?style=banner2" alt="Discord Server"></a>
### GitHub Issues ### GitHub Issues
To make a GitHub issue for movie-web, please visit the [new issue page](https://github.com/movie-web/movie-web/issues/new/choose) where you can pick either the "Bug Report" or "Feature Request" template. To make a GitHub issue for movie-web, please visit the [new issue page](https://github.com/movie-web/movie-web/issues/new/choose) where you can pick either the "Bug Report" or "Feature Request" template.
@@ -85,7 +85,8 @@ Here are some tips to make sure that your pull requests are :pinched_fingers: fi
### Language Contributions ### Language Contributions
Language contributions help movie-web massively, allowing people worldwide to use our app! Language contributions help movie-web massively, allowing people worldwide to use our app!
We use weblate for crowdsourcing our translations. [Click here to go to our translation tool.](https://weblate.movie-web.app/projects/movie-web/website/) We use Weblate for crowdsourcing our translations. [Click here to go to our translation tool.](https://movie-web.github.io/links/weblate)
1. First make sure you make an account. (click the link above) 1. First make sure you make an account. (click the link above)
2. Click the language you want to help translate, if it's not listed you can click the plus top left to add a new language. 2. Click the language you want to help translate, if it's not listed you can click the plus top left to add a new language.

9
.github/SECURITY.md vendored
View File

@@ -2,12 +2,9 @@
## Supported Versions ## Supported Versions
The movie-web maintainers only support the latest version of movie-web published at https://movie-web.app. The latest version of movie-web is the only version that is supported, as it is the only version that is being actively developed.
Support is not provided for any forks or mirrors of movie-web.
## Reporting a Vulnerability ## Reporting a Vulnerability
There are two ways you can contact the movie-web maintainers to report a vulnerability: You can contact the movie-web maintainers to report a vulnerability:
- Email [security@movie-web.app](mailto:security@movie-web.app) - Report the vulnerability in the [movie-web Discord server](https://movie-web.github.io/links/discord)
- Report the vulnerability in the [movie-web Discord server](https://discord.movie-web.app)

View File

@@ -8,6 +8,38 @@ COPY package.json ./
COPY pnpm-lock.yaml ./ COPY pnpm-lock.yaml ./
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
ARG PWA_ENABLED="false"
ARG GA_ID
ARG APP_DOMAIN
ARG OPENSEARCH_ENABLED="false"
ARG TMDB_READ_API_KEY
ARG CORS_PROXY_URL
ARG DMCA_EMAIL
ARG NORMAL_ROUTER="false"
ARG BACKEND_URL
ARG HAS_ONBOARDING="false"
ARG ONBOARDING_CHROME_EXTENSION_INSTALL_LINK
ARG ONBOARDING_PROXY_INSTALL_LINK
ARG DISALLOWED_IDS
ARG CDN_REPLACEMENTS
ARG TURNSTILE_KEY
ENV VITE_PWA_ENABLED=${PWA_ENABLED}
ENV VITE_GA_ID=${GA_ID}
ENV VITE_APP_DOMAIN=${APP_DOMAIN}
ENV VITE_OPENSEARCH_ENABLED=${OPENSEARCH_ENABLED}
ENV VITE_TMDB_READ_API_KEY=${TMDB_READ_API_KEY}
ENV VITE_CORS_PROXY_URL=${CORS_PROXY_URL}
ENV VITE_DMCA_EMAIL=${DMCA_EMAIL}
ENV VITE_NORMAL_ROUTER=${NORMAL_ROUTER}
ENV VITE_BACKEND_URL=${BACKEND_URL}
ENV VITE_HAS_ONBOARDING=${HAS_ONBOARDING}
ENV VITE_ONBOARDING_CHROME_EXTENSION_INSTALL_LINK=${ONBOARDING_CHROME_EXTENSION_INSTALL_LINK}
ENV VITE_ONBOARDING_PROXY_INSTALL_LINK=${ONBOARDING_PROXY_INSTALL_LINK}
ENV VITE_DISALLOWED_IDS=${DISALLOWED_IDS}
ENV VITE_CDN_REPLACEMENTS=${CDN_REPLACEMENTS}
ENV VITE_TURNSTILE_KEY=${TURNSTILE_KEY}
COPY . ./ COPY . ./
RUN pnpm run build RUN pnpm run build

View File

@@ -4,13 +4,13 @@
<p align="center"> <p align="center">
<img src="https://skillicons.dev/icons?i=react,vite,ts" /> <img src="https://skillicons.dev/icons?i=react,vite,ts" />
<br/> <br/>
<a href="https://discord.movie-web.app"><kbd>🔵 discord</kbd></a> <a href="https://movie-web.app"><kbd>🟢 website</kbd></a> <a href="https://movie-web.github.io/links/discord"><kbd>🔵 discord</kbd></a> <a href="https://movie-web.github.io/docs"><kbd>🟢 docs</kbd></a>
</p> </p>
<br/><br/> <br/><br/>
# ⚡What is movie-web? # ⚡What is movie-web?
movie-web is a web app for watching movies easily. Check it out at <a href="https://movie-web.app"><kbd>movie-web.app</kbd></a>. movie-web is a web app for watching movies easily.
This service works by displaying video files from third-party providers inside an intuitive and aesthetic user interface. This service works by displaying video files from third-party providers inside an intuitive and aesthetic user interface.
@@ -57,7 +57,7 @@ pnpm build
A simple guide has been written to assist in hosting your own instance of movie-web. Check it out below A simple guide has been written to assist in hosting your own instance of movie-web. Check it out below
|[Selfhosting guide](https://docs.movie-web.app)| |[Selfhosting guide](https://movie-web.github.io/docs)|
|---| |---|
## 🤝 Thanks to all Contributors ## 🤝 Thanks to all Contributors

26
docker-compose.yaml Normal file
View File

@@ -0,0 +1,26 @@
version: "3.8"
services:
movieweb:
build:
context: .
# args:
# PWA_ENABLED: "false"
# GA_ID: ""
# APP_DOMAIN: ""
# OPENSEARCH_ENABLED: "false"
# TMDB_READ_API_KEY: ""
# CORS_PROXY_URL: ""
# DMCA_EMAIL: ""
# NORMAL_ROUTER: "false"
# BACKEND_URL: ""
# HAS_ONBOARDING: "false"
# ONBOARDING_CHROME_EXTENSION_INSTALL_LINK: ""
# ONBOARDING_PROXY_INSTALL_LINK: ""
# DISALLOWED_IDS: ""
# CDN_REPLACEMENTS: ""
# TURNSTILE_KEY: ""
ports:
- "80:80"
restart: unless-stopped

View File

@@ -1,8 +1,8 @@
{ {
"name": "movie-web", "name": "movie-web",
"version": "4.4.2", "version": "4.6.2",
"private": true, "private": true,
"homepage": "https://movie-web.app", "homepage": "https://github.com/movie-web/movie-web",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
@@ -29,7 +29,7 @@
"@formkit/auto-animate": "^0.8.1", "@formkit/auto-animate": "^0.8.1",
"@headlessui/react": "^1.7.17", "@headlessui/react": "^1.7.17",
"@ladjs/country-language": "^1.0.3", "@ladjs/country-language": "^1.0.3",
"@movie-web/providers": "^2.2.0", "@movie-web/providers": "^2.2.3",
"@noble/hashes": "^1.3.3", "@noble/hashes": "^1.3.3",
"@plasmohq/messaging": "^0.6.1", "@plasmohq/messaging": "^0.6.1",
"@react-spring/web": "^9.7.3", "@react-spring/web": "^9.7.3",
@@ -44,11 +44,12 @@
"focus-trap-react": "^10.2.3", "focus-trap-react": "^10.2.3",
"fscreen": "^1.2.0", "fscreen": "^1.2.0",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"hls.js": "^1.4.14", "hls.js": "^1.5.7",
"i18next": "^23.7.11", "i18next": "^23.7.11",
"immer": "^10.0.3", "immer": "^10.0.3",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",
"lodash.isequal": "^4.5.0", "lodash.isequal": "^4.5.0",
"lodash.merge": "^4.6.2",
"million": "^2.6.4", "million": "^2.6.4",
"nanoid": "^5.0.4", "nanoid": "^5.0.4",
"node-forge": "^1.3.1", "node-forge": "^1.3.1",
@@ -67,6 +68,7 @@
"semver": "^7.5.4", "semver": "^7.5.4",
"slugify": "^1.6.6", "slugify": "^1.6.6",
"subsrt-ts": "^2.1.2", "subsrt-ts": "^2.1.2",
"zod": "^3.22.4",
"zustand": "^4.4.7" "zustand": "^4.4.7"
}, },
"devDependencies": { "devDependencies": {
@@ -79,6 +81,7 @@
"@types/dompurify": "^3.0.5", "@types/dompurify": "^3.0.5",
"@types/fscreen": "^1.0.4", "@types/fscreen": "^1.0.4",
"@types/lodash.isequal": "^4.5.8", "@types/lodash.isequal": "^4.5.8",
"@types/lodash.merge": "^4.6.9",
"@types/lodash.throttle": "^4.1.9", "@types/lodash.throttle": "^4.1.9",
"@types/node": "^20.10.5", "@types/node": "^20.10.5",
"@types/pako": "^2.0.3", "@types/pako": "^2.0.3",
@@ -123,7 +126,8 @@
"vite-plugin-package-version": "^1.1.0", "vite-plugin-package-version": "^1.1.0",
"vite-plugin-pwa": "^0.17.4", "vite-plugin-pwa": "^0.17.4",
"vite-plugin-static-copy": "^1.0.0", "vite-plugin-static-copy": "^1.0.0",
"vitest": "^1.1.0" "vitest": "^1.1.0",
"workbox-window": "^7.0.0"
}, },
"pnpm": { "pnpm": {
"overrides": { "overrides": {

97
pnpm-lock.yaml generated
View File

@@ -22,8 +22,8 @@ dependencies:
specifier: ^1.0.3 specifier: ^1.0.3
version: 1.0.3 version: 1.0.3
'@movie-web/providers': '@movie-web/providers':
specifier: ^2.2.0 specifier: ^2.2.3
version: 2.2.0 version: 2.2.3
'@noble/hashes': '@noble/hashes':
specifier: ^1.3.3 specifier: ^1.3.3
version: 1.3.3 version: 1.3.3
@@ -67,8 +67,8 @@ dependencies:
specifier: ^7.0.0 specifier: ^7.0.0
version: 7.0.0 version: 7.0.0
hls.js: hls.js:
specifier: ^1.4.14 specifier: ^1.5.7
version: 1.4.14 version: 1.5.7
i18next: i18next:
specifier: ^23.7.11 specifier: ^23.7.11
version: 23.7.11 version: 23.7.11
@@ -81,6 +81,9 @@ dependencies:
lodash.isequal: lodash.isequal:
specifier: ^4.5.0 specifier: ^4.5.0
version: 4.5.0 version: 4.5.0
lodash.merge:
specifier: ^4.6.2
version: 4.6.2
million: million:
specifier: ^2.6.4 specifier: ^2.6.4
version: 2.6.4 version: 2.6.4
@@ -135,6 +138,9 @@ dependencies:
subsrt-ts: subsrt-ts:
specifier: ^2.1.2 specifier: ^2.1.2
version: 2.1.2 version: 2.1.2
zod:
specifier: ^3.22.4
version: 3.22.4
zustand: zustand:
specifier: ^4.4.7 specifier: ^4.4.7
version: 4.4.7(@types/react@18.2.45)(immer@10.0.3)(react@18.2.0) version: 4.4.7(@types/react@18.2.45)(immer@10.0.3)(react@18.2.0)
@@ -167,6 +173,9 @@ devDependencies:
'@types/lodash.isequal': '@types/lodash.isequal':
specifier: ^4.5.8 specifier: ^4.5.8
version: 4.5.8 version: 4.5.8
'@types/lodash.merge':
specifier: ^4.6.9
version: 4.6.9
'@types/lodash.throttle': '@types/lodash.throttle':
specifier: ^4.1.9 specifier: ^4.1.9
version: 4.1.9 version: 4.1.9
@@ -268,7 +277,7 @@ devDependencies:
version: 0.5.9(prettier@3.1.1) version: 0.5.9(prettier@3.1.1)
rollup-plugin-visualizer: rollup-plugin-visualizer:
specifier: ^5.11.0 specifier: ^5.11.0
version: 5.11.0(@rollup/wasm-node@4.10.0) version: 5.11.0(@rollup/wasm-node@4.13.0)
tailwind-scrollbar: tailwind-scrollbar:
specifier: ^3.0.5 specifier: ^3.0.5
version: 3.0.5(tailwindcss@3.4.0) version: 3.0.5(tailwindcss@3.4.0)
@@ -302,6 +311,9 @@ devDependencies:
vitest: vitest:
specifier: ^1.1.0 specifier: ^1.1.0
version: 1.1.0(@types/node@20.10.5)(jsdom@23.0.1) version: 1.1.0(@types/node@20.10.5)(jsdom@23.0.1)
workbox-window:
specifier: ^7.0.0
version: 7.0.0
packages: packages:
@@ -1933,8 +1945,8 @@ packages:
engines: {node: '>= 14'} engines: {node: '>= 14'}
dev: false dev: false
/@movie-web/providers@2.2.0: /@movie-web/providers@2.2.3:
resolution: {integrity: sha512-7rKUpLPklwOtS5P2CAeh0P3sPIuYvtkKIgm0kVMp+OsSpKd9IcuYm79bbDrA0MDi3IMGik1W6la9Mzy91+8uYQ==} resolution: {integrity: sha512-0axy02Zzlk7Tvtalc/Ebv9u5vzUPVWmQm0Ts5+6l6KPU41JUdLnFgmOl0yf0lbNeHRNSTx5SDlvWcYNL8rgpyA==}
dependencies: dependencies:
cheerio: 1.0.0-rc.12 cheerio: 1.0.0-rc.12
cookie: 0.6.0 cookie: 0.6.0
@@ -2059,7 +2071,7 @@ packages:
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
dev: false dev: false
/@rollup/plugin-babel@5.3.1(@babel/core@7.23.6)(@rollup/wasm-node@4.10.0): /@rollup/plugin-babel@5.3.1(@babel/core@7.23.6)(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
peerDependencies: peerDependencies:
@@ -2072,36 +2084,36 @@ packages:
dependencies: dependencies:
'@babel/core': 7.23.6 '@babel/core': 7.23.6
'@babel/helper-module-imports': 7.22.15 '@babel/helper-module-imports': 7.22.15
'@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.10.0) '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.13.0)
rollup: /@rollup/wasm-node@4.10.0 rollup: /@rollup/wasm-node@4.13.0
dev: true dev: true
/@rollup/plugin-node-resolve@11.2.1(@rollup/wasm-node@4.10.0): /@rollup/plugin-node-resolve@11.2.1(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
peerDependencies: peerDependencies:
rollup: npm:@rollup/wasm-node rollup: npm:@rollup/wasm-node
dependencies: dependencies:
'@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.10.0) '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.13.0)
'@types/resolve': 1.17.1 '@types/resolve': 1.17.1
builtin-modules: 3.3.0 builtin-modules: 3.3.0
deepmerge: 4.3.1 deepmerge: 4.3.1
is-module: 1.0.0 is-module: 1.0.0
resolve: 1.22.4 resolve: 1.22.4
rollup: /@rollup/wasm-node@4.10.0 rollup: /@rollup/wasm-node@4.13.0
dev: true dev: true
/@rollup/plugin-replace@2.4.2(@rollup/wasm-node@4.10.0): /@rollup/plugin-replace@2.4.2(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==}
peerDependencies: peerDependencies:
rollup: npm:@rollup/wasm-node rollup: npm:@rollup/wasm-node
dependencies: dependencies:
'@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.10.0) '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.13.0)
magic-string: 0.25.9 magic-string: 0.25.9
rollup: /@rollup/wasm-node@4.10.0 rollup: /@rollup/wasm-node@4.13.0
dev: true dev: true
/@rollup/pluginutils@3.1.0(@rollup/wasm-node@4.10.0): /@rollup/pluginutils@3.1.0(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
engines: {node: '>= 8.0.0'} engines: {node: '>= 8.0.0'}
peerDependencies: peerDependencies:
@@ -2110,11 +2122,11 @@ packages:
'@types/estree': 0.0.39 '@types/estree': 0.0.39
estree-walker: 1.0.1 estree-walker: 1.0.1
picomatch: 2.3.1 picomatch: 2.3.1
rollup: /@rollup/wasm-node@4.10.0 rollup: /@rollup/wasm-node@4.13.0
dev: true dev: true
/@rollup/wasm-node@4.10.0: /@rollup/wasm-node@4.13.0:
resolution: {integrity: sha512-wH/ih4T/iP2PUyTrkyioZqDoFY/gmu63LPLTOM5Q21gSB/D3Ejw3UBpUOMLt86fIbN3mV+wL45MyA71XAj1ytg==} resolution: {integrity: sha512-oFX11wzU7RTaiW06WBtRpzIVN/oaG0I3XkevNO0brBklYnY9zpLhTfksN4b+TdBt6CfXV/KdVhdWLbb0fQIR7A==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'} engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true hasBin: true
dependencies: dependencies:
@@ -2259,17 +2271,23 @@ packages:
/@types/lodash.isequal@4.5.8: /@types/lodash.isequal@4.5.8:
resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==}
dependencies: dependencies:
'@types/lodash': 4.14.197 '@types/lodash': 4.14.202
dev: true
/@types/lodash.merge@4.6.9:
resolution: {integrity: sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==}
dependencies:
'@types/lodash': 4.14.202
dev: true dev: true
/@types/lodash.throttle@4.1.9: /@types/lodash.throttle@4.1.9:
resolution: {integrity: sha512-PCPVfpfueguWZQB7pJQK890F2scYKoDUL3iM522AptHWn7d5NQmeS/LTEHIcLr5PaTzl3dK2Z0xSUHHTHwaL5g==} resolution: {integrity: sha512-PCPVfpfueguWZQB7pJQK890F2scYKoDUL3iM522AptHWn7d5NQmeS/LTEHIcLr5PaTzl3dK2Z0xSUHHTHwaL5g==}
dependencies: dependencies:
'@types/lodash': 4.14.197 '@types/lodash': 4.14.202
dev: true dev: true
/@types/lodash@4.14.197: /@types/lodash@4.14.202:
resolution: {integrity: sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==} resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==}
dev: true dev: true
/@types/node-forge@1.3.10: /@types/node-forge@1.3.10:
@@ -4373,8 +4391,8 @@ packages:
function-bind: 1.1.2 function-bind: 1.1.2
dev: true dev: true
/hls.js@1.4.14: /hls.js@1.5.7:
resolution: {integrity: sha512-UppQjyvPVclg+6t2KY/Rv03h0+bA5u6zwqVoz4LAC/L0fgYmIaCD7ZCrwe8WI1Gv01be1XL0QFsRbSdIHV/Wbw==} resolution: {integrity: sha512-Hnyf7ojTBtXHeOW1/t6wCBJSiK1WpoKF9yg7juxldDx8u3iswrkPt2wbOA/1NiwU4j27DSIVoIEJRAhcdMef/A==}
dev: false dev: false
/hoist-non-react-statics@3.3.2: /hoist-non-react-statics@3.3.2:
@@ -5001,7 +5019,6 @@ packages:
/lodash.merge@4.6.2: /lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
dev: true
/lodash.mergewith@4.6.2: /lodash.mergewith@4.6.2:
resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
@@ -5098,7 +5115,7 @@ packages:
'@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.6) '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.6)
'@babel/types': 7.23.6 '@babel/types': 7.23.6
kleur: 4.1.5 kleur: 4.1.5
rollup: /@rollup/wasm-node@4.10.0 rollup: /@rollup/wasm-node@4.13.0
unplugin: 1.5.1 unplugin: 1.5.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -6026,7 +6043,7 @@ packages:
glob: 7.2.3 glob: 7.2.3
dev: true dev: true
/rollup-plugin-terser@7.0.2(@rollup/wasm-node@4.10.0): /rollup-plugin-terser@7.0.2(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==}
deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser
peerDependencies: peerDependencies:
@@ -6034,12 +6051,12 @@ packages:
dependencies: dependencies:
'@babel/code-frame': 7.23.5 '@babel/code-frame': 7.23.5
jest-worker: 26.6.2 jest-worker: 26.6.2
rollup: /@rollup/wasm-node@4.10.0 rollup: /@rollup/wasm-node@4.13.0
serialize-javascript: 4.0.0 serialize-javascript: 4.0.0
terser: 5.19.3 terser: 5.19.3
dev: true dev: true
/rollup-plugin-visualizer@5.11.0(@rollup/wasm-node@4.10.0): /rollup-plugin-visualizer@5.11.0(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-exM0Ms2SN3AgTzMeW7y46neZQcyLY7eKwWAop1ZoRTCZwyrIRdMMJ6JjToAJbML77X/9N8ZEpmXG4Z/Clb9k8g==} resolution: {integrity: sha512-exM0Ms2SN3AgTzMeW7y46neZQcyLY7eKwWAop1ZoRTCZwyrIRdMMJ6JjToAJbML77X/9N8ZEpmXG4Z/Clb9k8g==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
@@ -6051,7 +6068,7 @@ packages:
dependencies: dependencies:
open: 8.4.2 open: 8.4.2
picomatch: 2.3.1 picomatch: 2.3.1
rollup: /@rollup/wasm-node@4.10.0 rollup: /@rollup/wasm-node@4.13.0
source-map: 0.7.4 source-map: 0.7.4
yargs: 17.7.2 yargs: 17.7.2
dev: true dev: true
@@ -7037,7 +7054,7 @@ packages:
'@types/node': 20.10.5 '@types/node': 20.10.5
esbuild: 0.19.10 esbuild: 0.19.10
postcss: 8.4.32 postcss: 8.4.32
rollup: /@rollup/wasm-node@4.10.0 rollup: /@rollup/wasm-node@4.13.0
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
dev: true dev: true
@@ -7299,9 +7316,9 @@ packages:
'@babel/core': 7.23.6 '@babel/core': 7.23.6
'@babel/preset-env': 7.23.6(@babel/core@7.23.6) '@babel/preset-env': 7.23.6(@babel/core@7.23.6)
'@babel/runtime': 7.23.6 '@babel/runtime': 7.23.6
'@rollup/plugin-babel': 5.3.1(@babel/core@7.23.6)(@rollup/wasm-node@4.10.0) '@rollup/plugin-babel': 5.3.1(@babel/core@7.23.6)(@rollup/wasm-node@4.13.0)
'@rollup/plugin-node-resolve': 11.2.1(@rollup/wasm-node@4.10.0) '@rollup/plugin-node-resolve': 11.2.1(@rollup/wasm-node@4.13.0)
'@rollup/plugin-replace': 2.4.2(@rollup/wasm-node@4.10.0) '@rollup/plugin-replace': 2.4.2(@rollup/wasm-node@4.13.0)
'@surma/rollup-plugin-off-main-thread': 2.2.3 '@surma/rollup-plugin-off-main-thread': 2.2.3
ajv: 8.12.0 ajv: 8.12.0
common-tags: 1.8.2 common-tags: 1.8.2
@@ -7310,8 +7327,8 @@ packages:
glob: 7.2.3 glob: 7.2.3
lodash: 4.17.21 lodash: 4.17.21
pretty-bytes: 5.6.0 pretty-bytes: 5.6.0
rollup: /@rollup/wasm-node@4.10.0 rollup: /@rollup/wasm-node@4.13.0
rollup-plugin-terser: 7.0.2(@rollup/wasm-node@4.10.0) rollup-plugin-terser: 7.0.2(@rollup/wasm-node@4.13.0)
source-map: 0.8.0-beta.0 source-map: 0.8.0-beta.0
stringify-object: 3.3.0 stringify-object: 3.3.0
strip-comments: 2.0.1 strip-comments: 2.0.1
@@ -7512,6 +7529,10 @@ packages:
engines: {node: '>=12.20'} engines: {node: '>=12.20'}
dev: true dev: true
/zod@3.22.4:
resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
dev: false
/zustand@4.4.7(@types/react@18.2.45)(immer@10.0.3)(react@18.2.0): /zustand@4.4.7(@types/react@18.2.45)(immer@10.0.3)(react@18.2.0):
resolution: {integrity: sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==} resolution: {integrity: sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==}
engines: {node: '>=12.7.0'} engines: {node: '>=12.7.0'}

View File

@@ -1,9 +1,10 @@
window.__CONFIG__ = { window.__CONFIG__ = {
// The URL for the CORS proxy, the URL must NOT end with a slash! // The URL for the CORS proxy, the URL must NOT end with a slash!
VITE_CORS_PROXY_URL: "CHANGEME", // If not specified, the onboarding will not allow a "default setup". The user will have to use the extension or set up a proxy themselves
VITE_CORS_PROXY_URL: "",
// The READ API key to access TMDB // The READ API key to access TMDB
VITE_TMDB_READ_API_KEY: "CHANGEME", VITE_TMDB_READ_API_KEY: "",
// The DMCA email displayed in the footer, null to hide the DMCA link // The DMCA email displayed in the footer, null to hide the DMCA link
VITE_DMCA_EMAIL: null, VITE_DMCA_EMAIL: null,
@@ -11,9 +12,9 @@ window.__CONFIG__ = {
// Whether to disable hash-based routing, leave this as false if you don't know what this is // Whether to disable hash-based routing, leave this as false if you don't know what this is
VITE_NORMAL_ROUTER: false, VITE_NORMAL_ROUTER: false,
// The backend URL to communicate with, defaults to the movie-web hosted one at backend.movie-web.app // The backend URL to communicate with
VITE_BACKEND_URL: null, VITE_BACKEND_URL: null,
// A comma separated list of disallowed IDs in the case of a DMCA claim - in the format "series-<id>" and "movie-<id>" // A comma separated list of disallowed IDs in the case of a DMCA claim - in the format "series-<id>" and "movie-<id>"
VITE_DISALLOWED_IDS: "" VITE_DISALLOWED_IDS: "",
}; };

View File

@@ -2,6 +2,7 @@ import ar from "@/assets/locales/ar.json";
import bg from "@/assets/locales/bg.json"; import bg from "@/assets/locales/bg.json";
import bn from "@/assets/locales/bn.json"; import bn from "@/assets/locales/bn.json";
import ca from "@/assets/locales/ca.json"; import ca from "@/assets/locales/ca.json";
import caVl from "@/assets/locales/ca@valencia.json";
import cs from "@/assets/locales/cs.json"; import cs from "@/assets/locales/cs.json";
import de from "@/assets/locales/de.json"; import de from "@/assets/locales/de.json";
import el from "@/assets/locales/el.json"; import el from "@/assets/locales/el.json";
@@ -46,6 +47,7 @@ import zh from "@/assets/locales/zh.json";
export const locales = { export const locales = {
en, en,
ca, ca,
"ca-ES": caVl,
cs, cs,
de, de,
fr, fr,
@@ -80,7 +82,7 @@ export const locales = {
ko, ko,
sl, sl,
ta, ta,
"zh-HANT": zhhant, "zh-Hant": zhhant,
is, is,
ru, ru,
gl, gl,

View File

@@ -116,27 +116,24 @@
"failed": "تعذر العثور على الوسائط، حاول مجددا!", "failed": "تعذر العثور على الوسائط، حاول مجددا!",
"loading": "جار التحميل...", "loading": "جار التحميل...",
"noResults": "لم نتمكن من العثور على أي شيء!", "noResults": "لم نتمكن من العثور على أي شيء!",
"placeholder": "ماذا تريد أن تشاهد؟", "placeholder": {
"default": "ماذا تريد أن تشاهد؟",
"extra": []
},
"sectionTitle": "نتائج البحث" "sectionTitle": "نتائج البحث"
}, },
"titles": { "titles": {
"day": { "day": {
"default": "ماذا تريد أن تشاهد في هذه الظهيرة؟", "default": "ماذا تريد أن تشاهد في هذه الظهيرة؟",
"extra": [ "extra": ["متشوق للمغامرة؟ قد يكون Jurassic Park خيارًا مثاليًا لك."]
"متشوق للمغامرة؟ قد يكون Jurassic Park خيارًا مثاليًا لك."
]
}, },
"morning": { "morning": {
"default": "ماذا تريد أن تشاهد في هذا الصباح؟", "default": "ماذا تريد أن تشاهد في هذا الصباح؟",
"extra": [ "extra": ["سمعت أن فلم \"Before Sunrise\" جيد"]
"سمعت أن فلم \"Before Sunrise\" جيد"
]
}, },
"night": { "night": {
"default": "ماذا تريد أن تشاهد في هذه الليلة؟", "default": "ماذا تريد أن تشاهد في هذه الليلة؟",
"extra": [ "extra": ["مُرهَق؟ سمعت أن فيلم \"The Exorcist\" جيد."]
"مُرهَق؟ سمعت أن فيلم \"The Exorcist\" جيد."
]
} }
} }
}, },

View File

@@ -115,7 +115,10 @@
"failed": "Неуспешно намиране на медия, опитайте отново!", "failed": "Неуспешно намиране на медия, опитайте отново!",
"loading": "Зареждане...", "loading": "Зареждане...",
"noResults": "Не успяхме да намерим нищо!", "noResults": "Не успяхме да намерим нищо!",
"placeholder": "Какво искате да гледате?", "placeholder": {
"default": "Какво искате да гледате?",
"extra": []
},
"sectionTitle": "Резултати от търсенето" "sectionTitle": "Резултати от търсенето"
}, },
"titles": { "titles": {
@@ -127,15 +130,11 @@
}, },
"morning": { "morning": {
"default": "Какво бихте искали да гледате тази сутрин?", "default": "Какво бихте искали да гледате тази сутрин?",
"extra": [ "extra": ["Чух, че Before Sunrise е добър"]
"Чух, че Before Sunrise е добър"
]
}, },
"night": { "night": {
"default": "Какво бихте искали да гледате тази вечер?", "default": "Какво бихте искали да гледате тази вечер?",
"extra": [ "extra": ["Изморен? Чух, че Екзорсистът е добър."]
"Изморен? Чух, че Екзорсистът е добър."
]
} }
} }
}, },

View File

@@ -115,27 +115,24 @@
"failed": "মিডিয়া খুঁজে পেতে ব্যর্থ, আবার চেষ্টা করুন!", "failed": "মিডিয়া খুঁজে পেতে ব্যর্থ, আবার চেষ্টা করুন!",
"loading": "লোড হচ্ছে..।", "loading": "লোড হচ্ছে..।",
"noResults": "আমরা কিছুই খুঁজে পাইনি!", "noResults": "আমরা কিছুই খুঁজে পাইনি!",
"placeholder": "আপনি কি দেখতে চান?", "placeholder": {
"default": "আপনি কি দেখতে চান?",
"extra": []
},
"sectionTitle": "অনুসন্ধান ফলাফল" "sectionTitle": "অনুসন্ধান ফলাফল"
}, },
"titles": { "titles": {
"day": { "day": {
"default": "আপনি এই বিকেলে কি দেখতে চান?", "default": "আপনি এই বিকেলে কি দেখতে চান?",
"extra": [ "extra": ["দুঃসাহসিক বোধ করছেন? জুরাসিক পার্ক নিখুঁত পছন্দ হতে পারে।"]
"দুঃসাহসিক বোধ করছেন? জুরাসিক পার্ক নিখুঁত পছন্দ হতে পারে।"
]
}, },
"morning": { "morning": {
"default": "আপনি এই সকালে কি দেখতে চান?", "default": "আপনি এই সকালে কি দেখতে চান?",
"extra": [ "extra": ["শুনি সূর্যোদয়ের আগে ভালো"]
"শুনি সূর্যোদয়ের আগে ভালো"
]
}, },
"night": { "night": {
"default": "আপনি আজ রাতে কি দেখতে চান?", "default": "আপনি আজ রাতে কি দেখতে চান?",
"extra": [ "extra": ["ক্লান্ত? আমি শুনেছি দ্য এক্সরসিস্ট ভাল।"]
"ক্লান্ত? আমি শুনেছি দ্য এক্সরসিস্ট ভাল।"
]
} }
} }
}, },

View File

@@ -3,7 +3,7 @@
"description": "movie-web és una aplicació web que cerca fluxos a internet. L'equip té com a objectiu un enfocament majoritàriament minimalista del consum de contingut.", "description": "movie-web és una aplicació web que cerca fluxos a internet. L'equip té com a objectiu un enfocament majoritàriament minimalista del consum de contingut.",
"faqTitle": "Preguntes freqüents", "faqTitle": "Preguntes freqüents",
"q1": { "q1": {
"body": "movie-web no allotja cap contingut. Quan feu clic a alguna cosa per a mirar-la, es busca a Internet el contingut seleccionat (a la pantalla de càrrega i a la pestanya «Fonts de vídeo» podeu veure quina font utilitzeu). movie-web mai hi puja contingut, tot és a través d'aquest mecanisme de cerca.", "body": "movie-web no allotja cap contingut. Quan feu clic a un contingut per a mirar-lo, es busca a Internet (a la pantalla de càrrega i a la pestanya «Fonts de vídeo» podeu veure quina font utilitzeu). movie-web mai hi puja contingut, tot és a través d'aquest mecanisme de cerca.",
"title": "D'on prové el contingut?" "title": "D'on prové el contingut?"
}, },
"q2": { "q2": {
@@ -57,6 +57,8 @@
}, },
"host": "Us esteu connectant a <0>{{hostname}}</0>. Confirmeu que hi confieu abans de crear un compte", "host": "Us esteu connectant a <0>{{hostname}}</0>. Confirmeu que hi confieu abans de crear un compte",
"no": "Torna", "no": "Torna",
"noHost": "El servidor no s'ha configurat, per tant, no es pot crear un compte",
"noHostTitle": "El servidor no està configurat!",
"title": "Confieu en aquest servidor?", "title": "Confieu en aquest servidor?",
"yes": "Confie en aquest servidor" "yes": "Confie en aquest servidor"
}, },
@@ -95,6 +97,7 @@
"about": "Quant a", "about": "Quant a",
"dmca": "DMCA", "dmca": "DMCA",
"login": "Inicia sessió", "login": "Inicia sessió",
"onboarding": "Configura",
"pagetitle": "{{title}} - movie-web", "pagetitle": "{{title}} - movie-web",
"register": "Registra", "register": "Registra",
"settings": "Configuració" "settings": "Configuració"
@@ -115,7 +118,15 @@
"failed": "No s'ha pogut trobar cap contingut, torneu-ho a provar!", "failed": "No s'ha pogut trobar cap contingut, torneu-ho a provar!",
"loading": "S'està carregant…", "loading": "S'està carregant…",
"noResults": "No hem pogut trobar res!", "noResults": "No hem pogut trobar res!",
"placeholder": "Què voleu mirar?", "placeholder": {
"default": "Què voleu mirar?",
"extra": [
"Què voleu explorar?",
"Què hi ha a la vostra llista de seguiment?",
"Quina és la vostra pel·lícula preferida?",
"Quina és la vostra sèrie preferida?"
]
},
"sectionTitle": "Resultats de la cerca" "sectionTitle": "Resultats de la cerca"
}, },
"titles": { "titles": {
@@ -165,6 +176,69 @@
"message": "Hem mirat per tot arreu: davall de les papereres, a l'armari, darrere del servidor intermediari, però al remat no hem pogut trobar la pàgina que busqueu.", "message": "Hem mirat per tot arreu: davall de les papereres, a l'armari, darrere del servidor intermediari, però al remat no hem pogut trobar la pàgina que busqueu.",
"title": "No s'ha pogut trobar la pàgina" "title": "No s'ha pogut trobar la pàgina"
}, },
"onboarding": {
"defaultConfirm": {
"cancel": "Cancel·la",
"confirm": "Usa la configuració per defecte",
"description": "La configuració per defecte no té els millors fluxos i pot ser insuportablement lenta.",
"title": "Segur?"
},
"extension": {
"back": "Torna",
"explainer": "Mitjançant l'extensió del navegador, podeu obtenir els millors fluxos que oferim. Amb només una simple instal·lació.",
"explainerIos": "Lamentablement, l'extensió del navegador no és compatible amb iOS, premeu <bold>Torna</bold> per a triar una altra opció.",
"extensionHelp": "Si heu instal·lat l'extensió, però no es detecta, <bold>obriu l'extensió al menú d'extensions del navegador</bold> i seguiu els passos en pantalla.",
"linkChrome": "Instal·la l'extensió de Chrome",
"linkFirefox": "Instal·la l'extensió de Firefox",
"notDetecting": "S'ha instal·lat a Chrome, però el lloc no el detecta? Proveu de recarregar la pàgina.",
"notDetectingAction": "Recarrega la pàgina",
"status": {
"disallowed": "L'extensió no està activada per a aquesta pàgina",
"disallowedAction": "Activa l'extensió",
"failed": "No s'ha pogut sol·licitar l'estat",
"loading": "Esperant que instal·leu l'extensió",
"outdated": "La versió de l'extensió és massa antiga",
"success": "L'extensió funciona com s'esperava!"
},
"submit": "Continua",
"title": "Comencem amb una extensió"
},
"proxy": {
"back": "Torna",
"explainer": "Amb el mètode del servidor intermediari, podeu obtenir fluxos d'alta qualitat fent un servidor intermediari propi.",
"input": {
"errorConnection": "No s'ha pogut connectar al servidor intermediari",
"errorInvalidUrl": "URL no vàlid",
"errorNotProxy": "S'esperava un servidor intermediari, però és un lloc web",
"label": "URL del servidor intermediari",
"placeholder": "https://"
},
"link": "Com fer un servidor intermediari",
"submit": "Envia el servidor intermediari",
"title": "Fem un nou servidor intermediari"
},
"start": {
"explainer": "Per a obtenir els millors fluxos possibles, haureu de triar quin mètode de transmissió voleu utilitzar.",
"options": {
"default": {
"text": "No vull fluxos de bona qualitat,<0 /> <1>utilitza la configuració per defecte</1>"
},
"extension": {
"action": "Instal·la l'extensió",
"description": "Instal·leu l'extensió del navegador i accediu a les millors fonts.",
"quality": "Millor qualitat",
"title": "Extensió del navegador"
},
"proxy": {
"action": "Configura el servidor intermediari",
"description": "Configureu un servidor intermediari en només 5 minuts i accediu a bones fonts.",
"quality": "Bona qualitat",
"title": "Servidor intermediari personalitzat"
}
},
"title": "Configurem el movie-web"
}
},
"overlays": { "overlays": {
"close": "Tanca" "close": "Tanca"
}, },
@@ -178,10 +252,11 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "Copia l'enllaç de la llista HLS",
"disclaimer": "Les baixades s'obtenen directament del proveïdor. movie-web no té control sobre com es proporcionen les baixades.", "disclaimer": "Les baixades s'obtenen directament del proveïdor. movie-web no té control sobre com es proporcionen les baixades.",
"downloadSubtitle": "Baixa els subtítols actuals", "downloadSubtitle": "Baixa els subtítols actuals",
"downloadVideo": "Baixa el vídeo", "downloadVideo": "Baixa el vídeo",
"hlsDisclaimer": "Les baixades s'obtenen directament del proveïdor. movie-web no té control sobre com es proporcionen les baixades. Tingueu en compte que esteu baixant una llista de reproducció HLS, destinada als usuaris familiaritzats amb la transmissió multimèdia avançada.", "hlsDisclaimer": "Les baixades s'obtenen directament del proveïdor. movie-web no té control sobre com es proporcionen les baixades. <br /><br />Tingueu en compte que esteu baixant una llista de reproducció HLS, <bold>no es recomana baixar-la si no esteu familiaritzat amb formats de transmissió avançats</bold>. Proveu diferents fonts per a diferents formats.",
"onAndroid": { "onAndroid": {
"1": "Per a baixar-lo a Android, feu clic al botó de baixada i, a la pàgina nova, <bold>manteniu premut</bold> el vídeo i, a continuació, seleccioneu <bold>Desa</bold>.", "1": "Per a baixar-lo a Android, feu clic al botó de baixada i, a la pàgina nova, <bold>manteniu premut</bold> el vídeo i, a continuació, seleccioneu <bold>Desa</bold>.",
"shortTitle": "Baixa / Android", "shortTitle": "Baixa / Android",
@@ -262,6 +337,17 @@
"text": "No s'han pogut carregar les metadades de l'API, comproveu la connexió a Internet.", "text": "No s'han pogut carregar les metadades de l'API, comproveu la connexió a Internet.",
"title": "No s'han pogut carregar les metadades de l'API" "title": "No s'han pogut carregar les metadades de l'API"
}, },
"dmca": {
"badge": "Eliminat",
"text": "Aquest contingut ja no està disponible a causa d'un avís de retirada o d'una reclamació de drets d'autor.",
"title": "El contingut s'ha eliminat"
},
"extensionPermission": {
"badge": "Falta el permís",
"button": "Utilitza l'extensió",
"text": "Teniu l'extensió del navegador, però necessitem el vostre permís per a començar a utilitzar l'extensió.",
"title": "Configureu l'extensió"
},
"failed": { "failed": {
"badge": "Ha fallat", "badge": "Ha fallat",
"homeButton": "Vés a l'inici", "homeButton": "Vés a l'inici",
@@ -393,16 +479,46 @@
"label": "Servidor personalitzat", "label": "Servidor personalitzat",
"urlLabel": "URL del servidor personalitzat" "urlLabel": "URL del servidor personalitzat"
}, },
"setup": {
"doSetup": "Configura",
"errorStatus": {
"description": "Sembla que heu de revisar un o més elements de la configuració.",
"title": "S'ha de revisar algun element"
},
"itemError": "Hi ha alguna cosa malament en la configuració. Torneu a fer la configuració per a solucionar-ho.",
"items": {
"default": "Configuració per defecte",
"extension": "Extensió",
"proxy": "Servidor personalitzat"
},
"redoSetup": "Reconfigura",
"successStatus": {
"description": "Tot està preparat perquè comenceu a mirar el vostre contingut preferit.",
"title": "Tot està configurat!"
},
"unsetStatus": {
"description": "Feu clic al botó de la dreta per a iniciar el procés de configuració.",
"title": "No heu fet la configuració"
}
},
"title": "Connexions", "title": "Connexions",
"workers": { "workers": {
"addButton": "Afig un «worker»", "addButton": "Afegeix un «worker»",
"description": "Per fer funcionar l'aplicació, tot el trànsit s'encamina a través de servidors intermediaris. Activeu-ho si voleu portar els vostres propis «workers».<0>Instruccions.</0>", "description": "Per fer funcionar l'aplicació, tot el trànsit s'encamina a través de servidors intermediaris. Activeu-ho si voleu portar els vostres propis «workers». <0>Instruccions.</0>",
"emptyState": "Encara no hi ha «workers», afegiu-ne un a continuació", "emptyState": "Encara no hi ha «workers», afegiu-ne un a continuació",
"label": "Utilitza «workers» intermediaris personalitzats", "label": "Utilitza «workers» intermediaris personalitzats",
"urlLabel": "URL dels «workers»", "urlLabel": "URL dels «workers»",
"urlPlaceholder": "https://" "urlPlaceholder": "https://"
} }
}, },
"preferences": {
"language": "Llengua de l'aplicació",
"languageDescription": "La llengua s'aplica a tota l'aplicació.",
"thumbnail": "Genera miniatures",
"thumbnailDescription": "Majoritàriament, els vídeos no tenen miniatures. Podeu activar aquesta opció per a generar-les sobre la marxa, però poden alentir el vídeo.",
"thumbnailLabel": "Genera miniatures",
"title": "Configuració"
},
"reset": "Restableix", "reset": "Restableix",
"save": "Desa", "save": "Desa",
"sidebar": { "sidebar": {

View File

@@ -0,0 +1,547 @@
{
"about": {
"description": "movie-web és una aplicació web que cerca fluxos a internet. L'equip té com a objectiu un enfocament majoritàriament minimalista del consum de contingut.",
"faqTitle": "Preguntes freqüents",
"q1": {
"body": "movie-web no allotja cap contingut. Quan feu clic en un contingut per a mirar-lo, es busca a Internet (a la pantalla de càrrega i a la pestanya «Fonts de vídeo» podeu mirar quina font utilitzeu). movie-web mai puja contingut, tot és a través del mecanisme de cerca.",
"title": "D'on prové el contingut?"
},
"q2": {
"body": "No és possible sol·licitar un programa o una pel·lícula, movie-web no gestiona cap contingut. Tot el contingut es visualitza a través de fonts a internet.",
"title": "On puc sol·licitar un programa o una pel·lícula?"
},
"q3": {
"body": "Els resultats de cerca funcionen amb The Movie Database (TMDB) i es mostren independentment de si les nostres fonts realment tenen el contingut.",
"title": "Els resultats de la cerca mostren el programa o la pel·lícula, per què no puc reproduir-lo?"
},
"title": "Sobre movie-web"
},
"actions": {
"copied": "S'ha copiat",
"copy": "Copia"
},
"auth": {
"createAccount": "Encara no teniu un compte? <0>Creeu un compte.</0>",
"deviceNameLabel": "Nom del dispositiu",
"deviceNamePlaceholder": "Telèfon personal",
"generate": {
"description": "La frase de contrasenya actua com a nom d'usuari i contrasenya. Assegureu-vos de mantindre-la segura, ja que haureu d'introduir-la per a iniciar la sessió al vostre compte",
"next": "He desat la frase de contrasenya",
"passphraseFrameLabel": "Frase de contrasenya",
"title": "La vostra frase de contrasenya"
},
"hasAccount": "Ja teniu un compte? <0>Inicieu sessió ací.</0>",
"login": {
"description": "Introduïu la vostra frase de contrasenya per a iniciar sessió al vostre compte",
"deviceLengthError": "Introduïu un nom per al dispositiu",
"passphraseLabel": "Frase de contrasenya de 12 paraules",
"passphrasePlaceholder": "Frase de contrasenya",
"submit": "Inicia sessió",
"title": "Inicieu sessió al vostre compte",
"validationError": "Frase de contrasenya incorrecta o incompleta"
},
"register": {
"information": {
"color1": "Color de perfil 1",
"color2": "Color de perfil 2",
"header": "Introduïu un nom per al dispositiu i trieu els colors i la icona d'usuari que vulgueu",
"icon": "Icona d'usuari",
"next": "Següent",
"title": "Informació del compte"
}
},
"trust": {
"failed": {
"text": "L'heu configurada correctament?",
"title": "No s'ha pogut accedir al servidor"
},
"host": "Esteu connectant-vos a <0>{{hostname}}</0>. Confirmeu que hi confieu abans de crear un compte",
"no": "Torna",
"noHost": "El servidor no s'ha configurat, per tant, no es pot crear un compte",
"noHostTitle": "El servidor no està configurat!",
"title": "Confieu en este servidor?",
"yes": "Confie en este servidor"
},
"verify": {
"description": "Introduïu la vostra frase de contrasenya anterior per a confirmar que l'heu desat, i crear el compte",
"invalidData": "La data no és vàlida",
"noMatch": "La frase de contrasenya no coincideix",
"passphraseLabel": "Frase de contrasenya de 12 paraules",
"recaptchaFailed": "Ha fallat la validació de ReCaptcha",
"register": "Crea el compte",
"title": "Confirmeu la frase de contrasenya"
}
},
"errors": {
"badge": "S'ha trencat",
"details": "Detalls de l'error",
"reloadPage": "Recarrega la pàgina",
"showError": "Mostra els detalls de l'error",
"title": "Hem trobat un error!"
},
"footer": {
"legal": {
"disclaimer": "Avís d'exempció de responsabilitat",
"disclaimerText": "movie-web no allotja cap fitxer, només enllaça a serveis de tercers. Els problemes legals s'han d'abordar amb qui allotja i els proveïdors de fitxers. movie-web no es fa responsable del contingut mostrat pels proveïdors de vídeo."
},
"links": {
"discord": "Discord",
"dmca": "DMCA",
"github": "GitHub"
},
"tagline": "Mireu els vostres programes i pel·lícules preferits amb esta aplicació de codi obert de reproducció en temps real."
},
"global": {
"name": "movie-web",
"pages": {
"about": "Quant a",
"dmca": "DMCA",
"login": "Inicia sessió",
"onboarding": "Configura",
"pagetitle": "{{title}} - movie-web",
"register": "Registra",
"settings": "Configuració"
}
},
"home": {
"bookmarks": {
"sectionTitle": "Marcadors"
},
"continueWatching": {
"sectionTitle": "Continueu mirant"
},
"mediaList": {
"stopEditing": "Deixa d'editar"
},
"search": {
"allResults": "Això és tot el que tenim!",
"failed": "No s'ha pogut trobar cap contingut, torneu-ho a provar!",
"loading": "S'està carregant…",
"noResults": "No hem pogut trobar res!",
"placeholder": {
"default": "Què voleu mirar?",
"extra": [
"Què voleu explorar?",
"Què hi ha a la vostra llista de seguiment?",
"Quina és la vostra pel·lícula preferida?",
"Quina és la vostra sèrie preferida?"
]
},
"sectionTitle": "Resultats de la cerca"
},
"titles": {
"day": {
"default": "Què vos agradaria mirar esta vesprada?",
"extra": [
"Voleu aventura? Jurassic Park podria ser l'elecció perfecta."
]
},
"morning": {
"default": "Què vos agradaria mirar este matí?",
"extra": [
"He sentit que «Abans de l'alba» és bona"
]
},
"night": {
"default": "Què vos agradaria mirar esta nit?",
"extra": [
"Esteu cansat? He sentit que «L'exorcista» és bona."
]
}
}
},
"media": {
"episodeDisplay": "T{{season}} E{{episode}}",
"types": {
"movie": "Pel·lícula",
"show": "Sèrie/Programa"
}
},
"navigation": {
"banner": {
"offline": "Comproveu la connexió a internet"
},
"menu": {
"about": "Quant a nosaltres",
"donation": "Feu una donació",
"logout": "Tanca la sessió",
"register": "Sincronitza al núvol",
"settings": "Configuració",
"support": "Ajuda"
}
},
"notFound": {
"badge": "No s'ha trobat",
"goHome": "Torna a l'inici",
"message": "Hem mirat per tot arreu: davall de les papereres, a l'armari, darrere del servidor intermediari, però al remat no hem pogut trobar la pàgina que busqueu.",
"title": "No s'ha pogut trobar la pàgina"
},
"onboarding": {
"defaultConfirm": {
"cancel": "Cancel·la",
"confirm": "Usa la configuració per defecte",
"description": "La configuració per defecte no té els millors fluxos i pot ser insuportablement lenta.",
"title": "Segur?"
},
"extension": {
"back": "Torna",
"explainer": "Mitjançant l'extensió del navegador, podeu obtindre els millors fluxos que oferim. Amb només una simple instal·lació.",
"explainerIos": "Lamentablement, l'extensió del navegador no és compatible amb iOS, premeu <bold>Torna</bold> per a triar una altra opció.",
"extensionHelp": "Si heu instal·lat l'extensió, però no es detecta, <bold>obriu l'extensió al menú d'extensions del navegador</bold> i seguiu els passos en pantalla.",
"linkChrome": "Instal·la l'extensió de Chrome",
"linkFirefox": "Instal·la l'extensió de Firefox",
"notDetecting": "S'ha instal·lat a Chrome, però el lloc no el detecta? Proveu de recarregar la pàgina.",
"notDetectingAction": "Recarrega la pàgina",
"status": {
"disallowed": "L'extensió no està activada per a esta pàgina",
"disallowedAction": "Activa l'extensió",
"failed": "No s'ha pogut sol·licitar l'estat",
"loading": "Esperant que instal·leu l'extensió",
"outdated": "La versió de l'extensió és massa antiga",
"success": "L'extensió funciona com s'esperava!"
},
"submit": "Continua",
"title": "Comencem amb una extensió"
},
"proxy": {
"back": "Torna",
"explainer": "Amb el mètode del servidor intermediari, podeu obtindre fluxos d'alta qualitat fent un servidor intermediari propi.",
"input": {
"errorConnection": "No s'ha pogut connectar al servidor intermediari",
"errorInvalidUrl": "URL no vàlid",
"errorNotProxy": "S'esperava un servidor intermediari, però és un lloc web",
"label": "URL del servidor intermediari",
"placeholder": "https://"
},
"link": "Com fer un servidor intermediari",
"submit": "Envia el servidor intermediari",
"title": "Fem un nou servidor intermediari"
},
"start": {
"explainer": "Per a obtindre els millors fluxos possibles, haureu de triar quin mètode de transmissió voleu utilitzar.",
"options": {
"default": {
"text": "No vull fluxos de bona qualitat,<0 /> <1>utilitza la configuració per defecte</1>"
},
"extension": {
"action": "Instal·la l'extensió",
"description": "Instal·leu l'extensió del navegador i accediu a les millors fonts.",
"quality": "Millor qualitat",
"title": "Extensió del navegador"
},
"proxy": {
"action": "Configura el servidor intermediari",
"description": "Configureu un servidor intermediari en només 5 minuts i accediu a bones fonts.",
"quality": "Bona qualitat",
"title": "Servidor intermediari personalitzat"
}
},
"title": "Configurem el movie-web"
}
},
"overlays": {
"close": "Tanca"
},
"player": {
"back": {
"default": "Torna a l'inici",
"short": "Torna"
},
"casting": {
"enabled": "S'està emetent al dispositiu…"
},
"menus": {
"downloads": {
"copyHlsPlaylist": "Copia l'enllaç de la llista HLS",
"disclaimer": "Les baixades s'obtenen directament del proveïdor. movie-web no té control sobre com es proporcionen les baixades.",
"downloadSubtitle": "Baixa els subtítols actuals",
"downloadVideo": "Baixa el vídeo",
"hlsDisclaimer": "Les baixades s'obtenen directament del proveïdor. movie-web no té control sobre com es proporcionen les baixades. <br /><br />Tingueu en compte que esteu baixant una llista de reproducció HLS, <bold>no es recomana baixar-la si no esteu familiaritzat amb formats de transmissió avançats</bold>. Proveu diferents fonts per a diferents formats.",
"onAndroid": {
"1": "Per a baixar-lo a Android, feu clic al botó de baixada i, a la pàgina nova, <bold>manteniu premut</bold> el vídeo i, a continuació, seleccioneu <bold>Desa</bold>.",
"shortTitle": "Baixa / Android",
"title": "Baixada a Android"
},
"onIos": {
"1": "Per a baixar a iOS, feu clic al botó de baixada i, a la pàgina nova, feu clic a <bold><ios_share /></bold> /> i, a continuació, <bold>Desa als Arxius <ios_files /></bold>.",
"shortTitle": "Baixa / iOS",
"title": "Baixada a iOS"
},
"onPc": {
"1": "En un PC, feu clic al botó de baixada i, a la pàgina nova, feu clic amb el botó dret al vídeo i seleccioneu <bold>Anomena i desa el vídeo</bold>",
"shortTitle": "Baixa / PC",
"title": "Baixada a un PC"
},
"title": "Baixa"
},
"episodes": {
"button": "Episodi",
"emptyState": "No hi ha episodis en esta temporada, torneu-ho a comprovar més tard!",
"episodeBadge": "E{{episode}}",
"loadingError": "Error en carregar la temporada",
"loadingList": "S'està carregant…",
"loadingTitle": "S'està carregant…",
"unairedEpisodes": "Un o més episodis d'esta temporada s'han desactivat perquè encara no s'han emés."
},
"playback": {
"speedLabel": "Velocitat de la reproducció",
"title": "Configuració de la reproducció"
},
"quality": {
"automaticLabel": "Qualitat automàtica",
"hint": "Podeu provar a <0>canviar la font</0> per a obtindre diferents opcions de qualitat.",
"iosNoQuality": "A causa de les limitacions definides per Apple, la selecció de qualitat no està disponible a iOS per a esta font. Podeu provar a <0>canviar a una altra font</0> per a obtindre diferents opcions de qualitat.",
"title": "Qualitat"
},
"settings": {
"downloadItem": "Baixa",
"enableSubtitles": "Activa els subtítols",
"experienceSection": "Experiència de visualització",
"playbackItem": "Configuració de la reproducció",
"qualityItem": "Qualitat",
"sourceItem": "Fonts de vídeo",
"subtitleItem": "Configuració dels subtítols",
"videoSection": "Configuració de vídeo"
},
"sources": {
"failed": {
"text": "S'ha produït un error en intentar trobar vídeos, proveu una font diferent.",
"title": "No s'ha pogut obtindre"
},
"noEmbeds": {
"text": "No hem pogut trobar cap incrustat, proveu una font diferent.",
"title": "No s'ha trobat cap incrustació"
},
"noStream": {
"text": "esta font no té fluxos per a esta pel·lícula o programa.",
"title": "Cap flux"
},
"title": "Fonts",
"unknownOption": "Desconeguda"
},
"subtitles": {
"customChoice": "Selecciona un fitxer de subtítols",
"customizeLabel": "Personalitza",
"offChoice": "Desactivats",
"settings": {
"backlink": "Subtítols personalitzats",
"delay": "Retard dels subtítols",
"fixCapitals": "Corregeix les majúscules/minúscules"
},
"title": "Subtítols",
"unknownLanguage": "Desconeguda"
}
},
"metadata": {
"api": {
"text": "No s'han pogut carregar les metadades de l'API, comproveu la connexió a Internet.",
"title": "No s'han pogut carregar les metadades de l'API"
},
"dmca": {
"badge": "Eliminat",
"text": "este contingut ja no està disponible a causa d'un avís de retirada o d'una reclamació de drets d'autor.",
"title": "El contingut s'ha eliminat"
},
"extensionPermission": {
"badge": "Falta el permís",
"button": "Utilitza l'extensió",
"text": "Teniu l'extensió del navegador, però necessitem el vostre permís per a començar a utilitzar l'extensió.",
"title": "Configureu l'extensió"
},
"failed": {
"badge": "Ha fallat",
"homeButton": "Vés a l'inici",
"text": "No s'han pogut carregar les metadades del contingut des de TMDB. Comproveu si TMDB no funciona o està bloquejat a la vostra connexió a Internet.",
"title": "Ha fallat la càrrega de les metadades"
},
"notFound": {
"badge": "No s'ha trobat",
"homeButton": "Torna a l'inici",
"text": "No hem pogut trobar el contingut sol·licitat. O bé s'ha eliminat o bé heu alterat l'URL.",
"title": "No s'ha trobat el contingut."
}
},
"nextEpisode": {
"cancel": "Cancel·la",
"next": "Episodi següent"
},
"playbackError": {
"badge": "Error en la reproducció",
"errors": {
"errorAborted": "S'ha interromput l'obtenció del contingut per petició de l'usuari.",
"errorDecode": "Tot i haver-se determinat prèviament que era utilitzable, s'ha produït un error en intentar descodificar el recurs multimèdia.",
"errorGenericMedia": "S'ha produït un error desconegut al contingut.",
"errorNetwork": "S'ha produït algun tipus d'error de xarxa que ha impedit que el contingut s'obtinga correctament, tot i haver estat disponibles prèviament.",
"errorNotSupported": "El contingut o el proveïdor del contingut no és compatible."
},
"homeButton": "Torna a l'inici",
"text": "S'ha produït un error en intentar reproduir el contingut. Torneu-ho a provar.",
"title": "No s'ha pogut reproduir el vídeo!"
},
"scraping": {
"items": {
"failure": "S'ha produït un error",
"notFound": "No té el vídeo",
"pending": "S'estan cercant vídeos…"
},
"notFound": {
"badge": "No s'ha trobat",
"detailsButton": "Mostra els detalls",
"homeButton": "Torna a l'inici",
"text": "Hem buscat a través dels nostres proveïdors i no trobem el contingut que busqueu! No allotgem contingut i no tenim control sobre què hi ha disponible. Feu clic a «Mostra els detalls» a continuació per a més informació.",
"title": "No s'ha pogut trobar"
}
},
"time": {
"regular": "{{timeWatched}} / {{duration}}",
"remaining": "{{timeLeft}} restants • Acaba a les {{timeFinished, datetime}}",
"shortRegular": "{{timeWatched}}",
"shortRemaining": "-{{timeLeft}}"
},
"turnstile": {
"description": "Verifiqueu que sou humà completant el Captcha de la dreta. Ho fem per a mantindre segura movie-web!",
"error": "No s'ha pogut verificar la humanitat. Torneu-ho a provar.",
"title": "Necessitem verificar que sou humà.",
"verifyingHumanity": "Verificant la vostra humanitat…"
}
},
"screens": {
"dmca": {
"text": "Vos donem la benvinguda a la pàgina de contacte DMCA de movie-web! Respectem els drets de propietat intel·lectual i volem resoldre qualsevol problema de drets d'autor ràpidament. Si creieu que la vostra obra protegida per drets d'autor s'ha utilitzat incorrectament a la nostra plataforma, envieu un avís detallat de la DMCA al correu electrònic següent. Incloeu una descripció del material protegit per drets d'autor, les vostres dades de contacte i una declaració de creença de bona fe. Ens comprometem a resoldre estes assumptes amb agilitat i agraïm la vostra col·laboració per a mantindre movie-web en un lloc que respecta la creativitat i els drets d'autor.",
"title": "DMCA"
},
"loadingApp": "S'està carregant l'aplicació",
"loadingUser": "S'està carregant el perfil",
"loadingUserError": {
"logout": "Tanca la sessió",
"reset": "Restableix el servidor personalitzat",
"text": "Ha fallat la càrrega del perfil",
"textWithReset": "Error en carregar el vostre perfil des del servidor personalitzat, voleu restablir el servidor per defecte?"
},
"migration": {
"failed": "La migració de les dades ha fallat.",
"inProgress": "Espereu, estem migrant les vostres dades. No hauria de tardar massa."
}
},
"settings": {
"account": {
"accountDetails": {
"deviceNameLabel": "Nom del dispositiu",
"deviceNamePlaceholder": "Telèfon personal",
"editProfile": "Edita",
"logoutButton": "Tanca la sessió"
},
"actions": {
"delete": {
"button": "Elimina el compte",
"confirmButton": "Elimina el compte",
"confirmDescription": "Segur que voleu eliminar el compte? Es perdran totes les dades!",
"confirmTitle": "Segur?",
"text": "Esta acció és irreversible. Totes les dades s'eliminaran i no es podrà recuperar res.",
"title": "Elimina el compte"
},
"title": "Accions"
},
"devices": {
"deviceNameLabel": "Nom del dispositiu",
"failed": "La càrrega de sessions ha fallat",
"removeDevice": "Elimina",
"title": "Dispositiu"
},
"profile": {
"finish": "Finalitza l'edició",
"firstColor": "Color de perfil 1",
"secondColor": "Color de perfil dos",
"title": "Edita la foto de perfil",
"userIcon": "Icona d'usuari"
},
"register": {
"cta": "Comença",
"text": "Compartiu el progrés de la visualització entre dispositius i manteniu-los sincronitzats.",
"title": "Sincronització amb el núvol"
},
"title": "Compte"
},
"appearance": {
"activeTheme": "Actiu",
"themes": {
"blue": "Blau",
"default": "Per defecte",
"gray": "Gris",
"red": "Vermell",
"teal": "Verd blavós"
},
"title": "Aparença"
},
"connections": {
"server": {
"description": "Si voleu connectar-vos a un rerefons personalitzat per a emmagatzemar les vostres dades, activeu-ho i proporcioneu l'URL. <0>Instruccions.</0>",
"label": "Servidor personalitzat",
"urlLabel": "URL del servidor personalitzat"
},
"setup": {
"doSetup": "Configura",
"errorStatus": {
"description": "Sembla que heu de revisar un o més elements de la configuració.",
"title": "S'ha de revisar algun element"
},
"itemError": "Hi ha alguna cosa malament en la configuració. Torneu a fer la configuració per a solucionar-ho.",
"items": {
"default": "Configuració per defecte",
"extension": "Extensió",
"proxy": "Servidor personalitzat"
},
"redoSetup": "Reconfigura",
"successStatus": {
"description": "Tot està preparat perquè comenceu a mirar el vostre contingut preferit.",
"title": "Tot està configurat!"
},
"unsetStatus": {
"description": "Feu clic al botó de la dreta per a iniciar el procés de configuració.",
"title": "No heu fet la configuració"
}
},
"title": "Connexions",
"workers": {
"addButton": "Afig un «worker»",
"description": "Per fer funcionar l'aplicació, tot el trànsit s'encamina a través de servidors intermediaris. Activeu-ho si voleu portar els vostres propis «workers». <0>Instruccions.</0>",
"emptyState": "Encara no hi ha «workers», afegiu-ne un a continuació",
"label": "Utilitza «workers» intermediaris personalitzats",
"urlLabel": "URL dels «workers»",
"urlPlaceholder": "https://"
}
},
"preferences": {
"language": "Llengua de l'aplicació",
"languageDescription": "La llengua s'aplica a tota l'aplicació.",
"thumbnail": "Genera miniatures",
"thumbnailDescription": "Majoritàriament, els vídeos no tenen miniatures. Podeu activar esta opció per a generar-les sobre la marxa, però poden alentir el vídeo.",
"thumbnailLabel": "Genera miniatures",
"title": "Configuració"
},
"reset": "Restableix",
"save": "Desa",
"sidebar": {
"info": {
"appVersion": "Versió de l'aplicació",
"backendUrl": "URL del rerefons",
"backendVersion": "Versió del rerefons",
"hostname": "Nom de l'amfitrió",
"insecure": "Insegur",
"notLoggedIn": "No heu iniciat sessió",
"secure": "Segur",
"title": "Informació de l'aplicació",
"unknownVersion": "Desconeguda",
"userId": "ID d'usuari"
}
},
"subtitles": {
"backgroundLabel": "Opacitat del fons",
"colorLabel": "Color",
"previewQuote": "No he de tindre por. La por és l'assassina de la ment.",
"textSizeLabel": "Grandària del text",
"title": "Subtítols"
},
"unsaved": "Hi ha canvis sense desar"
}
}

View File

@@ -57,6 +57,8 @@
}, },
"host": "Připojujete se k <0>{{hostname}}</0> - potvrďte, že mu věříte před vytvořením účtu", "host": "Připojujete se k <0>{{hostname}}</0> - potvrďte, že mu věříte před vytvořením účtu",
"no": "Zpět", "no": "Zpět",
"noHost": "Server nebyl nakonfigurován, tudíž si nemůžete vytvořit účet",
"noHostTitle": "Server není nakonfigurován!",
"title": "Věříte tomuto serveru?", "title": "Věříte tomuto serveru?",
"yes": "Věřím tomuto serveru" "yes": "Věřím tomuto serveru"
}, },
@@ -116,7 +118,15 @@
"failed": "Nepodařilo se najít média, zkuste to znovu!", "failed": "Nepodařilo se najít média, zkuste to znovu!",
"loading": "Načítání...", "loading": "Načítání...",
"noResults": "Nemohli jsme nic najít!", "noResults": "Nemohli jsme nic najít!",
"placeholder": "Co si přejete sledovat?", "placeholder": {
"default": "Co si přejete sledovat?",
"extra": [
"Co chcete objevit?",
null,
"Jaký je váš oblíbený film?",
"Jaký je tvůj oblíbený seriál?"
]
},
"sectionTitle": "Výsledky vyhledávání" "sectionTitle": "Výsledky vyhledávání"
}, },
"titles": { "titles": {
@@ -176,7 +186,11 @@
"extension": { "extension": {
"back": "Zpět", "back": "Zpět",
"explainer": "Pomocí rozšíření prohlížeče můžete získat nejlepší streamy, které nabízíme. S pouhou instalací.", "explainer": "Pomocí rozšíření prohlížeče můžete získat nejlepší streamy, které nabízíme. S pouhou instalací.",
"explainerIos": "Bohužel rozšíření není podporováno na iOS. Stiskněte <bold>Zpět</bold> pro výběr jiné možnosti.",
"extensionHelp": "Pokud jste rozšíření nainstalovali, ale nebylo zjištěno. <bold>Otevřete rozšíření pomocí nabídky rozšíření ve vašem prohlížeči</bold> a postupujte podle pokynů na obrazovce.", "extensionHelp": "Pokud jste rozšíření nainstalovali, ale nebylo zjištěno. <bold>Otevřete rozšíření pomocí nabídky rozšíření ve vašem prohlížeči</bold> a postupujte podle pokynů na obrazovce.",
"linkChrome": "Instalovat rozšíření pro Chrome",
"linkFirefox": "Instalovat rozšíření pro Firefox",
"notDetectingAction": "Obnovit stránku",
"status": { "status": {
"disallowed": "Rozšíření není pro tuto stránku povoleno", "disallowed": "Rozšíření není pro tuto stránku povoleno",
"disallowedAction": "Povolit rozšíření", "disallowedAction": "Povolit rozšíření",

View File

@@ -57,6 +57,8 @@
}, },
"host": "Du verbindest dich mit <0>{{hostname}}</0> - stelle sicher das du dem Server vertraust, bevor du ein Konto erstellst", "host": "Du verbindest dich mit <0>{{hostname}}</0> - stelle sicher das du dem Server vertraust, bevor du ein Konto erstellst",
"no": "Zurück", "no": "Zurück",
"noHost": "Da der Server nicht eingerichtet wurde, kannst du keinen Account erstellen",
"noHostTitle": "Server nicht eingerichtet!",
"title": "Vertraust du diesem Server?", "title": "Vertraust du diesem Server?",
"yes": "Ich vertraue diesem Server" "yes": "Ich vertraue diesem Server"
}, },
@@ -116,7 +118,15 @@
"failed": "Das Medium wurde nicht gefunden, bitte versuchen Sie es erneut!", "failed": "Das Medium wurde nicht gefunden, bitte versuchen Sie es erneut!",
"loading": "Wird geladen...", "loading": "Wird geladen...",
"noResults": "Wir haben nichts gefunden!", "noResults": "Wir haben nichts gefunden!",
"placeholder": "Was möchtest du schauen?", "placeholder": {
"default": "Was möchtest du schauen?",
"extra": [
"Was möchtest du entdecken?",
"Was ist auf deiner Merkliste?",
"Was ist dein Lieblingsfilm?",
"Was ist deine Lieblingsserie?"
]
},
"sectionTitle": "Suchergebnisse" "sectionTitle": "Suchergebnisse"
}, },
"titles": { "titles": {
@@ -242,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "HLS-Playlist Link kopieren",
"disclaimer": "Videos werden direkt vom Provider heruntergeladen. movie-web hat keinen Einfluss darauf, wie Downloads bereitgestellt werden.", "disclaimer": "Videos werden direkt vom Provider heruntergeladen. movie-web hat keinen Einfluss darauf, wie Downloads bereitgestellt werden.",
"downloadSubtitle": "Aktuelle Untertitel herunterladen", "downloadSubtitle": "Aktuelle Untertitel herunterladen",
"downloadVideo": "Video herunterladen", "downloadVideo": "Video herunterladen",
@@ -525,6 +536,7 @@
} }
}, },
"subtitles": { "subtitles": {
"backgroundBlurLabel": "Hintergrundunschärfe",
"backgroundLabel": "Hintergrunddeckkraft", "backgroundLabel": "Hintergrunddeckkraft",
"colorLabel": "Farbe", "colorLabel": "Farbe",
"previewQuote": "Das Gras wächst nicht schneller, wenn man daran zieht.", "previewQuote": "Das Gras wächst nicht schneller, wenn man daran zieht.",

View File

@@ -115,7 +115,10 @@
"failed": "Απέτυχε η εύρεση πολυμέσων, δοκιμάστε ξανά!", "failed": "Απέτυχε η εύρεση πολυμέσων, δοκιμάστε ξανά!",
"loading": "Φόρτωση...", "loading": "Φόρτωση...",
"noResults": "Δεν μπορέσαμε να βρούμε τίποτα!", "noResults": "Δεν μπορέσαμε να βρούμε τίποτα!",
"placeholder": "Τι θέλετε να παρακολουθήσετε;", "placeholder": {
"default": "Τι θέλετε να παρακολουθήσετε;",
"extra": []
},
"sectionTitle": "Αποτελέσματα αναζήτησης" "sectionTitle": "Αποτελέσματα αναζήτησης"
}, },
"titles": { "titles": {
@@ -127,15 +130,11 @@
}, },
"morning": { "morning": {
"default": "Τι θα θέλατε να παρακολουθήσετε σήμερα το πρωί;", "default": "Τι θα θέλατε να παρακολουθήσετε σήμερα το πρωί;",
"extra": [ "extra": ["Έχω ακούσει ότι το Before Sunrise είναι καλό"]
"Έχω ακούσει ότι το Before Sunrise είναι καλό"
]
}, },
"night": { "night": {
"default": "Τι θα θέλατε να παρακολουθήσετε απόψε;", "default": "Τι θα θέλατε να παρακολουθήσετε απόψε;",
"extra": [ "extra": ["Κούραση; Έχω ακούσει ότι ο Εξορκιστής είναι καλός."]
"Κούραση; Έχω ακούσει ότι ο Εξορκιστής είναι καλός."
]
} }
} }
}, },

View File

@@ -55,6 +55,8 @@
"text": "Did you configure it correctly?", "text": "Did you configure it correctly?",
"title": "Failed to reach server" "title": "Failed to reach server"
}, },
"noHostTitle": "Server not configured!",
"noHost": "The server has not been configured, therefore you cannot create an account",
"host": "You are connecting to <0>{{hostname}}</0> - please confirm you trust it before making an account", "host": "You are connecting to <0>{{hostname}}</0> - please confirm you trust it before making an account",
"no": "Go back", "no": "Go back",
"title": "Do you trust this server?", "title": "Do you trust this server?",
@@ -98,7 +100,8 @@
"onboarding": "Setup", "onboarding": "Setup",
"pagetitle": "{{title}} - movie-web", "pagetitle": "{{title}} - movie-web",
"register": "Register", "register": "Register",
"settings": "Settings" "settings": "Settings",
"migration": "Migration"
} }
}, },
"home": { "home": {
@@ -116,7 +119,15 @@
"failed": "Failed to find media, try again!", "failed": "Failed to find media, try again!",
"loading": "Loading...", "loading": "Loading...",
"noResults": "We couldn't find anything!", "noResults": "We couldn't find anything!",
"placeholder": "What do you want to watch?", "placeholder": {
"default": "What do you want to watch?",
"extra": [
"What do you want to explore?",
"What's on your watchlist?",
"What's your favorite movie?",
"What's your favorite series?"
]
},
"sectionTitle": "Search results" "sectionTitle": "Search results"
}, },
"titles": { "titles": {
@@ -143,6 +154,27 @@
"show": "Show" "show": "Show"
} }
}, },
"migration": {
"start": {
"title": "Migrate your data",
"explainer": "If you wish to migrate or backup your data, you can do so using the options below. This will allow you to keep your data when you switch backend servers.",
"options": {
"or": "or",
"direct": {
"description": "This will directly migrate your data to the new server. This is the fastest option. <br /><br />This option allows you to keep your passphrase the same!",
"title": "Direct migration",
"quality": "Easiest and fastest",
"action": "Transfer data"
},
"download": {
"description": "This will download your data to your device. You can then upload it to the new server or just keep it for safekeeping.",
"title": "Download data",
"quality": "More technical",
"action": "Download data"
}
}
}
},
"navigation": { "navigation": {
"banner": { "banner": {
"offline": "Check your internet connection" "offline": "Check your internet connection"
@@ -206,6 +238,7 @@
"start": { "start": {
"explainer": "To get the best streams possible, you will need to choose which streaming method you want to use.", "explainer": "To get the best streams possible, you will need to choose which streaming method you want to use.",
"options": { "options": {
"or": "or",
"default": { "default": {
"text": "I don't want good quality streams,<0 /> <1>use the default setup</1>" "text": "I don't want good quality streams,<0 /> <1>use the default setup</1>"
}, },
@@ -523,6 +556,7 @@
}, },
"subtitles": { "subtitles": {
"backgroundLabel": "Background opacity", "backgroundLabel": "Background opacity",
"backgroundBlurLabel": "Background blur",
"colorLabel": "Color", "colorLabel": "Color",
"previewQuote": "I must not fear. Fear is the mind-killer.", "previewQuote": "I must not fear. Fear is the mind-killer.",
"textSizeLabel": "Text size", "textSizeLabel": "Text size",

View File

@@ -12,7 +12,7 @@
}, },
"q3": { "q3": {
"body": "Nuestros resultados de búsqueda están alimentados por The Movie Database (TMDB) y se muestran independientemente de si nuestras fuentes realmente tienen el contenido.", "body": "Nuestros resultados de búsqueda están alimentados por The Movie Database (TMDB) y se muestran independientemente de si nuestras fuentes realmente tienen el contenido.",
"title": "Los resultados de búsqueda muestran la serie o película, ¿por qué no puedo reproducirla?" "title": "Los resultados de búsqueda muestran la serie o película, ¿Por qué no puedo reproducirla?"
}, },
"title": "Acerca de movie-web" "title": "Acerca de movie-web"
}, },
@@ -57,6 +57,8 @@
}, },
"host": "Te estás conectando a <0>{{hostname}}</0> - por favor, confirma si confías en este antes de crear una cuenta", "host": "Te estás conectando a <0>{{hostname}}</0> - por favor, confirma si confías en este antes de crear una cuenta",
"no": "Regresar", "no": "Regresar",
"noHost": "El servidor no se ha configurado, por lo tanto, no puede crear una cuenta",
"noHostTitle": "¡El servidor no está configurado!",
"title": "¿Confías en este servidor?", "title": "¿Confías en este servidor?",
"yes": "Confío en este servidor" "yes": "Confío en este servidor"
}, },
@@ -116,7 +118,15 @@
"failed": "¡Error al encontrar contenido, inténtalo de nuevo!", "failed": "¡Error al encontrar contenido, inténtalo de nuevo!",
"loading": "Cargando...", "loading": "Cargando...",
"noResults": "¡No pudimos encontrar nada!", "noResults": "¡No pudimos encontrar nada!",
"placeholder": "¿Qué te gustaría ver?", "placeholder": {
"default": "¿Qué te gustaría ver?",
"extra": [
"¿Qué quieres explorar?",
"¿Qué hay en tu lista de reproducción?",
"¿Cuál es tu película favorita?",
"¿Cuál es tu serie favorita?"
]
},
"sectionTitle": "Resultados de búsqueda" "sectionTitle": "Resultados de búsqueda"
}, },
"titles": { "titles": {
@@ -171,7 +181,7 @@
"cancel": "Cancelar", "cancel": "Cancelar",
"confirm": "Usar configuración por defecto", "confirm": "Usar configuración por defecto",
"description": "La configuración predeterminada no tiene las mejores transmisiones y puede ser insoportablemente lenta.", "description": "La configuración predeterminada no tiene las mejores transmisiones y puede ser insoportablemente lenta.",
"title": "Estás seguro?" "title": "¿Estás seguro?"
}, },
"extension": { "extension": {
"back": "Volver atrás", "back": "Volver atrás",
@@ -242,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "Copiar enlace de reproducción HLS",
"disclaimer": "Las descargas se realizan directamente desde el proveedor. movie-web no tiene control sobre cómo se proporcionan las descargas.", "disclaimer": "Las descargas se realizan directamente desde el proveedor. movie-web no tiene control sobre cómo se proporcionan las descargas.",
"downloadSubtitle": "Descargar subtítulo actual", "downloadSubtitle": "Descargar subtítulo actual",
"downloadVideo": "Descargar vídeo", "downloadVideo": "Descargar vídeo",
@@ -388,7 +399,7 @@
"shortRemaining": "-{{timeLeft}}" "shortRemaining": "-{{timeLeft}}"
}, },
"turnstile": { "turnstile": {
"description": "Por favor, confirma que eres humano completando el Captcha. Esto es para mantener movie-web seguro!", "description": "Por favor, confirma que eres humano completando el Captcha. ¡Esto es para mantener movie-web seguro!",
"error": "Ha habido un error al verificar tu humanidad. Por favor, prueba de nuevo.", "error": "Ha habido un error al verificar tu humanidad. Por favor, prueba de nuevo.",
"title": "Necesitamos verificar que eres humano.", "title": "Necesitamos verificar que eres humano.",
"verifyingHumanity": "Verificando tu hunanidad…" "verifyingHumanity": "Verificando tu hunanidad…"
@@ -483,7 +494,7 @@
"redoSetup": "Rehacer configuración", "redoSetup": "Rehacer configuración",
"successStatus": { "successStatus": {
"description": "Todo lo necesario está en su sitio para que empieces a ver tu contenido favorito.", "description": "Todo lo necesario está en su sitio para que empieces a ver tu contenido favorito.",
"title": "Todo está configurado!" "title": "¡Todo está configurado!"
}, },
"unsetStatus": { "unsetStatus": {
"description": "Haga clic en el botón a la derecha para iniciar el proceso de configuración.", "description": "Haga clic en el botón a la derecha para iniciar el proceso de configuración.",

View File

@@ -57,6 +57,8 @@
}, },
"host": "Ühendate <0>{{hostname}}</0> - enne konto tegemist kinnitage, et usaldate seda", "host": "Ühendate <0>{{hostname}}</0> - enne konto tegemist kinnitage, et usaldate seda",
"no": "Tagasi", "no": "Tagasi",
"noHost": "Server ei ole konfigureeritud, seega ei saa kontot luua",
"noHostTitle": "Server ei ole konfigureeritud!",
"title": "Kas usaldate seda serverit?", "title": "Kas usaldate seda serverit?",
"yes": "Usaldan seda serverit" "yes": "Usaldan seda serverit"
}, },
@@ -116,7 +118,15 @@
"failed": "Meedia leidmine ebaõnnestus, proovige uuesti!", "failed": "Meedia leidmine ebaõnnestus, proovige uuesti!",
"loading": "Laadimine....", "loading": "Laadimine....",
"noResults": "Me ei leidnud midagi!", "noResults": "Me ei leidnud midagi!",
"placeholder": "Mida tahate vaadata?", "placeholder": {
"default": "Mida tahate vaadata?",
"extra": [
"Mida soovite uurida?",
"Mis on teie nimekirjas?",
"Milline on teie lemmikfilm?",
"Milline on teie lemmiksari?"
]
},
"sectionTitle": "Otsingutulemused" "sectionTitle": "Otsingutulemused"
}, },
"titles": { "titles": {
@@ -242,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "Kopeeri HLS esitusloendi link",
"disclaimer": "Allalaadimine toimub otse teenusepakkujalt. movie-web ei saa kontrollida, kuidas allalaadimine toimub.", "disclaimer": "Allalaadimine toimub otse teenusepakkujalt. movie-web ei saa kontrollida, kuidas allalaadimine toimub.",
"downloadSubtitle": "Laadige alla praegune subtiiter", "downloadSubtitle": "Laadige alla praegune subtiiter",
"downloadVideo": "Lae alla video", "downloadVideo": "Lae alla video",
@@ -525,6 +536,7 @@
} }
}, },
"subtitles": { "subtitles": {
"backgroundBlurLabel": "Tausta hägusus",
"backgroundLabel": "Tausta läbipaistmatus", "backgroundLabel": "Tausta läbipaistmatus",
"colorLabel": "Värv", "colorLabel": "Värv",
"previewQuote": "Ma ei tohi karta. Hirm on meelemõrvar.", "previewQuote": "Ma ei tohi karta. Hirm on meelemõrvar.",

View File

@@ -8,11 +8,11 @@
}, },
"q2": { "q2": {
"body": "از آنجایی که محتوا ها توسط مووی-وب مدیریت نمی‌شوند، امکان درخواست فیلم وجود ندارد. تمام محتوا از طریق منابع در اینترنت به شما نشان داده می‌شوند.", "body": "از آنجایی که محتوا ها توسط مووی-وب مدیریت نمی‌شوند، امکان درخواست فیلم وجود ندارد. تمام محتوا از طریق منابع در اینترنت به شما نشان داده می‌شوند.",
"title": "از کجا میتوانم درخواست فیلم کنم؟" "title": "از کجا میتونم درخواست فیلم کنم؟"
}, },
"q3": { "q3": {
"body": "نتایج جستجوی ما توسط پایگاه داده فیلم (TMDB) تامین می‌شوند و نمایش داده می‌شوند، بدون اینکه مهم باشد که منابع ما واقعاً محتوا را داشته باشند یا خیر.", "body": "نتایج جستجوی ما توسط پایگاه داده فیلم (TMDB) تامین می‌شوند و نمایش داده می‌شوند، بدون اینکه مهم باشد که منابع ما واقعاً محتوا را داشته باشند یا خیر.",
"title": "نتایج جستجو وجود دارد، اما چرا فیلم پخش نمی‌شود؟" "title": "جستجو همراه با نتیجه است، اما چرا فیلم پخش نمی‌شود؟"
}, },
"title": "درباره مووی-وب" "title": "درباره مووی-وب"
}, },
@@ -25,7 +25,7 @@
"deviceNameLabel": "نام دستگاه", "deviceNameLabel": "نام دستگاه",
"deviceNamePlaceholder": "تلفن شخصی", "deviceNamePlaceholder": "تلفن شخصی",
"generate": { "generate": {
"description": "جمله امنیتی شما به عنوان نام کاربری و رمز عبور عمل می‌کند. آن را در جایی ذخیره کنید چون برای ورود به آن نیاز دارید", "description": "عبارت عبور شما به عنوان نام کاربری و رمز عبور عمل می‌کند. آن را در جایی ذخیره کنید چون برای ورود به آن نیاز دارید",
"next": "عبارت عبور امنیتی خود را ذخیره کرده‌ام", "next": "عبارت عبور امنیتی خود را ذخیره کرده‌ام",
"passphraseFrameLabel": "عبارت عبور امنیتی", "passphraseFrameLabel": "عبارت عبور امنیتی",
"title": "عبارت عبور امنیتی شما" "title": "عبارت عبور امنیتی شما"
@@ -34,7 +34,7 @@
"login": { "login": {
"description": "لطفاً عبارت عبور امنیتی خود را وارد کنید تا وارد حساب کاربری شوید", "description": "لطفاً عبارت عبور امنیتی خود را وارد کنید تا وارد حساب کاربری شوید",
"deviceLengthError": "لطفا نامی برای دستگاه انتخاب کنید", "deviceLengthError": "لطفا نامی برای دستگاه انتخاب کنید",
"passphraseLabel": "جمله امنیتی 12 کلمه‌ای", "passphraseLabel": "عبارت عبور 12 کلمه‌ای",
"passphrasePlaceholder": "عبارت عبور امنیتی", "passphrasePlaceholder": "عبارت عبور امنیتی",
"submit": "ورود", "submit": "ورود",
"title": "ورود به حساب کاربری", "title": "ورود به حساب کاربری",
@@ -57,6 +57,8 @@
}, },
"host": "شما در حال اتصال به <0>{{hostname}}</0> هستید - لطفا قبل از ایجاد حساب کاربری خود از اعتماد به آن اطمینان حاصل کنید", "host": "شما در حال اتصال به <0>{{hostname}}</0> هستید - لطفا قبل از ایجاد حساب کاربری خود از اعتماد به آن اطمینان حاصل کنید",
"no": "بازگشت", "no": "بازگشت",
"noHost": "سرور پیکربندی نشده، بنابرین نمیتوانید حسابی ایجاد کنید",
"noHostTitle": "سرور پیکر بندی نشده!",
"title": "آیا به این سرور اعتماد دارید؟", "title": "آیا به این سرور اعتماد دارید؟",
"yes": "بله اعتماد دارم" "yes": "بله اعتماد دارم"
}, },
@@ -71,7 +73,7 @@
} }
}, },
"errors": { "errors": {
"badge": "مشکلی پیش آمده", "badge": "مشکلی رخ داده",
"details": "جزئیات خطا", "details": "جزئیات خطا",
"reloadPage": "صفحه را دوباره بارگذاری کنید", "reloadPage": "صفحه را دوباره بارگذاری کنید",
"showError": "نمایش جزئیات خطا", "showError": "نمایش جزئیات خطا",
@@ -115,8 +117,16 @@
"allResults": "همه چیزی بود که داشتیم!", "allResults": "همه چیزی بود که داشتیم!",
"failed": "چیزی پیدا نشد، دوباره تلاش کنید!", "failed": "چیزی پیدا نشد، دوباره تلاش کنید!",
"loading": "در حال بارگذاری...", "loading": "در حال بارگذاری...",
"noResults": "چیزی پیدا نکردیم!", "noResults": "نتونستیم چیزی پیدا کنیم!",
"placeholder": "چه می‌خواهید تماشا کنید؟", "placeholder": {
"default": "چه می‌خواهید تماشا کنید؟",
"extra": [
"دنبال چی میگردی؟",
"چی تو لیست تماشات داری؟",
"فیلم مورد علاقت چیه؟",
"سریال مورد علاقت چیه؟"
]
},
"sectionTitle": "نتایج جستجو" "sectionTitle": "نتایج جستجو"
}, },
"titles": { "titles": {
@@ -242,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "لینک HLS لیست پخش را کپی کنید",
"disclaimer": "دانلود ها به طور مستقیم از ارائه دهنده گرفته می شوند. مووی-وب کنترلی بر نحوه ارائه دانلود ها ندارد.", "disclaimer": "دانلود ها به طور مستقیم از ارائه دهنده گرفته می شوند. مووی-وب کنترلی بر نحوه ارائه دانلود ها ندارد.",
"downloadSubtitle": "دانلود زیرنویس فعلی", "downloadSubtitle": "دانلود زیرنویس فعلی",
"downloadVideo": "دانلود ویدیو", "downloadVideo": "دانلود ویدیو",
@@ -464,7 +475,7 @@
}, },
"connections": { "connections": {
"server": { "server": {
"description": "اگر میخواهید به یک بک-اند سفارشی برای ذخیره داده متصل شوید، با فعال و ارائه استفاده این لینک ادامه دهید. <0>دستورالعمل ها.</0>", "description": "اگر میخواید به یک بک-اند سفارشی برای ذخیره داده متصل شوید، با فعال و ارائه استفاده این لینک ادامه دهید. <0>دستورالعمل ها.</0>",
"label": "سرور سفارشی", "label": "سرور سفارشی",
"urlLabel": "لینک سرور سفارشی" "urlLabel": "لینک سرور سفارشی"
}, },
@@ -493,7 +504,7 @@
"title": "اتصالات", "title": "اتصالات",
"workers": { "workers": {
"addButton": "اضافه کردن worker جدید", "addButton": "اضافه کردن worker جدید",
"description": "برای ایجاد عملکرد برنامه، تمام ترافیک از طریق پروکسی ها هدایت می شود. اگر میخواهید این کار انجام دهید حتما از worker های خودتان استفاده کنید. <0>دستورالعمل ها.</0>", "description": "برای ایجاد عملکرد برنامه، تمام ترافیک از طریق پروکسی ها هدایت می شود. اگر میخواید این کار انجام دهید حتما از worker های خودتان استفاده کنید. <0>دستورالعمل ها.</0>",
"emptyState": "هنوز هیچ worker ای وجود ندارد، یکی اضافه کنید", "emptyState": "هنوز هیچ worker ای وجود ندارد، یکی اضافه کنید",
"label": "استفاده از worker های پروکسی سفارشی", "label": "استفاده از worker های پروکسی سفارشی",
"urlLabel": "لینک worker ها", "urlLabel": "لینک worker ها",
@@ -525,6 +536,7 @@
} }
}, },
"subtitles": { "subtitles": {
"backgroundBlurLabel": "تاری پس زمینه",
"backgroundLabel": "شفافیت پس‌زمینه", "backgroundLabel": "شفافیت پس‌زمینه",
"colorLabel": "رنگ", "colorLabel": "رنگ",
"previewQuote": "نباید بترسم، ترس قاتل ذهن است.", "previewQuote": "نباید بترسم، ترس قاتل ذهن است.",

View File

@@ -116,7 +116,10 @@
"failed": "Mediaa ei löytynyt, yritä uudelleen!", "failed": "Mediaa ei löytynyt, yritä uudelleen!",
"loading": "Ladataan...", "loading": "Ladataan...",
"noResults": "Emme löytäneet mitään!", "noResults": "Emme löytäneet mitään!",
"placeholder": "Mitä haluat katsoa?", "placeholder": {
"default": "Mitä haluat katsoa?",
"extra": []
},
"sectionTitle": "Hakutulokset" "sectionTitle": "Hakutulokset"
}, },
"titles": { "titles": {
@@ -128,15 +131,11 @@
}, },
"morning": { "morning": {
"default": "Mitä haluaisit katsoa tänä aamuna?", "default": "Mitä haluaisit katsoa tänä aamuna?",
"extra": [ "extra": ["Kuulen, että Rakkautta ennen aamua (Before Sunrise) on hyvä"]
"Kuulen, että Rakkautta ennen aamua (Before Sunrise) on hyvä"
]
}, },
"night": { "night": {
"default": "Mitä haluaisit katsoa tänä iltana?", "default": "Mitä haluaisit katsoa tänä iltana?",
"extra": [ "extra": ["Väsynyt? Kuulin, että Manaaja (The Exorcist) on hyvä."]
"Väsynyt? Kuulin, että Manaaja (The Exorcist) on hyvä."
]
} }
} }
}, },

View File

@@ -7,12 +7,12 @@
"title": "D'où vient le contenu ?" "title": "D'où vient le contenu ?"
}, },
"q2": { "q2": {
"body": "Il est impossible de solliciter une émission ou un film car movie-web ne gère aucun contenu. Les sources sur Internet sont utilisées pour consulter tous les contenus.", "body": "Il est impossible de demander un film ou une série car movie-web ne gère aucun contenu. Le contenu est récupéré en explorant d'autres sites sur Internet.",
"title": "Où puis-je demander une série ou un film ?" "title": "Où puis-je demander une série ou un film ?"
}, },
"q3": { "q3": {
"body": "Que nos sources soient propriétaires du contenu ou non, The Movie Database (TMDB) fournit et affiche nos résultats de recherche.", "body": "Nos résultats de recherche sont alimentés par The Movie Database (TMDB) et s'affichent indépendamment de la disponibilité réelle du contenu dans nos sources.",
"title": "Les résultats de la recherche affichent l'émission ou le film, pourquoi ne puis-je pas le lire ?" "title": "Les résultats de la recherche affichent la série ou le film, pourquoi ne puis-je pas le lire ?"
}, },
"title": "À propos de movie-web" "title": "À propos de movie-web"
}, },
@@ -27,27 +27,27 @@
"generate": { "generate": {
"description": "Le nom d'utilisateur et le mot de passe sont obtenus à partir de votre passphrase. Vous devrez la saisir pour accéder à votre compte, alors gardez-la précieusement", "description": "Le nom d'utilisateur et le mot de passe sont obtenus à partir de votre passphrase. Vous devrez la saisir pour accéder à votre compte, alors gardez-la précieusement",
"next": "J'ai sauvegardé ma passphrase", "next": "J'ai sauvegardé ma passphrase",
"passphraseFrameLabel": "Pass phrase", "passphraseFrameLabel": "Passphrase",
"title": "Votre passphrase" "title": "Votre passphrase"
}, },
"hasAccount": "Avez-vous déjà un compte? <0>Connectez-vous ici.</0>", "hasAccount": "Avez-vous déjà un compte? <0>Connectez-vous ici.</0>",
"login": { "login": {
"description": "Veuillez fournir votre passphrase pour accéder à votre compte", "description": "Veuillez saisir votre passphrase pour accéder à votre compte",
"deviceLengthError": "Veuillez saisir un nom d'appareil", "deviceLengthError": "Veuillez saisir un nom d'appareil",
"passphraseLabel": "Passphrase de 12 mots", "passphraseLabel": "Passphrase de 12 mots",
"passphrasePlaceholder": "Phrase secrète", "passphrasePlaceholder": "Passphrase",
"submit": "Se connecter", "submit": "Se connecter",
"title": "Se connecter à votre compte", "title": "Connectez-vous à votre compte",
"validationError": "Passphrase incorrecte ou incomplete" "validationError": "Passphrase incorrecte ou incomplète"
}, },
"register": { "register": {
"information": { "information": {
"color1": "Couleur de profile un", "color1": "Première couleur de profil",
"color2": "Couleur de profile deux", "color2": "Seconde couleur de profil",
"header": "Entrez un nom pour votre appareil et choisissez une couleur de profile ainsi qu'une icône d'utilisateur de votre choix", "header": "Veuillez entrer un nom pour votre appareil, choisir une couleur et une icône utilisateur de votre choix",
"icon": "Icône d'utilisateur", "icon": "Icône d'utilisateur",
"next": "Prochain", "next": "Suivant",
"title": "Informations sur le compte" "title": "Informations du compte"
} }
}, },
"trust": { "trust": {
@@ -55,8 +55,10 @@
"text": "L'avez-vous configuré correctement ?", "text": "L'avez-vous configuré correctement ?",
"title": "Échec de la connexion au serveur" "title": "Échec de la connexion au serveur"
}, },
"host": "Vous vous connectez à <0>{{hostname}}</0> - veuillez confirmer que vous lui faites confiance avant de créer un compte", "host": "Vous êtes en train de vous connecter à <0>{{hostname}}</0> - veuillez confirmer que vous lui faites confiance avant de créer un compte",
"no": "Retour", "no": "Retour",
"noHost": "Le serveur n'a pas été configuré, vous ne pouvez donc pas créer de compte",
"noHostTitle": "Serveur non configuré !",
"title": "Avez-vous confiance en ce serveur ?", "title": "Avez-vous confiance en ce serveur ?",
"yes": "Je fais confiance à ce serveur" "yes": "Je fais confiance à ce serveur"
}, },
@@ -65,13 +67,13 @@
"invalidData": "Les données ne sont pas valides", "invalidData": "Les données ne sont pas valides",
"noMatch": "La passphrase ne correspond pas", "noMatch": "La passphrase ne correspond pas",
"passphraseLabel": "Votre passphrase de 12 mots", "passphraseLabel": "Votre passphrase de 12 mots",
"recaptchaFailed": "La validation ReCaptcha a échoué", "recaptchaFailed": "La validation ReCaptcha a échouée",
"register": "Créer un compte", "register": "Créer un compte",
"title": "Confirmez votre passphrase" "title": "Resaisissez votre passphrase"
} }
}, },
"errors": { "errors": {
"badge": "Il s'est cassé", "badge": "Tout est cassé",
"details": "Détails de l'erreur", "details": "Détails de l'erreur",
"reloadPage": "Actualiser la page", "reloadPage": "Actualiser la page",
"showError": "Afficher les détails de l'erreur", "showError": "Afficher les détails de l'erreur",
@@ -87,7 +89,7 @@
"dmca": "DMCA", "dmca": "DMCA",
"github": "GitHub" "github": "GitHub"
}, },
"tagline": "Cette application de streaming open source vous permet de regarder vos émissions et films préférés." "tagline": "Regardez vos séries et films préférés avec cette application de streaming open source."
}, },
"global": { "global": {
"name": "movie-web", "name": "movie-web",
@@ -95,7 +97,7 @@
"about": "À propos", "about": "À propos",
"dmca": "DMCA", "dmca": "DMCA",
"login": "Se connecter", "login": "Se connecter",
"onboarding": "Setup", "onboarding": "Mise en place",
"pagetitle": "{{title}} - movie-web", "pagetitle": "{{title}} - movie-web",
"register": "Créer un compte", "register": "Créer un compte",
"settings": "Paramètres" "settings": "Paramètres"
@@ -106,7 +108,7 @@
"sectionTitle": "Favoris" "sectionTitle": "Favoris"
}, },
"continueWatching": { "continueWatching": {
"sectionTitle": "Continuer le visionnage" "sectionTitle": "Reprendre la lecture"
}, },
"mediaList": { "mediaList": {
"stopEditing": "Arrêter l'édition" "stopEditing": "Arrêter l'édition"
@@ -116,7 +118,15 @@
"failed": "Le média n'a pas été trouvé, veuillez réessayez!", "failed": "Le média n'a pas été trouvé, veuillez réessayez!",
"loading": "Chargement...", "loading": "Chargement...",
"noResults": "Nous n'avons rien trouvé!", "noResults": "Nous n'avons rien trouvé!",
"placeholder": "Que voulez-vous voir?", "placeholder": {
"default": "Que voulez-vous voir?",
"extra": [
"Que voulez-vous explorer ?",
"Que y a-t-il dans votre liste de lecture?",
"Quel est votre film préféré ?",
"Quelle est votre série préférée ?"
]
},
"sectionTitle": "Résultats de la recherche" "sectionTitle": "Résultats de la recherche"
}, },
"titles": { "titles": {
@@ -162,21 +172,21 @@
}, },
"notFound": { "notFound": {
"badge": "Introuvable", "badge": "Introuvable",
"goHome": "Retour à l'accueil", "goHome": "Retourner à l'accueil",
"message": "Nous avons cherché partout : sous les poubelles, dans le placard, derrière le proxy, mais nous n'avons finalement pas trouvé la page que vous cherchez.", "message": "Nous avons cherché partout : sous les poubelles, dans le placard, derrière le proxy, mais nous n'avons finalement pas trouvé la page que vous cherchez.",
"title": "Impossible de trouver cette page" "title": "Impossible de trouver cette page"
}, },
"onboarding": { "onboarding": {
"defaultConfirm": { "defaultConfirm": {
"cancel": "Annuler", "cancel": "Annuler",
"confirm": "Utiliser la configuration de départ", "confirm": "Utiliser la configuration par défaut",
"description": "La configuration par défaut n'offre pas les meilleurs flux et peut être insupportablement lente.", "description": "La configuration par défaut n'offre pas les meilleurs flux et peut être insupportablement lente.",
"title": "Es-tu sûr?" "title": "Êtes-vous sûr ?"
}, },
"extension": { "extension": {
"back": "Retour en arrière", "back": "Revenir en arrière",
"explainer": "En utilisant l'extension de navigateur, vous pouvez obtenir les meilleurs flux que nous avons à offrir. Avec juste une simple installation.", "explainer": "En utilisant l'extension de navigateur, vous pouvez obtenir les meilleurs flux que nous avons à offrir. Avec juste une simple installation.",
"explainerIos": "Malheureusement, l'extension web n'est pas prise en charge sur iOS, appuyez sur <bold> Retour en arrière </bold> pour choisir une autre option.", "explainerIos": "Malheureusement, l'extension web n'est pas prise en charge sur iOS, appuyez sur <bold> Revenir en arrière </bold> pour choisir une autre option.",
"extensionHelp": "Si vous avez installé l'extension mais qu'elle n'est pas détectée, <bold>ouvrez l'extension via le menu des extensions de votre navigateur</bold> et suivez les étapes à l'écran.", "extensionHelp": "Si vous avez installé l'extension mais qu'elle n'est pas détectée, <bold>ouvrez l'extension via le menu des extensions de votre navigateur</bold> et suivez les étapes à l'écran.",
"linkChrome": "Installer l'extension Chrome", "linkChrome": "Installer l'extension Chrome",
"linkFirefox": "Installer l'extension Firefox", "linkFirefox": "Installer l'extension Firefox",
@@ -186,7 +196,7 @@
"disallowed": "L'extension n'est pas activée pour cette page", "disallowed": "L'extension n'est pas activée pour cette page",
"disallowedAction": "Activer l'extension", "disallowedAction": "Activer l'extension",
"failed": "Échec de la demande de statut", "failed": "Échec de la demande de statut",
"loading": "En attendant que vous installiez l'extension", "loading": "En attente que vous installiez l'extension",
"outdated": "Version d'extension trop ancienne", "outdated": "Version d'extension trop ancienne",
"success": "L'extension fonctionne comme prévu!" "success": "L'extension fonctionne comme prévu!"
}, },
@@ -194,7 +204,7 @@
"title": "Commençons par une extension" "title": "Commençons par une extension"
}, },
"proxy": { "proxy": {
"back": "Retour en arrière", "back": "Revenir en arrière",
"explainer": "Avec la méthode du proxy, vous pouvez obtenir des flux de bonne qualité en créant un proxy en libre-service.", "explainer": "Avec la méthode du proxy, vous pouvez obtenir des flux de bonne qualité en créant un proxy en libre-service.",
"input": { "input": {
"errorConnection": "Impossible de se connecter au proxy", "errorConnection": "Impossible de se connecter au proxy",
@@ -204,18 +214,18 @@
"placeholder": "https://" "placeholder": "https://"
}, },
"link": "Apprenez à créer un proxy", "link": "Apprenez à créer un proxy",
"submit": "Soumettre le proxy", "submit": "Valider le proxy",
"title": "Créons un nouveau proxy" "title": "Créons un nouveau proxy"
}, },
"start": { "start": {
"explainer": "Pour obtenir les meilleurs flux possibles, vous devrez choisir la méthode de streaming que vous souhaitez utiliser.", "explainer": "Pour obtenir les meilleurs flux possibles, vous devrez choisir la méthode de streaming que vous souhaitez utiliser.",
"options": { "options": {
"default": { "default": {
"text": "Je ne veux pas de flux de bonne qualité,<0 /> <1>use the default setup</1>" "text": "Je ne veux pas de flux de bonne qualité,<0 /> <1>Utiliser le flux par défaut</1>"
}, },
"extension": { "extension": {
"action": "Installer l'extension", "action": "Installer l'extension",
"description": "Installez l'extension de navigateur et accédez aux meilleures sources.", "description": "Installez l'extension pour navigateur et accédez aux meilleures sources.",
"quality": "Meilleur qualité", "quality": "Meilleur qualité",
"title": "Extension du navigateur" "title": "Extension du navigateur"
}, },
@@ -226,7 +236,7 @@
"title": "Proxy personnalisé" "title": "Proxy personnalisé"
} }
}, },
"title": "Commençons par vous configurer movie-web" "title": "Commençons par configurer movie-web"
} }
}, },
"overlays": { "overlays": {
@@ -234,14 +244,15 @@
}, },
"player": { "player": {
"back": { "back": {
"default": "Retour à la page d'accueil", "default": "Revenir à la page d'accueil",
"short": "Retour" "short": "Retour"
}, },
"casting": { "casting": {
"enabled": "Casting à l'appareil..." "enabled": "Casting vers l'appareil..."
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "Copier le lien de la playlist HLS",
"disclaimer": "Les téléchargements sont effectués directement par le fournisseur. movie-web n'a aucun contrôle sur la manière dont les téléchargements sont effectués.", "disclaimer": "Les téléchargements sont effectués directement par le fournisseur. movie-web n'a aucun contrôle sur la manière dont les téléchargements sont effectués.",
"downloadSubtitle": "Télécharger les sous-titres", "downloadSubtitle": "Télécharger les sous-titres",
"downloadVideo": "Télécharger la vidéo", "downloadVideo": "Télécharger la vidéo",
@@ -265,9 +276,9 @@
}, },
"episodes": { "episodes": {
"button": "Épisodes", "button": "Épisodes",
"emptyState": "Il n'y a pas d'épisodes dans cette saison, revenez plus tard !", "emptyState": "Cette saison ne contient aucun épisode, revenez plus tard !",
"episodeBadge": "E{{episode}}", "episodeBadge": "E{{episode}}",
"loadingError": "Erreur de chargement de la saison", "loadingError": "Erreur lors du chargement de la saison",
"loadingList": "Chargement...", "loadingList": "Chargement...",
"loadingTitle": "Chargement...", "loadingTitle": "Chargement...",
"unairedEpisodes": "Un ou plusieurs épisodes de cette saison ont été désactivés car ils n'ont pas encore été diffusés." "unairedEpisodes": "Un ou plusieurs épisodes de cette saison ont été désactivés car ils n'ont pas encore été diffusés."
@@ -278,7 +289,7 @@
}, },
"quality": { "quality": {
"automaticLabel": "Qualité automatique", "automaticLabel": "Qualité automatique",
"hint": "Vous pouvez essayer de <0>changer de fournisseur</0> pour obtenir différentes options de qualité.", "hint": "Vous pouvez essayer de <0>changer de source</0> pour obtenir différentes options de qualité.",
"iosNoQuality": "En raison des limitations définies par Apple, la sélection de la qualité n'est pas disponible sur iOS pour cette source. Vous pouvez essayer <0>de passer à une autre source</0> pour obtenir des options de qualité différentes.", "iosNoQuality": "En raison des limitations définies par Apple, la sélection de la qualité n'est pas disponible sur iOS pour cette source. Vous pouvez essayer <0>de passer à une autre source</0> pour obtenir des options de qualité différentes.",
"title": "Qualité" "title": "Qualité"
}, },
@@ -302,7 +313,7 @@
"title": "Pas d'embeds trouvés" "title": "Pas d'embeds trouvés"
}, },
"noStream": { "noStream": {
"text": "Cette source n'a pas de flux pour ce film ou cette émission.", "text": "Cette source n'a pas de flux pour ce film ou cette série.",
"title": "Pas de flux" "title": "Pas de flux"
}, },
"title": "Sources", "title": "Sources",
@@ -345,7 +356,7 @@
}, },
"notFound": { "notFound": {
"badge": "Introuvable", "badge": "Introuvable",
"homeButton": "Retour à l'accueil", "homeButton": "Revenir à l'accueil",
"text": "Nous n'avons pas trouvé le média que vous avez demandé. Soit il a été supprimé, soit vous avez modifié l'URL.", "text": "Nous n'avons pas trouvé le média que vous avez demandé. Soit il a été supprimé, soit vous avez modifié l'URL.",
"title": "Impossible de trouver ce média." "title": "Impossible de trouver ce média."
} }
@@ -363,7 +374,7 @@
"errorNetwork": "Une erreur de réseau s'est produite qui a empêché la récupération du média, bien qu'il ait été disponible auparavant.", "errorNetwork": "Une erreur de réseau s'est produite qui a empêché la récupération du média, bien qu'il ait été disponible auparavant.",
"errorNotSupported": "L'objet du media ou de la source du média n'est pas supporté." "errorNotSupported": "L'objet du media ou de la source du média n'est pas supporté."
}, },
"homeButton": "Retour à la maison", "homeButton": "Revenir à l'accueil",
"text": "Une erreur s'est produite lors de la lecture du média. Veuillez réessayer.", "text": "Une erreur s'est produite lors de la lecture du média. Veuillez réessayer.",
"title": "Oups, c'est coupé !" "title": "Oups, c'est coupé !"
}, },
@@ -376,7 +387,7 @@
"notFound": { "notFound": {
"badge": "Non trouvé", "badge": "Non trouvé",
"detailsButton": "Afficher les détails", "detailsButton": "Afficher les détails",
"homeButton": "Retour à la maison", "homeButton": "Revenir à l'accueil",
"text": "Nous avons cherché parmi nos sources et n'avons pas trouvé les médias que vous recherchez ! Nous n'hébergeons pas les médias et n'avons aucun contrôle sur ce qui est disponible. Veuillez cliquer sur \"Afficher les détails\" ci-dessous pour plus d'informations.", "text": "Nous avons cherché parmi nos sources et n'avons pas trouvé les médias que vous recherchez ! Nous n'hébergeons pas les médias et n'avons aucun contrôle sur ce qui est disponible. Veuillez cliquer sur \"Afficher les détails\" ci-dessous pour plus d'informations.",
"title": "Nous n'avons pas trouvé cela" "title": "Nous n'avons pas trouvé cela"
} }
@@ -408,7 +419,7 @@
"textWithReset": "Echec du chargement de votre profil à partir de votre serveur personnalisé, souhaitez-vous revenir au serveur par défaut ?" "textWithReset": "Echec du chargement de votre profil à partir de votre serveur personnalisé, souhaitez-vous revenir au serveur par défaut ?"
}, },
"migration": { "migration": {
"failed": "La migration de vos données a échoué.", "failed": "La migration de vos données a échouée.",
"inProgress": "Veuillez patienter, nous sommes en train de migrer vos données. Cela ne devrait pas prendre longtemps." "inProgress": "Veuillez patienter, nous sommes en train de migrer vos données. Cela ne devrait pas prendre longtemps."
} }
}, },
@@ -434,18 +445,18 @@
"devices": { "devices": {
"deviceNameLabel": "Nom de l'appareil", "deviceNameLabel": "Nom de l'appareil",
"failed": "Échec du chargement des sessions", "failed": "Échec du chargement des sessions",
"removeDevice": "Enlever", "removeDevice": "Supprimer",
"title": "Appareils" "title": "Appareils"
}, },
"profile": { "profile": {
"finish": "Terminer l'édition", "finish": "Terminer l'édition",
"firstColor": "Couleur de profil un", "firstColor": "Première couleur de profil",
"secondColor": "Couleur de profil deux", "secondColor": "Seconde couleur de profil",
"title": "Éditer la photo de profil", "title": "Modifier la photo de profil",
"userIcon": "Icône de l'utilisateur" "userIcon": "Icône de l'utilisateur"
}, },
"register": { "register": {
"cta": "Commencer", "cta": "Démarrer",
"text": "Partagez la progression de vos films et séries entre vos appareils et gardez-les synchronisés.", "text": "Partagez la progression de vos films et séries entre vos appareils et gardez-les synchronisés.",
"title": "Synchroniser au Cloud" "title": "Synchroniser au Cloud"
}, },
@@ -469,12 +480,12 @@
"urlLabel": "URL du serveur personnalisé" "urlLabel": "URL du serveur personnalisé"
}, },
"setup": { "setup": {
"doSetup": "Faire la configuration", "doSetup": "Configurer",
"errorStatus": { "errorStatus": {
"description": "Il semble qu'un ou plusieurs éléments de cette configuration nécessitent votre attention.", "description": "Il semble qu'un ou plusieurs éléments de cette configuration nécessitent votre attention.",
"title": "Quelque chose nécessite votre attention" "title": "Quelque chose nécessite votre attention"
}, },
"itemError": "Ce paramètre présente un problème. Résolvez le problème en redémarrant la configuration.", "itemError": "Ce paramètre présente un problème. Résolvez le problème en recommençant la configuration.",
"items": { "items": {
"default": "Configuration par défaut", "default": "Configuration par défaut",
"extension": "Extension", "extension": "Extension",
@@ -487,7 +498,7 @@
}, },
"unsetStatus": { "unsetStatus": {
"description": "Pour commencer le processus de configuration, veuillez cliquer sur le bouton à droite.", "description": "Pour commencer le processus de configuration, veuillez cliquer sur le bouton à droite.",
"title": "Vous n'avez pas fait la configuration" "title": "Vous n'avez pas encore effectué la configuration"
} }
}, },
"title": "Connexions", "title": "Connexions",
@@ -514,9 +525,9 @@
"info": { "info": {
"appVersion": "Version de l'application", "appVersion": "Version de l'application",
"backendUrl": "URL de Backend", "backendUrl": "URL de Backend",
"backendVersion": "Version de la Backend", "backendVersion": "Version du Backend",
"hostname": "Nom d'hôte", "hostname": "Nom d'hôte",
"insecure": "Insécure", "insecure": "Non sécurisé",
"notLoggedIn": "Vous n'êtes pas connecté", "notLoggedIn": "Vous n'êtes pas connecté",
"secure": "Sécurisé", "secure": "Sécurisé",
"title": "Informations sur l'application", "title": "Informations sur l'application",
@@ -525,6 +536,7 @@
} }
}, },
"subtitles": { "subtitles": {
"backgroundBlurLabel": "Flou d'arrière-plan",
"backgroundLabel": "Opacité du fond", "backgroundLabel": "Opacité du fond",
"colorLabel": "Couleur", "colorLabel": "Couleur",
"previewQuote": "Plus l'obscurité est profonde, plus la lumière brille.", "previewQuote": "Plus l'obscurité est profonde, plus la lumière brille.",

View File

@@ -116,7 +116,10 @@
"failed": "Error ao encontrar contido... intentao de novo!", "failed": "Error ao encontrar contido... intentao de novo!",
"loading": "Cargando...", "loading": "Cargando...",
"noResults": "Non atopamos nada!", "noResults": "Non atopamos nada!",
"placeholder": "Que che gustaría ver?", "placeholder": {
"default": "Que che gustaría ver?",
"extra": []
},
"sectionTitle": "Resultados da busca" "sectionTitle": "Resultados da busca"
}, },
"titles": { "titles": {
@@ -128,15 +131,11 @@
}, },
"morning": { "morning": {
"default": "Que che gustaría ver esta mañá?", "default": "Que che gustaría ver esta mañá?",
"extra": [ "extra": ["Escoitei que “Antes del amanecer” é boa"]
"Escoitei que “Antes del amanecer” é boa"
]
}, },
"night": { "night": {
"default": "Que che gustaría ver esta noite?", "default": "Que che gustaría ver esta noite?",
"extra": [ "extra": ["Canso? Escoitei que “El Exorcista” é boa."]
"Canso? Escoitei que “El Exorcista” é boa."
]
} }
} }
}, },

View File

@@ -116,21 +116,20 @@
"failed": "મીડિયા શોધવામાં નિષ્ફળ, ફરી પ્રયાસ કરો!", "failed": "મીડિયા શોધવામાં નિષ્ફળ, ફરી પ્રયાસ કરો!",
"loading": "લોડ થાય છે...", "loading": "લોડ થાય છે...",
"noResults": "અમે કંઈપણ શોધી શક્યા નથી!", "noResults": "અમે કંઈપણ શોધી શક્યા નથી!",
"placeholder": "તમે શું જોવા માંગો છો?", "placeholder": {
"default": "તમે શું જોવા માંગો છો?",
"extra": []
},
"sectionTitle": "શોધ પરિણામો" "sectionTitle": "શોધ પરિણામો"
}, },
"titles": { "titles": {
"day": { "day": {
"default": "તમે આ બપોરે શું જોવા માંગો છો?", "default": "તમે આ બપોરે શું જોવા માંગો છો?",
"extra": [ "extra": ["સાહસિક લાગે છે? જુરાસિક પાર્ક યોગ્ય પસંદગી હોઈ શકે છે."]
"સાહસિક લાગે છે? જુરાસિક પાર્ક યોગ્ય પસંદગી હોઈ શકે છે."
]
}, },
"morning": { "morning": {
"default": "તમે આ સવારે શું જોવા માંગો છો?", "default": "તમે આ સવારે શું જોવા માંગો છો?",
"extra": [ "extra": ["હું સાંભળું છું કે Before Sunrise સારું છે"]
"હું સાંભળું છું કે Before Sunrise સારું છે"
]
}, },
"night": { "night": {
"default": "તમે આજે રાત્રે શું જોવા માંગો છો?", "default": "તમે આજે રાત્રે શું જોવા માંગો છો?",

View File

@@ -116,27 +116,24 @@
"failed": "לא הצלחנו למצוא מדיה, נסה שוב!", "failed": "לא הצלחנו למצוא מדיה, נסה שוב!",
"loading": "טוען...", "loading": "טוען...",
"noResults": "לא יכולנו למצוא כלום!", "noResults": "לא יכולנו למצוא כלום!",
"placeholder": "במה תרצה לצפות?", "placeholder": {
"default": "במה תרצה לצפות?",
"extra": []
},
"sectionTitle": "תוצאות חיפוש" "sectionTitle": "תוצאות חיפוש"
}, },
"titles": { "titles": {
"day": { "day": {
"default": "במה תרצה לצפות באחר צהריים זה?", "default": "במה תרצה לצפות באחר צהריים זה?",
"extra": [ "extra": ["מרגיש הרפתקני? פארק היורה עשוי להיות הבחירה המושלמת."]
"מרגיש הרפתקני? פארק היורה עשוי להיות הבחירה המושלמת."
]
}, },
"morning": { "morning": {
"default": "במה תרצה לצפות הבוקר?", "default": "במה תרצה לצפות הבוקר?",
"extra": [ "extra": ["שמעתי שלפני הזריחה זה סרט טוב"]
"שמעתי שלפני הזריחה זה סרט טוב"
]
}, },
"night": { "night": {
"default": "במה תרצה לצפות הלילה?", "default": "במה תרצה לצפות הלילה?",
"extra": [ "extra": ["רוצה לישון? הפיג'מות היא בחירה מצויינת."]
"רוצה לישון? הפיג'מות היא בחירה מצויינת."
]
} }
} }
}, },

View File

@@ -57,6 +57,8 @@
}, },
"host": "आप <0>{{hostname}}</0> से कनेक्ट हो रहे हैं - खाता बनाने से पहले कृपया पुष्टि करें कि आप इस पर भरोसा करते हैं", "host": "आप <0>{{hostname}}</0> से कनेक्ट हो रहे हैं - खाता बनाने से पहले कृपया पुष्टि करें कि आप इस पर भरोसा करते हैं",
"no": "पीछे जाये", "no": "पीछे जाये",
"noHost": "सर्वर कॉन्फ़िगर नहीं किया गया है, इसलिए आप खाता नहीं बना सकते",
"noHostTitle": "सर्वर कॉन्फ़िगर नहीं है!",
"title": "क्या आपको इस सर्वर पर भरोसा है?", "title": "क्या आपको इस सर्वर पर भरोसा है?",
"yes": "मुझे इस सर्वर पर भरोसा है" "yes": "मुझे इस सर्वर पर भरोसा है"
}, },
@@ -116,7 +118,15 @@
"failed": "मीडिया ढूंढने में विफल, पुनः प्रयास करें!", "failed": "मीडिया ढूंढने में विफल, पुनः प्रयास करें!",
"loading": "लोड हो रहा है..।", "loading": "लोड हो रहा है..।",
"noResults": "हमें कुछ नहीं मिला!", "noResults": "हमें कुछ नहीं मिला!",
"placeholder": "क्या देखना चाहते हो?", "placeholder": {
"default": "क्या देखना चाहते हो?",
"extra": [
"आप क्या अन्वेषण करना चाहते हैं?",
"आपकी वॉचलिस्ट में क्या है?",
"आपकी पसंदीदा फिल्म कौनसी है?",
"आपकी पसंदीदा सीरीज़ कौन सी है?"
]
},
"sectionTitle": "खोज के परिणाम" "sectionTitle": "खोज के परिणाम"
}, },
"titles": { "titles": {
@@ -242,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "HLS प्लेलिस्ट लिंक कॉपी करें",
"disclaimer": "डाउनलोड सीधे प्रदाता से लिए जाते हैं। मूवी-वेब का इस पर नियंत्रण नहीं है कि डाउनलोड कैसे प्रदान किए जाते हैं।", "disclaimer": "डाउनलोड सीधे प्रदाता से लिए जाते हैं। मूवी-वेब का इस पर नियंत्रण नहीं है कि डाउनलोड कैसे प्रदान किए जाते हैं।",
"downloadSubtitle": "वर्तमान उपशीर्षक डाउनलोड करें", "downloadSubtitle": "वर्तमान उपशीर्षक डाउनलोड करें",
"downloadVideo": "वीडियो डाउनलोड करें", "downloadVideo": "वीडियो डाउनलोड करें",

View File

@@ -115,7 +115,10 @@
"failed": "Gagal menemukan media, coba lagi!", "failed": "Gagal menemukan media, coba lagi!",
"loading": "Memuat...", "loading": "Memuat...",
"noResults": "Kami tidak dapat menemukan apapun!", "noResults": "Kami tidak dapat menemukan apapun!",
"placeholder": "Apa yang ingin anda tonton?", "placeholder": {
"default": "Apa yang ingin anda tonton?",
"extra": []
},
"sectionTitle": "Hasil pencarian" "sectionTitle": "Hasil pencarian"
}, },
"titles": { "titles": {
@@ -127,15 +130,11 @@
}, },
"morning": { "morning": {
"default": "Apa yang ingin anda tonton pagi ini?", "default": "Apa yang ingin anda tonton pagi ini?",
"extra": [ "extra": ["Kayaknya film Before Sunrise bagus deh"]
"Kayaknya film Before Sunrise bagus deh"
]
}, },
"night": { "night": {
"default": "Apa yang ingin anda tonton malam ini?", "default": "Apa yang ingin anda tonton malam ini?",
"extra": [ "extra": ["Capek? Katanya The Exocist rekomended."]
"Capek? Katanya The Exocist rekomended."
]
} }
} }
}, },

View File

@@ -1,9 +1,9 @@
{ {
"about": { "about": {
"description": "movie-web er vefforrit sem leitar á netinu að straumum. Markmið liðsins er naumhyggju nálgun á að horfa á efni.", "description": "movie-web er vefforrit sem leitar á netinu að streymum. Markmið liðsins er naumhyggju nálgun á að horfa á efni.",
"faqTitle": "Algengar spurningar", "faqTitle": "Algengar spurningar",
"q1": { "q1": {
"body": "movie-web hýsir ekki neitt efni. Þegar þú ýtir á eitthvað til að horfa á, leitað er á netinu fyrir það efni (Þú getur séð hvaða heimild við erum að nota á hleðslu skjánum og í 'myndbands heimildir' flipanum). Skrár eru aldrei settar in af movie-web, allt er í gegnum leitarvél.", "body": "movie-web hýsir ekki neitt efni. Þegar þú ýtir á eitthvað til að horfa á, er leitað á netinu fyrir það efni (Þú getur séð hvaða heimild við erum að nota á hleðslu skjánum og í 'myndbands heimildir' flipanum). Skrár eru aldrei settar inn af movie-web, allt er í gegnum leitarvél.",
"title": "Hvaðan kemur efnið?" "title": "Hvaðan kemur efnið?"
}, },
"q2": { "q2": {
@@ -11,7 +11,7 @@
"title": "Hvar get ég beðið um þætti eða myndir?" "title": "Hvar get ég beðið um þætti eða myndir?"
}, },
"q3": { "q3": {
"body": "Okkar leitar niðurstöður eru knúnar af The Movie Database (TMDB) og eru sýndar þótt að okkar heimildir hafa ekki efnið.", "body": "Leitarniðurstöður okkar eru knúnar af The Movie Database (TMDB) og eru sýndar þótt að heimildir okkar hafa ekki efnið.",
"title": "Leitarniðurstöðurnar sýna þættina eða myndina, af hverju get ég ekki spilað það?" "title": "Leitarniðurstöðurnar sýna þættina eða myndina, af hverju get ég ekki spilað það?"
}, },
"title": "Um movie-web" "title": "Um movie-web"
@@ -57,6 +57,8 @@
}, },
"host": "Þú ert að tengjast við <0>{{hostname}}</0> - vinsamlegast staðfestu að þú treystir því áður en þú býrð til reikning", "host": "Þú ert að tengjast við <0>{{hostname}}</0> - vinsamlegast staðfestu að þú treystir því áður en þú býrð til reikning",
"no": "Fara til baka", "no": "Fara til baka",
"noHost": "Netjónninn hefur ekki verið stilltur, þess vegna getur þú ekki búið til reikning",
"noHostTitle": "Netþjónn ekki stilltur!",
"title": "Treystir þú þessum netþjóni?", "title": "Treystir þú þessum netþjóni?",
"yes": "Ég treysti þessum netþjóni" "yes": "Ég treysti þessum netþjóni"
}, },
@@ -82,13 +84,21 @@
"disclaimer": "Fyrirvari", "disclaimer": "Fyrirvari",
"disclaimerText": "movie-web hýsir engar skrár, það tengist eingöngu þjónustu þriðja aðila. Lagleg atriði ættu að vera rædd við skráarhýsinga og veitanda. movie-web er ekki ábyrg fyrir neinum skrám sýndar af myndbands veitöndum." "disclaimerText": "movie-web hýsir engar skrár, það tengist eingöngu þjónustu þriðja aðila. Lagleg atriði ættu að vera rædd við skráarhýsinga og veitanda. movie-web er ekki ábyrg fyrir neinum skrám sýndar af myndbands veitöndum."
}, },
"links": {
"discord": "Discord",
"dmca": "DMCA",
"github": "GitHub"
},
"tagline": "Horfðu á uppáhalds þætti og myndirnar þínar með þessu opna hugbúnaða forriti." "tagline": "Horfðu á uppáhalds þætti og myndirnar þínar með þessu opna hugbúnaða forriti."
}, },
"global": { "global": {
"name": "movie-web",
"pages": { "pages": {
"about": "Um", "about": "Um",
"dmca": "DMCA",
"login": "Skrá inn", "login": "Skrá inn",
"onboarding": "Setja upp", "onboarding": "Setja upp",
"pagetitle": "{{title}} - movie-web",
"register": "Skrá", "register": "Skrá",
"settings": "Stillingar" "settings": "Stillingar"
} }
@@ -108,7 +118,15 @@
"failed": "Mostókst að finna miðil, reyndu aftur!", "failed": "Mostókst að finna miðil, reyndu aftur!",
"loading": "Hlaðið...", "loading": "Hlaðið...",
"noResults": "Við gátum ekki fundið neitt!", "noResults": "Við gátum ekki fundið neitt!",
"placeholder": "Hvað viltu horfa á?", "placeholder": {
"default": "Hvað viltu horfa á?",
"extra": [
"Hvað viltu kanna?",
"Hvað er á áhorfslistanum þínum?",
"Hvað er uppáhalds bíómyndin þín?",
"Hvað er uppáhalds þáttaröðin þín?"
]
},
"sectionTitle": "Leitar niðurstöður" "sectionTitle": "Leitar niðurstöður"
}, },
"titles": { "titles": {
@@ -192,7 +210,8 @@
"errorConnection": "Gat ekki tengst umboð", "errorConnection": "Gat ekki tengst umboð",
"errorInvalidUrl": "Ekki gildur hlekkur", "errorInvalidUrl": "Ekki gildur hlekkur",
"errorNotProxy": "Bjóst við umboði en fékk heimasíðu", "errorNotProxy": "Bjóst við umboði en fékk heimasíðu",
"label": "Umboðs hlekkur" "label": "Umboðs hlekkur",
"placeholder": "https://"
}, },
"link": "Lærðu hvernig þú býrð til umboð", "link": "Lærðu hvernig þú býrð til umboð",
"submit": "Staðfesta umboð", "submit": "Staðfesta umboð",
@@ -233,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "Afrita HLS spilalista hlekk",
"disclaimer": "Niðurhalningar eru teknar beint frá heimildini. movie-web hefur engan kraft yfir hvernig niðurhalningarnar eru gefnar.", "disclaimer": "Niðurhalningar eru teknar beint frá heimildini. movie-web hefur engan kraft yfir hvernig niðurhalningarnar eru gefnar.",
"downloadSubtitle": "Hlaða niður nú verandi texta", "downloadSubtitle": "Hlaða niður nú verandi texta",
"downloadVideo": "Hlaða niður myndbandi", "downloadVideo": "Hlaða niður myndbandi",
@@ -373,7 +393,10 @@
} }
}, },
"time": { "time": {
"remaining": "{{timeLeft}} er eftir • Þú klárar {{timeFinished, datetime}}" "regular": "{{timeWatched}} / {{duration}}",
"remaining": "{{timeLeft}} er eftir • Þú klárar {{timeFinished, datetime}}",
"shortRegular": "{{timeWatched}}",
"shortRemaining": "-{{timeLeft}}"
}, },
"turnstile": { "turnstile": {
"description": "Vinsamlegast sannreyndu að þú sért manneskja með því að klára Captcha-ið til hægri. Þetta er til þess að halda movie-web öruggu!", "description": "Vinsamlegast sannreyndu að þú sért manneskja með því að klára Captcha-ið til hægri. Þetta er til þess að halda movie-web öruggu!",
@@ -384,7 +407,8 @@
}, },
"screens": { "screens": {
"dmca": { "dmca": {
"text": "Velkomin á sambands síðu movie-web! Við virðum hugverkarétt og viljum ræða einhver höfundarréttar áhyggjur fljótt. Ef að þú trúir að höfundarréttur þíns verks hefur verið misnotaður á okkar síðu, vinsamlegast sentu ítarlega DMCA tilkynningu til netfangsing fyrir neðan þennan texta. Vinsamlegast láttu fylgja með lýsingu af höfundaréttavarna efninu, tengiliða upplýsingat þínar, og yfirlýsingu um góða trú. Við erum staðráðin í að leysa þessi mál tafarlaust og þökkum samstarf þitt við að halda movie-web stað sem virðir sköpunargáfu og höfundarrétt." "text": "Velkomin á sambands síðu movie-web! Við virðum hugverkarétt og viljum ræða einhver höfundarréttar áhyggjur fljótt. Ef að þú trúir að höfundarréttur þíns verks hefur verið misnotaður á okkar síðu, vinsamlegast sentu ítarlega DMCA tilkynningu til netfangsing fyrir neðan þennan texta. Vinsamlegast láttu fylgja með lýsingu af höfundaréttavarna efninu, tengiliða upplýsingat þínar, og yfirlýsingu um góða trú. Við erum staðráðin í að leysa þessi mál tafarlaust og þökkum samstarf þitt við að halda movie-web stað sem virðir sköpunargáfu og höfundarrétt.",
"title": "DMCA"
}, },
"loadingApp": "Hlaðandi forriti", "loadingApp": "Hlaðandi forriti",
"loadingUser": "Hlaðandi þínum reikningi", "loadingUser": "Hlaðandi þínum reikningi",
@@ -483,7 +507,8 @@
"description": "Til að láta forritið virka, allri umboð er beint í gegnum umboð. Virktu þetta ef þú villt koma með þína eigin starfsmenn. <0>Leiðbeiningar.</0>", "description": "Til að láta forritið virka, allri umboð er beint í gegnum umboð. Virktu þetta ef þú villt koma með þína eigin starfsmenn. <0>Leiðbeiningar.</0>",
"emptyState": "Engir starfsmenn komnir, bættu við einum fyir neðan þennan texta", "emptyState": "Engir starfsmenn komnir, bættu við einum fyir neðan þennan texta",
"label": "Notaðu sérsniðaða umboðs starfsmenn", "label": "Notaðu sérsniðaða umboðs starfsmenn",
"urlLabel": "Starfsmanna hlekkir" "urlLabel": "Starfsmanna hlekkir",
"urlPlaceholder": "https://"
} }
}, },
"preferences": { "preferences": {

View File

@@ -1,6 +1,6 @@
{ {
"about": { "about": {
"description": "movie-web è un'applicazione web che cerca per gli stream sull'internet. Il team cerca di usare un approccio per lo più minimalista per usufruire del contenuto.", "description": "movie-web è un'applicazione web che ricerca dei stream sull'internet. Il team cerca di usare un approccio prevalentemente minimalista per usufruire del contenuto.",
"faqTitle": "Domande comuni", "faqTitle": "Domande comuni",
"q1": { "q1": {
"body": "movie-web non ospita alcun contento. Quando clicci su qualcosa da guardare, movie-web cerca l'internet per media di vostra scelta (Sull schermata di caricamento e nell tab 'sorgenti video' si può vedere quale sorgente si sta utilizzando). I media non sono mai caricati da movie-web, tutto è attraverso questo meccanismo di ricerca.", "body": "movie-web non ospita alcun contento. Quando clicci su qualcosa da guardare, movie-web cerca l'internet per media di vostra scelta (Sull schermata di caricamento e nell tab 'sorgenti video' si può vedere quale sorgente si sta utilizzando). I media non sono mai caricati da movie-web, tutto è attraverso questo meccanismo di ricerca.",
@@ -30,14 +30,14 @@
"passphraseFrameLabel": "Frase password", "passphraseFrameLabel": "Frase password",
"title": "La tua frase password" "title": "La tua frase password"
}, },
"hasAccount": "Hai già un account? <0>Accedi </0>", "hasAccount": "Hai già un account? <0>Accedi.</0>",
"login": { "login": {
"description": "Inserisci la tua frase password per accedere al proprio account", "description": "Inserisci la tua frase password per accedere al vostro account",
"deviceLengthError": "Inserisci un nome per il dispositivo", "deviceLengthError": "Inserisci un nome per il dispositivo",
"passphraseLabel": "Frase password di 12 parole", "passphraseLabel": "Frase password di 12 parole",
"passphrasePlaceholder": "Frase password", "passphrasePlaceholder": "Frase password",
"submit": "Accedi", "submit": "Accedi",
"title": "Accedi al proprio account", "title": "Accedi al vostro account",
"validationError": "Frase password incompleta o sbagliata" "validationError": "Frase password incompleta o sbagliata"
}, },
"register": { "register": {
@@ -57,11 +57,13 @@
}, },
"host": "Ti stai collegando a <0>{{hostname}}</0> - conferma la tua fiducia prima di creare un account", "host": "Ti stai collegando a <0>{{hostname}}</0> - conferma la tua fiducia prima di creare un account",
"no": "Indietro", "no": "Indietro",
"noHost": "Il server non è configurato, quindi non si può creare un account",
"noHostTitle": "Server non è configurato!",
"title": "Ti fidi di questo server?", "title": "Ti fidi di questo server?",
"yes": "Mi fido di questo server" "yes": "Mi fido di questo server"
}, },
"verify": { "verify": {
"description": "Inserisci il tuo frase password da prima per confermare che è salvato per creare il proprio account", "description": "Inserisci il tuo frase password da prima per confermare che è salvato per creare un account",
"invalidData": "I dati non sono validi", "invalidData": "I dati non sono validi",
"noMatch": "Frase password non corrisponde", "noMatch": "Frase password non corrisponde",
"passphraseLabel": "La propria frase password di 12 parole", "passphraseLabel": "La propria frase password di 12 parole",
@@ -116,7 +118,15 @@
"failed": "Impossibile trovare i media, riprova!", "failed": "Impossibile trovare i media, riprova!",
"loading": "Caricamento...", "loading": "Caricamento...",
"noResults": "Non abbiamo trovato nulla!", "noResults": "Non abbiamo trovato nulla!",
"placeholder": "Cosa vuoi guardare?", "placeholder": {
"default": "Cosa vuoi guardare?",
"extra": [
"Cosa vorresti esplorare?",
"Cosa cè nella tua lista di guardare?",
"Cosè il vostro preferito film?",
"Cosè il vostro preferito serie?"
]
},
"sectionTitle": "Risultati della ricerca" "sectionTitle": "Risultati della ricerca"
}, },
"titles": { "titles": {
@@ -242,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "Copia link HLS playlist",
"disclaimer": "I download vengono effettuati direttamente dal provider. movie-web non ha il controllo sulle modalità di fornitura dei download.", "disclaimer": "I download vengono effettuati direttamente dal provider. movie-web non ha il controllo sulle modalità di fornitura dei download.",
"downloadSubtitle": "Scarica sottotitolo attuale", "downloadSubtitle": "Scarica sottotitolo attuale",
"downloadVideo": "Scarica video", "downloadVideo": "Scarica video",
@@ -389,9 +400,9 @@
}, },
"turnstile": { "turnstile": {
"description": "Verifica che siate umani completando il Captcha sulla destra. Questo serve a mantenere movie-web sicuro!", "description": "Verifica che siate umani completando il Captcha sulla destra. Questo serve a mantenere movie-web sicuro!",
"error": "Impossibile verificare il proprio umanità. Riprova.", "error": "Impossibile verificare la vostra umanità. Riprova.",
"title": "Dobbiamo verificare che lei sia umano.", "title": "Dobbiamo verificare che lei sia umano.",
"verifyingHumanity": "verificare il proprio umanità..." "verifyingHumanity": "Verificare la vostra umanità..."
} }
}, },
"screens": { "screens": {
@@ -400,12 +411,12 @@
"title": "DMCA" "title": "DMCA"
}, },
"loadingApp": "Caricamento dell'applicazione", "loadingApp": "Caricamento dell'applicazione",
"loadingUser": "Caricamento del proprio profilo", "loadingUser": "Caricamento del vostro profilo",
"loadingUserError": { "loadingUserError": {
"logout": "Esci", "logout": "Esci",
"reset": "Ripristino del server personalizzato", "reset": "Ripristino del server personalizzato",
"text": "Impossibile caricare il proprio profilo", "text": "Impossibile caricare il vostro profilo",
"textWithReset": "Impossibile caricare il proprio profile dal server personalizzato, vorresti ripristinare il server predefinito?" "textWithReset": "Impossibile caricare il vostro profile dal server personalizzato, vorresti ripristinare il server predefinito?"
}, },
"migration": { "migration": {
"failed": "Impossible migrare i propri dati.", "failed": "Impossible migrare i propri dati.",
@@ -424,7 +435,7 @@
"delete": { "delete": {
"button": "Eliminare l'account", "button": "Eliminare l'account",
"confirmButton": "Eliminare l'account", "confirmButton": "Eliminare l'account",
"confirmDescription": "Sei sicuro che vuoi eliminare il proprio account? Tutti i dati propri andranno persi!", "confirmDescription": "Sei sicuro che vuoi eliminare il vostro account? Tutti i dati andranno persi!",
"confirmTitle": "Sei sicuro?", "confirmTitle": "Sei sicuro?",
"text": "Questa azione è irreversibile. Tutti i propri dati verranno eliminati e non sarà possibile recuperare nulla.", "text": "Questa azione è irreversibile. Tutti i propri dati verranno eliminati e non sarà possibile recuperare nulla.",
"title": "Eliminare l'account" "title": "Eliminare l'account"
@@ -525,6 +536,7 @@
} }
}, },
"subtitles": { "subtitles": {
"backgroundBlurLabel": "Sfocatura di sfondo",
"backgroundLabel": "Opacità di sfondo", "backgroundLabel": "Opacità di sfondo",
"colorLabel": "Colore", "colorLabel": "Colore",
"previewQuote": "Cantami o Diva del pelide Achille l'ira funesta.", "previewQuote": "Cantami o Diva del pelide Achille l'ira funesta.",

View File

@@ -76,7 +76,10 @@
"allResults": "それがすべてです!", "allResults": "それがすべてです!",
"loading": "読み込み中...", "loading": "読み込み中...",
"noResults": "見つかりませんでした!", "noResults": "見つかりませんでした!",
"placeholder": "どんな映画を見たい?", "placeholder": {
"default": "どんな映画を見たい?",
"extra": []
},
"sectionTitle": "検索結果" "sectionTitle": "検索結果"
}, },
"titles": { "titles": {

View File

@@ -57,6 +57,8 @@
}, },
"host": "<0>{{hostname}}</0>에 연결 중입니다 - 계정을 만들기 전에 신뢰하는지 확인해 주세요", "host": "<0>{{hostname}}</0>에 연결 중입니다 - 계정을 만들기 전에 신뢰하는지 확인해 주세요",
"no": "뒤로 가기", "no": "뒤로 가기",
"noHost": "서버가 구성되어 있지 않기 때문에 계정을 만들 수 없습니다",
"noHostTitle": "서버가 구성되지 않았습니다!",
"title": "이 서버를 신뢰하십니까?", "title": "이 서버를 신뢰하십니까?",
"yes": "네, 신뢰합니다" "yes": "네, 신뢰합니다"
}, },
@@ -116,7 +118,15 @@
"failed": "미디어 검색에 실패하였습니다, 다시 시도해주세요!", "failed": "미디어 검색에 실패하였습니다, 다시 시도해주세요!",
"loading": "로딩...", "loading": "로딩...",
"noResults": "검색결과가 없습니다!", "noResults": "검색결과가 없습니다!",
"placeholder": "무엇을 보고 싶으신가요?", "placeholder": {
"default": "무엇을 보고 싶으신가요?",
"extra": [
"무엇을 탐험하고 싶으신가요?",
null,
"당신이 가장 좋아하는 영화는?",
"당신이 가장 좋아하는 시리즈는?"
]
},
"sectionTitle": "검색 결과" "sectionTitle": "검색 결과"
}, },
"titles": { "titles": {
@@ -242,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "HLS 플레이리스트 링크 복사하기",
"disclaimer": "다운로드는 제공업체에서 직접 가져옵니다. movie-web은 다운로드 제공 방식을 통제할 수 없습니다.", "disclaimer": "다운로드는 제공업체에서 직접 가져옵니다. movie-web은 다운로드 제공 방식을 통제할 수 없습니다.",
"downloadSubtitle": "현재 자막 다운로드", "downloadSubtitle": "현재 자막 다운로드",
"downloadVideo": "영상 다운로드", "downloadVideo": "영상 다운로드",

View File

@@ -114,7 +114,10 @@
"failed": "Neizdevās atrast multividi. Mēģiniet vēlreiz!", "failed": "Neizdevās atrast multividi. Mēģiniet vēlreiz!",
"loading": "Lādejas...", "loading": "Lādejas...",
"noResults": "Mēs nevarējām neko atrast!", "noResults": "Mēs nevarējām neko atrast!",
"placeholder": "Ko tu gribi skatīties?", "placeholder": {
"default": "Ko tu gribi skatīties?",
"extra": []
},
"sectionTitle": "Meklējuma rezultāti" "sectionTitle": "Meklējuma rezultāti"
}, },
"titles": { "titles": {
@@ -123,15 +126,11 @@
}, },
"morning": { "morning": {
"default": "Ko tu gribētu šorīt noskatīties?", "default": "Ko tu gribētu šorīt noskatīties?",
"extra": [ "extra": ["Es dzirdu, ka Pirms saullēkta ir labs"]
"Es dzirdu, ka Pirms saullēkta ir labs"
]
}, },
"night": { "night": {
"default": "Ko tu gribētu šovakar skatīties?", "default": "Ko tu gribētu šovakar skatīties?",
"extra": [ "extra": ["Noguris? Es dzirdu, ka Exorcist ir labs."]
"Noguris? Es dzirdu, ka Exorcist ir labs."
]
} }
} }
}, },

View File

@@ -115,7 +115,10 @@
"failed": "Failed to banana banana, try again!", "failed": "Failed to banana banana, try again!",
"loading": "Loading...", "loading": "Loading...",
"noResults": "We couldn't banana anything!", "noResults": "We couldn't banana anything!",
"placeholder": "Banana do you want to banana?", "placeholder": {
"default": "Banana do you want to banana?",
"extra": []
},
"sectionTitle": "Banana results" "sectionTitle": "Banana results"
}, },
"titles": { "titles": {
@@ -127,15 +130,11 @@
}, },
"morning": { "morning": {
"default": "What would you like to banana this banana?", "default": "What would you like to banana this banana?",
"extra": [ "extra": ["Banana! I hear Banana Sunrise is banana"]
"Banana! I hear Banana Sunrise is banana"
]
}, },
"night": { "night": {
"default": "What would you like to banana banana?", "default": "What would you like to banana banana?",
"extra": [ "extra": ["Banana? I hear The Banana is banana."]
"Banana? I hear The Banana is banana."
]
} }
} }
}, },

View File

@@ -57,6 +57,8 @@
}, },
"host": "तपाइँ <0>{{hostname}}</0> मा कनेक्ट हुनुहुन्छ - कृपया खाता बनाउनु अघि तपाइँ यसलाई विश्वास गर्नुहुन्छ भनेर पुष्टि गर्नुहोस्", "host": "तपाइँ <0>{{hostname}}</0> मा कनेक्ट हुनुहुन्छ - कृपया खाता बनाउनु अघि तपाइँ यसलाई विश्वास गर्नुहुन्छ भनेर पुष्टि गर्नुहोस्",
"no": "पछाडी जाउ", "no": "पछाडी जाउ",
"noHost": "सर्भर कन्फिगर गरिएको छैन, त्यसैले तपाईंले खाता सिर्जना गर्न सक्नुहुन्न",
"noHostTitle": "सर्भर कन्फिगर गरिएको छैन!",
"title": "के तपाइँ यो सर्भरमा भरोसा गर्नुहुन्छ?", "title": "के तपाइँ यो सर्भरमा भरोसा गर्नुहुन्छ?",
"yes": "म यो सर्भरलाई भरोसा गर्छु" "yes": "म यो सर्भरलाई भरोसा गर्छु"
}, },
@@ -79,6 +81,7 @@
}, },
"footer": { "footer": {
"legal": { "legal": {
"disclaimer": "अस्वीकरण",
"disclaimerText": "movie-webले कुनै पनि फाइलहरू होस्ट गर्दैन, यसले केवल तेस्रो पक्ष सेवाहरूमा लिङ्क गर्दछ। कानुनी मुद्दाहरू फाइल होस्ट र प्रदायकहरूसँग लिनु पर्छ। चलचित्र-वेब भिडियो प्रदायकहरू द्वारा देखाइएका कुनै पनि मिडिया फाइलहरूको लागि जिम्मेवार छैन।" "disclaimerText": "movie-webले कुनै पनि फाइलहरू होस्ट गर्दैन, यसले केवल तेस्रो पक्ष सेवाहरूमा लिङ्क गर्दछ। कानुनी मुद्दाहरू फाइल होस्ट र प्रदायकहरूसँग लिनु पर्छ। चलचित्र-वेब भिडियो प्रदायकहरू द्वारा देखाइएका कुनै पनि मिडिया फाइलहरूको लागि जिम्मेवार छैन।"
}, },
"links": { "links": {
@@ -115,7 +118,15 @@
"failed": "मिडिया फेला पार्न असफल भयो, फेरि प्रयास गर्नुहोस्!", "failed": "मिडिया फेला पार्न असफल भयो, फेरि प्रयास गर्नुहोस्!",
"loading": "लोड गर्दै...", "loading": "लोड गर्दै...",
"noResults": "हामीले केहि फेला पार्न सकेनौं!", "noResults": "हामीले केहि फेला पार्न सकेनौं!",
"placeholder": "तपाईं के हेर्न चाहनुहुन्छ?", "placeholder": {
"default": "तपाईं के हेर्न चाहनुहुन्छ?",
"extra": [
"तपाईं के खोज्न चाहनुहुन्छ?",
"तपाईको वाच लिस्टमा के छ?",
"तपाईलाई मन पर्ने चलचित्र कुन हो?",
"तपाईलाई मनपर्ने श्रृंखला कुन हो?"
]
},
"sectionTitle": "खोज परिणामहरू" "sectionTitle": "खोज परिणामहरू"
}, },
"titles": { "titles": {
@@ -175,11 +186,11 @@
"extension": { "extension": {
"back": "पछाडी जाउ", "back": "पछाडी जाउ",
"explainer": "ब्राउजर एक्सटेन्सन प्रयोग गरेर, तपाईंले हामीले प्रस्ताव गर्नु पर्ने उत्तम स्ट्रिमहरू प्राप्त गर्न सक्नुहुन्छ। केवल एक साधारण स्थापना संग।", "explainer": "ब्राउजर एक्सटेन्सन प्रयोग गरेर, तपाईंले हामीले प्रस्ताव गर्नु पर्ने उत्तम स्ट्रिमहरू प्राप्त गर्न सक्नुहुन्छ। केवल एक साधारण स्थापना संग।",
"explainerIos": "दुर्भाग्यवश, ब्राउजर एक्सटेन्सन IOS मा समर्थित छैन, अर्को विकल्प रोज्न <bold>Go back</bold> थिच्नुहोस्।", "explainerIos": "दुर्भाग्यवश, ब्राउजर एक्सटेन्सन iOS मा समर्थित छैन, अर्को विकल्प रोज्न <bold>Go back</bold> थिच्नुहोस्।",
"extensionHelp": "यदि तपाईंले एक्स्टेन्सन स्थापना गर्नुभएको छ तर यो पत्ता लागेको छैन <bold>तपाईंको ब्राउजर विस्तार मेनु मार्फत विस्तार खोल्नुहोस्</bold> र स्क्रिनमा चरणहरू पालना गर्नुहोस्।", "extensionHelp": "यदि तपाईंले एक्स्टेन्सन स्थापना गर्नुभएको छ तर यो पत्ता लागेको छैन, <bold>तपाईंको ब्राउजर विस्तार मेनु मार्फत विस्तार खोल्नुहोस्</bold> र स्क्रिनमा चरणहरू पालना गर्नुहोस्।",
"linkChrome": "क्रोम एक्सटेन्सन स्थापना गर्नुहोस्", "linkChrome": "क्रोम एक्सटेन्सन स्थापना गर्नुहोस्",
"linkFirefox": "फायरफक्स एक्सटेन्सन स्थापना गर्नुहोस्", "linkFirefox": "फायरफक्स एक्सटेन्सन स्थापना गर्नुहोस्",
"notDetecting": "chrome मा स्थापित तर देखिदैन? पृष्ठ पुन: लोड गर्ने प्रयास गर्नुहोस्!", "notDetecting": "Chrome मा स्थापित भयो तर देखिदैन? पृष्ठ पुन: लोड गर्ने प्रयास गर्नुहोस्!",
"notDetectingAction": "पृष्ठ पुन: लोड गर्नुहोस्", "notDetectingAction": "पृष्ठ पुन: लोड गर्नुहोस्",
"status": { "status": {
"disallowed": "यो पेजको लागि एक्सटेन्सन सक्षम गरिएको छैन", "disallowed": "यो पेजको लागि एक्सटेन्सन सक्षम गरिएको छैन",
@@ -207,7 +218,7 @@
"title": "एउटा नयाँ प्रोक्सी बनाऔं" "title": "एउटा नयाँ प्रोक्सी बनाऔं"
}, },
"start": { "start": {
"explainer": "सम्भावित उत्तम स्ट्रिमहरू प्राप्त गर्न, तपाईंले कुन स्ट्रिमिङ विधि प्रयोग गर्न चाहनुहुन्छ भनेर छनौट गर्न आवश्यक हुनेछ।", "explainer": "सम्भावित उत्तम स्ट्रिमहरू प्राप्त गर्न, तपाईंले कुन स्ट्रिमिङ विधि प्रयोग गर्न चाहनुहुन्छ भनेर छनौट गर्न आवश्यक हुनेछ।",
"options": { "options": {
"default": { "default": {
"text": "मलाई राम्रो गुणस्तरका स्ट्रिमहरू चाहिँदैन,<0 /> <1>पूर्वनिर्धारित सेटअप प्रयोग गर्नुहोस्</1>" "text": "मलाई राम्रो गुणस्तरका स्ट्रिमहरू चाहिँदैन,<0 /> <1>पूर्वनिर्धारित सेटअप प्रयोग गर्नुहोस्</1>"
@@ -241,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "HLS प्लेलिस्ट लिङ्क कपि गर्नुहोस्",
"disclaimer": "डाउनलोडहरू सीधा प्रदायकबाट लिइन्छ। movie-web ले डाउनलोडहरू कसरी प्रदान गरिन्छ भन्नेमा नियन्त्रण गर्दैन।", "disclaimer": "डाउनलोडहरू सीधा प्रदायकबाट लिइन्छ। movie-web ले डाउनलोडहरू कसरी प्रदान गरिन्छ भन्नेमा नियन्त्रण गर्दैन।",
"downloadSubtitle": "हालको उपशीर्षक डाउनलोड गर्नुहोस्", "downloadSubtitle": "हालको उपशीर्षक डाउनलोड गर्नुहोस्",
"downloadVideo": "डाउनलोड भिडियो", "downloadVideo": "डाउनलोड भिडियो",

View File

@@ -57,6 +57,8 @@
}, },
"host": "Je gaat zo verbinden met <0>{{hostname}}</0>, check even of je deze link vertrouwt", "host": "Je gaat zo verbinden met <0>{{hostname}}</0>, check even of je deze link vertrouwt",
"no": "Vorige pagina", "no": "Vorige pagina",
"noHost": "De server is nog niet geconfigureerd, daarom kunt u geen account aanmaken",
"noHostTitle": "Server niet geconfigureerd!",
"title": "Vertrouw je deze server?", "title": "Vertrouw je deze server?",
"yes": "Ik vertrouw deze server" "yes": "Ik vertrouw deze server"
}, },
@@ -116,7 +118,15 @@
"failed": "Het is niet gelukt de media te laden, probeer het nog eens!", "failed": "Het is niet gelukt de media te laden, probeer het nog eens!",
"loading": "Aan het zoeken...", "loading": "Aan het zoeken...",
"noResults": "We konden helaas niets vinden!", "noResults": "We konden helaas niets vinden!",
"placeholder": "Wat wil je graag kijken?", "placeholder": {
"default": "Wat wil je graag kijken?",
"extra": [
"Wat wil je verkennen?",
"Wat staat er op jouw kijklijst?",
"Wat is jouw favoriete film?",
"Wat is jouw favoriete serie?"
]
},
"sectionTitle": "Zoekresultaten" "sectionTitle": "Zoekresultaten"
}, },
"titles": { "titles": {
@@ -177,10 +187,10 @@
"back": "Terug", "back": "Terug",
"explainer": "Door gebruik te maken van de browserextensie kun je de beste streams krijgen. Met slechts een eenvoudige installatie.", "explainer": "Door gebruik te maken van de browserextensie kun je de beste streams krijgen. Met slechts een eenvoudige installatie.",
"explainerIos": "Helaas, de browserextensie is niet ondersteund op iOS. Druk op <bold>Terug</bold> om een andere optie te kiezen.", "explainerIos": "Helaas, de browserextensie is niet ondersteund op iOS. Druk op <bold>Terug</bold> om een andere optie te kiezen.",
"extensionHelp": "Als je de extensie hebt geïnstalleerd maar niet wordt gedetecteerd, <bold>Open dan de extensie via het extensies menu in je browser</bold> en volg de stappen op het scherm.", "extensionHelp": "Als je de extensie hebt geïnstalleerd maar niet wordt gedetecteerd, <bold>open dan de extensie via het extensies menu in je browser</bold> en volg de stappen op het scherm.",
"linkChrome": "Installeer de Chrome-extensie", "linkChrome": "Installeer de Chrome-extensie",
"linkFirefox": "Installeer de Firefox-extensie", "linkFirefox": "Installeer de Firefox-extensie",
"notDetecting": "Geïnstalleerd op Chrome maar wordt niet weergegeven? Probeer de pagina opnieuw te laden!", "notDetecting": "Geïnstalleerd op Chrome, maar de site detecteert het niet? Probeer de pagina opnieuw te laden!",
"notDetectingAction": "Pagina opnieuw laden", "notDetectingAction": "Pagina opnieuw laden",
"status": { "status": {
"disallowed": "Extensie is niet ingeschakeld voor deze pagina", "disallowed": "Extensie is niet ingeschakeld voor deze pagina",
@@ -208,7 +218,7 @@
"title": "Laten we een nieuwe proxy instellen" "title": "Laten we een nieuwe proxy instellen"
}, },
"start": { "start": {
"explainer": "Om de beste mogelijke streams te krijgen, moet je kiezen welke streamingmethode je wilt gebruiken.", "explainer": "Om de beste streams mogelijk te krijgen, moet je kiezen welke streammethode je wilt gebruiken.",
"options": { "options": {
"default": { "default": {
"text": "Ik wil geen streams van goede kwaliteit, <0 /> <1>Gebruik de standaardinstellingen.</1>" "text": "Ik wil geen streams van goede kwaliteit, <0 /> <1>Gebruik de standaardinstellingen.</1>"
@@ -242,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "HLS-afspeellijstlink kopiëren",
"disclaimer": "Downloads worden direct bij de bron opgehaald. movie-web heeft geen controle over het bestand dat je ontvangt.", "disclaimer": "Downloads worden direct bij de bron opgehaald. movie-web heeft geen controle over het bestand dat je ontvangt.",
"downloadSubtitle": "Download huidige ondertiteling", "downloadSubtitle": "Download huidige ondertiteling",
"downloadVideo": "Download filmpje", "downloadVideo": "Download filmpje",
@@ -525,6 +536,7 @@
} }
}, },
"subtitles": { "subtitles": {
"backgroundBlurLabel": "Achtergrondvervaging",
"backgroundLabel": "Achtergrond dekking", "backgroundLabel": "Achtergrond dekking",
"colorLabel": "Kleur", "colorLabel": "Kleur",
"previewQuote": "Ik mag niet bang zijn. Angst doodt de geest.", "previewQuote": "Ik mag niet bang zijn. Angst doodt de geest.",

View File

@@ -57,6 +57,8 @@
}, },
"host": "ਤੁਸੀਂ <0>{{hostname}}</0> ਨਾਲ ਜੁੜ ਰਹੇ ਹੋ - ਕਿਰਪਾ ਕਰਕੇ ਖਾਤਾ ਬਣਾਉਣ ਤੋਂ ਪਹਿਲਾਂ ਪੁਸ਼ਟੀ ਕਰੋ ਕਿ ਤੁਸੀਂ ਇਸ 'ਤੇ ਭਰੋਸਾ ਕਰਦੇ ਹੋ", "host": "ਤੁਸੀਂ <0>{{hostname}}</0> ਨਾਲ ਜੁੜ ਰਹੇ ਹੋ - ਕਿਰਪਾ ਕਰਕੇ ਖਾਤਾ ਬਣਾਉਣ ਤੋਂ ਪਹਿਲਾਂ ਪੁਸ਼ਟੀ ਕਰੋ ਕਿ ਤੁਸੀਂ ਇਸ 'ਤੇ ਭਰੋਸਾ ਕਰਦੇ ਹੋ",
"no": "ਵਾਪਸ ਜਾਓ", "no": "ਵਾਪਸ ਜਾਓ",
"noHost": "ਸਰਵਰ ਕੌਂਫਿਗਰ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ, ਇਸਲਈ ਤੁਸੀਂ ਖਾਤਾ ਨਹੀਂ ਬਣਾ ਸਕਦੇ ਹੋ",
"noHostTitle": "ਸਰਵਰ ਕੌਂਫਿਗਰ ਨਹੀਂ ਕੀਤਾ ਗਿਆ!",
"title": "ਕੀ ਤੁਸੀਂ ਇਸ ਸਰਵਰ 'ਤੇ ਭਰੋਸਾ ਕਰਦੇ ਹੋ?", "title": "ਕੀ ਤੁਸੀਂ ਇਸ ਸਰਵਰ 'ਤੇ ਭਰੋਸਾ ਕਰਦੇ ਹੋ?",
"yes": "ਮੈਨੂੰ ਇਸ ਸਰਵਰ 'ਤੇ ਭਰੋਸਾ ਹੈ" "yes": "ਮੈਨੂੰ ਇਸ ਸਰਵਰ 'ਤੇ ਭਰੋਸਾ ਹੈ"
}, },
@@ -116,7 +118,15 @@
"failed": "ਮੀਡੀਆ ਲੱਭਣ ਵਿੱਚ ਅਸਫਲ, ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ!", "failed": "ਮੀਡੀਆ ਲੱਭਣ ਵਿੱਚ ਅਸਫਲ, ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ!",
"loading": "ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...", "loading": "ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ...",
"noResults": "ਅਸੀਂ ਕੁਝ ਵੀ ਨਹੀਂ ਲੱਭ ਸਕੇ!", "noResults": "ਅਸੀਂ ਕੁਝ ਵੀ ਨਹੀਂ ਲੱਭ ਸਕੇ!",
"placeholder": "ਤੁਸੀਂ ਕੀ ਦੇਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?", "placeholder": {
"default": "ਤੁਸੀਂ ਕੀ ਦੇਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?",
"extra": [
"ਤੁਸੀਂ ਕੀ ਪੜਚੋਲ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?",
"ਤੁਹਾਡੀ ਨਿਗਰਾਨੀ ਸੂਚੀ ਵਿੱਚ ਕੀ ਹੈ?",
"ਤੁਹਾਡੀ ਮਨਪਸੰਦ ਫਿਲਮ ਕਿਹੜੀ ਹੈ?",
"ਤੁਹਾਡੀ ਮਨਪਸੰਦ ਲੜੀ ਕਿਹੜੀ ਹੈ?"
]
},
"sectionTitle": "ਖੋਜ ਨਤੀਜੇ" "sectionTitle": "ਖੋਜ ਨਤੀਜੇ"
}, },
"titles": { "titles": {
@@ -242,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "HLS ਪਲੇਲਿਸਟ ਲਿੰਕ ਕਾਪੀ ਕਰੋ",
"disclaimer": "ਡਾਊਨਲੋਡ ਸਿੱਧੇ ਪ੍ਰਦਾਤਾ ਤੋਂ ਲਏ ਜਾਂਦੇ ਹਨ. ਮੂਵੀ-ਵੈੱਬ ਦਾ ਇਸ 'ਤੇ ਕੰਟਰੋਲ ਨਹੀਂ ਹੈ ਕਿ ਡਾਊਨਲੋਡ ਕਿਵੇਂ ਪ੍ਰਦਾਨ ਕੀਤੇ ਜਾਂਦੇ ਹਨ.", "disclaimer": "ਡਾਊਨਲੋਡ ਸਿੱਧੇ ਪ੍ਰਦਾਤਾ ਤੋਂ ਲਏ ਜਾਂਦੇ ਹਨ. ਮੂਵੀ-ਵੈੱਬ ਦਾ ਇਸ 'ਤੇ ਕੰਟਰੋਲ ਨਹੀਂ ਹੈ ਕਿ ਡਾਊਨਲੋਡ ਕਿਵੇਂ ਪ੍ਰਦਾਨ ਕੀਤੇ ਜਾਂਦੇ ਹਨ.",
"downloadSubtitle": "ਮੌਜੂਦਾ ਉਪਸਿਰਲੇਖ ਡਾਊਨਲੋਡ ਕਰੋ", "downloadSubtitle": "ਮੌਜੂਦਾ ਉਪਸਿਰਲੇਖ ਡਾਊਨਲੋਡ ਕਰੋ",
"downloadVideo": "ਵੀਡੀਓ ਡਾਊਨਲੋਡ ਕਰੋ", "downloadVideo": "ਵੀਡੀਓ ਡਾਊਨਲੋਡ ਕਰੋ",

View File

@@ -116,7 +116,10 @@
"failed": "Arrrr failed to find media, try again!", "failed": "Arrrr failed to find media, try again!",
"loading": "Hold yer horses, me heartie!", "loading": "Hold yer horses, me heartie!",
"noResults": "We couldn't find anythin', arrr!", "noResults": "We couldn't find anythin', arrr!",
"placeholder": "What do ye want to watch?", "placeholder": {
"default": "What do ye want to watch?",
"extra": []
},
"sectionTitle": "Searchin' results" "sectionTitle": "Searchin' results"
}, },
"titles": { "titles": {

View File

@@ -116,7 +116,9 @@
"failed": "Nie udało się znaleźć mediów, Spróbuj ponownie!", "failed": "Nie udało się znaleźć mediów, Spróbuj ponownie!",
"loading": "Wczytywanie...", "loading": "Wczytywanie...",
"noResults": "Nie mogliśmy niczego znaleźć!", "noResults": "Nie mogliśmy niczego znaleźć!",
"placeholder": "Co chciałbyś obejrzeć?", "placeholder": {
"default": "Co chciałbyś obejrzeć?"
},
"sectionTitle": "Wyniki wyszukiwania" "sectionTitle": "Wyniki wyszukiwania"
}, },
"titles": { "titles": {
@@ -177,7 +179,7 @@
"back": "Wstecz", "back": "Wstecz",
"explainer": "Korzystając z rozszerzenia przeglądarki, możesz uzyskać najlepsze strumienie. Wystarczy prosta instalacja.", "explainer": "Korzystając z rozszerzenia przeglądarki, możesz uzyskać najlepsze strumienie. Wystarczy prosta instalacja.",
"explainerIos": "Niestety, rozszerzenie przeglądarki nie jest obsługiwane w systemie iOS, naciśnij <bold>Wstecz</bold>, aby wybrać inną opcję.", "explainerIos": "Niestety, rozszerzenie przeglądarki nie jest obsługiwane w systemie iOS, naciśnij <bold>Wstecz</bold>, aby wybrać inną opcję.",
"extensionHelp": "Jeżeli zainstalowałeś rozszerzenie, ale nie zostało ono wykryte. <bold>Otwórz rozszerzenie za pomocą menu rozszerzeń przeglądarki</bold> i postępuj zgodnie z instrukcjami wyświetlanymi na ekranie.", "extensionHelp": "Jeżeli zainstalowałeś rozszerzenie, ale nie zostało ono wykryte, <bold>otwórz rozszerzenie za pomocą menu rozszerzeń przeglądarki</bold> i postępuj zgodnie z instrukcjami wyświetlanymi na ekranie.",
"linkChrome": "Zainstaluj rozszerzenie na Chrome", "linkChrome": "Zainstaluj rozszerzenie na Chrome",
"linkFirefox": "Zainstaluj rozszerzenie na Firefox", "linkFirefox": "Zainstaluj rozszerzenie na Firefox",
"notDetecting": "Zainstalowano na Chrome, ale się nie wyświetla? Spróbuj odświeżyć stronę!", "notDetecting": "Zainstalowano na Chrome, ale się nie wyświetla? Spróbuj odświeżyć stronę!",
@@ -208,7 +210,7 @@
"title": "Stwórzmy nowe proxy" "title": "Stwórzmy nowe proxy"
}, },
"start": { "start": {
"explainer": "Aby uzyskać najlepsze transmisje strumieniowe. Będziesz musiał wybrać metodę strumieniowania, której chcesz użyć.", "explainer": "Aby uzyskać najlepsze transmisje strumieniowe, będziesz musiał wybrać metodę strumieniowania której chcesz użyć.",
"options": { "options": {
"default": { "default": {
"text": "Nie chcę dobrej jakości strumieni, <0 /> <1>użyj domyślnej konfiguracji</1>" "text": "Nie chcę dobrej jakości strumieni, <0 /> <1>użyj domyślnej konfiguracji</1>"
@@ -525,6 +527,7 @@
} }
}, },
"subtitles": { "subtitles": {
"backgroundBlurLabel": "Rozmycie tła",
"backgroundLabel": "Krycie tła", "backgroundLabel": "Krycie tła",
"colorLabel": "Kolor", "colorLabel": "Kolor",
"previewQuote": "Nie wolno mi się bać. Strach zabija myślenie.", "previewQuote": "Nie wolno mi się bać. Strach zabija myślenie.",

View File

@@ -116,7 +116,10 @@
"failed": "Falha ao encontrar mídia, tente novamente!", "failed": "Falha ao encontrar mídia, tente novamente!",
"loading": "Carregando...", "loading": "Carregando...",
"noResults": "Não conseguimos encontrar nada!", "noResults": "Não conseguimos encontrar nada!",
"placeholder": "O que você quer assistir?", "placeholder": {
"default": "O que você quer assistir?",
"extra": []
},
"sectionTitle": "Resultados da pesquisa" "sectionTitle": "Resultados da pesquisa"
}, },
"titles": { "titles": {
@@ -128,15 +131,11 @@
}, },
"morning": { "morning": {
"default": "O que você gostaria de assistir esta manhã?", "default": "O que você gostaria de assistir esta manhã?",
"extra": [ "extra": ["Ouvi dizer que Antes do Amanhecer é bom"]
"Ouvi dizer que Antes do Amanhecer é bom"
]
}, },
"night": { "night": {
"default": "O que você gostaria de assistir esta noite?", "default": "O que você gostaria de assistir esta noite?",
"extra": [ "extra": ["Cansado? Ouvi dizer que O Exorcista é bom."]
"Cansado? Ouvi dizer que O Exorcista é bom."
]
} }
} }
}, },
@@ -242,6 +241,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "Copiar link da playlist HLS",
"disclaimer": "Os downloads são feitos diretamente do provedor. movie-web não tem controle sobre como os downloads são fornecidos.", "disclaimer": "Os downloads são feitos diretamente do provedor. movie-web não tem controle sobre como os downloads são fornecidos.",
"downloadSubtitle": "Baixar legenda atual", "downloadSubtitle": "Baixar legenda atual",
"downloadVideo": "Baixar vídeo", "downloadVideo": "Baixar vídeo",

View File

@@ -115,7 +115,10 @@
"failed": "Falha ao encontrar mídia, tente novamente!", "failed": "Falha ao encontrar mídia, tente novamente!",
"loading": "A carregar...", "loading": "A carregar...",
"noResults": "Não conseguimos encontrar nada!", "noResults": "Não conseguimos encontrar nada!",
"placeholder": "O que deseja assistir?", "placeholder": {
"default": "O que deseja assistir?",
"extra": []
},
"sectionTitle": "Resultados da pesquisa" "sectionTitle": "Resultados da pesquisa"
}, },
"titles": { "titles": {
@@ -127,15 +130,11 @@
}, },
"morning": { "morning": {
"default": "O que gostaria de assistir esta manhã?", "default": "O que gostaria de assistir esta manhã?",
"extra": [ "extra": ["Dizem que Antes do Amanhecer é bom"]
"Dizem que Antes do Amanhecer é bom"
]
}, },
"night": { "night": {
"default": "O que gostaria de assistir esta noite?", "default": "O que gostaria de assistir esta noite?",
"extra": [ "extra": ["Cansado? Dizem que O Exorcista é bom."]
"Cansado? Dizem que O Exorcista é bom."
]
} }
} }
}, },

View File

@@ -114,7 +114,10 @@
"failed": "Găsire media eșuată, încearcă din nou!", "failed": "Găsire media eșuată, încearcă din nou!",
"loading": "Se încarcă...", "loading": "Se încarcă...",
"noResults": "Nu am putut găsi nimic!", "noResults": "Nu am putut găsi nimic!",
"placeholder": "La ce dorești să te uiți?", "placeholder": {
"default": "La ce dorești să te uiți?",
"extra": []
},
"sectionTitle": "Rezultate de căutare" "sectionTitle": "Rezultate de căutare"
}, },
"titles": { "titles": {
@@ -126,15 +129,11 @@
}, },
"morning": { "morning": {
"default": "La ce dorești să te in uiți dimineață aceasta?", "default": "La ce dorești să te in uiți dimineață aceasta?",
"extra": [ "extra": ["Aud că Before Sunrise este bun"]
"Aud că Before Sunrise este bun"
]
}, },
"night": { "night": {
"default": "La ce dorești să te uiți în astă seară?", "default": "La ce dorești să te uiți în astă seară?",
"extra": [ "extra": ["Obosit? Aud că The Exorcist is good."]
"Obosit? Aud că The Exorcist is good."
]
} }
} }
}, },

View File

@@ -57,6 +57,8 @@
}, },
"host": "Вы подключаетесь к <0>{{hostname}}</0> - пожалуйста, подтвердите, что вы доверяете ему, прежде чем создавать учётную запись", "host": "Вы подключаетесь к <0>{{hostname}}</0> - пожалуйста, подтвердите, что вы доверяете ему, прежде чем создавать учётную запись",
"no": "Вернуться назад", "no": "Вернуться назад",
"noHost": "Сервер не был настроен, поэтому вы не можете создать учётную запись",
"noHostTitle": "Сервер не настроен!",
"title": "Вы доверяете этому серверу?", "title": "Вы доверяете этому серверу?",
"yes": "Я доверяю этому серверу" "yes": "Я доверяю этому серверу"
}, },
@@ -116,7 +118,15 @@
"failed": "Не удалось найти медиафайл, попробуйте снова!", "failed": "Не удалось найти медиафайл, попробуйте снова!",
"loading": "Загрузка...", "loading": "Загрузка...",
"noResults": "Мы ничего не нашли!", "noResults": "Мы ничего не нашли!",
"placeholder": "Что вы хотите посмотреть?", "placeholder": {
"default": "Что вы хотите посмотреть?",
"extra": [
"Что вы хотите исследовать?",
"Что в вашем списке?",
"Какой ваш любимый фильм?",
"Какой ваш любимый сериал?"
]
},
"sectionTitle": "Результаты поиска" "sectionTitle": "Результаты поиска"
}, },
"titles": { "titles": {
@@ -242,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "Скопировать ссылку на плейлист HLS",
"disclaimer": "Загрузки осуществляются непосредственно поставщиком. movie-web не контролирует способ предоставления загрузок.", "disclaimer": "Загрузки осуществляются непосредственно поставщиком. movie-web не контролирует способ предоставления загрузок.",
"downloadSubtitle": "Скачать текущие субтитры", "downloadSubtitle": "Скачать текущие субтитры",
"downloadVideo": "Скачать видео", "downloadVideo": "Скачать видео",
@@ -525,6 +536,7 @@
} }
}, },
"subtitles": { "subtitles": {
"backgroundBlurLabel": "Размытие фона",
"backgroundLabel": "Прозрачность фона", "backgroundLabel": "Прозрачность фона",
"colorLabel": "Цвет", "colorLabel": "Цвет",
"previewQuote": "Я не должен бояться. Страх убивает разум.", "previewQuote": "Я не должен бояться. Страх убивает разум.",

View File

@@ -57,6 +57,8 @@
}, },
"host": "Povezujete se z <0>{{hostname}}</0> - pred ustvarjanjem računa potrdite, da mu zaupate", "host": "Povezujete se z <0>{{hostname}}</0> - pred ustvarjanjem računa potrdite, da mu zaupate",
"no": "Nazaj", "no": "Nazaj",
"noHost": "Strežnik ni nastavljen, zato ustvarjanje profila ni mogoče",
"noHostTitle": "Strežnik ni nastavljen!",
"title": "Ali zaupate temu strežniku?", "title": "Ali zaupate temu strežniku?",
"yes": "Zaupam strežniku" "yes": "Zaupam strežniku"
}, },
@@ -95,6 +97,7 @@
"about": "O projektu", "about": "O projektu",
"dmca": "DMCA", "dmca": "DMCA",
"login": "Prijava", "login": "Prijava",
"onboarding": "Nastavitev",
"pagetitle": "{{title}} - movie-web", "pagetitle": "{{title}} - movie-web",
"register": "Registriraj se", "register": "Registriraj se",
"settings": "Nastavitve" "settings": "Nastavitve"
@@ -115,7 +118,15 @@
"failed": "Ni uspelo najti medija, prosim poskusite znova!", "failed": "Ni uspelo najti medija, prosim poskusite znova!",
"loading": "Nalaganje...", "loading": "Nalaganje...",
"noResults": "Vsebin nismo našli!", "noResults": "Vsebin nismo našli!",
"placeholder": "Kaj si želite gledati?", "placeholder": {
"default": "Kaj si želite gledati?",
"extra": [
"Kaj želiš raziskati danes?",
"Kateri fdilm/serijo si boš ogledal/a naslednje?",
"Kateri film ti je najljubši?",
"Katera je tvoja najljubša serija?"
]
},
"sectionTitle": "Rezultati iskanja" "sectionTitle": "Rezultati iskanja"
}, },
"titles": { "titles": {
@@ -165,6 +176,69 @@
"message": "Iskali smo povsod: pod preprogo, v omari, za \"proxyjem\", vendar na koncu nismo našli strani, ki jo iščete.", "message": "Iskali smo povsod: pod preprogo, v omari, za \"proxyjem\", vendar na koncu nismo našli strani, ki jo iščete.",
"title": "Ni bilo mogoče najti te strani" "title": "Ni bilo mogoče najti te strani"
}, },
"onboarding": {
"defaultConfirm": {
"cancel": "Prekliči",
"confirm": "Upoeabi privzete nastavitve",
"description": "Privzeta nastavitev nima najboljših pretokov in je lahko neznosno počasna.",
"title": "Si prepričan?"
},
"extension": {
"back": "Nazaj",
"explainer": "Z razširitvijo brskalnika lahko dobite najboljše tokove, ki jih ponujamo. Namestitev je zelo preprosta!",
"explainerIos": "Na žalost razširitev brskalnika ni podprta v sistemu iOS, Pritisnite <bold>Najdi nazaj</bold> in izberite drugo možnost.",
"extensionHelp": "Če ste namestili razširitev, vendar ni zaznana, <bold>odprite razširitev v meniju razširitev brskalnika</bold> in sledite navodilom na zaslonu.",
"linkChrome": "Naloži Chrome razširitev",
"linkFirefox": "Naloži Firefox razširitev",
"notDetecting": "Nameščen v brskalniku Chrome, vendar ga spletno mesto ne zazna? Poskusite znova naložiti stran!",
"notDetectingAction": "Osveži stran",
"status": {
"disallowed": "Razširitev ni omogočena na tej strani",
"disallowedAction": "Omogoči razširitev",
"failed": "Ni uspelo zahtevati statusa",
"loading": "Čakamo, da namestite razširitev",
"outdated": "Verzija razširitve je prestara",
"success": "Razširitev deluje pravilno!"
},
"submit": "Nadaljuj",
"title": "Začnimo z razširitvijo"
},
"proxy": {
"back": "Nazaj",
"explainer": "Z metodo proxy lahko s samopostrežnim proxyjem pridobite zelo kakovostne prenose.",
"input": {
"errorConnection": "Povezava s proxyjem ni uspela",
"errorInvalidUrl": "Ni validen URL",
"errorNotProxy": "Pričakoval proxy, a dobil spletno stran",
"label": "URL proxyja",
"placeholder": "https://"
},
"link": "Kako postavim proxy",
"submit": "Oddaj proxy",
"title": "Naredimo nov proxy"
},
"start": {
"explainer": "Da bi dobili najboljše možne prenose, morate izbrati metodo pretakanja, ki jo želite uporabiti.",
"options": {
"default": {
"text": "Ne želim dobre kakovosti tokov,<0 /> <1>uporabite privzeto nastavitev</1>"
},
"extension": {
"action": "Naloži razširitev",
"description": "Namestite razširitev brskalnika in pridobite dostop do najboljših virov.",
"quality": "Najboljša kvaliteta",
"title": "Razširitev brskalnika"
},
"proxy": {
"action": "Nastavi proxy",
"description": "V samo 5 minutah nastavite proxy in pridobite dostop do odličnih virov.",
"quality": "Dobra kvaliteta",
"title": "Proxy po meri"
}
},
"title": "Pripravimo vas na namestitev s filmskim spletom"
}
},
"overlays": { "overlays": {
"close": "Zapri" "close": "Zapri"
}, },
@@ -178,10 +252,11 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "Kopiranje povezave do seznama predvajanja HLS",
"disclaimer": "Prenosi se opravijo neposredno pri ponudniku vsebin. movie-web nima nadzora nad načinom zagotavljanja prenosov.", "disclaimer": "Prenosi se opravijo neposredno pri ponudniku vsebin. movie-web nima nadzora nad načinom zagotavljanja prenosov.",
"downloadSubtitle": "Prenesi trenutne podnapise", "downloadSubtitle": "Prenesi trenutne podnapise",
"downloadVideo": "Prenesi video", "downloadVideo": "Prenesi video",
"hlsDisclaimer": "Prenosi se opravijo neposredno pri ponudniku vsebin. movie-web nima nadzora nad načinom zagotavljanja prenosov. Upoštevajte, da prenašate seznam predvajanja tipa \"HLS\"; ta je namenjen uporabnikom, ki poznajo napredno pretakanje večpredstavnostnih vsebin.", "hlsDisclaimer": "Prenosi se opravijo neposredno pri ponudniku vsebin. movie-web nima nadzora nad načinom zagotavljanja prenosov. Upoštevajte, da prenašate seznam predvajanja tipa \"HLS\" namenjen uporabnikom, ki poznajo napredno pretakanje večpredstavnostnih vsebin.",
"onAndroid": { "onAndroid": {
"1": "Če želite prenesti v sistemu Android, kliknite gumb za prenos, nato na novi strani <bold>tipnite in držite</bold> videoposnetek ter izberite <bold>shrani</bold>.", "1": "Če želite prenesti v sistemu Android, kliknite gumb za prenos, nato na novi strani <bold>tipnite in držite</bold> videoposnetek ter izberite <bold>shrani</bold>.",
"shortTitle": "Prenesi / Android", "shortTitle": "Prenesi / Android",
@@ -205,7 +280,8 @@
"episodeBadge": "E{{episode}}", "episodeBadge": "E{{episode}}",
"loadingError": "Napaka pri nalaganju sezone", "loadingError": "Napaka pri nalaganju sezone",
"loadingList": "Nalaganje...", "loadingList": "Nalaganje...",
"loadingTitle": "Nalaganje..." "loadingTitle": "Nalaganje...",
"unairedEpisodes": "Ena ali več epizod v tej sezoni so onemogočene, ker še niso bile predvajane."
}, },
"playback": { "playback": {
"speedLabel": "Hitrost predvajanja", "speedLabel": "Hitrost predvajanja",
@@ -257,6 +333,21 @@
} }
}, },
"metadata": { "metadata": {
"api": {
"text": "Metapodatkov API ni bilo mogoče naložiti, preverite internetno povezavo.",
"title": "Ni uspelo naložiti metapodatkov API"
},
"dmca": {
"badge": "Odstranjeno",
"text": "Ta medij ni več na voljo zaradi obvestila o odstranitvi ali zahtevka za avtorske pravice.",
"title": "Medij je bil odstranjen"
},
"extensionPermission": {
"badge": "Neveljavne pravice dostopa",
"button": "Uporabi razširitev",
"text": "Imate razširitev brskalnika, vendar potrebujemo vaše dovoljenje, da lahko začnemo uporabljati razširitev.",
"title": "Nastavi razširitev brskalnika"
},
"failed": { "failed": {
"badge": "Neuspešno", "badge": "Neuspešno",
"homeButton": "Domov", "homeButton": "Domov",
@@ -306,6 +397,12 @@
"remaining": "{{timeLeft}} do konca • Konča ob {{timeFinished, datetime}}", "remaining": "{{timeLeft}} do konca • Konča ob {{timeFinished, datetime}}",
"shortRegular": "{{timeWatched}}", "shortRegular": "{{timeWatched}}",
"shortRemaining": "-{{timeLeft}}" "shortRemaining": "-{{timeLeft}}"
},
"turnstile": {
"description": "Preverite, ali ste človek, tako da izpolnite Captcha na desni strani. S tem zagotavljamo varnost filmskega spleta!",
"error": "Ni uspelo preveriti vaše človeškosti. Poskusite znova.",
"title": "Preveriti moramo, ali ste človek.",
"verifyingHumanity": "Preverjanje vaše človeškosti..."
} }
}, },
"screens": { "screens": {
@@ -378,20 +475,50 @@
}, },
"connections": { "connections": {
"server": { "server": {
"description": "Če se želite povezati z zaledjem po meri za hrambo podatkov, to omogočite in navedite URL naslov.", "description": "Če se želite povezati z zaledjem po meri za hrambo podatkov, to omogočite in navedite URL naslov. <0>Navodila.</0>",
"label": "Strežnik po meri", "label": "Strežnik po meri",
"urlLabel": "Naslov strežnika po meri (URL)" "urlLabel": "Naslov strežnika po meri (URL)"
}, },
"setup": {
"doSetup": "Naredi nastavitev",
"errorStatus": {
"description": "Zdi se, da je treba posvetiti pozornost enemu ali več elementom v tej nastavitvi.",
"title": "Nekaj potrebuje vašo pozornost"
},
"itemError": "S to nastavitvijo je nekaj narobe. Ponovno opravite nastavitev in jo popravite.",
"items": {
"default": "Privzet način",
"extension": "Razširitev",
"proxy": "Proxy po meri"
},
"redoSetup": "Ponovnio izvedi nastavljanje",
"successStatus": {
"description": "Vse stvari so pripravljene, da lahko začnete gledati svoje najljubše medije.",
"title": "Vse je nastavljeno!"
},
"unsetStatus": {
"description": "Kliknite gumb na desni strani, da začnete postopek namestitve.",
"title": "Niste opravili nastavitev"
}
},
"title": "Povezave", "title": "Povezave",
"workers": { "workers": {
"addButton": "Dodaj novega \"workerja\"", "addButton": "Dodaj novega \"workerja\"",
"description": "Za delovanje aplikacije je ves promet usmerjen prek posredniških strežnikov. To nastavitev omogočite, če želite pripeljati lastne delavce.", "description": "Za delovanje aplikacije je ves promet usmerjen prek posredniških strežnikov. To nastavitev omogočite, če želite pripeljati lastne delavce. <0>Navodila</0>",
"emptyState": "Niste dodali \"workerja\", dodajte ga spodaj", "emptyState": "Niste dodali \"workerja\", dodajte ga spodaj",
"label": "Uporaba posrednikov po meri", "label": "Uporaba posrednikov po meri",
"urlLabel": "Naslov Workerja (URL)", "urlLabel": "Naslov Workerja (URL)",
"urlPlaceholder": "https://" "urlPlaceholder": "https://"
} }
}, },
"preferences": {
"language": "Jezik",
"languageDescription": "Jezik, ki se uporablja za celotno aplikacijo.",
"thumbnail": "Naloži predoglede",
"thumbnailDescription": "Videoposnetki večinoma nimajo predogledov. To nastavitev lahko omogočite in jih ustvarite sproti, vendar lahko zaradi tega videoposnetek postane počasnejši.",
"thumbnailLabel": "Naloži predoglede",
"title": "Nastavitve"
},
"reset": "Ponastavi", "reset": "Ponastavi",
"save": "Shrani", "save": "Shrani",
"sidebar": { "sidebar": {
@@ -409,6 +536,7 @@
} }
}, },
"subtitles": { "subtitles": {
"backgroundBlurLabel": "Zameglitev ozadja",
"backgroundLabel": "Motnost ozadja", "backgroundLabel": "Motnost ozadja",
"colorLabel": "Barva", "colorLabel": "Barva",
"previewQuote": "Ne smem se bati. Strah je ubijalec misli.", "previewQuote": "Ne smem se bati. Strah je ubijalec misli.",

View File

@@ -114,7 +114,10 @@
"failed": "Misslyckades med att hitta media, försök igen!", "failed": "Misslyckades med att hitta media, försök igen!",
"loading": "Laddar...", "loading": "Laddar...",
"noResults": "Vi kunde inte hitta någonting!", "noResults": "Vi kunde inte hitta någonting!",
"placeholder": "Vad vill du titta på?", "placeholder": {
"default": "Vad vill du titta på?",
"extra": []
},
"sectionTitle": "Sökresultat" "sectionTitle": "Sökresultat"
}, },
"titles": { "titles": {
@@ -123,15 +126,11 @@
}, },
"morning": { "morning": {
"default": "Vad vill du titta på den här morgonen?", "default": "Vad vill du titta på den här morgonen?",
"extra": [ "extra": ["Jag hör att Before Sunrise är bra"]
"Jag hör att Before Sunrise är bra"
]
}, },
"night": { "night": {
"default": "Vad vill du titta på ikväll?", "default": "Vad vill du titta på ikväll?",
"extra": [ "extra": ["Trött? Jag hör att The Exorcist är bra."]
"Trött? Jag hör att The Exorcist är bra."
]
} }
} }
}, },

View File

@@ -1,6 +1,19 @@
{ {
"about": { "about": {
"description": "movie-web என்பது இணையத்தில் ஸ்ட்ரீம்களைத் தேடும் ஒரு வலைப் பயன்பாடு ஆகும். உள்ளடக்கத்தை உட்கொள்வதற்கான ஒரு சிறிய அணுகுமுறையை குழு நோக்கமாகக் கொண்டுள்ளது.",
"faqTitle": "பொதுவான கேள்விகள்", "faqTitle": "பொதுவான கேள்விகள்",
"q1": {
"body": "மூவி வெப் எந்தவொரு பதிவுகளையும் வழங்குவதில்லை. நீங்கள் ஒரு காட்சியை தேர்ந்தெடுத்து காண விரும்பும் போது அதற்கான பதிவை இணையத்தில் உள்ள பல்வேறு தளங்களில் இருந்து தேடி உங்களுக்கு வழங்குகிறது. பதிவுகள் ஒரு போதும் மூவி வெப் மூலம் பதிவேற்றப்படுவது இல்லை. நீங்கள் காணும் அனைத்து காட்சிகளும் தேடு பொறி முறையின் சிறப்பம்சத்தின் மூலமாகவே உங்களுக்கு வழங்கப்படுகிறது.",
"title": "பதிவு எங்கிருந்து வருகிறது?"
},
"q2": {
"body": "ஒரு திரைப்படம் அல்லது தொடரை உங்களால் நேரடியாக கோர முடியாது. மூவி வெப் எந்த ஒரு பதிவுகளையும் நிர்வகிப்பதில்லை. அணைத்து பதிவுகளும் இணையத்தில் உள்ள தளங்களின் வழியாகவே உங்களுக்கு அளிக்கப்படுகின்றது.",
"title": "ஒரு தொடர் அல்லது திரைப்படத்தை நான் எங்கே கோருவது?"
},
"q3": {
"body": "எங்கள் தேடல் முடிவுகள் The Movie Database (TMDB) தரவு தளம் மூலம் வழங்கப்படுகிறது. தேடல் முடிவுகள் தேடலுக்கான பதிவுகளை கட்டாயம் உள்ளடக்கி இருக்கும் என்பதை உறுதியாக கூற இயலாது.",
"title": "தேடல் முடிவுகள் திரைப்படம் அல்லது தொடரை காட்டுகிறது. ஆனால், ஏன் என்னால் அதை பார்க்க முடியவில்லை?"
},
"title": "movie-web பற்றி" "title": "movie-web பற்றி"
}, },
"actions": { "actions": {
@@ -8,8 +21,8 @@
"copy": "நகல்" "copy": "நகல்"
}, },
"auth": { "auth": {
"createAccount": "கணக்கு இல்லையா? <0>கணக்கை உருவாக்குங்கள்</0>", "createAccount": "கணக்கு இல்லையா? <0>புதிய கணக்கை உருவாக்குங்கள்</0>",
"deviceNameLabel": "கருவியின் பெயர்", "deviceNameLabel": "சாதனத்தின் பெயர்",
"deviceNamePlaceholder": "எனது கைபேசி", "deviceNamePlaceholder": "எனது கைபேசி",
"generate": { "generate": {
"description": "தங்கள் கடவுச்சொற்றொடரே தங்களது பயனர் பெயர் மற்றும் கடவுச்சொல். கணக்கினுள் நுழைய அதனை பாதுகாப்பாக வைத்திருங்கள்", "description": "தங்கள் கடவுச்சொற்றொடரே தங்களது பயனர் பெயர் மற்றும் கடவுச்சொல். கணக்கினுள் நுழைய அதனை பாதுகாப்பாக வைத்திருங்கள்",
@@ -17,51 +30,507 @@
"passphraseFrameLabel": "கடவுச்சொற்றொடர்", "passphraseFrameLabel": "கடவுச்சொற்றொடர்",
"title": "உங்கள் கடவுச்சொற்றொடர்" "title": "உங்கள் கடவுச்சொற்றொடர்"
}, },
"hasAccount": "கணக்கு வைத்துள்ளீரா? <0>புகுபதிகை செய்க </0>", "hasAccount": "ஏற்கனவே ஒரு கணக்கு ள்ளா? <0>இங்கே உள்நுழைக. </0>",
"login": { "login": {
"description": "உங்கள் கணக்கினுள் புகுபதிய கடவுச்சொற்றொடரை உள்ளிடுக", "description": "உங்கள் கணக்கினுள் உள்நுழைய உங்கள் கடவுச்சொற்றொடரை உள்ளிடுக",
"deviceLengthError": "கருவியின் பெயரை உள்ளிடுக", "deviceLengthError": "சாதனத்தின் பெயரை உள்ளிடவும்",
"passphraseLabel": "12-சொல் கடவுச்சொற்றொடர்", "passphraseLabel": "12-சொல் கடவுச்சொற்றொடர்",
"passphrasePlaceholder": "கடவுச்சொற்றொடர்", "passphrasePlaceholder": "கடவுச்சொற்றொடர்",
"submit": "புகுபதிகை", "submit": "புகுபதிகை",
"title": "உங்கள் கணக்கினுள் புகுபதிய", "title": "உங்கள் கணக்கில் உள்நுழைக",
"validationError": "தவறான அல்லது முழுமையற்ற கடவுச்சொற்றொடர்" "validationError": "தவறான அல்லது முழுமையற்ற கடவுச்சொற்றொடர்"
}, },
"register": { "register": {
"information": { "information": {
"icon": "பயனர் குறிப்படம்", "color1": "Profile நிறம் ஒன்று",
"color2": "Profile நிறம் இரண்டு",
"header": "உங்கள் சாதனத்தின் பெயரை உள்ளிட்டு அதற்கான வண்ணம் மற்றும் ஐகானையும் தேர்ந்தெடுக்கவும்",
"icon": "பயனர் icon",
"next": "அடுத்து", "next": "அடுத்து",
"title": "கணக்கு விவரம்" "title": "கணக்கு விவரம்"
} }
}, },
"trust": { "trust": {
"no": "பின்செல்" "failed": {
"text": "நீங்கள் அதை சரியாக உள்ளமைத்தீர்களா?",
"title": "சர்வரை தொடர்பு கொள்ள முடியவில்லை"
},
"host": "நீங்கள் <0>{{hostname}}</0> உடன் இணைக்கப்படுகிறீர்கள். கணக்கை உருவாக்கும் முன் சரியான தகவல் தானா எனபதை உறுதி செய்து கொள்ளவும்",
"no": "பின்செல்",
"title": "நீங்கள் இந்த சர்வரை நம்புகிறீர்களா?",
"yes": "நான் இந்த சர்வரை நம்புகிறேன்"
}, },
"verify": { "verify": {
"description": "உங்கள் கடவுச்சொற்றொடரைச் சேமித்துள்ளீர்கள் என்பதை உறுதிப்படுத்த உங்கள் கடவுச்சொற்றொடர்களை மீண்டும் இங்கே உள்ளீடு செய்து உங்கள் கணக்கை உருவாக்கிக் கொள்ளுங்கள்",
"invalidData": "தரவு செல்லுபடியாகாது",
"noMatch": "கடவுச்சொற்றொடர்கள் பொருந்தவில்லை",
"passphraseLabel": "உங்களின் 12-சொல் கடவுச்சொற்றொடர்",
"recaptchaFailed": "ReCaptcha சரிபார்ப்பு தோல்வியடைந்தது",
"register": "கணக்கை உருவாக்கு", "register": "கணக்கை உருவாக்கு",
"title": "கடவுச்சொற்றொடரை உறுதி செய்க" "title": "கடவுச்சொற்றொடரை உறுதி செய்க"
} }
}, },
"errors": {
"badge": "சரியாக வேலை செய்யவில்லை",
"details": "பிழை விவரங்கள்",
"reloadPage": "இணையப் பக்கத்தை Reload செய்யவும்",
"showError": "பிழை விவரங்களைக் காட்டு",
"title": "நங்கள் ஒரு பிழையை எதிர் கொண்டு இருக்கிறோம்!"
},
"footer": {
"legal": {
"disclaimer": "பொறுப்பு துறப்பு",
"disclaimerText": "மூவி-வெப் எந்த பதிவுகளையும் தன்னகம் வைத்திருக்கவில்லை, இது மூன்றாம் தரப்பு பதிவுகளை வழங்குபவர்களை இணைக்கிறது. சட்ட சிக்கல்கள் ஏதேனும் இருப்பின் அது பதிவுகளை வழங்குபவரையே சாறும். வீடியோ வழங்குநர்கள் காண்பிக்கும் எந்த மீடியா பதிவுகளுக்கும் மூவி வெப் பொறுப்பாகாது."
},
"links": {
"discord": "Discord",
"dmca": "DMCA",
"github": "GitHub"
},
"tagline": "இந்த ஓப்பன் சோர்ஸ் (Open Source) இணைய தளம் மூலம் உங்களுக்குப் பிடித்த தொடர்களையும் திரைப்படங்களையும் பார்க்கலாம்."
},
"global": { "global": {
"name": "மூவி-வெப்",
"pages": { "pages": {
"about": "தெரிந்து கொள்ள",
"dmca": "DMCA",
"login": "புகுபதிகை", "login": "புகுபதிகை",
"onboarding": "அமை",
"pagetitle": "{{title}} - மூவி- வெப்",
"register": "பதிவு",
"settings": "அமைப்புகள்" "settings": "அமைப்புகள்"
} }
}, },
"home": { "home": {
"bookmarks": {
"sectionTitle": "குறிப்புகள்"
},
"continueWatching": {
"sectionTitle": "தொடர்ந்து பார்க்க"
},
"mediaList": {
"stopEditing": "திருத்துவதை நிறுத்து"
},
"search": { "search": {
"allResults": "மேலும் எங்களிடம் இல்லை!",
"failed": "மீடியாவைக் கண்டறிய முடியவில்லை, மீண்டும் முயலவும்!",
"loading": "Loading...",
"noResults": "எங்களால் எதையும் கண்டுபிடிக்க இயலவில்லை!",
"sectionTitle": "தேடல் முடிவுகள்" "sectionTitle": "தேடல் முடிவுகள்"
},
"titles": {
"day": {
"default": "மதிய வணக்கம். தற்போது என்ன காண விரும்புகிறீர்கள்?",
"extra": [
"சாகசமாக உணர்கிறீர்களா? ஜுராசிக் பார்க் (Jurassic Park) சரியான தேர்வாக இருக்கலாம்."
]
},
"morning": {
"default": "காலை வணக்கம். தற்போது என்ன காண விரும்புகிறீர்கள்?",
"extra": [
"சூரிய உதயத்திற்கு முன் நல்லது என்று கேள்விப்படுகிறேன்"
]
},
"night": {
"default": "இன்றைய இரவு என்ன காண விரும்புகிறீர்கள்?",
"extra": [
"சோர்வாக உள்ளீர்களா? Exorcist திரைப்படம் காணுங்கள்."
]
}
} }
}, },
"media": { "media": {
"episodeDisplay": "S{{season}} E{{episode}}",
"types": { "types": {
"movie": "திரைப்படம்", "movie": "திரைப்படம்",
"show": "காட்சி" "show": "காட்சி"
} }
}, },
"navigation": { "navigation": {
"banner": {
"offline": "உங்கள் இணைய இணைப்பைச் சரிபார்க்கவும்"
},
"menu": { "menu": {
"settings": "அமைப்புகள்" "about": "எங்களை பற்றி",
"donation": "நன்கொடை",
"logout": "வெளியேறு",
"register": "இணைய கணக்கோடு ஒத்திசை (Sync to cloud)",
"settings": "அமைப்புகள்",
"support": "உதவி மையம்"
} }
},
"notFound": {
"badge": "கிடைக்கவில்லை",
"goHome": "முகப்பிற்குச் செல்",
"message": "எங்களால் இயன்றவரை தேடினோம். ஆனால் உங்களுக்கான பக்கத்தை கண்டுபிடிக்க இயலவில்லை.",
"title": "உங்களுக்கான பக்கத்தை கண்டுபிடிக்க இயலவில்லை"
},
"onboarding": {
"defaultConfirm": {
"cancel": "ரத்து செய்",
"confirm": "இயல்புநிலை அமைப்பைப் பயன்படுத்தவும்",
"description": "இயல்புநிலை அமைப்பில் உள்ள தரவுகள் சிறந்த அனுபவத்தை வழங்க உகந்தவை அல்ல. மேலும், இவை மிகவும் மெதுவான இணைய வேகத்தையே கொண்டிருக்கும்.",
"title": "உறுதி செய்யலாமா?"
},
"extension": {
"back": "பின் செல்",
"explainer": "உலாவி நீட்டிப்பு (Browser Extension) வழி உங்களுக்கு சிறந்த காட்சிகளை எங்களால் வழங்க இயலும். மேலும், இந்த நிறுவல் மிகவும் எளிதான வழி முறையாகும்.",
"explainerIos": "துரதிர்ஷ்டவசமாக, உலாவி நீட்டிப்பு iOS இல் ஆதரிக்கப்படவில்லை, மற்றொரு விருப்பத்தைத் தேர்வுசெய்ய <bold>பின் செல்</bold> என்பதை அழுத்தவும்.",
"extensionHelp": "நீங்கள் நீட்டிப்பை நிறுவியிருந்தும் அது கண்டறியப்படவில்லை எனில், <bold>உங்கள் உலாவி நீட்டிப்பு மெனு மூலம் நீட்டிப்பைத் திறந்து</bold> திரையில் உள்ள படிகளைப் பின்பற்றவும்.",
"linkChrome": "Chrome நீட்டிப்பை நிறுவவும்",
"linkFirefox": "Firefox நீட்டிப்பை நிறுவவும்",
"notDetecting": "Chrome இல் நிறுவப்பட்டு விட்டது, ஆனால் தளம் அதைக் கண்டறியவில்லையா? பக்கத்தை மீண்டும் இயக்கி (Reload) முயற்சிக்கவும்!",
"notDetectingAction": "பக்கத்தை மீண்டும் இயக்கு",
"status": {
"disallowed": "இந்தப் பக்கத்திற்கு நீட்டிப்பு இயக்கப்படவில்லை",
"disallowedAction": "நீட்டிப்பை இயக்கு",
"failed": "தற்போதைய நிலையை அறிய இயலவில்லை",
"loading": "நீங்கள் நீட்டிப்பை நிறுவும் வரை காத்திருக்கிறது",
"outdated": "நீட்டிப்பு பதிப்பு மிகவும் பழையது",
"success": "நீட்டிப்பு எதிர்பார்த்தபடி செயல்படுகிறது!"
},
"submit": "தொடரவும்",
"title": "நீட்டிப்பு நிறுவலுடன் ஆரம்பிக்கலாம்"
},
"proxy": {
"back": "பின் செல்",
"explainer": "Proxy முறையில், உங்களின் தனிப்பட்ட Proxy யை உருவாக்குவதன் மூலம் சிறந்த தரமான ஸ்ட்ரீம்களைப் பெறலாம்.",
"input": {
"errorConnection": "Proxy உடன் இணைக்க முடியவில்லை",
"errorInvalidUrl": "சரியான URL அல்ல",
"errorNotProxy": "Proxy க்கு பதில் இணைய தளம் கிடைத்துள்ளது",
"label": "Proxy URL",
"placeholder": "https://"
},
"link": "Proxy எவ்வாறு உருவாக்குவது என்பதை அறிக",
"submit": "Proxy யை சமர்ப்பிக்க",
"title": "ஒரு புதிய Proxy யை உருவாக்குவோம்"
},
"start": {
"explainer": "சிறந்த ஸ்ட்ரீம்களைப் பெற, நீங்கள் எந்த ஸ்ட்ரீமிங் முறையைப் பயன்படுத்த விரும்புகிறீர்கள் என்பதை நீங்கள் தேர்வு செய்ய வேண்டும்.",
"options": {
"default": {
"text": "எனக்கு நல்ல தரமான ஸ்ட்ரீம்கள் வேண்டாம்,<0 /> <1>இயல்புநிலை அமைப்பைப் பயன்படுத்தவும்</1>"
},
"extension": {
"action": "நீட்டிப்பை நிறுவவும்",
"description": "உலாவி நீட்டிப்பை நிறுவி, சிறந்த தரவுகளை பெறுங்கள்.",
"quality": "சிறந்த தரம்",
"title": "உலாவி நீட்டிப்பு"
},
"proxy": {
"action": "Proxy யை அமைக்கவும்",
"description": "வெறும் 5 நிமிடங்களில் Proxy யை அமைத்து, சிறந்த தரவுகளை பெறுங்கள்.",
"quality": "நல்ல தரம்",
"title": "தனிப்பட்ட Proxy"
}
},
"title": "உங்களின் மூவி-வெப் அமைப்பை நிறுவலாம் வாருங்கள்"
}
},
"overlays": {
"close": "மூடு"
},
"player": {
"back": {
"default": "முகப்பிற்கு செல்",
"short": "பின் செல்"
},
"casting": {
"enabled": "சாதனத்தின் வழி திரையிடப்படுகிறது..."
},
"menus": {
"downloads": {
"copyHlsPlaylist": "HLS Playlist link யை காப்பி செய்யவும்",
"disclaimer": "பதிவிறக்கங்கள் வழங்குநரிடமிருந்து நேரடியாக வழங்கப்படுகின்றன. பதிவிறக்கங்கள் எவ்வாறு வழங்கப்படுகின்றன என்பதை மூவி-வெப் கட்டுப்படுத்தாது.",
"downloadSubtitle": "தற்போதைய வசனத்தைப் பதிவிறக்கவும்",
"downloadVideo": "Video வை பதிவிறக்கம் செய்",
"hlsDisclaimer": "பதிவிறக்கங்கள் வழங்குநரிடமிருந்து நேரடியாக எடுக்கப்படுகின்றன. பதிவிறக்கங்கள் எவ்வாறு வழங்கப்படுகின்றன என்பதை மூவி-வெப் கட்டுப்படுத்தவில்லை.<br /><br />நீங்கள் HLS Playlist யை பதிவிறக்குகிறீர்கள் என்பதை நினைவில் கொள்ளவும், <bold> Advanced streaming formats பற்றி நீங்கள் அறிந்து இருக்கவில்லையென்றால் பதிவிறக்கம் செய்ய உங்களை நாங்கள் பரிந்துரைக்கவில்லை. </bold>. வெவ்வேறு வடிவங்கள் அல்லது வெவ்வேறு ஆதாரங்களை முயற்சிக்கவும்.",
"onAndroid": {
"1": "Android இல் பதிவிறக்க, பதிவிறக்க பொத்தானைக் கிளிக் செய்து, புதிய பக்கத்தில், வீடியோ வின் மீது <bold>tap and hold</bold>கிளிக் செய்யவும், பின்னர் <bold>save</bold> என்பதைத் தேர்ந்தெடுக்கவும்.",
"shortTitle": "பதிவிறக்கம் / ஆண்ட்ராய்டு",
"title": "ஆண்ட்ராய்டு இல் பதிவிறங்குகிறது"
},
"onIos": {
"1": "iOS இல் பதிவிறக்க, பதிவிறக்க பொத்தானைக் கிளிக் செய்து, புதிய பக்கத்தில், <bold><ios_share /></bold> என்பதைக் கிளிக் செய்து பிறகு, <bold>Save to Files <ios_files /></bold> கிளிக் செய்யவும்.",
"shortTitle": "பதிவிறக்கு /iOS",
"title": "iOS இல் பதிவிறங்குகிறது"
},
"onPc": {
"1": "கணினியில், Download பொத்தானைக் கிளிக் செய்து வரும் புதிய பக்கத்தில், வீடியோவின் மீது சுட்டியின் வலது பக்கம் கிளிக் (Right Click) செய்து <bold>Save video as</bold> என்பதைத் தேர்ந்தெடுக்கவும்",
"shortTitle": "பதிவிறக்கு / PC",
"title": "கணினியில் பதிவிறங்குகிறது"
},
"title": "பதிவிறக்கு"
},
"episodes": {
"button": "அத்தியாயங்கள்",
"emptyState": "இந்த தொடரில் எந்தவொரு அத்தியாயங்களும் இல்லை. பிறகு பார்க்கவும்!",
"episodeBadge": "E{{episode}}",
"loadingError": "தொடரை திரையிட முடியவில்லை",
"loadingList": "திரையிடப்படுகிறது...",
"loadingTitle": "திரையிடப்படுகிறது...",
"unairedEpisodes": "இந்த Season இல் ஒன்று அல்லது அதற்கு மேற்பட்ட Episode கள் இன்னும் ஒளிபரப்பப்படாததால் அவை முடக்கப்பட்டுள்ளன."
},
"playback": {
"speedLabel": "காட்சி பின்னணி வேகம்",
"title": "காட்சி பின்னணி அமைப்புகள்"
},
"quality": {
"automaticLabel": "இயல்பான தரம்",
"hint": "வெவ்வேறு தர விருப்பங்களைப் பெற <0>switching source</0> தெரிவு செய்யலாம்.",
"iosNoQuality": "Apple நிறுவனத்தின் வரையறுக்கப்பட்ட வரம்புகள் காரணமாக, இந்த Source இன் தரமான தேர்வு iOS இயங்கு தளத்தில் வழங்க இயலவில்லை. வெவ்வேறு தர விருப்பங்களைப் பெற, <0>switching to another source</0> தெரிவு செய்யவும்.",
"title": "தரம்"
},
"settings": {
"downloadItem": "பதிவிறக்கம்",
"enableSubtitles": "வசனங்களை இயக்கு",
"experienceSection": "பார்க்கும் அனுபவம்",
"playbackItem": "காட்சி பின்னணி அமைப்புகள்",
"qualityItem": "தரம்",
"sourceItem": "வீடியோ ஆதாரங்கள்",
"subtitleItem": "வசன அமைப்புகள்",
"videoSection": "வீடியோ அமைப்புகள்"
},
"sources": {
"failed": {
"text": "வீடியோக்களை கண்டுபிடிக்க முயற்சிக்கும் போது பிழை ஏற்பட்டுள்ளது , வேறு ஆதாரங்களை முயற்சிக்கவும்.",
"title": "Scrape செய்ய முடியவில்லை"
},
"noEmbeds": {
"text": "எந்த உட்பொதிவுகளையும் (Embeds) எங்களால் கண்டுபிடிக்க இயலவில்லை, வேறு ஆதாரங்களை முயற்சிக்கவும்.",
"title": "உட்பொதிப்புகள் (Embeds) எதுவும் இல்லை"
},
"noStream": {
"text": "இந்த Source இல் திரைப்படம் அல்லது தொடருக்கான தரவுகள் இல்லை.",
"title": "தரவு கிடைக்கப்பெறவில்லை"
},
"title": "தரவு தளங்கள்",
"unknownOption": "தெரியவில்லை"
},
"subtitles": {
"customChoice": "File இல் இருந்து வசனத்தைத் தேர்ந்தெடுக்கவும்",
"customizeLabel": "தனிப்பயன்முறை",
"offChoice": "அனை",
"settings": {
"backlink": "தனிப்பட்ட வசன வரிகள்",
"delay": "வசன வரி தாமதம்",
"fixCapitals": "Capitals யை சரி செய்யவும்"
},
"title": "வசன வரிகள்",
"unknownLanguage": "தெரியவில்லை"
}
},
"metadata": {
"api": {
"text": "API தரவுகளை காண்பிக்க இயலவில்லை, உங்கள் இணைய இணைப்பைச் சரிபார்க்கவும்.",
"title": "API தரவுகளை காண்பிக்க இயலவில்லை"
},
"dmca": {
"badge": "அகற்றப்பட்டது",
"text": "Copyright Claim காரணமாக இந்த தரவு நீக்கப்பட்டது அல்லது காணக் கிடைக்காது.",
"title": "மீடியா நீக்கப்பட்டது"
},
"extensionPermission": {
"badge": "அனுமதி இல்லை",
"button": "நீட்டிப்பைப் பயன்படுத்தவும்",
"text": "உங்களிடம் உலாவி நீட்டிப்பு உள்ளது, ஆனால் நீட்டிப்பைப் பயன்படுத்தத் தொடங்க உங்கள் அனுமதி தேவை.",
"title": "நீட்டிப்பை உள்ளமைக்கவும்"
},
"failed": {
"badge": "தோல்வியடைந்தது",
"homeButton": "முகப்பிற்கு செல்",
"text": "TMDB இலிருந்து மீடியாவின் தரவுகளை ஏற்ற முடியவில்லை. உங்கள் இணைய இணைப்பில் TMDB செயலிழந்துள்ளதா அல்லது தடுக்கப்பட்டுள்ளதா என்பதைச் சரிபார்க்கவும்.",
"title": "மெட்டா டேட்டாவை ஏற்றுவதில் தோல்வி"
},
"notFound": {
"badge": "கிடைக்கவில்லை",
"homeButton": "முகப்பிற்கு செல்",
"text": "நீங்கள் கோரிய மீடியாவை எங்களால் கண்டுபிடிக்க இயலவில்லை. அது அகற்றப்பட்டிருக்கலாம் அல்லது நீங்கள் URL ஐ சேதப்படுத்தியிருக்கலாம்.",
"title": "மீடியாவைக் கண்டுபிடிக்க முடியவில்லை."
}
},
"nextEpisode": {
"cancel": "ரத்து செய்",
"next": "அடுத்த Episode"
},
"playbackError": {
"badge": "பின்னணி பிழை",
"errors": {
"errorAborted": "பயனரின் கோரிக்கையால் மீடியாவைப் பெறுவது நிறுத்தப்பட்டது.",
"errorDecode": "பயன்படுத்தக்கூடியது என்று முன்பே தீர்மானிக்கப்பட்டிருந்தாலும், மீடியாவை டிகோட் செய்ய முயற்சிக்கும்போது ஏற்பட்ட பிழை காரணமாக இந்த பிழை ஏற்பட்டுள்ளது.",
"errorGenericMedia": "அறியப்படாத மீடியா பிழை ஏற்பட்டுள்ளது.",
"errorNetwork": "மீடியா கிடைத்த போதிலும், சில நெட்ஒர்க் பிழையின் காரணமாக இந்த மீடியா இப்போது கிடைக்கப்பெறவில்லை.",
"errorNotSupported": "மீடியா அல்லது மீடியா வழங்குநர் பொருள் ஆதரிக்கப்படவில்லை."
},
"homeButton": "முகப்பிற்கு செல்",
"text": "மீடியாவை இயக்கும் முயற்சியில் பிழை ஏற்பட்டுள்ளது. தயவு செய்து மீண்டும் முயற்சிக்கவும்.",
"title": "வீடியோவை இயக்க முடியவில்லை!"
},
"scraping": {
"items": {
"failure": "பிழை ஏற்பட்டுள்ளது",
"notFound": "வீடியோ இல்லை",
"pending": "வீடியோக்களை எடுத்து கொண்டிருக்கிறது..."
},
"notFound": {
"badge": "கிடைக்கவில்லை",
"detailsButton": "விவரங்களை காட்டு",
"homeButton": "முகப்பிற்கு செல்",
"text": "எங்கள் வழங்குநர்கள் மூலம் நாங்கள் தேடினோம், நீங்கள் தேடும் மீடியாவைக் கண்டுபிடிக்க இயலவில்லை! நாங்கள் மீடியாவை host செய்யவில்லை, என்ன கிடைக்கும் என்பதில் எங்களுக்கு எந்த கட்டுப்பாடும் இல்லை. மேலும் விவரங்களுக்கு கீழே உள்ள 'விவரங்களைக் காட்டு' என்பதைக் கிளிக் செய்யவும்.",
"title": "அதை எங்களால் கண்டுபிடிக்க முடியவில்லை"
}
},
"time": {
"regular": "{{timeWatched}} / {{duration}}",
"remaining": "{{timeLeft}} மீதமுள்ள நேரம்• {{timeFinished, datetime}} அன்று முடிந்தது",
"shortRegular": "{{timeWatched}}",
"shortRemaining": "-{{timeLeft}}"
},
"turnstile": {
"description": "நீங்கள் மனிதர் தானா என்பதை உறுதி செய்யவும். இது எங்களை பாதுகாப்பாக வைத்திருக்க உதவும்!",
"error": "நீங்கள் மனிதர் என்பதை தான் சரிபார்க்க முடியவில்லை. தயவு செய்து மீண்டும் முயற்சிக்கவும்.",
"title": "நீங்கள் ஒரு மனிதர் என்பதை நாங்கள் உறுதி செய்ய வேண்டும்.",
"verifyingHumanity": "நீங்கள் மனிதர் தானா என்பதை உறுதி செய்கிறோம். காத்திருக்கவும்..."
}
},
"screens": {
"dmca": {
"text": "மூவி-வெப் DMCA தொடர்பு பக்கத்திற்கு வரவேற்கிறோம்! அறிவுசார் சொத்துரிமைகளை நாங்கள் மதிக்கிறோம் மற்றும் எந்தவொரு பதிப்புரிமைக் கவலைகளையும் விரைவாகத் தீர்க்க விரும்புகிறோம். உங்கள் பதிப்புரிமை பெற்ற வேலை எங்கள் தளத்தில் தவறாகப் பயன்படுத்தப்பட்டதாக நீங்கள் நம்பினால், கீழே உள்ள மின்னஞ்சலுக்கு விரிவான DMCA அறிவிப்பை அனுப்பவும். பதிப்புரிமை பெற்ற உள்ளடக்கத்தின் விளக்கம், உங்கள் தொடர்பு விவரங்கள் மற்றும் நல்ல நம்பிக்கையின் அறிக்கையைச் சேர்க்கவும். இந்த விஷயங்களை உடனடியாகத் தீர்ப்பதற்கு நாங்கள் கடமைப்பட்டுள்ளோம், மேலும் மூவி-வெப் படைப்பாற்றல் மற்றும் பதிப்புரிமைகளை மதிக்கும் இடமாக வைத்திருப்பதில் உங்கள் ஒத்துழைப்பைப் பாராட்டுகிறோம்.",
"title": "DMCA"
},
"loadingApp": "பயன்பாட்டை நிறுவுகிறது",
"loadingUser": "Profile லோட் செய்யப்படுகிறது",
"loadingUserError": {
"logout": "வெளியேறு",
"reset": "Custom server யை மீட்டமைக்கவும்",
"text": "Profile லோட் செய்ய முடியவில்லை",
"textWithReset": "உங்கள் Profile Custom Server இல் இருந்து லோட் செய்ய இயலவில்லை. மீண்டும் Default Server க்கு மாற்றியமைக்க விரும்புகிறீர்களா?"
},
"migration": {
"failed": "உங்கள் தரவுகளை புதுப்பிக்க இயலவில்லை.",
"inProgress": "தயவு செய்து காத்திருக்கவும், நாங்கள் உங்கள் தரவுகளை சேமித்து கொண்டு இருக்கிறோம். இதற்கு அதிக நேரம் தேவைப்படாது."
}
},
"settings": {
"account": {
"accountDetails": {
"deviceNameLabel": "சாதனத்தின் பெயர்",
"deviceNamePlaceholder": "தனிப்பட்ட போன்",
"editProfile": "எடிட்",
"logoutButton": "வெளியேறு"
},
"actions": {
"delete": {
"button": "கணக்கை நீக்குக",
"confirmButton": "கணக்கை நீக்குக",
"confirmDescription": "உங்கள் கணக்கை நிச்சயமாக நீக்க விரும்புகிறீர்களா? உங்கள் தரவுகள் அனைத்தும் நீக்கப்படும்!",
"confirmTitle": "நீங்கள் உறுதியா?",
"text": "இந்த நடவடிக்கை திரும்ப பெற முடியாதது. அனைத்து தரவுகளும் நீக்கப்படும் மற்றும் எதையும் மீட்டெடுக்க இயலாது.",
"title": "கணக்கை நீக்குக"
},
"title": "செயல்கள்"
},
"devices": {
"deviceNameLabel": "சாதனத்தின் பெயர்",
"failed": "சாதனத்தின் தரவுகளை காண்பிக்க இயலவில்லை",
"removeDevice": "நீக்கு",
"title": "சாதனங்கள்"
},
"profile": {
"finish": "எடிட்டிங் முடிக்கவும்",
"firstColor": "Profile நிறம் ஒன்று",
"secondColor": "Profile நிறம் இரண்டு",
"title": "Profile படத்தைத் திருத்தவும்",
"userIcon": "பயனர் icon"
},
"register": {
"cta": "தொடங்குங்கள்",
"text": "ஒன்றுக்கு மேற்பட்ட சாதனங்களுக்கு இடையே உங்களின் காட்சி நேரத்தை பகிர்ந்து அவற்றை ஒத்திசைத்து வைக்கவும்.",
"title": "Cloud இல் Sync செய்யவும்"
},
"title": "கணக்கு"
},
"appearance": {
"activeTheme": "செயலில் உள்ளது",
"themes": {
"blue": "நீலம்",
"default": "இயல்பு அமைப்பு",
"gray": "சாம்பல்",
"red": "சிவப்பு",
"teal": "அடர் பச்சை"
},
"title": "தோற்றம்"
},
"connections": {
"server": {
"description": "நீங்கள் உங்கள் தரவுகளை உங்களின் தனிப்பட்ட பின்தளம் (Backend) இல் சேமிக்க விரும்பினால் இதை தெரிவு செய்து URL யை உள்ளீடு செய்யவும்",
"label": "தனிப்பயன் சர்வர்",
"urlLabel": "தனிப்பட்ட சர்வரின் URL"
},
"setup": {
"doSetup": "அமைக்கவும்",
"errorStatus": {
"description": "இந்த அமைப்பில் உள்ள ஒன்று அல்லது அதற்கு மேற்பட்ட உருப்படிகளுக்கு உங்கள் கவனம் தேவை என்று தெரிகிறது.",
"title": "உங்களின் கவனம் இங்கு தேவைப்படுகிறது"
},
"itemError": "இந்த அமைப்பில் ஏதோ தவறு உள்ளது. அதை சரிசெய்ய மீண்டும் அமைவு வழியாக செல்லவும்.",
"items": {
"default": "இயல்புநிலை அமைப்பு",
"extension": "நீட்டிப்பு",
"proxy": "தனிப்பயன் Proxy"
},
"redoSetup": "அமைப்பை மீண்டும் செய்",
"successStatus": {
"description": "உங்களுக்குப் பிடித்த மீடியாவைப் பார்க்கத் தொடங்குவதற்கு அனைத்து விஷயங்களும் இங்கு உள்ளன.",
"title": "அணைத்தும் சரியாக உள்ளது!"
},
"unsetStatus": {
"description": "Setup Process யை தொடங்க வலதுபுறத்தில் உள்ள பொத்தானைக் கிளிக் செய்யவும்.",
"title": "நீங்கள் அமைப்பிற்குச் செல்லவில்லை"
}
},
"title": "இணைப்புகள்",
"workers": {
"addButton": "புதிய worker யை சேர்க்கவும்",
"description": "இந்த பயன்பாடு செயல்பட அனைத்து இணைய போக்குவரத்தும் இந்த proxies வழியாக அனுப்பப்பட உள்ளது. உங்களின் தனிப்பட்ட workers யை உபயோகிக்க இதை தெரிவு செய்யவும். <0>வழிமுறைகள்.</0>",
"emptyState": "எந்தவொரு workers ம் இல்லை. கீழே புதியதாக சேர்க்கவும்",
"label": "Custom proxy workers யை உபயோகப்படுத்த",
"urlLabel": "Worker URL கள்",
"urlPlaceholder": "https://"
}
},
"preferences": {
"language": "பயன்பாட்டின் மொழி",
"languageDescription": "பயன்பாடு முழுமைக்கும் மொழி பயன்படுத்தப்பட்டது.",
"thumbnail": "சிறுபடங்களை உருவாக்கவும்",
"thumbnailDescription": "பெரும்பாலான நேரங்களில், வீடியோக்களில் சிறுபடங்கள் இருக்காது. அவற்றை உருவாக்க இந்த அமைப்பை நீங்கள் இயக்கலாம். ஆனால் அவை உங்கள் வீடியோவை மெதுவாக்கும்.",
"thumbnailLabel": "சிறுபடங்களை உருவாக்கவும்",
"title": "விருப்பங்கள்"
},
"reset": "மீட்டமை",
"save": "சேமி",
"sidebar": {
"info": {
"appVersion": "பயன்பாட்டின் பதிப்பு",
"backendUrl": "பின்தள URL",
"backendVersion": "பின்தள பதிப்பு",
"hostname": "Hostname",
"insecure": "பாதுகாப்பற்றது",
"notLoggedIn": "நீங்கள் உள்நுழையவில்லை",
"secure": "பாதுகாப்பானது",
"title": "பயன்பாட்டின் தகவல்கள்",
"unknownVersion": "தெரியவில்லை",
"userId": "பயனர் ID"
}
},
"subtitles": {
"backgroundLabel": "பின்னணி ஒளிபுகாநிலை (Background opacity)",
"colorLabel": "நிறம்",
"previewQuote": "நான் பயப்படக்கூடாது. பயம் மனதைக் கொல்லும்.",
"textSizeLabel": "எழுத்து அளவு",
"title": "வசன வரிகள்"
},
"unsaved": "உங்களிடம் சேமிக்கப்படாத மாற்றங்கள் உள்ளன"
} }
} }

View File

@@ -115,7 +115,10 @@
"failed": "ไม่พบสื่อนี้ ลองอีกครั้ง!", "failed": "ไม่พบสื่อนี้ ลองอีกครั้ง!",
"loading": "กำลังโหลด..", "loading": "กำลังโหลด..",
"noResults": "เราไม่พบอะไรเลย!", "noResults": "เราไม่พบอะไรเลย!",
"placeholder": "คุณอยากดูอะไรคะ?", "placeholder": {
"default": "คุณอยากดูอะไรคะ?",
"extra": []
},
"sectionTitle": "ผลการค้นหา" "sectionTitle": "ผลการค้นหา"
}, },
"titles": { "titles": {
@@ -124,15 +127,11 @@
}, },
"morning": { "morning": {
"default": "คุณอยากดูอะไรเช้านี้?", "default": "คุณอยากดูอะไรเช้านี้?",
"extra": [ "extra": ["ฉันได้ยินมาว่าเรื่อง Before Sunrise สนุก"]
"ฉันได้ยินมาว่าเรื่อง Before Sunrise สนุก"
]
}, },
"night": { "night": {
"default": "คุณอยากดูเรื่องอะไรในช่วงค่ำ?", "default": "คุณอยากดูเรื่องอะไรในช่วงค่ำ?",
"extra": [ "extra": ["เหนื่อยมั้ย? ฉันได้ยินมาว่า The Exorcist นั้นดี"]
"เหนื่อยมั้ย? ฉันได้ยินมาว่า The Exorcist นั้นดี"
]
} }
} }
}, },

View File

@@ -57,6 +57,8 @@
}, },
"host": "lawa ilo sina li <0>{{hostname}}</0> - ona li pona tawa sina la sina ken pali e lipu open", "host": "lawa ilo sina li <0>{{hostname}}</0> - ona li pona tawa sina la sina ken pali e lipu open",
"no": "o weka", "no": "o weka",
"noHost": "lawa ilo ni li open ala li nasin ala la, sina ken ala pali e lipu open",
"noHostTitle": "lawa ilo li open ala a!",
"title": "lawa ilo ni li pona tawa sina anu seme?", "title": "lawa ilo ni li pona tawa sina anu seme?",
"yes": "lawa ilo ni li pona" "yes": "lawa ilo ni li pona"
}, },
@@ -79,7 +81,8 @@
}, },
"footer": { "footer": {
"legal": { "legal": {
"disclaimer": "o sona e ni:" "disclaimer": "o sona e ni:",
"disclaimerText": "ilo Muwi-We li mama ala e ijo sitelen. ona li toki taso tawa ilo ante. utala nasin li lon la o toki tawa ona pi ilo ante. sitelen ale li tan ala ilo Muwi-We"
}, },
"links": { "links": {
"discord": "kulupu Siko", "discord": "kulupu Siko",
@@ -115,7 +118,15 @@
"failed": "lukin li pakala a! o alasa sin", "failed": "lukin li pakala a! o alasa sin",
"loading": "alasa...", "loading": "alasa...",
"noResults": "ijo li lon ala a!", "noResults": "ijo li lon ala a!",
"placeholder": "sina wile lukin e seme?", "placeholder": {
"default": "sina wile lukin e seme?",
"extra": [
"sina wile alasa e seme?",
"sina wile lukin e seme?",
"sitelen nanpa wan sina li seme?",
"sitelen nanpa wan sina li seme?"
]
},
"sectionTitle": "mi lukin e ni:" "sectionTitle": "mi lukin e ni:"
}, },
"titles": { "titles": {
@@ -166,6 +177,9 @@
"title": "mi ken ala lukin e lipu ona" "title": "mi ken ala lukin e lipu ona"
}, },
"onboarding": { "onboarding": {
"defaultConfirm": {
"cancel": "ala"
},
"start": { "start": {
"title": "o open e ilo Muwi-We" "title": "o open e ilo Muwi-We"
} }

View File

@@ -116,7 +116,10 @@
"failed": "Medya bulunamadı, tekrar deneyin!", "failed": "Medya bulunamadı, tekrar deneyin!",
"loading": "Yükleniyor...", "loading": "Yükleniyor...",
"noResults": "Hiçbir şey bulamadık!", "noResults": "Hiçbir şey bulamadık!",
"placeholder": "Ne izlemek istersiniz?", "placeholder": {
"default": "Ne izlemek istersiniz?",
"extra": []
},
"sectionTitle": "Arama sonuçları" "sectionTitle": "Arama sonuçları"
}, },
"titles": { "titles": {
@@ -128,15 +131,11 @@
}, },
"morning": { "morning": {
"default": "Bu sabah ne izlemek istersiniz?", "default": "Bu sabah ne izlemek istersiniz?",
"extra": [ "extra": ["Before Sunrise'a iyi diyorlar"]
"Before Sunrise'a iyi diyorlar"
]
}, },
"night": { "night": {
"default": "Bu akşam ne izlemek istersiniz?", "default": "Bu akşam ne izlemek istersiniz?",
"extra": [ "extra": ["Yoruldun mu? The Exorcist'e iyi diyorlar."]
"Yoruldun mu? The Exorcist'e iyi diyorlar."
]
} }
} }
}, },

View File

@@ -57,6 +57,8 @@
}, },
"host": "Ви під’єднуєтеся до <0>{{hostname}}</0> підтвердьте, що довіряєте йому, перш ніж створювати обліковий запис", "host": "Ви під’єднуєтеся до <0>{{hostname}}</0> підтвердьте, що довіряєте йому, перш ніж створювати обліковий запис",
"no": "Повернутися назад", "no": "Повернутися назад",
"noHost": "Сервер не був налаштований, тому ви не можете створити обліковий запис",
"noHostTitle": "Сервер не налаштований!",
"title": "Чи довіряєте ви цьому серверу?", "title": "Чи довіряєте ви цьому серверу?",
"yes": "Я довіряю цьому серверу" "yes": "Я довіряю цьому серверу"
}, },
@@ -116,7 +118,15 @@
"failed": "Не вдалося знайти медіафайли, повторіть спробу!", "failed": "Не вдалося знайти медіафайли, повторіть спробу!",
"loading": "Завантаження...", "loading": "Завантаження...",
"noResults": "Ми не змогли знайти нічого!", "noResults": "Ми не змогли знайти нічого!",
"placeholder": "Що ви хочете подивитися?", "placeholder": {
"default": "Що ви хочете подивитися?",
"extra": [
"Що ви бажаєте дослідити?",
"Що у вашому списку спостереження?",
"Який ваш улюблений фільм?",
"Який ваш улюблений серіал?"
]
},
"sectionTitle": "Результати пошуку" "sectionTitle": "Результати пошуку"
}, },
"titles": { "titles": {
@@ -242,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "Копіювати посилання на плейлист HLS",
"disclaimer": "Завантаження беруться безпосередньо від провайдера. У movie-web немає контролю над тим, як надаються завантаження.", "disclaimer": "Завантаження беруться безпосередньо від провайдера. У movie-web немає контролю над тим, як надаються завантаження.",
"downloadSubtitle": "Завантажити поточні субтитри", "downloadSubtitle": "Завантажити поточні субтитри",
"downloadVideo": "Завантажити відео", "downloadVideo": "Завантажити відео",
@@ -525,6 +536,7 @@
} }
}, },
"subtitles": { "subtitles": {
"backgroundBlurLabel": "Розмиття фону",
"backgroundLabel": "Прозорість фону", "backgroundLabel": "Прозорість фону",
"colorLabel": "Колір", "colorLabel": "Колір",
"previewQuote": "Я не повинен боятися. Страх вбиває розум.", "previewQuote": "Я не повинен боятися. Страх вбиває розум.",

View File

@@ -116,12 +116,30 @@
"failed": "Không thể tìm thấy nội dung, hãy thử lại!", "failed": "Không thể tìm thấy nội dung, hãy thử lại!",
"loading": "Đang tải...", "loading": "Đang tải...",
"noResults": "Chúng tôi không thể tìm thấy gì!", "noResults": "Chúng tôi không thể tìm thấy gì!",
"placeholder": "Bạn muốn xem gì?", "placeholder": {
"default": "Bạn muốn xem gì?",
"extra": []
},
"sectionTitle": "Kết quả tìm kiếm" "sectionTitle": "Kết quả tìm kiếm"
}, },
"titles": { "titles": {
"day": {
"default": "Chiều nay bạn muốn coi gì?",
"extra": [
"Cảm thấy muốn phiêu lưu? Phim Công viên kỷ Jura có thể là sự lựa chọn hoàn hảo cho bạn."
]
},
"morning": {
"default": "Sáng nay bạn muốn coi gì?",
"extra": [
"Tôi nghe nói rằng bộ phim Before Sunrise hay đấy"
]
},
"night": { "night": {
"default": "Đêm nay bạn muốn coi gì?" "default": "Đêm nay bạn muốn coi gì?",
"extra": [
"Cảm thấy mệt? Tôi nghe nói phim The Exorcist hay đấy."
]
} }
} }
}, },
@@ -140,6 +158,7 @@
"about": "Về chúng tôi", "about": "Về chúng tôi",
"donation": "Ủng hộ", "donation": "Ủng hộ",
"logout": "Đăng xuất", "logout": "Đăng xuất",
"register": "Đồng bộ hóa với đám mây",
"settings": "Cài đặt", "settings": "Cài đặt",
"support": "Hỗ trợ" "support": "Hỗ trợ"
} }
@@ -158,7 +177,8 @@
"title": "Bạn có chắc không?" "title": "Bạn có chắc không?"
}, },
"extension": { "extension": {
"back": "Trở lại" "back": "Trở lại",
"explainer": "Bạn có thể sử dụng các nguồn tốt nhất mà chúng tôi cung cấp bằng cách sử dụng tiện ích mở rộng trình duyệt. Đơn giản chỉ cần cài đặt."
} }
}, },
"player": { "player": {

View File

@@ -116,27 +116,24 @@
"failed": "未能找到媒體,請重試!", "failed": "未能找到媒體,請重試!",
"loading": "載入中...", "loading": "載入中...",
"noResults": "我们找不到任何结果!", "noResults": "我们找不到任何结果!",
"placeholder": "您想看什麼?", "placeholder": {
"default": "您想看什麼?",
"extra": []
},
"sectionTitle": "搜索結果" "sectionTitle": "搜索結果"
}, },
"titles": { "titles": {
"day": { "day": {
"default": "您今天下午想看什麼?", "default": "您今天下午想看什麼?",
"extra": [ "extra": ["想要來場冒險嗎?《侏羅紀公園》可能是完美選擇。"]
"想要來場冒險嗎?《侏羅紀公園》可能是完美選擇。"
]
}, },
"morning": { "morning": {
"default": "您今天早上想看什麼?", "default": "您今天早上想看什麼?",
"extra": [ "extra": ["我聽說《情留半天》不錯"]
"我聽說《情留半天》不錯"
]
}, },
"night": { "night": {
"default": "您今晚想看什麼?", "default": "您今晚想看什麼?",
"extra": [ "extra": ["疲倦了嗎?我聽說《驅魔人》不錯。"]
"疲倦了嗎?我聽說《驅魔人》不錯。"
]
} }
} }
}, },

View File

@@ -57,6 +57,8 @@
}, },
"host": "您正在连接到 <0>{{hostname}}</0> - 在创建账户前,确保您信任它", "host": "您正在连接到 <0>{{hostname}}</0> - 在创建账户前,确保您信任它",
"no": "返回", "no": "返回",
"noHost": "服务器尚未进行配置,因此您无法创建账户",
"noHostTitle": "未配置服务器!",
"title": "您是否信任这个服务器?", "title": "您是否信任这个服务器?",
"yes": "我信任这个服务器" "yes": "我信任这个服务器"
}, },
@@ -71,11 +73,11 @@
} }
}, },
"errors": { "errors": {
"badge": "它已损坏", "badge": "坏",
"details": "错误细节", "details": "错误细节",
"reloadPage": "刷新页面", "reloadPage": "刷新页面",
"showError": "显示错误细节", "showError": "显示错误细节",
"title": "我们遇到错误!" "title": "我们遇到错误!"
}, },
"footer": { "footer": {
"legal": { "legal": {
@@ -116,7 +118,15 @@
"failed": "查找媒体失败,请重试!", "failed": "查找媒体失败,请重试!",
"loading": "载入中……", "loading": "载入中……",
"noResults": "我们找不到任何结果!", "noResults": "我们找不到任何结果!",
"placeholder": "您想看些什么?", "placeholder": {
"default": "您想看些什么?",
"extra": [
"您想探索些什么?",
"您的片单上都有啥?",
"您最喜欢的影片是什么?",
"您最喜欢的剧目是什么?"
]
},
"sectionTitle": "搜索结果" "sectionTitle": "搜索结果"
}, },
"titles": { "titles": {
@@ -242,6 +252,7 @@
}, },
"menus": { "menus": {
"downloads": { "downloads": {
"copyHlsPlaylist": "复制 HLS 播放列表链接",
"disclaimer": "下载内容是直接从内容提供者获取的。movie-web 无法控制下载内容如何被提供。", "disclaimer": "下载内容是直接从内容提供者获取的。movie-web 无法控制下载内容如何被提供。",
"downloadSubtitle": "下载当前字幕", "downloadSubtitle": "下载当前字幕",
"downloadVideo": "下载视频", "downloadVideo": "下载视频",
@@ -525,6 +536,7 @@
} }
}, },
"subtitles": { "subtitles": {
"backgroundBlurLabel": "背景模糊",
"backgroundLabel": "背景不透明度", "backgroundLabel": "背景不透明度",
"colorLabel": "颜色", "colorLabel": "颜色",
"previewQuote": "我不能害怕。恐惧是心灵的杀手。", "previewQuote": "我不能害怕。恐惧是心灵的杀手。",

View File

@@ -21,9 +21,7 @@ export function verifyValidMnemonic(mnemonic: string) {
return validateMnemonic(mnemonic, wordlist); return validateMnemonic(mnemonic, wordlist);
} }
export async function keysFromMnemonic(mnemonic: string): Promise<Keys> { export async function keysFromSeed(seed: Uint8Array): Promise<Keys> {
const seed = await seedFromMnemonic(mnemonic);
const { privateKey, publicKey } = forge.pki.ed25519.generateKeyPair({ const { privateKey, publicKey } = forge.pki.ed25519.generateKeyPair({
seed, seed,
}); });
@@ -35,6 +33,12 @@ export async function keysFromMnemonic(mnemonic: string): Promise<Keys> {
}; };
} }
export async function keysFromMnemonic(mnemonic: string): Promise<Keys> {
const seed = await seedFromMnemonic(mnemonic);
return keysFromSeed(seed);
}
export function genMnemonic(): string { export function genMnemonic(): string {
return generateMnemonic(wordlist); return generateMnemonic(wordlist);
} }

View File

@@ -6,6 +6,11 @@ import {
import { isAllowedExtensionVersion } from "@/backend/extension/compatibility"; import { isAllowedExtensionVersion } from "@/backend/extension/compatibility";
import { ExtensionMakeRequestResponse } from "@/backend/extension/plasmo"; import { ExtensionMakeRequestResponse } from "@/backend/extension/plasmo";
export const RULE_IDS = {
PREPARE_STREAM: 1,
SET_DOMAINS_HLS: 2,
};
// for some reason, about 500 ms is needed after // for some reason, about 500 ms is needed after
// page load before the extension starts responding properly // page load before the extension starts responding properly
const isExtensionReady = new Promise<void>((resolve) => { const isExtensionReady = new Promise<void>((resolve) => {

View File

@@ -1,6 +1,6 @@
import { Stream } from "@movie-web/providers"; import { Stream } from "@movie-web/providers";
import { setDomainRule } from "@/backend/extension/messaging"; import { RULE_IDS, setDomainRule } from "@/backend/extension/messaging";
function extractDomain(url: string): string | null { function extractDomain(url: string): string | null {
try { try {
@@ -36,7 +36,7 @@ function buildHeadersFromStream(stream: Stream): Record<string, string> {
export async function prepareStream(stream: Stream) { export async function prepareStream(stream: Stream) {
await setDomainRule({ await setDomainRule({
ruleId: 1, ruleId: RULE_IDS.PREPARE_STREAM,
targetDomains: extractDomainsFromStream(stream), targetDomains: extractDomainsFromStream(stream),
requestHeaders: buildHeadersFromStream(stream), requestHeaders: buildHeadersFromStream(stream),
}); });

View File

@@ -5,13 +5,14 @@ import { useCallback } from "react";
import { isExtensionActiveCached } from "@/backend/extension/messaging"; import { isExtensionActiveCached } from "@/backend/extension/messaging";
import { ScrapingItems, ScrapingSegment } from "@/hooks/useProviderScrape"; import { ScrapingItems, ScrapingSegment } from "@/hooks/useProviderScrape";
import { BACKEND_URL } from "@/setup/constants";
import { useAuthStore } from "@/stores/auth"; import { useAuthStore } from "@/stores/auth";
import { PlayerMeta } from "@/stores/player/slices/source"; import { PlayerMeta } from "@/stores/player/slices/source";
// for anybody who cares - these are anonymous metrics. // for anybody who cares - these are anonymous metrics.
// They are just used for figuring out if providers are broken or not // They are just used for figuring out if providers are broken or not
const metricsEndpoint = "https://backend.movie-web.app/metrics/providers"; const metricsEndpoint = `${BACKEND_URL}/metrics/providers`;
const captchaMetricsEndpoint = "https://backend.movie-web.app/metrics/captcha"; const captchaMetricsEndpoint = `${BACKEND_URL}/metrics/captcha`;
const batchId = () => nanoid(32); const batchId = () => nanoid(32);
export type ProviderMetric = { export type ProviderMetric = {
@@ -44,6 +45,7 @@ function getStackTrace(error: Error, lines: number) {
} }
export async function reportProviders(items: ProviderMetric[]): Promise<void> { export async function reportProviders(items: ProviderMetric[]): Promise<void> {
if (!BACKEND_URL) return;
return ofetch(metricsEndpoint, { return ofetch(metricsEndpoint, {
method: "POST", method: "POST",
body: { body: {
@@ -156,6 +158,7 @@ export function useReportProviders() {
} }
export function reportCaptchaSolve(success: boolean) { export function reportCaptchaSolve(success: boolean) {
if (!BACKEND_URL) return;
ofetch(captchaMetricsEndpoint, { ofetch(captchaMetricsEndpoint, {
method: "POST", method: "POST",
body: { body: {

View File

@@ -5,6 +5,11 @@ import { convertSubtitlesToSrt } from "@/components/player/utils/captions";
import { CaptionListItem } from "@/stores/player/slices/source"; import { CaptionListItem } from "@/stores/player/slices/source";
import { SimpleCache } from "@/utils/cache"; import { SimpleCache } from "@/utils/cache";
import {
isExtensionActiveCached,
sendExtensionRequest,
} from "../extension/messaging";
export const subtitleTypeList = list().map((type) => `.${type}`); export const subtitleTypeList = list().map((type) => `.${type}`);
const downloadCache = new SimpleCache<string, string>(); const downloadCache = new SimpleCache<string, string>();
downloadCache.setCompare((a, b) => a === b); downloadCache.setCompare((a, b) => a === b);
@@ -21,7 +26,22 @@ export async function downloadCaption(
let data: string | undefined; let data: string | undefined;
if (caption.needsProxy) { if (caption.needsProxy) {
if (isExtensionActiveCached()) {
const extensionResponse = await sendExtensionRequest({
url: caption.url,
method: "GET",
});
if (
!extensionResponse?.success ||
typeof extensionResponse.response.body !== "string"
) {
throw new Error("failed to get caption data from extension");
}
data = extensionResponse.response.body;
} else {
data = await proxiedFetch<string>(caption.url, { responseType: "text" }); data = await proxiedFetch<string>(caption.url, { responseType: "text" });
}
} else { } else {
data = await fetch(caption.url).then((v) => v.text()); data = await fetch(caption.url).then((v) => v.text());
} }
@@ -31,3 +51,15 @@ export async function downloadCaption(
downloadCache.set(caption.url, output, expirySeconds); downloadCache.set(caption.url, output, expirySeconds);
return output; return output;
} }
/**
* Downloads the WebVTT content. No different than a simple
* get request with a cache.
*/
export async function downloadWebVTT(url: string): Promise<string> {
const cached = downloadCache.get(url);
if (cached) return cached;
const data = await fetch(url).then((v) => v.text());
return data;
}

View File

@@ -144,12 +144,16 @@ export function decodeTMDBId(
const baseURL = "https://api.themoviedb.org/3"; const baseURL = "https://api.themoviedb.org/3";
const apiKey = conf().TMDB_READ_API_KEY;
const headers = { const headers = {
accept: "application/json", accept: "application/json",
Authorization: `Bearer ${conf().TMDB_READ_API_KEY}`, Authorization: `Bearer ${apiKey}`,
}; };
async function get<T>(url: string, params?: object): Promise<T> { async function get<T>(url: string, params?: object): Promise<T> {
if (!apiKey) throw new Error("TMDB API key not set");
const res = await mwFetch<any>(encodeURI(url), { const res = await mwFetch<any>(encodeURI(url), {
headers, headers,
baseURL, baseURL,

View File

@@ -64,6 +64,8 @@ export enum Icons {
DONATION = "donation", DONATION = "donation",
CIRCLE_QUESTION = "circle_question", CIRCLE_QUESTION = "circle_question",
BRUSH = "brush", BRUSH = "brush",
CLOUD_ARROW_UP = "cloud_arrow_up",
FILE_ARROW_DOWN = "file_arrow_down",
} }
export interface IconProps { export interface IconProps {
@@ -134,6 +136,8 @@ const iconList: Record<Icons, string> = {
donation: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="currentColor" d="M163.9 136.9c-29.4-29.8-29.4-78.2 0-108s77-29.8 106.4 0l17.7 18 17.7-18c29.4-29.8 77-29.8 106.4 0s29.4 78.2 0 108L310.5 240.1c-6.2 6.3-14.3 9.4-22.5 9.4s-16.3-3.1-22.5-9.4L163.9 136.9zM568.2 336.3c13.1 17.8 9.3 42.8-8.5 55.9L433.1 485.5c-23.4 17.2-51.6 26.5-80.7 26.5H192 32c-17.7 0-32-14.3-32-32V416c0-17.7 14.3-32 32-32H68.8l44.9-36c22.7-18.2 50.9-28 80-28H272h16 64c17.7 0 32 14.3 32 32s-14.3 32-32 32H288 272c-8.8 0-16 7.2-16 16s7.2 16 16 16H392.6l119.7-88.2c17.8-13.1 42.8-9.3 55.9 8.5zM193.6 384l0 0-.9 0c.3 0 .6 0 .9 0z"/></svg>`, donation: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="currentColor" d="M163.9 136.9c-29.4-29.8-29.4-78.2 0-108s77-29.8 106.4 0l17.7 18 17.7-18c29.4-29.8 77-29.8 106.4 0s29.4 78.2 0 108L310.5 240.1c-6.2 6.3-14.3 9.4-22.5 9.4s-16.3-3.1-22.5-9.4L163.9 136.9zM568.2 336.3c13.1 17.8 9.3 42.8-8.5 55.9L433.1 485.5c-23.4 17.2-51.6 26.5-80.7 26.5H192 32c-17.7 0-32-14.3-32-32V416c0-17.7 14.3-32 32-32H68.8l44.9-36c22.7-18.2 50.9-28 80-28H272h16 64c17.7 0 32 14.3 32 32s-14.3 32-32 32H288 272c-8.8 0-16 7.2-16 16s7.2 16 16 16H392.6l119.7-88.2c17.8-13.1 42.8-9.3 55.9 8.5zM193.6 384l0 0-.9 0c.3 0 .6 0 .9 0z"/></svg>`,
circle_question: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="currentColor" d="M464 256A208 208 0 1 0 48 256a208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zm169.8-90.7c7.9-22.3 29.1-37.3 52.8-37.3h58.3c34.9 0 63.1 28.3 63.1 63.1c0 22.6-12.1 43.5-31.7 54.8L280 264.4c-.2 13-10.9 23.6-24 23.6c-13.3 0-24-10.7-24-24V250.5c0-8.6 4.6-16.5 12.1-20.8l44.3-25.4c4.7-2.7 7.6-7.7 7.6-13.1c0-8.4-6.8-15.1-15.1-15.1H222.6c-3.4 0-6.4 2.1-7.5 5.3l-.4 1.2c-4.4 12.5-18.2 19-30.6 14.6s-19-18.2-14.6-30.6l.4-1.2zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>`, circle_question: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path opacity="1" fill="currentColor" d="M464 256A208 208 0 1 0 48 256a208 208 0 1 0 416 0zM0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zm169.8-90.7c7.9-22.3 29.1-37.3 52.8-37.3h58.3c34.9 0 63.1 28.3 63.1 63.1c0 22.6-12.1 43.5-31.7 54.8L280 264.4c-.2 13-10.9 23.6-24 23.6c-13.3 0-24-10.7-24-24V250.5c0-8.6 4.6-16.5 12.1-20.8l44.3-25.4c4.7-2.7 7.6-7.7 7.6-13.1c0-8.4-6.8-15.1-15.1-15.1H222.6c-3.4 0-6.4 2.1-7.5 5.3l-.4 1.2c-4.4 12.5-18.2 19-30.6 14.6s-19-18.2-14.6-30.6l.4-1.2zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>`,
brush: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M162.4 6c-1.5-3.6-5-6-8.9-6h-19c-3.9 0-7.5 2.4-8.9 6L104.9 57.7c-3.2 8-14.6 8-17.8 0L66.4 6c-1.5-3.6-5-6-8.9-6H48C21.5 0 0 21.5 0 48V224v22.4V256H9.6 374.4 384v-9.6V224 48c0-26.5-21.5-48-48-48H230.5c-3.9 0-7.5 2.4-8.9 6L200.9 57.7c-3.2 8-14.6 8-17.8 0L162.4 6zM0 288v32c0 35.3 28.7 64 64 64h64v64c0 35.3 28.7 64 64 64s64-28.7 64-64V384h64c35.3 0 64-28.7 64-64V288H0zM192 432a16 16 0 1 1 0 32 16 16 0 1 1 0-32z" fill="currentColor"/></svg>`, brush: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M162.4 6c-1.5-3.6-5-6-8.9-6h-19c-3.9 0-7.5 2.4-8.9 6L104.9 57.7c-3.2 8-14.6 8-17.8 0L66.4 6c-1.5-3.6-5-6-8.9-6H48C21.5 0 0 21.5 0 48V224v22.4V256H9.6 374.4 384v-9.6V224 48c0-26.5-21.5-48-48-48H230.5c-3.9 0-7.5 2.4-8.9 6L200.9 57.7c-3.2 8-14.6 8-17.8 0L162.4 6zM0 288v32c0 35.3 28.7 64 64 64h64v64c0 35.3 28.7 64 64 64s64-28.7 64-64V384h64c35.3 0 64-28.7 64-64V288H0zM192 432a16 16 0 1 1 0 32 16 16 0 1 1 0-32z" fill="currentColor"/></svg>`,
cloud_arrow_up: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M144 480C64.5 480 0 415.5 0 336c0-62.8 40.2-116.2 96.2-135.9c-.1-2.7-.2-5.4-.2-8.1c0-88.4 71.6-160 160-160c59.3 0 111 32.2 138.7 80.2C409.9 102 428.3 96 448 96c53 0 96 43 96 96c0 12.2-2.3 23.8-6.4 34.6C596 238.4 640 290.1 640 352c0 70.7-57.3 128-128 128H144zm79-217c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l39-39V392c0 13.3 10.7 24 24 24s24-10.7 24-24V257.9l39 39c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-80-80c-9.4-9.4-24.6-9.4-33.9 0l-80 80z" fill="currentColor"/></svg>`,
file_arrow_down: `<svg xmlns="http://www.w3.org/2000/svg" height="1em" width="1em" viewBox="0 0 384 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 0C28.7 0 0 28.7 0 64V448c0 35.3 28.7 64 64 64H320c35.3 0 64-28.7 64-64V160H256c-17.7 0-32-14.3-32-32V0H64zM256 0V128H384L256 0zM216 232V334.1l31-31c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-72 72c-9.4 9.4-24.6 9.4-33.9 0l-72-72c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l31 31V232c0-13.3 10.7-24 24-24s24 10.7 24 24z" fill="currentColor"/></svg>`,
}; };
function ChromeCastButton() { function ChromeCastButton() {

View File

@@ -31,10 +31,16 @@ export function Button(props: Props) {
>, >,
) => { ) => {
if (loading) return; if (loading) return;
if (href && !onClick) navigate(href); if (href && !onClick) {
else onClick?.(event); event.preventDefault();
if (!href.includes("http")) {
navigate(href);
} else {
window.open(href, "_blank", "noreferrer");
}
} else onClick?.(event);
}, },
[onClick, href, navigate, loading], [loading, href, onClick, navigate],
); );
let colorClasses = "bg-white hover:bg-gray-200 text-black"; let colorClasses = "bg-white hover:bg-gray-200 text-black";

View File

@@ -25,7 +25,7 @@ export function IconPatch(props: IconPatchProps) {
return ( return (
<div className={props.className || undefined} onClick={props.onClick}> <div className={props.className || undefined} onClick={props.onClick}>
<div <div
className={`flex items-center justify-center rounded-full border-2 border-transparent backdrop-blur-lg bg-pill-background bg-opacity-50 transition-[background-color,color,transform,border-color] duration-75 ${transparentClasses} ${clickableClasses} ${activeClasses} ${sizeClasses}`} className={`flex items-center justify-center rounded-full border-2 border-transparent bg-pill-background bg-opacity-100 transition-[background-color,color,transform,border-color] duration-75 ${transparentClasses} ${clickableClasses} ${activeClasses} ${sizeClasses}`}
> >
<Icon icon={props.icon} /> <Icon icon={props.icon} />
</div> </div>

View File

@@ -0,0 +1,9 @@
import classNames from "classnames";
export function VerticalLine(props: { className?: string }) {
return (
<div className={classNames("w-full grid justify-center", props.className)}>
<div className="w-px h-10 bg-onboarding-divider" />
</div>
);
}

View File

@@ -47,8 +47,22 @@ export function Volume(props: Props) {
if (dragging) percentage = makePercentage(dragPercentage); if (dragging) percentage = makePercentage(dragPercentage);
const percentageString = makePercentageString(percentage); const percentageString = makePercentageString(percentage);
const handleWheel = useCallback(
(event: React.WheelEvent<HTMLDivElement>) => {
event.preventDefault();
let newVolume = volume - event.deltaY / 1000;
newVolume = Math.max(0, Math.min(newVolume, 1));
setVolume(newVolume);
},
[volume, setVolume],
);
return ( return (
<div className={props.className} onMouseEnter={handleMouseEnter}> <div
className={props.className}
onMouseEnter={handleMouseEnter}
onWheel={handleWheel}
>
<div className="pointer-events-auto flex cursor-pointer items-center py-0"> <div className="pointer-events-auto flex cursor-pointer items-center py-0">
<div className="px-4 text-2xl text-white" onClick={handleClick}> <div className="px-4 text-2xl text-white" onClick={handleClick}>
<Icon icon={percentage > 0 ? Icons.VOLUME : Icons.VOLUME_X} /> <Icon icon={percentage > 0 ? Icons.VOLUME : Icons.VOLUME_X} />

View File

@@ -231,7 +231,7 @@ export function CaptionSettingsView({ id }: { id: string }) {
<Menu.BackLink onClick={() => router.navigate("/captions")}> <Menu.BackLink onClick={() => router.navigate("/captions")}>
{t("player.menus.subtitles.settings.backlink")} {t("player.menus.subtitles.settings.backlink")}
</Menu.BackLink> </Menu.BackLink>
<Menu.Section className="space-y-6"> <Menu.Section className="space-y-6 pb-5">
<CaptionSetting <CaptionSetting
label={t("player.menus.subtitles.settings.delay")} label={t("player.menus.subtitles.settings.delay")}
max={10} max={10}
@@ -262,6 +262,14 @@ export function CaptionSettingsView({ id }: { id: string }) {
value={styling.backgroundOpacity * 100} value={styling.backgroundOpacity * 100}
textTransformer={(s) => `${s}%`} textTransformer={(s) => `${s}%`}
/> />
<CaptionSetting
label={t("settings.subtitles.backgroundBlurLabel")}
max={100}
min={0}
onChange={(v) => updateStyling({ backgroundBlur: v / 100 })}
value={styling.backgroundBlur * 100}
textTransformer={(s) => `${s}%`}
/>
<CaptionSetting <CaptionSetting
label={t("settings.subtitles.textSizeLabel")} label={t("settings.subtitles.textSizeLabel")}
max={200} max={200}

View File

@@ -122,9 +122,16 @@ export function CaptionsView({ id }: { id: string }) {
>(null); >(null);
const { selectCaptionById, disable } = useCaptions(); const { selectCaptionById, disable } = useCaptions();
const captionList = usePlayerStore((s) => s.captionList); const captionList = usePlayerStore((s) => s.captionList);
const getHlsCaptionList = usePlayerStore((s) => s.display?.getCaptionList);
const captions = useMemo(
() =>
captionList.length !== 0 ? captionList : getHlsCaptionList?.() ?? [],
[captionList, getHlsCaptionList],
);
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const subtitleList = useSubtitleList(captionList, searchQuery); const subtitleList = useSubtitleList(captions, searchQuery);
const [downloadReq, startDownload] = useAsyncFn( const [downloadReq, startDownload] = useAsyncFn(
async (captionId: string) => { async (captionId: string) => {

View File

@@ -4,6 +4,7 @@ import { OverlayDisplay } from "@/components/overlays/OverlayDisplay";
import { CastingInternal } from "@/components/player/internals/CastingInternal"; import { CastingInternal } from "@/components/player/internals/CastingInternal";
import { HeadUpdater } from "@/components/player/internals/HeadUpdater"; import { HeadUpdater } from "@/components/player/internals/HeadUpdater";
import { KeyboardEvents } from "@/components/player/internals/KeyboardEvents"; import { KeyboardEvents } from "@/components/player/internals/KeyboardEvents";
import { MediaSession } from "@/components/player/internals/MediaSession";
import { MetaReporter } from "@/components/player/internals/MetaReporter"; import { MetaReporter } from "@/components/player/internals/MetaReporter";
import { ProgressSaver } from "@/components/player/internals/ProgressSaver"; import { ProgressSaver } from "@/components/player/internals/ProgressSaver";
import { ThumbnailScraper } from "@/components/player/internals/ThumbnailScraper"; import { ThumbnailScraper } from "@/components/player/internals/ThumbnailScraper";
@@ -91,6 +92,7 @@ export function Container(props: PlayerProps) {
<VideoContainer /> <VideoContainer />
<ProgressSaver /> <ProgressSaver />
<KeyboardEvents /> <KeyboardEvents />
<MediaSession />
<div className="relative h-screen overflow-hidden"> <div className="relative h-screen overflow-hidden">
<VideoClickTarget showingControls={props.showingControls} /> <VideoClickTarget showingControls={props.showingControls} />
<HeadUpdater /> <HeadUpdater />

View File

@@ -55,6 +55,10 @@ export function CaptionCue({
color: styling.color, color: styling.color,
fontSize: `${(1.5 * styling.size).toFixed(2)}em`, fontSize: `${(1.5 * styling.size).toFixed(2)}em`,
backgroundColor: `rgba(0,0,0,${styling.backgroundOpacity.toFixed(2)})`, backgroundColor: `rgba(0,0,0,${styling.backgroundOpacity.toFixed(2)})`,
backdropFilter:
styling.backgroundBlur !== 0
? `blur(${Math.floor(styling.backgroundBlur * 64)}px)`
: "none",
}} }}
> >
<span <span

View File

@@ -1,6 +1,11 @@
import fscreen from "fscreen"; import fscreen from "fscreen";
import Hls, { Level } from "hls.js"; import Hls, { Level } from "hls.js";
import {
RULE_IDS,
isExtensionActiveCached,
setDomainRule,
} from "@/backend/extension/messaging";
import { import {
DisplayInterface, DisplayInterface,
DisplayInterfaceEvents, DisplayInterfaceEvents,
@@ -31,8 +36,8 @@ const levelConversionMap: Record<number, SourceQuality> = {
480: "480", 480: "480",
}; };
function hlsLevelToQuality(level: Level): SourceQuality | null { function hlsLevelToQuality(level?: Level): SourceQuality | null {
return levelConversionMap[level.height] ?? null; return levelConversionMap[level?.height ?? 0] ?? null;
} }
function qualityToHlsLevel(quality: SourceQuality): number | null { function qualityToHlsLevel(quality: SourceQuality): number | null {
@@ -62,6 +67,11 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
let preferenceQuality: SourceQuality | null = null; let preferenceQuality: SourceQuality | null = null;
let lastVolume = 1; let lastVolume = 1;
const languagePromises = new Map<
string,
(value: void | PromiseLike<void>) => void
>();
function reportLevels() { function reportLevels() {
if (!hls) return; if (!hls) return;
const levels = hls.levels; const levels = hls.levels;
@@ -128,6 +138,7 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
}, },
}, },
}, },
renderTextTracksNatively: false,
}); });
hls.on(Hls.Events.ERROR, (event, data) => { hls.on(Hls.Events.ERROR, (event, data) => {
console.error("HLS error", data); console.error("HLS error", data);
@@ -144,12 +155,40 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
if (!hls) return; if (!hls) return;
reportLevels(); reportLevels();
setupQualityForHls(); setupQualityForHls();
if (isExtensionActiveCached()) {
hls.on(Hls.Events.LEVEL_LOADED, async (_, data) => {
const chunkUrlsDomains = data.details.fragments.map(
(v) => new URL(v.url).hostname,
);
const chunkUrls = [...new Set(chunkUrlsDomains)];
await setDomainRule({
ruleId: RULE_IDS.SET_DOMAINS_HLS,
targetDomains: chunkUrls,
requestHeaders: {
...src.preferredHeaders,
...src.headers,
},
});
});
}
}); });
hls.on(Hls.Events.LEVEL_SWITCHED, () => { hls.on(Hls.Events.LEVEL_SWITCHED, () => {
if (!hls) return; if (!hls) return;
const quality = hlsLevelToQuality(hls.levels[hls.currentLevel]); const quality = hlsLevelToQuality(hls.levels[hls.currentLevel]);
emit("changedquality", quality); emit("changedquality", quality);
}); });
hls.on(Hls.Events.SUBTITLE_TRACK_LOADED, () => {
for (const [lang, resolve] of languagePromises) {
const track = hls?.subtitleTracks.find((t) => t.lang === lang);
if (track) {
resolve();
languagePromises.delete(lang);
break;
}
}
});
} }
hls.attachMedia(vid); hls.attachMedia(vid);
@@ -390,5 +429,40 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
setPlaybackRate(rate) { setPlaybackRate(rate) {
if (videoElement) videoElement.playbackRate = rate; if (videoElement) videoElement.playbackRate = rate;
}, },
getCaptionList() {
return (
hls?.subtitleTracks.map((track) => {
return {
id: track.id.toString(),
language: track.lang ?? "unknown",
url: track.url,
needsProxy: false,
hls: true,
};
}) ?? []
);
},
getSubtitleTracks() {
return hls?.subtitleTracks ?? [];
},
async setSubtitlePreference(lang) {
// default subtitles are already loaded by hls.js
const track = hls?.subtitleTracks.find((t) => t.lang === lang);
if (track?.details !== undefined) return Promise.resolve();
// need to wait a moment before hls loads the subtitles
const promise = new Promise<void>((resolve, reject) => {
languagePromises.set(lang, resolve);
// reject after some time, if hls.js fails to load the subtitles
// for any reason
setTimeout(() => {
reject();
languagePromises.delete(lang);
}, 5000);
});
hls?.setSubtitleOption({ lang });
return promise;
},
}; };
} }

View File

@@ -274,5 +274,14 @@ export function makeChromecastDisplayInterface(
playbackRate = rate; playbackRate = rate;
setSource(); setSource();
}, },
getCaptionList() {
return [];
},
getSubtitleTracks() {
return [];
},
async setSubtitlePreference() {
return Promise.resolve();
},
}; };
} }

View File

@@ -1,4 +1,7 @@
import { MediaPlaylist } from "hls.js";
import { MWMediaType } from "@/backend/metadata/types/mw"; import { MWMediaType } from "@/backend/metadata/types/mw";
import { CaptionListItem } from "@/stores/player/slices/source";
import { LoadableSource, SourceQuality } from "@/stores/player/utils/qualities"; import { LoadableSource, SourceQuality } from "@/stores/player/utils/qualities";
import { Listener } from "@/utils/events"; import { Listener } from "@/utils/events";
@@ -70,4 +73,7 @@ export interface DisplayInterface extends Listener<DisplayInterfaceEvents> {
setMeta(meta: DisplayMeta): void; setMeta(meta: DisplayMeta): void;
setCaption(caption: DisplayCaption | null): void; setCaption(caption: DisplayCaption | null): void;
getType(): DisplayType; getType(): DisplayType;
getCaptionList(): CaptionListItem[];
getSubtitleTracks(): MediaPlaylist[];
setSubtitlePreference(lang: string): Promise<void>;
} }

View File

@@ -1,9 +1,16 @@
import { useCallback } from "react"; import { useCallback, useMemo } from "react";
import subsrt from "subsrt-ts";
import { downloadCaption } from "@/backend/helpers/subs"; import { downloadCaption, downloadWebVTT } from "@/backend/helpers/subs";
import { Caption } from "@/stores/player/slices/source";
import { usePlayerStore } from "@/stores/player/store"; import { usePlayerStore } from "@/stores/player/store";
import { useSubtitleStore } from "@/stores/subtitles"; import { useSubtitleStore } from "@/stores/subtitles";
import {
filterDuplicateCaptionCues,
parseVttSubtitles,
} from "../utils/captions";
export function useCaptions() { export function useCaptions() {
const setLanguage = useSubtitleStore((s) => s.setLanguage); const setLanguage = useSubtitleStore((s) => s.setLanguage);
const enabled = useSubtitleStore((s) => s.enabled); const enabled = useSubtitleStore((s) => s.enabled);
@@ -12,32 +19,85 @@ export function useCaptions() {
); );
const setCaption = usePlayerStore((s) => s.setCaption); const setCaption = usePlayerStore((s) => s.setCaption);
const lastSelectedLanguage = useSubtitleStore((s) => s.lastSelectedLanguage); const lastSelectedLanguage = useSubtitleStore((s) => s.lastSelectedLanguage);
const captionList = usePlayerStore((s) => s.captionList); const captionList = usePlayerStore((s) => s.captionList);
const getHlsCaptionList = usePlayerStore((s) => s.display?.getCaptionList);
const getSubtitleTracks = usePlayerStore((s) => s.display?.getSubtitleTracks);
const setSubtitlePreference = usePlayerStore(
(s) => s.display?.setSubtitlePreference,
);
const captions = useMemo(
() =>
captionList.length !== 0 ? captionList : getHlsCaptionList?.() ?? [],
[captionList, getHlsCaptionList],
);
const selectCaptionById = useCallback( const selectCaptionById = useCallback(
async (captionId: string) => { async (captionId: string) => {
const caption = captionList.find((v) => v.id === captionId); const caption = captions.find((v) => v.id === captionId);
if (!caption) return; if (!caption) return;
const srtData = await downloadCaption(caption);
setCaption({ const captionToSet: Caption = {
id: caption.id, id: caption.id,
language: caption.language, language: caption.language,
srtData,
url: caption.url, url: caption.url,
}); srtData: "",
};
if (!caption.hls) {
const srtData = await downloadCaption(caption);
captionToSet.srtData = srtData;
} else {
// request a language change to hls, so it can load the subtitles
await setSubtitlePreference?.(caption.language);
const track = getSubtitleTracks?.().find(
(t) => t.id.toString() === caption.id && t.details !== undefined,
);
if (!track) return;
const fragments =
track.details?.fragments?.filter(
(frag) => frag !== null && frag.url !== null,
) ?? [];
const vttCaptions = (
await Promise.all(
fragments.map(async (frag) => {
const vtt = await downloadWebVTT(frag.url);
return parseVttSubtitles(vtt);
}),
)
).flat();
const filtered = filterDuplicateCaptionCues(vttCaptions);
const srtData = subsrt.build(filtered, { format: "srt" });
captionToSet.srtData = srtData;
}
setCaption(captionToSet);
resetSubtitleSpecificSettings(); resetSubtitleSpecificSettings();
setLanguage(caption.language); setLanguage(caption.language);
}, },
[setLanguage, captionList, setCaption, resetSubtitleSpecificSettings], [
setLanguage,
captions,
setCaption,
resetSubtitleSpecificSettings,
getSubtitleTracks,
setSubtitlePreference,
],
); );
const selectLanguage = useCallback( const selectLanguage = useCallback(
async (language: string) => { async (language: string) => {
const caption = captionList.find((v) => v.language === language); const caption = captions.find((v) => v.language === language);
if (!caption) return; if (!caption) return;
return selectCaptionById(caption.id); return selectCaptionById(caption.id);
}, },
[captionList, selectCaptionById], [captions, selectCaptionById],
); );
const disable = useCallback(async () => { const disable = useCallback(async () => {

View File

@@ -0,0 +1,185 @@
import { useCallback, useEffect, useRef } from "react";
import { usePlayerStore } from "@/stores/player/store";
import { usePlayerMeta } from "../hooks/usePlayerMeta";
export function MediaSession() {
const { setDirectMeta } = usePlayerMeta();
const setShouldStartFromBeginning = usePlayerStore(
(s) => s.setShouldStartFromBeginning,
);
const shouldUpdatePositionState = useRef(false);
const lastPlaybackPosition = useRef(0);
const data = usePlayerStore.getState();
const changeEpisode = useCallback(
(change: number) => {
const nextEp = data.meta?.episodes?.find(
(v) => v.number === (data.meta?.episode?.number ?? 0) + change,
);
if (!data.meta || !nextEp) return;
const metaCopy = { ...data.meta };
metaCopy.episode = nextEp;
setShouldStartFromBeginning(true);
setDirectMeta(metaCopy);
},
[data.meta, setDirectMeta, setShouldStartFromBeginning],
);
const updatePositionState = useCallback(
(position: number) => {
// If the browser doesn't support setPositionState, return
if (typeof navigator.mediaSession.setPositionState !== "function") return;
// If the updated position needs to be buffered, queue an update
if (position > data.progress.buffered) {
shouldUpdatePositionState.current = true;
}
if (position > data.progress.duration) return;
lastPlaybackPosition.current = data.progress.time;
navigator.mediaSession.setPositionState({
duration: data.progress.duration,
playbackRate: data.mediaPlaying.playbackRate,
position,
});
},
[
data.mediaPlaying.playbackRate,
data.progress.buffered,
data.progress.duration,
data.progress.time,
],
);
useEffect(() => {
if (!("mediaSession" in navigator)) return;
// If the media is paused, update the navigator
if (data.mediaPlaying.isPaused) {
navigator.mediaSession.playbackState = "paused";
} else {
navigator.mediaSession.playbackState = "playing";
}
}, [data.mediaPlaying.isPaused]);
useEffect(() => {
if (!("mediaSession" in navigator)) return;
updatePositionState(data.progress.time);
}, [data.progress.time, data.mediaPlaying.playbackRate, updatePositionState]);
useEffect(() => {
if (!("mediaSession" in navigator)) return;
// If not already updating the position state, and the media is loading, queue an update
if (!shouldUpdatePositionState.current && data.mediaPlaying.isLoading) {
shouldUpdatePositionState.current = true;
}
// If the user has skipped (or MediaSession desynced) by more than 5 seconds, queue an update
if (
Math.abs(data.progress.time - lastPlaybackPosition.current) >= 5 &&
!data.mediaPlaying.isLoading &&
!shouldUpdatePositionState.current
) {
shouldUpdatePositionState.current = true;
}
// If not loading and the position state is queued, update it
if (shouldUpdatePositionState.current && !data.mediaPlaying.isLoading) {
shouldUpdatePositionState.current = false;
updatePositionState(data.progress.time);
}
lastPlaybackPosition.current = data.progress.time;
}, [updatePositionState, data.progress.time, data.mediaPlaying.isLoading]);
useEffect(() => {
if (
!("mediaSession" in navigator) ||
(!data.mediaPlaying.isLoading &&
data.mediaPlaying.isPlaying &&
!data.display)
)
return;
let title: string | undefined;
let artist: string | undefined;
if (data.meta?.type === "movie") {
title = data.meta?.title;
} else if (data.meta?.type === "show") {
artist = data.meta?.title;
title = `S${data.meta?.season?.number} E${data.meta?.episode?.number}: ${data.meta?.episode?.title}`;
}
navigator.mediaSession.metadata = new MediaMetadata({
title,
artist,
artwork: [
{
src: data.meta?.poster ?? "",
sizes: "342x513",
type: "image/png",
},
],
});
navigator.mediaSession.setActionHandler("play", () => {
if (data.mediaPlaying.isLoading) return;
data.display?.play();
updatePositionState(data.progress.time);
});
navigator.mediaSession.setActionHandler("pause", () => {
if (data.mediaPlaying.isLoading) return;
data.display?.pause();
updatePositionState(data.progress.time);
});
navigator.mediaSession.setActionHandler("seekto", (e) => {
if (!e.seekTime) return;
data.display?.setTime(e.seekTime);
updatePositionState(e.seekTime);
});
if ((data.meta?.episode?.number ?? 1) !== 1) {
navigator.mediaSession.setActionHandler("previoustrack", () => {
changeEpisode(-1);
});
} else {
navigator.mediaSession.setActionHandler("previoustrack", null);
}
if (data.meta?.episode?.number !== data.meta?.episodes?.length) {
navigator.mediaSession.setActionHandler("nexttrack", () => {
changeEpisode(1);
});
} else {
navigator.mediaSession.setActionHandler("nexttrack", null);
}
}, [
changeEpisode,
updatePositionState,
data.mediaPlaying.hasPlayedOnce,
data.mediaPlaying.isLoading,
data.progress.duration,
data.progress.time,
data.meta?.episode?.number,
data.meta?.episodes?.length,
data.display,
data.mediaPlaying,
data.meta?.episode?.title,
data.meta?.title,
data.meta?.type,
data.meta?.poster,
data.meta?.season?.number,
]);
return null;
}

View File

@@ -50,12 +50,30 @@ export function convertSubtitlesToSrt(text: string): string {
return srt; return srt;
} }
export function filterDuplicateCaptionCues(cues: ContentCaption[]) {
return cues.reduce((acc: ContentCaption[], cap: ContentCaption) => {
const lastCap = acc[acc.length - 1];
const isSameAsLast =
lastCap?.start === cap.start &&
lastCap?.end === cap.end &&
lastCap?.content === cap.content;
if (lastCap === undefined || !isSameAsLast) {
acc.push(cap);
}
return acc;
}, []);
}
export function parseVttSubtitles(vtt: string) {
return parse(vtt).filter((cue) => cue.type === "caption") as CaptionCueType[];
}
export function parseSubtitles( export function parseSubtitles(
text: string, text: string,
_language?: string, _language?: string,
): CaptionCueType[] { ): CaptionCueType[] {
const vtt = convertSubtitlesToVtt(text); const vtt = convertSubtitlesToVtt(text);
return parse(vtt).filter((cue) => cue.type === "caption") as CaptionCueType[]; return parseVttSubtitles(vtt);
} }
function stringToBase64(input: string): string { function stringToBase64(input: string): string {

View File

@@ -28,6 +28,7 @@ export function convertRunoutputToSource(out: {
return { return {
type: "hls", type: "hls",
url: out.stream.playlist, url: out.stream.playlist,
headers: out.stream.headers,
preferredHeaders: out.stream.preferredHeaders, preferredHeaders: out.stream.preferredHeaders,
}; };
} }
@@ -50,6 +51,7 @@ export function convertRunoutputToSource(out: {
return { return {
type: "file", type: "file",
qualities, qualities,
headers: out.stream.headers,
preferredHeaders: out.stream.preferredHeaders, preferredHeaders: out.stream.preferredHeaders,
}; };
} }

View File

@@ -63,6 +63,7 @@ export function useAuth() {
const login = useCallback( const login = useCallback(
async (loginData: LoginData) => { async (loginData: LoginData) => {
if (!backendUrl) return;
const keys = await keysFromMnemonic(loginData.mnemonic); const keys = await keysFromMnemonic(loginData.mnemonic);
const publicKeyBase64Url = bytesToBase64Url(keys.publicKey); const publicKeyBase64Url = bytesToBase64Url(keys.publicKey);
const { challenge } = await getLoginChallengeToken( const { challenge } = await getLoginChallengeToken(
@@ -87,7 +88,7 @@ export function useAuth() {
); );
const logout = useCallback(async () => { const logout = useCallback(async () => {
if (!currentAccount) return; if (!currentAccount || !backendUrl) return;
try { try {
await removeSession( await removeSession(
backendUrl, backendUrl,
@@ -102,6 +103,7 @@ export function useAuth() {
const register = useCallback( const register = useCallback(
async (registerData: RegistrationData) => { async (registerData: RegistrationData) => {
if (!backendUrl) return;
const { challenge } = await getRegisterChallengeToken( const { challenge } = await getRegisterChallengeToken(
backendUrl, backendUrl,
registerData.recaptchaToken, registerData.recaptchaToken,
@@ -134,6 +136,7 @@ export function useAuth() {
progressItems: Record<string, ProgressMediaItem>, progressItems: Record<string, ProgressMediaItem>,
bookmarks: Record<string, BookmarkMediaItem>, bookmarks: Record<string, BookmarkMediaItem>,
) => { ) => {
if (!backendUrl) return;
if ( if (
Object.keys(progressItems).length === 0 && Object.keys(progressItems).length === 0 &&
Object.keys(bookmarks).length === 0 Object.keys(bookmarks).length === 0
@@ -159,6 +162,7 @@ export function useAuth() {
const restore = useCallback( const restore = useCallback(
async (account: AccountWithToken) => { async (account: AccountWithToken) => {
if (!backendUrl) return;
let user: { user: UserResponse; session: SessionResponse }; let user: { user: UserResponse; session: SessionResponse };
try { try {
user = await getUser(backendUrl, account.token); user = await getUser(backendUrl, account.token);

View File

@@ -1,7 +1,7 @@
import { conf } from "@/setup/config"; import { conf } from "@/setup/config";
import { useAuthStore } from "@/stores/auth"; import { useAuthStore } from "@/stores/auth";
export function useBackendUrl() { export function useBackendUrl(): string | null {
const backendUrl = useAuthStore((s) => s.backendUrl); const backendUrl = useAuthStore((s) => s.backendUrl);
return backendUrl ?? conf().BACKEND_URL; return backendUrl ?? conf().BACKEND_URL;
} }

View File

@@ -0,0 +1,125 @@
import { useCallback } from "react";
import { SessionResponse } from "@/backend/accounts/auth";
import { bookmarkMediaToInput } from "@/backend/accounts/bookmarks";
import {
base64ToBuffer,
bytesToBase64,
bytesToBase64Url,
encryptData,
keysFromMnemonic,
keysFromSeed,
signChallenge,
} from "@/backend/accounts/crypto";
import { importBookmarks, importProgress } from "@/backend/accounts/import";
import { getLoginChallengeToken, loginAccount } from "@/backend/accounts/login";
import { progressMediaItemToInputs } from "@/backend/accounts/progress";
import {
getRegisterChallengeToken,
registerAccount,
} from "@/backend/accounts/register";
import { removeSession } from "@/backend/accounts/sessions";
import { getSettings } from "@/backend/accounts/settings";
import {
UserResponse,
getBookmarks,
getProgress,
getUser,
} from "@/backend/accounts/user";
import { useAuthData } from "@/hooks/auth/useAuthData";
import { useBackendUrl } from "@/hooks/auth/useBackendUrl";
import { AccountWithToken, useAuthStore } from "@/stores/auth";
import { BookmarkMediaItem, useBookmarkStore } from "@/stores/bookmarks";
import { ProgressMediaItem, useProgressStore } from "@/stores/progress";
export interface RegistrationData {
recaptchaToken?: string;
mnemonic: string;
userData: {
device: string;
profile: {
colorA: string;
colorB: string;
icon: string;
};
};
}
export interface LoginData {
mnemonic: string;
userData: {
device: string;
};
}
export function useMigration() {
const currentAccount = useAuthStore((s) => s.account);
const progress = useProgressStore((s) => s.items);
const bookmarks = useBookmarkStore((s) => s.bookmarks);
const { login: userDataLogin } = useAuthData();
const importData = async (
backendUrl: string,
account: AccountWithToken,
progressItems: Record<string, ProgressMediaItem>,
bookmarkItems: Record<string, BookmarkMediaItem>,
) => {
if (
Object.keys(progressItems).length === 0 &&
Object.keys(bookmarkItems).length === 0
) {
return;
}
const progressInputs = Object.entries(progressItems).flatMap(
([tmdbId, item]) => progressMediaItemToInputs(tmdbId, item),
);
const bookmarkInputs = Object.entries(bookmarkItems).map(([tmdbId, item]) =>
bookmarkMediaToInput(tmdbId, item),
);
await Promise.all([
importProgress(backendUrl, account, progressInputs),
importBookmarks(backendUrl, account, bookmarkInputs),
]);
};
const migrate = useCallback(
async (backendUrl: string, recaptchaToken: string) => {
if (!currentAccount) return;
const { challenge } = await getRegisterChallengeToken(
backendUrl,
recaptchaToken,
);
const keys = await keysFromSeed(base64ToBuffer(currentAccount.seed));
const signature = await signChallenge(keys, challenge);
const registerResult = await registerAccount(backendUrl, {
challenge: {
code: challenge,
signature,
},
publicKey: bytesToBase64Url(keys.publicKey),
device: await encryptData(currentAccount.deviceName, keys.seed),
profile: currentAccount.profile,
});
const account = await userDataLogin(
registerResult,
registerResult.user,
registerResult.session,
bytesToBase64(keys.seed),
);
await importData(backendUrl, account, progress, bookmarks);
return account;
},
[currentAccount, userDataLogin, bookmarks, progress],
);
return {
migrate,
};
}

View File

@@ -0,0 +1,103 @@
import { useCallback } from "react";
import { Settings } from "@/hooks/useSettingsImport";
import { useAuthStore } from "@/stores/auth";
import { useBookmarkStore } from "@/stores/bookmarks";
import { useLanguageStore } from "@/stores/language";
import { usePreferencesStore } from "@/stores/preferences";
import { useProgressStore } from "@/stores/progress";
import { useQualityStore } from "@/stores/quality";
import { useSubtitleStore } from "@/stores/subtitles";
import { useThemeStore } from "@/stores/theme";
import { useVolumeStore } from "@/stores/volume";
export function useSettingsExport() {
const authStore = useAuthStore();
const bookmarksStore = useBookmarkStore();
const languageStore = useLanguageStore();
const preferencesStore = usePreferencesStore();
const progressStore = useProgressStore();
const qualityStore = useQualityStore();
const subtitleStore = useSubtitleStore();
const themeStore = useThemeStore();
const volumeStore = useVolumeStore();
const collect = useCallback(
(includeAuth: boolean): Settings => {
return {
auth: {
account: includeAuth ? authStore.account : undefined,
backendUrl: authStore.backendUrl,
proxySet: authStore.proxySet,
},
bookmarks: {
bookmarks: bookmarksStore.bookmarks,
},
language: {
language: languageStore.language,
},
preferences: {
enableThumbnails: preferencesStore.enableThumbnails,
},
progress: {
items: progressStore.items,
},
quality: {
quality: {
automaticQuality: qualityStore.quality.automaticQuality,
lastChosenQuality: qualityStore.quality.lastChosenQuality,
},
},
subtitles: {
lastSelectedLanguage: subtitleStore.lastSelectedLanguage,
styling: {
backgroundBlur: subtitleStore.styling.backgroundBlur,
backgroundOpacity: subtitleStore.styling.backgroundOpacity,
color: subtitleStore.styling.color,
size: subtitleStore.styling.size,
},
overrideCasing: subtitleStore.overrideCasing,
delay: subtitleStore.delay,
},
theme: {
theme: themeStore.theme,
},
volume: {
volume: volumeStore.volume,
},
};
},
[
authStore,
bookmarksStore,
languageStore,
preferencesStore,
progressStore,
qualityStore,
subtitleStore,
themeStore,
volumeStore,
],
);
const exportSettings = useCallback(
(includeAuth: boolean) => {
const output = JSON.stringify(collect(includeAuth), null, 2);
const blob = new Blob([output], { type: "application/json" });
const elem = window.document.createElement("a");
elem.href = window.URL.createObjectURL(blob);
const date = new Date();
elem.download = `movie-web settings - ${
date.toISOString().split("T")[0]
}.json`;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
},
[collect],
);
return exportSettings;
}

View File

@@ -0,0 +1,234 @@
import { useCallback } from "react";
import { z } from "zod";
import { useAuthStore } from "@/stores/auth";
import { useBookmarkStore } from "@/stores/bookmarks";
import { useLanguageStore } from "@/stores/language";
import { usePreferencesStore } from "@/stores/preferences";
import { useProgressStore } from "@/stores/progress";
import { useQualityStore } from "@/stores/quality";
import { useSubtitleStore } from "@/stores/subtitles";
import { useThemeStore } from "@/stores/theme";
import { useVolumeStore } from "@/stores/volume";
const settingsSchema = z.object({
auth: z.object({
account: z
.object({
profile: z.object({
colorA: z.string(),
colorB: z.string(),
icon: z.string(),
}),
sessionId: z.string(),
userId: z.string(),
token: z.string(),
seed: z.string(),
deviceName: z.string(),
})
.nullish(),
backendUrl: z.string().nullable(),
proxySet: z.array(z.string()).nullable(),
}),
bookmarks: z.object({
bookmarks: z.record(
z.object({
title: z.string(),
year: z.number().optional(),
poster: z.string().optional(),
type: z.enum(["show", "movie"]),
updatedAt: z.number(),
}),
),
}),
language: z.object({
language: z.string(),
}),
preferences: z.object({
enableThumbnails: z.boolean(),
}),
progress: z.object({
items: z.record(
z.object({
title: z.string(),
year: z.number().optional(),
poster: z.string().optional(),
type: z.enum(["show", "movie"]),
updatedAt: z.number(),
progress: z
.object({
watched: z.number(),
duration: z.number(),
})
.optional(),
seasons: z.record(
z.object({
title: z.string(),
number: z.number(),
id: z.string(),
}),
),
episodes: z.record(
z.object({
title: z.string(),
number: z.number(),
id: z.string(),
seasonId: z.string(),
updatedAt: z.number(),
progress: z.object({
watched: z.number(),
duration: z.number(),
}),
}),
),
}),
),
}),
quality: z.object({
quality: z.object({
automaticQuality: z.boolean(),
lastChosenQuality: z
.enum(["unknown", "360", "480", "720", "1080", "4k"])
.nullable(),
}),
}),
subtitles: z.object({
lastSelectedLanguage: z.string().nullable(),
styling: z.object({
backgroundBlur: z.number(),
backgroundOpacity: z.number(),
color: z.string(),
size: z.number(),
}),
overrideCasing: z.boolean(),
delay: z.number(),
}),
theme: z.object({
theme: z.string().nullable(),
}),
volume: z.object({
volume: z.number(),
}),
});
const settingsPartialSchema = settingsSchema.partial();
export type Settings = z.infer<typeof settingsSchema>;
export function useSettingsImport() {
const authStore = useAuthStore();
const bookmarksStore = useBookmarkStore();
const languageStore = useLanguageStore();
const preferencesStore = usePreferencesStore();
const progressStore = useProgressStore();
const qualityStore = useQualityStore();
const subtitleStore = useSubtitleStore();
const themeStore = useThemeStore();
const volumeStore = useVolumeStore();
const importSettings = useCallback(
async (file: File) => {
const text = await file.text();
const data = settingsPartialSchema.parse(JSON.parse(text));
if (data.auth?.account) authStore.setAccount(data.auth.account);
if (data.auth?.backendUrl) authStore.setBackendUrl(data.auth.backendUrl);
if (data.auth?.proxySet) authStore.setProxySet(data.auth.proxySet);
if (data.bookmarks) {
for (const [id, item] of Object.entries(data.bookmarks.bookmarks)) {
bookmarksStore.setBookmark(id, {
title: item.title,
type: item.type,
year: item.year,
poster: item.poster,
updatedAt: item.updatedAt,
});
}
}
if (data.language) languageStore.setLanguage(data.language.language);
if (data.preferences) {
preferencesStore.setEnableThumbnails(data.preferences.enableThumbnails);
}
if (data.quality) {
qualityStore.setAutomaticQuality(data.quality.quality.automaticQuality);
qualityStore.setLastChosenQuality(
data.quality.quality.lastChosenQuality,
);
}
if (data.subtitles) {
subtitleStore.setLanguage(data.subtitles.lastSelectedLanguage);
subtitleStore.updateStyling(data.subtitles.styling);
subtitleStore.setOverrideCasing(data.subtitles.overrideCasing);
subtitleStore.setDelay(data.subtitles.delay);
}
if (data.theme) themeStore.setTheme(data.theme.theme);
if (data.volume) volumeStore.setVolume(data.volume.volume);
if (data.progress) {
for (const [id, item] of Object.entries(data.progress.items)) {
if (!progressStore.items[id]) {
progressStore.setItem(id, item);
}
// We want to preserve existing progress so we take the max of the updatedAt and the progress
const storeItem = progressStore.items[id];
storeItem.updatedAt = Math.max(storeItem.updatedAt, item.updatedAt);
storeItem.title = item.title;
storeItem.year = item.year;
storeItem.poster = item.poster;
storeItem.type = item.type;
storeItem.progress = item.progress
? {
duration: item.progress.duration,
watched: Math.max(
storeItem.progress?.watched ?? 0,
item.progress.watched,
),
}
: undefined;
for (const [seasonId, season] of Object.entries(item.seasons)) {
storeItem.seasons[seasonId] = season;
}
for (const [episodeId, episode] of Object.entries(item.episodes)) {
if (!storeItem.episodes[episodeId]) {
storeItem.episodes[episodeId] = episode;
}
const storeEpisode = storeItem.episodes[episodeId];
storeEpisode.updatedAt = Math.max(
storeEpisode.updatedAt,
episode.updatedAt,
);
storeEpisode.title = episode.title;
storeEpisode.number = episode.number;
storeEpisode.seasonId = episode.seasonId;
storeEpisode.progress = {
duration: episode.progress.duration,
watched: Math.max(
storeEpisode.progress.watched,
episode.progress.watched,
),
};
}
progressStore.setItem(id, storeItem);
}
}
},
[
authStore,
bookmarksStore,
languageStore,
preferencesStore,
progressStore,
qualityStore,
subtitleStore,
themeStore,
volumeStore,
],
);
return importSettings;
}

View File

@@ -70,6 +70,7 @@ export function AccountSettings(props: {
const url = useBackendUrl(); const url = useBackendUrl();
const { account } = props; const { account } = props;
const [sessionsResult, execSessions] = useAsyncFn(() => { const [sessionsResult, execSessions] = useAsyncFn(() => {
if (!url) return Promise.resolve([]);
return getSessions(url, account); return getSessions(url, account);
}, [account, url]); }, [account, url]);
useEffect(() => { useEffect(() => {
@@ -165,7 +166,7 @@ export function SettingsPage() {
); );
const saveChanges = useCallback(async () => { const saveChanges = useCallback(async () => {
if (account) { if (account && backendUrl) {
if ( if (
state.appLanguage.changed || state.appLanguage.changed ||
state.theme.changed || state.theme.changed ||
@@ -207,7 +208,13 @@ export function SettingsPage() {
// when backend url gets changed, log the user out first // when backend url gets changed, log the user out first
if (state.backendUrl.changed) { if (state.backendUrl.changed) {
await logout(); await logout();
setBackendUrl(state.backendUrl.state);
let url = state.backendUrl.state;
if (url && !url.startsWith("http://") && !url.startsWith("https://")) {
url = `https://${url}`;
}
setBackendUrl(url);
} }
}, [ }, [
state, state,

View File

@@ -0,0 +1,65 @@
import { Trans, useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { Icons } from "@/components/Icon";
import { Stepper } from "@/components/layout/Stepper";
import { CenterContainer } from "@/components/layout/ThinContainer";
import { VerticalLine } from "@/components/layout/VerticalLine";
import { Heading2, Paragraph } from "@/components/utils/Text";
import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout";
import { Card, CardContent, Link } from "@/pages/migration/utils";
import { PageTitle } from "@/pages/parts/util/PageTitle";
export function MigrationPage() {
const navigate = useNavigate();
const { t } = useTranslation();
return (
<MinimalPageLayout>
<PageTitle subpage k="global.pages.migration" />
<CenterContainer>
<Stepper steps={2} current={1} className="mb-12" />
<Heading2 className="!mt-0 !text-3xl max-w-[435px]">
{t("migration.start.title")}
</Heading2>
<Paragraph className="max-w-[320px]">
{t("migration.start.explainer")}
</Paragraph>
<div className="w-full flex flex-col md:flex-row gap-3">
<Card onClick={() => navigate("/migration/direct")}>
<CardContent
colorClass="!text-onboarding-best"
title={t("migration.start.options.direct.title")}
subtitle={t("migration.start.options.direct.quality")}
description={
<Trans i18nKey="migration.start.options.direct.description" />
}
icon={Icons.CLOUD_ARROW_UP}
>
<Link>{t("migration.start.options.direct.action")}</Link>
</CardContent>
</Card>
<div className="hidden md:grid grid-rows-[1fr,auto,1fr] justify-center gap-4">
<VerticalLine className="items-end" />
<span className="text-xs uppercase font-bold">
{t("migration.start.options.or")}
</span>
<VerticalLine />
</div>
<Card onClick={() => navigate("/migration/download")}>
<CardContent
colorClass="!text-migration-good"
title={t("migration.start.options.download.title")}
subtitle={t("migration.start.options.download.quality")}
description={t("migration.start.options.download.description")}
icon={Icons.FILE_ARROW_DOWN}
>
<Link>{t("migration.start.options.download.action")}</Link>
</CardContent>
</Card>
</div>
</CenterContainer>
</MinimalPageLayout>
);
}

View File

@@ -0,0 +1,26 @@
import { useCallback } from "react";
import { CenterContainer } from "@/components/layout/ThinContainer";
import { useSettingsExport } from "@/hooks/useSettingsExport";
import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout";
import { PageTitle } from "@/pages/parts/util/PageTitle";
export function MigrationDirectPage() {
const exportSettings = useSettingsExport();
const doDownload = useCallback(() => {
const data = exportSettings(false);
console.log(data);
}, [exportSettings]);
return (
<MinimalPageLayout>
<PageTitle subpage k="global.pages.migration" />
<CenterContainer>
<button onClick={doDownload} type="button">
Hello
</button>
</CenterContainer>
</MinimalPageLayout>
);
}

View File

@@ -0,0 +1,92 @@
import classNames from "classnames";
import { ReactNode } from "react";
import { useNavigate } from "react-router-dom";
import { Icon, Icons } from "@/components/Icon";
import { Heading2, Heading3, Paragraph } from "@/components/utils/Text";
export function Card(props: {
children?: React.ReactNode;
className?: string;
onClick?: () => void;
}) {
return (
<div
className={classNames(
{
"bg-onboarding-card duration-300 border border-onboarding-border rounded-lg p-7":
true,
"hover:bg-onboarding-cardHover transition-colors cursor-pointer":
!!props.onClick,
},
props.className,
)}
onClick={props.onClick}
>
{props.children}
</div>
);
}
export function CardContent(props: {
title: ReactNode;
description: ReactNode;
subtitle: ReactNode;
colorClass: string;
children?: React.ReactNode;
icon: Icons;
}) {
return (
<div className="grid grid-rows-[1fr,auto] h-full">
<div>
<Icon
icon={props.icon}
className={classNames("text-4xl mb-8 block", props.colorClass)}
/>
<Heading3
className={classNames(
"!mt-0 !mb-0 !text-xs uppercase",
props.colorClass,
)}
>
{props.subtitle}
</Heading3>
<Heading2 className="!mb-0 !mt-1 !text-base">{props.title}</Heading2>
<Paragraph className="max-w-[320px] !my-4">
{props.description}
</Paragraph>
</div>
<div>{props.children}</div>
</div>
);
}
export function Link(props: {
children?: React.ReactNode;
to?: string;
href?: string;
className?: string;
target?: "_blank";
}) {
const navigate = useNavigate();
return (
<a
onClick={() => {
if (props.to) navigate(props.to);
}}
href={props.href}
target={props.target}
className={classNames(
"text-onboarding-link cursor-pointer inline-flex gap-2 items-center group hover:opacity-75 transition-opacity",
props.className,
)}
rel="noreferrer"
>
{props.children}
<Icon
icon={Icons.ARROW_RIGHT}
className="group-hover:translate-x-0.5 transition-transform text-xl group-active:translate-x-0"
/>
</a>
);
}

View File

@@ -1,9 +1,9 @@
import classNames from "classnames";
import { Trans, useTranslation } from "react-i18next"; import { Trans, useTranslation } from "react-i18next";
import { Button } from "@/components/buttons/Button"; import { Button } from "@/components/buttons/Button";
import { Stepper } from "@/components/layout/Stepper"; import { Stepper } from "@/components/layout/Stepper";
import { CenterContainer } from "@/components/layout/ThinContainer"; import { CenterContainer } from "@/components/layout/ThinContainer";
import { VerticalLine } from "@/components/layout/VerticalLine";
import { Modal, ModalCard, useModal } from "@/components/overlays/Modal"; import { Modal, ModalCard, useModal } from "@/components/overlays/Modal";
import { Heading1, Heading2, Paragraph } from "@/components/utils/Text"; import { Heading1, Heading2, Paragraph } from "@/components/utils/Text";
import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout"; import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout";
@@ -13,20 +13,14 @@ import {
} from "@/pages/onboarding/onboardingHooks"; } from "@/pages/onboarding/onboardingHooks";
import { Card, CardContent, Link } from "@/pages/onboarding/utils"; import { Card, CardContent, Link } from "@/pages/onboarding/utils";
import { PageTitle } from "@/pages/parts/util/PageTitle"; import { PageTitle } from "@/pages/parts/util/PageTitle";
import { getProxyUrls } from "@/utils/proxyUrls";
function VerticalLine(props: { className?: string }) {
return (
<div className={classNames("w-full grid justify-center", props.className)}>
<div className="w-px h-10 bg-onboarding-divider" />
</div>
);
}
export function OnboardingPage() { export function OnboardingPage() {
const navigate = useNavigateOnboarding(); const navigate = useNavigateOnboarding();
const skipModal = useModal("skip"); const skipModal = useModal("skip");
const { completeAndRedirect } = useRedirectBack(); const { completeAndRedirect } = useRedirectBack();
const { t } = useTranslation(); const { t } = useTranslation();
const noProxies = getProxyUrls().length === 0;
return ( return (
<MinimalPageLayout> <MinimalPageLayout>
@@ -71,7 +65,9 @@ export function OnboardingPage() {
</Card> </Card>
<div className="hidden md:grid grid-rows-[1fr,auto,1fr] justify-center gap-4"> <div className="hidden md:grid grid-rows-[1fr,auto,1fr] justify-center gap-4">
<VerticalLine className="items-end" /> <VerticalLine className="items-end" />
<span className="text-xs uppercase font-bold">or</span> <span className="text-xs uppercase font-bold">
{t("onboarding.start.options.or")}
</span>
<VerticalLine /> <VerticalLine />
</div> </div>
<Card onClick={() => navigate("/onboarding/proxy")}> <Card onClick={() => navigate("/onboarding/proxy")}>
@@ -85,7 +81,8 @@ export function OnboardingPage() {
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
{noProxies ? null : (
<>
<p className="text-center hidden md:block mt-12"> <p className="text-center hidden md:block mt-12">
<Trans i18nKey="onboarding.start.options.default.text"> <Trans i18nKey="onboarding.start.options.default.text">
<br /> <br />
@@ -96,7 +93,6 @@ export function OnboardingPage() {
/> />
</Trans> </Trans>
</p> </p>
<div className=" max-w-[300px] mx-auto md:hidden mt-12 "> <div className=" max-w-[300px] mx-auto md:hidden mt-12 ">
<Button <Button
className="!text-type-text !bg-opacity-50" className="!text-type-text !bg-opacity-50"
@@ -111,6 +107,8 @@ export function OnboardingPage() {
</span> </span>
</Button> </Button>
</div> </div>
</>
)}
</CenterContainer> </CenterContainer>
</MinimalPageLayout> </MinimalPageLayout>
); );

View File

@@ -115,7 +115,7 @@ export function ExtensionStatus(props: {
</div> </div>
</Card> </Card>
{lastKnownStatus === "unknown" ? <RefreshBar /> : null} {lastKnownStatus === "unknown" ? <RefreshBar /> : null}
{props.showHelp ? ( {props.showHelp && props.status !== "success" ? (
<Card className="mt-4"> <Card className="mt-4">
<div className="flex items-center space-x-7"> <div className="flex items-center space-x-7">
<Icon icon={Icons.WARNING} className="text-type-danger text-2xl" /> <Icon icon={Icons.WARNING} className="text-type-danger text-2xl" />

View File

@@ -43,7 +43,7 @@ export function OnboardingProxyPage() {
throw new Error("onboarding.proxy.input.errorNotProxy"); throw new Error("onboarding.proxy.input.errorNotProxy");
setProxySet([url]); setProxySet([url]);
if (account) { if (account && backendUrl) {
await updateSettings(backendUrl, account, { await updateSettings(backendUrl, account, {
proxyUrls: [url], proxyUrls: [url],
}); });

View File

@@ -32,13 +32,21 @@ export function BackendTestPart() {
value: null, value: null,
}); });
if (!backendUrl) {
return setStatus({
hasTested: true,
success: false,
errorText: "Backend URL is not set",
value: null,
});
}
try { try {
const backendData = await getBackendMeta(backendUrl); const backendData = await getBackendMeta(backendUrl);
return setStatus({ return setStatus({
hasTested: true, hasTested: true,
success: true, success: true,
errorText: errorText: "",
"Failed to call backend, double check the URL key and your internet connection",
value: backendData, value: backendData,
}); });
} catch (err) { } catch (err) {
@@ -46,7 +54,7 @@ export function BackendTestPart() {
hasTested: true, hasTested: true,
success: false, success: false,
errorText: errorText:
"Failed to call backend, double check the URL key and your internet connection", "Failed to call backend, double check the URL, your internet connection, and ensure CORS is properly configured on your backend.",
value: null, value: null,
}); });
} }

View File

@@ -25,11 +25,11 @@ export function TMDBTestPart() {
errorText: "", errorText: "",
}); });
if (tmdbApiKey.length === 0) { if (!tmdbApiKey || tmdbApiKey.length === 0) {
return setStatus({ return setStatus({
hasTested: true, hasTested: true,
success: false, success: false,
errorText: "TMDB api key is not set", errorText: "TMDB API key is not set",
}); });
} }
const isJWT = tmdbApiKey.split(".").length > 2; const isJWT = tmdbApiKey.split(".").length > 2;
@@ -37,7 +37,7 @@ export function TMDBTestPart() {
return setStatus({ return setStatus({
hasTested: true, hasTested: true,
success: false, success: false,
errorText: "TMDB api key is not a read only key", errorText: "TMDB API key is not a read only key",
}); });
} }
@@ -48,7 +48,7 @@ export function TMDBTestPart() {
hasTested: true, hasTested: true,
success: false, success: false,
errorText: errorText:
"Failed to call tmdb, double check api key and your internet connection", "Failed to call TMDB, double check API key and your internet connection",
}); });
} }
@@ -61,7 +61,7 @@ export function TMDBTestPart() {
return ( return (
<> <>
<Heading2 className="mb-8 mt-12">TMDB tests</Heading2> <Heading2 className="mb-8 mt-12">TMDB test</Heading2>
<Box> <Box>
<div className="flex items-center"> <div className="flex items-center">
<div className="flex-1"> <div className="flex-1">

View File

@@ -52,14 +52,18 @@ export function WorkerTestPart() {
{ id: string; status: "error" | "success"; error?: Error }[] { id: string; status: "error" | "success"; error?: Error }[]
>([]); >([]);
const [buttonDisabled, setButtonDisabled] = useState(false);
const [testState, runTests] = useAsyncFn(async () => { const [testState, runTests] = useAsyncFn(async () => {
setButtonDisabled(true);
function updateWorker(id: string, data: (typeof workerState)[number]) { function updateWorker(id: string, data: (typeof workerState)[number]) {
setWorkerState((s) => { setWorkerState((s) => {
return [...s.filter((v) => v.id !== id), data]; return [...s.filter((v) => v.id !== id), data];
}); });
} }
setWorkerState([]); setWorkerState([]);
for (const worker of workerList) {
const workerPromises = workerList.map(async (worker) => {
try { try {
if (worker.url.endsWith("/")) { if (worker.url.endsWith("/")) {
updateWorker(worker.id, { updateWorker(worker.id, {
@@ -67,7 +71,7 @@ export function WorkerTestPart() {
status: "error", status: "error",
error: new Error("URL ends with slash"), error: new Error("URL ends with slash"),
}); });
continue; return;
} }
await singularProxiedFetch( await singularProxiedFetch(
worker.url, worker.url,
@@ -85,7 +89,10 @@ export function WorkerTestPart() {
error: err as Error, error: err as Error,
}); });
} }
} });
await Promise.all(workerPromises);
setTimeout(() => setButtonDisabled(false), 5000);
}, [workerList, setWorkerState]); }, [workerList, setWorkerState]);
return ( return (
@@ -112,7 +119,12 @@ export function WorkerTestPart() {
})} })}
<Divider /> <Divider />
<div className="flex justify-end"> <div className="flex justify-end">
<Button theme="purple" loading={testState.loading} onClick={runTests}> <Button
theme="purple"
loading={testState.loading}
onClick={buttonDisabled ? undefined : runTests}
disabled={buttonDisabled}
>
Test workers Test workers
</Button> </Button>
</div> </div>

View File

@@ -52,6 +52,9 @@ export function LoginFormPart(props: LoginFormPartProps) {
throw err; throw err;
} }
if (!account)
throw new Error(t("auth.login.validationError") ?? undefined);
await importData(account, progressItems, bookmarkItems); await importData(account, progressItems, bookmarkItems);
await restore(account); await restore(account);

View File

@@ -22,8 +22,12 @@ interface TrustBackendPartProps {
export function TrustBackendPart(props: TrustBackendPartProps) { export function TrustBackendPart(props: TrustBackendPartProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const backendUrl = useBackendUrl(); const backendUrl = useBackendUrl();
const hostname = useMemo(() => new URL(backendUrl).hostname, [backendUrl]); const hostname = useMemo(
() => (backendUrl ? new URL(backendUrl).hostname : undefined),
[backendUrl],
);
const result = useAsync(() => { const result = useAsync(() => {
if (!backendUrl) return Promise.resolve(null);
return getBackendMeta(backendUrl); return getBackendMeta(backendUrl);
}, [backendUrl]); }, [backendUrl]);
const { t } = useTranslation(); const { t } = useTranslation();
@@ -50,9 +54,10 @@ export function TrustBackendPart(props: TrustBackendPartProps) {
return ( return (
<LargeCard> <LargeCard>
<LargeCardText <LargeCardText
title={t("auth.trust.title")} title={hostname ? t("auth.trust.title") : t("auth.trust.noHostTitle")}
icon={<Icon icon={Icons.CIRCLE_EXCLAMATION} />} icon={<Icon icon={Icons.CIRCLE_EXCLAMATION} />}
> >
{hostname ? (
<Trans <Trans
i18nKey="auth.trust.host" i18nKey="auth.trust.host"
values={{ values={{
@@ -61,8 +66,13 @@ export function TrustBackendPart(props: TrustBackendPartProps) {
> >
<span className="text-white" /> <span className="text-white" />
</Trans> </Trans>
) : (
<p>{t("auth.trust.noHost")}</p>
)}
</LargeCardText> </LargeCardText>
{hostname ? (
<>
<div className="border border-authentication-border rounded-xl px-4 py-8 flex flex-col items-center space-y-2 my-8"> <div className="border border-authentication-border rounded-xl px-4 py-8 flex flex-col items-center space-y-2 my-8">
{cardContent} {cardContent}
</div> </div>
@@ -82,6 +92,14 @@ export function TrustBackendPart(props: TrustBackendPartProps) {
<MwLink to="/login">.</MwLink> <MwLink to="/login">.</MwLink>
</Trans> </Trans>
</p> </p>
</>
) : (
<LargeCardButtons>
<Button theme="purple" onClick={() => navigate("/")}>
{t("auth.trust.no")}
</Button>
</LargeCardButtons>
)}
</LargeCard> </LargeCard>
); );
} }

View File

@@ -47,6 +47,8 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) {
const [result, execute] = useAsyncFn( const [result, execute] = useAsyncFn(
async (inputMnemonic: string) => { async (inputMnemonic: string) => {
if (!backendUrl)
throw new Error(t("auth.verify.noBackendUrl") ?? undefined);
if (!props.mnemonic || !props.userData) if (!props.mnemonic || !props.userData)
throw new Error(t("auth.verify.invalidData") ?? undefined); throw new Error(t("auth.verify.invalidData") ?? undefined);
@@ -68,6 +70,9 @@ export function VerifyPassphrase(props: VerifyPassphraseProps) {
recaptchaToken, recaptchaToken,
}); });
if (!account)
throw new Error(t("auth.verify.registrationFailed") ?? undefined);
await importData(account, progressItems, bookmarkItems); await importData(account, progressItems, bookmarkItems);
await updateSettings(backendUrl, account, { await updateSettings(backendUrl, account, {

View File

@@ -1,5 +1,4 @@
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import Sticky from "react-sticky-el"; import Sticky from "react-sticky-el";
import { useWindowSize } from "react-use"; import { useWindowSize } from "react-use";
@@ -26,7 +25,6 @@ function getTimeOfDay(date: Date): "night" | "morning" | "day" {
export function HeroPart({ setIsSticky, searchParams }: HeroPartProps) { export function HeroPart({ setIsSticky, searchParams }: HeroPartProps) {
const { t: randomT } = useRandomTranslation(); const { t: randomT } = useRandomTranslation();
const { t } = useTranslation();
const [search, setSearch, setSearchUnFocus] = searchParams; const [search, setSearch, setSearchUnFocus] = searchParams;
const [, setShowBg] = useState(false); const [, setShowBg] = useState(false);
const bannerSize = useBannerSize(); const bannerSize = useBannerSize();
@@ -54,7 +52,7 @@ export function HeroPart({ setIsSticky, searchParams }: HeroPartProps) {
const time = getTimeOfDay(new Date()); const time = getTimeOfDay(new Date());
const title = randomT(`home.titles.${time}`); const title = randomT(`home.titles.${time}`);
const placeholder = randomT(`home.search.placeholder`);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
useSlashFocus(inputRef); useSlashFocus(inputRef);
@@ -77,7 +75,7 @@ export function HeroPart({ setIsSticky, searchParams }: HeroPartProps) {
onChange={setSearch} onChange={setSearch}
value={search} value={search}
onUnFocus={setSearchUnFocus} onUnFocus={setSearchUnFocus}
placeholder={t("home.search.placeholder") ?? ""} placeholder={placeholder ?? ""}
/> />
</Sticky> </Sticky>
</div> </div>

View File

@@ -18,7 +18,7 @@ export function AccountActionsPart() {
const deleteModal = useModal("account-delete"); const deleteModal = useModal("account-delete");
const [deleteResult, deleteExec] = useAsyncFn(async () => { const [deleteResult, deleteExec] = useAsyncFn(async () => {
if (!account) return; if (!account || !url) return;
await deleteUser(url, account); await deleteUser(url, account);
await logout(); await logout();
deleteModal.hide(); deleteModal.hide();

View File

@@ -92,6 +92,16 @@ export function CaptionsPart(props: {
value={props.styling.backgroundOpacity * 100} value={props.styling.backgroundOpacity * 100}
textTransformer={(s) => `${s}%`} textTransformer={(s) => `${s}%`}
/> />
<CaptionSetting
label={t("settings.subtitles.backgroundBlurLabel")}
max={100}
min={0}
onChange={(v) =>
props.setStyling({ ...props.styling, backgroundBlur: v / 100 })
}
value={props.styling.backgroundBlur * 100}
textTransformer={(s) => `${s}%`}
/>
<CaptionSetting <CaptionSetting
label={t("settings.subtitles.textSizeLabel")} label={t("settings.subtitles.textSizeLabel")}
max={200} max={200}

View File

@@ -55,7 +55,7 @@ function ProxyEdit({ proxyUrls, setProxyUrls }: ProxyEditProps) {
</p> </p>
<p className="max-w-[20rem] font-medium"> <p className="max-w-[20rem] font-medium">
<Trans i18nKey="settings.connections.workers.description"> <Trans i18nKey="settings.connections.workers.description">
<MwLink to="https://docs.movie-web.app/proxy/deploy"> <MwLink to="https://movie-web.github.io/docs/proxy/deploy">
Proxy documentation Proxy documentation
</MwLink> </MwLink>
</Trans> </Trans>
@@ -125,7 +125,7 @@ function BackendEdit({ backendUrl, setBackendUrl }: BackendEditProps) {
</p> </p>
<p className="max-w-[20rem] font-medium"> <p className="max-w-[20rem] font-medium">
<Trans i18nKey="settings.connections.server.description"> <Trans i18nKey="settings.connections.server.description">
<MwLink to="https://docs.movie-web.app/backend/deploy"> <MwLink to="https://movie-web.github.io/docs/backend/deploy">
Backend documentation Backend documentation
</MwLink> </MwLink>
</Trans> </Trans>

View File

@@ -24,6 +24,7 @@ export function Device(props: {
const token = useAuthStore((s) => s.account?.token); const token = useAuthStore((s) => s.account?.token);
const [result, exec] = useAsyncFn(async () => { const [result, exec] = useAsyncFn(async () => {
if (!token) throw new Error("No token present"); if (!token) throw new Error("No token present");
if (!url) throw new Error("No backend set");
await removeSession(url, token, props.id); await removeSession(url, token, props.id);
props.onRemove?.(); props.onRemove?.();
}, [url, token, props.id]); }, [url, token, props.id]);

View File

@@ -14,9 +14,9 @@ import { useAuthStore } from "@/stores/auth";
const rem = 16; const rem = 16;
function SecureBadge(props: { url: string }) { function SecureBadge(props: { url: string | null }) {
const { t } = useTranslation(); const { t } = useTranslation();
const secure = props.url.startsWith("https://"); const secure = props.url ? props.url.startsWith("https://") : false;
return ( return (
<div className="flex items-center gap-1 -mx-1 ml-3 px-1 rounded bg-largeCard-background font-bold"> <div className="flex items-center gap-1 -mx-1 ml-3 px-1 rounded bg-largeCard-background font-bold">
<Icon icon={secure ? Icons.LOCK : Icons.UNLOCK} /> <Icon icon={secure ? Icons.LOCK : Icons.UNLOCK} />
@@ -68,6 +68,7 @@ export function SidebarPart() {
const backendUrl = useBackendUrl(); const backendUrl = useBackendUrl();
const backendMeta = useAsync(async () => { const backendMeta = useAsync(async () => {
if (!backendUrl) return;
return getBackendMeta(backendUrl); return getBackendMeta(backendUrl);
}, [backendUrl]); }, [backendUrl]);
@@ -159,7 +160,7 @@ export function SidebarPart() {
<SecureBadge url={backendUrl} /> <SecureBadge url={backendUrl} />
</div> </div>
<p className="text-white"> <p className="text-white">
{backendUrl.replace(/https?:\/\//, "")} {backendUrl?.replace(/https?:\/\//, "") ?? "—"}
</p> </p>
</div> </div>

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