added subtitles to ExtractorApi

This commit is contained in:
LagradOst 2022-07-27 19:36:31 +02:00
parent a7e29dd8d5
commit 47f4e10078
69 changed files with 621 additions and 442 deletions

View file

@ -899,6 +899,11 @@ data class TvSeriesSearchResponse(
override var posterHeaders: Map<String, String>? = null,
) : SearchResponse
data class TrailerData(
var mirros: List<ExtractorLink>,
var subtitles: List<SubtitleFile> = emptyList(),
)
interface LoadResponse {
var name: String
var url: String
@ -910,7 +915,8 @@ interface LoadResponse {
var rating: Int? // 0-10000
var tags: List<String>?
var duration: Int? // in minutes
var trailers: List<ExtractorLink>?
var trailers: MutableList<TrailerData>
var recommendations: List<SearchResponse>?
var actors: List<ActorData>?
var comingSoon: Boolean
@ -965,35 +971,25 @@ interface LoadResponse {
/**better to call addTrailer with mutible trailers directly instead of calling this multiple times*/
suspend fun LoadResponse.addTrailer(trailerUrl: String?, referer: String? = null) {
if (!isTrailersEnabled || trailerUrl == null) return
try {
val newTrailers = loadExtractor(trailerUrl, referer)
addTrailer(newTrailers)
} catch (e: Exception) {
logError(e)
}
val links = arrayListOf<ExtractorLink>()
val subs = arrayListOf<SubtitleFile>()
loadExtractor(trailerUrl, referer, { subs.add(it) }, { links.add(it) })
this.trailers.add(TrailerData(links, subs))
}
fun LoadResponse.addTrailer(newTrailers: List<ExtractorLink>) {
if (this.trailers == null) {
this.trailers = newTrailers
} else {
val update = this.trailers?.toMutableList() ?: mutableListOf()
update.addAll(newTrailers)
this.trailers = update
}
trailers.addAll(newTrailers.map { TrailerData(listOf(it)) })
}
suspend fun LoadResponse.addTrailer(trailerUrls: List<String>?, referer: String? = null) {
if (!isTrailersEnabled || trailerUrls == null) return
val newTrailers = trailerUrls.apmap { trailerUrl ->
try {
loadExtractor(trailerUrl, referer)
} catch (e: Exception) {
logError(e)
emptyList()
}
}.flatten().distinct()
addTrailer(newTrailers)
val trailers = trailerUrls.apmap { trailerUrl ->
val links = arrayListOf<ExtractorLink>()
val subs = arrayListOf<SubtitleFile>()
loadExtractor(trailerUrl, referer, { subs.add(it) }, { links.add(it) })
links to subs
}.map { (links, subs) -> TrailerData(links, subs) }
this.trailers.addAll(trailers)
}
fun LoadResponse.addImdbId(id: String?) {
@ -1087,7 +1083,7 @@ data class TorrentLoadResponse(
override var rating: Int? = null,
override var tags: List<String>? = null,
override var duration: Int? = null,
override var trailers: List<ExtractorLink>? = null,
override var trailers: MutableList<TrailerData> = mutableListOf(),
override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false,
@ -1115,7 +1111,7 @@ data class AnimeLoadResponse(
override var rating: Int? = null,
override var duration: Int? = null,
override var trailers: List<ExtractorLink>? = null,
override var trailers: MutableList<TrailerData> = mutableListOf(),
override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false,
@ -1163,7 +1159,7 @@ data class LiveStreamLoadResponse(
override var rating: Int? = null,
override var tags: List<String>? = null,
override var duration: Int? = null,
override var trailers: List<ExtractorLink>? = null,
override var trailers: MutableList<TrailerData> = mutableListOf(),
override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false,
@ -1185,7 +1181,7 @@ data class MovieLoadResponse(
override var rating: Int? = null,
override var tags: List<String>? = null,
override var duration: Int? = null,
override var trailers: List<ExtractorLink>? = null,
override var trailers: MutableList<TrailerData> = mutableListOf(),
override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false,
@ -1306,7 +1302,7 @@ data class TvSeriesLoadResponse(
override var rating: Int? = null,
override var tags: List<String>? = null,
override var duration: Int? = null,
override var trailers: List<ExtractorLink>? = null,
override var trailers: MutableList<TrailerData> = mutableListOf(),
override var recommendations: List<SearchResponse>? = null,
override var actors: List<ActorData>? = null,
override var comingSoon: Boolean = false,

View file

@ -56,7 +56,8 @@ class AnimeFlickProvider : MainAPI() {
val title = doc.selectFirst("h2.title")!!.text()
val yearText = doc.selectFirst(".trending-year")?.text()
val year = if (yearText != null) Regex("""(\d{4})""").find(yearText)?.destructured?.component1()
val year =
if (yearText != null) Regex("""(\d{4})""").find(yearText)?.destructured?.component1()
?.toIntOrNull() else null
val description = doc.selectFirst("p")?.text()
@ -95,7 +96,7 @@ class AnimeFlickProvider : MainAPI() {
var alreadyAdded = false
for (extractor in extractorApis) {
if (link.startsWith(extractor.mainUrl)) {
extractor.getSafeUrl(link, data)?.forEach(callback)
extractor.getSafeUrl(link, data, subtitleCallback, callback)
alreadyAdded = true
break
}

View file

@ -8,7 +8,6 @@ import com.lagradost.cloudstream3.utils.loadExtractor
import com.lagradost.nicehttp.NiceResponse
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.util.ArrayList
class AnimeIndoProvider : MainAPI() {
override var mainUrl = "https://animeindo.sbs"
@ -183,7 +182,7 @@ class AnimeIndoProvider : MainAPI() {
it
}
}.apmap {
loadExtractor(it, data, callback)
loadExtractor(it, data, subtitleCallback, callback)
}
return true

View file

@ -112,7 +112,8 @@ class AnimeSailProvider : MainAPI() {
)
val episodes = document.select("ul.daftar > li").map {
val header = it.select("a").text().trim()
val name = Regex("(Episode\\s?[0-9]+)").find(header)?.groupValues?.getOrNull(0) ?: header
val name =
Regex("(Episode\\s?[0-9]+)").find(header)?.groupValues?.getOrNull(0) ?: header
val link = fixUrl(it.select("a").attr("href"))
Episode(link, name = name)
}.reversed()
@ -157,7 +158,8 @@ class AnimeSailProvider : MainAPI() {
iframe.contains("/race/") -> "Race"
else -> this.name
}
val quality = Regex("\\.([0-9]{3,4})\\.").find(link)?.groupValues?.get(1)
val quality =
Regex("\\.([0-9]{3,4})\\.").find(link)?.groupValues?.get(1)
callback.invoke(
ExtractorLink(
source = source,
@ -174,11 +176,11 @@ class AnimeSailProvider : MainAPI() {
iframe.startsWith("$mainUrl/utils/player/framezilla/") || iframe.startsWith("https://uservideo.xyz") -> {
request(iframe, ref = data).document.select("iframe").attr("src")
.let { link ->
loadExtractor(fixUrl(link), mainUrl, callback)
loadExtractor(fixUrl(link), mainUrl, subtitleCallback, callback)
}
}
else -> {
loadExtractor(iframe, mainUrl, callback)
loadExtractor(iframe, mainUrl, subtitleCallback, callback)
}
}
}

View file

@ -2,11 +2,12 @@ package com.lagradost.cloudstream3.animeproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import java.util.*
import kotlin.collections.ArrayList
class AnimefenixProvider:MainAPI() {
@ -179,7 +180,7 @@ class AnimefenixProvider:MainAPI() {
else -> ""
}
loadExtractor(links, data, callback)
loadExtractor(links, data, subtitleCallback, callback)
argamap({
if (links.contains("AmaNormal")) {

View file

@ -2,11 +2,13 @@ package com.lagradost.cloudstream3.movieproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import java.util.*
import kotlin.collections.ArrayList
class AnimeflvIOProvider:MainAPI() {
override var mainUrl = "https://animeflv.io" //Also scrapes from animeid.to
@ -220,7 +222,7 @@ class AnimeflvIOProvider:MainAPI() {
}
}
}
loadExtractor(url, data, callback)
loadExtractor(url, data, subtitleCallback, callback)
}
return true
}

View file

@ -173,7 +173,7 @@ class AnimeflvnetProvider : MainAPI() {
it.replace("https://embedsb.com/e/", "https://watchsb.com/e/")
.replace("https://ok.ru", "http://ok.ru")
}.apmap {
loadExtractor(it, data, callback)
loadExtractor(it, data, subtitleCallback, callback)
}
}
}

View file

@ -2,8 +2,6 @@ package com.lagradost.cloudstream3.animeproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
@ -126,7 +124,7 @@ class AnimekisaProvider : MainAPI() {
): Boolean {
app.get(data).document.select("#servers-list ul.nav li a").apmap {
val server = it.attr("data-embed")
loadExtractor(server, data, callback)
loadExtractor(server, data, subtitleCallback, callback)
}
return true
}

View file

@ -338,7 +338,11 @@ class GogoanimeProvider : MainAPI() {
@JsonProperty("default") val default: String? = null
)
private suspend fun extractVideos(uri: String, callback: (ExtractorLink) -> Unit) {
private suspend fun extractVideos(
uri: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val doc = app.get(uri).document
val iframe = fixUrlNull(doc.selectFirst("div.play-video > iframe")?.attr("src")) ?: return
@ -366,7 +370,7 @@ class GogoanimeProvider : MainAPI() {
)
} else {
val url = it.attr("href")
loadExtractor(url, null, callback)
loadExtractor(url, null, subtitleCallback, callback)
}
}
}, {
@ -378,7 +382,7 @@ class GogoanimeProvider : MainAPI() {
val status = element.attr("data-status") ?: return@forEach
if (status != "1") return@forEach
val data = element.attr("data-video") ?: return@forEach
loadExtractor(data, streamingResponse.url, callback)
loadExtractor(data, streamingResponse.url, subtitleCallback, callback)
}
}, {
val iv = "3134003223491201"
@ -405,7 +409,7 @@ class GogoanimeProvider : MainAPI() {
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
extractVideos(data, callback)
extractVideos(data, subtitleCallback, callback)
return true
}
}

View file

@ -145,7 +145,9 @@ class GomunimeProvider : MainAPI() {
document.select(".bixbox.bxcl.epcheck > script").toString().trim()
)?.groupValues?.get(1).toString().replace(Regex("""\\"""), "").trim()
).map {
val name = Regex("(Episode\\s?[0-9]+)").find(it.epTitle.toString())?.groupValues?.getOrNull(0) ?: it.epTitle
val name =
Regex("(Episode\\s?[0-9]+)").find(it.epTitle.toString())?.groupValues?.getOrNull(0)
?: it.epTitle
val link = it.epLink
Episode(link, name)
}.reversed()
@ -194,7 +196,7 @@ class GomunimeProvider : MainAPI() {
safeApiCall {
when {
it.second.contains("frame") -> {
loadExtractor(it.first, data, callback)
loadExtractor(it.first, data, subtitleCallback, callback)
}
it.second.contains("hls") -> {
app.post(

View file

@ -3,12 +3,12 @@ package com.lagradost.cloudstream3.animeproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.List
class JKAnimeProvider : MainAPI() {
@ -34,9 +34,18 @@ class JKAnimeProvider : MainAPI() {
override suspend fun getMainPage(): HomePageResponse {
val urls = listOf(
Pair("$mainUrl/directorio/?filtro=fecha&tipo=TV&estado=1&fecha=none&temporada=none&orden=desc", "En emisión"),
Pair("$mainUrl/directorio/?filtro=fecha&tipo=none&estado=none&fecha=none&temporada=none&orden=none", "Animes"),
Pair("$mainUrl/directorio/?filtro=fecha&tipo=Movie&estado=none&fecha=none&temporada=none&orden=none", "Películas"),
Pair(
"$mainUrl/directorio/?filtro=fecha&tipo=TV&estado=1&fecha=none&temporada=none&orden=desc",
"En emisión"
),
Pair(
"$mainUrl/directorio/?filtro=fecha&tipo=none&estado=none&fecha=none&temporada=none&orden=none",
"Animes"
),
Pair(
"$mainUrl/directorio/?filtro=fecha&tipo=Movie&estado=none&fecha=none&temporada=none&orden=none",
"Películas"
),
)
val items = ArrayList<HomePageList>()
@ -48,10 +57,12 @@ class JKAnimeProvider : MainAPI() {
val title = it.selectFirst("h5")?.text()
val dubstat = if (title!!.contains("Latino") || title.contains("Castellano"))
DubStatus.Dubbed else DubStatus.Subbed
val poster = it.selectFirst(".anime__sidebar__comment__item__pic img")?.attr("src") ?: ""
val poster =
it.selectFirst(".anime__sidebar__comment__item__pic img")?.attr("src") ?: ""
val epRegex = Regex("/(\\d+)/|/especial/|/ova/")
val url = it.attr("href").replace(epRegex, "")
val epNum = it.selectFirst("h6")?.text()?.replace("Episodio ", "")?.toIntOrNull()
val epNum =
it.selectFirst("h6")?.text()?.replace("Episodio ", "")?.toIntOrNull()
newAnimeSearchResponse(title, url) {
this.posterUrl = poster
addDubStatus(dubstat, epNum)
@ -134,7 +145,8 @@ class JKAnimeProvider : MainAPI() {
val title = doc.selectFirst(".anime__details__title > h3")?.text()
val type = doc.selectFirst(".anime__details__text")?.text()
val description = doc.selectFirst(".anime__details__text > p")?.text()
val genres = doc.select("div.col-lg-6:nth-child(1) > ul:nth-child(1) > li:nth-child(2) > a").map { it.text() }
val genres = doc.select("div.col-lg-6:nth-child(1) > ul:nth-child(1) > li:nth-child(2) > a")
.map { it.text() }
val status = when (doc.selectFirst("span.enemision")?.text()) {
"En emisión" -> ShowStatus.Ongoing
"Concluido" -> ShowStatus.Completed
@ -143,7 +155,8 @@ class JKAnimeProvider : MainAPI() {
val animeID = doc.selectFirst("div.ml-2")?.attr("data-anime")?.toInt()
val animeeps = "$mainUrl/ajax/last_episode/$animeID/"
val jsoneps = app.get(animeeps).text
val lastepnum = jsoneps.substringAfter("{\"number\":\"").substringBefore("\",\"title\"").toInt()
val lastepnum =
jsoneps.substringAfter("{\"number\":\"").substringBefore("\",\"title\"").toInt()
val episodes = (1..lastepnum).map {
val link = "${url.removeSuffix("/")}/$it"
Episode(link)
@ -198,11 +211,12 @@ class JKAnimeProvider : MainAPI() {
.replace("$mainUrl/jkvmixdrop.php?u=", "https://mixdrop.co/e/")
.replace("$mainUrl/jk.php?u=", "$mainUrl/")
}.apmap { link ->
loadExtractor(link, data, callback)
loadExtractor(link, data, subtitleCallback, callback)
if (link.contains("um2.php")) {
val doc = app.get(link, referer = data).document
val gsplaykey = doc.select("form input[value]").attr("value")
val postgsplay = app.post("$mainUrl/gsplay/redirect_post.php",
app.post(
"$mainUrl/gsplay/redirect_post.php",
headers = mapOf(
"Host" to "jkanime.net",
"User-Agent" to USER_AGENT,
@ -219,11 +233,14 @@ class JKAnimeProvider : MainAPI() {
"Sec-Fetch-Site" to "same-origin",
"TE" to "trailers",
"Pragma" to "no-cache",
"Cache-Control" to "no-cache",),
"Cache-Control" to "no-cache",
),
data = mapOf(Pair("data", gsplaykey)),
allowRedirects = false).okhttpResponse.headers.values("location").apmap { loc ->
allowRedirects = false
).okhttpResponse.headers.values("location").apmap { loc ->
val postkey = loc.replace("/gsplay/player.html#", "")
val nozomitext = app.post("$mainUrl/gsplay/api.php",
val nozomitext = app.post(
"$mainUrl/gsplay/api.php",
headers = mapOf(
"Host" to "jkanime.net",
"User-Agent" to USER_AGENT,
@ -236,7 +253,8 @@ class JKAnimeProvider : MainAPI() {
"Connection" to "keep-alive",
"Sec-Fetch-Dest" to "empty",
"Sec-Fetch-Mode" to "cors",
"Sec-Fetch-Site" to "same-origin",),
"Sec-Fetch-Site" to "same-origin",
),
data = mapOf(Pair("v", postkey)),
allowRedirects = false
).text
@ -245,13 +263,20 @@ class JKAnimeProvider : MainAPI() {
if (nozomiurl.isEmpty()) null else
nozomiurl.forEach { url ->
val nozominame = "Nozomi"
streamClean(nozominame, url!!, "", null, callback, url.contains(".m3u8"))
streamClean(
nozominame,
url!!,
"",
null,
callback,
url.contains(".m3u8")
)
}
}
}
if (link.contains("um.php")) {
val desutext = app.get(link, referer = data).text
val desuRegex = Regex("((https:|http:)\\/\\/.*\\.m3u8)")
val desuRegex = Regex("((https:|http:)//.*\\.m3u8)")
val file = desuRegex.find(desutext)?.value
val namedesu = "Desu"
generateM3u8(
@ -259,13 +284,31 @@ class JKAnimeProvider : MainAPI() {
file!!,
mainUrl,
).forEach { desurl ->
streamClean(namedesu, desurl.url, mainUrl, desurl.quality.toString(), callback, true)
streamClean(
namedesu,
desurl.url,
mainUrl,
desurl.quality.toString(),
callback,
true
)
}
}
if (link.contains("jkmedia")) {
app.get(link, referer = data, allowRedirects = false).okhttpResponse.headers.values("location").apmap { xtremeurl ->
app.get(
link,
referer = data,
allowRedirects = false
).okhttpResponse.headers.values("location").apmap { xtremeurl ->
val namex = "Xtreme S"
streamClean(namex, xtremeurl, "", null, callback, xtremeurl.contains(".m3u8"))
streamClean(
namex,
xtremeurl,
"",
null,
callback,
xtremeurl.contains(".m3u8")
)
}
}
}

View file

@ -3,7 +3,6 @@ package com.lagradost.cloudstream3.animeproviders
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
class KimCartoonProvider : MainAPI() {
@ -143,7 +142,7 @@ class KimCartoonProvider : MainAPI() {
servers.apmap {
app.get(it).document.select("#my_video_1").attr("src").let { iframe ->
if (iframe.isNotEmpty()) {
loadExtractor(iframe, "$mainUrl/", callback)
loadExtractor(iframe, "$mainUrl/", subtitleCallback, callback)
}
//There are other servers, but they require some work to do
}

View file

@ -180,7 +180,7 @@ class KuronimeProvider : MainAPI() {
safeApiCall {
when {
it.startsWith("https://animeku.org") -> invokeKuroSource(it, callback)
else -> loadExtractor(it, mainUrl, callback)
else -> loadExtractor(it, mainUrl, subtitleCallback, callback)
}
}
}

View file

@ -148,7 +148,7 @@ class MonoschinosProvider : MainAPI() {
callback.invoke(link)
}
} else {
loadExtractor(url, mainUrl, callback)
loadExtractor(url, mainUrl, subtitleCallback, callback)
}
}
return true

View file

@ -163,7 +163,7 @@ class MundoDonghuaProvider : MainAPI() {
}.toList().apmap {
val unpack = getAndUnpack(it).replace("diasfem","embedsito")
fetchUrls(unpack).apmap { url ->
loadExtractor(url, data, callback)
loadExtractor(url, data, subtitleCallback, callback)
}
if (unpack.contains("protea_tab")) {
val protearegex = Regex("(protea_tab.*slug.*,type)")

View file

@ -169,7 +169,7 @@ class NeonimeProvider : MainAPI() {
}
source.apmap {
loadExtractor(it, data, callback)
loadExtractor(it, data, subtitleCallback, callback)
}
return true

View file

@ -339,7 +339,7 @@ class NineAnimeProvider : MainAPI() {
val encodedStreamUrl =
getEpisodeLinks(it.attr("data-link-id"))?.result?.url ?: return@apmap
val url = decodeVrf(encodedStreamUrl)
if (!loadExtractor(url, callback = callback, referer = mainUrl)) {
if (!loadExtractor(url, mainUrl, subtitleCallback, callback)) {
callback(
ExtractorLink(
this.name,

View file

@ -3,10 +3,10 @@ package com.lagradost.cloudstream3.animeproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.util.ArrayList
class NontonAnimeIDProvider : MainAPI() {
override var mainUrl = "https://75.119.159.228"
@ -175,13 +175,17 @@ class NontonAnimeIDProvider : MainAPI() {
)
).parsed<EpResponse>().content
).select("li").map {
val name = Regex("(Episode\\s?[0-9]+)").find(it.selectFirst("a")?.text().toString())?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text()
val name = Regex("(Episode\\s?[0-9]+)").find(
it.selectFirst("a")?.text().toString()
)?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text()
val link = fixUrl(it.selectFirst("a")!!.attr("href"))
Episode(link, name)
}.reversed()
} else {
document.select("ul.misha_posts_wrap2 > li").map {
val name = Regex("(Episode\\s?[0-9]+)").find(it.selectFirst("a")?.text().toString())?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text()
val name = Regex("(Episode\\s?[0-9]+)").find(
it.selectFirst("a")?.text().toString()
)?.groupValues?.getOrNull(0) ?: it.selectFirst("a")?.text()
val link = it.select("a").attr("href")
Episode(link, name)
}.reversed()
@ -243,7 +247,7 @@ class NontonAnimeIDProvider : MainAPI() {
}
sources.apmap {
loadExtractor(it, "$mainUrl/", callback)
loadExtractor(it, "$mainUrl/", subtitleCallback, callback)
}
return true

View file

@ -193,7 +193,7 @@ class OploverzProvider : MainAPI() {
}
sources.apmap {
loadExtractor(it, data, callback)
loadExtractor(it, data, subtitleCallback, callback)
}
return true

View file

@ -189,7 +189,7 @@ class OtakudesuProvider : MainAPI() {
}
}
loadExtractor(sources, data, callback)
loadExtractor(sources, data, subtitleCallback, callback)
}

View file

@ -56,7 +56,8 @@ class WcoProvider : MainAPI() {
nameHeader.attr("href").replace("/watch/", "/anime/")
.replace(Regex("-episode-.*"), "/")
val isDub =
filmPoster!!.selectFirst("> div.film-poster-quality")?.text()?.contains("DUB")
filmPoster!!.selectFirst("> div.film-poster-quality")?.text()
?.contains("DUB")
?: false
val poster = filmPoster.selectFirst("> img")!!.attr("data-src")
val set: EnumSet<DubStatus> =
@ -231,8 +232,8 @@ class WcoProvider : MainAPI() {
}
for (server in servers) {
WcoStream().getSafeUrl(server["link"].toString(), "")?.forEach(callback)
Mcloud().getSafeUrl(server["link"].toString(), "")?.forEach(callback)
WcoStream().getSafeUrl(server["link"].toString(), null, subtitleCallback, callback)
Mcloud().getSafeUrl(server["link"].toString(), null, subtitleCallback, callback)
}
return true
}

View file

@ -60,7 +60,8 @@ class ZoroProvider : MainAPI() {
val dubExist = dubSub.contains("dub", ignoreCase = true)
val subExist = dubSub.contains("sub", ignoreCase = true)
val episodes = this.selectFirst(".film-poster > .tick.rtl > .tick-eps")?.text()?.let { eps ->
val episodes =
this.selectFirst(".film-poster > .tick.rtl > .tick-eps")?.text()?.let { eps ->
//println("REGEX:::: $eps")
// current episode / max episode
//Regex("Ep (\\d+)/(\\d+)")
@ -346,7 +347,7 @@ class ZoroProvider : MainAPI() {
link,
).parsed<RapidCloudResponse>().link
val hasLoadedExtractorLink =
loadExtractor(extractorLink, "https://rapid-cloud.ru/", callback)
loadExtractor(extractorLink, "https://rapid-cloud.ru/", subtitleCallback, callback)
if (!hasLoadedExtractorLink) {
extractRabbitStream(

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
@ -27,12 +28,16 @@ class Pelisplus(val mainUrl: String) {
private val normalApis = arrayListOf(MultiQuality())
// https://gogo-stream.com/streaming.php?id=MTE3NDg5
suspend fun getUrl(id: String, isCasting: Boolean = false, callback: (ExtractorLink) -> Unit): Boolean {
suspend fun getUrl(
id: String,
isCasting: Boolean = false,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
try {
normalApis.apmap { api ->
val url = api.getExtractorUrl(id)
val source = api.getSafeUrl(url)
source?.forEach { callback.invoke(it) }
api.getSafeUrl(url, subtitleCallback = subtitleCallback, callback = callback)
}
val extractorUrl = getExtractorUrl(id)
@ -50,9 +55,10 @@ class Pelisplus(val mainUrl: String) {
val href = element.attr("href") ?: return@apmap
val qual = if (element.text()
.contains("HDP")
) "1080" else qualityRegex.find(element.text())?.destructured?.component1().toString()
) "1080" else qualityRegex.find(element.text())?.destructured?.component1()
.toString()
if (!loadExtractor(href, link, callback)) {
if (!loadExtractor(href, link, subtitleCallback, callback)) {
callback.invoke(
ExtractorLink(
this.name,
@ -80,12 +86,7 @@ class Pelisplus(val mainUrl: String) {
// Matches vidstream links with extractors
extractorApis.filter { !it.requiresReferer || !isCasting }.apmap { api ->
if (link.startsWith(api.mainUrl)) {
val extractedLinks = api.getSafeUrl(link, extractorUrl)
if (extractedLinks?.isNotEmpty() == true) {
extractedLinks.forEach {
callback.invoke(it)
}
}
api.getSafeUrl(link, extractorUrl, subtitleCallback, callback)
}
}
}

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
@ -12,10 +13,16 @@ class VidSrcExtractor : ExtractorApi() {
override val mainUrl = "https://v2.vidsrc.me"
override val requiresReferer = false
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val iframedoc = app.get(url).document
val serverslist = iframedoc.select("div#sources.button_content div#content div#list div").map {
val serverslist =
iframedoc.select("div#sources.button_content div#content div#list div").map {
val datahash = it.attr("data-hash")
if (datahash.isNotBlank()) {
val links = try {
@ -27,20 +34,21 @@ class VidSrcExtractor : ExtractorApi() {
} else ""
}
return serverslist.apmap { server ->
serverslist.apmap { server ->
val linkfixed = server.replace("https://vidsrc.xyz/", "https://embedsito.com/")
if (linkfixed.contains("/pro")) {
val srcresponse = app.get(server, referer = mainUrl).text
val m3u8Regex = Regex("((https:|http:)//.*\\.m3u8)")
val srcm3u8 = m3u8Regex.find(srcresponse)?.value ?: return@apmap listOf()
val srcm3u8 = m3u8Regex.find(srcresponse)?.value ?: return@apmap
M3u8Helper.generateM3u8(
name,
srcm3u8,
mainUrl
)
).forEach(callback)
} else {
loadExtractor(linkfixed, url)
}
}.flatten()
loadExtractor(linkfixed, url, subtitleCallback, callback)
}
}
}
}

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.argamap
@ -30,15 +31,19 @@ class Vidstream(val mainUrl: String) {
suspend fun getUrl(
id: String,
isCasting: Boolean = false,
callback: (ExtractorLink) -> Unit
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
): Boolean {
val extractorUrl = getExtractorUrl(id)
argamap(
{
normalApis.apmap { api ->
val url = api.getExtractorUrl(id)
val source = api.getSafeUrl(url)
source?.forEach { callback.invoke(it) }
api.getSafeUrl(
url,
callback = callback,
subtitleCallback = subtitleCallback
)
}
}, {
/** Stolen from GogoanimeProvider.kt extractor */
@ -57,7 +62,7 @@ class Vidstream(val mainUrl: String) {
) "1080" else qualityRegex.find(element.text())?.destructured?.component1()
.toString()
if (!loadExtractor(href, link, callback)) {
if (!loadExtractor(href, link, subtitleCallback, callback)) {
callback.invoke(
ExtractorLink(
this.name,
@ -84,12 +89,7 @@ class Vidstream(val mainUrl: String) {
// Matches vidstream links with extractors
extractorApis.filter { !it.requiresReferer || !isCasting }.apmap { api ->
if (link.startsWith(api.mainUrl)) {
val extractedLinks = api.getSafeUrl(link, extractorUrl)
if (extractedLinks?.isNotEmpty() == true) {
extractedLinks.forEach {
callback.invoke(it)
}
}
api.getSafeUrl(link, extractorUrl, subtitleCallback, callback)
}
}
}

View file

@ -1,14 +1,14 @@
package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.schemaStripRegex
import org.schabi.newpipe.extractor.ServiceList
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory
import org.schabi.newpipe.extractor.stream.SubtitlesStream
import org.schabi.newpipe.extractor.stream.VideoStream
class YoutubeShortLinkExtractor : YoutubeExtractor() {
@ -26,15 +26,20 @@ open class YoutubeExtractor : ExtractorApi() {
companion object {
private var ytVideos: MutableMap<String, List<VideoStream>> = mutableMapOf()
private var ytVideosSubtitles: MutableMap<String, List<SubtitlesStream>> = mutableMapOf()
}
override fun getExtractorUrl(id: String): String {
return "$mainUrl/watch?v=$id"
}
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
val streams = safeApiCall {
val streams = ytVideos[url] ?: let {
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
if (ytVideos[url].isNullOrEmpty()) {
val link =
YoutubeStreamLinkHandlerFactory.getInstance().fromUrl(
url.replace(
@ -49,21 +54,15 @@ open class YoutubeExtractor : ExtractorApi() {
}
s.fetchPage()
val streams = s.videoStreams ?: return@let emptyList()
ytVideos[url] = streams
streams
ytVideos[url] = s.videoStreams
ytVideosSubtitles[url] = try {
s.subtitlesDefault.filterNotNull()
} catch (e: Exception) {
logError(e)
emptyList()
}
if (streams.isEmpty()) {
throw ErrorLoadingException("No Youtube streams")
}
streams
//streams.sortedBy { it.height }
// .firstOrNull { !it.isVideoOnly && it.height > 0 }
// ?: throw ErrorLoadingException("No valid Youtube stream")
}
if (streams is Resource.Success) {
return streams.value.mapNotNull {
ytVideos[url]?.mapNotNull {
if (it.isVideoOnly || it.height <= 0) return@mapNotNull null
ExtractorLink(
@ -73,9 +72,10 @@ open class YoutubeExtractor : ExtractorApi() {
"",
it.height
)
}?.forEach(callback)
ytVideosSubtitles[url]?.mapNotNull {
SubtitleFile(it.languageTag ?: return@mapNotNull null, it.url ?: return@mapNotNull null)
}?.forEach(subtitleCallback)
}
} else {
return null
}
}
}

View file

@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.extractors.helper
import android.util.Log
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorLink
@ -8,7 +9,11 @@ import com.lagradost.cloudstream3.utils.loadExtractor
class AsianEmbedHelper {
companion object {
suspend fun getUrls(url: String, callback: (ExtractorLink) -> Unit) {
suspend fun getUrls(
url: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
// Fetch links
val doc = app.get(url).document
val links = doc.select("div#list-server-more > ul > li.linkserver")
@ -17,7 +22,7 @@ class AsianEmbedHelper {
val datavid = it.attr("data-video") ?: ""
//Log.i("AsianEmbed", "Result => (datavid) ${datavid}")
if (datavid.isNotBlank()) {
val res = loadExtractor(datavid, url, callback)
val res = loadExtractor(datavid, url, subtitleCallback, callback)
Log.i("AsianEmbed", "Result => ($res) (datavid) $datavid")
}
}

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.extractors.helper
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
@ -10,7 +11,11 @@ class VstreamhubHelper {
private val baseUrl: String = "https://vstreamhub.com"
private val baseName: String = "Vstreamhub"
suspend fun getUrls(url: String, callback: (ExtractorLink) -> Unit) {
suspend fun getUrls(
url: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
if (url.startsWith(baseUrl)) {
// Fetch links
val doc = app.get(url).document.select("script")
@ -20,7 +25,8 @@ class VstreamhubHelper {
if (innerText.contains("file:")) {
val startString = "file: "
val aa = innerText.substring(innerText.indexOf(startString))
val linkUrl = aa.substring(startString.length + 1, aa.indexOf("\",")).trim()
val linkUrl =
aa.substring(startString.length + 1, aa.indexOf("\",")).trim()
//Log.i(baseName, "Result => (linkUrl) ${linkUrl}")
val exlink = ExtractorLink(
name = "$baseName m3u8",
@ -33,12 +39,14 @@ class VstreamhubHelper {
callback.invoke(exlink)
}
if (innerText.contains("playerInstance")) {
val aa = innerText.substring(innerText.indexOf("playerInstance.addButton"))
val aa =
innerText.substring(innerText.indexOf("playerInstance.addButton"))
val startString = "window.open(["
val bb = aa.substring(aa.indexOf(startString))
val datavid = bb.substring(startString.length, bb.indexOf("]")).removeSurrounding("\"")
val datavid = bb.substring(startString.length, bb.indexOf("]"))
.removeSurrounding("\"")
if (datavid.isNotBlank()) {
loadExtractor(datavid, url, callback)
loadExtractor(datavid, url, subtitleCallback, callback)
//Log.i(baseName, "Result => (datavid) ${datavid}")
}
}

View file

@ -197,9 +197,9 @@ class AllMoviesForYouProvider : MainAPI() {
val soup = app.get(id).document
soup.select("body iframe").map {
val link = fixUrl(it.attr("src").replace("streamhub.to/d/", "streamhub.to/e/"))
loadExtractor(link, data, callback)
loadExtractor(link, data, subtitleCallback, callback)
}
} else loadExtractor(id, data, callback)
} else loadExtractor(id, data, subtitleCallback, callback)
}
return true
}

View file

@ -2,10 +2,10 @@ package com.lagradost.cloudstream3.movieproviders
import androidx.core.text.parseAsHtml
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
class AltadefinizioneProvider : MainAPI() {
@ -55,12 +55,14 @@ class AltadefinizioneProvider : MainAPI() {
}
override suspend fun search(query: String): List<SearchResponse> {
val doc = app.post("$mainUrl/index.php", data = mapOf(
val doc = app.post(
"$mainUrl/index.php", data = mapOf(
"do" to "search",
"subaction" to "search",
"story" to query,
"sortby" to "news_read"
)).document
)
).document
return doc.select("div.box").map {
val title = it.selectFirst("img")!!.attr("alt")
val link = it.selectFirst("a")!!.attr("href")
@ -84,10 +86,12 @@ class AltadefinizioneProvider : MainAPI() {
val page = app.get(url)
val document = page.document
val title = document.selectFirst(" h1 > a")!!.text().replace("streaming", "")
val description = document.select("#sfull").toString().substringAfter("altadefinizione").substringBeforeLast("fonte trama").parseAsHtml().toString()
val description = document.select("#sfull").toString().substringAfter("altadefinizione")
.substringBeforeLast("fonte trama").parseAsHtml().toString()
val rating = null
val year = document.selectFirst("#details > li:nth-child(2)")!!.childNode(2).toString().filter { it.isDigit() }.toInt()
val year = document.selectFirst("#details > li:nth-child(2)")!!.childNode(2).toString()
.filter { it.isDigit() }.toInt()
val poster = fixUrl(document.selectFirst("div.thumbphoto > img")!!.attr("src"))
@ -143,22 +147,20 @@ class AltadefinizioneProvider : MainAPI() {
): Boolean {
val doc = app.get(data).document
if (doc.select("div.guardahd-player").isNullOrEmpty()) {
val videoUrl = doc.select("input").filter { it.hasAttr("data-mirror") }.last().attr("value")
loadExtractor(videoUrl, data, callback)
val videoUrl =
doc.select("input").filter { it.hasAttr("data-mirror") }.last().attr("value")
loadExtractor(videoUrl, data, subtitleCallback, callback)
doc.select("#mirrors > li > a").forEach {
loadExtractor(fixUrl(it.attr("data-target")), data, callback)
loadExtractor(fixUrl(it.attr("data-target")), data, subtitleCallback, callback)
}
}
else{
} else {
val pagelinks = doc.select("div.guardahd-player").select("iframe").attr("src")
val docLinks = app.get(pagelinks).document
docLinks.select("body > div > ul > li").forEach {
loadExtractor(fixUrl(it.attr("data-link")), data, callback)
loadExtractor(fixUrl(it.attr("data-link")), data, subtitleCallback, callback)
}
}
return true
}
}

View file

@ -27,8 +27,14 @@ open class BflixProvider : MainAPI() {
Pair("Movies", "div.tab-content[data-name=movies] div.filmlist div.item"),
Pair("Shows", "div.tab-content[data-name=shows] div.filmlist div.item"),
Pair("Trending", "div.tab-content[data-name=trending] div.filmlist div.item"),
Pair("Latest Movies", "div.container section.bl:contains(Latest Movies) div.filmlist div.item"),
Pair("Latest TV-Series", "div.container section.bl:contains(Latest TV-Series) div.filmlist div.item"),
Pair(
"Latest Movies",
"div.container section.bl:contains(Latest Movies) div.filmlist div.item"
),
Pair(
"Latest TV-Series",
"div.container section.bl:contains(Latest TV-Series) div.filmlist div.item"
),
)
for ((name, element) in testa) try {
val test = soup.select(element).map {
@ -57,7 +63,8 @@ open class BflixProvider : MainAPI() {
}
//Credits to https://github.com/jmir1
private val key = "5uLKesbh0nkrpPq9VwMC6+tQBdomjJ4HNl/fWOSiREvAYagT8yIG7zx2D13UZFXc" //key credits to @Modder4869
private val key =
"5uLKesbh0nkrpPq9VwMC6+tQBdomjJ4HNl/fWOSiREvAYagT8yIG7zx2D13UZFXc" //key credits to @Modder4869
private fun getVrf(id: String): String? {
val reversed = ue(encode(id) + "0000000").slice(0..5).reversed()
@ -240,7 +247,8 @@ open class BflixProvider : MainAPI() {
episode,
)
}
val tvType = if (url.contains("/movie/") && episodes.size == 1) TvType.Movie else TvType.TvSeries
val tvType =
if (url.contains("/movie/") && episodes.size == 1) TvType.Movie else TvType.TvSeries
val recommendations =
soup.select("div.bl-2 section.bl div.content div.filmlist div.item")
?.mapNotNull { element ->
@ -261,9 +269,12 @@ open class BflixProvider : MainAPI() {
val durationregex = Regex("((\\d+) min)")
val yearegex = Regex("<span>(\\d+)<\\/span>")
val duration = if (durationdoc.contains("na min")) null
else durationregex.find(durationdoc)?.destructured?.component1()?.replace(" min","")?.toIntOrNull()
val year = if (mainUrl == "https://bflix.ru") { yearegex.find(durationdoc)?.destructured?.component1()
?.replace(Regex("<span>|<\\/span>"),"") } else null
else durationregex.find(durationdoc)?.destructured?.component1()?.replace(" min", "")
?.toIntOrNull()
val year = if (mainUrl == "https://bflix.ru") {
yearegex.find(durationdoc)?.destructured?.component1()
?.replace(Regex("<span>|<\\/span>"), "")
} else null
return when (tvType) {
TvType.TvSeries -> {
TvSeriesLoadResponse(
@ -343,7 +354,8 @@ open class BflixProvider : MainAPI() {
val a = it.select("a").map {
it.attr("data-kname")
}
val tvType = if (data.contains("movie/") && a.size == 1) TvType.Movie else TvType.TvSeries
val tvType =
if (data.contains("movie/") && a.size == 1) TvType.Movie else TvType.TvSeries
val servers = if (tvType == TvType.Movie) it.select(".episode a").attr("data-ep")
else
it.select(".episode a[href=$cleandata]").attr("data-ep")
@ -364,7 +376,7 @@ open class BflixProvider : MainAPI() {
?.replace("/embed/", "/e/")?.replace(Regex("(\\?sub.info.*)"), "")
}.apmap { url ->
loadExtractor(
url, data, callback
url, data, subtitleCallback, callback
)
}
//Apparently any server works, I haven't found any diference

View file

@ -155,7 +155,7 @@ class CineblogProvider : MainAPI() {
rating,
null,
null,
null,
mutableListOf(),
recomm
)
} else {
@ -205,7 +205,7 @@ class CineblogProvider : MainAPI() {
val url2= Regex("""src='((.|\\n)*?)'""").find(test.text)?.groups?.get(1)?.value.toString()
val trueUrl = app.get(url2, headers = mapOf("referer" to mainUrl)).url
loadExtractor(trueUrl, data, callback)
loadExtractor(trueUrl, data, subtitleCallback, callback)
return true
}

View file

@ -98,7 +98,8 @@ class CinecalidadProvider:MainAPI() {
val href = li.selectFirst("a")!!.attr("href")
val epThumb = li.selectFirst("img.lazy")!!.attr("data-src")
val name = li.selectFirst(".episodiotitle a")!!.text()
val seasonid = li.selectFirst(".numerando")!!.text().replace(Regex("(S|E)"),"").let { str ->
val seasonid =
li.selectFirst(".numerando")!!.text().replace(Regex("(S|E)"), "").let { str ->
str.split("-").mapNotNull { subStr -> subStr.toIntOrNull() }
}
val isValid = seasonid.size == 2
@ -112,7 +113,8 @@ class CinecalidadProvider:MainAPI() {
if (epThumb.contains("svg")) null else epThumb
)
}
return when (val tvType = if (url.contains("/ver-pelicula/")) TvType.Movie else TvType.TvSeries) {
return when (val tvType =
if (url.contains("/ver-pelicula/")) TvType.Movie else TvType.TvSeries) {
TvType.TvSeries -> {
TvSeriesLoadResponse(
title,
@ -156,18 +158,18 @@ class CinecalidadProvider:MainAPI() {
val url = it.attr("data-option")
if (url.startsWith("https://cinestart.net")) {
val extractor = Cinestart()
extractor.getSafeUrl(url)?.forEach { link ->
callback.invoke(link)
}
extractor.getSafeUrl(url, null, subtitleCallback, callback)
} else {
loadExtractor(url, mainUrl, callback)
loadExtractor(url, mainUrl, subtitleCallback, callback)
}
if (url.startsWith("https://cinecalidad.lol")) {
val cineurlregex = Regex("(https:\\/\\/cinecalidad\\.lol\\/play\\/\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)")
val cineurlregex =
Regex("(https:\\/\\/cinecalidad\\.lol\\/play\\/\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)")
cineurlregex.findAll(url).map {
it.value.replace("/play/", "/play/r.php")
}.toList().apmap {
app.get(it,
app.get(
it,
headers = mapOf(
"Host" to "cinecalidad.lol",
"User-Agent" to USER_AGENT,
@ -182,31 +184,33 @@ class CinecalidadProvider:MainAPI() {
"Sec-Fetch-Site" to "same-origin",
"Sec-Fetch-User" to "?1",
),
allowRedirects = false).okhttpResponse.headers.values("location").apmap { extractedurl ->
allowRedirects = false
).okhttpResponse.headers.values("location").apmap { extractedurl ->
if (extractedurl.contains("cinestart")) {
loadExtractor(extractedurl, mainUrl, callback)
loadExtractor(extractedurl, mainUrl, subtitleCallback, callback)
}
}
}
}
}
if (datatext.contains("en castellano")) app.get("$data?ref=es").document.select(".dooplay_player_option").apmap {
if (datatext.contains("en castellano")) app.get("$data?ref=es").document.select(".dooplay_player_option")
.apmap {
val url = it.attr("data-option")
if (url.startsWith("https://cinestart.net")) {
val extractor = Cinestart()
extractor.getSafeUrl(url)?.forEach { link ->
callback.invoke(link)
}
extractor.getSafeUrl(url, null, subtitleCallback, callback)
} else {
loadExtractor(url, mainUrl, callback)
loadExtractor(url, mainUrl, subtitleCallback, callback)
}
if (url.startsWith("https://cinecalidad.lol")) {
val cineurlregex = Regex("(https:\\/\\/cinecalidad\\.lol\\/play\\/\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)")
val cineurlregex =
Regex("(https:\\/\\/cinecalidad\\.lol\\/play\\/\\?h=[a-zA-Z0-9]{0,8}[a-zA-Z0-9_-]+)")
cineurlregex.findAll(url).map {
it.value.replace("/play/", "/play/r.php")
}.toList().apmap {
app.get(it,
app.get(
it,
headers = mapOf(
"Host" to "cinecalidad.lol",
"User-Agent" to USER_AGENT,
@ -221,9 +225,10 @@ class CinecalidadProvider:MainAPI() {
"Sec-Fetch-Site" to "same-origin",
"Sec-Fetch-User" to "?1",
),
allowRedirects = false).okhttpResponse.headers.values("location").apmap { extractedurl ->
allowRedirects = false
).okhttpResponse.headers.values("location").apmap { extractedurl ->
if (extractedurl.contains("cinestart")) {
loadExtractor(extractedurl, mainUrl, callback)
loadExtractor(extractedurl, mainUrl, subtitleCallback, callback)
}
}
}
@ -231,7 +236,8 @@ class CinecalidadProvider:MainAPI() {
}
if (datatext.contains("Subtítulo LAT") || datatext.contains("Forzados LAT")) {
doc.select("#panel_descarga.pane a").apmap {
val link = if (data.contains("serie") || data.contains("episodio")) "${data}${it.attr("href")}"
val link =
if (data.contains("serie") || data.contains("episodio")) "${data}${it.attr("href")}"
else it.attr("href")
val docsub = app.get(link)
val linksub = docsub.document
@ -241,7 +247,10 @@ class CinecalidadProvider:MainAPI() {
val langdoc = linksub.selectFirst("div.titulo h3")!!.text()
val reallang = langregex.find(langdoc)?.destructured?.component1()
linksub.select("a.link").apmap {
val sublink = if (data.contains("serie") || data.contains("episodio")) "${data}${it.attr("href")}"
val sublink =
if (data.contains("serie") || data.contains("episodio")) "${data}${
it.attr("href")
}"
else it.attr("href")
subtitleCallback(
SubtitleFile(reallang!!, sublink)

View file

@ -227,7 +227,7 @@ class CuevanaProvider : MainAPI() {
val json = parseJson<Femcuevana>(url)
val link = json.url
if (link.contains("fembed")) {
loadExtractor(link, data, callback)
loadExtractor(link, data, subtitleCallback, callback)
}
}
}
@ -281,7 +281,7 @@ class CuevanaProvider : MainAPI() {
),
data = mapOf(Pair("url", gotolink))
).okhttpResponse.headers.values("location").apmap { golink ->
loadExtractor(golink, data, callback)
loadExtractor(golink, data, subtitleCallback, callback)
}
}
}
@ -311,7 +311,7 @@ class CuevanaProvider : MainAPI() {
),
data = mapOf(Pair("h", inlink))
).okhttpResponse.headers.values("location").apmap { link ->
loadExtractor(link, data, callback)
loadExtractor(link, data, subtitleCallback, callback)
}
}
}

View file

@ -147,7 +147,7 @@ class DoramasYTProvider : MainAPI() {
callback.invoke(link)
}
} else {
loadExtractor(url, mainUrl, callback)
loadExtractor(url, mainUrl, subtitleCallback, callback)
}
}
return true

View file

@ -57,7 +57,8 @@ class DramaSeeProvider : MainAPI() {
val innerA = it.select("a") ?: return@mapNotNull null
val link = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null
val title = innerA.attr("title") ?: return@mapNotNull null
val year = Regex(""".*\((\d{4})\)""").find(title)?.groupValues?.getOrNull(1)?.toIntOrNull()
val year =
Regex(""".*\((\d{4})\)""").find(title)?.groupValues?.getOrNull(1)?.toIntOrNull()
val imgSrc = it.select("img")?.attr("data-src") ?: return@mapNotNull null
val image = fixUrlNull(imgSrc)
@ -193,7 +194,7 @@ class DramaSeeProvider : MainAPI() {
val status = element.attr("data-status") ?: return@forEach
if (status != "1") return@forEach
val extractorData = element.attr("data-video") ?: return@forEach
loadExtractor(extractorData, iframe.url, callback)
loadExtractor(extractorData, iframe.url, subtitleCallback, callback)
}
}, {
val iv = "9262859232435825"

View file

@ -206,7 +206,7 @@ class DramaidProvider : MainAPI() {
}.apmap {
when {
it.contains("motonews.club") -> invokeDriveSource(it, this.name, subtitleCallback, callback)
else -> loadExtractor(it, data, callback)
else -> loadExtractor(it, data, subtitleCallback, callback)
}
}

View file

@ -87,7 +87,7 @@ class ElifilmsProvider : MainAPI() {
val encodedurl = it.attr("data-id")
val urlDecoded = base64Decode(encodedurl)
val url = fixUrl(urlDecoded)
loadExtractor(url, data, callback)
loadExtractor(url, data, subtitleCallback, callback)
}
return true
}

View file

@ -170,7 +170,7 @@ class EntrepeliculasyseriesProvider:MainAPI() {
data = mapOf(Pair("h", postkey)),
allowRedirects = false
).okhttpResponse.headers.values("location").apmap {
loadExtractor(it, data, callback)
loadExtractor(it, data, subtitleCallback, callback)
}
}
}

View file

@ -189,7 +189,7 @@ class EstrenosDoramasProvider : MainAPI() {
val document = app.get(data).document
document.select("div.tab_container iframe").apmap { container ->
val directlink = fixUrl(container.attr("src"))
loadExtractor(directlink, data, callback)
loadExtractor(directlink, data, subtitleCallback, callback)
if (directlink.contains("/repro/amz/")) {
val amzregex = Regex("https:\\/\\/repro3\\.estrenosdoramas\\.us\\/repro\\/amz\\/examples\\/.*\\.php\\?key=.*\$")
@ -239,7 +239,7 @@ class EstrenosDoramasProvider : MainAPI() {
).text
val extracteklink = link.substringAfter("\"urlremoto\":\"").substringBefore("\"}")
.replace("\\/", "/").replace("//ok.ru/","http://ok.ru/")
loadExtractor(extracteklink, data, callback)
loadExtractor(extracteklink, data, subtitleCallback, callback)
}
}

View file

@ -2,8 +2,9 @@ package com.lagradost.cloudstream3.movieproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import org.jsoup.select.Elements
@ -60,7 +61,8 @@ class FilmanProvider : MainAPI() {
fun getVideos(type: TvType, items: Elements): List<SearchResponse> {
return items.mapNotNull { i ->
val href = i.selectFirst(".poster > a")?.attr("href") ?: return@mapNotNull null
val img = i.selectFirst(".poster > a > img")?.attr("src")?.replace("/thumb/", "/big/")
val img =
i.selectFirst(".poster > a > img")?.attr("src")?.replace("/thumb/", "/big/")
val name = i.selectFirst(".film_title")?.text() ?: return@mapNotNull null
val year = i.selectFirst(".film_year")?.text()?.toIntOrNull()
if (type === TvType.TvSeries) {
@ -136,7 +138,7 @@ class FilmanProvider : MainAPI() {
document?.select(".link-to-video")?.apmap { item ->
val decoded = base64Decode(item.select("a").attr("data-iframe"))
val link = tryParseJson<LinkElement>(decoded)?.src ?: return@apmap
loadExtractor(link, null, callback)
loadExtractor(link, subtitleCallback, callback)
}
return true
}

View file

@ -224,15 +224,15 @@ class FilmpertuttiProvider : MainAPI() {
tryParseJson<List<String>>(data)?.apmap { id ->
if (id.contains("buckler")){
val id2 = unshorten_linkup(id).trim().replace("/v/","/e/").replace("/f/","/e/")
loadExtractor(id2, data, callback)
loadExtractor(id2, data, subtitleCallback, callback)
}
else if (id.contains("isecure")){
val doc1 = app.get(id).document
val id2 = doc1.selectFirst("iframe")!!.attr("src")
loadExtractor(id2, data, callback)
loadExtractor(id2, data, subtitleCallback, callback)
}
else{
loadExtractor(id, data, callback)
loadExtractor(id, data, subtitleCallback, callback)
}
}
return true

View file

@ -82,8 +82,7 @@ class FrenchStreamProvider : MainAPI() {
//println("listeEpisode:")
val episodeList = if ("<a" !in (listEpisode[0]).toString()) { // check if VF is empty
listEpisode[1] // no vf, return vostfr
}
else {
} else {
listEpisode[0] // no vostfr, return vf
}
@ -223,7 +222,7 @@ class FrenchStreamProvider : MainAPI() {
if (it.first.contains(extractor.name, ignoreCase = true)) {
// val name = it.first
// print("true for $name")
extractor.getSafeUrl(it.second, it.second)?.forEach(callback)
extractor.getSafeUrl(it.second, it.second, subtitleCallback, callback)
break
}
}

View file

@ -101,7 +101,7 @@ class HDMovie5 : MainAPI() {
(doc.select("#repimdb>strong").text().toFloatOrNull()?.times(1000))?.toInt(),
info.select(".sgeneros>a").map { it.text() },
info.select(".runtime").text().substringBefore(" Min.").toIntOrNull(),
null,
mutableListOf(),
doc.select("#single_relacionados>article>a").map {
val img = it.select("img")
MovieSearchResponse(
@ -149,7 +149,7 @@ class HDMovie5 : MainAPI() {
val html = p.parsedSafe<PlayerAjaxResponse>()?.embedURL ?: return@apmapIndexed false
val doc = Jsoup.parse(html)
val link = doc.select("iframe").attr("src")
loadExtractor(httpsify(link), "$mainUrl/", callback)
loadExtractor(httpsify(link), "$mainUrl/", subtitleCallback, callback)
}.contains(true)
}
}

View file

@ -215,7 +215,7 @@ class IHaveNoTvProvider : MainAPI() {
val iframe = soup.selectFirst("#videoWrap iframe")
if (iframe != null) {
loadExtractor(iframe.attr("src"), null, callback)
loadExtractor(iframe.attr("src"), null, subtitleCallback, callback)
}
return true
}

View file

@ -365,7 +365,7 @@ class IdlixProvider : MainAPI() {
if (source.startsWith("https://uservideo.xyz")) {
source = app.get(source).document.select("iframe").attr("src")
}
loadExtractor(source, data, callback)
loadExtractor(source, data, subtitleCallback, callback)
}
}
}

View file

@ -48,7 +48,9 @@ class KdramaHoodProvider : MainAPI() {
val rex = Regex("\\((\\d+)")
//Log.i(this.name, "Result => (rex value) ${rex.find(yearText)?.value}")
rex.find(yearText)?.value?.toIntOrNull()
} catch (e: Exception) { null }
} catch (e: Exception) {
null
}
MovieSearchResponse(
name = title,
@ -105,15 +107,21 @@ class KdramaHoodProvider : MainAPI() {
val startLink = "https://kdramahood.com/drama-release-year/"
var res: Int? = null
info?.select("div.metadatac")?.forEach {
if (res != null) { return@forEach }
if (it == null) { return@forEach }
if (res != null) {
return@forEach
}
if (it == null) {
return@forEach
}
val yearLink = it.select("a").attr("href") ?: return@forEach
if (yearLink.startsWith(startLink)) {
res = yearLink.substring(startLink.length).replace("/", "").toIntOrNull()
}
}
res
} catch (e: Exception) { null }
} catch (e: Exception) {
null
}
val recs = doc.select("div.sidebartv > div.tvitemrel").mapNotNull {
val a = it?.select("a") ?: return@mapNotNull null
@ -161,10 +169,13 @@ class KdramaHoodProvider : MainAPI() {
}
//Fetch default source and subtitles
epVidLinkEl.select("div.embed2")?.forEach { defsrc ->
if (defsrc == null) { return@forEach }
if (defsrc == null) {
return@forEach
}
val scriptstring = defsrc.toString()
if (scriptstring.contains("sources: [{")) {
"(?<=playerInstance2.setup\\()([\\s\\S]*?)(?=\\);)".toRegex().find(scriptstring)?.value?.let { itemjs ->
"(?<=playerInstance2.setup\\()([\\s\\S]*?)(?=\\);)".toRegex()
.find(scriptstring)?.value?.let { itemjs ->
listOfLinks.add("$mainUrl$itemjs")
}
}
@ -264,7 +275,7 @@ class KdramaHoodProvider : MainAPI() {
//Log.i(this.name, "Result => (url) $url")
when {
url.startsWith("https://asianembed.io") -> {
AsianEmbedHelper.getUrls(url, callback)
AsianEmbedHelper.getUrls(url, subtitleCallback, callback)
}
url.startsWith("https://embedsito.com") -> {
val extractor = XStreamCdn()
@ -274,7 +285,7 @@ class KdramaHoodProvider : MainAPI() {
}
}
else -> {
loadExtractor(url, mainUrl, callback)
loadExtractor(url, mainUrl, subtitleCallback, callback)
}
}
}

View file

@ -219,7 +219,7 @@ class LayarKacaProvider : MainAPI() {
} else {
it
}
loadExtractor(link, data, callback)
loadExtractor(link, data, subtitleCallback, callback)
}
return true

View file

@ -126,7 +126,7 @@ class OpenVidsProvider:TmdbProvider() {
membed.isUsingAdaptiveKeys,
membed.isUsingAdaptiveData)
} else
loadExtractor(links, data, callback)
loadExtractor(links, data, subtitleCallback, callback)
}
return true
}

View file

@ -152,7 +152,7 @@ class PeliSmartProvider: MainAPI() {
.replace("https://pelismarthd.com/p/1.php?v=","https://evoload.io/e/")
.replace("https://pelismarthd.com/p/2.php?v=","https://streamtape.com/e/")
.replace("https://pelismarthd.com/p/4.php?v=","https://dood.to/e/")
loadExtractor(urlc, data, callback)
loadExtractor(urlc, data, subtitleCallback, callback)
}
return true
}

View file

@ -222,7 +222,7 @@ class PelisflixProvider : MainAPI() {
allowRedirects = false
).okhttpResponse.headers.values("location").apmap { link ->
val url1 = link.replace("#bu", "")
loadExtractor(url1, data, callback)
loadExtractor(url1, data, subtitleCallback, callback)
}
}
}

View file

@ -165,7 +165,7 @@ class PelisplusHDProvider:MainAPI() {
): Boolean {
app.get(data).document.select("div.player > script").map { script ->
fetchUrls(script.data().replace("https://pelisplushd.net/fembed.php?url=","https://www.fembed.com/v/")).apmap {
loadExtractor(it, data, callback)
loadExtractor(it, data, subtitleCallback, callback)
}
}
return true

View file

@ -229,7 +229,7 @@ open class PelisplusProviderTemplate : MainAPI() {
if (info.contains("Latino")) {
doc.select(".server-item-1 li").apmap {
val serverid = fixUrl(it.attr("data-video")).replace("streaming.php","play")
loadExtractor(serverid, data, callback)
loadExtractor(serverid, data, subtitleCallback, callback)
if (serverid.contains("pelisplus.icu")) {
getPelisStream(serverid, callback)
}
@ -239,7 +239,7 @@ open class PelisplusProviderTemplate : MainAPI() {
if (info.contains("Subtitulado")) {
doc.select(".server-item-0 li").apmap {
val serverid = fixUrl(it.attr("data-video")).replace("streaming.php","play")
loadExtractor(serverid, data, callback)
loadExtractor(serverid, data, subtitleCallback, callback)
if (serverid.contains("pelisplus.icu")) {
getPelisStream(serverid, callback)
}
@ -249,7 +249,7 @@ open class PelisplusProviderTemplate : MainAPI() {
if (info.contains("Castellano")) {
doc.select(".server-item-2 li").apmap {
val serverid = fixUrl(it.attr("data-video")).replace("streaming.php","play")
loadExtractor(serverid, data, callback)
loadExtractor(serverid, data, subtitleCallback, callback)
if (serverid.contains("pelisplus.icu")) {
getPelisStream(serverid, callback)
}

View file

@ -221,7 +221,7 @@ class PinoyHDXyzProvider : MainAPI() {
mapper.readValue<List<String>>(data).forEach { item ->
val url = item.trim()
if (url.isNotBlank()) {
if (loadExtractor(url, mainUrl, callback)) {
if (loadExtractor(url, mainUrl, subtitleCallback, callback)) {
count++
}
}

View file

@ -231,7 +231,7 @@ class PinoyMoviePediaProvider : MainAPI() {
callback.invoke(it)
}
} else {
loadExtractor(link, mainUrl, callback)
loadExtractor(link, mainUrl, subtitleCallback, callback)
}
}
return count > 0

View file

@ -1,6 +1,5 @@
package com.lagradost.cloudstream3.movieproviders
import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.extractors.FEmbed
@ -26,7 +25,11 @@ class PinoyMoviesEsProvider : MainAPI() {
@JsonProperty("type") val type: String
)
private fun getRowElements(mainbody: Elements, rows: List<Pair<String, String>>, sep: String): MutableList<HomePageList> {
private fun getRowElements(
mainbody: Elements,
rows: List<Pair<String, String>>,
sep: String
): MutableList<HomePageList> {
val all = mutableListOf<HomePageList>()
for (item in rows) {
val title = item.first
@ -36,10 +39,14 @@ class PinoyMoviesEsProvider : MainAPI() {
if (urlTitle.isNullOrEmpty()) {
urlTitle = it?.select("div.data")
}
if (urlTitle.isNullOrEmpty()) { return@mapNotNull null }
if (urlTitle.isNullOrEmpty()) {
return@mapNotNull null
}
// Fetch details
val link = fixUrlNull(urlTitle.select("a")?.attr("href"))
if (link.isNullOrBlank()) { return@mapNotNull null }
if (link.isNullOrBlank()) {
return@mapNotNull null
}
val image = it?.select("div.poster > img")?.attr("data-src")
@ -47,7 +54,9 @@ class PinoyMoviesEsProvider : MainAPI() {
val name = urlTitle.select("h3")?.text()
?: urlTitle.select("h2")?.text()
?: urlTitle.select("h1")?.text()
if (name.isNullOrBlank()) { return@mapNotNull null }
if (name.isNullOrBlank()) {
return@mapNotNull null
}
var year = urlTitle.select("span")?.text()?.toIntOrNull()
@ -80,27 +89,32 @@ class PinoyMoviesEsProvider : MainAPI() {
}
return all
}
override suspend fun getMainPage(): HomePageResponse {
val all = ArrayList<HomePageList>()
val document = app.get(mainUrl).document
val mainbody = document.getElementsByTag("body")
if (mainbody != null) {
// All rows will be hardcoded bc of the nature of the site
val homepage1 = getRowElements(mainbody, listOf(
val homepage1 = getRowElements(
mainbody, listOf(
Pair("Suggestion", "items.featured"),
Pair("All Movies", "items.full")
), ".")
), "."
)
if (homepage1.isNotEmpty()) {
all.addAll(homepage1)
}
//2nd rows
val homepage2 = getRowElements(mainbody, listOf(
val homepage2 = getRowElements(
mainbody, listOf(
Pair("Action", "genre_action"),
Pair("Comedy", "genre_comedy"),
Pair("Romance", "genre_romance"),
Pair("Horror", "genre_horror")
//Pair("Rated-R", "genre_rated-r")
), "#")
), "#"
)
if (homepage2.isNotEmpty()) {
all.addAll(homepage2)
}
@ -156,7 +170,9 @@ class PinoyMoviesEsProvider : MainAPI() {
val aName = a.select("img")?.attr("alt") ?: return@mapNotNull null
val aYear = try {
aName.trim().takeLast(5).removeSuffix(")").toIntOrNull()
} catch (e: Exception) { null }
} catch (e: Exception) {
null
}
MovieSearchResponse(
url = aUrl,
name = aName,
@ -181,8 +197,10 @@ class PinoyMoviesEsProvider : MainAPI() {
Pair("nume", "1"),
Pair("type", "movie")
)
val innerPage = app.post("https://pinoymovies.es/wp-admin/admin-ajax.php ",
referer = url, data = content).document.select("body")?.text()?.trim()
val innerPage = app.post(
"https://pinoymovies.es/wp-admin/admin-ajax.php ",
referer = url, data = content
).document.select("body")?.text()?.trim()
if (!innerPage.isNullOrBlank()) {
tryParseJson<EmbedUrl>(innerPage)?.let {
listOfLinks.add(it.embed_url)
@ -214,7 +232,7 @@ class PinoyMoviesEsProvider : MainAPI() {
tryParseJson<List<String>>(data)?.forEach { link ->
//Log.i(this.name, "Result => (link) $link")
if (link.startsWith("https://vstreamhub.com")) {
VstreamhubHelper.getUrls(link, callback)
VstreamhubHelper.getUrls(link, subtitleCallback, callback)
count++
} else if (link.contains("fembed.com")) {
val extractor = FEmbed()
@ -224,7 +242,7 @@ class PinoyMoviesEsProvider : MainAPI() {
count++
}
} else {
if (loadExtractor(link, mainUrl, callback)) {
if (loadExtractor(link, mainUrl, subtitleCallback, callback)) {
count++
}
}

View file

@ -7,11 +7,12 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.network.WebViewResolver
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.nodes.Element
import java.net.URI
import java.util.ArrayList
class RebahinProvider : MainAPI() {
override var mainUrl = "http://167.88.14.149"
@ -263,7 +264,7 @@ class RebahinProvider : MainAPI() {
callback
)
else -> {
loadExtractor(link, "$mainUrl/", callback)
loadExtractor(link, "$mainUrl/", subtitleCallback, callback)
if (link.startsWith("https://sbfull.com")) {
val response = app.get(
link, interceptor = WebViewResolver(

View file

@ -217,7 +217,7 @@ class SeriesflixProvider : MainAPI() {
allowRedirects = false
).okhttpResponse.headers.values("location").apmap { link ->
val url1 = link.replace("#bu", "")
loadExtractor(url1, data, callback)
loadExtractor(url1, data, subtitleCallback, callback)
}
}
}

View file

@ -24,7 +24,6 @@ import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import java.net.URI
import java.util.*
import kotlin.collections.ArrayList
import kotlin.system.measureTimeMillis
open class SflixProvider : MainAPI() {
@ -346,7 +345,7 @@ open class SflixProvider : MainAPI() {
"https://ws11.rabbitstream.net/socket.io/?EIO=4&transport=polling"
if (iframeLink.contains("streamlare", ignoreCase = true)) {
loadExtractor(iframeLink, null).forEach(callback)
loadExtractor(iframeLink, null,subtitleCallback,callback)
} else {
extractRabbitStream(iframeLink, subtitleCallback, callback, false) { it }
}

View file

@ -232,7 +232,7 @@ class TantifilmProvider : MainAPI() {
iframe.forEach { id ->
val doc2 = app.get(id).document
val id2 = app.get(doc2.selectFirst("iframe")!!.attr("src")).url
loadExtractor(id2, data, callback)
loadExtractor(id2, data, subtitleCallback, callback)
}
return true
}

View file

@ -66,7 +66,7 @@ class TwoEmbedProvider : TmdbProvider() {
if (iframeLink.contains("rabbitstream")) {
extractRabbitStream(iframeLink, subtitleCallback, callback, false) { it }
} else {
loadExtractor(iframeLink, embedUrl, callback)
loadExtractor(iframeLink, embedUrl, subtitleCallback, callback)
}
}
return true

View file

@ -67,8 +67,10 @@ open class VidstreamProviderTemplate : MainAPI() {
open val iv: String? = null
open val secretKey: String? = null
open val secretDecryptKey: String? = null
/** Generated the key from IV and ID */
open val isUsingAdaptiveKeys: Boolean = false
/**
* Generate data for the encrypt-ajax automatically (only on supported sites)
* See $("script[data-name='episode']")[0].dataset.value
@ -260,7 +262,16 @@ open class VidstreamProviderTemplate : MainAPI() {
val iframeLink =
Jsoup.parse(app.get(data).text).selectFirst("iframe")?.attr("src") ?: return false
extractVidstream(iframeLink, this.name, callback, iv, secretKey, secretDecryptKey, isUsingAdaptiveKeys, isUsingAdaptiveData)
extractVidstream(
iframeLink,
this.name,
callback,
iv,
secretKey,
secretDecryptKey,
isUsingAdaptiveKeys,
isUsingAdaptiveData
)
// In this case the video player is a vidstream clone and can be handled by the vidstream extractor.
// This case is a both unorthodox and you normally do not call extractors as they detect the url returned and does the rest.
val vidstreamObject = Vidstream(vidstreamExtractorUrl ?: mainUrl)
@ -268,7 +279,7 @@ open class VidstreamProviderTemplate : MainAPI() {
val id = Regex("""id=([^&]*)""").find(iframeLink)?.groupValues?.get(1)
if (id != null) {
vidstreamObject.getUrl(id, isCasting, callback)
vidstreamObject.getUrl(id, isCasting, subtitleCallback, callback)
}
val html = app.get(fixUrl(iframeLink)).text

View file

@ -22,7 +22,7 @@ class WatchAsianProvider : MainAPI() {
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 {
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
@ -77,12 +77,12 @@ class WatchAsianProvider : MainAPI() {
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
val title = it.select("h3.title").text() ?: return@mapNotNull null
if (title.isEmpty()) {
return@mapNotNull null
}
val year = null
val imgsrc = innerA.select("img")?.attr("data-original") ?: return@mapNotNull 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(
@ -100,31 +100,31 @@ class WatchAsianProvider : MainAPI() {
val body = app.get(url).document
// Declare vars
val isDramaDetail = url.contains("/drama-detail/")
var poster = ""
var poster: String? = null
var title = ""
var descript: String? = null
var year: Int? = null
var tags: List<String>? = null
if (isDramaDetail) {
val main = body.select("div.details")
val inner = main?.select("div.info")
val inner = main.select("div.info")
// Video details
poster = fixUrlNull(main?.select("div.img > img")?.attr("src")) ?: ""
poster = fixUrlNull(main.select("div.img > img").attr("src"))
//Log.i(this.name, "Result => (imgLinkCode) ${imgLinkCode}")
title = inner?.select("h1")?.firstOrNull()?.text() ?: ""
title = inner.select("h1").firstOrNull()?.text() ?: ""
//Log.i(this.name, "Result => (year) ${title.substring(title.length - 5)}")
descript = inner?.text()
descript = inner.text()
inner?.select("p")?.forEach { p ->
inner.select("p").forEach { p ->
val caption =
p?.selectFirst("span")?.text()?.trim()?.lowercase()?.removeSuffix(":")?.trim()
?: return@forEach
when (caption) {
"genre" -> {
tags = p.select("a")?.mapNotNull { it?.text()?.trim() }
tags = p.select("a").mapNotNull { it?.text()?.trim() }
}
"released" -> {
year = p.select("a")?.text()?.trim()?.toIntOrNull()
year = p.select("a").text().trim()?.toIntOrNull()
}
}
}
@ -147,7 +147,7 @@ class WatchAsianProvider : MainAPI() {
// 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 ->
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"))}")
@ -164,7 +164,7 @@ class WatchAsianProvider : MainAPI() {
posterUrl = poster,
date = null
)
} ?: listOf()
}
//If there's only 1 episode, consider it a movie.
if (episodeList.size == 1) {
//Clean title
@ -216,21 +216,24 @@ class WatchAsianProvider : MainAPI() {
url.startsWith("https://asianembed.io") || url.startsWith("https://asianload.io") -> {
val iv = "9262859232435825"
val secretKey = "93422192433952489752342908585752"
extractVidstream(url, this.name, callback, iv, secretKey, secretKey,
extractVidstream(
url, this.name, callback, iv, secretKey, secretKey,
isUsingAdaptiveKeys = false,
isUsingAdaptiveData = false
)
AsianEmbedHelper.getUrls(url, callback)
AsianEmbedHelper.getUrls(url, subtitleCallback, callback)
}
url.startsWith("https://embedsito.com") -> {
val extractor = XStreamCdn()
extractor.domainUrl = "embedsito.com"
extractor.getSafeUrl(url)?.apmap { link ->
callback.invoke(link)
}
extractor.getSafeUrl(
url,
subtitleCallback = subtitleCallback,
callback = callback,
)
}
else -> {
loadExtractor(url, mainUrl, callback)
loadExtractor(url, mainUrl, subtitleCallback, callback)
}
}
}
@ -240,8 +243,8 @@ class WatchAsianProvider : MainAPI() {
private suspend fun getServerLinks(url: String): String {
val moviedoc = app.get(url, referer = mainUrl).document
return moviedoc.select("div.anime_muti_link > ul > li")
?.mapNotNull {
.mapNotNull {
fixUrlNull(it?.attr("data-video")) ?: return@mapNotNull null
}?.toJson() ?: ""
}.toJson()
}
}

View file

@ -7,7 +7,6 @@ import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.nodes.Element
import java.util.ArrayList
class YomoviesProvider : MainAPI() {
override var mainUrl = "https://yomovies.plus"
@ -122,8 +121,15 @@ class YomoviesProvider : MainAPI() {
source,
referer = "$mainUrl/"
).document.select("ul.list-server-items li")
.apmap { loadExtractor(it.attr("data-video").substringBefore("=https://msubload"), "$mainUrl/", callback) }
else -> loadExtractor(source, "$mainUrl/", callback)
.apmap {
loadExtractor(
it.attr("data-video").substringBefore("=https://msubload"),
"$mainUrl/",
subtitleCallback,
callback
)
}
else -> loadExtractor(source, "$mainUrl/", subtitleCallback, callback)
}
}
}

View file

@ -375,6 +375,7 @@ abstract class AbstractPlayerFragment(
subView = player_view?.findViewById(R.id.exo_subtitles)
subStyle = SubtitlesFragment.getCurrentSavedStyle()
player.initSubtitles(subView, subtitle_holder, subStyle)
SubtitlesFragment.applyStyleEvent += ::onSubStyleChanged
try {

View file

@ -44,7 +44,9 @@ class LinkGenerator(
offset: Int
): Boolean {
links.apmap { link ->
if (!extract || !loadExtractor(link, referer) {
if (!extract || !loadExtractor(link, referer, {
subtitleCallback(PlayerSubtitleHelper.getSubtitleData(it))
}) {
callback(it to null)
}) {

View file

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.ui.player
import android.util.Log
import android.util.TypedValue
import android.view.ViewGroup
import android.widget.FrameLayout
@ -89,6 +90,7 @@ class PlayerSubtitleHelper {
regexSubtitlesToRemoveCaptions = style.removeCaptions
subtitleView?.context?.let { ctx ->
subStyle = style
Log.i(TAG,"SET STYLE = $style")
subtitleView?.setStyle(ctx.fromSaveToStyle(style))
subtitleView?.translationY = -style.elevation.toPx.toFloat()
val size = style.fixedTextSize

View file

@ -1901,7 +1901,7 @@ class ResultFragment : ResultTrailerPlayer() {
setRecommendations(d.recommendations, null)
setActors(d.actors)
setNextEpisode(if (d is EpisodeResponse) d.nextAiring else null)
setTrailers(d.trailers)
setTrailers(d.trailers.flatMap { it.mirros }) // I dont care about subtitles yet!
if (syncModel.addSyncs(d.syncData)) {
syncModel.updateMetaAndUser()

View file

@ -1,12 +1,12 @@
package com.lagradost.cloudstream3.utils
import android.net.Uri
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.USER_AGENT
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.extractors.*
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import kotlinx.coroutines.delay
import org.jsoup.Jsoup
@ -155,12 +155,26 @@ suspend fun unshortenLinkSafe(url: String): String {
}
}
suspend fun loadExtractor(
url: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
return loadExtractor(
url = url,
referer = null,
subtitleCallback = subtitleCallback,
callback = callback
)
}
/**
* Tries to load the appropriate extractor based on link, returns true if any extractor is loaded.
* */
suspend fun loadExtractor(
url: String,
referer: String? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val currentUrl = unshortenLinkSafe(url)
@ -169,7 +183,7 @@ suspend fun loadExtractor(
if (currentUrl.replace(schemaStripRegex, "")
.startsWith(extractor.mainUrl.replace(schemaStripRegex, ""))
) {
extractor.getSafeUrl(currentUrl, referer)?.forEach(callback)
extractor.getSafeUrl(currentUrl, referer, subtitleCallback, callback)
return true
}
}
@ -177,20 +191,6 @@ suspend fun loadExtractor(
return false
}
suspend fun loadExtractor(
url: String,
referer: String? = null,
): List<ExtractorLink> {
val currentUrl = unshortenLinkSafe(url)
for (extractor in extractorApis) {
if (currentUrl.startsWith(extractor.mainUrl)) {
return extractor.getSafeUrl(currentUrl, referer) ?: emptyList()
}
}
return emptyList()
}
val extractorApis: Array<ExtractorApi> = arrayOf(
//AllProvider(),
WcoStream(),
@ -372,14 +372,39 @@ abstract class ExtractorApi {
abstract val mainUrl: String
abstract val requiresReferer: Boolean
suspend fun getSafeUrl(url: String, referer: String? = null): List<ExtractorLink>? {
return suspendSafeApiCall { getUrl(url, referer) }
//suspend fun getSafeUrl(url: String, referer: String? = null): List<ExtractorLink>? {
// return suspendSafeApiCall { getUrl(url, referer) }
//}
// this is the new extractorapi, override to add subtitles and stuff
open suspend fun getUrl(
url: String,
referer: String? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
getUrl(url, referer)?.forEach(callback)
}
suspend fun getSafeUrl(
url: String,
referer: String? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
try {
getUrl(url, referer, subtitleCallback, callback)
} catch (e: Exception) {
logError(e)
}
}
/**
* Will throw errors, use getSafeUrl if you don't want to handle the exception yourself
*/
abstract suspend fun getUrl(url: String, referer: String? = null): List<ExtractorLink>?
open suspend fun getUrl(url: String, referer: String? = null): List<ExtractorLink>? {
return emptyList()
}
open fun getExtractorUrl(id: String): String {
return id