fix(src/es): Fix video extraction and fix json parsing (#3140)

This commit is contained in:
imper1aldev 2024-04-16 00:52:40 -06:00 committed by GitHub
parent 0c552ed88e
commit 36b7d06ec7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 123 additions and 146 deletions

View File

@ -1,7 +1,7 @@
ext {
extName = 'Doramasflix'
extClass = '.Doramasflix'
extVersionCode = 18
extVersionCode = 19
}
apply from: "$rootDir/common.gradle"

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.animeextension.es.doramasflix
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject
// -----------------------Season models------------------------//
@Serializable
@ -27,7 +26,6 @@ data class ListSeason(
@SerialName("serie_name")
val serieName: String?,
val poster: String?,
val backdrop: String?,
@SerialName("__typename")
val typename: String,
)
@ -53,50 +51,17 @@ data class ListEpisode(
val serieName: String?,
@SerialName("serie_name_es")
val serieNameEs: String?,
@SerialName("serie_id")
val serieId: String?,
@SerialName("still_path")
val stillPath: String?,
@SerialName("air_date")
val airDate: String?,
@SerialName("season_number")
val seasonNumber: Long?,
@SerialName("episode_number")
val episodeNumber: Long?,
// val languages: List<Any?>,
val poster: String?,
val backdrop: String?,
@SerialName("__typename")
val typename: String,
)
// -----------------------Details Model------------------------//
data class DetailsModel(
val props: Props,
val page: String,
val query: Query,
val buildId: String,
val isFallback: Boolean,
val gip: Boolean,
)
data class Props(
val pageProps: PageProps,
)
data class PageProps(
val deviceType: String,
val slug: String,
// val apolloClient: Any?,
val apolloState: HashMap<String, HashMap<String, JsonObject>>,
val ssrComplete: Boolean,
)
data class Query(
val slug: String,
)
// -----------------------Pagination Model------------------------//
@Serializable
@ -136,67 +101,16 @@ data class Item(
@SerialName("name_es")
val nameEs: String?,
val slug: String,
val cast: List<Cast> = emptyList(),
val names: String?,
val overview: String?,
val languages: List<String> = emptyList(),
@SerialName("created_by")
val createdBy: List<CreatedBy> = emptyList(),
val popularity: Double?,
@SerialName("poster_path")
val posterPath: String?,
@SerialName("backdrop_path")
val backdropPath: String?,
@SerialName("first_air_date")
val firstAirDate: String?,
@SerialName("isTVShow")
val isTvshow: Boolean?,
val poster: String?,
val backdrop: String?,
val genres: List<Genre> = emptyList(),
val networks: List<Network> = emptyList(),
@SerialName("__typename")
val typename: String,
)
@Serializable
data class Cast(
val adult: Boolean?,
val gender: Long?,
val id: Long?,
@SerialName("known_for_department")
val knownForDepartment: String?,
val name: String?,
@SerialName("original_name")
val originalName: String?,
val popularity: Double?,
@SerialName("profile_path")
val profilePath: String?,
val character: String?,
@SerialName("credit_id")
val creditId: String?,
val order: Long?,
)
@Serializable
data class CreatedBy(
val adult: Boolean?,
val gender: Long?,
val id: Long?,
@SerialName("known_for_department")
val knownForDepartment: String?,
val name: String?,
@SerialName("original_name")
val originalName: String?,
val popularity: Double?,
@SerialName("profile_path")
val profilePath: String?,
@SerialName("credit_id")
val creditId: String?,
val department: String?,
val job: String?,
)
@Serializable
data class Genre(
val name: String?,
@ -205,14 +119,6 @@ data class Genre(
val typename: String?,
)
@Serializable
data class Network(
val name: String?,
val slug: String?,
@SerialName("__typename")
val typename: String?,
)
// -----------------------Search Model------------------------//
@Serializable
data class SearchModel(
@ -241,18 +147,6 @@ data class SearchDorama(
)
// -------------------------------------------------------
@Serializable
data class VideoModel(
val json: JsonVideo = JsonVideo(),
)
@Serializable
data class JsonVideo(
val lang: String? = "",
val page: String? = "",
val link: String? = "",
val server: String? = "",
)
@Serializable
data class VideoToken(
@ -274,7 +168,6 @@ data class TokenModel(
val isFallback: Boolean? = false,
val isExperimentalCompile: Boolean? = false,
val gssp: Boolean? = false,
// val scriptLoader: List<Any?>,
)
@Serializable

View File

@ -1,7 +1,7 @@
ext {
extName = 'Jkanime'
extClass = '.Jkanime'
extVersionCode = 19
extVersionCode = 20
}
apply from: "$rootDir/common.gradle"
@ -10,4 +10,8 @@ dependencies {
implementation(project(':lib:okru-extractor'))
implementation(project(':lib:mixdrop-extractor'))
implementation(project(':lib:streamwish-extractor'))
implementation(project(':lib:mp4upload-extractor'))
implementation(project(':lib:filemoon-extractor'))
implementation(project(':lib:streamtape-extractor'))
implementation(project(':lib:voe-extractor'))
}

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.animeextension.es.jkanime
import android.app.Application
import android.content.SharedPreferences
import android.util.Base64
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.es.jkanime.extractors.JkanimeExtractor
@ -13,11 +14,18 @@ import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.mixdropextractor.MixDropExtractor
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
import eu.kanade.tachiyomi.lib.okruextractor.OkruExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.streamwishextractor.StreamWishExtractor
import eu.kanade.tachiyomi.lib.voeextractor.VoeExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.Serializable
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
@ -49,13 +57,15 @@ class Jkanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
private val QUALITY_LIST = arrayOf("1080", "720", "480", "360")
private const val PREF_SERVER_KEY = "preferred_server"
private const val PREF_SERVER_DEFAULT = "Nozomi"
private const val PREF_SERVER_DEFAULT = "Voe"
private val SERVER_LIST = arrayOf(
"Okru",
"Mixdrop",
"StreamWish",
"Xtreme S",
"HentaiJk",
"Filemoon",
"Mp4Upload",
"StreamTape",
"Desuka",
"Nozomi",
"Desu",
)
@ -114,33 +124,74 @@ class Jkanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override fun episodeFromElement(element: Element) = throw UnsupportedOperationException()
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
val videos = mutableListOf<Video>()
document.select("div.col-lg-12.rounded.bg-servers.text-white.p-3.mt-2 a").forEach { it ->
private val languages = arrayOf(
Pair("1", "[JAP]"),
Pair("3", "[LAT]"),
Pair("4", "[CHIN]"),
)
private fun String.getLang(): String {
return languages.firstOrNull { it.first == this }?.second ?: ""
}
private fun getVideoLinks(document: Document): List<Pair<String, String>> {
val servers = mutableListOf<Pair<String, String>>()
val scriptServers = document.selectFirst("script:containsData(var video = [];)")?.data() ?: return emptyList()
val jsServer = scriptServers.substringAfter("var remote = '").substringBefore("'")
val jsPath = scriptServers.substringAfter("= remote+'").substringBefore("'")
if (jsServer.isNotEmpty() && jsPath.isNotEmpty()) {
val jsLinks = client.newCall(GET(jsServer + jsPath)).execute().body.string()
.substringAfter("var servers = ").parseAs<Array<JsLinks>>().map {
Pair(String(Base64.decode(it.remote, Base64.DEFAULT)), "${it.lang}".getLang())
}
servers.addAll(jsLinks)
}
val htmlLinks = document.select("div.col-lg-12.rounded.bg-servers.text-white.p-3.mt-2 a").map {
val serverId = it.attr("data-id")
val langClass = it.attr("class")
val lang = if (langClass.contains("lg_3")) "[LAT]" else if (langClass.contains("lg_1")) "[JAP]" else ""
val scriptServers = document.selectFirst("script:containsData(var video = [];)")!!
val url = scriptServers.data().substringAfter("video[$serverId] = '<iframe class=\"player_conte\" src=\"")
val lang = it.attr("class").substringAfter("lg_").substringBefore(" ").getLang()
val url = scriptServers
.substringAfter("video[$serverId] = '<iframe class=\"player_conte\" src=\"")
.substringBefore("\"")
.replace("/jkokru.php?u=", "http://ok.ru/videoembed/")
.replace("/jkvmixdrop.php?u=", "https://mixdrop.ag/e/")
.replace("/jksw.php?u=", "https://sfastwish.com/e/")
.replace("/jk.php?u=", "$baseUrl/")
Pair(if (url.contains("um2.php") || url.contains("um.php")) baseUrl + url else url, lang)
}
servers.addAll(htmlLinks)
return servers
}
try {
/*--------------------------------Video extractors------------------------------------*/
private val okruExtractor by lazy { OkruExtractor(client) }
private val voeExtractor by lazy { VoeExtractor(client) }
private val filemoonExtractor by lazy { FilemoonExtractor(client) }
private val streamTapeExtractor by lazy { StreamTapeExtractor(client) }
private val mp4uploadExtractor by lazy { Mp4uploadExtractor(client) }
private val mixDropExtractor by lazy { MixDropExtractor(client) }
private val streamWishExtractor by lazy { StreamWishExtractor(client, headers) }
private val jkanimeExtractor by lazy { JkanimeExtractor(client) }
override fun videoListParse(response: Response): List<Video> {
val document = response.asJsoup()
return getVideoLinks(document).parallelCatchingFlatMapBlocking { (url, lang) ->
when {
"ok" in url -> OkruExtractor(client).videosFromUrl(url, "$lang ").forEach { videos.add(it) }
"mixdrop" in url -> MixDropExtractor(client).videosFromUrl(url, prefix = "$lang ").forEach { videos.add(it) }
"sfastwish" in url -> StreamWishExtractor(client, headers).videosFromUrl(url, prefix = "$lang StreamWish").forEach { videos.add(it) }
"stream/jkmedia" in url -> videos.add(Video(url, "$lang Xtreme S", url))
"um2.php" in url -> JkanimeExtractor(client).getNozomiFromUrl(baseUrl + url, "$lang ").let { if (it != null) videos.add(it) }
"um.php" in url -> JkanimeExtractor(client).getDesuFromUrl(baseUrl + url, "$lang ").let { if (it != null) videos.add(it) }
"ok" in url -> okruExtractor.videosFromUrl(url, "$lang ")
"voe" in url -> voeExtractor.videosFromUrl(url, "$lang ")
"filemoon" in url || "moonplayer" in url -> filemoonExtractor.videosFromUrl(url, "$lang Filemoon:")
"streamtape" in url || "stp" in url || "stape" in url -> listOf(streamTapeExtractor.videoFromUrl(url, quality = "$lang StreamTape")!!)
"mp4upload" in url -> mp4uploadExtractor.videosFromUrl(url, prefix = "$lang ", headers = headers)
"mixdrop" in url || "mdbekjwqa" in url -> mixDropExtractor.videosFromUrl(url, prefix = "$lang ")
"sfastwish" in url || "wishembed" in url || "streamwish" in url || "strwish" in url || "wish" in url
-> streamWishExtractor.videosFromUrl(url, videoNameGen = { "$lang StreamWish:$it" })
"stream/jkmedia" in url -> jkanimeExtractor.getDesukaFromUrl(url, "$lang ")
"um2.php" in url -> jkanimeExtractor.getNozomiFromUrl(url, "$lang ")
"um.php" in url -> jkanimeExtractor.getDesuFromUrl(url, "$lang ")
else -> emptyList()
}
} catch (_: Exception) {}
}
return videos
}
override fun videoListSelector() = throw UnsupportedOperationException()
@ -462,4 +513,12 @@ class Jkanime : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
}
}.also(screen::addPreference)
}
@Serializable
data class JsLinks(
val remote: String? = null,
val server: String? = null,
val lang: Long? = null,
val slug: String? = null,
)
}

View File

@ -4,17 +4,18 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.Serializable
import okhttp3.Headers
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
class JkanimeExtractor(
private val client: OkHttpClient,
) {
fun getNozomiFromUrl(url: String, prefix: String = ""): Video? {
fun getNozomiFromUrl(url: String, prefix: String = ""): List<Video> {
val dataKeyHeaders = Headers.Builder().add("Referer", url).build()
val doc = client.newCall(GET(url, dataKeyHeaders)).execute().asJsoup()
val dataKey = doc.select("form input[value]").attr("value")
@ -26,20 +27,40 @@ class JkanimeExtractor(
val nozomiBody = "v=$postKey".toRequestBody("application/x-www-form-urlencoded".toMediaTypeOrNull())
val nozomiResponse = client.newCall(POST("https://jkanime.net/gsplay/api.php", body = nozomiBody)).execute()
val nozomiUrl = JSONObject(nozomiResponse.body.string()).getString("file")
if (nozomiResponse.isSuccessful && nozomiUrl.isNotBlank()) {
return Video(nozomiUrl, "${prefix}Nozomi", nozomiUrl)
}
return null
val nozomiUrl = nozomiResponse.body.string().parseAs<NozomiResponse>().file ?: return emptyList()
return listOf(Video(nozomiUrl, "${prefix}Nozomi", nozomiUrl))
}
fun getDesuFromUrl(url: String, prefix: String = ""): Video? {
fun getDesuFromUrl(url: String, prefix: String = ""): List<Video> {
val document = client.newCall(GET(url)).execute()
val script = document.asJsoup().selectFirst("script:containsData(var parts = {)")!!.data()
val streamUrl = script.substringAfter("url: '").substringBefore("'")
if (document.isSuccessful && streamUrl.isNotBlank()) {
return Video(streamUrl, "${prefix}Desu", streamUrl)
val streamUrl = document.asJsoup()
.selectFirst("script:containsData(var parts = {)")
?.data()?.substringAfter("url: '")
?.substringBefore("'") ?: return emptyList()
return listOf(Video(streamUrl, "${prefix}Desu", streamUrl))
}
return null
fun getDesukaFromUrl(url: String, prefix: String = ""): List<Video> {
val document = client.newCall(GET(url)).execute()
val contentType = document.header("Content-Type") ?: ""
if (contentType.startsWith("video/")) {
val realUrl = document.networkResponse.toString()
.substringAfter("url=")
.substringBefore("}")
return listOf(Video(realUrl, "${prefix}Desuka", realUrl))
}
val streamUrl = document.asJsoup()
.selectFirst("script:containsData(new DPlayer({)")
?.data()?.substringAfter("url: '")
?.substringBefore("'") ?: return emptyList()
return listOf(Video(streamUrl, "${prefix}Desuka", streamUrl))
}
@Serializable
data class NozomiResponse(val file: String? = null)
}