From 544f277d0cb87cb2601086286e34aa75c9bfce67 Mon Sep 17 00:00:00 2001
From: Blatzar <46196380+Blatzar@users.noreply.github.com>
Date: Mon, 24 Oct 2022 15:31:35 +0200
Subject: [PATCH 01/13] Fixed player caching being invalidated by OOM
---
app/src/main/AndroidManifest.xml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 216a5f21..d22e95c0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -27,6 +27,7 @@
+
Date: Tue, 25 Oct 2022 10:41:12 +0200
Subject: [PATCH 02/13] [skip ci] increase issue analysis threshold
---
.github/workflows/issue_action.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/issue_action.yml b/.github/workflows/issue_action.yml
index 79e7766c..a81c4b42 100644
--- a/.github/workflows/issue_action.yml
+++ b/.github/workflows/issue_action.yml
@@ -18,7 +18,7 @@ jobs:
uses: actions-cool/issues-similarity-analysis@v1
with:
token: ${{ steps.generate_token.outputs.token }}
- filter-threshold: 0.5
+ filter-threshold: 0.60
title-excludes: ''
comment-title: |
### Your issue looks similar to these issues:
From 7f71eef75550661eb9b418ebea31560d457442e3 Mon Sep 17 00:00:00 2001
From: Blatzar <46196380+Blatzar@users.noreply.github.com>
Date: Wed, 26 Oct 2022 14:59:29 +0200
Subject: [PATCH 03/13] Allow playback from buffer with no internet
---
.../cloudstream3/ui/player/CS3IPlayer.kt | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
index ea466120..a838c85c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
@@ -17,10 +17,7 @@ import com.google.android.exoplayer2.text.TextRenderer
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.trackselection.TrackSelector
import com.google.android.exoplayer2.ui.SubtitleView
-import com.google.android.exoplayer2.upstream.DataSource
-import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
-import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
-import com.google.android.exoplayer2.upstream.HttpDataSource
+import com.google.android.exoplayer2.upstream.*
import com.google.android.exoplayer2.upstream.cache.CacheDataSource
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
import com.google.android.exoplayer2.upstream.cache.SimpleCache
@@ -880,7 +877,16 @@ class CS3IPlayer : IPlayer {
}
override fun onPlayerError(error: PlaybackException) {
- playerError?.invoke(error)
+ // If the Network fails then ignore the exception if the duration is set.
+ // This is to switch mirrors automatically if the stream has not been fetched, but
+ // allow playing the buffer without internet as then the duration is fetched.
+ if (error.errorCode == PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED
+ && exoPlayer?.duration != C.TIME_UNSET
+ ) {
+ exoPlayer?.prepare()
+ } else {
+ playerError?.invoke(error)
+ }
super.onPlayerError(error)
}
From ecd363992c42d4c489f52191f657842a0a23eaf4 Mon Sep 17 00:00:00 2001
From: Hexated <37908684+hexated@users.noreply.github.com>
Date: Wed, 26 Oct 2022 22:38:46 +0700
Subject: [PATCH 04/13] added sub to streamsb & xtreamCdn (#163)
---
.../cloudstream3/extractors/Gdriveplayer.kt | 55 ++++++++++++----
.../cloudstream3/extractors/Moviehab.kt | 40 ++++++++++++
.../cloudstream3/extractors/SpeedoStream.kt | 6 +-
.../cloudstream3/extractors/StreamSB.kt | 20 +++++-
.../extractors/VidSrcExtractor.kt | 8 +--
.../cloudstream3/extractors/XStreamCdn.kt | 64 ++++++++++++++-----
.../cloudstream3/utils/ExtractorApi.kt | 6 ++
7 files changed, 159 insertions(+), 40 deletions(-)
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/Moviehab.kt
diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt
index dfccc118..df9c74a4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt
@@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
+import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import org.jsoup.nodes.Element
import java.security.DigestException
import java.security.MessageDigest
@@ -10,43 +11,47 @@ import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
+class DatabaseGdrive2 : Gdriveplayer() {
+ override var mainUrl = "https://databasegdriveplayer.co"
+}
+
class DatabaseGdrive : Gdriveplayer() {
override var mainUrl = "https://series.databasegdriveplayer.co"
}
-class Gdriveplayerapi: Gdriveplayer() {
+class Gdriveplayerapi : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayerapi.com"
}
-class Gdriveplayerapp: Gdriveplayer() {
+class Gdriveplayerapp : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.app"
}
-class Gdriveplayerfun: Gdriveplayer() {
+class Gdriveplayerfun : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.fun"
}
-class Gdriveplayerio: Gdriveplayer() {
+class Gdriveplayerio : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.io"
}
-class Gdriveplayerme: Gdriveplayer() {
+class Gdriveplayerme : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.me"
}
-class Gdriveplayerbiz: Gdriveplayer() {
+class Gdriveplayerbiz : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.biz"
}
-class Gdriveplayerorg: Gdriveplayer() {
+class Gdriveplayerorg : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.org"
}
-class Gdriveplayerus: Gdriveplayer() {
+class Gdriveplayerus : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.us"
}
-class Gdriveplayerco: Gdriveplayer() {
+class Gdriveplayerco : Gdriveplayer() {
override val mainUrl: String = "https://gdriveplayer.co"
}
@@ -136,6 +141,10 @@ open class Gdriveplayer : ExtractorApi() {
return find(str)?.groupValues?.getOrNull(1)
}
+ private fun String.addMarks(str: String): String {
+ return this.replace(Regex("\"?$str\"?"), "\"$str\"")
+ }
+
override suspend fun getUrl(
url: String,
referer: String?,
@@ -145,18 +154,19 @@ open class Gdriveplayer : ExtractorApi() {
val document = app.get(url).document
val eval = unpackJs(document)?.replace("\\", "") ?: return
- val data = AppUtils.tryParseJson(Regex("data='(\\S+?)'").first(eval)) ?: return
+ val data = tryParseJson(Regex("data='(\\S+?)'").first(eval)) ?: return
val password = Regex("null,['|\"](\\w+)['|\"]").first(eval)
?.split(Regex("\\D+"))
?.joinToString("") {
Char(it.toInt()).toString()
}.let { Regex("var pass = \"(\\S+?)\"").first(it ?: return)?.toByteArray() }
?: throw ErrorLoadingException("can't find password")
- val decryptedData =
- cryptoAESHandler(data, password, false)?.let { getAndUnpack(it) }?.replace("\\", "")
- ?.substringAfter("sources:[")?.substringBefore("],")
+ val decryptedData = cryptoAESHandler(data, password, false)?.let { getAndUnpack(it) }?.replace("\\", "")
- Regex("\"file\":\"(\\S+?)\".*?res=(\\d+)").findAll(decryptedData ?: return).map {
+ val sourceData = decryptedData?.substringAfter("sources:[")?.substringBefore("],")
+ val subData = decryptedData?.substringAfter("tracks:[")?.substringBefore("],")
+
+ Regex("\"file\":\"(\\S+?)\".*?res=(\\d+)").findAll(sourceData ?: return).map {
it.groupValues[1] to it.groupValues[2]
}.toList().distinctBy { it.second }.map { (link, quality) ->
callback.invoke(
@@ -171,6 +181,17 @@ open class Gdriveplayer : ExtractorApi() {
)
}
+ subData?.addMarks("file")?.addMarks("kind")?.addMarks("label").let { dataSub ->
+ tryParseJson>("[$dataSub]")?.map { sub ->
+ subtitleCallback.invoke(
+ SubtitleFile(
+ sub.label,
+ httpsify(sub.file)
+ )
+ )
+ }
+ }
+
}
data class AesData(
@@ -179,4 +200,10 @@ open class Gdriveplayer : ExtractorApi() {
@JsonProperty("s") val s: String
)
+ data class Tracks(
+ @JsonProperty("file") val file: String,
+ @JsonProperty("kind") val kind: String,
+ @JsonProperty("label") val label: String
+ )
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Moviehab.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Moviehab.kt
new file mode 100644
index 00000000..e2eb7bf0
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Moviehab.kt
@@ -0,0 +1,40 @@
+package com.lagradost.cloudstream3.extractors
+
+import com.lagradost.cloudstream3.SubtitleFile
+import com.lagradost.cloudstream3.app
+import com.lagradost.cloudstream3.utils.ExtractorApi
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.M3u8Helper
+
+class Moviehab : ExtractorApi() {
+ override var name = "Moviehab"
+ override var mainUrl = "https://play.moviehab.com"
+ override val requiresReferer = false
+
+ override suspend fun getUrl(
+ url: String,
+ referer: String?,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ) {
+ val res = app.get(url)
+ res.document.select("video#player").let {
+ //should redirect first for making it works
+ val link = app.get("$mainUrl/${it.select("source").attr("src")}", referer = url).url
+ M3u8Helper.generateM3u8(
+ this.name,
+ link,
+ url
+ ).forEach(callback)
+
+ Regex("src[\"|'],\\s[\"|'](\\S+)[\"|']\\)").find(res.text)?.groupValues?.get(1).let {sub ->
+ subtitleCallback.invoke(
+ SubtitleFile(
+ it.select("track").attr("label"),
+ "$mainUrl/$sub"
+ )
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/SpeedoStream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/SpeedoStream.kt
index 6153a7c1..8ef6c463 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/extractors/SpeedoStream.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/SpeedoStream.kt
@@ -7,7 +7,11 @@ import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
-class SpeedoStream : ExtractorApi() {
+class SpeedoStream1 : SpeedoStream() {
+ override val mainUrl = "https://speedostream.nl"
+}
+
+open class SpeedoStream : ExtractorApi() {
override val name = "SpeedoStream"
override val mainUrl = "https://speedostream.com"
override val requiresReferer = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt
index 30a0496d..913b410b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt
@@ -7,6 +7,11 @@ import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
+class Sbspeed : StreamSB() {
+ override var name = "Sbspeed"
+ override var mainUrl = "https://sbspeed.com"
+}
+
class Streamsss : StreamSB() {
override var mainUrl = "https://streamsss.net"
}
@@ -93,15 +98,15 @@ open class StreamSB : ExtractorApi() {
}
data class Subs (
- @JsonProperty("file") val file: String,
- @JsonProperty("label") val label: String,
+ @JsonProperty("file") val file: String? = null,
+ @JsonProperty("label") val label: String? = null,
)
data class StreamData (
@JsonProperty("file") val file: String,
@JsonProperty("cdn_img") val cdnImg: String,
@JsonProperty("hash") val hash: String,
- @JsonProperty("subs") val subs: List?,
+ @JsonProperty("subs") val subs: ArrayList? = arrayListOf(),
@JsonProperty("length") val length: String,
@JsonProperty("id") val id: String,
@JsonProperty("title") val title: String,
@@ -141,5 +146,14 @@ open class StreamSB : ExtractorApi() {
url,
headers = headers
).forEach(callback)
+
+ mapped.streamData.subs?.map {sub ->
+ subtitleCallback.invoke(
+ SubtitleFile(
+ sub.label.toString(),
+ sub.file ?: return@map null,
+ )
+ )
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt
index 63634704..287fe4d4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt
@@ -85,18 +85,12 @@ open class VidSrcExtractor : ExtractorApi() {
this.name,
this.name,
srcm3u8,
- this.mainUrl,
+ "https://vidsrc.stream/",
Qualities.Unknown.value,
extractorData = pass,
isM3u8 = true
)
)
-
-// M3u8Helper.generateM3u8(
-// name,
-// srcm3u8,
-// absoluteUrl
-// ).forEach(callback)
} else {
loadExtractor(linkfixed, url, subtitleCallback, callback)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/XStreamCdn.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/XStreamCdn.kt
index 9e3585ae..15ff0436 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/extractors/XStreamCdn.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/XStreamCdn.kt
@@ -1,12 +1,23 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
-import com.lagradost.cloudstream3.utils.AppUtils
+import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
+class Cdnplayer: XStreamCdn() {
+ override val name: String = "Cdnplayer"
+ override val mainUrl: String = "https://cdnplayer.online"
+}
+
+class Kotakajair: XStreamCdn() {
+ override val name: String = "Kotakajair"
+ override val mainUrl: String = "https://kotakajair.xyz"
+}
+
class FEnet: XStreamCdn() {
override val name: String = "FEnet"
override val mainUrl: String = "https://fembed.net"
@@ -59,44 +70,67 @@ open class XStreamCdn : ExtractorApi() {
//val type: String // Mp4
)
+ private data class Player(
+ @JsonProperty("poster_file") val poster_file: String? = null,
+ )
+
private data class ResponseJson(
@JsonProperty("success") val success: Boolean,
- @JsonProperty("data") val data: List?
+ @JsonProperty("player") val player: Player? = null,
+ @JsonProperty("data") val data: List?,
+ @JsonProperty("captions") val captions: List?,
+ )
+
+ private data class Captions(
+ @JsonProperty("id") val id: String,
+ @JsonProperty("hash") val hash: String,
+ @JsonProperty("language") val language: String,
+ @JsonProperty("extension") val extension: String
)
override fun getExtractorUrl(id: String): String {
return "$domainUrl/api/source/$id"
}
- override suspend fun getUrl(url: String, referer: String?): List {
+ override suspend fun getUrl(
+ url: String,
+ referer: String?,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ) {
val headers = mapOf(
"Referer" to url,
"User-Agent" to "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0",
)
val id = url.trimEnd('/').split("/").last()
val newUrl = "https://${domainUrl}/api/source/${id}"
- val extractedLinksList: MutableList = mutableListOf()
- with(app.post(newUrl, headers = headers)) {
- if (this.code != 200) return listOf()
- val text = this.text
- if (text.isEmpty()) return listOf()
- if (text == """{"success":false,"data":"Video not found or has been removed"}""") return listOf()
- AppUtils.parseJson(text)?.let {
+ app.post(newUrl, headers = headers).let { res ->
+ val sources = tryParseJson(res.text)
+ sources?.let {
if (it.success && it.data != null) {
- it.data.forEach { data ->
- extractedLinksList.add(
+ it.data.map { source ->
+ callback.invoke(
ExtractorLink(
name,
name = name,
- data.file,
+ source.file,
url,
- getQualityFromName(data.label),
+ getQualityFromName(source.label),
)
)
}
}
}
+
+ val userData = sources?.player?.poster_file?.split("/")?.get(2)
+ sources?.captions?.map {
+ subtitleCallback.invoke(
+ SubtitleFile(
+ it?.language.toString(),
+ "$mainUrl/asset/userdata/$userData/caption/${it?.hash}/${it?.id}.${it?.extension}"
+ )
+ )
+ }
}
- return extractedLinksList
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
index 8d6b1b82..ca612385 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt
@@ -235,6 +235,7 @@ val extractorApis: MutableList = arrayListOf(
Vidgomunime(),
Sbflix(),
Streamsss(),
+ Sbspeed(),
Fastream(),
@@ -246,6 +247,8 @@ val extractorApis: MutableList = arrayListOf(
LayarKaca(),
Rasacintaku(),
FEnet(),
+ Kotakajair(),
+ Cdnplayer(),
// WatchSB(), 'cause StreamSB.kt works
Uqload(),
Uqload1(),
@@ -317,6 +320,7 @@ val extractorApis: MutableList = arrayListOf(
Linkbox(),
Acefile(),
SpeedoStream(),
+ SpeedoStream1(),
Zorofile(),
Embedgram(),
Mvidoo(),
@@ -324,6 +328,7 @@ val extractorApis: MutableList = arrayListOf(
Vidmoly(),
Vidmolyme(),
Voe(),
+ Moviehab(),
Gdriveplayerapi(),
Gdriveplayerapp(),
@@ -336,6 +341,7 @@ val extractorApis: MutableList = arrayListOf(
Gdriveplayerco(),
Gdriveplayer(),
DatabaseGdrive(),
+ DatabaseGdrive2(),
YoutubeExtractor(),
YoutubeShortLinkExtractor(),
From 044822040f7da8c777958057a12d1f7ee3a4222c Mon Sep 17 00:00:00 2001
From: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Date: Wed, 26 Oct 2022 21:55:59 +0200
Subject: [PATCH 05/13] Translation in Bulgarian language.
Co-authored-by: ardoslav
---
README.md | 1 +
.../ui/settings/SettingsGeneral.kt | 1 +
app/src/main/res/values-bg/strings.xml | 547 ++++++++++++++++++
3 files changed, 549 insertions(+)
create mode 100644 app/src/main/res/values-bg/strings.xml
diff --git a/README.md b/README.md
index e7221440..5e961c61 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,7 @@
***The list of supported languages:***
* 🇱🇧 Arabic
+* 🇧🇬 Bulgarian
* 🇭🇷 Croatian
* 🇨🇿 Czech
* 🇳🇱 Dutch
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt
index 57074e74..8ea76cda 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt
@@ -79,6 +79,7 @@ val appLanguages = arrayListOf(
Triple("\uD83C\uDDEE\uD83C\uDDE9", "Indonesian", "in"),
Triple("", "Czech", "cs"),
Triple("", "Croatian", "hr"),
+ Triple("", "Bulgarian", "bg"),
).sortedBy { it.second } //ye, we go alphabetical, so ppl don't put their lang on top
class SettingsGeneral : PreferenceFragmentCompat() {
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
new file mode 100644
index 00000000..b8f43dd0
--- /dev/null
+++ b/app/src/main/res/values-bg/strings.xml
@@ -0,0 +1,547 @@
+
+
+
+
+ %s еп. %d
+ Актьори: %s
+ Епизод %d ще бъде пуснат след
+ %DD %dh %dm
+ %dh %dm
+ %dm
+
+
+ Плакат
+ @string/result_poster_img_des
+ Плакат на епизод
+ Основен плакат
+ Следващ произволен
+ Върни се
+ Смяна на доставчика
+ Визуализация на фона
+
+
+ Скорост (%.2fx)
+ Оценка: %.1f
+ Намерена е нова актуализация!\n%s -> %s
+ Шаблон
+ %d мин
+
+ CloudStream
+ Пусни с CloudStream
+ Начало
+ Търсене
+ Изтегляния
+ Настройки
+
+ Търсене…
+ Търсете %s...
+
+ Няма данни
+ Още опций
+ Следващ епизод
+ Жанрове
+ Сподели
+ Отвори в браузъра
+ Пропусни зареждането
+ Зареждане…
+
+ Гледане
+ На изчакване
+ Завършено
+ Изпуснат
+ План за гледане
+ Нито един
+ Повторно гледане
+
+ Пускане на филм
+ Възпроизвеждане на живо
+ Поточно предаване на торент
+ Източници
+ субтитри
+ Повторен опит за свързване...
+ Върни се
+ Пусни епизод
+
+
+ Изтегли
+ Изтеглено
+ Изтегля се
+ Изтеглянето е на пауза
+ Изтеглянето започна
+ Изтеглянето се провали
+ Изтеглянето е отменено
+ Изтеглянето е завършено
+ Поток
+
+ Грешка при зареждане на връзки
+ Вътрешна памет
+
+ Дублаж
+ Субтитри
+
+ Изтрий файла
+ Възпроизвеждане на файл
+ Възобновете изтеглянето
+ Пауза на изтеглянето
+
+ Деактивирайте автоматичното докладване на грешки
+ Повече информация
+ Скрий
+ Пусни
+ Информация
+ Филтриране на отметки
+ Отметки
+ Премахване
+ Задайте статус на гледане
+ Приложи
+ Отказ
+ Копирай
+ Затвори
+ Изчисти
+ Запазване
+
+ Скорост на възпроизвеждане
+
+ Настройки на субтитрите
+ Цвят на текста
+ Цвят на контура
+ Цвят на фона
+ Цвят на прозореца
+ Тип ръб
+ Височина на субтитрите
+ Шрифт
+ Размер на шрифта
+
+ Търсене чрез доставчици
+ Търсене с помощта на типове
+
+ %d Банан/а даден/и на разработчиците
+ Не е/са даден/и Банан/и
+
+ Автоматичен избор на език
+ Изтегляне на езици
+ Език на субтитрите
+ Задръжте, за да нулирате по подразбиране
+ Импортирайте шрифтове, като ги поставите в %s
+ Продължете да гледате
+
+ Премахване
+ Повече информация
+ @string/home_play
+
+ Може да е необходим VPN, за да работи правилно този доставчик
+ Този доставчик е торент, препоръчва се VPN
+
+ Метаданните не се предоставят от сайта, зареждането на видео ще бъде неуспешно, ако не съществува на сайта.
+
+ Описание
+ Няма намерен съдържание
+ Няма намерено описание
+
+ Покажи logcat 🐈
+
+ Картина в картина
+ Продължава възпроизвеждането в миниатюрен плейър върху други приложения
+ Бутон за преоразмеряване на плейъра
+ Премахнете черните граници
+ Субтитри
+ Настройки на субтитрите на плейъра
+ Chromecast субтитри
+ Настройки за субтитри на Chromecast
+
+ Режим Eigengravy (промяна скорост на възпроизвеждане)
+ Добавя опция за скорост в плейъра
+ Плъзнете за преместване
+ Плъзнете наляво или надясно, за да контролирате времето във видеоплейъра
+ Плъзнете, за да промените настройките
+ Плъзнете наляво или надясно, за да промените яркостта или силата на звука
+
+ Автоматично пускане на следващ епизод
+ Започнете следващия епизод, когато текущият приключи
+
+ Докоснете двукратно за превъртане
+ Докоснете два пъти за пауза
+ Размер на превъртане
+ Докоснете два пъти от дясната или лявата страна, за да превъртите напред или назад
+ Докоснете в средата, за да направите пауза
+ Използвайте яркостта на системата
+ Използвайте системна яркост в плейъра на приложението вместо тъмно
+ наслагване
+
+ Актуализирайте прогреса на гледане
+ Автоматично синхронизирайте прогреса на текущия си епизод
+
+ Възстановете данните от архив
+
+ Архивиране на данни
+ Зареден архивен файл
+ Неуспешно възстановяване на данни от файл %s
+ Успешно съхранени данни
+ Липсват разрешения за съхранение, моля, опитайте отново
+ Грешка при архивирането на %s
+
+ Търсене
+ Акаунти
+ Актуализации и архивиране
+
+ Информация
+ Подробно търсене
+ Дава ви резултатите от търсенето, разделени по доставчик
+ Изпраща данни само за сривове
+ Не изпраща данни
+ Показване заместващ епизод за аниме
+ Показване на трейлъри
+ Покажете плакати от kitsu
+ Скриване на избраното видео качество в резултатите от търсенето
+
+ Автоматични актуализации на плъгини
+ Показвай актуализации на приложението
+ Автоматично търси нови актуализации при стартиране
+ Актуализация до експериментални версии
+ Търсете експериментални актуализации вместо само пълни версии
+ Github
+ Light novel - приложение от същите разработчици
+ Приложение за аниме от същите разработчици
+ Присъединете се към Discord
+ Дайте банан/и разработчиците
+ Даден/и банан/и
+
+ Език на приложението
+
+ Този доставчик няма поддръжка за Chromecast
+ Няма намерени връзки
+ Връзката е копирана в клипборда
+ Пусни епизода
+ Възстановяване на стойността по подразбиране
+ За съжаление приложението се срина. Анонимен доклад за грешка ще бъде изпратен до
+ разработчиците
+
+ Сезон
+ %s %d%s
+ Без сезон
+ Епизод
+ Епизоди
+ %d-%d
+ %d %s
+ С
+ Е
+ Няма намерени епизоди
+
+ Изтрий файла
+ Изтрий
+ Пауза
+ Продължи
+ -30
+ 30
+ Това ще изтрие за постоянно %s\nСигурни ли сте?
+ %dm\nостава
+
+
+ Продължава
+ Завършен
+ Статус
+ Година
+ Рейтинг
+ Продължителност
+ Уебсайт
+ Синопсис
+
+ На опашката
+ Без субтитри
+ По подразбиране
+
+ Безплатно
+ Използвано
+ Приложения
+
+
+ Филми
+ Телевизионен сериал
+ Анимационни филми
+ Аниме
+ Торенти
+ Документални
+ OVA
+ Азиатски драми
+ На живо
+ Разпоретини
+ други
+
+
+ Филм
+ Серия
+ Анимационен филм
+ @string/аниме
+ @string/ova
+ Торент
+ Документален филм
+ Азиатска драма
+ Поток на живо
+ Разпоретинa
+ Видео
+
+ Грешка в източника
+ Дистанционна грешка
+ Грешка в рендъра
+ Неочаквана грешка на плеъра
+ Грешка при изтегляне, проверете разрешенията за съхранение
+
+ Епизод за Chromecast
+ Chromecast огледало
+ Пусни в приложението
+ Пусни в %s
+ Пусни в браузър
+ Копирай връзка
+ Автоматично изтегляне
+ Изтегляне на огледало
+ Презареждане на връзки
+ Изтегляне на субтитри
+
+ покажи качество
+ Покажи дублаж
+ Покажи субтитри
+ Заглавие
+ Превключване на елементите на потребителския интерфейс на плаката
+
+ Няма намерена актуализация
+ Проверка за актуализация
+
+ Заключен
+ Преоразмеряване
+ Източник
+ Пропусни
+
+ Не показвай отново
+ Пропуснете тази актуализация
+ Актуализация
+ Предпочитано качество за гледане
+ Максимален брой знаци за заглавие във видеоплейъра
+ Разделителна способност на видео плейъра
+
+ Размер на видео буфера
+ Дължина на видео буфера
+ Видео кеш на диск
+ Изчистете кеша за видео и изображения
+
+ Ще предизвика случайни сривове, ако е зададено твърде високо. Не променяйте, ако имате малко количество RAM, като Android TV или стар телефон
+ Може да причини проблеми на системи с малко място за съхранение, като устройства с Android TV, ако го зададете твърде високо
+
+ DNS през HTTPS
+ Полезно за заобикаляне на блокирания от ISP доставчик
+
+ Сайт за клониране
+ Премахване на сайта
+ Добавяне на клонинг на съществуващ сайт с различен URL адрес
+
+ Път за изтегляне
+
+ URL адрес на сървъра на Nginx
+
+ Показване на дублирани/субирани аниме
+
+ Побиране в екрана
+ Разтягане
+ Мащабиране
+
+ Опровержение
+ should be taken up with the actual file hosts and providers themselves as we are not affiliated with them.
+
+ In case of copyright infringement, please directly contact the responsible parties or the streaming websites.
+
+ The app is purely for educational and personal use.
+
+ CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down.
+ CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or
+ manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient,
+ user-friendly interface.
+
+ It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the
+ responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use
+ CloudStream 3 at your own risk.
+
+ Общ
+ Случаен бутон
+ Показване на произволен бутон на началната страница
+ Езици на доставчика
+ Оформление на приложението
+ Предпочитана медия
+ Активирайте разпоретините на поддържани доставчици
+ Кодиране на субтитрите
+ Доставчици
+ Оформление
+
+ Автоматично
+ ТВ оформление
+ Оформление като телефон
+ Оформление като емулатор
+
+ Основен цвят
+ Тема на приложението
+ Местоположение на заглавието на плаката
+ Поставете заглавието под плаката
+
+ парола123
+ MyCoolUsername
+ hello@world.com
+ 127.0.0.1
+ MyCoolSite
+ example.com
+ Езиков код (en)
+
+
+ %s %s
+ Акаунт
+ Излизане
+ Влизане
+ Превключване на акаунт
+ Добавяне на акаунт
+ Създай акаунт
+ Добавете проследяване
+ Добавен %s
+ Синхронизиране
+ Оценен
+ %d / 10
+ /??
+ /%d
+ Удостоверен %s
+ Неуспешно удостоверяване на %s
+
+
+ Нито един
+ Нормално
+ Всичко
+ Макс
+ Мин
+ Контур
+ Удвоени
+ Сянка
+ Повдигнати
+ Синхронизиране на суб
+ 1000ms
+ Забавяне на субтитрите
+ Използвайте това, ако субтитрите се показват %dms твърде рано
+ Използвайте това, ако субтитрите се показват %dms твърде късно
+ Без забавяне на субтитрите
+
+
+
+ Бързата кафява лисица прескача мързеливото куче
+
+ Препоръчва се
+ Заредено %s
+ Зареди от файл
+ Зареди от Интернет
+ Изтеглен файл
+ Главен
+ Поддържащ
+ Заден план
+
+ Източник
+ Случаен
+
+ Очаквайте скоро…
+
+ Cam
+ Cam
+ Cam
+ HQ
+ HD
+ TS
+ TC
+ BlueRay
+ WP
+ DVD
+ 4K
+ SD
+ UHD
+ HDR
+ SDR
+ Web
+
+ Снимка на плакат
+ Плеър
+ Резолюция и заглавие
+ Заглавие
+ Резолюция
+ Невалиден идентификатор
+ Невалидни данни
+ Невалиден адрес
+ Грешка
+ Премахнете затворените надписи от субтитрите
+ Премахнете рекламирането в субтитрите
+ Филтриране по предпочитан медиен език
+ Екстри
+ Трейлър
+ Връзка към потока
+ Обратно към
+ Следващ
+ Гледайте видеоклипове на тези езици
+ Предишен
+ Пропуснете настройката
+ Променете външния вид на приложението, за да отговаря на вашето устройство
+ Докладване за сривове
+ Какво искате да видите
+ Край
+ Разширения
+ Добавяне на хранилище
+ Име на хранилище
+ URL адрес на хранилището
+ Приставката е заредена
+ Приставката е изтрита
+ Неуспешно зареждане %s
+ 18+
+ Започна да изтегля %d %s
+ Изтеглено %d %s успешно
+ Всички %s вече са изтеглени
+ Пакетно изтегляне
+ Плъгин
+ Плъгини
+ Това също ще изтрие всички хранилища за плъгини
+ Изтриване на хранилище
+ Изтеглете списъка със сайтове, които искате да използвате
+ Изтеглено: %d
+ Деактивирано: %d
+ Не е изтеглено: %d
+ Актуализирани %d плъгини
+ CloudStream няма инсталирани сайтове по подразбиране. Трябва да инсталирате сайтовете от хранилища.\n\nПоради безмозъчно премахване на DMCA от Sky Uk Limited 🤮 не можем да свържем сайтовете на хранилищата в приложението.\n\nПрисъединете се към нашия дискорд за връзки или търсете онлайн.
+ Вижте хранилищата на общността
+ Публичен списък
+ Всички субтитри с главни букви
+
+ Изтегляне на всички добавки от това хранилище?
+ %s (Disabled)
+ Потоци
+ Аудио потоци
+ Видео потоци
+ Приложете при рестартиране
+
+ Безопасният режим е активиран
+ Възникна непоправим срив и ние автоматично деактивирахме всички разширения, така че можете да намерите и премахнете разширението, което причинява проблеми.
+ Вижте информация за срива
+
+ Оценка: %s
+ Описание
+ Версия
+ Статус
+ Размер
+ Автори
+ Поддържани
+ език
+ Първо инсталирайте разширението
+
+ HLS плейлист
+
+ Предпочитан видео плеър
+ Вътрешен плеър
+ VLC
+ MPV
+ Уеб видео предаване
+ Браузър
+ Приложението не е намерено
+
\ No newline at end of file
From 0f625142dacb4553b9af57f7f949eff9eab7b218 Mon Sep 17 00:00:00 2001
From: "J. Fronny"
Date: Wed, 26 Oct 2022 19:56:31 +0000
Subject: [PATCH 06/13] Switch to kotlin build scripts (#158)
---
.github/workflows/prerelease.yml | 4 +-
.github/workflows/pull_request.yml | 4 +-
.idea/jarRepositories.xml | 5 +
app/build.gradle | 233 ---------------------
app/build.gradle.kts | 250 +++++++++++++++++++++++
app/proguard-rules.pro | 2 +-
app/src/main/AndroidManifest.xml | 3 +-
build.gradle | 27 ---
build.gradle.kts | 26 +++
gradle/wrapper/gradle-wrapper.properties | 2 +-
settings.gradle | 2 -
settings.gradle.kts | 3 +
12 files changed, 291 insertions(+), 270 deletions(-)
delete mode 100644 app/build.gradle
create mode 100644 app/build.gradle.kts
delete mode 100644 build.gradle
create mode 100644 build.gradle.kts
delete mode 100644 settings.gradle
create mode 100644 settings.gradle.kts
diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml
index 37161d6b..a7615ad5 100644
--- a/.github/workflows/prerelease.yml
+++ b/.github/workflows/prerelease.yml
@@ -43,7 +43,7 @@ jobs:
echo "::set-output name=key_pwd::$KEY_PWD"
- name: Run Gradle
run: |
- ./gradlew assemblePrerelease makeJar androidSourcesJar
+ ./gradlew assemblePrereleaseRelease makeJar androidSourcesJar
env:
SIGNING_KEY_ALIAS: "key0"
SIGNING_KEY_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }}
@@ -56,6 +56,6 @@ jobs:
prerelease: true
title: "Pre-release Build"
files: |
- app/build/outputs/apk/prerelease/*.apk
+ app/build/outputs/apk/prerelease/stable/*.apk
app/build/libs/app-sources.jar
app/build/classes.jar
diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml
index 1a4db134..36199cd6 100644
--- a/.github/workflows/pull_request.yml
+++ b/.github/workflows/pull_request.yml
@@ -15,9 +15,9 @@ jobs:
- 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
with:
name: pull-request-build
- path: "app/build/outputs/apk/debug/*.apk"
+ path: "app/build/outputs/apk/prerelease/debug/*.apk"
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index 652d9f3f..333d4937 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -31,5 +31,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
deleted file mode 100644
index 9df01079..00000000
--- a/app/build.gradle
+++ /dev/null
@@ -1,233 +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 52
- versionName "3.1.6"
-
- 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' // need target 32 for 1.5.0
-
- // 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.3'
-
- // 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.TeamNewPipe:NewPipeExtractor:dev-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
-}
-
-// this is used by the gradlew plugin
-task makeJar(type: Copy) {
- from('build/intermediates/compile_app_classes_jar/debug')
- into('build')
- include('classes.jar')
- dependsOn('build')
-}
-
-dokkaHtml {
- moduleName.set("Cloudstream")
- dokkaSourceSets {
- 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(new 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")
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
new file mode 100644
index 00000000..ed2ad5a0
--- /dev/null
+++ b/app/build.gradle.kts
@@ -0,0 +1,250 @@
+import com.android.build.gradle.api.BaseVariantOutput
+import org.jetbrains.dokka.gradle.DokkaTask
+import java.io.ByteArrayOutputStream
+import java.net.URL
+
+plugins {
+ id("com.android.application")
+ id("kotlin-android")
+ id("kotlin-kapt")
+ id("kotlin-android-extensions")
+ 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
+ }
+ signingConfigs {
+ create("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")
+ }
+ }
+ }
+
+ compileSdk = 31
+ buildToolsVersion = "30.0.3"
+
+ defaultConfig {
+ applicationId = "com.lagradost.cloudstream3"
+ minSdk = 21
+ targetSdk = 30
+
+ versionCode = 52
+ versionName = "3.1.6"
+
+ resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}")
+
+ resValue("string", "commit_hash", "git rev-parse --short HEAD".execute() ?: "")
+
+ 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 {
+ 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"
+ signingConfig = signingConfigs.getByName("prerelease")
+ versionNameSuffix = "-PRE"
+ versionCode = (System.currentTimeMillis() / 60000).toInt()
+ }
+ }
+ compileOptions {
+ isCoreLibraryDesugaringEnabled = true
+
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = "1.8"
+ freeCompilerArgs = listOf("-Xjvm-default=compatibility")
+ }
+ lint {
+ abortOnError = false
+ checkReleaseBuilds = false
+ }
+ namespace = "com.lagradost.cloudstream3"
+}
+
+repositories {
+ maven("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") // need target 32 for 1.5.0
+
+ // 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.3")
+
+ // 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.TeamNewPipe:NewPipeExtractor:dev-SNAPSHOT")
+ coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
+
+ // Library/extensions searching with Levenshtein distance
+ implementation("me.xdrop:fuzzywuzzy:1.4.0")
+}
+
+tasks.register("androidSourcesJar", Jar::class) {
+ archiveClassifier.set("sources")
+ from(android.sourceSets.getByName("main").java.srcDirs) //full sources
+}
+
+// this is used by the gradlew plugin
+tasks.register("makeJar", Copy::class) {
+ from("build/intermediates/compile_app_classes_jar/debug")
+ into("build")
+ include("classes.jar")
+ dependsOn("build")
+}
+
+tasks.withType().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")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 481bb434..ff59496d 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -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
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d22e95c0..47676059 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,7 +1,6 @@
+ xmlns:tools="http://schemas.android.com/tools">
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index 90f9ac32..00000000
--- a/build.gradle
+++ /dev/null
@@ -1,27 +0,0 @@
-// Top-level build file where you can add configuration options common to all sub-projects/modules.
-buildscript {
- ext.kotlin_version = "1.7.10"
- repositories {
- google()
- jcenter()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:7.2.1'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.5.0"
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
-}
-
-allprojects {
- repositories {
- google()
- jcenter()
- }
-}
-
-task clean(type: Delete) {
- delete rootProject.buildDir
-}
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 00000000..6e2a24f3
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,26 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+ dependencies {
+ classpath("com.android.tools.build:gradle:7.3.1")
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20")
+ classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.5.0")
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle.kts files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+tasks.register("clean", Delete::class) {
+ delete(rootProject.buildDir)
+}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a2c4ced7..baa28c97 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Fri Apr 30 17:11:15 CEST 2021
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/settings.gradle b/settings.gradle
deleted file mode 100644
index 5d77faec..00000000
--- a/settings.gradle
+++ /dev/null
@@ -1,2 +0,0 @@
-include ':app'
-rootProject.name = "CloudStream"
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 00000000..17070047
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,3 @@
+rootProject.name = "CloudStream"
+
+include(":app")
\ No newline at end of file
From 49ebd27f80786eb2d067a5a2ba343c3b02b7a5cf Mon Sep 17 00:00:00 2001
From: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Date: Wed, 26 Oct 2022 22:06:19 +0200
Subject: [PATCH 07/13] fix broken strings.xml and migrate to $GITHUB_OUTPUT
---
.github/workflows/issue_action.yml | 2 +-
.github/workflows/prerelease.yml | 2 +-
app/src/main/res/values-bg/strings.xml | 14 --------------
3 files changed, 2 insertions(+), 16 deletions(-)
diff --git a/.github/workflows/issue_action.yml b/.github/workflows/issue_action.yml
index a81c4b42..28b737b3 100644
--- a/.github/workflows/issue_action.yml
+++ b/.github/workflows/issue_action.yml
@@ -41,7 +41,7 @@ jobs:
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}"
+ 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
diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml
index a7615ad5..bac0430f 100644
--- a/.github/workflows/prerelease.yml
+++ b/.github/workflows/prerelease.yml
@@ -40,7 +40,7 @@ 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 assemblePrereleaseRelease makeJar androidSourcesJar
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
index b8f43dd0..d67ab55c 100644
--- a/app/src/main/res/values-bg/strings.xml
+++ b/app/src/main/res/values-bg/strings.xml
@@ -344,21 +344,7 @@
Мащабиране
Опровержение
- should be taken up with the actual file hosts and providers themselves as we are not affiliated with them.
- In case of copyright infringement, please directly contact the responsible parties or the streaming websites.
-
- The app is purely for educational and personal use.
-
- CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down.
- CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or
- manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient,
- user-friendly interface.
-
- It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the
- responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use
- CloudStream 3 at your own risk.
-
Общ
Случаен бутон
Показване на произволен бутон на началната страница
From c9c339795ad94dc3027c44c97ab15098d089240c Mon Sep 17 00:00:00 2001
From: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Date: Wed, 26 Oct 2022 22:12:17 +0200
Subject: [PATCH 08/13] fix the bulgarian string once again
---
app/src/main/res/values-bg/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
index d67ab55c..e32d3d93 100644
--- a/app/src/main/res/values-bg/strings.xml
+++ b/app/src/main/res/values-bg/strings.xml
@@ -271,7 +271,7 @@
Филм
Серия
Анимационен филм
- @string/аниме
+ @string/anime
@string/ova
Торент
Документален филм
From fa6a620bf995073b48b4d5582e7266dae1a489f2 Mon Sep 17 00:00:00 2001
From: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Date: Wed, 26 Oct 2022 22:39:36 +0200
Subject: [PATCH 09/13] fix builds
---
.github/workflows/prerelease.yml | 2 +-
app/build.gradle.kts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml
index bac0430f..99ab00df 100644
--- a/.github/workflows/prerelease.yml
+++ b/.github/workflows/prerelease.yml
@@ -43,7 +43,7 @@ jobs:
echo "key_pwd=$KEY_PWD" >> $GITHUB_OUTPUT
- name: Run Gradle
run: |
- ./gradlew assemblePrereleaseRelease makeJar androidSourcesJar
+ ./gradlew assemblePrerelease makeJar androidSourcesJar
env:
SIGNING_KEY_ALIAS: "key0"
SIGNING_KEY_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }}
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index ed2ad5a0..42ca93fd 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -226,7 +226,7 @@ tasks.register("androidSourcesJar", Jar::class) {
// this is used by the gradlew plugin
tasks.register("makeJar", Copy::class) {
- from("build/intermediates/compile_app_classes_jar/debug")
+ from("build/intermediates/compile_app_classes_jar/prereleaseDebug")
into("build")
include("classes.jar")
dependsOn("build")
From 7e750a40e055b15ac303cf6eb8b977fa9a72de47 Mon Sep 17 00:00:00 2001
From: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Date: Wed, 26 Oct 2022 23:09:28 +0200
Subject: [PATCH 10/13] fix da build (again)
---
.github/workflows/prerelease.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml
index 99ab00df..4ce7dba1 100644
--- a/.github/workflows/prerelease.yml
+++ b/.github/workflows/prerelease.yml
@@ -56,6 +56,6 @@ jobs:
prerelease: true
title: "Pre-release Build"
files: |
- app/build/outputs/apk/prerelease/stable/*.apk
+ app/build/outputs/apk/prerelease/release/*.apk
app/build/libs/app-sources.jar
app/build/classes.jar
From 93cbd29f3dce5ee994030051f6a51271d330a631 Mon Sep 17 00:00:00 2001
From: Blatzar <46196380+Blatzar@users.noreply.github.com>
Date: Thu, 27 Oct 2022 15:38:53 +0200
Subject: [PATCH 11/13] Bumped the exoplayer version and fixed the audio &
video track selection
---
app/build.gradle.kts | 8 +-
.../cloudstream3/ui/ControllerActivity.kt | 14 +--
.../cloudstream3/ui/player/CS3IPlayer.kt | 100 ++++++++++++++----
.../cloudstream3/ui/player/GeneratorPlayer.kt | 5 +-
.../cloudstream3/ui/player/IPlayer.kt | 8 +-
.../ui/player/NonFinalTextRenderer.kt | 5 +-
.../ui/subtitles/SubtitlesFragment.kt | 2 +-
.../cloudstream3/utils/CastOptionsProvider.kt | 9 +-
8 files changed, 108 insertions(+), 43 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 42ca93fd..b3318675 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -155,10 +155,10 @@ dependencies {
// 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:exoplayer:2.18.1")
+ implementation("com.google.android.exoplayer:extension-cast:2.18.1")
+ implementation("com.google.android.exoplayer:extension-mediasession:2.18.1")
+ implementation("com.google.android.exoplayer:extension-okhttp:2.18.1")
//implementation("com.google.android.exoplayer:extension-leanback:2.14.0")
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
index ad0b4399..46ddce09 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
@@ -150,7 +150,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 +183,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 =
@@ -279,7 +281,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
val currentPosition = remoteMediaClient?.approximateStreamPosition
if (currentDuration != null && currentPosition != null)
DataStoreHelper.setViewPos(epData.id, currentPosition, currentDuration)
- } catch (t : Throwable) {
+ } catch (t: Throwable) {
logError(t)
}
@@ -358,10 +360,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())
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
index a838c85c..f60d8c78 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
@@ -15,6 +15,7 @@ import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
import com.google.android.exoplayer2.source.*
import com.google.android.exoplayer2.text.TextRenderer
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
+import com.google.android.exoplayer2.trackselection.TrackSelectionOverride
import com.google.android.exoplayer2.trackselection.TrackSelector
import com.google.android.exoplayer2.ui.SubtitleView
import com.google.android.exoplayer2.upstream.*
@@ -218,7 +219,43 @@ class CS3IPlayer : IPlayer {
var currentSubtitles: SubtitleData? = null
- override fun setMaxVideoSize(width: Int, height: Int) {
+ private fun List.getTrack(id: String?): Pair? {
+ if (id == null) return null
+ // This beast of an expression does:
+ // 1. Filter all audio tracks
+ // 2. Get all formats in said audio tacks
+ // 3. Gets all ids of the formats
+ // 4. Filters to find the first audio track with the same id as the audio track we are looking for
+ // 5. Returns the media group and the index of the audio track in the group
+ return this.firstNotNullOfOrNull { group ->
+ (0 until group.mediaTrackGroup.length).map {
+ group.getTrackFormat(it) to it
+ }.firstOrNull { it.first.id == id }
+ ?.let { group.mediaTrackGroup to it.second }
+ }
+ }
+
+ override fun setMaxVideoSize(width: Int, height: Int, id: String?) {
+ if (id != null) {
+ val videoTrack =
+ exoPlayer?.currentTracks?.groups?.filter { it.type == TRACK_TYPE_VIDEO }
+ ?.getTrack(id)
+
+ if (videoTrack != null) {
+ exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
+ ?.buildUpon()
+ ?.setOverrideForType(
+ TrackSelectionOverride(
+ videoTrack.first,
+ videoTrack.second
+ )
+ )
+ ?.build()
+ ?: return
+ return
+ }
+ }
+
exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
?.buildUpon()
?.setMaxVideoSize(width, height)
@@ -226,8 +263,29 @@ class CS3IPlayer : IPlayer {
?: return
}
- override fun setPreferredAudioTrack(trackLanguage: String?) {
+ override fun setPreferredAudioTrack(trackLanguage: String?, id: String?) {
preferredAudioTrackLanguage = trackLanguage
+
+ if (id != null) {
+ val audioTrack =
+ exoPlayer?.currentTracks?.groups?.filter { it.type == TRACK_TYPE_AUDIO }
+ ?.getTrack(id)
+
+ if (audioTrack != null) {
+ exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
+ ?.buildUpon()
+ ?.setOverrideForType(
+ TrackSelectionOverride(
+ audioTrack.first,
+ audioTrack.second
+ )
+ )
+ ?.build()
+ ?: return
+ return
+ }
+ }
+
exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
?.buildUpon()
?.setPreferredAudioLanguage(trackLanguage)
@@ -239,11 +297,11 @@ class CS3IPlayer : IPlayer {
/**
* Gets all supported formats in a list
* */
- private fun List.getFormats(): List {
+ private fun List.getFormats(): List> {
return this.map {
- (0 until it.trackGroup.length).mapNotNull { i ->
+ (0 until it.mediaTrackGroup.length).mapNotNull { i ->
if (it.isSupported)
- it.trackGroup.getFormat(i) // to it.isSelected
+ it.mediaTrackGroup.getFormat(i) to i
else null
}
}.flatten()
@@ -270,11 +328,12 @@ class CS3IPlayer : IPlayer {
}
override fun getVideoTracks(): CurrentTracks {
- val allTracks = exoPlayer?.currentTracksInfo?.trackGroupInfos ?: emptyList()
- val videoTracks = allTracks.filter { it.trackType == TRACK_TYPE_VIDEO }.getFormats()
- .map { it.toVideoTrack() }
- val audioTracks = allTracks.filter { it.trackType == TRACK_TYPE_AUDIO }.getFormats()
- .map { it.toAudioTrack() }
+ val allTracks = exoPlayer?.currentTracks?.groups ?: emptyList()
+ val videoTracks = allTracks.filter { it.type == TRACK_TYPE_VIDEO }
+ .getFormats()
+ .map { it.first.toVideoTrack() }
+ val audioTracks = allTracks.filter { it.type == TRACK_TYPE_AUDIO }.getFormats()
+ .map { it.first.toAudioTrack() }
return CurrentTracks(
exoPlayer?.videoFormat?.toVideoTrack(),
@@ -611,7 +670,12 @@ class CS3IPlayer : IPlayer {
} else it
}.toTypedArray()
}
- .setTrackSelector(trackSelector ?: getTrackSelector(context, maxVideoHeight))
+ .setTrackSelector(
+ trackSelector ?: getTrackSelector(
+ context,
+ maxVideoHeight
+ )
+ )
.setLoadControl(
DefaultLoadControl.Builder()
.setTargetBufferBytes(
@@ -781,10 +845,7 @@ class CS3IPlayer : IPlayer {
isPlaying = exo.isPlaying
}
exoPlayer?.addListener(object : Player.Listener {
- /**
- * Records the current used subtitle/track. Needed as exoplayer seems to have loose track language selection.
- * */
- override fun onTracksInfoChanged(tracksInfo: TracksInfo) {
+ override fun onTracksChanged(tracks: Tracks) {
fun Format.isSubtitle(): Boolean {
return this.sampleMimeType?.contains("video/") == false &&
this.sampleMimeType?.contains("audio/") == false
@@ -792,17 +853,17 @@ class CS3IPlayer : IPlayer {
normalSafeApiCall {
exoPlayerSelectedTracks =
- tracksInfo.trackGroupInfos.mapNotNull {
- val format = it.trackGroup.getFormat(0)
+ tracks.groups.mapNotNull {
+ val format = it.mediaTrackGroup.getFormat(0)
if (format.isSubtitle())
format.language?.let { lang -> lang to it.isSelected }
else null
}
- val exoPlayerReportedTracks = tracksInfo.trackGroupInfos.mapNotNull {
+ val exoPlayerReportedTracks = tracks.groups.mapNotNull {
// Filter out unsupported tracks
if (it.isSupported)
- it.trackGroup.getFormat(0)
+ it.mediaTrackGroup.getFormat(0)
else
null
}.mapNotNull {
@@ -827,7 +888,6 @@ class CS3IPlayer : IPlayer {
onTracksInfoChanged?.invoke()
subtitlesUpdates?.invoke()
}
- super.onTracksInfoChanged(tracksInfo)
}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
index e20a6c7b..172984a2 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
@@ -796,15 +796,16 @@ class GeneratorPlayer : FullScreenPlayer() {
}
tracksDialog.apply_btt?.setOnClickListener {
+ val currentTrack = currentAudioTracks.getOrNull(audioIndexStart)
player.setPreferredAudioTrack(
- currentAudioTracks.getOrNull(audioIndexStart)?.language
+ currentTrack?.language, currentTrack?.id
)
val currentVideo = currentVideoTracks.getOrNull(videoIndex)
val width = currentVideo?.width ?: NO_VALUE
val height = currentVideo?.height ?: NO_VALUE
if (width != NO_VALUE && height != NO_VALUE) {
- player.setMaxVideoSize(width, height)
+ player.setMaxVideoSize(width, height, currentVideo?.id)
}
tracksDialog.dismissSafe(activity)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt
index e8934250..473b3e65 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt
@@ -161,9 +161,9 @@ interface IPlayer {
fun getVideoTracks(): CurrentTracks
- /** If no parameters are set it'll default to no set size */
- fun setMaxVideoSize(width: Int = Int.MAX_VALUE, height: Int = Int.MAX_VALUE)
+ /** If no parameters are set it'll default to no set size, Specifying the id allows for track overrides to force the player to pick the quality. */
+ fun setMaxVideoSize(width: Int = Int.MAX_VALUE, height: Int = Int.MAX_VALUE, id: String? = null)
- /** If no trackLanguage is set it'll default to first track */
- fun setPreferredAudioTrack(trackLanguage: String?)
+ /** If no trackLanguage is set it'll default to first track. Specifying the id allows for track overrides as the language can be identical. */
+ fun setPreferredAudioTrack(trackLanguage: String?, id: String? = null)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt
index f7fb3139..7acb7c34 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt
@@ -75,7 +75,7 @@ open class NonFinalTextRenderer @JvmOverloads constructor(
return TAG
}
- @RendererCapabilities.Capabilities
+// @RendererCapabilities.Capabilities
override fun supportsFormat(format: Format): Int {
return if (decoderFactory.supportsFormat(format)) {
RendererCapabilities.create(
@@ -202,7 +202,8 @@ open class NonFinalTextRenderer @JvmOverloads constructor(
return
}
// Try and read the next subtitle from the source.
- @ReadDataResult val result =
+// @ReadDataResult
+ val result =
readSource(formatHold, nextInputBuffer, /* readFlags= */0)
if (result == C.RESULT_BUFFER_READ) {
if (nextInputBuffer.isEndOfStream) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt
index b97468e7..05a9416d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt
@@ -49,7 +49,7 @@ data class SaveCaptionStyle(
@JsonProperty("foregroundColor") var foregroundColor: Int,
@JsonProperty("backgroundColor") var backgroundColor: Int,
@JsonProperty("windowColor") var windowColor: Int,
- @CaptionStyleCompat.EdgeType
+// @CaptionStyleCompat.EdgeType
@JsonProperty("edgeType") var edgeType: Int,
@JsonProperty("edgeColor") var edgeColor: Int,
@FontRes
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt
index 17ec775b..ece9a4c0 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt
@@ -20,10 +20,13 @@ class CastOptionsProvider : OptionsProvider {
MediaIntentReceiver.ACTION_FORWARD,
MediaIntentReceiver.ACTION_STOP_CASTING
)
+
+ val name = ControllerActivity::class.qualifiedName!!
+
val compatButtonAction = intArrayOf(1, 3)
val notificationOptions =
NotificationOptions.Builder()
- .setTargetActivityClassName(ControllerActivity::class.qualifiedName)
+ .setTargetActivityClassName(name)
.setActions(buttonActions, compatButtonAction)
.setForward30DrawableResId(R.drawable.go_forward_30)
.setRewind30DrawableResId(R.drawable.go_back_30)
@@ -32,7 +35,7 @@ class CastOptionsProvider : OptionsProvider {
val mediaOptions = CastMediaOptions.Builder()
.setNotificationOptions(notificationOptions)
- .setExpandedControllerActivityClassName(ControllerActivity::class.qualifiedName)
+ .setExpandedControllerActivityClassName(name)
.build()
return CastOptions.Builder()
@@ -44,7 +47,7 @@ class CastOptionsProvider : OptionsProvider {
.build()
}
- override fun getAdditionalSessionProviders(p0: Context?): MutableList {
+ override fun getAdditionalSessionProviders(p0: Context): MutableList {
return Collections.emptyList()
}
}
\ No newline at end of file
From 6b586388b9ff68469a3312c61a3c7ec96e904609 Mon Sep 17 00:00:00 2001
From: Blatzar <46196380+Blatzar@users.noreply.github.com>
Date: Thu, 27 Oct 2022 18:31:38 +0200
Subject: [PATCH 12/13] Revert "Bumped the exoplayer version and fixed the
audio & video track selection"
This reverts commit 93cbd29f3dce5ee994030051f6a51271d330a631.
---
app/build.gradle.kts | 8 +-
.../cloudstream3/ui/ControllerActivity.kt | 14 +--
.../cloudstream3/ui/player/CS3IPlayer.kt | 100 ++++--------------
.../cloudstream3/ui/player/GeneratorPlayer.kt | 5 +-
.../cloudstream3/ui/player/IPlayer.kt | 8 +-
.../ui/player/NonFinalTextRenderer.kt | 5 +-
.../ui/subtitles/SubtitlesFragment.kt | 2 +-
.../cloudstream3/utils/CastOptionsProvider.kt | 9 +-
8 files changed, 43 insertions(+), 108 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index b3318675..42ca93fd 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -155,10 +155,10 @@ dependencies {
// implementation("androidx.leanback:leanback-paging:1.1.0-alpha09")
// Exoplayer
- implementation("com.google.android.exoplayer:exoplayer:2.18.1")
- implementation("com.google.android.exoplayer:extension-cast:2.18.1")
- implementation("com.google.android.exoplayer:extension-mediasession:2.18.1")
- implementation("com.google.android.exoplayer:extension-okhttp:2.18.1")
+ 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")
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
index 46ddce09..ad0b4399 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
@@ -150,7 +150,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
} else {
ChromecastSubtitlesFragment.getCurrentSavedStyle().apply {
val font = TextTrackStyle()
- font.setFontFamily(fontFamily ?: "Google Sans")
+ font.fontFamily = fontFamily ?: "Google Sans"
fontGenericFamily?.let {
font.fontGenericFamily = it
}
@@ -183,9 +183,7 @@ 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 =
@@ -281,7 +279,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
val currentPosition = remoteMediaClient?.approximateStreamPosition
if (currentDuration != null && currentPosition != null)
DataStoreHelper.setViewPos(epData.id, currentPosition, currentDuration)
- } catch (t: Throwable) {
+ } catch (t : Throwable) {
logError(t)
}
@@ -360,8 +358,10 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
}
}
- override fun onSessionConnected(castSession: CastSession) {
- super.onSessionConnected(castSession)
+ override fun onSessionConnected(castSession: CastSession?) {
+ castSession?.let {
+ super.onSessionConnected(it)
+ }
remoteMediaClient?.queueSetRepeatMode(REPEAT_MODE_REPEAT_OFF, JSONObject())
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
index f60d8c78..a838c85c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
@@ -15,7 +15,6 @@ import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
import com.google.android.exoplayer2.source.*
import com.google.android.exoplayer2.text.TextRenderer
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
-import com.google.android.exoplayer2.trackselection.TrackSelectionOverride
import com.google.android.exoplayer2.trackselection.TrackSelector
import com.google.android.exoplayer2.ui.SubtitleView
import com.google.android.exoplayer2.upstream.*
@@ -219,43 +218,7 @@ class CS3IPlayer : IPlayer {
var currentSubtitles: SubtitleData? = null
- private fun List.getTrack(id: String?): Pair? {
- if (id == null) return null
- // This beast of an expression does:
- // 1. Filter all audio tracks
- // 2. Get all formats in said audio tacks
- // 3. Gets all ids of the formats
- // 4. Filters to find the first audio track with the same id as the audio track we are looking for
- // 5. Returns the media group and the index of the audio track in the group
- return this.firstNotNullOfOrNull { group ->
- (0 until group.mediaTrackGroup.length).map {
- group.getTrackFormat(it) to it
- }.firstOrNull { it.first.id == id }
- ?.let { group.mediaTrackGroup to it.second }
- }
- }
-
- override fun setMaxVideoSize(width: Int, height: Int, id: String?) {
- if (id != null) {
- val videoTrack =
- exoPlayer?.currentTracks?.groups?.filter { it.type == TRACK_TYPE_VIDEO }
- ?.getTrack(id)
-
- if (videoTrack != null) {
- exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
- ?.buildUpon()
- ?.setOverrideForType(
- TrackSelectionOverride(
- videoTrack.first,
- videoTrack.second
- )
- )
- ?.build()
- ?: return
- return
- }
- }
-
+ override fun setMaxVideoSize(width: Int, height: Int) {
exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
?.buildUpon()
?.setMaxVideoSize(width, height)
@@ -263,29 +226,8 @@ class CS3IPlayer : IPlayer {
?: return
}
- override fun setPreferredAudioTrack(trackLanguage: String?, id: String?) {
+ override fun setPreferredAudioTrack(trackLanguage: String?) {
preferredAudioTrackLanguage = trackLanguage
-
- if (id != null) {
- val audioTrack =
- exoPlayer?.currentTracks?.groups?.filter { it.type == TRACK_TYPE_AUDIO }
- ?.getTrack(id)
-
- if (audioTrack != null) {
- exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
- ?.buildUpon()
- ?.setOverrideForType(
- TrackSelectionOverride(
- audioTrack.first,
- audioTrack.second
- )
- )
- ?.build()
- ?: return
- return
- }
- }
-
exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters
?.buildUpon()
?.setPreferredAudioLanguage(trackLanguage)
@@ -297,11 +239,11 @@ class CS3IPlayer : IPlayer {
/**
* Gets all supported formats in a list
* */
- private fun List.getFormats(): List> {
+ private fun List.getFormats(): List {
return this.map {
- (0 until it.mediaTrackGroup.length).mapNotNull { i ->
+ (0 until it.trackGroup.length).mapNotNull { i ->
if (it.isSupported)
- it.mediaTrackGroup.getFormat(i) to i
+ it.trackGroup.getFormat(i) // to it.isSelected
else null
}
}.flatten()
@@ -328,12 +270,11 @@ class CS3IPlayer : IPlayer {
}
override fun getVideoTracks(): CurrentTracks {
- val allTracks = exoPlayer?.currentTracks?.groups ?: emptyList()
- val videoTracks = allTracks.filter { it.type == TRACK_TYPE_VIDEO }
- .getFormats()
- .map { it.first.toVideoTrack() }
- val audioTracks = allTracks.filter { it.type == TRACK_TYPE_AUDIO }.getFormats()
- .map { it.first.toAudioTrack() }
+ val allTracks = exoPlayer?.currentTracksInfo?.trackGroupInfos ?: emptyList()
+ val videoTracks = allTracks.filter { it.trackType == TRACK_TYPE_VIDEO }.getFormats()
+ .map { it.toVideoTrack() }
+ val audioTracks = allTracks.filter { it.trackType == TRACK_TYPE_AUDIO }.getFormats()
+ .map { it.toAudioTrack() }
return CurrentTracks(
exoPlayer?.videoFormat?.toVideoTrack(),
@@ -670,12 +611,7 @@ class CS3IPlayer : IPlayer {
} else it
}.toTypedArray()
}
- .setTrackSelector(
- trackSelector ?: getTrackSelector(
- context,
- maxVideoHeight
- )
- )
+ .setTrackSelector(trackSelector ?: getTrackSelector(context, maxVideoHeight))
.setLoadControl(
DefaultLoadControl.Builder()
.setTargetBufferBytes(
@@ -845,7 +781,10 @@ class CS3IPlayer : IPlayer {
isPlaying = exo.isPlaying
}
exoPlayer?.addListener(object : Player.Listener {
- override fun onTracksChanged(tracks: Tracks) {
+ /**
+ * Records the current used subtitle/track. Needed as exoplayer seems to have loose track language selection.
+ * */
+ override fun onTracksInfoChanged(tracksInfo: TracksInfo) {
fun Format.isSubtitle(): Boolean {
return this.sampleMimeType?.contains("video/") == false &&
this.sampleMimeType?.contains("audio/") == false
@@ -853,17 +792,17 @@ class CS3IPlayer : IPlayer {
normalSafeApiCall {
exoPlayerSelectedTracks =
- tracks.groups.mapNotNull {
- val format = it.mediaTrackGroup.getFormat(0)
+ tracksInfo.trackGroupInfos.mapNotNull {
+ val format = it.trackGroup.getFormat(0)
if (format.isSubtitle())
format.language?.let { lang -> lang to it.isSelected }
else null
}
- val exoPlayerReportedTracks = tracks.groups.mapNotNull {
+ val exoPlayerReportedTracks = tracksInfo.trackGroupInfos.mapNotNull {
// Filter out unsupported tracks
if (it.isSupported)
- it.mediaTrackGroup.getFormat(0)
+ it.trackGroup.getFormat(0)
else
null
}.mapNotNull {
@@ -888,6 +827,7 @@ class CS3IPlayer : IPlayer {
onTracksInfoChanged?.invoke()
subtitlesUpdates?.invoke()
}
+ super.onTracksInfoChanged(tracksInfo)
}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
index 172984a2..e20a6c7b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
@@ -796,16 +796,15 @@ class GeneratorPlayer : FullScreenPlayer() {
}
tracksDialog.apply_btt?.setOnClickListener {
- val currentTrack = currentAudioTracks.getOrNull(audioIndexStart)
player.setPreferredAudioTrack(
- currentTrack?.language, currentTrack?.id
+ currentAudioTracks.getOrNull(audioIndexStart)?.language
)
val currentVideo = currentVideoTracks.getOrNull(videoIndex)
val width = currentVideo?.width ?: NO_VALUE
val height = currentVideo?.height ?: NO_VALUE
if (width != NO_VALUE && height != NO_VALUE) {
- player.setMaxVideoSize(width, height, currentVideo?.id)
+ player.setMaxVideoSize(width, height)
}
tracksDialog.dismissSafe(activity)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt
index 473b3e65..e8934250 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt
@@ -161,9 +161,9 @@ interface IPlayer {
fun getVideoTracks(): CurrentTracks
- /** If no parameters are set it'll default to no set size, Specifying the id allows for track overrides to force the player to pick the quality. */
- fun setMaxVideoSize(width: Int = Int.MAX_VALUE, height: Int = Int.MAX_VALUE, id: String? = null)
+ /** If no parameters are set it'll default to no set size */
+ fun setMaxVideoSize(width: Int = Int.MAX_VALUE, height: Int = Int.MAX_VALUE)
- /** If no trackLanguage is set it'll default to first track. Specifying the id allows for track overrides as the language can be identical. */
- fun setPreferredAudioTrack(trackLanguage: String?, id: String? = null)
+ /** If no trackLanguage is set it'll default to first track */
+ fun setPreferredAudioTrack(trackLanguage: String?)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt
index 7acb7c34..f7fb3139 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt
@@ -75,7 +75,7 @@ open class NonFinalTextRenderer @JvmOverloads constructor(
return TAG
}
-// @RendererCapabilities.Capabilities
+ @RendererCapabilities.Capabilities
override fun supportsFormat(format: Format): Int {
return if (decoderFactory.supportsFormat(format)) {
RendererCapabilities.create(
@@ -202,8 +202,7 @@ open class NonFinalTextRenderer @JvmOverloads constructor(
return
}
// Try and read the next subtitle from the source.
-// @ReadDataResult
- val result =
+ @ReadDataResult val result =
readSource(formatHold, nextInputBuffer, /* readFlags= */0)
if (result == C.RESULT_BUFFER_READ) {
if (nextInputBuffer.isEndOfStream) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt
index 05a9416d..b97468e7 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt
@@ -49,7 +49,7 @@ data class SaveCaptionStyle(
@JsonProperty("foregroundColor") var foregroundColor: Int,
@JsonProperty("backgroundColor") var backgroundColor: Int,
@JsonProperty("windowColor") var windowColor: Int,
-// @CaptionStyleCompat.EdgeType
+ @CaptionStyleCompat.EdgeType
@JsonProperty("edgeType") var edgeType: Int,
@JsonProperty("edgeColor") var edgeColor: Int,
@FontRes
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt
index ece9a4c0..17ec775b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt
@@ -20,13 +20,10 @@ class CastOptionsProvider : OptionsProvider {
MediaIntentReceiver.ACTION_FORWARD,
MediaIntentReceiver.ACTION_STOP_CASTING
)
-
- val name = ControllerActivity::class.qualifiedName!!
-
val compatButtonAction = intArrayOf(1, 3)
val notificationOptions =
NotificationOptions.Builder()
- .setTargetActivityClassName(name)
+ .setTargetActivityClassName(ControllerActivity::class.qualifiedName)
.setActions(buttonActions, compatButtonAction)
.setForward30DrawableResId(R.drawable.go_forward_30)
.setRewind30DrawableResId(R.drawable.go_back_30)
@@ -35,7 +32,7 @@ class CastOptionsProvider : OptionsProvider {
val mediaOptions = CastMediaOptions.Builder()
.setNotificationOptions(notificationOptions)
- .setExpandedControllerActivityClassName(name)
+ .setExpandedControllerActivityClassName(ControllerActivity::class.qualifiedName)
.build()
return CastOptions.Builder()
@@ -47,7 +44,7 @@ class CastOptionsProvider : OptionsProvider {
.build()
}
- override fun getAdditionalSessionProviders(p0: Context): MutableList {
+ override fun getAdditionalSessionProviders(p0: Context?): MutableList {
return Collections.emptyList()
}
}
\ No newline at end of file
From 997420a942bd643f17d0fb00d221af9a1faa2ef4 Mon Sep 17 00:00:00 2001
From: reduplicated <110570621+reduplicated@users.noreply.github.com>
Date: Fri, 28 Oct 2022 03:51:27 +0200
Subject: [PATCH 13/13] phone UI changes + cache
---
.../cloudstream3/ui/APIRepository.kt | 17 +-
.../lagradost/cloudstream3/ui/WatchType.kt | 13 +-
.../cloudstream3/ui/home/HomeFragment.kt | 235 ++---
.../cloudstream3/ui/home/HomeViewModel.kt | 39 +-
.../ui/result/ResultViewModel2.kt | 47 +-
.../cloudstream3/ui/search/SearchFragment.kt | 241 ++----
.../ui/settings/extensions/PluginsFragment.kt | 59 +-
app/src/main/res/color/chip_color.xml | 5 +
app/src/main/res/layout/activity_main.xml | 118 +--
app/src/main/res/layout/fragment_home.xml | 814 +++++++++---------
app/src/main/res/layout/fragment_plugins.xml | 103 +--
app/src/main/res/layout/fragment_search.xml | 81 +-
.../main/res/layout/home_select_mainpage.xml | 156 +---
app/src/main/res/layout/tvtypes_chips.xml | 68 ++
.../main/res/layout/tvtypes_chips_scroll.xml | 10 +
app/src/main/res/values/styles.xml | 4 +
16 files changed, 905 insertions(+), 1105 deletions(-)
create mode 100644 app/src/main/res/color/chip_color.xml
create mode 100644 app/src/main/res/layout/tvtypes_chips.xml
create mode 100644 app/src/main/res/layout/tvtypes_chips_scroll.xml
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt
index 0e5e544b..ce695873 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt
@@ -26,6 +26,8 @@ class APIRepository(val api: MainAPI) {
fun isInvalidData(data: String): Boolean {
return data.isEmpty() || data == "[]" || data == "about:blank"
}
+
+ private val cacheHash: HashMap, LoadResponse> = hashMapOf()
}
val hasMainPage = api.hasMainPage
@@ -39,7 +41,13 @@ class APIRepository(val api: MainAPI) {
suspend fun load(url: String): Resource {
return safeApiCall {
if (isInvalidData(url)) throw ErrorLoadingException()
- api.load(api.fixUrl(url)) ?: throw ErrorLoadingException()
+ val fixedUrl = api.fixUrl(url)
+ val key = Pair(api.name,url)
+ cacheHash[key] ?: api.load(fixedUrl)?.also {
+ // we cache 20 responses because ppl often go back to the same shit + 20 because I dont want to cause too much memory leak
+ if (cacheHash.size > 20) cacheHash.remove(cacheHash.keys.random())
+ cacheHash[key] = it
+ } ?: throw ErrorLoadingException()
}
}
@@ -75,7 +83,12 @@ class APIRepository(val api: MainAPI) {
api.lastHomepageRequest = unixTimeMS
nameIndex?.let { api.mainPage.getOrNull(it) }?.let { data ->
- listOf(api.getMainPage(page, MainPageRequest(data.name, data.data, data.horizontalImages)))
+ listOf(
+ api.getMainPage(
+ page,
+ MainPageRequest(data.name, data.data, data.horizontalImages)
+ )
+ )
} ?: run {
if (api.sequentialMainPage) {
var first = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt
index 417b4408..eb4eb666 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt
@@ -5,13 +5,12 @@ import androidx.annotation.StringRes
import com.lagradost.cloudstream3.R
enum class WatchType(val internalId: Int, @StringRes val stringRes: Int, @DrawableRes val iconRes: Int) {
- // FIX ICONS
- WATCHING(0, R.string.type_watching, R.drawable.ic_baseline_remove_red_eye_24),
- COMPLETED(1, R.string.type_completed, R.drawable.ic_baseline_check_24),
- ONHOLD(2, R.string.type_on_hold, R.drawable.ic_baseline_pause_24),
- DROPPED(3, R.string.type_dropped, R.drawable.ic_baseline_close_24),
- PLANTOWATCH(4, R.string.type_plan_to_watch, R.drawable.ic_baseline_close_24),
- NONE(5, R.string.type_none, R.drawable.ic_baseline_remove_red_eye_24);
+ WATCHING(0, R.string.type_watching, R.drawable.ic_baseline_bookmark_24),
+ COMPLETED(1, R.string.type_completed, R.drawable.ic_baseline_bookmark_24),
+ ONHOLD(2, R.string.type_on_hold, R.drawable.ic_baseline_bookmark_24),
+ DROPPED(3, R.string.type_dropped, R.drawable.ic_baseline_bookmark_24),
+ PLANTOWATCH(4, R.string.type_plan_to_watch, R.drawable.ic_baseline_bookmark_24),
+ NONE(5, R.string.type_none, R.drawable.ic_baseline_add_24);
companion object {
fun fromInternalId(id: Int?) = values().find { value -> value.internalId == id } ?: NONE
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt
index 0d571b76..2f539a33 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt
@@ -14,6 +14,8 @@ import android.view.ViewGroup
import android.widget.*
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SearchView
+import androidx.core.content.ContextCompat
+import androidx.core.content.ContextCompat.getDrawable
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
@@ -26,11 +28,14 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.button.MaterialButton
+import com.google.android.material.chip.Chip
+import com.google.android.material.chip.ChipGroup
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
+import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.MainActivity.Companion.mainPluginsLoadedEvent
@@ -43,6 +48,7 @@ import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
import com.lagradost.cloudstream3.ui.AutofitRecyclerView
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
+import com.lagradost.cloudstream3.ui.result.ResultViewModel2.Companion.updateWatchStatus
import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
import com.lagradost.cloudstream3.ui.search.*
@@ -50,6 +56,7 @@ import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallba
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.isRecyclerScrollable
+import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
import com.lagradost.cloudstream3.utils.AppUtils.setMaxViewPoolSize
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
@@ -61,11 +68,11 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllResumeStateIds
import com.lagradost.cloudstream3.utils.DataStoreHelper.removeLastWatched
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
import com.lagradost.cloudstream3.utils.Event
+import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
-import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarView
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
import com.lagradost.cloudstream3.utils.UIHelper.setImage
@@ -82,14 +89,12 @@ import kotlinx.android.synthetic.main.fragment_home.home_loading
import kotlinx.android.synthetic.main.fragment_home.home_loading_error
import kotlinx.android.synthetic.main.fragment_home.home_loading_shimmer
import kotlinx.android.synthetic.main.fragment_home.home_loading_statusbar
-import kotlinx.android.synthetic.main.fragment_home.home_main_poster_recyclerview
import kotlinx.android.synthetic.main.fragment_home.home_master_recycler
import kotlinx.android.synthetic.main.fragment_home.home_plan_to_watch_btt
import kotlinx.android.synthetic.main.fragment_home.home_provider_meta_info
import kotlinx.android.synthetic.main.fragment_home.home_provider_name
import kotlinx.android.synthetic.main.fragment_home.home_reload_connection_open_in_browser
import kotlinx.android.synthetic.main.fragment_home.home_reload_connectionerror
-import kotlinx.android.synthetic.main.fragment_home.home_statusbar
import kotlinx.android.synthetic.main.fragment_home.home_type_completed_btt
import kotlinx.android.synthetic.main.fragment_home.home_type_dropped_btt
import kotlinx.android.synthetic.main.fragment_home.home_type_on_hold_btt
@@ -100,6 +105,8 @@ import kotlinx.android.synthetic.main.fragment_home.home_watch_parent_item_title
import kotlinx.android.synthetic.main.fragment_home.result_error_text
import kotlinx.android.synthetic.main.fragment_home_tv.*
import kotlinx.android.synthetic.main.home_episodes_expanded.*
+import kotlinx.android.synthetic.main.tvtypes_chips.*
+import kotlinx.android.synthetic.main.tvtypes_chips.view.*
import java.util.*
const val HOME_BOOKMARK_VALUE_LIST = "home_bookmarked_last_list"
@@ -247,16 +254,16 @@ class HomeFragment : Fragment() {
}
fun getPairList(
- anime: MaterialButton?,
- cartoons: MaterialButton?,
- tvs: MaterialButton?,
- docs: MaterialButton?,
- movies: MaterialButton?,
- asian: MaterialButton?,
- livestream: MaterialButton?,
- nsfw: MaterialButton?,
- others: MaterialButton?,
- ): List>> {
+ anime: Chip?,
+ cartoons: Chip?,
+ tvs: Chip?,
+ docs: Chip?,
+ movies: Chip?,
+ asian: Chip?,
+ livestream: Chip?,
+ nsfw: Chip?,
+ others: Chip?,
+ ): List>> {
// This list should be same order as home screen to aid navigation
return listOf(
Pair(movies, listOf(TvType.Movie, TvType.Torrent)),
@@ -271,6 +278,59 @@ class HomeFragment : Fragment() {
)
}
+ private fun getPairList(header: ChipGroup) = getPairList(
+ header.home_select_anime,
+ header.home_select_cartoons,
+ header.home_select_tv_series,
+ header.home_select_documentaries,
+ header.home_select_movies,
+ header.home_select_asian,
+ header.home_select_livestreams,
+ header.home_select_nsfw,
+ header.home_select_others
+ )
+
+ fun validateChips(header: ChipGroup?, validTypes: List) {
+ if (header == null) return
+ val pairList = getPairList(header)
+ for ((button, types) in pairList) {
+ val isValid = validTypes.any { types.contains(it) }
+ button?.isVisible = isValid
+ }
+ }
+
+ fun updateChips(header: ChipGroup?, selectedTypes: List) {
+ if (header == null) return
+ val pairList = getPairList(header)
+ for ((button, types) in pairList) {
+ button?.isChecked =
+ button?.isVisible == true && selectedTypes.any { types.contains(it) }
+ }
+ }
+
+ fun bindChips(
+ header: ChipGroup?,
+ selectedTypes: List,
+ validTypes: List,
+ callback: (List) -> Unit
+ ) {
+ if (header == null) return
+ val pairList = getPairList(header)
+ for ((button, types) in pairList) {
+ val isValid = validTypes.any { types.contains(it) }
+ button?.isVisible = isValid
+ button?.isChecked = isValid && selectedTypes.any { types.contains(it) }
+ button?.setOnCheckedChangeListener { _, _ ->
+ val list = ArrayList()
+ for ((sbutton, vvalidTypes) in pairList) {
+ if (sbutton?.isChecked == true)
+ list.addAll(vvalidTypes)
+ }
+ callback(list)
+ }
+ }
+ }
+
fun Context.selectHomepage(selectedApiName: String?, callback: (String) -> Unit) {
val validAPIs = filterProviderByPreferredMedia().toMutableList()
@@ -296,21 +356,9 @@ class HomeFragment : Fragment() {
?.toMutableList()
?: mutableListOf(TvType.Movie, TvType.TvSeries)
- val anime = dialog.findViewById(R.id.home_select_anime)
- val cartoons = dialog.findViewById(R.id.home_select_cartoons)
- val tvs = dialog.findViewById(R.id.home_select_tv_series)
- val docs = dialog.findViewById(R.id.home_select_documentaries)
- val movies = dialog.findViewById(R.id.home_select_movies)
- val asian = dialog.findViewById(R.id.home_select_asian)
- val livestream = dialog.findViewById(R.id.home_select_livestreams)
- val nsfw = dialog.findViewById(R.id.home_select_nsfw)
- val others = dialog.findViewById(R.id.home_select_others)
val cancelBtt = dialog.findViewById(R.id.cancel_btt)
val applyBtt = dialog.findViewById(R.id.apply_btt)
- val pairList =
- getPairList(anime, cartoons, tvs, docs, movies, asian, livestream, nsfw, others)
-
cancelBtt?.setOnClickListener {
dialog.dismissSafe()
}
@@ -355,52 +403,14 @@ class HomeFragment : Fragment() {
arrayAdapter.notifyDataSetChanged()
}
- /**
- * Since fire tv is fucked we need to manually define the focus layout.
- * Since visible buttons are only known in runtime this is required.
- **/
- var lastButton: MaterialButton? = null
-
- for ((button, validTypes) in pairList) {
- val isValid =
- validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } }
- button?.isVisible = isValid
- if (isValid) {
-
- // Set focus navigation
- button?.let { currentButton ->
- lastButton?.nextFocusRightId = currentButton.id
- lastButton?.id?.let { currentButton.nextFocusLeftId = it }
- lastButton = currentButton
- }
-
- fun buttonContains(): Boolean {
- return preSelectedTypes.any { validTypes.contains(it) }
- }
-
- button?.isSelected = buttonContains()
- button?.setOnClickListener {
- preSelectedTypes.clear()
- preSelectedTypes.addAll(validTypes)
- for ((otherButton, _) in pairList) {
- otherButton?.isSelected = false
- }
- button.isSelected = true
- updateList()
- }
-
- button?.setOnLongClickListener {
- if (!buttonContains()) {
- button.isSelected = true
- preSelectedTypes.addAll(validTypes)
- } else {
- button.isSelected = false
- preSelectedTypes.removeAll(validTypes)
- }
- updateList()
- return@setOnLongClickListener true
- }
- }
+ bindChips(
+ dialog.home_select_group,
+ preSelectedTypes,
+ validAPIs.flatMap { it.supportedTypes }.distinct()
+ ) { list ->
+ preSelectedTypes.clear()
+ preSelectedTypes.addAll(list)
+ updateList()
}
updateList()
}
@@ -422,7 +432,6 @@ class HomeFragment : Fragment() {
}
private fun toggleMainVisibility(visible: Boolean) {
- home_main_holder?.isVisible = visible
home_main_poster_recyclerview?.isVisible = visible
}
@@ -531,6 +540,51 @@ class HomeFragment : Fragment() {
home_random?.visibility = View.GONE
}
+ observe(homeViewModel.preview) { preview ->
+ when (preview) {
+ is Resource.Success -> {
+ home_preview?.isVisible = true
+ preview.value.apply {
+ home_preview_tags?.text = tags?.joinToString(" • ") ?: ""
+ home_preview_tags?.isGone = tags.isNullOrEmpty()
+ home_preview_image?.setImage(posterUrl, posterHeaders)
+ home_preview_title?.text = name
+ home_preview_play?.setOnClickListener {
+ activity?.loadResult(url, apiName, START_ACTION_RESUME_LATEST)
+ //activity.loadSearchResult(url, START_ACTION_RESUME_LATEST)
+ }
+ home_preview_info?.setOnClickListener {
+ activity?.loadResult(url, apiName)
+ //activity.loadSearchResult(random)
+ }
+ // very ugly code, but I dont care
+ val watchType = DataStoreHelper.getResultWatchState(preview.value.getId())
+ home_preview_bookmark?.setText(watchType.stringRes)
+ home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds(null,getDrawable(home_preview_bookmark.context, watchType.iconRes),null,null)
+ home_preview_bookmark?.setOnClickListener { fab ->
+ activity?.showBottomDialog(
+ WatchType.values().map { fab.context.getString(it.stringRes) }
+ .toList(),
+ DataStoreHelper.getResultWatchState(preview.value.getId()).ordinal,
+ fab.context.getString(R.string.action_add_to_bookmarks),
+ showApply = false,
+ {}) {
+ val newValue = WatchType.values()[it]
+ home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds(null,getDrawable(home_preview_bookmark.context, newValue.iconRes),null,null)
+ home_preview_bookmark?.setText(newValue.stringRes)
+
+ updateWatchStatus(preview.value, newValue)
+ reloadStored()
+ }
+ }
+ }
+ }
+ else -> {
+ home_preview?.isVisible = false
+ }
+ }
+ }
+
observe(homeViewModel.apiName) { apiName ->
currentApiName = apiName
// setKey(USER_SELECTED_HOMEPAGE_API, apiName)
@@ -563,17 +617,17 @@ class HomeFragment : Fragment() {
HomeChildItemAdapter(
mutableListOf(),
R.layout.home_result_big_grid,
- nextFocusUp = home_main_poster_recyclerview.nextFocusUpId,
- nextFocusDown = home_main_poster_recyclerview.nextFocusDownId
+ nextFocusUp = home_main_poster_recyclerview?.nextFocusUpId,
+ nextFocusDown = home_main_poster_recyclerview?.nextFocusDownId
) { callback ->
homeHandleSearch(callback)
}
- home_main_poster_recyclerview.setLinearListLayout()
+ home_main_poster_recyclerview?.setLinearListLayout()
observe(homeViewModel.randomItems) { items ->
if (items.isNullOrEmpty()) {
toggleMainVisibility(false)
} else {
- val tempAdapter = home_main_poster_recyclerview.adapter as HomeChildItemAdapter?
+ val tempAdapter = home_main_poster_recyclerview?.adapter as? HomeChildItemAdapter?
// no need to reload if it has the same data
if (tempAdapter != null && tempAdapter.cardList == items) {
toggleMainVisibility(true)
@@ -938,7 +992,7 @@ class HomeFragment : Fragment() {
}
}
- context?.fixPaddingStatusbarView(home_statusbar)
+ //context?.fixPaddingStatusbarView(home_statusbar)
context?.fixPaddingStatusbar(home_loading_statusbar)
home_master_recycler.adapter =
@@ -959,33 +1013,6 @@ class HomeFragment : Fragment() {
}
} // GridLayoutManager(context, 1).also { it.supportsPredictiveItemAnimations() }
- if (!isTvSettings()) {
- LinearSnapHelper().attachToRecyclerView(home_main_poster_recyclerview) // snap
- val centerLayoutManager =
- CenterZoomLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
- centerLayoutManager.setOnSizeListener { index ->
- (home_main_poster_recyclerview?.adapter as HomeChildItemAdapter?)?.cardList?.get(
- index
- )?.let { random ->
- home_main_play?.setOnClickListener {
- activity.loadSearchResult(random, START_ACTION_RESUME_LATEST)
- }
- home_main_info?.setOnClickListener {
- activity.loadSearchResult(random)
- }
-
- home_main_text?.text =
- random.name + if (random is AnimeSearchResponse && !random.dubStatus.isNullOrEmpty()) {
- random.dubStatus?.joinToString(
- prefix = " • ",
- separator = " | "
- ) { it.name }
- } else ""
- }
- }
- home_main_poster_recyclerview?.layoutManager = centerLayoutManager // scale
- }
-
reloadStored()
loadHomePage()
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt
index 30fd45c1..0b928a55 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt
@@ -13,6 +13,7 @@ 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.HomePageList
+import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.mvvm.*
@@ -32,7 +33,6 @@ import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.*
import kotlin.collections.set
@@ -58,7 +58,9 @@ class HomeViewModel : ViewModel() {
val bookmarks: LiveData>> = _bookmarks
private val _resumeWatching = MutableLiveData>()
+ private val _preview = MutableLiveData>()
val resumeWatching: LiveData> = _resumeWatching
+ val preview: LiveData> = _preview
fun loadResumeWatching() = viewModelScope.launchSafe {
val resumeWatching = withContext(Dispatchers.IO) {
@@ -207,6 +209,7 @@ class HomeViewModel : ViewModel() {
expandAndReturn(name)
}
+
private fun load(api: MainAPI?) = viewModelScope.launchSafe {
repo = if (api != null) {
APIRepository(api)
@@ -219,6 +222,7 @@ class HomeViewModel : ViewModel() {
if (repo?.hasMainPage == true) {
_page.postValue(Resource.Loading())
+ _preview.postValue(Resource.Loading())
when (val data = repo?.getMainPage(1, null)) {
is Resource.Success -> {
@@ -232,8 +236,38 @@ class HomeViewModel : ViewModel() {
ExpandableHomepageList(filteredList, 1, home.hasNext)
}
}
- _page.postValue(Resource.Success(expandable))
val items = data.value.mapNotNull { it?.items }.flatten()
+ items.randomOrNull()?.list?.randomOrNull()?.url?.let { url ->
+ // backup request in case first fails
+ var first = repo?.load(url)
+ if(first == null ||first is Resource.Failure) {
+ first = repo?.load(items.random().list.random().url)
+ }
+ first?.let {
+ _preview.postValue(it)
+ } ?: run {
+ _preview.postValue(
+ Resource.Failure(
+ false,
+ null,
+ null,
+ "No repo found, this should never happen"
+ )
+ )
+ }
+ } ?: run {
+ _preview.postValue(
+ Resource.Failure(
+ false,
+ null,
+ null,
+ "No homepage items"
+ )
+ )
+ }
+
+ _page.postValue(Resource.Success(expandable))
+
//val home = data.value
if (items.isNotEmpty()) {
@@ -263,6 +297,7 @@ class HomeViewModel : ViewModel() {
}
} else {
_page.postValue(Resource.Success(emptyMap()))
+ _preview.postValue(Resource.Failure(false, null, null, "No homepage"))
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index 906b652d..7c099793 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -412,6 +412,29 @@ class ResultViewModel2 : ViewModel() {
return this?.firstOrNull { it.season == season }
}
+ fun updateWatchStatus(currentResponse : LoadResponse, status: WatchType) {
+ val currentId = currentResponse.getId()
+ val resultPage = currentResponse
+
+ DataStoreHelper.setResultWatchState(currentId, status.internalId)
+ val current = DataStoreHelper.getBookmarkedData(currentId)
+ val currentTime = System.currentTimeMillis()
+ DataStoreHelper.setBookmarkedData(
+ currentId,
+ DataStoreHelper.BookmarkedData(
+ currentId,
+ current?.bookmarkedTime ?: currentTime,
+ currentTime,
+ resultPage.name,
+ resultPage.url,
+ resultPage.apiName,
+ resultPage.type,
+ resultPage.posterUrl,
+ resultPage.year
+ )
+ )
+ }
+
private fun filterName(name: String?): String? {
if (name == null) return null
Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let {
@@ -764,28 +787,10 @@ class ResultViewModel2 : ViewModel() {
private val _selectPopup: MutableLiveData> = MutableLiveData(Some.None)
val selectPopup: LiveData> get() = _selectPopup
- fun updateWatchStatus(status: WatchType) {
- val currentId = currentId ?: return
- val resultPage = currentResponse ?: return
- _watchStatus.postValue(status)
- DataStoreHelper.setResultWatchState(currentId, status.internalId)
- val current = DataStoreHelper.getBookmarkedData(currentId)
- val currentTime = System.currentTimeMillis()
- DataStoreHelper.setBookmarkedData(
- currentId,
- DataStoreHelper.BookmarkedData(
- currentId,
- current?.bookmarkedTime ?: currentTime,
- currentTime,
- resultPage.name,
- resultPage.url,
- resultPage.apiName,
- resultPage.type,
- resultPage.posterUrl,
- resultPage.year
- )
- )
+ fun updateWatchStatus(status: WatchType) {
+ updateWatchStatus(currentResponse ?: return,status)
+ _watchStatus.postValue(status)
}
private fun startChromecast(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
index 5f108cd1..8f588bf7 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
@@ -26,15 +26,19 @@ import com.lagradost.cloudstream3.APIHolder.filterSearchResultByFilmQuality
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.APIHolder.getApiSettings
+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.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.home.HomeFragment
+import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.currentSpan
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList
+import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.updateChips
import com.lagradost.cloudstream3.ui.home.ParentItemAdapter
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
import com.lagradost.cloudstream3.utils.Coroutines.main
@@ -46,6 +50,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import kotlinx.android.synthetic.main.fragment_search.*
+import kotlinx.android.synthetic.main.tvtypes_chips.*
import java.util.concurrent.locks.ReentrantLock
const val SEARCH_PREF_TAGS = "search_pref_tags"
@@ -130,65 +135,26 @@ class SearchFragment : Fragment() {
// Null if defined as a variable
// This needs to be run after view created
- private fun getPairList(): List>> {
- return HomeFragment.getPairList(
- search_select_anime,
- search_select_cartoons,
- search_select_tv_series,
- search_select_documentaries,
- search_select_movies,
- search_select_asian,
- search_select_livestreams,
- search_select_nsfw,
- search_select_others
- )
- }
private fun reloadRepos(success: Boolean = false) = main {
- val pairList = getPairList()
-
searchViewModel.reloadRepos()
context?.filterProviderByPreferredMedia()?.let { validAPIs ->
- for ((button, validTypes) in pairList) {
- val isValid =
- validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } }
- button?.isVisible = isValid
- if (isValid) {
- fun buttonContains(): Boolean {
- return selectedSearchTypes.any { validTypes.contains(it) }
- }
-
- button?.isSelected = buttonContains()
- button?.setOnClickListener {
- val last = selectedSearchTypes.toSet()
- selectedSearchTypes.clear()
- selectedSearchTypes.addAll(validTypes)
- for ((otherButton, _) in pairList) {
- otherButton?.isSelected = false
- }
- it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
- it?.isSelected = true
- if (last != selectedSearchTypes.toSet()) // if you click the same button again the it does nothing
- search(main_search?.query?.toString())
- }
-
- button?.setOnLongClickListener {
- if (!buttonContains()) {
- it?.isSelected = true
- selectedSearchTypes.addAll(validTypes)
- } else {
- it?.isSelected = false
- selectedSearchTypes.removeAll(validTypes)
- }
- it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
- search(main_search?.query?.toString())
- return@setOnLongClickListener true
- }
+ bindChips(
+ home_select_group,
+ selectedSearchTypes,
+ validAPIs.flatMap { api -> api.supportedTypes }.distinct()
+ ) { list ->
+ if (selectedSearchTypes.toSet() != list.toSet()) {
+ setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
+ selectedSearchTypes.clear()
+ selectedSearchTypes.addAll(list)
+ search(main_search?.query?.toString())
}
}
}
}
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -239,31 +205,67 @@ class SearchFragment : Fragment() {
builder.let { dialog ->
val isMultiLang = ctx.getApiProviderLangSettings().size > 1
- val anime = dialog.findViewById(R.id.home_select_anime)
- val cartoons = dialog.findViewById(R.id.home_select_cartoons)
- val tvs = dialog.findViewById(R.id.home_select_tv_series)
- val docs = dialog.findViewById(R.id.home_select_documentaries)
- val movies = dialog.findViewById(R.id.home_select_movies)
- val asian = dialog.findViewById(R.id.home_select_asian)
- val livestream =
- dialog.findViewById(R.id.home_select_livestreams)
- val nsfw = dialog.findViewById(R.id.home_select_nsfw)
- val other = dialog.findViewById(R.id.home_select_others)
val cancelBtt = dialog.findViewById(R.id.cancel_btt)
val applyBtt = dialog.findViewById(R.id.apply_btt)
- val pairList =
- HomeFragment.getPairList(
- anime,
- cartoons,
- tvs,
- docs,
- movies,
- asian,
- livestream,
- nsfw,
- other
- )
+ val listView = dialog.findViewById(R.id.listview1)
+ val arrayAdapter = ArrayAdapter(ctx, R.layout.sort_bottom_single_choice)
+ listView?.adapter = arrayAdapter
+ listView?.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE
+
+ listView?.setOnItemClickListener { _, _, i, _ ->
+ if (currentValidApis.isNotEmpty()) {
+ val api = currentValidApis[i].name
+ if (currentSelectedApis.contains(api)) {
+ listView.setItemChecked(i, false)
+ currentSelectedApis -= api
+ } else {
+ listView.setItemChecked(i, true)
+ currentSelectedApis += api
+ }
+ }
+ }
+
+ fun updateList(types: List) {
+ setKey(SEARCH_PREF_TAGS, types.map {it.name})
+
+ arrayAdapter.clear()
+ currentValidApis = validAPIs.filter { api ->
+ api.supportedTypes.any {
+ types.contains(it)
+ }
+ }.sortedBy { it.name.lowercase() }
+
+ val names = currentValidApis.map {
+ if (isMultiLang) "${
+ SubtitleHelper.getFlagFromIso(
+ it.lang
+ )?.plus(" ") ?: ""
+ }${it.name}" else it.name
+ }
+ for ((index, api) in currentValidApis.map { it.name }.withIndex()) {
+ listView?.setItemChecked(index, currentSelectedApis.contains(api))
+ }
+
+ //arrayAdapter.notifyDataSetChanged()
+ arrayAdapter.addAll(names)
+ arrayAdapter.notifyDataSetChanged()
+ }
+
+ val selectedSearchTypes = getKey>(SEARCH_PREF_TAGS)
+ ?.mapNotNull { listName ->
+ TvType.values().firstOrNull { it.name == listName }
+ }
+ ?.toMutableList()
+ ?: mutableListOf(TvType.Movie, TvType.TvSeries)
+
+ bindChips(
+ dialog.home_select_group,
+ selectedSearchTypes,
+ TvType.values().toList()
+ ) { list ->
+ updateList(list)
+ }
cancelBtt?.setOnClickListener {
dialog.dismissSafe()
@@ -284,90 +286,7 @@ class SearchFragment : Fragment() {
context?.setKey(SEARCH_PREF_PROVIDERS, currentSelectedApis.toList())
selectedApis = currentSelectedApis
}
-
- val selectedSearchTypes = context?.getKey>(SEARCH_PREF_TAGS)
- ?.mapNotNull { listName ->
- TvType.values().firstOrNull { it.name == listName }
- }
- ?.toMutableList()
- ?: mutableListOf(TvType.Movie, TvType.TvSeries)
-
- val listView = dialog.findViewById(R.id.listview1)
- val arrayAdapter = ArrayAdapter(ctx, R.layout.sort_bottom_single_choice)
- listView?.adapter = arrayAdapter
- listView?.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE
-
- listView?.setOnItemClickListener { _, _, i, _ ->
- if (currentValidApis.isNotEmpty()) {
- val api = currentValidApis[i].name
- if (currentSelectedApis.contains(api)) {
- listView.setItemChecked(i, false)
- currentSelectedApis -= api
- } else {
- listView.setItemChecked(i, true)
- currentSelectedApis += api
- }
- }
- }
-
- fun updateList() {
- arrayAdapter.clear()
- currentValidApis = validAPIs.filter { api ->
- api.supportedTypes.any {
- selectedSearchTypes.contains(it)
- }
- }.sortedBy { it.name.lowercase() }
-
- val names = currentValidApis.map {
- if (isMultiLang) "${
- SubtitleHelper.getFlagFromIso(
- it.lang
- )?.plus(" ") ?: ""
- }${it.name}" else it.name
- }
- for ((index, api) in currentValidApis.map { it.name }.withIndex()) {
- listView?.setItemChecked(index, currentSelectedApis.contains(api))
- }
-
- //arrayAdapter.notifyDataSetChanged()
- arrayAdapter.addAll(names)
- arrayAdapter.notifyDataSetChanged()
- }
-
- for ((button, validTypes) in pairList) {
- val isValid =
- validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } }
- button?.isVisible = isValid
- if (isValid) {
- fun buttonContains(): Boolean {
- return selectedSearchTypes.any { validTypes.contains(it) }
- }
-
- button?.isSelected = buttonContains()
- button?.setOnClickListener {
- selectedSearchTypes.clear()
- selectedSearchTypes.addAll(validTypes)
- for ((otherButton, _) in pairList) {
- otherButton?.isSelected = false
- }
- button.isSelected = true
- updateList()
- }
-
- button?.setOnLongClickListener {
- if (!buttonContains()) {
- button.isSelected = true
- selectedSearchTypes.addAll(validTypes)
- } else {
- button.isSelected = false
- selectedSearchTypes.removeAll(validTypes)
- }
- updateList()
- return@setOnLongClickListener true
- }
- }
- }
- updateList()
+ updateList(selectedSearchTypes.toList())
}
}
}
@@ -380,14 +299,6 @@ class SearchFragment : Fragment() {
?.toMutableList()
?: mutableListOf(TvType.Movie, TvType.TvSeries)
- val pairList = getPairList()
- fun updateSelectedList(list: MutableList) {
- selectedSearchTypes = list
- for ((button, validTypes) in pairList) {
- button?.isSelected = selectedSearchTypes.any { validTypes.contains(it) }
- }
- }
-
if (isTrueTvSettings()) {
search_filter.isFocusable = true
search_filter.isFocusableInTouchMode = true
@@ -500,7 +411,7 @@ class SearchFragment : Fragment() {
SEARCH_HISTORY_OPEN -> {
searchViewModel.clearSearch()
if (searchItem.type.isNotEmpty())
- updateSelectedList(searchItem.type.toMutableList())
+ updateChips(home_select_group, searchItem.type.toMutableList())
main_search?.setQuery(searchItem.searchText, true)
}
SEARCH_HISTORY_REMOVE -> {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt
index aa302c5a..bacd26c8 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt
@@ -9,8 +9,9 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.mvvm.observe
-import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.getPairList
+import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.ui.settings.appLanguages
@@ -18,6 +19,8 @@ import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import kotlinx.android.synthetic.main.fragment_plugins.*
+import kotlinx.android.synthetic.main.tvtypes_chips.*
+import kotlinx.android.synthetic.main.tvtypes_chips_scroll.*
const val PLUGINS_BUNDLE_NAME = "name"
const val PLUGINS_BUNDLE_URL = "url"
@@ -145,56 +148,10 @@ class PluginsFragment : Fragment() {
pluginViewModel.updatePluginList(context, url)
tv_types_scroll_view?.isVisible = true
- // 💀💀💀💀💀💀💀 Recyclerview when
- val pairList = getPairList(
- home_select_anime,
- home_select_cartoons,
- home_select_tv_series,
- home_select_documentaries,
- home_select_movies,
- home_select_asian,
- home_select_livestreams,
- home_select_nsfw,
- home_select_others
- )
-
-// val supportedTypes: Array =
-// pluginViewModel.filteredPlugins.value!!.second.flatMap { it -> it.plugin.second.tvTypes ?: listOf("Other") }.distinct().toTypedArray()
-
- // Copy pasted code
- for ((button, validTypes) in pairList) {
- val validTypesMapped = validTypes.map { it.name }
- val isValid = true
- //validTypes.any { it -> supportedTypes.contains(it.name) }
- button?.isVisible = isValid
- if (isValid) {
- fun buttonContains(): Boolean {
- return pluginViewModel.tvTypes.any { validTypesMapped.contains(it) }
- }
-
- button?.isSelected = buttonContains()
- button?.setOnClickListener {
- pluginViewModel.tvTypes.clear()
- pluginViewModel.tvTypes.addAll(validTypesMapped)
- for ((otherButton, _) in pairList) {
- otherButton?.isSelected = false
- }
- button.isSelected = true
- pluginViewModel.updateFilteredPlugins()
- }
-
- button?.setOnLongClickListener {
- if (!buttonContains()) {
- button.isSelected = true
- pluginViewModel.tvTypes.addAll(validTypesMapped)
- } else {
- button.isSelected = false
- pluginViewModel.tvTypes.removeAll(validTypesMapped)
- }
- pluginViewModel.updateFilteredPlugins()
- return@setOnLongClickListener true
- }
- }
+ bindChips(home_select_group, emptyList(), TvType.values().toList()) { list ->
+ pluginViewModel.tvTypes.clear()
+ pluginViewModel.tvTypes.addAll(list.map { it.name })
+ pluginViewModel.updateFilteredPlugins()
}
}
}
diff --git a/app/src/main/res/color/chip_color.xml b/app/src/main/res/color/chip_color.xml
new file mode 100644
index 00000000..f4190551
--- /dev/null
+++ b/app/src/main/res/color/chip_color.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index f8ca8cbd..4905d454 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -1,79 +1,83 @@
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/homeRoot"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|keyboard|navigation"
+ android:paddingTop="0dp">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ app:itemTextColor="@color/item_select_color"
+ app:labelVisibilityMode="unlabeled"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:menu="@menu/bottom_nav_menu"
+ app:menuGravity="center">
+
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:menu="@menu/bottom_nav_menu" />
+ android:id="@+id/nav_host_fragment"
+ android:name="androidx.navigation.fragment.NavHostFragment"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:defaultNavHost="true"
+ app:layout_constraintBottom_toTopOf="@+id/cast_mini_controller_holder"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/nav_rail_view"
+ app:layout_constraintTop_toTopOf="parent"
+ app:navGraph="@navigation/mobile_navigation" />
+ android:id="@+id/cast_mini_controller_holder"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toTopOf="@+id/nav_view"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/nav_rail_view"
+ tools:layout_height="100dp">
+ android:id="@+id/cast_mini_controller"
+ class="com.lagradost.cloudstream3.ui.MyMiniControllerFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+
+ app:castControlButtons="@array/cast_mini_controller_control_buttons"
+ app:customCastBackgroundColor="?attr/primaryGrayBackground"
+ tools:ignore="FragmentTagUsage" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml
index 4abdef0b..9d430e3d 100644
--- a/app/src/main/res/layout/fragment_home.xml
+++ b/app/src/main/res/layout/fragment_home.xml
@@ -1,87 +1,87 @@
+ android:layout_height="match_parent"
+ tools:context=".ui.home.HomeFragment">
+ android:id="@+id/home_loading"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ tools:visibility="gone">
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:layout_gravity="center"
+ android:visibility="gone"
+ tools:visibility="gone" />
+ android:id="@+id/home_loading_shimmer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:layout_marginTop="15dp"
+ android:orientation="vertical"
+ android:paddingTop="40dp"
+ app:shimmer_auto_start="true"
+ app:shimmer_base_alpha="0.2"
+ app:shimmer_duration="@integer/loading_time"
+ app:shimmer_highlight_alpha="0.3">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ android:layout_width="125dp"
+ android:layout_height="200dp"
+ android:layout_gravity="center"
+ android:layout_margin="@dimen/loading_margin"
+ android:background="@color/grayShimmer"
+ android:translationX="-164dp"
+ app:cardCornerRadius="@dimen/loading_radius" />
+ android:layout_width="148dp"
+ android:layout_height="234dp"
+ android:layout_gravity="center"
+ android:layout_margin="@dimen/loading_margin"
+ android:background="@color/grayShimmer"
+ app:cardCornerRadius="@dimen/loading_radius" />
+ android:layout_width="125dp"
+ android:layout_height="200dp"
+ android:layout_gravity="center"
+ android:layout_margin="@dimen/loading_margin"
+ android:background="@color/grayShimmer"
+ android:translationX="164dp"
+ app:cardCornerRadius="@dimen/loading_radius" />
+ android:layout_marginEnd="@dimen/result_padding"
+ android:orientation="vertical">
@@ -93,168 +93,170 @@
+ android:id="@+id/home_loading_statusbar"
+ android:layout_width="match_parent"
+ android:layout_height="70dp">
+ android:layout_margin="10dp"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/home_change_provider_img_des"
+ android:src="@drawable/ic_baseline_keyboard_arrow_down_24" />
+
+
+ android:layout_margin="5dp"
+ android:minWidth="200dp"
+ android:text="@string/reload_error"
+ app:icon="@drawable/ic_baseline_autorenew_24" />
-
-
+ android:layout_gravity="center"
+ android:layout_margin="5dp"
+ android:minWidth="200dp"
+ android:text="@string/result_open_in_browser"
+ app:icon="@drawable/ic_baseline_public_24" />
+ android:id="@+id/result_error_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_margin="5dp"
+ android:gravity="center"
+ android:textColor="?attr/textColor" />
+ android:id="@+id/home_loaded"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?attr/primaryBlackBackground"
+ android:visibility="gone"
+ tools:visibility="visible">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ android:id="@+id/home_statusbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/primaryGrayBackground" />
+ android:id="@+id/home_settings_bar"
+
+ android:layout_width="match_parent"
+ android:layout_height="70dp"
+ android:background="?attr/primaryGrayBackground"
+ android:visibility="gone">
+ android:paddingTop="10dp"
+ android:paddingEnd="10dp"
+ android:paddingBottom="10dp">
+ android:id="@+id/home_profile_picture_holder"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="10dp"
+ app:cardCornerRadius="100dp">
+ android:id="@+id/home_profile_picture"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop"
+ tools:ignore="ContentDescription" />
+ android:layout_width="match_parent"
+ android:layout_height="40dp"
+ android:layout_gravity="center_vertical"
+ android:background="@drawable/search_background"
+ android:visibility="visible">
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:iconifiedByDefault="false"
+ android:paddingStart="-10dp"
+ app:iconifiedByDefault="false"
+ app:queryBackground="@color/transparent"
+ app:queryHint="@string/search_hint"
+ app:searchIcon="@drawable/search_icon"
+ tools:ignore="RtlSymmetry" />
+
+
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center_vertical"
+ android:textColor="?attr/textColor"
+ android:textSize="20sp"
+
+ tools:text="Hello World" />
-
-
+ android:id="@+id/home_provider_meta_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center_vertical"
+ android:textColor="?attr/grayTextColor"
+ android:textSize="14sp"
+ tools:text="Hello World" />
@@ -278,273 +280,309 @@
-->
-
+
+
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop"
+ tools:src="@drawable/example_poster" />
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_height="300dp"
+ android:layout_gravity="bottom"
+ android:background="@drawable/background_shadow" />
+
+
+
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:iconifiedByDefault="false"
+ android:paddingStart="-10dp"
+ app:iconifiedByDefault="false"
+
+ app:queryBackground="@color/transparent"
+ app:queryHint="@string/search_hint"
+
+ app:searchIcon="@drawable/search_icon"
+ tools:ignore="RtlSymmetry" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:id="@+id/home_watch_parent_item_title"
+ style="@style/WatchHeaderText"
+ android:layout_gravity="center_vertical"
+ android:text="@string/continue_watching" />
+ android:layout_width="30dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="end|center_vertical"
+ android:layout_marginEnd="5dp"
+ android:contentDescription="@string/home_more_info"
+ android:src="@drawable/ic_baseline_arrow_forward_24"
+ app:tint="?attr/textColor" />
+ android:id="@+id/home_watch_child_recyclerview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false"
+ android:descendantFocusability="afterDescendants"
+ android:orientation="horizontal"
+ android:paddingHorizontal="5dp"
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+ tools:listitem="@layout/home_result_grid" />
+ android:id="@+id/home_bookmarked_holder"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone"
+ tools:visibility="visible">
+ android:foreground="?android:attr/selectableItemBackgroundBorderless"
+ android:nextFocusLeft="@id/nav_rail_view"
+ android:nextFocusUp="@id/home_watch_child_recyclerview"
+ android:nextFocusForward="@id/home_bookmarked_child_recyclerview"
+ android:paddingStart="12dp"
+ android:paddingTop="5dp"
+ android:paddingEnd="12dp"
+ android:paddingBottom="5dp">
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="50dp"
+ android:fadingEdge="horizontal"
+ android:requiresFadingEdge="horizontal">
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ android:nextFocusLeft="@id/nav_rail_view"
+ android:nextFocusRight="@id/home_plan_to_watch_btt"
+ android:text="@string/type_watching" />
+ android:nextFocusLeft="@id/home_type_watching_btt"
+ android:nextFocusRight="@id/home_type_on_hold_btt"
+ android:text="@string/type_plan_to_watch" />
+ android:nextFocusLeft="@id/home_plan_to_watch_btt"
+ android:nextFocusRight="@id/home_type_dropped_btt"
+ android:text="@string/type_on_hold" />
+ android:nextFocusLeft="@id/home_type_on_hold_btt"
+ android:nextFocusRight="@id/home_type_completed_btt"
+ android:text="@string/type_dropped" />
+ style="@style/RoundedSelectableButton"
+ android:nextFocusLeft="@id/home_type_dropped_btt"
+ android:text="@string/type_completed" />
+ android:layout_width="30dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="end|center_vertical"
+ android:layout_marginEnd="5dp"
+ android:contentDescription="@string/home_more_info"
+ android:src="@drawable/ic_baseline_arrow_forward_24"
+ app:tint="?attr/textColor" />
+ android:clipToPadding="false"
+ android:descendantFocusability="afterDescendants"
+ android:orientation="horizontal"
+ android:paddingHorizontal="5dp"
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+ tools:listitem="@layout/home_result_grid" />
+ android:id="@+id/home_master_recycler"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:descendantFocusability="afterDescendants"
+ android:nextFocusLeft="@id/nav_rail_view"
+ tools:listitem="@layout/homepage_parent" />
+ android:id="@+id/home_api_fab"
+ style="@style/ExtendedFloatingActionButton"
+ android:text="@string/home_source"
+ android:textColor="?attr/textColor"
+ android:visibility="gone"
+ app:icon="@drawable/ic_baseline_filter_list_24"
+ tools:ignore="ContentDescription"
+ tools:visibility="visible" />
+ android:id="@+id/home_random"
+ style="@style/ExtendedFloatingActionButton"
+ android:layout_gravity="bottom|start"
+ android:text="@string/home_random"
+ android:textColor="?attr/textColor"
+ android:visibility="gone"
+ app:icon="@drawable/ic_baseline_play_arrow_24"
+ tools:ignore="ContentDescription"
+ tools:visibility="visible" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_plugins.xml b/app/src/main/res/layout/fragment_plugins.xml
index 15e0d2f9..40a0299c 100644
--- a/app/src/main/res/layout/fragment_plugins.xml
+++ b/app/src/main/res/layout/fragment_plugins.xml
@@ -25,108 +25,7 @@
app:titleTextColor="?attr/textColor"
tools:title="Overlord" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_width="match_parent"
+ android:layout_height="60dp">
+
+
-
+
-
+
+
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/tvtypes_chips.xml b/app/src/main/res/layout/tvtypes_chips.xml
new file mode 100644
index 00000000..33ca7762
--- /dev/null
+++ b/app/src/main/res/layout/tvtypes_chips.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/tvtypes_chips_scroll.xml b/app/src/main/res/layout/tvtypes_chips_scroll.xml
new file mode 100644
index 00000000..45b27dbc
--- /dev/null
+++ b/app/src/main/res/layout/tvtypes_chips_scroll.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 9840cb80..9e235bf3 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -75,6 +75,10 @@
- @color/amoledModeLight
+
+