Remove 3 dead sources (#8502)

Remove AsuraScansFree, Inmoral No FanSub, YuriNeko
This commit is contained in:
Prem Kumar 2025-04-17 19:27:43 +05:30 committed by Draff
parent 4524da7e08
commit c9fc08676f
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
27 changed files with 0 additions and 741 deletions

View File

@ -1,10 +0,0 @@
ext {
extName = 'Asura Scans Free (unoriginal)'
extClass = '.AsuraScansFree'
themePkg = 'mangathemesia'
baseUrl = 'https://asurascansfree.com'
overrideVersionCode = 0
isNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,10 +0,0 @@
package eu.kanade.tachiyomi.extension.en.asurascansfree
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
class AsuraScansFree : MangaThemesia(
"Asura Scans Free (unoriginal)",
"https://asurascansfree.com",
"en",
"/serie",
)

View File

@ -1,10 +0,0 @@
ext {
extName = 'Inmoral No Fansub'
extClass = '.InmoralNoFansub'
themePkg = 'madara'
baseUrl = 'https://inmoralnofansub.xyz'
overrideVersionCode = 0
isNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View File

@ -1,21 +0,0 @@
package eu.kanade.tachiyomi.extension.es.inmoralnofansub
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient
import java.text.SimpleDateFormat
import java.util.Locale
class InmoralNoFansub : Madara(
"Inmoral No Fansub",
"https://inmoralnofansub.xyz",
"es",
SimpleDateFormat("dd/MM/yyyy", Locale("es")),
) {
override val useLoadMoreRequest = LoadMoreStrategy.Always
override val useNewChapterEndpoint = true
override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(2)
.build()
}

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".vi.yurineko.YuriNekoUrlActivity"
android:excludeFromRecents="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="yurineko.site"
android:scheme="https" />
<data android:pathPattern="/manga/..*" />
<data android:pathPattern="/origin/..*" />
<data android:pathPattern="/author/..*" />
<data android:pathPattern="/tag/..*" />
<data android:pathPattern="/couple/..*" />
<data android:pathPattern="/team/..*" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -1,8 +0,0 @@
ext {
extName = 'YuriNeko'
extClass = '.YuriNeko'
extVersionCode = 8
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,453 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.yurineko
import android.content.SharedPreferences
import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.extension.vi.yurineko.dto.ErrorResponseDto
import eu.kanade.tachiyomi.extension.vi.yurineko.dto.MangaDto
import eu.kanade.tachiyomi.extension.vi.yurineko.dto.MangaListDto
import eu.kanade.tachiyomi.extension.vi.yurineko.dto.ReadResponseDto
import eu.kanade.tachiyomi.extension.vi.yurineko.dto.UserDto
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.Filter
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 keiyoushi.utils.getPreferences
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.CacheControl
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import rx.Observable
import java.io.IOException
import java.net.URLDecoder
import java.util.concurrent.TimeUnit
class YuriNeko : HttpSource(), ConfigurableSource {
override val name = "YuriNeko"
private val defaultDomain = "yurineko.site"
override val baseUrl by lazy { "https://${getPrefDomain()}" }
override val lang = "vi"
override val supportsLatest = false
private val apiUrl by lazy { "https://api.${getPrefDomain()}" }
private val storageUrl by lazy { "https://storage.${getPrefDomain()}" }
override val client = network.cloudflareClient.newBuilder()
.rateLimit(3, 1, TimeUnit.SECONDS)
.addInterceptor(::authIntercept)
.addInterceptor(::errorIntercept)
.build()
override fun headersBuilder() = Headers.Builder().add("Referer", "$baseUrl/")
private fun authIntercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val cookies = client.cookieJar.loadForRequest(baseUrl.toHttpUrl())
val authCookie = cookies
.firstOrNull { it.name == "user" }
?.let { URLDecoder.decode(it.value, "UTF-8") }
?.let { json.decodeFromString<UserDto>(it) }
?: return chain.proceed(request)
val authRequest = request.newBuilder().apply {
addHeader("Authorization", "Bearer ${authCookie.token}")
}.build()
return chain.proceed(authRequest)
}
private fun errorIntercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
if (response.code >= 400) {
val error = try {
response.parseAs<ErrorResponseDto>()
} catch (_: Throwable) {
return response
}
response.close()
throw IOException("${error.message}\nĐăng nhập qua WebView và thử lại.")
}
return response
}
override fun popularMangaRequest(page: Int): Request = GET(
url = apiUrl.toHttpUrl().newBuilder().apply {
addPathSegment("lastest2")
addQueryParameter("page", page.toString())
}.build().toString(),
cache = CacheControl.FORCE_NETWORK,
)
override fun popularMangaParse(response: Response): MangasPage {
val mangaListDto = response.parseAs<MangaListDto>()
val currentPage = response.request.url.queryParameter("page")!!.toFloat()
return mangaListDto.toMangasPage(currentPage, storageUrl)
}
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException()
override fun latestUpdatesParse(response: Response): MangasPage = throw UnsupportedOperationException()
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return when {
query.startsWith(PREFIX_ID_SEARCH) -> {
val id = query.removePrefix(PREFIX_ID_SEARCH).trim()
if (id.toIntOrNull() == null) {
throw Exception("ID tìm kiếm không hợp lệ (phải là một số).")
}
fetchMangaDetails(
SManga.create().apply {
url = "/manga/$id"
},
)
.map { MangasPage(listOf(it), false) }
}
else -> super.fetchSearchManga(page, query, filters)
}
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
return when {
query.startsWith(PREFIX_TAG_SEARCH) ||
query.startsWith(PREFIX_COUPLE_SEARCH) ||
query.startsWith(PREFIX_DOUJIN_SEARCH) ||
query.startsWith(PREFIX_AUTHOR_SEARCH) ||
query.startsWith(PREFIX_TEAM_SEARCH) -> {
val items = query.split(":")
val searchType = items[0]
val actualQuery = items[1].trim()
if (actualQuery.toIntOrNull() == null) {
throw Exception("ID tìm kiếm không hợp lệ (phải là một số).")
}
GET(
apiUrl.toHttpUrl().newBuilder().apply {
addPathSegment("searchType")
addQueryParameter("type", searchType)
addQueryParameter("id", actualQuery)
addQueryParameter("page", page.toString())
}.build().toString(),
)
}
query.isNotEmpty() -> {
GET(
apiUrl.toHttpUrl().newBuilder().apply {
addPathSegment("search")
addQueryParameter("query", query)
addQueryParameter("page", page.toString())
}.build().toString(),
)
}
else -> {
for (filter in (if (filters.isEmpty()) getFilterList() else filters)) {
when (filter) {
is UriPartFilter -> if (filter.state != 0) {
when (filter.name) {
"Tag" -> return GET(
apiUrl.toHttpUrl().newBuilder().apply {
addPathSegment("searchType")
addQueryParameter("type", "tag")
addQueryParameter("id", filter.toUriPart())
addQueryParameter("page", page.toString())
}.build().toString(),
)
else -> continue
}
}
else -> {}
}
}
return popularMangaRequest(page)
}
}
}
override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response)
override fun fetchMangaDetails(manga: SManga): Observable<SManga> =
client.newCall(GET("$apiUrl${manga.url}"))
.asObservableSuccess()
.map { mangaDetailsParse(it) }
override fun mangaDetailsRequest(manga: SManga): Request = GET("$baseUrl${manga.url}")
override fun mangaDetailsParse(response: Response): SManga =
response.parseAs<MangaDto>().toSManga(storageUrl)
override fun chapterListRequest(manga: SManga): Request = GET("$apiUrl${manga.url}")
override fun chapterListParse(response: Response): List<SChapter> {
val mangaDto = response.parseAs<MangaDto>()
val scanlator = mangaDto.team.joinToString(", ") { it.name }
return mangaDto.chapters?.map { it.toSChapter(scanlator) } ?: emptyList()
}
override fun pageListRequest(chapter: SChapter): Request = GET("$apiUrl${chapter.url}")
override fun pageListParse(response: Response): List<Page> =
response.parseAs<ReadResponseDto>().toPageList(storageUrl)
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
open class UriPartFilter(displayName: String, private val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
override fun getFilterList() = FilterList(
Filter.Header("Lưu ý rằng không thể vừa tìm kiếm vừa lọc bằng tag."),
Filter.Header("Tìm kiếm sẽ được ưu tiên."),
UriPartFilter("Tag", getGenreList()),
)
private fun getGenreList() = arrayOf(
Pair("Sao cũng được", "0"),
Pair("4-koma", "149"),
Pair(">", "306"),
Pair("Action", "113"),
Pair("Adventure", "114"),
Pair("Adult Life", "143"),
Pair("Animal Ears", "175"),
Pair("Age Gap", "179"),
Pair("Anal", "209"),
Pair("Ahegao", "211"),
Pair("Anime", "214"),
Pair("Amnesia", "242"),
Pair("Autobiographical", "255"),
Pair("Alien", "262"),
Pair("Amputee", "277"),
Pair("Assassin", "283"),
Pair("Angel", "298"),
Pair("Abuse", "300"),
Pair("Anilingus", "308"),
Pair("Blushing", "157"),
Pair("Body Swap", "158"),
Pair("Bisexual", "176"),
Pair("Birthday", "194"),
Pair("Big Breasts", "195"),
Pair("Butts", "196"),
Pair("BDSM", "199"),
Pair("Boob Sex", "210"),
Pair("Bath", "226"),
Pair("Bullying", "241"),
Pair("Biting", "270"),
Pair("Blackmail", "280"),
Pair("Biographical", "285"),
Pair("Beach", "289"),
Pair("BHTT", "304"),
Pair("Comedy", "115"),
Pair("College", "145"),
Pair("Co-worker", "180"),
Pair("Childhood Friends", "182"),
Pair("Christmas", "189"),
Pair("Creepy", "220"),
Pair("Childification", "239"),
Pair("Cheating", "267"),
Pair("Clones", "271"),
Pair("Cross-dressing", "288"),
Pair("Chibi", "307"),
Pair("Demon", "116"),
Pair("Drama", "117"),
Pair("Dark Skin", "208"),
Pair("Drunk", "219"),
Pair("Drugs", "236"),
Pair("Disability", "252"),
Pair("Delinquent", "258"),
Pair("Deity", "265"),
Pair("Depressing as fuck", "290"),
Pair("Ecchi", "118"),
Pair("Excuse me WTF?", "161"),
Pair("Exhibitionism", "245"),
Pair("Fantasy", "119"),
Pair("Full Color", "148"),
Pair("FBI Warning!!", "163"),
Pair("Futanari", "201"),
Pair("Food", "232"),
Pair("Feet", "256"),
Pair("Furry", "303"),
Pair("Game", "120"),
Pair("Gender Bender", "121"),
Pair("Glasses", "156"),
Pair("Guro", "206"),
Pair("Ghost", "244"),
Pair("Gyaru", "246"),
Pair("Harem", "122"),
Pair("Historical", "123"),
Pair("Horror", "124"),
Pair("Hints", "152"),
Pair("Het", "160"),
Pair("Halloween", "190"),
Pair("Hypnosis", "254"),
Pair("Height Gap", "281"),
Pair("Hardcore", "292"),
Pair("Isekai", "144"),
Pair("Idol", "169"),
Pair("Incest", "187"),
Pair("Idiot Couple", "282"),
Pair("Introspective", "286"),
Pair("Insane Amounts of Sex", "296"),
Pair("Kuudere", "235"),
Pair("Lỗi: không tìm thấy trai", "153"),
Pair("Love Triangle", "183"),
Pair("Loli", "197"),
Pair("Light Novel", "216"),
Pair("Lactation", "260"),
Pair("Lots of sex", "269"),
Pair("Martial Arts", "125"),
Pair("Mecha", "126"),
Pair("Military", "127"),
Pair("Music", "128"),
Pair("Mystery", "129"),
Pair("Manhua", "146"),
Pair("Manhwa", "147"),
Pair("Moe Paradise", "164"),
Pair("Mahou Shoujo", "168"),
Pair("Maid", "172"),
Pair("Monster Girl", "173"),
Pair("Marriage", "188"),
Pair("Massage", "204"),
Pair("Masturbation", "205"),
Pair("Mangaka", "227"),
Pair("Mermaid", "234"),
Pair("Moderate amounts of sex", "268"),
Pair("Miko", "301"),
Pair("No Text", "150"),
Pair("New Year's", "191"),
Pair("Netorare", "198"),
Pair("NSFW", "229"),
Pair("Ninja", "287"),
Pair("Non-moe art", "302"),
Pair("Office Lady", "174"),
Pair("Oneshot", "218"),
Pair("Official", "222"),
Pair("Orgy", "261"),
Pair("Omegaverse", "276"),
Pair("Parody", "130"),
Pair("Psychological", "131"),
Pair("Pay for Gay", "162"),
Pair("Polyamory", "185"),
Pair("Pocky Game", "212"),
Pair("Prostitution", "240"),
Pair("Player", "257"),
Pair("Prequel", "272"),
Pair("Post-Apocalyptic", "273"),
Pair("Philosophical", "274"),
Pair("R18", "1"),
Pair("Romance", "132"),
Pair("Reversal", "159"),
Pair("Roommates", "181"),
Pair("Rape", "203"),
Pair("Robot", "264"),
Pair("School Life", "133"),
Pair("Sci-Fi", "134"),
Pair("Slice of Life", "137"),
Pair("Sports", "138"),
Pair("Supernatural", "139"),
Pair("Science Babies", "165"),
Pair("Student x Teacher", "166"),
Pair("Siscon", "167"),
Pair("School Girl", "215"),
Pair("Spin-off", "223"),
Pair("Subtext", "231"),
Pair("Sleeping", "249"),
Pair("Sequel", "251"),
Pair("Swimsuits", "263"),
Pair("Stalking", "266"),
Pair("Space", "291"),
Pair("Spanking", "299"),
Pair("Tragedy", "142"),
Pair("Tomboy", "170"),
Pair("Tsundere", "177"),
Pair("Threesome", "184"),
Pair("Twins", "186"),
Pair("Thất Tịch", "193"),
Pair("Toys", "200"),
Pair("Tentacles", "202"),
Pair("Tailsex", "237"),
Pair("Time Travel", "243"),
Pair("Transgender", "284"),
Pair("Vampire", "140"),
Pair("Violence", "141"),
Pair("Valentine", "192"),
Pair("Watersports", "278"),
Pair("Wholesome", "279"),
Pair("Witch", "293"),
Pair("Web Novel", "305"),
Pair("Yuri", "151"),
Pair("Yankee", "171"),
Pair("Yandere", "178"),
Pair("Yuri Crush", "228"),
Pair("Yaoi", "230"),
Pair("Zombies", "238"),
)
private val json = Json {
isLenient = true
ignoreUnknownKeys = true
prettyPrint = true
}
private inline fun <reified T> Response.parseAs(): T = use {
json.decodeFromString(body.string())
}
private val preferences: SharedPreferences = getPreferences()
init {
preferences.getString(DEFAULT_DOMAIN_PREF, null).let { prefDefaultDomain ->
if (prefDefaultDomain != defaultDomain) {
preferences.edit()
.putString(BASE_DOMAIN_PREF, defaultDomain)
.putString(DEFAULT_DOMAIN_PREF, defaultDomain)
.apply()
}
}
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
EditTextPreference(screen.context).apply {
key = BASE_DOMAIN_PREF
title = BASE_DOMAIN_PREF_TITLE
summary = BASE_DOMAIN_PREF_SUMMARY
setDefaultValue(defaultDomain)
dialogTitle = BASE_DOMAIN_PREF_TITLE
dialogMessage = "Default: $defaultDomain"
setOnPreferenceChangeListener { _, _ ->
Toast.makeText(screen.context, RESTART_APP, Toast.LENGTH_LONG).show()
true
}
}.let(screen::addPreference)
}
private fun getPrefDomain(): String = preferences.getString(BASE_DOMAIN_PREF, defaultDomain)!!
companion object {
const val PREFIX_ID_SEARCH = "id:"
const val PREFIX_TAG_SEARCH = "tag:"
const val PREFIX_TEAM_SEARCH = "team:"
const val PREFIX_AUTHOR_SEARCH = "author:"
const val PREFIX_DOUJIN_SEARCH = "origin:"
const val PREFIX_COUPLE_SEARCH = "couple:"
private const val DEFAULT_DOMAIN_PREF = "defaultDomain"
private const val RESTART_APP = "Khởi chạy lại ứng dụng để áp dụng thay đổi."
private const val BASE_DOMAIN_PREF_TITLE = "Ghi đè URL cơ sở"
private const val BASE_DOMAIN_PREF = "overrideDomain"
private const val BASE_DOMAIN_PREF_SUMMARY =
"Dành cho sử dụng tạm thời, cập nhật tiện ích sẽ xóa cài đặt."
}
}

View File

@ -1,44 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.yurineko
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.util.Log
import kotlin.system.exitProcess
class YuriNekoUrlActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val pathSegments = intent?.data?.pathSegments
if (pathSegments != null && pathSegments.size > 1) {
val id = pathSegments[1]
try {
startActivity(
Intent().apply {
action = "eu.kanade.tachiyomi.SEARCH"
with(pathSegments[0]) {
when {
equals("manga") -> putExtra("query", "${YuriNeko.PREFIX_ID_SEARCH}$id")
equals("origin") -> putExtra("query", "${YuriNeko.PREFIX_DOUJIN_SEARCH}$id")
equals("author") -> putExtra("query", "${YuriNeko.PREFIX_AUTHOR_SEARCH}$id")
equals("tag") -> putExtra("query", "${YuriNeko.PREFIX_TAG_SEARCH}$id")
equals("couple") -> putExtra("query", "${YuriNeko.PREFIX_COUPLE_SEARCH}$id")
equals("team") -> putExtra("query", "${YuriNeko.PREFIX_TEAM_SEARCH}$id")
else -> putExtra("query", "${YuriNeko.PREFIX_ID_SEARCH}$id")
}
}
putExtra("filter", packageName)
},
)
} catch (e: ActivityNotFoundException) {
Log.e("YuriNekoUrlActivity", e.toString())
}
} else {
Log.e("YuriNekoUrlActivity", "Could not parse URI from intent $intent")
}
finish()
exitProcess(0)
}
}

