1
0
Fork 0
mirror of https://github.com/sussy-code/smov.git synced 2024-12-20 14:37:43 +01:00

Merge pull request #1015 from movie-web/dev

Version 4.6.2
This commit is contained in:
Jorrin 2024-03-14 23:52:56 +01:00 committed by GitHub
commit 6f3c700dcb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 248 additions and 61 deletions

View file

@ -1,6 +1,6 @@
{ {
"name": "movie-web", "name": "movie-web",
"version": "4.6.1", "version": "4.6.2",
"private": true, "private": true,
"homepage": "https://github.com/movie-web/movie-web", "homepage": "https://github.com/movie-web/movie-web",
"scripts": { "scripts": {
@ -29,7 +29,7 @@
"@formkit/auto-animate": "^0.8.1", "@formkit/auto-animate": "^0.8.1",
"@headlessui/react": "^1.7.17", "@headlessui/react": "^1.7.17",
"@ladjs/country-language": "^1.0.3", "@ladjs/country-language": "^1.0.3",
"@movie-web/providers": "^2.2.2", "@movie-web/providers": "^2.2.3",
"@noble/hashes": "^1.3.3", "@noble/hashes": "^1.3.3",
"@plasmohq/messaging": "^0.6.1", "@plasmohq/messaging": "^0.6.1",
"@react-spring/web": "^9.7.3", "@react-spring/web": "^9.7.3",
@ -44,7 +44,7 @@
"focus-trap-react": "^10.2.3", "focus-trap-react": "^10.2.3",
"fscreen": "^1.2.0", "fscreen": "^1.2.0",
"fuse.js": "^7.0.0", "fuse.js": "^7.0.0",
"hls.js": "^1.4.14", "hls.js": "^1.5.7",
"i18next": "^23.7.11", "i18next": "^23.7.11",
"immer": "^10.0.3", "immer": "^10.0.3",
"jwt-decode": "^4.0.0", "jwt-decode": "^4.0.0",

View file

@ -22,8 +22,8 @@ dependencies:
specifier: ^1.0.3 specifier: ^1.0.3
version: 1.0.3 version: 1.0.3
'@movie-web/providers': '@movie-web/providers':
specifier: ^2.2.2 specifier: ^2.2.3
version: 2.2.2 version: 2.2.3
'@noble/hashes': '@noble/hashes':
specifier: ^1.3.3 specifier: ^1.3.3
version: 1.3.3 version: 1.3.3
@ -67,8 +67,8 @@ dependencies:
specifier: ^7.0.0 specifier: ^7.0.0
version: 7.0.0 version: 7.0.0
hls.js: hls.js:
specifier: ^1.4.14 specifier: ^1.5.7
version: 1.4.14 version: 1.5.7
i18next: i18next:
specifier: ^23.7.11 specifier: ^23.7.11
version: 23.7.11 version: 23.7.11
@ -274,7 +274,7 @@ devDependencies:
version: 0.5.9(prettier@3.1.1) version: 0.5.9(prettier@3.1.1)
rollup-plugin-visualizer: rollup-plugin-visualizer:
specifier: ^5.11.0 specifier: ^5.11.0
version: 5.11.0(@rollup/wasm-node@4.12.1) version: 5.11.0(@rollup/wasm-node@4.13.0)
tailwind-scrollbar: tailwind-scrollbar:
specifier: ^3.0.5 specifier: ^3.0.5
version: 3.0.5(tailwindcss@3.4.0) version: 3.0.5(tailwindcss@3.4.0)
@ -1942,8 +1942,8 @@ packages:
engines: {node: '>= 14'} engines: {node: '>= 14'}
dev: false dev: false
/@movie-web/providers@2.2.2: /@movie-web/providers@2.2.3:
resolution: {integrity: sha512-pTlErE5bdu+b68mUW2YAKOJKz2hwSx63auGAfTkGQ+0SHDMlCV9QQ8S8O9IoSsvdXps7/YlWJWOMX8pmKuYbPQ==} resolution: {integrity: sha512-0axy02Zzlk7Tvtalc/Ebv9u5vzUPVWmQm0Ts5+6l6KPU41JUdLnFgmOl0yf0lbNeHRNSTx5SDlvWcYNL8rgpyA==}
dependencies: dependencies:
cheerio: 1.0.0-rc.12 cheerio: 1.0.0-rc.12
cookie: 0.6.0 cookie: 0.6.0
@ -2068,7 +2068,7 @@ packages:
engines: {node: '>=14.0.0'} engines: {node: '>=14.0.0'}
dev: false dev: false
/@rollup/plugin-babel@5.3.1(@babel/core@7.23.6)(@rollup/wasm-node@4.12.1): /@rollup/plugin-babel@5.3.1(@babel/core@7.23.6)(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
peerDependencies: peerDependencies:
@ -2081,36 +2081,36 @@ packages:
dependencies: dependencies:
'@babel/core': 7.23.6 '@babel/core': 7.23.6
'@babel/helper-module-imports': 7.22.15 '@babel/helper-module-imports': 7.22.15
'@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.12.1) '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.13.0)
rollup: /@rollup/wasm-node@4.12.1 rollup: /@rollup/wasm-node@4.13.0
dev: true dev: true
/@rollup/plugin-node-resolve@11.2.1(@rollup/wasm-node@4.12.1): /@rollup/plugin-node-resolve@11.2.1(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==} resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
peerDependencies: peerDependencies:
rollup: npm:@rollup/wasm-node rollup: npm:@rollup/wasm-node
dependencies: dependencies:
'@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.12.1) '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.13.0)
'@types/resolve': 1.17.1 '@types/resolve': 1.17.1
builtin-modules: 3.3.0 builtin-modules: 3.3.0
deepmerge: 4.3.1 deepmerge: 4.3.1
is-module: 1.0.0 is-module: 1.0.0
resolve: 1.22.4 resolve: 1.22.4
rollup: /@rollup/wasm-node@4.12.1 rollup: /@rollup/wasm-node@4.13.0
dev: true dev: true
/@rollup/plugin-replace@2.4.2(@rollup/wasm-node@4.12.1): /@rollup/plugin-replace@2.4.2(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==}
peerDependencies: peerDependencies:
rollup: npm:@rollup/wasm-node rollup: npm:@rollup/wasm-node
dependencies: dependencies:
'@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.12.1) '@rollup/pluginutils': 3.1.0(@rollup/wasm-node@4.13.0)
magic-string: 0.25.9 magic-string: 0.25.9
rollup: /@rollup/wasm-node@4.12.1 rollup: /@rollup/wasm-node@4.13.0
dev: true dev: true
/@rollup/pluginutils@3.1.0(@rollup/wasm-node@4.12.1): /@rollup/pluginutils@3.1.0(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
engines: {node: '>= 8.0.0'} engines: {node: '>= 8.0.0'}
peerDependencies: peerDependencies:
@ -2119,11 +2119,11 @@ packages:
'@types/estree': 0.0.39 '@types/estree': 0.0.39
estree-walker: 1.0.1 estree-walker: 1.0.1
picomatch: 2.3.1 picomatch: 2.3.1
rollup: /@rollup/wasm-node@4.12.1 rollup: /@rollup/wasm-node@4.13.0
dev: true dev: true
/@rollup/wasm-node@4.12.1: /@rollup/wasm-node@4.13.0:
resolution: {integrity: sha512-5j3BVQEccCzCb8fkl++IbDgAsnlsKBPz049C4C//j5s3pFKxKGlybl63QApdJKl1fNLr7HIwQEJcBImQtA3ZHg==} resolution: {integrity: sha512-oFX11wzU7RTaiW06WBtRpzIVN/oaG0I3XkevNO0brBklYnY9zpLhTfksN4b+TdBt6CfXV/KdVhdWLbb0fQIR7A==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'} engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true hasBin: true
dependencies: dependencies:
@ -4388,8 +4388,8 @@ packages:
function-bind: 1.1.2 function-bind: 1.1.2
dev: true dev: true
/hls.js@1.4.14: /hls.js@1.5.7:
resolution: {integrity: sha512-UppQjyvPVclg+6t2KY/Rv03h0+bA5u6zwqVoz4LAC/L0fgYmIaCD7ZCrwe8WI1Gv01be1XL0QFsRbSdIHV/Wbw==} resolution: {integrity: sha512-Hnyf7ojTBtXHeOW1/t6wCBJSiK1WpoKF9yg7juxldDx8u3iswrkPt2wbOA/1NiwU4j27DSIVoIEJRAhcdMef/A==}
dev: false dev: false
/hoist-non-react-statics@3.3.2: /hoist-non-react-statics@3.3.2:
@ -5112,7 +5112,7 @@ packages:
'@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.6) '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.6)
'@babel/types': 7.23.6 '@babel/types': 7.23.6
kleur: 4.1.5 kleur: 4.1.5
rollup: /@rollup/wasm-node@4.12.1 rollup: /@rollup/wasm-node@4.13.0
unplugin: 1.5.1 unplugin: 1.5.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@ -6040,7 +6040,7 @@ packages:
glob: 7.2.3 glob: 7.2.3
dev: true dev: true
/rollup-plugin-terser@7.0.2(@rollup/wasm-node@4.12.1): /rollup-plugin-terser@7.0.2(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==} resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==}
deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser
peerDependencies: peerDependencies:
@ -6048,12 +6048,12 @@ packages:
dependencies: dependencies:
'@babel/code-frame': 7.23.5 '@babel/code-frame': 7.23.5
jest-worker: 26.6.2 jest-worker: 26.6.2
rollup: /@rollup/wasm-node@4.12.1 rollup: /@rollup/wasm-node@4.13.0
serialize-javascript: 4.0.0 serialize-javascript: 4.0.0
terser: 5.19.3 terser: 5.19.3
dev: true dev: true
/rollup-plugin-visualizer@5.11.0(@rollup/wasm-node@4.12.1): /rollup-plugin-visualizer@5.11.0(@rollup/wasm-node@4.13.0):
resolution: {integrity: sha512-exM0Ms2SN3AgTzMeW7y46neZQcyLY7eKwWAop1ZoRTCZwyrIRdMMJ6JjToAJbML77X/9N8ZEpmXG4Z/Clb9k8g==} resolution: {integrity: sha512-exM0Ms2SN3AgTzMeW7y46neZQcyLY7eKwWAop1ZoRTCZwyrIRdMMJ6JjToAJbML77X/9N8ZEpmXG4Z/Clb9k8g==}
engines: {node: '>=14'} engines: {node: '>=14'}
hasBin: true hasBin: true
@ -6065,7 +6065,7 @@ packages:
dependencies: dependencies:
open: 8.4.2 open: 8.4.2
picomatch: 2.3.1 picomatch: 2.3.1
rollup: /@rollup/wasm-node@4.12.1 rollup: /@rollup/wasm-node@4.13.0
source-map: 0.7.4 source-map: 0.7.4
yargs: 17.7.2 yargs: 17.7.2
dev: true dev: true
@ -7051,7 +7051,7 @@ packages:
'@types/node': 20.10.5 '@types/node': 20.10.5
esbuild: 0.19.10 esbuild: 0.19.10
postcss: 8.4.32 postcss: 8.4.32
rollup: /@rollup/wasm-node@4.12.1 rollup: /@rollup/wasm-node@4.13.0
optionalDependencies: optionalDependencies:
fsevents: 2.3.3 fsevents: 2.3.3
dev: true dev: true
@ -7313,9 +7313,9 @@ packages:
'@babel/core': 7.23.6 '@babel/core': 7.23.6
'@babel/preset-env': 7.23.6(@babel/core@7.23.6) '@babel/preset-env': 7.23.6(@babel/core@7.23.6)
'@babel/runtime': 7.23.6 '@babel/runtime': 7.23.6
'@rollup/plugin-babel': 5.3.1(@babel/core@7.23.6)(@rollup/wasm-node@4.12.1) '@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.12.1) '@rollup/plugin-node-resolve': 11.2.1(@rollup/wasm-node@4.13.0)
'@rollup/plugin-replace': 2.4.2(@rollup/wasm-node@4.12.1) '@rollup/plugin-replace': 2.4.2(@rollup/wasm-node@4.13.0)
'@surma/rollup-plugin-off-main-thread': 2.2.3 '@surma/rollup-plugin-off-main-thread': 2.2.3
ajv: 8.12.0 ajv: 8.12.0
common-tags: 1.8.2 common-tags: 1.8.2
@ -7324,8 +7324,8 @@ packages:
glob: 7.2.3 glob: 7.2.3
lodash: 4.17.21 lodash: 4.17.21
pretty-bytes: 5.6.0 pretty-bytes: 5.6.0
rollup: /@rollup/wasm-node@4.12.1 rollup: /@rollup/wasm-node@4.13.0
rollup-plugin-terser: 7.0.2(@rollup/wasm-node@4.12.1) rollup-plugin-terser: 7.0.2(@rollup/wasm-node@4.13.0)
source-map: 0.8.0-beta.0 source-map: 0.8.0-beta.0
stringify-object: 3.3.0 stringify-object: 3.3.0
strip-comments: 2.0.1 strip-comments: 2.0.1
@ -7370,6 +7370,7 @@ packages:
/workbox-google-analytics@7.0.0: /workbox-google-analytics@7.0.0:
resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==} resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==}
deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained
dependencies: dependencies:
workbox-background-sync: 7.0.0 workbox-background-sync: 7.0.0
workbox-core: 7.0.0 workbox-core: 7.0.0

View file

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

View file

@ -57,6 +57,8 @@
}, },
"host": "lawa ilo sina li <0>{{hostname}}</0> - ona li pona tawa sina la sina ken pali e lipu open", "host": "lawa ilo sina li <0>{{hostname}}</0> - ona li pona tawa sina la sina ken pali e lipu open",
"no": "o weka", "no": "o weka",
"noHost": "lawa ilo ni li open ala li nasin ala la, sina ken ala pali e lipu open",
"noHostTitle": "lawa ilo li open ala a!",
"title": "lawa ilo ni li pona tawa sina anu seme?", "title": "lawa ilo ni li pona tawa sina anu seme?",
"yes": "lawa ilo ni li pona" "yes": "lawa ilo ni li pona"
}, },
@ -79,7 +81,8 @@
}, },
"footer": { "footer": {
"legal": { "legal": {
"disclaimer": "o sona e ni:" "disclaimer": "o sona e ni:",
"disclaimerText": "ilo Muwi-We li mama ala e ijo sitelen. ona li toki taso tawa ilo ante. utala nasin li lon la o toki tawa ona pi ilo ante. sitelen ale li tan ala ilo Muwi-We"
}, },
"links": { "links": {
"discord": "kulupu Siko", "discord": "kulupu Siko",
@ -117,22 +120,33 @@
"noResults": "ijo li lon ala a!", "noResults": "ijo li lon ala a!",
"placeholder": { "placeholder": {
"default": "sina wile lukin e seme?", "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:" "sectionTitle": "mi lukin e ni:"
}, },
"titles": { "titles": {
"day": { "day": {
"default": "tenpo suno ni la sina wile lukin e seme?", "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": { "morning": {
"default": "tenpo sin ni la sina wile lukin e seme?", "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": { "night": {
"default": "tenpo pimeja ni la sina wile lukin e seme?", "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" "title": "mi ken ala lukin e lipu ona"
}, },
"onboarding": { "onboarding": {
"defaultConfirm": {
"cancel": "ala"
},
"start": { "start": {
"title": "o open e ilo Muwi-We" "title": "o open e ilo Muwi-We"
} }

View file

@ -51,3 +51,15 @@ export async function downloadCaption(
downloadCache.set(caption.url, output, expirySeconds); downloadCache.set(caption.url, output, expirySeconds);
return output; return output;
} }
/**
* Downloads the WebVTT content. No different than a simple
* get request with a cache.
*/
export async function downloadWebVTT(url: string): Promise<string> {
const cached = downloadCache.get(url);
if (cached) return cached;
const data = await fetch(url).then((v) => v.text());
return data;
}

View file

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

View file

@ -67,6 +67,11 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
let preferenceQuality: SourceQuality | null = null; let preferenceQuality: SourceQuality | null = null;
let lastVolume = 1; let lastVolume = 1;
const languagePromises = new Map<
string,
(value: void | PromiseLike<void>) => void
>();
function reportLevels() { function reportLevels() {
if (!hls) return; if (!hls) return;
const levels = hls.levels; const levels = hls.levels;
@ -133,6 +138,7 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
}, },
}, },
}, },
renderTextTracksNatively: false,
}); });
hls.on(Hls.Events.ERROR, (event, data) => { hls.on(Hls.Events.ERROR, (event, data) => {
console.error("HLS error", data); console.error("HLS error", data);
@ -173,6 +179,16 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
const quality = hlsLevelToQuality(hls.levels[hls.currentLevel]); const quality = hlsLevelToQuality(hls.levels[hls.currentLevel]);
emit("changedquality", quality); emit("changedquality", quality);
}); });
hls.on(Hls.Events.SUBTITLE_TRACK_LOADED, () => {
for (const [lang, resolve] of languagePromises) {
const track = hls?.subtitleTracks.find((t) => t.lang === lang);
if (track) {
resolve();
languagePromises.delete(lang);
break;
}
}
});
} }
hls.attachMedia(vid); hls.attachMedia(vid);
@ -413,5 +429,40 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
setPlaybackRate(rate) { setPlaybackRate(rate) {
if (videoElement) videoElement.playbackRate = rate; if (videoElement) videoElement.playbackRate = rate;
}, },
getCaptionList() {
return (
hls?.subtitleTracks.map((track) => {
return {
id: track.id.toString(),
language: track.lang ?? "unknown",
url: track.url,
needsProxy: false,
hls: true,
};
}) ?? []
);
},
getSubtitleTracks() {
return hls?.subtitleTracks ?? [];
},
async setSubtitlePreference(lang) {
// default subtitles are already loaded by hls.js
const track = hls?.subtitleTracks.find((t) => t.lang === lang);
if (track?.details !== undefined) return Promise.resolve();
// need to wait a moment before hls loads the subtitles
const promise = new Promise<void>((resolve, reject) => {
languagePromises.set(lang, resolve);
// reject after some time, if hls.js fails to load the subtitles
// for any reason
setTimeout(() => {
reject();
languagePromises.delete(lang);
}, 5000);
});
hls?.setSubtitleOption({ lang });
return promise;
},
}; };
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -94,9 +94,11 @@ export function conf(): RuntimeConfig {
DMCA_EMAIL: getKey("DMCA_EMAIL"), DMCA_EMAIL: getKey("DMCA_EMAIL"),
ONBOARDING_CHROME_EXTENSION_INSTALL_LINK: getKey( ONBOARDING_CHROME_EXTENSION_INSTALL_LINK: getKey(
"ONBOARDING_CHROME_EXTENSION_INSTALL_LINK", "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: getKey(
"ONBOARDING_FIREFOX_EXTENSION_INSTALL_LINK", "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"), ONBOARDING_PROXY_INSTALL_LINK: getKey("ONBOARDING_PROXY_INSTALL_LINK"),
BACKEND_URL: getKey("BACKEND_URL", BACKEND_URL), BACKEND_URL: getKey("BACKEND_URL", BACKEND_URL),
@ -106,7 +108,7 @@ export function conf(): RuntimeConfig {
.map((v) => v.trim()) .map((v) => v.trim())
.filter((v) => v.length > 0), .filter((v) => v.length > 0),
NORMAL_ROUTER: getKey("NORMAL_ROUTER", "false") === "true", NORMAL_ROUTER: getKey("NORMAL_ROUTER", "false") === "true",
HAS_ONBOARDING: getKey("HAS_ONBOARDING", "false") === "true", HAS_ONBOARDING: getKey("HAS_ONBOARDING", "true") === "true",
TURNSTILE_KEY: getKey("TURNSTILE_KEY"), TURNSTILE_KEY: getKey("TURNSTILE_KEY"),
DISALLOWED_IDS: getKey("DISALLOWED_IDS", "") DISALLOWED_IDS: getKey("DISALLOWED_IDS", "")
.split(",") .split(",")

View file

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