Compare commits

..

62 commits

Author SHA1 Message Date
LagradOst
67b0549fd2
remove images 2023-03-21 21:01:47 +00:00
Osten
52d495f425
Update README.md 2023-03-21 20:50:13 +00:00
Cloudburst
0cbee70683
[skip ci] Update locales.py 2023-03-19 12:51:54 +01:00
Lag
4235c826a5 Better focus on Android TV
(Thank you ocean for reporting)
2023-03-18 23:55:58 +01:00
Cloudburst
5245eff6e1
[skip ci] fix xml header being slightly wrong 2023-03-18 09:22:07 +01:00
Lag
9c40abc4d3 Added player intent 2023-03-17 22:15:25 +01:00
Lag
019399952f Better subtitle decoding :) 2023-03-17 16:23:03 +01:00
Lag
cc99899cf1 Merge remote-tracking branch 'origin/master' 2023-03-17 16:07:33 +01:00
Lag
8fff809b79 Revert ffmpeg as it causes issues with subtitles :( 2023-03-17 16:07:28 +01:00
recloudstream[bot]
67318a62a3 chore(locales): fix locale issues 2023-03-17 15:04:00 +00:00
Hosted Weblate
288c5ffa39 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (German)

Currently translated at 100.0% (610 of 610 strings)

Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: Sdarfeesh <Sdarfeesh@protonmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/
Translation: Cloudstream/App
2023-03-17 16:03:40 +01:00
Lag
8ebf5185a3 Add ffmpeg audio decoding 2023-03-17 15:46:11 +01:00
Cloudburst
7bfcf25df4 add a way to autofix weblate's issue with @string 2023-03-14 18:50:13 +00:00
Lag
2d7126d71f Fix for fix for translations 2023-03-14 13:12:34 +01:00
Lag
40a4f319b6 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	app/src/main/res/values-uk/strings.xml
2023-03-14 13:01:08 +01:00
Lag
19dc1a2456 Un-bruh-momented some translations 2023-03-14 12:59:32 +01:00
Cloudburst
ac1012bcb8
Merge pull request #420 from recloudstream/weblate-guh 2023-03-13 18:32:15 +01:00
Cloudburst
ec3950ed4f Merge branch 'master' of https://hosted.weblate.org/git/cloudstream/app 2023-03-13 18:18:13 +01:00
Hosted Weblate
3e2b0f2a17 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Dutch)

Currently translated at 74.0% (452 of 610 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (610 of 610 strings)

Added translation using Weblate (Malay)

Translated using Weblate (Russian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Turkish)

Currently translated at 99.1% (605 of 610 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (English)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (English)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (English)

Currently translated at 100.0% (610 of 610 strings)

Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: Felipe Nogueira <contato.fnog@gmail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Frank Gerritsen Mulkes <frankgmwerk@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Samuel Gadiel <samuelgadiel@gmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: TZVS <akyasan@tuta.io>
Co-authored-by: Tang Yin <bingyuanshiye@126.com>
Co-authored-by: Walter H <walter75@gmail.com>
Co-authored-by: eightyy8 <oliver.kha@pm.me>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/
Translation: Cloudstream/App
2023-03-13 18:18:01 +01:00
LikDev-256
29174dbb30
Feat: fix Streamsb (#417)
* Fix Streamsb

* feat(StreamSB) stream break: support audiotracks

* Revert "feat(StreamSB) stream break: support audiotracks"

This reverts commit 078caf9f88.

* Feat: fix Streamsb

They normally update source numbers like 50, 51 but instead of 52 they totally dumped everything and just flipped the number into 15
2023-03-13 16:11:35 +00:00
Lag
7b47f93190 Merge remote-tracking branch 'origin/master' 2023-03-10 21:33:27 +01:00
Lag
13ee8e21d0 Semi-unfucked VLC on A13+ 2023-03-10 21:33:13 +01:00
recloudstream[bot]
3a5d872545 update list of locales 2023-03-10 20:01:20 +00:00
Hosted Weblate
fab55d82c4 Translated using Weblate (Portuguese)
Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Dutch)

Currently translated at 74.0% (452 of 610 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (610 of 610 strings)

Added translation using Weblate (Malay)

Translated using Weblate (Russian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Turkish)

Currently translated at 99.1% (605 of 610 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (English)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (English)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (English)

Currently translated at 100.0% (610 of 610 strings)

Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: Felipe Nogueira <contato.fnog@gmail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Frank Gerritsen Mulkes <frankgmwerk@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Samuel Gadiel <samuelgadiel@gmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: TZVS <akyasan@tuta.io>
Co-authored-by: Tang Yin <bingyuanshiye@126.com>
Co-authored-by: Walter H <walter75@gmail.com>
Co-authored-by: eightyy8 <oliver.kha@pm.me>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/
Translation: Cloudstream/App
2023-03-10 21:01:04 +01:00
Hosted Weblate
8b2881f5f6
Translated using Weblate (Ukrainian)
Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Dutch)

Currently translated at 74.0% (452 of 610 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (610 of 610 strings)

Added translation using Weblate (Malay)

Translated using Weblate (Russian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Turkish)

Currently translated at 99.1% (605 of 610 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (English)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (English)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (English)

Currently translated at 100.0% (610 of 610 strings)

Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: Felipe Nogueira <contato.fnog@gmail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Frank Gerritsen Mulkes <frankgmwerk@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Samuel Gadiel <samuelgadiel@gmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: TZVS <akyasan@tuta.io>
Co-authored-by: Tang Yin <bingyuanshiye@126.com>
Co-authored-by: Walter H <walter75@gmail.com>
Co-authored-by: eightyy8 <oliver.kha@pm.me>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/
Translation: Cloudstream/App
2023-03-10 20:45:19 +01:00
PokerFace
37244ab0f7
Intertal Player: Added MPD support (#402)
* added isDash in ExtractorLink
2023-03-10 19:45:11 +00:00
Lag
e85b31c35d Fixing rouge pixels in settings 2023-03-07 17:36:53 +01:00
Hosted Weblate
1eaa4620dc Translated using Weblate (qt (generated) (qt))
Currently translated at 54.5% (333 of 610 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (French)

Currently translated at 98.8% (603 of 610 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (German)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Russian)

Currently translated at 99.6% (608 of 610 strings)

Translated using Weblate (Portuguese)

Currently translated at 85.0% (519 of 610 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Vietnamese)

Currently translated at 96.8% (591 of 610 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (610 of 610 strings)

Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Duc Nguyen Tien <ducnt123@gmail.com>
Co-authored-by: Felipe Nogueira <contato.fnog@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Samuel Gadiel <samuelgadiel@gmail.com>
Co-authored-by: Sdarfeesh <Sdarfeesh@protonmail.com>
Co-authored-by: Walter H <walter75@gmail.com>
Co-authored-by: eightyy8 <oliver.kha@pm.me>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/qt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/
Translation: Cloudstream/App
2023-03-03 18:16:40 +01:00
no-commit
76545f55c3 Standardized some home screen padding and made subtitle delay persistent. Fixes #405 2023-03-03 17:45:26 +01:00
Stormunblessed
f0515c4dc9
Support qualities for Dailymotion (#407)
* Dailymotion qualities
2023-03-03 09:24:02 +00:00
no-commit
ab324b93e8 Small fixes to Intents and Subscriptions 2023-02-28 01:19:59 +01:00
Stormunblessed
d6df24eff2
Fixes on filesim and added filemoon, ztreamhub (#397)
* fix fastream, tomatomatela, and added okrulink

* forgot this

* sendvid extractor

* sendvid extractor

* fixes

* Filesim fix, added filemoon and ztreamhub
2023-02-27 20:05:42 +00:00
Hosted Weblate
e5834d485b Translated using Weblate (German)
Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (608 of 608 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (608 of 608 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (608 of 608 strings)

Translated using Weblate (Portuguese)

Currently translated at 81.0% (493 of 608 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (608 of 608 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 99.5% (605 of 608 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (608 of 608 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (608 of 608 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (608 of 608 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (608 of 608 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (608 of 608 strings)

Translated using Weblate (Polish)

Currently translated at 97.3% (592 of 608 strings)

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Japanese)

Currently translated at 44.5% (268 of 602 strings)

Translated using Weblate (qt (generated) (qt))

Currently translated at 50.4% (304 of 602 strings)

Translated using Weblate (Slovak)

Currently translated at 31.7% (191 of 602 strings)

Translated using Weblate (Portuguese)

Currently translated at 76.9% (463 of 602 strings)

Translated using Weblate (Somali)

Currently translated at 94.3% (568 of 602 strings)

Translated using Weblate (Somali)

Currently translated at 94.3% (568 of 602 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 44.5% (268 of 602 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 44.5% (268 of 602 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 44.5% (268 of 602 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Esperanto)

Currently translated at 27.5% (166 of 602 strings)

Translated using Weblate (Esperanto)

Currently translated at 27.5% (166 of 602 strings)

Translated using Weblate (Persian)

Currently translated at 20.0% (121 of 602 strings)

Translated using Weblate (Hungarian)

Currently translated at 55.6% (335 of 602 strings)

Translated using Weblate (German)

Currently translated at 99.1% (597 of 602 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Russian)

Currently translated at 99.1% (597 of 602 strings)

Translated using Weblate (Kannada)

Currently translated at 35.2% (212 of 602 strings)

Translated using Weblate (Urdu)

Currently translated at 72.2% (435 of 602 strings)

Translated using Weblate (Tamil)

Currently translated at 18.2% (110 of 602 strings)

Translated using Weblate (Tamil)

Currently translated at 18.2% (110 of 602 strings)

Translated using Weblate (Hebrew)

Currently translated at 97.1% (585 of 602 strings)

Translated using Weblate (Bengali)

Currently translated at 38.7% (233 of 602 strings)

Translated using Weblate (Bengali)

Currently translated at 38.7% (233 of 602 strings)

Translated using Weblate (Bengali)

Currently translated at 38.7% (233 of 602 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 94.1% (567 of 602 strings)

Translated using Weblate (Vietnamese)

Currently translated at 96.8% (583 of 602 strings)

Translated using Weblate (Turkish)

Currently translated at 97.1% (585 of 602 strings)

Translated using Weblate (Tagalog)

Currently translated at 56.1% (338 of 602 strings)

Translated using Weblate (Tagalog)

Currently translated at 56.1% (338 of 602 strings)

Translated using Weblate (Tagalog)

Currently translated at 56.1% (338 of 602 strings)

Translated using Weblate (Swedish)

Currently translated at 74.9% (451 of 602 strings)

Translated using Weblate (Swedish)

Currently translated at 74.9% (451 of 602 strings)

Translated using Weblate (Swedish)

Currently translated at 74.9% (451 of 602 strings)

Translated using Weblate (Romanian)

Currently translated at 73.0% (440 of 602 strings)

Translated using Weblate (Romanian)

Currently translated at 73.0% (440 of 602 strings)

Translated using Weblate (Romanian)

Currently translated at 73.0% (440 of 602 strings)

Translated using Weblate (Polish)

Currently translated at 98.0% (590 of 602 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 88.3% (532 of 602 strings)

Translated using Weblate (Dutch)

Currently translated at 75.0% (452 of 602 strings)

Translated using Weblate (Dutch)

Currently translated at 75.0% (452 of 602 strings)

Translated using Weblate (Dutch)

Currently translated at 75.0% (452 of 602 strings)

Translated using Weblate (Malayalam)

Currently translated at 37.2% (224 of 602 strings)

Translated using Weblate (Malayalam)

Currently translated at 37.2% (224 of 602 strings)

Translated using Weblate (Malayalam)

Currently translated at 37.2% (224 of 602 strings)

Translated using Weblate (Macedonian)

Currently translated at 48.6% (293 of 602 strings)

Translated using Weblate (Macedonian)

Currently translated at 48.6% (293 of 602 strings)

Translated using Weblate (Macedonian)

Currently translated at 48.6% (293 of 602 strings)

Translated using Weblate (Italian)

Currently translated at 99.1% (597 of 602 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Hindi)

Currently translated at 37.7% (227 of 602 strings)

Translated using Weblate (Hindi)

Currently translated at 37.7% (227 of 602 strings)

Translated using Weblate (Hindi)

Currently translated at 37.7% (227 of 602 strings)

Translated using Weblate (French)

Currently translated at 97.3% (586 of 602 strings)

Translated using Weblate (Greek)

Currently translated at 97.0% (584 of 602 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (bp (generated) (bp))

Currently translated at 77.4% (466 of 602 strings)

Translated using Weblate (bp (generated) (bp))

Currently translated at 77.4% (466 of 602 strings)

Translated using Weblate (bp (generated) (bp))

Currently translated at 77.4% (466 of 602 strings)

Translated using Weblate (Bulgarian)

Currently translated at 94.5% (569 of 602 strings)

Translated using Weblate (Bulgarian)

Currently translated at 94.5% (569 of 602 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (602 of 602 strings)

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Japanese)

Currently translated at 44.5% (268 of 602 strings)

Translated using Weblate (qt (generated) (qt))

Currently translated at 50.4% (304 of 602 strings)

Translated using Weblate (Slovak)

Currently translated at 31.7% (191 of 602 strings)

Translated using Weblate (Portuguese)

Currently translated at 76.9% (463 of 602 strings)

Translated using Weblate (Somali)

Currently translated at 94.3% (568 of 602 strings)

Translated using Weblate (Somali)

Currently translated at 94.3% (568 of 602 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 44.5% (268 of 602 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 44.5% (268 of 602 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 44.5% (268 of 602 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Esperanto)

Currently translated at 27.5% (166 of 602 strings)

Translated using Weblate (Esperanto)

Currently translated at 27.5% (166 of 602 strings)

Translated using Weblate (Persian)

Currently translated at 20.0% (121 of 602 strings)

Translated using Weblate (Hungarian)

Currently translated at 55.6% (335 of 602 strings)

Translated using Weblate (German)

Currently translated at 99.1% (597 of 602 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Russian)

Currently translated at 99.1% (597 of 602 strings)

Translated using Weblate (Kannada)

Currently translated at 35.2% (212 of 602 strings)

Translated using Weblate (Kannada)

Currently translated at 35.2% (212 of 602 strings)

Translated using Weblate (Urdu)

Currently translated at 72.2% (435 of 602 strings)

Translated using Weblate (Tamil)

Currently translated at 18.2% (110 of 602 strings)

Translated using Weblate (Tamil)

Currently translated at 18.2% (110 of 602 strings)

Translated using Weblate (Hebrew)

Currently translated at 97.1% (585 of 602 strings)

Translated using Weblate (Bengali)

Currently translated at 38.7% (233 of 602 strings)

Translated using Weblate (Bengali)

Currently translated at 38.7% (233 of 602 strings)

Translated using Weblate (Bengali)

Currently translated at 38.7% (233 of 602 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 94.1% (567 of 602 strings)

Translated using Weblate (Vietnamese)

Currently translated at 96.8% (583 of 602 strings)

Translated using Weblate (Turkish)

Currently translated at 97.1% (585 of 602 strings)

Translated using Weblate (Tagalog)

Currently translated at 56.1% (338 of 602 strings)

Translated using Weblate (Tagalog)

Currently translated at 56.1% (338 of 602 strings)

Translated using Weblate (Tagalog)

Currently translated at 56.1% (338 of 602 strings)

Translated using Weblate (Swedish)

Currently translated at 74.9% (451 of 602 strings)

Translated using Weblate (Swedish)

Currently translated at 74.9% (451 of 602 strings)

Translated using Weblate (Swedish)

Currently translated at 74.9% (451 of 602 strings)

Translated using Weblate (Romanian)

Currently translated at 73.0% (440 of 602 strings)

Translated using Weblate (Romanian)

Currently translated at 73.0% (440 of 602 strings)

Translated using Weblate (Romanian)

Currently translated at 73.0% (440 of 602 strings)

Translated using Weblate (Polish)

Currently translated at 98.0% (590 of 602 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 88.3% (532 of 602 strings)

Translated using Weblate (Dutch)

Currently translated at 75.0% (452 of 602 strings)

Translated using Weblate (Dutch)

Currently translated at 75.0% (452 of 602 strings)

Translated using Weblate (Dutch)

Currently translated at 75.0% (452 of 602 strings)

Translated using Weblate (Malayalam)

Currently translated at 37.2% (224 of 602 strings)

Translated using Weblate (Malayalam)

Currently translated at 37.2% (224 of 602 strings)

Translated using Weblate (Malayalam)

Currently translated at 37.2% (224 of 602 strings)

Translated using Weblate (Macedonian)

Currently translated at 48.6% (293 of 602 strings)

Translated using Weblate (Macedonian)

Currently translated at 48.6% (293 of 602 strings)

Translated using Weblate (Macedonian)

Currently translated at 48.6% (293 of 602 strings)

Translated using Weblate (Italian)

Currently translated at 99.1% (597 of 602 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Hindi)

Currently translated at 37.7% (227 of 602 strings)

Translated using Weblate (Hindi)

Currently translated at 37.7% (227 of 602 strings)

Translated using Weblate (Hindi)

Currently translated at 37.7% (227 of 602 strings)

Translated using Weblate (French)

Currently translated at 97.3% (586 of 602 strings)

Translated using Weblate (Greek)

Currently translated at 97.0% (584 of 602 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (bp (generated) (bp))

Currently translated at 77.4% (466 of 602 strings)

Translated using Weblate (bp (generated) (bp))

Currently translated at 77.4% (466 of 602 strings)

Translated using Weblate (bp (generated) (bp))

Currently translated at 77.4% (466 of 602 strings)

Translated using Weblate (Bulgarian)

Currently translated at 94.5% (569 of 602 strings)

Translated using Weblate (Bulgarian)

Currently translated at 94.5% (569 of 602 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (602 of 602 strings)

Co-authored-by: Aitor Salaberria <trslbrr@gmail.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Geovani Amaral <geovani.af4@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: MedRAM <mohammad7ram@users.noreply.hosted.weblate.org>
Co-authored-by: Prathap Rathod <prathap0144@gmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Sandyran <sandyran@protonmail.com>
Co-authored-by: Sdarfeesh <Sdarfeesh@protonmail.com>
Co-authored-by: Translator-3000 <weblate.m1d0h@8shield.net>
Co-authored-by: Walter H <walter75@gmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: gnu-ewm <gnu.ewm@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bg/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bp/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/el/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/eo/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fa/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/he/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hi/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hu/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ja/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/kn/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/mk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ml/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nn/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/qt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ro/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/so/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sv/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ta/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ur/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hant/
Translation: Cloudstream/App
2023-02-25 22:21:25 +01:00
Sarlay
6524eb220b
Added some extractors mirrors and added Vido Extractor (#393)
* Added some mirrors and fixed some extractors

* Fixed Vido extractor (for MesFilms and Wiflix)
2023-02-25 21:18:48 +00:00
Allen Baby
2926dc6c8e
Issue #376: Added new feature for separate watch quality on mobile data. (#391)
* Issue #376: Added new feature for separate watch quality on mobile data.
2023-02-24 18:51:03 +00:00
Hexated
f722785a37
fixed Linkbox (#390) 2023-02-24 18:49:53 +00:00
Lag
aeab423d29 Excluded the referer header when empty 2023-02-24 18:47:54 +01:00
recloudstream[bot]
1da6a92569 update list of locales 2023-02-21 18:07:04 +00:00
Cloudburst
b2fa765a2d
Merge pull request #364 from recloudstream/weblate 2023-02-21 19:06:48 +01:00
Cloudburst
bec0a2e7b9
Merge branch 'master' into weblate 2023-02-21 19:06:13 +01:00
Cloudburst
51137701f2
add proxy to raw.githubusercontent.com (#368) 2023-02-21 18:43:35 +01:00
Hosted Weblate
5f12d067f9
Translated using Weblate (Indonesian)
Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Slovak)

Currently translated at 31.7% (191 of 602 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (602 of 602 strings)

Translated using Weblate (German)

Currently translated at 100.0% (597 of 597 strings)

Translated using Weblate (Japanese)

Currently translated at 44.8% (268 of 597 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (597 of 597 strings)

Translated using Weblate (Japanese)

Currently translated at 40.5% (242 of 597 strings)

Translated using Weblate (Japanese)

Currently translated at 34.5% (206 of 597 strings)

Translated using Weblate (Japanese)

Currently translated at 33.1% (198 of 597 strings)

Translated using Weblate (Kannada)

Currently translated at 35.5% (212 of 597 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (597 of 597 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (597 of 597 strings)

Translated using Weblate (Japanese)

Currently translated at 25.7% (154 of 597 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (597 of 597 strings)

Translated using Weblate (German)

Currently translated at 100.0% (597 of 597 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (597 of 597 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (597 of 597 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (597 of 597 strings)

Translated using Weblate (Romanian)

Currently translated at 73.7% (440 of 597 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (597 of 597 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (597 of 597 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (597 of 597 strings)

Translated using Weblate (Japanese)

Currently translated at 22.7% (134 of 589 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (589 of 589 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (589 of 589 strings)

Translated using Weblate (Japanese)

Currently translated at 17.8% (105 of 589 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.3% (585 of 589 strings)

Translated using Weblate (Japanese)

Currently translated at 16.6% (98 of 589 strings)

Translated using Weblate (Russian)

Currently translated at 99.6% (587 of 589 strings)

Translated using Weblate (Russian)

Currently translated at 99.4% (586 of 589 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 96.0% (566 of 589 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (589 of 589 strings)

Translated using Weblate (German)

Currently translated at 100.0% (589 of 589 strings)

Translated using Weblate (French)

Currently translated at 99.3% (585 of 589 strings)

Added translation using Weblate (Japanese)

Translated using Weblate (Croatian)

Currently translated at 100.0% (589 of 589 strings)

Translated using Weblate (Russian)

Currently translated at 98.9% (583 of 589 strings)

Translated using Weblate (French)

Currently translated at 98.6% (581 of 589 strings)

Translated using Weblate (French)

Currently translated at 96.6% (569 of 589 strings)

Translated using Weblate (Greek)

Currently translated at 98.9% (583 of 589 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (589 of 589 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (589 of 589 strings)

jsdelivr wrapper to githubusercontent

Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Andrey Zapolsky <zapoland@gmail.com>
Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Cliff Heraldo <cliffkbrt11@gmail.com>
Co-authored-by: Cliff Heraldo <clxf12@users.noreply.hosted.weblate.org>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: Deleted User <noreply+57159@weblate.org>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Gabriel Cnudde <gabriel.cnudde59@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: Juraj Liso <lisojuraj@gmail.com>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: MedRAM <mohammad7ram@users.noreply.hosted.weblate.org>
Co-authored-by: Piotr Strebski <strebski@gmail.com>
Co-authored-by: Prathap Rathod <prathap0144@gmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Sdarfeesh <Sdarfeesh@protonmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: Translator-3000 <weblate.m1d0h@8shield.net>
Co-authored-by: abcabcc <xmmandxpp@outlook.com>
Co-authored-by: eightyy8 <oliver.kha@pm.me>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/el/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ja/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/kn/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ro/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hant/
Translation: Cloudstream/App
2023-02-21 18:28:21 +01:00
no-commit
00a91ca5fb Added Subscriptions (pinged every ~6 hours) 2023-02-19 19:27:40 +01:00
LikDev-256
33aecfbba5
Fix Streamsb (#380) 2023-02-18 12:15:50 +00:00
no-commit
0185854682 Merge remote-tracking branch 'origin/master' 2023-02-17 23:06:16 +01:00
no-commit
b4065b69be Added dropdown indicators
Solves #375
2023-02-17 23:05:11 +01:00
MhmdIbrahim1
b6ac155350
update VideoDownloadService (#377) 2023-02-17 21:42:20 +00:00
Lag
aacd57cb5d Fixed scrolling up on bottom dialogs and removing stuff from AniList 2023-02-16 01:15:30 +01:00
Lag
3dd0fc6c8e add view_test 2023-02-15 22:09:08 +01:00
Lag
135f63afff Merge remote-tracking branch 'origin/master' 2023-02-15 21:41:28 +01:00
Lag
789cd14ef6 remove placeholder 2023-02-15 21:41:20 +01:00
recloudstream[bot]
9d0cce47a6 update list of locales 2023-02-15 20:40:50 +00:00
Lag
4a8ee55018 Added provider tests 2023-02-15 21:40:10 +01:00
Stormunblessed
df6c395acb
Sendvid extractor (#365)
* fix fastream, tomatomatela, and added okrulink

* forgot this

* sendvid extractor

* sendvid extractor

* fixes
2023-02-14 15:11:20 +00:00
Cloudburst
1117271a71
[skip ci] português brasileiro 2023-02-10 15:16:58 +01:00
Cloudburst
7b11b9b585
fix wrong hebrew lang code 2023-02-09 20:27:37 +01:00
recloudstream[bot]
5c20b479e5 update list of locales 2023-02-09 19:25:34 +00:00
Hosted Weblate
0d2613d183 Translated using Weblate (qt (generated) (qt))
Currently translated at 51.4% (303 of 589 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.3% (585 of 589 strings)

Translated using Weblate (Persian)

Currently translated at 20.3% (120 of 589 strings)

Translated using Weblate (Russian)

Currently translated at 98.9% (583 of 589 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (589 of 589 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (589 of 589 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (589 of 589 strings)

Translated using Weblate (qt (generated) (qt))

Currently translated at 50.3% (293 of 582 strings)

Translated using Weblate (Persian)

Currently translated at 18.7% (109 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 99.1% (577 of 582 strings)

Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: Soroush <skaveh1384@gmail.com>
Co-authored-by: eightyy8 <oliver.kha@pm.me>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: sina <cnababaie@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fa/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/qt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translation: Cloudstream/App
2023-02-09 20:25:19 +01:00
Cloudburst
dd38556102
address issue #339
Co-authored-by: eightyy8 <64216434+eightyy8@users.noreply.github.com>
2023-02-09 20:25:00 +01:00
reduplicated
84493b7f3b mini fix 2023-02-09 01:46:07 +01:00
reduplicated
4596afee06 auto track anilist/mal 2023-02-09 01:32:48 +01:00
Sir Aguacata
3e2c2a5c86
Added a way for easy mal and anilist tracker (#359)
* Added a way for easy mal and anilist tracker, All credit gos to Hexated for helping me

* Made CodeFactor Fucking Happy

* prettified the getTracker method

* remove parenthesis

* fixed

---------

Co-authored-by: Blatzar <46196380+Blatzar@users.noreply.github.com>
Co-authored-by: reduplicated <110570621+reduplicated@users.noreply.github.com>
2023-02-08 23:58:15 +00:00
120 changed files with 3757 additions and 1147 deletions

BIN
.github/downloads.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

BIN
.github/home.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

17
.github/locales.py vendored
View file

@ -1,6 +1,7 @@
import re import re
import glob import glob
import requests import requests
import lxml.etree as ET # builtin library doesn't preserve comments
SETTINGS_PATH = "app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt" SETTINGS_PATH = "app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt"
@ -45,4 +46,18 @@ open(SETTINGS_PATH, "w+",encoding='utf-8').write(
"\n" + "\n" +
END_MARKER + END_MARKER +
after_src after_src
) )
# Go through each values.xml file and fix escaped \@string
for file in glob.glob(f"{XML_NAME}*/strings.xml"):
try:
tree = ET.parse(file)
for child in tree.getroot():
if child.text.startswith("\\@string/"):
print(f"[{file}] fixing {child.attrib['name']}")
child.text = child.text.replace("\\@string/", "@string/")
with open(file, 'wb') as fp:
fp.write(b'<?xml version="1.0" encoding="utf-8"?>\n')
tree.write(fp, encoding="utf-8", method="xml", pretty_print=True, xml_declaration=False)
except ET.ParseError as ex:
print(f"[{file}] {ex}")

BIN
.github/player.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

BIN
.github/results.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

BIN
.github/search.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

View file

@ -1,4 +1,4 @@
name: Update locale lists name: Fix locale issues
on: on:
workflow_dispatch: workflow_dispatch:
@ -9,7 +9,7 @@ on:
- master - master
concurrency: concurrency:
group: "locale-list" group: "locale"
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
@ -26,6 +26,9 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
token: ${{ steps.generate_token.outputs.token }} token: ${{ steps.generate_token.outputs.token }}
- name: Install dependencies
run: |
pip3 install lxml
- name: Edit files - name: Edit files
run: | run: |
python3 .github/locales.py python3 .github/locales.py
@ -35,5 +38,5 @@ jobs:
git config --local user.name "recloudstream[bot]" git config --local user.name "recloudstream[bot]"
git add . git add .
# "echo" returns true so the build succeeds, even if no changed files # "echo" returns true so the build succeeds, even if no changed files
git commit -m 'update list of locales' || echo git commit -m 'chore(locales): fix locale issues' || echo
git push git push

View file

@ -12,12 +12,7 @@
+ Download and stream movies, tv-shows and anime + Download and stream movies, tv-shows and anime
+ Chromecast + Chromecast
### Screenshots:
<img src="./.github/home.jpg" height="400"/><img src="./.github/search.jpg" height="400"/><img src="./.github/downloads.jpg" height="400"/><img src="./.github/results.jpg" height="400"/>
<img src="./.github/player.jpg" height="200"/>
### Supported languages: ### Supported languages:
<a href="https://hosted.weblate.org/engage/cloudstream/"> <a href="https://hosted.weblate.org/engage/cloudstream/">
<img src="https://hosted.weblate.org/widgets/cloudstream/-/app/multi-auto.svg" alt="Translation status" /> <img src="https://hosted.weblate.org/widgets/cloudstream/-/app/multi-auto.svg" alt="Translation status" />
</a> </a>

View file

@ -159,6 +159,8 @@ dependencies {
implementation("com.google.android.exoplayer:extension-cast:2.18.2") implementation("com.google.android.exoplayer:extension-cast:2.18.2")
implementation("com.google.android.exoplayer:extension-mediasession:2.18.2") implementation("com.google.android.exoplayer:extension-mediasession:2.18.2")
implementation("com.google.android.exoplayer:extension-okhttp:2.18.2") implementation("com.google.android.exoplayer:extension-okhttp:2.18.2")
// Use the Jellyfin ffmpeg extension for easy ffmpeg audio decoding in exoplayer. Thank you Jellyfin <3
// implementation("org.jellyfin.exoplayer:exoplayer-ffmpeg-extension:2.18.2+1")
//implementation("com.google.android.exoplayer:extension-leanback:2.14.0") //implementation("com.google.android.exoplayer:extension-leanback:2.14.0")
@ -184,8 +186,8 @@ dependencies {
//implementation("com.github.TorrentStream:TorrentStream-Android:2.7.0") //implementation("com.github.TorrentStream:TorrentStream-Android:2.7.0")
// Downloading // Downloading
implementation("androidx.work:work-runtime:2.7.1") implementation("androidx.work:work-runtime:2.8.0")
implementation("androidx.work:work-runtime-ktx:2.7.1") implementation("androidx.work:work-runtime-ktx:2.8.0")
// Networking // Networking
// implementation("com.squareup.okhttp3:okhttp:4.9.2") // implementation("com.squareup.okhttp3:okhttp:4.9.2")

View file

@ -1,9 +1,8 @@
package com.lagradost.cloudstream3 package com.lagradost.cloudstream3
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.TestingUtils
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
@ -16,142 +15,11 @@ import org.junit.runner.RunWith
*/ */
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest { class ExampleInstrumentedTest {
//@Test
//fun useAppContext() {
// // Context of the app under test.
// val appContext = InstrumentationRegistry.getInstrumentation().targetContext
// assertEquals("com.lagradost.cloudstream3", appContext.packageName)
//}
private fun getAllProviders(): List<MainAPI> { private fun getAllProviders(): List<MainAPI> {
println("Providers: ${APIHolder.allProviders.size}")
return APIHolder.allProviders //.filter { !it.usesWebView } return APIHolder.allProviders //.filter { !it.usesWebView }
} }
private suspend fun loadLinks(api: MainAPI, url: String?): Boolean {
Assert.assertNotNull("Api ${api.name} has invalid url on episode", url)
if (url == null) return true
var linksLoaded = 0
try {
val success = api.loadLinks(url, false, {}) { link ->
Assert.assertTrue(
"Api ${api.name} returns link with invalid Quality",
Qualities.values().map { it.value }.contains(link.quality)
)
Assert.assertTrue(
"Api ${api.name} returns link with invalid url ${link.url}",
link.url.length > 4
)
linksLoaded++
}
if (success) {
return linksLoaded > 0
}
Assert.assertTrue("Api ${api.name} has returns false on .loadLinks", success)
} catch (e: Exception) {
if (e.cause is NotImplementedError) {
Assert.fail("Provider has not implemented .loadLinks")
}
logError(e)
}
return true
}
private suspend fun testSingleProviderApi(api: MainAPI): Boolean {
val searchQueries = listOf("over", "iron", "guy")
var correctResponses = 0
var searchResult: List<SearchResponse>? = null
for (query in searchQueries) {
val response = try {
api.search(query)
} catch (e: Exception) {
if (e.cause is NotImplementedError) {
Assert.fail("Provider has not implemented .search")
}
logError(e)
null
}
if (!response.isNullOrEmpty()) {
correctResponses++
if (searchResult == null) {
searchResult = response
}
}
}
if (correctResponses == 0 || searchResult == null) {
System.err.println("Api ${api.name} did not return any valid search responses")
return false
}
try {
var validResults = false
for (result in searchResult) {
Assert.assertEquals(
"Invalid apiName on response on ${api.name}",
result.apiName,
api.name
)
val load = api.load(result.url) ?: continue
Assert.assertEquals(
"Invalid apiName on load on ${api.name}",
load.apiName,
result.apiName
)
Assert.assertTrue(
"Api ${api.name} on load does not contain any of the supportedTypes",
api.supportedTypes.contains(load.type)
)
when (load) {
is AnimeLoadResponse -> {
val gotNoEpisodes =
load.episodes.keys.isEmpty() || load.episodes.keys.any { load.episodes[it].isNullOrEmpty() }
if (gotNoEpisodes) {
println("Api ${api.name} got no episodes on ${load.url}")
continue
}
val url = (load.episodes[load.episodes.keys.first()])?.first()?.data
validResults = loadLinks(api, url)
if (!validResults) continue
}
is MovieLoadResponse -> {
val gotNoEpisodes = load.dataUrl.isBlank()
if (gotNoEpisodes) {
println("Api ${api.name} got no movie on ${load.url}")
continue
}
validResults = loadLinks(api, load.dataUrl)
if (!validResults) continue
}
is TvSeriesLoadResponse -> {
val gotNoEpisodes = load.episodes.isEmpty()
if (gotNoEpisodes) {
println("Api ${api.name} got no episodes on ${load.url}")
continue
}
validResults = loadLinks(api, load.episodes.first().data)
if (!validResults) continue
}
}
break
}
if (!validResults) {
System.err.println("Api ${api.name} did not load on any")
}
return validResults
} catch (e: Exception) {
if (e.cause is NotImplementedError) {
Assert.fail("Provider has not implemented .load")
}
logError(e)
return false
}
}
@Test @Test
fun providersExist() { fun providersExist() {
Assert.assertTrue(getAllProviders().isNotEmpty()) Assert.assertTrue(getAllProviders().isNotEmpty())
@ -159,6 +27,7 @@ class ExampleInstrumentedTest {
} }
@Test @Test
@Throws(AssertionError::class)
fun providerCorrectData() { fun providerCorrectData() {
val isoNames = SubtitleHelper.languages.map { it.ISO_639_1 } val isoNames = SubtitleHelper.languages.map { it.ISO_639_1 }
Assert.assertFalse("ISO does not contain any languages", isoNames.isNullOrEmpty()) Assert.assertFalse("ISO does not contain any languages", isoNames.isNullOrEmpty())
@ -181,67 +50,20 @@ class ExampleInstrumentedTest {
fun providerCorrectHomepage() { fun providerCorrectHomepage() {
runBlocking { runBlocking {
getAllProviders().amap { api -> getAllProviders().amap { api ->
if (api.hasMainPage) { TestingUtils.testHomepage(api, ::println)
try {
val f = api.mainPage.first()
val homepage =
api.getMainPage(1, MainPageRequest(f.name, f.data, f.horizontalImages))
when {
homepage == null -> {
System.err.println("Homepage provider ${api.name} did not correctly load homepage!")
}
homepage.items.isEmpty() -> {
System.err.println("Homepage provider ${api.name} does not contain any items!")
}
homepage.items.any { it.list.isEmpty() } -> {
System.err.println("Homepage provider ${api.name} does not have any items on result!")
}
}
} catch (e: Exception) {
if (e.cause is NotImplementedError) {
Assert.fail("Provider marked as hasMainPage, while in reality is has not been implemented")
}
logError(e)
}
}
} }
} }
println("Done providerCorrectHomepage") println("Done providerCorrectHomepage")
} }
// @Test
// fun testSingleProvider() {
// testSingleProviderApi(ThenosProvider())
// }
@Test @Test
fun providerCorrect() { fun testAllProvidersCorrect() {
runBlocking { runBlocking {
val invalidProvider = ArrayList<Pair<MainAPI, Exception?>>() TestingUtils.getDeferredProviderTests(
val providers = getAllProviders() this,
providers.amap { api -> getAllProviders(),
try { ::println
println("Trying $api") ) { _, _ -> }
if (testSingleProviderApi(api)) {
println("Success $api")
} else {
System.err.println("Error $api")
invalidProvider.add(Pair(api, null))
}
} catch (e: Exception) {
logError(e)
invalidProvider.add(Pair(api, e))
}
}
if (invalidProvider.isEmpty()) {
println("No Invalid providers! :D")
} else {
println("Invalid providers are: ")
for (provider in invalidProvider) {
println("${provider.first}")
}
}
} }
println("Done providerCorrect")
} }
} }

View file

@ -98,6 +98,16 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" /> <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter> </intent-filter>
<!-- cloudstreamplayer://encodedUrl?name=Dune -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="cloudstreamplayer" />
</intent-filter>
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />

View file

@ -17,8 +17,10 @@ import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.ui.player.SubtitleData import com.lagradost.cloudstream3.ui.player.SubtitleData
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.Coroutines.mainWork
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
import okhttp3.Interceptor import okhttp3.Interceptor
import org.mozilla.javascript.Scriptable
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@ -82,7 +84,7 @@ object APIHolder {
initMap() initMap()
return apiMap?.get(apiName)?.let { apis.getOrNull(it) } return apiMap?.get(apiName)?.let { apis.getOrNull(it) }
// Leave the ?. null check, it can crash regardless // Leave the ?. null check, it can crash regardless
?: allProviders.firstOrNull { it?.name == apiName } ?: allProviders.firstOrNull { it.name == apiName }
} }
} }
@ -734,6 +736,19 @@ fun fixTitle(str: String): String {
.replaceFirstChar { char -> if (char.isLowerCase()) char.titlecase(Locale.getDefault()) else it } .replaceFirstChar { char -> if (char.isLowerCase()) char.titlecase(Locale.getDefault()) else it }
} }
} }
/**
* Get rhino context in a safe way as it needs to be initialized on the main thread.
* Make sure you get the scope using: val scope: Scriptable = rhino.initSafeStandardObjects()
* Use like the following: rhino.evaluateString(scope, js, "JavaScript", 1, null)
**/
suspend fun getRhinoContext(): org.mozilla.javascript.Context {
return Coroutines.mainWork {
val rhino = org.mozilla.javascript.Context.enter()
rhino.initSafeStandardObjects()
rhino.optimizationLevel = -1
rhino
}
}
/** https://www.imdb.com/title/tt2861424/ -> tt2861424 */ /** https://www.imdb.com/title/tt2861424/ -> tt2861424 */
fun imdbUrlToId(url: String): String? { fun imdbUrlToId(url: String): String? {
@ -1312,7 +1327,7 @@ fun LoadResponse?.isAnimeBased(): Boolean {
fun TvType?.isEpisodeBased(): Boolean { fun TvType?.isEpisodeBased(): Boolean {
if (this == null) return false if (this == null) return false
return (this == TvType.TvSeries || this == TvType.Anime) return (this == TvType.TvSeries || this == TvType.Anime || this == TvType.AsianDrama)
} }
@ -1336,6 +1351,7 @@ interface EpisodeResponse {
var showStatus: ShowStatus? var showStatus: ShowStatus?
var nextAiring: NextAiring? var nextAiring: NextAiring?
var seasonNames: List<SeasonData>? var seasonNames: List<SeasonData>?
fun getLatestEpisodes(): Map<DubStatus, Int?>
} }
@JvmName("addSeasonNamesString") @JvmName("addSeasonNamesString")
@ -1404,7 +1420,18 @@ data class AnimeLoadResponse(
override var nextAiring: NextAiring? = null, override var nextAiring: NextAiring? = null,
override var seasonNames: List<SeasonData>? = null, override var seasonNames: List<SeasonData>? = null,
override var backgroundPosterUrl: String? = null, override var backgroundPosterUrl: String? = null,
) : LoadResponse, EpisodeResponse ) : LoadResponse, EpisodeResponse {
override fun getLatestEpisodes(): Map<DubStatus, Int?> {
return episodes.map { (status, episodes) ->
val maxSeason = episodes.maxOfOrNull { it.season ?: Int.MIN_VALUE }
.takeUnless { it == Int.MIN_VALUE }
status to episodes
.filter { it.season == maxSeason }
.maxOfOrNull { it.episode ?: Int.MIN_VALUE }
.takeUnless { it == Int.MIN_VALUE }
}.toMap()
}
}
/** /**
* If episodes already exist appends the list. * If episodes already exist appends the list.
@ -1602,7 +1629,17 @@ data class TvSeriesLoadResponse(
override var nextAiring: NextAiring? = null, override var nextAiring: NextAiring? = null,
override var seasonNames: List<SeasonData>? = null, override var seasonNames: List<SeasonData>? = null,
override var backgroundPosterUrl: String? = null, override var backgroundPosterUrl: String? = null,
) : LoadResponse, EpisodeResponse ) : LoadResponse, EpisodeResponse {
override fun getLatestEpisodes(): Map<DubStatus, Int?> {
val maxSeason =
episodes.maxOfOrNull { it.season ?: Int.MIN_VALUE }.takeUnless { it == Int.MIN_VALUE }
val max = episodes
.filter { it.season == maxSeason }
.maxOfOrNull { it.episode ?: Int.MIN_VALUE }
.takeUnless { it == Int.MIN_VALUE }
return mapOf(DubStatus.None to max)
}
}
suspend fun MainAPI.newTvSeriesLoadResponse( suspend fun MainAPI.newTvSeriesLoadResponse(
name: String, name: String,

View file

@ -5,6 +5,8 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Configuration import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log import android.util.Log
@ -32,6 +34,7 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.google.android.gms.cast.framework.* import com.google.android.gms.cast.framework.*
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.navigationrail.NavigationRailView import com.google.android.material.navigationrail.NavigationRailView
import com.google.android.material.snackbar.Snackbar
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
import com.lagradost.cloudstream3.APIHolder.allProviders import com.lagradost.cloudstream3.APIHolder.allProviders
import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.apis
@ -55,6 +58,7 @@ import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.OAuth2Apis import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.OAuth2Apis
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.accountManagers import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.accountManagers
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appString import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appString
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringPlayer
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringRepo import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringRepo
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringResumeWatching import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringResumeWatching
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringSearch import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringSearch
@ -63,6 +67,9 @@ import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
import com.lagradost.cloudstream3.ui.home.HomeViewModel import com.lagradost.cloudstream3.ui.home.HomeViewModel
import com.lagradost.cloudstream3.ui.player.BasicLink
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.ui.player.LinkGenerator
import com.lagradost.cloudstream3.ui.result.ResultViewModel2 import com.lagradost.cloudstream3.ui.result.ResultViewModel2
import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST
import com.lagradost.cloudstream3.ui.result.setImage import com.lagradost.cloudstream3.ui.result.setImage
@ -79,6 +86,7 @@ import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
import com.lagradost.cloudstream3.utils.AppUtils.isNetworkAvailable
import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.loadCache
import com.lagradost.cloudstream3.utils.AppUtils.loadRepository import com.lagradost.cloudstream3.utils.AppUtils.loadRepository
import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.AppUtils.loadResult
@ -86,6 +94,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching
@ -166,7 +175,12 @@ open class ResultResume(
val VLC = object : ResultResume( val VLC = object : ResultResume(
VLC_PACKAGE, VLC_PACKAGE,
"org.videolan.vlc.player.result", // Android 13 intent restrictions fucks up specifically launching the VLC player
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
"org.videolan.vlc.player.result"
} else {
Intent.ACTION_VIEW
},
"extra_position", "extra_position",
"extra_duration", "extra_duration",
) { ) {
@ -265,6 +279,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
isWebview: Boolean isWebview: Boolean
): Boolean = ): Boolean =
with(activity) { with(activity) {
// TODO MUCH BETTER HANDLING
// Invalid URIs can crash // Invalid URIs can crash
fun safeURI(uri: String) = normalSafeApiCall { URI(uri) } fun safeURI(uri: String) = normalSafeApiCall { URI(uri) }
@ -315,7 +331,25 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
} else if (safeURI(str)?.scheme == appStringSearch) { } else if (safeURI(str)?.scheme == appStringSearch) {
nextSearchQuery = nextSearchQuery =
URLDecoder.decode(str.substringAfter("$appStringSearch://"), "UTF-8") URLDecoder.decode(str.substringAfter("$appStringSearch://"), "UTF-8")
nav_view.selectedItemId = R.id.navigation_search
// Use both navigation views to support both layouts.
// It might be better to use the QuickSearch.
nav_view?.selectedItemId = R.id.navigation_search
nav_rail_view?.selectedItemId = R.id.navigation_search
} else if (safeURI(str)?.scheme == appStringPlayer) {
val uri = Uri.parse(str)
val name = uri.getQueryParameter("name")
val url = URLDecoder.decode(uri.authority, "UTF-8")
navigate(
R.id.global_to_navigation_player,
GeneratorPlayer.newInstance(
LinkGenerator(
listOf(BasicLink(url, name)),
extract = true,
)
)
)
} else if (safeURI(str)?.scheme == appStringResumeWatching) { } else if (safeURI(str)?.scheme == appStringResumeWatching) {
val id = val id =
str.substringAfter("$appStringResumeWatching://").toIntOrNull() str.substringAfter("$appStringResumeWatching://").toIntOrNull()
@ -402,6 +436,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
R.id.navigation_settings_general, R.id.navigation_settings_general,
R.id.navigation_settings_extensions, R.id.navigation_settings_extensions,
R.id.navigation_settings_plugins, R.id.navigation_settings_plugins,
R.id.navigation_test_providers,
).contains(destination.id) ).contains(destination.id)
@ -716,6 +751,29 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
changeStatusBarState(isEmulatorSettings()) changeStatusBarState(isEmulatorSettings())
// Automatically enable jsdelivr if cant connect to raw.githubusercontent.com
if (this.getKey<Boolean>(getString(R.string.jsdelivr_proxy_key)) == null && isNetworkAvailable()) {
main {
if (checkGithubConnectivity()) {
this.setKey(getString(R.string.jsdelivr_proxy_key), false)
} else {
this.setKey(getString(R.string.jsdelivr_proxy_key), true)
val parentView: View = findViewById(android.R.id.content)
Snackbar.make(parentView, R.string.jsdelivr_enabled, Snackbar.LENGTH_LONG)
.let { snackbar ->
snackbar.setAction(R.string.revert) {
setKey(getString(R.string.jsdelivr_proxy_key), false)
}
snackbar.setBackgroundTint(colorFromAttribute(R.attr.primaryGrayBackground))
snackbar.setTextColor(colorFromAttribute(R.attr.textColor))
snackbar.setActionTextColor(colorFromAttribute(R.attr.colorPrimary))
snackbar.show()
}
}
}
}
if (PluginManager.checkSafeModeFile()) { if (PluginManager.checkSafeModeFile()) {
normalSafeApiCall { normalSafeApiCall {
@ -1089,4 +1147,15 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
// } // }
} }
suspend fun checkGithubConnectivity(): Boolean {
return try {
app.get(
"https://raw.githubusercontent.com/recloudstream/.github/master/connectivitycheck",
timeout = 5
).text.trim() == "ok"
} catch (t: Throwable) {
false
}
}
} }

View file

@ -6,6 +6,7 @@ import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
import java.net.URL import java.net.URL
@ -42,18 +43,9 @@ open class Dailymotion : ExtractorApi() {
) )
val metaData = app.get(metaDataUrl, referer = embedUrl, cookies = cookies) val metaData = app.get(metaDataUrl, referer = embedUrl, cookies = cookies)
.parsedSafe<MetaData>() ?: return .parsedSafe<MetaData>() ?: return
metaData.qualities.forEach { (key, video) -> metaData.qualities.forEach { (_, video) ->
video.forEach { video.forEach {
callback.invoke( getStream(it.url, this.name, callback)
ExtractorLink(
name,
"$name $key",
it.url,
"",
Qualities.Unknown.value,
true
)
)
} }
} }
} }
@ -75,6 +67,17 @@ open class Dailymotion : ExtractorApi() {
return null return null
} }
private suspend fun getStream(
streamLink: String,
name: String,
callback: (ExtractorLink) -> Unit
) {
return generateM3u8(
name,
streamLink,
"",
).forEach(callback)
}
data class Config( data class Config(
val context: Context, val context: Context,
val dmInternalData: InternalData val dmInternalData: InternalData

View file

@ -38,6 +38,9 @@ class DoodWsExtractor : DoodLaExtractor() {
override var mainUrl = "https://dood.ws" override var mainUrl = "https://dood.ws"
} }
class DoodYtExtractor : DoodLaExtractor() {
override var mainUrl = "https://dood.yt"
}
open class DoodLaExtractor : ExtractorApi() { open class DoodLaExtractor : ExtractorApi() {
override var name = "DoodStream" override var name = "DoodStream"

View file

@ -16,26 +16,7 @@ open class Evoload : ExtractorApi() {
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> { override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val lang = url.substring(0, 2) val id = url.replace("https://evoload.io/e/", "") // wanted media id
val flag =
if (lang == "vo") {
" \uD83C\uDDEC\uD83C\uDDE7"
}
else if (lang == "vf"){
" \uD83C\uDDE8\uD83C\uDDF5"
} else {
""
}
val cleaned_url = if (lang == "ht") { // if url doesn't contain a flag and the url starts with http://
url
} else {
url.substring(2, url.length)
}
//println(lang)
//println(cleaned_url)
val id = cleaned_url.replace("https://evoload.io/e/", "") // wanted media id
val csrv_token = app.get("https://csrv.evosrv.com/captcha?m412548=").text // whatever that is val csrv_token = app.get("https://csrv.evosrv.com/captcha?m412548=").text // whatever that is
val captchaPass = app.get("https://cd2.evosrv.com/html/jsx/e.jsx").text.take(300).split("captcha_pass = '")[1].split("\'")[0] //extract the captcha pass from the js response (located in the 300 first chars) val captchaPass = app.get("https://cd2.evosrv.com/html/jsx/e.jsx").text.take(300).split("captcha_pass = '")[1].split("\'")[0] //extract the captcha pass from the js response (located in the 300 first chars)
val payload = mapOf("code" to id, "csrv_token" to csrv_token, "pass" to captchaPass) val payload = mapOf("code" to id, "csrv_token" to csrv_token, "pass" to captchaPass)
@ -44,9 +25,9 @@ open class Evoload : ExtractorApi() {
return listOf( return listOf(
ExtractorLink( ExtractorLink(
name, name,
name + flag, name,
link, link,
cleaned_url, url,
Qualities.Unknown.value, Qualities.Unknown.value,
) )
) )

View file

@ -1,18 +1,25 @@
package com.lagradost.cloudstream3.extractors package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import java.net.URI
class Ztreamhub : Filesim() {
override val mainUrl: String = "https://ztreamhub.com" //Here 'cause works
override val name = "Zstreamhub"
}
class FileMoon : Filesim() { class FileMoon : Filesim() {
override val mainUrl = "https://filemoon.to" override val mainUrl = "https://filemoon.to"
override val name = "FileMoon" override val name = "FileMoon"
} }
class FileMoonSx : Filesim() {
override val mainUrl = "https://filemoon.sx"
override val name = "FileMoonSx"
}
open class Filesim : ExtractorApi() { open class Filesim : ExtractorApi() {
override val name = "Filesim" override val name = "Filesim"
override val mainUrl = "https://files.im" override val mainUrl = "https://files.im"
@ -24,34 +31,27 @@ open class Filesim : ExtractorApi() {
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
) { ) {
with(app.get(url).document) { val response = app.get(url, referer = mainUrl).document
this.select("script").forEach { script -> response.select("script[type=text/javascript]").map { script ->
if (script.data().contains("eval(function(p,a,c,k,e,d)")) { if (script.data().contains(Regex("eval\\(function\\(p,a,c,k,e,[rd]"))) {
val data = getAndUnpack(script.data()) val unpackedscript = getAndUnpack(script.data())
val foundData = Regex("""sources:\[(.*?)]""").find(data)?.groupValues?.get(1) ?: return@forEach val m3u8Regex = Regex("file.\\\"(.*?m3u8.*?)\\\"")
val fixedData = foundData.replace("file:", """"file":""") val m3u8 = m3u8Regex.find(unpackedscript)?.destructured?.component1() ?: ""
if (m3u8.isNotEmpty()) {
parseJson<List<ResponseSource>>("[$fixedData]").forEach { generateM3u8(
callback.invoke( name,
ExtractorLink( m3u8,
name, mainUrl
name, ).forEach(callback)
it.file,
"$mainUrl/",
Qualities.Unknown.value,
URI(it.file).path.endsWith(".m3u8")
)
)
}
} }
} }
} }
} }
private data class ResponseSource( /* private data class ResponseSource(
@JsonProperty("file") val file: String, @JsonProperty("file") val file: String,
@JsonProperty("type") val type: String?, @JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String? @JsonProperty("label") val label: String?
) ) */
} }

View file

@ -18,31 +18,36 @@ open class Linkbox : ExtractorApi() {
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
) { ) {
val id = Regex("""(/file/|id=)(\S+)[&/?]""").find(url)?.groupValues?.get(2) val id = Regex("""(?:/f/|/file/|\?id=)(\w+)""").find(url)?.groupValues?.get(1)
app.get("$mainUrl/api/open/get_url?itemId=$id", referer=url).parsedSafe<Responses>()?.data?.rList?.map { link -> app.get("$mainUrl/api/file/detail?itemId=$id", referer = url)
callback.invoke( .parsedSafe<Responses>()?.data?.itemInfo?.resolutionList?.map { link ->
ExtractorLink( callback.invoke(
name, ExtractorLink(
name, name,
link.url, name,
url, link.url ?: return@map null,
getQualityFromName(link.resolution) url,
getQualityFromName(link.resolution)
)
) )
) }
}
} }
data class RList( data class Resolutions(
@JsonProperty("url") val url: String, @JsonProperty("url") val url: String? = null,
@JsonProperty("resolution") val resolution: String?, @JsonProperty("resolution") val resolution: String? = null,
)
data class ItemInfo(
@JsonProperty("resolutionList") val resolutionList: ArrayList<Resolutions>? = arrayListOf(),
) )
data class Data( data class Data(
@JsonProperty("rList") val rList: List<RList>?, @JsonProperty("itemInfo") val itemInfo: ItemInfo? = null,
) )
data class Responses( data class Responses(
@JsonProperty("data") val data: Data?, @JsonProperty("data") val data: Data? = null,
) )
} }

View file

@ -0,0 +1,28 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
open class Sendvid : ExtractorApi() {
override var name = "Sendvid"
override val mainUrl = "https://sendvid.com"
override val requiresReferer = false
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val doc = app.get(url).document
val urlString = doc.select("head meta[property=og:video:secure_url]").attr("content")
if (urlString.contains("m3u8")) {
generateM3u8(
name,
urlString,
mainUrl,
).forEach(callback)
}
}
}

View file

@ -77,6 +77,10 @@ class StreamSB10 : StreamSB() {
override var mainUrl = "https://sbplay2.xyz" override var mainUrl = "https://sbplay2.xyz"
} }
class StreamSB11 : StreamSB() {
override var mainUrl = "https://sbbrisk.com"
}
// This is a modified version of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/genoanime/src/eu/kanade/tachiyomi/animeextension/en/genoanime/extractors/StreamSBExtractor.kt // This is a modified version of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/genoanime/src/eu/kanade/tachiyomi/animeextension/en/genoanime/extractors/StreamSBExtractor.kt
// The following code is under the Apache License 2.0 https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE // The following code is under the Apache License 2.0 https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE
open class StreamSB : ExtractorApi() { open class StreamSB : ExtractorApi() {
@ -130,7 +134,7 @@ open class StreamSB : ExtractorApi() {
it.value.replace(Regex("(embed-|/e/)"), "") it.value.replace(Regex("(embed-|/e/)"), "")
}.first() }.first()
// val master = "$mainUrl/sources48/6d6144797752744a454267617c7c${bytesToHex.lowercase()}7c7c4e61755a56456f34385243727c7c73747265616d7362/6b4a33767968506e4e71374f7c7c343837323439333133333462353935333633373836643638376337633462333634663539343137373761333635313533333835333763376333393636363133393635366136323733343435323332376137633763373337343732363536313664373336327c7c504d754478413835306633797c7c73747265616d7362" // val master = "$mainUrl/sources48/6d6144797752744a454267617c7c${bytesToHex.lowercase()}7c7c4e61755a56456f34385243727c7c73747265616d7362/6b4a33767968506e4e71374f7c7c343837323439333133333462353935333633373836643638376337633462333634663539343137373761333635313533333835333763376333393636363133393635366136323733343435323332376137633763373337343732363536313664373336327c7c504d754478413835306633797c7c73747265616d7362"
val master = "$mainUrl/sources50/" + bytesToHex("||$id||||streamsb".toByteArray()) + "/" val master = "$mainUrl/sources15/" + bytesToHex("||$id||||streamsb".toByteArray()) + "/"
val headers = mapOf( val headers = mapOf(
"watchsb" to "sbstream", "watchsb" to "sbstream",
) )
@ -156,4 +160,4 @@ open class StreamSB : ExtractorApi() {
) )
} }
} }
} }

View file

@ -7,6 +7,10 @@ class Uqload1 : Uqload() {
override var mainUrl = "https://uqload.com" override var mainUrl = "https://uqload.com"
} }
class Uqload2 : Uqload() {
override var mainUrl = "https://uqload.co"
}
open class Uqload : ExtractorApi() { open class Uqload : ExtractorApi() {
override val name: String = "Uqload" override val name: String = "Uqload"
override val mainUrl: String = "https://www.uqload.com" override val mainUrl: String = "https://www.uqload.com"
@ -15,30 +19,14 @@ open class Uqload : ExtractorApi() {
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? { override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val lang = url.substring(0, 2) with(app.get(url)) { // raised error ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED (3003) is due to the response: "error_nofile"
val flag =
if (lang == "vo") {
" \uD83C\uDDEC\uD83C\uDDE7"
}
else if (lang == "vf"){
" \uD83C\uDDE8\uD83C\uDDF5"
} else {
""
}
val cleaned_url = if (lang == "ht") { // if url doesn't contain a flag and the url starts with http://
url
} else {
url.substring(2, url.length)
}
with(app.get(cleaned_url)) { // raised error ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED (3003) is due to the response: "error_nofile"
srcRegex.find(this.text)?.groupValues?.get(1)?.replace("\"", "")?.let { link -> srcRegex.find(this.text)?.groupValues?.get(1)?.replace("\"", "")?.let { link ->
return listOf( return listOf(
ExtractorLink( ExtractorLink(
name, name,
name + flag, name,
link, link,
cleaned_url, url,
Qualities.Unknown.value, Qualities.Unknown.value,
) )
) )

View file

@ -0,0 +1,34 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getAndUnpack
class Vido : ExtractorApi() {
override var name = "Vido"
override var mainUrl = "https://vido.lol"
private val srcRegex = Regex("""sources:\s*\["(.*?)"\]""")
override val requiresReferer = true
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val methode = app.get(url.replace("/e/", "/embed-")) // fix wiflix and mesfilms
with(methode) {
if (!methode.isSuccessful) return null
//val quality = unpackedText.lowercase().substringAfter(" height=").substringBefore(" ").toIntOrNull()
srcRegex.find(this.text)?.groupValues?.get(1)?.let { link ->
return listOf(
ExtractorLink(
name,
name,
link,
url,
Qualities.Unknown.value,
true,
)
)
}
}
return null
}
}

View file

@ -49,7 +49,7 @@ inline fun debugWarning(assert: () -> Boolean, message: () -> String) {
} }
} }
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, action: (t: T) -> Unit) { fun <T> LifecycleOwner.observe(liveData: LiveData<T>, action: (t: T) -> Unit) {
liveData.observe(this) { it?.let { t -> action(t) } } liveData.observe(this) { it?.let { t -> action(t) } }
} }
@ -121,13 +121,21 @@ suspend fun <T> suspendSafeApiCall(apiCall: suspend () -> T): T? {
} }
} }
fun Throwable.getAllMessages(): String {
return (this.localizedMessage ?: "") + (this.cause?.getAllMessages()?.let { "\n$it" } ?: "")
}
fun Throwable.getStackTracePretty(showMessage: Boolean = true): String {
val prefix = if (showMessage) this.localizedMessage?.let { "\n$it" } ?: "" else ""
return prefix + this.stackTrace.joinToString(
separator = "\n"
) {
"${it.fileName} ${it.lineNumber}"
}
}
fun <T> safeFail(throwable: Throwable): Resource<T> { fun <T> safeFail(throwable: Throwable): Resource<T> {
val stackTraceMsg = val stackTraceMsg = throwable.getStackTracePretty()
(throwable.localizedMessage ?: "") + "\n\n" + throwable.stackTrace.joinToString(
separator = "\n"
) {
"${it.fileName} ${it.lineNumber}"
}
return Resource.Failure(false, null, null, stackTraceMsg) return Resource.Failure(false, null, null, stackTraceMsg)
} }

View file

@ -16,6 +16,7 @@ import com.google.gson.Gson
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.APIHolder.removePluginMapping import com.lagradost.cloudstream3.APIHolder.removePluginMapping
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
@ -165,11 +166,11 @@ object PluginManager {
private var loadedLocalPlugins = false private var loadedLocalPlugins = false
private val gson = Gson() private val gson = Gson()
private suspend fun maybeLoadPlugin(activity: Activity, file: File) { private suspend fun maybeLoadPlugin(context: Context, file: File) {
val name = file.name val name = file.name
if (file.extension == "zip" || file.extension == "cs3") { if (file.extension == "zip" || file.extension == "cs3") {
loadPlugin( loadPlugin(
activity, context,
file, file,
PluginData(name, null, false, file.absolutePath, PLUGIN_VERSION_NOT_SET) PluginData(name, null, false, file.absolutePath, PLUGIN_VERSION_NOT_SET)
) )
@ -199,7 +200,7 @@ object PluginManager {
// var allCurrentOutDatedPlugins: Set<OnlinePluginData> = emptySet() // var allCurrentOutDatedPlugins: Set<OnlinePluginData> = emptySet()
suspend fun loadSinglePlugin(activity: Activity, apiName: String): Boolean { suspend fun loadSinglePlugin(context: Context, apiName: String): Boolean {
return (getPluginsOnline().firstOrNull { return (getPluginsOnline().firstOrNull {
// Most of the time the provider ends with Provider which isn't part of the api name // Most of the time the provider ends with Provider which isn't part of the api name
it.internalName.replace("provider", "", ignoreCase = true) == apiName it.internalName.replace("provider", "", ignoreCase = true) == apiName
@ -209,7 +210,7 @@ object PluginManager {
})?.let { savedData -> })?.let { savedData ->
// OnlinePluginData(savedData, onlineData) // OnlinePluginData(savedData, onlineData)
loadPlugin( loadPlugin(
activity, context,
File(savedData.filePath), File(savedData.filePath),
savedData savedData
) )
@ -371,11 +372,11 @@ object PluginManager {
/** /**
* Use updateAllOnlinePluginsAndLoadThem * Use updateAllOnlinePluginsAndLoadThem
* */ * */
fun loadAllOnlinePlugins(activity: Activity) { fun loadAllOnlinePlugins(context: Context) {
// Load all plugins as fast as possible! // Load all plugins as fast as possible!
(getPluginsOnline()).toList().apmap { pluginData -> (getPluginsOnline()).toList().apmap { pluginData ->
loadPlugin( loadPlugin(
activity, context,
File(pluginData.filePath), File(pluginData.filePath),
pluginData pluginData
) )
@ -398,7 +399,7 @@ object PluginManager {
* @param forceReload see afterPluginsLoadedEvent, basically a way to load all local plugins * @param forceReload see afterPluginsLoadedEvent, basically a way to load all local plugins
* and reload all pages even if they are previously valid * and reload all pages even if they are previously valid
**/ **/
fun loadAllLocalPlugins(activity: Activity, forceReload: Boolean) { fun loadAllLocalPlugins(context: Context, forceReload: Boolean) {
val dir = File(LOCAL_PLUGINS_PATH) val dir = File(LOCAL_PLUGINS_PATH)
removeKey(PLUGINS_KEY_LOCAL) removeKey(PLUGINS_KEY_LOCAL)
@ -416,7 +417,7 @@ object PluginManager {
Log.d(TAG, "Files in '${LOCAL_PLUGINS_PATH}' folder: $sortedPlugins") Log.d(TAG, "Files in '${LOCAL_PLUGINS_PATH}' folder: $sortedPlugins")
sortedPlugins?.sortedBy { it.name }?.apmap { file -> sortedPlugins?.sortedBy { it.name }?.apmap { file ->
maybeLoadPlugin(activity, file) maybeLoadPlugin(context, file)
} }
loadedLocalPlugins = true loadedLocalPlugins = true
@ -441,14 +442,14 @@ object PluginManager {
/** /**
* @return True if successful, false if not * @return True if successful, false if not
* */ * */
private suspend fun loadPlugin(activity: Activity, file: File, data: PluginData): Boolean { private suspend fun loadPlugin(context: Context, file: File, data: PluginData): Boolean {
val fileName = file.nameWithoutExtension val fileName = file.nameWithoutExtension
val filePath = file.absolutePath val filePath = file.absolutePath
currentlyLoading = fileName currentlyLoading = fileName
Log.i(TAG, "Loading plugin: $data") Log.i(TAG, "Loading plugin: $data")
return try { return try {
val loader = PathClassLoader(filePath, activity.classLoader) val loader = PathClassLoader(filePath, context.classLoader)
var manifest: Plugin.Manifest var manifest: Plugin.Manifest
loader.getResourceAsStream("manifest.json").use { stream -> loader.getResourceAsStream("manifest.json").use { stream ->
if (stream == null) { if (stream == null) {
@ -492,22 +493,22 @@ object PluginManager {
addAssetPath.invoke(assets, file.absolutePath) addAssetPath.invoke(assets, file.absolutePath)
pluginInstance.resources = Resources( pluginInstance.resources = Resources(
assets, assets,
activity.resources.displayMetrics, context.resources.displayMetrics,
activity.resources.configuration context.resources.configuration
) )
} }
plugins[filePath] = pluginInstance plugins[filePath] = pluginInstance
classLoaders[loader] = pluginInstance classLoaders[loader] = pluginInstance
urlPlugins[data.url ?: filePath] = pluginInstance urlPlugins[data.url ?: filePath] = pluginInstance
pluginInstance.load(activity) pluginInstance.load(context)
Log.i(TAG, "Loaded plugin ${data.internalName} successfully") Log.i(TAG, "Loaded plugin ${data.internalName} successfully")
currentlyLoading = null currentlyLoading = null
true true
} catch (e: Throwable) { } catch (e: Throwable) {
Log.e(TAG, "Failed to load $file: ${Log.getStackTraceString(e)}") Log.e(TAG, "Failed to load $file: ${Log.getStackTraceString(e)}")
showToast( showToast(
activity, context.getActivity(),
activity.getString(R.string.plugin_load_fail).format(fileName), context.getString(R.string.plugin_load_fail).format(fileName),
Toast.LENGTH_LONG Toast.LENGTH_LONG
) )
currentlyLoading = null currentlyLoading = null

View file

@ -2,8 +2,10 @@ package com.lagradost.cloudstream3.plugins
import android.content.Context import android.content.Context
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.AcraApplication.Companion.context
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.amap
import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
@ -71,6 +73,15 @@ object RepositoryManager {
val PREBUILT_REPOSITORIES: Array<RepositoryData> by lazy { val PREBUILT_REPOSITORIES: Array<RepositoryData> by lazy {
getKey("PREBUILT_REPOSITORIES") ?: emptyArray() getKey("PREBUILT_REPOSITORIES") ?: emptyArray()
} }
val GH_REGEX = Regex("^https://raw.githubusercontent.com/([A-Za-z0-9-]+)/([A-Za-z0-9_.-]+)/(.*)$")
/* Convert raw.githubusercontent.com urls to cdn.jsdelivr.net if enabled in settings */
fun convertRawGitUrl(url: String): String {
if (getKey<Boolean>(context!!.getString(R.string.jsdelivr_proxy_key)) != true) return url
val match = GH_REGEX.find(url) ?: return url
val (user, repo, rest) = match.destructured
return "https://cdn.jsdelivr.net/gh/$user/$repo@$rest"
}
suspend fun parseRepoUrl(url: String): String? { suspend fun parseRepoUrl(url: String): String? {
val fixedUrl = url.trim() val fixedUrl = url.trim()
@ -84,10 +95,15 @@ object RepositoryManager {
} }
} else if (fixedUrl.matches("^[a-zA-Z0-9!_-]+$".toRegex())) { } else if (fixedUrl.matches("^[a-zA-Z0-9!_-]+$".toRegex())) {
suspendSafeApiCall { suspendSafeApiCall {
app.get("https://l.cloudstream.cf/${fixedUrl}").let { app.get("https://l.cloudstream.cf/${fixedUrl}", allowRedirects = false).let {
return@let if (it.isSuccessful && !it.url.startsWith("https://cutt.ly/branded-domains")) it.url it.headers["Location"]?.let { url ->
else app.get("https://cutt.ly/${fixedUrl}").let let2@{ it2 -> return@suspendSafeApiCall if (!url.startsWith("https://cutt.ly/branded-domains")) url
return@let2 if (it2.isSuccessful) it2.url else null else null
}
app.get("https://cutt.ly/${fixedUrl}", allowRedirects = false).let { it2 ->
it2.headers["Location"]?.let { url ->
return@suspendSafeApiCall if (url.startsWith("https://cutt.ly/404")) url else null
}
} }
} }
} }
@ -97,14 +113,14 @@ object RepositoryManager {
suspend fun parseRepository(url: String): Repository? { suspend fun parseRepository(url: String): Repository? {
return suspendSafeApiCall { return suspendSafeApiCall {
// Take manifestVersion and such into account later // Take manifestVersion and such into account later
app.get(url).parsedSafe() app.get(convertRawGitUrl(url)).parsedSafe()
} }
} }
private suspend fun parsePlugins(pluginUrls: String): List<SitePlugin> { private suspend fun parsePlugins(pluginUrls: String): List<SitePlugin> {
// Take manifestVersion and such into account later // Take manifestVersion and such into account later
return try { return try {
val response = app.get(pluginUrls) val response = app.get(convertRawGitUrl(pluginUrls))
// Normal parsed function not working? // Normal parsed function not working?
// return response.parsedSafe() // return response.parsedSafe()
tryParseJson<Array<SitePlugin>>(response.text)?.toList() ?: emptyList() tryParseJson<Array<SitePlugin>>(response.text)?.toList() ?: emptyList()
@ -139,7 +155,7 @@ object RepositoryManager {
} }
file.createNewFile() file.createNewFile()
val body = app.get(pluginUrl).okhttpResponse.body val body = app.get(convertRawGitUrl(pluginUrl)).okhttpResponse.body
write(body.byteStream(), file.outputStream()) write(body.byteStream(), file.outputStream())
file file
} }

View file

@ -0,0 +1,224 @@
package com.lagradost.cloudstream3.services
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.net.toUri
import androidx.work.*
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.AppUtils.createNotificationChannel
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDub
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getImageBitmapFromUrl
import kotlinx.coroutines.withTimeoutOrNull
import java.util.concurrent.TimeUnit
const val SUBSCRIPTION_CHANNEL_ID = "cloudstream3.subscriptions"
const val SUBSCRIPTION_WORK_NAME = "work_subscription"
const val SUBSCRIPTION_CHANNEL_NAME = "Subscriptions"
const val SUBSCRIPTION_CHANNEL_DESCRIPTION = "Notifications for new episodes on subscribed shows"
const val SUBSCRIPTION_NOTIFICATION_ID = 938712897 // Random unique
class SubscriptionWorkManager(val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) {
companion object {
fun enqueuePeriodicWork(context: Context?) {
if (context == null) return
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val periodicSyncDataWork =
PeriodicWorkRequest.Builder(SubscriptionWorkManager::class.java, 6, TimeUnit.HOURS)
.addTag(SUBSCRIPTION_WORK_NAME)
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
SUBSCRIPTION_WORK_NAME,
ExistingPeriodicWorkPolicy.KEEP,
periodicSyncDataWork
)
// Uncomment below for testing
// val oneTimeSyncDataWork =
// OneTimeWorkRequest.Builder(SubscriptionWorkManager::class.java)
// .addTag(SUBSCRIPTION_WORK_NAME)
// .setConstraints(constraints)
// .build()
//
// WorkManager.getInstance(context).enqueue(oneTimeSyncDataWork)
}
}
private val progressNotificationBuilder =
NotificationCompat.Builder(context, SUBSCRIPTION_CHANNEL_ID)
.setAutoCancel(false)
.setColorized(true)
.setOnlyAlertOnce(true)
.setSilent(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setColor(context.colorFromAttribute(R.attr.colorPrimary))
.setContentTitle(context.getString(R.string.subscription_in_progress_notification))
.setSmallIcon(R.drawable.quantum_ic_refresh_white_24)
.setProgress(0, 0, true)
private val updateNotificationBuilder =
NotificationCompat.Builder(context, SUBSCRIPTION_CHANNEL_ID)
.setColorized(true)
.setOnlyAlertOnce(true)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setColor(context.colorFromAttribute(R.attr.colorPrimary))
.setSmallIcon(R.drawable.ic_cloudstream_monochrome_big)
private val notificationManager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
private fun updateProgress(max: Int, progress: Int, indeterminate: Boolean) {
notificationManager.notify(
SUBSCRIPTION_NOTIFICATION_ID, progressNotificationBuilder
.setProgress(max, progress, indeterminate)
.build()
)
}
override suspend fun doWork(): Result {
// println("Update subscriptions!")
context.createNotificationChannel(
SUBSCRIPTION_CHANNEL_ID,
SUBSCRIPTION_CHANNEL_NAME,
SUBSCRIPTION_CHANNEL_DESCRIPTION
)
setForeground(
ForegroundInfo(
SUBSCRIPTION_NOTIFICATION_ID,
progressNotificationBuilder.build()
)
)
val subscriptions = getAllSubscriptions()
if (subscriptions.isEmpty()) {
WorkManager.getInstance(context).cancelWorkById(this.id)
return Result.success()
}
val max = subscriptions.size
var progress = 0
updateProgress(max, progress, true)
// We need all plugins loaded.
PluginManager.loadAllOnlinePlugins(context)
PluginManager.loadAllLocalPlugins(context, false)
subscriptions.apmap { savedData ->
try {
val id = savedData.id ?: return@apmap null
val api = getApiFromNameNull(savedData.apiName) ?: return@apmap null
// Reasonable timeout to prevent having this worker run forever.
val response = withTimeoutOrNull(60_000) {
api.load(savedData.url) as? EpisodeResponse
} ?: return@apmap null
val dubPreference =
getDub(id) ?: if (
context.getApiDubstatusSettings().contains(DubStatus.Dubbed)
) {
DubStatus.Dubbed
} else {
DubStatus.Subbed
}
val latestEpisodes = response.getLatestEpisodes()
val latestPreferredEpisode = latestEpisodes[dubPreference]
val (shouldUpdate, latestEpisode) = if (latestPreferredEpisode != null) {
val latestSeenEpisode =
savedData.lastSeenEpisodeCount[dubPreference] ?: Int.MIN_VALUE
val shouldUpdate = latestPreferredEpisode > latestSeenEpisode
shouldUpdate to latestPreferredEpisode
} else {
val latestEpisode = latestEpisodes[DubStatus.None] ?: Int.MIN_VALUE
val latestSeenEpisode =
savedData.lastSeenEpisodeCount[DubStatus.None] ?: Int.MIN_VALUE
val shouldUpdate = latestEpisode > latestSeenEpisode
shouldUpdate to latestEpisode
}
DataStoreHelper.updateSubscribedData(
id,
savedData,
response
)
if (shouldUpdate) {
val updateHeader = savedData.name
val updateDescription = txt(
R.string.subscription_episode_released,
latestEpisode,
savedData.name
).asString(context)
val intent = Intent(context, MainActivity::class.java).apply {
data = savedData.url.toUri()
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_IMMUTABLE
)
} else {
PendingIntent.getActivity(context, 0, intent, 0)
}
val poster = ioWork {
savedData.posterUrl?.let { url ->
context.getImageBitmapFromUrl(
url,
savedData.posterHeaders
)
}
}
val updateNotification =
updateNotificationBuilder.setContentTitle(updateHeader)
.setContentText(updateDescription)
.setContentIntent(pendingIntent)
.setLargeIcon(poster)
.build()
notificationManager.notify(id, updateNotification)
}
// You can probably get some issues here since this is async but it does not matter much.
updateProgress(max, ++progress, false)
} catch (_: Throwable) {
}
}
return Result.success()
}
}

View file

@ -1,11 +1,22 @@
package com.lagradost.cloudstream3.services package com.lagradost.cloudstream3.services
import android.app.Service
import android.app.IntentService
import android.content.Intent import android.content.Intent
import android.os.IBinder
import com.lagradost.cloudstream3.utils.VideoDownloadManager import com.lagradost.cloudstream3.utils.VideoDownloadManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
class VideoDownloadService : IntentService("VideoDownloadService") { class VideoDownloadService : Service() {
override fun onHandleIntent(intent: Intent?) {
private val downloadScope = CoroutineScope(Dispatchers.Default)
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent != null) { if (intent != null) {
val id = intent.getIntExtra("id", -1) val id = intent.getIntExtra("id", -1)
val type = intent.getStringExtra("type") val type = intent.getStringExtra("type")
@ -14,10 +25,36 @@ class VideoDownloadService : IntentService("VideoDownloadService") {
"resume" -> VideoDownloadManager.DownloadActionType.Resume "resume" -> VideoDownloadManager.DownloadActionType.Resume
"pause" -> VideoDownloadManager.DownloadActionType.Pause "pause" -> VideoDownloadManager.DownloadActionType.Pause
"stop" -> VideoDownloadManager.DownloadActionType.Stop "stop" -> VideoDownloadManager.DownloadActionType.Stop
else -> return else -> return START_NOT_STICKY
}
downloadScope.launch {
VideoDownloadManager.downloadEvent.invoke(Pair(id, state))
} }
VideoDownloadManager.downloadEvent.invoke(Pair(id, state))
} }
} }
return START_NOT_STICKY
} }
}
override fun onDestroy() {
downloadScope.coroutineContext.cancel()
super.onDestroy()
}
}
// override fun onHandleIntent(intent: Intent?) {
// if (intent != null) {
// val id = intent.getIntExtra("id", -1)
// val type = intent.getStringExtra("type")
// if (id != -1 && type != null) {
// val state = when (type) {
// "resume" -> VideoDownloadManager.DownloadActionType.Resume
// "pause" -> VideoDownloadManager.DownloadActionType.Pause
// "stop" -> VideoDownloadManager.DownloadActionType.Stop
// else -> return
// }
// VideoDownloadManager.downloadEvent.invoke(Pair(id, state))
// }
// }
// }
//}

View file

@ -45,6 +45,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI {
const val appString = "cloudstreamapp" const val appString = "cloudstreamapp"
const val appStringRepo = "cloudstreamrepo" const val appStringRepo = "cloudstreamrepo"
const val appStringPlayer = "cloudstreamplayer"
// Instantly start the search given a query // Instantly start the search given a query
const val appStringSearch = "cloudstreamsearch" const val appStringSearch = "cloudstreamsearch"

View file

@ -759,6 +759,11 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
return data != "" return data != ""
} }
/** Used to query a saved MediaItem on the list to get the id for removal */
data class MediaListItemRoot(@JsonProperty("data") val data: MediaListItem? = null)
data class MediaListItem(@JsonProperty("MediaList") val MediaList: MediaListId? = null)
data class MediaListId(@JsonProperty("id") val id: Long? = null)
private suspend fun postDataAboutId( private suspend fun postDataAboutId(
id: Int, id: Int,
type: AniListStatusType, type: AniListStatusType,
@ -766,19 +771,43 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
progress: Int? progress: Int?
): Boolean { ): Boolean {
val q = val q =
"""mutation (${'$'}id: Int = $id, ${'$'}status: MediaListStatus = ${ // Delete item if status type is None
aniListStatusString[maxOf( if (type == AniListStatusType.None) {
0, val userID = getKey<AniListUser>(accountId, ANILIST_USER_KEY)?.id ?: return false
type.value // Get list ID for deletion
)] val idQuery = """
}, ${if (score != null) "${'$'}scoreRaw: Int = ${score * 10}" else ""} , ${if (progress != null) "${'$'}progress: Int = $progress" else ""}) { query MediaList(${'$'}userId: Int = $userID, ${'$'}mediaId: Int = $id) {
SaveMediaListEntry (mediaId: ${'$'}id, status: ${'$'}status, scoreRaw: ${'$'}scoreRaw, progress: ${'$'}progress) { MediaList(userId: ${'$'}userId, mediaId: ${'$'}mediaId) {
id id
status }
progress }
score """
} val response = postApi(idQuery)
val listId =
tryParseJson<MediaListItemRoot>(response)?.data?.MediaList?.id ?: return false
"""
mutation(${'$'}id: Int = $listId) {
DeleteMediaListEntry(id: ${'$'}id) {
deleted
}
}
"""
} else {
"""mutation (${'$'}id: Int = $id, ${'$'}status: MediaListStatus = ${
aniListStatusString[maxOf(
0,
type.value
)]
}, ${if (score != null) "${'$'}scoreRaw: Int = ${score * 10}" else ""} , ${if (progress != null) "${'$'}progress: Int = $progress" else ""}) {
SaveMediaListEntry (mediaId: ${'$'}id, status: ${'$'}status, scoreRaw: ${'$'}scoreRaw, progress: ${'$'}progress) {
id
status
progress
score
}
}""" }"""
}
val data = postApi(q) val data = postApi(q)
return data != "" return data != ""
} }

View file

@ -9,6 +9,7 @@ import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.library.ListSorting import com.lagradost.cloudstream3.ui.library.ListSorting
import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.Coroutines.ioWork import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
@ -74,13 +75,16 @@ class LocalList : SyncAPI {
group.value.mapNotNull { group.value.mapNotNull {
getBookmarkedData(it.first)?.toLibraryItem(it.first.toString()) getBookmarkedData(it.first)?.toLibraryItem(it.first.toString())
} }
} } + mapOf(R.string.subscription_list_name to getAllSubscriptions().mapNotNull {
it.toLibraryItem()
})
} }
val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate { val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate {
// None is not something to display // None is not something to display
it.stringRes to emptyList<SyncAPI.LibraryItem>() it.stringRes to emptyList<SyncAPI.LibraryItem>()
} } + mapOf(R.string.subscription_list_name to emptyList())
return SyncAPI.LibraryMetadata( return SyncAPI.LibraryMetadata(
(baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) }, (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) },
setOf( setOf(

View file

@ -24,7 +24,6 @@ import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.ui.player.LinkGenerator import com.lagradost.cloudstream3.ui.player.LinkGenerator
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE
@ -40,6 +39,7 @@ import kotlinx.android.synthetic.main.stream_input.*
import android.text.format.Formatter.formatShortFileSize import android.text.format.Formatter.formatShortFileSize
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.ui.player.BasicLink
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import java.net.URI import java.net.URI
@ -225,7 +225,7 @@ class DownloadFragment : Fragment() {
R.id.global_to_navigation_player, R.id.global_to_navigation_player,
GeneratorPlayer.newInstance( GeneratorPlayer.newInstance(
LinkGenerator( LinkGenerator(
listOf(url), listOf(BasicLink(url)),
extract = true, extract = true,
referer = referer, referer = referer,
isM3u8 = dialog.hls_switch?.isChecked isM3u8 = dialog.hls_switch?.isChecked

View file

@ -185,7 +185,7 @@ open class ParentItemAdapter(
) : ) :
RecyclerView.ViewHolder(itemView) { RecyclerView.ViewHolder(itemView) {
val title: TextView = itemView.home_child_more_info val title: TextView = itemView.home_child_more_info
val recyclerView: RecyclerView = itemView.home_child_recyclerview private val recyclerView: RecyclerView = itemView.home_child_recyclerview
fun update(expand: HomeViewModel.ExpandableHomepageList) { fun update(expand: HomeViewModel.ExpandableHomepageList) {
val info = expand.list val info = expand.list

View file

@ -9,8 +9,11 @@ import android.widget.FrameLayout
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.exoplayer2.* import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.C.* import com.google.android.exoplayer2.C.*
import com.google.android.exoplayer2.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON
import com.google.android.exoplayer2.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector
import com.google.android.exoplayer2.source.* import com.google.android.exoplayer2.source.*
import com.google.android.exoplayer2.text.TextRenderer import com.google.android.exoplayer2.text.TextRenderer
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
@ -35,11 +38,13 @@ import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle
import com.lagradost.cloudstream3.utils.EpisodeSkip import com.lagradost.cloudstream3.utils.EpisodeSkip
import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.ExtractorLinkPlayList import com.lagradost.cloudstream3.utils.ExtractorLinkPlayList
import com.lagradost.cloudstream3.utils.ExtractorUri import com.lagradost.cloudstream3.utils.ExtractorUri
import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
import java.io.File import java.io.File
import java.time.Duration
import javax.net.ssl.HttpsURLConnection import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSession import javax.net.ssl.SSLSession
@ -535,15 +540,17 @@ class CS3IPlayer : IPlayer {
OkHttpDataSource.Factory(client).setUserAgent(USER_AGENT) OkHttpDataSource.Factory(client).setUserAgent(USER_AGENT)
} }
// Do no include empty referer, if the provider wants those they can use the header map.
val refererMap =
if (link.referer.isBlank()) emptyMap() else mapOf("referer" to link.referer)
val headers = mapOf( val headers = mapOf(
"referer" to link.referer,
"accept" to "*/*", "accept" to "*/*",
"sec-ch-ua" to "\"Chromium\";v=\"91\", \" Not;A Brand\";v=\"99\"", "sec-ch-ua" to "\"Chromium\";v=\"91\", \" Not;A Brand\";v=\"99\"",
"sec-ch-ua-mobile" to "?0", "sec-ch-ua-mobile" to "?0",
"sec-fetch-user" to "?1", "sec-fetch-user" to "?1",
"sec-fetch-mode" to "navigate", "sec-fetch-mode" to "navigate",
"sec-fetch-dest" to "video" "sec-fetch-dest" to "video"
) + link.headers // Adds the headers from the provider, e.g Authorization ) + refererMap + link.headers // Adds the headers from the provider, e.g Authorization
return source.apply { return source.apply {
setDefaultRequestProperties(headers) setDefaultRequestProperties(headers)
@ -666,23 +673,27 @@ class CS3IPlayer : IPlayer {
val exoPlayerBuilder = val exoPlayerBuilder =
ExoPlayer.Builder(context) ExoPlayer.Builder(context)
.setRenderersFactory { eventHandler, videoRendererEventListener, audioRendererEventListener, textRendererOutput, metadataRendererOutput -> .setRenderersFactory { eventHandler, videoRendererEventListener, audioRendererEventListener, textRendererOutput, metadataRendererOutput ->
DefaultRenderersFactory(context).createRenderers( DefaultRenderersFactory(context).apply {
eventHandler, // setEnableDecoderFallback(true)
videoRendererEventListener, // Enable Ffmpeg extension
audioRendererEventListener, // setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON)
textRendererOutput, }.createRenderers(
metadataRendererOutput eventHandler,
).map { videoRendererEventListener,
if (it is TextRenderer) { audioRendererEventListener,
currentTextRenderer = CustomTextRenderer( textRendererOutput,
subtitleOffset, metadataRendererOutput
textRendererOutput, ).map {
eventHandler.looper, if (it is TextRenderer) {
CustomSubtitleDecoderFactory() currentTextRenderer = CustomTextRenderer(
) subtitleOffset,
currentTextRenderer!! textRendererOutput,
} else it eventHandler.looper,
}.toTypedArray() CustomSubtitleDecoderFactory()
)
currentTextRenderer!!
} else it
}.toTypedArray()
} }
.setTrackSelector( .setTrackSelector(
trackSelector ?: getTrackSelector( trackSelector ?: getTrackSelector(
@ -847,7 +858,7 @@ class CS3IPlayer : IPlayer {
Log.i(TAG, "loadExo") Log.i(TAG, "loadExo")
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
val maxVideoHeight = settingsManager.getInt( val maxVideoHeight = settingsManager.getInt(
context.getString(com.lagradost.cloudstream3.R.string.quality_pref_key), context.getString(if (context.isUsingMobileData()) com.lagradost.cloudstream3.R.string.quality_pref_mobile_data_key else com.lagradost.cloudstream3.R.string.quality_pref_key),
Int.MAX_VALUE Int.MAX_VALUE
) )
@ -1193,10 +1204,10 @@ class CS3IPlayer : IPlayer {
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.socketFactory) HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.socketFactory)
} }
val mime = if (link.isM3u8) { val mime = when {
MimeTypes.APPLICATION_M3U8 link.isM3u8 -> MimeTypes.APPLICATION_M3U8
} else { link.isDash -> MimeTypes.APPLICATION_MPD
MimeTypes.VIDEO_MP4 else -> MimeTypes.VIDEO_MP4
} }
val mediaItems = if (link is ExtractorLinkPlayList) { val mediaItems = if (link is ExtractorLinkPlayList) {
@ -1246,4 +1257,4 @@ class CS3IPlayer : IPlayer {
loadOfflinePlayer(context, it) loadOfflinePlayer(context, it)
} }
} }
} }

View file

@ -4,13 +4,16 @@ import android.content.Context
import android.util.Log import android.util.Log
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.exoplayer2.Format import com.google.android.exoplayer2.Format
import com.google.android.exoplayer2.text.SubtitleDecoder import com.google.android.exoplayer2.text.*
import com.google.android.exoplayer2.text.SubtitleDecoderFactory import com.google.android.exoplayer2.text.cea.Cea608Decoder
import com.google.android.exoplayer2.text.SubtitleInputBuffer import com.google.android.exoplayer2.text.cea.Cea708Decoder
import com.google.android.exoplayer2.text.SubtitleOutputBuffer import com.google.android.exoplayer2.text.dvb.DvbDecoder
import com.google.android.exoplayer2.text.pgs.PgsDecoder
import com.google.android.exoplayer2.text.ssa.SsaDecoder import com.google.android.exoplayer2.text.ssa.SsaDecoder
import com.google.android.exoplayer2.text.subrip.SubripDecoder import com.google.android.exoplayer2.text.subrip.SubripDecoder
import com.google.android.exoplayer2.text.ttml.TtmlDecoder import com.google.android.exoplayer2.text.ttml.TtmlDecoder
import com.google.android.exoplayer2.text.tx3g.Tx3gDecoder
import com.google.android.exoplayer2.text.webvtt.Mp4WebvttDecoder
import com.google.android.exoplayer2.text.webvtt.WebvttDecoder import com.google.android.exoplayer2.text.webvtt.WebvttDecoder
import com.google.android.exoplayer2.util.MimeTypes import com.google.android.exoplayer2.util.MimeTypes
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
@ -19,7 +22,11 @@ import org.mozilla.universalchardet.UniversalDetector
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.charset.Charset import java.nio.charset.Charset
class CustomDecoder : SubtitleDecoder { /**
* @param fallbackFormat used to create a decoder based on mimetype if the subtitle string is not
* enough to identify the subtitle format.
**/
class CustomDecoder(private val fallbackFormat: Format?) : SubtitleDecoder {
companion object { companion object {
fun updateForcedEncoding(context: Context) { fun updateForcedEncoding(context: Context) {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
@ -139,7 +146,7 @@ class CustomDecoder : SubtitleDecoder {
val inputString = getStr(inputBuffer) val inputString = getStr(inputBuffer)
if (realDecoder == null && !inputString.isNullOrBlank()) { if (realDecoder == null && !inputString.isNullOrBlank()) {
var str: String = inputString var str: String = inputString
// this way we read the subtitle file and decide what decoder to use instead of relying on mimetype // this way we read the subtitle file and decide what decoder to use instead of relying fully on mimetype
Log.i(TAG, "Got data from queueInputBuffer") Log.i(TAG, "Got data from queueInputBuffer")
//https://github.com/LagradOst/CloudStream-2/blob/ddd774ee66810137ff7bd65dae70bcf3ba2d2489/CloudStreamForms/CloudStreamForms/Script/MainChrome.cs#L388 //https://github.com/LagradOst/CloudStream-2/blob/ddd774ee66810137ff7bd65dae70bcf3ba2d2489/CloudStreamForms/CloudStreamForms/Script/MainChrome.cs#L388
realDecoder = when { realDecoder = when {
@ -148,8 +155,31 @@ class CustomDecoder : SubtitleDecoder {
(str.startsWith( (str.startsWith(
"[Script Info]", "[Script Info]",
ignoreCase = true ignoreCase = true
) || str.startsWith("Title:", ignoreCase = true)) -> SsaDecoder() ) || str.startsWith("Title:", ignoreCase = true)) -> SsaDecoder(fallbackFormat?.initializationData)
str.startsWith("1", ignoreCase = true) -> SubripDecoder() str.startsWith("1", ignoreCase = true) -> SubripDecoder()
fallbackFormat != null -> {
when (val mimeType = fallbackFormat.sampleMimeType) {
MimeTypes.TEXT_VTT -> WebvttDecoder()
MimeTypes.TEXT_SSA -> SsaDecoder(fallbackFormat.initializationData)
MimeTypes.APPLICATION_MP4VTT -> Mp4WebvttDecoder()
MimeTypes.APPLICATION_TTML -> TtmlDecoder()
MimeTypes.APPLICATION_SUBRIP -> SubripDecoder()
MimeTypes.APPLICATION_TX3G -> Tx3gDecoder(fallbackFormat.initializationData)
MimeTypes.APPLICATION_CEA608, MimeTypes.APPLICATION_MP4CEA608 -> Cea608Decoder(
mimeType,
fallbackFormat.accessibilityChannel,
Cea608Decoder.MIN_DATA_CHANNEL_TIMEOUT_MS
)
MimeTypes.APPLICATION_CEA708 -> Cea708Decoder(
fallbackFormat.accessibilityChannel,
fallbackFormat.initializationData
)
MimeTypes.APPLICATION_DVBSUBS -> DvbDecoder(fallbackFormat.initializationData)
MimeTypes.APPLICATION_PGS -> PgsDecoder()
MimeTypes.TEXT_EXOPLAYER_CUES -> ExoplayerCuesDecoder()
else -> null
}
}
else -> null else -> null
} }
Log.i( Log.i(
@ -246,28 +276,6 @@ class CustomSubtitleDecoderFactory : SubtitleDecoderFactory {
} }
override fun createDecoder(format: Format): SubtitleDecoder { override fun createDecoder(format: Format): SubtitleDecoder {
return CustomDecoder() return CustomDecoder(format)
//return when (val mimeType = format.sampleMimeType) {
// MimeTypes.TEXT_VTT -> WebvttDecoder()
// MimeTypes.TEXT_SSA -> SsaDecoder(format.initializationData)
// MimeTypes.APPLICATION_MP4VTT -> Mp4WebvttDecoder()
// MimeTypes.APPLICATION_TTML -> TtmlDecoder()
// MimeTypes.APPLICATION_SUBRIP -> SubripDecoder()
// MimeTypes.APPLICATION_TX3G -> Tx3gDecoder(format.initializationData)
// MimeTypes.APPLICATION_CEA608, MimeTypes.APPLICATION_MP4CEA608 -> return Cea608Decoder(
// mimeType,
// format.accessibilityChannel,
// Cea608Decoder.MIN_DATA_CHANNEL_TIMEOUT_MS
// )
// MimeTypes.APPLICATION_CEA708 -> Cea708Decoder(
// format.accessibilityChannel,
// format.initializationData
// )
// MimeTypes.APPLICATION_DVBSUBS -> DvbDecoder(format.initializationData)
// MimeTypes.APPLICATION_PGS -> PgsDecoder()
// MimeTypes.TEXT_EXOPLAYER_CUES -> ExoplayerCuesDecoder()
// // Default WebVttDecoder
// else -> WebvttDecoder()
//}
} }
} }

View file

@ -42,7 +42,7 @@ class DownloadedPlayerActivity : AppCompatActivity() {
R.id.global_to_navigation_player, GeneratorPlayer.newInstance( R.id.global_to_navigation_player, GeneratorPlayer.newInstance(
LinkGenerator( LinkGenerator(
listOf( listOf(
url BasicLink(url)
) )
) )
) )

View file

@ -40,6 +40,7 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer.Companion.subsProvidersIsActive import com.lagradost.cloudstream3.ui.player.GeneratorPlayer.Companion.subsProvidersIsActive
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
@ -83,6 +84,7 @@ const val HORIZONTAL_MULTIPLIER = 2.0f
const val DOUBLE_TAB_MAXIMUM_HOLD_TIME = 200L const val DOUBLE_TAB_MAXIMUM_HOLD_TIME = 200L
const val DOUBLE_TAB_MINIMUM_TIME_BETWEEN = 200L // this also affects the UI show response time const val DOUBLE_TAB_MINIMUM_TIME_BETWEEN = 200L // this also affects the UI show response time
const val DOUBLE_TAB_PAUSE_PERCENTAGE = 0.15 // in both directions const val DOUBLE_TAB_PAUSE_PERCENTAGE = 0.15 // in both directions
private const val SUBTITLE_DELAY_BUNDLE_KEY = "subtitle_delay"
// All the UI Logic for the player // All the UI Logic for the player
open class FullScreenPlayer : AbstractPlayerFragment() { open class FullScreenPlayer : AbstractPlayerFragment() {
@ -1119,11 +1121,20 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
resetRewindText() resetRewindText()
} }
override fun onSaveInstanceState(outState: Bundle) {
// As this is video specific it is better to not do any setKey/getKey
outState.putLong(SUBTITLE_DELAY_BUNDLE_KEY, subtitleDelay)
super.onSaveInstanceState(outState)
}
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
// init variables // init variables
setPlayBackSpeed(getKey(PLAYBACK_SPEED_KEY) ?: 1.0f) setPlayBackSpeed(getKey(PLAYBACK_SPEED_KEY) ?: 1.0f)
savedInstanceState?.getLong(SUBTITLE_DELAY_BUNDLE_KEY)?.let {
subtitleDelay = it
}
// handle tv controls // handle tv controls
playerEventListener = { eventType -> playerEventListener = { eventType ->
@ -1246,9 +1257,8 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
ctx.getString(R.string.double_tap_pause_enabled_key), ctx.getString(R.string.double_tap_pause_enabled_key),
false false
) )
currentPrefQuality = settingsManager.getInt( currentPrefQuality = settingsManager.getInt(
ctx.getString(R.string.quality_pref_key), ctx.getString(if (ctx.isUsingMobileData()) R.string.quality_pref_mobile_data_key else R.string.quality_pref_key),
currentPrefQuality currentPrefQuality
) )
// useSystemBrightness = // useSystemBrightness =

View file

@ -13,7 +13,6 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.* import android.widget.*
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.core.animation.addListener import androidx.core.animation.addListener
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isGone import androidx.core.view.isGone
@ -734,19 +733,17 @@ class GeneratorPlayer : FullScreenPlayer() {
} }
val currentAudioTracks = tracks.allAudioTracks val currentAudioTracks = tracks.allAudioTracks
val trackBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack) val trackDialog = Dialog(ctx, R.style.AlertDialogCustomBlack)
.setView(R.layout.player_select_tracks) trackDialog.setContentView(R.layout.player_select_tracks)
trackDialog.show()
val tracksDialog = trackBuilder.create()
// selectTracksDialog = tracksDialog // selectTracksDialog = tracksDialog
tracksDialog.show() val videosList = trackDialog.video_tracks_list
val videosList = tracksDialog.video_tracks_list val audioList = trackDialog.auto_tracks_list
val audioList = tracksDialog.auto_tracks_list
tracksDialog.video_tracks_holder.isVisible = currentVideoTracks.size > 1 trackDialog.video_tracks_holder.isVisible = currentVideoTracks.size > 1
tracksDialog.audio_tracks_holder.isVisible = currentAudioTracks.size > 1 trackDialog.audio_tracks_holder.isVisible = currentAudioTracks.size > 1
fun dismiss() { fun dismiss() {
if (isPlaying) { if (isPlaying) {
@ -781,7 +778,7 @@ class GeneratorPlayer : FullScreenPlayer() {
videosList.setItemChecked(which, true) videosList.setItemChecked(which, true)
} }
tracksDialog.setOnDismissListener { trackDialog.setOnDismissListener {
dismiss() dismiss()
// selectTracksDialog = null // selectTracksDialog = null
} }
@ -811,11 +808,11 @@ class GeneratorPlayer : FullScreenPlayer() {
audioList.setItemChecked(which, true) audioList.setItemChecked(which, true)
} }
tracksDialog.cancel_btt?.setOnClickListener { trackDialog.cancel_btt?.setOnClickListener {
tracksDialog.dismissSafe(activity) trackDialog.dismissSafe(activity)
} }
tracksDialog.apply_btt?.setOnClickListener { trackDialog.apply_btt?.setOnClickListener {
val currentTrack = currentAudioTracks.getOrNull(audioIndexStart) val currentTrack = currentAudioTracks.getOrNull(audioIndexStart)
player.setPreferredAudioTrack( player.setPreferredAudioTrack(
currentTrack?.language, currentTrack?.id currentTrack?.language, currentTrack?.id
@ -828,7 +825,7 @@ class GeneratorPlayer : FullScreenPlayer() {
player.setMaxVideoSize(width, height, currentVideo?.id) player.setMaxVideoSize(width, height, currentVideo?.id)
} }
tracksDialog.dismissSafe(activity) trackDialog.dismissSafe(activity)
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -1145,7 +1142,7 @@ class GeneratorPlayer : FullScreenPlayer() {
val source = currentSelectedLink?.first?.name ?: currentSelectedLink?.second?.name ?: "NULL" val source = currentSelectedLink?.first?.name ?: currentSelectedLink?.second?.name ?: "NULL"
val title = when (titleRez) { val title = when (titleRez) {
0 -> "" 0 -> ""
1 -> extra 1 -> extra
2 -> source 2 -> source

View file

@ -5,8 +5,15 @@ import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import java.net.URI import java.net.URI
/**
* Used to open the player more easily with the LinkGenerator
**/
data class BasicLink(
val url: String,
val name: String? = null,
)
class LinkGenerator( class LinkGenerator(
private val links: List<String>, private val links: List<BasicLink>,
private val extract: Boolean = true, private val extract: Boolean = true,
private val referer: String? = null, private val referer: String? = null,
private val isM3u8: Boolean? = null private val isM3u8: Boolean? = null
@ -47,7 +54,7 @@ class LinkGenerator(
offset: Int offset: Int
): Boolean { ): Boolean {
links.amap { link -> links.amap { link ->
if (!extract || !loadExtractor(link, referer, { if (!extract || !loadExtractor(link.url, referer, {
subtitleCallback(PlayerSubtitleHelper.getSubtitleData(it)) subtitleCallback(PlayerSubtitleHelper.getSubtitleData(it))
}) { }) {
callback(it to null) callback(it to null)
@ -57,11 +64,11 @@ class LinkGenerator(
callback( callback(
ExtractorLink( ExtractorLink(
"", "",
link, link.name ?: link.url,
unshortenLinkSafe(link), // unshorten because it might be a raw link unshortenLinkSafe(link.url), // unshorten because it might be a raw link
referer ?: "", referer ?: "",
Qualities.Unknown.value, isM3u8 ?: normalSafeApiCall { Qualities.Unknown.value, isM3u8 ?: normalSafeApiCall {
URI(link).path?.substringAfterLast(".")?.contains("m3u") URI(link.url).path?.substringAfterLast(".")?.contains("m3u")
} ?: false } ?: false
) to null ) to null
) )

View file

@ -7,13 +7,13 @@ import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
fun RecyclerView?.setLinearListLayout(isHorizontal: Boolean = true) { fun RecyclerView?.setLinearListLayout(isHorizontal: Boolean = true) {
if(this == null) return if (this == null) return
this.layoutManager = this.layoutManager =
this.context?.let { LinearListLayout(it).apply { if (isHorizontal) setHorizontal() else setVertical() } } this.context?.let { LinearListLayout(it).apply { if (isHorizontal) setHorizontal() else setVertical() } }
?: this.layoutManager ?: this.layoutManager
} }
class LinearListLayout(context: Context?) : open class LinearListLayout(context: Context?) :
LinearLayoutManager(context) { LinearLayoutManager(context) {
fun setHorizontal() { fun setHorizontal() {
@ -24,7 +24,8 @@ class LinearListLayout(context: Context?) :
orientation = VERTICAL orientation = VERTICAL
} }
private fun getCorrectParent(focused: View): View? { private fun getCorrectParent(focused: View?): View? {
if (focused == null) return null
var current: View? = focused var current: View? = focused
val last: ArrayList<View> = arrayListOf(focused) val last: ArrayList<View> = arrayListOf(focused)
while (current != null && current !is RecyclerView) { while (current != null && current !is RecyclerView) {
@ -54,10 +55,17 @@ class LinearListLayout(context: Context?) :
linearSmoothScroller.targetPosition = position linearSmoothScroller.targetPosition = position
startSmoothScroll(linearSmoothScroller) startSmoothScroll(linearSmoothScroller)
}*/ }*/
override fun onInterceptFocusSearch(focused: View, direction: Int): View? { override fun onInterceptFocusSearch(focused: View, direction: Int): View? {
val dir = if (orientation == HORIZONTAL) { val dir = if (orientation == HORIZONTAL) {
if (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP) return null if (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP) {
// This scrolls the recyclerview before doing focus search, which
// allows the focus search to work better.
// Without this the recyclerview focus location on the screen
// would change when scrolling between recyclerviews.
(focused.parent as? RecyclerView)?.focusSearch(direction)
return null
}
if (direction == View.FOCUS_RIGHT) 1 else -1 if (direction == View.FOCUS_RIGHT) 1 else -1
} else { } else {
if (direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT) return null if (direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT) return null

View file

@ -15,24 +15,28 @@ import android.view.ViewGroup
import android.widget.AbsListView import android.widget.AbsListView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.ImageView import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import com.discord.panels.OverlappingPanelsLayout import com.discord.panels.OverlappingPanelsLayout
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipDrawable import com.google.android.material.chip.ChipDrawable
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.DubStatus import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.mvvm.* import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.services.SubscriptionWorkManager
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
@ -528,6 +532,25 @@ open class ResultFragment : ResultTrailerPlayer() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
result_cast_items?.layoutManager = object : LinearListLayout(view.context) {
override fun onRequestChildFocus(
parent: RecyclerView,
state: RecyclerView.State,
child: View,
focused: View?
): Boolean {
// Make the cast always focus the first visible item when focused
// from somewhere else. Otherwise it jumps to the last item.
return if (parent.focusedChild == null) {
scrollToPosition(this.findFirstCompletelyVisibleItemPosition())
true
} else {
super.onRequestChildFocus(parent, state, child, focused)
}
}
}.apply {
this.orientation = RecyclerView.HORIZONTAL
}
result_cast_items?.adapter = ActorAdaptor() result_cast_items?.adapter = ActorAdaptor()
updateUIListener = ::updateUI updateUIListener = ::updateUI
@ -850,7 +873,7 @@ open class ResultFragment : ResultTrailerPlayer() {
} }
observe(viewModel.page) { data -> observe(viewModel.page) { data ->
if(data == null) return@observe if (data == null) return@observe
when (data) { when (data) {
is Resource.Success -> { is Resource.Success -> {
val d = data.value val d = data.value
@ -904,6 +927,36 @@ open class ResultFragment : ResultTrailerPlayer() {
updateList(d.actors ?: emptyList()) updateList(d.actors ?: emptyList())
} }
observeNullable(viewModel.subscribeStatus) { isSubscribed ->
result_subscribe?.isVisible = isSubscribed != null
if (isSubscribed == null) return@observeNullable
val drawable = if (isSubscribed) {
R.drawable.ic_baseline_notifications_active_24
} else {
R.drawable.baseline_notifications_none_24
}
result_subscribe?.setImageResource(drawable)
}
result_subscribe?.setOnClickListener {
val isSubscribed =
viewModel.toggleSubscriptionStatus() ?: return@setOnClickListener
val message = if (isSubscribed) {
// Kinda icky to have this here, but it works.
SubscriptionWorkManager.enqueuePeriodicWork(context)
R.string.subscription_new
} else {
R.string.subscription_deleted
}
val name = (viewModel.page.value as? Resource.Success)?.value?.title
?: txt(R.string.no_data).asStringNull(context) ?: ""
showToast(activity, txt(message, name), Toast.LENGTH_SHORT)
}
result_open_in_browser?.isVisible = d.url.startsWith("http") result_open_in_browser?.isVisible = d.url.startsWith("http")
result_open_in_browser?.setOnClickListener { result_open_in_browser?.setOnClickListener {
val i = Intent(ACTION_VIEW) val i = Intent(ACTION_VIEW)

View file

@ -322,9 +322,7 @@ class ResultFragmentPhone : ResultFragment() {
// it?.dismiss() // it?.dismiss()
//} //}
builder.setCanceledOnTouchOutside(true) builder.setCanceledOnTouchOutside(true)
builder.show() builder.show()
builder builder
} }
} }

View file

@ -176,8 +176,7 @@ class ResultFragmentTv : ResultFragment() {
loadingDialog = null loadingDialog = null
} }
loadingDialog = loadingDialog ?: context?.let { ctx -> loadingDialog = loadingDialog ?: context?.let { ctx ->
val builder = val builder = BottomSheetDialog(ctx)
BottomSheetDialog(ctx)
builder.setContentView(R.layout.bottom_loading) builder.setContentView(R.layout.bottom_loading)
builder.setOnDismissListener { builder.setOnDismissListener {
loadingDialog = null loadingDialog = null
@ -187,9 +186,7 @@ class ResultFragmentTv : ResultFragment() {
// it?.dismiss() // it?.dismiss()
//} //}
builder.setCanceledOnTouchOutside(true) builder.setCanceledOnTouchOutside(true)
builder.show() builder.show()
builder builder
} }
} }

View file

@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.ui.result
import android.app.Activity import android.app.Activity
import android.content.* import android.content.*
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
@ -16,6 +17,7 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.APIHolder.unixTime import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.getCastSession import com.lagradost.cloudstream3.CommonActivity.getCastSession
import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.showToast
@ -25,6 +27,7 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId
import com.lagradost.cloudstream3.LoadResponse.Companion.isMovie import com.lagradost.cloudstream3.LoadResponse.Companion.isMovie
import com.lagradost.cloudstream3.metaproviders.SyncRedirector import com.lagradost.cloudstream3.metaproviders.SyncRedirector
import com.lagradost.cloudstream3.mvvm.* import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.APIRepository
@ -413,6 +416,9 @@ class ResultViewModel2 : ViewModel() {
private val _episodeSynopsis: MutableLiveData<String?> = MutableLiveData(null) private val _episodeSynopsis: MutableLiveData<String?> = MutableLiveData(null)
val episodeSynopsis: LiveData<String?> = _episodeSynopsis val episodeSynopsis: LiveData<String?> = _episodeSynopsis
private val _subscribeStatus: MutableLiveData<Boolean?> = MutableLiveData(null)
val subscribeStatus: LiveData<Boolean?> = _subscribeStatus
companion object { companion object {
const val TAG = "RVM2" const val TAG = "RVM2"
private const val EPISODE_RANGE_SIZE = 20 private const val EPISODE_RANGE_SIZE = 20
@ -814,6 +820,42 @@ class ResultViewModel2 : ViewModel() {
} }
} }
/**
* @return true if the new status is Subscribed, false if not. Null if not possible to subscribe.
**/
fun toggleSubscriptionStatus(): Boolean? {
val isSubscribed = _subscribeStatus.value ?: return null
val response = currentResponse ?: return null
if (response !is EpisodeResponse) return null
val currentId = response.getId()
if (isSubscribed) {
DataStoreHelper.removeSubscribedData(currentId)
} else {
val current = DataStoreHelper.getSubscribedData(currentId)
DataStoreHelper.setSubscribedData(
currentId,
DataStoreHelper.SubscribedData(
currentId,
current?.bookmarkedTime ?: unixTimeMS,
unixTimeMS,
response.getLatestEpisodes(),
response.name,
response.url,
response.apiName,
response.type,
response.posterUrl,
response.year
)
)
}
_subscribeStatus.postValue(!isSubscribed)
return !isSubscribed
}
private fun startChromecast( private fun startChromecast(
activity: Activity?, activity: Activity?,
result: ResultEpisode, result: ResultEpisode,
@ -1084,7 +1126,12 @@ class ResultViewModel2 : ViewModel() {
1L 1L
} }
component = VLC_COMPONENT // Component no longer safe to use in A13 for VLC
// https://code.videolan.org/videolan/vlc-android/-/issues/2776
// This will likely need to be updated once VLC fixes their documentation.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
component = VLC_COMPONENT
}
putExtra("from_start", !resume) putExtra("from_start", !resume)
putExtra("position", position) putExtra("position", position)
@ -1422,85 +1469,128 @@ class ResultViewModel2 : ViewModel() {
meta: SyncAPI.SyncResult?, meta: SyncAPI.SyncResult?,
syncs: Map<String, String>? = null syncs: Map<String, String>? = null
): Pair<LoadResponse, Boolean> { ): Pair<LoadResponse, Boolean> {
if (meta == null) return resp to false //if (meta == null) return resp to false
var updateEpisodes = false var updateEpisodes = false
val out = resp.apply { val out = resp.apply {
Log.i(TAG, "applyMeta") Log.i(TAG, "applyMeta")
duration = duration ?: meta.duration if (meta != null) {
rating = rating ?: meta.publicScore duration = duration ?: meta.duration
tags = tags ?: meta.genres rating = rating ?: meta.publicScore
plot = if (plot.isNullOrBlank()) meta.synopsis else plot tags = tags ?: meta.genres
posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl plot = if (plot.isNullOrBlank()) meta.synopsis else plot
actors = actors ?: meta.actors posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl
actors = actors ?: meta.actors
if (this is EpisodeResponse) { if (this is EpisodeResponse) {
nextAiring = nextAiring ?: meta.nextAiring nextAiring = nextAiring ?: meta.nextAiring
}
val realRecommendations = ArrayList<SearchResponse>()
val apiNames = apis.filter {
it.name.contains("gogoanime", true) ||
it.name.contains("9anime", true)
}.map {
it.name
}
meta.recommendations?.forEach { rec ->
apiNames.forEach { name ->
realRecommendations.add(rec.copy(apiName = name))
}
}
recommendations = recommendations?.union(realRecommendations)?.toList()
?: realRecommendations
} }
for ((k, v) in syncs ?: emptyMap()) { for ((k, v) in syncs ?: emptyMap()) {
syncData[k] = v syncData[k] = v
} }
val realRecommendations = ArrayList<SearchResponse>() argamap(
// TODO: fix {
val apiNames = apis.filter { if (this !is AnimeLoadResponse) return@argamap
it.name.contains("gogoanime", true) || // already exist, no need to run getTracker
it.name.contains("9anime", true) if (this.getAniListId() != null && this.getMalId() != null) return@argamap
}.map {
it.name
}
meta.recommendations?.forEach { rec -> val res = APIHolder.getTracker(
apiNames.forEach { name -> listOfNotNull(
realRecommendations.add(rec.copy(apiName = name)) this.engName,
} this.name,
} this.japName
).filter { it.length > 2 }
recommendations = recommendations?.union(realRecommendations)?.toList() .distinct(), // the reason why we filter is due to not wanting smth like " " or "?"
?: realRecommendations TrackerType.getTypes(this.type),
this.year
argamap({
addTrailer(meta.trailers)
}, {
if (this !is AnimeLoadResponse) return@argamap
val map =
Kitsu.getEpisodesDetails(
getMalId(),
getAniListId(),
isResponseRequired = false
) )
if (map.isNullOrEmpty()) return@argamap
updateEpisodes = DubStatus.values().map { dubStatus -> val ids = arrayOf(
val current = AccountManager.malApi.idPrefix to res?.malId?.toString(),
this.episodes[dubStatus]?.mapIndexed { index, episode -> AccountManager.aniListApi.idPrefix to res?.aniId
episode.apply { )
this.episode = this.episode ?: (index + 1)
} if (ids.any { (id, new) ->
}?.sortedBy { it.episode ?: 0 }?.toMutableList() val current = syncData[id]
if (current.isNullOrEmpty()) return@map false new != null && current != null && current != new
val episodeNumbers = current.map { ep -> ep.episode!! } }
var updateCount = 0 ) {
map.forEach { (episode, node) -> // getTracker fucked up as it conflicts with current implementation
episodeNumbers.binarySearch(episode).let { index -> return@argamap
current.getOrNull(index)?.let { currentEp -> }
current[index] = currentEp.apply {
updateCount++ // set all the new data, prioritise old correct data
val currentBack = this ids.forEach { (id, new) ->
this.description = this.description ?: node.description?.en new?.let {
this.name = this.name ?: node.titles?.canonical syncData[id] = syncData[id] ?: it
this.episode = }
this.episode ?: node.num ?: episodeNumbers[index] }
this.posterUrl =
this.posterUrl ?: node.thumbnail?.original?.url // set posters, might fuck up due to headers idk
posterUrl = posterUrl ?: res?.image
backgroundPosterUrl = backgroundPosterUrl ?: res?.cover
},
{
if (meta == null) return@argamap
addTrailer(meta.trailers)
}, {
if (this !is AnimeLoadResponse) return@argamap
val map =
Kitsu.getEpisodesDetails(
getMalId(),
getAniListId(),
isResponseRequired = false
)
if (map.isNullOrEmpty()) return@argamap
updateEpisodes = DubStatus.values().map { dubStatus ->
val current =
this.episodes[dubStatus]?.mapIndexed { index, episode ->
episode.apply {
this.episode = this.episode ?: (index + 1)
}
}?.sortedBy { it.episode ?: 0 }?.toMutableList()
if (current.isNullOrEmpty()) return@map false
val episodeNumbers = current.map { ep -> ep.episode!! }
var updateCount = 0
map.forEach { (episode, node) ->
episodeNumbers.binarySearch(episode).let { index ->
current.getOrNull(index)?.let { currentEp ->
current[index] = currentEp.apply {
updateCount++
this.description = this.description ?: node.description?.en
this.name = this.name ?: node.titles?.canonical
this.episode =
this.episode ?: node.num ?: episodeNumbers[index]
this.posterUrl =
this.posterUrl ?: node.thumbnail?.original?.url
}
} }
} }
} }
} this.episodes[dubStatus] = current
this.episodes[dubStatus] = current updateCount > 0
updateCount > 0 }.any { it }
}.any { it } })
})
} }
return out to updateEpisodes return out to updateEpisodes
} }
@ -1627,6 +1717,16 @@ class ResultViewModel2 : ViewModel() {
postResume() postResume()
} }
private fun postSubscription(loadResponse: LoadResponse) {
if (loadResponse.isEpisodeBased()) {
val id = loadResponse.getId()
val data = DataStoreHelper.getSubscribedData(id)
DataStoreHelper.updateSubscribedData(id, data, loadResponse as? EpisodeResponse)
val isSubscribed = data != null
_subscribeStatus.postValue(isSubscribed)
}
}
private fun postEpisodeRange(indexer: EpisodeIndexer?, range: EpisodeRange?) { private fun postEpisodeRange(indexer: EpisodeIndexer?, range: EpisodeRange?) {
if (range == null || indexer == null) { if (range == null || indexer == null) {
return return
@ -1763,6 +1863,7 @@ class ResultViewModel2 : ViewModel() {
) { ) {
currentResponse = loadResponse currentResponse = loadResponse
postPage(loadResponse, apiRepository) postPage(loadResponse, apiRepository)
postSubscription(loadResponse)
if (updateEpisodes) if (updateEpisodes)
postEpisodes(loadResponse, updateFillers) postEpisodes(loadResponse, updateFillers)
} }
@ -2124,7 +2225,7 @@ class ResultViewModel2 : ViewModel() {
autostart: AutoResume?, autostart: AutoResume?,
loadTrailers: Boolean = true, loadTrailers: Boolean = true,
) = ) =
viewModelScope.launchSafe { ioSafe {
_page.postValue(Resource.Loading(url)) _page.postValue(Resource.Loading(url))
_episodes.postValue(ResourceSome.Loading()) _episodes.postValue(ResourceSome.Loading())
@ -2142,7 +2243,7 @@ class ResultViewModel2 : ViewModel() {
"This provider does not exist" "This provider does not exist"
) )
) )
return@launchSafe return@ioSafe
} }
@ -2153,21 +2254,15 @@ class ResultViewModel2 : ViewModel() {
api api
) )
} }
// TODO: fix
// val validUrlResource = safeApiCall {
// SyncRedirector.redirect(
// url,
// api.mainUrl.replace(NineAnimeProvider().mainUrl, "9anime")
// .replace(GogoanimeProvider().mainUrl, "gogoanime")
// )
// }
if (validUrlResource !is Resource.Success) { if (validUrlResource !is Resource.Success) {
if (validUrlResource is Resource.Failure) { if (validUrlResource is Resource.Failure) {
_page.postValue(validUrlResource) _page.postValue(validUrlResource)
} }
return@launchSafe return@ioSafe
} }
val validUrl = validUrlResource.value val validUrl = validUrlResource.value
val repo = APIRepository(api) val repo = APIRepository(api)
currentRepo = repo currentRepo = repo
@ -2177,11 +2272,11 @@ class ResultViewModel2 : ViewModel() {
_page.postValue(data) _page.postValue(data)
} }
is Resource.Success -> { is Resource.Success -> {
if (!isActive) return@launchSafe if (!isActive) return@ioSafe
val loadResponse = ioWork { val loadResponse = ioWork {
applyMeta(data.value, currentMeta, currentSync).first applyMeta(data.value, currentMeta, currentSync).first
} }
if (!isActive) return@launchSafe if (!isActive) return@ioSafe
val mainId = loadResponse.getId() val mainId = loadResponse.getId()
preferDubStatus = getDub(mainId) ?: preferDubStatus preferDubStatus = getDub(mainId) ?: preferDubStatus
@ -2209,7 +2304,7 @@ class ResultViewModel2 : ViewModel() {
updateFillers = showFillers, updateFillers = showFillers,
apiRepository = repo apiRepository = repo
) )
if (!isActive) return@launchSafe if (!isActive) return@ioSafe
handleAutoStart(activity, autostart) handleAutoStart(activity, autostart)
} }
is Resource.Loading -> { is Resource.Loading -> {

View file

@ -157,6 +157,28 @@ class SettingsAccount : PreferenceFragmentCompat() {
) )
dialog.dismissSafe() dialog.dismissSafe()
} }
val displayedItems = listOf(
dialog.login_username_input,
dialog.login_email_input,
dialog.login_server_input,
dialog.login_password_input
).filter { it.isVisible }
displayedItems.foldRight(displayedItems.firstOrNull()) { item, previous ->
item?.id?.let { previous?.nextFocusDownId = it }
previous?.id?.let { item?.nextFocusUpId = it }
item
}
displayedItems.firstOrNull()?.let {
dialog.create_account?.nextFocusDownId = it.id
it.nextFocusUpId = dialog.create_account.id
}
dialog.apply_btt?.id?.let {
displayedItems.lastOrNull()?.nextFocusDownId = it
}
dialog.text1?.text = api.name dialog.text1?.text = api.name
if (api.storesPasswordInPlainText) { if (api.storesPasswordInPlainText) {

View file

@ -57,47 +57,49 @@ fun getCurrentLocale(context: Context): String {
val appLanguages = arrayListOf( val appLanguages = arrayListOf(
/* begin language list */ /* begin language list */
Triple("", "العربية", "ar"), Triple("", "العربية", "ar"),
Triple("", "български език", "bg"), Triple("", "български", "bg"),
Triple("", "বাংলা", "bn"), Triple("", "বাংলা", "bn"),
Triple("\uD83C\uDDE7\uD83C\uDDF7", "Brazilian Portuguese", "bp"), Triple("\uD83C\uDDE7\uD83C\uDDF7", "português brasileiro", "bp"),
Triple("", "čeština", "cs"), Triple("", "čeština", "cs"),
Triple("", "Deutsch", "de"), Triple("", "Deutsch", "de"),
Triple("", "ελληνικά", "el"), Triple("", "Ελληνικά", "el"),
Triple("", "English", "en"), Triple("", "English", "en"),
Triple("", "Esperanto", "eo"), Triple("", "Esperanto", "eo"),
Triple("", "Español", "es"), Triple("", "español", "es"),
Triple("", "فارسی", "fa"), Triple("", "فارسی", "fa"),
Triple("", "français", "fr"), Triple("", "français", "fr"),
Triple("", "हिन्दी", "hi"), Triple("", "हिन्दी", "hi"),
Triple("", "hrvatski jezik", "hr"), Triple("", "hrvatski", "hr"),
Triple("", "magyar", "hu"), Triple("", "magyar", "hu"),
Triple("\uD83C\uDDEE\uD83C\uDDE9", "Indonesian", "in"), Triple("\uD83C\uDDEE\uD83C\uDDE9", "Bahasa Indonesia", "in"),
Triple("", "Italiano", "it"), Triple("", "italiano", "it"),
Triple("\uD83C\uDDEE\uD83C\uDDF1", "עִברִית", "iw"), Triple("\uD83C\uDDEE\uD83C\uDDF1", "עברית", "iw"),
Triple("", "日本語 (にほんご)", "ja"),
Triple("", "ಕನ್ನಡ", "kn"), Triple("", "ಕನ್ನಡ", "kn"),
Triple("", "македонски јазик", "mk"), Triple("", "македонски", "mk"),
Triple("", "മലയാളം", "ml"), Triple("", "മലയാളം", "ml"),
Triple("", "bahasa Melayu", "ms"),
Triple("", "Nederlands", "nl"), Triple("", "Nederlands", "nl"),
Triple("", "Norsk nynorsk", "nn"), Triple("", "norsk nynorsk", "nn"),
Triple("", "Norsk", "no"), Triple("", "norsk bokmål", "no"),
Triple("", "język polski", "pl"), Triple("", "polski", "pl"),
Triple("\uD83C\uDDF5\uD83C\uDDF9", "Português", "pt"), Triple("\uD83C\uDDF5\uD83C\uDDF9", "português", "pt"),
Triple("🦍", "mmmm... monke", "qt"), Triple("\uD83E\uDD8D", "mmmm... monke", "qt"),
Triple("", "Română", "ro"), Triple("", "română", "ro"),
Triple("", "Русский", "ru"), Triple("", "русский", "ru"),
Triple("", "slovenčina", "sk"), Triple("", "slovenčina", "sk"),
Triple("", "Soomaaliga", "so"), Triple("", "Soomaaliga", "so"),
Triple("", "svenska", "sv"), Triple("", "svenska", "sv"),
Triple("", "தமிழ்", "ta"), Triple("", "தமிழ்", "ta"),
Triple("", "Wikang Tagalog", "tl"), Triple("", "Tagalog", "tl"),
Triple("", "Türkçe", "tr"), Triple("", "Türkçe", "tr"),
Triple("", "Українська", "uk"), Triple("", "українська", "uk"),
Triple("", "اردو", "ur"), Triple("", "اردو", "ur"),
Triple("", "Tiếng Việt", "vi"), Triple("", "Tiếng Việt", "vi"),
Triple("", "中文 (Zhōngwén)", "zh"), Triple("", "中文", "zh"),
Triple("\uD83C\uDDF9\uD83C\uDDFC", "Chinese Traditional", "zh-rTW"), Triple("\uD83C\uDDF9\uD83C\uDDFC", "文言", "zh-rTW"),
/* end language list */ /* end language list */
).sortedBy { it.second } //ye, we go alphabetical, so ppl don't put their lang on top ).sortedBy { it.second.lowercase() } //ye, we go alphabetical, so ppl don't put their lang on top
class SettingsGeneral : PreferenceFragmentCompat() { class SettingsGeneral : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -157,9 +159,6 @@ class SettingsGeneral : PreferenceFragmentCompat() {
getPref(R.string.locale_key)?.setOnPreferenceClickListener { pref -> getPref(R.string.locale_key)?.setOnPreferenceClickListener { pref ->
val tempLangs = appLanguages.toMutableList() val tempLangs = appLanguages.toMutableList()
//if (beneneCount > 100) {
// tempLangs.add(Triple("\uD83E\uDD8D", "mmmm... monke", "mo"))
//}
val current = getCurrentLocale(pref.context) val current = getCurrentLocale(pref.context)
val languageCodes = tempLangs.map { (_, _, iso) -> iso } val languageCodes = tempLangs.map { (_, _, iso) -> iso }
val languageNames = tempLangs.map { (emoji, name, iso) -> val languageNames = tempLangs.map { (emoji, name, iso) ->
@ -316,6 +315,12 @@ class SettingsGeneral : PreferenceFragmentCompat() {
} ?: emptyList() } ?: emptyList()
} }
settingsManager.edit().putBoolean(getString(R.string.jsdelivr_proxy_key), getKey(getString(R.string.jsdelivr_proxy_key), false) ?: false).apply()
getPref(R.string.jsdelivr_proxy_key)?.setOnPreferenceChangeListener { _, newValue ->
setKey(getString(R.string.jsdelivr_proxy_key), newValue)
return@setOnPreferenceChangeListener true
}
getPref(R.string.download_path_key)?.setOnPreferenceClickListener { getPref(R.string.download_path_key)?.setOnPreferenceClickListener {
val dirs = getDownloadDirs() val dirs = getDownloadDirs()

View file

@ -113,6 +113,30 @@ class SettingsPlayer : PreferenceFragmentCompat() {
return@setOnPreferenceClickListener true return@setOnPreferenceClickListener true
} }
getPref(R.string.quality_pref_mobile_data_key)?.setOnPreferenceClickListener {
val prefValues = Qualities.values().map { it.value }.reversed().toMutableList()
prefValues.remove(Qualities.Unknown.value)
val prefNames = prefValues.map { Qualities.getStringByInt(it) }
val currentQuality =
settingsManager.getInt(
getString(R.string.quality_pref_mobile_data_key),
Qualities.values().last().value
)
activity?.showBottomDialog(
prefNames.toList(),
prefValues.indexOf(currentQuality),
getString(R.string.watch_quality_pref_data),
true,
{}) {
settingsManager.edit().putInt(getString(R.string.quality_pref_mobile_data_key), prefValues[it])
.apply()
}
return@setOnPreferenceClickListener true
}
getPref(R.string.player_pref_key)?.setOnPreferenceClickListener { getPref(R.string.player_pref_key)?.setOnPreferenceClickListener {
val prefNames = resources.getStringArray(R.array.player_pref_names) val prefNames = resources.getStringArray(R.array.player_pref_names)
val prefValues = resources.getIntArray(R.array.player_pref_values) val prefValues = resources.getIntArray(R.array.player_pref_values)

View file

@ -2,6 +2,8 @@ package com.lagradost.cloudstream3.ui.settings
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.navigation.NavOptions
import androidx.navigation.fragment.findNavController
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
@ -16,6 +18,7 @@ import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.UIHelper.navigate
class SettingsProviders : PreferenceFragmentCompat() { class SettingsProviders : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -56,6 +59,20 @@ class SettingsProviders : PreferenceFragmentCompat() {
return@setOnPreferenceClickListener true return@setOnPreferenceClickListener true
} }
getPref(R.string.test_providers_key)?.setOnPreferenceClickListener {
// Somehow animations do not work without this.
val options = NavOptions.Builder()
.setEnterAnim(R.anim.enter_anim)
.setExitAnim(R.anim.exit_anim)
.setPopEnterAnim(R.anim.pop_enter)
.setPopExitAnim(R.anim.pop_exit)
.build()
this@SettingsProviders.findNavController()
.navigate(R.id.navigation_test_providers, null, options)
true
}
getPref(R.string.prefer_media_type_key)?.setOnPreferenceClickListener { getPref(R.string.prefer_media_type_key)?.setOnPreferenceClickListener {
val names = enumValues<TvType>().sorted().map { it.name } val names = enumValues<TvType>().sorted().map { it.name }
val default = val default =

View file

@ -0,0 +1,97 @@
package com.lagradost.cloudstream3.ui.settings.testing
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.fragment.app.activityViewModels
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import kotlinx.android.synthetic.main.fragment_testing.*
import kotlinx.android.synthetic.main.view_test.*
class TestFragment : Fragment() {
private val testViewModel: TestViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setUpToolbar(R.string.category_provider_test)
super.onViewCreated(view, savedInstanceState)
provider_test_recycler_view?.adapter = TestResultAdapter(
mutableListOf()
)
testViewModel.init()
if (testViewModel.isRunningTest) {
provider_test?.setState(TestView.TestState.Running)
}
observe(testViewModel.providerProgress) { (passed, failed, total) ->
provider_test?.setProgress(passed, failed, total)
}
observeNullable(testViewModel.providerResults) {
normalSafeApiCall {
val newItems = it.sortedBy { api -> api.first.name }
(provider_test_recycler_view?.adapter as? TestResultAdapter)?.updateList(
newItems
)
}
}
provider_test?.setOnPlayButtonListener { state ->
when (state) {
TestView.TestState.Stopped -> testViewModel.stopTest()
TestView.TestState.Running -> testViewModel.startTest()
TestView.TestState.None -> testViewModel.startTest()
}
}
if (isTrueTvSettings()) {
tests_play_pause?.isFocusableInTouchMode = true
tests_play_pause?.requestFocus()
}
provider_test?.playPauseButton?.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
provider_test_appbar?.setExpanded(true, true)
}
}
fun focusRecyclerView() {
// Hack to make it possible to focus the recyclerview.
if (isTrueTvSettings()) {
provider_test_recycler_view?.requestFocus()
provider_test_appbar?.setExpanded(false, true)
}
}
provider_test?.setOnMainClick {
testViewModel.setFilterMethod(TestViewModel.ProviderFilter.All)
focusRecyclerView()
}
provider_test?.setOnFailedClick {
testViewModel.setFilterMethod(TestViewModel.ProviderFilter.Failed)
focusRecyclerView()
}
provider_test?.setOnPassedClick {
testViewModel.setFilterMethod(TestViewModel.ProviderFilter.Passed)
focusRecyclerView()
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_testing, container, false)
}
}

View file

@ -0,0 +1,80 @@
package com.lagradost.cloudstream3.ui.settings.testing
import android.app.AlertDialog
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.getAllMessages
import com.lagradost.cloudstream3.mvvm.getStackTracePretty
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
import com.lagradost.cloudstream3.utils.TestingUtils
import kotlinx.android.synthetic.main.provider_test_item.view.*
class TestResultAdapter(override val items: MutableList<Pair<MainAPI, TestingUtils.TestResultProvider>>) :
AppUtils.DiffAdapter<Pair<MainAPI, TestingUtils.TestResultProvider>>(items) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return ProviderTestViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.provider_test_item, parent, false),
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is ProviderTestViewHolder -> {
val item = items[position]
holder.bind(item.first, item.second)
}
}
}
inner class ProviderTestViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val languageText: TextView = itemView.lang_icon
private val providerTitle: TextView = itemView.main_text
private val statusText: TextView = itemView.passed_failed_marker
private val failDescription: TextView = itemView.fail_description
private val logButton: ImageView = itemView.action_button
private fun String.lastLine(): String? {
return this.lines().lastOrNull { it.isNotBlank() }
}
fun bind(api: MainAPI, result: TestingUtils.TestResultProvider) {
languageText.text = getFlagFromIso(api.lang)
providerTitle.text = api.name
val (resultText, resultColor) = if (result.success) {
R.string.test_passed to R.color.colorTestPass
} else {
R.string.test_failed to R.color.colorTestFail
}
statusText.setText(resultText)
statusText.setTextColor(ContextCompat.getColor(itemView.context, resultColor))
val stackTrace = result.exception?.getStackTracePretty(false)?.ifBlank { null }
val messages = result.exception?.getAllMessages()?.ifBlank { null }
val fullLog =
result.log + (messages?.let { "\n\n$it" } ?: "") + (stackTrace?.let { "\n\n$it" } ?: "")
failDescription.text = messages?.lastLine() ?: result.log.lastLine()
logButton.setOnClickListener {
val builder: AlertDialog.Builder =
AlertDialog.Builder(it.context, R.style.AlertDialogCustom)
builder.setMessage(fullLog)
.setTitle(R.string.test_log)
.show()
}
}
}
}

View file

@ -0,0 +1,119 @@
package com.lagradost.cloudstream3.ui.settings.testing
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.widget.ContentLoadingProgressBar
import com.google.android.material.button.MaterialButton
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.utils.AppUtils.animateProgressTo
class TestView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : CardView(context, attrs) {
enum class TestState(@StringRes val stringRes: Int, @DrawableRes val icon: Int) {
None(R.string.start, R.drawable.ic_baseline_play_arrow_24),
// Paused(R.string.resume, R.drawable.ic_baseline_play_arrow_24),
Stopped(R.string.restart, R.drawable.ic_baseline_play_arrow_24),
Running(R.string.stop, R.drawable.pause_to_play),
}
var mainSection: View? = null
var testsPassedSection: View? = null
var testsFailedSection: View? = null
var mainSectionText: TextView? = null
var mainSectionHeader: TextView? = null
var testsPassedSectionText: TextView? = null
var testsFailedSectionText: TextView? = null
var totalProgressBar: ContentLoadingProgressBar? = null
var playPauseButton: MaterialButton? = null
var stateListener: (TestState) -> Unit = {}
private var state = TestState.None
init {
LayoutInflater.from(context).inflate(R.layout.view_test, this, true)
mainSection = findViewById(R.id.main_test_section)
testsPassedSection = findViewById(R.id.passed_test_section)
testsFailedSection = findViewById(R.id.failed_test_section)
mainSectionHeader = findViewById(R.id.main_test_header)
mainSectionText = findViewById(R.id.main_test_section_progress)
testsPassedSectionText = findViewById(R.id.passed_test_section_progress)
testsFailedSectionText = findViewById(R.id.failed_test_section_progress)
totalProgressBar = findViewById(R.id.test_total_progress)
playPauseButton = findViewById(R.id.tests_play_pause)
attrs?.let {
val typedArray = context.obtainStyledAttributes(it, R.styleable.TestView)
val headerText = typedArray.getString(R.styleable.TestView_header_text)
mainSectionHeader?.text = headerText
typedArray.recycle()
}
playPauseButton?.setOnClickListener {
val newState = when (state) {
TestState.None -> TestState.Running
TestState.Running -> TestState.Stopped
TestState.Stopped -> TestState.Running
}
setState(newState)
}
}
fun setOnPlayButtonListener(listener: (TestState) -> Unit) {
stateListener = listener
}
fun setState(newState: TestState) {
state = newState
stateListener.invoke(newState)
playPauseButton?.setText(newState.stringRes)
playPauseButton?.icon = ContextCompat.getDrawable(context, newState.icon)
}
fun setProgress(passed: Int, failed: Int, total: Int?) {
val totalProgress = passed + failed
mainSectionText?.text = "$totalProgress / ${total?.toString() ?: "?"}"
testsPassedSectionText?.text = passed.toString()
testsFailedSectionText?.text = failed.toString()
totalProgressBar?.max = (total ?: 0) * 1000
totalProgressBar?.animateProgressTo(totalProgress * 1000)
totalProgressBar?.isVisible = !(totalProgress == 0 || (total ?: 0) == 0)
if (totalProgress == total) {
setState(TestState.Stopped)
}
}
fun setMainHeader(@StringRes header: Int) {
mainSectionHeader?.setText(header)
}
fun setOnMainClick(listener: OnClickListener) {
mainSection?.setOnClickListener(listener)
}
fun setOnPassedClick(listener: OnClickListener) {
testsPassedSection?.setOnClickListener(listener)
}
fun setOnFailedClick(listener: OnClickListener) {
testsFailedSection?.setOnClickListener(listener)
}
}

View file

@ -0,0 +1,108 @@
package com.lagradost.cloudstream3.ui.settings.testing
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
import com.lagradost.cloudstream3.utils.TestingUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
class TestViewModel : ViewModel() {
data class TestProgress(
val passed: Int,
val failed: Int,
val total: Int
)
enum class ProviderFilter {
All,
Passed,
Failed
}
private val _providerProgress = MutableLiveData<TestProgress>(null)
val providerProgress: LiveData<TestProgress> = _providerProgress
private val _providerResults =
MutableLiveData<List<Pair<MainAPI, TestingUtils.TestResultProvider>>>(
emptyList()
)
val providerResults: LiveData<List<Pair<MainAPI, TestingUtils.TestResultProvider>>> =
_providerResults
private var scope: CoroutineScope? = null
val isRunningTest
get() = scope != null
private var filter = ProviderFilter.All
private val providers = threadSafeListOf<Pair<MainAPI, TestingUtils.TestResultProvider>>()
private var passed = 0
private var failed = 0
private var total = 0
private fun updateProgress() {
_providerProgress.postValue(TestProgress(passed, failed, total))
postProviders()
}
private fun postProviders() {
synchronized(providers) {
val filtered = when (filter) {
ProviderFilter.All -> providers
ProviderFilter.Passed -> providers.filter { it.second.success }
ProviderFilter.Failed -> providers.filter { !it.second.success }
}
_providerResults.postValue(filtered)
}
}
fun setFilterMethod(filter: ProviderFilter) {
if (this.filter == filter) return
this.filter = filter
postProviders()
}
private fun addProvider(api: MainAPI, results: TestingUtils.TestResultProvider) {
synchronized(providers) {
val index = providers.indexOfFirst { it.first == api }
if (index == -1) {
providers.add(api to results)
if (results.success) passed++ else failed++
} else {
providers[index] = api to results
}
updateProgress()
}
}
fun init() {
val apis = APIHolder.allProviders
total = apis.size
updateProgress()
}
fun startTest() {
scope = CoroutineScope(Dispatchers.Default)
val apis = APIHolder.allProviders
total = apis.size
failed = 0
passed = 0
providers.clear()
updateProgress()
TestingUtils.getDeferredProviderTests(scope ?: return, apis, ::println) { api, result ->
addProvider(api, result)
}
}
fun stopTest() {
scope?.cancel()
scope = null
}
}

View file

@ -1,8 +1,11 @@
package com.lagradost.cloudstream3.utils package com.lagradost.cloudstream3.utils
import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.Activity.RESULT_CANCELED import android.app.Activity.RESULT_CANCELED
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.* import android.content.*
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.database.Cursor import android.database.Cursor
@ -17,6 +20,7 @@ import android.os.*
import android.provider.MediaStore import android.provider.MediaStore
import android.text.Spanned import android.text.Spanned
import android.util.Log import android.util.Log
import android.view.animation.DecelerateInterpolator
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
@ -25,6 +29,7 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.core.text.toSpanned import androidx.core.text.toSpanned
import androidx.core.widget.ContentLoadingProgressBar
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -179,6 +184,36 @@ object AppUtils {
touchSlopField.set(recyclerView, touchSlop * f) // "8" was obtained experimentally touchSlopField.set(recyclerView, touchSlop * f) // "8" was obtained experimentally
} }
fun ContentLoadingProgressBar?.animateProgressTo(to: Int) {
if (this == null) return
val animation: ObjectAnimator = ObjectAnimator.ofInt(
this,
"progress",
this.progress,
to
)
animation.duration = 500
animation.setAutoCancel(true)
animation.interpolator = DecelerateInterpolator()
animation.start()
}
fun Context.createNotificationChannel(channelId: String, channelName: String, description: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel =
NotificationChannel(channelId, channelName, importance).apply {
this.description = description
}
// Register the channel with the system.
val notificationManager: NotificationManager =
this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
fun getAllWatchNextPrograms(context: Context): Set<Long> { fun getAllWatchNextPrograms(context: Context): Set<Long> {
val COLUMN_WATCH_NEXT_ID_INDEX = 0 val COLUMN_WATCH_NEXT_ID_INDEX = 0
@ -456,6 +491,12 @@ object AppUtils {
} }
} }
fun Context.isNetworkAvailable(): Boolean {
val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetworkInfo = manager.activeNetworkInfo
return activeNetworkInfo != null && activeNetworkInfo.isConnected || manager.allNetworkInfo?.any { it.isConnected } ?: false
}
fun splitQuery(url: URL): Map<String, String> { fun splitQuery(url: URL): Map<String, String> {
val queryPairs: MutableMap<String, String> = LinkedHashMap() val queryPairs: MutableMap<String, String> = LinkedHashMap()
val query: String = url.query val query: String = url.query
@ -735,8 +776,13 @@ object AppUtils {
return networkInfo.any { return networkInfo.any {
conManager.getNetworkCapabilities(it) conManager.getNetworkCapabilities(it)
?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true ?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true
} &&
!networkInfo.any {
conManager.getNetworkCapabilities(it)
?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true
}
} }
}
private fun Activity?.cacheClass(clazz: String?) { private fun Activity?.cacheClass(clazz: String?) {
clazz?.let { c -> clazz?.let { c ->
@ -780,4 +826,4 @@ object AppUtils {
} }
return currentAudioFocusRequest return currentAudioFocusRequest
} }
} }

View file

@ -1,16 +1,13 @@
package com.lagradost.cloudstream3.utils package com.lagradost.cloudstream3.utils
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.APIHolder.capitalize import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.SearchQuality
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
@ -20,6 +17,7 @@ const val VIDEO_POS_DUR = "video_pos_dur"
const val VIDEO_WATCH_STATE = "video_watch_state" const val VIDEO_WATCH_STATE = "video_watch_state"
const val RESULT_WATCH_STATE = "result_watch_state" const val RESULT_WATCH_STATE = "result_watch_state"
const val RESULT_WATCH_STATE_DATA = "result_watch_state_data" const val RESULT_WATCH_STATE_DATA = "result_watch_state_data"
const val RESULT_SUBSCRIBED_STATE_DATA = "result_subscribed_state_data"
const val RESULT_RESUME_WATCHING = "result_resume_watching_2" // changed due to id changes const val RESULT_RESUME_WATCHING = "result_resume_watching_2" // changed due to id changes
const val RESULT_RESUME_WATCHING_OLD = "result_resume_watching" const val RESULT_RESUME_WATCHING_OLD = "result_resume_watching"
const val RESULT_RESUME_WATCHING_HAS_MIGRATED = "result_resume_watching_migrated" const val RESULT_RESUME_WATCHING_HAS_MIGRATED = "result_resume_watching_migrated"
@ -42,6 +40,37 @@ object DataStoreHelper {
return this return this
} }
/**
* Used to display notifications on new episodes and posters in library.
**/
data class SubscribedData(
@JsonProperty("id") override var id: Int?,
@JsonProperty("subscribedTime") val bookmarkedTime: Long,
@JsonProperty("latestUpdatedTime") val latestUpdatedTime: Long,
@JsonProperty("lastSeenEpisodeCount") val lastSeenEpisodeCount: Map<DubStatus, Int?>,
@JsonProperty("name") override val name: String,
@JsonProperty("url") override val url: String,
@JsonProperty("apiName") override val apiName: String,
@JsonProperty("type") override var type: TvType? = null,
@JsonProperty("posterUrl") override var posterUrl: String?,
@JsonProperty("year") val year: Int?,
@JsonProperty("quality") override var quality: SearchQuality? = null,
@JsonProperty("posterHeaders") override var posterHeaders: Map<String, String>? = null,
) : SearchResponse {
fun toLibraryItem(): SyncAPI.LibraryItem? {
return SyncAPI.LibraryItem(
name,
url,
id?.toString() ?: return null,
null,
null,
null,
latestUpdatedTime,
apiName, type, posterUrl, posterHeaders, quality, this.id
)
}
}
data class BookmarkedData( data class BookmarkedData(
@JsonProperty("id") override var id: Int?, @JsonProperty("id") override var id: Int?,
@JsonProperty("bookmarkedTime") val bookmarkedTime: Long, @JsonProperty("bookmarkedTime") val bookmarkedTime: Long,
@ -63,7 +92,7 @@ object DataStoreHelper {
null, null,
null, null,
null, null,
null, latestUpdatedTime,
apiName, type, posterUrl, posterHeaders, quality, this.id apiName, type, posterUrl, posterHeaders, quality, this.id
) )
} }
@ -75,9 +104,7 @@ object DataStoreHelper {
@JsonProperty("apiName") override val apiName: String, @JsonProperty("apiName") override val apiName: String,
@JsonProperty("type") override var type: TvType? = null, @JsonProperty("type") override var type: TvType? = null,
@JsonProperty("posterUrl") override var posterUrl: String?, @JsonProperty("posterUrl") override var posterUrl: String?,
@JsonProperty("watchPos") val watchPos: PosDur?, @JsonProperty("watchPos") val watchPos: PosDur?,
@JsonProperty("id") override var id: Int?, @JsonProperty("id") override var id: Int?,
@JsonProperty("parentId") val parentId: Int?, @JsonProperty("parentId") val parentId: Int?,
@JsonProperty("episode") val episode: Int?, @JsonProperty("episode") val episode: Int?,
@ -204,6 +231,41 @@ object DataStoreHelper {
return getKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString()) return getKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
} }
fun getAllSubscriptions(): List<SubscribedData> {
return getKeys("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA")?.mapNotNull {
getKey(it)
} ?: emptyList()
}
fun removeSubscribedData(id: Int?) {
if (id == null) return
AccountManager.localListApi.requireLibraryRefresh = true
removeKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString())
}
/**
* Set new seen episodes and update time
**/
fun updateSubscribedData(id: Int?, data: SubscribedData?, episodeResponse: EpisodeResponse?) {
if (id == null || data == null || episodeResponse == null) return
val newData = data.copy(
latestUpdatedTime = unixTimeMS,
lastSeenEpisodeCount = episodeResponse.getLatestEpisodes()
)
setKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString(), newData)
}
fun setSubscribedData(id: Int?, data: SubscribedData) {
if (id == null) return
setKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString(), data)
AccountManager.localListApi.requireLibraryRefresh = true
}
fun getSubscribedData(id: Int?): SubscribedData? {
if (id == null) return null
return getKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString())
}
fun setViewPos(id: Int?, pos: Long, dur: Long) { fun setViewPos(id: Int?, pos: Long, dur: Long) {
if (id == null) return if (id == null) return
if (dur < 30_000) return // too short if (dur < 30_000) return // too short

View file

@ -52,7 +52,7 @@ data class ExtractorLinkPlayList(
) )
open class ExtractorLink( open class ExtractorLink constructor(
open val source: String, open val source: String,
open val name: String, open val name: String,
override val url: String, override val url: String,
@ -62,7 +62,24 @@ open class ExtractorLink(
override val headers: Map<String, String> = mapOf(), override val headers: Map<String, String> = mapOf(),
/** Used for getExtractorVerifierJob() */ /** Used for getExtractorVerifierJob() */
open val extractorData: String? = null, open val extractorData: String? = null,
open val isDash: Boolean = false,
) : VideoDownloadManager.IDownloadableMinimum { ) : VideoDownloadManager.IDownloadableMinimum {
/**
* Old constructor without isDash, allows for backwards compatibility with extensions.
* Should be removed after all extensions have updated their cloudstream.jar
**/
constructor(
source: String,
name: String,
url: String,
referer: String,
quality: Int,
isM3u8: Boolean = false,
headers: Map<String, String> = mapOf(),
/** Used for getExtractorVerifierJob() */
extractorData: String? = null
) : this(source, name, url, referer, quality, isM3u8, headers, extractorData, false)
override fun toString(): String { override fun toString(): String {
return "ExtractorLink(name=$name, url=$url, referer=$referer, isM3u8=$isM3u8)" return "ExtractorLink(name=$name, url=$url, referer=$referer, isM3u8=$isM3u8)"
} }
@ -229,6 +246,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
StreamSB8(), StreamSB8(),
StreamSB9(), StreamSB9(),
StreamSB10(), StreamSB10(),
StreamSB11(),
SBfull(), SBfull(),
// Streamhub(), cause Streamhub2() works // Streamhub(), cause Streamhub2() works
Streamhub2(), Streamhub2(),
@ -254,6 +272,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
// WatchSB(), 'cause StreamSB.kt works // WatchSB(), 'cause StreamSB.kt works
Uqload(), Uqload(),
Uqload1(), Uqload1(),
Uqload2(),
Evoload(), Evoload(),
Evoload1(), Evoload1(),
VoeExtractor(), VoeExtractor(),
@ -265,6 +284,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
OkRu(), OkRu(),
OkRuHttps(), OkRuHttps(),
Okrulink(), Okrulink(),
Sendvid(),
// dood extractors // dood extractors
DoodCxExtractor(), DoodCxExtractor(),
@ -276,6 +296,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
DoodShExtractor(), DoodShExtractor(),
DoodWatchExtractor(), DoodWatchExtractor(),
DoodWfExtractor(), DoodWfExtractor(),
DoodYtExtractor(),
AsianLoad(), AsianLoad(),
@ -323,6 +344,8 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
Filesim(), Filesim(),
FileMoon(), FileMoon(),
FileMoonSx(),
Vido(),
Linkbox(), Linkbox(),
Acefile(), Acefile(),
SpeedoStream(), SpeedoStream(),
@ -364,6 +387,7 @@ val extractorApis: MutableList<ExtractorApi> = arrayListOf(
Cda(), Cda(),
Dailymotion(), Dailymotion(),
ByteShare(), ByteShare(),
Ztreamhub()
) )

View file

@ -14,6 +14,7 @@ import androidx.core.app.NotificationCompat
import com.lagradost.cloudstream3.MainActivity import com.lagradost.cloudstream3.MainActivity
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.createNotificationChannel
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -47,24 +48,12 @@ class PackageInstallerService : Service() {
.setSmallIcon(R.drawable.rdload) .setSmallIcon(R.drawable.rdload)
} }
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel =
NotificationChannel(UPDATE_CHANNEL_ID, UPDATE_CHANNEL_NAME, importance).apply {
description = UPDATE_CHANNEL_DESCRIPTION
}
// Register the channel with the system
val notificationManager: NotificationManager =
this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
override fun onCreate() { override fun onCreate() {
createNotificationChannel() this.createNotificationChannel(
UPDATE_CHANNEL_ID,
UPDATE_CHANNEL_NAME,
UPDATE_CHANNEL_DESCRIPTION
)
startForeground(UPDATE_NOTIFICATION_ID, baseNotification.build()) startForeground(UPDATE_NOTIFICATION_ID, baseNotification.build())
} }

View file

@ -0,0 +1,267 @@
package com.lagradost.cloudstream3.utils
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.logError
import kotlinx.coroutines.*
import org.junit.Assert
object TestingUtils {
open class TestResult(val success: Boolean) {
companion object {
val Pass = TestResult(true)
val Fail = TestResult(false)
}
}
class TestResultSearch(val results: List<SearchResponse>) : TestResult(true)
class TestResultLoad(val extractorData: String) : TestResult(true)
class TestResultProvider(success: Boolean, val log: String, val exception: Throwable?) :
TestResult(success)
@Throws(AssertionError::class, CancellationException::class)
suspend fun testHomepage(
api: MainAPI,
logger: (String) -> Unit
): TestResult {
if (api.hasMainPage) {
try {
val f = api.mainPage.first()
val homepage =
api.getMainPage(1, MainPageRequest(f.name, f.data, f.horizontalImages))
when {
homepage == null -> {
logger.invoke("Homepage provider ${api.name} did not correctly load homepage!")
}
homepage.items.isEmpty() -> {
logger.invoke("Homepage provider ${api.name} does not contain any items!")
}
homepage.items.any { it.list.isEmpty() } -> {
logger.invoke("Homepage provider ${api.name} does not have any items on result!")
}
}
} catch (e: Throwable) {
if (e is NotImplementedError) {
Assert.fail("Provider marked as hasMainPage, while in reality is has not been implemented")
} else if (e is CancellationException) {
throw e
}
logError(e)
}
}
return TestResult.Pass
}
@Throws(AssertionError::class, CancellationException::class)
private suspend fun testSearch(
api: MainAPI
): TestResult {
val searchQueries = listOf("over", "iron", "guy")
val searchResults = searchQueries.firstNotNullOfOrNull { query ->
try {
api.search(query).takeIf { !it.isNullOrEmpty() }
} catch (e: Throwable) {
if (e is NotImplementedError) {
Assert.fail("Provider has not implemented search()")
} else if (e is CancellationException) {
throw e
}
logError(e)
null
}
}
return if (searchResults.isNullOrEmpty()) {
Assert.fail("Api ${api.name} did not return any valid search responses")
TestResult.Fail // Should not be reached
} else {
TestResultSearch(searchResults)
}
}
@Throws(AssertionError::class, CancellationException::class)
private suspend fun testLoad(
api: MainAPI,
result: SearchResponse,
logger: (String) -> Unit
): TestResult {
try {
Assert.assertEquals(
"Invalid apiName on SearchResponse on ${api.name}",
result.apiName,
api.name
)
val loadResponse = api.load(result.url)
if (loadResponse == null) {
logger.invoke("Returned null loadResponse on ${result.url} on ${api.name}")
return TestResult.Fail
}
Assert.assertEquals(
"Invalid apiName on LoadResponse on ${api.name}",
loadResponse.apiName,
result.apiName
)
Assert.assertTrue(
"Api ${api.name} on load does not contain any of the supportedTypes: ${loadResponse.type}",
api.supportedTypes.contains(loadResponse.type)
)
val url = when (loadResponse) {
is AnimeLoadResponse -> {
val gotNoEpisodes =
loadResponse.episodes.keys.isEmpty() || loadResponse.episodes.keys.any { loadResponse.episodes[it].isNullOrEmpty() }
if (gotNoEpisodes) {
logger.invoke("Api ${api.name} got no episodes on ${loadResponse.url}")
return TestResult.Fail
}
(loadResponse.episodes[loadResponse.episodes.keys.firstOrNull()])?.firstOrNull()?.data
}
is MovieLoadResponse -> {
val gotNoEpisodes = loadResponse.dataUrl.isBlank()
if (gotNoEpisodes) {
logger.invoke("Api ${api.name} got no movie on ${loadResponse.url}")
return TestResult.Fail
}
loadResponse.dataUrl
}
is TvSeriesLoadResponse -> {
val gotNoEpisodes = loadResponse.episodes.isEmpty()
if (gotNoEpisodes) {
logger.invoke("Api ${api.name} got no episodes on ${loadResponse.url}")
return TestResult.Fail
}
loadResponse.episodes.firstOrNull()?.data
}
is LiveStreamLoadResponse -> {
loadResponse.dataUrl
}
else -> {
logger.invoke("Unknown load response: ${loadResponse.javaClass.name}")
return TestResult.Fail
}
} ?: return TestResult.Fail
return TestResultLoad(url)
// val loadTest = testLoadResponse(api, load, logger)
// if (loadTest is TestResultLoad) {
// testLinkLoading(api, loadTest.extractorData, logger).success
// } else {
// false
// }
// if (!validResults) {
// logger("Api ${api.name} did not load on the first search results: ${smallSearchResults.map { it.name }}")
// }
// return TestResult(validResults)
} catch (e: Throwable) {
if (e is NotImplementedError) {
Assert.fail("Provider has not implemented load()")
}
throw e
}
}
@Throws(AssertionError::class, CancellationException::class)
private suspend fun testLinkLoading(
api: MainAPI,
url: String?,
logger: (String) -> Unit
): TestResult {
Assert.assertNotNull("Api ${api.name} has invalid url on episode", url)
if (url == null) return TestResult.Fail // Should never trigger
var linksLoaded = 0
try {
val success = api.loadLinks(url, false, {}) { link ->
logger.invoke("Video loaded: ${link.name}")
Assert.assertTrue(
"Api ${api.name} returns link with invalid url ${link.url}",
link.url.length > 4
)
linksLoaded++
}
if (success) {
logger.invoke("Links loaded: $linksLoaded")
return TestResult(linksLoaded > 0)
} else {
Assert.fail("Api ${api.name} returns false on loadLinks() with $linksLoaded links loaded")
}
} catch (e: Throwable) {
when (e) {
is NotImplementedError -> {
Assert.fail("Provider has not implemented loadLinks()")
}
else -> {
logger.invoke("Failed link loading on ${api.name} using data: $url")
throw e
}
}
}
return TestResult.Pass
}
fun getDeferredProviderTests(
scope: CoroutineScope,
providers: List<MainAPI>,
logger: (String) -> Unit,
callback: (MainAPI, TestResultProvider) -> Unit
) {
providers.forEach { api ->
scope.launch {
var log = ""
fun addToLog(string: String) {
log += string + "\n"
logger.invoke(string)
}
fun getLog(): String {
return log.removeSuffix("\n")
}
val result = try {
addToLog("Trying ${api.name}")
// Test Homepage
val homepage = testHomepage(api, logger).success
Assert.assertTrue("Homepage failed to load", homepage)
// Test Search Results
val searchResults = testSearch(api)
Assert.assertTrue("Failed to get search results", searchResults.success)
searchResults as TestResultSearch
// Test Load and LoadLinks
// Only try the first 3 search results to prevent spamming
val success = searchResults.results.take(3).any { searchResponse ->
addToLog("Testing search result: ${searchResponse.url}")
val loadResponse = testLoad(api, searchResponse, ::addToLog)
if (loadResponse !is TestResultLoad) {
false
} else {
testLinkLoading(api, loadResponse.extractorData, ::addToLog).success
}
}
if (success) {
logger.invoke("Success ${api.name}")
TestResultProvider(true, getLog(), null)
} else {
logger.invoke("Error ${api.name}")
TestResultProvider(false, getLog(), null)
}
} catch (e: Throwable) {
TestResultProvider(false, getLog(), e)
}
callback.invoke(api, result)
}
}
}
}

View file

@ -20,6 +20,7 @@ import androidx.work.Data
import androidx.work.ExistingWorkPolicy import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
import com.bumptech.glide.load.model.GlideUrl
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
@ -213,7 +214,7 @@ object VideoDownloadManager {
} }
private val cachedBitmaps = hashMapOf<String, Bitmap>() private val cachedBitmaps = hashMapOf<String, Bitmap>()
private fun Context.getImageBitmapFromUrl(url: String): Bitmap? { fun Context.getImageBitmapFromUrl(url: String, headers: Map<String, String>? = null): Bitmap? {
try { try {
if (cachedBitmaps.containsKey(url)) { if (cachedBitmaps.containsKey(url)) {
return cachedBitmaps[url] return cachedBitmaps[url]
@ -221,12 +222,14 @@ object VideoDownloadManager {
val bitmap = GlideApp.with(this) val bitmap = GlideApp.with(this)
.asBitmap() .asBitmap()
.load(url).into(720, 720) .load(GlideUrl(url) { headers ?: emptyMap() })
.into(720, 720)
.get() .get()
if (bitmap != null) { if (bitmap != null) {
cachedBitmaps[url] = bitmap cachedBitmaps[url] = bitmap
} }
return null return bitmap
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)
return null return null
@ -426,7 +429,7 @@ object VideoDownloadManager {
} }
private const val reservedChars = "|\\?*<\":>+[]/\'" private const val reservedChars = "|\\?*<\":>+[]/\'"
fun sanitizeFilename(name: String, removeSpaces: Boolean= false): String { fun sanitizeFilename(name: String, removeSpaces: Boolean = false): String {
var tempName = name var tempName = name
for (c in reservedChars) { for (c in reservedChars) {
tempName = tempName.replace(c, ' ') tempName = tempName.replace(c, ' ')
@ -1612,7 +1615,7 @@ object VideoDownloadManager {
.mapIndexed { index, any -> DownloadQueueResumePackage(index, any) } .mapIndexed { index, any -> DownloadQueueResumePackage(index, any) }
.toTypedArray() .toTypedArray()
setKey(KEY_RESUME_QUEUE_PACKAGES, dQueue) setKey(KEY_RESUME_QUEUE_PACKAGES, dQueue)
} catch (t : Throwable) { } catch (t: Throwable) {
logError(t) logError(t)
} }
} }

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="?attr/white"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,14.67L3.41,6.09L2,7.5l8.5,8.5H4v2h16v-2h-6.5l5.15,-5.15C18.91,10.95 19.2,11 19.5,11c1.38,0 2.5,-1.12 2.5,-2.5S20.88,6 19.5,6S17,7.12 17,8.5c0,0.35 0.07,0.67 0.2,0.97L12,14.67z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="?attr/white"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM18,16v-5c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.64,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2zM16,17L8,17v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5v6z"/>
</vector>

View file

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="?attr/white" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?attr/white" android:pathData="M20.41,8.41l-4.83,-4.83C15.21,3.21 14.7,3 14.17,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V9.83C21,9.3 20.79,8.79 20.41,8.41zM7,7h7v2H7V7zM17,17H7v-2h10V17zM17,13H7v-2h10V13z"/>
</vector>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="108dp"
android:height="108dp"
android:viewportWidth="50"
android:viewportHeight="50"
android:name="vector">
<group android:scaleX="0.1755477"
android:scaleY="0.1755477"
android:translateX="0"
android:translateY="0">
<path android:name="path"
android:pathData="M 245.05 148.63 C 242.249 148.627 239.463 149.052 236.79 149.89 C 235.151 141.364 230.698 133.63 224.147 127.931 C 217.597 122.233 209.321 118.893 200.65 118.45 C 195.913 105.431 186.788 94.458 174.851 87.427 C 162.914 80.396 148.893 77.735 135.21 79.905 C 121.527 82.074 109.017 88.941 99.84 99.32 C 89.871 95.945 79.051 96.024 69.133 99.545 C 59.215 103.065 50.765 109.826 45.155 118.73 C 39.545 127.634 37.094 138.174 38.2 148.64 L 37.94 148.64 C 30.615 148.64 23.582 151.553 18.403 156.733 C 13.223 161.912 10.31 168.945 10.31 176.27 C 10.31 183.595 13.223 190.628 18.403 195.807 C 23.582 200.987 30.615 203.9 37.94 203.9 L 245.05 203.9 C 252.375 203.9 259.408 200.987 264.587 195.807 C 269.767 190.628 272.68 183.595 272.68 176.27 C 272.68 168.945 269.767 161.912 264.587 156.733 C 259.408 151.553 252.375 148.64 245.05 148.64 Z"
android:fillColor="#FFFFFF" android:strokeWidth="1"
tools:ignore="VectorPath"
android:fillAlpha="0.55"/>
<path android:name="path_1" android:pathData="M 208.61 125 C 208.61 123.22 208.55 121.45 208.48 119.69 C 205.919 119.01 203.296 118.595 200.65 118.45 C 195.913 105.431 186.788 94.458 174.851 87.427 C 162.914 80.396 148.893 77.735 135.21 79.905 C 121.527 82.074 109.017 88.941 99.84 99.32 C 89.871 95.945 79.051 96.024 69.133 99.545 C 59.215 103.065 50.765 109.826 45.155 118.73 C 39.545 127.634 37.094 138.174 38.2 148.64 L 37.94 148.64 C 30.615 148.64 23.582 151.553 18.403 156.733 C 13.223 161.912 10.31 168.945 10.31 176.27 C 10.31 183.595 13.223 190.628 18.403 195.807 C 23.582 200.987 30.615 203.9 37.94 203.9 L 179 203.9 C 198.116 182.073 208.646 154.015 208.61 125 Z"
android:fillColor="#FFFFFF" android:strokeWidth="1"
android:fillAlpha="0.55"/>
<path android:name="path_2" android:pathData="M 99.84 99.32 C 89.871 95.945 79.051 96.024 69.133 99.545 C 59.215 103.065 50.765 109.826 45.155 118.73 C 39.545 127.634 37.094 138.174 38.2 148.64 L 37.94 148.64 C 30.783 148.665 23.909 151.471 18.779 156.461 C 13.648 161.452 10.653 168.246 10.43 175.399 C 10.207 182.553 12.773 189.52 17.583 194.82 C 22.392 200.121 29.079 203.349 36.22 203.82 C 67.216 202.93 96.673 189.98 118.284 167.742 C 139.895 145.504 151.997 115.689 152 84.68 C 152 83 151.94 81.33 151.87 79.68 C 149.443 79.361 146.998 79.194 144.55 79.18 C 136.095 79.171 127.735 80.962 120.026 84.434 C 112.317 87.907 105.435 92.982 99.84 99.32 Z"
android:fillColor="#FFFFFF" android:strokeWidth="1"
android:fillAlpha="1"/>
</group>
</vector>

View file

@ -1,34 +1,34 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_height="match_parent"> android:orientation="vertical">
<TextView <TextView
android:id="@+id/text1" android:id="@+id/text1"
android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:layout_width="match_parent"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:layout_height="wrap_content"
android:layout_marginTop="20dp" android:layout_rowWeight="1"
android:layout_marginBottom="10dp" android:layout_marginTop="20dp"
android:textStyle="bold" android:layout_marginBottom="10dp"
android:textSize="20sp" android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:textColor="?attr/textColor" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:layout_width="match_parent" android:textColor="?attr/textColor"
android:layout_rowWeight="1" android:textSize="20sp"
tools:text="Test" android:textStyle="bold"
android:layout_height="wrap_content" /> tools:text="Test" />
<ListView <ListView
android:nextFocusRight="@id/cancel_btt" android:id="@+id/listview1"
android:nextFocusLeft="@id/apply_btt" android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listview1" android:layout_rowWeight="1"
android:layout_marginBottom="60dp" android:layout_marginBottom="60dp"
android:paddingTop="10dp" android:nestedScrollingEnabled="true"
android:requiresFadingEdge="vertical" android:nextFocusLeft="@id/apply_btt"
tools:listitem="@layout/sort_bottom_single_choice_no_checkmark" android:nextFocusRight="@id/cancel_btt"
android:layout_width="match_parent" android:paddingTop="10dp"
android:layout_height="match_parent" android:requiresFadingEdge="vertical"
android:layout_rowWeight="1" /> tools:listitem="@layout/sort_bottom_single_choice_no_checkmark" />
</LinearLayout> </LinearLayout>

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/home_header" android:id="@+id/home_header"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -86,7 +85,6 @@
android:gravity="center" android:gravity="center"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -115,7 +113,6 @@
style="@style/WhiteButton" style="@style/WhiteButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:text="@string/home_play" android:text="@string/home_play"
app:icon="@drawable/ic_baseline_play_arrow_24" /> app:icon="@drawable/ic_baseline_play_arrow_24" />
@ -148,17 +145,16 @@
<TextView <TextView
android:id="@+id/home_watch_parent_item_title" android:id="@+id/home_watch_parent_item_title"
style="@style/WatchHeaderText" style="@style/WatchHeaderText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="0dp" android:layout_marginEnd="0dp"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/home_more_info"
android:padding="12dp" android:padding="12dp"
android:text="@string/continue_watching" android:text="@string/continue_watching"
app:drawableRightCompat="@drawable/ic_baseline_arrow_forward_24" app:drawableRightCompat="@drawable/ic_baseline_arrow_forward_24"
app:drawableTint="?attr/white" app:drawableTint="?attr/white" />
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/home_more_info"/>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/home_watch_child_recyclerview" android:id="@+id/home_watch_child_recyclerview"
@ -167,7 +163,7 @@
android:clipToPadding="false" android:clipToPadding="false"
android:descendantFocusability="afterDescendants" android:descendantFocusability="afterDescendants"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingEnd="5dp" android:paddingHorizontal="5dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/home_result_grid" /> tools:listitem="@layout/home_result_grid" />
@ -184,9 +180,9 @@
<FrameLayout <FrameLayout
android:id="@+id/home_bookmark_parent_item_title" android:id="@+id/home_bookmark_parent_item_title"
android:background="?android:attr/selectableItemBackground"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground">
<HorizontalScrollView <HorizontalScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -262,14 +258,13 @@
</HorizontalScrollView> </HorizontalScrollView>
<ImageView <ImageView
android:layout_marginEnd="12dp"
android:layout_gravity="end"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="end"
android:layout_marginEnd="12dp"
android:contentDescription="@string/home_more_info"
android:src="@drawable/ic_baseline_arrow_forward_24" android:src="@drawable/ic_baseline_arrow_forward_24"
app:drawableTint="?attr/white" app:drawableTint="?attr/white" />
android:contentDescription="@string/home_more_info"/>
</FrameLayout> </FrameLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
@ -277,10 +272,9 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
android:descendantFocusability="afterDescendants" android:descendantFocusability="afterDescendants"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingEnd="5dp" android:paddingHorizontal="5dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/home_result_grid" /> tools:listitem="@layout/home_result_grid" />
</LinearLayout> </LinearLayout>

View file

@ -129,9 +129,9 @@
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/result_scroll" android:id="@+id/result_scroll"
android:layout_width="match_parent" android:layout_width="match_parent"
android:paddingBottom="100dp" android:layout_height="wrap_content"
android:clipToPadding="false" android:clipToPadding="false"
android:layout_height="wrap_content"> android:paddingBottom="100dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -326,13 +326,12 @@
<ImageView <ImageView
android:id="@+id/result_poster" android:id="@+id/result_poster"
android:layout_width="100dp" android:layout_width="100dp"
android:layout_height="140dp" android:layout_height="140dp"
android:layout_gravity="bottom"
android:contentDescription="@string/result_poster_img_des" android:contentDescription="@string/result_poster_img_des"
android:foreground="@drawable/outline_drawable" android:foreground="@drawable/outline_drawable"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:layout_gravity="bottom"
tools:src="@drawable/example_poster" /> tools:src="@drawable/example_poster" />
</androidx.cardview.widget.CardView> </androidx.cardview.widget.CardView>
@ -516,8 +515,8 @@
android:visibility="gone" /> android:visibility="gone" />
<com.google.android.material.chip.ChipGroup <com.google.android.material.chip.ChipGroup
style="@style/ChipParent"
android:id="@+id/result_tag" android:id="@+id/result_tag"
style="@style/ChipParent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
<!--<com.lagradost.cloudstream3.widget.FlowLayout <!--<com.lagradost.cloudstream3.widget.FlowLayout
@ -818,10 +817,13 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginStart="0dp" android:layout_marginStart="0dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:drawableEnd="@drawable/ic_baseline_keyboard_arrow_down_24"
android:nextFocusLeft="@id/result_episode_select" android:nextFocusLeft="@id/result_episode_select"
android:nextFocusRight="@id/result_episode_select" android:nextFocusRight="@id/result_episode_select"
android:nextFocusUp="@id/result_description" android:nextFocusUp="@id/result_description"
android:nextFocusDown="@id/result_episodes" android:nextFocusDown="@id/result_episodes"
android:paddingStart="10dp"
android:paddingEnd="5dp"
android:visibility="gone" android:visibility="gone"
tools:text="Season 1" tools:text="Season 1"
tools:visibility="visible" /> tools:visibility="visible" />
@ -829,16 +831,16 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/result_episode_select" android:id="@+id/result_episode_select"
style="@style/MultiSelectButton" style="@style/MultiSelectButton"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginStart="0dp" android:layout_marginStart="0dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:drawableEnd="@drawable/ic_baseline_keyboard_arrow_down_24"
android:nextFocusLeft="@id/result_season_button" android:nextFocusLeft="@id/result_season_button"
android:nextFocusRight="@id/result_season_button" android:nextFocusRight="@id/result_season_button"
android:nextFocusUp="@id/result_description" android:nextFocusUp="@id/result_description"
android:nextFocusDown="@id/result_episodes" android:nextFocusDown="@id/result_episodes"
android:paddingStart="10dp"
android:paddingEnd="5dp"
android:visibility="gone" android:visibility="gone"
tools:text="50-100" tools:text="50-100"
tools:visibility="visible" /> tools:visibility="visible" />
@ -846,15 +848,16 @@
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/result_dub_select" android:id="@+id/result_dub_select"
style="@style/MultiSelectButton" style="@style/MultiSelectButton"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginStart="0dp" android:layout_marginStart="0dp"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:drawableEnd="@drawable/ic_baseline_keyboard_arrow_down_24"
android:nextFocusLeft="@id/result_season_button" android:nextFocusLeft="@id/result_season_button"
android:nextFocusRight="@id/result_season_button" android:nextFocusRight="@id/result_season_button"
android:nextFocusUp="@id/result_description" android:nextFocusUp="@id/result_description"
android:nextFocusDown="@id/result_episodes" android:nextFocusDown="@id/result_episodes"
android:paddingStart="10dp"
android:paddingEnd="5dp"
android:visibility="gone" android:visibility="gone"
tools:text="Dubbed" tools:text="Dubbed"
tools:visibility="visible" /> tools:visibility="visible" />

View file

@ -57,6 +57,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="50dp" android:layout_height="50dp"
android:id="@+id/media_route_button_holder" android:id="@+id/media_route_button_holder"
android:animateLayoutChanges="true"
android:layout_gravity="center_vertical|end"> android:layout_gravity="center_vertical|end">
<androidx.mediarouter.app.MediaRouteButton <androidx.mediarouter.app.MediaRouteButton
@ -69,15 +70,35 @@
app:mediaRouteButtonTint="?attr/textColor" /> app:mediaRouteButtonTint="?attr/textColor" />
<ImageView <ImageView
android:visibility="gone"
android:nextFocusUp="@id/result_back" android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_description" android:nextFocusDown="@id/result_description"
android:nextFocusLeft="@id/result_add_sync" android:nextFocusLeft="@id/result_add_sync"
android:nextFocusRight="@id/result_share"
tools:visibility="visible"
android:id="@+id/result_subscribe"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_margin="5dp"
android:elevation="10dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/baseline_notifications_none_24"
android:layout_gravity="end|center_vertical"
app:tint="?attr/textColor" />
<ImageView
android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_description"
android:nextFocusLeft="@id/result_subscribe"
android:nextFocusRight="@id/result_open_in_browser" android:nextFocusRight="@id/result_open_in_browser"
android:id="@+id/result_share" android:id="@+id/result_share"
android:layout_width="25dp" android:layout_width="25dp"
android:layout_height="25dp" android:layout_height="25dp"
android:layout_marginEnd="10dp" android:layout_margin="5dp"
android:elevation="10dp" android:elevation="10dp"
android:background="?android:attr/selectableItemBackgroundBorderless" android:background="?android:attr/selectableItemBackgroundBorderless"

View file

@ -199,17 +199,13 @@
android:id="@+id/result_back" android:id="@+id/result_back"
android:layout_width="30dp" android:layout_width="30dp"
android:layout_height="30dp" android:layout_height="30dp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:background="?android:attr/selectableItemBackgroundBorderless" android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true" android:clickable="true"
android:contentDescription="@string/go_back" android:contentDescription="@string/go_back"
android:focusable="true" android:focusable="true"
android:gravity="center_vertical" android:gravity="center_vertical"
android:nextFocusDown="@id/result_description" android:nextFocusDown="@id/result_description"
android:src="@drawable/ic_baseline_arrow_back_24" android:src="@drawable/ic_baseline_arrow_back_24"
app:tint="?attr/white" /> app:tint="?attr/white" />
@ -385,8 +381,8 @@
<com.google.android.material.chip.ChipGroup <com.google.android.material.chip.ChipGroup
style="@style/ChipParent"
android:id="@+id/result_tag" android:id="@+id/result_tag"
style="@style/ChipParent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
@ -423,11 +419,11 @@
<LinearLayout <LinearLayout
android:animateLayoutChanges="true"
android:id="@+id/result_movie_parent" android:id="@+id/result_movie_parent"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="5dp" android:layout_marginTop="5dp"
android:animateLayoutChanges="true"
android:orientation="horizontal" android:orientation="horizontal"
tools:visibility="visible"> tools:visibility="visible">
@ -568,6 +564,7 @@
android:layout_weight="1" android:layout_weight="1"
android:minWidth="250dp" android:minWidth="250dp"
android:nextFocusLeft="@id/result_movie_progress_downloaded_holder" android:nextFocusLeft="@id/result_movie_progress_downloaded_holder"
android:nextFocusRight="@id/result_bookmark_button"
android:nextFocusDown="@id/result_resume_series_button_play" android:nextFocusDown="@id/result_resume_series_button_play"
android:text="@string/type_none" android:text="@string/type_none"
android:visibility="visible" /> android:visibility="visible" />

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:backgroundTint="?attr/primaryBlackBackground"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/provider_test_appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/settings_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/primaryGrayBackground"
android:paddingTop="@dimen/navbar_height"
app:layout_scrollFlags="scroll|enterAlways"
app:navigationIconTint="?attr/iconColor"
app:titleTextColor="?attr/textColor"
tools:title="@string/category_provider_test">
</com.google.android.material.appbar.MaterialToolbar>
<com.lagradost.cloudstream3.ui.settings.testing.TestView
android:id="@+id/provider_test"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusDown="@id/provider_test_recycler_view"
app:header_text="@string/category_provider_test"
app:layout_scrollFlags="scroll|enterAlways" />
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/provider_test_recycler_view"
android:clipToPadding="false"
android:paddingBottom="60dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/provider_test_item" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -23,9 +23,7 @@
android:nextFocusUp="@id/home_child_more_info" android:nextFocusUp="@id/home_child_more_info"
android:paddingHorizontal="5dp" android:paddingHorizontal="5dp"
android:clipToPadding="false" android:clipToPadding="false"
android:descendantFocusability="afterDescendants" android:descendantFocusability="afterDescendants"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:id="@+id/home_child_recyclerview" android:id="@+id/home_child_recyclerview"
android:orientation="horizontal" android:orientation="horizontal"

View file

@ -2,33 +2,30 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:orientation="vertical">
<TextView <TextView
android:layout_marginStart="@dimen/navbar_width"
android:id="@+id/home_child_more_info" android:id="@+id/home_child_more_info"
android:padding="12dp" style="@style/WatchHeaderText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
style="@style/WatchHeaderText" android:layout_marginStart="@dimen/navbar_width"
android:layout_marginEnd="0dp" android:layout_marginEnd="0dp"
android:padding="12dp"
tools:text="Trending" /> tools:text="Trending" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:nextFocusUp="@id/home_child_more_info"
android:paddingEnd="5dp"
android:paddingStart="@dimen/navbar_width"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:id="@+id/home_child_recyclerview" android:id="@+id/home_child_recyclerview"
android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
android:nextFocusUp="@id/home_child_more_info"
android:orientation="horizontal"
android:paddingStart="@dimen/navbar_width"
android:paddingEnd="5dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/home_result_grid" /> tools:listitem="@layout/home_result_grid" />
</LinearLayout> </LinearLayout>

View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:nextFocusRight="@id/action_button"
android:orientation="horizontal"
android:padding="12dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/main_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/textColor"
android:textSize="16sp"
tools:text="Test repository" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/lang_icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="5dp"
tools:text="🇷🇼"
android:textColor="?attr/grayTextColor"
tools:visibility="visible" />
<TextView
android:id="@+id/passed_failed_marker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Failed"
tools:visibility="visible" />
</LinearLayout>
<TextView
android:id="@+id/fail_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/grayTextColor"
tools:text="Unable to load videos"
tools:visibility="visible" />
</LinearLayout>
<ImageView
android:id="@+id/action_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:layout_marginStart="10dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
android:padding="12dp"
android:src="@drawable/baseline_text_snippet_24"
app:tint="?attr/white" />
</LinearLayout>

View file

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.Material3.CardView.Elevated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardBackgroundColor="?attr/primaryBlackBackground">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="10dp">
<LinearLayout
android:id="@+id/main_test_section"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/outline_drawable_less"
android:gravity="center"
android:orientation="horizontal"
android:paddingHorizontal="10dp"
android:paddingVertical="10dp">
<TextView
android:id="@+id/main_test_header"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="17sp"
tools:text="Homepage test" />
<TextView
android:id="@+id/main_test_section_progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end"
android:textSize="17sp"
tools:text="67 / 120 " />
</LinearLayout>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
app:cardBackgroundColor="?attr/primaryGrayBackground"
app:cardCornerRadius="@dimen/rounded_image_radius">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/passed_test_section"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/outline_drawable_less"
android:gravity="center"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Tests passed"
android:textSize="17sp" />
<TextView
android:id="@+id/passed_test_section_progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end"
android:textSize="17sp"
tools:text="55" />
</LinearLayout>
<LinearLayout
android:id="@+id/failed_test_section"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/outline_drawable_less"
android:gravity="center"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Tests failed"
android:textSize="17sp" />
<TextView
android:id="@+id/failed_test_section_progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end"
android:textSize="17sp"
tools:text="12" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<com.google.android.material.button.MaterialButton
android:id="@+id/tests_play_pause"
style="@style/WhiteButton"
android:layout_width="wrap_content"
android:layout_margin="10dp"
android:text="@string/start"
app:icon="@drawable/ic_baseline_play_arrow_24">
</com.google.android.material.button.MaterialButton>
</LinearLayout>
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/test_total_progress"
style="@android:style/Widget.Material.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="5dp"
android:layout_gravity="bottom"
android:layout_marginBottom="-1.5dp"
android:progressBackgroundTint="?attr/colorPrimary"
android:progressTint="?attr/colorPrimary"
android:visibility="gone"
tools:progress="50" />
</androidx.cardview.widget.CardView>

View file

@ -519,6 +519,23 @@
app:popExitAnim="@anim/exit_anim" app:popExitAnim="@anim/exit_anim"
tools:layout="@layout/fragment_player" /> tools:layout="@layout/fragment_player" />
<fragment
android:id="@+id/navigation_test_providers"
android:name="com.lagradost.cloudstream3.ui.settings.testing.TestFragment"
android:layout_height="match_parent"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim"
tools:layout="@layout/fragment_testing">
<action
android:id="@+id/action_navigation_global_to_navigation_test_providers"
app:destination="@id/navigation_test_providers"
app:enterAnim="@anim/enter_anim"
app:exitAnim="@anim/exit_anim"
app:popEnterAnim="@anim/enter_anim"
app:popExitAnim="@anim/exit_anim" />
</fragment>
<fragment <fragment
android:id="@+id/navigation_setup_language" android:id="@+id/navigation_setup_language"

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS --> <!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS -->
<string name="result_poster_img_des">ملصق</string> <string name="result_poster_img_des">ملصق</string>
@ -103,7 +103,7 @@
<string name="action_open_watching">مزيد من المعلومات</string> <string name="action_open_watching">مزيد من المعلومات</string>
<string name="vpn_might_be_needed">قد تكون هناك حاجة إلى VPN لكي يعمل هذا المزود بشكل صحيح</string> <string name="vpn_might_be_needed">قد تكون هناك حاجة إلى VPN لكي يعمل هذا المزود بشكل صحيح</string>
<string name="vpn_torrent">هذا المزود هو تورنت ، يوصى باستخدام شبكة ظاهرية خاصة</string> <string name="vpn_torrent">هذا المزود هو تورنت ، يوصى باستخدام شبكة ظاهرية خاصة</string>
<string name="provider_info_meta">لا يتم توفير البيانات الوصفية بواسطة الموقع ، وسيفشل تحميل الفيديو إذا لم يكن موجودًا في الموقع.</string> <string name="provider_info_meta">لا يتم توفير البيانات الوصفية بواسطة الموقع، وسيفشل تحميل الفيديو إذا لم يكن موجودًا في الموقع.</string>
<string name="torrent_plot">الوصف</string> <string name="torrent_plot">الوصف</string>
<string name="normal_no_plot">لم يتم العثور على وصف</string> <string name="normal_no_plot">لم يتم العثور على وصف</string>
<string name="torrent_no_plot">لم يتم العثور على وصف</string> <string name="torrent_no_plot">لم يتم العثور على وصف</string>
@ -119,16 +119,16 @@
<string name="eigengraumode_settings">وضع إيغنغرافي</string> <string name="eigengraumode_settings">وضع إيغنغرافي</string>
<string name="eigengraumode_settings_des">يضيف خيار السرعة في المُشغل</string> <string name="eigengraumode_settings_des">يضيف خيار السرعة في المُشغل</string>
<string name="swipe_to_seek_settings">السحب لتقديم</string> <string name="swipe_to_seek_settings">السحب لتقديم</string>
<string name="swipe_to_seek_settings_des">إسحب إلى اليسار أو اليمين للتحكم في الوقت في مُشغل الفيديو</string> <string name="swipe_to_seek_settings_des">اسحب من جانب إلى آخر للتحكم في موضعك في مقطع فيديو</string>
<string name="swipe_to_change_settings">السحب لتغيير الإعدادات</string> <string name="swipe_to_change_settings">السحب لتغيير الإعدادات</string>
<string name="swipe_to_change_settings_des">إسحب على الجانب الأيسر أو الأيمن لتغيير السطوع أو مستوى الصوت</string> <string name="swipe_to_change_settings_des">مرر لأعلى أو لأسفل على الجانب الأيسر أو الأيمن لتغيير السطوع أو مستوى الصوت</string>
<string name="autoplay_next_settings">تشغيل الحلقة التالية تلقائيًا</string> <string name="autoplay_next_settings">تشغيل الحلقة التالية تلقائيًا</string>
<string name="autoplay_next_settings_des">تبدأ الحلقة التالية عندما تنتهي الحالية</string> <string name="autoplay_next_settings_des">تبدأ الحلقة التالية عندما تنتهي الحالية</string>
<string name="double_tap_to_seek_settings">النقر مرتان للتقديم للأمام أو للخلف</string> <string name="double_tap_to_seek_settings">النقر مرتان للتقديم للأمام أو للخلف</string>
<string name="double_tap_to_pause_settings">الضغط مرتان لإيقاف مؤقت</string> <string name="double_tap_to_pause_settings">الضغط مرتان لإيقاف مؤقت</string>
<string name="double_tap_to_seek_amount_settings">التحكم في مدى تقديم المُشغل</string> <string name="double_tap_to_seek_amount_settings">التحكم في مدى تقديم المُشغل(ثوان)</string>
<string name="double_tap_to_seek_settings_des">إضغط مرتين على الجانب الأيمن أو الأيسر للتقديم للأمام أو للخلف</string> <string name="double_tap_to_seek_settings_des">إضغط مرتين على الجانب الأيمن أو الأيسر للتقديم للأمام أو للخلف</string>
<string name="double_tap_to_pause_settings_des">إضغط في الوسط لإيقاف مؤقت</string> <string name="double_tap_to_pause_settings_des">اضغط مرتين في المنتصف للتوقف</string>
<string name="use_system_brightness_settings">استخدم سطوع النظام</string> <string name="use_system_brightness_settings">استخدم سطوع النظام</string>
<string name="use_system_brightness_settings_des">استخدم سطوع النظام في مُشغل التطبيق بدلاً من التراكب الداكن</string> <string name="use_system_brightness_settings_des">استخدم سطوع النظام في مُشغل التطبيق بدلاً من التراكب الداكن</string>
<string name="episode_sync_settings">تحديث تقدم المشاهدة</string> <string name="episode_sync_settings">تحديث تقدم المشاهدة</string>
@ -155,7 +155,7 @@
<string name="automatic_plugin_updates">تحديث الإضافات تلقائيًا</string> <string name="automatic_plugin_updates">تحديث الإضافات تلقائيًا</string>
<string name="automatic_plugin_download">تنزيل الإضافات تلقائيًا</string> <string name="automatic_plugin_download">تنزيل الإضافات تلقائيًا</string>
<string name="updates_settings">التحديث التلقائي</string> <string name="updates_settings">التحديث التلقائي</string>
<string name="updates_settings_des">البحث تلقائيًا عن التحديثات الجديدة عند البداية</string> <string name="updates_settings_des">ابحث تلقائيا عن التحديثات الجديدة بعد بدء التطبيق.</string>
<string name="uprereleases_settings">التحديث إلى الاصدارات التجريبية (بيتا)</string> <string name="uprereleases_settings">التحديث إلى الاصدارات التجريبية (بيتا)</string>
<string name="uprereleases_settings_des">البحث عن التحديثات التجريبية بدلاً من الإصدارات الكاملة فقط</string> <string name="uprereleases_settings_des">البحث عن التحديثات التجريبية بدلاً من الإصدارات الكاملة فقط</string>
<string name="github">غيت هاب</string> <string name="github">غيت هاب</string>
@ -170,7 +170,7 @@
<string name="copy_link_toast">تم نسخ الرابط إلى الحافظة</string> <string name="copy_link_toast">تم نسخ الرابط إلى الحافظة</string>
<string name="play_episode_toast">تشغيل الحلقة</string> <string name="play_episode_toast">تشغيل الحلقة</string>
<string name="subs_default_reset_toast">إعادة التعيين إلى القيمة الافتراضية</string> <string name="subs_default_reset_toast">إعادة التعيين إلى القيمة الافتراضية</string>
<string name="acra_report_toast">عذرا ، تعطل التطبيق. سيتم إرسال تقرير خطأ مجهول إلى المطورين</string> <string name="acra_report_toast">عذرا، تعطل التطبيق. سيتم إرسال تقرير خطأ مجهول إلى المطورين</string>
<string name="season">موسم</string> <string name="season">موسم</string>
<string name="no_season">لا موسم</string> <string name="no_season">لا موسم</string>
<string name="episode">حلقة</string> <string name="episode">حلقة</string>
@ -218,8 +218,8 @@
<string name="movies_singular">فيلم</string> <string name="movies_singular">فيلم</string>
<string name="tv_series_singular">مسلسل</string> <string name="tv_series_singular">مسلسل</string>
<string name="cartoons_singular">كرتون</string> <string name="cartoons_singular">كرتون</string>
<string name="anime_singular">أنمي</string> <string name="anime_singular">أنيمي</string>
<string name="ova_singular">اوفا</string> <string name="ova_singular">أوفا</string>
<string name="torrent_singular">تورنت</string> <string name="torrent_singular">تورنت</string>
<string name="documentaries_singular">وثائقي</string> <string name="documentaries_singular">وثائقي</string>
<string name="asian_drama_singular">دراما آسيوية</string> <string name="asian_drama_singular">دراما آسيوية</string>
@ -259,15 +259,15 @@
<string name="dont_show_again">لا تظهر مرة أخرى</string> <string name="dont_show_again">لا تظهر مرة أخرى</string>
<string name="skip_update">تخطي هذا التحديث</string> <string name="skip_update">تخطي هذا التحديث</string>
<string name="update">تحديث</string> <string name="update">تحديث</string>
<string name="watch_quality_pref">جودة المشاهدة المفضلة</string> <string name="watch_quality_pref">جودة المشاهدة المفضلة (WiFi)</string>
<string name="limit_title">أقصى عدد لحروف عنوان مُشغل الفيديو</string> <string name="limit_title">أقصى عدد لحروف عنوان مُشغل الفيديو</string>
<string name="limit_title_rez">أبعاد مُشغل الفيديو</string> <string name="limit_title_rez">أبعاد مُشغل الفيديو</string>
<string name="video_buffer_size_settings">حجم ذاكرة التخزين المؤقت للفيديو</string> <string name="video_buffer_size_settings">حجم ذاكرة التخزين المؤقت للفيديو</string>
<string name="video_buffer_length_settings">طول التخزين المؤقت</string> <string name="video_buffer_length_settings">طول التخزين المؤقت</string>
<string name="video_buffer_disk_settings">التخزين المؤقت للفيديو على القرص</string> <string name="video_buffer_disk_settings">التخزين المؤقت للفيديو على القرص</string>
<string name="video_buffer_clear_settings">مسح التخزين المؤقت للصورة والفيديو</string> <string name="video_buffer_clear_settings">مسح التخزين المؤقت للصورة والفيديو</string>
<string name="video_ram_description">يتسبب في حدوث أعطال إذا تم ضبطه على مستوى مرتفع جدا على الأجهزة ذات الذاكرة المنخفضة ، مثل Android TV.</string> <string name="video_ram_description">يتسبب في حدوث أعطال إذا تم ضبطه على مستوى مرتفع جدا على الأجهزة ذات الذاكرة المنخفضة، مثل تلفزيون أندرويد.</string>
<string name="video_disk_description">يسبب مشاكل إذا تم ضبطه على مستوى مرتفع جدا على الأجهزة ذات مساحة التخزين المنخفضة ، مثل Android TV.</string> <string name="video_disk_description">يسبب مشاكل إذا تم ضبطه على مستوى مرتفع جدا على الأجهزة ذات مساحة التخزين المنخفضة، مثل تلفزيون أندرويد.</string>
<string name="dns_pref">إستخدام DNS بدلا من HTTPS</string> <string name="dns_pref">إستخدام DNS بدلا من HTTPS</string>
<string name="dns_pref_summary">مفيد لتجاوز حجب مزود خدمة الإنترنت</string> <string name="dns_pref_summary">مفيد لتجاوز حجب مزود خدمة الإنترنت</string>
<string name="add_site_pref">موقع بديل (نسخة)</string> <string name="add_site_pref">موقع بديل (نسخة)</string>
@ -284,7 +284,7 @@
<string name="legal_notice_text" translatable="false">Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.</string> <string name="legal_notice_text" translatable="false">Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.</string>
<string name="category_general">عام</string> <string name="category_general">عام</string>
<string name="random_button_settings">زر العشوائي</string> <string name="random_button_settings">زر العشوائي</string>
<string name="random_button_settings_desc">إظهار زر العشوائي على الصفحة الرئيسية</string> <string name="random_button_settings_desc">يظهر الزر على الصفحة الرئيسية والذي يمكنه اختيار فيلم عشوائي أو مسلسل تلفزيوني من الصفحة الرئيسية</string>
<string name="provider_lang_settings">لغات المزود</string> <string name="provider_lang_settings">لغات المزود</string>
<string name="app_layout">واجهة التطبيق</string> <string name="app_layout">واجهة التطبيق</string>
<string name="preferred_media_settings">المحتوى المفضل</string> <string name="preferred_media_settings">المحتوى المفضل</string>
@ -342,7 +342,7 @@
<string name="all">الكل</string> <string name="all">الكل</string>
<string name="max">الحد الاقصي</string> <string name="max">الحد الاقصي</string>
<string name="min">الحد الأدنى</string> <string name="min">الحد الأدنى</string>
<string name="subtitles_none" translatable="false">\@string/none</string> <string name="subtitles_none" translatable="false">@string/none</string>
<string name="subtitles_outline">الخطوط المحيطة</string> <string name="subtitles_outline">الخطوط المحيطة</string>
<string name="subtitles_depressed">النمط المنخفض</string> <string name="subtitles_depressed">النمط المنخفض</string>
<string name="subtitles_shadow">ظل</string> <string name="subtitles_shadow">ظل</string>
@ -360,7 +360,7 @@
https://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog https://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog
--> -->
<string name="subtitles_example_text">نصٌّ حكيمٌ لهُ سِرٌّ قاطِعٌ وَذُو شَأنٍ عَظيمٍ مكتوبٌ على ثوبٍ أخضرَ ومُغلفٌ بجلدٍ أزرق</string> <string name="subtitles_example_text">نصٌّ حكيمٌ لهُ سِرٌّ قاطِعٌ وَذُو شَأنٍ عَظيمٍ مكتوبٌ على ثوبٍ أخضرَ ومُغلفٌ بجلدٍ أزرق</string>
<string name="recommended">مُوصي به</string> <string name="recommended">مُوصى به</string>
<string name="player_loaded_subtitles" formatted="true">تم تحميل %s</string> <string name="player_loaded_subtitles" formatted="true">تم تحميل %s</string>
<string name="player_load_subtitles">إختيار ملف</string> <string name="player_load_subtitles">إختيار ملف</string>
<string name="player_load_subtitles_online">تحميل من الانترنت</string> <string name="player_load_subtitles_online">تحميل من الانترنت</string>
@ -538,4 +538,27 @@
<string name="library">المكتبة</string> <string name="library">المكتبة</string>
<string name="safe_mode_file">تم العثور على ملف الوضع الآمن! <string name="safe_mode_file">تم العثور على ملف الوضع الآمن!
\nلا يتم تحميل أي ملحقات عند بدء التشغيل حتى تتم إزالة الملف.</string> \nلا يتم تحميل أي ملحقات عند بدء التشغيل حتى تتم إزالة الملف.</string>
</resources> <string name="android_tv_interface_off_seek_settings_summary">مدة التقديم عنما يكون المشغل مخفيا</string>
<string name="android_tv_interface_off_seek_settings">مدة التقديم - المشغل مخفي</string>
<string name="pref_category_android_tv">تلفزيون أندرويد</string>
<string name="android_tv_interface_on_seek_settings_summary">مدة التقديم عنما يكون المشغل مرئيا</string>
<string name="android_tv_interface_on_seek_settings">مدة التقديم- المشغل المرئي</string>
<string name="test_failed">فشل</string>
<string name="test_passed">نجح</string>
<string name="category_provider_test">إختبار المزود</string>
<string name="restart">إعادة التشغيل</string>
<string name="test_log">سجل</string>
<string name="start">بَدأ</string>
<string name="stop">إيقاف</string>
<string name="subscription_in_progress_notification">تحديث العروض التي تم الاشتراك فيها</string>
<string name="subscription_deleted">إلغاء الاشتراك من %s</string>
<string name="subscription_episode_released">تم إصدار الحلقة %d!</string>
<string name="subscription_list_name">مشترك</string>
<string name="subscription_new">مشترك في %s</string>
<string name="pref_category_bypass">تجاوز مزود خدمة الإنترنت</string>
<string name="revert">استرجاع</string>
<string name="jsdelivr_enabled">فشل الوصول إلى GitHub ، وتمكين وكيل jsdelivr.</string>
<string name="jsdelivr_proxy_summary">باستخدام jsdelivr ، يمكن تجاوز حظر GitHub. قد يؤخر التحديثات لبضعة أيام.</string>
<string name="jsdelivr_proxy">وكيل raw.githubusercontent.com</string>
<string name="watch_quality_pref_data">جودة المشاهدة المفضلة (بيانات الجوال)</string>
</resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG --> <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string formatted="true" name="app_dub_sub_episode_text_format">%s еп. %d</string> <string formatted="true" name="app_dub_sub_episode_text_format">%s еп. %d</string>
<string formatted="true" name="cast_format">Актьори: %s</string> <string formatted="true" name="cast_format">Актьори: %s</string>
@ -105,7 +106,7 @@
<string name="continue_watching">Продължете да гледате</string> <string name="continue_watching">Продължете да гледате</string>
<string name="action_remove_watching">Премахване</string> <string name="action_remove_watching">Премахване</string>
<string name="action_open_watching">Повече информация</string> <string name="action_open_watching">Повече информация</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Може да е необходим VPN, за да работи правилно този доставчик</string> <string name="vpn_might_be_needed">Може да е необходим VPN, за да работи правилно този доставчик</string>
<string name="vpn_torrent">Този доставчик е торент, препоръчва се VPN</string> <string name="vpn_torrent">Този доставчик е торент, препоръчва се VPN</string>
<string name="provider_info_meta">Метаданните не се предоставят от сайта, зареждането на видео ще бъде неуспешно, ако не съществува на сайта.</string> <string name="provider_info_meta">Метаданните не се предоставят от сайта, зареждането на видео ще бъде неуспешно, ако не съществува на сайта.</string>
@ -223,8 +224,8 @@
<string name="movies_singular">Филм</string> <string name="movies_singular">Филм</string>
<string name="tv_series_singular">Серия</string> <string name="tv_series_singular">Серия</string>
<string name="cartoons_singular">Анимационен филм</string> <string name="cartoons_singular">Анимационен филм</string>
<string name="anime_singular">\@string/anime</string> <string name="anime_singular">@string/anime</string>
<string name="ova_singular">\@string/ova</string> <string name="ova_singular">@string/ova</string>
<string name="torrent_singular">Торент</string> <string name="torrent_singular">Торент</string>
<string name="documentaries_singular">Документален филм</string> <string name="documentaries_singular">Документален филм</string>
<string name="asian_drama_singular">Азиатска драма</string> <string name="asian_drama_singular">Азиатска драма</string>
@ -497,4 +498,4 @@
<string name="delayed_update_notice">Приложението ще се актуализира при изход от него</string> <string name="delayed_update_notice">Приложението ще се актуализира при изход от него</string>
<string name="update_started">Започна Актуализация</string> <string name="update_started">Започна Актуализация</string>
<string name="action_remove_from_watched">Премахване от гледани</string> <string name="action_remove_from_watched">Премахване от гледани</string>
</resources> </resources>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<string name="result_poster_img_des">পোস্টার</string> <string name="result_poster_img_des">পোস্টার</string>
<string name="play_with_app_name">ক্লাউডস্ট্রিম দিয়ে চালান</string> <string name="play_with_app_name">ক্লাউডস্ট্রিম দিয়ে চালান</string>
@ -143,9 +143,9 @@
<string name="category_updates">হালনাগাদ ও ব্যাকআপ</string> <string name="category_updates">হালনাগাদ ও ব্যাকআপ</string>
<string name="updates_settings">অ্যাপ এর হালনাগাদ দেখান</string> <string name="updates_settings">অ্যাপ এর হালনাগাদ দেখান</string>
<string name="swipe_to_seek_settings">খুঁজতে সোয়াইপ করুন</string> <string name="swipe_to_seek_settings">খুঁজতে সোয়াইপ করুন</string>
<string name="search_poster_img_des">\@string/result_poster_img_des</string> <string name="search_poster_img_des">@string/result_poster_img_des</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="double_tap_to_seek_settings">আগাতে ডবল ট্যাপ করুন</string> <string name="double_tap_to_seek_settings">আগাতে ডবল ট্যাপ করুন</string>
<string name="eigengraumode_settings">আইজেনগ্রাভি মোড</string> <string name="eigengraumode_settings">আইজেনগ্রাভি মোড</string>
<string name="update_started">আপডেট শুরু হয়েছে</string> <string name="update_started">আপডেট শুরু হয়েছে</string>
</resources> </resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- KEYS DON'T TRANSLATE --> <!-- KEYS DON'T TRANSLATE -->
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG --> <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string> <string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
@ -10,7 +11,7 @@
<string name="next_episode_time_min_format" formatted="true">%dm</string> <string name="next_episode_time_min_format" formatted="true">%dm</string>
<!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS --> <!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS -->
<string name="result_poster_img_des">Poster</string> <string name="result_poster_img_des">Poster</string>
<string name="search_poster_img_des">\@string/result_poster_img_des</string> <string name="search_poster_img_des">@string/result_poster_img_des</string>
<string name="episode_poster_img_des">Episode Poster</string> <string name="episode_poster_img_des">Episode Poster</string>
<string name="home_main_poster_img_des">Main Poster</string> <string name="home_main_poster_img_des">Main Poster</string>
<string name="home_next_random_img_des">Next Random</string> <string name="home_next_random_img_des">Next Random</string>
@ -108,7 +109,7 @@
<string name="continue_watching">Continue Assistindo</string> <string name="continue_watching">Continue Assistindo</string>
<string name="action_remove_watching">Remover</string> <string name="action_remove_watching">Remover</string>
<string name="action_open_watching">Mais Info</string> <string name="action_open_watching">Mais Info</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Uma VPN pode ser necessária para esse fornecedor funcionar corretamente</string> <string name="vpn_might_be_needed">Uma VPN pode ser necessária para esse fornecedor funcionar corretamente</string>
<string name="vpn_torrent">Esse fornecedor é um torrent, uma VPN é recomendada</string> <string name="vpn_torrent">Esse fornecedor é um torrent, uma VPN é recomendada</string>
<string name="provider_info_meta">Metadados não são oferecidas pelo site, o carregamento do video pode falhar se ele não existir no site.</string> <string name="provider_info_meta">Metadados não são oferecidas pelo site, o carregamento do video pode falhar se ele não existir no site.</string>
@ -222,8 +223,8 @@
<string name="movies_singular">Filme</string> <string name="movies_singular">Filme</string>
<string name="tv_series_singular">Série</string> <string name="tv_series_singular">Série</string>
<string name="cartoons_singular">Desenho Animado</string> <string name="cartoons_singular">Desenho Animado</string>
<string name="anime_singular">\@string/anime</string> <string name="anime_singular">@string/anime</string>
<string name="ova_singular">\@string/ova</string> <string name="ova_singular">@string/ova</string>
<string name="torrent_singular">Torrent</string> <string name="torrent_singular">Torrent</string>
<string name="documentaries_singular">Documentário</string> <string name="documentaries_singular">Documentário</string>
<string name="asian_drama_singular">Drama Asiático</string> <string name="asian_drama_singular">Drama Asiático</string>
@ -428,4 +429,4 @@
<string name="autoplay_next_settings_des">Começa o próximo episódio quando o atual termina</string> <string name="autoplay_next_settings_des">Começa o próximo episódio quando o atual termina</string>
<string name="enable_nsfw_on_providers">Ativar NSFW em fornecedores compatíveis</string> <string name="enable_nsfw_on_providers">Ativar NSFW em fornecedores compatíveis</string>
<string name="category_providers">Fornecedores</string> <string name="category_providers">Fornecedores</string>
</resources> </resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- KEYS DON'T TRANSLATE --> <!-- KEYS DON'T TRANSLATE -->
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG --> <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string> <string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
@ -103,7 +104,7 @@
<string name="continue_watching">Pokračovat ve sledování</string> <string name="continue_watching">Pokračovat ve sledování</string>
<string name="action_remove_watching">Odebrat</string> <string name="action_remove_watching">Odebrat</string>
<string name="action_open_watching">Další informace</string> <string name="action_open_watching">Další informace</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Aby tento poskytovatel fungoval správně, budete možná potřebovat VPN</string> <string name="vpn_might_be_needed">Aby tento poskytovatel fungoval správně, budete možná potřebovat VPN</string>
<string name="vpn_torrent">Tento poskytovatel je torrent, je doporučená VPN</string> <string name="vpn_torrent">Tento poskytovatel je torrent, je doporučená VPN</string>
<string name="provider_info_meta">Web neposkytnul žádná metadata, načítání videa selže, pokud na webu neexistuje.</string> <string name="provider_info_meta">Web neposkytnul žádná metadata, načítání videa selže, pokud na webu neexistuje.</string>
@ -122,14 +123,14 @@
<string name="eigengraumode_settings">Rychlostní režim</string> <string name="eigengraumode_settings">Rychlostní režim</string>
<string name="eigengraumode_settings_des">Přidá do přehrávače možnost rychlosti</string> <string name="eigengraumode_settings_des">Přidá do přehrávače možnost rychlosti</string>
<string name="swipe_to_seek_settings">Přejet pro posun</string> <string name="swipe_to_seek_settings">Přejet pro posun</string>
<string name="swipe_to_seek_settings_des">Přejeďte prstem vlevo nebo vpravo pro ovládání času v přehrávači</string> <string name="swipe_to_seek_settings_des">Přejeďte prstem ze strany na stranu pro ovládání své pozice ve videu</string>
<string name="swipe_to_change_settings">Přejet pro změnu nastavení</string> <string name="swipe_to_change_settings">Přejet pro změnu nastavení</string>
<string name="swipe_to_change_settings_des">Přejeďte prstem na levé nebo pravé straně pro změnu jasu nebo hlasitosti</string> <string name="swipe_to_change_settings_des">Přejeďte prstem nahoru nebo dolů na levé nebo pravé straně pro změnu jasu nebo hlasitosti</string>
<string name="double_tap_to_seek_settings">Dvojité klepnutí pro posun</string> <string name="double_tap_to_seek_settings">Dvojité klepnutí pro posun</string>
<string name="double_tap_to_pause_settings">Dvojité klepnutí pro pozastavení</string> <string name="double_tap_to_pause_settings">Dvojité klepnutí pro pozastavení</string>
<string name="double_tap_to_seek_amount_settings">Množství času k posunu</string> <string name="double_tap_to_seek_amount_settings">Množství času k posunu (sekundy)</string>
<string name="double_tap_to_seek_settings_des">Klepněte dvakrát vpravo nebo vlevo pro posun vpřed nebo vzad</string> <string name="double_tap_to_seek_settings_des">Klepněte dvakrát vpravo nebo vlevo pro posun vpřed nebo vzad</string>
<string name="double_tap_to_pause_settings_des">Klepněte doprostřed pro pozastavení</string> <string name="double_tap_to_pause_settings_des">Klepněte dvakrát doprostřed pro pozastavení</string>
<string name="use_system_brightness_settings">Použít systémový jas</string> <string name="use_system_brightness_settings">Použít systémový jas</string>
<string name="use_system_brightness_settings_des">V přehrávači použít systémov překrytí</string> <string name="use_system_brightness_settings_des">V přehrávači použít systémov překrytí</string>
<string name="episode_sync_settings">Aktualizovat postup sledování</string> <string name="episode_sync_settings">Aktualizovat postup sledování</string>
@ -151,7 +152,7 @@
<string name="bug_report_settings_on">Nebude odesílat žádná data</string> <string name="bug_report_settings_on">Nebude odesílat žádná data</string>
<string name="show_fillers_settings">Zobrazit výplňové epizody u anime</string> <string name="show_fillers_settings">Zobrazit výplňové epizody u anime</string>
<string name="updates_settings">Zobrazit aktualizace aplikace</string> <string name="updates_settings">Zobrazit aktualizace aplikace</string>
<string name="updates_settings_des">Při spuštění automaticky zkontrolovat nové aktualizace</string> <string name="updates_settings_des">Při spuštění aplikace automaticky zkontrolovat nové aktualizace.</string>
<string name="uprereleases_settings">Aktualizovat na předběžná vydání</string> <string name="uprereleases_settings">Aktualizovat na předběžná vydání</string>
<string name="uprereleases_settings_des">Kontrolovat aktualizace předběžných vydání, místo normálních plných vydání</string> <string name="uprereleases_settings_des">Kontrolovat aktualizace předběžných vydání, místo normálních plných vydání</string>
<string name="github">GitHub</string> <string name="github">GitHub</string>
@ -211,8 +212,8 @@
<string name="movies_singular">Film</string> <string name="movies_singular">Film</string>
<string name="tv_series_singular">Seriál</string> <string name="tv_series_singular">Seriál</string>
<string name="cartoons_singular">Animovaný</string> <string name="cartoons_singular">Animovaný</string>
<string name="anime_singular">\@string/anime</string> <string name="anime_singular">Anime</string>
<string name="ova_singular">\@string/ova</string> <string name="ova_singular">OVA</string>
<string name="torrent_singular">Torrent</string> <string name="torrent_singular">Torrent</string>
<string name="documentaries_singular">Dokument</string> <string name="documentaries_singular">Dokument</string>
<string name="asian_drama_singular">Asijské drama</string> <string name="asian_drama_singular">Asijské drama</string>
@ -245,7 +246,7 @@
<string name="dont_show_again">Již nezobrazovat</string> <string name="dont_show_again">Již nezobrazovat</string>
<string name="skip_update">Přeskočit tuto aktualizace</string> <string name="skip_update">Přeskočit tuto aktualizace</string>
<string name="update">Aktualizovat</string> <string name="update">Aktualizovat</string>
<string name="watch_quality_pref">Upřednostněná kvalita sledování</string> <string name="watch_quality_pref">Upřednostněná kvalita sledování (WiFi)</string>
<string name="limit_title">Maximální počet znaků v názvu přehrávače</string> <string name="limit_title">Maximální počet znaků v názvu přehrávače</string>
<string name="limit_title_rez">Rozlišení přehrávače</string> <string name="limit_title_rez">Rozlišení přehrávače</string>
<string name="video_buffer_size_settings">Velikost vyrovnávací paměti videa</string> <string name="video_buffer_size_settings">Velikost vyrovnávací paměti videa</string>
@ -266,7 +267,7 @@
<string name="legal_notice_text" translatable="false">Jakékoli právní otázky týkající se obsahu této aplikace je třeba řešit se samotnými hostiteli a poskytovateli souborů, protože s nimi nejsme nijak spojeni. V případě porušení autorských práv se obraťte přímo na odpovědné strany nebo na webové stránky, na kterých se streamování odehrává. Aplikace je určena výhradně pro vzdělávací a osobní účely. CloudStream 3 v aplikaci nehostuje žádný obsah a nemá žádnou kontrolu nad tím, jaká média jsou v aplikaci umístěna nebo odstraněna. CloudStream 3 funguje jako jakýkoli jiný vyhledávač, například Google. Služba CloudStream 3 nehostuje, nenahrává ani nespravuje žádná videa, filmy ani obsah. Pouze vyhledává, agreguje a zobrazuje odkazy v pohodlném, uživatelsky přívětivém rozhraní. Pouze shromažďuje webové stránky třetích stran, které jsou veřejně přístupné prostřednictvím jakéhokoli běžného webového prohlížeče. Je odpovědností uživatele, aby se vyvaroval jakýchkoli akcí, které by mohly porušovat zákony platné v jeho lokalitě. Použijte CloudStream 3 na vlastní nebezpečí.</string> <string name="legal_notice_text" translatable="false">Jakékoli právní otázky týkající se obsahu této aplikace je třeba řešit se samotnými hostiteli a poskytovateli souborů, protože s nimi nejsme nijak spojeni. V případě porušení autorských práv se obraťte přímo na odpovědné strany nebo na webové stránky, na kterých se streamování odehrává. Aplikace je určena výhradně pro vzdělávací a osobní účely. CloudStream 3 v aplikaci nehostuje žádný obsah a nemá žádnou kontrolu nad tím, jaká média jsou v aplikaci umístěna nebo odstraněna. CloudStream 3 funguje jako jakýkoli jiný vyhledávač, například Google. Služba CloudStream 3 nehostuje, nenahrává ani nespravuje žádná videa, filmy ani obsah. Pouze vyhledává, agreguje a zobrazuje odkazy v pohodlném, uživatelsky přívětivém rozhraní. Pouze shromažďuje webové stránky třetích stran, které jsou veřejně přístupné prostřednictvím jakéhokoli běžného webového prohlížeče. Je odpovědností uživatele, aby se vyvaroval jakýchkoli akcí, které by mohly porušovat zákony platné v jeho lokalitě. Použijte CloudStream 3 na vlastní nebezpečí.</string>
<string name="category_general">Obecné</string> <string name="category_general">Obecné</string>
<string name="random_button_settings">Náhodné tlačítko</string> <string name="random_button_settings">Náhodné tlačítko</string>
<string name="random_button_settings_desc">Zobrazit na domovské stránce náhodné tlačítko</string> <string name="random_button_settings_desc">Zobrazit na domovské stránce tlačítko, kterým lze vybrat náhodný film nebo seriál z domovské stránky</string>
<string name="provider_lang_settings">Jazyk poskytovatelů</string> <string name="provider_lang_settings">Jazyk poskytovatelů</string>
<string name="app_layout">Rozložení aplikace</string> <string name="app_layout">Rozložení aplikace</string>
<string name="preferred_media_settings">Preferovaná média</string> <string name="preferred_media_settings">Preferovaná média</string>
@ -530,4 +531,27 @@
<string name="delete_repository">Odstranit repozitář</string> <string name="delete_repository">Odstranit repozitář</string>
<string name="plugins_not_downloaded" formatted="true">Nestaženo: %d</string> <string name="plugins_not_downloaded" formatted="true">Nestaženo: %d</string>
<string name="no">Ne</string> <string name="no">Ne</string>
</resources> <string name="android_tv_interface_off_seek_settings">Skrytý přehrávač - doba hledání</string>
<string name="android_tv_interface_off_seek_settings_summary">Množství vyhledávané doby při skrytém přehrávači</string>
<string name="android_tv_interface_on_seek_settings">Zobrazený přehrávač - doba hledání</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="android_tv_interface_on_seek_settings_summary">Množství vyhledávané doby při zobrazeném přehrávači</string>
<string name="test_log">Protokol</string>
<string name="category_provider_test">Test poskytovatele</string>
<string name="test_failed">Neúspěšné</string>
<string name="test_passed">Úspěšné</string>
<string name="restart">Restart</string>
<string name="start">Spustit</string>
<string name="stop">Zastavit</string>
<string name="subscription_in_progress_notification">Aktualizace odebíraných pořadů</string>
<string name="subscription_new">Přihlášeno k odběru %s</string>
<string name="subscription_deleted">Odhlášen odběr od %s</string>
<string name="subscription_episode_released">Byla vydána epizoda %d!</string>
<string name="subscription_list_name">Odebíráno</string>
<string name="jsdelivr_proxy">Proxy raw.githubusercontent.com</string>
<string name="jsdelivr_enabled">Nepodařilo se připojit ke GitHubu, povolování proxy jsdelivr.</string>
<string name="watch_quality_pref_data">Upřednostněná kvalita sledování (mobilní data)</string>
<string name="revert">Vrátit zpět</string>
<string name="jsdelivr_proxy_summary">Pomocí jsdelivr lze obejít blokování GitHubu. Může dojít ke zpoždění aktualizací o několik dní.</string>
<string name="pref_category_bypass">Obcházení ISP</string>
</resources>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string> <string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
<string name="cast_format" formatted="true">Besetzung: %s</string> <string name="cast_format" formatted="true">Besetzung: %s</string>
@ -41,7 +41,7 @@
<string name="search_hint_site" formatted="true">Suche %s…</string> <string name="search_hint_site" formatted="true">Suche %s…</string>
<string name="no_data">Keine Daten vorhanden</string> <string name="no_data">Keine Daten vorhanden</string>
<string name="episode_more_options_des">Mehr Optionen</string> <string name="episode_more_options_des">Mehr Optionen</string>
<string name="next_episode">Nächste Epsisode</string> <string name="next_episode">Nächste Episode</string>
<string name="result_tags">Genres</string> <string name="result_tags">Genres</string>
<string name="result_share">Teilen</string> <string name="result_share">Teilen</string>
<string name="result_open_in_browser">In Browser öffnen</string> <string name="result_open_in_browser">In Browser öffnen</string>
@ -53,7 +53,7 @@
<string name="type_dropped">Abgebrochen</string> <string name="type_dropped">Abgebrochen</string>
<string name="type_plan_to_watch">Geplant</string> <string name="type_plan_to_watch">Geplant</string>
<string name="type_none">Nichts</string> <string name="type_none">Nichts</string>
<string name="type_re_watching">Erneut anschauen</string> <string name="type_re_watching">Erneut schauen</string>
<string name="play_movie_button">Film abspielen</string> <string name="play_movie_button">Film abspielen</string>
<string name="play_livestream_button">Livestream abspielen</string> <string name="play_livestream_button">Livestream abspielen</string>
<string name="play_torrent_button">Torrent streamen</string> <string name="play_torrent_button">Torrent streamen</string>
@ -115,7 +115,7 @@
<string name="continue_watching">Weiterschauen</string> <string name="continue_watching">Weiterschauen</string>
<string name="action_remove_watching">Entfernen</string> <string name="action_remove_watching">Entfernen</string>
<string name="action_open_watching">Mehr Infos</string> <string name="action_open_watching">Mehr Infos</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Damit dieser Anbieter korrekt funktioniert, ist möglicherweise ein VPN erforderlich</string> <string name="vpn_might_be_needed">Damit dieser Anbieter korrekt funktioniert, ist möglicherweise ein VPN erforderlich</string>
<string name="vpn_torrent">Dieser Anbieter bietet Torrents an, ein VPN wird dringend empfohlen</string> <string name="vpn_torrent">Dieser Anbieter bietet Torrents an, ein VPN wird dringend empfohlen</string>
<string name="provider_info_meta">Metadaten werden nicht von der Website bereitgestellt, das Laden des Videos schlägt fehl, wenn sie auf der Website nicht vorhanden sind.</string> <string name="provider_info_meta">Metadaten werden nicht von der Website bereitgestellt, das Laden des Videos schlägt fehl, wenn sie auf der Website nicht vorhanden sind.</string>
@ -136,14 +136,14 @@
<string name="swipe_to_seek_settings">Wischen zum vor- und zurückspulen</string> <string name="swipe_to_seek_settings">Wischen zum vor- und zurückspulen</string>
<string name="swipe_to_seek_settings_des">Nach links oder rechts wischen, um die Zeit im Videoplayer zu steuern</string> <string name="swipe_to_seek_settings_des">Nach links oder rechts wischen, um die Zeit im Videoplayer zu steuern</string>
<string name="swipe_to_change_settings">Wischen, um Einstellungen zu ändern</string> <string name="swipe_to_change_settings">Wischen, um Einstellungen zu ändern</string>
<string name="swipe_to_change_settings_des">Links oder rechts wischen, um die Helligkeit oder Lautstärke zu ändern</string> <string name="swipe_to_change_settings_des">Links oder rechts nach oben oder unten wischen, um die Helligkeit oder Lautstärke zu ändern</string>
<string name="autoplay_next_settings">Nächste Episode automatisch abspielen</string> <string name="autoplay_next_settings">Nächste Episode automatisch abspielen</string>
<string name="autoplay_next_settings_des">Nächste Episode wird gestartet, sobald die aktuelle Episode endet</string> <string name="autoplay_next_settings_des">Nächste Episode wird gestartet, sobald die aktuelle Episode endet</string>
<string name="double_tap_to_seek_settings">Doppeltippen zum vor- und zurückspulen</string> <string name="double_tap_to_seek_settings">Doppeltippen zum vor- und zurückspulen</string>
<string name="double_tap_to_pause_settings">Doppeltippen zum Pausieren</string> <string name="double_tap_to_pause_settings">Doppeltippen zum Pausieren</string>
<string name="double_tap_to_seek_amount_settings">Zeit für vor- und zurückspulen im Player</string> <string name="double_tap_to_seek_amount_settings">Zeit für vor- und zurückspulen im Player (Sekunden)</string>
<string name="double_tap_to_seek_settings_des">Zweimal auf die rechte oder linke Seite tippen, um vor- oder zurückzuspulen</string> <string name="double_tap_to_seek_settings_des">Zweimal auf die rechte oder linke Seite tippen, um vor- oder zurückzuspulen</string>
<string name="double_tap_to_pause_settings_des">In die Mitte tippen, um zu pausieren</string> <string name="double_tap_to_pause_settings_des">Doppelt in die Mitte tippen, um zu pausieren</string>
<string name="use_system_brightness_settings">Systemhelligkeit verwenden</string> <string name="use_system_brightness_settings">Systemhelligkeit verwenden</string>
<string name="use_system_brightness_settings_des">Systemhelligkeit anstelle eines dunklen Overlay im Player verwenden</string> <string name="use_system_brightness_settings_des">Systemhelligkeit anstelle eines dunklen Overlay im Player verwenden</string>
<string name="episode_sync_settings">Episodenfortschritt aktualisieren</string> <string name="episode_sync_settings">Episodenfortschritt aktualisieren</string>
@ -166,7 +166,7 @@
<string name="pref_filter_search_quality">Ausgewählte Videoqualität bei Suchergebnissen ausblenden</string> <string name="pref_filter_search_quality">Ausgewählte Videoqualität bei Suchergebnissen ausblenden</string>
<string name="automatic_plugin_updates">Automatische Plugin-Updates</string> <string name="automatic_plugin_updates">Automatische Plugin-Updates</string>
<string name="updates_settings">App-Updates anzeigen</string> <string name="updates_settings">App-Updates anzeigen</string>
<string name="updates_settings_des">Automatisches Suchen nach neuen Updates beim Start</string> <string name="updates_settings_des">Automatisches Suchen nach neuen Updates nach dem Start</string>
<string name="uprereleases_settings">Auf Vorabversionen updaten</string> <string name="uprereleases_settings">Auf Vorabversionen updaten</string>
<string name="uprereleases_settings_des">Suche nach Vorabversionen statt nur nach Vollversionen</string> <string name="uprereleases_settings_des">Suche nach Vorabversionen statt nur nach Vollversionen</string>
<string name="github">Github</string> <string name="github">Github</string>
@ -212,7 +212,7 @@
<string name="no_subtitles">Keine Untertitel</string> <string name="no_subtitles">Keine Untertitel</string>
<string name="default_subtitles">Standard</string> <string name="default_subtitles">Standard</string>
<string name="free_storage">Frei</string> <string name="free_storage">Frei</string>
<string name="used_storage">Benutzt</string> <string name="used_storage">Belegt</string>
<string name="app_storage">App</string> <string name="app_storage">App</string>
<string name="movies">Filme</string> <string name="movies">Filme</string>
<string name="tv_series">TV-Serien</string> <string name="tv_series">TV-Serien</string>
@ -246,7 +246,7 @@
<string name="episode_action_play_in_browser">In Browser wiedergeben</string> <string name="episode_action_play_in_browser">In Browser wiedergeben</string>
<string name="episode_action_copy_link">Link kopieren</string> <string name="episode_action_copy_link">Link kopieren</string>
<string name="episode_action_auto_download">Auto-Download</string> <string name="episode_action_auto_download">Auto-Download</string>
<string name="episode_action_download_mirror">Download-Mirror</string> <string name="episode_action_download_mirror">Alternativer Download</string>
<string name="episode_action_reload_links">Links neu laden</string> <string name="episode_action_reload_links">Links neu laden</string>
<string name="episode_action_download_subtitle">Untertitel herunterladen</string> <string name="episode_action_download_subtitle">Untertitel herunterladen</string>
<string name="show_hd">Qualitätsanzeige</string> <string name="show_hd">Qualitätsanzeige</string>
@ -263,7 +263,7 @@
<string name="dont_show_again">Nicht mehr anzeigen</string> <string name="dont_show_again">Nicht mehr anzeigen</string>
<string name="skip_update">Update ignorieren</string> <string name="skip_update">Update ignorieren</string>
<string name="update">Update</string> <string name="update">Update</string>
<string name="watch_quality_pref">Bevorzugte Auflösung</string> <string name="watch_quality_pref">Bevorzugte Videoqualität (WLAN)</string>
<string name="limit_title">Videoplayertitel max. Zeichen</string> <string name="limit_title">Videoplayertitel max. Zeichen</string>
<string name="limit_title_rez">Videoplayer Auflösung</string> <string name="limit_title_rez">Videoplayer Auflösung</string>
<string name="video_buffer_size_settings">Videopuffergröße</string> <string name="video_buffer_size_settings">Videopuffergröße</string>
@ -284,9 +284,9 @@
<string name="resize_fill">Strecken</string> <string name="resize_fill">Strecken</string>
<string name="resize_zoom">Vergrößern</string> <string name="resize_zoom">Vergrößern</string>
<string name="legal_notice">Haftungsausschluss</string> <string name="legal_notice">Haftungsausschluss</string>
<string name="category_general">General</string> <string name="category_general">Allgemein</string>
<string name="random_button_settings">Zufalls-Button</string> <string name="random_button_settings">Zufalls-Button</string>
<string name="random_button_settings_desc">Zufallsbutton auf der Startseite anzeigen</string> <string name="random_button_settings_desc">Zeigt einen Zufallsbutton auf der Startseite an, mit welchem eine Serie oder ein Film von der Website zufällig ausgewählt wird</string>
<string name="provider_lang_settings">Anbieter-Sprachen</string> <string name="provider_lang_settings">Anbieter-Sprachen</string>
<string name="app_layout">App-Layout</string> <string name="app_layout">App-Layout</string>
<string name="preferred_media_settings">Bevorzugte Medien</string> <string name="preferred_media_settings">Bevorzugte Medien</string>
@ -460,11 +460,11 @@
<string name="automatic_plugin_download_summary">Automatische Installation aller noch nicht installierten Plugins aus hinzugefügten Repositories.</string> <string name="automatic_plugin_download_summary">Automatische Installation aller noch nicht installierten Plugins aus hinzugefügten Repositories.</string>
<string name="redo_setup_process">Einrichtungsvorgang wiederholen</string> <string name="redo_setup_process">Einrichtungsvorgang wiederholen</string>
<string name="apk_installer_settings">APK-Installer</string> <string name="apk_installer_settings">APK-Installer</string>
<string name="apk_installer_settings_des">Einige Telefone unterstützen das neue Installationsprogramm für Pakete nicht. Benutze die Legacy-Option, wenn sich die Updates nicht installieren lassen.</string> <string name="apk_installer_settings_des">Einige Telefone unterstützen den neuen Package-Installer nicht. Benutze die Legacy-Option, wenn sich die Updates nicht installieren lassen.</string>
<string name="season_format">%s %d%s</string> <string name="season_format">%s %d%s</string>
<string name="pref_category_links">Links</string> <string name="pref_category_links">Links</string>
<string name="pref_category_app_updates">App-Updates</string> <string name="pref_category_app_updates">App-Updates</string>
<string name="pref_category_backup">Back-Up</string> <string name="pref_category_backup">Sicherung</string>
<string name="pref_category_extensions">Erweiterungen</string> <string name="pref_category_extensions">Erweiterungen</string>
<string name="pref_category_actions">Wartung</string> <string name="pref_category_actions">Wartung</string>
<string name="pref_category_cache">Cache</string> <string name="pref_category_cache">Cache</string>
@ -506,4 +506,27 @@
<string name="empty_library_logged_in_message">Diese Liste scheint leer zu sein. Versuche, zu einer anderen Liste zu wechseln.</string> <string name="empty_library_logged_in_message">Diese Liste scheint leer zu sein. Versuche, zu einer anderen Liste zu wechseln.</string>
<string name="safe_mode_file">Datei für abgesicherten Modus gefunden! <string name="safe_mode_file">Datei für abgesicherten Modus gefunden!
\nBeim Start werden keine Erweiterungen geladen, bis die Datei entfernt wird.</string> \nBeim Start werden keine Erweiterungen geladen, bis die Datei entfernt wird.</string>
</resources> <string name="android_tv_interface_off_seek_settings">Player ausgeblendet - Betrag zum vor- und zurückspulen</string>
<string name="android_tv_interface_on_seek_settings_summary">Der Betrag, welcher verwendet wird, wenn der Player eingeblendet ist</string>
<string name="android_tv_interface_off_seek_settings_summary">Der Betrag, welcher verwendet wird, wenn der Player ausgeblendet ist</string>
<string name="pref_category_android_tv">Android-TV</string>
<string name="android_tv_interface_on_seek_settings">Player eingeblendet - Betrag zum vor- und zurückspulen</string>
<string name="test_failed">Fehlgeschlagen</string>
<string name="test_passed">Erfolgreich</string>
<string name="category_provider_test">Anbieter-Test</string>
<string name="stop">Stopp</string>
<string name="test_log">Log</string>
<string name="start">Start</string>
<string name="restart">Neustarten</string>
<string name="watch_quality_pref_data">Bevorzugte Videoqualität (mobile Daten)</string>
<string name="jsdelivr_proxy_summary">Umgehung der GitHub Sperre mit jsdelivr. Kann zu einigen Tagen Verzögerung bei Updates führen.</string>
<string name="subscription_new">%s abonniert</string>
<string name="subscription_deleted">%s deabonniert</string>
<string name="subscription_episode_released">Episode %d erschienen!</string>
<string name="jsdelivr_proxy">raw.githubusercontent.com Proxy</string>
<string name="jsdelivr_enabled">GitHub kann nicht erreicht werden, der jsdelivr-Proxy wird aktiviert.</string>
<string name="subscription_in_progress_notification">Abonnierte Serien werden aktualisiert</string>
<string name="revert">Rückgängig</string>
<string name="subscription_list_name">Abonniert</string>
<string name="pref_category_bypass">ISP-Umgehungen</string>
</resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<string name="app_name">CloudStream</string> <string name="app_name">CloudStream</string>
<string name="title_home">Αρχική</string> <string name="title_home">Αρχική</string>
<string name="title_search">Αναζήτηση</string> <string name="title_search">Αναζήτηση</string>
@ -150,7 +151,7 @@
<string name="episodes">Επεισόδια</string> <string name="episodes">Επεισόδια</string>
<string name="episodes_range">%d-%d</string> <string name="episodes_range">%d-%d</string>
<string name="episode_format" formatted="true">%d %s</string> <string name="episode_format" formatted="true">%d %s</string>
<string name="season_short">Κ</string> <string name="season_short">Σ</string>
<string name="episode_short">E</string> <string name="episode_short">E</string>
<string name="no_episodes_found">Δεν βρέθηκαν επεισόδια</string> <string name="no_episodes_found">Δεν βρέθηκαν επεισόδια</string>
<string name="delete_file">Διαγραφή αρχείου</string> <string name="delete_file">Διαγραφή αρχείου</string>
@ -387,7 +388,7 @@
<string name="sort_close">Κλείσιμο</string> <string name="sort_close">Κλείσιμο</string>
<string name="sort_clear">Εκκαθάριση</string> <string name="sort_clear">Εκκαθάριση</string>
<string name="subs_subtitle_languages">Γλώσσα υποτίτλων</string> <string name="subs_subtitle_languages">Γλώσσα υποτίτλων</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="provider_info_meta">Δεν έχουν παρασχεθεί μεταδεδομένα από τον ιστότοπο, η φόρτωση του βίντεο θα αποτύχει αν δεν υπάρχει στον ιστότοπο.</string> <string name="provider_info_meta">Δεν έχουν παρασχεθεί μεταδεδομένα από τον ιστότοπο, η φόρτωση του βίντεο θα αποτύχει αν δεν υπάρχει στον ιστότοπο.</string>
<string name="double_tap_to_pause_settings">Διπλό πάτημα για παύση</string> <string name="double_tap_to_pause_settings">Διπλό πάτημα για παύση</string>
<string name="double_tap_to_seek_amount_settings">Μέγεθος αναζήτησης στο πρόγραμμα αναπαραγωγής</string> <string name="double_tap_to_seek_amount_settings">Μέγεθος αναζήτησης στο πρόγραμμα αναπαραγωγής</string>
@ -452,7 +453,7 @@
<string name="skip_type_mixed_ed">Ανάμεικτοι τίτλοι τέλους</string> <string name="skip_type_mixed_ed">Ανάμεικτοι τίτλοι τέλους</string>
<string name="go_back_30">-30</string> <string name="go_back_30">-30</string>
<string name="rating">Κριτική</string> <string name="rating">Κριτική</string>
<string name="ova_singular">\@string/ova</string> <string name="ova_singular">@string/ova</string>
<string name="pref_category_app_updates">Ενημερώσεις εφαρμογής</string> <string name="pref_category_app_updates">Ενημερώσεις εφαρμογής</string>
<string name="pref_category_backup">Αντίγραφο ασφαλείας</string> <string name="pref_category_backup">Αντίγραφο ασφαλείας</string>
<string name="pref_category_extensions">Extensions</string> <string name="pref_category_extensions">Extensions</string>
@ -464,7 +465,7 @@
<string name="pref_category_defaults">Προεπιλεγμένα</string> <string name="pref_category_defaults">Προεπιλεγμένα</string>
<string name="login_format" formatted="true">%s %s</string> <string name="login_format" formatted="true">%s %s</string>
<string name="subs_font_size">Μέγεθος γραμματοσειράς</string> <string name="subs_font_size">Μέγεθος γραμματοσειράς</string>
<string name="anime_singular">\@string/anime</string> <string name="anime_singular">@string/anime</string>
<string name="pref_category_links">Σύνδεσμοι</string> <string name="pref_category_links">Σύνδεσμοι</string>
<string name="pref_category_looks">Εμφάνιση</string> <string name="pref_category_looks">Εμφάνιση</string>
<string name="pref_category_ui_features">Χαρακτηριστικά</string> <string name="pref_category_ui_features">Χαρακτηριστικά</string>
@ -508,4 +509,4 @@
\nΣυνδέσου σε έναν λογαριασμό που έχει βιβλιοθήκη, ή πρόσθεσε σειρές στην τοπική βιβλιοθήκη σου</string> \nΣυνδέσου σε έναν λογαριασμό που έχει βιβλιοθήκη, ή πρόσθεσε σειρές στην τοπική βιβλιοθήκη σου</string>
<string name="safe_mode_file">Βρέθηκε αρχείο Ασφαλούς Λειτουργίας! <string name="safe_mode_file">Βρέθηκε αρχείο Ασφαλούς Λειτουργίας!
\nΔεν πρόκειται να φορτωθούν extensions κατά το ξεκίνημα μέχρι να διαγραφεί το αρχείο.</string> \nΔεν πρόκειται να φορτωθούν extensions κατά το ξεκίνημα μέχρι να διαγραφεί το αρχείο.</string>
</resources> </resources>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<string name="go_back_img_des">Reen</string> <string name="go_back_img_des">Reen</string>
<string name="title_home">Hejmo</string> <string name="title_home">Hejmo</string>
@ -78,4 +78,4 @@
<string name="player_speed_text_format" formatted="true">Rapido (%.2fx)</string> <string name="player_speed_text_format" formatted="true">Rapido (%.2fx)</string>
<string name="search_hint">Serĉi…</string> <string name="search_hint">Serĉi…</string>
<string name="download">Elŝuti</string> <string name="download">Elŝuti</string>
</resources> </resources>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<string name="pref_category_extensions">Extensiones</string> <string name="pref_category_extensions">Extensiones</string>
<string name="setup_extensions_subtext">Descargue la lista de sitios que quiera utilizar</string> <string name="setup_extensions_subtext">Descargue la lista de sitios que quiera utilizar</string>
@ -24,7 +24,7 @@
<string name="pref_filter_search_quality">Ocultar la calidad de video en los resultados de búsqueda</string> <string name="pref_filter_search_quality">Ocultar la calidad de video en los resultados de búsqueda</string>
<string name="pref_category_player_layout">Diseño</string> <string name="pref_category_player_layout">Diseño</string>
<string name="category_ui">Diseño</string> <string name="category_ui">Diseño</string>
<string name="watch_quality_pref">Calidad de visualización preferida</string> <string name="watch_quality_pref">Calidad de visualización preferida (WiFi)</string>
<string name="player_pref">Reproductor de video preferido</string> <string name="player_pref">Reproductor de video preferido</string>
<string name="emulator_layout">Diseño para emulador</string> <string name="emulator_layout">Diseño para emulador</string>
<string name="app_layout">Diseño de la aplicación</string> <string name="app_layout">Diseño de la aplicación</string>
@ -51,10 +51,10 @@
<string name="subtitles_raised">Elevado</string> <string name="subtitles_raised">Elevado</string>
<string name="subtitle_offset_extra_hint_later_format">Use esto si los subtítulos se muestran %d ms muy pronto</string> <string name="subtitle_offset_extra_hint_later_format">Use esto si los subtítulos se muestran %d ms muy pronto</string>
<string name="subtitle_offset_extra_hint_before_format">Use esto si los subtítulos se muestran %d ms tarde</string> <string name="subtitle_offset_extra_hint_before_format">Use esto si los subtítulos se muestran %d ms tarde</string>
<string name="swipe_to_seek_settings_des">Desliza el dedo hacia la izquierda o hacia la derecha para controlar el tiempo en el reproductor de video</string> <string name="swipe_to_seek_settings_des">Desliza el dedo de lado a lado para controlar la posición en un video</string>
<string name="subtitles_filter_lang">Filtrar por idioma de medios preferido</string> <string name="subtitles_filter_lang">Filtrar por idioma de medios preferido</string>
<string name="subtitles_remove_captions">Eliminar Closed Captions (CC) de los subtítulos</string> <string name="subtitles_remove_captions">Eliminar Closed Captions (CC) de los subtítulos</string>
<string name="double_tap_to_seek_amount_settings">Cantidad de tiempo de búsqueda en el reproductor (en segundos)</string> <string name="double_tap_to_seek_amount_settings">Cantidad de búsquedas del reproductor (segundos)</string>
<string name="use_system_brightness_settings_des">Use el brillo del sistema en el reproductor de la app en lugar de una superposición oscura</string> <string name="use_system_brightness_settings_des">Use el brillo del sistema en el reproductor de la app en lugar de una superposición oscura</string>
<string name="limit_title_rez">Resolución del reproductor de video</string> <string name="limit_title_rez">Resolución del reproductor de video</string>
<string name="player_settings_play_in_mpv">MPV</string> <string name="player_settings_play_in_mpv">MPV</string>
@ -194,7 +194,7 @@
<string name="continue_watching">Continuar Viendo</string> <string name="continue_watching">Continuar Viendo</string>
<string name="action_remove_watching">Remover</string> <string name="action_remove_watching">Remover</string>
<string name="action_open_watching">Más info</string> <string name="action_open_watching">Más info</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Una VPN puede ser necesaria para que este proveedor funcione correctamente</string> <string name="vpn_might_be_needed">Una VPN puede ser necesaria para que este proveedor funcione correctamente</string>
<string name="vpn_torrent">Este proveedor es un torrent, se recomienda una VPN</string> <string name="vpn_torrent">Este proveedor es un torrent, se recomienda una VPN</string>
<string name="provider_info_meta">El sitio no proporciona los metadatos, la carga del video fallará si no existe en el sitio.</string> <string name="provider_info_meta">El sitio no proporciona los metadatos, la carga del video fallará si no existe en el sitio.</string>
@ -205,16 +205,16 @@
<string name="eigengraumode_settings">Modo Eigengravy</string> <string name="eigengraumode_settings">Modo Eigengravy</string>
<string name="swipe_to_seek_settings">Deslice para avanzar/retroceder</string> <string name="swipe_to_seek_settings">Deslice para avanzar/retroceder</string>
<string name="swipe_to_change_settings">Deslice para cambiar la configuración</string> <string name="swipe_to_change_settings">Deslice para cambiar la configuración</string>
<string name="swipe_to_change_settings_des">Deslice el dedo hacia la izquierda o hacia la derecha para cambiar el brillo o el volumen</string> <string name="swipe_to_change_settings_des">Deslice hacia arriba o hacia abajo en el lado izquierdo o derecho para cambiar el brillo o el volumen</string>
<string name="double_tap_to_seek_settings">Toca dos veces para buscar</string> <string name="double_tap_to_seek_settings">Toca dos veces para buscar</string>
<string name="double_tap_to_pause_settings">Tocar dos veces para pausar</string> <string name="double_tap_to_pause_settings">Tocar dos veces para pausar</string>
<string name="double_tap_to_seek_settings_des">Toque dos veces en el lado derecho o izquierdo para buscar hacia adelante o hacia atrás</string> <string name="double_tap_to_seek_settings_des">Toque dos veces en el lado derecho o izquierdo para buscar hacia adelante o hacia atrás</string>
<string name="double_tap_to_pause_settings_des">Toque en el medio para pausar</string> <string name="double_tap_to_pause_settings_des">Toque dos veces en el medio para hacer una pausa</string>
<string name="use_system_brightness_settings">Usar brillo del sistema</string> <string name="use_system_brightness_settings">Usar brillo del sistema</string>
<string name="restore_settings">Restaurar datos desde el backup</string> <string name="restore_settings">Restaurar datos desde el backup</string>
<string name="backup_settings">Hacer copia de los datos (backup)</string> <string name="backup_settings">Hacer copia de los datos (backup)</string>
<string name="restore_success">Archivo de backup cargado</string> <string name="restore_success">Archivo de backup cargado</string>
<string name="updates_settings_des">Buscar automáticamente nuevas actualizaciones al inicio</string> <string name="updates_settings_des">Busque automáticamente nuevas actualizaciones después de iniciar la aplicación.</string>
<string name="redo_setup_process">Rehacer el proceso de configuración inicial</string> <string name="redo_setup_process">Rehacer el proceso de configuración inicial</string>
<string name="show_fillers_settings">Mostrar episodio de relleno para Anime</string> <string name="show_fillers_settings">Mostrar episodio de relleno para Anime</string>
<string name="play_episode_toast">Reproducir Episodio</string> <string name="play_episode_toast">Reproducir Episodio</string>
@ -306,7 +306,7 @@
<string name="pref_category_looks">Aspecto</string> <string name="pref_category_looks">Aspecto</string>
<string name="pref_category_ui_features">Características</string> <string name="pref_category_ui_features">Características</string>
<string name="random_button_settings">Botón de Al azar</string> <string name="random_button_settings">Botón de Al azar</string>
<string name="random_button_settings_desc">Muestra un botón de reproducción \"al azar\" en la página de inicio</string> <string name="random_button_settings_desc">Muestra un botón de reproducción \"al azar\" en la página de inicio para poelículas y series</string>
<string name="account">cuenta</string> <string name="account">cuenta</string>
<string name="logout">Cerrar sesión</string> <string name="logout">Cerrar sesión</string>
<string name="switch_account">Cambiar cuenta</string> <string name="switch_account">Cambiar cuenta</string>
@ -363,8 +363,8 @@
<string name="movies_singular">Película</string> <string name="movies_singular">Película</string>
<string name="tv_series_singular">Serie</string> <string name="tv_series_singular">Serie</string>
<string name="cartoons_singular">Dibujo animado</string> <string name="cartoons_singular">Dibujo animado</string>
<string name="anime_singular">\@string/anime</string> <string name="anime_singular">Anime</string>
<string name="ova_singular">\@string/ova</string> <string name="ova_singular">OVA</string>
<string name="torrent_singular">Torrent</string> <string name="torrent_singular">Torrent</string>
<string name="documentaries_singular">Documental</string> <string name="documentaries_singular">Documental</string>
<string name="asian_drama_singular">Drama asiático</string> <string name="asian_drama_singular">Drama asiático</string>
@ -506,8 +506,27 @@
\nInicia sesión en una cuenta de biblioteca o añade series desde tu biblioteca local</string> \nInicia sesión en una cuenta de biblioteca o añade series desde tu biblioteca local</string>
<string name="safe_mode_file">¡Se encontró un archivo en modo seguro! <string name="safe_mode_file">¡Se encontró un archivo en modo seguro!
\nNo cargar ninguna extensión al inicio hasta que se elimine el archivo.</string> \nNo cargar ninguna extensión al inicio hasta que se elimine el archivo.</string>
<string name="android_tv_interface_on_seek_settings">Jugadora mostrada - buscar cantidad</string> <string name="android_tv_interface_on_seek_settings">Reproductor visible - buscar cantidad</string>
<string name="android_tv_interface_off_seek_settings">Jugadora oculta - buscar cantidad</string> <string name="android_tv_interface_off_seek_settings">Reproductor oculto - buscar cantidad</string>
<string name="pref_category_android_tv">Android TV</string> <string name="pref_category_android_tv">Android TV</string>
<string name="android_tv_interface_on_seek_settings_summary">La cantidad de búsqueda utilizada cuando la jugadora es visible.</string> <string name="android_tv_interface_on_seek_settings_summary">Tiempo de búsqueda usado (en segundos) cuando el reproductor está visible</string>
</resources> <string name="android_tv_interface_off_seek_settings_summary">Tiempo de búsqueda usado (en segundos) cuando el reproductor está oculto</string>
<string name="stop">Parar</string>
<string name="test_failed">Falló</string>
<string name="test_log">Registro</string>
<string name="start">Empezar</string>
<string name="test_passed">Aprobado</string>
<string name="category_provider_test">Prueba del proveedor</string>
<string name="restart">Reiniciar</string>
<string name="subscription_list_name">Suscrito</string>
<string name="subscription_new">Suscrito a %s</string>
<string name="subscription_deleted">Darse de baja de %s</string>
<string name="subscription_in_progress_notification">Actualizando los programas suscritos</string>
<string name="subscription_episode_released">¡Episodio %d publicado!</string>
<string name="jsdelivr_proxy">Proxy raw.githubusercontent.com</string>
<string name="jsdelivr_enabled">No se ha podido acceder a GitHub, activando el proxy jsdelivr.</string>
<string name="jsdelivr_proxy_summary">Con jsdelivr, se puede omitir el bloqueo de GitHub. Puede retrasar las actualizaciones unos días.</string>
<string name="revert">Revertir</string>
<string name="pref_category_bypass">ISP Bypasses</string>
<string name="watch_quality_pref_data">Calidad de visualización preferida (Datos móviles)</string>
</resources>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<string name="delete">حذف</string> <string name="delete">حذف</string>
<string name="pause">مکث</string> <string name="pause">مکث</string>
@ -23,4 +23,14 @@
<string name="none">هیچ‌کدام</string> <string name="none">هیچ‌کدام</string>
<string name="title">عنوان</string> <string name="title">عنوان</string>
<string name="history">تاریخچه</string> <string name="history">تاریخچه</string>
</resources> <string name="search_poster_img_des">پوستر</string>
<string name="result_poster_img_des">پوستر</string>
<string name="episode_poster_img_des">پوستر قسمت</string>
<string name="next_episode_time_day_format" formatted="true">%dروز %dساعت %dدقیقه</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%s قسمت %d</string>
<string name="cast_format" formatted="true">بازیگران: %s</string>
<string name="next_episode_format" formatted="true">قسمت %d پخش خواهد شد</string>
<string name="next_episode_time_hour_format" formatted="true">%dساعت %dدقیقه</string>
<string name="next_episode_time_min_format" formatted="true">%dدقیقه</string>
<string name="home_main_poster_img_des">پوستر اصلی</string>
</resources>

View file

@ -1,17 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<string name="app_name">CloudStream</string> <string name="app_name">CloudStream</string>
<string name="title_home">Accueil</string> <string name="title_home">Accueil</string>
<string name="title_search">Rechercher</string> <string name="title_search">Rechercher</string>
<string name="title_downloads">Téléchargements</string> <string name="title_downloads">Téléchargements</string>
<string name="title_settings">Paramètres</string> <string name="title_settings">Paramètres</string>
<string name="search_hint">Rechercher…</string> <string name="search_hint">Rechercher…</string>
<string name="search_poster_img_des">Miniature</string> <string name="search_poster_img_des">Affiche</string>
<string name="no_data">Aucune Donnée</string> <string name="no_data">Aucune Donnée</string>
<string name="episode_more_options_des">Plus d\'options</string> <string name="episode_more_options_des">Plus d\'options</string>
<string name="go_back_img_des">Retour</string> <string name="go_back_img_des">Retour</string>
<string name="next_episode">Épisode suivant</string> <string name="next_episode">Épisode suivant</string>
<string name="result_poster_img_des">Miniature</string> <string name="result_poster_img_des">Affiche</string>
<string name="result_tags">Genres</string> <string name="result_tags">Genres</string>
<string name="result_share">Partager</string> <string name="result_share">Partager</string>
<string name="result_open_in_browser">Ouvrir dans le navigateur</string> <string name="result_open_in_browser">Ouvrir dans le navigateur</string>
@ -29,7 +30,7 @@
<string name="pick_subtitle">Sous-titres</string> <string name="pick_subtitle">Sous-titres</string>
<string name="reload_error">Réessayer la connection…</string> <string name="reload_error">Réessayer la connection…</string>
<string name="go_back">Retour</string> <string name="go_back">Retour</string>
<string name="episode_poster_img_des">Miniature de l\'Épisode</string> <string name="episode_poster_img_des">Affiche de l\'épisode</string>
<string name="play_episode">Lire l\'Épisode</string> <string name="play_episode">Lire l\'Épisode</string>
<!--<string name="need_storage">Permet de télécharger les épisodes</string>--> <!--<string name="need_storage">Permet de télécharger les épisodes</string>-->
<string name="download">Télécharger</string> <string name="download">Télécharger</string>
@ -51,10 +52,10 @@
<string name="pref_disable_acra">Désactiver le rapport de bug automatique</string> <string name="pref_disable_acra">Désactiver le rapport de bug automatique</string>
<string name="home_more_info">Plus d\'informations</string> <string name="home_more_info">Plus d\'informations</string>
<string name="home_expanded_hide">Cacher</string> <string name="home_expanded_hide">Cacher</string>
<string name="home_main_poster_img_des">Poster principal</string> <string name="home_main_poster_img_des">Affiche principale</string>
<string name="home_play">Lecture</string> <string name="home_play">Lecture</string>
<string name="home_info">Info</string> <string name="home_info">Infos</string>
<string name="home_next_random_img_des">Suivant Aléatoire</string> <string name="home_next_random_img_des">Aléatoire suivant</string>
<string name="home_change_provider_img_des">Changer le fournisseur</string> <string name="home_change_provider_img_des">Changer le fournisseur</string>
<string name="filter_bookmarks">Filtrer les marques-pages</string> <string name="filter_bookmarks">Filtrer les marques-pages</string>
<string name="error_bookmarks_text">Marque-pages</string> <string name="error_bookmarks_text">Marque-pages</string>
@ -130,7 +131,7 @@
<string name="new_update_format" formatted="true">Nouvelle mise à jour trouvée ! <string name="new_update_format" formatted="true">Nouvelle mise à jour trouvée !
\n%s -&gt; %s</string> \n%s -&gt; %s</string>
<string name="filler" formatted="true">Épisode spécial</string> <string name="filler" formatted="true">Épisode spécial</string>
<string name="watch_quality_pref">Qualité de visionnage préférée</string> <string name="watch_quality_pref">Qualité de visionnage préférée (WiFi)</string>
<string name="video_buffer_size_settings">Taille de la mémoire cache</string> <string name="video_buffer_size_settings">Taille de la mémoire cache</string>
<string name="resize_fill">Étendre</string> <string name="resize_fill">Étendre</string>
<string name="legal_notice">Non-responsabilité</string> <string name="legal_notice">Non-responsabilité</string>
@ -211,7 +212,7 @@
<string name="actor_background">Arrière plan</string> <string name="actor_background">Arrière plan</string>
<string name="home_source">Source</string> <string name="home_source">Source</string>
<string name="home_random">Aléatoire</string> <string name="home_random">Aléatoire</string>
<string name="coming_soon">À venir </string> <string name="coming_soon">Bientôt disponible</string>
<string name="poster_image">Image de l\'affiche</string> <string name="poster_image">Image de l\'affiche</string>
<string name="authenticated_user">%s Connecté</string> <string name="authenticated_user">%s Connecté</string>
<string name="action_add_to_bookmarks">Définir le statut de visionage</string> <string name="action_add_to_bookmarks">Définir le statut de visionage</string>
@ -240,7 +241,7 @@
<string name="continue_watching">Continuer à regarder</string> <string name="continue_watching">Continuer à regarder</string>
<string name="action_remove_watching">Retirer</string> <string name="action_remove_watching">Retirer</string>
<string name="action_open_watching">Plus d\'informations</string> <string name="action_open_watching">Plus d\'informations</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Un VPN peut être nécessaire pour que ce fournisseur fonctionne correctement</string> <string name="vpn_might_be_needed">Un VPN peut être nécessaire pour que ce fournisseur fonctionne correctement</string>
<string name="vpn_torrent">Ce fournisseur est un torrent, un VPN est recommandé</string> <string name="vpn_torrent">Ce fournisseur est un torrent, un VPN est recommandé</string>
<string name="provider_info_meta">Les métadonnées ne sont pas fournies par le site, le chargement de la vidéo échouera si elles n\'existent pas sur le site.</string> <string name="provider_info_meta">Les métadonnées ne sont pas fournies par le site, le chargement de la vidéo échouera si elles n\'existent pas sur le site.</string>
@ -385,8 +386,8 @@
<string name="quality_4k">4K</string> <string name="quality_4k">4K</string>
<string name="quality_webrip">Web</string> <string name="quality_webrip">Web</string>
<string name="go_back_30">-30</string> <string name="go_back_30">-30</string>
<string name="anime_singular">\@string/anime</string> <string name="anime_singular">@string/anime</string>
<string name="ova_singular">\@string/ova</string> <string name="ova_singular">@string/ova</string>
<string name="nsfw_singular">NSFW</string> <string name="nsfw_singular">NSFW</string>
<string name="login_format" formatted="true">%s %s</string> <string name="login_format" formatted="true">%s %s</string>
<string name="subtitles_filter_lang">Filtrez par langue préférée</string> <string name="subtitles_filter_lang">Filtrez par langue préférée</string>
@ -490,4 +491,37 @@
<string name="delayed_update_notice">L\'application sera mise à jour dès la fin de la session</string> <string name="delayed_update_notice">L\'application sera mise à jour dès la fin de la session</string>
<string name="plugin_downloaded">Plugin Téléchargé</string> <string name="plugin_downloaded">Plugin Téléchargé</string>
<string name="action_remove_from_watched">Retirer de la vue</string> <string name="action_remove_from_watched">Retirer de la vue</string>
</resources> <string name="library">Bibliothèque</string>
<string name="browser">Navigateur</string>
<string name="sort">Trier</string>
<string name="sort_rating_asc">Note (basse à haute)</string>
<string name="sort_rating_desc">Note (haut à bas)</string>
<string name="sort_alphabetical_a">Alphabétique (A à Z)</string>
<string name="empty_library_no_accounts_message">On dirait que votre bibliothèque est vide :(
\nConnectez-vous à un compte ou ajoutez des séries à votre bibliothèque locale</string>
<string name="empty_library_logged_in_message">Il semble que cette liste soit vide, essayez d\'en choisir une autre</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="sort_by">Trié par</string>
<string name="sort_alphabetical_z">Alphabétique (Z à A)</string>
<string name="select_library">Sélectionnez la bibliothèque</string>
<string name="open_with">Ouvrir avec</string>
<string name="sort_updated_new">Mis à jour (Nouveau vers ancien)</string>
<string name="sort_updated_old">Mis à jour (ancien vers nouveau)</string>
<string name="safe_mode_file">Fichier du mode sans échec trouvé !
\nAucune extension ne sera chargée au démarrage avant que le fichier ne soit enlevé.</string>
<string name="stop">Arrêter</string>
<string name="revert">Revenir à</string>
<string name="test_log">Enregistrer</string>
<string name="watch_quality_pref_data">Qualité de visionnage préférée (données mobiles)</string>
<string name="subscription_new">Abonné à %s</string>
<string name="start">Démarrer</string>
<string name="category_provider_test">Test des fournisseurs</string>
<string name="test_passed">Réussi</string>
<string name="subscription_deleted">Désabonné de %s</string>
<string name="restart">Redémarrer</string>
<string name="subscription_list_name">Abonné</string>
<string name="jsdelivr_proxy">raw.githubusercontent.com Proxy</string>
<string name="pref_category_bypass">Contournements de FAI</string>
<string name="subscription_episode_released">L\'épisode %d est sorti !</string>
<string name="test_failed">Échouer</string>
</resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- TRANSLATE, BUT DON'T FORGET FORMAT --> <!-- TRANSLATE, BUT DON'T FORGET FORMAT -->
<string name="player_speed_text_format" formatted="true">रफ्तार (%.2fx)</string> <string name="player_speed_text_format" formatted="true">रफ्तार (%.2fx)</string>
<string name="new_update_format" formatted="true">नया अपडेट आया है! <string name="new_update_format" formatted="true">नया अपडेट आया है!
@ -146,4 +147,4 @@
<string name="next_episode_time_hour_format" formatted="true">%dh %dm</string> <string name="next_episode_time_hour_format" formatted="true">%dh %dm</string>
<string name="next_episode_time_min_format" formatted="true">%dm</string> <string name="next_episode_time_min_format" formatted="true">%dm</string>
<string name="result_poster_img_des">विज्ञापन</string> <string name="result_poster_img_des">विज्ञापन</string>
</resources> </resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG --> <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" formatted="true" translatable="false">%d %s | %s</string> <string name="extra_info_format" formatted="true" translatable="false">%d %s | %s</string>
<string name="storage_size_format" formatted="true" translatable="false">%s • %s</string> <string name="storage_size_format" formatted="true" translatable="false">%s • %s</string>
@ -119,7 +120,7 @@
<string name="continue_watching">Nastavite s gledanjem</string> <string name="continue_watching">Nastavite s gledanjem</string>
<string name="action_remove_watching">Makni</string> <string name="action_remove_watching">Makni</string>
<string name="action_open_watching">Više informacija</string> <string name="action_open_watching">Više informacija</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Za ispravan rad ovog pružatelja usluga može biti potreban VPN</string> <string name="vpn_might_be_needed">Za ispravan rad ovog pružatelja usluga može biti potreban VPN</string>
<string name="vpn_torrent">Ovaj pružatelj usluga je torrent, preporučuje se VPN</string> <string name="vpn_torrent">Ovaj pružatelj usluga je torrent, preporučuje se VPN</string>
<string name="provider_info_meta">Stranica ne daje metapodatke, učitavanje videozapisa neće uspjeti ako ne postoji na stranici.</string> <string name="provider_info_meta">Stranica ne daje metapodatke, učitavanje videozapisa neće uspjeti ako ne postoji na stranici.</string>
@ -138,16 +139,16 @@
<string name="eigengraumode_settings">Eigengravy način</string> <string name="eigengraumode_settings">Eigengravy način</string>
<string name="eigengraumode_settings_des">Dodaje opciju brzine u playeru</string> <string name="eigengraumode_settings_des">Dodaje opciju brzine u playeru</string>
<string name="swipe_to_seek_settings">Prijeđi prstom za traženje</string> <string name="swipe_to_seek_settings">Prijeđi prstom za traženje</string>
<string name="swipe_to_seek_settings_des">Prijeđi prstom ulijevo ili udesno za kontrolu vremena u videoplayeru</string> <string name="swipe_to_seek_settings_des">Prijeđite prstom ulijevo ili udesno kako biste kontrolirali player</string>
<string name="swipe_to_change_settings">Klizni za promjenu postavki</string> <string name="swipe_to_change_settings">Klizni za promjenu postavki</string>
<string name="swipe_to_change_settings_des">Prijeđi prstom ulijevo ili udesno za promjenu svjetline ili glasnoće</string> <string name="swipe_to_change_settings_des">Kliznite prstom ulijevo ili udesno za promjenu svjetline ili glasnoće</string>
<string name="autoplay_next_settings">Automatski započni sljedeću epizodu</string> <string name="autoplay_next_settings">Automatski započni sljedeću epizodu</string>
<string name="autoplay_next_settings_des">Započne sljedeću epizodu kad trenutna završi</string> <string name="autoplay_next_settings_des">Započne sljedeću epizodu kad trenutna završi</string>
<string name="double_tap_to_seek_settings">Dodirni dvaput za traženje</string> <string name="double_tap_to_seek_settings">Dodirni dvaput za traženje</string>
<string name="double_tap_to_pause_settings">Dodirni dvaput za pauziranje</string> <string name="double_tap_to_pause_settings">Dodirni dvaput za pauziranje</string>
<string name="double_tap_to_seek_amount_settings">Iznos preskakanja u playeru</string> <string name="double_tap_to_seek_amount_settings">Iznos preskakanja u playeru (Sekunde)</string>
<string name="double_tap_to_seek_settings_des">Dvaput dodirni desnu ili lijevu stranu ekrana za pomicanje naprijed ili natrag</string> <string name="double_tap_to_seek_settings_des">Dvaput dodirni desnu ili lijevu stranu ekrana za pomicanje naprijed ili natrag</string>
<string name="double_tap_to_pause_settings_des">Dodirni u sredinu zaslona za pauziranje</string> <string name="double_tap_to_pause_settings_des">Dodirnite dvaput u sredinu zaslona za pauziranje</string>
<string name="use_system_brightness_settings">Koristi svijetlinu u sustavu</string> <string name="use_system_brightness_settings">Koristi svijetlinu u sustavu</string>
<string name="use_system_brightness_settings_des">Koristi svjetlinu sustava u playeru aplikacija umjesto tamnog preklopa</string> <string name="use_system_brightness_settings_des">Koristi svjetlinu sustava u playeru aplikacija umjesto tamnog preklopa</string>
<string name="episode_sync_settings">Ažuriraj napredak gledanja</string> <string name="episode_sync_settings">Ažuriraj napredak gledanja</string>
@ -173,7 +174,7 @@
<string name="pref_filter_search_quality">Sakrij odabranu kvalitetu videozapisa u rezultatima pretraživanja</string> <string name="pref_filter_search_quality">Sakrij odabranu kvalitetu videozapisa u rezultatima pretraživanja</string>
<string name="automatic_plugin_updates">Automatsko ažuriranje dodataka</string> <string name="automatic_plugin_updates">Automatsko ažuriranje dodataka</string>
<string name="updates_settings">Prikaži ažuriranja aplikacije</string> <string name="updates_settings">Prikaži ažuriranja aplikacije</string>
<string name="updates_settings_des">Automatski traži nova ažuriranja pri pokretanju aplikacije</string> <string name="updates_settings_des">Automatski traži nova ažuriranja nakon pokretanja aplikacije</string>
<string name="uprereleases_settings">Ažuriranje na predizdanja</string> <string name="uprereleases_settings">Ažuriranje na predizdanja</string>
<string name="uprereleases_settings_des">Tražite ažuriranja prije izdanja umjesto samo potpunih izdanja</string> <string name="uprereleases_settings_des">Tražite ažuriranja prije izdanja umjesto samo potpunih izdanja</string>
<string name="github">Github</string> <string name="github">Github</string>
@ -238,8 +239,8 @@
<string name="movies_singular">Film</string> <string name="movies_singular">Film</string>
<string name="tv_series_singular">Serija</string> <string name="tv_series_singular">Serija</string>
<string name="cartoons_singular">Crtić</string> <string name="cartoons_singular">Crtić</string>
<string name="anime_singular">\@string/anime</string> <string name="anime_singular">Anime</string>
<string name="ova_singular">\@string/ova</string> <string name="ova_singular">OVA</string>
<string name="torrent_singular">Torrent</string> <string name="torrent_singular">Torrent</string>
<string name="documentaries_singular">Dokumentarac</string> <string name="documentaries_singular">Dokumentarac</string>
<string name="asian_drama_singular">Azijska drama</string> <string name="asian_drama_singular">Azijska drama</string>
@ -299,7 +300,7 @@
<string name="legal_notice_text" translatable="false">Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.</string> <string name="legal_notice_text" translatable="false">Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.</string>
<string name="category_general">Općenito</string> <string name="category_general">Općenito</string>
<string name="random_button_settings">Random gumb</string> <string name="random_button_settings">Random gumb</string>
<string name="random_button_settings_desc">Prikaži random gumb na početnoj stranici</string> <string name="random_button_settings_desc">Prikazuje gumb na početnoj stranici koji može odabrati nasumični film ili TV seriju s početne stranice</string>
<string name="provider_lang_settings">Jezici pružatelja usluga</string> <string name="provider_lang_settings">Jezici pružatelja usluga</string>
<string name="app_layout">Izgled aplikacije</string> <string name="app_layout">Izgled aplikacije</string>
<string name="preferred_media_settings">Preferirani mediji</string> <string name="preferred_media_settings">Preferirani mediji</string>
@ -531,4 +532,27 @@
<string name="empty_library_logged_in_message">Čini se da je ova lista prazna, pokušajte se prebaciti na drugu</string> <string name="empty_library_logged_in_message">Čini se da je ova lista prazna, pokušajte se prebaciti na drugu</string>
<string name="safe_mode_file">Pronađena datoteka sigurnog načina rada! <string name="safe_mode_file">Pronađena datoteka sigurnog načina rada!
\nNe učitavaju se ekstenzije pri pokretanju dok se datoteka ne ukloni.</string> \nNe učitavaju se ekstenzije pri pokretanju dok se datoteka ne ukloni.</string>
</resources> <string name="android_tv_interface_on_seek_settings">Prikazan player- iznos preskakanja</string>
<string name="android_tv_interface_on_seek_settings_summary">Količina preskakanja koja se koristi kada je player vidljiv</string>
<string name="android_tv_interface_off_seek_settings">Player skriven - Količina preskakanja</string>
<string name="android_tv_interface_off_seek_settings_summary">Količina preskakanja koja se koristi kada je player skriven</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="test_passed">Prošlo</string>
<string name="restart">Restart</string>
<string name="test_log">Log</string>
<string name="start">Početak</string>
<string name="test_failed">Neuspješno</string>
<string name="stop">Stop</string>
<string name="category_provider_test">Test pružatelja usluga</string>
<string name="subscription_in_progress_notification">Ažuriram pretplaćene serije</string>
<string name="subscription_episode_released">Epizoda %d izbačena!</string>
<string name="subscription_list_name">Pretplaćeno</string>
<string name="subscription_new">Pretplaćen na %s</string>
<string name="subscription_deleted">Otkazana pretplata sa %s</string>
<string name="revert">Vraćanje</string>
<string name="pref_category_bypass">ISP zaobilaznice</string>
<string name="jsdelivr_proxy">raw.githubusercontent.com Proxy</string>
<string name="jsdelivr_enabled">Neuspješno dohvaćanje GitHuba, omogućavanje jsdelivr proxyja.</string>
<string name="jsdelivr_proxy_summary">Koristeći jsdelivr, GitHub blokiranje se može zaobići. Može odgoditi ažuriranja za nekoliko dana.</string>
<string name="watch_quality_pref_data">Preferirana kvaliteta gledanja (podatkovna mobilna mreža)</string>
</resources>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<string name="cast_format" formatted="true">Stáblista: %s</string> <string name="cast_format" formatted="true">Stáblista: %s</string>
<string name="next_episode_time_day_format" formatted="true">%dn %dó%dp</string> <string name="next_episode_time_day_format" formatted="true">%dn %dó%dp</string>
@ -57,7 +57,7 @@
<string name="result_open_in_browser">Megnyitás böngészőben</string> <string name="result_open_in_browser">Megnyitás böngészőben</string>
<string name="skip_loading">Betöltés kihagyása</string> <string name="skip_loading">Betöltés kihagyása</string>
<string name="result_poster_img_des">Poster</string> <string name="result_poster_img_des">Poster</string>
<string name="search_poster_img_des">\@string/result_poster_img_des</string> <string name="search_poster_img_des">@string/result_poster_img_des</string>
<string name="type_watching">Nézés</string> <string name="type_watching">Nézés</string>
<string name="type_completed">Befejezve</string> <string name="type_completed">Befejezve</string>
<string name="type_plan_to_watch">Később megnézés</string> <string name="type_plan_to_watch">Később megnézés</string>
@ -111,7 +111,7 @@
<string name="subs_import_text" formatted="true">Betűtípusok importálása %s</string> <string name="subs_import_text" formatted="true">Betűtípusok importálása %s</string>
<string name="action_remove_watching">Eltávolítás</string> <string name="action_remove_watching">Eltávolítás</string>
<string name="action_open_watching">Több információ</string> <string name="action_open_watching">Több információ</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">VPN szükséges lehet ehhez a szolgáltató megfelelő működéséhez</string> <string name="vpn_might_be_needed">VPN szükséges lehet ehhez a szolgáltató megfelelő működéséhez</string>
<string name="vpn_torrent">Ez a szolgáltató torrent, VPN ajánlott</string> <string name="vpn_torrent">Ez a szolgáltató torrent, VPN ajánlott</string>
<string name="torrent_plot">Leírás</string> <string name="torrent_plot">Leírás</string>
@ -172,11 +172,11 @@
<string name="ova">OVA</string> <string name="ova">OVA</string>
<string name="others">Egyebek</string> <string name="others">Egyebek</string>
<string name="tv_series_singular">Sorozat</string> <string name="tv_series_singular">Sorozat</string>
<string name="anime_singular">\@string/anime</string> <string name="anime_singular">@string/anime</string>
<string name="source_error">Forráshiba</string> <string name="source_error">Forráshiba</string>
<string name="nsfw">NSFW</string> <string name="nsfw">NSFW</string>
<string name="cartoons_singular">Rajzfilm</string> <string name="cartoons_singular">Rajzfilm</string>
<string name="ova_singular">\@string/ova</string> <string name="ova_singular">@string/ova</string>
<string name="live_singular">Élőadás</string> <string name="live_singular">Élőadás</string>
<string name="nsfw_singular">NSFW</string> <string name="nsfw_singular">NSFW</string>
<string name="other_singular">Videó</string> <string name="other_singular">Videó</string>
@ -275,4 +275,4 @@
<string name="show_hd">Minőségi jelzés</string> <string name="show_hd">Minőségi jelzés</string>
<string name="show_dub">Szinkroncímke</string> <string name="show_dub">Szinkroncímke</string>
<string name="show_sub">Alcímke</string> <string name="show_sub">Alcímke</string>
</resources> </resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG --> <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string> <string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
<string name="cast_format" formatted="true">Pemeran: %s</string> <string name="cast_format" formatted="true">Pemeran: %s</string>
@ -35,7 +36,7 @@
<string name="skip_loading">Skip Loading</string> <string name="skip_loading">Skip Loading</string>
<string name="loading">Loading…</string> <string name="loading">Loading…</string>
<string name="type_watching">Sedang Menonton</string> <string name="type_watching">Sedang Menonton</string>
<string name="type_on_hold">Tertahan</string> <string name="type_on_hold">Tertunda</string>
<string name="type_completed">Selesai</string> <string name="type_completed">Selesai</string>
<string name="type_dropped">Dihentikan</string> <string name="type_dropped">Dihentikan</string>
<string name="type_plan_to_watch">Rencana untuk Menonton</string> <string name="type_plan_to_watch">Rencana untuk Menonton</string>
@ -101,7 +102,7 @@
<string name="continue_watching">Lanjutkan Menonton</string> <string name="continue_watching">Lanjutkan Menonton</string>
<string name="action_remove_watching">Hapus</string> <string name="action_remove_watching">Hapus</string>
<string name="action_open_watching">Info lebih lanjut</string> <string name="action_open_watching">Info lebih lanjut</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Sebuah VPN mungkin diperlukan agar provider ini bisa bekerja dengan benar</string> <string name="vpn_might_be_needed">Sebuah VPN mungkin diperlukan agar provider ini bisa bekerja dengan benar</string>
<string name="vpn_torrent">Provider ini adalah sebuah torrent, VPN direkomendasikan</string> <string name="vpn_torrent">Provider ini adalah sebuah torrent, VPN direkomendasikan</string>
<string name="provider_info_meta">Metadata tidak disediakan oleh situs, loading video akan gagal jika tidak ada di situs.</string> <string name="provider_info_meta">Metadata tidak disediakan oleh situs, loading video akan gagal jika tidak ada di situs.</string>
@ -120,14 +121,14 @@
<string name="eigengraumode_settings">Mode Eigengravy</string> <string name="eigengraumode_settings">Mode Eigengravy</string>
<string name="eigengraumode_settings_des">Menambahkan opsi kecepatan di pemutar</string> <string name="eigengraumode_settings_des">Menambahkan opsi kecepatan di pemutar</string>
<string name="swipe_to_seek_settings">Geser untuk mengubah waktu</string> <string name="swipe_to_seek_settings">Geser untuk mengubah waktu</string>
<string name="swipe_to_seek_settings_des">Geser ke kiri atau kanan untuk mengontrol waktu di pemutar video</string> <string name="swipe_to_seek_settings_des">Geser dari sisi ke sisi untuk mengontrol posisi dalam video</string>
<string name="swipe_to_change_settings">Geser untuk mengubah pengaturan</string> <string name="swipe_to_change_settings">Geser untuk mengubah pengaturan</string>
<string name="swipe_to_change_settings_des">Geser ke sisi kiri atau kanan untuk mengubah pencerahan atau volume</string> <string name="swipe_to_change_settings_des">Geser ke atas atau ke bawah di sisi kiri atau kanan untuk mengubah kecerahan atau volume</string>
<string name="double_tap_to_seek_settings">Tekan dua kali untuk mengubah waktu</string> <string name="double_tap_to_seek_settings">Tekan dua kali untuk mengubah waktu</string>
<string name="double_tap_to_pause_settings">Tekan dua kali untuk menjeda</string> <string name="double_tap_to_pause_settings">Tekan dua kali untuk menjeda</string>
<string name="double_tap_to_seek_amount_settings">Jumlah pengubah waktu pemutar</string> <string name="double_tap_to_seek_amount_settings">Jumlah pengubah waktu pemutar (Detik)</string>
<string name="double_tap_to_seek_settings_des">Tekan dua kali di sisi kanan atau kiri untuk mengubah waktu ke depan atau ke belakang</string> <string name="double_tap_to_seek_settings_des">Tekan dua kali di sisi kanan atau kiri untuk mengubah waktu ke depan atau ke belakang</string>
<string name="double_tap_to_pause_settings_des">Tekan di tengah untuk menjeda</string> <string name="double_tap_to_pause_settings_des">Tekan dua kali di tengah untuk menjeda</string>
<string name="use_system_brightness_settings">Gunakan pencerahan sistem</string> <string name="use_system_brightness_settings">Gunakan pencerahan sistem</string>
<string name="use_system_brightness_settings_des">Gunakan pencerahan sistem di pemutar aplikasi dari pada hamparan gelap</string> <string name="use_system_brightness_settings_des">Gunakan pencerahan sistem di pemutar aplikasi dari pada hamparan gelap</string>
<string name="episode_sync_settings">Update progres tontonan</string> <string name="episode_sync_settings">Update progres tontonan</string>
@ -149,7 +150,7 @@
<string name="bug_report_settings_on">Tidak mengirim data</string> <string name="bug_report_settings_on">Tidak mengirim data</string>
<string name="show_fillers_settings">Tampilkan episode filler untuk anime</string> <string name="show_fillers_settings">Tampilkan episode filler untuk anime</string>
<string name="updates_settings">Tampilkan update aplikasi</string> <string name="updates_settings">Tampilkan update aplikasi</string>
<string name="updates_settings_des">Secara otomatis mencari update terbaru saat aplikasi dibuka</string> <string name="updates_settings_des">Secara otomatis mencari update terbaru setelah aplikasi dibuka.</string>
<string name="uprereleases_settings">Update ke prarilis</string> <string name="uprereleases_settings">Update ke prarilis</string>
<string name="uprereleases_settings_des">Hanya mencari update prarilis daripada rilis penuh</string> <string name="uprereleases_settings_des">Hanya mencari update prarilis daripada rilis penuh</string>
<string name="github">Github</string> <string name="github">Github</string>
@ -209,8 +210,8 @@
<string name="movies_singular">Movie</string> <string name="movies_singular">Movie</string>
<string name="tv_series_singular">Seri</string> <string name="tv_series_singular">Seri</string>
<string name="cartoons_singular">Kartun</string> <string name="cartoons_singular">Kartun</string>
<string name="anime_singular">\@string/anime</string> <string name="anime_singular">Anime</string>
<string name="ova_singular">\@string/ova</string> <string name="ova_singular">OVA</string>
<string name="torrent_singular">Torrent</string> <string name="torrent_singular">Torrent</string>
<string name="documentaries_singular">Film Dokumenter</string> <string name="documentaries_singular">Film Dokumenter</string>
<string name="asian_drama_singular">Drama Asia</string> <string name="asian_drama_singular">Drama Asia</string>
@ -243,7 +244,7 @@
<string name="dont_show_again">Jangan tunjukkan lagi</string> <string name="dont_show_again">Jangan tunjukkan lagi</string>
<string name="skip_update">Skip Update ini</string> <string name="skip_update">Skip Update ini</string>
<string name="update">Update</string> <string name="update">Update</string>
<string name="watch_quality_pref">Kualitas tontonan yang lebih diinginkan</string> <string name="watch_quality_pref">Kualitas tontonan yang lebih diinginkan (WIFI)</string>
<string name="limit_title">Karakter maksimal judul pemutar video</string> <string name="limit_title">Karakter maksimal judul pemutar video</string>
<string name="limit_title_rez">Resolusi pemutar video</string> <string name="limit_title_rez">Resolusi pemutar video</string>
<string name="video_buffer_size_settings">Ukuran buffer video</string> <string name="video_buffer_size_settings">Ukuran buffer video</string>
@ -264,7 +265,7 @@
<string name="legal_notice_text" translatable="false">Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.</string> <string name="legal_notice_text" translatable="false">Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.</string>
<string name="category_general">Umum</string> <string name="category_general">Umum</string>
<string name="random_button_settings">Tombol Acak</string> <string name="random_button_settings">Tombol Acak</string>
<string name="random_button_settings_desc">Tampilkan tombol acak di Beranda</string> <string name="random_button_settings_desc">Tampilkan tombol di halaman utama yang dapat memilih seri film atau TV acak dari halaman utama</string>
<string name="provider_lang_settings">Bahasa provider</string> <string name="provider_lang_settings">Bahasa provider</string>
<string name="app_layout">Tata Letak Aplikasi</string> <string name="app_layout">Tata Letak Aplikasi</string>
<string name="preferred_media_settings">Media yang lebih diinginkan</string> <string name="preferred_media_settings">Media yang lebih diinginkan</string>
@ -387,7 +388,7 @@
<string name="episode_format" formatted="true">%d %s</string> <string name="episode_format" formatted="true">%d %s</string>
<string name="nsfw">17+</string> <string name="nsfw">17+</string>
<string name="others">Lainnya</string> <string name="others">Lainnya</string>
<string name="other_singular">Vidio</string> <string name="other_singular">Video</string>
<string name="add_site_pref">Duplikasi Website</string> <string name="add_site_pref">Duplikasi Website</string>
<string name="add_site_summary">Duplikasi website yang telah ada, dengan alamat berbeda</string> <string name="add_site_summary">Duplikasi website yang telah ada, dengan alamat berbeda</string>
<string name="pref_category_links">Tautan</string> <string name="pref_category_links">Tautan</string>
@ -395,7 +396,7 @@
<string name="pref_category_backup">Cadangkan</string> <string name="pref_category_backup">Cadangkan</string>
<string name="pref_category_extensions">Fitur Tambahan</string> <string name="pref_category_extensions">Fitur Tambahan</string>
<string name="play_with_app_name">Putar di CloudStream</string> <string name="play_with_app_name">Putar di CloudStream</string>
<string name="pref_filter_search_quality">Sembunyikan kualitas vidio terpilih di pencarian</string> <string name="pref_filter_search_quality">Sembunyikan kualitas video terpilih di pencarian</string>
<string name="season_format">%s %d%s</string> <string name="season_format">%s %d%s</string>
<string name="livestreams">Siaran langsung</string> <string name="livestreams">Siaran langsung</string>
<string name="remove_site_pref">Hapus Website</string> <string name="remove_site_pref">Hapus Website</string>
@ -444,7 +445,7 @@
<string name="extension_rating" formatted="true">Peringkat: %s</string> <string name="extension_rating" formatted="true">Peringkat: %s</string>
<string name="extension_authors">Pembuat</string> <string name="extension_authors">Pembuat</string>
<string name="extension_language">Bahasa</string> <string name="extension_language">Bahasa</string>
<string name="player_pref">Pemutar vidio utama</string> <string name="player_pref">Pemutar video utama</string>
<string name="player_settings_play_in_app">Pemutar Bawaan</string> <string name="player_settings_play_in_app">Pemutar Bawaan</string>
<string name="player_settings_play_in_vlc">VLC</string> <string name="player_settings_play_in_vlc">VLC</string>
<string name="player_settings_play_in_mpv">MPV</string> <string name="player_settings_play_in_mpv">MPV</string>
@ -475,7 +476,7 @@
<string name="subtitles_remove_captions">Hapus teks tertutup dari subtitel</string> <string name="subtitles_remove_captions">Hapus teks tertutup dari subtitel</string>
<string name="subtitles_remove_bloat">Hapus karakter sampah dari subtitel</string> <string name="subtitles_remove_bloat">Hapus karakter sampah dari subtitel</string>
<string name="audio_tracks">Audio Trek</string> <string name="audio_tracks">Audio Trek</string>
<string name="video_tracks">Vidio Trek</string> <string name="video_tracks">Video Trek</string>
<string name="extension_types">Dukungan</string> <string name="extension_types">Dukungan</string>
<string name="hls_playlist">Daftar putar HLS</string> <string name="hls_playlist">Daftar putar HLS</string>
<string name="apk_installer_settings">Penginstal APK</string> <string name="apk_installer_settings">Penginstal APK</string>
@ -529,4 +530,27 @@
<string name="empty_library_logged_in_message">Yahh daftar ini kosong, coba ganti ke yang lain</string> <string name="empty_library_logged_in_message">Yahh daftar ini kosong, coba ganti ke yang lain</string>
<string name="safe_mode_file">Mode aman file ditemukan! <string name="safe_mode_file">Mode aman file ditemukan!
\nTidak memuat ekstensi pada startup sampai berkas dihapus.</string> \nTidak memuat ekstensi pada startup sampai berkas dihapus.</string>
</resources> <string name="android_tv_interface_off_seek_settings">Sembunyikan Pemutaran - Geser</string>
<string name="android_tv_interface_on_seek_settings">Pemutar terlihat - Geser</string>
<string name="android_tv_interface_on_seek_settings_summary">Geser untuk menghilangkan</string>
<string name="android_tv_interface_off_seek_settings_summary">Geser untuk menghilangkan</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="test_log">Log</string>
<string name="test_passed">Berhasil</string>
<string name="category_provider_test">Tes provider</string>
<string name="stop">Berhenti</string>
<string name="start">Mulai</string>
<string name="restart">Mulai lagi</string>
<string name="test_failed">Gagal</string>
<string name="subscription_in_progress_notification">Memperbarui acara langganan</string>
<string name="subscription_list_name">Berlangganan</string>
<string name="subscription_new">Berlangganan ke %s</string>
<string name="subscription_deleted">Berhenti berlangganan di %s</string>
<string name="subscription_episode_released">Episode %d telah rilis!</string>
<string name="jsdelivr_proxy">raw.githubusercontent.com Proksi</string>
<string name="jsdelivr_enabled">Gagal mencapai GitHub, mengaktifkan proksi jsdelivr.</string>
<string name="jsdelivr_proxy_summary">Mengunakan jsdelivers, bisa melewati pemblokiran GitHub. Mungkin dapat menyebabkan pembaruan tertunda dalam beberapa hari.</string>
<string name="pref_category_bypass">Bypass ISP</string>
<string name="revert">Pulihkan</string>
<string name="watch_quality_pref_data">Nonton dengan kualitas yang di inginkan (Data Seluler)</string>
</resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG --> <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string> <string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
<string name="cast_format" formatted="true">Cast: %s</string> <string name="cast_format" formatted="true">Cast: %s</string>
@ -108,7 +109,7 @@
<string name="continue_watching">Continua a guardare</string> <string name="continue_watching">Continua a guardare</string>
<string name="action_remove_watching">Rimuovi</string> <string name="action_remove_watching">Rimuovi</string>
<string name="action_open_watching">Più info</string> <string name="action_open_watching">Più info</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Potrebbe essere necessaria una VPN per far funzionare correttamente questo provider</string> <string name="vpn_might_be_needed">Potrebbe essere necessaria una VPN per far funzionare correttamente questo provider</string>
<string name="vpn_torrent">Questo provider è un torrent, si raccomanda una VPN</string> <string name="vpn_torrent">Questo provider è un torrent, si raccomanda una VPN</string>
<string name="provider_info_meta">I metadati non sono forniti dal sito, il caricamento del video fallirà se non esiste sul sito.</string> <string name="provider_info_meta">I metadati non sono forniti dal sito, il caricamento del video fallirà se non esiste sul sito.</string>
@ -265,7 +266,7 @@
<string name="dont_show_again">Non mostrare di nuovo</string> <string name="dont_show_again">Non mostrare di nuovo</string>
<string name="skip_update">Salta questo aggiornamento</string> <string name="skip_update">Salta questo aggiornamento</string>
<string name="update">Aggiorna</string> <string name="update">Aggiorna</string>
<string name="watch_quality_pref">Risoluzione preferita</string> <string name="watch_quality_pref">Qualità di visualizzazione preferita (WiFi)</string>
<string name="limit_title">Limita i caratteri del titolo nel player</string> <string name="limit_title">Limita i caratteri del titolo nel player</string>
<string name="limit_title_rez">Risoluzione video player</string> <string name="limit_title_rez">Risoluzione video player</string>
<string name="video_buffer_size_settings">Dimensione cache video</string> <string name="video_buffer_size_settings">Dimensione cache video</string>
@ -528,4 +529,27 @@
<string name="empty_library_logged_in_message">Sembra che questa lista sia vuota, prova a passare a un\'altra</string> <string name="empty_library_logged_in_message">Sembra che questa lista sia vuota, prova a passare a un\'altra</string>
<string name="safe_mode_file">File \"safe mode\" trovato! <string name="safe_mode_file">File \"safe mode\" trovato!
\nAll\'avvio non sarà caricata alcuna estensione finchè il file non verrà rimosso.</string> \nAll\'avvio non sarà caricata alcuna estensione finchè il file non verrà rimosso.</string>
</resources> <string name="android_tv_interface_off_seek_settings_summary">Quantità di ricerca usata quando il player è nascosto</string>
<string name="pref_category_android_tv">TV Android</string>
<string name="android_tv_interface_on_seek_settings_summary">Quantità di ricerca usata quando il player è visibile</string>
<string name="android_tv_interface_on_seek_settings">Player visibile - Quantità di ricerca</string>
<string name="android_tv_interface_off_seek_settings">Player nascosto - Quantità di ricerca</string>
<string name="test_log">Registro</string>
<string name="start">Avvia</string>
<string name="category_provider_test">Test del provider</string>
<string name="restart">Riavvia</string>
<string name="stop">Ferma</string>
<string name="test_passed">Superato</string>
<string name="test_failed">Fallito</string>
<string name="jsdelivr_proxy">Proxy raw.githubusercontent.com</string>
<string name="subscription_deleted">Disiscritto da %s</string>
<string name="subscription_list_name">Iscritto</string>
<string name="subscription_new">Iscritto a %s</string>
<string name="jsdelivr_enabled">Impossibile contattare GitHub, abilitazione proxy jsdelivr avviata.</string>
<string name="jsdelivr_proxy_summary">Bypassa il blocco di GitHub utilizzando jsdelivr, potrebbe causare un ritardo di alcuni giorni.</string>
<string name="pref_category_bypass">Baypass ISP</string>
<string name="revert">Ripristina</string>
<string name="subscription_in_progress_notification">Aggiornando shows a cui sei iscritto</string>
<string name="subscription_episode_released">L\'episodio %d è stato rilasciato!</string>
<string name="watch_quality_pref_data">Qualità di visualizzazione preferita (Dati mobili)</string>
</resources>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<string name="preview_background_img_des">הרקע של ההצגה לפני</string> <string name="preview_background_img_des">הרקע של ההצגה לפני</string>
<string name="cast_format" formatted="true">צוות שחקנים: %s</string> <string name="cast_format" formatted="true">צוות שחקנים: %s</string>
@ -116,7 +116,7 @@
<string name="chromecast_subtitles_settings">כתוביות כרומקאסט</string> <string name="chromecast_subtitles_settings">כתוביות כרומקאסט</string>
<string name="picture_in_picture_des">ממשיך ניגון בנגן מינימלי מעל ישומים אחרים</string> <string name="picture_in_picture_des">ממשיך ניגון בנגן מינימלי מעל ישומים אחרים</string>
<string name="player_subtitles_settings">כתוביות</string> <string name="player_subtitles_settings">כתוביות</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="history">היסטוריה</string> <string name="history">היסטוריה</string>
<string name="apk_installer_legacy">מורשת</string> <string name="apk_installer_legacy">מורשת</string>
<string name="no">לא</string> <string name="no">לא</string>
@ -164,8 +164,8 @@
<string name="used_storage">משומש</string> <string name="used_storage">משומש</string>
<string name="tv_series">סדרת טלוויזיה</string> <string name="tv_series">סדרת טלוויזיה</string>
<string name="cartoons">סדרות/סרטים מצוירים</string> <string name="cartoons">סדרות/סרטים מצוירים</string>
<string name="anime_singular">\@string/אנימה</string> <string name="anime_singular">@string/anime</string>
<string name="ova_singular">\@string/אנימציית וידאו מקורית</string> <string name="ova_singular">@string/ova</string>
<string name="asian_drama_singular">דרמה אסייתית</string> <string name="asian_drama_singular">דרמה אסייתית</string>
<string name="episode_action_chromecast_episode">כרומקאסט את הפרק</string> <string name="episode_action_chromecast_episode">כרומקאסט את הפרק</string>
<string name="episode_action_chromecast_mirror">כרומקאסט את המראה</string> <string name="episode_action_chromecast_mirror">כרומקאסט את המראה</string>
@ -506,4 +506,4 @@
<string name="sort_alphabetical_z">אלפביתי (ת\' עד א\')</string> <string name="sort_alphabetical_z">אלפביתי (ת\' עד א\')</string>
<string name="open_with">פתח עם</string> <string name="open_with">פתח עם</string>
<string name="empty_library_logged_in_message">נראה שהרשימה הזו ריקה, נסו לעבור לרשימה אחרת</string> <string name="empty_library_logged_in_message">נראה שהרשימה הזו ריקה, נסו לעבור לרשימה אחרת</string>
</resources> </resources>

View file

@ -0,0 +1,185 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="next_episode_time_min_format" formatted="true">%d分</string>
<string name="title_downloads">ダウンロード</string>
<string name="title_search">検索</string>
<string name="title_settings">設定</string>
<string name="result_share">シェア</string>
<string name="movies">映画</string>
<string name="title_home">ホーム</string>
<string name="library">ライブラリ</string>
<string name="home_play">再生</string>
<string name="next_episode_time_day_format" formatted="true">%d日 %d時間%d分</string>
<string name="next_episode_time_hour_format" formatted="true">%d時間%d分</string>
<string name="search_hint">検索…</string>
<string name="download">ダウンロード</string>
<string name="home_info">情報</string>
<string name="season">シーズン</string>
<string name="trailer">予告編</string>
<string name="tv_series_singular">シリーズ</string>
<string name="episodes">エピソード</string>
<string name="player_speed_text_format" formatted="true">再生速度 (%.2fx)</string>
<string name="next_episode">次のエピソード</string>
<string name="sort_apply">適用</string>
<string name="category_account">アカウント</string>
<string name="cartoons">カートゥーン</string>
<string name="tv_series">TVシリーズ</string>
<string name="torrent">トレント</string>
<string name="documentaries">ドキュメンタリー</string>
<string name="ova">OVA</string>
<string name="asian_drama">アジアドラマ</string>
<string name="livestreams">ライブ配信</string>
<string name="movies_singular">映画</string>
<string name="others">その他</string>
<string name="cartoons_singular">カートゥーン</string>
<string name="torrent_singular">トレント</string>
<string name="documentaries_singular">ドキュメンタリー</string>
<string name="asian_drama_singular">アジアドラマ</string>
<string name="live_singular">ライブ配信</string>
<string name="nsfw_singular">NSFW</string>
<string name="sort_cancel">キャンセル</string>
<string name="anime">アニメ</string>
<string name="video_lock">ロック</string>
<string name="video_source">ソース</string>
<string name="nsfw">NSFW</string>
<string name="clear_history">履歴を削除</string>
<string name="continue_watching">視聴中コンテンツ</string>
<string name="category_general">全般</string>
<string name="other_singular">動画</string>
<string name="category_player">プレーヤー</string>
<string name="type_plan_to_watch">懐う</string>
<string name="play_trailer_button">予告編を再生</string>
<string name="episode_short">エピソード</string>
<string name="type_watching">視聴</string>
<string name="result_tags">ジャンル</string>
<string name="play_movie_button">映画を再生</string>
<string name="pick_subtitle">字幕</string>
<string name="app_name">CloudStream</string>
<string name="play_with_app_name">CloudStreamで再生</string>
<string name="browser">ブラウザ</string>
<string name="type_completed">完成</string>
<string name="type_dropped">放置</string>
<string name="type_on_hold">保留</string>
<string name="loading">ローディング…</string>
<string name="result_open_in_browser">ブラウザで開く</string>
<string name="season_short">シーズン</string>
<string name="resume_time_left" formatted="true">残り
\n%d分</string>
<string name="play_episode">再生エピソード</string>
<string name="downloaded">ダウンロード済</string>
<string name="pref_category_backup">バックアップ</string>
<string name="home_source">ソース</string>
<string name="history">履歴</string>
<string name="result_poster_img_des">ポスター</string>
<string name="type_none">なし</string>
<string name="sort_copy">コピー</string>
<string name="sort_close">閉じる</string>
<string name="sort_save">保存</string>
<string name="sort_clear">消去</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%sエピ%d</string>
<string name="cast_format" formatted="true">出演者:%s</string>
<string name="search_poster_img_des">ポスター</string>
<string name="episode_poster_img_des">エピソードポスター</string>
<string name="home_main_poster_img_des">主要ポスター</string>
<string name="home_next_random_img_des">次のランダム</string>
<string name="go_back_img_des">戻り</string>
<string name="rated_format" formatted="true">視聴率 %.1f</string>
<string name="new_update_format" formatted="true">新しいアップデートを発見!
\n%s -&gt; %s</string>
<string name="duration_format" formatted="true">%d分</string>
<string name="search_hint_site" formatted="true">%sを検索…</string>
<string name="pick_source">ソース</string>
<string name="filler" formatted="true">ろくごうきじ</string>
<string name="reload_error">接続を再試行…</string>
<string name="go_back">戻り</string>
<string name="action_remove_from_bookmarks">削除</string>
<string name="home_more_info">詳細情報</string>
<string name="home_expanded_hide">閉じる</string>
<string name="category_updates">アップデート・バックアップ</string>
<string name="app_language">アプリ言語</string>
<string name="github">GitHubギットハブ</string>
<string name="go_back_30">-30</string>
<string name="go_forward_30">+30</string>
<string name="legal_notice">免責</string>
<string name="pref_category_extensions">拡張機能</string>
<string name="pref_category_app_updates">アプリ更新</string>
<string name="category_providers">提供者</string>
<string name="pref_category_subtitles">字幕</string>
<string name="pref_category_ui_features">特徴</string>
<string name="pref_category_defaults">デフォルト</string>
<string name="automatic">自動</string>
<string name="home_random">任意</string>
<string name="extensions">拡張機能</string>
<string name="pref_category_links">リンク</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="login">ログイン</string>
<string name="logout">ログアウト</string>
<string name="max">最大</string>
<string name="min">最小</string>
<string name="none">なし</string>
<string name="next"></string>
<string name="is_adult">18+</string>
<string name="no"></string>
<string name="open_with">で開く</string>
<string name="episode">エピソード</string>
<string name="duration">時間</string>
<string name="synopsis">概要</string>
<string name="site">サイト</string>
<string name="used_storage">使用</string>
<string name="app_storage">アプリ</string>
<string name="action_open_watching">詳細情報</string>
<string name="action_remove_watching">削除</string>
<string name="picture_in_picture">ピクチャーインピクチャー</string>
<string name="player_subtitles_settings">字幕</string>
<string name="settings_info">情報</string>
<string name="pause">一時停止</string>
<string name="play_episode_toast">再生エピソード</string>
<string name="delete">削除</string>
<string name="start">開始</string>
<string name="status">状態</string>
<string name="year"></string>
<string name="resume">再開</string>
<string name="test_failed">失敗</string>
<string name="test_passed">合格</string>
<string name="free_storage">空き</string>
<string name="status_completed">完成</string>
<string name="status_ongoing">進行中</string>
<string name="normal">デフォルト</string>
<string name="player_settings_play_in_browser">ウェブブラウザ</string>
<string name="player_settings_play_in_vlc">VLC</string>
<string name="player_settings_play_in_mpv">MPV</string>
<string name="extension_language">言語</string>
<string name="extension_authors">作成者</string>
<string name="extension_size">サイズ</string>
<string name="extension_status">状態</string>
<string name="extension_version">バージョン</string>
<string name="extension_rating" formatted="true">視聴率 %s</string>
<string name="rating">視聴率</string>
<string name="default_subtitles">デフォルト</string>
<string name="download_failed">ダウンロード失敗</string>
<string name="download_started">ダウンロード開始</string>
<string name="download_done">ダウンロード完了</string>
<string name="download_canceled">ダウンロード終了</string>
<string name="stream">ストリーム</string>
<string name="update_started">アップデート開始</string>
<string name="no_season">シーズンなし</string>
<string name="no_subtitles">字幕なし</string>
<string name="video_aspect_ratio_resize">アスペクト比</string>
<string name="skip_loading">ロードをスキップする</string>
<string name="episode_more_options_des">その他のオプション</string>
<string name="no_data">データなし</string>
<string name="downloading">ダウンロード中</string>
<string name="error_bookmarks_text">ブックマーク</string>
<string name="download_storage_text">内部記憶装置</string>
<string name="download_paused">ダウンロードが一時停止</string>
<string name="provider_info_meta">メタデータはこのサイトでは提供されません。メタデータがサイト上に存在しない場合、ビデオの読み込みに失敗します。</string>
<string name="torrent_plot">記述</string>
<string name="show_log_cat">Logcat 🐈を表示</string>
<string name="test_log">ログ</string>
<string name="search">検索</string>
<string name="discord">Discordに参加</string>
<string name="update">アップデート</string>
<string name="check_for_update">アップデートを確認</string>
<string name="show_title">作品名</string>
<string name="update_notification_installing">アプリのアップデートをインストール中…</string>
</resources>

View file

@ -1,3 +1,128 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
</resources> <string name="app_dub_sub_episode_text_format" formatted="true">%sಎಪಿ%d</string>
<string name="cast_format" formatted="true">ಕ್ಯಾಸ್ಟ್:%s</string>
<string name="go_back_img_des">ಹಿಂದೆ ಹೋಗು</string>
<string name="filler" formatted="true">ಫಿಲ್ಲರ್</string>
<string name="title_search">ಹುಡುಕು</string>
<string name="title_downloads">ಡೌನ್ಲೋಡ್</string>
<string name="subs_font">ಫಾಂಟ್</string>
<string name="search_provider_text_providers">ಪೂರೈಕೆದಾರರನ್ನು ಬಳಸಿಕೊಂಡು ಹುಡುಕಿ</string>
<string name="search_provider_text_types">ಪ್ರಕಾರಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಹುಡುಕಿ</string>
<string name="benene_count_text_none">ಯಾವುದೇ ಬೆನೆನ್ಸ್ ನೀಡಿಲ್ಲ</string>
<string name="subs_auto_select_language">ಸ್ವಯಂ-ಆಯ್ಕೆ ಭಾಷೆ</string>
<string name="action_open_watching">ಹೆಚ್ಚಿನ ಮಾಹಿತಿ</string>
<string name="action_open_play">\@ಸ್ಟ್ರಿಂಗ್/ಹೋಮ್_ಪ್ಲೇ</string>
<string name="vpn_might_be_needed">ಈ ಪೂರೈಕೆದಾರರು ಸರಿಯಾಗಿ ಕೆಲಸ ಮಾಡಲು VPN ಬೇಕಾಗಬಹುದು</string>
<string name="player_size_settings_des">ಕಪ್ಪು ಗಡಿಗಳನ್ನು ತೆಗೆದುಹಾಕಿ</string>
<string name="next_episode_format" formatted="true">ಸಂಚಿಕೆ%d ಬಿಡುಗಡೆಯಾಗಲಿದೆ</string>
<string name="next_episode_time_hour_format" formatted="true">%dh %dm</string>
<string name="result_poster_img_des">ಪೋಸ್ಟರ್</string>
<string name="search_poster_img_des">ಪೋಸ್ಟರ್</string>
<string name="episode_poster_img_des">ಸಂಚಿಕೆ ಪೋಸ್ಟರ್</string>
<string name="home_main_poster_img_des">ಮೇನ್ ಪೋಸ್ಟರ್</string>
<string name="update_started">ಅಪ್ಡೇಟ್ ಪ್ರಾರಂಭವಾಗಿದೆ</string>
<string name="error_loading_links_toast">ಲೋಡಿಂಗ್ ಲಿಂಕ್ ಎರರ್ ಬಂದಿದೆ</string>
<string name="download_storage_text">ಇಂಟರ್ನಲ್ ಸ್ಟೋರೇಜ್</string>
<string name="app_dubbed_text">ಡಬ್</string>
<string name="app_subbed_text">ಸಬ್</string>
<string name="pref_disable_acra">ಸ್ವಯಂಚಾಲಿತ ದೋಷ ವರದಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ</string>
<string name="home_expanded_hide">ಹೈಡ್</string>
<string name="home_play">ಪ್ಲೇ</string>
<string name="home_info">ಮಾಹಿತಿ</string>
<string name="action_add_to_bookmarks">ಸೆಟ್ ವಾಚ್ ಸ್ಟೇಟಸ್</string>
<string name="sort_apply">ಅನ್ವಯಿಸು</string>
<string name="sort_cancel">ರದ್ದುಮಾಡು</string>
<string name="subs_subtitle_elevation">ಸಬ್ ಟೈಟಲ್ಸ್ ಎಲೆವಷನ್</string>
<string name="subs_font_size">ಫಾಂಟ್ ಸೈಜ್</string>
<string name="subs_subtitle_languages">ಸಬ್ ಟೈಟಲ್ಸ್ ಭಾಷೆ</string>
<string name="action_remove_watching">ತೆಗೆದುಹಾಕಿ</string>
<string name="vpn_torrent">ಈ ಪೂರೈಕೆದಾರರು ಟೊರೆಂಟ್ ಆಗಿದೆ, VPN ಅನ್ನು ಶಿಫಾರಸು ಮಾಡಲಾಗಿದೆ</string>
<string name="normal_no_plot">ಯಾವುದೇ ಪ್ಲಾಟ್ ಕಂಡುಬಂದಿಲ್ಲ</string>
<string name="show_log_cat">ಲಾಗ್‌ಕ್ಯಾಟ್ 🐈 ತೋರಿಸಿ</string>
<string name="test_log">ಲಾಗ್</string>
<string name="picture_in_picture">ಚಿತ್ರದಲ್ಲಿ-ಚಿತ್ರದಲ್ಲಿ</string>
<string name="player_size_settings">ಪ್ಲೇಯರ್ ಮರುಗಾತ್ರಗೊಳಿಸಿ ಬಟನ್</string>
<string name="player_subtitles_settings">ಸಬ್ ಟೈಟಲ್ಸ್</string>
<string name="player_subtitles_settings_des">ಪ್ಲೇಯರ್ ಸಬ್ ಟೈಟಲ್ಸ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು</string>
<string name="chromecast_subtitles_settings_des">ಕ್ರೋಮ್ ಕ್ಯಾಸ್ಟ್ ಸಬ್ ಟೈಟಲ್ಸ್ ಸೆಟ್ಟಿಂಗ್ಸ್</string>
<string name="go_back">ಹಿಂದೆ ಹೋಗು</string>
<string name="popup_pause_download">ಡೌನ್‌ಲೋಡ್ ವಿರಾಮಗೊಳಿಸಿ</string>
<string name="error_bookmarks_text">ಬುಕ್‌ಮಾರ್ಕ್‌</string>
<string name="subs_background_color">ಬ್ಯಾಕ್ ಗ್ರೌಂಡ್ ಕಲರ್</string>
<string name="benene_count_text">%d ಡೇವ್‌ಗಳಿಗೆ ಬೆನೆನೆಸ್ ನೀಡಲಾಗಿದೆ</string>
<string name="subs_hold_to_reset_to_default">ಡೀಫಾಲ್ಟ್‌ಗೆ ಮರುಹೊಂದಿಸಲು ಹಿಡಿದುಕೊಳ್ಳಿ</string>
<string name="provider_info_meta">ಸೈಟ್‌ನಿಂದ ಮೆಟಾಡೇಟಾವನ್ನು ಒದಗಿಸಲಾಗಿಲ್ಲ, ಅದು ಸೈಟ್‌ನಲ್ಲಿ ಅಸ್ತಿತ್ವದಲ್ಲಿಲ್ಲದಿದ್ದರೆ ವೀಡಿಯೊ ಲೋಡಿಂಗ್ ವಿಫಲಗೊಳ್ಳುತ್ತದೆ.</string>
<string name="picture_in_picture_des">ಇತರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಮೇಲೆ ಚಿಕಣಿ ಪ್ಲೇಯರ್‌ನಲ್ಲಿ ಪ್ಲೇಬ್ಯಾಕ್ ಅನ್ನು ಮುಂದುವರಿಸುತ್ತದೆ</string>
<string name="chromecast_subtitles_settings">ಕ್ರೋಮ್ ಕ್ಯಾಸ್ಟ್ ಸಬ್ ಟೈಟಲ್ಸ್</string>
<string name="rated_format" formatted="true">ರೇಟೆಡ್:%.1f</string>
<string name="action_remove_from_bookmarks">ತೆಗೆದುಹಾಕಿ</string>
<string name="popup_resume_download">ಡೌನ್‌ಲೋಡ್ ಅನ್ನು ಪುನರಾರಂಭಿಸಿ</string>
<string name="sort_close">ಕ್ಲೋಸ್</string>
<string name="sort_clear">ಕ್ಲಿಯರ್</string>
<string name="sort_save">ಸೇವ್</string>
<string name="subtitles_settings">ಸಬ್ ಟೈಟಲ್ಸ್ ಸೆಟ್ಟಿಂಗ್ಸ್</string>
<string name="popup_play_file">ಫೈಲ್ ಪ್ಲೇ</string>
<string name="subs_text_color">ಟೆಕ್ಸ್ಟ್ ಕಲರ್</string>
<string name="subs_outline_color">ಔಟ್ ಲೈನ್ ಕಲರ್</string>
<string name="subs_window_color">ವಿಂಡೋ ಕಲರ್</string>
<string name="subs_edge_type">ಎಡ್ಜ್ ಟೈಪ್</string>
<string name="home_change_provider_img_des">ಪ್ರೊವೈಡರ್ ಬದಲಾಯಿಸಿ</string>
<string name="duration_format" formatted="true">%dಮಿನ</string>
<string name="torrent_plot">ವಿವರಣೆ</string>
<string name="player_speed_text_format" formatted="true">ಸ್ಪೀಡ್(%.2fx)</string>
<string name="title_home">ಹೋಂ</string>
<string name="pick_subtitle">ಸಬ್ ಟೈಟಲ್ಸ್</string>
<string name="title_settings">ಸೆಟ್ಟಿಂಗ್ಸ್</string>
<string name="filter_bookmarks">ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳನ್ನು ಫಿಲ್ಟರ್ ಮಾಡಿ</string>
<string name="search_hint">ಹುಡುಕು…</string>
<string name="play_movie_button">ಚಲನಚಿತ್ರವನ್ನು ಪ್ಲೇ ಮಾಡಿ</string>
<string name="preview_background_img_des">ಪ್ರಿವ್ಯೂ ಹಿನ್ನೆಲೆ</string>
<string name="next_episode">ಮುಂದಿನ ಸಂಚಿಕೆ</string>
<string name="app_name">ಕ್ಲೌಡ್ ಸ್ಟ್ರೀಮ್</string>
<string name="downloading">ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ</string>
<string name="stream">ಸ್ಟ್ರೀಮ್</string>
<string name="result_share">ಶೇರ್</string>
<string name="popup_delete_file">ಫೈಲ್ ಅಳಿಸಿ</string>
<string name="home_more_info">ಹೆಚ್ಚಿನ ಮಾಹಿತಿ</string>
<string name="new_update_format" formatted="true">ಹೊಸ ಅಪ್ಡೇಟ್ ಬಂದಿದೆ
\n%s-%s</string>
<string name="loading">ಲೋಡಿಂಗ್…</string>
<string name="subs_download_languages">ಡೌನ್‌ಲೋಡ್ ಭಾಷೆಗಳನ್ನು ಮಾಡಿ</string>
<string name="play_livestream_button">ಲೈವ್‌ಸ್ಟ್ರೀಮ್ ಪ್ಲೇ ಮಾಡಿ</string>
<string name="play_with_app_name">ಕ್ಲೌಡ್ ಸ್ಟ್ರೀಮ್ ಇದರೊಂದಿಗೆ ಪ್ಲೇ ಮಾಡಿ</string>
<string name="type_plan_to_watch">ವೀಕ್ಷಿಸಲು ಯೋಜನೆ</string>
<string name="play_episode">ಸಂಚಿಕೆಯನ್ನು ಪ್ಲೇ ಮಾಡಿ</string>
<string name="continue_watching">ಕಂಟಿನ್ಯೂ ವಾಟಚಿಂಗ್</string>
<string name="torrent_no_plot">ಯಾವುದೇ ವಿವರಣೆ ಕಂಡುಬಂದಿಲ್ಲ</string>
<string name="play_torrent_button">ಸ್ಟ್ರೀಮ್ ಟೊರೆಂಟ್</string>
<string name="download">ಡೌನ್‌ಲೋಡ್</string>
<string name="sort_copy">ಕಾಪಿ</string>
<string name="no_data">ನೋ ಡೇಟಾ</string>
<string name="player_speed">ಪ್ಲೇಯರ್ ಸ್ಪೀಡ್</string>
<string name="next_episode_time_day_format" formatted="true">%d %dh %dm</string>
<string name="search_hint_site" formatted="true">ಹುಡುಕು %s…</string>
<string name="episode_more_options_des">ಹೆಚ್ಚಿನ ಆಯ್ಕೆ</string>
<string name="subs_import_text" formatted="true">ಫಾಂಟ್‌ಗಳನ್ನು ಇರಿಸುವ ಮೂಲಕ ಆಮದು ಮಾಡಿ %s</string>
<string name="next_episode_time_min_format" formatted="true">%dm</string>
<string name="result_tags">ಪ್ರಕಾರಗಳು</string>
<string name="result_open_in_browser">ಬ್ರೌಸರ್ ತೆರೆಯಿರಿ</string>
<string name="type_on_hold">ಆನ್-ಹೋಲ್ಡ್</string>
<string name="type_none">ನನ್</string>
<string name="reload_error">ಸಂಪರ್ಕವನ್ನು ಮರುಪ್ರಯತ್ನಿಸಿ…</string>
<string name="download_paused">ಡೌನ್‌ಲೋಡ್ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ</string>
<string name="download_failed">ಡೌನ್‌ಲೋಡ್ ವಿಫಲವಾಗಿದೆ</string>
<string name="download_done">ಡೌನ್‌ಲೋಡ್ ಮುಗಿದಿದೆ</string>
<string name="browser">ಬ್ರೌಸರ್</string>
<string name="skip_loading">ಸ್ಕಿಪ್ ಲೋಡಿಂಗ್</string>
<string name="type_watching">ವಾಚಿಂಗ್</string>
<string name="type_completed">ಪೂರ್ಣಗೊಂಡಿದೆ</string>
<string name="type_dropped">ಕೈಬಿಡಲಾಯಿತು</string>
<string name="type_re_watching">ಪುನಃ ವೀಕ್ಷಿಸುತ್ತಿದೆ</string>
<string name="play_trailer_button">ಟ್ರೈಲರ್ ಪ್ಲೇ ಮಾಡಿ</string>
<string name="pick_source">ಮೂಲಗಳು</string>
<string name="downloaded">ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗಿದೆ</string>
<string name="download_started">ಡೌನ್‌ಲೋಡ್ ಪ್ರಾರಂಭವಾಗಿದೆ</string>
<string name="download_canceled">ಡೌನ್‌ಲೋಡ್ ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ</string>
<string name="home_next_random_img_des">ಮುಂದಿನ ರಾಂಡಮ್</string>
</resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- TRANSLATE, BUT DON'T FORGET FORMAT --> <!-- TRANSLATE, BUT DON'T FORGET FORMAT -->
<string name="player_speed_text_format" formatted="true">Брзина (%.2fx)</string> <string name="player_speed_text_format" formatted="true">Брзина (%.2fx)</string>
<string name="rated_format" formatted="true">Оценето: %.1f</string> <string name="rated_format" formatted="true">Оценето: %.1f</string>
@ -213,4 +214,4 @@
<string name="subtitles_shadow">Сенка</string> <string name="subtitles_shadow">Сенка</string>
<string name="subtitles_raised">Подигнат</string> <string name="subtitles_raised">Подигнат</string>
<string name="history">Историја</string> <string name="history">Историја</string>
</resources> </resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- TRANSLATE, BUT DON'T FORGET FORMAT --> <!-- TRANSLATE, BUT DON'T FORGET FORMAT -->
<string name="player_speed_text_format" formatted="true">വേഗം (%.2fx)</string> <string name="player_speed_text_format" formatted="true">വേഗം (%.2fx)</string>
<string name="rated_format" formatted="true">റേറ്റിംഗ്: %.1f</string> <string name="rated_format" formatted="true">റേറ്റിംഗ്: %.1f</string>
@ -169,4 +170,4 @@
<string name="watch_quality_pref">ഔചിത്യ വീഡിയോ ക്വാളിറ്റി</string> <string name="watch_quality_pref">ഔചിത്യ വീഡിയോ ക്വാളിറ്റി</string>
<string name="history">ചരിത്രം</string> <string name="history">ചരിത്രം</string>
<string name="action_mark_as_watched">കണ്ടതാണെന്ന് അടയാളപ്പെടുത്തുക</string> <string name="action_mark_as_watched">കണ്ടതാണെന്ന് അടയാളപ്പെടുത്തുക</string>
</resources> </resources>

View file

@ -0,0 +1,2 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources/>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG --> <!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string> <string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
<string name="cast_format" formatted="true">Cast: %s</string> <string name="cast_format" formatted="true">Cast: %s</string>
@ -9,7 +10,7 @@
<string name="next_episode_time_min_format" formatted="true">%dm</string> <string name="next_episode_time_min_format" formatted="true">%dm</string>
<!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS --> <!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS -->
<string name="result_poster_img_des">Poster</string> <string name="result_poster_img_des">Poster</string>
<string name="search_poster_img_des">\@string/result_poster_img_des</string> <string name="search_poster_img_des">Poster</string>
<string name="episode_poster_img_des">Aflevering Poster</string> <string name="episode_poster_img_des">Aflevering Poster</string>
<string name="home_main_poster_img_des">Hoofdposter</string> <string name="home_main_poster_img_des">Hoofdposter</string>
<string name="home_next_random_img_des">Volgende willekeurig</string> <string name="home_next_random_img_des">Volgende willekeurig</string>
@ -109,7 +110,7 @@
<string name="continue_watching">Doorgaan met kijken</string> <string name="continue_watching">Doorgaan met kijken</string>
<string name="action_remove_watching">Verwijder</string> <string name="action_remove_watching">Verwijder</string>
<string name="action_open_watching">Meer Info</string> <string name="action_open_watching">Meer Info</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Een VPN kan nodig zijn om deze provider correct te laten werken</string> <string name="vpn_might_be_needed">Een VPN kan nodig zijn om deze provider correct te laten werken</string>
<string name="vpn_torrent">Deze provider is een torrent, een VPN wordt aanbevolen</string> <string name="vpn_torrent">Deze provider is een torrent, een VPN wordt aanbevolen</string>
<string name="provider_info_meta">Metadata wordt niet geleverd door de site, het laden van video\'s zal mislukken als deze niet op de site bestaat.</string> <string name="provider_info_meta">Metadata wordt niet geleverd door de site, het laden van video\'s zal mislukken als deze niet op de site bestaat.</string>
@ -128,14 +129,14 @@
<string name="eigengraumode_settings">Eigengravy Modus</string> <string name="eigengraumode_settings">Eigengravy Modus</string>
<string name="eigengraumode_settings_des">Voegt een snelheidsoptie toe in de speler</string> <string name="eigengraumode_settings_des">Voegt een snelheidsoptie toe in de speler</string>
<string name="swipe_to_seek_settings">Swipe to seek</string> <string name="swipe_to_seek_settings">Swipe to seek</string>
<string name="swipe_to_seek_settings_des">Veeg naar links of rechts om de tijd in de videoplayer te regelen</string> <string name="swipe_to_seek_settings_des">Veeg naar links of rechts om de tijd in de videospeler te regelen</string>
<string name="swipe_to_change_settings">Veeg om instellingen te wijzigen</string> <string name="swipe_to_change_settings">Veeg om instellingen te wijzigen</string>
<string name="swipe_to_change_settings_des">Veeg naar links of rechts om de helderheid of het volume te wijzigen</string> <string name="swipe_to_change_settings_des">Veeg naar links of rechts om de helderheid of het volume te wijzigen</string>
<string name="double_tap_to_seek_settings">Dubbeltik om te zien</string> <string name="double_tap_to_seek_settings">Dubbeltik om te zien</string>
<string name="double_tap_to_pause_settings">Dubbeltik om te pauzeren</string> <string name="double_tap_to_pause_settings">Dubbeltik om te pauzeren</string>
<string name="double_tap_to_seek_amount_settings">Speler zoeken bedrag</string> <string name="double_tap_to_seek_amount_settings">Videospeler aantal zoeken</string>
<string name="double_tap_to_seek_settings_des">Tik twee keer aan de rechter- of linkerkant om vooruit of achteruit te zoeken</string> <string name="double_tap_to_seek_settings_des">Tik twee keer aan de rechter- of linkerkant om vooruit of achteruit te zoeken</string>
<string name="double_tap_to_pause_settings_des">Tik in het midden om te pauzeren</string> <string name="double_tap_to_pause_settings_des">Tik twee keer in het midden om te pauzeren</string>
<string name="use_system_brightness_settings">Systeemhelderheid gebruiken</string> <string name="use_system_brightness_settings">Systeemhelderheid gebruiken</string>
<string name="use_system_brightness_settings_des">Gebruik systeemhelderheid in de app-speler in plaats van een donkere overlay</string> <string name="use_system_brightness_settings_des">Gebruik systeemhelderheid in de app-speler in plaats van een donkere overlay</string>
<string name="episode_sync_settings">Kijkvoortgang bijwerken</string> <string name="episode_sync_settings">Kijkvoortgang bijwerken</string>
@ -222,8 +223,8 @@
<string name="movies_singular">Film</string> <string name="movies_singular">Film</string>
<string name="tv_series_singular">Serie</string> <string name="tv_series_singular">Serie</string>
<string name="cartoons_singular">Tekenfilm</string> <string name="cartoons_singular">Tekenfilm</string>
<string name="anime_singular">\@string/anime</string> <string name="anime_singular">@string/anime</string>
<string name="ova_singular">\@string/ova</string> <string name="ova_singular">@string/ova</string>
<string name="torrent_singular">Torrent</string> <string name="torrent_singular">Torrent</string>
<string name="documentaries_singular">Documentaire</string> <string name="documentaries_singular">Documentaire</string>
<string name="asian_drama_singular">Aziatisch drama</string> <string name="asian_drama_singular">Aziatisch drama</string>
@ -405,4 +406,7 @@
<string name="autoplay_next_settings_des">Start de volgende episode wanneer deze afgelopen is</string> <string name="autoplay_next_settings_des">Start de volgende episode wanneer deze afgelopen is</string>
<string name="autoplay_next_settings">Volgende episode automatisch afspelen</string> <string name="autoplay_next_settings">Volgende episode automatisch afspelen</string>
<string name="update_started">De update is gestart</string> <string name="update_started">De update is gestart</string>
</resources> <string name="library">Bibliotheek</string>
<string name="browser">Browser</string>
<string name="test_log">Logboek</string>
</resources>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<string name="episode_more_options_des">Fleire val</string> <string name="episode_more_options_des">Fleire val</string>
<string name="title_home">Heim</string> <string name="title_home">Heim</string>
@ -183,4 +183,4 @@
<string name="duration">Varigheit</string> <string name="duration">Varigheit</string>
<string name="livestreams">Direktesendingar</string> <string name="livestreams">Direktesendingar</string>
<string name="pref_category_app_updates">Programoppdateringar</string> <string name="pref_category_app_updates">Programoppdateringar</string>
</resources> </resources>

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS --> <!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS -->
<string name="result_poster_img_des">Plakat</string> <string name="result_poster_img_des">Plakat</string>
<string name="search_poster_img_des">\@string/result_poster_img_des</string> <string name="search_poster_img_des">@string/result_poster_img_des</string>
<string name="episode_poster_img_des">Episode Plakat</string> <string name="episode_poster_img_des">Episode Plakat</string>
<string name="home_main_poster_img_des">Main Plakat</string> <string name="home_main_poster_img_des">Main Plakat</string>
<string name="home_next_random_img_des">Neste tilfeldig</string> <string name="home_next_random_img_des">Neste tilfeldig</string>
@ -412,7 +412,7 @@
<string name="poster_ui_settings">Slå av/på grensesnittselementer på plakat</string> <string name="poster_ui_settings">Slå av/på grensesnittselementer på plakat</string>
<string name="skip_update">Hopp over denne oppdateringen</string> <string name="skip_update">Hopp over denne oppdateringen</string>
<string name="video_ram_description">Forårsaker tilfeldige krasj hvis satt for høyt. Ikke endre dette hvis du ikke har lite minne.</string> <string name="video_ram_description">Forårsaker tilfeldige krasj hvis satt for høyt. Ikke endre dette hvis du ikke har lite minne.</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="backup_settings">Sikkerhetskopier data</string> <string name="backup_settings">Sikkerhetskopier data</string>
<string name="backup_success">Data lagret</string> <string name="backup_success">Data lagret</string>
<string name="authenticated_user_fail" formatted="true">Kunne ikke logge inn på %s</string> <string name="authenticated_user_fail" formatted="true">Kunne ikke logge inn på %s</string>
@ -422,11 +422,11 @@
<string name="nsfw">Sensurerbart</string> <string name="nsfw">Sensurerbart</string>
<string name="quality_webrip">Vev</string> <string name="quality_webrip">Vev</string>
<string name="network_adress_example">Lenke til strøm</string> <string name="network_adress_example">Lenke til strøm</string>
<string name="anime_singular">\@string/anime</string> <string name="anime_singular">@string/anime</string>
<string name="pref_filter_search_quality">Skjul valgt videokvalitet i søkeresultater</string> <string name="pref_filter_search_quality">Skjul valgt videokvalitet i søkeresultater</string>
<string name="restore_success">Lastet inn sikkerhetkopifil</string> <string name="restore_success">Lastet inn sikkerhetkopifil</string>
<string name="category_updates">Oppdateringer og sikkerhetskopi</string> <string name="category_updates">Oppdateringer og sikkerhetskopi</string>
<string name="ova_singular">\@string/ova</string> <string name="ova_singular">@string/ova</string>
<string name="confirm_exit_dialog">Avslutt\?</string> <string name="confirm_exit_dialog">Avslutt\?</string>
<string name="nsfw_singular">Sensurerbart</string> <string name="nsfw_singular">Sensurerbart</string>
<string name="batch_download_nothing_to_download_format" formatted="true">Alle %s er allerede nedlastet</string> <string name="batch_download_nothing_to_download_format" formatted="true">Alle %s er allerede nedlastet</string>
@ -492,4 +492,4 @@
<string name="update_started">Oppdatering startet</string> <string name="update_started">Oppdatering startet</string>
<string name="plugin_downloaded">Programtillegg nedlastet</string> <string name="plugin_downloaded">Programtillegg nedlastet</string>
<string name="delayed_update_notice">Programmet vil oppgraderes når du avslutter det</string> <string name="delayed_update_notice">Programmet vil oppgraderes når du avslutter det</string>
</resources> </resources>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<!--https://newbedev.com/concatenate-multiple-strings-in-xml--><resources> <!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<string name="player_speed_text_format" formatted="true">Prędkość (%.2fx)</string> <string name="player_speed_text_format" formatted="true">Prędkość (%.2fx)</string>
<string name="rated_format" formatted="true">Ocena: %.1f</string> <string name="rated_format" formatted="true">Ocena: %.1f</string>
<string name="new_update_format" formatted="true">Znaleziono nową aktualizację! <string name="new_update_format" formatted="true">Znaleziono nową aktualizację!
@ -254,7 +255,7 @@
<string name="dont_show_again">Nie pokazuj ponownie</string> <string name="dont_show_again">Nie pokazuj ponownie</string>
<string name="skip_update">Pomiń tę aktualizację</string> <string name="skip_update">Pomiń tę aktualizację</string>
<string name="update">Aktualizacja</string> <string name="update">Aktualizacja</string>
<string name="watch_quality_pref">Domyślna jakość</string> <string name="watch_quality_pref">Domyślna jakość (WiFi)</string>
<string name="limit_title">Maksymalna ilość znaków w tytule odtwarzacza</string> <string name="limit_title">Maksymalna ilość znaków w tytule odtwarzacza</string>
<string name="limit_title_rez">Rozdzielczość odtwarzacza wideo</string> <string name="limit_title_rez">Rozdzielczość odtwarzacza wideo</string>
<string name="video_buffer_size_settings">Rozmiar bufora wideo</string> <string name="video_buffer_size_settings">Rozmiar bufora wideo</string>
@ -373,7 +374,7 @@
<string name="skip_setup">Pomiń setup</string> <string name="skip_setup">Pomiń setup</string>
<string name="app_layout_subtext">Dostosuj wygląd aplikacji do urządzenia</string> <string name="app_layout_subtext">Dostosuj wygląd aplikacji do urządzenia</string>
<string name="crash_reporting_title">Zgłaszanie błędów</string> <string name="crash_reporting_title">Zgłaszanie błędów</string>
<string name="preferred_media_subtext">Co chciałbyś obejrzeć\?</string> <string name="preferred_media_subtext">Co chciałbyś obejrzeć</string>
<string name="setup_done">Gotowe</string> <string name="setup_done">Gotowe</string>
<string name="extensions">Rozszerzenia</string> <string name="extensions">Rozszerzenia</string>
<string name="add_repository">Dodaj repozytorium</string> <string name="add_repository">Dodaj repozytorium</string>
@ -455,7 +456,7 @@
<string name="apk_installer_settings">Instalator APK</string> <string name="apk_installer_settings">Instalator APK</string>
<string name="apk_installer_settings_des">Niektóre telefony nie obsługują nowego instalatora pakietów. Wypróbuj tryb legacy, jeśli aktualizacje nie zostaną zainstalowane.</string> <string name="apk_installer_settings_des">Niektóre telefony nie obsługują nowego instalatora pakietów. Wypróbuj tryb legacy, jeśli aktualizacje nie zostaną zainstalowane.</string>
<string name="example_password">password123</string> <string name="example_password">password123</string>
<string name="ova_singular">\@string/ova</string> <string name="ova_singular">@string/ova</string>
<string name="example_site_name">MojaFajnaWitryna</string> <string name="example_site_name">MojaFajnaWitryna</string>
<string name="example_username">MyCoolUsername</string> <string name="example_username">MyCoolUsername</string>
<string name="example_ip">127.0.0.1</string> <string name="example_ip">127.0.0.1</string>
@ -463,9 +464,9 @@
<string name="example_site_url">przyklad.pl</string> <string name="example_site_url">przyklad.pl</string>
<string name="sync_total_episodes_none">/\?\?</string> <string name="sync_total_episodes_none">/\?\?</string>
<string name="apk_installer_package_installer">Instalator pakietów</string> <string name="apk_installer_package_installer">Instalator pakietów</string>
<string name="action_open_play">\@string/home_play</string> <string name="action_open_play">@string/home_play</string>
<string name="example_email">hello@world.com</string> <string name="example_email">hello@world.com</string>
<string name="anime_singular">\@string/anime</string> <string name="anime_singular">@string/anime</string>
<string name="skip_type_op">Opening</string> <string name="skip_type_op">Opening</string>
<string name="skip_type_ed">Ending</string> <string name="skip_type_ed">Ending</string>
<string name="skip_type_mixed_op">Mixed opening</string> <string name="skip_type_mixed_op">Mixed opening</string>
@ -509,4 +510,27 @@
<string name="empty_library_logged_in_message">Wygląda na to, że ta lista jest pusta, spróbuj przełączyć się na inną</string> <string name="empty_library_logged_in_message">Wygląda na to, że ta lista jest pusta, spróbuj przełączyć się na inną</string>
<string name="safe_mode_file">Znaleziono plik trybu bezpiecznego. <string name="safe_mode_file">Znaleziono plik trybu bezpiecznego.
\nRozszerzenia nie zostaną wczytane, dopóki plik nie zostanie usunięty.</string> \nRozszerzenia nie zostaną wczytane, dopóki plik nie zostanie usunięty.</string>
</resources> <string name="android_tv_interface_on_seek_settings_summary">Używana ilość przewijania, gdy widoczny jest odtwarzacz</string>
<string name="android_tv_interface_off_seek_settings">Ukryty odtwarzacz - ilość przewijania</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="android_tv_interface_on_seek_settings">Pokazany odtwarzacz — ilość przewijania</string>
<string name="android_tv_interface_off_seek_settings_summary">Używana ilość przewijania, gdy ukryty jest odtwarzacz</string>
<string name="test_log">Dziennik</string>
<string name="restart">Uruchom ponownie</string>
<string name="start">Rozpocznij</string>
<string name="test_failed">Nie powiodło się</string>
<string name="test_passed">Ukończone powodzeniem</string>
<string name="jsdelivr_proxy">Serwer pośredniczący raw.githubusercontent.com</string>
<string name="pref_category_bypass">Obejścia ISP</string>
<string name="category_provider_test">Test dostawcy</string>
<string name="stop">Zatrzymaj</string>
<string name="revert">Przywróć</string>
<string name="subscription_in_progress_notification">Aktualizowanie subskrybowanych programów</string>
<string name="subscription_list_name">Zasubskrybowano</string>
<string name="subscription_new">Zasubskrybowano %s</string>
<string name="subscription_deleted">Anulowano subskrypcję %s</string>
<string name="subscription_episode_released">Został wydany odcinek %d!</string>
<string name="jsdelivr_proxy_summary">Obchodzi blokadę GitHuba za pomocą jsdelivr, może spowodować opóźnienie aktualizacji o kilka dni.</string>
<string name="jsdelivr_enabled">Nie udało się połączyć z GitHub, włączono serwer pośredniczący jsdelivr.</string>
<string name="watch_quality_pref_data">Domyślna jakość (dane mobilne)</string>
</resources>

View file

@ -1,65 +1,65 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string> <string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
<string name="next_episode_time_hour_format" formatted="true">%dh %dm</string> <string name="next_episode_time_hour_format" formatted="true">%dh %dm</string>
<string name="next_episode_time_min_format" formatted="true">%dm</string> <string name="next_episode_time_min_format" formatted="true">%dm</string>
<string name="next_episode_format" formatted="true">Episódio %d será lançado em</string> <string name="next_episode_format" formatted="true">O episódio %d será lançado em</string>
<string name="result_poster_img_des">Poster</string> <string name="result_poster_img_des">Poster</string>
<string name="episode_poster_img_des">Capa do Episódio</string> <string name="episode_poster_img_des">Pôster do episódio</string>
<string name="search_poster_img_des">\@string/result_poster_img_des</string> <string name="search_poster_img_des">Poster</string>
<string name="home_main_poster_img_des">Capa Principal</string> <string name="home_main_poster_img_des">Pôster Principal</string>
<string name="home_next_random_img_des">Próximo Aleatório</string> <string name="home_next_random_img_des">Próximo Aleatório</string>
<string name="go_back_img_des">Voltar</string> <string name="go_back_img_des">Voltar</string>
<string name="home_change_provider_img_des">Trocar Provedor</string> <string name="home_change_provider_img_des">Alterar Provedor</string>
<string name="next_episode_time_day_format" formatted="true">%dd %dh %dm</string> <string name="next_episode_time_day_format" formatted="true">%dd %dh %dm</string>
<string name="home_source">Fonte</string> <string name="home_source">Fonte</string>
<string name="resolution">Resolução</string> <string name="resolution">Resolução</string>
<string name="extras">Extras</string> <string name="extras">Extras</string>
<string name="preview_background_img_des">Preview Background</string> <string name="preview_background_img_des">Visualizar plano de fundo</string>
<string name="player_speed_text_format" formatted="true">Velocidade (%.2fx)</string> <string name="player_speed_text_format" formatted="true">Velocidade (%.2fx)</string>
<string name="rated_format" formatted="true">Classificado: %.1f</string> <string name="rated_format" formatted="true">Classificado: %.1f</string>
<string name="new_update_format" formatted="true">Nova atualização encontrada! <string name="new_update_format" formatted="true">Nova atualização encontrada!
\n%s -&gt; %s</string> \n%s -&gt; %s</string>
<string name="filler" formatted="true">Enchimento</string> <string name="filler" formatted="true">Preenchimento</string>
<string name="app_name">CloudStream</string> <string name="app_name">CloudStream</string>
<string name="play_with_app_name">Reproduzir com CloudStream</string> <string name="play_with_app_name">Assistir com o CloudStream</string>
<string name="title_home">Início</string> <string name="title_home">Início</string>
<string name="title_search">Pesquisa</string> <string name="title_search">Pesquisar</string>
<string name="title_downloads">Transferências</string> <string name="title_downloads">Downloads</string>
<string name="title_settings">Opções</string> <string name="title_settings">Configurações</string>
<string name="search_hint">Procurar…</string> <string name="search_hint">Procurar…</string>
<string name="search_hint_site" formatted="true">Procurar em %s…</string> <string name="search_hint_site" formatted="true">Pesquisar %s…</string>
<string name="no_data">Sem Dados</string> <string name="no_data">Sem dados</string>
<string name="episode_more_options_des">Mais Opções</string> <string name="episode_more_options_des">Mais opções</string>
<string name="next_episode">Próximo episódio</string> <string name="next_episode">Próximo episódio</string>
<string name="result_tags">Géneros</string> <string name="result_tags">Gêneros</string>
<string name="result_share">Partilhar</string> <string name="result_share">Compartilhar</string>
<string name="result_open_in_browser">Abrir no Navegador</string> <string name="result_open_in_browser">Abrir no navegador</string>
<string name="skip_loading">Saltar Carga</string> <string name="skip_loading">Pular carregamento</string>
<string name="loading">Carregando…</string> <string name="loading">Carregando…</string>
<string name="type_watching">Assistindo</string> <string name="type_watching">Assistindo</string>
<string name="type_on_hold">Em Espera</string> <string name="type_on_hold">Em espera</string>
<string name="type_completed">Concluído</string> <string name="type_completed">Concluído</string>
<string name="type_dropped">Abandonado</string> <string name="type_dropped">Desistido</string>
<string name="type_plan_to_watch">Planeio Assistir</string> <string name="type_plan_to_watch">Pretendo assistir</string>
<string name="type_none">Nenhuma</string> <string name="type_none">Nenhum</string>
<string name="type_re_watching">Assistindo de Novo</string> <string name="type_re_watching">Reassistindo</string>
<string name="play_movie_button">Reproduzir Filme</string> <string name="play_movie_button">Reproduzir filme</string>
<string name="play_livestream_button">Reproduzir Livestream</string> <string name="play_livestream_button">Reproduzir transmissão ao vivo</string>
<string name="play_torrent_button">Transmitir Torrent</string> <string name="play_torrent_button">Transmitir Torrent</string>
<string name="pick_source">Fontes</string> <string name="pick_source">Fontes</string>
<string name="pick_subtitle">Legendas</string> <string name="pick_subtitle">Legendas</string>
<string name="reload_error">Voltar a tentar ligação</string> <string name="reload_error">Tentar conexão novamente</string>
<string name="go_back">Voltar Atrás</string> <string name="go_back">Voltar</string>
<string name="play_episode">Reproduzir Episódio</string> <string name="play_episode">Reproduzir episódio</string>
<string name="download">Transferir</string> <string name="download">Download</string>
<string name="downloaded">Transferido</string> <string name="downloaded">Baixado</string>
<string name="downloading">A Transferir</string> <string name="downloading">Baixando</string>
<string name="download_paused">Transferência em Pausa</string> <string name="download_paused">Download Pausado</string>
<string name="download_started">Transferência Iniciada</string> <string name="download_started">Download Iniciado</string>
<string name="download_failed">Transferência Falhou</string> <string name="download_failed">Falha no Download</string>
<string name="download_canceled">Transferência Cancelada</string> <string name="download_canceled">Download cancelado</string>
<string name="download_done">Transferência Completa</string> <string name="download_done">Download concluído</string>
<string name="stream">Stream</string> <string name="stream">Stream</string>
<string name="error_loading_links_toast">Erro a Carregar Links</string> <string name="error_loading_links_toast">Erro a Carregar Links</string>
<string name="download_storage_text">Armazenamento Interno</string> <string name="download_storage_text">Armazenamento Interno</string>
@ -123,16 +123,16 @@
<string name="eigengraumode_settings">Modo Eigengravy</string> <string name="eigengraumode_settings">Modo Eigengravy</string>
<string name="eigengraumode_settings_des">Acrescenta uma opção de velocidade no player</string> <string name="eigengraumode_settings_des">Acrescenta uma opção de velocidade no player</string>
<string name="swipe_to_seek_settings">Deslize para andar</string> <string name="swipe_to_seek_settings">Deslize para andar</string>
<string name="swipe_to_seek_settings_des">Deslize para a esq. ou dir. para controlar o tempo no player</string> <string name="swipe_to_seek_settings_des">Deslize para os lados para controlar a posição em um vídeo</string>
<string name="swipe_to_change_settings">Deslize para mudar as configurações</string> <string name="swipe_to_change_settings">Deslize para mudar as configurações</string>
<string name="swipe_to_change_settings_des">Deslize do lado esq. ou dir. para ajustar brilho ou volume</string> <string name="swipe_to_change_settings_des">Deslize para cima ou para baixo, no lado esquerdo ou direito, para ajustar brilho ou volume</string>
<string name="autoplay_next_settings">Reproduzir automaticamente próximo episódio</string> <string name="autoplay_next_settings">Reproduzir automaticamente próximo episódio</string>
<string name="autoplay_next_settings_des">Começa o próximo episódio quando o atual termina</string> <string name="autoplay_next_settings_des">Começa o próximo episódio quando o atual termina</string>
<string name="double_tap_to_seek_settings">Toque duplo para avançar</string> <string name="double_tap_to_seek_settings">Toque duplo para avançar</string>
<string name="double_tap_to_pause_settings">Toque duplo para pôr em pausa</string> <string name="double_tap_to_pause_settings">Toque duplo para pôr em pausa</string>
<string name="double_tap_to_seek_amount_settings">Segundos avançados no player</string> <string name="double_tap_to_seek_amount_settings">Tempo de busca no player (Segundos)</string>
<string name="double_tap_to_seek_settings_des">Toque duplo no lado esq. ou dir. para andar para trás ou para a frente</string> <string name="double_tap_to_seek_settings_des">Toque duplo no lado esq. ou dir. para andar para trás ou para a frente</string>
<string name="double_tap_to_pause_settings_des">Toque no meio para pôr em pausa</string> <string name="double_tap_to_pause_settings_des">Toque duas vezes no meio para pausar</string>
<string name="use_system_brightness_settings">Usar brilho da sistema</string> <string name="use_system_brightness_settings">Usar brilho da sistema</string>
<string name="use_system_brightness_settings_des">Usar brilho do sistema no player em vez de uma sobreposição escura</string> <string name="use_system_brightness_settings_des">Usar brilho do sistema no player em vez de uma sobreposição escura</string>
<string name="episode_sync_settings">Atualizar progresso</string> <string name="episode_sync_settings">Atualizar progresso</string>
@ -142,7 +142,7 @@
<string name="restore_success">Arquivo de backup carregado</string> <string name="restore_success">Arquivo de backup carregado</string>
<string name="restore_failed_format" formatted="true">Falha ao restaurar dados do ficheiro %s</string> <string name="restore_failed_format" formatted="true">Falha ao restaurar dados do ficheiro %s</string>
<string name="backup_success">Dados guardados com sucesso</string> <string name="backup_success">Dados guardados com sucesso</string>
<string name="backup_failed">Permissões de armazenamento em falta, por favor tente de novo</string> <string name="backup_failed">Permissão de armazenamento não encontrada, por favor tente novamente.</string>
<string name="backup_failed_error_format">Erro no backup de %s</string> <string name="backup_failed_error_format">Erro no backup de %s</string>
<string name="search">Procurar</string> <string name="search">Procurar</string>
<string name="category_account">Contas</string> <string name="category_account">Contas</string>
@ -158,7 +158,7 @@
<string name="pref_filter_search_quality">Esconder qualidades de vídeo selecionadas nos resultados da Pesquisa</string> <string name="pref_filter_search_quality">Esconder qualidades de vídeo selecionadas nos resultados da Pesquisa</string>
<string name="automatic_plugin_updates">Atualizações de plugin automáticas</string> <string name="automatic_plugin_updates">Atualizações de plugin automáticas</string>
<string name="updates_settings">Mostrar atualizações da app</string> <string name="updates_settings">Mostrar atualizações da app</string>
<string name="updates_settings_des">Procurar novas atualizações automaticamente ao iniciar</string> <string name="updates_settings_des">Procurar automaticamente por novas atualizações depois de iniciar o app.</string>
<string name="uprereleases_settings">Atualizar para pré-lançamentos</string> <string name="uprereleases_settings">Atualizar para pré-lançamentos</string>
<string name="uprereleases_settings_des">Procura atualizações de pré-lançamento em vez de só lançamentos oficiais</string> <string name="uprereleases_settings_des">Procura atualizações de pré-lançamento em vez de só lançamentos oficiais</string>
<string name="github">Github</string> <string name="github">Github</string>
@ -250,15 +250,15 @@
<string name="dont_show_again">Não mostrar de novo</string> <string name="dont_show_again">Não mostrar de novo</string>
<string name="skip_update">Saltar esta Atualização</string> <string name="skip_update">Saltar esta Atualização</string>
<string name="update">Atualizar</string> <string name="update">Atualizar</string>
<string name="watch_quality_pref">Qualidade Preferida</string> <string name="watch_quality_pref">Qualidade Preferida (WiFi)</string>
<string name="limit_title">Máximo de caracteres do título de vídeos</string> <string name="limit_title">Máximo de caracteres do título no player de video</string>
<string name="limit_title_rez">Resolução do player de vídeo</string> <string name="limit_title_rez">Resolução do player de vídeo</string>
<string name="video_buffer_size_settings">Tamanho do buffer do vídeo</string> <string name="video_buffer_size_settings">Tamanho do buffer do vídeo</string>
<string name="video_buffer_length_settings">Comprimento do buffer do vídeo</string> <string name="video_buffer_length_settings">Comprimento do buffer do vídeo</string>
<string name="video_buffer_disk_settings">Cache do vídeo em disco</string> <string name="video_buffer_disk_settings">Cache do vídeo em disco</string>
<string name="video_buffer_clear_settings">Limpar cache de vídeo e imagem</string> <string name="video_buffer_clear_settings">Limpar cache de vídeo e imagem</string>
<string name="video_ram_description">Causará travamentos aleatórios se definido muito alto. Não mude se tiver pouca memória RAM, como um Android TV ou um telefone antigo</string> <string name="video_ram_description">Causará travamentos em dispositivos com pouca memória se definido muito alto , como uma Android TV.</string>
<string name="video_disk_description">Pode causar problemas em sistemas com pouco espaço de armazenamento se definido muito alto, como em dispositivos Android TV</string> <string name="video_disk_description">Pode causar problemas em sistemas com pouco espaço de armazenamento se definido muito alto, como uma Android TV.</string>
<string name="dns_pref">DNS sobre HTTPS</string> <string name="dns_pref">DNS sobre HTTPS</string>
<string name="dns_pref_summary">Útil para contornar bloqueios do fornecedor de internet</string> <string name="dns_pref_summary">Útil para contornar bloqueios do fornecedor de internet</string>
<string name="add_site_pref">Clonar site</string> <string name="add_site_pref">Clonar site</string>
@ -273,7 +273,7 @@
<string name="legal_notice">Aviso Legal</string> <string name="legal_notice">Aviso Legal</string>
<string name="category_general">Geral</string> <string name="category_general">Geral</string>
<string name="random_button_settings">Botão Aleatório</string> <string name="random_button_settings">Botão Aleatório</string>
<string name="random_button_settings_desc">Mostra o botão Aleatório na página inicial</string> <string name="random_button_settings_desc">Mostra o botão Aleatório na página inicial, que pode escolher aleatoriamente um filme ou série</string>
<string name="provider_lang_settings">Idioma dos fornecedores</string> <string name="provider_lang_settings">Idioma dos fornecedores</string>
<string name="app_layout">Layout da App</string> <string name="app_layout">Layout da App</string>
<string name="preferred_media_settings">Mídia preferida</string> <string name="preferred_media_settings">Mídia preferida</string>
@ -363,7 +363,7 @@
<string name="plugin_loaded">Plugin Carregado</string> <string name="plugin_loaded">Plugin Carregado</string>
<string name="plugin_deleted">Plugin Apagado</string> <string name="plugin_deleted">Plugin Apagado</string>
<string name="plugin_load_fail" formatted="true">Falha ao carregar %s</string> <string name="plugin_load_fail" formatted="true">Falha ao carregar %s</string>
<string name="batch_download_start_format" formatted="true">Iniciada a transferência %d %s</string> <string name="batch_download_start_format" formatted="true">Download iniciado %d %s…</string>
<string name="batch_download_finish_format" formatted="true">Transferido %d %s com sucesso</string> <string name="batch_download_finish_format" formatted="true">Transferido %d %s com sucesso</string>
<string name="batch_download_nothing_to_download_format" formatted="true">Tudo %s já transferido</string> <string name="batch_download_nothing_to_download_format" formatted="true">Tudo %s já transferido</string>
<string name="batch_download">Transferência em batch</string> <string name="batch_download">Transferência em batch</string>
@ -375,10 +375,158 @@
<string name="plugins_downloaded" formatted="true">Transferido: %d</string> <string name="plugins_downloaded" formatted="true">Transferido: %d</string>
<string name="plugins_disabled" formatted="true">Desativado: %d</string> <string name="plugins_disabled" formatted="true">Desativado: %d</string>
<string name="plugins_not_downloaded" formatted="true">Não transferido: %d</string> <string name="plugins_not_downloaded" formatted="true">Não transferido: %d</string>
<string name="blank_repo_message">Adicionar um repositório para instalar extensões de sites</string> <string name="blank_repo_message">O CloudStream não possui sites instalados por padrão. Você precisa instalar os sites a partir de repositórios.
\n
\nDevido a uma restrição sem sentido de direitos autorais (DMCA) pela Sky UK Limited 🤮 não podemos vincular o site do repositório no aplicativo.
\n
\nJunte-se ao nosso Discord ou pesquise online.</string>
<string name="view_public_repositories_button">Ver repositórios da comunidade</string> <string name="view_public_repositories_button">Ver repositórios da comunidade</string>
<string name="view_public_repositories_button_short">Lista pública</string> <string name="view_public_repositories_button_short">Lista pública</string>
<string name="uppercase_all_subtitles">Todas as legendas em maiúsculas</string> <string name="uppercase_all_subtitles">Todas as legendas em maiúsculas</string>
<string name="download_all_plugins_from_repo">Transferir todos os plugins deste repositório\?</string> <string name="download_all_plugins_from_repo">Transferir todos os plugins deste repositório\?</string>
<string name="single_plugin_disabled" formatted="true">%s (Desativado)</string> <string name="single_plugin_disabled" formatted="true">%s (Desativado)</string>
</resources> <string name="apk_installer_settings">Instalador APK</string>
<string name="duration_format" formatted="true">%d min</string>
<string name="play_trailer_button">Assistir Trailer</string>
<string name="action_add_to_bookmarks">Marcar como visto/não visto</string>
<string name="action_open_play">Reproduzir</string>
<string name="automatic_plugin_download_summary">Instalar automaticamente todos os plugins ainda não instalados dos repositórios adicionados.</string>
<string name="automatic_plugin_download">Baixar extensões automaticamente</string>
<string name="redo_setup_process">Refazer o processo de configuração</string>
<string name="go_back_30">-30</string>
<string name="other_singular">Vídeo</string>
<string name="go_forward_30">+30</string>
<string name="season_format">%s %d%s</string>
<string name="cast_format" formatted="true">Elenco: %s</string>
<string name="update_started">Atualização iniciada</string>
<string name="test_log">Log</string>
<string name="apk_installer_settings_des">Alguns aparelhos não suportam o novo instalador de pacotes. Use a opção legado caso não esteja conseguindo atualizar.</string>
<string name="episodes_range">%d-%d</string>
<string name="episode_format" formatted="true">%d %s</string>
<string name="start">Iniciar</string>
<string name="test_failed">Falha</string>
<string name="test_passed">Sucesso</string>
<string name="library">Biblioteca</string>
<string name="browser">Navegar</string>
<string name="anim">Aplicativo de Anime pelos mesmos desenvolvedores</string>
<string name="ova_singular">Ova</string>
<string name="anime_singular">Anime</string>
<string name="android_tv_interface_on_seek_settings">Player visível - Procurar valor</string>
<string name="update_notification_installing">Instalando atualização do app…</string>
<string name="confirm_exit_dialog">Você tem certeza que deseja sair\?</string>
<string name="extension_version">Versão</string>
<string name="skip_type_ed">Encerramento</string>
<string name="clear_history">Limpar histórico</string>
<string name="skip_type_op">Abertura</string>
<string name="no">Não</string>
<string name="sort_by">Ordenar por</string>
<string name="yes">Sim</string>
<string name="update_notification_downloading">Baixando atualização do app…</string>
<string name="subscription_episode_released">Episódio %d lançado!</string>
<string name="skip_type_creddits">Créditos</string>
<string name="extension_description">Descrição</string>
<string name="extension_size">Tamanho</string>
<string name="stop">Parar</string>
<string name="safe_mode_title">Modo seguro ligado</string>
<string name="history">Histórico</string>
<string name="sort">Ordenar</string>
<string name="player_settings_play_in_app">Player interno</string>
<string name="extension_authors">Autores</string>
<string name="extension_types">Suportado</string>
<string name="extension_language">Idioma</string>
<string name="extension_install_first">Instalar a extensão primeiro</string>
<string name="hls_playlist">Playlist HLS</string>
<string name="player_pref">Player de vídeo preferido</string>
<string name="extension_status">Estado</string>
<string name="pref_category_gestures">Gestos</string>
<string name="tracks">Faixas</string>
<string name="quality_workprint">WP</string>
<string name="quality_cam">Cam</string>
<string name="skip_type_intro">Abertura</string>
<string name="select_library">Selecionar Biblioteca</string>
<string name="jsdelivr_proxy_summary">Usando jsdelivr o bloqueio do GitHub pode ser contornado. Pode atrasar atualizações em alguns dias.</string>
<string name="player_settings_play_in_vlc">VLC</string>
<string name="all_languages_preference">Todas as linguagens</string>
<string name="sort_updated_new">Atualizado (Novo para Antigo)</string>
<string name="subscription_list_name">Inscrito</string>
<string name="quality_hdr">HDR</string>
<string name="restart">Reiniciar</string>
<string name="player_settings_play_in_browser">Navegador Web</string>
<string name="sort_updated_old">Atualizado (Antigo para Novo)</string>
<string name="player_settings_play_in_web">Web Video Cast</string>
<string name="quality_dvd">DVD</string>
<string name="apk_installer_package_installer">Instalador de pacotes</string>
<string name="player_settings_play_in_mpv">MPV</string>
<string name="action_remove_from_watched">Remover dos assistidos</string>
<string name="update_notification_failed">Não foi possível instalar a nova versão do aplicativo</string>
<string name="subscription_deleted">Inscrição cancelada em %s</string>
<string name="skip_type_mixed_ed">Final misto</string>
<string name="sort_rating_desc">Avaliações (Decrescente)</string>
<string name="apply_on_restart">Aplicar ao reiniciar</string>
<string name="referer">Referente</string>
<string name="android_tv_interface_off_seek_settings">Player oculto - Quantidade de Busca</string>
<string name="jsdelivr_proxy">raw.githubusercontent.com Proxy</string>
<string name="quality_blueray">Blu-ray</string>
<string name="pref_category_looks">Aparência</string>
<string name="subtitle_offset_hint">1000 ms</string>
<string name="quality_sdr">SDR</string>
<string name="is_adult">18+</string>
<string name="open_with">Abrir com</string>
<string name="category_provider_test">Teste de provedor</string>
<string name="quality_uhd">UHD</string>
<string name="safe_mode_crash_info">Ver informações sobre falha</string>
<string name="app_not_found_error">Aplicativo não encontrado</string>
<string name="revert">Reverter</string>
<string name="network_adress_example">Link para transmitir</string>
<string name="plugin_downloaded">Plugins baixados</string>
<string name="plugins_updated" formatted="true">%d plugins atualizados</string>
<string name="skip_type_format" formatted="true">Pular %s</string>
<string name="skip_type_mixed_op">Abertura mista</string>
<string name="sort_alphabetical_z">Alfabético (Z a A)</string>
<string name="empty_library_logged_in_message">Parece que esta lista está vazia, tente trocar para outra</string>
<string name="subscription_new">Inscrito em %s</string>
<string name="quality_4k">4K</string>
<string name="video_tracks">Faixas de vídeo</string>
<string name="delayed_update_notice">O aplicativo será atualizado ao sair</string>
<string name="subscription_in_progress_notification">Atualizando shows inscritos</string>
<string name="sort_alphabetical_a">Alfabético (A a Z)</string>
<string name="sort_rating_asc">Avaliações (Crescente)</string>
<string name="empty_library_no_accounts_message">Parece que a sua biblioteca está vazia :(
\nFaça login em uma conta de biblioteca ou adicione shows à sua biblioteca local</string>
<string name="safe_mode_file">Arquivo de modo de segurança encontrado!
\nNenhuma extensão será carregada na inicialização do app até que o arquivo seja removido.</string>
<string name="pref_category_bypass">Contorno do provedor de serviço de internet (ISP)</string>
<string name="pref_category_links">Links</string>
<string name="pref_category_player_features">Recursos do Player</string>
<string name="pref_category_ui_features">Recursos</string>
<string name="pref_category_app_updates">Atualizações de aplicativos</string>
<string name="watch_quality_pref_data">Qualidade Preferida (Dados Móveis)</string>
<string name="android_tv_interface_on_seek_settings_summary">Quantidade de busca (em segundos) usada quando o player de video está visível</string>
<string name="android_tv_interface_off_seek_settings_summary">Quantidade de busca (em segundos) usada quando o player de video está oculto</string>
<string name="jsdelivr_enabled">Falha ao conectar com GitHub, ativando proxy jsdelivr.</string>
<string name="pref_category_cache">Cache</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="pref_category_subtitles">Legendas</string>
<string name="login_format" formatted="true">%s %s</string>
<string name="quality_ts">TS</string>
<string name="quality_cam_rip">Cam</string>
<string name="quality_cam_hd">Cam</string>
<string name="quality_hq">HQ</string>
<string name="quality_hd">HD</string>
<string name="quality_tc">TC</string>
<string name="quality_webrip">Web</string>
<string name="extension_rating" formatted="true">Nota: %s</string>
<string name="apk_installer_legacy">Legado</string>
<string name="safe_mode_description">Todas as extensões foram desativadas devido a uma falha para ajudá-lo a encontrar a que está causando o problema.</string>
<string name="skip_type_recap">Recapitular</string>
<string name="enable_skip_op_from_database_des">Mostrar pop-ups para pular abertura/encerramento</string>
<string name="clipboard_too_large">Muito texto. Não é possível salvar na área de transferência.</string>
<string name="action_mark_as_watched">Marcar como assistido</string>
<string name="pref_category_backup">Backup</string>
<string name="pref_category_extensions">Extensões</string>
<string name="pref_category_actions">Ações</string>
<string name="pref_category_player_layout">Layout</string>
<string name="pref_category_defaults">Configurações padrão</string>
<string name="quality_sd">SD</string>
<string name="audio_tracks">Faixas de áudio</string>
</resources>

View file

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version='1.0' encoding='UTF-8'?>
<resources> <resources>
<!-- For true intellectuals --> <!-- For true intellectuals -->
<string name="app_name">aauugghhaauuh</string> <string name="app_name">aauugghhaauuh</string>
<string name="title_home">ooh</string> <string name="title_home">ooh</string>
<string name="title_search">oouuh</string> <string name="title_search">oouuh</string>
<string name="title_downloads">ouuhhhooh ooh</string> <string name="title_downloads">ouuhhuhooh ooh</string>
<string name="title_settings">aaaghhoh aauugghh</string> <string name="title_settings">aaaghhoh aauugghh</string>
<string name="search_hint">haaooh</string> <string name="search_hint">haaooh ooo</string>
<string name="search_poster_img_des">aaaaaah</string> <string name="search_poster_img_des">aaaaaah</string>
<string name="no_data">oooohhoouuh</string> <string name="no_data">oooohhoouuh</string>
<string name="episode_more_options_des">aaaaaoouuhahhh ahh</string> <string name="episode_more_options_des">aaaaoouuuhahhh ahh</string>
<string name="go_back_img_des">oooohh aauuh</string> <string name="go_back_img_des">oooohh aauuh</string>
<string name="next_episode">ahhhaaaghh aaaaa</string> <string name="next_episode">ahhhaaaghh aaaaa</string>
<string name="result_poster_img_des">ahaauugghh</string> <string name="result_poster_img_des">ahaauugghh</string>
@ -172,28 +172,80 @@
<string name="resume">oouuh haa</string> <string name="resume">oouuh haa</string>
<string name="double_tap_to_seek_settings_des">oohahaha hahha ooooohaha</string> <string name="double_tap_to_seek_settings_des">oohahaha hahha ooooohaha</string>
<string name="storage_error">oohahaha hahha ooooohaha haaoou</string> <string name="storage_error">oohahaha hahha ooooohaha haaoou</string>
<string name="use_system_brightness_settings">u ahhu uuuh hau uaohuau</string> <string name="use_system_brightness_settings">u ahhu uuuh hau uaohuau</string>
<string name="use_system_brightness_settings_des">aahuuouhh ouh hhhah hhaohuhha</string> <string name="use_system_brightness_settings_des">aahuuouhh ouh hhhah hhaohuhha</string>
<string name="subs_font_size">a auoo ohauh</string> <string name="subs_font_size">a auoo ohauh</string>
<string name="source_error">uhaauauau ahuuouaha</string> <string name="source_error">uhaauauau ahuuouaha</string>
<string name="remote_error">auuuha h a ahuhaaaa</string> <string name="remote_error">auuuha h a ahuhaaaa</string>
<string name="render_error">uaoh uhu uahaaaaoo</string> <string name="render_error">uaoh uhu uahaaaaoo</string>
<string name="unexpected_error">uauhah u aao u oah</string> <string name="unexpected_error">uauhah u aao u oah</string>
<string name="watch_quality_pref">h u ahahh aoou ha</string> <string name="watch_quality_pref">h u ahahh aoou ha</string>
<string name="dns_pref">haoooo aaoou uou ah</string> <string name="dns_pref">haoooo aaoou uou ah</string>
<string name="dns_pref_summary">oahuouooaouoa ouuhh</string> <string name="dns_pref_summary">oahuouooaouoa ouuhh</string>
<string name="display_subbed_dubbed_settings">o ouou uhauuuoaah h</string> <string name="display_subbed_dubbed_settings">o ouou uhauuuoaah h</string>
<string name="resize_fit">ou aouhouo aaooao hh</string> <string name="resize_fit">ou aouhouo aaooao hh</string>
<string name="resize_fill">hhauhohhuu au aaohu</string> <string name="resize_fill">hhauhohhuu au aaohu</string>
<string name="resize_zoom">uhuoh o a ohahuhohoa hah</string> <string name="resize_zoom">uhuoh o a ohahuhohoa hah</string>
<string name="provider_lang_settings">ua hu ouo o aoau hah ah</string> <string name="provider_lang_settings">ua hu ouo o aoau hah ah</string>
<string name="legal_notice">ah huu oouhhau aoaoaaohoo ha</string> <string name="legal_notice">ah huu oouhhau aoaoaaohoo ha</string>
<string name="category_general">a ahu uoo uoahuo uo</string> <string name="category_general">a ahu uoo uoahuo uo</string>
<string name="app_layout">uo u ohouao</string> <string name="app_layout">uo u ohouao</string>
<string name="automatic">uuoouhh hhuhuuh ouhoaao hau aouo</string> <string name="automatic">uuoouhh hhuhuuh ouhoaao hau aouo</string>
<string name="tv_layout">uha uh huo uooaah u</string> <string name="tv_layout">uha uh huo uooaah u</string>
<string name="phone_layout">u ooah uo ahauao huhuu hauu h</string> <string name="phone_layout">u ooah uo ahauao huhuu hauu h</string>
<string name="primary_color_settings">a ou oh ouhuouhoaaha</string> <string name="primary_color_settings">a ou oh ouhuouhoaaha</string>
<string name="show_fillers_settings">aaooohhouhhha hauauuu</string> <string name="show_fillers_settings">aaooohhouhhha hauauuu</string>
<string name="new_update_format">aaaaaaa uuuuuu\n%s -&gt; %s</string> <string name="new_update_format">aaaaaaa uuuuuu
</resources> \n%s -&gt; %s</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%s aaou %d</string>
<string name="cast_format" formatted="true">oouaaahh %s</string>
<string name="next_episode_format" formatted="true">aaaaaaugh ouh %d uuoogahaaah ooua-h-ha</string>
<string name="next_episode_time_day_format" formatted="true">%daaa %duuu %dhhhg</string>
<string name="next_episode_time_hour_format" formatted="true">%daaaaaaauuhh %doouuahaaha</string>
<string name="next_episode_time_min_format" formatted="true">%dmmmm...</string>
<string name="player_speed_text_format" formatted="true">aaaaaaaaaaaaahh (%.2foouo)</string>
<string name="rated_format" formatted="true">aghaaaooo-ough %.1f</string>
<string name="filler" formatted="true">aaaaaaaaaaaaaaaaaaaaaaaaaaaaaooooohhuhuhuhuhuhhuhuhhhaaagaha-agagaoooo</string>
<string name="duration_format" formatted="true">%d ooaaaugha</string>
<string name="play_with_app_name">oooooh aaah aauuuggghauuh</string>
<string name="search_hint_site" formatted="true">aaoohhu %s aoouu</string>
<string name="browser">oooaaaauhgaaaa</string>
<string name="type_re_watching">aaaaaah-ooooooooouuagh</string>
<string name="action_add_to_bookmarks">auugggguuuuuuhhh uuuu hhhhhhhhuhggghggg</string>
<string name="play_trailer_button">ggaaaahhhhhhh gaauuuuuuuaaaau</string>
<string name="play_livestream_button">aaauuuuggggguu</string>
<string name="update_started">ooo aagg hhhh</string>
<string name="stream">ooo aagg hhhh</string>
<string name="sort_copy">uuuuhhhoouuooog ooaaahhhh</string>
<string name="sort_close">uuu ugggg</string>
<string name="sort_clear">ooo guggg ooh</string>
<string name="tracks">auuuooohaaaaagh</string>
<string name="library">uuuuuuuh aaaoo o</string>
<string name="category_updates">ooooooouuuua aa aaagh agh</string>
<string name="ova">AAAAUUUOH</string>
<string name="subscription_list_name">aoughoooaaaa</string>
<string name="movies_singular">oooouuuh</string>
<string name="category_player">ahaough aaouuuuh-h</string>
<string name="tv_series_singular">auughooo</string>
<string name="pref_category_player_layout">ooooooa aauoh</string>
<string name="subs_subtitle_languages">aaaaagh oouoo aaaaaaa</string>
<string name="category_account">aaaaaagh uuohuoh</string>
<string name="extensions">aaaaaauo agghhhhhhaoouu</string>
<string name="others">uuuuuuuuh</string>
<string name="test_log">ouaaahh</string>
<string name="subs_import_text" formatted="true">ooough aaoough aooou %s aaaa</string>
<string name="documentaries">ouooooouuuu oooooo</string>
<string name="livestreams">aaaaaaaaaaahhhgh-aooohoooo</string>
<string name="pref_category_extensions">aau aooooghaao</string>
<string name="provider_info_meta">aagh aaaaaaaaaaaa oooh, aaough, ooga oguuu aaaaaaaaaaa ooooooohghh a-a-aaauo</string>
<string name="resume_time_left" formatted="true">%dmmmmmm..
\naaaaooughugh</string>
<string name="asian_drama">aooohuohaaaa ooooagh</string>
<string name="subtitle_offset">oooooogh-aaaaaogh</string>
<string name="trailer">guuuaaaahhhhhhhaaa</string>
<string name="show_log_cat">woooaaahh ahahaaaauu 🦍</string>
<string name="nsfw">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOOOGGAGHAGHAAA</string>
<string name="clear_history">aoaaaaaoooghhh</string>
<string name="sort_save">oooooh uuaagh</string>
<string name="action_open_play">@string/home_play</string>
</resources>

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