Tsumino: Refactor and add search filters (#1534)
Tsumino: Refactor and add search filters
This commit is contained in:
parent
61a4baf175
commit
0f3be64801
|
@ -5,7 +5,7 @@ ext {
|
||||||
appName = 'Tachiyomi: Tsumino'
|
appName = 'Tachiyomi: Tsumino'
|
||||||
pkgNameSuffix = 'en.tsumino'
|
pkgNameSuffix = 'en.tsumino'
|
||||||
extClass = '.Tsumino'
|
extClass = '.Tsumino'
|
||||||
extVersionCode = 1
|
extVersionCode = 2
|
||||||
libVersion = '1.2'
|
libVersion = '1.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,13 +5,15 @@ import com.github.salomonbrys.kotson.get
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import eu.kanade.tachiyomi.extension.en.tsumino.TsuminoUtils.Companion.getArtists
|
import eu.kanade.tachiyomi.extension.en.tsumino.TsuminoUtils.Companion.getArtists
|
||||||
import eu.kanade.tachiyomi.extension.en.tsumino.TsuminoUtils.Companion.getDate
|
import eu.kanade.tachiyomi.extension.en.tsumino.TsuminoUtils.Companion.getCollection
|
||||||
|
import eu.kanade.tachiyomi.extension.en.tsumino.TsuminoUtils.Companion.getChapter
|
||||||
import eu.kanade.tachiyomi.extension.en.tsumino.TsuminoUtils.Companion.getDesc
|
import eu.kanade.tachiyomi.extension.en.tsumino.TsuminoUtils.Companion.getDesc
|
||||||
import eu.kanade.tachiyomi.extension.en.tsumino.TsuminoUtils.Companion.getGroups
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import eu.kanade.tachiyomi.source.model.*
|
import eu.kanade.tachiyomi.source.model.*
|
||||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import okhttp3.FormBody
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
@ -22,7 +24,7 @@ class Tsumino: ParsedHttpSource() {
|
||||||
|
|
||||||
override val name = "Tsumino"
|
override val name = "Tsumino"
|
||||||
|
|
||||||
override val baseUrl = "https://tsumino.com"
|
override val baseUrl = "https://www.tsumino.com"
|
||||||
|
|
||||||
override val lang = "en"
|
override val lang = "en"
|
||||||
|
|
||||||
|
@ -38,8 +40,8 @@ class Tsumino: ParsedHttpSource() {
|
||||||
|
|
||||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||||
val allManga = mutableListOf<SManga>()
|
val allManga = mutableListOf<SManga>()
|
||||||
val jsonManga = gson.fromJson<JsonObject>(response.body()!!.string())["data"].asJsonArray
|
val body = response.body()!!.string()
|
||||||
|
val jsonManga = gson.fromJson<JsonObject>(body)["data"].asJsonArray
|
||||||
for (i in 0 until jsonManga.size()) {
|
for (i in 0 until jsonManga.size()) {
|
||||||
val manga = SManga.create()
|
val manga = SManga.create()
|
||||||
manga.url = "/entry/" + jsonManga[i]["entry"]["id"].asString
|
manga.url = "/entry/" + jsonManga[i]["entry"]["id"].asString
|
||||||
|
@ -48,7 +50,11 @@ class Tsumino: ParsedHttpSource() {
|
||||||
allManga.add(manga)
|
allManga.add(manga)
|
||||||
}
|
}
|
||||||
|
|
||||||
return MangasPage(allManga, true)
|
val currentPage = gson.fromJson<JsonObject>(body)["pageNumber"].asString
|
||||||
|
val totalPage = gson.fromJson<JsonObject>(body)["pageCount"].asString
|
||||||
|
val hasNextPage = currentPage.toInt() != totalPage.toInt()
|
||||||
|
|
||||||
|
return MangasPage(allManga, hasNextPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun latestUpdatesFromElement(element: Element): SManga = throw UnsupportedOperationException("Not used")
|
override fun latestUpdatesFromElement(element: Element): SManga = throw UnsupportedOperationException("Not used")
|
||||||
|
@ -65,8 +71,38 @@ class Tsumino: ParsedHttpSource() {
|
||||||
|
|
||||||
override fun popularMangaNextPageSelector() = latestUpdatesNextPageSelector()
|
override fun popularMangaNextPageSelector() = latestUpdatesNextPageSelector()
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
throw UnsupportedOperationException("Not implemented yet")
|
// Taken from github.com/NerdNumber9/TachiyomiEH
|
||||||
|
val f = filters + getFilterList()
|
||||||
|
val advSearch = f.filterIsInstance<AdvSearchEntryFilter>().flatMap { filter ->
|
||||||
|
val splitState = filter.state.split(",").map(String::trim).filterNot(String::isBlank)
|
||||||
|
splitState.map {
|
||||||
|
AdvSearchEntry(filter.type, it.removePrefix("-"), it.startsWith("-"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val body = FormBody.Builder()
|
||||||
|
.add("PageNumber", page.toString())
|
||||||
|
.add("Text", query)
|
||||||
|
.add("Sort", SortType.values()[f.filterIsInstance<SortFilter>().first().state].name)
|
||||||
|
.add("List", "0")
|
||||||
|
.add("Length", LengthType.values()[f.filterIsInstance<LengthFilter>().first().state].id.toString())
|
||||||
|
.add("MinimumRating", f.filterIsInstance<MinimumRatingFilter>().first().state.toString())
|
||||||
|
.apply {
|
||||||
|
advSearch.forEachIndexed { index, entry ->
|
||||||
|
add("Tags[$index][Type]", entry.type.toString())
|
||||||
|
add("Tags[$index][Text]", entry.text)
|
||||||
|
add("Tags[$index][Exclude]", entry.exclude.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
if(f.filterIsInstance<ExcludeParodiesFilter>().first().state)
|
||||||
|
add("Exclude[]", "6")
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return POST("$baseUrl/Search/Operate/", headers, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response): MangasPage = latestUpdatesParse(response)
|
||||||
|
|
||||||
override fun searchMangaSelector() = latestUpdatesSelector()
|
override fun searchMangaSelector() = latestUpdatesSelector()
|
||||||
|
|
||||||
|
@ -84,18 +120,11 @@ class Tsumino: ParsedHttpSource() {
|
||||||
override fun mangaDetailsParse(document: Document): SManga {
|
override fun mangaDetailsParse(document: Document): SManga {
|
||||||
val infoElement = document.select("div.book-page-container")
|
val infoElement = document.select("div.book-page-container")
|
||||||
val manga = SManga.create()
|
val manga = SManga.create()
|
||||||
val genres = mutableListOf<String>()
|
|
||||||
|
|
||||||
infoElement.select("#Tag a").forEach { element ->
|
|
||||||
val genre = element.text()
|
|
||||||
genres.add(genre)
|
|
||||||
}
|
|
||||||
|
|
||||||
manga.title = infoElement.select("#Title").text()
|
manga.title = infoElement.select("#Title").text()
|
||||||
manga.artist = getArtists(document)
|
manga.artist = getArtists(document)
|
||||||
manga.author = manga.artist
|
manga.author = manga.artist
|
||||||
manga.status = SManga.COMPLETED
|
manga.status = SManga.COMPLETED
|
||||||
manga.genre = genres.joinToString(", ")
|
|
||||||
manga.thumbnail_url = infoElement.select("img").attr("src")
|
manga.thumbnail_url = infoElement.select("img").attr("src")
|
||||||
manga.description = getDesc(document)
|
manga.description = getDesc(document)
|
||||||
|
|
||||||
|
@ -109,28 +138,12 @@ class Tsumino: ParsedHttpSource() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
val chapterList = mutableListOf<SChapter>()
|
|
||||||
val document = response.asJsoup()
|
val document = response.asJsoup()
|
||||||
val collection = document.select(chapterListSelector())
|
val collection = document.select(chapterListSelector())
|
||||||
if (collection.isNotEmpty()) {
|
return if (collection.isNotEmpty()) {
|
||||||
return collection.map { element ->
|
getCollection(document, chapterListSelector())
|
||||||
SChapter.create().apply {
|
|
||||||
name = element.text()
|
|
||||||
scanlator = getGroups(document)
|
|
||||||
setUrlWithoutDomain(element.attr("href").
|
|
||||||
replace("entry", "Read/Index"))
|
|
||||||
}
|
|
||||||
}.reversed()
|
|
||||||
} else {
|
} else {
|
||||||
val chapter = SChapter.create().apply {
|
getChapter(document, response)
|
||||||
name = "Chapter"
|
|
||||||
scanlator = getGroups(document)
|
|
||||||
chapter_number = 1f
|
|
||||||
setUrlWithoutDomain(response.request().url().encodedPath().
|
|
||||||
replace("entry", "Read/Index"))
|
|
||||||
}
|
|
||||||
chapterList.add(chapter)
|
|
||||||
return chapterList
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,5 +176,60 @@ class Tsumino: ParsedHttpSource() {
|
||||||
|
|
||||||
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
|
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
|
||||||
|
|
||||||
override fun getFilterList() = FilterList()
|
data class AdvSearchEntry(val type: Int, val text: String, val exclude: Boolean)
|
||||||
|
|
||||||
|
override fun getFilterList() = FilterList(
|
||||||
|
Filter.Header("Separate tags with commas (,)"),
|
||||||
|
Filter.Header("Prepend with dash (-) to exclude"),
|
||||||
|
TagFilter(),
|
||||||
|
CategoryFilter(),
|
||||||
|
CollectionFilter(),
|
||||||
|
GroupFilter(),
|
||||||
|
ArtistFilter(),
|
||||||
|
ParodyFilter(),
|
||||||
|
CharactersFilter(),
|
||||||
|
UploaderFilter(),
|
||||||
|
|
||||||
|
Filter.Separator(),
|
||||||
|
|
||||||
|
SortFilter(),
|
||||||
|
LengthFilter(),
|
||||||
|
MinimumRatingFilter(),
|
||||||
|
ExcludeParodiesFilter()
|
||||||
|
)
|
||||||
|
|
||||||
|
class TagFilter : AdvSearchEntryFilter("Tags", 1)
|
||||||
|
class CategoryFilter : AdvSearchEntryFilter("Categories", 2)
|
||||||
|
class CollectionFilter : AdvSearchEntryFilter("Collections", 3)
|
||||||
|
class GroupFilter : AdvSearchEntryFilter("Groups", 4)
|
||||||
|
class ArtistFilter : AdvSearchEntryFilter("Artists", 5)
|
||||||
|
class ParodyFilter : AdvSearchEntryFilter("Parodies", 6)
|
||||||
|
class CharactersFilter : AdvSearchEntryFilter("Characters", 7)
|
||||||
|
class UploaderFilter : AdvSearchEntryFilter("Uploaders", 8)
|
||||||
|
open class AdvSearchEntryFilter(name: String, val type: Int) : Filter.Text(name)
|
||||||
|
|
||||||
|
class SortFilter : Filter.Select<SortType>("Sort by", SortType.values())
|
||||||
|
class LengthFilter : Filter.Select<LengthType>("Length", LengthType.values())
|
||||||
|
class MinimumRatingFilter : Filter.Select<String>("Minimum rating", (0 .. 5).map { "$it stars" }.toTypedArray())
|
||||||
|
class ExcludeParodiesFilter : Filter.CheckBox("Exclude parodies")
|
||||||
|
|
||||||
|
enum class SortType {
|
||||||
|
Newest,
|
||||||
|
Oldest,
|
||||||
|
Alphabetical,
|
||||||
|
Rating,
|
||||||
|
Pages,
|
||||||
|
Views,
|
||||||
|
Random,
|
||||||
|
Comments,
|
||||||
|
Popularity
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class LengthType(val id: Int) {
|
||||||
|
Any(0),
|
||||||
|
Short(1),
|
||||||
|
Medium(2),
|
||||||
|
Long(3)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.tsumino
|
package eu.kanade.tachiyomi.extension.en.tsumino
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
|
import okhttp3.Response
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import java.lang.StringBuilder
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class TsuminoUtils {
|
class TsuminoUtils {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -21,7 +20,7 @@ class TsuminoUtils {
|
||||||
return stringBuilder.toString()
|
return stringBuilder.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getGroups(document: Document): String? {
|
private fun getGroups(document: Document): String? {
|
||||||
val stringBuilder = StringBuilder()
|
val stringBuilder = StringBuilder()
|
||||||
val groups = document.select("#Group a")
|
val groups = document.select("#Group a")
|
||||||
|
|
||||||
|
@ -40,6 +39,7 @@ class TsuminoUtils {
|
||||||
val pages = document.select("#Pages").text()
|
val pages = document.select("#Pages").text()
|
||||||
val parodies = document.select("#Parody a")
|
val parodies = document.select("#Parody a")
|
||||||
val characters = document.select("#Character a")
|
val characters = document.select("#Character a")
|
||||||
|
val tags = document.select("#Tag a")
|
||||||
|
|
||||||
stringBuilder.append("Pages: $pages")
|
stringBuilder.append("Pages: $pages")
|
||||||
|
|
||||||
|
@ -66,17 +66,45 @@ class TsuminoUtils {
|
||||||
stringBuilder.append(", ")
|
stringBuilder.append(", ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tags.size > 0) {
|
||||||
|
stringBuilder.append("\n\n")
|
||||||
|
stringBuilder.append("Tags: ")
|
||||||
|
|
||||||
|
tags.forEach {
|
||||||
|
stringBuilder.append(it.text())
|
||||||
|
|
||||||
|
if (it != tags.last())
|
||||||
|
stringBuilder.append(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return stringBuilder.toString()
|
return stringBuilder.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDate(document: Document): Long {
|
fun getCollection(document: Document, selector: String): List<SChapter> {
|
||||||
val timeString = document.select("#Uploaded").text()
|
return document.select(selector).map { element ->
|
||||||
return try {
|
SChapter.create().apply {
|
||||||
SimpleDateFormat("yyyy MMMMM dd", Locale.ENGLISH).parse(timeString).time
|
val chapterNum = element.select("span")[0].text()
|
||||||
}catch (e: Exception) {
|
val chapterName = element.select("span")[1].text()
|
||||||
0
|
name = "$chapterNum. $chapterName"
|
||||||
|
scanlator = getGroups(document)
|
||||||
|
url = element.attr("href").replace("entry", "Read/Index")
|
||||||
}
|
}
|
||||||
|
}.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getChapter(document: Document, response: Response): List<SChapter> {
|
||||||
|
val chapterList = mutableListOf<SChapter>()
|
||||||
|
val chapter = SChapter.create().apply {
|
||||||
|
name = "Chapter"
|
||||||
|
scanlator = getGroups(document)
|
||||||
|
chapter_number = 1f
|
||||||
|
url = response.request().url().encodedPath().
|
||||||
|
replace("entry", "Read/Index")
|
||||||
|
}
|
||||||
|
chapterList.add(chapter)
|
||||||
|
return chapterList
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue