From 614165b64c1e397acef74a8c5d74d26538b603c4 Mon Sep 17 00:00:00 2001 From: Vetle Ledaal Date: Sun, 8 Dec 2024 16:02:24 +0100 Subject: [PATCH] MangaSwat: fix latest tab (#6531) --- src/ar/mangaswat/build.gradle | 2 +- .../extension/ar/mangaswat/MangaSwat.kt | 93 ++++++++++++++++++- 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/src/ar/mangaswat/build.gradle b/src/ar/mangaswat/build.gradle index f81dc0ce8..8fc1853ac 100644 --- a/src/ar/mangaswat/build.gradle +++ b/src/ar/mangaswat/build.gradle @@ -3,7 +3,7 @@ ext { extClass = '.MangaSwat' themePkg = 'mangathemesia' baseUrl = 'https://swatscans.com' - overrideVersionCode = 24 + overrideVersionCode = 25 } apply from: "$rootDir/common.gradle" diff --git a/src/ar/mangaswat/src/eu/kanade/tachiyomi/extension/ar/mangaswat/MangaSwat.kt b/src/ar/mangaswat/src/eu/kanade/tachiyomi/extension/ar/mangaswat/MangaSwat.kt index 0b2a129fa..a9ec6ef7d 100644 --- a/src/ar/mangaswat/src/eu/kanade/tachiyomi/extension/ar/mangaswat/MangaSwat.kt +++ b/src/ar/mangaswat/src/eu/kanade/tachiyomi/extension/ar/mangaswat/MangaSwat.kt @@ -4,14 +4,23 @@ import android.app.Application import android.widget.Toast import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString +import okhttp3.FormBody +import okhttp3.Interceptor +import okhttp3.MediaType.Companion.toMediaType import okhttp3.Request +import okhttp3.Response +import okhttp3.ResponseBody.Companion.toResponseBody +import org.jsoup.Jsoup import org.jsoup.nodes.Document import org.jsoup.nodes.Element import uy.kohesive.injekt.Injekt @@ -35,13 +44,86 @@ class MangaSwat : } override val client = super.client.newBuilder() + .addInterceptor(::tokenInterceptor) .rateLimit(1) .build() - override fun latestUpdatesRequest(page: Int): Request { - val filter = FilterList(OrderByFilter("", orderByFilterOptions, "added")) + // From Akuma - CSRF token + private var storedToken: String? = null - return searchMangaRequest(page, "", filter) + private fun tokenInterceptor(chain: Interceptor.Chain): Response { + val request = chain.request() + + if (request.method == "POST" && request.header("X-CSRF-TOKEN") == null) { + val newRequest = request.newBuilder() + val token = getToken() + val response = chain.proceed( + newRequest + .addHeader("X-CSRF-TOKEN", token) + .build(), + ) + + if (response.code == 419) { + response.close() + storedToken = null // reset the token + val newToken = getToken() + return chain.proceed( + newRequest + .addHeader("X-CSRF-TOKEN", newToken) + .build(), + ) + } + + return response + } + + val response = chain.proceed(request) + + if (response.header("Content-Type")?.contains("text/html") != true) { + return response + } + + storedToken = Jsoup.parse(response.peekBody(Long.MAX_VALUE).string()) + .selectFirst("head meta[name*=csrf-token]") + ?.attr("content") + + return response + } + + private fun getToken(): String { + if (storedToken.isNullOrEmpty()) { + val request = GET(baseUrl, headers) + client.newCall(request).execute().close() // updates token in interceptor + } + return storedToken!! + } + + override fun latestUpdatesRequest(page: Int): Request { + val xhrHeaders = headersBuilder() + .add("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") + .add("X-Requested-With", "XMLHttpRequest") + .build() + + val formBody = FormBody.Builder() + .add("action", "more_manga_home") + .add("paged", (page - 1).toString()) + .build() + + return POST("$baseUrl/ajax-request", xhrHeaders, formBody) + } + + override fun latestUpdatesParse(response: Response): MangasPage { + return json + .decodeFromString(response.body.string()) + .html.toResponseBody("text/html".toMediaType()) + .let { response.newBuilder().body(it).build() } + .let { super.latestUpdatesParse(it) } + .let { page -> + MangasPage( + mangas = page.mangas, + hasNextPage = page.mangas.size >= 24, // info not present + ) + } } override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { @@ -113,6 +195,11 @@ class MangaSwat : val images: List, ) + @Serializable + class MoreMangaHomeDto( + val html: String, + ) + companion object { private const val RESTART_TACHIYOMI = "Restart Tachiyomi to apply new setting." private const val BASE_URL_PREF_TITLE = "Override BaseUrl"