fix(en/seez): Fix Seez and move vidsrc to shared lib (#2800)

This commit is contained in:
Samfun75 2024-01-20 20:29:15 +03:00 committed by GitHub
parent 255bf2d6a6
commit cd5ab35f9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 84 additions and 339 deletions

View File

@ -0,0 +1,20 @@
plugins {
id("com.android.library")
kotlin("android")
id("kotlinx-serialization")
}
android {
compileSdk = AndroidConfig.compileSdk
namespace = "eu.kanade.tachiyomi.lib.vidsrcextractor"
defaultConfig {
minSdk = AndroidConfig.minSdk
}
}
dependencies {
compileOnly(libs.bundles.common)
implementation(project(":lib-playlist-utils"))
}
// BUMPS: 0

View File

@ -1,13 +1,14 @@
package eu.kanade.tachiyomi.animeextension.en.fmovies.extractors package eu.kanade.tachiyomi.lib.vidsrcextractor
import android.util.Base64 import android.util.Base64
import app.cash.quickjs.QuickJs import app.cash.quickjs.QuickJs
import eu.kanade.tachiyomi.animeextension.en.fmovies.MediaResponseBody
import eu.kanade.tachiyomi.animesource.model.Track import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.parseAs import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import okhttp3.CacheControl import okhttp3.CacheControl
import okhttp3.Headers import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
@ -16,6 +17,7 @@ import java.net.URLDecoder
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
@OptIn(ExperimentalSerializationApi::class)
class VidsrcExtractor(private val client: OkHttpClient, private val headers: Headers) { class VidsrcExtractor(private val client: OkHttpClient, private val headers: Headers) {
private val playlistUtils by lazy { PlaylistUtils(client, headers) } private val playlistUtils by lazy { PlaylistUtils(client, headers) }
@ -25,13 +27,14 @@ class VidsrcExtractor(private val client: OkHttpClient, private val headers: Hea
.cache(null) .cache(null)
.build() .build()
private val keys by lazy { private val keys by lazy {
noCacheClient.newCall( noCacheClient.newCall(
GET("https://raw.githubusercontent.com/KillerDogeEmpire/vidplay-keys/keys/keys.json", cache = cacheControl), GET("https://raw.githubusercontent.com/KillerDogeEmpire/vidplay-keys/keys/keys.json", cache = cacheControl),
).execute().parseAs<List<String>>() ).execute().parseAs<List<String>>()
} }
fun videosFromUrl(embedLink: String, hosterName: String): List<Video> { fun videosFromUrl(embedLink: String, hosterName: String, type: String = "" ): List<Video> {
val host = embedLink.toHttpUrl().host val host = embedLink.toHttpUrl().host
val apiUrl = getApiUrl(embedLink, keys) val apiUrl = getApiUrl(embedLink, keys)
@ -61,7 +64,7 @@ class VidsrcExtractor(private val client: OkHttpClient, private val headers: Hea
return playlistUtils.extractFromHls( return playlistUtils.extractFromHls(
data.result.sources.first().file, data.result.sources.first().file,
referer = "https://$host/", referer = "https://$host/",
videoNameGen = { q -> "$hosterName - $q" }, videoNameGen = { q -> hosterName + (if(type.isBlank()) "" else " - $type") + " - $q" },
subtitleList = data.result.tracks.toTracks(), subtitleList = data.result.tracks.toTracks(),
) )
} }
@ -142,3 +145,27 @@ class VidsrcExtractor(private val client: OkHttpClient, private val headers: Hea
} }
} }
} }
@Serializable
data class MediaResponseBody(
val status: Int,
val result: Result,
) {
@Serializable
data class Result(
val sources: ArrayList<Source>,
val tracks: ArrayList<SubTrack> = ArrayList(),
) {
@Serializable
data class Source(
val file: String,
)
@Serializable
data class SubTrack(
val file: String,
val label: String = "",
val kind: String,
)
}
}

View File

@ -7,7 +7,7 @@ ext {
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"
dependencies { dependencies {
implementation(project(':lib-vidsrc-extractor'))
implementation(project(':lib-filemoon-extractor')) implementation(project(':lib-filemoon-extractor'))
implementation(project(':lib-streamtape-extractor')) implementation(project(':lib-streamtape-extractor'))
implementation(project(':lib-playlist-utils'))
} }

View File

@ -5,7 +5,6 @@ import android.content.SharedPreferences
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.en.fmovies.extractors.VidsrcExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.animesource.model.SAnime
@ -15,6 +14,7 @@ import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.vidsrcextractor.VidsrcExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.awaitSuccess import eu.kanade.tachiyomi.network.awaitSuccess

View File

@ -7,8 +7,8 @@ ext {
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"
dependencies { dependencies {
implementation(project(':lib-vidsrc-extractor'))
implementation(project(':lib-filemoon-extractor')) implementation(project(':lib-filemoon-extractor'))
implementation(project(':lib-mp4upload-extractor')) implementation(project(':lib-mp4upload-extractor'))
implementation(project(':lib-streamtape-extractor')) implementation(project(':lib-streamtape-extractor'))
implementation(project(':lib-playlist-utils'))
} }

View File

@ -7,7 +7,6 @@ import androidx.preference.ListPreference
import androidx.preference.MultiSelectListPreference import androidx.preference.MultiSelectListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
import eu.kanade.tachiyomi.animeextension.en.nineanime.extractors.VidsrcExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.SAnime import eu.kanade.tachiyomi.animesource.model.SAnime
@ -17,19 +16,18 @@ import eu.kanade.tachiyomi.animesource.online.ParsedAnimeHttpSource
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor import eu.kanade.tachiyomi.lib.mp4uploadextractor.Mp4uploadExtractor
import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor import eu.kanade.tachiyomi.lib.streamtapeextractor.StreamTapeExtractor
import eu.kanade.tachiyomi.lib.vidsrcextractor.VidsrcExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelFlatMapBlocking import eu.kanade.tachiyomi.util.parallelFlatMapBlocking
import eu.kanade.tachiyomi.util.parallelMapBlocking import eu.kanade.tachiyomi.util.parallelMapBlocking
import eu.kanade.tachiyomi.util.parseAs import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.json.Json
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -47,8 +45,6 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
override val supportsLatest = true override val supportsLatest = true
private val json: Json by injectLazy()
private val utils by lazy { AniwaveUtils() } private val utils by lazy { AniwaveUtils() }
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
@ -269,7 +265,13 @@ class Aniwave : ConfigurableAnimeSource, ParsedAnimeHttpSource() {
val parsed = response.parseAs<ServerResponse>() val parsed = response.parseAs<ServerResponse>()
val embedLink = utils.vrfDecrypt(parsed.result.url) val embedLink = utils.vrfDecrypt(parsed.result.url)
when (server.serverName) { when (server.serverName) {
"vidplay", "mycloud" -> vidsrcExtractor.videosFromUrl(embedLink, server.serverName, server.type) "vidplay", "mycloud" -> {
val hosterName = when (server.serverName) {
"vidplay" -> "VidPlay"
else -> "MyCloud"
}
vidsrcExtractor.videosFromUrl(embedLink, hosterName, server.type)
}
"filemoon" -> filemoonExtractor.videosFromUrl(embedLink, "Filemoon - ${server.type} - ") "filemoon" -> filemoonExtractor.videosFromUrl(embedLink, "Filemoon - ${server.type} - ")
"streamtape" -> streamtapeExtractor.videoFromUrl(embedLink, "StreamTape - ${server.type}")?.let(::listOf) ?: emptyList() "streamtape" -> streamtapeExtractor.videoFromUrl(embedLink, "StreamTape - ${server.type}")?.let(::listOf) ?: emptyList()
"mp4upload" -> mp4uploadExtractor.videosFromUrl(embedLink, headers, suffix = " - ${server.type}") "mp4upload" -> mp4uploadExtractor.videosFromUrl(embedLink, headers, suffix = " - ${server.type}")

View File

@ -1,152 +0,0 @@
package eu.kanade.tachiyomi.animeextension.en.nineanime.extractors
import android.util.Base64
import app.cash.quickjs.QuickJs
import eu.kanade.tachiyomi.animeextension.en.nineanime.MediaResponseBody
import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.json.Json
import okhttp3.CacheControl
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import uy.kohesive.injekt.injectLazy
import java.net.URLDecoder
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
class VidsrcExtractor(private val client: OkHttpClient, private val headers: Headers) {
private val json: Json by injectLazy()
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
private val cacheControl = CacheControl.Builder().noStore().build()
private val noCacheClient = client.newBuilder()
.cache(null)
.build()
private val keys by lazy {
noCacheClient.newCall(
GET("https://raw.githubusercontent.com/KillerDogeEmpire/vidplay-keys/keys/keys.json", cache = cacheControl),
).execute().parseAs<List<String>>()
}
fun videosFromUrl(embedLink: String, name: String, type: String): List<Video> {
val hosterName = when (name) {
"vidplay" -> "VidPlay"
else -> "MyCloud"
}
val host = embedLink.toHttpUrl().host
val apiUrl = getApiUrl(embedLink, keys)
val apiHeaders = headers.newBuilder().apply {
add("Accept", "application/json, text/javascript, */*; q=0.01")
add("Host", host)
add("Referer", URLDecoder.decode(embedLink, "UTF-8"))
add("X-Requested-With", "XMLHttpRequest")
}.build()
val response = client.newCall(
GET(apiUrl, apiHeaders),
).execute()
val data = runCatching {
response.parseAs<MediaResponseBody>()
}.getOrElse { // Keys are out of date
val newKeys = noCacheClient.newCall(
GET("https://raw.githubusercontent.com/KillerDogeEmpire/vidplay-keys/keys/keys.json", cache = cacheControl),
).execute().parseAs<List<String>>()
val newApiUrL = getApiUrl(embedLink, newKeys)
client.newCall(
GET(newApiUrL, apiHeaders),
).execute().parseAs()
}
return playlistUtils.extractFromHls(
data.result.sources.first().file,
referer = "https://$host/",
videoNameGen = { q -> "$hosterName - $type - $q" },
subtitleList = data.result.tracks.toTracks(),
)
}
private fun getApiUrl(embedLink: String, keyList: List<String>): String {
val host = embedLink.toHttpUrl().host
val params = embedLink.toHttpUrl().let { url ->
url.queryParameterNames.map {
Pair(it, url.queryParameter(it) ?: "")
}
}
val vidId = embedLink.substringAfterLast("/").substringBefore("?")
val encodedID = encodeID(vidId, keyList)
val apiSlug = callFromFuToken(host, encodedID)
return buildString {
append("https://")
append(host)
append("/")
append(apiSlug)
if (params.isNotEmpty()) {
append("?")
append(
params.joinToString("&") {
"${it.first}=${it.second}"
},
)
}
}
}
private fun encodeID(videoID: String, keyList: List<String>): String {
val rc4Key1 = SecretKeySpec(keyList[0].toByteArray(), "RC4")
val rc4Key2 = SecretKeySpec(keyList[1].toByteArray(), "RC4")
val cipher1 = Cipher.getInstance("RC4")
val cipher2 = Cipher.getInstance("RC4")
cipher1.init(Cipher.DECRYPT_MODE, rc4Key1, cipher1.parameters)
cipher2.init(Cipher.DECRYPT_MODE, rc4Key2, cipher2.parameters)
var encoded = videoID.toByteArray()
encoded = cipher1.doFinal(encoded)
encoded = cipher2.doFinal(encoded)
encoded = Base64.encode(encoded, Base64.DEFAULT)
return encoded.toString(Charsets.UTF_8).replace("/", "_").trim()
}
private fun callFromFuToken(host: String, data: String): String {
val fuTokenScript = client.newCall(
GET("https://$host/futoken"),
).execute().use { it.body.string() }
val js = buildString {
append("(function")
append(
fuTokenScript.substringAfter("window")
.substringAfter("function")
.replace("jQuery.ajax(", "")
.substringBefore("+location.search"),
)
append("}(\"$data\"))")
}
return QuickJs.create().use {
it.evaluate(js)?.toString()!!
}
}
private fun List<MediaResponseBody.Result.SubTrack>.toTracks(): List<Track> {
return filter {
it.kind == "captions"
}.mapNotNull {
runCatching {
Track(
it.file,
it.label,
)
}.getOrNull()
}
}
}

View File

@ -7,7 +7,6 @@ ext {
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"
dependencies { dependencies {
implementation(project(':lib-vidsrc-extractor'))
implementation(project(':lib-filemoon-extractor')) implementation(project(':lib-filemoon-extractor'))
implementation(project(':lib-streamtape-extractor'))
implementation(project(':lib-playlist-utils'))
} }

View File

@ -2,9 +2,9 @@ package eu.kanade.tachiyomi.animeextension.en.seez
import android.app.Application import android.app.Application
import android.content.SharedPreferences import android.content.SharedPreferences
import android.util.Base64
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.en.seez.extractors.VidsrcExtractor
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilter import eu.kanade.tachiyomi.animesource.model.AnimeFilter
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
@ -14,6 +14,7 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.model.Video import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor import eu.kanade.tachiyomi.lib.filemoonextractor.FilemoonExtractor
import eu.kanade.tachiyomi.lib.vidsrcextractor.VidsrcExtractor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking import eu.kanade.tachiyomi.util.parallelCatchingFlatMapBlocking
@ -27,8 +28,11 @@ import okhttp3.Response
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.net.URLDecoder
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import javax.crypto.Cipher
import javax.crypto.spec.SecretKeySpec
class Seez : ConfigurableAnimeSource, AnimeHttpSource() { class Seez : ConfigurableAnimeSource, AnimeHttpSource() {
@ -48,8 +52,6 @@ class Seez : ConfigurableAnimeSource, AnimeHttpSource() {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
private val vrfHelper by lazy { VrfHelper(client, headers) }
private val apiKey by lazy { private val apiKey by lazy {
val jsUrl = client.newCall(GET(baseUrl, headers)).execute().asJsoup() val jsUrl = client.newCall(GET(baseUrl, headers)).execute().asJsoup()
.select("script[defer][src]")[1].attr("abs:src") .select("script[defer][src]")[1].attr("abs:src")
@ -301,14 +303,13 @@ class Seez : ConfigurableAnimeSource, AnimeHttpSource() {
GET("$embedUrl/ajax/embed/source/${it.id}", headers = sourcesHeaders), GET("$embedUrl/ajax/embed/source/${it.id}", headers = sourcesHeaders),
).execute().parseAs<EmbedUrlResponse>().result.url ).execute().parseAs<EmbedUrlResponse>().result.url
Pair(vrfHelper.decrypt(encrypted), it.title) Pair(decrypt(encrypted), it.title)
} }
return urlList.parallelCatchingFlatMapBlocking { return urlList.parallelCatchingFlatMapBlocking {
val url = it.first val url = it.first
val name = it.second
when (name) { when (val name = it.second) {
"Vidplay" -> vidsrcExtractor.videosFromUrl(url, name) "Vidplay" -> vidsrcExtractor.videosFromUrl(url, name)
"Filemoon" -> filemoonExtractor.videosFromUrl(url) "Filemoon" -> filemoonExtractor.videosFromUrl(url)
else -> emptyList() else -> emptyList()
@ -344,10 +345,22 @@ class Seez : ConfigurableAnimeSource, AnimeHttpSource() {
.getOrNull() ?: 0L .getOrNull() ?: 0L
} }
private fun decrypt(encrypted: String): String {
var vrf = encrypted.toByteArray()
vrf = Base64.decode(vrf, Base64.URL_SAFE)
val rc4Key = SecretKeySpec("8z5Ag5wgagfsOuhz".toByteArray(), "RC4")
val cipher = Cipher.getInstance("RC4")
cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters)
vrf = cipher.doFinal(vrf)
return URLDecoder.decode(vrf.toString(Charsets.UTF_8), "utf-8")
}
companion object { companion object {
private val TMDB_URL = "https://api.themoviedb.org/3".toHttpUrl() private val TMDB_URL = "https://api.themoviedb.org/3".toHttpUrl()
private val IMG_URL = "https://image.tmdb.org/t/p/w300/" private const val IMG_URL = "https://image.tmdb.org/t/p/w300/"
private val FALLBACK_IMG = "https://seez.su/fallback.png" private const val FALLBACK_IMG = "https://seez.su/fallback.png"
private val DATE_FORMATTER by lazy { private val DATE_FORMATTER by lazy {
SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)

View File

@ -1,63 +0,0 @@
package eu.kanade.tachiyomi.animeextension.en.seez
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import uy.kohesive.injekt.injectLazy
class VrfHelper(private val client: OkHttpClient, private val headers: Headers) {
private val json: Json by injectLazy()
fun decrypt(encrypted: String): String {
val url = API_URL.newBuilder().apply {
addPathSegment("fmovies-decrypt")
addQueryParameter("query", encrypted)
addQueryParameter("apikey", API_KEY)
}.build().toString()
return client.newCall(GET(url)).execute().parseAs<VrfResponse>().url
}
fun getVidSrc(query: String, host: String): String {
val url = API_URL.newBuilder().apply {
addPathSegment(if (host.contains("mcloud", true)) "rawMcloud" else "rawVizcloud")
addQueryParameter("apikey", API_KEY)
}.build().toString()
val futoken = client.newCall(
GET("https://$host/futoken", headers),
).execute().use { it.body.string() }
val body = FormBody.Builder().apply {
add("query", query)
add("futoken", futoken)
}.build()
return client.newCall(
POST(url, body = body),
).execute().parseAs<RawResponse>().rawURL
}
companion object {
const val API_KEY = "aniyomi"
val API_URL = "https://9anime.eltik.net".toHttpUrl()
}
@Serializable
data class VrfResponse(
val url: String,
val vrfQuery: String? = null,
)
@Serializable
data class RawResponse(
val rawURL: String,
)
}

View File

@ -1,101 +0,0 @@
package eu.kanade.tachiyomi.animeextension.en.seez.extractors
import eu.kanade.tachiyomi.animeextension.en.seez.VrfHelper
import eu.kanade.tachiyomi.animesource.model.Track
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.lib.playlistutils.PlaylistUtils
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import uy.kohesive.injekt.injectLazy
// Stolen from fmovies
class VidsrcExtractor(private val client: OkHttpClient, private val headers: Headers) {
private val json: Json by injectLazy()
private val vrfHelper by lazy { VrfHelper(client, headers) }
private val playlistUtils by lazy { PlaylistUtils(client, headers) }
fun videosFromUrl(url: String, name: String): List<Video> {
val httpUrl = url.toHttpUrl()
val host = httpUrl.host
val referer = "https://$host/"
val query = buildString {
append(httpUrl.pathSegments.last())
append("?")
append(
httpUrl.queryParameterNames.joinToString("&") {
"$it=${httpUrl.queryParameter(it)}"
},
)
}
val rawUrl = vrfHelper.getVidSrc(query, host).addAutoStart()
val refererHeaders = headers.newBuilder().apply {
add("Accept", "application/json, text/javascript, */*; q=0.01")
add("Host", host)
add("Referer", url.addAutoStart())
add("X-Requested-With", "XMLHttpRequest")
}.build()
val infoJson = client.newCall(
GET(rawUrl, headers = refererHeaders),
).execute().parseAs<VidsrcResponse>()
val subtitleList = httpUrl.queryParameter("sub.info")?.let {
val subtitlesHeaders = headers.newBuilder().apply {
add("Accept", "application/json, text/javascript, */*; q=0.01")
add("Host", it.toHttpUrl().host)
add("Origin", "https://$host")
add("Referer", referer)
}.build()
client.newCall(
GET(it, headers = subtitlesHeaders),
).execute().parseAs<List<FMoviesSubs>>().map {
Track(it.file, it.label)
}
} ?: emptyList()
return infoJson.result.sources.distinctBy { it.file }.flatMap {
val url = it.file
.toHttpUrl()
.newBuilder()
.fragment(null)
.build()
.toString()
playlistUtils.extractFromHls(url, subtitleList = subtitleList, referer = referer, videoNameGen = { q -> "$name - $q" })
}
}
private fun String.addAutoStart(): String {
return this.toHttpUrl().newBuilder().setQueryParameter("autostart", "true").build().toString()
}
@Serializable
data class VidsrcResponse(
val result: ResultObject,
) {
@Serializable
data class ResultObject(
val sources: List<SourceObject>,
) {
@Serializable
data class SourceObject(
val file: String,
)
}
}
@Serializable
data class FMoviesSubs(
val file: String,
val label: String,
)
}