forked from recloudstream/cloudstream
Merge pull request #2 from LagradOst/wcostream
Added Provider WcoStream, I will fix the rest
This commit is contained in:
commit
2f36a943fc
4 changed files with 280 additions and 1 deletions
|
@ -8,6 +8,7 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
import com.lagradost.cloudstream3.animeproviders.DubbedAnimeProvider
|
import com.lagradost.cloudstream3.animeproviders.DubbedAnimeProvider
|
||||||
import com.lagradost.cloudstream3.animeproviders.ShiroProvider
|
import com.lagradost.cloudstream3.animeproviders.ShiroProvider
|
||||||
import com.lagradost.cloudstream3.animeproviders.TenshiProvider
|
import com.lagradost.cloudstream3.animeproviders.TenshiProvider
|
||||||
|
import com.lagradost.cloudstream3.animeproviders.WcoProvider
|
||||||
import com.lagradost.cloudstream3.movieproviders.HDMProvider
|
import com.lagradost.cloudstream3.movieproviders.HDMProvider
|
||||||
import com.lagradost.cloudstream3.movieproviders.LookMovieProvider
|
import com.lagradost.cloudstream3.movieproviders.LookMovieProvider
|
||||||
import com.lagradost.cloudstream3.movieproviders.MeloMovieProvider
|
import com.lagradost.cloudstream3.movieproviders.MeloMovieProvider
|
||||||
|
@ -33,6 +34,7 @@ object APIHolder {
|
||||||
val apis = arrayListOf(
|
val apis = arrayListOf(
|
||||||
ShiroProvider(),
|
ShiroProvider(),
|
||||||
TenshiProvider(),
|
TenshiProvider(),
|
||||||
|
WcoProvider(),
|
||||||
MeloMovieProvider(),
|
MeloMovieProvider(),
|
||||||
DubbedAnimeProvider(),
|
DubbedAnimeProvider(),
|
||||||
HDMProvider(),
|
HDMProvider(),
|
||||||
|
|
|
@ -0,0 +1,213 @@
|
||||||
|
package com.lagradost.cloudstream3.animeproviders
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.extractors.WcoStream
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
|
||||||
|
class WcoProvider : MainAPI() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getType(t: String): TvType {
|
||||||
|
return if (t.contains("OVA") || t.contains("Special")) TvType.ONA
|
||||||
|
else if (t.contains("Movie")) TvType.Movie
|
||||||
|
else TvType.Anime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val mainUrl: String
|
||||||
|
get() = "https://wcostream.cc"
|
||||||
|
override val name: String
|
||||||
|
get() = "WCO Stream"
|
||||||
|
override val hasQuickSearch: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
|
||||||
|
private fun getSlug(href: String): String {
|
||||||
|
return href.replace("$mainUrl/anime/", "").replace("/", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fixAnimeLink(url: String): String {
|
||||||
|
val regex = "watch/([a-zA-Z\\-0-9]*)-episode".toRegex()
|
||||||
|
val (aniId) = regex.find(url)!!.destructured
|
||||||
|
return "$mainUrl/anime/$aniId"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseSearchPage(soup: Document): ArrayList<SearchResponse> {
|
||||||
|
val items = soup.select(".film_list-wrap > .flw-item")
|
||||||
|
if (items.isEmpty()) return ArrayList()
|
||||||
|
val returnValue = ArrayList<SearchResponse>()
|
||||||
|
for (i in items) {
|
||||||
|
val href = fixAnimeLink(i.selectFirst("a").attr("href"))
|
||||||
|
val img = fixUrl(i.selectFirst("img").attr("data-src"))
|
||||||
|
val title = i.selectFirst("img").attr("title")
|
||||||
|
val isDub = !i.select(".pick.film-poster-quality").isEmpty()
|
||||||
|
val year = i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(1)").text().toIntOrNull()
|
||||||
|
val type = i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(3)").text()
|
||||||
|
|
||||||
|
returnValue.add(
|
||||||
|
if (getType(type) == TvType.Movie) {
|
||||||
|
MovieSearchResponse(
|
||||||
|
title, href, getSlug(href), this.name, TvType.Movie, img, year
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
AnimeSearchResponse(
|
||||||
|
title,
|
||||||
|
href,
|
||||||
|
getSlug(href),
|
||||||
|
this.name,
|
||||||
|
TvType.Anime,
|
||||||
|
img,
|
||||||
|
year,
|
||||||
|
null,
|
||||||
|
EnumSet.of(if (isDub) DubStatus.Dubbed else DubStatus.Subbed),
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return returnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun search(query: String): ArrayList<SearchResponse> {
|
||||||
|
val url = "$mainUrl/search"
|
||||||
|
val response = khttp.get(url, params=mapOf("keyword" to query))
|
||||||
|
var document = Jsoup.parse(response.text)
|
||||||
|
val returnValue = parseSearchPage(document)
|
||||||
|
|
||||||
|
while (!document.select(".pagination").isEmpty()) {
|
||||||
|
val link = document.select("a.page-link[rel=\"next\"]")
|
||||||
|
if (!link.isEmpty()) {
|
||||||
|
val extraResponse = khttp.get(fixUrl(link[0].attr("href")))
|
||||||
|
document = Jsoup.parse(extraResponse.text)
|
||||||
|
returnValue.addAll(parseSearchPage(document))
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun quickSearch(query: String): ArrayList<SearchResponse> {
|
||||||
|
val returnValue: ArrayList<SearchResponse> = ArrayList()
|
||||||
|
|
||||||
|
val response = khttp.post(
|
||||||
|
"https://wcostream.cc/ajax/search",
|
||||||
|
data=mapOf("keyword" to query)
|
||||||
|
).jsonObject.getString("html") // I won't make a dataclass for this shit
|
||||||
|
val document = Jsoup.parse(response)
|
||||||
|
|
||||||
|
document.select("a.nav-item").forEach {
|
||||||
|
val title = it.selectFirst("img")?.attr("title").toString()
|
||||||
|
val img = it?.selectFirst("img")?.attr("src")
|
||||||
|
val href = it?.attr("href").toString()
|
||||||
|
val isDub = title.contains("(Dub)")
|
||||||
|
val filmInfo = it?.selectFirst(".film-infor")
|
||||||
|
val year = filmInfo?.select("span")?.get(0)?.text()?.toIntOrNull()
|
||||||
|
val type = filmInfo?.select("span")?.get(1)?.text().toString()
|
||||||
|
if (title != "null") {
|
||||||
|
returnValue.add(
|
||||||
|
if (getType(type) == TvType.Movie) {
|
||||||
|
MovieSearchResponse(
|
||||||
|
title, href, getSlug(href), this.name, TvType.Movie, img, year
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
AnimeSearchResponse(
|
||||||
|
title,
|
||||||
|
href,
|
||||||
|
getSlug(href),
|
||||||
|
this.name,
|
||||||
|
TvType.Anime,
|
||||||
|
img,
|
||||||
|
year,
|
||||||
|
null,
|
||||||
|
EnumSet.of(if (isDub) DubStatus.Dubbed else DubStatus.Subbed),
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun load(slug: String): LoadResponse? {
|
||||||
|
val url = "$mainUrl/anime/${slug}"
|
||||||
|
|
||||||
|
val response = khttp.get(url, timeout = 120.0)
|
||||||
|
val document = Jsoup.parse(response.text)
|
||||||
|
|
||||||
|
val japaneseTitle = document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(1)")
|
||||||
|
?.text()?.trim()?.replace("Other names:", "")?.trim()
|
||||||
|
|
||||||
|
val canonicalTitle = document.selectFirst("meta[name=\"title\"]")
|
||||||
|
?.attr("content")?.split("| W")?.get(0).toString()
|
||||||
|
|
||||||
|
val isDubbed = canonicalTitle.contains("Dub")
|
||||||
|
val episodeNodes = document.select(".tab-content .nav-item > a")
|
||||||
|
|
||||||
|
val episodes = ArrayList<AnimeEpisode>(episodeNodes?.map {
|
||||||
|
AnimeEpisode(it.attr("href"))
|
||||||
|
}
|
||||||
|
?: ArrayList<AnimeEpisode>())
|
||||||
|
val statusElem = document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(2)")
|
||||||
|
val status = when (statusElem?.text()?.replace("Status:", "")?.trim()) {
|
||||||
|
"Ongoing" -> ShowStatus.Ongoing
|
||||||
|
"Completed" -> ShowStatus.Completed
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
val yearText = document.selectFirst("div.elements div.row > div:nth-child(2) > div.row-line:nth-child(4)")?.text()
|
||||||
|
val year = yearText?.replace("Date release:", "")?.trim()?.split("-")?.get(0)?.toIntOrNull()
|
||||||
|
|
||||||
|
val poster = document.selectFirst(".film-poster-img")?.attr("src")
|
||||||
|
val type = document.selectFirst("span.item.mr-1 > a")?.text()?.trim()
|
||||||
|
|
||||||
|
val synopsis = document.selectFirst(".description > p")?.text()?.trim()
|
||||||
|
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,
|
||||||
|
"$mainUrl/anime/${slug}",
|
||||||
|
this.name,
|
||||||
|
WcoProvider.getType(type ?: ""),
|
||||||
|
poster,
|
||||||
|
year,
|
||||||
|
null,
|
||||||
|
episodes,
|
||||||
|
status,
|
||||||
|
synopsis,
|
||||||
|
ArrayList(genre),
|
||||||
|
ArrayList(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
|
val response = khttp.get(data)
|
||||||
|
val servers = Jsoup.parse(response.text).select("#servers-list > ul > li").map {
|
||||||
|
mapOf(
|
||||||
|
"link" to it?.selectFirst("a")?.attr("data-embed"),
|
||||||
|
"title" to it?.selectFirst("span")?.text()?.trim()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (server in servers) {
|
||||||
|
WcoStream().getUrl(server["link"].toString(), "").forEach {
|
||||||
|
callback.invoke(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.utils
|
||||||
import com.lagradost.cloudstream3.utils.extractors.MixDrop
|
import com.lagradost.cloudstream3.utils.extractors.MixDrop
|
||||||
import com.lagradost.cloudstream3.utils.extractors.Mp4Upload
|
import com.lagradost.cloudstream3.utils.extractors.Mp4Upload
|
||||||
import com.lagradost.cloudstream3.utils.extractors.Shiro
|
import com.lagradost.cloudstream3.utils.extractors.Shiro
|
||||||
|
import com.lagradost.cloudstream3.utils.extractors.WcoStream
|
||||||
import com.lagradost.cloudstream3.utils.extractors.StreamTape
|
import com.lagradost.cloudstream3.utils.extractors.StreamTape
|
||||||
import com.lagradost.cloudstream3.utils.extractors.XStreamCdn
|
import com.lagradost.cloudstream3.utils.extractors.XStreamCdn
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ fun getAndUnpack(string: String): String? {
|
||||||
val extractorApis: Array<ExtractorApi> = arrayOf(
|
val extractorApis: Array<ExtractorApi> = arrayOf(
|
||||||
//AllProvider(),
|
//AllProvider(),
|
||||||
Shiro(),
|
Shiro(),
|
||||||
|
WcoStream(),
|
||||||
Mp4Upload(),
|
Mp4Upload(),
|
||||||
StreamTape(),
|
StreamTape(),
|
||||||
MixDrop(),
|
MixDrop(),
|
||||||
|
@ -85,4 +87,4 @@ abstract class ExtractorApi {
|
||||||
open fun getExtractorUrl(id: String): String {
|
open fun getExtractorUrl(id: String): String {
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
package com.lagradost.cloudstream3.utils.extractors
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import com.lagradost.cloudstream3.utils.*
|
||||||
|
import com.lagradost.cloudstream3.mapper
|
||||||
|
|
||||||
|
class WcoStream : ExtractorApi() {
|
||||||
|
override val name: String = "WcoStream"
|
||||||
|
override val mainUrl: String = "https://vidstream.pro"
|
||||||
|
override val requiresReferer = false
|
||||||
|
|
||||||
|
override fun getUrl(url: String, referer: String?): List<ExtractorLink> {
|
||||||
|
try {
|
||||||
|
val baseUrl = url.split("/e/")[0]
|
||||||
|
|
||||||
|
val html = khttp.get(url, headers=mapOf("Referer" to "https://wcostream.cc/")).text
|
||||||
|
val (Id) = "/e/(.*?)?domain".toRegex().find(url)!!.destructured
|
||||||
|
val (skey) = """skey\s=\s['\"](.*?)['\"];""".toRegex().find(html)!!.destructured
|
||||||
|
|
||||||
|
val apiLink = "$baseUrl/info/$Id?domain=wcostream.cc&skey=$skey"
|
||||||
|
val referrer = "$baseUrl/e/$Id?domain=wcostream.cc"
|
||||||
|
|
||||||
|
val response = khttp.get(apiLink, headers=mapOf("Referer" to referrer)).text
|
||||||
|
|
||||||
|
data class Sources (
|
||||||
|
@JsonProperty("file") val file : String,
|
||||||
|
@JsonProperty("label") val label : String
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Media (
|
||||||
|
@JsonProperty("sources") val sources : List<Sources>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class WcoResponse (
|
||||||
|
@JsonProperty("success") val success : Boolean,
|
||||||
|
@JsonProperty("media") val media : Media
|
||||||
|
)
|
||||||
|
|
||||||
|
val mapped = response.let { mapper.readValue<WcoResponse>(it) }
|
||||||
|
val sources = mutableListOf<ExtractorLink>()
|
||||||
|
|
||||||
|
if (mapped.success) {
|
||||||
|
mapped.media.sources.forEach {
|
||||||
|
sources.add(
|
||||||
|
ExtractorLink(
|
||||||
|
"WcoStream",
|
||||||
|
"WcoStream" + "- ${it.label}",
|
||||||
|
it.file,
|
||||||
|
"",
|
||||||
|
Qualities.HD.value,
|
||||||
|
it.file.contains(".m3u8")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sources
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return listOf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue