SussyToons: Fix chapters and pages (#8804)

* Fix chapters and pages

* Bump version

* Fix chapter in webview
This commit is contained in:
Chopper 2025-05-10 16:29:45 -03:00 committed by Draff
parent db840dd353
commit 2905e17a9a
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
4 changed files with 95 additions and 59 deletions

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.multisrc.greenshit package eu.kanade.tachiyomi.multisrc.greenshit
import android.annotation.SuppressLint
import android.content.SharedPreferences import android.content.SharedPreferences
import android.widget.Toast import android.widget.Toast
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
@ -18,7 +17,6 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import keiyoushi.utils.getPreferences import keiyoushi.utils.getPreferences
import keiyoushi.utils.parseAs import keiyoushi.utils.parseAs
import keiyoushi.utils.tryParse
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor import okhttp3.Interceptor
@ -27,8 +25,6 @@ import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.io.IOException import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Locale
abstract class GreenShit( abstract class GreenShit(
override val name: String, override val name: String,
@ -43,9 +39,9 @@ abstract class GreenShit(
private val preferences: SharedPreferences = getPreferences() private val preferences: SharedPreferences = getPreferences()
private var apiUrl: String protected var apiUrl: String
get() = preferences.getString(API_BASE_URL_PREF, defaultApiUrl)!! get() = preferences.getString(API_BASE_URL_PREF, defaultApiUrl)!!
set(value) = preferences.edit().putString(API_BASE_URL_PREF, value).apply() private set(value) = preferences.edit().putString(API_BASE_URL_PREF, value).apply()
private var restoreDefaultEnable: Boolean private var restoreDefaultEnable: Boolean
get() = preferences.getBoolean(DEFAULT_PREF, false) get() = preferences.getBoolean(DEFAULT_PREF, false)
@ -152,16 +148,7 @@ abstract class GreenShit(
val json = response.parseScriptToJson().let(DETAILS_CHAPTER_REGEX::find) val json = response.parseScriptToJson().let(DETAILS_CHAPTER_REGEX::find)
?.groups?.get(0)?.value ?.groups?.get(0)?.value
?: return emptyList() ?: return emptyList()
return json.parseAs<ResultDto<WrapperChapterDto>>().results.chapters.map { return json.parseAs<ResultDto<WrapperChapterDto>>().toSChapterList()
SChapter.create().apply {
name = it.name
it.chapterNumber?.let {
chapter_number = it
}
setUrlWithoutDomain("$baseUrl/capitulo/${it.id}")
date_upload = dateFormat.tryParse(it.updateAt)
}
}.sortedByDescending(SChapter::chapter_number)
} }
// ============================= Pages ==================================== // ============================= Pages ====================================
@ -176,22 +163,7 @@ abstract class GreenShit(
val dto = extractScriptData(document) val dto = extractScriptData(document)
.let(::extractJsonContent) .let(::extractJsonContent)
.let(::parseJsonToChapterPageDto) .let(::parseJsonToChapterPageDto)
return dto.toPageList()
return dto.pages.mapIndexed { index, image ->
val imageUrl = when {
image.isWordPressContent() -> {
CDN_URL.toHttpUrl().newBuilder()
.addPathSegments("wp-content/uploads/WP-manga/data")
.addPathSegments(image.src.toPathSegment())
.build()
}
else -> {
"$CDN_URL/scans/${dto.manga.scanId}/obras/${dto.manga.id}/capitulos/${dto.chapterNumber}/${image.src}"
.toHttpUrl()
}
}
Page(index, imageUrl = imageUrl.toString())
}
} }
private fun pageListParse(document: Document): List<Page> { private fun pageListParse(document: Document): List<Page> {
return document.select(pageUrlSelector).mapIndexed { index, element -> return document.select(pageUrlSelector).mapIndexed { index, element ->
@ -211,9 +183,9 @@ abstract class GreenShit(
?: throw Exception("Failed to extract JSON from script") ?: throw Exception("Failed to extract JSON from script")
} }
private fun parseJsonToChapterPageDto(jsonContent: String): ChapterPageDto { private fun parseJsonToChapterPageDto(jsonContent: String): ResultDto<ChapterPageDto> {
return try { return try {
jsonContent.parseAs<ResultDto<ChapterPageDto>>().results jsonContent.parseAs<ResultDto<ChapterPageDto>>()
} catch (e: Exception) { } catch (e: Exception) {
throw Exception("Failed to load pages: ${e.message}") throw Exception("Failed to load pages: ${e.message}")
} }
@ -327,15 +299,6 @@ abstract class GreenShit(
return this return this
} }
/**
* Normalizes path segments:
* Ex: [ "/a/b/", "/a/b", "a/b/", "a/b" ]
* Result: "a/b"
*/
private fun String.toPathSegment() = this.trim().split("/")
.filter(String::isNotEmpty)
.joinToString("/")
companion object { companion object {
const val CDN_URL = "https://cdn.sussytoons.site" const val CDN_URL = "https://cdn.sussytoons.site"
@ -355,8 +318,5 @@ abstract class GreenShit(
private const val API_DEFAULT_BASE_URL_PREF = "defaultApiUrl" private const val API_DEFAULT_BASE_URL_PREF = "defaultApiUrl"
private const val DEFAULT_PREF = "defaultPref" private const val DEFAULT_PREF = "defaultPref"
@SuppressLint("SimpleDateFormat")
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT)
} }
} }

View File

@ -1,12 +1,19 @@
package eu.kanade.tachiyomi.multisrc.greenshit package eu.kanade.tachiyomi.multisrc.greenshit
import android.annotation.SuppressLint
import eu.kanade.tachiyomi.multisrc.greenshit.GreenShit.Companion.CDN_URL import eu.kanade.tachiyomi.multisrc.greenshit.GreenShit.Companion.CDN_URL
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import keiyoushi.utils.tryParse
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames import kotlinx.serialization.json.JsonNames
import okhttp3.HttpUrl.Companion.toHttpUrl
import org.jsoup.Jsoup import org.jsoup.Jsoup
import java.text.Normalizer import java.text.Normalizer
import java.text.SimpleDateFormat
import java.util.Locale
@Serializable @Serializable
class ResultDto<T>( class ResultDto<T>(
@ -25,6 +32,38 @@ class ResultDto<T>(
.map { it.apply { slug = it.slug ?: name.createSlug() } } .map { it.apply { slug = it.slug ?: name.createSlug() } }
.map(MangaDto::toSManga) .map(MangaDto::toSManga)
fun toSChapterList(): List<SChapter> = (results as WrapperChapterDto)
.chapters.map {
SChapter.create().apply {
name = it.name
it.chapterNumber?.let {
chapter_number = it
}
url = "/capitulo/${it.id}"
date_upload = dateFormat.tryParse(it.updateAt)
}
}.sortedByDescending(SChapter::chapter_number)
fun toPageList(): List<Page> {
val dto = (results as ChapterPageDto)
return dto.pages.mapIndexed { index, image ->
val imageUrl = when {
image.isWordPressContent() -> {
CDN_URL.toHttpUrl().newBuilder()
.addPathSegments("wp-content/uploads/WP-manga/data")
.addPathSegments(image.src.toPathSegment())
.build()
}
else -> {
"$CDN_URL/scans/${dto.manga.scanId}/obras/${dto.manga.id}/capitulos/${dto.chapterNumber}/${image.src}"
.toHttpUrl()
}
}
Page(index, imageUrl = imageUrl.toString())
}
}
private fun String.createSlug(): String { private fun String.createSlug(): String {
return Normalizer.normalize(this, Normalizer.Form.NFD) return Normalizer.normalize(this, Normalizer.Form.NFD)
.trim() .trim()
@ -33,17 +72,11 @@ class ResultDto<T>(
.replace("\\s+".toRegex(), "-") .replace("\\s+".toRegex(), "-")
.lowercase() .lowercase()
} }
}
@Serializable companion object {
class WrapperDto( @SuppressLint("SimpleDateFormat")
@SerialName("dataTop") val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT)
val popular: ResultDto<List<MangaDto>>?, }
@JsonNames("atualizacoesInicial")
private val dataLatest: ResultDto<List<MangaDto>>?,
) {
val latest: ResultDto<List<MangaDto>> get() = dataLatest!!
} }
@Serializable @Serializable
@ -154,3 +187,12 @@ class PageDto(
) { ) {
fun isWordPressContent(): Boolean = number == null fun isWordPressContent(): Boolean = number == null
} }
/**
* Normalizes path segments:
* Ex: [ "/a/b/", "/a/b", "a/b/", "a/b" ]
* Result: "a/b"
*/
private fun String.toPathSegment() = this.trim().split("/")
.filter(String::isNotEmpty)
.joinToString("/")

View File

@ -3,7 +3,7 @@ ext {
extClass = '.SussyToons' extClass = '.SussyToons'
themePkg = 'greenshit' themePkg = 'greenshit'
baseUrl = 'https://www.sussytoons.wtf' baseUrl = 'https://www.sussytoons.wtf'
overrideVersionCode = 54 overrideVersionCode = 55
isNsfw = true isNsfw = true
} }

View File

@ -1,6 +1,18 @@
package eu.kanade.tachiyomi.extension.pt.sussyscan package eu.kanade.tachiyomi.extension.pt.sussyscan
import eu.kanade.tachiyomi.multisrc.greenshit.ChapterPageDto
import eu.kanade.tachiyomi.multisrc.greenshit.GreenShit import eu.kanade.tachiyomi.multisrc.greenshit.GreenShit
import eu.kanade.tachiyomi.multisrc.greenshit.MangaDto
import eu.kanade.tachiyomi.multisrc.greenshit.ResultDto
import eu.kanade.tachiyomi.multisrc.greenshit.WrapperChapterDto
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import keiyoushi.utils.parseAs
import okhttp3.Request
import okhttp3.Response
class SussyToons : GreenShit( class SussyToons : GreenShit(
"Sussy Toons", "Sussy Toons",
@ -11,7 +23,29 @@ class SussyToons : GreenShit(
override val versionId = 2 override val versionId = 2
override val supportsLatest = false override fun popularMangaRequest(page: Int): Request =
GET("$apiUrl/obras/top5", headers)
override fun fetchPopularManga(page: Int) = fetchLatestUpdates(page) override fun popularMangaParse(response: Response): MangasPage {
val mangas = response.parseAs<ResultDto<List<MangaDto>>>().toSMangaList()
return MangasPage(mangas, hasNextPage = false)
}
override fun getChapterUrl(chapter: SChapter) = "$baseUrl${chapter.url}"
override fun chapterListRequest(manga: SManga): Request {
val pathSegment = manga.url.substringBeforeLast("/").replace("obra", "obras")
return GET("$apiUrl$pathSegment", headers)
}
override fun chapterListParse(response: Response): List<SChapter> =
response.parseAs<ResultDto<WrapperChapterDto>>().toSChapterList()
override fun pageListRequest(chapter: SChapter): Request {
val pathSegment = chapter.url.replace("capitulo", "capitulo-app")
return GET("$apiUrl$pathSegment", headers)
}
override fun pageListParse(response: Response): List<Page> =
response.parseAs<ResultDto<ChapterPageDto>>().toPageList()
} }