Refactor the MangaDex code a bit (#17267)

* Refactor the MangaDex code a bit.

* Bump the extension version.
This commit is contained in:
Alessandro Jean 2023-07-26 16:18:12 -03:00 committed by GitHub
parent 873f752d61
commit a59ef3a817
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 175 additions and 160 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'MangaDex'
pkgNameSuffix = 'all.mangadex'
extClass = '.MangaDexFactory'
extVersionCode = 183
extVersionCode = 184
isNsfw = true
}

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.extension.all.mangadex
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.TimeZone
import kotlin.time.Duration.Companion.minutes
object MDConstants {
@ -31,7 +32,7 @@ object MDConstants {
const val atHomePostUrl = "https://api.mangadex.network/report"
val whitespaceRegex = "\\s".toRegex()
const val mdAtHomeTokenLifespan = 5 * 60 * 1000
val mdAtHomeTokenLifespan = 5.minutes.inWholeMilliseconds
val dateFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss+SSS", Locale.US)
.apply { timeZone = TimeZone.getTimeZone("UTC") }
@ -74,6 +75,12 @@ object MDConstants {
const val contentRatingPrefValErotica = "erotica"
const val contentRatingPrefValPornographic = "pornographic"
val contentRatingPrefDefaults = setOf(contentRatingPrefValSafe, contentRatingPrefValSuggestive)
val allContentRatings = setOf(
contentRatingPrefValSafe,
contentRatingPrefValSuggestive,
contentRatingPrefValErotica,
contentRatingPrefValPornographic,
)
fun getContentRatingPrefKey(dexLang: String): String {
return "${contentRatingPref}_$dexLang"

View File

@ -34,7 +34,6 @@ import okhttp3.CacheControl
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Request
import okhttp3.Response
import rx.Observable
@ -42,9 +41,8 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Date
abstract class MangaDex(final override val lang: String, private val dexLang: String) :
ConfigurableSource,
HttpSource() {
abstract class MangaDex(final override val lang: String, private val dexLang: String = lang) :
ConfigurableSource, HttpSource() {
override val name = MangaDexIntl.MANGADEX_NAME
@ -82,12 +80,9 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
.addQueryParameter("includes[]", MDConstants.coverArt)
.addQueryParameter("contentRating[]", preferences.contentRating)
.addQueryParameter("originalLanguage[]", preferences.originalLanguages)
.build()
return GET(
url = url.build().toString(),
headers = headers,
cache = CacheControl.FORCE_NETWORK,
)
return GET(url, headers, CacheControl.FORCE_NETWORK)
}
override fun popularMangaParse(response: Response): MangasPage {
@ -96,7 +91,6 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
}
val mangaListDto = response.parseAs<MangaListDto>()
val hasMoreResults = mangaListDto.limit + mangaListDto.offset < mangaListDto.total
val coverSuffix = preferences.coverQuality
val firstVolumeCovers = fetchFirstVolumeCovers(mangaListDto.data).orEmpty()
@ -109,17 +103,37 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
helper.createBasicManga(mangaDataDto, fileName, coverSuffix, dexLang)
}
return MangasPage(mangaList, hasMoreResults)
return MangasPage(mangaList, mangaListDto.hasNextPage)
}
// Latest manga section
override fun latestUpdatesRequest(page: Int): Request {
val url = MDConstants.apiChapterUrl.toHttpUrl().newBuilder()
.addQueryParameter("offset", helper.getLatestChapterOffset(page))
.addQueryParameter("limit", MDConstants.latestChapterLimit.toString())
.addQueryParameter("translatedLanguage[]", dexLang)
.addQueryParameter("order[publishAt]", "desc")
.addQueryParameter("includeFutureUpdates", "0")
.addQueryParameter("originalLanguage[]", preferences.originalLanguages)
.addQueryParameter("contentRating[]", preferences.contentRating)
.addQueryParameter(
"excludedGroups[]",
MDConstants.defaultBlockedGroups + preferences.blockedGroups,
)
.addQueryParameter("excludedUploaders[]", preferences.blockedUploaders)
.addQueryParameter("includeFuturePublishAt", "0")
.addQueryParameter("includeEmptyPages", "0")
.build()
return GET(url, headers, CacheControl.FORCE_NETWORK)
}
/**
* The API endpoint can't sort by date yet, so not implemented.
*/
override fun latestUpdatesParse(response: Response): MangasPage {
val chapterListDto = response.parseAs<ChapterListDto>()
val hasMoreResults = chapterListDto.limit + chapterListDto.offset < chapterListDto.total
val mangaIds = chapterListDto.data
.flatMap { it.relationships }
@ -128,13 +142,14 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
.distinct()
.toSet()
val mangaUrl = MDConstants.apiMangaUrl.toHttpUrlOrNull()!!.newBuilder()
val mangaApiUrl = MDConstants.apiMangaUrl.toHttpUrl().newBuilder()
.addQueryParameter("includes[]", MDConstants.coverArt)
.addQueryParameter("limit", mangaIds.size.toString())
.addQueryParameter("contentRating[]", preferences.contentRating)
.addQueryParameter("ids[]", mangaIds)
.build()
val mangaRequest = GET(mangaUrl.build().toString(), headers, CacheControl.FORCE_NETWORK)
val mangaRequest = GET(mangaApiUrl, headers, CacheControl.FORCE_NETWORK)
val mangaResponse = client.newCall(mangaRequest).execute()
val mangaListDto = mangaResponse.parseAs<MangaListDto>()
val firstVolumeCovers = fetchFirstVolumeCovers(mangaListDto.data).orEmpty()
@ -151,27 +166,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
helper.createBasicManga(mangaDataDto, fileName, coverSuffix, dexLang)
}
return MangasPage(mangaList, hasMoreResults)
}
override fun latestUpdatesRequest(page: Int): Request {
val url = MDConstants.apiChapterUrl.toHttpUrlOrNull()!!.newBuilder()
.addQueryParameter("offset", helper.getLatestChapterOffset(page))
.addQueryParameter("limit", MDConstants.latestChapterLimit.toString())
.addQueryParameter("translatedLanguage[]", dexLang)
.addQueryParameter("order[publishAt]", "desc")
.addQueryParameter("includeFutureUpdates", "0")
.addQueryParameter("originalLanguage[]", preferences.originalLanguages)
.addQueryParameter("contentRating[]", preferences.contentRating)
.addQueryParameter(
"excludedGroups[]",
MDConstants.defaultBlockedGroups + preferences.blockedGroups,
)
.addQueryParameter("excludedUploaders[]", preferences.blockedUploaders)
.addQueryParameter("includeFuturePublishAt", "0")
.addQueryParameter("includeEmptyPages", "0")
return GET(url.build().toString(), headers, CacheControl.FORCE_NETWORK)
return MangasPage(mangaList, chapterListDto.hasNextPage)
}
// Search manga section
@ -228,26 +223,31 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
}
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
if (query.startsWith(MDConstants.prefixIdSearch)) {
val mangaId = query.removePrefix(MDConstants.prefixIdSearch)
if (!helper.containsUuid(mangaId)) {
throw Exception(helper.intl.invalidMangaId)
}
val url = MDConstants.apiMangaUrl.toHttpUrl().newBuilder()
.addQueryParameter("ids[]", query.removePrefix(MDConstants.prefixIdSearch))
.addQueryParameter("includes[]", MDConstants.coverArt)
.addQueryParameter("contentRating[]", MDConstants.allContentRatings)
.build()
return GET(url, headers, CacheControl.FORCE_NETWORK)
}
val tempUrl = MDConstants.apiMangaUrl.toHttpUrl().newBuilder()
.addQueryParameter("limit", MDConstants.mangaLimit.toString())
.addQueryParameter("offset", helper.getMangaListOffset(page))
.addQueryParameter("includes[]", MDConstants.coverArt)
when {
query.startsWith(MDConstants.prefixIdSearch) -> {
val url = MDConstants.apiMangaUrl.toHttpUrlOrNull()!!.newBuilder()
.addQueryParameter("ids[]", query.removePrefix(MDConstants.prefixIdSearch))
.addQueryParameter("includes[]", MDConstants.coverArt)
.addQueryParameter("contentRating[]", "safe")
.addQueryParameter("contentRating[]", "suggestive")
.addQueryParameter("contentRating[]", "erotica")
.addQueryParameter("contentRating[]", "pornographic")
return GET(url.build().toString(), headers, CacheControl.FORCE_NETWORK)
}
query.startsWith(MDConstants.prefixGrpSearch) -> {
val groupId = query.removePrefix(MDConstants.prefixGrpSearch)
if (!helper.containsUuid(groupId)) {
throw Exception(helper.intl.invalidGroupId)
}
@ -257,6 +257,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
query.startsWith(MDConstants.prefixAuthSearch) -> {
val authorId = query.removePrefix(MDConstants.prefixAuthSearch)
if (!helper.containsUuid(authorId)) {
throw Exception(helper.intl.invalidAuthorId)
}
@ -311,7 +312,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
url.addQueryParameter("ids[]", ids)
val mangaRequest = GET(url.build().toString(), headers, CacheControl.FORCE_NETWORK)
val mangaRequest = GET(url.build(), headers, CacheControl.FORCE_NETWORK)
val mangaResponse = client.newCall(mangaRequest).execute()
val mangaList = searchMangaListParse(mangaResponse)
@ -345,7 +346,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
}
private fun searchMangaUploaderRequest(page: Int, uploader: String): Request {
val url = MDConstants.apiChapterUrl.toHttpUrlOrNull()!!.newBuilder()
val url = MDConstants.apiChapterUrl.toHttpUrl().newBuilder()
.addQueryParameter("offset", helper.getLatestChapterOffset(page))
.addQueryParameter("limit", MDConstants.latestChapterLimit.toString())
.addQueryParameter("translatedLanguage[]", dexLang)
@ -361,8 +362,9 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
MDConstants.defaultBlockedGroups + preferences.blockedGroups,
)
.addQueryParameter("excludedUploaders[]", preferences.blockedUploaders)
.build()
return GET(url.build().toString(), headers, CacheControl.FORCE_NETWORK)
return GET(url, headers, CacheControl.FORCE_NETWORK)
}
// Manga Details section
@ -370,7 +372,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
override fun getMangaUrl(manga: SManga): String {
// TODO: Remove once redirect for /manga is fixed.
val title = manga.title
val url = "${baseUrl}${manga.url.replace("manga", "title")}"
val url = baseUrl + manga.url.replaceFirst("manga", "title")
return "$url/" + helper.titleToSlug(title)
}
@ -389,8 +391,9 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
.addQueryParameter("includes[]", MDConstants.coverArt)
.addQueryParameter("includes[]", MDConstants.author)
.addQueryParameter("includes[]", MDConstants.artist)
.build()
return GET(url.build().toString(), headers, CacheControl.FORCE_NETWORK)
return GET(url, headers, CacheControl.FORCE_NETWORK)
}
override fun mangaDetailsParse(response: Response): SManga {
@ -453,7 +456,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
.addQueryParameter("locales[]", locales.toSet())
.addQueryParameter("limit", limit.toString())
.addQueryParameter("offset", "0")
.toString()
.build()
val result = runCatching {
client.newCall(GET(apiUrl, headers)).execute().parseAs<CoverArtListDto>().data
@ -499,8 +502,9 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
.addQueryParameter("contentRating[]", "pornographic")
.addQueryParameter("excludedGroups[]", preferences.blockedGroups)
.addQueryParameter("excludedUploaders[]", preferences.blockedUploaders)
.build()
return GET(url.build().toString(), headers, CacheControl.FORCE_NETWORK)
return GET(url, headers, CacheControl.FORCE_NETWORK)
}
override fun chapterListParse(response: Response): List<SChapter> {
@ -508,7 +512,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
return emptyList()
}
val chapterListResponse = response.parseAs<ChapterListDto>()
var chapterListResponse = response.parseAs<ChapterListDto>()
val chapterListResults = chapterListResponse.data.toMutableList()
@ -516,21 +520,20 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St
.substringBefore("/feed")
.substringAfter("${MDConstants.apiMangaUrl}/")
val limit = chapterListResponse.limit
var offset = chapterListResponse.offset
var hasMoreResults = (limit + offset) < chapterListResponse.total
var hasNextPage = chapterListResponse.hasNextPage
// Max results that can be returned is 500 so need to make more API
// calls if limit + offset > total chapters
while (hasMoreResults) {
offset += limit
// calls if the chapter list response has a next page.
while (hasNextPage) {
offset += chapterListResponse.limit
val newRequest = paginatedChapterListRequest(mangaId, offset)
val newResponse = client.newCall(newRequest).execute()
val newChapterList = newResponse.parseAs<ChapterListDto>()
chapterListResults.addAll(newChapterList.data)
hasMoreResults = (limit + offset) < newChapterList.total
hasNextPage = newChapterList.hasNextPage
}
return chapterListResults

View File

@ -53,48 +53,48 @@ class MangaDexFactory : SourceFactory {
)
}
class MangaDexArabic : MangaDex("ar", "ar")
class MangaDexBengali : MangaDex("bn", "bn")
class MangaDexBulgarian : MangaDex("bg", "bg")
class MangaDexBurmese : MangaDex("my", "my")
class MangaDexCatalan : MangaDex("ca", "ca")
class MangaDexArabic : MangaDex("ar")
class MangaDexBengali : MangaDex("bn")
class MangaDexBulgarian : MangaDex("bg")
class MangaDexBurmese : MangaDex("my")
class MangaDexCatalan : MangaDex("ca")
class MangaDexChineseSimplified : MangaDex("zh-Hans", "zh")
class MangaDexChineseTraditional : MangaDex("zh-Hant", "zh-hk")
class MangaDexCzech : MangaDex("cs", "cs")
class MangaDexDanish : MangaDex("da", "da")
class MangaDexDutch : MangaDex("nl", "nl")
class MangaDexEnglish : MangaDex("en", "en")
class MangaDexCzech : MangaDex("cs")
class MangaDexDanish : MangaDex("da")
class MangaDexDutch : MangaDex("nl")
class MangaDexEnglish : MangaDex("en")
class MangaDexFilipino : MangaDex("fil", "tl")
class MangaDexFinnish : MangaDex("fi", "fi")
class MangaDexFrench : MangaDex("fr", "fr")
class MangaDexGerman : MangaDex("de", "de")
class MangaDexGreek : MangaDex("el", "el")
class MangaDexHebrew : MangaDex("he", "he")
class MangaDexHindi : MangaDex("hi", "hi")
class MangaDexHungarian : MangaDex("hu", "hu")
class MangaDexIndonesian : MangaDex("id", "id")
class MangaDexItalian : MangaDex("it", "it")
class MangaDexJapanese : MangaDex("ja", "ja")
class MangaDexKazakh : MangaDex("kk", "kk")
class MangaDexKorean : MangaDex("ko", "ko")
class MangaDexLatin : MangaDex("la", "la")
class MangaDexLithuanian : MangaDex("lt", "lt")
class MangaDexMalay : MangaDex("ms", "ms")
class MangaDexMongolian : MangaDex("mn", "mn")
class MangaDexNepali : MangaDex("ne", "ne")
class MangaDexNorwegian : MangaDex("no", "no")
class MangaDexPersian : MangaDex("fa", "fa")
class MangaDexPolish : MangaDex("pl", "pl")
class MangaDexFinnish : MangaDex("fi")
class MangaDexFrench : MangaDex("fr")
class MangaDexGerman : MangaDex("de")
class MangaDexGreek : MangaDex("el")
class MangaDexHebrew : MangaDex("he")
class MangaDexHindi : MangaDex("hi")
class MangaDexHungarian : MangaDex("hu")
class MangaDexIndonesian : MangaDex("id")
class MangaDexItalian : MangaDex("it")
class MangaDexJapanese : MangaDex("ja")
class MangaDexKazakh : MangaDex("kk")
class MangaDexKorean : MangaDex("ko")
class MangaDexLatin : MangaDex("la")
class MangaDexLithuanian : MangaDex("lt")
class MangaDexMalay : MangaDex("ms")
class MangaDexMongolian : MangaDex("mn")
class MangaDexNepali : MangaDex("ne")
class MangaDexNorwegian : MangaDex("no")
class MangaDexPersian : MangaDex("fa")
class MangaDexPolish : MangaDex("pl")
class MangaDexPortugueseBrazil : MangaDex("pt-BR", "pt-br")
class MangaDexPortuguesePortugal : MangaDex("pt", "pt")
class MangaDexRomanian : MangaDex("ro", "ro")
class MangaDexRussian : MangaDex("ru", "ru")
class MangaDexSerboCroatian : MangaDex("sh", "sh")
class MangaDexPortuguesePortugal : MangaDex("pt")
class MangaDexRomanian : MangaDex("ro")
class MangaDexRussian : MangaDex("ru")
class MangaDexSerboCroatian : MangaDex("sh")
class MangaDexSpanishLatinAmerica : MangaDex("es-419", "es-la")
class MangaDexSpanishSpain : MangaDex("es", "es")
class MangaDexSwedish : MangaDex("sv", "sv")
class MangaDexTamil : MangaDex("ta", "ta")
class MangaDexThai : MangaDex("th", "th")
class MangaDexTurkish : MangaDex("tr", "tr")
class MangaDexUkrainian : MangaDex("uk", "uk")
class MangaDexVietnamese : MangaDex("vi", "vi")
class MangaDexSpanishSpain : MangaDex("es")
class MangaDexSwedish : MangaDex("sv")
class MangaDexTamil : MangaDex("ta")
class MangaDexThai : MangaDex("th")
class MangaDexTurkish : MangaDex("tr")
class MangaDexUkrainian : MangaDex("uk")
class MangaDexVietnamese : MangaDex("vi")

View File

@ -373,11 +373,11 @@ class MangaDexFilters {
TagExclusionMode(intl, getTagModes(intl)),
)
internal fun addFiltersToUrl(url: HttpUrl.Builder, filters: FilterList, dexLang: String): String {
internal fun addFiltersToUrl(url: HttpUrl.Builder, filters: FilterList, dexLang: String): HttpUrl {
filters.filterIsInstance<UrlQueryFilter>()
.forEach { filter -> filter.addQueryParameter(url, dexLang) }
return url.toString()
return url.build()
}
private fun List<Tag>.sortIfTranslated(intl: MangaDexIntl): List<Tag> = apply {

View File

@ -199,20 +199,18 @@ class MangaDexHelper(lang: String) {
* Check the token map to see if the MD@Home host is still valid.
*/
fun getValidImageUrlForPage(page: Page, headers: Headers, client: OkHttpClient): Request {
val data = page.url.split(",")
val (host, tokenRequestUrl, time) = page.url.split(",")
val mdAtHomeServerUrl =
when (Date().time - data[2].toLong() > MDConstants.mdAtHomeTokenLifespan) {
false -> data[0]
when (Date().time - time.toLong() > MDConstants.mdAtHomeTokenLifespan) {
false -> host
true -> {
val tokenRequestUrl = data[1]
val tokenLifespan = Date().time - (tokenTracker[tokenRequestUrl] ?: 0)
val cacheControl =
if (tokenLifespan > MDConstants.mdAtHomeTokenLifespan) {
CacheControl.FORCE_NETWORK
} else {
USE_CACHE
}
val cacheControl = if (tokenLifespan > MDConstants.mdAtHomeTokenLifespan) {
CacheControl.FORCE_NETWORK
} else {
USE_CACHE
}
getMdAtHomeUrl(tokenRequestUrl, client, headers, cacheControl)
}
}
@ -265,22 +263,20 @@ class MangaDexHelper(lang: String) {
coverFileName: String?,
coverSuffix: String?,
lang: String,
): SManga {
return SManga.create().apply {
url = "/manga/${mangaDataDto.id}"
val titleMap = mangaDataDto.attributes!!.title
val dirtyTitle =
titleMap.values.firstOrNull() // use literally anything from title as first resort
?: mangaDataDto.attributes.altTitles
.find { (it[lang] ?: it["en"]) !== null }
?.values?.singleOrNull() // find something else from alt titles
title = (dirtyTitle ?: "").removeEntitiesAndMarkdown()
): SManga = SManga.create().apply {
url = "/manga/${mangaDataDto.id}"
val titleMap = mangaDataDto.attributes!!.title
val dirtyTitle =
titleMap.values.firstOrNull() // use literally anything from title as first resort
?: mangaDataDto.attributes.altTitles
.find { (it[lang] ?: it["en"]) !== null }
?.values?.singleOrNull() // find something else from alt titles
title = dirtyTitle?.removeEntitiesAndMarkdown().orEmpty()
coverFileName?.let {
thumbnail_url = when (!coverSuffix.isNullOrEmpty()) {
true -> "${MDConstants.cdnUrl}/covers/${mangaDataDto.id}/$coverFileName$coverSuffix"
else -> "${MDConstants.cdnUrl}/covers/${mangaDataDto.id}/$coverFileName"
}
coverFileName?.let {
thumbnail_url = when (!coverSuffix.isNullOrEmpty()) {
true -> "${MDConstants.cdnUrl}/covers/${mangaDataDto.id}/$coverFileName$coverSuffix"
else -> "${MDConstants.cdnUrl}/covers/${mangaDataDto.id}/$coverFileName"
}
}
}
@ -335,10 +331,12 @@ class MangaDexHelper(lang: String) {
val genreList = MDConstants.tagGroupsOrder.flatMap { genresMap[it].orEmpty() } + nonGenres
var desc = (attr.description[lang] ?: attr.description["en"])?.removeEntitiesAndMarkdown() ?: ""
var desc = (attr.description[lang] ?: attr.description["en"])
?.removeEntitiesAndMarkdown()
.orEmpty()
if (altTitlesInDesc) {
val romanizedOriginalLang = MDConstants.romanizedLangCodes[attr.originalLanguage] ?: ""
val romanizedOriginalLang = MDConstants.romanizedLangCodes[attr.originalLanguage].orEmpty()
val altTitles = attr.altTitles
.filter { it.containsKey(lang) || it.containsKey(romanizedOriginalLang) }
.mapNotNull { it.values.singleOrNull() }
@ -447,13 +445,9 @@ class MangaDexHelper(lang: String) {
editText.addTextChangedListener(
object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// Do nothing.
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// Do nothing.
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
override fun afterTextChanged(editable: Editable?) {
requireNotNull(editable)

View File

@ -18,6 +18,11 @@ class MangaDexIntl(lang: String) {
else -> "The text contains invalid UUIDs"
}
val invalidMangaId: String = when (availableLang) {
BRAZILIAN_PORTUGUESE, PORTUGUESE -> "ID do mangá inválido"
else -> "Not a valid manga ID"
}
val invalidGroupId: String = when (availableLang) {
BRAZILIAN_PORTUGUESE, PORTUGUESE -> "ID do grupo inválido"
SPANISH_LATAM, SPANISH -> "ID de grupo inválida"

View File

@ -14,10 +14,9 @@ import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import uy.kohesive.injekt.injectLazy
import java.io.IOException
/**
* Interceptor to post to md@home for MangaDex Stats
* Interceptor to post to MD@Home for MangaDex Stats
*/
class MdAtHomeReportInterceptor(
private val client: OkHttpClient,
@ -26,23 +25,20 @@ class MdAtHomeReportInterceptor(
private val json: Json by injectLazy()
private val mdAtHomeUrlRegex =
Regex("""^https://[\w\d]+\.[\w\d]+\.mangadex(\b-test\b)?\.network.*${'$'}""")
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()
val response = chain.proceed(chain.request())
val url = originalRequest.url.toString()
if (!url.contains(mdAtHomeUrlRegex)) {
if (!url.contains(MD_AT_HOME_URL_REGEX)) {
return response
}
val result = ImageReportDto(
url,
url = url,
success = response.isSuccessful,
bytes = response.peekBody(Long.MAX_VALUE).bytes().size,
cached = response.header("X-Cache", "") == "HIT",
cached = response.headers["X-Cache"] == "HIT",
duration = response.receivedResponseAtMillis - response.sentRequestAtMillis,
)
@ -57,22 +53,28 @@ class MdAtHomeReportInterceptor(
// Execute the report endpoint network call asynchronously to avoid blocking
// the reader from showing the image once it's fully loaded if the report call
// gets stuck, as it tend to happens sometimes.
client.newCall(reportRequest).enqueue(
object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.e("MangaDex", "Error trying to POST report to MD@Home: ${e.message}")
}
override fun onResponse(call: Call, response: Response) {
response.close()
}
},
)
client.newCall(reportRequest).enqueue(REPORT_CALLBACK)
return response
}
companion object {
private val JSON_MEDIA_TYPE = "application/json".toMediaType()
private val MD_AT_HOME_URL_REGEX =
"""^https://[\w\d]+\.[\w\d]+\.mangadex(\b-test\b)?\.network.*${'$'}""".toRegex()
private val REPORT_CALLBACK = object : Callback {
override fun onFailure(call: Call, e: okio.IOException) {
Log.e("MangaDex", "Error trying to POST report to MD@Home: ${e.message}")
}
override fun onResponse(call: Call, response: Response) {
if (!response.isSuccessful) {
Log.e("MangaDex", "Error trying to POST report to MD@Home: HTTP error ${response.code}")
}
response.close()
}
}
}
}

View File

@ -10,7 +10,11 @@ data class PaginatedResponseDto<T : EntityDto>(
val limit: Int = 0,
val offset: Int = 0,
val total: Int = 0,
)
) {
val hasNextPage: Boolean
get() = limit + offset < total
}
@Serializable
data class ResponseDto<T : EntityDto>(