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.app.Application
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST 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.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
@ -25,6 +28,7 @@ import okhttp3.Response
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.io.IOException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -33,12 +37,28 @@ abstract class HeanCms(
override val baseUrl: String, override val baseUrl: String,
override val lang: String, override val lang: String,
protected val apiUrl: String = baseUrl.replace("://", "://api."), protected val apiUrl: String = baseUrl.replace("://", "://api."),
) : HttpSource() { ) : ConfigurableSource, HttpSource() {
private val preferences: SharedPreferences by lazy { private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) 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 supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient override val client: OkHttpClient = network.cloudflareClient
@ -402,16 +422,18 @@ abstract class HeanCms(
val currentTimestamp = System.currentTimeMillis() val currentTimestamp = System.currentTimeMillis()
val showPaidChapters = preferences.showPaidChapters
if (useNewQueryEndpoint) { if (useNewQueryEndpoint) {
return result.seasons.orEmpty() return result.seasons.orEmpty()
.flatMap { it.chapters.orEmpty() } .flatMap { it.chapters.orEmpty() }
.filter { it.price == 0 } .filter { it.price == 0 || showPaidChapters }
.map { it.toSChapter(result.slug, mangaSubDirectory, dateFormat, slugStrategy) } .map { it.toSChapter(result.slug, mangaSubDirectory, dateFormat, slugStrategy) }
.filter { it.date_upload <= currentTimestamp } .filter { it.date_upload <= currentTimestamp }
} }
return result.chapters.orEmpty() return result.chapters.orEmpty()
.filter { it.price == 0 } .filter { it.price == 0 || showPaidChapters }
.map { it.toSChapter(result.slug, mangaSubDirectory, dateFormat, slugStrategy) } .map { it.toSChapter(result.slug, mangaSubDirectory, dateFormat, slugStrategy) }
.filter { it.date_upload <= currentTimestamp } .filter { it.date_upload <= currentTimestamp }
.reversed() .reversed()
@ -442,7 +464,7 @@ abstract class HeanCms(
return GET(baseUrl + chapter.url, headers) return GET(baseUrl + chapter.url, headers)
} }
val chapterId = chapter.url.substringAfterLast("#") val chapterId = chapter.url.substringAfterLast("#").substringBefore("-paid")
val apiHeaders = headersBuilder() val apiHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON) .add("Accept", ACCEPT_JSON)
@ -453,25 +475,37 @@ abstract class HeanCms(
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
if (useNewQueryEndpoint) { if (useNewQueryEndpoint) {
val paidChapter = response.request.url.fragment?.contains("-paid")
val document = response.asJsoup() val document = response.asJsoup()
val images = document.selectFirst("div.min-h-screen > div.container > p.items-center") 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 -> return images?.select("img").orEmpty().mapIndexed { i, img ->
val imageUrl = if (img.hasClass("lazy")) img.absUrl("data-src") else img.absUrl("src") val imageUrl = if (img.hasClass("lazy")) img.absUrl("data-src") else img.absUrl("src")
Page(i, "", imageUrl) Page(i, "", imageUrl)
} }
} }
return response.parseAs<HeanCmsReaderDto>().content?.images.orEmpty() val images = response.parseAs<HeanCmsReaderDto>().content?.images.orEmpty()
.filterNot { imageUrl -> val paidChapter = response.request.url.fragment?.contains("-paid")
// 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. if (images.isEmpty() && paidChapter == true) {
imageUrl throw IOException(intl.paidChapterError)
.removeSuffix("/") }
.substringAfterLast("/")
.startsWith(".") 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 -> .mapIndexed { i, url ->
Page(i, imageUrl = if (url.startsWith("http")) url else "$apiUrl/$url") Page(i, imageUrl = if (url.startsWith("http")) url else "$apiUrl/$url")
} }
@ -639,9 +673,12 @@ abstract class HeanCms(
set(newSlugMap) { set(newSlugMap) {
edit() edit()
.putString(PREF_URL_MAP_SLUG, json.encodeToString(newSlugMap)) .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 { companion object {
private const val ACCEPT_IMAGE = "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8" 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 const val ACCEPT_JSON = "application/json, text/plain, */*"
@ -655,5 +692,8 @@ abstract class HeanCms(
const val SEARCH_PREFIX = "slug:" const val SEARCH_PREFIX = "slug:"
private const val PREF_URL_MAP_SLUG = "pref_url_map" 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 { ): SChapter = SChapter.create().apply {
val seriesSlugOnly = seriesSlug.toPermSlugIfNeeded(slugStrategy) val seriesSlugOnly = seriesSlug.toPermSlugIfNeeded(slugStrategy)
name = this@HeanCmsChapterDto.name.trim() name = this@HeanCmsChapterDto.name.trim()
if (price != 0) {
name += " \uD83D\uDD12"
}
date_upload = runCatching { dateFormat.parse(createdAt)?.time } date_upload = runCatching { dateFormat.parse(createdAt)?.time }
.getOrNull() ?: 0L .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 themeClass = "HeanCms"
override val baseVersionCode: Int = 19 override val baseVersionCode: Int = 20
override val sources = listOf( override val sources = listOf(
SingleLang("Glorious Scan", "https://gloriousscan.com", "pt-BR", overrideVersionCode = 17), 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." 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) { fun urlChangedError(sourceName: String): String = when (availableLang) {
BRAZILIAN_PORTUGUESE -> BRAZILIAN_PORTUGUESE ->
"A URL da série mudou. Migre de $sourceName " + "A URL da série mudou. Migre de $sourceName " +