fixed search, made stuff more parallel

This commit is contained in:
LagradOst 2022-01-30 02:50:49 +01:00
parent b0cadda315
commit e0d9171f3e
26 changed files with 312 additions and 221 deletions

View File

@ -115,13 +115,13 @@ object APIHolder {
}
fun Context.getApiSettings(): HashSet<String> {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
//val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
val hashSet = HashSet<String>()
val activeLangs = getApiProviderLangSettings()
hashSet.addAll(apis.filter { activeLangs.contains(it.lang) }.map { it.name })
val set = settingsManager.getStringSet(
/*val set = settingsManager.getStringSet(
this.getString(R.string.search_providers_list_key),
hashSet
)?.toHashSet() ?: hashSet
@ -132,9 +132,10 @@ object APIHolder {
if (activeLangs.contains(api.lang)) {
list.add(name)
}
}
if (list.isEmpty()) return hashSet
return list
}*/
//if (list.isEmpty()) return hashSet
//return list
return hashSet
}
fun Context.getApiDubstatusSettings(): HashSet<DubStatus> {

View File

@ -1,5 +1,6 @@
package com.lagradost.cloudstream3
import com.lagradost.cloudstream3.mvvm.logError
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
@ -43,8 +44,17 @@ fun <A, B> List<A>.apmap(f: suspend (A) -> B): List<B> = runBlocking {
exec.awaitTermination(1, TimeUnit.DAYS)
}*/
// built in try catch
fun <R> argamap(
vararg transforms: suspend () -> R,
) = runBlocking {
transforms.map { async { it.invoke() } }.map { it.await() }
transforms.map {
async {
try {
it.invoke()
} catch (e: Exception) {
logError(e)
}
}
}.map { it.await() }
}

View File

@ -258,7 +258,7 @@ class AllAnimeProvider : MainAPI() {
val sources = Regex("""sourceUrl[:=]"(.+?)"""").findAll(html).toList()
.map { URLDecoder.decode(it.destructured.component1().sanitize(), "UTF-8") }
sources.forEach {
sources.apmap {
var link = it
if (URI(link).isAbsolute || link.startsWith("//")) {
if (link.startsWith("//")) link = "https:$it"

View File

@ -95,9 +95,7 @@ class AnimeFlickProvider : MainAPI() {
var alreadyAdded = false
for (extractor in extractorApis) {
if (link.startsWith(extractor.mainUrl)) {
extractor.getSafeUrl(link, data)?.forEach {
callback(it)
}
extractor.getSafeUrl(link, data)?.forEach(callback)
alreadyAdded = true
break
}

View File

@ -52,8 +52,11 @@ class WcoProvider : MainAPI() {
val nameHeader = filmDetail.selectFirst("> h3.film-name > a")
val title = nameHeader.text().replace(" (Dub)", "")
val href =
nameHeader.attr("href").replace("/watch/", "/anime/").replace("-episode-.*".toRegex(), "/")
val isDub = filmPoster.selectFirst("> div.film-poster-quality")?.text()?.contains("DUB") ?: false
nameHeader.attr("href").replace("/watch/", "/anime/")
.replace("-episode-.*".toRegex(), "/")
val isDub =
filmPoster.selectFirst("> div.film-poster-quality")?.text()?.contains("DUB")
?: false
val poster = filmPoster.selectFirst("> img").attr("data-src")
val set: EnumSet<DubStatus> =
EnumSet.of(if (isDub) DubStatus.Dubbed else DubStatus.Subbed)
@ -84,8 +87,11 @@ class WcoProvider : MainAPI() {
val img = fixUrl(i.selectFirst("img").attr("data-src"))
val title = i.selectFirst("img").attr("title")
val isDub = !i.select(".pick.film-poster-quality").isEmpty()
val year = i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(1)").text().toIntOrNull()
val type = i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(3)").text()
val year =
i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(1)").text()
.toIntOrNull()
val type =
i.selectFirst(".film-detail.film-detail-fix > div > span:nth-child(3)").text()
returnValue.add(
if (getType(type) == TvType.AnimeMovie) {
@ -174,8 +180,9 @@ class WcoProvider : MainAPI() {
val response = app.get(url, timeout = 120).text
val document = Jsoup.parse(response)
val japaneseTitle = document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(1)")
?.text()?.trim()?.replace("Other names:", "")?.trim()
val japaneseTitle =
document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(1)")
?.text()?.trim()?.replace("Other names:", "")?.trim()
val canonicalTitle = document.selectFirst("meta[name=\"title\"]")
?.attr("content")?.split("| W")?.get(0).toString()
@ -187,22 +194,25 @@ class WcoProvider : MainAPI() {
AnimeEpisode(it.attr("href"))
} ?: ArrayList())
val statusElem = document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(2)")
val statusElem =
document.selectFirst("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(2)")
val status = when (statusElem?.text()?.replace("Status:", "")?.trim()) {
"Ongoing" -> ShowStatus.Ongoing
"Completed" -> ShowStatus.Completed
else -> null
}
val yearText =
document.selectFirst("div.elements div.row > div:nth-child(2) > div.row-line:nth-child(4)")?.text()
document.selectFirst("div.elements div.row > div:nth-child(2) > div.row-line:nth-child(4)")
?.text()
val year = yearText?.replace("Date release:", "")?.trim()?.split("-")?.get(0)?.toIntOrNull()
val poster = document.selectFirst(".film-poster-img")?.attr("src")
val type = document.selectFirst("span.item.mr-1 > a")?.text()?.trim()
val synopsis = document.selectFirst(".description > p")?.text()?.trim()
val genre = document.select("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(5) > a")
.map { it?.text()?.trim().toString() }
val genre =
document.select("div.elements div.row > div:nth-child(1) > div.row-line:nth-child(5) > a")
.map { it?.text()?.trim().toString() }
return newAnimeLoadResponse(canonicalTitle, url, getType(type ?: "")) {
japName = japaneseTitle
@ -231,9 +241,7 @@ class WcoProvider : MainAPI() {
}
for (server in servers) {
WcoStream().getSafeUrl(server["link"].toString(), "")?.forEach {
callback.invoke(it)
}
WcoStream().getSafeUrl(server["link"].toString(), "")?.forEach(callback)
}
return true
}

View File

@ -2,7 +2,7 @@ package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
import com.lagradost.cloudstream3.argamap
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.extractorApis
import com.lagradost.cloudstream3.utils.getQualityFromName
@ -27,17 +27,22 @@ class Vidstream(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 {
try {
normalApis.apmap { 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 */
suspendSafeApiCall {
suspend fun getUrl(
id: String,
isCasting: Boolean = false,
callback: (ExtractorLink) -> Unit
): Boolean {
println("VIDSTREAM:: $id")
val extractorUrl = getExtractorUrl(id)
argamap(
{
normalApis.apmap { api ->
val url = api.getExtractorUrl(id)
val source = api.getSafeUrl(url)
source?.forEach { callback.invoke(it) }
}
}, {
/** Stolen from GogoanimeProvider.kt extractor */
val link = getDownloadUrl(id)
println("Generated vidstream download link: $link")
val page = app.get(link, referer = extractorUrl)
@ -50,7 +55,8 @@ class Vidstream(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)) {
callback.invoke(
@ -65,34 +71,32 @@ class Vidstream(val mainUrl: String) {
)
}
}
}
}, {
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()
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()
// 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 }.apmap { api ->
if (link.startsWith(api.mainUrl)) {
val extractedLinks = api.getSafeUrl(link, extractorUrl)
if (extractedLinks?.isNotEmpty() == true) {
extractedLinks.forEach {
callback.invoke(it)
// 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)
}
}
}
}
}
}
return true
}
} catch (e: Exception) {
return false
}
)
return true
}
}

View File

@ -1,9 +1,8 @@
package com.lagradost.cloudstream3.extractors
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mapper
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
@ -29,14 +28,14 @@ open class VoeExtractor : ExtractorApi() {
.replace("0,", "0")
.trim()
//Log.i(this.name, "Result => (src) ${src}")
mapper.readValue<ResponseLinks?>(src)?.let { voelink ->
parseJson<ResponseLinks?>(src)?.let { voelink ->
//Log.i(this.name, "Result => (voelink) ${voelink}")
val linkUrl = voelink.url
val linkLabel = voelink.label?.toString() ?: ""
if (!linkUrl.isNullOrEmpty()) {
extractedLinksList.add(
ExtractorLink(
name = "Voe ${linkLabel}",
name = "Voe $linkLabel",
source = this.name,
url = linkUrl,
quality = getQualityFromName(linkLabel),

View File

@ -2,8 +2,11 @@ package com.lagradost.cloudstream3.movieproviders
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.getPostForm
import com.lagradost.cloudstream3.utils.loadExtractor
import okio.Buffer
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
@ -211,14 +214,7 @@ class AllMoviesForYouProvider : MainAPI() {
}
}
} else if (requestUrl.startsWith("https://dood")) {
for (extractor in extractorApis) {
if (requestUrl.startsWith(extractor.mainUrl)) {
extractor.getSafeUrl(requestUrl)?.forEach { link ->
callback(link)
}
break
}
}
loadExtractor(requestUrl, null, callback)
} else {
callback(
ExtractorLink(

View File

@ -1,10 +1,11 @@
package com.lagradost.cloudstream3.movieproviders
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.extractorApis
import java.util.*
class CinecalidadProvider:MainAPI() {
class CinecalidadProvider : MainAPI() {
override val mainUrl = "https://cinecalidad.lol"
override val name = "Cinecalidad"
override val lang = "es"
@ -15,6 +16,7 @@ class CinecalidadProvider:MainAPI() {
TvType.Movie,
TvType.TvSeries,
)
override suspend fun getMainPage(): HomePageResponse {
val items = ArrayList<HomePageList>()
val urls = listOf(
@ -22,18 +24,22 @@ class CinecalidadProvider:MainAPI() {
Pair("$mainUrl/genero-de-la-pelicula/peliculas-en-calidad-4k/", "4K UHD"),
)
items.add(HomePageList("Series",app.get("$mainUrl/ver-serie/").document.select(".item.tvshows").map{
val title = it.selectFirst("div.in_title").text()
TvSeriesSearchResponse(
title,
it.selectFirst("a").attr("href"),
this.name,
TvType.TvSeries,
it.selectFirst(".poster.custom img").attr("data-src"),
null,
null,
)
}))
items.add(
HomePageList(
"Series",
app.get("$mainUrl/ver-serie/").document.select(".item.tvshows").map {
val title = it.selectFirst("div.in_title").text()
TvSeriesSearchResponse(
title,
it.selectFirst("a").attr("href"),
this.name,
TvType.TvSeries,
it.selectFirst(".poster.custom img").attr("data-src"),
null,
null,
)
})
)
for (i in urls) {
try {
@ -100,7 +106,9 @@ class CinecalidadProvider:MainAPI() {
val soup = app.get(url, timeout = 120).document
val title = soup.selectFirst(".single_left h1").text()
val description = soup.selectFirst(".single_left > table:nth-child(3) > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(2) > p")?.text()?.trim()
val description =
soup.selectFirst(".single_left > table:nth-child(3) > tbody:nth-child(1) > tr:nth-child(1) > td:nth-child(2) > p")
?.text()?.trim()
val poster: String? = soup.selectFirst(".alignnone").attr("data-src")
val episodes = soup.select("div.se-c div.se-a ul.episodios li").map { li ->
val href = li.selectFirst("a").attr("href")
@ -114,7 +122,8 @@ class CinecalidadProvider:MainAPI() {
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,
@ -157,9 +166,10 @@ class CinecalidadProvider:MainAPI() {
val urlserver = app.get(url).text
val serverRegex = Regex("(https:.*?\\\")")
val videos = serverRegex.findAll(urlserver).map {
it.value.replace("\\/", "/").replace("\"","")
it.value.replace("\\/", "/").replace("\"", "")
}.toList()
val serversRegex = Regex("(https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&\\/\\/=]*))")
val serversRegex =
Regex("(https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&\\/\\/=]*))")
val links = serversRegex.findAll(videos.toString()).map { it.value }.toList()
for (link in links) {
for (extractor in extractorApis) {

View File

@ -1,16 +1,14 @@
package com.lagradost.cloudstream3.animeproviders
import com.lagradost.cloudstream3.*
import java.util.*
import com.lagradost.cloudstream3.extractors.FEmbed
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import java.util.*
import kotlin.collections.ArrayList
class DoramasYTProvider:MainAPI() {
class DoramasYTProvider : MainAPI() {
companion object {
fun getType(t: String): TvType {
return if (t.contains("OVA") || t.contains("Especial")) TvType.ONA
@ -33,31 +31,44 @@ class DoramasYTProvider:MainAPI() {
override suspend fun getMainPage(): HomePageResponse {
val urls = listOf(
Pair("$mainUrl/emision", "En emisión"),
Pair("$mainUrl/doramas?categoria=pelicula&genero=false&fecha=false&letra=false", "Peliculas"),
Pair(
"$mainUrl/doramas?categoria=pelicula&genero=false&fecha=false&letra=false",
"Peliculas"
),
Pair("$mainUrl/doramas", "Doramas"),
Pair("$mainUrl/doramas?categoria=live-action&genero=false&fecha=false&letra=false", "Live Action"),
Pair(
"$mainUrl/doramas?categoria=live-action&genero=false&fecha=false&letra=false",
"Live Action"
),
)
val items = ArrayList<HomePageList>()
items.add(HomePageList("Capítulos actualizados", app.get(mainUrl, timeout = 120).document.select(".col-6").map{
val title = it.selectFirst("p").text()
val poster = it.selectFirst(".chapter img").attr("src")
val epRegex = Regex("episodio-(\\d+)")
val url = it.selectFirst("a").attr("href").replace("ver/","dorama/").replace(epRegex,"sub-espanol")
val epNum = it.selectFirst("h3").text().toIntOrNull()
AnimeSearchResponse(
title,
url,
this.name,
TvType.Anime,
poster,
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed),
subEpisodes = epNum,
dubEpisodes = epNum,
)
}))
items.add(
HomePageList(
"Capítulos actualizados",
app.get(mainUrl, timeout = 120).document.select(".col-6").map {
val title = it.selectFirst("p").text()
val poster = it.selectFirst(".chapter img").attr("src")
val epRegex = Regex("episodio-(\\d+)")
val url = it.selectFirst("a").attr("href").replace("ver/", "dorama/")
.replace(epRegex, "sub-espanol")
val epNum = it.selectFirst("h3").text().toIntOrNull()
AnimeSearchResponse(
title,
url,
this.name,
TvType.Anime,
poster,
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(
DubStatus.Dubbed
) else EnumSet.of(DubStatus.Subbed),
subEpisodes = epNum,
dubEpisodes = epNum,
)
})
)
for (i in urls) {
try {
@ -72,7 +83,9 @@ class DoramasYTProvider:MainAPI() {
TvType.Anime,
poster,
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed),
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(
DubStatus.Dubbed
) else EnumSet.of(DubStatus.Subbed),
)
}
@ -87,28 +100,32 @@ class DoramasYTProvider:MainAPI() {
}
override suspend fun search(query: String): ArrayList<SearchResponse> {
val search = app.get("$mainUrl/buscar?q=$query", timeout = 120).document.select(".col-6").map {
val title = it.selectFirst(".animedtls p").text()
val href = it.selectFirst("a").attr("href")
val image = it.selectFirst(".animes img").attr("src")
AnimeSearchResponse(
title,
href,
this.name,
TvType.Anime,
image,
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(DubStatus.Dubbed) else EnumSet.of(DubStatus.Subbed),
)
}
val search =
app.get("$mainUrl/buscar?q=$query", timeout = 120).document.select(".col-6").map {
val title = it.selectFirst(".animedtls p").text()
val href = it.selectFirst("a").attr("href")
val image = it.selectFirst(".animes img").attr("src")
AnimeSearchResponse(
title,
href,
this.name,
TvType.Anime,
image,
null,
if (title.contains("Latino") || title.contains("Castellano")) EnumSet.of(
DubStatus.Dubbed
) else EnumSet.of(DubStatus.Subbed),
)
}
return ArrayList(search)
}
override suspend fun load(url: String): LoadResponse {
val doc = app.get(url, timeout = 120).document
val poster = doc.selectFirst("div.flimimg img.img1").attr("src")
val title = doc.selectFirst("h1").text()
val type = doc.selectFirst("h4").text()
val description = doc.selectFirst("p.textComplete").text().replace("Ver menos","")
val description = doc.selectFirst("p.textComplete").text().replace("Ver menos", "")
val genres = doc.select(".nobel a").map { it.text() }
val status = when (doc.selectFirst(".state h6")?.text()) {
"Estreno" -> ShowStatus.Ongoing
@ -129,13 +146,14 @@ class DoramasYTProvider:MainAPI() {
tags = genres
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
app.get(data).document.select("div.playother p").forEach {
app.get(data).document.select("div.playother p").apmap {
val encodedurl = it.select("p").attr("data-player")
val urlDecoded = base64Decode(encodedurl)
val url = (urlDecoded).replace("https://doramasyt.com/reproductor?url=", "")

View File

@ -157,13 +157,10 @@ class DramaSeeProvider : MainAPI() {
callback: (ExtractorLink) -> Unit
): Boolean {
var count = 0
mapper.readValue<List<String>>(data).forEach { item ->
mapper.readValue<List<String>>(data).apmap { item ->
if (item.isNotEmpty()) {
count++
var url = item.trim()
if (url.startsWith("//")) {
url = "https:$url"
}
var url = fixUrl(item.trim())
//Log.i(this.name, "Result => (url) ${url}")
when {
url.startsWith("https://asianembed.io") -> {

View File

@ -69,7 +69,17 @@ class FilmanProvider : MainAPI() {
val img = i.selectFirst("> img").attr("src").replace("/thumb/", "/big/")
val name = i.selectFirst(".title").text()
if (type === TvType.TvSeries) {
returnValue.add(TvSeriesSearchResponse(name, href, this.name, type, img, null, null))
returnValue.add(
TvSeriesSearchResponse(
name,
href,
this.name,
type,
img,
null,
null
)
)
} else {
returnValue.add(MovieSearchResponse(name, href, this.name, type, img, null))
}
@ -98,15 +108,26 @@ class FilmanProvider : MainAPI() {
val regex = Regex("""\[s(\d{1,3})e(\d{1,3})]""").find(e)
if (regex != null) {
val eid = regex.groups
episodes.add(TvSeriesEpisode(
e.split("]")[1].trim(),
eid[1]?.value?.toInt(),
eid[2]?.value?.toInt(),
episode.attr("href"),
))
episodes.add(
TvSeriesEpisode(
e.split("]")[1].trim(),
eid[1]?.value?.toInt(),
eid[2]?.value?.toInt(),
episode.attr("href"),
)
)
}
}
return TvSeriesLoadResponse(title, url, name, TvType.TvSeries, episodes, posterUrl, year, plot)
return TvSeriesLoadResponse(
title,
url,
name,
TvType.TvSeries,
episodes,
posterUrl,
year,
plot
)
}
override suspend fun loadLinks(
@ -115,20 +136,16 @@ class FilmanProvider : MainAPI() {
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
if(data.isEmpty()) {
return false
}
val document = if (data.startsWith("http"))
Jsoup.parse(app.get(data).text).select("#links").first()
else Jsoup.parse(data)
app.get(data).document.select("#links").first()
else Jsoup.parse(data)
val items = document.select(".link-to-video")
for (i in items) {
val decoded = base64Decode(i.select("a").attr("data-iframe"))
document.select(".link-to-video")?.apmap { item ->
val decoded = base64Decode(item.select("a").attr("data-iframe"))
val link = mapper.readValue<LinkElement>(decoded).src
loadExtractor(link, null, callback)
}
return true
return true
}
}

View File

@ -1,8 +1,8 @@
package com.lagradost.cloudstream3.movieproviders
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.extractors.StreamTape
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import java.net.URLEncoder
@ -18,8 +18,23 @@ class IHaveNoTvProvider : MainAPI() {
// Uhh, I am too lazy to scrape the "latest documentaries" and "recommended documentaries",
// so I am just scraping 3 random categories
val allCategories = listOf(
"astronomy", "brain", "creativity", "design", "economics", "environment", "health", "history",
"lifehack", "math", "music", "nature", "people", "physics", "science", "technology", "travel"
"astronomy",
"brain",
"creativity",
"design",
"economics",
"environment",
"health",
"history",
"lifehack",
"math",
"music",
"nature",
"people",
"physics",
"science",
"technology",
"travel"
)
val categories = allCategories.asSequence().shuffled().take(3)
@ -82,7 +97,9 @@ class IHaveNoTvProvider : MainAPI() {
res.selectFirst("a[href][title]")
}
val year =
Regex("""•?\s+(\d{4})\s+•""").find(res.selectFirst(".episodeMeta").text())?.destructured?.component1()
Regex("""•?\s+(\d{4})\s+•""").find(
res.selectFirst(".episodeMeta").text()
)?.destructured?.component1()
?.toIntOrNull()
val title = aTag.attr("title")
@ -138,7 +155,8 @@ class IHaveNoTvProvider : MainAPI() {
ep.selectFirst(".episodeMeta").text()
)?.destructured?.component1()?.toIntOrNull()
categories.addAll(ep.select(".episodeMeta > a[href*=\"/category/\"]").map { it.text().trim() })
categories.addAll(
ep.select(".episodeMeta > a[href*=\"/category/\"]").map { it.text().trim() })
TvSeriesEpisode(
epTitle,
@ -165,7 +183,8 @@ class IHaveNoTvProvider : MainAPI() {
description,
null,
null,
soup.selectFirst(".videoDetails").select("a[href*=\"/category/\"]").map { it.text().trim() }
soup.selectFirst(".videoDetails").select("a[href*=\"/category/\"]")
.map { it.text().trim() }
))
}
@ -201,9 +220,7 @@ class IHaveNoTvProvider : MainAPI() {
val iframe = soup.selectFirst("#videoWrap iframe")
if (iframe != null) {
if (iframe.attr("src").startsWith("https://streamtape.com")) {
StreamTape().getSafeUrl(iframe.attr("src"))?.forEach(callback)
}
loadExtractor(iframe.attr("src"), null, callback)
}
return true
}

View File

@ -1,6 +1,5 @@
package com.lagradost.cloudstream3.movieproviders
import android.util.Log
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.extractors.*
@ -9,7 +8,6 @@ import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.loadExtractor
import org.jsoup.Jsoup
import kotlin.Exception
class KdramaHoodProvider : MainAPI() {
override val mainUrl = "https://kdramahood.com"
@ -131,13 +129,10 @@ class KdramaHoodProvider : MainAPI() {
if (!epLinksContent.isNullOrEmpty()) {
//Log.i(this.name, "Result => (epLinksContent) ${Jsoup.parse(epLinksContent)?.select("div")}")
Jsoup.parse(epLinksContent)?.select("div")?.forEach { em ->
var href = em?.html()?.trim()?.removePrefix("'") ?: return@forEach
if (href.startsWith("//")) {
href = "https:$href"
}
val href = em?.html()?.trim()?.removePrefix("'") ?: return@forEach
//Log.i(this.name, "Result => (ep#$count link) $href")
if (href.isNotEmpty()) {
listOfLinks.add(href)
listOfLinks.add(fixUrl(href))
}
}
/* Doesn't get all links for some reasons
@ -188,7 +183,7 @@ class KdramaHoodProvider : MainAPI() {
callback: (ExtractorLink) -> Unit
): Boolean {
var count = 0
mapper.readValue<List<String>>(data).forEach { item ->
mapper.readValue<List<String>>(data).apmap { item ->
if (item.isNotEmpty()) {
count++
var url = item.trim()

View File

@ -231,7 +231,7 @@ open class PelisplusProviderTemplate : MainAPI() {
null
}
}
servers.forEach {
servers.apmap {
// 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

View File

@ -1,6 +1,5 @@
package com.lagradost.cloudstream3.movieproviders
import android.util.Log
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.toJson
@ -192,7 +191,7 @@ class PinoyHDXyzProvider : MainAPI() {
callback: (ExtractorLink) -> Unit
): Boolean {
var count = 0
mapper.readValue<List<String>>(data).forEach { item ->
mapper.readValue<List<String>>(data).apmap { item ->
if (item.isNotEmpty()) {
val url = item.trim()
loadExtractor(url, mainUrl, callback)

View File

@ -179,7 +179,7 @@ class PinoyMoviePediaProvider : MainAPI() {
): Boolean {
// parse movie servers
var count = 0
mapper.readValue<List<String>>(data).forEach { link ->
mapper.readValue<List<String>>(data).apmap { link ->
count++
if (link.contains("fembed.com")) {
val extractor = FEmbed()

View File

@ -136,7 +136,7 @@ class PinoyMoviesEsProvider : MainAPI() {
it?.attr("data-post") ?: return@mapNotNull null
}?.filter { it.isNotEmpty() }?.distinct() ?: listOf()
postlist.forEach { datapost ->
postlist.apmap { datapost ->
//Log.i(this.name, "Result => (datapost) ${datapost}")
val content = mapOf(
Pair("action", "doo_player_ajax"),
@ -163,7 +163,7 @@ class PinoyMoviesEsProvider : MainAPI() {
): Boolean {
// parse movie servers
var count = 0
mapper.readValue<List<String>>(data).forEach { link ->
mapper.readValue<List<String>>(data).apmap { link ->
count++
//Log.i(this.name, "Result => (link) $link")
if (link.startsWith("https://vstreamhub.com")) {

View File

@ -215,10 +215,9 @@ class SflixProvider(providerUrl: String, providerName: String) : MainAPI() {
): Boolean {
val urls = (tryParseJson<Pair<String, String>>(data)?.let { (prefix, server) ->
val episodesUrl = "$mainUrl/ajax/v2/episode/servers/$server"
val episodes = app.get(episodesUrl).text
// Supported streams, they're identical
Jsoup.parse(episodes).select("a").mapNotNull { element ->
app.get(episodesUrl).document.select("a").mapNotNull { element ->
val id = element?.attr("data-id") ?: return@mapNotNull null
if (element.select("span")?.text()?.trim()?.isValidServer() == true) {
"$prefix.$id".replace("/tv/", "/watch-tv/")
@ -250,7 +249,6 @@ class SflixProvider(providerUrl: String, providerName: String) : MainAPI() {
mapped.sources2 to "source 3",
mapped.sourcesBackup to "source backup"
).forEach { (sources, sourceName) ->
println("SOURCE:::: $sourceName $sources")
sources?.forEach {
it?.toExtractorLink(this, sourceName)?.forEach(callback)
}

View File

@ -77,36 +77,40 @@ open class VidstreamProviderTemplate : MainAPI() {
val description = soup.selectFirst(".post-entry")?.text()?.trim()
var poster: String? = null
val episodes = soup.select(".listing.items.lists > .video-block").withIndex().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 = li.selectFirst("img")?.attr("src")
val epDate = li.selectFirst(".meta > .date").text()
val episodes =
soup.select(".listing.items.lists > .video-block").withIndex().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 = li.selectFirst("img")?.attr("src")
val epDate = li.selectFirst(".meta > .date").text()
if (poster == null) {
poster = li.selectFirst("img")?.attr("onerror")?.split("=")?.get(1)?.replace(Regex("[';]"), "")
}
if (poster == null) {
poster = li.selectFirst("img")?.attr("onerror")?.split("=")?.get(1)
?.replace(Regex("[';]"), "")
}
val epNum = Regex("""Episode (\d+)""").find(epTitle)?.destructured?.component1()?.toIntOrNull()
val epNum = Regex("""Episode (\d+)""").find(epTitle)?.destructured?.component1()
?.toIntOrNull()
TvSeriesEpisode(
epTitle,
null,
epNum,
fixUrl(li.selectFirst("a").attr("href")),
epThumb,
epDate
)
}.reversed()
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
val tvType =
if (episodes.size == 1 && episodes[0].name == title) TvType.Movie else TvType.TvSeries
return when (tvType) {
TvType.TvSeries -> {
@ -157,7 +161,8 @@ open class VidstreamProviderTemplate : MainAPI() {
val elements = inner.select(".video-block").map {
val link = fixUrl(it.select("a").attr("href"))
val image = it.select(".picture > img").attr("src")
val name = it.select("div.name").text().trim().replace(Regex("""[Ee]pisode \d+"""), "")
val name =
it.select("div.name").text().trim().replace(Regex("""[Ee]pisode \d+"""), "")
val isSeries = (name.contains("Season") || name.contains("Episode"))
if (isSeries) {
@ -188,9 +193,7 @@ open class VidstreamProviderTemplate : MainAPI() {
title, elements
)
)
}
}
return HomePageResponse(homePageList)
}
@ -206,7 +209,8 @@ open class VidstreamProviderTemplate : MainAPI() {
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("iframe")?.attr("src") ?: return false
val iframeLink =
Jsoup.parse(app.get(data).text).selectFirst("iframe")?.attr("src") ?: 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.
@ -228,13 +232,15 @@ open class VidstreamProviderTemplate : MainAPI() {
null
}
}
servers.forEach {
servers.apmap {
// 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)) {
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*["'](.*?)["']""")
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.

View File

@ -174,12 +174,9 @@ class WatchAsianProvider : MainAPI() {
getServerLinks(data)
} else { data }
var count = 0
mapper.readValue<List<String>>(links).forEach { item ->
mapper.readValue<List<String>>(links).apmap { item ->
count++
var url = item.trim()
if (url.startsWith("//")) {
url = "https:$url"
}
val url = fixUrl(item.trim())
//Log.i(this.name, "Result => (url) $url")
if (url.startsWith("https://asianembed.io")) {
// Fetch links

View File

@ -9,7 +9,7 @@ import java.net.InetAddress
* Based on https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/main/java/eu/kanade/tachiyomi/network/DohProviders.kt
*/
fun OkHttpClient.Builder.addGenericDns(url: String, ips: List<String>) = dns(
fun OkHttpClient.Builder.addGenericDns(url: String, ips: List<String>) = dns(
DnsOverHttps
.Builder()
.client(build())

View File

@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.network
import android.content.Context
import android.util.Log
import androidx.preference.PreferenceManager
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.R
@ -286,6 +287,7 @@ open class Requests {
timeout: Long = 0L,
interceptor: Interceptor? = null,
): AppResponse {
Log.i("GET", url)
val client = baseClient
.newBuilder()
.followRedirects(allowRedirects)
@ -315,6 +317,7 @@ open class Requests {
cacheUnit: TimeUnit = DEFAULT_TIME_UNIT,
timeout: Long = 0L,
): AppResponse {
Log.i("POST", url)
val client = baseClient
.newBuilder()
.followRedirects(allowRedirects)
@ -339,6 +342,7 @@ open class Requests {
cacheUnit: TimeUnit = DEFAULT_TIME_UNIT,
timeout: Long = 0L
): AppResponse {
Log.i("PUT", url)
val client = baseClient
.newBuilder()
.followRedirects(allowRedirects)

View File

@ -22,6 +22,7 @@ import com.google.android.material.button.MaterialButton
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
import com.lagradost.cloudstream3.APIHolder.getApiFromName
import com.lagradost.cloudstream3.APIHolder.getApiSettings
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe
@ -93,6 +94,22 @@ class SearchFragment : Fragment() {
var selectedSearchTypes = mutableListOf<TvType>()
var selectedApis = mutableSetOf<String>()
fun search(query: String?) {
if (query == null) return
context?.getApiSettings()?.let { settings ->
searchViewModel.searchAndCancel(
query = query,
providersActive = selectedApis.filter { name ->
settings.contains(name) && getApiFromName(name).supportedTypes.any {
selectedSearchTypes.contains(
it
)
}
}.toSet()
)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -274,6 +291,7 @@ class SearchFragment : Fragment() {
button?.isSelected = buttonContains()
button?.setOnClickListener {
val last = selectedSearchTypes.toSet()
selectedSearchTypes.clear()
selectedSearchTypes.addAll(validTypes)
for ((otherButton, _) in pairList) {
@ -281,6 +299,8 @@ class SearchFragment : Fragment() {
}
it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
it?.isSelected = true
if (last != selectedSearchTypes.toSet()) // if you click the same button again the it does nothing
search(main_search?.query?.toString())
}
button?.setOnLongClickListener {
@ -292,6 +312,7 @@ class SearchFragment : Fragment() {
selectedSearchTypes.removeAll(validTypes)
}
it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes)
search(main_search?.query?.toString())
return@setOnLongClickListener true
}
}
@ -305,12 +326,7 @@ class SearchFragment : Fragment() {
main_search.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
searchViewModel.searchAndCancel(
query = query,
providersActive = selectedApis.filter { name ->
getApiFromName(name).supportedTypes.any { selectedSearchTypes.contains(it) }
}.toSet()
)
search(query)
main_search?.let {
hideKeyboard(it)

View File

@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
const val DOWNLOAD_HEADER_CACHE = "download_header_cache"
//const val WATCH_HEADER_CACHE = "watch_header_cache"
const val DOWNLOAD_EPISODE_CACHE = "download_episode_cache"
const val VIDEO_PLAYER_BRIGHTNESS = "video_player_alpha_key"

View File

@ -79,7 +79,7 @@ fun getAndUnpack(string: String): String {
/**
* Tries to load the appropriate extractor based on link, returns true if any extractor is loaded.
* */
suspend fun loadExtractor(url: String, referer: String?, callback: (ExtractorLink) -> Unit) : Boolean {
suspend fun loadExtractor(url: String, referer: String? = null, callback: (ExtractorLink) -> Unit) : Boolean {
for (extractor in extractorApis) {
if (url.startsWith(extractor.mainUrl)) {
extractor.getSafeUrl(url, referer)?.forEach(callback)