mirror of
https://github.com/hexated/cloudstream-extensions-hexated.git
synced 2024-08-15 00:03:22 +00:00
Merge branch 'master' of https://github.com/geekboysuraj/cloudstream-extensions-hexated
This commit is contained in:
commit
8c386159ec
27 changed files with 593 additions and 600 deletions
|
@ -1,7 +1,7 @@
|
|||
import org.jetbrains.kotlin.konan.properties.Properties
|
||||
|
||||
// use an integer for version numbers
|
||||
version = 2
|
||||
version = 4
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.mvvm.safeApiCall
|
|||
import com.lagradost.cloudstream3.utils.*
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.nicehttp.RequestBodyTypes
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
|
@ -42,73 +43,54 @@ class Anichi : MainAPI() {
|
|||
override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie)
|
||||
|
||||
private val popularTitle = "Popular"
|
||||
private val recentTitle = "Latest Updated"
|
||||
override val mainPage = listOf(
|
||||
MainPageData(
|
||||
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"}}"""
|
||||
),
|
||||
MainPageData(
|
||||
popularTitle,
|
||||
"""$apiUrl?variables={"type":"anime","size":30,"dateRange":1,"page":%d,"allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$popularHash"}}"""
|
||||
)
|
||||
private val animeRecentTitle = "Latest Anime"
|
||||
private val donghuaRecentTitle = "Latest Donghua"
|
||||
private val movieTitle = "Movie"
|
||||
|
||||
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,
|
||||
"""$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,
|
||||
"""$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={"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 {
|
||||
|
||||
val url = request.data.format(page)
|
||||
val test = app.get(url, headers = headers).text
|
||||
|
||||
val home = when (request.name) {
|
||||
recentTitle -> {
|
||||
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.
|
||||
!(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
|
||||
|
||||
return HomePageResponse(
|
||||
listOf(
|
||||
HomePageList(request.name, home)
|
||||
), hasNext = home.isNotEmpty()
|
||||
val res = app.get(url, headers = headers).parsedSafe<AnichiQuery>()?.data
|
||||
val query = res?.shows ?: res?.queryPopular ?: res?.queryListForTag
|
||||
val card = if(request.name == popularTitle) query?.recommendations?.map { it.anyCard } else query?.edges
|
||||
val home = card?.filter {
|
||||
// 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)
|
||||
}?.mapNotNull { media ->
|
||||
media?.toSearchResponse()
|
||||
} ?: emptyList()
|
||||
return newHomePageResponse(
|
||||
list = HomePageList(
|
||||
name = request.name,
|
||||
list = home,
|
||||
),
|
||||
hasNext = request.name != movieTitle
|
||||
)
|
||||
}
|
||||
|
||||
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 =
|
||||
"""$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 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.
|
||||
!(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) {
|
||||
this.posterUrl = it.thumbnail
|
||||
this.year = it.airedStart?.year
|
||||
|
@ -161,19 +143,13 @@ class Anichi : MainAPI() {
|
|||
val poster = showData.thumbnail
|
||||
val type = getType(showData.type ?: "")
|
||||
|
||||
val episodes = showData.availableEpisodes.let {
|
||||
val episodes = showData.availableEpisodesDetail.let {
|
||||
if (it == null) return@let Pair(null, null)
|
||||
if (showData.Id == null) return@let Pair(null, null)
|
||||
|
||||
Pair(if (it.sub != 0) ((1..it.sub).map { epNum ->
|
||||
Episode(
|
||||
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)
|
||||
Pair(
|
||||
it.getEpisode("sub", showData.Id),
|
||||
it.getEpisode("dub", showData.Id),
|
||||
)
|
||||
}
|
||||
|
||||
val characters = showData.characters?.map {
|
||||
|
@ -191,7 +167,7 @@ class Anichi : MainAPI() {
|
|||
val names = showData.altNames?.plus(title)?.filterNotNull() ?: emptyList()
|
||||
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()
|
||||
posterUrl = trackers?.image ?: poster
|
||||
backgroundPosterUrl = trackers?.cover ?: showData.banner
|
||||
|
@ -229,7 +205,7 @@ class Anichi : MainAPI() {
|
|||
|
||||
apiResponse.data?.episode?.sourceUrls?.apmap { source ->
|
||||
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("//")) {
|
||||
val fixedLink = if (link.startsWith("//")) "https:$link" else link
|
||||
val host = link.getHost()
|
||||
|
@ -258,7 +234,7 @@ class Anichi : MainAPI() {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
val fixedLink = apiEndPoint + URI(link).path + ".json?" + URI(link).query
|
||||
val fixedLink = link.fixUrlPath()
|
||||
val links = app.get(fixedLink).parsedSafe<AnichiVideoApiResponse>()?.links
|
||||
?: emptyList()
|
||||
links.forEach { server ->
|
||||
|
@ -293,7 +269,8 @@ class Anichi : MainAPI() {
|
|||
).path),
|
||||
server.resolutionStr.removeSuffix("p").toIntOrNull()
|
||||
?: Qualities.P1080.value,
|
||||
false
|
||||
false,
|
||||
isDash = server.resolutionStr == "Dash 1"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -330,6 +307,19 @@ class Anichi : MainAPI() {
|
|||
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(
|
||||
m3u8Link: String,
|
||||
referer: String,
|
||||
|
@ -380,6 +370,18 @@ class Anichi : MainAPI() {
|
|||
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 {
|
||||
private const val apiUrl = BuildConfig.ANICHI_API
|
||||
private const val serverUrl = BuildConfig.ANICHI_SERVER
|
||||
|
@ -387,6 +389,7 @@ class Anichi : MainAPI() {
|
|||
|
||||
private const val mainHash = "e42a4466d984b2c0a2cecae5dd13aa68867f634b16ee0f17b380047d14482406"
|
||||
private const val popularHash = "31a117653812a2547fd981632e8c99fa8bf8a75c4ef1a77a1567ef1741a7ab9c"
|
||||
private const val slugHash = "bf603205eb2533ca21d0324a11f623854d62ed838a27e1b3fcfb712ab98b03f4"
|
||||
private const val detailHash = "bb263f91e5bdd048c1c978f324613aeccdfe2cbc694a419466a31edb58c0cc0b"
|
||||
private const val serverHash = "5e7e17cdd0166af5a2d8f43133d9ce3ce9253d1fdb5160a0cfd515564f98d061"
|
||||
|
||||
|
@ -400,7 +403,11 @@ class Anichi : MainAPI() {
|
|||
data class AnichiLoadData(
|
||||
val hash: String,
|
||||
val dubStatus: String,
|
||||
val episode: Int
|
||||
val episode: String
|
||||
)
|
||||
|
||||
data class AkIframe(
|
||||
@JsonProperty("idUrl") val idUrl: String? = null,
|
||||
)
|
||||
|
||||
data class Stream(
|
||||
|
@ -414,12 +421,19 @@ class Anichi : MainAPI() {
|
|||
@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(
|
||||
@JsonProperty("link") val link: String,
|
||||
@JsonProperty("hls") val hls: Boolean?,
|
||||
@JsonProperty("resolutionStr") val resolutionStr: String,
|
||||
@JsonProperty("src") val src: String?,
|
||||
@JsonProperty("portData") val portData: PortData? = null,
|
||||
@JsonProperty("subtitles") val subtitles: ArrayList<Subtitles>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class AnichiVideoApiResponse(
|
||||
|
@ -427,13 +441,18 @@ class Anichi : MainAPI() {
|
|||
)
|
||||
|
||||
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(
|
||||
@JsonProperty("pageInfo") val pageInfo: PageInfo,
|
||||
@JsonProperty("edges") val edges: List<Edges>,
|
||||
@JsonProperty("__typename") val _typename: String?
|
||||
@JsonProperty("edges") val edges: List<Edges>? = arrayListOf(),
|
||||
@JsonProperty("recommendations") val recommendations: List<EdgesCard>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class EdgesCard(
|
||||
@JsonProperty("anyCard") val anyCard: Edges? = null,
|
||||
)
|
||||
|
||||
data class CharacterImage(
|
||||
|
@ -493,13 +512,8 @@ class Anichi : MainAPI() {
|
|||
@JsonProperty("year") val year: Int
|
||||
)
|
||||
|
||||
data class PageInfo(
|
||||
@JsonProperty("total") val total: Int?,
|
||||
@JsonProperty("__typename") val _typename: String?
|
||||
)
|
||||
|
||||
data class AnichiQuery(
|
||||
@JsonProperty("data") val data: Data
|
||||
@JsonProperty("data") val data: Data? = null
|
||||
)
|
||||
|
||||
data class Detail(
|
||||
|
@ -537,10 +551,6 @@ class Anichi : MainAPI() {
|
|||
@JsonProperty("sourceUrls") val sourceUrls: ArrayList<SourceUrls> = arrayListOf(),
|
||||
)
|
||||
|
||||
data class PopularQuery(
|
||||
@JsonProperty("data") val data: DataPopular? = DataPopular()
|
||||
)
|
||||
|
||||
data class Sub(
|
||||
@JsonProperty("hour") val hour: Int? = null,
|
||||
@JsonProperty("minute") val minute: Int? = null,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// use an integer for version numbers
|
||||
version = 5
|
||||
version = 6
|
||||
|
||||
|
||||
cloudstream {
|
||||
|
|
|
@ -129,7 +129,7 @@ class Gomunimeis : MainAPI() {
|
|||
.let { id ->
|
||||
app.get("https://gomunimes.com/stream?id=$id")
|
||||
.parsedSafe<Sources>()?.server?.streamsb?.link?.let { link ->
|
||||
loadExtractor(link, "https://vidgomunime.xyz/", subtitleCallback, callback)
|
||||
loadExtractor(link.replace("vidgomunimesb.xyz", "watchsb.com"), mainUrl, subtitleCallback, callback)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// use an integer for version numbers
|
||||
version = 2
|
||||
version = 3
|
||||
|
||||
|
||||
cloudstream {
|
||||
|
|
|
@ -165,14 +165,20 @@ class Minioppai : MainAPI() {
|
|||
val sources = script.substringAfter("sources:[").substringBefore("]").replace("'", "\"")
|
||||
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(
|
||||
ExtractorLink(
|
||||
server,
|
||||
server,
|
||||
fixLink(it.file ?: return@map, if(server == "Stream 1") libPaistream else paistream),
|
||||
pStream ?: fixLink(source.file ?: return@map, libPaistream),
|
||||
"$paistream/",
|
||||
getQualityFromName(it.label)
|
||||
getQualityFromName(source.label)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// use an integer for version numbers
|
||||
version = 37
|
||||
version = 36
|
||||
|
||||
|
||||
cloudstream {
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.jsoup.nodes.Element
|
|||
import java.net.URI
|
||||
|
||||
class Movierulzhd : MainAPI() {
|
||||
override var mainUrl = "https://movierulzhd.help"
|
||||
override var mainUrl = "https://movierulzhd.trade"
|
||||
private var directUrl = mainUrl
|
||||
override var name = "Movierulzhd"
|
||||
override val hasMainPage = true
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// use an integer for version numbers
|
||||
version = 2
|
||||
version = 3
|
||||
|
||||
|
||||
cloudstream {
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
||||
}
|
|
@ -19,14 +19,6 @@ class Ngefilm : MainAPI() {
|
|||
TvType.AsianDrama
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val localServer = arrayOf(
|
||||
"https://bestx.stream",
|
||||
"https://chillx.top",
|
||||
"https://watchx.top",
|
||||
)
|
||||
}
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"?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",
|
||||
|
@ -128,11 +120,7 @@ class Ngefilm : MainAPI() {
|
|||
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")
|
||||
?.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
|
||||
|
|
|
@ -10,12 +10,5 @@ class NgefilmPlugin: Plugin() {
|
|||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Ngefilm())
|
||||
registerExtractorAPI(Sbsonic())
|
||||
registerExtractorAPI(Sbface())
|
||||
registerExtractorAPI(Sbrapid())
|
||||
registerExtractorAPI(Lvturbo())
|
||||
registerExtractorAPI(Ahvsh())
|
||||
registerExtractorAPI(Guccihide())
|
||||
registerExtractorAPI(Dooood())
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// use an integer for version numbers
|
||||
version = 18
|
||||
version = 20
|
||||
|
||||
|
||||
cloudstream {
|
||||
|
@ -23,5 +23,5 @@ cloudstream {
|
|||
"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%"
|
||||
}
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.extractors.Filesim
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class OploverzProvider : MainAPI() {
|
||||
|
@ -23,159 +20,120 @@ class OploverzProvider : MainAPI() {
|
|||
|
||||
companion object {
|
||||
const val acefile = "https://acefile.co"
|
||||
const val lbx = "https://lbx.to"
|
||||
const val linkbox = "https://www.linkbox.to"
|
||||
|
||||
fun getType(t: String): TvType {
|
||||
return when {
|
||||
t.contains("TV") -> TvType.Anime
|
||||
t.contains("Movie") -> TvType.AnimeMovie
|
||||
else -> TvType.OVA
|
||||
}
|
||||
return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA
|
||||
else if (t.contains("Movie", true)) TvType.AnimeMovie
|
||||
else TvType.Anime
|
||||
}
|
||||
|
||||
fun getStatus(t: String): ShowStatus {
|
||||
fun getStatus(t: String?): ShowStatus {
|
||||
return when (t) {
|
||||
"Finished Airing" -> ShowStatus.Completed
|
||||
"Completed" -> ShowStatus.Completed
|
||||
"Currently Airing" -> ShowStatus.Ongoing
|
||||
"Ongoing" -> ShowStatus.Ongoing
|
||||
else -> ShowStatus.Completed
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"&status=&type=&order=update" to "Episode Terbaru",
|
||||
"&status=&type=&order=latest" to "Anime Terbaru",
|
||||
"&sub=&order=popular" to "Popular Anime",
|
||||
"update" to "Latest Update",
|
||||
"latest" to "Latest Added",
|
||||
"popular" to "Popular Anime",
|
||||
"rating" to "Top Rated",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val document = app.get("$mainUrl/anime/?page=$page${request.data}").document
|
||||
val home = document.select("article[itemscope=itemscope]").mapNotNull {
|
||||
val document = app.get("$mainUrl/anime-list/page/$page/?title&order=${request.data}&status&type").document
|
||||
val home = document.select("div.relat > article").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun getProperAnimeLink(uri: String): String {
|
||||
|
||||
return if (uri.contains("/anime/")) {
|
||||
uri
|
||||
} else {
|
||||
var title = uri.substringAfter("$mainUrl/")
|
||||
title = when {
|
||||
(title.contains("-episode")) && !(title.contains("-ova")) -> Regex("(.+)-episode").find(
|
||||
(title.contains("-episode")) && !(title.contains("-movie")) -> Regex("(.+)-episode").find(
|
||||
title
|
||||
)?.groupValues?.get(1).toString()
|
||||
(title.contains("-ova")) -> Regex("(.+)-ova").find(title)?.groupValues?.get(1)
|
||||
.toString()
|
||||
(title.contains("-movie")) -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1)
|
||||
.toString()
|
||||
else -> Regex("(.+)-subtitle").find(title)?.groupValues?.get(1).toString()
|
||||
.replace(Regex("-\\d+"), "")
|
||||
(title.contains("-movie")) -> Regex("(.+)-movie").find(title)?.groupValues?.get(
|
||||
1
|
||||
).toString()
|
||||
else -> title
|
||||
}
|
||||
|
||||
when {
|
||||
title.contains("overlord") -> {
|
||||
title = title.replace("s", "season-")
|
||||
}
|
||||
title.contains("kaguya-sama") -> {
|
||||
title = title.replace("s3", "ultra-romantic")
|
||||
}
|
||||
}
|
||||
|
||||
"$mainUrl/anime/$title"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): AnimeSearchResponse? {
|
||||
val href = getProperAnimeLink(this.selectFirst("a.tip")!!.attr("href"))
|
||||
val title = this.selectFirst("h2[itemprop=headline]")?.text()?.trim() ?: return null
|
||||
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
|
||||
val type = getType(this.selectFirst(".eggtype, .typez")?.text()?.trim().toString())
|
||||
|
||||
val title = this.selectFirst("div.title")?.text()?.trim() ?: return null
|
||||
val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href"))
|
||||
val posterUrl = this.select("img[itemprop=image]").attr("src").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) {
|
||||
this.posterUrl = posterUrl
|
||||
addSub(epNum)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val link = "$mainUrl/?s=$query"
|
||||
val document = app.get(link).document
|
||||
|
||||
return document.select("article[itemscope=itemscope]").map {
|
||||
val title = it.selectFirst(".tt")?.ownText()?.trim().toString()
|
||||
val poster = fixUrlNull(it.selectFirst("img")?.attr("src"))
|
||||
val tvType = getType(it.selectFirst(".typez")?.text().toString())
|
||||
val href = fixUrl(it.selectFirst("a.tip")!!.attr("href"))
|
||||
|
||||
newAnimeSearchResponse(title, href, tvType) {
|
||||
this.posterUrl = poster
|
||||
addDubStatus(dubExist = false, subExist = true)
|
||||
val anime = mutableListOf<SearchResponse>()
|
||||
(1..2).forEach { page ->
|
||||
val link = "$mainUrl/page/$page/?s=$query"
|
||||
val document = app.get(link).document
|
||||
val media = document.select(".site-main.relat > article").mapNotNull {
|
||||
val title = it.selectFirst("div.title > h2")!!.ownText().trim()
|
||||
val href = it.selectFirst("a")!!.attr("href")
|
||||
val posterUrl = it.selectFirst("img")!!.attr("src").toString()
|
||||
val type = getType(it.select("div.type").text().trim())
|
||||
newAnimeSearchResponse(title, href, type) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
}
|
||||
if(media.isNotEmpty()) anime.addAll(media)
|
||||
}
|
||||
return anime
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("h1.entry-title")!!.text().trim()
|
||||
val poster = document.select(".thumb > img").attr("src")
|
||||
val tags = document.select(".genxed > a").map { it.text() }
|
||||
val title = document.selectFirst("h1.entry-title")?.text()
|
||||
?.replace("Subtitle Indonesia", "")?.trim() ?: ""
|
||||
val type = document.selectFirst("div.alternati span.type")?.text() ?: ""
|
||||
|
||||
val year = Regex("\\d, (\\d*)").find(
|
||||
document.selectFirst(".info-content > .spe > span > time")!!.text().trim()
|
||||
)?.groupValues?.get(1).toString().toIntOrNull()
|
||||
val status = getStatus(
|
||||
document.select(".info-content > .spe > span:nth-child(1)")
|
||||
.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)
|
||||
val episodes = document.select("div.lstepsiode.listeps ul li").mapNotNull {
|
||||
val header = it.selectFirst("a") ?: return@mapNotNull null
|
||||
val episode = header.text().trim().toIntOrNull()
|
||||
val link = fixUrl(header.attr("href"))
|
||||
Episode(link, header.text(), episode = episode)
|
||||
}.reversed()
|
||||
|
||||
val recommendations =
|
||||
document.select(".listupd > article[itemscope=itemscope]").mapNotNull { rec ->
|
||||
val epTitle = rec.selectFirst(".tt")!!.ownText().trim()
|
||||
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
|
||||
return newAnimeLoadResponse(title, url, getType(type)) {
|
||||
posterUrl = document.selectFirst("div.thumb > img")?.attr("src")
|
||||
this.year = document.selectFirst("div.alternati a")?.text()?.filter { it.isDigit() }?.toIntOrNull()
|
||||
addEpisodes(DubStatus.Subbed, episodes)
|
||||
showStatus = status
|
||||
plot = description
|
||||
this.tags = tags
|
||||
this.recommendations = recommendations
|
||||
addTrailer(trailer)
|
||||
showStatus =
|
||||
getStatus(
|
||||
document.selectFirst("div.alternati span:nth-child(2)")?.text()?.trim()
|
||||
)
|
||||
plot = document.selectFirst("div.entry-content > p")?.text()?.trim()
|
||||
this.tags =
|
||||
document.select("div.genre-info a").map { it.text() }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
|
@ -184,106 +142,81 @@ class OploverzProvider : MainAPI() {
|
|||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): 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") }.
|
||||
apmap { (quality, source) ->
|
||||
val video = fixedIframe(source)
|
||||
val videoQuality = getQualityFromName(quality)
|
||||
if(video.endsWith(".mp4") || video.endsWith(".mkv")) {
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
"Direct",
|
||||
"Direct",
|
||||
video,
|
||||
"",
|
||||
videoQuality
|
||||
)
|
||||
)
|
||||
} else {
|
||||
loadExtractor(video, data, subtitleCallback) { link ->
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
link.name,
|
||||
link.name,
|
||||
link.url,
|
||||
link.referer,
|
||||
videoQuality,
|
||||
link.isM3u8,
|
||||
link.headers,
|
||||
link.extractorData
|
||||
)
|
||||
)
|
||||
val document = app.get(data).document
|
||||
|
||||
argamap(
|
||||
{
|
||||
document.select("div#server ul li div").apmap {
|
||||
val dataPost = it.attr("data-post")
|
||||
val dataNume = it.attr("data-nume")
|
||||
val dataType = it.attr("data-type")
|
||||
|
||||
val iframe = app.post(
|
||||
url = "$mainUrl/wp-admin/admin-ajax.php",
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun fixedIframe(url: String): String {
|
||||
val id = Regex("""(?:/f/|/file/)(\w+)""").find(url)?.groupValues?.getOrNull(1)
|
||||
return when {
|
||||
url.startsWith(acefile) -> "$acefile/player/$id"
|
||||
url.startsWith(lbx) -> {
|
||||
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(
|
||||
private suspend fun loadFixedExtractor(
|
||||
url: String,
|
||||
referer: String?,
|
||||
name: String,
|
||||
referer: String? = null,
|
||||
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,
|
||||
loadExtractor(url, referer, subtitleCallback) { link ->
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
link.name,
|
||||
link.name,
|
||||
link.url,
|
||||
link.referer,
|
||||
name.fixQuality(),
|
||||
link.isM3u8,
|
||||
link.headers,
|
||||
link.extractorData
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
private fun fixedIframe(url: String): String {
|
||||
val id = Regex("""(?:/f/|/file/)(\w+)""").find(url)?.groupValues?.getOrNull(1)
|
||||
return when {
|
||||
url.startsWith(acefile) -> "${acefile}/player/$id"
|
||||
else -> fixUrl(url)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,7 +10,5 @@ class OploverzProviderPlugin: Plugin() {
|
|||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(OploverzProvider())
|
||||
registerExtractorAPI(Streamhide())
|
||||
registerExtractorAPI(Pixeldrain())
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// use an integer for version numbers
|
||||
version = 4
|
||||
version = 5
|
||||
|
||||
|
||||
cloudstream {
|
||||
|
|
|
@ -9,7 +9,7 @@ import org.jsoup.nodes.Element
|
|||
import java.net.URLDecoder
|
||||
|
||||
class PhimmoichillProvider : MainAPI() {
|
||||
override var mainUrl = "https://phimmoichilla.net"
|
||||
override var mainUrl = "https://phimmoichilld.net"
|
||||
override var name = "Phimmoichill"
|
||||
override val hasMainPage = true
|
||||
override var lang = "vi"
|
||||
|
@ -26,9 +26,11 @@ class PhimmoichillProvider : MainAPI() {
|
|||
"$mainUrl/list/phim-le/page-" to "Phim Lẻ",
|
||||
"$mainUrl/list/phim-bo/page-" to "Phim Bộ",
|
||||
"$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-trung-quoc/page-" to "Phim Trung Quốc",
|
||||
"$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(
|
||||
|
@ -64,6 +66,10 @@ class PhimmoichillProvider : MainAPI() {
|
|||
this.posterUrl = posterUrl
|
||||
addSub(episode)
|
||||
}
|
||||
} else if (temp.contains(Regex("Trailer"))) {
|
||||
newMovieSearchResponse(title, href, TvType.Movie) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
} else {
|
||||
val quality =
|
||||
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 title = document.selectFirst("h1[itemprop=name]")?.text()?.trim().toString()
|
||||
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 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()
|
||||
.toIntOrNull()
|
||||
val tvType = if (document.select("div.latest-episode").isNotEmpty()
|
||||
) TvType.TvSeries else TvType.Movie
|
||||
val description = document.select("div#film-content").text().trim()
|
||||
val trailer =
|
||||
document.select("div#trailer script").last()?.data()?.substringAfter("file: \"")
|
||||
?.substringBefore("\",")
|
||||
val description = document.select("div#film-content").text().substringAfter("Full HD Vietsub Thuyết Minh").substringBefore("@phimmoi").trim()
|
||||
val trailer = document.select("body script")
|
||||
.find { it.data().contains("youtube.com") }?.data()?.substringAfterLast("file: \"")?.substringBefore("\",")
|
||||
val rating =
|
||||
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() }
|
||||
|
@ -156,8 +161,7 @@ class PhimmoichillProvider : MainAPI() {
|
|||
.find { it.data().contains("filmInfo.episodeID =") }?.data()?.let { script ->
|
||||
val id = script.substringAfter("filmInfo.episodeID = parseInt('")
|
||||
app.post(
|
||||
// Not mainUrl
|
||||
url = "https://phimmoichills.net/pmplayer.php",
|
||||
url = "${this.mainUrl}/chillsplayer.php",
|
||||
data = mapOf("qcao" to id, "sv" to "0"),
|
||||
referer = data,
|
||||
headers = mapOf(
|
||||
|
@ -171,6 +175,7 @@ class PhimmoichillProvider : MainAPI() {
|
|||
listOf(
|
||||
Pair("https://so-trym.topphimmoi.org/raw/$key/index.m3u8", "PMFAST"),
|
||||
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")
|
||||
).apmap { (link, source) ->
|
||||
safeApiCall {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// use an integer for version numbers
|
||||
version = 10
|
||||
version = 11
|
||||
|
||||
|
||||
cloudstream {
|
||||
|
|
|
@ -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")
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -10,7 +10,5 @@ class SamehadakuPlugin: Plugin() {
|
|||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Samehadaku())
|
||||
registerExtractorAPI(Suzihaza())
|
||||
registerExtractorAPI(Wibufile())
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import org.jetbrains.kotlin.konan.properties.Properties
|
||||
|
||||
// use an integer for version numbers
|
||||
version = 141
|
||||
version = 144
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
|
|
|
@ -831,12 +831,19 @@ object SoraExtractor : SoraStream() {
|
|||
title: String? = null,
|
||||
season: Int? = null,
|
||||
episode: Int? = null,
|
||||
isAnime: Boolean = false,
|
||||
lastSeason: Int? = null,
|
||||
subtitleCallback: (SubtitleFile) -> 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(
|
||||
"$kissKhAPI/api/DramaList/Search?q=$title&type=0", referer = "$kissKhAPI/"
|
||||
"$kissKhAPI/api/DramaList/Search?q=$title&type=$type", referer = "$kissKhAPI/"
|
||||
).text.let {
|
||||
tryParseJson<ArrayList<KisskhResults>>(it)
|
||||
} ?: return
|
||||
|
@ -844,17 +851,15 @@ object SoraExtractor : SoraStream() {
|
|||
val (id, contentTitle) = if (res.size == 1) {
|
||||
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 {
|
||||
val data = res.find {
|
||||
it.title?.contains(
|
||||
"$fixTitle", true
|
||||
) == true && it.title.contains("Season $season", true)
|
||||
val data = res.find {
|
||||
val slugTitle = it.title.createSlug()
|
||||
when {
|
||||
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(
|
||||
|
@ -955,7 +960,7 @@ object SoraExtractor : SoraStream() {
|
|||
) {
|
||||
val res = app.get(
|
||||
"$biliBiliAPI/anime/episodes?id=${aniId ?: return}&source_id=bilibili",
|
||||
referer = kaguyaBaseUrl
|
||||
referer = otakuzBaseUrl
|
||||
)
|
||||
.parsedSafe<BiliBiliDetails>()?.episodes?.find {
|
||||
it.episodeNumber == episode
|
||||
|
@ -964,7 +969,7 @@ object SoraExtractor : SoraStream() {
|
|||
val sources =
|
||||
app.get(
|
||||
"$biliBiliAPI/source?episode_id=${res.sourceEpisodeId}&source_media_id=${res.sourceMediaId}&source_id=${res.sourceId}",
|
||||
referer = kaguyaBaseUrl
|
||||
referer = otakuzBaseUrl
|
||||
)
|
||||
.parsedSafe<BiliBiliSourcesResponse>()
|
||||
|
||||
|
@ -972,7 +977,7 @@ object SoraExtractor : SoraStream() {
|
|||
val quality =
|
||||
app.get(
|
||||
source.file ?: return@apmap null,
|
||||
referer = kaguyaBaseUrl
|
||||
referer = otakuzBaseUrl
|
||||
).document.selectFirst("Representation")
|
||||
?.attr("height")
|
||||
callback.invoke(
|
||||
|
@ -980,7 +985,7 @@ object SoraExtractor : SoraStream() {
|
|||
"BiliBili",
|
||||
"BiliBili",
|
||||
source.file,
|
||||
kaguyaBaseUrl,
|
||||
"",
|
||||
quality?.toIntOrNull() ?: Qualities.Unknown.value,
|
||||
isDash = true
|
||||
)
|
||||
|
@ -1008,16 +1013,18 @@ object SoraExtractor : SoraStream() {
|
|||
val animeId =
|
||||
app.get("https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/anilist/anime/${aniId ?: return}.json")
|
||||
.parsedSafe<MALSyncResponses>()?.pages?.zoro?.keys?.map { it }
|
||||
|
||||
val headers = mapOf(
|
||||
"X-Requested-With" to "XMLHttpRequest",
|
||||
)
|
||||
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 {
|
||||
Jsoup.parse(it)
|
||||
}?.select("div.ss-list a")?.find { it.attr("data-number") == "${episode ?: 1}" }
|
||||
?.attr("data-id")
|
||||
|
||||
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) }
|
||||
?.select("div.item.server-item")?.map {
|
||||
Triple(
|
||||
|
@ -1029,10 +1036,10 @@ object SoraExtractor : SoraStream() {
|
|||
|
||||
servers?.apmap servers@{ server ->
|
||||
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
|
||||
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(
|
||||
"${server.first} [$audio]",
|
||||
iframe,
|
||||
|
@ -1255,20 +1262,13 @@ object SoraExtractor : SoraStream() {
|
|||
extractMirrorUHD(bitLink, base)
|
||||
}
|
||||
|
||||
val tags =
|
||||
Regex("\\d{3,4}[Pp]\\.?(.*?)\\[").find(quality)?.groupValues?.getOrNull(1)
|
||||
?.replace(".", " ")?.trim()
|
||||
?: ""
|
||||
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
|
||||
val tags = getUhdTags(quality)
|
||||
val qualities = getIndexQuality(quality)
|
||||
val size = getIndexSize(quality)
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
"UHDMovies",
|
||||
"UHDMovies $tags $size",
|
||||
"UHDMovies $tags [$size]",
|
||||
downloadLink ?: return@apmap,
|
||||
"",
|
||||
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(
|
||||
imdbId: String? = 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,
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -487,3 +487,20 @@ data class EpisodeVo(
|
|||
data class DumpMediaDetail(
|
||||
@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,
|
||||
)
|
||||
|
|
|
@ -49,6 +49,9 @@ import com.hexated.SoraExtractor.invokeShinobiMovies
|
|||
import com.hexated.SoraExtractor.invokeShivamhw
|
||||
import com.hexated.SoraExtractor.invokeSmashyStream
|
||||
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.invokeUhdmovies
|
||||
import com.hexated.SoraExtractor.invokeVitoenMovies
|
||||
|
@ -97,11 +100,11 @@ open class SoraStream : TmdbProvider() {
|
|||
const val filmxyAPI = "https://www.filmxy.vip"
|
||||
const val kimcartoonAPI = "https://kimcartoon.li"
|
||||
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 kissKhAPI = "https://kisskh.co"
|
||||
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 gMoviesAPI = "https://gdrivemovies.xyz"
|
||||
const val fdMoviesAPI = "https://freedrivemovie.lol"
|
||||
|
@ -118,7 +121,7 @@ open class SoraStream : TmdbProvider() {
|
|||
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=")
|
||||
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 nineTvAPI = "https://api.9animetv.live"
|
||||
const val putlockerAPI = "https://ww7.putlocker.vip"
|
||||
|
@ -127,6 +130,9 @@ open class SoraStream : TmdbProvider() {
|
|||
const val gokuAPI = "https://goku.sx"
|
||||
const val ridomoviesAPI = "https://ridomovies.pw"
|
||||
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
|
||||
const val blackMoviesAPI = "https://dl.blacklistedbois.workers.dev/0:"
|
||||
|
@ -261,8 +267,7 @@ open class SoraStream : TmdbProvider() {
|
|||
val year = releaseDate?.split("-")?.first()?.toIntOrNull()
|
||||
val rating = res.vote_average.toString().toRatingInt()
|
||||
val genres = res.genres?.mapNotNull { it.name }
|
||||
val isAnime =
|
||||
genres?.contains("Animation") == true && (res.original_language == "zh" || res.original_language == "ja")
|
||||
val isAnime = genres?.contains("Animation") == true && (res.original_language == "zh" || res.original_language == "ja")
|
||||
val keywords = res.keywords?.results?.mapNotNull { it.name }.orEmpty()
|
||||
.ifEmpty { res.keywords?.keywords?.mapNotNull { it.name } }
|
||||
|
||||
|
@ -302,7 +307,7 @@ open class SoraStream : TmdbProvider() {
|
|||
epsTitle = eps.name,
|
||||
jpTitle = res.alternative_titles?.results?.find { it.iso_3166_1 == "JP" }?.title,
|
||||
date = season.airDate,
|
||||
airedDate = res.releaseDate ?: res.firstAirDate
|
||||
airedDate = res.releaseDate ?: res.firstAirDate,
|
||||
).toJson(),
|
||||
name = eps.name + if(isUpcoming(eps.airDate)) " - [UPCOMING]" else "",
|
||||
season = eps.seasonNumber,
|
||||
|
@ -346,7 +351,7 @@ open class SoraStream : TmdbProvider() {
|
|||
orgTitle = orgTitle,
|
||||
isAnime = isAnime,
|
||||
jpTitle = res.alternative_titles?.results?.find { it.iso_3166_1 == "JP" }?.title,
|
||||
airedDate = res.releaseDate ?: res.firstAirDate
|
||||
airedDate = res.releaseDate ?: res.firstAirDate,
|
||||
).toJson(),
|
||||
) {
|
||||
this.posterUrl = poster
|
||||
|
@ -493,18 +498,18 @@ open class SoraStream : TmdbProvider() {
|
|||
)
|
||||
},
|
||||
{
|
||||
invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback)
|
||||
},
|
||||
{
|
||||
invokeXmovies(
|
||||
res.title,
|
||||
res.year,
|
||||
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
|
||||
// )
|
||||
// },
|
||||
{
|
||||
if (!res.isAnime) invokeFmovies(
|
||||
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(
|
||||
|
@ -803,6 +808,15 @@ open class SoraStream : TmdbProvider() {
|
|||
{
|
||||
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
|
||||
|
|
|
@ -27,6 +27,8 @@ import com.hexated.SoraExtractor.invokeRidomovies
|
|||
import com.hexated.SoraExtractor.invokeSeries9
|
||||
import com.hexated.SoraExtractor.invokeSmashyStream
|
||||
import com.hexated.SoraExtractor.invokeDumpStream
|
||||
import com.hexated.SoraExtractor.invokeEmovies
|
||||
import com.hexated.SoraExtractor.invokeFourCartoon
|
||||
import com.hexated.SoraExtractor.invokeVidSrc
|
||||
import com.hexated.SoraExtractor.invokeWatchOnline
|
||||
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(
|
||||
|
@ -198,16 +200,16 @@ class SoraStreamLite : SoraStream() {
|
|||
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(
|
||||
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(
|
||||
|
@ -287,6 +289,25 @@ class SoraStreamLite : SoraStream() {
|
|||
res.year,
|
||||
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
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package com.hexated
|
|||
|
||||
import android.util.Base64
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.hexated.DumpUtils.createHeaders
|
||||
import com.hexated.DumpUtils.queryApi
|
||||
import com.hexated.SoraStream.Companion.anilistAPI
|
||||
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=")
|
||||
const val bflixKey = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
const val kaguyaBaseUrl = "https://kaguya.app/"
|
||||
const val otakuzBaseUrl = "https://otakuz.live/"
|
||||
val soraHeaders = mapOf(
|
||||
"lang" to "en",
|
||||
"versioncode" to "33",
|
||||
|
@ -111,7 +110,12 @@ data class FilmxyCookies(
|
|||
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 dotSlug = slug?.replace("-", ".")
|
||||
val spaceSlug = slug?.replace("-", " ")
|
||||
|
@ -271,8 +275,8 @@ suspend fun extractDrivebot(url: String): String? {
|
|||
|
||||
suspend fun extractGdflix(url: String): String? {
|
||||
val iframeGdflix =
|
||||
app.get(url).document.selectFirst("li.flex.flex-col.py-6 a:contains(GDFlix Direct)")
|
||||
?.attr("href") ?: return null
|
||||
if (!url.contains("gdflix")) app.get(url).document.selectFirst("li.flex.flex-col.py-6 a:contains(GDFlix Direct)")
|
||||
?.attr("href") ?: return null else url
|
||||
val base = getBaseUrl(iframeGdflix)
|
||||
|
||||
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(
|
||||
"$title",
|
||||
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 -> {
|
||||
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 goToken = thirdPage.substringAfter("?go=").substringBefore("\"")
|
||||
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)
|
||||
}
|
||||
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 {
|
||||
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(
|
||||
|
@ -1486,12 +1503,13 @@ fun getBaseUrl(url: String): String {
|
|||
}
|
||||
}
|
||||
|
||||
fun isUpcoming(dateString: String?) : Boolean {
|
||||
if(dateString == null) return false
|
||||
fun isUpcoming(dateString: String?): Boolean {
|
||||
if (dateString == null) return false
|
||||
val format = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
|
||||
val dateTime = format.parse(dateString)?.time ?: return false
|
||||
return unixTimeMS < dateTime
|
||||
}
|
||||
|
||||
fun decode(input: String): String = URLDecoder.decode(input, "utf-8")
|
||||
|
||||
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}") }
|
||||
}
|
||||
}
|
||||
val getSourcesUrl = "${
|
||||
mainIframeUrl.replace(
|
||||
val mainIframeAjax = mainIframeUrl.let {
|
||||
if(it.contains("/embed-2/e-1")) it.replace(
|
||||
"/embed-2/e-1",
|
||||
"/embed-2/ajax/e-1"
|
||||
) else it.replace(
|
||||
"/embed",
|
||||
"/ajax/embed"
|
||||
)
|
||||
}/getSources?id=$mainIframeId${sid?.let { "$&sId=$it" } ?: ""}"
|
||||
}
|
||||
val getSourcesUrl = "$mainIframeAjax/getSources?id=$mainIframeId${sid?.let { "$&sId=$it" } ?: ""}"
|
||||
val response = app.get(
|
||||
getSourcesUrl,
|
||||
referer = mainUrl,
|
||||
|
@ -1929,7 +1951,7 @@ object RabbitStream {
|
|||
}
|
||||
|
||||
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? {
|
||||
|
@ -2021,8 +2043,9 @@ object DumpUtils {
|
|||
return app.custom(
|
||||
method,
|
||||
url,
|
||||
requestBody = if(method == "POST") params.toJson().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) else null,
|
||||
params = if(method == "GET") params else emptyMap(),
|
||||
requestBody = if (method == "POST") params.toJson()
|
||||
.toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) else null,
|
||||
params = if (method == "GET") params else emptyMap(),
|
||||
headers = createHeaders(params)
|
||||
).parsedSafe<HashMap<String, String>>()?.get("data").let {
|
||||
cryptoHandler(
|
||||
|
@ -2062,7 +2085,8 @@ object DumpUtils {
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ cloudstream {
|
|||
language = "en"
|
||||
// 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")
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue