fix(src/es): Fix video extraction and fix json parsing (#3140)
This commit is contained in:
parent
0c552ed88e
commit
36b7d06ec7
@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'Doramasflix'
|
||||
extClass = '.Doramasflix'
|
||||
extVersionCode = 18
|
||||
extVersionCode = 19
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -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
|
||||
|
@ -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'))
|
||||
}
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user