KickAssAnime: Fix Sapphire, use reanimation jutsu on some dead server… (#1194)

This commit is contained in:
Samfun75
2023-01-20 19:50:55 +03:00
committed by GitHub
parent 72f191c705
commit ea8ae3909e
3 changed files with 279 additions and 139 deletions

View File

@ -1,15 +1,17 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'KickAssAnime'
pkgNameSuffix = 'en.kickassanime'
extClass = '.KickAssAnime'
extVersionCode = 13
extVersionCode = 14
libVersion = '13'
}
dependencies {
compileOnly libs.bundles.coroutines
implementation(project(':lib-streamsb-extractor'))
implementation(project(':lib-dood-extractor'))
}

View File

@ -19,7 +19,13 @@ import eu.kanade.tachiyomi.lib.doodextractor.DoodExtractor
import eu.kanade.tachiyomi.lib.streamsbextractor.StreamSBExtractor
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
@ -29,6 +35,7 @@ import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -45,7 +52,12 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
override val name = "KickAssAnime"
override val baseUrl by lazy { preferences.getString("preferred_domain", "https://www2.kickassanime.ro")!! }
override val baseUrl by lazy {
preferences.getString(
"preferred_domain",
"https://www2.kickassanime.ro"
)!!
}
override val lang = "en"
@ -67,24 +79,30 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
// Add non working server names here
private val deadServers = listOf(
"BETA-SERVER", "BETASERVER1", "BETASERVER3", "DEVSTREAM",
"THETA-ORIGINAL-V4", "DAILYMOTION", "KICKASSANIME1"
"BETASERVER1", "BETASERVER3", "DEVSTREAM",
"THETA-ORIGINAL-V4", "KICKASSANIME1"
)
private val workingServers = arrayOf(
"StreamSB", "PINK-BIRD", "Doodstream", "MAVERICKKI",
"StreamSB", "PINK-BIRD", "Doodstream", "MAVERICKKI", "BETA-SERVER", "DAILYMOTION",
"BETAPLAYER", "Vidstreaming", "SAPPHIRE-DUCK", "KICKASSANIMEV2", "ORIGINAL-QUALITY-V2"
)
override fun popularAnimeRequest(page: Int): Request = GET("$baseUrl/api/get_anime_list/all/$page")
override fun popularAnimeRequest(page: Int): Request =
GET("$baseUrl/api/get_anime_list/all/$page")
override fun popularAnimeParse(response: Response): AnimesPage {
val responseObject = json.decodeFromString<JsonObject>(response.body!!.string())
val data = responseObject["data"]!!.jsonArray
val animes = data.map { item ->
SAnime.create().apply {
setUrlWithoutDomain(item.jsonObject["slug"]!!.jsonPrimitive.content.substringBefore("/episode"))
thumbnail_url = "$baseUrl/uploads/" + item.jsonObject["poster"]!!.jsonPrimitive.content
setUrlWithoutDomain(
item.jsonObject["slug"]!!.jsonPrimitive.content.substringBefore(
"/episode"
)
)
thumbnail_url =
"$baseUrl/uploads/" + item.jsonObject["poster"]!!.jsonPrimitive.content
title = item.jsonObject["name"]!!.jsonPrimitive.content
}
}
@ -149,35 +167,45 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
val resp = client.newCall(GET(link)).execute()
val sources = getVideoSource(resp.asJsoup())
sources.forEach { source ->
when (source.jsonObject["name"]!!.jsonPrimitive.content) {
in deadServers -> {}
"BETAPLAYER" -> {
videoList.addAll(
extractBetaVideo(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
)
}
"KICKASSANIMEV2", "ORIGINAL-QUALITY-V2" -> {
videoList.addAll(
extractKickasssVideo(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
)
}
else -> {
videoList.addAll(
extractVideo(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
)
}
}
}
videoList.addAll(
sources.parallelMap { source ->
runCatching {
when (source.jsonObject["name"]!!.jsonPrimitive.content) {
in deadServers -> { null }
"SAPPHIRE-DUCK" -> {
extractSapphireVideo(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
}
"BETAPLAYER" -> {
extractBetaVideo(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
}
"KICKASSANIMEV2", "ORIGINAL-QUALITY-V2", "BETA-SERVER" -> {
extractKickasssVideo(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
}
"DAILYMOTION" -> {
extractDailymotion(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
}
else -> {
extractVideo(
source.jsonObject["src"]!!.jsonPrimitive.content,
source.jsonObject["name"]!!.jsonPrimitive.content
)
}
}
}.getOrNull()
}.filterNotNull().flatten()
)
}
}
return videoList
@ -186,13 +214,13 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
private fun extractVideo(serverLink: String, server: String): List<Video> {
val playlist = mutableListOf<Video>()
val subsList = mutableListOf<Track>()
val data: MutableList<Pair<String, Headers>>
var vidHeader = headers
if (server == "MAVERICKKI") {
val resp = if (server == "MAVERICKKI") {
val apiLink = serverLink.replace("embed", "api/source")
// for some reason the request to the api is only working reliably this way
val apiResponse = client.newCall(GET(apiLink, headers)).execute().asJsoup().text()
val json = Json.decodeFromString<JsonObject>(apiResponse)
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 {
@ -200,52 +228,46 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
val subUrl = "${uri.scheme}://${uri.host}" + it.jsonObject["src"]!!.jsonPrimitive.content
try {
subsList.add(Track(subUrl, subLang))
} catch (e: Error) {}
} catch (_: Error) {}
}
data = mutableListOf(Pair("${uri.scheme}://${uri.host}" + json["hls"]!!.jsonPrimitive.content, headers))
vidHeader = embedHeader
client.newCall(GET("${uri.scheme}://${uri.host}" + json["hls"]!!.jsonPrimitive.content, embedHeader)).execute()
} else {
val playlistInterceptor = MasterPlaylistInterceptor()
val kickAssClient = client.newBuilder().addInterceptor(playlistInterceptor).build()
val kickAssClient = client.newBuilder().addInterceptor(MasterPlaylistInterceptor()).build()
kickAssClient.newCall(GET(serverLink, headers)).execute()
data = playlistInterceptor.playlist
}
data.forEach { (videoLink, headers) ->
val masterPlaylist = client.newCall(GET(videoLink, headers)).execute().body!!.string()
masterPlaylist.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" +
if (subsList.size > 0) { " (Toggleable Sub Available)" } else { "" }
var videoUrl = it.substringAfter("\n").substringBefore("\n")
if (videoUrl.startsWith("https").not()) {
val pos = videoLink.lastIndexOf('/') + 1
videoUrl = videoLink.substring(0, pos) + videoUrl
}
try {
playlist.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subsList, headers = headers))
} catch (e: Error) {
playlist.add(Video(videoUrl, quality, videoUrl, headers = headers))
}
resp.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" +
if (subsList.size > 0) { " (Toggleable Sub Available)" } else { "" }
var videoUrl = it.substringAfter("\n").substringBefore("\n")
if (videoUrl.startsWith("https").not()) {
videoUrl = resp.request.url.toString().substringBeforeLast("/") + "/$videoUrl"
}
}
try {
playlist.add(Video(videoUrl, quality, videoUrl, subtitleTracks = subsList, headers = vidHeader))
} catch (e: Error) {
playlist.add(Video(videoUrl, quality, videoUrl, headers = vidHeader))
}
}
return playlist
}
private fun extractBetaVideo(serverLink: String, server: String): List<Video> {
val headers = Headers.headersOf("referer", "https://kaast1.com/")
val document = client.newCall(GET(serverLink, headers)).execute().asJsoup()
val scripts = document.getElementsByTag("script")
var playlistArray = JsonArray(arrayListOf())
for (element in scripts) {
if (element.data().contains("window.files")) {
val pattern = Pattern.compile(".*JSON\\.parse\\('(.*)'\\)")
val matcher = pattern.matcher(element.data())
if (matcher.find()) {
playlistArray = json.decodeFromString(matcher.group(1)!!.toString())
}
break
document.selectFirst("script:containsData(window.files)")?.data()?.let {
val pattern = Pattern.compile(".*JSON\\.parse\\('(.*)'\\)")
val matcher = pattern.matcher(it)
if (matcher.find()) {
playlistArray = json.decodeFromString(matcher.group(1)!!.toString())
}
}
val playlist = mutableListOf<Video>()
playlistArray.forEach {
val quality = it.jsonObject["label"]!!.jsonPrimitive.content + " $server"
@ -258,26 +280,25 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
}
private fun extractKickasssVideo(serverLink: String, server: String): List<Video> {
val url = serverLink.replace("embed.php", "pref.php")
val url = serverLink.replace("(?:embed|player)\\.php".toRegex(), "pref.php")
val document = client.newCall(GET(url)).execute().asJsoup()
val scripts = document.getElementsByTag("script")
var playlistArray = JsonArray(arrayListOf())
for (element in scripts) {
if (element.data().contains("document.write")) {
val pattern = Pattern.compile(".*atob\\(\"(.*)\"\\)")
val matcher = pattern.matcher(element.data())
if (matcher.find()) {
val player = Base64.decode(matcher.group(1)!!.toString(), Base64.DEFAULT).toString(Charsets.UTF_8)
val playerPattern = Pattern.compile(".*setup\\(\\{sources:\\[(.*)\\]")
val playerMatcher = playerPattern.matcher(player)
if (playerMatcher.find()) {
val playlistString = "[" + playerMatcher.group(1)!!.toString() + "]"
playlistArray = json.decodeFromString(playlistString)
}
document.selectFirst("script:containsData(document.write)")?.data()?.let {
val pattern = if (server.contains("Beta", true)) Pattern.compile(".*decode\\(\"(.*)\"\\)")
else Pattern.compile(".*atob\\(\"(.*)\"\\)")
val matcher = pattern.matcher(it)
if (matcher.find()) {
val player = matcher.group(1)!!.toString().decodeBase64()
val playerPattern = Pattern.compile(".*sources:[ ]*\\[(.*)\\]")
val playerMatcher = playerPattern.matcher(player)
if (playerMatcher.find()) {
val playlistString = "[" + playerMatcher.group(1)!!.toString() + "]"
playlistArray = json.decodeFromString(playlistString)
}
break
}
}
val playlist = mutableListOf<Video>()
playlistArray.forEach {
val quality = it.jsonObject["label"]!!.jsonPrimitive.content + " $server"
@ -289,6 +310,77 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
return playlist
}
private fun extractDailymotion(serverLink: String, server: String): List<Video> {
val url = serverLink.replace("player.php", "pref.php")
val document = client.newCall(GET(url)).execute().asJsoup()
var masterPlaylist = listOf<Video>()
document.selectFirst("script:containsData(Base64.decode)")?.data()?.let { iframe ->
val embedUrl = iframe.substringAfter("decode(\"").substringBefore("\"").decodeBase64()
.substringAfter("src=\"").substringBefore("\"").substringBefore("?")
.replace("/embed/", "/player/metadata/")
val response = client.newCall(GET(embedUrl, headers)).execute()
val decodedJson = json.decodeFromString<DailyQuality>(response.body!!.string())
masterPlaylist = decodedJson.qualities.auto.parallelMap { item ->
runCatching {
val resp = client.newCall(GET(item.url)).execute().body!!.string()
resp.substringAfter("#EXT-X-STREAM-INF:")
.split("#EXT-X-STREAM-INF:").map {
val videoUrl = it.substringAfter("\n").substringBefore("\n")
val proxy = videoUrl.substringAfter("proxy-").substringBefore(".")
val quality = it.substringAfter("RESOLUTION=").split(",")[0].split("\n")[0].substringAfter("x") + "p $server" +
if (proxy.isNotBlank()) " $proxy" else ""
Video(videoUrl, quality, videoUrl, headers = Headers.headersOf("referer", "https://www.dailymotion.com/"))
}
}.getOrNull()
}.filterNotNull().flatten().distinct()
}
return masterPlaylist
}
private fun String.decodeBase64(): String {
return Base64.decode(this, Base64.DEFAULT).toString(Charsets.UTF_8)
}
private fun extractSapphireVideo(serverLink: String, server: String): List<Video> {
val url = serverLink.toHttpUrl().newBuilder().addQueryParameter("action", "config").build()
val response = client.newCall(GET(url.toString(), Headers.headersOf("referer", serverLink))).execute()
val rawJson = response.body!!.string().let {
var decoded = it
while (!decoded.startsWith("{\"id")) decoded = decoded.decodeBase64()
return@let decoded
}
val decodedJson = json.decodeFromString<Sapphire>(rawJson)
val subsList = decodedJson.subtitles.mapNotNull {
try {
Track(it.url, it.language.getLocale())
} catch (_: Error) {
null
}
}
return decodedJson.streams.filter { it.format == "adaptive_hls" }.parallelMap { stream ->
runCatching {
val playlist = client.newCall(GET(stream.url)).execute().body!!.string()
playlist.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" +
(if (stream.audio.getLocale().isNotBlank()) " - Aud: ${stream.audio.getLocale()}" else "") +
(if (stream.hardSub.getLocale().isNotBlank()) " - HardSub: ${stream.hardSub}" else "")
val videoUrl = it.substringAfter("\n").substringBefore("\n")
try {
Video(videoUrl, quality, videoUrl, subtitleTracks = subsList)
} catch (e: Error) {
Video(videoUrl, quality, videoUrl)
}
}
}.getOrNull()
}
.filterNotNull()
.flatten()
}
private fun extractGogoVideo(link: String): List<Video> {
var url = decode(link).substringAfter("data=").substringBefore("&vref")
if (url.startsWith("https").not()) {
@ -298,7 +390,7 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
val document = client.newCall(GET(url)).execute().asJsoup()
// Vidstreaming:
videoList.addAll(GogoCdnExtractor(network.client, json).videosFromUrl(url))
videoList.addAll(GogoCdnExtractor(client, json).videosFromUrl(url))
// Doodstream mirror:
document.select("div#list-server-more > ul > li.linkserver:contains(Doodstream)")
.firstOrNull()?.attr("data-video")
@ -311,41 +403,15 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
}
override fun List<Video>.sort(): List<Video> {
val quality = preferences.getString("preferred_quality", "1080")
val server = preferences.getString("preferred_server", "MAVERICKKI")
val quality = preferences.getString("preferred_quality", "1080")!!
val server = preferences.getString("preferred_server", "MAVERICKKI")!!
if (quality != null || server != null) {
val qualityList = mutableListOf<Video>()
if (quality != null) {
var preferred = 0
for (video in this) {
if (video.quality.contains(quality)) {
qualityList.add(preferred, video)
preferred++
} else {
qualityList.add(video)
}
}
} else {
qualityList.addAll(this)
}
val newList = mutableListOf<Video>()
if (server != null) {
var preferred = 0
for (video in qualityList) {
if (video.quality.contains(server)) {
newList.add(preferred, video)
preferred++
} else {
newList.add(video)
}
}
} else {
newList.addAll(qualityList)
}
return newList
}
return this
return this.sortedWith(
compareBy(
{ it.quality.contains(quality) },
{ it.quality.contains(server) }
)
).reversed()
}
override fun searchAnimeRequest(page: Int, query: String, filters: AnimeFilterList): Request {
@ -358,7 +424,8 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
val animes = animeList.map { item ->
SAnime.create().apply {
setUrlWithoutDomain(item.jsonObject["slug"]!!.jsonPrimitive.content)
thumbnail_url = "$baseUrl/uploads/" + item.jsonObject["poster"]!!.jsonPrimitive.content
thumbnail_url =
"$baseUrl/uploads/" + item.jsonObject["poster"]!!.jsonPrimitive.content
title = item.jsonObject["name"]!!.jsonPrimitive.content
}
}
@ -371,19 +438,21 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
if (appData.isEmpty().not()) {
val ani = appData["anime"]!!.jsonObject
anime.title = ani["name"]!!.jsonPrimitive.content
anime.genre = ani["genres"]!!.jsonArray.joinToString { it.jsonObject["name"]!!.jsonPrimitive.content }
anime.genre =
ani["genres"]!!.jsonArray.joinToString { it.jsonObject["name"]!!.jsonPrimitive.content }
anime.description = JSONUtil.unescape(ani["description"]!!.jsonPrimitive.content)
anime.status = parseStatus(ani["status"]!!.jsonPrimitive.content)
val altName = "Other name(s): "
json.decodeFromString<JsonArray>(ani["alternate"].toString().replace("\"\"", "[]")).let { altArray ->
if (altArray.isEmpty().not()) {
anime.description = when {
anime.description.isNullOrBlank() -> altName + altArray.joinToString { it.jsonPrimitive.content }
else -> anime.description + "\n\n$altName" + altArray.joinToString { it.jsonPrimitive.content }
json.decodeFromString<JsonArray>(ani["alternate"].toString().replace("\"\"", "[]"))
.let { altArray ->
if (altArray.isEmpty().not()) {
anime.description = when {
anime.description.isNullOrBlank() -> altName + altArray.joinToString { it.jsonPrimitive.content }
else -> anime.description + "\n\n$altName" + altArray.joinToString { it.jsonPrimitive.content }
}
}
}
}
}
return anime
}
@ -482,4 +551,72 @@ class KickAssAnime : ConfigurableAnimeSource, AnimeHttpSource() {
private fun encode(input: String): String = java.net.URLEncoder.encode(input, "utf-8")
private fun decode(input: String): String = java.net.URLDecoder.decode(input, "utf-8")
private fun String.getLocale(): String {
return arrayOf(
Pair("ar-ME", "Arabic"),
Pair("ar-SA", "Arabic (Saudi Arabia)"),
Pair("de-DE", "German"),
Pair("en-US", "English"),
Pair("es-419", "Spanish"),
Pair("es-ES", "Spanish (Spain)"),
Pair("es-LA", "Spanish (Spanish)"),
Pair("fr-FR", "French"),
Pair("ja-JP", "Japanese"),
Pair("it-IT", "Italian"),
Pair("pt-BR", "Portuguese (Brazil)"),
Pair("pl-PL", "Polish"),
Pair("ru-RU", "Russian"),
Pair("tr-TR", "Turkish"),
Pair("uk-UK", "Ukrainian"),
Pair("he-IL", "Hebrew"),
Pair("ro-RO", "Romanian"),
Pair("sv-SE", "Swedish")
).firstOrNull { it.first == this }?.second ?: ""
}
@Serializable
data class DailyQuality(
val qualities: Auto
) {
@Serializable
data class Auto(
val auto: List<Item>
) {
@Serializable
data class Item(
val type: String,
val url: String
)
}
}
@Serializable
data class Sapphire(
val subtitles: List<Subtitle>,
val streams: List<Stream>
) {
@Serializable
data class Subtitle(
val language: String,
val url: String
)
@Serializable
data class Stream(
@SerialName("audio_lang")
val audio: String,
@SerialName("hardsub_lang")
val hardSub: String,
val url: String,
val format: String
)
}
// From Dopebox
private fun <A, B> Iterable<A>.parallelMap(f: suspend (A) -> B): List<B> =
runBlocking {
map { async(Dispatchers.Default) { f(it) } }.awaitAll()
}
}

View File

@ -8,7 +8,7 @@ import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import okhttp3.Headers
import eu.kanade.tachiyomi.network.GET
import okhttp3.Headers.Companion.toHeaders
import okhttp3.Interceptor
import okhttp3.Request
@ -22,17 +22,17 @@ class MasterPlaylistInterceptor : Interceptor {
private val context = Injekt.get<Application>()
private val handler by lazy { Handler(Looper.getMainLooper()) }
val playlist = mutableListOf<Pair<String, Headers>>()
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val newRequest = resolveWithWebView(originalRequest)
val newRequest = resolveWithWebView(originalRequest) ?: throw Exception("Could not find playlist")
return chain.proceed(newRequest)
}
@SuppressLint("SetJavaScriptEnabled")
private fun resolveWithWebView(request: Request): Request {
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)
@ -42,6 +42,8 @@ class MasterPlaylistInterceptor : Interceptor {
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
@ -51,8 +53,7 @@ class MasterPlaylistInterceptor : Interceptor {
databaseEnabled = true
useWideViewPort = false
loadWithOverviewMode = false
userAgentString = request.header("User-Agent")
?: "\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36 Edg/88.0.705.63\""
userAgentString = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0"
}
webview.webViewClient = object : WebViewClient() {
@ -61,7 +62,7 @@ class MasterPlaylistInterceptor : Interceptor {
request: WebResourceRequest,
): WebResourceResponse? {
if (request.url.toString().contains(".m3u8")) {
playlist.add(Pair(request.url.toString(), request.requestHeaders.toHeaders()))
newRequest = GET(request.url.toString(), request.requestHeaders.toHeaders())
latch.countDown()
}
return super.shouldInterceptRequest(view, request)
@ -81,6 +82,6 @@ class MasterPlaylistInterceptor : Interceptor {
webView = null
}
return request
return newRequest
}
}