[Comick] Various updates (#19118)
* Add setting for Score positioning Localization support for settings * Simplify score position evaluation Reformat code * Include backup domain in Manifest * Refine logic for picking the first volume cover * Officially moved their main TLD * Fallback to default value instead of empty string
This commit is contained in:
parent
76d9997262
commit
67c4e30577
|
@ -14,7 +14,9 @@
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
<data android:scheme="https" />
|
<data android:scheme="https" />
|
||||||
|
<data android:host="comick.ink" />
|
||||||
<data android:host="comick.app" />
|
<data android:host="comick.app" />
|
||||||
|
<data android:host="comick.fun" />
|
||||||
<data android:pathPattern="/comic/.*/..*" />
|
<data android:pathPattern="/comic/.*/..*" />
|
||||||
<data android:pathPattern="/comic/..*" />
|
<data android:pathPattern="/comic/..*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
ignored_groups_title=Ignored Groups
|
||||||
|
ignored_groups_summary=Chapters from these groups won't be shown.\nOne group name per line (case-insensitive)
|
||||||
|
include_tags_title=Include Tags
|
||||||
|
include_tags_on=More specific, but might contain spoilers!
|
||||||
|
include_tags_off=Only the broader genres
|
||||||
|
update_cover_title=Update Covers
|
||||||
|
update_cover_on=Keep cover updated
|
||||||
|
update_cover_off=Prefer first cover
|
||||||
|
score_position_title=Score Position in the Description
|
||||||
|
score_position_top=Top
|
||||||
|
score_position_middle=Middle
|
||||||
|
score_position_bottom=Bottom
|
||||||
|
score_position_none=Hide Score
|
|
@ -0,0 +1,13 @@
|
||||||
|
ignored_groups_title=Grupos Ignorados
|
||||||
|
ignored_groups_summary=Capítulos desses grupos não aparecerão.\nUm grupo por linha
|
||||||
|
include_tags_title=Incluir Tags
|
||||||
|
include_tags_on=Mais detalhadas, mas podem conter spoilers
|
||||||
|
include_tags_off=Apenas os gêneros básicos
|
||||||
|
update_cover_title=Atualizar Capas
|
||||||
|
update_cover_on=Manter capas atualizadas
|
||||||
|
update_cover_off=Usar apenas a primeira capa
|
||||||
|
score_position_title=Posição da Nota na Descrição
|
||||||
|
score_position_top=Topo
|
||||||
|
score_position_middle=Meio
|
||||||
|
score_position_bottom=Final
|
||||||
|
score_position_none=Sem Nota
|
|
@ -6,8 +6,12 @@ ext {
|
||||||
extName = 'Comick'
|
extName = 'Comick'
|
||||||
pkgNameSuffix = 'all.comickfun'
|
pkgNameSuffix = 'all.comickfun'
|
||||||
extClass = '.ComickFunFactory'
|
extClass = '.ComickFunFactory'
|
||||||
extVersionCode = 39
|
extVersionCode = 40
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":lib-i18n"))
|
||||||
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -3,8 +3,10 @@ package eu.kanade.tachiyomi.extension.all.comickfun
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.preference.EditTextPreference
|
import androidx.preference.EditTextPreference
|
||||||
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
|
import eu.kanade.tachiyomi.lib.i18n.Intl
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
@ -36,7 +38,7 @@ abstract class ComickFun(
|
||||||
|
|
||||||
override val name = "Comick"
|
override val name = "Comick"
|
||||||
|
|
||||||
override val baseUrl = "https://comick.app"
|
override val baseUrl = "https://comick.ink"
|
||||||
|
|
||||||
private val apiUrl = "https://api.comick.fun"
|
private val apiUrl = "https://api.comick.fun"
|
||||||
|
|
||||||
|
@ -51,6 +53,15 @@ abstract class ComickFun(
|
||||||
|
|
||||||
private lateinit var searchResponse: List<SearchManga>
|
private lateinit var searchResponse: List<SearchManga>
|
||||||
|
|
||||||
|
private val intl by lazy {
|
||||||
|
Intl(
|
||||||
|
language = lang,
|
||||||
|
baseLanguage = "en",
|
||||||
|
availableLanguages = setOf("en", "pt-BR"),
|
||||||
|
classLoader = this::class.java.classLoader!!,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -58,9 +69,8 @@ abstract class ComickFun(
|
||||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
EditTextPreference(screen.context).apply {
|
EditTextPreference(screen.context).apply {
|
||||||
key = IGNORED_GROUPS_PREF
|
key = IGNORED_GROUPS_PREF
|
||||||
title = "Ignored Groups"
|
title = intl["ignored_groups_title"]
|
||||||
summary =
|
summary = intl["ignored_groups_summary"]
|
||||||
"Chapters from these groups won't be shown.\nOne group name per line (case-insensitive)"
|
|
||||||
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
preferences.edit()
|
preferences.edit()
|
||||||
|
@ -71,9 +81,9 @@ abstract class ComickFun(
|
||||||
|
|
||||||
SwitchPreferenceCompat(screen.context).apply {
|
SwitchPreferenceCompat(screen.context).apply {
|
||||||
key = INCLUDE_MU_TAGS_PREF
|
key = INCLUDE_MU_TAGS_PREF
|
||||||
title = "Include Tags"
|
title = intl["include_tags_title"]
|
||||||
summaryOn = "More specific, but might contain spoilers!"
|
summaryOn = intl["include_tags_on"]
|
||||||
summaryOff = "Only the broader genres"
|
summaryOff = intl["include_tags_off"]
|
||||||
setDefaultValue(INCLUDE_MU_TAGS_DEFAULT)
|
setDefaultValue(INCLUDE_MU_TAGS_DEFAULT)
|
||||||
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
@ -85,9 +95,9 @@ abstract class ComickFun(
|
||||||
|
|
||||||
SwitchPreferenceCompat(screen.context).apply {
|
SwitchPreferenceCompat(screen.context).apply {
|
||||||
key = FIRST_COVER_PREF
|
key = FIRST_COVER_PREF
|
||||||
title = "Update Covers"
|
title = intl["update_cover_title"]
|
||||||
summaryOff = "Prefer first cover"
|
summaryOff = intl["update_cover_off"]
|
||||||
summaryOn = "Keep cover updated"
|
summaryOn = intl["update_cover_on"]
|
||||||
setDefaultValue(FIRST_COVER_DEFAULT)
|
setDefaultValue(FIRST_COVER_DEFAULT)
|
||||||
|
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
@ -96,6 +106,30 @@ abstract class ComickFun(
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
}.also(screen::addPreference)
|
}.also(screen::addPreference)
|
||||||
|
|
||||||
|
ListPreference(screen.context).apply {
|
||||||
|
key = SCORE_POSITION_PREF
|
||||||
|
title = intl["score_position_title"]
|
||||||
|
summary = "%s"
|
||||||
|
entries = arrayOf(
|
||||||
|
intl["score_position_top"],
|
||||||
|
intl["score_position_middle"],
|
||||||
|
intl["score_position_bottom"],
|
||||||
|
intl["score_position_none"],
|
||||||
|
)
|
||||||
|
entryValues = arrayOf(SCORE_POSITION_DEFAULT, "middle", "bottom", "none")
|
||||||
|
setDefaultValue(SCORE_POSITION_DEFAULT)
|
||||||
|
|
||||||
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
|
val selected = newValue as String
|
||||||
|
val index = findIndexOfValue(selected)
|
||||||
|
val entry = entryValues[index] as String
|
||||||
|
|
||||||
|
preferences.edit()
|
||||||
|
.putString(SCORE_POSITION_PREF, entry)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
|
}.also(screen::addPreference)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val SharedPreferences.ignoredGroups: Set<String>
|
private val SharedPreferences.ignoredGroups: Set<String>
|
||||||
|
@ -114,6 +148,9 @@ abstract class ComickFun(
|
||||||
private val SharedPreferences.updateCover: Boolean
|
private val SharedPreferences.updateCover: Boolean
|
||||||
get() = getBoolean(FIRST_COVER_PREF, FIRST_COVER_DEFAULT)
|
get() = getBoolean(FIRST_COVER_PREF, FIRST_COVER_DEFAULT)
|
||||||
|
|
||||||
|
private val SharedPreferences.scorePosition: String
|
||||||
|
get() = getString(SCORE_POSITION_PREF, SCORE_POSITION_DEFAULT) ?: SCORE_POSITION_DEFAULT
|
||||||
|
|
||||||
init {
|
init {
|
||||||
preferences.newLineIgnoredGroups()
|
preferences.newLineIgnoredGroups()
|
||||||
}
|
}
|
||||||
|
@ -314,13 +351,15 @@ abstract class ComickFun(
|
||||||
private fun mangaDetailsParse(response: Response, manga: SManga): SManga {
|
private fun mangaDetailsParse(response: Response, manga: SManga): SManga {
|
||||||
val mangaData = response.parseAs<Manga>()
|
val mangaData = response.parseAs<Manga>()
|
||||||
if (!preferences.updateCover && manga.thumbnail_url != mangaData.comic.cover) {
|
if (!preferences.updateCover && manga.thumbnail_url != mangaData.comic.cover) {
|
||||||
if (manga.thumbnail_url.toString().endsWith("#")) {
|
if (manga.thumbnail_url.toString().endsWith("#1")) {
|
||||||
return mangaData.toSManga(
|
return mangaData.toSManga(
|
||||||
includeMuTags = preferences.includeMuTags,
|
includeMuTags = preferences.includeMuTags,
|
||||||
|
scorePosition = preferences.scorePosition,
|
||||||
covers = listOf(
|
covers = listOf(
|
||||||
MDcovers(
|
MDcovers(
|
||||||
b2key = manga.thumbnail_url?.removeSuffix("#")
|
b2key = manga.thumbnail_url?.substringBeforeLast("#")
|
||||||
?.substringAfterLast("/"),
|
?.substringAfterLast("/"),
|
||||||
|
vol = "1",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -329,7 +368,10 @@ abstract class ComickFun(
|
||||||
"$apiUrl/comic/${mangaData.comic.slug ?: mangaData.comic.hid}/covers?tachiyomi=true"
|
"$apiUrl/comic/${mangaData.comic.slug ?: mangaData.comic.hid}/covers?tachiyomi=true"
|
||||||
val covers = client.newCall(GET(coversUrl)).execute()
|
val covers = client.newCall(GET(coversUrl)).execute()
|
||||||
.parseAs<Covers>().md_covers.reversed()
|
.parseAs<Covers>().md_covers.reversed()
|
||||||
return mangaData.toSManga(includeMuTags = preferences.includeMuTags, covers = covers)
|
return mangaData.toSManga(
|
||||||
|
includeMuTags = preferences.includeMuTags,
|
||||||
|
covers = if (covers.any { it.vol == "1" }) covers.filter { it.vol == "1" } else covers,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return mangaData.toSManga(includeMuTags = preferences.includeMuTags)
|
return mangaData.toSManga(includeMuTags = preferences.includeMuTags)
|
||||||
}
|
}
|
||||||
|
@ -440,6 +482,8 @@ abstract class ComickFun(
|
||||||
private const val MIGRATED_IGNORED_GROUPS = "MigratedIgnoredGroups"
|
private const val MIGRATED_IGNORED_GROUPS = "MigratedIgnoredGroups"
|
||||||
private const val FIRST_COVER_PREF = "DefaultCover"
|
private const val FIRST_COVER_PREF = "DefaultCover"
|
||||||
private const val FIRST_COVER_DEFAULT = true
|
private const val FIRST_COVER_DEFAULT = true
|
||||||
|
private const val SCORE_POSITION_PREF = "ScorePosition"
|
||||||
|
private const val SCORE_POSITION_DEFAULT = "top"
|
||||||
private const val limit = 20
|
private const val limit = 20
|
||||||
val dateFormat by lazy {
|
val dateFormat by lazy {
|
||||||
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH).apply {
|
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH).apply {
|
||||||
|
|
|
@ -13,7 +13,6 @@ data class SearchManga(
|
||||||
val title: String,
|
val title: String,
|
||||||
@SerialName("md_covers") val mdCovers: List<MDcovers> = emptyList(),
|
@SerialName("md_covers") val mdCovers: List<MDcovers> = emptyList(),
|
||||||
@SerialName("cover_url") val cover: String? = null,
|
@SerialName("cover_url") val cover: String? = null,
|
||||||
|
|
||||||
) {
|
) {
|
||||||
fun toSManga() = SManga.create().apply {
|
fun toSManga() = SManga.create().apply {
|
||||||
// appending # at end as part of migration from slug to hid
|
// appending # at end as part of migration from slug to hid
|
||||||
|
@ -31,24 +30,26 @@ data class Manga(
|
||||||
val genres: List<Name> = emptyList(),
|
val genres: List<Name> = emptyList(),
|
||||||
val demographic: String? = null,
|
val demographic: String? = null,
|
||||||
) {
|
) {
|
||||||
fun toSManga(includeMuTags: Boolean = false, covers: List<MDcovers>? = null) =
|
fun toSManga(
|
||||||
|
includeMuTags: Boolean = false,
|
||||||
|
scorePosition: String = "",
|
||||||
|
covers: List<MDcovers>? = null,
|
||||||
|
) =
|
||||||
SManga.create().apply {
|
SManga.create().apply {
|
||||||
// appennding # at end as part of migration from slug to hid
|
// appennding # at end as part of migration from slug to hid
|
||||||
url = "/comic/${comic.hid}#"
|
url = "/comic/${comic.hid}#"
|
||||||
title = comic.title
|
title = comic.title
|
||||||
description = buildString {
|
description = buildString {
|
||||||
if (!comic.score.isNullOrEmpty()) {
|
if (scorePosition == "top") append(comic.fancyScore)
|
||||||
val stars = comic.score.toBigDecimal().div(BigDecimal(2))
|
|
||||||
.setScale(0, RoundingMode.HALF_UP).toInt()
|
|
||||||
append("★".repeat(stars))
|
|
||||||
if (stars < 5) append("☆".repeat(5 - stars))
|
|
||||||
append(" ${comic.score} ")
|
|
||||||
}
|
|
||||||
val desc = comic.desc?.beautifyDescription()
|
val desc = comic.desc?.beautifyDescription()
|
||||||
if (!desc.isNullOrEmpty()) {
|
if (!desc.isNullOrEmpty()) {
|
||||||
if (this.isNotEmpty()) append("\n\n")
|
if (this.isNotEmpty()) append("\n\n")
|
||||||
append(desc)
|
append(desc)
|
||||||
}
|
}
|
||||||
|
if (scorePosition == "middle") {
|
||||||
|
if (this.isNotEmpty()) append("\n\n")
|
||||||
|
append(comic.fancyScore)
|
||||||
|
}
|
||||||
if (comic.altTitles.isNotEmpty()) {
|
if (comic.altTitles.isNotEmpty()) {
|
||||||
if (this.isNotEmpty()) append("\n\n")
|
if (this.isNotEmpty()) append("\n\n")
|
||||||
append("Alternative Titles:\n")
|
append("Alternative Titles:\n")
|
||||||
|
@ -58,6 +59,10 @@ data class Manga(
|
||||||
}.joinToString("\n"),
|
}.joinToString("\n"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (scorePosition == "bottom") {
|
||||||
|
if (this.isNotEmpty()) append("\n\n")
|
||||||
|
append(comic.fancyScore)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status = comic.status.parseStatus(comic.translationComplete)
|
status = comic.status.parseStatus(comic.translationComplete)
|
||||||
|
@ -106,6 +111,17 @@ data class Comic(
|
||||||
"cn" -> Name("Manhua")
|
"cn" -> Name("Manhua")
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
val fancyScore: String = if (score.isNullOrEmpty()) {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
val stars = score.toBigDecimal().div(BigDecimal(2))
|
||||||
|
.setScale(0, RoundingMode.HALF_UP).toInt()
|
||||||
|
buildString {
|
||||||
|
append("★".repeat(stars))
|
||||||
|
if (stars < 5) append("☆".repeat(5 - stars))
|
||||||
|
append(" $score")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -131,6 +147,7 @@ data class Covers(
|
||||||
@Serializable
|
@Serializable
|
||||||
data class MDcovers(
|
data class MDcovers(
|
||||||
val b2key: String?,
|
val b2key: String?,
|
||||||
|
val vol: String? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
|
@ -37,8 +37,9 @@ internal fun Int?.parseStatus(translationComplete: Boolean?): Int {
|
||||||
internal fun parseCover(thumbnailUrl: String?, mdCovers: List<MDcovers>): String? {
|
internal fun parseCover(thumbnailUrl: String?, mdCovers: List<MDcovers>): String? {
|
||||||
val b2key = mdCovers.firstOrNull()?.b2key
|
val b2key = mdCovers.firstOrNull()?.b2key
|
||||||
?: return thumbnailUrl
|
?: return thumbnailUrl
|
||||||
|
val vol = mdCovers.firstOrNull()?.vol.orEmpty()
|
||||||
|
|
||||||
return thumbnailUrl?.replaceAfterLast("/", "$b2key#")
|
return thumbnailUrl?.replaceAfterLast("/", "$b2key#$vol")
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun thumbnailIntercept(chain: Interceptor.Chain): Response {
|
internal fun thumbnailIntercept(chain: Interceptor.Chain): Response {
|
||||||
|
|
Loading…
Reference in New Issue