diff --git a/README.md b/README.md
index e74a8121..6a8917f5 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,12 @@
# CloudStream-3
+![GitHub release](https://img.shields.io/github/v/release/LagradOst/cloudstream-3?sort=semver&style=for-the-badge)
+[![Discord](https://img.shields.io/discord/737724143126052974?style=for-the-badge)](https://discord.gg/5Hus6fM)
+
+
**DOWNLOAD:**
https://github.com/LagradOst/CloudStream-3/releases
-**Discord:**
-https://discord.gg/5Hus6fM
***Features:***
+ **AdFree**, No ads whatsoever
@@ -27,6 +29,7 @@ https://discord.gg/5Hus6fM
* 🇪🇸 Spanish
* 🇳🇴 Norsk
* 🇩🇪 German
+* 🇹🇷 Turkish
***SCREENSHOTS:***
@@ -49,6 +52,7 @@ It merely scrapes 3rd-party websites that are publicly accessable via any regula
- [gogoanime.vc](https://gogoanime.vc)
+- [allanime.site](https://allanime.site)
- [animeflick.net](https://animeflick.net)
- [kawaiifu.com](https://kawaiifu.com)
- [tenshi.moe](https://tenshi.moe)
diff --git a/app/build.gradle b/app/build.gradle
index 27773e78..a8cb5578 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -34,8 +34,9 @@ android {
applicationId "com.lagradost.cloudstream3"
minSdkVersion 21
targetSdkVersion 31
- versionCode 31
- versionName "2.1.2"
+
+ versionCode 33
+ versionName "2.2.2"
resValue "string", "app_version",
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7f8694ec..722a8329 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -10,10 +10,16 @@
+
+
+
+
@@ -63,7 +70,7 @@
android:enabled="false"
android:exported="true">
-
+
): List {
}
/** https://www.imdb.com/title/tt2861424/ -> tt2861424 */
-fun imdbUrlToId(url: String): String {
- return url
- .removePrefix("https://www.imdb.com/title/")
- .removePrefix("https://imdb.com/title/tt2861424/")
- .replace("/", "")
+fun imdbUrlToId(url: String): String? {
+ return Regex("/title/(tt[0-9]*)").find(url)?.groupValues?.get(1)
+ ?: Regex("tt[0-9]{5,}").find(url)?.groupValues?.get(0)
}
fun imdbUrlToIdNullable(url: String?): String? {
@@ -392,12 +391,12 @@ fun LoadResponse?.isAnimeBased(): Boolean {
data class AnimeEpisode(
val url: String,
- val name: String? = null,
- val posterUrl: String? = null,
- val date: String? = null,
- val rating: Int? = null,
- val descript: String? = null,
- val episode: Int? = null,
+ var name: String? = null,
+ var posterUrl: String? = null,
+ var date: String? = null,
+ var rating: Int? = null,
+ var description: String? = null,
+ var episode: Int? = null,
)
data class TorrentLoadResponse(
@@ -418,32 +417,47 @@ data class TorrentLoadResponse(
) : LoadResponse
data class AnimeLoadResponse(
- val engName: String?,
- val japName: String?,
+ var engName: String? = null,
+ var japName: String? = null,
override val name: String,
override val url: String,
override val apiName: String,
override val type: TvType,
- override val posterUrl: String?,
- override val year: Int?,
+ override var posterUrl: String? = null,
+ override var year: Int? = null,
- val dubEpisodes: List?,
- val subEpisodes: List?,
- val showStatus: ShowStatus?,
+ var episodes: HashMap> = hashMapOf(),
+ var showStatus: ShowStatus? = null,
- override val plot: String?,
- override val tags: List? = null,
- val synonyms: List? = null,
+ override var plot: String? = null,
+ override var tags: List? = null,
+ var synonyms: List? = null,
- val malId: Int? = null,
- val anilistId: Int? = null,
- override val rating: Int? = null,
- override val duration: String? = null,
- override val trailerUrl: String? = null,
- override val recommendations: List? = null,
+ var malId: Int? = null,
+ var anilistId: Int? = null,
+ override var rating: Int? = null,
+ override var duration: String? = null,
+ override var trailerUrl: String? = null,
+ override var recommendations: List? = null,
) : LoadResponse
+fun AnimeLoadResponse.addEpisodes(status : DubStatus, episodes : List?) {
+ if(episodes == null) return
+ this.episodes[status] = episodes
+}
+
+fun MainAPI.newAnimeLoadResponse(
+ name: String,
+ url: String,
+ type: TvType,
+ initializer: AnimeLoadResponse.() -> Unit = { }
+): AnimeLoadResponse {
+ val builder = AnimeLoadResponse(name = name, url = url, apiName = this.name, type = type)
+ builder.initializer()
+ return builder
+}
+
data class MovieLoadResponse(
override val name: String,
override val url: String,
@@ -451,11 +465,11 @@ data class MovieLoadResponse(
override val type: TvType,
val dataUrl: String,
- override val posterUrl: String?,
- override val year: Int?,
- override val plot: String?,
+ override val posterUrl: String? = null,
+ override val year: Int? = null,
+ override val plot: String? = null,
- val imdbId: String?,
+ val imdbId: String? = null,
override val rating: Int? = null,
override val tags: List? = null,
override val duration: String? = null,
@@ -464,9 +478,9 @@ data class MovieLoadResponse(
) : LoadResponse
data class TvSeriesEpisode(
- val name: String?,
- val season: Int?,
- val episode: Int?,
+ val name: String? = null,
+ val season: Int? = null,
+ val episode: Int? = null,
val data: String,
val posterUrl: String? = null,
val date: String? = null,
@@ -481,12 +495,12 @@ data class TvSeriesLoadResponse(
override val type: TvType,
val episodes: List,
- override val posterUrl: String?,
- override val year: Int?,
- override val plot: String?,
+ override val posterUrl: String? = null,
+ override val year: Int? = null,
+ override val plot: String? = null,
- val showStatus: ShowStatus?,
- val imdbId: String?,
+ val showStatus: ShowStatus? = null,
+ val imdbId: String? = null,
override val rating: Int? = null,
override val tags: List? = null,
override val duration: String? = null,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index 0c9a956d..d7a8c6a8 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -31,6 +31,7 @@ import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
import com.lagradost.cloudstream3.ui.player.PlayerEventType
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.DataStore.getKey
@@ -160,16 +161,16 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_A, KeyEvent.KEYCODE_MEDIA_SKIP_BACKWARD, KeyEvent.KEYCODE_MEDIA_REWIND -> {
PlayerEventType.SeekBack
}
- KeyEvent.KEYCODE_MEDIA_NEXT -> {
+ KeyEvent.KEYCODE_MEDIA_NEXT, KeyEvent.KEYCODE_BUTTON_R1 -> {
PlayerEventType.NextEpisode
}
- KeyEvent.KEYCODE_MEDIA_PREVIOUS -> {
+ KeyEvent.KEYCODE_MEDIA_PREVIOUS, KeyEvent.KEYCODE_BUTTON_L1 -> {
PlayerEventType.PrevEpisode
}
KeyEvent.KEYCODE_MEDIA_PAUSE -> {
PlayerEventType.Pause
}
- KeyEvent.KEYCODE_MEDIA_PLAY -> {
+ KeyEvent.KEYCODE_MEDIA_PLAY, KeyEvent.KEYCODE_BUTTON_START -> {
PlayerEventType.Play
}
KeyEvent.KEYCODE_L, KeyEvent.KEYCODE_NUMPAD_7 -> {
@@ -184,13 +185,13 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
KeyEvent.KEYCODE_S, KeyEvent.KEYCODE_NUMPAD_9 -> {
PlayerEventType.ShowMirrors
}
- KeyEvent.KEYCODE_E, KeyEvent.KEYCODE_NUMPAD_3 -> {
+ KeyEvent.KEYCODE_E, KeyEvent.KEYCODE_NUMPAD_3 -> {
PlayerEventType.ShowSpeed
}
KeyEvent.KEYCODE_R, KeyEvent.KEYCODE_NUMPAD_0 -> {
PlayerEventType.Resize
}
- KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_P, KeyEvent.KEYCODE_SPACE -> { // space is not captured due to navigation
+ KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_P, KeyEvent.KEYCODE_SPACE, KeyEvent.KEYCODE_NUMPAD_ENTER, KeyEvent.KEYCODE_ENTER -> { // space is not captured due to navigation
PlayerEventType.PlayPauseToggle
}
else -> null
@@ -198,6 +199,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
playerEventListener?.invoke(playerEvent)
}
+ //when (keyCode) {
+ // KeyEvent.KEYCODE_DPAD_CENTER -> {
+ // println("DPAD PRESSED ${this.isKeyboardOpen()}")
+ // }
+ //}
+
return super.onKeyDown(keyCode, event)
}
@@ -354,10 +361,35 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
}
override fun onCreate(savedInstanceState: Bundle?) {
+ val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
+
+ val currentTheme = when (settingsManager.getString(getString(R.string.app_theme_key), "Black")) {
+ "Black" -> R.style.AppTheme
+ "Light" -> R.style.LightMode
+ "Amoled" -> R.style.AmoledMode
+ else -> R.style.AppTheme
+ }
+
+ val currentOverlayTheme = when (settingsManager.getString(getString(R.string.primary_color_key), "Normal")) {
+ "Normal" -> R.style.OverlayPrimaryColorNormal
+ "Blue" -> R.style.OverlayPrimaryColorBlue
+ "Purple" -> R.style.OverlayPrimaryColorPurple
+ "Green" -> R.style.OverlayPrimaryColorGreen
+ "GreenApple" -> R.style.OverlayPrimaryColorGreenApple
+ "Red" -> R.style.OverlayPrimaryColorRed
+ "Banana" -> R.style.OverlayPrimaryColorBanana
+ "Party" -> R.style.OverlayPrimaryColorParty
+ else -> R.style.OverlayPrimaryColorNormal
+ }
+
+ theme.applyStyle(currentTheme, true)
+ theme.applyStyle(currentOverlayTheme, true)
+
theme.applyStyle(
R.style.LoadedStyle,
true
) // THEME IS SET BEFORE VIEW IS CREATED TO APPLY THE THEME TO THE MAIN VIEW
+
updateLocale()
initRequestClient()
super.onCreate(savedInstanceState)
@@ -371,7 +403,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
- setContentView(R.layout.activity_main)
+ if (isTvSettings()) {
+ setContentView(R.layout.activity_main_tv)
+ } else {
+ setContentView(R.layout.activity_main)
+ }
+
// val navView: BottomNavigationView = findViewById(R.id.nav_view)
//https://stackoverflow.com/questions/52594181/how-to-know-if-user-has-disabled-picture-in-picture-feature-permission
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AllAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AllAnimeProvider.kt
new file mode 100644
index 00000000..19cbe536
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AllAnimeProvider.kt
@@ -0,0 +1,320 @@
+package com.lagradost.cloudstream3.animeproviders
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.fasterxml.jackson.module.kotlin.readValue
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.network.get
+import com.lagradost.cloudstream3.network.text
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.M3u8Helper
+import com.lagradost.cloudstream3.utils.getQualityFromName
+import org.jsoup.Jsoup
+import org.mozilla.javascript.Context
+import org.mozilla.javascript.Scriptable
+import java.net.URI
+import java.net.URLDecoder
+import java.util.*
+
+
+class AllAnimeProvider : MainAPI() {
+ override val mainUrl: String
+ get() = "https://allanime.site"
+ override val name: String
+ get() = "AllAnime"
+ override val hasQuickSearch: Boolean
+ get() = false
+ override val hasMainPage: Boolean
+ get() = false
+
+ private val hlsHelper = M3u8Helper()
+
+ private fun getStatus(t: String): ShowStatus {
+ return when (t) {
+ "Finished" -> ShowStatus.Completed
+ "Releasing" -> ShowStatus.Ongoing
+ else -> ShowStatus.Completed
+ }
+ }
+
+ override val supportedTypes: Set
+ get() = setOf(TvType.Anime, TvType.AnimeMovie)
+
+ private data class Data(
+ @JsonProperty("shows") val shows: Shows
+ )
+
+ private data class Shows(
+ @JsonProperty("pageInfo") val pageInfo: PageInfo,
+ @JsonProperty("edges") val edges: List,
+ @JsonProperty("__typename") val _typename: String
+ )
+
+ private data class Edges(
+ @JsonProperty("_id") val Id: String?,
+ @JsonProperty("name") val name: String,
+ @JsonProperty("englishName") val englishName: String?,
+ @JsonProperty("nativeName") val nativeName: String?,
+ @JsonProperty("thumbnail") val thumbnail: String?,
+ @JsonProperty("type") val type: String?,
+ @JsonProperty("season") val season: Season?,
+ @JsonProperty("score") val score: Double?,
+ @JsonProperty("airedStart") val airedStart: AiredStart?,
+ @JsonProperty("availableEpisodes") val availableEpisodes: AvailableEpisodes?,
+ @JsonProperty("availableEpisodesDetail") val availableEpisodesDetail: AvailableEpisodesDetail?,
+ @JsonProperty("studios") val studios: List?,
+ @JsonProperty("description") val description: String?,
+ @JsonProperty("status") val status: String?,
+ )
+
+ private data class AvailableEpisodes(
+ @JsonProperty("sub") val sub: Int,
+ @JsonProperty("dub") val dub: Int,
+ @JsonProperty("raw") val raw: Int
+ )
+
+ private data class AiredStart(
+ @JsonProperty("year") val year: Int,
+ @JsonProperty("month") val month: Int,
+ @JsonProperty("date") val date: Int
+ )
+
+ private data class Season(
+ @JsonProperty("quarter") val quarter: String,
+ @JsonProperty("year") val year: Int
+ )
+
+ private data class PageInfo(
+ @JsonProperty("total") val total: Int,
+ @JsonProperty("__typename") val _typename: String
+ )
+
+ private data class AllAnimeQuery(
+ @JsonProperty("data") val data: Data
+ )
+
+ override fun search(query: String): ArrayList {
+ val link =
+ """$mainUrl/graphql?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Afalse%2C%22query%22%3A%22$query%22%7D%2C%22limit%22%3A26%2C%22page%22%3A1%2C%22translationType%22%3A%22sub%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229343797cc3d9e3f444e2d3b7db9a84d759b816a4d84512ea72d079f85bb96e98%22%7D%7D"""
+ var res = get(link).text
+ if (res.contains("PERSISTED_QUERY_NOT_FOUND")) {
+ res = get(link).text
+ if (res.contains("PERSISTED_QUERY_NOT_FOUND")) return ArrayList()
+ }
+ val response = mapper.readValue(res)
+
+ val results = response.data.shows.edges.filter {
+ // filtering in case there is an anime with 0 episodes available on the site.
+ !(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
+ }
+
+ return ArrayList(results.map {
+ AnimeSearchResponse(
+ it.name,
+ "$mainUrl/anime/${it.Id}",
+ this.name,
+ TvType.Anime,
+ it.thumbnail,
+ it.airedStart?.year,
+ EnumSet.of(DubStatus.Subbed, DubStatus.Dubbed),
+ it.englishName,
+ it.availableEpisodes?.dub,
+ it.availableEpisodes?.sub
+ )
+ })
+ }
+
+ private data class AvailableEpisodesDetail(
+ @JsonProperty("sub") val sub: List,
+ @JsonProperty("dub") val dub: List,
+ @JsonProperty("raw") val raw: List
+ )
+
+
+ override fun load(url: String): LoadResponse? {
+ val rhino = Context.enter()
+ rhino.initStandardObjects()
+ rhino.optimizationLevel = -1
+ val scope: Scriptable = rhino.initStandardObjects()
+
+ val html = get(url).text
+ val soup = Jsoup.parse(html)
+
+ val script = soup.select("script").firstOrNull {
+ it.html().contains("window.__NUXT__")
+ } ?: return null
+
+ val js = """
+ const window = {}
+ ${script.html()}
+ const returnValue = JSON.stringify(window.__NUXT__.fetch[0].show)
+ """.trimIndent()
+
+ rhino.evaluateString(scope, js, "JavaScript", 1, null)
+ val jsEval = scope.get("returnValue", scope) ?: return null
+ val showData = mapper.readValue(jsEval as String)
+
+ val title = showData.name
+ val description = showData.description
+ val poster = showData.thumbnail
+
+ val episodes = showData.availableEpisodes.let {
+ if (it == null) return@let Pair(null, null)
+ Pair(if (it.sub != 0) ArrayList((1..it.sub).map { epNum ->
+ AnimeEpisode(
+ "$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum
+ )
+ }) else null, if (it.dub != 0) ArrayList((1..it.dub).map { epNum ->
+ AnimeEpisode(
+ "$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum
+ )
+ }) else null)
+ }
+
+ return newAnimeLoadResponse(title, url, TvType.Anime) {
+ posterUrl = poster
+ year = showData.airedStart?.year
+
+ addEpisodes(DubStatus.Subbed, episodes.first)
+ addEpisodes(DubStatus.Dubbed, episodes.second)
+
+ showStatus = getStatus(showData.status.toString())
+
+ plot = description?.replace(Regex("""<(.*?)>"""), "")
+ }
+ }
+
+ private val embedBlackList = listOf(
+ "https://mp4upload.com/",
+ "https://streamsb.net/",
+ "https://dood.to/",
+ "https://videobin.co/",
+ "https://ok.ru",
+ "https://streamlare.com",
+ )
+
+ private fun embedIsBlacklisted(url: String): Boolean {
+ embedBlackList.forEach {
+ if (it.javaClass.name == "kotlin.text.Regex") {
+ if ((it as Regex).matches(url)) {
+ return true
+ }
+ } else {
+ if (url.contains(it)) {
+ return true
+ }
+ }
+ }
+ return false
+ }
+
+ private fun String.sanitize(): String {
+ var out = this
+ listOf(Pair("\\u002F", "/")).forEach {
+ out = out.replace(it.first, it.second)
+ }
+ return out
+ }
+
+ private data class Links(
+ @JsonProperty("link") val link: String,
+ @JsonProperty("hls") val hls: Boolean?,
+ @JsonProperty("resolutionStr") val resolutionStr: String,
+ @JsonProperty("src") val src: String?
+ )
+
+ private data class AllAnimeVideoApiResponse(
+ @JsonProperty("links") val links: List
+ )
+
+ private data class ApiEndPoint(
+ @JsonProperty("episodeIframeHead") val episodeIframeHead: String
+ )
+
+ private fun getM3u8Qualities(m3u8Link: String, referer: String, qualityName: String): ArrayList {
+ return ArrayList(hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(m3u8Link, null), true).map { stream ->
+ val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p"
+ ExtractorLink(
+ this.name,
+ "${this.name} - $qualityName $qualityString",
+ stream.streamUrl,
+ referer,
+ getQualityFromName(stream.quality.toString()),
+ true,
+ stream.headers
+ )
+ })
+ }
+
+ override fun loadLinks(
+ data: String,
+ isCasting: Boolean,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ): Boolean {
+ var apiEndPoint = mapper.readValue(get("$mainUrl/getVersion").text).episodeIframeHead
+ if (apiEndPoint.endsWith("/")) apiEndPoint = apiEndPoint.slice(0 until apiEndPoint.length - 1)
+
+ val html = get(data).text
+
+ val sources = Regex("""sourceUrl[:=]"(.+?)"""").findAll(html).toList()
+ .map { URLDecoder.decode(it.destructured.component1().sanitize(), "UTF-8") }
+ sources.forEach {
+ var link = it
+ if (URI(link).isAbsolute || link.startsWith("//")) {
+ if (link.startsWith("//")) link = "https:$it"
+
+ if (Regex("""streaming\.php\?""").matches(link)) {
+ // for now ignore
+ } else if (!embedIsBlacklisted(link)) {
+ if (URI(link).path.contains(".m3u")) {
+ getM3u8Qualities(link, data, URI(link).host).forEach(callback)
+ } else {
+ callback(
+ ExtractorLink(
+ "AllAnime - " + URI(link).host,
+ "",
+ link,
+ data,
+ getQualityFromName("1080"),
+ false
+ )
+ )
+ }
+ }
+ } else {
+ link = apiEndPoint + URI(link).path + ".json?" + URI(link).query
+ val response = get(link)
+
+ if (response.code < 400) {
+ val links = mapper.readValue(response.text).links
+ links.forEach { server ->
+ if (server.hls != null && server.hls) {
+ getM3u8Qualities(
+ server.link,
+ "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(
+ server.link
+ ).path),
+ server.resolutionStr
+ ).forEach(callback)
+ } else {
+ callback(
+ ExtractorLink(
+ "AllAnime - " + URI(server.link).host,
+ server.resolutionStr,
+ server.link,
+ "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(
+ server.link
+ ).path),
+ getQualityFromName("1080"),
+ false
+ )
+ )
+ }
+ }
+ }
+ }
+ }
+ return true
+ }
+
+}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeFlickProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeFlickProvider.kt
index e18dc610..eabecc4e 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeFlickProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimeFlickProvider.kt
@@ -76,21 +76,15 @@ class AnimeFlickProvider : MainAPI() {
AnimeEpisode(link, name)
}.reversed()
- return AnimeLoadResponse(
- title,
- null,
- title,
- url,
- this.name,
- getType(title),
- poster,
- year,
- null,
- episodes,
- null,
- description,
- genres
- )
+ return newAnimeLoadResponse(title, url, getType(title)) {
+ posterUrl = poster
+ this.year = year
+
+ addEpisodes(DubStatus.Subbed, episodes)
+
+ plot = description
+ tags = genres
+ }
}
override fun loadLinks(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimePaheProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimePaheProvider.kt
index b056bbbd..13187b8b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimePaheProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/AnimePaheProvider.kt
@@ -293,30 +293,26 @@ class AnimePaheProvider : MainAPI() {
}
}
- AnimeLoadResponse(
- animeTitle,
- japTitle,
- animeTitle,
- url,
- this.name,
- getType(tvType.toString()),
- poster,
- year,
- null,
- episodes,
- status,
- synopsis,
- if (!doc.select(".anime-genre > ul a").isEmpty()) {
+ newAnimeLoadResponse(animeTitle, url, getType(tvType.toString())) {
+ engName = animeTitle
+ japName = japTitle
+
+ this.posterUrl = poster
+ this.year = year
+
+ addEpisodes(DubStatus.Subbed, episodes)
+ this.showStatus = status
+ plot = synopsis
+ tags = if (!doc.select(".anime-genre > ul a").isEmpty()) {
ArrayList(doc.select(".anime-genre > ul a").map { it.text().toString() })
} else {
null
- },
- ArrayList(),
- malId,
- anilistId,
- null,
- trailer
- )
+ }
+
+ this.malId = malId
+ this.anilistId = anilistId
+ this.trailerUrl = trailer
+ }
}
}
@@ -325,7 +321,6 @@ class AnimePaheProvider : MainAPI() {
return s?.toIntOrNull() != null
}
-
private fun cookieStrToMap(cookie: String): Map {
val cookies = mutableMapOf()
for (string in cookie.split("; ")) {
@@ -347,7 +342,6 @@ class AnimePaheProvider : MainAPI() {
val slice2 = characterMap.slice(0 until s2)
var acc: Long = 0
-
for ((n, i) in content.reversed().withIndex()) {
acc += (when (isNumber("$i")) {
true -> "$i".toLong()
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DubbedAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DubbedAnimeProvider.kt
index 80f22b9a..d3a4e277 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DubbedAnimeProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/DubbedAnimeProvider.kt
@@ -109,7 +109,7 @@ class DubbedAnimeProvider : MainAPI() {
HomePageList("Trending", parseDocumentTrending(trendingUrl)),
HomePageList("Recently Added", parseDocument(recentlyAddedUrl)),
HomePageList("Recent Releases", parseDocument(lastEpisodeUrl, true)),
- // HomePageList("All", parseDocument(allUrl))
+ // HomePageList("All", parseDocument(allUrl))
)
return HomePageResponse(listItems)
@@ -268,20 +268,12 @@ class DubbedAnimeProvider : MainAPI() {
}
val img = fixUrl(document.select("div.fkimgs > img").attr("src"))
- return AnimeLoadResponse(
- null,
- null,
- title,
- url,
- this.name,
- TvType.Anime,
- img,
- year,
- ArrayList(episodes),
- null,
- null,
- descript,
- )
+ return newAnimeLoadResponse(title, url, TvType.Anime) {
+ posterUrl = img
+ this.year = year
+ addEpisodes(DubStatus.Dubbed, episodes)
+ plot = descript
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt
index 2f14b8dd..3092e949 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/GogoanimeProvider.kt
@@ -185,24 +185,18 @@ class GogoanimeProvider : MainAPI() {
"Episode " + it.selectFirst(".name").text().replace("EP", "").trim()
)
}.reversed()
- return AnimeLoadResponse(
- title,
- nativeName,
- title,
- link,
- this.name,
- getType(type.toString()),
- poster,
- year,
- null,
- episodes,
- getStatus(status.toString()),
- description,
- ArrayList(genre),
- null,
- null,
- null,
- )
+
+ return newAnimeLoadResponse(title, link, getType(type.toString())) {
+ japName = nativeName
+ engName = title
+ posterUrl = poster
+ this.year = year
+ addEpisodes(DubStatus.Subbed, episodes) // TODO CHECK
+ plot = description
+ tags = genre
+
+ showStatus = getStatus(status.toString())
+ }
}
private fun extractVideos(uri: String): List {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KawaiifuProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KawaiifuProvider.kt
index d8de465a..cde4f208 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KawaiifuProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/KawaiifuProvider.kt
@@ -109,23 +109,13 @@ class KawaiifuProvider : MainAPI() {
}
val poster = soup.selectFirst("a.thumb > img").attr("src")
-
- return AnimeLoadResponse(
- title,
- null,
- title,
- url,
- this.name,
- TvType.Anime,
- poster,
- year,
- null,
- episodes,
- ShowStatus.Ongoing,
- description,
- ArrayList(tags),
- ArrayList()
- )
+ return newAnimeLoadResponse(title, url, TvType.Anime) {
+ this.year = year
+ posterUrl = poster
+ addEpisodes(DubStatus.Subbed, episodes)
+ plot = description
+ this.tags = tags
+ }
}
override fun loadLinks(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt
index 95d10a37..2bec47c6 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/TenshiProvider.kt
@@ -268,24 +268,19 @@ class TenshiProvider : MainAPI() {
val synonyms =
document.select("li.synonym.meta-data > div.info-box > span.value").map { it?.text()?.trim().toString() }
- return AnimeLoadResponse(
- englishTitle,
- japaneseTitle,
- canonicalTitle,
- url,
- this.name,
- getType(type ?: ""),
- poster,
- year.toIntOrNull(),
- null,
- episodes,
- status,
- synopsis,
- ArrayList(genre),
- ArrayList(synonyms),
- null,
- null,
- )
+ return newAnimeLoadResponse(canonicalTitle,url,getType(type ?: "")) {
+ engName = englishTitle
+ japName = japaneseTitle
+
+ posterUrl = poster
+ this.year = year.toIntOrNull()
+
+ addEpisodes(DubStatus.Subbed,episodes)
+ showStatus = status
+ tags = genre
+ this.synonyms = synonyms
+ plot = synopsis
+ }
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WcoProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WcoProvider.kt
index fe127565..6c96c92f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WcoProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/WcoProvider.kt
@@ -208,22 +208,16 @@ class WcoProvider : MainAPI() {
val genre = document.select("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(5) > a")
.map { it?.text()?.trim().toString() }
- return AnimeLoadResponse(
- canonicalTitle,
- japaneseTitle,
- canonicalTitle,
- url,
- this.name,
- getType(type ?: ""),
- poster,
- year,
- if (isDubbed) episodes else null,
- if (!isDubbed) episodes else null,
- status,
- synopsis,
- ArrayList(genre),
- ArrayList(),
- )
+ return newAnimeLoadResponse(canonicalTitle,url,getType(type ?: "")) {
+ japName = japaneseTitle
+ engName = canonicalTitle
+ posterUrl = poster
+ this.year = year
+ addEpisodes(if(isDubbed) DubStatus.Dubbed else DubStatus.Subbed,episodes)
+ showStatus = status
+ plot = synopsis
+ tags = genre
+ }
}
override fun loadLinks(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/ZoroProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/ZoroProvider.kt
index f13d79e3..21ac1511 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/animeproviders/ZoroProvider.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/animeproviders/ZoroProvider.kt
@@ -230,21 +230,17 @@ class ZoroProvider : MainAPI() {
it.selectFirst(".ssli-order")?.text()?.toIntOrNull()
)
}
- return AnimeLoadResponse(
- title,
- japaneseTitle,
- title,
- url,
- this.name,
- TvType.Anime,
- poster,
- year,
- null,
- episodes,
- status,
- description,
- tags,
- )
+
+ return newAnimeLoadResponse(title, url, TvType.Anime) {
+ japName = japaneseTitle
+ engName = title
+ posterUrl = poster
+ this.year = year
+ addEpisodes(DubStatus.Subbed, episodes)
+ showStatus = status
+ plot = description
+ this.tags = tags
+ }
}
private fun getM3u8FromRapidCloud(url: String): String {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt
index 9c694e68..2432c855 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadButtonSetup.kt
@@ -36,11 +36,13 @@ object DownloadButtonSetup {
builder.setTitle(R.string.delete_file)
.setMessage(
- ctx.getString(R.string.delete_message).format(ctx.getNameFull(
- click.data.name,
- click.data.episode,
- click.data.season
- ))
+ ctx.getString(R.string.delete_message).format(
+ ctx.getNameFull(
+ click.data.name,
+ click.data.episode,
+ click.data.season
+ )
+ )
)
.setPositiveButton(R.string.delete, dialogClickListener)
.setNegativeButton(R.string.cancel, dialogClickListener)
@@ -54,13 +56,19 @@ object DownloadButtonSetup {
}
DOWNLOAD_ACTION_RESUME_DOWNLOAD -> {
activity?.let { ctx ->
- val pkg = VideoDownloadManager.getDownloadResumePackage(ctx, id)
- if (pkg != null) {
- VideoDownloadManager.downloadFromResumeUsingWorker(ctx, pkg)
- } else {
+ if (VideoDownloadManager.downloadStatus.containsKey(id) && VideoDownloadManager.downloadStatus[id] == VideoDownloadManager.DownloadType.IsPaused) {
VideoDownloadManager.downloadEvent.invoke(
Pair(click.data.id, VideoDownloadManager.DownloadActionType.Resume)
)
+ } else {
+ val pkg = VideoDownloadManager.getDownloadResumePackage(ctx, id)
+ if (pkg != null) {
+ VideoDownloadManager.downloadFromResumeUsingWorker(ctx, pkg)
+ } else {
+ VideoDownloadManager.downloadEvent.invoke(
+ Pair(click.data.id, VideoDownloadManager.DownloadActionType.Resume)
+ )
+ }
}
}
}
@@ -69,7 +77,7 @@ object DownloadButtonSetup {
val length =
VideoDownloadManager.getDownloadFileInfoAndUpdateSettings(act, click.data.id)?.fileLength
?: 0
- if(length > 0) {
+ if (length > 0) {
MainActivity.showToast(act, R.string.delete, Toast.LENGTH_LONG)
} else {
MainActivity.showToast(act, R.string.download, Toast.LENGTH_LONG)
@@ -86,19 +94,21 @@ object DownloadButtonSetup {
click.data.id.toString()
) ?: return
- act.navigate(R.id.global_to_navigation_player, PlayerFragment.newInstance(
- UriData(
- info.path.toString(),
- keyInfo.relativePath,
- keyInfo.displayName,
- click.data.parentId,
- click.data.id,
- headerName ?: "null",
- if (click.data.episode <= 0) null else click.data.episode,
- click.data.season
- ),
- act.getViewPos(click.data.id)?.position ?: 0
- ))
+ act.navigate(
+ R.id.global_to_navigation_player, PlayerFragment.newInstance(
+ UriData(
+ info.path.toString(),
+ keyInfo.relativePath,
+ keyInfo.displayName,
+ click.data.parentId,
+ click.data.id,
+ headerName ?: "null",
+ if (click.data.episode <= 0) null else click.data.episode,
+ click.data.season
+ ),
+ act.getViewPos(click.data.id)?.position ?: 0
+ )
+ )
}
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt
index 13cf079d..c3c6e14c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt
@@ -5,6 +5,7 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
+import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
@@ -15,8 +16,9 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.isMovieType
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
+import com.lagradost.cloudstream3.utils.AppUtils.loadResult
import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE
-import com.lagradost.cloudstream3.utils.DataStore.getFolderName
+import com.lagradost.cloudstream3.utils.DataStore
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
@@ -99,13 +101,24 @@ class DownloadFragment : Fragment() {
DownloadHeaderAdapter(
ArrayList(),
{ click ->
- if (click.data.type.isMovieType()) {
- //wont be called
- } else {
- val folder = getFolderName(DOWNLOAD_EPISODE_CACHE, click.data.id.toString())
- val navController = activity?.findNavController(R.id.nav_host_fragment)
- navController?.navigate(R.id.navigation_download_child, DownloadChildFragment.newInstance(click.data.name,folder))
+ when (click.action) {
+ 0 -> {
+ if (click.data.type.isMovieType()) {
+ //wont be called
+ } else {
+ val folder = DataStore.getFolderName(DOWNLOAD_EPISODE_CACHE, click.data.id.toString())
+ val navController = activity?.findNavController(R.id.nav_host_fragment)
+ navController?.navigate(
+ R.id.navigation_download_child,
+ DownloadChildFragment.newInstance(click.data.name, folder)
+ )
+ }
+ }
+ 1 -> {
+ (activity as AppCompatActivity?)?.loadResult(click.data.url, click.data.apiName)
+ }
}
+
},
{ downloadClickEvent ->
if (downloadClickEvent.data !is VideoDownloadHelper.DownloadEpisodeCached) return@DownloadHeaderAdapter
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadHeaderAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadHeaderAdapter.kt
index 720496d8..74e3cc6d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadHeaderAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadHeaderAdapter.kt
@@ -105,6 +105,9 @@ class DownloadHeaderAdapter(
val d = card.data
poster?.setImage(d.poster)
+ poster?.setOnClickListener {
+ clickCallback.invoke(DownloadHeaderClickEvent(1, d))
+ }
title.text = d.name
val mbString = "%.1f".format(card.totalBytes / 1000000f)
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 ac4721bf..39b2c9c7 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,7 @@ import android.widget.TextView
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
+import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSnapHelper
@@ -166,11 +167,24 @@ class HomeFragment : Fragment() {
}
private val apiChangeClickListener = View.OnClickListener { view ->
- val validAPIs = apis.filter { api -> api.hasMainPage }.toMutableList()
+ val allApis = apis.filter { api -> api.hasMainPage }.toMutableList()
+ var validAPIs = allApis
+ val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
+ val currentPrefMedia = settingsManager.getInt(getString(R.string.preferred_media_settings), 0)
+
+ // Filter API depending on preferred media type
+ if (currentPrefMedia > 0) {
+ val listEnumAnime = listOf(TvType.Anime, TvType.AnimeMovie, TvType.ONA)
+ val listEnumMovieTv = listOf(TvType.Movie, TvType.TvSeries, TvType.Cartoon)
+ val mediaTypeList = if (currentPrefMedia==1) listEnumMovieTv else listEnumAnime
+
+ validAPIs = allApis.filter { api -> api.supportedTypes.any { it in mediaTypeList } }.toMutableList()
+ }
+
validAPIs.add(0, randomApi)
validAPIs.add(0, noneApi)
view.popupMenuNoIconsAndNoStringRes(validAPIs.mapIndexed { index, api -> Pair(index, api.name) }) {
- homeViewModel.loadAndCancel(validAPIs[itemId].name)
+ homeViewModel.loadAndCancel(validAPIs[itemId].name, currentPrefMedia)
}
}
@@ -411,7 +425,7 @@ class HomeFragment : Fragment() {
val apiName = context?.getKey(HOMEPAGE_API)
if (homeViewModel.apiName.value != apiName || apiName == null) {
//println("Caught home: " + homeViewModel.apiName.value + " at " + apiName)
- homeViewModel.loadAndCancel(apiName)
+ homeViewModel.loadAndCancel(apiName, 0)
}
}
}
\ No newline at end of file
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 a446b1ca..5d573c41 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
@@ -10,6 +10,7 @@ import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.HomePageResponse
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.SearchResponse
+import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
@@ -148,12 +149,21 @@ class HomeViewModel : ViewModel() {
}
}
- fun loadAndCancel(preferredApiName: String?) = viewModelScope.launch {
+ fun loadAndCancel(preferredApiName: String?, currentPrefMedia: Int) = viewModelScope.launch {
val api = getApiFromNameNull(preferredApiName)
if (preferredApiName == noneApi.name)
loadAndCancel(noneApi)
else if(preferredApiName == randomApi.name || api == null) {
- loadAndCancel(apis.filter { it.hasMainPage }.random())
+ val allApis = apis.filter { api -> api.hasMainPage }.toMutableList()
+ var validAPIs = allApis
+ if (currentPrefMedia > 0) {
+ val listEnumAnime = listOf(TvType.Anime, TvType.AnimeMovie, TvType.ONA)
+ val listEnumMovieTv = listOf(TvType.Movie, TvType.TvSeries, TvType.Cartoon)
+ val mediaTypeList = if (currentPrefMedia==1) listEnumMovieTv else listEnumAnime
+
+ validAPIs = allApis.filter { api -> api.supportedTypes.any { it in mediaTypeList } }.toMutableList()
+ }
+ loadAndCancel(validAPIs.random())
} else {
loadAndCancel(api)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerFragment.kt
index 9e016807..89d4a228 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerFragment.kt
@@ -32,7 +32,6 @@ import android.view.animation.AnimationUtils
import android.widget.*
import android.widget.Toast.LENGTH_SHORT
import androidx.appcompat.app.AlertDialog
-import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
@@ -89,6 +88,7 @@ import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.setLastWatched
import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
+import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.UIHelper.getNavigationBarHeight
import com.lagradost.cloudstream3.utils.UIHelper.getStatusBarHeight
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
@@ -829,7 +829,7 @@ class PlayerFragment : Fragment() {
}
isLocked = !isLocked
- if(isLocked && isShowing) {
+ if (isLocked && isShowing) {
player_pause_holder?.postDelayed({
if (isLocked && isShowing) {
onClickChange()
@@ -869,11 +869,13 @@ class PlayerFragment : Fragment() {
private fun updateLock() {
video_locked_img?.setImageResource(if (isLocked) R.drawable.video_locked else R.drawable.video_unlocked)
- val color = if (isLocked) ContextCompat.getColor(requireContext(), R.color.videoColorPrimary)
+ val color = if (isLocked) context?.colorFromAttribute(R.attr.colorPrimary)
else Color.WHITE
- video_locked_text?.setTextColor(color)
- video_locked_img?.setColorFilter(color)
+ if (color != null) {
+ video_locked_text?.setTextColor(color)
+ video_locked_img?.setColorFilter(color)
+ }
val isClick = !isLocked
@@ -996,7 +998,7 @@ class PlayerFragment : Fragment() {
activity?.unregisterReceiver(it)
}
activity?.hideSystemUI()
- this.view?.let { activity?.hideKeyboard(it) }
+ this.view?.let { hideKeyboard(it) }
}
}
@@ -1938,6 +1940,9 @@ class PlayerFragment : Fragment() {
) + currentUrl.headers // Adds the headers from the provider, e.g Authorization
setDefaultRequestProperties(headers)
}
+
+ //https://stackoverflow.com/questions/69040127/error-code-io-bad-http-status-exoplayer-android
+ setAllowCrossProtocolRedirects(true)
}
} else {
DefaultDataSourceFactory(requireContext(), USER_AGENT)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
index 8752a17c..382ceb01 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt
@@ -18,7 +18,6 @@ import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.coordinatorlayout.widget.CoordinatorLayout
-import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.core.text.color
import androidx.core.view.isVisible
@@ -196,7 +195,7 @@ class ResultFragment : Fragment() {
super.onDestroy()
activity?.let {
it.window?.navigationBarColor =
- it.colorFromAttribute(R.attr.darkBackground)
+ it.colorFromAttribute(R.attr.primaryGrayBackground)
}
}
@@ -236,7 +235,7 @@ class ResultFragment : Fragment() {
private var currentId: Int? = null
private var currentIsMovie: Boolean? = null
private var episodeRanges: List? = null
-
+ private var dubRange: Set? = null
var url: String? = null
private fun fromIndexToSeasonText(selection: Int?): String {
@@ -682,7 +681,6 @@ class ResultFragment : Fragment() {
}
outputFile.writeText(text)
-
val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
vlcIntent.setPackage(VLC_PACKAGE)
@@ -859,6 +857,25 @@ class ResultFragment : Fragment() {
}
}
+ observe(viewModel.dubStatus) { status ->
+ result_dub_select?.text = status.toString()
+ }
+
+ observe(viewModel.dubSubSelections) { range ->
+ dubRange = range
+ result_dub_select?.visibility = if (range.size <= 1) GONE else VISIBLE
+ }
+
+ result_dub_select.setOnClickListener {
+ val ranges = dubRange
+ if (ranges != null) {
+ it.popupMenuNoIconsAndNoStringRes(ranges.map { status -> Pair(status.ordinal, status.toString()) }
+ .toList()) {
+ viewModel.changeDubStatus(requireContext(), DubStatus.values()[itemId])
+ }
+ }
+ }
+
observe(viewModel.selectedRange) { range ->
result_episode_select?.text = range
}
@@ -959,20 +976,25 @@ class ResultFragment : Fragment() {
metadataInfoArray.add(Pair(R.string.site, d.apiName))
- if (metadataInfoArray.size > 0) {
- result_metadata.visibility = VISIBLE
- val text = SpannableStringBuilder()
- val grayColor = ContextCompat.getColor(requireContext(), R.color.grayTextColor)
- val textColor = ContextCompat.getColor(requireContext(), R.color.textColor)
- for (meta in metadataInfoArray) {
- text.color(grayColor) { append(getString(meta.first) + ": ") }
- .color(textColor) { append("${meta.second}\n") }
+ context?.let { ctx ->
+ if (metadataInfoArray.size > 0) {
+ result_metadata.visibility = VISIBLE
+ val text = SpannableStringBuilder()
+ val grayColor =
+ ctx.colorFromAttribute(R.attr.grayTextColor) //ContextCompat.getColor(requireContext(), R.color.grayTextColor)
+ val textColor =
+ ctx.colorFromAttribute(R.attr.textColor) //ContextCompat.getColor(requireContext(), R.color.textColor)
+ for (meta in metadataInfoArray) {
+ text.color(grayColor) { append(getString(meta.first) + ": ") }
+ .color(textColor) { append("${meta.second}\n") }
+ }
+ result_metadata.text = text
+ } else {
+ result_metadata.visibility = GONE
}
- result_metadata.text = text
- } else {
- result_metadata.visibility = GONE
}
+
result_poster?.setImage(d.posterUrl)
result_poster_holder?.visibility = if (d.posterUrl.isNullOrBlank()) GONE else VISIBLE
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt
index ad7f7d85..c6a09143 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel.kt
@@ -55,13 +55,20 @@ class ResultViewModel : ViewModel() {
val publicEpisodes: LiveData>> get() = _publicEpisodes
val publicEpisodesCount: LiveData get() = _publicEpisodesCount
- private val dubStatus: MutableLiveData = MutableLiveData()
+ val dubStatus: MutableLiveData get() = _dubStatus
+ private val _dubStatus: MutableLiveData = MutableLiveData()
private val page: MutableLiveData = MutableLiveData()
val id: MutableLiveData = MutableLiveData()
val selectedSeason: MutableLiveData = MutableLiveData(-2)
val seasonSelections: MutableLiveData> = MutableLiveData()
+ val dubSubSelections: MutableLiveData> get() = _dubSubSelections
+ private val _dubSubSelections: MutableLiveData> = MutableLiveData()
+
+ val dubSubEpisodes: MutableLiveData