Merge remote-tracking branch 'origin/NoodleMagazine' into NoodleMagazine

This commit is contained in:
KillerDogeEmpire 2024-02-12 23:26:12 -08:00
commit 8f8033178d
44 changed files with 1553 additions and 336 deletions

View File

@ -1,9 +0,0 @@
package com.jacekun
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.TvType
class Example : MainAPI() {
private val DEV = "DevDebug"
private val globaltvType = TvType.Movie
}

View File

@ -0,0 +1,18 @@
version = 4
cloudstream {
authors = listOf("KillerDogeEmpire, Coxju")
language = "en"
description = "FullPorner is the best free full length porn video site. Choose from millions of hardcore videos that stream quickly and in high quality and only full length"
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
**/
status = 1 // will be 3 if unspecified
tvTypes = listOf("NSFW")
iconUrl = "https://www.google.com/s2/favicons?domain=fullporner.com&sz=%size%"
}

View File

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/> <manifest package="com.coxjud"/>

View File

@ -0,0 +1,162 @@
package com.KillerDogeEmpire
import org.jsoup.nodes.Element
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.network.WebViewResolver
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import org.jsoup.Jsoup
class FullPorner : MainAPI() {
override var mainUrl = "https://fullporner.com"
override var name = "FullPorner"
override val hasMainPage = true
override var lang = "en"
override val hasQuickSearch = false
override val hasDownloadSupport = true
override val hasChromecastSupport = true
override val supportedTypes = setOf(TvType.NSFW)
override val vpnStatus = VPNStatus.MightBeNeeded
override val mainPage = mainPageOf(
"${mainUrl}/home/" to "Featured",
"${mainUrl}/category/amateur/" to "Amateur",
"${mainUrl}/category/teen/" to "Teen",
"${mainUrl}/category/cumshot/" to "CumShot",
"${mainUrl}/category/deepthroat/" to "DeepThroat",
"${mainUrl}/category/orgasm/" to "Orgasm",
"${mainUrl}/category/threesome/" to "ThreeSome",
"${mainUrl}/category/group-sex/" to "Group Sex",
)
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val document = app.get("${request.data}${page}").document
val home = document.select("div.video-block div.video-card").mapNotNull { it.toSearchResult() }
return newHomePageResponse(
list = HomePageList(
name = request.name,
list = home,
isHorizontalImages = true
),
hasNext = true
)
}
private fun Element.toSearchResult(): SearchResponse? {
val title = this.selectFirst("div.video-card div.video-card-body div.video-title a")?.text() ?: return null
val href = fixUrl(this.selectFirst("div.video-card div.video-card-body div.video-title a")!!.attr("href"))
val posterUrl = fixUrlNull(this.select("div.video-card div.video-card-image a img").attr("data-src"))
return newMovieSearchResponse(title, href, TvType.Movie) { this.posterUrl = posterUrl }
}
override suspend fun search(query: String): List<SearchResponse> {
val searchResponse = mutableListOf<SearchResponse>()
for (i in 1..15) {
val document = app.get("${mainUrl}/search?q=${query.replace(" ", "+")}&p=$i").document
val results = document.select("div.video-block div.video-card").mapNotNull { it.toSearchResult() }
searchResponse.addAll(results)
if (results.isEmpty()) break
}
return searchResponse
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst("div.video-block div.single-video-left div.single-video-title h2")?.text()?.trim().toString()
val iframeUrl = fixUrlNull(document.selectFirst("div.video-block div.single-video-left div.single-video iframe")?.attr("src")) ?: ""
val poster: String?
val posterHeaders: Map<String, String>
if (iframeUrl.contains("videoh")) {
val iframeDocument = app.get(iframeUrl, interceptor = WebViewResolver(Regex("""mydaddy"""))).document
val videoHtml = iframeDocument.selectXpath("//script[contains(text(),'poster')]").first()?.html()?.substringAfter("else{ \$(\"#jw\").html(\"")?.substringBefore("\");}if(hasAdblock)")?.replace("\\", "")
val video = Jsoup.parse(videoHtml.toString()).selectFirst("video")
poster = fixUrlNull(video?.attr("poster"))
posterHeaders = mapOf(Pair("referer", "https://mydaddy.cc/"))
} else {
val iframeDocument = app.get(iframeUrl).document
val videoDocument = Jsoup.parse("<video" + iframeDocument.selectXpath("//script[contains(text(),'\$(\"#jw\").html(')]")[0]?.toString()?.replace("\\", "")?.substringAfter("<video")?.substringBefore("</video>") + "</video>")
poster = fixUrlNull(videoDocument.selectFirst("video")?.attr("poster").toString())
posterHeaders = mapOf(Pair("referer", "https://xiaoshenke.net/"))
}
val tags = document.select("div.video-blockdiv.single-video-left div.single-video-title p.tag-link span a").map { it.text() }
val description = document.selectFirst("div.video-block div.single-video-left div.single-video-title h2")?.text()?.trim().toString()
val actors = document.select("div.video-block div.single-video-left div.single-video-info-content p a").map { it.text() }
val recommendations = document.select("div.video-block div.video-recommendation div.video-card").mapNotNull { it.toSearchResult() }
return newMovieLoadResponse(title, url, TvType.NSFW, url) {
this.posterUrl = poster
this.posterHeaders = posterHeaders
this.plot = description
this.tags = tags
this.recommendations = recommendations
addActors(actors)
}
}
override suspend fun loadLinks(data: String, isCasting: Boolean, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit): Boolean {
val document = app.get(data).document
val iframeUrl = fixUrlNull(document.selectFirst("div.video-block div.single-video-left div.single-video iframe")?.attr("src")) ?: ""
val extlinkList = mutableListOf<ExtractorLink>()
if (iframeUrl.contains("videoh")) {
val iframeDocument = app.get(iframeUrl, interceptor = WebViewResolver(Regex("""mydaddy"""))).document
val videoDocument = Jsoup.parse("<video" + iframeDocument.selectXpath("//script[contains(text(),'\$(\"#jw\").html(')]").first()?.toString()?.replace("\\", "")?.substringAfter("<video")?.substringAfter("<video")?.substringBefore("</video>") + "</video>")
videoDocument.select("source").map { res ->
extlinkList.add(ExtractorLink(
name,
name,
fixUrl(res.attr("src")),
referer = data,
quality = Regex("(\\d+.)").find(res.attr("title"))?.groupValues?.get(1).let { getQualityFromName(it) }
))
}
} else if (iframeUrl.contains("xiaoshenke")) {
val iframeDocument = app.get(iframeUrl).document
val videoID = Regex("""var id = \"(.+?)\"""").find(iframeDocument.html())?.groupValues?.get(1)
val pornTrexDocument = app.get("https://www.porntrex.com/embed/${videoID}").document
val video_url = fixUrlNull(Regex("""video_url: \'(.+?)\',""").find(pornTrexDocument.html())?.groupValues?.get(1))
if (video_url != null) {
extlinkList.add(ExtractorLink(
name,
name,
video_url,
referer = data,
quality = Qualities.Unknown.value
))
}
} else {
val iframeDocument = app.get(iframeUrl).document
val videoDocument = Jsoup.parse("<video" + iframeDocument.selectXpath("//script[contains(text(),'\$(\"#jw\").html(')]").first()?.toString()?.replace("\\", "")?.substringAfter("<video")?.substringBefore("</video>") + "</video>")
videoDocument.select("source").map { res ->
extlinkList.add(ExtractorLink(
this.name,
this.name,
fixUrl(res.attr("src")),
referer = mainUrl,
quality = Regex("(\\d+.)").find(res.attr("title"))?.groupValues?.get(1).let { getQualityFromName(it) }
))
}
}
extlinkList.forEach(callback)
return true
}
}

View File

@ -0,0 +1,12 @@
package com.KillerDogeEmpire
import android.content.Context
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
@CloudstreamPlugin
class FullPornerProvider : Plugin() {
override fun load(context: Context) {
registerMainAPI(FullPorner())
}
}

28
GoodPorn/build.gradle.kts Normal file
View File

