ReaperScans Unoriginal: Moved to ParsedHttpSource (#7002)
* move to ParsedHttpSource * remove old code * clean up
This commit is contained in:
parent
c88b8b28aa
commit
ad73175a1f
|
@ -1,9 +1,7 @@
|
||||||
ext {
|
ext {
|
||||||
extName = 'Reaper Scans (unoriginal)'
|
extName = 'Reaper Scans (unoriginal)'
|
||||||
extClass = '.ReaperScansUnoriginal'
|
extClass = '.ReaperScansUnoriginal'
|
||||||
themePkg = 'mangathemesia'
|
extVersionCode = 31
|
||||||
baseUrl = 'https://reaper-scans.com'
|
|
||||||
overrideVersionCode = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: "$rootDir/common.gradle"
|
apply from: "$rootDir/common.gradle"
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
package eu.kanade.tachiyomi.extension.en.reaperscansunoriginal
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.source.model.Filter
|
||||||
|
import eu.kanade.tachiyomi.source.model.FilterList
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
|
||||||
|
interface UrlPartFilter {
|
||||||
|
fun addUrlParameter(url: HttpUrl.Builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class SelectFilter(
|
||||||
|
name: String,
|
||||||
|
private val urlParameter: String,
|
||||||
|
private val options: List<Pair<String, String>>,
|
||||||
|
defaultValue: String? = null,
|
||||||
|
) : UrlPartFilter, Filter.Select<String>(
|
||||||
|
name,
|
||||||
|
options.map { it.first }.toTypedArray(),
|
||||||
|
options.indexOfFirst { it.second == defaultValue }.coerceAtLeast(0),
|
||||||
|
) {
|
||||||
|
override fun addUrlParameter(url: HttpUrl.Builder) {
|
||||||
|
url.addQueryParameter(urlParameter, options[state].second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CheckBoxFilter(name: String, val value: String) : Filter.CheckBox(name)
|
||||||
|
|
||||||
|
open class CheckBoxGroup(
|
||||||
|
name: String,
|
||||||
|
private val urlParameter: String,
|
||||||
|
options: List<Pair<String, String>>,
|
||||||
|
) : UrlPartFilter, Filter.Group<CheckBoxFilter>(
|
||||||
|
name,
|
||||||
|
options.map { CheckBoxFilter(it.first, it.second) },
|
||||||
|
) {
|
||||||
|
override fun addUrlParameter(url: HttpUrl.Builder) {
|
||||||
|
val checked = state.filter { it.state }.map { it.value }
|
||||||
|
|
||||||
|
if (checked.isNotEmpty()) {
|
||||||
|
checked.forEach { genre ->
|
||||||
|
url.addQueryParameter(urlParameter, genre)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TypeFilter : CheckBoxGroup(
|
||||||
|
"Status",
|
||||||
|
"type[]",
|
||||||
|
listOf(
|
||||||
|
Pair("Action", "action"),
|
||||||
|
Pair("Adventure", "adventure"),
|
||||||
|
Pair("Fantasy", "fantasy"),
|
||||||
|
Pair("Manga", "manga"),
|
||||||
|
Pair("Manhua", "manhua"),
|
||||||
|
Pair("Manhwa", "manhwa"),
|
||||||
|
Pair("Seinen", "seinen"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
class GenreFilter(genres: List<Pair<String, String>>) : CheckBoxGroup(
|
||||||
|
"Genres",
|
||||||
|
"genre[]",
|
||||||
|
genres,
|
||||||
|
)
|
||||||
|
|
||||||
|
class YearFilter : CheckBoxGroup(
|
||||||
|
"Status",
|
||||||
|
"release[]",
|
||||||
|
listOf(
|
||||||
|
Pair("2024", "2024"),
|
||||||
|
Pair("2023", "2023"),
|
||||||
|
Pair("2022", "2022"),
|
||||||
|
Pair("2021", "2021"),
|
||||||
|
Pair("2020", "2020"),
|
||||||
|
Pair("2019", "2019"),
|
||||||
|
Pair("2018", "2018"),
|
||||||
|
Pair("2017", "2017"),
|
||||||
|
Pair("2016", "2016"),
|
||||||
|
Pair("2015", "2015"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
class StatusFilter : CheckBoxGroup(
|
||||||
|
"Status",
|
||||||
|
"status[]",
|
||||||
|
listOf(
|
||||||
|
Pair("Releasing", "on-going"),
|
||||||
|
Pair("Completed", "end"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
class OrderFilter(default: String? = null) : SelectFilter(
|
||||||
|
"Sort by",
|
||||||
|
"sort",
|
||||||
|
listOf(
|
||||||
|
Pair("", ""),
|
||||||
|
Pair("Popular", "most_viewed"),
|
||||||
|
Pair("Latest", "recently_added"),
|
||||||
|
),
|
||||||
|
default,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
val POPULAR = FilterList(OrderFilter("most_viewed"))
|
||||||
|
val LATEST = FilterList(OrderFilter("recently_added"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,174 @@
|
||||||
package eu.kanade.tachiyomi.extension.en.reaperscansunoriginal
|
package eu.kanade.tachiyomi.extension.en.reaperscansunoriginal
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
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 eu.kanade.tachiyomi.util.asJsoup
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
import org.jsoup.nodes.Document
|
||||||
|
import org.jsoup.nodes.Element
|
||||||
|
import java.util.Calendar
|
||||||
|
|
||||||
|
class ReaperScansUnoriginal : ParsedHttpSource() {
|
||||||
|
override val baseUrl = "https://reaper-scans.com"
|
||||||
|
|
||||||
|
override val name = "Reaper Scans (unoriginal)"
|
||||||
|
|
||||||
|
override val lang = "en"
|
||||||
|
|
||||||
|
override val supportsLatest = true
|
||||||
|
|
||||||
class ReaperScansUnoriginal : MangaThemesia(
|
|
||||||
"Reaper Scans (unoriginal)",
|
|
||||||
"https://reaper-scans.com",
|
|
||||||
"en",
|
|
||||||
) {
|
|
||||||
override val client = super.client.newBuilder()
|
override val client = super.client.newBuilder()
|
||||||
.rateLimit(3)
|
.rateLimit(3)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
// Popular
|
||||||
|
override fun popularMangaFromElement(element: Element) = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun popularMangaNextPageSelector(): String = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun popularMangaRequest(page: Int) =
|
||||||
|
searchMangaRequest(page, "", OrderFilter.POPULAR)
|
||||||
|
|
||||||
|
override fun popularMangaSelector() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun popularMangaParse(response: Response) = searchMangaParse(response)
|
||||||
|
|
||||||
|
// Latest
|
||||||
|
override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun latestUpdatesRequest(page: Int) =
|
||||||
|
searchMangaRequest(page, "", OrderFilter.LATEST)
|
||||||
|
|
||||||
|
override fun latestUpdatesSelector() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun latestUpdatesParse(response: Response) = searchMangaParse(response)
|
||||||
|
|
||||||
|
// Search
|
||||||
|
override fun searchMangaFromElement(element: Element) = SManga.create().apply {
|
||||||
|
thumbnail_url = element.select(".poster-image-wrapper > img").attr("src")
|
||||||
|
title = element.select(".info > a").text()
|
||||||
|
setUrlWithoutDomain(element.selectFirst(".info a")!!.attr("href"))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaNextPageSelector() = "a[rel=\"next\"]"
|
||||||
|
|
||||||
|
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
|
||||||
|
val url = baseUrl.toHttpUrl().newBuilder().apply {
|
||||||
|
addQueryParameter("post_type", "wp-manga")
|
||||||
|
addQueryParameter("s", query)
|
||||||
|
filters.filterIsInstance<UrlPartFilter>().forEach {
|
||||||
|
it.addUrlParameter(this)
|
||||||
|
}
|
||||||
|
if (page > 1) {
|
||||||
|
addPathSegment("page")
|
||||||
|
addPathSegment(page.toString())
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
return GET(url, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun searchMangaSelector() = ".inner"
|
||||||
|
|
||||||
|
override fun searchMangaParse(response: Response): MangasPage {
|
||||||
|
if (genres.isEmpty()) {
|
||||||
|
genres = parseGenres(response.asJsoup(response.peekBody(Long.MAX_VALUE).string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.searchMangaParse(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chapter
|
||||||
|
override fun chapterFromElement(element: Element) = SChapter.create().apply {
|
||||||
|
setUrlWithoutDomain(element.attr("href"))
|
||||||
|
name = element.attr("title")
|
||||||
|
date_upload = parseRelativeDate(element.selectFirst("span + span")?.text())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseRelativeDate(date: String?): Long {
|
||||||
|
if (date == null) {
|
||||||
|
return 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
val trimmedDate = date.split(" ")
|
||||||
|
if (trimmedDate.size != 3 && trimmedDate[2] != "ago") return 0L
|
||||||
|
val number = trimmedDate[0].toIntOrNull() ?: return 0L
|
||||||
|
val unit = trimmedDate[1].removeSuffix("s") // Remove 's' suffix
|
||||||
|
val now = Calendar.getInstance()
|
||||||
|
|
||||||
|
val javaUnit = when (unit) {
|
||||||
|
"year", "yr" -> Calendar.YEAR
|
||||||
|
"month" -> Calendar.MONTH
|
||||||
|
"week", "wk" -> Calendar.WEEK_OF_MONTH
|
||||||
|
"day" -> Calendar.DAY_OF_MONTH
|
||||||
|
"hour", "hr" -> Calendar.HOUR
|
||||||
|
"minute", "min" -> Calendar.MINUTE
|
||||||
|
"second", "sec" -> Calendar.SECOND
|
||||||
|
else -> return 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
now.add(javaUnit, -number)
|
||||||
|
|
||||||
|
return now.timeInMillis
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chapterListSelector() = "a.cairo"
|
||||||
|
|
||||||
|
// Details
|
||||||
|
override fun mangaDetailsParse(document: Document) = SManga.create().apply {
|
||||||
|
document.selectFirst("div.serie-info")?.let { info ->
|
||||||
|
description = info.selectFirst("div.description-content")?.text()
|
||||||
|
author = info.selectFirst("span:containsOwn(Author) + span")?.text()
|
||||||
|
artist = info.selectFirst("span:containsOwn(Artist) + span")?.text()
|
||||||
|
status = info.selectFirst("span:containsOwn(Status) + span")?.text().toStatus()
|
||||||
|
genre = info.select("div.genre-link").joinToString { it.text() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String?.toStatus() = when {
|
||||||
|
this == null -> SManga.UNKNOWN
|
||||||
|
this.contains("Ongoing") -> SManga.ONGOING
|
||||||
|
this.contains("Completed") -> SManga.COMPLETED
|
||||||
|
else -> SManga.UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pages
|
||||||
|
override fun pageListParse(document: Document): List<Page> {
|
||||||
|
val chapterUrl = document.location()
|
||||||
|
return document.select("div.image-skeleton img")
|
||||||
|
.filterNot { it.attr("data-src").isEmpty() }
|
||||||
|
.mapIndexed { i, img -> Page(i, chapterUrl, img.attr("data-src")) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun imageUrlParse(document: Document) = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
// Filter
|
||||||
|
override fun getFilterList() = FilterList(
|
||||||
|
TypeFilter(),
|
||||||
|
Filter.Header("Press \"Reset\" to attempt to load genres"),
|
||||||
|
GenreFilter(genres),
|
||||||
|
YearFilter(),
|
||||||
|
StatusFilter(),
|
||||||
|
OrderFilter(),
|
||||||
|
)
|
||||||
|
|
||||||
|
private var genres = emptyList<Pair<String, String>>()
|
||||||
|
|
||||||
|
private fun parseGenres(document: Document): List<Pair<String, String>> {
|
||||||
|
return document.select("li:has(input[name=\"genre[]\"])")
|
||||||
|
.map {
|
||||||
|
Pair(it.selectFirst("label")!!.text(), it.selectFirst("input")!!.attr("value"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue