Compare commits

...

155 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
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
76 changed files with 3104 additions and 308 deletions

View File

@@ -9,8 +9,36 @@ COPY pnpm-lock.yaml ./
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 . ./
RUN pnpm run build

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

94
pnpm-lock.yaml generated
View File

@@ -22,8 +22,8 @@ dependencies:
specifier: ^1.0.3
version: 1.0.3
'@movie-web/providers':
specifier: ^2.2.0
version: 2.2.0
specifier: ^2.2.3
version: 2.2.3
'@noble/hashes':
specifier: ^1.3.3
version: 1.3.3
@@ -67,8 +67,8 @@ dependencies:
specifier: ^7.0.0
version: 7.0.0
hls.js:
specifier: ^1.4.14
version: 1.4.14
specifier: ^1.5.7
version: 1.5.7
i18next:
specifier: ^23.7.11
version: 23.7.11
@@ -81,6 +81,9 @@ dependencies:
lodash.isequal:
specifier: ^4.5.0
version: 4.5.0
lodash.merge:
specifier: ^4.6.2
version: 4.6.2
million:
specifier: ^2.6.4
version: 2.6.4
@@ -135,6 +138,9 @@ dependencies:
subsrt-ts:
specifier: ^2.1.2
version: 2.1.2
zod:
specifier: ^3.22.4
version: 3.22.4
zustand:
specifier: ^4.4.7
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':
specifier: ^4.5.8
version: 4.5.8
'@types/lodash.merge':
specifier: ^4.6.9
version: 4.6.9
'@types/lodash.throttle':
specifier: ^4.1.9
version: 4.1.9
@@ -268,7 +277,7 @@ devDependencies:
version: 0.5.9(prettier@3.1.1)
rollup-plugin-visualizer:
specifier: ^5.11.0
version: 5.11.0(@rollup/wasm-node@4.12.0)
version: 5.11.0(@rollup/wasm-node@4.13.0)
tailwind-scrollbar:
specifier: ^3.0.5
version: 3.0.5(tailwindcss@3.4.0)
@@ -1936,8 +1945,8 @@ packages:
engines: {node: '>= 14'}
dev: false
/@movie-web/providers@2.2.0:
resolution: {integrity: sha512-7rKUpLPklwOtS5P2CAeh0P3sPIuYvtkKIgm0kVMp+OsSpKd9IcuYm79bbDrA0MDi3IMGik1W6la9Mzy91+8uYQ==}
/@movie-web/providers@2.2.3:
resolution: {integrity: sha512-0axy02Zzlk7Tvtalc/Ebv9u5vzUPVWmQm0Ts5+6l6KPU41JUdLnFgmOl0yf0lbNeHRNSTx5SDlvWcYNL8rgpyA==}
dependencies:
cheerio: 1.0.0-rc.12
cookie: 0.6.0
@@ -2062,7 +2071,7 @@ packages:
engines: {node: '>=14.0.0'}
dev: false
/@rollup/plugin-babel@5.3.1(@babel/core@7.23.6)(@rollup/wasm-node@4.12.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==}
engines: {node: '>= 10.0.0'}
peerDependencies:
@@ -2075,36 +2084,36 @@ packages:
dependencies:
'@babel/core': 7.23.6
'@babel/helper-module-imports': 7.22.15
'@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.12.0)
rollup: /@rollup/wasm-node@4.12.0
'@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.13.0)
rollup: /@rollup/wasm-node@4.13.0
dev: true
/@rollup/plugin-node-resolve@11.2.1(@rollup/wasm-node@4.12.0):
/@rollup/plugin-node-resolve@11.2.1(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==}
engines: {node: '>= 10.0.0'}
peerDependencies:
rollup: npm:@rollup/wasm-node
dependencies:
'@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.12.0)
'@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.13.0)
'@types/resolve': 1.17.1
builtin-modules: 3.3.0
deepmerge: 4.3.1
is-module: 1.0.0
resolve: 1.22.4
rollup: /@rollup/wasm-node@4.12.0
rollup: /@rollup/wasm-node@4.13.0
dev: true
/@rollup/plugin-replace@2.4.2(@rollup/wasm-node@4.12.0):
/@rollup/plugin-replace@2.4.2(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==}
peerDependencies:
rollup: npm:@rollup/wasm-node
dependencies:
'@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.12.0)
'@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.13.0)
magic-string: 0.25.9
rollup: /@rollup/wasm-node@4.12.0
rollup: /@rollup/wasm-node@4.13.0
dev: true
/@rollup/pluginutils@3.1.0(@rollup/wasm-node@4.12.0):
/@rollup/pluginutils@3.1.0(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
engines: {node: '>= 8.0.0'}
peerDependencies:
@@ -2113,11 +2122,11 @@ packages:
'@types/estree': 0.0.39
estree-walker: 1.0.1
picomatch: 2.3.1
rollup: /@rollup/wasm-node@4.12.0
rollup: /@rollup/wasm-node@4.13.0
dev: true
/@rollup/wasm-node@4.12.0:
resolution: {integrity: sha512-sqy3+YvV/uWX6bPZOR5PlEdH6xyMPXoelllRQ/uZ13tzy9f4pXZTbajnoWN8IHHXwTNKPiLzsePLiDEVmkxMNw==}
/@rollup/wasm-node@4.13.0:
resolution: {integrity: sha512-oFX11wzU7RTaiW06WBtRpzIVN/oaG0I3XkevNO0brBklYnY9zpLhTfksN4b+TdBt6CfXV/KdVhdWLbb0fQIR7A==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
dependencies:
@@ -2262,17 +2271,23 @@ packages:
/@types/lodash.isequal@4.5.8:
resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==}
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
/@types/lodash.throttle@4.1.9:
resolution: {integrity: sha512-PCPVfpfueguWZQB7pJQK890F2scYKoDUL3iM522AptHWn7d5NQmeS/LTEHIcLr5PaTzl3dK2Z0xSUHHTHwaL5g==}
dependencies:
'@types/lodash': 4.14.197
'@types/lodash': 4.14.202
dev: true
/@types/lodash@4.14.197:
resolution: {integrity: sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==}
/@types/lodash@4.14.202:
resolution: {integrity: sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==}
dev: true
/@types/node-forge@1.3.10:
@@ -4376,8 +4391,8 @@ packages:
function-bind: 1.1.2
dev: true
/hls.js@1.4.14:
resolution: {integrity: sha512-UppQjyvPVclg+6t2KY/Rv03h0+bA5u6zwqVoz4LAC/L0fgYmIaCD7ZCrwe8WI1Gv01be1XL0QFsRbSdIHV/Wbw==}
/hls.js@1.5.7:
resolution: {integrity: sha512-Hnyf7ojTBtXHeOW1/t6wCBJSiK1WpoKF9yg7juxldDx8u3iswrkPt2wbOA/1NiwU4j27DSIVoIEJRAhcdMef/A==}
dev: false
/hoist-non-react-statics@3.3.2:
@@ -5004,7 +5019,6 @@ packages:
/lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
dev: true
/lodash.mergewith@4.6.2:
resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
@@ -5101,7 +5115,7 @@ packages:
'@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.6)
'@babel/types': 7.23.6
kleur: 4.1.5
rollup: /@rollup/wasm-node@4.12.0
rollup: /@rollup/wasm-node@4.13.0
unplugin: 1.5.1
transitivePeerDependencies:
- supports-color
@@ -6029,7 +6043,7 @@ packages:
glob: 7.2.3
dev: true
/rollup-plugin-terser@7.0.2(@rollup/wasm-node@4.12.0):
/rollup-plugin-terser@7.0.2(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==}
deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser
peerDependencies:
@@ -6037,12 +6051,12 @@ packages:
dependencies:
'@babel/code-frame': 7.23.5
jest-worker: 26.6.2
rollup: /@rollup/wasm-node@4.12.0
rollup: /@rollup/wasm-node@4.13.0
serialize-javascript: 4.0.0
terser: 5.19.3
dev: true
/rollup-plugin-visualizer@5.11.0(@rollup/wasm-node@4.12.0):
/rollup-plugin-visualizer@5.11.0(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-exM0Ms2SN3AgTzMeW7y46neZQcyLY7eKwWAop1ZoRTCZwyrIRdMMJ6JjToAJbML77X/9N8ZEpmXG4Z/Clb9k8g==}
engines: {node: '>=14'}
hasBin: true
@@ -6054,7 +6068,7 @@ packages:
dependencies:
open: 8.4.2
picomatch: 2.3.1
rollup: /@rollup/wasm-node@4.12.0
rollup: /@rollup/wasm-node@4.13.0
source-map: 0.7.4
yargs: 17.7.2
dev: true
@@ -7040,7 +7054,7 @@ packages:
'@types/node': 20.10.5
esbuild: 0.19.10
postcss: 8.4.32
rollup: /@rollup/wasm-node@4.12.0
rollup: /@rollup/wasm-node@4.13.0
optionalDependencies:
fsevents: 2.3.3
dev: true
@@ -7302,9 +7316,9 @@ packages:
'@babel/core': 7.23.6
'@babel/preset-env': 7.23.6(@babel/core@7.23.6)
'@babel/runtime': 7.23.6
'@rollup/plugin-babel': 5.3.1(@babel/core@7.23.6)(@rollup/wasm-node@4.12.0)
'@rollup/plugin-node-resolve': 11.2.1(@rollup/wasm-node@4.12.0)
'@rollup/plugin-replace': 2.4.2(@rollup/wasm-node@4.12.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.13.0)
'@rollup/plugin-replace': 2.4.2(@rollup/wasm-node@4.13.0)
'@surma/rollup-plugin-off-main-thread': 2.2.3
ajv: 8.12.0
common-tags: 1.8.2
@@ -7313,8 +7327,8 @@ packages:
glob: 7.2.3
lodash: 4.17.21
pretty-bytes: 5.6.0
rollup: /@rollup/wasm-node@4.12.0
rollup-plugin-terser: 7.0.2(@rollup/wasm-node@4.12.0)
rollup: /@rollup/wasm-node@4.13.0
rollup-plugin-terser: 7.0.2(@rollup/wasm-node@4.13.0)
source-map: 0.8.0-beta.0
stringify-object: 3.3.0
strip-comments: 2.0.1
@@ -7515,6 +7529,10 @@ packages:
engines: {node: '>=12.20'}
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):
resolution: {integrity: sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==}
engines: {node: '>=12.7.0'}

