diff --git a/.github/workflows/issue_moderator.yml b/.github/workflows/issue_moderator.yml index 93527ab35..92cd7b72e 100644 --- a/.github/workflows/issue_moderator.yml +++ b/.github/workflows/issue_moderator.yml @@ -35,7 +35,7 @@ jobs: }, { "type": "both", - "regex": ".*(mangago|mangafox|hq\\s*dragon|manga\\s*host|supermangas|superhentais|union\\s*mangas|yes\\s*mangas|manhuascan|heroscan|manhwahot|leitor\\.?net|manga\\s*livre|tsuki\\s*mangas|manga\\s*yabu|mangas\\.in|mangas\\.pw|hentaikai|toptoon\\+?|read\\s*comic\\s*online|coco\\s*manhua|hitomi\\.la).*", + "regex": ".*(mangago|mangafox|hq\\s*dragon|manga\\s*host|supermangas|superhentais|union\\s*mangas|yes\\s*mangas|manhuascan|heroscan|manhwahot|leitor\\.?net|manga\\s*livre|tsuki\\s*mangas|manga\\s*yabu|mangas\\.in|mangas\\.pw|hentaikai|toptoon\\+?|read\\s*comic\\s*online|coco\\s*manhua|hitomi\\.la|copymanga).*", "ignoreCase": true, "message": "{match} will not be added back as it is too difficult to maintain. Read #3475 for more information" }, diff --git a/src/zh/copymanga/AndroidManifest.xml b/src/zh/copymanga/AndroidManifest.xml deleted file mode 100644 index 30deb7f79..000000000 --- a/src/zh/copymanga/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/zh/copymanga/build.gradle b/src/zh/copymanga/build.gradle deleted file mode 100644 index 280ddfbab..000000000 --- a/src/zh/copymanga/build.gradle +++ /dev/null @@ -1,26 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlinx-serialization' - -ext { - extName = 'CopyManga' - pkgNameSuffix = 'zh.copymanga' - extClass = '.CopyManga' - extVersionCode = 31 -} - -dependencies { - implementation 'com.luhuiguo:chinese-utils:1.0' -} - -apply from: "$rootDir/common.gradle" - -android { - packagingOptions { - exclude '/pinyin.txt' - exclude '/polyphone.txt' - exclude '/trad.txt' - exclude '/traditional.txt' - exclude '/unknown.txt' - } -} diff --git a/src/zh/copymanga/res/mipmap-hdpi/ic_launcher.png b/src/zh/copymanga/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index d71429b79..000000000 Binary files a/src/zh/copymanga/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/src/zh/copymanga/res/mipmap-mdpi/ic_launcher.png b/src/zh/copymanga/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 5bb6d9327..000000000 Binary files a/src/zh/copymanga/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/src/zh/copymanga/res/mipmap-xhdpi/ic_launcher.png b/src/zh/copymanga/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 90b447b9d..000000000 Binary files a/src/zh/copymanga/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/src/zh/copymanga/res/mipmap-xxhdpi/ic_launcher.png b/src/zh/copymanga/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 4f85d5cd4..000000000 Binary files a/src/zh/copymanga/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/zh/copymanga/res/mipmap-xxxhdpi/ic_launcher.png b/src/zh/copymanga/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 387681a58..000000000 Binary files a/src/zh/copymanga/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/zh/copymanga/res/web_hi_res_512.png b/src/zh/copymanga/res/web_hi_res_512.png deleted file mode 100644 index f2d6b4ab5..000000000 Binary files a/src/zh/copymanga/res/web_hi_res_512.png and /dev/null differ diff --git a/src/zh/copymanga/src/eu/kanade/tachiyomi/extension/zh/copymanga/CopyManga.kt b/src/zh/copymanga/src/eu/kanade/tachiyomi/extension/zh/copymanga/CopyManga.kt deleted file mode 100644 index 6d1a9ad9f..000000000 --- a/src/zh/copymanga/src/eu/kanade/tachiyomi/extension/zh/copymanga/CopyManga.kt +++ /dev/null @@ -1,356 +0,0 @@ -package eu.kanade.tachiyomi.extension.zh.copymanga - -import android.app.Application -import android.content.SharedPreferences -import android.util.Log -import android.widget.Toast -import androidx.preference.EditTextPreference -import androidx.preference.ListPreference -import androidx.preference.PreferenceScreen -import androidx.preference.SwitchPreferenceCompat -import eu.kanade.tachiyomi.extension.zh.copymanga.MangaDto.Companion.parseChapterGroups -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.asObservableSuccess -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 kotlinx.serialization.json.Json -import kotlinx.serialization.json.decodeFromStream -import okhttp3.Headers -import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import rx.Observable -import rx.Single -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy -import kotlin.concurrent.thread - -class CopyManga : HttpSource(), ConfigurableSource { - override val name = "拷贝漫画" - override val lang = "zh" - override val supportsLatest = true - - private val json: Json by injectLazy() - - private val preferences: SharedPreferences = - Injekt.get().getSharedPreferences("source_$id", 0x0000) - - private var domain = DOMAINS[preferences.getString(DOMAIN_PREF, "0")!!.toInt().coerceIn(0, DOMAINS.size - 1)] - override val baseUrl = WWW_PREFIX + domain - private var apiUrl = API_PREFIX + domain // www. 也可以 - - override val client: OkHttpClient = network.client.newBuilder() - .addInterceptor(NonblockingRateLimitInterceptor(2, 4)) // 2 requests per 4 seconds - .build() - - private fun Headers.Builder.setUserAgent(userAgent: String) = set("User-Agent", userAgent) - private fun Headers.Builder.setRegion(useOverseasCdn: Boolean) = set("region", if (useOverseasCdn) "0" else "1") - private fun Headers.Builder.setReferer() = set("Referer", WWW_PREFIX + domain) - private fun Headers.Builder.setVersion(version: String) = set("version", version) - - override fun headersBuilder() = Headers.Builder() - .setUserAgent(preferences.getString(USER_AGENT_PREF, DEFAULT_USER_AGENT)!!) - .setRegion(preferences.getBoolean(OVERSEAS_CDN_PREF, false)) - .setReferer() - .add("platform", "1") - .setVersion(preferences.getString(VERSION_PREF, DEFAULT_VERSION)!!) - - private var apiHeaders = headersBuilder().build() - - private var useWebp = preferences.getBoolean(WEBP_PREF, true) - - init { - MangaDto.convertToSc = preferences.getBoolean(SC_TITLE_PREF, false) - } - - override fun popularMangaRequest(page: Int): Request { - val offset = PAGE_SIZE * (page - 1) - return GET("$apiUrl/api/v3/recs?pos=3200102&limit=$PAGE_SIZE&offset=$offset", apiHeaders) - } - - override fun popularMangaParse(response: Response): MangasPage { - val page: ListDto = response.parseAs() - val hasNextPage = page.offset + page.limit < page.total - return MangasPage(page.list.map { it.toSManga() }, hasNextPage) - } - - override fun latestUpdatesRequest(page: Int): Request { - val offset = PAGE_SIZE * (page - 1) - return GET("$apiUrl/api/v3/update/newest?limit=$PAGE_SIZE&offset=$offset", apiHeaders) - } - - override fun latestUpdatesParse(response: Response) = popularMangaParse(response) - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val offset = PAGE_SIZE * (page - 1) - val builder = apiUrl.toHttpUrl().newBuilder() - .addQueryParameter("limit", "$PAGE_SIZE") - .addQueryParameter("offset", "$offset") - if (query.isNotBlank()) { - builder.addPathSegments("api/v3/search/comic") - .addQueryParameter("q", query) - filters.filterIsInstance().firstOrNull()?.addQuery(builder) - } else { - builder.addPathSegments("api/v3/comics") - filters.filterIsInstance().forEach { - if (it !is SearchFilter) it.addQuery(builder) - } - } - return Request.Builder().url(builder.build()).headers(apiHeaders).build() - } - - override fun searchMangaParse(response: Response): MangasPage { - val page: ListDto = response.parseAs() - val hasNextPage = page.offset + page.limit < page.total - return MangasPage(page.list.map { it.toSManga() }, hasNextPage) - } - - // 让 WebView 打开网页而不是 API - override fun mangaDetailsRequest(manga: SManga) = GET(WWW_PREFIX + domain + manga.url, apiHeaders) - - private fun realMangaDetailsRequest(manga: SManga) = - GET("$apiUrl/api/v3/comic2/${manga.url.removePrefix(MangaDto.URL_PREFIX)}", apiHeaders) - - override fun fetchMangaDetails(manga: SManga): Observable = - client.newCall(realMangaDetailsRequest(manga)).asObservableSuccess().map { mangaDetailsParse(it) } - - override fun mangaDetailsParse(response: Response): SManga = - response.parseAs().toSMangaDetails() - - override fun fetchChapterList(manga: SManga): Observable> = Single.create> { - val result = ArrayList() - val groups = manga.description?.parseChapterGroups() ?: run { - val response = client.newCall(realMangaDetailsRequest(manga)).execute() - response.parseAs().groups!!.values - } - val mangaSlug = manga.url.removePrefix(MangaDto.URL_PREFIX) - result.fetchChapterGroup(mangaSlug, "default", "") - for (group in groups) { - result.fetchChapterGroup(mangaSlug, group.path_word, group.name) - } - it.onSuccess(result) - }.toObservable() - - private fun ArrayList.fetchChapterGroup(manga: String, key: String, name: String) { - val result = ArrayList(0) - var offset = 0 - var hasNextPage = true - while (hasNextPage) { - val response = client.newCall(GET("$apiUrl/api/v3/comic/$manga/group/$key/chapters?limit=$CHAPTER_PAGE_SIZE&offset=$offset", apiHeaders)).execute() - val chapters: ListDto = response.parseAs() - result.ensureCapacity(chapters.total) - chapters.list.mapTo(result) { it.toSChapter(name) } - offset += CHAPTER_PAGE_SIZE - hasNextPage = offset < chapters.total - } - addAll(result.asReversed()) - } - - override fun chapterListRequest(manga: SManga) = throw UnsupportedOperationException("Not used.") - override fun chapterListParse(response: Response) = throw UnsupportedOperationException("Not used.") - - // 新版 API 中间是 /chapter2/ 并且返回值需要排序 - override fun pageListRequest(chapter: SChapter) = GET("$apiUrl/api/v3${chapter.url}", apiHeaders) - - override fun pageListParse(response: Response): List { - val result: ChapterPageListWrapperDto = response.parseAs() - if (result.show_app) { - throw Exception("访问受限,请尝试在插件设置中修改 User Agent") - } - return result.chapter.contents.mapIndexed { i, it -> - Page(i, imageUrl = it.url) - } - } - - override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not used.") - - override fun imageRequest(page: Page): Request { - val imageUrl = page.imageUrl!! - return if (useWebp && imageUrl.endsWith(".jpg")) { - GET(imageUrl.removeSuffix(".jpg") + ".webp") - } else { - GET(imageUrl) - } - } - - private inline fun Response.parseAs(): T = use { - if (header("Content-Type") != "application/json") { - throw Exception("访问受限,请尝试在插件设置中修改 User Agent") - } else if (code != 200) { - throw Exception(json.decodeFromStream(body!!.byteStream()).message) - } - json.decodeFromStream>(body!!.byteStream()).results - } - - private var genres: Array = emptyArray() - private var isFetchingGenres = false - - override fun getFilterList(): FilterList { - val genreFilter = if (genres.isEmpty()) { - fetchGenres() - Filter.Header("点击“重置”尝试刷新题材分类") - } else { - GenreFilter(genres) - } - return FilterList( - SearchFilter(), - Filter.Separator(), - Filter.Header("分类(搜索文本时无效)"), - genreFilter, - RegionFilter(), - StatusFilter(), - SortFilter(), - ) - } - - private fun fetchGenres() { - if (genres.isNotEmpty() || isFetchingGenres) return - isFetchingGenres = true - thread { - try { - val response = client.newCall(GET("$apiUrl/api/v3/theme/comic/count?limit=500", apiHeaders)).execute() - val list = response.parseAs>().list - val result = ArrayList(list.size + 1).apply { add(Param("全部", "")) } - genres = list.mapTo(result) { it.toParam() }.toTypedArray() - } catch (e: Exception) { - Log.e("CopyManga", "failed to fetch genres", e) - } finally { - isFetchingGenres = false - } - } - } - - var fetchVersionState = 0 // 0 = not yet or failed, 1 = fetching, 2 = fetched - - override fun setupPreferenceScreen(screen: PreferenceScreen) { - ListPreference(screen.context).apply { - key = DOMAIN_PREF - title = "网址域名" - summary = "连接不稳定时可以尝试切换" - entries = DOMAINS - entryValues = DOMAIN_INDICES - setDefaultValue("0") - setOnPreferenceChangeListener { _, newValue -> - domain = DOMAINS[(newValue as String).toInt()] - apiUrl = API_PREFIX + domain - apiHeaders = apiHeaders.newBuilder().setReferer().build() - true - } - }.let { screen.addPreference(it) } - - EditTextPreference(screen.context).apply { - key = USER_AGENT_PREF - title = "User Agent (UA)" - summary = "可以使用 Windows/macOS/iOS 上浏览器的 UA,不要使用安卓浏览器和 Windows Chrome 103(“在 WebView 中打开”需要重启应用刷新)" - setDefaultValue(DEFAULT_USER_AGENT) - setOnPreferenceChangeListener { _, newValue -> - apiHeaders = apiHeaders.newBuilder().setUserAgent(newValue as String).build() - true - } - }.let { screen.addPreference(it) } - - EditTextPreference(screen.context).apply { - key = UA_CHECKER - title = "获取浏览器 UA 的链接" - summary = "点击后可以在弹出的对话框中复制链接" - setDefaultValue(UA_CHECKER) - setOnPreferenceChangeListener { _, _ -> false } - }.let { screen.addPreference(it) } - - SwitchPreferenceCompat(screen.context).apply { - title = "更新网页版本号" - summary = "点击尝试更新网页版本号,当前为:${preferences.getString(VERSION_PREF, DEFAULT_VERSION)}" - setOnPreferenceChangeListener { _, _ -> - if (fetchVersionState == 1) { - Toast.makeText(screen.context, "已经在尝试更新,请勿反复点击", Toast.LENGTH_SHORT).show() - return@setOnPreferenceChangeListener false - } else if (fetchVersionState == 2) { - Toast.makeText(screen.context, "版本号已经成功更新,返回重进刷新", Toast.LENGTH_SHORT).show() - return@setOnPreferenceChangeListener false - } - Toast.makeText(screen.context, "开始尝试更新网页版本号", Toast.LENGTH_SHORT).show() - fetchVersionState = 1 - thread { - try { - val headers = apiHeaders.newBuilder().setUserAgent(System.getProperty("http.agent")!!).build() - val html = client.newCall(GET("https://www.copymanga.org/h5", headers)).execute().body!!.string() - val jsRegex = Regex("""https\S+?index\.\w+?\.js""") - val jsUrl = jsRegex.find(html)!!.value - val js = client.newCall(GET(jsUrl, headers)).execute().body!!.string() - val versionRegex = Regex("""VERSION:"([\d.]+?)"""", RegexOption.IGNORE_CASE) - val version = versionRegex.find(js)!!.groupValues[1] - preferences.edit().putString(VERSION_PREF, version).apply() - apiHeaders = apiHeaders.newBuilder().setVersion(version).build() - fetchVersionState = 2 - } catch (e: Throwable) { - fetchVersionState = 0 - Log.e("CopyManga", "failed to fetch version", e) - } - } - false - } - }.let { screen.addPreference(it) } - - SwitchPreferenceCompat(screen.context).apply { - key = OVERSEAS_CDN_PREF - title = "使用“港台及海外线路”" - summary = "连接不稳定时可以尝试切换,关闭时使用“大陆用户线路”,已阅读章节需要清空缓存才能生效" - setDefaultValue(false) - setOnPreferenceChangeListener { _, newValue -> - apiHeaders = apiHeaders.newBuilder().setRegion(newValue as Boolean).build() - true - } - }.let { screen.addPreference(it) } - - SwitchPreferenceCompat(screen.context).apply { - key = WEBP_PREF - title = "使用 WebP 图片格式" - summary = "默认开启,可以节省网站流量" - setDefaultValue(true) - setOnPreferenceChangeListener { _, newValue -> - useWebp = newValue as Boolean - true - } - }.let { screen.addPreference(it) } - - SwitchPreferenceCompat(screen.context).apply { - key = SC_TITLE_PREF - title = "将作品标题转换为简体中文" - summary = "修改后,已添加漫画需要迁移才能更新标题" - setDefaultValue(false) - setOnPreferenceChangeListener { _, newValue -> - MangaDto.convertToSc = newValue as Boolean - true - } - }.let { screen.addPreference(it) } - } - - companion object { - private const val DOMAIN_PREF = "domain" - private const val OVERSEAS_CDN_PREF = "changeCDN" - private const val SC_TITLE_PREF = "showSCTitle" - private const val WEBP_PREF = "webp" - private const val USER_AGENT_PREF = "userAgent" - private const val VERSION_PREF = "version" - // private const val CHROME_VERSION_PREF = "chromeVersion" // default value was "103" - - private const val WWW_PREFIX = "https://www." - private const val API_PREFIX = "https://api." - private val DOMAINS = arrayOf("copymanga.org", "copymanga.info", "copymanga.net") - private val DOMAIN_INDICES = arrayOf("0", "1", "2") - private const val DEFAULT_USER_AGENT = "" - private const val DEFAULT_VERSION = "2022.06.29" - private const val UA_CHECKER = "https://tool.lu/useragent" - - private const val PAGE_SIZE = 20 - private const val CHAPTER_PAGE_SIZE = 500 - } -} diff --git a/src/zh/copymanga/src/eu/kanade/tachiyomi/extension/zh/copymanga/CopyMangaDto.kt b/src/zh/copymanga/src/eu/kanade/tachiyomi/extension/zh/copymanga/CopyMangaDto.kt deleted file mode 100644 index d99bd80c3..000000000 --- a/src/zh/copymanga/src/eu/kanade/tachiyomi/extension/zh/copymanga/CopyMangaDto.kt +++ /dev/null @@ -1,131 +0,0 @@ -package eu.kanade.tachiyomi.extension.zh.copymanga - -import com.luhuiguo.chinese.ChineseUtils -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import kotlinx.serialization.Serializable -import java.text.SimpleDateFormat -import java.util.Locale - -@Serializable -class MangaDto( - val name: String, - val path_word: String, - val author: List, - val cover: String, - val region: ValueDto? = null, - val status: ValueDto? = null, - val theme: List? = null, - val brief: String? = null, -) { - fun toSManga() = SManga.create().apply { - url = URL_PREFIX + path_word - title = if (convertToSc) ChineseUtils.toSimplified(name) else name - author = this@MangaDto.author.joinToString { it.name } - thumbnail_url = cover.removeSuffix(".328x422.jpg") - } - - fun toSMangaDetails(groups: ChapterGroups) = toSManga().apply { - description = brief + groups.toDescription() - genre = buildList(theme!!.size + 1) { - add(region!!.display) - theme.mapTo(this) { it.name } - }.joinToString { ChineseUtils.toSimplified(it) } - status = when (this@MangaDto.status!!.value) { - 0 -> SManga.ONGOING - 1 -> SManga.COMPLETED - else -> SManga.UNKNOWN - } - initialized = true - } - - companion object { - internal var convertToSc = false - - const val URL_PREFIX = "/comic/" - - private const val CHAPTER_GROUP_DELIMITER = "," - private const val CHAPTER_GROUP_PREFIX = "\n\n【其他版本:" - private const val CHAPTER_GROUP_POSTFIX = "】" - private const val NO_CHAPTER_GROUP = "无" - - private fun ChapterGroups.toDescription(): String { - if (size <= 1) return CHAPTER_GROUP_PREFIX + NO_CHAPTER_GROUP + CHAPTER_GROUP_POSTFIX - val groups = ArrayList(size - 1) - for ((key, group) in this) { - if (key != "default") groups.add(group) - } - return groups.joinToString(CHAPTER_GROUP_DELIMITER, CHAPTER_GROUP_PREFIX, CHAPTER_GROUP_POSTFIX) { - it.name + '#' + it.path_word - } - } - - fun String.parseChapterGroups(): List? { - val index = lastIndexOf(CHAPTER_GROUP_PREFIX) - if (index < 0) return null - val groups = substring(index + CHAPTER_GROUP_PREFIX.length, length - CHAPTER_GROUP_POSTFIX.length) - if (groups == NO_CHAPTER_GROUP) return emptyList() - return groups.split(CHAPTER_GROUP_DELIMITER).map { - val delimiterIndex = it.indexOf('#') - KeywordDto(it.substring(0, delimiterIndex), it.substring(delimiterIndex + 1, it.length)) - } - } - } -} - -@Serializable -class ChapterDto( - val uuid: String, - val name: String, - val comic_path_word: String, - val datetime_created: String, -) { - fun toSChapter(group: String) = SChapter.create().apply { - url = "/comic/$comic_path_word/chapter/$uuid" - name = if (group.isEmpty()) this@ChapterDto.name else group + ':' + this@ChapterDto.name - date_upload = dateFormat.parse(datetime_created)?.time ?: 0 - } - - companion object { - val dateFormat by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) } - } -} - -@Serializable -class KeywordDto(val name: String, val path_word: String) { - fun toParam() = Param(ChineseUtils.toSimplified(name), path_word) -} - -@Serializable -class ValueDto(val value: Int, val display: String) - -@Serializable -class MangaWrapperDto(val comic: MangaDto, val groups: ChapterGroups? = null) { - fun toSManga() = comic.toSManga() - fun toSMangaDetails() = comic.toSMangaDetails(groups!!) -} - -typealias ChapterGroups = LinkedHashMap - -@Serializable -class ChapterPageListDto(val contents: List) - -@Serializable -class UrlDto(val url: String) - -@Serializable -class ChapterPageListWrapperDto(val chapter: ChapterPageListDto, val show_app: Boolean) - -@Serializable -class ListDto( - val total: Int, - val limit: Int, - val offset: Int, - val list: List, -) - -@Serializable -class ResultDto(val results: T) - -@Serializable -class ResultMessageDto(val code: Int, val message: String) diff --git a/src/zh/copymanga/src/eu/kanade/tachiyomi/extension/zh/copymanga/CopyMangaFilters.kt b/src/zh/copymanga/src/eu/kanade/tachiyomi/extension/zh/copymanga/CopyMangaFilters.kt deleted file mode 100644 index 74ef8726c..000000000 --- a/src/zh/copymanga/src/eu/kanade/tachiyomi/extension/zh/copymanga/CopyMangaFilters.kt +++ /dev/null @@ -1,53 +0,0 @@ -package eu.kanade.tachiyomi.extension.zh.copymanga - -import eu.kanade.tachiyomi.source.model.Filter -import okhttp3.HttpUrl - -class Param(val name: String, val value: String) - -open class CopyMangaFilter(name: String, private val key: String, private val params: Array) : - Filter.Select(name, params.map { it.name }.toTypedArray()) { - fun addQuery(builder: HttpUrl.Builder) { - val param = params[state].value - if (param.isNotEmpty()) - builder.addQueryParameter(key, param) - } -} - -class SearchFilter : CopyMangaFilter("文本搜索范围", "q_type", SEARCH_FILTER_VALUES) - -private val SEARCH_FILTER_VALUES = arrayOf( - Param("全部", ""), - Param("名称", "name"), - Param("作者", "author"), - Param("汉化组", "local"), -) - -class GenreFilter(genres: Array) : CopyMangaFilter("题材", "theme", genres) - -class RegionFilter : CopyMangaFilter("地区", "region", REGION_VALUES) - -private val REGION_VALUES = arrayOf( - Param("全部", ""), - Param("日本", "0"), - Param("韩国", "1"), - Param("欧美", "2"), -) - -class StatusFilter : CopyMangaFilter("状态", "status", STATUS_VALUES) - -private val STATUS_VALUES = arrayOf( - Param("全部", ""), - Param("连载中", "0"), - Param("已完结", "1"), - Param("短篇", "2"), -) - -class SortFilter : CopyMangaFilter("排序", "ordering", SORT_VALUES) - -private val SORT_VALUES = arrayOf( - Param("热门", "-popular"), - Param("热门(逆序)", "popular"), - Param("更新时间", "-datetime_updated"), - Param("更新时间(逆序)", "datetime_updated"), -) diff --git a/src/zh/copymanga/src/eu/kanade/tachiyomi/extension/zh/copymanga/NonblockingRateLimitInterceptor.kt b/src/zh/copymanga/src/eu/kanade/tachiyomi/extension/zh/copymanga/NonblockingRateLimitInterceptor.kt deleted file mode 100644 index ccdf8dd92..000000000 --- a/src/zh/copymanga/src/eu/kanade/tachiyomi/extension/zh/copymanga/NonblockingRateLimitInterceptor.kt +++ /dev/null @@ -1,58 +0,0 @@ -package eu.kanade.tachiyomi.extension.zh.copymanga - -import android.os.SystemClock -import okhttp3.Interceptor -import okhttp3.Response -import java.io.IOException -import java.util.concurrent.TimeUnit - -// See https://github.com/tachiyomiorg/tachiyomi/pull/7389 -internal class NonblockingRateLimitInterceptor( - private val permits: Int, - period: Long = 1, - unit: TimeUnit = TimeUnit.SECONDS, -) : Interceptor { - - private val requestQueue = ArrayList(permits) - private val rateLimitMillis = unit.toMillis(period) - - override fun intercept(chain: Interceptor.Chain): Response { - // Ignore canceled calls, otherwise they would jam the queue - if (chain.call().isCanceled()) { - throw IOException() - } - - synchronized(requestQueue) { - val now = SystemClock.elapsedRealtime() - val waitTime = if (requestQueue.size < permits) { - 0 - } else { - val oldestReq = requestQueue[0] - val newestReq = requestQueue[permits - 1] - - if (newestReq - oldestReq > rateLimitMillis) { - 0 - } else { - oldestReq + rateLimitMillis - now // Remaining time - } - } - - // Final check - if (chain.call().isCanceled()) { - throw IOException() - } - - if (requestQueue.size == permits) { - requestQueue.removeAt(0) - } - if (waitTime > 0) { - requestQueue.add(now + waitTime) - Thread.sleep(waitTime) // Sleep inside synchronized to pause queued requests - } else { - requestQueue.add(now) - } - } - - return chain.proceed(chain.request()) - } -}