Skip to content

Commit 4db5330

Browse files
authored
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
1 parent a8a30c4 commit 4db5330

File tree

2 files changed

+56
-2
lines changed

2 files changed

+56
-2
lines changed

src/en/asurascans/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
ext {
22
extName = 'Asura Scans'
33
extClass = '.AsuraScans'
4-
extVersionCode = 44
4+
extVersionCode = 45
55
}
66

77
apply from: "$rootDir/common.gradle"

src/en/asurascans/src/eu/kanade/tachiyomi/extension/en/asurascans/AsuraScans.kt

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import kotlinx.serialization.decodeFromString
1717
import kotlinx.serialization.encodeToString
1818
import kotlinx.serialization.json.Json
1919
import okhttp3.HttpUrl.Companion.toHttpUrl
20+
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
21+
import okhttp3.Interceptor
2022
import okhttp3.Request
2123
import okhttp3.Response
2224
import org.jsoup.nodes.Document
@@ -66,9 +68,35 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource {
6668
private val json: Json by injectLazy()
6769

6870
override val client = network.cloudflareClient.newBuilder()
71+
.addInterceptor(::forceHighQualityInterceptor)
6972
.rateLimit(1, 3)
7073
.build()
7174

75+
private var failedHighQuality = false
76+
77+
private fun forceHighQualityInterceptor(chain: Interceptor.Chain): Response {
78+
val request = chain.request()
79+
80+
if (preferences.forceHighQuality() && !failedHighQuality && request.url.fragment == "pageListParse") {
81+
OPTIMIZED_IMAGE_PATH_REGEX.find(request.url.encodedPath)?.also { match ->
82+
val (id, page) = match.destructured
83+
val newUrl = request.url.newBuilder()
84+
.encodedPath("/storage/media/$id/$page.webp")
85+
.build()
86+
87+
val response = chain.proceed(request.newBuilder().url(newUrl).build())
88+
if (response.code != 404) {
89+
return response
90+
} else {
91+
failedHighQuality = true
92+
response.close()
93+
}
94+
}
95+
}
96+
97+
return chain.proceed(request)
98+
}
99+
72100
override fun headersBuilder() = super.headersBuilder()
73101
.add("Referer", "$baseUrl/")
74102

@@ -272,7 +300,16 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource {
272300
.joinToString("") { it.data().substringAfter("\"").substringBeforeLast("\"") }
273301
val pagesData = PAGES_REGEX.find(scriptData)?.groupValues?.get(1) ?: throw Exception("Failed to find chapter pages")
274302
val pageList = json.decodeFromString<List<PageDto>>(pagesData.unescape()).sortedBy { it.order }
275-
return pageList.mapIndexed { i, page -> Page(i, imageUrl = page.url) }
303+
return pageList.mapIndexed { i, page ->
304+
val newUrl = page.url.toHttpUrlOrNull()?.run {
305+
newBuilder()
306+
.fragment("pageListParse")
307+
.build()
308+
.toString()
309+
}
310+
311+
Page(i, imageUrl = newUrl ?: page.url)
312+
}
276313
}
277314

278315
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
@@ -296,6 +333,16 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource {
296333
summary = "Hides the chapters that require a subscription to view"
297334
setDefaultValue(true)
298335
}.let(screen::addPreference)
336+
337+
SwitchPreferenceCompat(screen.context).apply {
338+
key = PREF_FORCE_HIGH_QUALITY
339+
title = "Force high quality chapter images"
340+
summary = "Attempt to use high quality chapter images.\nWill increase bandwidth by ~50%."
341+
if (failedHighQuality) {
342+
summary = "$summary\n*DISABLED* because of missing high quality images."
343+
}
344+
setDefaultValue(false)
345+
}.let(screen::addPreference)
299346
}
300347

301348
private var SharedPreferences.slugMap: MutableMap<String, String>
@@ -318,6 +365,10 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource {
318365
PREF_HIDE_PREMIUM_CHAPTERS,
319366
true,
320367
)
368+
private fun SharedPreferences.forceHighQuality(): Boolean = getBoolean(
369+
PREF_FORCE_HIGH_QUALITY,
370+
false,
371+
)
321372

322373
private fun String.toPermSlugIfNeeded(): String {
323374
if (!preferences.dynamicUrl()) return this
@@ -337,8 +388,11 @@ class AsuraScans : ParsedHttpSource(), ConfigurableSource {
337388
private val CLEAN_DATE_REGEX = """(\d+)(st|nd|rd|th)""".toRegex()
338389
private val OLD_FORMAT_MANGA_REGEX = """^/manga/(\d+-)?([^/]+)/?$""".toRegex()
339390
private val OLD_FORMAT_CHAPTER_REGEX = """^/(\d+-)?[^/]*-chapter-\d+(-\d+)*/?$""".toRegex()
391+
private val OPTIMIZED_IMAGE_PATH_REGEX = """^/storage/media/(\d+)/conversions/(.*)-optimized\.webp$""".toRegex()
392+
340393
private const val PREF_SLUG_MAP = "pref_slug_map_2"
341394
private const val PREF_DYNAMIC_URL = "pref_dynamic_url"
342395
private const val PREF_HIDE_PREMIUM_CHAPTERS = "pref_hide_premium_chapters"
396+
private const val PREF_FORCE_HIGH_QUALITY = "pref_force_high_quality"
343397
}
344398
}

0 commit comments

Comments
 (0)