NEW - TaddyINK extension (#18699)
* sss feed parsing complete * removed old references * Updates based on comments * remove unneeded image * using SwitchPreferenceCompat * misc changes * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyUtils.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyUtils.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyInkUrlActivity.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyInkUrlActivity.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyInk.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyInk.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyInk.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyInk.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyInk.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyInk.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyInk.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyInk.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * changes as per review * Update src/all/taddyink/build.gradle Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyUtils.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * Update src/all/taddyink/src/eu/kanade/tachiyomi/extension/all/taddyink/TaddyUtils.kt Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> * small fix --------- Co-authored-by: Daniel Mathews <dmathewwws@Daniels-Air.pnwlumber.com> Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
This commit is contained in:
parent
c6f5a54c96
commit
308d945eb7
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<application>
|
||||||
|
<activity
|
||||||
|
android:name=".all.taddyink.TaddyInkUrlActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:exported="true"
|
||||||
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:host="taddy.org"
|
||||||
|
android:scheme="https" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
|
@ -0,0 +1,12 @@
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlinx-serialization'
|
||||||
|
|
||||||
|
ext {
|
||||||
|
extName = 'Taddy INK (Webtoons)'
|
||||||
|
pkgNameSuffix = 'all.taddyink'
|
||||||
|
extClass = '.TaddyInkFactory'
|
||||||
|
extVersionCode = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootDir/common.gradle"
|
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 6.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
|
@ -0,0 +1,188 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.all.taddyink
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||||
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
import eu.kanade.tachiyomi.source.model.MangasPage
|
||||||
|
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.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
|
open class TaddyInk(
|
||||||
|
override val lang: String,
|
||||||
|
private val taddyLang: String,
|
||||||
|
) : ConfigurableSource, HttpSource() {
|
||||||
|
|
||||||
|
final override val baseUrl = "https://taddy.org"
|
||||||
|
override val name = "Taddy INK (Webtoons)"
|
||||||
|
override val supportsLatest = false
|
||||||
|
|
||||||
|
override val client: OkHttpClient by lazy {
|
||||||
|
network.cloudflareClient.newBuilder()
|
||||||
|
.rateLimit(4)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||||
|
SwitchPreferenceCompat(screen.context).apply {
|
||||||
|
key = TITLE_PREF_KEY
|
||||||
|
title = TITLE_PREF
|
||||||
|
summaryOn = "Full Title"
|
||||||
|
summaryOff = "Short Title"
|
||||||
|
setDefaultValue(true)
|
||||||
|
}.also(screen::addPreference)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException("Not used!")
|
||||||
|
|
||||||
|
override fun latestUpdatesParse(response: Response): MangasPage = throw UnsupportedOperationException("Not used!")
|
||||||
|
|
||||||
|
override fun popularMangaRequest(page: Int): Request {
|
||||||
|
val url = "$baseUrl/feeds/directory/list".toHttpUrl().newBuilder()
|
||||||
|
.addQueryParameter("lang", taddyLang)
|
||||||
|
.addQueryParameter("taddyType", "comicseries")
|
||||||
|
.addQueryParameter("ua", "tc")
|
||||||
|
.addQueryParameter("page", page.toString())
|
||||||
|
.addQueryParameter("limit", POPULAR_MANGA_LIMIT.toString())
|
||||||
|
return GET(url.build(), headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun popularMangaParse(response: Response) = parseManga(response)
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
val filterList = if (filters.isEmpty()) getFilterList() else filters
|
||||||
|
val shouldFilterByGenre = filterList.findInstance<GenreFilter>()?.state != 0
|
||||||
|
val shouldFilterByCreator = filterList.findInstance<CreatorFilter>()?.state?.isNotBlank() ?: false
|
||||||
|
val shouldFilterForTags = filterList.findInstance<TagFilter>()?.state?.isNotBlank() ?: false
|
||||||
|
|
||||||
|
val url = "$baseUrl/feeds/directory/search".toHttpUrl().newBuilder()
|
||||||
|
.addQueryParameter("q", query)
|
||||||
|
.addQueryParameter("lang", taddyLang)
|
||||||
|
.addQueryParameter("taddyType", "comicseries")
|
||||||
|
.addQueryParameter("ua", "tc")
|
||||||
|
.addQueryParameter("page", page.toString())
|
||||||
|
.addQueryParameter("limit", SEARCH_MANGA_LIMIT.toString())
|
||||||
|
|
||||||
|
if (shouldFilterByGenre) {
|
||||||
|
filterList.findInstance<GenreFilter>()?.let { f ->
|
||||||
|
url.addQueryParameter("genre", f.toUriPart())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFilterByCreator) {
|
||||||
|
filterList.findInstance<CreatorFilter>()?.let { name ->
|
||||||
|
url.addQueryParameter("creator", name.state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFilterForTags) {
|
||||||
|
filterList.findInstance<TagFilter>()?.let { tags ->
|
||||||
|
url.addQueryParameter("tags", tags.state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GET(url.build(), headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response) = parseManga(response)
|
||||||
|
|
||||||
|
private fun parseManga(response: Response): MangasPage {
|
||||||
|
val comicSeries = json.decodeFromString<ComicResults>(response.body.string())
|
||||||
|
val mangas = comicSeries.comicseries.map { TaddyUtils.getManga(it) }
|
||||||
|
val hasNextPage = comicSeries.comicseries.size == POPULAR_MANGA_LIMIT
|
||||||
|
return MangasPage(mangas, hasNextPage)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mangaDetailsRequest(manga: SManga): Request {
|
||||||
|
return GET(manga.url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun mangaDetailsParse(response: Response): SManga {
|
||||||
|
val comicObj = json.decodeFromString<Comic>(response.body.string())
|
||||||
|
return TaddyUtils.getManga(comicObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListRequest(manga: SManga): Request {
|
||||||
|
return GET(manga.url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
|
val comic = json.decodeFromString<Comic>(response.body.string())
|
||||||
|
val sssUrl = comic.url
|
||||||
|
|
||||||
|
val chapters = comic.issues.orEmpty().mapIndexed { i, chapter ->
|
||||||
|
SChapter.create().apply {
|
||||||
|
url = "$sssUrl#${chapter.identifier}"
|
||||||
|
name = chapter.name
|
||||||
|
date_upload = TaddyUtils.getTime(chapter.datePublished)
|
||||||
|
chapter_number = (comic.issues.orEmpty().size - i).toFloat()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chapters.reversed()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListRequest(chapter: SChapter): Request {
|
||||||
|
return GET(chapter.url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
|
val requestUrl = response.request.url.toString()
|
||||||
|
val issueUuid = requestUrl.substringAfterLast("#")
|
||||||
|
val comic = json.decodeFromString<Comic>(response.body.string())
|
||||||
|
|
||||||
|
val issue = comic.issues.orEmpty().firstOrNull { it.identifier == issueUuid }
|
||||||
|
|
||||||
|
return issue?.stories.orEmpty().mapIndexed { index, storyObj ->
|
||||||
|
Page(index, "", "${storyObj.storyImage?.base_url}${storyObj.storyImage?.story}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun imageUrlParse(response: Response) = ""
|
||||||
|
|
||||||
|
override fun getFilterList(): FilterList = FilterList(
|
||||||
|
GenreFilter(),
|
||||||
|
Filter.Separator(),
|
||||||
|
Filter.Header("Filter by the creator or tags:"),
|
||||||
|
CreatorFilter(),
|
||||||
|
TagFilter(),
|
||||||
|
)
|
||||||
|
|
||||||
|
class CreatorFilter : AdvSearchEntryFilter("Creator")
|
||||||
|
class TagFilter : AdvSearchEntryFilter("Tags")
|
||||||
|
open class AdvSearchEntryFilter(name: String) : Filter.Text(name)
|
||||||
|
|
||||||
|
private class GenreFilter : UriPartFilter(
|
||||||
|
"Filter By Genre",
|
||||||
|
TaddyUtils.genrePairs,
|
||||||
|
)
|
||||||
|
|
||||||
|
private open class UriPartFilter(displayName: String, val vals: List<Pair<String, String>>) :
|
||||||
|
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
|
||||||
|
fun toUriPart() = vals[state].second
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <reified T> Iterable<*>.findInstance() = find { it is T } as? T
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TITLE_PREF_KEY = "display_full_title"
|
||||||
|
private const val TITLE_PREF = "Display manga title as"
|
||||||
|
|
||||||
|
private const val POPULAR_MANGA_LIMIT = 25
|
||||||
|
private const val SEARCH_MANGA_LIMIT = 25
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.all.taddyink
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.Source
|
||||||
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
|
|
||||||
|
class TaddyInkFactory : SourceFactory {
|
||||||
|
override fun createSources(): List<Source> = listOf(
|
||||||
|
TaddyInk("all", ""),
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.all.taddyink
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Springboard that accepts https://nhentai.net/g/xxxxxx intents and redirects them to
|
||||||
|
* the main Tachiyomi process.
|
||||||
|
*/
|
||||||
|
class TaddyInkUrlActivity : Activity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
val pathSegments = intent?.data?.pathSegments
|
||||||
|
if (pathSegments != null && pathSegments.size > 1) {
|
||||||
|
val id = pathSegments[1]
|
||||||
|
val mainIntent = Intent().apply {
|
||||||
|
action = "eu.kanade.tachiyomi.SEARCH"
|
||||||
|
putExtra("query", "id")
|
||||||
|
putExtra("filter", packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
startActivity(mainIntent)
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
Log.e("TaddyInkUrlActivity", e.toString())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e("TaddyInkUrlActivity", "Could not parse URI from intent $intent")
|
||||||
|
}
|
||||||
|
|
||||||
|
finish()
|
||||||
|
exitProcess(0)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.all.taddyink
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
object TaddyUtils {
|
||||||
|
private val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US)
|
||||||
|
|
||||||
|
fun getManga(comicObj: Comic): SManga {
|
||||||
|
val name = comicObj.name
|
||||||
|
val sssUrl = comicObj.url
|
||||||
|
val sssDescription = comicObj.description
|
||||||
|
val genres = comicObj.genres.orEmpty()
|
||||||
|
.mapNotNull { genreMap[it] }
|
||||||
|
.joinToString()
|
||||||
|
|
||||||
|
val creators = comicObj.creators
|
||||||
|
?.mapNotNull { it.name }
|
||||||
|
?.joinToString()
|
||||||
|
|
||||||
|
val thumbnailBaseUrl = comicObj.coverImage?.base_url ?: ""
|
||||||
|
val thumbnail = comicObj.coverImage?.cover_sm ?: ""
|
||||||
|
val thumbnailUrl = if (thumbnailBaseUrl.isNotEmpty() && thumbnail.isNotEmpty()) "$thumbnailBaseUrl$thumbnail" else ""
|
||||||
|
|
||||||
|
return SManga.create().apply {
|
||||||
|
url = sssUrl
|
||||||
|
title = name
|
||||||
|
creators?.takeIf { it.isNotBlank() }?.let { author = it }
|
||||||
|
description = sssDescription
|
||||||
|
thumbnail_url = thumbnailUrl
|
||||||
|
status = SManga.ONGOING
|
||||||
|
genre = genres
|
||||||
|
initialized = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTime(timeString: String): Long {
|
||||||
|
return runCatching { formatter.parse(timeString)?.time }
|
||||||
|
.getOrNull() ?: 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
val genrePairs: List<Pair<String, String>> = listOf(
|
||||||
|
Pair("", ""),
|
||||||
|
Pair("Action", "COMICSERIES_ACTION"),
|
||||||
|
Pair("Comedy", "COMICSERIES_COMEDY"),
|
||||||
|
Pair("Drama", "COMICSERIES_DRAMA"),
|
||||||
|
Pair("Educational", "COMICSERIES_EDUCATIONAL"),
|
||||||
|
Pair("Fantasy", "COMICSERIES_FANTASY"),
|
||||||
|
Pair("Historical", "COMICSERIES_HISTORICAL"),
|
||||||
|
Pair("Horror", "COMICSERIES_HORROR"),
|
||||||
|
Pair("Inspirational", "COMICSERIES_INSPIRATIONAL"),
|
||||||
|
Pair("Mystery", "COMICSERIES_MYSTERY"),
|
||||||
|
Pair("Romance", "COMICSERIES_ROMANCE"),
|
||||||
|
Pair("Sci-Fi", "COMICSERIES_SCI_FI"),
|
||||||
|
Pair("Slice Of Life", "COMICSERIES_SLICE_OF_LIFE"),
|
||||||
|
Pair("Superhero", "COMICSERIES_SUPERHERO"),
|
||||||
|
Pair("Supernatural", "COMICSERIES_SUPERNATURAL"),
|
||||||
|
Pair("Wholesome", "COMICSERIES_WHOLESOME"),
|
||||||
|
Pair("BL (Boy Love)", "COMICSERIES_BL"),
|
||||||
|
Pair("GL (Girl Love)", "COMICSERIES_GL"),
|
||||||
|
Pair("LGBTQ+", "COMICSERIES_LGBTQ"),
|
||||||
|
Pair("Thriller", "COMICSERIES_THRILLER"),
|
||||||
|
Pair("Zombies", "COMICSERIES_ZOMBIES"),
|
||||||
|
Pair("Post Apocalyptic", "COMICSERIES_POST_APOCALYPTIC"),
|
||||||
|
Pair("School", "COMICSERIES_SCHOOL"),
|
||||||
|
Pair("Sports", "COMICSERIES_SPORTS"),
|
||||||
|
Pair("Animals", "COMICSERIES_ANIMALS"),
|
||||||
|
Pair("Gaming", "COMICSERIES_GAMING"),
|
||||||
|
)
|
||||||
|
|
||||||
|
val genreMap: Map<String, String> = genrePairs.associateBy({ it.second }, { it.first })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ComicResults(
|
||||||
|
val status: String,
|
||||||
|
val comicseries: List<Comic> = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Comic(
|
||||||
|
val identifier: String? = null,
|
||||||
|
val name: String = "Unknown",
|
||||||
|
val url: String,
|
||||||
|
val description: String? = null,
|
||||||
|
val genres: List<String>? = emptyList(),
|
||||||
|
val creators: List<Creator>? = emptyList(),
|
||||||
|
val coverImage: CoverImage? = null,
|
||||||
|
val bannerImage: BannerImage? = null,
|
||||||
|
val thumbnailImage: ThumbnailImage? = null,
|
||||||
|
val contentRating: String? = null,
|
||||||
|
val inLanguage: String? = null,
|
||||||
|
val seriesType: String? = null,
|
||||||
|
val issues: List<Chapter>? = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CoverImage(
|
||||||
|
val base_url: String?,
|
||||||
|
val cover_sm: String?,
|
||||||
|
val cover_md: String?,
|
||||||
|
val cover_lg: String?,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class BannerImage(
|
||||||
|
val base_url: String?,
|
||||||
|
val banner_sm: String?,
|
||||||
|
val banner_md: String?,
|
||||||
|
val banner_lg: String?,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ThumbnailImage(
|
||||||
|
val base_url: String?,
|
||||||
|
val thumbnail: String?,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Creator(
|
||||||
|
val identifier: String? = null,
|
||||||
|
val name: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Chapter(
|
||||||
|
val identifier: String,
|
||||||
|
val name: String,
|
||||||
|
val datePublished: String,
|
||||||
|
val stories: List<Story>? = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Story(
|
||||||
|
val storyImage: StoryImage?,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class StoryImage(
|
||||||
|
val base_url: String?,
|
||||||
|
val story: String?,
|
||||||
|
)
|
Loading…
Reference in New Issue