Tsumino: Refactor and add search filters (#1534)

Tsumino: Refactor and add search filters
This commit is contained in:
Rama Bondan Prakoso 2019-09-20 10:25:36 +07:00 committed by arkon
parent 61a4baf175
commit 0f3be64801
3 changed files with 143 additions and 47 deletions

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: Tsumino'
pkgNameSuffix = 'en.tsumino'
extClass = '.Tsumino'
extVersionCode = 1
extVersionCode = 2
libVersion = '1.2'
}

View File

@ -5,13 +5,15 @@ import com.github.salomonbrys.kotson.get
import com.google.gson.Gson
import com.google.gson.JsonObject
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.getGroups
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.FormBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
@ -22,7 +24,7 @@ class Tsumino: ParsedHttpSource() {
override val name = "Tsumino"
override val baseUrl = "https://tsumino.com"
override val baseUrl = "https://www.tsumino.com"
override val lang = "en"
@ -38,8 +40,8 @@ class Tsumino: ParsedHttpSource() {
override fun latestUpdatesParse(response: Response): MangasPage {
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()) {
val manga = SManga.create()
manga.url = "/entry/" + jsonManga[i]["entry"]["id"].asString
@ -48,7 +50,11 @@ class Tsumino: ParsedHttpSource() {
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")
@ -65,8 +71,38 @@ class Tsumino: ParsedHttpSource() {
override fun popularMangaNextPageSelector() = latestUpdatesNextPageSelector()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
throw UnsupportedOperationException("Not implemented yet")
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
// 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()
@ -84,18 +120,11 @@ class Tsumino: ParsedHttpSource() {
override fun mangaDetailsParse(document: Document): SManga {
val infoElement = document.select("div.book-page-container")
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.artist = getArtists(document)
manga.author = manga.artist
manga.status = SManga.COMPLETED
manga.genre = genres.joinToString(", ")
manga.thumbnail_url = infoElement.select("img").attr("src")
manga.description = getDesc(document)
@ -109,28 +138,12 @@ class Tsumino: ParsedHttpSource() {
}
override fun chapterListParse(response: Response): List<SChapter> {
val chapterList = mutableListOf<SChapter>()
val document = response.asJsoup()
val collection = document.select(chapterListSelector())
if (collection.isNotEmpty()) {
return collection.map { element ->
SChapter.create().apply {
name = element.text()
scanlator = getGroups(document)
setUrlWithoutDomain(element.attr("href").
replace("entry", "Read/Index"))
}
}.reversed()
return if (collection.isNotEmpty()) {
getCollection(document, chapterListSelector())
} else {
val chapter = SChapter.create().apply {
name = "Chapter"
scanlator = getGroups(document)
chapter_number = 1f
setUrlWithoutDomain(response.request().url().encodedPath().
replace("entry", "Read/Index"))
}
chapterList.add(chapter)
return chapterList
getChapter(document, response)
}
}
@ -163,5 +176,60 @@ class Tsumino: ParsedHttpSource() {
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)
}
}

View File

@ -1,9 +1,8 @@
package eu.kanade.tachiyomi.extension.en.tsumino
import eu.kanade.tachiyomi.source.model.SChapter
import okhttp3.Response
import org.jsoup.nodes.Document
import java.lang.StringBuilder
import java.text.SimpleDateFormat
import java.util.*
class TsuminoUtils {
companion object {
@ -21,7 +20,7 @@ class TsuminoUtils {
return stringBuilder.toString()
}
fun getGroups(document: Document): String? {
private fun getGroups(document: Document): String? {
val stringBuilder = StringBuilder()
val groups = document.select("#Group a")
@ -40,6 +39,7 @@ class TsuminoUtils {
val pages = document.select("#Pages").text()
val parodies = document.select("#Parody a")
val characters = document.select("#Character a")
val tags = document.select("#Tag a")
stringBuilder.append("Pages: $pages")
@ -66,17 +66,45 @@ class TsuminoUtils {
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()
}
fun getDate(document: Document): Long {
val timeString = document.select("#Uploaded").text()
return try {
SimpleDateFormat("yyyy MMMMM dd", Locale.ENGLISH).parse(timeString).time
}catch (e: Exception) {
0
fun getCollection(document: Document, selector: String): List<SChapter> {
return document.select(selector).map { element ->
SChapter.create().apply {
val chapterNum = element.select("span")[0].text()
val chapterName = element.select("span")[1].text()
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
}
}
}