Add MikuDoujin and Rh2PlusManga extension (#9469)

* Add MikuDoujin
* Add MikuDoujin V.1.2.1

* Update MikuDoujin.kt

* Update MikuDoujin
* Add ability to search using genre

* Add Rh2PlusManga
* Add Rh2PlusManga V.1.12.2
* Use Madara multi-source themes

* Change MikuDoujin gradle and Add Rh2PlusManga generator.
* update file to suggestion.
* add Rh2PlusManga to MadaraGenerator.kt.
This commit is contained in:
Promchai Chooseang 2021-10-15 23:26:57 +07:00 committed by GitHub
parent 6c721039ff
commit d3b64152a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 274 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View File

@ -0,0 +1,61 @@
package eu.kanade.tachiyomi.extension.th.rh2plusmanga
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.source.model.Page
import org.jsoup.nodes.Document
import java.text.SimpleDateFormat
import java.util.Locale
class Rh2PlusManga : Madara("Rh2PlusManga", "https://www.rh2plusmanga.com", "th", SimpleDateFormat("d MMMM d yyyy", Locale("th"))) {
override val useNewChapterEndpoint = true
override fun getGenreList() = listOf(
Genre("เหนือธรรมชาติ", "supernatural"),
Genre("ทำอาหาร", "cooking"),
Genre("สยองขวัญ", "horror"),
Genre("ยูริ", "yuri"),
Genre("จิตวิทยา", "psychological"),
Genre("วัยรุ่น", "seinen"),
Genre("ชีวิตประจำวัน", "slice-of-life"),
Genre("เค-เว็บตูน", "เค-เว็บตูน"),
Genre("ต่างโลก", "ต่างโลก"),
Genre("แฟนตาซี", "fantasy"),
Genre("ไซ-ไฟ", "sci-fi"),
Genre("คอมเมดี้", "comedy"),
Genre("โรแมนติก", "romance"),
Genre("สำหรับผู้ใหญ่", "adult"),
Genre("ยาโอย", "yaoi"),
Genre("ศิลปะการต่อสู้", "martial-arts"),
Genre("โชเน็น", "shounen"),
Genre("ดราม่า", "drama"),
Genre("เกิดใหม่", "เกิดใหม่"),
Genre("ปริศนา", "mystery"),
Genre("ประวัติศาสตร์", "historical"),
Genre("มันฮวา", "มันฮวา"),
Genre("ผจญภัย", "adventure"),
Genre("กีฬา", "sports"),
Genre("มังงะ", "manga"),
Genre("One shot", "one-shot"),
Genre("โชโจ", "shoujo"),
Genre("หุ่นยนต์", "mecha"),
Genre("แอคชั่น", "action"),
Genre("ชีวิตในโรงเรียน", "school-life"),
Genre("ฮาเร็ม", "harem"),
Genre("ลามก", "ecchi")
)
override val pageListParseSelector = "div.reading-content p code img"
override fun pageListParse(document: Document): List<Page> {
countViews(document)
return document.select(pageListParseSelector).mapIndexed { index, element ->
Page(
index,
document.location(),
element.let {
it.absUrl(if (it.hasAttr("data-src")) "data-src" else "src")
}
)
}
}
}

View File

@ -334,6 +334,7 @@ class MadaraGenerator : ThemeSourceGenerator {
SingleLang("Red Ribbon Scanlator", "https://redribbon.site", "pt-BR", overrideVersionCode = 1),
SingleLang("Renascence Scans (Renascans)", "https://new.renascans.com", "en", className = "RenaScans", overrideVersionCode = 1),
SingleLang("Reset Scans", "https://reset-scans.com", "en", overrideVersionCode = 4),
SingleLang("Rh2PlusManga", "https://www.rh2plusmanga.com", "th", overrideVersionCode = 1),
SingleLang("Rüya Manga", "https://www.ruyamanga.com", "tr", className = "RuyaManga", overrideVersionCode = 1),
SingleLang("S2Manga", "https://s2manga.com", "en", overrideVersionCode = 1),
SingleLang("SISI GELAP", "https://sigel.xyz", "id", overrideVersionCode = 3),

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.extension" />

View File

@ -0,0 +1,12 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
extName = 'MikuDoujin'
pkgNameSuffix = 'th.mikudoujin'
extClass = '.MikuDoujin'
extVersionCode = 1
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -0,0 +1,198 @@
package eu.kanade.tachiyomi.extension.th.mikudoujin
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import java.net.URLEncoder
import java.util.concurrent.TimeUnit
class MikuDoujin : ParsedHttpSource() {
override val baseUrl: String = "https://miku-doujin.com"
override val lang: String = "th"
override val name: String = "MikuDoujin"
override val supportsLatest: Boolean = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.connectTimeout(1, TimeUnit.MINUTES)
.readTimeout(1, TimeUnit.MINUTES)
.writeTimeout(1, TimeUnit.MINUTES)
.build()
// Popular
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/?page=$page", headers)
}
override fun popularMangaSelector() = "div.col-6.inz-col"
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
element.select("a").let {
manga.setUrlWithoutDomain(it.attr("href"))
manga.title = it.select("div.inz-title").text()
manga.thumbnail_url = it.select("img").attr("abs:src")
manga.initialized = false
}
return manga
}
override fun popularMangaNextPageSelector() = "button.btn-secondary"
// Latest
override fun latestUpdatesRequest(page: Int): Request = popularMangaRequest(page)
override fun latestUpdatesSelector() = popularMangaSelector()
override fun latestUpdatesFromElement(element: Element): SManga =
popularMangaFromElement(element)
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
// Search
private fun genre(name: String): String {
return if (name != "สาวใหญ่/แม่บ้าน") {
URLEncoder.encode(name, "UTF-8")
} else {
name
}
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
throw Exception("Unused")
override fun searchMangaSelector(): String = throw Exception("Unused")
override fun searchMangaFromElement(element: Element): SManga = throw Exception("Unused")
override fun searchMangaNextPageSelector(): String = throw Exception("Unused")
override fun fetchSearchManga(
page: Int,
query: String,
filters: FilterList
): Observable<MangasPage> {
val searchMethod = query.startsWith("http")
return client.newCall(
GET(
if (searchMethod) {
query
} else {
"$baseUrl/genre/${genre(query)}/?page=$page"
}
)
)
.asObservableSuccess()
.map {
val document = it.asJsoup()
val mangas: List<SManga> = if (searchMethod) {
listOf(
SManga.create().apply {
url = query.substringAfter(baseUrl)
title = document.title()
thumbnail_url =
document.select("div.sr-card-body div.col-md-4 img").attr("abs:src")
initialized = false
}
)
} else {
document.select(popularMangaSelector()).map { element ->
popularMangaFromElement(element)
}
}
MangasPage(mangas, !searchMethod)
}
}
// Manga summary page
override fun mangaDetailsParse(document: Document): SManga {
val infoElement = document.select("div.sr-card-body").first()
return SManga.create().apply {
title = document.title()
author = infoElement.select("div.col-md-8 p a.badge-secondary")[2].ownText()
artist = author
status = SManga.UNKNOWN
genre = infoElement.select("div.sr-card-body div.col-md-8 div.tags a")
.joinToString { it.text() }
description = infoElement.select("div.col-md-8").first().ownText()
thumbnail_url = infoElement.select("div.col-md-4 img").first().attr("abs:src")
initialized = true
}
}
// Chapters
override fun chapterListSelector() = "table.table-episode tr"
override fun chapterFromElement(element: Element): SChapter {
val chapter = SChapter.create()
element.select("td a").let {
chapter.setUrlWithoutDomain(it.attr("href"))
chapter.name = it.text()
if (chapter.name.isEmpty()) {
chapter.chapter_number = 0.0f
} else {
chapter.chapter_number = chapter.name.split(" ").last().toFloat()
}
}
return chapter
}
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return client.newCall(GET("$baseUrl/${manga.url}"))
.asObservableSuccess()
.map {
val chList: List<SChapter>
val mangaDocument = it.asJsoup()
if (mangaDocument.select(chapterListSelector()).isEmpty()) {
val createdChapter = SChapter.create().apply {
url = manga.url
name = "Chapter 1"
chapter_number = 1.0f
}
chList = listOf(createdChapter)
} else {
chList = mangaDocument.select(chapterListSelector()).map { Chapter ->
chapterFromElement(Chapter)
}
}
chList
}
}
// Pages
override fun pageListParse(document: Document): List<Page> {
val pageList = document.select("img.lazy")
return pageList.subList(0, (pageList.size / 2) - 1).mapIndexed { i, img ->
Page(i, "", img.attr("abs:data-src"))
}
}
override fun imageUrlParse(document: Document): String =
throw UnsupportedOperationException("Not used")
override fun getFilterList() = FilterList()
}