From 16eaf905e4da0721b74b5938e53b7f750e907756 Mon Sep 17 00:00:00 2001 From: Ejan <35057681+e-shl@users.noreply.github.com> Date: Sat, 30 Apr 2022 18:59:15 +0500 Subject: [PATCH] [RU]Lib Latest use Json (#11681) * [RU]Mangalib Latest use Json * [RU]Hentailib Latest use Json * fix non Primitive --- src/ru/libhentai/build.gradle | 2 +- .../extension/ru/libhentai/LibHentai.kt | 78 ++++++++++++------ src/ru/libmanga/build.gradle | 2 +- .../extension/ru/libmanga/LibManga.kt | 82 ++++++++++++------- 4 files changed, 105 insertions(+), 59 deletions(-) diff --git a/src/ru/libhentai/build.gradle b/src/ru/libhentai/build.gradle index d1990a642..6398ac71a 100644 --- a/src/ru/libhentai/build.gradle +++ b/src/ru/libhentai/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'HentaiLib' pkgNameSuffix = 'ru.libhentai' extClass = '.LibHentai' - extVersionCode = 14 + extVersionCode = 15 isNsfw = true } diff --git a/src/ru/libhentai/src/eu/kanade/tachiyomi/extension/ru/libhentai/LibHentai.kt b/src/ru/libhentai/src/eu/kanade/tachiyomi/extension/ru/libhentai/LibHentai.kt index 3c78beb56..dc0a358b9 100644 --- a/src/ru/libhentai/src/eu/kanade/tachiyomi/extension/ru/libhentai/LibHentai.kt +++ b/src/ru/libhentai/src/eu/kanade/tachiyomi/extension/ru/libhentai/LibHentai.kt @@ -38,7 +38,6 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import okhttp3.ResponseBody.Companion.toResponseBody -import org.jsoup.nodes.Element import rx.Observable import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -98,30 +97,6 @@ class LibHentai : ConfigurableSource, HttpSource() { return@addInterceptor chain.proceed(originalRequest) } .build() - override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers) - - private val latestUpdatesSelector = "div.updates__item" - - 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 manga = SManga.create() - element.select("div.cover").first().let { img -> - manga.thumbnail_url = baseUrl + img.attr("data-src").replace("_thumb", "_250x350") - } - - element.select("a").first().let { link -> - manga.setUrlWithoutDomain(link.attr("href")) - manga.title = if (isEng.equals("rus") || element.select(".updates__name_rus").isNullOrEmpty()) { element.select("h4").first().text() } else element.select(".updates__name_rus").first().text() - } - return manga - } private var csrfToken: String = "" @@ -132,7 +107,47 @@ class LibHentai : ConfigurableSource, HttpSource() { add("x-csrf-token", csrfToken) } .build() + // Latest + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used") // popularMangaRequest() + override fun fetchLatestUpdates(page: Int): Observable { + 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 fetchLatestMangaFromApi(page) + } + } + return fetchLatestMangaFromApi(page) + } + private fun fetchLatestMangaFromApi(page: Int): Observable { + return client.newCall(POST("$baseUrl/latest-updates?page=$page", catalogHeaders())) + .asObservable().doOnNext { response -> + if (!response.isSuccessful) { + response.close() + if (response.code == 419) throw Exception("Для завершения авторизации необходимо перезапустить приложение с полной остановкой.") else throw Exception("HTTP error ${response.code}") + } + } + .map { response -> + latestUpdatesParse(response) + } + } + + override fun latestUpdatesParse(response: Response): MangasPage { + val resBody = response.body!!.string() + val result = json.decodeFromString(resBody) + val itemsLatest = result["data"]?.jsonArray?.map { popularMangaFromElement(it) } + if (itemsLatest != null) { + val hasNextPage = result["next_page_url"]?.jsonPrimitive?.contentOrNull != null + return MangasPage(itemsLatest, hasNextPage) + } + return MangasPage(emptyList(), false) + } + + // Popular override fun popularMangaRequest(page: Int) = GET("$baseUrl/login", headers) override fun fetchPopularManga(page: Int): Observable { @@ -175,16 +190,20 @@ class LibHentai : ConfigurableSource, HttpSource() { return MangasPage(emptyList(), false) } + // Popular cross Latest private fun popularMangaFromElement(el: JsonElement) = SManga.create().apply { + val slug = el.jsonObject["slug"]!!.jsonPrimitive.content title = when { isEng.equals("rus") && el.jsonObject["rus_name"]?.jsonPrimitive?.content.orEmpty().isNotEmpty() -> el.jsonObject["rus_name"]!!.jsonPrimitive.content isEng.equals("eng") && el.jsonObject["eng_name"]?.jsonPrimitive?.content.orEmpty().isNotEmpty() -> el.jsonObject["eng_name"]!!.jsonPrimitive.content else -> el.jsonObject["name"]!!.jsonPrimitive.content } - thumbnail_url = baseUrl + el.jsonObject["covers"]!!.jsonObject["default"]!!.jsonPrimitive.content - url = "/" + el.jsonObject["slug"]!!.jsonPrimitive.content + thumbnail_url = if (el.jsonObject["covers"] != null) baseUrl + el.jsonObject["covers"]!!.jsonObject["default"]!!.jsonPrimitive.content + else baseUrl + "/uploads/cover/" + slug + "/cover/" + el.jsonObject["cover"]!!.jsonPrimitive.content + "_250x350.jpg" + url = "/$slug" } + // Details override fun mangaDetailsParse(response: Response): SManga { val document = response.asJsoup() val dataStr = document @@ -261,6 +280,7 @@ class LibHentai : ConfigurableSource, HttpSource() { return manga } + // Chapters override fun chapterListParse(response: Response): List { val document = response.asJsoup() val redirect = document.html() @@ -389,6 +409,7 @@ class LibHentai : ConfigurableSource, HttpSource() { } } + // Pages override fun pageListParse(response: Response): List { val document = response.asJsoup() @@ -464,6 +485,7 @@ class LibHentai : ConfigurableSource, HttpSource() { override fun imageUrlParse(response: Response): String = "" + // Workaround to allow "Open in browser" use the private fun searchMangaByIdRequest(id: String): Request { return GET("$baseUrl/$id", headers) } @@ -487,6 +509,7 @@ class LibHentai : ConfigurableSource, HttpSource() { } } + // Search override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { if (csrfToken.isEmpty()) { val tokenResponse = client.newCall(popularMangaRequest(page)).execute() @@ -578,6 +601,7 @@ class LibHentai : ConfigurableSource, HttpSource() { return MangasPage(mangas, searchedMangas.hasNextPage) } + // Filters private class SearchFilter(name: String, val id: String) : Filter.TriState(name) private class CheckFilter(name: String, val id: String) : Filter.CheckBox(name) diff --git a/src/ru/libmanga/build.gradle b/src/ru/libmanga/build.gradle index 6ce48db01..24bf04783 100644 --- a/src/ru/libmanga/build.gradle +++ b/src/ru/libmanga/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'MangaLib' pkgNameSuffix = 'ru.libmanga' extClass = '.LibManga' - extVersionCode = 70 + extVersionCode = 71 } dependencies { diff --git a/src/ru/libmanga/src/eu/kanade/tachiyomi/extension/ru/libmanga/LibManga.kt b/src/ru/libmanga/src/eu/kanade/tachiyomi/extension/ru/libmanga/LibManga.kt index 12ca8cd4f..e58fb8d8d 100644 --- a/src/ru/libmanga/src/eu/kanade/tachiyomi/extension/ru/libmanga/LibManga.kt +++ b/src/ru/libmanga/src/eu/kanade/tachiyomi/extension/ru/libmanga/LibManga.kt @@ -38,7 +38,6 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import okhttp3.ResponseBody.Companion.toResponseBody -import org.jsoup.nodes.Element import rx.Observable import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -91,31 +90,6 @@ class LibManga : ConfigurableSource, HttpSource() { add("Referer", baseUrl) } - override fun latestUpdatesRequest(page: Int) = GET(baseUrl, headers) - - private val latestUpdatesSelector = "div.updates__item" - - 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 manga = SManga.create() - element.select("div.cover").first().let { img -> - manga.thumbnail_url = baseUrl + img.attr("data-src").replace("_thumb", "_250x350") - } - - element.select("a").first().let { link -> - manga.setUrlWithoutDomain(link.attr("href")) - manga.title = if (isEng.equals("rus") || element.select(".updates__name_rus").isNullOrEmpty()) { element.select("h4").first().text() } else element.select(".updates__name_rus").first().text() - } - return manga - } - private var csrfToken: String = "" private fun catalogHeaders() = Headers.Builder() @@ -126,8 +100,48 @@ class LibManga : ConfigurableSource, HttpSource() { } .build() - override fun popularMangaRequest(page: Int) = GET("$baseUrl/login", headers) + // Latest + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used") // popularMangaRequest() + override fun fetchLatestUpdates(page: Int): Observable { + 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 fetchLatestMangaFromApi(page) + } + } + return fetchLatestMangaFromApi(page) + } + private fun fetchLatestMangaFromApi(page: Int): Observable { + return client.newCall(POST("$baseUrl/latest-updates?page=$page", catalogHeaders())) + .asObservable().doOnNext { response -> + if (!response.isSuccessful) { + response.close() + if (response.code == 419) throw Exception("Для завершения авторизации необходимо перезапустить приложение с полной остановкой.") else throw Exception("HTTP error ${response.code}") + } + } + .map { response -> + latestUpdatesParse(response) + } + } + + override fun latestUpdatesParse(response: Response): MangasPage { + val resBody = response.body!!.string() + val result = json.decodeFromString(resBody) + val itemsLatest = result["data"]?.jsonArray?.map { popularMangaFromElement(it) } + if (itemsLatest != null) { + val hasNextPage = result["next_page_url"]?.jsonPrimitive?.contentOrNull != null + return MangasPage(itemsLatest, hasNextPage) + } + return MangasPage(emptyList(), false) + } + + // Popular + override fun popularMangaRequest(page: Int) = GET("$baseUrl/login", headers) override fun fetchPopularManga(page: Int): Observable { if (csrfToken.isEmpty()) { return client.newCall(popularMangaRequest(page)) @@ -160,7 +174,6 @@ class LibManga : ConfigurableSource, HttpSource() { val result = json.decodeFromString(resBody) val items = result["items"]!!.jsonObject val popularMangas = items["data"]?.jsonArray?.map { popularMangaFromElement(it) } - if (popularMangas != null) { val hasNextPage = items["next_page_url"]?.jsonPrimitive?.contentOrNull != null return MangasPage(popularMangas, hasNextPage) @@ -168,16 +181,20 @@ class LibManga : ConfigurableSource, HttpSource() { return MangasPage(emptyList(), false) } + // Popular cross Latest private fun popularMangaFromElement(el: JsonElement) = SManga.create().apply { + val slug = el.jsonObject["slug"]!!.jsonPrimitive.content title = when { isEng.equals("rus") && el.jsonObject["rus_name"]?.jsonPrimitive?.content.orEmpty().isNotEmpty() -> el.jsonObject["rus_name"]!!.jsonPrimitive.content isEng.equals("eng") && el.jsonObject["eng_name"]?.jsonPrimitive?.content.orEmpty().isNotEmpty() -> el.jsonObject["eng_name"]!!.jsonPrimitive.content else -> el.jsonObject["name"]!!.jsonPrimitive.content } - thumbnail_url = baseUrl + el.jsonObject["covers"]!!.jsonObject["default"]!!.jsonPrimitive.content - url = "/" + el.jsonObject["slug"]!!.jsonPrimitive.content + thumbnail_url = if (el.jsonObject["covers"] != null) baseUrl + el.jsonObject["covers"]!!.jsonObject["default"]!!.jsonPrimitive.content + else baseUrl + "/uploads/cover/" + slug + "/cover/" + el.jsonObject["cover"]!!.jsonPrimitive.content + "_250x350.jpg" + url = "/$slug" } + // Details override fun mangaDetailsParse(response: Response): SManga { val document = response.asJsoup() val dataStr = document @@ -254,6 +271,7 @@ class LibManga : ConfigurableSource, HttpSource() { return manga } + // Chapters override fun chapterListParse(response: Response): List { val document = response.asJsoup() val redirect = document.html() @@ -382,6 +400,7 @@ class LibManga : ConfigurableSource, HttpSource() { } } + // Pages override fun pageListParse(response: Response): List { val document = response.asJsoup() @@ -457,6 +476,7 @@ class LibManga : ConfigurableSource, HttpSource() { override fun imageUrlParse(response: Response): String = "" + // Workaround to allow "Open in browser" use the private fun searchMangaByIdRequest(id: String): Request { return GET("$baseUrl/$id", headers) } @@ -480,6 +500,7 @@ class LibManga : ConfigurableSource, HttpSource() { } } + // Search override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { if (csrfToken.isEmpty()) { val tokenResponse = client.newCall(popularMangaRequest(page)).execute() @@ -576,6 +597,7 @@ class LibManga : ConfigurableSource, HttpSource() { return MangasPage(mangas, searchedMangas.hasNextPage) } + // Filters private class SearchFilter(name: String, val id: String) : Filter.TriState(name) private class CheckFilter(name: String, val id: String) : Filter.CheckBox(name)