Compare commits

...

959 commits

Author SHA1 Message Date
Gian-Fr
4d3ab40093
Updated SuperVideo extractor url from supervideo.tv to supervideo.cc (#1265) 2024-08-12 04:04:14 +02:00
int3debug
c4ccc5d351
feat(ui): settings for thumbnail on seekbar (#1256) 2024-08-09 00:34:26 +02:00
Phisher98
fcac19737c
Update VidSrcTo.kt Domain Changed (#1257)
Vidsrc is changed from to to cc
2024-08-07 15:47:15 +02:00
Luna712
77dc9f7484
Add support for progress on header downloads (#1238) 2024-08-05 20:57:51 +02:00
Luna712
f6a65f38db
Add support for Next Episode in downloads (#1228) 2024-08-05 20:49:04 +02:00
CranberrySoup
4d9a080341
Create jitpack.yml (#1248) 2024-08-04 15:59:57 +02:00
CranberrySoup
7936ccf5d3
Update FcastManager.kt (#1244) 2024-08-02 12:30:37 +02:00
recloudstream[bot]
15b5013e28 chore(locales): fix locale issues 2024-08-02 09:40:37 +00:00
Hosted Weblate
6f522828a4 Update translation files
Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Amharic)

Currently translated at 28.9% (213 of 737 strings)

Translated using Weblate (Filipino)

Currently translated at 14.6% (108 of 737 strings)

Translated using Weblate (Arabic (Najdi))

Currently translated at 60.2% (444 of 737 strings)

Translated using Weblate (Malay)

Currently translated at 21.9% (162 of 737 strings)

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

Currently translated at 100.0% (737 of 737 strings)

Translated using Weblate (Portuguese)

Currently translated at 95.2% (702 of 737 strings)

Translated using Weblate (Tamil)

Currently translated at 94.0% (693 of 737 strings)

Co-authored-by: Beabfekad Zikie <beabfekadz@gmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Sam Cooper <samcooper838@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/am/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ars/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fil/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ms/
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/ta/
Translation: Cloudstream/App
2024-08-02 11:40:24 +02:00
Cloudburst
ad727b96cf
[skip ci] match weblate xml style 2024-08-02 11:40:03 +02:00
recloudstream[bot]
67e278b2b7 chore(locales): fix locale issues 2024-08-02 09:20:56 +00:00
Hosted Weblate
7f1cba99e4 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (737 of 737 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (737 of 737 strings)

Translated using Weblate (German)

Currently translated at 98.6% (727 of 737 strings)

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

Currently translated at 100.0% (737 of 737 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (737 of 737 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (737 of 737 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (737 of 737 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (737 of 737 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (737 of 737 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (737 of 737 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (737 of 737 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (727 of 727 strings)

Translated using Weblate (Russian)

Currently translated at 96.9% (705 of 727 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (727 of 727 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (727 of 727 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (727 of 727 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (727 of 727 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (727 of 727 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (727 of 727 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (727 of 727 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Italian)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (German)

Currently translated at 99.7% (723 of 725 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (725 of 725 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 99.7% (723 of 725 strings)

Translated using Weblate (German)

Currently translated at 96.8% (702 of 725 strings)

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Assamese)

Currently translated at 97.3% (706 of 725 strings)

Translated using Weblate (Assamese)

Currently translated at 97.3% (706 of 725 strings)

Translated using Weblate (Maltese)

Currently translated at 31.5% (229 of 725 strings)

Translated using Weblate (Maltese)

Currently translated at 31.5% (229 of 725 strings)

Translated using Weblate (Nepali)

Currently translated at 32.0% (232 of 725 strings)

Translated using Weblate (Nepali)

Currently translated at 32.0% (232 of 725 strings)

Translated using Weblate (Nepali)

Currently translated at 32.0% (232 of 725 strings)

Translated using Weblate (Nepali)

Currently translated at 32.0% (232 of 725 strings)

Translated using Weblate (Afrikaans)

Currently translated at 29.1% (211 of 725 strings)

Translated using Weblate (Afrikaans)

Currently translated at 29.1% (211 of 725 strings)

Translated using Weblate (Afrikaans)

Currently translated at 29.1% (211 of 725 strings)

Translated using Weblate (Lithuanian)

Currently translated at 45.1% (327 of 725 strings)

Translated using Weblate (Lithuanian)

Currently translated at 45.1% (327 of 725 strings)

Translated using Weblate (Lithuanian)

Currently translated at 45.1% (327 of 725 strings)

Translated using Weblate (Lithuanian)

Currently translated at 45.1% (327 of 725 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Amharic)

Currently translated at 29.2% (212 of 725 strings)

Translated using Weblate (Amharic)

Currently translated at 29.2% (212 of 725 strings)

Translated using Weblate (Amharic)

Currently translated at 29.2% (212 of 725 strings)

Translated using Weblate (Amharic)

Currently translated at 29.2% (212 of 725 strings)

Translated using Weblate (Amharic)

Currently translated at 29.2% (212 of 725 strings)

Translated using Weblate (Amharic)

Currently translated at 29.2% (212 of 725 strings)

Translated using Weblate (Tigrinya)

Currently translated at 15.1% (110 of 725 strings)

Translated using Weblate (Tigrinya)

Currently translated at 15.1% (110 of 725 strings)

Translated using Weblate (Tigrinya)

Currently translated at 15.1% (110 of 725 strings)

Translated using Weblate (Tigrinya)

Currently translated at 15.1% (110 of 725 strings)

Translated using Weblate (Filipino)

Currently translated at 14.7% (107 of 725 strings)

Translated using Weblate (Filipino)

Currently translated at 14.7% (107 of 725 strings)

Translated using Weblate (Filipino)

Currently translated at 14.7% (107 of 725 strings)

Translated using Weblate (Filipino)

Currently translated at 14.7% (107 of 725 strings)

Translated using Weblate (Burmese)

Currently translated at 86.2% (625 of 725 strings)

Translated using Weblate (Burmese)

Currently translated at 86.2% (625 of 725 strings)

Translated using Weblate (Burmese)

Currently translated at 86.2% (625 of 725 strings)

Translated using Weblate (Burmese)

Currently translated at 86.2% (625 of 725 strings)

Translated using Weblate (Galician)

Currently translated at 36.4% (264 of 725 strings)

Translated using Weblate (Galician)

Currently translated at 36.4% (264 of 725 strings)

Translated using Weblate (Galician)

Currently translated at 36.4% (264 of 725 strings)

Translated using Weblate (Galician)

Currently translated at 36.4% (264 of 725 strings)

Translated using Weblate (Galician)

Currently translated at 36.4% (264 of 725 strings)

Translated using Weblate (Odia)

Currently translated at 36.4% (264 of 725 strings)

Translated using Weblate (Odia)

Currently translated at 36.4% (264 of 725 strings)

Translated using Weblate (Odia)

Currently translated at 36.4% (264 of 725 strings)

Translated using Weblate (Odia)

Currently translated at 36.4% (264 of 725 strings)

Translated using Weblate (Korean)

Currently translated at 97.1% (704 of 725 strings)

Translated using Weblate (Korean)

Currently translated at 97.1% (704 of 725 strings)

Translated using Weblate (Korean)

Currently translated at 97.1% (704 of 725 strings)

Translated using Weblate (Arabic (Najdi))

Currently translated at 61.1% (443 of 725 strings)

Translated using Weblate (Arabic (Najdi))

Currently translated at 61.1% (443 of 725 strings)

Translated using Weblate (Arabic (Najdi))

Currently translated at 61.1% (443 of 725 strings)

Translated using Weblate (Arabic (Najdi))

Currently translated at 61.1% (443 of 725 strings)

Translated using Weblate (Arabic (Najdi))

Currently translated at 61.1% (443 of 725 strings)

Translated using Weblate (Latvian)

Currently translated at 83.8% (608 of 725 strings)

Translated using Weblate (Latvian)

Currently translated at 83.8% (608 of 725 strings)

Translated using Weblate (Latvian)

Currently translated at 83.8% (608 of 725 strings)

Translated using Weblate (Latvian)

Currently translated at 83.8% (608 of 725 strings)

Translated using Weblate (Malay)

Currently translated at 22.6% (164 of 725 strings)

Translated using Weblate (Malay)

Currently translated at 22.6% (164 of 725 strings)

Translated using Weblate (Malay)

Currently translated at 22.6% (164 of 725 strings)

Translated using Weblate (Malay)

Currently translated at 22.6% (164 of 725 strings)

Translated using Weblate (Malay)

Currently translated at 22.6% (164 of 725 strings)

Translated using Weblate (Malay)

Currently translated at 22.6% (164 of 725 strings)

Translated using Weblate (Malay)

Currently translated at 22.6% (164 of 725 strings)

Translated using Weblate (Japanese)

Currently translated at 46.3% (336 of 725 strings)

Translated using Weblate (Japanese)

Currently translated at 46.3% (336 of 725 strings)

Translated using Weblate (Japanese)

Currently translated at 46.3% (336 of 725 strings)

Translated using Weblate (Japanese)

Currently translated at 46.3% (336 of 725 strings)

Translated using Weblate (Japanese)

Currently translated at 46.3% (336 of 725 strings)

Translated using Weblate (Japanese)

Currently translated at 46.3% (336 of 725 strings)

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

Currently translated at 47.0% (341 of 725 strings)

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

Currently translated at 47.0% (341 of 725 strings)

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

Currently translated at 47.0% (341 of 725 strings)

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

Currently translated at 47.0% (341 of 725 strings)

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

Currently translated at 47.0% (341 of 725 strings)

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

Currently translated at 47.0% (341 of 725 strings)

Translated using Weblate (Slovak)

Currently translated at 64.2% (466 of 725 strings)

Translated using Weblate (Slovak)

Currently translated at 64.2% (466 of 725 strings)

Translated using Weblate (Slovak)

Currently translated at 64.2% (466 of 725 strings)

Translated using Weblate (Slovak)

Currently translated at 64.2% (466 of 725 strings)

Translated using Weblate (Slovak)

Currently translated at 64.2% (466 of 725 strings)

Translated using Weblate (Slovak)

Currently translated at 64.2% (466 of 725 strings)

Translated using Weblate (Portuguese)

Currently translated at 96.8% (702 of 725 strings)

Translated using Weblate (Portuguese)

Currently translated at 96.8% (702 of 725 strings)

Translated using Weblate (Portuguese)

Currently translated at 96.8% (702 of 725 strings)

Translated using Weblate (Portuguese)

Currently translated at 96.8% (702 of 725 strings)

Translated using Weblate (Portuguese)

Currently translated at 96.8% (702 of 725 strings)

Translated using Weblate (Portuguese)

Currently translated at 96.8% (702 of 725 strings)

Translated using Weblate (Somali)

Currently translated at 77.5% (562 of 725 strings)

Translated using Weblate (Somali)

Currently translated at 77.5% (562 of 725 strings)

Translated using Weblate (Somali)

Currently translated at 77.5% (562 of 725 strings)

Translated using Weblate (Somali)

Currently translated at 77.5% (562 of 725 strings)

Translated using Weblate (Somali)

Currently translated at 77.5% (562 of 725 strings)

Translated using Weblate (Somali)

Currently translated at 77.5% (562 of 725 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 40.5% (294 of 725 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 40.5% (294 of 725 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 40.5% (294 of 725 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 40.5% (294 of 725 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 40.5% (294 of 725 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 40.5% (294 of 725 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 40.5% (294 of 725 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Esperanto)

Currently translated at 31.4% (228 of 725 strings)

Translated using Weblate (Esperanto)

Currently translated at 31.4% (228 of 725 strings)

Translated using Weblate (Esperanto)

Currently translated at 31.4% (228 of 725 strings)

Translated using Weblate (Esperanto)

Currently translated at 31.4% (228 of 725 strings)

Translated using Weblate (Esperanto)

Currently translated at 31.4% (228 of 725 strings)

Translated using Weblate (Esperanto)

Currently translated at 31.4% (228 of 725 strings)

Translated using Weblate (Esperanto)

Currently translated at 31.4% (228 of 725 strings)

Translated using Weblate (Persian)

Currently translated at 34.0% (247 of 725 strings)

Translated using Weblate (Persian)

Currently translated at 34.0% (247 of 725 strings)

Translated using Weblate (Persian)

Currently translated at 34.0% (247 of 725 strings)

Translated using Weblate (Persian)

Currently translated at 34.0% (247 of 725 strings)

Translated using Weblate (Persian)

Currently translated at 34.0% (247 of 725 strings)

Translated using Weblate (Persian)

Currently translated at 34.0% (247 of 725 strings)

Translated using Weblate (Hungarian)

Currently translated at 88.6% (643 of 725 strings)

Translated using Weblate (Hungarian)

Currently translated at 88.6% (643 of 725 strings)

Translated using Weblate (Hungarian)

Currently translated at 88.6% (643 of 725 strings)

Translated using Weblate (Hungarian)

Currently translated at 88.6% (643 of 725 strings)

Translated using Weblate (Hungarian)

Currently translated at 88.6% (643 of 725 strings)

Translated using Weblate (German)

Currently translated at 96.6% (701 of 725 strings)

Translated using Weblate (German)

Currently translated at 96.6% (701 of 725 strings)

Translated using Weblate (German)

Currently translated at 96.6% (701 of 725 strings)

Translated using Weblate (German)

Currently translated at 96.6% (701 of 725 strings)

Translated using Weblate (German)

Currently translated at 96.6% (701 of 725 strings)

Translated using Weblate (Spanish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Spanish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Spanish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Russian)

Currently translated at 97.2% (705 of 725 strings)

Translated using Weblate (Russian)

Currently translated at 97.2% (705 of 725 strings)

Translated using Weblate (Russian)

Currently translated at 97.2% (705 of 725 strings)

Translated using Weblate (Russian)

Currently translated at 97.2% (705 of 725 strings)

Translated using Weblate (Russian)

Currently translated at 97.2% (705 of 725 strings)

Translated using Weblate (Russian)

Currently translated at 97.2% (705 of 725 strings)

Translated using Weblate (Kannada)

Currently translated at 32.4% (235 of 725 strings)

Translated using Weblate (Kannada)

Currently translated at 32.4% (235 of 725 strings)

Translated using Weblate (Kannada)

Currently translated at 32.4% (235 of 725 strings)

Translated using Weblate (Kannada)

Currently translated at 32.4% (235 of 725 strings)

Translated using Weblate (Kannada)

Currently translated at 32.4% (235 of 725 strings)

Translated using Weblate (Kannada)

Currently translated at 32.4% (235 of 725 strings)

Translated using Weblate (Kannada)

Currently translated at 32.4% (235 of 725 strings)

Translated using Weblate (Urdu)

Currently translated at 96.8% (702 of 725 strings)

Translated using Weblate (Urdu)

Currently translated at 96.8% (702 of 725 strings)

Translated using Weblate (Urdu)

Currently translated at 96.8% (702 of 725 strings)

Translated using Weblate (Urdu)

Currently translated at 96.8% (702 of 725 strings)

Translated using Weblate (Urdu)

Currently translated at 96.8% (702 of 725 strings)

Translated using Weblate (Tamil)

Currently translated at 97.3% (706 of 725 strings)

Translated using Weblate (Tamil)

Currently translated at 97.3% (706 of 725 strings)

Translated using Weblate (Tamil)

Currently translated at 97.3% (706 of 725 strings)

Translated using Weblate (Tamil)

Currently translated at 97.3% (706 of 725 strings)

Translated using Weblate (Hebrew)

Currently translated at 86.2% (625 of 725 strings)

Translated using Weblate (Hebrew)

Currently translated at 86.2% (625 of 725 strings)

Translated using Weblate (Hebrew)

Currently translated at 86.2% (625 of 725 strings)

Translated using Weblate (Hebrew)

Currently translated at 86.2% (625 of 725 strings)

Translated using Weblate (Hebrew)

Currently translated at 86.2% (625 of 725 strings)

Translated using Weblate (Hebrew)

Currently translated at 86.2% (625 of 725 strings)

Translated using Weblate (Hebrew)

Currently translated at 86.2% (625 of 725 strings)

Translated using Weblate (Bengali)

Currently translated at 63.5% (461 of 725 strings)

Translated using Weblate (Bengali)

Currently translated at 63.5% (461 of 725 strings)

Translated using Weblate (Bengali)

Currently translated at 63.5% (461 of 725 strings)

Translated using Weblate (Bengali)

Currently translated at 63.5% (461 of 725 strings)

Translated using Weblate (Bengali)

Currently translated at 63.5% (461 of 725 strings)

Translated using Weblate (Bengali)

Currently translated at 63.5% (461 of 725 strings)

Translated using Weblate (Bengali)

Currently translated at 63.5% (461 of 725 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.5% (707 of 725 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.5% (707 of 725 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.5% (707 of 725 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.5% (707 of 725 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 97.5% (707 of 725 strings)

Translated using Weblate (Vietnamese)

Currently translated at 98.6% (715 of 725 strings)

Translated using Weblate (Vietnamese)

Currently translated at 98.6% (715 of 725 strings)

Translated using Weblate (Vietnamese)

Currently translated at 98.6% (715 of 725 strings)

Translated using Weblate (Vietnamese)

Currently translated at 98.6% (715 of 725 strings)

Translated using Weblate (Vietnamese)

Currently translated at 98.6% (715 of 725 strings)

Translated using Weblate (Turkish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Turkish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Turkish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Turkish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Turkish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Tagalog)

Currently translated at 48.4% (351 of 725 strings)

Translated using Weblate (Tagalog)

Currently translated at 48.4% (351 of 725 strings)

Translated using Weblate (Tagalog)

Currently translated at 48.4% (351 of 725 strings)

Translated using Weblate (Tagalog)

Currently translated at 48.4% (351 of 725 strings)

Translated using Weblate (Tagalog)

Currently translated at 48.4% (351 of 725 strings)

Translated using Weblate (Tagalog)

Currently translated at 48.4% (351 of 725 strings)

Translated using Weblate (Swedish)

Currently translated at 97.7% (709 of 725 strings)

Translated using Weblate (Swedish)

Currently translated at 97.7% (709 of 725 strings)

Translated using Weblate (Swedish)

Currently translated at 97.7% (709 of 725 strings)

Translated using Weblate (Swedish)

Currently translated at 97.7% (709 of 725 strings)

Translated using Weblate (Swedish)

Currently translated at 97.7% (709 of 725 strings)

Translated using Weblate (Romanian)

Currently translated at 97.5% (707 of 725 strings)

Translated using Weblate (Romanian)

Currently translated at 97.5% (707 of 725 strings)

Translated using Weblate (Romanian)

Currently translated at 97.5% (707 of 725 strings)

Translated using Weblate (Polish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Polish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Polish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Polish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Polish)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 82.3% (597 of 725 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 82.3% (597 of 725 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 82.3% (597 of 725 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 82.3% (597 of 725 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 82.3% (597 of 725 strings)

Translated using Weblate (Dutch)

Currently translated at 90.3% (655 of 725 strings)

Translated using Weblate (Dutch)

Currently translated at 90.3% (655 of 725 strings)

Translated using Weblate (Dutch)

Currently translated at 90.3% (655 of 725 strings)

Translated using Weblate (Dutch)

Currently translated at 90.3% (655 of 725 strings)

Translated using Weblate (Dutch)

Currently translated at 90.3% (655 of 725 strings)

Translated using Weblate (Malayalam)

Currently translated at 47.1% (342 of 725 strings)

Translated using Weblate (Malayalam)

Currently translated at 47.1% (342 of 725 strings)

Translated using Weblate (Malayalam)

Currently translated at 47.1% (342 of 725 strings)

Translated using Weblate (Malayalam)

Currently translated at 47.1% (342 of 725 strings)

Translated using Weblate (Malayalam)

Currently translated at 47.1% (342 of 725 strings)

Translated using Weblate (Malayalam)

Currently translated at 47.1% (342 of 725 strings)

Translated using Weblate (Malayalam)

Currently translated at 47.1% (342 of 725 strings)

Translated using Weblate (Macedonian)

Currently translated at 97.3% (706 of 725 strings)

Translated using Weblate (Macedonian)

Currently translated at 97.3% (706 of 725 strings)

Translated using Weblate (Macedonian)

Currently translated at 97.3% (706 of 725 strings)

Translated using Weblate (Macedonian)

Currently translated at 97.3% (706 of 725 strings)

Translated using Weblate (Italian)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Italian)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Italian)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Italian)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Italian)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Italian)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Indonesian)

Currently translated at 97.7% (709 of 725 strings)

Translated using Weblate (Indonesian)

Currently translated at 97.7% (709 of 725 strings)

Translated using Weblate (Indonesian)

Currently translated at 97.7% (709 of 725 strings)

Translated using Weblate (Indonesian)

Currently translated at 97.7% (709 of 725 strings)

Translated using Weblate (Croatian)

Currently translated at 98.0% (711 of 725 strings)

Translated using Weblate (Croatian)

Currently translated at 98.0% (711 of 725 strings)

Translated using Weblate (Hindi)

Currently translated at 41.9% (304 of 725 strings)

Translated using Weblate (Hindi)

Currently translated at 41.9% (304 of 725 strings)

Translated using Weblate (Hindi)

Currently translated at 41.9% (304 of 725 strings)

Translated using Weblate (Hindi)

Currently translated at 41.9% (304 of 725 strings)

Translated using Weblate (Hindi)

Currently translated at 41.9% (304 of 725 strings)

Translated using Weblate (Hindi)

Currently translated at 41.9% (304 of 725 strings)

Translated using Weblate (Hindi)

Currently translated at 41.9% (304 of 725 strings)

Translated using Weblate (French)

Currently translated at 96.9% (703 of 725 strings)

Translated using Weblate (French)

Currently translated at 96.9% (703 of 725 strings)

Translated using Weblate (French)

Currently translated at 96.9% (703 of 725 strings)

Translated using Weblate (French)

Currently translated at 96.9% (703 of 725 strings)

Translated using Weblate (French)

Currently translated at 96.9% (703 of 725 strings)

Translated using Weblate (French)

Currently translated at 96.9% (703 of 725 strings)

Translated using Weblate (Greek)

Currently translated at 97.6% (708 of 725 strings)

Translated using Weblate (Greek)

Currently translated at 97.6% (708 of 725 strings)

Translated using Weblate (Greek)

Currently translated at 97.6% (708 of 725 strings)

Translated using Weblate (Greek)

Currently translated at 97.6% (708 of 725 strings)

Translated using Weblate (Czech)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Czech)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Bulgarian)

Currently translated at 93.7% (680 of 725 strings)

Translated using Weblate (Bulgarian)

Currently translated at 93.7% (680 of 725 strings)

Translated using Weblate (Bulgarian)

Currently translated at 93.7% (680 of 725 strings)

Translated using Weblate (Bulgarian)

Currently translated at 93.7% (680 of 725 strings)

Translated using Weblate (Bulgarian)

Currently translated at 93.7% (680 of 725 strings)

Translated using Weblate (Arabic)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Arabic)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Arabic)

Currently translated at 99.1% (719 of 725 strings)

Translated using Weblate (Arabic)

Currently translated at 99.1% (719 of 725 strings)

Co-authored-by: --//-- <htetoh2006@outlook.com>
Co-authored-by: Aayush Shah <shahaayush999@gmail.com>
Co-authored-by: Abinanthankv <abinanthankv@protonmail.com>
Co-authored-by: Ahmed seif al-nasr <ahmdsyfalnsr2@gmail.com>
Co-authored-by: Alessandro Burzio <alebu3007@gmail.com>
Co-authored-by: Alex Georgiou <alexandrosgeorgiou35@gmail.com>
Co-authored-by: Alexander Svärd <genc.demiri@hotmail.com>
Co-authored-by: Alexandru <negrualexandru52@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: Astrid <github@astrid.exposed>
Co-authored-by: Aydın <mtahaydn@gmail.com>
Co-authored-by: Beabfekad Zikie <beabfekadz@gmail.com>
Co-authored-by: Cait Martin Newnham <85128509+helloiamcait@users.noreply.github.com>
Co-authored-by: Carlos Luiz <ecarlos-luiz@hotmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Colgrave <hanqixu.blogs@simplelogin.co>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: DarkOrbFX <darkorbfx@gmail.com>
Co-authored-by: Deleted User <Skrripy@users.noreply.hosted.weblate.org>
Co-authored-by: Deleted User <noreply+53776@weblate.org>
Co-authored-by: Don Apis <apisapisapis@gmail.com>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: FastAct <alex.rijckaert@gmail.com>
Co-authored-by: Fedorov Alexei <aleksejfedorov963@gmail.com>
Co-authored-by: Felipe Nogueira <contato.fnog@gmail.com>
Co-authored-by: Filip Drogrishki <alekfilip425@gmail.com>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Fqwe1 <Fqwe1@users.noreply.hosted.weblate.org>
Co-authored-by: Giuseppe Terrana <terranagiuseppe03@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Itsmechinmoy <gituborah280@gmail.com>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: Joel Brink <joel.brink.handy@gmail.com>
Co-authored-by: Jose Delvani <jsdelvani@users.noreply.hosted.weblate.org>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: Kaan Çetin <cetinkaan895@hotmail.com>
Co-authored-by: Kai <rafahdamin@gmail.com>
Co-authored-by: Kardi Demha <kardi.demha@gmail.com>
Co-authored-by: LagradOst <46196380+Blatzar@users.noreply.github.com>
Co-authored-by: LiJu09 <lisojuraj@gmail.com>
Co-authored-by: Ma Ue <MattiaU59@gmail.com>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Matthaiks <kitynska@gmail.com>
Co-authored-by: Michael John Scerri <michaeljscerri@gmail.com>
Co-authored-by: Muhammad Fahad Khan <itxmfahadkhan@gmail.com>
Co-authored-by: Márkó <gost1336@gmail.com>
Co-authored-by: Osten <11805592+LagradOst@users.noreply.github.com>
Co-authored-by: Overmet15 <overmet15@gmail.com>
Co-authored-by: PiterDev <piterzdev@gmail.com>
Co-authored-by: Pizza Party <paol.m@proton.me>
Co-authored-by: Prathap Rathod <prathap0144@gmail.com>
Co-authored-by: Radoslav Vasilev Vasilev <fifata@gmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Roi Gabay <roigby@gmail.com>
Co-authored-by: Rudy Tantono <rudzlong@gmail.com>
Co-authored-by: Sam Cooper <samcooper838@gmail.com>
Co-authored-by: Samuel Gadiel <samuelgadiel@gmail.com>
Co-authored-by: Sandyran <sandyran@protonmail.com>
Co-authored-by: Sarlay <raphmd0@gmail.com>
Co-authored-by: SeMih Budur <zaaf10@hotmail.de>
Co-authored-by: Shafici Isxariifshe <mega12xhaphiee@gmail.com>
Co-authored-by: ShareASmile <ShareASmile@users.noreply.hosted.weblate.org>
Co-authored-by: Sintayew Gashaw <sintayewgashaw4@gmail.com>
Co-authored-by: SleepyOwl <artem726artem@gmail.com>
Co-authored-by: Subham Jena <subhamjena8465@gmail.com>
Co-authored-by: Sufyan Zahoor Jutt <sufyanpahore@gmail.com>
Co-authored-by: TZVS <akyasan@tuta.io>
Co-authored-by: The Initiator <eithansten@gmail.com>
Co-authored-by: TubaApollo <86665265+TubaApollo@users.noreply.github.com>
Co-authored-by: Veselin Ivanov <slavitransbg@gmail.com>
Co-authored-by: Vrwi <jurgisbums@gmail.com>
Co-authored-by: Walter H <walter75@gmail.com>
Co-authored-by: XblateX <blate@users.noreply.hosted.weblate.org>
Co-authored-by: ZsoltiHUB <zsoltizsolti043@gmail.com>
Co-authored-by: akku vijay <akkuvijay@duck.com>
Co-authored-by: dabao1955 <dabao1955@163.com>
Co-authored-by: duckling <salmanfc.bd@gmail.com>
Co-authored-by: edgolron <edgolron@tutanota.com>
Co-authored-by: eightyy8 <oliver.kha@pm.me>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: george kitsoukakis <norhorn@gmail.com>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: kaajjo <claymanoff@gmail.com>
Co-authored-by: l <thisuserooo@gmail.com>
Co-authored-by: ngocanhtve <ngocanh.tve@gmail.com>
Co-authored-by: phlostically <phlostically@mailinator.com>
Co-authored-by: sonacore <sonacore@gmail.com>
Co-authored-by: stojkovskistefan <stefanstojkovski@gmail.com>
Co-authored-by: tuan041 <30403510+tuan041@users.noreply.github.com>
Co-authored-by: tuan041 <tuananh163025ttt@gmail.com>
Co-authored-by: Ömer Faruk Sancak <keyiflerolsun@gmail.com>
Co-authored-by: Влад Николаев <vladnic1990@gmail.com>
Co-authored-by: தமிழ்நேரம் <anishprabu.t@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/af/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/am/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/apc/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ars/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/as/
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/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/fil/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/gl/
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/ko/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/lt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/lv/
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/ms/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/mt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/my/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ne/
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/or/
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/pt_BR/
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/ti/
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
2024-08-02 11:20:40 +02:00
recloudstream[bot]
ff29fe6ee6 chore(locales): fix locale issues 2024-08-01 22:14:37 +00:00
Luna712
aac2311722
Fix TV focus issues for add repo input (#1239) 2024-08-02 00:14:23 +02:00
Ömer Faruk Sancak
60e3c48aca
Helper Added: CryptoJS (#1237) 2024-08-02 00:13:54 +02:00
CranberrySoup
14dd418652
Update build.gradle.kts (#1240) 2024-08-01 03:28:47 +02:00
Cloudburst
5012821216
[skip ci] add trailing nline to strings to be consistent with weblate 2024-07-31 10:44:25 +02:00
Luna712
ab379ab31c
Support for multi deleting downloads and other major improvements/fixes (#1177) 2024-07-30 20:54:54 +02:00
Luna712
8fcb3e3121
Fix cast recycler scrolling (#1221) 2024-07-30 20:45:25 +02:00
Ömer Faruk Sancak
30adb1cd9d
fixed: Test Search & VidMoxy, RapidVid extractors (#1219) 2024-07-30 20:38:51 +02:00
KingLucius
63e27c2ea5
Fix Trailers on API<33 (#1226)
Recent NewPipeExtractor updates pushed minimum sdk to 33 which needs desugar_jdk_libs_nio
2024-07-30 20:16:11 +02:00
epireyn
b2f08847e1
Add system dark theme (#1208) 2024-07-29 01:01:45 +02:00
epireyn
150ad5fc9f
Add sorting by release date (#1206) 2024-07-29 01:00:44 +02:00
firelight
82f8ab489e
Fix prerelease test function 2024-07-29 00:58:35 +02:00
epireyn
04dda008c4
Clean up and mark questionable code issues (#1209) 2024-07-29 00:39:04 +02:00
Luna712
0aa48f335a
Fix subscription icon displaying for movie types in result previews (#1222) 2024-07-29 00:26:22 +02:00
Luna712
a28ee41368
Fix for navigation UI bug (#1220) 2024-07-28 23:59:37 +02:00
Osten
2fc279f4ae
Bump 4.4.0 2024-07-25 20:26:21 +02:00
epireyn
15d2d21631
Add the option to hide video controls (#1210) 2024-07-25 20:25:17 +02:00
KingLucius
e3ff1cf455
feat(UI): Show Episode Runtime (#1207) 2024-07-25 20:23:49 +02:00
KingLucius
dfd127265a
Trailers Fix (#1213) 2024-07-25 20:23:31 +02:00
firelight
c8a863e332
Fixed ExampleInstrumentedTest 2024-07-24 22:38:16 +02:00
RowdyRushya
0c418fdf9b
Updated VidSrc encryption methods (#1205) 2024-07-21 00:06:04 +02:00
firelight
4c7379c766
Revert #979 Episode download cache 2024-07-20 19:14:11 +02:00
KingLucius
bb8144a52e
feat(TV UI): Player's Top controls redesign (#1203) 2024-07-19 19:35:29 +02:00
firelight
073af50f5f
fixed html plot in preview 2024-07-19 18:28:36 +02:00
firelight
63465ed7a9
fix autohide 2024-07-19 18:24:06 +02:00
RowdyRushya
12de924559
updating vidplay encryption method (#1202) 2024-07-19 18:10:34 +02:00
firelight
627dd45309
0bytes downloads fix 2024-07-18 02:02:35 +02:00
KingLucius
a157115cfa
feat(Subtitles): SubSource subtitles provider (#1199) 2024-07-15 17:15:59 +02:00
IndusAryan
694193fa3e
refactor(fix): result sync, fix slider theme and trailer fix (#1187) 2024-07-15 17:10:41 +02:00
RowdyRushya
febb843424
Fix VidSrcTo extractor (#1198) 2024-07-15 17:06:20 +02:00
firelight
8be8e54746
Fixed log 2024-07-08 23:17:25 +02:00
Ömer Faruk Sancak
e86c926c30
Extractor: added Pichive & Sobreatsesuyp (#1184) 2024-07-08 22:59:02 +02:00
KingLucius
145c42f1c8
feat(UI): Use same Episode holder size (#1180) 2024-07-05 18:10:58 +02:00
KingLucius
9b1ac5fc28
feat(Trakt): Skip specials season for next airing (#1181) 2024-07-05 18:05:32 +02:00
KingLucius
699a6979a5
feat(TV UI): Fix clone site focus (#1179) 2024-07-05 18:04:32 +02:00
firelight
e1d4a46309
bugfix on lib startup 2024-07-05 15:26:44 +02:00
Luna712
c1b5f5c128
Fix download button display bug in adapter (#1175) 2024-07-04 23:51:07 +02:00
firelight
e5c9e96c83
fix filesystem 2024-07-04 22:33:21 +02:00
CranberrySoup
02b956940a
Port large parts of the API to crossplatform (#1163) 2024-07-04 20:07:01 +02:00
Luna712
03b8b6e637
Major performance and bug fixes to downloads (#1164) 2024-07-04 19:37:08 +02:00
Luna712
29ec554334
Fix an IllegalStateException crash (#1171) 2024-07-04 02:39:26 +02:00
CranberrySoup
5f64e40a7e
Fix debug exceptions in releases (#1168) 2024-07-03 03:42:10 +02:00
Cloudburst
d17111c1c1
[skip ci] update issue templates 2024-07-02 22:40:36 +02:00
IndusAryan
a5582a7a67
feat(ui): ability to play any local video from files using file chooser (#1158) 2024-07-01 23:34:36 +02:00
CranberrySoup
1a05651510
Fix nsfw visibility (#1162) 2024-06-30 17:08:06 +02:00
IndusAryan
ad27eb3b0e
feat(ui): show currently syncing api logo on navigation bar and rail (#1146) 2024-06-30 16:17:30 +02:00
Ömer Faruk Sancak
6b93af5803
Extractor: VideoSeyred » Referer Fix (#1159) 2024-06-28 17:24:57 +02:00
Phisher98
55a0eb66cb
MyCloud New Domain Adding Extractor for it (#1157) 2024-06-26 21:06:46 +02:00
recloudstream[bot]
b776642775 chore(locales): fix locale issues 2024-06-24 19:10:15 +00:00
Cloudburst
09fe9873cf
fix locales.py 2024-06-24 21:09:41 +02:00
Weblate (bot)
0d40b5ebe3
Translations update from Hosted Weblate (#1042)
Co-authored-by: Aaditya Bhandari <bhandariaaditya4@gmail.com>
Co-authored-by: Adrian Hermida <adrian.hermida.baloira@gmail.com>
Co-authored-by: Akhlak Ur Rahman <akhlak.pro.red@gmail.com>
Co-authored-by: Alexander Svärd <genc.demiri@hotmail.com>
Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Andre Costa <andrecaeu@gmail.com>
Co-authored-by: Antonio N <antonioenpidev@gmail.com>
Co-authored-by: Azgar <azgaresncf@gmail.com>
Co-authored-by: Colgrave <hanqixu.blogs@simplelogin.co>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: Eji-san <ejierubani@gmail.com>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Evgeniy Khramov <65224669+thejenja@users.noreply.github.com>
Co-authored-by: FUTURE <alwaysoutsmartyou@gmail.com>
Co-authored-by: Fikri Akbar <akbarfikri1221@gmail.com>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Huzaifah Asif <huzaifahasif3@gmail.com>
Co-authored-by: Itsmechinmoy <gituborah280@gmail.com>
Co-authored-by: Jose Delvani <delvani.eletricista@gmail.com>
Co-authored-by: Konstantinos Tranoudis <kontranpro@gmail.com>
Co-authored-by: Krisna A. Prayoga <krisnaadiprayoga@gmail.com>
Co-authored-by: Luna712 <142361265+Luna712@users.noreply.github.com>
Co-authored-by: Marian Turba <puki247@gmail.com>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Matthaiks <kitynska@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Mæve Rey <mrey@users.noreply.hosted.weblate.org>
Co-authored-by: Naga <yz2000.pro@gmail.com>
Co-authored-by: Nicoara Alex <alex.nicoara@yahoo.com>
Co-authored-by: Nuno Ferreira <nuno.f.gamer@gmail.com>
Co-authored-by: Only1337 <ymurathanusta@gmail.com>
Co-authored-by: Pizza Party <paol.m@proton.me>
Co-authored-by: Putra Iskandar <piskndar@gmail.com>
Co-authored-by: Qareen Skoll <qareen101@protonmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: SeMih Budur <zaaf10@hotmail.de>
Co-authored-by: Semih <semihbrn10@gmail.com>
Co-authored-by: Sufyan Zahoor Jutt <sufyanpahore@gmail.com>
Co-authored-by: Waheed Nazir <mwaheednazir8@gmail.com>
Co-authored-by: Walter H <walter75@gmail.com>
Co-authored-by: Wei-Cheng Yeh (IID) <iid@ccns.ncku.edu.tw>
Co-authored-by: amir <amirasyraf32@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: hugoalh <hugoalh@users.noreply.hosted.weblate.org>
Co-authored-by: ngocanhtve <ngocanh.tve@gmail.com>
Co-authored-by: programutox <programutox@disroot.org>
Co-authored-by: rwi <isaac.royallll@gmail.com>
Co-authored-by: stojkovskistefan <stefanstojkovski@gmail.com>
Co-authored-by: streaming s <fsrmllll1111@gmail.com>
Co-authored-by: tuan041 <tuananh163025ttt@gmail.com>
Co-authored-by: user0020 <855309c256@gmail.com>
Co-authored-by: ΣΤΑΥΡΟΣ ΔΑΛΙΑΚΟΠΟΥΛΟΣ <stavros.daliakopoulos@gmail.com>
Co-authored-by: Сергей (MrSabin) <sabin.21011986@gmail.com>
Co-authored-by: தமிழ்நேரம் <anishprabu.t@gmail.com>
Co-authored-by: 电棍老板 <qwertyuiop9296@outlook.com>
Co-authored-by: 구병우 <dodamby@ajou.ac.kr>
2024-06-24 21:03:09 +02:00
CranberrySoup
9ca1d02bdc
Improve tests (#1142) 2024-06-24 20:05:34 +02:00
Luna712
b06d9f224d
Downloads: performance improvements and merge adapters (#1145) 2024-06-24 20:04:45 +02:00
imgbot[bot]
b9746c2b17
[ImgBot] Optimize images (#1144)
/app/src/main/res/drawable/example_qr.png -- 45.27kb -> 1.28kb (97.17%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2024-06-19 19:03:55 +02:00
IndusAryan
c71d5d8add
feat(ui): new dialog on adding repository and auto redirection (#1025) 2024-06-19 16:36:40 +02:00
KingLucius
afa178a63a
feat(TV UI): Accounts PIN login support (#1123) 2024-06-19 16:06:08 +02:00
Misael Jiménez
b702b7b1ec
Fix DoodExtractor. (#1134)
Fix StreamWishExtractor
2024-06-19 15:40:23 +02:00
KingLucius
bda6673cfd
feat(Extensions): Consider time zone in Trakt durations (#1140) 2024-06-18 23:24:35 +02:00
KingLucius
7a0cd07dc1
feat(TV UI): Press Right to focus save on Logcat (#1136) 2024-06-18 05:02:32 +02:00
KingLucius
30d223cfe3
feat(UI): Reorganize Settings (#1137)
- Accounts Section & Remove "account" from title.
- Security Section for Biometric that is hidden on TV.
- Move "send logs" to "Action" section.
2024-06-17 03:01:14 +02:00
Stormunblessed
4c061edd7c
goodstream (#1133) 2024-06-15 23:47:30 +02:00
KingLucius
4c95610238
feat(UI): Hide Platform's not related settings (#1128) 2024-06-09 16:38:08 +02:00
RowdyRushya
3345326cb2
Extractor: VidSrcTo: better handling of runtime errors (#1121) 2024-06-08 21:19:29 +02:00
KingLucius
607a4510b6
feat(Extensions): Trakt season names remove (#1124) 2024-06-08 21:08:35 +02:00
KingLucius
f775c1725d
feat(TV UI): Subtitles Filter button focus fix (#1125) 2024-06-08 21:07:33 +02:00
firelight
7eec0eff02
Revert "chore: refactor gradlelocalproperties and update gradle plugin (#957)" (#1120)
This reverts commit 358a20eb77.
2024-06-05 23:41:06 +02:00
IndusAryan
358a20eb77
chore: refactor gradlelocalproperties and update gradle plugin (#957) 2024-06-05 23:18:33 +02:00
int3debug
0391a3b89c
feature(ui): added wikipedia to links (#1119) 2024-06-05 23:09:05 +02:00
int3debug
9bebfe4590
feature(ui): hide NSFW plugins (#1117)
Hide NSFW plugins if Settings / Providers NSFW is disabled
2024-06-05 23:07:54 +02:00
KingLucius
b3e3dadc72
Remove IndexSubtitles provider (#1111) 2024-06-01 18:17:41 +02:00
KingLucius
b87fdfbf85
feat(TV UI): Account switch focus fix (#1112) 2024-06-01 18:16:42 +02:00
KingLucius
dff56026de
SubDL Account login support (#1101) 2024-05-29 22:39:55 +02:00
IndusAryan
5502e478c4
chore: update material,kotlin compiler,newpipe extractor,rhino-js,guava,corektx (#1091) 2024-05-27 16:05:56 +02:00
CranberrySoup
960f8449b7
Update ResultViewModel2.kt (#1102) 2024-05-27 15:54:51 +02:00
Ömer Faruk Sancak
d0852449a5
Extractor: Added FourPichive (#1103)
🕊
2024-05-27 15:54:25 +02:00
KingLucius
e697bf7554
Next Airing episode support in Trakt meta provider (#1072) 2024-05-21 22:06:28 +02:00
Luna712
db2bf5e7be
Remove subscene (#1096)
subscene.com just shows a "Subscene is closed" message now.
2024-05-19 12:43:46 +02:00
KingLucius
469a71236b
SubDL subtitles provider (#1082) 2024-05-18 18:15:23 +02:00
CranberrySoup
4d5cd288ab
Ported more files for multiplatform (#1056) 2024-05-18 13:47:12 +02:00
KingLucius
af828de8d5
feat(TV UI: Fix online subtitles dialog focus (#1085) 2024-05-18 13:41:37 +02:00
CranberrySoup
ee4d1dedc5
Add basic fcast support (#1084) 2024-05-09 21:46:54 +02:00
KingLucius
f1cc4db89c
Show Season number for next airing episode (#1071) 2024-05-09 17:08:18 +02:00
b4byhuey
3874cb9f9d
Update Dailymotion Extractor (#1081) 2024-05-09 17:06:33 +02:00
phisher98
0a5399d9b6
Updates and Chillx Extractor Updated (#1065) 2024-05-05 01:00:42 +02:00
KingLucius
71bd48f493
feat(ui): Hide Downloads & Settings Back button on TV (#1074) 2024-05-04 13:17:52 +02:00
KingLucius
83c473d9f8
More external Ids in Trakt meta provider (#1075) 2024-05-04 13:16:09 +02:00
RowdyRushya
c28a3cb987
Extractor: new VidSrcTo extractor (#1044) 2024-05-04 13:15:34 +02:00
int3debug
d3828eeafe
refact: rename logcat file (#1061)
Rename logcat file to prevent override
2024-05-02 23:59:05 +02:00
int3debug
c07e6d3222
hotfix: Remove resume information (#1063) 2024-05-02 23:58:32 +02:00
KingLucius
949b5830b6
feat(ui): Fix downloads focus on TV (#1066) 2024-05-01 19:29:49 +02:00
b4byhuey
ff1ffbeb83
Update Voe.kt (#1062) 2024-04-28 21:42:38 +02:00
Luna712
138e1a1f0e
Don't check year when checking duplicates if year is empty (#1060)
Some sources don't use year which makes this not match when it really should match
2024-04-27 22:40:15 +02:00
KingLucius
004c481a5e
feat(ui): Episode Air date & Upcoming countdown (#1058) 2024-04-27 18:11:22 +02:00
b4byhuey
e2946cad6b
Added Vidguard Extractor (#1053) 2024-04-27 18:00:40 +02:00
int3debug
e6b9d621f9
feat(ui): added option to reset sub delay (#1041) 2024-04-22 17:00:27 +02:00
KingLucius
0019f85501
Trakt meta provider for extensions (#1026) 2024-04-22 16:59:14 +02:00
IndusAryan
0744189020
feat(ui): show account name and image on main settings page (#1001) 2024-04-22 16:48:54 +02:00
Ömer Faruk Sancak
4399a612df
Update Vidmoly.kt (#1051) 2024-04-22 01:14:36 +02:00
KingLucius
e01ff4d843
Fix NewPipeExtractor lib path (#1050) 2024-04-22 01:13:55 +02:00
KingLucius
6cef9f7ea2
Filtering first unwatched episode respects watched state (#1049) 2024-04-20 22:18:49 +02:00
int3debug
9a18ef6411
bugfix: fixing regex special chars break it (#1047) 2024-04-17 23:48:33 +02:00
CranberrySoup
6df3ef14f6
First steps for multiplatform API (#1003)
* First steps for multiplatform api

* Buildconfig testing

* Fix publishing and classes.jar

* Update build.gradle.kts
2024-04-16 23:07:28 +02:00
int3debug
5db541d7cc
feat(ui): added reset button to subtitle delay (#1040) 2024-04-14 02:13:12 +02:00
CranberrySoup
aa8972870c
Show download size on videos (#1038) 2024-04-14 00:45:58 +02:00
Rushikesh Chavan
afdc4988ac
Extractor: Update Vidplay Extractor (#1036) 2024-04-13 19:52:08 +02:00
KingLucius
e6c111532d
Defaults Play button to first unwatched Episode (#1035) 2024-04-13 19:51:39 +02:00
recloudstream[bot]
ffa7b0248a chore(locales): fix locale issues 2024-04-10 15:26:36 +00:00
firelight
c13d290377
Merge pull request #1012 from recloudstream/weblate
Translations update from Hosted Weblate
2024-04-10 17:26:17 +02:00
Hosted Weblate
1bf7e14eab
Merge remote-tracking branch 'origin/master' 2024-04-10 17:24:19 +02:00
phisher98
145ceea50f
Created vtbe and EPlay Extractor (#1014) 2024-04-10 17:24:15 +02:00
Hosted Weblate
2fad760426
Merge remote-tracking branch 'origin/master' 2024-04-10 17:16:09 +02:00
KingLucius
ff0dea3fbb
Fix focus for Tracks selection on TV (#1030) 2024-04-10 17:16:04 +02:00
Hosted Weblate
44e5b86176
Merge remote-tracking branch 'origin/master' 2024-04-10 17:14:51 +02:00
KingLucius
d8f89df163
Show player controls on pressing Pad Down (#1031) 2024-04-10 17:14:47 +02:00
Hosted Weblate
a74563d003
Translated using Weblate (Russian)
Currently translated at 100.0% (697 of 697 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (697 of 697 strings)

Translated using Weblate (Vietnamese)

Currently translated at 75.0% (3 of 4 strings)

Translated using Weblate (Vietnamese)

Currently translated at 98.7% (688 of 697 strings)

Translated using Weblate (French)

Currently translated at 96.8% (675 of 697 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (697 of 697 strings)

Translated using Weblate (Persian)

Currently translated at 34.7% (242 of 697 strings)

Translated using Weblate (Vietnamese)

Currently translated at 98.8% (689 of 697 strings)

Translated using Weblate (Russian)

Currently translated at 97.1% (677 of 697 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (697 of 697 strings)

Translated using Weblate (Malayalam)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Malayalam)

Currently translated at 48.4% (338 of 697 strings)

Translated using Weblate (Indonesian)

Currently translated at 99.8% (696 of 697 strings)

Translated using Weblate (Maltese)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Maltese)

Currently translated at 32.1% (224 of 697 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (697 of 697 strings)

Added translation using Weblate (Maltese)

Translated using Weblate (Spanish)

Currently translated at 100.0% (697 of 697 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (697 of 697 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.1% (684 of 697 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (697 of 697 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (697 of 697 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (697 of 697 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (697 of 697 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (697 of 697 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (697 of 697 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Spanish)

Currently translated at 99.4% (690 of 694 strings)

Translated using Weblate (Odia)

Currently translated at 37.5% (258 of 688 strings)

Co-authored-by: Andre Costa <andrecaeu@gmail.com>
Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Argo Carpathians <chrisarabagas@gmail.com>
Co-authored-by: Clxff H3r4ld0 <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Colgrave <hanqixu.blogs@simplelogin.co>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Fqwe1 <Fqwe1@users.noreply.hosted.weblate.org>
Co-authored-by: Gnkalk <github.fngyb@slmail.me>
Co-authored-by: Herderson Riker <herdersonriker@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Joshua Joseph <joshuasaju2@gmail.com>
Co-authored-by: Long Kim <kimlong01102000@icloud.com>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Matthaiks <kitynska@gmail.com>
Co-authored-by: Michael John Scerri <michaeljscerri@gmail.com>
Co-authored-by: Mika <akimivanov43@gmail.com>
Co-authored-by: Pizza Party <paol.m@proton.me>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Thanh <pancakes21f@gmail.com>
Co-authored-by: aleksej0R <omolice@hotmail.fr>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: kaajjo <claymanoff@gmail.com>
Co-authored-by: maxim <maximtested@gmail.com>
Co-authored-by: samwiaba <sambastianc@gmail.com>
Co-authored-by: Ömer Faruk Sancak <keyiflerolsun@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/apc/
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/fr/
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/ml/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/mt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/or/
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/pt_BR/
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/vi/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/ml/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/mt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/vi/
Translation: Cloudstream/App
Translation: Cloudstream/Fastlane
2024-04-08 04:02:02 +02:00
firelight
0a24661e4c
fix latest commit 2024-03-25 01:48:23 +01:00
firelight
ed2bdf44fb
New TvTypes + General fixes 2024-03-25 01:38:39 +01:00
IndusAryan
51d91bf9a7
feat(ui): add ignore battery optimisation dialog for uniterrupted downloads and notifications (#915) 2024-03-25 01:18:26 +01:00
firelight
fb89fd60b8
Merge pull request #996 from KingLucius/playFirstUnwatched
Set play button to first unwatched Episode on TV
2024-03-25 01:05:41 +01:00
recloudstream[bot]
7db7742c73 chore(locales): fix locale issues 2024-03-25 00:04:49 +00:00
firelight
b246d80861
Merge pull request #890 from recloudstream/weblate
Translations update from Hosted Weblate
2024-03-25 01:04:36 +01:00
Hosted Weblate
d321aba3a7
Merge remote-tracking branch 'origin/master' 2024-03-25 01:03:08 +01:00
IndusAryan
22937424fa
feat(ui): authenticate first when enabling security settings (#991) 2024-03-25 01:03:04 +01:00
Hosted Weblate
7f0034e872
Merge remote-tracking branch 'origin/master' 2024-03-25 00:59:59 +01:00
IndusAryan
35e38a53ad
refactor: format build date and time and make it copyable (#1002) 2024-03-25 00:59:55 +01:00
Hosted Weblate
6d8a31809d
Merge remote-tracking branch 'origin/master' 2024-03-25 00:55:53 +01:00
KingLucius
650c7583af
Fix Alert Dialog width on TV (#1010)
* Fix Alert Dialog width on TV

* Fix width for AlertDialogCustom on TV
2024-03-25 00:55:37 +01:00
Hosted Weblate
34af3a4b2f
Merge remote-tracking branch 'origin/master' 2024-03-25 00:47:29 +01:00
int3debug
9ef1f1cc41
fix: extension activity interruption (#1005)
fixed interruption with only local plugins

Co-authored-by: int3debug <gh.ditch236@passinbox.com>
2024-03-25 00:47:26 +01:00
Hosted Weblate
6ede44d85f
Merge remote-tracking branch 'origin/master' 2024-03-25 00:42:23 +01:00
KingLucius
2f03ca7de9
Extenstions' Github & Rate buttons are now focusable on TV (#1008)
- Disables useless focus for (Description, Author .. etc.) buttons one the left.
- Make GitHub & Rate focusable on TV.
2024-03-25 00:42:18 +01:00
Hosted Weblate
7ce2dfc4aa
Merge remote-tracking branch 'origin/master' 2024-03-25 00:41:08 +01:00
int3debug
16510923d2
fix: No access rights after restore from backup (#1009)
Co-authored-by: int3debug <gh.ditch236@passinbox.com>
2024-03-25 00:41:04 +01:00
Hosted Weblate
a9c2c0644a
Translated using Weblate (Indonesian)
Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.4% (677 of 688 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (French)

Currently translated at 98.1% (675 of 688 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.8% (687 of 688 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Croatian)

Currently translated at 99.2% (683 of 688 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (German)

Currently translated at 99.7% (686 of 688 strings)

Translated using Weblate (English)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (688 of 688 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (688 of 688 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Afrikaans)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Afrikaans)

Currently translated at 29.7% (205 of 688 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.7% (686 of 688 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Hindi)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Hindi)

Currently translated at 40.9% (282 of 688 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (688 of 688 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (688 of 688 strings)

Merge remote-tracking branch 'origin/master'

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Arabic (Levantine))

Currently translated at 99.7% (684 of 686 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.7% (684 of 686 strings)

Translated using Weblate (German)

Currently translated at 99.8% (685 of 686 strings)

Translated using Weblate (Malayalam)

Currently translated at 44.0% (302 of 686 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (686 of 686 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (686 of 686 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Arabic (Levantine))

Currently translated at 98.6% (678 of 687 strings)

Translated using Weblate (German)

Currently translated at 98.9% (680 of 687 strings)

Translated using Weblate (German)

Currently translated at 98.9% (680 of 687 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (687 of 687 strings)

Translated using Weblate (Swedish)

Currently translated at 99.8% (686 of 687 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (687 of 687 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.6% (678 of 687 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (687 of 687 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (687 of 687 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (687 of 687 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (687 of 687 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (684 of 684 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (684 of 684 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (684 of 684 strings)

Translated using Weblate (Swedish)

Currently translated at 99.8% (683 of 684 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (684 of 684 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (684 of 684 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (684 of 684 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (684 of 684 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (684 of 684 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (684 of 684 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (684 of 684 strings)

Translated using Weblate (Persian)

Currently translated at 33.7% (228 of 675 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Hungarian)

Currently translated at 95.1% (642 of 675 strings)

Translated using Weblate (Romanian)

Currently translated at 94.2% (636 of 675 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (Hungarian)

Currently translated at 86.3% (583 of 675 strings)

Translated using Weblate (Hindi)

Currently translated at 41.6% (281 of 675 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Russian)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (German)

Currently translated at 98.0% (662 of 675 strings)

Translated using Weblate (Portuguese)

Currently translated at 96.4% (651 of 675 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (French)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.3% (657 of 675 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (675 of 675 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Italian)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (675 of 675 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 99.7% (672 of 674 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (674 of 674 strings)

Translated using Weblate (Vietnamese)

Currently translated at 98.6% (665 of 674 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (674 of 674 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (674 of 674 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (674 of 674 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (674 of 674 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (674 of 674 strings)

Translated using Weblate (Bulgarian)

Currently translated at 98.9% (667 of 674 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (674 of 674 strings)

Translated using Weblate (Nepali)

Currently translated at 26.8% (181 of 674 strings)

Translated using Weblate (Afrikaans)

Currently translated at 29.9% (202 of 674 strings)

Translated using Weblate (Amharic)

Currently translated at 29.6% (200 of 674 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 42.8% (289 of 674 strings)

Translated using Weblate (Kannada)

Currently translated at 33.8% (228 of 674 strings)

Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Macedonian)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Dutch)

Currently translated at 99.5% (666 of 669 strings)

Translated using Weblate (Macedonian)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Portuguese)

Currently translated at 99.2% (664 of 669 strings)

Translated using Weblate (Portuguese)

Currently translated at 99.2% (664 of 669 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.0% (649 of 669 strings)

Translated using Weblate (Portuguese)

Currently translated at 97.0% (649 of 669 strings)

Translated using Weblate (Portuguese)

Currently translated at 95.9% (642 of 669 strings)

Translated using Weblate (French)

Currently translated at 99.4% (665 of 669 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Esperanto)

Currently translated at 33.7% (226 of 669 strings)

Translated using Weblate (Urdu)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Bulgarian)

Currently translated at 99.8% (668 of 669 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (French)

Currently translated at 99.4% (665 of 669 strings)

Translated using Weblate (Portuguese)

Currently translated at 95.9% (642 of 669 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (669 of 669 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Japanese)

Currently translated at 50.2% (336 of 669 strings)

Translated using Weblate (Hungarian)

Currently translated at 82.8% (554 of 669 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (669 of 669 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Russian)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (669 of 669 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Croatian)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Russian)

Currently translated at 94.7% (634 of 669 strings)

Translated using Weblate (Croatian)

Currently translated at 75.0% (3 of 4 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (669 of 669 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (German)

Currently translated at 99.1% (663 of 669 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 90.2% (603 of 668 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Japanese)

Currently translated at 49.8% (333 of 668 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 96.5% (645 of 668 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (668 of 668 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Swedish)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Japanese)

Currently translated at 47.3% (316 of 668 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Swedish)

Currently translated at 99.4% (664 of 668 strings)

Translated using Weblate (Nepali)

Currently translated at 26.9% (180 of 668 strings)

Translated using Weblate (German)

Currently translated at 99.1% (662 of 668 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Odia)

Currently translated at 38.3% (256 of 668 strings)

Translated using Weblate (Croatian)

Currently translated at 99.7% (666 of 668 strings)

Translated using Weblate (Nepali)

Currently translated at 16.9% (113 of 668 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Polish)

Currently translated at 99.7% (666 of 668 strings)

Translated using Weblate (Malayalam)

Currently translated at 45.2% (302 of 668 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Arabic)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Bengali)

Currently translated at 48.5% (324 of 668 strings)

Translated using Weblate (Bengali)

Currently translated at 48.5% (324 of 668 strings)

Translated using Weblate (Persian)

Currently translated at 27.6% (185 of 668 strings)

Translated using Weblate (Persian)

Currently translated at 27.6% (185 of 668 strings)

Translated using Weblate (Bengali)

Currently translated at 36.8% (246 of 668 strings)

Translated using Weblate (Tagalog)

Currently translated at 51.9% (347 of 668 strings)

Translated using Weblate (Swedish)

Currently translated at 78.8% (527 of 668 strings)

Translated using Weblate (Malayalam)

Currently translated at 39.0% (261 of 668 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Polish)

Currently translated at 98.6% (659 of 668 strings)

Co-authored-by: Aayush Shah <shahaayush999@gmail.com>
Co-authored-by: Ahmed Abd El-Fattah <a.aelfattah5@gmail.com>
Co-authored-by: Alexander Svärd <genc.demiri@hotmail.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Amir <amearb@duck.com>
Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Andre Costa <andrecaeu@gmail.com>
Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Apostol Penkov <apostol.penkov@gmail.com>
Co-authored-by: AppsTool <appstool03@gmail.com>
Co-authored-by: Argo Carpathians <chrisarabagas@gmail.com>
Co-authored-by: Bálint László <blaszlobors@gmail.com>
Co-authored-by: CPavRou <mag@cleparo.fr>
Co-authored-by: CakesTwix <cakestwix1@gmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Clxff H3r4ld0 <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Colgrave <hanqixu.blogs@simplelogin.co>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: DarkOrbFX <darkorbfx@gmail.com>
Co-authored-by: Davi Silveira <davilego10@gmail.com>
Co-authored-by: Davide Marcoli <davide.marcoli13@gmail.com>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Fjuro <fjuro@alius.cz>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Fqwe1 <Fqwe1@users.noreply.hosted.weblate.org>
Co-authored-by: Friso de Boer <collorfrisie@hotmail.com>
Co-authored-by: Gnkalk <github.fngyb@slmail.me>
Co-authored-by: H Tamás <hovanszki@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Hubert Naciasta <hubert.naciasta@skiff.com>
Co-authored-by: IamNotNickerson <IamNickerson@users.noreply.hosted.weblate.org>
Co-authored-by: Jean-Michel <arsene_lupin_57@hotmail.fr>
Co-authored-by: Joana Trashlieva <j.trashlieva@gmail.com>
Co-authored-by: Jose Delvani <delvani.eletricista@gmail.com>
Co-authored-by: Julia Sugawara <jm.sugawara@gmail.com>
Co-authored-by: Just Rocket (just) <phatal1988@gmail.com>
Co-authored-by: Kjev <77635620+Kjev666@users.noreply.github.com>
Co-authored-by: Levent SD <leventsd@gmail.com>
Co-authored-by: Luna712 <142361265+Luna712@users.noreply.github.com>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Matthaiks <kitynska@gmail.com>
Co-authored-by: Mc wolmarans <marthinuswolmarans61@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Naga <yz2000.pro@gmail.com>
Co-authored-by: NamelessGO <66227691+NameLessGO@users.noreply.github.com>
Co-authored-by: Only1337 <ymurathanusta@gmail.com>
Co-authored-by: Ovi329 <avijitb129@gmail.com>
Co-authored-by: Pizza Party <paol.m@proton.me>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Sanjeev Dhawan <smstranscom@gmail.com>
Co-authored-by: Semih <semihbrn10@gmail.com>
Co-authored-by: Slawa <slawa@slawagurevich.com>
Co-authored-by: Subham Jena <subhamjena8465@gmail.com>
Co-authored-by: Sufyan Zahoor Jutt <sufyanpahore@gmail.com>
Co-authored-by: T1z3n <info@njbraun.de>
Co-authored-by: TecnoLAZ <luispaulodias89@gmail.com>
Co-authored-by: Yosra Boussaid <yosra.boussaid@users.noreply.hosted.weblate.org>
Co-authored-by: Zhenye Dong <dongzhenye@gmail.com>
Co-authored-by: arcopnt <arcopnt@posteo.com>
Co-authored-by: dabao1955 <dabao1955@163.com>
Co-authored-by: delvani <inavleb@users.noreply.hosted.weblate.org>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: konatu saino <shironyann.tdbr@gmail.com>
Co-authored-by: lucasmz <github@lucasmz.dev>
Co-authored-by: maxim <maximtested@gmail.com>
Co-authored-by: oops-wtf <cert.potatoes@gmail.com>
Co-authored-by: simmon <simmon@nplob.com>
Co-authored-by: stojkovskistefan <stefanstojkovski@gmail.com>
Co-authored-by: tuan041 <tuananh163025ttt@gmail.com>
Co-authored-by: v1s7 <v1s7@users.noreply.hosted.weblate.org>
Co-authored-by: Ícaro Rodrigo Ferreira Da Fonseca Bezerra <icaro.bezerra@sptech.school>
Co-authored-by: Ömer Faruk Sancak <keyiflerolsun@gmail.com>
Co-authored-by: Сергій <sergiy.goncharuk.1@gmail.com>
Co-authored-by: सौम्य भाटी <saumyabhati2006@gmail.com>
Co-authored-by: 电棍老板 <qwertyuiop9296@outlook.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/af/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/am/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/apc/
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/cs/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/
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/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/ne/
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/or/
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/pt_BR/
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/sv/
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/fastlane/af/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/apc/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/hi/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/hr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/hu/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/id/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/ko/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/mk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/sv/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/zh_Hans/
Translation: Cloudstream/App
Translation: Cloudstream/Fastlane
2024-03-24 20:01:55 +01:00
firelight
4468ce3d80
Revert "make cloudstream very superfast boi, fast app startup and navigation …" (#1007)
This reverts commit faeb71da2c.
2024-03-22 23:06:05 +01:00
IndusAryan
faeb71da2c
make cloudstream very superfast boi, fast app startup and navigation (#965) 2024-03-22 22:56:05 +01:00
int3debug
86bc0b8345
fixed first setup extension activity interruption (#1000)
Co-authored-by: int3debug <gh.ditch236@passinbox.com>
2024-03-22 22:31:01 +01:00
int3debug
1ff0b5dccd
add build-date to main_settings (#995) 2024-03-20 08:33:50 +01:00
KingLucius
a2e63174be Set play button to first unwatched episode on TV 2024-03-19 17:47:36 +02:00
Osten
eb60be54ed fixed crash + fixed lib + fixed preview 2024-03-18 15:54:54 +01:00
Osten
8d5b73495d Added BaseAdapter to store internal state 2024-03-18 03:58:30 +01:00
KingLucius
a3bb853691
Extension's Settings Focus on TV (#990) 2024-03-17 17:07:18 +01:00
Osten
375b3ec46e
Update HomeParentItemAdapter.kt
Should fix last item problem
2024-03-17 16:42:31 +01:00
Osten
ad67b9ddab + Fixed ephemeral scroll
+ Fixed Unable to remove Subs
+ Fixed download 1 frame visual glitch
+ Maybe fixed worker
+ Updated layout API
+ Bump
2024-03-17 03:37:09 +01:00
KingLucius
638cc4fee9
New TV UI bug fixes (#983) 2024-03-16 03:50:49 +01:00
IndusAryan
4817b29b9c
refactor: toast view to use data binding (#919) 2024-03-14 00:12:13 +01:00
IndusAryan
040ac77b1a
refactor: add clipboard helper and improve repo url copy (#946) 2024-03-14 00:04:58 +01:00
firelight
527046766a
Fixed OOM when using restore data 2024-03-13 23:36:23 +01:00
Luna712
adc653943b
Improve synopsis/description display on phone and emulator (#967) 2024-03-13 18:29:12 +01:00
IndusAryan
81df68e137
fix(hotfix): bottom sheet appearing when turning biometrics off and remove toast. (#971)
* fix biometric regressions

* use nullable context

* aha!
2024-03-10 23:15:23 +01:00
Luna712
a01bb9e55b
Fix nonTransferableKeys in BackupUtils (#979)
* Fix nonTransferableKeys in BackupUtils

* Whoops...

* really...
2024-03-10 23:12:51 +01:00
Luna712
807bd85fa9
Move biometric_key to keys section in strings (#978) 2024-03-10 23:11:02 +01:00
KingLucius
510d11f705
New TV UI (#950) 2024-03-09 15:24:38 +01:00
firelight
bd69054f5d
Updated lib icon 2024-03-08 03:16:36 +01:00
firelight
694e7abbdf
Fixes #816 2024-03-08 03:00:00 +01:00
firelight
e3f9f255c7
Revert "feat: make cloudstream compilation and builds fast! using gradle conf…" (#968)
This reverts commit 21b341e12f.
2024-03-08 02:07:35 +01:00
IndusAryan
21b341e12f
feat: make cloudstream compilation and builds fast! using gradle configuration cache (#959) 2024-03-08 01:56:31 +01:00
IndusAryan
e3999d6e9a
feat(security): add biometric fingerprint sensor / face unlock authentication (#826) 2024-03-08 01:45:20 +01:00
Mater Yoda
f0f4ec87bc
Added lavender color (#949) 2024-03-08 01:20:49 +01:00
self-similarity
809a38507b
Update SimklApi.kt (#961) 2024-03-02 23:45:18 +01:00
KingLucius
1a380a3239
TV show airing status for phone (#953)
* TV show airing status for phone

* Bump Nicehttp & remove toImmutableList
2024-02-29 17:07:45 +01:00
Sofie
93d81ea038
WebViewResolver: added timeout (#941) 2024-02-19 21:18:36 +01:00
Sofie
e007714701
fix Rabbitstream (#936)
* fix Rabbitstream

* .
2024-02-19 21:06:55 +01:00
KingLucius
805f80b2ac
Long press Repo to copy URL (#934) 2024-02-19 16:46:02 +01:00
KingLucius
b5fb0997c4
[TV] Limit Homepage Header Description to 3 lines & 6 Tags (#938) 2024-02-19 16:44:50 +01:00
KingLucius
ca918b1581
[TV] More space around result description (#939) 2024-02-19 16:43:41 +01:00
IndusAryan
09779b4ee0
chore: add tooltips on results toolbar and rename speed mode (#925)
* add tooltips on results toolbar and better summaries, rename speed mode

* remove redundant space
2024-02-15 21:45:34 +01:00
Sofie
012d38398e
fix Acefile & Gofile (#926) 2024-02-15 21:42:47 +01:00
Ömer Faruk Sancak
d1db4c3370
Extractor: Added PlayRu (#930) 2024-02-15 21:42:11 +01:00
Sarlay
8d318ca84a
Fixed Chillx subtitles (#931)
* Fixed subtitles tracks for Chillx.kt

* Update Chillx.kt
2024-02-15 21:41:34 +01:00
KingLucius
eea6e13346
Make Rating non-focusable (Old API) (#935) 2024-02-15 21:40:44 +01:00
CranberrySoup
2b7d102716
Add SubScene (#923)
* Lower targetSdk to get all installed packages

* Update sdk version

* Let's not be too radical

* Many fixes

* Revert targetSdk

* Make account homepage persistent

* Add SubScene and change subtitle API

Co-authored-by: Aymanbest <51868001+aymanbest@users.noreply.github.com>

* Fix file deletion

---------

Co-authored-by: Aymanbest <51868001+aymanbest@users.noreply.github.com>
2024-02-06 23:27:35 +01:00
Osten
9ea7674a0f
Update MainAPI.kt 2024-02-02 22:18:54 +01:00
IndusAryan
3dcf7076d0
feat(ui): tap video duration to toggle remaining time counter (#878) 2024-01-21 20:11:51 +01:00
Sofie
8b14fcb881
added Mediafire (#906) 2024-01-21 15:35:33 +01:00
IndusAryan
01f21e0fe8
refactor: move buildconfig, bump ksp & better trailer scraping (#834) 2024-01-19 21:09:07 +01:00
coxju
bdef6524e7
feat : run custom js in webviewresolver (#888)
Co-authored-by: coxju <coxju>
2024-01-19 20:38:37 +01:00
Cloudburst
f40a8d9418
make *rotate_video_key untransatable (#896) 2024-01-19 20:12:52 +01:00
IndusAryan
03fcb106ac
new simple messages when updating app i.e, refined changelogs (#900) 2024-01-19 20:12:33 +01:00
coxju
636e157c63
fix: trailers not playing (#898)
Co-authored-by: coxju <coxju>
2024-01-19 12:03:20 +01:00
CranberrySoup
5af1b80cb7
Fix crash on plugin reload (#895)
* Update Event.kt

* Update Event.kt
2024-01-18 22:57:54 +01:00
coxju
5dfc08aabb
feat: added emturbovid extractor (#893)
Co-authored-by: coxju <coxju>
2024-01-17 23:28:17 +01:00
coxju
1676094488
feat (loadExtractor) : match mirror domains of extractor link (#877)
Co-authored-by: coxju <coxju>
2024-01-17 22:32:22 +01:00
Sir Aguacata
19145c6cc4
Screw it, Self host keys (#892) 2024-01-17 22:30:20 +01:00
coxju
ebb72d6a0c
feat : invalidate link cache after 20 mins (#875)
- additionaly clear cache if there is player errors or no links found

Co-authored-by: coxju <coxju>
2024-01-17 22:29:44 +01:00
Sir Aguacata
399b28c75b
Rest in piece old key repo (#891) 2024-01-17 00:35:23 +01:00
IndusAryan
601483e103
feat: limit genre tags on home to 2 lines and on result page, 10 tags max (#885) 2024-01-16 18:41:43 +01:00
recloudstream[bot]
9733d0b316 chore(locales): fix locale issues 2024-01-16 17:40:50 +00:00
Weblate (bot)
0cf199248a
Translated using Weblate (Croatian) (#856)
Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Russian)

Currently translated at 94.7% (634 of 669 strings)

Translated using Weblate (Croatian)

Currently translated at 75.0% (3 of 4 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (669 of 669 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (German)

Currently translated at 99.1% (663 of 669 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (669 of 669 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 90.2% (603 of 668 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Japanese)

Currently translated at 49.8% (333 of 668 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 96.5% (645 of 668 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (668 of 668 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Swedish)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Japanese)

Currently translated at 47.3% (316 of 668 strings)

Translated using Weblate (Swedish)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Swedish)

Currently translated at 99.4% (664 of 668 strings)

Translated using Weblate (Nepali)

Currently translated at 26.9% (180 of 668 strings)

Translated using Weblate (German)

Currently translated at 99.1% (662 of 668 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Odia)

Currently translated at 38.3% (256 of 668 strings)

Translated using Weblate (Croatian)

Currently translated at 99.7% (666 of 668 strings)

Translated using Weblate (Nepali)

Currently translated at 16.9% (113 of 668 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Polish)

Currently translated at 99.7% (666 of 668 strings)

Translated using Weblate (Malayalam)

Currently translated at 45.2% (302 of 668 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Arabic)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Bengali)

Currently translated at 48.5% (324 of 668 strings)

Translated using Weblate (Bengali)

Currently translated at 48.5% (324 of 668 strings)

Translated using Weblate (Persian)

Currently translated at 27.6% (185 of 668 strings)

Translated using Weblate (Persian)

Currently translated at 27.6% (185 of 668 strings)

Translated using Weblate (Bengali)

Currently translated at 36.8% (246 of 668 strings)

Translated using Weblate (Tagalog)

Currently translated at 51.9% (347 of 668 strings)

Translated using Weblate (Swedish)

Currently translated at 78.8% (527 of 668 strings)

Translated using Weblate (Malayalam)

Currently translated at 39.0% (261 of 668 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Polish)

Currently translated at 98.6% (659 of 668 strings)























Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/apc/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/
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/es/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fa/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ja/
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/ne/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/or/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sv/
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/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/apc/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/hr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/sv/
Translation: Cloudstream/App
Translation: Cloudstream/Fastlane

Co-authored-by: Aayush Shah <shahaayush999@gmail.com>
Co-authored-by: Alexander Svärd <genc.demiri@hotmail.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Amir <amearb@duck.com>
Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Hubert Naciasta <hubert.naciasta@skiff.com>
Co-authored-by: IamNotNickerson <IamNickerson@users.noreply.hosted.weblate.org>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Ovi329 <avijitb129@gmail.com>
Co-authored-by: Pizza Party <paol.m@proton.me>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Slawa <slawa@slawagurevich.com>
Co-authored-by: Subham Jena <subhamjena8465@gmail.com>
Co-authored-by: dabao1955 <dabao1955@163.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: v1s7 <v1s7@users.noreply.hosted.weblate.org>
Co-authored-by: Ömer Faruk Sancak <keyiflerolsun@gmail.com>
Co-authored-by: Сергій <sergiy.goncharuk.1@gmail.com>
2024-01-16 18:40:37 +01:00
Sofie
2624947b5b
fix keys (#887)
Co-authored-by: ghost <ghost@gmail.com>
2024-01-16 18:37:31 +01:00
coxju
31c783d0b4
feat: added extractor vidhide and streamwish (#889)
Co-authored-by: coxju <coxju>
2024-01-16 18:36:02 +01:00
firelight
9f1b172f34
fix plugin downloads trash can 2024-01-14 01:06:06 +01:00
IndusAryan
93dce8682e
fix system bars not appearing properly (#879) 2024-01-13 22:45:30 +01:00
IndusAryan
723c653b07
feat(ui): long press title to copy (#872)
* new feature: hold to copy movie title

* remove title copy hint
2024-01-12 16:48:43 +01:00
coxju
0c73f5e59a
fix: library not loading in TV (#871) 2024-01-11 17:08:37 +01:00
coxju
0eb152c5db
fix: search only if selection changed (#868) 2024-01-11 15:54:28 +01:00
IndusAryan
8c5ab86714
hotfix window compat bug (#870) 2024-01-11 15:53:31 +01:00
Ömer Faruk Sancak
85a769a898
Extractor: ContentX add external subtitle (#869) 2024-01-11 15:52:34 +01:00
Sir Aguacata
96aa56209b
Revert the repo change to get keys (#867) 2024-01-11 00:47:40 +01:00
coxju
d71d3890b5
feat: show random button on library (#855) 2024-01-10 22:28:06 +01:00
IndusAryan
19b1a40cf8
use window insets compat controller (#847) 2024-01-10 22:20:43 +01:00
Yutatsu
e5f483b0b2
Fix vidplay extractor (#866) 2024-01-10 22:06:45 +01:00
coxju
6f1e0bef80
feat: show the episodic range with current selection checked (#851) 2024-01-10 22:05:56 +01:00
LagradOst
5e6272be3f fix 2024-01-10 19:10:34 +01:00
coxju
97ec98b9e2
feat : show favorite button in bottom dialog (#858)
Co-authored-by: coxju <coxju>
2024-01-10 18:55:10 +01:00
coxju
42fd0b5c76
new streamtape extractor (#857) 2024-01-08 23:46:19 +01:00
Ömer Faruk Sancak
42774f6183
Extractor: ContentX expanded and fix (#859)
* Extractor: ContentX expanded and fix
2024-01-08 23:45:53 +01:00
Sofie
f687508521
Extractors: fix acefile, pixeldrain & vidplay tracks (#854)
Co-authored-by: ghost <ghost@gmail.com>
2024-01-05 17:08:48 +01:00
recloudstream[bot]
dbba6d7f27 chore(locales): fix locale issues 2024-01-05 16:08:37 +00:00
Hosted Weblate
f4da170a57 Translated using Weblate (Polish)
Currently translated at 99.7% (666 of 668 strings)

Translated using Weblate (Malayalam)

Currently translated at 45.2% (302 of 668 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Arabic)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Bengali)

Currently translated at 48.5% (324 of 668 strings)

Translated using Weblate (Bengali)

Currently translated at 48.5% (324 of 668 strings)

Translated using Weblate (Persian)

Currently translated at 27.6% (185 of 668 strings)

Translated using Weblate (Persian)

Currently translated at 27.6% (185 of 668 strings)

Translated using Weblate (Bengali)

Currently translated at 36.8% (246 of 668 strings)

Translated using Weblate (Tagalog)

Currently translated at 51.9% (347 of 668 strings)

Translated using Weblate (Swedish)

Currently translated at 78.8% (527 of 668 strings)

Translated using Weblate (Malayalam)

Currently translated at 39.0% (261 of 668 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Polish)

Currently translated at 98.6% (659 of 668 strings)

Co-authored-by: Amir <amearb@duck.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Hubert Naciasta <hubert.naciasta@skiff.com>
Co-authored-by: IamNotNickerson <IamNickerson@users.noreply.hosted.weblate.org>
Co-authored-by: Ovi329 <avijitb129@gmail.com>
Co-authored-by: Pizza Party <paol.m@proton.me>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fa/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ml/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sv/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/apc/
Translation: Cloudstream/App
Translation: Cloudstream/Fastlane
2024-01-05 17:08:20 +01:00
Cloudburst
2a1876f54c
fix build_to_archive.yml 2024-01-03 10:28:39 +01:00
wrongwrong
f1d0a8e955
Fixed to not use API that will be discontinued in the future (#843) 2024-01-03 10:24:42 +01:00
IndusAryan
1c6be2d5cb
refactor(bump): upgrade workflow versions to use node16 (#793) 2024-01-03 10:24:28 +01:00
Horis
fc802cdcdd
add extractors (#849) 2024-01-03 10:24:12 +01:00
Ömer Faruk Sancak
2cfdab5498
Extractor: added some extractors (#833)
* Extractor: added some extractors

* Extractor: fix same import

* Extractor: PeaceMakerst fix

* Extractor: source fix
2023-12-31 21:31:05 +01:00
recloudstream[bot]
4c2ee28d5a chore(locales): fix locale issues 2023-12-28 13:17:57 +00:00
firelight
657f2fbcb2
Merge pull request #824 from recloudstream/weblate
Translations update from Hosted Weblate
2023-12-28 14:17:41 +01:00
Hosted Weblate
b5ac668493
Added translation using Weblate (Nepali)
Translated using Weblate (Ukrainian)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 75.0% (3 of 4 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Hindi)

Currently translated at 41.9% (280 of 668 strings)

Translated using Weblate (French)

Currently translated at 98.5% (658 of 668 strings)

Translated using Weblate (Italian)

Currently translated at 99.2% (663 of 668 strings)

Translated using Weblate (Hindi)

Currently translated at 38.1% (255 of 668 strings)

Translated using Weblate (Burmese)

Currently translated at 94.9% (634 of 668 strings)

Translated using Weblate (Galician)

Currently translated at 38.9% (260 of 668 strings)

Translated using Weblate (Odia)

Currently translated at 38.1% (255 of 668 strings)

Translated using Weblate (Latvian)

Currently translated at 92.3% (617 of 668 strings)

Translated using Weblate (Somali)

Currently translated at 85.1% (569 of 668 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 43.1% (288 of 668 strings)

Translated using Weblate (Persian)

Currently translated at 22.6% (151 of 668 strings)

Translated using Weblate (Tamil)

Currently translated at 21.1% (141 of 668 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 94.0% (628 of 668 strings)

Translated using Weblate (Tagalog)

Currently translated at 51.3% (343 of 668 strings)

Translated using Weblate (Swedish)

Currently translated at 78.7% (526 of 668 strings)

Translated using Weblate (Romanian)

Currently translated at 90.8% (607 of 668 strings)

Translated using Weblate (Polish)

Currently translated at 94.9% (634 of 668 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 89.5% (598 of 668 strings)

Translated using Weblate (French)

Currently translated at 98.5% (658 of 668 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.5% (665 of 668 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Lithuanian)

Currently translated at 48.5% (324 of 668 strings)

Translated using Weblate (Amharic)

Currently translated at 29.6% (198 of 668 strings)

Translated using Weblate (Tigrinya)

Currently translated at 15.1% (101 of 668 strings)

Translated using Weblate (Korean)

Currently translated at 92.3% (617 of 668 strings)

Translated using Weblate (Malay)

Currently translated at 22.9% (153 of 668 strings)

Translated using Weblate (Japanese)

Currently translated at 46.5% (311 of 668 strings)

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

Currently translated at 50.5% (338 of 668 strings)

Translated using Weblate (Portuguese)

Currently translated at 94.1% (629 of 668 strings)

Translated using Weblate (Hungarian)

Currently translated at 82.0% (548 of 668 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Russian)

Currently translated at 94.9% (634 of 668 strings)

Translated using Weblate (Kannada)

Currently translated at 33.9% (227 of 668 strings)

Translated using Weblate (Urdu)

Currently translated at 94.9% (634 of 668 strings)

Translated using Weblate (Hebrew)

Currently translated at 94.9% (634 of 668 strings)

Translated using Weblate (Bengali)

Currently translated at 36.3% (243 of 668 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Malayalam)

Currently translated at 38.7% (259 of 668 strings)

Translated using Weblate (Macedonian)

Currently translated at 92.3% (617 of 668 strings)

Translated using Weblate (Greek)

Currently translated at 94.0% (628 of 668 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (668 of 668 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 94.9% (634 of 668 strings)

Translated using Weblate (Bulgarian)

Currently translated at 93.5% (625 of 668 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 98.8% (660 of 668 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 91.6% (612 of 668 strings)

Translated using Weblate (Macedonian)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 92.1% (610 of 662 strings)

Translated using Weblate (Macedonian)

Currently translated at 92.2% (611 of 662 strings)

Translated using Weblate (German)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (German)

Currently translated at 99.6% (660 of 662 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (662 of 662 strings)

Translated using Weblate (English)

Currently translated at 75.0% (3 of 4 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Italian)

Currently translated at 100.0% (662 of 662 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (662 of 662 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (662 of 662 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (662 of 662 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (662 of 662 strings)

Translated using Weblate (Italian)

Currently translated at 99.8% (660 of 661 strings)

Translated using Weblate (Italian)

Currently translated at 98.7% (653 of 661 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (German)

Currently translated at 99.6% (659 of 661 strings)

Translated using Weblate (French)

Currently translated at 98.1% (649 of 661 strings)

Co-authored-by: Aayush Shah <shahaayush999@gmail.com>
Co-authored-by: Azgar <azgar4485@gmail.com>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: Emanuele Frasca <noostale@live.it>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Filip Drogrishki <alekfilip425@gmail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Giuseppe Terrana <terranagiuseppe03@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Lacey Anaya <yecakeh263@anawalls.com>
Co-authored-by: Luna712 <142361265+Luna712@users.noreply.github.com>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Pizza Party <paol.m@proton.me>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Samuele Righi <blackdestinyx145@gmail.com>
Co-authored-by: Wei-Cheng Yeh (IID) <iid@ccns.ncku.edu.tw>
Co-authored-by: almost gamer <almost.gamer01+language@gmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: Ömer Faruk Sancak <keyiflerolsun@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/am/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/apc/
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/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/fa/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/gl/
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/hu/
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/ko/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/lt/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/lv/
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/ms/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/my/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nn/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/or/
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/pt_BR/
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/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/ti/
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/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hant/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/apc/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/de/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/en/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/mk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/uk/
Translation: Cloudstream/App
Translation: Cloudstream/Fastlane
2023-12-28 01:52:53 +01:00
Sofie
9d3b2ba3d2
vidplay/fallback : change to previous key (#836)
* change key

* fix rabbitstream's key
2023-12-23 23:55:02 +01:00
Osten
5f51a8f7bc
Update WatchType.kt 2023-12-21 00:10:20 +01:00
LagradOst
e886fde8b8 lib longhold 2023-12-21 00:07:39 +01:00
Sofie
1356a954f3
Vidplay: change for more accurate key (#831) 2023-12-20 01:18:32 +01:00
firelight
3d90af29eb
bookmark button on preview 2023-12-19 15:59:24 +01:00
recloudstream[bot]
2a4ce89452 chore(locales): fix locale issues 2023-12-19 14:22:10 +00:00
firelight
0543f1ffae
Merge pull request #799 from recloudstream/weblate
Translations update from Hosted Weblate
2023-12-19 14:21:55 +00:00
coxju
a5f7920bca
feat (player): optional rotate button in player and setting to enable auto rotate based on video orientation (#813)
Co-authored-by: coxju <coxju>
2023-12-19 15:20:58 +01:00
Hosted Weblate
e8fe2944bb
Translated using Weblate (German)
Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (German)

Currently translated at 99.6% (660 of 662 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (662 of 662 strings)

Translated using Weblate (English)

Currently translated at 75.0% (3 of 4 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Italian)

Currently translated at 100.0% (662 of 662 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (662 of 662 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (662 of 662 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (662 of 662 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (662 of 662 strings)

Translated using Weblate (Italian)

Currently translated at 99.8% (660 of 661 strings)

Translated using Weblate (Italian)

Currently translated at 98.7% (653 of 661 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (German)

Currently translated at 99.6% (659 of 661 strings)

Translated using Weblate (French)

Currently translated at 98.1% (649 of 661 strings)

Co-authored-by: Azgar <azgar4485@gmail.com>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Giuseppe Terrana <terranagiuseppe03@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Lacey Anaya <yecakeh263@anawalls.com>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Samuele Righi <blackdestinyx145@gmail.com>
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/es/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/de/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/en/
Translation: Cloudstream/App
Translation: Cloudstream/Fastlane
2023-12-19 09:10:19 +00:00
Luna712
db91552f39
PluginManager: uncomment setReadOnly for Android 14 (#823) 2023-12-18 18:49:08 +01:00
Luna712
484c21cc1c
CS3IPlayer: stop player when releasing (#822) 2023-12-17 20:04:20 +01:00
Sofie
ff9144ef54
fix exception (#819) 2023-12-16 20:13:19 +01:00
Luna712
10a477c2bd
Show toast when reloading links (#818)
I'm not really sure if this should be done or not. But I like to know if I actually press the button or miss click as I often have to press it twice for it to do anything, which might just be another issue though.
2023-12-16 18:42:51 +01:00
firelight
6d51c59b18
fix visibility 2023-12-15 15:24:08 +00:00
Sofie
f98ce0558d
add prevent option in CloudflareKiller (#808)
* add prevent option in CloudflareKiller
2023-12-15 15:34:58 +01:00
firelight
f5e6d98cb0
refactor (library search): sort and query current page only and delay search when typing (#806)
* refactor (library): delay search when typing

* fix comment

* review fixes

---------

Co-authored-by: Funny-Pen-7005 <Funny-Pen-7005>
2023-12-15 15:32:21 +01:00
coxju
91dc83e6a3
refactor (search filter) : syncing main tytype chips and bottom dialog tytype chips (#811) 2023-12-15 15:20:37 +01:00
IndusAryan
fe30a85a1c
refactor: general and ui settings and added explicit unstable api opt ins (#787) 2023-12-13 22:18:12 +01:00
yueehaoo
6e5a52e440
Fixing Source List Displaying Empty Items (#810)
* Fixing source list displaying empty items
2023-12-13 20:49:42 +01:00
coxju
410cedc128
refactor (home) : show account icon when no provider selected (#812)
Co-authored-by: Funny-Pen-7005 <Funny-Pen-7005>
2023-12-13 20:23:30 +01:00
Luna712
3c152e04d1
Support showing content ratings for TmdbProvider (#705)
* Support showing content ratings for TmdbProvider
2023-12-09 18:54:29 +01:00
Osten
d0aed5e51a
bump 2023-12-09 18:54:09 +01:00
Funny-Pen-7005
530619c8d0
fixes (library): reverted and updated currentPage logic (#802)
Co-authored-by: Funny-Pen-7005 <Funny-Pen-7005>
2023-12-09 16:38:39 +01:00
firelight
3ef8f3030c
fix from pr (#801) 2023-12-09 15:04:26 +01:00
Funny-Pen-7005
2d87983eca
fix (library tab): save and restore tab selection (#798)
Co-authored-by: Funny-Pen-7005 <Funny-Pen-7005>
2023-12-09 14:55:13 +01:00
Sofie
6f3a8c1cd2
extractor: added vidplay and fix few extractors (#795)
* extractor: added Vidplay

* fix id

* fix Chillx

* fix Linkbox

* update m3u8helper in chillx

* fix Dailymotion

---------

Co-authored-by: Sofie99 <Sofie99@gmail.com>
2023-12-08 17:28:16 +01:00
recloudstream[bot]
dfd6ce7651 chore(locales): fix locale issues 2023-12-07 20:54:23 +00:00
firelight
88ad64b3b0
Merge pull request #790 from recloudstream/weblate
Translations update from Hosted Weblate
2023-12-07 20:54:07 +00:00
Hosted Weblate
cebdbd2199
Merge remote-tracking branch 'origin/master' 2023-12-07 20:51:46 +00:00
IndusAryan
25b042fb83
refactor(bump): update glide module and fix regressions (#789)
* upgrade glide module and glide ksp

* fix glide breaking changes and regressions
2023-12-07 21:51:42 +01:00
Hosted Weblate
fac0ef4c25
Merge remote-tracking branch 'origin/master' 2023-12-07 20:44:17 +00:00
IndusAryan
f7bc83024a
upstream kotlin symbol processing (#788) 2023-12-07 21:44:12 +01:00
Hosted Weblate
5b170c0573
Merge remote-tracking branch 'origin/master' 2023-12-07 21:43:56 +01:00
firelight
38cc121755
Update build.gradle.kts (#797) 2023-12-07 21:43:52 +01:00
Hosted Weblate
951b2110ad
Merge remote-tracking branch 'origin/master' 2023-12-07 21:42:39 +01:00
IndusAryan
d4aefc4e64
bump guava, json(tests), kgp, desugaring (#781)
* bump guava(for ksp)

* Update build.gradle.kts

* .

* Update kotlin gradle plugin
2023-12-07 21:42:32 +01:00
Hosted Weblate
c324eaf543
Translated using Weblate (Hungarian)
Currently translated at 81.5% (539 of 661 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (661 of 661 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (661 of 661 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (661 of 661 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (661 of 661 strings)

Co-authored-by: Gyuri Bajzik <bajzikgy@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Pizza Party <paol.m@proton.me>
Co-authored-by: ngocanhtve <ngocanh.tve@gmail.com>
Co-authored-by: Ömer Faruk Sancak <keyiflerolsun@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/apc/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hu/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/tr/
Translation: Cloudstream/App
Translation: Cloudstream/Fastlane
2023-12-02 08:04:30 +01:00
recloudstream[bot]
962ff1c058 chore(locales): fix locale issues 2023-11-27 21:48:42 +00:00
firelight
7165b57268
Merge pull request #754 from recloudstream/weblate
Translations update from Hosted Weblate
2023-11-27 21:48:24 +00:00
Hosted Weblate
e80dc63381
Translated using Weblate (Russian)
Currently translated at 94.7% (626 of 661 strings)

Translated using Weblate (Russian)

Currently translated at 94.5% (625 of 661 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (661 of 661 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Portuguese)

Currently translated at 94.8% (627 of 661 strings)

Translated using Weblate (Japanese)

Currently translated at 46.4% (307 of 661 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (661 of 661 strings)

Translated using Weblate (Bengali)

Currently translated at 36.3% (240 of 661 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Slovak)

Currently translated at 68.2% (451 of 661 strings)

Translated using Weblate (Vietnamese)

Currently translated at 99.6% (659 of 661 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Arabic (Najdi))

Currently translated at 66.5% (440 of 661 strings)

Translated using Weblate (Dutch)

Currently translated at 94.8% (627 of 661 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (661 of 661 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (French)

Currently translated at 96.3% (637 of 661 strings)

Translated using Weblate (French)

Currently translated at 96.3% (637 of 661 strings)

Translated using Weblate (Turkish)

Currently translated at 25.0% (1 of 4 strings)

Translated using Weblate (Afrikaans)

Currently translated at 30.4% (201 of 661 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (661 of 661 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (661 of 661 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 100.0% (661 of 661 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (661 of 661 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (661 of 661 strings)

Translated using Weblate (Afrikaans)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 68.3% (452 of 661 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (661 of 661 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (661 of 661 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 56.8% (372 of 654 strings)

Translated using Weblate (Hindi)

Currently translated at 38.3% (251 of 654 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 52.4% (343 of 654 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 43.4% (284 of 654 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 42.2% (276 of 654 strings)

Translated using Weblate (Arabic (Levantine))

Currently translated at 39.6% (259 of 654 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (654 of 654 strings)

Added translation using Weblate (Afrikaans)

Translated using Weblate (German)

Currently translated at 99.5% (651 of 654 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (654 of 654 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (654 of 654 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (654 of 654 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (654 of 654 strings)

Co-authored-by: Alexthegib <traducoes@skiff.com>
Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Clxff H3r4ld0 <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Fqwe1 <Fqwe1@users.noreply.hosted.weblate.org>
Co-authored-by: Guillaume THOMAS <t.guillaume319@laposte.net>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jay Jay Cabugat <cabugatjayjay@gmail.com>
Co-authored-by: Leon de Klerk <deklerkleon5@gmail.com>
Co-authored-by: LiJu09 <lisojuraj@gmail.com>
Co-authored-by: LiberiBg <matheo.ngn@gmail.com>
Co-authored-by: Luna712 <hanstavo1@gmail.com>
Co-authored-by: Murat Han <murathancw@gmail.com>
Co-authored-by: Nepx <anandabaskara@outlook.com>
Co-authored-by: Pizza Party <paol.m@proton.me>
Co-authored-by: Ranforingus <ranforingus@hotmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Sam Cooper <samcooper838@gmail.com>
Co-authored-by: Samiul Islam <samiulislamsharan@gmail.com>
Co-authored-by: SehrGuterCode <philemonpfeiffer@gmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: maxim <maximtested@gmail.com>
Co-authored-by: ngocanhtve <ngocanh.tve@gmail.com>
Co-authored-by: 跨性别 <github@lgbt.sh>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/af/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/apc/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ars/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/
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/es/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/
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/id/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ja/
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/sk/
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/vi/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/af/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/tr/
Translation: Cloudstream/App
Translation: Cloudstream/Fastlane
2023-11-26 02:07:17 +01:00
firelight
fa7ebc05b3
Refix youtube
This reverts commit df0122c146.
2023-11-21 22:47:01 +01:00
IndusAryan
df0122c146
fix: bump rhino js and upgrade desugaring level (#774)
* fix and bump rhino js

* upgrade desugaring level

* uppercase run to Run
2023-11-18 14:31:41 +00:00
IndusAryan
b49368100b
upgrade gradle (#775) 2023-11-18 14:31:27 +00:00
Sofie
0077cebaa6
fixed AesHelper (#773)
Co-authored-by: Sofie99 <Sofie99@gmail.com>
2023-11-18 14:30:56 +00:00
IndusAryan
a2085202ec
refactor: remove kapt and it's annotation processor, clean gradle, upgrade build-dir deprecation (#764)
* remove kapt and its annotation processor

* cleanup

* change deprecated builddir
2023-11-15 17:31:02 +01:00
CranberrySoup
765071ebef
Fixed flag and name for Levantine Arabic and Najdi Arabic (#770) 2023-11-15 17:30:29 +01:00
self-similarity
e11d36aed8
Save selected subtitle language (#765) 2023-11-12 16:36:21 +01:00
Luna712
5bf2b4ead2
Massive changes to account flow (#741)
* Massive changes to account flow
2023-11-11 23:47:23 +01:00
Cloudburst
de61501b22
Update prerelease.yml 2023-11-11 17:45:10 +01:00
Cloudburst
685884e67f fix makeJar task 2023-11-11 17:40:47 +01:00
Luna712
6db295a799
Upgrade gradle (#726) 2023-11-11 17:30:36 +01:00
self-similarity
2b60e3a893
Fix faulty automatic subtitle selection (#760) 2023-11-10 23:49:37 +00:00
Luna712
3adf036135
Fix some deprecations and other warnings (#750)
* Fix some deprecations and other warnings
2023-11-10 23:48:53 +00:00
IndusAryan
c4aab5e5a8
feat: make cloudstream fast boi, ksp migration (#689)
* migrate from kapt to ksp

* fook codefactor
2023-11-10 17:02:51 +01:00
KingLucius
7e2908c0bb
Fix top bar in Extensions & Test settings (#753) 2023-11-10 15:36:38 +01:00
Sofie
22a0c25d83
extractor: fixed Rabbitstream (#757)
* Extractor: added Rabbitstream

* Extractor: added Rabbitstream

* fixed Rabbitstream

---------

Co-authored-by: Sofie99 <Sofie99@gmail.com>
2023-11-10 15:28:27 +01:00
self-similarity
11136fe63d
Fix selecting sources in cast (#752) 2023-11-05 22:33:11 +00:00
Luna712
a6786aaf98
Add done button for when creating new PINs (#742)
* Add done button for when creating new PINs

* Cleanup
2023-11-02 23:28:25 +00:00
Luna712
5b0cbbf09f
Use nicer grid layout for account select on TV (#737) 2023-11-02 21:14:16 +01:00
IndusAryan
6a8c251013
bump navigation lib (#749) 2023-11-02 21:07:34 +01:00
KingLucius
908f83c50e
Fix scroll for Library TV layout (#695)
* Fix scroll for Library TV layout

* Fixed without NestedScrollView
2023-11-02 21:03:00 +01:00
recloudstream[bot]
6f40d2750f chore(locales): fix locale issues 2023-11-02 19:59:11 +00:00
Weblate (bot)
199f5b3a9d
Translations update from Hosted Weblate (#681)
Co-authored-by: Ahmed Abd El-Fattah <a.aelfattah5@gmail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Fqwe1 <Fqwe1@users.noreply.hosted.weblate.org>
Co-authored-by: Giuseppe Terrana <terranagiuseppe03@gmail.com>
Co-authored-by: Luna712 <142361265+Luna712@users.noreply.github.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: ngocanhtve <ngocanh.tve@gmail.com>
2023-11-02 20:58:52 +01:00
KingLucius
6ce9f29331
Fix settings top bar on TV (#738) 2023-11-02 20:50:49 +01:00
firelight
8b73c35e43
faster account skip startup 2023-10-31 00:34:01 +01:00
Luna712
65313b4579
Add account selection activity and support profile locks (#736) 2023-10-31 00:32:01 +01:00
IndusAryan
a8fdf5e8f2
remove useless parent (#735) 2023-10-30 15:25:50 +00:00
Luna712
87c5aada8f
Bump androidx.preference:preference-ktx (#734) 2023-10-29 16:35:48 +00:00
Luna712
f0e429436f
Use TV results layout on emulator (#710)
* Use TV results layout on emulator

* Add subscription support to emulator

* Update
2023-10-29 01:20:04 +02:00
self-similarity
137d833d4a
Improved Simkl autosync and fixed syncing seasons (#722)
* Improved simkl autosync and fixed syncing seasons
2023-10-29 00:55:49 +02:00
Luna712
b2e0b7dec8
Fix "Multiple substitutions specified in non-positional format" (#727) 2023-10-29 00:52:58 +02:00
Luna712
d542febcda
Reload library when deleting bookmarks (#725) 2023-10-29 00:41:19 +02:00
Luna712
f0ebfa47c8
Bump material to 1.10.0 (#728) 2023-10-29 00:40:20 +02:00
Luna712
51a877f405
Fix some strings (#730) 2023-10-29 00:29:16 +02:00
Luna712
4b93524e57
Convert the rest of SingleSelectionHelper to ViewBinding (#724) 2023-10-26 02:15:50 +02:00
firelight
ef36bccc90
Delete old MultiAnimeProvider.kt (#717) 2023-10-26 02:10:08 +02:00
Luna712
968bd59188
Warn of potential duplicates when adding to library (#691) 2023-10-26 02:09:21 +02:00
Luna712
e4ba852007
Use bottom dialogs for synopsis (#709)
* Use bottom dialogs for synopsis

* Add bottomTextDialog
2023-10-25 16:43:29 +02:00
Luna712
504258bf15
Show confirm exit dialog on emulator layout (#704) 2023-10-23 18:42:17 +02:00
KingLucius
48053164dc
Old APIs focus fixes (#692)
* Movie button focus fix in TV layout

* Cast item focusable in old API

* Media description focus fix in old API

* Switch account button focus fix in old APi
2023-10-23 18:38:53 +02:00
Luna712
2a4468eb44
Add more info on homepage to emulator layout (#698)
* Add more info on homepage to emulator layout

* Support for continue watching and bookmarks

It does some manual changes to avoid having to duplicate the entire layout for minor changes
2023-10-23 18:33:44 +02:00
Luna712
5153a74d4f
Library/LocalList: enable subscriptions on emulator layout (#702) 2023-10-23 18:21:32 +02:00
KingLucius
138dea88c4
Poster cropped at 20% from Top (#693) 2023-10-23 18:16:48 +02:00
IndusAryan
eb58cb1184
fix crash when navigation graph is null (#706) 2023-10-23 18:11:05 +02:00
firelight
c9bffef7cb
Merge pull request #690 from Luna712/locallist-sort
Enable sorting by updated date in LocalList
2023-10-20 13:31:46 +00:00
IndusAryan
a7a6f2282a
Update build.gradle.kts (#694) 2023-10-17 19:13:29 +00:00
Luna712
8ed7418fe4 Enable sorting by updated date in LocalList 2023-10-14 10:30:45 -06:00
Luna712
3cb2196e62
Add favorites (#682)
* Add favorites
2023-10-14 01:02:12 +02:00
KingLucius
7e9d1ded7f
Larger top poster in TV loading layout (#685) 2023-10-14 00:54:34 +02:00
Luna712
b7322ffb19
Add clear search query icons (#687) 2023-10-14 00:44:51 +02:00
CranberrySoup
fd1620f3d7
Fix unresponsive settings (#688)
* Lower targetSdk to get all installed packages

* Update sdk version

* Let's not be too radical

* Many fixes

* Revert targetSdk

* Make account homepage persistent

* Update mobile_navigation.xml

* Update SettingsFragment.kt
2023-10-14 00:34:26 +02:00
KingLucius
fc8c0e809d
Focus fixes for old devices (#686)
- Fix Library on TV main buttons focus.
- Quick search back button focus.
2023-10-12 17:56:54 +00:00
self-similarity
1ccd3d732d
Reduce image cache to 100mb (#683)
* Reduce image cache to 100mb
2023-10-12 01:06:22 +02:00
LagradOst
bb6a17e23c resize images for lower mem footprint 2023-10-11 18:31:46 +02:00
recloudstream[bot]
2f2bbd7d88 chore(locales): fix locale issues 2023-10-10 20:19:06 +00:00
Sir Aguacata
749d131099
Added new primary colors for comfort (#653) 2023-10-10 22:18:34 +02:00
Weblate (bot)
de6dfec452
Translations update from Hosted Weblate (#596)
Co-authored-by: Alexandru <negrualexandru52@gmail.com>
Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Antonio N <antonioenpidev@gmail.com>
Co-authored-by: Beabfekad Zikie <beabfekadz@gmail.com>
Co-authored-by: Beach Cow <Beachcow@skiff.com>
Co-authored-by: Boz Osman <nachodev@proton.me>
Co-authored-by: Cait Martin Newnham <85128509+helloiamcait@users.noreply.github.com>
Co-authored-by: Carlos Luiz <ecarlos-luiz@hotmail.com>
Co-authored-by: Chi Uma <jivanov.2048@gmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: Don Apis <apisapisapis@gmail.com>
Co-authored-by: GobinathAL <gobinathal8@gmail.com>
Co-authored-by: Gyuri Bajzik <bajzikgy@gmail.com>
Co-authored-by: Joel Brink <joel.brink.handy@gmail.com>
Co-authored-by: John <yes20866@gmail.com>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Mubarek Seyd Juhar <mubareksd@gmail.com>
Co-authored-by: Muhammad Fahad Khan <itxmfahadkhan@gmail.com>
Co-authored-by: Pizza Party <paol.m@proton.me>
Co-authored-by: Radoslav Lelchev <rlelchev@abv.bg>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Roi Gabay <roigby@gmail.com>
Co-authored-by: Sam Cooper <samcooper838@gmail.com>
Co-authored-by: Samiul Islam <samiulislamsharan@gmail.com>
Co-authored-by: ShareASmile <aapshergill@gmail.com>
Co-authored-by: Skrripy <Skrripy@users.noreply.hosted.weblate.org>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: Subham Jena <subhamjena8465@gmail.com>
Co-authored-by: esfzzddfse <esfzzddfse@proton.me>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: l <thisuserooo@gmail.com>
Co-authored-by: mbottari <mbottari@quantum-systems.com>
Co-authored-by: pedrolinharesmoreira <pedrolinhares@unifei.edu.br>
Co-authored-by: shivashranz <shivatheboss11@gmail.com>
Co-authored-by: tabtomi8 <tabtomi88@gmail.com>
Co-authored-by: ßozo Mamed <bozo-mamed-27@hotmail.com>
Co-authored-by: Влад Николаев <vladnic1990@gmail.com>
2023-10-10 22:18:21 +02:00
LagradOst
b4da93c1de revert jackson 2023-10-10 21:45:36 +02:00
LagradOst
dd45ac9e8a reverted to android 14 -> 13 2023-10-10 20:49:04 +02:00
LagradOst
b47209483a reverted to instant outline 2023-10-10 18:05:31 +02:00
self-similarity
91b195241e
Automatic backups (#592)
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
2023-10-10 17:19:27 +02:00
KingLucius
abbad1bc94
Delete Focus frame from empty Downloads list & Search TV Layout (#675)
* Delete Focus frame in search TV layout

* Delete focus frame for empty Downloads list

* Chip rounded stroke frame
2023-10-10 17:17:18 +02:00
KingLucius
b120a7bce2
Library on TV (#663)
* implementation for Library on TV
2023-10-10 17:16:35 +02:00
Luna712
5b4fd8d77d
Fix issue where DownloadedPlayerActivity interferes with MainActivity (#674)
* Fix issue where DownloadedPlayerActivity interferes with MainActivity
2023-10-10 17:05:34 +02:00
LagradOst
d277d8a9aa bump upstream 2023-10-10 16:56:30 +02:00
LagradOst
33eb3a3b29 lib fix2 2023-10-07 21:48:24 +02:00
LagradOst
f14557fe6a lib fix 2023-10-07 01:54:34 +02:00
LagradOst
77294dc68e cleanup 2023-10-07 01:39:30 +02:00
Luna712
0a327ccbda
Reload library when reloading home (#656)
So that library is reloaded when switching accounts.

Fixes #650
2023-10-06 23:50:31 +02:00
LagradOst
177b1e47f3 added extra logging 2023-10-04 11:33:55 +02:00
Luna712
3f5119525c
Make library preferences account specific (#649) 2023-10-03 22:59:26 +02:00
Luna712
b5d4c3bd27
Make player preferences account specific (#646) 2023-10-03 19:26:56 +02:00
Luna712
cc00e73e16
Make homepage preferences account specific (#647)
* Make homepage preferences account specific

* Fix accidentally removed whitespace

* Fix in setkey
2023-10-03 19:25:31 +02:00
Luna712
462073bd74
Make search prefs account specific (#640) 2023-10-03 16:56:38 +02:00
LagradOst
08060314ad preview seekbar m3u8 2023-10-03 16:50:34 +02:00
Luna712
1d90858f64
Make search history account specific (#638)
* Make search history account specific

* Update for clear history
2023-10-02 21:04:40 +02:00
LagradOst
bd05a67f26 preview seekbar 2023-10-02 17:44:06 +02:00
KingLucius
bb8cbb5167
More Amazon FireTV focus fixes (#636)
* cast item: mimic the same focus as ATV

* Source & Subtitles priority focus

* Subtitles sync focus

* Account management focus fixes
2023-10-01 01:26:07 +02:00
KingLucius
16c2290090
Amazon FireTV focus fixes (#635)
* Fix quick search button focus

* Switch profile button focus

* Cast & Recommendations focus

* Player: Profiles settings focus

* Player: Subtitles encoding settings focus

* profile selection: card item focus

* Search history item selectable & deleteable

* Search: search filter button next focus fix
2023-09-28 12:22:51 +02:00
KingLucius
194678c419
Player Source & Subs navigation change (#633) 2023-09-28 12:21:03 +02:00
Osten
0351053d80
Readded downloads to tv 2023-09-25 22:57:18 +02:00
Sofie
74060e7da3
Chillx: fix key (#628) 2023-09-25 13:48:35 +02:00
IndusAryan
1e2a11d6e4
refactor: speedostream and newpipe, tmdb update (#632)
* migrate speedostream (yomovies)

* update tmdb and newpipe
2023-09-25 13:48:05 +02:00
CranberrySoup
b8917ffa39
Fix episode layout when using RTL language (#631)
* Fix episode layout when using RTL language
2023-09-25 11:40:58 +02:00
CranberrySoup
d4fff7cee6
Add homepage search on TV (#618)
* Add search button on homepage for TV
2023-09-21 22:50:31 +02:00
LagradOst
527a6388a9 small fix 2023-09-21 22:46:23 +02:00
CranberrySoup
15333123cd
TV UI fixes (#612)
* TV UI fixes
2023-09-18 23:22:39 +02:00
LagradOst
2ae5b6cefb fixed the fucking updater 💀 2023-09-18 22:28:26 +02:00
LagradOst
0d2a19b350 bump 2023-09-17 20:38:59 +02:00
LagradOst
bff9727f96 Merge remote-tracking branch 'origin/master' 2023-09-17 20:35:11 +02:00
LagradOst
a82059cb57 fix 2023-09-17 20:35:01 +02:00
CranberrySoup
627c1bb223
Many UI fixes (#606)
* Lower targetSdk to get all installed packages

* Update sdk version

* Let's not be too radical

* Many fixes

* Revert targetSdk

* Make account homepage persistent
2023-09-16 00:30:34 +02:00
CranberrySoup
24977a8d62
Potential fix for crash loops (#608) 2023-09-15 22:47:59 +02:00
LagradOst
6957a8f95d bump 2023-09-14 20:30:44 +02:00
Sofie
2bed79b1f1
Update Gofile.kt (#600) 2023-09-14 12:53:54 +02:00
self-similarity
a5450e5da2
More robust player release (#601) 2023-09-14 12:53:35 +02:00
LagradOst
8fe34d3d2a Merge remote-tracking branch 'origin/master' 2023-09-11 23:05:47 +02:00
LagradOst
7d6ba8c7a4 tv changes for better centering + bigger text + better contrast 2023-09-11 23:05:10 +02:00
self-similarity
2baa75496e
Fix opensubtitles (#598)
* Fix OpenSubtitles
2023-09-11 20:13:42 +02:00
Sofie
01e7acdeac
getTracker: switched to anilist api (#593)
* getTracker: switched to anilist api

---------

Co-authored-by: Sofie99 <Sofie99@gmail.com>
2023-09-11 14:31:11 +02:00
LagradOst
10bc688eaf fixed tracker on dub 2023-09-11 14:29:30 +02:00
LagradOst
7f7c81828a added UI event for seekbar 2023-09-11 13:37:11 +02:00
KingLucius
f6b0ea8dfa
Stream button type switch support (#597)
* Stream button type switch support

* Hide Hls playlist switch
2023-09-10 15:31:01 +02:00
LagradOst
0afbc90cd2 fixed last fix 2023-09-09 23:57:18 +02:00
LagradOst
85c4c74222 fixed shitty external extractor code 2023-09-09 23:53:35 +02:00
LagradOst
b6e99d7358 backend change for events in player 2023-09-09 23:18:21 +02:00
self-similarity
130cc16e25
Simkl API optimizations (#581)
* Fix episode removal in simkl

* Simkl API optimizations
2023-09-09 00:13:04 +02:00
recloudstream[bot]
1629db2fc9 chore(locales): fix locale issues 2023-09-08 08:01:34 +00:00
Weblate (bot)
f05c65cf5c
Translated using Weblate (Odia) (#574)
Currently translated at 39.5% (249 of 630 strings)

Translated using Weblate (Odia)

Currently translated at 25.0% (1 of 4 strings)

Translated using Weblate (Odia)

Currently translated at 38.8% (245 of 630 strings)

Translated using Weblate (German)

Currently translated at 99.8% (629 of 630 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Tamil)

Currently translated at 20.9% (132 of 630 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (630 of 630 strings)

Translated using Weblate (Croatian)

Currently translated at 99.8% (629 of 630 strings)

Translated using Weblate (Croatian)

Currently translated at 99.3% (626 of 630 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Arabic (Najdi))

Currently translated at 54.9% (346 of 630 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (630 of 630 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (French)

Currently translated at 100.0% (630 of 630 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Hungarian)

Currently translated at 85.7% (540 of 630 strings)

Translated using Weblate (Romanian)

Currently translated at 95.7% (603 of 630 strings)

Translated using Weblate (Arabic (Najdi))

Currently translated at 50.1% (316 of 630 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (630 of 630 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 96.1% (606 of 630 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Arabic (Najdi))

Currently translated at 44.9% (283 of 630 strings)

Translated using Weblate (Arabic (Najdi))

Currently translated at 39.6% (250 of 630 strings)

Translated using Weblate (Arabic (Najdi))

Currently translated at 39.5% (249 of 630 strings)

Translated using Weblate (German)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (German)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (German)

Currently translated at 99.8% (629 of 630 strings)

Translated using Weblate (German)

Currently translated at 99.8% (629 of 630 strings)

Translated using Weblate (Arabic (Najdi))

Currently translated at 33.4% (211 of 630 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (630 of 630 strings)

Translated using Weblate (German)

Currently translated at 99.8% (629 of 630 strings)

Translated using Weblate (German)

Currently translated at 99.8% (629 of 630 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Arabic (Najdi))

Currently translated at 32.0% (202 of 630 strings)

Translated using Weblate (Portuguese (Brazil))

Currently translated at 91.5% (577 of 630 strings)

Translated using Weblate (Arabic (Saudi Arabia))

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Arabic (Najdi))

Currently translated at 23.9% (151 of 630 strings)

Added translation using Weblate (Arabic (South Levantine))

Translated using Weblate (Amharic)

Currently translated at 14.9% (94 of 630 strings)

Translated using Weblate (Tigrinya)

Currently translated at 15.0% (95 of 630 strings)

Added translation using Weblate (Amharic)

Added translation using Weblate (Tigrinya)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Hungarian)

Currently translated at 85.2% (537 of 630 strings)

Translated using Weblate (Hungarian)

Currently translated at 81.4% (513 of 630 strings)


















Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/am/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ars/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/
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/hu/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/or/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ro/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ta/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ti/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/ar_SA/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/de/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/or/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/uk/
Translation: Cloudstream/App
Translation: Cloudstream/Fastlane

Co-authored-by: Alexandru <negrualexandru52@gmail.com>
Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Cait Martin Newnham <85128509+helloiamcait@users.noreply.github.com>
Co-authored-by: Carlos Luiz <ecarlos-luiz@hotmail.com>
Co-authored-by: Chi Uma <jivanov.2048@gmail.com>
Co-authored-by: GobinathAL <gobinathal8@gmail.com>
Co-authored-by: Gyuri Bajzik <bajzikgy@gmail.com>
Co-authored-by: Joel Brink <joel.brink.handy@gmail.com>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: Mubarek Seyd Juhar <mubareksd@gmail.com>
Co-authored-by: Sam Cooper <samcooper838@gmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: Subham Jena <subhamjena8465@gmail.com>
Co-authored-by: mbottari <mbottari@quantum-systems.com>
Co-authored-by: pedrolinharesmoreira <pedrolinhares@unifei.edu.br>
Co-authored-by: tabtomi8 <tabtomi88@gmail.com>
2023-09-08 10:01:11 +02:00
IndusAryan
4ddd78ebb6
fook jitpack (#595) 2023-09-08 10:00:00 +02:00
LagradOst
49731cd699 changed drm API a bit 2023-09-06 22:42:22 +02:00
LagradOst
3fe247fb19 added drm player support 2023-09-06 20:53:43 +02:00
CranberrySoup
0839775172
Upgrade SDK (#590)
* Update sdk version

* Let's not be too radical
2023-09-04 22:36:36 +02:00
LagradOst
6211b02e85 switched from isM3u8 to ExtractorLinkType 2023-09-03 23:32:43 +02:00
Sofie
9c991f2abd
extractor: fix chillx (#583)
* Extractor: added Rabbitstream

* Extractor: added Rabbitstream

* extractor: fix Chillx

* comply

---------

Co-authored-by: Sofie99 <Sofie99@gmail.com>
2023-09-02 00:32:18 +02:00
LagradOst
6089cbc484 fixed subs on downloads 2023-08-30 00:52:34 +02:00
LagradOst
ce1f48978b fixed download error 2023-08-28 20:56:58 +02:00
LagradOst
f01820059b delete resume watching + delete bookmarks buttons. fixed backup crash 2023-08-27 19:07:08 +02:00
LagradOst
7d3b8c464e Merge remote-tracking branch 'origin/master' 2023-08-25 23:16:45 +02:00
LagradOst
8193e39b30 bump + refactor 2023-08-25 23:16:34 +02:00
recloudstream[bot]
557003895b chore(locales): fix locale issues 2023-08-25 08:59:37 +00:00
Weblate (bot)
d0c03321b9
Translations update from Hosted Weblate (#568)
Co-authored-by: Carlos Luiz <ecarlos-luiz@hotmail.com>
Co-authored-by: Joel Brink <joel.brink.handy@gmail.com>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: Mubarek Seyd Juhar <mubareksd@gmail.com>
Co-authored-by: Sam Cooper <samcooper838@gmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: mbottari <mbottari@quantum-systems.com>
Co-authored-by: tabtomi8 <tabtomi88@gmail.com>
2023-08-25 10:59:18 +02:00
Sofie
2d82480398
fix Rabbitstream (#573)
Co-authored-by: Sofie99 <Sofie99@gmail.com>
2023-08-25 10:58:58 +02:00
LagradOst
b38a9b1ff5 fuck android 2023-08-24 21:39:05 +02:00
LagradOst
1a4cbcaea0 small fix 2023-08-24 21:17:42 +02:00
LagradOst
9b4701fe91 dont remove keys while this is tested 2023-08-24 18:14:54 +02:00
LagradOst
c92ac3e8b3 fixed removal of predownloaded files 2 + permission 2023-08-24 18:13:42 +02:00
LagradOst
39ff6ef8ef Merge remote-tracking branch 'origin/master' 2023-08-24 16:40:09 +02:00
LagradOst
460b1be525 fixed removal of predownloaded files 2023-08-24 16:39:50 +02:00
CranberrySoup
9a1358e295
Lower targetSdk to get all installed packages (#571) 2023-08-24 16:16:33 +02:00
LagradOst
823ffd8708 reverted low api crash handle crashing 2023-08-24 00:25:05 +02:00
LagradOst
5bad6aca35 fixed native crash handle 2023-08-23 23:57:54 +02:00
LagradOst
e2502de02c bump acra 2023-08-23 18:43:55 +02:00
LagradOst
bac2ee9805 fixed div by zero 2023-08-23 17:08:26 +02:00
LagradOst
d436171a2f removed possible duplicate download queue 2023-08-23 06:36:43 +02:00
LagradOst
3ea6b1a8d5 fixed resume download + migrated filesystem to SafeFile 2023-08-23 06:25:06 +02:00
LagradOst
afcbdeecc8 changes to downloader for stable resume 2023-08-22 04:00:05 +02:00
LagradOst
4e28e5f8cc fixed not downloading the last 20MiB on mp4 downloader + bump + mb/s notification 2023-08-20 03:58:31 +02:00
LagradOst
1901eb371e Merge remote-tracking branch 'origin/master' 2023-08-20 01:29:57 +02:00
LagradOst
c4852ce440 made HSL downloader even faster 2023-08-20 01:29:50 +02:00
self-similarity
a3009af4f5
Add Native Crash Handler (#565)
* Add NativeCrashHandler

* Safer init
2023-08-19 21:48:10 +02:00
recloudstream[bot]
6948bf8073 chore(locales): fix locale issues 2023-08-19 19:38:45 +00:00
Weblate (bot)
61ca0a56be
Translations update from Hosted Weblate (#546)
Co-authored-by: Alexandru <negrualexandru52@gmail.com>
Co-authored-by: Alexthegib <jcwkgxc@nightorb.com>
Co-authored-by: Alexthegib <traducoes@skiff.com>
Co-authored-by: Amir <amearb@duck.com>
Co-authored-by: Astrid <github@astrid.exposed>
Co-authored-by: Carlos Luiz <ecarlos-luiz@hotmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Danilo <danilomaiarochaw@gmail.com>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Htet Oo Hlaing <htetoh2006@outlook.com>
Co-authored-by: Imprevisible <imprevisible@duck.com>
Co-authored-by: Jan Haider <jan.haider@i-kunden.de>
Co-authored-by: Jimuel Mallari <jimuelmallari284@gmail.com>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: PiterDev <piterzdev@gmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Reza Almanda <rezaalmanda27@gmail.com>
Co-authored-by: Rudy Tantono <rudzlong@gmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: dabao1955 <dabao1955@163.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: george kitsoukakis <norhorn@gmail.com>
Co-authored-by: infoekcz <Admin@infoek.cz>
Co-authored-by: tuan041 <tuananh163025ttt@gmail.com>
2023-08-19 21:38:29 +02:00
LagradOst
98b6417140 made downloader faster with parallel downloads 2023-08-19 21:37:14 +02:00
LagradOst
10c1ea2f02 Merge remote-tracking branch 'origin/master' 2023-08-19 17:03:47 +02:00
LagradOst
b3abf1e45f fixed decryption 2023-08-19 17:03:27 +02:00
IndusAryan
f571596bbc
fix: expand resume watching sheet and ft: ripple on profile drawable when pressed (#566)
* add ripple to profile icon on home

* Update HomeParentItemAdapterPreview.kt
2023-08-19 16:04:21 +02:00
LagradOst
e20e3dcfd3 fixed some bugs caused by new download update 2023-08-19 04:46:47 +02:00
LagradOst
35e1b8b4dc bump 2023-08-19 01:38:40 +02:00
LagradOst
a05616e3e8 fix 2023-08-19 01:37:48 +02:00
LagradOst
56cb3d7181 refactored download system for better preference + bugfixes 2023-08-19 00:48:00 +02:00
IndusAryan
e95dc1db2a
fix: cast items recycler (finally) (#564)
* turn cast items visible(tools)

* prevent cast gesture listener from permanent RIP in one lifecycle
2023-08-18 17:46:03 +02:00
LagradOst
8f6e8a8e99 fixed #547
fuck inheritance
2023-08-18 01:46:29 +02:00
IndusAryan
61d63b17d8
chore: acra improvements and media3 bump (#562)
* Acra Bump

* Media3 bump
2023-08-17 23:11:59 +02:00
LagradOst
590c74111c fuck it we ball, m3u8 download is now fixed 2023-08-17 23:10:21 +02:00
LagradOst
c2b951a078 fixed #560 lock locks orientation 2023-08-17 01:19:24 +02:00
LagradOst
cbaca158fa Merge remote-tracking branch 'origin/master' 2023-08-17 01:00:51 +02:00
LagradOst
20da3807a2 fixed search query for intent 2023-08-17 01:00:43 +02:00
IndusAryan
d247640dcf
Play n Dowload button fix for NS*W results. (#557)
* Play n Dowload button fix for NS*W results.

* Revert MainAPI Changes

* Tweaked ResultViewModel
2023-08-16 16:18:15 +02:00
IndusAryan
d536dffaf5
Fix Trailers not Working (#559)
* Fix Trailers not Working

* smol tip
2023-08-16 16:15:39 +02:00
self-similarity
4e01d327c6
Fix episode removal in simkl (#555) 2023-08-15 20:37:33 +02:00
LagradOst
4d98690adb small fix to home load 2023-08-15 02:05:07 +02:00
IndusAryan
74867bed1c
Update SpeedoStream.kt (#552)
Fixes YoMovies Provider.
2023-08-13 17:37:36 +02:00
LagradOst
0eb241e6cb fixed fab expand 2023-08-12 23:54:37 +02:00
LagradOst
3ab9e11350 fixed SimklApi subscription 2023-08-12 23:41:53 +02:00
self-similarity
d2d2e41fb3
Added Simkl (#548) 2023-08-12 22:25:30 +02:00
LagradOst
dd4f4a2b78 should fix an issue with auto_download_plugins_key 2023-08-12 21:52:37 +02:00
LagradOst
e43b4808d1 phone fix 2023-08-12 21:23:43 +02:00
LagradOst
3ac462ae96 changed UI a bit for flashbang + fixed crash inf loading bug 2023-08-12 21:20:51 +02:00
self-similarity
ecd529f73b
TV UX improvements (#538)
* Update styles.xml
2023-08-12 17:44:35 +02:00
Cloudburst
2d65aefc76 fix values-in 2023-08-10 09:34:29 +02:00
recloudstream[bot]
3af0bf750c chore(locales): fix locale issues 2023-08-09 21:56:47 +00:00
Weblate (bot)
72871c18b5
Translations update from Hosted Weblate (#535)
Co-authored-by: Alexandru <negrualexandru52@gmail.com>
Co-authored-by: Alexthegib <jcwkgxc@nightorb.com>
Co-authored-by: Alexthegib <traducoes@skiff.com>
Co-authored-by: Amir <amearb@duck.com>
Co-authored-by: Astrid <github@astrid.exposed>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Jan Haider <jan.haider@i-kunden.de>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: PiterDev <piterzdev@gmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Reza Almanda <rezaalmanda27@gmail.com>
Co-authored-by: Rudy Tantono <rudzlong@gmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: dabao1955 <dabao1955@163.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: george kitsoukakis <norhorn@gmail.com>
Co-authored-by: infoekcz <Admin@infoek.cz>
2023-08-09 23:56:28 +02:00
Cloudburst
44a2146c12
fix voting api (#544) 2023-08-09 23:44:17 +02:00
Sofie
bbbb7c4982
Extractor: added Rabbitstream (#536)
* Extractor: added Rabbitstream

* fix all request

---------

Co-authored-by: Sofie99 <Sofie99@gmail.com>
2023-08-05 03:11:46 +02:00
self-similarity
ca6700e28d
More meaningful errors when adding repositories (#537)
* More meaningful errors when adding repositories
2023-08-04 17:21:20 +02:00
LagradOst
5103ad09dc reverted gradle bump 2023-08-04 17:20:23 +02:00
LagradOst
f5c4864a3c tv focus changes + gradle bump + pip crash fix 2023-08-04 05:37:41 +02:00
recloudstream[bot]
653982a6bd chore(locales): fix locale issues 2023-08-02 19:14:54 +00:00
Weblate (bot)
22c0022684
Translations update from Hosted Weblate (#527)
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: PiterDev <piterzdev@gmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Reza Almanda <rezaalmanda27@gmail.com>
Co-authored-by: dabao1955 <dabao1955@163.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: infoekcz <Admin@infoek.cz>
2023-08-02 21:14:37 +02:00
LagradOst
7e6a28bb99 fixed tv focus issue 2023-08-02 21:00:04 +02:00
Vu Hoan Huy
c5f6f36fc7
fix: can not switch subtitle after integrate ffmpeg decoder. (#533)
* Revert "Revert "Use ffmpeg library (#528)" (#532)"

This reverts commit 87d85429f8.

* fix: can not select subtitle
2023-08-02 19:36:05 +02:00
self-similarity
3137a68552
fix player session id (#534) 2023-08-02 19:35:41 +02:00
LagradOst
2475088f76 added local accounts to TV layout 2023-08-02 17:31:55 +02:00
Osten
87d85429f8
Revert "Use ffmpeg library (#528)" (#532)
This reverts commit 32e243ce94.
2023-08-02 17:13:50 +02:00
self-similarity
32e243ce94
Use ffmpeg library (#528) 2023-08-02 05:31:35 +02:00
LagradOst
180987e2d0 added local accounts 2023-08-02 05:30:50 +02:00
LagradOst
b06f098447 fixed android tv trailer bug 2023-08-02 02:13:30 +02:00
recloudstream[bot]
6ff4f4c1ce chore(locales): fix locale issues 2023-08-01 14:04:17 +00:00
Weblate (bot)
0afc9f15d2
Translated using Weblate (Swedish) (#522)
Currently translated at 83.1% (518 of 623 strings)

Translated using Weblate (Swedish)

Currently translated at 72.8% (454 of 623 strings)

Translated using Weblate (Croatian)

Currently translated at 99.8% (622 of 623 strings)

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Galician)

Currently translated at 16.6% (104 of 623 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (623 of 623 strings)

Added translation using Weblate (Galician)

Translated using Weblate (Czech)

Currently translated at 100.0% (623 of 623 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (623 of 623 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (623 of 623 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (623 of 623 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Bulgarian)

Currently translated at 92.5% (575 of 621 strings)

Translated using Weblate (German)

Currently translated at 25.0% (1 of 4 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 50.0% (2 of 4 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 50.0% (2 of 4 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.3% (617 of 621 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (English)

Currently translated at 100.0% (4 of 4 strings)

Translated using Weblate (Polish)

Currently translated at 50.0% (2 of 4 strings)

Translated using Weblate (Greek)

Currently translated at 94.5% (587 of 621 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (621 of 621 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Japanese)

Currently translated at 46.3% (288 of 621 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (621 of 621 strings)

Merge remote-tracking branch 'origin/master'

Translated using Weblate (Indonesian)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Japanese)

Currently translated at 46.2% (287 of 621 strings)

Translated using Weblate (Bulgarian)

Currently translated at 90.6% (563 of 621 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (621 of 621 strings)























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/cs/
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/gl/
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/ja/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sv/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
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/fastlane/de/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/en/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/uk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/fastlane/zh_Hans/
Translation: Cloudstream/App
Translation: Cloudstream/Fastlane

Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Hristo Hristov <hristov.tanev@gmail.com>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Osten <11805592+LagradOst@users.noreply.github.com>
Co-authored-by: PiterDev <piterzdev@gmail.com>
Co-authored-by: Red Star Over Earth <rs0vere@outlook.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Reza Almanda <rezaalmanda27@gmail.com>
Co-authored-by: Rudy Tantono <rudzlong@gmail.com>
Co-authored-by: Salif Mehmed <mail@salif.eu>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: Thanasis <thanasakis11mail@gmail.com>
Co-authored-by: dabao1955 <dabao1955@163.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: ngocanhtve <ngocanh.tve@gmail.com>
Co-authored-by: tictactoe <phandinhminh@protonmail.ch>
2023-08-01 16:03:58 +02:00
Jace
827cbbb0b5
Feature: Refactor autodownload plugin to have multiple modes. (#518) 2023-08-01 15:54:15 +02:00
Sofie
a8ed8773de
Extractor: fix Gofile and added Userscloud (#523)
* Extractor: added Pixeldrain, Wibufile and fix some extractors

* Extractor: added Moviesapi and fix some extractors

* Extractor: fix Gofile and added Userscloud

---------

Co-authored-by: Sofie99 <Sofie99@gmail.com>
2023-08-01 15:50:02 +02:00
Osten
363ffa26de
Update README.md 2023-08-01 04:11:20 +02:00
LagradOst
7c60ccdef2 fixed #521 2023-08-01 04:03:43 +02:00
LagradOst
d5316bff9b fixed #524 2023-08-01 03:12:32 +02:00
Osten
8dae4c2b0f
Self similarity/master (#525)
* Migrated to Media3

---------

Co-authored-by: self-similarity <137652432+self-similarity@users.noreply.github.com>
2023-08-01 01:25:28 +02:00
recloudstream[bot]
6b87fb7831 chore(locales): fix locale issues 2023-07-31 13:43:46 +00:00
Cloudburst
6c325cf721
Translated using Weblate (Vietnamese) (#513)
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Hristo Hristov <hristov.tanev@gmail.com>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: PiterDev <piterzdev@gmail.com>
Co-authored-by: Red Star Over Earth <rs0vere@outlook.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Reza Almanda <rezaalmanda27@gmail.com>
Co-authored-by: Rudy Tantono <rudzlong@gmail.com>
Co-authored-by: Salif Mehmed <mail@salif.eu>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: Thanasis <thanasakis11mail@gmail.com>
Co-authored-by: dabao1955 <dabao1955@163.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: ngocanhtve <ngocanh.tve@gmail.com>
Co-authored-by: tictactoe <phandinhminh@protonmail.ch>
2023-07-31 15:43:25 +02:00
LagradOst
04ef6043b0 fixed small tv issues 2023-07-31 15:35:42 +02:00
Cloudburst
661dfc0927 Revert "bump ver"
This reverts commit 21e5a1e244.
2023-07-30 18:11:47 +00:00
Osten
4b4e006f4a
Merge pull request #507 from recloudstream/viewbindings
Migration to viewbindings
2023-07-30 15:15:44 +02:00
LagradOst
3bdbb35754 alert fix + synchronized + bump + homepage load fix + small focus change 2023-07-30 05:05:13 +02:00
LagradOst
c987f7581e major focus fixes 2023-07-28 04:18:28 +02:00
LagradOst
c98f35fd94 focus fix + rtl fix 2023-07-27 21:47:42 +02:00
LagradOst
a1824c86a3 made tv UI better on resultspage + ep range + focus bugfix 2023-07-26 05:44:37 +02:00
Rex_sa
bfb3313137
Add Arabic fastlane (#517)
* add arabic

* add arabic

* add arabic
2023-07-26 04:47:17 +02:00
LagradOst
31da089eb1 testing tag for tv focus + player popup fix 2023-07-25 21:15:10 +02:00
LagradOst
3e4a5bdf4c testing follow focus on tv 2023-07-24 04:02:05 +02:00
Mater Yoda
446f774fb4
added streamoupload extractor (#506)
* added streamoupload extractor
2023-07-24 04:01:50 +02:00
LagradOst
51a6e917b5 testing tv UI 2, NOT FINISHED 2023-07-23 20:10:21 +02:00
LagradOst
35084389a1 testing tv UI, NOT FINISHED 2023-07-23 03:07:24 +02:00
LagradOst
5aa9019d6d changed a bit of TV UI 2023-07-19 20:37:57 +02:00
LagradOst
da6577e587 actions java 11 -> 17 & fixed parent job is cancelling message 2023-07-19 18:47:22 +02:00
LagradOst
9755bbacb9 redirected downloadbutton longclick 2023-07-19 17:58:40 +02:00
LagradOst
3ae44d5675 readded download text progress 2023-07-19 17:27:47 +02:00
LagradOst
483ce2854f bump -> 8.0.2 2023-07-19 17:04:29 +02:00
LagradOst
21e5a1e244 bump ver 2023-07-19 02:06:55 +02:00
LagradOst
ed0d374721 removed synthetic by removing ResultFragment 2023-07-19 01:51:17 +02:00
LagradOst
4fcf396591 bug fix + white filler button 2023-07-18 22:35:17 +02:00
LagradOst
03d50a943a result -> result specific 2023-07-18 22:18:14 +02:00
LagradOst
d5c42f7d5a more result bindings + player 2023-07-18 03:55:00 +02:00
LagradOst
4f28aef8f2 GeneratorPlayer -> viewbinding, AbstractPlayerFragment -> findViewById,
parity between player_custom_layout_tv, player_custom_layout and  fragment_player, fragment_player_tv
2023-07-17 21:47:27 +02:00
LagradOst
f30506a394 homeparentitemadapterpreview -> viewbinding 2023-07-17 20:25:41 +02:00
LagradOst
4d6e64adb6 weakrefrence activity for cleaner code + HomeParentItemAdapterPreview viewmodel + more stuff in viewmodel 2023-07-17 03:32:41 +02:00
LagradOst
afadf121f4 testing leaks 2023-07-16 02:25:36 +02:00
LagradOst
a2a4da5a29 moved download view to nicer button 2023-07-15 23:43:09 +02:00
LagradOst
6bc5d86ff9 fix to color + fixed title on cards 2023-07-15 20:38:06 +02:00
LagradOst
f209c7286e more viewbindings + result fix + more tests 2023-07-15 20:00:09 +02:00
LagradOst
c946115900 mini fix 2023-07-15 03:27:25 +02:00
LagradOst
04f52f4a6d added tests for layout 2023-07-15 03:25:32 +02:00
LagradOst
273a947f8e more views 2023-07-14 22:05:13 +02:00
LagradOst
647e91bc4b more views + MainActivity viewbindings 2023-07-14 21:43:46 +02:00
Osten
c3296f3210
fixed bug with source priority 2023-07-14 21:33:14 +02:00
LagradOst
166a21f74e more views -> viewbinding 2023-07-14 02:28:49 +02:00
LagradOst
05a0d3cd81 migrated some items to viewbindings + removed Some<T> 2023-07-13 23:18:37 +02:00
Sofie
927453d9fe
Extractor: added Moviesapi and fix some extractors (#504)
* Extractor: added Pixeldrain, Wibufile and fix some extractors

* Extractor: added Moviesapi and fix some extractors

---------

Co-authored-by: Sofie99 <Sofie99@gmail.com>
2023-07-12 18:15:25 +02:00
recloudstream[bot]
9237817bd3 chore(locales): fix locale issues 2023-07-11 06:36:01 +00:00
Hosted Weblate
525bf8d861 Translated using Weblate (Odia)
Currently translated at 38.9% (242 of 621 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Turkish)

Currently translated at 99.5% (618 of 621 strings)

Translated using Weblate (Turkish)

Currently translated at 99.5% (618 of 621 strings)

Translated using Weblate (German)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Malay)

Currently translated at 20.4% (127 of 621 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (German)

Currently translated at 99.8% (620 of 621 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (621 of 621 strings)

Translated using Weblate (Odia)

Currently translated at 39.1% (239 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Hebrew)

Currently translated at 94.9% (579 of 610 strings)

Translated using Weblate (Malay)

Currently translated at 18.1% (111 of 610 strings)

Translated using Weblate (Urdu)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Odia)

Currently translated at 39.0% (238 of 610 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 46.0% (281 of 610 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 96.7% (590 of 610 strings)

Translated using Weblate (Odia)

Currently translated at 39.0% (238 of 610 strings)

Translated using Weblate (Odia)

Currently translated at 38.6% (236 of 610 strings)

Translated using Weblate (Bengali)

Currently translated at 38.1% (233 of 610 strings)

Co-authored-by: Aftabuzzaman <Leemon432@gmail.com>
Co-authored-by: Aitor Salaberria <trslbrr@gmail.com>
Co-authored-by: Alexthegib <jcwkgxc@nightorb.com>
Co-authored-by: Andreas <andreas.blindheim.koppen@gmail.com>
Co-authored-by: Bananenbrot <keram2810@outlook.de>
Co-authored-by: BluTiger <beqirstafa@gmail.com>
Co-authored-by: Clxff H3r4ld0 <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Dan <jonweblin2205@protonmail.com>
Co-authored-by: Efe Devirgen <efedevirgen@gmail.com>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kai <rafahdamin@gmail.com>
Co-authored-by: Levi Klippel <leviklippel@gmail.com>
Co-authored-by: Nathan Khutorskoy <natank44@gmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Subham Jena <subhamjena8465@gmail.com>
Co-authored-by: Sufyan Zahoor Jutt <sufyanpahore@gmail.com>
Co-authored-by: enescan201 <enescan14083@gmail.com>
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/bn/
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/es/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/he/
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/ms/
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/or/
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/tr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ur/
Translation: Cloudstream/App
2023-07-11 08:35:42 +02:00
Sofie
847957362f
Extractor: added Pixeldrain, Wibufile and fix some extractors (#502)
Co-authored-by: Sofie99 <Sofie99@gmail.com>
2023-07-10 01:52:03 +02:00
Nexus
51c1089162
Updated some of the extractor Domains (#495)
* Update AsianLoad.kt

Asianload domain changed

* Update Hxfile.kt

Some of the old url fix

* Update Moviehab.kt

* Update MultiQuality.kt

* Update AsianLoad.kt

* Update Hxfile.kt

* Update MultiQuality.kt
2023-07-02 19:41:19 +02:00
Nexus
da0be63b7c
Update ByteShare.kt (#494)
* Update ByteShare.kt
2023-06-30 18:19:52 +02:00
Saksham Shekher
a95fcfc9db
SpeedoStream update (#493) 2023-06-27 14:54:05 +02:00
imgbot[bot]
40a963588f
[ImgBot] Optimize images (#485)
*Total -- 351.66kb -> 337.87kb (3.92%)

Signed-off-by: ImgBotApp <ImgBotHelp@gmail.com>
Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2023-06-15 21:45:03 +02:00
LagradOst
906f1fdc9a
Fixed skip loading (#484)
* Added quality profiles

* Better quality selection

* Added profile bg and fixed some sources

* Properly fixed skip loading

* Extra safety

---------

Co-authored-by: Lag <>
2023-06-15 00:42:42 +02:00
LagradOst
b5566af401
Added quality profiles (#414)
* Added quality profiles

* Better quality selection

* Added profile bg and fixed some sources (#483)

Co-authored-by: Blatzar <>

---------

Co-authored-by: Lag <>
Co-authored-by: Osten <11805592+LagradOst@users.noreply.github.com>
2023-06-14 19:30:39 +02:00
Hexated
0d431fd508
fixed Stramsb & Voe (#470) 2023-05-20 23:37:17 +00:00
recloudstream[bot]
b115817357 chore(locales): fix locale issues 2023-05-14 16:20:11 +00:00
Hosted Weblate
c0a8461b87 Translated using Weblate (Kannada)
Currently translated at 36.2% (221 of 610 strings)

Translated using Weblate (Odia)

Currently translated at 38.5% (235 of 610 strings)

Translated using Weblate (Odia)

Currently translated at 26.3% (161 of 610 strings)

Translated using Weblate (Odia)

Currently translated at 22.6% (138 of 610 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Subham Jena <subhamjena8465@gmail.com>
Co-authored-by: deepu2 <deepuhsdeepak@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/kn/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/or/
Translation: Cloudstream/App
2023-05-14 18:19:51 +02:00
Shif-Jess
8c9d52bc0e
Added new Extractors (#461) 2023-05-14 18:19:04 +02:00
recloudstream[bot]
0f00b1baf0 chore(locales): fix locale issues 2023-05-10 07:27:01 +00:00
Cloudburst
ae1aaa3d7d Translated using Weblate (Ukrainian)
Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Hindi)

Currently translated at 38.3% (234 of 610 strings)

Translated using Weblate (Japanese)

Currently translated at 46.5% (284 of 610 strings)

Translated using Weblate (Odia)

Currently translated at 21.4% (131 of 610 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Persian)

Currently translated at 22.1% (135 of 610 strings)

Translated using Weblate (Odia)

Currently translated at 21.3% (130 of 610 strings)

Translated using Weblate (Odia)

Currently translated at 18.1% (111 of 610 strings)

Co-authored-by: 1 <zagukya@goatmail.uk>
Co-authored-by: Adarsh0-s <jeff19whisper@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: KING APPS <kiperking1@gmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: Subham Jena <subhamjena8465@gmail.com>
Co-authored-by: jhihyu lin <thomas.jy.lin@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fa/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hi/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ja/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/or/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hant/
Translation: Cloudstream/App
2023-05-10 07:26:34 +00:00
Cloudburst
b37aa55343
remove strings.xml comment 2023-05-10 09:16:24 +02:00
jhih_yu
77d4ecd7c6
Fix Traditional Chinese (zh-rTW) display name (#467) 2023-05-10 09:14:01 +02:00
Cloudburst
3b21ec3794
NewPipeExtractor:v0.22.6 -> NewPipeExtractor:master-SNAPSHOT (#468) 2023-05-10 09:13:48 +02:00
recloudstream[bot]
386ce75df1 chore(locales): fix locale issues 2023-05-03 20:30:03 +00:00
Hosted Weblate
27155e0f7e 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 (Hungarian)

Currently translated at 83.6% (510 of 610 strings)

Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hu/
Translation: Cloudstream/App
2023-05-03 22:29:45 +02:00
recloudstream[bot]
3121b5b123 chore(locales): fix locale issues 2023-05-03 20:17:55 +00:00
Hosted Weblate
8a5ddcd126 Added translation using Weblate (Odia)
Translated using Weblate (Vietnamese)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Latvian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Vietnamese)

Currently translated at 99.3% (606 of 610 strings)

Translated using Weblate (Malayalam)

Currently translated at 41.4% (253 of 610 strings)

Translated using Weblate (Vietnamese)

Currently translated at 96.3% (588 of 610 strings)

Translated using Weblate (Hungarian)

Currently translated at 83.6% (510 of 610 strings)

Translated using Weblate (Slovak)

Currently translated at 72.4% (442 of 610 strings)

Translated using Weblate (Slovak)

Currently translated at 44.9% (274 of 610 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Slovak)

Currently translated at 34.2% (209 of 610 strings)

Co-authored-by: Bojtár Zsömle <bence.bojtar@gmail.com>
Co-authored-by: Dinh Nguyen <dinhitcom@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Juraj Liso <lisojuraj@gmail.com>
Co-authored-by: Khoi <phamkhoi06@duck.com>
Co-authored-by: Kiên Tài <kientai142@gmail.com>
Co-authored-by: Subham Jena <subhamjena8465@gmail.com>
Co-authored-by: akku vijay <akkuvijay@duck.com>
Co-authored-by: liva <livinja@proton.me>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hu/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/lv/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ml/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/
Translation: Cloudstream/App
2023-05-03 22:17:37 +02:00
Cloudburst
42bf8ed08e
Update newpipe (#462) 2023-05-03 22:16:35 +02:00
Horis
fb3576ea52
fix video download (#453) 2023-04-21 13:56:17 +02:00
recloudstream[bot]
56a680fa9c chore(locales): fix locale issues 2023-04-21 11:56:05 +00:00
Hosted Weblate
633aef8783 Translated using Weblate (Macedonian)
Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Polish)

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 (Croatian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Korean)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Korean)

Currently translated at 93.6% (571 of 610 strings)

Translated using Weblate (Korean)

Currently translated at 87.5% (534 of 610 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Korean)

Currently translated at 31.4% (192 of 610 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Added translation using Weblate (Korean)

Translated using Weblate (Ukrainian)

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 (French)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Italian)

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 (Dutch)

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 (German)

Currently translated at 99.6% (608 of 610 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Portuguese)

Currently translated at 99.6% (608 of 610 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 90.8% (554 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 (Chinese (Simplified))

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Romanian)

Currently translated at 95.4% (582 of 610 strings)

Translated using Weblate (Romanian)

Currently translated at 75.0% (458 of 610 strings)

Translated using Weblate (Portuguese)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Indonesian)

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 (Spanish)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 90.4% (552 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 (Malayalam)

Currently translated at 37.8% (231 of 610 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (610 of 610 strings)

Added translation using Weblate (Arabic (Najdi))

Translated using Weblate (Latvian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Japanese)

Currently translated at 46.2% (282 of 610 strings)

Translated using Weblate (Macedonian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Latvian)

Currently translated at 30.4% (186 of 610 strings)

Added translation using Weblate (Latvian)

Translated using Weblate (German)

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 (Turkish)

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)

Translated using Weblate (Russian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Macedonian)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Turkish)

Currently translated at 99.8% (609 of 610 strings)

Translated using Weblate (Malayalam)

Currently translated at 37.2% (227 of 610 strings)

Co-authored-by: AHOHNMYC <lqwh2h2cwa@protonmail.com>
Co-authored-by: Aitor Salaberria <trslbrr@gmail.com>
Co-authored-by: Alexandru <negrualexandru52@gmail.com>
Co-authored-by: Alexthegib <jcwkgxc@nightorb.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Anurag <anuragrajanp@gmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Clxff Heraldo <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Eryk Michalak <gnu.ewm@protonmail.com>
Co-authored-by: FastAct <alex.rijckaert@gmail.com>
Co-authored-by: Felipe Nogueira <contato.fnog@gmail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
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: Milo Ivir <mail@milotype.de>
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: Synertry <jonny.somrak@gmail.com>
Co-authored-by: Tang Yin <bingyuanshiye@126.com>
Co-authored-by: The Unbreakable Spirit <U2001072@rajagiri.edu.in>
Co-authored-by: Turgay Doğru <turgaydogru@gmail.com>
Co-authored-by: Vrwi <jurgisbums@gmail.com>
Co-authored-by: Zaki Bouta <zbouta02@gmail.com>
Co-authored-by: edgolron <edgolron@tutanota.com>
Co-authored-by: eightyy8 <oliver.kha@pm.me>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: jinu147 <nesqea20@gmail.com>
Co-authored-by: stojkovskistefan <stefanstojkovski@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/de/
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/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/ko/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/lv/
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/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/
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/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-04-21 13:55:42 +02:00
Cloudburst
a12d234ef4 fix shortcodes 2023-04-21 13:54:58 +02:00
reduplicated
bdb45b69d3 pip fixes 2023-04-11 18:04:24 +02:00
reduplicated
4449347593 bump 2023-04-11 17:32:55 +02:00
Shif-Jess
b356ad9e61
CS3IPlayer: fix buffer lost when seeked to backward (#448)
* CS3IPlayer: fix buffer lost when seeked to backward

* changed BUFFER_MS
2023-04-11 09:46:39 +00:00
Sarlay
94e7eb8e9d
added a mirror to streamsb (#439) 2023-04-09 14:21:41 +00:00
Shif-Jess
4f9016713f
CS3Player: fixed ERROR_CODE_BEHIND_LIVE_WINDOW (#447) 2023-04-09 09:37:27 +02:00
recloudstream[bot]
4ed65f8e07 chore(locales): fix locale issues 2023-03-29 13:27:53 +00:00
Hosted Weblate
7317278f57 Translated using Weblate (Macedonian)
Currently translated at 97.7% (596 of 610 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Romanian)

Currently translated at 71.4% (436 of 610 strings)

Translated using Weblate (Dutch)

Currently translated at 90.1% (550 of 610 strings)

Translated using Weblate (Portuguese)

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 (Italian)

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 (Czech)

Currently translated at 100.0% (610 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 (Hungarian)

Currently translated at 83.9% (512 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)

Translated using Weblate (English)

Currently translated at 100.0% (610 of 610 strings)

Translated using Weblate (Polish)

Currently translated at 99.6% (608 of 610 strings)

Translated using Weblate (Italian)

Currently translated at 99.6% (608 of 610 strings)

Translated using Weblate (English)

Currently translated at 100.0% (610 of 610 strings)

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Hungarian)

Currently translated at 60.9% (372 of 610 strings)

Co-authored-by: Alexandru <negrualexandru52@gmail.com>
Co-authored-by: Alexthegib <jcwkgxc@nightorb.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Clxff Heraldo <123844876+clxf12@users.noreply.github.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: FastAct <alex.rijckaert@gmail.com>
Co-authored-by: Fjuro <ifjuro@proton.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: ZsoltiHUB <zsoltizsolti043@gmail.com>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: stojkovskistefan <stefanstojkovski@gmail.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/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/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/mk/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/
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/ro/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translation: Cloudstream/App
2023-03-29 15:27:35 +02:00
Shif-Jess
53293dadd0 fixed streamsb 2023-03-29 15:27:06 +02:00
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
Terry Hanoman
6c646d65a8
Use user setting for seeking on Android TV (#342)
* Use user setting for seeking on Android TV

* Fixed left dpad seeking

* Added a Android TV section for seeking

* Fixed text

* Removed semi-colons
2023-02-08 15:46:39 +00:00
LagradOst
329966732f
increased app update buffer size 2023-02-07 16:01:14 +00:00
Hosted Weblate
19b2cae851 Translated using Weblate (English (en_MO))
Currently translated at 47.9% (279 of 582 strings)

Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en_MO/
Translation: Cloudstream/App
2023-02-07 12:53:13 +01:00
recloudstream[bot]
45eb9758e3 update list of locales 2023-02-07 11:52:20 +00:00
Cloudburst
f6be6081dc use qt for monke language 2023-02-07 11:52:02 +00:00
recloudstream[bot]
80f22cea16 update list of locales 2023-02-07 10:21:26 +00:00
Cloudburst
bf78fc95c2 use en-rMO 2023-02-07 10:21:07 +00:00
Cloudburst
a148f347cd
[skip ci] issue action.yml 2023-02-07 11:15:00 +01:00
Cloudburst
ff9942407b use language a better language code for the easter egg 2023-02-07 10:11:23 +00:00
Hosted Weblate
0ea624ff14 Translated using Weblate (Russian)
Currently translated at 98.9% (576 of 582 strings)

Translated using Weblate (Hebrew)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 98.7% (575 of 582 strings)

Translated using Weblate (Hebrew)

Currently translated at 87.4% (509 of 582 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 98.4% (573 of 582 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 98.4% (573 of 582 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (German)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 98.2% (572 of 582 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 97.9% (570 of 582 strings)

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Bulgarian)

Currently translated at 97.2% (566 of 582 strings)

Translated using Weblate (English)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 97.9% (570 of 582 strings)

Translated using Weblate (German)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 97.9% (570 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 97.9% (570 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 97.9% (570 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 97.9% (570 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 97.9% (570 of 582 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 97.5% (568 of 582 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Czech)

Currently translated at 100.0% (582 of 582 strings)

Co-authored-by: Alexey <aleksejfedorov963@gmail.com>
Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Cliff Heraldo <cliffkbrt11@gmail.com>
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: Joel Brink <joel.brink.handy@gmail.com>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: Massimo Pissarello <mapi68@gmail.com>
Co-authored-by: Muhammet <zumruduanka0013@gmail.com>
Co-authored-by: Sdarfeesh <Sdarfeesh@protonmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: The Initiator <eithansten@gmail.com>
Co-authored-by: eightyy8 <oliver.kha@pm.me>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: kaajjo <claymanoff@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bg/
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/en/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/he/
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/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-02-07 11:05:37 +01:00
Cloudburst
f939e4cff2 update langs to use native names fix #339 2023-02-07 10:04:27 +00:00
no-commit
2ff90c03ca Moved backup restore to IO thread. 2023-02-04 15:32:04 +01:00
no-commit
9988753432 Library and Light mode improvements. 2023-02-02 01:15:24 +01:00
LagradOst
b0921161a3
Nicehttp version bump 2023-01-31 23:43:29 +01:00
Cloudburst
490381451b
[skip ci] label issues if provider mentioned 2023-01-31 10:57:11 +01:00
hexated
b26a41bdaf fixed VidSrcExtractor 2023-01-31 09:13:46 +01:00
Hosted Weblate
c7c5fa250e Translated using Weblate (Polish)
Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 96.5% (562 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 91.4% (532 of 582 strings)

Translated using Weblate (Greek)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 90.3% (526 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 90.3% (526 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 88.3% (514 of 582 strings)

Translated using Weblate (Russian)

Currently translated at 87.9% (512 of 582 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (German)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (582 of 582 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (581 of 581 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (581 of 581 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (581 of 581 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (581 of 581 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (581 of 581 strings)

Translated using Weblate (Vietnamese)

Currently translated at 100.0% (581 of 581 strings)

Translated using Weblate (Indonesian)

Currently translated at 100.0% (581 of 581 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (581 of 581 strings)

Co-authored-by: Aitor Salaberria <trslbrr@gmail.com>
Co-authored-by: Alex Georgiou <alexandrosgeorgiou35@gmail.com>
Co-authored-by: Cliff Heraldo <cliffkbrt11@gmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: JL Pilgram <twich_89@hotmail.it>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: NickSkier <nikita.vasiliev.02@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: eightyy8 <oliver.kha@pm.me>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: gnu-ewm <gnu.ewm@protonmail.com>
Co-authored-by: kaajjo <claymanoff@gmail.com>
Co-authored-by: tuan041 <tuananh163025ttt@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
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/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/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
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-01-31 09:13:15 +01:00
Blatzar
6e9b1cb855 Made source dialog fullscreen and added some Extractors 2023-01-29 23:51:25 +01:00
Blatzar
fd2648df45 made the checkSafeModeFile() crash-proof 2023-01-29 16:31:16 +01:00
Blatzar
9905618a47 Added safe mode file as a last resort 2023-01-29 16:15:28 +01:00
LagradOst
2771dcb612
update version code 2023-01-28 22:38:55 +00:00
LagradOst
3c82548c20
Library merge (#343) 2023-01-28 22:38:02 +00:00
recloudstream[bot]
9d11dc76a1 update list of locales 2023-01-28 18:43:31 +00:00
Hosted Weblate
2a1311673a Translated using Weblate (Russian)
Currently translated at 88.0% (499 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 86.7% (492 of 567 strings)

Translated using Weblate (Romanian)

Currently translated at 75.6% (429 of 567 strings)

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Russian)

Currently translated at 86.5% (491 of 567 strings)

Translated using Weblate (Vietnamese)

Currently translated at 99.2% (563 of 567 strings)

Translated using Weblate (Slovak)

Currently translated at 33.1% (188 of 567 strings)

Translated using Weblate (Somali)

Currently translated at 99.6% (565 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 84.1% (477 of 567 strings)

Translated using Weblate (German)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 84.1% (477 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 84.1% (477 of 567 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (567 of 567 strings)

Added translation using Weblate (Slovak)

Translated using Weblate (Vietnamese)

Currently translated at 99.4% (564 of 567 strings)

Co-authored-by: Alexey <aleksejfedorov963@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: John Doe <vj4ud1mc@sonofdavid.anonaddy.me>
Co-authored-by: Juraj Liso <lisojuraj@gmail.com>
Co-authored-by: Shafici Isxariifshe <mega12xhaphiee@gmail.com>
Co-authored-by: alex <hdhdhfhfbbffhhfhfjfjf@gmail.com>
Co-authored-by: eightyy8 <oliver.kha@pm.me>
Co-authored-by: kaajjo <claymanoff@gmail.com>
Co-authored-by: tuan041 <30403510+tuan041@users.noreply.github.com>
Co-authored-by: tuan041 <tuananh163025ttt@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bg/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/
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/tr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/
Translation: Cloudstream/App
2023-01-28 19:43:15 +01:00
Blatzar
83d2e692e0 Made player_video_title_rez disappear if blank 2023-01-28 19:39:12 +01:00
Blatzar
b2389bf14c Add padding in info & download to remove obtrusion by FAB 2023-01-28 19:00:10 +01:00
LiJu09
b2b16fccc5
[extractor] added ByteShare (#337)
* add byteshare extractor

* reformat code

* make it simple

* no regex
2023-01-27 23:44:00 +00:00
Blatzar
01f1edab3c webview crash fix 2023-01-26 00:34:55 +01:00
reduplicated
5050ff65c0 disabled crash reporting because yall keep crashing 2023-01-25 15:06:48 +01:00
Hosted Weblate
de720983a6 Translated using Weblate (Russian)
Currently translated at 83.7% (475 of 567 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 (Portuguese)

Currently translated at 81.3% (461 of 567 strings)

Translated using Weblate (Somali)

Currently translated at 99.6% (565 of 567 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 46.7% (265 of 567 strings)

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 46.7% (265 of 567 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Esperanto)

Currently translated at 28.7% (163 of 567 strings)

Translated using Weblate (Persian)

Currently translated at 19.0% (108 of 567 strings)

Translated using Weblate (Hungarian)

Currently translated at 58.7% (333 of 567 strings)

Translated using Weblate (German)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 83.5% (474 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 83.5% (474 of 567 strings)

Translated using Weblate (Kannada)

Currently translated at 14.9% (85 of 567 strings)

Translated using Weblate (Urdu)

Currently translated at 76.3% (433 of 567 strings)

Translated using Weblate (Tamil)

Currently translated at 18.8% (107 of 567 strings)

Translated using Weblate (Hebrew)

Currently translated at 37.5% (213 of 567 strings)

Translated using Weblate (Bengali)

Currently translated at 40.7% (231 of 567 strings)

Translated using Weblate (Bengali)

Currently translated at 40.7% (231 of 567 strings)

Translated using Weblate (Bengali)

Currently translated at 40.7% (231 of 567 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 99.2% (563 of 567 strings)

Translated using Weblate (Vietnamese)

Currently translated at 87.6% (497 of 567 strings)

Translated using Weblate (Vietnamese)

Currently translated at 87.6% (497 of 567 strings)

Translated using Weblate (Vietnamese)

Currently translated at 87.6% (497 of 567 strings)

Translated using Weblate (Turkish)

Currently translated at 97.0% (550 of 567 strings)

Translated using Weblate (Tagalog)

Currently translated at 59.2% (336 of 567 strings)

Translated using Weblate (Tagalog)

Currently translated at 59.2% (336 of 567 strings)

Translated using Weblate (Tagalog)

Currently translated at 59.2% (336 of 567 strings)

Translated using Weblate (Swedish)

Currently translated at 79.1% (449 of 567 strings)

Translated using Weblate (Swedish)

Currently translated at 79.1% (449 of 567 strings)

Translated using Weblate (Swedish)

Currently translated at 79.1% (449 of 567 strings)

Translated using Weblate (Romanian)

Currently translated at 74.7% (424 of 567 strings)

Translated using Weblate (Romanian)

Currently translated at 74.7% (424 of 567 strings)

Translated using Weblate (Romanian)

Currently translated at 74.7% (424 of 567 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 93.4% (530 of 567 strings)

Translated using Weblate (Dutch)

Currently translated at 79.3% (450 of 567 strings)

Translated using Weblate (Dutch)

Currently translated at 79.3% (450 of 567 strings)

Translated using Weblate (Dutch)

Currently translated at 79.3% (450 of 567 strings)

Translated using Weblate (Malayalam)

Currently translated at 38.9% (221 of 567 strings)

Translated using Weblate (Malayalam)

Currently translated at 38.9% (221 of 567 strings)

Translated using Weblate (Malayalam)

Currently translated at 38.9% (221 of 567 strings)

Translated using Weblate (Macedonian)

Currently translated at 51.1% (290 of 567 strings)

Translated using Weblate (Macedonian)

Currently translated at 51.1% (290 of 567 strings)

Translated using Weblate (Macedonian)

Currently translated at 51.1% (290 of 567 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Indonesian)

Currently translated at 99.6% (565 of 567 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Hindi)

Currently translated at 39.5% (224 of 567 strings)

Translated using Weblate (Hindi)

Currently translated at 39.5% (224 of 567 strings)

Translated using Weblate (Hindi)

Currently translated at 39.5% (224 of 567 strings)

Translated using Weblate (French)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Greek)

Currently translated at 99.8% (566 of 567 strings)

Translated using Weblate (Czech)

Currently translated at 72.6% (412 of 567 strings)

Translated using Weblate (Czech)

Currently translated at 72.6% (412 of 567 strings)

Translated using Weblate (Czech)

Currently translated at 72.6% (412 of 567 strings)

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

Currently translated at 81.8% (464 of 567 strings)

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

Currently translated at 81.8% (464 of 567 strings)

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

Currently translated at 81.8% (464 of 567 strings)

Translated using Weblate (Bulgarian)

Currently translated at 99.8% (566 of 567 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 83.4% (473 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 83.2% (472 of 567 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 81.6% (463 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 81.6% (463 of 567 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Indonesian)

Currently translated at 99.6% (565 of 567 strings)

Translated using Weblate (Somali)

Currently translated at 99.6% (565 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 60.4% (343 of 567 strings)

Translated using Weblate (Vietnamese)

Currently translated at 87.6% (497 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 55.9% (317 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 55.9% (317 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 55.9% (317 of 567 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Russian)

Currently translated at 53.2% (302 of 567 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Turkish)

Currently translated at 97.0% (550 of 567 strings)

Translated using Weblate (Somali)

Currently translated at 69.6% (395 of 567 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (567 of 567 strings)

new string translations

Co-authored-by: Alexey <aleksejfedorov963@gmail.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: Fikri Akbar <akbarfikri1221@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jonas Kahnwald <elcan.osmanov123@gmail.com>
Co-authored-by: Piotr Z <pzdanowiczp@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: Shafici Isxariifshe <mega12xhaphiee@gmail.com>
Co-authored-by: Sina Sharifkazemi <negatics@gmail.com>
Co-authored-by: SleepyOwl <artem726artem@gmail.com>
Co-authored-by: Translator-3000 <weblate.m1d0h@8shield.net>
Co-authored-by: Walter H <walter75@gmail.com>
Co-authored-by: eightyy8 <oliver.kha@pm.me>
Co-authored-by: kaajjo <claymanoff@gmail.com>
Co-authored-by: tuan041 <tuananh163025ttt@gmail.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/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/ro/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/
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-01-24 19:18:41 +01:00
Blatzar
0b4de81811 (hopefully) Fix home search and OpenSubtitles 2023-01-23 00:29:14 +01:00
reduplicated
60aca3ebdc very nice long hold popups 2023-01-21 23:22:48 +01:00
reduplicated
65fda1889c expandable resume watching go brrr 2023-01-21 20:05:37 +01:00
Hosted Weblate
b2b894caa9 Translated using Weblate (Somali)
Currently translated at 17.9% (102 of 567 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (567 of 567 strings)

Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/so/
Translation: Cloudstream/App
2023-01-21 13:11:59 +01:00
Cloudburst
c7e2a19f5d
fix build error 2023-01-21 13:04:53 +01:00
Hosted Weblate
9f18cbbc20 Translated using Weblate (Portuguese)
Currently translated at 81.3% (461 of 567 strings)

Translated using Weblate (German)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (French)

Currently translated at 100.0% (567 of 567 strings)

Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: tachyglossues <tachyglossues@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/
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/pt/
Translation: Cloudstream/App
2023-01-21 13:04:26 +01:00
recloudstream[bot]
1994edb96c update list of locales 2023-01-21 09:29:07 +00:00
Hosted Weblate
c058409f9d Translated using Weblate (Somali)
Currently translated at 17.9% (102 of 567 strings)

Translated using Weblate (Dutch)

Currently translated at 79.3% (450 of 567 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.8% (566 of 567 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (567 of 567 strings)

Translated using Weblate (Portuguese)

Currently translated at 16.6% (94 of 566 strings)

Translated using Weblate (Romanian)

Currently translated at 74.9% (424 of 566 strings)

Added translation using Weblate (Portuguese)

Translated using Weblate (Ukrainian)

Currently translated at 99.8% (565 of 566 strings)

Translated using Weblate (Somali)

Currently translated at 16.0% (91 of 566 strings)

Added translation using Weblate (Somali)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (Ukrainian)

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 93.6% (530 of 566 strings)

Translated using Weblate (German)

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (Turkish)

Currently translated at 96.6% (547 of 566 strings)

Translated using Weblate (Dutch)

Currently translated at 79.5% (450 of 566 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 49.4% (280 of 566 strings)

Translated using Weblate (Bengali)

Currently translated at 40.8% (231 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 40.4% (229 of 566 strings)

Translated using Weblate (Greek)

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.8% (565 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 40.2% (228 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 35.5% (201 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 31.0% (176 of 566 strings)

Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Ananas <dawidveimer@outlook.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Dan <denqwerta@gmail.com>
Co-authored-by: Feroli <feroli@tuta.io>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Inside <mihairobu123@gmail.com>
Co-authored-by: Jonas Kahnwald <elcan.osmanov123@gmail.com>
Co-authored-by: Kardi Demha <kardi.demha@gmail.com>
Co-authored-by: Martijn Slob <info@algebrakit.nl>
Co-authored-by: Michael <zuoyfxieglwgurqgic@tmmcv.net>
Co-authored-by: Shafici Isxariifshe <mega12xhaphiee@gmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: SleepyOwl <artem726artem@gmail.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/bn/
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/nb_NO/
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/ro/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/so/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translation: Cloudstream/App
2023-01-21 10:28:53 +01:00
Stormunblessed
3ecaf47c9e
fix fastream, tomatomatela, and added okrulink (#320) 2023-01-21 10:25:06 +01:00
Blatzar
b8248d1053 Added Mark as watched and fixed clicking episode synopsis 2023-01-20 23:26:46 +01:00
Blatzar
89c5cb8a46 Added better focus on dialogs on TV & fixed stream button on emulator layout 2023-01-20 01:16:05 +01:00
Hexated
9fd2e84c7a
fixed Linkbox (#319) 2023-01-20 00:44:26 +01:00
Hosted Weblate
8e928a8a2b Translated using Weblate (Spanish)
Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 49.4% (280 of 566 strings)

Translated using Weblate (Bengali)

Currently translated at 40.8% (231 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 40.4% (229 of 566 strings)

Translated using Weblate (Greek)

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (Ukrainian)

Currently translated at 99.8% (565 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 40.2% (228 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 35.5% (201 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 31.0% (176 of 566 strings)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kardi Demha <kardi.demha@gmail.com>
Co-authored-by: Skrripy <rozihrash.ya6w7@simplelogin.com>
Co-authored-by: SleepyOwl <artem726artem@gmail.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/bn/
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/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/
Translation: Cloudstream/App
2023-01-18 19:55:15 +01:00
Blatzar
49d672718d Fix titles not showing in some cases 2023-01-18 19:03:50 +01:00
Blatzar
a8352d3f64 Prettified the subtitle year dialog 2023-01-17 16:57:46 +01:00
Jace
42f90a79c4
[Feature] Allow input of Year on Subtitle search (#232)
* Use query text as default filename of subtitle instead of empty string.

* [Feature] Allow input of Year on Subtitle search
2023-01-17 15:11:49 +00:00
Blatzar
cd8c5966e6 Made player_view unfocusable to prevent white overlay on Chromebooks 2023-01-16 23:49:59 +01:00
Cloudburst
307d4dd494
Update SettingsGeneral.kt 2023-01-15 12:07:54 +01:00
recloudstream[bot]
d606f84545 update list of locales 2023-01-15 11:05:23 +00:00
Cloudburst
60c1eb2579
Update update_locales.yml 2023-01-15 12:04:57 +01:00
hexated
5c8a667e9e fixed StreamSB 2023-01-15 11:58:41 +01:00
Hosted Weblate
2674d370a2 Translated using Weblate (Italian)
Currently translated at 100.0% (566 of 566 strings)

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translated using Weblate (Norwegian Nynorsk)

Currently translated at 46.8% (265 of 566 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 93.2% (528 of 566 strings)

Added translation using Weblate (Norwegian Nynorsk)

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Sandyran <sandyran@protonmail.com>
Co-authored-by: Translator-3000 <weblate.m1d0h@8shield.net>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nn/
Translation: Cloudstream/App
2023-01-15 11:58:25 +01:00
recloudstream[bot]
868bb8500f update list of locales 2023-01-14 12:08:00 +00:00
Hosted Weblate
a87bbd3cfc Translated using Weblate (Ukrainian)
Currently translated at 83.0% (470 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 28.9% (164 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 21.3% (121 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 20.8% (118 of 566 strings)

Translated using Weblate (Indonesian)

Currently translated at 99.4% (563 of 566 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (Ukrainian)

Currently translated at 62.0% (351 of 566 strings)

Translated using Weblate (Ukrainian)

Currently translated at 61.1% (346 of 566 strings)

Translated using Weblate (Ukrainian)

Currently translated at 37.1% (210 of 566 strings)

Translated using Weblate (Russian)

Currently translated at 19.4% (110 of 566 strings)

Translated using Weblate (Turkish)

Currently translated at 92.4% (523 of 566 strings)

Added translation using Weblate (Ukrainian)

Translated using Weblate (Hungarian)

Currently translated at 58.8% (333 of 566 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (French)

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (566 of 566 strings)

Translated using Weblate (French)

Currently translated at 100.0% (565 of 565 strings)

Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Anna Teplaya <na-nebesax-anna@yandex.ru>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jonas Kahnwald <elcan.osmanov123@gmail.com>
Co-authored-by: Nepx <anandabaskara@outlook.com>
Co-authored-by: Radoslav Vasilev Vasilev <fifata@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: Valere <weblare@hostux.net>
Co-authored-by: d4f5409d <d4f5409d-4b6a-4640-9ff3-155ebcdc7ab7@anonaddy.me>
Co-authored-by: eightyy8 <oliver.kha@pm.me>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: h <tachyglossues@gmail.com>
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/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/hu/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/
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-01-14 13:07:39 +01:00
Horis
92d03fc163
Add Dailymotion Extractor (#312) 2023-01-14 13:07:21 +01:00
Blatzar
06c2cf86ec Various Android TV homepage fixes 2023-01-09 02:15:06 +01:00
Blatzar
0ebc12e29b Merge remote-tracking branch 'origin/master' 2023-01-07 14:57:50 +01:00
Blatzar
308affb6aa Fixed loading disabled plugins on download 2023-01-07 14:56:52 +01:00
Cloudburst
75cc4f6dfa
trigger rerun ci 2023-01-06 22:28:16 +01:00
Cloudburst
61ab957e35
make the update_locales not use github actions token
gh actions token supposedly makes it so the next action doesnt trigger on push
2023-01-06 22:27:23 +01:00
GitHub Actions
36e780f7c9 update list of locales 2023-01-06 16:56:31 +00:00
Hosted Weblate
e7d37aa07c Translated using Weblate (German)
Currently translated at 100.0% (565 of 565 strings)

Translated using Weblate (Hungarian)

Currently translated at 56.6% (320 of 565 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (565 of 565 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (565 of 565 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (565 of 565 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (565 of 565 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (565 of 565 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (565 of 565 strings)

Translated using Weblate (Esperanto)

Currently translated at 28.9% (163 of 563 strings)

Added translation using Weblate (Esperanto)

Translated using Weblate (Hungarian)

Currently translated at 37.4% (211 of 563 strings)

Translated using Weblate (Hebrew)

Currently translated at 37.8% (213 of 563 strings)

Translated using Weblate (Hungarian)

Currently translated at 21.3% (120 of 563 strings)

Translated using Weblate (Persian)

Currently translated at 18.4% (104 of 563 strings)

Translated using Weblate (Russian)

Currently translated at 19.0% (107 of 563 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Swedish)

Currently translated at 79.2% (446 of 563 strings)

Translated using Weblate (Persian)

Currently translated at 17.0% (96 of 563 strings)

Translated using Weblate (Russian)

Currently translated at 17.7% (100 of 563 strings)

Translated using Weblate (Russian)

Currently translated at 17.7% (100 of 563 strings)

Translated using Weblate (Swedish)

Currently translated at 79.0% (445 of 563 strings)

Added translation using Weblate (Persian)

Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: LagradOst <blatzar@gmail.com>
Co-authored-by: Milo Ivir <mail@milotype.de>
Co-authored-by: Márkó <gost1336@gmail.com>
Co-authored-by: Piotr Strebski <strebski@gmail.com>
Co-authored-by: Pmmmp frd <Pmmmpfrd@hi2.in>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Sdarfeesh <Sdarfeesh@protonmail.com>
Co-authored-by: Translator-3000 <weblate.m1d0h@8shield.net>
Co-authored-by: Vandam <goatli@danwin1210.de>
Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Co-authored-by: inkarnation <94744834+inkarnation@users.noreply.github.com>
Co-authored-by: phlostically <phlostically@mailinator.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/
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/he/
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/it/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sv/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/
Translation: Cloudstream/App
2023-01-06 17:56:15 +01:00
reduplicated
c57fce2abc save state of popup menu 2023-01-06 17:51:34 +01:00
Blatzar
657971d008 Added delayed updating on A13 to make it less jarring 2023-01-03 20:56:03 +01:00
Cloudburst
0afb6b62aa
Update update_locales.yml 2023-01-02 12:44:23 +01:00
GitHub Actions
e362795493 chore: update list of locales 2023-01-02 11:43:28 +00:00
Hosted Weblate
8712f08bb1 Translated using Weblate (Bengali)
Currently translated at 39.9% (225 of 563 strings)

Translated using Weblate (Hungarian)

Currently translated at 17.7% (100 of 563 strings)

Translated using Weblate (Bengali)

Currently translated at 39.7% (224 of 563 strings)

Translated using Weblate (Turkish)

Currently translated at 88.9% (501 of 563 strings)

Translated using Weblate (Dutch)

Currently translated at 78.8% (444 of 563 strings)

Translated using Weblate (Hungarian)

Currently translated at 17.5% (99 of 563 strings)

Translated using Weblate (Hungarian)

Currently translated at 17.5% (99 of 563 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (563 of 563 strings)

Added translation using Weblate (Hungarian)

Translated using Weblate (Russian)

Currently translated at 17.5% (99 of 563 strings)

Translated using Weblate (Russian)

Currently translated at 15.6% (88 of 563 strings)

Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Eff ji <Paper.Pepperoni@protonmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: g0szt <gost1336@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hu/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/
Translation: Cloudstream/App
2023-01-02 12:43:15 +01:00
Cloudburst
591ac137f9
Add ebd.cda.pl and automatically set legacy for miui (#296)
Co-authored-by: codefactor-io <support@codefactor.io>
2023-01-02 12:42:58 +01:00
Hosted Weblate
dee269ce5e Translated using Weblate (German)
Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Indonesian)

Currently translated at 99.4% (560 of 563 strings)

Translated using Weblate (Hindi)

Currently translated at 39.7% (224 of 563 strings)

Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: Walter H <walter75@gmail.com>
Co-authored-by: sonacore <sonacore@gmail.com>
Co-authored-by: translate <boledo7225@khaxan.com>
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/hi/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/
Translation: Cloudstream/App
2022-12-29 11:24:06 +01:00
Horis
4926c91f6c
Fix .json file cant select on restore. (#292) 2022-12-29 11:19:08 +01:00
LagradOst
2b43342854 Merge branch 'master' of https://github.com/recloudstream/cloudstream
# Conflicts:
#	app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt
2022-12-29 03:11:08 +01:00
LagradOst
6e61fe5f3e mini fix 2022-12-29 03:08:08 +01:00
Cloudburst
1e8277b087
[skip ci] Apply fixes from CodeFactor (#289)
Co-authored-by: codefactor-io <support@codefactor.io>
2022-12-28 18:41:50 +01:00
LagradOst
710885a3b7 none fix 2022-12-28 15:25:22 +01:00
LagradOst
fbb7046390 Merge branch 'master' of https://github.com/recloudstream/cloudstream 2022-12-28 13:29:30 +01:00
LagradOst
714062c6d4 removed bloat 2022-12-28 13:29:23 +01:00
Cloudburst
83132f183a
fix translations 2022-12-28 13:21:46 +01:00
Hosted Weblate
79c8b4e523 Translated using Weblate (German)
Currently translated at 94.6% (533 of 563 strings)

Translated using Weblate (German)

Currently translated at 94.1% (530 of 563 strings)

Translated using Weblate (German)

Currently translated at 94.1% (530 of 563 strings)

Translated using Weblate (German)

Currently translated at 15.4% (87 of 563 strings)

Added translation using Weblate (German)

Translated using Weblate (Spanish)

Currently translated at 90.4% (509 of 563 strings)

Translated using Weblate (Spanish)

Currently translated at 90.4% (509 of 563 strings)

Translated using Weblate (Spanish)

Currently translated at 71.9% (405 of 563 strings)

Translated using Weblate (Indonesian)

Currently translated at 95.2% (536 of 563 strings)

Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Julian <hello@apollo.moe>
Co-authored-by: Sonaji Yusup <sonacore@gmail.com>
Co-authored-by: TubaApollo <86665265+TubaApollo@users.noreply.github.com>
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/id/
Translation: Cloudstream/App
2022-12-28 13:20:54 +01:00
codefactor-io
c6749bf988 [CodeFactor] Apply fixes to commit 4440096 2022-12-28 13:19:13 +01:00
LagradOst
7019631146 Merge branch 'master' of https://github.com/recloudstream/cloudstream 2022-12-28 13:10:54 +01:00
LagradOst
4440096ea4 fixed scroll 2022-12-28 13:09:00 +01:00
LagradOst
2a32f62fe3 tv UI change + homepage optimization 2022-12-28 12:51:55 +01:00
Cloudburst
7982f8c491
localization fixes 2022-12-28 11:52:56 +01:00
GitHub Actions
d6af1e4ab6 chore: update list of locales 2022-12-28 10:48:52 +00:00
Hosted Weblate
53b06612c1 Translated using Weblate (Spanish)
Currently translated at 68.0% (383 of 563 strings)

Translated using Weblate (Urdu)

Currently translated at 76.9% (433 of 563 strings)

Translated using Weblate (Spanish)

Currently translated at 18.8% (106 of 563 strings)

Added translation using Weblate (Spanish)

Added translation using Weblate (Russian)

Translated using Weblate (Urdu)

Currently translated at 53.4% (301 of 563 strings)

Translated using Weblate (Urdu)

Currently translated at 46.8% (264 of 563 strings)

Translated using Weblate (Tamil)

Currently translated at 19.0% (107 of 563 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Hindi)

Currently translated at 39.6% (223 of 563 strings)

Added translation using Weblate (Kannada)

Added translation using Weblate (Urdu)

Added translation using Weblate (Tamil)

Translated using Weblate (Hebrew)

Currently translated at 35.8% (202 of 563 strings)

Translated using Weblate (Bengali)

Currently translated at 34.9% (197 of 563 strings)

Translated using Weblate (Vietnamese)

Currently translated at 84.9% (478 of 563 strings)

Translated using Weblate (Turkish)

Currently translated at 87.9% (495 of 563 strings)

Translated using Weblate (Tagalog)

Currently translated at 59.6% (336 of 563 strings)

Translated using Weblate (Swedish)

Currently translated at 64.1% (361 of 563 strings)

Translated using Weblate (Romanian)

Currently translated at 74.6% (420 of 563 strings)

Translated using Weblate (Dutch)

Currently translated at 78.6% (443 of 563 strings)

Translated using Weblate (Malayalam)

Currently translated at 39.2% (221 of 563 strings)

Translated using Weblate (Macedonian)

Currently translated at 51.5% (290 of 563 strings)

Translated using Weblate (Indonesian)

Currently translated at 89.1% (502 of 563 strings)

Translated using Weblate (Hindi)

Currently translated at 39.0% (220 of 563 strings)

Translated using Weblate (Czech)

Currently translated at 73.1% (412 of 563 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Indonesian)

Currently translated at 89.1% (502 of 563 strings)

Translated using Weblate (Swedish)

Currently translated at 64.1% (361 of 563 strings)

Translated using Weblate (Indonesian)

Currently translated at 78.3% (441 of 563 strings)

Translated using Weblate (Croatian)

Currently translated at 100.0% (563 of 563 strings)

Co-authored-by: Abinanthankv <abinanthankv@protonmail.com>
Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: John Smith <r9hwlp66@anonaddy.me>
Co-authored-by: Kolon Brin <brinkolon@gmail.com>
Co-authored-by: LagradOst <blatzar@gmail.com>
Co-authored-by: Muhammad Fahad Khan <itxmfahadkhan@gmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Sonaji Yusup <sonacore@gmail.com>
Co-authored-by: Tempo <lafemot433@octovie.com>
Co-authored-by: Walter H <walter75@gmail.com>
Co-authored-by: jhihyu lin <thomas.jy.lin@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/
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/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/id/
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/nl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ro/
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/ur/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hant/
Translation: Cloudstream/App
2022-12-28 11:48:39 +01:00
Hosted Weblate
9fc5c5352e Translated using Weblate (Croatian)
Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Croatian)

Currently translated at 97.8% (551 of 563 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 93.6% (527 of 563 strings)

Translated using Weblate (Greek)

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (563 of 563 strings)

Co-authored-by: Alex Georgiou <alexandrosgeorgiou35@gmail.com>
Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Anarchydr <patrikpik879@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Radoslav Vasilev Vasilev <fifata@gmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
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/el/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nb_NO/
Translation: Cloudstream/App
2022-12-26 20:55:50 +01:00
Horis
5f1e790163
Fix backup on A13 (#280) 2022-12-26 19:55:23 +00:00
Cloudburst
0073ad8c81
fix zh-TW 2022-12-25 19:56:59 +01:00
Cloudburst
6db688e0bf
only run on master 2022-12-25 19:52:30 +01:00
GitHub Actions
c11bab4a51 chore: update list of locales 2022-12-25 19:50:50 +01:00
Hosted Weblate
e71b70b6a0 Translated using Weblate (Swedish)
Currently translated at 58.0% (327 of 563 strings)

Translated using Weblate (Hebrew)

Currently translated at 35.5% (200 of 563 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (French)

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Hebrew)

Currently translated at 29.8% (168 of 563 strings)

Translated using Weblate (Swedish)

Currently translated at 56.1% (316 of 563 strings)

Added translation using Weblate (Hebrew)

Translated using Weblate (Greek)

Currently translated at 100.0% (563 of 563 strings)

Co-authored-by: Alex Georgiou <alexandrosgeorgiou35@gmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: LagradOst <blatzar@gmail.com>
Co-authored-by: The Initiator <eithansten@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/el/
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/it/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sv/
Translation: Cloudstream/App
2022-12-25 19:50:50 +01:00
GitHub Actions
7cf9c640b8 chore: update list of locales 2022-12-25 18:50:12 +00:00
Cloudburst
9c956f68f9
make CI add langs from weblate 2022-12-25 19:49:59 +01:00
LagradOst
2ba78eb37e
Revert "Update AndroidManifest.xml (#273)" (#275)
This reverts commit 871dcf7171.
2022-12-24 17:11:52 +01:00
Hosted Weblate
9e059af0bb Translated using Weblate (Greek)
Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Greek)

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (563 of 563 strings)

Translated using Weblate (Polish)

Currently translated at 100.0% (562 of 562 strings)

Co-authored-by: Alex Georgiou <alexandrosgeorgiou35@gmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Sdarfeesh <Sdarfeesh@protonmail.com>
Co-authored-by: Translator-3000 <weblate.m1d0h@8shield.net>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/el/
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/zh_Hans/
Translation: Cloudstream/App
2022-12-24 16:37:42 +01:00
krrishkap
871dcf7171
Update AndroidManifest.xml (#273)
Enabling Api for predictive back gesture for A13 .
2022-12-24 16:05:20 +01:00
Blatzar
4f4061961a Merge remote-tracking branch 'origin/master' 2022-12-23 22:54:09 +01:00
Blatzar
5b26c998b4 Added trailers and poster to TV 2022-12-23 22:53:51 +01:00
Hosted Weblate
5953420774 Translated using Weblate (Polish)
Currently translated at 100.0% (562 of 562 strings)

Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/
Translation: Cloudstream/App
2022-12-23 20:54:43 +01:00
Cloudburst
f3e7a5daa6
Add categories to settings pages 2022-12-23 20:50:05 +01:00
Blatzar
b6b7cceea5 Changed SeekParameters to allow for faster seeking + fixed apk installer text 2022-12-23 17:33:54 +01:00
Deepak Patil
23973042f4
feat(utils): add Jshunter deobfuscator (#250)
* feat(utils): add Jshunter deobfuscator

Co-authored-by: contusionglory <102427829+contusionglory@users.noreply.github.com>
2022-12-23 12:55:27 +01:00
Hosted Weblate
a2dbabdb6e Translated using Weblate (Polish)
Currently translated at 100.0% (548 of 548 strings)

Translated using Weblate (Chinese (Traditional))

Currently translated at 100.0% (548 of 548 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (548 of 548 strings)

Translated using Weblate (French)

Currently translated at 100.0% (548 of 548 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 97.8% (536 of 548 strings)

Translated using Weblate (French)

Currently translated at 87.7% (481 of 548 strings)

Translated using Weblate (Czech)

Currently translated at 75.1% (412 of 548 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (548 of 548 strings)

Co-authored-by: Bitpaint <bitpaintclub@gmail.com>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Sarlay <raphmd0@gmail.com>
Co-authored-by: Sdarfeesh <Sdarfeesh@protonmail.com>
Co-authored-by: Václav Ornst <mplay.octopus@gmail.com>
Co-authored-by: jhihyu lin <thomas.jy.lin@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/fr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/
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
2022-12-23 12:48:15 +01:00
Blatzar
53519381d7 Set default start season to 1 2022-12-22 13:11:37 +01:00
Hosted Weblate
a522ef0edb Translated using Weblate (Polish)
Currently translated at 98.7% (541 of 548 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (548 of 548 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (543 of 543 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (543 of 543 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 93.5% (508 of 543 strings)

Translated using Weblate (Italian)

Currently translated at 99.0% (538 of 543 strings)

Translated using Weblate (French)

Currently translated at 79.7% (433 of 543 strings)

Translated using Weblate (French)

Currently translated at 79.7% (433 of 543 strings)

Translated using Weblate (Arabic)

Currently translated at 99.4% (540 of 543 strings)

Translated using Weblate (English)

Currently translated at 100.0% (543 of 543 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 87.8% (477 of 543 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (543 of 543 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 86.9% (472 of 543 strings)

Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Sarlay <raphmd0@gmail.com>
Co-authored-by: Sdarfeesh <Sdarfeesh@protonmail.com>
Co-authored-by: Translator-3000 <weblate.m1d0h@8shield.net>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/
Translation: Cloudstream/App
2022-12-21 18:45:30 +01:00
Blatzar
d727099c29 add apk installer options for memeUI 2022-12-21 15:19:49 +01:00
Hosted Weblate
e2fc946d91 Translated using Weblate (Tagalog)
Currently translated at 62.2% (338 of 543 strings)

Translated using Weblate (Chinese (Simplified))

Currently translated at 98.3% (534 of 543 strings)

Translated using Weblate (Tagalog)

Currently translated at 53.7% (292 of 543 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 86.9% (472 of 543 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (543 of 543 strings)

Translated using Weblate (English)

Currently translated at 100.0% (543 of 543 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 84.5% (459 of 543 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (543 of 543 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (543 of 543 strings)

Translated using Weblate (French)

Currently translated at 72.9% (396 of 543 strings)

Translated using Weblate (Arabic)

Currently translated at 100.0% (543 of 543 strings)

Translated using Weblate (English)

Currently translated at 100.0% (543 of 543 strings)

Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: J. Lavoie <j.lavoie@net-c.ca>
Co-authored-by: Jace Orwell <jaceorwell@gmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: Sdarfeesh <Sdarfeesh@protonmail.com>
Co-authored-by: Translator-3000 <weblate.m1d0h@8shield.net>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/
Translation: Cloudstream/App
2022-12-20 10:11:57 +01:00
Hosted Weblate
a1f5786f02 Translated using Weblate (Polish)
Currently translated at 95.0% (516 of 543 strings)

Translated using Weblate (English)

Currently translated at 100.0% (542 of 542 strings)

Translated using Weblate (Bengali)

Currently translated at 36.1% (196 of 542 strings)

Translated using Weblate (Vietnamese)

Currently translated at 90.5% (491 of 542 strings)

Translated using Weblate (Turkish)

Currently translated at 93.9% (509 of 542 strings)

Translated using Weblate (Tagalog)

Currently translated at 52.9% (287 of 542 strings)

Translated using Weblate (Romanian)

Currently translated at 78.9% (428 of 542 strings)

Translated using Weblate (Polish)

Currently translated at 94.6% (513 of 542 strings)

Translated using Weblate (Polish)

Currently translated at 94.6% (513 of 542 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 83.5% (453 of 542 strings)

Translated using Weblate (Dutch)

Currently translated at 83.2% (451 of 542 strings)

Translated using Weblate (Italian)

Currently translated at 99.2% (538 of 542 strings)

Translated using Weblate (Czech)

Currently translated at 77.4% (420 of 542 strings)

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

Currently translated at 87.6% (475 of 542 strings)

Translated using Weblate (Bulgarian)

Currently translated at 98.8% (536 of 542 strings)

Translated using Weblate (Bulgarian)

Currently translated at 98.8% (536 of 542 strings)

Translated using Weblate (Arabic)

Currently translated at 98.8% (536 of 542 strings)

Translated using Weblate (Arabic)

Currently translated at 98.8% (536 of 542 strings)

Translated using Weblate (English)

Currently translated at 100.0% (542 of 542 strings)

Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jace Orwell <jaceorwell@gmail.com>
Co-authored-by: Radoslav Vasilev Vasilev <fifata@gmail.com>
Co-authored-by: Rex_sa <rex.sa@pm.me>
Co-authored-by: duckling <salmanfc.bd@gmail.com>
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/en/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/
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/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ro/
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/vi/
Translation: Cloudstream/App
2022-12-19 10:13:47 +01:00
Cloudburst
363906cf3b add summary to automatic_plugin_download 2022-12-19 10:10:29 +01:00
Hosted Weblate
50fc8d0ffb Translated using Weblate (Bengali)
Currently translated at 25.0% (136 of 542 strings)

Translated using Weblate (Bengali)

Currently translated at 25.0% (136 of 542 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (542 of 542 strings)

Translated using Weblate (Greek)

Currently translated at 82.4% (447 of 542 strings)

Translated using Weblate (Bengali)

Currently translated at 19.9% (108 of 542 strings)

Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Thanasis Trispiotis <sdi2000194@di.uoa.gr>
Co-authored-by: Translator-3000 <weblate.m1d0h@8shield.net>
Co-authored-by: duckling <salmanfc.bd@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/el/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/
Translation: Cloudstream/App
2022-12-18 16:50:18 +01:00
Cloudburst
1e636c8b08
add bengali 2022-12-18 14:31:44 +01:00
Hosted Weblate
492c950b7a Translated using Weblate (Norwegian Bokmål)
Currently translated at 68.4% (371 of 542 strings)

Translated using Weblate (English)

Currently translated at 100.0% (542 of 542 strings)

Added translation using Weblate (Bengali)

Translated using Weblate (Chinese (Traditional))

Currently translated at 97.9% (531 of 542 strings)

Translated using Weblate (Vietnamese)

Currently translated at 92.4% (501 of 542 strings)

Translated using Weblate (Polish)

Currently translated at 95.9% (520 of 542 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 54.6% (296 of 542 strings)

Translated using Weblate (Dutch)

Currently translated at 84.5% (458 of 542 strings)

Translated using Weblate (Indonesian)

Currently translated at 76.1% (413 of 542 strings)

Translated using Weblate (French)

Currently translated at 70.4% (382 of 542 strings)

Translated using Weblate (Greek)

Currently translated at 81.5% (442 of 542 strings)

Translated using Weblate (Czech)

Currently translated at 78.4% (425 of 542 strings)

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

Currently translated at 89.2% (484 of 542 strings)

Translated using Weblate (Bulgarian)

Currently translated at 99.8% (541 of 542 strings)

Translated using Weblate (English)

Currently translated at 100.0% (542 of 542 strings)

Co-authored-by: Allan Nordhøy <epost@anotheragency.no>
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bg/
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/el/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/
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/pl/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/
Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hant/
Translation: Cloudstream/App
2022-12-18 14:30:38 +01:00
Weblate (bot)
4d13494a93
Translations update from Hosted Weblate (#254)
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: sfaresee <salmanfc.bd@gmail.com>
Co-authored-by: Radoslav Vasilev Vasilev <fifata@gmail.com>
2022-12-18 13:26:42 +01:00
Cloudburst
956c693d1b
[skip ci] Update README.md 2022-12-18 12:59:04 +01:00
contusionglory
6246d984a1
Fix uprot.net (#251)
Fix regex for upront.net it was giving the wrong url.
2022-12-16 23:45:23 +00:00
Blatzar
495d02d583 Merge remote-tracking branch 'origin/master' 2022-12-15 21:00:17 +01:00
Blatzar
304b103e32 Removed theme from some dialogs as the button text became invisible 2022-12-15 21:00:09 +01:00
Sdarfeesh
5af1a0e433
Update Simplified Chinese (#243) 2022-12-14 22:10:44 +00:00
Blatzar
3fdf41869e Added hot reloading for plugins when using deployWithAdb 2022-12-13 23:28:31 +01:00
Hexated
7362ac9f64
fixed StreamSB (#244) 2022-12-13 15:20:43 +00:00
Blatzar
751175b3f9 Updated the in app updater to include notifications and updates without user action on Android 12+ 2022-12-11 18:14:09 +01:00
reduplicated
c11f0c101b small trailer fix 2022-12-09 20:10:10 +01:00
Sdarfeesh
7c4f177e47
Update Simplified Chinese (Correction) (#240) 2022-12-09 17:35:16 +00:00
Blatzar
0d7c20e3bd Merge remote-tracking branch 'origin/master' 2022-12-09 18:20:19 +01:00
Blatzar
95f4a15864 + Added Confirm exit on Android TV
+ Added support for Play Next on Android TV
2022-12-09 18:20:10 +01:00
reduplicated
20ac21c25f small UI change 2022-12-09 18:12:51 +01:00
Blatzar
4f54bf3ae4 Added outlines to more things to increase accessibility on Android TV 2022-12-09 00:37:59 +01:00
Blatzar
f7b623ffc7 Merge remote-tracking branch 'origin/master' 2022-12-08 16:18:14 +01:00
Blatzar
4b0b6f6f20 Reworked the internal subtitle API to fix edge-cases when importing subtitles 2022-12-08 16:18:03 +01:00
Deepak Patil
0b17862049
feat(subtitle): add addic7ed.com (#221) 2022-12-07 19:10:19 +00:00
Jace
56c79e3b6a
[Bugfix] Re arrange Regex for Duration, fixing issue if inaccurate results. (#239) 2022-12-07 18:39:59 +00:00
Blatzar
6d13cf0b01 Fixed Android 13 notifications (bruh) 2022-12-06 19:41:09 +01:00
Blatzar
70dcc96026 Fixed AniList api 2022-12-06 11:52:58 +01:00
Blatzar
3fa82cdba7 Probably fixed a very weird exception 2022-12-05 16:09:51 +01:00
Blatzar
e7d7639776 Merge remote-tracking branch 'origin/master' 2022-12-05 12:39:44 +01:00
Blatzar
514e250d68 Update dependencies, lets hope it does not fuck up anything 2022-12-05 12:39:34 +01:00
nvticzek
5c3652d1e9
Polish translation update (#235) 2022-12-04 16:39:04 +01:00
reduplicated
2222a1b07b fixed rolling cache + update UI text color 2022-12-03 22:55:53 +01:00
Jace
42d1dd9f7d
[Feature] Use subtitle filename, instead of movie title for Opensubtitles search. (#223)
* [Feature] Use subtitle filename, instead of movie title for Opensubtitles search.

* Optimize iteration for first not null value
2022-12-03 14:48:56 +00:00
SANCTI-afk
eb90b79bf9
Arabic language update for latest keys (#231)
* arabicLanguage100%

* Update strings.xml

* Arabic Full

* translated(preffVplayerBtn)

* renamed homeBtn for arabic layout

* Arabic language update 

No more typical ×
Common language instead ✓

* Arabic translation minor update

Last commit ready to be merged

* arabic language update for latest keys

* ready to merge

* last minor edit and ready to merge
2022-12-03 13:17:33 +00:00
Blatzar
b79e2d768f Fixed subtitle elevation again 2022-12-03 02:42:16 +01:00
Sdarfeesh
e215747749
Update Simplified Chinese Translation (#229) 2022-12-01 21:07:47 +00:00
Blatzar
3f658a375e Fixed MPV return intent 2022-11-30 21:23:19 +01:00
Jace
723c554bc8
[Feature] Automatically download plugin, based on language setting (#172) 2022-11-29 19:46:31 +00:00
jhih_yu
58593ac8da
Add zh_TW (#202)
* Add zh_TW
2022-11-29 19:45:00 +00:00
Cloudburst
c513708d74
smol tweaks to the previous commit 2022-11-27 10:37:36 +01:00
Jace
9be50eb28b [Feature] Filter extension list automatically by preferred media language. 2022-11-27 10:10:02 +01:00
Blatzar
789f3db554 Merge remote-tracking branch 'origin/master' 2022-11-23 15:58:20 +01:00
Blatzar
e21c8f8038 Fixed DdosGuardKiller, SSL on android 9 and some OpenSubtitles fixes 2022-11-23 15:57:56 +01:00
Jace
9bca7a0780
Fix duration regex, returning null on first checker (#218) 2022-11-21 07:32:32 +00:00
Jace
a8f3d18c2e
[Feature] Get duration from string in format of '00 hr 00 min 00 sec', in any combination (#215) 2022-11-19 10:53:34 +00:00
Blatzar
263f74fb9c Removed a million duplicates in values-fr to make it compile 2022-11-18 20:24:24 +01:00
Cloudburst
dbd91d788c
?? 2022-11-18 18:11:25 +01:00
MXC48
c9fe7c79dc
update the strings.xml in french (#211)
* update the strings.xml in french

* fix build error

* Update and rename app/src/main/res/values-fr/strings.xml to application/src/principal/res/valeurs-fr/strings.xml

Removal of "sort_copy" "sort_close" "sort_clear" "sort_save" in duplicate

Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
2022-11-18 14:11:47 +00:00
Blatzar
924d797e07 Fix next episode button showing up erroneously 2022-11-15 16:32:31 +01:00
Blatzar
2b29e8078f Added intent to start searching 2022-11-13 01:40:49 +01:00
Blatzar
9a93b375f3 Fixed:
Lock when switching episodes
Lock on RTL layouts
Skipping to the next season
2022-11-12 22:29:22 +01:00
Blatzar
30316107c8 Merge remote-tracking branch 'origin/master' 2022-11-12 20:07:17 +01:00
Blatzar
cf22ada266 Fix VoeExtractor 2022-11-12 20:07:08 +01:00
Kylianalex
456cd2e6e2
Updated French translation (#207)
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
2022-11-11 16:22:35 +01:00
TubaApollo
81adb10c1f
german translation update (#210) 2022-11-11 16:22:26 +01:00
SANCTI-afk
639de891c6
Arabic language minor update (#204) 2022-11-11 16:22:13 +01:00
Davide
2e7823034b
Updated Italian translation (#206) 2022-11-11 16:21:56 +01:00
Sdarfeesh
e95d117ebc
Update Simplified Chinese Translation (#199) 2022-11-07 10:49:53 +00:00
Blatzar
1226426389 More crash fixes 2022-11-06 20:16:48 +01:00
darkdemon
aef6f93efe - Open all extractor classes
- Add StreamTape clones[Streamtape.net, ShaveTape.cash]
- optimize imports for AstreamHub, GMplayer
2022-11-06 12:31:33 +01:00
reduplicated
3e2c53a5b7 Merge remote-tracking branch 'origin/master' 2022-11-05 22:15:50 +01:00
reduplicated
8fa00f4ca9 added setting for skip op and changed key for player seek amount 2022-11-05 22:15:34 +01:00
Sdarfeesh
4fb65e7242
Update Simplified Chinese Translation (#198) 2022-11-05 20:55:41 +00:00
Blatzar
60bcbf0060 A lil cache fixing & search fixes 2022-11-05 19:54:04 +01:00
reduplicated
60a2f7c1c5 clear history 2022-11-05 01:27:35 +01:00
reduplicated
9e67e856a0 small fix for multi request 2022-11-05 00:40:31 +01:00
Osten
c10ec34ab8
Aniskip (#195)
* aniskip groundwork

* working

* removed prints

* bump nicehttp

* small fix

* small fixes

* bump

Co-authored-by: reduplicated <110570621+reduplicated@users.noreply.github.com>
2022-11-04 23:36:27 +00:00
Blatzar
f84259f898 Merge remote-tracking branch 'origin/master' 2022-11-03 11:56:59 +01:00
Blatzar
8810d5abd6 Trying to the dumbest crash ever 2022-11-03 11:56:45 +01:00
reduplicated
bc03f6ebb5 version bump 2022-11-02 23:59:14 +01:00
Blatzar
344f974af2 FIX CRASHING! 2022-11-02 23:55:41 +01:00
reduplicated
c09b6881e5 bump 2022-11-02 22:56:03 +01:00
reduplicated
4a193d5d27 small fix 2022-11-02 21:41:39 +01:00
Blatzar
b57a7c3772 Merge remote-tracking branch 'origin/master' 2022-11-01 23:29:41 +01:00
Blatzar
e5be703a47 Small code fix 2022-11-01 23:29:32 +01:00
Blatzar
6308fd0fec Fixed subtitles on new exoplayer version 2022-11-01 23:27:42 +01:00
Blatzar
3d3c85a1ad Revert "Revert "Bumped the exoplayer version and fixed the audio & video track selection""
This reverts commit 6b586388b9.
2022-11-01 23:16:56 +01:00
reduplicated
28b4456dfd resultpage UI update and readded trailers 2022-11-01 18:01:29 +01:00
reduplicated
f268418190 fixed homepage 2022-11-01 00:29:10 +01:00
Blatzar
1c494f0ce2 Add back provider languages option (settings > providers)
If any of your favorite sites disappear check that setting
2022-10-31 22:09:56 +01:00
Blatzar
ddae2ddf3c Revert "Remove provider language (#141)"
This reverts commit a43e950a48.
2022-10-31 20:55:33 +01:00
Hexated
64303eab8d
[extractor] added Jeniusplay (#183) 2022-10-31 19:47:15 +00:00
Blatzar
a201f5e4f8 Add a few more animations to homepage 2022-10-31 16:46:02 +01:00
Cloudburst
0e8aacf989
[skip ci] add builds archive 2022-10-31 14:23:11 +01:00
Cloudburst
e72f3ff8b9
oops forgor \ this time 2022-10-31 12:49:00 +01:00
Cloudburst
8406f6de65 i forgor the ? 2022-10-31 12:37:06 +01:00
Cloudburst
7272dc67b7 fix chip tint and make adding repos by url easier 2022-10-31 12:33:09 +01:00
reduplicated
d349190238 scroll 2022-10-31 01:16:15 +01:00
reduplicated
47b79550f1 color fixes 2022-10-30 23:15:43 +01:00
Blatzar
65b5efb848 Make searching always respect your preferred media setting 2022-10-28 22:29:32 +02:00
Blatzar
fd7cf51f57 Fix status bar padding again 2022-10-28 21:49:14 +02:00
Blatzar
617fc4a295 Fix status bar padding on None 2022-10-28 21:43:56 +02:00
Blatzar
9ee0653ecf Fixed critical TV crash and made the bookmarks single select 2022-10-28 21:14:57 +02:00
reduplicated
c18856c8c3 minor UI and icon changes + tmp readded search 2022-10-28 18:43:14 +02:00
reduplicated
47da6efb59 Merge remote-tracking branch 'origin/master' 2022-10-28 03:51:41 +02:00
reduplicated
997420a942 phone UI changes + cache 2022-10-28 03:51:27 +02:00
Blatzar
6b586388b9 Revert "Bumped the exoplayer version and fixed the audio & video track selection"
This reverts commit 93cbd29f3d.
2022-10-27 18:31:38 +02:00
Blatzar
93cbd29f3d Bumped the exoplayer version and fixed the audio & video track selection 2022-10-27 15:38:53 +02:00
Cloudburst
7e750a40e0
fix da build (again) 2022-10-26 23:09:28 +02:00
Cloudburst
fa6a620bf9 fix builds 2022-10-26 22:39:36 +02:00
Cloudburst
c9c339795a
fix the bulgarian string once again 2022-10-26 22:12:17 +02:00
Cloudburst
49ebd27f80
fix broken strings.xml and migrate to $GITHUB_OUTPUT 2022-10-26 22:06:19 +02:00
J. Fronny
0f625142da
Switch to kotlin build scripts (#158) 2022-10-26 21:56:31 +02:00
Cloudburst
044822040f Translation in Bulgarian language.
Co-authored-by: ardoslav <fifata@gmail.com>
2022-10-26 21:55:59 +02:00
Hexated
ecd363992c
added sub to streamsb & xtreamCdn (#163) 2022-10-26 15:38:46 +00:00
Blatzar
7cbcee4d48 Merge remote-tracking branch 'origin/master' 2022-10-26 14:59:40 +02:00
Blatzar
7f71eef755 Allow playback from buffer with no internet 2022-10-26 14:59:29 +02:00
Cloudburst
4c309bbb2a
[skip ci] increase issue analysis threshold 2022-10-25 10:41:12 +02:00
Blatzar
544f277d0c Fixed player caching being invalidated by OOM 2022-10-24 15:31:35 +02:00
Blatzar
034bad289f Crashfix + version bump 2022-10-23 18:59:01 +02:00
Blatzar
570fdb5af4 Merge remote-tracking branch 'origin/master' 2022-10-19 00:14:26 +02:00
Blatzar
6a5286e363 Several crashfixes 2022-10-19 00:14:15 +02:00
Cloudburst
4c0f6df1a2
update newpipe (#160) 2022-10-18 12:33:25 +02:00
Sdarfeesh
4848e43c97
Simplified Chinese Correction and Update (#150) 2022-10-18 12:33:17 +02:00
Cloudburst
a58ca547d7
Apply fixes from CodeFactor (#157)
Co-authored-by: codefactor-io <support@codefactor.io>
2022-10-16 19:51:00 +02:00
Blatzar
f49d9de09b Added CineGrabber Extractor 2022-10-16 19:42:32 +02:00
Cloudburst
cf08c958eb
[skip ci] remove link to site 2022-10-15 22:01:16 +02:00
Blatzar
e67d248f7f Fix searching in repos & search provider selection bottom sheet 2022-10-14 23:56:21 +02:00
Blatzar
63c713fc68 Changed home selection sheet to fully open and allow upwards scrolling :) 2022-10-14 20:18:32 +02:00
Blatzar
1228701f0e Fix focusing add repo on firestick 2022-10-13 22:58:18 +02:00
reduplicated
661f8c3c4e mini api fix 2022-10-11 15:24:16 +02:00
Blatzar
af4d57e842 Merge remote-tracking branch 'origin/master' 2022-10-10 23:57:59 +02:00
Blatzar
a565319ecb Remove links to website in app & fix plugin deletion bug 2022-10-10 23:57:50 +02:00
SANCTI-afk
fc7e39e3cc
arabicLanguage (#118)
* arabicLanguage100%

* Update strings.xml

* Arabic Full

* translated(preffVplayerBtn)
2022-10-10 19:58:28 +00:00
LagradOst
a43e950a48
Remove provider language (#141) 2022-10-10 19:51:03 +00:00
Hexated
98ef6a3f16
added Vidmoly & Voe (extractor) (#147) 2022-10-10 19:43:56 +00:00
Thanasis Trispiotis
b3ff3ec086
update strings.xml Greek (#145)
* update strings.xml

Add new translations to greek language (el) and fix typos.

* remove duplicate
2022-10-10 19:42:59 +00:00
Samet Mert Karataş
e2118c3271
Update Turkish translation (#143)
* Update strings.xml and array.xml

* Fix escape characters

* Little changes
2022-10-09 00:35:19 +00:00
reduplicated
ddcdb04d78 Merge remote-tracking branch 'origin/master' 2022-10-09 01:36:22 +02:00
reduplicated
61fb302a37 delay mainpage 2022-10-09 01:36:06 +02:00
Jace
b6d0141cd9
Fix Random button on Main page. (#136)
* Fix Random button on Main page.

* Removed unnecessary line
2022-10-08 20:34:41 +00:00
reduplicated
020b3b7472 Merge remote-tracking branch 'origin/master' 2022-10-08 22:34:03 +02:00
reduplicated
c2d245e8b4 mpv 2022-10-08 22:29:17 +02:00
Cloudburst
4538909d9a
[skip_ci] update issue config 2022-10-08 19:26:41 +02:00
reduplicated
071004f6c2 added webvideocaster 2022-10-08 17:48:46 +02:00
reduplicated
88a7248e47 💀 i forgor 2022-10-08 16:58:14 +02:00
reduplicated
0b95d6ad33 vlc stuff 2022-10-08 16:56:05 +02:00
Blatzar
1e83d21db4 Fix selecting online and local subtitles on Android TV 2022-10-07 18:17:31 +02:00
Blatzar
9e66245066 Actually delete the internal files when you delete a repo 2022-10-07 14:53:51 +02:00
Blatzar
4c4b6b1787 Fix selecting random api 2022-10-07 00:06:14 +02:00
Blatzar
601f4c5a77 Added .otf files to the manual subtitle whitelist 2022-10-06 20:04:43 +02:00
Blatzar
fc3abe415b Merge remote-tracking branch 'origin/master' 2022-10-06 18:31:10 +02:00
Blatzar
fb248b6192 Fixed internal plugin manager to fix name conflicts 2022-10-06 18:30:58 +02:00
Cloudburst
9ab43d06fc
Update issue_action.yml 2022-10-06 12:09:28 +02:00
Blatzar
fbbcdb4889 Add option for default media player (Internal, VLC and browser) & fixed VLC detection 2022-10-06 00:14:42 +02:00
Jace
03eb17149f
[Extractor] AStreamHub (#129) 2022-10-01 13:06:39 +00:00
Hexated
b83843d3ae
small fix IndexSubtitle (#126) 2022-10-01 11:20:41 +00:00
Hexated
365d470f82
changed IndexSubtitle domain (#125) 2022-09-30 10:58:49 +00:00
Blatzar
fd0fb969b2 Real fix for Fire TV home screen categories 2022-09-25 00:50:14 +02:00
Blatzar
3bea7f01ef Probable fix for Fire TV home provider categories selection 2022-09-25 00:21:58 +02:00
Blatzar
2a6d20cab4 Merge remote-tracking branch 'origin/master' 2022-09-23 20:20:53 +02:00
Blatzar
e89ee02dd4 The extractor part of the fix for VidSrc 2022-09-23 20:20:47 +02:00
Cloudburst
91258095a9 [hotfix] fix material you theme crashing on a11 2022-09-23 19:09:16 +02:00
Cloudburst
af1f8c52ac make the material you "less gray"
and add secondary color option
2022-09-23 17:27:08 +02:00
Cloudburst
c714b77687 add "material you" theme 2022-09-23 16:32:38 +02:00
Blatzar
c4295f55ae Fix internal plugin updater 2022-09-23 14:20:53 +02:00
Blatzar
7a640b58cb Fix app not responding when clicking extensions in settings 2022-09-23 12:24:41 +02:00
Blatzar
7e28517e5b Fix bookmark button for emulator layout 2022-09-22 21:04:55 +02:00
Blatzar
3e09ea9704 Probably fixed the extremely weird crashes on Android TV 2022-09-22 19:38:45 +02:00
imgbot[bot]
da09310595
[skip actions] [ImgBot] Optimize images (#116)
Co-authored-by: ImgBotApp <ImgBotHelp@gmail.com>
2022-09-22 19:24:00 +02:00
Blatzar
37e7e1ffdc Merge remote-tracking branch 'origin/master' 2022-09-22 17:20:35 +02:00
Blatzar
94fa6b963b Fix buggy OpenSubtitles caused by race condition 2022-09-22 17:20:26 +02:00
Cloudburst
b7ad79a02b Add a notification when updated extensions 2022-09-22 12:50:28 +02:00
Blatzar
880b89ea85 Fixed lock button on Arabic locale 2022-09-21 17:47:18 +02:00
Cloudburst
d057a73368
fix dokka? 2022-09-20 22:01:30 +02:00
Blatzar
29aa541dce Update Dutch translation to fix crashing caused by incorrect formatting 2022-09-20 21:53:26 +02:00
Cloudburst
9bbe3d65d2 link dokka to github and make builds concurrent 2022-09-18 14:06:48 +02:00
Blatzar
2f44c97b86 Update plugin backend 2022-09-17 17:41:44 +02:00
Blatzar
0f492c2c82 Added fallback webview to login pages and made account selectable on TV 2022-09-17 13:03:41 +02:00
Hexated
9402a28041
fixed StreamSB (#105) 2022-09-14 11:43:35 +02:00
SANCTI-afk
aab8e9ce2b
arabicSubs100% [SANCTI] (#101) 2022-09-14 11:43:30 +02:00
Blatzar
4385d4d2cc Made repository focusable on FireStick 💀 2022-09-13 19:50:25 +02:00
Blatzar
4f8f5024cb TV Navigation improvements 2022-09-13 11:06:11 +02:00
Blatzar
d38f36e617 Merge remote-tracking branch 'origin/master' 2022-09-13 10:52:56 +02:00
Blatzar
2bd4d67c5d Fixed random timeout crashes 2022-09-13 10:52:51 +02:00
Hexated
beca2b7b84
[extractor] added Gdriveplayer (#99)
* added Gdriveplayer

* added Sbflix

* added DoodWfExtractor

* fixed StreamSB
2022-09-12 19:38:33 +00:00
Blatzar
d6aa0e576c Fix create account dialog on RTL locale & remove update cache 2022-09-12 17:58:03 +02:00
Blatzar
7f475ba059 Part 3 update for named seasons 2022-09-12 17:22:48 +02:00
Blatzar
e830595c5f Part 2 update for named seasons 2022-09-12 16:00:27 +02:00
Blatzar
6f60298fc9 Fix selecting homepage 2022-09-11 13:43:05 +02:00
Blatzar
9c5f2ef2f1 Fix GMPlayer 2022-09-11 13:37:44 +02:00
Blatzar
dc6af1df43 Fix GMPlayer audio 2022-09-11 01:06:37 +02:00
Blatzar
93b60a023e Merge remote-tracking branch 'origin/master' 2022-09-10 19:59:47 +02:00
Blatzar
53965b13fb Add internal support for subtitle headers + season names 2022-09-10 19:59:37 +02:00
Cloudburst
11d3b576ba add RepoApi.installRepo for the website 2022-09-10 19:33:30 +02:00
Cloudburst
c8cd6f921d Merge branch 'master' of https://github.com/recloudstream/cloudstream 2022-09-10 19:22:27 +02:00
Cloudburst
74e3af0dbe only allow voting on installed extensions 2022-09-10 19:21:01 +02:00
Anarchydr
ce70cdd3d5
croatian language added (#95) 2022-09-10 16:47:42 +00:00
Cloudburst
9431cde2f9 add DNS.WATCH and Quad9
closes #94
2022-09-10 18:37:48 +02:00
Cloudburst
2db09e0c9a
[skip ci] improve discord invite image 2022-09-10 16:04:59 +02:00
Hexated
9383a2d176
removed "Provider" from extension name (#97)
* remove "Provider" from extension name
2022-09-09 11:13:25 +00:00
Sdarfeesh
5b67bde06a
Update Chinese translation (#96) 2022-09-09 11:12:40 +00:00
Alessio I
009d2f9961
Updated italian translation (#90)
* Updated italian translation
2022-09-08 21:07:17 +00:00
Blatzar
e40271afdc Merge remote-tracking branch 'origin/master' 2022-09-08 17:36:19 +02:00
Blatzar
e620d7b3d7 Add more keybindings for player actions 2022-09-08 17:35:56 +02:00
Sdarfeesh
6a7c4a2324
Update Chinese Translation (#91)
* Add files via upload

* Add files via upload

* Update strings.xml

* Update strings.xml

* Update strings.xml

* Add files via upload
2022-09-08 14:44:09 +02:00
Cloudburst
aa2023509a
[skip ci] create crowdin.yml 2022-09-07 15:38:21 +02:00
Cloudburst
73748ee55f
[skip ci] make prerelease a prerelease 2022-09-07 08:33:04 +02:00
reduplicated
1db1baef19 bump 2022-09-07 01:23:53 +02:00
Blatzar
d799a21277 Add voting cache to lessen spam 2022-09-07 00:40:31 +02:00
Cloudburst
39dce7935c
Merge pull request #88 from recloudstream/voting 2022-09-06 21:41:09 +02:00
Cloudburst
8c34994a7e
forgor to update tv one 2022-09-06 21:35:08 +02:00
Cloudburst
92c442c3c3 Merge branch 'voting' of https://github.com/recloudstream/cloudstream into voting 2022-09-06 21:33:01 +02:00
Cloudburst
8c6c4939d2 address requests 2022-09-06 21:31:44 +02:00
Blatzar
e1758e6346 Possible fix for audio track decoding on some devices 2022-09-06 17:50:41 +02:00
Cloudburst
d4b6a17ea9
Merge branch 'master' into voting 2022-09-05 20:52:25 +02:00
Blatzar
afb35f73b3 Add isM3u8 to raw stream button 2022-09-05 20:45:56 +02:00
Cloudburst
8172696d1a update votes to sha256 and add github link 2022-09-05 19:09:27 +02:00
Cloudburst
37c9be96bb update polish 2022-09-05 18:47:59 +02:00
Cloudburst
a3eef399a9 add voting system 2022-09-05 18:45:13 +02:00
Blatzar
6f3e9891c5 Disable exoplayer tunneling 2022-09-05 14:21:42 +02:00
Hexated
ae137f4a34
added extractors (#86) 2022-09-04 23:13:17 +00:00
Jace
abc9421fb1
[Feature] Add new extractor - PlayLtXyz (#71)
* [Feature] Add new extractor - PlayLtXyz

* various cleanups

* cleanups
2022-09-04 18:54:54 +00:00
Gustavo
93daf48361 Update strings.xml 2022-09-03 15:39:49 +02:00
Gustavo
fa8917a976 Update strings-pt.xml 2022-09-03 15:39:49 +02:00
tuan041
9df4f8be4a
Update vietnamese translation (#81)
* Update vietnamese translation

Update vietnamese translation

* Update strings.xml
2022-09-03 09:33:15 +02:00
Blatzar
a946127eb1 Dismiss dialog when clicking public repos 2022-09-02 23:33:58 +02:00
Hexated
ec9a39814b
[extractor] simple added Vidgomunime (#79) 2022-09-02 16:19:20 +00:00
Cloudburst
cdf0047bfd update pl and de translations 2022-09-01 14:26:37 +02:00
Cloudburst
93b176e3cd add safe mode that disabled extensions on crash 2022-09-01 14:13:54 +02:00
LagradOst
461f3d75d8
Add audio and video track support (#69)
* Add audio and video track support
2022-08-31 19:58:26 +00:00
reduplicated
7a68d6304f fixed subs bug + fixed generator bug + bump 3.1.4 + updated drive link 2022-08-31 20:08:45 +02:00
Hexated
cbff00090d
[extractor] simple added Rasacintaku (#74) 2022-08-31 16:54:39 +02:00
KillerDogeEmpire
e503534ab1
Made Language into provider (#68)
* made the language category to Provider category

* Remove the translations for the language category since it's now changed
2022-08-29 21:20:03 +00:00
TubaApollo
9d1bc4800c
German translation updated (#72) 2022-08-29 18:40:51 +02:00
reduplicated
cd119822f2 tv UI fixes 2022-08-29 01:52:15 +02:00
Jace
01c81fc421
[Feature] Copy settings values from Preferences to providers APIs (#48)
* [Feature] Copy settings values from Preferences to providers APIs

* Use companion object instead for singular object access across all providers

* Fix build

* Re-order code hierarchy. Prioritize loading settings from pref
2022-08-28 18:32:17 +00:00
Dilute Oxygen
2893cd8da3
Updated HI Translation (#66) 2022-08-28 18:31:30 +00:00
Hexated
66842daa93
Added Zorofile (extractor) (#58)
* Added Zorofile (extractor)

* fixes
2022-08-26 12:07:53 +00:00
Gustavo
5636bf2518
Update PT and PT BR translations (#60) 2022-08-25 20:59:44 +02:00
Blatzar
67addd79a0 update loadAllOnlinePlugins 2022-08-25 13:49:24 +02:00
Blatzar
241cc92e84 update loadAllOnlinePlugins 2022-08-25 13:48:55 +02:00
reduplicated
7a6d16d2c1 trailers fix + crash fix 2022-08-25 03:59:20 +02:00
Blatzar
abeda3e758 Fix randomized homepage 2022-08-24 04:21:46 +02:00
Blatzar
1e11a78698 Merge remote-tracking branch 'origin/master' 2022-08-24 03:10:33 +02:00
Blatzar
bbdb1d7ad3 Add toggle to disable autoplay
Fixes #57
2022-08-24 03:10:06 +02:00
antonydp
82d416ad7f
Cloudflare getCookieHeaders (#56) 2022-08-23 22:14:07 +00:00
Blatzar
92dbbf86b2 Switched prefer_media_type_key to fix crashes (you arent supposed to use this yet anyways 💀) 2022-08-24 00:12:56 +02:00
Blatzar
0f8a2df9e5 Optimized plugin loading 2022-08-23 21:28:42 +02:00
Cloudburst
8ff2540f4b fix #43 (again) 2022-08-23 17:13:35 +02:00
Blatzar
032ac325e5 Merge remote-tracking branch 'origin/master' 2022-08-22 23:31:04 +02:00
Blatzar
3eb5df5bc1 Reload result fragment on plugins fetched 2022-08-22 23:30:53 +02:00
Visiraptor
a3f4c0b953 Update strings-de.xml 2022-08-22 19:53:40 +02:00
Blatzar
e4f3d6f79a Merge remote-tracking branch 'origin/master' 2022-08-22 19:48:15 +02:00
Blatzar
942c9541a2 Fix CloudflareKiller (remove cookies on init) 2022-08-22 19:47:58 +02:00
Gustavo
f0668da0a8
Updated PT and PT-BR Translations (#51)
* Update strings-pt.xml

* Update strings.xml

* Update strings-pt.xml

* Fixed missing >
2022-08-22 19:06:12 +02:00
Cloudburst
8862b103a7 dont show nsfw in recently watched 2022-08-22 14:57:47 +02:00
Blatzar
7907e761ab Added OpenSubtitles and nginx account login to the backup blacklist 2022-08-21 23:36:12 +02:00
Blatzar
cc38cf4264 Fix TV layout bugs 2022-08-21 22:13:53 +02:00
Blatzar
a273be6d89 Fix oversight when cookies are already stored in cloudflare 2022-08-21 20:51:01 +02:00
835 changed files with 71705 additions and 22633 deletions

View file

@ -80,13 +80,13 @@ body:
label: Acknowledgements
description: Your issue will be closed if you haven't done these steps.
options:
- label: I am sure my issue is related to the app and **NOT some extension**.
required: true
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue.
required: true
- label: I have written a short but informative title.
required: true
- label: I have updated the app to pre-release version **[Latest](https://github.com/recloudstream/cloudstream/releases)**.
required: true
- label: If related to a provider, I have checked the site and it works, but not the app.
required: true
- label: I will fill out all of the requested information in this form.
required: true

View file

@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Report provider bug
- name: Request a new provider or report bug with an existing provider
url: https://github.com/recloudstream
about: Please do not report any provider bugs here. This repository does not contain any providers. Please find the appropriate repository and report your issue there or join the discord.
about: EXTREMELY IMPORTANT - Please do not report any provider bugs here or request new providers. This repository does not contain any providers. Please find the appropriate repository and report your issue there or join the discord.
- name: Discord
url: https://discord.gg/5Hus6fM
about: Join our discord for faster support on smaller issues.

View file

@ -27,9 +27,7 @@ body:
label: Acknowledgements
description: Your issue will be closed if you haven't done these steps.
options:
- label: My suggestion is **NOT** about adding a new provider
required: true
- label: I have searched the existing issues and this is a new ticket, **NOT** a duplicate or related to another open issue.
required: true
- label: I have written a short but informative title.
required: true
- label: I will fill out all of the requested information in this form.
required: true
required: true

BIN
.github/downloads.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

BIN
.github/home.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

69
.github/locales.py vendored Normal file
View file

@ -0,0 +1,69 @@
import re
import glob
import requests
import os
import lxml.etree as ET # builtin library doesn't preserve comments
SETTINGS_PATH = "app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt"
START_MARKER = "/* begin language list */"
END_MARKER = "/* end language list */"
XML_NAME = "app/src/main/res/values-"
ISO_MAP_URL = "https://raw.githubusercontent.com/haliaeetus/iso-639/master/data/iso_639-1.min.json"
INDENT = " "*4
iso_map = requests.get(ISO_MAP_URL, timeout=300).json()
# Load settings file
src = open(SETTINGS_PATH, "r", encoding='utf-8').read()
before_src, rest = src.split(START_MARKER)
rest, after_src = rest.split(END_MARKER)
# Load already added langs
languages = {}
for lang in re.finditer(r'Triple\("(.*)", "(.*)", "(.*)"\)', rest):
flag, name, iso = lang.groups()
languages[iso] = (flag, name)
# Add not yet added langs
for folder in glob.glob(f"{XML_NAME}*"):
iso = folder[len(XML_NAME):]
if iso not in languages.keys():
entry = iso_map.get(iso.lower(),{'nativeName':iso})
languages[iso] = ("", entry['nativeName'].split(',')[0])
# Create triples
triples = []
for iso in sorted(languages.keys()):
flag, name = languages[iso]
triples.append(f'{INDENT}Triple("{flag}", "{name}", "{iso}"),')
# Update settings file
open(SETTINGS_PATH, "w+",encoding='utf-8').write(
before_src +
START_MARKER +
"\n" +
"\n".join(triples) +
"\n" +
END_MARKER +
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 not child.text:
continue
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)
# Remove trailing new line to be consistent with weblate
fp.seek(-1, os.SEEK_END)
fp.truncate()
except ET.ParseError as ex:
print(f"[{file}] {ex}")

BIN
.github/player.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

BIN
.github/results.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

BIN
.github/search.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

78
.github/workflows/build_to_archive.yml vendored Normal file
View file

@ -0,0 +1,78 @@
name: Archive build
on:
push:
branches: [ master ]
paths-ignore:
- '*.md'
- '*.json'
- '**/wcokey.txt'
workflow_dispatch:
concurrency:
group: "Archive-build"
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Generate access token
id: generate_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_KEY }}
repository: "recloudstream/secrets"
- name: Generate access token (archive)
id: generate_archive_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_KEY }}
repository: "recloudstream/cloudstream-archive"
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'adopt'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Fetch keystore
id: fetch_keystore
run: |
TMP_KEYSTORE_FILE_PATH="${RUNNER_TEMP}"/keystore
mkdir -p "${TMP_KEYSTORE_FILE_PATH}"
curl -H "Authorization: token ${{ steps.generate_token.outputs.token }}" -o "${TMP_KEYSTORE_FILE_PATH}/prerelease_keystore.keystore" "https://raw.githubusercontent.com/recloudstream/secrets/master/keystore.jks"
curl -H "Authorization: token ${{ steps.generate_token.outputs.token }}" -o "keystore_password.txt" "https://raw.githubusercontent.com/recloudstream/secrets/master/keystore_password.txt"
KEY_PWD="$(cat keystore_password.txt)"
echo "::add-mask::${KEY_PWD}"
echo "key_pwd=$KEY_PWD" >> $GITHUB_OUTPUT
- name: Run Gradle
run: |
./gradlew assemblePrerelease
env:
SIGNING_KEY_ALIAS: "key0"
SIGNING_KEY_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }}
SIGNING_STORE_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }}
SIMKL_CLIENT_ID: ${{ secrets.SIMKL_CLIENT_ID }}
SIMKL_CLIENT_SECRET: ${{ secrets.SIMKL_CLIENT_SECRET }}
- uses: actions/checkout@v4
with:
repository: "recloudstream/cloudstream-archive"
token: ${{ steps.generate_archive_token.outputs.token }}
path: "archive"
- name: Move build
run: |
cp app/build/outputs/apk/prerelease/release/*.apk "archive/$(git rev-parse --short HEAD).apk"
- name: Push archive
run: |
cd $GITHUB_WORKSPACE/archive
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
git add .
git commit --amend -m "Build $GITHUB_SHA" || exit 0 # do not error if nothing to commit
git push --force

View file

@ -20,7 +20,7 @@ jobs:
steps:
- name: Generate access token
id: generate_token
uses: tibdex/github-app-token@v1
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_KEY }}
@ -39,17 +39,17 @@ jobs:
- name: Clean old builds
run: |
shopt -s extglob
cd $GITHUB_WORKSPACE/dokka/
rm -rf !(.git)
rm -rf "./-cloudstream"
- name: Setup JDK 11
uses: actions/setup-java@v1
- name: Setup JDK 17
uses: actions/setup-java@v4
with:
java-version: 11
java-version: 17
distribution: 'adopt'
- name: Setup Android SDK
uses: android-actions/setup-android@v2
uses: android-actions/setup-android@v3
- name: Generate Dokka
run: |

View file

@ -1,63 +1,88 @@
name: Issue automatic actions
on:
issues:
types: [opened, edited]
jobs:
issue-moderator:
runs-on: ubuntu-latest
steps:
- name: Generate access token
id: generate_token
uses: tibdex/github-app-token@v1
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_KEY }}
- name: Similarity analysis
uses: actions-cool/issues-similarity-analysis@v1
with:
token: ${{ steps.generate_token.outputs.token }}
filter-threshold: 0.5
title-excludes: ''
comment-title: |
### Your issue looks similar to these issues:
Please close if duplicate.
comment-body: '${index}. ${similarity} #${number}'
- uses: actions/checkout@v2
- name: Automatically close issues that dont follow the issue template
uses: lucasbento/auto-close-issues@v1.0.2
with:
github-token: ${{ steps.generate_token.outputs.token }}
issue-close-message: |
@${issue.user.login}: hello! :wave:
This issue is being automatically closed because it does not follow the issue template."
closed-issues-label: "invalid"
- name: Check if issue mentions a provider
id: provider_check
env:
GH_TEXT: "${{ github.event.issue.title }} ${{ github.event.issue.body }}"
run: |
wget --output-document check_issue.py "https://raw.githubusercontent.com/recloudstream/.github/master/.github/check_issue.py"
pip3 install httpx
RES="$(python3 ./check_issue.py)"
echo "::set-output name=name::${RES}"
- name: Comment if issue mentions a provider
if: steps.provider_check.outputs.name != 'none'
uses: actions-cool/issues-helper@v3
with:
actions: 'create-comment'
token: ${{ steps.generate_token.outputs.token }}
body: |
Hello ${{ github.event.issue.user.login }}.
Please do not report any provider bugs here. This repository does not contain any providers. Please find the appropriate repository and report your issue there or join the [discord](https://discord.gg/5Hus6fM).
Found provider name: `${{ steps.provider_check.outputs.name }}`
- name: Add eyes reaction to all issues
uses: actions-cool/emoji-helper@v1.0.0
with:
type: 'issue'
token: ${{ steps.generate_token.outputs.token }}
emoji: 'eyes'
name: Issue automatic actions
on:
issues:
types: [opened]
jobs:
issue-moderator:
runs-on: ubuntu-latest
steps:
- name: Generate access token
id: generate_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_KEY }}
- name: Similarity analysis
id: similarity
uses: actions-cool/issues-similarity-analysis@v1
with:
token: ${{ steps.generate_token.outputs.token }}
filter-threshold: 0.60
title-excludes: ''
comment-title: |
### Your issue looks similar to these issues:
Please close if duplicate.
comment-body: '${index}. ${similarity} #${number}'
- name: Label if possible duplicate
if: steps.similarity.outputs.similar-issues-found =='true'
uses: actions/github-script@v7
with:
github-token: ${{ steps.generate_token.outputs.token }}
script: |
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ["possible duplicate"]
})
- uses: actions/checkout@v4
- name: Automatically close issues that dont follow the issue template
uses: lucasbento/auto-close-issues@v1.0.2
with:
github-token: ${{ steps.generate_token.outputs.token }}
issue-close-message: |
@${issue.user.login}: hello! :wave:
This issue is being automatically closed because it does not follow the issue template."
closed-issues-label: "invalid"
- name: Check if issue mentions a provider
id: provider_check
env:
GH_TEXT: "${{ github.event.issue.title }} ${{ github.event.issue.body }}"
run: |
wget --output-document check_issue.py "https://raw.githubusercontent.com/recloudstream/.github/master/.github/check_issue.py"
pip3 install httpx
RES="$(python3 ./check_issue.py)"
echo "name=${RES}" >> $GITHUB_OUTPUT
- name: Comment if issue mentions a provider
if: steps.provider_check.outputs.name != 'none'
uses: actions-cool/issues-helper@v3
with:
actions: 'create-comment'
token: ${{ steps.generate_token.outputs.token }}
body: |
Hello ${{ github.event.issue.user.login }}.
Please do not report any provider bugs here. This repository does not contain any providers. Please find the appropriate repository and report your issue there or join the [discord](https://discord.gg/5Hus6fM).
Found provider name: `${{ steps.provider_check.outputs.name }}`
- name: Label if mentions provider
if: steps.provider_check.outputs.name != 'none'
uses: actions/github-script@v7
with:
github-token: ${{ steps.generate_token.outputs.token }}
script: |
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ["possible provider issue"]
})
- name: Add eyes reaction to all issues
uses: actions-cool/emoji-helper@v1.0.0
with:
type: 'issue'
token: ${{ steps.generate_token.outputs.token }}
emoji: 'eyes'

View file

@ -18,16 +18,16 @@ jobs:
steps:
- name: Generate access token
id: generate_token
uses: tibdex/github-app-token@v1
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_KEY }}
repository: "recloudstream/secrets"
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '11'
java-version: '17'
distribution: 'adopt'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
@ -40,24 +40,25 @@ jobs:
curl -H "Authorization: token ${{ steps.generate_token.outputs.token }}" -o "keystore_password.txt" "https://raw.githubusercontent.com/recloudstream/secrets/master/keystore_password.txt"
KEY_PWD="$(cat keystore_password.txt)"
echo "::add-mask::${KEY_PWD}"
echo "::set-output name=key_pwd::$KEY_PWD"
echo "key_pwd=$KEY_PWD" >> $GITHUB_OUTPUT
- name: Run Gradle
run: |
./gradlew assemblePrerelease
./gradlew androidSourcesJar
./gradlew makeJar
./gradlew assemblePrerelease build androidSourcesJar
./gradlew makeJar # for classes.jar, has to be done after assemblePrerelease
env:
SIGNING_KEY_ALIAS: "key0"
SIGNING_KEY_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }}
SIGNING_STORE_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }}
SIMKL_CLIENT_ID: ${{ secrets.SIMKL_CLIENT_ID }}
SIMKL_CLIENT_SECRET: ${{ secrets.SIMKL_CLIENT_SECRET }}
- name: Create pre-release
uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "pre-release"
prerelease: false
prerelease: true
title: "Pre-release Build"
files: |
app/build/outputs/apk/prerelease/*.apk
app/build/outputs/apk/prerelease/release/*.apk
app/build/libs/app-sources.jar
app/build/classes.jar

View file

@ -6,18 +6,18 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '11'
java-version: '17'
distribution: 'adopt'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Run Gradle
run: ./gradlew assembleDebug
run: ./gradlew assemblePrereleaseDebug
- name: Upload Artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: pull-request-build
path: "app/build/outputs/apk/debug/*.apk"
path: "app/build/outputs/apk/prerelease/debug/*.apk"

42
.github/workflows/update_locales.yml vendored Normal file
View file

@ -0,0 +1,42 @@
name: Fix locale issues
on:
workflow_dispatch:
push:
paths:
- '**.xml'
branches:
- master
concurrency:
group: "locale"
cancel-in-progress: true
jobs:
create:
runs-on: ubuntu-latest
steps:
- name: Generate access token
id: generate_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ secrets.GH_APP_ID }}
private_key: ${{ secrets.GH_APP_KEY }}
repository: "recloudstream/cloudstream"
- uses: actions/checkout@v4
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Install dependencies
run: |
pip3 install lxml
- name: Edit files
run: |
python3 .github/locales.py
- name: Commit to the repo
run: |
git config --local user.email "111277985+recloudstream[bot]@users.noreply.github.com"
git config --local user.name "recloudstream[bot]"
git add .
# "echo" returns true so the build succeeds, even if no changed files
git commit -m 'chore(locales): fix locale issues' || echo
git push

4
.idea/compiler.xml generated
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
<bytecodeTargetLevel target="17" />
</component>
</project>
</project>

7
.idea/gradle.xml generated
View file

@ -4,17 +4,16 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="delegatedBuild" value="true" />
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="11" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/library" />
</set>
</option>
<option name="resolveExternalAnnotations" value="false" />
</GradleProjectSettings>
</option>
</component>

View file

@ -31,5 +31,10 @@
<option name="name" value="maven2" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
</component>
</project>

View file

@ -1,44 +1,19 @@
# CloudStream
**⚠️ Warning: By default this app doesn't provide any video sources, you have to install extensions in order to add functionality to the app.**
You can find the list of community-maintained extension repositories [here
](https://recloudstream.github.io/repos/)
[![Discord](https://img.shields.io/discord/737724143126052974?style=for-the-badge)](https://discord.gg/5Hus6fM)
[![Discord](https://invidget.switchblade.xyz/5Hus6fM)](https://discord.gg/5Hus6fM)
***Features:***
### Features:
+ **AdFree**, No ads whatsoever
+ No tracking/analytics
+ Bookmarks
+ Download and stream movies, tv-shows and anime
+ Phone and TV support
+ Chromecast
+ Extension system for personal customization
***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"/>
***The list of supported languages:***
* 🇱🇧 Arabic
* 🇨🇿 Czech
* 🇳🇱 Dutch
* 🇬🇧 English
* 🇫🇷 French
* 🇩🇪 German
* 🇬🇷 Greek
* 🇮🇳 Hindi
* 🇮🇩 Indonesian
* 🇮🇹 Italian
* 🇲🇰 Macedonian
* 🇮🇳 Malayalam
* 🇳🇴 Norsk
* 🇵🇱 Polish
* 🇧🇷 Portuguese (Brazil)
* 🇷🇴 Romanian
* 🇪🇸 Spanish
* 🇸🇪 Swedish
* 🇵🇭 Tagalog
* 🇹🇷 Turkish
* 🇻🇳 Vietnamese
### Supported languages:
<a href="https://hosted.weblate.org/engage/cloudstream/">
<img src="https://hosted.weblate.org/widgets/cloudstream/-/app/multi-auto.svg" alt="Translation status" />
</a>

6
app/CMakeLists.txt Normal file
View file

@ -0,0 +1,6 @@
# Set this to the minimum version your project supports.
cmake_minimum_required(VERSION 3.18)
project(CrashHandler)
find_library(log-lib log)
add_library(native-lib SHARED src/main/cpp/native-lib.cpp)
target_link_libraries(native-lib ${log-lib})

View file

@ -1,215 +0,0 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'kotlin-android-extensions'
id 'org.jetbrains.dokka'
}
def tmpFilePath = System.getProperty("user.home") + "/work/_temp/keystore/"
def allFilesFromDir = new File(tmpFilePath).listFiles()
def prereleaseStoreFile = null
if (allFilesFromDir != null) {
prereleaseStoreFile = allFilesFromDir.first()
}
android {
testOptions {
unitTests.returnDefaultValues = true
}
signingConfigs {
prerelease {
if (prereleaseStoreFile != null) {
storeFile = file(prereleaseStoreFile)
storePassword System.getenv("SIGNING_STORE_PASSWORD")
keyAlias System.getenv("SIGNING_KEY_ALIAS")
keyPassword System.getenv("SIGNING_KEY_PASSWORD")
}
}
}
compileSdkVersion 31
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.lagradost.cloudstream3"
minSdkVersion 21
targetSdkVersion 30
versionCode 50
versionName "3.1.3"
resValue "string", "app_version",
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
resValue "string", "commit_hash",
("git rev-parse --short HEAD".execute().text.trim() ?: "")
resValue "bool", "is_prerelease", "false"
buildConfigField("String", "BUILDDATE", "new java.text.SimpleDateFormat(\"yyyy-MM-dd HH:mm\").format(new java.util.Date(" + System.currentTimeMillis() + "L));")
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
kapt {
includeCompileClasspath = true
}
}
buildTypes {
// release {
// debuggable false
// minifyEnabled false
// shrinkResources false
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
// resValue "bool", "is_prerelease", "false"
// }
prerelease {
applicationIdSuffix ".prerelease"
buildConfigField("boolean", "BETA", "true")
signingConfig signingConfigs.prerelease
versionNameSuffix '-PRE'
debuggable false
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
resValue "bool", "is_prerelease", "true"
}
debug {
debuggable true
applicationIdSuffix ".debug"
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
resValue "bool", "is_prerelease", "true"
}
}
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
freeCompilerArgs = ['-Xjvm-default=compatibility']
}
lintOptions {
checkReleaseBuilds false
abortOnError false
}
}
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.google.android.mediahome:video:1.0.0'
implementation 'androidx.test.ext:junit-ktx:1.1.3'
testImplementation 'org.json:json:20180813'
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
// dont change this to 1.6.0 it looks ugly af
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//implementation "io.karn:khttp-android:0.1.2" //okhttp instead
// implementation 'org.jsoup:jsoup:1.13.1'
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1"
implementation "androidx.preference:preference-ktx:1.2.0"
implementation 'com.github.bumptech.glide:glide:4.13.1'
kapt 'com.github.bumptech.glide:compiler:4.13.1'
implementation 'com.github.bumptech.glide:okhttp3-integration:4.13.0'
implementation 'jp.wasabeef:glide-transformations:4.3.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
// implementation "androidx.leanback:leanback-paging:1.1.0-alpha09"
// Exoplayer
implementation 'com.google.android.exoplayer:exoplayer:2.16.1'
implementation 'com.google.android.exoplayer:extension-cast:2.16.1'
implementation "com.google.android.exoplayer:extension-mediasession:2.16.1"
implementation 'com.google.android.exoplayer:extension-okhttp:2.16.1'
//implementation "com.google.android.exoplayer:extension-leanback:2.14.0"
// Bug reports
implementation "ch.acra:acra-core:5.8.4"
implementation "ch.acra:acra-toast:5.8.4"
compileOnly "com.google.auto.service:auto-service-annotations:1.0"
//either for java sources:
annotationProcessor "com.google.auto.service:auto-service:1.0"
//or for kotlin sources (requires kapt gradle plugin):
kapt "com.google.auto.service:auto-service:1.0"
// subtitle color picker
implementation 'com.jaredrummler:colorpicker:1.1.0'
//run JS
implementation 'org.mozilla:rhino:1.7.14'
// TorrentStream
//implementation 'com.github.TorrentStream:TorrentStream-Android:2.7.0'
// Downloading
implementation "androidx.work:work-runtime:2.7.1"
implementation "androidx.work:work-runtime-ktx:2.7.1"
// Networking
// implementation "com.squareup.okhttp3:okhttp:4.9.2"
// implementation "com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1"
implementation 'com.github.Blatzar:NiceHttp:0.3.2'
// Util to skip the URI file fuckery 🙏
implementation "com.github.tachiyomiorg:unifile:17bec43"
// API because cba maintaining it myself
implementation "com.uwetrottmann.tmdb2:tmdb-java:2.6.0"
implementation 'com.github.discord:OverlappingPanels:0.1.3'
// debugImplementation because LeakCanary should only run in debug builds.
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
// for shimmer when loading
implementation 'com.facebook.shimmer:shimmer:0.5.0'
implementation "androidx.tvprovider:tvprovider:1.0.0"
// used for subtitle decoding https://github.com/albfernandez/juniversalchardet
implementation 'com.github.albfernandez:juniversalchardet:2.4.0'
// slow af yt
//implementation 'com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT'
// newpipe yt
implementation 'com.github.recloudstream:NewPipeExtractor:master-SNAPSHOT'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
// Library/extensions searching with Levenshtein distance
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
}
task androidSourcesJar(type: Jar) {
getArchiveClassifier().set('sources')
from android.sourceSets.main.java.srcDirs//full sources
}
task makeJar(type: Copy) {
// after modifying here, you can export. Jar
from('build/intermediates/compile_app_classes_jar/debug')
into('build') // output location
include('classes.jar') // the classes file of the imported rack package
dependsOn build
}

304
app/build.gradle.kts Normal file
View file

@ -0,0 +1,304 @@
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.archivesName
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.ByteArrayOutputStream
import java.net.URL
plugins {
id("com.android.application")
id("com.google.devtools.ksp")
id("kotlin-android")
id("org.jetbrains.dokka")
}
val tmpFilePath = System.getProperty("user.home") + "/work/_temp/keystore/"
val prereleaseStoreFile: File? = File(tmpFilePath).listFiles()?.first()
fun String.execute() = ByteArrayOutputStream().use { baot ->
if (project.exec {
workingDir = projectDir
commandLine = this@execute.split(Regex("\\s"))
standardOutput = baot
}.exitValue == 0)
String(baot.toByteArray()).trim()
else null
}
android {
testOptions {
unitTests.isReturnDefaultValues = true
}
viewBinding {
enable = true
}
/* disable this for now
externalNativeBuild {
cmake {
path("CMakeLists.txt")
}
}*/
signingConfigs {
if (prereleaseStoreFile != null) {
create("prerelease") {
storeFile = file(prereleaseStoreFile)
storePassword = System.getenv("SIGNING_STORE_PASSWORD")
keyAlias = System.getenv("SIGNING_KEY_ALIAS")
keyPassword = System.getenv("SIGNING_KEY_PASSWORD")
}
}
}
compileSdk = 34
buildToolsVersion = "34.0.0"
defaultConfig {
applicationId = "com.lagradost.cloudstream3"
minSdk = 21
targetSdk = 33 /* Android 14 is Fu*ked
^ https://developer.android.com/about/versions/14/behavior-changes-14#safer-dynamic-code-loading*/
versionCode = 64
versionName = "4.4.0"
resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}")
resValue("string", "commit_hash", "git rev-parse --short HEAD".execute() ?: "")
resValue("bool", "is_prerelease", "false")
// Reads local.properties
val localProperties = gradleLocalProperties(rootDir)
buildConfigField(
"long",
"BUILD_DATE",
"${System.currentTimeMillis()}"
)
buildConfigField(
"String",
"SIMKL_CLIENT_ID",
"\"" + (System.getenv("SIMKL_CLIENT_ID") ?: localProperties["simkl.id"]) + "\""
)
buildConfigField(
"String",
"SIMKL_CLIENT_SECRET",
"\"" + (System.getenv("SIMKL_CLIENT_SECRET") ?: localProperties["simkl.secret"]) + "\""
)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
arg("exportSchema", "true")
}
}
buildTypes {
release {
isDebuggable = false
isMinifyEnabled = false
isShrinkResources = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
debug {
isDebuggable = true
applicationIdSuffix = ".debug"
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
flavorDimensions.add("state")
productFlavors {
create("stable") {
dimension = "state"
resValue("bool", "is_prerelease", "false")
}
create("prerelease") {
dimension = "state"
resValue("bool", "is_prerelease", "true")
buildConfigField("boolean", "BETA", "true")
applicationIdSuffix = ".prerelease"
if (signingConfigs.names.contains("prerelease")) {
signingConfig = signingConfigs.getByName("prerelease")
} else {
logger.warn("No prerelease signing config!")
}
versionNameSuffix = "-PRE"
versionCode = (System.currentTimeMillis() / 60000).toInt()
}
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
lint {
abortOnError = false
checkReleaseBuilds = false
}
buildFeatures {
buildConfig = true
}
namespace = "com.lagradost.cloudstream3"
}
repositories {
maven("https://jitpack.io")
}
dependencies {
// Testing
testImplementation("junit:junit:4.13.2")
testImplementation("org.json:json:20240303")
androidTestImplementation("androidx.test:core")
implementation("androidx.test.ext:junit-ktx:1.2.1")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
// Android Core & Lifecycle
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.8.3")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.3")
implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
// Design & UI
implementation("jp.wasabeef:glide-transformations:4.3.0")
implementation("androidx.preference:preference-ktx:1.2.1")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
// Glide Module
ksp("com.github.bumptech.glide:ksp:4.16.0")
implementation("com.github.bumptech.glide:glide:4.16.0")
implementation("com.github.bumptech.glide:okhttp3-integration:4.16.0")
// For KSP -> Official Annotation Processors are Not Yet Supported for KSP
ksp("dev.zacsweers.autoservice:auto-service-ksp:1.2.0")
implementation("com.google.guava:guava:33.2.1-android")
implementation("dev.zacsweers.autoservice:auto-service-ksp:1.2.0")
// Media 3 (ExoPlayer)
implementation("androidx.media3:media3-ui:1.1.1")
implementation("androidx.media3:media3-cast:1.1.1")
implementation("androidx.media3:media3-common:1.1.1")
implementation("androidx.media3:media3-session:1.1.1")
implementation("androidx.media3:media3-exoplayer:1.1.1")
implementation("com.google.android.mediahome:video:1.0.0")
implementation("androidx.media3:media3-exoplayer-hls:1.1.1")
implementation("androidx.media3:media3-exoplayer-dash:1.1.1")
implementation("androidx.media3:media3-datasource-okhttp:1.1.1")
// PlayBack
implementation("com.jaredrummler:colorpicker:1.1.0") // Subtitle Color Picker
implementation("com.github.recloudstream:media-ffmpeg:1.1.0") // Custom FF-MPEG Lib for Audio Codecs
implementation("com.github.teamnewpipe:NewPipeExtractor:176da72") /* For Trailers
^ Update to Latest Commits if Trailers Misbehave, github.com/TeamNewPipe/NewPipeExtractor/commits/dev */
implementation("com.github.albfernandez:juniversalchardet:2.5.0") // Subtitle Decoding
// Crash Reports (AcraApplication.kt)
implementation("ch.acra:acra-core:5.11.3")
implementation("ch.acra:acra-toast:5.11.3")
// UI Stuff
implementation("com.facebook.shimmer:shimmer:0.5.0") // Shimmering Effect (Loading Skeleton)
implementation("androidx.palette:palette-ktx:1.0.0") // Palette For Images -> Colors
implementation("androidx.tvprovider:tvprovider:1.0.0")
implementation("com.github.discord:OverlappingPanels:0.1.5") // Gestures
implementation("androidx.biometric:biometric:1.2.0-alpha05") // Fingerprint Authentication
implementation("com.github.rubensousa:previewseekbar-media3:1.1.1.0") // SeekBar Preview
implementation("io.github.g0dkar:qrcode-kotlin:4.2.0") // QR code for PIN Auth on TV
// Extensions & Other Libs
implementation("org.mozilla:rhino:1.7.15") // run JavaScript
implementation("me.xdrop:fuzzywuzzy:1.4.0") // Library/Ext Searching with Levenshtein Distance
implementation("com.github.LagradOst:SafeFile:0.0.6") // To Prevent the URI File Fu*kery
implementation("org.conscrypt:conscrypt-android:2.5.2") // To Fix SSL Fu*kery on Android 9
implementation("com.uwetrottmann.tmdb2:tmdb-java:2.11.0") // TMDB API v3 Wrapper Made with RetroFit
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs_nio:2.0.4") //nio flavor needed for NewPipeExtractor
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1") /* JSON Parser
^ Don't Bump Jackson above 2.13.1 , Crashes on Android TV's and FireSticks that have Min API
Level 25 or Less. */
// Downloading & Networking
implementation("androidx.work:work-runtime:2.9.0")
implementation("androidx.work:work-runtime-ktx:2.9.0")
implementation("com.github.Blatzar:NiceHttp:0.4.11") // HTTP Lib
implementation(project(":library") {
// There does not seem to be a good way of getting the android flavor.
val isDebug = gradle.startParameter.taskRequests.any { task ->
task.args.any { arg ->
arg.contains("debug", true)
}
}
this.extra.set("isDebug", isDebug)
})
}
tasks.register<Jar>("androidSourcesJar") {
archiveClassifier.set("sources")
from(android.sourceSets.getByName("main").java.srcDirs) // Full Sources
}
tasks.register<Copy>("copyJar") {
from(
"build/intermediates/compile_app_classes_jar/prereleaseDebug",
"../library/build/libs"
)
into("build/app-classes")
include("classes.jar", "library-jvm*.jar")
// Remove the version
rename("library-jvm.*.jar", "library-jvm.jar")
}
// Merge the app classes and the library classes into classes.jar
tasks.register<Jar>("makeJar") {
// Duplicates cause hard to catch errors, better to fail at compile time.
duplicatesStrategy = DuplicatesStrategy.FAIL
dependsOn(tasks.getByName("copyJar"))
from(
zipTree("build/app-classes/classes.jar"),
zipTree("build/app-classes/library-jvm.jar")
)
destinationDirectory.set(layout.buildDirectory)
archivesName = "classes"
}
tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = listOf("-Xjvm-default=all-compatibility")
}
}
tasks.withType<DokkaTask>().configureEach {
moduleName.set("Cloudstream")
dokkaSourceSets {
named("main") {
sourceLink {
// Unix based directory relative path to the root of the project (where you execute gradle respectively).
localDirectory.set(file("src/main/java"))
// URL showing where the source code can be accessed through the web browser
remoteUrl.set(URL("https://github.com/recloudstream/cloudstream/tree/master/app/src/main/java"))
// Suffix which is used to append the line number to the URL. Use #L for GitHub
remoteLineSuffix.set("#L")
}
}
}
}

View file

@ -1,6 +1,6 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
# proguardFiles setting in build.gradle.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

View file

@ -1,155 +1,57 @@
package com.lagradost.cloudstream3
import android.app.Activity
import android.os.Bundle
import android.os.PersistableBundle
import android.view.LayoutInflater
import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.Qualities
import androidx.viewbinding.ViewBinding
import com.lagradost.cloudstream3.databinding.FragmentHomeBinding
import com.lagradost.cloudstream3.databinding.FragmentHomeTvBinding
import com.lagradost.cloudstream3.databinding.FragmentLibraryBinding
import com.lagradost.cloudstream3.databinding.FragmentLibraryTvBinding
import com.lagradost.cloudstream3.databinding.FragmentPlayerBinding
import com.lagradost.cloudstream3.databinding.FragmentPlayerTvBinding
import com.lagradost.cloudstream3.databinding.FragmentResultBinding
import com.lagradost.cloudstream3.databinding.FragmentResultTvBinding
import com.lagradost.cloudstream3.databinding.FragmentSearchBinding
import com.lagradost.cloudstream3.databinding.FragmentSearchTvBinding
import com.lagradost.cloudstream3.databinding.HomeResultGridBinding
import com.lagradost.cloudstream3.databinding.HomepageParentBinding
import com.lagradost.cloudstream3.databinding.HomepageParentEmulatorBinding
import com.lagradost.cloudstream3.databinding.HomepageParentTvBinding
import com.lagradost.cloudstream3.databinding.PlayerCustomLayoutBinding
import com.lagradost.cloudstream3.databinding.PlayerCustomLayoutTvBinding
import com.lagradost.cloudstream3.databinding.RepositoryItemBinding
import com.lagradost.cloudstream3.databinding.RepositoryItemTvBinding
import com.lagradost.cloudstream3.databinding.SearchResultGridBinding
import com.lagradost.cloudstream3.databinding.SearchResultGridExpandedBinding
import com.lagradost.cloudstream3.databinding.TrailerCustomLayoutBinding
import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.TestingUtils
import kotlinx.coroutines.runBlocking
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class TestApplication : Activity() {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
}
}
@RunWith(AndroidJUnit4::class)
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> {
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
}
private fun getAllProviders(): Array<MainAPI> {
println("Providers: ${APIHolder.allProviders.size}")
return APIHolder.allProviders.toTypedArray() //.filter { !it.usesWebView }
}
@Test
@ -158,7 +60,78 @@ class ExampleInstrumentedTest {
println("Done providersExist")
}
@Throws
private inline fun <reified T : ViewBinding> testAllLayouts(
activity: Activity,
vararg layouts: Int
) {
val bind = T::class.java.methods.first { it.name == "bind" }
val inflater = LayoutInflater.from(activity)
for (layout in layouts) {
val root = inflater.inflate(layout, null, false)
bind.invoke(null, root)
}
}
@Test
@Throws
fun layoutTest() {
ActivityScenario.launch(MainActivity::class.java).use { scenario ->
scenario.onActivity { activity: MainActivity ->
// FragmentHomeHeadBinding and FragmentHomeHeadTvBinding CANT be the same
//testAllLayouts<FragmentHomeHeadBinding>(activity, R.layout.fragment_home_head, R.layout.fragment_home_head_tv)
//testAllLayouts<FragmentHomeHeadTvBinding>(activity, R.layout.fragment_home_head, R.layout.fragment_home_head_tv)
// main cant be tested
// testAllLayouts<ActivityMainTvBinding>(activity,R.layout.activity_main, R.layout.activity_main_tv)
// testAllLayouts<ActivityMainBinding>(activity,R.layout.activity_main, R.layout.activity_main_tv)
//testAllLayouts<ActivityMainBinding>(activity, R.layout.activity_main_tv)
testAllLayouts<FragmentPlayerBinding>(activity, R.layout.fragment_player,R.layout.fragment_player_tv)
testAllLayouts<FragmentPlayerTvBinding>(activity, R.layout.fragment_player,R.layout.fragment_player_tv)
// testAllLayouts<FragmentResultBinding>(activity, R.layout.fragment_result,R.layout.fragment_result_tv)
// testAllLayouts<FragmentResultTvBinding>(activity, R.layout.fragment_result,R.layout.fragment_result_tv)
testAllLayouts<PlayerCustomLayoutBinding>(activity, R.layout.player_custom_layout,R.layout.player_custom_layout_tv, R.layout.trailer_custom_layout)
testAllLayouts<PlayerCustomLayoutTvBinding>(activity, R.layout.player_custom_layout,R.layout.player_custom_layout_tv, R.layout.trailer_custom_layout)
testAllLayouts<TrailerCustomLayoutBinding>(activity, R.layout.player_custom_layout,R.layout.player_custom_layout_tv, R.layout.trailer_custom_layout)
testAllLayouts<RepositoryItemBinding>(activity, R.layout.repository_item_tv, R.layout.repository_item)
testAllLayouts<RepositoryItemTvBinding>(activity, R.layout.repository_item_tv, R.layout.repository_item)
testAllLayouts<RepositoryItemBinding>(activity, R.layout.repository_item_tv, R.layout.repository_item)
testAllLayouts<RepositoryItemTvBinding>(activity, R.layout.repository_item_tv, R.layout.repository_item)
testAllLayouts<FragmentHomeBinding>(activity, R.layout.fragment_home_tv, R.layout.fragment_home)
testAllLayouts<FragmentHomeTvBinding>(activity, R.layout.fragment_home_tv, R.layout.fragment_home)
testAllLayouts<FragmentSearchBinding>(activity, R.layout.fragment_search_tv, R.layout.fragment_search)
testAllLayouts<FragmentSearchTvBinding>(activity, R.layout.fragment_search_tv, R.layout.fragment_search)
testAllLayouts<HomeResultGridBinding>(activity, R.layout.home_result_grid_expanded, R.layout.home_result_grid)
//testAllLayouts<HomeResultGridExpandedBinding>(activity, R.layout.home_result_grid_expanded, R.layout.home_result_grid) ??? fails ???
testAllLayouts<SearchResultGridExpandedBinding>(activity, R.layout.search_result_grid, R.layout.search_result_grid_expanded)
testAllLayouts<SearchResultGridBinding>(activity, R.layout.search_result_grid, R.layout.search_result_grid_expanded)
// testAllLayouts<HomeScrollViewBinding>(activity, R.layout.home_scroll_view, R.layout.home_scroll_view_tv)
// testAllLayouts<HomeScrollViewTvBinding>(activity, R.layout.home_scroll_view, R.layout.home_scroll_view_tv)
testAllLayouts<HomepageParentTvBinding>(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent_emulator, R.layout.homepage_parent)
testAllLayouts<HomepageParentEmulatorBinding>(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent_emulator, R.layout.homepage_parent)
testAllLayouts<HomepageParentBinding>(activity, R.layout.homepage_parent_tv, R.layout.homepage_parent_emulator, R.layout.homepage_parent)
testAllLayouts<FragmentLibraryTvBinding>(activity, R.layout.fragment_library_tv, R.layout.fragment_library)
testAllLayouts<FragmentLibraryBinding>(activity, R.layout.fragment_library_tv, R.layout.fragment_library)
}
}
}
@Test
@Throws(AssertionError::class)
fun providerCorrectData() {
val isoNames = SubtitleHelper.languages.map { it.ISO_639_1 }
Assert.assertFalse("ISO does not contain any languages", isoNames.isNullOrEmpty())
@ -180,66 +153,20 @@ class ExampleInstrumentedTest {
@Test
fun providerCorrectHomepage() {
runBlocking {
getAllProviders().apmap { api ->
if (api.hasMainPage) {
try {
val homepage = api.getMainPage()
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)
}
}
getAllProviders().toList().amap { api ->
TestingUtils.testHomepage(api, TestingUtils.Logger())
}
}
println("Done providerCorrectHomepage")
}
// @Test
// fun testSingleProvider() {
// testSingleProviderApi(ThenosProvider())
// }
@Test
fun providerCorrect() {
fun testAllProvidersCorrect() {
runBlocking {
val invalidProvider = ArrayList<Pair<MainAPI, Exception?>>()
val providers = getAllProviders()
providers.apmap { api ->
try {
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}")
}
}
TestingUtils.getDeferredProviderTests(
this,
getAllProviders(),
) { _, _ -> }
}
println("Done providerCorrect")
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Before After
Before After

View file

@ -1,18 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.lagradost.cloudstream3">
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- I dont remember, probs has to do with downloads -->
<uses-permission android:name="android.permission.INTERNET" /> <!-- unless you only use cs3 as a player for downloaded stuff, you need this -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Downloads -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- Downloads on low api devices -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <!-- Plugin API -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> <!-- Plugin API -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <!-- some dependency needs this -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <!-- Used for player vertical slide -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <!-- Used for app update -->
<!-- <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> not used atm, but code exist that requires it that are not run -->
<!-- <permission android:name="android.permission.QUERY_ALL_PACKAGES" /> &lt;!&ndash; Used for getting if vlc is installed &ndash;&gt; -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Used for app notifications on Android 13+ -->
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> <!-- Used for Android TV watch next -->
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" /> <!-- Used for updates without prompt -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Used for update service -->
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<!-- Required for getting arbitrary Aniyomi packages -->
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<!-- Fixes android tv fuckery -->
<uses-feature
android:name="android.hardware.touchscreen"
@ -21,20 +30,30 @@
android:name="android.software.leanback"
android:required="false" />
<queries>
<package android:name="org.videolan.vlc" />
<package android:name="com.instantbits.cast.webvideo" />
<package android:name="is.xyz.mpv" />
</queries>
<!-- Without the large heap Exoplayer buffering gets reset due to OOM. -->
<!--TODO https://stackoverflow.com/questions/41799732/chromecast-button-not-visible-in-android-->
<application
android:name=".AcraApplication"
android:allowBackup="true"
android:enableOnBackInvokedCallback="true"
android:appCategory="video"
android:banner="@mipmap/ic_banner"
android:fullBackupContent="@xml/backup_descriptor"
android:dataExtractionRules="@xml/data_extraction_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"
tools:targetApi="o">
tools:targetApi="tiramisu">
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
@ -50,7 +69,9 @@
android:exported="true"
android:resizeableActivity="true"
android:screenOrientation="userLandscape"
android:supportsPictureInPicture="true">
android:supportsPictureInPicture="true"
android:taskAffinity="com.lagradost.cloudstream3.downloadedplayer"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -76,16 +97,20 @@
-->
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden|navigation"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden|navigation|uiMode"
android:exported="true"
android:launchMode="singleTask"
android:resizeableActivity="true"
android:supportsPictureInPicture="true">
<intent-filter android:exported="true">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
<!-- 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>
<action android:name="android.intent.action.VIEW" />
@ -103,6 +128,30 @@
<data android:scheme="cloudstreamrepo" />
</intent-filter>
<!-- Allow searching with intents: cloudstreamsearch://Your%20Name -->
<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="cloudstreamsearch" />
</intent-filter>
<!--
Allow opening from continue watching with intents: cloudstreamsearch://1234
Used on Android TV Watch Next
-->
<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="cloudstreamcontinuewatching" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -116,6 +165,21 @@
</intent-filter>
</activity>
<activity
android:name=".ui.account.AccountSelectActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
android:exported="true">
<intent-filter android:exported="true">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".ui.EasterEggMonke"
android:exported="true" />
@ -123,13 +187,14 @@
<receiver
android:name=".receivers.VideoDownloadRestartReceiver"
android:enabled="false"
android:exported="true">
<intent-filter android:exported="true">
android:exported="false">
<intent-filter android:exported="false">
<action android:name="restart_service" />
</intent-filter>
</receiver>
<service
android:foregroundServiceType="dataSync"
android:name=".services.VideoDownloadService"
android:enabled="true"
android:exported="false" />
@ -138,6 +203,11 @@
android:name=".ui.ControllerActivity"
android:exported="false" />
<service
android:foregroundServiceType="dataSync"
android:name=".utils.PackageInstallerService"
android:exported="false" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"

View file

@ -0,0 +1,28 @@
#include <jni.h>
#include <csignal>
#include <android/log.h>
#define TAG "CloudStream Crash Handler"
volatile sig_atomic_t gSignalStatus = 0;
void handleNativeCrash(int signal) {
gSignalStatus = signal;
}
extern "C" JNIEXPORT void JNICALL
Java_com_lagradost_cloudstream3_NativeCrashHandler_initNativeCrashHandler(JNIEnv *env, jobject) {
#define REGISTER_SIGNAL(X) signal(X, handleNativeCrash);
REGISTER_SIGNAL(SIGSEGV)
#undef REGISTER_SIGNAL
}
//extern "C" JNIEXPORT void JNICALL
//Java_com_lagradost_cloudstream3_NativeCrashHandler_triggerNativeCrash(JNIEnv *env, jobject thiz) {
// int *p = nullptr;
// *p = 0;
//}
extern "C" JNIEXPORT int JNICALL
Java_com_lagradost_cloudstream3_NativeCrashHandler_getSignalStatus(JNIEnv *env, jobject) {
//__android_log_print(ANDROID_LOG_INFO, TAG, "Got signal status %d", gSignalStatus);
return gSignalStatus;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Before After
Before After

View file

@ -4,12 +4,18 @@ import android.app.Activity
import android.app.Application
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.widget.Toast
import androidx.fragment.app.Fragment
import com.google.auto.service.AutoService
import androidx.fragment.app.FragmentActivity
import com.lagradost.api.setContext
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.AppContextUtils.openBrowser
import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread
import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.getKeys
@ -17,6 +23,7 @@ import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.DataStore.removeKeys
import com.lagradost.cloudstream3.utils.DataStore.setKey
import kotlinx.coroutines.runBlocking
import org.acra.ACRA
import org.acra.ReportField
import org.acra.config.CoreConfiguration
import org.acra.data.CrashReportData
@ -24,24 +31,29 @@ import org.acra.data.StringFormat
import org.acra.ktx.initAcra
import org.acra.sender.ReportSender
import org.acra.sender.ReportSenderFactory
import java.io.File
import java.io.FileNotFoundException
import java.io.PrintStream
import java.lang.ref.WeakReference
import java.util.Locale
import kotlin.concurrent.thread
import kotlin.system.exitProcess
class CustomReportSender : ReportSender {
// Sends all your crashes to google forms
override fun send(context: Context, errorContent: CrashReportData) {
println("Sending report")
val url =
"https://docs.google.com/forms/u/0/d/e/1FAIpQLSeFmyBChi6HF3IkhTVWPiDXJtxt8W0Hf4Agljm_0-0_QuEYFg/formResponse"
"https://docs.google.com/forms/d/e/1FAIpQLSfO4r353BJ79TTY_-t5KWSIJT2xfqcQWY81xjAA1-1N0U2eSg/formResponse"
val data = mapOf(
"entry.134906550" to errorContent.toJSON()
"entry.1993829403" to errorContent.toJSON()
)
thread { // to not run it on main thread
runBlocking {
suspendSafeApiCall {
val post = app.post(url, data = data)
println("Report response: $post")
app.post(url, data = data)
//println("Report response: $post")
}
}
}
@ -54,7 +66,6 @@ class CustomReportSender : ReportSender {
}
}
@AutoService(ReportSenderFactory::class)
class CustomSenderFactory : ReportSenderFactory {
override fun create(context: Context, config: CoreConfiguration): ReportSender {
return CustomReportSender()
@ -65,7 +76,40 @@ class CustomSenderFactory : ReportSenderFactory {
}
}
class ExceptionHandler(val errorFile: File, val onError: (() -> Unit)) :
Thread.UncaughtExceptionHandler {
override fun uncaughtException(thread: Thread, error: Throwable) {
ACRA.errorReporter.handleException(error)
try {
PrintStream(errorFile).use { ps ->
ps.println("Currently loading extension: ${PluginManager.currentlyLoading ?: "none"}")
ps.println("Fatal exception on thread ${thread.name} (${thread.id})")
error.printStackTrace(ps)
}
} catch (ignored: FileNotFoundException) {
}
try {
onError.invoke()
} catch (ignored: Exception) {
}
exitProcess(1)
}
}
class AcraApplication : Application() {
override fun onCreate() {
super.onCreate()
ExceptionHandler(filesDir.resolve("last_error")) {
val intent = context!!.packageManager.getLaunchIntentForPackage(context!!.packageName)
startActivity(Intent.makeRestartActivityTask(intent!!.component))
}.also {
exceptionHandler = it
Thread.setDefaultUncaughtExceptionHandler(it)
}
}
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
context = base
@ -75,10 +119,10 @@ class AcraApplication : Application() {
buildConfigClass = BuildConfig::class.java
reportFormat = StringFormat.JSON
reportContent = arrayOf(
reportContent = listOf(
ReportField.BUILD_CONFIG, ReportField.USER_CRASH_DATE,
ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL,
ReportField.STACK_TRACE
ReportField.STACK_TRACE,
)
// removed this due to bug when starting the app, moved it to when it actually crashes
@ -91,6 +135,8 @@ class AcraApplication : Application() {
}
companion object {
var exceptionHandler: ExceptionHandler? = null
/** Use to get activity from Context */
tailrec fun Context.getActivity(): Activity? = this as? Activity
?: (this as? ContextWrapper)?.baseContext?.getActivity()
@ -100,8 +146,17 @@ class AcraApplication : Application() {
get() = _context?.get()
private set(value) {
_context = WeakReference(value)
setContext(WeakReference(value))
}
fun <T : Any> getKeyClass(path: String, valueType: Class<T>): T? {
return context?.getKey(path, valueType)
}
fun <T : Any> setKeyClass(path: String, value: T) {
context?.setKey(path, value)
}
fun removeKeys(folder: String): Int? {
return context?.removeKeys(folder)
}
@ -148,5 +203,14 @@ class AcraApplication : Application() {
fun openBrowser(url: String, fallbackWebview: Boolean = false, fragment: Fragment? = null) {
context?.openBrowser(url, fallbackWebview, fragment)
}
/** Will fallback to webview if in TV layout */
fun openBrowser(url: String, activity: FragmentActivity?) {
openBrowser(
url,
isLayout(TV or EMULATOR),
activity?.supportFragmentManager?.fragments?.lastOrNull()
)
}
}
}
}

View file

@ -1,40 +1,95 @@
package com.lagradost.cloudstream3
import android.Manifest
import android.app.Activity
import android.app.PictureInPictureParams
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
import android.os.Looper
import android.util.DisplayMetrics
import android.util.Log
import android.view.*
import android.widget.TextView
import android.view.Gravity
import android.view.KeyEvent
import android.view.View
import android.view.View.NO_ID
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.MainThread
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
import androidx.core.view.children
import androidx.preference.PreferenceManager
import com.google.android.gms.cast.framework.CastSession
import com.google.android.material.chip.ChipGroup
import com.google.android.material.navigationrail.NavigationRailView
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.MainActivity.Companion.resumeApps
import com.lagradost.cloudstream3.databinding.ToastBinding
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.player.PlayerEventType
import com.lagradost.cloudstream3.ui.result.ResultFragment
import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.ui.settings.Globals.updateTv
import com.lagradost.cloudstream3.utils.AppContextUtils.isRtl
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.Event
import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.hasPIPPermission
import com.lagradost.cloudstream3.utils.UIHelper.shouldShowPIPMode
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import kotlinx.coroutines.currentCoroutineContext
import org.schabi.newpipe.extractor.NewPipe
import java.util.*
import java.lang.ref.WeakReference
import java.util.Locale
import kotlin.math.max
import kotlin.math.min
enum class FocusDirection {
Start,
End,
Up,
Down,
}
object CommonActivity {
private var _activity: WeakReference<Activity>? = null
var activity
get() = _activity?.get()
private set(value) {
_activity = WeakReference(value)
}
@MainThread
fun setActivityInstance(newActivity: Activity?) {
activity = newActivity
}
@MainThread
fun Activity?.getCastSession(): CastSession? {
return (this as MainActivity?)?.mSessionManager?.currentCastSession
}
val displayMetrics: DisplayMetrics = Resources.getSystem().displayMetrics
// screenWidth and screenHeight does always
// refer to the screen while in landscape mode
val screenWidth: Int
get() {
return max(displayMetrics.widthPixels, displayMetrics.heightPixels)
}
val screenHeight: Int
get() {
return min(displayMetrics.widthPixels, displayMetrics.heightPixels)
}
var canEnterPipMode: Boolean = false
var canShowPipMode: Boolean = false
var isInPIPMode: Boolean = false
@ -45,9 +100,32 @@ object CommonActivity {
var playerEventListener: ((PlayerEventType) -> Unit)? = null
var keyEventListener: ((Pair<KeyEvent?, Boolean>) -> Boolean)? = null
private var currentToast: Toast? = null
var currentToast: Toast? = null
fun showToast(@StringRes message: Int, duration: Int? = null) {
val act = activity ?: return
act.runOnUiThread {
showToast(act, act.getString(message), duration)
}
}
fun showToast(message: String?, duration: Int? = null) {
val act = activity ?: return
act.runOnUiThread {
showToast(act, message, duration)
}
}
fun showToast(message: UiText?, duration: Int? = null) {
val act = activity ?: return
if (message == null) return
act.runOnUiThread {
showToast(act, message.asString(act), duration)
}
}
@MainThread
fun showToast(act: Activity?, text: UiText, duration: Int) {
if (act == null) return
text.asStringNull(act)?.let {
@ -55,7 +133,9 @@ object CommonActivity {
}
}
fun showToast(act: Activity?, @StringRes message: Int, duration: Int) {
/** duration is Toast.LENGTH_SHORT if null*/
@MainThread
fun showToast(act: Activity?, @StringRes message: Int, duration: Int? = null) {
if (act == null) return
showToast(act, act.getString(message), duration)
}
@ -63,6 +143,7 @@ object CommonActivity {
const val TAG = "COMPACT"
/** duration is Toast.LENGTH_SHORT if null*/
@MainThread
fun showToast(act: Activity?, message: String?, duration: Int? = null) {
if (act == null || message == null) {
Log.w(TAG, "invalid showToast act = $act message = $message")
@ -75,33 +156,36 @@ object CommonActivity {
} catch (e: Exception) {
logError(e)
}
try {
val inflater =
act.getSystemService(AppCompatActivity.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val layout: View = inflater.inflate(
R.layout.toast,
act.findViewById<View>(R.id.toast_layout_root) as ViewGroup?
)
val text = layout.findViewById(R.id.text) as TextView
text.text = message.trim()
val binding = ToastBinding.inflate(act.layoutInflater)
binding.text.text = message.trim()
// custom toasts are deprecated and won't appear when cs3 sets minSDK to api30 (A11)
val toast = Toast(act)
toast.setGravity(Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM, 0, 5.toPx)
toast.duration = duration ?: Toast.LENGTH_SHORT
toast.view = layout
//https://github.com/PureWriter/ToastCompat
toast.show()
toast.setGravity(Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM, 0, 5.toPx)
toast.view = binding.root //fixme Find an alternative using default Toasts since custom toasts are deprecated and won't appear with api30 set as minSDK version.
currentToast = toast
toast.show()
} catch (e: Exception) {
logError(e)
}
}
/**
* Not all languages can be fetched from locale with a code.
* This map allows sidestepping the default Locale(languageCode)
* when setting the app language.
**/
val appLanguageExceptions = hashMapOf(
"zh-rTW" to Locale.TRADITIONAL_CHINESE
)
fun setLocale(context: Context?, languageCode: String?) {
if (context == null || languageCode == null) return
val locale = Locale(languageCode)
val locale = appLanguageExceptions[languageCode] ?: Locale(languageCode)
val resources: Resources = context.resources
val config = resources.configuration
Locale.setDefault(locale)
@ -118,18 +202,54 @@ object CommonActivity {
setLocale(this, localeCode)
}
fun init(act: Activity?) {
if (act == null) return
fun init(act: Activity) {
setActivityInstance(act)
val componentActivity = activity as? ComponentActivity ?: return
//https://stackoverflow.com/questions/52594181/how-to-know-if-user-has-disabled-picture-in-picture-feature-permission
//https://developer.android.com/guide/topics/ui/picture-in-picture
canShowPipMode =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && // OS SUPPORT
act.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && // HAS FEATURE, MIGHT BE BLOCKED DUE TO POWER DRAIN
act.hasPIPPermission() // CHECK IF FEATURE IS ENABLED IN SETTINGS
act.updateLocale()
componentActivity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && // HAS FEATURE, MIGHT BE BLOCKED DUE TO POWER DRAIN
componentActivity.hasPIPPermission() // CHECK IF FEATURE IS ENABLED IN SETTINGS
componentActivity.updateLocale()
componentActivity.updateTv()
NewPipe.init(DownloaderTestImpl.getInstance())
for (resumeApp in resumeApps) {
resumeApp.launcher =
componentActivity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
val resultCode = result.resultCode
val data = result.data
if (resultCode == AppCompatActivity.RESULT_OK && data != null && resumeApp.position != null && resumeApp.duration != null) {
val pos = resumeApp.getPosition(data)
val dur = resumeApp.getDuration(data)
if (dur > 0L && pos > 0L)
DataStoreHelper.setViewPos(getKey(resumeApp.lastId), pos, dur)
removeKey(resumeApp.lastId)
ResultFragment.updateUI()
}
}
}
// Ask for notification permissions on Android 13
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
ContextCompat.checkSelfPermission(
componentActivity,
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
val requestPermissionLauncher = componentActivity.registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
Log.d(TAG, "Notification permission: $isGranted")
}
requestPermissionLauncher.launch(
Manifest.permission.POST_NOTIFICATIONS
)
}
}
private fun Activity.enterPIPMode() {
@ -157,28 +277,57 @@ object CommonActivity {
}
}
fun updateTheme(act: Activity) {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(act)
if (settingsManager
.getString(act.getString(R.string.app_theme_key), "AmoledLight") == "System"
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
loadThemes(act)
}
}
private fun mapSystemTheme(act: Activity): Int {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val currentNightMode =
act.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
return when (currentNightMode) {
Configuration.UI_MODE_NIGHT_NO -> R.style.LightMode // Night mode is not active, we're using the light theme
else -> R.style.AppTheme // Night mode is active, we're using dark theme
}
} else {
return R.style.AppTheme
}
}
fun loadThemes(act: Activity?) {
if (act == null) return
val settingsManager = PreferenceManager.getDefaultSharedPreferences(act)
val currentTheme =
when (settingsManager.getString(act.getString(R.string.app_theme_key), "AmoledLight")) {
"System" -> mapSystemTheme(act)
"Black" -> R.style.AppTheme
"Light" -> R.style.LightMode
"Amoled" -> R.style.AmoledMode
"AmoledLight" -> R.style.AmoledModeLight
"Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
R.style.MonetMode else R.style.AppTheme
else -> R.style.AppTheme
}
val currentOverlayTheme =
when (settingsManager.getString(act.getString(R.string.primary_color_key), "Normal")) {
"Normal" -> R.style.OverlayPrimaryColorNormal
"DandelionYellow" -> R.style.OverlayPrimaryColorDandelionYellow
"CarnationPink" -> R.style.OverlayPrimaryColorCarnationPink
"Orange" -> R.style.OverlayPrimaryColorOrange
"DarkGreen" -> R.style.OverlayPrimaryColorDarkGreen
"Maroon" -> R.style.OverlayPrimaryColorMaroon
"NavyBlue" -> R.style.OverlayPrimaryColorNavyBlue
"Grey" -> R.style.OverlayPrimaryColorGrey
"White" -> R.style.OverlayPrimaryColorWhite
"CoolBlue" -> R.style.OverlayPrimaryColorCoolBlue
"Brown" -> R.style.OverlayPrimaryColorBrown
"Purple" -> R.style.OverlayPrimaryColorPurple
"Green" -> R.style.OverlayPrimaryColorGreen
@ -187,6 +336,13 @@ object CommonActivity {
"Banana" -> R.style.OverlayPrimaryColorBanana
"Party" -> R.style.OverlayPrimaryColorParty
"Pink" -> R.style.OverlayPrimaryColorPink
"Lavender" -> R.style.OverlayPrimaryColorLavender
"Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
R.style.OverlayPrimaryColorMonet else R.style.OverlayPrimaryColorNormal
"Monet2" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
R.style.OverlayPrimaryColorMonetTwo else R.style.OverlayPrimaryColorNormal
else -> R.style.OverlayPrimaryColorNormal
}
act.theme.applyStyle(currentTheme, true)
@ -198,120 +354,207 @@ object CommonActivity {
) // THEME IS SET BEFORE VIEW IS CREATED TO APPLY THE THEME TO THE MAIN VIEW
}
private fun getNextFocus(
act: Activity?,
/** because we want closes find, aka when multiple have the same id, we go to parent
until the correct one is found */
private fun localLook(from: View, id: Int): View? {
if (id == NO_ID) return null
var currentLook: View = from
// limit to 15 look depth
for (i in 0..15) {
currentLook.findViewById<View?>(id)?.let { return it }
currentLook = (currentLook.parent as? View) ?: break
}
return null
}
/*var currentLook: View = view
while (true) {
val tmpNext = currentLook.findViewById<View?>(nextId)
if (tmpNext != null) {
next = tmpNext
break
}
currentLook = currentLook.parent as? View ?: break
}*/
private fun View.hasContent(): Boolean {
return isShown && when (this) {
//is RecyclerView -> this.childCount > 0
is ViewGroup -> this.childCount > 0
else -> true
}
}
/** skips the initial stage of searching for an id using the view, see getNextFocus for specification */
fun continueGetNextFocus(
root: Any?,
view: View,
direction: FocusDirection,
nextId: Int,
depth: Int = 0
): View? {
if (nextId == NO_ID) return null
// do an initial search for the view, in case the localLook is too deep we can use this as
// an early break and backup view
var next =
when (root) {
is Activity -> root.findViewById(nextId)
is View -> root.rootView.findViewById<View?>(nextId)
else -> null
} ?: return null
next = localLook(view, nextId) ?: next
val shown = next.hasContent()
// if cant focus but visible then break and let android decide
// the exception if is the view is a parent and has children that wants focus
val hasChildrenThatWantsFocus = (next as? ViewGroup)?.let { parent ->
parent.descendantFocusability == ViewGroup.FOCUS_AFTER_DESCENDANTS && parent.childCount > 0
} ?: false
if (!next.isFocusable && shown && !hasChildrenThatWantsFocus) return null
// if not shown then continue because we will "skip" over views to get to a replacement
if (!shown) {
// we don't want a while true loop, so we let android decide if we find a recursive view
if (next == view) return null
return getNextFocus(root, next, direction, depth + 1)
}
(when (next) {
is ChipGroup -> {
next.children.firstOrNull { it.isFocusable && it.isShown }
}
is NavigationRailView -> {
next.findViewById(next.selectedItemId) ?: next.findViewById(R.id.navigation_home)
}
else -> null
})?.let {
return it
}
// nothing wrong with the view found, return it
return next
}
/** recursively looks for a next focus up to a depth of 10,
* this is used to override the normal shit focus system
* because this application has a lot of invisible views that messes with some tv devices*/
fun getNextFocus(
root: Any?,
view: View?,
direction: FocusDirection,
depth: Int = 0
): Int? {
if (view == null || depth >= 10 || act == null) {
): View? {
// if input is invalid let android decide + depth test to not crash if loop is found
if (view == null || depth >= 10 || root == null) {
return null
}
val nextId = when (direction) {
FocusDirection.Left -> {
view.nextFocusLeftId
var nextId = when (direction) {
FocusDirection.Start -> {
if (view.isRtl())
view.nextFocusRightId
else
view.nextFocusLeftId
}
FocusDirection.Up -> {
view.nextFocusUpId
}
FocusDirection.Right -> {
view.nextFocusRightId
FocusDirection.End -> {
if (view.isRtl())
view.nextFocusLeftId
else
view.nextFocusRightId
}
FocusDirection.Down -> {
view.nextFocusDownId
}
}
return if (nextId != -1) {
val next = act.findViewById<View?>(nextId)
//println("NAME: ${next.accessibilityClassName} | ${next?.isShown}" )
if (next?.isShown == false) {
getNextFocus(act, next, direction, depth + 1)
} else {
if (depth == 0) {
null
} else {
nextId
}
}
} else {
null
if (nextId == NO_ID) {
// if not specified then use forward id
nextId = view.nextFocusForwardId
// if view is still not found to next focus then return and let android decide
if (nextId == NO_ID)
return null
}
return continueGetNextFocus(root, view, direction, nextId, depth)
}
enum class FocusDirection {
Left,
Right,
Up,
Down,
}
fun onKeyDown(act: Activity?, keyCode: Int, event: KeyEvent?) {
//println("Keycode: $keyCode")
//showToast(
// this,
// "Got Keycode $keyCode | ${KeyEvent.keyCodeToString(keyCode)} \n ${event?.action}",
// Toast.LENGTH_LONG
//)
// Tested keycodes on remote:
// KeyEvent.KEYCODE_MEDIA_FAST_FORWARD
// KeyEvent.KEYCODE_MEDIA_REWIND
// KeyEvent.KEYCODE_MENU
// KeyEvent.KEYCODE_MEDIA_NEXT
// KeyEvent.KEYCODE_MEDIA_PREVIOUS
// KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
// 149 keycode_numpad 5
when (keyCode) {
KeyEvent.KEYCODE_FORWARD, KeyEvent.KEYCODE_D, KeyEvent.KEYCODE_MEDIA_SKIP_FORWARD, KeyEvent.KEYCODE_MEDIA_FAST_FORWARD -> {
PlayerEventType.SeekForward
}
KeyEvent.KEYCODE_A, KeyEvent.KEYCODE_MEDIA_SKIP_BACKWARD, KeyEvent.KEYCODE_MEDIA_REWIND -> {
PlayerEventType.SeekBack
}
KeyEvent.KEYCODE_MEDIA_NEXT, KeyEvent.KEYCODE_BUTTON_R1, KeyEvent.KEYCODE_N -> {
PlayerEventType.NextEpisode
}
KeyEvent.KEYCODE_MEDIA_PREVIOUS, KeyEvent.KEYCODE_BUTTON_L1, KeyEvent.KEYCODE_B -> {
PlayerEventType.PrevEpisode
}
KeyEvent.KEYCODE_MEDIA_PAUSE -> {
PlayerEventType.Pause
}
KeyEvent.KEYCODE_MEDIA_PLAY, KeyEvent.KEYCODE_BUTTON_START -> {
PlayerEventType.Play
}
KeyEvent.KEYCODE_L, KeyEvent.KEYCODE_NUMPAD_7 -> {
KeyEvent.KEYCODE_L, KeyEvent.KEYCODE_NUMPAD_7, KeyEvent.KEYCODE_7 -> {
PlayerEventType.Lock
}
KeyEvent.KEYCODE_H, KeyEvent.KEYCODE_MENU -> {
PlayerEventType.ToggleHide
}
KeyEvent.KEYCODE_M, KeyEvent.KEYCODE_VOLUME_MUTE -> {
PlayerEventType.ToggleMute
}
KeyEvent.KEYCODE_S, KeyEvent.KEYCODE_NUMPAD_9 -> {
KeyEvent.KEYCODE_S, KeyEvent.KEYCODE_NUMPAD_9, KeyEvent.KEYCODE_9 -> {
PlayerEventType.ShowMirrors
}
// OpenSubtitles shortcut
KeyEvent.KEYCODE_O, KeyEvent.KEYCODE_NUMPAD_8 -> {
KeyEvent.KEYCODE_O, KeyEvent.KEYCODE_NUMPAD_8, KeyEvent.KEYCODE_8 -> {
PlayerEventType.SearchSubtitlesOnline
}
KeyEvent.KEYCODE_E, KeyEvent.KEYCODE_NUMPAD_3 -> {
KeyEvent.KEYCODE_E, KeyEvent.KEYCODE_NUMPAD_3, KeyEvent.KEYCODE_3 -> {
PlayerEventType.ShowSpeed
}
KeyEvent.KEYCODE_R, KeyEvent.KEYCODE_NUMPAD_0 -> {
KeyEvent.KEYCODE_R, KeyEvent.KEYCODE_NUMPAD_0, KeyEvent.KEYCODE_0 -> {
PlayerEventType.Resize
}
KeyEvent.KEYCODE_C, KeyEvent.KEYCODE_NUMPAD_4 -> {
KeyEvent.KEYCODE_C, KeyEvent.KEYCODE_NUMPAD_4, KeyEvent.KEYCODE_4 -> {
PlayerEventType.SkipOp
}
KeyEvent.KEYCODE_V, KeyEvent.KEYCODE_NUMPAD_5, KeyEvent.KEYCODE_5 -> {
PlayerEventType.SkipCurrentChapter
}
KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_P, KeyEvent.KEYCODE_SPACE, KeyEvent.KEYCODE_NUMPAD_ENTER, KeyEvent.KEYCODE_ENTER -> { // space is not captured due to navigation
PlayerEventType.PlayPauseToggle
}
else -> null
}?.let { playerEvent ->
playerEventListener?.invoke(playerEvent)
@ -324,67 +567,67 @@ object CommonActivity {
//}
}
/** overrides focus and custom key events */
fun dispatchKeyEvent(act: Activity?, event: KeyEvent?): Boolean? {
if (act == null) return null
val currentFocus = act.currentFocus
event?.keyCode?.let { keyCode ->
when (event.action) {
KeyEvent.ACTION_DOWN -> {
if (act.currentFocus != null) {
val next = when (keyCode) {
KeyEvent.KEYCODE_DPAD_LEFT -> getNextFocus(
act,
act.currentFocus,
FocusDirection.Left
)
KeyEvent.KEYCODE_DPAD_RIGHT -> getNextFocus(
act,
act.currentFocus,
FocusDirection.Right
)
KeyEvent.KEYCODE_DPAD_UP -> getNextFocus(
act,
act.currentFocus,
FocusDirection.Up
)
KeyEvent.KEYCODE_DPAD_DOWN -> getNextFocus(
act,
act.currentFocus,
FocusDirection.Down
)
if (currentFocus == null || event.action != KeyEvent.ACTION_DOWN) return@let
val nextView = when (keyCode) {
KeyEvent.KEYCODE_DPAD_LEFT -> getNextFocus(
act,
currentFocus,
FocusDirection.Start
)
else -> null
}
KeyEvent.KEYCODE_DPAD_RIGHT -> getNextFocus(
act,
currentFocus,
FocusDirection.End
)
if (next != null && next != -1) {
val nextView = act.findViewById<View?>(next)
if (nextView != null) {
nextView.requestFocus()
keyEventListener?.invoke(Pair(event, true))
return true
}
}
KeyEvent.KEYCODE_DPAD_UP -> getNextFocus(
act,
currentFocus,
FocusDirection.Up
)
when (keyCode) {
KeyEvent.KEYCODE_DPAD_CENTER -> {
if (act.currentFocus is SearchView || act.currentFocus is SearchView.SearchAutoComplete) {
UIHelper.showInputMethod(act.currentFocus?.findFocus())
}
}
}
}
//println("Keycode: $keyCode")
//showToast(
// this,
// "Got Keycode $keyCode | ${KeyEvent.keyCodeToString(keyCode)} \n ${event?.action}",
// Toast.LENGTH_LONG
//)
}
KeyEvent.KEYCODE_DPAD_DOWN -> getNextFocus(
act,
currentFocus,
FocusDirection.Down
)
else -> null
}
// println("NEXT FOCUS : $nextView")
if (nextView != null) {
nextView.requestFocus()
keyEventListener?.invoke(Pair(event, true))
return true
}
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER &&
(act.currentFocus is SearchView || act.currentFocus is SearchView.SearchAutoComplete)
) {
UIHelper.showInputMethod(act.currentFocus?.findFocus())
}
//println("Keycode: $keyCode")
//showToast(
// this,
// "Got Keycode $keyCode | ${KeyEvent.keyCodeToString(keyCode)} \n ${event?.action}",
// Toast.LENGTH_LONG
//)
}
// if someone else want to override the focus then don't handle the event as it is already
// consumed. used in video player
if (keyEventListener?.invoke(Pair(event, false)) == true) {
return true
}
return null
}
}
}

View file

@ -2,6 +2,7 @@ package com.lagradost.cloudstream3
import okhttp3.OkHttpClient
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.schabi.newpipe.extractor.downloader.Downloader
import org.schabi.newpipe.extractor.downloader.Request
import org.schabi.newpipe.extractor.downloader.Response
@ -10,7 +11,7 @@ import java.util.concurrent.TimeUnit
class DownloaderTestImpl private constructor(builder: OkHttpClient.Builder) : Downloader() {
private val client: OkHttpClient
private val client: OkHttpClient = builder.readTimeout(30, TimeUnit.SECONDS).build()
override fun execute(request: Request): Response {
val httpMethod: String = request.httpMethod()
val url: String = request.url()
@ -18,7 +19,7 @@ class DownloaderTestImpl private constructor(builder: OkHttpClient.Builder) : Do
val dataToSend: ByteArray? = request.dataToSend()
var requestBody: RequestBody? = null
if (dataToSend != null) {
requestBody = RequestBody.create(null, dataToSend)
requestBody = dataToSend.toRequestBody(null, 0, dataToSend.size)
}
val requestBuilder: okhttp3.Request.Builder = okhttp3.Request.Builder()
.method(httpMethod, requestBody).url(url)
@ -50,7 +51,7 @@ class DownloaderTestImpl private constructor(builder: OkHttpClient.Builder) : Do
companion object {
private const val USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0"
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
private var instance: DownloaderTestImpl? = null
/**
@ -73,8 +74,4 @@ class DownloaderTestImpl private constructor(builder: OkHttpClient.Builder) : Do
return instance
}
}
init {
client = builder.readTimeout(30, TimeUnit.SECONDS).build()
}
}

View file

@ -0,0 +1,11 @@
package com.lagradost.cloudstream3
import android.view.LayoutInflater
import androidx.annotation.LayoutRes
import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.ui.HeaderViewDecoration
fun setHeaderDecoration(view: RecyclerView, @LayoutRes headerViewRes: Int) {
val headerView = LayoutInflater.from(view.context).inflate(headerViewRes, null)
view.addItemDecoration(HeaderViewDecoration(headerView))
}

File diff suppressed because it is too large Load diff

View file

@ -1,40 +0,0 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.base64Decode
import com.lagradost.cloudstream3.utils.*
class Acefile : ExtractorApi() {
override val name = "Acefile"
override val mainUrl = "https://acefile.co"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val sources = mutableListOf<ExtractorLink>()
app.get(url).document.select("script").map { script ->
if (script.data().contains("eval(function(p,a,c,k,e,d)")) {
val data = getAndUnpack(script.data())
val id = data.substringAfter("{\"id\":\"").substringBefore("\",")
val key = data.substringAfter("var nfck=\"").substringBefore("\";")
app.get("https://acefile.co/local/$id?key=$key").text.let {
base64Decode(
it.substringAfter("JSON.parse(atob(\"").substringBefore("\"))")
).let { res ->
sources.add(
ExtractorLink(
name,
name,
res.substringAfter("\"file\":\"").substringBefore("\","),
"$mainUrl/",
Qualities.Unknown.value,
headers = mapOf("range" to "bytes=0-")
)
)
}
}
}
}
return sources
}
}

View file

@ -1,39 +0,0 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
class Fastream: ExtractorApi() {
override var mainUrl = "https://fastream.to"
override var name = "Fastream"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val id = Regex("emb\\.html\\?(.*)\\=(enc|)").find(url)?.destructured?.component1() ?: return emptyList()
val sources = mutableListOf<ExtractorLink>()
val response = app.post("$mainUrl/dl",
data = mapOf(
Pair("op","embed"),
Pair("file_code",id),
Pair("auto","1")
)).document
response.select("script").apmap { script ->
if (script.data().contains("sources")) {
val m3u8regex = Regex("((https:|http:)\\/\\/.*\\.m3u8)")
val m3u8 = m3u8regex.find(script.data())?.value ?: return@apmap
generateM3u8(
name,
m3u8,
mainUrl
).forEach { link ->
sources.add(link)
}
}
}
return sources
}
}

View file

@ -1,38 +0,0 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
class Filesim : ExtractorApi() {
override val name = "Filesim"
override val mainUrl = "https://files.im"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val sources = mutableListOf<ExtractorLink>()
with(app.get(url).document) {
this.select("script").map { script ->
if (script.data().contains("eval(function(p,a,c,k,e,d)")) {
val data = getAndUnpack(script.data()).substringAfter("sources:[").substringBefore("]")
tryParseJson<List<ResponseSource>>("[$data]")?.map {
M3u8Helper.generateM3u8(
name,
it.file,
"$mainUrl/",
).forEach { m3uData -> sources.add(m3uData) }
}
}
}
}
return sources
}
private data class ResponseSource(
@JsonProperty("file") val file: String,
@JsonProperty("type") val type: String?,
@JsonProperty("label") val label: String?
)
}

View file

@ -1,36 +0,0 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
open class GuardareStream : ExtractorApi() {
override var name = "Guardare"
override var mainUrl = "https://guardare.stream"
override val requiresReferer = false
data class GuardareJsonData (
@JsonProperty("data") val data : List<GuardareData>,
)
data class GuardareData (
@JsonProperty("file") val file : String,
@JsonProperty("label") val label : String,
@JsonProperty("type") val type : String
)
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val response = app.post(url.replace("/v/","/api/source/"), data = mapOf("d" to mainUrl)).text
val jsonvideodata = AppUtils.parseJson<GuardareJsonData>(response)
return jsonvideodata.data.map {
ExtractorLink(
it.file+".${it.type}",
this.name,
it.file+".${it.type}",
mainUrl,
it.label.filter{ it.isDigit() }.toInt(),
false
)
}
}
}

View file

@ -1,46 +0,0 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
class Linkbox : ExtractorApi() {
override val name = "Linkbox"
override val mainUrl = "https://www.linkbox.to"
override val requiresReferer = true
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val id = url.substringAfter("id=")
val sources = mutableListOf<ExtractorLink>()
app.get("$mainUrl/api/open/get_url?itemId=$id", referer=url).parsedSafe<Responses>()?.data?.rList?.map { link ->
sources.add(
ExtractorLink(
name,
name,
link.url,
url,
getQualityFromName(link.resolution)
)
)
}
return sources
}
data class RList(
@JsonProperty("url") val url: String,
@JsonProperty("resolution") val resolution: String?,
)
data class Data(
@JsonProperty("rList") val rList: List<RList>?,
)
data class Responses(
@JsonProperty("data") val data: Data?,
)
}

View file

@ -1,7 +0,0 @@
package com.lagradost.cloudstream3.extractors
open class Mcloud : WcoStream() {
override var name = "Mcloud"
override var mainUrl = "https://mcloud.to"
override val requiresReferer = true
}

View file

@ -1,34 +0,0 @@
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 Mp4Upload : ExtractorApi() {
override var name = "Mp4Upload"
override var mainUrl = "https://www.mp4upload.com"
private val srcRegex = Regex("""player\.src\("(.*?)"""")
override val requiresReferer = true
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
with(app.get(url)) {
getAndUnpack(this.text).let { unpackedText ->
val quality = unpackedText.lowercase().substringAfter(" height=").substringBefore(" ").toIntOrNull()
srcRegex.find(unpackedText)?.groupValues?.get(1)?.let { link ->
return listOf(
ExtractorLink(
name,
name,
link,
url,
quality ?: Qualities.Unknown.value,
)
)
}
}
}
return null
}
}

View file

@ -1,67 +0,0 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
data class DataOptionsJson (
@JsonProperty("flashvars") var flashvars : Flashvars? = Flashvars(),
)
data class Flashvars (
@JsonProperty("metadata") var metadata : String? = null,
@JsonProperty("hlsManifestUrl") var hlsManifestUrl : String? = null, //m3u8
)
data class MetadataOkru (
@JsonProperty("videos") var videos: ArrayList<Videos> = arrayListOf(),
)
data class Videos (
@JsonProperty("name") var name : String,
@JsonProperty("url") var url : String,
@JsonProperty("seekSchema") var seekSchema : Int? = null,
@JsonProperty("disallowed") var disallowed : Boolean? = null
)
class OkRuHttps: OkRu(){
override var mainUrl = "https://ok.ru"
}
open class OkRu : ExtractorApi() {
override var name = "Okru"
override var mainUrl = "http://ok.ru"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val doc = app.get(url).document
val sources = ArrayList<ExtractorLink>()
val datajson = doc.select("div[data-options]").attr("data-options")
if (datajson.isNotBlank()) {
val main = parseJson<DataOptionsJson>(datajson)
val metadatajson = parseJson<MetadataOkru>(main.flashvars?.metadata!!)
val servers = metadatajson.videos
servers.forEach {
val quality = it.name.uppercase()
.replace("MOBILE","144p")
.replace("LOWEST","240p")
.replace("LOW","360p")
.replace("SD","480p")
.replace("HD","720p")
.replace("FULL","1080p")
.replace("QUAD","1440p")
.replace("ULTRA","4k")
val extractedurl = it.url.replace("\\\\u0026", "&")
sources.add(ExtractorLink(
name,
name = this.name,
extractedurl,
url,
getQualityFromName(quality),
isM3u8 = false
))
}
}
return sources
}
}

View file

@ -1,126 +0,0 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
class Ssbstream : StreamSB() {
override var mainUrl = "https://ssbstream.net"
}
class SBfull : StreamSB() {
override var mainUrl = "https://sbfull.com"
}
class StreamSB1 : StreamSB() {
override var mainUrl = "https://sbplay1.com"
}
class StreamSB2 : StreamSB() {
override var mainUrl = "https://sbplay2.com"
}
class StreamSB3 : StreamSB() {
override var mainUrl = "https://sbplay3.com"
}
class StreamSB4 : StreamSB() {
override var mainUrl = "https://cloudemb.com"
}
class StreamSB5 : StreamSB() {
override var mainUrl = "https://sbplay.org"
}
class StreamSB6 : StreamSB() {
override var mainUrl = "https://embedsb.com"
}
class StreamSB7 : StreamSB() {
override var mainUrl = "https://pelistop.co"
}
class StreamSB8 : StreamSB() {
override var mainUrl = "https://streamsb.net"
}
class StreamSB9 : StreamSB() {
override var mainUrl = "https://sbplay.one"
}
class StreamSB10 : StreamSB() {
override var mainUrl = "https://sbplay2.xyz"
}
// 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
open class StreamSB : ExtractorApi() {
override var name = "StreamSB"
override var mainUrl = "https://watchsb.com"
override val requiresReferer = false
private val hexArray = "0123456789ABCDEF".toCharArray()
private fun bytesToHex(bytes: ByteArray): String {
val hexChars = CharArray(bytes.size * 2)
for (j in bytes.indices) {
val v = bytes[j].toInt() and 0xFF
hexChars[j * 2] = hexArray[v ushr 4]
hexChars[j * 2 + 1] = hexArray[v and 0x0F]
}
return String(hexChars)
}
data class Subs (
@JsonProperty("file") val file: String,
@JsonProperty("label") val label: String,
)
data class StreamData (
@JsonProperty("file") val file: String,
@JsonProperty("cdn_img") val cdnImg: String,
@JsonProperty("hash") val hash: String,
@JsonProperty("subs") val subs: List<Subs>?,
@JsonProperty("length") val length: String,
@JsonProperty("id") val id: String,
@JsonProperty("title") val title: String,
@JsonProperty("backup") val backup: String,
)
data class Main (
@JsonProperty("stream_data") val streamData: StreamData,
@JsonProperty("status_code") val statusCode: Int,
)
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val regexID = Regex("(embed-[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+|\\/e\\/[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)")
val id = regexID.findAll(url).map {
it.value.replace(Regex("(embed-|\\/e\\/)"),"")
}.first()
val bytes = id.toByteArray()
val bytesToHex = bytesToHex(bytes)
val master = "$mainUrl/sources43/6d6144797752744a454267617c7c${bytesToHex.lowercase()}7c7c4e61755a56456f34385243727c7c73747265616d7362/6b4a33767968506e4e71374f7c7c343837323439333133333462353935333633373836643638376337633462333634663539343137373761333635313533333835333763376333393636363133393635366136323733343435323332376137633763373337343732363536313664373336327c7c504d754478413835306633797c7c73747265616d7362"
val headers = mapOf(
"watchsb" to "streamsb",
)
val urltext = app.get(master,
headers = headers,
allowRedirects = false
).text
val mapped = urltext.let { parseJson<Main>(it) }
val testurl = app.get(mapped.streamData.file, headers = headers).text
// val urlmain = mapped.streamData.file.substringBefore("/hls/")
if (urltext.contains("m3u8") && testurl.contains("EXTM3U"))
return M3u8Helper.generateM3u8(
name,
mapped.streamData.file,
url,
headers = headers
)
return null
}
}

View file

@ -1,41 +0,0 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
class Cinestart: Tomatomatela() {
override var name = "Cinestart"
override var mainUrl = "https://cinestart.net"
override val details = "vr.php?v="
}
open class Tomatomatela : ExtractorApi() {
override var name = "Tomatomatela"
override var mainUrl = "https://tomatomatela.com"
override val requiresReferer = false
private data class Tomato (
@JsonProperty("status") val status: Int,
@JsonProperty("file") val file: String
)
open val details = "details.php?v="
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val link = url.replace("$mainUrl/embed.html#","$mainUrl/$details")
val server = app.get(link, allowRedirects = false).text
val json = parseJson<Tomato>(server)
if (json.status == 200) return listOf(
ExtractorLink(
name,
name,
json.file,
"",
Qualities.Unknown.value,
isM3u8 = false
)
)
return null
}
}

View file

@ -1,68 +0,0 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
import com.lagradost.cloudstream3.utils.loadExtractor
class VidSrcExtractor2 : VidSrcExtractor() {
override val mainUrl = "https://vidsrc.me/embed"
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val newUrl = url.lowercase().replace(mainUrl, super.mainUrl)
super.getUrl(newUrl, referer, subtitleCallback, callback)
}
}
open class VidSrcExtractor : ExtractorApi() {
override val name = "VidSrc"
private val absoluteUrl = "https://v2.vidsrc.me"
override val mainUrl = "$absoluteUrl/embed"
override val requiresReferer = false
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val iframedoc = app.get(url).document
val serverslist =
iframedoc.select("div#sources.button_content div#content div#list div").map {
val datahash = it.attr("data-hash")
if (datahash.isNotBlank()) {
val links = try {
app.get("$absoluteUrl/src/$datahash", referer = "https://source.vidsrc.me/").url
} catch (e: Exception) {
""
}
links
} else ""
}
serverslist.apmap { server ->
val linkfixed = server.replace("https://vidsrc.xyz/", "https://embedsito.com/")
if (linkfixed.contains("/pro")) {
val srcresponse = app.get(server, referer = absoluteUrl).text
val m3u8Regex = Regex("((https:|http:)//.*\\.m3u8)")
val srcm3u8 = m3u8Regex.find(srcresponse)?.value ?: return@apmap
M3u8Helper.generateM3u8(
name,
srcm3u8,
absoluteUrl
).forEach(callback)
} else {
loadExtractor(linkfixed, url, subtitleCallback, callback)
}
}
}
}

View file

@ -1,51 +0,0 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
open class VoeExtractor : ExtractorApi() {
override val name: String = "Voe"
override val mainUrl: String = "https://voe.sx"
override val requiresReferer = false
private data class ResponseLinks(
@JsonProperty("hls") val url: String?,
@JsonProperty("video_height") val label: Int?
//val type: String // Mp4
)
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink> {
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
val doc = app.get(url).text
if (doc.isNotBlank()) {
val start = "const sources ="
var src = doc.substring(doc.indexOf(start))
src = src.substring(start.length, src.indexOf(";"))
.replace("0,", "0")
.trim()
//Log.i(this.name, "Result => (src) ${src}")
parseJson<ResponseLinks?>(src)?.let { voelink ->
//Log.i(this.name, "Result => (voelink) ${voelink}")
val linkUrl = voelink.url
val linkLabel = voelink.label?.toString() ?: ""
if (!linkUrl.isNullOrEmpty()) {
extractedLinksList.add(
ExtractorLink(
name = this.name,
source = this.name,
url = linkUrl,
quality = getQualityFromName(linkLabel),
referer = url,
isM3u8 = true
)
)
}
}
}
return extractedLinksList
}
}

View file

@ -1,30 +0,0 @@
package com.lagradost.cloudstream3.metaproviders
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
import com.lagradost.cloudstream3.utils.SyncUtil
object SyncRedirector {
val syncApis = SyncApis
suspend fun redirect(url: String, preferredUrl: String): String {
for (api in syncApis) {
if (url.contains(api.mainUrl)) {
val otherApi = when (api.name) {
aniListApi.name -> "anilist"
malApi.name -> "myanimelist"
else -> return url
}
return SyncUtil.getUrlsFromId(api.getIdFromUrl(url), otherApi).firstOrNull { realUrl ->
realUrl.contains(preferredUrl)
} ?: run {
throw ErrorLoadingException("Page does not exist on $preferredUrl")
}
}
}
return url
}
}

View file

@ -21,10 +21,11 @@ class CrossTmdbProvider : TmdbProvider() {
return Regex("""[^a-zA-Z0-9-]""").replace(name, "")
}
private val validApis by lazy {
apis.filter { it.lang == this.lang && it::class.java != this::class.java }
//.distinctBy { it.uniqueId }
}
private val validApis
get() =
synchronized(apis) { apis.filter { it.lang == this.lang && it::class.java != this::class.java } }
//.distinctBy { it.uniqueId }
data class CrossMetaData(
@JsonProperty("isSuccess") val isSuccess: Boolean,
@ -39,7 +40,7 @@ class CrossTmdbProvider : TmdbProvider() {
): Boolean {
tryParseJson<CrossMetaData>(data)?.let { metaData ->
if (!metaData.isSuccess) return false
metaData.movies?.apmap { (apiName, data) ->
metaData.movies?.amap { (apiName, data) ->
getApiFromNameNull(apiName)?.let {
try {
it.loadLinks(data, isCasting, subtitleCallback, callback)
@ -60,14 +61,15 @@ class CrossTmdbProvider : TmdbProvider() {
override suspend fun load(url: String): LoadResponse? {
val base = super.load(url)?.apply {
this.recommendations = this.recommendations?.filterIsInstance<MovieSearchResponse>() // TODO REMOVE
this.recommendations =
this.recommendations?.filterIsInstance<MovieSearchResponse>() // TODO REMOVE
val matchName = filterName(this.name)
when (this) {
is MovieLoadResponse -> {
val data = validApis.apmap { api ->
val data = validApis.amap { api ->
try {
if (api.supportedTypes.contains(TvType.Movie)) { //|| api.supportedTypes.contains(TvType.AnimeMovie)
return@apmap api.search(this.name)?.first {
return@amap api.search(this.name)?.first {
if (filterName(it.name).equals(
matchName,
ignoreCase = true
@ -98,6 +100,7 @@ class CrossTmdbProvider : TmdbProvider() {
this.dataUrl =
CrossMetaData(true, data.map { it.apiName to it.dataUrl }).toJson()
}
else -> {
throw ErrorLoadingException("Nothing besides movies are implemented for this provider")
}

View file

@ -1,70 +0,0 @@
package com.lagradost.cloudstream3.metaproviders
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.providers.AniListApi
import com.lagradost.cloudstream3.syncproviders.providers.MALApi
import com.lagradost.cloudstream3.utils.SyncUtil
// wont be implemented
class MultiAnimeProvider : MainAPI() {
override var name = "MultiAnime"
override var lang = "en"
override val usesWebView = true
override val supportedTypes = setOf(TvType.Anime)
private val syncApi: SyncAPI = aniListApi
private val syncUtilType by lazy {
when (syncApi) {
is AniListApi -> "anilist"
is MALApi -> "myanimelist"
else -> throw ErrorLoadingException("Invalid Api")
}
}
private val validApis by lazy {
APIHolder.apis.filter {
it.lang == this.lang && it::class.java != this::class.java && it.supportedTypes.contains(
TvType.Anime
)
}
}
private fun filterName(name: String): String {
return Regex("""[^a-zA-Z0-9-]""").replace(name, "")
}
override suspend fun search(query: String): List<SearchResponse>? {
return syncApi.search(query)?.map {
AnimeSearchResponse(it.name, it.url, this.name, TvType.Anime, it.posterUrl)
}
}
override suspend fun load(url: String): LoadResponse? {
return syncApi.getResult(url)?.let { res ->
val data = SyncUtil.getUrlsFromId(res.id, syncUtilType).apmap { url ->
validApis.firstOrNull { api -> url.startsWith(api.mainUrl) }?.load(url)
}.filterNotNull()
val type =
if (data.any { it.type == TvType.AnimeMovie }) TvType.AnimeMovie else TvType.Anime
newAnimeLoadResponse(
res.title ?: throw ErrorLoadingException("No Title found"),
url,
type
) {
posterUrl = res.posterUrl
plot = res.synopsis
tags = res.genres
rating = res.publicScore
addTrailer(res.trailers)
addAniListId(res.id.toIntOrNull())
recommendations = res.recommendations
}
}
}
}

View file

@ -0,0 +1,54 @@
package com.lagradost.cloudstream3.metaproviders
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.syncproviders.SyncIdName
object SyncRedirector {
private val syncIds =
listOf(
SyncIdName.MyAnimeList to Regex("""myanimelist\.net/anime/(\d+)"""),
SyncIdName.Anilist to Regex("""anilist\.co/anime/(\d+)""")
)
suspend fun redirect(
url: String,
providerApi: MainAPI
): String {
// Deprecated since providers should do this instead!
// Tries built in ID -> ProviderUrl
/*
for (api in syncApis) {
if (url.contains(api.mainUrl)) {
val otherApi = when (api.name) {
aniListApi.name -> "anilist"
malApi.name -> "myanimelist"
else -> return url
}
SyncUtil.getUrlsFromId(api.getIdFromUrl(url), otherApi).firstOrNull { realUrl ->
realUrl.contains(providerApi.mainUrl)
}?.let {
return it
}
// ?: run {
// throw ErrorLoadingException("Page does not exist on $preferredUrl")
// }
}
}
*/
// Tries provider solution
// This goes through all sync ids and finds supported id by said provider
return syncIds.firstNotNullOfOrNull { (syncName, syncRegex) ->
if (providerApi.supportedSyncNames.contains(syncName)) {
syncRegex.find(url)?.value?.let {
suspendSafeApiCall {
providerApi.getLoadUrl(syncName, it)
}
}
} else null
} ?: url
}
}

View file

@ -105,6 +105,7 @@ open class TmdbProvider : MainAPI() {
this.id,
episode.episode_number,
episode.season_number,
this.name ?: this.original_name,
).toJson(),
episode.name,
episode.season_number,
@ -122,6 +123,7 @@ open class TmdbProvider : MainAPI() {
this.id,
episodeNum,
season.season_number,
this.name ?: this.original_name,
).toJson(),
season = season.season_number
)
@ -151,6 +153,8 @@ open class TmdbProvider : MainAPI() {
recommendations = (this@toLoadResponse.recommendations
?: this@toLoadResponse.similar)?.results?.map { it.toSearchResponse() }
addActors(credits?.cast?.toList().toActors())
contentRating = fetchContentRating(id, "US")
}
}
@ -193,6 +197,8 @@ open class TmdbProvider : MainAPI() {
recommendations = (this@toLoadResponse.recommendations
?: this@toLoadResponse.similar)?.results?.map { it.toSearchResponse() }
addActors(credits?.cast?.toList().toActors())
contentRating = fetchContentRating(id, "US")
}
}
@ -264,6 +270,26 @@ open class TmdbProvider : MainAPI() {
return null
}
open suspend fun fetchContentRating(id: Int?, country: String): String? {
id ?: return null
val contentRatings = tmdb.tvService().content_ratings(id).awaitResponse().body()?.results
return if (!contentRatings.isNullOrEmpty()) {
contentRatings.firstOrNull { it: ContentRating ->
it.iso_3166_1 == country
}?.rating
} else {
val releaseDates = tmdb.moviesService().releaseDates(id).awaitResponse().body()?.results
val certification = releaseDates?.firstOrNull { it: ReleaseDatesResult ->
it.iso_3166_1 == country
}?.release_dates?.firstOrNull { it: ReleaseDate ->
!it.certification.isNullOrBlank()
}?.certification
certification
}
}
// Possible to add recommendations and such here.
override suspend fun load(url: String): LoadResponse? {
// https://www.themoviedb.org/movie/7445-brothers

View file

@ -0,0 +1,471 @@
package com.lagradost.cloudstream3.metaproviders
import android.net.Uri
import com.fasterxml.jackson.annotation.JsonAlias
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.Actor
import com.lagradost.cloudstream3.ActorData
import com.lagradost.cloudstream3.Episode
import com.lagradost.cloudstream3.HomePageResponse
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTMDbId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.MainPageRequest
import com.lagradost.cloudstream3.NextAiring
import com.lagradost.cloudstream3.ProviderType
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.ShowStatus
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.addDate
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.base64Decode
import com.lagradost.cloudstream3.mainPageOf
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.newHomePageResponse
import com.lagradost.cloudstream3.newMovieLoadResponse
import com.lagradost.cloudstream3.newMovieSearchResponse
import com.lagradost.cloudstream3.newTvSeriesLoadResponse
import com.lagradost.cloudstream3.newTvSeriesSearchResponse
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import java.text.SimpleDateFormat
import java.util.Locale
import kotlin.math.roundToInt
open class TraktProvider : MainAPI() {
override var name = "Trakt"
override val hasMainPage = true
override val providerType = ProviderType.MetaProvider
override val supportedTypes = setOf(
TvType.Movie,
TvType.TvSeries,
TvType.Anime,
)
private val traktClientId =
base64Decode("N2YzODYwYWQzNGI4ZTZmOTdmN2I5MTA0ZWQzMzEwOGI0MmQ3MTdlMTM0MmM2NGMxMTg5NGE1MjUyYTQ3NjE3Zg==")
private val traktApiUrl = base64Decode("aHR0cHM6Ly9hcGl6LnRyYWt0LnR2")
override val mainPage = mainPageOf(
"$traktApiUrl/movies/trending" to "Trending Movies", //Most watched movies right now
"$traktApiUrl/movies/popular" to "Popular Movies", //The most popular movies for all time
"$traktApiUrl/shows/trending" to "Trending Shows", //Most watched Shows right now
"$traktApiUrl/shows/popular" to "Popular Shows", //The most popular Shows for all time
)
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val apiResponse = getApi("${request.data}?extended=cloud9,full&page=$page")
val results = parseJson<List<MediaDetails>>(apiResponse).map { element ->
element.toSearchResponse()
}
return newHomePageResponse(request.name, results)
}
private fun MediaDetails.toSearchResponse(): SearchResponse {
val media = this.media ?: this
val mediaType = if (media.ids?.tvdb == null) TvType.Movie else TvType.TvSeries
val poster = media.images?.poster?.firstOrNull()
if (mediaType == TvType.Movie) {
return newMovieSearchResponse(
name = media.title!!,
url = Data(
type = mediaType,
mediaDetails = media,
).toJson(),
type = TvType.Movie,
) {
posterUrl = fixPath(poster)
}
} else {
return newTvSeriesSearchResponse(
name = media.title!!,
url = Data(
type = mediaType,
mediaDetails = media,
).toJson(),
type = TvType.TvSeries,
) {
this.posterUrl = fixPath(poster)
}
}
}
override suspend fun search(query: String): List<SearchResponse>? {
val apiResponse =
getApi("$traktApiUrl/search/movie,show?extended=cloud9,full&limit=20&page=1&query=$query")
val results = parseJson<List<MediaDetails>>(apiResponse).map { element ->
element.toSearchResponse()
}
return results
}
override suspend fun load(url: String): LoadResponse {
val data = parseJson<Data>(url)
val mediaDetails = data.mediaDetails
val moviesOrShows = if (data.type == TvType.Movie) "movies" else "shows"
val posterUrl = mediaDetails?.images?.poster?.firstOrNull()
val backDropUrl = mediaDetails?.images?.fanart?.firstOrNull()
val resActor =
getApi("$traktApiUrl/$moviesOrShows/${mediaDetails?.ids?.trakt}/people?extended=cloud9,full")
val actors = parseJson<People>(resActor).cast?.map {
ActorData(
Actor(
name = it.person?.name!!,
image = getWidthImageUrl(it.person.images?.headshot?.firstOrNull(), "w500")
),
roleString = it.character
)
}
val resRelated =
getApi("$traktApiUrl/$moviesOrShows/${mediaDetails?.ids?.trakt}/related?extended=cloud9,full&limit=20")
val relatedMedia = parseJson<List<MediaDetails>>(resRelated).map { it.toSearchResponse() }
val isCartoon =
mediaDetails?.genres?.contains("animation") == true || mediaDetails?.genres?.contains("anime") == true
val isAnime =
isCartoon && (mediaDetails?.language == "zh" || mediaDetails?.language == "ja")
val isAsian = !isAnime && (mediaDetails?.language == "zh" || mediaDetails?.language == "ko")
val isBollywood = mediaDetails?.country == "in"
if (data.type == TvType.Movie) {
val linkData = LinkData(
id = mediaDetails?.ids?.tmdb,
traktId = mediaDetails?.ids?.trakt,
traktSlug = mediaDetails?.ids?.slug,
tmdbId = mediaDetails?.ids?.tmdb,
imdbId = mediaDetails?.ids?.imdb.toString(),
tvdbId = mediaDetails?.ids?.tvdb,
tvrageId = mediaDetails?.ids?.tvrage,
type = data.type.toString(),
title = mediaDetails?.title,
year = mediaDetails?.year,
orgTitle = mediaDetails?.title,
isAnime = isAnime,
//jpTitle = later if needed as it requires another network request,
airedDate = mediaDetails?.released
?: mediaDetails?.firstAired,
isAsian = isAsian,
isBollywood = isBollywood,
).toJson()
return newMovieLoadResponse(
name = mediaDetails?.title!!,
url = data.toJson(),
dataUrl = linkData.toJson(),
type = if (isAnime) TvType.AnimeMovie else TvType.Movie,
) {
this.name = mediaDetails.title
this.type = if (isAnime) TvType.AnimeMovie else TvType.Movie
this.posterUrl = getOriginalWidthImageUrl(posterUrl)
this.year = mediaDetails.year
this.plot = mediaDetails.overview
this.rating = mediaDetails.rating?.times(1000)?.roundToInt()
this.tags = mediaDetails.genres
this.duration = mediaDetails.runtime
this.recommendations = relatedMedia
this.actors = actors
this.comingSoon = isUpcoming(mediaDetails.released)
//posterHeaders
this.backgroundPosterUrl = getOriginalWidthImageUrl(backDropUrl)
this.contentRating = mediaDetails.certification
addTrailer(mediaDetails.trailer)
addImdbId(mediaDetails.ids?.imdb)
addTMDbId(mediaDetails.ids?.tmdb.toString())
}
} else {
val resSeasons =
getApi("$traktApiUrl/shows/${mediaDetails?.ids?.trakt.toString()}/seasons?extended=cloud9,full,episodes")
val episodes = mutableListOf<Episode>()
val seasons = parseJson<List<Seasons>>(resSeasons)
var nextAir: NextAiring? = null
seasons.forEach { season ->
season.episodes?.map { episode ->
val linkData = LinkData(
id = mediaDetails?.ids?.tmdb,
traktId = mediaDetails?.ids?.trakt,
traktSlug = mediaDetails?.ids?.slug,
tmdbId = mediaDetails?.ids?.tmdb,
imdbId = mediaDetails?.ids?.imdb.toString(),
tvdbId = mediaDetails?.ids?.tvdb,
tvrageId = mediaDetails?.ids?.tvrage,
type = data.type.toString(),
season = episode.season,
episode = episode.number,
title = mediaDetails?.title,
year = mediaDetails?.year,
orgTitle = mediaDetails?.title,
isAnime = isAnime,
airedYear = mediaDetails?.year,
lastSeason = seasons.size,
epsTitle = episode.title,
//jpTitle = later if needed as it requires another network request,
date = episode.firstAired,
airedDate = episode.firstAired,
isAsian = isAsian,
isBollywood = isBollywood,
isCartoon = isCartoon
).toJson()
episodes.add(
Episode(
data = linkData.toJson(),
name = episode.title,
season = episode.season,
episode = episode.number,
posterUrl = fixPath(episode.images?.screenshot?.firstOrNull()),
rating = episode.rating?.times(10)?.roundToInt(),
description = episode.overview,
runTime = episode.runtime
).apply {
this.addDate(episode.firstAired, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
if (nextAir == null && this.date != null && this.date!! > unixTimeMS && this.season != 0) {
nextAir = NextAiring(
episode = this.episode!!,
unixTime = this.date!!.div(1000L),
season = if (this.season == 1) null else this.season,
)
}
}
)
}
}
return newTvSeriesLoadResponse(
name = mediaDetails?.title!!,
url = data.toJson(),
type = if (isAnime) TvType.Anime else TvType.TvSeries,
episodes = episodes
) {
this.name = mediaDetails.title
this.type = if (isAnime) TvType.Anime else TvType.TvSeries
this.episodes = episodes
this.posterUrl = getOriginalWidthImageUrl(posterUrl)
this.year = mediaDetails.year
this.plot = mediaDetails.overview
this.showStatus = getStatus(mediaDetails.status)
this.rating = mediaDetails.rating?.times(1000)?.roundToInt()
this.tags = mediaDetails.genres
this.duration = mediaDetails.runtime
this.recommendations = relatedMedia
this.actors = actors
this.comingSoon = isUpcoming(mediaDetails.released)
//posterHeaders
this.nextAiring = nextAir
this.backgroundPosterUrl = getOriginalWidthImageUrl(backDropUrl)
this.contentRating = mediaDetails.certification
addTrailer(mediaDetails.trailer)
addImdbId(mediaDetails.ids?.imdb)
addTMDbId(mediaDetails.ids?.tmdb.toString())
}
}
}
private suspend fun getApi(url: String): String {
return app.get(
url = url,
headers = mapOf(
"Content-Type" to "application/json",
"trakt-api-version" to "2",
"trakt-api-key" to traktClientId,
)
).toString()
}
private fun isUpcoming(dateString: String?): Boolean {
return try {
val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val dateTime = dateString?.let { format.parse(it)?.time } ?: return false
unixTimeMS < dateTime
} catch (t: Throwable) {
logError(t)
false
}
}
private fun getStatus(t: String?): ShowStatus {
return when (t) {
"returning series" -> ShowStatus.Ongoing
"continuing" -> ShowStatus.Ongoing
else -> ShowStatus.Completed
}
}
private fun fixPath(url: String?): String? {
url ?: return null
return "https://$url"
}
private fun getWidthImageUrl(path: String?, width: String): String? {
if (path == null) return null
if (!path.contains("image.tmdb.org")) return fixPath(path)
val fileName = Uri.parse(path).lastPathSegment ?: return null
return "https://image.tmdb.org/t/p/${width}/${fileName}"
}
private fun getOriginalWidthImageUrl(path: String?): String? {
if (path == null) return null
if (!path.contains("image.tmdb.org")) return fixPath(path)
return getWidthImageUrl(path, "original")
}
data class Data(
val type: TvType? = null,
val mediaDetails: MediaDetails? = null,
)
data class MediaDetails(
@JsonProperty("title") val title: String? = null,
@JsonProperty("year") val year: Int? = null,
@JsonProperty("ids") val ids: Ids? = null,
@JsonProperty("tagline") val tagline: String? = null,
@JsonProperty("overview") val overview: String? = null,
@JsonProperty("released") val released: String? = null,
@JsonProperty("runtime") val runtime: Int? = null,
@JsonProperty("country") val country: String? = null,
@JsonProperty("updatedAt") val updatedAt: String? = null,
@JsonProperty("trailer") val trailer: String? = null,
@JsonProperty("homepage") val homepage: String? = null,
@JsonProperty("status") val status: String? = null,
@JsonProperty("rating") val rating: Double? = null,
@JsonProperty("votes") val votes: Long? = null,
@JsonProperty("comment_count") val commentCount: Long? = null,
@JsonProperty("language") val language: String? = null,
@JsonProperty("languages") val languages: List<String>? = null,
@JsonProperty("available_translations") val availableTranslations: List<String>? = null,
@JsonProperty("genres") val genres: List<String>? = null,
@JsonProperty("certification") val certification: String? = null,
@JsonProperty("aired_episodes") val airedEpisodes: Int? = null,
@JsonProperty("first_aired") val firstAired: String? = null,
@JsonProperty("airs") val airs: Airs? = null,
@JsonProperty("network") val network: String? = null,
@JsonProperty("images") val images: Images? = null,
@JsonProperty("movie") @JsonAlias("show") val media: MediaDetails? = null
)
data class Airs(
@JsonProperty("day") val day: String? = null,
@JsonProperty("time") val time: String? = null,
@JsonProperty("timezone") val timezone: String? = null,
)
data class Ids(
@JsonProperty("trakt") val trakt: Int? = null,
@JsonProperty("slug") val slug: String? = null,
@JsonProperty("tvdb") val tvdb: Int? = null,
@JsonProperty("imdb") val imdb: String? = null,
@JsonProperty("tmdb") val tmdb: Int? = null,
@JsonProperty("tvrage") val tvrage: String? = null,
)
data class Images(
@JsonProperty("fanart") val fanart: List<String>? = null,
@JsonProperty("poster") val poster: List<String>? = null,
@JsonProperty("logo") val logo: List<String>? = null,
@JsonProperty("clearart") val clearart: List<String>? = null,
@JsonProperty("banner") val banner: List<String>? = null,
@JsonProperty("thumb") val thumb: List<String>? = null,
@JsonProperty("screenshot") val screenshot: List<String>? = null,
@JsonProperty("headshot") val headshot: List<String>? = null,
)
data class People(
@JsonProperty("cast") val cast: List<Cast>? = null,
)
data class Cast(
@JsonProperty("character") val character: String? = null,
@JsonProperty("characters") val characters: List<String>? = null,
@JsonProperty("episode_count") val episodeCount: Long? = null,
@JsonProperty("person") val person: Person? = null,
@JsonProperty("images") val images: Images? = null,
)
data class Person(
@JsonProperty("name") val name: String? = null,
@JsonProperty("ids") val ids: Ids? = null,
@JsonProperty("images") val images: Images? = null,
)
data class Seasons(
@JsonProperty("aired_episodes") val airedEpisodes: Int? = null,
@JsonProperty("episode_count") val episodeCount: Int? = null,
@JsonProperty("episodes") val episodes: List<TraktEpisode>? = null,
@JsonProperty("first_aired") val firstAired: String? = null,
@JsonProperty("ids") val ids: Ids? = null,
@JsonProperty("images") val images: Images? = null,
@JsonProperty("network") val network: String? = null,
@JsonProperty("number") val number: Int? = null,
@JsonProperty("overview") val overview: String? = null,
@JsonProperty("rating") val rating: Double? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("updated_at") val updatedAt: String? = null,
@JsonProperty("votes") val votes: Int? = null,
)
data class TraktEpisode(
@JsonProperty("available_translations") val availableTranslations: List<String>? = null,
@JsonProperty("comment_count") val commentCount: Int? = null,
@JsonProperty("episode_type") val episodeType: String? = null,
@JsonProperty("first_aired") val firstAired: String? = null,
@JsonProperty("ids") val ids: Ids? = null,
@JsonProperty("images") val images: Images? = null,
@JsonProperty("number") val number: Int? = null,
@JsonProperty("number_abs") val numberAbs: Int? = null,
@JsonProperty("overview") val overview: String? = null,
@JsonProperty("rating") val rating: Double? = null,
@JsonProperty("runtime") val runtime: Int? = null,
@JsonProperty("season") val season: Int? = null,
@JsonProperty("title") val title: String? = null,
@JsonProperty("updated_at") val updatedAt: String? = null,
@JsonProperty("votes") val votes: Int? = null,
)
data class LinkData(
val id: Int? = null,
val traktId: Int? = null,
val traktSlug: String? = null,
val tmdbId: Int? = null,
val imdbId: String? = null,
val tvdbId: Int? = null,
val tvrageId: String? = null,
val type: String? = null,
val season: Int? = null,
val episode: Int? = null,
val aniId: String? = null,
val animeId: String? = null,
val title: String? = null,
val year: Int? = null,
val orgTitle: String? = null,
val isAnime: Boolean = false,
val airedYear: Int? = null,
val lastSeason: Int? = null,
val epsTitle: String? = null,
val jpTitle: String? = null,
val date: String? = null,
val airedDate: String? = null,
val isAsian: Boolean = false,
val isBollywood: Boolean = false,
val isCartoon: Boolean = false,
)
}

View file

@ -1,202 +0,0 @@
package com.lagradost.cloudstream3.mvvm
import android.util.Log
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import com.bumptech.glide.load.HttpException
import com.lagradost.cloudstream3.BuildConfig
import com.lagradost.cloudstream3.ErrorLoadingException
import kotlinx.coroutines.*
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import javax.net.ssl.SSLHandshakeException
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
const val DEBUG_EXCEPTION = "THIS IS A DEBUG EXCEPTION!"
class DebugException(message: String) : Exception("$DEBUG_EXCEPTION\n$message")
inline fun debugException(message: () -> String) {
if (BuildConfig.DEBUG) {
throw DebugException(message.invoke())
}
}
inline fun debugWarning(message: () -> String) {
if (BuildConfig.DEBUG) {
logError(DebugException(message.invoke()))
}
}
inline fun debugAssert(assert: () -> Boolean, message: () -> String) {
if (BuildConfig.DEBUG && assert.invoke()) {
throw DebugException(message.invoke())
}
}
inline fun debugWarning(assert: () -> Boolean, message: () -> String) {
if (BuildConfig.DEBUG && assert.invoke()) {
logError(DebugException(message.invoke()))
}
}
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, action: (t: T) -> Unit) {
liveData.observe(this) { it?.let { t -> action(t) } }
}
inline fun <reified T : Any> some(value: T?): Some<T> {
return if (value == null) {
Some.None
} else {
Some.Success(value)
}
}
sealed class Some<out T> {
data class Success<out T>(val value: T) : Some<T>()
object None : Some<Nothing>()
override fun toString(): String {
return when (this) {
is None -> "None"
is Success -> "Some(${value.toString()})"
}
}
}
sealed class ResourceSome<out T> {
data class Success<out T>(val value: T) : ResourceSome<T>()
object None : ResourceSome<Nothing>()
data class Loading(val data: Any? = null) : ResourceSome<Nothing>()
}
sealed class Resource<out T> {
data class Success<out T>(val value: T) : Resource<T>()
data class Failure(
val isNetworkError: Boolean,
val errorCode: Int?,
val errorResponse: Any?, //ResponseBody
val errorString: String,
) : Resource<Nothing>()
data class Loading(val url: String? = null) : Resource<Nothing>()
}
fun logError(throwable: Throwable) {
Log.d("ApiError", "-------------------------------------------------------------------")
Log.d("ApiError", "safeApiCall: " + throwable.localizedMessage)
Log.d("ApiError", "safeApiCall: " + throwable.message)
throwable.printStackTrace()
Log.d("ApiError", "-------------------------------------------------------------------")
}
fun <T> normalSafeApiCall(apiCall: () -> T): T? {
return try {
apiCall.invoke()
} catch (throwable: Throwable) {
logError(throwable)
return null
}
}
suspend fun <T> suspendSafeApiCall(apiCall: suspend () -> T): T? {
return try {
apiCall.invoke()
} catch (throwable: Throwable) {
logError(throwable)
return null
}
}
fun <T> safeFail(throwable: Throwable): Resource<T> {
val stackTraceMsg =
(throwable.localizedMessage ?: "") + "\n\n" + throwable.stackTrace.joinToString(
separator = "\n"
) {
"${it.fileName} ${it.lineNumber}"
}
return Resource.Failure(false, null, null, stackTraceMsg)
}
fun CoroutineScope.launchSafe(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val obj: suspend CoroutineScope.() -> Unit = {
try {
block()
} catch (e: Exception) {
logError(e)
}
}
return this.launch(context, start, obj)
}
suspend fun <T> safeApiCall(
apiCall: suspend () -> T,
): Resource<T> {
return withContext(Dispatchers.IO) {
try {
Resource.Success(apiCall.invoke())
} catch (throwable: Throwable) {
logError(throwable)
when (throwable) {
is NullPointerException -> {
for (line in throwable.stackTrace) {
if (line?.fileName?.endsWith("provider.kt", ignoreCase = true) == true) {
return@withContext Resource.Failure(
false,
null,
null,
"NullPointerException at ${line.fileName} ${line.lineNumber}\nSite might have updated or added Cloudflare/DDOS protection"
)
}
}
safeFail(throwable)
}
is SocketTimeoutException -> {
Resource.Failure(
true,
null,
null,
"Connection Timeout\nPlease try again later."
)
}
is HttpException -> {
Resource.Failure(
false,
throwable.statusCode,
null,
throwable.message ?: "HttpException"
)
}
is UnknownHostException -> {
Resource.Failure(true, null, null, "Cannot connect to server, try again later.")
}
is ErrorLoadingException -> {
Resource.Failure(
true,
null,
null,
throwable.message ?: "Error loading, try again later."
)
}
is NotImplementedError -> {
Resource.Failure(false, null, null, "This operation is not implemented.")
}
is SSLHandshakeException -> {
Resource.Failure(
true,
null,
null,
(throwable.message ?: "SSLHandshakeException") + "\nTry again later."
)
}
else -> safeFail(throwable)
}
}
}
}

View file

@ -0,0 +1,16 @@
package com.lagradost.cloudstream3.mvvm
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
/** NOTE: Only one observer at a time per value */
fun <T> LifecycleOwner.observe(liveData: LiveData<T>, action: (t: T) -> Unit) {
liveData.removeObservers(this)
liveData.observe(this) { it?.let { t -> action(t) } }
}
/** NOTE: Only one observer at a time per value */
fun <T> LifecycleOwner.observeNullable(liveData: LiveData<T>, action: (t: T) -> Unit) {
liveData.removeObservers(this)
liveData.observe(this) { action(it) }
}

View file

@ -5,16 +5,23 @@ import android.webkit.CookieManager
import androidx.annotation.AnyThread
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.debugWarning
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.nicehttp.Requests.Companion.await
import com.lagradost.nicehttp.cookies
import kotlinx.coroutines.runBlocking
import okhttp3.*
import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import java.net.URI
@AnyThread
class CloudflareKiller : Interceptor {
companion object {
const val TAG = "CloudflareKiller"
private val ERROR_CODES = listOf(403, 503)
private val CLOUDFLARE_SERVERS = listOf("cloudflare-nginx", "cloudflare")
fun parseCookieMap(cookie: String): Map<String, String> {
return cookie.split(";").associate {
val split = it.split("=")
@ -23,22 +30,56 @@ class CloudflareKiller : Interceptor {
}
}
val savedCookies: MutableMap<String, String> = mutableMapOf()
init {
// Needs to clear cookies between sessions to generate new cookies.
normalSafeApiCall {
// This can throw an exception on unsupported devices :(
CookieManager.getInstance().removeAllCookies(null)
}
}
val savedCookies: MutableMap<String, Map<String, String>> = mutableMapOf()
/**
* Gets the headers with cookies, webview user agent included!
* */
fun getCookieHeaders(url: String): Headers {
val userAgentHeaders = WebViewResolver.webViewUserAgent?.let {
mapOf("user-agent" to it)
} ?: emptyMap()
return getHeaders(userAgentHeaders, savedCookies[URI(url).host] ?: emptyMap())
}
override fun intercept(chain: Interceptor.Chain): Response = runBlocking {
val request = chain.request()
if (savedCookies[request.url.host] == null) {
bypassCloudflare(request)?.let {
Log.d(TAG, "Succeeded bypassing cloudflare: ${request.url}")
return@runBlocking it
when (val cookies = savedCookies[request.url.host]) {
null -> {
val response = chain.proceed(request)
if(!(response.header("Server") in CLOUDFLARE_SERVERS && response.code in ERROR_CODES)) {
return@runBlocking response
} else {
response.close()
bypassCloudflare(request)?.let {
Log.d(TAG, "Succeeded bypassing cloudflare: ${request.url}")
return@runBlocking it
}
}
}
else -> {
return@runBlocking proceed(request, cookies)
}
}
debugWarning({ true }) { "Failed cloudflare at: ${request.url}" }
return@runBlocking chain.proceed(request)
}
private fun getWebViewCookie(url: String): String? {
return CookieManager.getInstance()?.getCookie(url)
return normalSafeApiCall {
CookieManager.getInstance()?.getCookie(url)
}
}
/**
@ -49,11 +90,25 @@ class CloudflareKiller : Interceptor {
// Not sure if this takes expiration into account
return getWebViewCookie(request.url.toString())?.let { cookie ->
cookie.contains("cf_clearance").also { solved ->
if (solved) savedCookies[request.url.host] = cookie
if (solved) savedCookies[request.url.host] = parseCookieMap(cookie)
}
} ?: false
}
private suspend fun proceed(request: Request, cookies: Map<String, String>): Response {
val userAgentMap = WebViewResolver.getWebViewUserAgent()?.let {
mapOf("user-agent" to it)
} ?: emptyMap()
val headers =
getHeaders(request.headers.toMap() + userAgentMap, cookies + request.cookies)
return app.baseClient.newCall(
request.newBuilder()
.headers(headers)
.build()
).await()
}
private suspend fun bypassCloudflare(request: Request): Response? {
val url = request.url.toString()
@ -78,17 +133,6 @@ class CloudflareKiller : Interceptor {
}
val cookies = savedCookies[request.url.host] ?: return null
val mappedCookies = parseCookieMap(cookies)
val userAgentMap = WebViewResolver.getWebViewUserAgent()?.let {
mapOf("user-agent" to it)
} ?: emptyMap()
val headers = getHeaders(request.headers.toMap() + userAgentMap, mappedCookies + request.cookies)
return app.baseClient.newCall(
request.newBuilder()
.headers(headers)
.build()
).await()
return proceed(request, cookies)
}
}

View file

@ -2,7 +2,7 @@ package com.lagradost.cloudstream3.network
import androidx.annotation.AnyThread
import com.lagradost.cloudstream3.app
import com.lagradost.nicehttp.Requests.Companion.await
import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.cookies
import kotlinx.coroutines.runBlocking
import okhttp3.Interceptor
@ -41,7 +41,8 @@ class DdosGuardKiller(private val alwaysBypass: Boolean) : Interceptor {
savedCookiesMap[request.url.host]
// If no cookies are found fetch and save em.
?: (request.url.scheme + "://" + request.url.host + (ddosBypassPath ?: "")).let {
app.get(it, cacheTime = 0).cookies.also { cookies ->
// Somehow app.get fails
Requests().get(it).cookies.also { cookies ->
savedCookiesMap[request.url.host] = cookies
}
}
@ -51,6 +52,6 @@ class DdosGuardKiller(private val alwaysBypass: Boolean) : Interceptor {
request.newBuilder()
.headers(headers)
.build()
).await()
).execute()
}
}

View file

@ -64,4 +64,24 @@ fun OkHttpClient.Builder.addAdGuardDns() = (
"94.140.14.140",
"94.140.14.141",
)
))
))
fun OkHttpClient.Builder.addDNSWatchDns() = (
addGenericDns(
"https://resolver2.dns.watch/dns-query",
// https://dns.watch/
listOf(
"84.200.69.80",
"84.200.70.40",
)
))
fun OkHttpClient.Builder.addQuad9Dns() = (
addGenericDns(
"https://dns.quad9.net/dns-query",
// https://www.quad9.net/service/service-addresses-and-features
listOf(
"9.9.9.9",
"149.112.112.112",
)
))

View file

@ -4,19 +4,19 @@ import android.content.Context
import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.USER_AGENT
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.getCookies
import com.lagradost.nicehttp.ignoreAllSSLErrors
import okhttp3.Cache
import okhttp3.Headers
import okhttp3.Headers.Companion.toHeaders
import okhttp3.OkHttpClient
import okhttp3.Request
import org.conscrypt.Conscrypt
import java.io.File
import java.util.concurrent.TimeUnit
import java.security.Security
fun Requests.initClient(context: Context): OkHttpClient {
normalSafeApiCall { Security.insertProviderAt(Conscrypt.newProvider(), 1) }
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
val dns = settingsManager.getInt(context.getString(R.string.dns_pref), 0)
baseClient = OkHttpClient.Builder()
@ -36,6 +36,8 @@ fun Requests.initClient(context: Context): OkHttpClient {
2 -> addCloudFlareDns()
// 3 -> addOpenDns()
4 -> addAdGuardDns()
5 -> addDNSWatchDns()
6 -> addQuad9Dns()
}
}
// Needs to be build as otherwise the other builders will change this object

View file

@ -2,5 +2,4 @@ package com.lagradost.cloudstream3.plugins
@Suppress("unused")
@Target(AnnotationTarget.CLASS)
annotation class CloudstreamPlugin(
)
annotation class CloudstreamPlugin

View file

@ -34,9 +34,11 @@ abstract class Plugin {
*/
fun registerMainAPI(element: MainAPI) {
Log.i(PLUGIN_TAG, "Adding ${element.name} (${element.mainUrl}) MainAPI")
element.sourcePlugin = this.__filename
element.sourcePlugin = this.filename
// Race condition causing which would case duplicates if not for distinctBy
APIHolder.allProviders.add(element)
synchronized(APIHolder.allProviders) {
APIHolder.allProviders.add(element)
}
APIHolder.addPluginMapping(element)
}
@ -46,22 +48,31 @@ abstract class Plugin {
*/
fun registerExtractorAPI(element: ExtractorApi) {
Log.i(PLUGIN_TAG, "Adding ${element.name} (${element.mainUrl}) ExtractorApi")
element.sourcePlugin = this.__filename
element.sourcePlugin = this.filename
extractorApis.add(element)
}
class Manifest {
@JsonProperty("name") var name: String? = null
@JsonProperty("pluginClassName") var pluginClassName: String? = null
@JsonProperty("version") var version: Int? = null
@JsonProperty("requiresResources") var requiresResources: Boolean = false
@JsonProperty("name")
var name: String? = null
@JsonProperty("pluginClassName")
var pluginClassName: String? = null
@JsonProperty("version")
var version: Int? = null
@JsonProperty("requiresResources")
var requiresResources: Boolean = false
}
/**
* This will contain your resources if you specified requiresResources in gradle
*/
var resources: Resources? = null
var __filename: String? = null
/** Full file path to the plugin. */
@Deprecated("Renamed to `filename` to follow conventions", replaceWith = ReplaceWith("filename"))
var __filename: String?
get() = filename
set(value) {filename = value}
var filename: String? = null
/**
* This will add a button in the settings allowing you to add custom settings

View file

@ -1,30 +1,47 @@
package com.lagradost.cloudstream3.plugins
import dalvik.system.PathClassLoader
import com.google.gson.Gson
import android.Manifest
import android.app.*
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.AssetManager
import android.content.res.Resources
import android.os.Build
import android.os.Environment
import android.widget.Toast
import android.app.Activity
import android.util.Log
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.fragment.app.FragmentActivity
import com.fasterxml.jackson.annotation.JsonProperty
import com.google.gson.Gson
import com.lagradost.cloudstream3.*
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.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.plugins.RepositoryManager.ONLINE_PLUGINS_FOLDER
import com.lagradost.cloudstream3.plugins.RepositoryManager.downloadPluginToFile
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.MainAPI.Companion.settingsForProvider
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.mvvm.debugPrint
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.plugins.RepositoryManager.ONLINE_PLUGINS_FOLDER
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
import com.lagradost.cloudstream3.plugins.RepositoryManager.downloadPluginToFile
import com.lagradost.cloudstream3.plugins.RepositoryManager.getRepoPlugins
import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.ui.settings.extensions.REPOSITORIES_KEY
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
import com.lagradost.cloudstream3.APIHolder.removePluginMapping
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
import com.lagradost.cloudstream3.utils.AppContextUtils.getApiProviderLangSettings
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
import com.lagradost.cloudstream3.utils.extractorApis
import dalvik.system.PathClassLoader
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.io.File
@ -35,6 +52,9 @@ import java.util.*
const val PLUGINS_KEY = "PLUGINS_KEY"
const val PLUGINS_KEY_LOCAL = "PLUGINS_KEY_LOCAL"
const val EXTENSIONS_CHANNEL_ID = "cloudstream3.extensions"
const val EXTENSIONS_CHANNEL_NAME = "Extensions"
const val EXTENSIONS_CHANNEL_DESCRIPT = "Extension notification channel"
// Data class for internal storage
data class PluginData(
@ -75,6 +95,8 @@ object PluginManager {
const val TAG = "PluginManager"
private var hasCreatedNotChanel = false
/**
* Store data about the plugin for fetching later
* */
@ -109,10 +131,28 @@ object PluginManager {
val plugins = getPluginsOnline().filter {
!it.filePath.contains(repositoryPath)
}
val file = File(repositoryPath)
normalSafeApiCall {
if (file.exists()) file.deleteRecursively()
}
setKey(PLUGINS_KEY, plugins)
}
}
/**
* Deletes all generated oat files which will force Android to recompile the dex extensions.
* This might fix unrecoverable SIGSEGV exceptions when old oat files are loaded in a new app update.
*/
fun deleteAllOatFiles(context: Context) {
File("${context.filesDir}/${ONLINE_PLUGINS_FOLDER}").listFiles()?.forEach { repo ->
repo.listFiles { file -> file.name == "oat" && file.isDirectory }?.forEach { file ->
val success = file.deleteRecursively()
Log.i(TAG, "Deleted oat directory: ${file.absolutePath} Success=$success")
}
}
}
fun getPluginsOnline(): Array<PluginData> {
return getKey(PLUGINS_KEY) ?: emptyArray()
}
@ -121,11 +161,15 @@ object PluginManager {
return getKey(PLUGINS_KEY_LOCAL) ?: emptyArray()
}
private val LOCAL_PLUGINS_PATH =
Environment.getExternalStorageDirectory().absolutePath + "/Cloudstream3/plugins"
private val CLOUD_STREAM_FOLDER =
Environment.getExternalStorageDirectory().absolutePath + "/Cloudstream3/"
private val LOCAL_PLUGINS_PATH = CLOUD_STREAM_FOLDER + "plugins"
var currentlyLoading: String? = null
// Maps filepath to plugin
private val plugins: MutableMap<String, Plugin> =
val plugins: MutableMap<String, Plugin> =
LinkedHashMap<String, Plugin>()
// Maps urls to plugin
@ -135,14 +179,18 @@ object PluginManager {
private val classLoaders: MutableMap<PathClassLoader, Plugin> =
HashMap<PathClassLoader, Plugin>()
private var loadedLocalPlugins = false
var loadedLocalPlugins = false
private set
var loadedOnlinePlugins = false
private set
private val gson = Gson()
private suspend fun maybeLoadPlugin(activity: Activity, file: File) {
private suspend fun maybeLoadPlugin(context: Context, file: File) {
val name = file.name
if (file.extension == "zip" || file.extension == "cs3") {
loadPlugin(
activity,
context,
file,
PluginData(name, null, false, file.absolutePath, PLUGIN_VERSION_NOT_SET)
)
@ -158,18 +206,31 @@ object PluginManager {
val onlineData: Pair<String, SitePlugin>,
) {
val isOutdated =
onlineData.second.version != savedData.version || onlineData.second.version == PLUGIN_VERSION_ALWAYS_UPDATE
onlineData.second.version > savedData.version || onlineData.second.version == PLUGIN_VERSION_ALWAYS_UPDATE
val isDisabled = onlineData.second.status == PROVIDER_STATUS_DOWN
fun validOnlineData(context: Context): Boolean {
return getPluginPath(
context,
savedData.internalName,
onlineData.first
).absolutePath == savedData.filePath
}
}
var allCurrentOutDatedPlugins: Set<OnlinePluginData> = emptySet()
// var allCurrentOutDatedPlugins: Set<OnlinePluginData> = emptySet()
suspend fun loadSinglePlugin(activity: Activity, apiName: String): Boolean {
return (getPluginsOnline().firstOrNull { it.internalName == apiName }
?: getPluginsLocal().firstOrNull { it.internalName == apiName })?.let { savedData ->
suspend fun loadSinglePlugin(context: Context, apiName: String): Boolean {
return (getPluginsOnline().firstOrNull {
// Most of the time the provider ends with Provider which isn't part of the api name
it.internalName.replace("provider", "", ignoreCase = true) == apiName
}
?: getPluginsLocal().firstOrNull {
it.internalName.replace("provider", "", ignoreCase = true) == apiName
})?.let { savedData ->
// OnlinePluginData(savedData, onlineData)
loadPlugin(
activity,
context,
File(savedData.filePath),
savedData
)
@ -184,6 +245,10 @@ object PluginManager {
* 4. Else load the plugin normally
**/
fun updateAllOnlinePluginsAndLoadThem(activity: Activity) {
// Load all plugins as fast as possible!
loadAllOnlinePlugins(activity)
afterPluginsLoadedEvent.invoke(false)
val urls = (getKey<Array<RepositoryData>>(REPOSITORIES_KEY)
?: emptyArray()) + PREBUILT_REPOSITORIES
@ -193,50 +258,179 @@ object PluginManager {
// Iterates over all offline plugins, compares to remote repo and returns the plugins which are outdated
val outdatedPlugins = getPluginsOnline().map { savedData ->
onlinePlugins.filter { onlineData -> savedData.internalName == onlineData.second.internalName }
onlinePlugins
.filter { onlineData -> savedData.internalName == onlineData.second.internalName }
.map { onlineData ->
OnlinePluginData(savedData, onlineData)
}.filter {
it.validOnlineData(activity)
}
}.flatten().distinctBy { it.onlineData.second.url }
allCurrentOutDatedPlugins = outdatedPlugins.toSet()
Log.i(TAG, "Outdated plugins: ${outdatedPlugins.filter { it.isOutdated }}")
debugPrint {
"Outdated plugins: ${outdatedPlugins.filter { it.isOutdated }}"
}
outdatedPlugins.apmap {
if (it.isDisabled) {
return@apmap
} else if (it.isOutdated) {
downloadAndLoadPlugin(
val updatedPlugins = mutableListOf<String>()
outdatedPlugins.apmap { pluginData ->
if (pluginData.isDisabled) {
//updatedPlugins.add(activity.getString(R.string.single_plugin_disabled, pluginData.onlineData.second.name))
unloadPlugin(pluginData.savedData.filePath)
} else if (pluginData.isOutdated) {
downloadPlugin(
activity,
it.onlineData.second.url,
it.savedData.internalName,
it.onlineData.first
)
} else {
loadPlugin(
activity,
File(it.savedData.filePath),
it.savedData
)
pluginData.onlineData.second.url,
pluginData.savedData.internalName,
File(pluginData.savedData.filePath),
true
).let { success ->
if (success)
updatedPlugins.add(pluginData.onlineData.second.name)
}
}
}
main {
val uitext = txt(R.string.plugins_updated, updatedPlugins.size)
createNotification(activity, uitext, updatedPlugins)
}
// ioSafe {
loadedOnlinePlugins = true
afterPluginsLoadedEvent.invoke(false)
// }
Log.i(TAG, "Plugin update done!")
}
/**
* Use updateAllOnlinePluginsAndLoadThem
* */
fun loadAllOnlinePlugins(activity: Activity) {
File(activity.filesDir, ONLINE_PLUGINS_FOLDER).listFiles()?.sortedBy { it.name }
?.apmap { file ->
maybeLoadPlugin(activity, file)
* Automatically download plugins not yet existing on local
* 1. Gets all online data from online plugins repo
* 2. Fetch all not downloaded plugins
* 3. Download them and reload plugins
**/
fun downloadNotExistingPluginsAndLoad(activity: Activity, mode: AutoDownloadMode) {
val newDownloadPlugins = mutableListOf<String>()
val urls = (getKey<Array<RepositoryData>>(REPOSITORIES_KEY)
?: emptyArray()) + PREBUILT_REPOSITORIES
val onlinePlugins = urls.toList().apmap {
getRepoPlugins(it.url)?.toList() ?: emptyList()
}.flatten().distinctBy { it.second.url }
val providerLang = activity.getApiProviderLangSettings()
//Log.i(TAG, "providerLang => ${providerLang.toJson()}")
// Iterate online repos and returns not downloaded plugins
val notDownloadedPlugins = onlinePlugins.mapNotNull { onlineData ->
val sitePlugin = onlineData.second
val tvtypes = sitePlugin.tvTypes ?: listOf()
//Don't include empty urls
if (sitePlugin.url.isBlank()) {
return@mapNotNull null
}
if (sitePlugin.repositoryUrl.isNullOrBlank()) {
return@mapNotNull null
}
//Omit already existing plugins
if (getPluginPath(activity, sitePlugin.internalName, onlineData.first).exists()) {
Log.i(TAG, "Skip > ${sitePlugin.internalName}")
return@mapNotNull null
}
//Omit non-NSFW if mode is set to NSFW only
if (mode == AutoDownloadMode.NsfwOnly) {
if (!tvtypes.contains(TvType.NSFW.name)) {
return@mapNotNull null
}
}
//Omit NSFW, if disabled
if (!settingsForProvider.enableAdult) {
if (tvtypes.contains(TvType.NSFW.name)) {
return@mapNotNull null
}
}
//Omit lang not selected on language setting
if (mode == AutoDownloadMode.FilterByLang) {
val lang = sitePlugin.language ?: return@mapNotNull null
//If set to 'universal', don't skip any language
if (!providerLang.contains(AllLanguagesName) && !providerLang.contains(lang)) {
return@mapNotNull null
}
//Log.i(TAG, "sitePlugin lang => $lang")
}
val savedData = PluginData(
url = sitePlugin.url,
internalName = sitePlugin.internalName,
isOnline = true,
filePath = "",
version = sitePlugin.version
)
OnlinePluginData(savedData, onlineData)
}
//Log.i(TAG, "notDownloadedPlugins => ${notDownloadedPlugins.toJson()}")
notDownloadedPlugins.apmap { pluginData ->
downloadPlugin(
activity,
pluginData.onlineData.second.url,
pluginData.savedData.internalName,
pluginData.onlineData.first,
!pluginData.isDisabled
).let { success ->
if (success)
newDownloadPlugins.add(pluginData.onlineData.second.name)
}
}
main {
val uitext = txt(R.string.plugins_downloaded, newDownloadPlugins.size)
createNotification(activity, uitext, newDownloadPlugins)
}
// ioSafe {
afterPluginsLoadedEvent.invoke(false)
// }
Log.i(TAG, "Plugin download done!")
}
fun loadAllLocalPlugins(activity: Activity) {
/**
* Use updateAllOnlinePluginsAndLoadThem
* */
fun loadAllOnlinePlugins(context: Context) {
// Load all plugins as fast as possible!
(getPluginsOnline()).toList().apmap { pluginData ->
loadPlugin(
context,
File(pluginData.filePath),
pluginData
)
}
}
/**
* Reloads all local plugins and forces a page update, used for hot reloading with deployWithAdb
**/
fun hotReloadAllLocalPlugins(activity: FragmentActivity?) {
Log.d(TAG, "Reloading all local plugins!")
if (activity == null) return
getPluginsLocal().forEach {
unloadPlugin(it.filePath)
}
loadAllLocalPlugins(activity, true)
}
/**
* @param forceReload see afterPluginsLoadedEvent, basically a way to load all local plugins
* and reload all pages even if they are previously valid
**/
fun loadAllLocalPlugins(context: Context, forceReload: Boolean) {
val dir = File(LOCAL_PLUGINS_PATH)
removeKey(PLUGINS_KEY_LOCAL)
if (!dir.exists()) {
val res = dir.mkdirs()
@ -252,22 +446,47 @@ object PluginManager {
Log.d(TAG, "Files in '${LOCAL_PLUGINS_PATH}' folder: $sortedPlugins")
sortedPlugins?.sortedBy { it.name }?.apmap { file ->
maybeLoadPlugin(activity, file)
maybeLoadPlugin(context, file)
}
loadedLocalPlugins = true
afterPluginsLoadedEvent.invoke(forceReload)
}
/**
* This can be used to override any extension loading to fix crashes!
* @return true if safe mode file is present
**/
fun checkSafeModeFile(): Boolean {
return normalSafeApiCall {
val folder = File(CLOUD_STREAM_FOLDER)
if (!folder.exists()) return@normalSafeApiCall false
val files = folder.listFiles { _, name ->
name.equals("safe", ignoreCase = true)
}
files?.any()
} ?: false
}
/**
* @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 filePath = file.absolutePath
currentlyLoading = fileName
Log.i(TAG, "Loading plugin: $data")
return try {
val loader = PathClassLoader(filePath, activity.classLoader)
// in case of android 14 then
try {
File(filePath).setReadOnly()
} catch (t: Throwable) {
Log.e(TAG, "Failed to set dex as readonly")
logError(t)
}
val loader = PathClassLoader(filePath, context.classLoader)
var manifest: Plugin.Manifest
loader.getResourceAsStream("manifest.json").use { stream ->
if (stream == null) {
@ -288,10 +507,12 @@ object PluginManager {
val version: Int = manifest.version ?: PLUGIN_VERSION_NOT_SET.also {
Log.d(TAG, "No manifest version for ${data.internalName}")
}
@Suppress("UNCHECKED_CAST")
val pluginClass: Class<*> =
loader.loadClass(manifest.pluginClassName) as Class<out Plugin?>
val pluginInstance: Plugin =
pluginClass.newInstance() as Plugin
pluginClass.getDeclaredConstructor().newInstance() as Plugin
// Sets with the proper version
setPluginData(data.copy(version = version))
@ -301,40 +522,42 @@ object PluginManager {
return true
}
pluginInstance.__filename = fileName
pluginInstance.filename = file.absolutePath
if (manifest.requiresResources) {
Log.d(TAG, "Loading resources for ${data.internalName}")
// based on https://stackoverflow.com/questions/7483568/dynamic-resource-loading-from-other-apk
val assets = AssetManager::class.java.newInstance()
val assets = AssetManager::class.java.getDeclaredConstructor().newInstance()
val addAssetPath =
AssetManager::class.java.getMethod("addAssetPath", String::class.java)
addAssetPath.invoke(assets, file.absolutePath)
@Suppress("DEPRECATION")
pluginInstance.resources = Resources(
assets,
activity.resources.displayMetrics,
activity.resources.configuration
context.resources.displayMetrics,
context.resources.configuration
)
}
plugins[filePath] = pluginInstance
classLoaders[loader] = pluginInstance
if (data.url != null) { // TODO: make this cleaner
urlPlugins[data.url] = pluginInstance
}
pluginInstance.load(activity)
urlPlugins[data.url ?: filePath] = pluginInstance
pluginInstance.load(context)
Log.i(TAG, "Loaded plugin ${data.internalName} successfully")
currentlyLoading = null
true
} catch (e: Throwable) {
Log.e(TAG, "Failed to load $file: ${Log.getStackTraceString(e)}")
showToast(
activity,
activity.getString(R.string.plugin_load_fail).format(fileName),
context.getActivity(),
context.getString(R.string.plugin_load_fail).format(fileName),
Toast.LENGTH_LONG
)
currentlyLoading = null
false
}
}
private fun unloadPlugin(absolutePath: String) {
fun unloadPlugin(absolutePath: String) {
Log.i(TAG, "Unloading plugin: $absolutePath")
val plugin = plugins[absolutePath]
if (plugin == null) {
@ -349,15 +572,20 @@ object PluginManager {
}
// remove all registered apis
APIHolder.apis.filter { api -> api.sourcePlugin == plugin.__filename }.forEach {
removePluginMapping(it)
synchronized(APIHolder.apis) {
APIHolder.apis.filter { api -> api.sourcePlugin == plugin.filename }.forEach {
removePluginMapping(it)
}
}
APIHolder.allProviders.removeIf { provider: MainAPI -> provider.sourcePlugin == plugin.__filename }
extractorApis.removeIf { provider: ExtractorApi -> provider.sourcePlugin == plugin.__filename }
synchronized(APIHolder.allProviders) {
APIHolder.allProviders.removeIf { provider: MainAPI -> provider.sourcePlugin == plugin.filename }
}
extractorApis.removeIf { provider: ExtractorApi -> provider.sourcePlugin == plugin.filename }
classLoaders.values.removeIf { v -> v == plugin }
plugins.remove(absolutePath)
urlPlugins.values.removeIf { v -> v == plugin }
}
/**
@ -371,41 +599,75 @@ object PluginManager {
) + "." + name.hashCode()
}
suspend fun downloadAndLoadPlugin(
/**
* This should not be changed as it is used to also detect if a plugin is installed!
**/
fun getPluginPath(
context: Context,
internalName: String,
repositoryUrl: String
): File {
val folderName = getPluginSanitizedFileName(repositoryUrl) // Guaranteed unique
val fileName = getPluginSanitizedFileName(internalName)
return File("${context.filesDir}/${ONLINE_PLUGINS_FOLDER}/${folderName}/$fileName.cs3")
}
suspend fun downloadPlugin(
activity: Activity,
pluginUrl: String,
internalName: String,
repositoryUrl: String
repositoryUrl: String,
loadPlugin: Boolean
): Boolean {
val file = getPluginPath(activity, internalName, repositoryUrl)
return downloadPlugin(activity, pluginUrl, internalName, file, loadPlugin)
}
suspend fun downloadPlugin(
activity: Activity,
pluginUrl: String,
internalName: String,
file: File,
loadPlugin: Boolean
): Boolean {
try {
val folderName = getPluginSanitizedFileName(repositoryUrl) // Guaranteed unique
val fileName = getPluginSanitizedFileName(internalName)
Log.i(TAG, "Downloading plugin: $pluginUrl to $folderName/$fileName")
Log.d(TAG, "Downloading plugin: $pluginUrl to ${file.absolutePath}")
// The plugin file needs to be salted with the repository url hash as to allow multiple repositories with the same internal plugin names
val file = downloadPluginToFile(activity, pluginUrl, fileName, folderName)
return loadPlugin(
activity,
file ?: return false,
PluginData(internalName, pluginUrl, true, file.absolutePath, PLUGIN_VERSION_NOT_SET)
val newFile = downloadPluginToFile(pluginUrl, file) ?: return false
val data = PluginData(
internalName,
pluginUrl,
true,
newFile.absolutePath,
PLUGIN_VERSION_NOT_SET
)
return if (loadPlugin) {
unloadPlugin(file.absolutePath)
loadPlugin(
activity,
newFile,
data
)
} else {
setPluginData(data)
true
}
} catch (e: Exception) {
logError(e)
return false
}
}
/**
* @param isFilePath will treat the pluginUrl as as the filepath instead of url
* */
suspend fun deletePlugin(pluginIdentifier: String, isFilePath: Boolean): Boolean {
val data =
(if (isFilePath) (getPluginsLocal() + getPluginsOnline()).firstOrNull { it.filePath == pluginIdentifier }
else getPluginsOnline().firstOrNull { it.url == pluginIdentifier }) ?: return false
suspend fun deletePlugin(file: File): Boolean {
val list =
(getPluginsLocal() + getPluginsOnline()).filter { it.filePath == file.absolutePath }
return try {
if (File(data.filePath).delete()) {
unloadPlugin(data.filePath)
deletePluginData(data)
if (File(file.absolutePath).delete()) {
unloadPlugin(file.absolutePath)
list.forEach { deletePluginData(it) }
return true
}
false
@ -413,4 +675,71 @@ object PluginManager {
false
}
}
}
private fun Context.createNotificationChannel() {
hasCreatedNotChanel = true
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = EXTENSIONS_CHANNEL_NAME //getString(R.string.channel_name)
val descriptionText =
EXTENSIONS_CHANNEL_DESCRIPT//getString(R.string.channel_description)
val importance = NotificationManager.IMPORTANCE_LOW
val channel = NotificationChannel(EXTENSIONS_CHANNEL_ID, name, importance).apply {
description = descriptionText
}
// Register the channel with the system
val notificationManager: NotificationManager =
this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
private fun createNotification(
context: Context,
uitext: UiText,
extensions: List<String>
): Notification? {
try {
if (extensions.isEmpty()) return null
val content = extensions.joinToString(", ")
// main { // DON'T WANT TO SLOW IT DOWN
val builder = NotificationCompat.Builder(context, EXTENSIONS_CHANNEL_ID)
.setAutoCancel(false)
.setColorized(true)
.setOnlyAlertOnce(true)
.setSilent(true)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setColor(context.colorFromAttribute(R.attr.colorPrimary))
.setContentTitle(uitext.asString(context))
//.setContentTitle(context.getString(title, extensionNames.size))
.setSmallIcon(R.drawable.ic_baseline_extension_24)
.setStyle(
NotificationCompat.BigTextStyle()
.bigText(content)
)
.setContentText(content)
if (!hasCreatedNotChanel) {
context.createNotificationChannel()
}
val notification = builder.build()
// notificationId is a unique int for each notification that you must define
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
NotificationManagerCompat.from(context)
.notify((System.currentTimeMillis() / 1000).toInt(), notification)
}
return notification
} catch (e: Exception) {
logError(e)
return null
}
}
}

View file

@ -2,13 +2,17 @@ package com.lagradost.cloudstream3.plugins
import android.content.Context
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.setKey
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.amap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.plugins.PluginManager.getPluginSanitizedFileName
import com.lagradost.cloudstream3.plugins.PluginManager.unloadPlugin
import com.lagradost.cloudstream3.ui.settings.extensions.REPOSITORIES_KEY
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
@ -69,23 +73,55 @@ object RepositoryManager {
val PREBUILT_REPOSITORIES: Array<RepositoryData> by lazy {
getKey("PREBUILT_REPOSITORIES") ?: emptyArray()
}
private 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? {
val fixedUrl = url.trim()
return if (fixedUrl.contains("^https?://".toRegex())) {
fixedUrl
} else if (fixedUrl.contains("^(cloudstreamrepo://)|(https://cs\\.repo/\\??)".toRegex())) {
fixedUrl.replace("^(cloudstreamrepo://)|(https://cs\\.repo/\\??)".toRegex(), "").let {
return@let if (!it.contains("^https?://".toRegex()))
"https://${it}"
else fixedUrl
}
} else if (fixedUrl.matches("^[a-zA-Z0-9!_-]+$".toRegex())) {
suspendSafeApiCall {
app.get("https://cutt.ly/${fixedUrl}", allowRedirects = false).let { it2 ->
it2.headers["Location"]?.let { url ->
if (url.startsWith("https://cutt.ly/404")) return@suspendSafeApiCall null
if (url.removeSuffix("/") == "https://cutt.ly") return@suspendSafeApiCall null
return@suspendSafeApiCall url
}
}
}
} else null
}
suspend fun parseRepository(url: String): Repository? {
return suspendSafeApiCall {
// Take manifestVersion and such into account later
app.get(url).parsedSafe()
app.get(convertRawGitUrl(url)).parsedSafe()
}
}
private suspend fun parsePlugins(pluginUrls: String): List<SitePlugin> {
// Take manifestVersion and such into account later
return try {
val response = app.get(pluginUrls)
val response = app.get(convertRawGitUrl(pluginUrls))
// Normal parsed function not working?
// return response.parsedSafe()
tryParseJson<Array<SitePlugin>>(response.text)?.toList() ?: emptyList()
} catch (e : Exception) {
logError(e)
} catch (t: Throwable) {
logError(t)
emptyList()
}
}
@ -95,7 +131,7 @@ object RepositoryManager {
* */
suspend fun getRepoPlugins(repositoryUrl: String): List<Pair<String, SitePlugin>>? {
val repo = parseRepository(repositoryUrl) ?: return null
return repo.pluginLists.apmap { url ->
return repo.pluginLists.amap { url ->
parsePlugins(url).map {
repositoryUrl to it
}
@ -103,29 +139,21 @@ object RepositoryManager {
}
suspend fun downloadPluginToFile(
context: Context,
pluginUrl: String,
fileName: String,
folder: String
file: File
): File? {
return suspendSafeApiCall {
val extensionsDir = File(context.filesDir, ONLINE_PLUGINS_FOLDER)
if (!extensionsDir.exists())
extensionsDir.mkdirs()
file.mkdirs()
val newDir = File(extensionsDir, folder)
newDir.mkdirs()
val newFile = File(newDir, "${fileName}.cs3")
// Overwrite if exists
if (newFile.exists()) {
newFile.delete()
if (file.exists()) {
file.delete()
}
newFile.createNewFile()
file.createNewFile()
val body = app.get(pluginUrl).okhttpResponse.body
write(body.byteStream(), newFile.outputStream())
newFile
val body = app.get(convertRawGitUrl(pluginUrl)).okhttpResponse.body
write(body.byteStream(), file.outputStream())
file
}
}
@ -160,9 +188,17 @@ object RepositoryManager {
extensionsDir,
getPluginSanitizedFileName(repository.url)
)
PluginManager.deleteRepositoryData(file.absolutePath)
file.delete()
// Unload all plugins, not using deletePlugin since we
// delete all data and files in deleteRepositoryData
normalSafeApiCall {
file.listFiles { plugin: File ->
unloadPlugin(plugin.absolutePath)
false
}
}
PluginManager.deleteRepositoryData(file.absolutePath)
}
private fun write(stream: InputStream, output: OutputStream) {
@ -173,4 +209,4 @@ object RepositoryManager {
output.write(dataBuffer, 0, readBytes)
}
}
}
}

View file

@ -0,0 +1,108 @@
package com.lagradost.cloudstream3.plugins
import android.util.Log
import android.widget.Toast
import com.lagradost.cloudstream3.AcraApplication.Companion.context
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.R
import java.security.MessageDigest
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.Coroutines.main
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
object VotingApi { // please do not cheat the votes lol
private const val LOGKEY = "VotingApi"
private const val API_DOMAIN = "https://counterapi.com/api"
private fun transformUrl(url: String): String = // dont touch or all votes get reset
MessageDigest
.getInstance("SHA-256")
.digest("${url}#funny-salt".toByteArray())
.fold("") { str, it -> str + "%02x".format(it) }
suspend fun SitePlugin.getVotes(): Int {
return getVotes(url)
}
fun SitePlugin.hasVoted(): Boolean {
return hasVoted(url)
}
suspend fun SitePlugin.vote(): Int {
return vote(url)
}
fun SitePlugin.canVote(): Boolean {
return canVote(this.url)
}
// Plugin url to Int
private val votesCache = mutableMapOf<String, Int>()
private fun getRepository(pluginUrl: String) = pluginUrl
.split("/")
.drop(2)
.take(3)
.joinToString("-")
private suspend fun readVote(pluginUrl: String): Int {
val url = "${API_DOMAIN}/cs-${getRepository(pluginUrl)}/vote/${transformUrl(pluginUrl)}?readOnly=true"
Log.d(LOGKEY, "Requesting: $url")
return app.get(url).parsedSafe<Result>()?.value ?: 0
}
private suspend fun writeVote(pluginUrl: String): Boolean {
val url = "${API_DOMAIN}/cs-${getRepository(pluginUrl)}/vote/${transformUrl(pluginUrl)}"
Log.d(LOGKEY, "Requesting: $url")
return app.get(url).parsedSafe<Result>()?.value != null
}
suspend fun getVotes(pluginUrl: String): Int =
votesCache[pluginUrl] ?: readVote(pluginUrl).also {
votesCache[pluginUrl] = it
}
fun hasVoted(pluginUrl: String) =
getKey("cs3-votes/${transformUrl(pluginUrl)}") ?: false
fun canVote(pluginUrl: String): Boolean {
return PluginManager.urlPlugins.contains(pluginUrl)
}
private val voteLock = Mutex()
suspend fun vote(pluginUrl: String): Int {
// Prevent multiple requests at the same time.
voteLock.withLock {
if (!canVote(pluginUrl)) {
main {
Toast.makeText(context, R.string.extension_install_first, Toast.LENGTH_SHORT)
.show()
}
return getVotes(pluginUrl)
}
if (hasVoted(pluginUrl)) {
main {
Toast.makeText(context, R.string.already_voted, Toast.LENGTH_SHORT)
.show()
}
return getVotes(pluginUrl)
}
if (writeVote(pluginUrl)) {
setKey("cs3-votes/${transformUrl(pluginUrl)}", true)
votesCache[pluginUrl] = votesCache[pluginUrl]?.plus(1) ?: 1
}
return getVotes(pluginUrl)
}
}
private data class Result(
val value: Int?
)
}

View file

@ -0,0 +1,96 @@
package com.lagradost.cloudstream3.services
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.work.Constraints
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.ForegroundInfo
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.utils.AppContextUtils.createNotificationChannel
import com.lagradost.cloudstream3.utils.BackupUtils
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import java.util.concurrent.TimeUnit
const val BACKUP_CHANNEL_ID = "cloudstream3.backups"
const val BACKUP_WORK_NAME = "work_backup"
const val BACKUP_CHANNEL_NAME = "Backups"
const val BACKUP_CHANNEL_DESCRIPTION = "Notifications for background backups"
const val BACKUP_NOTIFICATION_ID = 938712898 // Random unique
class BackupWorkManager(val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) {
companion object {
fun enqueuePeriodicWork(context: Context?, intervalHours: Long) {
if (context == null) return
if (intervalHours == 0L) {
WorkManager.getInstance(context).cancelUniqueWork(BACKUP_WORK_NAME)
return
}
val constraints = Constraints.Builder()
.setRequiresStorageNotLow(true)
.build()
val periodicSyncDataWork =
PeriodicWorkRequest.Builder(
BackupWorkManager::class.java,
intervalHours,
TimeUnit.HOURS
)
.addTag(BACKUP_WORK_NAME)
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
BACKUP_WORK_NAME,
ExistingPeriodicWorkPolicy.UPDATE,
periodicSyncDataWork
)
// Uncomment below for testing
// val oneTimeBackupWork =
// OneTimeWorkRequest.Builder(BackupWorkManager::class.java)
// .addTag(BACKUP_WORK_NAME)
// .setConstraints(constraints)
// .build()
//
// WorkManager.getInstance(context).enqueue(oneTimeBackupWork)
}
}
private val backupNotificationBuilder =
NotificationCompat.Builder(context, BACKUP_CHANNEL_ID)
.setColorized(true)
.setOnlyAlertOnce(true)
.setSilent(true)
.setAutoCancel(true)
.setContentTitle(context.getString(R.string.pref_category_backup))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setColor(context.colorFromAttribute(R.attr.colorPrimary))
.setSmallIcon(R.drawable.ic_cloudstream_monochrome_big)
override suspend fun doWork(): Result {
context.createNotificationChannel(
BACKUP_CHANNEL_ID,
BACKUP_CHANNEL_NAME,
BACKUP_CHANNEL_DESCRIPTION
)
setForeground(
ForegroundInfo(
BACKUP_NOTIFICATION_ID,
backupNotificationBuilder.build()
)
)
BackupUtils.backup(context)
return Result.success()
}
}

View file

@ -0,0 +1,235 @@
package com.lagradost.cloudstream3.services
import android.annotation.SuppressLint
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.getApiFromNameNull
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.AppContextUtils.createNotificationChannel
import com.lagradost.cloudstream3.utils.AppContextUtils.getApiDubstatusSettings
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()
)
}
@SuppressLint("UnspecifiedImmutableFlag")
override suspend fun doWork(): Result {
try {
// 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 (t: Throwable) {
logError(t)
}
}
return Result.success()
} catch (t: Throwable) {
logError(t)
// ye, while this is not correct, but because gods know why android just crashes
// and this causes major battery usage as it retries it inf times. This is better, just
// in case android decides to be android and fuck us
return Result.success()
}
}
}

View file

@ -1,11 +1,22 @@
package com.lagradost.cloudstream3.services
import android.app.IntentService
import android.app.Service
import android.content.Intent
import android.os.IBinder
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") {
override fun onHandleIntent(intent: Intent?) {
class VideoDownloadService : Service() {
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) {
val id = intent.getIntExtra("id", -1)
val type = intent.getStringExtra("type")
@ -14,10 +25,36 @@ class VideoDownloadService : IntentService("VideoDownloadService") {
"resume" -> VideoDownloadManager.DownloadActionType.Resume
"pause" -> VideoDownloadManager.DownloadActionType.Pause
"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

@ -1,11 +1,23 @@
package com.lagradost.cloudstream3.subtitles
import androidx.annotation.WorkerThread
import androidx.core.net.toUri
import com.lagradost.cloudstream3.MainActivity.Companion.deleteFileOnExit
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities.SubtitleEntity
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities.SubtitleSearch
import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.ui.player.SubtitleOrigin
import okio.BufferedSource
import okio.buffer
import okio.sink
import okio.source
import java.io.File
import java.util.zip.ZipInputStream
interface AbstractSubProvider {
val idPrefix: String
@WorkerThread
suspend fun search(query: SubtitleSearch): List<SubtitleEntity>? {
throw NotImplementedError()
@ -15,6 +27,98 @@ interface AbstractSubProvider {
suspend fun load(data: SubtitleEntity): String? {
throw NotImplementedError()
}
@WorkerThread
suspend fun SubtitleResource.getResources(data: SubtitleEntity) {
this.addUrl(load(data))
}
@WorkerThread
suspend fun getResource(data: SubtitleEntity): SubtitleResource {
return SubtitleResource().apply {
this.getResources(data)
}
}
}
/**
* A builder for subtitle files.
* @see addUrl
* @see addFile
*/
class SubtitleResource {
fun downloadFile(source: BufferedSource): File {
val file = File.createTempFile("temp-subtitle", ".tmp").apply {
deleteFileOnExit(this)
}
val sink = file.sink().buffer()
sink.writeAll(source)
sink.close()
source.close()
return file
}
private fun unzip(file: File): List<Pair<String, File>> {
val entries = mutableListOf<Pair<String, File>>()
ZipInputStream(file.inputStream()).use { zipInputStream ->
var zipEntry = zipInputStream.nextEntry
while (zipEntry != null) {
val tempFile = File.createTempFile("unzipped-subtitle", ".tmp").apply {
deleteFileOnExit(this)
}
entries.add(zipEntry.name to tempFile)
tempFile.sink().buffer().use { buffer ->
buffer.writeAll(zipInputStream.source())
}
zipEntry = zipInputStream.nextEntry
}
}
return entries
}
data class SingleSubtitleResource(
val name: String?,
val url: String,
val origin: SubtitleOrigin
)
private var resources: MutableList<SingleSubtitleResource> = mutableListOf()
fun getSubtitles(): List<SingleSubtitleResource> {
return resources.toList()
}
fun addUrl(url: String?, name: String? = null) {
if (url == null) return
this.resources.add(
SingleSubtitleResource(name, url, SubtitleOrigin.URL)
)
}
fun addFile(file: File, name: String? = null) {
this.resources.add(
SingleSubtitleResource(name, file.toUri().toString(), SubtitleOrigin.DOWNLOADED_FILE)
)
deleteFileOnExit(file)
}
suspend fun addZipUrl(
url: String,
nameGenerator: (String, File) -> String? = { _, _ -> null }
) {
val source = app.get(url).okhttpResponse.body.source()
val zip = downloadFile(source)
val realFiles = unzip(zip)
zip.deleteRecursively()
realFiles.forEach { (name, subtitleFile) ->
addFile(subtitleFile, nameGenerator(name, subtitleFile))
}
}
}
interface AbstractSubApi : AbstractSubProvider, AuthAPI

View file

@ -13,13 +13,17 @@ class AbstractSubtitleEntities {
var epNumber: Int? = null,
var seasonNumber: Int? = null,
var year: Int? = null,
var isHearingImpaired: Boolean = false
var isHearingImpaired: Boolean = false,
var headers: Map<String, String> = emptyMap()
)
data class SubtitleSearch(
var query: String = "",
var imdb: Long? = null,
var lang: String? = null,
var imdbId: String? = null,
var tmdbId: Int? = null,
var malId: Int? = null,
var aniListId: Int? = null,
var epNumber: Int? = null,
var seasonNumber: Int? = null,
var year: Int? = null

View file

@ -3,52 +3,75 @@ package com.lagradost.cloudstream3.syncproviders
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.syncproviders.providers.*
import java.util.concurrent.TimeUnit
abstract class AccountManager(private val defIndex: Int) : AuthAPI {
companion object {
val malApi = MALApi(0)
val aniListApi = AniListApi(0)
val malApi = MALApi(0).also { api ->
LoadResponse.Companion.malIdPrefix = api.idPrefix
}
val aniListApi = AniListApi(0).also { api ->
LoadResponse.Companion.aniListIdPrefix = api.idPrefix
}
val simklApi = SimklApi(0).also { api ->
LoadResponse.Companion.simklIdPrefix = api.idPrefix
}
val openSubtitlesApi = OpenSubtitlesApi(0)
val indexSubtitlesApi = IndexSubtitleApi()
val addic7ed = Addic7ed()
val subDlApi = SubDlApi(0)
val localListApi = LocalList()
val subSourceApi = SubSourceApi()
// used to login via app intent
val OAuth2Apis
get() = listOf<OAuth2API>(
malApi, aniListApi
malApi, aniListApi, simklApi
)
// this needs init with context and can be accessed in settings
val accountManagers
get() = listOf(
malApi, aniListApi, openSubtitlesApi, //nginxApi
malApi, aniListApi, openSubtitlesApi, subDlApi, simklApi //nginxApi
)
// used for active syncing
val SyncApis
get() = listOf(
SyncRepo(malApi), SyncRepo(aniListApi)
SyncRepo(malApi), SyncRepo(aniListApi), SyncRepo(localListApi), SyncRepo(simklApi)
)
val inAppAuths
get() = listOf(openSubtitlesApi)//, nginxApi)
get() = listOf<InAppAuthAPIManager>(
openSubtitlesApi,
subDlApi
)//, nginxApi)
val subtitleProviders
get() = listOf(
openSubtitlesApi,
// indexSubtitlesApi // they got anti scraping measures in place :(
addic7ed,
subDlApi,
subSourceApi
)
const val appString = "cloudstreamapp"
const val appStringRepo = "cloudstreamrepo"
const val APP_STRING = "cloudstreamapp"
const val APP_STRING_REPO = "cloudstreamrepo"
const val APP_STRING_PLAYER = "cloudstreamplayer"
// Instantly start the search given a query
const val APP_STRING_SEARCH = "cloudstreamsearch"
// Instantly resume watching a show
const val APP_STRING_RESUME_WATCHING = "cloudstreamcontinuewatching"
val unixTime: Long
get() = System.currentTimeMillis() / 1000L
val unixTimeMs: Long
get() = System.currentTimeMillis()
const val maxStale = 60 * 10
const val MAX_STALE = 60 * 10
fun secondsToReadable(seconds: Int, completedValue: String): String {
var secondsLong = seconds.toLong()

View file

@ -1,9 +1,27 @@
package com.lagradost.cloudstream3.syncproviders
import androidx.fragment.app.FragmentActivity
interface OAuth2API : AuthAPI {
val key: String
val redirectUrl: String
val supportDeviceAuth: Boolean
suspend fun handleRedirect(url: String) : Boolean
fun authenticate()
fun authenticate(activity: FragmentActivity?)
suspend fun getDevicePin() : PinAuthData? {
return null
}
suspend fun handleDeviceAuth(pinAuthData: PinAuthData) : Boolean {
return false
}
data class PinAuthData(
val deviceCode: String,
val userCode: String,
val verificationUrl: String,
val expiresIn: Int,
val interval: Int,
)
}

View file

@ -1,79 +0,0 @@
package com.lagradost.cloudstream3.syncproviders
import com.lagradost.cloudstream3.*
interface SyncAPI : OAuth2API {
val mainUrl: String
/**
-1 -> None
0 -> Watching
1 -> Completed
2 -> OnHold
3 -> Dropped
4 -> PlanToWatch
5 -> ReWatching
*/
suspend fun score(id: String, status: SyncStatus): Boolean
suspend fun getStatus(id: String): SyncStatus?
suspend fun getResult(id: String): SyncResult?
suspend fun search(name: String): List<SyncSearchResult>?
fun getIdFromUrl(url : String) : String
data class SyncSearchResult(
override val name: String,
override val apiName: String,
var syncId: String,
override val url: String,
override var posterUrl: String?,
override var type: TvType? = null,
override var quality: SearchQuality? = null,
override var posterHeaders: Map<String, String>? = null,
override var id: Int? = null,
) : SearchResponse
data class SyncStatus(
val status: Int,
/** 1-10 */
val score: Int?,
val watchedEpisodes: Int?,
var isFavorite: Boolean? = null,
var maxEpisodes : Int? = null,
)
data class SyncResult(
/**Used to verify*/
var id: String,
var totalEpisodes: Int? = null,
var title: String? = null,
/**1-1000*/
var publicScore: Int? = null,
/**In minutes*/
var duration: Int? = null,
var synopsis: String? = null,
var airStatus: ShowStatus? = null,
var nextAiring: NextAiring? = null,
var studio: List<String>? = null,
var genres: List<String>? = null,
var synonyms: List<String>? = null,
var trailers: List<String>? = null,
var isAdult : Boolean? = null,
var posterUrl: String? = null,
var backgroundPosterUrl : String? = null,
/** In unixtime */
var startDate: Long? = null,
/** In unixtime */
var endDate: Long? = null,
var recommendations: List<SyncSearchResult>? = null,
var nextSeason: SyncSearchResult? = null,
var prevSeason: SyncSearchResult? = null,
var actors: List<ActorData>? = null,
)
}

View file

@ -0,0 +1,170 @@
package com.lagradost.cloudstream3.syncproviders
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.ui.SyncWatchType
import com.lagradost.cloudstream3.ui.library.ListSorting
import com.lagradost.cloudstream3.ui.result.UiText
import me.xdrop.fuzzywuzzy.FuzzySearch
import java.util.Date
interface SyncAPI : OAuth2API {
/**
* Set this to true if the user updates something on the list like watch status or score
**/
var requireLibraryRefresh: Boolean
val mainUrl: String
/**
* Allows certain providers to open pages from
* library links.
**/
val syncIdName: SyncIdName
/**
-1 -> None
0 -> Watching
1 -> Completed
2 -> OnHold
3 -> Dropped
4 -> PlanToWatch
5 -> ReWatching
*/
suspend fun score(id: String, status: AbstractSyncStatus): Boolean
suspend fun getStatus(id: String): AbstractSyncStatus?
suspend fun getResult(id: String): SyncResult?
suspend fun search(name: String): List<SyncSearchResult>?
suspend fun getPersonalLibrary(): LibraryMetadata?
fun getIdFromUrl(url: String): String
data class SyncSearchResult(
override val name: String,
override val apiName: String,
var syncId: String,
override val url: String,
override var posterUrl: String?,
override var type: TvType? = null,
override var quality: SearchQuality? = null,
override var posterHeaders: Map<String, String>? = null,
override var id: Int? = null,
) : SearchResponse
abstract class AbstractSyncStatus {
abstract var status: SyncWatchType
/** 1-10 */
abstract var score: Int?
abstract var watchedEpisodes: Int?
abstract var isFavorite: Boolean?
abstract var maxEpisodes: Int?
}
data class SyncStatus(
override var status: SyncWatchType,
/** 1-10 */
override var score: Int?,
override var watchedEpisodes: Int?,
override var isFavorite: Boolean? = null,
override var maxEpisodes: Int? = null,
) : AbstractSyncStatus()
data class SyncResult(
/**Used to verify*/
var id: String,
var totalEpisodes: Int? = null,
var title: String? = null,
/**1-1000*/
var publicScore: Int? = null,
/**In minutes*/
var duration: Int? = null,
var synopsis: String? = null,
var airStatus: ShowStatus? = null,
var nextAiring: NextAiring? = null,
var studio: List<String>? = null,
var genres: List<String>? = null,
var synonyms: List<String>? = null,
var trailers: List<String>? = null,
var isAdult: Boolean? = null,
var posterUrl: String? = null,
var backgroundPosterUrl: String? = null,
/** In unixtime */
var startDate: Long? = null,
/** In unixtime */
var endDate: Long? = null,
var recommendations: List<SyncSearchResult>? = null,
var nextSeason: SyncSearchResult? = null,
var prevSeason: SyncSearchResult? = null,
var actors: List<ActorData>? = null,
)
data class Page(
val title: UiText, var items: List<LibraryItem>
) {
fun sort(method: ListSorting?, query: String? = null) {
items = when (method) {
ListSorting.Query ->
if (query != null) {
items.sortedBy {
-FuzzySearch.partialRatio(
query.lowercase(), it.name.lowercase()
)
}
} else items
ListSorting.RatingHigh -> items.sortedBy { -(it.personalRating ?: 0) }
ListSorting.RatingLow -> items.sortedBy { (it.personalRating ?: 0) }
ListSorting.AlphabeticalA -> items.sortedBy { it.name }
ListSorting.AlphabeticalZ -> items.sortedBy { it.name }.reversed()
ListSorting.UpdatedNew -> items.sortedBy { it.lastUpdatedUnixTime?.times(-1) }
ListSorting.UpdatedOld -> items.sortedBy { it.lastUpdatedUnixTime }
ListSorting.ReleaseDateNew -> items.sortedByDescending { it.releaseDate }
ListSorting.ReleaseDateOld -> items.sortedBy { it.releaseDate }
else -> items
}
}
}
data class LibraryMetadata(
val allLibraryLists: List<LibraryList>,
val supportedListSorting: Set<ListSorting>
)
data class LibraryList(
val name: UiText,
val items: List<LibraryItem>
)
data class LibraryItem(
override val name: String,
override val url: String,
/**
* Unique unchanging string used for data storage.
* This should be the actual id when you change scores and status
* since score changes from library might get added in the future.
**/
val syncId: String,
val episodesCompleted: Int?,
val episodesTotal: Int?,
/** Out of 100 */
val personalRating: Int?,
val lastUpdatedUnixTime: Long?,
override val apiName: String,
override var type: TvType?,
override var posterUrl: String?,
override var posterHeaders: Map<String, String>?,
override var quality: SearchQuality?,
val releaseDate: Date?,
override var id: Int? = null,
val plot : String? = null,
val rating: Int? = null,
val tags: List<String>? = null
) : SearchResponse
}

View file

@ -11,26 +11,38 @@ class SyncRepo(private val repo: SyncAPI) {
val icon = repo.icon
val mainUrl = repo.mainUrl
val requiresLogin = repo.requiresLogin
val syncIdName = repo.syncIdName
var requireLibraryRefresh: Boolean
get() = repo.requireLibraryRefresh
set(value) {
repo.requireLibraryRefresh = value
}
suspend fun score(id: String, status: SyncAPI.SyncStatus): Resource<Boolean> {
suspend fun score(id: String, status: SyncAPI.AbstractSyncStatus): Resource<Boolean> {
return safeApiCall { repo.score(id, status) }
}
suspend fun getStatus(id : String) : Resource<SyncAPI.SyncStatus> {
suspend fun getStatus(id: String): Resource<SyncAPI.AbstractSyncStatus> {
return safeApiCall { repo.getStatus(id) ?: throw ErrorLoadingException("No data") }
}
suspend fun getResult(id : String) : Resource<SyncAPI.SyncResult> {
suspend fun getResult(id: String): Resource<SyncAPI.SyncResult> {
return safeApiCall { repo.getResult(id) ?: throw ErrorLoadingException("No data") }
}
suspend fun search(query : String) : Resource<List<SyncAPI.SyncSearchResult>> {
suspend fun search(query: String): Resource<List<SyncAPI.SyncSearchResult>> {
return safeApiCall { repo.search(query) ?: throw ErrorLoadingException() }
}
fun hasAccount() : Boolean {
suspend fun getPersonalLibrary(): Resource<SyncAPI.LibraryMetadata> {
return safeApiCall { repo.getPersonalLibrary() ?: throw ErrorLoadingException() }
}
fun hasAccount(): Boolean {
return normalSafeApiCall { repo.loginInfo() != null } ?: false
}
fun getIdFromUrl(url : String) : String = repo.getIdFromUrl(url)
fun getIdFromUrl(url: String): String? = normalSafeApiCall {
repo.getIdFromUrl(url)
}
}

View file

@ -0,0 +1,108 @@
package com.lagradost.cloudstream3.syncproviders.providers
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.subtitles.AbstractSubApi
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
import com.lagradost.cloudstream3.utils.SubtitleHelper
class Addic7ed : AbstractSubApi {
override val name = "Addic7ed"
override val idPrefix = "addic7ed"
override val requiresLogin = false
override val icon: Nothing? = null
override val createAccountUrl: Nothing? = null
override fun loginInfo(): Nothing? = null
override fun logOut() {}
companion object {
const val HOST = "https://www.addic7ed.com"
const val TAG = "ADDIC7ED"
}
private fun fixUrl(url: String): String {
return if (url.startsWith("/")) HOST + url
else if (!url.startsWith("http")) "$HOST/$url"
else url
}
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity> {
val lang = query.lang
val queryLang = SubtitleHelper.fromTwoLettersToLanguage(lang.toString())
val queryText = query.query.trim()
val epNum = query.epNumber ?: 0
val seasonNum = query.seasonNumber ?: 0
val yearNum = query.year ?: 0
fun cleanResources(
results: MutableList<AbstractSubtitleEntities.SubtitleEntity>,
name: String,
link: String,
headers: Map<String, String>,
isHearingImpaired: Boolean
) {
results.add(
AbstractSubtitleEntities.SubtitleEntity(
idPrefix = idPrefix,
name = name,
lang = queryLang.toString(),
data = link,
source = this.name,
type = if (seasonNum > 0) TvType.TvSeries else TvType.Movie,
epNumber = epNum,
seasonNumber = seasonNum,
year = yearNum,
headers = headers,
isHearingImpaired = isHearingImpaired
)
)
}
val title = queryText.substringBefore("(").trim()
val url = "$HOST/search.php?search=${title}&Submit=Search"
val hostDocument = app.get(url).document
var searchResult = ""
if (!hostDocument.select("span:contains($title)").isNullOrEmpty()) searchResult = url
else if (!hostDocument.select("table.tabel")
.isNullOrEmpty()
) searchResult = hostDocument.select("a:contains($title)").attr("href").toString()
else {
val show =
hostDocument.selectFirst("#sl button")?.attr("onmouseup")?.substringAfter("(")
?.substringBefore(",")
val doc = app.get(
"$HOST/ajax_loadShow.php?show=$show&season=$seasonNum&langs=&hd=undefined&hi=undefined",
referer = "$HOST/"
).document
doc.select("#season tr:contains($queryLang)").mapNotNull { node ->
if (node.selectFirst("td")?.text()
?.toIntOrNull() == seasonNum && node.select("td:eq(1)")
.text()
.toIntOrNull() == epNum
) searchResult = fixUrl(node.select("a").attr("href"))
}
}
val results = mutableListOf<AbstractSubtitleEntities.SubtitleEntity>()
val document = app.get(
url = fixUrl(searchResult),
).document
document.select(".tabel95 .tabel95 tr:contains($queryLang)").mapNotNull { node ->
val name = if (seasonNum > 0) "${document.select(".titulo").text().replace("Subtitle","").trim()}${
node.parent()!!.select(".NewsTitle").text().substringAfter("Version").substringBefore(", Duration")
}" else "${document.select(".titulo").text().replace("Subtitle","").trim()}${node.parent()!!.select(".NewsTitle").text().substringAfter("Version").substringBefore(", Duration")}"
val link = fixUrl(node.select("a.buttonDownload").attr("href"))
val isHearingImpaired =
!node.parent()!!.select("tr:last-child [title=\"Hearing Impaired\"]").isNullOrEmpty()
cleanResources(results, name, link, mapOf("referer" to "$HOST/"), isHearingImpaired)
}
return results
}
override suspend fun load(data: AbstractSubtitleEntities.SubtitleEntity): String {
return data.data
}
}

View file

@ -1,36 +1,44 @@
package com.lagradost.cloudstream3.syncproviders.providers
import androidx.annotation.StringRes
import androidx.fragment.app.FragmentActivity
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.ui.SyncWatchType
import com.lagradost.cloudstream3.ui.library.ListSorting
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.AppContextUtils.splitQuery
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
import com.lagradost.cloudstream3.utils.DataStoreHelper.toYear
import java.net.URL
import java.util.*
import java.net.URLEncoder
import java.util.Locale
class AniListApi(index: Int) : AccountManager(index), SyncAPI {
override var name = "AniList"
override val key = "6871"
override val redirectUrl = "anilistlogin"
override val idPrefix = "anilist"
override var requireLibraryRefresh = true
override val supportDeviceAuth = false
override var mainUrl = "https://anilist.co"
override val icon = R.drawable.ic_anilist_icon
override val requiresLogin = false
override val createAccountUrl = "$mainUrl/signup"
override val syncIdName = SyncIdName.Anilist
override fun loginInfo(): AuthAPI.LoginInfo? {
// context.getUser(true)?.
@ -45,17 +53,18 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
}
override fun logOut() {
requireLibraryRefresh = true
removeAccountKeys()
}
override fun authenticate() {
override fun authenticate(activity: FragmentActivity?) {
val request = "https://anilist.co/api/v2/oauth/authorize?client_id=$key&response_type=token"
openBrowser(request)
openBrowser(request, activity)
}
override suspend fun handleRedirect(url: String): Boolean {
val sanitizer =
splitQuery(URL(url.replace(appString, "https").replace("/#", "?"))) // FIX ERROR
splitQuery(URL(url.replace(APP_STRING, "https").replace("/#", "?"))) // FIX ERROR
val token = sanitizer["access_token"]!!
val expiresIn = sanitizer["expires_in"]!!
@ -64,8 +73,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
switchToNewAccount()
setKey(accountId, ANILIST_UNIXTIME_KEY, endTime)
setKey(accountId, ANILIST_TOKEN_KEY, token)
setKey(ANILIST_SHOULD_UPDATE_LIST, true)
val user = getUser()
requireLibraryRefresh = true
return user != null
}
@ -79,7 +88,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
override suspend fun search(name: String): List<SyncAPI.SyncSearchResult>? {
val data = searchShows(name) ?: return null
return data.data?.Page?.media?.map {
return data.data?.page?.media?.map {
SyncAPI.SyncSearchResult(
it.title.romaji ?: return null,
this.name,
@ -93,7 +102,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
override suspend fun getResult(id: String): SyncAPI.SyncResult {
val internalId = (Regex("anilist\\.co/anime/(\\d*)").find(id)?.groupValues?.getOrNull(1)
?: id).toIntOrNull() ?: throw ErrorLoadingException("Invalid internalId")
val season = getSeason(internalId).data.Media
val season = getSeason(internalId).data.media
return SyncAPI.SyncResult(
season.id.toString(),
@ -140,7 +149,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
this.name,
recMedia.id?.toString() ?: return@mapNotNull null,
getUrlFromId(recMedia.id),
recMedia.coverImage?.large ?: recMedia.coverImage?.medium
recMedia.coverImage?.extraLarge ?: recMedia.coverImage?.large
?: recMedia.coverImage?.medium
)
},
trailers = when (season.trailer?.site?.lowercase()?.trim()) {
@ -151,26 +161,28 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
)
}
override suspend fun getStatus(id: String): SyncAPI.SyncStatus? {
override suspend fun getStatus(id: String): SyncAPI.AbstractSyncStatus? {
val internalId = id.toIntOrNull() ?: return null
val data = getDataAboutId(internalId) ?: return null
return SyncAPI.SyncStatus(
score = data.score,
watchedEpisodes = data.progress,
status = data.type?.value ?: return null,
status = SyncWatchType.fromInternalId(data.type?.value ?: return null),
isFavorite = data.isFavourite,
maxEpisodes = data.episodes,
)
}
override suspend fun score(id: String, status: SyncAPI.SyncStatus): Boolean {
override suspend fun score(id: String, status: SyncAPI.AbstractSyncStatus): Boolean {
return postDataAboutId(
id.toIntOrNull() ?: return false,
fromIntToAnimeStatus(status.status),
fromIntToAnimeStatus(status.status.internalId),
status.score,
status.watchedEpisodes
)
).also {
requireLibraryRefresh = requireLibraryRefresh || it
}
}
companion object {
@ -181,7 +193,6 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
const val ANILIST_TOKEN_KEY: String = "anilist_token" // anilist token for api
const val ANILIST_USER_KEY: String = "anilist_user" // user data like profile
const val ANILIST_CACHED_LIST: String = "anilist_cached_list"
const val ANILIST_SHOULD_UPDATE_LIST: String = "anilist_should_update_list"
private fun fixName(name: String): String {
return name.lowercase(Locale.ROOT).replace(" ", "")
@ -219,7 +230,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
romaji
}
idMal
coverImage { medium large }
coverImage { medium large extraLarge }
averageScore
}
}
@ -232,7 +243,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
format
id
idMal
coverImage { medium large }
coverImage { medium large extraLarge }
averageScore
title {
english
@ -291,16 +302,14 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
//println("NAME $name NEW NAME ${name.replace(blackListRegex, "")}")
val shows = searchShows(name.replace(blackListRegex, ""))
shows?.data?.Page?.media?.find {
malId ?: "NONE" == it.idMal.toString()
shows?.data?.page?.media?.find {
(malId ?: "NONE") == it.idMal.toString()
}?.let { return it }
val filtered =
shows?.data?.Page?.media?.filter {
(
it.startDate.year ?: year.toString() == year.toString()
|| year == null
)
shows?.data?.page?.media?.filter {
(((it.startDate.year ?: year.toString()) == year.toString()
|| year == null))
}
filtered?.forEach {
it.title.romaji?.let { romaji ->
@ -312,14 +321,14 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
}
// Changing names of these will show up in UI
enum class AniListStatusType(var value: Int) {
Watching(0),
Completed(1),
Paused(2),
Dropped(3),
Planning(4),
ReWatching(5),
None(-1)
enum class AniListStatusType(var value: Int, @StringRes val stringRes: Int) {
Watching(0, R.string.type_watching),
Completed(1, R.string.type_completed),
Paused(2, R.string.type_on_hold),
Dropped(3, R.string.type_dropped),
Planning(4, R.string.type_plan_to_watch),
ReWatching(5, R.string.type_re_watching),
None(-1, R.string.none)
}
fun fromIntToAnimeStatus(inp: Int): AniListStatusType {//= AniListStatusType.values().first { it.value == inp }
@ -335,7 +344,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
}
}
fun convertAnilistStringToStatus(string: String): AniListStatusType {
fun convertAniListStringToStatus(string: String): AniListStatusType {
return fromIntToAnimeStatus(aniListStatusString.indexOf(string))
}
@ -488,7 +497,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
val data = postApi(q, true)
val d = parseJson<GetDataRoot>(data ?: return null)
val main = d.data?.Media
val main = d.data?.media
if (main?.mediaListEntry != null) {
return AniListTitleHolder(
title = main.title,
@ -521,19 +530,27 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
}
private suspend fun postApi(q: String, cache: Boolean = false): String? {
return if (!checkToken()) {
app.post(
"https://graphql.anilist.co/",
headers = mapOf(
"Authorization" to "Bearer " + (getAuth() ?: return null),
if (cache) "Cache-Control" to "max-stale=$maxStale" else "Cache-Control" to "no-cache"
),
cacheTime = 0,
data = mapOf("query" to q),//(if (vars == null) mapOf("query" to q) else mapOf("query" to q, "variables" to vars))
timeout = 5 // REASONABLE TIMEOUT
).text.replace("\\/", "/")
} else {
null
return suspendSafeApiCall {
if (!checkToken()) {
app.post(
"https://graphql.anilist.co/",
headers = mapOf(
"Authorization" to "Bearer " + (getAuth()
?: return@suspendSafeApiCall null),
if (cache) "Cache-Control" to "max-stale=$MAX_STALE" else "Cache-Control" to "no-cache"
),
cacheTime = 0,
data = mapOf(
"query" to URLEncoder.encode(
q,
"UTF-8"
)
), //(if (vars == null) mapOf("query" to q) else mapOf("query" to q, "variables" to vars))
timeout = 5 // REASONABLE TIMEOUT
).text.replace("\\/", "/")
} else {
null
}
}
}
@ -568,7 +585,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
data class CoverImage(
@JsonProperty("medium") val medium: String?,
@JsonProperty("large") val large: String?
@JsonProperty("large") val large: String?,
@JsonProperty("extraLarge") val extraLarge: String?
)
data class Media(
@ -580,7 +598,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
//@JsonProperty("source") val source: String,
@JsonProperty("episodes") val episodes: Int,
@JsonProperty("title") val title: Title,
//@JsonProperty("description") val description: String,
@JsonProperty("description") val description: String?,
@JsonProperty("coverImage") val coverImage: CoverImage,
@JsonProperty("synonyms") val synonyms: List<String>,
@JsonProperty("nextAiringEpisode") val nextAiringEpisode: SeasonNextAiringEpisode?,
@ -595,7 +613,31 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
@JsonProperty("score") val score: Int,
@JsonProperty("private") val private: Boolean,
@JsonProperty("media") val media: Media
)
) {
fun toLibraryItem(): SyncAPI.LibraryItem {
return SyncAPI.LibraryItem(
// English title first
this.media.title.english ?: this.media.title.romaji
?: this.media.synonyms.firstOrNull()
?: "",
"https://anilist.co/anime/${this.media.id}/",
this.media.id.toString(),
this.progress,
this.media.episodes,
this.score,
this.updatedAt.toLong(),
"AniList",
TvType.Anime,
this.media.coverImage.extraLarge ?: this.media.coverImage.large
?: this.media.coverImage.medium,
null,
null,
this.media.seasonYear.toYear(),
null,
plot = this.media.description,
)
}
}
data class Lists(
@JsonProperty("status") val status: String?,
@ -607,43 +649,64 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
)
data class Data(
@JsonProperty("MediaListCollection") val MediaListCollection: MediaListCollection
@JsonProperty("MediaListCollection") val mediaListCollection: MediaListCollection
)
fun getAnilistListCached(): Array<Lists>? {
private fun getAniListListCached(): Array<Lists>? {
return getKey(ANILIST_CACHED_LIST) as? Array<Lists>
}
suspend fun getAnilistAnimeListSmart(): Array<Lists>? {
private suspend fun getAniListAnimeListSmart(): Array<Lists>? {
if (getAuth() == null) return null
if (checkToken()) return null
return if (getKey(ANILIST_SHOULD_UPDATE_LIST, true) == true) {
val list = getFullAnilistList()?.data?.MediaListCollection?.lists?.toTypedArray()
return if (requireLibraryRefresh) {
val list = getFullAniListList()?.data?.mediaListCollection?.lists?.toTypedArray()
if (list != null) {
setKey(ANILIST_CACHED_LIST, list)
setKey(ANILIST_SHOULD_UPDATE_LIST, false)
}
list
} else {
getAnilistListCached()
getAniListListCached()
}
}
private suspend fun getFullAnilistList(): FullAnilistList? {
var userID: Int? = null
/** WARNING ASSUMES ONE USER! **/
getKeys(ANILIST_USER_KEY)?.forEach { key ->
getKey<AniListUser>(key, null)?.let {
userID = it.id
}
}
override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata {
val list = getAniListAnimeListSmart()?.groupBy {
convertAniListStringToStatus(it.status ?: "").stringRes
}?.mapValues { group ->
group.value.map { it.entries.map { entry -> entry.toLibraryItem() } }.flatten()
} ?: emptyMap()
val fixedUserID = userID ?: return null
// To fill empty lists when AniList does not return them
val baseMap =
AniListStatusType.entries.filter { it.value >= 0 }.associate {
it.stringRes to emptyList<SyncAPI.LibraryItem>()
}
return SyncAPI.LibraryMetadata(
(baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) },
setOf(
ListSorting.AlphabeticalA,
ListSorting.AlphabeticalZ,
ListSorting.UpdatedNew,
ListSorting.UpdatedOld,
ListSorting.ReleaseDateNew,
ListSorting.ReleaseDateOld,
ListSorting.RatingHigh,
ListSorting.RatingLow,
)
)
}
private suspend fun getFullAniListList(): FullAnilistList? {
/** WARNING ASSUMES ONE USER! **/
val userID = getKey<AniListUser>(accountId, ANILIST_USER_KEY)?.id ?: return null
val mediaType = "ANIME"
val query = """
query (${'$'}userID: Int = $fixedUserID, ${'$'}MEDIA: MediaType = $mediaType) {
query (${'$'}userID: Int = $userID, ${'$'}MEDIA: MediaType = $mediaType) {
MediaListCollection (userId: ${'$'}userID, type: ${'$'}MEDIA) {
lists {
status
@ -654,7 +717,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
startedAt { year month day }
updatedAt
progress
score
score (format: POINT_100)
private
media
{
@ -670,7 +733,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
english
romaji
}
coverImage { medium }
coverImage { extraLarge large medium }
synonyms
nextAiringEpisode {
timeUntilAiring
@ -703,6 +766,11 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
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(
id: Int,
type: AniListStatusType,
@ -710,19 +778,43 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
progress: Int?
): Boolean {
val q =
"""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
}
// Delete item if status type is None
if (type == AniListStatusType.None) {
val userID = getKey<AniListUser>(accountId, ANILIST_USER_KEY)?.id ?: return false
// Get list ID for deletion
val idQuery = """
query MediaList(${'$'}userId: Int = $userID, ${'$'}mediaId: Int = $id) {
MediaList(userId: ${'$'}userId, mediaId: ${'$'}mediaId) {
id
}
}
"""
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)
return data != ""
}
@ -748,7 +840,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
val data = postApi(q)
if (data.isNullOrBlank()) return null
val userData = parseJson<AniListRoot>(data)
val u = userData.data?.Viewer
val u = userData.data?.viewer
val user = AniListUser(
u?.id,
u?.name,
@ -770,8 +862,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
suspend fun getSeasonRecursive(id: Int) {
val season = getSeason(id)
seasons.add(season)
if (season.data.Media.format?.startsWith("TV") == true) {
season.data.Media.relations?.edges?.forEach {
if (season.data.media.format?.startsWith("TV") == true) {
season.data.media.relations?.edges?.forEach {
if (it.node?.format != null) {
if (it.relationType == "SEQUEL" && it.node.format.startsWith("TV")) {
getSeasonRecursive(it.node.id)
@ -790,7 +882,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
)
data class SeasonData(
@JsonProperty("Media") val Media: SeasonMedia,
@JsonProperty("Media") val media: SeasonMedia,
)
data class SeasonMedia(
@ -962,7 +1054,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
)
data class AniListData(
@JsonProperty("Viewer") val Viewer: AniListViewer?,
@JsonProperty("Viewer") val viewer: AniListViewer?,
)
data class AniListRoot(
@ -1002,7 +1094,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
)
data class LikeData(
@JsonProperty("Viewer") val Viewer: LikeViewer?,
@JsonProperty("Viewer") val viewer: LikeViewer?,
)
data class LikeRoot(
@ -1042,7 +1134,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
)
data class GetDataData(
@JsonProperty("Media") val Media: GetDataMedia?,
@JsonProperty("Media") val media: GetDataMedia?,
)
data class GetDataRoot(
@ -1075,7 +1167,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
)
data class GetSearchPage(
@JsonProperty("Page") val Page: GetSearchData?,
@JsonProperty("Page") val page: GetSearchData?,
)
data class GetSearchData(

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.syncproviders.providers
import androidx.fragment.app.FragmentActivity
import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.syncproviders.OAuth2API
@ -10,12 +11,13 @@ class Dropbox : OAuth2API {
override val key = "zlqsamadlwydvb2"
override val redirectUrl = "dropboxlogin"
override val requiresLogin = true
override val supportDeviceAuth = false
override val createAccountUrl: String? = null
override val icon: Int
get() = TODO("Not yet implemented")
override fun authenticate() {
override fun authenticate(activity: FragmentActivity?) {
TODO("Not yet implemented")
}

View file

@ -1,264 +0,0 @@
package com.lagradost.cloudstream3.syncproviders.providers
import android.util.Log
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.imdbUrlToIdNullable
import com.lagradost.cloudstream3.subtitles.AbstractSubApi
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
import com.lagradost.cloudstream3.utils.SubtitleHelper
class IndexSubtitleApi : AbstractSubApi {
override val name = "IndexSubtitle"
override val idPrefix = "indexsubtitle"
override val requiresLogin = false
override val icon: Nothing? = null
override val createAccountUrl: Nothing? = null
override fun loginInfo(): Nothing? = null
override fun logOut() {}
companion object {
const val host = "https://indexsubtitle.com"
const val TAG = "INDEXSUBS"
}
private fun fixUrl(url: String): String {
if (url.startsWith("http")) {
return url
}
if (url.isEmpty()) {
return ""
}
val startsWithNoHttp = url.startsWith("//")
if (startsWithNoHttp) {
return "https:$url"
} else {
if (url.startsWith('/')) {
return host + url
}
return "$host/$url"
}
}
private fun getOrdinal(num: Int?): String? {
return when (num) {
1 -> "First"
2 -> "Second"
3 -> "Third"
4 -> "Fourth"
5 -> "Fifth"
6 -> "Sixth"
7 -> "Seventh"
8 -> "Eighth"
9 -> "Ninth"
10 -> "Tenth"
11 -> "Eleventh"
12 -> "Twelfth"
13 -> "Thirteenth"
14 -> "Fourteenth"
15 -> "Fifteenth"
16 -> "Sixteenth"
17 -> "Seventeenth"
18 -> "Eighteenth"
19 -> "Nineteenth"
20 -> "Twentieth"
21 -> "Twenty-First"
22 -> "Twenty-Second"
23 -> "Twenty-Third"
24 -> "Twenty-Fourth"
25 -> "Twenty-Fifth"
26 -> "Twenty-Sixth"
27 -> "Twenty-Seventh"
28 -> "Twenty-Eighth"
29 -> "Twenty-Ninth"
30 -> "Thirtieth"
31 -> "Thirty-First"
32 -> "Thirty-Second"
33 -> "Thirty-Third"
34 -> "Thirty-Fourth"
35 -> "Thirty-Fifth"
else -> null
}
}
private fun isRightEps(text: String, seasonNum: Int?, epNum: Int?): Boolean {
val FILTER_EPS_REGEX =
Regex("(?i)((Chapter\\s?0?${epNum})|((Season)?\\s?0?${seasonNum}?\\s?(Episode)\\s?0?${epNum}[^0-9]))|(?i)((S?0?${seasonNum}?E0?${epNum}[^0-9])|(0?${seasonNum}[a-z]0?${epNum}[^0-9]))")
return text.contains(FILTER_EPS_REGEX)
}
private fun haveEps(text: String): Boolean {
val HAVE_EPS_REGEX =
Regex("(?i)((Chapter\\s?0?\\d)|((Season)?\\s?0?\\d?\\s?(Episode)\\s?0?\\d))|(?i)((S?0?\\d?E0?\\d)|(0?\\d[a-z]0?\\d))")
return text.contains(HAVE_EPS_REGEX)
}
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity> {
val imdbId = query.imdb ?: 0
val lang = query.lang
val queryLang = SubtitleHelper.fromTwoLettersToLanguage(lang.toString())
val queryText = query.query
val epNum = query.epNumber ?: 0
val seasonNum = query.seasonNumber ?: 0
val yearNum = query.year ?: 0
val urlItems = ArrayList<String>()
fun cleanResources(
results: MutableList<AbstractSubtitleEntities.SubtitleEntity>,
name: String,
link: String
) {
results.add(
AbstractSubtitleEntities.SubtitleEntity(
idPrefix = idPrefix,
name = name,
lang = queryLang.toString(),
data = link,
source = this.name,
type = if (seasonNum > 0) TvType.TvSeries else TvType.Movie,
epNumber = epNum,
seasonNumber = seasonNum,
year = yearNum
)
)
}
val document = app.get("$host/?search=$queryText").document
document.select("div.my-3.p-3 div.media").map { block ->
if (seasonNum > 0) {
val name = block.select("strong.text-primary").text().trim()
val season = getOrdinal(seasonNum)
if ((block.selectFirst("a")?.attr("href")
?.contains(
"$season",
ignoreCase = true
)!! || name.contains(
"$season",
ignoreCase = true
)) && name.contains(queryText, ignoreCase = true)
) {
block.select("div.media").mapNotNull {
urlItems.add(
fixUrl(
it.selectFirst("a")!!.attr("href")
)
)
}
}
} else {
if (block.selectFirst("strong")!!.text().trim()
.matches(Regex("(?i)^$queryText\$"))
) {
if (block.select("span[title=Release]").isNullOrEmpty()) {
block.select("div.media").mapNotNull {
val urlItem = fixUrl(
it.selectFirst("a")!!.attr("href")
)
val itemDoc = app.get(urlItem).document
val id = imdbUrlToIdNullable(
itemDoc.selectFirst("div.d-flex span.badge.badge-primary")?.parent()
?.attr("href")
)?.toLongOrNull()
val year = itemDoc.selectFirst("div.d-flex span.badge.badge-success")
?.ownText()
?.trim().toString()
Log.i(TAG, "id => $id \nyear => $year||$yearNum")
if (imdbId > 0) {
if (id == imdbId) {
urlItems.add(urlItem)
}
} else {
if (year.contains("$yearNum")) {
urlItems.add(urlItem)
}
}
}
} else {
if (block.select("span[title=Release]").text().trim()
.contains("$yearNum")
) {
block.select("div.media").mapNotNull {
urlItems.add(
fixUrl(
it.selectFirst("a")!!.attr("href")
)
)
}
}
}
}
}
}
Log.i(TAG, "urlItems => $urlItems")
val results = mutableListOf<AbstractSubtitleEntities.SubtitleEntity>()
urlItems.forEach { url ->
val request = app.get(url)
if (request.isSuccessful) {
request.document.select("div.my-3.p-3 div.media").map { block ->
if (block.select("span.d-block span[data-original-title=Language]").text()
.trim()
.contains("$queryLang")
) {
var name = block.select("strong.text-primary").text().trim()
val link = fixUrl(block.selectFirst("a")!!.attr("href"))
if (seasonNum > 0) {
when {
isRightEps(name, seasonNum, epNum) -> {
cleanResources(results, name, link)
}
!(haveEps(name)) -> {
name = "$name (S${seasonNum}:E${epNum})"
cleanResources(results, name, link)
}
}
} else {
cleanResources(results, name, link)
}
}
}
}
}
return results
}
override suspend fun load(data: AbstractSubtitleEntities.SubtitleEntity): String? {
val seasonNum = data.seasonNumber
val epNum = data.epNumber
val req = app.get(data.data)
if (req.isSuccessful) {
val document = req.document
val link = if (document.select("div.my-3.p-3 div.media").size == 1) {
fixUrl(
document.selectFirst("div.my-3.p-3 div.media a")!!.attr("href")
)
} else {
document.select("div.my-3.p-3 div.media").mapNotNull { block ->
val name =
block.selectFirst("strong.d-block.text-primary")?.text()?.trim().toString()
if (seasonNum!! > 0) {
if (isRightEps(name, seasonNum, epNum)) {
fixUrl(block.selectFirst("a")!!.attr("href"))
} else {
null
}
} else {
fixUrl(block.selectFirst("a")!!.attr("href"))
}
}.first()
}
return link
}
return null
}
}

View file

@ -0,0 +1,134 @@
package com.lagradost.cloudstream3.syncproviders.providers
import androidx.fragment.app.FragmentActivity
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.library.ListSorting
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllFavorites
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
class LocalList : SyncAPI {
override val name = "Local"
override val icon: Int = R.drawable.ic_baseline_storage_24
override val requiresLogin = false
override val supportDeviceAuth = false
override val createAccountUrl: Nothing? = null
override val idPrefix = "local"
override var requireLibraryRefresh = true
override fun loginInfo(): AuthAPI.LoginInfo {
return AuthAPI.LoginInfo(
null,
null,
0
)
}
override fun logOut() {
}
override val key: String = ""
override val redirectUrl = ""
override suspend fun handleRedirect(url: String): Boolean {
return true
}
override fun authenticate(activity: FragmentActivity?) {
}
override val mainUrl = ""
override val syncIdName = SyncIdName.LocalList
override suspend fun score(id: String, status: SyncAPI.AbstractSyncStatus): Boolean {
return true
}
override suspend fun getStatus(id: String): SyncAPI.AbstractSyncStatus? {
return null
}
override suspend fun getResult(id: String): SyncAPI.SyncResult? {
return null
}
override suspend fun search(name: String): List<SyncAPI.SyncSearchResult>? {
return null
}
override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata? {
val watchStatusIds = ioWork {
getAllWatchStateIds()?.map { id ->
Pair(id, getResultWatchState(id))
}
}?.distinctBy { it.first } ?: return null
val list = ioWork {
val isTrueTv = isLayout(TV)
val baseMap = WatchType.entries.filter { it != WatchType.NONE }.associate {
// None is not something to display
it.stringRes to emptyList<SyncAPI.LibraryItem>()
} + mapOf(
R.string.favorites_list_name to emptyList()
) + if (!isTrueTv) {
mapOf(
R.string.subscription_list_name to emptyList()
)
} else {
emptyMap()
}
val watchStatusMap = watchStatusIds.groupBy { it.second.stringRes }.mapValues { group ->
group.value.mapNotNull {
getBookmarkedData(it.first)?.toLibraryItem(it.first.toString())
}
}
val favoritesMap = mapOf(R.string.favorites_list_name to getAllFavorites().mapNotNull {
it.toLibraryItem()
})
// Don't show subscriptions on TV
val result = if (isTrueTv) {
baseMap + watchStatusMap + favoritesMap
} else {
val subscriptionsMap = mapOf(R.string.subscription_list_name to getAllSubscriptions().mapNotNull {
it.toLibraryItem()
})
baseMap + watchStatusMap + subscriptionsMap + favoritesMap
}
result
}
return SyncAPI.LibraryMetadata(
list.map { SyncAPI.LibraryList(txt(it.key), it.value) },
setOf(
ListSorting.AlphabeticalA,
ListSorting.AlphabeticalZ,
ListSorting.UpdatedNew,
ListSorting.UpdatedOld,
ListSorting.ReleaseDateNew,
ListSorting.ReleaseDateOld,
// ListSorting.RatingHigh,
// ListSorting.RatingLow,
)
)
}
override fun getIdFromUrl(url: String): String {
return url
}
}

View file

@ -1,25 +1,37 @@
package com.lagradost.cloudstream3.syncproviders.providers
import android.util.Base64
import androidx.annotation.StringRes
import androidx.fragment.app.FragmentActivity
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.ShowStatus
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.ui.SyncWatchType
import com.lagradost.cloudstream3.ui.library.ListSorting
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.AppContextUtils.splitQuery
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
import java.net.URL
import java.security.SecureRandom
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
import java.time.Instant
import java.time.format.DateTimeFormatter
import java.util.Calendar
import java.util.Date
import java.util.Locale
import java.util.TimeZone
/** max 100 via https://myanimelist.net/apiconfig/references/api/v2#tag/anime */
const val MAL_MAX_SEARCH_LIMIT = 25
@ -30,18 +42,20 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
override val redirectUrl = "mallogin"
override val idPrefix = "mal"
override var mainUrl = "https://myanimelist.net"
val apiUrl = "https://api.myanimelist.net"
private val apiUrl = "https://api.myanimelist.net"
override val icon = R.drawable.mal_logo
override val requiresLogin = false
override val supportDeviceAuth = false
override val syncIdName = SyncIdName.MyAnimeList
override var requireLibraryRefresh = true
override val createAccountUrl = "$mainUrl/register.php"
override fun logOut() {
requireLibraryRefresh = true
removeAccountKeys()
}
override fun loginInfo(): AuthAPI.LoginInfo? {
//getMalUser(true)?
getKey<MalUser>(accountId, MAL_USER_KEY)?.let { user ->
return AuthAPI.LoginInfo(
profilePicture = user.picture,
@ -74,7 +88,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
this.name,
node.id.toString(),
"$mainUrl/anime/${node.id}/",
node.main_picture?.large ?: node.main_picture?.medium
node.mainPicture?.large ?: node.mainPicture?.medium
)
}
}
@ -83,13 +97,15 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
return Regex("""/anime/((.*)/|(.*))""").find(url)!!.groupValues.first()
}
override suspend fun score(id: String, status: SyncAPI.SyncStatus): Boolean {
override suspend fun score(id: String, status: SyncAPI.AbstractSyncStatus): Boolean {
return setScoreRequest(
id.toIntOrNull() ?: return false,
fromIntToAnimeStatus(status.status),
fromIntToAnimeStatus(status.status.internalId),
status.score,
status.watchedEpisodes
)
).also {
requireLibraryRefresh = requireLibraryRefresh || it
}
}
data class MalAnime(
@ -166,7 +182,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
private fun parseDate(string: String?): Long? {
return try {
SimpleDateFormat("yyyy-MM-dd")?.parse(string ?: return null)?.time
SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse(string ?: return null)?.time
} catch (e: Exception) {
null
}
@ -178,7 +194,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
apiName = this.name,
syncId = node.id.toString(),
url = "$mainUrl/anime/${node.id}",
posterUrl = node.main_picture?.large
posterUrl = node.mainPicture?.large
)
}
@ -232,12 +248,12 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
val internalId = id.toIntOrNull() ?: return null
val data =
getDataAboutMalId(internalId)?.my_list_status //?: throw ErrorLoadingException("No my_list_status")
getDataAboutMalId(internalId)?.myListStatus //?: throw ErrorLoadingException("No my_list_status")
return SyncAPI.SyncStatus(
score = data?.score,
status = malStatusAsString.indexOf(data?.status),
status = SyncWatchType.fromInternalId(malStatusAsString.indexOf(data?.status)),
isFavorite = null,
watchedEpisodes = data?.num_episodes_watched,
watchedEpisodes = data?.numEpisodesWatched,
)
}
@ -247,15 +263,50 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
const val MAL_USER_KEY: String = "mal_user" // user data like profile
const val MAL_CACHED_LIST: String = "mal_cached_list"
const val MAL_SHOULD_UPDATE_LIST: String = "mal_should_update_list"
const val MAL_UNIXTIME_KEY: String = "mal_unixtime" // When token expires
const val MAL_REFRESH_TOKEN_KEY: String = "mal_refresh_token" // refresh token
const val MAL_TOKEN_KEY: String = "mal_token" // anilist token for api
fun convertToStatus(string: String): MalStatusType {
return fromIntToAnimeStatus(malStatusAsString.indexOf(string))
}
enum class MalStatusType(var value: Int, @StringRes val stringRes: Int) {
Watching(0, R.string.type_watching),
Completed(1, R.string.type_completed),
OnHold(2, R.string.type_on_hold),
Dropped(3, R.string.type_dropped),
PlanToWatch(4, R.string.type_plan_to_watch),
None(-1, R.string.type_none)
}
private fun fromIntToAnimeStatus(inp: Int): MalStatusType {//= AniListStatusType.values().first { it.value == inp }
return when (inp) {
-1 -> MalStatusType.None
0 -> MalStatusType.Watching
1 -> MalStatusType.Completed
2 -> MalStatusType.OnHold
3 -> MalStatusType.Dropped
4 -> MalStatusType.PlanToWatch
5 -> MalStatusType.Watching
else -> MalStatusType.None
}
}
private fun parseDateLong(string: String?): Long? {
return try {
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()).parse(
string ?: return null
)?.time?.div(1000)
} catch (e: Exception) {
null
}
}
}
override suspend fun handleRedirect(url: String): Boolean {
val sanitizer =
splitQuery(URL(url.replace(appString, "https").replace("/#", "?"))) // FIX ERROR
splitQuery(URL(url.replace(APP_STRING, "https").replace("/#", "?"))) // FIX ERROR
val state = sanitizer["state"]!!
if (state == "RequestID$requestId") {
val currentCode = sanitizer["code"]!!
@ -274,14 +325,14 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
switchToNewAccount()
storeToken(res)
val user = getMalUser()
setKey(MAL_SHOULD_UPDATE_LIST, true)
requireLibraryRefresh = true
return user != null
}
}
return false
}
override fun authenticate() {
override fun authenticate(activity: FragmentActivity?) {
// It is recommended to use a URL-safe string as code_verifier.
// See section 4 of RFC 7636 for more details.
@ -294,7 +345,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
val codeChallenge = codeVerifier
val request =
"$mainUrl/v1/oauth2/authorize?response_type=code&client_id=$key&code_challenge=$codeChallenge&state=RequestID$requestId"
openBrowser(request)
openBrowser(request, activity)
}
private var requestId = 0
@ -304,12 +355,13 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
try {
if (response != "") {
val token = parseJson<ResponseToken>(response)
setKey(accountId, MAL_UNIXTIME_KEY, (token.expires_in + unixTime))
setKey(accountId, MAL_REFRESH_TOKEN_KEY, token.refresh_token)
setKey(accountId, MAL_TOKEN_KEY, token.access_token)
setKey(accountId, MAL_UNIXTIME_KEY, (token.expiresIn + unixTime))
setKey(accountId, MAL_REFRESH_TOKEN_KEY, token.refreshToken)
setKey(accountId, MAL_TOKEN_KEY, token.accessToken)
requireLibraryRefresh = true
}
} catch (e: Exception) {
e.printStackTrace()
logError(e)
}
}
@ -328,7 +380,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
).text
storeToken(res)
} catch (e: Exception) {
e.printStackTrace()
logError(e)
}
}
@ -347,41 +399,65 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
data class Node(
@JsonProperty("id") val id: Int,
@JsonProperty("title") val title: String,
@JsonProperty("main_picture") val main_picture: MainPicture?,
@JsonProperty("alternative_titles") val alternative_titles: AlternativeTitles?,
@JsonProperty("media_type") val media_type: String?,
@JsonProperty("num_episodes") val num_episodes: Int?,
@JsonProperty("main_picture") val mainPicture: MainPicture?,
@JsonProperty("alternative_titles") val alternativeTitles: AlternativeTitles?,
@JsonProperty("media_type") val mediaType: String?,
@JsonProperty("num_episodes") val numEpisodes: Int?,
@JsonProperty("status") val status: String?,
@JsonProperty("start_date") val start_date: String?,
@JsonProperty("end_date") val end_date: String?,
@JsonProperty("average_episode_duration") val average_episode_duration: Int?,
@JsonProperty("start_date") val startDate: String?,
@JsonProperty("end_date") val endDate: String?,
@JsonProperty("average_episode_duration") val averageEpisodeDuration: Int?,
@JsonProperty("synopsis") val synopsis: String?,
@JsonProperty("mean") val mean: Double?,
@JsonProperty("genres") val genres: List<Genres>?,
@JsonProperty("rank") val rank: Int?,
@JsonProperty("popularity") val popularity: Int?,
@JsonProperty("num_list_users") val num_list_users: Int?,
@JsonProperty("num_favorites") val num_favorites: Int?,
@JsonProperty("num_scoring_users") val num_scoring_users: Int?,
@JsonProperty("start_season") val start_season: StartSeason?,
@JsonProperty("num_list_users") val numListUsers: Int?,
@JsonProperty("num_favorites") val numFavorites: Int?,
@JsonProperty("num_scoring_users") val numScoringUsers: Int?,
@JsonProperty("start_season") val startSeason: StartSeason?,
@JsonProperty("broadcast") val broadcast: Broadcast?,
@JsonProperty("nsfw") val nsfw: String?,
@JsonProperty("created_at") val created_at: String?,
@JsonProperty("updated_at") val updated_at: String?
@JsonProperty("created_at") val createdAt: String?,
@JsonProperty("updated_at") val updatedAt: String?
)
data class ListStatus(
@JsonProperty("status") val status: String?,
@JsonProperty("score") val score: Int,
@JsonProperty("num_episodes_watched") val num_episodes_watched: Int,
@JsonProperty("is_rewatching") val is_rewatching: Boolean,
@JsonProperty("updated_at") val updated_at: String,
@JsonProperty("num_episodes_watched") val numEpisodesWatched: Int,
@JsonProperty("is_rewatching") val isRewatching: Boolean,
@JsonProperty("updated_at") val updatedAt: String,
)
data class Data(
@JsonProperty("node") val node: Node,
@JsonProperty("list_status") val list_status: ListStatus?,
)
@JsonProperty("list_status") val listStatus: ListStatus?,
) {
fun toLibraryItem(): SyncAPI.LibraryItem {
return SyncAPI.LibraryItem(
this.node.title,
"https://myanimelist.net/anime/${this.node.id}/",
this.node.id.toString(),
this.listStatus?.numEpisodesWatched,
this.node.numEpisodes,
this.listStatus?.score?.times(10),
parseDateLong(this.listStatus?.updatedAt),
"MAL",
TvType.Anime,
this.node.mainPicture?.large ?: this.node.mainPicture?.medium,
null,
null,
plot = this.node.synopsis,
releaseDate = if (this.node.startDate == null) null else try {Date.from(
Instant.from(
DateTimeFormatter.ofPattern(if (this.node.startDate.length == 4) "yyyy" else if (this.node.startDate.length == 7) "yyyy-MM" else "yyyy-MM-dd")
.parse(this.node.startDate)
)
)} catch (_: RuntimeException) {null}
)
}
}
data class Paging(
@JsonProperty("next") val next: String?
@ -404,26 +480,53 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
)
data class Broadcast(
@JsonProperty("day_of_the_week") val day_of_the_week: String?,
@JsonProperty("start_time") val start_time: String?
@JsonProperty("day_of_the_week") val dayOfTheWeek: String?,
@JsonProperty("start_time") val startTime: String?
)
private fun getMalAnimeListCached(): Array<Data>? {
return getKey(MAL_CACHED_LIST) as? Array<Data>
}
suspend fun getMalAnimeListSmart(): Array<Data>? {
private suspend fun getMalAnimeListSmart(): Array<Data>? {
if (getAuth() == null) return null
return if (getKey(MAL_SHOULD_UPDATE_LIST, true) == true) {
return if (requireLibraryRefresh) {
val list = getMalAnimeList()
setKey(MAL_CACHED_LIST, list)
setKey(MAL_SHOULD_UPDATE_LIST, false)
list
} else {
getMalAnimeListCached()
}
}
override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata {
val list = getMalAnimeListSmart()?.groupBy {
convertToStatus(it.listStatus?.status ?: "").stringRes
}?.mapValues { group ->
group.value.map { it.toLibraryItem() }
} ?: emptyMap()
// To fill empty lists when MAL does not return them
val baseMap =
MalStatusType.entries.filter { it.value >= 0 }.associate {
it.stringRes to emptyList<SyncAPI.LibraryItem>()
}
return SyncAPI.LibraryMetadata(
(baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) },
setOf(
ListSorting.AlphabeticalA,
ListSorting.AlphabeticalZ,
ListSorting.UpdatedNew,
ListSorting.UpdatedOld,
ListSorting.ReleaseDateNew,
ListSorting.ReleaseDateOld,
ListSorting.RatingHigh,
ListSorting.RatingLow,
)
)
}
private suspend fun getMalAnimeList(): Array<Data> {
checkMalToken()
var offset = 0
@ -439,10 +542,6 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
return fullList.toTypedArray()
}
fun convertToStatus(string: String): MalStatusType {
return fromIntToAnimeStatus(malStatusAsString.indexOf(string))
}
private suspend fun getMalAnimeListSlice(offset: Int = 0): MalList? {
val user = "@me"
val auth = getAuth() ?: return null
@ -486,7 +585,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
).text
val values = parseJson<MalRoot>(res)
val titles =
values.data.map { MalTitleHolder(it.list_status, it.node.id, it.node.title) }
values.data.map { MalTitleHolder(it.listStatus, it.node.id, it.node.title) }
for (t in titles) {
allTitles[t.id] = t
}
@ -495,11 +594,13 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
}
}
fun convertJapanTimeToTimeRemaining(date: String, endDate: String? = null): String? {
private fun convertJapanTimeToTimeRemaining(date: String, endDate: String? = null): String? {
// No time remaining if the show has already ended
try {
endDate?.let {
if (SimpleDateFormat("yyyy-MM-dd").parse(it).time < System.currentTimeMillis()) return@convertJapanTimeToTimeRemaining null
if (SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse(it)
?.before(Date.from(Instant.now())) != false
) return@convertJapanTimeToTimeRemaining null
}
} catch (e: ParseException) {
logError(e)
@ -516,7 +617,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
val currentWeek = currentDate.get(Calendar.WEEK_OF_MONTH)
val currentYear = currentDate.get(Calendar.YEAR)
val dateFormat = SimpleDateFormat("yyyy MM W EEEE HH:mm")
val dateFormat = SimpleDateFormat("yyyy MM W EEEE HH:mm", Locale.getDefault())
dateFormat.timeZone = TimeZone.getTimeZone("Japan")
val parsedDate =
dateFormat.parse("$currentYear $currentMonth $currentWeek $date") ?: return null
@ -556,39 +657,17 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
return user
}
enum class MalStatusType(var value: Int) {
Watching(0),
Completed(1),
OnHold(2),
Dropped(3),
PlanToWatch(4),
None(-1)
}
private fun fromIntToAnimeStatus(inp: Int): MalStatusType {//= AniListStatusType.values().first { it.value == inp }
return when (inp) {
-1 -> MalStatusType.None
0 -> MalStatusType.Watching
1 -> MalStatusType.Completed
2 -> MalStatusType.OnHold
3 -> MalStatusType.Dropped
4 -> MalStatusType.PlanToWatch
5 -> MalStatusType.Watching
else -> MalStatusType.None
}
}
private suspend fun setScoreRequest(
id: Int,
status: MalStatusType? = null,
score: Int? = null,
num_watched_episodes: Int? = null,
numWatchedEpisodes: Int? = null,
): Boolean {
val res = setScoreRequest(
id,
if (status == null) null else malStatusAsString[maxOf(0, status.value)],
score,
num_watched_episodes
numWatchedEpisodes
)
return if (res.isNullOrBlank()) {
@ -605,17 +684,18 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
}
}
@Suppress("UNCHECKED_CAST")
private suspend fun setScoreRequest(
id: Int,
status: String? = null,
score: Int? = null,
num_watched_episodes: Int? = null,
numWatchedEpisodes: Int? = null,
): String? {
val data = mapOf(
"status" to status,
"score" to score?.toString(),
"num_watched_episodes" to num_watched_episodes?.toString()
).filter { it.value != null } as Map<String, String>
"num_watched_episodes" to numWatchedEpisodes?.toString()
).filterValues { it != null } as Map<String, String>
return app.put(
"$apiUrl/v2/anime/$id/my_list_status",
@ -628,10 +708,10 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
data class ResponseToken(
@JsonProperty("token_type") val token_type: String,
@JsonProperty("expires_in") val expires_in: Int,
@JsonProperty("access_token") val access_token: String,
@JsonProperty("refresh_token") val refresh_token: String,
@JsonProperty("token_type") val tokenType: String,
@JsonProperty("expires_in") val expiresIn: Int,
@JsonProperty("access_token") val accessToken: String,
@JsonProperty("refresh_token") val refreshToken: String,
)
data class MalRoot(
@ -640,7 +720,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
data class MalDatum(
@JsonProperty("node") val node: MalNode,
@JsonProperty("list_status") val list_status: MalStatus,
@JsonProperty("list_status") val listStatus: MalStatus,
)
data class MalNode(
@ -657,16 +737,16 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
data class MalStatus(
@JsonProperty("status") val status: String,
@JsonProperty("score") val score: Int,
@JsonProperty("num_episodes_watched") val num_episodes_watched: Int,
@JsonProperty("is_rewatching") val is_rewatching: Boolean,
@JsonProperty("updated_at") val updated_at: String,
@JsonProperty("num_episodes_watched") val numEpisodesWatched: Int,
@JsonProperty("is_rewatching") val isRewatching: Boolean,
@JsonProperty("updated_at") val updatedAt: String,
)
data class MalUser(
@JsonProperty("id") val id: Int,
@JsonProperty("name") val name: String,
@JsonProperty("location") val location: String,
@JsonProperty("joined_at") val joined_at: String,
@JsonProperty("joined_at") val joinedAt: String,
@JsonProperty("picture") val picture: String?,
)
@ -679,9 +759,9 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
data class SmallMalAnime(
@JsonProperty("id") val id: Int,
@JsonProperty("title") val title: String?,
@JsonProperty("num_episodes") val num_episodes: Int,
@JsonProperty("my_list_status") val my_list_status: MalStatus?,
@JsonProperty("main_picture") val main_picture: MalMainPicture?,
@JsonProperty("num_episodes") val numEpisodes: Int,
@JsonProperty("my_list_status") val myListStatus: MalStatus?,
@JsonProperty("main_picture") val mainPicture: MalMainPicture?,
)
data class MalSearchNode(

View file

@ -2,12 +2,13 @@ package com.lagradost.cloudstream3.syncproviders.providers
import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty
import com.google.common.collect.BiMap
import com.google.common.collect.HashBiMap
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.subtitles.AbstractSubApi
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
@ -15,6 +16,8 @@ import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPIManager
import com.lagradost.cloudstream3.utils.AppUtils
import okhttp3.Interceptor
import okhttp3.Response
class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi {
override val idPrefix = "opensubtitles"
@ -26,14 +29,31 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
companion object {
const val OPEN_SUBTITLES_USER_KEY: String = "open_subtitles_user" // user data like profile
const val apiKey = "uyBLgFD17MgrYmA0gSXoKllMJBelOYj2"
const val host = "https://api.opensubtitles.com/api/v1"
const val API_KEY = "uyBLgFD17MgrYmA0gSXoKllMJBelOYj2"
const val HOST = "https://api.opensubtitles.com/api/v1"
const val TAG = "OPENSUBS"
const val coolDownDuration: Long = 1000L * 30L // CoolDown if 429 error code in ms
const val COOLDOWN_DURATION: Long = 1000L * 30L // CoolDown if 429 error code in ms
var currentCoolDown: Long = 0L
var currentSession: SubtitleOAuthEntity? = null
}
private val headerInterceptor = OpenSubtitleInterceptor()
/** Automatically adds required api headers */
private class OpenSubtitleInterceptor : Interceptor {
/** Required user agent! */
private val userAgent = "Cloudstream3 v0.1"
override fun intercept(chain: Interceptor.Chain): Response {
return chain.proceed(
chain.request().newBuilder()
.removeHeader("user-agent")
.addHeader("user-agent", userAgent)
.addHeader("Api-Key", API_KEY)
.build()
)
}
}
private fun canDoRequest(): Boolean {
return unixTimeMs > currentCoolDown
}
@ -45,7 +65,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
}
private fun throwGotTooManyRequests() {
currentCoolDown = unixTimeMs + coolDownDuration
currentCoolDown = unixTimeMs + COOLDOWN_DURATION
throw ErrorLoadingException("Too many requests")
}
@ -94,15 +114,15 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
private suspend fun initLogin(username: String, password: String): Boolean {
//Log.i(TAG, "DATA = [$username] [$password]")
val response = app.post(
url = "$host/login",
url = "$HOST/login",
headers = mapOf(
"Api-Key" to apiKey,
"Content-Type" to "application/json"
"Content-Type" to "application/json",
),
data = mapOf(
"username" to username,
"password" to password
)
),
interceptor = headerInterceptor
)
//Log.i(TAG, "Responsecode = ${response.code}")
//Log.i(TAG, "Result => ${response.text}")
@ -113,7 +133,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
SubtitleOAuthEntity(
user = username,
pass = password,
access_token = token.token ?: run {
accessToken = token.token ?: run {
return false
})
)
@ -147,11 +167,13 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
// "pt" to "pt-PT",
// "pt" to "pt-BR"
)
private fun fixLanguage(language: String?) : String? {
private fun fixLanguage(language: String?): String? {
return languageExceptions[language] ?: language
}
// O(n) but good enough, BiMap did not want to work properly
private fun fixLanguageReverse(language: String?) : String? {
private fun fixLanguageReverse(language: String?): String? {
return languageExceptions.entries.firstOrNull { it.value == language }?.key ?: language
}
@ -163,8 +185,8 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
throwIfCantDoRequest()
val fixedLang = fixLanguage(query.lang)
val imdbId = query.imdb ?: 0
val queryText = query.query.replace(" ", "+")
val imdbId = query.imdbId?.replace("tt", "")?.toInt() ?: 0
val queryText = query.query
val epNum = query.epNumber ?: 0
val seasonNum = query.seasonNumber ?: 0
val yearNum = query.year ?: 0
@ -174,16 +196,16 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
val searchQueryUrl = when (imdbId > 0) {
//Use imdb_id to search if its valid
true -> "$host/subtitles?imdb_id=$imdbId&languages=${fixedLang}$yearQuery$epQuery$seasonQuery"
false -> "$host/subtitles?query=$queryText&languages=${fixedLang}$yearQuery$epQuery$seasonQuery"
true -> "$HOST/subtitles?imdb_id=$imdbId&languages=${fixedLang}$yearQuery$epQuery$seasonQuery"
false -> "$HOST/subtitles?query=${queryText}&languages=${fixedLang}$yearQuery$epQuery$seasonQuery"
}
val req = app.get(
url = searchQueryUrl,
headers = mapOf(
Pair("Api-Key", apiKey),
Pair("Content-Type", "application/json")
)
),
interceptor = headerInterceptor
)
Log.i(TAG, "Search Req => ${req.text}")
if (!req.isSuccessful) {
@ -198,15 +220,19 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
it.data?.forEach { item ->
val attr = item.attributes ?: return@forEach
val featureDetails = attr.featDetails
//Use filename as name, if its valid
val filename = attr.files?.firstNotNullOfOrNull { subfile ->
subfile.fileName
}
//Use any valid name/title in hierarchy
val name = featureDetails?.movieName ?: featureDetails?.title
?: featureDetails?.parentTitle ?: attr.release ?: ""
val lang = fixLanguageReverse(attr.language)?: ""
val name = filename ?: featureDetails?.movieName ?: featureDetails?.title
?: featureDetails?.parentTitle ?: attr.release ?: query.query
val lang = fixLanguageReverse(attr.language) ?: ""
val resEpNum = featureDetails?.episodeNumber ?: query.epNumber
val resSeasonNum = featureDetails?.seasonNumber ?: query.seasonNumber
val year = featureDetails?.year ?: query.year
val type = if ((resSeasonNum ?: 0) > 0) TvType.TvSeries else TvType.Movie
val isHearingImpaired = attr.hearing_impaired ?: false
val isHearingImpaired = attr.hearingImpaired ?: false
//Log.i(TAG, "Result id/name => ${item.id} / $name")
item.attributes?.files?.forEach { file ->
val resultData = file.fileId?.toString() ?: ""
@ -239,19 +265,19 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
throwIfCantDoRequest()
val req = app.post(
url = "$host/download",
url = "$HOST/download",
headers = mapOf(
Pair(
"Authorization",
"Bearer ${currentSession?.access_token ?: throw ErrorLoadingException("No access token active in current session")}"
"Bearer ${currentSession?.accessToken ?: throw ErrorLoadingException("No access token active in current session")}"
),
Pair("Api-Key", apiKey),
Pair("Content-Type", "application/json"),
Pair("Accept", "*/*")
),
data = mapOf(
Pair("file_id", data.data)
)
),
interceptor = headerInterceptor
)
Log.i(TAG, "Request result => (${req.code}) ${req.text}")
//Log.i(TAG, "Request headers => ${req.headers}")
@ -272,7 +298,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
data class SubtitleOAuthEntity(
var user: String,
var pass: String,
var access_token: String,
var accessToken: String,
)
data class OAuthToken(
@ -297,7 +323,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
@JsonProperty("url") var url: String? = null,
@JsonProperty("files") var files: List<ResultFiles>? = listOf(),
@JsonProperty("feature_details") var featDetails: ResultFeatureDetails? = ResultFeatureDetails(),
@JsonProperty("hearing_impaired") var hearing_impaired: Boolean? = null,
@JsonProperty("hearing_impaired") var hearingImpaired: Boolean? = null,
)
data class ResultFiles(
@ -328,4 +354,4 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
@JsonProperty("parent_tmdb_id") var parentTmdbId: Int? = null,
@JsonProperty("parent_feature_id") var parentFeatureId: Int? = null
)
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,159 @@
package com.lagradost.cloudstream3.syncproviders.providers
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.subtitles.AbstractSubProvider
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
import com.lagradost.cloudstream3.subtitles.SubtitleResource
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.SubtitleHelper
class SubSourceApi : AbstractSubProvider {
override val idPrefix = "subsource"
val name = "SubSource"
companion object {
const val APIURL = "https://api.subsource.net/api"
const val DOWNLOADENDPOINT = "https://api.subsource.net/api/downloadSub"
}
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity>? {
//Only supports Imdb Id search for now
if (query.imdbId == null) return null
val queryLang = SubtitleHelper.fromTwoLettersToLanguage(query.lang!!)
val type = if ((query.seasonNumber ?: 0) > 0) TvType.TvSeries else TvType.Movie
val searchRes = app.post(
url = "$APIURL/searchMovie",
data = mapOf(
"query" to query.imdbId!!
)
).parsedSafe<ApiSearch>() ?: return null
val postData = if (type == TvType.TvSeries) {
mapOf(
"langs" to "[]",
"movieName" to searchRes.found.first().linkName,
"season" to "season-${query.seasonNumber}"
)
} else {
mapOf(
"langs" to "[]",
"movieName" to searchRes.found.first().linkName,
)
}
val getMovieRes = app.post(
url = "$APIURL/getMovie",
data = postData
).parsedSafe<ApiResponse>().let {
// api doesn't has episode number or lang filtering
if (type == TvType.Movie) {
it?.subs?.filter { sub ->
sub.lang == queryLang
}
} else {
it?.subs?.filter { sub ->
sub.releaseName!!.contains(
String.format(
null,
"E%02d",
query.epNumber
)
) && sub.lang == queryLang
}
}
} ?: return null
return getMovieRes.map { subtitle ->
AbstractSubtitleEntities.SubtitleEntity(
idPrefix = this.idPrefix,
name = subtitle.releaseName!!,
lang = subtitle.lang!!,
data = SubData(
movie = subtitle.linkName!!,
lang = subtitle.lang,
id = subtitle.subId.toString(),
).toJson(),
type = type,
source = this.name,
epNumber = query.epNumber,
seasonNumber = query.seasonNumber,
isHearingImpaired = subtitle.hi == 1,
)
}
}
override suspend fun SubtitleResource.getResources(data: AbstractSubtitleEntities.SubtitleEntity) {
val parsedSub = parseJson<SubData>(data.data)
val subRes = app.post(
url = "$APIURL/getSub",
data = mapOf(
"movie" to parsedSub.movie,
"lang" to data.lang,
"id" to parsedSub.id
)
).parsedSafe<SubTitleLink>() ?: return
this.addZipUrl(
"$DOWNLOADENDPOINT/${subRes.sub.downloadToken}"
) { name, _ ->
name
}
}
data class ApiSearch(
@JsonProperty("success") val success: Boolean,
@JsonProperty("found") val found: List<Found>,
)
data class Found(
@JsonProperty("id") val id: Long,
@JsonProperty("title") val title: String,
@JsonProperty("seasons") val seasons: Long,
@JsonProperty("type") val type: String,
@JsonProperty("releaseYear") val releaseYear: Long,
@JsonProperty("linkName") val linkName: String,
)
data class ApiResponse(
@JsonProperty("success") val success: Boolean,
@JsonProperty("movie") val movie: Movie,
@JsonProperty("subs") val subs: List<Sub>,
)
data class Movie(
@JsonProperty("id") val id: Long? = null,
@JsonProperty("type") val type: String? = null,
@JsonProperty("year") val year: Long? = null,
@JsonProperty("fullName") val fullName: String? = null,
)
data class Sub(
@JsonProperty("hi") val hi: Int? = null,
@JsonProperty("fullLink") val fullLink: String? = null,
@JsonProperty("linkName") val linkName: String? = null,
@JsonProperty("lang") val lang: String? = null,
@JsonProperty("releaseName") val releaseName: String? = null,
@JsonProperty("subId") val subId: Long? = null,
)
data class SubData(
@JsonProperty("movie") val movie: String,
@JsonProperty("lang") val lang: String,
@JsonProperty("id") val id: String,
)
data class SubTitleLink(
@JsonProperty("sub") val sub: SubToken,
)
data class SubToken(
@JsonProperty("downloadToken") val downloadToken: String,
)
}

View file

@ -0,0 +1,247 @@
package com.lagradost.cloudstream3.syncproviders.providers
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.subtitles.AbstractSubApi
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities
import com.lagradost.cloudstream3.subtitles.SubtitleResource
import com.lagradost.cloudstream3.syncproviders.AuthAPI.LoginInfo
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPIManager
class SubDlApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi {
override val idPrefix = "subdl"
override val name = "SubDL"
override val icon = R.drawable.subdl_logo_big
override val requiresPassword = true
override val requiresEmail = true
override val createAccountUrl = "https://subdl.com/login"
companion object {
const val APIURL = "https://api.subdl.com"
const val APIENDPOINT = "$APIURL/api/v1/subtitles"
const val DOWNLOADENDPOINT = "https://dl.subdl.com"
const val SUBDL_SUBTITLES_USER_KEY: String = "subdl_user"
var currentSession: SubtitleOAuthEntity? = null
}
override suspend fun initialize() {
currentSession = getAuthKey()
}
override fun logOut() {
setAuthKey(null)
removeAccountKeys()
currentSession = getAuthKey()
}
override suspend fun login(data: InAppAuthAPI.LoginData): Boolean {
val email = data.email ?: throw ErrorLoadingException("Requires Email")
val password = data.password ?: throw ErrorLoadingException("Requires Password")
switchToNewAccount()
try {
if (initLogin(email, password)) {
registerAccount()
return true
}
} catch (e: Exception) {
logError(e)
switchToOldAccount()
}
switchToOldAccount()
return false
}
override fun getLatestLoginData(): InAppAuthAPI.LoginData? {
val current = getAuthKey() ?: return null
return InAppAuthAPI.LoginData(
email = current.userEmail,
password = current.pass
)
}
override fun loginInfo(): LoginInfo? {
getAuthKey()?.let { user ->
return LoginInfo(
profilePicture = null,
name = user.name ?: user.userEmail,
accountIndex = accountIndex
)
}
return null
}
override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List<AbstractSubtitleEntities.SubtitleEntity>? {
val queryText = query.query
val epNum = query.epNumber ?: 0
val seasonNum = query.seasonNumber ?: 0
val yearNum = query.year ?: 0
val idQuery = when {
query.imdbId != null -> "&imdb_id=${query.imdbId}"
query.tmdbId != null -> "&tmdb_id=${query.tmdbId}"
else -> null
}
val epQuery = if (epNum > 0) "&episode_number=$epNum" else ""
val seasonQuery = if (seasonNum > 0) "&season_number=$seasonNum" else ""
val yearQuery = if (yearNum > 0) "&year=$yearNum" else ""
val searchQueryUrl = when (idQuery) {
//Use imdb/tmdb id to search if its valid
null -> "$APIENDPOINT?api_key=${currentSession?.apiKey}&film_name=$queryText&languages=${query.lang}$epQuery$seasonQuery$yearQuery"
else -> "$APIENDPOINT?api_key=${currentSession?.apiKey}$idQuery&languages=${query.lang}$epQuery$seasonQuery$yearQuery"
}
val req = app.get(
url = searchQueryUrl,
headers = mapOf(
"Accept" to "application/json"
)
)
return req.parsedSafe<ApiResponse>()?.subtitles?.map { subtitle ->
val lang = subtitle.lang.replaceFirstChar { it.uppercase() }
val resEpNum = subtitle.episode ?: query.epNumber
val resSeasonNum = subtitle.season ?: query.seasonNumber
val type = if ((resSeasonNum ?: 0) > 0) TvType.TvSeries else TvType.Movie
AbstractSubtitleEntities.SubtitleEntity(
idPrefix = this.idPrefix,
name = subtitle.releaseName,
lang = lang,
data = "${DOWNLOADENDPOINT}${subtitle.url}",
type = type,
source = this.name,
epNumber = resEpNum,
seasonNumber = resSeasonNum,
isHearingImpaired = subtitle.hearingImpaired ?: false,
)
}
}
override suspend fun SubtitleResource.getResources(data: AbstractSubtitleEntities.SubtitleEntity) {
this.addZipUrl(data.data) { name, _ ->
name
}
}
private suspend fun initLogin(useremail: String, password: String): Boolean {
val tokenResponse = app.post(
url = "$APIURL/login",
data = mapOf(
"email" to useremail,
"password" to password
)
).parsedSafe<OAuthTokenResponse>()
if (tokenResponse?.token == null) return false
val apiResponse = app.get(
url = "$APIURL/user/userApi",
headers = mapOf(
"Authorization" to "Bearer ${tokenResponse.token}"
)
).parsedSafe<ApiKeyResponse>()
if (apiResponse?.ok == false) return false
setAuthKey(
SubtitleOAuthEntity(
userEmail = useremail,
pass = password,
name = tokenResponse.userData?.username ?: tokenResponse.userData?.name,
accessToken = tokenResponse.token,
apiKey = apiResponse?.apiKey
)
)
return true
}
private fun getAuthKey(): SubtitleOAuthEntity? {
return getKey(accountId, SUBDL_SUBTITLES_USER_KEY)
}
private fun setAuthKey(data: SubtitleOAuthEntity?) {
if (data == null) removeKey(
accountId,
SUBDL_SUBTITLES_USER_KEY
)
currentSession = data
setKey(accountId, SUBDL_SUBTITLES_USER_KEY, data)
}
data class SubtitleOAuthEntity(
@JsonProperty("userEmail") var userEmail: String,
@JsonProperty("pass") var pass: String,
@JsonProperty("name") var name: String? = null,
@JsonProperty("accessToken") var accessToken: String? = null,
@JsonProperty("apiKey") var apiKey: String? = null,
)
data class OAuthTokenResponse(
@JsonProperty("token") val token: String? = null,
@JsonProperty("userData") val userData: UserData? = null,
@JsonProperty("status") val status: Boolean? = null,
@JsonProperty("message") val message: String? = null,
)
data class UserData(
@JsonProperty("email") val email: String,
@JsonProperty("name") val name: String,
@JsonProperty("country") val country: String,
@JsonProperty("scStepCode") val scStepCode: String,
@JsonProperty("scVerified") val scVerified: Boolean,
@JsonProperty("username") val username: String? = null,
@JsonProperty("scUsername") val scUsername: String,
)
data class ApiKeyResponse(
@JsonProperty("ok") val ok: Boolean? = false,
@JsonProperty("api_key") val apiKey: String? = null,
@JsonProperty("usage") val usage: Usage? = null,
)
data class Usage(
@JsonProperty("total") val total: Long? = 0,
@JsonProperty("today") val today: Long? = 0,
)
data class ApiResponse(
@JsonProperty("status") val status: Boolean? = null,
@JsonProperty("results") val results: List<Result>? = null,
@JsonProperty("subtitles") val subtitles: List<Subtitle>? = null,
)
data class Result(
@JsonProperty("sd_id") val sdId: Int? = null,
@JsonProperty("type") val type: String? = null,
@JsonProperty("name") val name: String? = null,
@JsonProperty("imdb_id") val imdbId: String? = null,
@JsonProperty("tmdb_id") val tmdbId: Long? = null,
@JsonProperty("first_air_date") val firstAirDate: String? = null,
@JsonProperty("year") val year: Int? = null,
)
data class Subtitle(
@JsonProperty("release_name") val releaseName: String,
@JsonProperty("name") val name: String,
@JsonProperty("lang") val lang: String,
@JsonProperty("author") val author: String? = null,
@JsonProperty("url") val url: String? = null,
@JsonProperty("subtitlePage") val subtitlePage: String? = null,
@JsonProperty("season") val season: Int? = null,
@JsonProperty("episode") val episode: Int? = null,
@JsonProperty("language") val language: String? = null,
@JsonProperty("hi") val hearingImpaired: Boolean? = null,
)
}

View file

@ -1,10 +1,27 @@
package com.lagradost.cloudstream3.ui
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.HomePageResponse
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.MainPageRequest
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.fixUrl
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
import com.lagradost.cloudstream3.utils.ExtractorLink
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.GlobalScope.coroutineContext
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
class APIRepository(val api: MainAPI) {
companion object {
@ -24,20 +41,67 @@ class APIRepository(val api: MainAPI) {
fun isInvalidData(data: String): Boolean {
return data.isEmpty() || data == "[]" || data == "about:blank"
}
data class SavedLoadResponse(
val unixTime: Long,
val response: LoadResponse,
val hash: Pair<String, String>
)
private val cache = threadSafeListOf<SavedLoadResponse>()
private var cacheIndex: Int = 0
const val CACHE_SIZE = 20
}
private fun afterPluginsLoaded(forceReload: Boolean) {
if (forceReload) {
synchronized(cache) {
cache.clear()
}
}
}
init {
afterPluginsLoadedEvent += ::afterPluginsLoaded
}
val hasMainPage = api.hasMainPage
val providerType = api.providerType
val name = api.name
val mainUrl = api.mainUrl
val mainPage = api.mainPage
val hasQuickSearch = api.hasQuickSearch
val vpnStatus = api.vpnStatus
val providerType = api.providerType
suspend fun load(url: String): Resource<LoadResponse> {
return safeApiCall {
if (isInvalidData(url)) throw ErrorLoadingException()
api.load(api.fixUrl(url)) ?: throw ErrorLoadingException()
val fixedUrl = api.fixUrl(url)
val lookingForHash = Pair(api.name, fixedUrl)
synchronized(cache) {
for (item in cache) {
// 10 min save
if (item.hash == lookingForHash && (unixTime - item.unixTime) < 60 * 10) {
return@safeApiCall item.response
}
}
}
api.load(fixedUrl)?.also { response ->
// Remove all blank tags as early as possible
response.tags = response.tags?.filter { it.isNotBlank() }
val add = SavedLoadResponse(unixTime, response, lookingForHash)
synchronized(cache) {
if (cache.size > CACHE_SIZE) {
cache[cacheIndex] = add // rolling cache
cacheIndex = (cacheIndex + 1) % CACHE_SIZE
} else {
cache.add(add)
}
}
} ?: throw ErrorLoadingException()
}
}
@ -62,12 +126,48 @@ class APIRepository(val api: MainAPI) {
}
}
suspend fun waitForHomeDelay() {
val delta = api.sequentialMainPageScrollDelay + api.lastHomepageRequest - unixTimeMS
if (delta < 0) return
delay(delta)
}
suspend fun getMainPage(page: Int, nameIndex: Int? = null): Resource<List<HomePageResponse?>> {
return safeApiCall {
api.lastHomepageRequest = unixTimeMS
nameIndex?.let { api.mainPage.getOrNull(it) }?.let { data ->
listOf(api.getMainPage(page, MainPageRequest(data.name, data.data)))
} ?: api.mainPage.apmap { data ->
api.getMainPage(page, MainPageRequest(data.name, data.data))
listOf(
api.getMainPage(
page,
MainPageRequest(data.name, data.data, data.horizontalImages)
)
)
} ?: run {
if (api.sequentialMainPage) {
var first = true
api.mainPage.map { data ->
if (!first) // dont want to sleep on first request
delay(api.sequentialMainPageDelay)
first = false
api.getMainPage(
page,
MainPageRequest(data.name, data.data, data.horizontalImages)
)
}
} else {
with(CoroutineScope(coroutineContext)) {
api.mainPage.map { data ->
async {
api.getMainPage(
page,
MainPageRequest(data.name, data.data, data.horizontalImages)
)
}
}.map { it.await() }
}
}
}
}
}
@ -82,7 +182,7 @@ class APIRepository(val api: MainAPI) {
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
callback: (ExtractorLink) -> Unit,
): Boolean {
if (isInvalidData(data)) return false // this makes providers cleaner
return try {

View file

@ -0,0 +1,252 @@
package com.lagradost.cloudstream3.ui
import android.view.View
import android.view.ViewGroup
import androidx.core.view.children
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModel
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import androidx.viewbinding.ViewBinding
import java.util.concurrent.CopyOnWriteArrayList
open class ViewHolderState<T>(val view: ViewBinding) : ViewHolder(view.root) {
open fun save(): T? = null
open fun restore(state: T) = Unit
open fun onViewAttachedToWindow() = Unit
open fun onViewDetachedFromWindow() = Unit
open fun onViewRecycled() = Unit
}
// Based of the concept https://github.com/brahmkshatriya/echo/blob/main/app%2Fsrc%2Fmain%2Fjava%2Fdev%2Fbrahmkshatriya%2Fecho%2Fui%2Fadapters%2FMediaItemsContainerAdapter.kt#L108-L154
class StateViewModel : ViewModel() {
val layoutManagerStates = hashMapOf<Int, HashMap<Int, Any?>>()
}
abstract class NoStateAdapter<T : Any>(fragment: Fragment) : BaseAdapter<T, Any>(fragment, 0)
/**
* BaseAdapter is a persistent state stored adapter that supports headers and footers.
* This should be used for restoring eg scroll or focus related to a view when it is recreated.
*
* Id is a per fragment based unique id used to store the underlying data done in an internal ViewModel.
*
* diffCallback is how the view should be handled when updating, override onUpdateContent for updates
*
* NOTE:
*
* By default it should save automatically, but you can also call save(recycle)
*
* By default no state is stored, but doing an id != 0 will store
*
* By default no headers or footers exist, override footers and headers count
*/
abstract class BaseAdapter<
T : Any,
S : Any>(
fragment: Fragment,
val id: Int = 0,
diffCallback: DiffUtil.ItemCallback<T> = BaseDiffCallback()
) : RecyclerView.Adapter<ViewHolderState<S>>() {
open val footers: Int = 0
open val headers: Int = 0
fun getItem(position: Int): T {
return mDiffer.currentList[position]
}
fun getItemOrNull(position: Int): T? {
return mDiffer.currentList.getOrNull(position)
}
private val mDiffer: AsyncListDiffer<T> = AsyncListDiffer(
object : NonFinalAdapterListUpdateCallback(this) {
override fun onMoved(fromPosition: Int, toPosition: Int) {
super.onMoved(fromPosition + headers, toPosition + headers)
}
override fun onRemoved(position: Int, count: Int) {
super.onRemoved(position + headers, count)
}
override fun onChanged(position: Int, count: Int, payload: Any?) {
super.onChanged(position + headers, count, payload)
}
override fun onInserted(position: Int, count: Int) {
super.onInserted(position + headers, count)
}
},
AsyncDifferConfig.Builder(diffCallback).build()
)
open fun submitList(list: List<T>?) {
// deep copy at least the top list, because otherwise adapter can go crazy
mDiffer.submitList(list?.let { CopyOnWriteArrayList(it) })
}
override fun getItemCount(): Int {
return mDiffer.currentList.size + footers + headers
}
open fun onUpdateContent(holder: ViewHolderState<S>, item: T, position: Int) =
onBindContent(holder, item, position)
open fun onBindContent(holder: ViewHolderState<S>, item: T, position: Int) = Unit
open fun onBindFooter(holder: ViewHolderState<S>) = Unit
open fun onBindHeader(holder: ViewHolderState<S>) = Unit
open fun onCreateContent(parent: ViewGroup): ViewHolderState<S> = throw NotImplementedError()
open fun onCreateFooter(parent: ViewGroup): ViewHolderState<S> = throw NotImplementedError()
open fun onCreateHeader(parent: ViewGroup): ViewHolderState<S> = throw NotImplementedError()
override fun onViewAttachedToWindow(holder: ViewHolderState<S>) {
holder.onViewAttachedToWindow()
}
override fun onViewDetachedFromWindow(holder: ViewHolderState<S>) {
holder.onViewDetachedFromWindow()
}
@Suppress("UNCHECKED_CAST")
fun save(recyclerView: RecyclerView) {
for (child in recyclerView.children) {
val holder =
recyclerView.findContainingViewHolder(child) as? ViewHolderState<S> ?: continue
setState(holder)
}
}
fun clear() {
stateViewModel.layoutManagerStates[id]?.clear()
}
@Suppress("UNCHECKED_CAST")
private fun getState(holder: ViewHolderState<S>): S? =
stateViewModel.layoutManagerStates[id]?.get(holder.absoluteAdapterPosition) as? S
private fun setState(holder: ViewHolderState<S>) {
if(id == 0) return
if (!stateViewModel.layoutManagerStates.contains(id)) {
stateViewModel.layoutManagerStates[id] = HashMap()
}
stateViewModel.layoutManagerStates[id]?.let { map ->
map[holder.absoluteAdapterPosition] = holder.save()
}
}
private val attachListener = object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) = Unit
override fun onViewDetachedFromWindow(v: View) {
if (v !is RecyclerView) return
save(v)
}
}
final override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
recyclerView.addOnAttachStateChangeListener(attachListener)
super.onAttachedToRecyclerView(recyclerView)
}
final override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
recyclerView.removeOnAttachStateChangeListener(attachListener)
super.onDetachedFromRecyclerView(recyclerView)
}
final override fun getItemViewType(position: Int): Int {
if (position < headers) {
return HEADER
}
if (position - headers >= mDiffer.currentList.size) {
return FOOTER
}
return CONTENT
}
private val stateViewModel: StateViewModel by fragment.viewModels()
final override fun onViewRecycled(holder: ViewHolderState<S>) {
setState(holder)
holder.onViewRecycled()
super.onViewRecycled(holder)
}
final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderState<S> {
return when (viewType) {
CONTENT -> onCreateContent(parent)
HEADER -> onCreateHeader(parent)
FOOTER -> onCreateFooter(parent)
else -> throw NotImplementedError()
}
}
// https://medium.com/@domen.lanisnik/efficiently-updating-recyclerview-items-using-payloads-1305f65f3068
override fun onBindViewHolder(
holder: ViewHolderState<S>,
position: Int,
payloads: MutableList<Any>
) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads)
return
}
when (getItemViewType(position)) {
CONTENT -> {
val realPosition = position - headers
val item = getItem(realPosition)
onUpdateContent(holder, item, realPosition)
}
FOOTER -> {
onBindFooter(holder)
}
HEADER -> {
onBindHeader(holder)
}
}
}
final override fun onBindViewHolder(holder: ViewHolderState<S>, position: Int) {
when (getItemViewType(position)) {
CONTENT -> {
val realPosition = position - headers
val item = getItem(realPosition)
onBindContent(holder, item, realPosition)
}
FOOTER -> {
onBindFooter(holder)
}
HEADER -> {
onBindHeader(holder)
}
}
getState(holder)?.let { state ->
holder.restore(state)
}
}
companion object {
private const val HEADER: Int = 1
private const val FOOTER: Int = 2
private const val CONTENT: Int = 0
}
}
class BaseDiffCallback<T : Any>(
val itemSame: (T, T) -> Boolean = { a, b -> a.hashCode() == b.hashCode() },
val contentSame: (T, T) -> Boolean = { a, b -> a.hashCode() == b.hashCode() }
) : DiffUtil.ItemCallback<T>() {
override fun areItemsTheSame(oldItem: T, newItem: T): Boolean = itemSame(oldItem, newItem)
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean = contentSame(oldItem, newItem)
override fun getChangePayload(oldItem: T, newItem: T): Any = Any()
}

View file

@ -6,9 +6,10 @@ import android.view.Menu
import android.view.View.*
import android.widget.*
import androidx.appcompat.app.AlertDialog
import androidx.media3.common.util.UnstableApi
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.kotlinModule
import com.google.android.gms.cast.MediaQueueItem
import com.google.android.gms.cast.MediaSeekOptions
import com.google.android.gms.cast.MediaStatus.REPEAT_MODE_REPEAT_OFF
@ -23,12 +24,13 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.sortSubs
import com.lagradost.cloudstream3.sortUrls
import com.lagradost.cloudstream3.ui.player.LoadType
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
import com.lagradost.cloudstream3.ui.player.SubtitleData
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.subtitles.ChromecastSubtitlesFragment
import com.lagradost.cloudstream3.utils.AppContextUtils.sortSubs
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.CastHelper.awaitLinks
import com.lagradost.cloudstream3.utils.CastHelper.getMediaInfo
@ -97,7 +99,7 @@ data class MetadataHolder(
class SelectSourceController(val view: ImageView, val activity: ControllerActivity) :
UIController() {
private val mapper: JsonMapper = JsonMapper.builder().addModule(KotlinModule())
private val mapper: JsonMapper = JsonMapper.builder().addModule(kotlinModule())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()
init {
@ -150,7 +152,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
} else {
ChromecastSubtitlesFragment.getCurrentSavedStyle().apply {
val font = TextTrackStyle()
font.fontFamily = fontFamily ?: "Google Sans"
font.setFontFamily(fontFamily ?: "Google Sans")
fontGenericFamily?.let {
font.fontGenericFamily = it
}
@ -183,7 +185,9 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
val contentUrl = (remoteMediaClient?.currentItem?.media?.contentUrl
?: remoteMediaClient?.currentItem?.media?.contentId)
val sortingMethods = items.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" }.toTypedArray()
val sortingMethods =
items.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" }
.toTypedArray()
val sotringIndex = items.indexOfFirst { it.url == contentUrl }
val arrayAdapter =
@ -260,6 +264,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
var isLoadingMore = false
override fun onMediaStatusUpdated() {
super.onMediaStatusUpdated()
val meta = getCurrentMetaData()
@ -279,8 +284,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
val currentPosition = remoteMediaClient?.approximateStreamPosition
if (currentDuration != null && currentPosition != null)
DataStoreHelper.setViewPos(epData.id, currentPosition, currentDuration)
} catch (e : Exception) {
logError(e)
} catch (t: Throwable) {
logError(t)
}
if (itemCount != null && itemCount - currentIdIndex == 1 && !isLoadingMore) {
@ -292,7 +297,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
val generator = RepoLinkGenerator(listOf(epData))
val isSuccessful = safeApiCall {
generator.generateLinks(clearCache = false, isCasting = true,
generator.generateLinks(
clearCache = false, type = LoadType.Chromecast,
callback = {
it.first?.let { link ->
currentLinks.add(link)
@ -358,10 +364,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
}
}
override fun onSessionConnected(castSession: CastSession?) {
castSession?.let {
super.onSessionConnected(it)
}
override fun onSessionConnected(castSession: CastSession) {
super.onSessionConnected(castSession)
remoteMediaClient?.queueSetRepeatMode(REPEAT_MODE_REPEAT_OFF, JSONObject())
}
}

View file

@ -3,11 +3,13 @@ package com.lagradost.cloudstream3.ui
import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.core.view.children
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.abs
class GrdLayoutManager(val context: Context, _spanCount: Int) : GridLayoutManager(context, _spanCount) {
class GrdLayoutManager(val context: Context, spanCount: Int) :
GridLayoutManager(context, spanCount) {
override fun onFocusSearchFailed(
focused: View,
focusDirection: Int,
@ -23,7 +25,7 @@ class GrdLayoutManager(val context: Context, _spanCount: Int) : GridLayoutManage
}
}
override fun onRequestChildFocus(
/*override fun onRequestChildFocus(
parent: RecyclerView,
state: RecyclerView.State,
child: View,
@ -31,13 +33,17 @@ class GrdLayoutManager(val context: Context, _spanCount: Int) : GridLayoutManage
): Boolean {
// android.widget.FrameLayout$LayoutParams cannot be cast to androidx.recyclerview.widget.RecyclerView$LayoutParams
return try {
val pos = maxOf(0, getPosition(focused!!) - 2)
parent.scrollToPosition(pos)
if(focused != null) {
// val pos = maxOf(0, getPosition(focused) - 2) // IDK WHY
val pos = getPosition(focused)
if(pos >= 0) parent.scrollToPosition(pos)
}
super.onRequestChildFocus(parent, state, child, focused)
} catch (e: Exception){
} catch (e: Exception) {
false
}
}
}*/
// Allows moving right and left with focus https://gist.github.com/vganin/8930b41f55820ec49e4d
override fun onInterceptFocusSearch(focused: View, direction: Int): View? {
@ -64,32 +70,47 @@ class GrdLayoutManager(val context: Context, _spanCount: Int) : GridLayoutManage
val spanCount = this.spanCount
val orientation = this.orientation
if (orientation == VERTICAL) {
// fixes arabic by inverting left and right layout focus
val correctDirection = if (this.isLayoutRTL) {
when (direction) {
View.FOCUS_RIGHT -> View.FOCUS_LEFT
View.FOCUS_LEFT -> View.FOCUS_RIGHT
else -> direction
}
} else direction
if (orientation == VERTICAL) {
when (correctDirection) {
View.FOCUS_DOWN -> {
return spanCount
}
View.FOCUS_UP -> {
return -spanCount
}
View.FOCUS_RIGHT -> {
return 1
}
View.FOCUS_LEFT -> {
return -1
}
}
} else if (orientation == HORIZONTAL) {
when (direction) {
when (correctDirection) {
View.FOCUS_DOWN -> {
return 1
}
View.FOCUS_UP -> {
return -1
}
View.FOCUS_RIGHT -> {
return spanCount
}
View.FOCUS_LEFT -> {
return -spanCount
}
@ -141,4 +162,32 @@ class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: Att
layoutManager = manager
}
}
/**
* Recyclerview wherein the max item width or height is set by the biggest view to prevent inconsistent view sizes.
*/
class MaxRecyclerView(ctx: Context, attrs: AttributeSet) : RecyclerView(ctx, attrs) {
private var biggestObserved: Int = 0
private val orientation = LayoutManager.getProperties(context, attrs, 0, 0).orientation
private val isHorizontal = orientation == HORIZONTAL
private fun View.updateMaxSize() {
if (isHorizontal) {
this.minimumHeight = biggestObserved
} else {
this.minimumWidth = biggestObserved
}
}
override fun onChildAttachedToWindow(child: View) {
child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
val observed = if (isHorizontal) child.measuredHeight else child.measuredWidth
if (observed > biggestObserved) {
biggestObserved = observed
children.forEach { it.updateMaxSize() }
} else {
child.updateMaxSize()
}
super.onChildAttachedToWindow(child)
}
}

View file

@ -16,14 +16,16 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.view.isVisible
import com.lagradost.cloudstream3.R
import kotlinx.android.synthetic.main.activity_easter_egg_monke.*
import java.util.*
import com.lagradost.cloudstream3.databinding.ActivityEasterEggMonkeBinding
class EasterEggMonke : AppCompatActivity() {
lateinit var binding : ActivityEasterEggMonkeBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_easter_egg_monke)
binding = ActivityEasterEggMonkeBinding.inflate(layoutInflater)
setContentView(binding.root)
val handler = Handler(mainLooper)
lateinit var runnable: Runnable
@ -32,15 +34,14 @@ class EasterEggMonke : AppCompatActivity() {
handler.postDelayed(runnable, 300)
}
handler.postDelayed(runnable, 1000)
}
private fun shower() {
val containerW = frame.width
val containerH = frame.height
var starW: Float = monke.width.toFloat()
var starH: Float = monke.height.toFloat()
val containerW = binding.frame.width
val containerH = binding.frame.height
var starW: Float = binding.monke.width.toFloat()
var starH: Float = binding.monke.height.toFloat()
val newStar = AppCompatImageView(this)
val idx = (monkeys.size * Math.random()).toInt()
@ -48,9 +49,9 @@ class EasterEggMonke : AppCompatActivity() {
newStar.isVisible = true
newStar.layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT)
frame.addView(newStar)
binding.frame.addView(newStar)
newStar.scaleX = Math.random().toFloat() * 1.5f + newStar.scaleX
newStar.scaleX += Math.random().toFloat() * 1.5f
newStar.scaleY = newStar.scaleX
starW *= newStar.scaleX
starH *= newStar.scaleY
@ -69,8 +70,8 @@ class EasterEggMonke : AppCompatActivity() {
set.duration = (Math.random() * 1500 + 2500).toLong()
set.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
frame.removeView(newStar)
override fun onAnimationEnd(animation: Animator) {
binding.frame.removeView(newStar)
}
})

View file

@ -0,0 +1,42 @@
package com.lagradost.cloudstream3.ui
import android.graphics.Canvas
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
class HeaderViewDecoration(private val customView: View) : RecyclerView.ItemDecoration() {
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDraw(c, parent, state)
customView.layout(parent.left, 0, parent.right, customView.measuredHeight)
for (i in 0 until parent.childCount) {
val view = parent.getChildAt(i)
if (parent.getChildAdapterPosition(view) == 0) {
c.save()
val height = customView.measuredHeight
val top = view.top - height
c.translate(0f, top.toFloat())
customView.draw(c)
c.restore()
break
}
}
}
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
if (parent.getChildAdapterPosition(view) == 0) {
customView.measure(
View.MeasureSpec.makeMeasureSpec(parent.measuredWidth, View.MeasureSpec.AT_MOST),
View.MeasureSpec.makeMeasureSpec(parent.measuredHeight, View.MeasureSpec.AT_MOST)
)
outRect.set(0, customView.measuredHeight, 0, 0)
} else {
outRect.setEmpty()
}
}
}

View file

@ -0,0 +1,39 @@
package com.lagradost.cloudstream3.ui
import android.annotation.SuppressLint
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListUpdateCallback
import androidx.recyclerview.widget.RecyclerView
/**
* ListUpdateCallback that dispatches update events to the given adapter.
*
* @see DiffUtil.DiffResult.dispatchUpdatesTo
*/
open class NonFinalAdapterListUpdateCallback
/**
* Creates an AdapterListUpdateCallback that will dispatch update events to the given adapter.
*
* @param mAdapter The Adapter to send updates to.
*/(private var mAdapter: RecyclerView.Adapter<*>) :
ListUpdateCallback {
override fun onInserted(position: Int, count: Int) {
mAdapter.notifyItemRangeInserted(position, count)
}
override fun onRemoved(position: Int, count: Int) {
mAdapter.notifyItemRangeRemoved(position, count)
}
override fun onMoved(fromPosition: Int, toPosition: Int) {
mAdapter.notifyItemMoved(fromPosition, toPosition)
}
@SuppressLint("UnknownNullness") // b/240775049: Cannot annotate properly
override fun onChanged(position: Int, count: Int, payload: Any?) {
mAdapter.notifyItemRangeChanged(position, count, payload)
}
}

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