From 03850d43a763e2869999c478ddf225d2e5a49612 Mon Sep 17 00:00:00 2001 From: Johannes Joens <34899572+jopejoe1@users.noreply.github.com> Date: Wed, 7 Jul 2021 21:52:33 +1200 Subject: [PATCH] nHentai.com switch to kotlinx and add support for multilang (#7990) * Create NHentaiCom.kt * Create NHentaiComFactory.kt * Create NHentaiCom.kt * Delete NHentaiCom.kt * Create build.gradle * Create AndroidManifest.xml * Added Icons * Remove Single Lang files * Added (unoriginal) back to naming * Changed name back to (unoriginal) ending * Removed Langues * Update NHentaiComFactory.kt * Update NHentaiCom.kt * change from try to runCatching --- .../nhentaicom/AndroidManifest.xml | 0 src/{en => all}/nhentaicom/build.gradle | 6 +- .../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 .../nhentaicom/res/web_hi_res_512.png | Bin .../extension/all/nhentaicom}/NHentaiCom.kt | 145 +++++++++++------- .../all/nhentaicom/NHentaiComFactory.kt | 14 ++ 10 files changed, 105 insertions(+), 60 deletions(-) rename src/{en => all}/nhentaicom/AndroidManifest.xml (100%) rename src/{en => all}/nhentaicom/build.gradle (68%) rename src/{en => all}/nhentaicom/res/mipmap-hdpi/ic_launcher.png (100%) rename src/{en => all}/nhentaicom/res/mipmap-mdpi/ic_launcher.png (100%) rename src/{en => all}/nhentaicom/res/mipmap-xhdpi/ic_launcher.png (100%) rename src/{en => all}/nhentaicom/res/mipmap-xxhdpi/ic_launcher.png (100%) rename src/{en => all}/nhentaicom/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename src/{en => all}/nhentaicom/res/web_hi_res_512.png (100%) rename src/{en/nhentaicom/src/eu/kanade/tachiyomi/extension/en/nhentai/com => all/nhentaicom/src/eu/kanade/tachiyomi/extension/all/nhentaicom}/NHentaiCom.kt (51%) create mode 100644 src/all/nhentaicom/src/eu/kanade/tachiyomi/extension/all/nhentaicom/NHentaiComFactory.kt diff --git a/src/en/nhentaicom/AndroidManifest.xml b/src/all/nhentaicom/AndroidManifest.xml similarity index 100% rename from src/en/nhentaicom/AndroidManifest.xml rename to src/all/nhentaicom/AndroidManifest.xml diff --git a/src/en/nhentaicom/build.gradle b/src/all/nhentaicom/build.gradle similarity index 68% rename from src/en/nhentaicom/build.gradle rename to src/all/nhentaicom/build.gradle index f0180d800..554b756ce 100644 --- a/src/en/nhentaicom/build.gradle +++ b/src/all/nhentaicom/build.gradle @@ -3,9 +3,9 @@ apply plugin: 'kotlin-android' ext { extName = 'nHentai.com (unoriginal)' - pkgNameSuffix = 'en.nhentai.com' - extClass = '.NHentaiCom' - extVersionCode = 1 + pkgNameSuffix = 'all.nhentaicom' + extClass = '.NHentaiComFactory' + extVersionCode = 2 libVersion = '1.2' containsNsfw = true } diff --git a/src/en/nhentaicom/res/mipmap-hdpi/ic_launcher.png b/src/all/nhentaicom/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from src/en/nhentaicom/res/mipmap-hdpi/ic_launcher.png rename to src/all/nhentaicom/res/mipmap-hdpi/ic_launcher.png diff --git a/src/en/nhentaicom/res/mipmap-mdpi/ic_launcher.png b/src/all/nhentaicom/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from src/en/nhentaicom/res/mipmap-mdpi/ic_launcher.png rename to src/all/nhentaicom/res/mipmap-mdpi/ic_launcher.png diff --git a/src/en/nhentaicom/res/mipmap-xhdpi/ic_launcher.png b/src/all/nhentaicom/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from src/en/nhentaicom/res/mipmap-xhdpi/ic_launcher.png rename to src/all/nhentaicom/res/mipmap-xhdpi/ic_launcher.png diff --git a/src/en/nhentaicom/res/mipmap-xxhdpi/ic_launcher.png b/src/all/nhentaicom/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from src/en/nhentaicom/res/mipmap-xxhdpi/ic_launcher.png rename to src/all/nhentaicom/res/mipmap-xxhdpi/ic_launcher.png diff --git a/src/en/nhentaicom/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/nhentaicom/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from src/en/nhentaicom/res/mipmap-xxxhdpi/ic_launcher.png rename to src/all/nhentaicom/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/src/en/nhentaicom/res/web_hi_res_512.png b/src/all/nhentaicom/res/web_hi_res_512.png similarity index 100% rename from src/en/nhentaicom/res/web_hi_res_512.png rename to src/all/nhentaicom/res/web_hi_res_512.png diff --git a/src/en/nhentaicom/src/eu/kanade/tachiyomi/extension/en/nhentai/com/NHentaiCom.kt b/src/all/nhentaicom/src/eu/kanade/tachiyomi/extension/all/nhentaicom/NHentaiCom.kt similarity index 51% rename from src/en/nhentaicom/src/eu/kanade/tachiyomi/extension/en/nhentai/com/NHentaiCom.kt rename to src/all/nhentaicom/src/eu/kanade/tachiyomi/extension/all/nhentaicom/NHentaiCom.kt index c281717ed..e9147eacf 100644 --- a/src/en/nhentaicom/src/eu/kanade/tachiyomi/extension/en/nhentai/com/NHentaiCom.kt +++ b/src/all/nhentaicom/src/eu/kanade/tachiyomi/extension/all/nhentaicom/NHentaiCom.kt @@ -1,14 +1,5 @@ -package eu.kanade.tachiyomi.extension.en.nhentai.com +package eu.kanade.tachiyomi.extension.all.nhentaicom -import android.app.Application -import android.content.SharedPreferences -import com.github.salomonbrys.kotson.fromJson -import com.github.salomonbrys.kotson.get -import com.github.salomonbrys.kotson.int -import com.github.salomonbrys.kotson.nullString -import com.github.salomonbrys.kotson.string -import com.google.gson.Gson -import com.google.gson.JsonObject import eu.kanade.tachiyomi.annotations.Nsfw import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.asObservableSuccess @@ -19,59 +10,87 @@ import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive import okhttp3.Headers import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import rx.Observable -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get +import uy.kohesive.injekt.injectLazy @Nsfw -class NHentaiCom : HttpSource() { +class NHentaiCom(override val lang: String) : HttpSource() { - override val name = "nHentai.com (unoriginal)" + override val name = when (lang) { + "other" -> "nHentai.com (unoriginal)(Text Cleaned)" + "all" -> "nHentai.com (unoriginal)(Unfiltered)" + else -> "nHentai.com (unoriginal)" + } + + override val id = when (lang) { + "en" -> 5591830863732393712 + else -> super.id + } override val baseUrl = "https://nhentai.com" - override val lang = "en" + private val langId = toLangId(lang) override val supportsLatest = true + private val json: Json by injectLazy() + + private fun toLangId(langCode: String): String { + return when (langCode) { + "en" -> "1" + "zh" -> "2" + "ja" -> "3" + "other" -> "4" + "cz" -> "5" + "ar" -> "6" + "sk" -> "7" + "eo" -> "8" + else -> "" + } + } + override fun headersBuilder(): Headers.Builder = Headers.Builder() .add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)") - private val preferences: SharedPreferences by lazy { - Injekt.get().getSharedPreferences("source_$id", 0x0000) - } - - override val client: OkHttpClient = network.cloudflareClient - - private val gson = Gson() - private fun parseMangaFromJson(response: Response): MangasPage { - val jsonObject = gson.fromJson(response.body!!.string()) + val jsonRaw = response.body!!.string() + val jsonResult = json.parseToJsonElement(jsonRaw).jsonObject - val mangas = jsonObject["data"].asJsonArray.map { json -> + val mangas = jsonResult["data"]!!.jsonArray.map { jsonEl -> SManga.create().apply { - title = json["title"].string - thumbnail_url = json["image_url"].string - url = json["slug"].string + val jsonObj = jsonEl.jsonObject + title = jsonObj["title"]!!.jsonPrimitive.content + thumbnail_url = jsonObj["image_url"]!!.jsonPrimitive.content + url = jsonObj["slug"]!!.jsonPrimitive.content } } - return MangasPage(mangas, jsonObject["current_page"].int < jsonObject["last_page"].int) + return MangasPage(mangas, jsonResult["current_page"]!!.jsonPrimitive.content.toInt() < jsonResult["last_page"]!!.jsonPrimitive.content.toInt()) } - private fun getMangaUrl(url: String): String { - return url.toHttpUrlOrNull()!!.newBuilder() - .setQueryParameter("nsfw", "false").toString() + + private fun getMangaUrl(page: Int, sort: String): String { + val url = "$baseUrl/api/comics".toHttpUrlOrNull()!!.newBuilder() + if (langId.isNotBlank()) { + url.setQueryParameter("languages[]", langId) + } + url.setQueryParameter("page", "$page") + url.setQueryParameter("sort", sort) + url.setQueryParameter("nsfw", "false") + return url.toString() } // Popular override fun popularMangaRequest(page: Int): Request { - return GET(getMangaUrl("$baseUrl/api/comics?page=$page&q=&sort=popularity&order=desc&duration=week"), headers) + return GET(getMangaUrl(page, "popularity"), headers) } override fun popularMangaParse(response: Response): MangasPage = parseMangaFromJson(response) @@ -79,7 +98,7 @@ class NHentaiCom : HttpSource() { // Latest override fun latestUpdatesRequest(page: Int): Request { - return GET(getMangaUrl("$baseUrl/api/comics?page=$page&q=&sort=uploaded_at&order=desc&duration=day"), headers) + return GET(getMangaUrl(page, "uploaded_at"), headers) } override fun latestUpdatesParse(response: Response): MangasPage = parseMangaFromJson(response) @@ -90,14 +109,20 @@ class NHentaiCom : HttpSource() { val url = "$baseUrl/api/comics".toHttpUrlOrNull()!!.newBuilder() .addQueryParameter("per_page", "18") .addQueryParameter("page", page.toString()) - .addQueryParameter("order", "desc") .addQueryParameter("q", query) .addQueryParameter("nsfw", "false") + if (langId.isNotBlank()) { + url.setQueryParameter("languages[]", langId) + } + url.setQueryParameter("page", "$page") + url.setQueryParameter("nsfw", "false") + filters.forEach { filter -> when (filter) { is SortFilter -> url.addQueryParameter("sort", filter.toUriPart()) is DurationFilter -> url.addQueryParameter("duration", filter.toUriPart()) + is SortOrderFilter -> url.addQueryParameter("order", filter.toUriPart()) } } return GET(url.toString(), headers) @@ -113,32 +138,26 @@ class NHentaiCom : HttpSource() { .map { mangaDetailsParse(it).apply { initialized = true } } // Return the real URL for "Open in browser" - override fun mangaDetailsRequest(manga: SManga) = GET(getMangaUrl("$baseUrl/en/webtoon/${manga.url}"), headers) + override fun mangaDetailsRequest(manga: SManga) = GET("$baseUrl/en/comic/${manga.url}", headers) private fun apiMangaDetailsRequest(manga: SManga): Request { - return GET(getMangaUrl("$baseUrl/api/comics/${manga.url}"), headers) + return GET("$baseUrl/api/comics/${manga.url}", headers) } override fun mangaDetailsParse(response: Response): SManga { - val jsonObject = gson.fromJson(response.body!!.string()) + val jsonRaw = response.body!!.string() + val jsonObject = json.parseToJsonElement(jsonRaw).jsonObject return SManga.create().apply { - description = jsonObject["description"].nullString - status = jsonObject["status"].nullString.toStatus() - thumbnail_url = jsonObject["image_url"].nullString - genre = try { jsonObject["tags"].asJsonArray.joinToString { it["name"].string } } catch (_: Exception) { null } - artist = try { jsonObject["artists"].asJsonArray.joinToString { it["name"].string } } catch (_: Exception) { null } - author = try { jsonObject["authors"].asJsonArray.joinToString { it["name"].string } } catch (_: Exception) { null } + description = jsonObject["description"]!!.jsonPrimitive.content + status = SManga.COMPLETED + thumbnail_url = jsonObject["image_url"]!!.jsonPrimitive.content + genre = runCatching{ jsonObject["tags"]!!.jsonArray.joinToString { it.jsonObject["name"]!!.jsonPrimitive.content }}.getOrNull() + artist = runCatching{ jsonObject["artists"]!!.jsonArray.joinToString { it.jsonObject["name"]!!.jsonPrimitive.content }}.getOrNull() + author = runCatching{ jsonObject["authors"]!!.jsonArray.joinToString { it.jsonObject["name"]!!.jsonPrimitive.content }}.getOrNull() } } - private fun String?.toStatus() = when { - this == null -> SManga.UNKNOWN - this.contains("ongoing", ignoreCase = true) -> SManga.ONGOING - this.contains("complete", ignoreCase = true) -> SManga.COMPLETED - else -> SManga.UNKNOWN - } - // Chapters override fun fetchChapterList(manga: SManga): Observable> { @@ -159,12 +178,13 @@ class NHentaiCom : HttpSource() { // Pages override fun pageListRequest(chapter: SChapter): Request { - return GET(getMangaUrl("$baseUrl/api/comics/${chapter.url}/images"), headers) + return GET("$baseUrl/api/comics/${chapter.url}/images", headers) } override fun pageListParse(response: Response): List { - return gson.fromJson(response.body!!.string())["images"].asJsonArray.mapIndexed { i, json -> - Page(i, "", json["source_url"].string) + return json.parseToJsonElement(response.body!!.string()).jsonObject["images"]!!.jsonArray.mapIndexed { i, jsonEl -> + val jsonObj = jsonEl.jsonObject + Page(i, "", jsonObj["source_url"]!!.jsonPrimitive.content) } } @@ -174,18 +194,26 @@ class NHentaiCom : HttpSource() { override fun getFilterList() = FilterList( DurationFilter(getDurationList()), - SortFilter(getSortList()) + SortFilter(getSortList()), + SortOrderFilter(getSortOrder()) ) private class DurationFilter(pairs: Array>) : UriPartFilter("Duration", pairs) private class SortFilter(pairs: Array>) : UriPartFilter("Sorted by", pairs) + private class SortOrderFilter(pairs: Array>) : UriPartFilter("Order", pairs) + open class UriPartFilter(displayName: String, private val vals: Array>) : Filter.Select(displayName, vals.map { it.first }.toTypedArray()) { fun toUriPart() = vals[state].second } + private fun getSortOrder() = arrayOf( + Pair("Descending", "desc"), + Pair("Ascending", "asc"), + ) + private fun getDurationList() = arrayOf( Pair("All time", "all"), Pair("Year", "year"), @@ -195,7 +223,10 @@ class NHentaiCom : HttpSource() { ) private fun getSortList() = arrayOf( + Pair("Upload date", "uploaded_at"), + Pair("Title", "title"), + Pair("Pages", "pages"), + Pair("Favorites", "favorites"), Pair("Popularity", "popularity"), - Pair("Date", "uploaded_at") ) } diff --git a/src/all/nhentaicom/src/eu/kanade/tachiyomi/extension/all/nhentaicom/NHentaiComFactory.kt b/src/all/nhentaicom/src/eu/kanade/tachiyomi/extension/all/nhentaicom/NHentaiComFactory.kt new file mode 100644 index 000000000..b57417df3 --- /dev/null +++ b/src/all/nhentaicom/src/eu/kanade/tachiyomi/extension/all/nhentaicom/NHentaiComFactory.kt @@ -0,0 +1,14 @@ +package eu.kanade.tachiyomi.extension.all.nhentaicom + +import eu.kanade.tachiyomi.annotations.Nsfw +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceFactory + +@Nsfw +class NHentaiComFactory : SourceFactory { + override fun createSources(): List = languages.map { NHentaiCom(it) } +} + +private val languages = listOf( + "all", "en", "zh", "ja", "other", "eo", "cz", "ar", "sk" +)