Remove old single RS extension. (#13714)

This commit is contained in:
Alessandro Jean 2022-10-04 15:53:26 -03:00 committed by GitHub
parent 2debfe8a43
commit 05e2858ca7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 0 additions and 398 deletions

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="eu.kanade.tachiyomi.extension" />

View File

@ -1,12 +0,0 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
ext {
extName = 'Reaper Scans'
pkgNameSuffix = 'pt.reaperscans'
extClass = '.ReaperScans'
extVersionCode = 33
}
apply from: "$rootDir/common.gradle"

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.6 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: 60 KiB

View File

@ -1,258 +0,0 @@
package eu.kanade.tachiyomi.extension.pt.reaperscans
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
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 kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.injectLazy
class ReaperScans : HttpSource() {
override val name = "Reaper Scans"
override val baseUrl = "https://reaperscans.com.br"
override val lang = "pt-BR"
override val supportsLatest = true
// Migrated from Madara to a custom CMS.
override val versionId = 2
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.rateLimitHost(API_URL.toHttpUrl(), 1, 2)
.build()
private val json: Json by injectLazy()
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("Origin", baseUrl)
.add("Referer", "$baseUrl/")
override fun popularMangaRequest(page: Int): Request {
val payloadObj = ReaperSearchDto(
order = "desc",
orderBy = "total_views",
status = "Ongoing",
type = "Comic"
)
val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE)
val apiHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON)
.add("Content-Type", payload.contentType().toString())
.build()
return POST("$API_URL/series/querysearch", apiHeaders, payload)
}
override fun popularMangaParse(response: Response): MangasPage {
val mangaList = response.parseAs<List<ReaperSeriesDto>>()
.map(ReaperSeriesDto::toSManga)
return MangasPage(mangaList, hasNextPage = false)
}
override fun latestUpdatesRequest(page: Int): Request {
val payloadObj = ReaperSearchDto(
order = "desc",
orderBy = "latest",
status = "Ongoing",
type = "Comic"
)
val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE)
val apiHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON)
.add("Content-Type", payload.contentType().toString())
.build()
return POST("$API_URL/series/querysearch", apiHeaders, payload)
}
override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response)
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
val sortByFilter = filters.firstInstanceOrNull<SortByFilter>()
val payloadObj = ReaperSearchDto(
order = if (sortByFilter?.state?.ascending == true) "asc" else "desc",
orderBy = sortByFilter?.selected ?: "total_views",
status = filters.firstInstanceOrNull<StatusFilter>()?.selected?.value ?: "Ongoing",
type = "Comic",
tagIds = filters.firstInstanceOrNull<GenreFilter>()?.state
?.filter(Genre::state)
?.map(Genre::id)
.orEmpty()
)
val payload = json.encodeToString(payloadObj).toRequestBody(JSON_MEDIA_TYPE)
val apiHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON)
.add("Content-Type", payload.contentType().toString())
.build()
val apiUrl = "$API_URL/series/querysearch".toHttpUrl().newBuilder()
.addQueryParameter("q", query)
.toString()
return POST(apiUrl, apiHeaders, payload)
}
override fun searchMangaParse(response: Response): MangasPage {
val query = response.request.url.queryParameter("q").orEmpty()
var mangaList = response.parseAs<List<ReaperSeriesDto>>()
.map(ReaperSeriesDto::toSManga)
if (query.isNotBlank()) {
mangaList = mangaList.filter { it.title.contains(query, ignoreCase = true) }
}
return MangasPage(mangaList, hasNextPage = false)
}
// Workaround to allow "Open in browser" use the real URL.
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return client.newCall(seriesDetailsRequest(manga))
.asObservableSuccess()
.map { response ->
mangaDetailsParse(response).apply { initialized = true }
}
}
private fun seriesDetailsRequest(manga: SManga): Request {
val seriesSlug = manga.url.substringAfterLast("/")
val apiHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON)
.build()
return GET("$API_URL/series/$seriesSlug#${manga.status}", apiHeaders)
}
override fun mangaDetailsParse(response: Response): SManga {
return response.parseAs<ReaperSeriesDto>().toSManga().apply {
status = response.request.url.fragment?.toIntOrNull() ?: SManga.UNKNOWN
}
}
override fun chapterListRequest(manga: SManga): Request = seriesDetailsRequest(manga)
override fun chapterListParse(response: Response): List<SChapter> {
val result = response.parseAs<ReaperSeriesDto>()
val seriesSlug = response.request.url.pathSegments.last()
return result.chapters.orEmpty()
.map { it.toSChapter(seriesSlug) }
.reversed()
}
override fun pageListRequest(chapter: SChapter): Request {
val chapterId = chapter.url.substringAfterLast("#")
val apiHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON)
.build()
return GET("$API_URL/series/chapter/$chapterId", apiHeaders)
}
override fun pageListParse(response: Response): List<Page> {
return response.parseAs<ReaperReaderDto>().content?.images.orEmpty()
.mapIndexed { i, url -> Page(i, "", "$API_URL/$url") }
}
override fun fetchImageUrl(page: Page): Observable<String> = Observable.just(page.imageUrl!!)
override fun imageUrlParse(response: Response): String = ""
override fun imageRequest(page: Page): Request {
val imageHeaders = headersBuilder()
.add("Accept", ACCEPT_IMAGE)
.build()
return GET(page.imageUrl!!, imageHeaders)
}
private fun getStatusList(): List<Status> = listOf(
Status("Em andamento", "Ongoing"),
Status("Em hiato", "Hiatus"),
Status("Cancelado", "Dropped"),
)
private fun getSortProperties(): List<SortProperty> = listOf(
SortProperty("Título", "title"),
SortProperty("Visualizações", "total_views"),
SortProperty("Data de criação", "latest")
)
private fun getGenreList(): List<Genre> = listOf(
Genre("Artes Marciais", 2),
Genre("Aventura", 10),
Genre("Ação", 9),
Genre("Comédia", 14),
Genre("Drama", 15),
Genre("Escolar", 7),
Genre("Fantasia", 11),
Genre("Ficção científica", 16),
Genre("Guerra", 17),
Genre("Isekai", 18),
Genre("Jogo", 12),
Genre("Mangá", 24),
Genre("Manhua", 23),
Genre("Manhwa", 22),
Genre("Mecha", 19),
Genre("Mistério", 20),
Genre("Nacional", 8),
Genre("Realidade Virtual", 21),
Genre("Retorno", 3),
Genre("Romance", 5),
Genre("Segunda vida", 4),
Genre("Seinen", 1),
Genre("Shounen", 13),
Genre("Terror", 6)
)
override fun getFilterList(): FilterList = FilterList(
StatusFilter(getStatusList()),
SortByFilter(getSortProperties()),
GenreFilter(getGenreList())
)
private inline fun <reified T> Response.parseAs(): T = use {
json.decodeFromString(it.body?.string().orEmpty())
}
private inline fun <reified R> List<*>.firstInstanceOrNull(): R? =
filterIsInstance<R>().firstOrNull()
companion object {
private const val ACCEPT_IMAGE = "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8"
private const val ACCEPT_JSON = "application/json, text/plain, */*"
const val API_URL = "https://api.reaperscans.com.br"
private val JSON_MEDIA_TYPE = "application/json".toMediaType()
}
}

