ZeistManga: Add popular tab and more manga details + add sources (#19031)

* Update ZeistManga

* Wrong order

* Add more sources

* Lint

* Add MikoRoku

* Lint again

* Finally fix search

* Finally fix search

* Use correct parser for search
This commit is contained in:
bapeey 2023-11-20 11:35:09 -05:00 committed by GitHub
parent 824c0e44fa
commit e1f29d314a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 297 additions and 109 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@ -0,0 +1,41 @@
package eu.kanade.tachiyomi.extension.pt.animexnovel
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Response
class AnimeXNovel : ZeistManga("AnimeXNovel", "https://www.animexnovel.com", "pt-BR") {
override val mangaCategory: String = "Manga"
override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
val mangas = document.select("div.PopularPosts div.grid > figure").map { element ->
SManga.create().apply {
thumbnail_url = element.selectFirst("img")!!.attr("abs:src")
title = element.selectFirst("figcaption > a")!!.text()
setUrlWithoutDomain(element.selectFirst("figcaption > a")!!.attr("href"))
}
}.filter { it.title.contains("[Mangá]") }
return MangasPage(mangas, false)
}
override val mangaDetailsSelectorDescription = "div.bc-fff.s1 > h3:contains(Sinopse) ~ div[style=text-align: justify;]"
private val chapterListSelector = "div:has(> .list-judul:contains(Lista de Capítulos)) div#latest ul > li, div.tab:has(> label:contains(Lista de Capítulos)) div.tab-content ul > li"
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
val chapters = document.select(chapterListSelector)
return chapters.map {
SChapter.create().apply {
name = it.select("a").text()
setUrlWithoutDomain(it.select("a").attr("href"))
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View File

@ -0,0 +1,7 @@
package eu.kanade.tachiyomi.extension.pt.guildatierdraw
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
class GuildaTierDraw : ZeistManga("Guilda Tier Draw", "https://www.guildatierdraw.com", "pt-BR") {
override val mangaDetailsSelectorDescription = "#Sinopse"
}

View File

@ -2,12 +2,20 @@ package eu.kanade.tachiyomi.extension.ar.hijala
import eu.kanade.tachiyomi.multisrc.zeistmanga.Genre import eu.kanade.tachiyomi.multisrc.zeistmanga.Genre
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
import eu.kanade.tachiyomi.source.model.MangasPage
import okhttp3.Request
import okhttp3.Response
class Hijala : ZeistManga("Hijala", "https://hijala.blogspot.com", "ar") { class Hijala : ZeistManga("Hijala", "https://hijala.blogspot.com", "ar") {
override val hasFilters = true override val hasFilters = true
override val hasLanguageFilter = false override val hasLanguageFilter = false
override val supportsLatest = false
override fun popularMangaRequest(page: Int): Request = latestUpdatesRequest(page)
override fun popularMangaParse(response: Response): MangasPage = latestUpdatesParse(response)
override fun getGenreList(): List<Genre> = listOf( override fun getGenreList(): List<Genre> = listOf(
Genre("أكشن", "Action"), Genre("أكشن", "Action"),
Genre("أثارة", "Thriller"), Genre("أثارة", "Thriller"),

View File

@ -7,12 +7,14 @@ import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistMangaDto import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistMangaDto
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistMangaIntl import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistMangaIntl
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -30,6 +32,27 @@ class KomikRealm : ZeistManga(
override val chapterCategory = "" override val chapterCategory = ""
override fun popularMangaRequest(page: Int): Request {
val url = apiUrl("Project")
.addQueryParameter("orderby", "updated")
.addQueryParameter("max-results", "12")
.build()
return GET(url, headers)
}
override fun popularMangaParse(response: Response): MangasPage {
val jsonString = response.body.string()
val result = json.decodeFromString<ZeistMangaDto>(jsonString)
val mangas = result.feed?.entry.orEmpty()
.filter { it.category.orEmpty().any { category -> category.term == "Series" } }
.filter { !it.category.orEmpty().any { category -> category.term == "Anime" } }
.map { it.toSManga(baseUrl) }
return MangasPage(mangas, false)
}
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {
val document = response.asJsoup() val document = response.asJsoup()
val profileManga = document.select(".bigcontent") val profileManga = document.select(".bigcontent")
@ -54,12 +77,6 @@ class KomikRealm : ZeistManga(
} }
} }
private fun parseStatus(element: String): Int = when (element.lowercase()) {
"ongoing" -> SManga.ONGOING
"completed" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup() val document = response.asJsoup()

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@ -0,0 +1,7 @@
package eu.kanade.tachiyomi.extension.ar.mangasoul
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
class MangaSoul : ZeistManga("Manga Soul", "https://www.manga-soul.com", "ar") {
override val mangaDetailsSelectorInfo = ".y6x11p, .y6x11p > b > span"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

View File

@ -0,0 +1,26 @@
package eu.kanade.tachiyomi.extension.id.mikoroku
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Response
class MikoRoku : ZeistManga("MikoRoku", "https://www.mikoroku.web.id", "id") {
override val popularMangaSelector = "div.PopularPosts article"
override val popularMangaSelectorTitle = ".post-title a"
override val popularMangaSelectorUrl = ".post-title a"
override val pageListSelector = "article#reader div.separator a"
override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply {
val document = response.asJsoup()
with(document.selectFirst("div.section#main div.widget header")!!) {
thumbnail_url = selectFirst("img")!!.attr("abs:src")
genre = select("aside dl:has(dt:contains(Genre)) dd a")
.joinToString { it.text() }
status = parseStatus(selectFirst("span[data-status]")!!.text())
}
description = document.select("div.section#main div.widget div.grid #synopsis").text()
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

View File

@ -0,0 +1,22 @@
package eu.kanade.tachiyomi.extension.tr.mikrokosmosfansub
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Response
import org.jsoup.Jsoup
class MikrokosmosFansub : ZeistManga("Mikrokosmos Fansub", "https://mikrokosmosfb.blogspot.com", "tr") {
override val pageListSelector = "div.check-box > script"
override fun pageListParse(response: Response): List<Page> {
val document = response.asJsoup()
val script = document.select(pageListSelector)
val content = script.html().substringAfter("const content = `").substringBefore("`;")
val images = Jsoup.parse(content).select("a")
return images.select("img[src]").mapIndexed { i, img ->
Page(i, "", img.attr("abs:src"))
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

View File

@ -1,9 +0,0 @@
package eu.kanade.tachiyomi.extension.es.muslosnosekai
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
class MuslosNoSekai : ZeistManga("Muslos No Sekai", "https://muslosnosekai.blogspot.com", "es") {
override val hasFilters = true
override val hasLanguageFilter = false
}

View File

@ -1,8 +1,10 @@
package eu.kanade.tachiyomi.extension.id.shiyurasub package eu.kanade.tachiyomi.extension.id.shiyurasub
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Request
import okhttp3.Response import okhttp3.Response
class ShiyuraSub : ZeistManga("ShiyuraSub", "https://shiyurasub.blogspot.com", "id") { class ShiyuraSub : ZeistManga("ShiyuraSub", "https://shiyurasub.blogspot.com", "id") {
@ -10,6 +12,11 @@ class ShiyuraSub : ZeistManga("ShiyuraSub", "https://shiyurasub.blogspot.com", "
override val hasFilters = true override val hasFilters = true
override val hasLanguageFilter = false override val hasLanguageFilter = false
override val supportsLatest = false
override fun popularMangaRequest(page: Int): Request = latestUpdatesRequest(page)
override fun popularMangaParse(response: Response): MangasPage = latestUpdatesParse(response)
override val pageListSelector = "main.content article.container" override val pageListSelector = "main.content article.container"
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {

View File

@ -1,69 +1,13 @@
package eu.kanade.tachiyomi.extension.id.sobatmanku package eu.kanade.tachiyomi.extension.id.sobatmanku
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Request
import okhttp3.Response import okhttp3.Response
class SobatManKu : ZeistManga("SobatManKu", "https://www.sobatmanku19.site", "id") { class SobatManKu : ZeistManga("SobatManKu", "https://www.sobatmanku19.site", "id") {
override val supportsLatest = true
override val hasFilters = true override val hasFilters = true
override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/search/label/Update", headers)
}
override fun latestUpdatesParse(response: Response): MangasPage {
val doc = response.asJsoup()
val selector = doc.select(".grid.gtc-f141a > div")
val mangas = selector.map { element ->
SManga.create().apply {
element.select("a:nth-child(2)").let {
title = it.text()
setUrlWithoutDomain(it.attr("href"))
}
thumbnail_url = element.select("img").first()!!.attr("abs:src")
}
}
return MangasPage(mangas, false)
}
override fun mangaDetailsParse(response: Response): SManga {
val document = response.asJsoup()
val profileManga = document.selectFirst(".grid.gtc-235fr")!!
return SManga.create().apply {
thumbnail_url = profileManga.selectFirst("img")!!.attr("abs:src")
description = profileManga.select("#synopsis").text()
genre = profileManga.select("div.mt-15 > a[rel=tag]").joinToString { it.text() }
val infoElement = profileManga.select(".y6x11p")
infoElement.forEach {
val descText = it.select("span.dt").text()
when (it.ownText().trim()) {
"Status" -> {
status = parseStatus(descText)
}
"Author" -> {
author = descText
}
"Artist" -> {
artist = descText
}
}
}
}
}
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
return super.chapterListParse(response).onEach { return super.chapterListParse(response).onEach {
// fix some chapter name // fix some chapter name
@ -72,10 +16,4 @@ class SobatManKu : ZeistManga("SobatManKu", "https://www.sobatmanku19.site", "id
} }
} }
} }
private fun parseStatus(element: String): Int = when (element.lowercase()) {
"ongoing" -> SManga.ONGOING
"completed" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
} }

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.extension.id.tooncubus
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.Response import okhttp3.Response
@ -14,7 +13,7 @@ class Tooncubus : ZeistManga("Tooncubus", "https://www.tooncubus.top", "id") {
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
return response.asJsoup().selectFirst("ul.series-chapterlist")!!.select("div.flexch-infoz").map { element -> return response.asJsoup().selectFirst("ul.series-chapterlist")!!.select("div.flexch-infoz").map { element ->
SChapter.create().apply { SChapter.create().apply {
name = element.select("span")!!.text() name = element.select("span").text()
url = element.select("a").attr("href") // The website uses another domain for reading url = element.select("a").attr("href") // The website uses another domain for reading
} }
} }
@ -23,14 +22,4 @@ class Tooncubus : ZeistManga("Tooncubus", "https://www.tooncubus.top", "id") {
override fun pageListRequest(chapter: SChapter) = GET(chapter.url, headers) override fun pageListRequest(chapter: SChapter) = GET(chapter.url, headers)
override fun getChapterUrl(chapter: SChapter) = chapter.url override fun getChapterUrl(chapter: SChapter) = chapter.url
override fun mangaDetailsParse(response: Response): SManga {
val document = response.asJsoup()
val profileManga = document.selectFirst(".grid.gtc-235fr")!!
return SManga.create().apply {
thumbnail_url = profileManga.selectFirst("img")!!.attr("src")
genre = profileManga.select("div.mt-15 > a[rel=tag]")
.joinToString { it.text() }
}
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@ -0,0 +1,15 @@
package eu.kanade.tachiyomi.extension.pt.tyrantscans
import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga
import eu.kanade.tachiyomi.source.model.MangasPage
import okhttp3.Request
import okhttp3.Response
class TyrantScans : ZeistManga("Tyrant Scans", "https://www.tyrantscans.com", "pt-BR") {
override val supportsLatest = false
override fun popularMangaRequest(page: Int): Request = latestUpdatesRequest(page)
override fun popularMangaParse(response: Response): MangasPage = latestUpdatesParse(response)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request import okhttp3.Request
@ -24,13 +25,34 @@ abstract class ZeistManga(
override val lang: String, override val lang: String,
) : HttpSource() { ) : HttpSource() {
override val supportsLatest = false override val supportsLatest = true
private val json: Json by injectLazy() private val json: Json by injectLazy()
private val intl by lazy { ZeistMangaIntl(lang) } private val intl by lazy { ZeistMangaIntl(lang) }
override fun popularMangaRequest(page: Int): Request { override fun headersBuilder(): Headers.Builder = super.headersBuilder()
.add("Referer", "$baseUrl/")
override fun popularMangaRequest(page: Int): Request = GET(baseUrl, headers)
protected open val popularMangaSelector = "div.PopularPosts div.grid > figure"
protected open val popularMangaSelectorTitle = "figcaption > a"
protected open val popularMangaSelectorUrl = "figcaption > a"
override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
val mangas = document.select(popularMangaSelector).map { element ->
SManga.create().apply {
thumbnail_url = element.selectFirst("img")!!.attr("abs:src")
title = element.selectFirst(popularMangaSelectorTitle)!!.text()
setUrlWithoutDomain(element.selectFirst(popularMangaSelectorUrl)!!.attr("href"))
}
}
return MangasPage(mangas, false)
}
override fun latestUpdatesRequest(page: Int): Request {
val startIndex = maxMangaResults * (page - 1) + 1 val startIndex = maxMangaResults * (page - 1) + 1
val url = apiUrl() val url = apiUrl()
.addQueryParameter("orderby", "published") .addQueryParameter("orderby", "published")
@ -41,7 +63,7 @@ abstract class ZeistManga(
return GET(url, headers) return GET(url, headers)
} }
override fun popularMangaParse(response: Response): MangasPage { override fun latestUpdatesParse(response: Response): MangasPage {
val jsonString = response.body.string() val jsonString = response.body.string()
val result = json.decodeFromString<ZeistMangaDto>(jsonString) val result = json.decodeFromString<ZeistMangaDto>(jsonString)
@ -59,9 +81,6 @@ abstract class ZeistManga(
return MangasPage(mangalist, false) return MangasPage(mangalist, false)
} }
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException("Not used.")
override fun latestUpdatesParse(response: Response): MangasPage = throw UnsupportedOperationException("Not used.")
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val startIndex = maxMangaResults * (page - 1) + 1 val startIndex = maxMangaResults * (page - 1) + 1
val url = apiUrl() val url = apiUrl()
@ -70,7 +89,8 @@ abstract class ZeistManga(
if (query.isNotBlank()) { if (query.isNotBlank()) {
url.addQueryParameter("q", query) url.addQueryParameter("q", query)
return GET(url.build(), headers) val searchUrl = url.build().toString().replaceLast("q=", "q=label:$mangaCategory+")
return GET(searchUrl, headers)
} }
filters.forEach { filter -> filters.forEach { filter ->
@ -103,20 +123,65 @@ abstract class ZeistManga(
return GET(url.build(), headers) return GET(url.build(), headers)
} }
override fun searchMangaParse(response: Response) = popularMangaParse(response) override fun searchMangaParse(response: Response) = latestUpdatesParse(response)
protected open val statusSelectorList = listOf(
"Status",
"Estado",
)
protected open val authorSelectorList = listOf(
"Author",
"Autor",
"الكاتب",
"Yazar",
)
protected open val artisSelectorList = listOf(
"Artist",
"Artista",
"الرسام",
"Çizer",
)
protected open val mangaDetailsSelector = ".grid.gtc-235fr"
protected open val mangaDetailsSelectorDescription = "#synopsis"
protected open val mangaDetailsSelectorGenres = "div.mt-15 > a[rel=tag]"
protected open val mangaDetailsSelectorInfo = ".y6x11p"
protected open val mangaDetailsSelectorInfoTitle = "strong"
protected open val mangaDetailsSelectorInfoDescription = "span.dt"
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {
val document = response.asJsoup() val document = response.asJsoup()
val profileManga = document.selectFirst(".grid.gtc-235fr")!! val profileManga = document.selectFirst(mangaDetailsSelector)!!
return SManga.create().apply { return SManga.create().apply {
thumbnail_url = profileManga.selectFirst("img")!!.attr("abs:src") thumbnail_url = profileManga.selectFirst("img")!!.attr("abs:src")
description = profileManga.select("#synopsis").text() description = profileManga.select(mangaDetailsSelectorDescription).text()
genre = profileManga.select("div.mt-15 > a[rel=tag]") genre = profileManga.select(mangaDetailsSelectorGenres)
.joinToString { it.text() } .joinToString { it.text() }
val infoElement = profileManga.select(mangaDetailsSelectorInfo)
infoElement.forEach { element ->
val infoText = element.ownText().trim().ifEmpty { element.selectFirst(mangaDetailsSelectorInfoTitle)?.text()?.trim() ?: "" }
val descText = element.select(mangaDetailsSelectorInfoDescription).text().trim()
when {
statusSelectorList.any { infoText.contains(it) } -> {
status = parseStatus(descText)
}
authorSelectorList.any { infoText.contains(it) } -> {
author = descText
}
artisSelectorList.any { infoText.contains(it) } -> {
artist = descText
}
}
}
} }
} }
protected open val chapterCategory = "Chapter" protected open val chapterCategory: String = "Chapter"
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup() val document = response.asJsoup()
@ -197,7 +262,7 @@ abstract class ZeistManga(
return url.toString() return url.toString()
} }
open val pageListSelector = "div.check-box div.separator" protected open val pageListSelector = "div.check-box div.separator"
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val document = response.asJsoup() val document = response.asJsoup()
@ -209,7 +274,9 @@ abstract class ZeistManga(
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not used.") override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not used.")
open fun apiUrl(feed: String = "Series"): HttpUrl.Builder { protected open val mangaCategory: String = "Series"
open fun apiUrl(feed: String = mangaCategory): HttpUrl.Builder {
return "$baseUrl/feeds/posts/default/-/".toHttpUrl().newBuilder() return "$baseUrl/feeds/posts/default/-/".toHttpUrl().newBuilder()
.addPathSegment(feed) .addPathSegment(feed)
.addQueryParameter("alt", "json") .addQueryParameter("alt", "json")
@ -229,6 +296,9 @@ abstract class ZeistManga(
return FilterList(emptyList()) return FilterList(emptyList())
} }
filterList.add(Filter.Header(intl.filterWarning))
filterList.add(Filter.Separator())
if (hasStatusFilter) filterList.add(StatusList(intl.statusFilterTitle, getStatusList())) if (hasStatusFilter) filterList.add(StatusList(intl.statusFilterTitle, getStatusList()))
if (hasTypeFilter) filterList.add(TypeList(intl.typeFilterTitle, getTypeList())) if (hasTypeFilter) filterList.add(TypeList(intl.typeFilterTitle, getTypeList()))
if (hasLanguageFilter) filterList.add(LanguageList(intl.languageFilterTitle, getLanguageList())) if (hasLanguageFilter) filterList.add(LanguageList(intl.languageFilterTitle, getLanguageList()))
@ -306,6 +376,48 @@ abstract class ZeistManga(
Language("English", "English"), Language("English", "English"),
) )
protected open val statusOnGoingList = listOf(
"ongoing",
"en curso",
"ativo",
"lançando",
"مستمر",
)
protected open val statusCompletedList = listOf(
"completed",
"completo",
)
protected open val statusHiatusList = listOf(
"hiatus",
)
protected open val statusCancelledList = listOf(
"cancelled",
"dropped",
"dropado",
"abandonado",
"cancelado",
)
protected open fun parseStatus(element: String): Int = when (element.lowercase().trim()) {
in statusOnGoingList -> SManga.ONGOING
in statusCompletedList -> SManga.COMPLETED
in statusHiatusList -> SManga.ON_HIATUS
in statusCancelledList -> SManga.CANCELLED
else -> SManga.UNKNOWN
}
private fun String.replaceLast(oldValue: String, newValue: String): String {
val lastIndexOf = lastIndexOf(oldValue)
return if (lastIndexOf == -1) {
this
} else {
substring(0, lastIndexOf) + newValue + substring(lastIndexOf + oldValue.length)
}
}
companion object { companion object {
private const val maxMangaResults = 20 private const val maxMangaResults = 20
private const val maxChapterResults = 999999 private const val maxChapterResults = 999999

View File

@ -9,18 +9,26 @@ class ZeistMangaGenerator : ThemeSourceGenerator {
override val themeClass = "ZeistManga" override val themeClass = "ZeistManga"
override val baseVersionCode: Int = 7 override val baseVersionCode: Int = 8
override val sources = listOf( override val sources = listOf(
SingleLang("AnimeXNovel", "https://www.animexnovel.com", "pt-BR"),
SingleLang("Asupan Komik", "https://www.asupankomik.my.id", "id", overrideVersionCode = 1), SingleLang("Asupan Komik", "https://www.asupankomik.my.id", "id", overrideVersionCode = 1),
SingleLang("Eleven Scanlator", "https://elevenscanlator.blogspot.com", "pt-BR"),
SingleLang("Guilda Tier Draw", "https://www.guildatierdraw.com", "pt-BR", isNsfw = true),
SingleLang("Hijala", "https://hijala.blogspot.com", "ar"), SingleLang("Hijala", "https://hijala.blogspot.com", "ar"),
SingleLang("KLManhua", "https://klmanhua.blogspot.com", "id", isNsfw = true), SingleLang("KLManhua", "https://klmanhua.blogspot.com", "id", isNsfw = true),
SingleLang("Manga Ai Land", "https://manga-ai-land.blogspot.com", "ar"),
SingleLang("Muslos No Sekai", "https://muslosnosekai.blogspot.com", "es"),
SingleLang("ShiyuraSub", "https://shiyurasub.blogspot.com", "id"),
SingleLang("Tooncubus", "https://www.tooncubus.top", "id", isNsfw = true),
SingleLang("SobatManKu", "https://www.sobatmanku19.site", "id"),
SingleLang("KomikRealm", "https://www.komikrealm.my.id", "id"), SingleLang("KomikRealm", "https://www.komikrealm.my.id", "id"),
SingleLang("Loner Translations", "https://loner-tl.blogspot.com", "ar"),
SingleLang("Manga Ai Land", "https://manga-ai-land.blogspot.com", "ar"),
SingleLang("Manga Soul", "https://www.manga-soul.com", "ar", isNsfw = true),
SingleLang("MikoRoku", "https://www.mikoroku.web.id", "id", isNsfw = true),
SingleLang("Mikrokosmos Fansub", "https://mikrokosmosfb.blogspot.com", "tr", isNsfw = true),
SingleLang("ShiyuraSub", "https://shiyurasub.blogspot.com", "id"),
SingleLang("SobatManKu", "https://www.sobatmanku19.site", "id"),
SingleLang("Tooncubus", "https://www.tooncubus.top", "id", isNsfw = true),
SingleLang("Tyrant Scans", "https://www.tyrantscans.com", "pt-BR"),
SingleLang("Yokai", "https://yokai-team.blogspot.com", "ar"),
) )
companion object { companion object {