Fixed WatchCartoonOnline

This commit is contained in:
no-commit 2023-03-18 21:31:56 +01:00
parent b1a9851fc0
commit b9d86b5be2
6 changed files with 140 additions and 127 deletions

View File

@ -5,7 +5,6 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.cloudstream3.utils.loadExtractor
class KimCartoonProvider : MainAPI() { class KimCartoonProvider : MainAPI() {
override var mainUrl = "https://kimcartoon.li" override var mainUrl = "https://kimcartoon.li"
override var name = "Kim Cartoon" override var name = "Kim Cartoon"
override val hasQuickSearch = true override val hasQuickSearch = true
@ -73,7 +72,7 @@ class KimCartoonProvider : MainAPI() {
AnimeSearchResponse( AnimeSearchResponse(
it.select("span").text(), it.select("span").text(),
mainUrl + it.attr("href"), mainUrl + it.attr("href"),
mainUrl, name,
TvType.Cartoon, TvType.Cartoon,
fixUrl(it.select("img").attr("src")) fixUrl(it.select("img").attr("src"))
) )

View File

@ -16,7 +16,7 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 1 // will be 3 if unspecified status = 0 // will be 3 if unspecified
tvTypes = listOf( tvTypes = listOf(
"TvSeries", "TvSeries",
"Movie", "Movie",

View File

@ -21,5 +21,5 @@ class AsianLoadProvider : VidstreamProviderTemplate() {
override val secretKey = "93422192433952489752342908585752" override val secretKey = "93422192433952489752342908585752"
override val secretDecryptKey = secretKey override val secretDecryptKey = secretKey
override val supportedTypes = setOf(TvType.AsianDrama) override val supportedTypes = setOf(TvType.AsianDrama, TvType.TvSeries)
} }

View File

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 1 version = 2
cloudstream { cloudstream {

View File

@ -2,10 +2,9 @@ package com.lagradost
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.Coroutines.mainWork
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
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.util.* import java.util.*
@ -13,7 +12,8 @@ import java.util.*
class WatchCartoonOnlineProvider : MainAPI() { class WatchCartoonOnlineProvider : MainAPI() {
override var name = "WatchCartoonOnline" override var name = "WatchCartoonOnline"
override var mainUrl = "https://www.wcostream.com" override var mainUrl = "https://www.wcostream.net"
override val hasMainPage = true
override val supportedTypes = setOf( override val supportedTypes = setOf(
TvType.Cartoon, TvType.Cartoon,
@ -22,94 +22,116 @@ class WatchCartoonOnlineProvider : MainAPI() {
TvType.TvSeries TvType.TvSeries
) )
override suspend fun search(query: String): List<SearchResponse> { override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse? {
val url = "https://www.wcostream.com/search" val doc = app.get(mainUrl).document
val rows = doc.select("div.recent-release").mapNotNull {
val rowName = it.text()
val parent = it.parent() ?: return@mapNotNull null
val list = parent.select("ul.items > li").map { item ->
val link = item.select("a").attr("href")
val name = item.text()
newTvSeriesSearchResponse(
name, link
) {
this.posterUrl = fixUrl(item.select("img").attr("src"))
}
}
HomePageList(rowName, list)
}
return HomePageResponse(rows, false)
}
var response =
override suspend fun search(query: String): List<SearchResponse> {
val url = "$mainUrl/search"
val seriesDocument =
app.post( app.post(
url, url,
headers = mapOf("Referer" to url), headers = mapOf("Referer" to url),
data = mapOf("catara" to query, "konuara" to "series") data = mapOf("catara" to query, "konuara" to "series")
).text ).document
var document = Jsoup.parse(response)
var items = document.select("div#blog > div.cerceve").toList()
val returnValue = ArrayList<SearchResponse>() val series = seriesDocument.select("div#blog > div.cerceve").toList().mapNotNull { item ->
val header = item.selectFirst("> div.iccerceve") ?: return@mapNotNull null
for (item in items) { val titleHeader = header.selectFirst("> div.aramadabaslik > a")
val header = item.selectFirst("> div.iccerceve")
val titleHeader = header!!.selectFirst("> div.aramadabaslik > a")
val title = titleHeader!!.text() val title = titleHeader!!.text()
val href = fixUrl(titleHeader.attr("href")) val href = fixUrl(titleHeader.attr("href"))
val poster = fixUrl(header.selectFirst("> a > img")!!.attr("src")) val poster = fixUrl(header.selectFirst("> a > img")!!.attr("src"))
val genreText = item.selectFirst("div.cerceve-tur-ve-genre")!!.ownText() val genreText = item.selectFirst("div.cerceve-tur-ve-genre")!!.ownText()
if (genreText.contains("cartoon")) { if (genreText.contains("cartoon")) {
returnValue.add(TvSeriesSearchResponse(title, href, this.name, TvType.Cartoon, poster, null, null)) TvSeriesSearchResponse(
title,
href,
this.name,
TvType.Cartoon,
poster,
null,
null
)
} else { } else {
val isDubbed = genreText.contains("dubbed") val isDubbed = genreText.contains("dubbed")
val set: EnumSet<DubStatus> = val set: EnumSet<DubStatus> =
EnumSet.of(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed) EnumSet.of(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed)
returnValue.add( AnimeSearchResponse(
AnimeSearchResponse( title,
title, href,
href, this.name,
this.name, TvType.Anime,
TvType.Anime, poster,
poster, null,
null, set,
set,
)
) )
} }
} }
// "episodes-search", is used for finding movies, anime episodes should be filtered out // "episodes-search", is used for finding movies, anime episodes should be filtered out
response = val episodesDocument =
app.post( app.post(
url, url,
headers = mapOf("Referer" to url), headers = mapOf("Referer" to url),
data = mapOf("catara" to query, "konuara" to "episodes") data = mapOf("catara" to query, "konuara" to "episodes")
).text ).document
document = Jsoup.parse(response) val items = episodesDocument.select("#catlist-listview2 > ul > li")
items = document.select("#catlist-listview2 > ul > li") // Filter away episodes and blanks
.filter { it -> it?.text() != null && !it.text().toString().contains("Episode") } .filterNot { element ->
element.text().contains("Episode") || element.text().isNullOrBlank()
}
for (item in items) { val episodes = items.mapNotNull { item ->
val titleHeader = item.selectFirst("a") val titleHeader = item.selectFirst("a") ?: return@mapNotNull null
val title = titleHeader!!.text() val title = titleHeader.text()
val href = fixUrl(titleHeader.attr("href")) val href = fixUrl(titleHeader.attr("href"))
//val isDubbed = title.contains("dubbed") //val isDubbed = title.contains("dubbed")
//val set: EnumSet<DubStatus> = //val set: EnumSet<DubStatus> =
// EnumSet.of(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed) // EnumSet.of(if (isDubbed) DubStatus.Dubbed else DubStatus.Subbed)
returnValue.add( MovieSearchResponse(
TvSeriesSearchResponse( title,
title, href,
href, this.name,
this.name, TvType.AnimeMovie,
TvType.AnimeMovie, null,
null, null,
null, null,
null,
)
) )
} }
return returnValue return series + episodes
} }
override suspend fun load(url: String): LoadResponse { override suspend fun load(url: String): LoadResponse {
val isMovie = !url.contains("/anime/") val isSeries = url.contains("/anime/")
val response = app.get(url).text val document = app.get(url).document
val document = Jsoup.parse(response)
return if (!isMovie) { return if (isSeries) {
val title = document.selectFirst("td.vsbaslik > h2")!!.text() val title = document.selectFirst("td.vsbaslik > h2")!!.text()
val poster = fixUrlNull(document.selectFirst("div#cat-img-desc > div > img")?.attr("src")) val poster =
fixUrlNull(document.selectFirst("div#cat-img-desc > div > img")?.attr("src"))
val plot = document.selectFirst("div.iltext")!!.text() val plot = document.selectFirst("div.iltext")!!.text()
val genres = document.select("div#cat-genre > div.wcobtn > a").map { it.text() } val genres = document.select("div#cat-genre > div.wcobtn > a").map { it.text() }
val episodes = document.select("div#catlist-listview > ul > li > a").reversed().map { val episodes = document.select("div#catlist-listview > ul > li > a").reversed().map {
val text = it.text() val text = it.text()
val match = Regex("Season ([0-9]*) Episode ([0-9]*).*? (.*)").find(text) val match = Regex("""Season (\d*) Episode (\d*).*? (.*)""").find(text)
val href = it.attr("href") val href = it.attr("href")
if (match != null) { if (match != null) {
val last = match.groupValues[3] val last = match.groupValues[3]
@ -120,7 +142,7 @@ class WatchCartoonOnlineProvider : MainAPI() {
match.groupValues[2].toIntOrNull(), match.groupValues[2].toIntOrNull(),
) )
} }
val match2 = Regex("Episode ([0-9]*).*? (.*)").find(text) val match2 = Regex("""Episode (\d*).*? (.*)""").find(text)
if (match2 != null) { if (match2 != null) {
val last = match2.groupValues[2] val last = match2.groupValues[2]
return@map Episode( return@map Episode(
@ -149,36 +171,28 @@ class WatchCartoonOnlineProvider : MainAPI() {
tags = genres tags = genres
) )
} else { } else {
val title = document.selectFirst(".iltext .Apple-style-span")?.text().toString() val title = document.selectFirst("td.ilxbaslik8")?.text().toString()
val b = document.select(".iltext b")
val description = if (b.isNotEmpty()) {
b.last()!!.html().split("<br>")[0]
} else null
TvSeriesLoadResponse( newMovieLoadResponse(
title, title,
url, url,
this.name, TvType.AnimeMovie,
TvType.TvSeries, url
listOf(Episode(url,title)), ) {
null, plot = document.select("div.iltext").text()
null, .substringAfter("Episode Description:")
description, .substringBefore("Share:")
null, }
null
)
} }
} }
data class LinkResponse( data class LinkResponse(
// @JsonProperty("cdn") // @JsonProperty("cdn")
// val cdn: String, // val cdn: String,
@JsonProperty("enc") @JsonProperty("enc") val enc: String,
val enc: String, @JsonProperty("hd") val hd: String,
@JsonProperty("hd") @JsonProperty("fhd") val fhd: String,
val hd: String, @JsonProperty("server") val server: String,
@JsonProperty("server")
val server: String,
) )
override suspend fun loadLinks( override suspend fun loadLinks(
@ -187,43 +201,54 @@ class WatchCartoonOnlineProvider : MainAPI() {
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
val response = app.get(data).text val document = app.get(data).document
/*val embedUrl = fixUrl( val foundJS = document.select("div.iltext > script").html()
Regex("itemprop=\"embedURL\" content=\"(.*?)\"").find(response.text)?.groupValues?.get(1) ?: return false
)*/
val start = response.indexOf("itemprop=\"embedURL")
val foundJS = Regex("<script>(.*?)</script>").find(response, start)?.groupValues?.get(1)
?.replace("document.write", "var returnValue = ")
val rhino = Context.enter() // Find the variable name, eg: var HAi = "";
rhino.initStandardObjects() val varRegex = Regex("""var (\S*)""")
rhino.optimizationLevel = -1
val scope: Scriptable = rhino.initStandardObjects()
val decodeBase64 = "atob = function(s) {\n" + val varName = varRegex.find(foundJS)?.groupValues?.get(1)
" var e={},i,b=0,c,x,l=0,a,r='',w=String.fromCharCode,L=s.length;\n" + ?: throw RuntimeException("Cannot find var name!")
" var A=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n" +
" for(i=0;i<64;i++){e[A.charAt(i)]=i;}\n" +
" for(x=0;x<L;x++){\n" +
" c=e[s.charAt(x)];b=(b<<6)+c;l+=6;\n" +
" while(l>=8){((a=(b>>>(l-=8))&0xff)||(x<(L-2)))&&(r+=w(a));}\n" +
" }\n" +
" return r;\n" +
"};"
rhino.evaluateString(scope, decodeBase64 + foundJS, "JavaScript", 1, null) val src = document.select("iframe#frameNewAnimeuploads0").attr("src") ?: mainWork {
val jsEval = scope.get("returnValue", scope) ?: return false // Rhino needs to be on main
val src = fixUrl(Regex("src=\"(.*?)\"").find(jsEval as String)?.groupValues?.get(1) ?: return false) val rhino = Context.enter()
rhino.optimizationLevel = -1
val scope: Scriptable = rhino.initSafeStandardObjects()
val decodeBase64 = "atob = function(s) {\n" +
" var e={},i,b=0,c,x,l=0,a,r='',w=String.fromCharCode,L=s.length;\n" +
" var A=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n" +
" for(i=0;i<64;i++){e[A.charAt(i)]=i;}\n" +
" for(x=0;x<L;x++){\n" +
" c=e[s.charAt(x)];b=(b<<6)+c;l+=6;\n" +
" while(l>=8){((a=(b>>>(l-=8))&0xff)||(x<(L-2)))&&(r+=w(a));}\n" +
" }\n" +
" return r;\n" +
"};"
val documentJs = """
document = { write: function () {} };
""".trimIndent()
rhino.evaluateString(scope, documentJs + decodeBase64 + foundJS, "JavaScript", 1, null)
val jsEval = scope.get(varName, scope)?.toString()
?: throw RuntimeException("Cannot get the Rhino scope variable")
val url =
Regex("src=\"(.*?)\"").find(jsEval)?.groupValues?.get(1) ?: return@mainWork null
fixUrl(url)
} ?: return false
val embedResponse = app.get( val embedResponse = app.get(
(src), src,
headers = mapOf("Referer" to data) headers = mapOf("Referer" to src)
) )
val getVidLink = fixUrl( val getVidLink = fixUrl(
Regex("get\\(\"(.*?)\"").find(embedResponse.text)?.groupValues?.get(1) ?: return false Regex("getJSON\\(\"(.*?)\"").find(embedResponse.text)?.groupValues?.get(1)
?: return false
) )
val linkResponse = app.get(
val link = app.get(
getVidLink, headers = mapOf( getVidLink, headers = mapOf(
"sec-ch-ua" to "\"Chromium\";v=\"91\", \" Not;A Brand\";v=\"99\"", "sec-ch-ua" to "\"Chromium\";v=\"91\", \" Not;A Brand\";v=\"99\"",
"sec-ch-ua-mobile" to "?0", "sec-ch-ua-mobile" to "?0",
@ -236,34 +261,24 @@ class WatchCartoonOnlineProvider : MainAPI() {
"user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", "user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"cookie" to "countrytabs=0" "cookie" to "countrytabs=0"
) )
) ).parsed<LinkResponse>()
val link = parseJson<LinkResponse>(linkResponse.text) fun loadLink(server: String, link: String?, quality: Qualities) {
if (link.isNullOrBlank()) return
val hdLink = "${link.server}/getvid?evid=${link.hd}"
val sdLink = "${link.server}/getvid?evid=${link.enc}"
if (link.hd.isNotBlank())
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
this.name, this.name,
this.name + " HD",
hdLink,
"",
Qualities.P720.value
)
)
if (link.enc.isNotBlank())
callback.invoke(
ExtractorLink(
this.name, this.name,
this.name + " SD", "${server}/getvid?evid=${link}",
sdLink,
"", "",
Qualities.P480.value quality.value
) )
) )
}
loadLink(link.server, link.hd, Qualities.P720)
loadLink(link.server, link.enc, Qualities.P480)
loadLink(link.server, link.fhd, Qualities.P1080)
return true return true
} }

View File

@ -1,4 +1,3 @@
package com.lagradost package com.lagradost
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
@ -6,7 +5,7 @@ import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context import android.content.Context
@CloudstreamPlugin @CloudstreamPlugin
class WatchCartoonOnlineProviderPlugin: Plugin() { class WatchCartoonOnlineProviderPlugin : Plugin() {
override fun load(context: Context) { override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly. // All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(WatchCartoonOnlineProvider()) registerMainAPI(WatchCartoonOnlineProvider())