HeanCms: Re-add fetchAllTitles (#17590)
* Re-add fetchAllTitles * fetchAllTitles in pagelistParse * Lint * Change allTitlesRequest parameters * Rename val * Remove unnecesary permSlug in chapters
This commit is contained in:
parent
dce77b7290
commit
31d420cd50
@ -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<SortByFilter>()
|
||||
val statusFilter = filters.firstInstanceOrNull<StatusFilter>()
|
||||
|
||||
val tagIds = filters.firstInstanceOrNull<GenreFilter>()?.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<SChapter> {
|
||||
val result = response.parseAs<HeanCmsSeriesDto>()
|
||||
|
||||
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<Page> {
|
||||
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<Genre> = listOf(
|
||||
Genre("Artes Marciais", 2),
|
||||
Genre("Aventura", 10),
|
||||
|
@ -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<SortByFilter>()
|
||||
val statusFilter = filters.firstInstanceOrNull<StatusFilter>()
|
||||
|
||||
val tagIds = filters.firstInstanceOrNull<GenreFilter>()?.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<SChapter> {
|
||||
val result = response.parseAs<HeanCmsSeriesDto>()
|
||||
|
||||
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<Page> {
|
||||
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<Genre> = listOf(
|
||||
Genre("+18", 1),
|
||||
Genre("Acción", 36),
|
||||
|
@ -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<String, HeanCmsTitle>? = 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<HeanCmsQuerySearchDto>()
|
||||
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<List<HeanCmsSeriesDto>>()
|
||||
.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<MangasPage> {
|
||||
@ -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<SortByFilter>()
|
||||
val statusFilter = filters.firstInstanceOrNull<StatusFilter>()
|
||||
|
||||
val tagIds = filters.firstInstanceOrNull<GenreFilter>()?.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<List<HeanCmsSearchDto>>()
|
||||
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<HeanCmsQuerySearchDto>()
|
||||
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<List<HeanCmsSeriesDto>>()
|
||||
.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<HeanCmsSeriesDto>() }
|
||||
|
||||
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<Page> {
|
||||
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<HeanCmsReaderDto>().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<String, HeanCmsTitle>()
|
||||
|
||||
while (hasNextPage) {
|
||||
val response = client.newCall(allTitlesRequest(page)).execute()
|
||||
val json = response.body.string()
|
||||
|
||||
if (json.startsWith("{")) {
|
||||
val result = json.parseAs<HeanCmsQuerySearchDto>()
|
||||
tempMap.putAll(parseAllTitles(result.data))
|
||||
hasNextPage = result.meta?.hasNextPage ?: false
|
||||
page++
|
||||
} else {
|
||||
val result = json.parseAs<List<HeanCmsSeriesDto>>()
|
||||
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<HeanCmsSeriesDto>): Map<String, HeanCmsTitle> {
|
||||
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<Status> = 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:"
|
||||
}
|
||||
}
|
||||
|
@ -35,10 +35,15 @@ data class HeanCmsSearchDto(
|
||||
fun toSManga(
|
||||
apiUrl: String,
|
||||
coverPath: String,
|
||||
slugMap: Map<String, HeanCms.HeanCmsTitle>,
|
||||
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
|
||||
|
@ -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),
|
||||
|
Loading…
x
Reference in New Issue
Block a user