small cleanup

This commit is contained in:
LagradOst 2021-08-19 22:05:18 +02:00
parent 1da670af80
commit 29d5f3848a
26 changed files with 215 additions and 542 deletions

View file

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

View file

@ -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 {

View file

@ -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)

View file

@ -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,

View file

@ -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)
}
}
}

View file

@ -217,7 +217,7 @@ class TenshiProvider : MainAPI() {
canonicalTitle,
url,
this.name,
TenshiProvider.getType(type ?: ""),
getType(type ?: ""),
poster,
year.toIntOrNull(),
null,

View file

@ -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)

View file

@ -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(

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)
}
}

View file

@ -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)

View file

@ -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) {

View file

@ -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))

View file

@ -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

View file

@ -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()
}
}
}
}

View file

@ -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
}

View file

@ -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) {

View file

@ -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))

View file

@ -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)
}
}

View file

@ -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

View file

@ -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(

View file

@ -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)
}
}