Add Ưng Tỷ Comics (#1286)

This commit is contained in:
beerpsi 2024-02-16 18:50:17 +07:00 committed by GitHub
parent 6399255e78
commit fcd38a3ed6
7 changed files with 247 additions and 0 deletions

View File

@ -0,0 +1,8 @@
ext {
extName = "Ung Ty Comics"
extClass = ".UngTyComics"
extVersionCode = 1
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@ -0,0 +1,239 @@
package eu.kanade.tachiyomi.extension.vi.ungtycomics
import android.app.Application
import android.widget.Toast
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList
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 uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.SimpleDateFormat
import java.util.Locale
class UngTyComics : ParsedHttpSource(), ConfigurableSource {
override val name = "Ưng Tỷ Comics"
override val baseUrl by lazy {
when {
System.getenv("CI") == "true" -> MIRRORS.joinToString("#, ")
else -> preferences.getString(PREF_BASE_URL, MIRRORS[0])!!
}
}
override val lang = "vi"
override val supportsLatest = true
private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override val client = network.cloudflareClient.newBuilder()
.rateLimitHost(baseUrl.toHttpUrl(), 2)
.build()
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
override fun popularMangaRequest(page: Int) = GET("$baseUrl/truyen-hot?page=$page", headers)
override fun popularMangaSelector() = "div.item-comics"
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
element.selectFirst(".content-title a")!!.let {
setUrlWithoutDomain(it.attr("href"))
title = it.text()
}
thumbnail_url = element.selectFirst(".content-image img")?.absUrl("data-src")
}
override fun popularMangaNextPageSelector() = "ul.pagination li:has(.fa-angle-double-right):not(.disabled)"
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/truyen-tranh?page=$page", headers)
override fun latestUpdatesSelector() = popularMangaSelector()
override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val genreFilter = filters.ifEmpty { getFilterList() }
.filterIsInstance<GenreFilter>()
.firstOrNull()
val url = baseUrl.toHttpUrl().newBuilder().apply {
if (query.isNotEmpty()) {
addPathSegment("search")
addQueryParameter("query_string", query)
} else if (genreFilter != null) {
addPathSegments(genreFilter.genres[genreFilter.state].path)
} else {
addPathSegment("truyen-tranh")
}
if (page > 1) {
addQueryParameter("page", page.toString())
}
}.build()
return GET(url, headers)
}
override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
val statusText = document.selectFirst(".comics-info .meta_label:contains(Trạng thái) + .meta_info")?.text()
title = document.selectFirst(".title-heading")!!.text()
author = document.selectFirst(".comics-info .meta_label:contains(Tác giả) + .meta_info")?.text()
genre = document.selectFirst(".comics-info .meta_label:contains(Thể loại) + .meta_info")?.text()
status = when (statusText) {
"Đang tiến hành" -> SManga.ONGOING
"Đã đủ bộ" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
thumbnail_url = document.selectFirst(".comics-thumbnail img")?.absUrl("src")
}
override fun chapterListParse(response: Response): List<SChapter> {
var document = response.asJsoup()
var page = 2
return buildList {
document.select(chapterListSelector())
.forEach { add(chapterFromElement(it)) }
while (document.selectFirst(chapterNextPageSelector()) != null) {
val url = response.request.url.newBuilder()
.addQueryParameter("page", page.toString())
.build()
document = client.newCall(GET(url, headers)).execute().asJsoup()
document.select(chapterListSelector())
.forEach { add(chapterFromElement(it)) }
page++
}
}
}
override fun chapterListSelector() = ".list-comics-chapter .item-chapter"
override fun chapterFromElement(element: Element) = SChapter.create().apply {
element.selectFirst(".episode-title a")!!.let {
setUrlWithoutDomain(it.attr("href"))
name = it.text()
}
date_upload = try {
val date = element.selectFirst(".episode-date span")!!.text()
DATE_FORMAT.parse(date)!!.time
} catch (_: Exception) {
0L
}
}
private fun chapterNextPageSelector() = popularMangaNextPageSelector()
override fun pageListParse(document: Document) =
document.select("img.chapter-img").mapIndexed { i, it ->
Page(i, imageUrl = it.absUrl("data-src"))
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
override fun getFilterList() = FilterList(
Filter.Header("Không dùng được khi tìm kiếm bằng chữ"),
GenreFilter(getGenreList()),
)
override fun setupPreferenceScreen(screen: PreferenceScreen) {
ListPreference(screen.context).apply {
key = PREF_BASE_URL
title = "Tên miền"
summary = "%s"
entries = MIRRORS
entryValues = MIRRORS
setDefaultValue(MIRRORS[0])
setOnPreferenceChangeListener { _, _ ->
Toast.makeText(screen.context, "Khởi động lại ứng dụng để áp dụng cài đặt mới.", Toast.LENGTH_LONG)
.show()
true
}
}.also(screen::addPreference)
}
}
private const val PREF_BASE_URL = "baseUrl"
private val MIRRORS = arrayOf("https://ungtycomicsvip.com", "https://topdammy.com")
private val DATE_FORMAT = SimpleDateFormat("dd-MM-yyyy", Locale.ROOT)
private class GenreFilter(val genres: List<Genre>) : Filter.Select<String>(
"Thể loại",
genres.map { it.name }.toTypedArray(),
)
private class Genre(val name: String, val path: String)
// https://ungtycomicsvip.com/truyen-tranh
// copy([...document.querySelectorAll(".item-category a")].map((e) => `Genre("${e.textContent.trim()}", "${new URL(e.href).pathname.replace("/", "")}"),`).join("\n"))
// removed the Truyện Chữ genre since this is not a light novel reader. there's nothing in that genre
// anyways.
private fun getGenreList() = listOf(
Genre("Tất cả", "truyen-tranh"),
Genre("Truyện hot", "truyen-hot"),
Genre("Giới giải trí", "gioi-giai-tri.html"),
Genre("Ngôn Tình", "ngon-tinh.html"),
Genre("Cổ Trang", "co-trang.html"),
Genre("Lãng Mạn", "lang-man.html"),
Genre("Đam Mỹ", "dam-my.html"),
Genre("Boys Love", "boys-love.html"),
Genre("Manhua", "manhua.html"),
Genre("Romance", "romance.html"),
Genre("Ngược", "nguoc.html"),
Genre("Sủng", "sung.html"),
Genre("Cung Đấu", "cung-dau.html"),
Genre("Drama", "drama.html"),
Genre("Trinh Thám", "trinh-tham.html"),
Genre("Học Đường", "hoc-duong.html"),
Genre("Xuyên Không", "xuyen-khong.html"),
Genre("Trọng Sinh", "trong-sinh.html"),
Genre("School Life", "school-life.html"),
Genre("Hiện Đại", "hien-dai.html"),
Genre("Võng Du", "vong-du.html"),
Genre("Báo Thù", "bao-thu.html"),
Genre("Tổng Tài", "tong-tai.html"),
Genre("ABO", "abo.html"),
Genre("Hài Hước", "hai-huoc.html"),
Genre("Niên Hạ", "nien-ha.html"),
Genre("Hiện Thực", "hien-thuc.html"),
Genre("Xuyên Nhanh", "xuyen-nhanh.html"),
Genre("Sư Đồ Luyến", "su-do-luyen.html"),
Genre("Hệ Thống", "he-thong.html"),
Genre("Huyết Tộc", "huyet-toc.html"),
Genre("Hắc Bang", "hac-bang.html"),
Genre("Full Trọn Bộ", "full-tron-bo.html"),
Genre("Phá Án", "pha-an.html"),
Genre("Linh Dị", "linh-di.html"),
Genre("Tu Tiên", "tu-tien.html"),
Genre("eSports", "esports.html"),
)