diff --git a/src/en/kagane/build.gradle b/src/en/kagane/build.gradle index bc56297ef..5d57a7080 100644 --- a/src/en/kagane/build.gradle +++ b/src/en/kagane/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Kagane' extClass = '.Kagane' - extVersionCode = 9 + extVersionCode = 10 isNsfw = true } diff --git a/src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Dto.kt b/src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Dto.kt index 6e868479a..3782fbc5a 100644 --- a/src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Dto.kt +++ b/src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Dto.kt @@ -56,7 +56,7 @@ class DetailsDto( author = authors.joinToString() description = desc.toString() - genre = genres.joinToString() + genre = (listOf(source) + genres).joinToString() status = this@DetailsDto.status.toStatus() } @@ -86,11 +86,13 @@ class ChapterDto( @SerialName("number_sort") val number: Float, ) { - fun toSChapter(): SChapter = SChapter.create().apply { + fun toSChapter(useSourceChapterNumber: Boolean = false): SChapter = SChapter.create().apply { url = "$seriesId;$id;$pagesCount" name = title date_upload = dateFormat.tryParse(releaseDate) - chapter_number = number + if (useSourceChapterNumber) { + chapter_number = number + } } } diff --git a/src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Filters.kt b/src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Filters.kt index 813905a97..73e421687 100644 --- a/src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Filters.kt +++ b/src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Filters.kt @@ -102,8 +102,6 @@ internal class SourcesFilter( }, ) -internal class ScanlationsFilter() : Filter.CheckBox("Show scanlations", true) - class FilterData( val id: String, val name: String, @@ -116,7 +114,7 @@ internal open class JsonMultiSelectFilter( private val param: String, genres: List, ) : Filter.Group(name, genres), JsonFilter { - override fun addToJsonObject(builder: JsonObjectBuilder) { + override fun addToJsonObject(builder: JsonObjectBuilder, additionExcludeList: List) { val whatToInclude = state.filter { it.state }.map { it.id } if (whatToInclude.isNotEmpty()) { @@ -134,9 +132,9 @@ internal open class JsonMultiSelectTriFilter( private val param: String, genres: List, ) : Filter.Group(name, genres), JsonFilter { - override fun addToJsonObject(builder: JsonObjectBuilder) { + override fun addToJsonObject(builder: JsonObjectBuilder, additionExcludeList: List) { 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) { if (whatToInclude.isNotEmpty()) { @@ -160,5 +158,74 @@ internal open class JsonMultiSelectTriFilter( } internal interface JsonFilter { - fun addToJsonObject(builder: JsonObjectBuilder) + fun addToJsonObject(builder: JsonObjectBuilder, additionExcludeList: List = 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", +) diff --git a/src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Kagane.kt b/src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Kagane.kt index d6bfe5977..22cdbaf30 100644 --- a/src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Kagane.kt +++ b/src/en/kagane/src/eu/kanade/tachiyomi/extension/en/kagane/Kagane.kt @@ -13,6 +13,7 @@ import android.webkit.PermissionRequest import android.webkit.WebChromeClient import android.webkit.WebView import androidx.preference.ListPreference +import androidx.preference.MultiSelectListPreference import androidx.preference.PreferenceScreen import androidx.preference.SwitchPreferenceCompat import eu.kanade.tachiyomi.network.GET @@ -125,7 +126,7 @@ class Kagane : HttpSource(), ConfigurableSource { ContentRatingFilter( preferences.contentRating.toSet(), ), - ScanlationsFilter(), + GenresFilter(emptyList()), ), ) @@ -142,7 +143,7 @@ class Kagane : HttpSource(), ConfigurableSource { ContentRatingFilter( preferences.contentRating.toSet(), ), - ScanlationsFilter(), + GenresFilter(emptyList()), ), ) @@ -154,6 +155,9 @@ class Kagane : HttpSource(), ConfigurableSource { val body = buildJsonObject { filters.forEach { filter -> when (filter) { + is GenresFilter -> { + filter.addToJsonObject(this, preferences.excludedGenres.toList()) + } is JsonFilter -> { filter.addToJsonObject(this) } @@ -175,15 +179,17 @@ class Kagane : HttpSource(), ConfigurableSource { is SortFilter -> { filter.toUriPart().takeIf { it.isNotEmpty() } ?.let { uriPart -> addQueryParameter("sort", uriPart) } - } - - is ScanlationsFilter -> { - addQueryParameter("scanlations", filter.state.toString()) + ?: run { + if (query.isBlank()) { + addQueryParameter("sort", "updated_at,desc") + } + } } else -> {} } } + addQueryParameter("scanlations", preferences.showScanlations.toString()) } return POST(url.toString(), headers, body) @@ -203,7 +209,11 @@ class Kagane : HttpSource(), ConfigurableSource { } 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 { @@ -213,8 +223,25 @@ class Kagane : HttpSource(), ConfigurableSource { // ============================== Chapters ============================== override fun chapterListParse(response: Response): List { + val seriesId = response.request.url.toString() + .substringAfterLast("/") + val dto = response.parseAs() - return dto.content.map { it -> it.toSChapter() }.reversed() + + val source = runCatching { + client.newCall(mangaDetailsRequest(seriesId)) + .execute() + .parseAs() + .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 { @@ -430,6 +457,12 @@ class Kagane : HttpSource(), ConfigurableSource { return CONTENT_RATINGS.slice(0..index.coerceAtLeast(0)) } + private val SharedPreferences.excludedGenres: Set + get() = this.getStringSet(GENRES_PREF, emptySet()) ?: emptySet() + + private val SharedPreferences.showScanlations: Boolean + get() = this.getBoolean(SHOW_SCANLATIONS, SHOW_SCANLATIONS_DEFAULT) + private val SharedPreferences.dataSaver get() = this.getBoolean(DATA_SAVER, false) @@ -443,6 +476,27 @@ class Kagane : HttpSource(), ConfigurableSource { setDefaultValue(CONTENT_RATING_DEFAULT) }.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()) + + setOnPreferenceChangeListener { _, values -> + val selected = values as Set + 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 { key = DATA_SAVER title = "Data saver" @@ -462,6 +516,10 @@ class Kagane : HttpSource(), ConfigurableSource { "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" } @@ -486,7 +544,6 @@ class Kagane : HttpSource(), ConfigurableSource { // TagsFilter(), // SourcesFilter(), Filter.Separator(), - ScanlationsFilter(), ) val response = metadataClient.newCall(