mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	updated anime API for dub/sub
This commit is contained in:
		
							parent
							
								
									32a0658611
								
							
						
					
					
						commit
						2f9c76d9b5
					
				
					 13 changed files with 280 additions and 262 deletions
				
			
		|  | @ -255,7 +255,8 @@ fun sortSubs(urls: List<SubtitleFile>): List<SubtitleFile> { | |||
| 
 | ||||
| /** https://www.imdb.com/title/tt2861424/ -> tt2861424 */ | ||||
| fun imdbUrlToId(url: String): String? { | ||||
|     return Regex("/title/(tt[0-9]*)").find(url)?.groupValues?.get(1) ?: Regex("tt[0-9]{5,}").find(url)?.groupValues?.get(0) | ||||
|     return Regex("/title/(tt[0-9]*)").find(url)?.groupValues?.get(1) | ||||
|         ?: Regex("tt[0-9]{5,}").find(url)?.groupValues?.get(0) | ||||
| } | ||||
| 
 | ||||
| fun imdbUrlToIdNullable(url: String?): String? { | ||||
|  | @ -390,12 +391,12 @@ fun LoadResponse?.isAnimeBased(): Boolean { | |||
| 
 | ||||
| data class AnimeEpisode( | ||||
|     val url: String, | ||||
|     val name: String? = null, | ||||
|     val posterUrl: String? = null, | ||||
|     val date: String? = null, | ||||
|     val rating: Int? = null, | ||||
|     val descript: String? = null, | ||||
|     val episode: Int? = null, | ||||
|     var name: String? = null, | ||||
|     var posterUrl: String? = null, | ||||
|     var date: String? = null, | ||||
|     var rating: Int? = null, | ||||
|     var description: String? = null, | ||||
|     var episode: Int? = null, | ||||
| ) | ||||
| 
 | ||||
| data class TorrentLoadResponse( | ||||
|  | @ -416,32 +417,47 @@ data class TorrentLoadResponse( | |||
| ) : LoadResponse | ||||
| 
 | ||||
| data class AnimeLoadResponse( | ||||
|     val engName: String?, | ||||
|     val japName: String?, | ||||
|     var engName: String? = null, | ||||
|     var japName: String? = null, | ||||
|     override val name: String, | ||||
|     override val url: String, | ||||
|     override val apiName: String, | ||||
|     override val type: TvType, | ||||
| 
 | ||||
|     override val posterUrl: String?, | ||||
|     override val year: Int?, | ||||
|     override var posterUrl: String? = null, | ||||
|     override var year: Int? = null, | ||||
| 
 | ||||
|     val dubEpisodes: List<AnimeEpisode>?, | ||||
|     val subEpisodes: List<AnimeEpisode>?, | ||||
|     val showStatus: ShowStatus?, | ||||
|     var episodes: HashMap<DubStatus, List<AnimeEpisode>> = hashMapOf(), | ||||
|     var showStatus: ShowStatus? = null, | ||||
| 
 | ||||
|     override val plot: String?, | ||||
|     override val tags: List<String>? = null, | ||||
|     val synonyms: List<String>? = null, | ||||
|     override var plot: String? = null, | ||||
|     override var tags: List<String>? = null, | ||||
|     var synonyms: List<String>? = null, | ||||
| 
 | ||||
|     val malId: Int? = null, | ||||
|     val anilistId: Int? = null, | ||||
|     override val rating: Int? = null, | ||||
|     override val duration: String? = null, | ||||
|     override val trailerUrl: String? = null, | ||||
|     override val recommendations: List<SearchResponse>? = null, | ||||
|     var malId: Int? = null, | ||||
|     var anilistId: Int? = null, | ||||
|     override var rating: Int? = null, | ||||
|     override var duration: String? = null, | ||||
|     override var trailerUrl: String? = null, | ||||
|     override var recommendations: List<SearchResponse>? = null, | ||||
| ) : LoadResponse | ||||
| 
 | ||||
| fun AnimeLoadResponse.addEpisodes(status : DubStatus, episodes : List<AnimeEpisode>?) { | ||||
|     if(episodes == null) return | ||||
|     this.episodes[status] = episodes | ||||
| } | ||||
| 
 | ||||
| fun MainAPI.newAnimeLoadResponse( | ||||
|     name: String, | ||||
|     url: String, | ||||
|     type: TvType, | ||||
|     initializer: AnimeLoadResponse.() -> Unit = { } | ||||
| ): AnimeLoadResponse { | ||||
|     val builder = AnimeLoadResponse(name = name, url = url, apiName = this.name, type = type) | ||||
|     builder.initializer() | ||||
|     return builder | ||||
| } | ||||
| 
 | ||||
| data class MovieLoadResponse( | ||||
|     override val name: String, | ||||
|     override val url: String, | ||||
|  | @ -449,11 +465,11 @@ data class MovieLoadResponse( | |||
|     override val type: TvType, | ||||
|     val dataUrl: String, | ||||
| 
 | ||||
|     override val posterUrl: String?, | ||||
|     override val year: Int?, | ||||
|     override val plot: String?, | ||||
|     override val posterUrl: String? = null, | ||||
|     override val year: Int? = null, | ||||
|     override val plot: String? = null, | ||||
| 
 | ||||
|     val imdbId: String?, | ||||
|     val imdbId: String? = null, | ||||
|     override val rating: Int? = null, | ||||
|     override val tags: List<String>? = null, | ||||
|     override val duration: String? = null, | ||||
|  | @ -462,9 +478,9 @@ data class MovieLoadResponse( | |||
| ) : LoadResponse | ||||
| 
 | ||||
| data class TvSeriesEpisode( | ||||
|     val name: String?, | ||||
|     val season: Int?, | ||||
|     val episode: Int?, | ||||
|     val name: String? = null, | ||||
|     val season: Int? = null, | ||||
|     val episode: Int? = null, | ||||
|     val data: String, | ||||
|     val posterUrl: String? = null, | ||||
|     val date: String? = null, | ||||
|  | @ -479,12 +495,12 @@ data class TvSeriesLoadResponse( | |||
|     override val type: TvType, | ||||
|     val episodes: List<TvSeriesEpisode>, | ||||
| 
 | ||||
|     override val posterUrl: String?, | ||||
|     override val year: Int?, | ||||
|     override val plot: String?, | ||||
|     override val posterUrl: String? = null, | ||||
|     override val year: Int? = null, | ||||
|     override val plot: String? = null, | ||||
| 
 | ||||
|     val showStatus: ShowStatus?, | ||||
|     val imdbId: String?, | ||||
|     val showStatus: ShowStatus? = null, | ||||
|     val imdbId: String? = null, | ||||
|     override val rating: Int? = null, | ||||
|     override val tags: List<String>? = null, | ||||
|     override val duration: String? = null, | ||||
|  |  | |||
|  | @ -1,21 +1,19 @@ | |||
| package com.lagradost.cloudstream3.animeproviders | ||||
| 
 | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import com.lagradost.cloudstream3.network.get | ||||
| import com.lagradost.cloudstream3.network.text | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import org.jsoup.Jsoup | ||||
| import java.util.* | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.fasterxml.jackson.module.kotlin.readValue | ||||
| import kotlin.collections.ArrayList | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.network.get | ||||
| import com.lagradost.cloudstream3.network.text | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.M3u8Helper | ||||
| import com.lagradost.cloudstream3.utils.getQualityFromName | ||||
| import org.jsoup.Jsoup | ||||
| import org.mozilla.javascript.Context | ||||
| import org.mozilla.javascript.Scriptable | ||||
| import java.net.URI | ||||
| import java.net.URLDecoder | ||||
| import com.lagradost.cloudstream3.utils.getQualityFromName | ||||
| import java.util.* | ||||
| 
 | ||||
| 
 | ||||
| class AllAnimeProvider : MainAPI() { | ||||
|  | @ -27,7 +25,7 @@ class AllAnimeProvider : MainAPI() { | |||
|         get() = false | ||||
|     override val hasMainPage: Boolean | ||||
|         get() = false | ||||
|      | ||||
| 
 | ||||
|     private val hlsHelper = M3u8Helper() | ||||
| 
 | ||||
|     private fun getStatus(t: String): ShowStatus { | ||||
|  | @ -41,17 +39,17 @@ class AllAnimeProvider : MainAPI() { | |||
|     override val supportedTypes: Set<TvType> | ||||
|         get() = setOf(TvType.Anime, TvType.AnimeMovie) | ||||
| 
 | ||||
|     private data class Data ( | ||||
|     private data class Data( | ||||
|         @JsonProperty("shows") val shows: Shows | ||||
|     ) | ||||
| 
 | ||||
|     private data class Shows ( | ||||
|     private data class Shows( | ||||
|         @JsonProperty("pageInfo") val pageInfo: PageInfo, | ||||
|         @JsonProperty("edges") val edges: List<Edges>, | ||||
|         @JsonProperty("__typename") val _typename: String | ||||
|     ) | ||||
| 
 | ||||
|     private data class Edges ( | ||||
|     private data class Edges( | ||||
|         @JsonProperty("_id") val Id: String?, | ||||
|         @JsonProperty("name") val name: String, | ||||
|         @JsonProperty("englishName") val englishName: String?, | ||||
|  | @ -68,34 +66,35 @@ class AllAnimeProvider : MainAPI() { | |||
|         @JsonProperty("status") val status: String?, | ||||
|     ) | ||||
| 
 | ||||
|     private data class AvailableEpisodes ( | ||||
|     private data class AvailableEpisodes( | ||||
|         @JsonProperty("sub") val sub: Int, | ||||
|         @JsonProperty("dub") val dub: Int, | ||||
|         @JsonProperty("raw") val raw: Int | ||||
|     ) | ||||
| 
 | ||||
|     private data class AiredStart ( | ||||
|     private data class AiredStart( | ||||
|         @JsonProperty("year") val year: Int, | ||||
|         @JsonProperty("month") val month: Int, | ||||
|         @JsonProperty("date") val date: Int | ||||
|     ) | ||||
| 
 | ||||
|     private data class Season ( | ||||
|     private data class Season( | ||||
|         @JsonProperty("quarter") val quarter: String, | ||||
|         @JsonProperty("year") val year: Int | ||||
|     ) | ||||
| 
 | ||||
|     private data class PageInfo ( | ||||
|     private data class PageInfo( | ||||
|         @JsonProperty("total") val total: Int, | ||||
|         @JsonProperty("__typename") val _typename: String | ||||
|     ) | ||||
| 
 | ||||
|     private data class AllAnimeQuery ( | ||||
|     private data class AllAnimeQuery( | ||||
|         @JsonProperty("data") val data: Data | ||||
|     ) | ||||
| 
 | ||||
|     override fun search(query: String): ArrayList<SearchResponse> { | ||||
|         val link = """$mainUrl/graphql?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Afalse%2C%22query%22%3A%22$query%22%7D%2C%22limit%22%3A26%2C%22page%22%3A1%2C%22translationType%22%3A%22sub%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229343797cc3d9e3f444e2d3b7db9a84d759b816a4d84512ea72d079f85bb96e98%22%7D%7D""" | ||||
|         val link = | ||||
|             """$mainUrl/graphql?variables=%7B%22search%22%3A%7B%22allowAdult%22%3Afalse%2C%22query%22%3A%22$query%22%7D%2C%22limit%22%3A26%2C%22page%22%3A1%2C%22translationType%22%3A%22sub%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%229343797cc3d9e3f444e2d3b7db9a84d759b816a4d84512ea72d079f85bb96e98%22%7D%7D""" | ||||
|         var res = get(link).text | ||||
|         if (res.contains("PERSISTED_QUERY_NOT_FOUND")) { | ||||
|             res = get(link).text | ||||
|  | @ -116,15 +115,15 @@ class AllAnimeProvider : MainAPI() { | |||
|                 TvType.Anime, | ||||
|                 it.thumbnail, | ||||
|                 it.airedStart?.year, | ||||
|                 EnumSet.of(DubStatus.Subbed),  //, DubStatus.Dubbed), | ||||
|                 EnumSet.of(DubStatus.Subbed, DubStatus.Dubbed), | ||||
|                 it.englishName, | ||||
|                 null, //it.availableEpisodes?.dub, | ||||
|                 it.availableEpisodes?.dub, | ||||
|                 it.availableEpisodes?.sub | ||||
|             ) | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     private data class AvailableEpisodesDetail ( | ||||
|     private data class AvailableEpisodesDetail( | ||||
|         @JsonProperty("sub") val sub: List<String>, | ||||
|         @JsonProperty("dub") val dub: List<String>, | ||||
|         @JsonProperty("raw") val raw: List<String> | ||||
|  | @ -137,7 +136,6 @@ class AllAnimeProvider : MainAPI() { | |||
|         rhino.optimizationLevel = -1 | ||||
|         val scope: Scriptable = rhino.initStandardObjects() | ||||
| 
 | ||||
| 
 | ||||
|         val html = get(url).text | ||||
|         val soup = Jsoup.parse(html) | ||||
| 
 | ||||
|  | @ -161,43 +159,28 @@ class AllAnimeProvider : MainAPI() { | |||
| 
 | ||||
|         val episodes = showData.availableEpisodes.let { | ||||
|             if (it == null) return@let Pair(null, null) | ||||
|             Pair(if (it.sub != 0) ArrayList((1 .. it.sub).map { epNum -> | ||||
|             Pair(if (it.sub != 0) ArrayList((1..it.sub).map { epNum -> | ||||
|                 AnimeEpisode( | ||||
|                     "$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", | ||||
|                     null, | ||||
|                     null, | ||||
|                     null, | ||||
|                     null, | ||||
|                     null, | ||||
|                     epNum | ||||
|                     "$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum | ||||
|                 ) | ||||
|             }) else null, if (it.dub != 0) ArrayList((1 .. it.dub).map { epNum -> | ||||
|             }) else null, if (it.dub != 0) ArrayList((1..it.dub).map { epNum -> | ||||
|                 AnimeEpisode( | ||||
|                     "$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", | ||||
|                     null, | ||||
|                     null, | ||||
|                     null, | ||||
|                     null, | ||||
|                     null, | ||||
|                     epNum | ||||
|                     "$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum | ||||
|                 ) | ||||
|             }) else null) | ||||
|         } | ||||
| 
 | ||||
|         return AnimeLoadResponse( | ||||
|             null, | ||||
|             null, | ||||
|             title, | ||||
|             url, | ||||
|             this.name, | ||||
|             TvType.Anime, | ||||
|             poster, | ||||
|             showData.airedStart?.year, | ||||
|             null, // no dub, because there is no way to switch from dub to sub //episodes.second, | ||||
|             episodes.first, | ||||
|             getStatus(showData.status.toString()), | ||||
|             description?.replace(Regex("""\<(.*?)\>"""), "") | ||||
|         ) | ||||
|         return newAnimeLoadResponse(title, url, TvType.Anime) { | ||||
|             posterUrl = poster | ||||
|             year = showData.airedStart?.year | ||||
| 
 | ||||
|             addEpisodes(DubStatus.Subbed, episodes.first) | ||||
|             addEpisodes(DubStatus.Dubbed, episodes.second) | ||||
| 
 | ||||
|             showStatus = getStatus(showData.status.toString()) | ||||
| 
 | ||||
|             plot = description?.replace(Regex("""<(.*?)>"""), "") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private val embedBlackList = listOf( | ||||
|  | @ -216,7 +199,7 @@ class AllAnimeProvider : MainAPI() { | |||
|                     return true | ||||
|                 } | ||||
|             } else { | ||||
|                 if (url.contains(it as String)) { | ||||
|                 if (url.contains(it)) { | ||||
|                     return true | ||||
|                 } | ||||
|             } | ||||
|  | @ -232,21 +215,21 @@ class AllAnimeProvider : MainAPI() { | |||
|         return out | ||||
|     } | ||||
| 
 | ||||
|     private data class Links ( | ||||
|     private data class Links( | ||||
|         @JsonProperty("link") val link: String, | ||||
|         @JsonProperty("hls") val hls: Boolean?, | ||||
|         @JsonProperty("resolutionStr") val resolutionStr: String, | ||||
|         @JsonProperty("src") val src: String? | ||||
|     ) | ||||
| 
 | ||||
|     private data class AllAnimeVideoApiResponse ( | ||||
|     private data class AllAnimeVideoApiResponse( | ||||
|         @JsonProperty("links") val links: List<Links> | ||||
|     ) | ||||
| 
 | ||||
|     private data class ApiEndPoint( | ||||
|         @JsonProperty("episodeIframeHead") val episodeIframeHead: String | ||||
|     ) | ||||
|      | ||||
| 
 | ||||
|     private fun getM3u8Qualities(m3u8Link: String, referer: String, qualityName: String): ArrayList<ExtractorLink> { | ||||
|         return ArrayList(hlsHelper.m3u8Generation(M3u8Helper.M3u8Stream(m3u8Link, null), true).map { stream -> | ||||
|             val qualityString = if ((stream.quality ?: 0) == 0) "" else "${stream.quality}p" | ||||
|  | @ -273,7 +256,8 @@ class AllAnimeProvider : MainAPI() { | |||
| 
 | ||||
|         val html = get(data).text | ||||
| 
 | ||||
|         val sources = Regex("""sourceUrl[:=]"(.+?)"""").findAll(html).toList().map { URLDecoder.decode(it.destructured.component1().sanitize(), "UTF-8") } | ||||
|         val sources = Regex("""sourceUrl[:=]"(.+?)"""").findAll(html).toList() | ||||
|             .map { URLDecoder.decode(it.destructured.component1().sanitize(), "UTF-8") } | ||||
|         sources.forEach { | ||||
|             var link = it | ||||
|             if (URI(link).isAbsolute || link.startsWith("//")) { | ||||
|  | @ -305,16 +289,26 @@ class AllAnimeProvider : MainAPI() { | |||
|                     val links = mapper.readValue<AllAnimeVideoApiResponse>(response.text).links | ||||
|                     links.forEach { server -> | ||||
|                         if (server.hls != null && server.hls) { | ||||
|                             getM3u8Qualities(server.link, "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(server.link).path), server.resolutionStr).forEach(callback) | ||||
|                         } else { | ||||
|                             callback(ExtractorLink( | ||||
|                                 "AllAnime - " + URI(server.link).host, | ||||
|                                 server.resolutionStr, | ||||
|                             getM3u8Qualities( | ||||
|                                 server.link, | ||||
|                                 "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(server.link).path), | ||||
|                                 getQualityFromName("1080"), | ||||
|                                 false | ||||
|                             )) | ||||
|                                 "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI( | ||||
|                                     server.link | ||||
|                                 ).path), | ||||
|                                 server.resolutionStr | ||||
|                             ).forEach(callback) | ||||
|                         } else { | ||||
|                             callback( | ||||
|                                 ExtractorLink( | ||||
|                                     "AllAnime - " + URI(server.link).host, | ||||
|                                     server.resolutionStr, | ||||
|                                     server.link, | ||||
|                                     "$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI( | ||||
|                                         server.link | ||||
|                                     ).path), | ||||
|                                     getQualityFromName("1080"), | ||||
|                                     false | ||||
|                                 ) | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  |  | |||
|  | @ -76,21 +76,15 @@ class AnimeFlickProvider : MainAPI() { | |||
|             AnimeEpisode(link, name) | ||||
|         }.reversed() | ||||
| 
 | ||||
|         return AnimeLoadResponse( | ||||
|             title, | ||||
|             null, | ||||
|             title, | ||||
|             url, | ||||
|             this.name, | ||||
|             getType(title), | ||||
|             poster, | ||||
|             year, | ||||
|             null, | ||||
|             episodes, | ||||
|             null, | ||||
|             description, | ||||
|             genres | ||||
|         ) | ||||
|         return newAnimeLoadResponse(title, url, getType(title)) { | ||||
|             posterUrl = poster | ||||
|             this.year = year | ||||
| 
 | ||||
|             addEpisodes(DubStatus.Subbed, episodes) | ||||
| 
 | ||||
|             plot = description | ||||
|             tags = genres | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun loadLinks( | ||||
|  |  | |||
|  | @ -293,30 +293,26 @@ class AnimePaheProvider : MainAPI() { | |||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             AnimeLoadResponse( | ||||
|                 animeTitle, | ||||
|                 japTitle, | ||||
|                 animeTitle, | ||||
|                 url, | ||||
|                 this.name, | ||||
|                 getType(tvType.toString()), | ||||
|                 poster, | ||||
|                 year, | ||||
|                 null, | ||||
|                 episodes, | ||||
|                 status, | ||||
|                 synopsis, | ||||
|                 if (!doc.select(".anime-genre > ul a").isEmpty()) { | ||||
|             newAnimeLoadResponse(animeTitle, url, getType(tvType.toString())) { | ||||
|                 engName = animeTitle | ||||
|                 japName = japTitle | ||||
| 
 | ||||
|                 this.posterUrl = poster | ||||
|                 this.year = year | ||||
| 
 | ||||
|                 addEpisodes(DubStatus.Subbed, episodes) | ||||
|                 this.showStatus = status | ||||
|                 plot = synopsis | ||||
|                 tags = if (!doc.select(".anime-genre > ul a").isEmpty()) { | ||||
|                     ArrayList(doc.select(".anime-genre > ul a").map { it.text().toString() }) | ||||
|                 } else { | ||||
|                     null | ||||
|                 }, | ||||
|                 ArrayList(), | ||||
|                 malId, | ||||
|                 anilistId, | ||||
|                 null, | ||||
|                 trailer | ||||
|             ) | ||||
|                 } | ||||
| 
 | ||||
|                 this.malId = malId | ||||
|                 this.anilistId = anilistId | ||||
|                 this.trailerUrl = trailer | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -325,7 +321,6 @@ class AnimePaheProvider : MainAPI() { | |||
|         return s?.toIntOrNull() != null | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     private fun cookieStrToMap(cookie: String): Map<String, String> { | ||||
|         val cookies = mutableMapOf<String, String>() | ||||
|         for (string in cookie.split("; ")) { | ||||
|  | @ -347,7 +342,6 @@ class AnimePaheProvider : MainAPI() { | |||
|         val slice2 = characterMap.slice(0 until s2) | ||||
|         var acc: Long = 0 | ||||
| 
 | ||||
| 
 | ||||
|         for ((n, i) in content.reversed().withIndex()) { | ||||
|             acc += (when (isNumber("$i")) { | ||||
|                 true -> "$i".toLong() | ||||
|  |  | |||
|  | @ -109,7 +109,7 @@ class DubbedAnimeProvider : MainAPI() { | |||
|             HomePageList("Trending", parseDocumentTrending(trendingUrl)), | ||||
|             HomePageList("Recently Added", parseDocument(recentlyAddedUrl)), | ||||
|             HomePageList("Recent Releases", parseDocument(lastEpisodeUrl, true)), | ||||
|            // HomePageList("All", parseDocument(allUrl)) | ||||
|             // HomePageList("All", parseDocument(allUrl)) | ||||
|         ) | ||||
| 
 | ||||
|         return HomePageResponse(listItems) | ||||
|  | @ -268,20 +268,12 @@ class DubbedAnimeProvider : MainAPI() { | |||
|             } | ||||
| 
 | ||||
|             val img = fixUrl(document.select("div.fkimgs > img").attr("src")) | ||||
|             return AnimeLoadResponse( | ||||
|                 null, | ||||
|                 null, | ||||
|                 title, | ||||
|                 url, | ||||
|                 this.name, | ||||
|                 TvType.Anime, | ||||
|                 img, | ||||
|                 year, | ||||
|                 ArrayList(episodes), | ||||
|                 null, | ||||
|                 null, | ||||
|                 descript, | ||||
|             ) | ||||
|             return newAnimeLoadResponse(title, url, TvType.Anime) { | ||||
|                 posterUrl = img | ||||
|                 this.year = year | ||||
|                 addEpisodes(DubStatus.Dubbed, episodes) | ||||
|                 plot = descript | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -185,24 +185,18 @@ class GogoanimeProvider : MainAPI() { | |||
|                 "Episode " + it.selectFirst(".name").text().replace("EP", "").trim() | ||||
|             ) | ||||
|         }.reversed() | ||||
|         return AnimeLoadResponse( | ||||
|             title, | ||||
|             nativeName, | ||||
|             title, | ||||
|             link, | ||||
|             this.name, | ||||
|             getType(type.toString()), | ||||
|             poster, | ||||
|             year, | ||||
|             null, | ||||
|             episodes, | ||||
|             getStatus(status.toString()), | ||||
|             description, | ||||
|             ArrayList(genre), | ||||
|             null, | ||||
|             null, | ||||
|             null, | ||||
|         ) | ||||
| 
 | ||||
|         return newAnimeLoadResponse(title, link, getType(type.toString())) { | ||||
|             japName = nativeName | ||||
|             engName = title | ||||
|             posterUrl = poster | ||||
|             this.year = year | ||||
|             addEpisodes(DubStatus.Subbed, episodes) // TODO CHECK | ||||
|             plot = description | ||||
|             tags = genre | ||||
| 
 | ||||
|             showStatus = getStatus(status.toString()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun extractVideos(uri: String): List<ExtractorLink> { | ||||
|  |  | |||
|  | @ -109,23 +109,13 @@ class KawaiifuProvider : MainAPI() { | |||
|         } | ||||
|         val poster = soup.selectFirst("a.thumb > img").attr("src") | ||||
| 
 | ||||
| 
 | ||||
|         return AnimeLoadResponse( | ||||
|             title, | ||||
|             null, | ||||
|             title, | ||||
|             url, | ||||
|             this.name, | ||||
|             TvType.Anime, | ||||
|             poster, | ||||
|             year, | ||||
|             null, | ||||
|             episodes, | ||||
|             ShowStatus.Ongoing, | ||||
|             description, | ||||
|             ArrayList(tags), | ||||
|             ArrayList() | ||||
|         ) | ||||
|         return newAnimeLoadResponse(title, url, TvType.Anime) { | ||||
|             this.year = year | ||||
|             posterUrl = poster | ||||
|             addEpisodes(DubStatus.Subbed, episodes) | ||||
|             plot = description | ||||
|             this.tags = tags | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun loadLinks( | ||||
|  |  | |||
|  | @ -268,24 +268,19 @@ class TenshiProvider : MainAPI() { | |||
|         val synonyms = | ||||
|             document.select("li.synonym.meta-data > div.info-box > span.value").map { it?.text()?.trim().toString() } | ||||
| 
 | ||||
|         return AnimeLoadResponse( | ||||
|             englishTitle, | ||||
|             japaneseTitle, | ||||
|             canonicalTitle, | ||||
|             url, | ||||
|             this.name, | ||||
|             getType(type ?: ""), | ||||
|             poster, | ||||
|             year.toIntOrNull(), | ||||
|             null, | ||||
|             episodes, | ||||
|             status, | ||||
|             synopsis, | ||||
|             ArrayList(genre), | ||||
|             ArrayList(synonyms), | ||||
|             null, | ||||
|             null, | ||||
|         ) | ||||
|         return newAnimeLoadResponse(canonicalTitle,url,getType(type ?: "")) { | ||||
|             engName = englishTitle | ||||
|             japName = japaneseTitle | ||||
| 
 | ||||
|             posterUrl = poster | ||||
|             this.year = year.toIntOrNull() | ||||
| 
 | ||||
|             addEpisodes(DubStatus.Subbed,episodes) | ||||
|             showStatus = status | ||||
|             tags = genre | ||||
|             this.synonyms = synonyms | ||||
|             plot = synopsis | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -208,22 +208,16 @@ class WcoProvider : MainAPI() { | |||
|         val genre = document.select("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(5) > a") | ||||
|             .map { it?.text()?.trim().toString() } | ||||
| 
 | ||||
|         return AnimeLoadResponse( | ||||
|             canonicalTitle, | ||||
|             japaneseTitle, | ||||
|             canonicalTitle, | ||||
|             url, | ||||
|             this.name, | ||||
|             getType(type ?: ""), | ||||
|             poster, | ||||
|             year, | ||||
|             if (isDubbed) episodes else null, | ||||
|             if (!isDubbed) episodes else null, | ||||
|             status, | ||||
|             synopsis, | ||||
|             ArrayList(genre), | ||||
|             ArrayList(), | ||||
|         ) | ||||
|         return newAnimeLoadResponse(canonicalTitle,url,getType(type ?: "")) { | ||||
|             japName = japaneseTitle | ||||
|             engName = canonicalTitle | ||||
|             posterUrl = poster | ||||
|             this.year = year | ||||
|             addEpisodes(if(isDubbed) DubStatus.Dubbed else DubStatus.Subbed,episodes) | ||||
|             showStatus = status | ||||
|             plot = synopsis | ||||
|             tags = genre | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun loadLinks( | ||||
|  |  | |||
|  | @ -230,21 +230,17 @@ class ZoroProvider : MainAPI() { | |||
|                 it.selectFirst(".ssli-order")?.text()?.toIntOrNull() | ||||
|             ) | ||||
|         } | ||||
|         return AnimeLoadResponse( | ||||
|             title, | ||||
|             japaneseTitle, | ||||
|             title, | ||||
|             url, | ||||
|             this.name, | ||||
|             TvType.Anime, | ||||
|             poster, | ||||
|             year, | ||||
|             null, | ||||
|             episodes, | ||||
|             status, | ||||
|             description, | ||||
|             tags, | ||||
|         ) | ||||
| 
 | ||||
|         return newAnimeLoadResponse(title, url, TvType.Anime) { | ||||
|             japName = japaneseTitle | ||||
|             engName = title | ||||
|             posterUrl = poster | ||||
|             this.year = year | ||||
|             addEpisodes(DubStatus.Subbed, episodes) | ||||
|             showStatus = status | ||||
|             plot = description | ||||
|             this.tags = tags | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun getM3u8FromRapidCloud(url: String): String { | ||||
|  |  | |||
|  | @ -235,7 +235,7 @@ class ResultFragment : Fragment() { | |||
|     private var currentId: Int? = null | ||||
|     private var currentIsMovie: Boolean? = null | ||||
|     private var episodeRanges: List<String>? = null | ||||
| 
 | ||||
|     private var dubRange: Set<DubStatus>? = null | ||||
|     var url: String? = null | ||||
| 
 | ||||
|     private fun fromIndexToSeasonText(selection: Int?): String { | ||||
|  | @ -681,7 +681,6 @@ class ResultFragment : Fragment() { | |||
|                         } | ||||
|                         outputFile.writeText(text) | ||||
| 
 | ||||
| 
 | ||||
|                         val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT) | ||||
| 
 | ||||
|                         vlcIntent.setPackage(VLC_PACKAGE) | ||||
|  | @ -858,6 +857,25 @@ class ResultFragment : Fragment() { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         observe(viewModel.dubStatus) { status -> | ||||
|             result_dub_select?.text = status.toString() | ||||
|         } | ||||
| 
 | ||||
|         observe(viewModel.dubSubSelections) { range -> | ||||
|             dubRange = range | ||||
|             result_dub_select?.visibility = if (range.size <= 1) GONE else VISIBLE | ||||
|         } | ||||
| 
 | ||||
|         result_dub_select.setOnClickListener { | ||||
|             val ranges = dubRange | ||||
|             if (ranges != null) { | ||||
|                 it.popupMenuNoIconsAndNoStringRes(ranges.map { status -> Pair(status.ordinal, status.toString()) } | ||||
|                     .toList()) { | ||||
|                     viewModel.changeDubStatus(requireContext(), DubStatus.values()[itemId]) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         observe(viewModel.selectedRange) { range -> | ||||
|             result_episode_select?.text = range | ||||
|         } | ||||
|  | @ -962,8 +980,10 @@ class ResultFragment : Fragment() { | |||
|                             if (metadataInfoArray.size > 0) { | ||||
|                                 result_metadata.visibility = VISIBLE | ||||
|                                 val text = SpannableStringBuilder() | ||||
|                                 val grayColor = ctx.colorFromAttribute(R.attr.grayTextColor) //ContextCompat.getColor(requireContext(), R.color.grayTextColor) | ||||
|                                 val textColor = ctx.colorFromAttribute(R.attr.textColor) //ContextCompat.getColor(requireContext(), R.color.textColor) | ||||
|                                 val grayColor = | ||||
|                                     ctx.colorFromAttribute(R.attr.grayTextColor) //ContextCompat.getColor(requireContext(), R.color.grayTextColor) | ||||
|                                 val textColor = | ||||
|                                     ctx.colorFromAttribute(R.attr.textColor) //ContextCompat.getColor(requireContext(), R.color.textColor) | ||||
|                                 for (meta in metadataInfoArray) { | ||||
|                                     text.color(grayColor) { append(getString(meta.first) + ": ") } | ||||
|                                         .color(textColor) { append("${meta.second}\n") } | ||||
|  |  | |||
|  | @ -55,13 +55,20 @@ class ResultViewModel : ViewModel() { | |||
|     val publicEpisodes: LiveData<Resource<List<ResultEpisode>>> get() = _publicEpisodes | ||||
|     val publicEpisodesCount: LiveData<Int> get() = _publicEpisodesCount | ||||
| 
 | ||||
|     private val dubStatus: MutableLiveData<DubStatus> = MutableLiveData() | ||||
|     val dubStatus: MutableLiveData<DubStatus> get() = _dubStatus | ||||
|     private val _dubStatus: MutableLiveData<DubStatus> = MutableLiveData() | ||||
| 
 | ||||
|     private val page: MutableLiveData<LoadResponse> = MutableLiveData() | ||||
|     val id: MutableLiveData<Int> = MutableLiveData() | ||||
|     val selectedSeason: MutableLiveData<Int> = MutableLiveData(-2) | ||||
|     val seasonSelections: MutableLiveData<List<Int?>> = MutableLiveData() | ||||
| 
 | ||||
|     val dubSubSelections: MutableLiveData<Set<DubStatus>> get() = _dubSubSelections | ||||
|     private val _dubSubSelections: MutableLiveData<Set<DubStatus>> = MutableLiveData() | ||||
| 
 | ||||
|     val dubSubEpisodes: MutableLiveData<Map<DubStatus, List<ResultEpisode>>?> get() = _dubSubEpisodes | ||||
|     private val _dubSubEpisodes: MutableLiveData<Map<DubStatus, List<ResultEpisode>>?> = MutableLiveData() | ||||
| 
 | ||||
|     private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData() | ||||
|     val watchStatus: LiveData<WatchType> get() = _watchStatus | ||||
| 
 | ||||
|  | @ -110,7 +117,7 @@ class ResultViewModel : ViewModel() { | |||
|         val seasons = seasonTypes.toList().map { it.first }.sortedBy { it } | ||||
|         seasonSelections.postValue(seasons) | ||||
|         if (seasons.isEmpty()) { // WHAT THE FUCK DID YOU DO????? HOW DID YOU DO THIS | ||||
|             _publicEpisodes.postValue(Resource.Success( ArrayList())) | ||||
|             _publicEpisodes.postValue(Resource.Success(ArrayList())) | ||||
|             return | ||||
|         } | ||||
| 
 | ||||
|  | @ -160,7 +167,7 @@ class ResultViewModel : ViewModel() { | |||
|             selectedRange.postValue(allRange) | ||||
|         } | ||||
| 
 | ||||
|         _publicEpisodes.postValue(Resource.Success( currentList)) | ||||
|         _publicEpisodes.postValue(Resource.Success(currentList)) | ||||
|     } | ||||
| 
 | ||||
|     fun changeSeason(context: Context, selection: Int?) { | ||||
|  | @ -171,6 +178,13 @@ class ResultViewModel : ViewModel() { | |||
|         filterEpisodes(context, _episodes.value, null, range) | ||||
|     } | ||||
| 
 | ||||
|     fun changeDubStatus(context: Context, status: DubStatus?) { | ||||
|         dubSubEpisodes.value?.get(status)?.let { episodes -> | ||||
|             dubStatus.postValue(status) | ||||
|             updateEpisodes(context, null, episodes, null) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun updateEpisodes(context: Context, localId: Int?, list: List<ResultEpisode>, selection: Int?) { | ||||
|         _episodes.postValue(list) | ||||
|         val set = HashMap<Int, Int>() | ||||
|  | @ -237,7 +251,7 @@ class ResultViewModel : ViewModel() { | |||
|         return name | ||||
|     } | ||||
| 
 | ||||
|     fun load(context: Context, url: String, apiName: String, showFillers : Boolean) = viewModelScope.launch { | ||||
|     fun load(context: Context, url: String, apiName: String, showFillers: Boolean) = viewModelScope.launch { | ||||
|         _resultResponse.postValue(Resource.Loading(url)) | ||||
|         _publicEpisodes.postValue(Resource.Loading()) | ||||
| 
 | ||||
|  | @ -273,16 +287,22 @@ class ResultViewModel : ViewModel() { | |||
| 
 | ||||
|                 when (d) { | ||||
|                     is AnimeLoadResponse -> { | ||||
|                         val isDub = d.dubEpisodes != null && d.dubEpisodes.isNotEmpty() | ||||
|                         dubStatus.postValue(if (isDub) DubStatus.Dubbed else DubStatus.Subbed) | ||||
|                         //TODO context.getKey<>() isdub | ||||
| 
 | ||||
|                         val dataList = (if (isDub) d.dubEpisodes else d.subEpisodes) | ||||
|                         val isDub = | ||||
|                             d.episodes.containsKey(DubStatus.Dubbed) && !d.episodes[DubStatus.Dubbed].isNullOrEmpty() | ||||
|                         val dubStatus = if (isDub) DubStatus.Dubbed else DubStatus.Subbed | ||||
|                         _dubStatus.postValue(dubStatus) | ||||
| 
 | ||||
|                         val fillerEpisodes = if(showFillers) safeApiCall { getFillerEpisodes(d.name) } else null | ||||
|                         _dubSubSelections.postValue(d.episodes.keys) | ||||
|                         val fillerEpisodes = if (showFillers) safeApiCall { getFillerEpisodes(d.name) } else null | ||||
| 
 | ||||
|                         if (dataList != null) { // TODO dub and sub at the same time | ||||
|                         var idIndex = mainId | ||||
|                         val res = d.episodes.map { ep -> | ||||
|                             val episodes = ArrayList<ResultEpisode>() | ||||
|                             for ((index, i) in dataList.withIndex()) { | ||||
|                             for ((index, i) in ep.value.withIndex()) { | ||||
|                                 idIndex++ | ||||
| 
 | ||||
|                                 val episode = i.episode ?: (index + 1) | ||||
|                                 episodes.add( | ||||
|                                     context.buildResultEpisode( | ||||
|  | @ -292,17 +312,22 @@ class ResultViewModel : ViewModel() { | |||
|                                         null, // TODO FIX SEASON | ||||
|                                         i.url, | ||||
|                                         apiName, | ||||
|                                         (mainId + index + 1), | ||||
|                                         idIndex, | ||||
|                                         index, | ||||
|                                         i.rating, | ||||
|                                         i.descript, | ||||
|                                         i.description, | ||||
|                                         if (fillerEpisodes is Resource.Success) fillerEpisodes.value?.let { | ||||
|                                             it.contains(episode) && it[episode] == true | ||||
|                                         } | ||||
|                                             ?: false else false, | ||||
|                                         } ?: false else false, | ||||
|                                     ) | ||||
|                                 ) | ||||
|                             } | ||||
| 
 | ||||
|                             Pair(ep.key, episodes) | ||||
|                         }.toMap() | ||||
| 
 | ||||
|                         _dubSubEpisodes.postValue(res) | ||||
|                         res[dubStatus]?.let { episodes -> | ||||
|                             updateEpisodes(context, mainId, episodes, -1) | ||||
|                         } | ||||
|                     } | ||||
|  | @ -366,11 +391,10 @@ class ResultViewModel : ViewModel() { | |||
|                             ), -1 | ||||
|                         ) | ||||
|                     } | ||||
| 
 | ||||
|                 } | ||||
|             } | ||||
|             else -> { | ||||
| 
 | ||||
|                 // nothing | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -431,8 +431,23 @@ | |||
|                             android:layout_gravity="center_vertical" | ||||
|                             android:layout_marginStart="0dp" | ||||
|                             style="@style/MultiSelectButton" | ||||
|                     > | ||||
|                     </com.google.android.material.button.MaterialButton> | ||||
|                     /> | ||||
| 
 | ||||
|                     <com.google.android.material.button.MaterialButton | ||||
|                             tools:visibility="visible" | ||||
|                             tools:text="Dubbed" | ||||
| 
 | ||||
|                             android:nextFocusUp="@id/result_descript" | ||||
|                             android:nextFocusRight="@id/result_season_button" | ||||
|                             android:nextFocusLeft="@id/result_season_button" | ||||
|                             android:nextFocusDown="@id/result_episodes" | ||||
| 
 | ||||
|                             android:id="@+id/result_dub_select" | ||||
|                             android:visibility="gone" | ||||
|                             android:layout_gravity="center_vertical" | ||||
|                             android:layout_marginStart="0dp" | ||||
|                             style="@style/MultiSelectButton" | ||||
|                     /> | ||||
|                     <TextView | ||||
|                             android:layout_width="wrap_content" | ||||
|                             android:layout_height="wrap_content" | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue