修复包子漫画导航解析

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 okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
/**
* Interceptor to set custom useragent for MangaDex

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.extension.en.collectedcurios
import android.util.Log
import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page

View File

@ -97,12 +97,16 @@ class MangaClub : ParsedHttpSource() {
title = document.select("div.info strong").text().replace("\\'", "'").substringBefore("/").trim()
author = document.select("div.info a[href*=author]").joinToString(", ") { it.text().trim() }
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.LICENSED
} else {
when (document.select("div.info a[href*=status_translation]").text().trim()) {
"Продолжается" -> SManga.ONGOING
"Завершен" -> SManga.COMPLETED
"Заморожено/Заброшено" -> SManga.ON_HIATUS
else -> SManga.UNKNOWN
}
}
description = document.select(".description").first()!!.text()
genre = document.select("div.info a[href*=tags]").joinToString(", ") {

View File

@ -108,7 +108,10 @@ class Baimangu : ConfigurableSource, ParsedHttpSource() {
// Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
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 {
var channelValue = "4" // 漫画大全
var sortValue = "time" // 按时间
@ -128,7 +131,8 @@ class Baimangu : ConfigurableSource, ParsedHttpSource() {
// https://www.darpou.com/vodshow/2-----------.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)
}

View File

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

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.extension.zh.baozimhorg
import android.app.Application
import android.util.Log
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.network.GET
@ -17,24 +18,28 @@ import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.select.Evaluator
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.Locale
// Uses WPManga + GeneratePress/Blocksy Child
class BaozimhOrg : HttpSource(), ConfigurableSource {
override val name get() = "包子漫画导航"
override val name get() = "包子漫画导航 by 芋头"
override val lang get() = "zh"
override val supportsLatest get() = true
override val baseUrl: String
private val baseHttpUrl: HttpUrl
private val enableGenres: Boolean
private val TAG = "包子"
init {
val mirrors = MIRRORS
@ -50,24 +55,28 @@ class BaozimhOrg : HttpSource(), ConfigurableSource {
.build()
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 toIndex = if (pathSegments.last().isEmpty()) pathSegments.size - 1 else pathSegments.size
val toIndex =
if (pathSegments.last().isEmpty()) pathSegments.size - 1 else pathSegments.size
val list = pathSegments.subList(fromIndex, toIndex).toMutableList()
list[0] = list[0].split("-").take(2).joinToString("-")
return list.joinToString("/")
return list.joinToString("/")*/
return link.split("manga/")[1]
}
override fun popularMangaRequest(page: Int) = GET("$baseUrl/hots/page/$page/", headers)
override fun popularMangaParse(response: Response): MangasPage {
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 {
val link = element.selectFirst(Evaluator.Tag("h2"))!!.child(0)
url = getKey(link.attr("href"))
title = link.ownText()
thumbnail_url = element.selectFirst(Evaluator.Tag("img"))!!.imgSrc
// val link = element.selectFirst(Evaluator.Tag("h3"))!!.child(0)
url = element.attr("href").split("/manga/")[1]
Log.i("包子", "热门链接:$url ")
title = element.select("h3").text()
thumbnail_url = element.select("img").attr("src")
}
}
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 {
if (query.isNotEmpty()) {
val url = "$baseUrl/page/$page/".toHttpUrl().newBuilder()
.addQueryParameter("s", query)
val url = "$baseUrl/s/$query".toHttpUrl().newBuilder()
.addQueryParameter("page", page.toString())
Log.i("包子", "搜索链接:$url ")
return Request.Builder().url(url.build()).headers(headers).build()
}
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)
}
@ -101,36 +116,52 @@ class BaozimhOrg : HttpSource(), ConfigurableSource {
override fun mangaDetailsParse(response: Response) = SManga.create().apply {
val document = response.asJsoup()
title = document.selectFirst(Evaluator.Tag("h1"))!!.ownText()
author = document.selectFirst(Evaluator.Class("author-content"))!!.children().joinToString { it.ownText() }
description = document.selectFirst(".descrip_manga_info, .wp-block-stackable-text")!!.text()
thumbnail_url = document.selectFirst("img.wp-post-image")!!.imgSrc
Log.i(TAG, "mangaDetailsParse: " + response.request.url)
title = document.select("#info").select("h1")[0].childNode(0).toString()
author = document.select(".text-small").select(".py-1")
.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"))!!
.children().eachText().toMutableSet()
if ("连载中" in genreList) {
genreList.remove("连载中")
val _status = document.select("#info").select("h1")[0].childNode(1).childNode(0).toString()
Log.i(TAG, "mangaDetailsParse: $_status")
if ("連載中" in _status) {
status = SManga.ONGOING
} else if ("已完结" in genreList) {
genreList.remove("已完结")
} else {
status = SManga.COMPLETED
}
genre = genreList.joinToString()
}
override fun chapterListRequest(manga: SManga): Request {
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)
return GET("$baseUrl/chapterlist/$url/", headers)
}
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
return document.selectFirst(Evaluator.Class("version-chaps"))!!.children().map {
var document = response.asJsoup()
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 {
url = getKey(it.attr("href"))
name = it.ownText()
date_upload = parseChapterDate(it.child(0).text())
name = it.attr("data-ct")
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> {
val document = response.asJsoup()
var document = response.asJsoup()
// Jsoup won't ignore duplicates inside <noscript> tag
document.select(Evaluator.Tag("noscript")).remove()
return document.select("img[decoding=async]").mapIndexed { index, element ->
Log.i(TAG, "pageListParse: 解析漫画地址:${document.baseUri()}")
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)
}
}
@ -156,14 +193,31 @@ class BaozimhOrg : HttpSource(), ConfigurableSource {
private fun parseGenres(document: Document) {
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()
genres = buildList(items.size + 1) {
add(Pair("全部", "/allmanga/"))
add(Pair("全部", "/manga/"))
items.mapTo(this) {
val link = it.child(0)
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()
}
@ -208,9 +262,52 @@ class BaozimhOrg : HttpSource(), ConfigurableSource {
private val dateFormat by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) }
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) {
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
import android.util.Log
import okhttp3.Interceptor
import okhttp3.Response
@ -16,10 +17,12 @@ object UrlInterceptor : Interceptor {
}
val mangaUrl = "/manga/$slug/"
Log.i("包子", "mangaUrl = $mangaUrl url = $url")
val headRequest = request.newBuilder()
.head()
.url(url.resolve(mangaUrl)!!)
.build()
Log.i("包子", "不知道是哪的链接:${headRequest.url} ")
// might redirect multiple times
val headResponse = chain.proceed(headRequest)
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.api.get
import java.net.URLEncoder
import java.nio.charset.Charset
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale