diff --git a/multisrc/overrides/mymangacms/default/AndroidManifest.xml b/multisrc/overrides/mymangacms/default/AndroidManifest.xml new file mode 100644 index 000000000..8a9512979 --- /dev/null +++ b/multisrc/overrides/mymangacms/default/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/multisrc/overrides/mymangacms/lkdtt/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mymangacms/lkdtt/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..c42b4063a Binary files /dev/null and b/multisrc/overrides/mymangacms/lkdtt/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mymangacms/lkdtt/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mymangacms/lkdtt/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..b2150bb89 Binary files /dev/null and b/multisrc/overrides/mymangacms/lkdtt/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mymangacms/lkdtt/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mymangacms/lkdtt/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..dc243cd2a Binary files /dev/null and b/multisrc/overrides/mymangacms/lkdtt/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mymangacms/lkdtt/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mymangacms/lkdtt/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..580330e4f Binary files /dev/null and b/multisrc/overrides/mymangacms/lkdtt/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mymangacms/lkdtt/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mymangacms/lkdtt/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..1d00d3744 Binary files /dev/null and b/multisrc/overrides/mymangacms/lkdtt/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mymangacms/lkdtt/res/web_hi_res_512.png b/multisrc/overrides/mymangacms/lkdtt/res/web_hi_res_512.png new file mode 100644 index 000000000..57120ab13 Binary files /dev/null and b/multisrc/overrides/mymangacms/lkdtt/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mymangacms/lkdtt/src/LKDTT.kt b/multisrc/overrides/mymangacms/lkdtt/src/LKDTT.kt new file mode 100644 index 000000000..adf7f5b56 --- /dev/null +++ b/multisrc/overrides/mymangacms/lkdtt/src/LKDTT.kt @@ -0,0 +1,121 @@ +package eu.kanade.tachiyomi.extension.vi.lkdtt + +import eu.kanade.tachiyomi.multisrc.mymangacms.MyMangaCMS +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.TimeZone + +class LKDTT : MyMangaCMS("LKDTT", "https://lkdtt.com", "vi") { + override val dateFormatter = SimpleDateFormat("dd/MM/yy", Locale.US).apply { + timeZone = TimeZone.getTimeZone(super.timeZone) + } + + override fun dateUpdatedParser(date: String): Long = + runCatching { super.dateUpdatedParser(date.split(" - ")[1]) }.getOrNull() ?: 0L + + override fun getGenreList() = listOf( + Genre("Học đường", 1), + Genre("Hài hước", 2), + Genre("Cổ Đại", 3), + Genre("Hiện đại", 4), + Genre("Kinh dị", 5), + Genre("Tổng tài", 6), + Genre("Xuyên không", 7), + Genre("Manhua", 8), + Genre("Manhwa", 9), + Genre("Mystery", 10), + Genre("One shot", 11), + Genre("Smut", 12), + Genre("Webtoon", 13), + Genre("Yaoi", 14), + Genre("Yuri", 15), + Genre("Trinh Thám", 16), + Genre("Tình Cảm", 17), + Genre("Drama", 18), + Genre("Comedy", 19), + Genre("Fantasy", 20), + Genre("Novel", 21), + Genre("Action", 22), + Genre("Manga", 23), + Genre("Đam Mỹ", 24), + Genre("Trọng Sinh", 25), + Genre("Ngôn Tình", 26), + Genre("Phiêu Lưu", 27), + Genre("Boy Love", 28), + Genre("giới giải trí", 29), + Genre("đô thị", 30), + Genre("Romance", 31), + Genre("Đô Thị", 32), + Genre("Shoujo", 33), + Genre("Historical", 34), + Genre("Slice of life", 35), + Genre("Mature", 36), + Genre("GL", 37), + Genre("Adult", 38), + Genre("Huyền huyễn", 39), + Genre("Baby", 40), + Genre("Tragedy", 41), + Genre("Truyện Màu", 42), + Genre("School Life", 43), + Genre("Josei", 44), + Genre("Oneshot", 45), + Genre("Gender Bender", 46), + Genre("Nữ cường", 47), + Genre("Harem", 48), + Genre("Reverse Harem", 49), + Genre("Isekai", 50), + Genre("Adventure", 51), + Genre("Chuyển Sinh", 52), + Genre("Đại Nữ Chủ", 53), + Genre("Shounen", 54), + Genre("Sports", 55), + Genre("Sủng Ngọt", 56), + Genre("Truyện 18+", 57), + Genre("Trung Cổ", 58), + Genre("Ma Thuật", 59), + Genre("Webtoons", 60), + Genre("Xuyên", 61), + Genre("Ngôn", 62), + Genre("Tiểu Bạch Thỏ", 63), + Genre("Sủng", 65), + Genre("Trùng Sinh", 66), + Genre("Ma Cà Rồng", 67), + Genre("Tái Sinh", 68), + Genre("Quân Nhân", 69), + Genre("Showbiz", 70), + Genre("Comic", 71), + Genre("Phép Thuật", 72), + Genre("Psychological", 73), + Genre("Supernatural", 74), + Genre("Lãng Mạn", 75), + Genre("Gender", 76), + Genre("Bender", 77), + Genre("Vườn Trường", 78), + Genre("Magic", 79), + Genre("Nhân Thú", 80), + Genre("Soft Yaoi", 81), + Genre("Hôn Nhân Hợp Đồng", 82), + Genre("Cưới Trước Yêu Sau", 83), + Genre("Bi Kịch", 84), + Genre("Horror", 85), + Genre("Reincarnation", 86), + Genre("Hồi Sinh", 87), + Genre("Hoàng Gia", 88), + Genre("Giả Tưởng", 89), + Genre("Xuyên Sách", 90), + Genre("Hài", 91), + Genre("Ngọt", 92), + Genre("Nam Cường", 93), + Genre("Chủ Nam", 94), + Genre("Minh Tinh", 95), + Genre("Cổ Trang", 96), + Genre("Xuyên Game", 97), + Genre("Villainess", 98), + Genre("Cung Đấu", 99), + Genre("Hành Động", 100), + Genre("Truyện Tranh", 101), + Genre("Adaptation", 102), + Genre("Magi", 103), + Genre("Âu Cổ", 104), + ) +} diff --git a/multisrc/overrides/mymangacms/phemanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mymangacms/phemanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..5f47cda3d Binary files /dev/null and b/multisrc/overrides/mymangacms/phemanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mymangacms/phemanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mymangacms/phemanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..5622f03a6 Binary files /dev/null and b/multisrc/overrides/mymangacms/phemanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mymangacms/phemanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mymangacms/phemanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..b279d4d2c Binary files /dev/null and b/multisrc/overrides/mymangacms/phemanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mymangacms/phemanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mymangacms/phemanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..fac42cf6d Binary files /dev/null and b/multisrc/overrides/mymangacms/phemanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mymangacms/phemanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mymangacms/phemanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..96a63ca6e Binary files /dev/null and b/multisrc/overrides/mymangacms/phemanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mymangacms/phemanga/res/web_hi_res_512.png b/multisrc/overrides/mymangacms/phemanga/res/web_hi_res_512.png new file mode 100644 index 000000000..e7546a5a7 Binary files /dev/null and b/multisrc/overrides/mymangacms/phemanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mymangacms/phemanga/src/PheManga.kt b/multisrc/overrides/mymangacms/phemanga/src/PheManga.kt new file mode 100644 index 000000000..f26e9ff44 --- /dev/null +++ b/multisrc/overrides/mymangacms/phemanga/src/PheManga.kt @@ -0,0 +1,68 @@ +package eu.kanade.tachiyomi.extension.vi.phemanga + +import eu.kanade.tachiyomi.multisrc.mymangacms.MyMangaCMS + +class PheManga : MyMangaCMS("Phê Manga", "https://phemanga.net", "vi") { + override fun dateUpdatedParser(date: String): Long = + runCatching { super.dateUpdatedParser(date.split(" - ")[1]) }.getOrNull() ?: 0L + + override fun getGenreList() = listOf( + Genre("16+", 1), + Genre("18+", 2), + Genre("Action", 3), + Genre("Adult", 4), + Genre("Adventure", 5), + Genre("Anime", 6), + Genre("Comedy", 7), + Genre("Comic", 8), + Genre("Doujinshi", 9), + Genre("Drama", 10), + Genre("Ecchi", 11), + Genre("Fantasy", 13), + Genre("Full màu", 14), + Genre("Game", 15), + Genre("Gender Bender", 16), + Genre("Harem", 17), + Genre("Historical", 18), + Genre("Horror", 19), + Genre("Isekai/Dị giới/Trọng sinh", 20), + Genre("Josei", 21), + Genre("Live action", 22), + Genre("Magic", 23), + Genre("Manga", 24), + Genre("Manhua", 25), + Genre("Manhwa", 26), + Genre("Martial Arts", 27), + Genre("Mature", 28), + Genre("Mecha", 29), + Genre("Mystery", 30), + Genre("Nấu Ăn", 31), + Genre("Ngôn Tình", 32), + Genre("NTR", 33), + Genre("One shot", 34), + Genre("Psychological", 35), + Genre("Romance", 36), + Genre("School Life", 37), + Genre("Sci-fi", 38), + Genre("Seinen", 39), + Genre("Shoujo", 40), + Genre("Shoujo Ai", 41), + Genre("Shounen", 42), + Genre("Shounen Ai", 43), + Genre("Slice of life", 44), + Genre("Smut", 45), + Genre("Soft Yaoi", 46), + Genre("Soft Yuri", 47), + Genre("Sports", 48), + Genre("Supernatural", 49), + Genre("Tạp chí truyện tranh", 50), + Genre("Tragedy", 51), + Genre("Trap (Crossdressing)", 52), + Genre("Trinh Thám", 53), + Genre("Truyện scan", 54), + Genre("Tu chân - tu tiên", 55), + Genre("VnComic", 56), + Genre("Webtoon", 57), + Genre("Yuri", 58), + ) +} diff --git a/src/vi/truyentranhlh/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mymangacms/truyentranhlh/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from src/vi/truyentranhlh/res/mipmap-hdpi/ic_launcher.png rename to multisrc/overrides/mymangacms/truyentranhlh/res/mipmap-hdpi/ic_launcher.png diff --git a/src/vi/truyentranhlh/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mymangacms/truyentranhlh/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from src/vi/truyentranhlh/res/mipmap-mdpi/ic_launcher.png rename to multisrc/overrides/mymangacms/truyentranhlh/res/mipmap-mdpi/ic_launcher.png diff --git a/src/vi/truyentranhlh/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mymangacms/truyentranhlh/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from src/vi/truyentranhlh/res/mipmap-xhdpi/ic_launcher.png rename to multisrc/overrides/mymangacms/truyentranhlh/res/mipmap-xhdpi/ic_launcher.png diff --git a/src/vi/truyentranhlh/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mymangacms/truyentranhlh/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from src/vi/truyentranhlh/res/mipmap-xxhdpi/ic_launcher.png rename to multisrc/overrides/mymangacms/truyentranhlh/res/mipmap-xxhdpi/ic_launcher.png diff --git a/src/vi/truyentranhlh/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mymangacms/truyentranhlh/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from src/vi/truyentranhlh/res/mipmap-xxxhdpi/ic_launcher.png rename to multisrc/overrides/mymangacms/truyentranhlh/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/src/vi/truyentranhlh/res/web_hi_res_512.png b/multisrc/overrides/mymangacms/truyentranhlh/res/web_hi_res_512.png similarity index 100% rename from src/vi/truyentranhlh/res/web_hi_res_512.png rename to multisrc/overrides/mymangacms/truyentranhlh/res/web_hi_res_512.png diff --git a/multisrc/overrides/mymangacms/truyentranhlh/src/TruyenTranhLH.kt b/multisrc/overrides/mymangacms/truyentranhlh/src/TruyenTranhLH.kt new file mode 100644 index 000000000..182c40d28 --- /dev/null +++ b/multisrc/overrides/mymangacms/truyentranhlh/src/TruyenTranhLH.kt @@ -0,0 +1,68 @@ +package eu.kanade.tachiyomi.extension.vi.truyentranhlh + +import eu.kanade.tachiyomi.multisrc.mymangacms.MyMangaCMS + +class TruyenTranhLH : MyMangaCMS("TruyenTranhLH", "https://truyentranhlh.net", "vi") { + override val id: Long = 7969606392351831672 + + override fun getGenreList() = listOf( + Genre("Action", 1), + Genre("Adult", 2), + Genre("Adventure", 3), + Genre("Anime", 4), + Genre("Chuyển Sinh", 5), + Genre("Cổ Đại", 6), + Genre("Comedy", 7), + Genre("Comic", 8), + Genre("Demons", 9), + Genre("Detective", 10), + Genre("Doujinshi", 11), + Genre("Drama", 12), + Genre("Đam Mỹ", 13), + Genre("Ecchi", 14), + Genre("Fantasy", 15), + Genre("Gender Bender", 16), + Genre("Harem", 17), + Genre("Historical", 18), + Genre("Horror", 19), + Genre("Huyền Huyễn", 20), + Genre("Isekai", 21), + Genre("Josei", 22), + Genre("Mafia", 23), + Genre("Magic", 24), + Genre("Manhua", 25), + Genre("Manhwa", 26), + Genre("Martial Arts", 27), + Genre("Mature", 28), + Genre("Military", 29), + Genre("Mystery", 30), + Genre("Ngôn Tình", 31), + Genre("One shot", 32), + Genre("Psychological", 33), + Genre("Romance", 34), + Genre("School Life", 35), + Genre("Sci-fi", 36), + Genre("Seinen", 37), + Genre("Shoujo", 38), + Genre("Shoujo Ai", 39), + Genre("Shounen", 40), + Genre("Shounen Ai", 41), + Genre("Slice of life", 42), + Genre("Smut", 43), + Genre("Sports", 44), + Genre("Supernatural", 45), + Genre("Tragedy", 46), + Genre("Trọng Sinh", 47), + Genre("Truyện Màu", 48), + Genre("Webtoon", 49), + Genre("Xuyên Không", 50), + Genre("Yaoi", 51), + Genre("Yuri", 52), + Genre("Mecha", 53), + Genre("Cooking", 54), + Genre("Trùng Sinh", 55), + Genre("Gourmet", 56), + Genre("Dark Fantasy", 57), + ) +} + diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mymangacms/MyMangaCMS.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mymangacms/MyMangaCMS.kt new file mode 100644 index 000000000..e75639780 --- /dev/null +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mymangacms/MyMangaCMS.kt @@ -0,0 +1,334 @@ +package eu.kanade.tachiyomi.multisrc.mymangacms + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.interceptor.rateLimit +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.Headers +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import org.jsoup.select.Evaluator +import rx.Observable +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.TimeZone +import java.util.concurrent.TimeUnit + +abstract class MyMangaCMS( + override val name: String, + override val baseUrl: String, + override val lang: String +) : ParsedHttpSource() { + + override val supportsLatest = true + + override val client = network.cloudflareClient.newBuilder().apply { + rateLimit(3, 1) + connectTimeout(1, TimeUnit.MINUTES) + readTimeout(1, TimeUnit.MINUTES) + writeTimeout(1, TimeUnit.MINUTES) + }.build() + + override fun headersBuilder(): Headers.Builder = Headers.Builder().apply { + add("Referer", "$baseUrl/") + add( + "User-Agent", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:101.0) Gecko/20100101 Firefox/101.0" + ) + } + + //region Source settings + + open val timeZone = "Asia/Ho_Chi_Minh" + + open val dateFormatter = SimpleDateFormat("dd/MM/yyyy", Locale.US).apply { + timeZone = TimeZone.getTimeZone(this@MyMangaCMS.timeZone) + } + + open fun dateUpdatedParser(date: String): Long = + runCatching { dateFormatter.parse(date)?.time }.getOrNull() ?: 0L + + private val floatingNumberRegex = Regex("""([+-]?(?:[0-9]*[.])?[0-9]+)""") + + /** + * Regex for extracting URL from CSS `background-image: url()` property. + * + * - `url\(` matches the opening `url(` + * - `['"]?` checks for the existence (or lack thereof) of single/double quotes + * - `(.*?)` captures everything up to but not including the next quote + * - `\)` to match the closing bracket. + */ + private val backgroundImageRegex = Regex("""url\(['"]?(.*?)['"]?\)""") + //endregion + + //region Popular + + override fun popularMangaRequest(page: Int): Request = GET( + baseUrl.toHttpUrl().newBuilder().apply { + addPathSegment("tim-kiem") + addQueryParameter("sort", "top") + addQueryParameter("page", page.toString()) + }.build().toString() + ) + + override fun popularMangaSelector(): String = "div.thumb-item-flow.col-6.col-md-2" + + override fun popularMangaNextPageSelector(): String? = + "div.pagination_wrap a.paging_item:last-of-type:not(.disabled)" + + override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { + setUrlWithoutDomain(element.select("a").first().attr("abs:href")) + title = element.select("div.thumb_attr.series-title a[title]").first().text() + thumbnail_url = element.select("div[data-bg]").first().attr("data-bg") + } + //endregion + + //region Latest + + override fun latestUpdatesRequest(page: Int): Request = GET( + baseUrl.toHttpUrl().newBuilder().apply { + addPathSegment("tim-kiem") + addQueryParameter("sort", "update") + addQueryParameter("page", page.toString()) + }.build().toString() + ) + + override fun latestUpdatesSelector(): String = popularMangaSelector() + + override fun latestUpdatesNextPageSelector(): String? = popularMangaNextPageSelector() + + override fun latestUpdatesFromElement(element: Element): SManga = + popularMangaFromElement(element) + //endregion + + //region Search + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { + return when { + query.startsWith(PREFIX_URL_SEARCH) -> { + fetchMangaDetails(SManga.create().apply { + url = query.removePrefix(PREFIX_URL_SEARCH).trim().replace(baseUrl, "") + }) + .map { MangasPage(listOf(it), false) } + } + else -> super.fetchSearchManga(page, query, filters) + } + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = + GET( + baseUrl.toHttpUrl().newBuilder().apply { + val genres = mutableListOf() + val genresEx = mutableListOf() + addPathSegment("tim-kiem") + addQueryParameter("page", page.toString()) + (if (filters.isEmpty()) getFilterList() else filters).forEach { + when (it) { + is GenreList -> it.state.forEach { genre -> + when (genre.state) { + Filter.TriState.STATE_INCLUDE -> genres.add(genre.id) + Filter.TriState.STATE_EXCLUDE -> genresEx.add(genre.id) + else -> {} + } + } + is Author -> if (it.state.isNotEmpty()) { + addQueryParameter("artist", it.state) + } + is Sort -> addQueryParameter("sort", it.toUriPart()) + is Status -> if (it.state != 0) { + addQueryParameter("status", it.state.toString()) + } + else -> {} + } + } + if (genresEx.isNotEmpty()) { + addQueryParameter("reject_genres", genresEx.joinToString(",")) + } + if (genres.isNotEmpty()) { + addQueryParameter("accept_genres", genres.joinToString(",")) + } + if (query.isNotEmpty()) { + addQueryParameter("q", query) + } + }.build().toString() + ) + + override fun searchMangaSelector(): String = popularMangaSelector() + + override fun searchMangaNextPageSelector(): String? = popularMangaNextPageSelector() + + override fun searchMangaFromElement(element: Element): SManga = + popularMangaFromElement(element) + //endregion + + //region Manga details + + override fun mangaDetailsRequest(manga: SManga): Request = GET("$baseUrl${manga.url}") + + override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply { + setUrlWithoutDomain( + document.select(".series-name-group a") + .first() + .attr("abs:href") + ) + title = document.select(".series-name").first().text().trim() + + var alternativeNames: String? = null + document.select(".info-item").forEach { + val value = it.select(".info-value") + when (it.select(".info-name").text().trim()) { + "Tên khác:" -> alternativeNames = value.joinToString(", ") { name -> + name.text().trim() + } + "Tác giả:" -> author = value.joinToString(", ") { auth -> + auth.text().trim() + } + "Tình trạng:" -> status = when (value.first().text().lowercase().trim()) { + "đang tiến hành" -> SManga.ONGOING + "tạm ngưng" -> SManga.ON_HIATUS + "đã hoàn thành" -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + } + } + + val descElem = document.select(".summary-content") + description = if (descElem.select("p").any()) { + descElem.select("p").joinToString("\n") { + it.run { + select(Evaluator.Tag("br")).prepend("\\n") + this.text() + .replace("\\n", "\n") + .replace("\n ", "\n") + } + }.trim() + } else { + descElem.text().trim() + } + + if (!alternativeNames.isNullOrEmpty()) { + description = "Tên khác: ${alternativeNames}\n\n" + description + } + + genre = document.select("a[href*=the-loai] span.badge") + .joinToString(", ") { it.text().trim() } + + thumbnail_url = document + .select("div.content.img-in-ratio") + .first() + .attr("style") + .let { backgroundImageRegex.find(it)?.groups?.get(1)?.value } + } + //endregion + + //region Chapter list + + override fun chapterListRequest(manga: SManga): Request = mangaDetailsRequest(manga) + + override fun chapterListSelector(): String = "ul.list-chapters > a" + + override fun chapterFromElement(element: Element): SChapter = throw Exception("Not used") + + private fun chapterFromElement(element: Element, scanlator: String?): SChapter = + SChapter.create().apply { + setUrlWithoutDomain(element.attr("abs:href")) + name = element.select("div.chapter-name").first().text() + date_upload = dateUpdatedParser( + element.select("div.chapter-time").first().text() + ) + + val match = floatingNumberRegex.find(name) + chapter_number = if (name.lowercase().startsWith("vol")) { + match?.groups?.get(2) + } else { + match?.groups?.get(1) + }?.value?.toFloat() ?: -1f + + this.scanlator = scanlator + } + + override fun chapterListParse(response: Response): List { + val document = response.asJsoup() + val originalScanlator = document.select("div.fantrans-value a") + val scanlator: String? = if (originalScanlator.isEmpty() || + originalScanlator.first().text().trim().lowercase() == "đang cập nhật") { + null + } else { + originalScanlator.first().text().trim() + } + + return document.select(chapterListSelector()).map { chapterFromElement(it, scanlator) } + } + //endregion + + //region Pages + + override fun pageListRequest(chapter: SChapter): Request = GET("$baseUrl${chapter.url}") + + override fun pageListParse(document: Document): List = + document + .select("div#chapter-content img") + .filterNot { it.attr("abs:data-src").isNullOrEmpty() } + .mapIndexed { index, elem -> Page(index, "", elem.attr("abs:data-src")) } + + override fun imageUrlParse(document: Document): String = throw Exception("Not used") + //endregion + + //region Filters + open class UriPartFilter( + displayName: String, + private val vals: Array>, + state: Int = 0, + ) : Filter.Select(displayName, vals.map { it.first }.toTypedArray(), state) { + fun toUriPart() = vals[state].second + } + private class Status : Filter.Select( + "Tình trạng", + arrayOf( + "Tất cả", + "Đang tiến hành", + "Tạm ngưng", + "Hoàn thành" + ) + ) + private class Sort : UriPartFilter( + "Sắp xếp", + arrayOf( + Pair("A-Z", "az"), + Pair("Z-A", "za"), + Pair("Mới cập nhật", "update"), + Pair("Truyện mới", "new"), + Pair("Xem nhiều", "top"), + Pair("Được thích nhiều", "like"), + ), + 4 + ) + open class Genre(name: String, val id: Int) : Filter.TriState(name) + private class Author : Filter.Text("Tác giả") + private class GenreList(genres: List) : Filter.Group("Thể loại", genres) + + override fun getFilterList(): FilterList = FilterList( + Author(), + Status(), + Sort(), + GenreList(getGenreList()), + ) + + // To populate this list: + // console.log([...document.querySelectorAll("div.search-gerne_item")].map(elem => `Genre("${elem.textContent.trim()}", ${elem.querySelector("label").getAttribute("data-genre-id")}),`).join("\n")) + abstract fun getGenreList(): List + //endregion + + companion object { + const val PREFIX_URL_SEARCH = "url:" + } +} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mymangacms/MyMangaCMSGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mymangacms/MyMangaCMSGenerator.kt new file mode 100644 index 000000000..3d0f4e684 --- /dev/null +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mymangacms/MyMangaCMSGenerator.kt @@ -0,0 +1,38 @@ +package eu.kanade.tachiyomi.multisrc.mymangacms + +import generator.ThemeSourceData.SingleLang +import generator.ThemeSourceGenerator + +class MyMangaCMSGenerator : ThemeSourceGenerator { + + override val themePkg = "mymangacms" + + override val themeClass = "MyMangaCMS" + + override val baseVersionCode: Int = 1 + + override val sources = listOf( + SingleLang( + "TruyenTranhLH", + "https://truyentranhlh.net", + "vi", + overrideVersionCode = 9 + ), + SingleLang( + "Phê Manga", + "https://phemanga.net", + "vi", + true, + "PheManga", + "phemanga", + ), + SingleLang("LKDTT", "https://lkdtt.com", "vi", true) + ) + + companion object { + @JvmStatic + fun main(args: Array) { + MyMangaCMSGenerator().createAll() + } + } +} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mymangacms/MyMangaCMSUrlActivity.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mymangacms/MyMangaCMSUrlActivity.kt new file mode 100644 index 000000000..0f3d43f3e --- /dev/null +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mymangacms/MyMangaCMSUrlActivity.kt @@ -0,0 +1,31 @@ +package eu.kanade.tachiyomi.multisrc.mymangacms + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle +import android.util.Log +import kotlin.system.exitProcess + +class MyMangaCMSUrlActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val pathSegments = intent?.data?.pathSegments + if (pathSegments != null && pathSegments.size > 1) { + try { + startActivity(Intent().apply { + action = "eu.kanade.tachiyomi.SEARCH" + putExtra("query", "${MyMangaCMS.PREFIX_URL_SEARCH}${intent?.data?.path}") + putExtra("filter", packageName) + }) + } catch (e: ActivityNotFoundException) { + Log.e("MyMangaCMSUrlActivity", e.toString()) + } + } else { + Log.e("MyMangaCMSUrlActivity", "Could not parse URI from intent $intent") + } + + finish() + exitProcess(0) + } +} diff --git a/src/vi/truyentranhlh/AndroidManifest.xml b/src/vi/truyentranhlh/AndroidManifest.xml deleted file mode 100644 index 30deb7f79..000000000 --- a/src/vi/truyentranhlh/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/vi/truyentranhlh/build.gradle b/src/vi/truyentranhlh/build.gradle deleted file mode 100644 index 252e4c958..000000000 --- a/src/vi/truyentranhlh/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' - -ext { - extName = 'TruyenTranhLH' - pkgNameSuffix = 'vi.truyentranhlh' - extClass = '.TruyenTranhLH' - extVersionCode = 9 -} - -apply from: "$rootDir/common.gradle" diff --git a/src/vi/truyentranhlh/src/eu/kanade/tachiyomi/extension/vi/truyentranhlh/TruyenTranhLH.kt b/src/vi/truyentranhlh/src/eu/kanade/tachiyomi/extension/vi/truyentranhlh/TruyenTranhLH.kt deleted file mode 100644 index 084c7a143..000000000 --- a/src/vi/truyentranhlh/src/eu/kanade/tachiyomi/extension/vi/truyentranhlh/TruyenTranhLH.kt +++ /dev/null @@ -1,126 +0,0 @@ -package eu.kanade.tachiyomi.extension.vi.truyentranhlh - -import eu.kanade.tachiyomi.network.GET -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 okhttp3.Headers -import okhttp3.OkHttpClient -import okhttp3.Request -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class TruyenTranhLH : ParsedHttpSource() { - - override val name = "TruyenTranhLH" - - override val baseUrl = "https://truyentranhlh.net" - - override val lang = "vi" - - override val supportsLatest = true - - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(1, TimeUnit.MINUTES) - .readTimeout(1, TimeUnit.MINUTES) - .writeTimeout(1, TimeUnit.MINUTES) - .build() - - override fun headersBuilder(): Headers.Builder = Headers.Builder() - .add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0") - - // Popular - - override fun popularMangaRequest(page: Int): Request { - return GET("$baseUrl/tim-kiem?sort=top&page=$page", headers) - } - - override fun popularMangaSelector() = "div.thumb-item-flow" - - override fun popularMangaFromElement(element: Element): SManga { - return SManga.create().apply { - element.select("div.series-title a").let { - title = it.text() - setUrlWithoutDomain(it.attr("href")) - } - thumbnail_url = element.select("div.content").attr("abs:data-bg") - } - } - - override fun popularMangaNextPageSelector() = "div.pagination_wrap a.page_num.current + a:not(.disabled)" - - // Latest - - override fun latestUpdatesRequest(page: Int): Request { - return GET("$baseUrl/tim-kiem?sort=update&page=$page", headers) - } - - override fun latestUpdatesSelector() = popularMangaSelector() - - override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element) - - override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector() - - // Search - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - return GET("$baseUrl/tim-kiem?q=$query&sort=update&page=$page", headers) - } - - override fun searchMangaSelector() = popularMangaSelector() - - override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element) - - override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() - - // Details - - override fun mangaDetailsParse(document: Document): SManga { - val infoElement = document.select("div.top-part") - return SManga.create().apply { - genre = infoElement.select("span.info-name:contains(Thể loại) + span a").joinToString { it.text() } - author = infoElement.select("span.info-name:contains(Tác giả) + span").text() - status = infoElement.select("span.info-name:contains(Tình trạng) + span").text().toStatus() - thumbnail_url = infoElement.select("div.content").attr("style") - .let { Regex("""url\("(.*)"\)""").find(it)?.groups?.get(1)?.value } - description = document.select("div.summary-content").text() - } - } - - private fun String?.toStatus() = when { - this == null -> SManga.UNKNOWN - this.contains("Đang tiến hành", ignoreCase = true) -> SManga.ONGOING - this.contains("Đã hoàn thành", ignoreCase = true) -> SManga.COMPLETED - else -> SManga.UNKNOWN - } - - // Chapters - - override fun chapterListSelector(): String = "ul.list-chapters a" - - override fun chapterFromElement(element: Element): SChapter { - return SChapter.create().apply { - setUrlWithoutDomain(element.attr("href")) - name = element.select("div.chapter-name").text() - date_upload = element.select("div.chapter-time").firstOrNull()?.text() - ?.let { SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).parse(it)?.time ?: 0L } ?: 0 - } - } - - // Pages - - override fun pageListParse(document: Document): List { - return document.select("div#chapter-content img") - .filterNot { imgEl -> imgEl.attr("abs:data-src").isNullOrEmpty() } - .mapIndexed { i, img -> - Page(i, "", img.attr("abs:data-src")) - } - } - - override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used") -}