SussyToons: Fix chapters and pages (#8804)
* Fix chapters and pages * Bump version * Fix chapter in webview
This commit is contained in:
parent
db840dd353
commit
2905e17a9a
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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("/")
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user