Move mangasect to liliana theme (#2534)

* fix page request + move mangasect to liliana

* remove unused file

* Update src/en/mangasect/build.gradle
This commit is contained in:
Secozzi 2024-04-25 15:46:47 +00:00 committed by GitHub
parent 35112d56f7
commit cfd6f0549e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 14 additions and 556 deletions

View File

@ -2,4 +2,4 @@ plugins {
id("lib-multisrc")
}
baseVersionCode = 2
baseVersionCode = 3

View File

@ -342,6 +342,7 @@ abstract class Liliana(
val imgHeaders = headersBuilder().apply {
add("Accept", "image/avif,image/webp,*/*")
add("Host", page.imageUrl!!.toHttpUrl().host)
removeAll("Referer")
}.build()
return GET(page.imageUrl!!, imgHeaders)
}

View File

@ -1,142 +0,0 @@
package eu.kanade.tachiyomi.extension.en.comickiba
import eu.kanade.tachiyomi.source.model.Filter
object Note : Filter.Header("NOTE: Ignored if using text search!")
sealed class Select(
name: String,
val param: String,
values: Array<String>,
) : Filter.Select<String>(name, values) {
open val selection: String
get() = if (state == 0) "" else state.toString()
}
class StatusFilter(
values: Array<String> = statuses.keys.toTypedArray(),
) : Select("Status", "status", values) {
override val selection: String
get() = statuses[values[state]]!!
companion object {
private val statuses = mapOf(
"All" to "",
"Completed" to "completed",
"OnGoing" to "on-going",
"On-Hold" to "on-hold",
"Canceled" to "canceled",
)
}
}
class SortFilter(
values: Array<String> = orders.keys.toTypedArray(),
) : Select("Sort", "sort", values) {
override val selection: String
get() = orders[values[state]]!!
companion object {
private val orders = mapOf(
"Default" to "default",
"Latest Updated" to "latest-updated",
"Most Viewed" to "views",
"Most Viewed Month" to "views_month",
"Most Viewed Week" to "views_week",
"Most Viewed Day" to "views_day",
"Score" to "score",
"Name A-Z" to "az",
"Name Z-A" to "za",
"The highest chapter count" to "chapters",
"Newest" to "new",
"Oldest" to "old",
)
}
}
class Genre(name: String, val id: String) : Filter.CheckBox(name)
class GenresFilter(
values: List<Genre> = genres,
) : Filter.Group<Genre>("Genres", values) {
val param = "genres"
val selection: String
get() = state.filter { it.state }.joinToString(",") { it.id }
companion object {
private val genres: List<Genre>
get() = listOf(
Genre("Action", "37"),
Genre("Adaptation", "19"),
Genre("Adult", "5310"),
Genre("Adventure", "38"),
Genre("Aliens", "5436"),
Genre("Animals", "1552"),
Genre("Award Winning", "39"),
Genre("Comedy", "202"),
Genre("Comic", "287"),
Genre("Cooking", "277"),
Genre("Crime", "2723"),
Genre("Delinquents", "4438"),
Genre("Demons", "379"),
Genre("Drama", "3"),
Genre("Ecchi", "17"),
Genre("Fantasy", "197"),
Genre("Full Color", "13"),
Genre("Gender Bender", "221"),
Genre("Genderswap", "2290"),
Genre("Ghosts", "2866"),
Genre("Gore", "42"),
Genre("Harem", "222"),
Genre("Historical", "4"),
Genre("Horror", "5"),
Genre("Isekai", "259"),
Genre("Josei", "292"),
Genre("Loli", "5449"),
Genre("Long Strip", "7"),
Genre("Magic", "272"),
Genre("Manhwa", "266"),
Genre("Martial Arts", "40"),
Genre("Mature", "5311"),
Genre("Mecha", "2830"),
Genre("Medical", "1598"),
Genre("Military", "43"),
Genre("Monster Girls", "2307"),
Genre("Monsters", "298"),
Genre("Music", "3182"),
Genre("Mystery", "6"),
Genre("Office Workers", "14"),
Genre("Official Colored", "1046"),
Genre("Philosophical", "2776"),
Genre("Post-Apocalyptic", "1059"),
Genre("Psychological", "493"),
Genre("Reincarnation", "204"),
Genre("Reverse", "280"),
Genre("Reverse Harem", "199"),
Genre("Romance", "186"),
Genre("School Life", "601"),
Genre("Sci-Fi", "1845"),
Genre("Sexual Violence", "731"),
Genre("Shoujo", "254"),
Genre("Slice of Life", "10"),
Genre("Sports", "4066"),
Genre("Superhero", "481"),
Genre("Supernatural", "198"),
Genre("Survival", "44"),
Genre("Thriller", "1058"),
Genre("Time Travel", "299"),
Genre("Tragedy", "41"),
Genre("Video Games", "1846"),
Genre("Villainess", "278"),
Genre("Virtual Reality", "1847"),
Genre("Web Comic", "12"),
Genre("Webtoon", "279"),
Genre("Webtoons", "267"),
Genre("Wuxia", "203"),
Genre("Yaoi", "18"),
Genre("Yuri", "11"),
Genre("Zombies", "1060"),
)
}
}

View File

@ -1,7 +1,9 @@
ext {
extName = 'Manga Sect'
extClass = '.MangaSect'
extVersionCode = 1
themePkg = 'liliana'
baseUrl = 'https://mangasect.net'
overrideVersionCode = 0
}
apply from: "$rootDir/common.gradle"

View File

@ -1,238 +1,15 @@
package eu.kanade.tachiyomi.extension.en.mangasect
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.multisrc.liliana.Liliana
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.ParsedHttpSource
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.injectLazy
class MangaSect : ParsedHttpSource() {
override val name = "Manga Sect"
override val baseUrl = "https://mangasect.com"
override val lang = "en"
override val supportsLatest = true
private val json: Json by injectLazy()
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
class MangaSect : Liliana(
"Manga Sect",
"https://mangasect.net",
"en",
usesPostSearch = true,
) {
override val client = super.client.newBuilder()
.rateLimit(1)
.build()
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
// Popular
override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/ranking/week/$page", headers)
override fun popularMangaSelector(): String = "div#main div.grid > div"
override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply {
thumbnail_url = element.selectFirst("img")?.imgAttr()
element.selectFirst(".text-center a")!!.run {
title = text().trim()
setUrlWithoutDomain(attr("href"))
}
}
override fun popularMangaNextPageSelector(): String = ".blog-pager > span.pagecurrent + span"
// Latest
override fun latestUpdatesRequest(page: Int): Request =
GET("$baseUrl/all-manga/$page/?sort=1", headers)
override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response)
override fun latestUpdatesSelector(): String =
throw UnsupportedOperationException()
override fun latestUpdatesFromElement(element: Element): SManga =
throw UnsupportedOperationException()
override fun latestUpdatesNextPageSelector(): String =
throw UnsupportedOperationException()
// Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val url = baseUrl.toHttpUrl().newBuilder().apply {
if (query.isNotBlank()) {
addPathSegment("search")
addQueryParameter("keyword", query)
} else {
addPathSegment("filter")
filters.forEach { filter ->
when (filter) {
is GenreFilter -> {
if (filter.checked.isNotEmpty()) {
addQueryParameter("genres", filter.checked.joinToString(","))
}
}
is StatusFilter -> {
if (filter.selected.isNotBlank()) {
addQueryParameter("status", filter.selected)
}
}
is SortFilter -> {
addQueryParameter("sort", filter.selected)
}
is ChapterCountFilter -> {
addQueryParameter("chapter_count", filter.selected)
}
is GenderFilter -> {
addQueryParameter("sex", filter.selected)
}
else -> {}
}
}
}
addPathSegment(page.toString())
addPathSegment("")
}
return GET(url.build(), headers)
}
override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response)
override fun searchMangaSelector(): String =
throw UnsupportedOperationException()
override fun searchMangaFromElement(element: Element): SManga =
throw UnsupportedOperationException()
override fun searchMangaNextPageSelector(): String =
throw UnsupportedOperationException()
// Filters
override fun getFilterList(): FilterList = FilterList(
Filter.Header("Ignored when using text search"),
Filter.Separator(),
GenreFilter(),
ChapterCountFilter(),
GenderFilter(),
StatusFilter(),
SortFilter(),
)
// Details
override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply {
description = document.selectFirst("div#syn-target")?.text()
thumbnail_url = document.selectFirst(".a1 > figure img")?.imgAttr()
title = document.selectFirst(".a2 header h1")?.text()?.trim() ?: "N/A"
genre = document.select(".a2 div > a[rel='tag'].label").joinToString(", ") { it.text() }
document.selectFirst(".a1 > aside")?.run {
author = select("div:contains(Authors) > span a")
.joinToString(", ") { it.text().trim() }
.takeUnless { it.isBlank() || it.equals("Updating", true) }
status = selectFirst("div:contains(Status) > span")?.text().let(::parseStatus)
}
}
private fun parseStatus(status: String?): Int = when {
status.equals("ongoing", true) -> SManga.ONGOING
status.equals("completed", true) -> SManga.COMPLETED
status.equals("on-hold", true) -> SManga.ON_HIATUS
status.equals("canceled", true) -> SManga.CANCELLED
else -> SManga.UNKNOWN
}
// Chapters
override fun chapterListSelector() = "ul > li.chapter"
override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply {
element.selectFirst("time[datetime]")?.also {
date_upload = it.attr("datetime").toLongOrNull()?.let { it * 1000L } ?: 0L
}
element.selectFirst("a")!!.run {
text().trim().also {
name = it
chapter_number = it.substringAfter("hapter ").toFloatOrNull() ?: 0F
}
setUrlWithoutDomain(attr("href"))
}
}
// Pages
override fun pageListRequest(chapter: SChapter): Request {
val pageHeaders = headersBuilder().apply {
add("Accept", "application/json, text/javascript, */*; q=0.01")
add("Host", baseUrl.toHttpUrl().host)
add("Referer", baseUrl + chapter.url)
add("X-Requested-With", "XMLHttpRequest")
}.build()
val id = chapter.url.split("/").last()
return GET("$baseUrl/ajax/image/list/chap/$id", pageHeaders)
}
@Serializable
data class PageListResponseDto(val html: String)
override fun pageListParse(response: Response): List<Page> {
val data = response.parseAs<PageListResponseDto>().html
return pageListParse(
Jsoup.parseBodyFragment(
data,
response.request.header("Referer")!!,
),
)
}
override fun pageListParse(document: Document): List<Page> {
return document.select("div.separator").map { page ->
val index = page.attr("data-index").toInt()
val url = page.selectFirst("a")!!.attr("abs:href")
Page(index, document.location(), url)
}.sortedBy { it.index }
}
override fun imageUrlParse(document: Document) = ""
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)
}
// Utilities
// From mangathemesia
private fun Element.imgAttr(): String = when {
hasAttr("data-lazy-src") -> attr("abs:data-lazy-src")
hasAttr("data-src") -> attr("abs:data-src")
else -> attr("abs:src")
}
private inline fun <reified T> Response.parseAs(): T {
return json.decodeFromString(body.string())
}
}

