[PerfScan] Update domain + Fixes (#2047)

Naga 9 months ago committed by GitHub
parent db9a7161f4
commit 8bc7480d84
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,13 +1,228 @@
package org.koitharu.kotatsu.parsers.site.heancms.fr package org.koitharu.kotatsu.parsers.site.heancms.fr
import okhttp3.Headers
import org.koitharu.kotatsu.parsers.Broken import okhttp3.Interceptor
import okhttp3.Response
import org.json.JSONArray
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.ContentRating
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaListFilter
import org.koitharu.kotatsu.parsers.model.MangaListFilterOptions
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.site.heancms.HeanCms import org.koitharu.kotatsu.parsers.site.heancms.HeanCms
import org.koitharu.kotatsu.parsers.util.generateUid
import org.koitharu.kotatsu.parsers.util.json.mapJSON
import org.koitharu.kotatsu.parsers.util.json.mapJSONIndexed
import org.koitharu.kotatsu.parsers.util.json.mapJSONToSet
import org.koitharu.kotatsu.parsers.util.parseJson
import org.koitharu.kotatsu.parsers.util.parseSafe
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
import org.koitharu.kotatsu.parsers.util.toTitleCase
import org.koitharu.kotatsu.parsers.util.urlEncoded
import java.text.SimpleDateFormat
import java.util.EnumSet
import java.util.Locale
@Broken
@MangaSourceParser("PERF_SCAN", "PerfScan", "fr") @MangaSourceParser("PERF_SCAN", "PerfScan", "fr")
internal class PerfScan(context: MangaLoaderContext) : internal class PerfScan(context: MangaLoaderContext) :
HeanCms(context, MangaParserSource.PERF_SCAN, "perf-scan.fr") HeanCms(context, MangaParserSource.PERF_SCAN, "perf-scan.xyz"), Interceptor {
override suspend fun getFilterOptions() = MangaListFilterOptions(
availableTags = fetchAvailableTags(),
availableStates = fetchStatusMap().keys,
availableContentTypes = EnumSet.of(
ContentType.MANGA,
ContentType.MANHWA,
ContentType.MANHUA,
ContentType.NOVEL,
),
)
private var statusMap: Map<MangaState, String>? = null
private val apiHeaders = Headers.headersOf(
"Origin", "https://$domain",
"Referer", "https://$domain/",
"Cookie", "NEXT_LOCALE=$sourceLocale",
)
override val availableSortOrders: Set<SortOrder> = EnumSet.of(SortOrder.UPDATED)
override val cdn = "https://$apiPath/cdn/"
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = buildString {
append("https://$apiPath/series?page=$page&take=$pageSize&type=COMIC")
filter.query?.let { append("&title=").append(it.urlEncoded()) }
if (filter.states.isNotEmpty()) {
val statusMapping = fetchStatusMap()
filter.states.forEach { state ->
statusMapping[state]?.let { statusId ->
append("&statusIds[]=").append(statusId)
}
}
}
filter.tags.forEach { tag ->
append("&genreIds[]=").append(tag.key.urlEncoded())
}
}
val response = webClient.httpGet(url).parseJson()
val data = response.optJSONArray("data")
return parseMangaList(data)
}
private fun parseMangaList(jsonArray: JSONArray): List<Manga> {
return jsonArray.mapJSON { mangaObject ->
val id = mangaObject.getString("id")
val slug = mangaObject.getString("slug")
val authors = listOfNotNull(
mangaObject.optString("author").takeIf { it.isNotBlank() && it != "null" },
mangaObject.optString("artist").takeIf { it.isNotBlank() && it != "null" },
).joinToString(" & ")
Manga(
id = generateUid(id),
url = id,
title = mangaObject.getString("title"),
publicUrl = "/series/$slug".toAbsoluteUrl(domain),
coverUrl = cdn + mangaObject.getString("thumbnail"),
description = mangaObject.optString("description"),
authors = setOf(authors).filter { it.isNotBlank() }.toSet(),
tags = mangaObject.optJSONArray("SeriesGenre")?.mapJSONToSet {
val genre = it.getJSONObject("Genre")
MangaTag(genre.getString("name").toTitleCase(sourceLocale), genre.getString("id"), source)
} ?: emptySet(),
state = mangaObject.optJSONObject("Status")?.let { parseState(it.getString("name")) },
contentRating = if (mangaObject.optBoolean("isAdult")) ContentRating.ADULT else ContentRating.SAFE,
source = source,
altTitles = setOf(),
rating = RATING_UNKNOWN,
largeCoverUrl = null,
chapters = null,
)
}
}
override suspend fun getDetails(manga: Manga): Manga {
val seriesId = manga.url
val url = "https://$apiPath/series/$seriesId"
val response = webClient.httpGet(url).parseJson()
val seriesData = response.getJSONObject("data")
val chapters = seriesData.optJSONArray("Chapter")?.mapJSON { chapterObj ->
val chapterId = chapterObj.getString("id")
val chapterNumber = chapterObj.getInt("index")
MangaChapter(
id = generateUid(chapterId),
url = "/series/${seriesData.getString("slug")}/chapter/$chapterNumber",
title = chapterObj.optString("title", "Chapitre $chapterNumber")
.takeIf { it.isNotBlank() && it != "-" },
number = chapterNumber.toFloat(),
uploadDate = parseDate(chapterObj.getString("createdAt")),
source = source,
volume = 0,
scanlator = null,
branch = null,
)
}
return manga.copy(chapters = chapters)
}
private fun parseDate(dateString: String): Long {
return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH).parseSafe(dateString)
}
override suspend fun getPageUrl(page: MangaPage): String {
return page.url
}
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val newRequest = request.newBuilder()
.headers(apiHeaders)
.build()
return chain.proceed(newRequest)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullApiUrl = "https://$apiPath${chapter.url}"
val response = webClient.httpGet(fullApiUrl).parseJson()
val data = response.getJSONObject("data")
val chapterData = data.getJSONArray("content")
return chapterData.mapJSONIndexed { i, pageObject ->
val imageId = pageObject.getString("value")
MangaPage(
id = generateUid(imageId),
url = cdn + imageId,
preview = null,
source = source,
)
}
}
private suspend fun fetchAvailableTags(): Set<MangaTag> {
val url = "https://$apiPath/genre"
val response = webClient.httpGet(url).parseJson()
val genresArray = response.getJSONArray("data")
return genresArray.mapJSONToSet { genreObject ->
MangaTag(
title = genreObject.getString("name").toTitleCase(sourceLocale),
key = genreObject.getString("id"),
source = source,
)
}
}
private fun parseState(status: String) = when (status) {
"En cours" -> MangaState.ONGOING
"Terminé" -> MangaState.FINISHED
"Annulé" -> MangaState.ABANDONED
"En pause" -> MangaState.PAUSED
else -> null
}
private suspend fun fetchStatusMap(): Map<MangaState, String> {
if (statusMap != null) return statusMap!!
val url = "https://$apiPath/status"
val response = webClient.httpGet(url).parseJson()
val statusArray = response.getJSONArray("data")
val map = mutableMapOf<MangaState, String>()
statusArray.mapJSON { statusObject ->
val name = statusObject.getString("name")
val id = statusObject.getString("id")
when (name) {
"En cours" -> map[MangaState.ONGOING] = id
"Terminé" -> map[MangaState.FINISHED] = id
"Annulé" -> map[MangaState.ABANDONED] = id
"En pause" -> map[MangaState.PAUSED] = id
}
}
statusMap = map
return map
}
}

Loading…
Cancel
Save