Remove 78 broken extensions (#7658)

* Remove Franxx Mangás - 403 Forbidden

* Remove MIC MIC IDOL - coming soon

* Remove Ler Yaoi - Connection timed out

* Remove Yaoi TR - Connection timed out

* Remove Hunlight Scans - Connection timed out

* Remove Manga Time - Connection timed out

* Remove Manga-Scan - Connection timed out

* Remove Momo no Hana Scan - Connection timed out

* Remove BlogTruyen.vn (unoriginal) - Connection timed out

* Remove MangaDoom - Connection timed out

* Remove MangaStorm - Connection timed out

* Remove Etheral Radiance - default cpanel

* Remove Moon Witch Scan - Domínio não encontrado

* Remove Hensekai - empty

* Remove Lunar Scans - empty

* Remove Tecno Scans - empty

* Remove Manga-Titan - empty

* Remove Lich Subs - empty

* Remove Lector Online - for sale

* Remove I Love Manhwa - Invalid SSL certificate

* Remove ManhwaNew - Invalid SSL certificate

* Remove MangaRolls - Invalid SSL certificate

* Remove Ladron Corps - Looks like this domain isn't connected to a website yet

* Remove Kishisan - no A record

* Remove MELOKOMIK - no A record

* Remove Comic Fans - no A record

* Remove MMFenix - no A record

* Remove Manga Bari - Not Found

* Remove GMANGA (unoriginal) - Origin is unreachable

* Remove ComicExtra - Origin is unreachable

* Remove Nabi Scans - Origin is unreachable

Closes #5649

* Remove MangaCV - Origin is unreachable

* Remove Fay Scans - parked

* Remove Manga-fast.com - parked

* Remove Cookie Kiara - parked

* Remove Pink Tea Comic - parked

* Remove Riot Hentai - parked

* Remove Read Goblin Slayer Manga Online - parked

* Remove Mystical Merries - parked

* Remove CopyPasteScan - parked

* Remove ManhuaChill - parked

* Remove Lolicon - parked

* Remove Mystic Moon - parked

* Remove Manga68 - parked

* Remove MyRockManga - parked

* Remove Manhua Kiss - parked

* Remove AscalonScans - parked

* Remove Doujins.lat - parked

* Remove Komik Pix - parked

* Remove MirrorDesu - parked

* Remove SISI GELAP - parked

* Remove Komiksan - parked

* Remove Pian Manga - parked

* Remove Sekaikomik - parked

* Remove My Manhwa - parked

* Remove 1st Kiss-Manga (unoriginal) - parked

* Remove Lady Manga - parked

* Remove Mangá Kun - SSL handshake failed

* Remove Oh No Manga - The connection has timed out

* Remove Dark-Scan.com - The connection has timed out

* Remove Babel Wuxia - The connection has timed out

* Remove Mangazavr - The connection has timed out

* Remove Read Noblesse Manhwa Online - Unable to connect

* Remove Nirvana Scan - Unable to connect

* Remove Mantraz Scan - Unable to connect

* Remove SCARManga - Unable to connect

* Remove Gatemanga - unrelated

* Remove ManhwaBookShelf - unrelated

* Remove Read Tower of God Manhwa Manga Online - unrelated

* Remove Kofi Scans - Web server is down

* Remove Nyrax Manga - Web server is down

* Remove YD-Comics - Web server is down

* Remove AnonimusTLS - Web server is down

* Remove Mangaland - Website not found...

* Remove MangaNoon - We're having trouble finding that site

Closes #7042

* Remove NoonScan - We're having trouble finding that site

Closes #7042

* Remove SobatManKu - Your domain is expired

* Remove Comic 21 - Your domain is expired
This commit is contained in:
Vetle Ledaal 2025-02-19 08:20:02 +01:00 committed by Draff
parent 94603b3843
commit 10aa286c04
No known key found for this signature in database
GPG Key ID: E8A89F3211677653
526 changed files with 0 additions and 3835 deletions

View File

@ -1,10 +0,0 @@
ext {
extName = 'MyRockManga'
extClass = '.MyRockMangaFactory'
themePkg = 'otakusanctuary'
baseUrl = 'https://myrockmanga.com'
overrideVersionCode = 0
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,14 +0,0 @@
package eu.kanade.tachiyomi.extension.all.myrockmanga
import eu.kanade.tachiyomi.multisrc.otakusanctuary.OtakuSanctuary
import eu.kanade.tachiyomi.source.SourceFactory
class MyRockMangaFactory : SourceFactory {
override fun createSources() = listOf(
OtakuSanctuary("MyRockManga", "https://myrockmanga.com", "all"),
OtakuSanctuary("MyRockManga", "https://myrockmanga.com", "vi"),
OtakuSanctuary("MyRockManga", "https://myrockmanga.com", "en"),
OtakuSanctuary("MyRockManga", "https://myrockmanga.com", "it"),
OtakuSanctuary("MyRockManga", "https://myrockmanga.com", "es"),
)
}

View File

@ -1,9 +0,0 @@
ext {
extName = 'SCARManga'
extClass = '.ScarManga'
themePkg = 'mangathemesia'
baseUrl = 'https://scarmanga.com'
overrideVersionCode = 2
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

View File

@ -1,15 +0,0 @@
package eu.kanade.tachiyomi.extension.ar.aresnov
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import java.text.SimpleDateFormat
import java.util.Locale
class ScarManga : MangaThemesia(
"SCARManga",
"https://scarmanga.com",
"ar",
mangaUrlDirectory = "/series",
dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("ar")),
) {
override val id = 1046935749022479891
}

View File

@ -1,9 +0,0 @@
ext {
extName = 'Gatemanga'
extClass = '.Gatemanga'
themePkg = 'madara'
baseUrl = 'https://gatemanga.com'
overrideVersionCode = 2
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -1,14 +0,0 @@
package eu.kanade.tachiyomi.extension.ar.gatemanga
import eu.kanade.tachiyomi.multisrc.madara.Madara
import java.text.SimpleDateFormat
import java.util.Locale
class Gatemanga : Madara(
"Gatemanga",
"https://gatemanga.com",
"ar",
dateFormat = SimpleDateFormat("d MMMM، yyyy", Locale("ar")),
) {
override val mangaSubString = "ar"
}

View File

@ -1,9 +0,0 @@
ext {
extName = 'GMANGA (unoriginal)'
extClass = '.GmangaSite'
themePkg = 'madara'
baseUrl = 'https://gmanga.site'
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 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: 18 KiB

View File

@ -1,16 +0,0 @@
package eu.kanade.tachiyomi.extension.ar.gmangasite
import eu.kanade.tachiyomi.multisrc.madara.Madara
import java.text.SimpleDateFormat
import java.util.Locale
class GmangaSite : Madara(
"GMANGA (unoriginal)",
"https://gmanga.site",
"ar",
dateFormat = SimpleDateFormat("MMMM dd، yyyy", Locale("ar")),
) {
override val chapterUrlSuffix = ""
override val useLoadMoreRequest = LoadMoreStrategy.Always
override val useNewChapterEndpoint = true
}

View File

@ -1,10 +0,0 @@
ext {
extName = 'MangaNoon'
extClass = '.MangaNoon'
themePkg = 'mangathemesia'
baseUrl = 'https://vrnoin.site'
overrideVersionCode = 8
isNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@ -1,89 +0,0 @@
package eu.kanade.tachiyomi.extension.ar.manganoon
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.source.model.SChapter
import org.jsoup.nodes.Element
import java.util.Calendar
class MangaNoon : MangaThemesia(
"مانجا نون",
"https://vrnoin.site",
"ar",
) {
override fun chapterFromElement(element: Element): SChapter {
return super.chapterFromElement(element).apply {
date_upload = element.selectFirst(".chapterdate")?.text().parseChapterDate()
}
}
// From Galaxy
override fun String?.parseChapterDate(): Long {
this ?: return 0L
val number = Regex("""(\d+)""").find(this)?.value?.toIntOrNull() ?: 0
val cal = Calendar.getInstance()
return when {
listOf("second", "ثانية").any { contains(it, true) } -> {
cal.apply { add(Calendar.SECOND, -number) }.timeInMillis
}
contains("دقيقتين", true) -> {
cal.apply { add(Calendar.MINUTE, -2) }.timeInMillis
}
listOf("minute", "دقائق").any { contains(it, true) } -> {
cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis
}
contains("ساعتان", true) -> {
cal.apply { add(Calendar.HOUR, -2) }.timeInMillis
}
listOf("hour", "ساعات").any { contains(it, true) } -> {
cal.apply { add(Calendar.HOUR, -number) }.timeInMillis
}
contains("يوم", true) -> {
cal.apply { add(Calendar.DAY_OF_YEAR, -1) }.timeInMillis
}
contains("يومين", true) -> {
cal.apply { add(Calendar.DAY_OF_YEAR, -2) }.timeInMillis
}
listOf("day", "أيام").any { contains(it, true) } -> {
cal.apply { add(Calendar.DAY_OF_YEAR, -number) }.timeInMillis
}
contains("أسبوع", true) -> {
cal.apply { add(Calendar.WEEK_OF_YEAR, -1) }.timeInMillis
}
contains("أسبوعين", true) -> {
cal.apply { add(Calendar.WEEK_OF_YEAR, -2) }.timeInMillis
}
listOf("week", "أسابيع").any { contains(it, true) } -> {
cal.apply { add(Calendar.WEEK_OF_YEAR, -number) }.timeInMillis
}
contains("شهر", true) -> {
cal.apply { add(Calendar.MONTH, -1) }.timeInMillis
}
contains("شهرين", true) -> {
cal.apply { add(Calendar.MONTH, -2) }.timeInMillis
}
listOf("month", "أشهر").any { contains(it, true) } -> {
cal.apply { add(Calendar.MONTH, -number) }.timeInMillis
}
contains("سنة", true) -> {
cal.apply { add(Calendar.YEAR, -1) }.timeInMillis
}
contains("سنتان", true) -> {
cal.apply { add(Calendar.YEAR, -2) }.timeInMillis
}
listOf("year", "سنوات").any { contains(it, true) } -> {
cal.apply { add(Calendar.YEAR, -number) }.timeInMillis
}
else -> 0L
}
}
}

View File

@ -1,11 +0,0 @@
ext {
extName = 'MangaStorm'
extClass = '.MangaStorm'
extVersionCode = 1
}
apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(":lib:randomua"))
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,105 +0,0 @@
package eu.kanade.tachiyomi.extension.ar.mangastorm
import eu.kanade.tachiyomi.lib.randomua.UserAgentType
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
import eu.kanade.tachiyomi.network.GET
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.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.lang.UnsupportedOperationException
class MangaStorm : ParsedHttpSource() {
override val name = "MangaStorm"
override val lang = "ar"
override val baseUrl = "https://mangastorm.org"
override val supportsLatest = true
override val client = network.cloudflareClient.newBuilder()
.setRandomUserAgent(
UserAgentType.DESKTOP,
filterInclude = listOf("chrome"),
)
.build()
override fun headersBuilder() = super.headersBuilder()
.set("Referer", "$baseUrl/")
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/mangas?page=$page", headers)
}
override fun popularMangaSelector() = "div.row div.col"
override fun popularMangaNextPageSelector() = ".page-link[rel=next]"
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href"))
title = element.select(".manga-ct-title").text()
thumbnail_url = element.selectFirst("img")?.imgAttr()
}
override fun latestUpdatesRequest(page: Int): Request {
return GET(baseUrl, headers)
}
override fun latestUpdatesSelector() = popularMangaSelector()
override fun latestUpdatesNextPageSelector() = null
override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/mangas".toHttpUrl().newBuilder()
.addQueryParameter("page", page.toString())
.addQueryParameter("query", query.trim())
.build()
return GET(url, headers)
}
override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element)
override fun mangaDetailsParse(document: Document): SManga {
val root = document.selectFirst(".card-body .col-lg-9")!!
return SManga.create().apply {
title = document.select(".card-header").text()
thumbnail_url = document.selectFirst("img.card-img-right")?.imgAttr()
genre = root.select(".flex-wrap a").eachText().joinToString()
description = root.selectFirst(".card-text")?.text()
}
}
override fun chapterListSelector() = ".card-body a.btn-fixed-width"
override fun chapterFromElement(element: Element) = SChapter.create().apply {
setUrlWithoutDomain(element.absUrl("href"))
name = element.text()
}
override fun pageListParse(document: Document): List<Page> {
return document.select("div.text-center .img-fluid")
.mapIndexed { idx, img ->
Page(idx, "", img.imgAttr())
}
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
private fun Element.imgAttr() = when {
hasAttr("data-cfsrc") -> attr("abs:data-cfsrc")
hasAttr("data-src") -> attr("abs:data-src")
hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
hasAttr("srcset") -> attr("abs:srcset").substringBefore(" ")
else -> attr("abs:src")
}
}

View File

@ -1,10 +0,0 @@
ext {
extName = 'Manga Time'
extClass = '.MangaTime'
themePkg = 'madara'
baseUrl = 'https://mangatime.us'
overrideVersionCode = 1
isNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,15 +0,0 @@
package eu.kanade.tachiyomi.extension.ar.mangatime
import eu.kanade.tachiyomi.multisrc.madara.Madara
import java.text.SimpleDateFormat
import java.util.Locale
class MangaTime : Madara(
"Manga Time",
"https://mangatime.us",
"ar",
dateFormat = SimpleDateFormat("dd MMMM، yyyy", Locale("ar")),
) {
override val useLoadMoreRequest = LoadMoreStrategy.Always
override val useNewChapterEndpoint = true
}

View File

@ -1,10 +0,0 @@
ext {
extName = 'NoonScan'
extClass = '.NoonScan'
themePkg = 'mangathemesia'
baseUrl = 'https://noonscan.com'
overrideVersionCode = 0
isNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

View File

@ -1,12 +0,0 @@
package eu.kanade.tachiyomi.extension.ar.noonscan
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import java.text.SimpleDateFormat
import java.util.Locale
class NoonScan : MangaThemesia(
"نون سكان",
"https://noonscan.com",
"ar",
dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale("ar")),
)

View File

@ -1,9 +0,0 @@
ext {
extName = 'AscalonScans'
extClass = '.AscalonScans'
themePkg = 'mangathemesia'
baseUrl = 'https://ascalonscans.com'
overrideVersionCode = 1
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1,55 +0,0 @@
package eu.kanade.tachiyomi.extension.en.ascalonscans
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
import okhttp3.FormBody
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
import java.security.MessageDigest
class AscalonScans : MangaThemesia("AscalonScans", "https://ascalonscans.com", "en") {
override val client = super.client.newBuilder()
.rateLimitHost(baseUrl.toHttpUrl(), 2)
.addInterceptor(::jsChallengeInterceptor)
.build()
private fun jsChallengeInterceptor(chain: Interceptor.Chain): Response {
val request = chain.request()
val origRes = chain.proceed(request)
if (origRes.code != 403) return origRes
origRes.close()
// Same delay as the source
Thread.sleep(3000L)
val token = fetchToken(chain).sha256()
val body = FormBody.Builder().add("challenge", token).build()
val challengeReq = POST("$baseUrl/hcdn-cgi/jschallenge-validate", headers, body = body)
val challengeResponse = chain.proceed(challengeReq)
challengeResponse.close()
if (challengeResponse.code != 200) throw IOException("Failed to bypass js challenge!")
return chain.proceed(request)
}
private tailrec fun fetchToken(chain: Interceptor.Chain, attempt: Int = 0): String {
if (attempt > 5) throw IOException("Failed to fetch challenge token!")
val request = GET("$baseUrl/hcdn-cgi/jschallenge", headers)
val res = chain.proceed(request).body.string()
return res.substringAfter("cjs = '").substringBefore("'")
.takeUnless { it == "nil" } ?: fetchToken(chain, attempt + 1)
}
private fun String.sha256(): String {
return MessageDigest
.getInstance("SHA-256")
.digest(toByteArray())
.fold("", { str, it -> str + "%02x".format(it) })
}
}

View File

@ -1,9 +0,0 @@
ext {
extName = 'Babel Wuxia'
extClass = '.BabelWuxia'
themePkg = 'madara'
baseUrl = 'https://babelwuxia.com'
overrideVersionCode = 1
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.babelwuxia
import eu.kanade.tachiyomi.multisrc.madara.Madara
class BabelWuxia : Madara("Babel Wuxia", "https://babelwuxia.com", "en") {
// moved from MangaThemesia
override val versionId = 2
override val useNewChapterEndpoint = true
override val useLoadMoreRequest = LoadMoreStrategy.Always
}

View File

@ -1,7 +0,0 @@
ext {
extName = 'ComicExtra'
extClass = '.ComicExtra'
extVersionCode = 17
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,243 +0,0 @@
package eu.kanade.tachiyomi.extension.en.comicextra
import eu.kanade.tachiyomi.network.GET
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 eu.kanade.tachiyomi.util.asJsoup
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import java.text.SimpleDateFormat
import java.util.ArrayList
import java.util.Date
import java.util.Locale
class ComicExtra : ParsedHttpSource() {
override val name = "ComicExtra"
override val baseUrl = "https://azcomix.me"
override val lang = "en"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient
override fun popularMangaSelector() = "div.eg-box"
override fun latestUpdatesSelector() = "ul.line-list"
override fun popularMangaRequest(page: Int) = GET("$baseUrl/popular-comics?page=$page", headers)
override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/comic-updates?page=$page", headers)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$baseUrl/advanced-search".toHttpUrl().newBuilder().apply {
if (query.isNotBlank()) addQueryParameter("key", query)
if (page > 1) addQueryParameter("page", page.toString())
filters.forEach { filter ->
when (filter) {
is GenreGroupFilter -> {
with(filter) {
addQueryParameter("wg", included.joinToString("%20"))
addQueryParameter("wog", excluded.joinToString("%20"))
}
}
is StatusFilter -> addQueryParameter("status", filter.selected)
else -> {}
}
}
}.build()
return GET(url, headers)
}
override fun popularMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("a.eg-image")!!.absUrl("href"))
title = element.selectFirst("div.egb-right a")!!.text()
element.selectFirst("img")?.also { thumbnail_url = it.absUrl("src") }
}
override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("a.big-link")!!.absUrl("href"))
title = element.selectFirst(".big-link")!!.text()
thumbnail_url = "https://azcomix.me/images/sites/default.jpg"
}
override fun popularMangaNextPageSelector() = "div.general-nav > a:contains(Next)"
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
override fun searchMangaSelector() = "div.dl-box"
override fun searchMangaFromElement(element: Element) = SManga.create().apply {
setUrlWithoutDomain(element.selectFirst("a.dlb-image")!!.absUrl("href"))
title = element.selectFirst("div.dlb-right a.dlb-title")!!.text()
element.selectFirst("a.dlb-image img")?.also { thumbnail_url = it.absUrl("src") }
}
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
override fun mangaDetailsParse(document: Document): SManga {
return SManga.create().apply {
title = document.selectFirst("h1")!!.text()
thumbnail_url = document.selectFirst("div.anime-image > img")?.absUrl("src")
document.selectFirst(".status a")?.also { status = parseStatus(it.text()) }
document.selectFirst("td:contains(Author:) + td")?.also { author = it.text() }
document.selectFirst("div.detail-desc-content p")?.also { description = it.text() }
genre = document.select("ul.anime-genres > li + li").joinToString { it.text() }
}
}
private fun parseStatus(element: String): Int = when {
element.contains("Completed") -> SManga.COMPLETED
element.contains("Ongoing") -> SManga.ONGOING
else -> SManga.UNKNOWN
}
override fun chapterListParse(response: Response): List<SChapter> {
val document = response.asJsoup()
val chapters = ArrayList<SChapter>()
document.select(chapterListSelector()).forEach {
chapters.add(chapterFromElement(it))
}
return chapters
}
override fun chapterListSelector() = "ul.basic-list li"
override fun chapterFromElement(element: Element): SChapter {
val urlEl = element.selectFirst("a")
val dateEl = element.selectFirst("span")
return SChapter.create().apply {
urlEl!!.also {
setUrlWithoutDomain(it.absUrl("href").replace(" ", "%20"))
name = it.text()
}
dateEl?.also { date_upload = dateParse(it.text()) }
}
}
private fun dateParse(dateAsString: String): Long {
val date: Date? = SimpleDateFormat("MM/dd/yy", Locale.ENGLISH).parse(dateAsString)
return date?.time ?: 0L
}
override fun pageListRequest(chapter: SChapter): Request {
return GET(baseUrl + chapter.url + "/full", headers)
}
override fun pageListParse(document: Document): List<Page> {
val pages = mutableListOf<Page>()
document.select("div.chapter-container img").forEachIndexed { i, img ->
pages.add(Page(i, "", img.absUrl("src")))
}
return pages
}
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
// Filters
override fun getFilterList() = FilterList(
Filter.Header("Note: can't leave both filters as default with a blank search string"),
Filter.Separator(),
StatusFilter(getStatusList, 0),
GenreGroupFilter(getGenreList()),
)
class SelectFilterOption(val name: String, val value: String)
class TriStateFilterOption(val value: String, name: String, default: Int = 0) : Filter.TriState(name, default)
abstract class SelectFilter(name: String, private val options: List<SelectFilterOption>, default: Int = 0) : Filter.Select<String>(name, options.map { it.name }.toTypedArray(), default) {
val selected: String
get() = options[state].value
}
abstract class TriStateGroupFilter(name: String, options: List<TriStateFilterOption>) : Filter.Group<TriStateFilterOption>(name, options) {
val included: List<String>
get() = state.filter { it.isIncluded() }.map { it.value }
val excluded: List<String>
get() = state.filter { it.isExcluded() }.map { it.value }
}
class StatusFilter(options: List<SelectFilterOption>, default: Int) : SelectFilter("Status", options, default)
class GenreGroupFilter(options: List<TriStateFilterOption>) : TriStateGroupFilter("Genre", options)
private val getStatusList = listOf(
SelectFilterOption("All", ""),
SelectFilterOption("Ongoing", "ONG"),
SelectFilterOption("Completed", "CMP"),
)
private fun getGenreList() = listOf(
TriStateFilterOption("Action", "Action"),
TriStateFilterOption("Adventure", "Adventure"),
TriStateFilterOption("Anthology", "Anthology"),
TriStateFilterOption("Anthropomorphic", "Anthropomorphic"),
TriStateFilterOption("Biography", "Biography"),
TriStateFilterOption("Children", "Children"),
TriStateFilterOption("Comedy", "Comedy"),
TriStateFilterOption("Crime", "Crime"),
TriStateFilterOption("Cyborgs", "Cyborgs"),
TriStateFilterOption("DC Comics", "DC Comics"),
TriStateFilterOption("Dark Horse", "Dark Horse"),
TriStateFilterOption("Demons", "Demons"),
TriStateFilterOption("Drama", "Drama"),
TriStateFilterOption("Family", "Family"),
TriStateFilterOption("Fantasy", "Fantasy"),
TriStateFilterOption("Fighting", "Fighting"),
TriStateFilterOption("Gore", "Gore"),
TriStateFilterOption("Graphic Novels", "Graphic Novels"),
TriStateFilterOption("Historical", "Historical"),
TriStateFilterOption("Horror", "Horror"),
TriStateFilterOption("Leading Ladies", "Leading Ladies"),
TriStateFilterOption("Literature", "Literature"),
TriStateFilterOption("Magic", "Magic"),
TriStateFilterOption("Manga", "Manga"),
TriStateFilterOption("Martial Arts", "Martial Arts"),
TriStateFilterOption("Marvel", "Marvel"),
TriStateFilterOption("Mature", "Mature"),
TriStateFilterOption("Mecha", "Mecha"),
TriStateFilterOption("Military", "Military"),
TriStateFilterOption("Movie Cinematic Link", "Movie Cinematic Link"),
TriStateFilterOption("Mystery", "Mystery"),
TriStateFilterOption("Mythology", "Mythology"),
TriStateFilterOption("Personal", "Personal"),
TriStateFilterOption("Political", "Political"),
TriStateFilterOption("Post-Apocalyptic", "Post-Apocalyptic"),
TriStateFilterOption("Psychological", "Psychological"),
TriStateFilterOption("Pulp", "Pulp"),
TriStateFilterOption("Robots", "Robots"),
TriStateFilterOption("Romance", "Romance"),
TriStateFilterOption("Sci-Fi", "Sci-Fi"),
TriStateFilterOption("Science Fiction", "Science Fiction"),
TriStateFilterOption("Slice of Life", "Slice of Life"),
TriStateFilterOption("Sports", "Sports"),
TriStateFilterOption("Spy", "Spy"),
TriStateFilterOption("Superhero", "Superhero"),
TriStateFilterOption("Supernatural", "Supernatural"),
TriStateFilterOption("Suspense", "Suspense"),
TriStateFilterOption("Thriller", "Thriller"),
TriStateFilterOption("Tragedy", "Tragedy"),
TriStateFilterOption("Vampires", "Vampires"),
TriStateFilterOption("Vertigo", "Vertigo"),
TriStateFilterOption("Video Games", "Video Games"),
TriStateFilterOption("War", "War"),
TriStateFilterOption("Western", "Western"),
TriStateFilterOption("Zombies", "Zombies"),
)
}

View File

@ -1,7 +0,0 @@
ext {
extName = 'Comic Fans'
extClass = '.ComicFans'
extVersionCode = 1
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

View File

@ -1,205 +0,0 @@
package eu.kanade.tachiyomi.extension.en.comicfans
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.interceptor.rateLimit
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 eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import uy.kohesive.injekt.injectLazy
class ComicFans : HttpSource() {
override val name = "Comic Fans"
override val baseUrl = "https://comicfans.io"
private val apiUrl = "https://api.comicfans.io/comic-backend/api/v1/content"
private val cdnUrl = "https://static.comicfans.io"
override val lang = "en"
override val supportsLatest = true
override val client = network.cloudflareClient.newBuilder()
.rateLimit(2)
.build()
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
private fun apiHeadersBuilder() = headersBuilder().apply {
add("Accept", "*/*")
add("Host", apiUrl.toHttpUrl().host)
add("Origin", baseUrl)
add("site-domain", "www.${baseUrl.toHttpUrl().host}")
}
private val apiHeaders by lazy { apiHeadersBuilder().build() }
private val json: Json by injectLazy()
// ============================== Popular ===============================
override fun popularMangaRequest(page: Int): Request {
val body = buildJsonObject {
put("conditionJson", "{\"title\":\"You may also like\",\"maxSize\":15}")
put("pageNumber", page)
put("pageSize", 30)
}.let(json::encodeToString).toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
val popularHeaders = apiHeadersBuilder().apply {
set("Accept", "application/json")
}.build()
return POST("$apiUrl/books/custom/MostPopularLocal#$page", popularHeaders, body)
}
override fun popularMangaParse(response: Response): MangasPage {
val data = response.parseAs<ListDataDto<MangaDto>>().data
val hasNextPage = response.request.url.fragment!!.toInt() < data.totalPages
return MangasPage(data.list.map { it.toSManga(cdnUrl) }, hasNextPage)
}
// =============================== Latest ===============================
override fun latestUpdatesRequest(page: Int): Request = GET(baseUrl, headers)
override fun latestUpdatesParse(response: Response): MangasPage {
val document = response.asJsoup()
val mangaList = document.select(
"div:has(>.block-title-bar > .title:contains(New Updates))" +
"> .book-container > .book",
).map { element ->
SManga.create().apply {
thumbnail_url = element.selectFirst("img")!!.attr("abs:src")
with(element.selectFirst(".book-name > a")!!) {
title = text()
setUrlWithoutDomain(attr("abs:href"))
}
}
}
return MangasPage(mangaList, false)
}
// =============================== Search ===============================
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = "$apiUrl/books".toHttpUrl().newBuilder().apply {
addQueryParameter("pageNumber", page.toString())
addQueryParameter("pageSize", "20")
fragment(page.toString())
if (query.isNotBlank()) {
addPathSegment("search")
addQueryParameter("keyWord", query)
} else {
filters.getUriPart<GenreFilter>()?.let {
addQueryParameter("genre", it)
}
filters.getUriPart<LastUpdateFilter>()?.let {
addQueryParameter("withinDay", it)
}
filters.getUriPart<StatusFilter>()?.let {
addQueryParameter("status", it)
}
}
}.build()
return GET(url, apiHeaders)
}
override fun searchMangaParse(response: Response): MangasPage =
popularMangaParse(response)
// =============================== Filters ==============================
override fun getFilterList(): FilterList = FilterList(
Filter.Header("Text search ignores filters"),
Filter.Separator(),
GenreFilter(),
LastUpdateFilter(),
StatusFilter(),
)
// =========================== Manga Details ============================
override fun getMangaUrl(manga: SManga): String = baseUrl + manga.url
override fun mangaDetailsRequest(manga: SManga): Request {
val bookId = manga.url.substringAfter("/comic/")
.substringBefore("-")
return GET("$apiUrl/books/$bookId", apiHeaders)
}
override fun mangaDetailsParse(response: Response): SManga {
return response.parseAs<DataDto<MangaDto>>().data.toSManga(cdnUrl)
}
// ============================== Chapters ==============================
override fun getChapterUrl(chapter: SChapter): String = baseUrl + chapter.url
override fun chapterListRequest(manga: SManga): Request {
val bookId = manga.url.substringAfter("/comic/")
.substringBefore("-")
return GET("$apiUrl/chapters/page?sortDirection=ASC&bookId=$bookId&pageNumber=1&pageSize=9999", apiHeaders)
}
override fun chapterListParse(response: Response): List<SChapter> {
return response.parseAs<ListDataDto<ChapterDto>>().data.list.mapIndexed { index, chapterDto ->
chapterDto.toSChapter(index + 1)
}.reversed()
}
// =============================== Pages ================================
override fun pageListRequest(chapter: SChapter): Request {
val chapterId = chapter.url.substringAfter("/episode/")
.substringBefore("-")
return GET("$apiUrl/chapters/$chapterId", apiHeaders)
}
override fun pageListParse(response: Response): List<Page> {
return response.parseAs<DataDto<PageDataDto>>().data.comicImageList.map {
Page(it.sortNum, imageUrl = "$cdnUrl/${it.imageUrl}")
}
}
override fun imageRequest(page: Page): Request {
val imgHeaders = headersBuilder().apply {
add("Accept", "image/avif,image/webp,*/*")
add("Host", page.imageUrl!!.toHttpUrl().host)
}.build()
return GET(page.imageUrl!!, imgHeaders)
}
override fun imageUrlParse(response: Response): String =
throw UnsupportedOperationException()
// ============================= Utilities ==============================
private inline fun <reified T> Response.parseAs(): T {
return json.decodeFromString(body.string())
}
}

