276 lines
10 KiB
Kotlin
276 lines
10 KiB
Kotlin
package exh.md.handlers
|
|
|
|
import eu.kanade.tachiyomi.source.model.SChapter
|
|
import eu.kanade.tachiyomi.source.model.SManga
|
|
import exh.log.xLogE
|
|
import exh.md.dto.ChapterDataDto
|
|
import exh.md.dto.ChapterDto
|
|
import exh.md.dto.MangaDto
|
|
import exh.md.dto.StatisticsMangaDto
|
|
import exh.md.utils.MdConstants
|
|
import exh.md.utils.MdUtil
|
|
import exh.md.utils.asMdMap
|
|
import exh.metadata.metadata.MangaDexSearchMetadata
|
|
import exh.metadata.metadata.base.RaisedTag
|
|
import exh.util.capitalize
|
|
import exh.util.floor
|
|
import exh.util.nullIfEmpty
|
|
import tachiyomi.domain.manga.interactor.GetFlatMetadataById
|
|
import tachiyomi.domain.manga.interactor.GetManga
|
|
import tachiyomi.domain.manga.interactor.InsertFlatMetadata
|
|
import uy.kohesive.injekt.injectLazy
|
|
import java.util.Locale
|
|
|
|
class ApiMangaParser(
|
|
private val lang: String,
|
|
) {
|
|
private val getManga: GetManga by injectLazy()
|
|
private val insertFlatMetadata: InsertFlatMetadata by injectLazy()
|
|
private val getFlatMetadataById: GetFlatMetadataById by injectLazy()
|
|
|
|
val metaClass = MangaDexSearchMetadata::class
|
|
|
|
/**
|
|
* Use reflection to create a new instance of metadata
|
|
*/
|
|
private fun newMetaInstance() = MangaDexSearchMetadata()
|
|
|
|
suspend fun parseToManga(
|
|
manga: SManga,
|
|
sourceId: Long,
|
|
input: MangaDto,
|
|
simpleChapters: List<String>,
|
|
statistics: StatisticsMangaDto?,
|
|
): SManga {
|
|
val mangaId = getManga.await(manga.url, sourceId)?.id
|
|
val metadata = if (mangaId != null) {
|
|
val flatMetadata = getFlatMetadataById.await(mangaId)
|
|
flatMetadata?.raise(metaClass) ?: newMetaInstance()
|
|
} else {
|
|
newMetaInstance()
|
|
}
|
|
|
|
parseIntoMetadata(metadata, input, simpleChapters, statistics)
|
|
if (mangaId != null) {
|
|
metadata.mangaId = mangaId
|
|
insertFlatMetadata.await(metadata.flatten())
|
|
}
|
|
|
|
return metadata.createMangaInfo(manga)
|
|
}
|
|
|
|
fun parseIntoMetadata(
|
|
metadata: MangaDexSearchMetadata,
|
|
mangaDto: MangaDto,
|
|
simpleChapters: List<String>,
|
|
statistics: StatisticsMangaDto?,
|
|
) {
|
|
with(metadata) {
|
|
try {
|
|
val mangaAttributesDto = mangaDto.data.attributes
|
|
mdUuid = mangaDto.data.id
|
|
title = MdUtil.getTitleFromManga(mangaAttributesDto, lang)
|
|
altTitles = mangaAttributesDto.altTitles.mapNotNull { it[lang] }.nullIfEmpty()
|
|
|
|
val mangaRelationshipsDto = mangaDto.data.relationships
|
|
mangaRelationshipsDto
|
|
.firstOrNull { relationshipDto -> relationshipDto.type == MdConstants.Types.coverArt }
|
|
?.attributes
|
|
?.fileName
|
|
?.let { coverFileName ->
|
|
cover = MdUtil.cdnCoverUrl(mangaDto.data.id, coverFileName)
|
|
}
|
|
|
|
description = MdUtil.cleanDescription(
|
|
MdUtil.getFromLangMap(
|
|
langMap = mangaAttributesDto.description.asMdMap(),
|
|
currentLang = lang,
|
|
originalLanguage = mangaAttributesDto.originalLanguage,
|
|
).orEmpty(),
|
|
)
|
|
|
|
authors = mangaRelationshipsDto.filter { relationshipDto ->
|
|
relationshipDto.type.equals(MdConstants.Types.author, true)
|
|
}.mapNotNull { it.attributes?.name }.distinct()
|
|
|
|
artists = mangaRelationshipsDto.filter { relationshipDto ->
|
|
relationshipDto.type.equals(MdConstants.Types.artist, true)
|
|
}.mapNotNull { it.attributes?.name }.distinct()
|
|
|
|
langFlag = mangaAttributesDto.originalLanguage
|
|
val lastChapter = mangaAttributesDto.lastChapter?.toFloatOrNull()
|
|
lastChapterNumber = lastChapter?.floor()
|
|
|
|
statistics?.rating?.let {
|
|
rating = it.bayesian?.toFloat()
|
|
// manga.users = it.users
|
|
}
|
|
|
|
mangaAttributesDto.links?.asMdMap<String>()?.let { links ->
|
|
links["al"]?.let { anilistId = it }
|
|
links["kt"]?.let { kitsuId = it }
|
|
links["mal"]?.let { myAnimeListId = it }
|
|
links["mu"]?.let { mangaUpdatesId = it }
|
|
links["ap"]?.let { animePlanetId = it }
|
|
}
|
|
|
|
// val filteredChapters = filterChapterForChecking(networkApiManga)
|
|
|
|
val tempStatus = parseStatus(mangaAttributesDto.status)
|
|
val publishedOrCancelled = tempStatus == SManga.PUBLISHING_FINISHED || tempStatus == SManga.CANCELLED
|
|
status = if (
|
|
mangaAttributesDto.lastChapter != null &&
|
|
publishedOrCancelled &&
|
|
mangaAttributesDto.lastChapter in simpleChapters
|
|
) {
|
|
SManga.COMPLETED
|
|
} else {
|
|
tempStatus
|
|
}
|
|
|
|
// things that will go with the genre tags but aren't actually genre
|
|
val nonGenres = listOfNotNull(
|
|
mangaAttributesDto.publicationDemographic
|
|
?.let { RaisedTag("Demographic", it.capitalize(Locale.US), MangaDexSearchMetadata.TAG_TYPE_DEFAULT) },
|
|
mangaAttributesDto.contentRating
|
|
?.takeUnless { it == "safe" }
|
|
?.let { RaisedTag("Content Rating", it.capitalize(Locale.US), MangaDexSearchMetadata.TAG_TYPE_DEFAULT) },
|
|
)
|
|
|
|
val genres = nonGenres + mangaAttributesDto.tags
|
|
.mapNotNull {
|
|
it.attributes.name[lang] ?: it.attributes.name["en"]
|
|
}
|
|
.map {
|
|
RaisedTag("Tags", it, MangaDexSearchMetadata.TAG_TYPE_DEFAULT)
|
|
}
|
|
|
|
if (tags.isNotEmpty()) tags.clear()
|
|
tags += genres
|
|
} catch (e: Exception) {
|
|
xLogE("Parse into metadata error", e)
|
|
throw e
|
|
}
|
|
}
|
|
}
|
|
|
|
/* private fun filterChapterForChecking(serializer: ApiMangaSerializer): List<ChapterSerializer> {
|
|
serializer.data.chapters ?: return emptyList()
|
|
return serializer.data.chapters.asSequence()
|
|
.filter { langs.contains(it.language) }
|
|
.filter {
|
|
it.chapter?.let { chapterNumber ->
|
|
if (chapterNumber.toDoubleOrNull() == null) {
|
|
return@filter false
|
|
}
|
|
return@filter true
|
|
}
|
|
return@filter false
|
|
}.toList()
|
|
}*/
|
|
|
|
/*private fun isOneShot(chapter: ChapterSerializer, finalChapterNumber: String): Boolean {
|
|
return chapter.title.equals("oneshot", true) ||
|
|
((chapter.chapter.isNullOrEmpty() || chapter.chapter == "0") && MdUtil.validOneShotFinalChapters.contains(finalChapterNumber))
|
|
}*/
|
|
|
|
private fun parseStatus(status: String?) = when (status) {
|
|
"ongoing" -> SManga.ONGOING
|
|
"completed" -> SManga.PUBLISHING_FINISHED
|
|
"cancelled" -> SManga.CANCELLED
|
|
"hiatus" -> SManga.ON_HIATUS
|
|
else -> SManga.UNKNOWN
|
|
}
|
|
|
|
fun chapterListParse(chapterListResponse: List<ChapterDataDto>, groupMap: Map<String, String>): List<SChapter> {
|
|
val now = System.currentTimeMillis()
|
|
return chapterListResponse
|
|
.filterNot { MdUtil.parseDate(it.attributes.publishAt) > now && it.attributes.externalUrl == null }
|
|
.map {
|
|
mapChapter(it, groupMap)
|
|
}
|
|
}
|
|
|
|
fun chapterParseForMangaId(chapterDto: ChapterDto): String? {
|
|
return chapterDto.data.relationships.find { it.type.equals("manga", true) }?.id
|
|
}
|
|
|
|
fun StringBuilder.appends(string: String): StringBuilder = append("$string ")
|
|
|
|
private fun mapChapter(
|
|
networkChapter: ChapterDataDto,
|
|
groups: Map<String, String>,
|
|
): SChapter {
|
|
val attributes = networkChapter.attributes
|
|
val key = MdUtil.chapterSuffix + networkChapter.id
|
|
val chapterName = StringBuilder()
|
|
// Build chapter name
|
|
|
|
if (attributes.volume != null) {
|
|
val vol = "Vol." + attributes.volume
|
|
chapterName.appends(vol)
|
|
// todo
|
|
// chapter.vol = vol
|
|
}
|
|
|
|
if (attributes.chapter.isNullOrBlank().not()) {
|
|
val chp = "Ch.${attributes.chapter}"
|
|
chapterName.appends(chp)
|
|
// chapter.chapter_txt = chp
|
|
}
|
|
|
|
if (!attributes.title.isNullOrBlank()) {
|
|
if (chapterName.isNotEmpty()) {
|
|
chapterName.appends("-")
|
|
}
|
|
chapterName.append(attributes.title)
|
|
}
|
|
|
|
// if volume, chapter and title is empty its a oneshot
|
|
if (chapterName.isEmpty()) {
|
|
chapterName.append("Oneshot")
|
|
}
|
|
/*if ((status == 2 || status == 3)) {
|
|
if (finalChapterNumber != null) {
|
|
if ((isOneShot(networkChapter, finalChapterNumber) && totalChapterCount == 1) ||
|
|
networkChapter.chapter == finalChapterNumber && finalChapterNumber.toIntOrNull() != 0
|
|
) {
|
|
chapterName.add("[END]")
|
|
}
|
|
}
|
|
}*/
|
|
|
|
val name = chapterName.toString()
|
|
// Convert from unix time
|
|
val dateUpload = MdUtil.parseDate(attributes.publishAt)
|
|
|
|
val scanlatorName = networkChapter.relationships
|
|
.filter {
|
|
it.type == MdConstants.Types.scanlator
|
|
}
|
|
.mapNotNull { groups[it.id] }
|
|
.map {
|
|
if (it == "no group") {
|
|
"No Group"
|
|
} else {
|
|
it
|
|
}
|
|
}
|
|
.toSet()
|
|
.ifEmpty { setOf("No Group") }
|
|
|
|
val scanlator = MdUtil.getScanlatorString(scanlatorName)
|
|
|
|
// chapter.mangadex_chapter_id = MdUtil.getChapterId(chapter.url)
|
|
|
|
// chapter.language = MdLang.fromIsoCode(attributes.translatedLanguage)?.prettyPrint ?: ""
|
|
|
|
return SChapter(
|
|
url = key,
|
|
name = name,
|
|
scanlator = scanlator,
|
|
date_upload = dateUpload,
|
|
)
|
|
}
|
|
}
|