fix(zh/happymh): Fix http 403 in page lists (#19470)

* fix: Fix page list - Set API version

* refactor: Use serializable data classes

* chore: Bump version
This commit is contained in:
Claudemirovsky 2023-12-29 12:20:09 -03:00 committed by GitHub
parent 4f22d3f064
commit 5c71163ef6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 75 additions and 26 deletions

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Happymh'
pkgNameSuffix = 'zh.happymh'
extClass = '.Happymh'
extVersionCode = 5
extVersionCode = 6
}
apply from: "$rootDir/common.gradle"

View File

@ -4,6 +4,9 @@ import android.app.Application
import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.extension.zh.happymh.dto.ChapterListDto
import eu.kanade.tachiyomi.extension.zh.happymh.dto.PageListResponseDto
import eu.kanade.tachiyomi.extension.zh.happymh.dto.PopularResponseDto
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.ConfigurableSource
@ -14,12 +17,9 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.decodeFromStream
import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.OkHttpClient
@ -37,9 +37,12 @@ class Happymh : HttpSource(), ConfigurableSource {
override val client: OkHttpClient = network.cloudflareClient
private val json: Json by injectLazy()
private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override fun headersBuilder(): Headers.Builder {
val builder = super.headersBuilder()
val preferences = Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
val userAgent = preferences.getString(USER_AGENT_PREF, "")!!
return if (userAgent.isNotBlank()) {
builder.set("User-Agent", userAgent)
@ -57,17 +60,18 @@ class Happymh : HttpSource(), ConfigurableSource {
}
override fun popularMangaParse(response: Response): MangasPage {
val data = json.parseToJsonElement(response.body.string()).jsonObject["data"]!!.jsonObject
val items = data["items"]!!.jsonArray.map {
val data = response.parseAs<PopularResponseDto>().data
val items = data.items.map {
SManga.create().apply {
val obj = it.jsonObject
title = obj["name"]!!.jsonPrimitive.content
url = "/manga/${obj["manga_code"]!!.jsonPrimitive.content}"
thumbnail_url = obj["cover"]!!.jsonPrimitive.content
title = it.name
url = it.url
thumbnail_url = it.cover
}
}
val isEnd = data["isEnd"]!!.jsonPrimitive.boolean
return MangasPage(items, !isEnd)
val hasNextPage = data.isEnd.not()
return MangasPage(items, hasNextPage)
}
// Latest
@ -108,12 +112,12 @@ class Happymh : HttpSource(), ConfigurableSource {
override fun chapterListParse(response: Response): List<SChapter> {
val comicId = response.request.url.pathSegments.last()
val document = response.asJsoup()
val script = document.selectFirst("mip-data > script:containsData(chapterList)")!!.html()
return json.parseToJsonElement(script).jsonObject["chapterList"]!!.jsonArray.map {
val script = document.selectFirst("mip-data > script:containsData(chapterList)")!!.data()
return json.decodeFromString<ChapterListDto>(script).chapterList.map {
SChapter.create().apply {
val chapterId = it.jsonObject["id"]!!.jsonPrimitive.content
url = "/v2.0/apis/manga/read?code=$comicId&cid=$chapterId"
name = it.jsonObject["chapterName"]!!.jsonPrimitive.content
val chapterId = it.id
url = "/v2.0/apis/manga/read?code=$comicId&cid=$chapterId&v=v2.13"
name = it.chapterName
}
}
}
@ -121,18 +125,21 @@ class Happymh : HttpSource(), ConfigurableSource {
// Pages
override fun pageListRequest(chapter: SChapter): Request {
val url = baseUrl + chapter.url
// Some chapters return 403 without this header
val header = headersBuilder().add("X-Requested-With", "XMLHttpRequest").build()
return GET(baseUrl + chapter.url, header)
val header = headersBuilder()
.add("X-Requested-With", "XMLHttpRequest")
.set("Referer", url)
.build()
return GET(url, header)
}
override fun pageListParse(response: Response): List<Page> {
return json.parseToJsonElement(response.body.string())
.jsonObject["data"]!!.jsonObject["scans"]!!.jsonArray
return response.parseAs<PageListResponseDto>().data.scans
// If n == 1, the image is from next chapter
.filter { it.jsonObject["n"]!!.jsonPrimitive.int == 0 }
.filter { it.n == 0 }
.mapIndexed { index, it ->
Page(index, "", it.jsonObject["url"]!!.jsonPrimitive.content)
Page(index, "", it.url)
}
}
@ -158,6 +165,10 @@ class Happymh : HttpSource(), ConfigurableSource {
}.let(screen::addPreference)
}
private inline fun <reified T> Response.parseAs(): T = use {
json.decodeFromStream(it.body.byteStream())
}
companion object {
private const val USER_AGENT_PREF = "userAgent"
}

View File

@ -0,0 +1,37 @@
package eu.kanade.tachiyomi.extension.zh.happymh.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
// Popular / Latest / Search pages
@Serializable
data class PopularResponseDto(val data: PopularData)
@Serializable
data class PopularData(val items: List<MangaDto>, val isEnd: Boolean)
@Serializable
data class MangaDto(
val name: String,
@SerialName("manga_code") val code: String,
val cover: String,
) {
val url = "/manga/$code"
}
// Chapters
@Serializable
data class ChapterListDto(val chapterList: List<ChapterDto>) {
@Serializable
data class ChapterDto(val id: String, val chapterName: String)
}
// Pages
@Serializable
data class PageListResponseDto(val data: PageListData)
@Serializable
data class PageListData(val scans: List<PageDto>) {
@Serializable
data class PageDto(val n: Int, val url: String)
}