From d68ff1afceef58665235a48983ff2cf2233c27dd Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Fri, 30 Sep 2022 23:10:36 +0600 Subject: [PATCH] Cleanup 9Hentai (#13642) * Cleanup 9Hentai * Recache cover when total page count changes --- .../ninehentai/AndroidManifest.xml | 2 +- src/{all => en}/ninehentai/build.gradle | 4 +- .../res/mipmap-hdpi/ic_launcher.png | Bin .../res/mipmap-mdpi/ic_launcher.png | Bin .../res/mipmap-xhdpi/ic_launcher.png | Bin .../res/mipmap-xxhdpi/ic_launcher.png | Bin .../res/mipmap-xxxhdpi/ic_launcher.png | Bin .../ninehentai/res/web_hi_res_512.png | Bin .../extension/en}/ninehentai/NineHentai.kt | 231 +++++++++--------- .../extension/en}/ninehentai/NineHentaiDto.kt | 14 +- .../en}/ninehentai/NineHentaiUrlActivity.kt | 2 +- 11 files changed, 138 insertions(+), 115 deletions(-) rename src/{all => en}/ninehentai/AndroidManifest.xml (92%) rename src/{all => en}/ninehentai/build.gradle (79%) rename src/{all => en}/ninehentai/res/mipmap-hdpi/ic_launcher.png (100%) rename src/{all => en}/ninehentai/res/mipmap-mdpi/ic_launcher.png (100%) rename src/{all => en}/ninehentai/res/mipmap-xhdpi/ic_launcher.png (100%) rename src/{all => en}/ninehentai/res/mipmap-xxhdpi/ic_launcher.png (100%) rename src/{all => en}/ninehentai/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename src/{all => en}/ninehentai/res/web_hi_res_512.png (100%) rename src/{all/ninehentai/src/eu/kanade/tachiyomi/extension/all => en/ninehentai/src/eu/kanade/tachiyomi/extension/en}/ninehentai/NineHentai.kt (85%) rename src/{all/ninehentai/src/eu/kanade/tachiyomi/extension/all => en/ninehentai/src/eu/kanade/tachiyomi/extension/en}/ninehentai/NineHentaiDto.kt (77%) rename src/{all/ninehentai/src/eu/kanade/tachiyomi/extension/all => en/ninehentai/src/eu/kanade/tachiyomi/extension/en}/ninehentai/NineHentaiUrlActivity.kt (95%) diff --git a/src/all/ninehentai/AndroidManifest.xml b/src/en/ninehentai/AndroidManifest.xml similarity index 92% rename from src/all/ninehentai/AndroidManifest.xml rename to src/en/ninehentai/AndroidManifest.xml index 02f8050b4..caf8fce1c 100644 --- a/src/all/ninehentai/AndroidManifest.xml +++ b/src/en/ninehentai/AndroidManifest.xml @@ -4,7 +4,7 @@ diff --git a/src/all/ninehentai/build.gradle b/src/en/ninehentai/build.gradle similarity index 79% rename from src/all/ninehentai/build.gradle rename to src/en/ninehentai/build.gradle index 3c5109c24..59197986f 100644 --- a/src/all/ninehentai/build.gradle +++ b/src/en/ninehentai/build.gradle @@ -4,9 +4,9 @@ apply plugin: 'kotlinx-serialization' ext { extName = 'NineHentai' - pkgNameSuffix = 'all.ninehentai' + pkgNameSuffix = 'en.ninehentai' extClass = '.NineHentai' - extVersionCode = 14 + extVersionCode = 1 isNsfw = true } diff --git a/src/all/ninehentai/res/mipmap-hdpi/ic_launcher.png b/src/en/ninehentai/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from src/all/ninehentai/res/mipmap-hdpi/ic_launcher.png rename to src/en/ninehentai/res/mipmap-hdpi/ic_launcher.png diff --git a/src/all/ninehentai/res/mipmap-mdpi/ic_launcher.png b/src/en/ninehentai/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from src/all/ninehentai/res/mipmap-mdpi/ic_launcher.png rename to src/en/ninehentai/res/mipmap-mdpi/ic_launcher.png diff --git a/src/all/ninehentai/res/mipmap-xhdpi/ic_launcher.png b/src/en/ninehentai/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from src/all/ninehentai/res/mipmap-xhdpi/ic_launcher.png rename to src/en/ninehentai/res/mipmap-xhdpi/ic_launcher.png diff --git a/src/all/ninehentai/res/mipmap-xxhdpi/ic_launcher.png b/src/en/ninehentai/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from src/all/ninehentai/res/mipmap-xxhdpi/ic_launcher.png rename to src/en/ninehentai/res/mipmap-xxhdpi/ic_launcher.png diff --git a/src/all/ninehentai/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/ninehentai/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from src/all/ninehentai/res/mipmap-xxxhdpi/ic_launcher.png rename to src/en/ninehentai/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/src/all/ninehentai/res/web_hi_res_512.png b/src/en/ninehentai/res/web_hi_res_512.png similarity index 100% rename from src/all/ninehentai/res/web_hi_res_512.png rename to src/en/ninehentai/res/web_hi_res_512.png diff --git a/src/all/ninehentai/src/eu/kanade/tachiyomi/extension/all/ninehentai/NineHentai.kt b/src/en/ninehentai/src/eu/kanade/tachiyomi/extension/en/ninehentai/NineHentai.kt similarity index 85% rename from src/all/ninehentai/src/eu/kanade/tachiyomi/extension/all/ninehentai/NineHentai.kt rename to src/en/ninehentai/src/eu/kanade/tachiyomi/extension/en/ninehentai/NineHentai.kt index 3938d05dc..f1191754b 100644 --- a/src/all/ninehentai/src/eu/kanade/tachiyomi/extension/all/ninehentai/NineHentai.kt +++ b/src/en/ninehentai/src/eu/kanade/tachiyomi/extension/en/ninehentai/NineHentai.kt @@ -1,5 +1,6 @@ -package eu.kanade.tachiyomi.extension.all.ninehentai +package eu.kanade.tachiyomi.extension.en.ninehentai +import android.util.Log import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.asObservableSuccess @@ -11,10 +12,11 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.decodeFromJsonElement -import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.put @@ -23,6 +25,7 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response +import okio.Buffer import org.jsoup.nodes.Element import rx.Observable import rx.schedulers.Schedulers @@ -52,7 +55,7 @@ class NineHentai : HttpSource() { includedTags: List = listOf(), excludedTags: List = listOf() ): Request { - val request = SearchRequest( + val searchRequest = SearchRequest( text = searchText, page = page - 1, // Source starts counting from 0, not 1 sort = sort, @@ -64,41 +67,119 @@ class NineHentai : HttpSource() { ) ) ) - val jsonString = buildJsonObject { - put("search", json.encodeToJsonElement(request)) - }.toString() + val jsonString = json.encodeToString(SearchRequestPayload(search = searchRequest)) return POST("$baseUrl$SEARCH_URL", headers, jsonString.toRequestBody(MEDIA_TYPE)) } + private fun parseSearchResponse(response: Response): MangasPage { + return response.use { + val page = json.decodeFromString(it.request.bodyString).search.page + json.decodeFromString(it.body?.string().orEmpty()).let { searchResponse -> + MangasPage( + searchResponse.results.map { + SManga.create().apply { + url = "/g/${it.id}" + title = it.title + // Cover is the compressed first page (cover might change if page count changes) + thumbnail_url = "${it.image_server}${it.id}/1.jpg?${it.total_page}" + } + }, + searchResponse.totalCount - 1 > page + ) + } + } + } + // Builds request for /api/getBookById endpoint private fun buildDetailRequest(id: Int): Request { val jsonString = buildJsonObject { put("id", id) }.toString() return POST("$baseUrl$MANGA_URL", headers, jsonString.toRequestBody(MEDIA_TYPE)) } - // Popular and Latest + // Popular override fun popularMangaRequest(page: Int): Request = buildSearchRequest(page = page, sort = 1) - override fun popularMangaParse(response: Response): MangasPage { - val results = json.parseToJsonElement(response.body!!.string()).jsonObject["results"]!!.jsonArray - if (results.isEmpty()) return MangasPage(listOf(), false) - return MangasPage( - results.map { - val manga = json.decodeFromJsonElement(it) - SManga.create().apply { - setUrlWithoutDomain("/g/${manga.id}") - title = manga.title - thumbnail_url = "${manga.image_server + manga.id}/cover.jpg" + override fun popularMangaParse(response: Response): MangasPage = parseSearchResponse(response) + + // Latest + override fun latestUpdatesRequest(page: Int): Request = buildSearchRequest(page = page) + + override fun latestUpdatesParse(response: Response): MangasPage = parseSearchResponse(response) + + // Search + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { + if (query.startsWith("id:")) { + val id = query.substringAfter("id:").toInt() + return client.newCall(buildDetailRequest(id)) + .asObservableSuccess() + .map { response -> + fetchSingleManga(response) } - }, - true + } + return super.fetchSearchManga(page, query, filters) + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val filterList = if (filters.isEmpty()) getFilterList() else filters + var sort = 0 + val range = mutableListOf(0, 2000) + val includedTags = mutableListOf() + val excludedTags = mutableListOf() + for (filter in filterList) { + when (filter) { + is SortFilter -> { + sort = filter.state + } + is MinPagesFilter -> { + try { + range[0] = filter.state.toInt() + } catch (_: NumberFormatException) { + // Suppress and retain default value + } + } + is MaxPagesFilter -> { + try { + range[1] = filter.state.toInt() + } catch (_: NumberFormatException) { + // Suppress and retain default value + } + } + is IncludedFilter -> { + includedTags += getTags(filter.state, 1) + } + is ExcludedFilter -> { + excludedTags += getTags(filter.state, 1) + } + is GroupFilter -> { + includedTags += getTags(filter.state, 2) + } + is ParodyFilter -> { + includedTags += getTags(filter.state, 3) + } + is ArtistFilter -> { + includedTags += getTags(filter.state, 4) + } + is CharacterFilter -> { + includedTags += getTags(filter.state, 5) + } + is CategoryFilter -> { + includedTags += getTags(filter.state, 6) + } + else -> { /* Do nothing */ } + } + } + return buildSearchRequest( + searchText = query, + page = page, + sort = sort, + range = range, + includedTags = includedTags, + excludedTags = excludedTags ) } - override fun latestUpdatesRequest(page: Int): Request = buildSearchRequest(page = page) - - override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response) + override fun searchMangaParse(response: Response): MangasPage = parseSearchResponse(response) // Manga Details @@ -109,7 +190,7 @@ class NineHentai : HttpSource() { thumbnail_url = info.selectFirst("div#cover v-lazy-image").attr("abs:src") status = SManga.COMPLETED artist = info.selectTextOrNull("div.field-name:contains(Artist:) a.tag") - author = info.selectTextOrNull("div.field-name:contains(Group:) a.tag") ?: artist + author = info.selectTextOrNull("div.field-name:contains(Group:) a.tag") ?: "Unknown circle" genre = info.selectTextOrNull("div.field-name:contains(Tag:) a.tag") // Additional details description = listOf( @@ -141,16 +222,16 @@ class NineHentai : HttpSource() { SChapter.create().apply { name = "Chapter" date_upload = parseChapterDate(time) - setUrlWithoutDomain(response.request.url.encodedPath) + url = response.request.url.encodedPath } ) } private fun parseChapterDate(date: String): Long { - val value = date.split(' ')[0].toInt() - val timeStr = date.split(' ')[1].removeSuffix("s") + val dateStringSplit = date.split(" ") + val value = dateStringSplit[0].toInt() - return when (timeStr) { + return when (dateStringSplit[1].removeSuffix("s")) { "sec" -> Calendar.getInstance().apply { add(Calendar.SECOND, value * -1) }.timeInMillis @@ -191,8 +272,6 @@ class NineHentai : HttpSource() { val imageUrl = manga.image_server + manga.id var totalPages = manga.total_page - val pages = mutableListOf() - client.newCall( GET( "$imageUrl/preview/${totalPages}t.jpg", @@ -202,71 +281,9 @@ class NineHentai : HttpSource() { if (code == 404) totalPages-- } - for (i in 1..totalPages) { - pages.add(Page(pages.size, "", "$imageUrl/$i.jpg")) + return (1..totalPages).map { + Page(it, "", "$imageUrl/$it.jpg") } - - return pages - } - - // Search - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val filterList = if (filters.isEmpty()) getFilterList() else filters - var sort = 0 - val range = mutableListOf(0, 2000) - val includedTags = mutableListOf() - val excludedTags = mutableListOf() - for (filter in filterList) { - when (filter) { - is SortFilter -> { - sort = filter.state!!.index - } - is MinPagesFilter -> { - try { - range[0] = filter.state.toInt() - } catch (_: NumberFormatException) { - // Suppress and retain default value - } - } - is MaxPagesFilter -> { - try { - range[1] = filter.state.toInt() - } catch (_: NumberFormatException) { - // Suppress and retain default value - } - } - is IncludedFilter -> { - includedTags += getTags(filter.state, 1) - } - is ExcludedFilter -> { - excludedTags += getTags(filter.state, 1) - } - is GroupFilter -> { - includedTags += getTags(filter.state, 2) - } - is ParodyFilter -> { - includedTags += getTags(filter.state, 3) - } - is ArtistFilter -> { - includedTags += getTags(filter.state, 4) - } - is CharacterFilter -> { - includedTags += getTags(filter.state, 5) - } - is CategoryFilter -> { - includedTags += getTags(filter.state, 6) - } - } - } - return buildSearchRequest( - searchText = query, - page = page, - sort = sort, - range = range, - includedTags = includedTags, - excludedTags = excludedTags - ) } private fun getTags(queries: String, type: Int): List { @@ -295,18 +312,6 @@ class NineHentai : HttpSource() { }.toBlocking().first() } - override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { - if (query.startsWith("id:")) { - val id = query.substringAfter("id:").toInt() - return client.newCall(buildDetailRequest(id)) - .asObservableSuccess() - .map { response -> - fetchSingleManga(response) - } - } - return super.fetchSearchManga(page, query, filters) - } - private fun fetchSingleManga(response: Response): MangasPage { val resultsObj = json.parseToJsonElement(response.body!!.string()).jsonObject["results"]!! val manga = json.decodeFromJsonElement(resultsObj) @@ -320,14 +325,11 @@ class NineHentai : HttpSource() { return MangasPage(list, false) } - override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response) - // Filters - private class SortFilter : Filter.Sort( + private class SortFilter : Filter.Select( "Sort by", - arrayOf("Newest", "Popular Right now", "Most Fapped", "Most Viewed", "By Title"), - Selection(1, false) + arrayOf("Newest", "Popular Right now", "Most Fapped", "Most Viewed", "By Title") ) private class MinPagesFilter : Filter.Text("Minimum Pages") @@ -357,6 +359,15 @@ class NineHentai : HttpSource() { override fun imageUrlParse(response: Response): String = throw Exception("Not Used") + private val Request.bodyString: String + get() { + val requestCopy = newBuilder().build() + val buffer = Buffer() + + return runCatching { buffer.apply { requestCopy.body!!.writeTo(this) }.readUtf8() } + .getOrNull() ?: "" + } + companion object { private val MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() private const val SEARCH_URL = "/api/getBook" diff --git a/src/all/ninehentai/src/eu/kanade/tachiyomi/extension/all/ninehentai/NineHentaiDto.kt b/src/en/ninehentai/src/eu/kanade/tachiyomi/extension/en/ninehentai/NineHentaiDto.kt similarity index 77% rename from src/all/ninehentai/src/eu/kanade/tachiyomi/extension/all/ninehentai/NineHentaiDto.kt rename to src/en/ninehentai/src/eu/kanade/tachiyomi/extension/en/ninehentai/NineHentaiDto.kt index 1c08191e2..828a80a29 100644 --- a/src/all/ninehentai/src/eu/kanade/tachiyomi/extension/all/ninehentai/NineHentaiDto.kt +++ b/src/en/ninehentai/src/eu/kanade/tachiyomi/extension/en/ninehentai/NineHentaiDto.kt @@ -1,5 +1,6 @@ -package eu.kanade.tachiyomi.extension.all.ninehentai +package eu.kanade.tachiyomi.extension.en.ninehentai +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable @@ -47,6 +48,17 @@ data class SearchRequest( val tag: Items ) +@Serializable +data class SearchRequestPayload( + val search: SearchRequest +) + +@Serializable +data class SearchResponse( + @SerialName("total_count") val totalCount: Int, + val results: List +) + @Serializable data class Range( val range: List diff --git a/src/all/ninehentai/src/eu/kanade/tachiyomi/extension/all/ninehentai/NineHentaiUrlActivity.kt b/src/en/ninehentai/src/eu/kanade/tachiyomi/extension/en/ninehentai/NineHentaiUrlActivity.kt similarity index 95% rename from src/all/ninehentai/src/eu/kanade/tachiyomi/extension/all/ninehentai/NineHentaiUrlActivity.kt rename to src/en/ninehentai/src/eu/kanade/tachiyomi/extension/en/ninehentai/NineHentaiUrlActivity.kt index 87f3ea5e3..378c38a07 100644 --- a/src/all/ninehentai/src/eu/kanade/tachiyomi/extension/all/ninehentai/NineHentaiUrlActivity.kt +++ b/src/en/ninehentai/src/eu/kanade/tachiyomi/extension/en/ninehentai/NineHentaiUrlActivity.kt @@ -1,4 +1,4 @@ -package eu.kanade.tachiyomi.extension.all.ninehentai +package eu.kanade.tachiyomi.extension.en.ninehentai import android.app.Activity import android.content.ActivityNotFoundException