Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/all/manta/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
168 changes: 107 additions & 61 deletions src/all/manta/src/eu/kanade/tachiyomi/extension/all/manta/MantaComics.kt
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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()) {
Expand All @@ -73,70 +77,102 @@ abstract class MantaComics : HttpSource() {
addQueryParameter(key, value)
}
}.build()
return GET(url, headers)
return url.fetch(::parseSearchManga)
}

override fun searchMangaParse(response: Response) = response.parseAs<MantaResponse<List<Series<Title>>>>().data.map {
private fun parseSearchManga(response: Response) = response.parseAs<MantaResponse<List<Series<Title>>>>().data.map {
SManga.create().apply {
title = it.asString(lang)
url = it.id.toString()
thumbnail_url = it.image.toString()
}
}.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<MantaResponse<Series<Details>>>().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<MantaResponse<Series<Title>>>().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<SChapter>,
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<MantaResponse<Series<Details>>>()
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<MantaResponse<Episode>>().data.cutImages?.mapIndexed { idx, img ->
Page(idx, "", img.toString())
} ?: emptyList()
override suspend fun getPageList(chapter: SChapter): List<Page> {
val url = "$apiUrl/front/v1/episodes/${chapter.url}?lang=$lang"
return url.fetch { response ->
response.parseAs<MantaResponse<Episode>>().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),
Expand All @@ -148,8 +184,18 @@ abstract class MantaComics : HttpSource() {

override fun getChapterUrl(chapter: SChapter) = "$baseUrl/episodes/${chapter.url}"

private fun <R> Request.fetch(parse: (Response) -> R) = client.newCall(this).asObservable().map { res ->
if (res.isSuccessful) return@map parse(res)
error(res.parseAs<MantaResponse<Unit>>().status.toString())
}!!
private suspend fun <R> String.fetch(parse: (Response) -> R): R {
val res = client.get(this, headers, ensureSuccess = false)
if (res.isSuccessful) {
return parse(res)
}
val err = res.parseAs<MantaResponse<Unit>>().status.toString()
throw Exception(err)
}

private suspend fun <R> HttpUrl.fetch(parse: (Response) -> R): R = toString().fetch(parse)

companion object {
private const val PREF_SHOW_LOCKED = "show_locked_chapters"
}
}