parent
539530517e
commit
4c520021b8
|
@ -6,7 +6,7 @@ ext {
|
||||||
extName = 'Pixiv'
|
extName = 'Pixiv'
|
||||||
pkgNameSuffix = 'all.pixiv'
|
pkgNameSuffix = 'all.pixiv'
|
||||||
extClass = '.PixivFactory'
|
extClass = '.PixivFactory'
|
||||||
extVersionCode = 2
|
extVersionCode = 3
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 }
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.extension.all.pixiv
|
||||||
|
|
||||||
import android.util.LruCache
|
import android.util.LruCache
|
||||||
import eu.kanade.tachiyomi.network.asObservable
|
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.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
|
||||||
|
@ -35,23 +34,18 @@ class Pixiv(override val lang: String) : HttpSource() {
|
||||||
|
|
||||||
override fun headersBuilder() = super.headersBuilder()
|
override fun headersBuilder() = super.headersBuilder()
|
||||||
.add("Referer", "$baseUrl/")
|
.add("Referer", "$baseUrl/")
|
||||||
|
.add("Accept-Language", siteLang)
|
||||||
|
|
||||||
class SortFilter : Filter.Sort("Sort", arrayOf("Date Uploaded"), Selection(0, false))
|
private fun apiRequest(method: String, path: String, params: Map<String, String> = emptyMap()) = Request(
|
||||||
class NonMangaFilter : Filter.CheckBox("Include non-manga posts", false)
|
url = baseUrl.toHttpUrl().newBuilder()
|
||||||
|
|
||||||
private fun apiRequest(method: String, path: String, params: Map<String, String> = emptyMap()): Request {
|
|
||||||
val url = baseUrl.toHttpUrl().newBuilder()
|
|
||||||
.addEncodedPathSegments("ajax$path")
|
.addEncodedPathSegments("ajax$path")
|
||||||
.addEncodedQueryParameter("lang", siteLang)
|
.addEncodedQueryParameter("lang", siteLang)
|
||||||
.apply { params.forEach { (k, v) -> addEncodedQueryParameter(k, v) } }
|
.apply { params.forEach { (k, v) -> addEncodedQueryParameter(k, v) } }
|
||||||
.build()
|
.build(),
|
||||||
|
|
||||||
val headers = headersBuilder()
|
headers = headersBuilder().add("Accept", "application/json").build(),
|
||||||
.add("Accept", "application/json")
|
method = method,
|
||||||
.build()
|
)
|
||||||
|
|
||||||
return Request(url, headers, method)
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <reified T> apiResponseParse(response: Response): T {
|
private inline fun <reified T> apiResponseParse(response: Response): T {
|
||||||
if (!response.isSuccessful) {
|
if (!response.isSuccessful) {
|
||||||
|
@ -67,13 +61,20 @@ class Pixiv(override val lang: String) : HttpSource() {
|
||||||
private fun illustUrlToId(url: String): String =
|
private fun illustUrlToId(url: String): String =
|
||||||
url.substringAfterLast("/")
|
url.substringAfterLast("/")
|
||||||
|
|
||||||
|
private fun urlEncode(string: String): String =
|
||||||
|
URLEncoder.encode(string, "UTF-8").replace("+", "%20")
|
||||||
|
|
||||||
private fun parseTimestamp(string: String) =
|
private fun parseTimestamp(string: String) =
|
||||||
runCatching { dateFormat.parse(string)?.time!! }.getOrDefault(0)
|
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> =
|
private fun fetchIllust(url: String): Observable<PixivIllust> =
|
||||||
Observable.fromCallable { illustCache.get(url) }
|
Observable.fromCallable { illustCache.get(url) }.filter { it != null }.switchIfEmpty(
|
||||||
.filter { it != null }
|
|
||||||
.switchIfEmpty(
|
|
||||||
Observable.defer {
|
Observable.defer {
|
||||||
client.newCall(illustRequest(url)).asObservable()
|
client.newCall(illustRequest(url)).asObservable()
|
||||||
.map { illustParse(it) }
|
.map { illustParse(it) }
|
||||||
|
@ -93,54 +94,56 @@ class Pixiv(override val lang: String) : HttpSource() {
|
||||||
override fun popularMangaParse(response: Response) = MangasPage(
|
override fun popularMangaParse(response: Response) = MangasPage(
|
||||||
mangas = apiResponseParse<PixivSearchResults>(response)
|
mangas = apiResponseParse<PixivSearchResults>(response)
|
||||||
.popular?.run { recent.orEmpty() + permanent.orEmpty() }
|
.popular?.run { recent.orEmpty() + permanent.orEmpty() }
|
||||||
?.map(::searchResultParse)
|
?.map(::parseSearchResult)
|
||||||
.orEmpty(),
|
.orEmpty(),
|
||||||
|
|
||||||
hasNextPage = false,
|
hasNextPage = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
var includeNonManga = false
|
val word = urlEncode(query.ifBlank { "漫画" })
|
||||||
var ascendingSort = false
|
|
||||||
|
|
||||||
filters.forEach {
|
val parameters = mutableMapOf(
|
||||||
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",
|
|
||||||
"word" to query,
|
"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 {
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
val mangas = apiResponseParse<PixivSearchResults>(response)
|
val mangas = apiResponseParse<PixivSearchResults>(response)
|
||||||
.run { manga ?: illustManga }?.data
|
.run { illustManga ?: illust ?: manga }?.data
|
||||||
?.filter { it.isAdContainer != true }
|
?.filter { it.isAdContainer != true }
|
||||||
?.map(::searchResultParse)
|
?.map(::parseSearchResult)
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
|
|
||||||
return MangasPage(mangas, hasNextPage = mangas.isNotEmpty())
|
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 =
|
override fun latestUpdatesRequest(page: Int): Request =
|
||||||
searchMangaRequest(page, "", FilterList())
|
searchMangaRequest(page, "", FilterList())
|
||||||
|
|
||||||
|
@ -151,7 +154,7 @@ class Pixiv(override val lang: String) : HttpSource() {
|
||||||
illustRequest(manga.url)
|
illustRequest(manga.url)
|
||||||
|
|
||||||
override fun mangaDetailsParse(response: Response) = SManga.create().apply {
|
override fun mangaDetailsParse(response: Response) = SManga.create().apply {
|
||||||
val illust = apiResponseParse<PixivIllust>(response)
|
val illust = illustParse(response)
|
||||||
|
|
||||||
url = "/artworks/${illust.id!!}"
|
url = "/artworks/${illust.id!!}"
|
||||||
title = illust.title ?: ""
|
title = illust.title ?: ""
|
||||||
|
@ -200,5 +203,14 @@ class Pixiv(override val lang: String) : HttpSource() {
|
||||||
override fun getChapterUrl(chapter: SChapter): String =
|
override fun getChapterUrl(chapter: SChapter): String =
|
||||||
baseUrl + chapter.url
|
baseUrl + chapter.url
|
||||||
|
|
||||||
override fun getFilterList() = FilterList(listOf(SortFilter(), NonMangaFilter()))
|
override fun getFilterList() = FilterList(
|
||||||
|
listOf(
|
||||||
|
FilterType(),
|
||||||
|
FilterRating(),
|
||||||
|
FilterSearchMode(),
|
||||||
|
FilterOrder(),
|
||||||
|
FilterDateBefore(),
|
||||||
|
FilterDateAfter(),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,8 +40,9 @@ internal data class PixivTags(
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class PixivSearchResults(
|
internal data class PixivSearchResults(
|
||||||
val manga: PixivSearchResultsIllusts? = null,
|
|
||||||
val illustManga: PixivSearchResultsIllusts? = null,
|
val illustManga: PixivSearchResultsIllusts? = null,
|
||||||
|
val illust: PixivSearchResultsIllusts? = null,
|
||||||
|
val manga: PixivSearchResultsIllusts? = null,
|
||||||
val popular: PixivSearchResultsPopular? = null,
|
val popular: PixivSearchResultsPopular? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue