From 219ceaac1ee01c406c31428f334f50582e1b14f7 Mon Sep 17 00:00:00 2001 From: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> Date: Thu, 17 Apr 2025 18:57:14 +0500 Subject: [PATCH] MangaPark: fixes & improvements (#8483) * cover absolute url & uncensored cover for hentai * utils * nsfw pref and thumbnail baseurl * lint * try upload status when original status is unknown * include extra info in description * off by default * bump * clean title * nullable * status set using nullability * review changes * revert * actually set to off --- src/all/mangapark/build.gradle | 2 +- .../extension/all/mangapark/MangaPark.kt | 89 ++++++++++++++----- .../extension/all/mangapark/MangaParkDto.kt | 79 ++++++++++++---- .../all/mangapark/MangaParkQueries.kt | 30 +++++++ 4 files changed, 160 insertions(+), 40 deletions(-) diff --git a/src/all/mangapark/build.gradle b/src/all/mangapark/build.gradle index 7fe709ec3..f8b425e46 100644 --- a/src/all/mangapark/build.gradle +++ b/src/all/mangapark/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'MangaPark' extClass = '.MangaParkFactory' - extVersionCode = 21 + extVersionCode = 22 isNsfw = true } diff --git a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaPark.kt b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaPark.kt index b4674b7d0..09a52a37d 100644 --- a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaPark.kt +++ b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaPark.kt @@ -17,20 +17,19 @@ 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 keiyoushi.utils.firstInstanceOrNull import keiyoushi.utils.getPreferences +import keiyoushi.utils.parseAs +import keiyoushi.utils.toJsonString 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 import okhttp3.Response -import uy.kohesive.injekt.injectLazy import java.util.concurrent.CountDownLatch import java.util.concurrent.atomic.AtomicBoolean @@ -54,13 +53,15 @@ class MangaPark( private val apiUrl = "$baseUrl/apo/" - 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() + override val client = network.cloudflareClient.newBuilder().apply { + if (preference.getBoolean(ENABLE_NSFW, true)) { + addInterceptor(::siteSettingsInterceptor) + addNetworkInterceptor(CookieInterceptor(domain, "nsfw" to "2")) + } + rateLimitHost(apiUrl.toHttpUrl(), 1) + // intentionally after rate limit interceptor so thumbnails are not rate limited + addInterceptor(::thumbnailDomainInterceptor) + }.build() override fun headersBuilder() = super.headersBuilder() .set("Referer", "$baseUrl/") @@ -96,8 +97,10 @@ class MangaPark( override fun searchMangaParse(response: Response): MangasPage { val result = response.parseAs() + val pageAsCover = preference.getString(UNCENSORED_COVER_PREF, "off")!! + val shortenTitle = preference.getBoolean(SHORTEN_TITLE_PREF, false) - val entries = result.data.searchComics.items.map { it.data.toSManga() } + val entries = result.data.searchComics.items.map { it.data.toSManga(shortenTitle, pageAsCover) } val hasNextPage = entries.size == size return MangasPage(entries, hasNextPage) @@ -164,8 +167,10 @@ class MangaPark( override fun mangaDetailsParse(response: Response): SManga { val result = response.parseAs() + val pageAsCover = preference.getString(UNCENSORED_COVER_PREF, "off")!! + val shortenTitle = preference.getBoolean(SHORTEN_TITLE_PREF, false) - return result.data.comic.data.toSManga() + return result.data.comic.data.toSManga(shortenTitle, pageAsCover) } override fun getMangaUrl(manga: SManga) = baseUrl + manga.url.substringBeforeLast("#") @@ -220,7 +225,7 @@ class MangaPark( summary = "%s" setOnPreferenceChangeListener { _, _ -> - Toast.makeText(screen.context, "Restart Tachiyomi to apply changes", Toast.LENGTH_LONG).show() + Toast.makeText(screen.context, "Restart the app to apply changes", Toast.LENGTH_LONG).show() true } }.also(screen::addPreference) @@ -231,16 +236,34 @@ class MangaPark( summary = "Refresh chapter list to apply changes" setDefaultValue(false) }.also(screen::addPreference) + + SwitchPreferenceCompat(screen.context).apply { + key = ENABLE_NSFW + title = "Enable NSFW content" + summary = "Clear Cookies & Restart the app to apply changes." + setDefaultValue(true) + }.also(screen::addPreference) + + SwitchPreferenceCompat(screen.context).apply { + key = SHORTEN_TITLE_PREF + title = "Remove extra information from title" + summary = "Clear database to apply changes\n\n" + + "Note: doesn't not work for entries in library" + setDefaultValue(false) + }.also(screen::addPreference) + + ListPreference(screen.context).apply { + key = UNCENSORED_COVER_PREF + title = "Attempt to use Uncensored Cover for Hentai" + summary = "Uses first or last chapter page as cover" + entries = arrayOf("Off", "First Chapter", "Last Chapter") + entryValues = arrayOf("off", "first", "last") + setDefaultValue("off") + }.also(screen::addPreference) } - private inline fun Response.parseAs(): T = - use { body.string() }.let(json::decodeFromString) - - private inline fun List<*>.firstInstanceOrNull(): T? = - filterIsInstance().firstOrNull() - private inline fun T.toJsonRequestBody() = - json.encodeToString(this).toRequestBody(JSON_MEDIA_TYPE) + toJsonString().toRequestBody(JSON_MEDIA_TYPE) private val cookiesNotSet = AtomicBoolean(true) private val latch = CountDownLatch(1) @@ -271,6 +294,25 @@ class MangaPark( return chain.proceed(request) } + private fun thumbnailDomainInterceptor(chain: Interceptor.Chain): Response { + val request = chain.request() + val url = request.url + + return if (url.host == THUMBNAIL_LOOPBACK_HOST) { + val newUrl = url.newBuilder() + .host(domain) + .build() + + val newRequest = request.newBuilder() + .url(newUrl) + .build() + + chain.proceed(newRequest) + } else { + chain.proceed(request) + } + } + override fun imageUrlParse(response: Response): String { throw UnsupportedOperationException() } @@ -298,6 +340,11 @@ class MangaPark( "mpark.to", ) + private const val ENABLE_NSFW = "pref_nsfw" private const val DUPLICATE_CHAPTER_PREF_KEY = "pref_dup_chapters" + private const val SHORTEN_TITLE_PREF = "pref_shorten_title" + private const val UNCENSORED_COVER_PREF = "pref_uncensored_cover" } } + +const val THUMBNAIL_LOOPBACK_HOST = "127.0.0.1" diff --git a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkDto.kt b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkDto.kt index 5ef81ada2..100c8a85c 100644 --- a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkDto.kt +++ b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkDto.kt @@ -38,33 +38,68 @@ class MangaParkComic( private val originalStatus: String? = null, private val uploadStatus: String? = null, private val summary: String? = null, + private val extraInfo: String? = null, @SerialName("urlCoverOri") private val cover: String? = null, private val urlPath: String, + @SerialName("max_chapterNode") private val latestChapter: Data? = null, + @SerialName("first_chapterNode") private val firstChapter: Data? = null, ) { - fun toSManga() = SManga.create().apply { + fun toSManga(shortenTitle: Boolean, pageAsCover: String) = SManga.create().apply { url = "$urlPath#$id" - title = name - thumbnail_url = cover + title = if (shortenTitle) { + var shortName = name + while (shortenTitleRegex.containsMatchIn(shortName)) { + shortName = shortName.replace(shortenTitleRegex, "").trim() + } + + shortName + } else { + name + } + thumbnail_url = run { + val coverUrl = cover?.let { + when { + it.startsWith("http") -> it + it.startsWith("/") -> "https://$THUMBNAIL_LOOPBACK_HOST$it" + else -> null + } + } + + if (pageAsCover != "off" && useLatestPageAsCover(genres)) { + if (pageAsCover == "first") { + firstChapter?.data?.imageFile?.urlList?.firstOrNull() ?: coverUrl + } else { + latestChapter?.data?.imageFile?.urlList?.firstOrNull() ?: coverUrl + } + } else { + coverUrl + } + } author = authors?.joinToString() artist = artists?.joinToString() description = buildString { - val desc = summary?.let { Jsoup.parse(it).text() } - val names = altNames?.takeUnless { it.isEmpty() } - ?.joinToString("\n") { "• ${it.trim()}" } - - if (desc.isNullOrEmpty()) { - if (!names.isNullOrEmpty()) { - append("Alternative Names:\n", names) - } - } else { - append(desc) - if (!names.isNullOrEmpty()) { - append("\n\nAlternative Names:\n", names) - } + if (shortenTitle) { + append(name) + append("\n\n") } - } + summary?.also { + append(Jsoup.parse(it).wholeText().trim()) + append("\n\n") + } + extraInfo?.takeUnless(String::isBlank)?.also { + append("Extra Info:\n") + append(Jsoup.parse(it).wholeText().trim()) + append("\n\n") + } + altNames?.takeUnless(List::isEmpty) + ?.joinToString( + prefix = "Alternative Names:\n", + separator = "\n", + ) { "• ${it.trim()}" } + ?.also(::append) + }.trim() genre = genres?.joinToString { it.replace("_", " ").toCamelCase() } - status = when (originalStatus) { + status = when (originalStatus ?: uploadStatus) { "ongoing" -> SManga.ONGOING "completed" -> { if (uploadStatus == "ongoing") { @@ -96,6 +131,14 @@ class MangaParkComic( } return result.toString() } + + private fun useLatestPageAsCover(genres: List?): Boolean { + return genres.orEmpty().let { + it.contains("hentai") && !it.contains("webtoon") + } + } + + private val shortenTitleRegex = Regex("""^(\[[^]]+\])|^(\([^)]+\))|^(\{[^}]+\})|(\[[^]]+\])${'$'}|(\([^)]+\))${'$'}|(\{[^}]+\})${'$'}""") } } diff --git a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkQueries.kt b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkQueries.kt index d303e9fc6..a59e3c8f7 100644 --- a/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkQueries.kt +++ b/src/all/mangapark/src/eu/kanade/tachiyomi/extension/all/mangapark/MangaParkQueries.kt @@ -25,8 +25,23 @@ val SEARCH_QUERY = buildQuery { originalStatus uploadStatus summary + extraInfo urlCoverOri urlPath + max_chapterNode { + data { + imageFile { + urlList + } + } + } + first_chapterNode { + data { + imageFile { + urlList + } + } + } } } } @@ -52,8 +67,23 @@ val DETAILS_QUERY = buildQuery { originalStatus uploadStatus summary + extraInfo urlCoverOri urlPath + max_chapterNode { + data { + imageFile { + urlList + } + } + } + first_chapterNode { + data { + imageFile { + urlList + } + } + } } } }