View File

@ -1,54 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.yurineko.dto
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import kotlinx.serialization.Serializable
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone
val DATE_FORMATTER = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply {
timeZone = TimeZone.getTimeZone("Asia/Ho_Chi_Minh")
}
val CHAPTER_NUMBER_REGEX = Regex("""[+\-]?([0-9]*[\.])?[0-9]+""", RegexOption.IGNORE_CASE)
@Serializable
data class ChapterDto(
val id: Int,
val name: String,
val date: String? = null,
val mangaID: Int? = null,
val maxID: Int? = null,
val likeCount: Int? = null,
) {
fun toSChapter(teams: String): SChapter = SChapter.create().apply {
val dto = this@ChapterDto
url = "/read/${dto.mangaID}/${dto.id}"
name = dto.name
if (!dto.date.isNullOrEmpty()) {
date_upload = runCatching {
DATE_FORMATTER.parse(dto.date)?.time
}.getOrNull() ?: 0L
}
val match = CHAPTER_NUMBER_REGEX.findAll(dto.name)
chapter_number = if (match.count() > 1 && dto.name.lowercase().startsWith("vol")) {
match.elementAt(1)
} else {
match.elementAtOrNull(0)
}?.value?.toFloat() ?: -1f
scanlator = teams
}
}
@Serializable
data class ReadResponseDto(
val listChapter: List<ChapterDto>,
val chapterInfo: ChapterDto,
val url: List<String>,
) {
fun toPageList(storageUrl: String): List<Page> = this@ReadResponseDto
.url
.mapIndexed { index, url -> Page(index, imageUrl = storageUrl + url) }
}

View File

@ -1,71 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.yurineko.dto
import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.Serializable
import org.jsoup.Jsoup
import kotlin.math.ceil
@Serializable
data class MangaDto(
val id: Int,
val originalName: String,
val otherName: String,
val description: String,
val status: Int,
val thumbnail: String,
val type: String,
val lastUpdate: String,
val totalView: Int? = null,
val totalFollow: Int? = null,
val likeCount: Int? = null,
val team: List<TagDto>,
val origin: List<TagDto>,
val author: List<TagDto>,
val tag: List<TagDto>,
val couple: List<TagDto>,
val lastChapter: ChapterDto? = null,
val chapters: List<ChapterDto>? = null,
) {
fun toSManga(storageUrl: String): SManga = SManga.create().apply {
val dto = this@MangaDto
url = "/manga/${dto.id}"
title = dto.originalName
author = dto.author.joinToString(", ") { author -> author.name }
val descElem = Jsoup.parseBodyFragment(dto.description).select("p")
.joinToString("\n") { it.wholeText() }.trim()
description = if (dto.otherName.isNotEmpty()) {
"Tên khác: ${dto.otherName}\n\n" + descElem
} else {
descElem
}
genre = dto.tag.joinToString(", ") { tag -> tag.name }
status = when (dto.status) {
1 -> SManga.UNKNOWN // "Chưa ra mắt" -> Not released
2 -> SManga.COMPLETED
3 -> SManga.UNKNOWN // "Sắp ra mắt" -> Upcoming
4 -> SManga.ONGOING
5 -> SManga.CANCELLED // "Ngừng dịch" -> source not translating it anymomre
6 -> SManga.ON_HIATUS
7 -> SManga.CANCELLED // "Ngừng xuất bản" -> No more publications
else -> SManga.UNKNOWN
}
thumbnail_url = "$storageUrl/" + dto.thumbnail
initialized = true
}
}
@Serializable
data class MangaListDto(
val result: List<MangaDto>,
val resultCount: Int,
) {
fun toMangasPage(currentPage: Float = 1f, storageUrl: String): MangasPage {
val dto = this@MangaListDto
return MangasPage(
dto.result.map { it.toSManga(storageUrl) },
currentPage + 1f <= ceil(dto.resultCount.toFloat() / 20f),
)
}
}

View File

@ -1,22 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.yurineko.dto
import kotlinx.serialization.Serializable
@Serializable
data class ErrorResponseDto(
val message: String? = null,
)
@Serializable
data class UserDto(
val id: Int,
val name: String,
val email: String,
val avatar: String,
val role: Int,
val money: Int,
val username: String,
val isBanned: Int,
val isPremium: Int,
val token: String,
)

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.vi.yurineko.dto
import kotlinx.serialization.Serializable
@Serializable
data class TagDto(
val id: Int,
val name: String,
val url: String,
val origin: String? = null,
)