diff --git a/src/zh/jinmantiantang/AndroidManifest.xml b/src/zh/jinmantiantang/AndroidManifest.xml index 30deb7f79..e05fdf795 100644 --- a/src/zh/jinmantiantang/AndroidManifest.xml +++ b/src/zh/jinmantiantang/AndroidManifest.xml @@ -1,2 +1,39 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="eu.kanade.tachiyomi.extension"> + + <application> + <activity + android:name=".zh.jinmantiantang.JinmantiantangUrlActivity" + android:excludeFromRecents="true" + android:theme="@android:style/Theme.NoDisplay"> + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + + <data + android:host="18comic.vip" + android:pathPattern="/album/..*" + android:scheme="https" /> + <data + android:host="18comic.org" + android:pathPattern="/album/..*" + android:scheme="https" /> + <data + android:host="18comic.art" + android:pathPattern="/album/..*" + android:scheme="https" /> + <data + android:host="18comic1.art" + android:pathPattern="/album/..*" + android:scheme="https" /> + <data + android:host="18comic2.art" + android:pathPattern="/album/..*" + android:scheme="https" /> + </intent-filter> + </activity> + </application> +</manifest> \ No newline at end of file diff --git a/src/zh/jinmantiantang/build.gradle b/src/zh/jinmantiantang/build.gradle index cec932865..9d8b57d25 100644 --- a/src/zh/jinmantiantang/build.gradle +++ b/src/zh/jinmantiantang/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Jinmantiantang' pkgNameSuffix = 'zh.jinmantiantang' extClass = '.Jinmantiantang' - extVersionCode = 12 + extVersionCode = 13 libVersion = '1.2' containsNsfw = true } diff --git a/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/Jinmantiantang.kt b/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/Jinmantiantang.kt index 10ff84f54..9eca3fb4d 100644 --- a/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/Jinmantiantang.kt +++ b/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/Jinmantiantang.kt @@ -11,9 +11,11 @@ import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.annotations.Nsfw import eu.kanade.tachiyomi.lib.ratelimit.SpecificHostRateLimitInterceptor import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.asObservableSuccess 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.MangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga @@ -29,6 +31,7 @@ import okhttp3.ResponseBody.Companion.toResponseBody import org.jsoup.nodes.Document import org.jsoup.nodes.Element import org.jsoup.select.Elements +import rx.Observable import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.io.ByteArrayOutputStream @@ -40,7 +43,6 @@ import kotlin.math.floor @Nsfw class Jinmantiantang : ConfigurableSource, ParsedHttpSource() { - override val baseUrl: String = "https://18comic.vip" override val lang: String = "zh" override val name: String = "禁漫天堂" override val supportsLatest: Boolean = true @@ -49,6 +51,8 @@ class Jinmantiantang : ConfigurableSource, ParsedHttpSource() { Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) } + override val baseUrl: String = "https://" + + SITE_ENTRIES_ARRAY[preferences.getString(USE_MIRROR_URL_PREF, "0")!!.toInt()] private val baseHttpUrl: HttpUrl = baseUrl.toHttpUrlOrNull()!! // Add rate limit to fix manga thumbnail load failure @@ -59,9 +63,6 @@ class Jinmantiantang : ConfigurableSource, ParsedHttpSource() { // 图片开始分割的ID编号 private val scrambleId = 220980 - // 对只有一章的漫画进行判断条件 - private var chapterArea = "a[class=col btn btn-primary dropdown-toggle reading]" - // 处理URL请求 override val client: OkHttpClient = network.cloudflareClient .newBuilder() @@ -154,6 +155,26 @@ class Jinmantiantang : ConfigurableSource, ParsedHttpSource() { override fun latestUpdatesSelector(): String = popularMangaSelector() override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element) + // For JinmantiantangUrlActivity + private fun searchMangaByIdRequest(id: String) = GET("$baseUrl/album/$id", headers) + + private fun searchMangaByIdParse(response: Response, id: String): MangasPage { + val sManga = mangaDetailsParse(response) + sManga.url = "/album/$id/" + return MangasPage(listOf(sManga), false) + } + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { + return if (query.startsWith(PREFIX_ID_SEARCH)) { + val id = query.removePrefix(PREFIX_ID_SEARCH) + client.newCall(searchMangaByIdRequest(id)) + .asObservableSuccess() + .map { response -> searchMangaByIdParse(response, id) } + } else { + super.fetchSearchManga(page, query, filters) + } + } + // 查询信息 override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { var params = filters.map { @@ -195,7 +216,6 @@ class Jinmantiantang : ConfigurableSource, ParsedHttpSource() { // status状态 0未知,1连载,2完结,3领取牌照 override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply { - determineChapterInfo(document) title = document.select("div.panel-heading").select("div.pull-left").first().text() // keep thumbnail_url same as the one in popularMangaFromElement() thumbnail_url = document.select("img.lazy_img.img-responsive").attr("src").split("?")[0].replace(".jpg", "_3x4.jpg") @@ -211,18 +231,17 @@ class Jinmantiantang : ConfigurableSource, ParsedHttpSource() { // 查询作者信息 private fun selectAuthor(document: Document): String { - val element = document.select("div.tag-block")[9] + val element = document.select("div.panel-body div.tag-block")[3] return if (element.select("a").size == 0) { "未知" } else { - element.select("a").first().text() + element.select("a").text().trim().replace(" ", ", ") } } // 查询漫画状态和类别信息 private fun selectDetailsStatusAndGenre(document: Document, index: Int): String { - determineChapterInfo(document) - var status = "0" + var status = "2" var genre = "" if (document.select("span[itemprop=genre] a").size == 0) { return if (index == 1) { @@ -237,9 +256,6 @@ class Jinmantiantang : ConfigurableSource, ParsedHttpSource() { "連載中" -> { status = "1" } - "完結" -> { - status = "2" - } else -> { genre = "$genre$vote " } @@ -253,31 +269,28 @@ class Jinmantiantang : ConfigurableSource, ParsedHttpSource() { } // 漫画章节信息 - override fun chapterListSelector(): String = chapterArea + override fun chapterListSelector(): String = "div[id=episode-block] a[href^=/photo/]" + override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) - if (chapterArea == "body") { - name = "Ch. 1" - url = element.select("a[class=col btn btn-primary dropdown-toggle reading]").attr("href") - date_upload = sdf.parse(element.select("div[itemprop='datePublished']").attr("content"))?.time - ?: 0 - } else { - url = element.select("a").attr("href") - name = element.select("a li").first().ownText() - date_upload = sdf.parse(element.select("a li span.hidden-xs").text().trim())?.time ?: 0 - } - } - - private fun determineChapterInfo(document: Document) { - chapterArea = if (document.select("div[id=episode-block] a li").size == 0) { - "body" - } else { - "div[id=episode-block] a[href^=/photo/]" - } + url = element.select("a").attr("href") + name = element.select("a li").first().ownText() + date_upload = sdf.parse(element.select("a li span.hidden-xs").text().trim())?.time ?: 0 } override fun chapterListParse(response: Response): List<SChapter> { - return super.chapterListParse(response).asReversed() + val document = response.asJsoup() + if (document.select("div[id=episode-block] a li").size == 0) { + val singleChapter = SChapter.create().apply { + val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) + name = "单章节" + url = document.select("a[class=col btn btn-primary dropdown-toggle reading]").attr("href") + date_upload = sdf.parse(document.select("div[itemprop='datePublished']").attr("content"))?.time + ?: 0 + } + return listOf(singleChapter) + } + return document.select(chapterListSelector()).map { chapterFromElement(it) } } // 漫画图片信息 @@ -340,7 +353,7 @@ class Jinmantiantang : ConfigurableSource, ParsedHttpSource() { Pair("近亲", "/search/photos?search_query=近親&"), Pair("百合", "/search/photos?search_query=百合&"), Pair("男同", "/search/photos?search_query=YAOI&"), - Pair("性转换", "/search/photos?search_query=性轉換&"), + Pair("性转", "/search/photos?search_query=性轉&"), Pair("NTR", "/search/photos?search_query=NTR&"), Pair("伪娘", "/search/photos?search_query=偽娘&"), Pair("痴女", "/search/photos?search_query=癡女&"), @@ -456,7 +469,26 @@ class Jinmantiantang : ConfigurableSource, ParsedHttpSource() { setDefaultValue("3") setOnPreferenceChangeListener { _, newValue -> try { - val setting = preferences.edit().putString(MAINSITE_RATELIMIT_PREF, newValue as String).commit() + val setting = preferences.edit().putString(MAINSITE_RATELIMIT_PERIOD, newValue as String).commit() + setting + } catch (e: Exception) { + e.printStackTrace() + false + } + } + } + + val mirrorURLPreference = androidx.preference.ListPreference(screen.context).apply { + key = USE_MIRROR_URL_PREF + title = USE_MIRROR_URL_PREF_TITLE + entries = SITE_ENTRIES_ARRAY_DESCRIPTION + entryValues = SITE_ENTRIES_ARRAY_VALUE + summary = USE_MIRROR_URL_PREF_SUMMARY + + setDefaultValue("0") + setOnPreferenceChangeListener { _, newValue -> + try { + val setting = preferences.edit().putString(USE_MIRROR_URL_PREF, newValue as String).commit() setting } catch (e: Exception) { e.printStackTrace() @@ -479,9 +511,12 @@ class Jinmantiantang : ConfigurableSource, ParsedHttpSource() { screen.addPreference(mainSiteRateLimitPreference) screen.addPreference(mainSiteRateLimitPeriodPreference) + screen.addPreference(mirrorURLPreference) } companion object { + private const val DEFAULT_SITE = "18comic.vip" + const val PREFIX_ID_SEARCH = "JM:" private const val BLOCK_PREF_TITLE = "屏蔽词列表" private const val BLOCK_PREF_DEFAULT = "// 例如 \"YAOI cos 扶他 毛絨絨 獵奇 韩漫 韓漫\", " + @@ -496,7 +531,22 @@ class Jinmantiantang : ConfigurableSource, ParsedHttpSource() { private const val MAINSITE_RATELIMIT_PERIOD_TITLE = "限制持续时间。单位秒" // The limiting duration. Defaults to 3. private const val MAINSITE_RATELIMIT_PERIOD_SUMMARY = "此值影响更新书架时请求的间隔时间。调大此值可能减小IP被屏蔽的几率,但更新时间也会变慢。需要重启软件以生效。\n当前值:%s" + private const val USE_MIRROR_URL_PREF = "useMirrorWebsitePreference" + private const val USE_MIRROR_URL_PREF_TITLE = "使用镜像网址" + private const val USE_MIRROR_URL_PREF_SUMMARY = "使用镜像网址。需要重启软件以生效。" // "Use mirror url. Defaults to main site" + private val PREF_ENTRIES_ARRAY = (1..10).map { i -> i.toString() }.toTypedArray() private val PERIOD_ENTRIES_ARRAY = (1..60).map { i -> i.toString() }.toTypedArray() + private val SITE_ENTRIES_ARRAY_DESCRIPTION = arrayOf( + "主站", "海外分流", + "中国大陆总站", "中国大陆分流1", "中国大陆分流2" + ) + private val SITE_ENTRIES_ARRAY_VALUE = (0..4).map { i -> i.toString() }.toTypedArray() + // List is based on http://jmcomic.xyz + // Please also update AndroidManifest + private val SITE_ENTRIES_ARRAY = arrayOf( + DEFAULT_SITE, "18comic.org", + "18comic.art", "18comic1.art", "18comic2.art" + ) } } diff --git a/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/JinmantiantangUrlActivity.kt b/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/JinmantiantangUrlActivity.kt new file mode 100644 index 000000000..f708309c7 --- /dev/null +++ b/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/JinmantiantangUrlActivity.kt @@ -0,0 +1,41 @@ +package eu.kanade.tachiyomi.extension.zh.jinmantiantang + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle +import android.util.Log +import kotlin.system.exitProcess + +/** + * Springboard that accepts https://www.manhuagui.com/comic/xxx intents and redirects them to + * the main tachiyomi process. The idea is to not install the intent filter unless + * you have this extension installed, but still let the main tachiyomi app control + * things. + */ +class JinmantiantangUrlActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val pathSegments = intent?.data?.pathSegments + if (pathSegments != null && pathSegments.size > 1) { + val titleid = pathSegments[1] + val mainIntent = Intent().apply { + action = "eu.kanade.tachiyomi.SEARCH" + putExtra("query", "${Jinmantiantang.PREFIX_ID_SEARCH}$titleid") + putExtra("filter", packageName) + } + + try { + startActivity(mainIntent) + } catch (e: ActivityNotFoundException) { + Log.e("JinmantiantangUrlActivity", e.toString()) + } + } else { + Log.e("JinmantiantangUrlActivity", "could not parse uri from intent $intent") + } + + finish() + exitProcess(0) + } +}