forked from recloudstream/cloudstream
New source with content in Latin Spanish (#369)
* Pelisplus spanish provider Co-authored-by: LagradOst <balt.758@gmail.com>
This commit is contained in:
parent
8c6e5a63d5
commit
bd91cfb5fc
11 changed files with 446 additions and 6 deletions
|
@ -75,4 +75,5 @@ It merely scrapes 3rd-party websites that are publicly accessable via any regula
|
||||||
- [pinoymoviepedia.ru](https://pinoymoviepedia.ru)
|
- [pinoymoviepedia.ru](https://pinoymoviepedia.ru)
|
||||||
- [pinoy-hd.xyz](https://www.pinoy-hd.xyz)
|
- [pinoy-hd.xyz](https://www.pinoy-hd.xyz)
|
||||||
- [asiaflix.app](https://asiaflix.app)
|
- [asiaflix.app](https://asiaflix.app)
|
||||||
|
- [pelisplus.icu](https://pelisplus.icu)
|
||||||
<!--SITE LIST END-->
|
<!--SITE LIST END-->
|
||||||
|
|
|
@ -27,6 +27,7 @@ object APIHolder {
|
||||||
private const val defProvider = 0
|
private const val defProvider = 0
|
||||||
|
|
||||||
val apis = arrayListOf(
|
val apis = arrayListOf(
|
||||||
|
PelisplusProvider(),
|
||||||
GogoanimeProvider(),
|
GogoanimeProvider(),
|
||||||
AllAnimeProvider(),
|
AllAnimeProvider(),
|
||||||
//ShiroProvider(), // v2 fucked me
|
//ShiroProvider(), // v2 fucked me
|
||||||
|
@ -622,4 +623,4 @@ fun MainAPI.newTvSeriesLoadResponse(
|
||||||
val builder = TvSeriesLoadResponse(name = name, url = url, apiName = this.name, type = type, episodes = episodes)
|
val builder = TvSeriesLoadResponse(name = name, url = url, apiName = this.name, type = type, episodes = episodes)
|
||||||
builder.initializer()
|
builder.initializer()
|
||||||
return builder
|
return builder
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,11 @@ class DoodSoExtractor : DoodLaExtractor() {
|
||||||
override val mainUrl = "https://dood.so"
|
override val mainUrl = "https://dood.so"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DoodWsExtractor : DoodLaExtractor() {
|
||||||
|
override val mainUrl = "https://dood.ws"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
open class DoodLaExtractor : ExtractorApi() {
|
open class DoodLaExtractor : ExtractorApi() {
|
||||||
override val name = "DoodStream"
|
override val name = "DoodStream"
|
||||||
override val mainUrl = "https://dood.la"
|
override val mainUrl = "https://dood.la"
|
||||||
|
@ -49,4 +54,4 @@ open class DoodLaExtractor : ExtractorApi() {
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import com.lagradost.cloudstream3.network.Session
|
||||||
|
import com.lagradost.cloudstream3.utils.*
|
||||||
|
import com.lagradost.cloudstream3.mapper
|
||||||
|
|
||||||
|
class FeHD: XStreamCdn() {
|
||||||
|
override val name: String = "FeHD"
|
||||||
|
override val mainUrl: String = "https://fembed-hd.com"
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import com.lagradost.cloudstream3.network.Session
|
||||||
|
import com.lagradost.cloudstream3.utils.*
|
||||||
|
import com.lagradost.cloudstream3.mapper
|
||||||
|
|
||||||
|
class Fplayer: XStreamCdn() {
|
||||||
|
override val name: String = "Fplayer"
|
||||||
|
override val mainUrl: String = "https://fplayer.info"
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package com.lagradost.cloudstream3.extractors
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.app
|
||||||
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
|
import com.lagradost.cloudstream3.pmap
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.extractorApis
|
||||||
|
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||||
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
|
||||||
|
/**
|
||||||
|
* overrideMainUrl is necessary for for other vidstream clones like vidembed.cc
|
||||||
|
* If they diverge it'd be better to make them separate.
|
||||||
|
* */
|
||||||
|
class Pelisplus(val mainUrl: String) {
|
||||||
|
val name: String = "Vidstream"
|
||||||
|
|
||||||
|
private fun getExtractorUrl(id: String): String {
|
||||||
|
return "$mainUrl/play?id=$id"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDownloadUrl(id: String): String {
|
||||||
|
return "$mainUrl/download?id=$id"
|
||||||
|
}
|
||||||
|
|
||||||
|
private val normalApis = arrayListOf(MultiQuality())
|
||||||
|
|
||||||
|
// https://gogo-stream.com/streaming.php?id=MTE3NDg5
|
||||||
|
fun getUrl(id: String, isCasting: Boolean = false, callback: (ExtractorLink) -> Unit): Boolean {
|
||||||
|
try {
|
||||||
|
normalApis.pmap { api ->
|
||||||
|
val url = api.getExtractorUrl(id)
|
||||||
|
val source = api.getSafeUrl(url)
|
||||||
|
source?.forEach { callback.invoke(it) }
|
||||||
|
}
|
||||||
|
val extractorUrl = getExtractorUrl(id)
|
||||||
|
|
||||||
|
/** Stolen from GogoanimeProvider.kt extractor */
|
||||||
|
normalSafeApiCall {
|
||||||
|
val link = getDownloadUrl(id)
|
||||||
|
println("Generated vidstream download link: $link")
|
||||||
|
val page = app.get(link, referer = extractorUrl)
|
||||||
|
|
||||||
|
val pageDoc = Jsoup.parse(page.text)
|
||||||
|
val qualityRegex = Regex("(\\d+)P")
|
||||||
|
|
||||||
|
//a[download]
|
||||||
|
pageDoc.select(".dowload > a")?.pmap { element ->
|
||||||
|
val href = element.attr("href") ?: return@pmap
|
||||||
|
val qual = if (element.text()
|
||||||
|
.contains("HDP")
|
||||||
|
) "1080" else qualityRegex.find(element.text())?.destructured?.component1().toString()
|
||||||
|
|
||||||
|
if (!loadExtractor(href, link, callback)) {
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
this.name,
|
||||||
|
if (qual == "null") this.name else "${this.name} - " + qual + "p",
|
||||||
|
href,
|
||||||
|
page.url,
|
||||||
|
getQualityFromName(qual),
|
||||||
|
element.attr("href").contains(".m3u8")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with(app.get(extractorUrl)) {
|
||||||
|
val document = Jsoup.parse(this.text)
|
||||||
|
val primaryLinks = document.select("ul.list-server-items > li.linkserver")
|
||||||
|
//val extractedLinksList: MutableList<ExtractorLink> = mutableListOf()
|
||||||
|
|
||||||
|
// All vidstream links passed to extractors
|
||||||
|
primaryLinks.distinctBy { it.attr("data-video") }.forEach { element ->
|
||||||
|
val link = element.attr("data-video")
|
||||||
|
//val name = element.text()
|
||||||
|
|
||||||
|
// Matches vidstream links with extractors
|
||||||
|
extractorApis.filter { !it.requiresReferer || !isCasting }.pmap { api ->
|
||||||
|
if (link.startsWith(api.mainUrl)) {
|
||||||
|
val extractedLinks = api.getSafeUrl(link, extractorUrl)
|
||||||
|
if (extractedLinks?.isNotEmpty() == true) {
|
||||||
|
extractedLinks.forEach {
|
||||||
|
callback.invoke(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,10 @@ import com.lagradost.cloudstream3.utils.Qualities
|
||||||
import com.lagradost.cloudstream3.utils.getPostForm
|
import com.lagradost.cloudstream3.utils.getPostForm
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
|
|
||||||
|
class SBPlay1 : SBPlay() {
|
||||||
|
override val mainUrl = "https://sbplay1.com"
|
||||||
|
}
|
||||||
|
|
||||||
class SBPlay2 : SBPlay() {
|
class SBPlay2 : SBPlay() {
|
||||||
override val mainUrl = "https://sbplay2.com"
|
override val mainUrl = "https://sbplay2.com"
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||||
|
|
||||||
class WatchSB : ExtractorApi() {
|
open class WatchSB : ExtractorApi() {
|
||||||
override val name: String
|
override val name: String
|
||||||
get() = "WatchSB"
|
get() = "WatchSB"
|
||||||
override val mainUrl: String
|
override val mainUrl: String
|
||||||
|
@ -22,7 +22,7 @@ class WatchSB : ExtractorApi() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val extractedLinksList = M3u8Helper().m3u8Generation(
|
return M3u8Helper().m3u8Generation(
|
||||||
M3u8Helper.M3u8Stream(
|
M3u8Helper.M3u8Stream(
|
||||||
response.url,
|
response.url,
|
||||||
headers = response.headers.toMap()
|
headers = response.headers.toMap()
|
||||||
|
@ -39,7 +39,5 @@ class WatchSB : ExtractorApi() {
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return extractedLinksList
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.lagradost.cloudstream3.movieproviders
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.TvType
|
||||||
|
|
||||||
|
/** Needs to inherit from MainAPI() to
|
||||||
|
* make the app know what functions to call
|
||||||
|
*/
|
||||||
|
class PelisplusProvider : PelisplusProviderTemplate() {
|
||||||
|
// mainUrl is good to have as a holder for the url to make future changes easier.
|
||||||
|
override val mainUrl: String
|
||||||
|
get() = "https://pelisplus.icu"
|
||||||
|
|
||||||
|
// name is for how the provider will be named which is visible in the UI, no real rules for this.
|
||||||
|
override val name: String
|
||||||
|
get() = "Pelisplus"
|
||||||
|
|
||||||
|
override val homePageUrlList: List<String> = listOf(
|
||||||
|
mainUrl,
|
||||||
|
"$mainUrl/movies",
|
||||||
|
"$mainUrl/series",
|
||||||
|
"$mainUrl/new-season",
|
||||||
|
"$mainUrl/popular"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is just extra metadata about what type of movies the provider has.
|
||||||
|
// Needed for search functionality.
|
||||||
|
override val supportedTypes: Set<TvType>
|
||||||
|
get() = setOf(TvType.TvSeries, TvType.Movie)
|
||||||
|
}
|
|
@ -0,0 +1,274 @@
|
||||||
|
package com.lagradost.cloudstream3.movieproviders
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.extractors.Pelisplus
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||||
|
import org.jsoup.Jsoup
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
|
/** Needs to inherit from MainAPI() to
|
||||||
|
* make the app know what functions to call
|
||||||
|
*/
|
||||||
|
|
||||||
|
open class PelisplusProviderTemplate : MainAPI() {
|
||||||
|
override val lang = "es"
|
||||||
|
open val homePageUrlList = listOf<String>()
|
||||||
|
open val pelisplusExtractorUrl: String? = null
|
||||||
|
|
||||||
|
// // mainUrl is good to have as a holder for the url to make future changes easier.
|
||||||
|
// override val mainUrl: String
|
||||||
|
// get() = "https://vidembed.cc"
|
||||||
|
//
|
||||||
|
// // name is for how the provider will be named which is visible in the UI, no real rules for this.
|
||||||
|
// override val name: String
|
||||||
|
// get() = "VidEmbed"
|
||||||
|
|
||||||
|
// hasQuickSearch defines if quickSearch() should be called, this is only when typing the searchbar
|
||||||
|
// gives results on the site instead of bringing you to another page.
|
||||||
|
// if hasQuickSearch is true and quickSearch() hasn't been overridden you will get errors.
|
||||||
|
// VidEmbed actually has quick search on their site, but the function wasn't implemented.
|
||||||
|
override val hasQuickSearch = false
|
||||||
|
|
||||||
|
// If getMainPage() is functional, used to display the homepage in app, an optional, but highly encouraged endevour.
|
||||||
|
override val hasMainPage = true
|
||||||
|
|
||||||
|
// Searching returns a SearchResponse, which can be one of the following: AnimeSearchResponse, MovieSearchResponse, TorrentSearchResponse, TvSeriesSearchResponse
|
||||||
|
// Each of the classes requires some different data, but always has some critical things like name, poster and url.
|
||||||
|
override fun search(query: String): ArrayList<SearchResponse> {
|
||||||
|
// Simply looking at devtools network is enough to spot a request like:
|
||||||
|
// https://vidembed.cc/search.html?keyword=neverland where neverland is the query, can be written as below.
|
||||||
|
val link = "$mainUrl/search.html?keyword=$query"
|
||||||
|
val html = app.get(link).text
|
||||||
|
val soup = Jsoup.parse(html)
|
||||||
|
|
||||||
|
return ArrayList(soup.select(".listing.items > .video-block").map { li ->
|
||||||
|
// Selects the href in <a href="...">
|
||||||
|
val href = fixUrl(li.selectFirst("a").attr("href"))
|
||||||
|
val poster = fixUrl(li.selectFirst("img").attr("src"))
|
||||||
|
|
||||||
|
// .text() selects all the text in the element, be careful about doing this while too high up in the html hierarchy
|
||||||
|
val title = li.selectFirst(".name").text()
|
||||||
|
// Use get(0) and toIntOrNull() to prevent any possible crashes, [0] or toInt() will error the search on unexpected values.
|
||||||
|
val year = li.selectFirst(".date")?.text()?.split("-")?.get(0)?.toIntOrNull()
|
||||||
|
|
||||||
|
TvSeriesSearchResponse(
|
||||||
|
// .trim() removes unwanted spaces in the start and end.
|
||||||
|
if (!title.contains("Episode")) title else title.split("Episode")[0].trim(),
|
||||||
|
href,
|
||||||
|
this.name,
|
||||||
|
TvType.TvSeries,
|
||||||
|
poster, year,
|
||||||
|
// You can't get the episodes from the search bar.
|
||||||
|
null
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Load, like the name suggests loads the info page, where all the episodes and data usually is.
|
||||||
|
// Like search you should return either of: AnimeLoadResponse, MovieLoadResponse, TorrentLoadResponse, TvSeriesLoadResponse.
|
||||||
|
override fun load(url: String): LoadResponse? {
|
||||||
|
// Gets the url returned from searching.
|
||||||
|
val html = app.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").map { 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 = fixUrl(li.selectFirst("img").attr("src"))
|
||||||
|
val epDate = li.selectFirst(".meta > .date").text()
|
||||||
|
|
||||||
|
if (poster == null) {
|
||||||
|
poster = li.selectFirst("img")?.attr("onerror")?.replace("//img", "https://img")?.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 = episodes.first().date?.split("-")?.get(0)?.toIntOrNull()
|
||||||
|
|
||||||
|
// Make sure to get the type right to display the correct UI.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This loads the homepage, which is basically a collection of search results with labels.
|
||||||
|
// Optional function, but make sure to enable hasMainPage if you program this.
|
||||||
|
override fun getMainPage(): HomePageResponse {
|
||||||
|
val urls = homePageUrlList
|
||||||
|
val homePageList = ArrayList<HomePageList>()
|
||||||
|
// .pmap {} is used to fetch the different pages in parallel
|
||||||
|
urls.pmap { url ->
|
||||||
|
val response = app.get(url, timeout = 20).text
|
||||||
|
val document = Jsoup.parse(response)
|
||||||
|
document.select("div.main-inner")?.forEach { inner ->
|
||||||
|
// Always trim your text unless you want the risk of spaces at the start or end.
|
||||||
|
val title = inner.select(".widget-title").text().trim()
|
||||||
|
val elements = inner.select(".video-block").map {
|
||||||
|
val link = fixUrl(it.select("a").attr("href"))
|
||||||
|
val image = it.select(".picture > img").attr("src").replace("//img", "https://img")
|
||||||
|
val name = it.select("div.name").text().trim().replace(Regex("""[Ee]pisode \d+"""), "")
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadLinks gets the raw .mp4 or .m3u8 urls from the data parameter in the episodes class generated in load()
|
||||||
|
// See TvSeriesEpisode(...) in this provider.
|
||||||
|
// The data are usually links, but can be any other string to help aid loading the links.
|
||||||
|
override fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
// These callbacks are functions you should call when you get a link to a subtitle file or media file.
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
|
// "?: return" is a very useful statement which returns if the iframe link isn't found.
|
||||||
|
val iframeLink = Jsoup.parse(app.get(data).text).selectFirst(".tab-video")?.attr("data-video") ?: return false
|
||||||
|
|
||||||
|
// 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 = Pelisplus(pelisplusExtractorUrl ?: mainUrl)
|
||||||
|
// 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 = app.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 {
|
||||||
|
// When checking strings make sure to make them lowercase and trimmed because edgecases like "beta server " wouldn't work otherwise.
|
||||||
|
if (it.first.trim().equals("beta server", ignoreCase = true)) {
|
||||||
|
// Group 1: link, Group 2: Label
|
||||||
|
// Regex can be used to effectively parse small amounts of json without bothering with writing a json class.
|
||||||
|
val sourceRegex = Regex("""sources:[\W\w]*?file:\s*["'](.*?)["'][\W\w]*?label:\s*["'](.*?)["']""")
|
||||||
|
val trackRegex = Regex("""tracks:[\W\w]*?file:\s*["'](.*?)["'][\W\w]*?label:\s*["'](.*?)["']""")
|
||||||
|
|
||||||
|
// Having a referer is often required. It's a basic security check most providers have.
|
||||||
|
// Try to replicate what your browser does.
|
||||||
|
val serverHtml = app.get(it.second, headers = mapOf("referer" to iframeLink)).text
|
||||||
|
sourceRegex.findAll(serverHtml).forEach { match ->
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
this.name,
|
||||||
|
match.groupValues.getOrNull(2)?.let { "${this.name} $it" } ?: this.name,
|
||||||
|
match.groupValues[1],
|
||||||
|
it.second,
|
||||||
|
// Useful function to turn something like "1080p" to an app quality.
|
||||||
|
getQualityFromName(match.groupValues.getOrNull(2) ?: ""),
|
||||||
|
// Kinda risky
|
||||||
|
// isM3u8 makes the player pick the correct extractor for the source.
|
||||||
|
// If isM3u8 is wrong the player will error on that source.
|
||||||
|
URI(match.groupValues[1]).path.endsWith(".m3u8"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
trackRegex.findAll(serverHtml).forEach { match ->
|
||||||
|
subtitleCallback.invoke(
|
||||||
|
SubtitleFile(
|
||||||
|
match.groupValues.getOrNull(2) ?: "Unknown",
|
||||||
|
match.groupValues[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,6 +81,8 @@ val extractorApis: Array<ExtractorApi> = arrayOf(
|
||||||
Streamhub(),
|
Streamhub(),
|
||||||
|
|
||||||
FEmbed(),
|
FEmbed(),
|
||||||
|
FeHD(),
|
||||||
|
Fplayer(),
|
||||||
WatchSB(),
|
WatchSB(),
|
||||||
Uqload(),
|
Uqload(),
|
||||||
Evoload(),
|
Evoload(),
|
||||||
|
@ -91,10 +93,12 @@ val extractorApis: Array<ExtractorApi> = arrayOf(
|
||||||
DoodToExtractor(),
|
DoodToExtractor(),
|
||||||
DoodSoExtractor(),
|
DoodSoExtractor(),
|
||||||
DoodLaExtractor(),
|
DoodLaExtractor(),
|
||||||
|
DoodWsExtractor(),
|
||||||
|
|
||||||
AsianLoad(),
|
AsianLoad(),
|
||||||
|
|
||||||
SBPlay(),
|
SBPlay(),
|
||||||
|
SBPlay1(),
|
||||||
SBPlay2(),
|
SBPlay2(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue