Webtoons.com: use api to get chapters (#9332)
* Webtoons.com: use api to get chapters * completed status * rename
This commit is contained in:
parent
68df5d3b69
commit
4d61698687
@ -1,7 +1,7 @@
|
|||||||
ext {
|
ext {
|
||||||
extName = 'Webtoons.com'
|
extName = 'Webtoons.com'
|
||||||
extClass = '.WebtoonsFactory'
|
extClass = '.WebtoonsFactory'
|
||||||
extVersionCode = 47
|
extVersionCode = 48
|
||||||
isNsfw = false
|
isNsfw = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,27 @@ package eu.kanade.tachiyomi.extension.all.webtoons
|
|||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
typealias EpisodeListResponse = ResultDto<EpisodeList>
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class ResultDto<T>(
|
||||||
|
val result: T,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class EpisodeList(
|
||||||
|
val episodeList: List<Episode>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Episode(
|
||||||
|
val episodeNo: Float,
|
||||||
|
val episodeTitle: String,
|
||||||
|
val viewerLink: String,
|
||||||
|
val exposureDateMillis: Long,
|
||||||
|
val hasBgm: Boolean = false,
|
||||||
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
class MotionToonResponse(
|
class MotionToonResponse(
|
||||||
val assets: MotionToonAssets,
|
val assets: MotionToonAssets,
|
||||||
|
@ -18,7 +18,6 @@ import eu.kanade.tachiyomi.util.asJsoup
|
|||||||
import keiyoushi.utils.firstInstanceOrNull
|
import keiyoushi.utils.firstInstanceOrNull
|
||||||
import keiyoushi.utils.getPreferencesLazy
|
import keiyoushi.utils.getPreferencesLazy
|
||||||
import keiyoushi.utils.parseAs
|
import keiyoushi.utils.parseAs
|
||||||
import keiyoushi.utils.tryParse
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
@ -26,14 +25,12 @@ import org.jsoup.nodes.Document
|
|||||||
import org.jsoup.nodes.Element
|
import org.jsoup.nodes.Element
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import java.net.SocketException
|
import java.net.SocketException
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
|
|
||||||
open class Webtoons(
|
open class Webtoons(
|
||||||
override val lang: String,
|
override val lang: String,
|
||||||
private val langCode: String = lang,
|
private val langCode: String = lang,
|
||||||
localeForCookie: String = lang,
|
localeForCookie: String = lang,
|
||||||
private val dateFormat: SimpleDateFormat,
|
|
||||||
) : HttpSource(), ConfigurableSource {
|
) : HttpSource(), ConfigurableSource {
|
||||||
override val name = "Webtoons.com"
|
override val name = "Webtoons.com"
|
||||||
override val baseUrl = "https://www.webtoons.com"
|
override val baseUrl = "https://www.webtoons.com"
|
||||||
@ -199,7 +196,7 @@ open class Webtoons(
|
|||||||
status = with(infoElement?.selectFirst("p.day_info")?.text().orEmpty()) {
|
status = with(infoElement?.selectFirst("p.day_info")?.text().orEmpty()) {
|
||||||
when {
|
when {
|
||||||
contains("UP") || contains("EVERY") || contains("NOUVEAU") -> SManga.ONGOING
|
contains("UP") || contains("EVERY") || contains("NOUVEAU") -> SManga.ONGOING
|
||||||
contains("END") || contains("TERMINÉ") -> SManga.COMPLETED
|
contains("END") || contains("COMPLETED") || contains("TERMINÉ") -> SManga.COMPLETED
|
||||||
else -> SManga.UNKNOWN
|
else -> SManga.UNKNOWN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,24 +228,44 @@ open class Webtoons(
|
|||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun chapterListRequest(manga: SManga) = GET(mobileUrl + manga.url, mobileHeaders)
|
override fun chapterListRequest(manga: SManga): Request {
|
||||||
|
val webtoonUrl = getMangaUrl(manga).toHttpUrl()
|
||||||
|
val titleId = webtoonUrl.queryParameter("title_no")
|
||||||
|
?: webtoonUrl.queryParameter("titleNo")
|
||||||
|
?: throw Exception("id not found, Migrate from $name to $name")
|
||||||
|
|
||||||
|
val isCanvas = webtoonUrl.pathSegments.getOrNull(1)?.equals("canvas")
|
||||||
|
?: throw Exception("unknown type, Migrate from $name to $name")
|
||||||
|
|
||||||
|
val url = mobileUrl.toHttpUrl().newBuilder().apply {
|
||||||
|
addPathSegments("api/v1")
|
||||||
|
if (isCanvas) {
|
||||||
|
addPathSegment("canvas")
|
||||||
|
} else {
|
||||||
|
addPathSegment("webtoon")
|
||||||
|
}
|
||||||
|
addPathSegment(titleId)
|
||||||
|
addPathSegment("episodes")
|
||||||
|
addQueryParameter("pageSize", "99999")
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
return GET(url, mobileHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
override fun chapterListParse(response: Response): List<SChapter> {
|
||||||
val document = response.asJsoup()
|
val result = response.parseAs<EpisodeListResponse>()
|
||||||
|
|
||||||
return document.select("ul#_episodeList li[id*=episode] a").map { element ->
|
return result.result.episodeList.map { episode ->
|
||||||
SChapter.create().apply {
|
SChapter.create().apply {
|
||||||
setUrlWithoutDomain(element.absUrl("href"))
|
url = episode.viewerLink
|
||||||
name = element.selectFirst(".sub_title > span.ellipsis")!!.text()
|
name = episode.episodeTitle
|
||||||
element.selectFirst("a > div.row > div.num")?.let {
|
if (episode.hasBgm) {
|
||||||
name += " Ch. " + it.text().substringAfter("#")
|
|
||||||
}
|
|
||||||
element.selectFirst(".ico_bgm")?.also {
|
|
||||||
name += " ♫"
|
name += " ♫"
|
||||||
}
|
}
|
||||||
date_upload = dateFormat.tryParse(element.selectFirst(".sub_info .date")?.text())
|
date_upload = episode.exposureDateMillis
|
||||||
|
chapter_number = episode.episodeNo
|
||||||
}
|
}
|
||||||
}
|
}.asReversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
override fun pageListParse(response: Response): List<Page> {
|
||||||
|
@ -1,23 +1,21 @@
|
|||||||
package eu.kanade.tachiyomi.extension.all.webtoons
|
package eu.kanade.tachiyomi.extension.all.webtoons
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.SourceFactory
|
import eu.kanade.tachiyomi.source.SourceFactory
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class WebtoonsFactory : SourceFactory {
|
class WebtoonsFactory : SourceFactory {
|
||||||
override fun createSources() = listOf(
|
override fun createSources() = listOf(
|
||||||
Webtoons("en", dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH)),
|
Webtoons("en"),
|
||||||
object : Webtoons("id", dateFormat = SimpleDateFormat("d MMMM yyyy", Locale("id"))) {
|
object : Webtoons("id") {
|
||||||
// Override ID as part of the name was removed to be more consiten with other enteries
|
// Override ID as part of the name was removed to be more consistent with other entries
|
||||||
override val id: Long = 8749627068478740298
|
override val id: Long = 8749627068478740298
|
||||||
},
|
},
|
||||||
Webtoons("th", dateFormat = SimpleDateFormat("d MMM yyyy", Locale("th"))),
|
Webtoons("th"),
|
||||||
Webtoons("es", dateFormat = SimpleDateFormat("d MMMM. yyyy", Locale("es"))),
|
Webtoons("es"),
|
||||||
Webtoons("fr", dateFormat = SimpleDateFormat("d MMM yyyy", Locale.FRENCH)),
|
Webtoons("fr"),
|
||||||
object : Webtoons("zh-Hant", "zh-hant", "zh_TW", SimpleDateFormat("yyyy/MM/dd", Locale.TRADITIONAL_CHINESE)) {
|
object : Webtoons("zh-Hant", "zh-hant", "zh_TW") {
|
||||||
// Due to lang code getting more specific
|
// Due to lang code getting more specific
|
||||||
override val id: Long = 2959982438613576472
|
override val id: Long = 2959982438613576472
|
||||||
},
|
},
|
||||||
Webtoons("de", dateFormat = SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN)),
|
Webtoons("de"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user