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<ChapterDto>) - -@Serializable -data class ChapterDto( - val number: String, - val title: String? = null, - val created_at: String? = null, - private val versions: List<Version>, -) { - @Serializable - data class Version(val id: Int) - - val versionId = versions.first().id -} - -@Serializable -data class PageListDto(val pages: List<PageDto>) - -@Serializable -data class PageDto(val url: String)