SinMH: update sources (#831)
| Before Width: | Height: | Size: 3.4 KiB | 
| Before Width: | Height: | Size: 1.8 KiB | 
| Before Width: | Height: | Size: 4.9 KiB | 
| Before Width: | Height: | Size: 9.3 KiB | 
| Before Width: | Height: | Size: 14 KiB | 
| @ -1,16 +0,0 @@ | ||||
| package eu.kanade.tachiyomi.extension.zh.imitui | ||||
| 
 | ||||
| import eu.kanade.tachiyomi.multisrc.sinmh.SinMH | ||||
| import eu.kanade.tachiyomi.source.model.Page | ||||
| import org.jsoup.nodes.Document | ||||
| 
 | ||||
| class Imitui : SinMH("爱米推漫画", "https://www.imitui.com") { | ||||
| 
 | ||||
|     override fun chapterListSelector() = ".chapter-body li > a:not([href^=/comic/app/])" | ||||
| 
 | ||||
|     override fun pageListParse(document: Document): List<Page> = | ||||
|         document.select("img[onclick]").mapIndexed { index, img -> | ||||
|             val url = img.attr("data-src").ifEmpty { img.attr("src") } | ||||
|             Page(index, imageUrl = url) | ||||
|         } | ||||
| } | ||||
| After Width: | Height: | Size: 4.5 KiB | 
| After Width: | Height: | Size: 2.3 KiB | 
| After Width: | Height: | Size: 6.9 KiB | 
| After Width: | Height: | Size: 13 KiB | 
| After Width: | Height: | Size: 21 KiB | 
							
								
								
									
										13
									
								
								multisrc/overrides/sinmh/jiuermanhua/src/JiuerManhua.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,13 @@ | ||||
| package eu.kanade.tachiyomi.extension.zh.jiuermanhua | ||||
| 
 | ||||
| import eu.kanade.tachiyomi.multisrc.sinmh.SinMH | ||||
| import eu.kanade.tachiyomi.network.GET | ||||
| import eu.kanade.tachiyomi.source.model.SChapter | ||||
| import org.jsoup.nodes.Document | ||||
| 
 | ||||
| class JiuerManhua : SinMH("92漫画", "http://www.92mh.com") { | ||||
| 
 | ||||
|     override fun mangaDetailsParse(document: Document) = mangaDetailsParseDMZJStyle(document, hasBreadcrumb = false) | ||||
| 
 | ||||
|     override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers) | ||||
| } | ||||
| @ -7,7 +7,7 @@ import org.jsoup.nodes.Document | ||||
| // This site blocks IP outside China | ||||
| class YKMH : SinMH("优酷漫画", "http://www.ykmh.com") { | ||||
|     override val id = 1637952806167036168 | ||||
|     override val mobileUrl = "http://wap.ykmh.com" | ||||
|     override val mobileUrl = "http://h5.ykmh.com" | ||||
| 
 | ||||
|     override fun mangaDetailsParse(document: Document) = mangaDetailsParseDMZJStyle(document, hasBreadcrumb = false) | ||||
| 
 | ||||
|  | ||||
| @ -9,7 +9,7 @@ import javax.crypto.Cipher | ||||
| import javax.crypto.spec.IvParameterSpec | ||||
| import javax.crypto.spec.SecretKeySpec | ||||
| 
 | ||||
| class Qinqin : SinMH("亲亲漫画", "https://www.acgud.com") { | ||||
| class Qinqin : SinMH("亲亲漫画", "http://www.acgwd.com") { | ||||
| 
 | ||||
|     override fun popularMangaRequest(page: Int) = GET("$baseUrl/list/post/?page=$page", headers) | ||||
| 
 | ||||
|  | ||||
| @ -1,3 +0,0 @@ | ||||
| dependencies { | ||||
|     implementation project(':lib:unpacker') | ||||
| } | ||||
| Before Width: | Height: | Size: 3.1 KiB | 
| Before Width: | Height: | Size: 1.6 KiB | 
| Before Width: | Height: | Size: 4.2 KiB | 
| Before Width: | Height: | Size: 7.7 KiB | 
| Before Width: | Height: | Size: 10 KiB | 
| @ -1,128 +0,0 @@ | ||||
| package eu.kanade.tachiyomi.extension.zh.wuqimanga | ||||
| 
 | ||||
| import eu.kanade.tachiyomi.lib.unpacker.Unpacker | ||||
| import eu.kanade.tachiyomi.multisrc.sinmh.SinMH | ||||
| import eu.kanade.tachiyomi.network.GET | ||||
| import eu.kanade.tachiyomi.source.model.Filter | ||||
| import eu.kanade.tachiyomi.source.model.FilterList | ||||
| import eu.kanade.tachiyomi.source.model.Page | ||||
| import eu.kanade.tachiyomi.source.model.SChapter | ||||
| import okhttp3.Request | ||||
| import org.jsoup.nodes.Document | ||||
| import org.jsoup.nodes.Element | ||||
| import org.jsoup.select.Evaluator | ||||
| 
 | ||||
| // Memo: the old implementation had a string preference with key "IMAGE_SERVER" | ||||
| // Updating the domain to www.wuqimh.com causes some requests to return 404 | ||||
| class WuqiManga : SinMH("57漫画", "http://www.wuqimh.net") { | ||||
| 
 | ||||
|     override val nextPageSelector = "span.pager > a:last-child" // in the last page it's a span | ||||
|     override val comicItemSelector = "#contList > li, .book-result > li" | ||||
|     override val comicItemTitleSelector = "p > a, dt > a" | ||||
| 
 | ||||
|     // 人气排序的漫画全是 404,所以就用默认的最新发布了 | ||||
|     override fun popularMangaRequest(page: Int) = GET("$baseUrl/list/order-id-p-$page", headers) | ||||
| 
 | ||||
|     override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/list/order-addtime-p-$page", headers) | ||||
| 
 | ||||
|     override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { | ||||
|         val isSearch = query.isNotEmpty() | ||||
|         val params = arrayListOf<String>() | ||||
|         if (isSearch) params.add(query) | ||||
|         filters.filterIsInstance<UriPartFilter>().mapTo(params) { it.toUriPart() } | ||||
|         params.add("p-") | ||||
|         val url = buildString(120) { | ||||
|             append(baseUrl) | ||||
|             append(if (isSearch) "/search/q_" else "/list/") | ||||
|             params.joinTo(this, separator = "-", postfix = page.toString()) | ||||
|         } | ||||
|         return GET(url, headers) | ||||
|     } | ||||
| 
 | ||||
|     override fun mangaDetailsParse(document: Document) = super.mangaDetailsParse(document).apply { | ||||
|         val comment = document.selectFirst(".book-title > h2")!!.text() | ||||
|         if (comment.isNotEmpty()) description = "$comment\n\n$description" | ||||
|     } | ||||
| 
 | ||||
|     override fun mangaDetailsParseDefaultGenre(document: Document, detailsList: Element): String = | ||||
|         document.selectFirst("div.crumb")!!.select("a[href^=/list/]") | ||||
|             .map { it.text().removeSuffix("年").removeSuffix("漫画") } | ||||
|             .filter { it.isNotEmpty() }.joinToString(", ") | ||||
| 
 | ||||
|     override fun chapterListSelector() = ".chapter-list li > a" | ||||
|     override fun List<SChapter>.sortedDescending() = this | ||||
|     override val dateSelector = ".cont-list dt:contains(更新于) + dd" | ||||
| 
 | ||||
|     override val imageHost: String by lazy { | ||||
|         client.newCall(GET("$mobileUrl/templates_pc/default/scripts/configs.js", headers)).execute().let { | ||||
|             Regex("""\['(.+?)']""").find(it.body.string())!!.groupValues[1].run { "http://$this" } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url, headers) | ||||
| 
 | ||||
|     override fun pageListParse(document: Document): List<Page> { | ||||
|         val script = document.selectFirst("body > script")!!.html() | ||||
|         val unpacked = Unpacker.unpack(script, ":[", "]") | ||||
|             .ifEmpty { return emptyList() } | ||||
|             .replace("\\", "") | ||||
|             .removeSurrounding("\"").split("\",\"") | ||||
|         val list = unpacked.filterNot { it.endsWith("/ManHuaKu/222.jpg") }.map { image -> | ||||
|             if (image.startsWith("http")) image else imageHost + image | ||||
|         } | ||||
|         if (list.isEmpty()) return emptyList() | ||||
|         client.newCall(GET(list[0], headers)).execute().apply { close() }.also { | ||||
|             if (!it.isSuccessful) throw Exception("该章节的图片加载出错:${it.code}") | ||||
|         } | ||||
|         return list.mapIndexed { i, imageUrl -> Page(i, imageUrl = imageUrl) } | ||||
|     } | ||||
| 
 | ||||
|     override fun parseCategories(document: Document) { | ||||
|         if (categories.isNotEmpty()) return | ||||
|         val labelSelector = Evaluator.Tag("label") | ||||
|         val linkSelector = Evaluator.Tag("a") | ||||
|         val filterMap = LinkedHashMap<String, LinkedHashMap<String, String>>(8) | ||||
|         document.select(Evaluator.Class("filter")).forEach { row -> | ||||
|             val tags = row.select(linkSelector) | ||||
|             if (tags.isEmpty()) return@forEach | ||||
|             val name = row.selectFirst(labelSelector)!!.text().removeSuffix(":") | ||||
|             if (!filterMap.containsKey(name)) { | ||||
|                 filterMap[name] = LinkedHashMap(tags.size * 2) | ||||
|             } | ||||
|             val tagMap = filterMap[name]!! | ||||
|             for (tag in tags) { | ||||
|                 val tagName = tag.text() | ||||
|                 if (!tagMap.containsKey(tagName)) { | ||||
|                     tagMap[tagName] = tag.attr("href").removePrefix("/list/").substringBeforeLast("-order-") | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         categories = filterMap.map { | ||||
|             val tagMap = it.value | ||||
|             Category(it.key, tagMap.keys.toTypedArray(), tagMap.values.toTypedArray()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun getFilterList(): FilterList { | ||||
|         val list: ArrayList<Filter<*>> | ||||
|         if (categories.isNotEmpty()) { | ||||
|             list = ArrayList(categories.size + 2) | ||||
|             with(list) { | ||||
|                 add(Filter.Header("使用文本搜索时,只有地区、年份、字母选项有效")) | ||||
|                 categories.forEach { add(it.toUriPartFilter()) } | ||||
|             } | ||||
|         } else { | ||||
|             list = ArrayList(4) | ||||
|             with(list) { | ||||
|                 add(Filter.Header("点击“重置”即可刷新分类,如果失败,")) | ||||
|                 add(Filter.Header("请尝试重新从图源列表点击进入图源")) | ||||
|                 add(Filter.Header("使用文本搜索时,只有地区、年份、字母选项有效")) | ||||
|             } | ||||
|         } | ||||
|         list.add(UriPartFilter("排序方式", sortNames, sortKeys)) | ||||
|         return FilterList(list) | ||||
|     } | ||||
| 
 | ||||
|     private val sortNames = arrayOf("最新发布", "最近更新", "人气最旺", "评分最高") | ||||
|     private val sortKeys = arrayOf("order-id", "order-addtime", "order-hits", "order-gold") | ||||
| } | ||||
| @ -37,6 +37,7 @@ abstract class SinMH( | ||||
|     override val client = network.client.newBuilder().rateLimit(2).build() | ||||
| 
 | ||||
|     override fun headersBuilder(): Headers.Builder = Headers.Builder() | ||||
|         .add("User-Agent", System.getProperty("http.agent")!!) | ||||
|         .add("Referer", baseUrl) | ||||
| 
 | ||||
|     protected open val nextPageSelector = "ul.pagination > li.next:not(.disabled)" | ||||
| @ -100,6 +101,8 @@ abstract class SinMH( | ||||
| 
 | ||||
|     // Details | ||||
| 
 | ||||
|     override fun getMangaUrl(manga: SManga) = mobileUrl + manga.url | ||||
| 
 | ||||
|     override fun mangaDetailsParse(document: Document) = SManga.create().apply { | ||||
|         title = document.selectFirst(".book-title > h1")!!.text() | ||||
|         val detailsList = document.selectFirst(Evaluator.Class("detail-list"))!! | ||||
| @ -130,13 +133,13 @@ abstract class SinMH( | ||||
|         title = detailsDiv.selectFirst(Evaluator.Tag("h1"))!!.text() | ||||
|         val details = detailsDiv.select("> ul > li") | ||||
|         val linkSelector = Evaluator.Tag("a") | ||||
|         author = details[0].selectFirst(linkSelector)!!.text() | ||||
|         author = details[0].text().removePrefix("作者:").trimStart() | ||||
|         status = when (details[1].selectFirst(linkSelector)!!.text()) { | ||||
|             "连载中" -> SManga.ONGOING | ||||
|             "已完结" -> SManga.COMPLETED | ||||
|             else -> SManga.UNKNOWN | ||||
|         } | ||||
|         genre = mutableListOf<Element>().apply { | ||||
|         genre = buildList { | ||||
|             add(details[2].selectFirst(linkSelector)!!) // 类别 | ||||
|             addAll(details[3].select(linkSelector)) // 类型 | ||||
|             if (hasBreadcrumb) addAll(document.selectFirst("div.mianbao")!!.select("a[href^=/list/]")) | ||||
| @ -175,19 +178,21 @@ abstract class SinMH( | ||||
|     /** 必须是 "section item" */ | ||||
|     override fun chapterListSelector() = ".chapter-body li > a" | ||||
|     override fun chapterFromElement(element: Element) = SChapter.create().apply { | ||||
|         runCatching { setUrlWithoutDomain(element.attr("href")) }.onFailure { url = "" } | ||||
|         setUrlWithoutDomain(element.attr("href")) | ||||
|         val children = element.children() | ||||
|         name = if (children.isEmpty()) element.text() else children[0].text() | ||||
|     } | ||||
| 
 | ||||
|     // Pages | ||||
| 
 | ||||
|     override fun getChapterUrl(chapter: SChapter) = mobileUrl + chapter.url | ||||
| 
 | ||||
|     override fun pageListRequest(chapter: SChapter) = GET(mobileUrl + chapter.url, headers) | ||||
| 
 | ||||
|     protected open val imageHost: String by lazy { | ||||
|         client.newCall(GET("$baseUrl/js/config.js", headers)).execute().let { | ||||
|             Regex("""resHost:.+?"?domain"?:\["(.+?)"""").find(it.body.string())!! | ||||
|                 .groupValues[1].substringAfter(':').run { "https:$this" } | ||||
|                 .groupValues[1] | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,7 @@ import generator.ThemeSourceGenerator | ||||
| class SinMHGenerator : ThemeSourceGenerator { | ||||
|     override val themeClass = "SinMH" | ||||
|     override val themePkg = "sinmh" | ||||
|     override val baseVersionCode = 10 | ||||
|     override val baseVersionCode = 11 | ||||
|     override val sources = listOf( | ||||
|         SingleLang( | ||||
|             name = "Gufeng Manhua", | ||||
| @ -16,18 +16,11 @@ class SinMHGenerator : ThemeSourceGenerator { | ||||
|             sourceName = "古风漫画网", | ||||
|             overrideVersionCode = 6, | ||||
|         ), | ||||
|         SingleLang( | ||||
|             name = "Imitui Manhua", | ||||
|             baseUrl = "https://www.imitui.com", | ||||
|             lang = "zh", | ||||
|             className = "Imitui", | ||||
|             sourceName = "爱米推漫画", | ||||
|             overrideVersionCode = 3, | ||||
|         ), | ||||
|         SingleLang( // This site blocks IP outside China | ||||
|             name = "YKMH", | ||||
|             baseUrl = "http://www.ykmh.com", | ||||
|             lang = "zh", | ||||
|             isNsfw = true, | ||||
|             className = "YKMH", | ||||
|             pkgName = "manhuadui", | ||||
|             sourceName = "优酷漫画", | ||||
| @ -35,19 +28,20 @@ class SinMHGenerator : ThemeSourceGenerator { | ||||
|         ), | ||||
|         SingleLang( | ||||
|             name = "Qinqin Manhua", | ||||
|             baseUrl = "https://www.acgud.com", | ||||
|             baseUrl = "http://www.acgwd.com", | ||||
|             lang = "zh", | ||||
|             className = "Qinqin", | ||||
|             sourceName = "亲亲漫画", | ||||
|             overrideVersionCode = 2, | ||||
|         ), | ||||
|         SingleLang( | ||||
|             name = "57Manhua", | ||||
|             baseUrl = "http://www.wuqimh.net", | ||||
|             name = "92Manhua", | ||||
|             baseUrl = "http://www.92mh.com", | ||||
|             lang = "zh", | ||||
|             className = "WuqiManga", | ||||
|             sourceName = "57漫画", | ||||
|             overrideVersionCode = 5, | ||||
|             isNsfw = true, | ||||
|             className = "JiuerManhua", | ||||
|             sourceName = "92漫画", | ||||
|             overrideVersionCode = 0, | ||||
|         ), | ||||
|     ) | ||||
| 
 | ||||
|  | ||||
 stevenyomi
						stevenyomi