From 254087d9129d4471a253f4472867b153741750e5 Mon Sep 17 00:00:00 2001 From: KenjieDec <65448230+KenjieDec@users.noreply.github.com> Date: Thu, 6 Jun 2024 18:52:56 +0700 Subject: [PATCH] Akuma Tags Fix (#3390) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix - Removed "Pages" filter — They don't work - Added "Other Tags" filter - Added Filter limit warning - Fixed problem on tags with spaces ( e.g. bxg brxxsts ) * Extension version: 2 -> 3 * typo fix * Change line endings, make prefrence default - Line endings: CRLF -> LF - Iconified Tag Preference -> Iconified Tag is default * Missing space * Fix1 * Change * Change2 * Change3 - Removed override for fetchChapterList and provide implementation in chapterListParse - Filter out empty tags ( For example: user can put 2 commas together ) * Change4 Moved Date Format to Class val * try catch for date parse * Update Akuma.kt --------- Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> --- src/all/akuma/build.gradle | 2 +- .../tachiyomi/extension/all/akuma/Akuma.kt | 170 ++++++------------ .../extension/all/akuma/AkumaFactory.kt | 1 + .../extension/all/akuma/AkumaFilters.kt | 49 +++++ 4 files changed, 110 insertions(+), 112 deletions(-) create mode 100644 src/all/akuma/src/eu/kanade/tachiyomi/extension/all/akuma/AkumaFilters.kt diff --git a/src/all/akuma/build.gradle b/src/all/akuma/build.gradle index 4ffbdfe15..2c8f0d29c 100644 --- a/src/all/akuma/build.gradle +++ b/src/all/akuma/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Akuma' extClass = '.AkumaFactory' - extVersionCode = 2 + extVersionCode = 3 isNsfw = true } diff --git a/src/all/akuma/src/eu/kanade/tachiyomi/extension/all/akuma/Akuma.kt b/src/all/akuma/src/eu/kanade/tachiyomi/extension/all/akuma/Akuma.kt index c5a357061..f28cdd7da 100644 --- a/src/all/akuma/src/eu/kanade/tachiyomi/extension/all/akuma/Akuma.kt +++ b/src/all/akuma/src/eu/kanade/tachiyomi/extension/all/akuma/Akuma.kt @@ -1,14 +1,8 @@ package eu.kanade.tachiyomi.extension.all.akuma -import android.app.Application -import android.content.SharedPreferences -import androidx.preference.PreferenceScreen -import androidx.preference.SwitchPreferenceCompat import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.interceptor.rateLimit -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 @@ -26,14 +20,16 @@ import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element import rx.Observable -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import java.io.IOException +import java.text.ParseException +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.TimeZone class Akuma( override val lang: String, private val akumaLang: String, -) : ConfigurableSource, ParsedHttpSource() { +) : ParsedHttpSource() { override val name = "Akuma" @@ -47,12 +43,9 @@ class Akuma( private val ddosGuardIntercept = DDosGuardInterceptor(network.client) - private val preferences: SharedPreferences by lazy { - Injekt.get().getSharedPreferences("source_$id", 0x0000) + private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ENGLISH).apply { + timeZone = TimeZone.getTimeZone("UTC") } - - private var iconified = preferences.getBoolean(PREF_TAG_GENDER_ICON, false) - override val client: OkHttpClient = network.client.newBuilder() .addInterceptor(ddosGuardIntercept) .addInterceptor(::tokenInterceptor) @@ -138,6 +131,10 @@ class Akuma( override fun popularMangaParse(response: Response): MangasPage { val document = response.asJsoup() + if (document.text().contains("Max keywords of 3 exceeded.")) { + throw Exception("Login required for more than 3 filters") + } else if (document.text().contains("Max keywords of 8 exceeded.")) throw Exception("Only max of 8 filters are allowed") + val mangas = document.select(popularMangaSelector()).map { element -> popularMangaFromElement(element) } @@ -176,39 +173,39 @@ class Akuma( override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { val request = popularMangaRequest(page) - val finalQuery = buildString { - append(query) - if (lang != "all") { - append(" language:", akumaLang, "$") - } - filters.filterIsInstance().forEach { filter -> - if (filter.state.isBlank()) return@forEach - filter.state.split(",").forEach { - // append like `a:"eye-covering bang"$` - if (it.startsWith("-")) { - append(" -", filter.identifier, ":", it.trim().substring(1)) - } else { - append(" ", filter.identifier, ":", it.trim()) + val finalQuery: MutableList = mutableListOf(query) + + if (lang != "all") { + finalQuery.add("language: $akumaLang$") + } + filters.forEach { filter -> + when (filter) { + is TextFilter -> { + if (filter.state.isNotEmpty()) { + finalQuery.addAll( + filter.state.split(",").filter { it.isNotBlank() }.map { + (if (it.trim().startsWith("-")) "-" else "") + "${filter.tag}:\"${it.trim().replace("-", "")}\"" + }, + ) } } - } - filters.filterIsInstance().firstOrNull()?.let { - val filter = options[it.state].second - if (filter.isNotBlank()) { - append(" opt:", filter) + is OptionFilter -> { + if (filter.state > 0) finalQuery.add("opt:${filter.getValue()}") } - } - filters.filterIsInstance().firstOrNull()?.state?.forEach { - if (it.isIncluded()) { - append(" category:\"", it.name, "\"$") - } else if (it.isExcluded()) { - append(" -category:\"", it.name, "\"$") + is CategoryFilter -> { + filter.state.forEach { + when { + it.isIncluded() -> finalQuery.add("category:\"${it.name}\"") + it.isExcluded() -> finalQuery.add("-category:\"${it.name}\"") + } + } } + else -> {} } } val url = request.url.newBuilder() - .setQueryParameter("q", finalQuery) + .setQueryParameter("q", finalQuery.joinToString(" ")) .build() return request.newBuilder() @@ -232,12 +229,14 @@ class Akuma( val characters = select(".character~.value").eachText() val parodies = select(".parody~.value").eachText() val males = select(".male~.value") - .map { it.text() + if (iconified) " ♂" else " (male)" } + .map { "${it.text()} ♂" } val females = select(".female~.value") - .map { it.text() + if (iconified) " ♀" else " (female)" } + .map { "${it.text()} ♀" } + val others = select(".other~.value") + .map { "${it.text()} ◊" } // show all in tags for quickly searching - genre = (characters + parodies + males + females).joinToString() + genre = (males + females + others).joinToString() description = buildString { append( "Full English and Japanese title: \n", @@ -248,28 +247,33 @@ class Akuma( ) // translated should show up in the description - append("Language: ", select(".language~.value").text(), "\n") + append("Language: ", select(".language~.value").eachText().joinToString(), "\n") append("Pages: ", select(".pages .value").text(), "\n") - append("Upload Date: ", select(".date .value>time").text(), "\n") + append("Upload Date: ", select(".date .value>time").text().replace(" ", ", ") + " UTC", "\n") append("Categories: ", selectFirst(".info-list .value")?.text() ?: "Unknown", "\n\n") // show followings for easy to reference - append("Parodies: ", parodies.joinToString(), "\n") - append("Characters: ", characters.joinToString(), "\n") + parodies.takeIf { it.isNotEmpty() }?.let { append("Parodies: ", parodies.joinToString(), "\n") } + characters.takeIf { it.isNotEmpty() }?.let { append("Characters: ", characters.joinToString(), "\n") } } update_strategy = UpdateStrategy.ONLY_FETCH_ONCE status = SManga.UNKNOWN } } - override fun fetchChapterList(manga: SManga): Observable> { - return Observable.just( - listOf( - SChapter.create().apply { - url = "${manga.url}/1" - name = "Chapter" - }, - ), + override fun chapterListParse(response: Response): List { + val document = response.asJsoup() + + return listOf( + SChapter.create().apply { + url = "${response.request.url}/1" + name = "Chapter" + date_upload = try { + dateFormat.parse(document.select(".date .value>time").text())!!.time + } catch (_: ParseException) { + 0L + } + }, ) } @@ -292,66 +296,10 @@ class Akuma( return document.select(".entry-content img").attr("abs:src") } - override fun getFilterList(): FilterList = FilterList( - Filter.Header("Separate tags with commas (,)"), - Filter.Header("Prepend with dash (-) to exclude"), - TextFilter("Female Tags", "female"), - TextFilter("Male Tags", "male"), - CategoryFilter(), - TextFilter("Groups", "group"), - TextFilter("Artists", "artist"), - TextFilter("Parody", "parody"), - TextFilter("Characters", "character"), - Filter.Header("Filter by pages, for example: (>20)"), - TextFilter("Pages", "pages"), - Filter.Header("Search in favorites, read, or commented"), - OptionFilter(), - ) - - private class CategoryFilter : Filter.Group("Categories", values()) { - class TagTriState(name: String) : TriState(name) - private companion object { - fun values() = listOf( - TagTriState("doujinshi"), - TagTriState("manga"), - TagTriState("artist cg"), - TagTriState("game cg"), - TagTriState("west"), - TagTriState("non-h"), - TagTriState("gallery"), - TagTriState("cosplay"), - TagTriState("asian pron"), - TagTriState("misc"), - ) - } - } - private class TextFilter(placeholder: String, val identifier: String) : Filter.Text(placeholder) - private class OptionFilter : - Filter.Select("Options", options.map { it.first }.toTypedArray()) - - override fun setupPreferenceScreen(screen: PreferenceScreen) { - SwitchPreferenceCompat(screen.context).apply { - key = PREF_TAG_GENDER_ICON - title = "Show gender as text or icon in tags (requires refresh)" - summaryOff = "Show gender as text" - summaryOn = "Show gender as icon" - - setOnPreferenceChangeListener { _, newValue -> - iconified = newValue == true - true - } - }.also(screen::addPreference) - } + override fun getFilterList(): FilterList = getFilters() companion object { const val PREFIX_ID = "id:" - private const val PREF_TAG_GENDER_ICON = "pref_tag_gender_icon" - private val options = listOf( - "None" to "", - "Favorited only" to "favorited", - "Read only" to "read", - "Commented only" to "commented", - ) } override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException() diff --git a/src/all/akuma/src/eu/kanade/tachiyomi/extension/all/akuma/AkumaFactory.kt b/src/all/akuma/src/eu/kanade/tachiyomi/extension/all/akuma/AkumaFactory.kt index 45ae32d52..7c15130d4 100644 --- a/src/all/akuma/src/eu/kanade/tachiyomi/extension/all/akuma/AkumaFactory.kt +++ b/src/all/akuma/src/eu/kanade/tachiyomi/extension/all/akuma/AkumaFactory.kt @@ -21,6 +21,7 @@ class AkumaFactory : SourceFactory { Akuma("it", "italian"), Akuma("hi", "hindi"), Akuma("hu", "hungarian"), + Akuma("nl", "dutch"), Akuma("pl", "polish"), Akuma("pt", "portuguese"), Akuma("vi", "vietnamese"), diff --git a/src/all/akuma/src/eu/kanade/tachiyomi/extension/all/akuma/AkumaFilters.kt b/src/all/akuma/src/eu/kanade/tachiyomi/extension/all/akuma/AkumaFilters.kt new file mode 100644 index 000000000..b3d1c0245 --- /dev/null +++ b/src/all/akuma/src/eu/kanade/tachiyomi/extension/all/akuma/AkumaFilters.kt @@ -0,0 +1,49 @@ +package eu.kanade.tachiyomi.extension.all.akuma + +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList + +fun getFilters(): FilterList { + return FilterList( + Filter.Header("Separate tags with commas (,)"), + Filter.Header("Prepend with dash (-) to exclude"), + TextFilter("Female Tags", "female"), + TextFilter("Male Tags", "male"), + TextFilter("Other Tags", "other"), + CategoryFilter(), + TextFilter("Groups", "group"), + TextFilter("Artists", "artist"), + TextFilter("Parody", "parody"), + TextFilter("Characters", "character"), + Filter.Separator(), + Filter.Header("Search in favorites, read, or commented"), + OptionFilter(), + ) +} + +internal class TextFilter(name: String, val tag: String) : Filter.Text(name) +internal class OptionFilter(val value: List> = options) : Filter.Select("Options", options.map { it.first }.toTypedArray()) { + fun getValue() = options[state].second +} + +internal open class TagTriState(name: String) : Filter.TriState(name) +internal class CategoryFilter() : + Filter.Group("Categories", categoryList.map { TagTriState(it) }) + +private val categoryList = listOf( + "Doujinshi", + "Manga", + "Image Set", + "Artist CG", + "Game CG", + "Western", + "Non-H", + "Cosplay", + "Misc", +) +private val options = listOf( + "None" to "", + "Favorited only" to "favorited", + "Read only" to "read", + "Commented only" to "commented", +)