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