Hiperdex: add regex to clean title (#11163)

* feat(Hiperdex): add preferences to remove title regex

* feat(Hiperdex): add preferences to remove title regex

* add original title to description

* make companion

* custom regex verification

* verify regex

* Revert "Revert "switch to checkbox""

This reverts commit 491fd17282fad18a04b3ff1570784954fde2703f.

* add real-time validation for custom regex input & update summary on change

* Fix version

* Also clean title while browsing/searching
This commit is contained in:
Cuong-Tran 2025-10-22 12:28:04 +07:00 committed by Draff
parent ad03299e49
commit 6824183cf1
Signed by: Draff
GPG Key ID: E8A89F3211677653
2 changed files with 110 additions and 1 deletions

View File

@ -3,7 +3,7 @@ ext {
extClass = '.Hiperdex' extClass = '.Hiperdex'
themePkg = 'madara' themePkg = 'madara'
baseUrl = 'https://hiperdex.com' baseUrl = 'https://hiperdex.com'
overrideVersionCode = 20 overrideVersionCode = 21
isNsfw = true isNsfw = true
} }

View File

@ -1,6 +1,10 @@
package eu.kanade.tachiyomi.extension.en.hiperdex package eu.kanade.tachiyomi.extension.en.hiperdex
import android.text.Editable
import android.text.TextWatcher
import android.widget.Button
import android.widget.Toast import android.widget.Toast
import androidx.preference.CheckBoxPreference
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.lib.randomua.addRandomUAPreferenceToScreen import eu.kanade.tachiyomi.lib.randomua.addRandomUAPreferenceToScreen
@ -10,7 +14,10 @@ import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.SManga
import keiyoushi.utils.getPreferences import keiyoushi.utils.getPreferences
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -53,12 +60,109 @@ class Hiperdex :
} }
}.also { screen.addPreference(it) } }.also { screen.addPreference(it) }
CheckBoxPreference(screen.context).apply {
key = "${REMOVE_TITLE_VERSION_PREF}_$lang"
title = "Remove version information from entry titles"
summary = "This removes version tags like '(Official)' or '(Uncensored)' from entry titles " +
"and helps identify duplicate entries in your library. " +
"To update existing entries, remove them from your library (unfavorite) and refresh manually. " +
"You might also want to clear the database in advanced settings."
setDefaultValue(false)
}.also { screen.addPreference(it) }
EditTextPreference(screen.context).apply {
key = "${REMOVE_TITLE_CUSTOM_PREF}_$lang"
title = "Custom regex to be removed from title"
summary = customRemoveTitle()
setDefaultValue("")
val validate = { str: String ->
runCatching { Regex(str) }
.map { true to "" }
.getOrElse { false to it.message }
}
setOnBindEditTextListener { editText ->
editText.addTextChangedListener(
object : TextWatcher {
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun afterTextChanged(editable: Editable?) {
editable ?: return
val text = editable.toString()
val valid = validate(text)
editText.error = if (!valid.first) valid.second else null
editText.rootView.findViewById<Button>(android.R.id.button1)?.isEnabled = editText.error == null
}
},
)
}
setOnPreferenceChangeListener { _, newValue ->
val (isValid, message) = validate(newValue as String)
if (isValid) {
summary = newValue
} else {
Toast.makeText(screen.context, message, Toast.LENGTH_LONG).show()
}
isValid
}
}.also { screen.addPreference(it) }
addRandomUAPreferenceToScreen(screen) addRandomUAPreferenceToScreen(screen)
} }
override fun popularMangaFromElement(element: Element): SManga {
return super.popularMangaFromElement(element).apply {
title = title.cleanTitleIfNeeded()
}
}
override fun latestUpdatesFromElement(element: Element): SManga {
return super.latestUpdatesFromElement(element).apply {
title = title.cleanTitleIfNeeded()
}
}
override fun searchMangaFromElement(element: Element): SManga {
return super.searchMangaFromElement(element).apply {
title = title.cleanTitleIfNeeded()
}
}
override fun searchMangaSelector() = "#loop-content div.page-listing-item" override fun searchMangaSelector() = "#loop-content div.page-listing-item"
override fun mangaDetailsParse(document: Document): SManga {
return super.mangaDetailsParse(document).apply {
val cleanedTitle = title.cleanTitleIfNeeded()
if (cleanedTitle != title.trim()) {
description = listOfNotNull(title, description)
.joinToString("\n\n")
title = cleanedTitle
}
}
}
private fun String.cleanTitleIfNeeded(): String {
var tempTitle = this
customRemoveTitle().takeIf { it.isNotEmpty() }?.let { customRegex ->
runCatching {
tempTitle = tempTitle.replace(Regex(customRegex), "")
}
}
if (isRemoveTitleVersion()) {
tempTitle = tempTitle.replace(titleRegex, "")
}
return tempTitle.trim()
}
private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, super.baseUrl)!! private fun getPrefBaseUrl(): String = preferences.getString(BASE_URL_PREF, super.baseUrl)!!
private fun isRemoveTitleVersion(): Boolean {
return preferences.getBoolean("${REMOVE_TITLE_VERSION_PREF}_$lang", false)
}
private fun customRemoveTitle(): String =
preferences.getString("${REMOVE_TITLE_CUSTOM_PREF}_$lang", "")!!
init { init {
preferences.getString(DEFAULT_BASE_URL_PREF, null).let { defaultBaseUrl -> preferences.getString(DEFAULT_BASE_URL_PREF, null).let { defaultBaseUrl ->
@ -77,5 +181,10 @@ class Hiperdex :
private const val BASE_URL_PREF_SUMMARY = "The default settings will be applied when the extension is next updated" private const val BASE_URL_PREF_SUMMARY = "The default settings will be applied when the extension is next updated"
private const val DEFAULT_BASE_URL_PREF = "defaultBaseUrl" private const val DEFAULT_BASE_URL_PREF = "defaultBaseUrl"
private const val RESTART_APP_MESSAGE = "Restart app to apply new setting." private const val RESTART_APP_MESSAGE = "Restart app to apply new setting."
private const val REMOVE_TITLE_VERSION_PREF = "REMOVE_TITLE_VERSION"
private const val REMOVE_TITLE_CUSTOM_PREF = "REMOVE_TITLE_CUSTOM"
private val titleRegex: Regex =
Regex("\\([^()]*\\)|\\{[^{}]*\\}|\\[(?:(?!]).)*]|«[^»]*»|〘[^〙]*〙|「[^」]*」|『[^』]*』|≪[^≫]*≫|﹛[^﹜]*﹜|〖[^〖〗]*〗|\uD81A\uDD0D.+?\uD81A\uDD0D|《[^》]*》|⌜.+?⌝|⟨[^⟩]*⟩|/Official|/ Official", RegexOption.IGNORE_CASE)
} }
} }