mirror of
https://github.com/hexated/cloudstream-extensions-hexated.git
synced 2024-08-15 00:03:22 +00:00
Just Keep SoraStream
This commit is contained in:
parent
8f805ffbcb
commit
54f74509a8
246 changed files with 0 additions and 16895 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"java.configuration.updateBuildConfiguration": "interactive"
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import org.jetbrains.kotlin.konan.properties.Properties
|
||||
|
||||
// use an integer for version numbers
|
||||
version = 4
|
||||
|
||||
android {
|
||||
defaultConfig {
|
||||
val properties = Properties()
|
||||
properties.load(project.rootProject.file("local.properties").inputStream())
|
||||
|
||||
buildConfigField("String", "ANICHI_API", "\"${properties.getProperty("ANICHI_API")}\"")
|
||||
buildConfigField("String", "ANICHI_SERVER", "\"${properties.getProperty("ANICHI_SERVER")}\"")
|
||||
buildConfigField("String", "ANICHI_ENDPOINT", "\"${properties.getProperty("ANICHI_ENDPOINT")}\"")
|
||||
buildConfigField("String", "ANICHI_APP", "\"${properties.getProperty("ANICHI_APP")}\"")
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
cloudstream {
|
||||
language = "en"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AnimeMovie",
|
||||
"Anime",
|
||||
"OVA",
|
||||
)
|
||||
|
||||
iconUrl = "https://media.discordapp.net/attachments/1059306855865782282/1123970193274712096/Anichi.png"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated" />
|
|
@ -1,609 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.getTracker
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
||||
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.AppUtils.parseJson
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.nicehttp.RequestBodyTypes
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import java.net.URI
|
||||
|
||||
class Anichi : MainAPI() {
|
||||
override var name = "Anichi"
|
||||
override val instantLinkLoading = true
|
||||
override val hasQuickSearch = false
|
||||
override val hasMainPage = true
|
||||
|
||||
private fun getStatus(t: String): ShowStatus {
|
||||
return when (t) {
|
||||
"Finished" -> ShowStatus.Completed
|
||||
"Releasing" -> ShowStatus.Ongoing
|
||||
else -> ShowStatus.Completed
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
private val popularTitle = "Popular"
|
||||
private val animeRecentTitle = "Latest Anime"
|
||||
private val donghuaRecentTitle = "Latest Donghua"
|
||||
private val movieTitle = "Movie"
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"""$apiUrl?variables={"search":{"sortBy":"Latest_Update","allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false},"limit":26,"page":%d,"translationType":"sub","countryOrigin":"JP"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" to animeRecentTitle,
|
||||
"""$apiUrl?variables={"search":{"sortBy":"Latest_Update","allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false},"limit":26,"page":%d,"translationType":"sub","countryOrigin":"CN"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}""" to donghuaRecentTitle,
|
||||
"""$apiUrl?variables={"type":"anime","size":30,"dateRange":1,"page":%d,"allowAdult":${settingsForProvider.enableAdult},"allowUnknown":false}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$popularHash"}}""" to popularTitle,
|
||||
"""$apiUrl?variables={"search":{"slug":"movie-anime","format":"anime","tagType":"upcoming","name":"Trending Movies"}}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$slugHash"}}""" to movieTitle
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||
|
||||
val url = request.data.format(page)
|
||||
val res = app.get(url, headers = headers).parsedSafe<AnichiQuery>()?.data
|
||||
val query = res?.shows ?: res?.queryPopular ?: res?.queryListForTag
|
||||
val card = if(request.name == popularTitle) query?.recommendations?.map { it.anyCard } else query?.edges
|
||||
val home = card?.filter {
|
||||
// filtering in case there is an anime with 0 episodes available on the site.
|
||||
!(it?.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
|
||||
}?.mapNotNull { media ->
|
||||
media?.toSearchResponse()
|
||||
} ?: emptyList()
|
||||
return newHomePageResponse(
|
||||
list = HomePageList(
|
||||
name = request.name,
|
||||
list = home,
|
||||
),
|
||||
hasNext = request.name != movieTitle
|
||||
)
|
||||
}
|
||||
|
||||
private fun Edges.toSearchResponse(): AnimeSearchResponse? {
|
||||
|
||||
return newAnimeSearchResponse(
|
||||
name ?: englishName ?: nativeName ?: "",
|
||||
Id ?: return null,
|
||||
fix = false
|
||||
) {
|
||||
this.posterUrl = thumbnail
|
||||
this.year = airedStart?.year
|
||||
this.otherName = englishName
|
||||
addDub(availableEpisodes?.dub)
|
||||
addSub(availableEpisodes?.sub)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse>? {
|
||||
|
||||
val link =
|
||||
"""$apiUrl?variables={"search":{"allowAdult":false,"allowUnknown":false,"query":"$query"},"limit":26,"page":1,"translationType":"sub","countryOrigin":"ALL"}&extensions={"persistedQuery":{"version":1,"sha256Hash":"$mainHash"}}"""
|
||||
val res = app.get(
|
||||
link,
|
||||
headers = headers
|
||||
).text.takeUnless { it.contains("PERSISTED_QUERY_NOT_FOUND") }
|
||||
// Retries
|
||||
?: app.get(
|
||||
link,
|
||||
headers = headers
|
||||
).text.takeUnless { it.contains("PERSISTED_QUERY_NOT_FOUND") }
|
||||
?: return emptyList()
|
||||
|
||||
val response = parseJson<AnichiQuery>(res)
|
||||
|
||||
val results = response.data?.shows?.edges?.filter {
|
||||
// filtering in case there is an anime with 0 episodes available on the site.
|
||||
!(it.availableEpisodes?.raw == 0 && it.availableEpisodes.sub == 0 && it.availableEpisodes.dub == 0)
|
||||
}
|
||||
|
||||
return results?.map {
|
||||
newAnimeSearchResponse(it.name ?: "", "${it.Id}", fix = false) {
|
||||
this.posterUrl = it.thumbnail
|
||||
this.year = it.airedStart?.year
|
||||
this.otherName = it.englishName
|
||||
addDub(it.availableEpisodes?.dub)
|
||||
addSub(it.availableEpisodes?.sub)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
|
||||
val id = url.substringAfterLast("/")
|
||||
// lazy to format
|
||||
val body = """
|
||||
{
|
||||
"query": " query(\n ${'$'}_id: String!\n ) {\n show(\n _id: ${'$'}_id\n ) {\n _id\n name\n description\n thumbnail\n thumbnails\n lastEpisodeInfo\n lastEpisodeDate \n type\n genres\n score\n status\n season\n altNames \n averageScore\n rating\n episodeCount\n episodeDuration\n broadcastInterval\n banner\n airedEnd\n airedStart \n studios\n characters\n availableEpisodesDetail\n availableEpisodes\n prevideos\n nameOnlyString\n relatedShows\n relatedMangas\n musics\n isAdult\n \n tags\n countryOfOrigin\n\n pageStatus{\n _id\n notes\n pageId\n showId\n \n # ranks:[Object]\n views\n likesCount\n commentCount\n dislikesCount\n reviewCount\n userScoreCount\n userScoreTotalValue\n userScoreAverValue\n viewers{\n firstViewers{\n viewCount\n lastWatchedDate\n user{\n _id\n displayName\n picture\n # description\n hideMe\n # createdAt\n # badges\n brief\n }\n \n }\n recViewers{\n viewCount\n lastWatchedDate\n user{\n _id\n displayName\n picture\n # description\n hideMe\n # createdAt\n # badges\n brief\n }\n \n }\n }\n\n }\n }\n }",
|
||||
"extensions": "{\"persistedQuery\":{\"version\":1,\"sha256Hash\":\"$detailHash\"}}",
|
||||
"variables": "{\"_id\":\"$id\"}"
|
||||
}
|
||||
""".trimIndent().trim().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull())
|
||||
|
||||
val res = app.post(apiUrl, requestBody = body, headers = headers)
|
||||
val showData = res.parsedSafe<Detail>()?.data?.show ?: return null
|
||||
|
||||
val title = showData.name
|
||||
val description = showData.description
|
||||
val poster = showData.thumbnail
|
||||
val type = getType(showData.type ?: "")
|
||||
|
||||
val episodes = showData.availableEpisodesDetail.let {
|
||||
if (it == null) return@let Pair(null, null)
|
||||
if (showData.Id == null) return@let Pair(null, null)
|
||||
Pair(
|
||||
it.getEpisode("sub", showData.Id),
|
||||
it.getEpisode("dub", showData.Id),
|
||||
)
|
||||
}
|
||||
|
||||
val characters = showData.characters?.map {
|
||||
val role = when (it.role) {
|
||||
"Main" -> ActorRole.Main
|
||||
"Supporting" -> ActorRole.Supporting
|
||||
"Background" -> ActorRole.Background
|
||||
else -> null
|
||||
}
|
||||
val name = it.name?.full ?: it.name?.native ?: ""
|
||||
val image = it.image?.large ?: it.image?.medium
|
||||
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) {
|
||||
engName = showData.altNames?.firstOrNull()
|
||||
posterUrl = trackers?.image ?: poster
|
||||
backgroundPosterUrl = trackers?.cover ?: showData.banner
|
||||
rating = showData.averageScore?.times(100)
|
||||
tags = showData.genres
|
||||
year = showData.airedStart?.year
|
||||
duration = showData.episodeDuration?.div(60_000)
|
||||
addTrailer(showData.prevideos.filter { it.isNotBlank() }
|
||||
.map { "https://www.youtube.com/watch?v=$it" })
|
||||
|
||||
addEpisodes(DubStatus.Subbed, episodes.first)
|
||||
addEpisodes(DubStatus.Dubbed, episodes.second)
|
||||
addActors(characters)
|
||||
//this.recommendations = recommendations
|
||||
|
||||
showStatus = getStatus(showData.status.toString())
|
||||
addMalId(trackers?.malId)
|
||||
addAniListId(trackers?.aniId?.toIntOrNull())
|
||||
plot = description?.replace(Regex("""<(.*?)>"""), "")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
val loadData = parseJson<AnichiLoadData>(data)
|
||||
|
||||
val apiUrl =
|
||||
"""$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>()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
private const val apiUrl = BuildConfig.ANICHI_API
|
||||
private const val serverUrl = BuildConfig.ANICHI_SERVER
|
||||
private const val apiEndPoint = BuildConfig.ANICHI_ENDPOINT
|
||||
|
||||
private const val mainHash = "e42a4466d984b2c0a2cecae5dd13aa68867f634b16ee0f17b380047d14482406"
|
||||
private const val popularHash = "31a117653812a2547fd981632e8c99fa8bf8a75c4ef1a77a1567ef1741a7ab9c"
|
||||
private const val slugHash = "bf603205eb2533ca21d0324a11f623854d62ed838a27e1b3fcfb712ab98b03f4"
|
||||
private const val detailHash = "bb263f91e5bdd048c1c978f324613aeccdfe2cbc694a419466a31edb58c0cc0b"
|
||||
private const val serverHash = "5e7e17cdd0166af5a2d8f43133d9ce3ce9253d1fdb5160a0cfd515564f98d061"
|
||||
|
||||
private val headers = mapOf(
|
||||
"app-version" to "android_c-247",
|
||||
"from-app" to BuildConfig.ANICHI_APP,
|
||||
"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()
|
||||
)
|
||||
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class AnichiPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Anichi())
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 1
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "de"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 0 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AnimeMovie",
|
||||
"Anime",
|
||||
"OVA",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=anifreakz.com&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,193 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import com.lagradost.cloudstream3.utils.getAndUnpack
|
||||
import org.jsoup.nodes.Element
|
||||
import java.net.URLDecoder
|
||||
|
||||
class Anifreakz : MainAPI() {
|
||||
override var mainUrl = "https://anifreakz.com"
|
||||
override var name = "Anifreakz"
|
||||
override val hasMainPage = true
|
||||
override var lang = "de"
|
||||
override val hasDownloadSupport = true
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Anime,
|
||||
TvType.AnimeMovie,
|
||||
TvType.OVA
|
||||
)
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/series?filter=null&page=" to "Anime Neuste",
|
||||
"$mainUrl/series?filter={\"sorting\":\"popular\"}&page=" to "Anime Angesagt",
|
||||
"$mainUrl/series?filter={\"sorting\":\"released\"}&page=" to "Anime Veröffentlicht",
|
||||
"$mainUrl/kalender" to "Anime Kalender",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val items = mutableListOf<HomePageList>()
|
||||
val nonPaged = request.name == "Anime Kalender" && page <= 1
|
||||
|
||||
if (!nonPaged) {
|
||||
val document = app.get(request.data + page).document
|
||||
val home = document.select("div.app-section div.col").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
items.add(HomePageList(request.name, home))
|
||||
}
|
||||
|
||||
if (nonPaged) {
|
||||
val document = app.get(request.data).document
|
||||
document.select("div.app-content div.app-section").forEach { block ->
|
||||
val header = block.selectFirst("div.app-heading div.text")?.ownText() ?: ""
|
||||
val home = block.select("div.col").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
items.add(HomePageList(header, home))
|
||||
}
|
||||
}
|
||||
|
||||
return newHomePageResponse(items)
|
||||
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): AnimeSearchResponse? {
|
||||
val href = fixUrl(this.selectFirst("a")?.attr("href") ?: return null)
|
||||
val title = this.selectFirst("a.list-title")?.text()?.trim() ?: return null
|
||||
val posterUrl = fixUrlNull(this.selectFirst("div.media-cover")?.attr("data-src"))
|
||||
|
||||
return newAnimeSearchResponse(title, href, TvType.Anime) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val document = app.get("$mainUrl/search/$query").document
|
||||
return document.select("div.app-section div.col").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
}
|
||||
|
||||
private fun decode(input: String): String? = URLDecoder.decode(input, "utf-8")
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title =
|
||||
document.selectFirst("div.pl-md-4 h1, div.caption-content h1")?.text() ?: return null
|
||||
val poster = fixUrlNull(document.selectFirst("div.media.media-cover")?.attr("data-src"))
|
||||
val tags = document.select("div.categories a").map { it.text() }
|
||||
val rating = document.selectFirst("div.featured-box div:contains(IMDB) div.text")?.text()
|
||||
.toRatingInt()
|
||||
val year =
|
||||
document.selectFirst("div.featured-box div:contains(Veröffentlicht) div.text")?.text()
|
||||
?.trim()?.toIntOrNull()
|
||||
val type = if (document.select("div.episodes.tab-content")
|
||||
.isNullOrEmpty()
|
||||
) TvType.Movie else TvType.TvSeries
|
||||
val description =
|
||||
document.select("div.detail-attr div.text-content, div.caption div:contains(Übersicht) div.text")
|
||||
.text().trim()
|
||||
val trailer = document.selectFirst("button:contains(Trailer)")?.attr("data-remote")
|
||||
?.substringAfter("?trailer=")?.let {
|
||||
decode(it)
|
||||
}
|
||||
|
||||
return if (type == TvType.Movie) {
|
||||
newMovieLoadResponse(title, url, type, url) {
|
||||
posterUrl = poster
|
||||
this.year = year
|
||||
this.rating = rating
|
||||
plot = description
|
||||
this.tags = tags
|
||||
addTrailer(trailer)
|
||||
}
|
||||
} else {
|
||||
val subEpisodes = mutableListOf<Episode>()
|
||||
document.select("div.episodes.tab-content div[id*=season-]").forEach { season ->
|
||||
val seasonNum = season.attr("id").substringAfterLast("-").toIntOrNull()
|
||||
season.select("a").map { eps ->
|
||||
val href = eps.attr("href")
|
||||
val name = eps.select("div.name").text()
|
||||
val episode =
|
||||
eps.select("div.episode").text().substringBefore(".").toIntOrNull()
|
||||
val episodes = Episode(
|
||||
href,
|
||||
name,
|
||||
seasonNum,
|
||||
episode
|
||||
)
|
||||
subEpisodes.add(episodes)
|
||||
}
|
||||
}
|
||||
newAnimeLoadResponse(title, url, type) {
|
||||
posterUrl = poster
|
||||
this.year = year
|
||||
addEpisodes(DubStatus.Subbed, subEpisodes)
|
||||
this.rating = rating
|
||||
plot = description
|
||||
this.tags = tags
|
||||
addTrailer(trailer)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
val document = app.get(data).document
|
||||
|
||||
val servers = mutableListOf<Pair<String, String>>()
|
||||
|
||||
document.select("div[aria-labelledby=videoSource] button").map {
|
||||
servers.add(it.attr("data-embed") to it.text())
|
||||
}
|
||||
|
||||
document.select("div.nav-player-select a").map {
|
||||
servers.add(it.attr("data-embed") to it.select("span").text())
|
||||
}
|
||||
|
||||
servers.distinctBy { it.first }.apmap { (id, source) ->
|
||||
val iframe = app.post(
|
||||
"$mainUrl/ajax/embed",
|
||||
data = mapOf("id" to id, "captcha" to ""),
|
||||
headers = mapOf(
|
||||
"X-Requested-With" to "XMLHttpRequest"
|
||||
),
|
||||
referer = data
|
||||
).document.select("iframe").attr("src")
|
||||
val doc = app.get(iframe, referer = "$mainUrl/").document
|
||||
val link =
|
||||
unpackJs(doc)?.substringAfter("file:\"")?.substringBefore("\"") ?: return@apmap null
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
source,
|
||||
source,
|
||||
link,
|
||||
"https://filemoon.sx/",
|
||||
Qualities.Unknown.value,
|
||||
isM3u8 = link.contains(".m3u8")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun unpackJs(script: Element): String? {
|
||||
return script.select("script").find { it.data().contains("eval(function(p,a,c,k,e,d)") }
|
||||
?.data()?.let { getAndUnpack(it) }
|
||||
}
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class AnifreakzPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Anifreakz())
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 2
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "ru"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AnimeMovie",
|
||||
"OVA",
|
||||
"Anime",
|
||||
)
|
||||
|
||||
iconUrl = "https://raw.githubusercontent.com/hexated/cloudstream-extensions-hexated/master/Anilibria/icon.png"
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 48 KiB |
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,197 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class Anilibria : MainAPI() {
|
||||
override var mainUrl = "https://anilibria.tv"
|
||||
override var name = "Anilibria"
|
||||
override val hasMainPage = true
|
||||
override var lang = "ru"
|
||||
override val hasDownloadSupport = true
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Anime,
|
||||
TvType.AnimeMovie,
|
||||
TvType.OVA
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun getType(t: String): TvType {
|
||||
return when {
|
||||
t.contains("Фильм", true) -> TvType.Movie
|
||||
t.contains("ТВ", true) -> TvType.Anime
|
||||
else -> TvType.OVA
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"1" to "Новое",
|
||||
"2" to "Популярное",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val document = app.post(
|
||||
"$mainUrl/public/catalog.php", data = mapOf(
|
||||
"page" to "$page",
|
||||
"xpage" to "catalog",
|
||||
"sort" to request.data,
|
||||
"finish" to "1",
|
||||
"search" to "{\"year\":\"\",\"genre\":\"\",\"season\":\"\"}"
|
||||
), headers = mapOf("X-Requested-With" to "XMLHttpRequest")
|
||||
).parsedSafe<Home>()?.table?.let { Jsoup.parse(it) }
|
||||
val home = document?.select("a")?.mapNotNull {
|
||||
it.toSearchResult()
|
||||
} ?: throw ErrorLoadingException("Invalid json responses")
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): AnimeSearchResponse? {
|
||||
val href = fixUrl(this.attr("href"))
|
||||
val title = this.selectFirst("span")?.text() ?: return null
|
||||
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
|
||||
|
||||
return newAnimeSearchResponse(title, href, TvType.Anime) {
|
||||
this.posterUrl = posterUrl
|
||||
addDubStatus(isDub = true)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val document = app.post(
|
||||
"$mainUrl/public/search.php",
|
||||
data = mapOf("search" to query, "small" to "1"),
|
||||
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
|
||||
).parsedSafe<Search>()?.mes?.let { Jsoup.parse(it) }
|
||||
|
||||
return document?.select("a")?.mapNotNull {
|
||||
it.toSearchResult()
|
||||
} ?: throw ErrorLoadingException("Invalid json responses")
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("h1.release-title")?.text() ?: return null
|
||||
val poster = fixUrlNull(document.selectFirst("img#adminPoster")?.attr("src"))
|
||||
val trackTitle = (document.selectFirst("h1.release-title br")?.nextSibling()
|
||||
?: document.selectFirst("h1.release-title")?.text()?.substringAfter("/")?.trim()).toString()
|
||||
val type = document.selectFirst("div#xreleaseInfo b:contains(Тип:)")?.nextSibling()
|
||||
.toString().substringBefore(",").trim()
|
||||
val trackType = type.let {
|
||||
if(it.contains("Фильм", true)) "movie" else "tv"
|
||||
}
|
||||
val year = document.selectFirst("div#xreleaseInfo b:contains(Сезон:)")?.nextElementSibling()
|
||||
?.text()?.filter { it.isDigit() }?.toIntOrNull()
|
||||
val (malId, anilistId, image, cover) = getTracker(trackTitle, trackType, year)
|
||||
val episodes = document.select("script").find { it.data().contains("var player =") }?.data()
|
||||
?.substringAfter("file:[")?.substringBefore("],")?.let { data ->
|
||||
tryParseJson<List<Episodes>>("[$data]")?.mapNotNull { eps ->
|
||||
Episode(
|
||||
eps.file ?: return@mapNotNull null,
|
||||
name = eps.title ?: return@mapNotNull null,
|
||||
posterUrl = fixUrlNull(eps.poster),
|
||||
)
|
||||
}
|
||||
}
|
||||
return newAnimeLoadResponse(title, url, getType(type)) {
|
||||
posterUrl = image ?: poster
|
||||
backgroundPosterUrl = cover ?: image ?: poster
|
||||
this.year = year
|
||||
addEpisodes(DubStatus.Subbed, episodes)
|
||||
plot = document.select("p.detail-description").text().trim()
|
||||
this.tags = document.selectFirst("div#xreleaseInfo b:contains(Жанры:)")?.nextSibling()
|
||||
.toString().split(",").map { it.trim() }
|
||||
addMalId(malId)
|
||||
addAniListId(anilistId?.toIntOrNull())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
data.split(",").map { it.trim() }.map { m3u ->
|
||||
val quality = Regex("\\[([0-9]+p)]").find(m3u)?.groupValues?.getOrNull(1)
|
||||
val link = m3u.removePrefix("[$quality]").trim()
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
this.name,
|
||||
this.name,
|
||||
link,
|
||||
"$mainUrl/",
|
||||
getQualityFromName(quality),
|
||||
true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun getTracker(title: String?, type: String?, year: Int?): Tracker {
|
||||
val res = app.get("https://api.consumet.org/meta/anilist/$title")
|
||||
.parsedSafe<AniSearch>()?.results?.find { media ->
|
||||
(media.title?.english.equals(title, true) || media.title?.romaji.equals(
|
||||
title,
|
||||
true
|
||||
)) || (media.type.equals(type, true) && media.releaseDate == year)
|
||||
}
|
||||
return Tracker(res?.malId, res?.aniId, res?.image, res?.cover)
|
||||
}
|
||||
|
||||
data class Tracker(
|
||||
val malId: Int? = null,
|
||||
val aniId: String? = null,
|
||||
val image: String? = null,
|
||||
val cover: String? = null,
|
||||
)
|
||||
|
||||
data class Title(
|
||||
@JsonProperty("romaji") val romaji: String? = null,
|
||||
@JsonProperty("english") val english: String? = null,
|
||||
)
|
||||
|
||||
data class Results(
|
||||
@JsonProperty("id") val aniId: String? = null,
|
||||
@JsonProperty("malId") val malId: Int? = null,
|
||||
@JsonProperty("title") val title: Title? = null,
|
||||
@JsonProperty("releaseDate") val releaseDate: Int? = null,
|
||||
@JsonProperty("type") val type: String? = null,
|
||||
@JsonProperty("image") val image: String? = null,
|
||||
@JsonProperty("cover") val cover: String? = null,
|
||||
)
|
||||
|
||||
data class AniSearch(
|
||||
@JsonProperty("results") val results: ArrayList<Results>? = arrayListOf(),
|
||||
)
|
||||
|
||||
private data class Episodes(
|
||||
@JsonProperty("file") val file: String? = null,
|
||||
@JsonProperty("title") val title: String? = null,
|
||||
@JsonProperty("poster") val poster: String? = null,
|
||||
)
|
||||
|
||||
private data class Home(
|
||||
@JsonProperty("table") val table: String? = null,
|
||||
)
|
||||
|
||||
private data class Search(
|
||||
@JsonProperty("mes") val mes: String? = null,
|
||||
)
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class AnilibriaPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Anilibria())
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 10
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "id"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AnimeMovie",
|
||||
"OVA",
|
||||
"Anime",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=animeindo.fun&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated" />
|
|
@ -1,169 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.httpsify
|
||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class AnimeIndoProvider : MainAPI() {
|
||||
override var mainUrl = "https://animeindo.quest"
|
||||
override var name = "AnimeIndo"
|
||||
override val hasMainPage = true
|
||||
override var lang = "id"
|
||||
override val hasDownloadSupport = true
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Anime,
|
||||
TvType.AnimeMovie,
|
||||
TvType.OVA
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun getType(t: String): TvType {
|
||||
return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA
|
||||
else if (t.contains("Movie", true)) TvType.AnimeMovie
|
||||
else TvType.Anime
|
||||
}
|
||||
|
||||
fun getStatus(t: String): ShowStatus {
|
||||
return when (t) {
|
||||
"Finished Airing" -> ShowStatus.Completed
|
||||
"Currently Airing" -> ShowStatus.Ongoing
|
||||
else -> ShowStatus.Completed
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/anime-terbaru/page/" to "Anime Terbaru",
|
||||
"$mainUrl/ongoing/page/" to "Anime Ongoing",
|
||||
"$mainUrl/populer/page/" to "Anime Populer",
|
||||
"$mainUrl/donghua-terbaru/page/" to "Donghua Terbaru",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val document = app.get(request.data + page).document
|
||||
val home = document.select("div.post-show > article, div.relat > article").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun getProperAnimeLink(uri: String): String {
|
||||
return if (uri.contains("/anime/")) {
|
||||
uri
|
||||
} else {
|
||||
var title = uri.substringAfter("$mainUrl/")
|
||||
title = when {
|
||||
(title.contains("-episode")) && !(title.contains("-movie")) -> Regex("(.+)-episode").find(
|
||||
title
|
||||
)?.groupValues?.get(1).toString()
|
||||
(title.contains("-movie")) -> Regex("(.+)-movie").find(title)?.groupValues?.get(
|
||||
1
|
||||
).toString()
|
||||
else -> title
|
||||
}
|
||||
"$mainUrl/anime/$title"
|
||||
}
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): AnimeSearchResponse? {
|
||||
val title = this.selectFirst("div.title")?.text()?.trim() ?: return null
|
||||
val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href"))
|
||||
val posterUrl = this.select("img[itemprop=image]").attr("src").toString()
|
||||
val type = getType(this.select("div.type").text().trim())
|
||||
val epNum =
|
||||
this.selectFirst("span.episode")?.ownText()?.replace(Regex("\\D"), "")?.trim()
|
||||
?.toIntOrNull()
|
||||
return newAnimeSearchResponse(title, href, type) {
|
||||
this.posterUrl = posterUrl
|
||||
addSub(epNum)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val anime = mutableListOf<SearchResponse>()
|
||||
(1..2).forEach { page ->
|
||||
val link = "$mainUrl/page/$page/?s=$query"
|
||||
val document = app.get(link).document
|
||||
val media = document.select(".site-main.relat > article").mapNotNull {
|
||||
val title = it.selectFirst("div.title > h2")!!.ownText().trim()
|
||||
val href = it.selectFirst("a")!!.attr("href")
|
||||
val posterUrl = it.selectFirst("img")!!.attr("src").toString()
|
||||
val type = getType(it.select("div.type").text().trim())
|
||||
newAnimeSearchResponse(title, href, type) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
}
|
||||
if(media.isNotEmpty()) anime.addAll(media)
|
||||
}
|
||||
return anime
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("h1.entry-title")?.text()?.replace("Subtitle Indonesia", "")
|
||||
?.trim() ?: return null
|
||||
val poster = document.selectFirst("div.thumb > img[itemprop=image]")?.attr("src")
|
||||
val tags = document.select("div.genxed > a").map { it.text() }
|
||||
val type = document.selectFirst("div.info-content > div.spe > span:contains(Type:)")?.ownText()
|
||||
?.trim()?.lowercase() ?: "tv"
|
||||
val year = document.selectFirst("div.info-content > div.spe > span:contains(Released:)")?.ownText()?.let {
|
||||
Regex("\\d,\\s(\\d*)").find(it)?.groupValues?.get(1)?.toIntOrNull()
|
||||
}
|
||||
val status = getStatus(document.selectFirst("div.info-content > div.spe > span:nth-child(1)")!!.ownText().trim())
|
||||
val description = document.select("div[itemprop=description] > p").text()
|
||||
|
||||
val trailer = document.selectFirst("div.player-embed iframe")?.attr("src")
|
||||
val episodes = document.select("div.lstepsiode.listeps ul li").mapNotNull {
|
||||
val header = it.selectFirst("span.lchx > a") ?: return@mapNotNull null
|
||||
val episode = header.text().trim().replace("Episode", "").trim().toIntOrNull()
|
||||
val link = fixUrl(header.attr("href"))
|
||||
Episode(link, header.text(), episode = episode)
|
||||
}.reversed()
|
||||
|
||||
return newAnimeLoadResponse(title, url, getType(type)) {
|
||||
engName = title
|
||||
posterUrl = poster
|
||||
this.year = year
|
||||
addEpisodes(DubStatus.Subbed, episodes)
|
||||
showStatus = status
|
||||
plot = description
|
||||
this.tags = tags
|
||||
addTrailer(trailer)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
val document = app.get(data).document
|
||||
document.select("div.itemleft > .mirror > option").mapNotNull {
|
||||
fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src"))
|
||||
}.apmap {
|
||||
if (it.startsWith(mainUrl)) {
|
||||
app.get(it, referer = "$mainUrl/").document.select("iframe").attr("src")
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}.apmap {
|
||||
loadExtractor(httpsify(it), data, subtitleCallback, callback)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class AnimeIndoProviderPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(AnimeIndoProvider())
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 8
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "id"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AnimeMovie",
|
||||
"Anime",
|
||||
"OVA",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=111.90.143.42&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated" />
|
|
@ -1,196 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||
import com.lagradost.nicehttp.NiceResponse
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class AnimeSailProvider : MainAPI() {
|
||||
override var mainUrl = "https://154.26.137.28"
|
||||
override var name = "AnimeSail"
|
||||
override val hasMainPage = true
|
||||
override var lang = "id"
|
||||
override val hasDownloadSupport = true
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Anime,
|
||||
TvType.AnimeMovie,
|
||||
TvType.OVA
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun getType(t: String): TvType {
|
||||
return if (t.contains("OVA", true) || t.contains("Special")) TvType.OVA
|
||||
else if (t.contains("Movie", true)) TvType.AnimeMovie
|
||||
else TvType.Anime
|
||||
}
|
||||
|
||||
fun getStatus(t: String): ShowStatus {
|
||||
return when (t) {
|
||||
"Completed" -> ShowStatus.Completed
|
||||
"Ongoing" -> ShowStatus.Ongoing
|
||||
else -> ShowStatus.Completed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun request(url: String, ref: String? = null): NiceResponse {
|
||||
return app.get(
|
||||
url,
|
||||
headers = mapOf("Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"),
|
||||
cookies = mapOf("_as_ipin_ct" to "ID"),
|
||||
referer = ref
|
||||
)
|
||||
}
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/page/" to "Episode Terbaru",
|
||||
"$mainUrl/movie-terbaru/page/" to "Movie Terbaru",
|
||||
"$mainUrl/genres/donghua/page/" to "Donghua"
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||
val document = request(request.data + page).document
|
||||
val home = document.select("article").map {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun getProperAnimeLink(uri: String): String {
|
||||
return if (uri.contains("/anime/")) {
|
||||
uri
|
||||
} else {
|
||||
var title = uri.substringAfter("$mainUrl/")
|
||||
title = when {
|
||||
(title.contains("-episode")) && !(title.contains("-movie")) -> title.substringBefore(
|
||||
"-episode"
|
||||
)
|
||||
(title.contains("-movie")) -> title.substringBefore("-movie")
|
||||
else -> title
|
||||
}
|
||||
|
||||
"$mainUrl/anime/$title"
|
||||
}
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): AnimeSearchResponse {
|
||||
val href = getProperAnimeLink(fixUrlNull(this.selectFirst("a")?.attr("href")).toString())
|
||||
val title = this.select(".tt > h2").text().trim()
|
||||
val posterUrl = fixUrlNull(this.selectFirst("div.limit img")?.attr("src"))
|
||||
val epNum = this.selectFirst(".tt > h2")?.text()?.let {
|
||||
Regex("Episode\\s?(\\d+)").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull()
|
||||
}
|
||||
return newAnimeSearchResponse(title, href, TvType.Anime) {
|
||||
this.posterUrl = posterUrl
|
||||
addSub(epNum)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val link = "$mainUrl/?s=$query"
|
||||
val document = request(link).document
|
||||
|
||||
return document.select("div.listupd article").map {
|
||||
it.toSearchResult()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse {
|
||||
val document = request(url).document
|
||||
|
||||
val title = document.selectFirst("h1.entry-title")?.text().toString()
|
||||
.replace("Subtitle Indonesia", "").trim()
|
||||
val poster = document.selectFirst("div.entry-content > img")?.attr("src")
|
||||
val type = document.select("tbody th:contains(Tipe)").next().text().lowercase()
|
||||
val year = document.select("tbody th:contains(Dirilis)").next().text().trim().toIntOrNull()
|
||||
|
||||
val episodes = document.select("ul.daftar > li").map {
|
||||
val link = fixUrl(it.select("a").attr("href"))
|
||||
val name = it.select("a").text()
|
||||
val episode = Regex("Episode\\s?(\\d+)").find(name)?.groupValues?.getOrNull(0)?.toIntOrNull()
|
||||
Episode(link, name, episode = episode)
|
||||
}.reversed()
|
||||
|
||||
return newAnimeLoadResponse(title, url, getType(type)) {
|
||||
posterUrl = poster
|
||||
this.year = year
|
||||
addEpisodes(DubStatus.Subbed, episodes)
|
||||
showStatus =
|
||||
getStatus(document.select("tbody th:contains(Status)").next().text().trim())
|
||||
plot = document.selectFirst("div.entry-content > p")?.text()
|
||||
this.tags =
|
||||
document.select("tbody th:contains(Genre)").next().select("a").map { it.text() }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
val document = request(data).document
|
||||
|
||||
document.select(".mobius > .mirror > option").apmap {
|
||||
safeApiCall {
|
||||
val iframe = fixUrl(
|
||||
Jsoup.parse(base64Decode(it.attr("data-em"))).select("iframe").attr("src")
|
||||
?: throw ErrorLoadingException("No iframe found")
|
||||
)
|
||||
|
||||
when {
|
||||
iframe.startsWith("$mainUrl/utils/player/arch/") || iframe.startsWith(
|
||||
"$mainUrl/utils/player/race/"
|
||||
) -> request(iframe, ref = data).document.select("source").attr("src")
|
||||
.let { link ->
|
||||
val source =
|
||||
when {
|
||||
iframe.contains("/arch/") -> "Arch"
|
||||
iframe.contains("/race/") -> "Race"
|
||||
else -> this.name
|
||||
}
|
||||
val quality =
|
||||
Regex("\\.(\\d{3,4})\\.").find(link)?.groupValues?.get(1)
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
source = source,
|
||||
name = source,
|
||||
url = link,
|
||||
referer = mainUrl,
|
||||
quality = quality?.toIntOrNull() ?: Qualities.Unknown.value
|
||||
)
|
||||
)
|
||||
}
|
||||
// skip for now
|
||||
// iframe.startsWith("$mainUrl/utils/player/fichan/") -> ""
|
||||
// iframe.startsWith("$mainUrl/utils/player/blogger/") -> ""
|
||||
iframe.startsWith("https://aghanim.xyz/tools/redirect/") -> {
|
||||
val link = "https://rasa-cintaku-semakin-berantai.xyz/v/${
|
||||
iframe.substringAfter("id=").substringBefore("&token")
|
||||
}"
|
||||
loadExtractor(link, mainUrl, subtitleCallback, callback)
|
||||
}
|
||||
iframe.startsWith("$mainUrl/utils/player/framezilla/") || iframe.startsWith("https://uservideo.xyz") -> {
|
||||
request(iframe, ref = data).document.select("iframe").attr("src")
|
||||
.let { link ->
|
||||
loadExtractor(fixUrl(link), mainUrl, subtitleCallback, callback)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
loadExtractor(iframe, mainUrl, subtitleCallback, callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class AnimeSailProviderPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(AnimeSailProvider())
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 7
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "en"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AnimeMovie",
|
||||
"Anime",
|
||||
"OVA",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=animixplay.to&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated" />
|
|
@ -1,446 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.hexated.GogoExtractor.extractVidstream
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||
import org.jsoup.Jsoup
|
||||
import java.net.URI
|
||||
|
||||
|
||||
class Animixplay : MainAPI() {
|
||||
override var mainUrl = "https://animixplay.red"
|
||||
override var name = "Animixplay"
|
||||
override val hasMainPage = true
|
||||
override var lang = "en"
|
||||
override val hasQuickSearch = true
|
||||
override val hasDownloadSupport = true
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Anime,
|
||||
TvType.AnimeMovie,
|
||||
TvType.OVA
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun getStatus(t: String?): ShowStatus {
|
||||
return when (t) {
|
||||
"Finished Airing" -> ShowStatus.Completed
|
||||
"Currently Airing" -> ShowStatus.Ongoing
|
||||
else -> ShowStatus.Completed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/api/search" to "Sub",
|
||||
"$mainUrl/api/search" to "Dub",
|
||||
"$mainUrl/a/XsWgdGCnKJfNvDFAM28EV" to "All",
|
||||
"$mainUrl/api/search" to "Movie",
|
||||
)
|
||||
|
||||
private var newPagination: String? = null
|
||||
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||
val items = mutableListOf<HomePageList>()
|
||||
val paged = page.toString()
|
||||
val pagination = if (request.name == "Movie") {
|
||||
paged.replace(paged, "99999999")
|
||||
} else {
|
||||
paged.replace(paged, "3020-05-06 00:00:00")
|
||||
}
|
||||
|
||||
if (page <= 1) {
|
||||
val headers = when (request.name) {
|
||||
"Sub" -> mapOf("seasonal" to pagination)
|
||||
"Dub" -> mapOf("seasonaldub" to pagination)
|
||||
"All" -> mapOf("recent" to pagination)
|
||||
"Movie" -> mapOf("movie" to pagination)
|
||||
else -> mapOf()
|
||||
}
|
||||
val res = app.post(
|
||||
request.data,
|
||||
data = headers,
|
||||
referer = mainUrl,
|
||||
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
|
||||
).parsedSafe<Result>()
|
||||
newPagination = res?.last.toString()
|
||||
val home = res?.result?.mapNotNull {
|
||||
it.toSearchResponse()
|
||||
} ?: throw ErrorLoadingException("No media found")
|
||||
items.add(
|
||||
HomePageList(
|
||||
name = request.name,
|
||||
list = home,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val headers = when (request.name) {
|
||||
"Sub" -> mapOf("seasonal" to "$newPagination")
|
||||
"Dub" -> mapOf("seasonaldub" to "$newPagination")
|
||||
"All" -> mapOf("recent" to "$newPagination")
|
||||
"Movie" -> mapOf("movie" to "$newPagination")
|
||||
else -> mapOf()
|
||||
}
|
||||
val res = app.post(
|
||||
request.data,
|
||||
data = headers,
|
||||
referer = mainUrl,
|
||||
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
|
||||
).parsedSafe<Result>()
|
||||
newPagination = res?.last.toString()
|
||||
val home = res?.result?.mapNotNull {
|
||||
it.toSearchResponse()
|
||||
} ?: throw ErrorLoadingException("No media found")
|
||||
items.add(
|
||||
HomePageList(
|
||||
name = request.name,
|
||||
list = home,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return newHomePageResponse(items)
|
||||
}
|
||||
|
||||
private fun Anime.toSearchResponse(): AnimeSearchResponse? {
|
||||
return newAnimeSearchResponse(
|
||||
title ?: return null,
|
||||
fixUrl(url ?: return null),
|
||||
TvType.TvSeries,
|
||||
) {
|
||||
this.posterUrl = img ?: picture
|
||||
addDubStatus(
|
||||
isDub = title.contains("Dub"),
|
||||
episodes = Regex("EP\\s([0-9]+)/").find(
|
||||
infotext ?: ""
|
||||
)?.groupValues?.getOrNull(1)
|
||||
?.toIntOrNull()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse>? {
|
||||
return app.post(
|
||||
url = "https://v1.ij7p9towl8uj4qafsopjtrjk.workers.dev",
|
||||
referer = mainUrl,
|
||||
data = mapOf(
|
||||
"q2" to query,
|
||||
"origin" to "1",
|
||||
"root" to "animixplay.to",
|
||||
"d" to "gogoanime.tel"
|
||||
)
|
||||
).parsedSafe<FullSearch>()?.result?.let {
|
||||
Jsoup.parse(it).select("div").map { elem ->
|
||||
|
||||
val href = fixUrl(elem.select("a").attr("href"))
|
||||
val title = elem.select("a").attr("title")
|
||||
newAnimeSearchResponse(title, href, TvType.Anime) {
|
||||
this.posterUrl = elem.select("img").attr("src")
|
||||
addDubStatus(isDub = title.contains("Dub"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun quickSearch(query: String): List<SearchResponse>? {
|
||||
return app.post(
|
||||
"https://cdn.animixplay.to/api/search",
|
||||
data = mapOf("qfast" to query, "root" to URI(mainUrl).host)
|
||||
).parsedSafe<Search>()?.result?.let {
|
||||
Jsoup.parse(it).select("a").map { elem ->
|
||||
val href = elem.attr("href")
|
||||
val title = elem.select("p.name").text()
|
||||
newAnimeSearchResponse(title, href, TvType.Anime) {
|
||||
this.posterUrl = elem.select("img").attr("src")
|
||||
addDubStatus(isDub = title.contains("Dub"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadMissingAnime(url: String): LoadResponse? {
|
||||
val doc = app.get(url).document
|
||||
|
||||
val title = doc.selectFirst("span.animetitle")?.text()
|
||||
val image = fixUrlNull(doc.selectFirst("meta[property=og:image]")?.attr("content"))
|
||||
val genres = doc.selectFirst("span#genredata")?.text()?.split(",")?.map { it.trim() }
|
||||
|
||||
val subEpisodes = mutableListOf<Episode>()
|
||||
val dubEpisodes = mutableListOf<Episode>()
|
||||
val dataEps = doc.select("div#epslistplace")
|
||||
.text().trim()
|
||||
Regex("\"([0-9]+)\":\"(\\S+?)\"").findAll(dataEps).toList()
|
||||
.map { it.groupValues[1] to it.groupValues[2] }.map { (ep, link) ->
|
||||
val episode = Episode(fixUrl(link), episode = ep.toInt() + 1)
|
||||
if (url.contains("-dub")) {
|
||||
dubEpisodes.add(episode)
|
||||
} else {
|
||||
subEpisodes.add(episode)
|
||||
}
|
||||
}
|
||||
|
||||
return newAnimeLoadResponse(
|
||||
title ?: return null,
|
||||
url,
|
||||
TvType.Anime
|
||||
) {
|
||||
this.posterUrl = image
|
||||
this.tags = genres
|
||||
if (subEpisodes.isNotEmpty()) addEpisodes(DubStatus.Subbed, subEpisodes)
|
||||
if (dubEpisodes.isNotEmpty()) addEpisodes(DubStatus.Dubbed, dubEpisodes)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
|
||||
val (fixUrl, malId) = if (url.contains("/anime/")) {
|
||||
listOf(url, Regex("anime/([0-9]+)/?").find(url)?.groupValues?.get(1))
|
||||
} else {
|
||||
val malId = app.get(url).text.substringAfterLast("malid = '").substringBefore("';")
|
||||
listOf("$mainUrl/anime/$malId", malId)
|
||||
}
|
||||
|
||||
val anilistId = app.post(
|
||||
"https://graphql.anilist.co/", data = mapOf(
|
||||
"query" to "{Media(idMal:$malId,type:ANIME){id}}",
|
||||
)
|
||||
).parsedSafe<DataAni>()?.data?.media?.id
|
||||
|
||||
val res = app.get("$mainUrl/assets/mal/$malId.json").parsedSafe<AnimeDetail>()
|
||||
?: return loadMissingAnime(url)
|
||||
|
||||
val subEpisodes = mutableListOf<Episode>()
|
||||
val dubEpisodes = mutableListOf<Episode>()
|
||||
|
||||
app.post("$mainUrl/api/search", data = mapOf("recomended" to "$malId"))
|
||||
.parsedSafe<Data>()?.data?.map { server ->
|
||||
server.items?.apmap { data ->
|
||||
val jsonData =
|
||||
app.get(
|
||||
fixUrl(
|
||||
data.url ?: return@apmap null
|
||||
)
|
||||
).document.select("div#epslistplace")
|
||||
.text().trim()
|
||||
val episodeData = when (server.type) {
|
||||
"AL" -> Regex("\"([0-9]+)\":\\[(.*?)]").findAll(jsonData).toList()
|
||||
.map { it.groupValues[1] to it.groupValues[2] }.map { (ep, link) ->
|
||||
Episode(link, episode = ep.toInt() + 1)
|
||||
}
|
||||
"RUSH" -> Regex("\"([0-9]+)\":\\[(.*?)]").findAll(jsonData).toList()
|
||||
.map { it.groupValues[1] to it.groupValues[2] }.map { (ep, link) ->
|
||||
val linkData =
|
||||
Regex("\"vid\":\"(\\S+?)\"").findAll(link)
|
||||
.map { it.groupValues[1] }
|
||||
.toList().joinToString("")
|
||||
Episode(linkData, episode = ep.toInt() + 1)
|
||||
}
|
||||
else -> {
|
||||
Regex("\"([0-9]+)\":\"(\\S+?)\"").findAll(jsonData).toList()
|
||||
.map { it.groupValues[1] to it.groupValues[2] }.map { (ep, link) ->
|
||||
Episode(fixUrl(link), episode = ep.toInt() + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
episodeData.map {
|
||||
if (data.url.contains("-dub")) {
|
||||
dubEpisodes.add(it)
|
||||
} else {
|
||||
subEpisodes.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val recommendations = app.get("$mainUrl/assets/similar/$malId.json")
|
||||
.parsedSafe<RecResult>()?.recommendations?.mapNotNull { rec ->
|
||||
newAnimeSearchResponse(
|
||||
rec.title ?: return@mapNotNull null,
|
||||
"$mainUrl/anime/${rec.malId}/",
|
||||
TvType.Anime
|
||||
) {
|
||||
this.posterUrl = rec.imageUrl
|
||||
addDubStatus(dubExist = false, subExist = true)
|
||||
}
|
||||
}
|
||||
|
||||
return newAnimeLoadResponse(
|
||||
res.title ?: return null,
|
||||
url,
|
||||
TvType.Anime
|
||||
) {
|
||||
engName = res.title
|
||||
posterUrl = res.imageUrl
|
||||
this.year = res.aired?.from?.split("-")?.firstOrNull()?.toIntOrNull()
|
||||
showStatus = getStatus(res.status)
|
||||
plot = res.synopsis
|
||||
this.tags = res.genres?.mapNotNull { it.name }
|
||||
this.recommendations = recommendations
|
||||
addMalId(malId?.toIntOrNull())
|
||||
addAniListId(anilistId?.toIntOrNull())
|
||||
addTrailer(res.trailerUrl)
|
||||
if (subEpisodes.isNotEmpty()) addEpisodes(DubStatus.Subbed, groupEpisodes(subEpisodes))
|
||||
if (dubEpisodes.isNotEmpty()) addEpisodes(DubStatus.Dubbed, groupEpisodes(dubEpisodes))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
if (!data.contains("\"")) {
|
||||
invokeGogo(data, subtitleCallback, callback)
|
||||
} else {
|
||||
data.split("http").apmap {
|
||||
val link = it.replace("\"", "").let { url -> "http$url".trim() }
|
||||
if (link.startsWith("https://gogohd.net")) {
|
||||
invokeGogo(link, subtitleCallback, callback)
|
||||
} else {
|
||||
loadExtractor(link, "$mainUrl/", subtitleCallback) { links ->
|
||||
val name =
|
||||
if (link.startsWith("https://streamsb.net")) "StreamNet" else links.name
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
name,
|
||||
name,
|
||||
links.url,
|
||||
links.referer,
|
||||
links.quality,
|
||||
links.isM3u8,
|
||||
links.headers,
|
||||
links.extractorData
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun groupEpisodes(episodes: List<Episode>): List<Episode> {
|
||||
return episodes.groupBy { it.episode }.map { eps ->
|
||||
val epsNum = eps.key
|
||||
val epsLink = eps.value.joinToString("") { it.data }.replace("\",\"", "")
|
||||
Episode(epsLink, episode = epsNum)
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
extractVidstream(
|
||||
iframe.url,
|
||||
this.name,
|
||||
callback,
|
||||
iv,
|
||||
secretKey,
|
||||
secretDecryptKey,
|
||||
isUsingAdaptiveKeys = false,
|
||||
isUsingAdaptiveData = true,
|
||||
iframeDocument = iframeDoc
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
private data class IdAni(
|
||||
@JsonProperty("id") val id: String? = null,
|
||||
)
|
||||
|
||||
private data class MediaAni(
|
||||
@JsonProperty("Media") val media: IdAni? = null,
|
||||
)
|
||||
|
||||
private data class DataAni(
|
||||
@JsonProperty("data") val data: MediaAni? = null,
|
||||
)
|
||||
|
||||
private data class Items(
|
||||
@JsonProperty("url") val url: String? = null,
|
||||
@JsonProperty("title") val title: String? = null,
|
||||
)
|
||||
|
||||
private data class Episodes(
|
||||
@JsonProperty("type") val type: String? = null,
|
||||
@JsonProperty("items") val items: ArrayList<Items>? = arrayListOf(),
|
||||
)
|
||||
|
||||
private data class Data(
|
||||
@JsonProperty("data") val data: ArrayList<Episodes>? = arrayListOf(),
|
||||
)
|
||||
|
||||
private data class Aired(
|
||||
@JsonProperty("from") val from: String? = null,
|
||||
)
|
||||
|
||||
private data class Genres(
|
||||
@JsonProperty("name") val name: String? = null,
|
||||
)
|
||||
|
||||
private data class RecResult(
|
||||
@JsonProperty("recommendations") val recommendations: ArrayList<Recommendations>? = arrayListOf(),
|
||||
)
|
||||
|
||||
private data class Recommendations(
|
||||
@JsonProperty("mal_id") val malId: String? = null,
|
||||
@JsonProperty("image_url") val imageUrl: String? = null,
|
||||
@JsonProperty("title") val title: String? = null,
|
||||
)
|
||||
|
||||
private data class AnimeDetail(
|
||||
@JsonProperty("title") val title: String? = null,
|
||||
@JsonProperty("image_url") val imageUrl: String? = null,
|
||||
@JsonProperty("type") val type: String? = null,
|
||||
@JsonProperty("aired") val aired: Aired? = null,
|
||||
@JsonProperty("status") val status: String? = null,
|
||||
@JsonProperty("synopsis") val synopsis: String? = null,
|
||||
@JsonProperty("trailer_url") val trailerUrl: String? = null,
|
||||
@JsonProperty("genres") val genres: ArrayList<Genres>? = arrayListOf(),
|
||||
)
|
||||
|
||||
private data class Search(
|
||||
@JsonProperty("result") val result: String? = null,
|
||||
)
|
||||
|
||||
private data class Result(
|
||||
@JsonProperty("result") val result: ArrayList<Anime> = arrayListOf(),
|
||||
@JsonProperty("last") val last: Any? = null,
|
||||
)
|
||||
|
||||
private data class Anime(
|
||||
@JsonProperty("title") val title: String? = null,
|
||||
@JsonProperty("url") val url: String? = null,
|
||||
@JsonProperty("img") val img: String? = null,
|
||||
@JsonProperty("picture") val picture: String? = null,
|
||||
@JsonProperty("infotext") val infotext: String? = null,
|
||||
)
|
||||
|
||||
private data class FullSearch(
|
||||
@JsonProperty("result") val result: String? = null,
|
||||
)
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class AnimixplayPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Animixplay())
|
||||
}
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.base64Decode
|
||||
import com.lagradost.cloudstream3.base64DecodeArray
|
||||
import com.lagradost.cloudstream3.base64Encode
|
||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.utils.AppUtils
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||
import org.jsoup.nodes.Document
|
||||
import java.net.URI
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
object GogoExtractor {
|
||||
|
||||
/**
|
||||
* @param id base64Decode(show_id) + IV
|
||||
* @return the encryption key
|
||||
* */
|
||||
private fun getKey(id: String): String? {
|
||||
return normalSafeApiCall {
|
||||
id.map {
|
||||
it.code.toString(16)
|
||||
}.joinToString("").substring(0, 32)
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt#L60
|
||||
// No Licence on the function
|
||||
private fun cryptoHandler(
|
||||
string: String,
|
||||
iv: String,
|
||||
secretKeyString: String,
|
||||
encrypt: Boolean = true
|
||||
): String {
|
||||
//println("IV: $iv, Key: $secretKeyString, encrypt: $encrypt, Message: $string")
|
||||
val ivParameterSpec = IvParameterSpec(iv.toByteArray())
|
||||
val secretKey = SecretKeySpec(secretKeyString.toByteArray(), "AES")
|
||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
return if (!encrypt) {
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec)
|
||||
String(cipher.doFinal(base64DecodeArray(string)))
|
||||
} else {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec)
|
||||
base64Encode(cipher.doFinal(string.toByteArray()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param iframeUrl something like https://gogoplay4.com/streaming.php?id=XXXXXX
|
||||
* @param mainApiName used for ExtractorLink names and source
|
||||
* @param iv secret iv from site, required non-null if isUsingAdaptiveKeys is off
|
||||
* @param secretKey secret key for decryption from site, required non-null if isUsingAdaptiveKeys is off
|
||||
* @param secretDecryptKey secret key to decrypt the response json, required non-null if isUsingAdaptiveKeys is off
|
||||
* @param isUsingAdaptiveKeys generates keys from IV and ID, see getKey()
|
||||
* @param isUsingAdaptiveData generate encrypt-ajax data based on $("script[data-name='episode']")[0].dataset.value
|
||||
* */
|
||||
suspend fun extractVidstream(
|
||||
iframeUrl: String,
|
||||
mainApiName: String,
|
||||
callback: (ExtractorLink) -> Unit,
|
||||
iv: String?,
|
||||
secretKey: String?,
|
||||
secretDecryptKey: String?,
|
||||
// This could be removed, but i prefer it verbose
|
||||
isUsingAdaptiveKeys: Boolean,
|
||||
isUsingAdaptiveData: Boolean,
|
||||
// If you don't want to re-fetch the document
|
||||
iframeDocument: Document? = null
|
||||
) = safeApiCall {
|
||||
// https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt
|
||||
// No Licence on the following code
|
||||
// Also modified of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/gogoanime/src/eu/kanade/tachiyomi/animeextension/en/gogoanime/extractors/GogoCdnExtractor.kt
|
||||
// License on the code above https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE
|
||||
|
||||
if ((iv == null || secretKey == null || secretDecryptKey == null) && !isUsingAdaptiveKeys)
|
||||
return@safeApiCall
|
||||
|
||||
val id = Regex("id=([^&]+)").find(iframeUrl)!!.value.removePrefix("id=")
|
||||
|
||||
var document: Document? = iframeDocument
|
||||
val foundIv =
|
||||
iv ?: (document ?: app.get(iframeUrl).document.also { document = it })
|
||||
.select("""div.wrapper[class*=container]""")
|
||||
.attr("class").split("-").lastOrNull() ?: return@safeApiCall
|
||||
val foundKey = secretKey ?: getKey(base64Decode(id) + foundIv) ?: return@safeApiCall
|
||||
val foundDecryptKey = secretDecryptKey ?: foundKey
|
||||
|
||||
val uri = URI(iframeUrl)
|
||||
val mainUrl = "https://" + uri.host
|
||||
|
||||
val encryptedId = cryptoHandler(id, foundIv, foundKey)
|
||||
val encryptRequestData = if (isUsingAdaptiveData) {
|
||||
// Only fetch the document if necessary
|
||||
val realDocument = document ?: app.get(iframeUrl).document
|
||||
val dataEncrypted =
|
||||
realDocument.select("script[data-name='episode']").attr("data-value")
|
||||
val headers = cryptoHandler(dataEncrypted, foundIv, foundKey, false)
|
||||
"id=$encryptedId&alias=$id&" + headers.substringAfter("&")
|
||||
} else {
|
||||
"id=$encryptedId&alias=$id"
|
||||
}
|
||||
|
||||
val jsonResponse =
|
||||
app.get(
|
||||
"$mainUrl/encrypt-ajax.php?$encryptRequestData",
|
||||
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
|
||||
)
|
||||
val dataencrypted =
|
||||
jsonResponse.text.substringAfter("{\"data\":\"").substringBefore("\"}")
|
||||
val datadecrypted = cryptoHandler(dataencrypted, foundIv, foundDecryptKey, false)
|
||||
val sources = AppUtils.parseJson<GogoSources>(datadecrypted)
|
||||
|
||||
suspend fun invokeGogoSource(
|
||||
source: GogoSource,
|
||||
sourceCallback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
if (source.file.contains(".m3u8")) {
|
||||
M3u8Helper.generateM3u8(
|
||||
mainApiName,
|
||||
source.file,
|
||||
mainUrl,
|
||||
headers = mapOf("Origin" to "https://plyr.link")
|
||||
).forEach(sourceCallback)
|
||||
} else {
|
||||
sourceCallback.invoke(
|
||||
ExtractorLink(
|
||||
mainApiName,
|
||||
mainApiName,
|
||||
source.file,
|
||||
mainUrl,
|
||||
getQualityFromName(source.label),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sources.source?.forEach {
|
||||
invokeGogoSource(it, callback)
|
||||
}
|
||||
sources.sourceBk?.forEach {
|
||||
invokeGogoSource(it, callback)
|
||||
}
|
||||
}
|
||||
|
||||
data class GogoSources(
|
||||
@JsonProperty("source") val source: List<GogoSource>?,
|
||||
@JsonProperty("sourceBk") val sourceBk: List<GogoSource>?,
|
||||
//val track: List<Any?>,
|
||||
//val advertising: List<Any?>,
|
||||
//val linkiframe: String
|
||||
)
|
||||
|
||||
data class GogoSource(
|
||||
@JsonProperty("file") val file: String,
|
||||
@JsonProperty("label") val label: String?,
|
||||
@JsonProperty("type") val type: String?,
|
||||
@JsonProperty("default") val default: String? = null
|
||||
)
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 4
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "de"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AnimeMovie",
|
||||
"Anime",
|
||||
"OVA",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=aniworld.to&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,233 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
||||
import com.lagradost.cloudstream3.extractors.DoodLaExtractor
|
||||
import com.lagradost.cloudstream3.extractors.Voe
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
import java.net.URI
|
||||
|
||||
class Aniworld : MainAPI() {
|
||||
override var mainUrl = "https://aniworld.to"
|
||||
override var name = "Aniworld"
|
||||
override val hasMainPage = true
|
||||
override var lang = "de"
|
||||
override val hasDownloadSupport = true
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Anime,
|
||||
TvType.AnimeMovie,
|
||||
TvType.OVA
|
||||
)
|
||||
|
||||
companion object {
|
||||
fun getType(t: String): TvType {
|
||||
return when {
|
||||
t.contains("Anime Ova") -> TvType.OVA
|
||||
t.contains("Anime Movie") -> TvType.AnimeMovie
|
||||
else -> TvType.Anime
|
||||
}
|
||||
}
|
||||
|
||||
fun getStatus(t: String): ShowStatus {
|
||||
return when {
|
||||
t.contains("/complete", true) -> ShowStatus.Completed
|
||||
t.contains("/running", true) -> ShowStatus.Ongoing
|
||||
else -> ShowStatus.Completed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
|
||||
val document = app.get(mainUrl).document
|
||||
val item = arrayListOf<HomePageList>()
|
||||
document.select("div.carousel").map { ele ->
|
||||
val header = ele.selectFirst("h2")?.text() ?: return@map
|
||||
val home = ele.select("div.coverListItem").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
if (home.isNotEmpty()) item.add(HomePageList(header, home))
|
||||
}
|
||||
return HomePageResponse(item)
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val json = app.post(
|
||||
"$mainUrl/ajax/search",
|
||||
data = mapOf("keyword" to query),
|
||||
referer = "$mainUrl/search",
|
||||
headers = mapOf(
|
||||
"x-requested-with" to "XMLHttpRequest"
|
||||
)
|
||||
)
|
||||
return tryParseJson<List<AnimeSearch>>(json.text)?.filter {
|
||||
!it.link.contains("episode-") && it.link.contains(
|
||||
"/stream"
|
||||
)
|
||||
}?.map {
|
||||
newAnimeSearchResponse(
|
||||
it.title?.replace(Regex("</?em>"), "") ?: "",
|
||||
fixUrl(it.link),
|
||||
TvType.Anime
|
||||
) {
|
||||
}
|
||||
} ?: throw ErrorLoadingException()
|
||||
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("div.series-title span")?.text() ?: return null
|
||||
val poster = fixUrlNull(document.selectFirst("div.seriesCoverBox img")?.attr("data-src"))
|
||||
val tags = document.select("div.genres li a").map { it.text() }
|
||||
val year = document.selectFirst("span[itemprop=startDate] a")?.text()?.toIntOrNull()
|
||||
val description = document.select("p.seri_des").text()
|
||||
val actor =
|
||||
document.select("li:contains(Schauspieler:) ul li a").map { it.select("span").text() }
|
||||
|
||||
val episodes = mutableListOf<Episode>()
|
||||
document.select("div#stream > ul:first-child li").map { ele ->
|
||||
val page = ele.selectFirst("a")
|
||||
val epsDocument = app.get(fixUrl(page?.attr("href") ?: return@map)).document
|
||||
epsDocument.select("div#stream > ul:nth-child(4) li").mapNotNull { eps ->
|
||||
episodes.add(
|
||||
Episode(
|
||||
fixUrl(eps.selectFirst("a")?.attr("href") ?: return@mapNotNull null),
|
||||
episode = eps.selectFirst("a")?.text()?.toIntOrNull(),
|
||||
season = page.text().toIntOrNull()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return newAnimeLoadResponse(
|
||||
title,
|
||||
url,
|
||||
TvType.Anime
|
||||
) {
|
||||
engName = title
|
||||
posterUrl = poster
|
||||
this.year = year
|
||||
addEpisodes(
|
||||
DubStatus.Subbed,
|
||||
episodes
|
||||
)
|
||||
addActors(actor)
|
||||
plot = description
|
||||
this.tags = tags
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
val document = app.get(data).document
|
||||
document.select("div.hosterSiteVideo ul li").map {
|
||||
Triple(
|
||||
it.attr("data-lang-key"),
|
||||
it.attr("data-link-target"),
|
||||
it.select("h4").text()
|
||||
)
|
||||
}.filter {
|
||||
it.third != "Vidoza"
|
||||
}.apmap {
|
||||
val redirectUrl = app.get(fixUrl(it.second)).url
|
||||
val lang = it.first.getLanguage(document)
|
||||
if (it.third == "VOE") {
|
||||
invokeVoe(redirectUrl, lang, data, callback)
|
||||
} else {
|
||||
loadExtractor(redirectUrl, data, subtitleCallback) { link ->
|
||||
val name = "${link.name} [${lang}]"
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
name,
|
||||
name,
|
||||
link.url,
|
||||
link.referer,
|
||||
link.quality,
|
||||
link.isM3u8,
|
||||
link.headers,
|
||||
link.extractorData
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun invokeVoe(
|
||||
url: String,
|
||||
lang: String?,
|
||||
referer: String,
|
||||
callback: (ExtractorLink) -> Unit,
|
||||
) {
|
||||
val name = "Voe [${lang}]"
|
||||
val request = app.get(url, referer = referer)
|
||||
val baseUrl = getBaseUrl(request.url)
|
||||
val res = request.document
|
||||
val script = res.select("script").find { it.data().contains("sources =") }?.data()
|
||||
val link =
|
||||
Regex("[\"']hls[\"']:\\s*[\"'](.*)[\"']").find(script ?: return)?.groupValues?.get(1)
|
||||
|
||||
M3u8Helper.generateM3u8(
|
||||
name,
|
||||
link ?: return,
|
||||
"$baseUrl/",
|
||||
headers = mapOf("Origin" to "$baseUrl/")
|
||||
).forEach(callback)
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): AnimeSearchResponse? {
|
||||
val href = fixUrlNull(this.selectFirst("a")?.attr("href")) ?: return null
|
||||
val title = this.selectFirst("h3")?.text() ?: return null
|
||||
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("data-src"))
|
||||
return newAnimeSearchResponse(title, href, TvType.Anime) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.getLanguage(document: Document): String? {
|
||||
return document.selectFirst("div.changeLanguageBox img[data-lang-key=$this]")?.attr("title")
|
||||
?.removePrefix("mit")?.trim()
|
||||
}
|
||||
|
||||
private fun getBaseUrl(url: String): String {
|
||||
return URI(url).let {
|
||||
"${it.scheme}://${it.host}"
|
||||
}
|
||||
}
|
||||
|
||||
private data class AnimeSearch(
|
||||
@JsonProperty("link") val link: String,
|
||||
@JsonProperty("title") val title: String? = null,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
class Dooood : DoodLaExtractor() {
|
||||
override var mainUrl = "https://urochsunloath.com"
|
||||
}
|
||||
|
||||
class Simpulumlamerop : Voe() {
|
||||
override var mainUrl = "https://simpulumlamerop.com"
|
||||
}
|
||||
|
||||
class Urochsunloath : Voe() {
|
||||
override var mainUrl = "https://urochsunloath.com"
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class AniworldPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Aniworld())
|
||||
registerExtractorAPI(Urochsunloath())
|
||||
registerExtractorAPI(Simpulumlamerop())
|
||||
registerExtractorAPI(Dooood())
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 1
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "tr"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AnimeMovie",
|
||||
"Anime",
|
||||
"OVA",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=anizm.net&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated" />
|
|
@ -1,195 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import android.util.Log
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
|
||||
class Anizm : MainAPI() {
|
||||
override var mainUrl = "https://anizm.net"
|
||||
override var name = "Anizm"
|
||||
override val hasMainPage = true
|
||||
override var lang = "tr"
|
||||
override val hasDownloadSupport = true
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Anime,
|
||||
TvType.AnimeMovie,
|
||||
TvType.OVA
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val mainServer = "https://anizmplayer.com"
|
||||
}
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/anime-izle?sayfa=" to "Son Eklenen Animeler",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val document = app.get(request.data + page).document
|
||||
val home = document.select("div.restrictedWidth div#episodesMiddle").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun getProperAnimeLink(uri: String): String {
|
||||
return if (uri.contains("-bolum")) {
|
||||
"$mainUrl/${uri.substringAfter("$mainUrl/").replace(Regex("-[0-9]+-bolum.*"), "")}"
|
||||
} else {
|
||||
uri
|
||||
}
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): AnimeSearchResponse? {
|
||||
val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href"))
|
||||
val title = this.selectFirst("div.title, h5.animeTitle a")?.text() ?: return null
|
||||
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
|
||||
val episode = this.selectFirst("div.truncateText")?.text()?.let {
|
||||
Regex("([0-9]+).\\s?Bölüm").find(it)?.groupValues?.getOrNull(1)?.toIntOrNull()
|
||||
}
|
||||
|
||||
return newAnimeSearchResponse(title, href, TvType.Anime) {
|
||||
this.posterUrl = posterUrl
|
||||
addSub(episode)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val document = app.get(
|
||||
"$mainUrl/fullViewSearch?search=$query&skip=0",
|
||||
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
|
||||
).document
|
||||
|
||||
return document.select("div.searchResultItem").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("h2.anizm_pageTitle a")!!.text().trim()
|
||||
val type =
|
||||
if (document.select("div.ui.grid div.four.wide").size == 1) TvType.Movie else TvType.Anime
|
||||
val trailer = document.select("div.yt-hd-thumbnail-inner-container iframe").attr("src")
|
||||
val episodes = document.select("div.ui.grid div.four.wide").map {
|
||||
val name = it.select("div.episodeBlock").text()
|
||||
val link = fixUrl(it.selectFirst("a")?.attr("href").toString())
|
||||
Episode(link, name)
|
||||
}
|
||||
return newAnimeLoadResponse(title, url, type) {
|
||||
posterUrl = fixUrlNull(document.selectFirst("div.infoPosterImg > img")?.attr("src"))
|
||||
this.year = document.select("div.infoSta ul li:first-child").text().trim().toIntOrNull()
|
||||
addEpisodes(DubStatus.Subbed, episodes)
|
||||
plot = document.select("div.infoDesc").text().trim()
|
||||
this.tags = document.select("span.dataValue span.ui.label").map { it.text() }
|
||||
addTrailer(trailer)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun invokeLokalSource(
|
||||
url: String,
|
||||
translator: String,
|
||||
sourceCallback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
app.get(url, referer = "$mainUrl/").document.select("script").find { script ->
|
||||
script.data().contains("eval(function(p,a,c,k,e,d)")
|
||||
}?.let {
|
||||
val key = getAndUnpack(it.data()).substringAfter("FirePlayer(\"").substringBefore("\",")
|
||||
val referer = "$mainServer/video/$key"
|
||||
val link = "$mainServer/player/index.php?data=$key&do=getVideo"
|
||||
Log.i("hexated", link)
|
||||
app.post(
|
||||
link,
|
||||
data = mapOf("hash" to key, "r" to "$mainUrl/"),
|
||||
referer = referer,
|
||||
headers = mapOf(
|
||||
"Accept" to "*/*",
|
||||
"Origin" to mainServer,
|
||||
"Content-Type" to "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
"X-Requested-With" to "XMLHttpRequest"
|
||||
)
|
||||
).parsedSafe<Source>()?.videoSource?.let { m3uLink ->
|
||||
M3u8Helper.generateM3u8(
|
||||
"${this.name} ($translator)",
|
||||
m3uLink,
|
||||
referer
|
||||
).forEach(sourceCallback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
val document = app.get(data).document
|
||||
document.select("div.episodeTranslators div#fansec").map {
|
||||
Pair(it.select("a").attr("translator"), it.select("div.title").text())
|
||||
}.apmap { (url, translator) ->
|
||||
safeApiCall {
|
||||
app.get(
|
||||
url,
|
||||
referer = data,
|
||||
headers = mapOf(
|
||||
"Accept" to "application/json, text/javascript, */*; q=0.01",
|
||||
"X-Requested-With" to "XMLHttpRequest"
|
||||
)
|
||||
).parsedSafe<Translators>()?.data?.let {
|
||||
Jsoup.parse(it).select("a").apmap { video ->
|
||||
app.get(
|
||||
video.attr("video"),
|
||||
referer = data,
|
||||
headers = mapOf(
|
||||
"Accept" to "application/json, text/javascript, */*; q=0.01",
|
||||
"X-Requested-With" to "XMLHttpRequest"
|
||||
)
|
||||
).parsedSafe<Videos>()?.player?.let { iframe ->
|
||||
Jsoup.parse(iframe).select("iframe").attr("src").let { link ->
|
||||
when {
|
||||
link.startsWith(mainServer) -> {
|
||||
invokeLokalSource(link, translator, callback)
|
||||
}
|
||||
else -> {
|
||||
loadExtractor(
|
||||
fixUrl(link),
|
||||
"$mainUrl/",
|
||||
subtitleCallback,
|
||||
callback
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
data class Source(
|
||||
@JsonProperty("videoSource") val videoSource: String?,
|
||||
)
|
||||
|
||||
data class Videos(
|
||||
@JsonProperty("player") val player: String?,
|
||||
)
|
||||
|
||||
data class Translators(
|
||||
@JsonProperty("data") val data: String?,
|
||||
)
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class AnizmPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Anizm())
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 1
|
||||
|
||||
cloudstream {
|
||||
language = "pt-pt"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"Anime",
|
||||
"AnimeMovie",
|
||||
"OVA",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=www.anroll.net&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,241 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class Anroll : MainAPI() {
|
||||
override var mainUrl = "https://www.anroll.net"
|
||||
override var name = "Anroll"
|
||||
override val hasMainPage = true
|
||||
override var lang = "pt"
|
||||
override val hasDownloadSupport = true
|
||||
override val hasQuickSearch = true
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Anime,
|
||||
TvType.AnimeMovie,
|
||||
TvType.OVA
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val searchUrl = "https://apiv2-prd.anroll.net"
|
||||
private const val episodeUrl = "https://apiv3-prd.anroll.net"
|
||||
private const val posterUrl = "https://static.anroll.net"
|
||||
private const val videoUrl = "https://cdn-zenitsu.gamabunta.xyz"
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val document = app.get("$mainUrl/home").document
|
||||
val home = mutableListOf<HomePageList>()
|
||||
document.select("div.sc-f5d5b250-1.iJHcsI").map { div ->
|
||||
val header = div.selectFirst("h2")?.text() ?: return@map
|
||||
val child = HomePageList(
|
||||
header,
|
||||
div.select("ul li").mapNotNull {
|
||||
it.toSearchResult()
|
||||
},
|
||||
header == "Últimos Laçamentos"
|
||||
)
|
||||
home.add(child)
|
||||
}
|
||||
return HomePageResponse(home)
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): AnimeSearchResponse? {
|
||||
val title = this.selectFirst("h1")?.text()?.trim() ?: ""
|
||||
val href = fixUrl(this.selectFirst("a")?.attr("href") ?: return null)
|
||||
val posterUrl = fixUrlNull(this.select("img").attr("src"))
|
||||
val epNum = this.selectFirst("span.sc-f5d5b250-3.fsTgnD b")?.text()?.toIntOrNull()
|
||||
val isDub = this.selectFirst("div.sc-9dbd1f1d-5.efznig")?.text() == "DUB"
|
||||
return newAnimeSearchResponse(title, href, TvType.Anime) {
|
||||
this.posterUrl = posterUrl
|
||||
addDubStatus(isDub, epNum)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun quickSearch(query: String): List<SearchResponse> = search(query)
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val res = app.get("$searchUrl/search?q=$query").parsedSafe<SearchAnime>()
|
||||
val collection = mutableListOf<SearchResponse>()
|
||||
val anime = res?.data_anime?.mapNotNull {
|
||||
addAnimeSearch(
|
||||
it.titulo ?: return@mapNotNull null,
|
||||
"a/${it.generate_id}",
|
||||
it.slug_serie ?: return@mapNotNull null,
|
||||
Image.Anime
|
||||
)
|
||||
}
|
||||
if (anime?.isNotEmpty() == true) collection.addAll(anime)
|
||||
val filme = res?.data_filme?.mapNotNull {
|
||||
addAnimeSearch(
|
||||
it.nome_filme ?: return@mapNotNull null,
|
||||
"f/${it.generate_id}",
|
||||
it.slug_filme ?: return@mapNotNull null,
|
||||
Image.Filme
|
||||
)
|
||||
}
|
||||
if (filme?.isNotEmpty() == true) collection.addAll(filme)
|
||||
return collection
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
val fixUrl = getProperAnimeLink(url) ?: throw ErrorLoadingException()
|
||||
val document = app.get(fixUrl).document
|
||||
|
||||
val article = document.selectFirst("article.sc-f5d5b250-9") ?: return null
|
||||
val title = article.selectFirst("h2")?.text() ?: return null
|
||||
val poster = fixUrlNull(document.select("article.sc-f5d5b250-8 img").attr("src"))
|
||||
val tags = article.select("div#generos a").map { it.text() }
|
||||
val year = article.selectFirst("div.sc-f5d5b250-4")?.nextElementSibling()?.text()
|
||||
?.toIntOrNull()
|
||||
val description = document.select("div.sinopse").text().trim()
|
||||
val type = if (fixUrl.contains("/a/")) TvType.Anime else TvType.AnimeMovie
|
||||
|
||||
val episodes = mutableListOf<Episode>()
|
||||
|
||||
if (type == TvType.Anime) {
|
||||
for (i in 1..10) {
|
||||
val dataEpisode = app.get("$episodeUrl/animes/${fixUrl.substringAfterLast("/")}/episodes?page=$i&order=desc")
|
||||
.parsedSafe<LoadAnime>()?.data?.map {
|
||||
Episode(
|
||||
Load(it.anime?.get("slug_serie"), it.n_episodio, "animes").toJson(),
|
||||
it.titulo_episodio,
|
||||
episode = it.n_episodio?.toIntOrNull(),
|
||||
posterUrl = it.anime?.get("slug_serie")?.fixImageUrl(Image.Episode),
|
||||
description = it.sinopse_episodio
|
||||
)
|
||||
}?.reversed() ?: emptyList()
|
||||
if(dataEpisode.isEmpty()) break else episodes.addAll(dataEpisode)
|
||||
}
|
||||
} else {
|
||||
val dataEpisode = listOf(
|
||||
Episode(
|
||||
Load(
|
||||
document.selectFirst("script:containsData(slug_filme)")?.data()?.let {
|
||||
Regex("[\"']slug_filme[\"']:[\"'](\\S+?)[\"']").find(it)?.groupValues?.get(1)
|
||||
} ?: return null, "movie", "movies"
|
||||
).toJson()
|
||||
|
||||
)
|
||||
)
|
||||
episodes.addAll(dataEpisode)
|
||||
}
|
||||
|
||||
return newAnimeLoadResponse(title, url, type) {
|
||||
engName = title
|
||||
posterUrl = poster
|
||||
this.year = year
|
||||
addEpisodes(DubStatus.Subbed, episodes)
|
||||
plot = description
|
||||
this.tags = tags
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
val load = tryParseJson<Load>(data)
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
this.name,
|
||||
this.name,
|
||||
if(load?.type == "movies") {
|
||||
"$videoUrl/hls/${load.type}/${load.slug_serie}/${load.n_episodio}.mp4/media-1/stream.m3u8"
|
||||
} else {
|
||||
"$videoUrl/cf/hls/${load?.type}/${load?.slug_serie}/${load?.n_episodio}.mp4/media-1/stream.m3u8"
|
||||
},
|
||||
"$mainUrl/",
|
||||
Qualities.Unknown.value,
|
||||
true
|
||||
)
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun getProperAnimeLink(uri: String): String? {
|
||||
return if (uri.contains("/e/")) {
|
||||
app.get(uri).document.selectFirst("div.epcontrol2 a[href*=/a/]")?.attr("href")?.let {
|
||||
fixUrl(it)
|
||||
}
|
||||
} else {
|
||||
uri
|
||||
}
|
||||
}
|
||||
|
||||
private fun addAnimeSearch(titulo: String, id: String, slug: String, type: Image): AnimeSearchResponse {
|
||||
return newAnimeSearchResponse(titulo, "$mainUrl/$id", TvType.Anime) {
|
||||
this.posterUrl = slug.fixImageUrl(type)
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.fixImageUrl(param: Image): String {
|
||||
return when (param) {
|
||||
Image.Episode -> {
|
||||
"$posterUrl/images/animes/screens/$this/130x74/007.jpg"
|
||||
}
|
||||
Image.Anime -> {
|
||||
"$mainUrl/_next/image?url=$posterUrl/images/animes/capas/130x209/$this.jpg&w=384&q=75"
|
||||
}
|
||||
Image.Filme -> {
|
||||
"$mainUrl/_next/image?url=$posterUrl/images/filmes/capas/130x209/$this.jpg&w=384&q=75"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class Image {
|
||||
Episode,
|
||||
Anime,
|
||||
Filme,
|
||||
}
|
||||
|
||||
data class Load(
|
||||
val slug_serie: String? = null,
|
||||
val n_episodio: String? = null,
|
||||
val type: String? = null,
|
||||
)
|
||||
|
||||
data class DataEpisode(
|
||||
@JsonProperty("id_series_episodios") val id_series_episodios: Int? = null,
|
||||
@JsonProperty("n_episodio") val n_episodio: String? = null,
|
||||
@JsonProperty("titulo_episodio") val titulo_episodio: String? = null,
|
||||
@JsonProperty("sinopse_episodio") val sinopse_episodio: String? = null,
|
||||
@JsonProperty("generate_id") val generate_id: String? = null,
|
||||
@JsonProperty("anime") val anime: HashMap<String, String>? = null,
|
||||
)
|
||||
|
||||
data class LoadAnime(
|
||||
@JsonProperty("data") val data: ArrayList<DataEpisode>? = arrayListOf()
|
||||
)
|
||||
|
||||
data class DataAnime(
|
||||
@JsonProperty("titulo") val titulo: String? = null,
|
||||
@JsonProperty("generate_id") val generate_id: String? = null,
|
||||
@JsonProperty("slug_serie") val slug_serie: String? = null,
|
||||
@JsonProperty("total_eps_anime") val total_eps_anime: Int? = null,
|
||||
)
|
||||
|
||||
data class DataFilme(
|
||||
@JsonProperty("nome_filme") val nome_filme: String? = null,
|
||||
@JsonProperty("generate_id") val generate_id: String? = null,
|
||||
@JsonProperty("slug_filme") val slug_filme: String? = null,
|
||||
)
|
||||
|
||||
data class SearchAnime(
|
||||
@JsonProperty("data_anime") val data_anime: ArrayList<DataAnime>? = arrayListOf(),
|
||||
@JsonProperty("data_filme") val data_filme: ArrayList<DataFilme>? = arrayListOf(),
|
||||
)
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class AnrollPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Anroll())
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 1
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "tr"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf("AsianDrama",)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=dizikorea.com&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,203 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import org.jsoup.nodes.Element
|
||||
import java.net.URI
|
||||
|
||||
class Dizikorea : MainAPI() {
|
||||
override var mainUrl = "https://dizikorea.com"
|
||||
override var name = "Dizikorea"
|
||||
override val hasMainPage = true
|
||||
override var lang = "tr"
|
||||
override val hasDownloadSupport = true
|
||||
override val hasQuickSearch = true
|
||||
override val supportedTypes = setOf(TvType.AsianDrama)
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/diziler/page/" to "Kore Dizileri",
|
||||
"$mainUrl/kore-filmleri-izle/page/" to "Son Eklenen Filmler",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||
val document = app.get(request.data + page).document
|
||||
val home = document.select("ul li.segment-poster").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): SearchResponse? {
|
||||
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
|
||||
val title = this.selectFirst("h2.truncate")?.text()?.trim() ?: return null
|
||||
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("data-src"))
|
||||
|
||||
return newTvSeriesSearchResponse(title, href, TvType.AsianDrama) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun quickSearch(query: String): List<SearchResponse>? = search(query)
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse>? {
|
||||
return app.post("$mainUrl/ajax.php?qr=$query")
|
||||
.parsedSafe<Search>()?.data?.result?.mapNotNull { item ->
|
||||
newTvSeriesSearchResponse(
|
||||
item.s_name ?: return@mapNotNull null,
|
||||
item.s_link ?: return@mapNotNull null,
|
||||
TvType.AsianDrama
|
||||
) {
|
||||
this.posterUrl = fixUrlNull(item.s_image)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("h1.page-title")?.ownText()?.trim() ?: return null
|
||||
val poster = fixUrlNull(document.selectFirst("a.ui.image img")?.attr("data-src"))
|
||||
val tags = document.select("div.nano-content div:contains(Tür:) a").map { it.text() }
|
||||
|
||||
val year = document.selectFirst("table.ui.unstackable tr td:contains(Yapım Yılı) a")?.text()
|
||||
?.trim()
|
||||
?.toIntOrNull()
|
||||
val description = document.selectFirst("p#tv-series-desc")?.text()?.trim()
|
||||
val rating =
|
||||
document.selectFirst("table.ui.unstackable tr td:contains(IMDb Puanı) .color-imdb")
|
||||
?.text()?.trim()
|
||||
.toRatingInt()
|
||||
val actors = document.select("div.global-box div.item").map {
|
||||
Actor(
|
||||
it.select("h5.truncate").text().trim(),
|
||||
fixUrlNull(it.selectFirst("img")?.attr("src"))
|
||||
)
|
||||
}
|
||||
val type = if (document.select("div.all-seriespart")
|
||||
.isNullOrEmpty()
|
||||
) TvType.Movie else TvType.TvSeries
|
||||
val trailer = document.selectFirst("a.prettyPhoto")?.attr("href")
|
||||
|
||||
return when (type) {
|
||||
TvType.Movie -> {
|
||||
newMovieLoadResponse(title, url, TvType.Movie, url) {
|
||||
this.posterUrl = poster
|
||||
this.year = year
|
||||
this.plot = description
|
||||
this.tags = tags
|
||||
this.rating = rating
|
||||
addActors(actors)
|
||||
addTrailer(trailer)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val episodes = document.select("div.all-seriespart div.el-item").map { ep ->
|
||||
Episode(
|
||||
fixUrl(ep.selectFirst("a")!!.attr("href")),
|
||||
episode = ep.attr("data-epnumber").toIntOrNull(),
|
||||
season = ep.selectFirst("span.season-name")?.text()?.filter { it.isDigit() }
|
||||
?.toIntOrNull()
|
||||
)
|
||||
}
|
||||
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
|
||||
this.posterUrl = poster
|
||||
this.year = year
|
||||
this.plot = description
|
||||
this.tags = tags
|
||||
this.rating = rating
|
||||
addActors(actors)
|
||||
addTrailer(trailer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBaseUrl(url: String): String {
|
||||
return URI(url).let {
|
||||
"${it.scheme}://${it.host}"
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun invokeLokalSource(
|
||||
url: String,
|
||||
source: String,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
val script = app.get(url, referer = "$mainUrl/").document.select("script")
|
||||
.find { it.data().contains("sources:") }?.data()?.substringAfter("sources: [")
|
||||
?.substringBefore("],")?.replace(Regex("\"?file\"?"), "\"file\"")
|
||||
|
||||
AppUtils.tryParseJson<Source>(script)?.file?.let { link ->
|
||||
if (link.contains(".m3u8")) {
|
||||
M3u8Helper.generateM3u8(
|
||||
source,
|
||||
fixUrl(link),
|
||||
getBaseUrl(url)
|
||||
).forEach(callback)
|
||||
} else {
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
source,
|
||||
source,
|
||||
fixUrl(link),
|
||||
"$mainUrl/",
|
||||
Qualities.Unknown.value,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
val document = app.get(data).document
|
||||
val sources: MutableList<String> = mutableListOf()
|
||||
|
||||
val mainServer = document.select("div#singlePlay iframe").attr("src")
|
||||
sources.add(mainServer)
|
||||
|
||||
document.select("ul.linkler li").apmap {
|
||||
val server =
|
||||
app.get(it.select("a").attr("href")).document.select("div#singlePlay iframe")
|
||||
.attr("src")
|
||||
if (sources.isNotEmpty()) sources.add(fixUrl(server))
|
||||
}
|
||||
|
||||
sources.distinct().apmap { link ->
|
||||
when {
|
||||
link.startsWith("https://playerkorea") -> invokeLokalSource(link, this.name, callback)
|
||||
link.startsWith("https://vidmoly") -> invokeLokalSource(link, "Vidmoly", callback)
|
||||
else -> loadExtractor(link, "$mainUrl/", subtitleCallback, callback)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private data class SearchItem(
|
||||
@JsonProperty("s_link") val s_link: String? = null,
|
||||
@JsonProperty("s_image") val s_image: String? = null,
|
||||
@JsonProperty("s_name") val s_name: String? = null,
|
||||
)
|
||||
|
||||
private data class Result(
|
||||
@JsonProperty("result") val result: ArrayList<SearchItem> = arrayListOf(),
|
||||
)
|
||||
|
||||
private data class Search(
|
||||
@JsonProperty("data") val data: Result? = null,
|
||||
)
|
||||
|
||||
private data class Source(
|
||||
@JsonProperty("file") val file: String? = null,
|
||||
)
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class DizikoreaPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Dizikorea())
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 3
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "id"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AsianDrama",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=149.3.170.35&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,124 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class DramaSerial : MainAPI() {
|
||||
override var mainUrl = "https://dramaserial.wiki"
|
||||
override var name = "DramaSerial"
|
||||
override val hasMainPage = true
|
||||
override var lang = "id"
|
||||
override val hasDownloadSupport = true
|
||||
override val supportedTypes = setOf(TvType.AsianDrama)
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/page/" to "Latest Movie",
|
||||
"$mainUrl/Genre/ongoing/page/" to "Ongoing",
|
||||
"$mainUrl/Genre/drama-serial-korea/page/" to "Drama Serial Korea",
|
||||
"$mainUrl/Genre/drama-serial-jepang/page/" to "Drama Serial Jepang",
|
||||
"$mainUrl/Genre/drama-serial-mandarin/page/" to "Drama Serial Mandarin",
|
||||
"$mainUrl/Genre/drama-serial-filipina/page/" to "Drama Serial Filipina",
|
||||
"$mainUrl/Genre/drama-serial-india/page/" to "Drama Serial India",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||
val document = app.get(request.data + page).document
|
||||
val home = document.select("main#main article").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): SearchResponse? {
|
||||
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
|
||||
val title = this.selectFirst("h2.entry-title a")?.text()?.trim() ?: return null
|
||||
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
|
||||
val episode =
|
||||
this.selectFirst("div.gmr-episode-item")?.text()?.filter { it.isDigit() }?.toIntOrNull()
|
||||
|
||||
return newAnimeSearchResponse(title, href, TvType.AsianDrama) {
|
||||
this.posterUrl = posterUrl
|
||||
addSub(episode)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val link = "$mainUrl/?s=$query&post_type[]=post&post_type[]=tv"
|
||||
val document = app.get(link).document
|
||||
|
||||
return document.select("main#main article").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("h1.entry-title")!!.text().trim()
|
||||
val poster = fixUrlNull(document.selectFirst("figure.pull-left img")?.attr("src"))
|
||||
val tags =
|
||||
document.select("div.gmr-movie-innermeta span:contains(Genre:) a").map { it.text() }
|
||||
val year =
|
||||
document.selectFirst("div.gmr-movie-innermeta span:contains(Year:) a")!!.text().trim()
|
||||
.toIntOrNull()
|
||||
val duration =
|
||||
document.selectFirst("div.gmr-movie-innermeta span:contains(Duration:)")?.text()
|
||||
?.filter { it.isDigit() }?.toIntOrNull()
|
||||
val description = document.select("div.entry-content.entry-content-single div.entry-content.entry-content-single").text().trim()
|
||||
val type = if(document.select("div.page-links").isNullOrEmpty()) TvType.Movie else TvType.AsianDrama
|
||||
|
||||
if (type == TvType.Movie) {
|
||||
return newMovieLoadResponse(title, url, TvType.Movie, url) {
|
||||
posterUrl = poster
|
||||
this.year = year
|
||||
plot = description
|
||||
this.tags = tags
|
||||
this.duration = duration
|
||||
}
|
||||
} else {
|
||||
val episodes = document.select("div.page-links span.page-link-number").mapNotNull { eps ->
|
||||
val episode = eps.text().filter { it.isDigit() }.toIntOrNull()
|
||||
val link = if(episode == 1) {
|
||||
url
|
||||
} else {
|
||||
eps.parent()?.attr("href")
|
||||
}
|
||||
Episode(
|
||||
link ?: return@mapNotNull null,
|
||||
episode = episode,
|
||||
)
|
||||
}
|
||||
return newTvSeriesLoadResponse(title, url, TvType.AsianDrama, episodes = episodes) {
|
||||
posterUrl = poster
|
||||
this.year = year
|
||||
this.duration = duration
|
||||
plot = description
|
||||
this.tags = tags
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
val document = app.get(data).document
|
||||
|
||||
val iframe = document.select("div.gmr-server-wrap iframe").attr("src")
|
||||
app.get(iframe, referer = "$mainUrl/").document.select("div#header-slider ul li").apmap { mLink ->
|
||||
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 link = app.get(uLink, referer = iLink).document.selectFirst("iframe")?.attr("src") ?: return@apmap null
|
||||
loadExtractor(fixUrl(link), "$mainUrl/", subtitleCallback, callback)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class DramaSerialPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(DramaSerial())
|
||||
registerExtractorAPI(Lkctwoone())
|
||||
}
|
||||
}
|
|
@ -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,26 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 6
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "id"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AsianDrama",
|
||||
"Movie",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=dramaid.asia&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,225 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.extractors.XStreamCdn
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class DramaidProvider : MainAPI() {
|
||||
override var mainUrl = "https://dramaid.best"
|
||||
override var name = "DramaId"
|
||||
override val hasQuickSearch = false
|
||||
override val hasMainPage = true
|
||||
override var lang = "id"
|
||||
override val hasDownloadSupport = true
|
||||
override val hasChromecastSupport = false
|
||||
override val supportedTypes = setOf(TvType.AsianDrama)
|
||||
|
||||
companion object {
|
||||
fun getStatus(t: String): ShowStatus {
|
||||
return when (t) {
|
||||
"Completed" -> ShowStatus.Completed
|
||||
"Ongoing" -> ShowStatus.Ongoing
|
||||
else -> ShowStatus.Completed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"&status=&type=&order=update" to "Drama Terbaru",
|
||||
"&order=latest" to "Baru Ditambahkan",
|
||||
"&status=&type=&order=popular" to "Drama Popular",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||
val document = app.get("$mainUrl/series/?page=$page${request.data}").document
|
||||
val home = document.select("article[itemscope=itemscope]").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun getProperDramaLink(uri: String): String {
|
||||
return if (uri.contains("/series/")) {
|
||||
uri
|
||||
} else {
|
||||
"$mainUrl/series/" + Regex("$mainUrl/(.+)-ep.+").find(uri)?.groupValues?.get(1)
|
||||
.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): SearchResponse? {
|
||||
val href = getProperDramaLink(this.selectFirst("a.tip")!!.attr("href"))
|
||||
val title = this.selectFirst("h2[itemprop=headline]")?.text()?.trim() ?: return null
|
||||
val posterUrl = fixUrlNull(this.selectFirst(".limit > noscript > img")?.attr("src"))
|
||||
|
||||
return newTvSeriesSearchResponse(title, href, TvType.AsianDrama) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val link = "$mainUrl/?s=$query"
|
||||
val document = app.get(link).document
|
||||
|
||||
return document.select("article[itemscope=itemscope]").map {
|
||||
val title = it.selectFirst("h2[itemprop=headline]")!!.text().trim()
|
||||
val poster = it.selectFirst(".limit > noscript > img")!!.attr("src")
|
||||
val href = it.selectFirst("a.tip")!!.attr("href")
|
||||
|
||||
newTvSeriesSearchResponse(title, href, TvType.AsianDrama) {
|
||||
this.posterUrl = poster
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("h1.entry-title")!!.text().trim()
|
||||
val poster = document.select(".thumb > noscript > img").attr("src")
|
||||
val tags = document.select(".genxed > a").map { it.text() }
|
||||
|
||||
val year = Regex("\\d, ([0-9]*)").find(
|
||||
document.selectFirst(".info-content > .spe > span > time")!!.text().trim()
|
||||
)?.groupValues?.get(1).toString().toIntOrNull()
|
||||
val status = getStatus(
|
||||
document.select(".info-content > .spe > span:nth-child(1)")
|
||||
.text().trim().replace("Status: ", "")
|
||||
)
|
||||
val description = document.select(".entry-content > p").text().trim()
|
||||
|
||||
val episodes = document.select(".eplister > ul > li").map {
|
||||
val name = it.selectFirst("a > .epl-title")!!.text().trim()
|
||||
val link = it.select("a").attr("href")
|
||||
val epNum = it.selectFirst("a > .epl-num")!!.text().trim().toIntOrNull()
|
||||
newEpisode(link) {
|
||||
this.name = name
|
||||
this.episode = epNum
|
||||
}
|
||||
}.reversed()
|
||||
|
||||
val recommendations =
|
||||
document.select(".listupd > article[itemscope=itemscope]").map { rec ->
|
||||
val epTitle = rec.selectFirst("h2[itemprop=headline]")!!.text().trim()
|
||||
val epPoster = rec.selectFirst(".limit > noscript > img")!!.attr("src")
|
||||
val epHref = fixUrl(rec.selectFirst("a.tip")!!.attr("href"))
|
||||
|
||||
newTvSeriesSearchResponse(epTitle, epHref, TvType.AsianDrama) {
|
||||
this.posterUrl = epPoster
|
||||
}
|
||||
}
|
||||
|
||||
if (episodes.size == 1) {
|
||||
return newMovieLoadResponse(title, url, TvType.Movie, episodes[0].data) {
|
||||
posterUrl = poster
|
||||
this.year = year
|
||||
plot = description
|
||||
this.tags = tags
|
||||
this.recommendations = recommendations
|
||||
}
|
||||
} else {
|
||||
return newTvSeriesLoadResponse(title, url, TvType.AsianDrama, episodes = episodes) {
|
||||
posterUrl = poster
|
||||
this.year = year
|
||||
showStatus = status
|
||||
plot = description
|
||||
this.tags = tags
|
||||
this.recommendations = recommendations
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private data class Sources(
|
||||
@JsonProperty("file") val file: String,
|
||||
@JsonProperty("label") val label: String,
|
||||
@JsonProperty("type") val type: String,
|
||||
@JsonProperty("default") val default: Boolean?
|
||||
)
|
||||
|
||||
private data class Tracks(
|
||||
@JsonProperty("file") val file: String,
|
||||
@JsonProperty("label") val label: String,
|
||||
@JsonProperty("kind") val type: String,
|
||||
@JsonProperty("default") val default: Boolean?
|
||||
)
|
||||
|
||||
private suspend fun invokeDriveSource(
|
||||
url: String,
|
||||
name: String,
|
||||
subCallback: (SubtitleFile) -> Unit,
|
||||
sourceCallback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
val server = app.get(url).document.selectFirst(".picasa")?.nextElementSibling()?.data()
|
||||
|
||||
val source = "[${server!!.substringAfter("sources: [").substringBefore("],")}]".trimIndent()
|
||||
val trackers = server.substringAfter("tracks:[").substringBefore("],")
|
||||
.replace("//language", "")
|
||||
.replace("file", "\"file\"")
|
||||
.replace("label", "\"label\"")
|
||||
.replace("kind", "\"kind\"").trimIndent()
|
||||
|
||||
tryParseJson<List<Sources>>(source)?.map {
|
||||
sourceCallback(
|
||||
ExtractorLink(
|
||||
name,
|
||||
"Drive",
|
||||
fixUrl(it.file),
|
||||
referer = "https://motonews.club/",
|
||||
quality = getQualityFromName(it.label)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
tryParseJson<Tracks>(trackers)?.let {
|
||||
subCallback.invoke(
|
||||
SubtitleFile(
|
||||
if (it.label.contains("Indonesia")) "${it.label}n" else it.label,
|
||||
it.file
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
val document = app.get(data).document
|
||||
val sources = document.select(".mobius > .mirror > option").mapNotNull {
|
||||
fixUrl(Jsoup.parse(base64Decode(it.attr("value"))).select("iframe").attr("src"))
|
||||
}
|
||||
|
||||
sources.map {
|
||||
it.replace("https://ndrama.xyz", "https://www.fembed.com")
|
||||
}.apmap {
|
||||
when {
|
||||
it.contains("motonews.club") -> invokeDriveSource(
|
||||
it,
|
||||
this.name,
|
||||
subtitleCallback,
|
||||
callback
|
||||
)
|
||||
else -> loadExtractor(it, data, subtitleCallback, callback)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Vanfem : XStreamCdn() {
|
||||
override val name: String = "Vanfem"
|
||||
override val mainUrl: String = "https://vanfem.com"
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class DramaidProviderPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(DramaidProvider())
|
||||
registerExtractorAPI(Vanfem())
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// 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("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"TvSeries",
|
||||
"Movie",
|
||||
"Cartoon",
|
||||
"Anime",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=www.dubbindo.xyz&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,136 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class Dubbindo : MainAPI() {
|
||||
override var mainUrl = "https://www.dubbindo.xyz"
|
||||
override var name = "Dubbindo"
|
||||
override val hasMainPage = true
|
||||
override var lang = "id"
|
||||
override val hasDownloadSupport = true
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.TvSeries,
|
||||
TvType.Movie,
|
||||
TvType.Cartoon,
|
||||
TvType.Anime,
|
||||
)
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/videos/category/1" to "Movie",
|
||||
"$mainUrl/videos/category/3" to "TV Series",
|
||||
"$mainUrl/videos/category/5" to "Anime Series",
|
||||
"$mainUrl/videos/category/4" to "Anime Movie",
|
||||
"$mainUrl/videos/category/other" to "Other",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val document = app.get("${request.data}?page_id=$page").document
|
||||
val home = document.select("div.videos-latest-list.pt_timeline_vids div.video-wrapper")
|
||||
.mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(
|
||||
list = HomePageList(
|
||||
name = request.name,
|
||||
list = home,
|
||||
isHorizontalImages = true
|
||||
),
|
||||
hasNext = true
|
||||
)
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): TvSeriesSearchResponse? {
|
||||
val title = this.selectFirst("h4,div.video-title")?.text()?.trim() ?: ""
|
||||
val href = this.selectFirst("a")?.attr("href") ?: return null
|
||||
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
|
||||
return newTvSeriesSearchResponse(title, href, TvType.TvSeries) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val searchResponse = mutableListOf<SearchResponse>()
|
||||
for (i in 1..10) {
|
||||
val document =
|
||||
app.get(
|
||||
"$mainUrl/search?keyword=$query&page_id=$i",
|
||||
).document
|
||||
val results = document.select("div.videos-latest-list.row div.video-wrapper")
|
||||
.mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
searchResponse.addAll(results)
|
||||
if (results.isEmpty()) break
|
||||
}
|
||||
return searchResponse
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("div.video-big-title h1")?.text() ?: return null
|
||||
val poster = document.selectFirst("meta[property=og:image]")?.attr("content")
|
||||
val tags = document.select("div.pt_categories li a").map { it.text() }
|
||||
val description = document.select("div.watch-video-description p").text()
|
||||
val recommendations = document.select("div.related-video-wrapper").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
val video = document.select("video#my-video source").map {
|
||||
Video(
|
||||
it.attr("src"),
|
||||
it.attr("size"),
|
||||
it.attr("type"),
|
||||
)
|
||||
}
|
||||
|
||||
return newMovieLoadResponse(title, url, TvType.Movie, video.toJson()) {
|
||||
posterUrl = poster
|
||||
plot = description
|
||||
this.tags = tags
|
||||
this.recommendations = recommendations
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
tryParseJson<List<Video>>(data)?.map { video ->
|
||||
if(video.type == "video/mp4" || video.type == "video/x-msvideo" || video.type == "video/x-matroska") {
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
this.name,
|
||||
this.name,
|
||||
video.src ?: return@map,
|
||||
"",
|
||||
video.res?.toIntOrNull() ?: Qualities.Unknown.value,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
loadExtractor(video.src ?: return@map, "", subtitleCallback, callback)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
data class Video(
|
||||
val src: String? = null,
|
||||
val res: String? = null,
|
||||
val type: String? = null,
|
||||
)
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class DubbindoPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Dubbindo())
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 1
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "zh"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AsianDrama",
|
||||
"TvSeries",
|
||||
"Movie",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=www.duboku.tv&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,133 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class DubokuProvider : MainAPI() {
|
||||
override var mainUrl = "https://www.duboku.tv"
|
||||
override var name = "Duboku"
|
||||
override val hasMainPage = true
|
||||
override var lang = "zh"
|
||||
override val hasDownloadSupport = true
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Movie,
|
||||
TvType.TvSeries,
|
||||
TvType.AsianDrama,
|
||||
)
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/vodshow/2--time------" to "连续剧 时间",
|
||||
"$mainUrl/vodshow/2--hits------" to "连续剧 人气",
|
||||
"$mainUrl/vodshow/13--time------" to "陆剧 时间",
|
||||
"$mainUrl/vodshow/13--hits------" to "陆剧 人气",
|
||||
"$mainUrl/vodshow/15--time------" to "日韩剧 时间",
|
||||
"$mainUrl/vodshow/15--hits------" to "日韩剧 人气",
|
||||
"$mainUrl/vodshow/21--time------" to "短剧 时间",
|
||||
"$mainUrl/vodshow/21--hits------" to "短剧 人气",
|
||||
"$mainUrl/vodshow/16--time------" to "英美剧 时间",
|
||||
"$mainUrl/vodshow/16--hits------" to "英美剧 人气",
|
||||
"$mainUrl/vodshow/14--time------" to "台泰剧 时间",
|
||||
"$mainUrl/vodshow/14--hits------" to "台泰剧 人气",
|
||||
"$mainUrl/vodshow/20--time------" to "港剧 时间",
|
||||
"$mainUrl/vodshow/20--hits------" to "港剧 人气",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val document = app.get("${request.data}$page---.html").document
|
||||
val home = document.select("ul.myui-vodlist.clearfix li").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): SearchResponse? {
|
||||
val title = this.selectFirst("h4.title a")?.text()?.trim() ?: return null
|
||||
val href = fixUrl(this.selectFirst("a")?.attr("href").toString())
|
||||
val posterUrl = fixUrlNull(this.selectFirst("a")?.attr("data-original"))
|
||||
val episode = this.selectFirst("span.pic-text.text-right")?.text()?.filter { it.isDigit() }
|
||||
?.toIntOrNull()
|
||||
|
||||
return newAnimeSearchResponse(title, href, TvType.Movie) {
|
||||
this.posterUrl = posterUrl
|
||||
addSub(episode)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val document = app.get("$mainUrl/vodsearch/-------------.html?wd=$query&submit=").document
|
||||
|
||||
return document.select("ul#searchList li").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("h1.title")?.text()?.trim() ?: return null
|
||||
val tvType = if (document.select("ul.myui-content__list li").size == 1
|
||||
) TvType.Movie else TvType.TvSeries
|
||||
val actors = document.select("p.data")[2].select("a").map { it.text() }
|
||||
|
||||
val episodes = document.select("ul.myui-content__list li").map {
|
||||
val href = fixUrl(it.select("a").attr("href"))
|
||||
val name = it.select("a").text().trim()
|
||||
Episode(
|
||||
data = href,
|
||||
name = name,
|
||||
)
|
||||
}
|
||||
return newTvSeriesLoadResponse(title, url, tvType, episodes) {
|
||||
this.posterUrl = fixUrlNull(
|
||||
document.selectFirst("a.myui-vodlist__thumb.picture img")?.attr("data-original")
|
||||
)
|
||||
this.year =
|
||||
document.select("p.data")[0].select("a").last()?.text()?.trim()?.toIntOrNull()
|
||||
this.plot = document.selectFirst("span.sketch.content")?.text()?.trim()
|
||||
this.tags = document.select("p.data")[0].select("a").map { it.text() }
|
||||
this.rating = document.select("div#rating span.branch").text().toRatingInt()
|
||||
addActors(actors)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
app.get(data).document.select("script").map { script ->
|
||||
if (script.data().contains("var player_data={")) {
|
||||
val dataJson =
|
||||
script.data().substringAfter("var player_data={").substringBefore("}")
|
||||
tryParseJson<Sources>("{$dataJson}")?.let { source ->
|
||||
M3u8Helper.generateM3u8(
|
||||
this.name,
|
||||
source.url ?: return@map,
|
||||
referer = "https://w.duboku.io/",
|
||||
headers = mapOf("Origin" to "https://w.duboku.io")
|
||||
).forEach(callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
data class Sources(
|
||||
@JsonProperty("url") val url: String?,
|
||||
)
|
||||
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class DubokuProviderPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(DubokuProvider())
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 2
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "id"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 0 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AnimeMovie",
|
||||
"Anime",
|
||||
"OVA",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=185.231.223.76&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,238 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
class GomunimeProvider : MainAPI() {
|
||||
override var mainUrl = "https://185.231.223.76"
|
||||
override var name = "Gomunime"
|
||||
override val hasMainPage = true
|
||||
override var lang = "id"
|
||||
override val hasDownloadSupport = true
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Anime,
|
||||
TvType.AnimeMovie,
|
||||
TvType.OVA
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
private const val mainServer = "https://path.onicdn.xyz/app/rapi.php"
|
||||
|
||||
fun getType(t: String): TvType {
|
||||
return if (t.contains("OVA") || t.contains("Special")) TvType.OVA
|
||||
else if (t.contains("Movie")) TvType.AnimeMovie
|
||||
else TvType.Anime
|
||||
}
|
||||
|
||||
fun getStatus(t: String): ShowStatus {
|
||||
return when (t) {
|
||||
"Completed" -> ShowStatus.Completed
|
||||
"Ongoing" -> ShowStatus.Ongoing
|
||||
else -> ShowStatus.Completed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"e" to "Episode Baru",
|
||||
"c" to "Completed",
|
||||
"la" to "Live Action",
|
||||
"t" to "Trending"
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val home = Jsoup.parse(
|
||||
(app.post(
|
||||
url = "$mainUrl/wp-admin/admin-ajax.php/wp-admin/admin-ajax.php",
|
||||
headers = mapOf("Referer" to mainUrl),
|
||||
data = mapOf(
|
||||
"action" to "home_ajax",
|
||||
"fungsi" to request.data,
|
||||
"pag" to "$page"
|
||||
)
|
||||
).parsedSafe<Response>()?.html ?: throw ErrorLoadingException("Invalid Json reponse"))
|
||||
).select("li").mapNotNull {
|
||||
val title = it.selectFirst("a.name")?.text()?.trim() ?: return@mapNotNull null
|
||||
val href = getProperAnimeLink(it.selectFirst("a")!!.attr("href"))
|
||||
val posterUrl = it.selectFirst("img")?.attr("src")
|
||||
val type = getType(it.selectFirst(".taglist > span")!!.text().trim())
|
||||
val epNum = it.select(".tag.ep").text().replace(Regex("[^0-9]"), "").trim()
|
||||
.toIntOrNull()
|
||||
newAnimeSearchResponse(title, href, type) {
|
||||
this.posterUrl = posterUrl
|
||||
addSub(epNum)
|
||||
}
|
||||
}
|
||||
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun getProperAnimeLink(uri: String): String {
|
||||
return if (uri.contains("-episode")) {
|
||||
val href =
|
||||
"$mainUrl/anime/" + Regex("\\w\\d/(.*)-episode.*").find(uri)?.groupValues?.get(1)
|
||||
.toString()
|
||||
when {
|
||||
href.contains("pokemon") -> href.replace(Regex("-[0-9]+"), "")
|
||||
else -> href
|
||||
}
|
||||
} else {
|
||||
uri
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val link = "$mainUrl/?s=$query"
|
||||
val document = app.get(link).document
|
||||
|
||||
return document.select(".anime-list > li").map {
|
||||
val title = it.selectFirst("a.name")!!.text()
|
||||
val poster = it.selectFirst("img")!!.attr("src")
|
||||
val tvType = getType(it.selectFirst(".taglist > span")?.text().toString())
|
||||
val href = fixUrl(it.selectFirst("a.name")!!.attr("href"))
|
||||
|
||||
newAnimeSearchResponse(title, href, tvType) {
|
||||
this.posterUrl = poster
|
||||
addDubStatus(dubExist = false, subExist = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst(".entry-title")?.text().toString()
|
||||
val poster = document.selectFirst(".thumbposter > img")?.attr("data-lazy-src")
|
||||
val tags = document.select(".genxed > a").map { it.text() }
|
||||
|
||||
val year = Regex("\\d, ([0-9]*)").find(
|
||||
document.select("time[itemprop = datePublished]").text()
|
||||
)?.groupValues?.get(1)?.toIntOrNull()
|
||||
val status = getStatus(document.selectFirst(".spe > span")!!.ownText())
|
||||
val description = document.select("div[itemprop = description] > p").text()
|
||||
val trailer = document.selectFirst("div.embed-responsive noscript iframe")?.attr("src")
|
||||
val episodes = parseJson<List<EpisodeElement>>(
|
||||
Regex("var episodelist = (\\[.*])").find(
|
||||
document.select(".bixbox.bxcl.epcheck > script").toString().trim()
|
||||
)?.groupValues?.get(1).toString().replace(Regex("""\\"""), "").trim()
|
||||
).map {
|
||||
val name =
|
||||
Regex("(Episode\\s?[0-9]+)").find(it.epTitle.toString())?.groupValues?.getOrNull(0)
|
||||
?: it.epTitle
|
||||
val link = it.epLink
|
||||
Episode(link, name)
|
||||
}.reversed()
|
||||
|
||||
return newAnimeLoadResponse(title, url, TvType.Anime) {
|
||||
engName = title
|
||||
posterUrl = poster
|
||||
this.year = year
|
||||
addEpisodes(DubStatus.Subbed, episodes)
|
||||
showStatus = status
|
||||
plot = description
|
||||
this.tags = tags
|
||||
addTrailer(trailer)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
val document = app.get(data).document
|
||||
val scriptData = document.select("aside.sidebar > script").dataNodes().toString()
|
||||
val key = scriptData.substringAfter("var a_ray = '").substringBefore("';")
|
||||
val title = scriptData.substringAfter("var judul_postingan = \"").substringBefore("\";")
|
||||
.replace(" ", "+")
|
||||
val image = document.select("img#tempvid").last()?.attr("src").toString()
|
||||
|
||||
val sources: List<Pair<String, String>> = app.post(
|
||||
url = mainServer,
|
||||
data = mapOf("data" to key, "gambar" to image, "judul" to title, "func" to "mirror"),
|
||||
referer = "$mainUrl/"
|
||||
).document.select("div.gomunime-server-mirror").map {
|
||||
Pair(
|
||||
it.attr("data-vhash"),
|
||||
it.attr("data-type")
|
||||
)
|
||||
}
|
||||
|
||||
sources.apmap {
|
||||
safeApiCall {
|
||||
when {
|
||||
it.second.contains("frame") -> {
|
||||
loadExtractor(it.first, mainUrl, subtitleCallback, callback)
|
||||
}
|
||||
// Skip for now
|
||||
// it.second.contains("hls") -> {
|
||||
// app.post(
|
||||
// url = mainServer,
|
||||
// data = mapOf("fid" to it.first, "func" to "hls")
|
||||
// ).text.let { link ->
|
||||
// M3u8Helper.generateM3u8(
|
||||
// this.name,
|
||||
// link,
|
||||
// "$mainUrl/",
|
||||
// headers = mapOf("Origin" to mainUrl)
|
||||
// ).forEach(callback)
|
||||
// }
|
||||
// }
|
||||
it.second.contains("mp4") -> {
|
||||
app.post(
|
||||
url = mainServer,
|
||||
data = mapOf("data" to it.first, "func" to "blogs")
|
||||
).parsed<List<MobiSource>>().map {
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
source = name,
|
||||
name = "Mobi SD",
|
||||
url = it.file,
|
||||
referer = "$mainUrl/",
|
||||
quality = Qualities.P360.value
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private data class Response(
|
||||
@JsonProperty("status") val status: Boolean,
|
||||
@JsonProperty("html") val html: String
|
||||
)
|
||||
|
||||
data class MobiSource(
|
||||
@JsonProperty("file") val file: String,
|
||||
@JsonProperty("label") val label: String,
|
||||
@JsonProperty("type") val type: String
|
||||
)
|
||||
|
||||
private data class EpisodeElement(
|
||||
@JsonProperty("data-index") val dataIndex: Long?,
|
||||
@JsonProperty("ep-num") val epNum: String?,
|
||||
@JsonProperty("ep-title") val epTitle: String?,
|
||||
@JsonProperty("ep-link") val epLink: String,
|
||||
@JsonProperty("ep-date") val epDate: String?
|
||||
)
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class GomunimeProviderPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(GomunimeProvider())
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 5
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "id"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AnimeMovie",
|
||||
"Anime",
|
||||
"OVA",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=gomunime.is&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,163 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import java.util.ArrayList
|
||||
|
||||
|
||||
class Gomunimeis : MainAPI() {
|
||||
override var mainUrl = "https://anoboy.life"
|
||||
override var name = "Gomunime.is"
|
||||
override val hasMainPage = true
|
||||
override var lang = "id"
|
||||
override val hasQuickSearch = true
|
||||
override val hasDownloadSupport = true
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Anime,
|
||||
TvType.AnimeMovie,
|
||||
TvType.OVA
|
||||
)
|
||||
|
||||
companion object {
|
||||
private const val mainImageUrl = "https://upload.anoboy.life"
|
||||
|
||||
fun getType(t: String): TvType {
|
||||
return if (t.contains("OVA", true) || t.contains("Special", true)) TvType.OVA
|
||||
else if (t.contains("Movie", true)) TvType.AnimeMovie
|
||||
else TvType.Anime
|
||||
}
|
||||
|
||||
fun getStatus(t: String): ShowStatus {
|
||||
return when (t) {
|
||||
"Completed" -> ShowStatus.Completed
|
||||
"Ongoing" -> ShowStatus.Ongoing
|
||||
else -> ShowStatus.Completed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"&limit=12&action=load_movie_last_update&status=Ongoing" to "Episode Baru",
|
||||
"&limit=15&action=load_movie_last_update&status=Completed" to "Completed",
|
||||
"&limit=15&action=load_movie_last_update&type=Live Action" to "Live Action",
|
||||
"&limit=15&action=load_movie_trending" to "Trending"
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val home = app.get(
|
||||
"$mainUrl/my-ajax?page=$page${request.data}",
|
||||
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
|
||||
)
|
||||
.parsedSafe<Responses>()?.data
|
||||
?.mapNotNull { media ->
|
||||
media.toSearchResponse()
|
||||
} ?: throw ErrorLoadingException("Invalid Json reponse")
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun Anime.toSearchResponse(): SearchResponse? {
|
||||
|
||||
return newAnimeSearchResponse(
|
||||
postTitle ?: return null,
|
||||
"$mainUrl/anime/$postName",
|
||||
TvType.TvSeries,
|
||||
) {
|
||||
this.posterUrl = "$mainImageUrl/$image"
|
||||
addSub(totalEpisode?.toIntOrNull())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun quickSearch(query: String): List<SearchResponse> = search(query)
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
return app.get(
|
||||
"$mainUrl/my-ajax?page=1&limit=10&action=load_search_movie&keyword=$query",
|
||||
referer = "$mainUrl/search/?keyword=$query",
|
||||
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
|
||||
).parsedSafe<Responses>()?.data
|
||||
?.mapNotNull { media ->
|
||||
media.toSearchResponse()
|
||||
} ?: throw ErrorLoadingException("Invalid Json reponse")
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst(".entry-title")?.text().toString()
|
||||
val poster = document.selectFirst(".thumbposter > img")?.attr("src")
|
||||
val tags = document.select(".genxed > a").map { it.text() }
|
||||
val type = document.selectFirst("div.info-content .spe span:last-child")?.ownText()?.lowercase() ?: "tv"
|
||||
|
||||
val year = Regex("\\d, (\\d*)").find(
|
||||
document.selectFirst("div.info-content .spe span.split")?.ownText().toString()
|
||||
)?.groupValues?.get(1)?.toIntOrNull()
|
||||
val status = getStatus(document.selectFirst(".spe > span")!!.ownText())
|
||||
val description = document.select("div[itemprop = description] > p").text()
|
||||
val episodes = document.select(".eplister > ul > li").map {
|
||||
val episode = Regex("Episode\\s?(\\d+)").find(
|
||||
it.select(".epl-title").text()
|
||||
)?.groupValues?.getOrNull(0)
|
||||
val link = it.select("a").attr("href")
|
||||
Episode(link, episode = episode?.toIntOrNull())
|
||||
}.reversed()
|
||||
|
||||
return newAnimeLoadResponse(title, url, getType(type)) {
|
||||
engName = title
|
||||
posterUrl = poster
|
||||
this.year = year
|
||||
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 document = app.get(data).document
|
||||
document.select("div.player-container iframe").attr("src").substringAfter("html#")
|
||||
.let { id ->
|
||||
app.get("https://gomunimes.com/stream?id=$id")
|
||||
.parsedSafe<Sources>()?.server?.streamsb?.link?.let { link ->
|
||||
loadExtractor(link, "https://vidgomunime.xyz/", subtitleCallback, callback)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
data class Streamsb(
|
||||
@JsonProperty("link") val link: String?,
|
||||
)
|
||||
|
||||
data class Server(
|
||||
@JsonProperty("streamsb") val streamsb: Streamsb?,
|
||||
)
|
||||
|
||||
data class Sources(
|
||||
@JsonProperty("server") val server: Server?,
|
||||
)
|
||||
|
||||
data class Responses(
|
||||
@JsonProperty("data") val data: ArrayList<Anime>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class Anime(
|
||||
@JsonProperty("post_title") val postTitle: String?,
|
||||
@JsonProperty("post_name") val postName: String?,
|
||||
@JsonProperty("image") val image: String?,
|
||||
@JsonProperty("total_episode") val totalEpisode: String?,
|
||||
@JsonProperty("salt") val salt: String?,
|
||||
)
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class GomunimeisPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Gomunimeis())
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 7
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "en"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
description = "Premium porn with 4K support (use VPN if links not working)"
|
||||
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=goodporn.to&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,126 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import org.jsoup.nodes.Element
|
||||
import java.util.*
|
||||
|
||||
class GoodPorn : MainAPI() {
|
||||
override var mainUrl = "https://goodporn.to"
|
||||
override var name = "GoodPorn"
|
||||
override val hasMainPage = true
|
||||
override val hasDownloadSupport = true
|
||||
override val vpnStatus = VPNStatus.MightBeNeeded
|
||||
override val supportedTypes = setOf(TvType.NSFW)
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=post_date&from=" to "New Videos",
|
||||
"$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=video_viewed&from=" to "Most Viewed Videos",
|
||||
"$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=rating&from=" to "Top Rated Videos ",
|
||||
"$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=most_commented&from=" to "Most Commented Videos",
|
||||
"$mainUrl/?mode=async&function=get_block&block_id=list_videos_most_recent_videos&sort_by=duration&from=" to "Longest Videos",
|
||||
"$mainUrl/channels/brazzers/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Brazzers",
|
||||
"$mainUrl/channels/digitalplayground/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Digital Playground",
|
||||
"$mainUrl/channels/realitykings/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Realitykings",
|
||||
"$mainUrl/channels/babes-network/?mode=async&function=get_block&block_id=list_videos_common_videos_list&sort_by=post_date&from=" to "Babes Network",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val document = app.get(request.data + page).document
|
||||
val home =
|
||||
document.select("div#list_videos_most_recent_videos_items div.item, div#list_videos_common_videos_list_items div.item")
|
||||
.mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(
|
||||
list = HomePageList(
|
||||
name = request.name,
|
||||
list = home,
|
||||
isHorizontalImages = true
|
||||
),
|
||||
hasNext = true
|
||||
)
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): SearchResponse? {
|
||||
val title = this.selectFirst("strong.title")?.text() ?: return null
|
||||
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
|
||||
val posterUrl = fixUrlNull(this.select("div.img > img").attr("data-original"))
|
||||
return newMovieSearchResponse(title, href, TvType.Movie) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val searchResponse = mutableListOf<SearchResponse>()
|
||||
for (i in 1..15) {
|
||||
val document =
|
||||
app.get(
|
||||
"$mainUrl/search/nikki-benz/?mode=async&function=get_block&block_id=list_videos_videos_list_search_result&q=$query&category_ids=&sort_by=&from_videos=$i&from_albums=$i",
|
||||
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
|
||||
).document
|
||||
val results =
|
||||
document.select("div#list_videos_videos_list_search_result_items div.item")
|
||||
.mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
searchResponse.addAll(results)
|
||||
if (results.isEmpty()) break
|
||||
}
|
||||
return searchResponse
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("div.headline > h1")?.text()?.trim().toString()
|
||||
val poster =
|
||||
fixUrlNull(document.selectFirst("meta[property=og:image]")?.attr("content").toString())
|
||||
val tags = document.select("div.info div:nth-child(5) > a").map { it.text() }
|
||||
val description = document.select("div.info div:nth-child(2)").text().trim()
|
||||
val actors = document.select("div.info div:nth-child(6) > a").map { it.text() }
|
||||
val recommendations =
|
||||
document.select("div#list_videos_related_videos_items div.item").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
|
||||
return newMovieLoadResponse(title, url, TvType.NSFW, url) {
|
||||
this.posterUrl = poster
|
||||
this.plot = description
|
||||
this.tags = tags
|
||||
addActors(actors)
|
||||
this.recommendations = recommendations
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
val document = app.get(data).document
|
||||
document.select("div.info div:last-child a").map { res ->
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
this.name,
|
||||
this.name,
|
||||
res.attr("href")
|
||||
.replace(Regex("\\?download\\S+.mp4&"), "?") + "&rnd=${Date().time}",
|
||||
referer = data,
|
||||
quality = Regex("([0-9]+p),").find(res.text())?.groupValues?.get(1)
|
||||
.let { getQualityFromName(it) },
|
||||
headers = mapOf("Range" to "bytes=0-"),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class GoodPornPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(GoodPorn())
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 3
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "ru"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AsianDrama",
|
||||
"Anime",
|
||||
"TvSeries",
|
||||
"Movie",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=hdrezka19139.org&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,395 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Element
|
||||
import java.util.*
|
||||
|
||||
class HDrezkaProvider : MainAPI() {
|
||||
override var mainUrl = "https://rezka.ag"
|
||||
override var name = "HDrezka"
|
||||
override val hasMainPage = true
|
||||
override var lang = "ru"
|
||||
override val hasDownloadSupport = true
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Movie,
|
||||
TvType.TvSeries,
|
||||
TvType.Anime,
|
||||
TvType.AsianDrama
|
||||
)
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/films/?filter=watching" to "фильмы",
|
||||
"$mainUrl/series/?filter=watching" to "сериалы",
|
||||
"$mainUrl/cartoons/?filter=watching" to "мультфильмы",
|
||||
"$mainUrl/animation/?filter=watching" to "аниме",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val url = request.data.split("?")
|
||||
val home = app.get("${url.first()}page/$page/?${url.last()}").document.select(
|
||||
"div.b-content__inline_items div.b-content__inline_item"
|
||||
).map {
|
||||
it.toSearchResult()
|
||||
}
|
||||
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): SearchResponse {
|
||||
val title =
|
||||
this.selectFirst("div.b-content__inline_item-link > a")?.text()?.trim().toString()
|
||||
val href = this.selectFirst("a")?.attr("href").toString()
|
||||
val posterUrl = this.select("img").attr("src")
|
||||
val type = if (this.select("span.info").isNotEmpty()) TvType.TvSeries else TvType.Movie
|
||||
return if (type == TvType.Movie) {
|
||||
newMovieSearchResponse(title, href, TvType.Movie) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
} else {
|
||||
val episode =
|
||||
this.select("span.info").text().substringAfter(",").replace(Regex("[^0-9]"), "")
|
||||
.toIntOrNull()
|
||||
newAnimeSearchResponse(title, href, TvType.TvSeries) {
|
||||
this.posterUrl = posterUrl
|
||||
addDubStatus(
|
||||
dubExist = true,
|
||||
dubEpisodes = episode,
|
||||
subExist = true,
|
||||
subEpisodes = episode
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val link = "$mainUrl/search/?do=search&subaction=search&q=$query"
|
||||
val document = app.get(link).document
|
||||
|
||||
return document.select("div.b-content__inline_items div.b-content__inline_item").map {
|
||||
it.toSearchResult()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse {
|
||||
val document = app.get(url).document
|
||||
|
||||
val id = url.split("/").last().split("-").first()
|
||||
val title = (document.selectFirst("div.b-post__origtitle")?.text()?.trim()
|
||||
?: document.selectFirst("div.b-post__title h1")?.text()?.trim()).toString()
|
||||
val poster = fixUrlNull(document.selectFirst("div.b-sidecover img")?.attr("src"))
|
||||
val tags =
|
||||
document.select("table.b-post__info > tbody > tr:contains(Жанр) span[itemprop=genre]")
|
||||
.map { it.text() }
|
||||
val year = document.select("div.film-info > div:nth-child(2) a").text().toIntOrNull()
|
||||
val tvType = if (document.select("div#simple-episodes-tabs")
|
||||
.isNullOrEmpty()
|
||||
) TvType.Movie else TvType.TvSeries
|
||||
val description = document.selectFirst("div.b-post__description_text")?.text()?.trim()
|
||||
val trailer = app.post(
|
||||
"$mainUrl/engine/ajax/gettrailervideo.php",
|
||||
data = mapOf("id" to id),
|
||||
referer = url
|
||||
).parsedSafe<Trailer>()?.code.let {
|
||||
Jsoup.parse(it.toString()).select("iframe").attr("src")
|
||||
}
|
||||
val rating =
|
||||
document.selectFirst("table.b-post__info > tbody > tr:nth-child(1) span.bold")?.text()
|
||||
.toRatingInt()
|
||||
val actors =
|
||||
document.select("table.b-post__info > tbody > tr:last-child span.item").mapNotNull {
|
||||
Actor(
|
||||
it.selectFirst("span[itemprop=name]")?.text() ?: return@mapNotNull null,
|
||||
it.selectFirst("span[itemprop=actor]")?.attr("data-photo")
|
||||
)
|
||||
}
|
||||
|
||||
val recommendations = document.select("div.b-sidelist div.b-content__inline_item").map {
|
||||
it.toSearchResult()
|
||||
}
|
||||
|
||||
val data = HashMap<String, Any>()
|
||||
val server = ArrayList<Map<String, String>>()
|
||||
|
||||
data["id"] = id
|
||||
data["favs"] = document.selectFirst("input#ctrl_favs")?.attr("value").toString()
|
||||
data["ref"] = url
|
||||
|
||||
return if (tvType == TvType.TvSeries) {
|
||||
document.select("ul#translators-list li").map { res ->
|
||||
server.add(
|
||||
mapOf(
|
||||
"translator_name" to res.text(),
|
||||
"translator_id" to res.attr("data-translator_id"),
|
||||
)
|
||||
)
|
||||
}
|
||||
val episodes = document.select("div#simple-episodes-tabs ul li").map {
|
||||
val season = it.attr("data-season_id").toIntOrNull()
|
||||
val episode = it.attr("data-episode_id").toIntOrNull()
|
||||
val name = "Episode $episode"
|
||||
|
||||
data["season"] = "$season"
|
||||
data["episode"] = "$episode"
|
||||
data["server"] = server
|
||||
data["action"] = "get_stream"
|
||||
|
||||
Episode(
|
||||
data.toJson(),
|
||||
name,
|
||||
season,
|
||||
episode,
|
||||
)
|
||||
}
|
||||
|
||||
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
|
||||
this.posterUrl = poster
|
||||
this.year = year
|
||||
this.plot = description
|
||||
this.tags = tags
|
||||
this.rating = rating
|
||||
addActors(actors)
|
||||
this.recommendations = recommendations
|
||||
addTrailer(trailer)
|
||||
}
|
||||
} else {
|
||||
document.select("ul#translators-list li").map { res ->
|
||||
server.add(
|
||||
mapOf(
|
||||
"translator_name" to res.text(),
|
||||
"translator_id" to res.attr("data-translator_id"),
|
||||
"camrip" to res.attr("data-camrip"),
|
||||
"ads" to res.attr("data-ads"),
|
||||
"director" to res.attr("data-director")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
data["server"] = server
|
||||
data["action"] = "get_movie"
|
||||
|
||||
newMovieLoadResponse(title, url, TvType.Movie, data.toJson()) {
|
||||
this.posterUrl = poster
|
||||
this.year = year
|
||||
this.plot = description
|
||||
this.tags = tags
|
||||
this.rating = rating
|
||||
addActors(actors)
|
||||
this.recommendations = recommendations
|
||||
addTrailer(trailer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun decryptStreamUrl(data: String): String {
|
||||
|
||||
fun getTrash(arr: List<String>, item: Int): List<String> {
|
||||
val trash = ArrayList<List<String>>()
|
||||
for (i in 1..item) {
|
||||
trash.add(arr)
|
||||
}
|
||||
return trash.reduce { acc, list ->
|
||||
val temp = ArrayList<String>()
|
||||
acc.forEach { ac ->
|
||||
list.forEach { li ->
|
||||
temp.add(ac.plus(li))
|
||||
}
|
||||
}
|
||||
return@reduce temp
|
||||
}
|
||||
}
|
||||
|
||||
val trashList = listOf("@", "#", "!", "^", "$")
|
||||
val trashSet = getTrash(trashList, 2) + getTrash(trashList, 3)
|
||||
var trashString = data.replace("#h", "").split("//_//").joinToString("")
|
||||
|
||||
trashSet.forEach {
|
||||
val temp = base64Encode(it.toByteArray())
|
||||
trashString = trashString.replace(temp, "")
|
||||
}
|
||||
|
||||
return base64Decode(trashString)
|
||||
|
||||
}
|
||||
|
||||
private fun cleanCallback(
|
||||
source: String,
|
||||
url: String,
|
||||
quality: String,
|
||||
isM3u8: Boolean,
|
||||
sourceCallback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
sourceCallback.invoke(
|
||||
ExtractorLink(
|
||||
source,
|
||||
source,
|
||||
url,
|
||||
"$mainUrl/",
|
||||
getQuality(quality),
|
||||
isM3u8,
|
||||
headers = mapOf(
|
||||
"Origin" to mainUrl
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getLanguage(str: String): String {
|
||||
return when (str) {
|
||||
"Русский" -> "Russian"
|
||||
"Українська" -> "Ukrainian"
|
||||
else -> str
|
||||
}
|
||||
}
|
||||
|
||||
private fun getQuality(str: String): Int {
|
||||
return when (str) {
|
||||
"360p" -> Qualities.P240.value
|
||||
"480p" -> Qualities.P360.value
|
||||
"720p" -> Qualities.P480.value
|
||||
"1080p" -> Qualities.P720.value
|
||||
"1080p Ultra" -> Qualities.P1080.value
|
||||
else -> getQualityFromName(str)
|
||||
}
|
||||
}
|
||||
|
||||
private fun invokeSources(
|
||||
source: String,
|
||||
url: String,
|
||||
subtitle: String,
|
||||
subCallback: (SubtitleFile) -> Unit,
|
||||
sourceCallback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
decryptStreamUrl(url).split(",").map { links ->
|
||||
val quality =
|
||||
Regex("\\[([0-9]{3,4}p\\s?\\w*?)]").find(links)?.groupValues?.getOrNull(1)
|
||||
?.trim() ?: return@map null
|
||||
links.replace("[$quality]", "").split(" or ")
|
||||
.map {
|
||||
val link = it.trim()
|
||||
val type = if(link.contains(".m3u8")) "(Main)" else "(Backup)"
|
||||
cleanCallback(
|
||||
"$source $type",
|
||||
link,
|
||||
quality,
|
||||
link.contains(".m3u8"),
|
||||
sourceCallback,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
subtitle.split(",").map { sub ->
|
||||
val language =
|
||||
Regex("\\[(.*)]").find(sub)?.groupValues?.getOrNull(1) ?: return@map null
|
||||
val link = sub.replace("[$language]", "").trim()
|
||||
subCallback.invoke(
|
||||
SubtitleFile(
|
||||
getLanguage(language),
|
||||
link
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
tryParseJson<Data>(data)?.let { res ->
|
||||
if (res.server?.isEmpty() == true) {
|
||||
val document = app.get(res.ref ?: return@let).document
|
||||
document.select("script").map { script ->
|
||||
if (script.data().contains("sof.tv.initCDNMoviesEvents(")) {
|
||||
val dataJson =
|
||||
script.data().substringAfter("false, {").substringBefore("});")
|
||||
tryParseJson<LocalSources>("{$dataJson}")?.let { source ->
|
||||
invokeSources(
|
||||
this.name,
|
||||
source.streams,
|
||||
source.subtitle.toString(),
|
||||
subtitleCallback,
|
||||
callback
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res.server?.apmap { server ->
|
||||
app.post(
|
||||
url = "$mainUrl/ajax/get_cdn_series/?t=${Date().time}",
|
||||
data = mapOf(
|
||||
"id" to res.id,
|
||||
"translator_id" to server.translator_id,
|
||||
"favs" to res.favs,
|
||||
"is_camrip" to server.camrip,
|
||||
"is_ads" to server.ads,
|
||||
"is_director" to server.director,
|
||||
"season" to res.season,
|
||||
"episode" to res.episode,
|
||||
"action" to res.action,
|
||||
).filterValues { it != null }.mapValues { it.value as String },
|
||||
referer = res.ref
|
||||
).parsedSafe<Sources>()?.let { source ->
|
||||
invokeSources(
|
||||
server.translator_name.toString(),
|
||||
source.url,
|
||||
source.subtitle.toString(),
|
||||
subtitleCallback,
|
||||
callback
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
data class LocalSources(
|
||||
@JsonProperty("streams") val streams: String,
|
||||
@JsonProperty("subtitle") val subtitle: Any?,
|
||||
)
|
||||
|
||||
data class Sources(
|
||||
@JsonProperty("url") val url: String,
|
||||
@JsonProperty("subtitle") val subtitle: Any?,
|
||||
)
|
||||
|
||||
data class Server(
|
||||
@JsonProperty("translator_name") val translator_name: String?,
|
||||
@JsonProperty("translator_id") val translator_id: String?,
|
||||
@JsonProperty("camrip") val camrip: String?,
|
||||
@JsonProperty("ads") val ads: String?,
|
||||
@JsonProperty("director") val director: String?,
|
||||
)
|
||||
|
||||
data class Data(
|
||||
@JsonProperty("id") val id: String?,
|
||||
@JsonProperty("favs") val favs: String?,
|
||||
@JsonProperty("server") val server: List<Server>?,
|
||||
@JsonProperty("season") val season: String?,
|
||||
@JsonProperty("episode") val episode: String?,
|
||||
@JsonProperty("action") val action: String?,
|
||||
@JsonProperty("ref") val ref: String?,
|
||||
)
|
||||
|
||||
data class Trailer(
|
||||
@JsonProperty("success") val success: Boolean?,
|
||||
@JsonProperty("code") val code: String?,
|
||||
)
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class HDrezkaProviderPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(HDrezkaProvider())
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 9
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "tr"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"TvSeries",
|
||||
"Movie",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=hdfilmcehennemi.live&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,240 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class Hdfilmcehennemi : MainAPI() {
|
||||
override var mainUrl = "https://www.hdfilmcehennemi.life"
|
||||
override var name = "hdfilmcehennemi"
|
||||
override val hasMainPage = true
|
||||
override var lang = "tr"
|
||||
override val hasQuickSearch = true
|
||||
override val hasDownloadSupport = true
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Movie,
|
||||
TvType.TvSeries,
|
||||
)
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/category/tavsiye-filmler-izle2/page/" to "Tavsiye Filmler Kategorisi",
|
||||
"$mainUrl/yabancidiziizle-1/page/" to "Son Eklenen Yabancı Diziler",
|
||||
"$mainUrl/imdb-7-puan-uzeri-filmler/page/" to "Imdb 7+ Filmler",
|
||||
"$mainUrl/en-cok-yorumlananlar/page/" to "En Çok Yorumlananlar",
|
||||
"$mainUrl/en-cok-begenilen-filmleri-izle/page/" to "En Çok Beğenilenler",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val document = app.get(request.data + page).document
|
||||
val home = document.select("div.card-body div.row div.col-6.col-sm-3.poster-container")
|
||||
.mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): SearchResponse? {
|
||||
val title = this.selectFirst("h2.title")?.text() ?: return null
|
||||
val href = fixUrlNull(this.selectFirst("a")?.attr("href")) ?: return null
|
||||
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("data-src"))
|
||||
return newMovieSearchResponse(title, href, TvType.Movie) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun Media.toSearchResponse(): SearchResponse? {
|
||||
return newMovieSearchResponse(
|
||||
title ?: return null,
|
||||
"$mainUrl/$slugPrefix$slug",
|
||||
TvType.TvSeries,
|
||||
) {
|
||||
this.posterUrl = "$mainUrl/uploads/poster/$poster"
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun quickSearch(query: String): List<SearchResponse> = search(query)
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
return app.post(
|
||||
"$mainUrl/search/",
|
||||
data = mapOf("query" to query),
|
||||
referer = "$mainUrl/",
|
||||
headers = mapOf(
|
||||
"Accept" to "application/json, text/javascript, */*; q=0.01",
|
||||
"X-Requested-With" to "XMLHttpRequest"
|
||||
)
|
||||
).parsedSafe<Result>()?.result?.mapNotNull { media ->
|
||||
media.toSearchResponse()
|
||||
} ?: throw ErrorLoadingException("Invalid Json reponse")
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("div.card-header > h1, div.card-header > h2")?.text()
|
||||
?.removeSuffix("Filminin Bilgileri")?.trim()
|
||||
?: return null
|
||||
val poster = fixUrlNull(document.selectFirst("img.img-fluid")?.attr("src"))
|
||||
val tags = document.select("div.mb-0.lh-lg div:nth-child(5) a").map { it.text() }
|
||||
val year =
|
||||
document.selectFirst("div.mb-0.lh-lg div:nth-child(4) a")?.text()?.trim()?.toIntOrNull()
|
||||
val tvType = if (document.select("nav#seasonsTabs").isNullOrEmpty()
|
||||
) TvType.Movie else TvType.TvSeries
|
||||
val description = document.selectFirst("article.text-white > p")?.text()?.trim()
|
||||
val rating = document.selectFirst("div.rating-votes div.rate span")?.text()?.toRatingInt()
|
||||
val actors = document.select("div.mb-0.lh-lg div:last-child a.chip").map {
|
||||
Actor(it.text(), it.select("img").attr("src"))
|
||||
}
|
||||
val recommendations =
|
||||
document.select("div.swiper-wrapper div.poster.poster-pop").mapNotNull {
|
||||
val recName = it.selectFirst("h2.title")?.text() ?: return@mapNotNull null
|
||||
val recHref =
|
||||
fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null
|
||||
val recPosterUrl = fixUrlNull(it.selectFirst("img")?.attr("data-src"))
|
||||
newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) {
|
||||
this.posterUrl = recPosterUrl
|
||||
}
|
||||
}
|
||||
|
||||
return if (tvType == TvType.TvSeries) {
|
||||
val trailer =
|
||||
document.selectFirst("button.btn.btn-fragman.btn-danger")?.attr("data-trailer")
|
||||
?.let {
|
||||
"https://www.youtube.com/embed/$it"
|
||||
}
|
||||
val episodes = document.select("div#seasonsTabs-tabContent div.card-list-item").map {
|
||||
val href = it.select("a").attr("href")
|
||||
val name = it.select("h3").text().trim()
|
||||
val episode = it.select("h3").text().let { num ->
|
||||
Regex("Sezon\\s?([0-9]+).").find(num)?.groupValues?.getOrNull(1)?.toIntOrNull()
|
||||
}
|
||||
val season = it.parents()[1].attr("id").substringAfter("-").toIntOrNull()
|
||||
Episode(
|
||||
href,
|
||||
name,
|
||||
season,
|
||||
episode,
|
||||
)
|
||||
}
|
||||
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
|
||||
this.posterUrl = poster
|
||||
this.year = year
|
||||
this.plot = description
|
||||
this.tags = tags
|
||||
this.rating = rating
|
||||
addActors(actors)
|
||||
this.recommendations = recommendations
|
||||
addTrailer(trailer)
|
||||
}
|
||||
} else {
|
||||
val trailer =
|
||||
document.selectFirst("nav.nav.card-nav.nav-slider a[data-bs-toggle=\"modal\"]")
|
||||
?.attr("data-trailer")?.let {
|
||||
"https://www.youtube.com/embed/$it"
|
||||
}
|
||||
newMovieLoadResponse(title, url, TvType.Movie, url) {
|
||||
this.posterUrl = poster
|
||||
this.year = year
|
||||
this.plot = description
|
||||
this.tags = tags
|
||||
this.rating = rating
|
||||
addActors(actors)
|
||||
this.recommendations = recommendations
|
||||
addTrailer(trailer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.addMarks(str: String): String {
|
||||
return this.replace(Regex("\"?$str\"?"), "\"$str\"")
|
||||
}
|
||||
|
||||
private suspend fun invokeLocalSource(
|
||||
source: String,
|
||||
url: String,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
val script = app.get(
|
||||
url,
|
||||
referer = "${mainUrl}/"
|
||||
).document.select("script")
|
||||
.find { it.data().contains("sources:") }?.data() ?: return
|
||||
val videoData = getAndUnpack(script).substringAfter("file_link=\"").substringBefore("\";")
|
||||
val subData = script.substringAfter("tracks: [").substringBefore("]")
|
||||
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
source,
|
||||
source,
|
||||
base64Decode(videoData),
|
||||
"$mainUrl/",
|
||||
Qualities.Unknown.value,
|
||||
true
|
||||
)
|
||||
)
|
||||
|
||||
tryParseJson<List<SubSource>>("[${subData}]")
|
||||
?.filter { it.kind == "captions" }?.map {
|
||||
subtitleCallback.invoke(
|
||||
SubtitleFile(
|
||||
it.label.toString(),
|
||||
fixUrl(it.file.toString())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
app.get(data).document.select("nav.nav.card-nav.nav-slider a.nav-link").map {
|
||||
Pair(it.attr("href"), it.text())
|
||||
}.apmap { (url, source) ->
|
||||
safeApiCall {
|
||||
app.get(url).document.select("div.card-video > iframe").attr("data-src")
|
||||
.let { link ->
|
||||
if (link.startsWith(mainUrl)) {
|
||||
invokeLocalSource(source, link, subtitleCallback, callback)
|
||||
} else {
|
||||
loadExtractor(link, "$mainUrl/", subtitleCallback, callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private data class Source(
|
||||
@JsonProperty("file") val file: String? = null,
|
||||
)
|
||||
|
||||
private data class SubSource(
|
||||
@JsonProperty("file") val file: String? = null,
|
||||
@JsonProperty("label") val label: String? = null,
|
||||
@JsonProperty("kind") val kind: String? = null,
|
||||
)
|
||||
|
||||
data class Result(
|
||||
@JsonProperty("result") val result: ArrayList<Media>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class Media(
|
||||
@JsonProperty("title") val title: String? = null,
|
||||
@JsonProperty("poster") val poster: String? = null,
|
||||
@JsonProperty("slug") val slug: String? = null,
|
||||
@JsonProperty("slug_prefix") val slugPrefix: String? = null,
|
||||
)
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class HdfilmcehennemiPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Hdfilmcehennemi())
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 3
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "en"
|
||||
// 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=hentaihaven.xxx&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,166 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import okhttp3.FormBody
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class Hentaiheaven : MainAPI() {
|
||||
override var mainUrl = "https://hentaihaven.xxx"
|
||||
override var name = "Hentaiheaven"
|
||||
override val hasMainPage = true
|
||||
override var lang = "en"
|
||||
override val hasDownloadSupport = true
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.NSFW)
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"?m_orderby=new-manga" to "New",
|
||||
"?m_orderby=views" to "Most Views",
|
||||
"?m_orderby=rating" to "Rating",
|
||||
"?m_orderby=alphabet" to "A-Z",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val document = app.get("$mainUrl/page/$page/${request.data}").document
|
||||
val home =
|
||||
document.select("div.page-listing-item div.col-6.col-md-zarat.badge-pos-1").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): AnimeSearchResponse? {
|
||||
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
|
||||
val title =
|
||||
this.selectFirst("h3 a, h5 a")?.text()?.trim() ?: this.selectFirst("a")?.attr("title")
|
||||
?: return null
|
||||
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
|
||||
val episode = this.selectFirst("span.chapter.font-meta a")?.text()?.filter { it.isDigit() }
|
||||
?.toIntOrNull()
|
||||
|
||||
return newAnimeSearchResponse(title, href, TvType.Anime) {
|
||||
this.posterUrl = posterUrl
|
||||
addSub(episode)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val link = "$mainUrl/?s=$query&post_type=wp-manga"
|
||||
val document = app.get(link).document
|
||||
|
||||
return document.select("div.c-tabs-item div.row.c-tabs-item__content").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("div.post-title h1")?.text()?.trim() ?: return null
|
||||
val poster = document.select("div.summary_image img").attr("src")
|
||||
val tags = document.select("div.genres-content > a").map { it.text() }
|
||||
|
||||
val description = document.select("div.description-summary p").text().trim()
|
||||
val trailer = document.selectFirst("a.trailerbutton")?.attr("href")
|
||||
|
||||
val episodes = document.select("div.listing-chapters_wrap ul li").mapNotNull {
|
||||
val name = it.selectFirst("a")?.text() ?: return@mapNotNull null
|
||||
val image = fixUrlNull(it.selectFirst("a img")?.attr("src"))
|
||||
val link = fixUrlNull(it.selectFirst("a")?.attr("href")) ?: return@mapNotNull null
|
||||
Episode(link, name, posterUrl = image)
|
||||
}.reversed()
|
||||
|
||||
val recommendations =
|
||||
document.select("div.row div.col-6.col-md-zarat").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
|
||||
return newAnimeLoadResponse(title, url, TvType.NSFW) {
|
||||
engName = title
|
||||
posterUrl = poster
|
||||
addEpisodes(DubStatus.Subbed, episodes)
|
||||
plot = description
|
||||
this.tags = tags
|
||||
this.recommendations = recommendations
|
||||
addTrailer(trailer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
val doc = app.get(data).document
|
||||
val meta = doc.selectFirst("meta[itemprop=thumbnailUrl]")?.attr("content")?.substringAfter("/hh/")?.substringBefore("/") ?: return false
|
||||
doc.select("div.player_logic_item iframe").attr("src").let { iframe ->
|
||||
val document = app.get(iframe, referer = data).text
|
||||
val en = Regex("var\\sen\\s=\\s'(\\S+)';").find(document)?.groupValues?.getOrNull(1)
|
||||
val iv = Regex("var\\siv\\s=\\s'(\\S+)';").find(document)?.groupValues?.getOrNull(1)
|
||||
|
||||
val body = FormBody.Builder()
|
||||
.addEncoded("action", "zarat_get_data_player_ajax")
|
||||
.addEncoded("a", "$en")
|
||||
.addEncoded("b", "$iv")
|
||||
.build()
|
||||
|
||||
app.post(
|
||||
"$mainUrl/wp-content/plugins/player-logic/api.php",
|
||||
// data = mapOf(
|
||||
// "action" to "zarat_get_data_player_ajax",
|
||||
// "a" to "$en",
|
||||
// "b" to "$iv"
|
||||
// ),
|
||||
requestBody = body,
|
||||
// headers = mapOf("Sec-Fetch-Mode" to "cors")
|
||||
).parsedSafe<Response>()?.data?.sources?.map { res ->
|
||||
// M3u8Helper.generateM3u8(
|
||||
// this.name,
|
||||
// res.src ?: return@map null,
|
||||
// referer = "$mainUrl/",
|
||||
// headers = mapOf(
|
||||
// "Origin" to mainUrl,
|
||||
// )
|
||||
// ).forEach(callback)
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
this.name,
|
||||
this.name,
|
||||
res.src?.replace("/hh//", "/hh/$meta/") ?: return@map null,
|
||||
referer = "",
|
||||
quality = Qualities.Unknown.value,
|
||||
isM3u8 = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
data class Response(
|
||||
@JsonProperty("data") val data: Data? = null,
|
||||
)
|
||||
|
||||
data class Data(
|
||||
@JsonProperty("sources") val sources: ArrayList<Sources>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class Sources(
|
||||
@JsonProperty("src") val src: String? = null,
|
||||
@JsonProperty("type") val type: String? = null,
|
||||
@JsonProperty("label") val label: String? = null,
|
||||
)
|
||||
|
||||
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class HentaiheavenPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Hentaiheaven())
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 1
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "th"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"TvSeries",
|
||||
"Movie",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=www.i-moviehd.com&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,169 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import org.jsoup.nodes.Element
|
||||
import java.net.URI
|
||||
|
||||
class IMoviehd : MainAPI() {
|
||||
override var mainUrl = "https://www.i-moviehd.com"
|
||||
override var name = "I-Moviehd"
|
||||
override val hasMainPage = true
|
||||
override var lang = "th"
|
||||
override val hasDownloadSupport = true
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Movie,
|
||||
TvType.TvSeries,
|
||||
)
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/page/" to "RECOMMENDATION",
|
||||
"$mainUrl/category/series-ซีรี่ส์/page/" to "NEW SERIES",
|
||||
"$mainUrl/top-movie/page/" to "TOP MOVIES IMDB",
|
||||
"$mainUrl/top-series/page/" to "TOP SERIES IMDB",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val document = app.get(request.data + page).document
|
||||
val home = document.select("div.item-wrap.clearfix div.item").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(fixTitle(request.name), home)
|
||||
}
|
||||
|
||||
private fun getProperAnimeLink(uri: String): String {
|
||||
return if (uri.substringAfter("$mainUrl/").contains("-ep-")) {
|
||||
val title = uri.substringAfter("$mainUrl/").replace(Regex("-ep-[0-9]+"), "")
|
||||
"$mainUrl/$title"
|
||||
} else {
|
||||
uri
|
||||
}
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): SearchResponse? {
|
||||
val title = this.selectFirst("a")?.attr("title") ?: return null
|
||||
val href = getProperAnimeLink(this.selectFirst("a")!!.attr("href"))
|
||||
val posterUrl = fixUrlNull(this.select("img").attr("src"))
|
||||
return newMovieSearchResponse(title, href, TvType.Movie) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val document = app.get("$mainUrl/?s=$query").document
|
||||
|
||||
return document.select("div.item-wrap.clearfix div.item").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
val document = app.get(url).document
|
||||
|
||||
val title = document.selectFirst("h1.entry-title")?.text() ?: return null
|
||||
val poster = document.selectFirst("table#imdbinfo td img")?.attr("src")
|
||||
val tags = document.select("span.categories > a").map { it.text() }
|
||||
|
||||
val tvType = if (document.select("table#Sequel").isNullOrEmpty()
|
||||
) TvType.Movie else TvType.TvSeries
|
||||
val description = document.select("div.entry-content.post_content p").text().trim()
|
||||
val trailer = document.selectFirst("div#tabt iframe")?.attr("sub_src")
|
||||
val rating = document.selectFirst("div.imdb-rating-content span")?.text()?.toRatingInt()
|
||||
|
||||
val recommendations = document.select("div.item-wrap.clearfix div.item").mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
|
||||
return if (tvType == TvType.TvSeries) {
|
||||
val episodes = document.select("table#Sequel tbody tr").mapNotNull {
|
||||
val href = fixUrl(it.selectFirst("a")?.attr("href") ?: return null)
|
||||
val name = it.selectFirst("a")?.text()?.trim() ?: return null
|
||||
Episode(
|
||||
href,
|
||||
name,
|
||||
)
|
||||
}
|
||||
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
|
||||
this.posterUrl = poster
|
||||
this.plot = description
|
||||
this.tags = tags
|
||||
this.rating = rating
|
||||
this.recommendations = recommendations
|
||||
addTrailer(trailer)
|
||||
}
|
||||
} else {
|
||||
newMovieLoadResponse(title, url, TvType.Movie, url) {
|
||||
this.posterUrl = poster
|
||||
this.plot = description
|
||||
this.tags = tags
|
||||
this.rating = rating
|
||||
this.recommendations = recommendations
|
||||
addTrailer(trailer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
val document = app.get(data).document
|
||||
|
||||
document.select("script").find { it.data().contains("\$.getJSON(") }?.data()
|
||||
?.substringAfter("\$.getJSON( \"")?.substringBefore("\"+")?.let { iframe ->
|
||||
val server = app.get("$iframe\\0&b=", referer = "$mainUrl/")
|
||||
.parsedSafe<Server>()?.link
|
||||
val id = app.post(
|
||||
"https://vlp.77player.xyz/initPlayer/${server?.substringAfter("key=")}",
|
||||
referer = server,
|
||||
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
|
||||
).parsedSafe<Source>()?.data?.substringAfter("id=")
|
||||
|
||||
// M3u8Helper.generateM3u8(
|
||||
// this.name,
|
||||
// "https://xxx.77player.xyz/iosplaylist/$id/$id.m3u8",
|
||||
// referer = "$mainUrl/",
|
||||
// headers = mapOf(
|
||||
//// "Origin" to "https://xxx.77player.xyz",
|
||||
// )
|
||||
// ).forEach(callback)
|
||||
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
this.name,
|
||||
this.name,
|
||||
"https://xxx.77player.xyz/iosplaylist/$id/$id.m3u8",
|
||||
referer = "$mainUrl/",
|
||||
quality = Qualities.Unknown.value,
|
||||
isM3u8 = true
|
||||
)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
data class Server(
|
||||
@JsonProperty("ตัวเล่นไว") val link: String?,
|
||||
)
|
||||
|
||||
data class Source(
|
||||
@JsonProperty("data") val data: String?,
|
||||
)
|
||||
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class IMoviehdPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(IMoviehd())
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 10
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "id"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"TvSeries",
|
||||
"Movie",
|
||||
"Anime",
|
||||
"AsianDrama",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=109.234.36.69&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated"/>
|
|
@ -1,228 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import org.jsoup.nodes.Element
|
||||
import java.net.URI
|
||||
|
||||
class IdlixProvider : MainAPI() {
|
||||
override var mainUrl = "https://idlixian.com"
|
||||
private var directUrl = mainUrl
|
||||
override var name = "Idlix"
|
||||
override val hasMainPage = true
|
||||
override var lang = "id"
|
||||
override val hasDownloadSupport = true
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Movie,
|
||||
TvType.TvSeries,
|
||||
TvType.Anime,
|
||||
TvType.AsianDrama
|
||||
)
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/" to "Featured",
|
||||
"$mainUrl/trending/page/?get=movies" to "Trending Movies",
|
||||
"$mainUrl/trending/page/?get=tv" to "Trending TV Series",
|
||||
"$mainUrl/movie/page/" to "Movie Terbaru",
|
||||
"$mainUrl/tvseries/page/" to "TV Series Terbaru",
|
||||
"$mainUrl/network/netflix/page/" to "Netflix",
|
||||
"$mainUrl/genre/anime/page/" to "Anime",
|
||||
"$mainUrl/genre/drama-korea/page/" to "Drama Korea",
|
||||
)
|
||||
|
||||
private fun getBaseUrl(url: String): String {
|
||||
return URI(url).let {
|
||||
"${it.scheme}://${it.host}"
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val url = request.data.split("?")
|
||||
val nonPaged = request.name == "Featured" && page <= 1
|
||||
val req = if (nonPaged) {
|
||||
app.get(request.data)
|
||||
} else {
|
||||
app.get("${url.first()}$page/?${url.lastOrNull()}")
|
||||
}
|
||||
mainUrl = getBaseUrl(req.url)
|
||||
val document = req.document
|
||||
val home = (if (nonPaged) {
|
||||
document.select("div.items.featured article")
|
||||
} else {
|
||||
document.select("div.items.full article, div#archive-content article")
|
||||
}).mapNotNull {
|
||||
it.toSearchResult()
|
||||
}
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun getProperLink(uri: String): String {
|
||||
return when {
|
||||
uri.contains("/episode/") -> {
|
||||
var title = uri.substringAfter("$mainUrl/episode/")
|
||||
title = Regex("(.+?)-season").find(title)?.groupValues?.get(1).toString()
|
||||
"$mainUrl/tvseries/$title"
|
||||
}
|
||||
uri.contains("/season/") -> {
|
||||
var title = uri.substringAfter("$mainUrl/season/")
|
||||
title = Regex("(.+?)-season").find(title)?.groupValues?.get(1).toString()
|
||||
"$mainUrl/tvseries/$title"
|
||||
}
|
||||
else -> {
|
||||
uri
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Element.toSearchResult(): SearchResponse {
|
||||
val title = this.selectFirst("h3 > a")!!.text().replace(Regex("\\(\\d{4}\\)"), "").trim()
|
||||
val href = getProperLink(this.selectFirst("h3 > a")!!.attr("href"))
|
||||
val posterUrl = this.select("div.poster > img").attr("src").toString()
|
||||
val quality = getQualityFromString(this.select("span.quality").text())
|
||||
return newMovieSearchResponse(title, href, TvType.Movie) {
|
||||
this.posterUrl = posterUrl
|
||||
this.quality = quality
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val req = app.get("$mainUrl/search/$query")
|
||||
mainUrl = getBaseUrl(req.url)
|
||||
val document = req.document
|
||||
return document.select("div.result-item").map {
|
||||
val title =
|
||||
it.selectFirst("div.title > a")!!.text().replace(Regex("\\(\\d{4}\\)"), "").trim()
|
||||
val href = getProperLink(it.selectFirst("div.title > a")!!.attr("href"))
|
||||
val posterUrl = it.selectFirst("img")!!.attr("src").toString()
|
||||
newMovieSearchResponse(title, href, TvType.TvSeries) {
|
||||
this.posterUrl = posterUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse {
|
||||
val request = app.get(url)
|
||||
directUrl = getBaseUrl(request.url)
|
||||
val document = request.document
|
||||
val title =
|
||||
document.selectFirst("div.data > h1")?.text()?.replace(Regex("\\(\\d{4}\\)"), "")
|
||||
?.trim().toString()
|
||||
val poster = document.select("div.poster > img").attr("src").toString()
|
||||
val tags = document.select("div.sgeneros > a").map { it.text() }
|
||||
|
||||
val year = Regex(",\\s?(\\d+)").find(
|
||||
document.select("span.date").text().trim()
|
||||
)?.groupValues?.get(1).toString().toIntOrNull()
|
||||
val tvType = if (document.select("ul#section > li:nth-child(1)").text().contains("Episodes")
|
||||
) TvType.TvSeries else TvType.Movie
|
||||
val description = document.select("div.wp-content > p").text().trim()
|
||||
val trailer = document.selectFirst("div.embed iframe")?.attr("src")
|
||||
val rating =
|
||||
document.selectFirst("span.dt_rating_vgs")?.text()?.toRatingInt()
|
||||
val actors = document.select("div.persons > div[itemprop=actor]").map {
|
||||
Actor(it.select("meta[itemprop=name]").attr("content"), it.select("img").attr("src"))
|
||||
}
|
||||
|
||||
val recommendations = document.select("div.owl-item").map {
|
||||
val recName =
|
||||
it.selectFirst("a")!!.attr("href").toString().removeSuffix("/").split("/").last()
|
||||
val recHref = it.selectFirst("a")!!.attr("href")
|
||||
val recPosterUrl = it.selectFirst("img")?.attr("src").toString()
|
||||
newTvSeriesSearchResponse(recName, recHref, TvType.TvSeries) {
|
||||
this.posterUrl = recPosterUrl
|
||||
}
|
||||
}
|
||||
|
||||
return if (tvType == TvType.TvSeries) {
|
||||
val episodes = document.select("ul.episodios > li").map {
|
||||
val href = it.select("a").attr("href")
|
||||
val name = fixTitle(it.select("div.episodiotitle > a").text().trim())
|
||||
val image = it.select("div.imagen > img").attr("src")
|
||||
val episode = it.select("div.numerando").text().replace(" ", "").split("-").last()
|
||||
.toIntOrNull()
|
||||
val season = it.select("div.numerando").text().replace(" ", "").split("-").first()
|
||||
.toIntOrNull()
|
||||
Episode(
|
||||
href,
|
||||
name,
|
||||
season,
|
||||
episode,
|
||||
image
|
||||
)
|
||||
}
|
||||
newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodes) {
|
||||
this.posterUrl = poster
|
||||
this.year = year
|
||||
this.plot = description
|
||||
this.tags = tags
|
||||
this.rating = rating
|
||||
addActors(actors)
|
||||
this.recommendations = recommendations
|
||||
addTrailer(trailer)
|
||||
}
|
||||
} else {
|
||||
newMovieLoadResponse(title, url, TvType.Movie, url) {
|
||||
this.posterUrl = poster
|
||||
this.year = year
|
||||
this.plot = description
|
||||
this.tags = tags
|
||||
this.rating = rating
|
||||
addActors(actors)
|
||||
this.recommendations = recommendations
|
||||
addTrailer(trailer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
val document = app.get(data).document
|
||||
val id = document.select("meta#dooplay-ajax-counter").attr("data-postid")
|
||||
val type = if (data.contains("/movie/")) "movie" else "tv"
|
||||
|
||||
document.select("ul#playeroptionsul > li").map {
|
||||
it.attr("data-nume")
|
||||
}.apmap { nume ->
|
||||
safeApiCall {
|
||||
var source = app.post(
|
||||
url = "$directUrl/wp-admin/admin-ajax.php",
|
||||
data = mapOf(
|
||||
"action" to "doo_player_ajax",
|
||||
"post" to id,
|
||||
"nume" to nume,
|
||||
"type" to type
|
||||
),
|
||||
headers = mapOf("X-Requested-With" to "XMLHttpRequest"),
|
||||
referer = data
|
||||
).parsed<ResponseHash>().embed_url
|
||||
|
||||
if (source.startsWith("https://uservideo.xyz")) {
|
||||
source = app.get(source).document.select("iframe").attr("src")
|
||||
}
|
||||
loadExtractor(source, directUrl, subtitleCallback, callback)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
data class ResponseHash(
|
||||
@JsonProperty("embed_url") val embed_url: String,
|
||||
@JsonProperty("type") val type: String?,
|
||||
)
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class IdlixProviderPlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(IdlixProvider())
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 10
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "en"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf(
|
||||
"AnimeMovie",
|
||||
"Anime",
|
||||
"OVA",
|
||||
)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=www2.kickassanime.ro&sz=%size%"
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.hexated" />
|
|
@ -1,166 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.base64Decode
|
||||
import com.lagradost.cloudstream3.base64DecodeArray
|
||||
import com.lagradost.cloudstream3.base64Encode
|
||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||
import com.lagradost.cloudstream3.utils.AppUtils
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||
import org.jsoup.nodes.Document
|
||||
import java.net.URI
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
object GogoExtractor {
|
||||
|
||||
/**
|
||||
* @param id base64Decode(show_id) + IV
|
||||
* @return the encryption key
|
||||
* */
|
||||
private fun getKey(id: String): String? {
|
||||
return normalSafeApiCall {
|
||||
id.map {
|
||||
it.code.toString(16)
|
||||
}.joinToString("").substring(0, 32)
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt#L60
|
||||
// No Licence on the function
|
||||
private fun cryptoHandler(
|
||||
string: String,
|
||||
iv: String,
|
||||
secretKeyString: String,
|
||||
encrypt: Boolean = true
|
||||
): String {
|
||||
//println("IV: $iv, Key: $secretKeyString, encrypt: $encrypt, Message: $string")
|
||||
val ivParameterSpec = IvParameterSpec(iv.toByteArray())
|
||||
val secretKey = SecretKeySpec(secretKeyString.toByteArray(), "AES")
|
||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
return if (!encrypt) {
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec)
|
||||
String(cipher.doFinal(base64DecodeArray(string)))
|
||||
} else {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec)
|
||||
base64Encode(cipher.doFinal(string.toByteArray()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param iframeUrl something like https://gogoplay4.com/streaming.php?id=XXXXXX
|
||||
* @param mainApiName used for ExtractorLink names and source
|
||||
* @param iv secret iv from site, required non-null if isUsingAdaptiveKeys is off
|
||||
* @param secretKey secret key for decryption from site, required non-null if isUsingAdaptiveKeys is off
|
||||
* @param secretDecryptKey secret key to decrypt the response json, required non-null if isUsingAdaptiveKeys is off
|
||||
* @param isUsingAdaptiveKeys generates keys from IV and ID, see getKey()
|
||||
* @param isUsingAdaptiveData generate encrypt-ajax data based on $("script[data-name='episode']")[0].dataset.value
|
||||
* */
|
||||
suspend fun extractVidstream(
|
||||
iframeUrl: String,
|
||||
mainApiName: String,
|
||||
callback: (ExtractorLink) -> Unit,
|
||||
iv: String?,
|
||||
secretKey: String?,
|
||||
secretDecryptKey: String?,
|
||||
// This could be removed, but i prefer it verbose
|
||||
isUsingAdaptiveKeys: Boolean,
|
||||
isUsingAdaptiveData: Boolean,
|
||||
// If you don't want to re-fetch the document
|
||||
iframeDocument: Document? = null
|
||||
) = safeApiCall {
|
||||
// https://github.com/saikou-app/saikou/blob/3e756bd8e876ad7a9318b17110526880525a5cd3/app/src/main/java/ani/saikou/anime/source/extractors/GogoCDN.kt
|
||||
// No Licence on the following code
|
||||
// Also modified of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/gogoanime/src/eu/kanade/tachiyomi/animeextension/en/gogoanime/extractors/GogoCdnExtractor.kt
|
||||
// License on the code above https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE
|
||||
|
||||
if ((iv == null || secretKey == null || secretDecryptKey == null) && !isUsingAdaptiveKeys)
|
||||
return@safeApiCall
|
||||
|
||||
val id = Regex("id=([^&]+)").find(iframeUrl)!!.value.removePrefix("id=")
|
||||
|
||||
var document: Document? = iframeDocument
|
||||
val foundIv =
|
||||
iv ?: (document ?: app.get(iframeUrl).document.also { document = it })
|
||||
.select("""div.wrapper[class*=container]""")
|
||||
.attr("class").split("-").lastOrNull() ?: return@safeApiCall
|
||||
val foundKey = secretKey ?: getKey(base64Decode(id) + foundIv) ?: return@safeApiCall
|
||||
val foundDecryptKey = secretDecryptKey ?: foundKey
|
||||
|
||||
val uri = URI(iframeUrl)
|
||||
val mainUrl = "https://" + uri.host
|
||||
|
||||
val encryptedId = cryptoHandler(id, foundIv, foundKey)
|
||||
val encryptRequestData = if (isUsingAdaptiveData) {
|
||||
// Only fetch the document if necessary
|
||||
val realDocument = document ?: app.get(iframeUrl).document
|
||||
val dataEncrypted =
|
||||
realDocument.select("script[data-name='episode']").attr("data-value")
|
||||
val headers = cryptoHandler(dataEncrypted, foundIv, foundKey, false)
|
||||
"id=$encryptedId&alias=$id&" + headers.substringAfter("&")
|
||||
} else {
|
||||
"id=$encryptedId&alias=$id"
|
||||
}
|
||||
|
||||
val jsonResponse =
|
||||
app.get(
|
||||
"$mainUrl/encrypt-ajax.php?$encryptRequestData",
|
||||
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
|
||||
)
|
||||
val dataencrypted =
|
||||
jsonResponse.text.substringAfter("{\"data\":\"").substringBefore("\"}")
|
||||
val datadecrypted = cryptoHandler(dataencrypted, foundIv, foundDecryptKey, false)
|
||||
val sources = AppUtils.parseJson<GogoSources>(datadecrypted)
|
||||
|
||||
suspend fun invokeGogoSource(
|
||||
source: GogoSource,
|
||||
sourceCallback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
if (source.file.contains(".m3u8")) {
|
||||
M3u8Helper.generateM3u8(
|
||||
mainApiName,
|
||||
source.file,
|
||||
mainUrl,
|
||||
headers = mapOf("Origin" to "https://plyr.link")
|
||||
).forEach(sourceCallback)
|
||||
} else {
|
||||
sourceCallback.invoke(
|
||||
ExtractorLink(
|
||||
mainApiName,
|
||||
mainApiName,
|
||||
source.file,
|
||||
mainUrl,
|
||||
getQualityFromName(source.label),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sources.source?.forEach {
|
||||
invokeGogoSource(it, callback)
|
||||
}
|
||||
sources.sourceBk?.forEach {
|
||||
invokeGogoSource(it, callback)
|
||||
}
|
||||
}
|
||||
|
||||
data class GogoSources(
|
||||
@JsonProperty("source") val source: List<GogoSource>?,
|
||||
@JsonProperty("sourceBk") val sourceBk: List<GogoSource>?,
|
||||
//val track: List<Any?>,
|
||||
//val advertising: List<Any?>,
|
||||
//val linkiframe: String
|
||||
)
|
||||
|
||||
data class GogoSource(
|
||||
@JsonProperty("file") val file: String,
|
||||
@JsonProperty("label") val label: String?,
|
||||
@JsonProperty("type") val type: String?,
|
||||
@JsonProperty("default") val default: String? = null
|
||||
)
|
||||
}
|
|
@ -1,382 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.hexated.KickassanimeExtractor.invokeAlpha
|
||||
import com.hexated.KickassanimeExtractor.invokeBeta
|
||||
import com.hexated.KickassanimeExtractor.invokeDailymotion
|
||||
import com.hexated.KickassanimeExtractor.invokeGogo
|
||||
import com.hexated.KickassanimeExtractor.invokeMave
|
||||
import com.hexated.KickassanimeExtractor.invokePinkbird
|
||||
import com.hexated.KickassanimeExtractor.invokeSapphire
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
||||
import com.lagradost.cloudstream3.syncproviders.SyncIdName
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
|
||||
open class Kickassanime : MainAPI() {
|
||||
final override var mainUrl = "https://www2.kickassanime.ro"
|
||||
override var name = "Kickassanime"
|
||||
override val hasMainPage = true
|
||||
override var lang = "en"
|
||||
override val hasDownloadSupport = true
|
||||
|
||||
override val supportedSyncNames = setOf(
|
||||
SyncIdName.MyAnimeList,
|
||||
SyncIdName.Anilist
|
||||
)
|
||||
|
||||
override val supportedTypes = setOf(
|
||||
TvType.Anime,
|
||||
TvType.AnimeMovie,
|
||||
TvType.OVA
|
||||
)
|
||||
|
||||
companion object {
|
||||
const val kaast = "https://kaast1.com"
|
||||
private const val consumetAnilist = "https://api.consumet.org/meta/anilist"
|
||||
private const val consumetMal = "https://api.consumet.org/meta/mal"
|
||||
fun getType(t: String): TvType {
|
||||
return when {
|
||||
t.contains("Ova", true) -> TvType.OVA
|
||||
t.contains("Movie", true) -> TvType.AnimeMovie
|
||||
else -> TvType.Anime
|
||||
}
|
||||
}
|
||||
|
||||
fun getStatus(t: String): ShowStatus {
|
||||
return when (t) {
|
||||
"Finished Airing" -> ShowStatus.Completed
|
||||
"Currently Airing" -> ShowStatus.Ongoing
|
||||
else -> ShowStatus.Completed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val mainPage = mainPageOf(
|
||||
"$mainUrl/api/get_anime_list/all/" to "All",
|
||||
"$mainUrl/api/get_anime_list/sub/" to "Sub",
|
||||
"$mainUrl/api/get_anime_list/dub/" to "Dub",
|
||||
)
|
||||
|
||||
override suspend fun getMainPage(
|
||||
page: Int,
|
||||
request: MainPageRequest
|
||||
): HomePageResponse {
|
||||
val home = app.get(request.data + page).parsedSafe<Responses>()?.data?.mapNotNull { media ->
|
||||
media.toSearchResponse()
|
||||
} ?: throw ErrorLoadingException()
|
||||
return newHomePageResponse(request.name, home)
|
||||
}
|
||||
|
||||
private fun getProperAnimeLink(uri: String): String {
|
||||
return when {
|
||||
uri.contains("/episode") -> fixUrl(uri.substringBeforeLast("/"))
|
||||
else -> fixUrl(uri)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Animes.toSearchResponse(): AnimeSearchResponse? {
|
||||
val href = getProperAnimeLink(this.slug ?: return null)
|
||||
val title = this.name ?: return null
|
||||
val posterUrl = getImageUrl(this.poster)
|
||||
val episode = this.episode?.toIntOrNull()
|
||||
val isDub = this.name.contains("(Dub)")
|
||||
return newAnimeSearchResponse(title, href, TvType.Anime) {
|
||||
this.posterUrl = posterUrl
|
||||
addDubStatus(isDub, episode)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun search(query: String): List<SearchResponse> {
|
||||
val document = app.get("$mainUrl/search?q=$query").document
|
||||
val data = document.selectFirst("script:containsData(appData)")?.data()
|
||||
?.substringAfter("\"animes\":[")?.substringBefore("],")
|
||||
return tryParseJson<List<Animes>>("[$data]")?.mapNotNull { media -> media.toSearchResponse() }
|
||||
?: throw ErrorLoadingException()
|
||||
}
|
||||
|
||||
override suspend fun getLoadUrl(name: SyncIdName, id: String): String {
|
||||
val syncId = id.split("/").last()
|
||||
val url = if (name == SyncIdName.Anilist) {
|
||||
"$consumetAnilist/info/$syncId"
|
||||
} else {
|
||||
"$consumetMal/info/$syncId"
|
||||
}
|
||||
|
||||
val res = app.get(url).parsedSafe<SyncInfo>()?.title
|
||||
|
||||
val romanjiUrl = "$mainUrl/anime/${res?.romaji?.createSlug()}"
|
||||
val englishUrl = "$mainUrl/anime/${res?.english?.createSlug()}"
|
||||
|
||||
return if (app.get(romanjiUrl).url != "$mainUrl/") romanjiUrl else englishUrl
|
||||
}
|
||||
|
||||
override suspend fun load(url: String): LoadResponse? {
|
||||
val document = app.get(url).document
|
||||
|
||||
val res = document.selectFirst("script:containsData(appData)")?.data()
|
||||
?.substringAfter("\"anime\":{")?.substringBefore("},\"wkl\"")?.let {
|
||||
tryParseJson<DetailAnime>("{$it}")
|
||||
} ?: throw ErrorLoadingException()
|
||||
|
||||
val title = res.name ?: return null
|
||||
val trackerTitle = res.en_title.orEmpty().ifEmpty { res.name }.getTrackerTitle()
|
||||
val poster = getImageUrl(res.image)
|
||||
val tags = res.genres?.map { it.name ?: return null }
|
||||
val year = res.startdate?.substringBefore("-")?.toIntOrNull()
|
||||
val status = getStatus(res.status ?: return null)
|
||||
val description = res.description
|
||||
|
||||
val episodes = res.episodes?.mapNotNull { eps ->
|
||||
Episode(fixUrl(eps.slug ?: return@mapNotNull null), episode = eps.num?.toIntOrNull())
|
||||
}?.reversed() ?: emptyList()
|
||||
|
||||
val type = res.type?.substringBefore(",")?.trim()?.let {
|
||||
when (it) {
|
||||
"TV Series" -> "tv"
|
||||
"Ova" -> "ova"
|
||||
"ONA" -> "ona"
|
||||
"Movie" -> "movie"
|
||||
else -> "tv"
|
||||
}
|
||||
} ?: if (episodes.size == 1) "movie" else "tv"
|
||||
|
||||
val (malId, anilistId, image, cover) = getTracker(
|
||||
trackerTitle,
|
||||
title.getTrackerTitle(),
|
||||
type,
|
||||
year
|
||||
)
|
||||
|
||||
return newAnimeLoadResponse(title, url, getType(type)) {
|
||||
engName = title
|
||||
posterUrl = image ?: poster
|
||||
backgroundPosterUrl = cover ?: image ?: poster
|
||||
this.year = year
|
||||
addEpisodes(DubStatus.Subbed, episodes)
|
||||
showStatus = status
|
||||
plot = description
|
||||
this.tags = tags
|
||||
addMalId(malId)
|
||||
addAniListId(anilistId?.toIntOrNull())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override suspend fun loadLinks(
|
||||
data: String,
|
||||
isCasting: Boolean,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
|
||||
val document = app.get(data).document
|
||||
val sources = document.selectFirst("script:containsData(appData)")?.data()?.let {
|
||||
tryParseJson<Resources>("{${Regex("(\"episode\":.*),\"wkl").find(it)?.groupValues?.get(1)}}")
|
||||
}?.let { server ->
|
||||
listOf(
|
||||
server.episode?.link1.orEmpty().ifEmpty { server.episode?.link4 },
|
||||
server.ext_servers?.find { it.name == "Vidstreaming" }?.link
|
||||
)
|
||||
}?.filterNotNull()
|
||||
val isDub = data.contains("-dub-")
|
||||
sources?.flatMap {
|
||||
httpsify(it).fixIframe()
|
||||
}?.apmap { (name, iframe) ->
|
||||
val sourceName = fixTitle(name ?: this.name)
|
||||
val link = httpsify(iframe ?: return@apmap null)
|
||||
when {
|
||||
link.startsWith("https://www.dailymotion.com") -> {
|
||||
invokeDailymotion(link, subtitleCallback, callback)
|
||||
}
|
||||
name?.contains(Regex("(?i)(KICKASSANIMEV2|ORIGINAL-QUALITY-V2|BETA-SERVER|DAILYMOTION)")) == true -> {
|
||||
invokeAlpha(sourceName, link, subtitleCallback, callback)
|
||||
}
|
||||
name?.contains(Regex("(?i)(BETAPLAYER)")) == true -> {
|
||||
invokeBeta(sourceName, link, callback)
|
||||
}
|
||||
name?.contains(Regex("(?i)(MAVERICKKI)")) == true -> {
|
||||
invokeMave(sourceName, link, subtitleCallback, callback)
|
||||
}
|
||||
name?.contains(Regex("(?i)(gogo)")) == true -> {
|
||||
invokeGogo(link, subtitleCallback, callback)
|
||||
}
|
||||
name?.contains(Regex("(?i)(SAPPHIRE-DUCK)")) == true -> {
|
||||
invokeSapphire(link, isDub, subtitleCallback, callback)
|
||||
}
|
||||
name?.contains(Regex("(?i)(PINK-BIRD)")) == true -> {
|
||||
invokePinkbird(sourceName, link, callback)
|
||||
}
|
||||
else -> return@apmap null
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun getTracker(
|
||||
title: String?,
|
||||
romajiTitle: String?,
|
||||
type: String?,
|
||||
year: Int?
|
||||
): Tracker {
|
||||
val res = searchAnime(title).orEmpty().ifEmpty { searchAnime(romajiTitle) }
|
||||
val media = res?.find { media ->
|
||||
(media.title?.english.equals(title, true) || media.title?.romaji.equals(
|
||||
title,
|
||||
true
|
||||
)) || (media.type.equals(type, true) && media.releaseDate == year)
|
||||
}
|
||||
return Tracker(media?.malId, media?.aniId, media?.image, media?.cover)
|
||||
}
|
||||
|
||||
private suspend fun searchAnime(title: String?): ArrayList<Results>? {
|
||||
return app.get("$consumetAnilist/$title")
|
||||
.parsedSafe<AniSearch>()?.results
|
||||
}
|
||||
|
||||
data class Tracker(
|
||||
val malId: Int? = null,
|
||||
val aniId: String? = null,
|
||||
val image: String? = null,
|
||||
val cover: String? = null,
|
||||
)
|
||||
|
||||
data class Title(
|
||||
@JsonProperty("romaji") val romaji: String? = null,
|
||||
@JsonProperty("english") val english: String? = null,
|
||||
)
|
||||
|
||||
data class Results(
|
||||
@JsonProperty("id") val aniId: String? = null,
|
||||
@JsonProperty("malId") val malId: Int? = null,
|
||||
@JsonProperty("title") val title: Title? = null,
|
||||
@JsonProperty("releaseDate") val releaseDate: Int? = null,
|
||||
@JsonProperty("type") val type: String? = null,
|
||||
@JsonProperty("image") val image: String? = null,
|
||||
@JsonProperty("cover") val cover: String? = null,
|
||||
)
|
||||
|
||||
data class AniSearch(
|
||||
@JsonProperty("results") val results: ArrayList<Results>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class Genres(
|
||||
@JsonProperty("name") val name: String? = null,
|
||||
@JsonProperty("slug") val slug: String? = null,
|
||||
)
|
||||
|
||||
data class Episodes(
|
||||
@JsonProperty("epnum") val epnum: String? = null,
|
||||
@JsonProperty("name") val name: String? = null,
|
||||
@JsonProperty("slug") val slug: String? = null,
|
||||
@JsonProperty("createddate") val createddate: String? = null,
|
||||
@JsonProperty("num") val num: String? = null,
|
||||
)
|
||||
|
||||
data class DetailAnime(
|
||||
@JsonProperty("name") val name: String? = null,
|
||||
@JsonProperty("en_title") val en_title: String? = null,
|
||||
@JsonProperty("slug") val slug: String? = null,
|
||||
@JsonProperty("description") val description: String? = null,
|
||||
@JsonProperty("status") val status: String? = null,
|
||||
@JsonProperty("image") val image: String? = null,
|
||||
@JsonProperty("startdate") val startdate: String? = null,
|
||||
@JsonProperty("broadcast_day") val broadcast_day: String? = null,
|
||||
@JsonProperty("broadcast_time") val broadcast_time: String? = null,
|
||||
@JsonProperty("type") val type: String? = null,
|
||||
@JsonProperty("episodes") val episodes: ArrayList<Episodes>? = null,
|
||||
@JsonProperty("genres") val genres: ArrayList<Genres>? = null,
|
||||
)
|
||||
|
||||
data class Animes(
|
||||
@JsonProperty("episode") val episode: String? = null,
|
||||
@JsonProperty("slug") val slug: String? = null,
|
||||
@JsonProperty("type") val type: String? = null,
|
||||
@JsonProperty("episode_date") val episode_date: String? = null,
|
||||
@JsonProperty("name") val name: String? = null,
|
||||
@JsonProperty("poster") val poster: String? = null,
|
||||
)
|
||||
|
||||
data class Responses(
|
||||
@JsonProperty("data") val data: ArrayList<Animes>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class Iframe(
|
||||
@JsonProperty("name") val name: String? = null,
|
||||
@JsonProperty("src") val src: String? = null,
|
||||
)
|
||||
|
||||
data class ExtServers(
|
||||
@JsonProperty("name") val name: String? = null,
|
||||
@JsonProperty("link") val link: String? = null,
|
||||
)
|
||||
|
||||
data class Eps(
|
||||
@JsonProperty("link1") val link1: String? = null,
|
||||
@JsonProperty("link4") val link4: String? = null,
|
||||
)
|
||||
|
||||
data class Resources(
|
||||
@JsonProperty("episode") val episode: Eps? = null,
|
||||
@JsonProperty("ext_servers") val ext_servers: ArrayList<ExtServers>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class BetaSources(
|
||||
@JsonProperty("file") val file: String? = null,
|
||||
@JsonProperty("label") val label: String? = null,
|
||||
)
|
||||
|
||||
data class AlphaSources(
|
||||
@JsonProperty("file") val file: String? = null,
|
||||
@JsonProperty("label") val label: String? = null,
|
||||
)
|
||||
|
||||
data class MaveSubtitles(
|
||||
@JsonProperty("name") val name: String? = null,
|
||||
@JsonProperty("src") val src: String? = null,
|
||||
)
|
||||
|
||||
data class MaveSources(
|
||||
@JsonProperty("hls") val hls: String? = null,
|
||||
@JsonProperty("subtitles") val subtitles: ArrayList<MaveSubtitles>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class SapphireSubtitles(
|
||||
@JsonProperty("language") val language: String? = null,
|
||||
@JsonProperty("url") val url: String? = null,
|
||||
)
|
||||
|
||||
data class SapphireStreams(
|
||||
@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 SapphireSources(
|
||||
@JsonProperty("streams") val streams: ArrayList<SapphireStreams>? = arrayListOf(),
|
||||
@JsonProperty("subtitles") val subtitles: ArrayList<SapphireSubtitles>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class PinkbirdSources(
|
||||
@JsonProperty("data") val data: ArrayList<PinkbirdData>? = arrayListOf(),
|
||||
)
|
||||
|
||||
data class PinkbirdData(
|
||||
@JsonProperty("eid") val eid: String? = null,
|
||||
@JsonProperty("lh") val lh: String? = null,
|
||||
)
|
||||
|
||||
data class SyncTitle(
|
||||
@JsonProperty("romaji") val romaji: String? = null,
|
||||
@JsonProperty("english") val english: String? = null,
|
||||
)
|
||||
|
||||
data class SyncInfo(
|
||||
@JsonProperty("title") val title: SyncTitle? = null,
|
||||
)
|
||||
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.SubtitleFile
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.argamap
|
||||
import com.lagradost.cloudstream3.base64Decode
|
||||
import com.lagradost.cloudstream3.utils.*
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
object KickassanimeExtractor : Kickassanime() {
|
||||
|
||||
suspend fun invokePinkbird(
|
||||
name: String,
|
||||
url: String? = null,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
val fixUrl = url?.replace(Regex("(player|embed)\\.php"), "pref.php")
|
||||
app.get(fixUrl ?: return,
|
||||
).parsedSafe<PinkbirdSources>()?.data?.map { source ->
|
||||
val eid = base64Decode(source.eid ?: return@map null)
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
name,
|
||||
name,
|
||||
"https://pb.kaast1.com/manifest/$eid/master.m3u8",
|
||||
"$kaast/",
|
||||
Qualities.P1080.value,
|
||||
true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun invokeAlpha(
|
||||
name: String,
|
||||
url: String? = null,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
val fixUrl = url?.replace(Regex("(player|embed)\\.php"), "pref.php")
|
||||
val data = app.get(
|
||||
fixUrl ?: return,
|
||||
referer = kaast
|
||||
).document.selectFirst("script:containsData(Base64.decode)")?.data()
|
||||
?.substringAfter("Base64.decode(\"")?.substringBefore("\")")?.let { base64Decode(it) } ?: return
|
||||
|
||||
if (name == "Dailymotion") {
|
||||
val iframe = Jsoup.parse(data).select("iframe").attr("src")
|
||||
invokeDailymotion(iframe, subtitleCallback, callback)
|
||||
} else {
|
||||
val json = data.substringAfter("sources: [").substringBefore("],")
|
||||
AppUtils.tryParseJson<List<AlphaSources>>("[$json]")?.map {
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
name,
|
||||
name,
|
||||
it.file ?: return@map null,
|
||||
url,
|
||||
getQualityFromName(it.label)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
suspend fun invokeBeta(
|
||||
name: String,
|
||||
url: String? = null,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
app.get(
|
||||
url ?: return,
|
||||
referer = kaast
|
||||
).document.selectFirst("script:containsData(JSON.parse)")?.data()
|
||||
?.substringAfter("JSON.parse('")?.substringBeforeLast("')")
|
||||
?.let { AppUtils.tryParseJson<List<BetaSources>>(it) }?.map {
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
name,
|
||||
name,
|
||||
it.file ?: return@map null,
|
||||
getBaseUrl(url),
|
||||
getQualityFromName(it.label)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun invokeMave(
|
||||
name: String,
|
||||
url: String? = null,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit,
|
||||
) {
|
||||
val fixUrl = url?.replace("/embed/", "/api/source/") ?: return
|
||||
val base = getBaseUrl(url)
|
||||
val data = app.get(fixUrl, referer = url).parsedSafe<MaveSources>()
|
||||
|
||||
M3u8Helper.generateM3u8(
|
||||
if(data?.subtitles.isNullOrEmpty()) "$name [Hardsub]" else "$name [Softsub]",
|
||||
fixUrl(data?.hls ?: return, base),
|
||||
url
|
||||
).forEach(callback)
|
||||
|
||||
data.subtitles?.map { sub ->
|
||||
subtitleCallback.invoke(
|
||||
SubtitleFile(
|
||||
sub.name ?: "",
|
||||
fixUrl(sub.src ?: return@map null, base)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
suspend fun invokeSapphire(
|
||||
url: String? = null,
|
||||
isDub: Boolean = false,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit,
|
||||
) {
|
||||
var data = app.get("$url&action=config", referer = url).text
|
||||
while(true) {
|
||||
if(data.startsWith("{") || data == "null") break
|
||||
data = data.base64Decode()
|
||||
}
|
||||
AppUtils.tryParseJson<SapphireSources>(data).let { res ->
|
||||
res?.streams?.filter { it.format == "adaptive_hls" }?.reversed()?.map { source ->
|
||||
val name = if(isDub) source.audio_lang else source.hardsub_lang.orEmpty().ifEmpty { "raw" }
|
||||
M3u8Helper.generateM3u8(
|
||||
"Crunchyroll [$name]",
|
||||
source.url ?: return@map null,
|
||||
"https://static.crunchyroll.com/",
|
||||
).forEach(callback)
|
||||
}
|
||||
res?.subtitles?.map { sub ->
|
||||
subtitleCallback.invoke(
|
||||
SubtitleFile(
|
||||
getLanguage(sub.language ?: return@map null) ?: sub.language,
|
||||
sub.url ?: return@map null
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
GogoExtractor.extractVidstream(
|
||||
iframe.url,
|
||||
"Gogoanime",
|
||||
callback,
|
||||
iv,
|
||||
secretKey,
|
||||
secretDecryptKey,
|
||||
isUsingAdaptiveKeys = false,
|
||||
isUsingAdaptiveData = true,
|
||||
iframeDocument = iframeDoc
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
suspend fun invokeDailymotion(
|
||||
link: String,
|
||||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
) {
|
||||
loadExtractor(link, mainUrl, subtitleCallback) { video ->
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
video.name,
|
||||
video.name,
|
||||
video.url,
|
||||
video.referer,
|
||||
Qualities.P1080.value,
|
||||
video.isM3u8,
|
||||
video.headers,
|
||||
video.extractorData
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package com.hexated
|
||||
|
||||
import com.lagradost.cloudstream3.plugins.CloudstreamPlugin
|
||||
import com.lagradost.cloudstream3.plugins.Plugin
|
||||
import android.content.Context
|
||||
|
||||
@CloudstreamPlugin
|
||||
class KickassanimePlugin: Plugin() {
|
||||
override fun load(context: Context) {
|
||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||
registerMainAPI(Kickassanime())
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
package com.hexated
|
||||
|
||||
import android.util.Base64
|
||||
import com.hexated.KickassanimeExtractor.mainUrl
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.utils.AppUtils
|
||||
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||
import java.net.URI
|
||||
import java.net.URLDecoder
|
||||
|
||||
suspend fun String.fixIframe(): List<Pair<String?, String?>> {
|
||||
return when {
|
||||
this.startsWith("${Kickassanime.kaast}/dust/") -> {
|
||||
val document = app.get(this).document
|
||||
document.selectFirst("script:containsData(sources =)")?.data()
|
||||
?.substringAfter("sources = [")?.substringBefore("];")?.let {
|
||||
AppUtils.tryParseJson<List<Kickassanime.Iframe>>("[$it]")?.map { source ->
|
||||
source.name to source.src
|
||||
}
|
||||
} ?: emptyList()
|
||||
}
|
||||
this.startsWith("${Kickassanime.kaast}/axplayer/") -> {
|
||||
val source = decode(
|
||||
this.substringAfter("&data=").substringBefore("&vref=")
|
||||
)
|
||||
listOf("gogo" to source)
|
||||
}
|
||||
else -> {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun String.base64Decode(): String {
|
||||
return Base64.decode(this, Base64.DEFAULT).toString(Charsets.UTF_8)
|
||||
}
|
||||
|
||||
fun decode(input: String): String =
|
||||
URLDecoder.decode(input, "utf-8").replace(" ", "%20")
|
||||
|
||||
fun String.createSlug(): String {
|
||||
return this.replace(Regex("[^\\w ]+"), "").replace(" ", "-").lowercase()
|
||||
}
|
||||
|
||||
fun String.getTrackerTitle(): String {
|
||||
val blacklist = arrayOf(
|
||||
"Dub",
|
||||
"Uncensored",
|
||||
"TV",
|
||||
"JPN DUB",
|
||||
"Uncensored"
|
||||
).joinToString("|") { "\\($it\\)" }
|
||||
return this.replace(Regex(blacklist), "").trim()
|
||||
}
|
||||
|
||||
fun getImageUrl(link: String?): String? {
|
||||
if (link == null) return null
|
||||
return if (link.startsWith(mainUrl)) link else "$mainUrl/uploads/$link"
|
||||
}
|
||||
|
||||
fun getBaseUrl(url: String): String {
|
||||
return URI(url).let {
|
||||
"${it.scheme}://${it.host}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun getLanguage(language: String?): String? {
|
||||
return SubtitleHelper.fromTwoLettersToLanguage(language ?: return null)
|
||||
?: SubtitleHelper.fromTwoLettersToLanguage(language.substringBefore("-"))
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
// use an integer for version numbers
|
||||
version = 2
|
||||
|
||||
|
||||
cloudstream {
|
||||
language = "en"
|
||||
// All of these properties are optional, you can safely remove them
|
||||
|
||||
// description = "Lorem Ipsum"
|
||||
authors = listOf("Hexated")
|
||||
|
||||
/**
|
||||
* Status int as the following:
|
||||
* 0: Down
|
||||
* 1: Ok
|
||||
* 2: Slow
|
||||
* 3: Beta only
|
||||
* */
|
||||
status = 1 // will be 3 if unspecified
|
||||
tvTypes = listOf("AsianDrama",)
|
||||
|
||||
iconUrl = "https://www.google.com/s2/favicons?domain=kissasian.pe&sz=%size%"
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue