Fix #550 and Improve KwikExtractor Decrypt Function (#551)

* Improve KwikExtractor Decrypt Function

- Optimize and improve performance of KwikExtractor decrypt functionality

* Fix issue #550

* Update version
This commit is contained in:
CursedSheep 2025-01-18 00:47:35 +08:00 committed by GitHub
parent 970d14a93c
commit b4e9c0d3a5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 23 additions and 69 deletions

View file

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

View file

@ -19,9 +19,6 @@ import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.util.asJsoup
import eu.kanade.tachiyomi.util.parseAs
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.Request
@ -67,16 +64,8 @@ class AnimePahe : ConfigurableAnimeSource, AnimeHttpSource() {
* @see episodeListRequest
*/
override fun animeDetailsRequest(anime: SAnime): Request {
val animeId = anime.getId()
// We're using coroutines here to run it inside another thread and
// prevent android.os.NetworkOnMainThreadException when trying to open
// webview or share it.
val session = runBlocking {
withContext(Dispatchers.IO) {
fetchSession(anime.title, animeId)
}
}
return GET("$baseUrl/anime/$session?anime_id=$animeId")
val session = anime.getSession()
return GET("$baseUrl/anime/$session")
}
override fun animeDetailsParse(response: Response): SAnime {
@ -106,8 +95,8 @@ class AnimePahe : ConfigurableAnimeSource, AnimeHttpSource() {
SAnime.create().apply {
title = anime.title
thumbnail_url = anime.snapshot
val animeId = anime.id
setUrlWithoutDomain("/anime/?anime_id=$animeId")
val sessionId = anime.anime_session
setUrlWithoutDomain("/anime/?session_id=$sessionId")
artist = anime.fansub
}
}
@ -124,8 +113,8 @@ class AnimePahe : ConfigurableAnimeSource, AnimeHttpSource() {
SAnime.create().apply {
title = anime.title
thumbnail_url = anime.poster
val animeId = anime.id
setUrlWithoutDomain("/anime/?anime_id=$animeId")
val sessionId = anime.session
setUrlWithoutDomain("/anime/?session_id=$sessionId")
}
}
return AnimesPage(animeList, false)
@ -146,7 +135,7 @@ class AnimePahe : ConfigurableAnimeSource, AnimeHttpSource() {
* @see animeDetailsRequest
*/
override fun episodeListRequest(anime: SAnime): Request {
val session = fetchSession(anime.title, anime.getId())
val session = anime.getSession()
return GET("$baseUrl/api?m=release&id=$session&sort=episode_desc&page=1")
}
@ -310,15 +299,6 @@ class AnimePahe : ConfigurableAnimeSource, AnimeHttpSource() {
}
// ============================= Utilities ==============================
private fun fetchSession(title: String, animeId: String): String {
return client.newCall(GET("$baseUrl/api?m=search&q=$title"))
.execute()
.body.string()
.substringAfter("\"id\":$animeId")
.substringAfter("\"session\":\"")
.substringBefore("\"")
}
private fun parseStatus(statusString: String): Int {
return when (statusString) {
"Currently Airing" -> SAnime.ONGOING
@ -327,7 +307,7 @@ class AnimePahe : ConfigurableAnimeSource, AnimeHttpSource() {
}
}
private fun SAnime.getId() = url.substringAfterLast("?anime_id=").substringBefore("\"")
private fun SAnime.getSession() = url.substringAfterLast("?session_id=").substringBefore("\"")
private fun String.toDate(): Long {
return runCatching {

View file

@ -35,7 +35,6 @@ import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.OkHttpClient
import okhttp3.Response
import kotlin.math.pow
class KwikExtractor(private val client: OkHttpClient) {
private var cookies: String = ""
@ -108,52 +107,25 @@ class KwikExtractor(private val client: OkHttpClient) {
}
private fun decrypt(fullString: String, key: String, v1: Int, v2: Int): String {
var r = ""
val keyIndexMap = key.withIndex().associate { it.value to it.index }
val sb = StringBuilder()
var i = 0
val toFind = key[v2]
while (i < fullString.length) {
var s = ""
while (fullString[i] != key[v2]) {
s += fullString[i]
++i
val nextIndex = fullString.indexOf(toFind, i)
val decodedCharStr = buildString {
for (j in i until nextIndex) {
append(keyIndexMap[fullString[j]] ?: -1)
}
var j = 0
while (j < key.length) {
s = s.replace(key[j].toString(), j.toString())
++j
}
r += (getString(s, v2).toInt() - v1).toChar()
++i
}
return r
}
private fun getString(content: String, s1: Int): String {
val s2 = 10
val characterMap = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
i = nextIndex + 1
val slice2 = characterMap.slice(0 until s2)
var acc: Long = 0
for ((n, i) in content.reversed().withIndex()) {
acc += when (isNumber("$i")) {
true -> "$i".toLong()
false -> 0L
} * s1.toDouble().pow(n.toDouble()).toInt()
val decodedChar = (decodedCharStr.toInt(v2) - v1).toChar()
sb.append(decodedChar)
}
var k = ""
while (acc > 0) {
k = slice2[(acc % s2).toInt()] + k
acc = (acc - (acc % s2)) / s2
}
return when (k != "") {
true -> k
false -> "0"
}
return sb.toString()
}
}

View file

@ -23,6 +23,7 @@ data class LatestAnimeDto(
@SerialName("anime_id")
val id: Int,
val fansub: String,
val anime_session: String,
)
@Serializable
@ -30,6 +31,7 @@ data class SearchResultDto(
val title: String,
val poster: String,
val id: Int,
val session: String,
)
@Serializable