Fix Jingmantiantang's ownText is null and add deeplink support (#7623)
* Fix Jingmantiantang's ownText is null and add deeplink support [Fix] Jingmantiantang's ownText is null during chapter loading The old implementation is to use chapterArea to store wheather or not the Manga requested is only single page or multi-chapter. This, however, creates a big issue as the chapterArea can be changed to multi-chapter in preveious Manga, and that information is carried over to other Manga which might be single chapter. The new implementation remove global value chapterArea and it helper function determineChapterInfo. Instead, the single-multi check is performed in chapterListParse, which only happen once per manga update request, vs three times call of determineChapterInfo. [Fix] Change Author's CSS selector and enable multi-author listing [Fix] Default manga status to Completed As Jingmantiantang often do not mark manga as complete, and there is a high chance that manga user requested in single page. Thus defaulting to completed, unless marked as ongoing. [FEAT] Enable BaseUrl switch As mentioned in (tachiyomiorg#5363) and (tachiyomiorg#4908). There is a frquent PR for mirror site change. Unable user choice to stick with main site or mirror site should please everyone [FIX] Record mainSiteRateLimitPeriodPreference to correct key. [FIX] Change one tag to the correct name [FEAT] Enable deeplink support * Fix mirror site relase page link, change ID prefix to JM
This commit is contained in:
parent
99c1871901
commit
5a6f7b45d8
@ -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>
|
@ -5,7 +5,7 @@ ext {
|
||||
extName = 'Jinmantiantang'
|
||||
pkgNameSuffix = 'zh.jinmantiantang'
|
||||
extClass = '.Jinmantiantang'
|
||||
extVersionCode = 12
|
||||
extVersionCode = 13
|
||||
libVersion = '1.2'
|
||||
containsNsfw = true
|
||||
}
|
||||
|
@ -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/]"
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user