diff --git a/src/en/asurascansfree/build.gradle b/src/en/asurascansfree/build.gradle
deleted file mode 100644
index 48bb6bd37..000000000
--- a/src/en/asurascansfree/build.gradle
+++ /dev/null
@@ -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"
diff --git a/src/en/asurascansfree/res/mipmap-hdpi/ic_launcher.png b/src/en/asurascansfree/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 9ec41f51c..000000000
Binary files a/src/en/asurascansfree/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/asurascansfree/res/mipmap-mdpi/ic_launcher.png b/src/en/asurascansfree/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 74bc64103..000000000
Binary files a/src/en/asurascansfree/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/asurascansfree/res/mipmap-xhdpi/ic_launcher.png b/src/en/asurascansfree/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 68decbfc5..000000000
Binary files a/src/en/asurascansfree/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/asurascansfree/res/mipmap-xxhdpi/ic_launcher.png b/src/en/asurascansfree/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 0c9d21c59..000000000
Binary files a/src/en/asurascansfree/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/asurascansfree/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/asurascansfree/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 261b9e6ac..000000000
Binary files a/src/en/asurascansfree/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/asurascansfree/src/eu/kanade/tachiyomi/extension/en/asurascansfree/AsuraScansFree.kt b/src/en/asurascansfree/src/eu/kanade/tachiyomi/extension/en/asurascansfree/AsuraScansFree.kt
deleted file mode 100644
index 789e500d1..000000000
--- a/src/en/asurascansfree/src/eu/kanade/tachiyomi/extension/en/asurascansfree/AsuraScansFree.kt
+++ /dev/null
@@ -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",
-)
diff --git a/src/es/inmoralnofansub/build.gradle b/src/es/inmoralnofansub/build.gradle
deleted file mode 100644
index c397c1ed0..000000000
--- a/src/es/inmoralnofansub/build.gradle
+++ /dev/null
@@ -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"
diff --git a/src/es/inmoralnofansub/res/mipmap-hdpi/ic_launcher.png b/src/es/inmoralnofansub/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index d549449a0..000000000
Binary files a/src/es/inmoralnofansub/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/es/inmoralnofansub/res/mipmap-mdpi/ic_launcher.png b/src/es/inmoralnofansub/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index d16867ebb..000000000
Binary files a/src/es/inmoralnofansub/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/es/inmoralnofansub/res/mipmap-xhdpi/ic_launcher.png b/src/es/inmoralnofansub/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index b61f0bb70..000000000
Binary files a/src/es/inmoralnofansub/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/es/inmoralnofansub/res/mipmap-xxhdpi/ic_launcher.png b/src/es/inmoralnofansub/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 952b01cdd..000000000
Binary files a/src/es/inmoralnofansub/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/es/inmoralnofansub/res/mipmap-xxxhdpi/ic_launcher.png b/src/es/inmoralnofansub/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 0ab1febff..000000000
Binary files a/src/es/inmoralnofansub/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/es/inmoralnofansub/src/eu/kanade/tachiyomi/extension/es/inmoralnofansub/InmoralNoFansub.kt b/src/es/inmoralnofansub/src/eu/kanade/tachiyomi/extension/es/inmoralnofansub/InmoralNoFansub.kt
deleted file mode 100644
index 80a50e5f7..000000000
--- a/src/es/inmoralnofansub/src/eu/kanade/tachiyomi/extension/es/inmoralnofansub/InmoralNoFansub.kt
+++ /dev/null
@@ -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()
-}
diff --git a/src/vi/yurineko/AndroidManifest.xml b/src/vi/yurineko/AndroidManifest.xml
deleted file mode 100644
index e0ac1498b..000000000
--- a/src/vi/yurineko/AndroidManifest.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/vi/yurineko/build.gradle b/src/vi/yurineko/build.gradle
deleted file mode 100644
index cf9edfe6d..000000000
--- a/src/vi/yurineko/build.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
-ext {
- extName = 'YuriNeko'
- extClass = '.YuriNeko'
- extVersionCode = 8
- isNsfw = true
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/vi/yurineko/res/mipmap-hdpi/ic_launcher.png b/src/vi/yurineko/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index c3d989632..000000000
Binary files a/src/vi/yurineko/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/vi/yurineko/res/mipmap-mdpi/ic_launcher.png b/src/vi/yurineko/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index ae69ab49f..000000000
Binary files a/src/vi/yurineko/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/vi/yurineko/res/mipmap-xhdpi/ic_launcher.png b/src/vi/yurineko/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index e5f4765d1..000000000
Binary files a/src/vi/yurineko/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/vi/yurineko/res/mipmap-xxhdpi/ic_launcher.png b/src/vi/yurineko/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 037377b7a..000000000
Binary files a/src/vi/yurineko/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/vi/yurineko/res/mipmap-xxxhdpi/ic_launcher.png b/src/vi/yurineko/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index fb0201174..000000000
Binary files a/src/vi/yurineko/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/YuriNeko.kt b/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/YuriNeko.kt
deleted file mode 100644
index d3a4ce756..000000000
--- a/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/YuriNeko.kt
+++ /dev/null
@@ -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(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()
- } 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()
- 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 {
- 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 =
- 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().toSManga(storageUrl)
-
- override fun chapterListRequest(manga: SManga): Request = GET("$apiUrl${manga.url}")
-
- override fun chapterListParse(response: Response): List {
- val mangaDto = response.parseAs()
- 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 =
- response.parseAs().toPageList(storageUrl)
-
- override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
-
- open class UriPartFilter(displayName: String, private val vals: Array>) :
- Filter.Select(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 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."
- }
-}
diff --git a/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/YuriNekoUrlActivity.kt b/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/YuriNekoUrlActivity.kt
deleted file mode 100644
index e0b54cc9e..000000000
--- a/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/YuriNekoUrlActivity.kt
+++ /dev/null
@@ -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)
- }
-}
diff --git a/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/dto/ChapterDto.kt b/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/dto/ChapterDto.kt
deleted file mode 100644
index 2b6075b4c..000000000
--- a/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/dto/ChapterDto.kt
+++ /dev/null
@@ -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,
- val chapterInfo: ChapterDto,
- val url: List,
-) {
- fun toPageList(storageUrl: String): List = this@ReadResponseDto
- .url
- .mapIndexed { index, url -> Page(index, imageUrl = storageUrl + url) }
-}
diff --git a/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/dto/MangaDto.kt b/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/dto/MangaDto.kt
deleted file mode 100644
index d252263ae..000000000
--- a/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/dto/MangaDto.kt
+++ /dev/null
@@ -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,
- val origin: List,
- val author: List,
- val tag: List,
- val couple: List,
- val lastChapter: ChapterDto? = null,
- val chapters: List? = 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,
- 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),
- )
- }
-}
diff --git a/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/dto/MiscDto.kt b/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/dto/MiscDto.kt
deleted file mode 100644
index 082dbfd93..000000000
--- a/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/dto/MiscDto.kt
+++ /dev/null
@@ -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,
-)
diff --git a/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/dto/TagDto.kt b/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/dto/TagDto.kt
deleted file mode 100644
index 0ab72f512..000000000
--- a/src/vi/yurineko/src/eu/kanade/tachiyomi/extension/vi/yurineko/dto/TagDto.kt
+++ /dev/null
@@ -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,
-)