Mangapark: duplicate chapters & unblock site blocked genres (#3979)
* fetch duplicate chapters + small refactor => data class -> class * site settings to not block "Hentai" genre * user name as scanlator for first choice * forgor * try not to make duplicate calls concurrency my beloved * move fetch genre call to `getFilterList`
This commit is contained in:
parent
33afc95944
commit
d11495d8a1
@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'MangaPark'
|
||||
extClass = '.MangaParkFactory'
|
||||
extVersionCode = 19
|
||||
extVersionCode = 20
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import android.app.Application
|
||||
import android.widget.Toast
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import eu.kanade.tachiyomi.lib.cookieinterceptor.CookieInterceptor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
@ -17,10 +18,14 @@ 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.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
@ -28,6 +33,8 @@ import okhttp3.Response
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
class MangaPark(
|
||||
override val lang: String,
|
||||
@ -53,6 +60,7 @@ class MangaPark(
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override val client = network.cloudflareClient.newBuilder()
|
||||
.addInterceptor(::siteSettingsInterceptor)
|
||||
.addNetworkInterceptor(CookieInterceptor(domain, "nsfw" to "2"))
|
||||
.rateLimitHost(apiUrl.toHttpUrl(), 1)
|
||||
.build()
|
||||
@ -90,8 +98,6 @@ class MangaPark(
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
runCatching(::getGenres)
|
||||
|
||||
val result = response.parseAs<SearchResponse>()
|
||||
|
||||
val entries = result.data.searchComics.items.map { it.data.toSManga() }
|
||||
@ -126,6 +132,10 @@ class MangaPark(
|
||||
}
|
||||
|
||||
override fun getFilterList(): FilterList {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
runCatching(::getGenres)
|
||||
}
|
||||
|
||||
val filters = mutableListOf<Filter<*>>(
|
||||
SortFilter(),
|
||||
OriginalLanguageFilter(),
|
||||
@ -175,7 +185,13 @@ class MangaPark(
|
||||
override fun chapterListParse(response: Response): List<SChapter> {
|
||||
val result = response.parseAs<ChapterListResponse>()
|
||||
|
||||
return result.data.chapterList.map { it.data.toSChapter() }.reversed()
|
||||
return if (preference.getBoolean(DUPLICATE_CHAPTER_PREF_KEY, false)) {
|
||||
result.data.chapterList.flatMap {
|
||||
it.data.dupChapters.map { it.data.toSChapter() }
|
||||
}.reversed()
|
||||
} else {
|
||||
result.data.chapterList.map { it.data.toSChapter() }.reversed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url.substringBeforeLast("#")
|
||||
@ -211,6 +227,13 @@ class MangaPark(
|
||||
true
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
|
||||
SwitchPreferenceCompat(screen.context).apply {
|
||||
key = DUPLICATE_CHAPTER_PREF_KEY
|
||||
title = "Fetch Duplicate Chapters"
|
||||
summary = "Refresh chapter list to apply changes"
|
||||
setDefaultValue(false)
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
|
||||
private inline fun <reified T> Response.parseAs(): T =
|
||||
@ -222,6 +245,35 @@ class MangaPark(
|
||||
private inline fun <reified T : Any> T.toJsonRequestBody() =
|
||||
json.encodeToString(this).toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
private val cookiesNotSet = AtomicBoolean(true)
|
||||
private val latch = CountDownLatch(1)
|
||||
|
||||
// sets necessary cookies to not block genres like `Hentai`
|
||||
private fun siteSettingsInterceptor(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
|
||||
val settingsUrl = "$baseUrl/aok/settings-save"
|
||||
|
||||
if (
|
||||
request.url.toString() != settingsUrl &&
|
||||
request.url.host == domain
|
||||
) {
|
||||
if (cookiesNotSet.getAndSet(false)) {
|
||||
val payload =
|
||||
"""{"data":{"general_autoLangs":[],"general_userLangs":[],"general_excGenres":[],"general_prefLangs":[]}}"""
|
||||
.toRequestBody(JSON_MEDIA_TYPE)
|
||||
|
||||
client.newCall(POST(settingsUrl, headers, payload)).execute().close()
|
||||
|
||||
latch.countDown()
|
||||
} else {
|
||||
latch.await()
|
||||
}
|
||||
}
|
||||
|
||||
return chain.proceed(request)
|
||||
}
|
||||
|
||||
override fun imageUrlParse(response: Response): String {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
@ -248,5 +300,7 @@ class MangaPark(
|
||||
"parkmanga.org",
|
||||
"mpark.to",
|
||||
)
|
||||
|
||||
private const val DUPLICATE_CHAPTER_PREF_KEY = "pref_dup_chapters"
|
||||
}
|
||||
}
|
||||
|
@ -12,34 +12,34 @@ typealias ChapterListResponse = Data<ChapterList>
|
||||
typealias PageListResponse = Data<ChapterPages>
|
||||
|
||||
@Serializable
|
||||
data class Data<T>(val data: T)
|
||||
class Data<T>(val data: T)
|
||||
|
||||
@Serializable
|
||||
data class Items<T>(val items: List<T>)
|
||||
class Items<T>(val items: List<T>)
|
||||
|
||||
@Serializable
|
||||
data class SearchComics(
|
||||
class SearchComics(
|
||||
@SerialName("get_searchComic") val searchComics: Items<Data<MangaParkComic>>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ComicNode(
|
||||
class ComicNode(
|
||||
@SerialName("get_comicNode") val comic: Data<MangaParkComic>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MangaParkComic(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val altNames: List<String>? = null,
|
||||
val authors: List<String>? = null,
|
||||
val artists: List<String>? = null,
|
||||
val genres: List<String>? = null,
|
||||
val originalStatus: String? = null,
|
||||
val uploadStatus: String? = null,
|
||||
val summary: String? = null,
|
||||
@SerialName("urlCoverOri") val cover: String? = null,
|
||||
val urlPath: String,
|
||||
class MangaParkComic(
|
||||
private val id: String,
|
||||
private val name: String,
|
||||
private val altNames: List<String>? = null,
|
||||
private val authors: List<String>? = null,
|
||||
private val artists: List<String>? = null,
|
||||
private val genres: List<String>? = null,
|
||||
private val originalStatus: String? = null,
|
||||
private val uploadStatus: String? = null,
|
||||
private val summary: String? = null,
|
||||
@SerialName("urlCoverOri") private val cover: String? = null,
|
||||
private val urlPath: String,
|
||||
) {
|
||||
fun toSManga() = SManga.create().apply {
|
||||
url = "$urlPath#$id"
|
||||
@ -100,18 +100,21 @@ data class MangaParkComic(
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ChapterList(
|
||||
class ChapterList(
|
||||
@SerialName("get_comicChapterList") val chapterList: List<Data<MangaParkChapter>>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MangaParkChapter(
|
||||
val id: String,
|
||||
@SerialName("dname") val displayName: String,
|
||||
val title: String? = null,
|
||||
val dateCreate: Long? = null,
|
||||
val dateModify: Long? = null,
|
||||
val urlPath: String,
|
||||
class MangaParkChapter(
|
||||
private val id: String,
|
||||
@SerialName("dname") private val displayName: String,
|
||||
private val title: String? = null,
|
||||
private val dateCreate: Long? = null,
|
||||
private val dateModify: Long? = null,
|
||||
private val urlPath: String,
|
||||
private val srcTitle: String? = null,
|
||||
private val userNode: Data<Name>? = null,
|
||||
val dupChapters: List<Data<MangaParkChapter>> = emptyList(),
|
||||
) {
|
||||
fun toSChapter() = SChapter.create().apply {
|
||||
url = "$urlPath#$id"
|
||||
@ -120,20 +123,24 @@ data class MangaParkChapter(
|
||||
title?.let { append(": ", it) }
|
||||
}
|
||||
date_upload = dateModify ?: dateCreate ?: 0L
|
||||
scanlator = userNode?.data?.name ?: srcTitle ?: "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ChapterPages(
|
||||
class Name(val name: String)
|
||||
|
||||
@Serializable
|
||||
class ChapterPages(
|
||||
@SerialName("get_chapterNode") val chapterPages: Data<ImageFiles>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ImageFiles(
|
||||
class ImageFiles(
|
||||
val imageFile: UrlList,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class UrlList(
|
||||
class UrlList(
|
||||
val urlList: List<String>,
|
||||
)
|
||||
|
@ -4,28 +4,28 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class GraphQL<T>(
|
||||
val variables: T,
|
||||
val query: String,
|
||||
class GraphQL<T>(
|
||||
private val variables: T,
|
||||
private val query: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class SearchVariables(val select: SearchPayload)
|
||||
class SearchVariables(private val select: SearchPayload)
|
||||
|
||||
@Serializable
|
||||
data class SearchPayload(
|
||||
@SerialName("word") val query: String? = null,
|
||||
val incGenres: List<String>? = null,
|
||||
val excGenres: List<String>? = null,
|
||||
val incTLangs: List<String>? = null,
|
||||
val incOLangs: List<String>? = null,
|
||||
val sortby: String? = null,
|
||||
val chapCount: String? = null,
|
||||
val origStatus: String? = null,
|
||||
val siteStatus: String? = null,
|
||||
val page: Int,
|
||||
val size: Int,
|
||||
class SearchPayload(
|
||||
@SerialName("word") private val query: String? = null,
|
||||
private val incGenres: List<String>? = null,
|
||||
private val excGenres: List<String>? = null,
|
||||
private val incTLangs: List<String>? = null,
|
||||
private val incOLangs: List<String>? = null,
|
||||
private val sortby: String? = null,
|
||||
private val chapCount: String? = null,
|
||||
private val origStatus: String? = null,
|
||||
private val siteStatus: String? = null,
|
||||
private val page: Int,
|
||||
private val size: Int,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class IdVariables(val id: String)
|
||||
class IdVariables(private val id: String)
|
||||
|
@ -75,6 +75,28 @@ val CHAPTERS_QUERY = buildQuery {
|
||||
dateModify
|
||||
dateCreate
|
||||
urlPath
|
||||
srcTitle
|
||||
userNode {
|
||||
data {
|
||||
name
|
||||
}
|
||||
}
|
||||
dupChapters {
|
||||
data {
|
||||
id
|
||||
dname
|
||||
title
|
||||
dateModify
|
||||
dateCreate
|
||||
urlPath
|
||||
srcTitle
|
||||
userNode {
|
||||
data {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user