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.
This commit is contained in:
Alessandro Jean 2021-06-02 07:40:59 -03:00 committed by GitHub
parent 3d0119f2b8
commit e8b6a225aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 197 additions and 134 deletions

View File

@ -12,6 +12,8 @@ dependencies {
compileOnly 'org.jsoup:jsoup:1.13.1' compileOnly 'org.jsoup:jsoup:1.13.1'
compileOnly 'com.google.code.gson:gson:2.8.6' compileOnly 'com.google.code.gson:gson:2.8.6'
compileOnly 'com.github.salomonbrys.kotson:kotson:2.5.0' 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") implementation project(":annotations")
compileOnly project(':duktape-stub') compileOnly project(':duktape-stub')

View File

@ -6,12 +6,8 @@ ext {
extName = 'MANGA Plus by SHUEISHA' extName = 'MANGA Plus by SHUEISHA'
pkgNameSuffix = 'all.mangaplus' pkgNameSuffix = 'all.mangaplus'
extClass = '.MangaPlusFactory' extClass = '.MangaPlusFactory'
extVersionCode = 18 extVersionCode = 19
libVersion = '1.2' libVersion = '1.2'
} }
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.2.0'
}
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -18,4 +18,3 @@ class MangaPlusIndonesian : MangaPlus("id", "eng", Language.INDONESIAN)
class MangaPlusPortuguese : MangaPlus("pt-BR", "eng", Language.PORTUGUESE_BR) class MangaPlusPortuguese : MangaPlus("pt-BR", "eng", Language.PORTUGUESE_BR)
class MangaPlusSpanish : MangaPlus("es", "esp", Language.SPANISH) class MangaPlusSpanish : MangaPlus("es", "esp", Language.SPANISH)
class MangaPlusThai : MangaPlus("th", "eng", Language.THAI) class MangaPlusThai : MangaPlus("th", "eng", Language.THAI)

View File

@ -1,15 +1,15 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'Bruttal' extName = 'Bruttal'
pkgNameSuffix = 'pt.bruttal' pkgNameSuffix = 'pt.bruttal'
extClass = '.Bruttal' extClass = '.Bruttal'
extVersionCode = 2 extVersionCode = 3
libVersion = '1.2' libVersion = '1.2'
} }
dependencies { dependencies {
implementation project(':lib-ratelimit') implementation project(':lib-ratelimit')
} }

View File

@ -1,12 +1,5 @@
package eu.kanade.tachiyomi.extension.pt.bruttal 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.lib.ratelimit.RateLimitInterceptor
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess 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.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 kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
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.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class Bruttal : HttpSource() { class Bruttal : HttpSource() {
@ -41,6 +37,8 @@ class Bruttal : HttpSource() {
.add("Referer", "$baseUrl/bruttal/") .add("Referer", "$baseUrl/bruttal/")
.add("User-Agent", USER_AGENT) .add("User-Agent", USER_AGENT)
private val json: Json by injectLazy()
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
val newHeaders = headersBuilder() val newHeaders = headersBuilder()
.add("Accept", "application/json, text/plain, */*") .add("Accept", "application/json, text/plain, */*")
@ -50,19 +48,17 @@ class Bruttal : HttpSource() {
} }
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
val json = response.asJson().obj val homeDto = json.decodeFromString<BruttalHomeDto>(response.body!!.string())
val titles = json["list"].array.map { jsonEl -> val titles = homeDto.list.map(::popularMangaFromObject)
popularMangaFromObject(jsonEl.obj)
}
return MangasPage(titles, false) return MangasPage(titles, false)
} }
private fun popularMangaFromObject(obj: JsonObject): SManga = SManga.create().apply { private fun popularMangaFromObject(comicbook: BruttalComicBookDto): SManga = SManga.create().apply {
title = obj["title"].string title = comicbook.title
thumbnail_url = "$baseUrl/bruttal/" + obj["image_mobile"].string.removePrefix("./") thumbnail_url = "$baseUrl/bruttal/" + comicbook.imageMobile.removePrefix("./")
url = "/bruttal" + obj["url"].string url = "/bruttal" + comicbook.url
} }
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
@ -96,20 +92,20 @@ class Bruttal : HttpSource() {
} }
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {
val json = response.asJson().array val comicBooks = json.decodeFromString<List<BruttalComicBookDto>>(response.body!!.string())
val titleUrl = response.request.header("Referer")!!.substringAfter("/bruttal") val comicBookUrl = response.request.header("Referer")!!
val titleObj = json.first { it.obj["url"].string == titleUrl }.obj .substringAfter("/bruttal")
val soonText = titleObj["soon_text"].string val currentComicBook = comicBooks.first { it.url == comicBookUrl }
return SManga.create().apply { return SManga.create().apply {
title = titleObj["title"].string title = currentComicBook.title
thumbnail_url = "$baseUrl/bruttal/" + titleObj["image_mobile"].string.removePrefix("./") thumbnail_url = "$baseUrl/bruttal/" + currentComicBook.imageMobile.removePrefix("./")
description = titleObj["synopsis"].string + description = currentComicBook.synopsis +
(if (soonText.isEmpty()) "" else "\n\n$soonText") (if (currentComicBook.soonText.isEmpty()) "" else "\n\n${currentComicBook.soonText}")
artist = titleObj["illustrator"].string artist = currentComicBook.illustrator
author = titleObj["author"].string author = currentComicBook.author
genre = titleObj["keywords"].string.replace("; ", ", ") genre = currentComicBook.keywords.replace("; ", ", ")
status = SManga.ONGOING status = SManga.ONGOING
} }
} }
@ -118,21 +114,24 @@ class Bruttal : HttpSource() {
override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga) override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga)
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val json = response.asJson().array val comicBooks = json.decodeFromString<List<BruttalComicBookDto>>(response.body!!.string())
val titleUrl = response.request.header("Referer")!!.substringAfter("/bruttal") val comicBookUrl = response.request.header("Referer")!!
val title = json.first { it.obj["url"].string == titleUrl }.obj .substringAfter("/bruttal")
val currentComicBook = comicBooks.first { it.url == comicBookUrl }
return title["seasons"].array return currentComicBook.seasons
.flatMap { it.obj["chapters"].array } .flatMap { it.chapters }
.map { jsonEl -> chapterFromObject(jsonEl.obj) } .map(::chapterFromObject)
.reversed() .reversed()
} }
private fun chapterFromObject(obj: JsonObject): SChapter = SChapter.create().apply { private fun chapterFromObject(chapter: BruttalChapterDto): SChapter = SChapter.create().apply {
name = obj["title"].string name = chapter.title
chapter_number = obj["share_title"].string.removePrefix("Capítulo ").toFloatOrNull() ?: -1f chapter_number = chapter.shareTitle
url = "/bruttal" + obj["url"].string .removePrefix("Capítulo ")
.toFloatOrNull() ?: -1f
url = "/bruttal" + chapter.url
} }
override fun pageListRequest(chapter: SChapter): Request { override fun pageListRequest(chapter: SChapter): Request {
@ -145,22 +144,28 @@ class Bruttal : HttpSource() {
} }
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
val json = response.asJson().array val comicBooks = json.decodeFromString<List<BruttalComicBookDto>>(response.body!!.string())
val chapterUrl = response.request.header("Referer")!! val chapterUrl = response.request.header("Referer")!!
val titleSlug = chapterUrl.substringAfter("bruttal/").substringBefore("/") val comicBookSlug = chapterUrl
val season = chapterUrl.substringAfter("temporada-").substringBefore("/").toInt() .substringAfter("bruttal/")
val chapter = chapterUrl.substringAfter("capitulo-") .substringBefore("/")
val seasonNumber = chapterUrl
.substringAfter("temporada-")
.substringBefore("/")
val chapterNumber = chapterUrl.substringAfter("capitulo-")
val titleObj = json.first { it.obj["url"].string == "/$titleSlug" }.obj val currentComicBook = comicBooks.first { it.url == "/$comicBookSlug" }
val seasonObj = titleObj["seasons"].array[season - 1].obj val currentSeason = currentComicBook.seasons.first {
val chapterObj = seasonObj["chapters"].array.first { it.alias.substringAfter("-") == seasonNumber
it.obj["alias"].string.substringAfter("-") == chapter }
val currentChapter = currentSeason.chapters.first {
it.alias.substringAfter("-") == chapterNumber
} }
return chapterObj["images"].array return currentChapter.images
.mapIndexed { i, jsonEl -> .mapIndexed { i, bruttalImage ->
val imageUrl = "$baseUrl/bruttal/" + jsonEl.obj["image"].string.removePrefix("./") val imageUrl = "$baseUrl/bruttal/" + bruttalImage.image.removePrefix("./")
Page(i, chapterUrl, imageUrl) Page(i, chapterUrl, imageUrl)
} }
} }
@ -184,10 +189,8 @@ class Bruttal : HttpSource() {
override fun latestUpdatesParse(response: Response): MangasPage = throw UnsupportedOperationException("Not used") override fun latestUpdatesParse(response: Response): MangasPage = throw UnsupportedOperationException("Not used")
private fun Response.asJson(): JsonElement = JsonParser.parseString(body!!.string())
companion object { companion object {
private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + 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"
} }
} }

View File

@ -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<BruttalComicBookDto> = emptyList()
)
@Serializable
data class BruttalComicBookDto(
val author: String,
val illustrator: String,
@SerialName("image_mobile") val imageMobile: String,
val keywords: String,
val seasons: List<BruttalSeasonDto> = 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<BruttalChapterDto> = emptyList()
)
@Serializable
data class BruttalChapterDto(
val alias: String,
val images: List<BruttalImageDto> = emptyList(),
@SerialName("share_title") val shareTitle: String,
val title: String,
val url: String
)
@Serializable
data class BruttalImageDto(
val image: String
)

View File

@ -1,11 +1,12 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext { ext {
extName = 'HipercooL' extName = 'HipercooL'
pkgNameSuffix = 'pt.hipercool' pkgNameSuffix = 'pt.hipercool'
extClass = '.Hipercool' extClass = '.Hipercool'
extVersionCode = 6 extVersionCode = 7
libVersion = '1.2' libVersion = '1.2'
containsNsfw = true containsNsfw = true
} }

View File

@ -1,17 +1,7 @@
package eu.kanade.tachiyomi.extension.pt.hipercool 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.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.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess 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.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 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.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -29,10 +25,10 @@ import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import rx.Observable import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit
@Nsfw @Nsfw
class Hipercool : HttpSource() { class Hipercool : HttpSource() {
@ -49,7 +45,8 @@ class Hipercool : HttpSource() {
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder() 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() .build()
override fun headersBuilder(): Headers.Builder = Headers.Builder() override fun headersBuilder(): Headers.Builder = Headers.Builder()
@ -57,31 +54,27 @@ class Hipercool : HttpSource() {
.add("Referer", baseUrl) .add("Referer", baseUrl)
.add("X-Requested-With", "XMLHttpRequest") .add("X-Requested-With", "XMLHttpRequest")
private fun genericMangaListParse(response: Response): MangasPage { private val json: Json by injectLazy()
val result = response.asJson().array
if (result.size() == 0) private fun genericMangaListParse(response: Response): MangasPage {
val chapters = json.decodeFromString<List<HipercoolChapterDto>>(response.body!!.string())
if (chapters.isEmpty())
return MangasPage(emptyList(), false) return MangasPage(emptyList(), false)
val mangaList = result val mangaList = chapters
.map { genericMangaFromObject(it.obj) } .map(::genericMangaFromObject)
.distinctBy { it.title } .distinctBy { it.title }
val hasNextPage = result.size() == DEFAULT_COUNT val hasNextPage = chapters.size == DEFAULT_COUNT
return MangasPage(mangaList, hasNextPage) return MangasPage(mangaList, hasNextPage)
} }
private fun genericMangaFromObject(obj: JsonObject): SManga { private fun genericMangaFromObject(chapter: HipercoolChapterDto): SManga = SManga.create().apply {
val book = obj["_book"].obj title = chapter.book!!.title
val bookSlug = book["slug"].string thumbnail_url = chapter.book.slug.toThumbnailUrl(chapter.book.revision)
val bookRevision = book["revision"]?.int ?: 1 url = "/books/" + chapter.book.slug
return SManga.create().apply {
title = book["title"].string
thumbnail_url = bookSlug.toThumbnailUrl(bookRevision)
url = "/books/$bookSlug"
}
} }
// The source does not have popular mangas, so use latest instead. // 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() val mediaType = "application/json; charset=utf-8".toMediaTypeOrNull()
// Create json body. // Create json body.
val json = jsonObject( val json = buildJsonObject {
"start" to (page - 1) * DEFAULT_COUNT, put("start", (page - 1) * DEFAULT_COUNT)
"count" to DEFAULT_COUNT, put("content", DEFAULT_COUNT)
"text" to query, put("text", query)
"type" to "text" put("type", "text")
) }
val body = json.toString().toRequestBody(mediaType) val body = json.toString().toRequestBody(mediaType)
@ -130,27 +123,27 @@ class Hipercool : HttpSource() {
} }
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {
val result = response.asJson().obj val book = json.decodeFromString<HipercoolBookDto>(response.body!!.string())
val artists = result["tags"].array val artists = book.tags
.filter { it["label"].string == "Artista" } .filter { it.label == "Artista" }
.flatMap { it["values"].array } .flatMap { it.values }
.joinToString("; ") { it["label"].string } .joinToString("; ") { it.label }
val authors = result["tags"].array val authors = book.tags
.filter { it["label"].string == "Autor" } .filter { it.label == "Autor" }
.flatMap { it["values"].array } .flatMap { it.values }
.joinToString("; ") { it["label"].string } .joinToString("; ") { it.label }
val tags = result["tags"].array val tags = book.tags
.filter { it["label"].string == "Tags" } .filter { it.label == "Tags" }
.flatMap { it["values"].array } .flatMap { it.values }
.joinToString(", ") { it["label"].string } .joinToString { it.label }
return SManga.create().apply { return SManga.create().apply {
title = result["title"].string title = book.title
thumbnail_url = result["slug"].string.toThumbnailUrl(result["revision"].int) thumbnail_url = book.slug.toThumbnailUrl(book.revision)
description = result["synopsis"]?.string ?: "" description = book.synopsis.orEmpty()
artist = artists artist = artists
author = authors author = authors
genre = tags genre = tags
@ -161,27 +154,27 @@ class Hipercool : HttpSource() {
override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga) override fun chapterListRequest(manga: SManga): Request = mangaDetailsApiRequest(manga)
override fun chapterListParse(response: Response): List<SChapter> { override fun chapterListParse(response: Response): List<SChapter> {
val result = response.asJson().obj val book = json.decodeFromString<HipercoolBookDto>(response.body!!.string())
if (!result["chapters"]!!.isJsonArray) if (book.chapters is JsonPrimitive)
return emptyList() return emptyList()
return result["chapters"].array return json.decodeFromString<List<HipercoolChapterDto>>(book.chapters.toString())
.map { chapterListItemParse(result, it.obj) } .map { chapterListItemParse(book, it) }
.reversed() .reversed()
} }
private fun chapterListItemParse(book: JsonObject, obj: JsonObject): SChapter = SChapter.create().apply { private fun chapterListItemParse(book: HipercoolBookDto, chapter: HipercoolChapterDto): SChapter =
name = obj["title"].string SChapter.create().apply {
chapter_number = obj["title"].string.toFloatOrNull() ?: -1f name = "Cap. " + chapter.title
// The property is written wrong. chapter_number = chapter.title.toFloatOrNull() ?: -1f
date_upload = DATE_FORMATTER.tryParseTime(obj["publishied_at"].string) date_upload = chapter.publishedAt.toDate()
val fullUrl = "$baseUrl/books".toHttpUrlOrNull()!!.newBuilder() val fullUrl = "$baseUrl/books".toHttpUrlOrNull()!!.newBuilder()
.addPathSegment(book["slug"].string) .addPathSegment(book.slug)
.addPathSegment(obj["slug"].string) .addPathSegment(chapter.slug)
.addQueryParameter("images", obj["images"].int.toString()) .addQueryParameter("images", chapter.images.toString())
.addQueryParameter("revision", book["revision"].int.toString()) .addQueryParameter("revision", book.revision.toString())
.toString() .toString()
setUrlWithoutDomain(fullUrl) setUrlWithoutDomain(fullUrl)
@ -228,9 +221,9 @@ class Hipercool : HttpSource() {
return GET(page.imageUrl!!, newHeaders) return GET(page.imageUrl!!, newHeaders)
} }
private fun SimpleDateFormat.tryParseTime(date: String): Long { private fun String.toDate(): Long {
return try { return try {
parse(date.substringBefore("T"))?.time ?: 0L DATE_FORMATTER.parse(substringBefore("T"))?.time ?: 0L
} catch (e: ParseException) { } catch (e: ParseException) {
0L 0L
} }
@ -243,13 +236,11 @@ class Hipercool : HttpSource() {
.addQueryParameter("revision", revision.toString()) .addQueryParameter("revision", revision.toString())
.toString() .toString()
private fun Response.asJson(): JsonElement = JsonParser.parseString(body!!.string())
companion object { companion object {
private const val STATIC_URL = "https://static.hiper.cool" private const val STATIC_URL = "https://static.hiper.cool"
private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + 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 private const val DEFAULT_COUNT = 40

View File

@ -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<HipercoolTagDto> = emptyList(),
val title: String
)
@Serializable
data class HipercoolTagDto(
val label: String,
val values: List<HipercoolTagDto> = 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
)

View File

@ -6,13 +6,12 @@ ext {
extName = 'Dmzj' extName = 'Dmzj'
pkgNameSuffix = 'zh.dmzj' pkgNameSuffix = 'zh.dmzj'
extClass = '.Dmzj' extClass = '.Dmzj'
extVersionCode = 17 extVersionCode = 18
libVersion = '1.2' libVersion = '1.2'
} }
dependencies { dependencies {
implementation project(':lib-ratelimit') implementation project(':lib-ratelimit')
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.2.0'
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"