@ -0,0 +1,28 @@
// use an integer for version numbers
version = 5
cloudstream {
// All of these properties are optional, you can safely remove them
description = "GoodPorn"
authors = listOf(" KillerDogeEmpire, Stormunblessed, Jace, Hexated, Coxju")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here:
// https://recloudstream.github.io/cloudstream/html/app/com.lagradost.cloudstream3/-tv-type/index.html
tvTypes = listOf("NSFW")
iconUrl = "https://www.google.com/s2/favicons?domain=goodporn.to&sz=%size%"
language = "en"
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.KillerDogeEmpire"/>

View File

@ -0,0 +1,127 @@
package com.KillerDogeEmpire
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import org.jsoup.nodes.Element
import java.util.*
class GoodPorn : MainAPI() {
override var mainUrl = "https://goodporn.to"
override var name = "GoodPorn"
override val hasMainPage = true
override val hasDownloadSupport = true
override val vpnStatus = VPNStatus.MightBeNeeded
override val supportedTypes = setOf(TvType.NSFW)
override val mainPage = mainPageOf(
"$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=post_date&from=" to "New Videos",
"$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=video_viewed&from=" to "Most Viewed Videos",
"$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=rating&from=" to "Top Rated Videos ",
"$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=most_commented&from=" to "Most Commented Videos",
"$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=duration&from=" to "Longest Videos",
"$mainUrl/sites/fitness-rooms/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Fitness Rooms",
"$mainUrl/sites/public-agent/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Public Agent",
"$mainUrl/sites/massage-rooms/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Massage Rooms",
"$mainUrl/sites/dane-jones/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Dane Jones",
"$mainUrl/channels/brazzers/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Brazzers",
"$mainUrl/channels/digitalplayground/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Digital Playground",
"$mainUrl/channels/realitykings/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Realitykings",
"$mainUrl/channels/babes-network/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Babes Network",
)
override suspend fun getMainPage(
page: Int, request: MainPageRequest
): HomePageResponse {
val document = app.get(request.data + page).document
val home =
document.select("div#list_videos_most_recent_videos_items div.item, div#list_videos_common_videos_list_items div.item")
.mapNotNull {
it.toSearchResult()
}
return newHomePageResponse(
list = HomePageList(
name = request.name, list = home, isHorizontalImages = true
), hasNext = true
)
}
private fun Element.toSearchResult(): SearchResponse? {
val title = this.selectFirst("strong.title")?.text() ?: return null
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
val posterUrl = fixUrlNull(this.select("div.img > img").attr("data-original"))
return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
}
}
override suspend fun search(query: String): List<SearchResponse> {
val searchResponse = mutableListOf<SearchResponse>()
for (i in 1..15) {
val document = app.get(
"$mainUrl/search/nikki-benz/?mode=async&function=get_block&block_id=list_videos_videos_list_search_result&q=$query&category_ids=&sort_by=&from_videos=$i&from_albums=$i",
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
).document
val results =
document.select("div#list_videos_videos_list_search_result_items div.item")
.mapNotNull {
it.toSearchResult()
}
searchResponse.addAll(results)
if (results.isEmpty()) break
}
return searchResponse
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst("div.headline > h1")?.text()?.trim().toString()
val poster =
fixUrlNull(document.selectFirst("meta[property=og:image]")?.attr("content").toString())
val tags = document.select("div.info div:nth-child(5) > a").map { it.text() }
val description = document.select("div.info div:nth-child(2)").text().trim()
val actors = document.select("div.info div:nth-child(6) > a").map { it.text() }
val recommendations =
document.select("div#list_videos_related_videos_items div.item").mapNotNull {
it.toSearchResult()
}
return newMovieLoadResponse(title, url, TvType.NSFW, url) {
this.posterUrl = poster
this.plot = description
this.tags = tags
addActors(actors)
this.recommendations = recommendations
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
val extlinkList = mutableListOf<ExtractorLink>()
document.select("div.info div:last-child a").map { res ->
extlinkList.add(
ExtractorLink(
this.name,
this.name,
res.attr("href")
.replace(Regex("\\?download\\S+.mp4&"), "?") + "&rnd=${Date().time}",
referer = data,
quality = Regex("(\\d+.),").find(res.text())?.groupValues?.get(1)
.let { getQualityFromName(it) },
headers = mapOf("Range" to "bytes=0-"),
)
)
}
extlinkList.forEach(callback)
return true
}
}

View File

@ -0,0 +1,14 @@
package com.KillerDogeEmpire
import android.content.Context
import com.KillerDogeEmpire.GoodPorn
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
@CloudstreamPlugin
class GoodPornProvider : Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(GoodPorn())
}
}

View File

