sora: added watchflx,hdmovies4u & solve #272, #273, #274

This commit is contained in:
hexated 2023-09-15 21:24:05 +07:00
parent 36e77332c5
commit 86f2735502
9 changed files with 207 additions and 187 deletions

View File

@ -6,10 +6,9 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.utils.*
import org.jsoup.nodes.Element
import java.net.URLDecoder
class LayarKacaProvider : MainAPI() {
override var mainUrl = "https://tv3.lk21official.pro"
override var mainUrl = "https://tv.lk21official.wiki"
private var seriesUrl = "https://tv1.nontondrama.click"
override var name = "LayarKaca"
override val hasMainPage = true

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.konan.properties.Properties
// use an integer for version numbers
version = 169
version = 170
android {
defaultConfig {

View File

@ -186,11 +186,6 @@ open class VCloud : ExtractorApi() {
}
class HubcloudLol : VCloud() {
override val name = "Hubcloud"
override val mainUrl = "https://hubcloud.lol"
}
class Hubcloud : VCloud() {
override val name = "Hubcloud"
override val mainUrl = "https://hubcloud.in"

View File

@ -580,6 +580,56 @@ object SoraExtractor : SoraStream() {
}
suspend fun invokeWatchflx(
tmdbId: Int? = null,
season: Int? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit
) {
val epsSlug = getEpisodeSlug(season, episode)
val cookies = getWatchflxCookies()
val url = if (season == null) {
"$watchflxAPI/browse/playmovie/$tmdbId/directplay"
} else {
"$watchflxAPI/Playseries/series/$tmdbId/directplay"
}
val res = app.get(url, cookies = cookies).document
val showUrl = if (season == null) {
res.selectFirst("iframe.movie_player")?.attr("src")
} else {
val seasonUrl =
res.select("ul.nav.nav-tabs.tabs-left li:matches(Season $season\$) a").attr("href")
val episodeUrl = app.get(
seasonUrl,
cookies = cookies
).document.select("div.thumb-nail-list a:contains(${epsSlug.second}:)").attr("href")
app.get(episodeUrl, cookies = cookies).document.selectFirst("iframe.movie_player")
?.attr("src")
}
val iframe = app.get(
showUrl ?: return, referer = "$watchflxAPI/"
).document.selectFirst("div#the_frame iframe")?.attr("src")
?.let { fixUrl(it, getBaseUrl(showUrl)) } ?: return
val video = app.get(iframe.replace("/loc/", "/pro/"), referer = iframe).text.let {
"""mp4_url\s*=\s*["'](.*)["'];""".toRegex().find(it)?.groupValues?.getOrNull(1)
}
callback.invoke(
ExtractorLink(
"Watchflx",
"Watchflx",
video ?: return,
"$watchflxAPI/",
Qualities.P1080.value,
INFER_TYPE
)
)
}
suspend fun invokeKimcartoon(
title: String? = null,
season: Int? = null,
@ -1144,7 +1194,13 @@ object SoraExtractor : SoraStream() {
val aTag = if (season == null) "Download Now" else "V-Cloud"
res.select("div.entry-content > $hTag:matches(1080p|2160p)").apmap {
val tags = """(?:1080p|2160p)(.*)""".toRegex().find(it.text())?.groupValues?.get(1)?.trim()
val href = it.nextElementSibling()?.select("a:contains($aTag)")?.attr("href")
val href = it.nextElementSibling()?.select("a:contains($aTag)")?.attr("href")?.let { url ->
app.post(
"${getBaseUrl(url)}/red.php",
data = mapOf("link" to url),
referer = "$vegaMoviesAPI/"
).text.substringAfter("location.href = \"").substringBefore("\"")
}
val selector = if (season == null) "p a:contains(V-Cloud)" else "h4:matches(0?$episode) + p a:contains(V-Cloud)"
val server = app.get(href ?: return@apmap).document.selectFirst("div.entry-content > $selector")
?.attr("href")
@ -1233,6 +1289,45 @@ object SoraExtractor : SoraStream() {
}
suspend fun invokeHdmovies4u(
title: String? = null,
imdbId: String? = null,
season: Int? = null,
episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
fun String.decodeLink(): String {
return base64Decode(this.substringAfterLast("/"))
}
val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode)
val media = app.get("$hdmovies4uAPI/?s=${if (season == null) imdbId else title}").document
.let {
val selector = if (season == null) "a" else "a:matches((?i)$title.*Season $season)"
it.selectFirst("div.gridxw.gridxe $selector")?.attr("href")
}
val selector = if (season == null) "1080p|2160p" else "(?i)Episode.*(?:1080p|2160p)"
app.get(
media ?: return
).document.select("section h4:matches($selector)").apmap { ele ->
val (tags, size) = ele.select("span").map {
it.text()
}.let { it[it.lastIndex - 1] to it.last().substringAfter("-").trim() }
val link = ele.nextElementSibling()?.select("a:contains(DriveTOT)")?.attr("href")
val iframe = bypassBqrecipes(link?.decodeLink() ?: return@apmap).let {
if (it?.contains("/pack/") == true) {
val href =
app.get(it).document.select("table tbody tr:contains(S${seasonSlug}E${episodeSlug}) a")
.attr("href")
bypassBqrecipes(href.decodeLink())
} else {
it
}
}
invokeDrivetot(iframe ?: return@apmap, tags, size, subtitleCallback, callback)
}
}
suspend fun invokeFwatayako(
imdbId: String? = null,
season: Int? = null,
@ -1859,25 +1954,12 @@ object SoraExtractor : SoraStream() {
invokeSmashyFfix(it.second, it.first, url, callback)
}
it.first.contains("/gtop") -> {
invokeSmashyGtop(it.second, it.first, callback)
}
it.first.contains("/dude_tv") -> {
invokeSmashyDude(it.second, it.first, callback)
}
it.first.contains("/rip") -> {
invokeSmashyRip(it.second, it.first, subtitleCallback, callback)
}
it.first.contains("/im.php") && !isAnime -> {
invokeSmashyIm(it.second, it.first, subtitleCallback, callback)
}
it.first.contains("/rw.php") && !isAnime -> {
invokeSmashyRw(it.second, it.first, subtitleCallback, callback)
}
it.second.equals("Player FM", true) && !isAnime -> invokeSmashyFm(
it.second,
it.first,
url,
callback
)
else -> return@apmap
}

View File

@ -141,16 +141,6 @@ data class FDAds(
@JsonProperty("linkr") val linkr: String? = null,
)
data class Smashy1Tracks(
@JsonProperty("file") val file: String? = null,
@JsonProperty("label") val label: String? = null,
)
data class Smashy1Source(
@JsonProperty("file") val file: String? = null,
@JsonProperty("tracks") val tracks: ArrayList<Smashy1Tracks>? = arrayListOf(),
)
data class WatchsomuchTorrents(
@JsonProperty("id") val id: Int? = null,
@JsonProperty("movieId") val movieId: Int? = null,
@ -240,11 +230,6 @@ data class CryMoviesResponse(
@JsonProperty("streams") val streams: List<CryMoviesStream>? = null,
)
data class DudetvSources(
@JsonProperty("file") val file: String? = null,
@JsonProperty("title") val title: String? = null,
)
data class FmoviesResponses(
@JsonProperty("result") val result: FmoviesResult? = null,
)

View File

@ -38,6 +38,7 @@ import com.hexated.SoraExtractor.invokeSmashyStream
import com.hexated.SoraExtractor.invokeDumpStream
import com.hexated.SoraExtractor.invokeEmovies
import com.hexated.SoraExtractor.invokeFourCartoon
import com.hexated.SoraExtractor.invokeHdmovies4u
import com.hexated.SoraExtractor.invokeJump1
import com.hexated.SoraExtractor.invokeMoment
import com.hexated.SoraExtractor.invokeMultimovies
@ -50,6 +51,7 @@ import com.hexated.SoraExtractor.invokeUhdmovies
import com.hexated.SoraExtractor.invokeVegamovies
import com.hexated.SoraExtractor.invokeVidsrcto
import com.hexated.SoraExtractor.invokeWatchOnline
import com.hexated.SoraExtractor.invokeWatchflx
import com.hexated.SoraExtractor.invokeWatchsomuch
import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId
import com.lagradost.cloudstream3.LoadResponse.Companion.addTMDbId
@ -134,6 +136,8 @@ open class SoraStream : TmdbProvider() {
const val jump1API = "https://ca.jump1.net"
const val vegaMoviesAPI = "https://vegamovies.im"
const val netflixAPI = "https://m.netflixmirror.com"
const val hdmovies4uAPI = "https://hdmovies4u.name"
const val watchflxAPI = "https://watchflx.tv"
// INDEX SITE
const val dahmerMoviesAPI = "https://edytjedhgmdhm.abfhaqrhbnf.workers.dev"
@ -748,6 +752,19 @@ open class SoraStream : TmdbProvider() {
res.episode,
callback
)
},
{
if (!res.isAnime) invokeHdmovies4u(
res.title,
res.imdbId,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{
if (!res.isAnime) invokeWatchflx(res.id, res.season, res.episode, callback)
}
)

View File

@ -34,6 +34,7 @@ import com.hexated.SoraExtractor.invokePrimewire
import com.hexated.SoraExtractor.invokeVidSrc
import com.hexated.SoraExtractor.invokeVidsrcto
import com.hexated.SoraExtractor.invokeWatchOnline
import com.hexated.SoraExtractor.invokeWatchflx
import com.hexated.SoraExtractor.invokeWatchsomuch
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.argamap
@ -320,6 +321,14 @@ class SoraStreamLite : SoraStream() {
callback
)
},
{
if (!res.isAnime) invokeWatchflx(
res.id,
res.season,
res.episode,
callback
)
},
{
if(!res.isAnime) invoke2embed(
res.imdbId,

View File

@ -21,6 +21,5 @@ class SoraStreamPlugin: Plugin() {
registerExtractorAPI(VCloud())
registerExtractorAPI(Pixeldra())
registerExtractorAPI(Hubcloud())
registerExtractorAPI(HubcloudLol())
}
}

View File

@ -9,10 +9,12 @@ import com.hexated.SoraStream.Companion.crunchyrollAPI
import com.hexated.SoraStream.Companion.filmxyAPI
import com.hexated.SoraStream.Companion.fmoviesAPI
import com.hexated.SoraStream.Companion.gdbot
import com.hexated.SoraStream.Companion.hdmovies4uAPI
import com.hexated.SoraStream.Companion.malsyncAPI
import com.hexated.SoraStream.Companion.smashyStreamAPI
import com.hexated.SoraStream.Companion.tvMoviesAPI
import com.hexated.SoraStream.Companion.watchOnlineAPI
import com.hexated.SoraStream.Companion.watchflxAPI
import com.hexated.SoraStream.Companion.watchhubApi
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getCaptchaToken
@ -43,6 +45,8 @@ import javax.crypto.spec.SecretKeySpec
import kotlin.collections.ArrayList
import kotlin.math.min
var watchflxCookies: Map<String, String>? = null
var filmxyCookies: Map<String,String>? = null
val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=")
const val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
val encodedIndex = arrayOf(
@ -484,158 +488,29 @@ suspend fun invokeSmashyFfix(
}
suspend fun invokeSmashyGtop(
suspend fun invokeSmashyFm(
name: String,
url: String,
callback: (ExtractorLink) -> Unit
) {
val doc = app.get(url).document
val script = doc.selectFirst("script:containsData(var secret)")?.data() ?: return
val secret =
script.substringAfter("secret = \"").substringBefore("\";").let { base64Decode(it) }
val key = script.substringAfter("token = \"").substringBefore("\";")
val source = app.get(
"$secret$key",
headers = mapOf(
"X-Requested-With" to "XMLHttpRequest"
)
).parsedSafe<Smashy1Source>() ?: return
val videoUrl = base64Decode(source.file ?: return)
if (videoUrl.contains("/bug")) return
val quality =
Regex("(\\d{3,4})[Pp]").find(videoUrl)?.groupValues?.getOrNull(1)?.toIntOrNull()
?: Qualities.P720.value
callback.invoke(
ExtractorLink(
"Smashy [$name]",
"Smashy [$name]",
videoUrl,
"",
quality,
videoUrl.contains(".m3u8")
)
)
}
suspend fun invokeSmashyDude(
name: String,
url: String,
callback: (ExtractorLink) -> Unit
) {
val script =
app.get(url).document.selectFirst("script:containsData(player =)")?.data() ?: return
val source = Regex("file:\\s*(\\[.*]),").find(script)?.groupValues?.get(1) ?: return
tryParseJson<ArrayList<DudetvSources>>(source)?.filter { it.title == "English" }?.map {
M3u8Helper.generateM3u8(
"Smashy [Player 2]",
it.file ?: return@map,
""
).forEach(callback)
}
}
suspend fun invokeSmashyRip(
name: String,
url: String,
subtitleCallback: (SubtitleFile) -> Unit,
ref: String,
callback: (ExtractorLink) -> Unit,
) {
val script =
app.get(url).document.selectFirst("script:containsData(player =)")?.data() ?: return
val source = Regex("file:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1)
val subtitle = Regex("subtitle:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1)
source?.split(",")?.map { links ->
val quality = Regex("\\[(\\d+)]").find(links)?.groupValues?.getOrNull(1)?.trim()
val link = links.removePrefix("[$quality]").substringAfter("dev/").trim()
if (link.isEmpty()) return@map
callback.invoke(
ExtractorLink(
"Smashy [$name]",
"Smashy [$name]",
link,
"",
quality?.toIntOrNull() ?: return@map,
isM3u8 = true,
)
)
fun String.removeProxy(): String {
return if (this.contains("proxy")) {
"https${this.substringAfterLast("https")}"
} else {
this
}
}
subtitle?.replace("<br>", "")?.split(",")?.map { sub ->
val lang = Regex("\\[(.*?)]").find(sub)?.groupValues?.getOrNull(1)?.trim()
val link = sub.removePrefix("[$lang]")
subtitleCallback.invoke(
SubtitleFile(
lang.orEmpty().ifEmpty { return@map },
link
)
)
}
}
suspend fun invokeSmashyIm(
name: String,
url: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val script =
app.get(url).document.selectFirst("script:containsData(player =)")?.data() ?: return
val sources =
Regex("['\"]?file['\"]?:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1) ?: return
val subtitles =
Regex("['\"]?subtitle['\"]?:\\s*\"([^\"]+)").find(script)?.groupValues?.get(1) ?: return
val res = app.get(url, referer = ref).text
val source = Regex("['\"]?file['\"]?:\\s*\"([^\"]+)").find(res)?.groupValues?.get(1) ?: return
M3u8Helper.generateM3u8(
"Smashy [$name]",
sources,
""
source.removeProxy(),
"https://vidstream.pro/"
).forEach(callback)
subtitles.split(",").map { sub ->
val lang = Regex("\\[(.*?)]").find(sub)?.groupValues?.getOrNull(1)?.trim()
val trimmedSubLink = sub.removePrefix("[$lang]").trim().substringAfter("?url=")
subtitleCallback.invoke(
SubtitleFile(
lang.takeIf { !it.isNullOrEmpty() } ?: return@map,
trimmedSubLink
)
)
}
}
suspend fun invokeSmashyRw(
name: String,
url: String,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val res = app.get(url).document
val video = res.selectFirst("media-player")?.attr("src")
M3u8Helper.generateM3u8(
"Smashy [$name]",
video ?: return,
""
).forEach(callback)
res.select("track").map { track ->
subtitleCallback.invoke(
SubtitleFile(
track.attr("label"),
track.attr("src"),
)
)
}
}
suspend fun getDumpIdAndType(title: String?, year: Int?, season: Int?): Pair<String?, Int?> {
@ -696,6 +571,52 @@ suspend fun fetchDumpEpisodes(id: String, type: String, episode: Int?): EpisodeV
}
}
suspend fun invokeDrivetot(
url: String,
tags: String? = null,
size: String? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val res = app.get(url)
val data = res.document.select("form input").associate { it.attr("name") to it.attr("value") }
app.post(res.url, data = data, cookies = res.cookies).document.select("div.card-body a").apmap { ele ->
val href = base64Decode(ele.attr("href").substringAfterLast("/")).let {
if(it.contains("hubcloud.lol")) it.replace("hubcloud.lol", "hubcloud.in") else it
}
loadExtractor(href, "$hdmovies4uAPI/", subtitleCallback) { link ->
callback.invoke(
ExtractorLink(
link.source,
"${link.name} $tags [$size]",
link.url,
link.referer,
link.quality,
link.type,
link.headers,
link.extractorData
)
)
}
}
}
suspend fun bypassBqrecipes(url: String): String? {
var res = app.get(url)
var location = res.text.substringAfter(".replace('").substringBefore("');")
var cookies = res.cookies
res = app.get(location, cookies = cookies)
cookies = cookies + res.cookies
val document = res.document
location = document.select("form#recaptcha").attr("action")
val data =
document.select("form#recaptcha input").associate { it.attr("name") to it.attr("value") }
res = app.post(location, data = data, cookies = cookies)
location = res.document.selectFirst("a#messagedown")?.attr("href") ?: return null
cookies = (cookies + res.cookies).minus("var")
return app.get(location, cookies = cookies, allowRedirects = false).headers["location"]
}
suspend fun bypassOuo(url: String?): String? {
var res = session.get(url ?: return null)
run lit@{
@ -915,8 +836,6 @@ suspend fun getTvMoviesServer(url: String, season: Int?, episode: Int?): Pair<St
}.lastOrNull()
}
}
var filmxyCookies: Map<String,String>? = null
suspend fun getFilmxyCookies(url: String) = filmxyCookies ?: fetchFilmxyCookies(url).also { filmxyCookies = it }
suspend fun fetchFilmxyCookies(url: String): Map<String, String> {
@ -957,6 +876,21 @@ suspend fun fetchFilmxyCookies(url: String): Map<String, String> {
return cookieJar.plus(defaultCookies)
}
suspend fun getWatchflxCookies() = watchflxCookies ?: fetchWatchflxCookies().also { watchflxCookies = it }
suspend fun fetchWatchflxCookies(): Map<String, String> {
session.get(watchflxAPI)
val cookies = session.baseClient.cookieJar.loadForRequest(watchflxAPI.toHttpUrl())
.associate { it.name to it.value }
val loginUrl = "$watchflxAPI/cookie-based-login"
session.post(
loginUrl, data = mapOf(
"continue_as_temp" to "true"
), cookies = cookies, headers = mapOf("X-Requested-With" to "XMLHttpRequest")
)
return session.baseClient.cookieJar.loadForRequest(loginUrl.toHttpUrl()).associate { it.name to it.value }
}
fun Document.findTvMoviesIframe(): String? {
return this.selectFirst("script:containsData(var seconds)")?.data()?.substringAfter("href='")
?.substringBefore("'>")