Kagane: Add excluded genres & Fix chapter number (#11537)
* Don't use DTO chapter number as it is , not actual chapter number * Add Exclude Genres preference * pump version * Move 'Show scanlations' to preferences * Add source to tags so it can be searched with filter by clicking on it * Don't sort by relevant if filtering without query string * Some sources prefer 'number_sort' * catching error * optimize
This commit is contained in:
parent
9194e31208
commit
b8154e7698
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'Kagane'
|
extName = 'Kagane'
|
||||||
extClass = '.Kagane'
|
extClass = '.Kagane'
|
||||||
extVersionCode = 9
|
extVersionCode = 10
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -56,7 +56,7 @@ class DetailsDto(
|
|||||||
|
|
||||||
author = authors.joinToString()
|
author = authors.joinToString()
|
||||||
description = desc.toString()
|
description = desc.toString()
|
||||||
genre = genres.joinToString()
|
genre = (listOf(source) + genres).joinToString()
|
||||||
status = this@DetailsDto.status.toStatus()
|
status = this@DetailsDto.status.toStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,11 +86,13 @@ class ChapterDto(
|
|||||||
@SerialName("number_sort")
|
@SerialName("number_sort")
|
||||||
val number: Float,
|
val number: Float,
|
||||||
) {
|
) {
|
||||||
fun toSChapter(): SChapter = SChapter.create().apply {
|
fun toSChapter(useSourceChapterNumber: Boolean = false): SChapter = SChapter.create().apply {
|
||||||
url = "$seriesId;$id;$pagesCount"
|
url = "$seriesId;$id;$pagesCount"
|
||||||
name = title
|
name = title
|
||||||
date_upload = dateFormat.tryParse(releaseDate)
|
date_upload = dateFormat.tryParse(releaseDate)
|
||||||
chapter_number = number
|
if (useSourceChapterNumber) {
|
||||||
|
chapter_number = number
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -102,8 +102,6 @@ internal class SourcesFilter(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
internal class ScanlationsFilter() : Filter.CheckBox("Show scanlations", true)
|
|
||||||
|
|
||||||
class FilterData(
|
class FilterData(
|
||||||
val id: String,
|
val id: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
@ -116,7 +114,7 @@ internal open class JsonMultiSelectFilter(
|
|||||||
private val param: String,
|
private val param: String,
|
||||||
genres: List<MultiSelectOption>,
|
genres: List<MultiSelectOption>,
|
||||||
) : Filter.Group<MultiSelectOption>(name, genres), JsonFilter {
|
) : Filter.Group<MultiSelectOption>(name, genres), JsonFilter {
|
||||||
override fun addToJsonObject(builder: JsonObjectBuilder) {
|
override fun addToJsonObject(builder: JsonObjectBuilder, additionExcludeList: List<String>) {
|
||||||
val whatToInclude = state.filter { it.state }.map { it.id }
|
val whatToInclude = state.filter { it.state }.map { it.id }
|
||||||
|
|
||||||
if (whatToInclude.isNotEmpty()) {
|
if (whatToInclude.isNotEmpty()) {
|
||||||
@ -134,9 +132,9 @@ internal open class JsonMultiSelectTriFilter(
|
|||||||
private val param: String,
|
private val param: String,
|
||||||
genres: List<MultiSelectTriOption>,
|
genres: List<MultiSelectTriOption>,
|
||||||
) : Filter.Group<MultiSelectTriOption>(name, genres), JsonFilter {
|
) : Filter.Group<MultiSelectTriOption>(name, genres), JsonFilter {
|
||||||
override fun addToJsonObject(builder: JsonObjectBuilder) {
|
override fun addToJsonObject(builder: JsonObjectBuilder, additionExcludeList: List<String>) {
|
||||||
val whatToInclude = state.filter { it.state == TriState.STATE_INCLUDE }.map { it.id }
|
val whatToInclude = state.filter { it.state == TriState.STATE_INCLUDE }.map { it.id }
|
||||||
val whatToExclude = state.filter { it.state == TriState.STATE_EXCLUDE }.map { it.id }
|
val whatToExclude = state.filter { it.state == TriState.STATE_EXCLUDE }.map { it.id } + additionExcludeList
|
||||||
|
|
||||||
with(builder) {
|
with(builder) {
|
||||||
if (whatToInclude.isNotEmpty()) {
|
if (whatToInclude.isNotEmpty()) {
|
||||||
@ -160,5 +158,74 @@ internal open class JsonMultiSelectTriFilter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal interface JsonFilter {
|
internal interface JsonFilter {
|
||||||
fun addToJsonObject(builder: JsonObjectBuilder)
|
fun addToJsonObject(builder: JsonObjectBuilder, additionExcludeList: List<String> = emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val GenresList = arrayOf(
|
||||||
|
"Romance",
|
||||||
|
"Drama",
|
||||||
|
"Manhwa",
|
||||||
|
"Fantasy",
|
||||||
|
"Manga",
|
||||||
|
"Comedy",
|
||||||
|
"Action",
|
||||||
|
"Mature",
|
||||||
|
"LGBTQIA+",
|
||||||
|
"Shoujo",
|
||||||
|
"Josei",
|
||||||
|
"Shounen",
|
||||||
|
"Supernatural",
|
||||||
|
"Boys' Love",
|
||||||
|
"Slice of Life",
|
||||||
|
"Seinen",
|
||||||
|
"Adventure",
|
||||||
|
"Manhua",
|
||||||
|
"School Life",
|
||||||
|
"Smut",
|
||||||
|
"Yaoi",
|
||||||
|
"Hentai",
|
||||||
|
"Historical",
|
||||||
|
"Isekai",
|
||||||
|
"Mystery",
|
||||||
|
"Psychological",
|
||||||
|
"Tragedy",
|
||||||
|
"Harem",
|
||||||
|
"Martial Arts",
|
||||||
|
"Science Fiction",
|
||||||
|
"Shounen Ai",
|
||||||
|
"Ecchi",
|
||||||
|
"Horror",
|
||||||
|
"Girls' Love",
|
||||||
|
"Anime",
|
||||||
|
"Thriller",
|
||||||
|
"Yuri",
|
||||||
|
"Coming of Age",
|
||||||
|
"Sports",
|
||||||
|
"OEL",
|
||||||
|
"Gender Bender",
|
||||||
|
"Suspense",
|
||||||
|
"Music",
|
||||||
|
"Shoujo Ai",
|
||||||
|
"Award Winning",
|
||||||
|
"Cooking",
|
||||||
|
"Crime",
|
||||||
|
"Doujinshi",
|
||||||
|
"Mecha",
|
||||||
|
"Oneshot",
|
||||||
|
"Philosophical",
|
||||||
|
"Magical Girls",
|
||||||
|
"Anthology",
|
||||||
|
"Wuxia",
|
||||||
|
"Medical",
|
||||||
|
"official colored",
|
||||||
|
"family life",
|
||||||
|
"parody",
|
||||||
|
"Superhero",
|
||||||
|
"4-Koma",
|
||||||
|
"educational",
|
||||||
|
"self-published",
|
||||||
|
"Animals",
|
||||||
|
"Magic",
|
||||||
|
"fan colored",
|
||||||
|
"monsters",
|
||||||
|
)
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import android.webkit.PermissionRequest
|
|||||||
import android.webkit.WebChromeClient
|
import android.webkit.WebChromeClient
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
|
import androidx.preference.MultiSelectListPreference
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
@ -125,7 +126,7 @@ class Kagane : HttpSource(), ConfigurableSource {
|
|||||||
ContentRatingFilter(
|
ContentRatingFilter(
|
||||||
preferences.contentRating.toSet(),
|
preferences.contentRating.toSet(),
|
||||||
),
|
),
|
||||||
ScanlationsFilter(),
|
GenresFilter(emptyList()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -142,7 +143,7 @@ class Kagane : HttpSource(), ConfigurableSource {
|
|||||||
ContentRatingFilter(
|
ContentRatingFilter(
|
||||||
preferences.contentRating.toSet(),
|
preferences.contentRating.toSet(),
|
||||||
),
|
),
|
||||||
ScanlationsFilter(),
|
GenresFilter(emptyList()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -154,6 +155,9 @@ class Kagane : HttpSource(), ConfigurableSource {
|
|||||||
val body = buildJsonObject {
|
val body = buildJsonObject {
|
||||||
filters.forEach { filter ->
|
filters.forEach { filter ->
|
||||||
when (filter) {
|
when (filter) {
|
||||||
|
is GenresFilter -> {
|
||||||
|
filter.addToJsonObject(this, preferences.excludedGenres.toList())
|
||||||
|
}
|
||||||
is JsonFilter -> {
|
is JsonFilter -> {
|
||||||
filter.addToJsonObject(this)
|
filter.addToJsonObject(this)
|
||||||
}
|
}
|
||||||
@ -175,15 +179,17 @@ class Kagane : HttpSource(), ConfigurableSource {
|
|||||||
is SortFilter -> {
|
is SortFilter -> {
|
||||||
filter.toUriPart().takeIf { it.isNotEmpty() }
|
filter.toUriPart().takeIf { it.isNotEmpty() }
|
||||||
?.let { uriPart -> addQueryParameter("sort", uriPart) }
|
?.let { uriPart -> addQueryParameter("sort", uriPart) }
|
||||||
}
|
?: run {
|
||||||
|
if (query.isBlank()) {
|
||||||
is ScanlationsFilter -> {
|
addQueryParameter("sort", "updated_at,desc")
|
||||||
addQueryParameter("scanlations", filter.state.toString())
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
addQueryParameter("scanlations", preferences.showScanlations.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
return POST(url.toString(), headers, body)
|
return POST(url.toString(), headers, body)
|
||||||
@ -203,7 +209,11 @@ class Kagane : HttpSource(), ConfigurableSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
return GET("$apiUrl/api/v1/series/${manga.url}", apiHeaders)
|
return mangaDetailsRequest(manga.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mangaDetailsRequest(seriesId: String): Request {
|
||||||
|
return GET("$apiUrl/api/v1/series/$seriesId", apiHeaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMangaUrl(manga: SManga): String {
|
override fun getMangaUrl(manga: SManga): String {
|
||||||
@ -213,8 +223,25 @@ class Kagane : HttpSource(), ConfigurableSource {
|
|||||||
// ============================== Chapters ==============================
|
// ============================== Chapters ==============================
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
|
val seriesId = response.request.url.toString()
|
||||||
|
.substringAfterLast("/")
|
||||||
|
|
||||||
val dto = response.parseAs<ChapterDto>()
|
val dto = response.parseAs<ChapterDto>()
|
||||||
return dto.content.map { it -> it.toSChapter() }.reversed()
|
|
||||||
|
val source = runCatching {
|
||||||
|
client.newCall(mangaDetailsRequest(seriesId))
|
||||||
|
.execute()
|
||||||
|
.parseAs<DetailsDto>()
|
||||||
|
.source
|
||||||
|
}.getOrDefault("")
|
||||||
|
val useSourceChapterNumber = source in setOf(
|
||||||
|
"Dark Horse Comics",
|
||||||
|
"Flame Comics",
|
||||||
|
"MangaDex",
|
||||||
|
"Square Enix Manga",
|
||||||
|
)
|
||||||
|
|
||||||
|
return dto.content.map { it -> it.toSChapter(useSourceChapterNumber) }.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun chapterListRequest(manga: SManga): Request {
|
override fun chapterListRequest(manga: SManga): Request {
|
||||||
@ -430,6 +457,12 @@ class Kagane : HttpSource(), ConfigurableSource {
|
|||||||
return CONTENT_RATINGS.slice(0..index.coerceAtLeast(0))
|
return CONTENT_RATINGS.slice(0..index.coerceAtLeast(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val SharedPreferences.excludedGenres: Set<String>
|
||||||
|
get() = this.getStringSet(GENRES_PREF, emptySet()) ?: emptySet()
|
||||||
|
|
||||||
|
private val SharedPreferences.showScanlations: Boolean
|
||||||
|
get() = this.getBoolean(SHOW_SCANLATIONS, SHOW_SCANLATIONS_DEFAULT)
|
||||||
|
|
||||||
private val SharedPreferences.dataSaver
|
private val SharedPreferences.dataSaver
|
||||||
get() = this.getBoolean(DATA_SAVER, false)
|
get() = this.getBoolean(DATA_SAVER, false)
|
||||||
|
|
||||||
@ -443,6 +476,27 @@ class Kagane : HttpSource(), ConfigurableSource {
|
|||||||
setDefaultValue(CONTENT_RATING_DEFAULT)
|
setDefaultValue(CONTENT_RATING_DEFAULT)
|
||||||
}.let(screen::addPreference)
|
}.let(screen::addPreference)
|
||||||
|
|
||||||
|
MultiSelectListPreference(screen.context).apply {
|
||||||
|
key = GENRES_PREF
|
||||||
|
title = "Exclude Genres"
|
||||||
|
entries = GenresList.map { it.replaceFirstChar { c -> c.uppercase() } }.toTypedArray()
|
||||||
|
entryValues = GenresList
|
||||||
|
summary = preferences.excludedGenres.joinToString { it.replaceFirstChar { c -> c.uppercase() } }
|
||||||
|
setDefaultValue(emptySet<String>())
|
||||||
|
|
||||||
|
setOnPreferenceChangeListener { _, values ->
|
||||||
|
val selected = values as Set<String>
|
||||||
|
this.summary = selected.joinToString { it.replaceFirstChar { c -> c.uppercase() } }
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}.let(screen::addPreference)
|
||||||
|
|
||||||
|
SwitchPreferenceCompat(screen.context).apply {
|
||||||
|
key = SHOW_SCANLATIONS
|
||||||
|
title = "Show scanlations"
|
||||||
|
setDefaultValue(SHOW_SCANLATIONS_DEFAULT)
|
||||||
|
}.let(screen::addPreference)
|
||||||
|
|
||||||
SwitchPreferenceCompat(screen.context).apply {
|
SwitchPreferenceCompat(screen.context).apply {
|
||||||
key = DATA_SAVER
|
key = DATA_SAVER
|
||||||
title = "Data saver"
|
title = "Data saver"
|
||||||
@ -462,6 +516,10 @@ class Kagane : HttpSource(), ConfigurableSource {
|
|||||||
"pornographic",
|
"pornographic",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private const val GENRES_PREF = "pref_genres_exclude"
|
||||||
|
private const val SHOW_SCANLATIONS = "pref_show_scanlations"
|
||||||
|
private const val SHOW_SCANLATIONS_DEFAULT = true
|
||||||
|
|
||||||
private const val DATA_SAVER = "data_saver_default"
|
private const val DATA_SAVER = "data_saver_default"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,7 +544,6 @@ class Kagane : HttpSource(), ConfigurableSource {
|
|||||||
// TagsFilter(),
|
// TagsFilter(),
|
||||||
// SourcesFilter(),
|
// SourcesFilter(),
|
||||||
Filter.Separator(),
|
Filter.Separator(),
|
||||||
ScanlationsFilter(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val response = metadataClient.newCall(
|
val response = metadataClient.newCall(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user