修复包子漫画导航解析

This commit is contained in:
Yutou 2024-05-22 10:05:59 +08:00
parent 01fa3eade2
commit 64432f50ba
9 changed files with 155 additions and 50 deletions

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.extension.all.mangadex
import android.content.SharedPreferences import android.content.SharedPreferences
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
import java.io.IOException
/** /**
* Interceptor to set custom useragent for MangaDex * Interceptor to set custom useragent for MangaDex

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.extension.en.collectedcurios package eu.kanade.tachiyomi.extension.en.collectedcurios
import android.util.Log
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
@ -58,7 +57,7 @@ class Collectedcurios : ParsedHttpSource() {
description = "Spider and Scorpion webcomic." description = "Spider and Scorpion webcomic."
thumbnail_url = "https://www.collectedcurios.com/images/CC_2011_Spider_And_Scorpion_Button.jpg" thumbnail_url = "https://www.collectedcurios.com/images/CC_2011_Spider_And_Scorpion_Button.jpg"
}, },
*/ */
), ),
false, false,
), ),
@ -113,7 +112,7 @@ class Collectedcurios : ParsedHttpSource() {
override fun imageUrlParse(response: Response): String { override fun imageUrlParse(response: Response): String {
val url = response.request.url.toString() val url = response.request.url.toString()
val document = response.asJsoup() val document = response.asJsoup()
return when { return when {
url.contains("sequentialart") -> url.contains("sequentialart") ->
document.selectFirst(".w3-image")!!.absUrl("src") document.selectFirst(".w3-image")!!.absUrl("src")

View File

@ -68,7 +68,7 @@ class Twi4 : HttpSource() {
author = manga.select("div.hgroup > p").text() author = manga.select("div.hgroup > p").text()
status = status =
if (manga.select("ul:first-child > li:last-child > em.is-completed") if (manga.select("ul:first-child > li:last-child > em.is-completed")
.isEmpty() .isEmpty()
) { ) {
SManga.ONGOING SManga.ONGOING
} else { } else {

View File

@ -97,11 +97,15 @@ class MangaClub : ParsedHttpSource() {
title = document.select("div.info strong").text().replace("\\'", "'").substringBefore("/").trim() title = document.select("div.info strong").text().replace("\\'", "'").substringBefore("/").trim()
author = document.select("div.info a[href*=author]").joinToString(", ") { it.text().trim() } author = document.select("div.info a[href*=author]").joinToString(", ") { it.text().trim() }
artist = author artist = author
status = if (document.select("div.fullstory").text().contains("Данное произведение лицензировано на территории РФ. Главы удалены.")) SManga.LICENSED else when (document.select("div.info a[href*=status_translation]").text().trim()) { status = if (document.select("div.fullstory").text().contains("Данное произведение лицензировано на территории РФ. Главы удалены.")) {
"Продолжается" -> SManga.ONGOING SManga.LICENSED
"Завершен" -> SManga.COMPLETED } else {
"Заморожено/Заброшено" -> SManga.ON_HIATUS when (document.select("div.info a[href*=status_translation]").text().trim()) {
else -> SManga.UNKNOWN "Продолжается" -> SManga.ONGOING
"Завершен" -> SManga.COMPLETED
"Заморожено/Заброшено" -> SManga.ON_HIATUS
else -> SManga.UNKNOWN
}
} }
description = document.select(".description").first()!!.text() description = document.select(".description").first()!!.text()

View File

@ -108,7 +108,10 @@ class Baimangu : ConfigurableSource, ParsedHttpSource() {
// Search // Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
return if (query.isNotBlank()) { return if (query.isNotBlank()) {
GET("$baseUrl/vodsearch/$query----------$page---", headers) // GET("$baseUrl/vodsearch/$query----------$page---", headers)
// https://www.darpou.com/cccsearch/-------.html?wd=%E8%BD%AC%E7%94%9F%E8%B4%B5%E6%97%8F
// https://www.darpou.com/cccsearch/------.html?wd=%E8%BD%AC%E7%94%9F%E8%B4%B5%E6%97%8F
GET("$baseUrl/cccsearch/-------.html?wd=$query", headers)
} else { } else {
var channelValue = "4" // 漫画大全 var channelValue = "4" // 漫画大全
var sortValue = "time" // 按时间 var sortValue = "time" // 按时间
@ -128,7 +131,8 @@ class Baimangu : ConfigurableSource, ParsedHttpSource() {
// https://www.darpou.com/vodshow/2-----------.html // https://www.darpou.com/vodshow/2-----------.html
// https://www.darpou.com/vodshow/2--hits------3---.html // https://www.darpou.com/vodshow/2--hits------3---.html
val url = "$baseUrl/vodshow/$channelValue--$sortValue------$page---" // val url = "$baseUrl/vodshow/$channelValue--$sortValue------$page---"
val url = "$baseUrl/vodshow/$channelValue--$sortValue"
GET(url, headers) GET(url, headers)
} }

View File

@ -5,7 +5,7 @@ ext {
extName = 'Baozimh.org' extName = 'Baozimh.org'
pkgNameSuffix = 'zh.baozimhorg' pkgNameSuffix = 'zh.baozimhorg'
extClass = '.BaozimhOrg' extClass = '.BaozimhOrg'
extVersionCode = 28 extVersionCode = 29
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.extension.zh.baozimhorg package eu.kanade.tachiyomi.extension.zh.baozimhorg
import android.app.Application import android.app.Application
import android.util.Log
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
@ -17,24 +18,28 @@ import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.jsoup.select.Evaluator import org.jsoup.select.Evaluator
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale import java.util.Locale
// Uses WPManga + GeneratePress/Blocksy Child // Uses WPManga + GeneratePress/Blocksy Child
class BaozimhOrg : HttpSource(), ConfigurableSource { class BaozimhOrg : HttpSource(), ConfigurableSource {
override val name get() = "包子漫画导航" override val name get() = "包子漫画导航 by 芋头"
override val lang get() = "zh" override val lang get() = "zh"
override val supportsLatest get() = true override val supportsLatest get() = true
override val baseUrl: String override val baseUrl: String
private val baseHttpUrl: HttpUrl private val baseHttpUrl: HttpUrl
private val enableGenres: Boolean private val enableGenres: Boolean
private val TAG = "包子"
init { init {
val mirrors = MIRRORS val mirrors = MIRRORS
@ -50,24 +55,28 @@ class BaozimhOrg : HttpSource(), ConfigurableSource {
.build() .build()
private fun getKey(link: String): String { private fun getKey(link: String): String {
val pathSegments = baseHttpUrl.resolve(link)!!.pathSegments /* val pathSegments = baseHttpUrl.resolve(link)!!.pathSegments
val fromIndex = if (pathSegments[0] == "manga") 1 else 0 val fromIndex = if (pathSegments[0] == "manga") 1 else 0
val toIndex = if (pathSegments.last().isEmpty()) pathSegments.size - 1 else pathSegments.size val toIndex =
val list = pathSegments.subList(fromIndex, toIndex).toMutableList() if (pathSegments.last().isEmpty()) pathSegments.size - 1 else pathSegments.size
list[0] = list[0].split("-").take(2).joinToString("-") val list = pathSegments.subList(fromIndex, toIndex).toMutableList()
return list.joinToString("/") list[0] = list[0].split("-").take(2).joinToString("-")
return list.joinToString("/")*/
return link.split("manga/")[1]
} }
override fun popularMangaRequest(page: Int) = GET("$baseUrl/hots/page/$page/", headers) override fun popularMangaRequest(page: Int) = GET("$baseUrl/hots/page/$page/", headers)
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val document = response.asJsoup().also(::parseGenres) val document = response.asJsoup().also(::parseGenres)
val mangas = document.select("article.wp-manga").map { element -> Log.i(TAG, "popularMangaParse: 3")
val mangas = document.select(".cardlist a").map { element ->
SManga.create().apply { SManga.create().apply {
val link = element.selectFirst(Evaluator.Tag("h2"))!!.child(0) // val link = element.selectFirst(Evaluator.Tag("h3"))!!.child(0)
url = getKey(link.attr("href")) url = element.attr("href").split("/manga/")[1]
title = link.ownText() Log.i("包子", "热门链接:$url ")
thumbnail_url = element.selectFirst(Evaluator.Tag("img"))!!.imgSrc title = element.select("h3").text()
thumbnail_url = element.select("img").attr("src")
} }
} }
val hasNextPage = document.selectFirst(Evaluator.Class("next"))?.tagName() == "a" || val hasNextPage = document.selectFirst(Evaluator.Class("next"))?.tagName() == "a" ||
@ -81,12 +90,18 @@ class BaozimhOrg : HttpSource(), ConfigurableSource {
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.isNotEmpty()) { if (query.isNotEmpty()) {
val url = "$baseUrl/page/$page/".toHttpUrl().newBuilder() val url = "$baseUrl/s/$query".toHttpUrl().newBuilder()
.addQueryParameter("s", query) .addQueryParameter("page", page.toString())
Log.i("包子", "搜索链接:$url ")
return Request.Builder().url(url.build()).headers(headers).build() return Request.Builder().url(url.build()).headers(headers).build()
} }
for (filter in filters) { for (filter in filters) {
if (filter is UriPartFilter) return GET(baseUrl + filter.toUriPart() + "page/$page/", headers) if (filter is UriPartFilter) {
return GET(
baseUrl + filter.toUriPart() + "page/$page/",
headers,
)
}
} }
return popularMangaRequest(page) return popularMangaRequest(page)
} }
@ -101,36 +116,52 @@ class BaozimhOrg : HttpSource(), ConfigurableSource {
override fun mangaDetailsParse(response: Response) = SManga.create().apply { override fun mangaDetailsParse(response: Response) = SManga.create().apply {
val document = response.asJsoup() val document = response.asJsoup()
title = document.selectFirst(Evaluator.Tag("h1"))!!.ownText() Log.i(TAG, "mangaDetailsParse: " + response.request.url)
author = document.selectFirst(Evaluator.Class("author-content"))!!.children().joinToString { it.ownText() } title = document.select("#info").select("h1")[0].childNode(0).toString()
description = document.selectFirst(".descrip_manga_info, .wp-block-stackable-text")!!.text() author = document.select(".text-small").select(".py-1")
thumbnail_url = document.selectFirst("img.wp-post-image")!!.imgSrc .select(".pb-2")[0].select("span")[1].text()
description = document.select("#info").select("p")[0].childNode(0).toString()
thumbnail_url = document.select("#MangaCard").select("img").attr("src")
val genreList = document.selectFirst(Evaluator.Class("genres-content"))!! val _status = document.select("#info").select("h1")[0].childNode(1).childNode(0).toString()
.children().eachText().toMutableSet() Log.i(TAG, "mangaDetailsParse: $_status")
if ("连载中" in genreList) { if ("連載中" in _status) {
genreList.remove("连载中")
status = SManga.ONGOING status = SManga.ONGOING
} else if ("已完结" in genreList) { } else {
genreList.remove("已完结")
status = SManga.COMPLETED status = SManga.COMPLETED
} }
genre = genreList.joinToString()
} }
override fun chapterListRequest(manga: SManga): Request { override fun chapterListRequest(manga: SManga): Request {
val url = manga.url val url = manga.url
Log.i(
TAG,
"${manga.title} | ${manga.author} | ${manga.genre} | ${manga.description} | ${manga.artist} | ${manga.update_strategy}",
)
if (url[0] == '/') throw Exception(MIGRATE) if (url[0] == '/') throw Exception(MIGRATE)
return GET("$baseUrl/chapterlist/$url/", headers) return GET("$baseUrl/chapterlist/$url/", headers)
} }
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup() var document = response.asJsoup()
return document.selectFirst(Evaluator.Class("version-chaps"))!!.children().map { val allchapters = document.getElementById("allchapters")
val host = allchapters?.attr("data-host")
val mid = allchapters?.attr("data-mid")
document = Jsoup.connect("$host/manga/get?mid=$mid&mode=all").get()
Log.i(TAG, "章节html ${document.text()}")
Log.i(TAG, "chapterListParse: $host | $mid | ${document.select("a").size}")
return document.select("a").map {
SChapter.create().apply { SChapter.create().apply {
url = getKey(it.attr("href")) url = getKey(it.attr("href"))
name = it.ownText() name = it.attr("data-ct")
date_upload = parseChapterDate(it.child(0).text()) date_upload = parseChapterDate(it.select("span")[1].text())
Log.i(
TAG,
"chapterListParse: 时间 $date_upload | ${
it.selectFirst("a")!!.select("span")[1].text()
} | key url = $url | srcUrl = ${it.attr("href")}",
)
} }
} }
} }
@ -142,10 +173,16 @@ class BaozimhOrg : HttpSource(), ConfigurableSource {
} }
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val document = response.asJsoup() var document = response.asJsoup()
// Jsoup won't ignore duplicates inside <noscript> tag // Jsoup won't ignore duplicates inside <noscript> tag
document.select(Evaluator.Tag("noscript")).remove() Log.i(TAG, "pageListParse: 解析漫画地址:${document.baseUri()}")
return document.select("img[decoding=async]").mapIndexed { index, element -> val chapterContent = document.getElementById("chapterContent")
val host = chapterContent?.attr("data-host")
val mid = chapterContent?.attr("data-ms")
val cs = chapterContent?.attr("data-cs")
document = Jsoup.connect("$host/chapter/getcontent?m=$mid&c=$cs").get()
return document.select("img[data-sizes=auto]").mapIndexed { index, element ->
Log.i(TAG, "pageListParse: 漫画地址 ${element.imgSrc}")
Page(index, imageUrl = element.imgSrc) Page(index, imageUrl = element.imgSrc)
} }
} }
@ -156,14 +193,31 @@ class BaozimhOrg : HttpSource(), ConfigurableSource {
private fun parseGenres(document: Document) { private fun parseGenres(document: Document) {
if (!enableGenres || genres.isNotEmpty()) return if (!enableGenres || genres.isNotEmpty()) return
val box = document.selectFirst(Evaluator.Class("wp-block-navigation__container")) ?: return /*val box = document.selectFirst(Evaluator.Class("container")) ?: return
Log.i(TAG, "container size ${box.childrenSize()}")
val items = box.children() val items = box.children()
genres = buildList(items.size + 1) { genres = buildList(items.size + 1) {
add(Pair("全部", "/allmanga/")) add(Pair("全部", "/manga/"))
items.mapTo(this) { items.mapTo(this) {
val link = it.child(0) val link = it.child(0)
Pair(link.text(), link.attr("href")) Pair(link.text(), link.attr("href"))
} }
}.toTypedArray()*/
val items = document.select("#info").select(".py-1").select(".bg-default-200")
Log.i(
TAG,
"parseGenres: info = ${document.select("#info").size} | ${
document.select("#info").select(".py-1").size
} | ${items.size} ",
)
genres = buildList(items.size) {
add(Pair("全部", "/manga/"))
Log.i(TAG, "parseGenres: 分类数量 ${items.size}")
items.mapTo(this) {
val link = it.parent()!!.attr("href")
Log.i(TAG, "分类 ${it.text()} | $link")
Pair(it.text(), link)
}
}.toTypedArray() }.toTypedArray()
} }
@ -208,9 +262,52 @@ class BaozimhOrg : HttpSource(), ConfigurableSource {
private val dateFormat by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) } private val dateFormat by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) }
fun parseChapterDate(text: String): Long = try { fun parseChapterDate(text: String): Long = try {
dateFormat.parse(text)!!.time if (text.contains("天前")) {
convertDaysAgoToTimestamp(text.trim().replace("天前", "").toInt())
} else if (text.contains("小时前")) {
convertHourToTimestamp(text.trim().replace("小时前", "").toInt())
} else {
// dateFormat.parse(text)!!.time
convertDefTimerToTimestamp(text)
}
} catch (_: Throwable) { } catch (_: Throwable) {
0 0
} }
private fun convertDaysAgoToTimestamp(daysAgo: Int): Long {
val now = Calendar.getInstance()
now.time = Date()
now.add(Calendar.DATE, -daysAgo)
return now.time.time
}
private fun convertHourToTimestamp(hour: Int): Long {
val now = Calendar.getInstance()
now.time = Date()
now.add(Calendar.HOUR, -hour)
return now.time.time
}
private fun convertDefTimerToTimestamp(dateString: String): Long {
val dateFormat = SimpleDateFormat("MMM dd", Locale.ENGLISH)
dateFormat.isLenient = false
try {
val date = dateFormat.parse(dateString)
// 由于没有提供年份,我们设置默认年份
val calendar = Calendar.getInstance()
calendar.setTime(date!!)
calendar.set(
Calendar.YEAR,
Calendar.getInstance().get(Calendar.YEAR),
) // 设置默认年份比如2023
// 转换为时间戳
return calendar.getTimeInMillis()
} catch (e: Exception) {
Log.e("包子", "convertDefTimerToTimestamp: ", e)
return 0
}
}
} }
} }

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.extension.zh.baozimhorg package eu.kanade.tachiyomi.extension.zh.baozimhorg
import android.util.Log
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
@ -16,10 +17,12 @@ object UrlInterceptor : Interceptor {
} }
val mangaUrl = "/manga/$slug/" val mangaUrl = "/manga/$slug/"
Log.i("包子", "mangaUrl = $mangaUrl url = $url")
val headRequest = request.newBuilder() val headRequest = request.newBuilder()
.head() .head()
.url(url.resolve(mangaUrl)!!) .url(url.resolve(mangaUrl)!!)
.build() .build()
Log.i("包子", "不知道是哪的链接:${headRequest.url} ")
// might redirect multiple times // might redirect multiple times
val headResponse = chain.proceed(headRequest) val headResponse = chain.proceed(headRequest)
if (headResponse.priorResponse == null) return chain.proceed(request) if (headResponse.priorResponse == null) return chain.proceed(request)

View File

@ -30,7 +30,6 @@ import org.json.JSONObject
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.net.URLEncoder import java.net.URLEncoder
import java.nio.charset.Charset
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale