mirror of
https://github.com/hexated/cloudstream-extensions-hexated.git
synced 2024-08-15 00:03:22 +00:00
Merge branch 'hexated:master' into master
This commit is contained in:
commit
ded26f7fe0
35 changed files with 1068 additions and 490 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 = 4
|
version = 6
|
||||||
|
|
||||||
android {
|
android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
|
|
@ -1,24 +1,19 @@
|
||||||
package com.hexated
|
package com.hexated
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.hexated.AnichiExtractors.invokeExternalSources
|
||||||
|
import com.hexated.AnichiExtractors.invokeInternalSources
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.getTracker
|
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||||
import com.lagradost.cloudstream3.extractors.helper.GogoHelper
|
|
||||||
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.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
|
||||||
import java.net.URI
|
|
||||||
|
|
||||||
class Anichi : MainAPI() {
|
open class Anichi : MainAPI() {
|
||||||
override var name = "Anichi"
|
override var name = "Anichi"
|
||||||
override val instantLinkLoading = true
|
override val instantLinkLoading = true
|
||||||
override val hasQuickSearch = false
|
override val hasQuickSearch = false
|
||||||
|
@ -32,14 +27,6 @@ class Anichi : MainAPI() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getType(t: String?): TvType {
|
|
||||||
return when {
|
|
||||||
t.equals("OVA", true) || t.equals("Special") -> TvType.OVA
|
|
||||||
t.equals("Movie", true) -> TvType.AnimeMovie
|
|
||||||
else -> TvType.Anime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie)
|
override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie)
|
||||||
|
|
||||||
private val popularTitle = "Popular"
|
private val popularTitle = "Popular"
|
||||||
|
@ -141,14 +128,21 @@ class Anichi : MainAPI() {
|
||||||
val title = showData.name
|
val title = showData.name
|
||||||
val description = showData.description
|
val description = showData.description
|
||||||
val poster = showData.thumbnail
|
val poster = showData.thumbnail
|
||||||
val type = getType(showData.type ?: "")
|
|
||||||
|
val trackers = getTracker(
|
||||||
|
title,
|
||||||
|
showData.altNames?.firstOrNull(),
|
||||||
|
showData.airedStart?.year,
|
||||||
|
showData.season?.quarter,
|
||||||
|
showData.type
|
||||||
|
)
|
||||||
|
|
||||||
val episodes = showData.availableEpisodesDetail.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(
|
||||||
it.getEpisode("sub", showData.Id),
|
it.getEpisode("sub", showData.Id, trackers?.idMal),
|
||||||
it.getEpisode("dub", showData.Id),
|
it.getEpisode("dub", showData.Id, trackers?.idMal),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,28 +158,24 @@ class Anichi : MainAPI() {
|
||||||
Pair(Actor(name, image), role)
|
Pair(Actor(name, image), role)
|
||||||
}
|
}
|
||||||
|
|
||||||
val names = showData.altNames?.plus(title)?.filterNotNull() ?: emptyList()
|
|
||||||
val trackers = getTracker(names, TrackerType.getTypes(type), showData.airedStart?.year)
|
|
||||||
|
|
||||||
return newAnimeLoadResponse(title ?: "", url, TvType.Anime) {
|
return newAnimeLoadResponse(title ?: "", url, TvType.Anime) {
|
||||||
engName = showData.altNames?.firstOrNull()
|
engName = showData.altNames?.firstOrNull()
|
||||||
posterUrl = trackers?.image ?: poster
|
posterUrl = trackers?.coverImage?.extraLarge ?: trackers?.coverImage?.large ?: poster
|
||||||
backgroundPosterUrl = trackers?.cover ?: showData.banner
|
backgroundPosterUrl = trackers?.bannerImage ?: showData.banner
|
||||||
rating = showData.averageScore?.times(100)
|
rating = showData.averageScore?.times(100)
|
||||||
tags = showData.genres
|
tags = showData.genres
|
||||||
year = showData.airedStart?.year
|
year = showData.airedStart?.year
|
||||||
duration = showData.episodeDuration?.div(60_000)
|
duration = showData.episodeDuration?.div(60_000)
|
||||||
addTrailer(showData.prevideos.filter { it.isNotBlank() }
|
addTrailer(showData.prevideos.filter { it.isNotBlank() }
|
||||||
.map { "https://www.youtube.com/watch?v=$it" })
|
.map { "https://www.youtube.com/watch?v=$it" })
|
||||||
|
|
||||||
addEpisodes(DubStatus.Subbed, episodes.first)
|
addEpisodes(DubStatus.Subbed, episodes.first)
|
||||||
addEpisodes(DubStatus.Dubbed, episodes.second)
|
addEpisodes(DubStatus.Dubbed, episodes.second)
|
||||||
addActors(characters)
|
addActors(characters)
|
||||||
//this.recommendations = recommendations
|
//this.recommendations = recommendations
|
||||||
|
|
||||||
showStatus = getStatus(showData.status.toString())
|
showStatus = getStatus(showData.status.toString())
|
||||||
addMalId(trackers?.malId)
|
addMalId(trackers?.idMal)
|
||||||
addAniListId(trackers?.aniId?.toIntOrNull())
|
addAniListId(trackers?.id)
|
||||||
plot = description?.replace(Regex("""<(.*?)>"""), "")
|
plot = description?.replace(Regex("""<(.*?)>"""), "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,411 +189,48 @@ class Anichi : MainAPI() {
|
||||||
|
|
||||||
val loadData = parseJson<AnichiLoadData>(data)
|
val loadData = parseJson<AnichiLoadData>(data)
|
||||||
|
|
||||||
val apiUrl =
|
argamap(
|
||||||
"""$apiUrl?variables={"showId":"${loadData.hash}","translationType":"${loadData.dubStatus}","episodeString":"${loadData.episode}"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$serverHash"}}"""
|
{
|
||||||
val apiResponse = app.get(apiUrl, headers = headers).parsed<LinksQuery>()
|
invokeInternalSources(
|
||||||
|
loadData.hash,
|
||||||
|
loadData.dubStatus,
|
||||||
|
loadData.episode,
|
||||||
|
subtitleCallback,
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
invokeExternalSources(
|
||||||
|
loadData.idMal,
|
||||||
|
loadData.dubStatus,
|
||||||
|
loadData.episode,
|
||||||
|
subtitleCallback,
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
apiResponse.data?.episode?.sourceUrls?.apmap { source ->
|
|
||||||
safeApiCall {
|
|
||||||
val link = fixSourceUrls(source.sourceUrl ?: return@safeApiCall, source.sourceName) ?: return@safeApiCall
|
|
||||||
if (URI(link).isAbsolute || link.startsWith("//")) {
|
|
||||||
val fixedLink = if (link.startsWith("//")) "https:$link" else link
|
|
||||||
val host = link.getHost()
|
|
||||||
|
|
||||||
when {
|
|
||||||
fixedLink.contains(Regex("(?i)playtaku|gogo")) || source.sourceName == "Vid-mp4" -> {
|
|
||||||
invokeGogo(fixedLink, subtitleCallback, callback)
|
|
||||||
}
|
|
||||||
embedIsBlacklisted(fixedLink) -> {
|
|
||||||
loadExtractor(fixedLink, subtitleCallback, callback)
|
|
||||||
}
|
|
||||||
URI(fixedLink).path.contains(".m3u") -> {
|
|
||||||
getM3u8Qualities(fixedLink, serverUrl, host).forEach(callback)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
callback(
|
|
||||||
ExtractorLink(
|
|
||||||
name,
|
|
||||||
host,
|
|
||||||
fixedLink,
|
|
||||||
serverUrl,
|
|
||||||
Qualities.P1080.value,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val fixedLink = link.fixUrlPath()
|
|
||||||
val links = app.get(fixedLink).parsedSafe<AnichiVideoApiResponse>()?.links
|
|
||||||
?: emptyList()
|
|
||||||
links.forEach { server ->
|
|
||||||
val host = server.link.getHost()
|
|
||||||
when {
|
|
||||||
source.sourceName == "Default" -> {
|
|
||||||
if (server.resolutionStr == "SUB" || server.resolutionStr == "Alt vo_SUB") {
|
|
||||||
getM3u8Qualities(
|
|
||||||
server.link,
|
|
||||||
"https://static.crunchyroll.com/",
|
|
||||||
host,
|
|
||||||
).forEach(callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
server.hls != null && server.hls -> {
|
|
||||||
getM3u8Qualities(
|
|
||||||
server.link,
|
|
||||||
"$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(
|
|
||||||
server.link
|
|
||||||
).path),
|
|
||||||
host
|
|
||||||
).forEach(callback)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
callback(
|
|
||||||
ExtractorLink(
|
|
||||||
host,
|
|
||||||
host,
|
|
||||||
server.link,
|
|
||||||
"$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(
|
|
||||||
server.link
|
|
||||||
).path),
|
|
||||||
server.resolutionStr.removeSuffix("p").toIntOrNull()
|
|
||||||
?: Qualities.P1080.value,
|
|
||||||
false,
|
|
||||||
isDash = server.resolutionStr == "Dash 1"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private val embedBlackList = listOf(
|
|
||||||
"https://mp4upload.com/",
|
|
||||||
"https://streamsb.net/",
|
|
||||||
"https://dood.to/",
|
|
||||||
"https://videobin.co/",
|
|
||||||
"https://ok.ru",
|
|
||||||
"https://streamlare.com",
|
|
||||||
"streaming.php",
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun embedIsBlacklisted(url: String): Boolean {
|
|
||||||
embedBlackList.forEach {
|
|
||||||
if (it.javaClass.name == "kotlin.text.Regex") {
|
|
||||||
if ((it as Regex).matches(url)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (url.contains(it)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun AvailableEpisodesDetail.getEpisode(
|
|
||||||
lang: String,
|
|
||||||
id: String
|
|
||||||
): List<com.lagradost.cloudstream3.Episode> {
|
|
||||||
val meta = if (lang == "sub") this.sub else this.dub
|
|
||||||
return meta.map { eps ->
|
|
||||||
Episode(
|
|
||||||
AnichiLoadData(id, lang, eps).toJson(),
|
|
||||||
"Ep $eps"
|
|
||||||
)
|
|
||||||
}.reversed()
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun getM3u8Qualities(
|
|
||||||
m3u8Link: String,
|
|
||||||
referer: String,
|
|
||||||
qualityName: String,
|
|
||||||
): List<ExtractorLink> {
|
|
||||||
return M3u8Helper.generateM3u8(
|
|
||||||
this.name,
|
|
||||||
m3u8Link,
|
|
||||||
referer,
|
|
||||||
name = qualityName
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun invokeGogo(
|
|
||||||
link: String,
|
|
||||||
subtitleCallback: (SubtitleFile) -> Unit,
|
|
||||||
callback: (ExtractorLink) -> Unit
|
|
||||||
) {
|
|
||||||
val iframe = app.get(link)
|
|
||||||
val iframeDoc = iframe.document
|
|
||||||
argamap({
|
|
||||||
iframeDoc.select(".list-server-items > .linkserver")
|
|
||||||
.forEach { element ->
|
|
||||||
val status = element.attr("data-status") ?: return@forEach
|
|
||||||
if (status != "1") return@forEach
|
|
||||||
val extractorData = element.attr("data-video") ?: return@forEach
|
|
||||||
loadExtractor(extractorData, iframe.url, subtitleCallback, callback)
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
val iv = "3134003223491201"
|
|
||||||
val secretKey = "37911490979715163134003223491201"
|
|
||||||
val secretDecryptKey = "54674138327930866480207815084989"
|
|
||||||
GogoHelper.extractVidstream(
|
|
||||||
iframe.url,
|
|
||||||
"Gogoanime",
|
|
||||||
callback,
|
|
||||||
iv,
|
|
||||||
secretKey,
|
|
||||||
secretDecryptKey,
|
|
||||||
isUsingAdaptiveKeys = false,
|
|
||||||
isUsingAdaptiveData = true,
|
|
||||||
iframeDocument = iframeDoc
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun String.getHost(): String {
|
|
||||||
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
|
const val apiUrl = BuildConfig.ANICHI_API
|
||||||
private const val serverUrl = BuildConfig.ANICHI_SERVER
|
const val serverUrl = BuildConfig.ANICHI_SERVER
|
||||||
private const val apiEndPoint = BuildConfig.ANICHI_ENDPOINT
|
const val apiEndPoint = BuildConfig.ANICHI_ENDPOINT
|
||||||
|
|
||||||
|
const val marinHost = "https://marin.moe"
|
||||||
|
|
||||||
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 slugHash = "bf603205eb2533ca21d0324a11f623854d62ed838a27e1b3fcfb712ab98b03f4"
|
||||||
private const val detailHash = "bb263f91e5bdd048c1c978f324613aeccdfe2cbc694a419466a31edb58c0cc0b"
|
private const val detailHash = "bb263f91e5bdd048c1c978f324613aeccdfe2cbc694a419466a31edb58c0cc0b"
|
||||||
private const val serverHash = "5e7e17cdd0166af5a2d8f43133d9ce3ce9253d1fdb5160a0cfd515564f98d061"
|
const val serverHash = "5e7e17cdd0166af5a2d8f43133d9ce3ce9253d1fdb5160a0cfd515564f98d061"
|
||||||
|
|
||||||
private val headers = mapOf(
|
val headers = mapOf(
|
||||||
"app-version" to "android_c-247",
|
"app-version" to "android_c-247",
|
||||||
"from-app" to BuildConfig.ANICHI_APP,
|
"from-app" to BuildConfig.ANICHI_APP,
|
||||||
"platformstr" to "android_c",
|
"platformstr" to "android_c",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class AnichiLoadData(
|
|
||||||
val hash: String,
|
|
||||||
val dubStatus: String,
|
|
||||||
val episode: String
|
|
||||||
)
|
|
||||||
|
|
||||||
data class AkIframe(
|
|
||||||
@JsonProperty("idUrl") val idUrl: String? = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Stream(
|
|
||||||
@JsonProperty("format") val format: String? = null,
|
|
||||||
@JsonProperty("audio_lang") val audio_lang: String? = null,
|
|
||||||
@JsonProperty("hardsub_lang") val hardsub_lang: String? = null,
|
|
||||||
@JsonProperty("url") val url: String? = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
data class PortData(
|
|
||||||
@JsonProperty("streams") val streams: ArrayList<Stream>? = arrayListOf(),
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Subtitles(
|
|
||||||
@JsonProperty("lang") val lang: String?,
|
|
||||||
@JsonProperty("label") val label: String?,
|
|
||||||
@JsonProperty("src") val src: String?,
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Links(
|
|
||||||
@JsonProperty("link") val link: String,
|
|
||||||
@JsonProperty("hls") val hls: Boolean?,
|
|
||||||
@JsonProperty("resolutionStr") val resolutionStr: String,
|
|
||||||
@JsonProperty("src") val src: String?,
|
|
||||||
@JsonProperty("portData") val portData: PortData? = null,
|
|
||||||
@JsonProperty("subtitles") val subtitles: ArrayList<Subtitles>? = arrayListOf(),
|
|
||||||
)
|
|
||||||
|
|
||||||
data class AnichiVideoApiResponse(
|
|
||||||
@JsonProperty("links") val links: List<Links>
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Data(
|
|
||||||
@JsonProperty("shows") val shows: Shows? = null,
|
|
||||||
@JsonProperty("queryListForTag") val queryListForTag: Shows? = null,
|
|
||||||
@JsonProperty("queryPopular") val queryPopular: Shows? = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Shows(
|
|
||||||
@JsonProperty("edges") val edges: List<Edges>? = arrayListOf(),
|
|
||||||
@JsonProperty("recommendations") val recommendations: List<EdgesCard>? = arrayListOf(),
|
|
||||||
)
|
|
||||||
|
|
||||||
data class EdgesCard(
|
|
||||||
@JsonProperty("anyCard") val anyCard: Edges? = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
data class CharacterImage(
|
|
||||||
@JsonProperty("large") val large: String?,
|
|
||||||
@JsonProperty("medium") val medium: String?
|
|
||||||
)
|
|
||||||
|
|
||||||
data class CharacterName(
|
|
||||||
@JsonProperty("full") val full: String?,
|
|
||||||
@JsonProperty("native") val native: String?
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Characters(
|
|
||||||
@JsonProperty("image") val image: CharacterImage?,
|
|
||||||
@JsonProperty("role") val role: String?,
|
|
||||||
@JsonProperty("name") val name: CharacterName?,
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Edges(
|
|
||||||
@JsonProperty("_id") val Id: String?,
|
|
||||||
@JsonProperty("name") val name: String?,
|
|
||||||
@JsonProperty("englishName") val englishName: String?,
|
|
||||||
@JsonProperty("nativeName") val nativeName: String?,
|
|
||||||
@JsonProperty("thumbnail") val thumbnail: String?,
|
|
||||||
@JsonProperty("type") val type: String?,
|
|
||||||
@JsonProperty("season") val season: Season?,
|
|
||||||
@JsonProperty("score") val score: Double?,
|
|
||||||
@JsonProperty("airedStart") val airedStart: AiredStart?,
|
|
||||||
@JsonProperty("availableEpisodes") val availableEpisodes: AvailableEpisodes?,
|
|
||||||
@JsonProperty("availableEpisodesDetail") val availableEpisodesDetail: AvailableEpisodesDetail?,
|
|
||||||
@JsonProperty("studios") val studios: List<String>?,
|
|
||||||
@JsonProperty("genres") val genres: List<String>?,
|
|
||||||
@JsonProperty("averageScore") val averageScore: Int?,
|
|
||||||
@JsonProperty("characters") val characters: List<Characters>?,
|
|
||||||
@JsonProperty("altNames") val altNames: List<String>?,
|
|
||||||
@JsonProperty("description") val description: String?,
|
|
||||||
@JsonProperty("status") val status: String?,
|
|
||||||
@JsonProperty("banner") val banner: String?,
|
|
||||||
@JsonProperty("episodeDuration") val episodeDuration: Int?,
|
|
||||||
@JsonProperty("prevideos") val prevideos: List<String> = emptyList(),
|
|
||||||
)
|
|
||||||
|
|
||||||
data class AvailableEpisodes(
|
|
||||||
@JsonProperty("sub") val sub: Int,
|
|
||||||
@JsonProperty("dub") val dub: Int,
|
|
||||||
@JsonProperty("raw") val raw: Int
|
|
||||||
)
|
|
||||||
|
|
||||||
data class AiredStart(
|
|
||||||
@JsonProperty("year") val year: Int,
|
|
||||||
@JsonProperty("month") val month: Int,
|
|
||||||
@JsonProperty("date") val date: Int
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Season(
|
|
||||||
@JsonProperty("quarter") val quarter: String,
|
|
||||||
@JsonProperty("year") val year: Int
|
|
||||||
)
|
|
||||||
|
|
||||||
data class AnichiQuery(
|
|
||||||
@JsonProperty("data") val data: Data? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Detail(
|
|
||||||
@JsonProperty("data") val data: DetailShow
|
|
||||||
)
|
|
||||||
|
|
||||||
data class DetailShow(
|
|
||||||
@JsonProperty("show") val show: Edges
|
|
||||||
)
|
|
||||||
|
|
||||||
data class AvailableEpisodesDetail(
|
|
||||||
@JsonProperty("sub") val sub: List<String>,
|
|
||||||
@JsonProperty("dub") val dub: List<String>,
|
|
||||||
@JsonProperty("raw") val raw: List<String>
|
|
||||||
)
|
|
||||||
|
|
||||||
data class LinksQuery(
|
|
||||||
@JsonProperty("data") val data: LinkData? = LinkData()
|
|
||||||
)
|
|
||||||
|
|
||||||
data class LinkData(
|
|
||||||
@JsonProperty("episode") val episode: Episode? = Episode()
|
|
||||||
)
|
|
||||||
|
|
||||||
data class SourceUrls(
|
|
||||||
@JsonProperty("sourceUrl") val sourceUrl: String? = null,
|
|
||||||
@JsonProperty("priority") val priority: Int? = null,
|
|
||||||
@JsonProperty("sourceName") val sourceName: String? = null,
|
|
||||||
@JsonProperty("type") val type: String? = null,
|
|
||||||
@JsonProperty("className") val className: String? = null,
|
|
||||||
@JsonProperty("streamerId") val streamerId: String? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Episode(
|
|
||||||
@JsonProperty("sourceUrls") val sourceUrls: ArrayList<SourceUrls> = arrayListOf(),
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Sub(
|
|
||||||
@JsonProperty("hour") val hour: Int? = null,
|
|
||||||
@JsonProperty("minute") val minute: Int? = null,
|
|
||||||
@JsonProperty("year") val year: Int? = null,
|
|
||||||
@JsonProperty("month") val month: Int? = null,
|
|
||||||
@JsonProperty("date") val date: Int? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
data class LastEpisodeDate(
|
|
||||||
@JsonProperty("dub") val dub: Sub? = Sub(),
|
|
||||||
@JsonProperty("sub") val sub: Sub? = Sub(),
|
|
||||||
@JsonProperty("raw") val raw: Sub? = Sub()
|
|
||||||
)
|
|
||||||
|
|
||||||
data class AnyCard(
|
|
||||||
@JsonProperty("_id") val Id: String? = null,
|
|
||||||
@JsonProperty("name") val name: String? = null,
|
|
||||||
@JsonProperty("englishName") val englishName: String? = null,
|
|
||||||
@JsonProperty("nativeName") val nativeName: String? = null,
|
|
||||||
@JsonProperty("availableEpisodes") val availableEpisodes: AvailableEpisodes? = null,
|
|
||||||
@JsonProperty("score") val score: Double? = null,
|
|
||||||
@JsonProperty("lastEpisodeDate") val lastEpisodeDate: LastEpisodeDate? = LastEpisodeDate(),
|
|
||||||
@JsonProperty("thumbnail") val thumbnail: String? = null,
|
|
||||||
@JsonProperty("lastChapterDate") val lastChapterDate: String? = null,
|
|
||||||
@JsonProperty("availableChapters") val availableChapters: String? = null,
|
|
||||||
@JsonProperty("__typename") val _typename: String? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
data class PageStatus(
|
|
||||||
@JsonProperty("_id") val Id: String? = null,
|
|
||||||
@JsonProperty("views") val views: String? = null,
|
|
||||||
@JsonProperty("showId") val showId: String? = null,
|
|
||||||
@JsonProperty("rangeViews") val rangeViews: String? = null,
|
|
||||||
@JsonProperty("isManga") val isManga: Boolean? = null,
|
|
||||||
@JsonProperty("__typename") val _typename: String? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
data class Recommendations(
|
|
||||||
@JsonProperty("anyCard") val anyCard: AnyCard? = null,
|
|
||||||
@JsonProperty("pageStatus") val pageStatus: PageStatus? = PageStatus(),
|
|
||||||
@JsonProperty("__typename") val _typename: String? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
data class QueryPopular(
|
|
||||||
@JsonProperty("total") val total: Int? = null,
|
|
||||||
@JsonProperty("recommendations") val recommendations: ArrayList<Recommendations> = arrayListOf(),
|
|
||||||
@JsonProperty("__typename") val _typename: String? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
data class DataPopular(
|
|
||||||
@JsonProperty("queryPopular") val queryPopular: QueryPopular? = QueryPopular()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
217
Anichi/src/main/kotlin/com/hexated/AnichiExtractors.kt
Normal file
217
Anichi/src/main/kotlin/com/hexated/AnichiExtractors.kt
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
package com.hexated
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
|
import com.lagradost.cloudstream3.apmap
|
||||||
|
import com.lagradost.cloudstream3.app
|
||||||
|
import com.lagradost.cloudstream3.argamap
|
||||||
|
import com.lagradost.cloudstream3.extractors.helper.GogoHelper
|
||||||
|
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.Qualities
|
||||||
|
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||||
|
import com.lagradost.cloudstream3.utils.httpsify
|
||||||
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
|
object AnichiExtractors : Anichi() {
|
||||||
|
|
||||||
|
suspend fun invokeInternalSources(
|
||||||
|
hash: String,
|
||||||
|
dubStatus: String,
|
||||||
|
episode: String,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit,
|
||||||
|
) {
|
||||||
|
val apiUrl =
|
||||||
|
"""$apiUrl?variables={"showId":"$hash","translationType":"$dubStatus","episodeString":"$episode"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$serverHash"}}"""
|
||||||
|
val apiResponse = app.get(apiUrl, headers = headers).parsed<LinksQuery>()
|
||||||
|
|
||||||
|
apiResponse.data?.episode?.sourceUrls?.apmap { source ->
|
||||||
|
safeApiCall {
|
||||||
|
val link = fixSourceUrls(source.sourceUrl ?: return@safeApiCall, source.sourceName)
|
||||||
|
?: return@safeApiCall
|
||||||
|
if (URI(link).isAbsolute || link.startsWith("//")) {
|
||||||
|
val fixedLink = if (link.startsWith("//")) "https:$link" else link
|
||||||
|
val host = link.getHost()
|
||||||
|
|
||||||
|
when {
|
||||||
|
fixedLink.contains(Regex("(?i)playtaku|gogo")) || source.sourceName == "Vid-mp4" -> {
|
||||||
|
invokeGogo(fixedLink, subtitleCallback, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
embedIsBlacklisted(fixedLink) -> {
|
||||||
|
loadExtractor(fixedLink, subtitleCallback, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
URI(fixedLink).path.contains(".m3u") -> {
|
||||||
|
getM3u8Qualities(fixedLink, serverUrl, host).forEach(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
callback(
|
||||||
|
ExtractorLink(
|
||||||
|
name,
|
||||||
|
host,
|
||||||
|
fixedLink,
|
||||||
|
serverUrl,
|
||||||
|
Qualities.P1080.value,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val fixedLink = link.fixUrlPath()
|
||||||
|
val links = app.get(fixedLink).parsedSafe<AnichiVideoApiResponse>()?.links
|
||||||
|
?: emptyList()
|
||||||
|
links.forEach { server ->
|
||||||
|
val host = server.link.getHost()
|
||||||
|
when {
|
||||||
|
source.sourceName == "Default" -> {
|
||||||
|
if (server.resolutionStr == "SUB" || server.resolutionStr == "Alt vo_SUB") {
|
||||||
|
getM3u8Qualities(
|
||||||
|
server.link,
|
||||||
|
"https://static.crunchyroll.com/",
|
||||||
|
host,
|
||||||
|
).forEach(callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.hls != null && server.hls -> {
|
||||||
|
getM3u8Qualities(
|
||||||
|
server.link,
|
||||||
|
"$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(
|
||||||
|
server.link
|
||||||
|
).path),
|
||||||
|
host
|
||||||
|
).forEach(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
callback(
|
||||||
|
ExtractorLink(
|
||||||
|
host,
|
||||||
|
host,
|
||||||
|
server.link,
|
||||||
|
"$apiEndPoint/player?uri=" + (if (URI(server.link).host.isNotEmpty()) server.link else apiEndPoint + URI(
|
||||||
|
server.link
|
||||||
|
).path),
|
||||||
|
server.resolutionStr.removeSuffix("p").toIntOrNull()
|
||||||
|
?: Qualities.P1080.value,
|
||||||
|
false,
|
||||||
|
isDash = server.resolutionStr == "Dash 1"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
server.subtitles?.map { sub ->
|
||||||
|
subtitleCallback.invoke(
|
||||||
|
SubtitleFile(
|
||||||
|
SubtitleHelper.fromTwoLettersToLanguage(sub.lang ?: "")
|
||||||
|
?: sub.lang ?: "",
|
||||||
|
httpsify(sub.src ?: return@map)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun invokeExternalSources(
|
||||||
|
idMal: Int? = null,
|
||||||
|
dubStatus: String,
|
||||||
|
episode: String,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit,
|
||||||
|
) {
|
||||||
|
val ids = app.get("https://api.malsync.moe/mal/anime/${idMal ?: return}")
|
||||||
|
.parsedSafe<MALSyncResponses>()?.sites
|
||||||
|
|
||||||
|
if (dubStatus == "sub") invokeMarin(ids?.marin?.keys?.firstOrNull(), episode, callback)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun invokeMarin(
|
||||||
|
id: String? = null,
|
||||||
|
episode: String,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
|
val url = "$marinHost/anime/${id ?: return}/$episode"
|
||||||
|
val cookies = app.get(
|
||||||
|
"$marinHost/anime",
|
||||||
|
headers = mapOf(
|
||||||
|
"Cookie" to "__ddg1_=;__ddg2_=;"
|
||||||
|
),
|
||||||
|
referer = "$marinHost/anime",
|
||||||
|
).cookies.let {
|
||||||
|
it["XSRF-TOKEN"] to it["marin_session"]
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
url,
|
||||||
|
headers = mapOf(
|
||||||
|
"Referer" to "$marinHost/",
|
||||||
|
"Cookie" to "__ddg1=;__ddg2_=; XSRF-TOKEN=${cookies.first}; marin_session=${cookies.second};",
|
||||||
|
"x-inertia" to "true",
|
||||||
|
"x-inertia-version" to "5ee7503af8c9844b1e8d34466b727694",
|
||||||
|
"X-Requested-With" to "XMLHttpRequest",
|
||||||
|
"X-XSRF-TOKEN" to decode(cookies.first.toString())
|
||||||
|
)
|
||||||
|
).parsedSafe<MarinResponses>()?.props?.video?.data?.mirror?.map { video ->
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
"Marin",
|
||||||
|
"Marin",
|
||||||
|
video.code?.file ?: return@map,
|
||||||
|
url,
|
||||||
|
video.code.height ?: Qualities.Unknown.value,
|
||||||
|
headers = mapOf(
|
||||||
|
"Accept" to "video/webm,video/ogg,video/*;q=0.9,application/ogg;q=0.7,audio/*;q=0.6,*/*;q=0.5",
|
||||||
|
"Accept-Language" to "en-US,en;q=0.5",
|
||||||
|
"Cookie" to "__ddg1=;__ddg2_=; XSRF-TOKEN=${cookies.first}; marin_session=${cookies.second};",
|
||||||
|
"Connection" to "keep-alive",
|
||||||
|
"Sec-Fetch-Dest" to "video",
|
||||||
|
"Sec-Fetch-Mode" to "cors",
|
||||||
|
"Sec-Fetch-Site" to "cross-site",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun invokeGogo(
|
||||||
|
link: String,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
|
val iframe = app.get(link)
|
||||||
|
val iframeDoc = iframe.document
|
||||||
|
argamap({
|
||||||
|
iframeDoc.select(".list-server-items > .linkserver")
|
||||||
|
.forEach { element ->
|
||||||
|
val status = element.attr("data-status") ?: return@forEach
|
||||||
|
if (status != "1") return@forEach
|
||||||
|
val extractorData = element.attr("data-video") ?: return@forEach
|
||||||
|
loadExtractor(extractorData, iframe.url, subtitleCallback, callback)
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
val iv = "3134003223491201"
|
||||||
|
val secretKey = "37911490979715163134003223491201"
|
||||||
|
val secretDecryptKey = "54674138327930866480207815084989"
|
||||||
|
GogoHelper.extractVidstream(
|
||||||
|
iframe.url,
|
||||||
|
"Gogoanime",
|
||||||
|
callback,
|
||||||
|
iv,
|
||||||
|
secretKey,
|
||||||
|
secretDecryptKey,
|
||||||
|
isUsingAdaptiveKeys = false,
|
||||||
|
isUsingAdaptiveData = true,
|
||||||
|
iframeDocument = iframeDoc
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
267
Anichi/src/main/kotlin/com/hexated/AnichiParser.kt
Normal file
267
Anichi/src/main/kotlin/com/hexated/AnichiParser.kt
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
package com.hexated
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
|
||||||
|
data class AnichiLoadData(
|
||||||
|
val hash: String,
|
||||||
|
val dubStatus: String,
|
||||||
|
val episode: String,
|
||||||
|
val idMal: Int? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class CoverImage(
|
||||||
|
@JsonProperty("extraLarge") var extraLarge: String? = null,
|
||||||
|
@JsonProperty("large") var large: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AniMedia(
|
||||||
|
@JsonProperty("id") var id: Int? = null,
|
||||||
|
@JsonProperty("idMal") var idMal: Int? = null,
|
||||||
|
@JsonProperty("coverImage") var coverImage: CoverImage? = null,
|
||||||
|
@JsonProperty("bannerImage") var bannerImage: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AniPage(
|
||||||
|
@JsonProperty("media") var media: ArrayList<AniMedia> = arrayListOf()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AniData(
|
||||||
|
@JsonProperty("Page") var Page: AniPage? = AniPage()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AniSearch(
|
||||||
|
@JsonProperty("data") var data: AniData? = AniData()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AkIframe(
|
||||||
|
@JsonProperty("idUrl") val idUrl: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Stream(
|
||||||
|
@JsonProperty("format") val format: String? = null,
|
||||||
|
@JsonProperty("audio_lang") val audio_lang: String? = null,
|
||||||
|
@JsonProperty("hardsub_lang") val hardsub_lang: String? = null,
|
||||||
|
@JsonProperty("url") val url: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class PortData(
|
||||||
|
@JsonProperty("streams") val streams: ArrayList<Stream>? = arrayListOf(),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Subtitles(
|
||||||
|
@JsonProperty("lang") val lang: String?,
|
||||||
|
@JsonProperty("label") val label: String?,
|
||||||
|
@JsonProperty("src") val src: String?,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Links(
|
||||||
|
@JsonProperty("link") val link: String,
|
||||||
|
@JsonProperty("hls") val hls: Boolean?,
|
||||||
|
@JsonProperty("resolutionStr") val resolutionStr: String,
|
||||||
|
@JsonProperty("src") val src: String?,
|
||||||
|
@JsonProperty("portData") val portData: PortData? = null,
|
||||||
|
@JsonProperty("subtitles") val subtitles: ArrayList<Subtitles>? = arrayListOf(),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AnichiVideoApiResponse(
|
||||||
|
@JsonProperty("links") val links: List<Links>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Data(
|
||||||
|
@JsonProperty("shows") val shows: Shows? = null,
|
||||||
|
@JsonProperty("queryListForTag") val queryListForTag: Shows? = null,
|
||||||
|
@JsonProperty("queryPopular") val queryPopular: Shows? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Shows(
|
||||||
|
@JsonProperty("edges") val edges: List<Edges>? = arrayListOf(),
|
||||||
|
@JsonProperty("recommendations") val recommendations: List<EdgesCard>? = arrayListOf(),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class EdgesCard(
|
||||||
|
@JsonProperty("anyCard") val anyCard: Edges? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class CharacterImage(
|
||||||
|
@JsonProperty("large") val large: String?,
|
||||||
|
@JsonProperty("medium") val medium: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class CharacterName(
|
||||||
|
@JsonProperty("full") val full: String?,
|
||||||
|
@JsonProperty("native") val native: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Characters(
|
||||||
|
@JsonProperty("image") val image: CharacterImage?,
|
||||||
|
@JsonProperty("role") val role: String?,
|
||||||
|
@JsonProperty("name") val name: CharacterName?,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Edges(
|
||||||
|
@JsonProperty("_id") val Id: String?,
|
||||||
|
@JsonProperty("name") val name: String?,
|
||||||
|
@JsonProperty("englishName") val englishName: String?,
|
||||||
|
@JsonProperty("nativeName") val nativeName: String?,
|
||||||
|
@JsonProperty("thumbnail") val thumbnail: String?,
|
||||||
|
@JsonProperty("type") val type: String?,
|
||||||
|
@JsonProperty("season") val season: Season?,
|
||||||
|
@JsonProperty("score") val score: Double?,
|
||||||
|
@JsonProperty("airedStart") val airedStart: AiredStart?,
|
||||||
|
@JsonProperty("availableEpisodes") val availableEpisodes: AvailableEpisodes?,
|
||||||
|
@JsonProperty("availableEpisodesDetail") val availableEpisodesDetail: AvailableEpisodesDetail?,
|
||||||
|
@JsonProperty("studios") val studios: List<String>?,
|
||||||
|
@JsonProperty("genres") val genres: List<String>?,
|
||||||
|
@JsonProperty("averageScore") val averageScore: Int?,
|
||||||
|
@JsonProperty("characters") val characters: List<Characters>?,
|
||||||
|
@JsonProperty("altNames") val altNames: List<String>?,
|
||||||
|
@JsonProperty("description") val description: String?,
|
||||||
|
@JsonProperty("status") val status: String?,
|
||||||
|
@JsonProperty("banner") val banner: String?,
|
||||||
|
@JsonProperty("episodeDuration") val episodeDuration: Int?,
|
||||||
|
@JsonProperty("prevideos") val prevideos: List<String> = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AvailableEpisodes(
|
||||||
|
@JsonProperty("sub") val sub: Int,
|
||||||
|
@JsonProperty("dub") val dub: Int,
|
||||||
|
@JsonProperty("raw") val raw: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AiredStart(
|
||||||
|
@JsonProperty("year") val year: Int,
|
||||||
|
@JsonProperty("month") val month: Int,
|
||||||
|
@JsonProperty("date") val date: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Season(
|
||||||
|
@JsonProperty("quarter") val quarter: String,
|
||||||
|
@JsonProperty("year") val year: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AnichiQuery(
|
||||||
|
@JsonProperty("data") val data: Data? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Detail(
|
||||||
|
@JsonProperty("data") val data: DetailShow
|
||||||
|
)
|
||||||
|
|
||||||
|
data class DetailShow(
|
||||||
|
@JsonProperty("show") val show: Edges
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AvailableEpisodesDetail(
|
||||||
|
@JsonProperty("sub") val sub: List<String>,
|
||||||
|
@JsonProperty("dub") val dub: List<String>,
|
||||||
|
@JsonProperty("raw") val raw: List<String>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class LinksQuery(
|
||||||
|
@JsonProperty("data") val data: LinkData? = LinkData()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class LinkData(
|
||||||
|
@JsonProperty("episode") val episode: Episode? = Episode()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SourceUrls(
|
||||||
|
@JsonProperty("sourceUrl") val sourceUrl: String? = null,
|
||||||
|
@JsonProperty("priority") val priority: Int? = null,
|
||||||
|
@JsonProperty("sourceName") val sourceName: String? = null,
|
||||||
|
@JsonProperty("type") val type: String? = null,
|
||||||
|
@JsonProperty("className") val className: String? = null,
|
||||||
|
@JsonProperty("streamerId") val streamerId: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Episode(
|
||||||
|
@JsonProperty("sourceUrls") val sourceUrls: ArrayList<SourceUrls> = arrayListOf(),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Sub(
|
||||||
|
@JsonProperty("hour") val hour: Int? = null,
|
||||||
|
@JsonProperty("minute") val minute: Int? = null,
|
||||||
|
@JsonProperty("year") val year: Int? = null,
|
||||||
|
@JsonProperty("month") val month: Int? = null,
|
||||||
|
@JsonProperty("date") val date: Int? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class LastEpisodeDate(
|
||||||
|
@JsonProperty("dub") val dub: Sub? = Sub(),
|
||||||
|
@JsonProperty("sub") val sub: Sub? = Sub(),
|
||||||
|
@JsonProperty("raw") val raw: Sub? = Sub()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AnyCard(
|
||||||
|
@JsonProperty("_id") val Id: String? = null,
|
||||||
|
@JsonProperty("name") val name: String? = null,
|
||||||
|
@JsonProperty("englishName") val englishName: String? = null,
|
||||||
|
@JsonProperty("nativeName") val nativeName: String? = null,
|
||||||
|
@JsonProperty("availableEpisodes") val availableEpisodes: AvailableEpisodes? = null,
|
||||||
|
@JsonProperty("score") val score: Double? = null,
|
||||||
|
@JsonProperty("lastEpisodeDate") val lastEpisodeDate: LastEpisodeDate? = LastEpisodeDate(),
|
||||||
|
@JsonProperty("thumbnail") val thumbnail: String? = null,
|
||||||
|
@JsonProperty("lastChapterDate") val lastChapterDate: String? = null,
|
||||||
|
@JsonProperty("availableChapters") val availableChapters: String? = null,
|
||||||
|
@JsonProperty("__typename") val _typename: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class PageStatus(
|
||||||
|
@JsonProperty("_id") val Id: String? = null,
|
||||||
|
@JsonProperty("views") val views: String? = null,
|
||||||
|
@JsonProperty("showId") val showId: String? = null,
|
||||||
|
@JsonProperty("rangeViews") val rangeViews: String? = null,
|
||||||
|
@JsonProperty("isManga") val isManga: Boolean? = null,
|
||||||
|
@JsonProperty("__typename") val _typename: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
data class Recommendations(
|
||||||
|
@JsonProperty("anyCard") val anyCard: AnyCard? = null,
|
||||||
|
@JsonProperty("pageStatus") val pageStatus: PageStatus? = PageStatus(),
|
||||||
|
@JsonProperty("__typename") val _typename: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class QueryPopular(
|
||||||
|
@JsonProperty("total") val total: Int? = null,
|
||||||
|
@JsonProperty("recommendations") val recommendations: ArrayList<Recommendations> = arrayListOf(),
|
||||||
|
@JsonProperty("__typename") val _typename: String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class DataPopular(
|
||||||
|
@JsonProperty("queryPopular") val queryPopular: QueryPopular? = QueryPopular()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MALSyncSites(
|
||||||
|
@JsonProperty("Zoro") val zoro: HashMap<String?, HashMap<String, String?>>? = hashMapOf(),
|
||||||
|
@JsonProperty("Marin") val marin: HashMap<String?, HashMap<String, String?>>? = hashMapOf(),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MALSyncResponses(
|
||||||
|
@JsonProperty("Sites") val sites: MALSyncSites? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MarinCode(
|
||||||
|
@JsonProperty("file") val file: String? = null,
|
||||||
|
@JsonProperty("height") val height: Int? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MarinMirror(
|
||||||
|
@JsonProperty("code") val code: MarinCode? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MarinData(
|
||||||
|
@JsonProperty("mirror") val mirror: ArrayList<MarinMirror>? = arrayListOf(),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MarinVideos(
|
||||||
|
@JsonProperty("data") val data: MarinData? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MarinProps(
|
||||||
|
@JsonProperty("video") val video: MarinVideos? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MarinResponses(
|
||||||
|
@JsonProperty("props") val props: MarinProps? = null,
|
||||||
|
)
|
138
Anichi/src/main/kotlin/com/hexated/AnichiUtils.kt
Normal file
138
Anichi/src/main/kotlin/com/hexated/AnichiUtils.kt
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package com.hexated
|
||||||
|
|
||||||
|
import com.hexated.Anichi.Companion.apiEndPoint
|
||||||
|
import com.lagradost.cloudstream3.Episode
|
||||||
|
import com.lagradost.cloudstream3.app
|
||||||
|
import com.lagradost.cloudstream3.base64Decode
|
||||||
|
import com.lagradost.cloudstream3.fixTitle
|
||||||
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||||
|
import com.lagradost.nicehttp.RequestBodyTypes
|
||||||
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import java.net.URI
|
||||||
|
import java.net.URLDecoder
|
||||||
|
|
||||||
|
suspend fun getTracker(name: String?, altName: String?, year: Int?, season: String?, type: String?): AniMedia? {
|
||||||
|
return fetchId(name, year, season, type).takeIf { it?.id != null } ?: fetchId(
|
||||||
|
altName,
|
||||||
|
year,
|
||||||
|
season,
|
||||||
|
type
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun fetchId(title: String?, year: Int?, season: String?, type: String?): AniMedia? {
|
||||||
|
val query = """
|
||||||
|
query (
|
||||||
|
${'$'}page: Int = 1
|
||||||
|
${'$'}search: String
|
||||||
|
${'$'}sort: [MediaSort] = [POPULARITY_DESC, SCORE_DESC]
|
||||||
|
${'$'}type: MediaType
|
||||||
|
${'$'}season: MediaSeason
|
||||||
|
${'$'}year: String
|
||||||
|
${'$'}format: [MediaFormat]
|
||||||
|
) {
|
||||||
|
Page(page: ${'$'}page, perPage: 20) {
|
||||||
|
media(
|
||||||
|
search: ${'$'}search
|
||||||
|
sort: ${'$'}sort
|
||||||
|
type: ${'$'}type
|
||||||
|
season: ${'$'}season
|
||||||
|
startDate_like: ${'$'}year
|
||||||
|
format_in: ${'$'}format
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
idMal
|
||||||
|
coverImage { extraLarge large }
|
||||||
|
bannerImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent().trim()
|
||||||
|
|
||||||
|
val variables = mapOf(
|
||||||
|
"search" to title,
|
||||||
|
"sort" to "SEARCH_MATCH",
|
||||||
|
"type" to "ANIME",
|
||||||
|
"season" to if(type.equals("ona", true)) "" else season?.uppercase(),
|
||||||
|
"year" to "$year%",
|
||||||
|
"format" to listOf(type?.uppercase())
|
||||||
|
).filterValues { value -> value != null && value.toString().isNotEmpty() }
|
||||||
|
|
||||||
|
val data = mapOf(
|
||||||
|
"query" to query,
|
||||||
|
"variables" to variables
|
||||||
|
).toJson().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull())
|
||||||
|
|
||||||
|
return try {
|
||||||
|
app.post("https://graphql.anilist.co", requestBody = data)
|
||||||
|
.parsedSafe<AniSearch>()?.data?.Page?.media?.firstOrNull()
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
logError(t)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decode(input: String): String = URLDecoder.decode(input, "utf-8")
|
||||||
|
|
||||||
|
private val embedBlackList = listOf(
|
||||||
|
"https://mp4upload.com/",
|
||||||
|
"https://streamsb.net/",
|
||||||
|
"https://dood.to/",
|
||||||
|
"https://videobin.co/",
|
||||||
|
"https://ok.ru",
|
||||||
|
"https://streamlare.com",
|
||||||
|
"streaming.php",
|
||||||
|
)
|
||||||
|
|
||||||
|
fun embedIsBlacklisted(url: String): Boolean {
|
||||||
|
return embedBlackList.any { url.contains(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun AvailableEpisodesDetail.getEpisode(
|
||||||
|
lang: String,
|
||||||
|
id: String,
|
||||||
|
malId: Int?,
|
||||||
|
): List<Episode> {
|
||||||
|
val meta = if (lang == "sub") this.sub else this.dub
|
||||||
|
return meta.map { eps ->
|
||||||
|
Episode(
|
||||||
|
AnichiLoadData(id, lang, eps, malId).toJson(),
|
||||||
|
"Ep $eps",
|
||||||
|
episode = eps.toIntOrNull()
|
||||||
|
)
|
||||||
|
}.reversed()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getM3u8Qualities(
|
||||||
|
m3u8Link: String,
|
||||||
|
referer: String,
|
||||||
|
qualityName: String,
|
||||||
|
): List<ExtractorLink> {
|
||||||
|
return M3u8Helper.generateM3u8(
|
||||||
|
qualityName,
|
||||||
|
m3u8Link,
|
||||||
|
referer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.getHost(): String {
|
||||||
|
return fixTitle(URI(this).host.substringBeforeLast(".").substringAfterLast("."))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.fixUrlPath() : String {
|
||||||
|
return if(this.contains(".json?")) apiEndPoint + this else apiEndPoint + URI(this).path + ".json?" + URI(this).query
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fixSourceUrls(url: String, source: String?) : String? {
|
||||||
|
return if(source == "Ak" || url.contains("/player/vitemb")) {
|
||||||
|
AppUtils.tryParseJson<AkIframe>(base64Decode(url.substringAfter("=")))?.idUrl
|
||||||
|
} else {
|
||||||
|
url.replace(" ", "%20")
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,5 +23,5 @@ cloudstream {
|
||||||
"OVA",
|
"OVA",
|
||||||
)
|
)
|
||||||
|
|
||||||
iconUrl = "https://www.google.com/s2/favicons?domain=111.90.143.42&sz=%size%"
|
iconUrl = "https://aghanim.xyz/wp-content/themes/animesail/assets/images/ico.png"
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@ cloudstream {
|
||||||
* 2: Slow
|
* 2: Slow
|
||||||
* 3: Beta only
|
* 3: Beta only
|
||||||
* */
|
* */
|
||||||
status = 1 // will be 3 if unspecified
|
status = 0 // will be 3 if unspecified
|
||||||
tvTypes = listOf(
|
tvTypes = listOf(
|
||||||
"AnimeMovie",
|
"AnimeMovie",
|
||||||
"Anime",
|
"Anime",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 1
|
version = 2
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
language = "pt-pt"
|
language = "pt-pt"
|
||||||
|
|
|
@ -35,7 +35,7 @@ class Anroll : MainAPI() {
|
||||||
): HomePageResponse {
|
): HomePageResponse {
|
||||||
val document = app.get("$mainUrl/home").document
|
val document = app.get("$mainUrl/home").document
|
||||||
val home = mutableListOf<HomePageList>()
|
val home = mutableListOf<HomePageList>()
|
||||||
document.select("div.sc-f5d5b250-1.iJHcsI").map { div ->
|
document.select("div.hAbQAe").map { div ->
|
||||||
val header = div.selectFirst("h2")?.text() ?: return@map
|
val header = div.selectFirst("h2")?.text() ?: return@map
|
||||||
val child = HomePageList(
|
val child = HomePageList(
|
||||||
header,
|
header,
|
||||||
|
@ -91,11 +91,11 @@ class Anroll : MainAPI() {
|
||||||
val fixUrl = getProperAnimeLink(url) ?: throw ErrorLoadingException()
|
val fixUrl = getProperAnimeLink(url) ?: throw ErrorLoadingException()
|
||||||
val document = app.get(fixUrl).document
|
val document = app.get(fixUrl).document
|
||||||
|
|
||||||
val article = document.selectFirst("article.sc-f5d5b250-9") ?: return null
|
val article = document.selectFirst("article.animedetails") ?: return null
|
||||||
val title = article.selectFirst("h2")?.text() ?: return null
|
val title = article.selectFirst("h2")?.text() ?: return null
|
||||||
val poster = fixUrlNull(document.select("article.sc-f5d5b250-8 img").attr("src"))
|
val poster = fixUrlNull(document.select("section.animecontent img").attr("src"))
|
||||||
val tags = article.select("div#generos a").map { it.text() }
|
val tags = article.select("div#generos a").map { it.text() }
|
||||||
val year = article.selectFirst("div.sc-f5d5b250-4")?.nextElementSibling()?.text()
|
val year = article.selectFirst("div.dfuefM")?.nextElementSibling()?.text()
|
||||||
?.toIntOrNull()
|
?.toIntOrNull()
|
||||||
val description = document.select("div.sinopse").text().trim()
|
val description = document.select("div.sinopse").text().trim()
|
||||||
val type = if (fixUrl.contains("/a/")) TvType.Anime else TvType.AnimeMovie
|
val type = if (fixUrl.contains("/a/")) TvType.Anime else TvType.AnimeMovie
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
package com.hexated
|
package com.hexated
|
||||||
|
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.extractors.Filesim
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
|
|
||||||
class DramaSerial : MainAPI() {
|
class DramaSerial : MainAPI() {
|
||||||
override var mainUrl = "https://dramaserial.wiki"
|
override var mainUrl = "https://dramaserial.sbs"
|
||||||
override var name = "DramaSerial"
|
override var name = "DramaSerial"
|
||||||
override val hasMainPage = true
|
override val hasMainPage = true
|
||||||
override var lang = "id"
|
override var lang = "id"
|
||||||
|
@ -113,7 +114,7 @@ class DramaSerial : MainAPI() {
|
||||||
mLink.attr("onclick").substringAfter("frame('").substringBefore("')").let { iLink ->
|
mLink.attr("onclick").substringAfter("frame('").substringBefore("')").let { iLink ->
|
||||||
val uLink = app.get(iLink, referer = iframe).document.select("script").find { it.data().contains("(document).ready") }?.data()?.substringAfter("replace(\"")?.substringBefore("\");") ?: return@apmap null
|
val uLink = app.get(iLink, referer = iframe).document.select("script").find { it.data().contains("(document).ready") }?.data()?.substringAfter("replace(\"")?.substringBefore("\");") ?: return@apmap null
|
||||||
val link = app.get(uLink, referer = iLink).document.selectFirst("iframe")?.attr("src") ?: return@apmap null
|
val link = app.get(uLink, referer = iLink).document.selectFirst("iframe")?.attr("src") ?: return@apmap null
|
||||||
loadExtractor(fixUrl(link), "$mainUrl/", subtitleCallback, callback)
|
loadExtractor(fixUrl(link), "https://juraganfilm.info/", subtitleCallback, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,3 +123,8 @@ class DramaSerial : MainAPI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Bk21 : Filesim() {
|
||||||
|
override val name = "Bk21"
|
||||||
|
override var mainUrl = "https://bk21.net"
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,6 @@ class DramaSerialPlugin: 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(DramaSerial())
|
registerMainAPI(DramaSerial())
|
||||||
registerExtractorAPI(Lkctwoone())
|
registerExtractorAPI(Bk21())
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
package com.hexated
|
|
||||||
|
|
||||||
import com.lagradost.cloudstream3.extractors.XStreamCdn
|
|
||||||
|
|
||||||
class Lkctwoone: XStreamCdn() {
|
|
||||||
override val name: String = "LKC21"
|
|
||||||
override val mainUrl: String = "https://lkc21.net"
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 9
|
version = 10
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -83,7 +83,7 @@ class Hdfilmcehennemi : MainAPI() {
|
||||||
val title = document.selectFirst("div.card-header > h1, div.card-header > h2")?.text()
|
val title = document.selectFirst("div.card-header > h1, div.card-header > h2")?.text()
|
||||||
?.removeSuffix("Filminin Bilgileri")?.trim()
|
?.removeSuffix("Filminin Bilgileri")?.trim()
|
||||||
?: return null
|
?: return null
|
||||||
val poster = fixUrlNull(document.selectFirst("img.img-fluid")?.attr("src"))
|
val poster = fixUrlNull(document.select("img.img-fluid").lastOrNull()?.attr("src"))
|
||||||
val tags = document.select("div.mb-0.lh-lg div:nth-child(5) a").map { it.text() }
|
val tags = document.select("div.mb-0.lh-lg div:nth-child(5) a").map { it.text() }
|
||||||
val year =
|
val year =
|
||||||
document.selectFirst("div.mb-0.lh-lg div:nth-child(4) a")?.text()?.trim()?.toIntOrNull()
|
document.selectFirst("div.mb-0.lh-lg div:nth-child(4) a")?.text()?.trim()?.toIntOrNull()
|
||||||
|
@ -154,10 +154,6 @@ class Hdfilmcehennemi : MainAPI() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.addMarks(str: String): String {
|
|
||||||
return this.replace(Regex("\"?$str\"?"), "\"$str\"")
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun invokeLocalSource(
|
private suspend fun invokeLocalSource(
|
||||||
source: String,
|
source: String,
|
||||||
url: String,
|
url: String,
|
||||||
|
@ -205,11 +201,24 @@ class Hdfilmcehennemi : MainAPI() {
|
||||||
}.apmap { (url, source) ->
|
}.apmap { (url, source) ->
|
||||||
safeApiCall {
|
safeApiCall {
|
||||||
app.get(url).document.select("div.card-video > iframe").attr("data-src")
|
app.get(url).document.select("div.card-video > iframe").attr("data-src")
|
||||||
.let { link ->
|
.let { url ->
|
||||||
if (link.startsWith(mainUrl)) {
|
if (url.startsWith(mainUrl)) {
|
||||||
invokeLocalSource(source, link, subtitleCallback, callback)
|
invokeLocalSource(source, url, subtitleCallback, callback)
|
||||||
} else {
|
} else {
|
||||||
loadExtractor(link, "$mainUrl/", subtitleCallback, callback)
|
loadExtractor(url, "$mainUrl/", subtitleCallback) { link ->
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
source,
|
||||||
|
source,
|
||||||
|
link.url,
|
||||||
|
link.referer,
|
||||||
|
link.quality,
|
||||||
|
link.isM3u8,
|
||||||
|
link.headers,
|
||||||
|
link.extractorData
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,10 +226,6 @@ class Hdfilmcehennemi : MainAPI() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class Source(
|
|
||||||
@JsonProperty("file") val file: String? = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
private data class SubSource(
|
private data class SubSource(
|
||||||
@JsonProperty("file") val file: String? = null,
|
@JsonProperty("file") val file: String? = null,
|
||||||
@JsonProperty("label") val label: String? = null,
|
@JsonProperty("label") val label: String? = null,
|
||||||
|
|
|
@ -24,5 +24,5 @@ cloudstream {
|
||||||
"AsianDrama",
|
"AsianDrama",
|
||||||
)
|
)
|
||||||
|
|
||||||
iconUrl = "https://www.google.com/s2/favicons?domain=109.234.36.69&sz=%size%"
|
iconUrl = "https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://idlixian.com&size=16"
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 13
|
version = 14
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -6,11 +6,10 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.net.URI
|
|
||||||
|
|
||||||
class LayarKacaProvider : MainAPI() {
|
class LayarKacaProvider : MainAPI() {
|
||||||
override var mainUrl = "https://d21.fun"
|
override var mainUrl = "https://tv.lk21official.pro"
|
||||||
private var seriesUrl = "https://tv.nontondrama.click"
|
private var seriesUrl = "https://tv1.nontondrama.click"
|
||||||
override var name = "LayarKaca"
|
override var name = "LayarKaca"
|
||||||
override val hasMainPage = true
|
override val hasMainPage = true
|
||||||
override var lang = "id"
|
override var lang = "id"
|
||||||
|
@ -57,7 +56,7 @@ class LayarKacaProvider : MainAPI() {
|
||||||
private fun Element.toSearchResult(): SearchResponse? {
|
private fun Element.toSearchResult(): SearchResponse? {
|
||||||
val title = this.selectFirst("h1.grid-title > a")?.ownText()?.trim() ?: return null
|
val title = this.selectFirst("h1.grid-title > a")?.ownText()?.trim() ?: return null
|
||||||
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
|
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
|
||||||
val posterUrl = fixUrlNull(this.selectFirst(".grid-poster > a > img")?.attr("src"))
|
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
|
||||||
val type =
|
val type =
|
||||||
if (this.selectFirst("div.last-episode") == null) TvType.Movie else TvType.TvSeries
|
if (this.selectFirst("div.last-episode") == null) TvType.Movie else TvType.TvSeries
|
||||||
return if (type == TvType.TvSeries) {
|
return if (type == TvType.TvSeries) {
|
||||||
|
@ -176,33 +175,34 @@ class LayarKacaProvider : MainAPI() {
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
invokeCast(link, callback)
|
loadExtractor(link, bananalicious, subtitleCallback, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun invokeCast(
|
|
||||||
url: String,
|
|
||||||
callback: (ExtractorLink) -> Unit
|
|
||||||
) {
|
|
||||||
val response = app.get(url, referer = bananalicious).document
|
|
||||||
response.select("script[type=text/javascript]").map { script ->
|
|
||||||
if (script.data().contains(Regex("eval\\(function\\(p,a,c,k,e,[rd]"))) {
|
|
||||||
val unpackedscript = getAndUnpack(script.data())
|
|
||||||
val m3u8Regex = Regex("file.\"(.*?m3u8.*?)\"")
|
|
||||||
val m3u8 = m3u8Regex.find(unpackedscript)?.destructured?.component1() ?: ""
|
|
||||||
if (m3u8.isNotEmpty()) {
|
|
||||||
M3u8Helper.generateM3u8(
|
|
||||||
fixTitle(URI(url).host).substringBefore("."),
|
|
||||||
m3u8,
|
|
||||||
mainUrl
|
|
||||||
).forEach(callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun decode(input: String): String = URLDecoder.decode(input, "utf-8").replace(" ", "%20")
|
private fun decode(input: String): String = URLDecoder.decode(input, "utf-8").replace(" ", "%20")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open class Emturbovid : ExtractorApi() {
|
||||||
|
override val name = "Emturbovid"
|
||||||
|
override val mainUrl = "https://emturbovid.com"
|
||||||
|
override val requiresReferer = true
|
||||||
|
|
||||||
|
override suspend fun getUrl(
|
||||||
|
url: String,
|
||||||
|
referer: String?,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
|
val response = app.get(url, referer = referer)
|
||||||
|
val m3u8 = Regex("[\"'](.*?master\\.m3u8.*?)[\"']").find(response.text)?.groupValues?.getOrNull(1)
|
||||||
|
M3u8Helper.generateM3u8(
|
||||||
|
name,
|
||||||
|
m3u8 ?: return,
|
||||||
|
mainUrl
|
||||||
|
).forEach(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -10,5 +10,6 @@ class LayarKacaProviderPlugin: 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(LayarKacaProvider())
|
registerMainAPI(LayarKacaProvider())
|
||||||
|
registerExtractorAPI(Emturbovid())
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 36
|
version = 37
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -11,6 +11,11 @@ import kotlin.random.Random
|
||||||
|
|
||||||
const val twoEmbedAPI = "https://www.2embed.to"
|
const val twoEmbedAPI = "https://www.2embed.to"
|
||||||
|
|
||||||
|
class Sbnmp : Sbflix() {
|
||||||
|
override val name = "Sbnmp"
|
||||||
|
override var mainUrl = "https://sbnmp.bar"
|
||||||
|
}
|
||||||
|
|
||||||
class Sbrulz : Sbflix() {
|
class Sbrulz : Sbflix() {
|
||||||
override val name = "Sbrulz"
|
override val name = "Sbrulz"
|
||||||
override var mainUrl = "https://sbrulz.xyz"
|
override var mainUrl = "https://sbrulz.xyz"
|
||||||
|
|
|
@ -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.trade"
|
override var mainUrl = "https://movierulzhd.help"
|
||||||
private var directUrl = mainUrl
|
private var directUrl = mainUrl
|
||||||
override var name = "Movierulzhd"
|
override var name = "Movierulzhd"
|
||||||
override val hasMainPage = true
|
override val hasMainPage = true
|
||||||
|
|
|
@ -13,5 +13,6 @@ class MovierulzhdPlugin: Plugin() {
|
||||||
registerExtractorAPI(Sbflix())
|
registerExtractorAPI(Sbflix())
|
||||||
registerExtractorAPI(Sbrulz())
|
registerExtractorAPI(Sbrulz())
|
||||||
registerExtractorAPI(Sbmiz())
|
registerExtractorAPI(Sbmiz())
|
||||||
|
registerExtractorAPI(Sbnmp())
|
||||||
}
|
}
|
||||||
}
|
}
|
25
Nekopoi/build.gradle.kts
Normal file
25
Nekopoi/build.gradle.kts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// use an integer for version numbers
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
|
||||||
|
cloudstream {
|
||||||
|
language = "id"
|
||||||
|
// All of these properties are optional, you can safely remove them
|
||||||
|
|
||||||
|
// description = "Lorem Ipsum"
|
||||||
|
authors = listOf("Sora")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status int as the following:
|
||||||
|
* 0: Down
|
||||||
|
* 1: Ok
|
||||||
|
* 2: Slow
|
||||||
|
* 3: Beta only
|
||||||
|
* */
|
||||||
|
status = 1 // will be 3 if unspecified
|
||||||
|
tvTypes = listOf(
|
||||||
|
"NSFW",
|
||||||
|
)
|
||||||
|
|
||||||
|
iconUrl = "https://www.google.com/s2/favicons?domain=nekopoi.care&sz=%size%"
|
||||||
|
}
|
2
Nekopoi/src/main/AndroidManifest.xml
Normal file
2
Nekopoi/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="com.hexated"/>
|
276
Nekopoi/src/main/kotlin/com/hexated/Nekopoi.kt
Normal file
276
Nekopoi/src/main/kotlin/com/hexated/Nekopoi.kt
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
package com.hexated
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.utils.*
|
||||||
|
import com.lagradost.nicehttp.Requests
|
||||||
|
import com.lagradost.nicehttp.Session
|
||||||
|
import org.jsoup.nodes.Element
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
|
class Nekopoi : MainAPI() {
|
||||||
|
override var mainUrl = "https://nekopoi.care"
|
||||||
|
override var name = "Nekopoi"
|
||||||
|
override val hasMainPage = true
|
||||||
|
override var lang = "id"
|
||||||
|
|
||||||
|
override val supportedTypes = setOf(
|
||||||
|
TvType.NSFW,
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val session = Session(Requests().baseClient)
|
||||||
|
val mirrorBlackList = arrayOf(
|
||||||
|
"MegaupNet",
|
||||||
|
"DropApk",
|
||||||
|
"Racaty",
|
||||||
|
"ZippyShare",
|
||||||
|
"ZippySha",
|
||||||
|
"VideobinCo",
|
||||||
|
"DropApk",
|
||||||
|
"SendCm",
|
||||||
|
"GoogleDrive",
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getStatus(t: String?): ShowStatus {
|
||||||
|
return when (t) {
|
||||||
|
"Completed" -> ShowStatus.Completed
|
||||||
|
"Ongoing" -> ShowStatus.Ongoing
|
||||||
|
else -> ShowStatus.Completed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override val mainPage = mainPageOf(
|
||||||
|
"$mainUrl/category/hentai/" to "Hentai",
|
||||||
|
"$mainUrl/category/jav/" to "Jav",
|
||||||
|
"$mainUrl/category/3d-hentai/" to "3D Hentai",
|
||||||
|
"$mainUrl/category/jav-cosplay/" to "Jav Cosplay",
|
||||||
|
)
|
||||||
|
|
||||||
|
override suspend fun getMainPage(
|
||||||
|
page: Int,
|
||||||
|
request: MainPageRequest
|
||||||
|
): HomePageResponse {
|
||||||
|
val document = app.get("${request.data}/page/$page").document
|
||||||
|
val home = document.select("div.result ul li").mapNotNull {
|
||||||
|
it.toSearchResult()
|
||||||
|
}
|
||||||
|
return newHomePageResponse(
|
||||||
|
list = HomePageList(
|
||||||
|
name = request.name,
|
||||||
|
list = home,
|
||||||
|
isHorizontalImages = true
|
||||||
|
),
|
||||||
|
hasNext = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getProperAnimeLink(uri: String): String {
|
||||||
|
return if (uri.contains("-episode-")) {
|
||||||
|
val title = uri.substringAfter("$mainUrl/").substringBefore("-episode-")
|
||||||
|
.removePrefix("new-release-").removePrefix("uncensored-")
|
||||||
|
"$mainUrl/hentai/$title"
|
||||||
|
} else {
|
||||||
|
uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Element.toSearchResult(): AnimeSearchResponse? {
|
||||||
|
val title = this.selectFirst("h2 a")?.text()?.trim() ?: return null
|
||||||
|
val href = getProperAnimeLink(this.selectFirst("a")?.attr("href") ?: return null)
|
||||||
|
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
|
||||||
|
val epNum = this.selectFirst("i.dot")?.text()?.filter { it.isDigit() }?.toIntOrNull()
|
||||||
|
return newAnimeSearchResponse(title, href, TvType.NSFW) {
|
||||||
|
this.posterUrl = posterUrl
|
||||||
|
addSub(epNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
|
return app.get("$mainUrl/search/$query").document.select("div.result ul li")
|
||||||
|
.mapNotNull {
|
||||||
|
it.toSearchResult()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun load(url: String): LoadResponse {
|
||||||
|
val document = app.get(url).document
|
||||||
|
|
||||||
|
val title = document.selectFirst("span.desc b, div.eroinfo h1")?.text()?.trim() ?: ""
|
||||||
|
val poster = fixUrlNull(document.selectFirst("div.imgdesc img, div.thm img")?.attr("src"))
|
||||||
|
val table = document.select("div.listinfo ul, div.konten")
|
||||||
|
val tags =
|
||||||
|
table.select("li:contains(Genres) a").map { it.text() }.takeIf { it.isNotEmpty() }
|
||||||
|
?: table.select("p:contains(Genre)").text().substringAfter(":").split(",")
|
||||||
|
.map { it.trim() }
|
||||||
|
val year =
|
||||||
|
document.selectFirst("li:contains(Tayang)")?.text()?.substringAfterLast(",")
|
||||||
|
?.filter { it.isDigit() }?.toIntOrNull()
|
||||||
|
val status = getStatus(
|
||||||
|
document.selectFirst("li:contains(Status)")?.text()?.substringAfter(":")?.trim()
|
||||||
|
)
|
||||||
|
val duration = document.selectFirst("li:contains(Durasi)")?.text()?.substringAfterLast(":")
|
||||||
|
?.filter { it.isDigit() }?.toIntOrNull()
|
||||||
|
val description = document.selectFirst("span.desc p")?.text()
|
||||||
|
|
||||||
|
val episodes = document.select("div.episodelist ul li").mapNotNull {
|
||||||
|
val name = it.selectFirst("a")?.text()
|
||||||
|
val link = fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null
|
||||||
|
Episode(link, name = name)
|
||||||
|
}.takeIf { it.isNotEmpty() } ?: listOf(Episode(url, title))
|
||||||
|
|
||||||
|
return newAnimeLoadResponse(title, url, TvType.NSFW) {
|
||||||
|
engName = title
|
||||||
|
posterUrl = poster
|
||||||
|
this.year = year
|
||||||
|
this.duration = duration
|
||||||
|
addEpisodes(DubStatus.Subbed, episodes)
|
||||||
|
showStatus = status
|
||||||
|
plot = description
|
||||||
|
this.tags = tags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
|
|
||||||
|
val res = app.get(data).document
|
||||||
|
|
||||||
|
argamap(
|
||||||
|
{
|
||||||
|
res.select("div#show-stream iframe").apmap { iframe ->
|
||||||
|
loadExtractor(iframe.attr("src"), "$mainUrl/", subtitleCallback, callback)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
res.select("div.boxdownload div.liner").map { ele ->
|
||||||
|
getIndexQuality(
|
||||||
|
ele.select("div.name").text()
|
||||||
|
) to ele.selectFirst("a:contains(ouo)")
|
||||||
|
?.attr("href")
|
||||||
|
}.filter { it.first != Qualities.P360.value }.map {
|
||||||
|
val bypassedAds = bypassMirrored(bypassOuo(it.second ?: return@map) ?: return@map)
|
||||||
|
bypassedAds.apmap ads@{ adsLink ->
|
||||||
|
loadExtractor(
|
||||||
|
fixEmbed(adsLink) ?: return@ads,
|
||||||
|
"$mainUrl/",
|
||||||
|
subtitleCallback,
|
||||||
|
) { link ->
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
link.name,
|
||||||
|
link.name,
|
||||||
|
link.url,
|
||||||
|
link.referer,
|
||||||
|
if(link.isM3u8) link.quality else it.first,
|
||||||
|
link.isM3u8,
|
||||||
|
link.headers,
|
||||||
|
link.extractorData
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fixEmbed(url: String?): String? {
|
||||||
|
if (url == null) return null
|
||||||
|
val host = getBaseUrl(url)
|
||||||
|
return when {
|
||||||
|
url.contains("streamsb", true) -> url.replace("$host/", "$host/e/")
|
||||||
|
else -> url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBaseUrl(url: String): String {
|
||||||
|
return URI(url).let {
|
||||||
|
"${it.scheme}://${it.host}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun bypassOuo(url: String?): String? {
|
||||||
|
var res = session.get(url ?: return null)
|
||||||
|
run lit@{
|
||||||
|
(1..2).forEach { _ ->
|
||||||
|
if (res.headers["location"] != null) return@lit
|
||||||
|
val document = res.document
|
||||||
|
val nextUrl = document.select("form").attr("action")
|
||||||
|
val data = document.select("form input").mapNotNull {
|
||||||
|
it.attr("name") to it.attr("value")
|
||||||
|
}.toMap().toMutableMap()
|
||||||
|
val captchaKey =
|
||||||
|
document.select("script[src*=https://www.google.com/recaptcha/api.js?render=]")
|
||||||
|
.attr("src").substringAfter("render=")
|
||||||
|
val token = APIHolder.getCaptchaToken(url, captchaKey)
|
||||||
|
data["x-token"] = token ?: ""
|
||||||
|
res = session.post(
|
||||||
|
nextUrl,
|
||||||
|
data = data,
|
||||||
|
headers = mapOf("content-type" to "application/x-www-form-urlencoded"),
|
||||||
|
allowRedirects = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.headers["location"]
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun bypassMirrored(url: String): List<String?> {
|
||||||
|
val request = app.get(url)
|
||||||
|
val hostUrl = getBaseUrl(request.url)
|
||||||
|
var nextUrl = request.document.selectFirst("div.row div.centered a")?.attr("href")
|
||||||
|
nextUrl = app.get(nextUrl ?: return emptyList()).text.substringAfter("\"GET\", \"")
|
||||||
|
.substringBefore("\"")
|
||||||
|
return app.get(fixUrl(nextUrl, hostUrl)).document.select("table.hoverable tbody tr")
|
||||||
|
.filter { mirror ->
|
||||||
|
!mirrorIsBlackList(mirror.selectFirst("img")?.attr("alt"))
|
||||||
|
}.apmap {
|
||||||
|
val fileLink = it.selectFirst("a")?.attr("href")
|
||||||
|
app.get(
|
||||||
|
fixUrl(
|
||||||
|
fileLink.toString(),
|
||||||
|
hostUrl
|
||||||
|
)
|
||||||
|
).document.selectFirst("div.code_wrap code")?.text()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mirrorIsBlackList(host: String?) : Boolean {
|
||||||
|
return mirrorBlackList.any { it.equals(host, true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fixUrl(url: String, domain: String): String {
|
||||||
|
if (url.startsWith("http")) {
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
if (url.isEmpty()) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
val startsWithNoHttp = url.startsWith("//")
|
||||||
|
if (startsWithNoHttp) {
|
||||||
|
return "https:$url"
|
||||||
|
} else {
|
||||||
|
if (url.startsWith('/')) {
|
||||||
|
return domain + url
|
||||||
|
}
|
||||||
|
return "$domain/$url"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getIndexQuality(str: String?): Int {
|
||||||
|
return Regex("(\\d{3,4})[pP]").find(str ?: "")?.groupValues?.getOrNull(1)?.toIntOrNull()
|
||||||
|
?: Qualities.Unknown.value
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
13
Nekopoi/src/main/kotlin/com/hexated/NekopoiPlugin.kt
Normal file
13
Nekopoi/src/main/kotlin/com/hexated/NekopoiPlugin.kt
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package com.hexated
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||||
|
import com.lagradost.cloudstream3.plugins.Plugin
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
@CloudstreamPlugin
|
||||||
|
class NekopoiPlugin: Plugin() {
|
||||||
|
override fun load(context: Context) {
|
||||||
|
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||||
|
registerMainAPI(Nekopoi())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
|
|
||||||
class Ngefilm : MainAPI() {
|
class Ngefilm : MainAPI() {
|
||||||
override var mainUrl = "https://ngefilm21.club"
|
override var mainUrl = "https://ngefilm21.cfd"
|
||||||
override var name = "Ngefilm"
|
override var name = "Ngefilm"
|
||||||
override val hasMainPage = true
|
override val hasMainPage = true
|
||||||
override var lang = "id"
|
override var lang = "id"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 5
|
version = 6
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.hexated
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
|
|
||||||
class Kitanonton : RebahinProvider() {
|
class Kitanonton : RebahinProvider() {
|
||||||
override var mainUrl = "http://kitanonton.org"
|
override var mainUrl = "http://kitanonton.site"
|
||||||
override var name = "KitaNonton"
|
override var name = "KitaNonton"
|
||||||
override var mainServer = "https://199.87.210.226"
|
override var mainServer = "https://199.87.210.226"
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,6 @@ class RebahinProviderPlugin: 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(RebahinProvider())
|
registerMainAPI(RebahinProvider())
|
||||||
// registerMainAPI(Kitanonton())
|
registerMainAPI(Kitanonton())
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -19,7 +19,7 @@ open class TimefourTv : MainAPI() {
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val daddyUrl = "https://daddylivehd.sx"
|
const val daddyUrl = "https://daddylive.watch"
|
||||||
val daddyHost: String = URI(daddyUrl).host.split(".").first()
|
val daddyHost: String = URI(daddyUrl).host.split(".").first()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 14
|
version = 15
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import org.jsoup.nodes.Element
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
||||||
class YomoviesProvider : MainAPI() {
|
class YomoviesProvider : MainAPI() {
|
||||||
override var mainUrl = "https://yomovies.team"
|
override var mainUrl = "https://yomovies.baby"
|
||||||
private var directUrl = mainUrl
|
private var directUrl = mainUrl
|
||||||
override var name = "Yomovies"
|
override var name = "Yomovies"
|
||||||
override val hasMainPage = true
|
override val hasMainPage = true
|
||||||
|
@ -142,7 +142,7 @@ class YomoviesProvider : MainAPI() {
|
||||||
callback: (ExtractorLink) -> Unit
|
callback: (ExtractorLink) -> Unit
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
|
||||||
if (data.contains("yomovies")) {
|
if (data.contains(directUrl.getHost(), true)) {
|
||||||
val doc = app.get(data).document
|
val doc = app.get(data).document
|
||||||
doc.select("div.movieplay iframe").map { fixUrl(it.attr("src")) }
|
doc.select("div.movieplay iframe").map { fixUrl(it.attr("src")) }
|
||||||
.apmap { source ->
|
.apmap { source ->
|
||||||
|
@ -171,5 +171,8 @@ class YomoviesProvider : MainAPI() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun String.getHost(): String {
|
||||||
|
return fixTitle(URI(this).host.substringBeforeLast(".").substringAfterLast("."))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue