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
|
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 {
|
||||||
|
|
|
@ -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)
|
// filtering in case there is an anime with 0 episodes available on the site.
|
||||||
val results = json.data.shows.edges.filter {
|
!(it?.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
|
||||||
// filtering in case there is an anime with 0 episodes available on the site.
|
}?.mapNotNull { media ->
|
||||||
!(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
|
media?.toSearchResponse()
|
||||||
}
|
} ?: emptyList()
|
||||||
|
return newHomePageResponse(
|
||||||
results.map {
|
list = HomePageList(
|
||||||
newAnimeSearchResponse(it.name ?: "", "${it.Id}", fix = false) {
|
name = request.name,
|
||||||
this.posterUrl = it.thumbnail
|
list = home,
|
||||||
this.year = it.airedStart?.year
|
),
|
||||||
this.otherName = it.englishName
|
hasNext = request.name != movieTitle
|
||||||
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()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 5
|
version = 6
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 2
|
version = 3
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -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)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 37
|
version = 36
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 2
|
version = 3
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
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
|
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,11 +120,7 @@ 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) }) {
|
loadExtractor(iframe, "$mainUrl/", subtitleCallback, callback)
|
||||||
LocalServer.getUrl(iframe, "$mainUrl/", subtitleCallback, callback)
|
|
||||||
} else {
|
|
||||||
loadExtractor(iframe, "$mainUrl/", subtitleCallback, callback)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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%"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>()
|
||||||
val document = app.get(link).document
|
(1..2).forEach { page ->
|
||||||
|
val link = "$mainUrl/page/$page/?s=$query"
|
||||||
return document.select("article[itemscope=itemscope]").map {
|
val document = app.get(link).document
|
||||||
val title = it.selectFirst(".tt")?.ownText()?.trim().toString()
|
val media = document.select(".site-main.relat > article").mapNotNull {
|
||||||
val poster = fixUrlNull(it.selectFirst("img")?.attr("src"))
|
val title = it.selectFirst("div.title > h2")!!.ownText().trim()
|
||||||
val tvType = getType(it.selectFirst(".typez")?.text().toString())
|
val href = it.selectFirst("a")!!.attr("href")
|
||||||
val href = fixUrl(it.selectFirst("a.tip")!!.attr("href"))
|
val posterUrl = it.selectFirst("img")!!.attr("src").toString()
|
||||||
|
val type = getType(it.select("div.type").text().trim())
|
||||||
newAnimeSearchResponse(title, href, tvType) {
|
newAnimeSearchResponse(title, href, type) {
|
||||||
this.posterUrl = poster
|
this.posterUrl = posterUrl
|
||||||
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,106 +142,81 @@ 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,
|
||||||
} else {
|
"nume" to dataNume,
|
||||||
loadExtractor(video, data, subtitleCallback) { link ->
|
"type" to dataType
|
||||||
callback.invoke(
|
),
|
||||||
ExtractorLink(
|
referer = data,
|
||||||
link.name,
|
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
|
||||||
link.name,
|
).document.select("iframe").attr("src")
|
||||||
link.url,
|
|
||||||
link.referer,
|
loadExtractor(fixedIframe(iframe), "$mainUrl/", subtitleCallback, callback)
|
||||||
videoQuality,
|
|
||||||
link.isM3u8,
|
}
|
||||||
link.headers,
|
},
|
||||||
link.extractorData
|
{
|
||||||
)
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fixedIframe(url: String): String {
|
private suspend fun loadFixedExtractor(
|
||||||
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(
|
|
||||||
url: String,
|
url: String,
|
||||||
referer: String?,
|
name: String,
|
||||||
|
referer: String? = null,
|
||||||
subtitleCallback: (SubtitleFile) -> Unit,
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
callback: (ExtractorLink) -> Unit
|
callback: (ExtractorLink) -> Unit
|
||||||
) {
|
) {
|
||||||
val mId = Regex("/([ul]/[\\da-zA-Z\\-]+)").find(url)?.groupValues?.get(1)?.split("/")
|
loadExtractor(url, referer, subtitleCallback) { link ->
|
||||||
callback.invoke(
|
callback.invoke(
|
||||||
ExtractorLink(
|
ExtractorLink(
|
||||||
this.name,
|
link.name,
|
||||||
this.name,
|
link.name,
|
||||||
"$mainUrl/api/file/${mId?.last() ?: return}?download",
|
link.url,
|
||||||
url,
|
link.referer,
|
||||||
Qualities.Unknown.value,
|
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) {
|
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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 4
|
version = 5
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -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() }
|
||||||
|
@ -143,7 +148,7 @@ class PhimmoichillProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun loadLinks(
|
override suspend fun loadLinks(
|
||||||
data: String,
|
data: String,
|
||||||
isCasting: Boolean,
|
isCasting: Boolean,
|
||||||
|
@ -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 {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 10
|
version = 11
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
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) {
|
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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -831,12 +831,19 @@ 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
|
||||||
|
@ -844,17 +851,15 @@ object SoraExtractor : SoraStream() {
|
||||||
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 {
|
} else {
|
||||||
if (season == null) {
|
val data = res.find {
|
||||||
val data = res.find { it.title.equals(fixTitle, true) }
|
val slugTitle = it.title.createSlug()
|
||||||
data?.id to data?.title
|
when {
|
||||||
} else {
|
season == null -> slugTitle?.equals(slug) == true
|
||||||
val data = res.find {
|
lastSeason == 1 -> slugTitle?.contains(slug) == true
|
||||||
it.title?.contains(
|
else -> slugTitle?.contains(slug) == true && it.title?.contains("Season $season", true) == true
|
||||||
"$fixTitle", true
|
|
||||||
) == true && it.title.contains("Season $season", true)
|
|
||||||
}
|
}
|
||||||
data?.id to data?.title
|
|
||||||
}
|
}
|
||||||
|
data?.id to data?.title
|
||||||
}
|
}
|
||||||
|
|
||||||
val resDetail = app.get(
|
val resDetail = app.get(
|
||||||
|
@ -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,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue