[RU]UniComics Events & Filters parse (break id/url in library) (#11781)
* [RU]UniComics filters * filter list * extVersionCode * no blank search * baseDefaultUrl * simple CheckboxCaptcha * Events parse/filters (break id/url in library) * search fixes * only * toUpperCase * fix/change chapter naming * chapter_number substringAfterLast * fix open WebView
This commit is contained in:
parent
93054925ae
commit
51b2cc75b5
|
@ -6,7 +6,7 @@ ext {
|
|||
extName = 'UniComics'
|
||||
pkgNameSuffix = 'ru.unicomics'
|
||||
extClass = '.UniComics'
|
||||
extVersionCode = 2
|
||||
extVersionCode = 3
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.extension.ru.unicomics
|
|||
import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
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
|
||||
|
@ -11,6 +12,7 @@ import eu.kanade.tachiyomi.source.model.SManga
|
|||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
@ -46,11 +48,36 @@ class UniComics : ParsedHttpSource() {
|
|||
override fun latestUpdatesRequest(page: Int): Request =
|
||||
GET("$baseDefaultUrl/comics/online/page/$page", headers)
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
|
||||
GET("https://yandex.ru/search/site/?frame=1&lr=172&searchid=1959358&topdoc=xdm_e=$baseDefaultUrl&xdm_c=default5044&xdm_p=1&v=2.0&web=0&text=$query&p=$page", headers)
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
|
||||
when (filter) {
|
||||
is getEventsList -> {
|
||||
if (filter.state > 0) {
|
||||
return GET("$baseDefaultUrl$PATH_events", headers)
|
||||
}
|
||||
}
|
||||
is Publishers -> {
|
||||
if (filter.state > 0) {
|
||||
val publisherName = getPublishersList()[filter.state].url
|
||||
val publisherUrl =
|
||||
"$baseDefaultUrl$PATH_publishers/$publisherName/page/$page".toHttpUrlOrNull()!!
|
||||
.newBuilder()
|
||||
return GET(publisherUrl.toString(), headers)
|
||||
}
|
||||
}
|
||||
else -> return@forEach
|
||||
}
|
||||
}
|
||||
if (query.isNotEmpty()) {
|
||||
return GET(
|
||||
"https://yandex.ru/search/site/?frame=1&lr=172&searchid=1959358&topdoc=xdm_e=$baseDefaultUrl&xdm_c=default5044&xdm_p=1&v=2.0&web=0&text=$query&p=$page",
|
||||
headers
|
||||
)
|
||||
}
|
||||
return popularMangaRequest(page)
|
||||
}
|
||||
override fun searchMangaSelector() =
|
||||
".b-serp-item__content:has(.b-serp-url__item:contains(/comics/):not(:contains(/comics/events)):not(:contains(/comics/publishers)):not(:contains(/page/))):has(.b-serp-item__title-link:not(:contains(Комиксы читать онлайн бесплатно)))"
|
||||
".b-serp-item__content:has(.b-serp-url__item:contains(/comics/):not(:contains($PATH_events)):not(:contains($PATH_publishers)):not(:contains(/page/))):has(.b-serp-item__title-link:not(:contains(Комиксы читать онлайн бесплатно)))"
|
||||
|
||||
override fun searchMangaNextPageSelector() = ".b-pager__next"
|
||||
override fun searchMangaFromElement(element: Element): SManga {
|
||||
|
@ -61,7 +88,7 @@ class UniComics : ParsedHttpSource() {
|
|||
"/characters$|/creators$".toRegex().replace(
|
||||
"/page$".toRegex().replace(
|
||||
"/[0-9]+/?$".toRegex().replace(
|
||||
originUrl.substringAfter(PATH_URL).substringAfter(PATH_online).substringAfter(PATH_issue), ""
|
||||
originUrl.replace(PATH_online, PATH_URL).replace(PATH_issue, PATH_URL), ""
|
||||
),
|
||||
""
|
||||
),
|
||||
|
@ -81,18 +108,37 @@ class UniComics : ParsedHttpSource() {
|
|||
|
||||
override fun searchMangaParse(response: Response): MangasPage {
|
||||
val document = response.asJsoup()
|
||||
if (document.select(".CheckboxCaptcha").isNotEmpty() && baseUrl == baseDefaultUrl) {
|
||||
|
||||
if (document.location().contains("$baseDefaultUrl$PATH_events")) {
|
||||
val mangas = document.select(".list_events").map { element ->
|
||||
SManga.create().apply {
|
||||
element.select("a").first().let {
|
||||
setUrlWithoutDomain("/" + it.attr("href"))
|
||||
title = it.text()
|
||||
}
|
||||
}
|
||||
}
|
||||
return MangasPage(mangas, false)
|
||||
}
|
||||
|
||||
if (document.location().contains("$baseDefaultUrl/comics")) {
|
||||
val mangas = document.select(popularMangaSelector()).map { element ->
|
||||
popularMangaFromElement(element)
|
||||
}
|
||||
return MangasPage(mangas, mangas.isNotEmpty())
|
||||
}
|
||||
|
||||
if (document.select(".CheckboxCaptcha").isNotEmpty()) {
|
||||
baseUrl = document.location()
|
||||
throw Exception("Пройдите капчу в WebView(слишком много запросов)")
|
||||
} else if (baseUrl != baseDefaultUrl) {
|
||||
baseUrl = baseDefaultUrl
|
||||
}
|
||||
|
||||
var hasNextPage = false
|
||||
|
||||
val mangas = document.select(searchMangaSelector()).map { element ->
|
||||
searchMangaFromElement(element)
|
||||
}
|
||||
var hasNextPage = false
|
||||
val nextSearchPage = document.select(searchMangaNextPageSelector())
|
||||
if (nextSearchPage.isNotEmpty()) {
|
||||
hasNextPage = true
|
||||
|
@ -111,7 +157,7 @@ class UniComics : ParsedHttpSource() {
|
|||
.asObservableSuccess()
|
||||
.map { response ->
|
||||
val details = mangaDetailsParse(response)
|
||||
details.url = realQuery
|
||||
details.url = PATH_URL + realQuery
|
||||
MangasPage(listOf(details), false)
|
||||
}
|
||||
} else {
|
||||
|
@ -129,21 +175,14 @@ class UniComics : ParsedHttpSource() {
|
|||
|
||||
override fun popularMangaFromElement(element: Element): SManga {
|
||||
val manga = SManga.create()
|
||||
manga.thumbnail_url = element.select(".left_comics img").first().attr("src").replace(".jpg", "_big.jpg")
|
||||
manga.thumbnail_url = element.select(".left_comics img")!!.first().attr("src").replace(".jpg", "_big.jpg")
|
||||
element.select("a").first().let {
|
||||
manga.setUrlWithoutDomain(it.attr("href").substringAfter(PATH_URL))
|
||||
manga.setUrlWithoutDomain(it.attr("href"))
|
||||
}
|
||||
manga.title = element.select(".list_title").first().text()
|
||||
return manga
|
||||
}
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val document = response.asJsoup()
|
||||
|
||||
val mangas = document.select(popularMangaSelector()).map { element ->
|
||||
popularMangaFromElement(element)
|
||||
}
|
||||
return MangasPage(mangas, true)
|
||||
}
|
||||
override fun popularMangaParse(response: Response) = searchMangaParse(response)
|
||||
|
||||
override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
|
||||
override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response)
|
||||
|
@ -153,20 +192,23 @@ class UniComics : ParsedHttpSource() {
|
|||
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
|
||||
|
||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||
return GET(baseDefaultUrl + PATH_URL + manga.url, headers)
|
||||
return GET(baseDefaultUrl + manga.url, headers)
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
|
||||
val infoElement = document.select(".block.left.common").first()
|
||||
val infoElement = document.select(".left_container div").first()
|
||||
title = infoElement.select("h1").first().text()
|
||||
thumbnail_url = infoElement.select("img").first().attr("src")
|
||||
thumbnail_url = if (infoElement.select("img").isNotEmpty())
|
||||
infoElement.select("img").first().attr("src")
|
||||
else
|
||||
document.select(".left_comics img").first().attr("src").replace(".jpg", "_big.jpg")
|
||||
description = infoElement.select("p").last()?.text()
|
||||
author = infoElement.select("tr:contains(Издательство)").text()
|
||||
genre = infoElement.select("tr:contains(Жанр) a").joinToString { it.text() }
|
||||
}
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
val document = client.newCall(GET(baseDefaultUrl + PATH_URL + manga.url, headers)).execute().asJsoup()
|
||||
val document = client.newCall(GET(baseDefaultUrl + manga.url, headers)).execute().asJsoup()
|
||||
val pages = mutableListOf(1)
|
||||
val dataStrArray = document.toString()
|
||||
.substringAfter("new Paginator(")
|
||||
|
@ -177,33 +219,41 @@ class UniComics : ParsedHttpSource() {
|
|||
}
|
||||
return Observable.just(
|
||||
pages.flatMap { page ->
|
||||
chapterListParse(client.newCall(chapterPageListRequest(manga, page)).execute())
|
||||
chapterListParse(client.newCall(chapterPageListRequest(manga, page)).execute(), manga)
|
||||
}.reversed()
|
||||
)
|
||||
}
|
||||
|
||||
private fun chapterPageListRequest(manga: SManga, page: Int): Request {
|
||||
return GET("$baseDefaultUrl$PATH_URL${manga.url}/page/$page", headers)
|
||||
return GET("$baseDefaultUrl${manga.url}/page/$page", headers)
|
||||
}
|
||||
|
||||
override fun chapterListSelector() = "div.right_comics"
|
||||
|
||||
override fun chapterFromElement(element: Element): SChapter {
|
||||
val urlElement = element.select(".button.online a").first()
|
||||
private fun chapterListParse(response: Response, manga: SManga): List<SChapter> {
|
||||
val document = response.asJsoup()
|
||||
return document.select(chapterListSelector()).map { chapterFromElement(it, manga) }
|
||||
}
|
||||
|
||||
private fun chapterFromElement(element: Element, manga: SManga): SChapter {
|
||||
val urlElement = element.select("td:eq(1) a")
|
||||
val chapter = SChapter.create()
|
||||
element.select(".list_title").first().text().let {
|
||||
if (it.contains(" №")) {
|
||||
chapter.name = it.substringAfterLast(" ")
|
||||
chapter.chapter_number = it.substringAfter(" №").toFloatOrNull() ?: -1f
|
||||
} else {
|
||||
chapter.name = "$it Сингл"
|
||||
chapter.chapter_number = 0f
|
||||
val titleNoPrefix = it.removePrefix(manga.title).removePrefix(":").trim()
|
||||
chapter.name = if (titleNoPrefix.isNotEmpty())
|
||||
titleNoPrefix.replaceFirst(titleNoPrefix.first(), titleNoPrefix.first().toUpperCase())
|
||||
else
|
||||
"Сингл"
|
||||
if (titleNoPrefix.contains("№")) {
|
||||
chapter.chapter_number = titleNoPrefix.substringAfterLast("№").toFloatOrNull() ?: -1f
|
||||
}
|
||||
}
|
||||
chapter.setUrlWithoutDomain(urlElement.attr("href"))
|
||||
return chapter
|
||||
}
|
||||
|
||||
override fun chapterFromElement(element: Element) = throw UnsupportedOperationException("Not used")
|
||||
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
return GET(baseDefaultUrl + chapter.url, headers)
|
||||
}
|
||||
|
@ -218,13 +268,75 @@ class UniComics : ParsedHttpSource() {
|
|||
}
|
||||
}
|
||||
override fun imageUrlParse(document: Document): String {
|
||||
return document.select("#b_image").attr("src")
|
||||
return document.select(".image_online").attr("src")
|
||||
}
|
||||
private class Publishers(publishers: Array<String>) : Filter.Select<String>("Издательства (только)", publishers)
|
||||
|
||||
override fun getFilterList() = FilterList(
|
||||
Publishers(publishersName),
|
||||
getEventsList()
|
||||
)
|
||||
private class getEventsList : Filter.Select<String>(
|
||||
"События (только)",
|
||||
arrayOf("Нет", "в комиксах")
|
||||
)
|
||||
|
||||
private data class Publisher(val name: String, val url: String)
|
||||
|
||||
private fun getPublishersList() = listOf(
|
||||
Publisher("Все", "not"),
|
||||
Publisher("Marvel", "marvel"),
|
||||
Publisher("DC Comics", "dc"),
|
||||
Publisher("Image Comics", "imagecomics"),
|
||||
Publisher("Dark Horse Comics", "dark-horse-comics"),
|
||||
Publisher("IDW Publishing", "idw"),
|
||||
Publisher("Vertigo", "vertigo"),
|
||||
Publisher("WildStorm", "wildstorm"),
|
||||
Publisher("Dynamite Entertainment", "dynamite"),
|
||||
Publisher("Boom! Studios", "boomstudios"),
|
||||
Publisher("Avatar Press", "avatarpress"),
|
||||
Publisher("Fox Atomicg", "foxatomic"),
|
||||
Publisher("Top Shelf Productions", "topshelfproduct"),
|
||||
Publisher("Topps", "topps"),
|
||||
Publisher("Radical Publishing", "radical-publishing"),
|
||||
Publisher("Top Cow", "top-cow"),
|
||||
Publisher("Zenescope Entertainment", "zenescope"),
|
||||
Publisher("88MPH", "88mph"),
|
||||
Publisher("Soleil", "soleil"),
|
||||
Publisher("Warner Bros. Entertainment", "warner-bros"),
|
||||
Publisher("Ubisoft Entertainment", "ubisoft"),
|
||||
Publisher("Oni Press", "oni-press"),
|
||||
Publisher("Armada", "delcourt"),
|
||||
Publisher("Heavy Metal", "heavy-metal"),
|
||||
Publisher("Harris Comics", "harris-comics"),
|
||||
Publisher("Antarctic Press", "antarctic-press"),
|
||||
Publisher("Valiant", "valiant"),
|
||||
Publisher("Disney", "disney"),
|
||||
Publisher("Malibu", "malibu"),
|
||||
Publisher("Slave Labor", "slave-labor"),
|
||||
Publisher("Nbm", "nbm"),
|
||||
Publisher("Viper Comics", "viper-comics"),
|
||||
Publisher("Random House", "random-house"),
|
||||
Publisher("Active Images", "active-images"),
|
||||
Publisher("Eurotica", "eurotica"),
|
||||
Publisher("Vortex", "vortex"),
|
||||
Publisher("Fantagraphics", "fantagraphics"),
|
||||
Publisher("Epic", "epic"),
|
||||
Publisher("Warp Graphics", "warp-graphics"),
|
||||
Publisher("Scholastic Book Services", "scholastic-book-services"),
|
||||
Publisher("Ballantine Books", "ballantine-books"),
|
||||
Publisher("Id Software", "id-software")
|
||||
)
|
||||
private val publishersName = getPublishersList().map {
|
||||
it.name
|
||||
}.toTypedArray()
|
||||
|
||||
companion object {
|
||||
const val PREFIX_SLUG_SEARCH = "slug:"
|
||||
private const val PATH_URL = "/comics/series/"
|
||||
private const val PATH_online = "/comics/online/"
|
||||
private const val PATH_issue = "/comics/issue/"
|
||||
private const val PATH_publishers = "/comics/publishers"
|
||||
private const val PATH_events = "/comics/events"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue