[InovaScanManga] Remake parser (Close #1988)

master
Naga 9 months ago committed by GitHub
parent 33a0a68ac7
commit e7316b5cd0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,271 @@
package org.koitharu.kotatsu.parsers.site.fr
import okhttp3.HttpUrl
import org.json.JSONObject
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.core.SinglePageMangaParser
import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.util.json.getStringOrNull
import org.koitharu.kotatsu.parsers.util.json.mapJSON
import java.text.SimpleDateFormat
import java.util.*
@MangaSourceParser("INOVASCANMANGA", "InovaScanManga", "fr", type = ContentType.HENTAI)
internal class InovaScanManga(context: MangaLoaderContext) :
SinglePageMangaParser(context, MangaParserSource.INOVASCANMANGA) {
override val configKeyDomain = ConfigKey.Domain("inovascanmanga.com")
override val availableSortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.POPULARITY,
SortOrder.NEWEST,
)
override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities(
isMultipleTagsSupported = true,
isSearchSupported = true,
isSearchWithFiltersSupported = true,
)
private var genreCache: Set<MangaTag>? = null
override suspend fun getFilterOptions(): MangaListFilterOptions {
return MangaListFilterOptions(
availableTags = fetchAvailableGenres(),
availableStates = EnumSet.of(
MangaState.ONGOING,
MangaState.FINISHED,
MangaState.PAUSED,
),
availableContentTypes = EnumSet.of(
ContentType.MANGA,
ContentType.MANHWA,
ContentType.MANHUA,
),
)
}
private suspend fun fetchAvailableGenres(): Set<MangaTag> {
genreCache?.let { return it }
val url = buildApiUrl(
search = "",
sort = "trending",
status = "all",
genre = "all",
type = "all",
)
val json = webClient.httpGet(url).parseJson()
val genresArray = json.getJSONArray("availableGenres")
val genres = HashSet<MangaTag>(genresArray.length())
for (i in 0 until genresArray.length()) {
val genreName = genresArray.getString(i)
genres.add(
MangaTag(
key = genreName.toTitleCase(sourceLocale),
title = genreName.toTitleCase(sourceLocale),
source = source,
)
)
}
genreCache = genres
return genres
}
override suspend fun getList(order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = buildApiUrl(
search = filter.query.orEmpty(),
sort = when (order) {
SortOrder.POPULARITY -> "popular"
SortOrder.NEWEST -> "new"
else -> "popular"
},
status = filter.states.oneOrThrowIfMany()?.let {
when (it) {
MangaState.ONGOING -> "ongoing"
MangaState.FINISHED -> "completed"
MangaState.PAUSED -> "hiatus"
else -> "all"
}
} ?: "all",
genre = filter.tags.oneOrThrowIfMany()?.key ?: "all",
type = filter.types.oneOrThrowIfMany()?.let {
when (it) {
ContentType.MANGA -> "Manga"
ContentType.MANHWA -> "Manhwa"
ContentType.MANHUA -> "Manhua"
else -> "all"
}
} ?: "all",
)
val json = webClient.httpGet(url).parseJson()
return json.getJSONArray("manga").mapJSON { jo ->
parseMangaFromList(jo)
}
}
override suspend fun getDetails(manga: Manga): Manga {
val json = webClient.httpGet(manga.url.toAbsoluteUrl(domain)).parseJson()
val mangaJson = json.getJSONObject("manga")
val mangaId = mangaJson.getInt("id")
val chaptersUrl = "https://$domain/api/manga/$mangaId/chapters"
val chaptersJson = webClient.httpGet(chaptersUrl).parseJson()
val allChapters = parseAllChapters(chaptersJson, mangaId, mangaJson.optString("team_name").nullIfEmpty())
return parseMangaDetails(mangaJson, allChapters)
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val json = webClient.httpGet(chapter.url.toAbsoluteUrl(domain)).parseJson()
return json.getJSONArray("pages").mapJSON { jo ->
val url = jo.getString("url").toAbsoluteUrl(domain)
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source,
)
}
}
private fun buildApiUrl(
search: String,
sort: String,
status: String,
genre: String,
type: String,
): String = buildString {
append("https://")
append(domain)
append("/api/manga/discover?")
append("search=").append(search.urlEncoded())
append("&sort=").append(sort)
append("&status=").append(status)
append("&genre=").append(genre.urlEncoded())
append("&type=").append(type)
append("&year=").append("all")
}
private fun parseMangaFromList(jo: JSONObject): Manga {
val mangaId = jo.getInt("id")
val mangaUrl = "/api/manga/$mangaId"
val genres = jo.getJSONArray("genres")
return Manga(
id = generateUid(mangaUrl),
url = mangaUrl,
publicUrl = "https://$domain/manga/$mangaId",
coverUrl = jo.getStringOrNull("cover_url")?.toAbsoluteUrl(domain),
title = jo.getString("title"),
altTitles = emptySet(),
rating = jo.optDouble("rating", 0.0).let { if (it > 0) it.toFloat() / 10f else RATING_UNKNOWN },
tags = parseGenreTags(genres),
authors = emptySet(),
state = parseStatus(jo.getString("status")),
source = source,
contentRating = if (genres.toString().contains("Adulte") || genres.toString().contains("Mature") || isNsfwSource) {
ContentRating.ADULT
} else null,
)
}
private fun parseMangaDetails(mangaJson: JSONObject, chapters: List<MangaChapter>): Manga {
val mangaId = mangaJson.getInt("id")
val mangaUrl = "/api/manga/$mangaId"
val genres = mangaJson.getJSONArray("genres")
val authors = parseStringArray(mangaJson.optJSONArray("authors"))
val artists = parseStringArray(mangaJson.optJSONArray("artists"))
return Manga(
id = generateUid(mangaUrl),
url = mangaUrl,
publicUrl = "https://$domain/manga/$mangaId",
coverUrl = mangaJson.getStringOrNull("cover_url")?.toAbsoluteUrl(domain),
title = mangaJson.getString("title"),
altTitles = parseStringArray(mangaJson.optJSONArray("alternative_titles")),
rating = mangaJson.optDouble("rating", 0.0).let { if (it > 0) it.toFloat() / 10f else RATING_UNKNOWN },
tags = parseGenreTags(genres),
authors = authors + artists,
state = parseStatus(mangaJson.getString("status")),
source = source,
contentRating = if (genres.toString().contains("Adulte") || genres.toString().contains("Mature")) {
ContentRating.ADULT
} else null,
description = mangaJson.getString("description"),
chapters = chapters,
)
}
private fun parseAllChapters(json: JSONObject, mangaId: Int, teamName: String?): List<MangaChapter> {
return json.getJSONArray("chapters").mapJSON { jo ->
val chapterId = jo.getInt("id")
val chapterUrl = "/api/manga/$mangaId/chapters/$chapterId/pages"
val numberStr = jo.getString("number")
val chapterNumber = numberStr.substringBefore('.').substringBefore(' ').toFloatOrNull() ?: 0f
MangaChapter(
id = generateUid(chapterUrl),
title = jo.optString("title").nullIfEmpty(),
number = chapterNumber,
volume = 0,
url = chapterUrl,
scanlator = teamName,
uploadDate = parseDate(jo.getString("date")),
branch = null,
source = source,
)
}.reversed()
}
private fun parseGenreTags(genresArray: org.json.JSONArray): Set<MangaTag> {
val tags = HashSet<MangaTag>(genresArray.length())
for (i in 0 until genresArray.length()) {
val genreName = genresArray.getString(i)
tags.add(
MangaTag(
key = genreName.toTitleCase(),
title = genreName.toTitleCase(),
source = source,
)
)
}
return tags
}
private fun parseStringArray(array: org.json.JSONArray?): Set<String> {
if (array == null) return emptySet()
val result = HashSet<String>(array.length())
for (i in 0 until array.length()) {
result.add(array.getString(i))
}
return result
}
private fun parseStatus(status: String): MangaState? = when (status) {
"ongoing" -> MangaState.ONGOING
"completed" -> MangaState.FINISHED
"hiatus" -> MangaState.PAUSED
else -> null
}
private fun parseDate(dateStr: String): Long {
return SimpleDateFormat("dd/MM/yyyy", Locale.FRENCH).parse(dateStr)
?.time ?: 0L
}
override suspend fun resolveLink(resolver: LinkResolver, link: HttpUrl): Manga? {
val mangaId = link.pathSegments.lastOrNull()?.toIntOrNull() ?: return null
val apiUrl = "/api/manga/$mangaId"
return resolver.resolveManga(this, url = apiUrl, id = generateUid(apiUrl))
}
}

@ -1,14 +0,0 @@
package org.koitharu.kotatsu.parsers.site.madara.fr
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Source change template")
@MangaSourceParser("INOVASCANMANGA", "InovaScanManga", "fr")
internal class InovaScanManga(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.INOVASCANMANGA, "inovascanmanga.com") {
override val datePattern = "d MMMM yyyy"
}
Loading…
Cancel
Save