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:
vulpes310 2021-06-14 04:16:46 -07:00 committed by GitHub
parent 99c1871901
commit 5a6f7b45d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 164 additions and 36 deletions

View File

@ -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>

View File

@ -5,7 +5,7 @@ ext {
extName = 'Jinmantiantang'
pkgNameSuffix = 'zh.jinmantiantang'
extClass = '.Jinmantiantang'
extVersionCode = 12
extVersionCode = 13
libVersion = '1.2'
containsNsfw = true
}

View File

@ -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"
)
}
}

View File

@ -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)
}
}