Add Yidan Girl (#14571)
* Add Yidan Girl * check http prefix instead of https in image URL
This commit is contained in:
parent
3317191ba7
commit
eefd807c55
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="eu.kanade.tachiyomi.extension" />
|
|
@ -0,0 +1,13 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
|
||||
ext {
|
||||
extName = 'Yidan Girl'
|
||||
pkgNameSuffix = 'zh.yidan'
|
||||
extClass = '.Yidan'
|
||||
extVersionCode = 1
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
After Width: | Height: | Size: 101 KiB |
|
@ -0,0 +1,62 @@
|
|||
package eu.kanade.tachiyomi.extension.zh.yidan
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SChapter
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.jsoup.nodes.Entities
|
||||
|
||||
@Serializable
|
||||
class MangaDto(
|
||||
private val title: String,
|
||||
private val mhcate: String?,
|
||||
private val cateids: String?,
|
||||
private val author: String?,
|
||||
private val summary: String?,
|
||||
private val coverPic: String?,
|
||||
private val id: Int,
|
||||
) {
|
||||
fun toSManga() = SManga.create().apply {
|
||||
url = id.toString()
|
||||
title = this@MangaDto.title
|
||||
author = this@MangaDto.author
|
||||
description = summary?.trim()
|
||||
genre = when {
|
||||
cateids.isNullOrEmpty() -> null
|
||||
else -> cateids.split(",").joinToString { GENRES[it.toInt()] }
|
||||
}
|
||||
status = when {
|
||||
mhcate.isNullOrEmpty() -> SManga.ONGOING
|
||||
"5" in mhcate.split(",") -> SManga.COMPLETED
|
||||
else -> SManga.ONGOING
|
||||
}
|
||||
thumbnail_url = coverPic
|
||||
initialized = true
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class ChapterDto(
|
||||
private val createTime: Long,
|
||||
private val mhid: String,
|
||||
private val title: String,
|
||||
private val jiNo: Int
|
||||
) {
|
||||
fun toSChapter() = SChapter.create().apply {
|
||||
url = "$mhid/$jiNo"
|
||||
name = Entities.unescape(title)
|
||||
date_upload = createTime * 1000L
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class PageListDto(private val pics: String) {
|
||||
val images get() = pics.split(",")
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class ListingDto(val list: List<MangaDto>, private val total: String) {
|
||||
val totalCount get() = total.toInt()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class ResponseDto<T>(val data: T)
|
|
@ -0,0 +1,52 @@
|
|||
package eu.kanade.tachiyomi.extension.zh.yidan
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
fun getFilterListInternal() = FilterList(ListingFilter(), GenreFilter())
|
||||
|
||||
fun parseFilters(filters: FilterList, builder: HttpUrl.Builder) {
|
||||
for (filter in filters) when (filter) {
|
||||
is ListingFilter -> {
|
||||
if (filter.state > 0)
|
||||
builder.addEncodedQueryParameter("mhcate", LISTING_VALUES[filter.state].toString())
|
||||
}
|
||||
is GenreFilter -> {
|
||||
if (filter.state > 0)
|
||||
builder.addEncodedQueryParameter("cateid", String.format("%02d", filter.state))
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
class ListingFilter : Filter.Select<String>("分类", LISTINGS)
|
||||
|
||||
val LISTINGS = arrayOf("全部", "排行榜", "新作", "完结漫", "分类0", "分类1", "分类3", "分类7")
|
||||
val LISTING_VALUES = arrayOf(0, 2, 4, 5, 0, 1, 3, 7)
|
||||
|
||||
class GenreFilter : Filter.Select<String>("标签", GENRES)
|
||||
|
||||
val GENRES = arrayOf(
|
||||
"全部",
|
||||
"短漫", // 01
|
||||
"甜漫", // 02
|
||||
"强强", // 03
|
||||
"年下攻", // 04
|
||||
"诱受", // 05
|
||||
"骨科", // 06
|
||||
"调教", // 07
|
||||
"健气受", // 08
|
||||
"ABO", // 09
|
||||
"重生/重逢", // 10
|
||||
"财阀", // 11
|
||||
"校园", // 12
|
||||
"女王受", // 13
|
||||
"NP/SM", // 14
|
||||
"韩国榜单", // 15
|
||||
"高H", // 16
|
||||
"架空", // 17
|
||||
"娱乐圈", // 18
|
||||
"办公室", // 19
|
||||
"青梅竹马", // 20
|
||||
)
|
|
@ -0,0 +1,137 @@
|
|||
package eu.kanade.tachiyomi.extension.zh.yidan
|
||||
|
||||
import android.app.Application
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.asObservableSuccess
|
||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||
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.json.Json
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class Yidan : HttpSource(), ConfigurableSource {
|
||||
override val name get() = "一耽女孩"
|
||||
override val lang get() = "zh"
|
||||
override val supportsLatest get() = true
|
||||
|
||||
override val baseUrl: String
|
||||
|
||||
init {
|
||||
val mirrors = MIRRORS
|
||||
val index = Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||
.getString(MIRROR_PREF, "0")!!.toInt().coerceAtMost(mirrors.size - 1)
|
||||
baseUrl = "https://" + mirrors[index]
|
||||
}
|
||||
|
||||
override fun headersBuilder() = Headers.Builder()
|
||||
.add("User-Agent", System.getProperty("http.agent")!!)
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
override fun popularMangaRequest(page: Int) =
|
||||
GET("$baseUrl/prod-api/app-api/vv/mh-list/page?mhcate=2&pageSize=50&pageNo=$page", headers)
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage {
|
||||
val listing: ListingDto = response.parseAs()
|
||||
val mangas = listing.list.map { it.toSManga() }
|
||||
val hasNextPage = run {
|
||||
val url = response.request.url
|
||||
val pageSize = url.queryParameter("pageSize")!!.toInt()
|
||||
val pageNumber = url.queryParameter("pageNo")!!.toInt()
|
||||
pageSize * pageNumber < listing.totalCount
|
||||
}
|
||||
return MangasPage(mangas, hasNextPage)
|
||||
}
|
||||
|
||||
override fun latestUpdatesRequest(page: Int) =
|
||||
GET("$baseUrl/prod-api/app-api/vv/mh-list/page?mhcate=4&pageSize=50&pageNo=$page", headers)
|
||||
|
||||
override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||
val url = "$baseUrl/prod-api/app-api/vv/mh-list/page".toHttpUrl().newBuilder()
|
||||
.apply { if (query.isNotBlank()) addQueryParameter("word", query) }
|
||||
.apply { parseFilters(filters, this) }
|
||||
.addEncodedQueryParameter("pageSize", "50")
|
||||
.addEncodedQueryParameter("pageNo", page.toString())
|
||||
.build()
|
||||
return Request.Builder().url(url).headers(headers).build()
|
||||
}
|
||||
|
||||
override fun searchMangaParse(response: Response) = popularMangaParse(response)
|
||||
|
||||
// for WebView
|
||||
override fun mangaDetailsRequest(manga: SManga) =
|
||||
GET("$baseUrl/#/pages/detail/detail?id=${manga.url}")
|
||||
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
|
||||
val request = GET("$baseUrl/prod-api/app-api/vv/mh-list/get?id=${manga.url}", headers)
|
||||
return client.newCall(request).asObservableSuccess().map { mangaDetailsParse(it) }
|
||||
}
|
||||
|
||||
override fun mangaDetailsParse(response: Response) =
|
||||
response.parseAs<MangaDto>().toSManga()
|
||||
|
||||
override fun chapterListRequest(manga: SManga) =
|
||||
GET("$baseUrl/prod-api/app-api/vv/mh-episodes/list?mhid=${manga.url}", headers)
|
||||
|
||||
override fun chapterListParse(response: Response) =
|
||||
response.parseAs<List<ChapterDto>>().map { it.toSChapter() }
|
||||
|
||||
// for WebView
|
||||
override fun pageListRequest(chapter: SChapter): Request {
|
||||
val (mangaId, chapterIndex) = chapter.url.split("/")
|
||||
return GET("$baseUrl/#/pages/read/read?no=$chapterIndex&id=$mangaId")
|
||||
}
|
||||
|
||||
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
|
||||
val (mangaId, chapterIndex) = chapter.url.split("/")
|
||||
val url = "$baseUrl/prod-api/app-api/vv/mh-episodes/get?jiNo=$chapterIndex&mhid=$mangaId"
|
||||
return client.newCall(GET(url, headers)).asObservableSuccess().map { pageListParse(it) }
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response) =
|
||||
response.parseAs<PageListDto>().images.mapIndexed { index, url ->
|
||||
val imageUrl = if (url.startsWith("http")) url else baseUrl + url
|
||||
Page(index, imageUrl = imageUrl)
|
||||
}
|
||||
|
||||
override fun imageUrlParse(response: Response) = throw UnsupportedOperationException()
|
||||
|
||||
private inline fun <reified T> Response.parseAs(): T = use {
|
||||
json.decodeFromStream<ResponseDto<T>>(body!!.byteStream()).data
|
||||
}
|
||||
|
||||
override fun getFilterList() = getFilterListInternal()
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) {
|
||||
ListPreference(screen.context).apply {
|
||||
val mirrors = MIRRORS
|
||||
key = MIRROR_PREF
|
||||
title = "镜像网址(重启生效)"
|
||||
summary = "%s"
|
||||
entries = mirrors
|
||||
entryValues = Array(mirrors.size, Int::toString)
|
||||
setDefaultValue("0")
|
||||
}.let(screen::addPreference)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MIRROR_PREF = "MIRROR"
|
||||
private val MIRRORS get() = arrayOf("ydan.cc", "ydan.vip", "dans.cc")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue