Doujindesu: Change website appearance (#13605)

* Update build.gradle

Updated extVersionCode

* Update DoujinDesu.kt

* Remove duplicate genre
* Updated and added latest genre available (on Doujindesu website)
* Updated convenience filter
* Waiting for the Doujindesu domain (not the naked IP's)

* Update ParseStatus

* Update Many Feature

- Better Filter
- Add Some Missing Genre's
- Change Genre From `Array` to `List` for Multiple Genre Search
- Update Parse Status

* Update Many Feature 2.0

- Better genre
- Better filter
- Full genre
- Fix ParseStatus

* Blank again

hhmm......
it actualy can be used in automatic refreshing library download, and now they blank again
still confused in basicInformationFromElement 

updates :
fix missing genres

* Description finally showed

but fortunately the status, author, and genres still can't appear

* Apply suggestion and fix mangaDetails

Fixed :
- Stats
- Author
- Artist
- Genres

* Apply suggestion and re-run

* Fix typo
This commit is contained in:
TheKingTermux 2022-10-10 00:12:34 +07:00 committed by GitHub
parent 5d09f08d37
commit 5a820925c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 209 additions and 137 deletions

View File

@ -5,7 +5,7 @@ ext {
extName = 'DoujinDesu' extName = 'DoujinDesu'
pkgNameSuffix = 'id.doujindesu' pkgNameSuffix = 'id.doujindesu'
extClass = '.DoujinDesu' extClass = '.DoujinDesu'
extVersionCode = 2 extVersionCode = 3
isNsfw = true isNsfw = true
} }

View File

@ -30,8 +30,8 @@ class DoujinDesu : ParsedHttpSource() {
} }
private fun parseStatus(status: String) = when { private fun parseStatus(status: String) = when {
status.lowercase(Locale.US).contains("finished") -> SManga.ONGOING status.lowercase(Locale.US).contains("publishing") -> SManga.ONGOING
status.lowercase(Locale.US).contains("publishing") -> SManga.COMPLETED status.lowercase(Locale.US).contains("finished") -> SManga.COMPLETED
else -> SManga.UNKNOWN else -> SManga.UNKNOWN
} }
@ -41,9 +41,9 @@ class DoujinDesu : ParsedHttpSource() {
} }
} }
private class Genre(title: String, val key: String) : Filter.TriState(title) { private class Genre(name: String, val id: String = name) : Filter.CheckBox(name) {
override fun toString(): String { override fun toString(): String {
return name return id
} }
} }
@ -62,7 +62,6 @@ class DoujinDesu : ParsedHttpSource() {
private val orderBy = arrayOf( private val orderBy = arrayOf(
Order("All", ""), Order("All", ""),
Order("A-Z", "title"), Order("A-Z", "title"),
Order("Z-A", "titlereverse"),
Order("Latest Update", "update"), Order("Latest Update", "update"),
Order("Latest Added", "latest"), Order("Latest Added", "latest"),
Order("Popular", "popular") Order("Popular", "popular")
@ -74,132 +73,194 @@ class DoujinDesu : ParsedHttpSource() {
Status("Finished", "Finished") Status("Finished", "Finished")
) )
private val genreList = arrayOf(
Genre("All", ""),
Genre("Age Regression", "age-regression"),
Genre("Ahegao", "ahegao"),
Genre("All The Way Through", "all-the-way-through"),
Genre("Amputee", "amputee"),
Genre("Anal", "anal"),
Genre("Anorexia", "anorexia"),
Genre("Apron", "apron"),
Genre("Artist CG", "artist-cg"),
Genre("Aunt", "aunt"),
Genre("Bald", "bald"),
Genre("Bestiality", "bestiality"),
Genre("Big As", "big-as"),
Genre("Big Ass", "big-ass"),
Genre("Big Breast", "big-breast"),
Genre("Big Penis", "big-penis"),
Genre("Bike Shorts", "bike-shorts"),
Genre("Bikini", "bikini"),
Genre("Birth", "birth"),
Genre("Bisexual", "bisexual"),
Genre("Blackmail", "blackmail"),
Genre("Blindfold", "blindfold"),
Genre("Bloomers", "bloomers"),
Genre("Blowjob", "blowjob"),
Genre("Body Swap", "body-swap"),
Genre("Bodysuit", "bodysuit"),
Genre("Bondage", "bondage"),
Genre("Business Suit", "business-suit"),
Genre("Cheating", "cheating"),
Genre("Collar", "collar"),
Genre("Condom", "condom"),
Genre("Cousin", "cousin"),
Genre("Crossdressing", "crossdressing"),
Genre("Cunnilingus", "cunnilingus"),
Genre("Dark Skin", "dark-skin"),
Genre("Daughter", "daughter"),
Genre("Defloartion", "defloartion"),
Genre("Defloration", "defloration"),
Genre("Demon", "demon"),
Genre("Demon Girl", "demon-girl"),
Genre("Dick Growth", "dick-growth"),
Genre("DILF", "dilf"),
Genre("Double Penetration", "double-penetration"),
Genre("Drugs", "drugs"),
Genre("Drunk", "drunk"),
Genre("Elf", "elf"),
Genre("Emotionless Sex", "emotionless-sex"),
Genre("Exhibitionism", "exhibitionism"),
Genre("Eyepatch", "eyepatch"),
Genre("Fantasy", "fantasy"),
Genre("Females Only", "females-only"),
Genre("Femdom", "femdom"),
Genre("Filming", "filming"),
Genre("Fingering", "fingering"),
Genre("Footjob", "footjob"),
Genre("Full Color", "full-color"),
Genre("Furry", "furry"),
Genre("Futanari", "futanari"),
Genre("Garter Belt", "garter-belt"),
Genre("Gender Bender", "gender-bender"),
Genre("Ghost", "ghost"),
Genre("Glasses", "glasses"),
Genre("Gore", "gore"),
Genre("Group", "group"),
Genre("Guro", "guro"),
Genre("Gyaru", "gyaru"),
Genre("Hairy", "hairy"),
Genre("Handjob", "handjob"),
Genre("Harem", "harem"),
Genre("Horns", "horns"),
Genre("Huge Breast", "huge-breast"),
Genre("Humiliation", "humiliation"),
Genre("Impregnation", "impregnation"),
Genre("Incest", "incest"),
Genre("Inflation", "inflation"),
Genre("Insect", "insect"),
Genre("Inseki", "inseki"),
Genre("Inverted Nipples", "inverted-nipples"),
Genre("Invisible", "invisible"),
Genre("Kemomimi", "kemomimi"),
Genre("Kimono", "kimono"),
Genre("Lactation", "lactation"),
Genre("Leotard", "leotard"),
Genre("Lingerie", "lingerie"),
Genre("Loli", "loli"),
Genre("Lolipai", "lolipai"),
Genre("Maid", "maid"),
Genre("Males Only", "males-only"),
Genre("Masturbation", "masturbation"),
Genre("Miko", "miko"),
Genre("MILF", "milf"),
Genre("Mind Break", "mind-break"),
Genre("Mind Control", "mind-control"),
Genre("Minigirl", "minigirl"),
Genre("Miniguy", "miniguy"),
Genre("Monster", "monster"),
Genre("Monster Girl", "monster-girl"),
Genre("Mother", "mother"),
Genre("Multi-work Series", "multi-work-series"),
Genre("Muscle", "muscle")
)
private val categoryNames = arrayOf( private val categoryNames = arrayOf(
Category("All", ""), Category("All", ""),
Category("Manga", "Manga"),
Category("Manhua", "Manhua"),
Category("Doujinshi", "Doujinshi"), Category("Doujinshi", "Doujinshi"),
Category("Manga", "Manga"),
Category("Manhwa", "Manhwa") Category("Manhwa", "Manhwa")
) )
private fun genreList() = listOf(
Genre("Age Progression"),
Genre("Age Regression"),
Genre("Ahegao"),
Genre("All The Way Through"),
Genre("Amputee"),
Genre("Anal"),
Genre("Anorexia"),
Genre("Apron"),
Genre("Artist CG"),
Genre("Aunt"),
Genre("Bald"),
Genre("Bestiality"),
Genre("Big Ass"),
Genre("Big Breast"),
Genre("Big Penis"),
Genre("Bike Shorts"),
Genre("Bikini"),
Genre("Birth"),
Genre("Bisexual"),
Genre("Blackmail"),
Genre("Blindfold"),
Genre("Bloomers"),
Genre("Blowjob"),
Genre("Body Swap"),
Genre("Bodysuit"),
Genre("Bondage"),
Genre("Bowjob"),
Genre("Business Suit"),
Genre("Cheating"),
Genre("Collar"),
Genre("Collor"),
Genre("Condom"),
Genre("Cousin"),
Genre("Crossdressing"),
Genre("Cunnilingus"),
Genre("Dark Skin"),
Genre("Daughter"),
Genre("Defloartion"),
Genre("Demon"),
Genre("Demon Girl"),
Genre("Dick Growth"),
Genre("DILF"),
Genre("Double Penetration"),
Genre("Drugs"),
Genre("Drunk"),
Genre("Elf"),
Genre("Emotionless Sex"),
Genre("Exhibitionism"),
Genre("Eyepatch"),
Genre("Females Only"),
Genre("Femdom"),
Genre("Filming"),
Genre("Fingering"),
Genre("Footjob"),
Genre("Full Color"),
Genre("Furry"),
Genre("Futanari"),
Genre("Garter Belt"),
Genre("Gender Bender"),
Genre("Ghost"),
Genre("Glasses"),
Genre("Gore"),
Genre("Group"),
Genre("Guro"),
Genre("Gyaru"),
Genre("Hairy"),
Genre("Handjob"),
Genre("Harem"),
Genre("Horns"),
Genre("Huge Breast"),
Genre("Huge Penis"),
Genre("Humiliation"),
Genre("Impregnation"),
Genre("Incest"),
Genre("Inflation"),
Genre("Insect"),
Genre("Inseki"),
Genre("Inverted Nipples"),
Genre("Invisible"),
Genre("Kemomimi"),
Genre("Kimono"),
Genre("Lactation"),
Genre("Leotard"),
Genre("Lingerie"),
Genre("Loli"),
Genre("Lolipai"),
Genre("Maid"),
Genre("Males"),
Genre("Males Only"),
Genre("Masturbation"),
Genre("Miko"),
Genre("MILF"),
Genre("Mind Break"),
Genre("Mind Control"),
Genre("Minigirl"),
Genre("Miniguy"),
Genre("Monster"),
Genre("Monster Girl"),
Genre("Mother"),
Genre("Multi-work Series"),
Genre("Muscle"),
Genre("Nakadashi"),
Genre("Necrophilia"),
Genre("Netorare"),
Genre("Niece"),
Genre("Nipple Fuck"),
Genre("Nurse"),
Genre("Old Man"),
Genre("Only"),
Genre("Oyakodon"),
Genre("Paizuri"),
Genre("Pantyhose"),
Genre("Possession"),
Genre("Pregnant"),
Genre("Prostitution"),
Genre("Rape"),
Genre("Rimjob"),
Genre("Scat"),
Genre("School Uniform"),
Genre("Sex Toys"),
Genre("Shemale"),
Genre("Shota"),
Genre("Sister"),
Genre("Sleeping"),
Genre("Slime"),
Genre("Small Breast"),
Genre("Snuff"),
Genre("Sole Female"),
Genre("Sole Male"),
Genre("Stocking"),
Genre("Story Arc"),
Genre("Sumata"),
Genre("Sweating"),
Genre("Swimsuit"),
Genre("Tanlines"),
Genre("Teacher"),
Genre("Tentacles"),
Genre("Tomboy"),
Genre("Tomgirl"),
Genre("Torture"),
Genre("Twins"),
Genre("Twintails"),
Genre("Uncensored"),
Genre("Unusual Pupils"),
Genre("Virginity"),
Genre("Webtoon"),
Genre("Widow"),
Genre("X-Ray"),
Genre("Yandere"),
Genre("Yaoi"),
Genre("Yuri")
)
private class CategoryNames(categories: Array<Category>) : Filter.Select<Category>("Category", categories, 0) private class CategoryNames(categories: Array<Category>) : Filter.Select<Category>("Category", categories, 0)
private class OrderBy(orders: Array<Order>) : Filter.Select<Order>("Order", orders, 0) private class OrderBy(orders: Array<Order>) : Filter.Select<Order>("Order", orders, 0)
private class GenreList(genres: Array<Genre>) : Filter.Select<Genre>("Genre", genres, 0) private class GenreList(genres: List<Genre>) : Filter.Group<Genre>("Genre", genres)
private class StatusList(statuses: Array<Status>) : Filter.Select<Status>("Status", statuses, 0) private class StatusList(statuses: Array<Status>) : Filter.Select<Status>("Status", statuses, 0)
private fun basicInformationFromElement(element: Element): SManga { private fun basicInformationFromElement(element: Element): SManga {
val manga = SManga.create() val manga = SManga.create()
element.select("a").let {
manga.title = element.select("div > div > a").attr("alt") manga.title = it.attr("title")
manga.setUrlWithoutDomain(element.select("div > div > a").attr("href")) manga.setUrlWithoutDomain(it.attr("href"))
manga.thumbnail_url = element.select("div > div > a > div > img").attr("src") }
element.select("a > figure.thumbnail > img").first()?.let {
manga.thumbnail_url = imageFromElement(it)
}
return manga return manga
} }
protected open fun imageFromElement(element: Element): String? {
return when {
element.hasAttr("data-src") -> element.attr("abs:data-src")
element.hasAttr("data-lazy-src") -> element.attr("abs:data-lazy-src")
element.hasAttr("srcset") -> element.attr("abs:srcset").substringBefore(" ")
else -> element.attr("abs:src")
}
}
private fun getNumberFromString(epsStr: String): String { private fun getNumberFromString(epsStr: String): String {
return epsStr.filter { it.isDigit() } return epsStr.filter { it.isDigit() }
} }
@ -218,7 +279,7 @@ class DoujinDesu : ParsedHttpSource() {
override fun popularMangaFromElement(element: Element): SManga = basicInformationFromElement(element) override fun popularMangaFromElement(element: Element): SManga = basicInformationFromElement(element)
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/komik-list/page/$page/?&order=popular") return GET("$baseUrl/manga/page/$page/?&order=popular", headers)
} }
// Latest // Latest
@ -226,40 +287,43 @@ class DoujinDesu : ParsedHttpSource() {
override fun latestUpdatesFromElement(element: Element): SManga = basicInformationFromElement(element) override fun latestUpdatesFromElement(element: Element): SManga = basicInformationFromElement(element)
override fun latestUpdatesRequest(page: Int): Request { override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/komik-list/page/$page/?order=update") return GET("$baseUrl/manga/page/$page/?order=update", headers)
} }
// Element Selectors // Element Selectors
override fun latestUpdatesSelector(): String = "#main > div.relat > article" override fun latestUpdatesSelector(): String = "#archives > div.entries > article"
override fun popularMangaSelector(): String = "#main > div.relat > article" override fun popularMangaSelector(): String = "#archives > div.entries > article"
override fun searchMangaSelector(): String = "#main > div.relat > article" override fun searchMangaSelector(): String = "#archives > div.entries > article"
override fun popularMangaNextPageSelector(): String = "#nextpagination" override fun popularMangaNextPageSelector(): String = "nav.pagination > ul > li.last > a"
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector() override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
// Search & FIlter // Search & FIlter
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
var url = "$baseUrl/komik-list/page/$page/".toHttpUrlOrNull()?.newBuilder()!!.addQueryParameter("title", query) var url = "$baseUrl/manga/page/$page/".toHttpUrlOrNull()?.newBuilder()!!.addQueryParameter("title", query)
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter -> (if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) { when (filter) {
is CategoryNames -> { is CategoryNames -> {
val category = filter.values[filter.state] val category = filter.values[filter.state]
url.addQueryParameter("type", category.key) url.addQueryParameter("typex", category.key)
} }
is OrderBy -> { is OrderBy -> {
val order = filter.values[filter.state] val order = filter.values[filter.state]
url.addQueryParameter("order", order.key) url.addQueryParameter("order", order.key)
} }
is GenreList -> { is GenreList -> {
val genre = filter.values[filter.state] filter.state
url.addQueryParameter("genre", genre.key) .filter { it.state }
.let { list ->
if (list.isNotEmpty()) { list.forEach { genre -> url.addQueryParameter("genre[]", genre.id) } }
}
} }
is StatusList -> { is StatusList -> {
val status = filter.values[filter.state] val status = filter.values[filter.state]
url.addQueryParameter("status", status.key) url.addQueryParameter("statusx", status.key)
} }
} }
} }
@ -269,25 +333,33 @@ class DoujinDesu : ParsedHttpSource() {
override fun searchMangaFromElement(element: Element): SManga = basicInformationFromElement(element) override fun searchMangaFromElement(element: Element): SManga = basicInformationFromElement(element)
override fun getFilterList() = FilterList( override fun getFilterList() = FilterList(
Filter.Header("NB: Filter diabaikan jika memakai pencarian teks!"),
Filter.Separator(),
StatusList(statusList),
CategoryNames(categoryNames), CategoryNames(categoryNames),
OrderBy(orderBy), OrderBy(orderBy),
GenreList(genreList), GenreList(genreList())
StatusList(statusList)
) )
// Detail Parse // Detail Parse
override fun mangaDetailsParse(document: Document): SManga { override fun mangaDetailsParse(document: Document): SManga {
val infoElement = document.select("section.metadata").first()
val manga = SManga.create() val manga = SManga.create()
manga.description = when { manga.description = when {
document.select("div.infox > div.entry-content.entry-content-single > p").isEmpty() -> "No description specified" document.select("section.metadata > div.pb-2 > p:nth-child(1)").isEmpty() -> "Tidak ada deskripsi yang tersedia bosque"
else -> document.select("div.infox > div.entry-content.entry-content-single > p").first().text() else -> document.select("section.metadata > div.pb-2 > p:nth-child(1)").first().text()
} }
manga.author = document.select("div.infox > div.spe > span:nth-child(5)").text() val genres = mutableListOf<String>()
manga.genre = document.select("div.genre-info > a[itemprop=genre]").joinToString { it.text() } infoElement.select("div.tags > a").forEach { element ->
manga.status = parseStatus(document.select("div.infox > div.spe > span:nth-child(1)").text()) val genre = element.text()
manga.thumbnail_url = document.select("div.thumb > img").attr("src") genres.add(genre)
manga.artist = document.select("div.infox > div.spe > span:nth-child(6)").text() }
manga.author = document.select("section.metadata > table:nth-child(2) > tbody > tr.pages > td:contains(Author) + td:nth-child(2) > a").joinToString { it.text() }
manga.genre = infoElement.select("div.tags > a").joinToString { it.text() }
manga.status = parseStatus(document.select("section.metadata > table:nth-child(2) > tbody > tr:nth-child(1) > td:nth-child(2) > a").first().text())
manga.thumbnail_url = document.select("figure.thumbnail > img").attr("src")
manga.artist = document.select("section.metadata > table:nth-child(2) > tbody > tr.pages > td:contains(Character) + td:nth-child(2) > a").joinToString { it.text() }
return manga return manga
} }
@ -314,7 +386,7 @@ class DoujinDesu : ParsedHttpSource() {
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not Used") override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not Used")
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
return document.select("div.reader-area > img").mapIndexed { i, element -> return document.select("#reader > div.main > div > img").mapIndexed { i, element ->
Page(i, "", element.attr("src")) Page(i, "", element.attr("src"))
} }
} }