MangaTaro: migrate to new chapter endpoint (#11636)

This commit is contained in:
AwkwardPeak7 2025-11-14 22:29:44 +05:00 committed by Draff
parent 1aa1119924
commit 60e78280c8
Signed by: Draff
GPG Key ID: E8A89F3211677653
3 changed files with 70 additions and 19 deletions

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'MangaTaro' extName = 'MangaTaro'
extClass = '.MangaTaro' extClass = '.MangaTaro'
extVersionCode = 5 extVersionCode = 6
isNsfw = false isNsfw = false
} }

View File

@ -112,3 +112,19 @@ class Thumbnail(
class Rendered( class Rendered(
val rendered: String, val rendered: String,
) )
@Serializable
class ChapterList(
val chapters: List<Chapter>,
)
@Serializable
class Chapter(
val url: String,
val chapter: String,
val title: String? = null,
val date: String,
@SerialName("group_name")
val groupName: String? = null,
val language: String,
)

View File

@ -28,7 +28,12 @@ import org.jsoup.Jsoup
import org.jsoup.parser.Parser import org.jsoup.parser.Parser
import rx.Observable import rx.Observable
import java.lang.UnsupportedOperationException import java.lang.UnsupportedOperationException
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Date
import java.util.Locale
import java.util.TimeZone
class MangaTaro : HttpSource() { class MangaTaro : HttpSource() {
@ -232,37 +237,55 @@ class MangaTaro : HttpSource() {
} }
override fun chapterListRequest(manga: SManga): Request { override fun chapterListRequest(manga: SManga): Request {
val (id, slug) = manga.url.parseAs<MangaUrl>() val timestamp = System.currentTimeMillis() / 1000
val token = md5(
"${timestamp}mng_ch_${isoDateFormatter.format(Date())}",
).substring(0, 16)
val mangaId = manga.url.parseAs<MangaUrl>().id
return GET("$baseUrl/manga/$slug#$id", headers) val url = "$baseUrl/auth/manga-chapters".toHttpUrl().newBuilder().apply {
addQueryParameter("manga_id", mangaId)
addQueryParameter("offset", "0")
addQueryParameter("limit", "9999")
addQueryParameter("order", "DESC")
addQueryParameter("_t", token)
addQueryParameter("_ts", timestamp.toString())
}.build()
return GET(url, headers)
} }
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
countViews(response.request.url.fragment!!) countViews(response.request.url.queryParameter("manga_id")!!)
val document = response.asJsoup() val data = response.parseAs<ChapterList>()
val placeholders = listOf("", "N/A", "")
val placeholders = listOf(null, "", "N/A", "")
var hasScanlator = false var hasScanlator = false
val chapters = document.select(".chapter-list a").map { // currently there is only English chapters on the site, at least from a quick look.
// if they ever have multiple languages, we would need to make this a source factory
val chapters = data.chapters.filter {
it.language == "en"
}.map {
SChapter.create().apply { SChapter.create().apply {
setUrlWithoutDomain(it.absUrl("href")) setUrlWithoutDomain(it.url)
val details = it.select("> div + div > div")
name = buildString { name = buildString {
append(it.attr("title")) append("Chapter ")
details[1].text().also { title -> append(it.chapter)
it.title.also { title ->
if (title !in placeholders) { if (title !in placeholders) {
append(": ", title) append(": ", title)
} }
} }
} }
it.attr("data-group-name").let { group -> it.groupName.let { group ->
if (group !in placeholders) { if (group !in placeholders) {
scanlator = group scanlator = group
hasScanlator = true hasScanlator = true
} }
} }
date_upload = details[3].text().parseRelativeDate() date_upload = it.date.parseRelativeDate()
} }
} }
@ -285,6 +308,16 @@ class MangaTaro : HttpSource() {
} }
} }
fun md5(input: String): String {
val md = MessageDigest.getInstance("MD5")
val digest = md.digest(input.toByteArray())
return digest.joinToString("") { "%02x".format(it) }
}
private val isoDateFormatter = SimpleDateFormat("yyyyMMddHH", Locale.US).apply {
timeZone = TimeZone.getTimeZone("UTC")
}
private fun String.toSlug() = toHttpUrl().let { url -> private fun String.toSlug() = toHttpUrl().let { url ->
val path = url.pathSegments.filter(String::isNotBlank) val path = url.pathSegments.filter(String::isNotBlank)
@ -301,17 +334,19 @@ class MangaTaro : HttpSource() {
?: return 0L ?: return 0L
when (unit) { when (unit) {
"h" -> calendar.add(Calendar.HOUR, -amount.toInt()) "second" -> calendar.add(Calendar.SECOND, -amount.toInt())
"d" -> calendar.add(Calendar.DAY_OF_YEAR, -amount.toInt()) "minute" -> calendar.add(Calendar.MINUTE, -amount.toInt())
"w" -> calendar.add(Calendar.WEEK_OF_YEAR, -amount.toInt()) "hour" -> calendar.add(Calendar.HOUR, -amount.toInt())
"mo" -> calendar.add(Calendar.MONTH, -amount.toInt()) "day" -> calendar.add(Calendar.DAY_OF_YEAR, -amount.toInt())
"y" -> calendar.add(Calendar.YEAR, -amount.toInt()) "week" -> calendar.add(Calendar.WEEK_OF_YEAR, -amount.toInt())
"month" -> calendar.add(Calendar.MONTH, -amount.toInt())
"year" -> calendar.add(Calendar.YEAR, -amount.toInt())
} }
return calendar.timeInMillis return calendar.timeInMillis
} }
private val relativeDateRegex = Regex("""(\d+)(h|d|w|mo|y) ago""") private val relativeDateRegex = Regex("""(\d+)\s+(second|minute|hour|day|week|month|year)s?\s+ago""")
private fun countViews(postId: String) { private fun countViews(postId: String) {
val payload = """{"post_id":"$postId"}""" val payload = """{"post_id":"$postId"}"""