From f03fd3c5f787b6f277a5584b035ca8df17395225 Mon Sep 17 00:00:00 2001 From: Vetle Ledaal Date: Thu, 19 Dec 2024 12:45:29 +0100 Subject: [PATCH] Asura Scans: support high quality chapter images (#6657) * Asura Scans: support high quality chapter images * Only rewrite chapter images, add fallback if broken - explained in ext settings --- src/en/asurascans/build.gradle | 2 +- .../extension/en/asurascans/AsuraScans.kt | 56 ++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/en/asurascans/build.gradle b/src/en/asurascans/build.gradle index ec4cc84f2..ae67f844f 100644 --- a/src/en/asurascans/build.gradle +++ b/src/en/asurascans/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Asura Scans' extClass = '.AsuraScans' - extVersionCode = 44 + extVersionCode = 45 } apply from: "$rootDir/common.gradle" diff --git a/src/en/asurascans/src/eu/kanade/tachiyomi/extension/en/asurascans/AsuraScans.kt b/src/en/asurascans/src/eu/kanade/tachiyomi/extension/en/asurascans/AsuraScans.kt index 22d038403..66890b20c 100644 --- a/src/en/asurascans/src/eu/kanade/tachiyomi/extension/en/asurascans/AsuraScans.kt +++ b/src/en/asurascans/src/eu/kanade/tachiyomi/extension/en/asurascans/AsuraScans.kt @@ -17,6 +17,8 @@ import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response import org.jsoup.nodes.Document @@ -66,9 +68,35 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource { private val json: Json by injectLazy() override val client = network.cloudflareClient.newBuilder() + .addInterceptor(::forceHighQualityInterceptor) .rateLimit(1, 3) .build() + private var failedHighQuality = false + + private fun forceHighQualityInterceptor(chain: Interceptor.Chain): Response { + val request = chain.request() + + if (preferences.forceHighQuality() && !failedHighQuality && request.url.fragment == "pageListParse") { + OPTIMIZED_IMAGE_PATH_REGEX.find(request.url.encodedPath)?.also { match -> + val (id, page) = match.destructured + val newUrl = request.url.newBuilder() + .encodedPath("/storage/media/$id/$page.webp") + .build() + + val response = chain.proceed(request.newBuilder().url(newUrl).build()) + if (response.code != 404) { + return response + } else { + failedHighQuality = true + response.close() + } + } + } + + return chain.proceed(request) + } + override fun headersBuilder() = super.headersBuilder() .add("Referer", "$baseUrl/") @@ -272,7 +300,16 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource { .joinToString("") { it.data().substringAfter("\"").substringBeforeLast("\"") } val pagesData = PAGES_REGEX.find(scriptData)?.groupValues?.get(1) ?: throw Exception("Failed to find chapter pages") val pageList = json.decodeFromString>(pagesData.unescape()).sortedBy { it.order } - return pageList.mapIndexed { i, page -> Page(i, imageUrl = page.url) } + return pageList.mapIndexed { i, page -> + val newUrl = page.url.toHttpUrlOrNull()?.run { + newBuilder() + .fragment("pageListParse") + .build() + .toString() + } + + Page(i, imageUrl = newUrl ?: page.url) + } } override fun imageUrlParse(document: Document) = throw UnsupportedOperationException() @@ -296,6 +333,16 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource { summary = "Hides the chapters that require a subscription to view" setDefaultValue(true) }.let(screen::addPreference) + + SwitchPreferenceCompat(screen.context).apply { + key = PREF_FORCE_HIGH_QUALITY + title = "Force high quality chapter images" + summary = "Attempt to use high quality chapter images.\nWill increase bandwidth by ~50%." + if (failedHighQuality) { + summary = "$summary\n*DISABLED* because of missing high quality images." + } + setDefaultValue(false) + }.let(screen::addPreference) } private var SharedPreferences.slugMap: MutableMap @@ -318,6 +365,10 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource { PREF_HIDE_PREMIUM_CHAPTERS, true, ) + private fun SharedPreferences.forceHighQuality(): Boolean = getBoolean( + PREF_FORCE_HIGH_QUALITY, + false, + ) private fun String.toPermSlugIfNeeded(): String { if (!preferences.dynamicUrl()) return this @@ -337,8 +388,11 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource { private val CLEAN_DATE_REGEX = """(\d+)(st|nd|rd|th)""".toRegex() private val OLD_FORMAT_MANGA_REGEX = """^/manga/(\d+-)?([^/]+)/?$""".toRegex() private val OLD_FORMAT_CHAPTER_REGEX = """^/(\d+-)?[^/]*-chapter-\d+(-\d+)*/?$""".toRegex() + private val OPTIMIZED_IMAGE_PATH_REGEX = """^/storage/media/(\d+)/conversions/(.*)-optimized\.webp$""".toRegex() + private const val PREF_SLUG_MAP = "pref_slug_map_2" private const val PREF_DYNAMIC_URL = "pref_dynamic_url" private const val PREF_HIDE_PREMIUM_CHAPTERS = "pref_hide_premium_chapters" + private const val PREF_FORCE_HIGH_QUALITY = "pref_force_high_quality" } }