fix(en/Aniplay): Video list empty fixed and other improvments (#343)

* fix(en/AniPlay): Updated VersionCode

* fix(en/AniPlay): Added idMal to request

* fix(en/AniPlay): Added headers for backup domain

* fix(en/AniPlay): Better episode list parsing
This commit is contained in:
Josef František Straka 2024-11-01 05:23:51 +01:00 committed by GitHub
parent 21bc5b8bfb
commit 0f10653698
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 68 additions and 10 deletions

View file

@ -2,7 +2,7 @@ ext {
extName = 'AniPlay' extName = 'AniPlay'
extClass = '.AniPlay' extClass = '.AniPlay'
themePkg = 'anilist' themePkg = 'anilist'
overrideVersionCode = 3 overrideVersionCode = 4
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View file

@ -15,6 +15,7 @@ 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.multisrc.anilist.AniListAnimeHttpSource import eu.kanade.tachiyomi.multisrc.anilist.AniListAnimeHttpSource
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.util.parallelFlatMapBlocking import eu.kanade.tachiyomi.util.parallelFlatMapBlocking
import eu.kanade.tachiyomi.util.parseAs import eu.kanade.tachiyomi.util.parseAs
@ -67,6 +68,8 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
} }
} }
val baseHost: String get() = "${preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN_DEFAULT)}"
/* ====================================== Episode List ====================================== */ /* ====================================== Episode List ====================================== */
override fun episodeListRequest(anime: SAnime): Request { override fun episodeListRequest(anime: SAnime): Request {
@ -79,7 +82,7 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
val headersWithAction = val headersWithAction =
headers.newBuilder() headers.newBuilder()
// next.js stuff I guess // next.js stuff I guess
.add("Next-Action", HEADER_NEXT_ACTION_EPISODE_LIST_VALUE) .add("Next-Action", getHeaderValue(baseHost, NEXT_ACTION_EPISODE_LIST))
.build() .build()
return POST(url = "$baseUrl/anime/info/$animeId", headersWithAction, requestBody) return POST(url = "$baseUrl/anime/info/$animeId", headersWithAction, requestBody)
@ -90,8 +93,28 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
val episodeListUrl = response.request.url val episodeListUrl = response.request.url
val animeId = episodeListUrl.pathSegments[2] val animeId = episodeListUrl.pathSegments[2]
val responsePage = client.newCall(GET("$baseUrl/anime/watch/$animeId")).execute()
val responsePageString = responsePage.body.string()
var idMal: Number? = null
val idMalIndex = responsePageString.indexOf("\\\"idMal\\\":")
if (idMalIndex != -1) {
val startIndex = idMalIndex + "\\\"idMal\\\":".length
val endIndex = responsePageString.indexOf(',', startIndex)
if (endIndex != -1) {
idMal = responsePageString.substring(startIndex, endIndex).toIntOrNull()
}
}
if (idMal == null) {
Log.e("AniPlay", "idMal not found - responsePageString: $responsePageString")
throw Exception("idMal not found")
}
val responseString = response.body.string() val responseString = response.body.string()
val episodesArrayString = responseString.split("1:").last() val episodesArrayString = extractEpisodeList(responseString)
if (episodesArrayString == null) {
Log.e("AniPlay", "Episode list not found - ${response.request}\nbody:${response.request.body}\n${responseString.substring(0,200)}")
throw Exception("Episode list not found")
}
val providers = episodesArrayString.parseAs<List<EpisodeListResponse>>() val providers = episodesArrayString.parseAs<List<EpisodeListResponse>>()
val episodes = mutableMapOf<Int, EpisodeListResponse.Episode>() val episodes = mutableMapOf<Int, EpisodeListResponse.Episode>()
@ -127,6 +150,7 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
.addQueryParameter("id", animeId) .addQueryParameter("id", animeId)
.addQueryParameter("ep", episodeNumber.toString()) .addQueryParameter("ep", episodeNumber.toString())
.addQueryParameter("extras", episodeExtraString) .addQueryParameter("extras", episodeExtraString)
.addQueryParameter("idMal", idMal.toString())
.build() .build()
val name = parseEpisodeName(episodeNumber.toString(), episode.title) val name = parseEpisodeName(episodeNumber.toString(), episode.title)
@ -156,7 +180,7 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
override suspend fun getVideoList(episode: SEpisode): List<Video> { override suspend fun getVideoList(episode: SEpisode): List<Video> {
val episodeUrl = episode.url.toHttpUrl() val episodeUrl = episode.url.toHttpUrl()
val animeId = episodeUrl.queryParameter("id") ?: return emptyList() val animeId = episodeUrl.queryParameter("id") ?: return emptyList()
// val episodeNum = episodeUrl.queryParameter("ep") ?: return emptyList() val idMal = episodeUrl.queryParameter("idMal") ?: return emptyList()
val extras = episodeUrl.queryParameter("extras") val extras = episodeUrl.queryParameter("extras")
?.let { ?.let {
try { try {
@ -170,7 +194,7 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
try { try {
json.decodeFromString<List<EpisodeExtra>>(it) json.decodeFromString<List<EpisodeExtra>>(it)
} catch (e: SerializationException) { } catch (e: SerializationException) {
Log.e("AniPlay", "Error parsing JSON", e) Log.e("AniPlay", "Error parsing JSON extras", e)
emptyList() emptyList()
} }
} }
@ -179,7 +203,7 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
val headersWithAction = val headersWithAction =
headers.newBuilder() headers.newBuilder()
// next.js stuff I guess // next.js stuff I guess
.add("Next-Action", HEADER_NEXT_ACTION_SOURCES_LIST_VALUE) .add("Next-Action", getHeaderValue(baseHost, NEXT_ACTION_SOURCES_LIST))
.build() .build()
val episodeDataList = extras.parallelFlatMapBlocking { extra -> val episodeDataList = extras.parallelFlatMapBlocking { extra ->
@ -193,7 +217,7 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
extra.episodeNum.toString() // If it has a fractional part, leave it as a float extra.episodeNum.toString() // If it has a fractional part, leave it as a float
} }
val requestBody = "[\"$animeId\",\"${extra.source}\",\"${extra.episodeId}\",\"$epNum\",\"$language\"]" val requestBody = "[\"$animeId\",$idMal,\"${extra.source}\",\"${extra.episodeId}\",\"$epNum\",\"$language\"]"
.toRequestBody("application/json".toMediaType()) .toRequestBody("application/json".toMediaType())
val params = mapOf( val params = mapOf(
@ -205,7 +229,6 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
val builder = Uri.parse("$baseUrl/anime/watch").buildUpon() val builder = Uri.parse("$baseUrl/anime/watch").buildUpon()
params.map { (k, v) -> builder.appendQueryParameter(k, v); } params.map { (k, v) -> builder.appendQueryParameter(k, v); }
val url = builder.build().toString() val url = builder.build().toString()
Log.i("AniPlay", "Url: $url")
try { try {
val request = POST(url, headersWithAction, requestBody) val request = POST(url, headersWithAction, requestBody)
val response = client.newCall(request).execute() val response = client.newCall(request).execute()
@ -270,6 +293,26 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
) )
} }
private fun extractEpisodeList(input: String): String? {
val startMarker = "1:["
val list1Index = input.indexOf(startMarker)
if (list1Index == -1) return null
val startIndex = list1Index + startMarker.length
var endIndex = startIndex
var bracketCount = 1
while (endIndex < input.length && bracketCount > 0) {
when (input[endIndex]) {
'[' -> bracketCount++
']' -> bracketCount--
}
endIndex++
}
return if (bracketCount == 0) input.substring(startIndex - 1, endIndex) else null
}
/* ====================================== Preferences ====================================== */ /* ====================================== Preferences ====================================== */
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
@ -391,6 +434,10 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
} ?: 0L } ?: 0L
} }
private fun getHeaderValue(serverHost: String, key: String): String {
return HEADER_NEXT_ACTION[serverHost]?.get(key) ?: throw Exception("Bad host/key")
}
companion object { companion object {
private const val PREF_DOMAIN_KEY = "domain" private const val PREF_DOMAIN_KEY = "domain"
private val PREF_DOMAIN_ENTRIES = arrayOf("aniplaynow.live (default)", "aniplay.lol (backup)") private val PREF_DOMAIN_ENTRIES = arrayOf("aniplaynow.live (default)", "aniplay.lol (backup)")
@ -421,8 +468,19 @@ class AniPlay : AniListAnimeHttpSource(), ConfigurableAnimeSource {
private const val PREF_MARK_FILLER_EPISODE_DEFAULT = true private const val PREF_MARK_FILLER_EPISODE_DEFAULT = true
// These values has probably something to do with Next.js server and hydration // These values has probably something to do with Next.js server and hydration
private const val HEADER_NEXT_ACTION_EPISODE_LIST_VALUE = "f3422af67c84852f5e63d50e1f51718f1c0225c4" private const val NEXT_ACTION_EPISODE_LIST = "NEXT_ACTION_EPISODE_LIST"
private const val HEADER_NEXT_ACTION_SOURCES_LIST_VALUE = "5dbcd21c7c276c4d15f8de29d9ef27aef5ea4a5e" private const val NEXT_ACTION_SOURCES_LIST = "NEXT_ACTION_SOURCES_LIST"
private val HEADER_NEXT_ACTION = mapOf(
PREF_DOMAIN_ENTRY_VALUES[0] to mapOf(
"NEXT_ACTION_EPISODE_LIST" to "f3422af67c84852f5e63d50e1f51718f1c0225c4",
"NEXT_ACTION_SOURCES_LIST" to "5dbcd21c7c276c4d15f8de29d9ef27aef5ea4a5e",
),
PREF_DOMAIN_ENTRY_VALUES[1] to mapOf(
"NEXT_ACTION_EPISODE_LIST" to "56e4151352ded056cbe226d2376c7436cffc9a37",
"NEXT_ACTION_SOURCES_LIST" to "8a76af451978c817dde2364326a5e4e45eb43db1",
),
)
private val DATE_FORMATTER = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) private val DATE_FORMATTER = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
} }