This commit is contained in:
Suraj 2023-07-11 19:07:52 +05:30
commit 8c386159ec
27 changed files with 593 additions and 600 deletions

View file

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

View file

@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
import com.lagradost.nicehttp.RequestBodyTypes import com.lagradost.nicehttp.RequestBodyTypes
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
@ -42,73 +43,54 @@ class Anichi : MainAPI() {
override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie) override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie)
private val popularTitle = "Popular" private val popularTitle = "Popular"
private val recentTitle = "Latest Updated" private val animeRecentTitle = "Latest Anime"
override val mainPage = listOf( private val donghuaRecentTitle = "Latest Donghua"
MainPageData( private val movieTitle = "Movie"
recentTitle,
"""$apiUrl?variables={"search":{"sortBy":"Latest_Update","allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false},"limit":26,"page":%d,"translationType":"sub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" override val mainPage = mainPageOf(
), """$apiUrl?variables={"search":{"sortBy":"Latest_Update","allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false},"limit":26,"page":%d,"translationType":"sub","countryOrigin":"JP"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" to animeRecentTitle,
MainPageData( """$apiUrl?variables={"search":{"sortBy":"Latest_Update","allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false},"limit":26,"page":%d,"translationType":"sub","countryOrigin":"CN"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" to donghuaRecentTitle,
popularTitle, """$apiUrl?variables={"type":"anime","size":30,"dateRange":1,"page":%d,"allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$popularHash"}}""" to popularTitle,
"""$apiUrl?variables={"type":"anime","size":30,"dateRange":1,"page":%d,"allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$popularHash"}}""" """$apiUrl?variables={"search":{"slug":"movie-anime","format":"anime","tagType":"upcoming","name":"Trending Movies"}}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$slugHash"}}""" to movieTitle
)
) )
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
val url = request.data.format(page) val url = request.data.format(page)
val test = app.get(url, headers = headers).text val res = app.get(url, headers = headers).parsedSafe<AnichiQuery>()?.data
val query = res?.shows ?: res?.queryPopular ?: res?.queryListForTag
val home = when (request.name) { val card = if(request.name == popularTitle) query?.recommendations?.map { it.anyCard } else query?.edges
recentTitle -> { val home = card?.filter {
val json = parseJson<AnichiQuery>(test)
val results = json.data.shows.edges.filter {
// filtering in case there is an anime with 0 episodes available on the site. // filtering in case there is an anime with 0 episodes available on the site.
!(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0) !(it?.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
} }?.mapNotNull { media ->
media?.toSearchResponse()
results.map {
newAnimeSearchResponse(it.name ?: "", "${it.Id}", fix = false) {
this.posterUrl = it.thumbnail
this.year = it.airedStart?.year
this.otherName = it.englishName
addDub(it.availableEpisodes?.dub)
addSub(it.availableEpisodes?.sub)
}
}
}
popularTitle -> {
val json = parseJson<PopularQuery>(test)
val results = json.data?.queryPopular?.recommendations?.filter {
// filtering in case there is an anime with 0 episodes available on the site.
!(it.anyCard?.availableEpisodes?.raw == 0 && it.anyCard.availableEpisodes.sub == 0 && it.anyCard.availableEpisodes.dub == 0)
}
results?.mapNotNull {
newAnimeSearchResponse(
it.anyCard?.name ?: return@mapNotNull null,
"${it.anyCard.Id ?: it.pageStatus?.Id}",
fix = false
) {
this.posterUrl = it.anyCard.thumbnail
this.otherName = it.anyCard.englishName
addDub(it.anyCard.availableEpisodes?.dub)
addSub(it.anyCard.availableEpisodes?.sub)
}
} ?: emptyList() } ?: emptyList()
} return newHomePageResponse(
else -> emptyList() list = HomePageList(
} name = request.name,
list = home,
),
hasNext = request.name != movieTitle
return HomePageResponse(
listOf(
HomePageList(request.name, home)
), hasNext = home.isNotEmpty()
) )
} }
override suspend fun search(query: String): List<SearchResponse> { private fun Edges.toSearchResponse(): AnimeSearchResponse? {
return newAnimeSearchResponse(
name ?: englishName ?: nativeName ?: "",
Id ?: return null,
fix = false
) {
this.posterUrl = thumbnail
this.year = airedStart?.year
this.otherName = englishName
addDub(availableEpisodes?.dub)
addSub(availableEpisodes?.sub)
}
}
override suspend fun search(query: String): List<SearchResponse>? {
val link = val link =
"""$apiUrl?variables={"search":{"allowAdult":false,"allowUnknown":false,"query":"$query"},"limit":26,"page":1,"translationType":"sub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" """$apiUrl?variables={"search":{"allowAdult":false,"allowUnknown":false,"query":"$query"},"limit":26,"page":1,"translationType":"sub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}"""
@ -125,12 +107,12 @@ class Anichi : MainAPI() {
val response = parseJson<AnichiQuery>(res) val response = parseJson<AnichiQuery>(res)
val results = response.data.shows.edges.filter { val results = response.data?.shows?.edges?.filter {
// filtering in case there is an anime with 0 episodes available on the site. // filtering in case there is an anime with 0 episodes available on the site.
!(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0) !(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
} }
return results.map { return results?.map {
newAnimeSearchResponse(it.name ?: "", "${it.Id}", fix = false) { newAnimeSearchResponse(it.name ?: "", "${it.Id}", fix = false) {
this.posterUrl = it.thumbnail this.posterUrl = it.thumbnail
this.year = it.airedStart?.year this.year = it.airedStart?.year
@ -161,19 +143,13 @@ class Anichi : MainAPI() {
val poster = showData.thumbnail val poster = showData.thumbnail
val type = getType(showData.type ?: "") val type = getType(showData.type ?: "")
val episodes = showData.availableEpisodes.let { val episodes = showData.availableEpisodesDetail.let {
if (it == null) return@let Pair(null, null) if (it == null) return@let Pair(null, null)
if (showData.Id == null) return@let Pair(null, null) if (showData.Id == null) return@let Pair(null, null)
Pair(
Pair(if (it.sub != 0) ((1..it.sub).map { epNum -> it.getEpisode("sub", showData.Id),
Episode( it.getEpisode("dub", showData.Id),
AnichiLoadData(showData.Id, "sub", epNum).toJson(), episode = epNum
) )
}) else null, if (it.dub != 0) ((1..it.dub).map { epNum ->
Episode(
AnichiLoadData(showData.Id, "dub", epNum).toJson(), episode = epNum
)
}) else null)
} }
val characters = showData.characters?.map { val characters = showData.characters?.map {
@ -191,7 +167,7 @@ class Anichi : MainAPI() {
val names = showData.altNames?.plus(title)?.filterNotNull() ?: emptyList() val names = showData.altNames?.plus(title)?.filterNotNull() ?: emptyList()
val trackers = getTracker(names, TrackerType.getTypes(type), showData.airedStart?.year) val trackers = getTracker(names, TrackerType.getTypes(type), showData.airedStart?.year)
return newAnimeLoadResponse(title ?: "", url, type) { return newAnimeLoadResponse(title ?: "", url, TvType.Anime) {
engName = showData.altNames?.firstOrNull() engName = showData.altNames?.firstOrNull()
posterUrl = trackers?.image ?: poster posterUrl = trackers?.image ?: poster
backgroundPosterUrl = trackers?.cover ?: showData.banner backgroundPosterUrl = trackers?.cover ?: showData.banner
@ -229,7 +205,7 @@ class Anichi : MainAPI() {
apiResponse.data?.episode?.sourceUrls?.apmap { source -> apiResponse.data?.episode?.sourceUrls?.apmap { source ->
safeApiCall { safeApiCall {
val link = source.sourceUrl?.replace(" ", "%20") ?: return@safeApiCall val link = fixSourceUrls(source.sourceUrl ?: return@safeApiCall, source.sourceName) ?: return@safeApiCall
if (URI(link).isAbsolute || link.startsWith("//")) { if (URI(link).isAbsolute || link.startsWith("//")) {
val fixedLink = if (link.startsWith("//")) "https:$link" else link val fixedLink = if (link.startsWith("//")) "https:$link" else link
val host = link.getHost() val host = link.getHost()
@ -258,7 +234,7 @@ class Anichi : MainAPI() {
} }
} }
} else { } else {
val fixedLink = apiEndPoint + URI(link).path + ".json?" + URI(link).query val fixedLink = link.fixUrlPath()
val links = app.get(fixedLink).parsedSafe<AnichiVideoApiResponse>()?.links val links = app.get(fixedLink).parsedSafe<AnichiVideoApiResponse>()?.links
?: emptyList() ?: emptyList()
links.forEach { server -> links.forEach { server ->
@ -293,7 +269,8 @@ class Anichi : MainAPI() {
).path), ).path),
server.resolutionStr.removeSuffix("p").toIntOrNull() server.resolutionStr.removeSuffix("p").toIntOrNull()
?: Qualities.P1080.value, ?: Qualities.P1080.value,
false false,
isDash = server.resolutionStr == "Dash 1"
) )
) )
} }
@ -330,6 +307,19 @@ class Anichi : MainAPI() {
return false return false
} }
private fun AvailableEpisodesDetail.getEpisode(
lang: String,
id: String
): List<com.lagradost.cloudstream3.Episode> {
val meta = if (lang == "sub") this.sub else this.dub
return meta.map { eps ->
Episode(
AnichiLoadData(id, lang, eps).toJson(),
"Ep $eps"
)
}.reversed()
}
private suspend fun getM3u8Qualities( private suspend fun getM3u8Qualities(
m3u8Link: String, m3u8Link: String,
referer: String, referer: String,
@ -380,6 +370,18 @@ class Anichi : MainAPI() {
return fixTitle(URI(this).host.substringBeforeLast(".").substringAfterLast(".")) return fixTitle(URI(this).host.substringBeforeLast(".").substringAfterLast("."))
} }
private fun String.fixUrlPath() : String {
return if(this.contains(".json?")) apiEndPoint + this else apiEndPoint + URI(this).path + ".json?" + URI(this).query
}
private fun fixSourceUrls(url: String, source: String?) : String? {
return if(source == "Ak" || url.contains("/player/vitemb")) {
tryParseJson<AkIframe>(base64Decode(url.substringAfter("=")))?.idUrl
} else {
url.replace(" ", "%20")
}
}
companion object { companion object {
private const val apiUrl = BuildConfig.ANICHI_API private const val apiUrl = BuildConfig.ANICHI_API
private const val serverUrl = BuildConfig.ANICHI_SERVER private const val serverUrl = BuildConfig.ANICHI_SERVER
@ -387,6 +389,7 @@ class Anichi : MainAPI() {
private const val mainHash = "e42a4466d984b2c0a2cecae5dd13aa68867f634b16ee0f17b380047d14482406" private const val mainHash = "e42a4466d984b2c0a2cecae5dd13aa68867f634b16ee0f17b380047d14482406"
private const val popularHash = "31a117653812a2547fd981632e8c99fa8bf8a75c4ef1a77a1567ef1741a7ab9c" private const val popularHash = "31a117653812a2547fd981632e8c99fa8bf8a75c4ef1a77a1567ef1741a7ab9c"
private const val slugHash = "bf603205eb2533ca21d0324a11f623854d62ed838a27e1b3fcfb712ab98b03f4"
private const val detailHash = "bb263f91e5bdd048c1c978f324613aeccdfe2cbc694a419466a31edb58c0cc0b" private const val detailHash = "bb263f91e5bdd048c1c978f324613aeccdfe2cbc694a419466a31edb58c0cc0b"
private const val serverHash = "5e7e17cdd0166af5a2d8f43133d9ce3ce9253d1fdb5160a0cfd515564f98d061" private const val serverHash = "5e7e17cdd0166af5a2d8f43133d9ce3ce9253d1fdb5160a0cfd515564f98d061"
@ -400,7 +403,11 @@ class Anichi : MainAPI() {
data class AnichiLoadData( data class AnichiLoadData(
val hash: String, val hash: String,
val dubStatus: String, val dubStatus: String,
val episode: Int val episode: String
)
data class AkIframe(
@JsonProperty("idUrl") val idUrl: String? = null,
) )
data class Stream( data class Stream(
@ -414,12 +421,19 @@ class Anichi : MainAPI() {
@JsonProperty("streams") val streams: ArrayList<Stream>? = arrayListOf(), @JsonProperty("streams") val streams: ArrayList<Stream>? = arrayListOf(),
) )
data class Subtitles(
@JsonProperty("lang") val lang: String?,
@JsonProperty("label") val label: String?,
@JsonProperty("src") val src: String?,
)
data class Links( data class Links(
@JsonProperty("link") val link: String, @JsonProperty("link") val link: String,
@JsonProperty("hls") val hls: Boolean?, @JsonProperty("hls") val hls: Boolean?,
@JsonProperty("resolutionStr") val resolutionStr: String, @JsonProperty("resolutionStr") val resolutionStr: String,
@JsonProperty("src") val src: String?, @JsonProperty("src") val src: String?,
@JsonProperty("portData") val portData: PortData? = null, @JsonProperty("portData") val portData: PortData? = null,
@JsonProperty("subtitles") val subtitles: ArrayList<Subtitles>? = arrayListOf(),
) )
data class AnichiVideoApiResponse( data class AnichiVideoApiResponse(
@ -427,13 +441,18 @@ class Anichi : MainAPI() {
) )
data class Data( data class Data(
@JsonProperty("shows") val shows: Shows @JsonProperty("shows") val shows: Shows? = null,
@JsonProperty("queryListForTag") val queryListForTag: Shows? = null,
@JsonProperty("queryPopular") val queryPopular: Shows? = null,
) )
data class Shows( data class Shows(
@JsonProperty("pageInfo") val pageInfo: PageInfo, @JsonProperty("edges") val edges: List<Edges>? = arrayListOf(),
@JsonProperty("edges") val edges: List<Edges>, @JsonProperty("recommendations") val recommendations: List<EdgesCard>? = arrayListOf(),
@JsonProperty("__typename") val _typename: String? )
data class EdgesCard(
@JsonProperty("anyCard") val anyCard: Edges? = null,
) )
data class CharacterImage( data class CharacterImage(
@ -493,13 +512,8 @@ class Anichi : MainAPI() {
@JsonProperty("year") val year: Int @JsonProperty("year") val year: Int
) )
data class PageInfo(
@JsonProperty("total") val total: Int?,
@JsonProperty("__typename") val _typename: String?
)
data class AnichiQuery( data class AnichiQuery(
@JsonProperty("data") val data: Data @JsonProperty("data") val data: Data? = null
) )
data class Detail( data class Detail(
@ -537,10 +551,6 @@ class Anichi : MainAPI() {
@JsonProperty("sourceUrls") val sourceUrls: ArrayList<SourceUrls> = arrayListOf(), @JsonProperty("sourceUrls") val sourceUrls: ArrayList<SourceUrls> = arrayListOf(),
) )
data class PopularQuery(
@JsonProperty("data") val data: DataPopular? = DataPopular()
)
data class Sub( data class Sub(
@JsonProperty("hour") val hour: Int? = null, @JsonProperty("hour") val hour: Int? = null,
@JsonProperty("minute") val minute: Int? = null, @JsonProperty("minute") val minute: Int? = null,

View file

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 5 version = 6
cloudstream { cloudstream {

View file

@ -129,7 +129,7 @@ class Gomunimeis : MainAPI() {
.let { id -> .let { id ->
app.get("https://gomunimes.com/stream?id=$id") app.get("https://gomunimes.com/stream?id=$id")
.parsedSafe<Sources>()?.server?.streamsb?.link?.let { link -> .parsedSafe<Sources>()?.server?.streamsb?.link?.let { link ->
loadExtractor(link, "https://vidgomunime.xyz/", subtitleCallback, callback) loadExtractor(link.replace("vidgomunimesb.xyz", "watchsb.com"), mainUrl, subtitleCallback, callback)
} }
} }

View file

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 2 version = 3
cloudstream { cloudstream {

View file

@ -165,14 +165,20 @@ class Minioppai : MainAPI() {
val sources = script.substringAfter("sources:[").substringBefore("]").replace("'", "\"") val sources = script.substringAfter("sources:[").substringBefore("]").replace("'", "\"")
val subtitles = script.substringAfter("\"tracks\":[").substringBefore("]") val subtitles = script.substringAfter("\"tracks\":[").substringBefore("]")
tryParseJson<List<Sources>>("[$sources]")?.map { tryParseJson<List<Sources>>("[$sources]")?.map { source ->
val pStream = fixLink(source.file ?: return@map, paistream).takeIf {
app.get(
it,
referer = "$paistream/"
).isSuccessful
}
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
server, server,
server, server,
fixLink(it.file ?: return@map, if(server == "Stream 1") libPaistream else paistream), pStream ?: fixLink(source.file ?: return@map, libPaistream),
"$paistream/", "$paistream/",
getQualityFromName(it.label) getQualityFromName(source.label)
) )
) )
} }

View file

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 37 version = 36
cloudstream { cloudstream {

View file

@ -12,7 +12,7 @@ import org.jsoup.nodes.Element
import java.net.URI import java.net.URI
class Movierulzhd : MainAPI() { class Movierulzhd : MainAPI() {
override var mainUrl = "https://movierulzhd.help" override var mainUrl = "https://movierulzhd.trade"
private var directUrl = mainUrl private var directUrl = mainUrl
override var name = "Movierulzhd" override var name = "Movierulzhd"
override val hasMainPage = true override val hasMainPage = true

View file

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 2 version = 3
cloudstream { cloudstream {

View file

@ -1,149 +0,0 @@
package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.extractors.DoodLaExtractor
import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.extractors.StreamSB
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import java.net.URI
import javax.crypto.Cipher
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec
class Dooood : DoodLaExtractor() {
override var mainUrl = "https://dooood.com"
}
class Guccihide : Filesim() {
override val name = "Guccihide"
override var mainUrl = "https://guccihide.com"
}
class Ahvsh : Filesim() {
override val name = "Ahvsh"
override var mainUrl = "https://ahvsh.com"
}
class Lvturbo : StreamSB() {
override var name = "Lvturbo"
override var mainUrl = "https://lvturbo.com"
}
class Sbrapid : StreamSB() {
override var name = "Sbrapid"
override var mainUrl = "https://sbrapid.com"
}
class Sbface : StreamSB() {
override var name = "Sbface"
override var mainUrl = "https://sbface.com"
}
class Sbsonic : StreamSB() {
override var name = "Sbsonic"
override var mainUrl = "https://sbsonic.com"
}
object LocalServer {
private const val KEY = "4VqE3#N7zt&HEP^a"
private fun getBaseUrl(url: String): String {
return URI(url).let {
"${it.scheme}://${it.host}"
}
}
suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val mainUrl = getBaseUrl(url)
val master = Regex("MasterJS\\s*=\\s*'([^']+)").find(
app.get(
url,
referer = referer
).text
)?.groupValues?.get(1)
val encData = AppUtils.tryParseJson<AESData>(base64Decode(master ?: return))
val decrypt = cryptoAESHandler(encData ?: return, KEY, false)
val source = Regex(""""?file"?:\s*"([^"]+)""").find(decrypt)?.groupValues?.get(1)
// required
val headers = mapOf(
"Accept" to "*/*",
"Connection" to "keep-alive",
"Sec-Fetch-Dest" to "empty",
"Sec-Fetch-Mode" to "cors",
"Sec-Fetch-Site" to "cross-site",
"Origin" to mainUrl,
)
callback.invoke(
ExtractorLink(
Ngefilm().name,
Ngefilm().name,
source ?: return,
"$mainUrl/",
Qualities.P1080.value,
headers = headers,
isM3u8 = true
)
)
}
private fun cryptoAESHandler(
data: AESData,
pass: String,
encrypt: Boolean = true
): String {
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512")
val spec = PBEKeySpec(
pass.toCharArray(),
data.salt?.hexToByteArray(),
data.iterations?.toIntOrNull() ?: 1,
256
)
val key = factory.generateSecret(spec)
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
return if (!encrypt) {
cipher.init(
Cipher.DECRYPT_MODE,
SecretKeySpec(key.encoded, "AES"),
IvParameterSpec(data.iv?.hexToByteArray())
)
String(cipher.doFinal(base64DecodeArray(data.ciphertext.toString())))
} else {
cipher.init(
Cipher.ENCRYPT_MODE,
SecretKeySpec(key.encoded, "AES"),
IvParameterSpec(data.iv?.hexToByteArray())
)
base64Encode(cipher.doFinal(data.ciphertext?.toByteArray()))
}
}
private fun String.hexToByteArray(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
return chunked(2)
.map { it.toInt(16).toByte() }
.toByteArray()
}
data class AESData(
@JsonProperty("ciphertext") val ciphertext: String? = null,
@JsonProperty("iv") val iv: String? = null,
@JsonProperty("salt") val salt: String? = null,
@JsonProperty("iterations") val iterations: String? = null,
)
}

View file

@ -19,14 +19,6 @@ class Ngefilm : MainAPI() {
TvType.AsianDrama TvType.AsianDrama
) )
companion object {
private val localServer = arrayOf(
"https://bestx.stream",
"https://chillx.top",
"https://watchx.top",
)
}
override val mainPage = mainPageOf( override val mainPage = mainPageOf(
"?s&search=advanced&post_type=movie&index&orderby&genre&movieyear&country&quality=" to "Movies Terbaru", "?s&search=advanced&post_type=movie&index&orderby&genre&movieyear&country&quality=" to "Movies Terbaru",
"?s=&search=advanced&post_type=tv&index=&orderby=&genre=&movieyear=&country=&quality=" to "Series Terbaru", "?s=&search=advanced&post_type=tv&index=&orderby=&genre=&movieyear=&country=&quality=" to "Series Terbaru",
@ -128,12 +120,8 @@ class Ngefilm : MainAPI() {
document.select("ul.muvipro-player-tabs li a").apmap { server -> document.select("ul.muvipro-player-tabs li a").apmap { server ->
val iframe = app.get(fixUrl(server.attr("href"))).document.selectFirst("div.gmr-embed-responsive iframe") val iframe = app.get(fixUrl(server.attr("href"))).document.selectFirst("div.gmr-embed-responsive iframe")
?.attr("src")?.let { fixUrl(it) } ?: return@apmap ?.attr("src")?.let { fixUrl(it) } ?: return@apmap
if (localServer.any { iframe.startsWith(it) }) {
LocalServer.getUrl(iframe, "$mainUrl/", subtitleCallback, callback)
} else {
loadExtractor(iframe, "$mainUrl/", subtitleCallback, callback) loadExtractor(iframe, "$mainUrl/", subtitleCallback, callback)
} }
}
return true return true

View file

@ -10,12 +10,5 @@ class NgefilmPlugin: 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(Ngefilm()) registerMainAPI(Ngefilm())
registerExtractorAPI(Sbsonic())
registerExtractorAPI(Sbface())
registerExtractorAPI(Sbrapid())
registerExtractorAPI(Lvturbo())
registerExtractorAPI(Ahvsh())
registerExtractorAPI(Guccihide())
registerExtractorAPI(Dooood())
} }
} }

View file

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 18 version = 20
cloudstream { cloudstream {
@ -23,5 +23,5 @@ cloudstream {
"OVA", "OVA",
) )
iconUrl = "https://www.google.com/s2/favicons?domain=65.108.132.145&sz=%size%" iconUrl = "https://www.google.com/s2/favicons?domain=oploverz.care&sz=%size%"
} }

View file

@ -1,11 +1,8 @@
package com.hexated package com.hexated
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
import com.lagradost.cloudstream3.extractors.Filesim import com.lagradost.cloudstream3.extractors.Filesim
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import org.jsoup.Jsoup
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
class OploverzProvider : MainAPI() { class OploverzProvider : MainAPI() {
@ -23,159 +20,120 @@ class OploverzProvider : MainAPI() {
companion object { companion object {
const val acefile = "https://acefile.co" const val acefile = "https://acefile.co"
const val lbx = "https://lbx.to"
const val linkbox = "https://www.linkbox.to"
fun getType(t: String): TvType { fun getType(t: String): TvType {
return when { return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA
t.contains("TV") -> TvType.Anime else if (t.contains("Movie", true)) TvType.AnimeMovie
t.contains("Movie") -> TvType.AnimeMovie else TvType.Anime
else -> TvType.OVA
}
} }
fun getStatus(t: String): ShowStatus { fun getStatus(t: String?): ShowStatus {
return when (t) { return when (t) {
"Finished Airing" -> ShowStatus.Completed
"Completed" -> ShowStatus.Completed "Completed" -> ShowStatus.Completed
"Currently Airing" -> ShowStatus.Ongoing
"Ongoing" -> ShowStatus.Ongoing "Ongoing" -> ShowStatus.Ongoing
else -> ShowStatus.Completed else -> ShowStatus.Completed
} }
} }
} }
override val mainPage = mainPageOf( override val mainPage = mainPageOf(
"&status=&type=&order=update" to "Episode Terbaru", "update" to "Latest Update",
"&status=&type=&order=latest" to "Anime Terbaru", "latest" to "Latest Added",
"&sub=&order=popular" to "Popular Anime", "popular" to "Popular Anime",
"rating" to "Top Rated",
) )
override suspend fun getMainPage( override suspend fun getMainPage(
page: Int, page: Int,
request: MainPageRequest request: MainPageRequest
): HomePageResponse { ): HomePageResponse {
val document = app.get("$mainUrl/anime/?page=$page${request.data}").document val document = app.get("$mainUrl/anime-list/page/$page/?title&order=${request.data}&status&type").document
val home = document.select("article[itemscope=itemscope]").mapNotNull { val home = document.select("div.relat > article").mapNotNull {
it.toSearchResult() it.toSearchResult()
} }
return newHomePageResponse(request.name, home) return newHomePageResponse(request.name, home)
} }
private fun getProperAnimeLink(uri: String): String { private fun getProperAnimeLink(uri: String): String {
return if (uri.contains("/anime/")) { return if (uri.contains("/anime/")) {
uri uri
} else { } else {
var title = uri.substringAfter("$mainUrl/") var title = uri.substringAfter("$mainUrl/")
title = when { title = when {
(title.contains("-episode")) && !(title.contains("-ova")) -> Regex("(.+)-episode").find( (title.contains("-episode")) && !(title.contains("-movie")) -> Regex("(.+)-episode").find(
title title
)?.groupValues?.get(1).toString() )?.groupValues?.get(1).toString()
(title.contains("-ova")) -> Regex("(.+)-ova").find(title)?.groupValues?.get(1) (title.contains("-movie")) -> Regex("(.+)-movie").find(title)?.groupValues?.get(
.toString() 1
(title.contains("-movie")) -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1) ).toString()
.toString() else -> title
else -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1).toString()
.replace(Regex("-\\d+"), "")
} }
when {
title.contains("overlord") -> {
title = title.replace("s", "season-")
}
title.contains("kaguya-sama") -> {
title = title.replace("s3", "ultra-romantic")
}
}
"$mainUrl/anime/$title" "$mainUrl/anime/$title"
} }
} }
private fun Element.toSearchResult(): AnimeSearchResponse? { private fun Element.toSearchResult(): AnimeSearchResponse? {
val href = getProperAnimeLink(this.selectFirst("a.tip")!!.attr("href")) val title = this.selectFirst("div.title")?.text()?.trim() ?: return null
val title = this.selectFirst("h2[itemprop=headline]")?.text()?.trim() ?: return null val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href"))
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src")) val posterUrl = this.select("img[itemprop=image]").attr("src").toString()
val type = getType(this.selectFirst(".eggtype, .typez")?.text()?.trim().toString()) val type = getType(this.select("div.type").text().trim())
val epNum =
this.selectFirst("span.episode")?.ownText()?.replace(Regex("\\D"), "")?.trim()
?.toIntOrNull()
return newAnimeSearchResponse(title, href, type) { return newAnimeSearchResponse(title, href, type) {
this.posterUrl = posterUrl this.posterUrl = posterUrl
addSub(epNum)
} }
} }
override suspend fun search(query: String): List<SearchResponse> { override suspend fun search(query: String): List<SearchResponse> {
val link = "$mainUrl/?s=$query" val anime = mutableListOf<SearchResponse>()
(1..2).forEach { page ->
val link = "$mainUrl/page/$page/?s=$query"
val document = app.get(link).document val document = app.get(link).document
val media = document.select(".site-main.relat > article").mapNotNull {
return document.select("article[itemscope=itemscope]").map { val title = it.selectFirst("div.title > h2")!!.ownText().trim()
val title = it.selectFirst(".tt")?.ownText()?.trim().toString() val href = it.selectFirst("a")!!.attr("href")
val poster = fixUrlNull(it.selectFirst("img")?.attr("src")) val posterUrl = it.selectFirst("img")!!.attr("src").toString()
val tvType = getType(it.selectFirst(".typez")?.text().toString()) val type = getType(it.select("div.type").text().trim())
val href = fixUrl(it.selectFirst("a.tip")!!.attr("href")) newAnimeSearchResponse(title, href, type) {
this.posterUrl = posterUrl
newAnimeSearchResponse(title, href, tvType) {
this.posterUrl = poster
addDubStatus(dubExist = false, subExist = true)
} }
} }
if(media.isNotEmpty()) anime.addAll(media)
}
return anime
} }
override suspend fun load(url: String): LoadResponse { override suspend fun load(url: String): LoadResponse {
val document = app.get(url).document val document = app.get(url).document
val title = document.selectFirst("h1.entry-title")!!.text().trim() val title = document.selectFirst("h1.entry-title")?.text()
val poster = document.select(".thumb > img").attr("src") ?.replace("Subtitle Indonesia", "")?.trim() ?: ""
val tags = document.select(".genxed > a").map { it.text() } val type = document.selectFirst("div.alternati span.type")?.text() ?: ""
val year = Regex("\\d, (\\d*)").find( val episodes = document.select("div.lstepsiode.listeps ul li").mapNotNull {
document.selectFirst(".info-content > .spe > span > time")!!.text().trim() val header = it.selectFirst("a") ?: return@mapNotNull null
)?.groupValues?.get(1).toString().toIntOrNull() val episode = header.text().trim().toIntOrNull()
val status = getStatus( val link = fixUrl(header.attr("href"))
document.select(".info-content > .spe > span:nth-child(1)") Episode(link, header.text(), episode = episode)
.text().trim().replace("Status: ", "")
)
val typeCheck =
when (document.select(".info-content > .spe > span:nth-child(5), .info-content > .spe > span")
.text().trim()) {
"OVA" -> "OVA"
"Movie" -> "Movie"
else -> "TV"
}
val description = document.select(".entry-content > p").text().trim()
val trailer = document.selectFirst("a.trailerbutton")?.attr("href")
val episodes = document.select(".eplister > ul > li").map {
val link = fixUrl(it.select("a").attr("href"))
val name = it.select(".epl-title").text()
val episode = Regex("Episode\\s?(\\d+[.,]?\\d*)").find(name)?.groupValues?.getOrNull(1)?.toIntOrNull()
Episode(link, name, episode = episode)
}.reversed() }.reversed()
val recommendations = return newAnimeLoadResponse(title, url, getType(type)) {
document.select(".listupd > article[itemscope=itemscope]").mapNotNull { rec -> posterUrl = document.selectFirst("div.thumb > img")?.attr("src")
val epTitle = rec.selectFirst(".tt")!!.ownText().trim() this.year = document.selectFirst("div.alternati a")?.text()?.filter { it.isDigit() }?.toIntOrNull()
val epPoster = rec.selectFirst("img")!!.attr("src")
val epType = getType(rec.selectFirst(".typez")?.text().toString())
val epHref = fixUrl(rec.selectFirst("a.tip")!!.attr("href"))
newAnimeSearchResponse(epTitle, epHref, epType) {
this.posterUrl = epPoster
addDubStatus(dubExist = false, subExist = true)
}
}
return newAnimeLoadResponse(title, url, getType(typeCheck)) {
engName = title
posterUrl = poster
this.year = year
addEpisodes(DubStatus.Subbed, episodes) addEpisodes(DubStatus.Subbed, episodes)
showStatus = status showStatus =
plot = description getStatus(
this.tags = tags document.selectFirst("div.alternati span:nth-child(2)")?.text()?.trim()
this.recommendations = recommendations )
addTrailer(trailer) plot = document.selectFirst("div.entry-content > p")?.text()?.trim()
this.tags =
document.select("div.genre-info a").map { it.text() }
} }
} }
override suspend fun loadLinks( override suspend fun loadLinks(
@ -184,41 +142,59 @@ class OploverzProvider : MainAPI() {
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
val document = app.get(data).document
val sources = mutableListOf<Pair<String?, String>>()
val streamingSources = document.select(".mobius > .mirror > option").mapNotNull {
"" to fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src"))
}
if (streamingSources.isNotEmpty()) sources.addAll(streamingSources)
val downloadSources =
document.select("div.mctnx div.soraurlx").mapNotNull { item ->
item.select("a").map { item.select("strong").text() to it.attr("href") }
}.flatten()
if (downloadSources.isNotEmpty()) sources.addAll(downloadSources)
sources.filter { it.second.startsWith("https") }. val document = app.get(data).document
apmap { (quality, source) ->
val video = fixedIframe(source) argamap(
val videoQuality = getQualityFromName(quality) {
if(video.endsWith(".mp4") || video.endsWith(".mkv")) { document.select("div#server ul li div").apmap {
callback.invoke( val dataPost = it.attr("data-post")
ExtractorLink( val dataNume = it.attr("data-nume")
"Direct", val dataType = it.attr("data-type")
"Direct",
video, val iframe = app.post(
"", url = "$mainUrl/wp-admin/admin-ajax.php",
videoQuality data = mapOf(
"action" to "player_ajax",
"post" to dataPost,
"nume" to dataNume,
"type" to dataType
),
referer = data,
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
).document.select("iframe").attr("src")
loadExtractor(fixedIframe(iframe), "$mainUrl/", subtitleCallback, callback)
}
},
{
document.select("div#download tr").map { el ->
el.select("a").apmap {
loadFixedExtractor(fixedIframe(it.attr("href")), el.select("strong").text(), "$mainUrl/", subtitleCallback, callback)
}
}
}
) )
)
} else { return true
loadExtractor(video, data, subtitleCallback) { link -> }
private suspend fun loadFixedExtractor(
url: String,
name: String,
referer: String? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
loadExtractor(url, referer, subtitleCallback) { link ->
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
link.name, link.name,
link.name, link.name,
link.url, link.url,
link.referer, link.referer,
videoQuality, name.fixQuality(),
link.isM3u8, link.isM3u8,
link.headers, link.headers,
link.extractorData link.extractorData
@ -226,64 +202,21 @@ class OploverzProvider : MainAPI() {
) )
} }
} }
private fun String.fixQuality() : Int {
return when(this) {
"MP4HD" -> Qualities.P720.value
"FULLHD" -> Qualities.P1080.value
else -> Regex("(\\d{3,4})p").find(this)?.groupValues?.get(1)?.toIntOrNull() ?: Qualities.Unknown.value
}
} }
return true private fun fixedIframe(url: String): String {
}
private suspend fun fixedIframe(url: String): String {
val id = Regex("""(?:/f/|/file/)(\w+)""").find(url)?.groupValues?.getOrNull(1) val id = Regex("""(?:/f/|/file/)(\w+)""").find(url)?.groupValues?.getOrNull(1)
return when { return when {
url.startsWith(acefile) -> "$acefile/player/$id" url.startsWith(acefile) -> "${acefile}/player/$id"
url.startsWith(lbx) -> { else -> fixUrl(url)
val itemId = app.get("$linkbox/api/file/share_out_list/?sortField=utime&sortAsc=0&pageNo=1&pageSize=50&shareToken=$id&scene=singleItem&needTpInfo=1&lan=en").parsedSafe<Responses>()?.data?.itemId
"$linkbox/a/f/$itemId"
} }
else -> url
}
}
data class RList(
@JsonProperty("url") val url: String,
@JsonProperty("resolution") val resolution: String?,
)
data class Data(
@JsonProperty("rList") val rList: List<RList>? = arrayListOf(),
@JsonProperty("itemId") val itemId: String? = null,
)
data class Responses(
@JsonProperty("data") val data: Data?,
)
}
class Streamhide : Filesim() {
override val mainUrl = "https://streamhide.to"
override val name = "Streamhide"
}
open class Pixeldrain : ExtractorApi() {
override val name = "Pixeldrain"
override val mainUrl = "https://pixeldrain.com"
override val requiresReferer = false
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val mId = Regex("/([ul]/[\\da-zA-Z\\-]+)").find(url)?.groupValues?.get(1)?.split("/")
callback.invoke(
ExtractorLink(
this.name,
this.name,
"$mainUrl/api/file/${mId?.last() ?: return}?download",
url,
Qualities.Unknown.value,
)
)
} }
} }

View file

@ -10,7 +10,5 @@ class OploverzProviderPlugin: 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(OploverzProvider()) registerMainAPI(OploverzProvider())
registerExtractorAPI(Streamhide())
registerExtractorAPI(Pixeldrain())
} }
} }

View file

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 4 version = 5
cloudstream { cloudstream {

View file

@ -9,7 +9,7 @@ import org.jsoup.nodes.Element
import java.net.URLDecoder import java.net.URLDecoder
class PhimmoichillProvider : MainAPI() { class PhimmoichillProvider : MainAPI() {
override var mainUrl = "https://phimmoichilla.net" override var mainUrl = "https://phimmoichilld.net"
override var name = "Phimmoichill" override var name = "Phimmoichill"
override val hasMainPage = true override val hasMainPage = true
override var lang = "vi" override var lang = "vi"
@ -26,9 +26,11 @@ class PhimmoichillProvider : MainAPI() {
"$mainUrl/list/phim-le/page-" to "Phim Lẻ", "$mainUrl/list/phim-le/page-" to "Phim Lẻ",
"$mainUrl/list/phim-bo/page-" to "Phim Bộ", "$mainUrl/list/phim-bo/page-" to "Phim Bộ",
"$mainUrl/genre/phim-hoat-hinh/page-" to "Phim Hoạt Hình", "$mainUrl/genre/phim-hoat-hinh/page-" to "Phim Hoạt Hình",
"$mainUrl/genre/phim-anime/page-" to "Phim Anime",
"$mainUrl/country/phim-han-quoc/page-" to "Phim Hàn Quốc", "$mainUrl/country/phim-han-quoc/page-" to "Phim Hàn Quốc",
"$mainUrl/country/phim-trung-quoc/page-" to "Phim Trung Quốc", "$mainUrl/country/phim-trung-quoc/page-" to "Phim Trung Quốc",
"$mainUrl/country/phim-thai-lan/page-" to "Phim Thái Lan", "$mainUrl/country/phim-thai-lan/page-" to "Phim Thái Lan",
"$mainUrl/genre/phim-sap-chieu/page-" to "Phim Sắp Chiếu",
) )
override suspend fun getMainPage( override suspend fun getMainPage(
@ -64,6 +66,10 @@ class PhimmoichillProvider : MainAPI() {
this.posterUrl = posterUrl this.posterUrl = posterUrl
addSub(episode) addSub(episode)
} }
} else if (temp.contains(Regex("Trailer"))) {
newMovieSearchResponse(title, href, TvType.Movie) {
this.posterUrl = posterUrl
}
} else { } else {
val quality = val quality =
temp.replace(Regex("(-.*)|(\\|.*)|(?i)(VietSub.*)|(?i)(Thuyết.*)"), "").trim() temp.replace(Regex("(-.*)|(\\|.*)|(?i)(VietSub.*)|(?i)(Thuyết.*)"), "").trim()
@ -83,21 +89,20 @@ class PhimmoichillProvider : MainAPI() {
} }
} }
override suspend fun load(url: String): LoadResponse { override suspend fun load( url: String ): LoadResponse {
val document = app.get(url).document val document = app.get(url).document
val title = document.selectFirst("h1[itemprop=name]")?.text()?.trim().toString() val title = document.selectFirst("h1[itemprop=name]")?.text()?.trim().toString()
val link = document.select("ul.list-button li:last-child a").attr("href") val link = document.select("ul.list-button li:last-child a").attr("href")
val poster = document.selectFirst("div.image img[itemprop=image]")?.attr("src") val poster = document.selectFirst("div.image img[itemprop=image]")?.attr("src")
val tags = document.select("ul.entry-meta.block-film li:nth-child(4) a").map { it.text() } val tags = document.select("ul.entry-meta.block-film li:nth-child(4) a").map { it.text()!!.substringAfter("Phim") }
val year = document.select("ul.entry-meta.block-film li:nth-child(2) a").text().trim() val year = document.select("ul.entry-meta.block-film li:nth-child(2) a").text().trim()
.toIntOrNull() .toIntOrNull()
val tvType = if (document.select("div.latest-episode").isNotEmpty() val tvType = if (document.select("div.latest-episode").isNotEmpty()
) TvType.TvSeries else TvType.Movie ) TvType.TvSeries else TvType.Movie
val description = document.select("div#film-content").text().trim() val description = document.select("div#film-content").text().substringAfter("Full HD Vietsub Thuyết Minh").substringBefore("@phimmoi").trim()
val trailer = val trailer = document.select("body script")
document.select("div#trailer script").last()?.data()?.substringAfter("file: \"") .find { it.data().contains("youtube.com") }?.data()?.substringAfterLast("file: \"")?.substringBefore("\",")
?.substringBefore("\",")
val rating = val rating =
document.select("ul.entry-meta.block-film li:nth-child(7) span").text().toRatingInt() document.select("ul.entry-meta.block-film li:nth-child(7) span").text().toRatingInt()
val actors = document.select("ul.entry-meta.block-film li:last-child a").map { it.text() } val actors = document.select("ul.entry-meta.block-film li:last-child a").map { it.text() }
@ -156,8 +161,7 @@ class PhimmoichillProvider : MainAPI() {
.find { it.data().contains("filmInfo.episodeID =") }?.data()?.let { script -> .find { it.data().contains("filmInfo.episodeID =") }?.data()?.let { script ->
val id = script.substringAfter("filmInfo.episodeID = parseInt('") val id = script.substringAfter("filmInfo.episodeID = parseInt('")
app.post( app.post(
// Not mainUrl url = "${this.mainUrl}/chillsplayer.php",
url = "https://phimmoichills.net/pmplayer.php",
data = mapOf("qcao" to id, "sv" to "0"), data = mapOf("qcao" to id, "sv" to "0"),
referer = data, referer = data,
headers = mapOf( headers = mapOf(
@ -171,6 +175,7 @@ class PhimmoichillProvider : MainAPI() {
listOf( listOf(
Pair("https://so-trym.topphimmoi.org/raw/$key/index.m3u8", "PMFAST"), Pair("https://so-trym.topphimmoi.org/raw/$key/index.m3u8", "PMFAST"),
Pair("https://dash.megacdn.xyz/raw/$key/index.m3u8", "PMHLS"), Pair("https://dash.megacdn.xyz/raw/$key/index.m3u8", "PMHLS"),
Pair("https://so-trym.phimchill.net/dash/$key/index.m3u8", "PMPRO"),
Pair("https://dash.megacdn.xyz/dast/$key/index.m3u8", "PMBK") Pair("https://dash.megacdn.xyz/dast/$key/index.m3u8", "PMBK")
).apmap { (link, source) -> ).apmap { (link, source) ->
safeApiCall { safeApiCall {

View file

@ -1,5 +1,5 @@
// use an integer for version numbers // use an integer for version numbers
version = 10 version = 11
cloudstream { cloudstream {

View file

@ -1,43 +0,0 @@
package com.hexated
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.extractors.XStreamCdn
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import java.net.URI
class Suzihaza: XStreamCdn() {
override val name: String = "Suzihaza"
override val mainUrl: String = "https://suzihaza.com"
}
open class Wibufile : ExtractorApi() {
override val name: String = "Wibufile"
override val mainUrl: String = "https://wibufile.com"
override val requiresReferer = false
override suspend fun getUrl(
url: String,
referer: String?,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
val res = app.get(url).text
val video = Regex("src: ['\"](.*?)['\"]").find(res)?.groupValues?.get(1)
callback.invoke(
ExtractorLink(
name,
name,
video ?: return,
"$mainUrl/",
Qualities.Unknown.value,
URI(url).path.endsWith(".m3u8")
)
)
}
}

View file

@ -10,7 +10,5 @@ class SamehadakuPlugin: 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(Samehadaku()) registerMainAPI(Samehadaku())
registerExtractorAPI(Suzihaza())
registerExtractorAPI(Wibufile())
} }
} }

View file

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

View file

@ -831,31 +831,36 @@ object SoraExtractor : SoraStream() {
title: String? = null, title: String? = null,
season: Int? = null, season: Int? = null,
episode: Int? = null, episode: Int? = null,
isAnime: Boolean = false,
lastSeason: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
) { ) {
val fixTitle = title?.replace("", "-") val slug = title.createSlug() ?: return
val type = when {
isAnime -> "3"
season == null -> "2"
else -> "1"
}
val res = app.get( val res = app.get(
"$kissKhAPI/api/DramaList/Search?q=$title&type=0", referer = "$kissKhAPI/" "$kissKhAPI/api/DramaList/Search?q=$title&type=$type", referer = "$kissKhAPI/"
).text.let { ).text.let {
tryParseJson<ArrayList<KisskhResults>>(it) tryParseJson<ArrayList<KisskhResults>>(it)
} ?: return } ?: return
val (id, contentTitle) = if (res.size == 1) { val (id, contentTitle) = if (res.size == 1) {
res.first().id to res.first().title res.first().id to res.first().title
} else {
if (season == null) {
val data = res.find { it.title.equals(fixTitle, true) }
data?.id to data?.title
} else { } else {
val data = res.find { val data = res.find {
it.title?.contains( val slugTitle = it.title.createSlug()
"$fixTitle", true when {
) == true && it.title.contains("Season $season", true) season == null -> slugTitle?.equals(slug) == true
lastSeason == 1 -> slugTitle?.contains(slug) == true
else -> slugTitle?.contains(slug) == true && it.title?.contains("Season $season", true) == true
}
} }
data?.id to data?.title data?.id to data?.title
} }
}
val resDetail = app.get( val resDetail = app.get(
"$kissKhAPI/api/DramaList/Drama/$id?isq=false", referer = "$kissKhAPI/Drama/${ "$kissKhAPI/api/DramaList/Drama/$id?isq=false", referer = "$kissKhAPI/Drama/${
@ -955,7 +960,7 @@ object SoraExtractor : SoraStream() {
) { ) {
val res = app.get( val res = app.get(
"$biliBiliAPI/anime/episodes?id=${aniId ?: return}&source_id=bilibili", "$biliBiliAPI/anime/episodes?id=${aniId ?: return}&source_id=bilibili",
referer = kaguyaBaseUrl referer = otakuzBaseUrl
) )
.parsedSafe<BiliBiliDetails>()?.episodes?.find { .parsedSafe<BiliBiliDetails>()?.episodes?.find {
it.episodeNumber == episode it.episodeNumber == episode
@ -964,7 +969,7 @@ object SoraExtractor : SoraStream() {
val sources = val sources =
app.get( app.get(
"$biliBiliAPI/source?episode_id=${res.sourceEpisodeId}&source_media_id=${res.sourceMediaId}&source_id=${res.sourceId}", "$biliBiliAPI/source?episode_id=${res.sourceEpisodeId}&source_media_id=${res.sourceMediaId}&source_id=${res.sourceId}",
referer = kaguyaBaseUrl referer = otakuzBaseUrl
) )
.parsedSafe<BiliBiliSourcesResponse>() .parsedSafe<BiliBiliSourcesResponse>()
@ -972,7 +977,7 @@ object SoraExtractor : SoraStream() {
val quality = val quality =
app.get( app.get(
source.file ?: return@apmap null, source.file ?: return@apmap null,
referer = kaguyaBaseUrl referer = otakuzBaseUrl
).document.selectFirst("Representation") ).document.selectFirst("Representation")
?.attr("height") ?.attr("height")
callback.invoke( callback.invoke(
@ -980,7 +985,7 @@ object SoraExtractor : SoraStream() {
"BiliBili", "BiliBili",
"BiliBili", "BiliBili",
source.file, source.file,
kaguyaBaseUrl, "",
quality?.toIntOrNull() ?: Qualities.Unknown.value, quality?.toIntOrNull() ?: Qualities.Unknown.value,
isDash = true isDash = true
) )
@ -1008,16 +1013,18 @@ object SoraExtractor : SoraStream() {
val animeId = val animeId =
app.get("https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/anilist/anime/${aniId ?: return}.json") app.get("https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/anilist/anime/${aniId ?: return}.json")
.parsedSafe<MALSyncResponses>()?.pages?.zoro?.keys?.map { it } .parsedSafe<MALSyncResponses>()?.pages?.zoro?.keys?.map { it }
val headers = mapOf(
"X-Requested-With" to "XMLHttpRequest",
)
animeId?.apmap { id -> animeId?.apmap { id ->
val episodeId = app.get("$zoroAPI/ajax/v2/episode/list/${id ?: return@apmap}") val episodeId = app.get("$zoroAPI/ajax/episode/list/${id ?: return@apmap}", headers = headers)
.parsedSafe<ZoroResponses>()?.html?.let { .parsedSafe<ZoroResponses>()?.html?.let {
Jsoup.parse(it) Jsoup.parse(it)
}?.select("div.ss-list a")?.find { it.attr("data-number") == "${episode ?: 1}" } }?.select("div.ss-list a")?.find { it.attr("data-number") == "${episode ?: 1}" }
?.attr("data-id") ?.attr("data-id")
val servers = val servers =
app.get("$zoroAPI/ajax/v2/episode/servers?episodeId=${episodeId ?: return@apmap}") app.get("$zoroAPI/ajax/episode/servers?episodeId=${episodeId ?: return@apmap}", headers = headers)
.parsedSafe<ZoroResponses>()?.html?.let { Jsoup.parse(it) } .parsedSafe<ZoroResponses>()?.html?.let { Jsoup.parse(it) }
?.select("div.item.server-item")?.map { ?.select("div.item.server-item")?.map {
Triple( Triple(
@ -1029,10 +1036,10 @@ object SoraExtractor : SoraStream() {
servers?.apmap servers@{ server -> servers?.apmap servers@{ server ->
val iframe = val iframe =
app.get("$zoroAPI/ajax/v2/episode/sources?id=${server.second ?: return@servers}") app.get("$zoroAPI/ajax/episode/sources?id=${server.second ?: return@servers}", headers = headers)
.parsedSafe<ZoroResponses>()?.link ?: return@servers .parsedSafe<ZoroResponses>()?.link ?: return@servers
val audio = if (server.third == "sub") "Raw" else "English Dub" val audio = if (server.third == "sub") "Raw" else "English Dub"
if (server.first == "Vidstreaming" || server.first == "Vidcloud") { if (server.first.contains(Regex("Vidstreaming|MegaCloud|Vidcloud"))) {
extractRabbitStream( extractRabbitStream(
"${server.first} [$audio]", "${server.first} [$audio]",
iframe, iframe,
@ -1255,20 +1262,13 @@ object SoraExtractor : SoraStream() {
extractMirrorUHD(bitLink, base) extractMirrorUHD(bitLink, base)
} }
val tags = val tags = getUhdTags(quality)
Regex("\\d{3,4}[Pp]\\.?(.*?)\\[").find(quality)?.groupValues?.getOrNull(1) val qualities = getIndexQuality(quality)
?.replace(".", " ")?.trim() val size = getIndexSize(quality)
?: ""
val qualities =
Regex("(\\d{3,4})[Pp]").find(quality)?.groupValues?.getOrNull(1)?.toIntOrNull()
?: Qualities.Unknown.value
val size =
Regex("(?i)\\[(\\S+\\s?(gb|mb))[]/]").find(quality)?.groupValues?.getOrNull(1)
?.let { "[$it]" } ?: quality
callback.invoke( callback.invoke(
ExtractorLink( ExtractorLink(
"UHDMovies", "UHDMovies",
"UHDMovies $tags $size", "UHDMovies $tags [$size]",
downloadLink ?: return@apmap, downloadLink ?: return@apmap,
"", "",
qualities qualities
@ -1280,6 +1280,72 @@ object SoraExtractor : SoraStream() {
} }
suspend fun invokePobmovies(
title: String? = null,
year: Int? = null,
callback: (ExtractorLink) -> Unit
) {
val detailDoc = app.get("$pobmoviesAPI/${title.createSlug()}-$year").document
val iframeList = detailDoc.select("div.entry-content p").map { it }
.filter { it.text().filterIframe(year = year, title = title) }.mapNotNull {
it.text() to it.nextElementSibling()?.select("a")?.attr("href")
}.filter { it.second?.contains(Regex("(https:)|(http:)")) == true }
val sources = mutableListOf<Pair<String, String?>>()
if (iframeList.any {
it.first.contains(
"2160p",
true
)
}) {
sources.addAll(iframeList.filter {
it.first.contains(
"2160p",
true
)
})
sources.add(iframeList.first {
it.first.contains(
"1080p",
true
)
})
} else {
sources.addAll(iframeList.filter { it.first.contains("1080p", true) })
}
sources.apmap { (name, link) ->
if (link.isNullOrEmpty()) return@apmap
val videoLink = when {
link.contains("gdtot") -> {
val gdBotLink = extractGdbot(link)
extractGdflix(gdBotLink ?: return@apmap)
}
link.contains("gdflix") -> {
extractGdflix(link)
}
else -> {
return@apmap
}
}
val tags = getUhdTags(name)
val qualities = getIndexQuality(name)
val size = getIndexSize(name)
callback.invoke(
ExtractorLink(
"Pobmovies",
"Pobmovies $tags [${size}]",
videoLink ?: return@apmap,
"",
qualities
)
)
}
}
suspend fun invokeFwatayako( suspend fun invokeFwatayako(
imdbId: String? = null, imdbId: String? = null,
season: Int? = null, season: Int? = null,
@ -3033,6 +3099,118 @@ object SoraExtractor : SoraStream() {
} }
suspend fun invokeEmovies(
title: String? = null,
year: Int? = null,
season: Int? = null,
episode: Int? = null,
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit,
) {
val slug = title.createSlug()
val url = if (season == null) {
"$emoviesAPI/watch-$slug-$year-1080p-hd-online-free/watching.html"
} else {
val first = "$emoviesAPI/watch-$slug-season-$season-$year-1080p-hd-online-free.html"
val second = "$emoviesAPI/watch-$slug-$year-1080p-hd-online-free.html"
if (app.get(first).isSuccessful) first else second
}
val res = app.get(url).document
val id = (if (season == null) {
res.selectFirst("select#selectServer option[sv=oserver]")?.attr("value")
} else {
res.select("div.le-server a").find {
val num =
Regex("Episode (\\d+)").find(it.text())?.groupValues?.get(1)?.toIntOrNull()
num == episode
}?.attr("href")
})?.substringAfter("id=")?.substringBefore("&")
val server =
app.get(
"$emoviesAPI/ajax/v4_get_sources?s=oserver&id=${id ?: return}&_=${APIHolder.unixTimeMS}",
headers = mapOf(
"X-Requested-With" to "XMLHttpRequest"
)
).parsedSafe<EMovieServer>()?.value
val script = app.get(server ?: return, referer = "$emoviesAPI/").document.selectFirst("script:containsData(sources:)")?.data() ?: return
val sources = Regex("sources:\\s*\\[(.*)],").find(script)?.groupValues?.get(1)?.let {
tryParseJson<List<EMovieSources>>("[$it]")
}
val tracks = Regex("tracks:\\s*\\[(.*)],").find(script)?.groupValues?.get(1)?.let {
tryParseJson<List<EMovieTraks>>("[$it]")
}
sources?.map { source ->
M3u8Helper.generateM3u8(
"Emovies",
source.file ?: return@map,
"https://embed.vodstream.xyz/"
).forEach(callback)
}
tracks?.map { track ->
subtitleCallback.invoke(
SubtitleFile(
track.label ?: "",
track.file ?: return@map,
)
)
}
}
suspend fun invokeFourCartoon(
title: String? = null,
year: Int? = null,
season: Int? = null,
episode: Int? = null,
callback: (ExtractorLink) -> Unit
) {
val fixTitle = title.createSlug()
val headers = mapOf(
"X-Requested-With" to "XMLHttpRequest"
)
val url = if (season == null) {
"$fourCartoonAPI/movies/$fixTitle-$year"
} else {
"$fourCartoonAPI/episode/$fixTitle-season-$season-episode-$episode"
}
val document = app.get(url).document
val id = document.selectFirst("input[name=idpost]")?.attr("value")
val server = app.get(
"$fourCartoonAPI/ajax-get-link-stream/?server=streamango&filmId=${id ?: return}",
headers = headers
).text
val hash =
getAndUnpack(app.get(server, referer = fourCartoonAPI).text).substringAfter("(\"")
.substringBefore("\",")
val iframeUrl = getBaseUrl(server)
val source = app.post(
"$iframeUrl/player/index.php?data=$hash&do=getVideo", data = mapOf(
"hast" to hash,
"r" to "$fourCartoonAPI/",
),
headers = headers
).parsedSafe<FourCartoonSources>()?.videoSource
callback.invoke(
ExtractorLink(
"4Cartoon",
"4Cartoon",
source ?: return,
"$iframeUrl/",
Qualities.P720.value,
true,
)
)
}
} }

View file

@ -487,3 +487,20 @@ data class EpisodeVo(
data class DumpMediaDetail( data class DumpMediaDetail(
@JsonProperty("episodeVo") val episodeVo: ArrayList<EpisodeVo>? = arrayListOf(), @JsonProperty("episodeVo") val episodeVo: ArrayList<EpisodeVo>? = arrayListOf(),
) )
data class EMovieServer(
@JsonProperty("value") val value: String? = null,
)
data class EMovieSources(
@JsonProperty("file") val file: String? = null,
)
data class EMovieTraks(
@JsonProperty("file") val file: String? = null,
@JsonProperty("label") val label: String? = null,
)
data class FourCartoonSources(
@JsonProperty("videoSource") val videoSource: String? = null,
)

View file

@ -49,6 +49,9 @@ import com.hexated.SoraExtractor.invokeShinobiMovies
import com.hexated.SoraExtractor.invokeShivamhw import com.hexated.SoraExtractor.invokeShivamhw
import com.hexated.SoraExtractor.invokeSmashyStream import com.hexated.SoraExtractor.invokeSmashyStream
import com.hexated.SoraExtractor.invokeDumpStream import com.hexated.SoraExtractor.invokeDumpStream
import com.hexated.SoraExtractor.invokeEmovies
import com.hexated.SoraExtractor.invokeFourCartoon
import com.hexated.SoraExtractor.invokePobmovies
import com.hexated.SoraExtractor.invokeTvMovies import com.hexated.SoraExtractor.invokeTvMovies
import com.hexated.SoraExtractor.invokeUhdmovies import com.hexated.SoraExtractor.invokeUhdmovies
import com.hexated.SoraExtractor.invokeVitoenMovies import com.hexated.SoraExtractor.invokeVitoenMovies
@ -97,11 +100,11 @@ open class SoraStream : TmdbProvider() {
const val filmxyAPI = "https://www.filmxy.vip" const val filmxyAPI = "https://www.filmxy.vip"
const val kimcartoonAPI = "https://kimcartoon.li" const val kimcartoonAPI = "https://kimcartoon.li"
const val xMovieAPI = "https://xemovies.to" const val xMovieAPI = "https://xemovies.to"
const val zoroAPI = "https://sanji.to" const val zoroAPI = "https://kaido.to"
const val crunchyrollAPI = "https://beta-api.crunchyroll.com" const val crunchyrollAPI = "https://beta-api.crunchyroll.com"
const val kissKhAPI = "https://kisskh.co" const val kissKhAPI = "https://kisskh.co"
const val lingAPI = "https://ling-online.net" const val lingAPI = "https://ling-online.net"
const val uhdmoviesAPI = "https://uhdmovies.cc" const val uhdmoviesAPI = "https://uhdmovies.life"
const val fwatayakoAPI = "https://5100.svetacdn.in" const val fwatayakoAPI = "https://5100.svetacdn.in"
const val gMoviesAPI = "https://gdrivemovies.xyz" const val gMoviesAPI = "https://gdrivemovies.xyz"
const val fdMoviesAPI = "https://freedrivemovie.lol" const val fdMoviesAPI = "https://freedrivemovie.lol"
@ -118,7 +121,7 @@ open class SoraStream : TmdbProvider() {
const val watchSomuchAPI = "https://watchsomuch.tv" // sub only const val watchSomuchAPI = "https://watchsomuch.tv" // sub only
val gomoviesAPI = base64DecodeAPI("bQ==Y28=ZS4=aW4=bmw=LW8=ZXM=dmk=bW8=Z28=Ly8=czo=dHA=aHQ=") val gomoviesAPI = base64DecodeAPI("bQ==Y28=ZS4=aW4=bmw=LW8=ZXM=dmk=bW8=Z28=Ly8=czo=dHA=aHQ=")
const val ask4MoviesAPI = "https://ask4movie.net" const val ask4MoviesAPI = "https://ask4movie.net"
const val biliBiliAPI = "https://api-vn.kaguya.app/server" const val biliBiliAPI = "https://api-vn.otakuz.live/server"
const val watchOnlineAPI = "https://watchonline.ag" const val watchOnlineAPI = "https://watchonline.ag"
const val nineTvAPI = "https://api.9animetv.live" const val nineTvAPI = "https://api.9animetv.live"
const val putlockerAPI = "https://ww7.putlocker.vip" const val putlockerAPI = "https://ww7.putlocker.vip"
@ -127,6 +130,9 @@ open class SoraStream : TmdbProvider() {
const val gokuAPI = "https://goku.sx" const val gokuAPI = "https://goku.sx"
const val ridomoviesAPI = "https://ridomovies.pw" const val ridomoviesAPI = "https://ridomovies.pw"
const val navyAPI = "https://navy-issue-i-239.site" const val navyAPI = "https://navy-issue-i-239.site"
const val emoviesAPI = "https://emovies.si"
const val pobmoviesAPI = "https://pobmovies.cam"
const val fourCartoonAPI = "https://4cartoon.net"
// INDEX SITE // INDEX SITE
const val blackMoviesAPI = "https://dl.blacklistedbois.workers.dev/0:" const val blackMoviesAPI = "https://dl.blacklistedbois.workers.dev/0:"
@ -261,8 +267,7 @@ open class SoraStream : TmdbProvider() {
val year = releaseDate?.split("-")?.first()?.toIntOrNull() val year = releaseDate?.split("-")?.first()?.toIntOrNull()
val rating = res.vote_average.toString().toRatingInt() val rating = res.vote_average.toString().toRatingInt()
val genres = res.genres?.mapNotNull { it.name } val genres = res.genres?.mapNotNull { it.name }
val isAnime = val isAnime = genres?.contains("Animation") == true && (res.original_language == "zh" || res.original_language == "ja")
genres?.contains("Animation") == true && (res.original_language == "zh" || res.original_language == "ja")
val keywords = res.keywords?.results?.mapNotNull { it.name }.orEmpty() val keywords = res.keywords?.results?.mapNotNull { it.name }.orEmpty()
.ifEmpty { res.keywords?.keywords?.mapNotNull { it.name } } .ifEmpty { res.keywords?.keywords?.mapNotNull { it.name } }
@ -302,7 +307,7 @@ open class SoraStream : TmdbProvider() {
epsTitle = eps.name, epsTitle = eps.name,
jpTitle = res.alternative_titles?.results?.find { it.iso_3166_1 == "JP" }?.title, jpTitle = res.alternative_titles?.results?.find { it.iso_3166_1 == "JP" }?.title,
date = season.airDate, date = season.airDate,
airedDate = res.releaseDate ?: res.firstAirDate airedDate = res.releaseDate ?: res.firstAirDate,
).toJson(), ).toJson(),
name = eps.name + if(isUpcoming(eps.airDate)) " - [UPCOMING]" else "", name = eps.name + if(isUpcoming(eps.airDate)) " - [UPCOMING]" else "",
season = eps.seasonNumber, season = eps.seasonNumber,
@ -346,7 +351,7 @@ open class SoraStream : TmdbProvider() {
orgTitle = orgTitle, orgTitle = orgTitle,
isAnime = isAnime, isAnime = isAnime,
jpTitle = res.alternative_titles?.results?.find { it.iso_3166_1 == "JP" }?.title, jpTitle = res.alternative_titles?.results?.find { it.iso_3166_1 == "JP" }?.title,
airedDate = res.releaseDate ?: res.firstAirDate airedDate = res.releaseDate ?: res.firstAirDate,
).toJson(), ).toJson(),
) { ) {
this.posterUrl = poster this.posterUrl = poster
@ -493,18 +498,18 @@ open class SoraStream : TmdbProvider() {
) )
}, },
{ {
invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback) if(!res.isAnime) invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback)
},
{
invokeXmovies(
res.title,
res.year,
res.season,
res.episode,
subtitleCallback,
callback
)
}, },
// {
// invokeXmovies(
// res.title,
// res.year,
// res.season,
// res.episode,
// subtitleCallback,
// callback
// )
// },
{ {
if (!res.isAnime) invokeFmovies( if (!res.isAnime) invokeFmovies(
res.title, res.title,
@ -516,7 +521,7 @@ open class SoraStream : TmdbProvider() {
) )
}, },
{ {
invokeKisskh(res.title, res.season, res.episode, subtitleCallback, callback) invokeKisskh(res.title, res.season, res.episode, res.isAnime, res.lastSeason, subtitleCallback, callback)
}, },
{ {
invokeLing( invokeLing(
@ -803,6 +808,15 @@ open class SoraStream : TmdbProvider() {
{ {
invokeNavy(res.imdbId, res.season, res.episode, callback) invokeNavy(res.imdbId, res.season, res.episode, callback)
}, },
{
if (!res.isAnime) invokeEmovies(res.title, res.year, res.season, res.episode, subtitleCallback, callback)
},
{
if(!res.isAnime && res.season == null) invokePobmovies(res.title, res.year, callback)
},
{
if(!res.isAnime) invokeFourCartoon(res.title, res.year, res.season, res.episode, callback)
}
) )
return true return true

View file

@ -27,6 +27,8 @@ import com.hexated.SoraExtractor.invokeRidomovies
import com.hexated.SoraExtractor.invokeSeries9 import com.hexated.SoraExtractor.invokeSeries9
import com.hexated.SoraExtractor.invokeSmashyStream import com.hexated.SoraExtractor.invokeSmashyStream
import com.hexated.SoraExtractor.invokeDumpStream import com.hexated.SoraExtractor.invokeDumpStream
import com.hexated.SoraExtractor.invokeEmovies
import com.hexated.SoraExtractor.invokeFourCartoon
import com.hexated.SoraExtractor.invokeVidSrc import com.hexated.SoraExtractor.invokeVidSrc
import com.hexated.SoraExtractor.invokeWatchOnline import com.hexated.SoraExtractor.invokeWatchOnline
import com.hexated.SoraExtractor.invokeWatchsomuch import com.hexated.SoraExtractor.invokeWatchsomuch
@ -186,7 +188,7 @@ class SoraStreamLite : SoraStream() {
) )
}, },
{ {
invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback) if(!res.isAnime) invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback)
}, },
{ {
invokeSmashyStream( invokeSmashyStream(
@ -198,16 +200,16 @@ class SoraStreamLite : SoraStream() {
callback callback
) )
}, },
{ // {
invokeXmovies( // invokeXmovies(
res.title, // res.title,
res.year, // res.year,
res.season, // res.season,
res.episode, // res.episode,
subtitleCallback, // subtitleCallback,
callback // callback
) // )
}, // },
{ {
if (!res.isAnime) invokeFmovies( if (!res.isAnime) invokeFmovies(
res.title, res.title,
@ -219,7 +221,7 @@ class SoraStreamLite : SoraStream() {
) )
}, },
{ {
invokeKisskh(res.title, res.season, res.episode, subtitleCallback, callback) invokeKisskh(res.title, res.season, res.episode, res.isAnime, res.lastSeason, subtitleCallback, callback)
}, },
{ {
invokeLing( invokeLing(
@ -287,6 +289,25 @@ class SoraStreamLite : SoraStream() {
res.year, res.year,
callback callback
) )
},
{
if (!res.isAnime) invokeEmovies(
res.title,
res.year,
res.season,
res.episode,
subtitleCallback,
callback
)
},
{
if(!res.isAnime) invokeFourCartoon(
res.title,
res.year,
res.season,
res.episode,
callback
)
} }
) )

View file

@ -2,7 +2,6 @@ package com.hexated
import android.util.Base64 import android.util.Base64
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.hexated.DumpUtils.createHeaders
import com.hexated.DumpUtils.queryApi import com.hexated.DumpUtils.queryApi
import com.hexated.SoraStream.Companion.anilistAPI import com.hexated.SoraStream.Companion.anilistAPI
import com.hexated.SoraStream.Companion.base64DecodeAPI import com.hexated.SoraStream.Companion.base64DecodeAPI
@ -51,7 +50,7 @@ import kotlin.math.min
val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=") val bflixChipperKey = base64DecodeAPI("Yjc=ejM=TzA=YTk=WHE=WnU=bXU=RFo=")
const val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" const val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
const val kaguyaBaseUrl = "https://kaguya.app/" const val otakuzBaseUrl = "https://otakuz.live/"
val soraHeaders = mapOf( val soraHeaders = mapOf(
"lang" to "en", "lang" to "en",
"versioncode" to "33", "versioncode" to "33",
@ -111,7 +110,12 @@ data class FilmxyCookies(
val wSec: String? = null, val wSec: String? = null,
) )
fun String.filterIframe(seasonNum: Int?, lastSeason: Int?, year: Int?, title: String?): Boolean { fun String.filterIframe(
seasonNum: Int? = null,
lastSeason: Int? = null,
year: Int?,
title: String?
): Boolean {
val slug = title.createSlug() val slug = title.createSlug()
val dotSlug = slug?.replace("-", ".") val dotSlug = slug?.replace("-", ".")
val spaceSlug = slug?.replace("-", " ") val spaceSlug = slug?.replace("-", " ")
@ -271,8 +275,8 @@ suspend fun extractDrivebot(url: String): String? {
suspend fun extractGdflix(url: String): String? { suspend fun extractGdflix(url: String): String? {
val iframeGdflix = val iframeGdflix =
app.get(url).document.selectFirst("li.flex.flex-col.py-6 a:contains(GDFlix Direct)") if (!url.contains("gdflix")) app.get(url).document.selectFirst("li.flex.flex-col.py-6 a:contains(GDFlix Direct)")
?.attr("href") ?: return null ?.attr("href") ?: return null else url
val base = getBaseUrl(iframeGdflix) val base = getBaseUrl(iframeGdflix)
val req = app.get(iframeGdflix).document.selectFirst("script:containsData(replace)")?.data() val req = app.get(iframeGdflix).document.selectFirst("script:containsData(replace)")?.data()
@ -586,7 +590,10 @@ suspend fun getDumpIdAndType(title: String?, year: Int?, season: Int?): Pair<Str
it.name?.contains( it.name?.contains(
"$title", "$title",
true true
) == true && (it.releaseTime == "$year" || it.name.contains("Season $season", true)) && it.domainType == 1 ) == true && (it.releaseTime == "$year" || it.name.contains(
"Season $season",
true
)) && it.domainType == 1
} }
else -> { else -> {
it.name?.contains(Regex("(?i)$title\\s?($season|${season.toRomanNumeral()}|Season\\s$season)")) == true && it.releaseTime == "$year" && it.domainType == 1 it.name?.contains(Regex("(?i)$title\\s?($season|${season.toRomanNumeral()}|Season\\s$season)")) == true && it.releaseTime == "$year" && it.domainType == 1
@ -744,7 +751,11 @@ suspend fun bypassTechmny(url: String): String? {
val thirdPage = secondPage.getNextTechPage().text val thirdPage = secondPage.getNextTechPage().text
val goToken = thirdPage.substringAfter("?go=").substringBefore("\"") val goToken = thirdPage.substringAfter("?go=").substringBefore("\"")
val tokenUrl = "$postUrl?go=$goToken" val tokenUrl = "$postUrl?go=$goToken"
val headers = mapOf("Cookie" to "$goToken=${secondPage.select("form#landing input[name=_wp_http2]").attr("value")}") val headers = mapOf(
"Cookie" to "$goToken=${
secondPage.select("form#landing input[name=_wp_http2]").attr("value")
}"
)
Pair(tokenUrl, headers) Pair(tokenUrl, headers)
} }
val driveUrl = val driveUrl =
@ -1279,6 +1290,12 @@ fun String.getFileSize(): Float? {
} }
} }
fun getUhdTags(str: String?): String {
return Regex("\\d{3,4}[Pp]\\.?(.*?)\\[").find(str ?: "")?.groupValues?.getOrNull(1)
?.replace(".", " ")?.trim()
?: str ?: ""
}
fun getIndexQualityTags(str: String?, fullTag: Boolean = false): String { fun getIndexQualityTags(str: String?, fullTag: Boolean = false): String {
return if (fullTag) Regex("(?i)(.*)\\.(?:mkv|mp4|avi)").find(str ?: "")?.groupValues?.get(1) return if (fullTag) Regex("(?i)(.*)\\.(?:mkv|mp4|avi)").find(str ?: "")?.groupValues?.get(1)
?.trim() ?: str ?: "" else Regex("(?i)\\d{3,4}[pP]\\.?(.*?)\\.(mkv|mp4|avi)").find( ?.trim() ?: str ?: "" else Regex("(?i)\\d{3,4}[pP]\\.?(.*?)\\.(mkv|mp4|avi)").find(
@ -1486,12 +1503,13 @@ fun getBaseUrl(url: String): String {
} }
} }
fun isUpcoming(dateString: String?) : Boolean { fun isUpcoming(dateString: String?): Boolean {
if(dateString == null) return false if (dateString == null) return false
val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val dateTime = format.parse(dateString)?.time ?: return false val dateTime = format.parse(dateString)?.time ?: return false
return unixTimeMS < dateTime return unixTimeMS < dateTime
} }
fun decode(input: String): String = URLDecoder.decode(input, "utf-8") fun decode(input: String): String = URLDecoder.decode(input, "utf-8")
fun encode(input: String): String = URLEncoder.encode(input, "utf-8").replace("+", "%20") fun encode(input: String): String = URLEncoder.encode(input, "utf-8").replace("+", "%20")
@ -1761,12 +1779,16 @@ object RabbitStream {
ioSafe { app.get("$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}") } ioSafe { app.get("$extractorData&t=${generateTimeStamp()}&sid=${pollingData.sid}") }
} }
} }
val getSourcesUrl = "${ val mainIframeAjax = mainIframeUrl.let {
mainIframeUrl.replace( if(it.contains("/embed-2/e-1")) it.replace(
"/embed-2/e-1",
"/embed-2/ajax/e-1"
) else it.replace(
"/embed", "/embed",
"/ajax/embed" "/ajax/embed"
) )
}/getSources?id=$mainIframeId${sid?.let { "$&sId=$it" } ?: ""}" }
val getSourcesUrl = "$mainIframeAjax/getSources?id=$mainIframeId${sid?.let { "$&sId=$it" } ?: ""}"
val response = app.get( val response = app.get(
getSourcesUrl, getSourcesUrl,
referer = mainUrl, referer = mainUrl,
@ -1929,7 +1951,7 @@ object RabbitStream {
} }
suspend fun getZoroKey(): String { suspend fun getZoroKey(): String {
return app.get("https://raw.githubusercontent.com/enimax-anime/key/e6/key.txt").text return app.get("https://raw.githubusercontent.com/enimax-anime/key/e0/key.txt").text
} }
private inline fun <reified T> decryptMapped(input: String, key: String): T? { private inline fun <reified T> decryptMapped(input: String, key: String): T? {
@ -2021,8 +2043,9 @@ object DumpUtils {
return app.custom( return app.custom(
method, method,
url, url,
requestBody = if(method == "POST") params.toJson().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) else null, requestBody = if (method == "POST") params.toJson()
params = if(method == "GET") params else emptyMap(), .toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) else null,
params = if (method == "GET") params else emptyMap(),
headers = createHeaders(params) headers = createHeaders(params)
).parsedSafe<HashMap<String, String>>()?.get("data").let { ).parsedSafe<HashMap<String, String>>()?.get("data").let {
cryptoHandler( cryptoHandler(
@ -2062,7 +2085,8 @@ object DumpUtils {
} }
private fun getAesKey(): String? { private fun getAesKey(): String? {
val publicKey = RSAEncryptionHelper.getPublicKeyFromString(BuildConfig.DUMP_KEY) ?: return null val publicKey =
RSAEncryptionHelper.getPublicKeyFromString(BuildConfig.DUMP_KEY) ?: return null
return RSAEncryptionHelper.encryptText(deviceId, publicKey) return RSAEncryptionHelper.encryptText(deviceId, publicKey)
} }

View file

@ -6,7 +6,7 @@ cloudstream {
language = "en" language = "en"
// All of these properties are optional, you can safely remove them // All of these properties are optional, you can safely remove them
description = "- StremioX allows you to use stream addons \n- StremioC allows you to use catalog addons \n<!> Requires Setup" description = "[!] Requires Setup \n- StremioX allows you to use stream addons \n- StremioC allows you to use catalog addons"
authors = listOf("Hexated") authors = listOf("Hexated")
/** /**