View File

@ -1,89 +0,0 @@
package eu.kanade.tachiyomi.extension.en.comicfans
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.Serializable
typealias ListDataDto<T> = DataDto<ListDto<T>>
@Serializable
class ListDto<T>(
val totalPages: Int,
val list: List<T>,
)
@Serializable
class DataDto<T>(
val data: T,
)
@Serializable
class MangaDto(
val id: Int,
val title: String,
val coverImgUrl: String,
val status: Int,
val authorPseudonym: String? = null,
val synopsis: String? = null,
) {
fun toSManga(cdnUrl: String): SManga = SManga.create().apply {
title = this@MangaDto.title
thumbnail_url = "$cdnUrl/$coverImgUrl"
author = authorPseudonym
url = buildString {
append("/comic/")
append(slugify(id, title))
}
description = synopsis
status = when (this@MangaDto.status) {
0 -> SManga.ONGOING
1 -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
initialized = true
}
}
@Serializable
class ChapterDto(
val id: Int,
val title: String,
val updateTime: Long? = null,
) {
fun toSChapter(index: Int): SChapter = SChapter.create().apply {
name = "Ch. $index - $title"
chapter_number = index.toFloat()
date_upload = updateTime ?: 0L
url = buildString {
append("/episode/")
append(slugify(id, title))
}
}
}
@Serializable
class PageDataDto(
val comicImageList: List<PageDto>,
) {
@Serializable
class PageDto(
val imageUrl: String,
val sortNum: Int,
)
}
private val symbolsRegex = Regex("\\W")
private val hyphenRegex = Regex("-{2,}")
private fun slugify(id: Int, title: String): String = buildString {
append(id)
append("-")
append(
title.lowercase()
.replace(symbolsRegex, "-")
.replace(hyphenRegex, "-")
.removeSuffix("-")
.removePrefix("-"),
)
}

View File

@ -1,64 +0,0 @@
package eu.kanade.tachiyomi.extension.en.comicfans
import eu.kanade.tachiyomi.source.model.Filter
open class UriPartFilter(displayName: String, val vals: Array<Pair<String, String>>) :
Filter.Select<String>(displayName, vals.map { it.first }.toTypedArray()) {
fun toUriPart() = vals[state].second
}
inline fun <reified R> List<*>.getUriPart(): String? =
(filterIsInstance<R>().first() as UriPartFilter).toUriPart().takeIf { it.isNotEmpty() }
class GenreFilter : UriPartFilter(
"Genre",
arrayOf(
Pair("All", ""),
Pair("BL", "1001"),
Pair("Fantasy", "1002"),
Pair("GL", "1003"),
Pair("CEO", "1004"),
Pair("Romance", "1005"),
Pair("Harem", "1006"),
Pair("Action", "1007"),
Pair("Teen", "1008"),
Pair("Adventure", "1009"),
Pair("Eastern", "1010"),
Pair("Comedy", "1011"),
Pair("Esports", "1012"),
Pair("Historical", "1013"),
Pair("Mystery", "1014"),
Pair("Modern", "1015"),
Pair("Urban", "1016"),
Pair("Wuxia", "1017"),
Pair("Suspense", "1018"),
Pair("Female Lead", "1019"),
Pair("Western Fantasy", "1020"),
Pair("Horror", "1022"),
Pair("Realistic Fiction", "1023"),
Pair("Cute", "1024"),
Pair("Campus", "1025"),
Pair("Sci-fi", "1026"),
Pair("History", "1027"),
),
)
class LastUpdateFilter : UriPartFilter(
"Last Update",
arrayOf(
Pair("All", ""),
Pair("Within 3 Days", "3"),
Pair("Within 7 Days", "7"),
Pair("Within 15 Days", "15"),
Pair("Within 30 Days", "30"),
),
)
class StatusFilter : UriPartFilter(
"Status",
arrayOf(
Pair("All", ""),
Pair("Ongoing", "0"),
Pair("Completed", "1"),
),
)

View File

@ -1,10 +0,0 @@
ext {
extName = 'Cookie Kiara'
extClass = '.CookieKiara'
themePkg = 'madara'
baseUrl = 'https://18.kiara.cool'
overrideVersionCode = 0
isNsfw = true
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.cookiekiara
import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.source.model.SChapter
import okhttp3.Response
class CookieKiara : Madara("Cookie Kiara", "https://18.kiara.cool", "en") {
override fun chapterListParse(response: Response): List<SChapter> {
return super.chapterListParse(response).reversed()
}
}

View File

@ -1,10 +0,0 @@
ext {
extName = 'Dark-Scan.com'
extClass = '.DarkScanCom'
themePkg = 'madara'
baseUrl = 'https://dark-scan.com'
overrideVersionCode = 0
isNsfw = false
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,14 +0,0 @@
package eu.kanade.tachiyomi.extension.en.darkscancom
import eu.kanade.tachiyomi.multisrc.madara.Madara
class DarkScanCom : Madara(
"Dark-Scan.com",
"https://dark-scan.com",
"en",
) {
override val useLoadMoreRequest = LoadMoreStrategy.Never
override val useNewChapterEndpoint = true
override val mangaDetailsSelectorStatus = "div.summary-heading:contains(Status) + div.summary-content"
}

Some files were not shown because too many files have changed in this diff Show More