View File

@ -1,167 +0,0 @@
package eu.kanade.tachiyomi.extension.en.mangasect
import eu.kanade.tachiyomi.source.model.Filter
abstract class SelectFilter(
name: String,
private val options: List<Pair<String, String>>,
) : Filter.Select<String>(
name,
options.map { it.first }.toTypedArray(),
) {
val selected get() = options[state].second
}
class CheckBoxFilter(
name: String,
val value: String,
) : Filter.CheckBox(name)
class ChapterCountFilter : SelectFilter("Chapter count", chapterCount) {
companion object {
private val chapterCount = listOf(
Pair(">= 0", "0"),
Pair(">= 10", "10"),
Pair(">= 30", "30"),
Pair(">= 50", "50"),
Pair(">= 100", "100"),
Pair(">= 200", "200"),
Pair(">= 300", "300"),
Pair(">= 400", "400"),
Pair(">= 500", "500"),
)
}
}
class GenderFilter : SelectFilter("Manga Gender", gender) {
companion object {
private val gender = listOf(
Pair("All", "All"),
Pair("Boy", "Boy"),
Pair("Girl", "Girl"),
)
}
}
class StatusFilter : SelectFilter("Status", status) {
companion object {
private val status = listOf(
Pair("All", ""),
Pair("Completed", "completed"),
Pair("OnGoing", "on-going"),
Pair("On-Hold", "on-hold"),
Pair("Canceled", "canceled"),
)
}
}
class SortFilter : SelectFilter("Sort", sort) {
companion object {
private val sort = listOf(
Pair("Default", "default"),
Pair("Latest Updated", "latest-updated"),
Pair("Most Viewed", "most-viewd"),
Pair("Score", "score"),
Pair("Name A-Z", "az"),
Pair("Name Z-A", "za"),
Pair("Newest", "new"),
Pair("Oldest", "old"),
)
}
}
class GenreFilter : Filter.Group<CheckBoxFilter>(
"Genre",
genres.map { CheckBoxFilter(it.first, it.second) },
) {
val checked get() = state.filter { it.state }.map { it.value }
companion object {
private val genres = listOf(
Pair("Action", "29"),
Pair("Adaptation", "66"),
Pair("Adult", "108"),
Pair("Adventure", "33"),
Pair("Aliens", "2326"),
Pair("Animals", "199"),
Pair("Comedy", "35"),
Pair("Comic", "109"),
Pair("Cooking", "26"),
Pair("Crime", "274"),
Pair("Delinquents", "234"),
Pair("Demons", "136"),
Pair("Drama", "39"),
Pair("Dungeons", "204"),
Pair("Ecchi", "54"),
Pair("Fantasy", "30"),
Pair("Full Color", "27"),
Pair("Genderswap", "1441"),
Pair("Genius MC", "209"),
Pair("Ghosts", "1527"),
Pair("Gore", "1678"),
Pair("Harem", "43"),
Pair("Historical", "49"),
Pair("Horror", "69"),
Pair("Incest", "1189"),
Pair("Isekai", "40"),
Pair("Loli", "198"),
Pair("Long Strip", "233"),
Pair("Magic", "212"),
Pair("Magical Girls", "1676"),
Pair("Manhua", "58"),
Pair("Manhwa", "80"),
Pair("Martial Arts", "32"),
Pair("Mature", "34"),
Pair("Mecha", "70"),
Pair("Medical", "2113"),
Pair("Military", "1531"),
Pair("Monster", "218"),
Pair("Monster Girls", "201"),
Pair("Monsters", "63"),
Pair("Murim", "208"),
Pair("Music", "412"),
Pair("Mystery", "31"),
Pair("One shot", "155"),
Pair("Overpowered", "206"),
Pair("Police", "275"),
Pair("Post-Apocalyptic", "197"),
Pair("Psychological", "36"),
Pair("Rebirth", "1435"),
Pair("Recarnation", "67"),
Pair("Regression", "205"),
Pair("Reincarnation", "64"),
Pair("Return", "1454"),
Pair("Returner", "211"),
Pair("Revenge", "219"),
Pair("Romance", "37"),
Pair("School Life", "44"),
Pair("Sci fi", "42"),
Pair("Sci-fi", "216"),
Pair("Seinen", "52"),
Pair("Sexual Violence", "2325"),
Pair("Shota", "2327"),
Pair("Shoujo", "92"),
Pair("Shounen", "38"),
Pair("Shounen ai", "103"),
Pair("Slice of Life", "68"),
Pair("Super power", "213"),
Pair("Superhero", "1630"),
Pair("Supernatural", "41"),
Pair("Survival", "463"),
Pair("System", "203"),
Pair("Thriller", "462"),
Pair("Time travel", "65"),
Pair("tower", "207"),
Pair("Tragedy", "51"),
Pair("Transmigration", "217"),
Pair("Uncategorized", "55"),
Pair("Vampires", "200"),
Pair("Video Games", "1606"),
Pair("Virtual Reality", "757"),
Pair("Web comic", "98"),
Pair("Webtoons", "77"),
Pair("Wuxia", "202"),
Pair("Zombies", "464"),
)
}
}

View File

@ -1,18 +1,5 @@
package eu.kanade.tachiyomi.extension.ja.mangakoma
import eu.kanade.tachiyomi.multisrc.liliana.Liliana
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.Page
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Request
class MangaKoma : Liliana("Manga Koma", "https://mangakoma01.net", "ja") {
override fun imageRequest(page: Page): Request {
val imgHeaders = headersBuilder().apply {
add("Accept", "image/avif,image/webp,*/*")
add("Host", page.imageUrl!!.toHttpUrl().host)
removeAll("Referer")
}.build()
return GET(page.imageUrl!!, imgHeaders)
}
}
class MangaKoma : Liliana("Manga Koma", "https://mangakoma01.net", "ja")