Compare commits

..

71 Commits

Author SHA1 Message Date
Jorrin
3c4d84db5c Merge pull request #1117 from movie-web/dev
Version 4.7.0
2024-04-14 23:04:46 +02:00
William Oldham
dad968ee0f Bump version 2024-04-14 21:52:12 +01:00
William Oldham
8eeb200558 Bump provider to 2.3.0 2024-04-14 21:52:12 +01:00
Jorrin
0b3eb67a52 Merge branch 'master' into dev 2024-04-14 22:50:36 +02:00
William Oldham
0c543b6516 Merge pull request #1094 from gh-movie-web/weblate-movie-web-website
Translations update from movie-web weblate
2024-04-14 21:44:51 +01:00
Erwann Lagouche
612c98c246 Translated using Weblate (French)
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/fr/
Author: Erwann Lagouche <popGthyrd@gmail.com>
2024-04-14 20:44:14 +00:00
Mehdi
8da9db2c83 Translated using Weblate (Persian)
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-04-14 20:44:14 +00:00
Mehdi
2a827bd0a4 Translated using Weblate (Persian)
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-04-14 20:44:14 +00:00
William Oldham
ff95d1f713 Merge pull request #1072 from qtchaos/feat/autoplay
feat: add autoplay preference
2024-04-14 21:44:11 +01:00
William Oldham
ad83797451 Merge branch 'dev' into feat/autoplay 2024-04-14 21:30:44 +01:00
William Oldham
926018310e Fix TMDB code 2024-04-14 21:29:45 +01:00
William Oldham
44694c6c5a Merge pull request #1108 from meghrathod/dev
Added alternate tmdb endpoint to fallback when main url is blocked
2024-04-14 21:21:09 +01:00
William Oldham
527b473835 Merge pull request #1098 from dro-1/fix/next-episode
Fixed next episode button not loading new season
2024-04-14 21:18:56 +01:00
William Oldham
85337018b1 Merge branch 'dev' into fix/next-episode 2024-04-14 21:17:41 +01:00
Seun Taiwo
90c4365422 Fixed next episode button not showing next season 2024-04-14 20:20:48 +01:00
Megh Rathod
995c855ac2 added useFallback to decide which TMDB url to use
Signed-off-by: Megh Rathod <me@meghrathod.dev>
2024-04-14 16:19:51 +05:30
Megh Rathod
0e3f82df30 Return values instead of promise
Co-authored-by: William Oldham <github@binaryoverload.co.uk>
2024-04-14 15:14:14 +05:30
Megh Rathod
76d906c95a fix: use AbortSignal.timeout instead of setTimeout
Signed-off-by: Megh Rathod <me@meghrathod.dev>
2024-04-12 23:29:42 +05:30
Jorrin
391554538a Merge branch 'dev' into feat/autoplay 2024-04-12 19:06:40 +02:00
Megh Rathod
1ec51699d1 fix: add alternate tmdb endpoint to fix errors when main url is blocked
Signed-off-by: Megh Rathod <me@meghrathod.dev>
2024-04-12 11:33:18 +05:30
qtchaos
ed451763ed chore: clean up classNames 2024-04-12 00:04:19 +03:00
qtchaos
e46ca23516 chore: clean up code and remove intervals 2024-04-12 00:01:57 +03:00
Megh Rathod
8a9def00de fix: tmdb fetch failure due ISP blocks in India
Signed-off-by: Megh Rathod <me@meghrathod.dev>
2024-04-11 23:34:40 +05:30
William Oldham
cf8018c9e9 Merge pull request #1102 from vijaysingh2219/dev
Implement functionality to open URL in new tab on middle click
2024-04-10 23:34:31 +01:00
Vijay
374fd57dbc Replaced button with react-router-dom's Link component for better navigation. 2024-04-11 03:48:56 +05:30
Vijay
5275c56725 Implement functionality to open URL in new tab on middle click
Added handleClick function to check for middle mouse button (event.button === 1), opening the URL in a new tab using window.open.
Improves user experience by offering an alternative method to open URLs without leaving the current page.
2024-04-11 00:39:55 +05:30
qtchaos
9044b4407f chore: remove whitespace 2024-04-10 20:37:36 +03:00
qtchaos
92afd66166 chore: update pnpm-lock.yaml 2024-04-10 20:27:08 +03:00
qtchaos
c8fa561c7f fix: remove unnecessary lodash functions and use setInterval instead 2024-04-10 20:25:45 +03:00
chaos
e78d7a36f5 Merge branch 'dev' into feat/autoplay 2024-04-10 19:24:26 +03:00
Jorrin
b6c894a87a Merge pull request #1099 from movie-web/feature/default-audio-language
Select default audio language based on setting
2024-04-10 18:20:15 +02:00
Jorrin
4bdb366139 Merge branch 'dev' into feature/default-audio-language 2024-04-10 18:18:52 +02:00
Jorrin
2f92bc4ee6 Merge pull request #1101 from vijaysingh2219/dev
Fix keyboard event handling in KeyboardEvents component
2024-04-10 18:18:39 +02:00
Vijay
2722a7db96 Fix keyboard event handling in KeyboardEvents component
- Changed the condition from 'k' to 'keyL' for 'j', 'l', 'm', 'f', 'c', 'r' keys to handle uppercase keys properly.
- Fixed the condition for toggling play/pause to work with both ' ' and 'k' keys.

This commit addresses issues with keyboard event handling and ensures proper functionality with both uppercase and lowercase keys.
2024-04-10 21:24:29 +05:30
Jorrin
9d4be2cb55 Select default audio language based on setting 2024-04-08 16:32:33 +02:00
Jorrin
fed9a0c2dd Merge pull request #1089 from movie-web/dev
Version 4.6.6
2024-04-05 20:26:21 +02:00
Jorrin
892292088d Merge branch 'master' into dev 2024-04-05 20:13:55 +02:00
Jorrin
bd9db1dc80 Update pnpm-lock.yaml 2024-04-05 20:12:39 +02:00
Jorrin
c0edae8a36 bump versions 2024-04-05 20:10:41 +02:00
Jorrin
cab9609132 Merge pull request #1080 from gh-movie-web/weblate-movie-web-website
Translations update from movie-web weblate
2024-04-05 20:10:03 +02:00
Matic Boncina
d014bcee55 Translated using Weblate (Slovenian)
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/sl/
Author: Matic Boncina <476328473@express.ninja>
2024-04-05 18:07:59 +00:00
Aayush Shah
53de238bd3 Translated using Weblate (Hindi)
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/hi/
Author: Aayush Shah <shahaayush999@gmail.com>
2024-04-05 18:07:59 +00:00
Aayush Shah
ae4adddee6 Translated using Weblate (Nepali)
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/ne/
Author: Aayush Shah <shahaayush999@gmail.com>
2024-04-05 18:07:59 +00:00
Mehdi
38d32b294e Translated using Weblate (Persian)
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-04-05 18:07:59 +00:00
superlincoln
0077a5ace7 Translated using Weblate (Chinese (Traditional))
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/zh_Hant/
Author: superlincoln <littlelittlelincoln@gmail.com>
2024-04-05 18:07:59 +00:00
superlincoln
377f6740b5 Translated using Weblate (Korean)
Currently translated at 100.0% (328 of 328 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/ko/
Author: superlincoln <littlelittlelincoln@gmail.com>
2024-04-05 18:07:59 +00:00
superlincoln
b8fec30d29 Translated using Weblate (Dutch)
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/nl/
Author: superlincoln <littlelittlelincoln@gmail.com>
2024-04-05 18:07:59 +00:00
superlincoln
01687da4df Translated using Weblate (German)
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/de/
Author: superlincoln <littlelittlelincoln@gmail.com>
2024-04-05 18:07:59 +00:00
Raymond Nee
f046728434 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/zh_Hans/
Author: Raymond Nee <monstorix@outlook.com>
2024-04-05 18:07:59 +00:00
Jorrin
17d7f292b5 Merge pull request #1088 from movie-web/fix/vidsrcto-and-ridomovies
Fixes for VidSrcTo & Ridomovies
2024-04-05 20:07:54 +02:00
Jorrin
855e594c09 Fix switching from hls source to hls source 2024-04-05 19:16:46 +02:00
Jorrin
af2e6b793d Merge pull request #1081 from movie-web/dependabot/npm_and_yarn/vite-5.0.13
Bump vite from 5.0.12 to 5.0.13
2024-04-03 20:42:48 +02:00
dependabot[bot]
f0df8c3efb Bump vite from 5.0.12 to 5.0.13
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.0.12 to 5.0.13.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.0.13/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.0.13/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-03 17:55:25 +00:00
William Oldham
83af8891f7 Merge pull request #1074 from movie-web/dev
Version 4.6.5: Fixing showbox once again
2024-03-31 23:37:45 +01:00
Jorrin
eab7b09292 Merge branch 'master' into dev 2024-04-01 00:34:56 +02:00
Jorrin
84bddd2e4d bump versions 2024-04-01 00:31:40 +02:00
qtchaos
34168a7037 feat: add autoplay configurability with VITE_ALLOW_AUTOPLAY and custom proxy 2024-04-01 00:43:00 +03:00
Jorrin
a392f943f8 Merge pull request #1071 from movie-web/dev
Version 4.6.4: Fixing showbox once again
2024-03-31 21:07:10 +02:00
Jorrin
05a714d50f bump versions 2024-03-31 21:00:42 +02:00
Jorrin
7a2f417cdd Merge pull request #1060 from gh-movie-web/weblate-movie-web-website
Translations update from movie-web weblate
2024-03-31 20:58:16 +02:00
qtchaos
20cec61eac feat: add autoplay preference for extension users 2024-03-31 21:55:06 +03:00
Mehdi
5355791486 Translated using Weblate (Persian)
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/fa/
Author: Mehdi <pyrexrj@gmail.com>
2024-03-31 18:40:23 +00:00
Dave
caf5faeb49 Translated using Weblate (Indonesian)
Currently translated at 83.3% (280 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/id/
Author: Dave <dave.turmawan@outlook.com>
2024-03-31 18:40:23 +00:00
Thais Palmer
232fb7e895 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/pt_BR/
Author: Thais Palmer <eu@thaispalmer.com.br>
2024-03-31 18:40:23 +00:00
Dave
f20fe984d3 Translated using Weblate (Indonesian)
Currently translated at 77.6% (261 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/id/
Author: Dave <dave.turmawan@outlook.com>
2024-03-31 18:40:23 +00:00
Dave
35c6d25268 Translated using Weblate (Indonesian)
Currently translated at 76.1% (256 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/id/
Author: Dave <dave.turmawan@outlook.com>
2024-03-31 18:40:23 +00:00
Alex
0ae96309c7 Translated using Weblate (Russian)
Currently translated at 100.0% (336 of 336 strings)

Translation: movie-web/website
Translate-URL: https://weblate.476328473.xyz/projects/movie-web/website/ru/
Author: Alex <aslanych99@mail.ru>
2024-03-31 18:40:23 +00:00
William Oldham
1e0b86badf Merge pull request #1070 from movie-web/fix/hls-audio-not-proxied
Route HLS audio tracks through extension
2024-03-31 19:40:18 +01:00
Jorrin
6a905bf517 Also route hls audio tracks through extension 2024-03-31 20:20:10 +02:00
William Oldham
a5694fa430 Merge pull request #1068 from lonelil/dev
add sync fork workflow
2024-03-31 16:53:49 +01:00
lonelil
d006ed9a49 add sync fork workflow 2024-03-31 23:05:50 +08:00
31 changed files with 2269 additions and 265 deletions

26
.github/workflows/sync.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Sync fork
permissions:
contents: write
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
jobs:
sync:
name: Sync fork
runs-on: ubuntu-latest
if: ${{ github.event.repository.fork }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Sync fork
run: gh repo sync ${{ github.repository }}
env:
GH_TOKEN: ${{ github.token }}
- uses: gautamkrishnar/keepalive-workflow@v1

View File

@@ -23,6 +23,7 @@ ARG ONBOARDING_PROXY_INSTALL_LINK
ARG DISALLOWED_IDS
ARG CDN_REPLACEMENTS
ARG TURNSTILE_KEY
ARG ALLOW_AUTOPLAY="false"
ENV VITE_PWA_ENABLED=${PWA_ENABLED}
ENV VITE_GA_ID=${GA_ID}
@@ -39,6 +40,7 @@ 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}
ENV VITE_ALLOW_AUTOPLAY=${ALLOW_AUTOPLAY}
COPY . ./
RUN pnpm run build

View File

@@ -1,6 +1,6 @@
{
"name": "movie-web",
"version": "4.6.3",
"version": "4.7.0",
"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.5",
"@movie-web/providers": "^2.3.0",
"@noble/hashes": "^1.3.3",
"@plasmohq/messaging": "^0.6.1",
"@react-spring/web": "^9.7.3",
@@ -120,7 +120,7 @@
"tailwindcss-themer": "^4.0.0",
"type-fest": "^4.8.3",
"typescript": "^5.3.3",
"vite": "^5.0.12",
"vite": "^5.0.13",
"vite-plugin-checker": "^0.6.2",
"vite-plugin-package-version": "^1.1.0",
"vite-plugin-pwa": "^0.17.4",

1859
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -155,7 +155,8 @@
"types": {
"movie": "Film",
"show": "Serie"
}
},
"unreleased": "Unveröffentlicht"
},
"navigation": {
"banner": {

View File

@@ -362,7 +362,8 @@
},
"nextEpisode": {
"cancel": "Cancel",
"next": "Next episode"
"next": "Next episode",
"nextSeason": "Next season"
},
"playbackError": {
"badge": "Playback error",
@@ -523,6 +524,9 @@
"thumbnail": "Generate thumbnails",
"thumbnailDescription": "Most of the time, videos don't have thumbnails. You can enable this setting to generate them on the fly but they can make your video slower.",
"thumbnailLabel": "Generate thumbnails",
"autoplay": "Autoplay",
"autoplayDescription": "Automatically play the next episode in a series after reaching the end. Can be enabled by users with the browser extension, a custom proxy, or with the default setup if allowed by the host.",
"autoplayLabel": "Autoplay",
"title": "Preferences"
},
"reset": "Reset",

View File

@@ -312,7 +312,7 @@
},
"noEmbeds": {
"text": "نتوانستیم اطلاعات را پیدا کنیم، لطفا منبع دیگری را امتحان کنید.",
"title": "اطلاعات پیدا نشد"
"title": "هیج اطلاعاتی پیدا نشد"
},
"noStream": {
"text": "فیلم یا سریال شما در این منبع وجود ندارد.",
@@ -372,10 +372,10 @@
"badge": "مشکلی در پخش به وجود آمده",
"errors": {
"errorAborted": "دریافت محتوا با درخواست کاربر لغو شد.",
"errorDecode": "با وجود اینکه قبلا مشخص شده بود که قابل استفاده است، یک خطا در هنگام تلاش برای رمزگشایی رسانه رخ داد که باعث مشکل شد.",
"errorDecode": "با وجود اینکه قبلا مشخص شده بود که قابل استفاده است، یک خطا در هنگام تلاش برای رمزگشایی محتوا رخ داد که باعث مشکل شد.",
"errorGenericMedia": "خطای محتوای ناشناخته رخ داد.",
"errorNetwork": "با وجود اینکه قبلا در دسترس بود، نوعی خطای شبکه رخ داد که مانع از دریافت محتوا شد.",
"errorNotSupported": "محتوا یا ارائه دهنده رسانه پشتیبانی نمی‌شود."
"errorNotSupported": "محتوا یا ارائه دهنده محتوا پشتیبانی نمی‌شود."
},
"homeButton": "بازگشت به خانه",
"text": "مشکلی در پخش محتوا وجود داشت. لطفا دوباره تلاش کنید.",
@@ -386,7 +386,7 @@
"badge": "اکستنشن غیر فعال شد",
"enableExtension": "اکستنشن را فعال کنید",
"homeButton": "خانه",
"text": "شما اکستنشن مووی-وب را نصب کردید. برای شروع استفاده از ان، شما نیاز به فعال کردن آن دارید",
"text": "شما اکستنشن مووی-وب را نصب کردید. برای شروع استفاده از ان، نیاز به فعال کردن آن دارید",
"title": "لطفا اکستنشن را فعال کنید"
},
"items": {
@@ -398,7 +398,7 @@
"badge": "پیدا نشد",
"detailsButton": "نمایش جزئیات",
"homeButton": "بازگشت به خانه",
"text": "ما در ارائه دهندگان جستجو کرده ایم ولی نمی توانیم محتوایی را که به دنبال آن را هستید پیدا کنیم! ما رسانه ها را میزبانی نمی کنیم و هیچ کنترلی بر آنچه در دسترس است نداریم. لطفا برای جزئیات بیشتر روی \"نمایش جزئیات\" در زیر کلیک کنید.",
"text": "ما در ارائه دهندگان جستجو کرده ایم ولی نمی توانیم محتوایی را که به دنبال آن را هستید پیدا کنیم! ما محتواها را میزبانی نمی کنیم و هیچ کنترلی بر آنچه در دسترس است نداریم. لطفا برای جزئیات بیشتر روی \"نمایش جزئیات\" در زیر کلیک کنید.",
"title": "نتونستیم پیداش کنیم"
}
},
@@ -467,7 +467,7 @@
},
"register": {
"cta": "شروع کنید",
"text": "پیشرفت تماشای خود را بین دستگاه‌ها به اشتراک بگذارید و آنها را با یکدیگر همگام سازی کنید.",
"text": "جریان تماشای خود را بین دستگاه‌ها به اشتراک بگذارید و آنها را با یکدیگر همگام سازی کنید.",
"title": "همگام سازی"
},
"title": "حساب کاربری"
@@ -487,7 +487,7 @@
"server": {
"description": "اگر میخواید به یک بک-اند سفارشی برای ذخیره داده متصل شوید، با فعال و ارائه استفاده این لینک ادامه دهید. <0>دستورالعمل ها.</0>",
"label": "سرور سفارشی",
"urlLabel": "لینک سرور سفارشی"
"urlLabel": "لینک سرور کاستوم"
},
"setup": {
"doSetup": "راه اندازی کنید",
@@ -503,7 +503,7 @@
},
"redoSetup": "تنظیم مجدد",
"successStatus": {
"description": "همه چیز برای شروع تماشای محتوای مورد علاقه‌تان آماده است.",
"description": "همه چیز برای شروع تماشای فیلم مورد علاقه‌تان آماده است.",
"title": "همه چیز تنظیم شده است!"
},
"unsetStatus": {
@@ -516,7 +516,7 @@
"addButton": "اضافه کردن worker جدید",
"description": "برای ایجاد عملکرد برنامه، تمام ترافیک از طریق پروکسی ها هدایت می شود. اگر میخواید این کار انجام دهید حتما از worker های خودتان استفاده کنید. <0>دستورالعمل ها.</0>",
"emptyState": "هنوز هیچ worker ای وجود ندارد، یکی اضافه کنید",
"label": "استفاده از worker های پروکسی سفارشی",
"label": "از پروکسی worker کاستوم استفاده کنید",
"urlLabel": "لینک worker ها",
"urlPlaceholder": "https://"
}
@@ -525,7 +525,7 @@
"language": "زبان برنامه",
"languageDescription": "زبان برای کل برنامه اعمال شد.",
"thumbnail": "ایجاد تامبنیل",
"thumbnailDescription": "بیشتر اوقات، ویدیوها تامبنیل ندارند. شما می توانید این تنظیم را فعال کنید تا آنها را در لحظه تولید کنید، اما آنها می توانند ویدیوی شما را کندتر کنند.",
"thumbnailDescription": "بیشتر اوقات، ویدیوها تامبنیل ندارند. شما می توانید این تنظیم را فعال کنید تا آنها را در لحظه ببینید، اما آنها می توانند ویدیوی شما را کندتر کنند.",
"thumbnailLabel": "ایجاد تامبنیل",
"title": "اولویت ها"
},
@@ -536,8 +536,8 @@
"appVersion": "نسخه برنامه",
"backendUrl": "لینک بک-اند",
"backendVersion": "نسخه بک-اند",
"hostname": "نام میزبان",
"insecure": "نا امن",
"hostname": "نام هاست",
"insecure": "ناامن",
"notLoggedIn": "شما وارد نشده اید",
"secure": "امن",
"title": "اطلاعات برنامه",
@@ -551,7 +551,7 @@
"colorLabel": "رنگ",
"previewQuote": "نباید بترسم، ترس قاتل ذهن است.",
"textSizeLabel": "اندازه متن",
"title": "زیرنویس"
"title": "زیرنویسها"
},
"unsaved": "شما تغییرات ذخیره نشده دارید"
}

