mirror of
https://github.com/hexated/cloudstream-extensions-hexated.git
synced 2024-08-15 00:03:22 +00:00
fix some providers: Anichi, Layarkaca, Anroll, Movierulzhd
This commit is contained in:
parent
20dbd2a774
commit
1e272a0893
9 changed files with 143 additions and 50 deletions
|
@ -1,7 +1,7 @@
|
||||||
import org.jetbrains.kotlin.konan.properties.Properties
|
import org.jetbrains.kotlin.konan.properties.Properties
|
||||||
|
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 4
|
version = 5
|
||||||
|
|
||||||
android {
|
android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
|
|
@ -2,12 +2,12 @@ package com.hexated
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.getTracker
|
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addActors
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||||
import com.lagradost.cloudstream3.extractors.helper.GogoHelper
|
import com.lagradost.cloudstream3.extractors.helper.GogoHelper
|
||||||
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
import com.lagradost.cloudstream3.mvvm.safeApiCall
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||||
|
@ -32,14 +32,6 @@ class Anichi : MainAPI() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getType(t: String?): TvType {
|
|
||||||
return when {
|
|
||||||
t.equals("OVA", true) || t.equals("Special") -> TvType.OVA
|
|
||||||
t.equals("Movie", true) -> TvType.AnimeMovie
|
|
||||||
else -> TvType.Anime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie)
|
override val supportedTypes = setOf(TvType.Anime, TvType.AnimeMovie)
|
||||||
|
|
||||||
private val popularTitle = "Popular"
|
private val popularTitle = "Popular"
|
||||||
|
@ -141,7 +133,6 @@ class Anichi : MainAPI() {
|
||||||
val title = showData.name
|
val title = showData.name
|
||||||
val description = showData.description
|
val description = showData.description
|
||||||
val poster = showData.thumbnail
|
val poster = showData.thumbnail
|
||||||
val type = getType(showData.type ?: "")
|
|
||||||
|
|
||||||
val episodes = showData.availableEpisodesDetail.let {
|
val episodes = showData.availableEpisodesDetail.let {
|
||||||
if (it == null) return@let Pair(null, null)
|
if (it == null) return@let Pair(null, null)
|
||||||
|
@ -164,13 +155,12 @@ class Anichi : MainAPI() {
|
||||||
Pair(Actor(name, image), role)
|
Pair(Actor(name, image), role)
|
||||||
}
|
}
|
||||||
|
|
||||||
val names = showData.altNames?.plus(title)?.filterNotNull() ?: emptyList()
|
val trackers = getTracker(title, showData.altNames?.firstOrNull(), showData.airedStart?.year, showData.season?.quarter, showData.type)
|
||||||
val trackers = getTracker(names, TrackerType.getTypes(type), showData.airedStart?.year)
|
|
||||||
|
|
||||||
return newAnimeLoadResponse(title ?: "", url, TvType.Anime) {
|
return newAnimeLoadResponse(title ?: "", url, TvType.Anime) {
|
||||||
engName = showData.altNames?.firstOrNull()
|
engName = showData.altNames?.firstOrNull()
|
||||||
posterUrl = trackers?.image ?: poster
|
posterUrl = trackers?.coverImage?.extraLarge ?: trackers?.coverImage?.large ?: poster
|
||||||
backgroundPosterUrl = trackers?.cover ?: showData.banner
|
backgroundPosterUrl = trackers?.bannerImage ?: showData.banner
|
||||||
rating = showData.averageScore?.times(100)
|
rating = showData.averageScore?.times(100)
|
||||||
tags = showData.genres
|
tags = showData.genres
|
||||||
year = showData.airedStart?.year
|
year = showData.airedStart?.year
|
||||||
|
@ -184,8 +174,8 @@ class Anichi : MainAPI() {
|
||||||
//this.recommendations = recommendations
|
//this.recommendations = recommendations
|
||||||
|
|
||||||
showStatus = getStatus(showData.status.toString())
|
showStatus = getStatus(showData.status.toString())
|
||||||
addMalId(trackers?.malId)
|
addMalId(trackers?.idMal)
|
||||||
addAniListId(trackers?.aniId?.toIntOrNull())
|
addAniListId(trackers?.id)
|
||||||
plot = description?.replace(Regex("""<(.*?)>"""), "")
|
plot = description?.replace(Regex("""<(.*?)>"""), "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,6 +263,14 @@ class Anichi : MainAPI() {
|
||||||
isDash = server.resolutionStr == "Dash 1"
|
isDash = server.resolutionStr == "Dash 1"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
server.subtitles?.map { sub ->
|
||||||
|
subtitleCallback.invoke(
|
||||||
|
SubtitleFile(
|
||||||
|
SubtitleHelper.fromTwoLettersToLanguage(sub.lang ?: "") ?: sub.lang ?: "",
|
||||||
|
httpsify(sub.src ?: return@map)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -315,7 +313,8 @@ class Anichi : MainAPI() {
|
||||||
return meta.map { eps ->
|
return meta.map { eps ->
|
||||||
Episode(
|
Episode(
|
||||||
AnichiLoadData(id, lang, eps).toJson(),
|
AnichiLoadData(id, lang, eps).toJson(),
|
||||||
"Ep $eps"
|
"Ep $eps",
|
||||||
|
episode = eps.toIntOrNull()
|
||||||
)
|
)
|
||||||
}.reversed()
|
}.reversed()
|
||||||
}
|
}
|
||||||
|
@ -382,6 +381,69 @@ class Anichi : MainAPI() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun getTracker(name: String?, altName: String?, year: Int?, season: String?, type: String?): AniMedia? {
|
||||||
|
val ids = fetchId(name, year, season, type)
|
||||||
|
return if (ids?.id == null && ids?.idMal == null) fetchId(
|
||||||
|
altName,
|
||||||
|
year,
|
||||||
|
season,
|
||||||
|
type
|
||||||
|
) else ids
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun fetchId(title: String?, year: Int?, season: String?, type: String?): AniMedia? {
|
||||||
|
val query = """
|
||||||
|
query (
|
||||||
|
${'$'}page: Int = 1
|
||||||
|
${'$'}search: String
|
||||||
|
${'$'}sort: [MediaSort] = [POPULARITY_DESC, SCORE_DESC]
|
||||||
|
${'$'}type: MediaType
|
||||||
|
${'$'}season: MediaSeason
|
||||||
|
${'$'}year: String
|
||||||
|
${'$'}format: [MediaFormat]
|
||||||
|
) {
|
||||||
|
Page(page: ${'$'}page, perPage: 20) {
|
||||||
|
media(
|
||||||
|
search: ${'$'}search
|
||||||
|
sort: ${'$'}sort
|
||||||
|
type: ${'$'}type
|
||||||
|
season: ${'$'}season
|
||||||
|
startDate_like: ${'$'}year
|
||||||
|
format_in: ${'$'}format
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
idMal
|
||||||
|
coverImage { extraLarge large }
|
||||||
|
bannerImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent().trim()
|
||||||
|
|
||||||
|
val variables = mapOf(
|
||||||
|
"search" to title,
|
||||||
|
"sort" to "SEARCH_MATCH",
|
||||||
|
"type" to "ANIME",
|
||||||
|
"season" to if(type.equals("ona", true)) "" else season?.uppercase(),
|
||||||
|
"year" to "$year%",
|
||||||
|
"format" to listOf(type?.uppercase())
|
||||||
|
).filterValues { value -> value != null && value.toString().isNotEmpty() }
|
||||||
|
|
||||||
|
val data = mapOf(
|
||||||
|
"query" to query,
|
||||||
|
"variables" to variables
|
||||||
|
).toJson().toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull())
|
||||||
|
|
||||||
|
return try {
|
||||||
|
app.post("https://graphql.anilist.co", requestBody = data)
|
||||||
|
.parsedSafe<AniSearch>()?.data?.Page?.media?.firstOrNull()
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
logError(t)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val apiUrl = BuildConfig.ANICHI_API
|
private const val apiUrl = BuildConfig.ANICHI_API
|
||||||
private const val serverUrl = BuildConfig.ANICHI_SERVER
|
private const val serverUrl = BuildConfig.ANICHI_SERVER
|
||||||
|
@ -406,6 +468,30 @@ class Anichi : MainAPI() {
|
||||||
val episode: String
|
val episode: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class CoverImage(
|
||||||
|
@JsonProperty("extraLarge") var extraLarge: String? = null,
|
||||||
|
@JsonProperty("large") var large: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AniMedia(
|
||||||
|
@JsonProperty("id") var id: Int? = null,
|
||||||
|
@JsonProperty("idMal") var idMal: Int? = null,
|
||||||
|
@JsonProperty("coverImage") var coverImage: CoverImage? = null,
|
||||||
|
@JsonProperty("bannerImage") var bannerImage: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AniPage(
|
||||||
|
@JsonProperty("media") var media: ArrayList<AniMedia> = arrayListOf()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AniData(
|
||||||
|
@JsonProperty("Page") var Page: AniPage? = AniPage()
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AniSearch(
|
||||||
|
@JsonProperty("data") var data: AniData? = AniData()
|
||||||
|
)
|
||||||
|
|
||||||
data class AkIframe(
|
data class AkIframe(
|
||||||
@JsonProperty("idUrl") val idUrl: String? = null,
|
@JsonProperty("idUrl") val idUrl: String? = null,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 1
|
version = 2
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
language = "pt-pt"
|
language = "pt-pt"
|
||||||
|
|
|
@ -35,7 +35,7 @@ class Anroll : MainAPI() {
|
||||||
): HomePageResponse {
|
): HomePageResponse {
|
||||||
val document = app.get("$mainUrl/home").document
|
val document = app.get("$mainUrl/home").document
|
||||||
val home = mutableListOf<HomePageList>()
|
val home = mutableListOf<HomePageList>()
|
||||||
document.select("div.sc-f5d5b250-1.iJHcsI").map { div ->
|
document.select("div.hAbQAe").map { div ->
|
||||||
val header = div.selectFirst("h2")?.text() ?: return@map
|
val header = div.selectFirst("h2")?.text() ?: return@map
|
||||||
val child = HomePageList(
|
val child = HomePageList(
|
||||||
header,
|
header,
|
||||||
|
@ -91,11 +91,11 @@ class Anroll : MainAPI() {
|
||||||
val fixUrl = getProperAnimeLink(url) ?: throw ErrorLoadingException()
|
val fixUrl = getProperAnimeLink(url) ?: throw ErrorLoadingException()
|
||||||
val document = app.get(fixUrl).document
|
val document = app.get(fixUrl).document
|
||||||
|
|
||||||
val article = document.selectFirst("article.sc-f5d5b250-9") ?: return null
|
val article = document.selectFirst("article.animedetails") ?: return null
|
||||||
val title = article.selectFirst("h2")?.text() ?: return null
|
val title = article.selectFirst("h2")?.text() ?: return null
|
||||||
val poster = fixUrlNull(document.select("article.sc-f5d5b250-8 img").attr("src"))
|
val poster = fixUrlNull(document.select("section.animecontent img").attr("src"))
|
||||||
val tags = article.select("div#generos a").map { it.text() }
|
val tags = article.select("div#generos a").map { it.text() }
|
||||||
val year = article.selectFirst("div.sc-f5d5b250-4")?.nextElementSibling()?.text()
|
val year = article.selectFirst("div.dfuefM")?.nextElementSibling()?.text()
|
||||||
?.toIntOrNull()
|
?.toIntOrNull()
|
||||||
val description = document.select("div.sinopse").text().trim()
|
val description = document.select("div.sinopse").text().trim()
|
||||||
val type = if (fixUrl.contains("/a/")) TvType.Anime else TvType.AnimeMovie
|
val type = if (fixUrl.contains("/a/")) TvType.Anime else TvType.AnimeMovie
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// use an integer for version numbers
|
// use an integer for version numbers
|
||||||
version = 13
|
version = 14
|
||||||
|
|
||||||
|
|
||||||
cloudstream {
|
cloudstream {
|
||||||
|
|
|
@ -6,11 +6,10 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
import java.net.URI
|
|
||||||
|
|
||||||
class LayarKacaProvider : MainAPI() {
|
class LayarKacaProvider : MainAPI() {
|
||||||
override var mainUrl = "https://d21.fun"
|
override var mainUrl = "https://tv.lk21official.pro"
|
||||||
private var seriesUrl = "https://tv.nontondrama.click"
|
private var seriesUrl = "https://tv1.nontondrama.click"
|
||||||
override var name = "LayarKaca"
|
override var name = "LayarKaca"
|
||||||
override val hasMainPage = true
|
override val hasMainPage = true
|
||||||
override var lang = "id"
|
override var lang = "id"
|
||||||
|
@ -57,7 +56,7 @@ class LayarKacaProvider : MainAPI() {
|
||||||
private fun Element.toSearchResult(): SearchResponse? {
|
private fun Element.toSearchResult(): SearchResponse? {
|
||||||
val title = this.selectFirst("h1.grid-title > a")?.ownText()?.trim() ?: return null
|
val title = this.selectFirst("h1.grid-title > a")?.ownText()?.trim() ?: return null
|
||||||
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
|
val href = fixUrl(this.selectFirst("a")!!.attr("href"))
|
||||||
val posterUrl = fixUrlNull(this.selectFirst(".grid-poster > a > img")?.attr("src"))
|
val posterUrl = fixUrlNull(this.selectFirst("img")?.attr("src"))
|
||||||
val type =
|
val type =
|
||||||
if (this.selectFirst("div.last-episode") == null) TvType.Movie else TvType.TvSeries
|
if (this.selectFirst("div.last-episode") == null) TvType.Movie else TvType.TvSeries
|
||||||
return if (type == TvType.TvSeries) {
|
return if (type == TvType.TvSeries) {
|
||||||
|
@ -176,33 +175,34 @@ class LayarKacaProvider : MainAPI() {
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
invokeCast(link, callback)
|
loadExtractor(link, bananalicious, subtitleCallback, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun invokeCast(
|
|
||||||
url: String,
|
|
||||||
callback: (ExtractorLink) -> Unit
|
|
||||||
) {
|
|
||||||
val response = app.get(url, referer = bananalicious).document
|
|
||||||
response.select("script[type=text/javascript]").map { script ->
|
|
||||||
if (script.data().contains(Regex("eval\\(function\\(p,a,c,k,e,[rd]"))) {
|
|
||||||
val unpackedscript = getAndUnpack(script.data())
|
|
||||||
val m3u8Regex = Regex("file.\"(.*?m3u8.*?)\"")
|
|
||||||
val m3u8 = m3u8Regex.find(unpackedscript)?.destructured?.component1() ?: ""
|
|
||||||
if (m3u8.isNotEmpty()) {
|
|
||||||
M3u8Helper.generateM3u8(
|
|
||||||
fixTitle(URI(url).host).substringBefore("."),
|
|
||||||
m3u8,
|
|
||||||
mainUrl
|
|
||||||
).forEach(callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun decode(input: String): String = URLDecoder.decode(input, "utf-8").replace(" ", "%20")
|
private fun decode(input: String): String = URLDecoder.decode(input, "utf-8").replace(" ", "%20")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open class Emturbovid : ExtractorApi() {
|
||||||
|
override val name = "Emturbovid"
|
||||||
|
override val mainUrl = "https://emturbovid.com"
|
||||||
|
override val requiresReferer = true
|
||||||
|
|
||||||
|
override suspend fun getUrl(
|
||||||
|
url: String,
|
||||||
|
referer: String?,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
|
val response = app.get(url, referer = referer)
|
||||||
|
val m3u8 = Regex("[\"'](.*?master\\.m3u8.*?)[\"']").find(response.text)?.groupValues?.getOrNull(1)
|
||||||
|
M3u8Helper.generateM3u8(
|
||||||
|
name,
|
||||||
|
m3u8 ?: return,
|
||||||
|
mainUrl
|
||||||
|
).forEach(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -10,5 +10,6 @@ class LayarKacaProviderPlugin: Plugin() {
|
||||||
override fun load(context: Context) {
|
override fun load(context: Context) {
|
||||||
// All providers should be added in this manner. Please don't edit the providers list directly.
|
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||||
registerMainAPI(LayarKacaProvider())
|
registerMainAPI(LayarKacaProvider())
|
||||||
|
registerExtractorAPI(Emturbovid())
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,6 +11,11 @@ import kotlin.random.Random
|
||||||
|
|
||||||
const val twoEmbedAPI = "https://www.2embed.to"
|
const val twoEmbedAPI = "https://www.2embed.to"
|
||||||
|
|
||||||
|
class Sbnmp : Sbflix() {
|
||||||
|
override val name = "Sbnmp"
|
||||||
|
override var mainUrl = "https://sbnmp.bar"
|
||||||
|
}
|
||||||
|
|
||||||
class Sbrulz : Sbflix() {
|
class Sbrulz : Sbflix() {
|
||||||
override val name = "Sbrulz"
|
override val name = "Sbrulz"
|
||||||
override var mainUrl = "https://sbrulz.xyz"
|
override var mainUrl = "https://sbrulz.xyz"
|
||||||
|
|
|
@ -13,5 +13,6 @@ class MovierulzhdPlugin: Plugin() {
|
||||||
registerExtractorAPI(Sbflix())
|
registerExtractorAPI(Sbflix())
|
||||||
registerExtractorAPI(Sbrulz())
|
registerExtractorAPI(Sbrulz())
|
||||||
registerExtractorAPI(Sbmiz())
|
registerExtractorAPI(Sbmiz())
|
||||||
|
registerExtractorAPI(Sbnmp())
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue