KickAssAnime: fix pink bird (#1249)

This commit is contained in:
Samfun75
2023-02-07 04:51:09 +03:00
committed by GitHub
parent 491199b1d4
commit f8b7e8b6ba
4 changed files with 75 additions and 134 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'KickAssAnime'
pkgNameSuffix = 'en.kickassanime'
extClass = '.KickAssAnime'
extVersionCode = 14
extVersionCode = 15
libVersion = '13'
}

View File

@ -7,6 +7,7 @@ import android.util.Base64
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.animeextension.en.kickassanime.extractors.GogoCdnExtractor
import eu.kanade.tachiyomi.animeextension.en.kickassanime.extractors.PinkBird
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.animesource.model.AnimesPage
@ -170,38 +171,29 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
videoList.addAll(
sources.parallelMap { source ->
runCatching {
when (source.jsonObject["name"]!!.jsonPrimitive.content) {
val src = source.jsonObject["src"]!!.jsonPrimitive.content
val name = source.jsonObject["name"]!!.jsonPrimitive.content
when (name) {
in deadServers -> { null }
"SAPPHIRE-DUCK" -> {
extractSapphireVideo(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
extractSapphireVideo(src, name)
}
"PINK-BIRD" -> {
PinkBird(client, json).videosFromUrl(src, name)
}
"BETAPLAYER" -> {
extractBetaVideo(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
extractBetaVideo(src, name)
}
"KICKASSANIMEV2", "ORIGINAL-QUALITY-V2", "BETA-SERVER" -> {
extractKickasssVideo(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
extractKickasssVideo(src, name)
}
"DAILYMOTION" -> {
extractDailymotion(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
extractDailymotion(src, name)
}
else -> {
extractVideo(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
"MAVERICKKI" -> {
extractMavrick(src, name)
}
else -> null
}
}.getOrNull()
}.filterNotNull().flatten()
@ -211,31 +203,23 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
return videoList
}
private fun extractVideo(serverLink: String, server: String): List<Video> {
private fun extractMavrick(serverLink: String, server: String): List<Video> {
val playlist = mutableListOf<Video>()
val subsList = mutableListOf<Track>()
var vidHeader = headers
val apiLink = serverLink.replace("embed", "api/source")
val embedHeader = Headers.headersOf("referer", serverLink)
val apiResponse = client.newCall(GET(apiLink, embedHeader)).execute()
val json = Json.decodeFromString<JsonObject>(apiResponse.body!!.string())
val uri = Uri.parse(serverLink)
val resp = if (server == "MAVERICKKI") {
val apiLink = serverLink.replace("embed", "api/source")
val embedHeader = Headers.headersOf("referer", serverLink)
val apiResponse = client.newCall(GET(apiLink, embedHeader)).execute()
val json = Json.decodeFromString<JsonObject>(apiResponse.body!!.string())
val uri = Uri.parse(serverLink)
json["subtitles"]!!.jsonArray.forEach {
val subLang = it.jsonObject["name"]!!.jsonPrimitive.content
val subUrl = "${uri.scheme}://${uri.host}" + it.jsonObject["src"]!!.jsonPrimitive.content
try {
subsList.add(Track(subUrl, subLang))
} catch (_: Error) {}
}
vidHeader = embedHeader
client.newCall(GET("${uri.scheme}://${uri.host}" + json["hls"]!!.jsonPrimitive.content, embedHeader)).execute()
} else {
val kickAssClient = client.newBuilder().addInterceptor(MasterPlaylistInterceptor()).build()
kickAssClient.newCall(GET(serverLink, headers)).execute()
json["subtitles"]!!.jsonArray.forEach {
val subLang = it.jsonObject["name"]!!.jsonPrimitive.content
val subUrl = "${uri.scheme}://${uri.host}" + it.jsonObject["src"]!!.jsonPrimitive.content
try {
subsList.add(Track(subUrl, subLang))
} catch (_: Error) {}
}
val resp = client.newCall(GET("${uri.scheme}://${uri.host}" + json["hls"]!!.jsonPrimitive.content, embedHeader)).execute()
resp.body!!.string().substringAfter("#EXT-X-STREAM-INF:")
.split("#EXT-X-STREAM-INF:").map {
@ -246,12 +230,11 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
videoUrl = resp.request.url.toString().substringBeforeLast("/") + "/$videoUrl"
}
try {
playlist.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subsList, headers = vidHeader))
playlist.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subsList, headers = embedHeader))
} catch (e: Error) {
playlist.add(Video(videoUrl, quality, videoUrl, headers = vidHeader))
playlist.add(Video(videoUrl, quality, videoUrl, headers = embedHeader))
}
}
return playlist
}

View File

@ -1,87 +0,0 @@
package eu.kanade.tachiyomi.animeextension.en.kickassanime
import android.annotation.SuppressLint
import android.app.Application
import android.os.Handler
import android.os.Looper
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers.Companion.toHeaders
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
class MasterPlaylistInterceptor : Interceptor {
private val context = Injekt.get<Application>()
private val handler by lazy { Handler(Looper.getMainLooper()) }
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val newRequest = resolveWithWebView(originalRequest) ?: throw Exception("Could not find playlist")
return chain.proceed(newRequest)
}
@SuppressLint("SetJavaScriptEnabled")
private fun resolveWithWebView(request: Request): Request? {
// We need to lock this thread until the WebView finds the challenge solution url, because
// OkHttp doesn't support asynchronous interceptors.
val latch = CountDownLatch(1)
var webView: WebView? = null
val origRequestUrl = request.url.toString()
val headers = request.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }.toMutableMap()
var newRequest: Request? = null
handler.post {
val webview = WebView(context)
webView = webview
with(webview.settings) {
javaScriptEnabled = true
domStorageEnabled = true
databaseEnabled = true
useWideViewPort = false
loadWithOverviewMode = false
userAgentString = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0"
}
webview.webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest,
): WebResourceResponse? {
if (request.url.toString().contains(".m3u8")) {
newRequest = GET(request.url.toString(), request.requestHeaders.toHeaders())
latch.countDown()
}
return super.shouldInterceptRequest(view, request)
}
}
webView?.loadUrl(origRequestUrl, headers)
}
// Wait a reasonable amount of time to retrieve the solution. The minimum should be
// around 4 seconds but it can take more due to slow networks or server issues.
latch.await(9, TimeUnit.SECONDS)
handler.post {
webView?.stopLoading()
webView?.destroy()
webView = null
}
return newRequest
}
}

View File

@ -0,0 +1,45 @@
package eu.kanade.tachiyomi.animeextension.en.kickassanime.extractors
import android.util.Base64
import eu.kanade.tachiyomi.animesource.model.Video
import eu.kanade.tachiyomi.network.GET
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.OkHttpClient
import java.lang.Exception
@ExperimentalSerializationApi
class PinkBird(private val client: OkHttpClient, private val json: Json) {
fun videosFromUrl(serverUrl: String, server: String): List<Video> {
return try {
val apiLink = serverUrl.replace("player.php", "pref.php")
val resp = client.newCall(GET(apiLink)).execute()
val jsonResp = json.decodeFromString<JsonObject>(resp.body!!.string())
jsonResp["data"]!!.jsonArray.map { el ->
val eid = el.jsonObject["eid"]!!.jsonPrimitive.content.decodeBase64()
val response = client.newCall(GET("https://pb.kaast1.com/manifest/$eid/master.m3u8")).execute()
if (response.code != 200) return emptyList()
response.body!!.string().substringAfter("#EXT-X-STREAM-INF:")
.split("#EXT-X-STREAM-INF:").map {
val quality = it.substringAfter("RESOLUTION=").split(",")[0].split("\n")[0].substringAfter("x") + "p $server"
var videoUrl = it.substringAfter("\n").substringBefore("\n")
if (videoUrl.startsWith("https").not()) {
videoUrl = "https://${response.request.url.host}$videoUrl"
}
Video(videoUrl, quality, videoUrl)
}
}.flatten()
} catch (e: Exception) {
emptyList()
}
}
private fun String.decodeBase64(): String {
return Base64.decode(this, Base64.DEFAULT).toString(Charsets.UTF_8)
}
}