diff --git a/src/zh/tohomh123/build.gradle b/src/zh/tohomh123/build.gradle new file mode 100644 index 000000000..d5e48d33f --- /dev/null +++ b/src/zh/tohomh123/build.gradle @@ -0,0 +1,17 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + appName = 'Tachiyomi: Tohomh123' + pkgNameSuffix = 'zh.tohomh123' + extClass = '.Tohomh' + extVersionCode = 1 + libVersion = '1.2' +} + +dependencies { + compileOnly 'com.google.code.gson:gson:2.8.2' + compileOnly 'com.github.salomonbrys.kotson:kotson:2.5.0' +} + +apply from: "$rootDir/common.gradle" diff --git a/src/zh/tohomh123/res/mipmap-hdpi/ic_launcher.png b/src/zh/tohomh123/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..b44315a63 Binary files /dev/null and b/src/zh/tohomh123/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/zh/tohomh123/res/mipmap-mdpi/ic_launcher.png b/src/zh/tohomh123/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..8bad12d98 Binary files /dev/null and b/src/zh/tohomh123/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/zh/tohomh123/res/mipmap-xhdpi/ic_launcher.png b/src/zh/tohomh123/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..c6422128a Binary files /dev/null and b/src/zh/tohomh123/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/zh/tohomh123/res/mipmap-xxhdpi/ic_launcher.png b/src/zh/tohomh123/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..1a3a3282a Binary files /dev/null and b/src/zh/tohomh123/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/zh/tohomh123/res/mipmap-xxxhdpi/ic_launcher.png b/src/zh/tohomh123/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..37d0074e6 Binary files /dev/null and b/src/zh/tohomh123/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/zh/tohomh123/res/web_hi_res_512.png b/src/zh/tohomh123/res/web_hi_res_512.png new file mode 100644 index 000000000..aa1b2edf7 Binary files /dev/null and b/src/zh/tohomh123/res/web_hi_res_512.png differ diff --git a/src/zh/tohomh123/src/eu/kanade/tachiyomi/extension/zh/tohomh123/Tohomh.kt b/src/zh/tohomh123/src/eu/kanade/tachiyomi/extension/zh/tohomh123/Tohomh.kt new file mode 100644 index 000000000..aaf39649d --- /dev/null +++ b/src/zh/tohomh123/src/eu/kanade/tachiyomi/extension/zh/tohomh123/Tohomh.kt @@ -0,0 +1,155 @@ +package eu.kanade.tachiyomi.extension.zh.tohomh123 + +import com.github.salomonbrys.kotson.fromJson +import com.google.gson.Gson +import com.google.gson.JsonObject +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 eu.kanade.tachiyomi.util.asJsoup +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.* + +class Tohomh : ParsedHttpSource() { + + override val name = "Tohomh123" + + override val baseUrl = "https://www.tohomh123.com" + + override val lang = "zh" + + override val supportsLatest = true + + override val client: OkHttpClient = network.cloudflareClient + + // Popular + + override fun popularMangaRequest(page: Int): Request { + return GET("$baseUrl/f-1-------hits--$page.html", headers) + } + + override fun popularMangaSelector() = "div.mh-item" + + override fun popularMangaFromElement(element: Element): SManga { + val manga = SManga.create() + element.select("h2 a").let { + manga.setUrlWithoutDomain(it.attr("href")) + manga.title = it.attr("title") + } + manga.thumbnail_url = element.select("p").attr("style").substringAfter("(").substringBefore(")") + return manga + } + + override fun popularMangaNextPageSelector() = "div.page-pagination li a:contains(>)" + + // Latest + + override fun latestUpdatesRequest(page: Int): Request { + return GET("$baseUrl/f-1------updatetime--$page.html", headers) + } + + override fun latestUpdatesSelector() = popularMangaSelector() + + override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element) + + override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector() + + // Search + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + return GET("$baseUrl/action/Search?keyword=$query&page=$page", headers) + } + + override fun searchMangaSelector() = popularMangaSelector() + + override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element) + + override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() + + // Manga summary page + + override fun mangaDetailsParse(document: Document): SManga { + val infoElement = document.select("div.banner_detail_form").first() + + val manga = SManga.create() + manga.title = infoElement.select("h1").first().text() + manga.author = infoElement.select("div.info p.subtitle").text().substringAfter(":").trim() + val status = infoElement.select("div.banner_detail_form div.info span.block:contains(状态) span").text() + manga.status = parseStatus(status) + manga.description = infoElement.select("div.info p.content").text() + manga.thumbnail_url = infoElement.select("div.banner_detail_form div.cover img").first().attr("src") + return manga + } + + private fun parseStatus(status: String?) = when { + status == null -> SManga.UNKNOWN + status.contains("连载中") -> SManga.ONGOING + status.contains("完结") -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + + // Chapters + + override fun chapterListSelector() = "ul#detail-list-select-1 li a" + + override fun chapterListParse(response: Response): List { + val document = response.asJsoup() + val chapters = mutableListOf() + document.select(chapterListSelector()).map { chapters.add(chapterFromElement(it)) } + // Add date for most recent chapter + document.select("div.banner_detail_form div.info span:contains(更新时间)").text() + .substringAfter(":").trim().let { chapters[0].date_upload = parseChapterDate(it) } + return chapters + } + + override fun chapterFromElement(element: Element): SChapter { + val chapter = SChapter.create() + chapter.setUrlWithoutDomain(element.attr("href")) + chapter.name = element.ownText() + return chapter + } + + companion object { + val dateFormat by lazy { + SimpleDateFormat("yyyy-MM-dd", Locale.CHINA) + } + } + + private fun parseChapterDate(string: String): Long { + return dateFormat.parse(string).time + } + + // Pages + + override fun pageListParse(document: Document): List { + val pages = mutableListOf() + val script = document.select("script:containsData(did)").first().data() + val did = script.substringAfter("did=").substringBefore(";") + val sid = script.substringAfter("sid=").substringBefore(";") + val lastPage = script.substringAfter("pcount =").substringBefore(";").trim().toInt() + + for (i in 1..lastPage) { + pages.add(Page(i, "$baseUrl/action/play/read?did=$did&sid=$sid&iid=$i", "")) + } + return pages + } + + private val gson = Gson() + + override fun imageUrlParse(response: Response): String { + return gson.fromJson(response.body()!!.string())["Code"].asString + } + + override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used") + + override fun getFilterList() = FilterList() + +}