diff --git a/src/vi/truyentranh3q/build.gradle b/src/vi/truyentranh3q/build.gradle index 02ed19119..03bc432ee 100644 --- a/src/vi/truyentranh3q/build.gradle +++ b/src/vi/truyentranh3q/build.gradle @@ -1,8 +1,8 @@ ext { extName = 'TruyenTranh3Q' extClass = '.TruyenTranh3Q' - baseUrl = 'https://truyentranh3q.com' - extVersionCode = 1 + baseUrl = 'https://truyentranh3qb.com' + extVersionCode = 2 isNsfw = true } diff --git a/src/vi/truyentranh3q/res/mipmap-hdpi/ic_launcher.png b/src/vi/truyentranh3q/res/mipmap-hdpi/ic_launcher.png index 63c1ecedf..9fbe594d9 100644 Binary files a/src/vi/truyentranh3q/res/mipmap-hdpi/ic_launcher.png and b/src/vi/truyentranh3q/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/vi/truyentranh3q/res/mipmap-mdpi/ic_launcher.png b/src/vi/truyentranh3q/res/mipmap-mdpi/ic_launcher.png index 6f7a804cb..62e88f26b 100644 Binary files a/src/vi/truyentranh3q/res/mipmap-mdpi/ic_launcher.png and b/src/vi/truyentranh3q/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/vi/truyentranh3q/res/mipmap-xhdpi/ic_launcher.png b/src/vi/truyentranh3q/res/mipmap-xhdpi/ic_launcher.png index 3b2959eb5..84e6e0bae 100644 Binary files a/src/vi/truyentranh3q/res/mipmap-xhdpi/ic_launcher.png and b/src/vi/truyentranh3q/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/vi/truyentranh3q/res/mipmap-xxhdpi/ic_launcher.png b/src/vi/truyentranh3q/res/mipmap-xxhdpi/ic_launcher.png index ded3213c9..4134790ea 100644 Binary files a/src/vi/truyentranh3q/res/mipmap-xxhdpi/ic_launcher.png and b/src/vi/truyentranh3q/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/vi/truyentranh3q/res/mipmap-xxxhdpi/ic_launcher.png b/src/vi/truyentranh3q/res/mipmap-xxxhdpi/ic_launcher.png index e27de1a9c..d7fb3118e 100644 Binary files a/src/vi/truyentranh3q/res/mipmap-xxxhdpi/ic_launcher.png and b/src/vi/truyentranh3q/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/vi/truyentranh3q/src/eu/kanade/tachiyomi/extension/vi/truyentranh3q/TruyenTranh3Q.kt b/src/vi/truyentranh3q/src/eu/kanade/tachiyomi/extension/vi/truyentranh3q/TruyenTranh3Q.kt index 655a251e2..94303feb7 100644 --- a/src/vi/truyentranh3q/src/eu/kanade/tachiyomi/extension/vi/truyentranh3q/TruyenTranh3Q.kt +++ b/src/vi/truyentranh3q/src/eu/kanade/tachiyomi/extension/vi/truyentranh3q/TruyenTranh3Q.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.extension.vi.truyentranh3q -import android.net.ParseException import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.source.model.Filter @@ -10,6 +9,7 @@ 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 keiyoushi.utils.tryParse import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -27,7 +27,7 @@ import java.util.Locale class TruyenTranh3Q : ParsedHttpSource() { override val name: String = "TruyenTranh3Q" override val lang: String = "vi" - override val baseUrl: String = "https://truyentranh3q.com" + override val baseUrl: String = "https://truyentranh3qb.com" override val supportsLatest: Boolean = true override val client: OkHttpClient = network.cloudflareClient.newBuilder() @@ -74,15 +74,14 @@ class TruyenTranh3Q : ParsedHttpSource() { override fun latestUpdatesNextPageSelector(): String? = popularMangaNextPageSelector() // search - private val searchPath = "tim-kiem-nang-cao" - private val queryParam = "keyword" + private val searchUrl = "$baseUrl/tim-kiem-nang-cao" override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val url = "$baseUrl/$searchPath".toHttpUrl().newBuilder() + val url = searchUrl.toHttpUrl().newBuilder() .addQueryParameter("page", page.toString()) // always add search query if present if (query.isNotBlank()) { - url.addQueryParameter(queryParam, query) + url.addQueryParameter("keyword", query) } // process filters regardless of search query @@ -124,62 +123,64 @@ class TruyenTranh3Q : ParsedHttpSource() { override fun mangaDetailsParse(document: Document): SManga { return SManga.create().apply { document.selectFirst(".book_info > .book_other")?.let { info -> - title = info.selectFirst("h1[itemprop=name]")!!.text() + title = info.select("h1[itemprop=name]").text() author = info.selectFirst("ul.list-info li.author p.col-xs-9")?.text() - status = when (info.selectFirst("ul.list-info li.status p.col-xs-9")?.text()) { - "Đang Cập Nhật" -> SManga.ONGOING - "Hoàn Thành" -> SManga.COMPLETED - else -> SManga.UNKNOWN - } + status = parseStatus(info.selectFirst("ul.list-info li.status p.col-xs-9")?.text()) genre = info.select(".list01 li a").joinToString { it.text() } } - description = document.selectFirst(".book_detail > .story-detail-info")?.text() - thumbnail_url = document.selectFirst(".book_detail > .book_info > .book_avatar > img")?.attr("abs:src") + description = document.select(".book_detail > .story-detail-info").joinToString { it.wholeText() } + thumbnail_url = document.selectFirst(".book_detail > .book_info > .book_avatar > img")?.absUrl("abs:src") + } + } + + private fun parseStatus(status: String?): Int { + val ongoingStatus = listOf("Đang Cập Nhật", "Đang Tiến Hành") + val completedStatus = listOf("Hoàn Thành", "Đã Hoàn Thành") + return when { + status == null -> SManga.UNKNOWN + ongoingStatus.any { status.contains(it, ignoreCase = true) } -> SManga.ONGOING + completedStatus.any { status.contains(it, ignoreCase = true) } -> SManga.COMPLETED + else -> SManga.UNKNOWN } } // chapters override fun chapterListSelector(): String = ".works-chapter-list .works-chapter-item" - private fun parseRelativeDate(dateString: String): Long { - val now = Calendar.getInstance() + val number = Regex("""(\d+)""").find(dateString)?.value?.toIntOrNull() ?: return 0 + val cal = Calendar.getInstance() - val timeUnits = mapOf( - "giây" to Calendar.SECOND, - "phút" to Calendar.MINUTE, - "giờ" to Calendar.HOUR, - "ngày" to Calendar.DAY_OF_YEAR, - "tuần" to Calendar.WEEK_OF_YEAR, - "tháng" to Calendar.MONTH, - "năm" to Calendar.YEAR, - ) - - // extract the number and time unit from the string - val parts = dateString.replace(" trước", "").split(" ") - if (parts.size < 2) return 0L - - val number = parts[0].toIntOrNull() ?: return 0L - val unit = parts[1] - - // find the matching time unit - val calendarUnit = timeUnits.entries.find { (key, _) -> unit.startsWith(key) }?.value ?: return 0L - - // subtract the time - now.add(calendarUnit, -number) - - return now.timeInMillis + return when { + listOf("giây", "giây trước").any { dateString.contains(it, ignoreCase = true) } -> { + cal.add(Calendar.SECOND, -number); cal.timeInMillis + } + listOf("phút", "phút trước").any { dateString.contains(it, ignoreCase = true) } -> { + cal.add(Calendar.MINUTE, -number); cal.timeInMillis + } + listOf("giờ", "giờ trước").any { dateString.contains(it, ignoreCase = true) } -> { + cal.add(Calendar.HOUR, -number); cal.timeInMillis + } + listOf("ngày", "ngày trước").any { dateString.contains(it, ignoreCase = true) } -> { + cal.add(Calendar.DAY_OF_YEAR, -number); cal.timeInMillis + } + listOf("tuần", "tuần trước").any { dateString.contains(it, ignoreCase = true) } -> { + cal.add(Calendar.WEEK_OF_YEAR, -number); cal.timeInMillis + } + listOf("tháng", "tháng trước").any { dateString.contains(it, ignoreCase = true) } -> { + cal.add(Calendar.MONTH, -number); cal.timeInMillis + } + listOf("năm", "năm trước").any { dateString.contains(it, ignoreCase = true) } -> { + cal.add(Calendar.YEAR, -number); cal.timeInMillis + } + else -> 0 + } } private fun parseChapterDate(dateString: String): Long { - val relativeTime = parseRelativeDate(dateString) - return if (relativeTime != 0L) { - relativeTime - } else { - try { - dateFormat.parse(dateString)?.time ?: 0L - } catch (e: ParseException) { - 0L - } + val listCal = listOf("giây", "phút", "giờ", "ngày", "tuần", "tháng", "năm", "trước") + return when { + listCal.any { dateString.contains(it, ignoreCase = true) } -> parseRelativeDate(dateString) + else -> dateFormat.tryParse(dateString) } } @@ -205,10 +206,10 @@ class TruyenTranh3Q : ParsedHttpSource() { override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException() // filters - class SortFilter(name: String, val options: List) : Filter.Select(name, options.toTypedArray()) - class StatusFilter(name: String, val options: List) : Filter.Select(name, options.toTypedArray()) - class CountryFilter(name: String, val options: List, val countryValues: List) : Filter.Select(name, options.toTypedArray()) - class MinChapterFilter(name: String, val options: List, val chapterValues: List) : Filter.Select(name, options.toTypedArray()) + class SortFilter(name: String, options: List) : Filter.Select(name, options.toTypedArray()) + class StatusFilter(name: String, options: List) : Filter.Select(name, options.toTypedArray()) + class CountryFilter(name: String, options: List, val countryValues: List) : Filter.Select(name, options.toTypedArray()) + class MinChapterFilter(name: String, options: List, val chapterValues: List) : Filter.Select(name, options.toTypedArray()) class Genre(name: String, val id: Int) : Filter.TriState(name) class GenreFilter(name: String, state: List) : Filter.Group(name, state) @@ -246,7 +247,7 @@ class TruyenTranh3Q : ParsedHttpSource() { private var genreList: List = emptyList() private var fetchGenreAttempts: Int = 0 - private fun genresRequest() = GET("$baseUrl/$searchPath", headers) + private fun genresRequest() = GET(searchUrl, headers) private fun parseGenres(document: Document): List { return document.select(".genre-item").mapIndexed { index, element ->