diff --git a/src/en/hanime/src/eu/kanade/tachiyomi/animeextension/en/hanime/Hanime.kt b/src/en/hanime/src/eu/kanade/tachiyomi/animeextension/en/hanime/Hanime.kt index 77bc94095..7c2238973 100644 --- a/src/en/hanime/src/eu/kanade/tachiyomi/animeextension/en/hanime/Hanime.kt +++ b/src/en/hanime/src/eu/kanade/tachiyomi/animeextension/en/hanime/Hanime.kt @@ -59,7 +59,7 @@ class Hanime : AnimeHttpSource() { val responseString = response.body!!.string() return parseSearchJson(responseString) } - fun parseSearchJson(jsonLine: String?): AnimesPage { + private fun parseSearchJson(jsonLine: String?): AnimesPage { val jElement: JsonElement = JsonParser.parseString(jsonLine) val jObject: JsonObject = jElement.asJsonObject val nbPages = jObject.get("nbPages").asInt diff --git a/src/en/twistmoe/src/eu/kanade/tachiyomi/animeextension/en/twistmoe/AESDecrypt.kt b/src/en/twistmoe/src/eu/kanade/tachiyomi/animeextension/en/twistmoe/AESDecrypt.kt index 84cfffd12..739c00c66 100644 --- a/src/en/twistmoe/src/eu/kanade/tachiyomi/animeextension/en/twistmoe/AESDecrypt.kt +++ b/src/en/twistmoe/src/eu/kanade/tachiyomi/animeextension/en/twistmoe/AESDecrypt.kt @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.animeextension.en.twistmoe import android.annotation.TargetApi import android.os.Build -import android.util.Log import java.security.MessageDigest import java.util.Base64 import javax.crypto.Cipher @@ -15,20 +14,27 @@ class AESDecrypt { fun aesEncrypt(v: String, secretKey: ByteArray, initializationVector: ByteArray) = encrypt(v, secretKey, initializationVector) fun aesDecrypt(v: ByteArray, secretKey: ByteArray, initializationVector: ByteArray) = decrypt(v, secretKey, initializationVector) fun getIvAndKey(v: String): ByteArray { + // credits: https://github.com/anime-dl/anime-downloader/blob/c030fded0b7f79d5bb8a07f5cf6b2ae8fa3954a1/anime_downloader/sites/twistmoe.py val byteStr = decoder.decode(v.toByteArray(Charsets.UTF_8)) val md5 = MessageDigest.getInstance("MD5") assert(byteStr.decodeToString(0, 8) == "Salted__") val salt = byteStr.sliceArray(8..15) + assert(salt.lastIndex == 7) val secretStr = "267041df55ca2b36f2e322d05ee2c9cf" val secret = secretStr .map { it.toByte() } .toByteArray() - val step1 = md5.digest(salt) - val step2 = step1 + md5.digest(step1 + secret) - val step3 = step2 + md5.digest(step2 + secret) - Log.i("lol", step3.decodeToString()) - assert(step3.lastIndex == 47) - return step3 + val data = secret + salt + var key = md5.digest(data) + var finalKey = key + while (finalKey.lastIndex < 47) { + key = md5.digest(key + data) + finalKey += key + } + return finalKey.sliceArray(0..47) + } + fun unpad(v: String): String { + return v.substring(0..v.lastIndex - v.last().toInt()) } fun getToDecode(v: String): ByteArray { val byteStr = decoder.decode(v.toByteArray(Charsets.UTF_8)) @@ -37,6 +43,7 @@ class AESDecrypt { } private fun cipher(opmode: Int, secretKey: ByteArray, initializationVector: ByteArray): Cipher { if (secretKey.lastIndex != 31) throw RuntimeException("SecretKey length is not 32 chars") + if (initializationVector.lastIndex != 15) throw RuntimeException("IV length is not 16 chars") val c = Cipher.getInstance("AES/CBC/NoPadding") val sk = SecretKeySpec(secretKey, "AES") val iv = IvParameterSpec(initializationVector) diff --git a/src/en/twistmoe/src/eu/kanade/tachiyomi/animeextension/en/twistmoe/TwistMoe.kt b/src/en/twistmoe/src/eu/kanade/tachiyomi/animeextension/en/twistmoe/TwistMoe.kt index c42a8363f..3d47ffa8b 100644 --- a/src/en/twistmoe/src/eu/kanade/tachiyomi/animeextension/en/twistmoe/TwistMoe.kt +++ b/src/en/twistmoe/src/eu/kanade/tachiyomi/animeextension/en/twistmoe/TwistMoe.kt @@ -19,6 +19,7 @@ import okhttp3.Request import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response +import java.lang.Exception import java.util.Date class TwistMoe : AnimeHttpSource() { @@ -32,7 +33,7 @@ class TwistMoe : AnimeHttpSource() { override val supportsLatest = false private val popularRequestHeaders = - Headers.headersOf("x-access-token", "0df14814b9e590a1f26d3071a4ed7974") + Headers.headersOf("x-access-token", "0df14814b9e590a1f26d3071a4ed7974", "referer", baseUrl) override fun popularAnimeRequest(page: Int): Request = GET("https://api.twist.moe/api/anime", popularRequestHeaders) @@ -56,7 +57,6 @@ class TwistMoe : AnimeHttpSource() { else -> SAnime.UNKNOWN } anime.thumbnail_url = "https://homepages.cae.wisc.edu/~ece533/images/cat.png" - anime.initialized = true animeList.add(anime) } return AnimesPage(animeList, false) @@ -88,7 +88,6 @@ class TwistMoe : AnimeHttpSource() { 1 -> SAnime.ONGOING else -> SAnime.UNKNOWN } - anime.initialized = true return anime } @@ -116,29 +115,30 @@ class TwistMoe : AnimeHttpSource() { } override fun episodeListRequest(anime: SAnime): Request { - val aes = AESDecrypt() - val ivAndKey = aes.getIvAndKey("U2FsdGVkX19njUQXx448lKxE4wUQA8tH45sgjCYckbrdS15QHY3fW5ChD6UpcoackxmWn8/5Tk88yAAwSukKwKpfvI6rQ1ERxFcAspfBCj8U/IQYoE3gZy+Esgumt/Fz") - val toDecode = aes.getToDecode("U2FsdGVkX19njUQXx448lKxE4wUQA8tH45sgjCYckbrdS15QHY3fW5ChD6UpcoackxmWn8/5Tk88yAAwSukKwKpfvI6rQ1ERxFcAspfBCj8U/IQYoE3gZy+Esgumt/Fz") - Log.i("lol_key", ivAndKey.sliceArray(0..31).decodeToString()) - Log.i("lol_iv", ivAndKey.sliceArray(32..47).decodeToString()) - Log.i("lol_final", aes.aesDecrypt(toDecode, ivAndKey.sliceArray(0..31), ivAndKey.sliceArray(32..47))) + // aes.unpad(aes.aesDecrypt(toDecode, ivAndKey.sliceArray(0..31), ivAndKey.sliceArray(32..47))) val slug = anime.url.substringAfter("/a/") return GET("https://api.twist.moe/api/anime/$slug/sources", popularRequestHeaders) } override fun episodeListParse(response: Response): List { val responseString = response.body!!.string() - val jElement: JsonElement = JsonParser.parseString(responseString) - val array: JsonArray = jElement.asJsonArray + Log.i("lol_response", responseString) + val array = JsonParser.parseString(responseString).asJsonArray val episodeList = mutableListOf() for (entry in array) { - val episode = SEpisode.create() - episode.date_upload = Date.parse(entry.asJsonObject.get("updated_at").asString) - episode.name = "Episode " + entry.asJsonObject.get("number").asString - episode.url = entry.asJsonObject.get("source").asString - episode.episode_number = entry.asJsonObject.get("number").asFloat - episodeList.add(episode) + try { + Log.i("lol", entry.toString()) + val episode = SEpisode.create() + episode.date_upload = Date.parse(entry.asJsonObject.get("updated_at").asString) + episode.name = "Episode " + entry.asJsonObject.get("number").asNumber.toString() + episode.episode_number = entry.asJsonObject.get("number").asFloat + episode.url = response.request.url.toString() + "#${episode.episode_number}" + episodeList.add(episode) + } catch (e: Exception) { + Log.i("lol_e", e.message!!) + } } + Log.i("lol", episodeList.lastIndex.toString()) return episodeList }