SussyToons: Fix thumbnail, loading pages and update domain (#7461)

* Fix thumbnail, loading pages and update domain

* Handling JSON chunks with json lib
This commit is contained in:
Chopper 2025-02-02 05:30:34 -03:00 committed by Draff
parent 3403b7a0ec
commit e2c7543d3e
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
3 changed files with 54 additions and 11 deletions

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'Sussy Toons' extName = 'Sussy Toons'
extClass = '.SussyToons' extClass = '.SussyToons'
extVersionCode = 49 extVersionCode = 50
isNsfw = true isNsfw = true
} }

View File

@ -15,6 +15,8 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
@ -22,6 +24,8 @@ import okhttp3.Interceptor
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -61,7 +65,7 @@ class SussyToons : HttpSource(), ConfigurableSource {
else -> preferences.getString(BASE_URL_PREF, defaultBaseUrl)!! else -> preferences.getString(BASE_URL_PREF, defaultBaseUrl)!!
} }
private val defaultBaseUrl: String = "https://www.sussytoons.site" private val defaultBaseUrl: String = "https://www.sussytoons.wtf"
private val defaultApiUrl: String = "https://api-dev.sussytoons.site" private val defaultApiUrl: String = "https://api-dev.sussytoons.site"
override val client = network.cloudflareClient.newBuilder() override val client = network.cloudflareClient.newBuilder()
@ -144,7 +148,6 @@ class SussyToons : HttpSource(), ConfigurableSource {
override fun mangaDetailsRequest(manga: SManga): Request { override fun mangaDetailsRequest(manga: SManga): Request {
val url = "$apiUrl/obras".toHttpUrl().newBuilder() val url = "$apiUrl/obras".toHttpUrl().newBuilder()
.addPathSegment(manga.id) .addPathSegment(manga.id)
.fragment("$mangaPagePrefix${getMangaUrl(manga)}")
.build() .build()
return GET(url, headers) return GET(url, headers)
} }
@ -178,14 +181,17 @@ class SussyToons : HttpSource(), ConfigurableSource {
// ============================= Pages ==================================== // ============================= Pages ====================================
override fun pageListRequest(chapter: SChapter): Request { private val pageUrlSelector = "img.chakra-image"
val request = super.pageListRequest(chapter)
val chapterPageId = request.url.pathSegments.last()
return GET("$apiUrl/capitulos/$chapterPageId", headers)
}
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val dto = response.parseAs<WrapperDto<ChapterPageDto>>().results val document = response.asJsoup()
pageListParse(document).takeIf(List<Page>::isNotEmpty)?.let { return it }
val dto = extractScriptData(document)
.let(::extractJsonContent)
.let(::parseJsonToChapterPageDto)
return dto.pages.mapIndexed { index, image -> return dto.pages.mapIndexed { index, image ->
val imageUrl = when { val imageUrl = when {
image.isWordPressContent() -> { image.isWordPressContent() -> {
@ -202,6 +208,31 @@ class SussyToons : HttpSource(), ConfigurableSource {
Page(index, imageUrl = imageUrl.toString()) Page(index, imageUrl = imageUrl.toString())
} }
} }
private fun pageListParse(document: Document): List<Page> {
return document.select(pageUrlSelector).mapIndexed { index, element ->
Page(index, document.location(), element.absUrl("src"))
}
}
private fun extractScriptData(document: Document): String {
return document.select("script").map(Element::data)
.firstOrNull(pageRegex::containsMatchIn)
?: throw Exception("Failed to load pages: Script data not found")
}
private fun extractJsonContent(scriptData: String): String {
return pageRegex.find(scriptData)
?.groups?.get(1)?.value
?.let { json.decodeFromString<String>("\"$it\"") }
?: throw Exception("Failed to extract JSON from script")
}
private fun parseJsonToChapterPageDto(jsonContent: String): ChapterPageDto {
return try {
jsonContent.parseAs<WrapperDto<ChapterPageDto>>().results
} catch (e: Exception) {
throw Exception("Failed to load pages: ${e.message}")
}
}
override fun imageUrlParse(response: Response): String = "" override fun imageUrlParse(response: Response): String = ""
@ -290,7 +321,12 @@ class SussyToons : HttpSource(), ConfigurableSource {
private fun MangaDto.toSManga(): SManga { private fun MangaDto.toSManga(): SManga {
val sManga = SManga.create().apply { val sManga = SManga.create().apply {
title = name title = name
thumbnail_url = thumbnail thumbnail_url = thumbnail?.let {
when {
it.startsWith("http") -> thumbnail
else -> "$OLDI_URL/scans/$scanId/obras/${this@toSManga.id}/$thumbnail"
}
}
initialized = true initialized = true
val mangaUrl = "$baseUrl/obra".toHttpUrl().newBuilder() val mangaUrl = "$baseUrl/obra".toHttpUrl().newBuilder()
.addPathSegment(this@toSManga.id.toString()) .addPathSegment(this@toSManga.id.toString())
@ -309,6 +345,10 @@ class SussyToons : HttpSource(), ConfigurableSource {
return json.decodeFromStream(body.byteStream()) return json.decodeFromStream(body.byteStream())
} }
private inline fun <reified T> String.parseAs(): T {
return json.decodeFromString(this)
}
private fun String.toDate() = private fun String.toDate() =
try { dateFormat.parse(this)!!.time } catch (_: Exception) { 0L } try { dateFormat.parse(this)!!.time } catch (_: Exception) { 0L }
@ -324,7 +364,8 @@ class SussyToons : HttpSource(), ConfigurableSource {
companion object { companion object {
const val CDN_URL = "https://cdn.sussytoons.site" const val CDN_URL = "https://cdn.sussytoons.site"
const val OLDI_URL = "https://oldi.sussytoons.site" const val OLDI_URL = "https://oldi.sussytoons.site"
const val mangaPagePrefix = "mangaPage:"
val pageRegex = """capituloInicial.{3}(.*?)(\}\]\})""".toRegex()
private const val URL_PREF_SUMMARY = "Para uso temporário, se a extensão for atualizada, a alteração será perdida." private const val URL_PREF_SUMMARY = "Para uso temporário, se a extensão for atualizada, a alteração será perdida."

View File

@ -33,6 +33,8 @@ class MangaDto(
val slug: String?, val slug: String?,
@SerialName("status") @SerialName("status")
val status: MangaStatus, val status: MangaStatus,
@SerialName("scan_id")
val scanId: Int,
) { ) {
@Serializable @Serializable
class MangaStatus( class MangaStatus(