diff --git a/src/pt/spectralscan/build.gradle b/src/pt/spectralscan/build.gradle index 2b1f16de4..8ca137a59 100644 --- a/src/pt/spectralscan/build.gradle +++ b/src/pt/spectralscan/build.gradle @@ -1,9 +1,7 @@ ext { extName = 'Spectral Scan' extClass = '.SpectralScan' - themePkg = 'yuyu' - baseUrl = 'https://spectralscan.xyz' - overrideVersionCode = 41 + extVersionCode = 43 isNsfw = false } diff --git a/src/pt/spectralscan/src/eu/kanade/tachiyomi/extension/pt/spectralscan/SelectFilter.kt b/src/pt/spectralscan/src/eu/kanade/tachiyomi/extension/pt/spectralscan/SelectFilter.kt new file mode 100644 index 000000000..0f2d035dd --- /dev/null +++ b/src/pt/spectralscan/src/eu/kanade/tachiyomi/extension/pt/spectralscan/SelectFilter.kt @@ -0,0 +1,62 @@ +package eu.kanade.tachiyomi.extension.pt.spectralscan + +import eu.kanade.tachiyomi.source.model.Filter + +class SelectFilter(displayName: String = "", val parameter: String = "", private val vals: Array>, state: Int = 0) : + Filter.Select(displayName, vals.map { it.first }.toTypedArray(), state) { + fun selected() = vals[state].second +} + +val sortList = arrayOf( + "Mais Recentes" to "latest", + "Mais Populares" to "popular", + "Melhor Avaliação" to "rating", + "Nome (A-Z)" to "name_asc", + "Nome (Z-A)" to "name_desc", +) + +val genreList = arrayOf( + "Todos os Gêneros" to "", + "Ação" to "acao", + "Armas e Combate" to "armas-e-combate", + "Artes Marciais" to "artes-marciais", + "Aventura" to "aventura", + "Comédia" to "comedia", + "Comida" to "comida", + "Cultivo" to "cultivo", + "Cyberpunk" to "cyberpunk", + "Drama" to "drama", + "Ecchi" to "ecchi", + "Escolar" to "escolar", + "Espacial" to "espacial", + "Esportes" to "esportes", + "Fantasia" to "fantasia", + "Ficção Científica" to "ficcao-cientifica", + "Harem" to "harem", + "Hentai" to "hentai", + "Horror" to "horror", + "Jogo" to "jogo", + "Josei" to "josei", + "Kodomomuke" to "kodomomuke", + "Mature Themes" to "mature-themes", + "Mecha" to "mecha", + "Mistério" to "misterio", + "Monstros" to "monstros", + "Psicológico" to "psicologico", + "Realidade Virtual" to "realidade-virtual", + "Reencarnação" to "reencarnacao", + "Regressão" to "regressao", + "Romance" to "romance", + "Seinen" to "seinen", + "Shoujo" to "shoujo", + "Shounen" to "shounen", + "Sistema" to "sistema", + "Slice of Life" to "slice-of-life", + "Sobrenatural" to "sobrenatural", + "Superpoderes" to "superpoderes", + "Suspense" to "suspense", + "Tela de Sistema" to "tela-de-sistema", + "Tragédia" to "tragedia", + "Vida Cotidiana" to "vida-cotidiana", + "Volta no Tempo" to "volta-no-tempo", +) diff --git a/src/pt/spectralscan/src/eu/kanade/tachiyomi/extension/pt/spectralscan/SpectralScan.kt b/src/pt/spectralscan/src/eu/kanade/tachiyomi/extension/pt/spectralscan/SpectralScan.kt index 451487ba9..2777f1ed6 100644 --- a/src/pt/spectralscan/src/eu/kanade/tachiyomi/extension/pt/spectralscan/SpectralScan.kt +++ b/src/pt/spectralscan/src/eu/kanade/tachiyomi/extension/pt/spectralscan/SpectralScan.kt @@ -1,17 +1,139 @@ package eu.kanade.tachiyomi.extension.pt.spectralscan -import eu.kanade.tachiyomi.multisrc.yuyu.YuYu +import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.model.FilterList +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 + +class SpectralScan : ParsedHttpSource() { + + override val lang = "pt-BR" + + override val name = "Spectral Scan" + + override val baseUrl = "https://spectralscan.xyz" + + override val supportsLatest = true + + override val versionId = 3 -class SpectralScan : YuYu( - "Spectral Scan", - "https://spectralscan.xyz", - "pt-BR", -) { override val client = super.client.newBuilder() .rateLimit(2) .build() - // Moved from Madara to YuYu - override val versionId = 2 + // ==================== Popular ========================== + + private val popularFilter = FilterList(SelectFilter(vals = arrayOf("" to "popular"), parameter = "sort")) + + override fun popularMangaRequest(page: Int) = searchMangaRequest(page, "", popularFilter) + override fun popularMangaSelector() = searchMangaSelector() + override fun popularMangaFromElement(element: Element) = searchMangaFromElement(element) + override fun popularMangaNextPageSelector() = searchMangaNextPageSelector() + + // ==================== Latest ========================== + + private val latestFilter = FilterList(SelectFilter(vals = arrayOf("" to "latest"), parameter = "sort")) + + override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", latestFilter) + override fun latestUpdatesSelector() = searchMangaSelector() + override fun latestUpdatesFromElement(element: Element) = searchMangaFromElement(element) + override fun latestUpdatesNextPageSelector() = searchMangaNextPageSelector() + + // ==================== Search ========================== + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = "$baseUrl/mangas".toHttpUrl().newBuilder() + .addQueryParameter("page", page.toString()) + .addQueryParameter("q", query) + filters.forEach { filter -> + when (filter) { + is SelectFilter -> { + url.addQueryParameter(filter.parameter, filter.selected()) + } + else -> {} + } + } + return GET(url.build(), headers) + } + + override fun searchMangaSelector() = ".content-grid .content-card" + + override fun searchMangaFromElement(element: Element) = SManga.create().apply { + title = element.selectFirst(".p-3 .text-sm")!!.text() + thumbnail_url = element.selectFirst("img")?.absUrl("src") + setUrlWithoutDomain(element.absUrl("href")) + } + + override fun searchMangaNextPageSelector() = "a[title='Última Página']" + + // ==================== Details ======================= + + override fun mangaDetailsParse(document: Document) = SManga.create().apply { + title = document.selectFirst("h1")!!.text() + thumbnail_url = document.selectFirst("img.item-cover-image")?.absUrl("src") + description = document.selectFirst(".item-description")?.text() + genre = document.select(".item-genres a").joinToString { it.text() } + document.select("span:contains(Status) + span")?.text()?.let { + status = when (it.lowercase()) { + "em andamento" -> SManga.ONGOING + else -> SManga.UNKNOWN + } + } + setUrlWithoutDomain(document.location()) + } + + // ==================== Chapter ======================= + + override fun chapterListParse(response: Response): List { + val document = response.asJsoup() + val element = document.selectFirst("section.chapter-section")!! + + val chapters = element.attr("data-total-chapters") + val pathSegment = element.attr("data-ajax-url") + val url = "$baseUrl$pathSegment".toHttpUrl().newBuilder() + .addQueryParameter("per_page", chapters) + .build() + + val newHeaders = headers.newBuilder() + .set("X-Requested-With", "XMLHttpRequest") + .build() + + return super.chapterListParse(client.newCall(GET(url, newHeaders)).execute()) + } + + override fun chapterListSelector() = ".chapter-item" + + override fun chapterFromElement(element: Element) = SChapter.create().apply { + name = element.selectFirst(".chapter-number")!!.text() + scanlator = element.selectFirst(".chapter-meta span")?.text() + setUrlWithoutDomain(element.absUrl("data-final-url")) + } + + // ==================== Page ========================== + + override fun pageListParse(document: Document): List { + return document.select("#chapterPagesContainer img").mapIndexed { index, element -> + Page(index, imageUrl = element.absUrl("src")) + } + } + + override fun imageUrlParse(document: Document) = "" + + // ==================== Filters ========================== + + override fun getFilterList(): FilterList { + return FilterList( + SelectFilter("Ordenar Por", "sort", sortList), + SelectFilter("Gênero", "genre", genreList), + ) + } }