@ -1,12 +1,12 @@
// use an integer for version numbers // use an integer for version numbers
version = 5 version = 6
cloudstream { cloudstream {
// All of these properties are optional, you can safely remove them // All of these properties are optional, you can safely remove them
description = "" description = ""
authors = listOf("Jace") authors = listOf("KillerDogeEmpire, Jace")
/** /**
* Status int as the following: * Status int as the following:

View File

@ -1,101 +1,99 @@
package com.jacekun package com.jacekun
import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.getQualityFromName import okhttp3.FormBody
import org.jsoup.select.Elements import org.jsoup.nodes.Element
class HentaiHaven : MainAPI() { class HentaiHaven : MainAPI() {
private val globalTvType = TvType.NSFW
override var name = "Hentai Haven"
override var mainUrl = "https://hentaihaven.xxx" override var mainUrl = "https://hentaihaven.xxx"
override val supportedTypes = setOf(TvType.NSFW) override var name = "Hentai Haven"
override val hasDownloadSupport = false override val hasMainPage = true
override val hasMainPage= true override var lang = "en"
override val hasQuickSearch = false override val hasDownloadSupport = true
override val supportedTypes = setOf(
TvType.NSFW)
override val mainPage = mainPageOf(
"?m_orderby=new-manga" to "New",
"?m_orderby=views" to "Most Views",
"?m_orderby=rating" to "Rating",
"?m_orderby=alphabet" to "A-Z",
)
override suspend fun getMainPage( override suspend fun getMainPage(
page: Int, page: Int,
request: MainPageRequest request: MainPageRequest
): HomePageResponse { ): HomePageResponse {
val doc = app.get(mainUrl).document val document = app.get("$mainUrl/page/$page/${request.data}").document
val all = ArrayList<HomePageList>() val home =
document.select("div.page-listing-item div.col-6.col-md-zarat.badge-pos-1").mapNotNull {
doc.getElementsByTag("body").select("div.c-tabs-item") it.toSearchResult()
.select("div.vraven_home_slider").forEach { it2 ->
// Fetch row title
val title = it2?.select("div.home_slider_header")?.text() ?: "Unnamed Row"
// Fetch list of items and map
it2.select("div.page-content-listing div.item.vraven_item.badge-pos-1").let { inner ->
all.add(
HomePageList(
name = title,
list = inner.getResults(this.name),
isHorizontalImages = false
)
)
}
} }
return HomePageResponse(all) return newHomePageResponse(request.name, home)
}
private fun Element.toSearchResult(): AnimeSearchResponse? {
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
val title =
this.selectFirst("h3 a, h5 a")?.text()?.trim() ?: this.selectFirst("a")?.attr("title")
?: return null
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
val episode = this.selectFirst("span.chapter.font-meta a")?.text()?.filter { it.isDigit() }
?.toIntOrNull()
return newAnimeSearchResponse(title, href, TvType.Anime) {
this.posterUrl = posterUrl
addSub(episode)
}
} }
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val searchUrl = "${mainUrl}/?s=${query}&post_type=wp-manga" val link = "$mainUrl/?s=$query&post_type=wp-manga"
return app.get(searchUrl).document val document = app.get(link).document
.select("div.c-tabs-item div.row.c-tabs-item__content")
.getResults(this.name) return document.select("div.c-tabs-item > div.c-tabs-item__content").mapNotNull {
it.toSearchResult()
}
} }
override suspend fun load(url: String): LoadResponse { override suspend fun load(url: String): LoadResponse? {
//TODO: Load polishing val document = app.get(url).document
val doc = app.get(url).document
//Log.i(this.name, "Result => (url) ${url}")
val poster = doc.select("meta[property=og:image]")
.firstOrNull()?.attr("content")
val title = doc.select("meta[name=title]")
.firstOrNull()?.attr("content")
?.toString() ?: ""
val descript = doc.select("div.description-summary").text()
val body = doc.getElementsByTag("body") val title = document.selectFirst("div.post-title h1")?.text()?.trim() ?: return null
val episodes = body.select("div.page-content-listing.single-page") val poster = document.select("div.summary_image img").attr("src")
.first()?.select("li") val tags = document.select("div.genres-content > a").map { it.text() }
val year = episodes?.last() val description = document.select("div.description-summary p").text().trim()
?.selectFirst("span.chapter-release-date") val trailer = document.selectFirst("a.trailerbutton")?.attr("href")
?.text()?.trim()?.takeLast(4)?.toIntOrNull()
val episodeList = episodes?.mapNotNull { val episodes = document.select("div.listing-chapters_wrap ul li").mapNotNull {
val innerA = it?.selectFirst("a") ?: return@mapNotNull null val name = it.selectFirst("a")?.text() ?: return@mapNotNull null
val eplink = innerA.attr("href") ?: return@mapNotNull null val image = fixUrlNull(it.selectFirst("a img")?.attr("src"))
val epCount = innerA.text().trim().filter { a -> a.isDigit() }.toIntOrNull() val link = fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null
val imageEl = innerA.selectFirst("img") Episode(link, name, posterUrl = image)
val epPoster = imageEl?.attr("src") ?: imageEl?.attr("data-src") }.reversed()
Episode(
name = innerA.text(), val recommendations =
data = eplink, document.select("div.row div.col-6.col-md-zarat").mapNotNull {
posterUrl = epPoster, it.toSearchResult()
episode = epCount, }
)
} ?: listOf() return newAnimeLoadResponse(title, url, TvType.NSFW) {
engName = title
posterUrl = poster
addEpisodes(DubStatus.Subbed, episodes)
plot = description
this.tags = tags
this.recommendations = recommendations
addTrailer(trailer)
}
//Log.i(this.name, "Result => (id) ${id}")
return AnimeLoadResponse(
name = title,
url = url,
apiName = this.name,
type = globalTvType,
posterUrl = poster,
year = year,
plot = descript,
episodes = mutableMapOf(
Pair(DubStatus.Subbed, episodeList.reversed())
)
)
} }
override suspend fun loadLinks( override suspend fun loadLinks(
@ -105,120 +103,66 @@ class HentaiHaven : MainAPI() {
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
try { val doc = app.get(data).document
Log.i(name, "Loading iframe") val meta = doc.selectFirst("meta[itemprop=thumbnailUrl]")?.attr("content")?.substringAfter("/hh/")?.substringBefore("/") ?: return false
val requestLink = "${mainUrl}/wp-content/plugins/player-logic/api.php" doc.select("div.player_logic_item iframe").attr("src").let { iframe ->
val action = "zarat_get_data_player_ajax" val document = app.get(iframe, referer = data).text
val reA = Regex("(?<=var en =)(.*?)(?=';)", setOf(RegexOption.DOT_MATCHES_ALL)) val en = Regex("var\\sen\\s=\\s'(\\S+)';").find(document)?.groupValues?.getOrNull(1)
val reB = Regex("(?<=var iv =)(.*?)(?=';)", setOf(RegexOption.DOT_MATCHES_ALL)) val iv = Regex("var\\siv\\s=\\s'(\\S+)';").find(document)?.groupValues?.getOrNull(1)
app.get(data).document.selectFirst("div.player_logic_item iframe") val body = FormBody.Builder()
?.attr("src")?.let { epLink -> .addEncoded("action", "zarat_get_data_player_ajax")
.addEncoded("a", "$en")
.addEncoded("b", "$iv")
.build()
Log.i(name, "Loading ep link => $epLink") app.post(
val scrAppGet = app.get(epLink, referer = data) "$mainUrl/wp-content/plugins/player-logic/api.php",
val scrDoc = scrAppGet.document.getElementsByTag("script").toString() // data = mapOf(
//Log.i(name, "Loading scrDoc => (${scrAppGet.code}) $scrDoc") // "action" to "zarat_get_data_player_ajax",
if (scrDoc.isNotBlank()) { // "a" to "$en",
//en // "b" to "$iv"
val a = reA.find(scrDoc)?.groupValues?.getOrNull(1) // ),
?.trim()?.removePrefix("'") ?: "" requestBody = body,
//iv // headers = mapOf("Sec-Fetch-Mode" to "cors")
val b = reB.find(scrDoc)?.groupValues?.getOrNull(1) ).parsedSafe<Response>()?.data?.sources?.map { res ->
?.trim()?.removePrefix("'") ?: "" // M3u8Helper.generateM3u8(
// this.name,
Log.i(name, "a => $a") // res.src ?: return@map null,
Log.i(name, "b => $b") // referer = "$mainUrl/",
// headers = mapOf(
val doc = app.post( // "Origin" to mainUrl,
url = requestLink, // )
headers = mapOf( // ).forEach(callback)
// Pair("mode", "cors"), callback.invoke(
// Pair("Content-Type", "multipart/form-data"), ExtractorLink(
// Pair("Origin", mainUrl), this.name,
// Pair("Host", mainUrl.split("//").last()), this.name,
Pair("User-Agent", USER_AGENT), res.src?.replace("/hh//", "/hh/$meta/") ?: return@map null,
Pair("Sec-Fetch-Mode", "cors") referer = "",
), quality = Qualities.Unknown.value,
data = mapOf( isM3u8 = true
Pair("action", action), )
Pair("a", a), )
Pair("b", b) }
)
)
Log.i(name, "Response (${doc.code}) => ${doc.text}")
//AppUtils.tryParseJson<ResponseJson?>(doc.text)
doc.parsedSafe<ResponseJson>()?.data?.sources?.map { m3src ->
val m3srcFile = m3src.src ?: return@map null
val label = m3src.label ?: ""
Log.i(name, "M3u8 link: $m3srcFile")
callback.invoke(
ExtractorLink(
name = "$name m3u8",
source = "$name m3u8",
url = m3srcFile,
referer = "$mainUrl/",
quality = getQualityFromName(label),
isM3u8 = true
)
)
}
}
}
} catch (e: Exception) {
Log.i(name, "Error => $e")
logError(e)
return false
} }
return true return true
} }
private fun Elements?.getResults(apiName: String): List<AnimeSearchResponse> { data class Response(
return this?.mapNotNull { @JsonProperty("data") val data: Data? = null,
val innerDiv = it.select("div").firstOrNull()
val firstA = innerDiv?.selectFirst("a")
val link = fixUrlNull(firstA?.attr("href")) ?: return@mapNotNull null
val name = firstA?.attr("title") ?: "<No Title>"
val year = innerDiv?.selectFirst("span.c-new-tag")?.selectFirst("a")
?.attr("title")?.takeLast(4)?.toIntOrNull()
val imageDiv = firstA?.selectFirst("img")
var image = imageDiv?.attr("src")
if (image.isNullOrBlank()) {
image = imageDiv?.attr("data-src")
}
val latestEp = innerDiv?.selectFirst("div.list-chapter")
?.selectFirst("div.chapter-item")
?.selectFirst("a")
?.text()
?.filter { a -> a.isDigit() }
?.toIntOrNull() ?: 0
val dubStatus = mutableMapOf(
Pair(DubStatus.Subbed, latestEp)
)
AnimeSearchResponse(
name = name,
url = link,
apiName = apiName,
type = globalTvType,
posterUrl = image,
year = year,
episodes = dubStatus
)
} ?: listOf()
}
private data class ResponseJson(
@JsonProperty("data") val data: ResponseData?
) )
private data class ResponseData(
@JsonProperty("sources") val sources: List<ResponseSources>? = listOf() data class Data(
@JsonProperty("sources") val sources: ArrayList<Sources>? = arrayListOf(),
) )
private data class ResponseSources(
@JsonProperty("src") val src: String?, data class Sources(
@JsonProperty("type") val type: String?, @JsonProperty("src") val src: String? = null,
@JsonProperty("label") val label: String? @JsonProperty("type") val type: String? = null,
@JsonProperty("label") val label: String? = null,
) )
} }

View File

@ -15,7 +15,7 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 3 // will be 3 if unspecified status = 0 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category. // List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here: // You can find a list of avaliable types here:

View File

@ -15,7 +15,7 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 1 // will be 3 if unspecified status = 0 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category. // List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here: // You can find a list of avaliable types here:

View File

@ -15,7 +15,7 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 3 // will be 3 if unspecified status = 0 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category. // List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here: // You can find a list of avaliable types here:

View File

@ -15,7 +15,7 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 1 // will be 3 if unspecified status = 0 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category. // List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here: // You can find a list of avaliable types here:

View File

@ -15,7 +15,7 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 3 // will be 3 if unspecified status = 0 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category. // List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here: // You can find a list of avaliable types here:

View File

@ -15,7 +15,7 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 1 // will be 3 if unspecified status = 0 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category. // List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here: // You can find a list of avaliable types here:

View File

@ -15,7 +15,7 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 1 // will be 3 if unspecified status = 0 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category. // List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here: // You can find a list of avaliable types here:

View File

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest package="com.lagradost"/> <manifest package="com.lagradost"/>

View File

@ -38,18 +38,22 @@ class NoodleMagazineProvider : MainAPI() { // all providers must be an instance
return newHomePageResponse(request.name, home) return newHomePageResponse(request.name, home)
} }
private fun Element.toSearchResult(): MovieSearchResponse? { private fun Element.toSearchResult(): MovieSearchResponse? {
val href = fixUrl(this.selectFirst("a")?.attr("href") ?: return null) val href = fixUrl(this.selectFirst("a")?.attr("href") ?: return null)
val title = this.selectFirst("a div.i_info div.title")?.text() ?: return null val title = this.selectFirst("a div.i_info div.title")?.text() ?: return null
val posterUrl = fixUrlNull(this.selectFirst("a div.i_img img")?.attr("data-src")) val posterUrl = fixUrlNull(this.selectFirst("a div.i_img img")?.attr("data-src"))
return newMovieSearchResponse(title, href, TvType.Movie) { return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl this.posterUrl = posterUrl
} }
} }
override suspend fun search(query: String): List<MovieSearchResponse> { override suspend fun search(query: String): List<MovieSearchResponse> {
val searchresult = mutableListOf<MovieSearchResponse>() val searchresult = mutableListOf<MovieSearchResponse>()
(0..10).toList().apmap { page -> (0..10).toList().apmap { page ->
val doc = app.get("$mainUrl/video/$query?p=$page").document val doc = app.get("$mainUrl/video/$query?p=$page").document
//return document.select("div.post-filter-image").mapNotNull { //return document.select("div.post-filter-image").mapNotNull {

View File

@ -15,7 +15,7 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 2 // will be 3 if unspecified status = 0 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category. // List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here: // You can find a list of avaliable types here:

28
Pornhits/build.gradle.kts Normal file
View File

@ -0,0 +1,28 @@
// use an integer for version numbers
version = 5
cloudstream {
// All of these properties are optional, you can safely remove them
description = "Pornhits"
authors = listOf("KillerDogeEmpire, Coxju")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here:
// https://recloudstream.github.io/cloudstream/html/app/com.lagradost.cloudstream3/-tv-type/index.html
tvTypes = listOf("NSFW")
iconUrl = "https://www.google.com/s2/favicons?domain=pornhits.com&sz=%size%"
language = "en"
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.KillerDogeEmpire"/>

View File

@ -0,0 +1,216 @@
package com.KillerDogeEmpire
import com.lagradost.cloudstream3.HomePageList
import com.lagradost.cloudstream3.HomePageResponse
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.MainPageRequest
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.VPNStatus
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.fixUrl
import com.lagradost.cloudstream3.fixUrlNull
import com.lagradost.cloudstream3.mainPageOf
import com.lagradost.cloudstream3.newHomePageResponse
import com.lagradost.cloudstream3.newMovieLoadResponse
import com.lagradost.cloudstream3.newMovieSearchResponse
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import org.json.JSONObject
import org.jsoup.nodes.Element
class Pornhits : MainAPI() {
override var mainUrl = "https://www.pornhits.com"
override var name = "Pornhits"
override val hasMainPage = true
override val hasDownloadSupport = true
override val vpnStatus = VPNStatus.MightBeNeeded
override val supportedTypes = setOf(TvType.NSFW)
override val mainPage = mainPageOf(
"$mainUrl/videos.php?p=%d&s=l" to "Latest",
"$mainUrl/videos.php?p=%d&s=pd" to "Popular last day",
"$mainUrl/videos.php?p=%d&s=bd" to "Top Rated (day)",
"$mainUrl/videos.php?p=%d&s=pw" to "Popular last week",
"$mainUrl/videos.php?p=%d&s=bw" to "Top Rated (week)",
"$mainUrl/videos.php?p=%d&s=pm" to "Popular last month",
"$mainUrl/videos.php?p=%d&s=bm" to "Top Rated (month)",
)
override suspend fun getMainPage(
page: Int, request: MainPageRequest
): HomePageResponse {
val document = app.get(request.data.format(page)).document
val home =
document.select("div.main-content section.main-container div.list-videos article.item")
.mapNotNull {
it.toSearchResult()
}
return newHomePageResponse(
list = HomePageList(
name = request.name, list = home, isHorizontalImages = true
), hasNext = true
)
}
private fun Element.toSearchResult(): SearchResponse? {
val title = this.selectFirst("div.item-info h2.title")?.text() ?: return null
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
val posterUrl = fixUrlNull(this.select("a div.img img").attr("data-original"))
return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
}
}
override suspend fun search(query: String): List<SearchResponse> {
val searchResponse = mutableListOf<SearchResponse>()
for (i in 1..15) {
val document = app.get(
"$mainUrl/videos.php?p=${i}&q=${query.trim().replace(" ", "+")}"
).document
val results =
document.select("div.main-content section.main-container div.list-videos article.item")
.mapNotNull {
it.toSearchResult()
}
searchResponse.addAll(results)
if (results.isEmpty()) break
}
return searchResponse
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title =
document.selectFirst("section.video-holder div.video-info div.info-holder article#tab_video_info.tab-content div.headline h1")
?.text()
?: ""
val poster = fixUrlNull(
document.selectXpath("//script[contains(text(),'var schemaJson')]").first()?.data()
?.replace("\"", "")
?.substringAfter("thumbnailUrl:")
?.substringBefore(",uploadDate:")
?.trim() ?: ""
)
val tags =
document.select(" section.video-holder div.video-info div.info-holder article#tab_video_info.tab-content div.block-details div.info h3.item a")
.map { it.text() }
val recommendations =
document.select("div.related-videos div.list-videos article.item")
.mapNotNull {
it.toSearchResult()
}
return newMovieLoadResponse(title, url, TvType.NSFW, url) {
this.posterUrl = poster
this.tags = tags
this.recommendations = recommendations
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
val script =
document.selectXpath("//script[contains(text(),'let vpage_data')]").first()?.html()
var isVHQ = false
if (script != null && script.contains("VHQ")) {
isVHQ = true
}
val pattern = Regex("""window\.initPlayer\((.*])\);""")
val matchResult = pattern.find(script ?: "")
val jsonArray = matchResult?.groups?.get(1)?.value
val encodedString = getEncodedString(jsonArray) ?: ""
val decodedString = customBase64Decoder(encodedString)
val videos = JSONObject("{ videos:$decodedString}").getJSONArray("videos")
val externalLinkList = mutableListOf<ExtractorLink>()
for (i in 0 until videos.length()) {
val video = videos.getJSONObject(i)
var quality = Qualities.Unknown.value
var isM3u8 = false
if (video.getString("format").contains("lq")) {
quality = Qualities.P480.value
}
if (video.getString("format").contains("hq")) {
quality = Qualities.P720.value
}
var url = customBase64Decoder(video.getString("video_url"))
if (isVHQ) {
url = "$url&f=video.m3u8"
isM3u8 = true
quality = Qualities.Unknown.value
}
externalLinkList.add(
ExtractorLink(
this.name,
this.name,
fixUrl(url),
referer = mainUrl,
quality = quality,
isM3u8 = isM3u8
)
)
if (isVHQ) break
}
externalLinkList.forEach(callback)
return true
}
private fun customBase64Decoder(encodedString: String): String {
val base64CharacterSet = "АВСDЕFGHIJKLМNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,~"
var decodedString = ""
var currentIndex = 0
Regex("[^АВСЕМA-Za-z0-9.,~]").find(encodedString)?.let {
println("Error decoding URL")
}
val sanitizedString = encodedString.replace("[^АВСЕМA-Za-z0-9.,~]".toRegex(), "")
do {
val firstCharIndex = base64CharacterSet.indexOf(sanitizedString[currentIndex++])
val secondCharIndex = base64CharacterSet.indexOf(sanitizedString[currentIndex++])
val thirdCharIndex = base64CharacterSet.indexOf(sanitizedString[currentIndex++])
val fourthCharIndex = base64CharacterSet.indexOf(sanitizedString[currentIndex++])
val reconstructedFirstChar = (firstCharIndex shl 2) or (secondCharIndex shr 4)
val reconstructedSecondChar = ((15 and secondCharIndex) shl 4) or (thirdCharIndex shr 2)
val lastPart = ((3 and thirdCharIndex) shl 6) or fourthCharIndex
decodedString += reconstructedFirstChar.toChar().toString()
if (64 != thirdCharIndex) {
decodedString += reconstructedSecondChar.toChar().toString()
}
if (64 != fourthCharIndex) {
decodedString += lastPart.toChar().toString()
}
} while (currentIndex < sanitizedString.length)
return java.net.URLDecoder.decode(decodedString, "UTF-8")
}
private fun getEncodedString(json: String?): String? {
val stringPattern = Regex("""'([^']+)',""")
val stringMatch = stringPattern.find(json ?: "")
return when {
stringMatch != null -> stringMatch.groups[1]?.value
else -> null
}
}
}

View File

@ -0,0 +1,14 @@
package com.KillerDogeEmpire
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
import com.KillerDogeEmpire.Pornhits
@CloudstreamPlugin
class PornhitsProvider: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(Pornhits())
}
}

View File

@ -1,12 +1,12 @@
// use an integer for version numbers // use an integer for version numbers
version = 5 version = 6
cloudstream { cloudstream {
// All of these properties are optional, you can safely remove them // All of these properties are optional, you can safely remove them
description = "Cornhub" description = "Cornhub"
authors = listOf("Stormunblessed", "Jace") authors = listOf("KillerDogeEmpire, Stormunblessed, Jace ,Hexated, Coxju")
/** /**
* Status int as the following: * Status int as the following:

View File

@ -1,142 +1,117 @@
package com.jacekun package com.jacekun
import com.lagradost.cloudstream3.MainAPI import android.util.Log
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.network.WebViewResolver
import com.lagradost.cloudstream3.utils.*
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
class Pornhub : MainAPI() { class PornHub : MainAPI() {
private val globalTvType = TvType.NSFW override var mainUrl = "https://www.pornhub.com"
override var name = "PornHub"
override var mainUrl = "https://www.pornhub.com" override val hasMainPage = true
override var name = "Pornhub" override var lang = "en"
override val hasMainPage = true override val hasQuickSearch = false
override val hasDownloadSupport = true
override val hasChromecastSupport = true override val hasChromecastSupport = true
override val hasDownloadSupport = true override val supportedTypes = setOf(TvType.NSFW)
override val vpnStatus = VPNStatus.MightBeNeeded //Cause it's a big site override val vpnStatus = VPNStatus.MightBeNeeded
override val supportedTypes = setOf(TvType.NSFW)
override val mainPage = mainPageOf( override val mainPage = mainPageOf(
"$mainUrl/video?page=" to "Main Page", "${mainUrl}/video?o=mr&hd=1&page=" to "Recently Featured",
"${mainUrl}/video?o=tr&t=w&hd=1&page=" to "Top Rated",
"${mainUrl}/video?o=mv&t=w&hd=1&page=" to "Most Viewed",
"${mainUrl}/video?o=ht&t=w&hd=1&page=" to "Hottest",
"${mainUrl}/video?p=professional&hd=1&page=" to "Professional",
"${mainUrl}/video?o=lg&hd=1&page=" to "Longest",
"${mainUrl}/video?p=homemade&hd=1&page=" to "Homemade",
"${mainUrl}/video?o=cm&t=w&hd=1&page=" to "Newest",
) )
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
try { val document = app.get(request.data + page).document
val categoryData = request.data val home = document.select("li.pcVideoListItem").mapNotNull { it.toSearchResult() }
val categoryName = request.name
val pagedLink = if (page > 0) categoryData + page else categoryData return newHomePageResponse(
val soup = app.get(pagedLink).document list = HomePageList(
val home = soup.select("div.sectionWrapper div.wrap").mapNotNull { name = request.name,
if (it == null) { return@mapNotNull null } list = home,
val title = it.selectFirst("span.title a")?.text() ?: "" isHorizontalImages = true
val link = fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null ),
val img = fetchImgUrl(it.selectFirst("img")) hasNext = true
MovieSearchResponse( )
name = title, }
url = link,
apiName = this.name, private fun Element.toSearchResult(): SearchResponse? {
type = globalTvType, val title = this.selectFirst("a")?.attr("title") ?: return null
posterUrl = img val link = this.selectFirst("a")?.attr("href") ?: return null
) val posterUrl = fixUrlNull(this.selectFirst("img.thumb")?.attr("src"))
}
if (home.isNotEmpty()) { return newMovieSearchResponse(title, link, TvType.Movie) { this.posterUrl = posterUrl }
return newHomePageResponse(
list = HomePageList(
name = categoryName,
list = home,
isHorizontalImages = true
),
hasNext = true
)
} else {
throw ErrorLoadingException("No homepage data found!")
}
} catch (e: Exception) {
//e.printStackTrace()
logError(e)
}
throw ErrorLoadingException()
} }
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val url = "$mainUrl/video/search?search=${query}" val document = app.get("${mainUrl}/video/search?search=${query}").document
val document = app.get(url).document
return document.select("div.sectionWrapper div.wrap").mapNotNull { return document.select("li.pcVideoListItem").mapNotNull { it.toSearchResult() }
if (it == null) { return@mapNotNull null }
val title = it.selectFirst("span.title a")?.text() ?: return@mapNotNull null
val link = fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null
val image = fetchImgUrl(it.selectFirst("img"))
MovieSearchResponse(
name = title,
url = link,
apiName = this.name,
type = globalTvType,
posterUrl = image
)
}.distinctBy { it.url }
} }
override suspend fun load(url: String): LoadResponse { override suspend fun quickSearch(query: String): List<SearchResponse> = search(query)
val soup = app.get(url).document
val title = soup.selectFirst(".title span")?.text() ?: "" override suspend fun load(url: String): LoadResponse? {
val poster: String? = soup.selectFirst("div.video-wrapper .mainPlayerDiv img")?.attr("src") ?: val document = app.get(url).document
soup.selectFirst("head meta[property=og:image]")?.attr("content")
val tags = soup.select("div.categoriesWrapper a") val title = document.selectFirst("h1.title span[class='inlineFree']")?.text()?.trim() ?: return null
.map { it?.text()?.trim().toString().replace(", ","") } val description = title
return MovieLoadResponse( val poster = fixUrlNull(document.selectFirst("div.mainPlayerDiv img")?.attr("src"))
name = title, val year = Regex("""uploadDate\": \"(\d+)""").find(document.html())?.groupValues?.get(1)?.toIntOrNull()
url = url, val tags = document.select("div.categoriesWrapper a[data-label='Category']").map { it?.text()?.trim().toString().replace(", ","") }
apiName = this.name, val rating = document.selectFirst("span.percent")?.text()?.first()?.toString()?.toRatingInt()
type = globalTvType, val duration = Regex("duration' : '(.*)',").find(document.html())?.groupValues?.get(1)?.toIntOrNull()
dataUrl = url, val actors = document.select("div.pornstarsWrapper a[data-label='Pornstar']").mapNotNull {
posterUrl = poster, Actor(it.text().trim(), it.select("img").attr("src"))
tags = tags, }
plot = title
) val recommendations = document.selectXpath("//a[contains(@class, 'img')]").mapNotNull {
} val recName = it?.attr("title")?.trim() ?: return@mapNotNull null
override suspend fun loadLinks( val recHref = fixUrlNull(it.attr("href")) ?: return@mapNotNull null
data: String, val recPosterUrl = fixUrlNull(it.selectFirst("img")?.attr("src"))
isCasting: Boolean, newMovieSearchResponse(recName, recHref, TvType.NSFW) {
subtitleCallback: (SubtitleFile) -> Unit, this.posterUrl = recPosterUrl
callback: (ExtractorLink) -> Unit
): Boolean {
app.get(
url = data,
interceptor = WebViewResolver(
Regex("(master\\.m3u8\\?.*)")
)
).let { response ->
M3u8Helper().m3u8Generation(
M3u8Helper.M3u8Stream(
response.url,
headers = response.headers.toMap()
), true
).apmap { stream ->
callback(
ExtractorLink(
source = name,
name = "${this.name} m3u8",
url = stream.streamUrl,
referer = mainUrl,
quality = getQualityFromName(stream.quality?.toString()),
isM3u8 = true
)
)
} }
} }
return true
return newMovieLoadResponse(title, url, TvType.NSFW, url) {
this.posterUrl = poster
this.year = year
this.plot = description
this.tags = tags
this.rating = rating
this.duration = duration
this.recommendations = recommendations
addActors(actors)
}
} }
private fun fetchImgUrl(imgsrc: Element?): String? { override suspend fun loadLinks(data: String, isCasting: Boolean, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit): Boolean {
return try { imgsrc?.attr("data-src") Log.d("PHub", "url » ${data}")
?: imgsrc?.attr("data-mediabook") val source = app.get(data).text
?: imgsrc?.attr("alt") val extracted_value = Regex("""([^\"]*master.m3u8?.[^\"]*)""").find(source)?.groups?.last()?.value ?: return false
?: imgsrc?.attr("data-mediumthumb") val m3u_link = extracted_value.replace("\\", "")
?: imgsrc?.attr("data-thumb_url") Log.d("PHub", "extracted_value » ${extracted_value}")
?: imgsrc?.attr("src") Log.d("PHub", "m3u_link » ${m3u_link}")
} catch (e:Exception) { null }
callback.invoke(
ExtractorLink(
source = this.name,
name = this.name,
url = m3u_link,
referer = "${mainUrl}/",
quality = Qualities.Unknown.value,
isM3u8 = true
)
)
return true
} }
} }

28
Porntrex/build.gradle.kts Normal file
View File

@ -0,0 +1,28 @@
// use an integer for version numbers
version = 5
cloudstream {
// All of these properties are optional, you can safely remove them
description = "Porntrex"
authors = listOf("KillerDogeEmpire, Coxju")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here:
// https://recloudstream.github.io/cloudstream/html/app/com.lagradost.cloudstream3/-tv-type/index.html
tvTypes = listOf("NSFW")
iconUrl = "https://www.google.com/s2/favicons?domain=www.porntrex.com&sz=%size%"
language = "en"
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.KillerDogeEmpire"/>

View File

@ -0,0 +1,183 @@
package com.KillerDogeEmpire
import com.lagradost.cloudstream3.HomePageList
import com.lagradost.cloudstream3.HomePageResponse
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.MainPageRequest
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.VPNStatus
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.fixUrl
import com.lagradost.cloudstream3.fixUrlNull
import com.lagradost.cloudstream3.mainPageOf
import com.lagradost.cloudstream3.newHomePageResponse
import com.lagradost.cloudstream3.newMovieLoadResponse
import com.lagradost.cloudstream3.newMovieSearchResponse
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getQualityFromName
import org.json.JSONObject
import org.jsoup.internal.StringUtil
import org.jsoup.nodes.Element
class Porntrex : MainAPI() {
override var mainUrl = "https://www.porntrex.com"
override var name = "Porntrex"
override val hasMainPage = true
override val hasDownloadSupport = true
override val vpnStatus = VPNStatus.MightBeNeeded
override val supportedTypes = setOf(TvType.NSFW)
override val mainPage = mainPageOf(
"latest-updates" to "Latest Videos",
"most-popular/daily/?mode=async&function=get_block&block_id=list_videos_common_videos_list_norm&sort_by=video_viewed_today&from4=" to "Most popular daily",
"top-rated/daily/?mode=async&function=get_block&block_id=list_videos_common_videos_list_norm&sort_by=rating_today&from4=" to "Top rated daily",
"most-popular/weekly/?mode=async&function=get_block&block_id=list_videos_common_videos_list_norm&sort_by=video_viewed_week&from4=" to "Most popular weekly",
"top-rated/weekly/?mode=async&function=get_block&block_id=list_videos_common_videos_list_norm&sort_by=rating_week&from4=" to "Top rated weekly",
"most-popular/monthly/?mode=async&function=get_block&block_id=list_videos_common_videos_list_norm&sort_by=video_viewed_month&from4=" to "Most popular monthly",
"top-rated/monthly/?mode=async&function=get_block&block_id=list_videos_common_videos_list_norm&sort_by=rating_month&from4=" to "Top rated monthly",
"most-popular/?mode=async&function=get_block&block_id=list_videos_common_videos_list_norm&sort_by=video_viewed&from4=" to "Most popular all time",
"top-rated/?mode=async&function=get_block&block_id=list_videos_common_videos_list_norm&sort_by=rating&from4=" to "Top rated all time",
)
override suspend fun getMainPage(
page: Int,
request: MainPageRequest
): HomePageResponse {
var url: String
url = if (page == 1) {
"$mainUrl/${request.data}/"
} else {
"$mainUrl/${request.data}/${page}/"
}
if (request.data.contains("mode=async")) {
url = "$mainUrl/${request.data}${page}"
}
val document = app.get(url).document
val home =
document.select("div.video-list div.video-item")
.mapNotNull {
it.toSearchResult()
}
return newHomePageResponse(
list = HomePageList(
name = request.name,
list = home,
isHorizontalImages = true
),
hasNext = true
)
}
private fun Element.toSearchResult(): SearchResponse? {
val title = this.selectFirst("p.inf a")?.text() ?: return null
val href = fixUrl(this.selectFirst("p.inf a")!!.attr("href"))
val posterUrl = fixUrlNull(this.select("a.thumb img.cover").attr("data-src"))
return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
this.posterHeaders = mapOf(Pair("referer", "${mainUrl}/"))
}
}
override suspend fun search(query: String): List<SearchResponse> {
val searchResponse = mutableListOf<SearchResponse>()
for (i in 1..15) {
val url: String = if (i == 1) {
"$mainUrl/search/${query.replace(" ", "-")}/"
} else {
"$mainUrl/search/${query.replace(" ", "-")}/$i/"
}
val document =
app.get(url).document
val results =
document.select("div.video-list div.video-item")
.mapNotNull {
it.toSearchResult()
}
searchResponse.addAll(results)
if (results.isEmpty()) break
}
return searchResponse
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val jsonObject = JSONObject(document.selectXpath("//script[contains(text(),'var flashvars')]").first()?.data()
?.substringAfter("var flashvars = ")
?.substringBefore("var player_obj")
?.replace(";", "") ?: "")
val title = jsonObject.getString("video_title")
val poster =
fixUrlNull(jsonObject.getString("preview_url"))
val tags = jsonObject.getString("video_tags").split(", ").map { it.replace("-", "") }.filter { it.isNotBlank() && !StringUtil.isNumeric(it) }
val description = jsonObject.getString("video_title")
val recommendations =
document.select("div#list_videos_related_videos div.video-list div.video-item").mapNotNull {
it.toSearchResult()
}
return newMovieLoadResponse(title, url, TvType.NSFW, url) {
this.posterUrl = poster
this.posterHeaders = mapOf(Pair("referer", "${mainUrl}/"))
this.plot = description
this.tags = tags
this.recommendations = recommendations
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
val jsonObject = JSONObject(document.selectXpath("//script[contains(text(),'var flashvars')]").first()?.data()
?.substringAfter("var flashvars = ")
?.substringBefore("var player_obj")
?.replace(";", "") ?: "")
val extlinkList = mutableListOf<ExtractorLink>()
for (i in 0 until 7) {
var url: String
var quality: String
if (i == 0) {
url = jsonObject.optString("video_url") ?: ""
quality = jsonObject.optString("video_url_text") ?: ""
} else {
if (i == 1) {
url = jsonObject.optString("video_alt_url") ?: ""
quality = jsonObject.optString("video_alt_url_text") ?: ""
} else {
url = jsonObject.optString("video_alt_url${i}") ?: ""
quality = jsonObject.optString("video_alt_url${i}_text") ?: ""
}
}
if (url == "") {
continue
}
extlinkList.add(
ExtractorLink(
name,
name,
fixUrl(url),
referer = "${mainUrl}/",
quality =
Regex("(\\d+.)").find(quality)?.groupValues?.get(1)
.let { getQualityFromName(it) }
)
)
}
extlinkList.forEach(callback)
return true
}
}

View File

@ -0,0 +1,14 @@
package com.KillerDogeEmpire
import android.content.Context
import com.KillerDogeEmpire.Porntrex
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
@CloudstreamPlugin
class PorntrexProvider : Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(Porntrex())
}
}

View File

@ -1,12 +1,12 @@
// use an integer for version numbers // use an integer for version numbers
version = 1 version = 6
cloudstream { cloudstream {
// All of these properties are optional, you can safely remove them // All of these properties are optional, you can safely remove them
description = "" description = "sxyprn"
authors = listOf("Jace") authors = listOf("Coxju")
/** /**
* Status int as the following: * Status int as the following:
@ -15,12 +15,14 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 0 // will be 3 if unspecified status = 1 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category. // List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here: // You can find a list of avaliable types here:
// https://recloudstream.github.io/cloudstream/html/app/com.lagradost.cloudstream3/-tv-type/index.html // https://recloudstream.github.io/cloudstream/html/app/com.lagradost.cloudstream3/-tv-type/index.html
tvTypes = listOf("NSFW") tvTypes = listOf("NSFW")
iconUrl = "https://www.google.com/s2/favicons?domain=example.com&sz=%size%" iconUrl = "https://www.google.com/s2/favicons?domain=sxyprn.com&sz=%size%"
language = "en"
} }

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.KillerDogeEmpire"/>

View File

@ -0,0 +1,143 @@
package com.KillerDogeEmpire
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import org.jsoup.nodes.Element
class SxyPrn : MainAPI() {
override var mainUrl = "https://sxyprn.com"
override var name = "Sxyprn"
override val hasMainPage = true
override val hasDownloadSupport = true
override val vpnStatus = VPNStatus.MightBeNeeded
override val supportedTypes = setOf(TvType.NSFW)
override val mainPage = mainPageOf(
"$mainUrl/new.html?page=" to "New Videos",
"$mainUrl/new.html?sm=trending&page=" to "Trending",
"$mainUrl/new.html?sm=views&page=" to "Most Viewed",
"$mainUrl/popular/top-viewed.html?p=day" to "Popular - Day",
"$mainUrl/popular/top-viewed.html" to "Popular - Week",
"$mainUrl/popular/top-viewed.html?p=month" to "Popular - Month",
"$mainUrl/popular/top-viewed.html?p=all" to "Popular - All Time"
)
override suspend fun getMainPage(
page: Int, request: MainPageRequest
): HomePageResponse {
var pageStr = ((page - 1) * 30).toString()
val document = if ("page=" in request.data) {
app.get(request.data + pageStr).document
} else if ("/blog/" in request.data) {
pageStr = ((page - 1) * 20).toString()
app.get(request.data.replace(".html", "$pageStr.html")).document
} else {
app.get(request.data.replace(".html", ".html/$pageStr")).document
}
val home = document.select("div.main_content div.post_el_small").mapNotNull {
it.toSearchResult()
}
return newHomePageResponse(
list = HomePageList(
name = request.name, list = home, isHorizontalImages = true
), hasNext = true
)
}
private fun Element.toSearchResult(): SearchResponse? {
val title = this.selectFirst("div.post_text")?.text() ?: return null
val href = fixUrl(this.selectFirst("a.js-pop")!!.attr("href"))
var posterUrl = fixUrl(this.select("div.vid_container div.post_vid_thumb img").attr("src"))
if (posterUrl == "") {
posterUrl =
fixUrl(this.select("div.vid_container div.post_vid_thumb img").attr("data-src"))
}
return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
}
}
override suspend fun search(query: String): List<SearchResponse> {
val searchResponse = mutableListOf<SearchResponse>()
for (i in 0 until 15) {
val document = app.get(
"$mainUrl/${query.replace(" ", "-")}.html?page=${i * 30}"
).document
val results = document.select("div.main_content div.post_el_small").mapNotNull {
it.toSearchResult()
}
if (!searchResponse.containsAll(results)) {
searchResponse.addAll(results)
} else {
break
}
if (results.isEmpty()) break
}
return searchResponse
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst("div.post_text")?.text()?.trim().toString()
val poster = fixUrlNull(
document.selectFirst("div#vid_container_id meta[itemprop=thumbnailUrl]")
?.attr("content")
)
val recommendations = document.select("div.main_content div div.post_el_small").mapNotNull {
it.toSearchResult()
}
return newMovieLoadResponse(title, url, TvType.NSFW, url) {
this.posterUrl = poster
this.recommendations = recommendations
}
}
private fun updateUrl(arg: MutableList<String>): MutableList<String> {
arg[5] =
(Integer.parseInt(arg[5]) - (generateNumber(arg[6]) + generateNumber(arg[7]))).toString()
return arg
}
private fun generateNumber(arg: String): Int {
val str = arg.replace(Regex("\\D"), "")
var sut = 0
for (element in str) {
sut += Integer.parseInt(element.toString(), 10)
}
return sut
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
val parsed = AppUtils.parseJson<Map<String, String>>(
document.select("span.vidsnfo").attr("data-vnfo")
)
parsed[parsed.keys.toList()[0]]
var url = parsed[parsed.keys.toList()[0]].toString()
var tmp = url.split("/").toMutableList()
tmp[1] += "8"
tmp = updateUrl(tmp)
url = fixUrl(tmp.joinToString("/"))
callback.invoke(
ExtractorLink(
this.name, this.name, url, referer = data, quality = Qualities.Unknown.value
)
)
return true
}
}

View File

@ -0,0 +1,14 @@
package com.KillerDogeEmpire
import android.content.Context
import com.KillerDogeEmpire.SxyPrn
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
@CloudstreamPlugin
class SxyPrnProvider : Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(SxyPrn())
}
}

View File

@ -0,0 +1,28 @@
// use an integer for version numbers
version = 5
cloudstream {
// All of these properties are optional, you can safely remove them
description = "Uncutmaza"
authors = listOf("Coxju")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here:
// https://recloudstream.github.io/cloudstream/html/app/com.lagradost.cloudstream3/-tv-type/index.html
tvTypes = listOf("NSFW")
iconUrl = "https://www.google.com/s2/favicons?domain=uncutmaza.com&sz=%size%"
language = "en"
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.KillerDogeEmpire"/>

View File

@ -0,0 +1,118 @@
package com.KillerDogeEmpire
import com.lagradost.cloudstream3.HomePageList
import com.lagradost.cloudstream3.HomePageResponse
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.MainAPI
import com.lagradost.cloudstream3.MainPageRequest
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.VPNStatus
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.fixTitle
import com.lagradost.cloudstream3.fixUrl
import com.lagradost.cloudstream3.fixUrlNull
import com.lagradost.cloudstream3.mainPageOf
import com.lagradost.cloudstream3.newHomePageResponse
import com.lagradost.cloudstream3.newMovieLoadResponse
import com.lagradost.cloudstream3.newMovieSearchResponse
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import org.jsoup.nodes.Element
class UncutMaza : MainAPI() {
override var mainUrl = "https://uncutmaza.com"
override var name = "Uncutmaza"
override val hasMainPage = true
override val hasDownloadSupport = true
override val vpnStatus = VPNStatus.MightBeNeeded
override val supportedTypes = setOf(TvType.NSFW)
override val mainPage = mainPageOf(
"$mainUrl/page/" to "Home", "$mainUrl/category/niks-indian-porn/page/" to "Niks Indian"
)
override suspend fun getMainPage(
page: Int, request: MainPageRequest
): HomePageResponse {
val document = app.get(request.data + page).document
val home = document.select("div.videos-list > article.post").mapNotNull {
it.toSearchResult()
}
return newHomePageResponse(
list = HomePageList(
name = request.name, list = home, isHorizontalImages = true
), hasNext = true
)
}
private fun Element.toSearchResult(): SearchResponse {
val title = fixTitle(this.select("a").attr("title"))
val href = fixUrl(this.select("a").attr("href"))
val posterUrl = fixUrlNull(
this.select("a > div.post-thumbnail>div.post-thumbnail-container>img").attr("data-src")
)
return newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
}
}
override suspend fun search(query: String): List<SearchResponse> {
val searchResponse = mutableListOf<SearchResponse>()
for (i in 1..5) {
val document = app.get(
"$mainUrl/page/$i?s=$query"
).document
val results = document.select("article.post").mapNotNull {
it.toSearchResult()
}
if (!searchResponse.containsAll(results)) {
searchResponse.addAll(results)
} else {
break
}
if (results.isEmpty()) break
}
return searchResponse
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title =
document.selectFirst("meta[property=og:title]")?.attr("content")?.trim().toString()
val poster =
fixUrlNull(document.selectFirst("meta[property=og:image]")?.attr("content").toString())
val description =
document.selectFirst("meta[property=og:description]")?.attr("content")?.trim()
return newMovieLoadResponse(title, url, TvType.NSFW, url) {
this.posterUrl = poster
this.plot = description
}
}
override suspend fun loadLinks(
data: String,
isCasting: Boolean,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
): Boolean {
val document = app.get(data).document
document.select("div.video-player").map { res ->
callback.invoke(
ExtractorLink(
this.name, this.name, fixUrl(
res.selectFirst("meta[itemprop=contentURL]")?.attr("content")?.trim()
.toString()
), referer = data, quality = Qualities.Unknown.value
)
)
}
return true
}
}

View File

@ -0,0 +1,14 @@
package com.KillerDogeEmpire
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context
import com.KillerDogeEmpire.UncutMaza
@CloudstreamPlugin
class UncutMazaProvider: Plugin() {
override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(UncutMaza())
}
}

View File

@ -15,7 +15,7 @@ cloudstream {
* 2: Slow * 2: Slow
* 3: Beta only * 3: Beta only
* */ * */
status = 3 // will be 3 if unspecified status = 0 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category. // List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here: // You can find a list of avaliable types here:

28
Xhamster/build.gradle.kts Normal file
View File

@ -0,0 +1,28 @@
// use an integer for version numbers
version = 5
cloudstream {
// All of these properties are optional, you can safely remove them
description = "Xhamster"
authors = listOf("KillerDogeEmpire, Coxju")
/**
* Status int as the following:
* 0: Down
* 1: Ok
* 2: Slow
* 3: Beta only
* */
status = 1 // will be 3 if unspecified
// List of video source types. Users are able to filter for extensions in a given category.
// You can find a list of avaliable types here:
// https://recloudstream.github.io/cloudstream/html/app/com.lagradost.cloudstream3/-tv-type/index.html
tvTypes = listOf("NSFW")
iconUrl = "https://www.google.com/s2/favicons?domain=xhamster.com&sz=%size%"
language = "en"
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.KillerDogeEmpire"/>

View File

@ -0,0 +1,100 @@
package com.KillerDogeEmpire
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import org.jsoup.nodes.Element
class Xhamster : MainAPI() {
override var mainUrl = "https://xhamster.com"
override var name = "xHamster"
override val hasMainPage = true
override var lang = "en"
override val hasQuickSearch = false
override val hasDownloadSupport = true
override val hasChromecastSupport = true
override val supportedTypes = setOf(TvType.NSFW)
override val vpnStatus = VPNStatus.MightBeNeeded
override val mainPage = mainPageOf(
"${mainUrl}/newest/" to "Newest",
"${mainUrl}/most-viewed/weekly/" to "Most viewed weekly",
"${mainUrl}/most-viewed/monthly/" to "Most viewed monthly",
"${mainUrl}/most-viewed" to "Most viewed all time",
"${mainUrl}/most-viewed/weekly/" to "Most viewed weekly"
)
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val document = app.get(request.data + page + "?x_platform_switch=desktop").document
val home = document.select("div.thumb-list div.thumb-list__item").mapNotNull { it.toSearchResult() }
return newHomePageResponse(
list = HomePageList(
name = request.name,
list = home,
isHorizontalImages = true
),
hasNext = true
)
}
private fun Element.toSearchResult(): SearchResponse? {
val title = this.selectFirst("a.video-thumb-info__name")?.text() ?: return null
val href = fixUrl(this.selectFirst("a.video-thumb-info__name")!!.attr("href"))
val posterUrl = fixUrlNull(this.select("img.thumb-image-container__image").attr("src"))
return newMovieSearchResponse(title, href, TvType.Movie) { this.posterUrl = posterUrl }
}
override suspend fun search(query: String): List<SearchResponse> {
val searchResponse = mutableListOf<SearchResponse>()
for (i in 0 until 15) {
val document = app.get("${mainUrl}/search/${query.replace(" ", "+")}/?page=$i&x_platform_switch=desktop").document
val results = document.select("div.thumb-list div.thumb-list__item").mapNotNull { it.toSearchResult() }
if (!searchResponse.containsAll(results)) {
searchResponse.addAll(results)
} else {
break
}
if (results.isEmpty()) break
}
return searchResponse
}
override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document
val title = document.selectFirst("div.with-player-container h1")?.text()?.trim().toString()
val poster = fixUrlNull(document.selectFirst("div.xp-preload-image")?.attr("style")?.substringAfter("https:")?.substringBefore("\');"))
val tags = document.select(" nav#video-tags-list-container ul.root-8199e.video-categories-tags.collapsed-8199e li.item-8199e a.video-tag").map { it.text() }
val recommendations = document.select("div.related-container div.thumb-list div.thumb-list__item").mapNotNull { it.toSearchResult() }
return newMovieLoadResponse(title, url, TvType.NSFW, url) {
this.posterUrl = poster
this.tags = tags
this.recommendations = recommendations
}
}
override suspend fun loadLinks(data: String, isCasting: Boolean, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit): Boolean {
app.get(url = data).let { response ->
callback(
ExtractorLink(
source = name,
name = name,
url = fixUrl(response.document.selectXpath("//link[contains(@href,'.m3u8')]")[0]?.attr("href").toString()),
referer = mainUrl,
quality = Qualities.Unknown.value,
isM3u8 = true
)
)
}
return true
}
}

View File

@ -1,13 +1,13 @@
package com.jacekun package com.KillerDogeEmpire
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
import com.lagradost.cloudstream3.plugins.Plugin import com.lagradost.cloudstream3.plugins.Plugin
import android.content.Context import android.content.Context
@CloudstreamPlugin @CloudstreamPlugin
class ExamplePlugin: Plugin() { class XhamsterProvider: Plugin() {
override fun load(context: Context) { override fun load(context: Context) {
// All providers should be added in this manner. Please don't edit the providers list directly. // All providers should be added in this manner. Please don't edit the providers list directly.
registerMainAPI(Example()) registerMainAPI(Xhamster())
} }
} }