forked from recloudstream/cloudstream
small cleanup
This commit is contained in:
parent
1da670af80
commit
29d5f3848a
26 changed files with 215 additions and 542 deletions
|
@ -65,20 +65,24 @@ object APIHolder {
|
|||
fun Activity.getApiSettings(): HashSet<String> {
|
||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
val hashSet = HashSet<String>()
|
||||
hashSet.addAll(apis.map { it.name })
|
||||
|
||||
return settingsManager.getStringSet(
|
||||
this.getString(R.string.search_providers_list_key),
|
||||
setOf(apis[defProvider].name)
|
||||
)?.toHashSet() ?: hashSetOf(apis[defProvider].name)
|
||||
hashSet
|
||||
)?.toHashSet() ?: hashSet
|
||||
}
|
||||
|
||||
fun Activity.getApiTypeSettings(): HashSet<TvType> {
|
||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val list = settingsManager.getStringSet(
|
||||
this.getString(R.string.search_types_list_key),
|
||||
setOf(apis[defProvider].name)
|
||||
)
|
||||
val hashSet = HashSet<TvType>()
|
||||
hashSet.addAll(TvType.values())
|
||||
val list = settingsManager.getStringSet(
|
||||
this.getString(R.string.search_types_list_key),
|
||||
hashSet.map { it.name }.toMutableSet()
|
||||
)
|
||||
|
||||
if(list.isNullOrEmpty()) return hashSet
|
||||
|
||||
val names = TvType.values().map { it.name }.toHashSet()
|
||||
|
|
|
@ -296,6 +296,13 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
/*thread {
|
||||
createISO()
|
||||
}*/
|
||||
|
||||
var providersString = "Current providers are:\n"
|
||||
for (api in apis) {
|
||||
providersString += "+ ${api.mainUrl}\n"
|
||||
}
|
||||
println(providersString)
|
||||
|
||||
handleAppIntent(intent)
|
||||
|
||||
thread {
|
||||
|
|
|
@ -11,35 +11,39 @@ import org.jsoup.Jsoup
|
|||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
|
||||
class AnimePaheProvider : MainAPI() {
|
||||
companion object {
|
||||
const val MAIN_URL = "https://animepahe.com"
|
||||
|
||||
var cookies = CookieJar()
|
||||
private fun getType(t: String): TvType {
|
||||
return if (t.contains("OVA") || t.contains("Special")) TvType.ONA
|
||||
else if (t.contains("Movie")) TvType.AnimeMovie
|
||||
else TvType.Anime
|
||||
}
|
||||
|
||||
fun generateSession(): Boolean {
|
||||
if (cookies.entries.size != 0) return true
|
||||
return try {
|
||||
val response = khttp.get("https://animepahe.com/")
|
||||
val response = khttp.get("$MAIN_URL/")
|
||||
cookies = response.cookies
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
val YTSM = "ysmm = '([^']+)".toRegex()
|
||||
|
||||
val KWIK_PARAMS_RE = Regex("""\(\"(\w+)\",\d+,\"(\w+)\",(\d+),(\d+),\d+\)""")
|
||||
val KWIK_PARAMS_RE = Regex("""\("(\w+)",\d+,"(\w+)",(\d+),(\d+),\d+\)""")
|
||||
val KWIK_D_URL = Regex("action=\"([^\"]+)\"")
|
||||
val KWIK_D_TOKEN = Regex("value=\"([^\"]+)\"")
|
||||
val YOUTUBE_VIDEO_LINK = Regex("""(^(?:https?:)?(?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube(?:\-nocookie)?\.(?:[A-Za-z]{2,4}|[A-Za-z]{2,3}\.[A-Za-z]{2})\/)(?:watch|embed\/|vi?\/)*(?:\?[\w=&]*vi?=)?[^#&\?\/]{11}.*${'$'})""")
|
||||
val YOUTUBE_VIDEO_LINK =
|
||||
Regex("""(^(?:https?:)?(?://)?(?:www\.)?(?:youtu\.be/|youtube(?:-nocookie)?\.(?:[A-Za-z]{2,4}|[A-Za-z]{2,3}\.[A-Za-z]{2})/)(?:watch|embed/|vi?/)*(?:\?[\w=&]*vi?=)?[^#&?/]{11}.*${'$'})""")
|
||||
}
|
||||
|
||||
override val mainUrl: String
|
||||
get() = "https://animepahe.com"
|
||||
get() = MAIN_URL
|
||||
override val name: String
|
||||
get() = "AnimePahe"
|
||||
override val hasQuickSearch: Boolean
|
||||
|
@ -54,19 +58,19 @@ class AnimePaheProvider : MainAPI() {
|
|||
TvType.ONA
|
||||
)
|
||||
|
||||
override fun getMainPage(): HomePageResponse? {
|
||||
data class Data (
|
||||
@JsonProperty("id") val id : Int,
|
||||
@JsonProperty("anime_id") val animeId : Int,
|
||||
@JsonProperty("anime_title") val animeTitle : String,
|
||||
@JsonProperty("episode") val episode : Int,
|
||||
@JsonProperty("snapshot") val snapshot : String,
|
||||
@JsonProperty("created_at") val createdAt : String,
|
||||
override fun getMainPage(): HomePageResponse {
|
||||
data class Data(
|
||||
@JsonProperty("id") val id: Int,
|
||||
@JsonProperty("anime_id") val animeId: Int,
|
||||
@JsonProperty("anime_title") val animeTitle: String,
|
||||
@JsonProperty("episode") val episode: Int,
|
||||
@JsonProperty("snapshot") val snapshot: String,
|
||||
@JsonProperty("created_at") val createdAt: String,
|
||||
)
|
||||
|
||||
data class AnimePaheLatestReleases (
|
||||
@JsonProperty("total") val total : Int,
|
||||
@JsonProperty("data") val data : List<Data>
|
||||
data class AnimePaheLatestReleases(
|
||||
@JsonProperty("total") val total: Int,
|
||||
@JsonProperty("data") val data: List<Data>
|
||||
)
|
||||
|
||||
val urls = listOf(
|
||||
|
@ -98,36 +102,36 @@ class AnimePaheProvider : MainAPI() {
|
|||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
if(items.size <= 0) throw ErrorLoadingException()
|
||||
if (items.size <= 0) throw ErrorLoadingException()
|
||||
return HomePageResponse(items)
|
||||
}
|
||||
|
||||
override fun search(query: String): ArrayList<SearchResponse> {
|
||||
|
||||
data class AnimePaheSearchData (
|
||||
@JsonProperty("id") val id : Int,
|
||||
@JsonProperty("slug") val slug : String,
|
||||
@JsonProperty("title") val title : String,
|
||||
@JsonProperty("type") val type : String,
|
||||
@JsonProperty("episodes") val episodes : Int,
|
||||
@JsonProperty("status") val status : String,
|
||||
@JsonProperty("season") val season : String,
|
||||
@JsonProperty("year") val year : Int,
|
||||
@JsonProperty("score") val score : Double,
|
||||
@JsonProperty("poster") val poster : String,
|
||||
@JsonProperty("session") val session : String,
|
||||
@JsonProperty("relevance") val relevance : String
|
||||
data class AnimePaheSearchData(
|
||||
@JsonProperty("id") val id: Int,
|
||||
@JsonProperty("slug") val slug: String,
|
||||
@JsonProperty("title") val title: String,
|
||||
@JsonProperty("type") val type: String,
|
||||
@JsonProperty("episodes") val episodes: Int,
|
||||
@JsonProperty("status") val status: String,
|
||||
@JsonProperty("season") val season: String,
|
||||
@JsonProperty("year") val year: Int,
|
||||
@JsonProperty("score") val score: Double,
|
||||
@JsonProperty("poster") val poster: String,
|
||||
@JsonProperty("session") val session: String,
|
||||
@JsonProperty("relevance") val relevance: String
|
||||
)
|
||||
|
||||
data class AnimePaheSearch (
|
||||
@JsonProperty("total") val total : Int,
|
||||
@JsonProperty("data") val data : List<AnimePaheSearchData>
|
||||
data class AnimePaheSearch(
|
||||
@JsonProperty("total") val total: Int,
|
||||
@JsonProperty("data") val data: List<AnimePaheSearchData>
|
||||
)
|
||||
|
||||
val url = "https://animepahe.com/api?m=search&l=8&q=$query"
|
||||
val headers = mapOf("referer" to "https://animepahe.com/")
|
||||
val url = "$mainUrl/api?m=search&l=8&q=$query"
|
||||
val headers = mapOf("referer" to "$mainUrl/")
|
||||
|
||||
val req = khttp.get(url, headers=headers)
|
||||
val req = khttp.get(url, headers = headers)
|
||||
val data = req.let { mapper.readValue<AnimePaheSearch>(it.text) }
|
||||
|
||||
return ArrayList(data.data.map {
|
||||
|
@ -172,11 +176,11 @@ class AnimePaheProvider : MainAPI() {
|
|||
|
||||
private fun generateListOfEpisodes(link: String): ArrayList<AnimeEpisode> {
|
||||
try {
|
||||
val attrs = link.split('/')
|
||||
val attrs = link.split('/')
|
||||
val id = attrs[attrs.size - 1]
|
||||
|
||||
val uri = "https://animepahe.com/api?m=release&id=$id&sort=episode_asc&page=1"
|
||||
val headers = mapOf("referer" to "https://animepahe.com/")
|
||||
val uri = "$mainUrl/api?m=release&id=$id&sort=episode_asc&page=1"
|
||||
val headers = mapOf("referer" to "$mainUrl/")
|
||||
|
||||
val req = khttp.get(uri, headers = headers)
|
||||
val data = req.let { mapper.readValue<AnimePaheAnimeData>(it.text) }
|
||||
|
@ -188,7 +192,7 @@ class AnimePaheProvider : MainAPI() {
|
|||
val episodes = ArrayList<AnimeEpisode>()
|
||||
|
||||
fun getEpisodeTitle(k: AnimeData): String {
|
||||
return if (k.title.length == 0) {
|
||||
return if (k.title.isEmpty()) {
|
||||
"Episode ${k.episode}"
|
||||
} else {
|
||||
k.title
|
||||
|
@ -199,9 +203,13 @@ class AnimePaheProvider : MainAPI() {
|
|||
data.data.forEach {
|
||||
episodes.add(
|
||||
AnimeEpisode(
|
||||
"https://animepahe.com/api?m=links&id=${it.animeId}&session=${it.session}&p=kwik!!TRUE!!",
|
||||
"$mainUrl/api?m=links&id=${it.animeId}&session=${it.session}&p=kwik!!TRUE!!",
|
||||
getEpisodeTitle(it),
|
||||
if (it.snapshot.length == 0) {null} else {it.snapshot},
|
||||
if (it.snapshot.length == 0) {
|
||||
null
|
||||
} else {
|
||||
it.snapshot
|
||||
},
|
||||
it.createdAt
|
||||
)
|
||||
)
|
||||
|
@ -212,7 +220,7 @@ class AnimePaheProvider : MainAPI() {
|
|||
if (ep <= total) {
|
||||
episodes.add(
|
||||
AnimeEpisode(
|
||||
"https://animepahe.com/api?m=release&id=${id}&sort=episode_asc&page=${page + 1}&ep=${ep}!!FALSE!!"
|
||||
"$mainUrl/api?m=release&id=${id}&sort=episode_asc&page=${page + 1}&ep=${ep}!!FALSE!!"
|
||||
)
|
||||
)
|
||||
++ep
|
||||
|
@ -222,7 +230,7 @@ class AnimePaheProvider : MainAPI() {
|
|||
}
|
||||
return episodes
|
||||
} catch (e: Exception) {
|
||||
return ArrayList<AnimeEpisode>()
|
||||
return ArrayList()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,7 +243,7 @@ class AnimePaheProvider : MainAPI() {
|
|||
val japTitle = doc.selectFirst("h2.japanese")?.text()
|
||||
val poster = doc.selectFirst(".anime-poster a").attr("href")
|
||||
|
||||
val TvType = doc.selectFirst("""a[href*="/anime/type/"]""")?.text()
|
||||
val tvType = doc.selectFirst("""a[href*="/anime/type/"]""")?.text()
|
||||
|
||||
val trailer: String? = if (html.contains("https://www.youtube.com/watch")) {
|
||||
YOUTUBE_VIDEO_LINK.find(html)?.destructured?.component1()
|
||||
|
@ -243,9 +251,11 @@ class AnimePaheProvider : MainAPI() {
|
|||
null
|
||||
}
|
||||
|
||||
val episodes = generateListOfEpisodes(url) ?: ArrayList<AnimeEpisode>()
|
||||
val year = """<strong>Aired:<\/strong>[^,]*, (\d+)""".toRegex().find(html)!!.destructured?.component1()?.toIntOrNull()
|
||||
val status = when ("""<strong>Status:<\/strong>[^a]*a href=[\"']\/anime\/(.*?)[\"']""".toRegex().find(html)!!.destructured?.component1().toString()) {
|
||||
val episodes = generateListOfEpisodes(url)
|
||||
val year = """<strong>Aired:</strong>[^,]*, (\d+)""".toRegex().find(html)!!.destructured.component1()
|
||||
.toIntOrNull()
|
||||
val status = when ("""<strong>Status:</strong>[^a]*a href=["']/anime/(.*?)["']""".toRegex()
|
||||
.find(html)!!.destructured.component1()) {
|
||||
"airing" -> ShowStatus.Ongoing
|
||||
"completed" -> ShowStatus.Completed
|
||||
else -> null
|
||||
|
@ -259,9 +269,9 @@ class AnimePaheProvider : MainAPI() {
|
|||
val split = aTag.attr("href").split("/")
|
||||
|
||||
if (aTag.attr("href").contains("anilist.co")) {
|
||||
anilistId = split[split.size-1].toIntOrNull()
|
||||
anilistId = split[split.size - 1].toIntOrNull()
|
||||
} else if (aTag.attr("href").contains("myanimelist.net")) {
|
||||
malId = split[split.size-1].toIntOrNull()
|
||||
malId = split[split.size - 1].toIntOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -271,7 +281,7 @@ class AnimePaheProvider : MainAPI() {
|
|||
title.toString(),
|
||||
url,
|
||||
this.name,
|
||||
getType(TvType.toString()),
|
||||
getType(tvType.toString()),
|
||||
poster,
|
||||
year,
|
||||
null,
|
||||
|
@ -280,7 +290,9 @@ class AnimePaheProvider : MainAPI() {
|
|||
synopsis,
|
||||
if (!doc.select(".anime-genre > ul a").isEmpty()) {
|
||||
ArrayList(doc.select(".anime-genre > ul a").map { it.text().toString() })
|
||||
} else { null },
|
||||
} else {
|
||||
null
|
||||
},
|
||||
ArrayList(),
|
||||
malId,
|
||||
anilistId,
|
||||
|
@ -301,21 +313,25 @@ class AnimePaheProvider : MainAPI() {
|
|||
for (string in cookie.split("; ")) {
|
||||
val split = string.split("=").toMutableList()
|
||||
val name = split.removeFirst().trim()
|
||||
val value = if (split.size == 0) {"true"} else {split.joinToString("=")}
|
||||
val value = if (split.size == 0) {
|
||||
"true"
|
||||
} else {
|
||||
split.joinToString("=")
|
||||
}
|
||||
cookies[name] = value
|
||||
}
|
||||
return cookies.toMap()
|
||||
}
|
||||
|
||||
private fun getString(content: String, s1: Int, s2:Int): String {
|
||||
val characterMap: String = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/";
|
||||
private fun getString(content: String, s1: Int, s2: Int): String {
|
||||
val characterMap = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
|
||||
|
||||
val slice2 = characterMap.slice(0..s2-1)
|
||||
val slice2 = characterMap.slice(0 until s2)
|
||||
var acc: Long = 0
|
||||
|
||||
|
||||
for ((n, i) in content.reversed().withIndex()) {
|
||||
acc += (when(isNumber("$i")) {
|
||||
acc += (when (isNumber("$i")) {
|
||||
true -> "$i".toLong()
|
||||
false -> "0".toLong()
|
||||
}) * Math.pow(s1.toDouble(), n.toDouble()).toInt()
|
||||
|
@ -335,7 +351,6 @@ class AnimePaheProvider : MainAPI() {
|
|||
}
|
||||
|
||||
private fun decrypt(fullString: String, key: String, v1: Int, v2: Int): String {
|
||||
|
||||
var r = ""
|
||||
var i = 0
|
||||
|
||||
|
@ -363,7 +378,7 @@ class AnimePaheProvider : MainAPI() {
|
|||
val newList = ArrayList<Pair<Pair<Int, Int>, Pair<Int, Int>>>()
|
||||
|
||||
while (allItems.size > 1) {
|
||||
newList.add(Pair<Pair<Int, Int>, Pair<Int, Int>>(allItems[0], allItems[1]))
|
||||
newList.add(Pair(allItems[0], allItems[1]))
|
||||
allItems.removeAt(0)
|
||||
allItems.removeAt(0)
|
||||
}
|
||||
|
@ -371,7 +386,8 @@ class AnimePaheProvider : MainAPI() {
|
|||
}
|
||||
|
||||
private fun decodeAdfly(codedKey: String): String {
|
||||
var r = ""; var j = ""
|
||||
var r = ""
|
||||
var j = ""
|
||||
|
||||
for ((n, l) in codedKey.withIndex()) {
|
||||
if (n % 2 != 0) {
|
||||
|
@ -385,7 +401,7 @@ class AnimePaheProvider : MainAPI() {
|
|||
val numbers = sequence {
|
||||
for ((i, n) in encodedUri.withIndex()) {
|
||||
if (isNumber(n)) {
|
||||
yield(Pair<Int, Int>(i, n.toInt()))
|
||||
yield(Pair(i, n.toInt()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -398,21 +414,20 @@ class AnimePaheProvider : MainAPI() {
|
|||
}
|
||||
var returnValue = String(encodedUri.joinToString("").toByteArray(), Charsets.UTF_8)
|
||||
returnValue = String(android.util.Base64.decode(returnValue, android.util.Base64.DEFAULT), Charsets.ISO_8859_1)
|
||||
return returnValue.slice(16..returnValue.length-17)
|
||||
return returnValue.slice(16..returnValue.length - 17)
|
||||
}
|
||||
|
||||
private data class VideoQuality (
|
||||
@JsonProperty("id") val id : Int?,
|
||||
@JsonProperty("audio") val audio : String?,
|
||||
@JsonProperty("kwik") val kwik : String?,
|
||||
@JsonProperty("kwik_adfly") val kwikAdfly : String
|
||||
private data class VideoQuality(
|
||||
@JsonProperty("id") val id: Int?,
|
||||
@JsonProperty("audio") val audio: String?,
|
||||
@JsonProperty("kwik") val kwik: String?,
|
||||
@JsonProperty("kwik_adfly") val kwikAdfly: String
|
||||
)
|
||||
|
||||
private data class AnimePaheEpisodeLoadLinks (
|
||||
@JsonProperty("data") val data : List<Map<String, VideoQuality>>
|
||||
private data class AnimePaheEpisodeLoadLinks(
|
||||
@JsonProperty("data") val data: List<Map<String, VideoQuality>>
|
||||
)
|
||||
|
||||
|
||||
private fun bypassAdfly(adflyUri: String): String {
|
||||
if (!generateSession()) {
|
||||
return bypassAdfly(adflyUri)
|
||||
|
@ -423,7 +438,11 @@ class AnimePaheProvider : MainAPI() {
|
|||
var tries = 0
|
||||
|
||||
while (responseCode != 200 && tries < 20) {
|
||||
adflyContent = khttp.get(khttp.get(adflyUri, cookies=cookies, allowRedirects = false).headers.getValue("location"), cookies=cookies, allowRedirects = false)
|
||||
adflyContent = khttp.get(
|
||||
khttp.get(adflyUri, cookies = cookies, allowRedirects = false).headers.getValue("location"),
|
||||
cookies = cookies,
|
||||
allowRedirects = false
|
||||
)
|
||||
cookies.putAll(adflyContent.cookies.toMap())
|
||||
responseCode = adflyContent.statusCode
|
||||
++tries
|
||||
|
@ -435,10 +454,11 @@ class AnimePaheProvider : MainAPI() {
|
|||
}
|
||||
|
||||
private fun getStreamUrlFromKwik(adflyUri: String): String {
|
||||
val fContent = khttp.get(bypassAdfly(adflyUri), headers=mapOf("referer" to "https://kwik.cx/"), cookies=cookies)
|
||||
val fContent =
|
||||
khttp.get(bypassAdfly(adflyUri), headers = mapOf("referer" to "https://kwik.cx/"), cookies = cookies)
|
||||
cookies.putAll(fContent.cookies.toMap())
|
||||
|
||||
val (fullString, key, v1, v2) = KWIK_PARAMS_RE.find(fContent.text.toString())!!.destructured
|
||||
val (fullString, key, v1, v2) = KWIK_PARAMS_RE.find(fContent.text)!!.destructured
|
||||
val decrypted = decrypt(fullString, key, v1.toInt(), v2.toInt())
|
||||
val uri = KWIK_D_URL.find(decrypted)!!.destructured.component1()
|
||||
val tok = KWIK_D_TOKEN.find(decrypted)!!.destructured.component1()
|
||||
|
@ -451,9 +471,9 @@ class AnimePaheProvider : MainAPI() {
|
|||
content = khttp.post(
|
||||
uri,
|
||||
allowRedirects = false,
|
||||
data=mapOf("_token" to tok),
|
||||
headers=mapOf("referer" to fContent.url),
|
||||
cookies=cookieStrToMap(fContent.headers.getValue("set-cookie").replace("path=/,", ""))
|
||||
data = mapOf("_token" to tok),
|
||||
headers = mapOf("referer" to fContent.url),
|
||||
cookies = cookieStrToMap(fContent.headers.getValue("set-cookie").replace("path=/,", ""))
|
||||
)
|
||||
code = content.statusCode
|
||||
++tries
|
||||
|
@ -466,7 +486,7 @@ class AnimePaheProvider : MainAPI() {
|
|||
|
||||
private fun extractVideoLinks(episodeLink: String): List<ExtractorLink> {
|
||||
var link = episodeLink
|
||||
val headers = mapOf("referer" to "https://animepahe.com/")
|
||||
val headers = mapOf("referer" to "$mainUrl/")
|
||||
|
||||
if (link.contains("!!TRUE!!")) {
|
||||
link = link.replace("!!TRUE!!", "")
|
||||
|
@ -485,7 +505,7 @@ class AnimePaheProvider : MainAPI() {
|
|||
null
|
||||
}
|
||||
}).filterNotNull())[0]
|
||||
link = "https://animepahe.com/api?m=links&id=${ep.animeId}&session=${ep.session}&p=kwik"
|
||||
link = "$mainUrl/api?m=links&id=${ep.animeId}&session=${ep.session}&p=kwik"
|
||||
}
|
||||
val req = khttp.get(link, headers = headers)
|
||||
val data = mapper.readValue<AnimePaheEpisodeLoadLinks>(req.text)
|
||||
|
|
|
@ -48,11 +48,11 @@ class GogoanimeProvider : MainAPI() {
|
|||
"dnt" to "1",
|
||||
"sec-ch-ua-mobile" to "?0",
|
||||
"user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36",
|
||||
"origin" to "https://gogoanime.vc",
|
||||
"origin" to mainUrl,
|
||||
"sec-fetch-site" to "cross-site",
|
||||
"sec-fetch-mode" to "cors",
|
||||
"sec-fetch-dest" to "empty",
|
||||
"referer" to "https://gogoanime.vc/"
|
||||
"referer" to "$mainUrl/"
|
||||
)
|
||||
val parseRegex = Regex("""<li>\s*\n.*\n.*<a\s*href=["'](.*?-episode-(\d+))["']\s*title=["'](.*?)["']>\n.*?img src="(.*?)"""")
|
||||
|
||||
|
@ -179,7 +179,7 @@ class GogoanimeProvider : MainAPI() {
|
|||
title,
|
||||
link,
|
||||
this.name,
|
||||
GogoanimeProvider.getType(type.toString()),
|
||||
getType(type.toString()),
|
||||
poster,
|
||||
year,
|
||||
null,
|
||||
|
|
|
@ -1,315 +0,0 @@
|
|||
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.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.extractors.Vidstream
|
||||
import java.net.URLEncoder
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
const val SHIRO_TIMEOUT_TIME = 60.0
|
||||
|
||||
class ShiroProvider : MainAPI() {
|
||||
companion object {
|
||||
var token: String? = null
|
||||
|
||||
fun getType(t: String?): TvType {
|
||||
return when (t) {
|
||||
"TV" -> TvType.Anime
|
||||
"OVA" -> TvType.ONA
|
||||
"movie" -> TvType.Movie
|
||||
else -> TvType.Anime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun autoLoadToken(): Boolean {
|
||||
if (token != null) return true
|
||||
return loadToken()
|
||||
}
|
||||
|
||||
private fun loadToken(): Boolean {
|
||||
return try {
|
||||
val response = khttp.get(mainUrl, headers = baseHeader)
|
||||
|
||||
val jsMatch = Regex("""src="(/static/js/main.*?)"""").find(response.text)
|
||||
val (destructed) = jsMatch!!.destructured
|
||||
val jsLocation = "$mainUrl$destructed"
|
||||
val js = khttp.get(jsLocation, headers = baseHeader)
|
||||
val tokenMatch = Regex("""token:"(.*?)"""").find(js.text)
|
||||
token = (tokenMatch!!.destructured).component1()
|
||||
|
||||
token != null
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override val mainUrl: String
|
||||
get() = "https://shiro.is"
|
||||
|
||||
override val name: String
|
||||
get() = "Shiro"
|
||||
|
||||
override val hasQuickSearch: Boolean
|
||||
get() = true
|
||||
|
||||
override val hasMainPage: Boolean
|
||||
get() = true
|
||||
|
||||
data class ShiroSearchResponseShow(
|
||||
@JsonProperty("image") val image: String,
|
||||
@JsonProperty("_id") val _id: String,
|
||||
@JsonProperty("slug") val slug: String,
|
||||
@JsonProperty("name") val name: String,
|
||||
@JsonProperty("episodeCount") val episodeCount: String?,
|
||||
@JsonProperty("language") val language: String?,
|
||||
@JsonProperty("type") val type: String?,
|
||||
@JsonProperty("year") val year: String?,
|
||||
@JsonProperty("canonicalTitle") val canonicalTitle: String,
|
||||
@JsonProperty("english") val english: String?,
|
||||
)
|
||||
|
||||
data class ShiroSearchResponse(
|
||||
@JsonProperty("data") val data: List<ShiroSearchResponseShow>,
|
||||
@JsonProperty("status") val status: String,
|
||||
)
|
||||
|
||||
data class ShiroFullSearchResponseCurrentPage(
|
||||
@JsonProperty("items") val items: List<ShiroSearchResponseShow>,
|
||||
)
|
||||
|
||||
data class ShiroFullSearchResponseNavItems(
|
||||
@JsonProperty("currentPage") val currentPage: ShiroFullSearchResponseCurrentPage,
|
||||
)
|
||||
|
||||
data class ShiroFullSearchResponseNav(
|
||||
@JsonProperty("nav") val nav: ShiroFullSearchResponseNavItems,
|
||||
)
|
||||
|
||||
data class ShiroFullSearchResponse(
|
||||
@JsonProperty("data") val data: ShiroFullSearchResponseNav,
|
||||
@JsonProperty("status") val status: String,
|
||||
)
|
||||
|
||||
data class ShiroVideo(
|
||||
@JsonProperty("video_id") val video_id: String,
|
||||
@JsonProperty("host") val host: String,
|
||||
)
|
||||
|
||||
data class ShiroEpisodes(
|
||||
@JsonProperty("anime") val anime: AnimePageData?,
|
||||
@JsonProperty("anime_slug") val anime_slug: String,
|
||||
@JsonProperty("create") val create: String,
|
||||
@JsonProperty("dayOfTheWeek") val dayOfTheWeek: String,
|
||||
@JsonProperty("episode_number") val episode_number: Int,
|
||||
@JsonProperty("slug") val slug: String,
|
||||
@JsonProperty("update") val update: String,
|
||||
@JsonProperty("_id") val _id: String,
|
||||
@JsonProperty("videos") val videos: List<ShiroVideo>,
|
||||
)
|
||||
|
||||
data class AnimePageData(
|
||||
@JsonProperty("banner") val banner: String?,
|
||||
@JsonProperty("canonicalTitle") val canonicalTitle: String?,
|
||||
@JsonProperty("episodeCount") val episodeCount: String,
|
||||
@JsonProperty("genres") val genres: List<String>?,
|
||||
@JsonProperty("image") val image: String,
|
||||
@JsonProperty("japanese") val japanese: String?,
|
||||
@JsonProperty("english") val english: String?,
|
||||
@JsonProperty("language") val language: String,
|
||||
@JsonProperty("name") val name: String,
|
||||
@JsonProperty("slug") val slug: String,
|
||||
@JsonProperty("synopsis") val synopsis: String,
|
||||
@JsonProperty("type") val type: String?,
|
||||
@JsonProperty("views") val views: Int?,
|
||||
@JsonProperty("year") val year: String?,
|
||||
@JsonProperty("_id") val _id: String,
|
||||
@JsonProperty("episodes") var episodes: List<ShiroEpisodes>?,
|
||||
@JsonProperty("synonyms") var synonyms: List<String>?,
|
||||
@JsonProperty("status") val status: String?,
|
||||
@JsonProperty("schedule") val schedule: String?,
|
||||
)
|
||||
|
||||
data class AnimePage(
|
||||
@JsonProperty("data") val data: AnimePageData,
|
||||
@JsonProperty("status") val status: String,
|
||||
)
|
||||
|
||||
data class ShiroHomePageData(
|
||||
@JsonProperty("trending_animes") val trending_animes: List<AnimePageData>,
|
||||
@JsonProperty("ongoing_animes") val ongoing_animes: List<AnimePageData>,
|
||||
@JsonProperty("latest_animes") val latest_animes: List<AnimePageData>,
|
||||
@JsonProperty("latest_episodes") val latest_episodes: List<ShiroEpisodes>,
|
||||
)
|
||||
|
||||
data class ShiroHomePage(
|
||||
@JsonProperty("status") val status: String,
|
||||
@JsonProperty("data") val data: ShiroHomePageData,
|
||||
@JsonProperty("random") var random: AnimePage?,
|
||||
)
|
||||
|
||||
private fun toHomePageList(list: List<AnimePageData>, name: String): HomePageList {
|
||||
return HomePageList(name, list.map { data ->
|
||||
val type = getType(data.type)
|
||||
val isDubbed =
|
||||
data.language == "dubbed"
|
||||
|
||||
val set: EnumSet<DubStatus> =
|
||||
EnumSet.of(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed)
|
||||
|
||||
val episodeCount = data.episodeCount.toIntOrNull()
|
||||
|
||||
return@map AnimeSearchResponse(
|
||||
data.name.replace("Dubbed", ""), // i.english ?: i.canonicalTitle,
|
||||
"$mainUrl/anime/${data.slug}",
|
||||
this.name,
|
||||
type,
|
||||
"https://cdn.shiro.is/${data.image}",
|
||||
data.year?.toIntOrNull(),
|
||||
data.canonicalTitle,
|
||||
set,
|
||||
if (isDubbed) episodeCount else null,
|
||||
if (!isDubbed) episodeCount else null,
|
||||
)
|
||||
}.toList())
|
||||
}
|
||||
|
||||
private fun turnSearchIntoResponse(data: ShiroSearchResponseShow): AnimeSearchResponse {
|
||||
val type = getType(data.type)
|
||||
val isDubbed =
|
||||
if (data.language != null)
|
||||
data.language == "dubbed"
|
||||
else
|
||||
data.slug.contains("dubbed")
|
||||
val set: EnumSet<DubStatus> =
|
||||
EnumSet.of(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed)
|
||||
|
||||
val episodeCount = data.episodeCount?.toIntOrNull()
|
||||
|
||||
return AnimeSearchResponse(
|
||||
data.name.replace("Dubbed", ""), // i.english ?: i.canonicalTitle,
|
||||
"$mainUrl/anime/${data.slug}",
|
||||
this.name,
|
||||
type,
|
||||
"https://cdn.shiro.is/${data.image}",
|
||||
data.year?.toIntOrNull(),
|
||||
data.canonicalTitle,
|
||||
set,
|
||||
if (isDubbed) episodeCount else null,
|
||||
if (!isDubbed) episodeCount else null,
|
||||
)
|
||||
}
|
||||
|
||||
override fun getMainPage(): HomePageResponse? {
|
||||
if (!autoLoadToken()) return null
|
||||
|
||||
val url = "https://tapi.shiro.is/latest?token=$token"
|
||||
val response = khttp.get(url, timeout = SHIRO_TIMEOUT_TIME)
|
||||
val res = response.text.let { mapper.readValue<ShiroHomePage>(it) }
|
||||
|
||||
val d = res.data
|
||||
return HomePageResponse(
|
||||
listOf(
|
||||
toHomePageList(d.trending_animes, "Trending"),
|
||||
toHomePageList(d.ongoing_animes, "Ongoing"),
|
||||
toHomePageList(d.latest_animes, "Latest")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun quickSearch(query: String): ArrayList<SearchResponse>? {
|
||||
if (!autoLoadToken()) return null
|
||||
|
||||
val returnValue: ArrayList<SearchResponse> = ArrayList()
|
||||
|
||||
val response = khttp.get(
|
||||
"https://tapi.shiro.is/anime/auto-complete/${
|
||||
URLEncoder.encode(
|
||||
query,
|
||||
"UTF-8"
|
||||
)
|
||||
}?token=$token".replace("+", "%20")
|
||||
)
|
||||
if (response.text == "{\"status\":\"Found\",\"data\":[]}") return returnValue // OR ELSE WILL CAUSE WEIRD ERROR
|
||||
|
||||
val mapped = response.let { mapper.readValue<ShiroSearchResponse>(it.text) }
|
||||
for (i in mapped.data) {
|
||||
returnValue.add(turnSearchIntoResponse(i))
|
||||
}
|
||||
return returnValue
|
||||
}
|
||||
|
||||
override fun search(query: String): ArrayList<SearchResponse>? {
|
||||
if (!autoLoadToken()) return null
|
||||
val returnValue: ArrayList<SearchResponse> = ArrayList()
|
||||
val response = khttp.get(
|
||||
"https://tapi.shiro.is/advanced?search=${
|
||||
URLEncoder.encode(
|
||||
query,
|
||||
"UTF-8"
|
||||
)
|
||||
}&token=$token".replace("+", "%20")
|
||||
)
|
||||
if (response.text == "{\"status\":\"Found\",\"data\":[]}") return returnValue // OR ELSE WILL CAUSE WEIRD ERROR
|
||||
|
||||
val mapped = response.let { mapper.readValue<ShiroFullSearchResponse>(it.text) }
|
||||
for (i in mapped.data.nav.currentPage.items) {
|
||||
returnValue.add(turnSearchIntoResponse(i))
|
||||
}
|
||||
return returnValue
|
||||
}
|
||||
|
||||
override fun load(url: String): LoadResponse? {
|
||||
if (!autoLoadToken()) return null
|
||||
val slug = url.replace("$mainUrl/anime/", "").replace("$mainUrl/", "")
|
||||
val rurl = "https://tapi.shiro.is/anime/slug/${slug}?token=${token}"
|
||||
val response = khttp.get(rurl, timeout = 120.0)
|
||||
val mapped = response.let { mapper.readValue<AnimePage>(it.text) }
|
||||
val data = mapped.data
|
||||
val isDubbed = data.language == "dubbed"
|
||||
val episodes =
|
||||
ArrayList<AnimeEpisode>(
|
||||
data.episodes?.distinctBy { it.episode_number }?.sortedBy { it.episode_number }
|
||||
?.filter { it.videos.isNotEmpty() }
|
||||
?.map { AnimeEpisode(it.videos.first().video_id) }
|
||||
?: ArrayList<AnimeEpisode>())
|
||||
val status = when (data.status) {
|
||||
"current" -> ShowStatus.Ongoing
|
||||
"finished" -> ShowStatus.Completed
|
||||
else -> null
|
||||
}
|
||||
|
||||
return AnimeLoadResponse(
|
||||
data.english,
|
||||
data.japanese,
|
||||
data.name.replace("Dubbed", ""),//data.canonicalTitle ?: data.name.replace("Dubbed", ""),
|
||||
"$mainUrl/anime/${url}",
|
||||
this.name,
|
||||
getType(data.type ?: ""),
|
||||
"https://cdn.shiro.is/${data.image}",
|
||||
data.year?.toIntOrNull(),
|
||||
if (isDubbed) episodes else null,
|
||||
if (!isDubbed) episodes else null,
|
||||
status,
|
||||
data.synopsis,
|
||||
ArrayList(data.genres ?: ArrayList()),
|
||||
ArrayList(data.synonyms ?: ArrayList()),
|
||||
null,
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
override fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
return Vidstream().getUrl(data, isCasting) {
|
||||
callback.invoke(it)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -217,7 +217,7 @@ class TenshiProvider : MainAPI() {
|
|||
canonicalTitle,
|
||||
url,
|
||||
this.name,
|
||||
TenshiProvider.getType(type ?: ""),
|
||||
getType(type ?: ""),
|
||||
poster,
|
||||
year.toIntOrNull(),
|
||||
null,
|
||||
|
|
|
@ -23,7 +23,7 @@ class WatchCartoonOnlineProvider : MainAPI() {
|
|||
TvType.Anime,
|
||||
)
|
||||
|
||||
override fun search(query: String): ArrayList<SearchResponse>? {
|
||||
override fun search(query: String): List<SearchResponse> {
|
||||
val url = "https://www.wcostream.com/search"
|
||||
|
||||
val response =
|
||||
|
@ -69,7 +69,7 @@ class WatchCartoonOnlineProvider : MainAPI() {
|
|||
return returnValue
|
||||
}
|
||||
|
||||
override fun load(url: String): LoadResponse? {
|
||||
override fun load(url: String): LoadResponse {
|
||||
val response = khttp.get(url)
|
||||
val document = Jsoup.parse(response.text)
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class WcoProvider : MainAPI() {
|
|||
TvType.ONA
|
||||
)
|
||||
|
||||
override fun getMainPage(): HomePageResponse? {
|
||||
override fun getMainPage(): HomePageResponse {
|
||||
val urls = listOf(
|
||||
Pair("$mainUrl/ajax/list/recently_updated?type=tv", "Recently Updated Anime"),
|
||||
Pair("$mainUrl/ajax/list/recently_updated?type=movie", "Recently Updated Movies"),
|
||||
|
@ -114,7 +114,7 @@ class WcoProvider : MainAPI() {
|
|||
return returnValue
|
||||
}
|
||||
|
||||
override fun search(query: String): ArrayList<SearchResponse> {
|
||||
override fun search(query: String): List<SearchResponse> {
|
||||
val url = "$mainUrl/search"
|
||||
val response = khttp.get(url, params = mapOf("keyword" to query))
|
||||
var document = Jsoup.parse(response.text)
|
||||
|
@ -133,7 +133,7 @@ class WcoProvider : MainAPI() {
|
|||
return returnValue
|
||||
}
|
||||
|
||||
override fun quickSearch(query: String): ArrayList<SearchResponse> {
|
||||
override fun quickSearch(query: String): List<SearchResponse> {
|
||||
val returnValue: ArrayList<SearchResponse> = ArrayList()
|
||||
|
||||
val response = khttp.post(
|
||||
|
|
|
@ -18,7 +18,7 @@ class HDMProvider : MainAPI() {
|
|||
TvType.Movie,
|
||||
)
|
||||
|
||||
override fun search(query: String): ArrayList<SearchResponse> {
|
||||
override fun search(query: String): List<SearchResponse> {
|
||||
val url = "$mainUrl/search/$query"
|
||||
val response = khttp.get(url)
|
||||
val document = Jsoup.parse(response.text)
|
||||
|
|
|
@ -69,7 +69,7 @@ class LookMovieProvider : MainAPI() {
|
|||
@JsonProperty("season") var season: String,
|
||||
)
|
||||
|
||||
override fun quickSearch(query: String): ArrayList<SearchResponse> {
|
||||
override fun quickSearch(query: String): List<SearchResponse> {
|
||||
val movieUrl = "$mainUrl/api/v1/movies/search/?q=$query"
|
||||
val movieResponse = khttp.get(movieUrl)
|
||||
val movies = mapper.readValue<LookMovieSearchResultRoot>(movieResponse.text).result
|
||||
|
@ -114,7 +114,7 @@ class LookMovieProvider : MainAPI() {
|
|||
return returnValue
|
||||
}
|
||||
|
||||
override fun search(query: String): ArrayList<SearchResponse> {
|
||||
override fun search(query: String): List<SearchResponse> {
|
||||
fun search(query: String, isMovie: Boolean): ArrayList<SearchResponse> {
|
||||
val url = "$mainUrl/${if (isMovie) "movies" else "shows"}/search/?q=$query"
|
||||
val response = khttp.get(url)
|
||||
|
@ -248,7 +248,7 @@ class LookMovieProvider : MainAPI() {
|
|||
val accessToken = root.data?.accessToken ?: return null
|
||||
|
||||
val window =
|
||||
"window\\[\\'show_storage\\'\\] =((.|\\n)*?\\<)".toRegex().find(response.text)?.groupValues?.get(1)
|
||||
"window\\['show_storage'] =((.|\\n)*?<)".toRegex().find(response.text)?.groupValues?.get(1)
|
||||
?: return null
|
||||
// val id = "id_show:(.*?),".toRegex().find(response.text)?.groupValues?.get(1) ?: return null
|
||||
val season = "seasons:.*\\[((.|\\n)*?)]".toRegex().find(window)?.groupValues?.get(1) ?: return null
|
||||
|
|
|
@ -31,11 +31,11 @@ class MeloMovieProvider : MainAPI() {
|
|||
|
||||
data class MeloMovieLink(val name: String, val link: String)
|
||||
|
||||
override fun quickSearch(query: String): ArrayList<SearchResponse> {
|
||||
override fun quickSearch(query: String): List<SearchResponse> {
|
||||
return search(query)
|
||||
}
|
||||
|
||||
override fun search(query: String): ArrayList<SearchResponse> {
|
||||
override fun search(query: String): List<SearchResponse> {
|
||||
val url = "$mainUrl/movie/search/?name=$query"
|
||||
val returnValue: ArrayList<SearchResponse> = ArrayList()
|
||||
val response = khttp.get(url)
|
||||
|
|
|
@ -71,7 +71,7 @@ class TrailersToProvider : MainAPI() {
|
|||
//section.section > div.container > div.owl-carousel
|
||||
}
|
||||
|
||||
override fun quickSearch(query: String): ArrayList<SearchResponse> {
|
||||
override fun quickSearch(query: String): List<SearchResponse> {
|
||||
val url = "$mainUrl/en/quick-search?q=$query"
|
||||
val response = khttp.get(url)
|
||||
val document = Jsoup.parse(response.text)
|
||||
|
@ -99,7 +99,7 @@ class TrailersToProvider : MainAPI() {
|
|||
return returnValue
|
||||
}
|
||||
|
||||
override fun search(query: String): ArrayList<SearchResponse> {
|
||||
override fun search(query: String): List<SearchResponse> {
|
||||
val url = "$mainUrl/en/popular/movies-tvshows-collections?q=$query"
|
||||
val response = khttp.get(url)
|
||||
val document = Jsoup.parse(response.text)
|
||||
|
@ -180,7 +180,7 @@ class TrailersToProvider : MainAPI() {
|
|||
} else if (url.contains("/episode/")) {
|
||||
val response = khttp.get(url)
|
||||
val document = Jsoup.parse(response.text)
|
||||
val qSub = document.select("subtitle-content")
|
||||
//val qSub = document.select("subtitle-content")
|
||||
val subUrl = document.select("subtitle-content")?.attr("data-url") ?: ""
|
||||
|
||||
val subData = fixUrl(document.selectFirst("content").attr("data-url") ?: return false)
|
||||
|
|
|
@ -19,7 +19,7 @@ class VMoveeProvider : MainAPI() {
|
|||
TvType.Movie,
|
||||
)
|
||||
|
||||
override fun search(query: String): ArrayList<SearchResponse>? {
|
||||
override fun search(query: String): List<SearchResponse> {
|
||||
val url = "$mainUrl/?s=$query"
|
||||
val response = khttp.get(url)
|
||||
val document = Jsoup.parse(response.text)
|
||||
|
@ -110,7 +110,7 @@ class VMoveeProvider : MainAPI() {
|
|||
return super.loadLinks(data, isCasting, subtitleCallback, callback)
|
||||
}
|
||||
|
||||
override fun load(url: String): LoadResponse? {
|
||||
override fun load(url: String): LoadResponse {
|
||||
val response = khttp.get(url)
|
||||
val document = Jsoup.parse(response.text)
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ class DownloadChildAdapter(
|
|||
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
|
||||
if (holder is DownloadButtonViewHolder) {
|
||||
holder.downloadButton.dispose()
|
||||
mBoundViewHolders.remove(holder);
|
||||
mBoundViewHolders.remove(holder)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,10 +9,8 @@ import android.widget.TextView
|
|||
import androidx.cardview.widget.CardView
|
||||
import androidx.core.widget.ContentLoadingProgressBar
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.utils.IDisposable
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||
import kotlinx.android.synthetic.main.download_header_episode.view.*
|
||||
import java.util.*
|
||||
|
@ -54,7 +52,7 @@ class DownloadHeaderAdapter(
|
|||
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
|
||||
if (holder is DownloadButtonViewHolder) {
|
||||
holder.downloadButton.dispose()
|
||||
mBoundViewHolders.remove(holder);
|
||||
mBoundViewHolders.remove(holder)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +91,7 @@ class DownloadHeaderAdapter(
|
|||
) : RecyclerView.ViewHolder(itemView), DownloadButtonViewHolder {
|
||||
override var downloadButton = EasyDownloadButton()
|
||||
|
||||
private val poster: ImageView = itemView.download_header_poster
|
||||
private val poster: ImageView? = itemView.download_header_poster
|
||||
private val title: TextView = itemView.download_header_title
|
||||
private val extraInfo: TextView = itemView.download_header_info
|
||||
private val holder: CardView = itemView.episode_holder
|
||||
|
@ -107,17 +105,8 @@ class DownloadHeaderAdapter(
|
|||
fun bind(card: VisualDownloadHeaderCached) {
|
||||
localCard = card
|
||||
val d = card.data
|
||||
if (d.poster != null) {
|
||||
|
||||
val glideUrl =
|
||||
GlideUrl(d.poster)
|
||||
|
||||
poster.context.let {
|
||||
Glide.with(it)
|
||||
.load(glideUrl)
|
||||
.into(poster)
|
||||
}
|
||||
}
|
||||
poster?.setImage(d.poster)
|
||||
|
||||
title.text = d.name
|
||||
val mbString = "%.1f".format(card.totalBytes / 1000000f)
|
||||
|
|
|
@ -38,8 +38,8 @@ class EasyDownloadButton : IDisposable {
|
|||
clickCallback: (DownloadClickEvent) -> Unit,
|
||||
) {
|
||||
setUpDownloadButton(setupCurrentBytes, setupTotalBytes, progressBar, textView, data, downloadButton, {
|
||||
downloadButton?.setIconResource(it.first)
|
||||
downloadButton?.text = it.second
|
||||
downloadButton.setIconResource(it.first)
|
||||
downloadButton.text = it.second
|
||||
}, clickCallback)
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ class EasyDownloadButton : IDisposable {
|
|||
clickCallback: (DownloadClickEvent) -> Unit,
|
||||
) {
|
||||
setUpDownloadButton(setupCurrentBytes, setupTotalBytes, progressBar, textView, data, downloadImage, {
|
||||
downloadImage?.setImageResource(it.first)
|
||||
downloadImage.setImageResource(it.first)
|
||||
}, clickCallback)
|
||||
}
|
||||
|
||||
|
@ -98,20 +98,20 @@ class EasyDownloadButton : IDisposable {
|
|||
if (currentBytes == 0L) {
|
||||
changeDownloadImage(VideoDownloadManager.DownloadType.IsStopped)
|
||||
textView?.visibility = View.GONE
|
||||
progressBar?.visibility = View.GONE
|
||||
progressBar.visibility = View.GONE
|
||||
} else {
|
||||
if (lastState == VideoDownloadManager.DownloadType.IsStopped) {
|
||||
changeDownloadImage(VideoDownloadManager.getDownloadState(data.id))
|
||||
}
|
||||
textView?.visibility = View.VISIBLE
|
||||
progressBar?.visibility = View.VISIBLE
|
||||
progressBar.visibility = View.VISIBLE
|
||||
val currentMbString = "%.1f".format(setCurrentBytes / 1000000f)
|
||||
val totalMbString = "%.1f".format(setTotalBytes / 1000000f)
|
||||
|
||||
textView?.text =
|
||||
"${currentMbString}MB / ${totalMbString}MB"
|
||||
|
||||
progressBar?.let { bar ->
|
||||
progressBar.let { bar ->
|
||||
bar.max = (setTotalBytes / 1000).toInt()
|
||||
|
||||
if (animate) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.*
|
|||
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
|
||||
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
|
||||
import com.lagradost.cloudstream3.ui.search.SearchClickCallback
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
import kotlinx.android.synthetic.main.home_result_grid.view.*
|
||||
|
||||
class HomeChildItemAdapter(
|
||||
|
@ -72,16 +73,7 @@ class HomeChildItemAdapter(
|
|||
cardText.text = card.name
|
||||
|
||||
//imageTextProvider.text = card.apiName
|
||||
if (!card.posterUrl.isNullOrEmpty()) {
|
||||
|
||||
val glideUrl =
|
||||
GlideUrl(card.posterUrl)
|
||||
|
||||
Glide.with(cardView.context)
|
||||
.load(glideUrl)
|
||||
.into(cardView)
|
||||
|
||||
}
|
||||
cardView.setImage(card.posterUrl)
|
||||
|
||||
bg.setOnClickListener {
|
||||
clickCallback.invoke(SearchClickCallback(SEARCH_ACTION_LOAD, it, card))
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.lagradost.cloudstream3.ui.home
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
|
@ -16,15 +15,10 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.apis
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.backEvent
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.observe
|
||||
import com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
||||
|
@ -36,12 +30,15 @@ import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
|||
import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.removeKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
|
||||
import com.lagradost.cloudstream3.utils.Event
|
||||
import com.lagradost.cloudstream3.utils.HOMEPAGE_API
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
import kotlinx.android.synthetic.main.fragment_home.*
|
||||
|
||||
const val HOME_BOOKMARK_VALUE = "home_bookmarked_last"
|
||||
|
@ -148,18 +145,7 @@ class HomeFragment : Fragment() {
|
|||
home_main_text.text = random.name + if (random is AnimeSearchResponse) {
|
||||
random.dubStatus?.joinToString(prefix = " • ", separator = " | ") { it.name }
|
||||
} else ""
|
||||
val glideUrl =
|
||||
GlideUrl(random.posterUrl)
|
||||
requireContext().let {
|
||||
Glide.with(it)
|
||||
.load(glideUrl)
|
||||
.into(home_main_poster)
|
||||
/*
|
||||
Glide.with(it)
|
||||
.load(glideUrl)
|
||||
.apply(RequestOptions.bitmapTransform(BlurTransformation(80, 3)))
|
||||
.into(result_poster_blur)*/
|
||||
}
|
||||
home_main_poster?.setImage(random.posterUrl)
|
||||
|
||||
toggleMainVisibility(true)
|
||||
return random
|
||||
|
|
|
@ -545,17 +545,17 @@ class PlayerFragment : Fragment() {
|
|||
val isAnime =
|
||||
data.isAnimeBased()//(data is AnimeLoadResponse && (data.type == TvType.Anime || data.type == TvType.ONA))
|
||||
|
||||
skip_op.setVis(isAnime && !nextEp)
|
||||
skip_op?.setVis(isAnime && !nextEp)
|
||||
skip_episode.setVis((!isAnime || nextEp) && hasNext)
|
||||
} else {
|
||||
val isAnime = data.isAnimeBased()
|
||||
|
||||
if (isAnime) {
|
||||
skip_op.setVis(true)
|
||||
skip_op?.setVis(true)
|
||||
skip_episode.setVis(false)
|
||||
} else {
|
||||
skip_episode.setVis(data.isEpisodeBased())
|
||||
skip_op.setVis(false)
|
||||
skip_op?.setVis(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -684,7 +684,7 @@ class PlayerFragment : Fragment() {
|
|||
exo_progress.isClickable = isClick
|
||||
//next_episode_btt.isClickable = isClick
|
||||
playback_speed_btt.isClickable = isClick
|
||||
skip_op.isClickable = isClick
|
||||
skip_op?.isClickable = isClick
|
||||
skip_episode.isClickable = isClick
|
||||
resize_player.isClickable = isClick
|
||||
exo_progress.isEnabled = isClick
|
||||
|
@ -1301,7 +1301,7 @@ class PlayerFragment : Fragment() {
|
|||
resize_player.visibility = GONE
|
||||
}
|
||||
|
||||
skip_op.setOnClickListener {
|
||||
skip_op?.setOnClickListener {
|
||||
skipOP()
|
||||
}
|
||||
|
||||
|
@ -1787,7 +1787,7 @@ class PlayerFragment : Fragment() {
|
|||
}
|
||||
|
||||
override fun onPlayerError(error: ExoPlaybackException) {
|
||||
println("CURRENT URL: " + currentUrl?.url ?: uri)
|
||||
println("CURRENT URL: " + currentUrl?.url)
|
||||
// Lets pray this doesn't spam Toasts :)
|
||||
when (error.type) {
|
||||
ExoPlaybackException.TYPE_SOURCE -> {
|
||||
|
@ -1854,7 +1854,9 @@ class PlayerFragment : Fragment() {
|
|||
initPlayer(getCurrentUrl())
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(context, "No Links Found", Toast.LENGTH_SHORT).show()
|
||||
context?.let { ctx ->
|
||||
Toast.makeText(ctx, "No Links Found", LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -11,13 +10,12 @@ import android.widget.Toast
|
|||
import androidx.annotation.LayoutRes
|
||||
import androidx.core.widget.ContentLoadingProgressBar
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
|
||||
import com.lagradost.cloudstream3.ui.download.DownloadButtonViewHolder
|
||||
import com.lagradost.cloudstream3.ui.download.DownloadClickEvent
|
||||
import com.lagradost.cloudstream3.ui.download.EasyDownloadButton
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager
|
||||
import kotlinx.android.synthetic.main.result_episode.view.episode_holder
|
||||
|
@ -71,7 +69,7 @@ class EpisodeAdapter(
|
|||
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
|
||||
if (holder is DownloadButtonViewHolder) {
|
||||
holder.downloadButton.dispose()
|
||||
mBoundViewHolders.remove(holder);
|
||||
mBoundViewHolders.remove(holder)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,13 +150,7 @@ class EpisodeAdapter(
|
|||
|
||||
if (card.poster != null) {
|
||||
episodePoster?.visibility = View.VISIBLE
|
||||
if (episodePoster != null) {
|
||||
val glideUrl =
|
||||
GlideUrl(card.poster)
|
||||
Glide.with(episodePoster.context)
|
||||
.load(glideUrl)
|
||||
.into(episodePoster)
|
||||
}
|
||||
episodePoster?.setImage(card.poster)
|
||||
} else {
|
||||
episodePoster?.visibility = View.GONE
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ import com.lagradost.cloudstream3.utils.Coroutines.main
|
|||
import com.lagradost.cloudstream3.utils.DataStore.getFolderName
|
||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
|
||||
import jp.wasabeef.glide.transformations.BlurTransformation
|
||||
|
@ -398,7 +399,7 @@ class ResultFragment : Fragment() {
|
|||
fun startChromecast(startIndex: Int) {
|
||||
val eps = currentEpisodes ?: return
|
||||
context?.startCast(
|
||||
apiName ?: return,
|
||||
apiName,
|
||||
currentIsMovie ?: return,
|
||||
currentHeaderName,
|
||||
currentPoster,
|
||||
|
@ -850,19 +851,7 @@ class ResultFragment : Fragment() {
|
|||
}
|
||||
|
||||
if (d.posterUrl != null) {
|
||||
val glideUrl =
|
||||
GlideUrl(d.posterUrl)
|
||||
requireContext().let {
|
||||
|
||||
Glide.with(it)
|
||||
.load(glideUrl)
|
||||
.into(result_poster)
|
||||
|
||||
Glide.with(it)
|
||||
.load(glideUrl)
|
||||
.apply(bitmapTransform(BlurTransformation(80, 3)))
|
||||
.into(result_poster_blur)
|
||||
}
|
||||
result_poster?.setImage(d.posterUrl)
|
||||
}
|
||||
|
||||
if (d.plot != null) {
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.getGridFormatId
|
|||
import com.lagradost.cloudstream3.utils.UIHelper.getGridIsCompact
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
||||
import com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
import kotlinx.android.synthetic.main.search_result_compact.view.backgroundCard
|
||||
import kotlinx.android.synthetic.main.search_result_compact.view.imageText
|
||||
import kotlinx.android.synthetic.main.search_result_compact.view.imageView
|
||||
|
@ -104,15 +105,7 @@ class SearchAdapter(
|
|||
cardText.text = card.name
|
||||
|
||||
//imageTextProvider.text = card.apiName
|
||||
if (!card.posterUrl.isNullOrEmpty()) {
|
||||
|
||||
val glideUrl =
|
||||
GlideUrl(card.posterUrl)
|
||||
|
||||
Glide.with(cardView.context)
|
||||
.load(glideUrl)
|
||||
.into(cardView)
|
||||
}
|
||||
cardView.setImage(card.posterUrl)
|
||||
|
||||
bg.setOnClickListener {
|
||||
clickCallback.invoke(SearchClickCallback(SEARCH_ACTION_LOAD, it, card))
|
||||
|
|
|
@ -171,11 +171,11 @@ class SearchFragment : Fragment() {
|
|||
getString(if (isOn) R.string.search_provider_text_types else R.string.search_provider_text_providers)
|
||||
|
||||
if (isOn) {
|
||||
listView2?.visibility = View.VISIBLE
|
||||
listView?.visibility = View.GONE
|
||||
listView2.visibility = View.VISIBLE
|
||||
listView.visibility = View.GONE
|
||||
} else {
|
||||
listView?.visibility = View.VISIBLE
|
||||
listView2?.visibility = View.GONE
|
||||
listView.visibility = View.VISIBLE
|
||||
listView2.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,7 @@ class SearchFragment : Fragment() {
|
|||
listView.setOnItemClickListener { _, _, i, _ ->
|
||||
val types = HashSet<TvType>()
|
||||
for ((index, api) in apis.withIndex()) {
|
||||
if (listView?.checkedItemPositions[index]) {
|
||||
if (listView.checkedItemPositions[index]) {
|
||||
types.addAll(api.supportedTypes)
|
||||
}
|
||||
}
|
||||
|
@ -204,14 +204,14 @@ class SearchFragment : Fragment() {
|
|||
var isSupported = false
|
||||
|
||||
for ((typeIndex, type) in typeChoices.withIndex()) {
|
||||
if (listView2?.checkedItemPositions[typeIndex]) {
|
||||
if (listView2.checkedItemPositions[typeIndex]) {
|
||||
if (api.supportedTypes.any { type.second.contains(it) }) {
|
||||
isSupported = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
listView?.setItemChecked(
|
||||
listView.setItemChecked(
|
||||
index,
|
||||
isSupported
|
||||
)
|
||||
|
@ -221,7 +221,7 @@ class SearchFragment : Fragment() {
|
|||
}
|
||||
|
||||
dialog.setOnDismissListener {
|
||||
context?.setKey(SEARCH_PROVIDER_TOGGLE, toggle.isChecked ?: true)
|
||||
context?.setKey(SEARCH_PROVIDER_TOGGLE, toggle.isChecked)
|
||||
}
|
||||
|
||||
applyButton.setOnClickListener {
|
||||
|
@ -229,7 +229,7 @@ class SearchFragment : Fragment() {
|
|||
|
||||
val activeTypes = HashSet<TvType>()
|
||||
for ((index, name) in typeChoices.withIndex()) {
|
||||
if (listView2?.checkedItemPositions[index]) {
|
||||
if (listView2.checkedItemPositions[index]) {
|
||||
activeTypes.addAll(typeChoices[index].second)
|
||||
}
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ class SearchFragment : Fragment() {
|
|||
|
||||
val activeApis = HashSet<String>()
|
||||
for ((index, name) in apiNames.withIndex()) {
|
||||
if (listView?.checkedItemPositions[index]) {
|
||||
if (listView.checkedItemPositions[index]) {
|
||||
activeApis.add(name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,17 +8,16 @@ import android.content.Context
|
|||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.media.AudioAttributes
|
||||
import android.media.AudioFocusRequest
|
||||
import android.media.AudioManager
|
||||
import android.os.Build
|
||||
import android.view.Gravity
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.appcompat.view.menu.MenuBuilder
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
|
@ -31,6 +30,8 @@ import androidx.core.graphics.red
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.model.GlideUrl
|
||||
import com.lagradost.cloudstream3.R
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
@ -83,6 +84,17 @@ object UIHelper {
|
|||
return color
|
||||
}
|
||||
|
||||
fun ImageView?.setImage(url : String?) {
|
||||
if(this == null || url.isNullOrBlank()) return
|
||||
try {
|
||||
Glide.with(this.context)
|
||||
.load(GlideUrl(url))
|
||||
.into(this)
|
||||
} catch (e : Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
fun adjustAlpha(@ColorInt color: Int, factor: Float): Int {
|
||||
val alpha = (Color.alpha(color) * factor).roundToInt()
|
||||
val red = Color.red(color)
|
||||
|
@ -243,6 +255,7 @@ object UIHelper {
|
|||
return settingsManager?.getBoolean("pip_enabled", true) ?: true && isInPlayer
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
fun Context.hasPIPPermission(): Boolean {
|
||||
val appOps =
|
||||
getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
|
||||
|
|
|
@ -19,15 +19,14 @@ import androidx.core.net.toUri
|
|||
import com.bumptech.glide.Glide
|
||||
import com.lagradost.cloudstream3.MainActivity
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||
import com.lagradost.cloudstream3.services.VideoDownloadService
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.removeKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -37,7 +36,6 @@ import java.net.URL
|
|||
import java.net.URLConnection
|
||||
import java.util.*
|
||||
|
||||
|
||||
const val DOWNLOAD_CHANNEL_ID = "cloudstream3.general"
|
||||
const val DOWNLOAD_CHANNEL_NAME = "Downloads"
|
||||
const val DOWNLOAD_CHANNEL_DESCRIPT = "The download notification channel"
|
||||
|
@ -180,18 +178,22 @@ object VideoDownloadManager {
|
|||
|
||||
private val cachedBitmaps = hashMapOf<String, Bitmap>()
|
||||
private fun Context.getImageBitmapFromUrl(url: String): Bitmap? {
|
||||
if (cachedBitmaps.containsKey(url)) {
|
||||
return cachedBitmaps[url]
|
||||
}
|
||||
try {
|
||||
if (cachedBitmaps.containsKey(url)) {
|
||||
return cachedBitmaps[url]
|
||||
}
|
||||
|
||||
val bitmap = Glide.with(this)
|
||||
.asBitmap()
|
||||
.load(url).into(720, 720)
|
||||
.get()
|
||||
if (bitmap != null) {
|
||||
cachedBitmaps[url] = bitmap
|
||||
val bitmap = Glide.with(this)
|
||||
.asBitmap()
|
||||
.load(url).into(720, 720)
|
||||
.get()
|
||||
if (bitmap != null) {
|
||||
cachedBitmaps[url] = bitmap
|
||||
}
|
||||
return null
|
||||
} catch (e : Exception) {
|
||||
return null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createNotification(
|
||||
|
|
|
@ -8,11 +8,10 @@ import com.lagradost.cloudstream3.R
|
|||
import kotlin.math.max
|
||||
|
||||
class FlowLayout : ViewGroup {
|
||||
constructor(context: Context?) : super(context) {}
|
||||
constructor(context: Context?) : super(context)
|
||||
|
||||
@JvmOverloads
|
||||
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int = 0) : super(context, attrs, defStyleAttr) {
|
||||
}
|
||||
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int = 0) : super(context, attrs, defStyleAttr)
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
val realWidth = MeasureSpec.getSize(widthMeasureSpec)
|
||||
|
@ -92,7 +91,7 @@ class FlowLayout : ViewGroup {
|
|||
spacing = 0
|
||||
}
|
||||
|
||||
constructor(source: MarginLayoutParams?) : super(source) {}
|
||||
internal constructor(source: ViewGroup.LayoutParams?) : super(source) {}
|
||||
constructor(source: MarginLayoutParams?) : super(source)
|
||||
internal constructor(source: ViewGroup.LayoutParams?) : super(source)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue