diff --git a/src/pt/tsukimangas/AndroidManifest.xml b/src/pt/tsukimangas/AndroidManifest.xml
deleted file mode 100644
index 825716396..000000000
--- a/src/pt/tsukimangas/AndroidManifest.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/pt/tsukimangas/build.gradle b/src/pt/tsukimangas/build.gradle
deleted file mode 100644
index d40149110..000000000
--- a/src/pt/tsukimangas/build.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
-ext {
- extName = 'Tsuki Mangás'
- extClass = '.TsukiMangas'
- extVersionCode = 7
- isNsfw = true
-}
-
-apply from: "$rootDir/common.gradle"
diff --git a/src/pt/tsukimangas/res/mipmap-hdpi/ic_launcher.png b/src/pt/tsukimangas/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 8be15d792..000000000
Binary files a/src/pt/tsukimangas/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/tsukimangas/res/mipmap-mdpi/ic_launcher.png b/src/pt/tsukimangas/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index fd4f11242..000000000
Binary files a/src/pt/tsukimangas/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/tsukimangas/res/mipmap-xhdpi/ic_launcher.png b/src/pt/tsukimangas/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 0e72a5571..000000000
Binary files a/src/pt/tsukimangas/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/tsukimangas/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/tsukimangas/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 652fffad6..000000000
Binary files a/src/pt/tsukimangas/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/tsukimangas/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/tsukimangas/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 82085d654..000000000
Binary files a/src/pt/tsukimangas/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/src/pt/tsukimangas/src/eu/kanade/tachiyomi/extension/pt/tsukimangas/TsukiMangas.kt b/src/pt/tsukimangas/src/eu/kanade/tachiyomi/extension/pt/tsukimangas/TsukiMangas.kt
deleted file mode 100644
index 85891bfc0..000000000
--- a/src/pt/tsukimangas/src/eu/kanade/tachiyomi/extension/pt/tsukimangas/TsukiMangas.kt
+++ /dev/null
@@ -1,360 +0,0 @@
-package eu.kanade.tachiyomi.extension.pt.tsukimangas
-
-import android.annotation.SuppressLint
-import android.app.Application
-import android.os.Handler
-import android.os.Looper
-import android.webkit.WebView
-import android.webkit.WebViewClient
-import eu.kanade.tachiyomi.extension.pt.tsukimangas.dto.ChapterListDto
-import eu.kanade.tachiyomi.extension.pt.tsukimangas.dto.CompleteMangaDto
-import eu.kanade.tachiyomi.extension.pt.tsukimangas.dto.MangaListDto
-import eu.kanade.tachiyomi.extension.pt.tsukimangas.dto.PageListDto
-import eu.kanade.tachiyomi.network.GET
-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 eu.kanade.tachiyomi.util.asJsoup
-import kotlinx.serialization.json.Json
-import kotlinx.serialization.json.decodeFromStream
-import okhttp3.HttpUrl
-import okhttp3.HttpUrl.Companion.toHttpUrl
-import okhttp3.Interceptor
-import okhttp3.Request
-import okhttp3.Response
-import rx.Observable
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
-import uy.kohesive.injekt.injectLazy
-import java.text.SimpleDateFormat
-import java.util.Locale
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-
-class TsukiMangas : HttpSource() {
-
- override val name = "Tsuki Mangás"
-
- override val baseUrl = "https://tsuki-mangas.com"
-
- private val apiUrl = baseUrl + API_PATH
-
- override val lang = "pt-BR"
-
- override val supportsLatest = true
-
- @delegate:SuppressLint("SetJavaScriptEnabled")
- private val token: String by lazy {
- val latch = CountDownLatch(1)
- var token = ""
- Handler(Looper.getMainLooper()).post {
- val webView = WebView(Injekt.get())
- with(webView.settings) {
- javaScriptEnabled = true
- domStorageEnabled = true
- databaseEnabled = true
- blockNetworkImage = true
- }
- webView.webViewClient = object : WebViewClient() {
- override fun onPageFinished(view: WebView, url: String) {
- val script = "javascript:localStorage['token']"
- view.evaluateJavascript(script) {
- view.apply {
- stopLoading()
- destroy()
- }
- if (it.isBlank() || it in listOf("null", "undefined")) {
- return@evaluateJavascript
- }
- token = it.replace("[\"]+".toRegex(), "")
- latch.countDown()
- }
- }
- }
- webView.loadUrl(baseUrl)
- }
- latch.await(10, TimeUnit.SECONDS)
-
- token
- }
-
- override val client by lazy {
- network.client.newBuilder()
- .addInterceptor(::apiHeadersInterceptor)
- .addInterceptor(::imageCdnSwapper)
- .rateLimitHost(baseUrl.toHttpUrl(), 2)
- .rateLimitHost(MAIN_CDN.toHttpUrl(), 1)
- .rateLimitHost(SECONDARY_CDN.toHttpUrl(), 1)
- .build()
- }
-
- override fun headersBuilder() = super.headersBuilder()
- .add("Referer", "$baseUrl/")
-
- private val json: Json by injectLazy()
-
- // ============================== Popular ===============================
- override fun popularMangaRequest(page: Int) = GET("$apiUrl/mangas?page=$page&filter=0", headers)
-
- override fun popularMangaParse(response: Response): MangasPage {
- val item = response.parseAs()
- val mangas = item.data.map {
- SManga.create().apply {
- url = "/obra" + it.entryPath
- thumbnail_url = baseUrl + it.imagePath
- title = it.title
- }
- }
- val hasNextPage = item.page < item.lastPage
- return MangasPage(mangas, hasNextPage)
- }
-
- // =============================== Latest ===============================
- // Yes, "lastests". High IQ move.
- // Also yeah, there's a "?format=0" glued to the page number. Without this,
- // the request will blow up with a HTTP 500.
- override fun latestUpdatesRequest(page: Int) = GET("$apiUrl/home/lastests?page=$page%3Fformat%3D0", headers)
-
- override fun latestUpdatesParse(response: Response) = popularMangaParse(response)
-
- // =============================== Search ===============================
- override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable {
- return if (query.startsWith(PREFIX_SEARCH)) { // URL intent handler
- val id = query.removePrefix(PREFIX_SEARCH)
- client.newCall(GET("$apiUrl/mangas/$id", headers))
- .asObservableSuccess()
- .map(::searchMangaByIdParse)
- } else {
- super.fetchSearchManga(page, query, filters)
- }
- }
-
- private fun searchMangaByIdParse(response: Response): MangasPage {
- val details = mangaDetailsParse(response)
- return MangasPage(listOf(details), false)
- }
-
- override fun getFilterList() = TsukiMangasFilters.FILTER_LIST
-
- override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
- val params = TsukiMangasFilters.getSearchParameters(filters)
- val url = "$apiUrl/mangas".toHttpUrl().newBuilder()
- .addQueryParameter("page", page.toString())
- .addQueryParameter("title", query.trim())
- .addIfNotBlank("filter", params.filter)
- .addIfNotBlank("format", params.format)
- .addIfNotBlank("status", params.status)
- .addIfNotBlank("adult_content", params.adult)
- .apply {
- params.genres.forEach { addQueryParameter("genres[]", it) }
- params.tags.forEach { addQueryParameter("tags[]", it) }
- }.build()
-
- return GET(url, headers)
- }
-
- override fun searchMangaParse(response: Response) = popularMangaParse(response)
-
- // =========================== Manga Details ============================
- override fun mangaDetailsRequest(manga: SManga): Request {
- val id = manga.url.getMangaId()
- return GET("$apiUrl/mangas/$id", headers)
- }
-
- override fun getMangaUrl(manga: SManga) = baseUrl + manga.url
-
- override fun mangaDetailsParse(response: Response) = SManga.create().apply {
- val mangaDto = response.parseAs()
- url = "/obra" + mangaDto.entryPath
- thumbnail_url = baseUrl + mangaDto.imagePath
- title = mangaDto.title
- artist = mangaDto.staff
- genre = mangaDto.genres.joinToString { it.genre }
- status = parseStatus(mangaDto.status.orEmpty())
- description = buildString {
- mangaDto.synopsis?.also { append("$it\n\n") }
- if (mangaDto.titles.isNotEmpty()) {
- append("Títulos alternativos: ${mangaDto.titles.joinToString { it.title }}")
- }
- }
- }
-
- private fun parseStatus(status: String) = when (status) {
- "Ativo" -> SManga.ONGOING
- "Completo" -> SManga.COMPLETED
- "Hiato" -> SManga.ON_HIATUS
- else -> SManga.UNKNOWN
- }
-
- // ============================== Chapters ==============================
- override fun chapterListRequest(manga: SManga): Request {
- val split = manga.url.split("/").reversed()
- val slug = split[0]
- val id = split[1]
-
- return GET("$apiUrl/chapters/$id/all#$slug", headers)
- }
-
- override fun chapterListParse(response: Response): List {
- val parsed = response.parseAs()
- val mangaSlug = response.request.url.fragment!!
- val mangaId = response.request.url.pathSegments.reversed()[1]
-
- return parsed.chapters.reversed().map {
- SChapter.create().apply {
- name = "Capítulo ${it.number}"
- // Sometimes the "number" attribute have letters or other characters,
- // which could ruin the automatic chapter number recognition system.
- chapter_number = it.number.trim { char -> !char.isDigit() }.toFloatOrNull() ?: 1F
-
- url = "/leitor/$mangaId/${it.versionId}/$mangaSlug/${it.number}"
-
- date_upload = it.created_at.orEmpty().toDate()
- }
- }
- }
-
- // =============================== Pages ================================
- override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url
-
- override fun pageListRequest(chapter: SChapter): Request {
- val versionId = chapter.url.split("/")[3]
-
- return GET("$apiUrl/chapter/versions/$versionId", headers)
- }
-
- override fun pageListParse(response: Response): List {
- val data = response.parseAs()
- val sortedPages = data.pages.sortedBy { it.url.extractPageNumber() }
- val host = getImageHost(sortedPages.first().url)
-
- return sortedPages.mapIndexed { index, item ->
- Page(index, imageUrl = host + item.url)
- }
- }
-
- /**
- * The source normally uses only one CDN per chapter, so we'll try to get
- * the correct CDN before loading all pages, leaving the [imageCdnSwapper]
- * as the last choice.
- */
- private fun getImageHost(path: String): String {
- val pageCheck = super.client.newCall(GET(MAIN_CDN + path, headers)).execute()
- pageCheck.close()
- return when {
- !pageCheck.isSuccessful -> SECONDARY_CDN
- else -> MAIN_CDN
- }
- }
-
- override fun imageUrlParse(response: Response): String {
- throw UnsupportedOperationException()
- }
-
- // ============================= Utilities ==============================
- private inline fun Response.parseAs(): T = use {
- try {
- json.decodeFromStream(it.body.byteStream())
- } catch (_: Exception) {
- throw Exception(
- """
- Contéudo protegido ou foi removido.
- Faça o login na WebView e tente novamente
- """.trimIndent(),
- )
- }
- }
-
- private fun HttpUrl.Builder.addIfNotBlank(query: String, value: String): HttpUrl.Builder {
- if (value.isNotBlank()) addQueryParameter(query, value)
- return this
- }
-
- private fun String.getMangaId() = substringAfter("/obra/").substringBefore("/")
-
- private fun String.toDate(): Long {
- return runCatching { DATE_FORMATTER.parse(trim())?.time }
- .getOrNull() ?: 0L
- }
-
- private val pageNumberRegex = Regex("""(\d+)\.(png|jpg|jpeg|gif|webp)$""")
-
- private fun String.extractPageNumber() = pageNumberRegex
- .find(substringBefore("?"))
- ?.groupValues
- ?.get(1)
- ?.toInt() ?: 0
-
- /**
- * This may sound stupid (because it is), but a similar approach exists
- * in the source itself, because they somehow don't know to which server
- * each page belongs to. I thought the `server` attribute returned by page
- * objects would be enough, but it turns out that it isn't. Day ruined.
- */
- private fun imageCdnSwapper(chain: Interceptor.Chain): Response {
- val request = chain.request()
- val response = chain.proceed(request)
-
- return if (response.code != 404) {
- response
- } else {
- response.close()
- val url = request.url.toString()
- val newUrl = when {
- url.startsWith(MAIN_CDN) -> url.replace("$MAIN_CDN/tsuki", SECONDARY_CDN)
- url.startsWith(SECONDARY_CDN) -> url.replace(SECONDARY_CDN, "$MAIN_CDN/tsuki")
- else -> url
- }
-
- val newRequest = GET(newUrl, request.headers)
- chain.proceed(newRequest)
- }
- }
-
- private val apiHeadersRegex = Regex("""headers\.common(?:\.([0-9A-Za-z_]+)|\[['"]([0-9A-Za-z-_]+)['"]])\s*=\s*['"]([a-zA-Z0-9_ :;.,\\/?!(){}\[\]@<>=\-+*#$&`|~^%]+)['"]""")
-
- private val apiHeaders by lazy {
- val document = client.newCall(GET(baseUrl, headers)).execute().asJsoup()
- val scriptUrl = document.selectFirst("script[src*=index-]")!!.absUrl("src")
- val script = client.newCall(GET(scriptUrl, headers)).execute().body.string()
- val matches = apiHeadersRegex.findAll(script)
-
- matches.associate {
- (it.groups[1] ?: it.groups[2]!!).value to it.groups[3]!!.value
- }
- }
-
- private fun apiHeadersInterceptor(chain: Interceptor.Chain): Response {
- val request = chain.request()
-
- if (!request.url.encodedPath.startsWith(API_PATH)) {
- return chain.proceed(request)
- }
-
- val newRequest = request.newBuilder().apply {
- apiHeaders.entries.forEach { addHeader(it.key, it.value) }
- if (token.isNotBlank()) {
- addHeader("Authorization", "Bearer $token")
- }
- }.build()
-
- return chain.proceed(newRequest)
- }
-
- companion object {
- const val PREFIX_SEARCH = "id:"
-
- private val DATE_FORMATTER by lazy {
- SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH)
- }
-
- private const val MAIN_CDN = "https://cdn.tsuki-mangas.com/tsuki"
- private const val SECONDARY_CDN = "https://cdn2.tsuki-mangas.com"
- private const val API_PATH = "/api/v3"
- }
-}
diff --git a/src/pt/tsukimangas/src/eu/kanade/tachiyomi/extension/pt/tsukimangas/TsukiMangasFilters.kt b/src/pt/tsukimangas/src/eu/kanade/tachiyomi/extension/pt/tsukimangas/TsukiMangasFilters.kt
deleted file mode 100644
index 77c5c6c5e..000000000
--- a/src/pt/tsukimangas/src/eu/kanade/tachiyomi/extension/pt/tsukimangas/TsukiMangasFilters.kt
+++ /dev/null
@@ -1,475 +0,0 @@
-package eu.kanade.tachiyomi.extension.pt.tsukimangas
-
-import eu.kanade.tachiyomi.source.model.Filter
-import eu.kanade.tachiyomi.source.model.FilterList
-
-object TsukiMangasFilters {
- open class CheckBoxFilterList(name: String, val pairs: Array>) :
- Filter.Group(name, pairs.map { CheckBoxVal(it.first) })
-
- private class CheckBoxVal(name: String) : Filter.CheckBox(name, false)
-
- private inline fun FilterList.parseCheckbox(
- options: Array>,
- ): Sequence {
- return (first { it is R } as CheckBoxFilterList).state
- .asSequence()
- .filter { it.state }
- .map { checkbox -> options.find { it.first == checkbox.name }!!.second }
- }
-
- open class SelectFilter(
- displayName: String,
- val vals: Array>,
- ) : Filter.Select(
- displayName,
- vals.map { it.first }.toTypedArray(),
- ) {
- val selected get() = vals[state].second
- }
-
- private inline fun FilterList.getSelected(): String {
- return (first { it is R } as SelectFilter).selected
- }
-
- internal class GenresFilter : CheckBoxFilterList("Gêneros", GENRES)
- internal class TagsFilter : CheckBoxFilterList("Tags", TAGS)
-
- internal class FormatFilter : SelectFilter("Formato", FORMATS)
- internal class AdultFilter : SelectFilter("Mostrar conteúdo adulto", ADULT_OPTIONS)
- internal class ContentFilter : SelectFilter("Filtro", CONTENT_FILTER)
- internal class StatusFilter : SelectFilter("Status", STATUS)
-
- internal val FILTER_LIST get() = FilterList(
- GenresFilter(),
- TagsFilter(),
-
- FormatFilter(),
- AdultFilter(),
- ContentFilter(),
- StatusFilter(),
- )
-
- internal data class FilterSearchParams(
- val genres: Sequence = emptySequence(),
- val tags: Sequence = emptySequence(),
-
- val format: String = "",
- val adult: String = "",
- val filter: String = "",
- val status: String = "",
- )
-
- internal fun getSearchParameters(filters: FilterList): FilterSearchParams {
- if (filters.isEmpty()) return FilterSearchParams()
-
- return FilterSearchParams(
- filters.parseCheckbox(GENRES),
- filters.parseCheckbox(TAGS),
-
- filters.getSelected(),
- filters.getSelected(),
- filters.getSelected(),
- filters.getSelected(),
- )
- }
-
- private val GENRES = arrayOf(
- "4-Koma",
- "Adaptação",
- "Aliens",
- "Animais",
- "Antologia",
- "Artes Marciais",
- "Aventura",
- "Ação",
- "Colorido por fã",
- "Comédia",
- "Criado pelo Usuário",
- "Crime",
- "Cross-dressing",
- "Deliquentes",
- "Demônios",
- "Doujinshi",
- "Drama",
- "Ecchi",
- "Esportes",
- "Fantasia",
- "Fantasmas",
- "Filosófico",
- "Gals",
- "Ganhador de Prêmio",
- "Garotas Monstro",
- "Garotas Mágicas",
- "Gastronomia",
- "Gore",
- "Harém",
- "Harém Reverso",
- "Hentai",
- "Histórico",
- "Horror",
- "Incesto",
- "Isekai",
- "Jogos Tradicionais",
- "Lolis",
- "Long Strip",
- "Mafia",
- "Magia",
- "Mecha",
- "Medicina",
- "Militar",
- "Mistério",
- "Monstros",
- "Música",
- "Ninjas",
- "Obscenidade",
- "Oficialmente Colorido",
- "One-shot",
- "Policial",
- "Psicológico",
- "Pós-apocalíptico",
- "Realidade Virtual",
- "Reencarnação",
- "Romance",
- "Samurais",
- "Sci-Fi",
- "Shotas",
- "Shoujo Ai",
- "Shounen Ai",
- "Slice of Life",
- "Sobrenatural",
- "Sobrevivência",
- "Super Herói",
- "Thriller",
- "Todo Colorido",
- "Trabalho de Escritório",
- "Tragédia",
- "Troca de Gênero",
- "Vampiros",
- "Viagem no Tempo",
- "Vida Escolar",
- "Violência Sexual",
- "Vídeo Games",
- "Webcomic",
- "Wuxia",
- "Yaoi",
- "Yuri",
- "Zumbis",
- ).map { Pair(it, it) }.toTypedArray()
-
- private val TAGS = arrayOf(
- Pair("4-Koma", "4-Koma"),
- Pair("Acromático", "Achromatic"),
- Pair("Adoção", "Adoption"),
- Pair("Agricultura", "Agriculture"),
- Pair("Airsoft", "Airsoft"),
- Pair("Alienígenas", "Aliens"),
- Pair("Alquimia", "Alchemy"),
- Pair("Amadurecimento", "Coming Of Age"),
- Pair("Ambiental", "Environmental"),
- Pair("Amnésia", "Amnesia"),
- Pair("Amor entre Adolescentes", "Teens' Love"),
- Pair("Amor entre Homens", "Boys' Love"),
- Pair("Anacronismo", "Anachronism"),
- Pair("Animais", "Animals"),
- Pair("Anjos", "Angels"),
- Pair("Anti-Herói", "Anti-Hero"),
- Pair("Antologia", "Anthology"),
- Pair("Antropomorfismo", "Anthropomorphism"),
- Pair("Anúncio Publicitário", "Advertisement"),
- Pair("Ao Ar Livre", "Outdoor"),
- Pair("Arco e Flecha", "Archery"),
- Pair("Armas", "Guns"),
- Pair("Artes Marciais", "Martial Arts"),
- Pair("Assassinos", "Assassins"),
- Pair("Assexual", "Asexual"),
- Pair("Astronomia", "Astronomy"),
- Pair("Atletismo", "Athletics"),
- Pair("Atuação", "Acting"),
- Pair("Autobiográfico", "Autobiographical"),
- Pair("Aviação", "Aviation"),
- Pair("Badminton", "Badminton"),
- Pair("Banda", "Band"),
- Pair("Bar", "Bar"),
- Pair("Barreira de Idioma Estrangeiro", "Foreign Language Barrier"),
- Pair("Basquete", "Basketball"),
- Pair("Batalha Real", "Battle Royale"),
- Pair("Batalha de Cartas", "Card Battle"),
- Pair("Beisebol", "Baseball"),
- Pair("Biográfico", "Biographical"),
- Pair("Bissexual", "Bisexual"),
- Pair("Bombeiros", "Firefighters"),
- Pair("Boxe", "Boxing"),
- Pair("Bruxa", "Witch"),
- Pair("Bullying", "Bullying"),
- Pair("CGI Completo", "Full CGI"),
- Pair("CGI", "CGI"),
- Pair("Caligrafia", "Calligraphy"),
- Pair("Canibalismo", "Cannibalism"),
- Pair("Carros", "Cars"),
- Pair("Casamento", "Marriage"),
- Pair("Centauro", "Centaur"),
- Pair("Chibi", "Chibi"),
- Pair("Chuunibyou", "Chuunibyou"),
- Pair("Ciborgue", "Cyborg"),
- Pair("Ciclismo", "Cycling"),
- Pair("Ciclomotores", "Mopeds"),
- Pair("Circo", "Circus"),
- Pair("Civilização Perdida", "Lost Civilization"),
- Pair("Clone", "Clone"),
- Pair("Clube Escolar", "School Club"),
- Pair("Comida", "Food"),
- Pair("Comédia Surrealista", "Surreal Comedy"),
- Pair("Conspiração", "Conspiracy"),
- Pair("Conto de Fadas", "Fairy Tale"),
- Pair("Cor Completa", "Full Color"),
- Pair("Cosplay", "Cosplay"),
- Pair("Crime", "Crime"),
- Pair("Crossover", "Crossover"),
- Pair("Cultivo", "Cultivation"),
- Pair("Culto", "Cult"),
- Pair("Cultura Otaku", "Otaku Culture"),
- Pair("Cyberpunk", "Cyberpunk"),
- Pair("Dança", "Dancing"),
- Pair("Deficiência", "Disability"),
- Pair("Delinquentes", "Delinquents"),
- Pair("Demônios", "Demons"),
- Pair("Denpa", "Denpa"),
- Pair("Desenho", "Drawing"),
- Pair("Desenvolvimento de Software", "Software Development"),
- Pair("Deserto", "Desert"),
- Pair("Detetive", "Detective"),
- Pair("Deuses", "Gods"),
- Pair("Diferença de Idade", "Age Gap"),
- Pair("Dinossauros", "Dinosaurs"),
- Pair("Distópico", "Dystopian"),
- Pair("Donzela do Santuário", "Shrine Maiden"),
- Pair("Dragões", "Dragons"),
- Pair("Drogas", "Drugs"),
- Pair("Dullahan", "Dullahan"),
- Pair("E-Sports", "E-Sports"),
- Pair("Economia", "Economics"),
- Pair("Educacional", "Educational"),
- Pair("Elenco Conjunto", "Ensemble Cast"),
- Pair("Elenco Principalmente Adolescente", "Primarily Teen Cast"),
- Pair("Elenco Principalmente Adulto", "Primarily Adult Cast"),
- Pair("Elenco Principalmente Feminino", "Primarily Female Cast"),
- Pair("Elenco Principalmente Infantil", "Primarily Child Cast"),
- Pair("Elenco Principalmente Masculino", "Primarily Male Cast"),
- Pair("Elfo", "Elf"),
- Pair("Empregadas", "Maids"),
- Pair("Episódico", "Episodic"),
- Pair("Ero Guro", "Ero Guro"),
- Pair("Escola", "School"),
- Pair("Escravidão", "Slavery"),
- Pair("Escrita", "Writing"),
- Pair("Esgrima", "Fencing"),
- Pair("Espaço", "Space"),
- Pair("Espionagem", "Espionage"),
- Pair("Esqueleto", "Skeleton"),
- Pair("Faculdade", "College"),
- Pair("Fada", "Fairy"),
- Pair("Família Encontrada", "Found Family"),
- Pair("Fantasia Urbana", "Urban Fantasy"),
- Pair("Fantasma", "Ghost"),
- Pair("Filosofia", "Philosophy"),
- Pair("Fitness", "Fitness"),
- Pair("Flash", "Flash"),
- Pair("Fotografia", "Photography"),
- Pair("Freira", "Nun"),
- Pair("Fugitivo", "Fugitive"),
- Pair("Futebol Americano", "American Football"),
- Pair("Futebol", "Football"),
- Pair("Gangues", "Gangs"),
- Pair("Garota Monstro", "Monster Girl"),
- Pair("Garotas Bonitinhas Fazendo Coisas Bonitinhas", "Cute Girls Doing Cute Things"),
- Pair("Garoto Feminino", "Femboy"),
- Pair("Garoto Monstro", "Monster Boy"),
- Pair("Garotos Bonitinhos Fazendo Coisas Bonitinhas", "Cute Boys Doing Cute Things"),
- Pair("Go", "Go"),
- Pair("Goblin", "Goblin"),
- Pair("Golfe", "Golf"),
- Pair("Gore", "Gore"),
- Pair("Guerra", "War"),
- Pair("Gyaru", "Gyaru"),
- Pair("Gêmeos", "Twins"),
- Pair("Handebol", "Handball"),
- Pair("Harém Feminino", "Female Harem"),
- Pair("Harém Masculino", "Male Harem"),
- Pair("Harém com Gêneros Mistos", "Mixed Gender Harem"),
- Pair("Henshin", "Henshin"),
- Pair("Heterossexual", "Heterosexual"),
- Pair("Hikikomori", "Hikikomori"),
- Pair("Histórico", "Historical"),
- Pair("Horror Corporal", "Body Horror"),
- Pair("Horror Cósmico", "Cosmic Horror"),
- Pair("Identidades Dissociativas", "Dissociative Identities"),
- Pair("Inteligência Artificial", "Artificial Intelligence"),
- Pair("Isekai", "Isekai"),
- Pair("Iyashikei", "Iyashikei"),
- Pair("Jogo da Morte", "Death Game"),
- Pair("Jogos Eletrônicos", "Video Games"),
- Pair("Jogos de Azar", "Gambling"),
- Pair("Judô", "Judo"),
- Pair("Kaiju", "Kaiju"),
- Pair("Karuta", "Karuta"),
- Pair("Kemonomimi", "Kemonomimi"),
- Pair("Kuudere", "Kuudere"),
- Pair("Lacrosse", "Lacrosse"),
- Pair("Literatura Clássica", "Classic Literature"),
- Pair("Lobisomem", "Werewolf"),
- Pair("Luta Livre", "Wrestling"),
- Pair("Luta com Espada", "Swordplay"),
- Pair("Luta com Lança", "Spearplay"),
- Pair("Líder de Torcida", "Cheerleading"),
- Pair("Magia", "Magic"),
- Pair("Mahjong", "Mahjong"),
- Pair("Manipulação de Memória", "Memory Manipulation"),
- Pair("Manipulação do Tempo", "Time Manipulation"),
- Pair("Maquiagem", "Makeup"),
- Pair("Maria-rapaz", "Tomboy"),
- Pair("Masmorra", "Dungeon"),
- Pair("Medicina", "Medicine"),
- Pair("Mergulho", "Scuba Diving"),
- Pair("Meta", "Meta"),
- Pair("Militar", "Military"),
- Pair("Mitologia", "Mythology"),
- Pair("Moda", "Fashion"),
- Pair("Mordomo", "Butler"),
- Pair("Motocicletas", "Motorcycles"),
- Pair("Mudança de Forma", "Shapeshifting"),
- Pair("Mulher de Escritório", "Office Lady"),
- Pair("Mundo Virtual", "Virtual World"),
- Pair("Musical", "Musical"),
- Pair("Máfia", "Mafia"),
- Pair("Natação", "Swimming"),
- Pair("Navios", "Ships"),
- Pair("Necromancia", "Necromancy"),
- Pair("Nekomimi", "Nekomimi"),
- Pair("Ninja", "Ninja"),
- Pair("Noir", "Noir"),
- Pair("Nudez", "Nudity"),
- Pair("Não Ficção", "Non-Fiction"),
- Pair("Oiran", "Oiran"),
- Pair("Ojou-Sama", "Ojou-Sama"),
- Pair("Ordem Acrônica", "Achronological Order"),
- Pair("Pandemia", "Pandemic"),
- Pair("Parkour", "Parkour"),
- Pair("Paródia", "Parody"),
- Pair("Patinagem no Gelo", "Ice Skating"),
- Pair("Pele Bronzeada", "Tanned Skin"),
- Pair("Pesca", "Fishing"),
- Pair("Piratas", "Pirates"),
- Pair("Polícia", "Police"),
- Pair("Política", "Politics"),
- Pair("Ponto de Vista", "POV"),
- Pair("Prisão", "Prison"),
- Pair("Professor(a)", "Teacher"),
- Pair("Protagonista Feminina", "Female Protagonist"),
- Pair("Protagonista Masculino", "Male Protagonist"),
- Pair("Pular no Tempo", "Time Skip"),
- Pair("Puppetry", "Puppetry"),
- Pair("Pós-Apocalíptico", "Post-Apocalyptic"),
- Pair("Pós-Vida", "Afterlife"),
- Pair("Pôquer", "Poker"),
- Pair("Quimera", "Chimera"),
- Pair("Rakugo", "Rakugo"),
- Pair("Reabilitação", "Rehabilitation"),
- Pair("Realidade Aumentada", "Augmented Reality"),
- Pair("Reencarnação", "Reincarnation"),
- Pair("Regressão de Idade", "Age Regression"),
- Pair("Religião", "Religion"),
- Pair("Robô Real", "Real Robot"),
- Pair("Robôs", "Robots"),
- Pair("Rotoscopia", "Rotoscoping"),
- Pair("Rugby", "Rugby"),
- Pair("Rural", "Rural"),
- Pair("Samurai", "Samurai"),
- Pair("Sem Diálogo", "No Dialogue"),
- Pair("Sem Gênero", "Agender"),
- Pair("Sem-teto", "Homeless"),
- Pair("Sereia", "Mermaid"),
- Pair("Shogi", "Shogi"),
- Pair("Skateboarding", "Skateboarding"),
- Pair("Slapstick", "Slapstick"),
- Pair("Sobrevivência", "Survival"),
- Pair("Steampunk", "Steampunk"),
- Pair("Stop Motion", "Stop Motion"),
- Pair("Suicídio", "Suicide"),
- Pair("Sumô", "Sumo"),
- Pair("Super Robô", "Super Robot"),
- Pair("Super-herói", "Superhero"),
- Pair("Superpoder", "Super Power"),
- Pair("Surf", "Surfing"),
- Pair("Sátira", "Satire"),
- Pair("Súcubo", "Succubus"),
- Pair("Tanques", "Tanks"),
- Pair("Temas LGBTQ+", "LGBTQ+ Themes"),
- Pair("Terrorismo", "Terrorism"),
- Pair("Tokusatsu", "Tokusatsu"),
- Pair("Tortura", "Torture"),
- Pair("Trabalho", "Work"),
- Pair("Tragédia", "Tragedy"),
- Pair("Transgênero", "Transgender"),
- Pair("Travestismo", "Crossdressing"),
- Pair("Trens", "Trains"),
- Pair("Triângulo Amoroso", "Love Triangle"),
- Pair("Troca de Corpos", "Body Swapping"),
- Pair("Troca de Gênero", "Gender Bending"),
- Pair("Tríades", "Triads"),
- Pair("Tsundere", "Tsundere"),
- Pair("Tênis de Mesa", "Table Tennis"),
- Pair("Tênis", "Tennis"),
- Pair("Universo Alternativo", "Alternate Universe"),
- Pair("Urbano", "Urban"),
- Pair("VTuber", "VTuber"),
- Pair("Vampiro", "Vampire"),
- Pair("Viagem", "Travel"),
- Pair("Vida Familiar", "Family Life"),
- Pair("Vikings", "Vikings"),
- Pair("Vilã", "Villainess"),
- Pair("Vingança", "Revenge"),
- Pair("Vôlei", "Volleyball"),
- Pair("Wuxia", "Wuxia"),
- Pair("Yakuza", "Yakuza"),
- Pair("Yandere", "Yandere"),
- Pair("Youkai", "Youkai"),
- Pair("Yuri", "Yuri"),
- Pair("Zumbi", "Zombie"),
- Pair("Ídolo", "Idol"),
- Pair("Ópera Espacial", "Space Opera"),
- Pair("Órfão/Órfã", "Orphan"),
- )
-
- private val ANY = Pair("Qualquer um", "")
-
- private val FORMATS = arrayOf(
- ANY,
- Pair("Mangá", "1"),
- Pair("Manhwa", "2"),
- Pair("Manhua", "3"),
- Pair("Novel", "4"),
- )
-
- private val ADULT_OPTIONS = arrayOf(
- ANY,
- Pair("Sim", "1"),
- Pair("Não", "0"),
- )
-
- private val CONTENT_FILTER = arrayOf(
- ANY,
- Pair("Mais popular", "0"),
- Pair("Menos popular", "1"),
- Pair("Melhores notas", "2"),
- Pair("Piores notas", "3"),
- )
-
- private val STATUS = arrayOf(
- ANY,
- Pair("Ativo", "0"),
- Pair("Completo", "1"),
- Pair("Cancelado", "2"),
- Pair("Hiato", "3"),
- )
-}
diff --git a/src/pt/tsukimangas/src/eu/kanade/tachiyomi/extension/pt/tsukimangas/TsukiMangasUrlActivity.kt b/src/pt/tsukimangas/src/eu/kanade/tachiyomi/extension/pt/tsukimangas/TsukiMangasUrlActivity.kt
deleted file mode 100644
index 1722468b1..000000000
--- a/src/pt/tsukimangas/src/eu/kanade/tachiyomi/extension/pt/tsukimangas/TsukiMangasUrlActivity.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-package eu.kanade.tachiyomi.extension.pt.tsukimangas
-
-import android.app.Activity
-import android.content.ActivityNotFoundException
-import android.content.Intent
-import android.os.Bundle
-import android.util.Log
-import kotlin.system.exitProcess
-
-/**
- * Springboard that accepts https://tsuki-mangas.com/obra//- intents
- * and redirects them to the main Tachiyomi process.
- */
-class TsukiMangasUrlActivity : Activity() {
-
- private val tag = javaClass.simpleName
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val pathSegments = intent?.data?.pathSegments
- if (pathSegments != null && pathSegments.size > 1) {
- val id = pathSegments[1]
- val mainIntent = Intent().apply {
- action = "eu.kanade.tachiyomi.SEARCH"
- putExtra("query", "${TsukiMangas.PREFIX_SEARCH}$id")
- putExtra("filter", packageName)
- }
-
- try {
- startActivity(mainIntent)
- } catch (e: ActivityNotFoundException) {
- Log.e(tag, e.toString())
- }
- } else {
- Log.e(tag, "could not parse uri from intent $intent")
- }
-
- finish()
- exitProcess(0)
- }
-}
diff --git a/src/pt/tsukimangas/src/eu/kanade/tachiyomi/extension/pt/tsukimangas/dto/TsukiMangasDto.kt b/src/pt/tsukimangas/src/eu/kanade/tachiyomi/extension/pt/tsukimangas/dto/TsukiMangasDto.kt
deleted file mode 100644
index 7207a0150..000000000
--- a/src/pt/tsukimangas/src/eu/kanade/tachiyomi/extension/pt/tsukimangas/dto/TsukiMangasDto.kt
+++ /dev/null
@@ -1,70 +0,0 @@
-package eu.kanade.tachiyomi.extension.pt.tsukimangas.dto
-
-import kotlinx.serialization.SerialName
-import kotlinx.serialization.Serializable
-
-@Serializable
-data class MangaListDto(
- val data: List,
- val page: Int,
- val lastPage: Int,
-)
-
-@Serializable
-data class SimpleMangaDto(
- val id: Int,
- @SerialName("url") val slug: String,
- val title: String,
- val poster: String? = null,
- val cover: String? = null,
-) {
- val imagePath = "/img/imgs/${poster ?: cover ?: "nobackground.jpg"}"
- val entryPath = "/$id/$slug"
-}
-
-@Serializable
-data class CompleteMangaDto(
- val id: Int,
- @SerialName("url") val slug: String,
-
- val title: String,
- val poster: String? = null,
- val cover: String? = null,
- val status: String? = null,
- val synopsis: String? = null,
- val staff: String? = null,
- val genres: List = emptyList(),
- val titles: List = emptyList(),
-) {
- val entryPath = "/$id/$slug"
-
- val imagePath = "/img/imgs/${poster ?: cover ?: "nobackground.jpg"}"
-
- @Serializable
- data class Genre(val genre: String)
-
- @Serializable
- data class Title(val title: String)
-}
-
-@Serializable
-data class ChapterListDto(val chapters: List)
-
-@Serializable
-data class ChapterDto(
- val number: String,
- val title: String? = null,
- val created_at: String? = null,
- private val versions: List,
-) {
- @Serializable
- data class Version(val id: Int)
-
- val versionId = versions.first().id
-}
-
-@Serializable
-data class PageListDto(val pages: List)
-
-@Serializable
-data class PageDto(val url: String)