parent
2b8629b21a
commit
61a4baf175
|
@ -0,0 +1,17 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
ext {
|
||||
appName = 'Tachiyomi: Tsumino'
|
||||
pkgNameSuffix = 'en.tsumino'
|
||||
extClass = '.Tsumino'
|
||||
extVersionCode = 1
|
||||
libVersion = '1.2'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'com.google.code.gson:gson:2.8.5'
|
||||
compileOnly 'com.github.salomonbrys.kotson:kotson:2.5.0'
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
|
@ -0,0 +1,167 @@
|
|||
package eu.kanade.tachiyomi.extension.en.tsumino
|
||||
|
||||
import com.github.salomonbrys.kotson.fromJson
|
||||
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.getDesc
|
||||
import eu.kanade.tachiyomi.extension.en.tsumino.TsuminoUtils.Companion.getGroups
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.source.model.*
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.nodes.Element
|
||||
|
||||
class Tsumino: ParsedHttpSource() {
|
||||
|
||||
override val name = "Tsumino"
|
||||
|
||||
override val baseUrl = "https://tsumino.com"
|
||||
|
||||
override val lang = "en"
|
||||
|
||||
override val supportsLatest = true
|
||||
|
||||
override val client: OkHttpClient = network.cloudflareClient
|
||||
|
||||
private val gson = Gson()
|
||||
|
||||
override fun latestUpdatesSelector() = "Not needed"
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/Search/Operate/?PageNumber=$page&Sort=Newest")
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
||||
val allManga = mutableListOf<SManga>()
|
||||
val jsonManga = gson.fromJson<JsonObject>(response.body()!!.string())["data"].asJsonArray
|
||||
|
||||
for (i in 0 until jsonManga.size()) {
|
||||
val manga = SManga.create()
|
||||
manga.url = "/entry/" + jsonManga[i]["entry"]["id"].asString
|
||||
manga.title = jsonManga[i]["entry"]["title"].asString
|
||||
manga.thumbnail_url = jsonManga[i]["entry"]["thumbnailUrl"].asString
|
||||
allManga.add(manga)
|
||||
}
|
||||
|
||||
return MangasPage(allManga, true)
|
||||
}
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element): SManga = throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun latestUpdatesNextPageSelector() = "Not needed"
|
||||
|
||||
override fun popularMangaRequest(page: Int) = GET("$baseUrl/Search/Operate/?PageNumber=$page&Sort=Popularity")
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage = latestUpdatesParse(response)
|
||||
|
||||
override fun popularMangaFromElement(element: Element) = latestUpdatesFromElement(element)
|
||||
|
||||
override fun popularMangaSelector() = latestUpdatesSelector()
|
||||
|
||||
override fun popularMangaNextPageSelector() = latestUpdatesNextPageSelector()
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
|
||||
throw UnsupportedOperationException("Not implemented yet")
|
||||
|
||||
override fun searchMangaSelector() = latestUpdatesSelector()
|
||||
|
||||
override fun searchMangaFromElement(element: Element) = latestUpdatesFromElement(element)
|
||||
|
||||
override fun searchMangaNextPageSelector() = latestUpdatesNextPageSelector()
|
||||
|
||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||
if (manga.url.startsWith("http")) {
|
||||
return GET(manga.url, headers)
|
||||
}
|
||||
return super.mangaDetailsRequest(manga)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
return manga
|
||||
}
|
||||
override fun chapterListRequest(manga: SManga): Request {
|
||||
if (manga.url.startsWith("http")) {
|
||||
return GET(manga.url, headers)
|
||||
}
|
||||
return super.chapterListRequest(manga)
|
||||
}
|
||||
|
||||
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()
|
||||
} 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
|
||||
}
|
||||
}
|
||||
|
||||
override fun chapterListSelector() = ".book-collection-table a"
|
||||
|
||||
override fun chapterFromElement(element: Element) = throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
if (chapter.url.startsWith("http")) {
|
||||
return GET(chapter.url, headers)
|
||||
}
|
||||
return super.pageListRequest(chapter)
|
||||
}
|
||||
|
||||
override fun pageListParse(document: Document): List<Page> {
|
||||
val pages = mutableListOf<Page>()
|
||||
val numPages = document.select("h1").text().split(" ").last()
|
||||
|
||||
if (numPages.isNotEmpty()) {
|
||||
for (i in 1 until numPages.toInt() + 1) {
|
||||
val data = document.select("#image-container").attr("data-cdn")
|
||||
.replace("[PAGE]", i.toString())
|
||||
pages.add(Page(i, "", data))
|
||||
}
|
||||
} else {
|
||||
throw UnsupportedOperationException("Error: Open in WebView and solve the Captcha!")
|
||||
}
|
||||
return pages
|
||||
}
|
||||
|
||||
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun getFilterList() = FilterList()
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package eu.kanade.tachiyomi.extension.en.tsumino
|
||||
|
||||
import org.jsoup.nodes.Document
|
||||
import java.lang.StringBuilder
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class TsuminoUtils {
|
||||
companion object {
|
||||
fun getArtists(document: Document): String {
|
||||
val stringBuilder = StringBuilder()
|
||||
val artists = document.select("#Artist a")
|
||||
|
||||
artists.forEach {
|
||||
stringBuilder.append(it.text())
|
||||
|
||||
if (it != artists.last())
|
||||
stringBuilder.append(", ")
|
||||
}
|
||||
|
||||
return stringBuilder.toString()
|
||||
}
|
||||
|
||||
fun getGroups(document: Document): String? {
|
||||
val stringBuilder = StringBuilder()
|
||||
val groups = document.select("#Group a")
|
||||
|
||||
groups.forEach {
|
||||
stringBuilder.append(it.text())
|
||||
|
||||
if (it != groups.last())
|
||||
stringBuilder.append(", ")
|
||||
}
|
||||
|
||||
return if (stringBuilder.toString().isEmpty()) null else stringBuilder.toString()
|
||||
}
|
||||
|
||||
fun getDesc(document: Document): String {
|
||||
val stringBuilder = StringBuilder()
|
||||
val pages = document.select("#Pages").text()
|
||||
val parodies = document.select("#Parody a")
|
||||
val characters = document.select("#Character a")
|
||||
|
||||
stringBuilder.append("Pages: $pages")
|
||||
|
||||
if (parodies.size > 0) {
|
||||
stringBuilder.append("\n\n")
|
||||
stringBuilder.append("Parodies: ")
|
||||
|
||||
parodies.forEach {
|
||||
stringBuilder.append(it.text())
|
||||
|
||||
if (it != parodies.last())
|
||||
stringBuilder.append(", ")
|
||||
}
|
||||
}
|
||||
|
||||
if (characters.size > 0) {
|
||||
stringBuilder.append("\n\n")
|
||||
stringBuilder.append("Characters: ")
|
||||
|
||||
characters.forEach {
|
||||
stringBuilder.append(it.text())
|
||||
|
||||
if (it != characters.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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue