Manga District: add tag browse (#7035)

* Manga District: add tag browse

* Apply suggestions from code review

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

* correction

* Separate method to load tags from preferences & always initialize with at least 1 tag to avoid excessively reading preferences

* remove unnecessary getter

* Fix: actually update backing field; also improve load tags from preferences

---------

Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>
This commit is contained in:
Cuong-Tran 2025-01-08 19:50:03 +07:00 committed by Draff
parent 6f90a79c96
commit 4bc138aa39
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
2 changed files with 68 additions and 17 deletions

View File

@ -3,7 +3,7 @@ ext {
extClass = '.MangaDistrict' extClass = '.MangaDistrict'
themePkg = 'madara' themePkg = 'madara'
baseUrl = 'https://mangadistrict.com' baseUrl = 'https://mangadistrict.com'
overrideVersionCode = 7 overrideVersionCode = 8
isNsfw = true isNsfw = true
} }

View File

@ -7,7 +7,12 @@ import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.ConfigurableSource 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
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
@ -17,6 +22,7 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
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.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -41,23 +47,13 @@ class MangaDistrict :
private val titleVersion = Regex("\\(.*\\)") private val titleVersion = Regex("\\(.*\\)")
override fun popularMangaFromElement(element: Element): SManga {
return super.popularMangaFromElement(element).apply {
if (isRemoveTitleVersion()) {
title = this.title.replace(titleVersion, "").trim()
}
}
}
override fun searchMangaFromElement(element: Element): SManga {
return super.searchMangaFromElement(element).apply {
if (isRemoveTitleVersion()) {
title = this.title.replace(titleVersion, "").trim()
}
}
}
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val tags = document.select(mangaDetailsSelectorTag).mapNotNull { element ->
element.ownText() to element.attr("href")
.removeSuffix("/").substringAfterLast('/')
}
tagList = tagList.plus(tags)
return super.mangaDetailsParse(document).apply { return super.mangaDetailsParse(document).apply {
if (isRemoveTitleVersion()) { if (isRemoveTitleVersion()) {
title = this.title.replace(titleVersion, "").trim() title = this.title.replace(titleVersion, "").trim()
@ -103,6 +99,60 @@ class MangaDistrict :
return super.pageListParse(document) return super.pageListParse(document)
} }
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
val tagFilter = filters.filterIsInstance<TagList>().firstOrNull()
if (tagFilter != null && tagFilter.state > 0) {
val urlBuilder = baseUrl.toHttpUrl().newBuilder()
urlBuilder.addPathSegment("publication-tag")
urlBuilder.addPathSegment(tagFilter.toUriPart())
urlBuilder.addPathSegments("page/$page")
return client.newCall(GET(urlBuilder.build(), headers))
.asObservableSuccess().map { response ->
popularMangaParse(response)
}
} else {
return super.fetchSearchManga(page, query, filters)
}
}
private fun loadTagListFromPreferences(): Set<Pair<String, String>> =
preferences.getStringSet(TAG_LIST_PREF, emptySet())
?.mapNotNull {
it.split('|')
.let { splits ->
if (splits.size == 2) {
splits[0] to splits[1]
} else {
null
}
}
}
?.toSet()
// Create at least 1 tag to avoid excessively reading preferences
.let { if (it.isNullOrEmpty()) setOf("Manhwa" to "manhwa") else it }
private var tagList: Set<Pair<String, String>> = loadTagListFromPreferences()
set(value) {
preferences.edit().putStringSet(
TAG_LIST_PREF,
value.map { "${it.first}|${it.second}" }.toSet(),
).apply()
field = value
}
override fun getFilterList(): FilterList {
val filters = super.getFilterList().list.toMutableList()
if (tagList.isNotEmpty()) {
filters += Filter.Separator()
filters += Filter.Header("Tag browse will ignore other filters")
filters += TagList("Tag browse", listOf(Pair("<Browse tag>", "")) + tagList.toList())
}
return FilterList(filters)
}
private class TagList(title: String, options: List<Pair<String, String>>, state: Int = 0) :
UriPartFilter(title, options.toTypedArray(), state)
private fun String.urlKey(): String { private fun String.urlKey(): String {
return toHttpUrl().pathSegments.let { path -> return toHttpUrl().pathSegments.let { path ->
"${path[1]}/${path[2]}" "${path[1]}/${path[2]}"
@ -154,6 +204,7 @@ class MangaDistrict :
companion object { companion object {
private const val REMOVE_TITLE_VERSION_PREF = "REMOVE_TITLE_VERSION" private const val REMOVE_TITLE_VERSION_PREF = "REMOVE_TITLE_VERSION"
private const val TAG_LIST_PREF = "TAG_LIST"
private const val IMG_RES_PREF = "IMG_RES" private const val IMG_RES_PREF = "IMG_RES"
private const val IMG_RES_ALL = "all" private const val IMG_RES_ALL = "all"