Merge remote-tracking branch 'origin/master'

This commit is contained in:
LagradOst 2021-09-25 15:01:09 +02:00
commit ef29d0efaa
7 changed files with 299 additions and 56 deletions

View file

@ -1,6 +1,5 @@
package com.lagradost.cloudstream3 package com.lagradost.cloudstream3
import android.app.Activity
import android.content.Context import android.content.Context
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.DeserializationFeature
@ -10,11 +9,10 @@ import com.lagradost.cloudstream3.animeproviders.*
import com.lagradost.cloudstream3.movieproviders.* import com.lagradost.cloudstream3.movieproviders.*
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import java.util.* import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashSet
const val USER_AGENT = const val USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
//val baseHeader = mapOf("User-Agent" to USER_AGENT) //val baseHeader = mapOf("User-Agent" to USER_AGENT)
val mapper = JsonMapper.builder().addModule(KotlinModule()) val mapper = JsonMapper.builder().addModule(KotlinModule())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!! .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!!
@ -44,7 +42,8 @@ object APIHolder {
WatchCartoonOnlineProvider(), WatchCartoonOnlineProvider(),
AllMoviesForYouProvider(), AllMoviesForYouProvider(),
AsiaFlixProvider(), AsiaFlixProvider(),
ThenosProvider() ThenosProvider(),
VidEmbedProvider()
) )
val restrictedApis = arrayListOf( val restrictedApis = arrayListOf(

View file

@ -1,34 +0,0 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import org.jsoup.Jsoup
class Shiro : ExtractorApi() {
override val name: String = "Shiro"
override val mainUrl: String = "https://cherry.subsplea.se"
override val requiresReferer = false
override fun getExtractorUrl(id: String): String {
return "$mainUrl/$id"
}
override fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val headers = mapOf("Referer" to "https://shiro.is/")
val res = khttp.get(url, headers = headers).text
Jsoup.parse(res).select("source").firstOrNull()?.attr("src")?.replace("&amp;", "?")?.let {
return listOf(
ExtractorLink(
name,
name,
it.replace(" ", "%20"),
"https://cherry.subsplea.se/",
// UHD to give top priority
Qualities.P2160.value
)
)
}
return null
}
}

View file

@ -1,19 +1,29 @@
package com.lagradost.cloudstream3.extractors package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.pmap import com.lagradost.cloudstream3.pmap
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.extractorApis import com.lagradost.cloudstream3.utils.extractorApis
import com.lagradost.cloudstream3.utils.getQualityFromName
import org.jsoup.Jsoup import org.jsoup.Jsoup
class Vidstream { /**
* overrideMainUrl is necessary for for other vidstream clones like vidembed.cc
* If they diverge it'd be better to make them separate.
* */
class Vidstream(overrideMainUrl: String? = null) {
val name: String = "Vidstream" val name: String = "Vidstream"
private val mainUrl: String = "https://gogo-stream.com" private val mainUrl: String = overrideMainUrl ?: "https://gogo-stream.com"
private fun getExtractorUrl(id: String): String { private fun getExtractorUrl(id: String): String {
return "$mainUrl/streaming.php?id=$id" return "$mainUrl/streaming.php?id=$id"
} }
private val normalApis = arrayListOf(Shiro(), MultiQuality()) private fun getDownloadUrl(id: String): String {
return "$mainUrl/download?id=$id"
}
private val normalApis = arrayListOf(MultiQuality())
// https://gogo-stream.com/streaming.php?id=MTE3NDg5 // https://gogo-stream.com/streaming.php?id=MTE3NDg5
fun getUrl(id: String, isCasting: Boolean = false, callback: (ExtractorLink) -> Unit): Boolean { fun getUrl(id: String, isCasting: Boolean = false, callback: (ExtractorLink) -> Unit): Boolean {
@ -23,22 +33,47 @@ class Vidstream {
val source = api.getSafeUrl(url) val source = api.getSafeUrl(url)
source?.forEach { callback.invoke(it) } source?.forEach { callback.invoke(it) }
} }
val extractorUrl = getExtractorUrl(id)
val url = getExtractorUrl(id) /** Stolen from GogoanimeProvider.kt extractor */
with(khttp.get(url)) { normalSafeApiCall {
val link = getDownloadUrl(id)
val page = khttp.get(link, headers = mapOf("Referer" to extractorUrl))
val pageDoc = Jsoup.parse(page.text)
val qualityRegex = Regex("(\\d+)P")
pageDoc.select(".dowload > a[download]").forEach {
val qual = if (it.text()
.contains("HDP")
) "1080" else qualityRegex.find(it.text())?.destructured?.component1().toString()
callback.invoke(
ExtractorLink(
this.name,
if (qual == "null") this.name else "${this.name} - " + qual + "p",
it.attr("href"),
page.url,
getQualityFromName(qual),
it.attr("href").contains(".m3u8")
)
)
}
}
with(khttp.get(extractorUrl)) {
val document = Jsoup.parse(this.text) val document = Jsoup.parse(this.text)
val primaryLinks = document.select("ul.list-server-items > li.linkserver") val primaryLinks = document.select("ul.list-server-items > li.linkserver")
//val extractedLinksList: MutableList<ExtractorLink> = mutableListOf() //val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
// All vidstream links passed to extractors // All vidstream links passed to extractors
primaryLinks.forEach { element -> primaryLinks.distinctBy { it.attr("data-video") }.forEach { element ->
val link = element.attr("data-video") val link = element.attr("data-video")
//val name = element.text() //val name = element.text()
// Matches vidstream links with extractors // Matches vidstream links with extractors
extractorApis.filter { !it.requiresReferer || !isCasting }.pmap { api -> extractorApis.filter { !it.requiresReferer || !isCasting }.pmap { api ->
if (link.startsWith(api.mainUrl)) { if (link.startsWith(api.mainUrl)) {
val extractedLinks = api.getSafeUrl(link, url) val extractedLinks = api.getSafeUrl(link, extractorUrl)
if (extractedLinks?.isNotEmpty() == true) { if (extractedLinks?.isNotEmpty() == true) {
extractedLinks.forEach { extractedLinks.forEach {
callback.invoke(it) callback.invoke(it)

View file

@ -45,6 +45,7 @@ class XStreamCdn : ExtractorApi() {
val newUrl = url.replace("$mainUrl/v/", "$mainUrl/api/source/") val newUrl = url.replace("$mainUrl/v/", "$mainUrl/api/source/")
val extractedLinksList: MutableList<ExtractorLink> = mutableListOf() val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
with(khttp.post(newUrl, headers = headers)) { with(khttp.post(newUrl, headers = headers)) {
if (this.text == """{"success":false,"data":"Video not found or has been removed"}""") return listOf()
mapper.readValue<ResponseJson?>(this.text)?.let { mapper.readValue<ResponseJson?>(this.text)?.let {
if (it.success && it.data != null) { if (it.success && it.data != null) {
it.data.forEach { data -> it.data.forEach { data ->

View file

@ -0,0 +1,243 @@
package com.lagradost.cloudstream3.movieproviders
import org.jsoup.Jsoup
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.extractors.Vidstream
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import java.util.*
import kotlin.collections.ArrayList
class VidEmbedProvider : MainAPI() {
override val mainUrl: String
get() = "https://vidembed.cc"
override val name: String
get() = "VidEmbed"
override val hasQuickSearch: Boolean
get() = false
override val hasMainPage: Boolean
get() = true
private fun fixUrl(url: String): String {
return if (url.startsWith("//")) {
"https:$url"
} else if (url.startsWith("/")) {
"$mainUrl$url"
} else {
url
}
}
override val supportedTypes: Set<TvType>
get() = setOf(TvType.Anime, TvType.AnimeMovie, TvType.TvSeries, TvType.Movie)
override fun search(query: String): ArrayList<SearchResponse> {
val link = "$mainUrl/search.html?keyword=$query"
val html = khttp.get(link).text
val soup = Jsoup.parse(html)
return ArrayList(soup.select(".listing.items > .video-block").map { li ->
val href = fixUrl(li.selectFirst("a").attr("href"))
val poster = li.selectFirst("img")?.attr("src")
val title = li.selectFirst(".name").text()
val year = li.selectFirst(".date")?.text()?.split("-")?.get(0)?.toIntOrNull()
TvSeriesSearchResponse(
if (!title.contains("Episode")) title else title.split("Episode")[0].trim(),
href,
this.name,
TvType.TvSeries,
poster, year,
null
)
})
}
override fun load(url: String): LoadResponse? {
val html = khttp.get(url).text
val soup = Jsoup.parse(html)
var title = soup.selectFirst("h1,h2,h3").text()
title = if (!title.contains("Episode")) title else title.split("Episode")[0].trim()
val description = soup.selectFirst(".post-entry")?.text()?.trim()
var poster: String? = null
val episodes = soup.select(".listing.items.lists > .video-block").withIndex().map { (index, li) ->
val epTitle = if (li.selectFirst(".name") != null)
if (li.selectFirst(".name").text().contains("Episode"))
"Episode " + li.selectFirst(".name").text().split("Episode")[1].trim()
else
li.selectFirst(".name").text()
else ""
val epThumb = li.selectFirst("img")?.attr("src")
val epDate = li.selectFirst(".meta > .date").text()
if (poster == null) {
poster = li.selectFirst("img")?.attr("onerror")?.split("=")?.get(1)?.replace(Regex("[';]"), "")
}
val epNum = Regex("""Episode (\d+)""").find(epTitle)?.destructured?.component1()?.toIntOrNull()
TvSeriesEpisode(
epTitle,
null,
epNum,
fixUrl(li.selectFirst("a").attr("href")),
epThumb,
epDate
)
}.reversed()
val year = if (episodes.isNotEmpty()) episodes.first().date?.split("-")?.get(0)?.toIntOrNull() else null
val tvType = if (episodes.size == 1 && episodes[0].name == title) TvType.Movie else TvType.TvSeries
return when (tvType) {
TvType.TvSeries -> {
TvSeriesLoadResponse(
title,
url,
this.name,
tvType,
episodes,
poster,
year,
description,
ShowStatus.Ongoing,
null,
null
)
}
TvType.Movie -> {
MovieLoadResponse(
title,
url,
this.name,
tvType,
episodes[0].data,
poster,
year,
description,
null,
null
)
}
else -> null
}
}
override fun getMainPage(): HomePageResponse? {
val urls = listOf(
mainUrl,
"$mainUrl/movies",
"$mainUrl/series",
"$mainUrl/recommended-series",
"$mainUrl/cinema-movies"
)
val homePageList = ArrayList<HomePageList>()
urls.pmap { url ->
val response = khttp.get(url, timeout = 20.0)
val document = Jsoup.parse(response.text)
document.select("div.main-inner")?.forEach {
val title = it.select(".widget-title").text().trim()
val elements = it.select(".video-block").map {
val link = fixUrl(it.select("a").attr("href"))
val image = it.select(".picture > img").attr("src")
val name = it.select("div.name").text().trim()
val isSeries = (name.contains("Season") || name.contains("Episode"))
if (isSeries) {
TvSeriesSearchResponse(
name,
link,
this.name,
TvType.TvSeries,
image,
null,
null,
)
} else {
MovieSearchResponse(
name,
link,
this.name,
TvType.Movie,
image,
null,
null,
)
}
}
homePageList.add(
HomePageList(
title, elements
)
)
}
}
return HomePageResponse(homePageList)
}
override fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val iframeLink = Jsoup.parse(khttp.get(data).text).selectFirst("iframe")?.attr("src") ?: return false
val vidstreamObject = Vidstream("https://vidembed.cc")
// https://vidembed.cc/streaming.php?id=MzUwNTY2&... -> MzUwNTY2
val id = Regex("""id=([^&]*)""").find(iframeLink)?.groupValues?.get(1)
if (id != null) {
vidstreamObject.getUrl(id, isCasting, callback)
}
val html = khttp.get(fixUrl(iframeLink)).text
val soup = Jsoup.parse(html)
val servers = soup.select(".list-server-items > .linkserver").mapNotNull { li ->
if (!li?.attr("data-video").isNullOrEmpty()) {
Pair(li.text(), fixUrl(li.attr("data-video")))
} else {
null
}
}
servers.forEach {
if (it.first.toLowerCase(Locale.ROOT).trim() == "beta server") {
// Group 1: link, Group 2: Label
val sourceRegex = Regex("""sources:[\W\w]*?file:\s*["'](.*?)["'][\W\w]*?label:\s*["'](.*?)["']""")
val trackRegex = Regex("""tracks:[\W\w]*?file:\s*["'](.*?)["'][\W\w]*?label:\s*["'](.*?)["']""")
val html = khttp.get(it.second, headers = mapOf("referer" to iframeLink)).text
sourceRegex.findAll(html).forEach { match ->
callback.invoke(
ExtractorLink(
this.name,
match.groupValues.getOrNull(2)?.let { "${this.name} $it" } ?: this.name,
match.groupValues[1],
it.second,
getQualityFromName(match.groupValues.getOrNull(2) ?: ""),
// Kinda risky
match.groupValues[1].endsWith(".m3u8"),
)
)
}
trackRegex.findAll(html).forEach { match ->
subtitleCallback.invoke(
SubtitleFile(
match.groupValues.getOrNull(2) ?: "Unknown",
match.groupValues[1]
)
)
}
}
}
return true
}
}

View file

@ -49,9 +49,9 @@ fun getPacked(string: String): String? {
return packedRegex.find(string)?.value return packedRegex.find(string)?.value
} }
fun getAndUnpack(string: String): String? { fun getAndUnpack(string: String): String {
val packedText = getPacked(string) val packedText = getPacked(string)
return JsUnpacker(packedText).unpack() return JsUnpacker(packedText).unpack() ?: string
} }
fun loadExtractor(url: String, referer: String?, callback: (ExtractorLink) -> Unit) { fun loadExtractor(url: String, referer: String?, callback: (ExtractorLink) -> Unit) {
@ -65,7 +65,6 @@ fun loadExtractor(url: String, referer: String?, callback: (ExtractorLink) -> Un
val extractorApis: Array<ExtractorApi> = arrayOf( val extractorApis: Array<ExtractorApi> = arrayOf(
//AllProvider(), //AllProvider(),
Shiro(),
WcoStream(), WcoStream(),
Mp4Upload(), Mp4Upload(),
StreamTape(), StreamTape(),

View file

@ -32,7 +32,7 @@ class JsUnpacker(packedJS: String?) {
val js = packedJS val js = packedJS
try { try {
var p = var p =
Pattern.compile("""}\s*\('(.*)',\s*(.*?),\s*(\d+),\s*'(.*?)'\.split\('\|'\)""", Pattern.DOTALL) Pattern.compile("""\}\s*\('(.*)',\s*(.*?),\s*(\d+),\s*'(.*?)'\.split\('\|'\)""", Pattern.DOTALL)
var m = p.matcher(js) var m = p.matcher(js)
if (m.find() && m.groupCount() == 4) { if (m.find() && m.groupCount() == 4) {
val payload = m.group(1).replace("\\'", "'") val payload = m.group(1).replace("\\'", "'")
@ -72,7 +72,7 @@ class JsUnpacker(packedJS: String?) {
return decoded.toString() return decoded.toString()
} }
} catch (e: Exception) { } catch (e: Exception) {
logError(e) // logError(e)
} }
return null return null
} }