Merge branch 'hexated:master' into master

This commit is contained in:
tuan041 2023-07-19 23:29:52 +07:00 committed by GitHub
commit ded26f7fe0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 1068 additions and 490 deletions

View file

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

View file

@ -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()
)
} }

View 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
)
})
}
}

View 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,
)

View 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")
}
}

View file

@ -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"
} }

View file

@ -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",

View file

@ -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"

View file

@ -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

View file

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

View file

@ -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"
}

View file

@ -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())
} }
} }

View file

@ -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"
}

View file

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

View file

@ -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,

View file

@ -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"
} }

View file

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

View file

@ -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)
}
}

View file

@ -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())
} }
} }

View file

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

View file

@ -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"

View file

@ -12,7 +12,7 @@ import org.jsoup.nodes.Element
import java.net.URI import java.net.URI
class Movierulzhd : MainAPI() { class Movierulzhd : MainAPI() {
override var mainUrl = "https://movierulzhd.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

View file

@ -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
View 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%"
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.hexated"/>

View 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
}
}

View 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())
}
}

View file

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

View file

@ -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"

View file

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

View file

@ -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"

View file

@ -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())
} }
} }

View file

@ -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()
} }

View file

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

View file

@ -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("."))
}
} }