Migrates nepnep multisrc to kotlinx.serialization (#8494)

* Fix chapter number issue

* Migration to kotlinx.serialization
This commit is contained in:
Arraiment 2021-08-10 00:44:19 +08:00 committed by GitHub
parent 76ff5c9090
commit b98ede3e0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 49 deletions

View File

@ -1,12 +1,5 @@
package eu.kanade.tachiyomi.multisrc.nepnep package eu.kanade.tachiyomi.multisrc.nepnep
import com.github.salomonbrys.kotson.fromJson
import com.github.salomonbrys.kotson.get
import com.github.salomonbrys.kotson.nullString
import com.github.salomonbrys.kotson.string
import com.google.gson.GsonBuilder
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
@ -17,10 +10,18 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Headers import okhttp3.Headers
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -40,10 +41,20 @@ abstract class NepNep(
.add("Referer", "$baseUrl/") .add("Referer", "$baseUrl/")
.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/77.0") .add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/77.0")
private val gson = GsonBuilder().setLenient().create() private val json: Json by injectLazy()
private lateinit var directory: List<JsonElement> private lateinit var directory: List<JsonElement>
// Convenience functions to shorten later code
/** Returns value corresponding to given key as a string, or null */
private fun JsonElement.getString(key: String): String? {
return this.jsonObject[key]!!.jsonPrimitive.contentOrNull
}
/** Returns value corresponding to given key as a JsonArray */
private fun JsonElement.getArray(key: String): JsonArray {
return this.jsonObject[key]!!.jsonArray
}
// Popular // Popular
override fun fetchPopularManga(page: Int): Observable<MangasPage> { override fun fetchPopularManga(page: Int): Observable<MangasPage> {
@ -63,15 +74,15 @@ abstract class NepNep(
} }
// don't use ";" for substringBefore() ! // don't use ";" for substringBefore() !
private fun directoryFromResponse(response: Response): String { private fun directoryFromResponse(response: Response): JsonArray {
return response.asJsoup().select("script:containsData(MainFunction)").first().data() val str = response.asJsoup().select("script:containsData(MainFunction)").first().data()
.substringAfter("vm.Directory = ").substringBefore("vm.GetIntValue").trim() .substringAfter("vm.Directory = ").substringBefore("vm.GetIntValue").trim()
.replace(";", " ") .replace(";", " ")
return json.parseToJsonElement(str).jsonArray
} }
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
directory = gson.fromJson<JsonArray>(directoryFromResponse(response)) directory = directoryFromResponse(response).sortedByDescending { it.getString("v") }
.sortedByDescending { it["v"].string }
return parseDirectory(1) return parseDirectory(1)
} }
@ -82,9 +93,9 @@ abstract class NepNep(
for (i in (((page - 1) * 24)..endRange)) { for (i in (((page - 1) * 24)..endRange)) {
mangas.add( mangas.add(
SManga.create().apply { SManga.create().apply {
title = directory[i]["s"].string title = directory[i].getString("s")!!
url = "/manga/${directory[i]["i"].string}" url = "/manga/${directory[i].getString("i")}"
thumbnail_url = "https://cover.nep.li/cover/${directory[i]["i"].string}.jpg" thumbnail_url = "https://cover.nep.li/cover/${directory[i].getString("i")}.jpg"
} }
) )
} }
@ -108,8 +119,7 @@ abstract class NepNep(
override fun latestUpdatesRequest(page: Int): Request = popularMangaRequest(1) override fun latestUpdatesRequest(page: Int): Request = popularMangaRequest(1)
override fun latestUpdatesParse(response: Response): MangasPage { override fun latestUpdatesParse(response: Response): MangasPage {
directory = gson.fromJson<JsonArray>(directoryFromResponse(response)) directory = directoryFromResponse(response).sortedByDescending { it.getString("lt") }
.sortedByDescending { it["lt"].string }
return parseDirectory(1) return parseDirectory(1)
} }
@ -131,10 +141,14 @@ abstract class NepNep(
private fun searchMangaParse(response: Response, query: String, filters: FilterList): MangasPage { private fun searchMangaParse(response: Response, query: String, filters: FilterList): MangasPage {
val trimmedQuery = query.trim() val trimmedQuery = query.trim()
directory = gson.fromJson<JsonArray>(directoryFromResponse(response)) directory = directoryFromResponse(response)
.filter { .filter {
it["s"].string.contains(trimmedQuery, ignoreCase = true) or // Comparing query with display name
it["al"].asJsonArray.any { altName -> altName.string.contains(trimmedQuery, ignoreCase = true) } it.getString("s")!!.contains(trimmedQuery, ignoreCase = true) or
// Comparing query with list of alternate names
it.getArray("al").any { altName ->
altName.jsonPrimitive.content.contains(trimmedQuery, ignoreCase = true)
}
} }
val genres = mutableListOf<String>() val genres = mutableListOf<String>()
@ -149,20 +163,24 @@ abstract class NepNep(
else -> "s" else -> "s"
} }
directory = if (filter.state?.ascending != true) { directory = if (filter.state?.ascending != true) {
directory.sortedByDescending { it[sortBy].string } directory.sortedByDescending { it.getString(sortBy) }
} else { } else {
directory.sortedByDescending { it[sortBy].string }.reversed() directory.sortedByDescending { it.getString(sortBy) }.reversed()
} }
} }
is SelectField -> if (filter.state != 0) directory = when (filter.name) { is SelectField -> if (filter.state != 0) directory = when (filter.name) {
"Scan Status" -> directory.filter { it["ss"].string.contains(filter.values[filter.state], ignoreCase = true) } "Scan Status" -> directory.filter { it.getString("ss")!!.contains(filter.values[filter.state], ignoreCase = true) }
"Publish Status" -> directory.filter { it["ps"].string.contains(filter.values[filter.state], ignoreCase = true) } "Publish Status" -> directory.filter { it.getString("ps")!!.contains(filter.values[filter.state], ignoreCase = true) }
"Type" -> directory.filter { it["t"].string.contains(filter.values[filter.state], ignoreCase = true) } "Type" -> directory.filter { it.getString("t")!!.contains(filter.values[filter.state], ignoreCase = true) }
"Translation" -> directory.filter { it["o"].string.contains("yes", ignoreCase = true) } "Translation" -> directory.filter { it.getString("o")!!.contains("yes", ignoreCase = true) }
else -> directory else -> directory
} }
is YearField -> if (filter.state.isNotEmpty()) directory = directory.filter { it["y"].string.contains(filter.state) } is YearField -> if (filter.state.isNotEmpty()) directory = directory.filter { it.getString("y")!!.contains(filter.state) }
is AuthorField -> if (filter.state.isNotEmpty()) directory = directory.filter { e -> e["a"].asJsonArray.any { it.string.contains(filter.state, ignoreCase = true) } } is AuthorField -> if (filter.state.isNotEmpty()) directory = directory.filter { e ->
e.getArray("a").any {
it.jsonPrimitive.content.contains(filter.state, ignoreCase = true)
}
}
is GenreList -> filter.state.forEach { genre -> is GenreList -> filter.state.forEach { genre ->
when (genre.state) { when (genre.state) {
Filter.TriState.STATE_INCLUDE -> genres.add(genre.name) Filter.TriState.STATE_INCLUDE -> genres.add(genre.name)
@ -171,8 +189,16 @@ abstract class NepNep(
} }
} }
} }
if (genres.isNotEmpty()) genres.map { genre -> directory = directory.filter { e -> e["g"].asJsonArray.any { it.string.contains(genre, ignoreCase = true) } } } if (genres.isNotEmpty()) genres.map { genre ->
if (genresNo.isNotEmpty()) genresNo.map { genre -> directory = directory.filterNot { e -> e["g"].asJsonArray.any { it.string.contains(genre, ignoreCase = true) } } } directory = directory.filter { e ->
e.getArray("g").any { it.jsonPrimitive.content.contains(genre, ignoreCase = true) }
}
}
if (genresNo.isNotEmpty()) genresNo.map { genre ->
directory = directory.filterNot { e ->
e.getArray("g").any { it.jsonPrimitive.content.contains(genre, ignoreCase = true) }
}
}
return parseDirectory(1) return parseDirectory(1)
} }
@ -206,7 +232,7 @@ abstract class NepNep(
// add alternative name to manga description // add alternative name to manga description
val altName = "Alternative Name: " val altName = "Alternative Name: "
info.select("li.list-group-item:has(span:contains(Alter))").firstOrNull()?.ownText()?.let { info.select("li.list-group-item:has(span:contains(Alter))").firstOrNull()?.ownText()?.let {
if (it.isEmpty().not() && it !="N/A") { if (it.isEmpty().not() && it != "N/A") {
description += when { description += when {
description!!.isEmpty() -> altName + it description!!.isEmpty() -> altName + it
else -> "\n\n$altName" + it else -> "\n\n$altName" + it
@ -242,10 +268,14 @@ abstract class NepNep(
private val chapterImageRegex = Regex("""^0+""") private val chapterImageRegex = Regex("""^0+""")
private fun chapterImage(e: String, cleanString: Boolean = false): String { private fun chapterImage(e: String, cleanString: Boolean = false): String {
// cleanString will result in an empty string if chapter number is 0, hence the else if below
val a = e.substring(1, e.length - 1).let { if (cleanString) it.replace(chapterImageRegex, "") else it } val a = e.substring(1, e.length - 1).let { if (cleanString) it.replace(chapterImageRegex, "") else it }
// If b is not zero, indicates chapter has decimal numbering
val b = e.substring(e.length - 1).toInt() val b = e.substring(e.length - 1).toInt()
return if (b == 0) { return if (b == 0 && a.isNotEmpty()) {
a a
} else if (b == 0 && a.isEmpty()) {
"0"
} else { } else {
"$a.$b" "$a.$b"
} }
@ -255,13 +285,13 @@ abstract class NepNep(
val vmChapters = response.asJsoup().select("script:containsData(MainFunction)").first().data() val vmChapters = response.asJsoup().select("script:containsData(MainFunction)").first().data()
.substringAfter("vm.Chapters = ").substringBefore(";") .substringAfter("vm.Chapters = ").substringBefore(";")
return gson.fromJson<JsonArray>(vmChapters).map { json -> return json.parseToJsonElement(vmChapters).jsonArray.map { json ->
val indexChapter = json["Chapter"].string val indexChapter = json.getString("Chapter")!!
SChapter.create().apply { SChapter.create().apply {
name = json["ChapterName"].nullString.let { if (it.isNullOrEmpty()) "${json["Type"].string} ${chapterImage(indexChapter, true)}" else it } name = json.getString("ChapterName").let { if (it.isNullOrEmpty()) "${json.getString("Type")} ${chapterImage(indexChapter, true)}" else it }
url = "/read-online/" + response.request.url.toString().substringAfter("/manga/") + chapterURLEncode(indexChapter) url = "/read-online/" + response.request.url.toString().substringAfter("/manga/") + chapterURLEncode(indexChapter)
date_upload = try { date_upload = try {
json["Date"].nullString?.let { dateFormat.parse("$it +0600")?.time } ?: 0 json.getString("Date").let { dateFormat.parse("$it +0600")?.time } ?: 0
} catch (_: Exception) { } catch (_: Exception) {
0L 0L
} }
@ -274,24 +304,24 @@ abstract class NepNep(
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val document = response.asJsoup() val document = response.asJsoup()
val script = document.select("script:containsData(MainFunction)").first().data() val script = document.select("script:containsData(MainFunction)").first().data()
val curChapter = gson.fromJson<JsonElement>(script.substringAfter("vm.CurChapter = ").substringBefore(";")) val curChapter = json.parseToJsonElement(script.substringAfter("vm.CurChapter = ").substringBefore(";")).jsonObject
val pageTotal = curChapter["Page"].string.toInt() val pageTotal = curChapter.getString("Page")!!.toInt()
val host = "https://" + val host = "https://" +
script script
.substringAfter("vm.CurPathName = \"", "") .substringAfter("vm.CurPathName = \"", "")
.substringBefore("\"") .substringBefore("\"")
.also { .also {
if (it.isEmpty()) if (it.isEmpty())
throw Exception("$name is overloaded and blocking Tachiyomi right now. Wait for unblock.") throw Exception("$name is overloaded and blocking Tachiyomi right now. Wait for unblock.")
} }
val titleURI = script.substringAfter("vm.IndexName = \"").substringBefore("\"") val titleURI = script.substringAfter("vm.IndexName = \"").substringBefore("\"")
val seasonURI = curChapter["Directory"].string val seasonURI = curChapter.getString("Directory")!!
.let { if (it.isEmpty()) "" else "$it/" } .let { if (it.isEmpty()) "" else "$it/" }
val path = "$host/manga/$titleURI/$seasonURI" val path = "$host/manga/$titleURI/$seasonURI"
val chNum = chapterImage(curChapter["Chapter"].string) val chNum = chapterImage(curChapter.getString("Chapter")!!)
return IntRange(1, pageTotal).mapIndexed { i, _ -> return IntRange(1, pageTotal).mapIndexed { i, _ ->
val imageNum = (i + 1).toString().let { "000$it" }.let { it.substring(it.length - 3) } val imageNum = (i + 1).toString().let { "000$it" }.let { it.substring(it.length - 3) }
@ -362,4 +392,4 @@ abstract class NepNep(
Genre("Yaoi"), Genre("Yaoi"),
Genre("Yuri") Genre("Yuri")
) )
} }

View File

@ -9,7 +9,7 @@ class NepNepGenerator : ThemeSourceGenerator {
override val themeClass = "NepNep" override val themeClass = "NepNep"
override val baseVersionCode: Int = 4 override val baseVersionCode: Int = 5
override val sources = listOf( override val sources = listOf(
SingleLang("MangaSee", "https://mangasee123.com", "en", overrideVersionCode = 20), SingleLang("MangaSee", "https://mangasee123.com", "en", overrideVersionCode = 20),