View File

@ -1,92 +0,0 @@
package eu.kanade.tachiyomi.extension.pt.reaperscans
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.jsoup.Jsoup
import java.text.SimpleDateFormat
import java.util.Locale
@Serializable
data class ReaperSeriesDto(
val id: Int,
@SerialName("series_slug") val slug: String,
val author: String? = null,
val description: String? = null,
val studio: String? = null,
val status: String? = null,
val thumbnail: String,
val title: String,
val tags: List<ReaperTagDto>? = emptyList(),
val chapters: List<ReaperChapterDto>? = emptyList()
) {
fun toSManga(): SManga = SManga.create().apply {
val descriptionBody = this@ReaperSeriesDto.description?.let(Jsoup::parseBodyFragment)
title = this@ReaperSeriesDto.title
author = this@ReaperSeriesDto.author?.trim()
artist = this@ReaperSeriesDto.studio?.trim()
description = descriptionBody?.select("p")
?.joinToString("\n\n") { it.text() }
?.ifEmpty { descriptionBody.text().replace("\n", "\n\n") }
genre = tags.orEmpty()
.sortedBy(ReaperTagDto::name)
.joinToString { it.name }
thumbnail_url = "${ReaperScans.API_URL}/cover/$thumbnail"
status = when (this@ReaperSeriesDto.status) {
"Ongoing" -> SManga.ONGOING
"Hiatus" -> SManga.ON_HIATUS
"Dropped" -> SManga.CANCELLED
"Completed", "Finished" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
url = "/series/$slug"
}
}
@Serializable
data class ReaperTagDto(val name: String)
@Serializable
data class ReaperChapterDto(
val id: Int,
@SerialName("chapter_name") val name: String,
@SerialName("chapter_slug") val slug: String,
val index: String,
@SerialName("created_at") val createdAt: String,
) {
fun toSChapter(seriesSlug: String): SChapter = SChapter.create().apply {
name = this@ReaperChapterDto.name.trim()
date_upload = runCatching { DATE_FORMAT.parse(createdAt.substringBefore("."))?.time }
.getOrNull() ?: 0L
url = "/series/$seriesSlug/$slug#$id"
}
companion object {
private val DATE_FORMAT by lazy {
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale("pt", "BR"))
}
}
}
@Serializable
data class ReaperReaderDto(
val content: ReaperReaderContentDto? = null
)
@Serializable
data class ReaperReaderContentDto(
val images: List<String>? = emptyList()
)
@Serializable
data class ReaperSearchDto(
val order: String,
@SerialName("order_by") val orderBy: String,
@SerialName("series_status") val status: String,
@SerialName("series_type") val type: String,
@SerialName("tags_ids") val tagIds: List<Int> = emptyList()
)

View File

@ -1,34 +0,0 @@
package eu.kanade.tachiyomi.extension.pt.reaperscans
import eu.kanade.tachiyomi.source.model.Filter
class Genre(title: String, val id: Int) : Filter.CheckBox(title)
class GenreFilter(genres: List<Genre>) : Filter.Group<Genre>("Gêneros", genres)
open class EnhancedSelect<T>(name: String, values: Array<T>) : Filter.Select<T>(name, values) {
val selected: T
get() = values[state]
}
data class Status(val name: String, val value: String) {
override fun toString(): String = name
}
class StatusFilter(statuses: List<Status>) : EnhancedSelect<Status>(
"Status",
statuses.toTypedArray()
)
data class SortProperty(val name: String, val value: String) {
override fun toString(): String = name
}
class SortByFilter(private val sortProperties: List<SortProperty>) : Filter.Sort(
"Ordenar por",
sortProperties.map { it.name }.toTypedArray(),
Selection(1, ascending = false)
) {
val selected: String
get() = sortProperties[state!!.index].value
}