Remove AgiToon (#4465)
This commit is contained in:
parent
987aa83570
commit
18b6668b54
|
@ -1,8 +0,0 @@
|
||||||
ext {
|
|
||||||
extName = 'AgiToon'
|
|
||||||
extClass = '.AgiToon'
|
|
||||||
extVersionCode = 1
|
|
||||||
isNsfw = true
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
|
@ -1,218 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.ko.agitoon
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
|
||||||
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 eu.kanade.tachiyomi.util.asJsoup
|
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
|
||||||
import okhttp3.Request
|
|
||||||
import okhttp3.Response
|
|
||||||
import okio.IOException
|
|
||||||
import rx.Observable
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
|
||||||
import kotlin.math.min
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
class AgiToon : HttpSource() {
|
|
||||||
|
|
||||||
override val name = "아지툰"
|
|
||||||
|
|
||||||
override val lang = "ko"
|
|
||||||
|
|
||||||
private var currentBaseUrlHost = ""
|
|
||||||
override val baseUrl = "https://agitoon.in"
|
|
||||||
|
|
||||||
private val cdnUrl = "https://blacktoonimg.com/"
|
|
||||||
|
|
||||||
override val supportsLatest = true
|
|
||||||
|
|
||||||
override val client = network.cloudflareClient.newBuilder().addInterceptor { chain ->
|
|
||||||
if (currentBaseUrlHost.isBlank()) {
|
|
||||||
noRedirectClient.newCall(GET(baseUrl, headers)).execute().use {
|
|
||||||
currentBaseUrlHost = it.headers["location"]?.toHttpUrlOrNull()?.host
|
|
||||||
?: throw IOException("unable to get updated url")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val request = chain.request().newBuilder().apply {
|
|
||||||
if (chain.request().url.toString().startsWith(baseUrl)) {
|
|
||||||
url(
|
|
||||||
chain.request().url.newBuilder()
|
|
||||||
.host(currentBaseUrlHost)
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
header("Referer", "https://$currentBaseUrlHost/")
|
|
||||||
header("Origin", "https://$currentBaseUrlHost")
|
|
||||||
}.build()
|
|
||||||
|
|
||||||
return@addInterceptor chain.proceed(request)
|
|
||||||
}.build()
|
|
||||||
|
|
||||||
private val noRedirectClient = network.cloudflareClient.newBuilder()
|
|
||||||
.followRedirects(false)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
private val json by injectLazy<Json>()
|
|
||||||
|
|
||||||
private val db by lazy {
|
|
||||||
val doc = client.newCall(GET(baseUrl, headers)).execute().asJsoup()
|
|
||||||
doc.select("script[src*=data/webtoon]").flatMap { scriptEl ->
|
|
||||||
var listIdx: Int
|
|
||||||
client.newCall(GET(scriptEl.absUrl("src"), headers))
|
|
||||||
.execute().body.string()
|
|
||||||
.also {
|
|
||||||
listIdx = it.substringBefore(" = ")
|
|
||||||
.substringAfter("data")
|
|
||||||
.toInt()
|
|
||||||
}
|
|
||||||
.substringAfter(" = ")
|
|
||||||
.removeSuffix(";")
|
|
||||||
.let { json.decodeFromString<List<SeriesItem>>(it) }
|
|
||||||
.onEach { it.listIndex = listIdx }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun List<SeriesItem>.getPageChunk(page: Int): MangasPage {
|
|
||||||
return MangasPage(
|
|
||||||
mangas = subList((page - 1) * 24, min(page * 24, size))
|
|
||||||
.map { it.toSManga(cdnUrl) },
|
|
||||||
hasNextPage = (page + 1) * 24 <= size,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
|
||||||
return Observable.just(
|
|
||||||
db.sortedByDescending { it.hot }.getPageChunk(page),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
|
|
||||||
return Observable.just(
|
|
||||||
db.sortedByDescending { it.updatedAt }.getPageChunk(page),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
|
|
||||||
var list = db
|
|
||||||
|
|
||||||
if (query.isNotBlank()) {
|
|
||||||
val stdQuery = query.trim()
|
|
||||||
list = list.filter {
|
|
||||||
it.name.contains(stdQuery, true) ||
|
|
||||||
it.author.contains(stdQuery, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
filters.filterIsInstance<ListFilter>().forEach {
|
|
||||||
list = it.applyFilter(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Observable.just(
|
|
||||||
list.getPageChunk(page),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getFilterList() = getFilters()
|
|
||||||
|
|
||||||
override fun mangaDetailsRequest(manga: SManga): Request {
|
|
||||||
return GET("$baseUrl/azi_toon/${manga.url}.html#${manga.status}", headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getMangaUrl(manga: SManga): String {
|
|
||||||
return buildString {
|
|
||||||
if (currentBaseUrlHost.isBlank()) {
|
|
||||||
append(baseUrl)
|
|
||||||
} else {
|
|
||||||
append("https://")
|
|
||||||
append(currentBaseUrlHost)
|
|
||||||
}
|
|
||||||
append("/azi_toon/")
|
|
||||||
append(manga.url)
|
|
||||||
append(".html")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun mangaDetailsParse(response: Response): SManga {
|
|
||||||
val doc = response.asJsoup()
|
|
||||||
return SManga.create().apply {
|
|
||||||
description = doc.select("p.mt-2").last()?.text()
|
|
||||||
thumbnail_url = doc.selectFirst("script:containsData(+img_domain+)")?.data()?.let {
|
|
||||||
cdnUrl + it.substringAfter("+'").substringBefore("'+")
|
|
||||||
}
|
|
||||||
status = response.request.url.fragment!!.toInt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun chapterListRequest(manga: SManga): Request {
|
|
||||||
val url = "$baseUrl/data/toonlist/${manga.url}.js?v=${"%.17f".format(Random.nextDouble())}"
|
|
||||||
|
|
||||||
return GET(url, headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun chapterListParse(response: Response): List<SChapter> {
|
|
||||||
val mangaId = response.request.url.pathSegments.last().removeSuffix(".js")
|
|
||||||
|
|
||||||
val data = response.body.string()
|
|
||||||
.substringAfter(" = ")
|
|
||||||
.removeSuffix(";")
|
|
||||||
.let { json.decodeFromString<List<Chapter>>(it) }
|
|
||||||
|
|
||||||
return data.map { it.toSChapter(mangaId) }.reversed()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getChapterUrl(chapter: SChapter): String {
|
|
||||||
return buildString {
|
|
||||||
if (currentBaseUrlHost.isBlank()) {
|
|
||||||
append(baseUrl)
|
|
||||||
} else {
|
|
||||||
append("https://")
|
|
||||||
append(currentBaseUrlHost)
|
|
||||||
}
|
|
||||||
append("/azi_toons/")
|
|
||||||
append(chapter.url)
|
|
||||||
append(".html")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pageListRequest(chapter: SChapter): Request {
|
|
||||||
return GET("$baseUrl/azi_toons/${chapter.url}.html", headers)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pageListParse(response: Response): List<Page> {
|
|
||||||
val document = response.asJsoup()
|
|
||||||
|
|
||||||
return document.select("#toon_content_imgs img").map {
|
|
||||||
Page(0, imageUrl = cdnUrl + it.attr("o_src"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// unused
|
|
||||||
override fun popularMangaRequest(page: Int): Request {
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
override fun popularMangaParse(response: Response): MangasPage {
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
override fun latestUpdatesRequest(page: Int): Request {
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
override fun latestUpdatesParse(response: Response): MangasPage {
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
override fun searchMangaParse(response: Response): MangasPage {
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
override fun imageUrlParse(response: Response): String {
|
|
||||||
throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.ko.agitoon
|
|
||||||
|
|
||||||
val platformsMap = mapOf(
|
|
||||||
Pair(1, "네이버"),
|
|
||||||
Pair(2, "다음"),
|
|
||||||
Pair(3, "카카오"),
|
|
||||||
Pair(4, "레진"),
|
|
||||||
Pair(5, "투믹스"),
|
|
||||||
Pair(6, "탑툰"),
|
|
||||||
Pair(7, "코미카"),
|
|
||||||
Pair(8, "배틀코믹"),
|
|
||||||
Pair(9, "코믹GT"),
|
|
||||||
Pair(10, "케이툰"),
|
|
||||||
Pair(11, "애니툰"),
|
|
||||||
Pair(12, "폭스툰"),
|
|
||||||
Pair(13, "피너툰"),
|
|
||||||
Pair(14, "봄툰"),
|
|
||||||
Pair(15, "코미코"),
|
|
||||||
Pair(16, "무툰"),
|
|
||||||
Pair(17, "지존신마"),
|
|
||||||
Pair(99, "기타"),
|
|
||||||
)
|
|
||||||
|
|
||||||
val tagsMap = mapOf(
|
|
||||||
Pair(1, "학원"),
|
|
||||||
Pair(2, "액션"),
|
|
||||||
Pair(3, "SF"),
|
|
||||||
Pair(4, "스토리"),
|
|
||||||
Pair(5, "판타지"),
|
|
||||||
Pair(6, "BL/백합"),
|
|
||||||
Pair(7, "개그/코미디"),
|
|
||||||
Pair(8, "연애/순정"),
|
|
||||||
Pair(9, "드라마"),
|
|
||||||
Pair(10, "로맨스"),
|
|
||||||
Pair(11, "시대극"),
|
|
||||||
Pair(12, "스포츠"),
|
|
||||||
Pair(13, "일상"),
|
|
||||||
Pair(14, "추리/미스터리"),
|
|
||||||
Pair(15, "공포/스릴러"),
|
|
||||||
Pair(16, "성인"),
|
|
||||||
Pair(17, "옴니버스"),
|
|
||||||
Pair(18, "에피소드"),
|
|
||||||
Pair(19, "무협"),
|
|
||||||
Pair(20, "소년"),
|
|
||||||
Pair(99, "기타"),
|
|
||||||
)
|
|
||||||
|
|
||||||
val publishDayMap = mapOf(
|
|
||||||
Pair(1, "월"),
|
|
||||||
Pair(2, "화"),
|
|
||||||
Pair(3, "수"),
|
|
||||||
Pair(4, "목"),
|
|
||||||
Pair(5, "금"),
|
|
||||||
Pair(6, "토"),
|
|
||||||
Pair(7, "일"),
|
|
||||||
Pair(10, "열흘"),
|
|
||||||
)
|
|
|
@ -1,84 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.ko.agitoon
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
|
||||||
import kotlinx.serialization.SerialName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import java.text.ParseException
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class SeriesItem(
|
|
||||||
@SerialName("x")
|
|
||||||
private val id: String,
|
|
||||||
@SerialName("t")
|
|
||||||
val name: String,
|
|
||||||
@SerialName("p")
|
|
||||||
private val poster: String = "",
|
|
||||||
@SerialName("au")
|
|
||||||
val author: String = "",
|
|
||||||
@SerialName("g")
|
|
||||||
val updatedAt: Long = 0,
|
|
||||||
@SerialName("tag")
|
|
||||||
private val tagIds: String = "",
|
|
||||||
@SerialName("c")
|
|
||||||
private val platformId: String = "-1",
|
|
||||||
@SerialName("d")
|
|
||||||
private val publishDayId: String = "-1",
|
|
||||||
@SerialName("h")
|
|
||||||
val hot: Int = 0,
|
|
||||||
) {
|
|
||||||
val tag get() = tagIds.split(",")
|
|
||||||
.filter(String::isNotBlank)
|
|
||||||
.map(String::toInt)
|
|
||||||
|
|
||||||
val platform get() = platformId.toInt()
|
|
||||||
|
|
||||||
val publishDay get() = publishDayId.toInt()
|
|
||||||
|
|
||||||
var listIndex = -1
|
|
||||||
|
|
||||||
fun toSManga(cdnUrl: String) = SManga.create().apply {
|
|
||||||
url = id
|
|
||||||
title = name
|
|
||||||
thumbnail_url = poster.takeIf { it.isNotBlank() }?.let {
|
|
||||||
cdnUrl + it.replace("_x4", "").replace("_x3", "")
|
|
||||||
}
|
|
||||||
genre = buildList {
|
|
||||||
add(platformsMap[platform])
|
|
||||||
add(publishDayMap[publishDay])
|
|
||||||
tag.forEach {
|
|
||||||
add(tagsMap[it])
|
|
||||||
}
|
|
||||||
}.filterNotNull().joinToString()
|
|
||||||
author = this@SeriesItem.author
|
|
||||||
status = when (listIndex) {
|
|
||||||
0 -> SManga.COMPLETED
|
|
||||||
1 -> SManga.ONGOING
|
|
||||||
else -> SManga.UNKNOWN
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class Chapter(
|
|
||||||
@SerialName("id")
|
|
||||||
val id: String,
|
|
||||||
@SerialName("t")
|
|
||||||
val title: String,
|
|
||||||
@SerialName("d")
|
|
||||||
val date: String = "",
|
|
||||||
) {
|
|
||||||
fun toSChapter(mangaId: String) = SChapter.create().apply {
|
|
||||||
url = "$mangaId/$id"
|
|
||||||
name = title
|
|
||||||
date_upload = try {
|
|
||||||
dateFormat.parse(date)!!.time
|
|
||||||
} catch (_: ParseException) {
|
|
||||||
0L
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
|
|
|
@ -1,122 +0,0 @@
|
||||||
package eu.kanade.tachiyomi.extension.ko.agitoon
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.source.model.Filter
|
|
||||||
import eu.kanade.tachiyomi.source.model.FilterList
|
|
||||||
|
|
||||||
interface ListFilter {
|
|
||||||
fun applyFilter(list: List<SeriesItem>): List<SeriesItem>
|
|
||||||
}
|
|
||||||
|
|
||||||
class TriFilter(name: String, val id: Int) : Filter.TriState(name)
|
|
||||||
|
|
||||||
abstract class TriFilterGroup(
|
|
||||||
name: String,
|
|
||||||
values: Map<Int, String>,
|
|
||||||
) : Filter.Group<TriFilter>(name, values.map { TriFilter(it.value, it.key) }), ListFilter {
|
|
||||||
private val included get() = state.filter { it.isIncluded() }.map { it.id }
|
|
||||||
private val excluded get() = state.filter { it.isExcluded() }.map { it.id }
|
|
||||||
|
|
||||||
abstract fun SeriesItem.getAttribute(): List<Int>
|
|
||||||
override fun applyFilter(list: List<SeriesItem>): List<SeriesItem> {
|
|
||||||
return list.filter { series ->
|
|
||||||
included.all {
|
|
||||||
it in series.getAttribute()
|
|
||||||
} and excluded.all {
|
|
||||||
it !in series.getAttribute()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class SelectFilter(
|
|
||||||
name: String,
|
|
||||||
private val options: List<Pair<Int, String>>,
|
|
||||||
) : Filter.Select<String>(
|
|
||||||
name,
|
|
||||||
options.map { it.second }.toTypedArray(),
|
|
||||||
) {
|
|
||||||
|
|
||||||
val selected get() = options[state].first
|
|
||||||
}
|
|
||||||
|
|
||||||
class TagFilter : TriFilterGroup("Tag", tagsMap) {
|
|
||||||
override fun SeriesItem.getAttribute(): List<Int> {
|
|
||||||
return tag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PlatformFilter :
|
|
||||||
SelectFilter(
|
|
||||||
"Platform",
|
|
||||||
buildList {
|
|
||||||
add(-1 to "")
|
|
||||||
platformsMap.forEach {
|
|
||||||
add(it.key to it.value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListFilter {
|
|
||||||
override fun applyFilter(list: List<SeriesItem>): List<SeriesItem> {
|
|
||||||
return list.filter { selected == -1 || it.platform == selected }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PublishDayFilter :
|
|
||||||
SelectFilter(
|
|
||||||
"Publishing Day",
|
|
||||||
buildList {
|
|
||||||
add(-1 to "")
|
|
||||||
publishDayMap.forEach {
|
|
||||||
add(it.key to it.value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListFilter {
|
|
||||||
override fun applyFilter(list: List<SeriesItem>): List<SeriesItem> {
|
|
||||||
return list.filter { selected == -1 || it.publishDay == state }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Status :
|
|
||||||
SelectFilter(
|
|
||||||
"Status",
|
|
||||||
listOf(
|
|
||||||
-1 to "All",
|
|
||||||
1 to "연재",
|
|
||||||
0 to "완결",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListFilter {
|
|
||||||
override fun applyFilter(list: List<SeriesItem>): List<SeriesItem> {
|
|
||||||
return when (selected) {
|
|
||||||
1, 0 -> list.filter { it.listIndex == selected }
|
|
||||||
else -> list
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Order :
|
|
||||||
SelectFilter(
|
|
||||||
"Order by",
|
|
||||||
listOf(
|
|
||||||
0 to "최신순",
|
|
||||||
1 to "인기순",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
ListFilter {
|
|
||||||
override fun applyFilter(list: List<SeriesItem>): List<SeriesItem> {
|
|
||||||
return when (selected) {
|
|
||||||
0 -> list.sortedByDescending { it.updatedAt }
|
|
||||||
1 -> list.sortedByDescending { it.hot }
|
|
||||||
else -> list
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getFilters() = FilterList(
|
|
||||||
Order(),
|
|
||||||
Status(),
|
|
||||||
PlatformFilter(),
|
|
||||||
PublishDayFilter(),
|
|
||||||
TagFilter(),
|
|
||||||
)
|
|
Loading…
Reference in New Issue