mirror of
https://github.com/keiyoushi/extensions-source.git
synced 2024-11-25 19:52:56 +01:00
Akuma: Add Filters, Add Factory for specific language, Improve manga description and tags (#3351)
* 1. Add language for manga description 2. Add a custom preference to choice if show gender as text or icon * fix code style * revert extra formatting * change preference into switch * remove unused import, increase extVersionCode * Add filter, split into specific languages * Apply suggestions from code review * Change Filter style, Add Filter, Add exclude syntax * Change Code Style * Remove RateLimit Preference, Remove Syntax Filter --------- Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com>
This commit is contained in:
parent
bfef7faafb
commit
c47b9d852d
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'Akuma'
|
extName = 'Akuma'
|
||||||
extClass = '.Akuma'
|
extClass = '.AkumaFactory'
|
||||||
extVersionCode = 1
|
extVersionCode = 2
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
package eu.kanade.tachiyomi.extension.all.akuma
|
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.GET
|
||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
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.model.Filter
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.model.MangasPage
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
@ -20,16 +26,19 @@ 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 rx.Observable
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class Akuma : ParsedHttpSource() {
|
class Akuma(
|
||||||
|
override val lang: String,
|
||||||
|
private val akumaLang: String,
|
||||||
|
) : ConfigurableSource, ParsedHttpSource() {
|
||||||
|
|
||||||
override val name = "Akuma"
|
override val name = "Akuma"
|
||||||
|
|
||||||
override val baseUrl = "https://akuma.moe"
|
override val baseUrl = "https://akuma.moe"
|
||||||
|
|
||||||
override val lang = "all"
|
|
||||||
|
|
||||||
override val supportsLatest = false
|
override val supportsLatest = false
|
||||||
|
|
||||||
private var nextHash: String? = null
|
private var nextHash: String? = null
|
||||||
@ -38,6 +47,12 @@ class Akuma : ParsedHttpSource() {
|
|||||||
|
|
||||||
private val ddosGuardIntercept = DDosGuardInterceptor(network.client)
|
private val ddosGuardIntercept = DDosGuardInterceptor(network.client)
|
||||||
|
|
||||||
|
private val preferences: SharedPreferences by lazy {
|
||||||
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var iconified = preferences.getBoolean(PREF_TAG_GENDER_ICON, false)
|
||||||
|
|
||||||
override val client: OkHttpClient = network.client.newBuilder()
|
override val client: OkHttpClient = network.client.newBuilder()
|
||||||
.addInterceptor(ddosGuardIntercept)
|
.addInterceptor(ddosGuardIntercept)
|
||||||
.addInterceptor(::tokenInterceptor)
|
.addInterceptor(::tokenInterceptor)
|
||||||
@ -102,12 +117,19 @@ class Akuma : ParsedHttpSource() {
|
|||||||
.add("view", "3")
|
.add("view", "3")
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return if (page == 1) {
|
val url = baseUrl.toHttpUrlOrNull()!!.newBuilder()
|
||||||
|
|
||||||
|
if (page == 1) {
|
||||||
nextHash = null
|
nextHash = null
|
||||||
POST(baseUrl, headers, payload)
|
|
||||||
} else {
|
} else {
|
||||||
POST("$baseUrl/?cursor=$nextHash", headers, payload)
|
url.addQueryParameter("cursor", nextHash)
|
||||||
}
|
}
|
||||||
|
if (lang != "all") {
|
||||||
|
// append like `q=language:english$`
|
||||||
|
url.addQueryParameter("q", "language:$akumaLang$")
|
||||||
|
}
|
||||||
|
|
||||||
|
return POST(url.toString(), headers, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun popularMangaSelector() = ".post-loop li"
|
override fun popularMangaSelector() = ".post-loop li"
|
||||||
@ -154,8 +176,39 @@ class Akuma : ParsedHttpSource() {
|
|||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
val request = popularMangaRequest(page)
|
val request = popularMangaRequest(page)
|
||||||
|
|
||||||
|
val finalQuery = buildString {
|
||||||
|
append(query)
|
||||||
|
if (lang != "all") {
|
||||||
|
append(" language:", akumaLang, "$")
|
||||||
|
}
|
||||||
|
filters.filterIsInstance<TextFilter>().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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filters.filterIsInstance<OptionFilter>().firstOrNull()?.let {
|
||||||
|
val filter = options[it.state].second
|
||||||
|
if (filter.isNotBlank()) {
|
||||||
|
append(" opt:", filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filters.filterIsInstance<CategoryFilter>().firstOrNull()?.state?.forEach {
|
||||||
|
if (it.isIncluded()) {
|
||||||
|
append(" category:\"", it.name, "\"$")
|
||||||
|
} else if (it.isExcluded()) {
|
||||||
|
append(" -category:\"", it.name, "\"$")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val url = request.url.newBuilder()
|
val url = request.url.newBuilder()
|
||||||
.addQueryParameter("q", query.trim())
|
.setQueryParameter("q", finalQuery)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
return request.newBuilder()
|
return request.newBuilder()
|
||||||
@ -168,14 +221,45 @@ class Akuma : ParsedHttpSource() {
|
|||||||
override fun searchMangaParse(response: Response) = popularMangaParse(response)
|
override fun searchMangaParse(response: Response) = popularMangaParse(response)
|
||||||
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
|
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
|
||||||
|
|
||||||
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
|
override fun mangaDetailsParse(document: Document) = with(document) {
|
||||||
title = document.select(".entry-title").text()
|
SManga.create().apply {
|
||||||
thumbnail_url = document.select(".img-thumbnail").attr("abs:src")
|
title = select(".entry-title").text()
|
||||||
author = document.select("li.meta-data > span.artist + span.value").text()
|
thumbnail_url = select(".img-thumbnail").attr("abs:src")
|
||||||
genre = document.select(".info-list a").joinToString { it.text() }
|
|
||||||
description = document.select(".pages span.value").text() + " Pages"
|
author = select(".group~.value").eachText().joinToString()
|
||||||
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
|
artist = select(".artist~.value").eachText().joinToString()
|
||||||
status = SManga.COMPLETED
|
|
||||||
|
val characters = select(".character~.value").eachText()
|
||||||
|
val parodies = select(".parody~.value").eachText()
|
||||||
|
val males = select(".male~.value")
|
||||||
|
.map { it.text() + if (iconified) " ♂" else " (male)" }
|
||||||
|
val females = select(".female~.value")
|
||||||
|
.map { it.text() + if (iconified) " ♀" else " (female)" }
|
||||||
|
// show all in tags for quickly searching
|
||||||
|
|
||||||
|
genre = (characters + parodies + males + females).joinToString()
|
||||||
|
description = buildString {
|
||||||
|
append(
|
||||||
|
"Full English and Japanese title: \n",
|
||||||
|
select(".entry-title").text(),
|
||||||
|
"\n",
|
||||||
|
select(".entry-title+span").text(),
|
||||||
|
"\n\n",
|
||||||
|
)
|
||||||
|
|
||||||
|
// translated should show up in the description
|
||||||
|
append("Language: ", select(".language~.value").text(), "\n")
|
||||||
|
append("Pages: ", select(".pages .value").text(), "\n")
|
||||||
|
append("Upload Date: ", select(".date .value>time").text(), "\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")
|
||||||
|
}
|
||||||
|
update_strategy = UpdateStrategy.ONLY_FETCH_ONCE
|
||||||
|
status = SManga.UNKNOWN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||||
@ -208,8 +292,66 @@ class Akuma : ParsedHttpSource() {
|
|||||||
return document.select(".entry-content img").attr("abs:src")
|
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<CategoryFilter.TagTriState>("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<String>("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)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val PREFIX_ID = "id:"
|
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()
|
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
package eu.kanade.tachiyomi.extension.all.akuma
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.Source
|
||||||
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
|
|
||||||
|
class AkumaFactory : SourceFactory {
|
||||||
|
override fun createSources(): List<Source> = listOf(
|
||||||
|
Akuma("all", "all"),
|
||||||
|
Akuma("en", "english"),
|
||||||
|
Akuma("id", "indonesian"),
|
||||||
|
Akuma("jv", "javanese"),
|
||||||
|
Akuma("ca", "catalan"),
|
||||||
|
Akuma("ceb", "cebuano"),
|
||||||
|
Akuma("cs", "czech"),
|
||||||
|
Akuma("da", "danish"),
|
||||||
|
Akuma("de", "german"),
|
||||||
|
Akuma("et", "estonian"),
|
||||||
|
Akuma("es", "spanish"),
|
||||||
|
Akuma("eo", "esperanto"),
|
||||||
|
Akuma("fr", "french"),
|
||||||
|
Akuma("it", "italian"),
|
||||||
|
Akuma("hi", "hindi"),
|
||||||
|
Akuma("hu", "hungarian"),
|
||||||
|
Akuma("pl", "polish"),
|
||||||
|
Akuma("pt", "portuguese"),
|
||||||
|
Akuma("vi", "vietnamese"),
|
||||||
|
Akuma("tr", "turkish"),
|
||||||
|
Akuma("ru", "russian"),
|
||||||
|
Akuma("uk", "ukrainian"),
|
||||||
|
Akuma("ar", "arabic"),
|
||||||
|
Akuma("ko", "korean"),
|
||||||
|
Akuma("zh", "chinese"),
|
||||||
|
Akuma("ja", "japanese"),
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user