Remove MangaHub (#13174)

* Remove MangaHub

* escape regex

* [skip ci] dedupe
This commit is contained in:
Vetle Ledaal 2022-08-24 22:45:05 +02:00 committed by GitHub
parent b0861b5f79
commit 1f6f5534bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
75 changed files with 1 additions and 708 deletions

View File

@ -37,7 +37,7 @@ jobs:
}, },
{ {
"type": "both", "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\\+?|cocomanga|hitomi\\.la|copymanga|neox).*", "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\\+?|cocomanga|hitomi\\.la|copymanga|neox|1manga\\.co|mangafox\\.fun|mangahere\\.onl|manga\\s*hub|mangakakalot\\.fun|manganel(?!o)|mangaonline\\.fun|mangapanda\\.onl|mangareader\\.site|mangatoday|manga\\.town|onemanga\\.info).*",
"ignoreCase": true, "ignoreCase": true,
"message": "{match} will not be added back as it is too difficult to maintain. Read #3475 for more information" "message": "{match} will not be added back as it is too difficult to maintain. Read #3475 for more information"
}, },

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.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.mangafoxfun
import eu.kanade.tachiyomi.multisrc.mangahub.MangaHub
class MangaFoxFun : MangaHub(
"MangaFox.fun",
"https://mangafox.fun",
"en"
) {
override val serverId = "mf01"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.mangahereonl
import eu.kanade.tachiyomi.multisrc.mangahub.MangaHub
class MangaHereOnl : MangaHub(
"MangaHere.onl",
"https://mangahere.onl",
"en"
) {
override val serverId = "mh01"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.mangahubio
import eu.kanade.tachiyomi.multisrc.mangahub.MangaHub
class MangaHubIo : MangaHub(
"MangaHub",
"https://mangahub.io",
"en"
) {
override val serverId = "m01"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.mangakakalotfun
import eu.kanade.tachiyomi.multisrc.mangahub.MangaHub
class MangakakalotFun : MangaHub(
"Mangakakalot.fun",
"https://mangakakalot.fun",
"en"
) {
override val serverId = "mn01"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.manganel
import eu.kanade.tachiyomi.multisrc.mangahub.MangaHub
class MangaNel : MangaHub(
"MangaNel",
"https://manganel.me",
"en"
) {
override val serverId = "mn05"
}

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.mangaonlinefun
import eu.kanade.tachiyomi.multisrc.mangahub.MangaHub
class MangaOnlineFun : MangaHub(
"MangaOnline.fun",
"https://mangaonline.fun",
"en"
) {
override val serverId = "m02"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.mangapandaonl
import eu.kanade.tachiyomi.multisrc.mangahub.MangaHub
class MangaPandaOnl : MangaHub(
"MangaPanda.onl",
"https://mangapanda.onl",
"en"
) {
override val serverId = "mr02"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.mangareadersite
import eu.kanade.tachiyomi.multisrc.mangahub.MangaHub
class MangaReaderSite : MangaHub(
"MangaReader.site",
"https://mangareader.site",
"en"
) {
override val serverId = "mr01"
}

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.mangatoday
import eu.kanade.tachiyomi.multisrc.mangahub.MangaHub
class MangaToday : MangaHub(
"MangaToday",
"https://mangatoday.fun",
"en"
) {
override val serverId = "m03"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.mangatownhub
import eu.kanade.tachiyomi.multisrc.mangahub.MangaHub
class MangaTownHub : MangaHub(
"MangaTown (unoriginal)",
"https://manga.town",
"en"
) {
override val serverId = "mt01"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.onemangaco
import eu.kanade.tachiyomi.multisrc.mangahub.MangaHub
class OneMangaCo : MangaHub(
"1Manga.co",
"https://1manga.co",
"en"
) {
override val serverId = "mn03"
}

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.extension.en.onemangainfo
import eu.kanade.tachiyomi.multisrc.mangahub.MangaHub
class OneMangaInfo : MangaHub(
"OneManga.info",
"https://onemanga.info",
"en"
) {
override val serverId = "mn02"
}

View File

@ -1,539 +0,0 @@
package eu.kanade.tachiyomi.multisrc.mangahub
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
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.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonObject
import okhttp3.Cookie
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
import uy.kohesive.injekt.injectLazy
import java.net.URLEncoder
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
abstract class MangaHub(
override val name: String,
override val baseUrl: String,
override val lang: String,
private val dateFormat: SimpleDateFormat = SimpleDateFormat("MM-dd-yyyy", Locale.US)
) : ParsedHttpSource() {
override val supportsLatest = true
private val json: Json by injectLazy()
protected abstract val serverId: String
protected open val cdnImgUrl = "https://img.mghubcdn.com"
protected open val cdnApiUrl = "https://api.mghubcdn.com"
override val client: OkHttpClient by lazy {
super.client.newBuilder()
.addInterceptor(::apiAuthInterceptor)
.rateLimitHost(cdnImgUrl.toHttpUrl(), 1, 2)
.build()
}
private fun apiAuthInterceptor(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val cookie = client.cookieJar
.loadForRequest(baseUrl.toHttpUrl())
.firstOrNull { it.name == "mhub_access" }
val request =
if (originalRequest.url.toString() == "$cdnApiUrl/graphql" && cookie != null) {
originalRequest.newBuilder()
.header("x-mhub-access", cookie.value)
.build()
} else {
originalRequest
}
return chain.proceed(request)
}
private fun refreshApiKey(chapter: SChapter) {
val now = Calendar.getInstance().time.time
val slug = "$baseUrl${chapter.url}"
.toHttpUrlOrNull()
?.pathSegments
?.get(1)
val url = if (slug != null) {
"$baseUrl/manga/$slug".toHttpUrl()
} else {
baseUrl.toHttpUrl()
}
// Clear key cookie
val cookie = Cookie.parse(url, "mhub_access=; Max-Age=8640000; Path=/; Expires=Mon, 1 Jan 2080 00:00:00 GMT")!!
client.cookieJar.saveFromResponse(url, listOf(cookie))
// Set required cookie (for cache busting?)
val recently = buildJsonObject {
putJsonObject((now - (0..3600).random()).toString()) {
put("mangaID", (1..42_000).random())
put("number", (1..20).random())
}
}.toString()
client.cookieJar.saveFromResponse(
url,
listOf(
Cookie.Builder()
.domain(url.host)
.name("recently")
.value(URLEncoder.encode(recently, "utf-8"))
.expiresAt(now + 2 * 60 * 60 * 24 * 31) // +2 months
.build()
)
)
val request = GET("$url?reloadKey=1", headers)
client.newCall(request).execute()
}
override fun headersBuilder(): Headers.Builder {
val defaultUserAgent = super.headersBuilder()["User-Agent"]
val chromeVersion = defaultUserAgent
?.substringAfter("Chrome/")
?.substringBefore(".")
?.toIntOrNull()
?: "102"
val edgeVersion = defaultUserAgent
?.substringAfter("Edg/")
?.substringBefore(".")
?.toIntOrNull()
?: chromeVersion
return super.headersBuilder()
.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
.add("Accept-Language", "en-US,en;q=0.5")
.add("DNT", "1")
.add("Referer", "$baseUrl/")
.add("Sec-CH-UA", "\"Chromium\";v=\"$chromeVersion\", \" Not A;Brand\";v=\"99\", \"Microsoft Edge\";v=\"$edgeVersion\"")
.add("Sec-CH-UA-Mobile", "?0")
.add("Sec-CH-UA-Platform", "\"Windows\"")
.add("Sec-Fetch-Dest", "document")
.add("Sec-Fetch-Mode", "navigate")
.add("Sec-Fetch-Site", "same-origin")
.add("Upgrade-Insecure-Requests", "1")
}
// Popular
override fun popularMangaRequest(page: Int): Request =
GET("$baseUrl/popular/page/$page", headers)
override fun popularMangaParse(response: Response): MangasPage =
searchMangaParse(response)
override fun popularMangaSelector(): String =
"#mangalist div.media-manga.media"
override fun popularMangaFromElement(element: Element): SManga =
searchMangaFromElement(element)
override fun popularMangaNextPageSelector(): String? =
searchMangaNextPageSelector()
// Latest
override fun latestUpdatesRequest(page: Int): Request =
GET("$baseUrl/updates/page/$page", headers)
override fun latestUpdatesParse(response: Response): MangasPage =
searchMangaParse(response)
override fun latestUpdatesSelector(): String =
popularMangaSelector()
override fun latestUpdatesFromElement(element: Element): SManga =
searchMangaFromElement(element)
override fun latestUpdatesNextPageSelector(): String? =
searchMangaNextPageSelector()
// Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
// https://mangahub.io/search/page/1?q=a&order=POPULAR&genre=all
val url = "$baseUrl/search/page/$page".toHttpUrl().newBuilder()
.addQueryParameter("q", query)
(if (filters.isEmpty()) getFilterList() else filters).forEach { filter ->
when (filter) {
is OrderBy -> {
val order = filter.values[filter.state]
url.addQueryParameter("order", order.key)
}
is GenreList -> {
val genre = filter.values[filter.state]
url.addQueryParameter("genre", genre.key)
}
else -> {}
}
}
return GET(url.toString(), headers)
}
override fun searchMangaParse(response: Response): MangasPage {
val document = response.asJsoup()
/*
* To remove duplicates we group by the thumbnail_url, which is
* common between duplicates. The duplicates have a suffix in the
* url "-by-{name}". Here we select the shortest url, to avoid
* removing manga that has "by" in the title already.
* Example:
* /manga/tales-of-demons-and-gods (kept)
* /manga/tales-of-demons-and-gods-by-mad-snail (removed)
* /manga/leveling-up-by-only-eating (kept)
*/
val mangas = document.select(searchMangaSelector()).map { element ->
searchMangaFromElement(element)
}.groupBy { it.thumbnail_url }.mapValues { (_, values) ->
values.minByOrNull { it.url.length }!!
}.values.toList()
val hasNextPage = searchMangaNextPageSelector()?.let { selector ->
document.select(selector).first()
} != null
return MangasPage(mangas, hasNextPage)
}
override fun searchMangaSelector() = "div#mangalist div.media-manga.media"
override fun searchMangaFromElement(element: Element): SManga = SManga.create().apply {
val titleElement = element.select(".media-heading > a").first()
setUrlWithoutDomain(titleElement.attr("abs:href"))
title = titleElement.text()
thumbnail_url = element
.select("img.manga-thumb.list-item-thumb")
?.first()?.attr("abs:src")
}
override fun searchMangaNextPageSelector() = "ul.pager li.next > a"
// Details
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
title = document.select("h1._3xnDj").first().ownText()
author = document
.select("._3QCtP > div:nth-child(2) > div:nth-child(1) > span:nth-child(2)")
?.first()?.text()
artist = document
.select("._3QCtP > div:nth-child(2) > div:nth-child(2) > span:nth-child(2)")
?.first()?.text()
genre = document.select("._3Czbn a")?.joinToString { it.text() }
description = document.select("div#noanim-content-tab-pane-99 p.ZyMp7")?.first()?.text()
thumbnail_url = document.select("img.img-responsive")?.first()?.attr("abs:src")
document.select("._3QCtP > div:nth-child(2) > div:nth-child(3) > span:nth-child(2)")
?.first()?.text()?.also { statusText ->
status = when {
statusText.contains("ongoing", true) -> SManga.ONGOING
statusText.contains("completed", true) -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
}
// add alternative name to manga description
val altName = "Alternative Name: "
document.select("h1 small").firstOrNull()?.ownText()?.let {
if (it.isBlank().not()) {
description = when {
description.isNullOrBlank() -> altName + it
else -> description + "\n\n$altName" + it
}
}
}
}
// Chapters
override fun chapterListSelector() = ".tab-content .tab-pane li.list-group-item > a"
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
setUrlWithoutDomain(element.attr("abs:href"))
name = element.select("span._8Qtbo").text()
date_upload = element.select("small.UovLc").first()?.text()
?.let { parseChapterDate(it) } ?: 0
}
private fun parseChapterDate(date: String): Long {
val now = Calendar.getInstance().apply {
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}
var parsedDate = 0L
when {
"just now" in date || "less than an hour" in date -> {
parsedDate = now.timeInMillis
}
// parses: "1 hour ago" and "2 hours ago"
"hour" in date -> {
val hours = date.replaceAfter(" ", "").trim().toInt()
parsedDate = now.apply { add(Calendar.HOUR, -hours) }.timeInMillis
}
// parses: "Yesterday" and "2 days ago"
"day" in date -> {
val days = date.replace("days ago", "").trim().toIntOrNull() ?: 1
parsedDate = now.apply { add(Calendar.DAY_OF_YEAR, -days) }.timeInMillis
}
// parses: "2 weeks ago"
"weeks" in date -> {
val weeks = date.replace("weeks ago", "").trim().toInt()
parsedDate = now.apply { add(Calendar.WEEK_OF_YEAR, -weeks) }.timeInMillis
}
// parses: "12-20-2019" and defaults everything that wasn't taken into account to 0
else -> {
try {
parsedDate = dateFormat.parse(date)?.time ?: 0L
} catch (e: ParseException) {
/* nothing to do, parsedDate is initialized with 0L */
}
}
}
return parsedDate
}
// Pages
override fun pageListRequest(chapter: SChapter): Request {
val jsonHeaders = headersBuilder()
.set("Accept", "application/json")
.add("Content-Type", "application/json")
.add("Origin", baseUrl)
.set("Sec-Fetch-Dest", "empty")
.set("Sec-Fetch-Mode", "cors")
.set("Sec-Fetch-Site", "cross-site")
.removeAll("Upgrade-Insecure-Requests")
.build()
val slug = chapter.url
.substringAfter("chapter/")
.substringBefore("/")
val number = chapter.url
.substringAfter("chapter-")
.removeSuffix("/")
val body =
"{\"query\":\"{chapter(x:$serverId,slug:\\\"$slug\\\",number:$number){id,title,mangaID,number,slug,date,pages,noAd,manga{id,title,slug,mainSlug,author,isWebtoon,isYaoi,isPorn,isSoftPorn,unauthFile,isLicensed}}}\"}".toRequestBody(
null
)
return POST("$cdnApiUrl/graphql", jsonHeaders, body)
}
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> =
super.fetchPageList(chapter)
.doOnError { refreshApiKey(chapter) }
.retry(1)
override fun pageListParse(response: Response): List<Page> {
val cdn = "$cdnImgUrl/file/imghub"
val chapterObject = json
.decodeFromString<GraphQLDataDto<ChapterDto>>(response.body!!.string())
if (chapterObject.data?.chapter == null) {
if (chapterObject.errors != null) {
val errors = chapterObject.errors.joinToString("\n") { it.message }
throw Exception(errors)
}
throw Exception("Unknown error while processing pages")
}
val pagesObject = json
.decodeFromString<JsonObject>(chapterObject.data.chapter.pages)
val pages = pagesObject.values.map { it.jsonPrimitive.content }
return pages.mapIndexed { i, path -> Page(i, "", "$cdn/$path") }
}
override fun pageListParse(document: Document): List<Page> =
throw UnsupportedOperationException("Not used.")
// Image
override fun imageUrlRequest(page: Page): Request {
val newHeaders = headersBuilder()
.set("Accept", "image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8")
.set("Sec-Fetch-Dest", "image")
.set("Sec-Fetch-Mode", "no-cors")
.set("Sec-Fetch-Site", "cross-site")
.removeAll("Upgrade-Insecure-Requests")
.build()
return GET(page.url, newHeaders)
}
override fun imageUrlParse(document: Document): String =
throw UnsupportedOperationException("Not used.")
// Filters
private class Genre(title: String, val key: String) : Filter.TriState(title) {
override fun toString(): String = name
}
private class Order(title: String, val key: String) : Filter.TriState(title) {
override fun toString(): String = name
}
private class OrderBy(orders: Array<Order>) : Filter.Select<Order>("Order", orders, 0)
private class GenreList(genres: Array<Genre>) : Filter.Select<Genre>("Genres", genres, 0)
override fun getFilterList() = FilterList(
OrderBy(orderBy),
GenreList(genres)
)
private val orderBy = arrayOf(
Order("Popular", "POPULAR"),
Order("Updates", "LATEST"),
Order("A-Z", "ALPHABET"),
Order("New", "NEW"),
Order("Completed", "COMPLETED")
)
private val genres = arrayOf(
Genre("All Genres", "all"),
Genre("[no chapters]", "no-chapters"),
Genre("4-Koma", "4-koma"),
Genre("Action", "action"),
Genre("Adult", "adult"),
Genre("Adventure", "adventure"),
Genre("Award Winning", "award-winning"),
Genre("Comedy", "comedy"),
Genre("Cooking", "cooking"),
Genre("Crime", "crime"),
Genre("Demons", "demons"),
Genre("Doujinshi", "doujinshi"),
Genre("Drama", "drama"),
Genre("Ecchi", "ecchi"),
Genre("Fantasy", "fantasy"),
Genre("Food", "food"),
Genre("Game", "game"),
Genre("Gender bender", "gender-bender"),
Genre("Harem", "harem"),
Genre("Historical", "historical"),
Genre("Horror", "horror"),
Genre("Isekai", "isekai"),
Genre("Josei", "josei"),
Genre("Kids", "kids"),
Genre("Magic", "magic"),
Genre("Magical Girls", "magical-girls"),
Genre("Manhua", "manhua"),
Genre("Manhwa", "manhwa"),
Genre("Martial arts", "martial-arts"),
Genre("Mature", "mature"),
Genre("Mecha", "mecha"),
Genre("Medical", "medical"),
Genre("Military", "military"),
Genre("Music", "music"),
Genre("Mystery", "mystery"),
Genre("One shot", "one-shot"),
Genre("Oneshot", "oneshot"),
Genre("Parody", "parody"),
Genre("Police", "police"),
Genre("Psychological", "psychological"),
Genre("Romance", "romance"),
Genre("School life", "school-life"),
Genre("Sci fi", "sci-fi"),
Genre("Seinen", "seinen"),
Genre("Shotacon", "shotacon"),
Genre("Shoujo", "shoujo"),
Genre("Shoujo ai", "shoujo-ai"),
Genre("Shoujoai", "shoujoai"),
Genre("Shounen", "shounen"),
Genre("Shounen ai", "shounen-ai"),
Genre("Shounenai", "shounenai"),
Genre("Slice of life", "slice-of-life"),
Genre("Smut", "smut"),
Genre("Space", "space"),
Genre("Sports", "sports"),
Genre("Super Power", "super-power"),
Genre("Superhero", "superhero"),
Genre("Supernatural", "supernatural"),
Genre("Thriller", "thriller"),
Genre("Tragedy", "tragedy"),
Genre("Vampire", "vampire"),
Genre("Webtoon", "webtoon"),
Genre("Webtoons", "webtoons"),
Genre("Wuxia", "wuxia"),
Genre("Yaoi", "yaoi"),
Genre("Yuri", "yuri")
)
}
// DTO
@Serializable
data class GraphQLDataDto<T>(
val errors: List<ErrorDto>? = null,
val data: T? = null
)
@Serializable
data class ErrorDto(
val message: String
)
@Serializable
data class ChapterDto(
val chapter: ChapterInnerDto?
)
@Serializable
data class ChapterInnerDto(
val date: String,
val id: Int,
val manga: MangaInnerDto,
@SerialName("mangaID") val mangaId: Int,
val noAd: Boolean,
val number: Float,
val pages: String,
val slug: String,
val title: String
)
@Serializable
data class MangaInnerDto(
val author: String,
val id: Int,
val isLicensed: Boolean,
val isPorn: Boolean,
val isSoftPorn: Boolean,
val isWebtoon: Boolean,
val isYaoi: Boolean,
val mainSlug: String,
val slug: String,
val title: String,
val unauthFile: Boolean
)

View File

@ -1,36 +0,0 @@
package eu.kanade.tachiyomi.multisrc.mangahub
import generator.ThemeSourceData.SingleLang
import generator.ThemeSourceGenerator
class MangaHubGenerator : ThemeSourceGenerator {
override val themePkg = "mangahub"
override val themeClass = "MangaHub"
override val baseVersionCode: Int = 10
override val sources = listOf(
SingleLang("1Manga.co", "https://1manga.co", "en", isNsfw = true, className = "OneMangaCo"),
SingleLang("MangaFox.fun", "https://mangafox.fun", "en", isNsfw = true, className = "MangaFoxFun"),
SingleLang("MangaHere.onl", "https://mangahere.onl", "en", isNsfw = true, className = "MangaHereOnl"),
SingleLang("MangaHub", "https://mangahub.io", "en", isNsfw = true, overrideVersionCode = 10, className = "MangaHubIo"),
SingleLang("Mangakakalot.fun", "https://mangakakalot.fun", "en", isNsfw = true, className = "MangakakalotFun"),
SingleLang("MangaNel", "https://manganel.me", "en", isNsfw = true),
SingleLang("MangaOnline.fun", "https://mangaonline.fun", "en", isNsfw = true, className = "MangaOnlineFun"),
SingleLang("MangaPanda.onl", "https://mangapanda.onl", "en", className = "MangaPandaOnl"),
SingleLang("MangaReader.site", "https://mangareader.site", "en", className = "MangaReaderSite"),
SingleLang("MangaToday", "https://mangatoday.fun", "en", isNsfw = true),
SingleLang("MangaTown (unoriginal)", "https://manga.town", "en", isNsfw = true, className = "MangaTownHub"),
// SingleLang("MF Read Online", "https://mangafreereadonline.com", "en", isNsfw = true), // different pageListParse logic
// SingleLang("OneManga.info", "https://onemanga.info", "en", isNsfw = true, className = "OneMangaInfo"), // Some chapters link to 1manga.co, hard to filter
)
companion object {
@JvmStatic
fun main(args: Array<String>) {
MangaHubGenerator().createAll()
}
}
}