View File

@@ -7,7 +7,7 @@
"title": "D'où vient le contenu ?"
},
"q2": {
"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.",
"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": {
@@ -25,26 +25,26 @@
"deviceNameLabel": "Nom de l'appareil",
"deviceNamePlaceholder": "Téléphone personnel",
"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": "Passphrase",
"title": "Votre passphrase"
"description": "Le nom d'utilisateur et le mot de passe sont obtenus à partir de votre phrase d'accès. Vous devrez la saisir pour accéder à votre compte, alors gardez-la précieusement",
"next": "J'ai sauvegardé ma phrase d'accès",
"passphraseFrameLabel": "Phrase d'accès",
"title": "Votre phrase d'accès"
},
"hasAccount": "Avez-vous déjà un compte? <0>Connectez-vous ici.</0>",
"login": {
"description": "Veuillez saisir votre passphrase pour accéder à votre compte",
"description": "Veuillez saisir votre phrase d'accès pour accéder à votre compte",
"deviceLengthError": "Veuillez saisir un nom d'appareil",
"passphraseLabel": "Passphrase de 12 mots",
"passphrasePlaceholder": "Passphrase",
"passphraseLabel": "Phrase d'accès de 12 mots",
"passphrasePlaceholder": "Phrase d'accès",
"submit": "Se connecter",
"title": "Connectez-vous à votre compte",
"validationError": "Passphrase incorrecte ou incomplète"
"validationError": "Phrase d'accès incorrecte ou incomplète"
},
"register": {
"information": {
"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",
"header": "Veuillez entrer un nom pour votre appareil, choisir vos couleurs et une icône utilisateur de votre choix",
"icon": "Icône d'utilisateur",
"next": "Suivant",
"title": "Informations du compte"
@@ -63,13 +63,13 @@
"yes": "Je fais confiance à ce serveur"
},
"verify": {
"description": "Veuillez saisir votre passphrase pour confirmer que vous l'avez enregistrée et pour créer votre compte",
"description": "Veuillez saisir votre phrase d'accès pour confirmer que vous l'avez enregistrée et pour créer votre compte",
"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ée",
"noMatch": "La phrase d'accès ne correspond pas",
"passphraseLabel": "Votre phrase d'accès de 12 mots",
"recaptchaFailed": "La validation ReCaptcha a échoué",
"register": "Créer un compte",
"title": "Resaisissez votre passphrase"
"title": "Ressaisissez votre phrase d'accès"
}
},
"errors": {
@@ -82,7 +82,7 @@
"footer": {
"legal": {
"disclaimer": "Avertissement",
"disclaimerText": "Le site movie-web ne stocke pas de fichiers, mais propose des liens vers des services externes. Les problèmes juridiques doivent être traités avec les fournisseurs et les hébergeurs de fichiers. Les fichiers multimédias diffusés par les fournisseurs de vidéos ne sont pas couverts par movie-web."
"disclaimerText": "movie-web ne stocke pas de fichiers, mais propose des liens vers des services externes. Les problèmes juridiques doivent être traités avec les fournisseurs et les hébergeurs de fichiers. Les fichiers multimédias diffusés par les fournisseurs de vidéos ne sont pas couverts par movie-web."
},
"links": {
"discord": "Discord",
@@ -115,14 +115,14 @@
},
"search": {
"allResults": "C'est tout ce que nous avons!",
"failed": "Le média n'a pas été trouvé, veuillez réessayez!",
"failed": "Le média n'a pas été trouvé, veuillez réessayer!",
"loading": "Chargement...",
"noResults": "Nous n'avons rien trouvé!",
"placeholder": {
"default": "Que voulez-vous voir?",
"extra": [
"Que voulez-vous explorer ?",
"Que y a-t-il dans votre liste de lecture?",
"Qu'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 ?"
]
@@ -155,11 +155,12 @@
"types": {
"movie": "Film",
"show": "Série"
}
},
"unreleased": "Non publié"
},
"navigation": {
"banner": {
"offline": "Vérifiez votre connexion internet"
"offline": "Veuillez vérifier votre connexion internet"
},
"menu": {
"about": "À propos de nous",
@@ -173,24 +174,24 @@
"notFound": {
"badge": "Introuvable",
"goHome": "Retourner à l'accueil",
"message": "Nous avons cherché partout : sous les poubelles, dans le placard, derrière le proxy, mais nous n'avons finalement pas trouvé la page que vous cherchez.",
"message": "Nous avons cherché partout : sous les poubelles, dans le placard, derrière le proxy, mais nous n'avons pas pu trouver la page que vous cherchez.",
"title": "Impossible de trouver cette page"
},
"onboarding": {
"defaultConfirm": {
"cancel": "Annuler",
"confirm": "Utiliser la configuration par défaut",
"description": "La configuration par défaut n'offre pas les meilleurs flux et peut être insupportablement lente.",
"description": "La configuration par défaut n'offre pas les meilleurs flux et peut-être insupportablement lente.",
"title": "Êtes-vous sûr ?"
},
"extension": {
"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> 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.",
"explainerIos": "Malheureusement, l'extension web n'est pas prise en charge sur iOS, appuyez sur <bold>Retour</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",
"notDetecting": "L'extension est installée sur Chrome mais le site ne la détecte pas ? Essayez de rafraîchir la page !",
"notDetecting": "L'extension est installée sur Chrome, mais le site ne la détecte pas ? Essayez de rafraîchir la page !",
"notDetectingAction": "Rafraîchir la page",
"status": {
"disallowed": "L'extension n'est pas activée pour cette page",
@@ -204,12 +205,12 @@
"title": "Commençons par une extension"
},
"proxy": {
"back": "Revenir en arrière",
"back": "Retour",
"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",
"errorInvalidUrl": "URL non valide",
"errorNotProxy": "Je m'attendais à un proxy mais j'ai obtenu un site Web",
"errorNotProxy": "Je m'attendais à un proxy, mais j'ai obtenu un site Web",
"label": "URL du proxy",
"placeholder": "https://"
},
@@ -221,17 +222,17 @@
"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>Utiliser le flux par défaut</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 pour navigateur et accédez aux meilleures sources.",
"quality": "Meilleur qualité",
"quality": "Meilleure qualité",
"title": "Extension du navigateur"
},
"proxy": {
"action": "Configurez le proxy",
"description": "Configurez un proxy en seulement 5 minutes et accédez à d'excellentes sources.",
"description": "Configurez un proxy en seulement cinq minutes et accédez à d'excellentes sources.",
"quality": "Bonne qualité",
"title": "Proxy personnalisé"
}
@@ -256,14 +257,14 @@
"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",
"hlsDisclaimer": "Les téléchargements sont effectués directement auprès du fournisseur. movie-web n'a aucun contrôle sur la façon dont les téléchargements sont fournis.<br /><br />Veuillez noter que vous téléchargez une liste de lecture HLS, <bold>il n'est pas recommandé de la télécharger si vous n'êtes pas familier avec les formats de streaming avancés. </bold>. Essayez différentes sources pour différents formats.",
"hlsDisclaimer": "Les téléchargements sont effectués directement auprès du fournisseur. movie-web n'a aucun contrôle sur la façon dont les téléchargements sont fournis.<br /><br />Veuillez noter que vous téléchargez une liste de lecture HLS, <bold>il n'est pas recommandé de la télécharger si vous n'êtes pas familier avec les formats de streaming avancés</bold>. Essayez différentes sources pour différents formats.",
"onAndroid": {
"1": "Pour télécharger sur Android, cliquez sur le bouton de téléchargement puis, sur la nouvelle page, <bold>tapez et maintenez </bold> sur la vidéo, puis sélectionnez <bold>enregistrer</bold>.",
"1": "Pour télécharger sur Android, cliquez sur le bouton de téléchargement, puis, sur la nouvelle page, <bold>tapez et maintenez</bold> sur la vidéo, et sélectionnez <bold>enregistrer</bold>.",
"shortTitle": "Télécharger / Android",
"title": "Téléchargement sur Android"
},
"onIos": {
"1": "Pour télécharger sur iOS, cliquez sur le bouton de téléchargement puis, sur la nouvelle page, cliquez sur <bold><ios_share /></bold>, puis <bold>Enregistrer dans les fichiers <ios_files /></bold>.",
"1": "Pour télécharger sur iOS, cliquez sur le bouton de téléchargement, puis, sur la nouvelle page, cliquez sur <bold><ios_share /></bold>, et <bold>Enregistrer dans les fichiers <ios_files /></bold>.",
"shortTitle": "Télécharger / iOS",
"title": "Télécharger sur iOS"
},
@@ -281,7 +282,7 @@
"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."
"unairedEpisodes": "Un ou plusieurs épisodes de cette saison ont été désactivés, car ils n'ont pas encore été diffusés."
},
"playback": {
"speedLabel": "Vitesse de lecture",
@@ -290,7 +291,7 @@
"quality": {
"automaticLabel": "Qualité automatique",
"hint": "Vous pouvez essayer de <0>changer de source</0> pour obtenir différentes options de qualité.",
"iosNoQuality": "En raison des limitations définies par Apple, la sélection de la qualité n'est pas disponible sur iOS pour cette source. Vous pouvez essayer <0>de passer à une autre source</0> pour obtenir des options de qualité différentes.",
"iosNoQuality": "En raison des limitations définies par Apple, la sélection de la qualité n'est pas disponible sur iOS pour cette source. Vous pouvez essayer <0>de changer de source</0> pour obtenir des options de qualité différentes.",
"title": "Qualité"
},
"settings": {
@@ -311,7 +312,7 @@
},
"noEmbeds": {
"text": "Nous n'avons pas trouvé de liens, veuillez essayer une autre source.",
"title": "Pas d'embeds trouvés"
"title": "Pas d'intégrations (embeds) trouvées"
},
"noStream": {
"text": "Cette source n'a pas de flux pour ce film ou cette série.",
@@ -374,13 +375,20 @@
"errorDecode": "Bien qu'elle ait été jugée utilisable, une erreur s'est produite lors de la tentative de décodage de la ressource multimédia, ce qui a entraîné une erreur.",
"errorGenericMedia": "Une erreur de média inconnue est survenue.",
"errorNetwork": "Une erreur de réseau s'est produite qui a empêché la récupération du média, bien qu'il ait été disponible auparavant.",
"errorNotSupported": "L'objet du media ou de la source du média n'est pas supporté."
"errorNotSupported": "L'objet du média ou de la source du média n'est pas supporté."
},
"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é !"
},
"scraping": {
"extensionFailure": {
"badge": "Extension désactivée",
"enableExtension": "Activer l'extension",
"homeButton": "Revenir à l'accueil",
"text": "Vous avez installé l'extension movie-web. Pour commencer à l'utiliser, vous devez activer l'extension pour ce site.",
"title": "Veuillez activer l'extension"
},
"items": {
"failure": "Une erreur est survenue",
"notFound": "N'a pas la vidéo",
@@ -409,7 +417,7 @@
},
"screens": {
"dmca": {
"text": "Bienvenue sur la page de contact DMCA de movie-web ! Nous respectons les droits de propriété intellectuelle et souhaitons répondre rapidement à toute question relative aux droits d'auteur. Si vous pensez que votre œuvre protégée par des droits d'auteur a été utilisée de manière inappropriée sur notre plateforme, veuillez envoyer une notification DMCA détaillée à l'adresse électronique ci-dessous. Veuillez inclure une description du matériel protégé par des droits d'auteur, vos coordonnées et une déclaration de bonne foi. Nous nous engageons à résoudre ces problèmes rapidement et vous remercions de votre coopération pour que movie-web reste un lieu respectueux de la créativité et des droits d'auteur.",
"text": "Bienvenue sur la page de contact DMCA de movie-web ! Nous respectons les droits de propriété intellectuelle et souhaitons répondre rapidement à toute question relative aux droits d'auteur. Si vous pensez que votre œuvre protégée par des droits d'auteur a été utilisée de manière inappropriée sur notre plateforme, veuillez envoyer une notification DMCA détaillée à l'adresse électronique ci-dessous. Veuillez inclure une description du matériel protégé par des droits d'auteur, vos coordonnées et une déclaration de bonne foi. Nous nous engageons à résoudre ces problèmes rapidement et vous remercions pour votre coopération pour que movie-web reste un lieu respectueux de la créativité et des droits d'auteur.",
"title": "DMCA"
},
"loadingApp": "Chargement de l'application",
@@ -421,7 +429,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ée.",
"failed": "La migration de vos données a échoué.",
"inProgress": "Veuillez patienter, nous sommes en train de migrer vos données. Cela ne devrait pas prendre longtemps."
}
},
@@ -458,7 +466,7 @@
"userIcon": "Icône de l'utilisateur"
},
"register": {
"cta": "Démarrer",
"cta": "Commencer",
"text": "Partagez la progression de vos films et séries entre vos appareils et gardez-les synchronisés.",
"title": "Synchroniser au Cloud"
},
@@ -471,7 +479,7 @@
"default": "Défaut",
"gray": "Gris",
"red": "Rouge",
"teal": "Saphir"
"teal": "Bleu canard"
},
"title": "Apparence"
},
@@ -495,8 +503,8 @@
},
"redoSetup": "Refaire la configuration",
"successStatus": {
"description": "Tout est réuni pour que vous puissiez commencer à regarder vos médias préférés.",
"title": "Tout est mis en place !"
"description": "Tout prêt pour que vous puissiez commencer à regarder vos médias préférés.",
"title": "Tout est en place !"
},
"unsetStatus": {
"description": "Pour commencer le processus de configuration, veuillez cliquer sur le bouton à droite.",
@@ -514,11 +522,11 @@
}
},
"preferences": {
"language": "Language de l'application",
"language": "Langage de l'application",
"languageDescription": "Langue appliquée à lensemble de lapplication.",
"thumbnail": "Générer des miniatures",
"thumbnail": "Générer les miniatures",
"thumbnailDescription": "La plupart du temps, les vidéos n'ont pas de miniatures. Vous pouvez activer ce paramètre pour les générer à la volée, mais ils peuvent ralentir votre vidéo.",
"thumbnailLabel": "Générer des miniatures",
"thumbnailLabel": "Générer les miniatures",
"title": "Préférences"
},
"reset": "Réinitialiser",
@@ -526,7 +534,7 @@
"sidebar": {
"info": {
"appVersion": "Version de l'application",
"backendUrl": "URL de Backend",
"backendUrl": "URL du Backend",
"backendVersion": "Version du Backend",
"hostname": "Nom d'hôte",
"insecure": "Non sécurisé",
@@ -539,7 +547,7 @@
},
"subtitles": {
"backgroundBlurLabel": "Flou d'arrière-plan",
"backgroundLabel": "Opacité du fond",
"backgroundLabel": "Opacité de l'arrière-plan",
"colorLabel": "Couleur",
"previewQuote": "Plus l'obscurité est profonde, plus la lumière brille.",
"textSizeLabel": "Taille des textes",

View File

@@ -155,7 +155,8 @@
"types": {
"movie": "मूवी",
"show": "शृंखला"
}
},
"unreleased": "रिलीज़ नहीं हुवा"
},
"navigation": {
"banner": {
@@ -172,7 +173,7 @@
},
"notFound": {
"badge": "नहीं मिला",
"goHome": "घर वापिस जा रहा हूँ",
"goHome": "घर वापिस जाइये",
"message": "हमने हर जगह देखा: डिब्बे के नीचे, कोठरी में, प्रॉक्सी के पीछे लेकिन अंततः वह पेज नहीं मिला जिसे आप ढूंढ रहे थे।",
"title": "वह पृष्ठ नहीं मिल सका"
},
@@ -294,6 +295,7 @@
"title": "गुणवत्ता"
},
"settings": {
"audioItem": "ऑडियो",
"downloadItem": "डाउनलोड",
"enableSubtitles": "उपशीर्षक सक्षम करें",
"experienceSection": "देखने का अनुभव",
@@ -320,8 +322,9 @@
"unknownOption": "अज्ञात"
},
"subtitles": {
"customChoice": "फ़ाइल से उपशीर्षक चुनें",
"customChoice": "फ़ाइल ड्रॉप या अपलोड करें",
"customizeLabel": "अनुकूलित करें",
"dropSubtitleFile": "उपशीर्षक फ़ाइल यहां छोड़ें",
"offChoice": "बंद",
"settings": {
"backlink": "कस्टम उपशीर्षक",
@@ -379,6 +382,13 @@
"title": "वीडियो चलाने में विफल!"
},
"scraping": {
"extensionFailure": {
"badge": "एक्सटेंशन बन्द हें",
"enableExtension": "एक्सटेंशन सक्षम करें",
"homeButton": "घर जाओ",
"text": "आपने मूवी-वेब एक्सटेंशन इंस्टॉल कर लिया है. इसका उपयोग शुरू करने के लिए, आपको इस साइट के लिए एक्सटेंशन सक्षम करना होगा।",
"title": "कृपया एक्सटेंशनको सक्षम करें"
},
"items": {
"failure": "त्रुटि हुई",
"notFound": "वीडियो नहीं है",
@@ -536,6 +546,7 @@
}
},
"subtitles": {
"backgroundBlurLabel": "पृष्ठभूमि धुंधला",
"backgroundLabel": "पृष्ठभूमि अस्पष्टता",
"colorLabel": "रंग",
"previewQuote": "मुझे डरना नहीं चाहिए. डर मन हत्यारा है।",

View File

@@ -4,7 +4,7 @@
"faqTitle": "Pertanyaan umum",
"q1": {
"body": "movie-web tidak menyimpan berkas media apapun. Ketika anda mengklik sesuatu untuk ditonton, aplikasi akan mencari berkas media di internet (saat media dimuat dan pada tab 'sumber media' anda dapat melihat sumber mana yang digunakan). movie-web tidak pernah mengunggah media apapun, semua media didapat melalui mekanisme pencarian di internet.",
"title": "Dari mana konten media di sini berasal?"
"title": "Dari mana konten berasal?"
},
"q2": {
"body": "Kami tidak menerima permintaan penambahan serial televisi atau film, movie-web tidak mengelola konten apapun. Semua konten ditonton melalui sumber-sumber dari internet.",
@@ -26,24 +26,24 @@
"deviceNamePlaceholder": "Perangkat personal",
"generate": {
"description": "Kombinasi kata ini berfungsi sebagai nama pengguna sekaligus kata sandi anda. Pastikan untuk menyimpannya dengan aman karena anda memerlukannya untuk masuk ke akun anda",
"next": "Saya sudah menyimpan kombinasi kata di atas",
"next": "Saya sudah menyimpan kombinasi kata",
"passphraseFrameLabel": "Kombinasi kata",
"title": "Kombinasi kata anda"
"title": "Kombinasi kata Anda"
},
"hasAccount": "Sudah memiliki akun? <0>Masuk disini.</0>",
"login": {
"description": "Mohon masukkan kombinasi kata anda untuk masuk ke akun anda",
"description": "Mohon masukkan kombinasi kata anda untuk masuk ke akun Anda",
"deviceLengthError": "Mohon masukkan nama perangkat",
"passphraseLabel": "12 kombinasi kata unik",
"passphrasePlaceholder": "Kombinasi kata",
"submit": "Masuk",
"title": "Masuk ke akun anda",
"validationError": "Kombinasi kata unik salah"
"title": "Masuk ke akun Anda",
"validationError": "Kombinasi kata salah atau tidak lengkap"
},
"register": {
"information": {
"color1": "Gradasi warna profil pertama",
"color2": "Gradasi warna profil kedua",
"color1": "Warna profil pertama",
"color2": "Warna profil kedua",
"header": "Masukkan nama perangkat anda lalu pilih warna latar belakang dan ikon pengguna yang ingin anda gunakan",
"icon": "Ikon pengguna",
"next": "Berikutnya",
@@ -52,29 +52,31 @@
},
"trust": {
"failed": {
"text": "Apakah anda mengkonfigurasi server dengan benar?",
"text": "Apakah Anda mengkonfigurasi server dengan benar?",
"title": "Gagal terhubung dengan server"
},
"host": "Anda ingin terhubung ke <0>{{hostname}}</0> - mohon konfirmasi anda mempercayai server ini sebelum anda membuat akun",
"host": "Anda ingin terhubung ke <0>{{hostname}}</0> - mohon konfirmasi Anda mempercayai server ini sebelum Anda membuat akun",
"no": "Kembali",
"title": "Apakah anda mempercayai server ini?",
"yes": "Saya percaya"
"noHost": "Server belum dikonfigurasi, maka dari itu Anda tidak dapat membuat akun",
"noHostTitle": "Server belum dikonfigurasi!",
"title": "Apakah Anda mempercayai server ini?",
"yes": "Saya percaya server ini"
},
"verify": {
"description": "Mohon masukkan kombinasi kata pribadi anda sebelumnya untuk mengonfirmasi bahwa anda telah menyimpannya untuk melanjutkan proses pembuatan akun",
"description": "Mohon masukkan kombinasi kata pribadi Anda sebelumnya untuk mengonfirmasi bahwa Anda telah menyimpannya untuk melanjutkan proses pembuatan akun",
"invalidData": "Data tidak valid",
"noMatch": "Kombinasi kata tidak cocok",
"passphraseLabel": "12 kombinasi kata pribadi anda",
"passphraseLabel": "12 kombinasi kata Anda",
"recaptchaFailed": "Validasi reCaptcha gagal",
"register": "Buat akun",
"title": "Konfirmasi kombinasi kata pribadi anda"
"title": "Konfirmasi kombinasi kata Anda"
}
},
"errors": {
"badge": "Terjadi masalah",
"details": "Detail eror",
"details": "Rincian kesalahan",
"reloadPage": "Muat ulang halaman",
"showError": "Lihat detail eror",
"showError": "Lihat rincian kesalahan",
"title": "Kami mengalami galat!"
},
"footer": {
@@ -87,7 +89,7 @@
"dmca": "DMCA",
"github": "GitHub"
},
"tagline": "Tonton serial televisi dan film favorit anda dengan aplikasi streaming open source ini."
"tagline": "Tonton serial televisi dan film favorit anda dengan aplikasi streaming sumber terbuka ini."
},
"global": {
"name": "movie-web",
@@ -109,7 +111,7 @@
"sectionTitle": "Lanjut menonton"
},
"mediaList": {
"stopEditing": "Berhenti mengubah"
"stopEditing": "Berhenti menyunting"
},
"search": {
"allResults": "Hanya itu yang kami punya!",
@@ -131,19 +133,19 @@
"day": {
"default": "Apa yang ingin anda tonton sore ini?",
"extra": [
"Lagi pengen nonton genre Adventure? Jurassic Park mungkin cocok buat anda."
"Merasa suka bertualang? Jurassic Park mungkin pilihan cocok untuk Anda."
]
},
"morning": {
"default": "Apa yang ingin anda tonton pagi ini?",
"default": "Apa yang ingin Anda tonton pagi ini?",
"extra": [
"Kayaknya film Before Sunrise bagus deh"
"Saya dengar film Before Sunrise bagus"
]
},
"night": {
"default": "Apa yang ingin anda tonton malam ini?",
"default": "Apa yang ingin Anda tonton malam ini?",
"extra": [
"Capek? Katanya The Exocist rekomended."
"Capek? Saya dengar The Exocist bagus."
]
}
}
@@ -153,11 +155,12 @@
"types": {
"movie": "Film",
"show": "Serial TV"
}
},
"unreleased": "Belum dirilis"
},
"navigation": {
"banner": {
"offline": "Periksa koneksi internet anda"
"offline": "Periksa koneksi internet Anda"
},
"menu": {
"about": "Tentang kami",
@@ -171,17 +174,67 @@
"notFound": {
"badge": "Tidak ditemukan",
"goHome": "Kembali",
"message": "Kami sudah mencari dimana-mana: di bawah tempat sampah, di lemari, di belakang server proxy, tapi tetap gagal menemukan halaman yang anda cari.",
"message": "Kami sudah mencari dimana-mana: di bawah tempat sampah, di lemari, di belakang server proxy, tetapi tidak dapat menemukan halaman yang Anda cari.",
"title": "Gagal menemukan halaman"
},
"onboarding": {
"defaultConfirm": {
"cancel": "Batal",
"title": "Apa kamu yakin?"
"confirm": "Gunakan pengaturan bawaan",
"description": "Pengaturan bawaan tidak memiliki streaming terbaik dan bisa menjadi sangat lambat.",
"title": "Apa Anda yakin?"
},
"extension": {
"back": "Kembali",
"explainerIos": "Sayangnya, ekstensi browser tidak didukung di iOS, Tekan <bold>Kembali<bold> untuk memilih opsi lain."
"explainer": "Menggunakan ekstensi browser, Anda dapat mendapatkan streaming terbaik yang kami tawarkan. Dengan instalasi sederhana.",
"explainerIos": "Sayangnya, ekstensi browser tidak didukung di iOS, Tekan <bold>Kembali<bold> untuk memilih opsi lain.",
"extensionHelp": "Jika Anda telah menginstal ekstensi tetapi tidak terdeteksi, <bold>buka ekstensi melalui menu ekstensi browser Anda</bold> dan ikuti instruksi di layar.",
"linkChrome": "Instal ekstensi Chrome",
"linkFirefox": "Instal ekstensi Firefox",
"notDetecting": "Terinstal di Chrome tetapi situs tidak mendeteksinya? Coba muat ulang halaman!",
"notDetectingAction": "Muat ulang halaman",
"status": {
"disallowed": "Ekstensi tidak diaktifkan untuk halaman ini",
"disallowedAction": "Aktifkan ekstensi",
"failed": "Gagal meminta status",
"loading": "Menunggu Anda menginstal ekstensi",
"outdated": "Versi ekstensi terlalu tua",
"success": "Ekstensi berkerja sesuai harapan!"
},
"submit": "Lanjutkan",
"title": "Mari mulai dengan ekstensi"
},
"proxy": {
"back": "Kembali",
"explainer": "Menggunakan metode proxy, Anda dapat mendapatkan streaming berkualitas baik dengan membuat layanan proxy mandiri.",
"input": {
"errorConnection": "Tidak dapat terhubung ke proxy",
"errorInvalidUrl": "URL tidak valid",
"errorNotProxy": "Mengharapkan proxy tapi menerima situs",
"label": "URL proxy",
"placeholder": "https://"
},
"link": "Belajar cara membuat proxy",
"title": "Mari buat proxy baru"
},
"start": {
"explainer": "Untuk mendapatkan streaming terbaik, Anda perlu memilih metode streaming yang Anda ingin gunakan.",
"options": {
"default": {
"text": "Saya tidak menginginkan streaming kualitas baik, <0 /> <1>gunakan pengaturan bawaan</1>"
},
"extension": {
"action": "Instal ekstensi",
"description": "Instal ekstensi browser dan dapatkan akses ke sumber terbaik.",
"quality": "Kualitas terbaik",
"title": "Ekstensi browser"
},
"proxy": {
"action": "Atur proxy",
"description": "Atur proxy dalam hanya 5 menit dan dapatkan akses ke sumber terbaik.",
"quality": "Kualitas baik"
}
}
}
},
"overlays": {
@@ -197,12 +250,13 @@
},
"menus": {
"downloads": {
"copyHlsPlaylist": "Salin tautan daftar putar HLS",
"disclaimer": "Tautan unduhan diambil langsung dari penyedia pihak ketiga. Aplikasi ini tidak memiliki kendali bagaimana unduhan disediakan.",
"downloadSubtitle": "Unduh subtitle",
"downloadSubtitle": "Unduh subtitle saat ini",
"downloadVideo": "Unduh media",
"hlsDisclaimer": "Tautan unduhan diambil langsung dari penyedia pihak ketiga. Aplikasi ini tidak memiliki kendali bagaimana unduhan disediakan. Harap diperhatikan, anda akan mengunduh HLS playlist, media ini hanya ditunjukan bagi pengguna tingkat lanjut.",
"hlsDisclaimer": "Unduhan didapatkan langsung dari penyedia. movie-web tidak memiliki kendali bagaimana unduan disediakan.<br /><br />Harap diperhatikan bahwa Anda mengunduh daftar putar HLS, <bold>ini tidak direkomendasikan untuk diunduh jika Anda tidak terbiasa dengan format streaming tingkat lanjut</bold>Coba sumber berbeda untuk format berbeda.",
"onAndroid": {
"1": "Untuk mengunduh di Android, klik tombol unduh, lalu di halaman baru <bold>klik dan tahan</bold> pada video, lalu pilih <bold>save</bold>.",
"1": "Untuk mengunduh di Android, klik tombol unduh, lalu di halaman baru <bold>klik dan tahan</bold> pada video, lalu pilih <bold>simpan</bold>.",
"shortTitle": "Unduh / Android",
"title": "Unduh di Android"
},
@@ -220,11 +274,12 @@
},
"episodes": {
"button": "Episode",
"emptyState": "Tidak ada episode di season ini, check lagi nanti!",
"emptyState": "Tidak ada episode di season ini, periksa kembali nanti!",
"episodeBadge": "E{{episode}}",
"loadingError": "Eror memuat season",
"loadingError": "Gagal memuat season",
"loadingList": "Memuat...",
"loadingTitle": "Memuat..."
"loadingTitle": "Memuat...",
"unairedEpisodes": "Satu atau lebih episode dalam season ini telah dinonaktifkan karena mereka belum ditayangkan."
},
"playback": {
"speedLabel": "Kecepatan pemutar",
@@ -232,13 +287,13 @@
},
"quality": {
"automaticLabel": "Otomatis",
"hint": "Anda dapat mencoba <0>mengganti sumber media</0> untuk mendapatkan opsi kualitas yang berbeda.",
"iosNoQuality": "Karena keterbatasan dari Apple, opsi kualitas pada sumber ini tidak tersedia untuk iOS. Anda dapat mencoba <0>mengganti sumber media</0> untuk mendapatkan opsi kualitas yang berbeda.",
"hint": "Anda dapat mencoba <0>mengganti sumber</0> untuk mendapatkan opsi kualitas yang berbeda.",
"iosNoQuality": "Karena keterbatasan dari Apple, opsi kualitas pada sumber ini tidak tersedia untuk iOS. Anda dapat mencoba <0>mengganti sumber</0> untuk mendapatkan opsi kualitas yang berbeda.",
"title": "Kualitas"
},
"settings": {
"downloadItem": "Unduh",
"enableSubtitles": "Hidupkan subtitle",
"enableSubtitles": "Aktifkan subtitle",
"experienceSection": "Pengaturan tambahan",
"playbackItem": "Pengaturan pemutar",
"qualityItem": "Kualitas",
@@ -248,22 +303,22 @@
},
"sources": {
"failed": {
"text": "Terjadi galat saat mencoba mencari media, mohon pilih sumber yang lain.",
"text": "Terjadi galat saat mencoba mencari media, mohon pilih sumber lain.",
"title": "Gagal memuat data"
},
"noEmbeds": {
"text": "Kami tidak dapat menemukan tautan, mohon pilih sumber yang lain.",
"text": "Kami tidak dapat menemukan tautan, mohon pilih sumber lain.",
"title": "Tautan tidak ditemukan"
},
"noStream": {
"text": "Sumber ini tidak memiliki media untuk film atau seri yang anda cari.",
"text": "Sumber ini tidak memiliki media untuk film atau seri yang Anda cari.",
"title": "Tidak ada media"
},
"title": "Sumber",
"unknownOption": "Tidak diketahui"
},
"subtitles": {
"customChoice": "Pilih subtitle dari file",
"customChoice": "Jatuhkan atau unggah berkas",
"customizeLabel": "Sesuaikan",
"offChoice": "Matikan",
"settings": {

View File

@@ -122,7 +122,7 @@
"default": "무엇을 보고 싶으신가요?",
"extra": [
"무엇을 탐험하고 싶으신가요?",
null,
"관심 목록에 무엇이 있나요?",
"당신이 가장 좋아하는 영화는?",
"당신이 가장 좋아하는 시리즈는?"
]
@@ -536,6 +536,7 @@
}
},
"subtitles": {
"backgroundBlurLabel": "배경 흐림",
"backgroundLabel": "배경 투명도",
"colorLabel": "색상",
"previewQuote": "두려워해서는 안 됩니다. 두려움은 마음을 죽이는 존재입니다.",

View File

@@ -155,7 +155,8 @@
"types": {
"movie": "चलचित्र",
"show": "कार्यक्रम"
}
},
"unreleased": "रिलीज नभएको"
},
"navigation": {
"banner": {
@@ -294,6 +295,7 @@
"title": "क्वालिटी"
},
"settings": {
"audioItem": "आवाज",
"downloadItem": "डाउनलोड",
"enableSubtitles": "उपशीर्षकहरू सक्षम गर्नुहोस्",
"experienceSection": "हेर्ने अनुभव",
@@ -320,8 +322,9 @@
"unknownOption": "अज्ञात"
},
"subtitles": {
"customChoice": "फाइलबाट उपशीर्षक चयन गर्नुहोस्",
"customChoice": "ड्रप वा फाइल अपलोड गर्नुहोस्",
"customizeLabel": "अनुकूलन गर्नुहोस्",
"dropSubtitleFile": "सबटाइटल फाइल यहाँ छोड्नुहोस्",
"offChoice": "बन्द",
"settings": {
"backlink": "अनुकूलन उपशीर्षकहरू",
@@ -379,6 +382,13 @@
"title": "भिडियो प्ले गर्न असफल भयो!"
},
"scraping": {
"extensionFailure": {
"badge": "एक्स्टेन्सन बन्द छ",
"enableExtension": "एक्स्टेन्सन सक्षम गर्नुहोस्",
"homeButton": "होम् जाउँ",
"text": "तपाईंले चलचित्र-वेब एक्स्टेन्सन स्थापना गर्नुभएको छ। यसलाई प्रयोग गर्न सुरु गर्न, तपाईंले यो साइटको लागिएक्स्टेन्सन सक्षम गर्न आवश्यक छ।",
"title": "कृपया एक्स्टेन्सन सक्षम गर्नुहोस्"
},
"items": {
"failure": "त्रुटि भयो",
"notFound": "भिडियो छैन",
@@ -536,6 +546,7 @@
}
},
"subtitles": {
"backgroundBlurLabel": "पृष्ठभूमि धमिलो",
"backgroundLabel": "पृष्ठभूमि अस्पष्टता",
"colorLabel": "रङ",
"previewQuote": "म डराउनु हुँदैन। डर दिमागको हत्यारा हो।",

View File

@@ -155,7 +155,8 @@
"types": {
"movie": "Film",
"show": "Serie"
}
},
"unreleased": "Niet uitgebracht"
},
"navigation": {
"banner": {

View File

@@ -155,7 +155,8 @@
"types": {
"movie": "Filme",
"show": "Série"
}
},
"unreleased": "Não lançado"
},
"navigation": {
"banner": {
@@ -381,6 +382,13 @@
"title": "Falha ao reproduzir o vídeo!"
},
"scraping": {
"extensionFailure": {
"badge": "Extensão desativada",
"enableExtension": "Ativar extensão",
"homeButton": "Ir para o início",
"text": "Você instalou a extensão movie-web. Para começar a usá-la, você precisa ativar a extensão neste site.",
"title": "Por favor, ative a extensão"
},
"items": {
"failure": "Ocorreu um erro",
"notFound": "Não tem o vídeo",

View File

@@ -322,7 +322,7 @@
"unknownOption": "Неизвестный"
},
"subtitles": {
"customChoice": "Перетащите или загрузите файл",
"customChoice": "Перетащите файл или нажмите для загрузки",
"customizeLabel": "Настроить",
"dropSubtitleFile": "Перетащите файл субтитров",
"offChoice": "Выключить",

View File

@@ -155,7 +155,8 @@
"types": {
"movie": "Film",
"show": "Serija"
}
},
"unreleased": "Neizdano"
},
"navigation": {
"banner": {
@@ -294,6 +295,7 @@
"title": "Kvaliteta"
},
"settings": {
"audioItem": "Zvok",
"downloadItem": "Prenesi",
"enableSubtitles": "Vklopi podnapise",
"experienceSection": "Izkušnje ogleda",
@@ -320,8 +322,9 @@
"unknownOption": "Neznano"
},
"subtitles": {
"customChoice": "Izberi podnapise iz datoteke",
"customChoice": "Povleci in naloži datoteko",
"customizeLabel": "Prilagodi",
"dropSubtitleFile": "Povleci datoteko s podnapisi tukaj",
"offChoice": "Off",
"settings": {
"backlink": "Podnapisi po meri",
@@ -379,6 +382,13 @@
"title": "Ni uspelo predvajati videoposnetka!"
},
"scraping": {
"extensionFailure": {
"badge": "Razširitev brskalnika je onemogočena",
"enableExtension": "Vklopi razširitev brskalnika",
"homeButton": "Pojdi domov",
"text": "Namestili ste razširitev movie-web. Če jo želite začeti uporabljati, morate razširitev omogočiti za to spletno mesto.",
"title": "Prosim vklopite razširitev brskalnika"
},
"items": {
"failure": "Zgodila se je napaka",
"notFound": "Nima videoposnetka",

View File

@@ -155,7 +155,8 @@
"types": {
"movie": "電影",
"show": "節目"
}
},
"unreleased": "尚未上映"
},
"navigation": {
"banner": {
@@ -294,6 +295,7 @@
"title": "品質"
},
"settings": {
"audioItem": "音頻",
"downloadItem": "下載",
"enableSubtitles": "啟用字幕",
"experienceSection": "觀看體驗",
@@ -320,8 +322,9 @@
"unknownOption": "未知"
},
"subtitles": {
"customChoice": "從檔案中選擇字幕",
"customChoice": "把字幕檔案拖入或上傳",
"customizeLabel": "自訂",
"dropSubtitleFile": "將字幕檔案拖曳到這裡",
"offChoice": "關閉",
"settings": {
"backlink": "自訂字幕",
@@ -379,6 +382,13 @@
"title": "無法播放視頻!"
},
"scraping": {
"extensionFailure": {
"badge": "擴充功能已被禁用",
"enableExtension": "啟用擴充功能",
"homeButton": "回到首頁",
"text": "您已安裝該擴充功能, 你要啟用擴充程式去用它。",
"title": "請啟用擴充功能"
},
"items": {
"failure": "發生錯誤",
"notFound": "沒有視頻",

View File

@@ -155,7 +155,8 @@
"types": {
"movie": "电影",
"show": "电视节目"
}
},
"unreleased": "未发布"
},
"navigation": {
"banner": {
@@ -294,6 +295,7 @@
"title": "质量"
},
"settings": {
"audioItem": "音频",
"downloadItem": "下载",
"enableSubtitles": "启用字幕",
"experienceSection": "观看体验",
@@ -320,8 +322,9 @@
"unknownOption": "未知"
},
"subtitles": {
"customChoice": "从文件选取字幕",
"customChoice": "拖入或上传文件",
"customizeLabel": "自定义",
"dropSubtitleFile": "将字幕拖入这里",
"offChoice": "关闭",
"settings": {
"backlink": "自定义字幕",
@@ -379,6 +382,13 @@
"title": "视频播放失败!"
},
"scraping": {
"extensionFailure": {
"badge": "扩展程序已禁用",
"enableExtension": "启用扩展程序",
"homeButton": "返回首页",
"text": "您已经安装 movie-web 扩展程序。要开始使用,您需要为此站点启用扩展。",
"title": "请启用扩展程序"
},
"items": {
"failure": "发生了错误",
"notFound": "没有视频",

View File

@@ -9,6 +9,7 @@ import { ExtensionMakeRequestResponse } from "@/backend/extension/plasmo";
export const RULE_IDS = {
PREPARE_STREAM: 1,
SET_DOMAINS_HLS: 2,
SET_DOMAINS_HLS_AUDIO: 3,
};
// for some reason, about 500 ms is needed after

View File

@@ -143,26 +143,37 @@ export function decodeTMDBId(
};
}
const baseURL = "https://api.themoviedb.org/3";
const tmdbBaseUrl1 = "https://api.themoviedb.org/3";
const tmdbBaseUrl2 = "https://api.tmdb.org/3";
const apiKey = conf().TMDB_READ_API_KEY;
const headers = {
const tmdbHeaders = {
accept: "application/json",
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,
params: {
...params,
},
});
return res;
try {
return await mwFetch<T>(encodeURI(url), {
headers: tmdbHeaders,
baseURL: tmdbBaseUrl1,
params: {
...params,
},
signal: AbortSignal.timeout(5000),
});
} catch (err) {
return mwFetch<T>(encodeURI(url), {
headers: tmdbHeaders,
baseURL: tmdbBaseUrl2,
params: {
...params,
},
signal: AbortSignal.timeout(30000),
});
}
}
export async function multiSearch(

View File

@@ -1,13 +1,20 @@
import classNames from "classnames";
import { useCallback } from "react";
import { useCallback, useEffect, useRef } from "react";
import { useTranslation } from "react-i18next";
import { useAsync } from "react-use";
import { getMetaFromId } from "@/backend/metadata/getmeta";
import { MWMediaType, MWSeasonMeta } from "@/backend/metadata/types/mw";
import { Icon, Icons } from "@/components/Icon";
import { usePlayerMeta } from "@/components/player/hooks/usePlayerMeta";
import { Transition } from "@/components/utils/Transition";
import { PlayerMeta } from "@/stores/player/slices/source";
import { usePlayerStore } from "@/stores/player/store";
import { usePreferencesStore } from "@/stores/preferences";
import { useProgressStore } from "@/stores/progress";
import { isAutoplayAllowed } from "@/utils/autoplay";
import { hasAired } from "../utils/aired";
function shouldShowNextEpisodeButton(
time: number,
@@ -39,6 +46,45 @@ function Button(props: {
);
}
function useSeasons(mediaId: string, isLastEpisode: boolean = false) {
const state = useAsync(async () => {
if (isLastEpisode) {
const data = await getMetaFromId(MWMediaType.SERIES, mediaId ?? "");
if (data?.meta.type !== MWMediaType.SERIES) return null;
return data.meta.seasons;
}
}, [mediaId, isLastEpisode]);
return state;
}
function useNextSeasonEpisode(
nextSeason: MWSeasonMeta | undefined,
mediaId: string,
) {
const state = useAsync(async () => {
if (nextSeason) {
const data = await getMetaFromId(
MWMediaType.SERIES,
mediaId ?? "",
nextSeason?.id,
);
if (data?.meta.type !== MWMediaType.SERIES) return null;
const nextSeasonEpisodes = data?.meta?.seasonData?.episodes
.filter((episode) => hasAired(episode.air_date))
.map((episode) => ({
number: episode.number,
title: episode.title,
tmdbId: episode.id,
}));
if (nextSeasonEpisodes.length > 0) return nextSeasonEpisodes[0];
}
}, [mediaId, nextSeason?.id]);
return state;
}
export function NextEpisodeButton(props: {
controlsShowing: boolean;
onChange?: (meta: PlayerMeta) => void;
@@ -57,8 +103,24 @@ export function NextEpisodeButton(props: {
(s) => s.setShouldStartFromBeginning,
);
const updateItem = useProgressStore((s) => s.updateItem);
const enableAutoplay = usePreferencesStore((s) => s.enableAutoplay);
const isLastEpisode =
meta?.episode?.number === meta?.episodes?.at(-1)?.number;
const seasons = useSeasons(meta?.tmdbId ?? "", isLastEpisode);
const nextSeason = seasons.value?.find(
(season) => season.number === (meta?.season?.number ?? 0) + 1,
);
const nextSeasonEpisode = useNextSeasonEpisode(
nextSeason,
meta?.tmdbId ?? "",
);
let show = false;
const hasAutoplayed = useRef(false);
if (showingState === "always") show = true;
else if (showingState === "hover" && props.controlsShowing) show = true;
if (isHidden || status !== "playing" || duration === 0) show = false;
@@ -70,14 +132,23 @@ export function NextEpisodeButton(props: {
? bottom
: "bottom-[calc(3rem+env(safe-area-inset-bottom))]";
const nextEp = meta?.episodes?.find(
(v) => v.number === (meta?.episode?.number ?? 0) + 1,
);
const nextEp = isLastEpisode
? nextSeasonEpisode.value
: meta?.episodes?.find(
(v) => v.number === (meta?.episode?.number ?? 0) + 1,
);
const loadNextEpisode = useCallback(() => {
if (!meta || !nextEp) return;
const metaCopy = { ...meta };
metaCopy.episode = nextEp;
metaCopy.season =
isLastEpisode && nextSeason
? {
...nextSeason,
tmdbId: nextSeason.id,
}
: metaCopy.season;
setShouldStartFromBeginning(true);
setDirectMeta(metaCopy);
props.onChange?.(metaCopy);
@@ -93,8 +164,22 @@ export function NextEpisodeButton(props: {
props,
setShouldStartFromBeginning,
updateItem,
isLastEpisode,
nextSeason,
]);
useEffect(() => {
if (!enableAutoplay || metaType !== "show") return;
const onePercent = duration / 100;
const isEnding = time >= duration - onePercent && duration !== 0;
if (duration === 0) hasAutoplayed.current = false;
if (isEnding && isAutoplayAllowed() && !hasAutoplayed.current) {
hasAutoplayed.current = true;
loadNextEpisode();
}
}, [duration, enableAutoplay, loadNextEpisode, metaType, time]);
if (!meta?.episode || !nextEp) return null;
if (metaType !== "show") return null;
@@ -121,7 +206,9 @@ export function NextEpisodeButton(props: {
className="bg-buttons-primary hover:bg-buttons-primaryHover text-buttons-primaryText flex justify-center items-center"
>
<Icon className="text-xl mr-1" icon={Icons.SKIP_EPISODE} />
{t("player.nextEpisode.next")}
{isLastEpisode && nextEp
? t("player.nextEpisode.nextSeason")
: t("player.nextEpisode.next")}
</Button>
</div>
</Transition>

View File

@@ -1,23 +1,21 @@
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { Link } from "react-router-dom";
import { Icon, Icons } from "@/components/Icon";
export function BackLink(props: { url: string }) {
const { t } = useTranslation();
const navigate = useNavigate();
return (
<div className="flex items-center">
<button
type="button"
onClick={() => navigate(props.url)}
<Link
to={props.url}
className="py-1 -my-1 px-2 -mx-2 tabbable rounded-lg flex items-center cursor-pointer text-type-secondary hover:text-white transition-colors duration-200 font-medium"
>
<Icon className="mr-2" icon={Icons.ARROW_LEFT} />
<span className="md:hidden">{t("player.back.short")}</span>
<span className="hidden md:block">{t("player.back.default")}</span>
</button>
</Link>
</div>
);
}

View File

@@ -12,6 +12,7 @@ import {
} from "@/components/player/display/displayInterface";
import { handleBuffered } from "@/components/player/utils/handleBuffered";
import { getMediaErrorDetails } from "@/components/player/utils/mediaErrorDetails";
import { useLanguageStore } from "@/stores/language";
import {
LoadableSource,
SourceQuality,
@@ -83,7 +84,14 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
function reportAudioTracks() {
if (!hls) return;
const currentTrack = hls.audioTracks[hls.audioTrack];
const currentLanguage = useLanguageStore.getState().language;
const audioTracks = hls.audioTracks;
const languageTrack = audioTracks.find((v) => v.lang === currentLanguage);
if (languageTrack) {
hls.audioTrack = audioTracks.indexOf(languageTrack);
}
const currentTrack = audioTracks?.[hls.audioTrack ?? 0];
if (!currentTrack) return;
emit("changedaudiotrack", {
id: currentTrack.id.toString(),
label: currentTrack.name,
@@ -129,6 +137,7 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
}
function setupSource(vid: HTMLVideoElement, src: LoadableSource) {
hls = null;
if (src.type === "hls") {
if (canPlayHlsNatively(vid)) {
vid.src = processCdnLink(src.url);
@@ -191,6 +200,21 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
},
});
});
hls.on(Hls.Events.AUDIO_TRACK_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_AUDIO,
targetDomains: chunkUrls,
requestHeaders: {
...src.preferredHeaders,
...src.headers,
},
});
});
}
});
hls.on(Hls.Events.LEVEL_SWITCHED, () => {

View File

@@ -71,9 +71,10 @@ export function KeyboardEvents() {
return;
const k = evt.key;
const keyL = evt.key.toLowerCase();
// Volume
if (["ArrowUp", "ArrowDown", "m"].includes(k)) {
if (["ArrowUp", "ArrowDown", "m", "M"].includes(k)) {
dataRef.current.setShowVolume(true);
if (volumeDebounce.current) clearTimeout(volumeDebounce.current);
@@ -89,7 +90,7 @@ export function KeyboardEvents() {
dataRef.current.setVolume(
(dataRef.current.mediaPlaying?.volume || 0) - 0.15,
);
if (k === "m") dataRef.current.toggleMute();
if (keyL === "m") dataRef.current.toggleMute();
// Video playback speed
if (k === ">" || k === "<") {
@@ -106,9 +107,9 @@ export function KeyboardEvents() {
dataRef.current.display?.setTime(dataRef.current.time + 5);
if (k === "ArrowLeft")
dataRef.current.display?.setTime(dataRef.current.time - 5);
if (k === "j")
if (keyL === "j")
dataRef.current.display?.setTime(dataRef.current.time - 10);
if (k === "l")
if (keyL === "l")
dataRef.current.display?.setTime(dataRef.current.time + 10);
if (k === "." && dataRef.current.mediaPlaying?.isPaused)
dataRef.current.display?.setTime(dataRef.current.time + 1);
@@ -116,18 +117,18 @@ export function KeyboardEvents() {
dataRef.current.display?.setTime(dataRef.current.time - 1);
// Utils
if (k === "f") dataRef.current.display?.toggleFullscreen();
if (k === " ")
if (keyL === "f") dataRef.current.display?.toggleFullscreen();
if (k === " " || keyL === "k")
dataRef.current.display?.[
dataRef.current.mediaPlaying.isPaused ? "play" : "pause"
]();
if (k === "Escape") dataRef.current.router.close();
// captions
if (k === "c") dataRef.current.toggleLastUsed().catch(() => {}); // ignore errors
if (keyL === "c") dataRef.current.toggleLastUsed().catch(() => {}); // ignore errors
// Do a barrell roll!
if (k === "r") {
if (keyL === "r") {
if (dataRef.current.isRolling || evt.ctrlKey || evt.metaKey) return;
dataRef.current.setIsRolling(true);

View File

@@ -51,6 +51,7 @@ export function useSettingsState(
}
| undefined,
enableThumbnails: boolean,
enableAutoplay: boolean,
) {
const [proxyUrlsState, setProxyUrls, resetProxyUrls, proxyUrlsChanged] =
useDerived(proxyUrls);
@@ -84,6 +85,12 @@ export function useSettingsState(
resetEnableThumbnails,
enableThumbnailsChanged,
] = useDerived(enableThumbnails);
const [
enableAutoplayState,
setEnableAutoplayState,
resetEnableAutoplay,
enableAutoplayChanged,
] = useDerived(enableAutoplay);
function reset() {
resetTheme();
@@ -95,6 +102,7 @@ export function useSettingsState(
resetDeviceName();
resetProfile();
resetEnableThumbnails();
resetEnableAutoplay();
}
const changed =
@@ -105,7 +113,8 @@ export function useSettingsState(
backendUrlChanged ||
proxyUrlsChanged ||
profileChanged ||
enableThumbnailsChanged;
enableThumbnailsChanged ||
enableAutoplayChanged;
return {
reset,
@@ -150,5 +159,10 @@ export function useSettingsState(
set: setEnableThumbnailsState,
changed: enableThumbnailsChanged,
},
enableAutoplay: {
state: enableAutoplayState,
set: setEnableAutoplayState,
changed: enableAutoplayChanged,
},
};
}

View File

@@ -122,6 +122,9 @@ export function SettingsPage() {
const enableThumbnails = usePreferencesStore((s) => s.enableThumbnails);
const setEnableThumbnails = usePreferencesStore((s) => s.setEnableThumbnails);
const enableAutoplay = usePreferencesStore((s) => s.enableAutoplay);
const setEnableAutoplay = usePreferencesStore((s) => s.setEnableAutoplay);
const account = useAuthStore((s) => s.account);
const updateProfile = useAuthStore((s) => s.setAccountProfile);
const updateDeviceName = useAuthStore((s) => s.updateDeviceName);
@@ -144,6 +147,7 @@ export function SettingsPage() {
backendUrlSetting,
account?.profile,
enableThumbnails,
enableAutoplay,
);
useEffect(() => {
@@ -196,6 +200,7 @@ export function SettingsPage() {
}
setEnableThumbnails(state.enableThumbnails.state);
setEnableAutoplay(state.enableAutoplay.state);
setAppLanguage(state.appLanguage.state);
setTheme(state.theme.state);
setSubStyling(state.subtitleStyling.state);
@@ -217,18 +222,19 @@ export function SettingsPage() {
setBackendUrl(url);
}
}, [
state,
account,
backendUrl,
setEnableThumbnails,
state,
setEnableAutoplay,
setAppLanguage,
setTheme,
setSubStyling,
setProxySet,
updateDeviceName,
updateProfile,
setProxySet,
setBackendUrl,
logout,
setBackendUrl,
]);
return (
<SubPageLayout>
@@ -266,6 +272,8 @@ export function SettingsPage() {
setLanguage={state.appLanguage.set}
enableThumbnails={state.enableThumbnails.state}
setEnableThumbnails={state.enableThumbnails.set}
enableAutoplay={state.enableAutoplay.state}
setEnableAutoplay={state.enableAutoplay.set}
/>
</div>
<div id="settings-appearance" className="mt-48">

View File

@@ -1,3 +1,4 @@
import classNames from "classnames";
import { useTranslation } from "react-i18next";
import { Toggle } from "@/components/buttons/Toggle";
@@ -5,6 +6,7 @@ import { FlagIcon } from "@/components/FlagIcon";
import { Dropdown } from "@/components/form/Dropdown";
import { Heading1 } from "@/components/utils/Text";
import { appLanguageOptions } from "@/setup/i18n";
import { isAutoplayAllowed } from "@/utils/autoplay";
import { getLocaleInfo, sortLangCodes } from "@/utils/language";
export function PreferencesPart(props: {
@@ -12,10 +14,14 @@ export function PreferencesPart(props: {
setLanguage: (l: string) => void;
enableThumbnails: boolean;
setEnableThumbnails: (v: boolean) => void;
enableAutoplay: boolean;
setEnableAutoplay: (v: boolean) => void;
}) {
const { t } = useTranslation();
const sorted = sortLangCodes(appLanguageOptions.map((item) => item.code));
const allowAutoplay = isAutoplayAllowed();
const options = appLanguageOptions
.sort((a, b) => sorted.indexOf(a.code) - sorted.indexOf(b.code))
.map((opt) => ({
@@ -62,6 +68,32 @@ export function PreferencesPart(props: {
</p>
</div>
</div>
<div>
<p className="text-white font-bold mb-3">
{t("settings.preferences.autoplay")}
</p>
<p className="max-w-[25rem] font-medium">
{t("settings.preferences.autoplayDescription")}
</p>
<div
onClick={() =>
allowAutoplay
? props.setEnableAutoplay(!props.enableAutoplay)
: null
}
className={classNames(
"bg-dropdown-background hover:bg-dropdown-hoverBackground select-none my-4 cursor-pointer space-x-3 flex items-center max-w-[25rem] py-3 px-4 rounded-lg",
allowAutoplay
? "cursor-pointer opacity-100 pointer-events-auto"
: "cursor-not-allowed opacity-50 pointer-events-none",
)}
>
<Toggle enabled={props.enableAutoplay && allowAutoplay} />
<p className="flex-1 text-white font-bold">
{t("settings.preferences.autoplayLabel")}
</p>
</div>
</div>
</div>
);
}

View File

@@ -23,6 +23,7 @@ interface Config {
ONBOARDING_CHROME_EXTENSION_INSTALL_LINK: string;
ONBOARDING_FIREFOX_EXTENSION_INSTALL_LINK: string;
ONBOARDING_PROXY_INSTALL_LINK: string;
ALLOW_AUTOPLAY: boolean;
}
export interface RuntimeConfig {
@@ -39,6 +40,7 @@ export interface RuntimeConfig {
TURNSTILE_KEY: string | null;
CDN_REPLACEMENTS: Array<string[]>;
HAS_ONBOARDING: boolean;
ALLOW_AUTOPLAY: boolean;
ONBOARDING_CHROME_EXTENSION_INSTALL_LINK: string | null;
ONBOARDING_FIREFOX_EXTENSION_INSTALL_LINK: string | null;
ONBOARDING_PROXY_INSTALL_LINK: string | null;
@@ -64,6 +66,7 @@ const env: Record<keyof Config, undefined | string> = {
TURNSTILE_KEY: import.meta.env.VITE_TURNSTILE_KEY,
CDN_REPLACEMENTS: import.meta.env.VITE_CDN_REPLACEMENTS,
HAS_ONBOARDING: import.meta.env.VITE_HAS_ONBOARDING,
ALLOW_AUTOPLAY: import.meta.env.VITE_ALLOW_AUTOPLAY,
};
function coerceUndefined(value: string | null | undefined): string | undefined {
@@ -109,6 +112,7 @@ export function conf(): RuntimeConfig {
.filter((v) => v.length > 0),
NORMAL_ROUTER: getKey("NORMAL_ROUTER", "false") === "true",
HAS_ONBOARDING: getKey("HAS_ONBOARDING", "true") === "true",
ALLOW_AUTOPLAY: getKey("ALLOW_AUTOPLAY", "false") === "true",
TURNSTILE_KEY: getKey("TURNSTILE_KEY"),
DISALLOWED_IDS: getKey("DISALLOWED_IDS", "")
.split(",")

View File

@@ -169,6 +169,8 @@ export const createSourceSlice: MakeSlice<SourceSlice> = (set, get) => ({
s.captionList = captions;
s.interface.error = undefined;
s.status = playerStatus.PLAYING;
s.audioTracks = [];
s.currentAudioTrack = null;
});
const store = get();
store.redisplaySource(startAt);

View File

@@ -5,6 +5,8 @@ import { immer } from "zustand/middleware/immer";
export interface PreferencesStore {
enableThumbnails: boolean;
setEnableThumbnails(v: boolean): void;
enableAutoplay: boolean;
setEnableAutoplay(v: boolean): void;
}
export const usePreferencesStore = create(
@@ -16,6 +18,12 @@ export const usePreferencesStore = create(
s.enableThumbnails = v;
});
},
enableAutoplay: false,
setEnableAutoplay(v) {
set((s) => {
s.enableAutoplay = v;
});
},
})),
{
name: "__MW::preferences",

11
src/utils/autoplay.ts Normal file
View File

@@ -0,0 +1,11 @@
import { isExtensionActiveCached } from "@/backend/extension/messaging";
import { conf } from "@/setup/config";
import { useAuthStore } from "@/stores/auth";
export function isAutoplayAllowed() {
return Boolean(
conf().ALLOW_AUTOPLAY ||
isExtensionActiveCached() ||
useAuthStore.getState().proxySet,
);
}