Comick: option to de-duplicate chapters based on "score" (#8923)

* Adds the ability to automatically filter chapters in comick based on their "score"

* wording update, version bump

* use chapter_ prefix when referring to score filtering

* Update src/all/comickfun/assets/i18n/messages_en.properties

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>

* change syntax for filtering based on PR feedback

* figured out how to make an extension

* updated, thanks!

---------

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>
This commit is contained in:
Tejas Sharma 2025-05-23 19:53:06 -07:00 committed by Draff
parent c8cc594055
commit 36295f9b69
Signed by: Draff
GPG Key ID: E8A89F3211677653
4 changed files with 38 additions and 2 deletions

View File

@ -22,3 +22,6 @@ score_position_top=Top
score_position_middle=Middle score_position_middle=Middle
score_position_bottom=Bottom score_position_bottom=Bottom
score_position_none=Hide Score score_position_none=Hide Score
chapter_score_filtering_title=Automatically de-duplicate chapters
chapter_score_filtering_on=For each chapter, only displays the scanlator with the highest score
chapter_score_filtering_off=Does not filterout any chapters based on score (any other scanlator filtering will still apply)

View File

@ -1,7 +1,7 @@
ext { ext {
extName = 'Comick' extName = 'Comick'
extClass = '.ComickFactory' extClass = '.ComickFactory'
extVersionCode = 56 extVersionCode = 57
isNsfw = true isNsfw = true
} }

View File

@ -178,6 +178,20 @@ abstract class Comick(
.commit() .commit()
} }
}.also(screen::addPreference) }.also(screen::addPreference)
SwitchPreferenceCompat(screen.context).apply {
key = CHAPTER_SCORE_FILTERING_PREF
title = intl["chapter_score_filtering_title"]
summaryOff = intl["chapter_score_filtering_off"]
summaryOn = intl["chapter_score_filtering_on"]
setDefaultValue(CHAPTER_SCORE_FILTERING_DEFAULT)
setOnPreferenceChangeListener { _, newValue ->
preferences.edit()
.putBoolean(CHAPTER_SCORE_FILTERING_PREF, newValue as Boolean)
.commit()
}
}.also(screen::addPreference)
} }
private val SharedPreferences.ignoredGroups: Set<String> private val SharedPreferences.ignoredGroups: Set<String>
@ -224,6 +238,9 @@ abstract class Comick(
private val SharedPreferences.scorePosition: String private val SharedPreferences.scorePosition: String
get() = getString(SCORE_POSITION_PREF, SCORE_POSITION_DEFAULT) ?: SCORE_POSITION_DEFAULT get() = getString(SCORE_POSITION_PREF, SCORE_POSITION_DEFAULT) ?: SCORE_POSITION_DEFAULT
private val SharedPreferences.chapterScoreFiltering: Boolean
get() = getBoolean(CHAPTER_SCORE_FILTERING_PREF, CHAPTER_SCORE_FILTERING_DEFAULT)
override fun headersBuilder() = Headers.Builder().apply { override fun headersBuilder() = Headers.Builder().apply {
add("Referer", "$baseUrl/") add("Referer", "$baseUrl/")
add("User-Agent", "Tachiyomi ${System.getProperty("http.agent")}") add("User-Agent", "Tachiyomi ${System.getProperty("http.agent")}")
@ -546,9 +563,19 @@ abstract class Comick(
publishedChapter && noGroupBlock publishedChapter && noGroupBlock
} }
.filterOnScore(preferences.chapterScoreFiltering)
.map { it.toSChapter(mangaUrl) } .map { it.toSChapter(mangaUrl) }
} }
private fun List<Chapter>.filterOnScore(shouldFilter: Boolean): Collection<Chapter> {
if (shouldFilter) {
return groupBy { it.chap }
.map { (_, chapters) -> chapters.maxBy { it.score } }
} else {
return this
}
}
private val publishedDateFormat = private val publishedDateFormat =
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH).apply { SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH).apply {
timeZone = TimeZone.getTimeZone("UTC") timeZone = TimeZone.getTimeZone("UTC")
@ -626,6 +653,8 @@ abstract class Comick(
const val SCORE_POSITION_DEFAULT = "top" const val SCORE_POSITION_DEFAULT = "top"
private const val LOCAL_TITLE_PREF = "LocalTitle" private const val LOCAL_TITLE_PREF = "LocalTitle"
private const val LOCAL_TITLE_DEFAULT = false private const val LOCAL_TITLE_DEFAULT = false
private const val CHAPTER_SCORE_FILTERING_PREF = "ScoreAutoFiltering"
private const val CHAPTER_SCORE_FILTERING_DEFAULT = false
private const val LIMIT = 20 private const val LIMIT = 20
private const val CHAPTERS_LIMIT = 99999 private const val CHAPTERS_LIMIT = 99999
} }

View File

@ -199,10 +199,14 @@ class Chapter(
private val title: String = "", private val title: String = "",
@SerialName("created_at") private val createdAt: String = "", @SerialName("created_at") private val createdAt: String = "",
@SerialName("publish_at") val publishedAt: String = "", @SerialName("publish_at") val publishedAt: String = "",
private val chap: String = "", val chap: String = "",
private val vol: String = "", private val vol: String = "",
@SerialName("group_name") val groups: List<String> = emptyList(), @SerialName("group_name") val groups: List<String> = emptyList(),
@SerialName("up_count") private val upCount: Int,
@SerialName("down_count") private val downCount: Int,
) { ) {
val score get() = upCount - downCount
fun toSChapter(mangaUrl: String) = SChapter.create().apply { fun toSChapter(mangaUrl: String) = SChapter.create().apply {
url = "$mangaUrl/$hid-chapter-$chap-$lang" url = "$mangaUrl/$hid-chapter-$chap-$lang"
name = beautifyChapterName(vol, chap, title) name = beautifyChapterName(vol, chap, title)