2022-01-09 12:27:10 +00:00
|
|
|
package com.lagradost.cloudstream3.movieproviders
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.module.kotlin.readValue
|
|
|
|
import com.lagradost.cloudstream3.*
|
2022-03-17 15:04:05 +00:00
|
|
|
import com.lagradost.cloudstream3.animeproviders.GogoanimeProvider.Companion.extractVidstream
|
2022-01-31 21:01:25 +00:00
|
|
|
import com.lagradost.cloudstream3.extractors.XStreamCdn
|
2022-01-22 14:14:47 +00:00
|
|
|
import com.lagradost.cloudstream3.extractors.helper.AsianEmbedHelper
|
2022-01-09 12:27:10 +00:00
|
|
|
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
|
|
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
|
|
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
|
|
|
|
|
|
|
class WatchAsianProvider : MainAPI() {
|
2022-03-16 15:29:11 +00:00
|
|
|
override var mainUrl = "https://watchasian.sh"
|
|
|
|
override var name = "WatchAsian"
|
2022-01-09 12:27:10 +00:00
|
|
|
override val hasQuickSearch = false
|
|
|
|
override val hasMainPage = true
|
|
|
|
override val hasChromecastSupport = false
|
|
|
|
override val hasDownloadSupport = true
|
2022-03-21 11:07:36 +00:00
|
|
|
override val supportedTypes = setOf(TvType.AsianDrama)
|
2022-01-09 12:27:10 +00:00
|
|
|
|
2022-01-16 22:31:42 +00:00
|
|
|
override suspend fun getMainPage(): HomePageResponse {
|
2022-01-09 12:27:10 +00:00
|
|
|
val headers = mapOf("X-Requested-By" to mainUrl)
|
|
|
|
val doc = app.get(mainUrl, headers = headers).document
|
|
|
|
val rowPair = mutableListOf<Pair<String, String>>()
|
|
|
|
doc.select("div.block-tab")?.forEach {
|
|
|
|
it?.select("ul.tab > li")?.mapNotNull { row ->
|
|
|
|
val link = row?.attr("data-tab") ?: return@mapNotNull null
|
|
|
|
val title = row.text() ?: return@mapNotNull null
|
|
|
|
Pair(title, link)
|
|
|
|
}?.let { it1 ->
|
|
|
|
rowPair.addAll(
|
|
|
|
it1
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return HomePageResponse(
|
|
|
|
rowPair.mapNotNull { row ->
|
2022-03-17 15:04:05 +00:00
|
|
|
val main = (doc.select("div.tab-content.${row.second}")
|
|
|
|
?: doc.select("div.tab-content.${row.second}.selected"))
|
|
|
|
?: return@mapNotNull null
|
2022-01-09 12:27:10 +00:00
|
|
|
|
2022-03-17 15:04:05 +00:00
|
|
|
val title = row.first
|
|
|
|
val inner = main.select("li") ?: return@mapNotNull null
|
2022-01-09 12:27:10 +00:00
|
|
|
|
2022-03-17 15:04:05 +00:00
|
|
|
HomePageList(
|
|
|
|
title,
|
|
|
|
inner.map {
|
|
|
|
// Get inner div from article
|
|
|
|
val innerBody = it?.selectFirst("a")
|
|
|
|
// Fetch details
|
|
|
|
val link = fixUrlNull(innerBody?.attr("href")) ?: return@map null
|
|
|
|
val image =
|
|
|
|
fixUrlNull(innerBody?.select("img")?.attr("data-original")) ?: ""
|
|
|
|
val name = (innerBody?.selectFirst("h3.title")?.text() ?: innerBody?.text())
|
|
|
|
?: "<Untitled>"
|
|
|
|
//Log.i(this.name, "Result => (innerBody, image) ${innerBody} / ${image}")
|
|
|
|
MovieSearchResponse(
|
|
|
|
name,
|
|
|
|
link,
|
|
|
|
this.name,
|
|
|
|
TvType.TvSeries,
|
|
|
|
image,
|
|
|
|
year = null,
|
|
|
|
id = null,
|
|
|
|
)
|
|
|
|
}.filterNotNull().distinctBy { c -> c.url })
|
2022-01-09 12:27:10 +00:00
|
|
|
}.filter { a -> a.list.isNotEmpty() }
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-01-16 22:31:42 +00:00
|
|
|
override suspend fun search(query: String): List<SearchResponse> {
|
2022-01-09 12:27:10 +00:00
|
|
|
val url = "$mainUrl/search?type=movies&keyword=$query"
|
|
|
|
val document = app.get(url).document.getElementsByTag("body")
|
2022-03-17 15:04:05 +00:00
|
|
|
.select("div.block.tab-container > div > ul > li") ?: return listOf()
|
2022-01-09 12:27:10 +00:00
|
|
|
|
|
|
|
return document.mapNotNull {
|
|
|
|
val innerA = it?.selectFirst("a") ?: return@mapNotNull null
|
|
|
|
val link = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null
|
|
|
|
val title = it.select("h3.title")?.text() ?: return@mapNotNull null
|
2022-03-17 15:04:05 +00:00
|
|
|
if (title.isEmpty()) {
|
|
|
|
return@mapNotNull null
|
|
|
|
}
|
2022-01-09 12:27:10 +00:00
|
|
|
val year = null
|
|
|
|
val imgsrc = innerA.select("img")?.attr("data-original") ?: return@mapNotNull null
|
|
|
|
val image = fixUrlNull(imgsrc)
|
|
|
|
//Log.i(this.name, "Result => (img movie) $title / $link")
|
|
|
|
MovieSearchResponse(
|
|
|
|
title,
|
|
|
|
link,
|
|
|
|
this.name,
|
|
|
|
TvType.Movie,
|
|
|
|
image,
|
|
|
|
year
|
|
|
|
)
|
|
|
|
}.distinctBy { a -> a.url }
|
|
|
|
}
|
|
|
|
|
2022-01-16 22:31:42 +00:00
|
|
|
override suspend fun load(url: String): LoadResponse {
|
2022-01-09 12:27:10 +00:00
|
|
|
val body = app.get(url).document
|
|
|
|
// Declare vars
|
|
|
|
val isDramaDetail = url.contains("/drama-detail/")
|
|
|
|
var poster = ""
|
|
|
|
var title = ""
|
2022-03-17 15:04:05 +00:00
|
|
|
var descript: String? = null
|
|
|
|
var year: Int? = null
|
|
|
|
var tags: List<String>? = null
|
2022-01-09 12:27:10 +00:00
|
|
|
if (isDramaDetail) {
|
|
|
|
val main = body.select("div.details")
|
|
|
|
val inner = main?.select("div.info")
|
|
|
|
// Video details
|
|
|
|
poster = fixUrlNull(main?.select("div.img > img")?.attr("src")) ?: ""
|
|
|
|
//Log.i(this.name, "Result => (imgLinkCode) ${imgLinkCode}")
|
|
|
|
title = inner?.select("h1")?.firstOrNull()?.text() ?: ""
|
|
|
|
//Log.i(this.name, "Result => (year) ${title.substring(title.length - 5)}")
|
|
|
|
descript = inner?.text()
|
2022-02-06 11:43:12 +00:00
|
|
|
|
|
|
|
inner?.select("p")?.forEach { p ->
|
2022-03-17 15:04:05 +00:00
|
|
|
val caption =
|
|
|
|
p?.selectFirst("span")?.text()?.trim()?.lowercase()?.removeSuffix(":")?.trim()
|
|
|
|
?: return@forEach
|
2022-02-06 11:43:12 +00:00
|
|
|
when (caption) {
|
|
|
|
"genre" -> {
|
|
|
|
tags = p.select("a")?.mapNotNull { it?.text()?.trim() }
|
|
|
|
}
|
|
|
|
"released" -> {
|
|
|
|
year = p.select("a")?.text()?.trim()?.toIntOrNull()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-01-09 12:27:10 +00:00
|
|
|
} else {
|
|
|
|
poster = body.select("meta[itemprop=\"image\"]")?.attr("content") ?: ""
|
|
|
|
title = body.selectFirst("div.block.watch-drama")?.selectFirst("h1")
|
|
|
|
?.text() ?: ""
|
|
|
|
year = null
|
|
|
|
descript = body.select("meta[name=\"description\"]")?.attr("content")
|
|
|
|
}
|
2022-02-06 11:43:12 +00:00
|
|
|
//Fallback year from title
|
|
|
|
if (year == null) {
|
|
|
|
year = if (title.length > 5) {
|
|
|
|
title.replace(")", "").replace("(", "").substring(title.length - 5)
|
|
|
|
.trim().trimEnd(')').toIntOrNull()
|
2022-03-17 15:04:05 +00:00
|
|
|
} else {
|
|
|
|
null
|
|
|
|
}
|
2022-02-06 11:43:12 +00:00
|
|
|
}
|
2022-01-09 12:27:10 +00:00
|
|
|
|
|
|
|
// Episodes Links
|
|
|
|
//Log.i(this.name, "Result => (all eps) ${body.select("ul.list-episode-item-2.all-episode > li")}")
|
|
|
|
val episodeList = body.select("ul.list-episode-item-2.all-episode > li")?.mapNotNull { ep ->
|
|
|
|
//Log.i(this.name, "Result => (epA) ${ep.select("a")}")
|
|
|
|
val innerA = ep.select("a") ?: return@mapNotNull null
|
|
|
|
//Log.i(this.name, "Result => (innerA) ${fixUrlNull(innerA.attr("href"))}")
|
|
|
|
val epLink = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null
|
|
|
|
|
|
|
|
val regex = "(?<=episode-).*?(?=.html)".toRegex()
|
|
|
|
val count = regex.find(epLink, mainUrl.length)?.value?.toIntOrNull() ?: 0
|
|
|
|
//Log.i(this.name, "Result => $epLink (regexYear) ${count}")
|
2022-04-09 14:50:04 +00:00
|
|
|
Episode(
|
2022-01-09 12:27:10 +00:00
|
|
|
name = null,
|
|
|
|
season = null,
|
|
|
|
episode = count,
|
|
|
|
data = epLink,
|
|
|
|
posterUrl = poster,
|
|
|
|
date = null
|
|
|
|
)
|
|
|
|
} ?: listOf()
|
|
|
|
//If there's only 1 episode, consider it a movie.
|
|
|
|
if (episodeList.size == 1) {
|
|
|
|
//Clean title
|
2022-01-31 21:01:25 +00:00
|
|
|
title = title.trim().removeSuffix("Episode 1")
|
2022-01-09 12:27:10 +00:00
|
|
|
val streamlink = getServerLinks(episodeList[0].data)
|
|
|
|
//Log.i(this.name, "Result => (streamlink) $streamlink")
|
2022-02-06 11:43:12 +00:00
|
|
|
return MovieLoadResponse(
|
|
|
|
name = title,
|
|
|
|
url = url,
|
|
|
|
apiName = this.name,
|
|
|
|
type = TvType.Movie,
|
|
|
|
dataUrl = streamlink,
|
|
|
|
posterUrl = poster,
|
|
|
|
year = year,
|
|
|
|
plot = descript,
|
|
|
|
tags = tags
|
|
|
|
)
|
2022-01-09 12:27:10 +00:00
|
|
|
}
|
|
|
|
return TvSeriesLoadResponse(
|
2022-02-06 11:43:12 +00:00
|
|
|
name = title,
|
|
|
|
url = url,
|
|
|
|
apiName = this.name,
|
2022-03-21 11:07:36 +00:00
|
|
|
type = TvType.AsianDrama,
|
2022-02-06 11:43:12 +00:00
|
|
|
episodes = episodeList.reversed(),
|
|
|
|
posterUrl = poster,
|
|
|
|
year = year,
|
|
|
|
plot = descript,
|
|
|
|
tags = tags
|
2022-01-09 12:27:10 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-01-16 22:31:42 +00:00
|
|
|
override suspend fun loadLinks(
|
2022-01-09 12:27:10 +00:00
|
|
|
data: String,
|
|
|
|
isCasting: Boolean,
|
|
|
|
subtitleCallback: (SubtitleFile) -> Unit,
|
|
|
|
callback: (ExtractorLink) -> Unit
|
|
|
|
): Boolean {
|
|
|
|
val links = if (data.startsWith(mainUrl)) {
|
|
|
|
getServerLinks(data)
|
2022-03-17 15:04:05 +00:00
|
|
|
} else {
|
|
|
|
data
|
|
|
|
}
|
2022-01-22 14:14:47 +00:00
|
|
|
var count = 0
|
2022-01-30 01:50:49 +00:00
|
|
|
mapper.readValue<List<String>>(links).apmap { item ->
|
2022-01-22 14:14:47 +00:00
|
|
|
count++
|
2022-01-30 01:50:49 +00:00
|
|
|
val url = fixUrl(item.trim())
|
2022-01-22 14:14:47 +00:00
|
|
|
//Log.i(this.name, "Result => (url) $url")
|
2022-01-31 21:01:25 +00:00
|
|
|
when {
|
|
|
|
url.startsWith("https://asianembed.io") || url.startsWith("https://asianload.io") -> {
|
2022-04-17 14:26:51 +00:00
|
|
|
val iv = "9262859232435825"
|
|
|
|
val secretKey = "93422192433952489752342908585752"
|
|
|
|
extractVidstream(url, this.name, callback, iv, secretKey, secretKey, false)
|
2022-01-31 21:01:25 +00:00
|
|
|
AsianEmbedHelper.getUrls(url, callback)
|
|
|
|
}
|
|
|
|
url.startsWith("https://embedsito.com") -> {
|
|
|
|
val extractor = XStreamCdn()
|
|
|
|
extractor.domainUrl = "embedsito.com"
|
|
|
|
extractor.getSafeUrl(url)?.apmap { link ->
|
|
|
|
callback.invoke(link)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else -> {
|
|
|
|
loadExtractor(url, mainUrl, callback)
|
|
|
|
}
|
2022-01-09 12:27:10 +00:00
|
|
|
}
|
2022-01-22 14:14:47 +00:00
|
|
|
}
|
|
|
|
return count > 0
|
2022-01-09 12:27:10 +00:00
|
|
|
}
|
|
|
|
|
2022-03-17 15:04:05 +00:00
|
|
|
private suspend fun getServerLinks(url: String): String {
|
2022-01-09 12:27:10 +00:00
|
|
|
val moviedoc = app.get(url, referer = mainUrl).document
|
|
|
|
return moviedoc.select("div.anime_muti_link > ul > li")
|
|
|
|
?.mapNotNull {
|
|
|
|
fixUrlNull(it?.attr("data-video")) ?: return@mapNotNull null
|
|
|
|
}?.toJson() ?: ""
|
|
|
|
}
|
|
|
|
}
|