HeanCMS: Added paid chapter preference (#19006)

* Added paid chapter preference

* Added requested changes and fixed a warning

* Paid chapters now have a 🔒 next to it

* Added Spanish translations

* Converted protected into private functions

* Improved the error message for unavailable paid chapters.

* Revert "Converted protected into private functions"

This reverts commit 864186d7b1f96b867c2d26e4410a6578e53223d5.

* Added spanish translation for error message

* Fixed: paidStatus would be set if price was 0

* Fixed: paidStatus would be set if price was null

* Fixed: chapterId retrieval from url in pageListRequest
This commit is contained in:
Buhbbl 2023-11-20 17:41:41 +01:00 committed by GitHub
parent 496f8d26f3
commit ac077880f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 84 additions and 16 deletions

View File

@ -2,8 +2,11 @@ package eu.kanade.tachiyomi.multisrc.heancms
import android.app.Application
import android.content.SharedPreferences
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
@ -25,6 +28,7 @@ import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Locale
@ -33,12 +37,28 @@ abstract class HeanCms(
override val baseUrl: String,
override val lang: String,
protected val apiUrl: String = baseUrl.replace("://", "://api."),
) : HttpSource() {
) : ConfigurableSource, HttpSource() {
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
SwitchPreferenceCompat(screen.context).apply {
key = SHOW_PAID_CHAPTERS_PREF
title = intl.prefShowPaidChapterTitle
summaryOn = intl.prefShowPaidChapterSummaryOn
summaryOff = intl.prefShowPaidChapterSummaryOff
setDefaultValue(SHOW_PAID_CHAPTERS_DEFAULT)
setOnPreferenceChangeListener { _, newValue ->
preferences.edit()
.putBoolean(SHOW_PAID_CHAPTERS_PREF, newValue as Boolean)
.commit()
}
}.also(screen::addPreference)
}
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
@ -402,16 +422,18 @@ abstract class HeanCms(
val currentTimestamp = System.currentTimeMillis()
val showPaidChapters = preferences.showPaidChapters
if (useNewQueryEndpoint) {
return result.seasons.orEmpty()
.flatMap { it.chapters.orEmpty() }
.filter { it.price == 0 }
.filter { it.price == 0 || showPaidChapters }
.map { it.toSChapter(result.slug, mangaSubDirectory, dateFormat, slugStrategy) }
.filter { it.date_upload <= currentTimestamp }
}
return result.chapters.orEmpty()
.filter { it.price == 0 }
.filter { it.price == 0 || showPaidChapters }
.map { it.toSChapter(result.slug, mangaSubDirectory, dateFormat, slugStrategy) }
.filter { it.date_upload <= currentTimestamp }
.reversed()
@ -442,7 +464,7 @@ abstract class HeanCms(
return GET(baseUrl + chapter.url, headers)
}
val chapterId = chapter.url.substringAfterLast("#")
val chapterId = chapter.url.substringAfterLast("#").substringBefore("-paid")
val apiHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON)
@ -453,25 +475,37 @@ abstract class HeanCms(
override fun pageListParse(response: Response): List<Page> {
if (useNewQueryEndpoint) {
val paidChapter = response.request.url.fragment?.contains("-paid")
val document = response.asJsoup()
val images = document.selectFirst("div.min-h-screen > div.container > p.items-center")
if (images == null && paidChapter == true) {
throw IOException(intl.paidChapterError)
}
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
// with a dot in the file name. To avoid download errors, these are removed.
imageUrl
.removeSuffix("/")
.substringAfterLast("/")
.startsWith(".")
}
val images = response.parseAs<HeanCmsReaderDto>().content?.images.orEmpty()
val paidChapter = response.request.url.fragment?.contains("-paid")
if (images.isEmpty() && paidChapter == true) {
throw IOException(intl.paidChapterError)
}
return images.filterNot { imageUrl ->
// Their image server returns HTTP 403 for hidden files that starts
// with a dot in the file name. To avoid download errors, these are removed.
imageUrl
.removeSuffix("/")
.substringAfterLast("/")
.startsWith(".")
}
.mapIndexed { i, url ->
Page(i, imageUrl = if (url.startsWith("http")) url else "$apiUrl/$url")
}
@ -639,9 +673,12 @@ abstract class HeanCms(
set(newSlugMap) {
edit()
.putString(PREF_URL_MAP_SLUG, json.encodeToString(newSlugMap))
.commit()
.apply()
}
private val SharedPreferences.showPaidChapters: Boolean
get() = getBoolean(SHOW_PAID_CHAPTERS_PREF, SHOW_PAID_CHAPTERS_DEFAULT)
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, */*"
@ -655,5 +692,8 @@ abstract class HeanCms(
const val SEARCH_PREFIX = "slug:"
private const val PREF_URL_MAP_SLUG = "pref_url_map"
private const val SHOW_PAID_CHAPTERS_PREF = "pref_show_paid_chap"
private const val SHOW_PAID_CHAPTERS_DEFAULT = false
}
}

View File

@ -120,9 +120,17 @@ data class HeanCmsChapterDto(
): SChapter = SChapter.create().apply {
val seriesSlugOnly = seriesSlug.toPermSlugIfNeeded(slugStrategy)
name = this@HeanCmsChapterDto.name.trim()
if (price != 0) {
name += " \uD83D\uDD12"
}
date_upload = runCatching { dateFormat.parse(createdAt)?.time }
.getOrNull() ?: 0L
url = "/$mangaSubDirectory/$seriesSlugOnly/$slug#$id"
val paidStatus = if (price != 0 && price != null) "-paid" else ""
url = "/$mangaSubDirectory/$seriesSlugOnly/$slug#$id$paidStatus"
}
}

View File

@ -9,7 +9,7 @@ class HeanCmsGenerator : ThemeSourceGenerator {
override val themeClass = "HeanCms"
override val baseVersionCode: Int = 19
override val baseVersionCode: Int = 20
override val sources = listOf(
SingleLang("Glorious Scan", "https://gloriousscan.com", "pt-BR", overrideVersionCode = 17),

View File

@ -76,6 +76,26 @@ class HeanCmsIntl(lang: String) {
else -> "Filters will be ignored if the search is not empty."
}
val prefShowPaidChapterTitle: String = when (availableLang) {
SPANISH -> "Mostrar capítulos de pago"
else -> "Display paid chapters"
}
val prefShowPaidChapterSummaryOn: String = when (availableLang) {
SPANISH -> "Se mostrarán capítulos de pago. Deberá iniciar sesión"
else -> "Paid chapters will appear. A login might be needed!"
}
val prefShowPaidChapterSummaryOff: String = when (availableLang) {
SPANISH -> "Solo se mostrarán los capítulos gratuitos"
else -> "Only free chapters will be displayed."
}
val paidChapterError: String = when (availableLang) {
SPANISH -> "Capítulo no disponible. Debe iniciar sesión en Webview y tener el capítulo comprado."
else -> "Paid chapter unavailable.\nA login/purchase might be needed (using webview)."
}
fun urlChangedError(sourceName: String): String = when (availableLang) {
BRAZILIAN_PORTUGUESE ->
"A URL da série mudou. Migre de $sourceName " +