diff --git a/.github/workflows/issue_moderator.yml b/.github/workflows/issue_moderator.yml
index e927ed77d..c8d91fe58 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\\+?).*",
+ "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).*",
"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/en/readcomiconline/AndroidManifest.xml b/src/en/readcomiconline/AndroidManifest.xml
deleted file mode 100644
index 30deb7f79..000000000
--- a/src/en/readcomiconline/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/src/en/readcomiconline/build.gradle b/src/en/readcomiconline/build.gradle
deleted file mode 100644
index b013d9d30..000000000
--- a/src/en/readcomiconline/build.gradle
+++ /dev/null
@@ -1,11 +0,0 @@
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-
-ext {
- extName = 'ReadComicOnline'
- pkgNameSuffix = 'en.readcomiconline'
- extClass = '.Readcomiconline'
- extVersionCode = 12
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/en/readcomiconline/res/mipmap-hdpi/ic_launcher.png b/src/en/readcomiconline/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 8ed86a9a7..000000000
Binary files a/src/en/readcomiconline/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/readcomiconline/res/mipmap-mdpi/ic_launcher.png b/src/en/readcomiconline/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 5729b1fe5..000000000
Binary files a/src/en/readcomiconline/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/readcomiconline/res/mipmap-xhdpi/ic_launcher.png b/src/en/readcomiconline/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index b91895671..000000000
Binary files a/src/en/readcomiconline/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/readcomiconline/res/mipmap-xxhdpi/ic_launcher.png b/src/en/readcomiconline/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 3159e2302..000000000
Binary files a/src/en/readcomiconline/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/readcomiconline/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/readcomiconline/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index eb1335f4c..000000000
Binary files a/src/en/readcomiconline/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/en/readcomiconline/res/web_hi_res_512.png b/src/en/readcomiconline/res/web_hi_res_512.png
deleted file mode 100644
index 9894ccb25..000000000
Binary files a/src/en/readcomiconline/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/en/readcomiconline/src/eu/kanade/tachiyomi/extension/en/readcomiconline/Readcomiconline.kt b/src/en/readcomiconline/src/eu/kanade/tachiyomi/extension/en/readcomiconline/Readcomiconline.kt
deleted file mode 100644
index d802d1b41..000000000
--- a/src/en/readcomiconline/src/eu/kanade/tachiyomi/extension/en/readcomiconline/Readcomiconline.kt
+++ /dev/null
@@ -1,254 +0,0 @@
-package eu.kanade.tachiyomi.extension.en.readcomiconline
-
-import android.app.Application
-import android.content.SharedPreferences
-import android.util.Base64
-import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.network.POST
-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.Page
-import eu.kanade.tachiyomi.source.model.SChapter
-import eu.kanade.tachiyomi.source.model.SManga
-import eu.kanade.tachiyomi.source.online.ParsedHttpSource
-import okhttp3.FormBody
-import okhttp3.Headers
-import okhttp3.OkHttpClient
-import okhttp3.Request
-import okhttp3.Response
-import org.jsoup.nodes.Document
-import org.jsoup.nodes.Element
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-import java.text.SimpleDateFormat
-import java.util.Locale
-
-class Readcomiconline : ConfigurableSource, ParsedHttpSource() {
-
- override val name = "ReadComicOnline"
-
- override val baseUrl = "https://readcomiconline.li"
-
- override val lang = "en"
-
- override val supportsLatest = true
-
- override val client: OkHttpClient = network.cloudflareClient
-
- override fun headersBuilder() = Headers.Builder().apply {
- add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
- }
-
- private val preferences: SharedPreferences by lazy {
- Injekt.get().getSharedPreferences("source_$id", 0x0000)
- }
-
- override fun popularMangaSelector() = ".list-comic > .item > a:first-child"
-
- override fun latestUpdatesSelector() = popularMangaSelector()
-
- override fun popularMangaRequest(page: Int): Request {
- return GET("$baseUrl/ComicList/MostPopular?page=$page", headers)
- }
-
- override fun latestUpdatesRequest(page: Int): Request {
- return GET("$baseUrl/ComicList/LatestUpdate?page=$page", headers)
- }
-
- override fun popularMangaFromElement(element: Element): SManga {
- return SManga.create().apply {
- setUrlWithoutDomain(element.attr("abs:href"))
- title = element.text()
- thumbnail_url = element.selectFirst("img")!!.attr("abs:src")
- }
- }
-
- override fun latestUpdatesFromElement(element: Element): SManga {
- return popularMangaFromElement(element)
- }
-
- override fun popularMangaNextPageSelector() = "li > a:contains(Next)"
-
- override fun latestUpdatesNextPageSelector(): String = "ul.pager > li > a:contains(Next)"
-
- override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
- val form = FormBody.Builder().apply {
- add("comicName", query)
-
- for (filter in if (filters.isEmpty()) getFilterList() else filters) {
- when (filter) {
- is Status -> add("status", arrayOf("", "Completed", "Ongoing")[filter.state])
- is GenreList -> filter.state.forEach { genre -> add("genres", genre.state.toString()) }
- }
- }
- }
- return POST("$baseUrl/AdvanceSearch", headers, form.build())
- }
-
- override fun searchMangaSelector() = popularMangaSelector()
-
- override fun searchMangaFromElement(element: Element): SManga {
- return popularMangaFromElement(element)
- }
-
- override fun searchMangaNextPageSelector(): String? = null
-
- override fun mangaDetailsParse(document: Document): SManga {
- val infoElement = document.select("div.barContent").first()
-
- val manga = SManga.create()
- manga.artist = infoElement.select("p:has(span:contains(Artist:)) > a").first()?.text()
- manga.author = infoElement.select("p:has(span:contains(Writer:)) > a").first()?.text()
- manga.genre = infoElement.select("p:has(span:contains(Genres:)) > *:gt(0)").text()
- manga.description = infoElement.select("p:has(span:contains(Summary:)) ~ p").text()
- manga.status = infoElement.select("p:has(span:contains(Status:))").first()?.text().orEmpty().let { parseStatus(it) }
- manga.thumbnail_url = document.select(".rightBox:eq(0) img").first()?.absUrl("src")
- return manga
- }
-
- private fun parseStatus(status: String) = when {
- status.contains("Ongoing") -> SManga.ONGOING
- status.contains("Completed") -> SManga.COMPLETED
- else -> SManga.UNKNOWN
- }
-
- override fun chapterListSelector() = "table.listing tr:gt(1)"
-
- override fun chapterFromElement(element: Element): SChapter {
- val urlElement = element.select("a").first()
-
- val chapter = SChapter.create()
- chapter.setUrlWithoutDomain(urlElement.attr("href"))
- chapter.name = urlElement.text()
- chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let {
- SimpleDateFormat("MM/dd/yyyy", Locale.getDefault()).parse(it)?.time ?: 0L
- } ?: 0
- return chapter
- }
-
- override fun pageListRequest(chapter: SChapter) = GET(baseUrl + chapter.url + "&quality=${qualitypref()}", headers)
-
- override fun pageListParse(document: Document): List {
- val script = document.selectFirst("script:containsData(lstImages.push)")?.data()
- ?: return emptyList()
-
- return CHAPTER_IMAGES_REGEX.findAll(script).toList()
- .mapIndexed { i, match -> Page(i, "", match.groupValues[1]) }
- }
-
- override fun imageUrlParse(document: Document) = ""
-
- override fun imageRequest(page: Page): Request {
- if (page.imageUrl!!.startsWith("https")) {
- return super.imageRequest(page)
- }
-
- val scrambledUrl = page.imageUrl!!
- val containsS0 = scrambledUrl.contains("=s0")
- val imagePathResult = runCatching {
- scrambledUrl
- .substring(0, scrambledUrl.length - (if (containsS0) 3 else 6))
- .let { it.substring(4, 21) + it.substring(24) }
- .let { it.substring(0, it.length - 6) + it[it.length - 2] + it[it.length - 1] }
- .let { Base64.decode(it, Base64.DEFAULT).toString(Charsets.UTF_8) }
- .let { it.substring(0, 11) + it.substring(14) }
- .let { it.substring(0, it.length - 2) + if (containsS0) "=s0" else "=s1600" }
- }
-
- val imagePath = imagePathResult.getOrNull()
- ?: throw Exception("Failed to decrypt the image URL.")
-
- return GET("https://2.bp.blogspot.com/$imagePath")
- }
-
- private class Status : Filter.TriState("Completed")
- private class Genre(name: String) : Filter.TriState(name)
- private class GenreList(genres: List) : Filter.Group("Genres", genres)
-
- override fun getFilterList() = FilterList(
- Status(),
- GenreList(getGenreList())
- )
-
- // $("select[name=\"genres\"]").map((i,el) => `Genre("${$(el).next().text().trim()}", ${i})`).get().join(',\n')
- // on https://readcomiconline.li/AdvanceSearch
- private fun getGenreList() = listOf(
- Genre("Action"),
- Genre("Adventure"),
- Genre("Anthology"),
- Genre("Anthropomorphic"),
- Genre("Biography"),
- Genre("Children"),
- Genre("Comedy"),
- Genre("Crime"),
- Genre("Drama"),
- Genre("Family"),
- Genre("Fantasy"),
- Genre("Fighting"),
- Genre("Graphic Novels"),
- Genre("Historical"),
- Genre("Horror"),
- Genre("Leading Ladies"),
- Genre("LGBTQ"),
- Genre("Literature"),
- Genre("Manga"),
- Genre("Martial Arts"),
- Genre("Mature"),
- Genre("Military"),
- Genre("Movies & TV"),
- Genre("Music"),
- Genre("Mystery"),
- Genre("Mythology"),
- Genre("Personal"),
- Genre("Political"),
- Genre("Post-Apocalyptic"),
- Genre("Psychological"),
- Genre("Pulp"),
- Genre("Religious"),
- Genre("Robots"),
- Genre("Romance"),
- Genre("School Life"),
- Genre("Sci-Fi"),
- Genre("Slice of Life"),
- Genre("Sport"),
- Genre("Spy"),
- Genre("Superhero"),
- Genre("Supernatural"),
- Genre("Suspense"),
- Genre("Thriller"),
- Genre("Vampires"),
- Genre("Video Games"),
- Genre("War"),
- Genre("Western"),
- Genre("Zombies")
- )
- // Preferences Code
-
- override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) {
- val qualitypref = androidx.preference.ListPreference(screen.context).apply {
- key = QUALITY_PREF_Title
- title = QUALITY_PREF_Title
- entries = arrayOf("High Quality", "Low Quality")
- entryValues = arrayOf("hq", "lq")
- summary = "%s"
-
- setOnPreferenceChangeListener { _, newValue ->
- val selected = newValue as String
- val index = this.findIndexOfValue(selected)
- val entry = entryValues[index] as String
- preferences.edit().putString(QUALITY_PREF, entry).commit()
- }
- }
- screen.addPreference(qualitypref)
- }
-
- private fun qualitypref() = preferences.getString(QUALITY_PREF, "hq")
-
- companion object {
- private const val QUALITY_PREF_Title = "Image Quality Selector"
- private const val QUALITY_PREF = "qualitypref"
-
- private val CHAPTER_IMAGES_REGEX = "lstImages\\.push\\(\"(.*)\"\\)".toRegex()
- }
-}