Add Yidan Girl (#14571)

* Add Yidan Girl

* check http prefix instead of https in image URL
This commit is contained in:
stevenyomi 2022-12-18 20:58:28 +08:00 committed by GitHub
parent 3317191ba7
commit eefd807c55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 266 additions and 0 deletions

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.extension" />

13
src/zh/yidan/build.gradle Normal file
View File

@ -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

View File

@ -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)

View File

@ -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
)

View File

@ -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")
}
}