diff --git a/src/all/manta/build.gradle.kts b/src/all/manta/build.gradle.kts index ee836b5cd26..f80664cf8cc 100644 --- a/src/all/manta/build.gradle.kts +++ b/src/all/manta/build.gradle.kts @@ -6,9 +6,9 @@ plugins { keiyoushi { name = "Manta Comics" - versionCode = 9 + versionCode = 10 contentWarning = ContentWarning.MIXED - libVersion = "1.4" + libVersion = "1.6" listOf("en", "es").forEach { source { diff --git a/src/all/manta/src/eu/kanade/tachiyomi/extension/all/manta/MantaComics.kt b/src/all/manta/src/eu/kanade/tachiyomi/extension/all/manta/MantaComics.kt index 5e47c72f20d..367b858d359 100644 --- a/src/all/manta/src/eu/kanade/tachiyomi/extension/all/manta/MantaComics.kt +++ b/src/all/manta/src/eu/kanade/tachiyomi/extension/all/manta/MantaComics.kt @@ -1,33 +1,44 @@ package eu.kanade.tachiyomi.extension.all.manta -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.asObservable +import android.content.SharedPreferences +import androidx.preference.PreferenceScreen +import androidx.preference.SwitchPreferenceCompat +import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.Filter 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.source.model.SMangaUpdate import keiyoushi.annotation.Source +import keiyoushi.network.get +import keiyoushi.source.KeiSource +import keiyoushi.utils.getPreferencesLazy import keiyoushi.utils.parseAs +import kotlinx.serialization.json.JsonElement +import okhttp3.Headers +import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient -import okhttp3.Request import okhttp3.Response @Source -abstract class MantaComics : HttpSource() { +abstract class MantaComics : + KeiSource(), + ConfigurableSource { private val apiUrl get() = "https://" + baseUrl.toHttpUrl().host + private val preferences: SharedPreferences by getPreferencesLazy() + override val supportsLatest = false - override val client: OkHttpClient = network.client.newBuilder() + override fun OkHttpClient.Builder.configureClient(): OkHttpClient.Builder = this .addInterceptor { chain -> val request = chain.request() val url = request.url - val cookies = client.cookieJar.loadForRequest(url) + val cookies = network.client.cookieJar.loadForRequest(url) val token = cookies.find { it.name == "token" }?.value if (token != null) { @@ -39,29 +50,22 @@ abstract class MantaComics : HttpSource() { chain.proceed(request) } } - .build() - override fun headersBuilder() = super.headersBuilder() + override fun Headers.Builder.configureHeaders(): Headers.Builder = this .set("Origin", apiUrl) .set("Accept-Language", lang) // ============================== Popular =============================== - override fun popularMangaRequest(page: Int) = latestUpdatesRequest(page) - - override fun popularMangaParse(response: Response) = searchMangaParse(response) - - override fun fetchPopularManga(page: Int) = latestUpdatesRequest(page).fetch(::searchMangaParse) + override suspend fun getPopularManga(page: Int): MangasPage = "$apiUrl/manta/v1/search/series?cat=New&lang=$lang".fetch(::parseSearchManga) // =============================== Latest =============================== - override fun latestUpdatesRequest(page: Int) = GET("$apiUrl/manta/v1/search/series?cat=New&lang=$lang", headers) - - override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException() + override suspend fun getLatestUpdates(page: Int): MangasPage = throw UnsupportedOperationException() // =============================== Search =============================== - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + override suspend fun getSearchMangaList(page: Int, query: String, filters: FilterList): MangasPage { val url = "$apiUrl/manta/v1/search/series".toHttpUrl().newBuilder().apply { addQueryParameter("lang", lang) if (query.isNotEmpty()) { @@ -73,10 +77,10 @@ abstract class MantaComics : HttpSource() { addQueryParameter(key, value) } }.build() - return GET(url, headers) + return url.fetch(::parseSearchManga) } - override fun searchMangaParse(response: Response) = response.parseAs>>>().data.map { + private fun parseSearchManga(response: Response) = response.parseAs>>>().data.map { SManga.create().apply { title = it.asString(lang) url = it.id.toString() @@ -84,59 +88,91 @@ abstract class MantaComics : HttpSource() { } }.let { MangasPage(it, false) } - override fun fetchSearchManga(page: Int, query: String, filters: FilterList) = searchMangaRequest(page, query, filters).fetch(::searchMangaParse) + // =========================== Manga Details / Chapters ============================ - // =========================== Manga Details ============================ + override suspend fun getMangaByUrl(url: HttpUrl): SManga? { + val host = url.host + val baseHost = baseUrl.toHttpUrl().host + if (host != baseHost && host != "www.$baseHost") return null - override fun mangaDetailsRequest(manga: SManga) = GET("$apiUrl/front/v1/series/${manga.url}?lang=$lang", headers) + val segments = url.pathSegments + val seriesIdx = segments.indexOf("series") + if (seriesIdx == -1 || seriesIdx >= segments.lastIndex) return null - override fun mangaDetailsParse(response: Response) = SManga.create().apply { - val data = response.parseAs>>().data.data - description = data.asString(lang) - genre = data.tags.joinToString { it.asString(lang) } - artist = data.artists.joinToString() - author = data.authors.joinToString() - status = when (data.isCompleted) { - true -> SManga.COMPLETED - else -> SManga.ONGOING + val id = segments[seriesIdx + 1] + return SManga.create().apply { + this.url = id } - initialized = true } - override fun fetchMangaDetails(manga: SManga) = mangaDetailsRequest(manga).fetch(::mangaDetailsParse) - - // ============================ Chapter List ============================ - - override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga) - - override fun chapterListParse(response: Response) = response.parseAs>>().data.episodes!! - .filterNot { it.lockData?.isLocked == true } - .map { - SChapter.create().apply { - name = it.asString(lang) - url = it.id.toString() - date_upload = it.timestamp - chapter_number = it.ord.toFloat() + override suspend fun fetchMangaUpdate( + manga: SManga, + chapters: List, + fetchDetails: Boolean, + fetchChapters: Boolean, + ): SMangaUpdate { + val seriesUrl = "$apiUrl/front/v1/series/${manga.url}?lang=$lang" + val response = client.get(seriesUrl, headers) + val mantaResponse = response.parseAs>>() + val series = mantaResponse.data + val details = series.data + + val mangaDetails = SManga.create().apply { + description = details.asString(lang) + genre = details.tags.joinToString { it.asString(lang) } + artist = details.artists.joinToString() + author = details.authors.joinToString() + status = when (details.isCompleted) { + true -> SManga.COMPLETED + else -> SManga.ONGOING } - }.reversed() + initialized = true + } - override fun fetchChapterList(manga: SManga) = chapterListRequest(manga).fetch(::chapterListParse) + val showLocked = preferences.getBoolean(PREF_SHOW_LOCKED, true) + val chapterList = series.episodes.orEmpty() + .filter { showLocked || it.lockData?.isLocked != true } + .map { + SChapter.create().apply { + name = it.asString(lang) + url = it.id.toString() + date_upload = it.timestamp + chapter_number = it.ord.toFloat() + } + }.reversed() + + return SMangaUpdate(mangaDetails, chapterList) + } // ============================= Page List ============================== - override fun pageListRequest(chapter: SChapter) = GET("$apiUrl/front/v1/episodes/${chapter.url}?lang=$lang", headers) - - override fun pageListParse(response: Response) = response.parseAs>().data.cutImages?.mapIndexed { idx, img -> - Page(idx, "", img.toString()) - } ?: emptyList() + override suspend fun getPageList(chapter: SChapter): List { + val url = "$apiUrl/front/v1/episodes/${chapter.url}?lang=$lang" + return url.fetch { response -> + response.parseAs>().data.cutImages?.mapIndexed { idx, img -> + Page(idx, "", img.toString()) + } ?: emptyList() + } + } - override fun fetchPageList(chapter: SChapter) = pageListRequest(chapter).fetch(::pageListParse) + // ============================ Preferences ============================= - override fun imageUrlParse(response: Response) = throw UnsupportedOperationException() + override fun setupPreferenceScreen(screen: PreferenceScreen) { + SwitchPreferenceCompat(screen.context).apply { + key = PREF_SHOW_LOCKED + title = if (lang == "es") "Mostrar capítulos de pago/bloqueados" else "Show paid/locked chapters" + summary = if (lang == "es") { + "Muestra los capítulos de pago o bloqueados en la lista de capítulos" + } else { + "Show paid or locked chapters in the chapter list" + } + setDefaultValue(true) + }.also(screen::addPreference) + } // ============================== Filters =============================== - override fun getFilterList() = FilterList( + override fun getFilterList(data: JsonElement?) = FilterList( Filter.Header("Filters are ignored when searching"), Filter.Separator(), Category(lang), @@ -148,8 +184,18 @@ abstract class MantaComics : HttpSource() { override fun getChapterUrl(chapter: SChapter) = "$baseUrl/episodes/${chapter.url}" - private fun Request.fetch(parse: (Response) -> R) = client.newCall(this).asObservable().map { res -> - if (res.isSuccessful) return@map parse(res) - error(res.parseAs>().status.toString()) - }!! + private suspend fun String.fetch(parse: (Response) -> R): R { + val res = client.get(this, headers, ensureSuccess = false) + if (res.isSuccessful) { + return parse(res) + } + val err = res.parseAs>().status.toString() + throw Exception(err) + } + + private suspend fun HttpUrl.fetch(parse: (Response) -> R): R = toString().fetch(parse) + + companion object { + private const val PREF_SHOW_LOCKED = "show_locked_chapters" + } }