diff --git a/multisrc/overrides/wpmangareader/flamescans/src/FlameScans.kt b/multisrc/overrides/wpmangareader/flamescans/src/FlameScans.kt index 4de9e9bc6..2df1898bf 100644 --- a/multisrc/overrides/wpmangareader/flamescans/src/FlameScans.kt +++ b/multisrc/overrides/wpmangareader/flamescans/src/FlameScans.kt @@ -1,21 +1,127 @@ package eu.kanade.tachiyomi.extension.en.flamescans +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.Rect +import android.util.Log import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor import eu.kanade.tachiyomi.multisrc.wpmangareader.WPMangaReader +import eu.kanade.tachiyomi.source.model.Page import okhttp3.Headers +import okhttp3.Interceptor +import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient +import okhttp3.Protocol +import okhttp3.Response +import okhttp3.ResponseBody.Companion.toResponseBody +import org.jsoup.nodes.Document +import java.io.ByteArrayOutputStream import java.util.concurrent.TimeUnit -class FlameScans : WPMangaReader("Flame Scans", "https://flamescans.org", "en", "/series") { - private val rateLimitInterceptor = RateLimitInterceptor(1) - private val userAgent = "Tachiyomi Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36" - override fun headersBuilder(): Headers.Builder = Headers.Builder() - .add("User-Agent", userAgent) +class FlameScans : WPMangaReader( + "Flame Scans", + "https://flamescans.org", + "en", + "/series" +) { override val client: OkHttpClient = network.cloudflareClient.newBuilder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) - .addNetworkInterceptor(rateLimitInterceptor) + .addInterceptor(::composedImageIntercept) + .addInterceptor(RateLimitInterceptor(1, 1, TimeUnit.SECONDS)) .build() -} + override fun headersBuilder(): Headers.Builder = Headers.Builder() + .add("User-Agent", USER_AGENT) + + private val composedSelector: String = "#readerarea div.figure_container div.composed_figure" + + override fun pageListParse(document: Document): List { + val hasSplitImages = document + .select(composedSelector) + .firstOrNull() != null + + if (!hasSplitImages) { + return super.pageListParse(document) + } + + return document.select("#readerarea p:has(img), $composedSelector") + .filter { + it.select("img").all { imgEl -> + imgEl.attr("abs:src").isNullOrEmpty().not() + } + } + .mapIndexed { i, el -> + if (el.tagName() == "p") { + Page(i, "", el.select("img").attr("abs:src")) + } else { + val imageUrls = el.select("img") + .joinToString("|") { it.attr("abs:src") } + + Page(i, "", imageUrls + COMPOSED_SUFFIX) + } + } + } + + private fun composedImageIntercept(chain: Interceptor.Chain): Response { + if (!chain.request().url.toString().endsWith(COMPOSED_SUFFIX)) { + return chain.proceed(chain.request()) + } + + val imageUrls = chain.request().url.toString() + .removeSuffix(COMPOSED_SUFFIX) + .split("%7C") + + var width = 0 + var height = 0 + + val imageBitmaps = imageUrls.map { imageUrl -> + val request = chain.request().newBuilder().url(imageUrl).build() + val response = chain.proceed(request) + + val bitmap = BitmapFactory.decodeStream(response.body!!.byteStream()) + + width += bitmap.width + height = bitmap.height + + bitmap + } + + val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + val canvas = Canvas(result) + + var left = 0 + + imageBitmaps.forEach { bitmap -> + val srcRect = Rect(0, 0, bitmap.width, bitmap.height) + val dstRect = Rect(left, 0, left + bitmap.width, bitmap.height) + + canvas.drawBitmap(bitmap, srcRect, dstRect, null) + + left += bitmap.width + } + + val output = ByteArrayOutputStream() + result.compress(Bitmap.CompressFormat.PNG, 100, output) + + val responseBody = output.toByteArray().toResponseBody(MEDIA_TYPE) + + return Response.Builder() + .code(200) + .protocol(Protocol.HTTP_1_1) + .request(chain.request()) + .message("OK") + .body(responseBody) + .build() + } + + companion object { + private const val USER_AGENT = "Tachiyomi Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36" + + private const val COMPOSED_SUFFIX = "?comp" + private val MEDIA_TYPE = "image/png".toMediaType() + } +} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/wpmangareader/WPMangaReaderGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/wpmangareader/WPMangaReaderGenerator.kt index 2b7324454..15284b34b 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/wpmangareader/WPMangaReaderGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/wpmangareader/WPMangaReaderGenerator.kt @@ -23,7 +23,7 @@ class WPMangaReaderGenerator : ThemeSourceGenerator { SingleLang("Mangasusu", "https://mangasusu.co.in", "id", isNsfw = true), SingleLang("TurkToon", "https://turktoon.com", "tr"), SingleLang("Gecenin Lordu", "https://geceninlordu.com/", "tr", overrideVersionCode = 1), - SingleLang("Flame Scans", "https://flamescans.org", "en", overrideVersionCode = 6), + SingleLang("Flame Scans", "https://flamescans.org", "en", overrideVersionCode = 7), SingleLang("A Pair of 2+", "https://pairof2.com", "en", className = "APairOf2"), SingleLang("PMScans", "https://reader.pmscans.com", "en"), SingleLang("Skull Scans", "https://www.skullscans.com", "en"),