mirror of
https://github.com/hexated/cloudstream-extensions-hexated.git
synced 2024-08-15 00:03:22 +00:00
218 lines
7.5 KiB
Kotlin
218 lines
7.5 KiB
Kotlin
|
package com.hexated
|
||
|
|
||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||
|
import com.lagradost.cloudstream3.*
|
||
|
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||
|
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||
|
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.M3u8Helper
|
||
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
||
|
import java.util.ArrayList
|
||
|
|
||
|
class KisskhProvider : MainAPI() {
|
||
|
override var mainUrl = "https://kisskh.me"
|
||
|
override var name = "Kisskh"
|
||
|
override val hasMainPage = true
|
||
|
override val hasDownloadSupport = true
|
||
|
override val supportedTypes = setOf(
|
||
|
TvType.AsianDrama,
|
||
|
TvType.Anime
|
||
|
)
|
||
|
|
||
|
override val mainPage = mainPageOf(
|
||
|
"&type=2&sub=0&country=2&status=0&order=1" to "Movie Popular",
|
||
|
"&type=2&sub=0&country=2&status=0&order=2" to "Movie Last Update",
|
||
|
"&type=1&sub=0&country=2&status=0&order=1" to "TVSeries Popular",
|
||
|
"&type=1&sub=0&country=2&status=0&order=2" to "TVSeries Last Update",
|
||
|
"&type=3&sub=0&country=0&status=0&order=1" to "Anime Popular",
|
||
|
"&type=3&sub=0&country=0&status=0&order=2" to "Anime Last Update",
|
||
|
"&type=4&sub=0&country=0&status=0&order=1" to "Hollywood Popular",
|
||
|
"&type=4&sub=0&country=0&status=0&order=2" to "Hollywood Last Update",
|
||
|
)
|
||
|
|
||
|
override suspend fun getMainPage(
|
||
|
page: Int,
|
||
|
request: MainPageRequest
|
||
|
): HomePageResponse {
|
||
|
val home = app.get("$mainUrl/api/DramaList/List?page=$page${request.data}")
|
||
|
.parsedSafe<Responses>()?.data
|
||
|
?.mapNotNull { media ->
|
||
|
media.toSearchResponse()
|
||
|
} ?: throw ErrorLoadingException("Invalid Json reponse")
|
||
|
return newHomePageResponse(
|
||
|
list = HomePageList(
|
||
|
name = request.name,
|
||
|
list = home,
|
||
|
isHorizontalImages = true
|
||
|
),
|
||
|
hasNext = true
|
||
|
)
|
||
|
}
|
||
|
|
||
|
private fun Media.toSearchResponse(): SearchResponse? {
|
||
|
|
||
|
return newAnimeSearchResponse(
|
||
|
title ?: return null,
|
||
|
"$title/$id",
|
||
|
TvType.TvSeries,
|
||
|
) {
|
||
|
this.posterUrl = thumbnail
|
||
|
addSub(episodesCount)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override suspend fun search(query: String): List<SearchResponse> {
|
||
|
val searchResponse =
|
||
|
app.get("$mainUrl/api/DramaList/Search?q=$query&type=0", referer = "$mainUrl/").text
|
||
|
return tryParseJson<ArrayList<Media>>(searchResponse)?.mapNotNull { media ->
|
||
|
media.toSearchResponse()
|
||
|
} ?: throw ErrorLoadingException("Invalid Json reponse")
|
||
|
}
|
||
|
|
||
|
private fun getTitle(str: String): String {
|
||
|
return str.replace(Regex("[^a-zA-Z0-9]"), "-")
|
||
|
}
|
||
|
|
||
|
override suspend fun load(url: String): LoadResponse? {
|
||
|
val id = url.split("/")
|
||
|
val res = app.get(
|
||
|
"$mainUrl/api/DramaList/Drama/${id.last()}?isq=false",
|
||
|
referer = "$mainUrl/Drama/${
|
||
|
getTitle(id.first())
|
||
|
}?id=${id.last()}"
|
||
|
).parsedSafe<MediaDetail>()
|
||
|
?: throw ErrorLoadingException("Invalid Json reponse")
|
||
|
|
||
|
val episodes = res.episodes?.map { eps ->
|
||
|
Episode(
|
||
|
data = Data(res.title, eps.number, res.id, eps.id).toJson(),
|
||
|
episode = eps.number
|
||
|
)
|
||
|
} ?: throw ErrorLoadingException("No Episode")
|
||
|
|
||
|
return newTvSeriesLoadResponse(
|
||
|
res.title ?: return null,
|
||
|
url,
|
||
|
if (res.type == "Movie" || episodes.size == 1) TvType.Movie else TvType.TvSeries,
|
||
|
episodes
|
||
|
) {
|
||
|
this.posterUrl = res.thumbnail
|
||
|
this.year = res.releaseDate?.split("-")?.first()?.toIntOrNull()
|
||
|
this.plot = res.description
|
||
|
this.tags = listOf("${res.country}", "${res.status}", "${res.type}")
|
||
|
this.showStatus = when (res.status) {
|
||
|
"Completed" -> ShowStatus.Completed
|
||
|
"Ongoing" -> ShowStatus.Ongoing
|
||
|
else -> null
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
private fun getLanguage(str: String): String {
|
||
|
return when (str) {
|
||
|
"Indonesia" -> "Indonesian"
|
||
|
else -> str
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override suspend fun loadLinks(
|
||
|
data: String,
|
||
|
isCasting: Boolean,
|
||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||
|
callback: (ExtractorLink) -> Unit
|
||
|
): Boolean {
|
||
|
|
||
|
val loadData = parseJson<Data>(data)
|
||
|
|
||
|
app.get(
|
||
|
"$mainUrl/api/DramaList/Episode/${loadData.epsId}.png?err=false&ts=&time=",
|
||
|
referer = "$mainUrl/Drama/${getTitle("${loadData.title}")}/Episode-${loadData.eps}?id=${loadData.id}&ep=${loadData.epsId}&page=0&pageSize=100"
|
||
|
).parsedSafe<Sources>()?.let { source ->
|
||
|
listOf(source.video, source.thirdParty).apmap { link ->
|
||
|
safeApiCall {
|
||
|
if (link?.contains(".m3u8") == true) {
|
||
|
M3u8Helper.generateM3u8(
|
||
|
this.name,
|
||
|
link,
|
||
|
referer = "$mainUrl/",
|
||
|
headers = mapOf("Origin" to mainUrl)
|
||
|
).forEach(callback)
|
||
|
} else {
|
||
|
loadExtractor(
|
||
|
link?.substringBefore("=http") ?: return@safeApiCall,
|
||
|
"$mainUrl/",
|
||
|
subtitleCallback,
|
||
|
callback
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// parsedSafe doesn't work in <List<Object>>
|
||
|
app.get("$mainUrl/api/Sub/${loadData.epsId}").text.let { res ->
|
||
|
tryParseJson<List<Subtitle>>(res)?.map { sub ->
|
||
|
subtitleCallback.invoke(
|
||
|
SubtitleFile(
|
||
|
getLanguage(sub.label ?: return@map),
|
||
|
sub.src ?: return@map
|
||
|
)
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
|
||
|
}
|
||
|
|
||
|
data class Data(
|
||
|
val title: String?,
|
||
|
val eps: Int?,
|
||
|
val id: Int?,
|
||
|
val epsId: Int?,
|
||
|
)
|
||
|
|
||
|
data class Sources(
|
||
|
@JsonProperty("Video") val video: String?,
|
||
|
@JsonProperty("ThirdParty") val thirdParty: String?,
|
||
|
)
|
||
|
|
||
|
data class Subtitle(
|
||
|
@JsonProperty("src") val src: String?,
|
||
|
@JsonProperty("label") val label: String?,
|
||
|
)
|
||
|
|
||
|
data class Responses(
|
||
|
@JsonProperty("data") val data: ArrayList<Media>? = arrayListOf(),
|
||
|
)
|
||
|
|
||
|
data class Media(
|
||
|
@JsonProperty("episodesCount") val episodesCount: Int?,
|
||
|
@JsonProperty("thumbnail") val thumbnail: String?,
|
||
|
@JsonProperty("id") val id: Int?,
|
||
|
@JsonProperty("title") val title: String?,
|
||
|
)
|
||
|
|
||
|
data class Episodes(
|
||
|
@JsonProperty("id") val id: Int?,
|
||
|
@JsonProperty("number") val number: Int?,
|
||
|
@JsonProperty("sub") val sub: Int?,
|
||
|
)
|
||
|
|
||
|
data class MediaDetail(
|
||
|
@JsonProperty("description") val description: String?,
|
||
|
@JsonProperty("releaseDate") val releaseDate: String?,
|
||
|
@JsonProperty("status") val status: String?,
|
||
|
@JsonProperty("type") val type: String?,
|
||
|
@JsonProperty("country") val country: String?,
|
||
|
@JsonProperty("episodes") val episodes: ArrayList<Episodes>? = arrayListOf(),
|
||
|
@JsonProperty("thumbnail") val thumbnail: String?,
|
||
|
@JsonProperty("id") val id: Int?,
|
||
|
@JsonProperty("title") val title: String?,
|
||
|
)
|
||
|
|
||
|
}
|