Zaimanhua: fix image url expire problem & incorrect chapter upload time (#5455)
* Zaimanhua: fix incorrect chapter upload time For some chapters, api will always return current time as upload time. * Zaimanhua: fix image url expire problem Store params in the fragment of the imageUrl, re-fetch the image URL if loading fails
This commit is contained in:
parent
86c7d2e64b
commit
d1b75272d0
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Zaimanhua'
|
extName = 'Zaimanhua'
|
||||||
extClass = '.Zaimanhua'
|
extClass = '.Zaimanhua'
|
||||||
extVersionCode = 1
|
extVersionCode = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -15,6 +15,10 @@ import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.CacheControl
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
|
@ -24,9 +28,13 @@ import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
import rx.Observable
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import java.io.IOException
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class Zaimanhua : HttpSource(), ConfigurableSource {
|
class Zaimanhua : HttpSource(), ConfigurableSource {
|
||||||
override val lang = "zh"
|
override val lang = "zh"
|
||||||
|
@ -37,11 +45,16 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
|
||||||
private val apiUrl = "https://v4api.zaimanhua.com/app/v1"
|
private val apiUrl = "https://v4api.zaimanhua.com/app/v1"
|
||||||
private val accountApiUrl = "https://account-api.zaimanhua.com/v1"
|
private val accountApiUrl = "https://account-api.zaimanhua.com/v1"
|
||||||
|
|
||||||
|
private val json by injectLazy<Json>()
|
||||||
|
|
||||||
private val preferences: SharedPreferences =
|
private val preferences: SharedPreferences =
|
||||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
|
|
||||||
override val client: OkHttpClient =
|
override val client: OkHttpClient = network.client.newBuilder()
|
||||||
network.client.newBuilder().rateLimit(5).addInterceptor(::authIntercept).build()
|
.rateLimit(5)
|
||||||
|
.addInterceptor(::authIntercept)
|
||||||
|
.addInterceptor(::imageRetryInterceptor)
|
||||||
|
.build()
|
||||||
|
|
||||||
private fun authIntercept(chain: Interceptor.Chain): Response {
|
private fun authIntercept(chain: Interceptor.Chain): Response {
|
||||||
val request = chain.request()
|
val request = chain.request()
|
||||||
|
@ -129,24 +142,48 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
|
|
||||||
|
|
||||||
// PageList
|
// PageList
|
||||||
// path: "/comic/chapter/mangaId/chapterId"
|
// path: "/comic/chapter/mangaId/chapterId"
|
||||||
override fun pageListRequest(chapter: SChapter) =
|
private fun pageListApiRequest(path: String): Request =
|
||||||
GET("$apiUrl/comic/chapter/${chapter.url}", apiHeaders)
|
GET("$apiUrl/comic/chapter/$path", apiHeaders, USE_CACHE)
|
||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
override fun pageListParse(response: Response): List<Page> = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
||||||
|
val response = client.newCall(pageListApiRequest(chapter.url)).execute()
|
||||||
val result = response.parseAs<ResponseDto<DataWrapperDto<ChapterImagesDto>>>()
|
val result = response.parseAs<ResponseDto<DataWrapperDto<ChapterImagesDto>>>()
|
||||||
if (result.errmsg.isNotBlank()) {
|
if (result.errmsg.isNotBlank()) {
|
||||||
throw Exception(result.errmsg)
|
throw Exception(result.errmsg)
|
||||||
} else {
|
} else {
|
||||||
return result.data.data!!.images.mapIndexed { index, it ->
|
return Observable.just(
|
||||||
Page(index, imageUrl = it)
|
result.data.data!!.images.mapIndexed { index, it ->
|
||||||
}
|
val fragment = json.encodeToString(ImageRetryParamsDto(chapter.url, index))
|
||||||
|
Page(index, imageUrl = "$it#$fragment")
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun imageRetryInterceptor(chain: Interceptor.Chain): Response {
|
||||||
|
val request = chain.request()
|
||||||
|
val response = chain.proceed(request)
|
||||||
|
val fragment = request.url.fragment
|
||||||
|
if (response.isSuccessful || request.url.host != "images.zaimanhua.com" || fragment == null) return response
|
||||||
|
response.close()
|
||||||
|
|
||||||
|
val params = json.decodeFromString<ImageRetryParamsDto>(fragment)
|
||||||
|
val pageListResponse = client.newCall(pageListApiRequest(params.url)).execute()
|
||||||
|
val result = pageListResponse.parseAs<ResponseDto<DataWrapperDto<ChapterImagesDto>>>()
|
||||||
|
if (result.errmsg.isNotBlank()) {
|
||||||
|
throw IOException(result.errmsg)
|
||||||
|
} else {
|
||||||
|
val imageUrl = result.data.data!!.images[params.index]
|
||||||
|
return chain.proceed(GET(imageUrl, headers))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
|
||||||
|
|
||||||
// Popular
|
// Popular
|
||||||
private fun rankApiUrl(): HttpUrl.Builder =
|
private fun rankApiUrl(): HttpUrl.Builder =
|
||||||
"$apiUrl/comic/rank/list".toHttpUrl().newBuilder().addQueryParameter("by_time", "3")
|
"$apiUrl/comic/rank/list".toHttpUrl().newBuilder().addQueryParameter("by_time", "3")
|
||||||
|
@ -187,6 +224,9 @@ class Zaimanhua : HttpSource(), ConfigurableSource {
|
||||||
return MangasPage(mangas.map { it.toSManga() }, true)
|
return MangasPage(mangas.map { it.toSManga() }, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val USE_CACHE = CacheControl.Builder().maxStale(170, TimeUnit.SECONDS).build()
|
||||||
|
}
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
ListPreference(screen.context).apply {
|
ListPreference(screen.context).apply {
|
||||||
EditTextPreference(screen.context).apply {
|
EditTextPreference(screen.context).apply {
|
||||||
|
|
|
@ -47,10 +47,14 @@ class ChapterGroupDto(
|
||||||
fun toSChapterList(mangaId: String): List<SChapter> {
|
fun toSChapterList(mangaId: String): List<SChapter> {
|
||||||
val groupName = title
|
val groupName = title
|
||||||
val isDefaultGroup = groupName == "连载"
|
val isDefaultGroup = groupName == "连载"
|
||||||
|
val current = System.currentTimeMillis()
|
||||||
return data.map {
|
return data.map {
|
||||||
it.toSChapterInternal().apply {
|
it.toSChapterInternal().apply {
|
||||||
url = "$mangaId/$url"
|
url = "$mangaId/$url"
|
||||||
if (!isDefaultGroup) scanlator = groupName
|
if (!isDefaultGroup) scanlator = groupName
|
||||||
|
// For some chapters, api will always return current time as upload time
|
||||||
|
// Therefore upload times that differ too little from the current time will be ignored
|
||||||
|
if ((current - date_upload) < 10000) date_upload = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,3 +146,9 @@ class ResponseDto<T>(
|
||||||
val errmsg: String = "",
|
val errmsg: String = "",
|
||||||
val data: T,
|
val data: T,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ImageRetryParamsDto(
|
||||||
|
val url: String,
|
||||||
|
val index: Int,
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue