Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
98868a6f2c
|
@ -16,7 +16,7 @@ cloudstream {
|
|||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
status = 0 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"Anime",
|
||||
"AnimeMovie",
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// use an integer for version numbers
|
||||
version = 4
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "en"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Uses TMDB"
|
||||
authors = listOf("Blatzar")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"TvSeries",
|
||||
"Movie",
|
||||
"AnimeMovie"
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=ask4movie.mx&sz=%size%"
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.lagradost"/>
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
package com.lagradost
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class Ask4MoviePlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Ask4MovieProvider())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
package com.lagradost
|
||||
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Element
|
||||
import java.net.URI
|
||||
|
||||
class Ask4MovieProvider : MainAPI() {
|
||||
override var mainUrl = "https://ask4movie.mx"
|
||||
|
||||
override var name = "Ask4Movie"
|
||||
override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie, TvType.AnimeMovie)
|
||||
override val hasMainPage = true
|
||||
|
||||
private fun Element.toSearchResponse(): MovieSearchResponse {
|
||||
// style="background-image: url(https://ask4movie.me/wp-content/uploads/2022/08/Your-Name.-2016-cover.jpg)"
|
||||
val posterRegex = Regex("""url\((.*?)\)""")
|
||||
val poster = posterRegex.find(this.attr("style"))?.groupValues?.get(1)
|
||||
|
||||
val a = this.select("a")
|
||||
val href = a.attr("href")
|
||||
val title = a.text().trim()
|
||||
|
||||
// Title (2022) -> 2022
|
||||
val year =
|
||||
Regex("""\((\d{4})\)$""").find(title)?.groupValues?.getOrNull(1)?.toIntOrNull()
|
||||
|
||||
return MovieSearchResponse(
|
||||
title,
|
||||
href,
|
||||
this@Ask4MovieProvider.name,
|
||||
TvType.Movie,
|
||||
poster,
|
||||
year,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
// Used in movies/single seasons to get recommendations
|
||||
private fun Element.articleToSearchResponse(): MovieSearchResponse {
|
||||
val poster = this.select("img").attr("src")
|
||||
|
||||
val a = this.select("a")
|
||||
val href = a.attr("href")
|
||||
val title = a.attr("title")
|
||||
|
||||
// Title (2022) -> 2022
|
||||
val year =
|
||||
Regex("""\((\d{4})\)$""").find(title)?.groupValues?.getOrNull(1)?.toIntOrNull()
|
||||
|
||||
return MovieSearchResponse(
|
||||
title,
|
||||
href,
|
||||
this@Ask4MovieProvider.name,
|
||||
TvType.Movie,
|
||||
poster,
|
||||
year,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val url = "$mainUrl/?s=$query"
|
||||
val doc = app.post(
|
||||
url,
|
||||
// data = mapOf("np_asl_data" to "customset[]=post&customset[]=ct_channel&customset[]=post&customset[]=post&customset[]=post&customset[]=post&asl_gen[]=title&asl_gen[]=exact&qtranslate_lang=0&filters_initial=1&filters_changed=0")
|
||||
).document
|
||||
return doc.select("div.item").map {
|
||||
it.toSearchResponse()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIframe(html: String): String? {
|
||||
val data = Regex("""<script src="data:text\/javascript;base64,([^"']*)""").findAll(html)
|
||||
.lastOrNull()?.groupValues?.getOrNull(1) ?: return null
|
||||
val decoded = base64Decode(data)
|
||||
val iframeUrlRegex = Regex("""dir['"],['"]([^"']*)""")
|
||||
|
||||
val iframeEncoded = iframeUrlRegex.find(decoded)?.groupValues?.getOrNull(1) ?: return null
|
||||
val iframe = base64Decode(iframeEncoded)
|
||||
return Jsoup.parse(iframe).select("iframe").attr("src")
|
||||
}
|
||||
|
||||
private suspend fun getEpisodes(iframe: String): List<Episode> {
|
||||
val playlistDoc = app.get(iframe).document
|
||||
|
||||
// S04┋E01
|
||||
val episodeRegex = Regex("""S(\d+).E(\d+)""")
|
||||
return playlistDoc.select("span.episode").mapNotNull { episode ->
|
||||
val partialUrl = episode.attr("data-url")
|
||||
val fullUrl = "https://${URI(iframe).rawAuthority}$partialUrl"
|
||||
val info = episodeRegex.find(episode.text())
|
||||
val seasonIndex = info?.groupValues?.getOrNull(1)?.toIntOrNull()
|
||||
val episodeIndex = info?.groupValues?.getOrNull(2)?.toIntOrNull()
|
||||
Episode(
|
||||
fullUrl,
|
||||
episode = episodeIndex,
|
||||
season = seasonIndex
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse? {
|
||||
val document = app.get(mainUrl).document
|
||||
val rows = document.select("div.row")
|
||||
|
||||
val posterRegex = Regex("""url\((.*?)\)""")
|
||||
val mappedRows = rows.mapNotNull {
|
||||
var isHorizontal = true
|
||||
val items = it.select("div.slide-item").map { element ->
|
||||
val thumb = element.select("div.item-thumb")
|
||||
val poster = posterRegex.find(thumb.attr("style"))?.groupValues?.get(1)
|
||||
val href = thumb.select("a").attr("href")
|
||||
val title = element.select("div.video-short-intro a").text()
|
||||
val year =
|
||||
Regex("""\((\d{4})\)$""").find(title)?.groupValues?.getOrNull(1)?.toIntOrNull()
|
||||
MovieSearchResponse(title, href, this.name, TvType.Movie, poster, year)
|
||||
}.ifEmpty {
|
||||
isHorizontal = false
|
||||
it.select("div.channel-content.clearfix").map { searchElement ->
|
||||
searchElement.articleToSearchResponse()
|
||||
}
|
||||
}
|
||||
|
||||
val title = it.select("div.title").text()
|
||||
if (title.contains("porn", true) && !settingsForProvider.enableAdult) return@mapNotNull null
|
||||
HomePageList(title, items, isHorizontal)
|
||||
}
|
||||
return HomePageResponse(mappedRows)
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse {
|
||||
val response = app.get(url)
|
||||
val document = response.document
|
||||
|
||||
val seasons = document.select("div.item")
|
||||
val isSingleVideo = (seasons.isNullOrEmpty())
|
||||
val yearRegex = Regex("""\((\d{4})\)$""")
|
||||
|
||||
if (isSingleVideo) {
|
||||
val description = document.select("div.custom.video-the-content").text().trim()
|
||||
val (title, year) = document.select("a.video-title").text().let {
|
||||
it.replace(yearRegex, "") to yearRegex.find(it)?.groupValues?.get(1)?.toIntOrNull()
|
||||
}
|
||||
val genres =
|
||||
document.selectFirst("div.categories.cactus-info")?.select("a")?.map { it.text() }
|
||||
|
||||
// This is actually a json with all the data, but I opted to just scrape the html
|
||||
// Try the json in the future if html turns out bad
|
||||
val posterRegex = Regex("""contentUrl['"].*?(http[^"']*)""")
|
||||
val poster = posterRegex.find(response.text)?.groupValues?.get(1)
|
||||
val recommendations = document.select("div.cactus-sub-wrap article").mapNotNull {
|
||||
it.articleToSearchResponse()
|
||||
}
|
||||
|
||||
val iframe = getIframe(response.text)
|
||||
// It can be a season as a single video iframe!
|
||||
return if (iframe?.contains("/p/") == true) {
|
||||
val episodes = getEpisodes(iframe)
|
||||
TvSeriesLoadResponse(
|
||||
title,
|
||||
url,
|
||||
this.name,
|
||||
TvType.TvSeries,
|
||||
episodes,
|
||||
poster,
|
||||
year,
|
||||
recommendations = recommendations,
|
||||
tags = genres,
|
||||
plot = description
|
||||
)
|
||||
} else {
|
||||
newMovieLoadResponse(title, url, TvType.Movie, iframe) {
|
||||
this.posterUrl = poster
|
||||
this.tags = genres
|
||||
this.plot = description
|
||||
this.year = year
|
||||
this.recommendations = recommendations
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val recommendations = document.select("div.channel.clearfix").mapNotNull {
|
||||
it.articleToSearchResponse()
|
||||
}
|
||||
|
||||
val descriptionDiv = document.select("div.channel-description")
|
||||
val description = descriptionDiv.select("p").firstOrNull()?.text()?.trim()
|
||||
|
||||
val genres = descriptionDiv.select("span.genres").let {
|
||||
if (it.isNotEmpty()) it.text().split(",")
|
||||
else descriptionDiv.select("p")
|
||||
.firstOrNull { element -> element.text().contains("Genre:") }
|
||||
?.text()?.substringAfter("Genre:")?.split(",")
|
||||
}
|
||||
|
||||
val (title, year) = document.select("h3.channel-name").text().let {
|
||||
it.replace(yearRegex, "") to yearRegex.find(it)?.groupValues?.get(1)?.toIntOrNull()
|
||||
}
|
||||
|
||||
val poster = document.select("div.channel-pic > img").attr("src")
|
||||
val mappedSeasons = seasons.apmap {
|
||||
val href = it.select("div.top-item > a").attr("href")
|
||||
val text = app.get(href).text
|
||||
val iframe = getIframe(text) ?: return@apmap emptyList()
|
||||
getEpisodes(iframe)
|
||||
}.flatten()
|
||||
|
||||
return TvSeriesLoadResponse(
|
||||
title,
|
||||
url,
|
||||
this.name,
|
||||
TvType.TvSeries,
|
||||
mappedSeasons,
|
||||
poster,
|
||||
year,
|
||||
recommendations = recommendations,
|
||||
tags = genres,
|
||||
plot = description
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
loadExtractor(data, this.mainUrl, subtitleCallback, callback)
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ dependencies {
|
|||
}
|
||||
|
||||
// use an integer for version numbers
|
||||
version = 9
|
||||
version = 10
|
||||
|
||||
|
||||
cloudstream {
|
||||
|
@ -29,4 +29,4 @@ cloudstream {
|
|||
"Movie",
|
||||
)
|
||||
requiresResources = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
override val requiresUsername = true
|
||||
override val requiresPassword = true
|
||||
override val requiresServer = true
|
||||
override val createAccountUrl = "https://recloudstream.github.io/docs/users/use-nginx.md"
|
||||
override val createAccountUrl = "https://www.cloudstream.cf/docs/users/use-nginx.md"
|
||||
|
||||
companion object {
|
||||
const val NGINX_USER_KEY: String = "nginx_user"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// use an integer for version numbers
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
|
||||
cloudstream {
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.lagradost
|
|||
|
||||
import com.lagradost.cloudstream3.SubtitleFile
|
||||
import com.lagradost.cloudstream3.TvType
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.metaproviders.TmdbLink
|
||||
import com.lagradost.cloudstream3.metaproviders.TmdbProvider
|
||||
import com.lagradost.cloudstream3.network.WebViewResolver
|
||||
|
@ -10,9 +9,6 @@ import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
|||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import com.lagradost.nicehttp.requestCreator
|
||||
import org.mozilla.javascript.Context
|
||||
import org.mozilla.javascript.Scriptable
|
||||
import org.mozilla.javascript.ScriptableObject
|
||||
|
||||
class OlgplyProvider : TmdbProvider() {
|
||||
override var mainUrl = "https://olgply.com"
|
||||
|
@ -31,7 +27,7 @@ class OlgplyProvider : TmdbProvider() {
|
|||
Regex("""\.m3u8|i7njdjvszykaieynzsogaysdgb0hm8u1mzubmush4maopa4wde\.com""")
|
||||
).resolveUsingWebView(
|
||||
requestCreator(
|
||||
"GET", url, referer = "https://olgply.xyz/"
|
||||
"GET", url, referer = "https://olgply.com/"
|
||||
)
|
||||
)
|
||||
.first ?: return
|
||||
|
@ -39,7 +35,7 @@ class OlgplyProvider : TmdbProvider() {
|
|||
callback.invoke(
|
||||
ExtractorLink(
|
||||
this.name,
|
||||
"Movies4Discord",
|
||||
this.name,
|
||||
foundVideo.url.toString(),
|
||||
"",
|
||||
Qualities.Unknown.value,
|
||||
|
@ -57,7 +53,7 @@ class OlgplyProvider : TmdbProvider() {
|
|||
): Boolean {
|
||||
val mappedData = parseJson<TmdbLink>(data)
|
||||
val tmdbId = mappedData.tmdbID ?: return false
|
||||
val jsRegex = Regex("""eval\(.*\);""")
|
||||
// val jsRegex = Regex("""eval\(.*\);""")
|
||||
|
||||
val apiUrl =
|
||||
"https://olgply.xyz/${tmdbId}${mappedData.season?.let { "/$it" } ?: ""}${mappedData.episode?.let { "/$it" } ?: ""}"
|
||||
|
|
|
@ -6,7 +6,7 @@ cloudstream {
|
|||
language = "en"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
description = "Also includes Dopebox, Solarmovie, Zoro, HDToday and 2embed"
|
||||
description = "Due to the video host changing encryption methods extremely often these extensions might not work perfectly. Also includes Dopebox, Solarmovie, Zoro, HDToday and 2embed"
|
||||
// authors = listOf("Cloudburst")
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue