Fix mangalib (#1202)

Fix mangalib
This commit is contained in:
red 2019-06-19 22:11:06 +03:00 committed by Eugene
parent f27da3e9cb
commit 291c54e2d4
2 changed files with 200 additions and 144 deletions

View File

@ -5,7 +5,7 @@ ext {
appName = 'Tachiyomi: LibManga'
pkgNameSuffix = 'ru.libmanga'
extClass = '.LibMangaFactory'
extVersionCode = 4
extVersionCode = 5
libVersion = '1.2'
}

View File

@ -1,51 +1,124 @@
package eu.kanade.tachiyomi.extension.ru.libmanga
import com.github.salomonbrys.kotson.*
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.*
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl
import okhttp3.Request
import okhttp3.Response
import org.json.JSONObject
import org.jsoup.nodes.Document
import okhttp3.*
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.*
import android.util.Base64.decode as base64Decode
import rx.Observable
open class LibManga(override val name: String, override val baseUrl: String, private val staticUrl: String) : ParsedHttpSource() {
open class LibManga(override val name: String, override val baseUrl: String, private val staticUrl: String) : HttpSource() {
override val lang = "ru"
override val supportsLatest = true
override fun popularMangaRequest(page: Int): Request =
GET("$baseUrl/manga-list?dir=desc&page=$page&sort=views", headers)
override val client: OkHttpClient = network.cloudflareClient
override fun popularMangaSelector() = "div.manga-list-item"
private val jsonParser = JsonParser()
override fun popularMangaFromElement(element: Element): SManga {
val item = element.select("a.manga-list-item__content").first()
override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers)
private val latestUpdatesSelector = "div.updates__left"
override fun latestUpdatesParse(response: Response): MangasPage {
val elements = response.asJsoup().select(latestUpdatesSelector)
val latestMangas = elements?.map { latestUpdatesFromElement(it) }
if (latestMangas != null)
return MangasPage(latestMangas,false) // TODO: use API
return MangasPage(emptyList(), false)
}
private fun latestUpdatesFromElement(element: Element): SManga {
val link = element.select("a").first()
val img = link.select("img").first()
val manga = SManga.create()
manga.thumbnail_url = item.attr("data-src")
manga.setUrlWithoutDomain(item.attr("href"))
manga.title = item.select("h3.manga-list-item__name").first().text()
manga.thumbnail_url = img.attr("data-src")
.replace("cover_thumb", "cover_250x350")
manga.setUrlWithoutDomain(link.attr("href"))
manga.title = img.attr("alt")
return manga
}
override fun popularMangaNextPageSelector() = "a[rel=\"next\"]"
private var csrfToken: String = ""
override fun mangaDetailsParse(document: Document): SManga {
private fun catalogHeaders() = Headers.Builder()
.apply {
add("Accept", "application/json, text/plain, */*")
add("X-Requested-With", "XMLHttpRequest")
add("x-csrf-token", csrfToken)
}
.build()
override fun popularMangaRequest(page: Int) = GET("$baseUrl/login", headers)
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
if (csrfToken.isEmpty()) {
return client.newCall(popularMangaRequest(page))
.asObservableSuccess()
.flatMap { response ->
// Obtain token
val resBody = response.body()!!.string()
csrfToken = "_token\" content=\"(.*)\"".toRegex().find(resBody)!!.groups[1]!!.value
return@flatMap fetchPopularMangaFromApi(page)
}
}
return fetchPopularMangaFromApi(page)
}
private fun fetchPopularMangaFromApi(page : Int): Observable<MangasPage> {
return client.newCall(POST("$baseUrl/filterlist?dir=desc&sort=views&page=$page", catalogHeaders()))
.asObservableSuccess()
.map { response ->
popularMangaParse(response)
}
}
override fun popularMangaParse(response: Response): MangasPage {
val resBody = response.body()!!.string()
val result = jsonParser.parse(resBody).obj
val items = result["items"]
val popularMangas = items["data"].nullArray?.map { popularMangaFromElement(it) }
if (popularMangas != null) {
val hasNextPage = items["next_page_url"].nullString != null
return MangasPage(popularMangas, hasNextPage)
}
return MangasPage(emptyList(), false)
}
private fun popularMangaFromElement(el: JsonElement) = SManga.create().apply {
title = el["name"].string
thumbnail_url = "$baseUrl/uploads/" + if (el["cover"].nullInt != null)
"cover/${el["slug"].string}/cover/cover_250x350.jpg" else
"no-image.png"
url = "/" + el["slug"].string
}
override fun mangaDetailsParse(response: Response): SManga {
val document = response.asJsoup()
val body = document.select("div.section__body").first()
val manga = SManga.create()
manga.title = body.select(".manga__title").text()
manga.thumbnail_url = body.select(".manga__cover").attr("src")
manga.author = body.select(".info-list__row:nth-child(2) > a").text()
manga.artist = body.select(".info-list__row:nth-child(3) > a").text()
manga.status = when (body.select(".info-list__row:nth-child(4) > span").text()) {
manga.status = when (
body.select(".info-list__row:has(strong:contains(Перевод))")
.first()
.select("span.m-label_info")
.text())
{
"продолжается" -> SManga.ONGOING
"завершен" -> SManga.COMPLETED
else -> SManga.UNKNOWN
@ -55,9 +128,15 @@ open class LibManga(override val name: String, override val baseUrl: String, pri
return manga
}
override fun chapterListSelector() = "div.chapter-item"
private val chapterListSelector = "div.chapter-item"
override fun chapterFromElement(element: Element): SChapter {
override fun chapterListParse(response: Response): List<SChapter> {
val elements = response.asJsoup().select(chapterListSelector)
val chapters = elements?.map { chapterFromElement(it) }
return chapters ?: emptyList()
}
private fun chapterFromElement(element: Element): SChapter {
val chapterLink = element.select("div.chapter-item__name > a").first()
val chapter = SChapter.create()
chapter.setUrlWithoutDomain(chapterLink.attr("href"))
@ -74,44 +153,40 @@ open class LibManga(override val name: String, override val baseUrl: String, pri
}
}
override fun pageListParse(document: Document): List<Page> {
override fun pageListParse(response: Response): List<Page> {
val document = response.asJsoup()
val chapInfo = document
.select("script:containsData(window.__info)")
.first()
.html()
.replace("window.__info = ", "")
.replace(";", "")
val chapInfoJson = jsonParser.parse(chapInfo).obj
// Get pages
val baseStr = document.select("span.pp")
.first()
.html()
.replace("<!--", "")
.replace("-->", "")
.trim()
val decodedArr = base64Decode(baseStr, android.util.Base64.DEFAULT)
val pagesJson = jsonParser.parse(String(decodedArr)).array
val pages = mutableListOf<Page>()
// Parse script
val script = document.select("script:containsData(window.__info)").first().html()
val json: String = script.replace("window.__info = ", "")
val chapterInfo = JSONObject(json)
val pagesJson = chapterInfo.getJSONArray("pages")
for (i in 0..(pagesJson.length() - 1)) {
val page = pagesJson.getJSONObject(i)
pages.add(Page(page.getInt("page_slug"), "", staticUrl + chapterInfo.getString("imgUrl") + page.getString("page_image")))
pagesJson.forEach { page ->
pages.add(Page(page["p"].int, "", staticUrl + chapInfoJson["imgUrl"].string + page["u"].string))
}
return pages
}
override fun imageUrlParse(document: Document) = ""
override fun latestUpdatesRequest(page: Int): Request = GET(baseUrl, headers)
override fun latestUpdatesSelector() = "div.updates__left"
override fun latestUpdatesFromElement(element: Element): SManga {
val link = element.select("a").first()
val img = link.select("img").first()
val manga = SManga.create()
manga.thumbnail_url = img.attr("data-src")
manga.setUrlWithoutDomain(link.attr("href"))
manga.title = img.attr("alt")
return manga
}
override fun latestUpdatesNextPageSelector(): String? = null
override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
override fun imageUrlParse(response: Response): String = ""
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = HttpUrl.parse("$baseUrl/manga-list?page=$page")!!.newBuilder()
val url = HttpUrl.parse("$baseUrl/filterlist?page=$page")!!.newBuilder()
if (query.isNotEmpty()) {
url.addQueryParameter("name", query)
}
@ -134,18 +209,18 @@ open class LibManga(override val name: String, override val baseUrl: String, pri
}
is OrderBy -> {
url.addQueryParameter("dir", if (filter.state!!.ascending) "asc" else "desc")
url.addQueryParameter("sort", arrayOf("rate", "name", "views", "created_at")[filter.state!!.index])
url.addQueryParameter("sort", arrayOf("rate", "name", "views", "created_at", "chap_count")[filter.state!!.index])
}
}
}
return GET(url.toString(), headers)
return POST(url.toString(), catalogHeaders())
}
// Hack search method to add some results from search popup
override fun searchMangaParse(response: Response): MangasPage {
val searchRequest = response.request().url().queryParameter("name")
val mangas = mutableListOf<SManga>()
if (!searchRequest.isNullOrEmpty()) {
val popupSearchHeaders = headers
.newBuilder()
@ -155,43 +230,24 @@ open class LibManga(override val name: String, override val baseUrl: String, pri
// +200ms
val popup = client.newCall(
GET("https://mangalib.me/search?query=$searchRequest", headers = popupSearchHeaders)
).execute().body()!!.string()
val jsonList = JsonParser().parse(popup).asJsonArray
GET("$baseUrl/search?query=$searchRequest", popupSearchHeaders))
.execute().body()!!.string()
val jsonList = jsonParser.parse(popup).array
jsonList.forEach {
val element = it.asJsonObject
val manga = SManga.create()
manga.setUrlWithoutDomain("/" + element.get("slug").string)
manga.description = element.get("summary").nullString
manga.author = element.get("author").nullString
manga.title = element.get("name").string
val status = element.get("status_id").int
manga.status = if (status > 2) 2 else status
mangas.add(manga)
mangas.add(popularMangaFromElement(it))
}
}
val document = response.asJsoup()
val searchedMangas = document.select(searchMangaSelector()).map { element ->
searchMangaFromElement(element)
}
val searchedMangas = popularMangaParse(response)
// Filtered out what find in popup search
mangas.addAll(searchedMangas.filter { search ->
mangas.addAll(searchedMangas.mangas.filter { search ->
mangas.find { search.title == it.title } == null
})
val hasNextPage = searchMangaNextPageSelector().let { selector ->
document.select(selector).first()
} != null
return MangasPage(mangas, hasNextPage)
return MangasPage(mangas, searchedMangas.hasNextPage)
}
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
private class SearchFilter(name: String, val id: String) : Filter.TriState(name)
private class CategoryList(categories: List<SearchFilter>) : Filter.Group<SearchFilter>("Категории", categories)
@ -199,15 +255,15 @@ open class LibManga(override val name: String, override val baseUrl: String, pri
private class GenreList(genres: List<SearchFilter>) : Filter.Group<SearchFilter>("Жанры", genres)
override fun getFilterList() = FilterList(
CategoryList(getCategoryList()),
StatusList(getStatusList()),
GenreList(getGenreList()),
OrderBy()
CategoryList(getCategoryList()),
StatusList(getStatusList()),
GenreList(getGenreList()),
OrderBy()
)
private class OrderBy : Filter.Sort("Сортировка",
arrayOf("Рейтинг", "Имя", "Просмотры", "Дата"),
Filter.Sort.Selection(0, false))
arrayOf("Рейтинг", "Имя", "Просмотры", "Дата", "Кол-во глав"),
Filter.Sort.Selection(0, false))
/*
* Use console
@ -215,13 +271,13 @@ open class LibManga(override val name: String, override val baseUrl: String, pri
* on /manga-list
*/
private fun getCategoryList() = listOf(
SearchFilter("Манга", "1"),
SearchFilter("OEL-манга", "4"),
SearchFilter("Манхва", "5"),
SearchFilter("Маньхуа", "6"),
SearchFilter("Сингл", "7"),
SearchFilter("Руманга", "8"),
SearchFilter("Комикс западный", "9")
SearchFilter("Манга", "1"),
SearchFilter("OEL-манга", "4"),
SearchFilter("Манхва", "5"),
SearchFilter("Маньхуа", "6"),
SearchFilter("Сингл", "7"),
SearchFilter("Руманга", "8"),
SearchFilter("Комикс западный", "9")
)
/*
@ -230,9 +286,9 @@ open class LibManga(override val name: String, override val baseUrl: String, pri
* on /manga-list
*/
private fun getStatusList() = listOf(
SearchFilter("Продолжается", "1"),
SearchFilter("Завершен", "2"),
SearchFilter("Заморожен", "3")
SearchFilter("Продолжается", "1"),
SearchFilter("Завершен", "2"),
SearchFilter("Заморожен", "3")
)
/*
@ -241,51 +297,51 @@ open class LibManga(override val name: String, override val baseUrl: String, pri
* on /manga-list
*/
private fun getGenreList() = listOf(
SearchFilter("арт", "32"),
SearchFilter("бара", "33"),
SearchFilter("боевик", "34"),
SearchFilter("боевые искусства", "35"),
SearchFilter("вампиры", "36"),
SearchFilter("гарем", "37"),
SearchFilter("гендерная интрига", "38"),
SearchFilter("героическое фэнтези", "39"),
SearchFilter("детектив", "40"),
SearchFilter("дзёсэй", "41"),
SearchFilter("додзинси", "42"),
SearchFilter("драма", "43"),
SearchFilter("игра", "44"),
SearchFilter("история", "45"),
SearchFilter("киберпанк", "46"),
SearchFilter("комедия", "47"),
SearchFilter("махо-сёдзё", "48"),
SearchFilter("меха", "49"),
SearchFilter("мистика", "50"),
SearchFilter("научная фантастика", "51"),
SearchFilter("повседневность", "52"),
SearchFilter("постапокалиптика", "53"),
SearchFilter("приключения", "54"),
SearchFilter("психология", "55"),
SearchFilter("романтика", "56"),
SearchFilter("самурайский боевик", "57"),
SearchFilter("сверхъестественное", "58"),
SearchFilter("сёдзё", "59"),
SearchFilter("сёдзё-ай", "60"),
SearchFilter("сёнэн", "61"),
SearchFilter("сёнэн-ай", "62"),
SearchFilter("спорт", "63"),
SearchFilter("сэйнэн", "64"),
SearchFilter("трагедия", "65"),
SearchFilter("триллер", "66"),
SearchFilter("ужасы", "67"),
SearchFilter("фантастика", "68"),
SearchFilter("фэнтези", "69"),
SearchFilter("школа", "70"),
SearchFilter("эротика", "71"),
SearchFilter("этти", "72"),
SearchFilter("юри", "73"),
SearchFilter("яой", "74"),
SearchFilter("ёнкома", "75"),
SearchFilter("кодомо", "76"),
SearchFilter("омегаверс", "77")
SearchFilter("арт", "32"),
SearchFilter("боевик", "34"),
SearchFilter("боевые искусства", "35"),
SearchFilter("вампиры", "36"),
SearchFilter("веб", "78"),
SearchFilter("гарем", "37"),
SearchFilter("гендерная интрига", "38"),
SearchFilter("героическое фэнтези", "39"),
SearchFilter("детектив", "40"),
SearchFilter("дзёсэй", "41"),
SearchFilter("додзинси", "42"),
SearchFilter("драма", "43"),
SearchFilter("ёнкома", "75"),
SearchFilter("игра", "44"),
SearchFilter("история", "45"),
SearchFilter("киберпанк", "46"),
SearchFilter("кодомо", "76"),
SearchFilter("комедия", "47"),
SearchFilter("махо-сёдзё", "48"),
SearchFilter("меха", "49"),
SearchFilter("мистика", "50"),
SearchFilter("научная фантастика", "51"),
SearchFilter("омегаверс", "77"),
SearchFilter("повседневность", "52"),
SearchFilter("постапокалиптика", "53"),
SearchFilter("приключения", "54"),
SearchFilter("психология", "55"),
SearchFilter("романтика", "56"),
SearchFilter("самурайский боевик", "57"),
SearchFilter("сверхъестественное", "58"),
SearchFilter("сёдзё", "59"),
SearchFilter("сёдзё-ай", "60"),
SearchFilter("сёнэн", "61"),
SearchFilter("сёнэн-ай", "62"),
SearchFilter("спорт", "63"),
SearchFilter("сэйнэн", "64"),
SearchFilter("трагедия", "65"),
SearchFilter("триллер", "66"),
SearchFilter("ужасы", "67"),
SearchFilter("фантастика", "68"),
SearchFilter("фэнтези", "69"),
SearchFilter("школа", "70"),
SearchFilter("эротика", "71"),
SearchFilter("этти", "72"),
SearchFilter("юри", "73"),
SearchFilter("яой", "74")
)
}