AnimeXNovel: Fix loading chapters (#9392)

Fix loading chapters
This commit is contained in:
Chopper 2025-06-23 14:09:54 -03:00 committed by Draff
parent ee6ec2bd75
commit 53ff72f22c
Signed by: Draff
GPG Key ID: E8A89F3211677653
3 changed files with 76 additions and 27 deletions

View File

@ -3,7 +3,7 @@ ext {
extClass = '.AnimeXNovel'
themePkg = 'zeistmanga'
baseUrl = 'https://www.animexnovel.com'
overrideVersionCode = 1
overrideVersionCode = 2
isNsfw = false
}

View File

@ -1,15 +1,19 @@
package eu.kanade.tachiyomi.extension.pt.animexnovel
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistMangaDto
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import keiyoushi.utils.parseAs
import keiyoushi.utils.tryParse
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Response
import org.jsoup.nodes.Element
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Locale
class AnimeXNovel : ZeistManga(
"AnimeXNovel",
@ -24,19 +28,13 @@ class AnimeXNovel : ZeistManga(
override val popularMangaSelectorUrl = popularMangaSelectorTitle
override fun popularMangaParse(response: Response): MangasPage {
return super.popularMangaParse(response).let { mangaPage ->
mangaPage.mangas.filter { it.title.contains("[Mangá]") }.let {
mangaPage.copy(it)
}
}
return super.popularMangaParse(response).let(::filterMangaEntries)
}
// ============================== Latest ===============================
override fun latestUpdatesParse(response: Response): MangasPage {
return super.latestUpdatesParse(response).let {
it.copy(it.mangas.filter { it.title.contains("[Mangá]") })
}
return super.latestUpdatesParse(response).let(::filterMangaEntries)
}
// ============================== Details ===============================
@ -47,7 +45,7 @@ class AnimeXNovel : ZeistManga(
thumbnail_url = document.selectFirst(".thumb")?.absUrl("src")
description = document.selectFirst("#synopsis")?.text()
document.selectFirst("span[data-status]")?.let {
status = parseStatus(it.text())
status = parseStatus(it.text().lowercase())
}
genre = document.select("dl:has(dt:contains(Gênero)) dd a")
.joinToString { it.text() }
@ -57,31 +55,67 @@ class AnimeXNovel : ZeistManga(
// ============================== Chapters ===============================
override val chapterCategory = "Capítulo"
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
val url = getChapterFeedUrl(document).toHttpUrl().newBuilder()
.setQueryParameter("start-index", "1")
val label = document.select("script")
.map(Element::data)
.firstOrNull(MANGA_TITLE_REGEX::containsMatchIn)?.let {
MANGA_TITLE_REGEX.find(it)?.groups?.get(1)?.value
} ?: return emptyList()
val chapters = mutableListOf<SChapter>()
do {
val res = client.newCall(GET(url.build(), headers)).execute()
val script = document.select("script")
.map(Element::data)
.firstOrNull(API_KEY_REGEX::containsMatchIn)
?: return emptyList()
val page = json.decodeFromString<ZeistMangaDto>(res.body.string()).feed?.entry
?.filter { it.category.orEmpty().any { category -> category.term == chapterCategory } }
?.map { it.toSChapter(baseUrl) }
?: emptyList()
val blogId = BLOG_ID_REGEX.find(script)?.groups?.get(1)?.value ?: return emptyList()
val apiKey = API_KEY_REGEX.find(script)?.groups?.get(1)?.value ?: return emptyList()
chapters += page
url.setQueryParameter("start-index", "${chapters.size + 1}")
} while (page.isNotEmpty())
val url = "https://www.googleapis.com/blogger/v3/blogs/$blogId/posts".toHttpUrl().newBuilder()
.addQueryParameter("key", apiKey)
.addQueryParameter("labels", label)
.addQueryParameter("maxResults", "500")
.build()
return chapters
val response = client.newCall(GET(url, headers)).execute()
if (response.isSuccessful.not()) {
throw IOException("Capítulos não encontrados")
}
return response.parseAs<ChapterWrapperDto>().items.map {
SChapter.create().apply {
name = it.title
date_upload = dateFormat.tryParse(it.published)
setUrlWithoutDomain(it.url)
}
}
}
// ============================== Pages ===============================
override val pageListSelector = "#reader .separator"
// ============================== Utils ===============================
private fun filterMangaEntries(mangasPage: MangasPage): MangasPage {
val prefix = "[Mangá]"
return mangasPage.copy(
mangasPage.mangas.filter {
it.title.contains(prefix)
}.map {
it.apply {
title = title.substringAfter(prefix).trim()
}
},
)
}
companion object {
private val API_KEY_REGEX = """(?:API_KEY(?:\s+)?=(?:\s+)?.)"([^(\\|")]+)""".toRegex()
private val BLOG_ID_REGEX = """(?:BLOG_ID(?:\s+)?=(?:\s+)?.)"([^(\\|")]+)""".toRegex()
private val MANGA_TITLE_REGEX = """iniciarCapituloLoader\("([^"]+)"\)""".toRegex()
private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT)
}
}

View File

@ -0,0 +1,15 @@
package eu.kanade.tachiyomi.extension.pt.animexnovel
import kotlinx.serialization.Serializable
@Serializable
class ChapterWrapperDto(
val items: List<ChapterDto>,
)
@Serializable
class ChapterDto(
val title: String,
val published: String,
val url: String,
)