diff --git a/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt b/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt index 422b98aee..78e494d92 100644 --- a/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt +++ b/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt @@ -21,8 +21,6 @@ class ReaperScans : HeanCms( // Site changed from Madara to HeanCms. override val versionId = 2 - override val fetchAllTitlesStrategy = FetchAllStrategy.SEARCH_ALL - override val coverPath: String = "" override val dateFormat: SimpleDateFormat = super.dateFormat.apply { diff --git a/multisrc/overrides/heancms/yugenmangas/src/YugenMangas.kt b/multisrc/overrides/heancms/yugenmangas/src/YugenMangas.kt index d34970799..9c51306b7 100644 --- a/multisrc/overrides/heancms/yugenmangas/src/YugenMangas.kt +++ b/multisrc/overrides/heancms/yugenmangas/src/YugenMangas.kt @@ -1,16 +1,9 @@ package eu.kanade.tachiyomi.extension.es.yugenmangas -import android.app.Application -import android.content.SharedPreferences -import android.widget.Toast -import androidx.preference.ListPreference -import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.multisrc.heancms.Genre import eu.kanade.tachiyomi.multisrc.heancms.HeanCms -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import eu.kanade.tachiyomi.source.ConfigurableSource -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +import eu.kanade.tachiyomi.network.interceptor.rateLimitHost +import okhttp3.HttpUrl.Companion.toHttpUrl import java.text.SimpleDateFormat import java.util.TimeZone import java.util.concurrent.TimeUnit @@ -21,26 +14,15 @@ class YugenMangas : "https://yugenmangas.net", "es", "https://api.yugenmangas.net", - ), - ConfigurableSource { - - private val preferences: SharedPreferences by lazy { - Injekt.get().getSharedPreferences("source_$id", 0x0000) - } + ) { // Site changed from Madara to HeanCms. override val versionId = 2 - override val fetchAllTitlesStrategy = when (getfetchAllStrategyPref()) { - "all" -> FetchAllStrategy.SEARCH_ALL - "each" -> FetchAllStrategy.SEARCH_EACH - else -> FetchAllStrategy.NONE - } - override val client = super.client.newBuilder() .connectTimeout(60, TimeUnit.SECONDS) .readTimeout(90, TimeUnit.SECONDS) - .rateLimit(2, 3) + .rateLimitHost(apiUrl.toHttpUrl(), 2, 3) .build() override val coverPath: String = "" @@ -99,42 +81,4 @@ class YugenMangas : Genre("Yaoi", 43), Genre("Yuri", 44), ) - - private fun getfetchAllStrategyPref(): String? { - return preferences.getString(PREF_FETCH_ALL_STRATEGY_KEY, PREF_FETCH_ALL_STRATEGY_DEFAULT) - } - - override fun setupPreferenceScreen(screen: PreferenceScreen) { - val fetchAllStrategyPreference = ListPreference(screen.context).apply { - key = PREF_FETCH_ALL_STRATEGY_KEY - title = PREF_FETCH_ALL_STRATEGY_TITLE - summary = PREF_FETCH_ALL_STRATEGY_SUMMARY - entries = PREF_FETCH_ALL_STRATEGY_ENTRIES - entryValues = PREF_FETCH_ALL_STRATEGY_VALUES - setDefaultValue(PREF_FETCH_ALL_STRATEGY_DEFAULT) - - setOnPreferenceChangeListener { _, newValue -> - Toast.makeText(screen.context, RESTART_MESSAGE, Toast.LENGTH_LONG).show() - true - } - } - - screen.addPreference(fetchAllStrategyPreference) - } - - companion object { - const val PREF_FETCH_ALL_STRATEGY_KEY = "prefFetchAllStrategy" - const val PREF_FETCH_ALL_STRATEGY_TITLE = "Método de búsqueda" - const val PREF_FETCH_ALL_STRATEGY_SUMMARY = "Global: Busca las URLs de todas las series al iniciar la aplicación, lento pero más estable.\n" + - "Individual: Busca la URL de la serie al actualizar, rápido pero puede fallar.\n" + - "Ninguno: Usa la URL con la que fue agregado, tendrá que migrar si la URL cambia.\n" + - "Valor actual: %s" - - val PREF_FETCH_ALL_STRATEGY_ENTRIES = arrayOf("Ninguno", "Individual", "Global") - val PREF_FETCH_ALL_STRATEGY_VALUES = arrayOf("off", "each", "all") - - const val PREF_FETCH_ALL_STRATEGY_DEFAULT = "off" - - const val RESTART_MESSAGE = "Reinicie la aplicación para que los cambios surtan efecto." - } } 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 5bef4cfa8..469f25576 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 @@ -1,7 +1,5 @@ package eu.kanade.tachiyomi.multisrc.heancms -import android.app.Application -import android.content.SharedPreferences import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.source.model.Filter @@ -21,8 +19,6 @@ import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import rx.Observable -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.text.SimpleDateFormat import java.util.Locale @@ -33,10 +29,6 @@ abstract class HeanCms( protected val apiUrl: String = baseUrl.replace("://", "://api."), ) : HttpSource() { - private val preferences: SharedPreferences by lazy { - Injekt.get().getSharedPreferences("source_$id", 0x0000) - } - override val supportsLatest = true override val client: OkHttpClient = network.cloudflareClient @@ -53,14 +45,10 @@ abstract class HeanCms( protected val intl by lazy { HeanCmsIntl(lang) } - protected open val fetchAllTitlesStrategy = FetchAllStrategy.NONE - protected open val coverPath: String = "cover/" protected open val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ", Locale.US) - private var seriesSlugMap: Map? = null - override fun headersBuilder(): Headers.Builder = Headers.Builder() .add("Origin", baseUrl) .add("Referer", "$baseUrl/") @@ -70,7 +58,7 @@ abstract class HeanCms( page = page, order = "desc", orderBy = "total_views", - status = "Ongoing", + status = "All", type = "Comic", ) @@ -89,17 +77,13 @@ abstract class HeanCms( if (json.startsWith("{")) { val result = json.parseAs() - val mangaList = result.data.map { it.toSManga(apiUrl, coverPath, fetchAllTitlesStrategy) } - - fetchAllTitles() + val mangaList = result.data.map { it.toSManga(apiUrl, coverPath) } return MangasPage(mangaList, result.meta?.hasNextPage ?: false) } val mangaList = json.parseAs>() - .map { it.toSManga(apiUrl, coverPath, fetchAllTitlesStrategy) } - - fetchAllTitles() + .map { it.toSManga(apiUrl, coverPath) } return MangasPage(mangaList, hasNextPage = false) } @@ -109,7 +93,7 @@ abstract class HeanCms( page = page, order = "desc", orderBy = "latest", - status = "Ongoing", + status = "All", type = "Comic", ) @@ -137,10 +121,6 @@ abstract class HeanCms( } override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - /** - * Their query search endpoint doesn't return the thumbnails, so we need to do - * later an special parsing to get the thumbnails as well from the slug map. - */ if (query.isNotBlank()) { val searchPayloadObj = HeanCmsSearchPayloadDto(query) val searchPayload = json.encodeToString(searchPayloadObj) @@ -182,96 +162,49 @@ abstract class HeanCms( 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.toSManga(apiUrl, coverPath, seriesSlugMap.orEmpty(), fetchAllTitlesStrategy) } + .map { + it.slug = it.slug.replace(TIMESTAMP_REGEX, "") + it.toSManga(apiUrl, coverPath) + } return MangasPage(mangaList, false) } if (json.startsWith("{")) { val result = json.parseAs() - val mangaList = result.data.map { it.toSManga(apiUrl, coverPath, fetchAllTitlesStrategy) } - - fetchAllTitles() + val mangaList = result.data.map { it.toSManga(apiUrl, coverPath) } return MangasPage(mangaList, result.meta?.hasNextPage ?: false) } val mangaList = json.parseAs>() - .map { it.toSManga(apiUrl, coverPath, fetchAllTitlesStrategy) } - - fetchAllTitles() + .map { it.toSManga(apiUrl, coverPath) } return MangasPage(mangaList, hasNextPage = false) } - override fun getMangaUrl(manga: SManga): String { - val seriesSlug = manga.url - .substringAfterLast("/") - .replace(TIMESTAMP_REGEX, "") - - val currentSlug = if (fetchAllTitlesStrategy == FetchAllStrategy.SEARCH_EACH) { - preferences.slugMap[seriesSlug] ?: manga.url.substringAfterLast("/") - } else { - seriesSlugMap?.get(seriesSlug)?.slug ?: manga.url.substringAfterLast("/") - } - - return "$baseUrl/series/$currentSlug" - } + override fun getMangaUrl(manga: SManga) = baseUrl + manga.url override fun mangaDetailsRequest(manga: SManga): Request { - if (fetchAllTitlesStrategy == FetchAllStrategy.SEARCH_EACH) { - val searchQuery = manga.title - val searchPayloadObj = HeanCmsSearchPayloadDto(searchQuery) - val searchPayload = json.encodeToString(searchPayloadObj) - .toRequestBody(JSON_MEDIA_TYPE) - - val apiHeaders = headersBuilder() - .add("Accept", ACCEPT_JSON) - .add("Content-Type", searchPayload.contentType().toString()) - .build() - - val mangaSlug = manga.url - .substringAfterLast("/") - - return POST("$apiUrl/series/search#$mangaSlug", apiHeaders, searchPayload) - } - val seriesSlug = manga.url .substringAfterLast("/") - .replace(TIMESTAMP_REGEX, "") - - fetchAllTitles() - - val seriesDetails = seriesSlugMap?.get(seriesSlug) - val currentSlug = seriesDetails?.slug ?: manga.url.substringAfterLast("/") - val currentStatus = seriesDetails?.status ?: manga.status val apiHeaders = headersBuilder() .add("Accept", ACCEPT_JSON) .build() - return GET("$apiUrl/series/$currentSlug#$currentStatus", apiHeaders) + return GET("$apiUrl/series/$seriesSlug#${manga.status}", apiHeaders) } override fun mangaDetailsParse(response: Response): SManga { val mangaStatus = response.request.url.fragment?.toIntOrNull() ?: SManga.UNKNOWN - val result = runCatching { - if (fetchAllTitlesStrategy == FetchAllStrategy.SEARCH_EACH) { - val originalSlug = response.request.url.fragment!! - val searchResult = response.parseAs>() - searchResultToSeries(originalSlug, searchResult) - } else { - response.parseAs() - } - } + val result = runCatching { response.parseAs() } - val seriesDetails = result.getOrNull()?.toSManga(apiUrl, coverPath, fetchAllTitlesStrategy) + val seriesDetails = result.getOrNull()?.toSManga(apiUrl, coverPath) ?: throw Exception(intl.urlChangedError(name)) return seriesDetails.apply { @@ -283,13 +216,7 @@ abstract class HeanCms( override fun chapterListRequest(manga: SManga): Request = mangaDetailsRequest(manga) override fun chapterListParse(response: Response): List { - val result = if (fetchAllTitlesStrategy == FetchAllStrategy.SEARCH_EACH) { - val originalSlug = response.request.url.fragment!! - val searchResult = response.parseAs>() - searchResultToSeries(originalSlug, searchResult) - } else { - response.parseAs() - } + val result = response.parseAs() val currentTimestamp = System.currentTimeMillis() @@ -300,31 +227,6 @@ abstract class HeanCms( .reversed() } - private fun searchResultToSeries(originalSlug: String, searchResult: List): HeanCmsSeriesDto { - val mangaSlug = searchResult - .filter { it.type == "Comic" } - .map { it.slug } - .find { it.startsWith(originalSlug) || originalSlug.startsWith(it) } - ?: originalSlug - - val apiHeaders = headersBuilder() - .add("Accept", ACCEPT_JSON) - .build() - - val detailsRequest = GET("$apiUrl/series/$mangaSlug", apiHeaders) - val result = client.newCall(detailsRequest).execute() - .parseAs() - - val permSlug = result.slug - .substringAfterLast("/") - .replace(TIMESTAMP_REGEX, "") - - preferences.slugMap = preferences.slugMap.toMutableMap() - .also { it[permSlug] = result.slug.substringAfterLast("/") } - - return result - } - override fun getChapterUrl(chapter: SChapter): String = baseUrl + chapter.url override fun pageListRequest(chapter: SChapter): Request { @@ -379,83 +281,6 @@ abstract class HeanCms( protected open fun getGenreList(): List = emptyList() - protected open fun fetchAllTitles() { - if (!seriesSlugMap.isNullOrEmpty() || fetchAllTitlesStrategy != FetchAllStrategy.SEARCH_ALL) { - 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 { - 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 var SharedPreferences.slugMap: MutableMap - get() { - val jsonMap = getString(PREF_URL_MAP, "{}")!! - val slugMap = runCatching { json.decodeFromString>(jsonMap) } - return slugMap.getOrNull()?.toMutableMap() ?: mutableMapOf() - } - set(newSlugMap) { - edit() - .putString(PREF_URL_MAP, json.encodeToString(newSlugMap)) - .commit() - } - - 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, - ) - }, - ) - } - override fun getFilterList(): FilterList { val genres = getGenreList() @@ -478,28 +303,14 @@ abstract class HeanCms( private inline fun List<*>.firstInstanceOrNull(): R? = filterIsInstance().firstOrNull() - /** - * 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) - - enum class FetchAllStrategy { - NONE, - SEARCH_EACH, - SEARCH_ALL, - } - companion object { private const val ACCEPT_IMAGE = "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8" private const val ACCEPT_JSON = "application/json, text/plain, */*" private val JSON_MEDIA_TYPE = "application/json".toMediaType() - val TIMESTAMP_REGEX = "-\\d+$".toRegex() + val TIMESTAMP_REGEX = """-\d{13}$""".toRegex() const val SEARCH_PREFIX = "slug:" - - private const val PREF_URL_MAP = "pref_url_map" } } 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 8116c7dea..201220bb1 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 @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.multisrc.heancms -import eu.kanade.tachiyomi.multisrc.heancms.HeanCms.FetchAllStrategy import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import kotlinx.serialization.SerialName @@ -27,7 +26,7 @@ data class HeanCmsQuerySearchMetaDto( @Serializable data class HeanCmsSearchDto( val description: String? = null, - @SerialName("series_slug") val slug: String, + @SerialName("series_slug") var slug: String, @SerialName("series_type") val type: String, val title: String, val thumbnail: String? = null, @@ -36,15 +35,9 @@ data class HeanCmsSearchDto( fun toSManga( apiUrl: String, coverPath: String, - slugMap: Map, - fetchStrategy: FetchAllStrategy = FetchAllStrategy.NONE, ): SManga = SManga.create().apply { - val slug = if (fetchStrategy == FetchAllStrategy.NONE) slug else slug.replace(HeanCms.TIMESTAMP_REGEX, "") - val thumbnailFileName = slugMap[slug]?.thumbnailFileName - title = this@HeanCmsSearchDto.title thumbnail_url = thumbnail?.toAbsoluteThumbnailUrl(apiUrl, coverPath) - ?: thumbnailFileName?.toAbsoluteThumbnailUrl(apiUrl, coverPath) url = "/series/$slug" } } @@ -67,10 +60,8 @@ data class HeanCmsSeriesDto( fun toSManga( apiUrl: String, coverPath: String, - fetchStrategy: FetchAllStrategy = FetchAllStrategy.NONE, ): SManga = SManga.create().apply { val descriptionBody = this@HeanCmsSeriesDto.description?.let(Jsoup::parseBodyFragment) - val slug = if (fetchStrategy == FetchAllStrategy.NONE) slug else slug.replace(HeanCms.TIMESTAMP_REGEX, "") title = this@HeanCmsSeriesDto.title author = this@HeanCmsSeriesDto.author?.trim() 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 36403a2ab..6418abd61 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 = 15 + override val baseVersionCode: Int = 16 override val sources = listOf( SingleLang("Glorious Scan", "https://gloriousscan.com", "pt-BR", overrideVersionCode = 17),