forked from recloudstream/cloudstream
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 */
|
/** https://www.imdb.com/title/tt2861424/ -> tt2861424 */
|
||||||
fun imdbUrlToId(url: String): String? {
|
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? {
|
fun imdbUrlToIdNullable(url: String?): String? {
|
||||||
|
@ -390,12 +391,12 @@ fun LoadResponse?.isAnimeBased(): Boolean {
|
||||||
|
|
||||||
data class AnimeEpisode(
|
data class AnimeEpisode(
|
||||||
val url: String,
|
val url: String,
|
||||||
val name: String? = null,
|
var name: String? = null,
|
||||||
val posterUrl: String? = null,
|
var posterUrl: String? = null,
|
||||||
val date: String? = null,
|
var date: String? = null,
|
||||||
val rating: Int? = null,
|
var rating: Int? = null,
|
||||||
val descript: String? = null,
|
var description: String? = null,
|
||||||
val episode: Int? = null,
|
var episode: Int? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class TorrentLoadResponse(
|
data class TorrentLoadResponse(
|
||||||
|
@ -416,32 +417,47 @@ data class TorrentLoadResponse(
|
||||||
) : LoadResponse
|
) : LoadResponse
|
||||||
|
|
||||||
data class AnimeLoadResponse(
|
data class AnimeLoadResponse(
|
||||||
val engName: String?,
|
var engName: String? = null,
|
||||||
val japName: String?,
|
var japName: String? = null,
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val url: String,
|
override val url: String,
|
||||||
override val apiName: String,
|
override val apiName: String,
|
||||||
override val type: TvType,
|
override val type: TvType,
|
||||||
|
|
||||||
override val posterUrl: String?,
|
override var posterUrl: String? = null,
|
||||||
override val year: Int?,
|
override var year: Int? = null,
|
||||||
|
|
||||||
val dubEpisodes: List<AnimeEpisode>?,
|
var episodes: HashMap<DubStatus, List<AnimeEpisode>> = hashMapOf(),
|
||||||
val subEpisodes: List<AnimeEpisode>?,
|
var showStatus: ShowStatus? = null,
|
||||||
val showStatus: ShowStatus?,
|
|
||||||
|
|
||||||
override val plot: String?,
|
override var plot: String? = null,
|
||||||
override val tags: List<String>? = null,
|
override var tags: List<String>? = null,
|
||||||
val synonyms: List<String>? = null,
|
var synonyms: List<String>? = null,
|
||||||
|
|
||||||
val malId: Int? = null,
|
var malId: Int? = null,
|
||||||
val anilistId: Int? = null,
|
var anilistId: Int? = null,
|
||||||
override val rating: Int? = null,
|
override var rating: Int? = null,
|
||||||
override val duration: String? = null,
|
override var duration: String? = null,
|
||||||
override val trailerUrl: String? = null,
|
override var trailerUrl: String? = null,
|
||||||
override val recommendations: List<SearchResponse>? = null,
|
override var recommendations: List<SearchResponse>? = null,
|
||||||
) : LoadResponse
|
) : 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(
|
data class MovieLoadResponse(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val url: String,
|
override val url: String,
|
||||||
|
@ -449,11 +465,11 @@ data class MovieLoadResponse(
|
||||||
override val type: TvType,
|
override val type: TvType,
|
||||||
val dataUrl: String,
|
val dataUrl: String,
|
||||||
|
|
||||||
override val posterUrl: String?,
|
override val posterUrl: String? = null,
|
||||||
override val year: Int?,
|
override val year: Int? = null,
|
||||||
override val plot: String?,
|
override val plot: String? = null,
|
||||||
|
|
||||||
val imdbId: String?,
|
val imdbId: String? = null,
|
||||||
override val rating: Int? = null,
|
override val rating: Int? = null,
|
||||||
override val tags: List<String>? = null,
|
override val tags: List<String>? = null,
|
||||||
override val duration: String? = null,
|
override val duration: String? = null,
|
||||||
|
@ -462,9 +478,9 @@ data class MovieLoadResponse(
|
||||||
) : LoadResponse
|
) : LoadResponse
|
||||||
|
|
||||||
data class TvSeriesEpisode(
|
data class TvSeriesEpisode(
|
||||||
val name: String?,
|
val name: String? = null,
|
||||||
val season: Int?,
|
val season: Int? = null,
|
||||||
val episode: Int?,
|
val episode: Int? = null,
|
||||||
val data: String,
|
val data: String,
|
||||||
val posterUrl: String? = null,
|
val posterUrl: String? = null,
|
||||||
val date: String? = null,
|
val date: String? = null,
|
||||||
|
@ -479,12 +495,12 @@ data class TvSeriesLoadResponse(
|
||||||
override val type: TvType,
|
override val type: TvType,
|
||||||
val episodes: List<TvSeriesEpisode>,
|
val episodes: List<TvSeriesEpisode>,
|
||||||
|
|
||||||
override val posterUrl: String?,
|
override val posterUrl: String? = null,
|
||||||
override val year: Int?,
|
override val year: Int? = null,
|
||||||
override val plot: String?,
|
override val plot: String? = null,
|
||||||
|
|
||||||
val showStatus: ShowStatus?,
|
val showStatus: ShowStatus? = null,
|
||||||
val imdbId: String?,
|
val imdbId: String? = null,
|
||||||
override val rating: Int? = null,
|
override val rating: Int? = null,
|
||||||
override val tags: List<String>? = null,
|
override val tags: List<String>? = null,
|
||||||
override val duration: String? = null,
|
override val duration: String? = null,
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
package com.lagradost.cloudstream3.animeproviders
|
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.annotation.JsonProperty
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
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.Context
|
||||||
import org.mozilla.javascript.Scriptable
|
import org.mozilla.javascript.Scriptable
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
class AllAnimeProvider : MainAPI() {
|
class AllAnimeProvider : MainAPI() {
|
||||||
|
@ -41,17 +39,17 @@ class AllAnimeProvider : MainAPI() {
|
||||||
override val supportedTypes: Set<TvType>
|
override val supportedTypes: Set<TvType>
|
||||||
get() = setOf(TvType.Anime, TvType.AnimeMovie)
|
get() = setOf(TvType.Anime, TvType.AnimeMovie)
|
||||||
|
|
||||||
private data class Data (
|
private data class Data(
|
||||||
@JsonProperty("shows") val shows: Shows
|
@JsonProperty("shows") val shows: Shows
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class Shows (
|
private data class Shows(
|
||||||
@JsonProperty("pageInfo") val pageInfo: PageInfo,
|
@JsonProperty("pageInfo") val pageInfo: PageInfo,
|
||||||
@JsonProperty("edges") val edges: List<Edges>,
|
@JsonProperty("edges") val edges: List<Edges>,
|
||||||
@JsonProperty("__typename") val _typename: String
|
@JsonProperty("__typename") val _typename: String
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class Edges (
|
private data class Edges(
|
||||||
@JsonProperty("_id") val Id: String?,
|
@JsonProperty("_id") val Id: String?,
|
||||||
@JsonProperty("name") val name: String,
|
@JsonProperty("name") val name: String,
|
||||||
@JsonProperty("englishName") val englishName: String?,
|
@JsonProperty("englishName") val englishName: String?,
|
||||||
|
@ -68,34 +66,35 @@ class AllAnimeProvider : MainAPI() {
|
||||||
@JsonProperty("status") val status: String?,
|
@JsonProperty("status") val status: String?,
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class AvailableEpisodes (
|
private data class AvailableEpisodes(
|
||||||
@JsonProperty("sub") val sub: Int,
|
@JsonProperty("sub") val sub: Int,
|
||||||
@JsonProperty("dub") val dub: Int,
|
@JsonProperty("dub") val dub: Int,
|
||||||
@JsonProperty("raw") val raw: Int
|
@JsonProperty("raw") val raw: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class AiredStart (
|
private data class AiredStart(
|
||||||
@JsonProperty("year") val year: Int,
|
@JsonProperty("year") val year: Int,
|
||||||
@JsonProperty("month") val month: Int,
|
@JsonProperty("month") val month: Int,
|
||||||
@JsonProperty("date") val date: Int
|
@JsonProperty("date") val date: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class Season (
|
private data class Season(
|
||||||
@JsonProperty("quarter") val quarter: String,
|
@JsonProperty("quarter") val quarter: String,
|
||||||
@JsonProperty("year") val year: Int
|
@JsonProperty("year") val year: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class PageInfo (
|
private data class PageInfo(
|
||||||
@JsonProperty("total") val total: Int,
|
@JsonProperty("total") val total: Int,
|
||||||
@JsonProperty("__typename") val _typename: String
|
@JsonProperty("__typename") val _typename: String
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class AllAnimeQuery (
|
private data class AllAnimeQuery(
|
||||||
@JsonProperty("data") val data: Data
|
@JsonProperty("data") val data: Data
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun search(query: String): ArrayList<SearchResponse> {
|
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
|
var res = get(link).text
|
||||||
if (res.contains("PERSISTED_QUERY_NOT_FOUND")) {
|
if (res.contains("PERSISTED_QUERY_NOT_FOUND")) {
|
||||||
res = get(link).text
|
res = get(link).text
|
||||||
|
@ -116,15 +115,15 @@ class AllAnimeProvider : MainAPI() {
|
||||||
TvType.Anime,
|
TvType.Anime,
|
||||||
it.thumbnail,
|
it.thumbnail,
|
||||||
it.airedStart?.year,
|
it.airedStart?.year,
|
||||||
EnumSet.of(DubStatus.Subbed), //, DubStatus.Dubbed),
|
EnumSet.of(DubStatus.Subbed, DubStatus.Dubbed),
|
||||||
it.englishName,
|
it.englishName,
|
||||||
null, //it.availableEpisodes?.dub,
|
it.availableEpisodes?.dub,
|
||||||
it.availableEpisodes?.sub
|
it.availableEpisodes?.sub
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class AvailableEpisodesDetail (
|
private data class AvailableEpisodesDetail(
|
||||||
@JsonProperty("sub") val sub: List<String>,
|
@JsonProperty("sub") val sub: List<String>,
|
||||||
@JsonProperty("dub") val dub: List<String>,
|
@JsonProperty("dub") val dub: List<String>,
|
||||||
@JsonProperty("raw") val raw: List<String>
|
@JsonProperty("raw") val raw: List<String>
|
||||||
|
@ -137,7 +136,6 @@ class AllAnimeProvider : MainAPI() {
|
||||||
rhino.optimizationLevel = -1
|
rhino.optimizationLevel = -1
|
||||||
val scope: Scriptable = rhino.initStandardObjects()
|
val scope: Scriptable = rhino.initStandardObjects()
|
||||||
|
|
||||||
|
|
||||||
val html = get(url).text
|
val html = get(url).text
|
||||||
val soup = Jsoup.parse(html)
|
val soup = Jsoup.parse(html)
|
||||||
|
|
||||||
|
@ -161,43 +159,28 @@ class AllAnimeProvider : MainAPI() {
|
||||||
|
|
||||||
val episodes = showData.availableEpisodes.let {
|
val episodes = showData.availableEpisodes.let {
|
||||||
if (it == null) return@let Pair(null, null)
|
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(
|
AnimeEpisode(
|
||||||
"$mainUrl/anime/${showData.Id}/episodes/sub/$epNum",
|
"$mainUrl/anime/${showData.Id}/episodes/sub/$epNum", episode = epNum
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
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(
|
AnimeEpisode(
|
||||||
"$mainUrl/anime/${showData.Id}/episodes/dub/$epNum",
|
"$mainUrl/anime/${showData.Id}/episodes/dub/$epNum", episode = epNum
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
epNum
|
|
||||||
)
|
)
|
||||||
}) else null)
|
}) else null)
|
||||||
}
|
}
|
||||||
|
|
||||||
return AnimeLoadResponse(
|
return newAnimeLoadResponse(title, url, TvType.Anime) {
|
||||||
null,
|
posterUrl = poster
|
||||||
null,
|
year = showData.airedStart?.year
|
||||||
title,
|
|
||||||
url,
|
addEpisodes(DubStatus.Subbed, episodes.first)
|
||||||
this.name,
|
addEpisodes(DubStatus.Dubbed, episodes.second)
|
||||||
TvType.Anime,
|
|
||||||
poster,
|
showStatus = getStatus(showData.status.toString())
|
||||||
showData.airedStart?.year,
|
|
||||||
null, // no dub, because there is no way to switch from dub to sub //episodes.second,
|
plot = description?.replace(Regex("""<(.*?)>"""), "")
|
||||||
episodes.first,
|
}
|
||||||
getStatus(showData.status.toString()),
|
|
||||||
description?.replace(Regex("""\<(.*?)\>"""), "")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val embedBlackList = listOf(
|
private val embedBlackList = listOf(
|
||||||
|
@ -216,7 +199,7 @@ class AllAnimeProvider : MainAPI() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (url.contains(it as String)) {
|
if (url.contains(it)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,14 +215,14 @@ class AllAnimeProvider : MainAPI() {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class Links (
|
private data class Links(
|
||||||
@JsonProperty("link") val link: String,
|
@JsonProperty("link") val link: String,
|
||||||
@JsonProperty("hls") val hls: Boolean?,
|
@JsonProperty("hls") val hls: Boolean?,
|
||||||
@JsonProperty("resolutionStr") val resolutionStr: String,
|
@JsonProperty("resolutionStr") val resolutionStr: String,
|
||||||
@JsonProperty("src") val src: String?
|
@JsonProperty("src") val src: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
private data class AllAnimeVideoApiResponse (
|
private data class AllAnimeVideoApiResponse(
|
||||||
@JsonProperty("links") val links: List<Links>
|
@JsonProperty("links") val links: List<Links>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -273,7 +256,8 @@ class AllAnimeProvider : MainAPI() {
|
||||||
|
|
||||||
val html = get(data).text
|
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 {
|
sources.forEach {
|
||||||
var link = it
|
var link = it
|
||||||
if (URI(link).isAbsolute || link.startsWith("//")) {
|
if (URI(link).isAbsolute || link.startsWith("//")) {
|
||||||
|
@ -305,16 +289,26 @@ class AllAnimeProvider : MainAPI() {
|
||||||
val links = mapper.readValue<AllAnimeVideoApiResponse>(response.text).links
|
val links = mapper.readValue<AllAnimeVideoApiResponse>(response.text).links
|
||||||
links.forEach { server ->
|
links.forEach { server ->
|
||||||
if (server.hls != null && server.hls) {
|
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)
|
getM3u8Qualities(
|
||||||
} else {
|
|
||||||
callback(ExtractorLink(
|
|
||||||
"AllAnime - " + URI(server.link).host,
|
|
||||||
server.resolutionStr,
|
|
||||||
server.link,
|
server.link,
|
||||||
"$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(server.link).path),
|
"$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(
|
||||||
getQualityFromName("1080"),
|
server.link
|
||||||
false
|
).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)
|
AnimeEpisode(link, name)
|
||||||
}.reversed()
|
}.reversed()
|
||||||
|
|
||||||
return AnimeLoadResponse(
|
return newAnimeLoadResponse(title, url, getType(title)) {
|
||||||
title,
|
posterUrl = poster
|
||||||
null,
|
this.year = year
|
||||||
title,
|
|
||||||
url,
|
addEpisodes(DubStatus.Subbed, episodes)
|
||||||
this.name,
|
|
||||||
getType(title),
|
plot = description
|
||||||
poster,
|
tags = genres
|
||||||
year,
|
}
|
||||||
null,
|
|
||||||
episodes,
|
|
||||||
null,
|
|
||||||
description,
|
|
||||||
genres
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadLinks(
|
override fun loadLinks(
|
||||||
|
|
|
@ -293,30 +293,26 @@ class AnimePaheProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimeLoadResponse(
|
newAnimeLoadResponse(animeTitle, url, getType(tvType.toString())) {
|
||||||
animeTitle,
|
engName = animeTitle
|
||||||
japTitle,
|
japName = japTitle
|
||||||
animeTitle,
|
|
||||||
url,
|
this.posterUrl = poster
|
||||||
this.name,
|
this.year = year
|
||||||
getType(tvType.toString()),
|
|
||||||
poster,
|
addEpisodes(DubStatus.Subbed, episodes)
|
||||||
year,
|
this.showStatus = status
|
||||||
null,
|
plot = synopsis
|
||||||
episodes,
|
tags = if (!doc.select(".anime-genre > ul a").isEmpty()) {
|
||||||
status,
|
|
||||||
synopsis,
|
|
||||||
if (!doc.select(".anime-genre > ul a").isEmpty()) {
|
|
||||||
ArrayList(doc.select(".anime-genre > ul a").map { it.text().toString() })
|
ArrayList(doc.select(".anime-genre > ul a").map { it.text().toString() })
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
},
|
}
|
||||||
ArrayList(),
|
|
||||||
malId,
|
this.malId = malId
|
||||||
anilistId,
|
this.anilistId = anilistId
|
||||||
null,
|
this.trailerUrl = trailer
|
||||||
trailer
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,7 +321,6 @@ class AnimePaheProvider : MainAPI() {
|
||||||
return s?.toIntOrNull() != null
|
return s?.toIntOrNull() != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun cookieStrToMap(cookie: String): Map<String, String> {
|
private fun cookieStrToMap(cookie: String): Map<String, String> {
|
||||||
val cookies = mutableMapOf<String, String>()
|
val cookies = mutableMapOf<String, String>()
|
||||||
for (string in cookie.split("; ")) {
|
for (string in cookie.split("; ")) {
|
||||||
|
@ -347,7 +342,6 @@ class AnimePaheProvider : MainAPI() {
|
||||||
val slice2 = characterMap.slice(0 until s2)
|
val slice2 = characterMap.slice(0 until s2)
|
||||||
var acc: Long = 0
|
var acc: Long = 0
|
||||||
|
|
||||||
|
|
||||||
for ((n, i) in content.reversed().withIndex()) {
|
for ((n, i) in content.reversed().withIndex()) {
|
||||||
acc += (when (isNumber("$i")) {
|
acc += (when (isNumber("$i")) {
|
||||||
true -> "$i".toLong()
|
true -> "$i".toLong()
|
||||||
|
|
|
@ -109,7 +109,7 @@ class DubbedAnimeProvider : MainAPI() {
|
||||||
HomePageList("Trending", parseDocumentTrending(trendingUrl)),
|
HomePageList("Trending", parseDocumentTrending(trendingUrl)),
|
||||||
HomePageList("Recently Added", parseDocument(recentlyAddedUrl)),
|
HomePageList("Recently Added", parseDocument(recentlyAddedUrl)),
|
||||||
HomePageList("Recent Releases", parseDocument(lastEpisodeUrl, true)),
|
HomePageList("Recent Releases", parseDocument(lastEpisodeUrl, true)),
|
||||||
// HomePageList("All", parseDocument(allUrl))
|
// HomePageList("All", parseDocument(allUrl))
|
||||||
)
|
)
|
||||||
|
|
||||||
return HomePageResponse(listItems)
|
return HomePageResponse(listItems)
|
||||||
|
@ -268,20 +268,12 @@ class DubbedAnimeProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val img = fixUrl(document.select("div.fkimgs > img").attr("src"))
|
val img = fixUrl(document.select("div.fkimgs > img").attr("src"))
|
||||||
return AnimeLoadResponse(
|
return newAnimeLoadResponse(title, url, TvType.Anime) {
|
||||||
null,
|
posterUrl = img
|
||||||
null,
|
this.year = year
|
||||||
title,
|
addEpisodes(DubStatus.Dubbed, episodes)
|
||||||
url,
|
plot = descript
|
||||||
this.name,
|
}
|
||||||
TvType.Anime,
|
|
||||||
img,
|
|
||||||
year,
|
|
||||||
ArrayList(episodes),
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
descript,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -185,24 +185,18 @@ class GogoanimeProvider : MainAPI() {
|
||||||
"Episode " + it.selectFirst(".name").text().replace("EP", "").trim()
|
"Episode " + it.selectFirst(".name").text().replace("EP", "").trim()
|
||||||
)
|
)
|
||||||
}.reversed()
|
}.reversed()
|
||||||
return AnimeLoadResponse(
|
|
||||||
title,
|
return newAnimeLoadResponse(title, link, getType(type.toString())) {
|
||||||
nativeName,
|
japName = nativeName
|
||||||
title,
|
engName = title
|
||||||
link,
|
posterUrl = poster
|
||||||
this.name,
|
this.year = year
|
||||||
getType(type.toString()),
|
addEpisodes(DubStatus.Subbed, episodes) // TODO CHECK
|
||||||
poster,
|
plot = description
|
||||||
year,
|
tags = genre
|
||||||
null,
|
|
||||||
episodes,
|
showStatus = getStatus(status.toString())
|
||||||
getStatus(status.toString()),
|
}
|
||||||
description,
|
|
||||||
ArrayList(genre),
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractVideos(uri: String): List<ExtractorLink> {
|
private fun extractVideos(uri: String): List<ExtractorLink> {
|
||||||
|
|
|
@ -109,23 +109,13 @@ class KawaiifuProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
val poster = soup.selectFirst("a.thumb > img").attr("src")
|
val poster = soup.selectFirst("a.thumb > img").attr("src")
|
||||||
|
|
||||||
|
return newAnimeLoadResponse(title, url, TvType.Anime) {
|
||||||
return AnimeLoadResponse(
|
this.year = year
|
||||||
title,
|
posterUrl = poster
|
||||||
null,
|
addEpisodes(DubStatus.Subbed, episodes)
|
||||||
title,
|
plot = description
|
||||||
url,
|
this.tags = tags
|
||||||
this.name,
|
}
|
||||||
TvType.Anime,
|
|
||||||
poster,
|
|
||||||
year,
|
|
||||||
null,
|
|
||||||
episodes,
|
|
||||||
ShowStatus.Ongoing,
|
|
||||||
description,
|
|
||||||
ArrayList(tags),
|
|
||||||
ArrayList()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadLinks(
|
override fun loadLinks(
|
||||||
|
|
|
@ -268,24 +268,19 @@ class TenshiProvider : MainAPI() {
|
||||||
val synonyms =
|
val synonyms =
|
||||||
document.select("li.synonym.meta-data > div.info-box > span.value").map { it?.text()?.trim().toString() }
|
document.select("li.synonym.meta-data > div.info-box > span.value").map { it?.text()?.trim().toString() }
|
||||||
|
|
||||||
return AnimeLoadResponse(
|
return newAnimeLoadResponse(canonicalTitle,url,getType(type ?: "")) {
|
||||||
englishTitle,
|
engName = englishTitle
|
||||||
japaneseTitle,
|
japName = japaneseTitle
|
||||||
canonicalTitle,
|
|
||||||
url,
|
posterUrl = poster
|
||||||
this.name,
|
this.year = year.toIntOrNull()
|
||||||
getType(type ?: ""),
|
|
||||||
poster,
|
addEpisodes(DubStatus.Subbed,episodes)
|
||||||
year.toIntOrNull(),
|
showStatus = status
|
||||||
null,
|
tags = genre
|
||||||
episodes,
|
this.synonyms = synonyms
|
||||||
status,
|
plot = synopsis
|
||||||
synopsis,
|
}
|
||||||
ArrayList(genre),
|
|
||||||
ArrayList(synonyms),
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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")
|
val genre = document.select("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(5) > a")
|
||||||
.map { it?.text()?.trim().toString() }
|
.map { it?.text()?.trim().toString() }
|
||||||
|
|
||||||
return AnimeLoadResponse(
|
return newAnimeLoadResponse(canonicalTitle,url,getType(type ?: "")) {
|
||||||
canonicalTitle,
|
japName = japaneseTitle
|
||||||
japaneseTitle,
|
engName = canonicalTitle
|
||||||
canonicalTitle,
|
posterUrl = poster
|
||||||
url,
|
this.year = year
|
||||||
this.name,
|
addEpisodes(if(isDubbed) DubStatus.Dubbed else DubStatus.Subbed,episodes)
|
||||||
getType(type ?: ""),
|
showStatus = status
|
||||||
poster,
|
plot = synopsis
|
||||||
year,
|
tags = genre
|
||||||
if (isDubbed) episodes else null,
|
}
|
||||||
if (!isDubbed) episodes else null,
|
|
||||||
status,
|
|
||||||
synopsis,
|
|
||||||
ArrayList(genre),
|
|
||||||
ArrayList(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadLinks(
|
override fun loadLinks(
|
||||||
|
|
|
@ -230,21 +230,17 @@ class ZoroProvider : MainAPI() {
|
||||||
it.selectFirst(".ssli-order")?.text()?.toIntOrNull()
|
it.selectFirst(".ssli-order")?.text()?.toIntOrNull()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return AnimeLoadResponse(
|
|
||||||
title,
|
return newAnimeLoadResponse(title, url, TvType.Anime) {
|
||||||
japaneseTitle,
|
japName = japaneseTitle
|
||||||
title,
|
engName = title
|
||||||
url,
|
posterUrl = poster
|
||||||
this.name,
|
this.year = year
|
||||||
TvType.Anime,
|
addEpisodes(DubStatus.Subbed, episodes)
|
||||||
poster,
|
showStatus = status
|
||||||
year,
|
plot = description
|
||||||
null,
|
this.tags = tags
|
||||||
episodes,
|
}
|
||||||
status,
|
|
||||||
description,
|
|
||||||
tags,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getM3u8FromRapidCloud(url: String): String {
|
private fun getM3u8FromRapidCloud(url: String): String {
|
||||||
|
|
|
@ -235,7 +235,7 @@ class ResultFragment : Fragment() {
|
||||||
private var currentId: Int? = null
|
private var currentId: Int? = null
|
||||||
private var currentIsMovie: Boolean? = null
|
private var currentIsMovie: Boolean? = null
|
||||||
private var episodeRanges: List<String>? = null
|
private var episodeRanges: List<String>? = null
|
||||||
|
private var dubRange: Set<DubStatus>? = null
|
||||||
var url: String? = null
|
var url: String? = null
|
||||||
|
|
||||||
private fun fromIndexToSeasonText(selection: Int?): String {
|
private fun fromIndexToSeasonText(selection: Int?): String {
|
||||||
|
@ -681,7 +681,6 @@ class ResultFragment : Fragment() {
|
||||||
}
|
}
|
||||||
outputFile.writeText(text)
|
outputFile.writeText(text)
|
||||||
|
|
||||||
|
|
||||||
val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
|
val vlcIntent = Intent(VLC_INTENT_ACTION_RESULT)
|
||||||
|
|
||||||
vlcIntent.setPackage(VLC_PACKAGE)
|
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 ->
|
observe(viewModel.selectedRange) { range ->
|
||||||
result_episode_select?.text = range
|
result_episode_select?.text = range
|
||||||
}
|
}
|
||||||
|
@ -962,8 +980,10 @@ class ResultFragment : Fragment() {
|
||||||
if (metadataInfoArray.size > 0) {
|
if (metadataInfoArray.size > 0) {
|
||||||
result_metadata.visibility = VISIBLE
|
result_metadata.visibility = VISIBLE
|
||||||
val text = SpannableStringBuilder()
|
val text = SpannableStringBuilder()
|
||||||
val grayColor = ctx.colorFromAttribute(R.attr.grayTextColor) //ContextCompat.getColor(requireContext(), R.color.grayTextColor)
|
val grayColor =
|
||||||
val textColor = ctx.colorFromAttribute(R.attr.textColor) //ContextCompat.getColor(requireContext(), R.color.textColor)
|
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) {
|
for (meta in metadataInfoArray) {
|
||||||
text.color(grayColor) { append(getString(meta.first) + ": ") }
|
text.color(grayColor) { append(getString(meta.first) + ": ") }
|
||||||
.color(textColor) { append("${meta.second}\n") }
|
.color(textColor) { append("${meta.second}\n") }
|
||||||
|
|
|
@ -55,13 +55,20 @@ class ResultViewModel : ViewModel() {
|
||||||
val publicEpisodes: LiveData<Resource<List<ResultEpisode>>> get() = _publicEpisodes
|
val publicEpisodes: LiveData<Resource<List<ResultEpisode>>> get() = _publicEpisodes
|
||||||
val publicEpisodesCount: LiveData<Int> get() = _publicEpisodesCount
|
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()
|
private val page: MutableLiveData<LoadResponse> = MutableLiveData()
|
||||||
val id: MutableLiveData<Int> = MutableLiveData()
|
val id: MutableLiveData<Int> = MutableLiveData()
|
||||||
val selectedSeason: MutableLiveData<Int> = MutableLiveData(-2)
|
val selectedSeason: MutableLiveData<Int> = MutableLiveData(-2)
|
||||||
val seasonSelections: MutableLiveData<List<Int?>> = MutableLiveData()
|
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()
|
private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData()
|
||||||
val watchStatus: LiveData<WatchType> get() = _watchStatus
|
val watchStatus: LiveData<WatchType> get() = _watchStatus
|
||||||
|
|
||||||
|
@ -110,7 +117,7 @@ class ResultViewModel : ViewModel() {
|
||||||
val seasons = seasonTypes.toList().map { it.first }.sortedBy { it }
|
val seasons = seasonTypes.toList().map { it.first }.sortedBy { it }
|
||||||
seasonSelections.postValue(seasons)
|
seasonSelections.postValue(seasons)
|
||||||
if (seasons.isEmpty()) { // WHAT THE FUCK DID YOU DO????? HOW DID YOU DO THIS
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +167,7 @@ class ResultViewModel : ViewModel() {
|
||||||
selectedRange.postValue(allRange)
|
selectedRange.postValue(allRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
_publicEpisodes.postValue(Resource.Success( currentList))
|
_publicEpisodes.postValue(Resource.Success(currentList))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeSeason(context: Context, selection: Int?) {
|
fun changeSeason(context: Context, selection: Int?) {
|
||||||
|
@ -171,6 +178,13 @@ class ResultViewModel : ViewModel() {
|
||||||
filterEpisodes(context, _episodes.value, null, range)
|
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?) {
|
private fun updateEpisodes(context: Context, localId: Int?, list: List<ResultEpisode>, selection: Int?) {
|
||||||
_episodes.postValue(list)
|
_episodes.postValue(list)
|
||||||
val set = HashMap<Int, Int>()
|
val set = HashMap<Int, Int>()
|
||||||
|
@ -237,7 +251,7 @@ class ResultViewModel : ViewModel() {
|
||||||
return name
|
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))
|
_resultResponse.postValue(Resource.Loading(url))
|
||||||
_publicEpisodes.postValue(Resource.Loading())
|
_publicEpisodes.postValue(Resource.Loading())
|
||||||
|
|
||||||
|
@ -273,16 +287,22 @@ class ResultViewModel : ViewModel() {
|
||||||
|
|
||||||
when (d) {
|
when (d) {
|
||||||
is AnimeLoadResponse -> {
|
is AnimeLoadResponse -> {
|
||||||
val isDub = d.dubEpisodes != null && d.dubEpisodes.isNotEmpty()
|
//TODO context.getKey<>() isdub
|
||||||
dubStatus.postValue(if (isDub) DubStatus.Dubbed else DubStatus.Subbed)
|
|
||||||
|
|
||||||
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>()
|
val episodes = ArrayList<ResultEpisode>()
|
||||||
for ((index, i) in dataList.withIndex()) {
|
for ((index, i) in ep.value.withIndex()) {
|
||||||
|
idIndex++
|
||||||
|
|
||||||
val episode = i.episode ?: (index + 1)
|
val episode = i.episode ?: (index + 1)
|
||||||
episodes.add(
|
episodes.add(
|
||||||
context.buildResultEpisode(
|
context.buildResultEpisode(
|
||||||
|
@ -292,17 +312,22 @@ class ResultViewModel : ViewModel() {
|
||||||
null, // TODO FIX SEASON
|
null, // TODO FIX SEASON
|
||||||
i.url,
|
i.url,
|
||||||
apiName,
|
apiName,
|
||||||
(mainId + index + 1),
|
idIndex,
|
||||||
index,
|
index,
|
||||||
i.rating,
|
i.rating,
|
||||||
i.descript,
|
i.description,
|
||||||
if (fillerEpisodes is Resource.Success) fillerEpisodes.value?.let {
|
if (fillerEpisodes is Resource.Success) fillerEpisodes.value?.let {
|
||||||
it.contains(episode) && it[episode] == true
|
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)
|
updateEpisodes(context, mainId, episodes, -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,11 +391,10 @@ class ResultViewModel : ViewModel() {
|
||||||
), -1
|
), -1
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
// nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -431,8 +431,23 @@
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_marginStart="0dp"
|
android:layout_marginStart="0dp"
|
||||||
style="@style/MultiSelectButton"
|
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
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
Loading…
Reference in a new issue