From 31d420cd50dfa25ad63716033f5e971217cecc5f Mon Sep 17 00:00:00 2001 From: bapeey <90949336+bapeey@users.noreply.github.com> Date: Sat, 19 Aug 2023 09:56:33 -0500 Subject: [PATCH] HeanCms: Re-add fetchAllTitles (#17590) * Re-add fetchAllTitles * fetchAllTitles in pagelistParse * Lint * Change allTitlesRequest parameters * Rename val * Remove unnecesary permSlug in chapters --- .../heancms/reaperscans/src/ReaperScans.kt | 89 +------ .../heancms/yugenmangas/src/YugenMangas.kt | 89 +------ .../tachiyomi/multisrc/heancms/HeanCms.kt | 232 +++++++++++++++++- .../tachiyomi/multisrc/heancms/HeanCmsDto.kt | 20 +- .../multisrc/heancms/HeanCmsGenerator.kt | 2 +- 5 files changed, 246 insertions(+), 186 deletions(-) diff --git a/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt b/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt index 89a6c1027..d382dfcb7 100644 --- a/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt +++ b/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt @@ -1,21 +1,10 @@ package eu.kanade.tachiyomi.extension.pt.reaperscans import eu.kanade.tachiyomi.multisrc.heancms.Genre -import eu.kanade.tachiyomi.multisrc.heancms.GenreFilter import eu.kanade.tachiyomi.multisrc.heancms.HeanCms -import eu.kanade.tachiyomi.multisrc.heancms.HeanCmsSeriesDto -import eu.kanade.tachiyomi.multisrc.heancms.SortByFilter -import eu.kanade.tachiyomi.multisrc.heancms.StatusFilter -import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.interceptor.rateLimitHost -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.util.asJsoup import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response import java.text.SimpleDateFormat import java.util.TimeZone @@ -32,87 +21,15 @@ class ReaperScans : HeanCms( // Site changed from Madara to HeanCms. override val versionId = 2 + override val fetchAllTitles = true + override val useNewQueryEndpoint = true + override val coverPath: String = "" override val dateFormat: SimpleDateFormat = super.dateFormat.apply { timeZone = TimeZone.getTimeZone("GMT+01:00") } - override fun popularMangaRequest(page: Int): Request { - val url = "$apiUrl/query".toHttpUrl().newBuilder() - .addQueryParameter("query_string", "") - .addQueryParameter("series_status", "All") - .addQueryParameter("order", "desc") - .addQueryParameter("orderBy", "total_views") - .addQueryParameter("series_type", "Comic") - .addQueryParameter("page", page.toString()) - .addQueryParameter("perPage", "12") - .addQueryParameter("tags_ids", "[]") - - return GET(url.build(), headers) - } - - override fun latestUpdatesRequest(page: Int): Request { - val url = "$apiUrl/query".toHttpUrl().newBuilder() - .addQueryParameter("query_string", "") - .addQueryParameter("series_status", "All") - .addQueryParameter("order", "desc") - .addQueryParameter("orderBy", "latest") - .addQueryParameter("series_type", "Comic") - .addQueryParameter("page", page.toString()) - .addQueryParameter("perPage", "12") - .addQueryParameter("tags_ids", "[]") - - return GET(url.build(), headers) - } - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val sortByFilter = filters.firstInstanceOrNull() - val statusFilter = filters.firstInstanceOrNull() - - val tagIds = filters.firstInstanceOrNull()?.state.orEmpty() - .filter(Genre::state) - .map(Genre::id) - .joinToString(",", prefix = "[", postfix = "]") - - val url = "$apiUrl/query".toHttpUrl().newBuilder() - .addQueryParameter("query_string", query) - .addQueryParameter("series_status", statusFilter?.selected?.value ?: "All") - .addQueryParameter("order", if (sortByFilter?.state?.ascending == true) "asc" else "desc") - .addQueryParameter("orderBy", sortByFilter?.selected ?: "total_views") - .addQueryParameter("series_type", "Comic") - .addQueryParameter("page", page.toString()) - .addQueryParameter("perPage", "12") - .addQueryParameter("tags_ids", tagIds) - - return GET(url.build(), headers) - } - - override fun chapterListParse(response: Response): List { - val result = response.parseAs() - - val currentTimestamp = System.currentTimeMillis() - - return result.seasons.orEmpty() - .flatMap { it.chapters.orEmpty() } - .filterNot { it.price == 1 } - .map { it.toSChapter(result.slug, dateFormat) } - .filter { it.date_upload <= currentTimestamp } - } - - override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers) - - override fun pageListParse(response: Response): List { - val document = response.asJsoup() - - val images = document.selectFirst("div.min-h-screen > div.container > p.items-center") - - return images?.select("img").orEmpty().mapIndexed { i, img -> - val imageUrl = if (img.hasClass("lazy")) img.absUrl("data-src") else img.absUrl("src") - Page(i, "", imageUrl) - } - } - override fun getGenreList(): List = listOf( Genre("Artes Marciais", 2), Genre("Aventura", 10), diff --git a/multisrc/overrides/heancms/yugenmangas/src/YugenMangas.kt b/multisrc/overrides/heancms/yugenmangas/src/YugenMangas.kt index 6dbc15f89..83038a2ca 100644 --- a/multisrc/overrides/heancms/yugenmangas/src/YugenMangas.kt +++ b/multisrc/overrides/heancms/yugenmangas/src/YugenMangas.kt @@ -1,20 +1,9 @@ package eu.kanade.tachiyomi.extension.es.yugenmangas import eu.kanade.tachiyomi.multisrc.heancms.Genre -import eu.kanade.tachiyomi.multisrc.heancms.GenreFilter import eu.kanade.tachiyomi.multisrc.heancms.HeanCms -import eu.kanade.tachiyomi.multisrc.heancms.HeanCmsSeriesDto -import eu.kanade.tachiyomi.multisrc.heancms.SortByFilter -import eu.kanade.tachiyomi.multisrc.heancms.StatusFilter -import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.interceptor.rateLimitHost -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.util.asJsoup import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.Request -import okhttp3.Response import java.text.SimpleDateFormat import java.util.TimeZone import java.util.concurrent.TimeUnit @@ -30,6 +19,9 @@ class YugenMangas : // Site changed from Madara to HeanCms. override val versionId = 2 + override val fetchAllTitles = true + override val useNewQueryEndpoint = true + override val client = super.client.newBuilder() .connectTimeout(60, TimeUnit.SECONDS) .readTimeout(90, TimeUnit.SECONDS) @@ -42,81 +34,6 @@ class YugenMangas : timeZone = TimeZone.getTimeZone("UTC") } - override fun popularMangaRequest(page: Int): Request { - val url = "$apiUrl/query".toHttpUrl().newBuilder() - .addQueryParameter("query_string", "") - .addQueryParameter("series_status", "All") - .addQueryParameter("order", "desc") - .addQueryParameter("orderBy", "total_views") - .addQueryParameter("series_type", "Comic") - .addQueryParameter("page", page.toString()) - .addQueryParameter("perPage", "12") - .addQueryParameter("tags_ids", "[]") - - return GET(url.build(), headers) - } - - override fun latestUpdatesRequest(page: Int): Request { - val url = "$apiUrl/query".toHttpUrl().newBuilder() - .addQueryParameter("query_string", "") - .addQueryParameter("series_status", "All") - .addQueryParameter("order", "desc") - .addQueryParameter("orderBy", "latest") - .addQueryParameter("series_type", "Comic") - .addQueryParameter("page", page.toString()) - .addQueryParameter("perPage", "12") - .addQueryParameter("tags_ids", "[]") - - return GET(url.build(), headers) - } - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val sortByFilter = filters.firstInstanceOrNull() - val statusFilter = filters.firstInstanceOrNull() - - val tagIds = filters.firstInstanceOrNull()?.state.orEmpty() - .filter(Genre::state) - .map(Genre::id) - .joinToString(",", prefix = "[", postfix = "]") - - val url = "$apiUrl/query".toHttpUrl().newBuilder() - .addQueryParameter("query_string", query) - .addQueryParameter("series_status", statusFilter?.selected?.value ?: "All") - .addQueryParameter("order", if (sortByFilter?.state?.ascending == true) "asc" else "desc") - .addQueryParameter("orderBy", sortByFilter?.selected ?: "total_views") - .addQueryParameter("series_type", "Comic") - .addQueryParameter("page", page.toString()) - .addQueryParameter("perPage", "12") - .addQueryParameter("tags_ids", tagIds) - - return GET(url.build(), headers) - } - - override fun chapterListParse(response: Response): List { - val result = response.parseAs() - - val currentTimestamp = System.currentTimeMillis() - - return result.seasons.orEmpty() - .flatMap { it.chapters.orEmpty() } - .filterNot { it.price == 1 } - .map { it.toSChapter(result.slug, dateFormat) } - .filter { it.date_upload <= currentTimestamp } - } - - override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers) - - override fun pageListParse(response: Response): List { - val document = response.asJsoup() - - val images = document.selectFirst("div.min-h-screen > div.container > p.items-center") - - return images?.select("img").orEmpty().mapIndexed { i, img -> - val imageUrl = if (img.hasClass("lazy")) img.absUrl("data-src") else img.absUrl("src") - Page(i, "", imageUrl) - } - } - override fun getGenreList(): List = listOf( Genre("+18", 1), Genre("Acción", 36), diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCms.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCms.kt index e1cdce178..f644777f1 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCms.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCms.kt @@ -9,10 +9,12 @@ 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.online.HttpSource +import eu.kanade.tachiyomi.util.asJsoup import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import okhttp3.Headers +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request @@ -33,6 +35,12 @@ abstract class HeanCms( override val client: OkHttpClient = network.cloudflareClient + protected open val fetchAllTitles = false + + protected open val useNewQueryEndpoint = false + + private var seriesSlugMap: Map? = null + /** * Custom Json instance to make usage of `encodeDefaults`, * which is not enabled on the injected instance of the app. @@ -54,6 +62,10 @@ abstract class HeanCms( .add("Referer", "$baseUrl/") override fun popularMangaRequest(page: Int): Request { + if (useNewQueryEndpoint) { + return newEndpointPopularMangaRequest(page) + } + val payloadObj = HeanCmsQuerySearchPayloadDto( page = page, order = "desc", @@ -72,23 +84,45 @@ abstract class HeanCms( return POST("$apiUrl/series/querysearch", apiHeaders, payload) } + protected fun newEndpointPopularMangaRequest(page: Int): Request { + val url = "$apiUrl/query".toHttpUrl().newBuilder() + .addQueryParameter("query_string", "") + .addQueryParameter("series_status", "All") + .addQueryParameter("order", "desc") + .addQueryParameter("orderBy", "total_views") + .addQueryParameter("series_type", "Comic") + .addQueryParameter("page", page.toString()) + .addQueryParameter("perPage", "12") + .addQueryParameter("tags_ids", "[]") + + return GET(url.build(), headers) + } + override fun popularMangaParse(response: Response): MangasPage { val json = response.body.string() if (json.startsWith("{")) { val result = json.parseAs() - val mangaList = result.data.map { it.toSManga(apiUrl, coverPath) } + val mangaList = result.data.map { it.toSManga(apiUrl, coverPath, fetchAllTitles) } + + fetchAllTitles() return MangasPage(mangaList, result.meta?.hasNextPage ?: false) } val mangaList = json.parseAs>() - .map { it.toSManga(apiUrl, coverPath) } + .map { it.toSManga(apiUrl, coverPath, fetchAllTitles) } + + fetchAllTitles() return MangasPage(mangaList, hasNextPage = false) } override fun latestUpdatesRequest(page: Int): Request { + if (useNewQueryEndpoint) { + return newEndpointLatestUpdatesRequest(page) + } + val payloadObj = HeanCmsQuerySearchPayloadDto( page = page, order = "desc", @@ -107,6 +141,20 @@ abstract class HeanCms( return POST("$apiUrl/series/querysearch", apiHeaders, payload) } + protected fun newEndpointLatestUpdatesRequest(page: Int): Request { + val url = "$apiUrl/query".toHttpUrl().newBuilder() + .addQueryParameter("query_string", "") + .addQueryParameter("series_status", "All") + .addQueryParameter("order", "desc") + .addQueryParameter("orderBy", "latest") + .addQueryParameter("series_type", "Comic") + .addQueryParameter("page", page.toString()) + .addQueryParameter("perPage", "12") + .addQueryParameter("tags_ids", "[]") + + return GET(url.build(), headers) + } + override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response) override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { @@ -121,6 +169,10 @@ abstract class HeanCms( } override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + if (useNewQueryEndpoint) { + return newEndpointSearchMangaRequest(page, query, filters) + } + if (query.isNotBlank()) { val searchPayloadObj = HeanCmsSearchPayloadDto(query) val searchPayload = json.encodeToString(searchPayloadObj) @@ -158,16 +210,40 @@ abstract class HeanCms( return POST("$apiUrl/series/querysearch", apiHeaders, payload) } + protected fun newEndpointSearchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val sortByFilter = filters.firstInstanceOrNull() + val statusFilter = filters.firstInstanceOrNull() + + val tagIds = filters.firstInstanceOrNull()?.state.orEmpty() + .filter(Genre::state) + .map(Genre::id) + .joinToString(",", prefix = "[", postfix = "]") + + val url = "$apiUrl/query".toHttpUrl().newBuilder() + .addQueryParameter("query_string", query) + .addQueryParameter("series_status", statusFilter?.selected?.value ?: "All") + .addQueryParameter("order", if (sortByFilter?.state?.ascending == true) "asc" else "desc") + .addQueryParameter("orderBy", sortByFilter?.selected ?: "total_views") + .addQueryParameter("series_type", "Comic") + .addQueryParameter("page", page.toString()) + .addQueryParameter("perPage", "12") + .addQueryParameter("tags_ids", tagIds) + + return GET(url.build(), headers) + } + override fun searchMangaParse(response: Response): MangasPage { val json = response.body.string() if (response.request.url.pathSegments.last() == "search") { + fetchAllTitles() + val result = json.parseAs>() val mangaList = result .filter { it.type == "Comic" } .map { it.slug = it.slug.replace(TIMESTAMP_REGEX, "") - it.toSManga(apiUrl, coverPath) + it.toSManga(apiUrl, coverPath, seriesSlugMap.orEmpty(), fetchAllTitles) } return MangasPage(mangaList, false) @@ -175,28 +251,51 @@ abstract class HeanCms( if (json.startsWith("{")) { val result = json.parseAs() - val mangaList = result.data.map { it.toSManga(apiUrl, coverPath) } + val mangaList = result.data.map { it.toSManga(apiUrl, coverPath, fetchAllTitles) } + + fetchAllTitles() return MangasPage(mangaList, result.meta?.hasNextPage ?: false) } val mangaList = json.parseAs>() - .map { it.toSManga(apiUrl, coverPath) } + .map { it.toSManga(apiUrl, coverPath, fetchAllTitles) } + + fetchAllTitles() return MangasPage(mangaList, hasNextPage = false) } - override fun getMangaUrl(manga: SManga) = baseUrl + manga.url - - override fun mangaDetailsRequest(manga: SManga): Request { + override fun getMangaUrl(manga: SManga): String { val seriesSlug = manga.url .substringAfterLast("/") + .toPermSlugIfNeeded() + + val currentSlug = seriesSlugMap?.get(seriesSlug)?.slug ?: seriesSlug + + return "$baseUrl/series/$currentSlug" + } + + override fun mangaDetailsRequest(manga: SManga): Request { + if (fetchAllTitles && manga.url.contains(TIMESTAMP_REGEX)) { + throw Exception(intl.urlChangedError(name)) + } + + val seriesSlug = manga.url + .substringAfterLast("/") + .toPermSlugIfNeeded() + + fetchAllTitles() + + val seriesDetails = seriesSlugMap?.get(seriesSlug) + val currentSlug = seriesDetails?.slug ?: seriesSlug + val currentStatus = seriesDetails?.status ?: manga.status val apiHeaders = headersBuilder() .add("Accept", ACCEPT_JSON) .build() - return GET("$apiUrl/series/$seriesSlug#${manga.status}", apiHeaders) + return GET("$apiUrl/series/$currentSlug#$currentStatus", apiHeaders) } override fun mangaDetailsParse(response: Response): SManga { @@ -204,7 +303,7 @@ abstract class HeanCms( val result = runCatching { response.parseAs() } - val seriesDetails = result.getOrNull()?.toSManga(apiUrl, coverPath) + val seriesDetails = result.getOrNull()?.toSManga(apiUrl, coverPath, fetchAllTitles) ?: throw Exception(intl.urlChangedError(name)) return seriesDetails.apply { @@ -220,6 +319,14 @@ abstract class HeanCms( val currentTimestamp = System.currentTimeMillis() + if (useNewQueryEndpoint) { + return result.seasons.orEmpty() + .flatMap { it.chapters.orEmpty() } + .filterNot { it.price == 1 } + .map { it.toSChapter(result.slug, dateFormat) } + .filter { it.date_upload <= currentTimestamp } + } + return result.chapters.orEmpty() .filterNot { it.price == 1 } .map { it.toSChapter(result.slug, dateFormat) } @@ -230,6 +337,10 @@ abstract class HeanCms( override fun getChapterUrl(chapter: SChapter): String = baseUrl + chapter.url override fun pageListRequest(chapter: SChapter): Request { + if (useNewQueryEndpoint) { + return GET(baseUrl + chapter.url, headers) + } + val chapterId = chapter.url.substringAfterLast("#") val apiHeaders = headersBuilder() @@ -240,6 +351,17 @@ abstract class HeanCms( } override fun pageListParse(response: Response): List { + if (useNewQueryEndpoint) { + val document = response.asJsoup() + + val images = document.selectFirst("div.min-h-screen > div.container > p.items-center") + + return images?.select("img").orEmpty().mapIndexed { i, img -> + val imageUrl = if (img.hasClass("lazy")) img.absUrl("data-src") else img.absUrl("src") + Page(i, "", imageUrl) + } + } + return response.parseAs().content?.images.orEmpty() .filterNot { imageUrl -> // Their image server returns HTTP 403 for hidden files that starts @@ -266,6 +388,94 @@ abstract class HeanCms( return GET(page.imageUrl!!, imageHeaders) } + protected open fun fetchAllTitles() { + if (!seriesSlugMap.isNullOrEmpty() || !fetchAllTitles) { + return + } + + val result = runCatching { + var hasNextPage = true + var page = 1 + val tempMap = mutableMapOf() + + while (hasNextPage) { + val response = client.newCall(allTitlesRequest(page)).execute() + val json = response.body.string() + + if (json.startsWith("{")) { + val result = json.parseAs() + tempMap.putAll(parseAllTitles(result.data)) + hasNextPage = result.meta?.hasNextPage ?: false + page++ + } else { + val result = json.parseAs>() + tempMap.putAll(parseAllTitles(result)) + hasNextPage = false + } + } + + tempMap.toMap() + } + + seriesSlugMap = result.getOrNull() + } + + protected open fun allTitlesRequest(page: Int): Request { + if (useNewQueryEndpoint) { + val url = "$apiUrl/query".toHttpUrl().newBuilder() + .addQueryParameter("series_type", "Comic") + .addQueryParameter("page", page.toString()) + .addQueryParameter("perPage", PER_PAGE_MANGA_TITLES.toString()) + + return GET(url.build(), headers) + } + + val payloadObj = HeanCmsQuerySearchPayloadDto( + page = page, + order = "desc", + orderBy = "total_views", + type = "Comic", + ) + + val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE) + + val apiHeaders = headersBuilder() + .add("Accept", ACCEPT_JSON) + .add("Content-Type", payload.contentType().toString()) + .build() + + return POST("$apiUrl/series/querysearch", apiHeaders, payload) + } + + protected open fun parseAllTitles(result: List): Map { + return result + .filter { it.type == "Comic" } + .associateBy( + keySelector = { it.slug.replace(TIMESTAMP_REGEX, "") }, + valueTransform = { + HeanCmsTitle( + slug = it.slug, + thumbnailFileName = it.thumbnail, + status = it.status?.toStatus() ?: SManga.UNKNOWN, + ) + }, + ) + } + + /** + * Used to store the current slugs for sources that change it periodically and for the + * search that doesn't return the thumbnail URLs. + */ + data class HeanCmsTitle(val slug: String, val thumbnailFileName: String, val status: Int) + + private fun String.toPermSlugIfNeeded(): String { + return if (fetchAllTitles) { + this.replace(TIMESTAMP_REGEX, "") + } else { + this + } + } + protected open fun getStatusList(): List = listOf( Status(intl.statusAll, "All"), Status(intl.statusOngoing, "Ongoing"), @@ -312,6 +522,8 @@ abstract class HeanCms( val TIMESTAMP_REGEX = """-\d{13}$""".toRegex() + private const val PER_PAGE_MANGA_TITLES = 10000 + const val SEARCH_PREFIX = "slug:" } } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsDto.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsDto.kt index 24e296a33..6e765ad44 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsDto.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsDto.kt @@ -35,10 +35,15 @@ data class HeanCmsSearchDto( fun toSManga( apiUrl: String, coverPath: String, + slugMap: Map, + fetchAllTiles: Boolean, ): SManga = SManga.create().apply { + val slugOnly = slug.toPermSlugIfNeeded(fetchAllTiles) + val thumbnailFileName = slugMap[slugOnly]?.thumbnailFileName title = this@HeanCmsSearchDto.title thumbnail_url = thumbnail?.toAbsoluteThumbnailUrl(apiUrl, coverPath) - url = "/series/$slug" + ?: thumbnailFileName?.toAbsoluteThumbnailUrl(apiUrl, coverPath) + url = "/series/$slugOnly" } } @@ -61,8 +66,10 @@ data class HeanCmsSeriesDto( fun toSManga( apiUrl: String, coverPath: String, + fetchAllTiles: Boolean, ): SManga = SManga.create().apply { val descriptionBody = this@HeanCmsSeriesDto.description?.let(Jsoup::parseBodyFragment) + val slugOnly = slug.toPermSlugIfNeeded(fetchAllTiles) title = this@HeanCmsSeriesDto.title author = this@HeanCmsSeriesDto.author?.trim() @@ -76,7 +83,7 @@ data class HeanCmsSeriesDto( thumbnail_url = thumbnail.ifEmpty { null } ?.toAbsoluteThumbnailUrl(apiUrl, coverPath) status = this@HeanCmsSeriesDto.status?.toStatus() ?: SManga.UNKNOWN - url = "/series/$slug" + url = "/series/$slugOnly" } } @@ -98,7 +105,6 @@ data class HeanCmsChapterDto( @SerialName("created_at") val createdAt: String, val price: Int? = null, ) { - fun toSChapter(seriesSlug: String, dateFormat: SimpleDateFormat): SChapter = SChapter.create().apply { name = this@HeanCmsChapterDto.name.trim() date_upload = runCatching { dateFormat.parse(createdAt)?.time } @@ -134,6 +140,14 @@ private fun String.toAbsoluteThumbnailUrl(apiUrl: String, coverPath: String): St return if (startsWith("https://")) this else "$apiUrl/$coverPath$this" } +private fun String.toPermSlugIfNeeded(fetchAllTitles: Boolean): String { + return if (fetchAllTitles) { + this.replace(HeanCms.TIMESTAMP_REGEX, "") + } else { + this + } +} + fun String.toStatus(): Int = when (this) { "Ongoing" -> SManga.ONGOING "Hiatus" -> SManga.ON_HIATUS diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsGenerator.kt index 956515456..57c75cd24 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsGenerator.kt @@ -9,7 +9,7 @@ class HeanCmsGenerator : ThemeSourceGenerator { override val themeClass = "HeanCms" - override val baseVersionCode: Int = 16 + override val baseVersionCode: Int = 17 override val sources = listOf( SingleLang("Glorious Scan", "https://gloriousscan.com", "pt-BR", overrideVersionCode = 17),