From e8b6a225aa8a5696ced5bdacebc8b5bee4d14f15 Mon Sep 17 00:00:00 2001 From: Alessandro Jean Date: Wed, 2 Jun 2021 07:40:59 -0300 Subject: [PATCH] Replace Gson usage with kotlinx.serialization in some sources (#7372) * Replace Gson usage with kotlinx.serialization in some sources. * Add kotlinx.serialization to common-dependencies. * Add missing dependencies. --- common-dependencies.gradle | 2 + src/all/mangaplus/build.gradle | 6 +- .../all/mangaplus/MangaPlusFactory.kt | 1 - src/pt/bruttal/build.gradle | 4 +- .../tachiyomi/extension/pt/bruttal/Bruttal.kt | 103 ++++++------- .../extension/pt/bruttal/BruttalDto.kt | 42 ++++++ src/pt/hipercool/build.gradle | 3 +- .../extension/pt/hipercool/Hipercool.kt | 137 ++++++++---------- .../extension/pt/hipercool/HipercoolDto.kt | 30 ++++ src/zh/dmzj/build.gradle | 3 +- 10 files changed, 197 insertions(+), 134 deletions(-) create mode 100644 src/pt/bruttal/src/eu/kanade/tachiyomi/extension/pt/bruttal/BruttalDto.kt create mode 100644 src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/HipercoolDto.kt diff --git a/common-dependencies.gradle b/common-dependencies.gradle index 0e652f81b..5fda67310 100644 --- a/common-dependencies.gradle +++ b/common-dependencies.gradle @@ -12,6 +12,8 @@ dependencies { compileOnly 'org.jsoup:jsoup:1.13.1' compileOnly 'com.google.code.gson:gson:2.8.6' compileOnly 'com.github.salomonbrys.kotson:kotson:2.5.0' + compileOnly 'org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.2.0' + compileOnly 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.0' implementation project(":annotations") compileOnly project(':duktape-stub') diff --git a/src/all/mangaplus/build.gradle b/src/all/mangaplus/build.gradle index bdf041b4a..983b0c894 100644 --- a/src/all/mangaplus/build.gradle +++ b/src/all/mangaplus/build.gradle @@ -6,12 +6,8 @@ ext { extName = 'MANGA Plus by SHUEISHA' pkgNameSuffix = 'all.mangaplus' extClass = '.MangaPlusFactory' - extVersionCode = 18 + extVersionCode = 19 libVersion = '1.2' } -dependencies { - implementation 'org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.2.0' -} - apply from: "$rootDir/common.gradle" diff --git a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusFactory.kt b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusFactory.kt index f775e6198..bb0f6b26b 100644 --- a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusFactory.kt +++ b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusFactory.kt @@ -18,4 +18,3 @@ class MangaPlusIndonesian : MangaPlus("id", "eng", Language.INDONESIAN) class MangaPlusPortuguese : MangaPlus("pt-BR", "eng", Language.PORTUGUESE_BR) class MangaPlusSpanish : MangaPlus("es", "esp", Language.SPANISH) class MangaPlusThai : MangaPlus("th", "eng", Language.THAI) - diff --git a/src/pt/bruttal/build.gradle b/src/pt/bruttal/build.gradle index ab3eacca6..79def1be9 100644 --- a/src/pt/bruttal/build.gradle +++ b/src/pt/bruttal/build.gradle @@ -1,15 +1,15 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' ext { extName = 'Bruttal' pkgNameSuffix = 'pt.bruttal' extClass = '.Bruttal' - extVersionCode = 2 + extVersionCode = 3 libVersion = '1.2' } - dependencies { implementation project(':lib-ratelimit') } diff --git a/src/pt/bruttal/src/eu/kanade/tachiyomi/extension/pt/bruttal/Bruttal.kt b/src/pt/bruttal/src/eu/kanade/tachiyomi/extension/pt/bruttal/Bruttal.kt index e4b33647d..ce552f316 100644 --- a/src/pt/bruttal/src/eu/kanade/tachiyomi/extension/pt/bruttal/Bruttal.kt +++ b/src/pt/bruttal/src/eu/kanade/tachiyomi/extension/pt/bruttal/Bruttal.kt @@ -1,12 +1,5 @@ package eu.kanade.tachiyomi.extension.pt.bruttal -import com.github.salomonbrys.kotson.array -import com.github.salomonbrys.kotson.get -import com.github.salomonbrys.kotson.obj -import com.github.salomonbrys.kotson.string -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import com.google.gson.JsonParser import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.asObservableSuccess @@ -16,11 +9,14 @@ 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.decodeFromString +import kotlinx.serialization.json.Json import okhttp3.Headers import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import rx.Observable +import uy.kohesive.injekt.injectLazy import java.util.concurrent.TimeUnit class Bruttal : HttpSource() { @@ -41,6 +37,8 @@ class Bruttal : HttpSource() { .add("Referer", "$baseUrl/bruttal/") .add("User-Agent", USER_AGENT) + private val json: Json by injectLazy() + override fun popularMangaRequest(page: Int): Request { val newHeaders = headersBuilder() .add("Accept", "application/json, text/plain, */*") @@ -50,19 +48,17 @@ class Bruttal : HttpSource() { } override fun popularMangaParse(response: Response): MangasPage { - val json = response.asJson().obj + val homeDto = json.decodeFromString(response.body!!.string()) - val titles = json["list"].array.map { jsonEl -> - popularMangaFromObject(jsonEl.obj) - } + val titles = homeDto.list.map(::popularMangaFromObject) return MangasPage(titles, false) } - private fun popularMangaFromObject(obj: JsonObject): SManga = SManga.create().apply { - title = obj["title"].string - thumbnail_url = "$baseUrl/bruttal/" + obj["image_mobile"].string.removePrefix("./") - url = "/bruttal" + obj["url"].string + private fun popularMangaFromObject(comicbook: BruttalComicBookDto): SManga = SManga.create().apply { + title = comicbook.title + thumbnail_url = "$baseUrl/bruttal/" + comicbook.imageMobile.removePrefix("./") + url = "/bruttal" + comicbook.url } override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { @@ -96,20 +92,20 @@ class Bruttal : HttpSource() { } override fun mangaDetailsParse(response: Response): SManga { - val json = response.asJson().array + val comicBooks = json.decodeFromString>(response.body!!.string()) - val titleUrl = response.request.header("Referer")!!.substringAfter("/bruttal") - val titleObj = json.first { it.obj["url"].string == titleUrl }.obj - val soonText = titleObj["soon_text"].string + val comicBookUrl = response.request.header("Referer")!! + .substringAfter("/bruttal") + val currentComicBook = comicBooks.first { it.url == comicBookUrl } return SManga.create().apply { - title = titleObj["title"].string - thumbnail_url = "$baseUrl/bruttal/" + titleObj["image_mobile"].string.removePrefix("./") - description = titleObj["synopsis"].string + - (if (soonText.isEmpty()) "" else "\n\n$soonText") - artist = titleObj["illustrator"].string - author = titleObj["author"].string - genre = titleObj["keywords"].string.replace("; ", ", ") + title = currentComicBook.title + thumbnail_url = "$baseUrl/bruttal/" + currentComicBook.imageMobile.removePrefix("./") + description = currentComicBook.synopsis + + (if (currentComicBook.soonText.isEmpty()) "" else "\n\n${currentComicBook.soonText}") + artist = currentComicBook.illustrator + author = currentComicBook.author + genre = currentComicBook.keywords.replace("; ", ", ") status = SManga.ONGOING } } @@ -118,21 +114,24 @@ class Bruttal : HttpSource() { override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga) override fun chapterListParse(response: Response): List { - val json = response.asJson().array + val comicBooks = json.decodeFromString>(response.body!!.string()) - val titleUrl = response.request.header("Referer")!!.substringAfter("/bruttal") - val title = json.first { it.obj["url"].string == titleUrl }.obj + val comicBookUrl = response.request.header("Referer")!! + .substringAfter("/bruttal") + val currentComicBook = comicBooks.first { it.url == comicBookUrl } - return title["seasons"].array - .flatMap { it.obj["chapters"].array } - .map { jsonEl -> chapterFromObject(jsonEl.obj) } + return currentComicBook.seasons + .flatMap { it.chapters } + .map(::chapterFromObject) .reversed() } - private fun chapterFromObject(obj: JsonObject): SChapter = SChapter.create().apply { - name = obj["title"].string - chapter_number = obj["share_title"].string.removePrefix("Capítulo ").toFloatOrNull() ?: -1f - url = "/bruttal" + obj["url"].string + private fun chapterFromObject(chapter: BruttalChapterDto): SChapter = SChapter.create().apply { + name = chapter.title + chapter_number = chapter.shareTitle + .removePrefix("Capítulo ") + .toFloatOrNull() ?: -1f + url = "/bruttal" + chapter.url } override fun pageListRequest(chapter: SChapter): Request { @@ -145,22 +144,28 @@ class Bruttal : HttpSource() { } override fun pageListParse(response: Response): List { - val json = response.asJson().array + val comicBooks = json.decodeFromString>(response.body!!.string()) val chapterUrl = response.request.header("Referer")!! - val titleSlug = chapterUrl.substringAfter("bruttal/").substringBefore("/") - val season = chapterUrl.substringAfter("temporada-").substringBefore("/").toInt() - val chapter = chapterUrl.substringAfter("capitulo-") + val comicBookSlug = chapterUrl + .substringAfter("bruttal/") + .substringBefore("/") + val seasonNumber = chapterUrl + .substringAfter("temporada-") + .substringBefore("/") + val chapterNumber = chapterUrl.substringAfter("capitulo-") - val titleObj = json.first { it.obj["url"].string == "/$titleSlug" }.obj - val seasonObj = titleObj["seasons"].array[season - 1].obj - val chapterObj = seasonObj["chapters"].array.first { - it.obj["alias"].string.substringAfter("-") == chapter + val currentComicBook = comicBooks.first { it.url == "/$comicBookSlug" } + val currentSeason = currentComicBook.seasons.first { + it.alias.substringAfter("-") == seasonNumber + } + val currentChapter = currentSeason.chapters.first { + it.alias.substringAfter("-") == chapterNumber } - return chapterObj["images"].array - .mapIndexed { i, jsonEl -> - val imageUrl = "$baseUrl/bruttal/" + jsonEl.obj["image"].string.removePrefix("./") + return currentChapter.images + .mapIndexed { i, bruttalImage -> + val imageUrl = "$baseUrl/bruttal/" + bruttalImage.image.removePrefix("./") Page(i, chapterUrl, imageUrl) } } @@ -184,10 +189,8 @@ class Bruttal : HttpSource() { override fun latestUpdatesParse(response: Response): MangasPage = throw UnsupportedOperationException("Not used") - private fun Response.asJson(): JsonElement = JsonParser.parseString(body!!.string()) - companion object { private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + - "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36" + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36" } } diff --git a/src/pt/bruttal/src/eu/kanade/tachiyomi/extension/pt/bruttal/BruttalDto.kt b/src/pt/bruttal/src/eu/kanade/tachiyomi/extension/pt/bruttal/BruttalDto.kt new file mode 100644 index 000000000..c1088eb2d --- /dev/null +++ b/src/pt/bruttal/src/eu/kanade/tachiyomi/extension/pt/bruttal/BruttalDto.kt @@ -0,0 +1,42 @@ +package eu.kanade.tachiyomi.extension.pt.bruttal + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class BruttalHomeDto( + val list: List = emptyList() +) + +@Serializable +data class BruttalComicBookDto( + val author: String, + val illustrator: String, + @SerialName("image_mobile") val imageMobile: String, + val keywords: String, + val seasons: List = emptyList(), + @SerialName("soon_text") val soonText: String = "", + val synopsis: String, + val title: String, + val url: String +) + +@Serializable +data class BruttalSeasonDto( + val alias: String, + val chapters: List = emptyList() +) + +@Serializable +data class BruttalChapterDto( + val alias: String, + val images: List = emptyList(), + @SerialName("share_title") val shareTitle: String, + val title: String, + val url: String +) + +@Serializable +data class BruttalImageDto( + val image: String +) diff --git a/src/pt/hipercool/build.gradle b/src/pt/hipercool/build.gradle index 8acd7b957..f01503d28 100644 --- a/src/pt/hipercool/build.gradle +++ b/src/pt/hipercool/build.gradle @@ -1,11 +1,12 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' ext { extName = 'HipercooL' pkgNameSuffix = 'pt.hipercool' extClass = '.Hipercool' - extVersionCode = 6 + extVersionCode = 7 libVersion = '1.2' containsNsfw = true } diff --git a/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/Hipercool.kt b/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/Hipercool.kt index 8858f5e51..56ed3e822 100644 --- a/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/Hipercool.kt +++ b/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/Hipercool.kt @@ -1,17 +1,7 @@ package eu.kanade.tachiyomi.extension.pt.hipercool -import com.github.salomonbrys.kotson.array -import com.github.salomonbrys.kotson.get -import com.github.salomonbrys.kotson.int -import com.github.salomonbrys.kotson.jsonObject -import com.github.salomonbrys.kotson.obj -import com.github.salomonbrys.kotson.string -import com.google.gson.JsonArray -import com.google.gson.JsonElement -import com.google.gson.JsonObject -import com.google.gson.JsonParser import eu.kanade.tachiyomi.annotations.Nsfw -import eu.kanade.tachiyomi.lib.ratelimit.RateLimitInterceptor +import eu.kanade.tachiyomi.lib.ratelimit.SpecificHostRateLimitInterceptor import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.asObservableSuccess @@ -21,7 +11,13 @@ 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.decodeFromString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put import okhttp3.Headers +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient @@ -29,10 +25,10 @@ import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import rx.Observable +import uy.kohesive.injekt.injectLazy import java.text.ParseException import java.text.SimpleDateFormat import java.util.Locale -import java.util.concurrent.TimeUnit @Nsfw class Hipercool : HttpSource() { @@ -49,7 +45,8 @@ class Hipercool : HttpSource() { override val supportsLatest = true override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .addInterceptor(RateLimitInterceptor(1, 1, TimeUnit.SECONDS)) + .addInterceptor(SpecificHostRateLimitInterceptor(baseUrl.toHttpUrl(), 1)) + .addInterceptor(SpecificHostRateLimitInterceptor(STATIC_URL.toHttpUrl(), 2)) .build() override fun headersBuilder(): Headers.Builder = Headers.Builder() @@ -57,31 +54,27 @@ class Hipercool : HttpSource() { .add("Referer", baseUrl) .add("X-Requested-With", "XMLHttpRequest") - private fun genericMangaListParse(response: Response): MangasPage { - val result = response.asJson().array + private val json: Json by injectLazy() - if (result.size() == 0) + private fun genericMangaListParse(response: Response): MangasPage { + val chapters = json.decodeFromString>(response.body!!.string()) + + if (chapters.isEmpty()) return MangasPage(emptyList(), false) - val mangaList = result - .map { genericMangaFromObject(it.obj) } + val mangaList = chapters + .map(::genericMangaFromObject) .distinctBy { it.title } - val hasNextPage = result.size() == DEFAULT_COUNT + val hasNextPage = chapters.size == DEFAULT_COUNT return MangasPage(mangaList, hasNextPage) } - private fun genericMangaFromObject(obj: JsonObject): SManga { - val book = obj["_book"].obj - val bookSlug = book["slug"].string - val bookRevision = book["revision"]?.int ?: 1 - - return SManga.create().apply { - title = book["title"].string - thumbnail_url = bookSlug.toThumbnailUrl(bookRevision) - url = "/books/$bookSlug" - } + private fun genericMangaFromObject(chapter: HipercoolChapterDto): SManga = SManga.create().apply { + title = chapter.book!!.title + thumbnail_url = chapter.book.slug.toThumbnailUrl(chapter.book.revision) + url = "/books/" + chapter.book.slug } // The source does not have popular mangas, so use latest instead. @@ -100,12 +93,12 @@ class Hipercool : HttpSource() { val mediaType = "application/json; charset=utf-8".toMediaTypeOrNull() // Create json body. - val json = jsonObject( - "start" to (page - 1) * DEFAULT_COUNT, - "count" to DEFAULT_COUNT, - "text" to query, - "type" to "text" - ) + val json = buildJsonObject { + put("start", (page - 1) * DEFAULT_COUNT) + put("content", DEFAULT_COUNT) + put("text", query) + put("type", "text") + } val body = json.toString().toRequestBody(mediaType) @@ -130,27 +123,27 @@ class Hipercool : HttpSource() { } override fun mangaDetailsParse(response: Response): SManga { - val result = response.asJson().obj + val book = json.decodeFromString(response.body!!.string()) - val artists = result["tags"].array - .filter { it["label"].string == "Artista" } - .flatMap { it["values"].array } - .joinToString("; ") { it["label"].string } + val artists = book.tags + .filter { it.label == "Artista" } + .flatMap { it.values } + .joinToString("; ") { it.label } - val authors = result["tags"].array - .filter { it["label"].string == "Autor" } - .flatMap { it["values"].array } - .joinToString("; ") { it["label"].string } + val authors = book.tags + .filter { it.label == "Autor" } + .flatMap { it.values } + .joinToString("; ") { it.label } - val tags = result["tags"].array - .filter { it["label"].string == "Tags" } - .flatMap { it["values"].array } - .joinToString(", ") { it["label"].string } + val tags = book.tags + .filter { it.label == "Tags" } + .flatMap { it.values } + .joinToString { it.label } return SManga.create().apply { - title = result["title"].string - thumbnail_url = result["slug"].string.toThumbnailUrl(result["revision"].int) - description = result["synopsis"]?.string ?: "" + title = book.title + thumbnail_url = book.slug.toThumbnailUrl(book.revision) + description = book.synopsis.orEmpty() artist = artists author = authors genre = tags @@ -161,31 +154,31 @@ class Hipercool : HttpSource() { override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga) override fun chapterListParse(response: Response): List { - val result = response.asJson().obj + val book = json.decodeFromString(response.body!!.string()) - if (!result["chapters"]!!.isJsonArray) + if (book.chapters is JsonPrimitive) return emptyList() - return result["chapters"].array - .map { chapterListItemParse(result, it.obj) } + return json.decodeFromString>(book.chapters.toString()) + .map { chapterListItemParse(book, it) } .reversed() } - private fun chapterListItemParse(book: JsonObject, obj: JsonObject): SChapter = SChapter.create().apply { - name = obj["title"].string - chapter_number = obj["title"].string.toFloatOrNull() ?: -1f - // The property is written wrong. - date_upload = DATE_FORMATTER.tryParseTime(obj["publishied_at"].string) + private fun chapterListItemParse(book: HipercoolBookDto, chapter: HipercoolChapterDto): SChapter = + SChapter.create().apply { + name = "Cap. " + chapter.title + chapter_number = chapter.title.toFloatOrNull() ?: -1f + date_upload = chapter.publishedAt.toDate() - val fullUrl = "$baseUrl/books".toHttpUrlOrNull()!!.newBuilder() - .addPathSegment(book["slug"].string) - .addPathSegment(obj["slug"].string) - .addQueryParameter("images", obj["images"].int.toString()) - .addQueryParameter("revision", book["revision"].int.toString()) - .toString() + val fullUrl = "$baseUrl/books".toHttpUrlOrNull()!!.newBuilder() + .addPathSegment(book.slug) + .addPathSegment(chapter.slug) + .addQueryParameter("images", chapter.images.toString()) + .addQueryParameter("revision", book.revision.toString()) + .toString() - setUrlWithoutDomain(fullUrl) - } + setUrlWithoutDomain(fullUrl) + } override fun fetchPageList(chapter: SChapter): Observable> { val chapterUrl = (baseUrl + chapter.url).toHttpUrlOrNull()!! @@ -228,9 +221,9 @@ class Hipercool : HttpSource() { return GET(page.imageUrl!!, newHeaders) } - private fun SimpleDateFormat.tryParseTime(date: String): Long { + private fun String.toDate(): Long { return try { - parse(date.substringBefore("T"))?.time ?: 0L + DATE_FORMATTER.parse(substringBefore("T"))?.time ?: 0L } catch (e: ParseException) { 0L } @@ -243,13 +236,11 @@ class Hipercool : HttpSource() { .addQueryParameter("revision", revision.toString()) .toString() - private fun Response.asJson(): JsonElement = JsonParser.parseString(body!!.string()) - companion object { private const val STATIC_URL = "https://static.hiper.cool" private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + - "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36" + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36" private const val DEFAULT_COUNT = 40 diff --git a/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/HipercoolDto.kt b/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/HipercoolDto.kt new file mode 100644 index 000000000..31ebd048e --- /dev/null +++ b/src/pt/hipercool/src/eu/kanade/tachiyomi/extension/pt/hipercool/HipercoolDto.kt @@ -0,0 +1,30 @@ +package eu.kanade.tachiyomi.extension.pt.hipercool + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement + +@Serializable +data class HipercoolBookDto( + val chapters: JsonElement, + val revision: Int = 1, + val slug: String, + val synopsis: String? = null, + val tags: List = emptyList(), + val title: String +) + +@Serializable +data class HipercoolTagDto( + val label: String, + val values: List = emptyList() +) + +@Serializable +data class HipercoolChapterDto( + @SerialName("_book") val book: HipercoolBookDto? = null, + val images: Int = 0, + @SerialName("publishied_at") val publishedAt: String, + val slug: String, + val title: String +) diff --git a/src/zh/dmzj/build.gradle b/src/zh/dmzj/build.gradle index 54748c9f9..b662c7aba 100644 --- a/src/zh/dmzj/build.gradle +++ b/src/zh/dmzj/build.gradle @@ -6,13 +6,12 @@ ext { extName = 'Dmzj' pkgNameSuffix = 'zh.dmzj' extClass = '.Dmzj' - extVersionCode = 17 + extVersionCode = 18 libVersion = '1.2' } dependencies { implementation project(':lib-ratelimit') - implementation 'org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.2.0' } apply from: "$rootDir/common.gradle"