parent
							
								
									fc03fb5abb
								
							
						
					
					
						commit
						0bb01b70b8
					
				@ -1,11 +1,12 @@
 | 
			
		||||
apply plugin: 'com.android.application'
 | 
			
		||||
apply plugin: 'kotlin-android'
 | 
			
		||||
apply plugin: 'kotlinx-serialization'
 | 
			
		||||
 | 
			
		||||
ext {
 | 
			
		||||
    extName = 'Naver Comic'
 | 
			
		||||
    pkgNameSuffix = 'ko.navercomic'
 | 
			
		||||
    extClass = '.NaverComicFactory'
 | 
			
		||||
    extVersionCode = 2
 | 
			
		||||
    extVersionCode = 3
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
apply from: "$rootDir/common.gradle"
 | 
			
		||||
 | 
			
		||||
@ -2,10 +2,8 @@ package eu.kanade.tachiyomi.extension.ko.navercomic
 | 
			
		||||
 | 
			
		||||
import android.annotation.SuppressLint
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SChapter
 | 
			
		||||
import eu.kanade.tachiyomi.source.model.SManga
 | 
			
		||||
import eu.kanade.tachiyomi.util.asJsoup
 | 
			
		||||
import okhttp3.Response
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.Calendar
 | 
			
		||||
@ -14,70 +12,47 @@ import java.util.Locale
 | 
			
		||||
class NaverWebtoon : NaverComicBase("webtoon") {
 | 
			
		||||
    override val name = "Naver Webtoon"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int) = GET("$baseUrl/$mType/weekday.nhn")
 | 
			
		||||
    override fun popularMangaSelector() = ".list_area.daily_all .col ul > li"
 | 
			
		||||
    override fun popularMangaRequest(page: Int) = GET("$mobileUrl/$mType/weekday?sort=ALL_READER")
 | 
			
		||||
    override fun popularMangaSelector() = ".list_toon > [class='item ']"
 | 
			
		||||
    override fun popularMangaNextPageSelector() = null
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val thumb = element.select("div.thumb img").first()!!.attr("src")
 | 
			
		||||
        val title = element.select("a.title").first()!!
 | 
			
		||||
        val thumb = element.select("img").attr("src")
 | 
			
		||||
        val title = element.select("strong").text()
 | 
			
		||||
        val author = element.select("span.author").text().trim().split(" / ").joinToString()
 | 
			
		||||
        val url = element.select("a").attr("href")
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.url = title.attr("href").substringBefore("&week")
 | 
			
		||||
        manga.title = title.text().trim()
 | 
			
		||||
        manga.url = url
 | 
			
		||||
        manga.title = title
 | 
			
		||||
        manga.author = author
 | 
			
		||||
        manga.thumbnail_url = thumb
 | 
			
		||||
        return manga
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/$mType/weekday.nhn?order=Update")
 | 
			
		||||
    override fun latestUpdatesSelector() = ".list_area.daily_all .col.col_selected ul > li"
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int) = GET("$mobileUrl/$mType/weekday?sort=UPDATE")
 | 
			
		||||
    override fun latestUpdatesSelector() = ".list_toon > [class='item ']"
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = null
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document) =
 | 
			
		||||
        throw UnsupportedOperationException("Not used")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class NaverBestChallenge : NaverComicChallengeBase("bestChallenge") {
 | 
			
		||||
    override val name = "Naver Webtoon Best Challenge"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int) = GET("$baseUrl/genre/$mType.nhn?m=main&order=StarScore")
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/genre/$mType.nhn?m=main&order=Update")
 | 
			
		||||
    override fun popularMangaRequest(page: Int) = GET("$baseUrl/api/$mType/list?order=VIEW&page=$page")
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/api/$mType/list?order=UPDATE&page=$page")
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document) =
 | 
			
		||||
        throw UnsupportedOperationException("Not used")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class NaverChallenge : NaverComicChallengeBase("challenge") {
 | 
			
		||||
    override val name = "Naver Webtoon Challenge"
 | 
			
		||||
 | 
			
		||||
    override fun popularMangaRequest(page: Int) = GET("$baseUrl/genre/$mType.nhn")
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/genre/$mType.nhn?m=list&order=Update")
 | 
			
		||||
 | 
			
		||||
    // Chapter list is paginated, but there are no mobile pages to work with
 | 
			
		||||
    override fun chapterListRequest(manga: SManga) = GET("$baseUrl${manga.url}", headers)
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = "tbody tr:not([class])"
 | 
			
		||||
 | 
			
		||||
    override fun chapterListParse(response: Response): List<SChapter> {
 | 
			
		||||
        var document = response.asJsoup()
 | 
			
		||||
        val chapters = mutableListOf<SChapter>()
 | 
			
		||||
        document.select(chapterListSelector()).map { chapters.add(chapterFromElement(it)) }
 | 
			
		||||
        while (document.select(paginationNextPageSelector).hasText()) {
 | 
			
		||||
            document.select(paginationNextPageSelector).let {
 | 
			
		||||
                document = client.newCall(GET(it.attr("abs:href"))).execute().asJsoup()
 | 
			
		||||
                document.select(chapterListSelector()).map { element -> chapters.add(chapterFromElement(element)) }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return chapters
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override val paginationNextPageSelector = "div.paginate a.next"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        element.select("td + td a").let {
 | 
			
		||||
            val rawName = it.text()
 | 
			
		||||
            chapter.url = it.attr("href")
 | 
			
		||||
            chapter.chapter_number = parseChapterNumber(rawName)
 | 
			
		||||
            chapter.name = rawName
 | 
			
		||||
            chapter.date_upload = parseChapterDate(element.select("td.num").text().trim())
 | 
			
		||||
        }
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
    override fun popularMangaRequest(page: Int) = GET("$baseUrl/api/$mType/list?order=VIEW&page=$page")
 | 
			
		||||
    override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/api/$mType/list?order=UPDATE&page=$page")
 | 
			
		||||
 | 
			
		||||
    @SuppressLint("SimpleDateFormat")
 | 
			
		||||
    private fun parseChapterDate(date: String): Long {
 | 
			
		||||
@ -92,4 +67,7 @@ class NaverChallenge : NaverComicChallengeBase("challenge") {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document) =
 | 
			
		||||
        throw UnsupportedOperationException("Not used")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,18 +1,25 @@
 | 
			
		||||
package eu.kanade.tachiyomi.extension.ko.navercomic
 | 
			
		||||
 | 
			
		||||
import android.annotation.SuppressLint
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import eu.kanade.tachiyomi.network.GET
 | 
			
		||||
import eu.kanade.tachiyomi.network.asObservableSuccess
 | 
			
		||||
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.ParsedHttpSource
 | 
			
		||||
import eu.kanade.tachiyomi.util.asJsoup
 | 
			
		||||
import kotlinx.serialization.Serializable
 | 
			
		||||
import kotlinx.serialization.decodeFromString
 | 
			
		||||
import kotlinx.serialization.json.Json
 | 
			
		||||
import okhttp3.OkHttpClient
 | 
			
		||||
import okhttp3.Request
 | 
			
		||||
import okhttp3.Response
 | 
			
		||||
import org.jsoup.nodes.Document
 | 
			
		||||
import org.jsoup.nodes.Element
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.Calendar
 | 
			
		||||
import java.util.Locale
 | 
			
		||||
@ -20,75 +27,66 @@ import java.util.Locale
 | 
			
		||||
abstract class NaverComicBase(protected val mType: String) : ParsedHttpSource() {
 | 
			
		||||
    override val lang: String = "ko"
 | 
			
		||||
    override val baseUrl: String = "https://comic.naver.com"
 | 
			
		||||
    private val mobileUrl = "https://m.comic.naver.com"
 | 
			
		||||
    internal val mobileUrl = "https://m.comic.naver.com"
 | 
			
		||||
    override val supportsLatest = true
 | 
			
		||||
    override val client: OkHttpClient = network.client
 | 
			
		||||
    internal val json: Json by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val mobileHeaders = super.headersBuilder()
 | 
			
		||||
        .add("Referer", mobileUrl)
 | 
			
		||||
        .build()
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaSelector() = ".resultList > li h5 > a"
 | 
			
		||||
    override fun searchMangaNextPageSelector() = ".paginate a.next"
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/search.nhn?m=$mType&keyword=$query&type=title&page=$page")
 | 
			
		||||
    override fun searchMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.url = element.attr("href").substringBefore("&week").substringBefore("&listPage=")
 | 
			
		||||
        manga.title = element.text().trim()
 | 
			
		||||
        return manga
 | 
			
		||||
    override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/api/search/$mType?keyword=$query&page=$page")
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaNextPageSelector() = throw UnsupportedOperationException("Not used")
 | 
			
		||||
    override fun searchMangaSelector() = throw UnsupportedOperationException("Not used")
 | 
			
		||||
    override fun searchMangaFromElement(element: Element) = throw UnsupportedOperationException("Not used")
 | 
			
		||||
 | 
			
		||||
    override fun searchMangaParse(response: Response): MangasPage {
 | 
			
		||||
        val result = json.decodeFromString<ApiMangaSearchResponse>(response.body.string())
 | 
			
		||||
        val mangas = result.searchList.map {
 | 
			
		||||
            SManga.create().apply {
 | 
			
		||||
                title = it.titleName
 | 
			
		||||
                description = it.synopsis
 | 
			
		||||
                thumbnail_url = it.thumbnailUrl
 | 
			
		||||
                url = "/$mType/list?titleId=${it.titleId}"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return MangasPage(mangas, result.pageInfo.nextPage != 0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListSelector() = "div#ct > ul.section_episode_list li.item"
 | 
			
		||||
 | 
			
		||||
    // Chapter list is paginated, use mobile version of site for speed and data savings
 | 
			
		||||
    override fun chapterListSelector() = throw UnsupportedOperationException("Not used")
 | 
			
		||||
    override fun chapterListRequest(manga: SManga) = chapterListRequest(manga.url, 1)
 | 
			
		||||
 | 
			
		||||
    private fun chapterListRequest(mangaUrl: String, page: Int): Request {
 | 
			
		||||
        return GET("$mobileUrl$mangaUrl&page=$page", mobileHeaders)
 | 
			
		||||
        val titleId = Uri.parse("$baseUrl$mangaUrl").getQueryParameter("titleId")
 | 
			
		||||
        return GET("$baseUrl/api/article/list?titleId=$titleId&page=$page")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterListParse(response: Response): List<SChapter> {
 | 
			
		||||
        var document = response.asJsoup()
 | 
			
		||||
        var result = json.decodeFromString<ApiMangaChapterListResponse>(response.body.string())
 | 
			
		||||
        val chapters = mutableListOf<SChapter>()
 | 
			
		||||
        document.select(chapterListSelector()).map { chapters.add(chapterFromElement(it)) }
 | 
			
		||||
        var nextPage = 2
 | 
			
		||||
        while (document.select(paginationNextPageSelector).isNotEmpty()) {
 | 
			
		||||
            document.select(paginationNextPageSelector).let {
 | 
			
		||||
                document = client.newCall(chapterListRequest(it.attr("href"), nextPage)).execute().asJsoup()
 | 
			
		||||
                document.select(chapterListSelector()).map { element -> chapters.add(chapterFromElement(element)) }
 | 
			
		||||
                nextPage++
 | 
			
		||||
            }
 | 
			
		||||
        chapters.addAll(result.articleList.map { createChapter(it, result.titleId) })
 | 
			
		||||
 | 
			
		||||
        while (result.pageInfo.nextPage != 0) {
 | 
			
		||||
            result = json.decodeFromString(client.newCall(chapterListRequest("/$mType/list?titleId=${result.titleId}", result.pageInfo.nextPage)).execute().body.string())
 | 
			
		||||
            chapters.addAll(result.articleList.map { createChapter(it, result.titleId) })
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return chapters
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open val paginationNextPageSelector = "a.btn_next:not(.disabled)"
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element): SChapter {
 | 
			
		||||
        val chapter = SChapter.create()
 | 
			
		||||
        val rawName = element.select("span.name").text()
 | 
			
		||||
        chapter.url = element.select("a").attr("href")
 | 
			
		||||
        chapter.chapter_number = parseChapterNumber(rawName)
 | 
			
		||||
        chapter.name = rawName
 | 
			
		||||
        chapter.date_upload = parseChapterDate(element.select("span.date").text().trim())
 | 
			
		||||
 | 
			
		||||
        return chapter
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected fun parseChapterNumber(name: String): Float {
 | 
			
		||||
        try {
 | 
			
		||||
            if (name.contains("[단편]")) return 1f
 | 
			
		||||
            // `특별` means `Special`, so It can be buggy. so pad `편`(Chapter) to prevent false return
 | 
			
		||||
            if (name.contains("번외") || name.contains("특별편")) return -2f
 | 
			
		||||
            val regex = Regex("([0-9]+)(?:[-.]([0-9]+))?(?:화)")
 | 
			
		||||
            val (ch_primal, ch_second) = regex.find(name)!!.destructured
 | 
			
		||||
            return (ch_primal + if (ch_second.isBlank()) "" else ".$ch_second").toFloatOrNull() ?: -1f
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            e.printStackTrace()
 | 
			
		||||
            return -1f
 | 
			
		||||
    private fun createChapter(chapter: MangaChapter, id: Int): SChapter {
 | 
			
		||||
        return SChapter.create().apply {
 | 
			
		||||
            url = "/$mType/detail?titleId=$id&no=${chapter.no}"
 | 
			
		||||
            name = chapter.subtitle
 | 
			
		||||
            chapter_number = chapter.no.toFloat()
 | 
			
		||||
            date_upload = parseChapterDate(chapter.serviceDateDescription)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun chapterFromElement(element: Element) = throw UnsupportedOperationException("Not used")
 | 
			
		||||
 | 
			
		||||
    @SuppressLint("SimpleDateFormat")
 | 
			
		||||
    private fun parseChapterDate(date: String): Long {
 | 
			
		||||
        return if (date.contains(":")) {
 | 
			
		||||
@ -103,16 +101,24 @@ abstract class NaverComicBase(protected val mType: String) : ParsedHttpSource()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun mangaDetailsParse(document: Document): SManga {
 | 
			
		||||
        val element = document.select(".comicinfo")
 | 
			
		||||
        val titleElement = element.select(".detail > h2")
 | 
			
		||||
    override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
 | 
			
		||||
        val titleId = Uri.parse(manga.url).getQueryParameter("titleId")
 | 
			
		||||
        return client.newCall(GET("$baseUrl/api/article/list/info?titleId=$titleId")).asObservableSuccess().map { mangaDetailsParse(it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.title = titleElement.first()!!.ownText().trim()
 | 
			
		||||
        manga.author = titleElement.select("span").text().trim()
 | 
			
		||||
        manga.description = document.select("div.detail p").text().trim()
 | 
			
		||||
        manga.thumbnail_url = element.select(".thumb > a > img").last()!!.attr("src")
 | 
			
		||||
        return manga
 | 
			
		||||
    override fun mangaDetailsParse(response: Response): SManga {
 | 
			
		||||
        val manga = json.decodeFromString<Manga>(response.body.string())
 | 
			
		||||
        val authors = manga.author.run {
 | 
			
		||||
            setOf(writers, painters, originAuthors).flatten().map { it.name }
 | 
			
		||||
        }.joinToString()
 | 
			
		||||
 | 
			
		||||
        return SManga.create().apply {
 | 
			
		||||
            title = manga.titleName
 | 
			
		||||
            author = authors
 | 
			
		||||
            description = manga.synopsis
 | 
			
		||||
            thumbnail_url = manga.thumbnailUrl
 | 
			
		||||
            status = if (manga.finished) SManga.COMPLETED else SManga.ONGOING
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun pageListParse(document: Document): List<Page> {
 | 
			
		||||
@ -133,26 +139,104 @@ abstract class NaverComicBase(protected val mType: String) : ParsedHttpSource()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // We are able to get the image URL directly from the page list
 | 
			
		||||
    override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("This method should not be called!")
 | 
			
		||||
    override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used")
 | 
			
		||||
 | 
			
		||||
    override fun getFilterList() = FilterList()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
abstract class NaverComicChallengeBase(mType: String) : NaverComicBase(mType) {
 | 
			
		||||
    override fun popularMangaSelector() = ".weekchallengeBox tbody td:not([class])"
 | 
			
		||||
    override fun popularMangaNextPageSelector(): String? = ".paginate .page_wrap a.next"
 | 
			
		||||
    override fun popularMangaFromElement(element: Element): SManga {
 | 
			
		||||
        val thumb = element.select("a img").first()!!.attr("src")
 | 
			
		||||
        val title = element.select(".challengeTitle a").first()!!
 | 
			
		||||
    override fun popularMangaSelector() = throw UnsupportedOperationException("Not used")
 | 
			
		||||
    override fun popularMangaNextPageSelector() = throw UnsupportedOperationException("Not used")
 | 
			
		||||
    override fun popularMangaFromElement(element: Element) = throw UnsupportedOperationException("Not used")
 | 
			
		||||
    override fun popularMangaParse(response: Response): MangasPage {
 | 
			
		||||
        val apiMangaResponse = json.decodeFromString<ApiMangaChallengeResponse>(response.body.string())
 | 
			
		||||
        val mangas = apiMangaResponse.list.map {
 | 
			
		||||
            SManga.create().apply {
 | 
			
		||||
                title = it.titleName
 | 
			
		||||
                thumbnail_url = it.thumbnailUrl
 | 
			
		||||
                url = "/$mType/list?titleId=${it.titleId}"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val manga = SManga.create()
 | 
			
		||||
        manga.url = title.attr("href").substringBefore("&week")
 | 
			
		||||
        manga.title = title.text().trim()
 | 
			
		||||
        manga.thumbnail_url = thumb
 | 
			
		||||
        return manga
 | 
			
		||||
        var pageInfo = apiMangaResponse.pageInfo
 | 
			
		||||
 | 
			
		||||
        if (pageInfo == null) {
 | 
			
		||||
            val page = response.request.url.queryParameter("page")
 | 
			
		||||
            pageInfo = client.newCall(GET("$baseUrl/api/$mType/pageInfo?order=VIEW&page=$page")).execute().let { parsePageInfo(it) }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return MangasPage(mangas, pageInfo?.nextPage != 0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun latestUpdatesSelector() = popularMangaSelector()
 | 
			
		||||
    override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
 | 
			
		||||
    override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
 | 
			
		||||
    override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
 | 
			
		||||
 | 
			
		||||
    private fun parsePageInfo(response: Response): PageInfo? {
 | 
			
		||||
        return json.decodeFromString<ApiMangaChallengeResponse>(response.body.string()).pageInfo
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class ApiMangaSearchResponse(
 | 
			
		||||
    val pageInfo: PageInfo,
 | 
			
		||||
    val searchList: List<Manga>,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class ApiMangaChallengeResponse(
 | 
			
		||||
    val pageInfo: PageInfo?,
 | 
			
		||||
    val list: List<MangaChallenge>,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class ApiMangaChapterListResponse(
 | 
			
		||||
    val pageInfo: PageInfo,
 | 
			
		||||
    val titleId: Int,
 | 
			
		||||
    val articleList: List<MangaChapter>,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class PageInfo(
 | 
			
		||||
    val nextPage: Int,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class MangaChapter(
 | 
			
		||||
    val serviceDateDescription: String,
 | 
			
		||||
    val subtitle: String,
 | 
			
		||||
    val no: Int,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class Manga(
 | 
			
		||||
    val thumbnailUrl: String,
 | 
			
		||||
    val titleName: String,
 | 
			
		||||
    val titleId: Int,
 | 
			
		||||
    val finished: Boolean,
 | 
			
		||||
    val author: AuthorList,
 | 
			
		||||
    val synopsis: String,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class MangaChallenge(
 | 
			
		||||
    val thumbnailUrl: String,
 | 
			
		||||
    val titleName: String,
 | 
			
		||||
    val titleId: Int,
 | 
			
		||||
    val finish: Boolean,
 | 
			
		||||
    val author: String,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class AuthorList(
 | 
			
		||||
    val writers: List<Author>,
 | 
			
		||||
    val painters: List<Author>,
 | 
			
		||||
    val originAuthors: List<Author>,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Serializable
 | 
			
		||||
data class Author(
 | 
			
		||||
    val id: Int,
 | 
			
		||||
    val name: String,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user