Migrates nepnep multisrc to kotlinx.serialization (#8494)
* Fix chapter number issue * Migration to kotlinx.serialization
This commit is contained in:
parent
76ff5c9090
commit
b98ede3e0a
|
@ -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")
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -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),
|
||||||
|
|
Loading…
Reference in New Issue