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