Pixiv: additional filters (#15495)

* Pixiv: additional filters

* ugh
This commit is contained in:
Solitai7e 2023-02-27 18:33:56 +00:00 committed by GitHub
parent 539530517e
commit 4c520021b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 53 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'Pixiv'
pkgNameSuffix = 'all.pixiv'
extClass = '.PixivFactory'
extVersionCode = 2
extVersionCode = 3
isNsfw = true
}

View File

@ -0,0 +1,41 @@
package eu.kanade.tachiyomi.extension.all.pixiv
import eu.kanade.tachiyomi.source.model.Filter
internal class FilterType : Filter.Select<String>("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<String>("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<String>("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 }
}

View File

@ -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<String, String> = emptyMap()): Request {
val url = baseUrl.toHttpUrl().newBuilder()
private fun apiRequest(method: String, path: String, params: Map<String, String> = 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 <reified T> apiResponseParse(response: Response): T {
if (!response.isSuccessful) {
@ -67,13 +61,20 @@ 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<PixivIllust> =
Observable.fromCallable { illustCache.get(url) }
.filter { it != null }
.switchIfEmpty(
Observable.fromCallable { illustCache.get(url) }.filter { it != null }.switchIfEmpty(
Observable.defer {
client.newCall(illustRequest(url)).asObservable()
.map { illustParse(it) }
@ -93,54 +94,56 @@ class Pixiv(override val lang: String) : HttpSource() {
override fun popularMangaParse(response: Response) = MangasPage(
mangas = apiResponseParse<PixivSearchResults>(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 word = URLEncoder.encode(query.ifBlank { "漫画" }, "UTF-8").replace("+", "%20")
val type = if (includeNonManga) "artworks" else "manga"
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",
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",
)
return apiRequest("GET", "/search/$type/$word", parameters)
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 endpoint = when (parameters["type"]) {
"all" -> "artworks"
"illust" -> "illustrations"
"manga" -> "manga"
else -> ""
}
return apiRequest("GET", "/search/$endpoint/$word", parameters)
}
override fun searchMangaParse(response: Response): MangasPage {
val mangas = apiResponseParse<PixivSearchResults>(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<PixivIllust>(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(),
),
)
}

View File

@ -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,
)