From 4c520021b89bb467d66f50aebc45d7f75632cb26 Mon Sep 17 00:00:00 2001 From: Solitai7e <60846439+Solitai7e@users.noreply.github.com> Date: Mon, 27 Feb 2023 18:33:56 +0000 Subject: [PATCH] Pixiv: additional filters (#15495) * Pixiv: additional filters * ugh --- src/all/pixiv/build.gradle | 2 +- .../tachiyomi/extension/all/pixiv/Filters.kt | 41 +++++++ .../tachiyomi/extension/all/pixiv/Pixiv.kt | 114 ++++++++++-------- .../extension/all/pixiv/Structures.kt | 3 +- 4 files changed, 107 insertions(+), 53 deletions(-) create mode 100644 src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Filters.kt diff --git a/src/all/pixiv/build.gradle b/src/all/pixiv/build.gradle index 91174fa15..0215f94db 100644 --- a/src/all/pixiv/build.gradle +++ b/src/all/pixiv/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Pixiv' pkgNameSuffix = 'all.pixiv' extClass = '.PixivFactory' - extVersionCode = 2 + extVersionCode = 3 isNsfw = true } diff --git a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Filters.kt b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Filters.kt new file mode 100644 index 000000000..bea80d1ce --- /dev/null +++ b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Filters.kt @@ -0,0 +1,41 @@ +package eu.kanade.tachiyomi.extension.all.pixiv +import eu.kanade.tachiyomi.source.model.Filter + +internal class FilterType : Filter.Select("Type", values, 2) { + companion object { + val keys = arrayOf("all", "illust", "manga") + val values = arrayOf("All", "Illustrations", "Manga") + } + + val value: String get() = keys[state] +} + +internal class FilterRating : Filter.Select("Rating", values, 0) { + companion object { + val keys = arrayOf("all", "safe", "r18") + val values = arrayOf("All", "All ages", "R-18") + } + + val value: String get() = keys[state] +} + +internal class FilterSearchMode : Filter.Select("Mode", values, 1) { + companion object { + val keys = arrayOf("s_tag", "s_tag_full", "s_tc") + val values = arrayOf("Tags (partial)", "Tags (full)", "Title, description") + } + + val value: String get() = keys[state] +} + +internal class FilterOrder : Filter.Sort("Order", arrayOf("Date posted")) { + val value: String get() = if (state?.ascending == true) "date" else "date_d" +} + +internal class FilterDateBefore : Filter.Text("Posted before") { + val value: String? get() = state.ifEmpty { null } +} + +internal class FilterDateAfter : Filter.Text("Posted after") { + val value: String? get() = state.ifEmpty { null } +} diff --git a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Pixiv.kt b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Pixiv.kt index 3c675be77..81cfa939d 100644 --- a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Pixiv.kt +++ b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Pixiv.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.extension.all.pixiv import android.util.LruCache import eu.kanade.tachiyomi.network.asObservable -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 @@ -35,23 +34,18 @@ class Pixiv(override val lang: String) : HttpSource() { override fun headersBuilder() = super.headersBuilder() .add("Referer", "$baseUrl/") + .add("Accept-Language", siteLang) - class SortFilter : Filter.Sort("Sort", arrayOf("Date Uploaded"), Selection(0, false)) - class NonMangaFilter : Filter.CheckBox("Include non-manga posts", false) - - private fun apiRequest(method: String, path: String, params: Map = emptyMap()): Request { - val url = baseUrl.toHttpUrl().newBuilder() + private fun apiRequest(method: String, path: String, params: Map = emptyMap()) = Request( + url = baseUrl.toHttpUrl().newBuilder() .addEncodedPathSegments("ajax$path") .addEncodedQueryParameter("lang", siteLang) .apply { params.forEach { (k, v) -> addEncodedQueryParameter(k, v) } } - .build() + .build(), - val headers = headersBuilder() - .add("Accept", "application/json") - .build() - - return Request(url, headers, method) - } + headers = headersBuilder().add("Accept", "application/json").build(), + method = method, + ) private inline fun apiResponseParse(response: Response): T { if (!response.isSuccessful) { @@ -67,19 +61,26 @@ class Pixiv(override val lang: String) : HttpSource() { private fun illustUrlToId(url: String): String = url.substringAfterLast("/") + private fun urlEncode(string: String): String = + URLEncoder.encode(string, "UTF-8").replace("+", "%20") + private fun parseTimestamp(string: String) = runCatching { dateFormat.parse(string)?.time!! }.getOrDefault(0) + private fun parseSearchResult(result: PixivSearchResult) = SManga.create().apply { + url = "/artworks/${result.id!!}" + title = result.title ?: "" + thumbnail_url = result.url + } + private fun fetchIllust(url: String): Observable = - Observable.fromCallable { illustCache.get(url) } - .filter { it != null } - .switchIfEmpty( - Observable.defer { - client.newCall(illustRequest(url)).asObservable() - .map { illustParse(it) } - .doOnNext { illustCache.put(url, it) } - }, - ) + Observable.fromCallable { illustCache.get(url) }.filter { it != null }.switchIfEmpty( + Observable.defer { + client.newCall(illustRequest(url)).asObservable() + .map { illustParse(it) } + .doOnNext { illustCache.put(url, it) } + }, + ) private fun illustRequest(url: String): Request = apiRequest("GET", "/illust/${illustUrlToId(url)}") @@ -93,54 +94,56 @@ class Pixiv(override val lang: String) : HttpSource() { override fun popularMangaParse(response: Response) = MangasPage( mangas = apiResponseParse(response) .popular?.run { recent.orEmpty() + permanent.orEmpty() } - ?.map(::searchResultParse) + ?.map(::parseSearchResult) .orEmpty(), hasNextPage = false, ) override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - var includeNonManga = false - var ascendingSort = false + val word = urlEncode(query.ifBlank { "漫画" }) - filters.forEach { - when (it) { - is NonMangaFilter -> includeNonManga = it.state - is SortFilter -> ascendingSort = it.state!!.ascending - else -> throw IllegalStateException("Filter unrecognized") + val parameters = mutableMapOf( + "word" to query, + "order" to "date_d", + "mode" to "all", + "p" to page.toString(), + "s_mode" to "s_tag_full", + "type" to "manga", + ) + + filters.forEach { filter -> + when (filter) { + is FilterType -> parameters["type"] = filter.value + is FilterRating -> parameters["mode"] = filter.value + is FilterSearchMode -> parameters["s_mode"] = filter.value + is FilterOrder -> parameters["order"] = filter.value + is FilterDateBefore -> filter.value?.let { parameters["ecd"] = it } + is FilterDateAfter -> filter.value?.let { parameters["scd"] = it } + else -> {} } } - val word = URLEncoder.encode(query.ifBlank { "漫画" }, "UTF-8").replace("+", "%20") - val type = if (includeNonManga) "artworks" else "manga" + val endpoint = when (parameters["type"]) { + "all" -> "artworks" + "illust" -> "illustrations" + "manga" -> "manga" + else -> "" + } - val parameters = mapOf( - "mode" to "all", - "order" to if (ascendingSort) "date" else "date_d", - "p" to page.toString(), - "type" to if (includeNonManga) "all" else "manga", - "word" to query, - ) - - return apiRequest("GET", "/search/$type/$word", parameters) + return apiRequest("GET", "/search/$endpoint/$word", parameters) } override fun searchMangaParse(response: Response): MangasPage { val mangas = apiResponseParse(response) - .run { manga ?: illustManga }?.data + .run { illustManga ?: illust ?: manga }?.data ?.filter { it.isAdContainer != true } - ?.map(::searchResultParse) + ?.map(::parseSearchResult) .orEmpty() return MangasPage(mangas, hasNextPage = mangas.isNotEmpty()) } - private fun searchResultParse(result: PixivSearchResult) = SManga.create().apply { - url = "/artworks/${result.id!!}" - title = result.title ?: "" - thumbnail_url = result.url - } - override fun latestUpdatesRequest(page: Int): Request = searchMangaRequest(page, "", FilterList()) @@ -151,7 +154,7 @@ class Pixiv(override val lang: String) : HttpSource() { illustRequest(manga.url) override fun mangaDetailsParse(response: Response) = SManga.create().apply { - val illust = apiResponseParse(response) + val illust = illustParse(response) url = "/artworks/${illust.id!!}" title = illust.title ?: "" @@ -200,5 +203,14 @@ class Pixiv(override val lang: String) : HttpSource() { override fun getChapterUrl(chapter: SChapter): String = baseUrl + chapter.url - override fun getFilterList() = FilterList(listOf(SortFilter(), NonMangaFilter())) + override fun getFilterList() = FilterList( + listOf( + FilterType(), + FilterRating(), + FilterSearchMode(), + FilterOrder(), + FilterDateBefore(), + FilterDateAfter(), + ), + ) } diff --git a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Structures.kt b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Structures.kt index a9634fbe2..f5dca8fe8 100644 --- a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Structures.kt +++ b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Structures.kt @@ -40,8 +40,9 @@ internal data class PixivTags( @Serializable internal data class PixivSearchResults( - val manga: PixivSearchResultsIllusts? = null, val illustManga: PixivSearchResultsIllusts? = null, + val illust: PixivSearchResultsIllusts? = null, + val manga: PixivSearchResultsIllusts? = null, val popular: PixivSearchResultsPopular? = null, )