View File

@@ -4,7 +4,7 @@ window.__CONFIG__ = {
VITE_CORS_PROXY_URL: "",
// 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
VITE_DMCA_EMAIL: null,
@@ -16,5 +16,5 @@ window.__CONFIG__ = {
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>"
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 bn from "@/assets/locales/bn.json";
import ca from "@/assets/locales/ca.json";
import caVl from "@/assets/locales/ca@valencia.json";
import cs from "@/assets/locales/cs.json";
import de from "@/assets/locales/de.json";
import el from "@/assets/locales/el.json";
@@ -46,6 +47,7 @@ import zh from "@/assets/locales/zh.json";
export const locales = {
en,
ca,
"ca-ES": caVl,
cs,
de,
fr,
@@ -80,7 +82,7 @@ export const locales = {
ko,
sl,
ta,
"zh-HANT": zhhant,
"zh-Hant": zhhant,
is,
ru,
gl,

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.",
"faqTitle": "Preguntes freqüents",
"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?"
},
"q2": {
@@ -57,6 +57,8 @@
},
"host": "Us esteu connectant 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 aquest servidor?",
"yes": "Confie en aquest servidor"
},
@@ -95,6 +97,7 @@
"about": "Quant a",
"dmca": "DMCA",
"login": "Inicia sessió",
"onboarding": "Configura",
"pagetitle": "{{title}} - movie-web",
"register": "Registra",
"settings": "Configuració"
@@ -117,7 +120,12 @@
"noResults": "No hem pogut trobar res!",
"placeholder": {
"default": "Què voleu mirar?",
"extra": []
"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"
},
@@ -130,11 +138,15 @@
},
"morning": {
"default": "Què us agradaria mirar aquest matí?",
"extra": ["He sentit que «Abans de l'alba» és bona"]
"extra": [
"He sentit que «Abans de l'alba» és bona"
]
},
"night": {
"default": "Què us agradaria mirar aquesta nit?",
"extra": ["Esteu cansat? He sentit que «L'exorcista» és bona."]
"extra": [
"Esteu cansat? He sentit que «L'exorcista» és bona."
]
}
}
},
@@ -164,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.",
"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": {
"close": "Tanca"
},
@@ -177,10 +252,11 @@
},
"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. 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": {
"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",
@@ -261,6 +337,17 @@
"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": "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": {
"badge": "Ha fallat",
"homeButton": "Vés a l'inici",
@@ -392,16 +479,46 @@
"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>",
"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>",
"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 aquesta opció per a generar-les sobre la marxa, però poden alentir el vídeo.",
"thumbnailLabel": "Genera miniatures",
"title": "Configuració"
},
"reset": "Restableix",
"save": "Desa",
"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",
"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?",
"yes": "Věřím tomuto serveru"
},
@@ -118,22 +120,33 @@
"noResults": "Nemohli jsme nic najít!",
"placeholder": {
"default": "Co si přejete sledovat?",
"extra": []
"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í"
},
"titles": {
"day": {
"default": "Na co byste se chtěli dnes odpoledne dívat?",
"extra": ["Chceš zažít dobrodružství? Jurský Park je pro tebe."]
"extra": [
"Chceš zažít dobrodružství? Jurský Park je pro tebe."
]
},
"morning": {
"default": "Na co byste se chtěli dnes ráno dívat?",
"extra": ["Slyšel jsem, že Před úsvitem je super."]
"extra": [
"Slyšel jsem, že Před úsvitem je super."
]
},
"night": {
"default": "Na co byste se chtěli dnes večer dívat?",
"extra": ["Unaven? Slyšel jsem, že Vymítač ďábla je super."]
"extra": [
"Unaven? Slyšel jsem, že Vymítač ďábla je super."
]
}
}
},
@@ -173,7 +186,11 @@
"extension": {
"back": "Zpět",
"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.",
"linkChrome": "Instalovat rozšíření pro Chrome",
"linkFirefox": "Instalovat rozšíření pro Firefox",
"notDetectingAction": "Obnovit stránku",
"status": {
"disallowed": "Rozšíření není pro tuto stránku povoleno",
"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",
"no": "Zurück",
"noHost": "Da der Server nicht eingerichtet wurde, kannst du keinen Account erstellen",
"noHostTitle": "Server nicht eingerichtet!",
"title": "Vertraust du diesem Server?",
"yes": "Ich vertraue diesem Server"
},
@@ -118,7 +120,12 @@
"noResults": "Wir haben nichts gefunden!",
"placeholder": {
"default": "Was möchtest du schauen?",
"extra": []
"extra": [
"Was möchtest du entdecken?",
"Was ist auf deiner Merkliste?",
"Was ist dein Lieblingsfilm?",
"Was ist deine Lieblingsserie?"
]
},
"sectionTitle": "Suchergebnisse"
},
@@ -131,11 +138,15 @@
},
"morning": {
"default": "Was würdest du diesen Morgen gerne schauen?",
"extra": ["Before Sunrise soll gut sein"]
"extra": [
"Before Sunrise soll gut sein"
]
},
"night": {
"default": "Was möchtest du diesen Abend gerne schauen?",
"extra": ["Müde? Ich hab gehört The Exorcist soll gut sein."]
"extra": [
"Müde? Ich hab gehört The Exorcist soll gut sein."
]
}
}
},
@@ -241,6 +252,7 @@
},
"menus": {
"downloads": {
"copyHlsPlaylist": "HLS-Playlist Link kopieren",
"disclaimer": "Videos werden direkt vom Provider heruntergeladen. movie-web hat keinen Einfluss darauf, wie Downloads bereitgestellt werden.",
"downloadSubtitle": "Aktuelle Untertitel herunterladen",
"downloadVideo": "Video herunterladen",
@@ -524,6 +536,7 @@
}
},
"subtitles": {
"backgroundBlurLabel": "Hintergrundunschärfe",
"backgroundLabel": "Hintergrunddeckkraft",
"colorLabel": "Farbe",
"previewQuote": "Das Gras wächst nicht schneller, wenn man daran zieht.",

View File

@@ -100,7 +100,8 @@
"onboarding": "Setup",
"pagetitle": "{{title}} - movie-web",
"register": "Register",
"settings": "Settings"
"settings": "Settings",
"migration": "Migration"
}
},
"home": {
@@ -153,6 +154,27 @@
"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": {
"banner": {
"offline": "Check your internet connection"
@@ -216,6 +238,7 @@
"start": {
"explainer": "To get the best streams possible, you will need to choose which streaming method you want to use.",
"options": {
"or": "or",
"default": {
"text": "I don't want good quality streams,<0 /> <1>use the default setup</1>"
},
@@ -533,6 +556,7 @@
},
"subtitles": {
"backgroundLabel": "Background opacity",
"backgroundBlurLabel": "Background blur",
"colorLabel": "Color",
"previewQuote": "I must not fear. Fear is the mind-killer.",
"textSizeLabel": "Text size",

View File

@@ -12,7 +12,7 @@
},
"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.",
"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"
},
@@ -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",
"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?",
"yes": "Confío en este servidor"
},
@@ -118,7 +120,12 @@
"noResults": "¡No pudimos encontrar nada!",
"placeholder": {
"default": "¿Qué te gustaría ver?",
"extra": []
"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"
},
@@ -131,11 +138,15 @@
},
"morning": {
"default": "¿Qué te gustaría ver esta mañana?",
"extra": ["Escuché que “Antes del amanecer” es buena"]
"extra": [
"Escuché que “Antes del amanecer” es buena"
]
},
"night": {
"default": "¿Qué te gustaría ver esta noche?",
"extra": ["¿Cansado? Escuché que “El Exorcista” es buena."]
"extra": [
"¿Cansado? Escuché que “El Exorcista” es buena."
]
}
}
},
@@ -170,7 +181,7 @@
"cancel": "Cancelar",
"confirm": "Usar configuración por defecto",
"description": "La configuración predeterminada no tiene las mejores transmisiones y puede ser insoportablemente lenta.",
"title": "Estás seguro?"
"title": "¿Estás seguro?"
},
"extension": {
"back": "Volver atrás",
@@ -241,6 +252,7 @@
},
"menus": {
"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.",
"downloadSubtitle": "Descargar subtítulo actual",
"downloadVideo": "Descargar vídeo",
@@ -387,7 +399,7 @@
"shortRemaining": "-{{timeLeft}}"
},
"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.",
"title": "Necesitamos verificar que eres humano.",
"verifyingHumanity": "Verificando tu hunanidad…"
@@ -482,7 +494,7 @@
"redoSetup": "Rehacer configuración",
"successStatus": {
"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": {
"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",
"no": "Tagasi",
"noHost": "Server ei ole konfigureeritud, seega ei saa kontot luua",
"noHostTitle": "Server ei ole konfigureeritud!",
"title": "Kas usaldate seda serverit?",
"yes": "Usaldan seda serverit"
},
@@ -118,7 +120,12 @@
"noResults": "Me ei leidnud midagi!",
"placeholder": {
"default": "Mida tahate vaadata?",
"extra": []
"extra": [
"Mida soovite uurida?",
"Mis on teie nimekirjas?",
"Milline on teie lemmikfilm?",
"Milline on teie lemmiksari?"
]
},
"sectionTitle": "Otsingutulemused"
},
@@ -131,11 +138,15 @@
},
"morning": {
"default": "Mida te soovite täna hommikul vaadata?",
"extra": ["Ma kuulsin, et Before Sunrise on hea"]
"extra": [
"Ma kuulsin, et Before Sunrise on hea"
]
},
"night": {
"default": "Mida te soovite täna õhtul vaadata?",
"extra": ["Väsinud? Olen kuulnud, et The Exorcist on hea."]
"extra": [
"Väsinud? Olen kuulnud, et The Exorcist on hea."
]
}
}
},
@@ -241,6 +252,7 @@
},
"menus": {
"downloads": {
"copyHlsPlaylist": "Kopeeri HLS esitusloendi link",
"disclaimer": "Allalaadimine toimub otse teenusepakkujalt. movie-web ei saa kontrollida, kuidas allalaadimine toimub.",
"downloadSubtitle": "Laadige alla praegune subtiiter",
"downloadVideo": "Lae alla video",
@@ -524,6 +536,7 @@
}
},
"subtitles": {
"backgroundBlurLabel": "Tausta hägusus",
"backgroundLabel": "Tausta läbipaistmatus",
"colorLabel": "Värv",
"previewQuote": "Ma ei tohi karta. Hirm on meelemõrvar.",

View File

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

View File

@@ -7,12 +7,12 @@
"title": "D'où vient le contenu ?"
},
"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 ?"
},
"q3": {
"body": "Que nos sources soient propriétaires du contenu ou non, The Movie Database (TMDB) fournit et affiche nos résultats de recherche.",
"title": "Les résultats de la recherche affichent l'émission ou le film, pourquoi ne puis-je pas le lire ?"
"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 la série ou le film, pourquoi ne puis-je pas le lire ?"
},
"title": "À propos de movie-web"
},
@@ -27,27 +27,27 @@
"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",
"next": "J'ai sauvegardé ma passphrase",
"passphraseFrameLabel": "Pass phrase",
"passphraseFrameLabel": "Passphrase",
"title": "Votre passphrase"
},
"hasAccount": "Avez-vous déjà un compte? <0>Connectez-vous ici.</0>",
"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",
"passphraseLabel": "Passphrase de 12 mots",
"passphrasePlaceholder": "Phrase secrète",
"passphrasePlaceholder": "Passphrase",
"submit": "Se connecter",
"title": "Se connecter à votre compte",
"validationError": "Passphrase incorrecte ou incomplete"
"title": "Connectez-vous à votre compte",
"validationError": "Passphrase incorrecte ou incomplète"
},
"register": {
"information": {
"color1": "Couleur de profile un",
"color2": "Couleur de profile deux",
"header": "Entrez un nom pour votre appareil et choisissez une couleur de profile ainsi qu'une icône d'utilisateur de votre choix",
"color1": "Première couleur de profil",
"color2": "Seconde couleur de profil",
"header": "Veuillez entrer un nom pour votre appareil, choisir une couleur et une icône utilisateur de votre choix",
"icon": "Icône d'utilisateur",
"next": "Prochain",
"title": "Informations sur le compte"
"next": "Suivant",
"title": "Informations du compte"
}
},
"trust": {
@@ -55,8 +55,10 @@
"text": "L'avez-vous configuré correctement ?",
"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",
"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 ?",
"yes": "Je fais confiance à ce serveur"
},
@@ -65,13 +67,13 @@
"invalidData": "Les données ne sont pas valides",
"noMatch": "La passphrase ne correspond pas",
"passphraseLabel": "Votre passphrase de 12 mots",
"recaptchaFailed": "La validation ReCaptcha a échoué",
"recaptchaFailed": "La validation ReCaptcha a échouée",
"register": "Créer un compte",
"title": "Confirmez votre passphrase"
"title": "Resaisissez votre passphrase"
}
},
"errors": {
"badge": "Il s'est cassé",
"badge": "Tout est cassé",
"details": "Détails de l'erreur",
"reloadPage": "Actualiser la page",
"showError": "Afficher les détails de l'erreur",
@@ -87,7 +89,7 @@
"dmca": "DMCA",
"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": {
"name": "movie-web",
@@ -95,7 +97,7 @@
"about": "À propos",
"dmca": "DMCA",
"login": "Se connecter",
"onboarding": "Setup",
"onboarding": "Mise en place",
"pagetitle": "{{title}} - movie-web",
"register": "Créer un compte",
"settings": "Paramètres"
@@ -106,7 +108,7 @@
"sectionTitle": "Favoris"
},
"continueWatching": {
"sectionTitle": "Continuer le visionnage"
"sectionTitle": "Reprendre la lecture"
},
"mediaList": {
"stopEditing": "Arrêter l'édition"
@@ -118,7 +120,12 @@
"noResults": "Nous n'avons rien trouvé!",
"placeholder": {
"default": "Que voulez-vous voir?",
"extra": []
"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"
},
@@ -131,7 +138,9 @@
},
"morning": {
"default": "Que voulez-vous regarder ce matin ?",
"extra": ["Les films, c'est comme les voyages : ça nous ouvre l'esprit"]
"extra": [
"Les films, c'est comme les voyages : ça nous ouvre l'esprit"
]
},
"night": {
"default": "Que voulez-vous regarder ce soir ?",
@@ -163,21 +172,21 @@
},
"notFound": {
"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.",
"title": "Impossible de trouver cette page"
},
"onboarding": {
"defaultConfirm": {
"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.",
"title": "Es-tu sûr?"
"title": "Êtes-vous sûr ?"
},
"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.",
"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.",
"linkChrome": "Installer l'extension Chrome",
"linkFirefox": "Installer l'extension Firefox",
@@ -187,7 +196,7 @@
"disallowed": "L'extension n'est pas activée pour cette page",
"disallowedAction": "Activer l'extension",
"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",
"success": "L'extension fonctionne comme prévu!"
},
@@ -195,7 +204,7 @@
"title": "Commençons par une extension"
},
"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.",
"input": {
"errorConnection": "Impossible de se connecter au proxy",
@@ -205,18 +214,18 @@
"placeholder": "https://"
},
"link": "Apprenez à créer un proxy",
"submit": "Soumettre le proxy",
"submit": "Valider le proxy",
"title": "Créons un nouveau proxy"
},
"start": {
"explainer": "Pour obtenir les meilleurs flux possibles, vous devrez choisir la méthode de streaming que vous souhaitez utiliser.",
"options": {
"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": {
"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é",
"title": "Extension du navigateur"
},
@@ -227,7 +236,7 @@
"title": "Proxy personnalisé"
}
},
"title": "Commençons par vous configurer movie-web"
"title": "Commençons par configurer movie-web"
}
},
"overlays": {
@@ -235,14 +244,15 @@
},
"player": {
"back": {
"default": "Retour à la page d'accueil",
"default": "Revenir à la page d'accueil",
"short": "Retour"
},
"casting": {
"enabled": "Casting à l'appareil..."
"enabled": "Casting vers l'appareil..."
},
"menus": {
"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.",
"downloadSubtitle": "Télécharger les sous-titres",
"downloadVideo": "Télécharger la vidéo",
@@ -266,9 +276,9 @@
},
"episodes": {
"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}}",
"loadingError": "Erreur de chargement de la saison",
"loadingError": "Erreur lors du chargement de la saison",
"loadingList": "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."
@@ -279,7 +289,7 @@
},
"quality": {
"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.",
"title": "Qualité"
},
@@ -303,7 +313,7 @@
"title": "Pas d'embeds trouvés"
},
"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": "Sources",
@@ -346,7 +356,7 @@
},
"notFound": {
"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.",
"title": "Impossible de trouver ce média."
}
@@ -364,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.",
"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.",
"title": "Oups, c'est coupé !"
},
@@ -377,7 +387,7 @@
"notFound": {
"badge": "Non trouvé",
"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.",
"title": "Nous n'avons pas trouvé cela"
}
@@ -409,7 +419,7 @@
"textWithReset": "Echec du chargement de votre profil à partir de votre serveur personnalisé, souhaitez-vous revenir au serveur par défaut ?"
},
"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."
}
},
@@ -435,18 +445,18 @@
"devices": {
"deviceNameLabel": "Nom de l'appareil",
"failed": "Échec du chargement des sessions",
"removeDevice": "Enlever",
"removeDevice": "Supprimer",
"title": "Appareils"
},
"profile": {
"finish": "Terminer l'édition",
"firstColor": "Couleur de profil un",
"secondColor": "Couleur de profil deux",
"title": "Éditer la photo de profil",
"firstColor": "Première couleur de profil",
"secondColor": "Seconde couleur de profil",
"title": "Modifier la photo de profil",
"userIcon": "Icône de l'utilisateur"
},
"register": {
"cta": "Commencer",
"cta": "Démarrer",
"text": "Partagez la progression de vos films et séries entre vos appareils et gardez-les synchronisés.",
"title": "Synchroniser au Cloud"
},
@@ -470,12 +480,12 @@
"urlLabel": "URL du serveur personnalisé"
},
"setup": {
"doSetup": "Faire la configuration",
"doSetup": "Configurer",
"errorStatus": {
"description": "Il semble qu'un ou plusieurs éléments de cette configuration nécessitent 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": {
"default": "Configuration par défaut",
"extension": "Extension",
@@ -488,7 +498,7 @@
},
"unsetStatus": {
"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",
@@ -515,9 +525,9 @@
"info": {
"appVersion": "Version de l'application",
"backendUrl": "URL de Backend",
"backendVersion": "Version de la Backend",
"backendVersion": "Version du Backend",
"hostname": "Nom d'hôte",
"insecure": "Insécure",
"insecure": "Non sécurisé",
"notLoggedIn": "Vous n'êtes pas connecté",
"secure": "Sécurisé",
"title": "Informations sur l'application",
@@ -526,6 +536,7 @@
}
},
"subtitles": {
"backgroundBlurLabel": "Flou d'arrière-plan",
"backgroundLabel": "Opacité du fond",
"colorLabel": "Couleur",
"previewQuote": "Plus l'obscurité est profonde, plus la lumière brille.",

View File

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

View File

@@ -1,9 +1,9 @@
{
"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",
"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ð?"
},
"q2": {
@@ -11,7 +11,7 @@
"title": "Hvar get ég beðið um þætti eða myndir?"
},
"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": "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",
"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?",
"yes": "Ég treysti þessum netþjóni"
},
@@ -82,13 +84,21 @@
"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."
},
"links": {
"discord": "Discord",
"dmca": "DMCA",
"github": "GitHub"
},
"tagline": "Horfðu á uppáhalds þætti og myndirnar þínar með þessu opna hugbúnaða forriti."
},
"global": {
"name": "movie-web",
"pages": {
"about": "Um",
"dmca": "DMCA",
"login": "Skrá inn",
"onboarding": "Setja upp",
"pagetitle": "{{title}} - movie-web",
"register": "Skrá",
"settings": "Stillingar"
}
@@ -110,7 +120,12 @@
"noResults": "Við gátum ekki fundið neitt!",
"placeholder": {
"default": "Hvað viltu horfa á?",
"extra": []
"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"
},
@@ -123,11 +138,15 @@
},
"morning": {
"default": "Hvað myndirðu vilja horfa á þessum morgni?",
"extra": ["Ég heyrði að Before Sunrise sé góð"]
"extra": [
"Ég heyrði að Before Sunrise sé góð"
]
},
"night": {
"default": "Hvað myndirðu vilja horfa á í nótt?",
"extra": ["Þreytt? Ég heyrði að The Exorcist sé góð."]
"extra": [
"Þreytt? Ég heyrði að The Exorcist sé góð."
]
}
}
},
@@ -191,7 +210,8 @@
"errorConnection": "Gat ekki tengst umboð",
"errorInvalidUrl": "Ekki gildur hlekkur",
"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ð",
"submit": "Staðfesta umboð",
@@ -232,6 +252,7 @@
},
"menus": {
"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.",
"downloadSubtitle": "Hlaða niður nú verandi texta",
"downloadVideo": "Hlaða niður myndbandi",
@@ -372,7 +393,10 @@
}
},
"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": {
"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!",
@@ -383,7 +407,8 @@
},
"screens": {
"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",
"loadingUser": "Hlaðandi þínum reikningi",
@@ -482,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>",
"emptyState": "Engir starfsmenn komnir, bættu við einum fyir neðan þennan texta",
"label": "Notaðu sérsniðaða umboðs starfsmenn",
"urlLabel": "Starfsmanna hlekkir"
"urlLabel": "Starfsmanna hlekkir",
"urlPlaceholder": "https://"
}
},
"preferences": {

View File

@@ -1,6 +1,6 @@
{
"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",
"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.",
@@ -30,14 +30,14 @@
"passphraseFrameLabel": "Frase password",
"title": "La tua frase password"
},
"hasAccount": "Hai già un account? <0>Accedi </0>",
"hasAccount": "Hai già un account? <0>Accedi.</0>",
"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",
"passphraseLabel": "Frase password di 12 parole",
"passphrasePlaceholder": "Frase password",
"submit": "Accedi",
"title": "Accedi al proprio account",
"title": "Accedi al vostro account",
"validationError": "Frase password incompleta o sbagliata"
},
"register": {
@@ -57,11 +57,13 @@
},
"host": "Ti stai collegando a <0>{{hostname}}</0> - conferma la tua fiducia prima di creare un account",
"no": "Indietro",
"noHost": "Il server non è configurato, quindi non si può creare un account",
"noHostTitle": "Server non è configurato!",
"title": "Ti fidi di questo server?",
"yes": "Mi fido di questo server"
},
"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",
"noMatch": "Frase password non corrisponde",
"passphraseLabel": "La propria frase password di 12 parole",
@@ -118,7 +120,12 @@
"noResults": "Non abbiamo trovato nulla!",
"placeholder": {
"default": "Cosa vuoi guardare?",
"extra": []
"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"
},
@@ -131,11 +138,15 @@
},
"morning": {
"default": "Cosa vorresti guardare questa mattina?",
"extra": ["Ho sentito che «Prima Dell'alba» è buono"]
"extra": [
"Ho sentito che «Prima Dell'alba» è buono"
]
},
"night": {
"default": "Cosa vorresti guardare questa sera?",
"extra": ["Stanco? Ho sentito che L'esorciccio è buono."]
"extra": [
"Stanco? Ho sentito che L'esorciccio è buono."
]
}
}
},
@@ -241,6 +252,7 @@
},
"menus": {
"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.",
"downloadSubtitle": "Scarica sottotitolo attuale",
"downloadVideo": "Scarica video",
@@ -388,9 +400,9 @@
},
"turnstile": {
"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.",
"verifyingHumanity": "verificare il proprio umanità..."
"verifyingHumanity": "Verificare la vostra umanità..."
}
},
"screens": {
@@ -399,12 +411,12 @@
"title": "DMCA"
},
"loadingApp": "Caricamento dell'applicazione",
"loadingUser": "Caricamento del proprio profilo",
"loadingUser": "Caricamento del vostro profilo",
"loadingUserError": {
"logout": "Esci",
"reset": "Ripristino del server personalizzato",
"text": "Impossibile caricare il proprio profilo",
"textWithReset": "Impossibile caricare il proprio profile dal server personalizzato, vorresti ripristinare il server predefinito?"
"text": "Impossibile caricare il vostro profilo",
"textWithReset": "Impossibile caricare il vostro profile dal server personalizzato, vorresti ripristinare il server predefinito?"
},
"migration": {
"failed": "Impossible migrare i propri dati.",
@@ -423,7 +435,7 @@
"delete": {
"button": "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?",
"text": "Questa azione è irreversibile. Tutti i propri dati verranno eliminati e non sarà possibile recuperare nulla.",
"title": "Eliminare l'account"
@@ -524,6 +536,7 @@
}
},
"subtitles": {
"backgroundBlurLabel": "Sfocatura di sfondo",
"backgroundLabel": "Opacità di sfondo",
"colorLabel": "Colore",
"previewQuote": "Cantami o Diva del pelide Achille l'ira funesta.",

View File

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

View File

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

View File

@@ -57,6 +57,8 @@
},
"host": "Je gaat zo verbinden met <0>{{hostname}}</0>, check even of je deze link vertrouwt",
"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?",
"yes": "Ik vertrouw deze server"
},
@@ -118,7 +120,12 @@
"noResults": "We konden helaas niets vinden!",
"placeholder": {
"default": "Wat wil je graag kijken?",
"extra": []
"extra": [
"Wat wil je verkennen?",
"Wat staat er op jouw kijklijst?",
"Wat is jouw favoriete film?",
"Wat is jouw favoriete serie?"
]
},
"sectionTitle": "Zoekresultaten"
},
@@ -131,11 +138,15 @@
},
"morning": {
"default": "Waar wil je deze ochtend naar kijken?",
"extra": ["Ik hoor dat Before Sunrise goed is"]
"extra": [
"Ik hoor dat Before Sunrise goed is"
]
},
"night": {
"default": "Wat wil je vanavond bekijken?",
"extra": ["Moe? Ik hoor dat The Exorcist goed is."]
"extra": [
"Moe? Ik hoor dat The Exorcist goed is."
]
}
}
},
@@ -176,10 +187,10 @@
"back": "Terug",
"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.",
"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",
"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",
"status": {
"disallowed": "Extensie is niet ingeschakeld voor deze pagina",
@@ -207,7 +218,7 @@
"title": "Laten we een nieuwe proxy instellen"
},
"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": {
"default": {
"text": "Ik wil geen streams van goede kwaliteit, <0 /> <1>Gebruik de standaardinstellingen.</1>"
@@ -241,6 +252,7 @@
},
"menus": {
"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.",
"downloadSubtitle": "Download huidige ondertiteling",
"downloadVideo": "Download filmpje",
@@ -524,6 +536,7 @@
}
},
"subtitles": {
"backgroundBlurLabel": "Achtergrondvervaging",
"backgroundLabel": "Achtergrond dekking",
"colorLabel": "Kleur",
"previewQuote": "Ik mag niet bang zijn. Angst doodt de geest.",

View File

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

View File

@@ -117,8 +117,7 @@
"loading": "Wczytywanie...",
"noResults": "Nie mogliśmy niczego znaleźć!",
"placeholder": {
"default": "Co chciałbyś obejrzeć?",
"extra": []
"default": "Co chciałbyś obejrzeć?"
},
"sectionTitle": "Wyniki wyszukiwania"
},
@@ -131,11 +130,15 @@
},
"morning": {
"default": "Co chciałbyś obejrzeć dziś rano?",
"extra": ["Słyszałem że „Przed wschodem słońca” jest dobre"]
"extra": [
"Słyszałem że „Przed wschodem słońca” jest dobre"
]
},
"night": {
"default": "Co chciałbyś obejrzeć dziś wieczorem?",
"extra": ["Zmęczony? Słyszałem że „Egzorcysta” jest dobry."]
"extra": [
"Zmęczony? Słyszałem że „Egzorcysta” jest dobry."
]
}
}
},
@@ -176,7 +179,7 @@
"back": "Wstecz",
"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ę.",
"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",
"linkFirefox": "Zainstaluj rozszerzenie na Firefox",
"notDetecting": "Zainstalowano na Chrome, ale się nie wyświetla? Spróbuj odświeżyć stronę!",
@@ -207,7 +210,7 @@
"title": "Stwórzmy nowe proxy"
},
"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": {
"default": {
"text": "Nie chcę dobrej jakości strumieni, <0 /> <1>użyj domyślnej konfiguracji</1>"
@@ -524,6 +527,7 @@
}
},
"subtitles": {
"backgroundBlurLabel": "Rozmycie tła",
"backgroundLabel": "Krycie tła",
"colorLabel": "Kolor",
"previewQuote": "Nie wolno mi się bać. Strach zabija myślenie.",

View File

@@ -241,6 +241,7 @@
},
"menus": {
"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.",
"downloadSubtitle": "Baixar legenda atual",
"downloadVideo": "Baixar vídeo",

View File

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

View File

@@ -57,6 +57,8 @@
},
"host": "Povezujete se z <0>{{hostname}}</0> - pred ustvarjanjem računa potrdite, da mu zaupate",
"no": "Nazaj",
"noHost": "Strežnik ni nastavljen, zato ustvarjanje profila ni mogoče",
"noHostTitle": "Strežnik ni nastavljen!",
"title": "Ali zaupate temu strežniku?",
"yes": "Zaupam strežniku"
},
@@ -95,6 +97,7 @@
"about": "O projektu",
"dmca": "DMCA",
"login": "Prijava",
"onboarding": "Nastavitev",
"pagetitle": "{{title}} - movie-web",
"register": "Registriraj se",
"settings": "Nastavitve"
@@ -117,7 +120,12 @@
"noResults": "Vsebin nismo našli!",
"placeholder": {
"default": "Kaj si želite gledati?",
"extra": []
"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"
},
@@ -130,7 +138,9 @@
},
"morning": {
"default": "Kateri film ali serijo bi si želeli ogledati ob tem jutru?",
"extra": ["Slišala sem, da je film \"Pred sončnim vzhodom\" odličen"]
"extra": [
"Slišala sem, da je film \"Pred sončnim vzhodom\" odličen"
]
},
"night": {
"default": "Kateri film ali serijo bi si želeli ogledati nocoj?",
@@ -166,6 +176,69 @@
"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"
},
"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": {
"close": "Zapri"
},
@@ -179,10 +252,11 @@
},
"menus": {
"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.",
"downloadSubtitle": "Prenesi trenutne podnapise",
"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": {
"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",
@@ -206,7 +280,8 @@
"episodeBadge": "E{{episode}}",
"loadingError": "Napaka pri nalaganju sezone",
"loadingList": "Nalaganje...",
"loadingTitle": "Nalaganje..."
"loadingTitle": "Nalaganje...",
"unairedEpisodes": "Ena ali več epizod v tej sezoni so onemogočene, ker še niso bile predvajane."
},
"playback": {
"speedLabel": "Hitrost predvajanja",
@@ -258,6 +333,21 @@
}
},
"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": {
"badge": "Neuspešno",
"homeButton": "Domov",
@@ -307,6 +397,12 @@
"remaining": "{{timeLeft}} do konca • Konča ob {{timeFinished, datetime}}",
"shortRegular": "{{timeWatched}}",
"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": {
@@ -379,20 +475,50 @@
},
"connections": {
"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",
"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",
"workers": {
"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",
"label": "Uporaba posrednikov po meri",
"urlLabel": "Naslov Workerja (URL)",
"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",
"save": "Shrani",
"sidebar": {
@@ -410,6 +536,7 @@
}
},
"subtitles": {
"backgroundBlurLabel": "Zameglitev ozadja",
"backgroundLabel": "Motnost ozadja",
"colorLabel": "Barva",
"previewQuote": "Ne smem se bati. Strah je ubijalec misli.",

View File

@@ -1,6 +1,19 @@
{
"about": {
"description": "movie-web என்பது இணையத்தில் ஸ்ட்ரீம்களைத் தேடும் ஒரு வலைப் பயன்பாடு ஆகும். உள்ளடக்கத்தை உட்கொள்வதற்கான ஒரு சிறிய அணுகுமுறையை குழு நோக்கமாகக் கொண்டுள்ளது.",
"faqTitle": "பொதுவான கேள்விகள்",
"q1": {
"body": "மூவி வெப் எந்தவொரு பதிவுகளையும் வழங்குவதில்லை. நீங்கள் ஒரு காட்சியை தேர்ந்தெடுத்து காண விரும்பும் போது அதற்கான பதிவை இணையத்தில் உள்ள பல்வேறு தளங்களில் இருந்து தேடி உங்களுக்கு வழங்குகிறது. பதிவுகள் ஒரு போதும் மூவி வெப் மூலம் பதிவேற்றப்படுவது இல்லை. நீங்கள் காணும் அனைத்து காட்சிகளும் தேடு பொறி முறையின் சிறப்பம்சத்தின் மூலமாகவே உங்களுக்கு வழங்கப்படுகிறது.",
"title": "பதிவு எங்கிருந்து வருகிறது?"
},
"q2": {
"body": "ஒரு திரைப்படம் அல்லது தொடரை உங்களால் நேரடியாக கோர முடியாது. மூவி வெப் எந்த ஒரு பதிவுகளையும் நிர்வகிப்பதில்லை. அணைத்து பதிவுகளும் இணையத்தில் உள்ள தளங்களின் வழியாகவே உங்களுக்கு அளிக்கப்படுகின்றது.",
"title": "ஒரு தொடர் அல்லது திரைப்படத்தை நான் எங்கே கோருவது?"
},
"q3": {
"body": "எங்கள் தேடல் முடிவுகள் The Movie Database (TMDB) தரவு தளம் மூலம் வழங்கப்படுகிறது. தேடல் முடிவுகள் தேடலுக்கான பதிவுகளை கட்டாயம் உள்ளடக்கி இருக்கும் என்பதை உறுதியாக கூற இயலாது.",
"title": "தேடல் முடிவுகள் திரைப்படம் அல்லது தொடரை காட்டுகிறது. ஆனால், ஏன் என்னால் அதை பார்க்க முடியவில்லை?"
},
"title": "movie-web பற்றி"
},
"actions": {
@@ -8,8 +21,8 @@
"copy": "நகல்"
},
"auth": {
"createAccount": "கணக்கு இல்லையா? <0>கணக்கை உருவாக்குங்கள்</0>",
"deviceNameLabel": "கருவியின் பெயர்",
"createAccount": "கணக்கு இல்லையா? <0>புதிய கணக்கை உருவாக்குங்கள்</0>",
"deviceNameLabel": "சாதனத்தின் பெயர்",
"deviceNamePlaceholder": "எனது கைபேசி",
"generate": {
"description": "தங்கள் கடவுச்சொற்றொடரே தங்களது பயனர் பெயர் மற்றும் கடவுச்சொல். கணக்கினுள் நுழைய அதனை பாதுகாப்பாக வைத்திருங்கள்",
@@ -17,51 +30,507 @@
"passphraseFrameLabel": "கடவுச்சொற்றொடர்",
"title": "உங்கள் கடவுச்சொற்றொடர்"
},
"hasAccount": "கணக்கு வைத்துள்ளீரா? <0>புகுபதிகை செய்க </0>",
"hasAccount": "ஏற்கனவே ஒரு கணக்கு ள்ளா? <0>இங்கே உள்நுழைக. </0>",
"login": {
"description": "உங்கள் கணக்கினுள் புகுபதிய கடவுச்சொற்றொடரை உள்ளிடுக",
"deviceLengthError": "கருவியின் பெயரை உள்ளிடுக",
"description": "உங்கள் கணக்கினுள் உள்நுழைய உங்கள் கடவுச்சொற்றொடரை உள்ளிடுக",
"deviceLengthError": "சாதனத்தின் பெயரை உள்ளிடவும்",
"passphraseLabel": "12-சொல் கடவுச்சொற்றொடர்",
"passphrasePlaceholder": "கடவுச்சொற்றொடர்",
"submit": "புகுபதிகை",
"title": "உங்கள் கணக்கினுள் புகுபதிய",
"title": "உங்கள் கணக்கில் உள்நுழைக",
"validationError": "தவறான அல்லது முழுமையற்ற கடவுச்சொற்றொடர்"
},
"register": {
"information": {
"icon": "பயனர் குறிப்படம்",
"color1": "Profile நிறம் ஒன்று",
"color2": "Profile நிறம் இரண்டு",
"header": "உங்கள் சாதனத்தின் பெயரை உள்ளிட்டு அதற்கான வண்ணம் மற்றும் ஐகானையும் தேர்ந்தெடுக்கவும்",
"icon": "பயனர் icon",
"next": "அடுத்து",
"title": "கணக்கு விவரம்"
}
},
"trust": {
"no": "பின்செல்"
"failed": {
"text": "நீங்கள் அதை சரியாக உள்ளமைத்தீர்களா?",
"title": "சர்வரை தொடர்பு கொள்ள முடியவில்லை"
},
"host": "நீங்கள் <0>{{hostname}}</0> உடன் இணைக்கப்படுகிறீர்கள். கணக்கை உருவாக்கும் முன் சரியான தகவல் தானா எனபதை உறுதி செய்து கொள்ளவும்",
"no": "பின்செல்",
"title": "நீங்கள் இந்த சர்வரை நம்புகிறீர்களா?",
"yes": "நான் இந்த சர்வரை நம்புகிறேன்"
},
"verify": {
"description": "உங்கள் கடவுச்சொற்றொடரைச் சேமித்துள்ளீர்கள் என்பதை உறுதிப்படுத்த உங்கள் கடவுச்சொற்றொடர்களை மீண்டும் இங்கே உள்ளீடு செய்து உங்கள் கணக்கை உருவாக்கிக் கொள்ளுங்கள்",
"invalidData": "தரவு செல்லுபடியாகாது",
"noMatch": "கடவுச்சொற்றொடர்கள் பொருந்தவில்லை",
"passphraseLabel": "உங்களின் 12-சொல் கடவுச்சொற்றொடர்",
"recaptchaFailed": "ReCaptcha சரிபார்ப்பு தோல்வியடைந்தது",
"register": "கணக்கை உருவாக்கு",
"title": "கடவுச்சொற்றொடரை உறுதி செய்க"
}
},
"errors": {
"badge": "சரியாக வேலை செய்யவில்லை",
"details": "பிழை விவரங்கள்",
"reloadPage": "இணையப் பக்கத்தை Reload செய்யவும்",
"showError": "பிழை விவரங்களைக் காட்டு",
"title": "நங்கள் ஒரு பிழையை எதிர் கொண்டு இருக்கிறோம்!"
},
"footer": {
"legal": {
"disclaimer": "பொறுப்பு துறப்பு",
"disclaimerText": "மூவி-வெப் எந்த பதிவுகளையும் தன்னகம் வைத்திருக்கவில்லை, இது மூன்றாம் தரப்பு பதிவுகளை வழங்குபவர்களை இணைக்கிறது. சட்ட சிக்கல்கள் ஏதேனும் இருப்பின் அது பதிவுகளை வழங்குபவரையே சாறும். வீடியோ வழங்குநர்கள் காண்பிக்கும் எந்த மீடியா பதிவுகளுக்கும் மூவி வெப் பொறுப்பாகாது."
},
"links": {
"discord": "Discord",
"dmca": "DMCA",
"github": "GitHub"
},
"tagline": "இந்த ஓப்பன் சோர்ஸ் (Open Source) இணைய தளம் மூலம் உங்களுக்குப் பிடித்த தொடர்களையும் திரைப்படங்களையும் பார்க்கலாம்."
},
"global": {
"name": "மூவி-வெப்",
"pages": {
"about": "தெரிந்து கொள்ள",
"dmca": "DMCA",
"login": "புகுபதிகை",
"onboarding": "அமை",
"pagetitle": "{{title}} - மூவி- வெப்",
"register": "பதிவு",
"settings": "அமைப்புகள்"
}
},
"home": {
"bookmarks": {
"sectionTitle": "குறிப்புகள்"
},
"continueWatching": {
"sectionTitle": "தொடர்ந்து பார்க்க"
},
"mediaList": {
"stopEditing": "திருத்துவதை நிறுத்து"
},
"search": {
"allResults": "மேலும் எங்களிடம் இல்லை!",
"failed": "மீடியாவைக் கண்டறிய முடியவில்லை, மீண்டும் முயலவும்!",
"loading": "Loading...",
"noResults": "எங்களால் எதையும் கண்டுபிடிக்க இயலவில்லை!",
"sectionTitle": "தேடல் முடிவுகள்"
},
"titles": {
"day": {
"default": "மதிய வணக்கம். தற்போது என்ன காண விரும்புகிறீர்கள்?",
"extra": [
"சாகசமாக உணர்கிறீர்களா? ஜுராசிக் பார்க் (Jurassic Park) சரியான தேர்வாக இருக்கலாம்."
]
},
"morning": {
"default": "காலை வணக்கம். தற்போது என்ன காண விரும்புகிறீர்கள்?",
"extra": [
"சூரிய உதயத்திற்கு முன் நல்லது என்று கேள்விப்படுகிறேன்"
]
},
"night": {
"default": "இன்றைய இரவு என்ன காண விரும்புகிறீர்கள்?",
"extra": [
"சோர்வாக உள்ளீர்களா? Exorcist திரைப்படம் காணுங்கள்."
]
}
}
},
"media": {
"episodeDisplay": "S{{season}} E{{episode}}",
"types": {
"movie": "திரைப்படம்",
"show": "காட்சி"
}
},
"navigation": {
"banner": {
"offline": "உங்கள் இணைய இணைப்பைச் சரிபார்க்கவும்"
},
"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

@@ -57,6 +57,8 @@
},
"host": "lawa ilo sina li <0>{{hostname}}</0> - ona li pona tawa sina la sina ken pali e lipu open",
"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?",
"yes": "lawa ilo ni li pona"
},
@@ -79,7 +81,8 @@
},
"footer": {
"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": {
"discord": "kulupu Siko",
@@ -117,22 +120,33 @@
"noResults": "ijo li lon ala a!",
"placeholder": {
"default": "sina wile lukin e seme?",
"extra": []
"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:"
},
"titles": {
"day": {
"default": "tenpo suno ni la sina wile lukin e seme?",
"extra": ["sina pilin alasa la o lukin e sitelen Jurassic Park"]
"extra": [
"sina pilin alasa la o lukin e sitelen Jurassic Park"
]
},
"morning": {
"default": "tenpo sin ni la sina wile lukin e seme?",
"extra": ["ken la sitelen Before Sunrise li pona"]
"extra": [
"ken la sitelen Before Sunrise li pona"
]
},
"night": {
"default": "tenpo pimeja ni la sina wile lukin e seme?",
"extra": ["sina pilin lape anu seme? o alasa lukin e sitelen Exorcist"]
"extra": [
"sina pilin lape anu seme? o alasa lukin e sitelen Exorcist"
]
}
}
},
@@ -163,6 +177,9 @@
"title": "mi ken ala lukin e lipu ona"
},
"onboarding": {
"defaultConfirm": {
"cancel": "ala"
},
"start": {
"title": "o open e ilo Muwi-We"
}

View File

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

View File

@@ -123,8 +123,23 @@
"sectionTitle": "Kết quả tìm kiếm"
},
"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": {
"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."
]
}
}
},
@@ -143,6 +158,7 @@
"about": "Về chúng tôi",
"donation": "Ủng hộ",
"logout": "Đăng xuất",
"register": "Đồng bộ hóa với đám mây",
"settings": "Cài đặt",
"support": "Hỗ trợ"
}
@@ -161,7 +177,8 @@
"title": "Bạn có chắc không?"
},
"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": {

View File

@@ -57,6 +57,8 @@
},
"host": "您正在连接到 <0>{{hostname}}</0> - 在创建账户前,确保您信任它",
"no": "返回",
"noHost": "服务器尚未进行配置,因此您无法创建账户",
"noHostTitle": "未配置服务器!",
"title": "您是否信任这个服务器?",
"yes": "我信任这个服务器"
},
@@ -71,11 +73,11 @@
}
},
"errors": {
"badge": "它已损坏",
"badge": "坏",
"details": "错误细节",
"reloadPage": "刷新页面",
"showError": "显示错误细节",
"title": "我们遇到错误!"
"title": "我们遇到错误!"
},
"footer": {
"legal": {
@@ -118,22 +120,33 @@
"noResults": "我们找不到任何结果!",
"placeholder": {
"default": "您想看些什么?",
"extra": []
"extra": [
"您想探索些什么?",
"您的片单上都有啥?",
"您最喜欢的影片是什么?",
"您最喜欢的剧目是什么?"
]
},
"sectionTitle": "搜索结果"
},
"titles": {
"day": {
"default": "您今天下午想看什么?",
"extra": ["想要来场冒险?《侏罗纪公园》可能是最佳选项。"]
"extra": [
"想要来场冒险?《侏罗纪公园》可能是最佳选项。"
]
},
"morning": {
"default": "您今早想看什么?",
"extra": ["我听说《爱在黎明破晓前》不错"]
"extra": [
"我听说《爱在黎明破晓前》不错"
]
},
"night": {
"default": "您今晚想看什么?",
"extra": ["累了?我听说《驱魔人》不错。"]
"extra": [
"累了?我听说《驱魔人》不错。"
]
}
}
},
@@ -239,6 +252,7 @@
},
"menus": {
"downloads": {
"copyHlsPlaylist": "复制 HLS 播放列表链接",
"disclaimer": "下载内容是直接从内容提供者获取的。movie-web 无法控制下载内容如何被提供。",
"downloadSubtitle": "下载当前字幕",
"downloadVideo": "下载视频",
@@ -522,6 +536,7 @@
}
},
"subtitles": {
"backgroundBlurLabel": "背景模糊",
"backgroundLabel": "背景不透明度",
"colorLabel": "颜色",
"previewQuote": "我不能害怕。恐惧是心灵的杀手。",

View File

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

View File

@@ -6,6 +6,11 @@ import {
import { isAllowedExtensionVersion } from "@/backend/extension/compatibility";
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
// page load before the extension starts responding properly
const isExtensionReady = new Promise<void>((resolve) => {

View File

@@ -1,6 +1,6 @@
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 {
try {
@@ -36,7 +36,7 @@ function buildHeadersFromStream(stream: Stream): Record<string, string> {
export async function prepareStream(stream: Stream) {
await setDomainRule({
ruleId: 1,
ruleId: RULE_IDS.PREPARE_STREAM,
targetDomains: extractDomainsFromStream(stream),
requestHeaders: buildHeadersFromStream(stream),
});

View File

@@ -51,3 +51,15 @@ export async function downloadCaption(
downloadCache.set(caption.url, output, expirySeconds);
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 apiKey = conf().TMDB_READ_API_KEY;
const headers = {
accept: "application/json",
Authorization: `Bearer ${conf().TMDB_READ_API_KEY}`,
Authorization: `Bearer ${apiKey}`,
};
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), {
headers,
baseURL,

View File

@@ -64,6 +64,8 @@ export enum Icons {
DONATION = "donation",
CIRCLE_QUESTION = "circle_question",
BRUSH = "brush",
CLOUD_ARROW_UP = "cloud_arrow_up",
FILE_ARROW_DOWN = "file_arrow_down",
}
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>`,
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>`,
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() {

View File

@@ -1,5 +1,6 @@
import classNames from "classnames";
import { ReactNode, useCallback } from "react";
import { useNavigate } from "react-router-dom";
import { Icon, Icons } from "@/components/Icon";
import { Spinner } from "@/components/layout/Spinner";
@@ -20,6 +21,7 @@ interface Props {
}
export function Button(props: Props) {
const navigate = useNavigate();
const { onClick, href, loading } = props;
const cb = useCallback(
(
@@ -31,10 +33,14 @@ export function Button(props: Props) {
if (loading) return;
if (href && !onClick) {
event.preventDefault();
window.open(href, "_blank", "noreferrer");
if (!href.includes("http")) {
navigate(href);
} else {
window.open(href, "_blank", "noreferrer");
}
} else onClick?.(event);
},
[onClick, href, loading],
[loading, href, onClick, navigate],
);
let colorClasses = "bg-white hover:bg-gray-200 text-black";

View File

@@ -25,7 +25,7 @@ export function IconPatch(props: IconPatchProps) {
return (
<div className={props.className || undefined} onClick={props.onClick}>
<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} />
</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

@@ -231,7 +231,7 @@ export function CaptionSettingsView({ id }: { id: string }) {
<Menu.BackLink onClick={() => router.navigate("/captions")}>
{t("player.menus.subtitles.settings.backlink")}
</Menu.BackLink>
<Menu.Section className="space-y-6">
<Menu.Section className="space-y-6 pb-5">
<CaptionSetting
label={t("player.menus.subtitles.settings.delay")}
max={10}
@@ -262,6 +262,14 @@ export function CaptionSettingsView({ id }: { id: string }) {
value={styling.backgroundOpacity * 100}
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
label={t("settings.subtitles.textSizeLabel")}
max={200}

View File

@@ -122,9 +122,16 @@ export function CaptionsView({ id }: { id: string }) {
>(null);
const { selectCaptionById, disable } = useCaptions();
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 subtitleList = useSubtitleList(captionList, searchQuery);
const subtitleList = useSubtitleList(captions, searchQuery);
const [downloadReq, startDownload] = useAsyncFn(
async (captionId: string) => {

View File

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

View File

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

View File

@@ -1,6 +1,11 @@
import fscreen from "fscreen";
import Hls, { Level } from "hls.js";
import {
RULE_IDS,
isExtensionActiveCached,
setDomainRule,
} from "@/backend/extension/messaging";
import {
DisplayInterface,
DisplayInterfaceEvents,
@@ -31,8 +36,8 @@ const levelConversionMap: Record<number, SourceQuality> = {
480: "480",
};
function hlsLevelToQuality(level: Level): SourceQuality | null {
return levelConversionMap[level.height] ?? null;
function hlsLevelToQuality(level?: Level): SourceQuality | null {
return levelConversionMap[level?.height ?? 0] ?? null;
}
function qualityToHlsLevel(quality: SourceQuality): number | null {
@@ -62,6 +67,11 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
let preferenceQuality: SourceQuality | null = null;
let lastVolume = 1;
const languagePromises = new Map<
string,
(value: void | PromiseLike<void>) => void
>();
function reportLevels() {
if (!hls) return;
const levels = hls.levels;
@@ -128,6 +138,7 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
},
},
},
renderTextTracksNatively: false,
});
hls.on(Hls.Events.ERROR, (event, data) => {
console.error("HLS error", data);
@@ -144,12 +155,40 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
if (!hls) return;
reportLevels();
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, () => {
if (!hls) return;
const quality = hlsLevelToQuality(hls.levels[hls.currentLevel]);
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);
@@ -390,5 +429,40 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
setPlaybackRate(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;
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 { CaptionListItem } from "@/stores/player/slices/source";
import { LoadableSource, SourceQuality } from "@/stores/player/utils/qualities";
import { Listener } from "@/utils/events";
@@ -70,4 +73,7 @@ export interface DisplayInterface extends Listener<DisplayInterfaceEvents> {
setMeta(meta: DisplayMeta): void;
setCaption(caption: DisplayCaption | null): void;
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 { useSubtitleStore } from "@/stores/subtitles";
import {
filterDuplicateCaptionCues,
parseVttSubtitles,
} from "../utils/captions";
export function useCaptions() {
const setLanguage = useSubtitleStore((s) => s.setLanguage);
const enabled = useSubtitleStore((s) => s.enabled);
@@ -12,32 +19,85 @@ export function useCaptions() {
);
const setCaption = usePlayerStore((s) => s.setCaption);
const lastSelectedLanguage = useSubtitleStore((s) => s.lastSelectedLanguage);
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(
async (captionId: string) => {
const caption = captionList.find((v) => v.id === captionId);
const caption = captions.find((v) => v.id === captionId);
if (!caption) return;
const srtData = await downloadCaption(caption);
setCaption({
const captionToSet: Caption = {
id: caption.id,
language: caption.language,
srtData,
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();
setLanguage(caption.language);
},
[setLanguage, captionList, setCaption, resetSubtitleSpecificSettings],
[
setLanguage,
captions,
setCaption,
resetSubtitleSpecificSettings,
getSubtitleTracks,
setSubtitlePreference,
],
);
const selectLanguage = useCallback(
async (language: string) => {
const caption = captionList.find((v) => v.language === language);
const caption = captions.find((v) => v.language === language);
if (!caption) return;
return selectCaptionById(caption.id);
},
[captionList, selectCaptionById],
[captions, selectCaptionById],
);
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;
}
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(
text: string,
_language?: string,
): CaptionCueType[] {
const vtt = convertSubtitlesToVtt(text);
return parse(vtt).filter((cue) => cue.type === "caption") as CaptionCueType[];
return parseVttSubtitles(vtt);
}
function stringToBase64(input: string): string {

View File

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

View File

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

@@ -1,5 +1,5 @@
import classNames from "classnames";
import { useCallback, useEffect, useMemo, useRef } from "react";
import { useCallback, useEffect, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { useAsyncFn } from "react-use";
@@ -208,7 +208,13 @@ export function SettingsPage() {
// when backend url gets changed, log the user out first
if (state.backendUrl.changed) {
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,

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 { Button } from "@/components/buttons/Button";
import { Stepper } from "@/components/layout/Stepper";
import { CenterContainer } from "@/components/layout/ThinContainer";
import { VerticalLine } from "@/components/layout/VerticalLine";
import { Modal, ModalCard, useModal } from "@/components/overlays/Modal";
import { Heading1, Heading2, Paragraph } from "@/components/utils/Text";
import { MinimalPageLayout } from "@/pages/layouts/MinimalPageLayout";
@@ -15,14 +15,6 @@ import { Card, CardContent, Link } from "@/pages/onboarding/utils";
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() {
const navigate = useNavigateOnboarding();
const skipModal = useModal("skip");
@@ -73,7 +65,9 @@ export function OnboardingPage() {
</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">or</span>
<span className="text-xs uppercase font-bold">
{t("onboarding.start.options.or")}
</span>
<VerticalLine />
</div>
<Card onClick={() => navigate("/onboarding/proxy")}>

View File

@@ -25,7 +25,7 @@ export function TMDBTestPart() {
errorText: "",
});
if (tmdbApiKey.length === 0) {
if (!tmdbApiKey || tmdbApiKey.length === 0) {
return setStatus({
hasTested: true,
success: false,

View File

@@ -92,6 +92,16 @@ export function CaptionsPart(props: {
value={props.styling.backgroundOpacity * 100}
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
label={t("settings.subtitles.textSizeLabel")}
max={200}

View File

@@ -14,7 +14,7 @@ import { useAuthStore } from "@/stores/auth";
const rem = 16;
function SecureBadge(props: { url: string | undefined }) {
function SecureBadge(props: { url: string | null }) {
const { t } = useTranslation();
const secure = props.url ? props.url.startsWith("https://") : false;
return (

View File

@@ -19,6 +19,8 @@ import { DmcaPage, shouldHaveDmcaPage } from "@/pages/Dmca";
import { NotFoundPage } from "@/pages/errors/NotFoundPage";
import { HomePage } from "@/pages/HomePage";
import { LoginPage } from "@/pages/Login";
import { MigrationPage } from "@/pages/migration/Migration";
import { MigrationDirectPage } from "@/pages/migration/MigrationDirect";
import { OnboardingPage } from "@/pages/onboarding/Onboarding";
import { OnboardingExtensionPage } from "@/pages/onboarding/OnboardingExtension";
import { OnboardingProxyPage } from "@/pages/onboarding/OnboardingProxy";
@@ -129,6 +131,9 @@ function App() {
/>
<Route path="/onboarding/proxy" element={<OnboardingProxyPage />} />
<Route path="/migration" element={<MigrationPage />} />
<Route path="/migration/direct" element={<MigrationDirectPage />} />
{shouldHaveDmcaPage() ? (
<Route path="/dmca" element={<DmcaPage />} />
) : null}

View File

@@ -31,10 +31,10 @@ export interface RuntimeConfig {
DONATION_LINK: string;
DISCORD_LINK: string;
DMCA_EMAIL: string | null;
TMDB_READ_API_KEY: string;
TMDB_READ_API_KEY: string | null;
NORMAL_ROUTER: boolean;
PROXY_URLS: string[];
BACKEND_URL: string;
BACKEND_URL: string | null;
DISALLOWED_IDS: string[];
TURNSTILE_KEY: string | null;
CDN_REPLACEMENTS: Array<string[]>;
@@ -66,48 +66,50 @@ const env: Record<keyof Config, undefined | string> = {
HAS_ONBOARDING: import.meta.env.VITE_HAS_ONBOARDING,
};
// loads from different locations, in order: environment (VITE_{KEY}), window (public/config.js)
function getKeyValue(key: keyof Config): string | undefined {
let windowValue = (window as any)?.__CONFIG__?.[`VITE_${key}`];
if (
windowValue !== null &&
windowValue !== undefined &&
windowValue.length === 0
)
windowValue = undefined;
return env[key] ?? windowValue ?? undefined;
function coerceUndefined(value: string | null | undefined): string | undefined {
if (value == null) return undefined;
if (value.length === 0) return undefined;
return value;
}
function getKey(key: keyof Config, defaultString?: string): string {
return getKeyValue(key)?.toString() ?? defaultString ?? "";
// loads from different locations, in order: environment (VITE_{KEY}), window (public/config.js)
function getKeyValue(key: keyof Config): string | undefined {
const windowValue = (window as any)?.__CONFIG__?.[`VITE_${key}`];
return coerceUndefined(env[key]) ?? coerceUndefined(windowValue) ?? undefined;
}
function getKey(key: keyof Config): string | null;
function getKey(key: keyof Config, defaultString: string): string;
function getKey(key: keyof Config, defaultString?: string): string | null {
return getKeyValue(key)?.toString() ?? defaultString ?? null;
}
export function conf(): RuntimeConfig {
const dmcaEmail = getKey("DMCA_EMAIL");
const chromeExtension = getKey("ONBOARDING_CHROME_EXTENSION_INSTALL_LINK");
const firefoxExtension = getKey("ONBOARDING_FIREFOX_EXTENSION_INSTALL_LINK");
const proxyInstallLink = getKey("ONBOARDING_PROXY_INSTALL_LINK");
const turnstileKey = getKey("TURNSTILE_KEY");
return {
APP_VERSION,
GITHUB_LINK,
DONATION_LINK,
DISCORD_LINK,
DMCA_EMAIL: dmcaEmail.length > 0 ? dmcaEmail : null,
ONBOARDING_CHROME_EXTENSION_INSTALL_LINK:
chromeExtension.length > 0 ? chromeExtension : null,
ONBOARDING_FIREFOX_EXTENSION_INSTALL_LINK:
firefoxExtension.length > 0 ? firefoxExtension : null,
ONBOARDING_PROXY_INSTALL_LINK:
proxyInstallLink.length > 0 ? proxyInstallLink : null,
DMCA_EMAIL: getKey("DMCA_EMAIL"),
ONBOARDING_CHROME_EXTENSION_INSTALL_LINK: getKey(
"ONBOARDING_CHROME_EXTENSION_INSTALL_LINK",
"https://chromewebstore.google.com/detail/movie-web-extension/hoffoikpiofojilgpofjhnkkamfnnhmm",
),
ONBOARDING_FIREFOX_EXTENSION_INSTALL_LINK: getKey(
"ONBOARDING_FIREFOX_EXTENSION_INSTALL_LINK",
"https://addons.mozilla.org/en-GB/firefox/addon/movie-web-extension",
),
ONBOARDING_PROXY_INSTALL_LINK: getKey("ONBOARDING_PROXY_INSTALL_LINK"),
BACKEND_URL: getKey("BACKEND_URL", BACKEND_URL),
TMDB_READ_API_KEY: getKey("TMDB_READ_API_KEY"),
PROXY_URLS: getKey("CORS_PROXY_URL")
PROXY_URLS: getKey("CORS_PROXY_URL", "")
.split(",")
.map((v) => v.trim()),
.map((v) => v.trim())
.filter((v) => v.length > 0),
NORMAL_ROUTER: getKey("NORMAL_ROUTER", "false") === "true",
HAS_ONBOARDING: getKey("HAS_ONBOARDING", "false") === "true",
TURNSTILE_KEY: turnstileKey.length > 0 ? turnstileKey : null,
HAS_ONBOARDING: getKey("HAS_ONBOARDING", "true") === "true",
TURNSTILE_KEY: getKey("TURNSTILE_KEY"),
DISALLOWED_IDS: getKey("DISALLOWED_IDS", "")
.split(",")
.map((v) => v.trim())

View File

@@ -10,6 +10,7 @@ export function Banner(props: {
id: string;
}) {
const [ref] = useRegisterBanner<HTMLDivElement>(props.id);
const hideBanner = useBannerStore((s) => s.hideBanner);
const styles = {
error: "bg-[#C93957] text-white",
};
@@ -29,6 +30,12 @@ export function Banner(props: {
<Icon icon={icons[props.type]} />
<div>{props.children}</div>
</div>
<span
className="absolute right-4 hover:cursor-pointer"
onClick={() => hideBanner(props.id, true)}
>
<Icon icon={Icons.X} />
</span>
</div>
</div>
);
@@ -38,6 +45,7 @@ export function BannerLocation(props: { location?: string }) {
const { t } = useTranslation();
const isOnline = useBannerStore((s) => s.isOnline);
const setLocation = useBannerStore((s) => s.setLocation);
const ignoredBannerIds = useBannerStore((s) => s.ignoredBannerIds);
const currentLocation = useBannerStore((s) => s.location);
const loc = props.location ?? null;
@@ -53,7 +61,7 @@ export function BannerLocation(props: { location?: string }) {
return (
<div>
{!isOnline ? (
{!isOnline && !ignoredBannerIds.includes("offline") ? (
<Banner id="offline" type="error">
{t("navigation.banner.offline")}
</Banner>

View File

@@ -13,9 +13,10 @@ interface BannerStore {
isOnline: boolean;
isTurnstile: boolean;
location: string | null;
ignoredBannerIds: string[];
updateHeight(id: string, height: number): void;
showBanner(id: string): void;
hideBanner(id: string): void;
hideBanner(id: string, force?: boolean): void;
setLocation(loc: string | null): void;
updateOnline(isOnline: boolean): void;
updateTurnstile(isTurnstile: boolean): void;
@@ -27,6 +28,7 @@ export const useBannerStore = create(
isOnline: true,
isTurnstile: false,
location: null,
ignoredBannerIds: [],
updateOnline(isOnline) {
set((s) => {
s.isOnline = isOnline;
@@ -45,14 +47,16 @@ export const useBannerStore = create(
showBanner(id) {
set((s) => {
if (s.banners.find((v) => v.id === id)) return;
if (s.ignoredBannerIds.includes(id)) return;
s.banners.push({
id,
height: 0,
});
});
},
hideBanner(id) {
hideBanner(id, force = false) {
set((s) => {
if (force) s.ignoredBannerIds.push(id);
s.banners = s.banners.filter((v) => v.id !== id);
});
},

View File

@@ -26,6 +26,7 @@ export interface BookmarkStore {
bookmarks: Record<string, BookmarkMediaItem>;
updateQueue: BookmarkUpdateItem[];
addBookmark(meta: PlayerMeta): void;
setBookmark(id: string, item: BookmarkMediaItem): void;
removeBookmark(id: string): void;
replaceBookmarks(items: Record<string, BookmarkMediaItem>): void;
clear(): void;
@@ -94,6 +95,11 @@ export const useBookmarkStore = create(
s.updateQueue = [...s.updateQueue.filter((v) => v.id !== id)];
});
},
setBookmark(id, item) {
set((s) => {
s.bookmarks[id] = item;
});
},
})),
{
name: "__MW::bookmarks",

View File

@@ -53,6 +53,7 @@ export interface CaptionListItem {
language: string;
url: string;
needsProxy: boolean;
hls?: boolean;
}
export interface SourceSlice {

View File

@@ -14,6 +14,7 @@ export type SourceFileStream = {
export type LoadableSource = {
type: StreamType;
url: string;
headers?: Stream["headers"];
preferredHeaders?: Stream["preferredHeaders"];
};
@@ -21,11 +22,13 @@ export type SourceSliceSource =
| {
type: "file";
qualities: Partial<Record<SourceQuality, SourceFileStream>>;
headers?: Stream["headers"];
preferredHeaders?: Stream["preferredHeaders"];
}
| {
type: "hls";
url: string;
headers?: Stream["headers"];
preferredHeaders?: Stream["preferredHeaders"];
};

View File

@@ -64,6 +64,7 @@ export interface ProgressStore {
clear(): void;
clearUpdateQueue(): void;
removeUpdateItem(id: string): void;
setItem(id: string, item: ProgressMediaItem): void;
}
let updateId = 0;
@@ -173,6 +174,11 @@ export const useProgressStore = create(
s.updateQueue = [...s.updateQueue.filter((v) => v.id !== id)];
});
},
setItem(id, item) {
set((s) => {
s.items[id] = item;
});
},
})),
{
name: "__MW::progress",

View File

@@ -1,3 +1,4 @@
import merge from "lodash.merge";
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
@@ -33,6 +34,7 @@ export const useQualityStore = create(
})),
{
name: "__MW::quality",
merge: (persisted, current) => merge({}, current, persisted),
},
),
);

View File

@@ -1,3 +1,4 @@
import merge from "lodash.merge";
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";
@@ -17,6 +18,11 @@ export interface SubtitleStyling {
* background opacity, ranges between 0 and 1
*/
backgroundOpacity: number;
/**
* background blur, ranges between 0 and 1
*/
backgroundBlur: number;
}
export interface SubtitleStore {
@@ -51,6 +57,7 @@ export const useSubtitleStore = create(
color: "#ffffff",
backgroundOpacity: 0.5,
size: 1,
backgroundBlur: 0.5,
},
resetSubtitleSpecificSettings() {
set((s) => {
@@ -61,11 +68,19 @@ export const useSubtitleStore = create(
updateStyling(newStyling) {
set((s) => {
if (newStyling.backgroundOpacity !== undefined)
s.styling.backgroundOpacity = newStyling.backgroundOpacity;
s.styling.backgroundOpacity = Math.min(
1,
Math.max(0, newStyling.backgroundOpacity),
);
if (newStyling.backgroundBlur !== undefined)
s.styling.backgroundBlur = Math.min(
1,
Math.max(0, newStyling.backgroundBlur),
);
if (newStyling.color !== undefined)
s.styling.color = newStyling.color.toLowerCase();
if (newStyling.size !== undefined)
s.styling.size = Math.min(2, Math.max(0.01, newStyling.size));
s.styling.size = Math.min(10, Math.max(0.01, newStyling.size));
});
},
setLanguage(lang) {
@@ -99,6 +114,7 @@ export const useSubtitleStore = create(
})),
{
name: "__MW::subtitles",
merge: (persisted, current) => merge({}, current, persisted),
},
),
);

View File

@@ -31,7 +31,7 @@ const tokens = {
c200: "#8A293B",
c300: "#812435",
c400: "#701B2B",
}
},
},
blue: {
c50: "#ADADF5",
@@ -43,7 +43,7 @@ const tokens = {
c600: "#1B1B41",
c700: "#171736",
c800: "#101020",
c900: "#0B0B13"
c900: "#0B0B13",
},
purple: {
c50: "#D5AAFF",
@@ -55,7 +55,7 @@ const tokens = {
c600: "#411F64",
c700: "#31184A",
c800: "#221134",
c900: "#160B22"
c900: "#160B22",
},
ash: {
c50: "#7F8D9B",
@@ -67,7 +67,7 @@ const tokens = {
c600: "#172532",
c700: "#131E29",
c800: "#101820",
c900: "#0C1216"
c900: "#0C1216",
},
shade: {
c50: "#676790",
@@ -79,9 +79,9 @@ const tokens = {
c600: "#171728",
c700: "#131322",
c800: "#0F0F1B",
c900: "#0A0A12"
}
}
c900: "#0A0A12",
},
};
export const defaultTheme = {
extend: {
@@ -100,25 +100,25 @@ export const defaultTheme = {
activeBackground: tokens.shade.c300,
},
// meta data for the theme itself
global: {
accentA: tokens.blue.c200,
accentB: tokens.blue.c300,
},
// light bar
lightBar: {
light: tokens.blue.c400,
},
// Buttons
buttons: {
toggle: tokens.purple.c300,
toggleDisabled: tokens.ash.c500,
danger: tokens.semantic.rose.c300,
dangerHover: tokens.semantic.rose.c200,
secondary: tokens.ash.c700,
secondaryText: tokens.semantic.silver.c300,
secondaryHover: tokens.ash.c700,
@@ -130,7 +130,7 @@ export const defaultTheme = {
cancel: tokens.ash.c500,
cancelHover: tokens.ash.c300,
},
// only used for body colors/textures
background: {
main: tokens.shade.c900,
@@ -144,7 +144,7 @@ export const defaultTheme = {
modal: {
background: tokens.shade.c800,
},
// typography
type: {
logo: tokens.purple.c100,
@@ -233,8 +233,8 @@ export const defaultTheme = {
},
saveBar: {
background: tokens.shade.c800
}
background: tokens.shade.c800,
},
},
// Utilities
@@ -290,7 +290,7 @@ export const defaultTheme = {
autoPlay: {
background: tokens.ash.c700,
hover: tokens.ash.c500
hover: tokens.ash.c500,
},
scraping: {

49
vercel.json Normal file
View File

@@ -0,0 +1,49 @@
{
"rewrites": [
{
"source": "/(.*)",
"destination": "/"
}
],
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "DENY"
},
{
"key": "X-XSS-Protection",
"value": "1; mode=block"
},
{
"key": "Cache-Control",
"value": "public, max-age=0, s-maxage=0, must-revalidate"
}
]
},
{
"source": "/manifest.webmanifest",
"headers": [
{
"key": "Content-Type",
"value": "application/manifest+json"
}
]
},
{
"source": "/assets/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, s-maxage=31536000, immutable"
}
]
}
]
}

View File

@@ -24,6 +24,7 @@ const captioningPackages = [
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd());
return {
base: env.VITE_BASE_URL || '/',
plugins: [
million.vite({ auto: true, mute: true }),
handlebars({