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:
parent
3403b7a0ec
commit
e2c7543d3e
|
@ -1,7 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Sussy Toons'
|
extName = 'Sussy Toons'
|
||||||
extClass = '.SussyToons'
|
extClass = '.SussyToons'
|
||||||
extVersionCode = 49
|
extVersionCode = 50
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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."
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue