add Manga18 multisrc (#1135)

* add Manga18 multisrc

18 Porn Comic
Comic1000
HANMAN18
Hentai3z.CC
Manga18.Club
TuManhwas.Club

* tag filters

* empty alt name

* lint
This commit is contained in:
AwkwardPeak7 2024-02-08 21:10:57 +05:00 committed by Draff
parent d8f4f38676
commit 879eb629b1
15 changed files with 290 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -0,0 +1,8 @@
package eu.kanade.tachiyomi.extension.zh.hanman18
import eu.kanade.tachiyomi.multisrc.manga18.Manga18
class HANMAN18 : Manga18("HANMAN18", "https://hanman18.com", "zh") {
// tag filter doesn't work on site
override val getAvailableTags = false
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1,16 @@
package eu.kanade.tachiyomi.extension.en.hentai3zcc
import eu.kanade.tachiyomi.multisrc.manga18.Manga18
import eu.kanade.tachiyomi.source.model.SManga
import org.jsoup.nodes.Element
class Hentai3zCC : Manga18("Hentai3z.CC", "https://hentai3z.cc", "en") {
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
title = element.selectFirst("div.mg_info > div.mg_name a")!!.text()
thumbnail_url = element.selectFirst("img")?.absUrl("src")
?.replace("cover_thumb_2.webp", "cover_250x350.jpg")
?.replace("admin.manga18.us", "bk.18porncomic.com")
}
}

View File

@ -0,0 +1,214 @@
package eu.kanade.tachiyomi.multisrc.manga18
import android.util.Base64
import android.util.Log
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Filter
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.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.lang.Exception
import java.lang.UnsupportedOperationException
import java.text.SimpleDateFormat
import java.util.Locale
abstract class Manga18(
override val name: String,
override val baseUrl: String,
override val lang: String,
) : ParsedHttpSource() {
override val supportsLatest = true
override val client = network.cloudflareClient
override fun headersBuilder() = super.headersBuilder()
.set("Referer", "$baseUrl/")
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/list-manga/$page?order_by=views", headers)
}
override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
getTags(document)
val entries = document.select(popularMangaSelector()).map(::popularMangaFromElement)
val hasNextPage = document.selectFirst(popularMangaNextPageSelector()) != null
return MangasPage(entries, hasNextPage)
}
override fun popularMangaSelector() = "div.story_item"
override fun popularMangaNextPageSelector() = ".pagination a[rel=next]"
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
title = element.selectFirst("div.mg_info > div.mg_name a")!!.text()
thumbnail_url = element.selectFirst("img")?.absUrl("src")
}
override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/list-manga/$page", headers)
}
override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
override fun latestUpdatesSelector() = popularMangaSelector()
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = baseUrl.toHttpUrl().newBuilder().apply {
val tag = filters.filterIsInstance<TagFilter>().firstOrNull()
if (query.isNotEmpty() || tag?.selected.isNullOrEmpty()) {
addPathSegment("list-manga")
addPathSegment(page.toString())
addQueryParameter("search", query.trim())
} else {
addPathSegment("manga-list")
addPathSegment(tag!!.selected!!)
addPathSegment(page.toString())
filters.filterIsInstance<SortFilter>().firstOrNull()?.selected?.let {
addQueryParameter("order_by", it)
}
}
}.build()
return GET(url, headers)
}
override fun searchMangaParse(response: Response) = popularMangaParse(response)
override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
protected open val getAvailableTags = true
protected open val tagsSelector = "div.grid_cate li > a"
private var tags = listOf<Pair<String, String>>()
private var getTagsAttempts = 0
protected open fun getTags(document: Document) {
if (getAvailableTags && tags.isEmpty() && getTagsAttempts < 3) {
try {
tags = document.select(tagsSelector).map {
Pair(
it.text().trim(),
it.attr("href")
.removeSuffix("/")
.substringAfterLast("/"),
)
}.let {
listOf(Pair("", "")) + it
}
} catch (e: Exception) {
Log.d(name, e.stackTraceToString())
}
}
}
override fun getFilterList(): FilterList {
if (!getAvailableTags) return FilterList()
return if (tags.isEmpty()) {
FilterList(
Filter.Header("Press 'reset' to attempt to load genres"),
)
} else {
FilterList(
Filter.Header("Ignored with text search"),
Filter.Separator(),
SortFilter(),
TagFilter(tags),
)
}
}
protected open val infoElementSelector = "div.detail_listInfo"
protected open val titleSelector = "div.detail_name > h1"
protected open val descriptionSelector = "div.detail_reviewContent"
protected open val statusSelector = "div.item:contains(Status) div.info_value"
protected open val altNameSelector = "div.item:contains(Other name) div.info_value"
protected open val genreSelector = "div.info_value > a[href*=/manga-list/]"
protected open val authorSelector = "div.info_label:contains(author) + div.info_value, div.info_label:contains(autor) + div.info_value"
protected open val artistSelector = "div.info_label:contains(artist) + div.info_value"
protected open val thumbnailSelector = "div.detail_avatar > img"
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
val info = document.selectFirst(infoElementSelector)!!
title = document.select(titleSelector).text()
description = buildString {
document.select(descriptionSelector)
.eachText().onEach {
append(it.trim())
append("\n\n")
}
info.selectFirst(altNameSelector)
?.text()
?.takeIf { it != "Updating" && it.isNotEmpty() }
?.let {
append("Alternative Names:\n")
append(it.trim())
}
}
status = when (info.select(statusSelector).text()) {
"On Going" -> SManga.ONGOING
"Completed" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
author = info.selectFirst(authorSelector)?.text()?.takeIf { it != "Updating" }
artist = info.selectFirst(artistSelector)?.text()?.takeIf { it != "Updating" }
genre = info.select(genreSelector).eachText().joinToString()
thumbnail_url = document.selectFirst(thumbnailSelector)?.absUrl("src")
}
override fun chapterListSelector() = "div.chapter_box .item"
protected open val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.ENGLISH)
override fun chapterFromElement(element: Element) = SChapter.create().apply {
element.selectFirst("a")!!.run {
setUrlWithoutDomain(absUrl("href"))
name = text()
}
date_upload = try {
dateFormat.parse(element.selectFirst("p")!!.text())!!.time
} catch (_: Exception) {
0L
}
}
override fun pageListParse(document: Document): List<Page> {
val script = document.selectFirst("script:containsData(slides_p_path)")
?: throw Exception("Unable to find script with image data")
val encodedImages = script.data()
.substringAfter('[')
.substringBefore(",]")
.replace("\"", "")
.split(",")
return encodedImages.mapIndexed { idx, encoded ->
val url = Base64.decode(encoded, Base64.DEFAULT).toString(Charsets.UTF_8)
val imageUrl = when {
url.startsWith("/") -> "$baseUrl$url"
else -> url
}
Page(idx, imageUrl = imageUrl)
}
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
}

View File

@ -0,0 +1,23 @@
package eu.kanade.tachiyomi.multisrc.manga18
import eu.kanade.tachiyomi.source.model.Filter
abstract class SelectFilter(
name: String,
private val options: List<Pair<String, String>>,
) : Filter.Select<String>(
name,
options.map { it.first }.toTypedArray(),
) {
val selected get() = options[state].second.takeUnless { it.isEmpty() }
}
class TagFilter(tags: List<Pair<String, String>>) : SelectFilter("Tags", tags)
class SortFilter : SelectFilter("Sort", sortValues)
private val sortValues = listOf(
Pair("Latest", ""),
Pair("Views", "views"),
Pair("A-Z", "name"),
)

View File

@ -0,0 +1,29 @@
package eu.kanade.tachiyomi.multisrc.manga18
import generator.ThemeSourceData.SingleLang
import generator.ThemeSourceGenerator
class Manga18Generator : ThemeSourceGenerator {
override val themePkg = "manga18"
override val themeClass = "Manga18"
override val baseVersionCode = 1
override val sources = listOf(
SingleLang("18 Porn Comic", "https://18porncomic.com", "en", isNsfw = true, className = "EighteenPornComic"),
SingleLang("Comic1000", "https://comic1000.com", "en", isNsfw = true),
SingleLang("HANMAN18", "https://hanman18.com", "zh", isNsfw = true),
SingleLang("Hentai3z.CC", "https://hentai3z.cc", "en", isNsfw = true, className = "Hentai3zCC"),
SingleLang("Manga18.Club", "https://manga18.club", "en", isNsfw = true, className = "Manga18Club"),
SingleLang("TuManhwas.Club", "https://tumanhwas.club", "es", isNsfw = true, className = "TuManhwasClub"),
)
companion object {
@JvmStatic
fun main(args: Array<String>) {
Manga18Generator().createAll()
}
}
}