Doujindesu: Add more filter and fix description (#8405)
* [WIP] DoujinDesu Add Group and Series Filter https://github.com/keiyoushi/extensions-source/issues/7816 * Remove Unused Feature * Bump version * Add Class * Rephrase * Progress today * Fixing errors that were not fixed yesterday * Separate Author Filter same as Group and Series * Update again about the desc * Uhh my left over * Changes requested Thanks to @AwkwardPeak7 * I left it * Don't use Author, Group, and Series filter when query isn't blank
This commit is contained in:
parent
fceca33d30
commit
b9a05d4fcc
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'DoujinDesu'
|
extName = 'DoujinDesu'
|
||||||
extClass = '.DoujinDesu'
|
extClass = '.DoujinDesu'
|
||||||
extVersionCode = 9
|
extVersionCode = 10
|
||||||
isNsfw = true
|
isNsfw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ 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.source.online.ParsedHttpSource
|
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||||
import eu.kanade.tachiyomi.util.asJsoup
|
import eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import keiyoushi.utils.firstInstanceOrNull
|
||||||
import keiyoushi.utils.getPreferencesLazy
|
import keiyoushi.utils.getPreferencesLazy
|
||||||
import okhttp3.FormBody
|
import okhttp3.FormBody
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
@ -249,6 +250,8 @@ class DoujinDesu : ParsedHttpSource(), ConfigurableSource {
|
|||||||
)
|
)
|
||||||
|
|
||||||
private class AuthorFilter : Filter.Text("Author")
|
private class AuthorFilter : Filter.Text("Author")
|
||||||
|
private class GroupFilter : Filter.Text("Group")
|
||||||
|
private class SeriesFilter : Filter.Text("Series")
|
||||||
private class CharacterFilter : Filter.Text("Karakter")
|
private class CharacterFilter : Filter.Text("Karakter")
|
||||||
private class CategoryNames(categories: Array<Category>) : Filter.Select<Category>("Kategori", categories, 0)
|
private class CategoryNames(categories: Array<Category>) : Filter.Select<Category>("Kategori", categories, 0)
|
||||||
private class OrderBy(orders: Array<Order>) : Filter.Select<Order>("Urutkan", orders, 0)
|
private class OrderBy(orders: Array<Order>) : Filter.Select<Order>("Urutkan", orders, 0)
|
||||||
@ -317,8 +320,10 @@ class DoujinDesu : ParsedHttpSource(), ConfigurableSource {
|
|||||||
// Search & FIlter
|
// Search & FIlter
|
||||||
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
// Anything else filter handling
|
||||||
val url = "$baseUrl/manga/page/$page/".toHttpUrl().newBuilder()
|
val url = "$baseUrl/manga/page/$page/".toHttpUrl().newBuilder()
|
||||||
.addQueryParameter("title", query)
|
url.addQueryParameter("title", query.ifBlank { "" })
|
||||||
|
|
||||||
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||||
when (filter) {
|
when (filter) {
|
||||||
is CategoryNames -> {
|
is CategoryNames -> {
|
||||||
@ -329,19 +334,14 @@ class DoujinDesu : ParsedHttpSource(), ConfigurableSource {
|
|||||||
val order = filter.values[filter.state]
|
val order = filter.values[filter.state]
|
||||||
url.addQueryParameter("order", order.key)
|
url.addQueryParameter("order", order.key)
|
||||||
}
|
}
|
||||||
is AuthorFilter -> {
|
|
||||||
url.addQueryParameter("author", filter.state)
|
|
||||||
}
|
|
||||||
is CharacterFilter -> {
|
is CharacterFilter -> {
|
||||||
url.addQueryParameter("character", filter.state)
|
url.addQueryParameter("character", filter.state)
|
||||||
}
|
}
|
||||||
is GenreList -> {
|
is GenreList -> {
|
||||||
filter.state
|
filter.state
|
||||||
.filter { it.state }
|
.filter { it.state }
|
||||||
.let { list ->
|
.forEach { genre ->
|
||||||
if (list.isNotEmpty()) {
|
url.addQueryParameter("genre[]", genre.id)
|
||||||
list.forEach { genre -> url.addQueryParameter("genre[]", genre.id) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is StatusList -> {
|
is StatusList -> {
|
||||||
@ -352,16 +352,76 @@ class DoujinDesu : ParsedHttpSource(), ConfigurableSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val author = filters.firstInstanceOrNull<AuthorFilter>()?.state?.trim()
|
||||||
|
val group = filters.firstInstanceOrNull<GroupFilter>()?.state?.trim()
|
||||||
|
val series = filters.firstInstanceOrNull<SeriesFilter>()?.state?.trim()
|
||||||
|
|
||||||
|
// Author filter handling
|
||||||
|
if (query.isBlank()) {
|
||||||
|
if (!author.isNullOrBlank()) {
|
||||||
|
val slug = author.toMultiSlug()
|
||||||
|
if (slug.isNotBlank()) {
|
||||||
|
val authorUrl = if (page == 1) {
|
||||||
|
"$baseUrl/author/$slug/"
|
||||||
|
} else {
|
||||||
|
"$baseUrl/author/$slug/page/$page/"
|
||||||
|
}
|
||||||
|
return GET(authorUrl, headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group filter handling
|
||||||
|
if (!group.isNullOrBlank()) {
|
||||||
|
val slug = group.toMultiSlug()
|
||||||
|
if (slug.isNotBlank()) {
|
||||||
|
val groupUrl = if (page == 1) {
|
||||||
|
"$baseUrl/group/$slug/"
|
||||||
|
} else {
|
||||||
|
"$baseUrl/group/$slug/page/$page/"
|
||||||
|
}
|
||||||
|
return GET(groupUrl, headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Series filter handling
|
||||||
|
if (!series.isNullOrBlank()) {
|
||||||
|
val slug = series.toMultiSlug()
|
||||||
|
if (slug.isNotBlank()) {
|
||||||
|
val seriesUrl = if (page == 1) {
|
||||||
|
"$baseUrl/series/$slug/"
|
||||||
|
} else {
|
||||||
|
"$baseUrl/series/$slug/page/$page/"
|
||||||
|
}
|
||||||
|
return GET(seriesUrl, headers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return GET(url.build(), headers)
|
return GET(url.build(), headers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val nonAlphaNumSpaceDashRegex = Regex("[^a-z0-9\\s-]")
|
||||||
|
private val multiSpaceRegex = Regex("\\s+")
|
||||||
|
|
||||||
|
private fun String.toMultiSlug(): String {
|
||||||
|
return this
|
||||||
|
.trim()
|
||||||
|
.lowercase()
|
||||||
|
.replace(nonAlphaNumSpaceDashRegex, "")
|
||||||
|
.replace(multiSpaceRegex, "-")
|
||||||
|
}
|
||||||
|
|
||||||
override fun searchMangaFromElement(element: Element): SManga =
|
override fun searchMangaFromElement(element: Element): SManga =
|
||||||
basicInformationFromElement(element)
|
basicInformationFromElement(element)
|
||||||
|
|
||||||
override fun getFilterList() = FilterList(
|
override fun getFilterList() = FilterList(
|
||||||
Filter.Header("NB: Filter bisa digabungkan dengan memakai pencarian teks!"),
|
Filter.Header("NB: Filter bisa digabungkan dengan memakai pencarian teks selain Author, Group dan Series!"),
|
||||||
Filter.Separator(),
|
Filter.Separator(),
|
||||||
|
Filter.Header("NB: Gunakan ini untuk filter per Author, Group dan Series saja, tidak bisa digabungkan dengan memakai pencarian teks dan filter lainnya!"),
|
||||||
AuthorFilter(),
|
AuthorFilter(),
|
||||||
|
GroupFilter(),
|
||||||
|
SeriesFilter(),
|
||||||
|
Filter.Separator(),
|
||||||
|
Filter.Header("NB: Untuk Character Filter akan mengambil hasil apapun jika diinput, misal 'alice', maka hasil akan memunculkan semua Karakter yang memiliki nama 'Alice', bisa digabungkan dengan filter lainnya"),
|
||||||
CharacterFilter(),
|
CharacterFilter(),
|
||||||
StatusList(statusList),
|
StatusList(statusList),
|
||||||
CategoryNames(categoryNames),
|
CategoryNames(categoryNames),
|
||||||
@ -371,6 +431,9 @@ class DoujinDesu : ParsedHttpSource(), ConfigurableSource {
|
|||||||
|
|
||||||
// Detail Parse
|
// Detail Parse
|
||||||
|
|
||||||
|
private val chapterListRegex = Regex("""\d+[-–]?\d*\..+<br>""", RegexOption.IGNORE_CASE)
|
||||||
|
private val htmlTagRegex = Regex("<[^>]*>")
|
||||||
|
|
||||||
override fun mangaDetailsParse(document: Document): SManga {
|
override fun mangaDetailsParse(document: Document): SManga {
|
||||||
val infoElement = document.selectFirst("section.metadata")!!
|
val infoElement = document.selectFirst("section.metadata")!!
|
||||||
val authorName = if (infoElement.select("td:contains(Author) ~ td").isEmpty()) {
|
val authorName = if (infoElement.select("td:contains(Author) ~ td").isEmpty()) {
|
||||||
@ -414,13 +477,96 @@ class DoujinDesu : ParsedHttpSource(), ConfigurableSource {
|
|||||||
Seri : $seriesParser
|
Seri : $seriesParser
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
} else {
|
} else {
|
||||||
val showDescription = infoElement.selectFirst("div.pb-2 > p:nth-child(1)")!!.text()
|
val pb2Element = infoElement.selectFirst("div.pb-2")
|
||||||
"""
|
|
||||||
$showDescription
|
|
||||||
|
|
||||||
Judul Alternatif : $alternativeTitle
|
val showDescription = pb2Element?.let { element ->
|
||||||
Seri : $seriesParser
|
val paragraphs = element.select("p")
|
||||||
""".trimIndent()
|
val firstText = paragraphs.firstOrNull()?.text()?.trim()?.lowercase()
|
||||||
|
|
||||||
|
// CASE 1: Gabungan chapter dalam satu paragraf
|
||||||
|
val mergedChapterElement = element.select("p:has(strong:matchesOwn(^\\s*Sinopsis\\s*:))").firstOrNull {
|
||||||
|
chapterListRegex.containsMatchIn(it.html())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mergedChapterElement != null) {
|
||||||
|
val chapterList = mergedChapterElement.html()
|
||||||
|
.split("<br>")
|
||||||
|
.drop(1)
|
||||||
|
.map { it.replace(htmlTagRegex, "").trim() }
|
||||||
|
.filter { it.isNotEmpty() }
|
||||||
|
|
||||||
|
return@let "Daftar Chapter:\n" + chapterList.joinToString(" | ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CASE 2: Dua paragraf: p[0] = "Sinopsis:", p[1] = daftar chapter
|
||||||
|
if (
|
||||||
|
firstText == "sinopsis:" &&
|
||||||
|
paragraphs.size > 1 &&
|
||||||
|
chapterListRegex.containsMatchIn(paragraphs[1].html())
|
||||||
|
) {
|
||||||
|
val chapterList = paragraphs[1].html()
|
||||||
|
.split("<br>")
|
||||||
|
.map { it.replace(htmlTagRegex, "").trim() }
|
||||||
|
.filter { it.isNotEmpty() }
|
||||||
|
|
||||||
|
return@let "Daftar Chapter:\n" + chapterList.joinToString(" | ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CASE 3: Sinopsis biasa pakai <strong>Sinopsis:</strong> di p awal
|
||||||
|
val sinopsisPara = element.select("p:has(strong:matchesOwn(^\\s*Sinopsis\\s*:))")
|
||||||
|
if (sinopsisPara.isNotEmpty()) {
|
||||||
|
val sinopsisStart = sinopsisPara.first()!!
|
||||||
|
val htmlSplit = sinopsisStart.html().split("<br>")
|
||||||
|
|
||||||
|
val startText = htmlSplit.getOrNull(1)?.replace(htmlTagRegex, "")?.trim().orEmpty()
|
||||||
|
|
||||||
|
val sinopsisTexts = buildList {
|
||||||
|
if (startText.isNotEmpty()) add(startText)
|
||||||
|
|
||||||
|
val allP = element.select("p")
|
||||||
|
val startIndex = allP.indexOf(sinopsisStart)
|
||||||
|
|
||||||
|
for (i in startIndex + 1 until allP.size) {
|
||||||
|
val content = allP[i].text().trim()
|
||||||
|
if (!content.lowercase().startsWith("download")) {
|
||||||
|
add(content)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return@let "Sinopsis:\n" + sinopsisTexts.joinToString("\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CASE 4: Satu paragraf saja dengan <strong> dan <br>
|
||||||
|
if (
|
||||||
|
paragraphs.size == 1 &&
|
||||||
|
element.select("p:has(strong:matchesOwn(^\\s*Sinopsis\\s*:))").isNotEmpty()
|
||||||
|
) {
|
||||||
|
val para = paragraphs[0]
|
||||||
|
val htmlSplit = para.html().split("<br>")
|
||||||
|
|
||||||
|
val content = htmlSplit.getOrNull(1)?.replace(htmlTagRegex, "")?.trim().orEmpty()
|
||||||
|
|
||||||
|
return@let "Sinopsis:\n$content"
|
||||||
|
}
|
||||||
|
|
||||||
|
// CASE 5: Fallback
|
||||||
|
if (firstText == "sinopsis:") {
|
||||||
|
val sinopsisLines = paragraphs.drop(1)
|
||||||
|
.map { it.text().trim() }
|
||||||
|
.filter { !it.lowercase().startsWith("download") }
|
||||||
|
|
||||||
|
return@let "Sinopsis:\n" + sinopsisLines.joinToString("\n\n")
|
||||||
|
}
|
||||||
|
return@let ""
|
||||||
|
} ?: ""
|
||||||
|
"""
|
||||||
|
|$showDescription
|
||||||
|
|
|
||||||
|
|Judul Alternatif : $alternativeTitle
|
||||||
|
|Seri : $seriesParser
|
||||||
|
""".trimMargin().replace(Regex(" +"), " ")
|
||||||
}
|
}
|
||||||
val genres = mutableListOf<String>()
|
val genres = mutableListOf<String>()
|
||||||
infoElement.select("div.tags > a").forEach { element ->
|
infoElement.select("div.tags > a").forEach { element ->
|
||||||
|
Loading…
x
Reference in New Issue
Block a user