diff --git a/src/pt/reaperscans/AndroidManifest.xml b/src/pt/reaperscans/AndroidManifest.xml
deleted file mode 100644
index 30deb7f79..000000000
--- a/src/pt/reaperscans/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/src/pt/reaperscans/build.gradle b/src/pt/reaperscans/build.gradle
deleted file mode 100644
index 549ba0ab6..000000000
--- a/src/pt/reaperscans/build.gradle
+++ /dev/null
@@ -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"
diff --git a/src/pt/reaperscans/res/mipmap-hdpi/ic_launcher.png b/src/pt/reaperscans/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index f6c5fc5a7..000000000
Binary files a/src/pt/reaperscans/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/reaperscans/res/mipmap-mdpi/ic_launcher.png b/src/pt/reaperscans/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index e7ffd65f3..000000000
Binary files a/src/pt/reaperscans/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/reaperscans/res/mipmap-xhdpi/ic_launcher.png b/src/pt/reaperscans/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index d79fd54e9..000000000
Binary files a/src/pt/reaperscans/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/reaperscans/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/reaperscans/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 779d9aea3..000000000
Binary files a/src/pt/reaperscans/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/reaperscans/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/reaperscans/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 08a0864d9..000000000
Binary files a/src/pt/reaperscans/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/reaperscans/res/web_hi_res_512.png b/src/pt/reaperscans/res/web_hi_res_512.png
deleted file mode 100644
index 1c6f8c99c..000000000
Binary files a/src/pt/reaperscans/res/web_hi_res_512.png and /dev/null differ
diff --git a/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScans.kt b/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScans.kt
deleted file mode 100644
index cd91ca0a9..000000000
--- a/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScans.kt
+++ /dev/null
@@ -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>()
- .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()
-
- val payloadObj = ReaperSearchDto(
- order = if (sortByFilter?.state?.ascending == true) "asc" else "desc",
- orderBy = sortByFilter?.selected ?: "total_views",
- status = filters.firstInstanceOrNull()?.selected?.value ?: "Ongoing",
- type = "Comic",
- tagIds = filters.firstInstanceOrNull()?.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>()
- .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 {
- 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().toSManga().apply {
- status = response.request.url.fragment?.toIntOrNull() ?: SManga.UNKNOWN
- }
- }
-
- override fun chapterListRequest(manga: SManga): Request = seriesDetailsRequest(manga)
-
- override fun chapterListParse(response: Response): List {
- val result = response.parseAs()
- 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 {
- return response.parseAs().content?.images.orEmpty()
- .mapIndexed { i, url -> Page(i, "", "$API_URL/$url") }
- }
-
- override fun fetchImageUrl(page: Page): Observable = 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 = listOf(
- Status("Em andamento", "Ongoing"),
- Status("Em hiato", "Hiatus"),
- Status("Cancelado", "Dropped"),
- )
-
- private fun getSortProperties(): List = listOf(
- SortProperty("Título", "title"),
- SortProperty("Visualizações", "total_views"),
- SortProperty("Data de criação", "latest")
- )
-
- private fun getGenreList(): List = 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 Response.parseAs(): T = use {
- json.decodeFromString(it.body?.string().orEmpty())
- }
-
- private inline fun List<*>.firstInstanceOrNull(): R? =
- filterIsInstance().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()
- }
-}
diff --git a/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansDto.kt b/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansDto.kt
deleted file mode 100644
index 2b50a30a2..000000000
--- a/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansDto.kt
+++ /dev/null
@@ -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? = emptyList(),
- val chapters: List? = 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? = 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 = emptyList()
-)
diff --git a/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansFilters.kt b/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansFilters.kt
deleted file mode 100644
index 5a4c451c6..000000000
--- a/src/pt/reaperscans/src/eu/kanade/tachiyomi/extension/pt/reaperscans/ReaperScansFilters.kt
+++ /dev/null
@@ -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) : Filter.Group("Gêneros", genres)
-
-open class EnhancedSelect(name: String, values: Array) : Filter.Select(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) : EnhancedSelect(
- "Status",
- statuses.toTypedArray()
-)
-
-data class SortProperty(val name: String, val value: String) {
- override fun toString(): String = name
-}
-
-class SortByFilter(private val sortProperties: List) : Filter.Sort(
- "Ordenar por",
- sortProperties.map { it.name }.toTypedArray(),
- Selection(1, ascending = false)
-) {
- val selected: String
- get() = sortProperties[state!!.index].value
-}