diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/mdlist/MdList.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/mdlist/MdList.kt index 57b049207..a40eba7b1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/mdlist/MdList.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/mdlist/MdList.kt @@ -168,17 +168,17 @@ class MdList(id: Long) : BaseTracker(id, "MDList") { trackPreferences.trackToken(this).delete() } - override suspend fun getMangaMetadata(track: DomainTrack): TrackMangaMetadata? { + override suspend fun getMangaMetadata(track: DomainTrack): TrackMangaMetadata { return withIOContext { val mdex = mdex ?: throw MangaDexNotFoundException() val manga = mdex.getMangaMetadata(track.toDbTrack()) TrackMangaMetadata( remoteId = 0, - title = manga?.title, - thumbnailUrl = manga?.thumbnail_url, // Doesn't load the actual cover because of Refer header - description = manga?.description, - authors = manga?.author, - artists = manga?.artist, + title = manga.title, + thumbnailUrl = manga.thumbnail_url, // Doesn't load the actual cover because of Refer header + description = manga.description, + authors = manga.author, + artists = manga.artist, ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt index fdfe640cc..49a95c201 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/MangaDex.kt @@ -90,6 +90,8 @@ class MangaDex(delegate: HttpSource, val context: Context) : private fun coverQuality() = sourcePreferences.getString(getCoverQualityPrefKey(mdLang.lang), "").orEmpty() private fun tryUsingFirstVolumeCover() = sourcePreferences.getBoolean(getTryUsingFirstVolumeCoverKey(mdLang.lang), false) private fun altTitlesInDesc() = sourcePreferences.getBoolean(getAltTitlesInDescKey(mdLang.lang), false) + private fun finalChapterInDesc() = sourcePreferences.getBoolean(getFinalChapterInDescPrefKey(mdLang.lang), false) + private fun preferExtensionLangTitle() = sourcePreferences.getBoolean(getPreferExtensionLangTitlePrefKey(mdLang.extLang), true) private val mangadexService by lazy { MangaDexService(client) @@ -107,7 +109,7 @@ class MangaDex(delegate: HttpSource, val context: Context) : FollowsHandler(mdLang.lang, mangadexAuthService) } private val mangaHandler by lazy { - MangaHandler(mdLang.lang, mangadexService, apiMangaParser, followsHandler) + MangaHandler(mdLang.lang, mangadexService, apiMangaParser) } private val similarHandler by lazy { SimilarHandler(mdLang.lang, mangadexService, similarService) @@ -192,11 +194,27 @@ class MangaDex(delegate: HttpSource, val context: Context) : @Deprecated("Use the 1.x API instead", replaceWith = ReplaceWith("getMangaDetails")) override fun fetchMangaDetails(manga: SManga): Observable { - return mangaHandler.fetchMangaDetailsObservable(manga, id, coverQuality(), tryUsingFirstVolumeCover(), altTitlesInDesc()) + return mangaHandler.fetchMangaDetailsObservable( + manga, + id, + coverQuality(), + tryUsingFirstVolumeCover(), + altTitlesInDesc(), + finalChapterInDesc(), + preferExtensionLangTitle(), + ) } override suspend fun getMangaDetails(manga: SManga): SManga { - return mangaHandler.getMangaDetails(manga, id, coverQuality(), tryUsingFirstVolumeCover(), altTitlesInDesc()) + return mangaHandler.getMangaDetails( + manga, + id, + coverQuality(), + tryUsingFirstVolumeCover(), + altTitlesInDesc(), + finalChapterInDesc(), + preferExtensionLangTitle(), + ) } @Deprecated("Use the 1.x API instead", replaceWith = ReplaceWith("getChapterList")) @@ -241,8 +259,21 @@ class MangaDex(delegate: HttpSource, val context: Context) : override fun newMetaInstance() = MangaDexSearchMetadata() - override suspend fun parseIntoMetadata(metadata: MangaDexSearchMetadata, input: Triple, StatisticsMangaDto>) { - apiMangaParser.parseIntoMetadata(metadata, input.first, input.second, input.third, null, coverQuality(), altTitlesInDesc()) + override suspend fun parseIntoMetadata( + metadata: MangaDexSearchMetadata, + input: Triple, StatisticsMangaDto>, + ) { + apiMangaParser.parseIntoMetadata( + metadata, + input.first, + input.second, + input.third, + null, + coverQuality(), + altTitlesInDesc(), + finalChapterInDesc(), + preferExtensionLangTitle(), + ) } // LoginSource methods @@ -296,10 +327,6 @@ class MangaDex(delegate: HttpSource, val context: Context) : return followsHandler.updateRating(track) } - suspend fun getTrackingAndMangaInfo(track: Track): Pair { - return mangaHandler.getTrackingInfo(track) - } - // RandomMangaSource method override suspend fun fetchRandomMangaUrl(): String { return mangaHandler.fetchRandomMangaId() @@ -313,51 +340,62 @@ class MangaDex(delegate: HttpSource, val context: Context) : return similarHandler.getRelated(manga) } - suspend fun getMangaMetadata(track: Track): SManga? { - return mangaHandler.getMangaMetadata(track, id, coverQuality(), tryUsingFirstVolumeCover(), altTitlesInDesc()) + suspend fun getMangaMetadata(track: Track): SManga { + return mangaHandler.getMangaMetadata( + track, + id, + coverQuality(), + tryUsingFirstVolumeCover(), + altTitlesInDesc(), + finalChapterInDesc(), + preferExtensionLangTitle(), + ) } companion object { private const val dataSaverPref = "dataSaverV5" - fun getDataSaverPreferenceKey(dexLang: String): String { return "${dataSaverPref}_$dexLang" } private const val standardHttpsPortPref = "usePort443" - fun getStandardHttpsPreferenceKey(dexLang: String): String { return "${standardHttpsPortPref}_$dexLang" } private const val blockedGroupsPref = "blockedGroups" - fun getBlockedGroupsPrefKey(dexLang: String): String { return "${blockedGroupsPref}_$dexLang" } private const val blockedUploaderPref = "blockedUploader" - fun getBlockedUploaderPrefKey(dexLang: String): String { return "${blockedUploaderPref}_$dexLang" } private const val coverQualityPref = "thumbnailQuality" - fun getCoverQualityPrefKey(dexLang: String): String { return "${coverQualityPref}_$dexLang" } - private const val tryUsingFirstVolumeCover = "tryUsingFirstVolumeCover" - + private const val tryUsingFirstVolumeCoverPref = "tryUsingFirstVolumeCover" fun getTryUsingFirstVolumeCoverKey(dexLang: String): String { - return "${tryUsingFirstVolumeCover}_$dexLang" + return "${tryUsingFirstVolumeCoverPref}_$dexLang" } - private const val altTitlesInDesc = "altTitlesInDesc" - + private const val altTitlesInDescPref = "altTitlesInDesc" fun getAltTitlesInDescKey(dexLang: String): String { - return "${altTitlesInDesc}_$dexLang" + return "${altTitlesInDescPref}_$dexLang" + } + + private const val finalChapterInDescPref = "finalChapterInDesc" + fun getFinalChapterInDescPrefKey(dexLang: String): String { + return "${finalChapterInDescPref}_$dexLang" + } + + private const val preferExtensionLangTitlePref = "preferExtensionLangTitle" + fun getPreferExtensionLangTitlePrefKey(dexLang: String): String { + return "${preferExtensionLangTitlePref}_$dexLang" } } } diff --git a/app/src/main/java/exh/md/handlers/ApiMangaParser.kt b/app/src/main/java/exh/md/handlers/ApiMangaParser.kt index 770abb387..6d80c3bbd 100644 --- a/app/src/main/java/exh/md/handlers/ApiMangaParser.kt +++ b/app/src/main/java/exh/md/handlers/ApiMangaParser.kt @@ -44,6 +44,8 @@ class ApiMangaParser( coverFileName: String?, coverQuality: String, altTitlesInDesc: Boolean, + finalChapterInDesc: Boolean, + preferExtensionLangTitle: Boolean, ): SManga { val mangaId = getManga.await(manga.url, sourceId)?.id val metadata = if (mangaId != null) { @@ -53,7 +55,17 @@ class ApiMangaParser( newMetaInstance() } - parseIntoMetadata(metadata, input, simpleChapters, statistics, coverFileName, coverQuality, altTitlesInDesc) + parseIntoMetadata( + metadata, + input, + simpleChapters, + statistics, + coverFileName, + coverQuality, + altTitlesInDesc, + finalChapterInDesc, + preferExtensionLangTitle, + ) if (mangaId != null) { metadata.mangaId = mangaId insertFlatMetadata.await(metadata.flatten()) @@ -70,13 +82,17 @@ class ApiMangaParser( coverFileName: String?, coverQuality: String, altTitlesInDesc: Boolean, + finalChapterInDesc: Boolean, + preferExtensionLangTitle: Boolean, ) { with(metadata) { try { val mangaAttributesDto = mangaDto.data.attributes mdUuid = mangaDto.data.id - title = MdUtil.getTitleFromManga(mangaAttributesDto, lang) - altTitles = mangaAttributesDto.altTitles.mapNotNull { it[lang] }.nullIfEmpty() + title = MdUtil.getTitleFromManga(mangaAttributesDto, lang, preferExtensionLangTitle) + altTitles = mangaAttributesDto.altTitles + .filter { it.containsKey(lang) || it.containsKey("${mangaAttributesDto.originalLanguage}-ro") } + .mapNotNull { it.values.singleOrNull() }.nullIfEmpty() val mangaRelationshipsDto = mangaDto.data.relationships cover = if (!coverFileName.isNullOrEmpty()) { @@ -96,9 +112,19 @@ class ApiMangaParser( originalLanguage = mangaAttributesDto.originalLanguage, ).orEmpty() - val cleanDesc = MdUtil.cleanDescription(rawDesc) - - description = if (altTitlesInDesc) MdUtil.addAltTitleToDesc(cleanDesc, altTitles) else cleanDesc + description = MdUtil.cleanDescription(rawDesc) + .let { if (altTitlesInDesc) MdUtil.addAltTitleToDesc(it, altTitles) else it } + .let { + if (finalChapterInDesc) { + MdUtil.addFinalChapterToDesc( + it, + mangaAttributesDto.lastVolume, + mangaAttributesDto.lastChapter, + ) + } else { + it + } + } authors = mangaRelationshipsDto.filter { relationshipDto -> relationshipDto.type.equals(MdConstants.Types.author, true) @@ -148,7 +174,11 @@ class ApiMangaParser( mangaAttributesDto.contentRating ?.takeUnless { it == "safe" } ?.let { - RaisedTag("Content Rating", it.capitalize(Locale.US), MangaDexSearchMetadata.TAG_TYPE_DEFAULT) + RaisedTag( + "Content Rating", + it.capitalize(Locale.US), + MangaDexSearchMetadata.TAG_TYPE_DEFAULT, + ) }, ) diff --git a/app/src/main/java/exh/md/handlers/MangaHandler.kt b/app/src/main/java/exh/md/handlers/MangaHandler.kt index 7666662ac..34edc62e2 100644 --- a/app/src/main/java/exh/md/handlers/MangaHandler.kt +++ b/app/src/main/java/exh/md/handlers/MangaHandler.kt @@ -8,7 +8,6 @@ import exh.md.service.MangaDexService import exh.md.utils.MdConstants import exh.md.utils.MdUtil import exh.md.utils.mdListCall -import exh.metadata.metadata.MangaDexSearchMetadata import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async @@ -21,7 +20,6 @@ class MangaHandler( private val lang: String, private val service: MangaDexService, private val apiMangaParser: ApiMangaParser, - private val followsHandler: FollowsHandler, ) { suspend fun getMangaDetails( manga: SManga, @@ -29,6 +27,8 @@ class MangaHandler( coverQuality: String, tryUsingFirstVolumeCover: Boolean, altTitlesInDesc: Boolean, + finalChapterInDesc: Boolean, + preferExtensionLangTitle: Boolean, ): SManga { return coroutineScope { val mangaId = MdUtil.getMangaId(manga.url) @@ -55,13 +55,31 @@ class MangaHandler( coverFileName?.await(), coverQuality, altTitlesInDesc, + finalChapterInDesc, + preferExtensionLangTitle, ) } } - fun fetchMangaDetailsObservable(manga: SManga, sourceId: Long, coverQuality: String, tryUsingFirstVolumeCover: Boolean, altTitlesInDesc: Boolean): Observable { + fun fetchMangaDetailsObservable( + manga: SManga, + sourceId: Long, + coverQuality: String, + tryUsingFirstVolumeCover: Boolean, + altTitlesInDesc: Boolean, + finalChapterInDesc: Boolean, + preferExtensionLangTitle: Boolean, + ): Observable { return runAsObservable { - getMangaDetails(manga, sourceId, coverQuality, tryUsingFirstVolumeCover, altTitlesInDesc) + getMangaDetails( + manga, + sourceId, + coverQuality, + tryUsingFirstVolumeCover, + altTitlesInDesc, + finalChapterInDesc, + preferExtensionLangTitle, + ) } } @@ -92,11 +110,10 @@ class MangaHandler( } private fun getGroupMap(results: List): Map { - return results.map { chapter -> chapter.relationships } - .flatten() + return results + .flatMap { it.relationships } .filter { it.type == MdConstants.Types.scanlator } - .map { it.id to it.attributes!!.name!! } - .toMap() + .associate { it.id to it.attributes!!.name!! } } suspend fun fetchRandomMangaId(): String { @@ -105,23 +122,6 @@ class MangaHandler( } } - suspend fun getTrackingInfo(track: Track): Pair { - return withIOContext { - /*val metadata = async { - val mangaUrl = MdUtil.buildMangaUrl(MdUtil.getMangaId(track.tracking_url)) - val manga = MangaInfo(mangaUrl, track.title) - val response = client.newCall(mangaRequest(manga)).await() - val metadata = MangaDexSearchMetadata() - apiMangaParser.parseIntoMetadata(metadata, response, emptyList()) - metadata - }*/ - val remoteTrack = async { - followsHandler.fetchTrackingInfo(track.tracking_url) - } - remoteTrack.await() to null - } - } - suspend fun getMangaFromChapterId(chapterId: String): String? { return withIOContext { apiMangaParser.chapterParseForMangaId(service.viewChapter(chapterId)) @@ -134,7 +134,9 @@ class MangaHandler( coverQuality: String, tryUsingFirstVolumeCover: Boolean, altTitlesInDesc: Boolean, - ): SManga? { + finalChapterInDesc: Boolean, + preferExtensionLangTitle: Boolean, + ): SManga { return withIOContext { val mangaId = MdUtil.getMangaId(track.tracking_url) val response = service.viewManga(mangaId) @@ -154,6 +156,8 @@ class MangaHandler( coverFileName, coverQuality, altTitlesInDesc, + finalChapterInDesc, + preferExtensionLangTitle, ) } } diff --git a/app/src/main/java/exh/md/utils/MdUtil.kt b/app/src/main/java/exh/md/utils/MdUtil.kt index fc9dc88b0..99d8a8fd5 100644 --- a/app/src/main/java/exh/md/utils/MdUtil.kt +++ b/app/src/main/java/exh/md/utils/MdUtil.kt @@ -3,19 +3,15 @@ package exh.md.utils import android.app.Application import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.domain.track.service.TrackPreferences -import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.track.mdlist.MdList import eu.kanade.tachiyomi.data.track.myanimelist.dto.MALOAuth import eu.kanade.tachiyomi.network.POST -import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.all.MangaDex import eu.kanade.tachiyomi.util.PkceUtil import exh.md.dto.MangaAttributesDto import exh.md.dto.MangaDataDto import exh.source.getMainSource -import exh.util.dropBlank -import exh.util.floor import exh.util.nullIfZero import kotlinx.serialization.json.Json import okhttp3.FormBody @@ -25,7 +21,9 @@ import okhttp3.Request import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody import org.jsoup.parser.Parser +import tachiyomi.core.common.i18n.stringResource import tachiyomi.domain.source.service.SourceManager +import tachiyomi.i18n.sy.SYMR import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.text.SimpleDateFormat @@ -39,21 +37,10 @@ class MdUtil { const val baseUrl = "https://mangadex.org" const val chapterSuffix = "/chapter/" - const val similarCacheMapping = "https://api.similarmanga.com/mapping/mdex2search.csv" - const val similarCacheMangas = "https://api.similarmanga.com/manga/" const val similarBaseApi = "https://api.similarmanga.com/similar/" - const val groupSearchUrl = "$baseUrl/groups/0/1/" - const val reportUrl = "https://api.mangadex.network/report" - - const val mdAtHomeTokenLifespan = 10 * 60 * 1000 const val mangaLimit = 20 - /** - * Get the manga offset pages are 1 based, so subtract 1 - */ - fun getMangaListOffset(page: Int): String = (mangaLimit * (page - 1)).toString() - val jsonParser = Json { isLenient = true @@ -65,15 +52,8 @@ class MdUtil { private const val scanlatorSeparator = " & " - const val contentRatingSafe = "safe" - const val contentRatingSuggestive = "suggestive" - const val contentRatingErotica = "erotica" - const val contentRatingPornographic = "pornographic" - - val validOneShotFinalChapters = listOf("0", "1") - - val markdownLinksRegex = "\\[([^]]+)\\]\\(([^)]+)\\)".toRegex() - val markdownItalicBoldRegex = "\\*+\\s*([^\\*]*)\\s*\\*+".toRegex() + val markdownLinksRegex = "\\[([^]]+)]\\(([^)]+)\\)".toRegex() + val markdownItalicBoldRegex = "\\*+\\s*([^*]*)\\s*\\*+".toRegex() val markdownItalicRegex = "_+\\s*([^_]*)\\s*_+".toRegex() fun buildMangaUrl(mangaUuid: String): String { @@ -94,47 +74,10 @@ class MdUtil { .trim() } - fun getImageUrl(attr: String): String { - // Some images are hosted elsewhere - if (attr.startsWith("http")) { - return attr - } - return baseUrl + attr - } - - fun getScanlators(scanlators: String?): Set { - return scanlators?.split(scanlatorSeparator)?.dropBlank()?.toSet().orEmpty() - } - fun getScanlatorString(scanlators: Set): String { return scanlators.sorted().joinToString(scanlatorSeparator) } - fun getMissingChapterCount(chapters: List, mangaStatus: Int): String? { - if (mangaStatus == SManga.COMPLETED) return null - - val remove0ChaptersFromCount = chapters.distinctBy { - /*if (it.chapter_txt.isNotEmpty()) { - it.vol + it.chapter_txt - } else {*/ - it.name - /*}*/ - }.sortedByDescending { it.chapter_number } - - remove0ChaptersFromCount.firstOrNull()?.let { chapter -> - val chpNumber = chapter.chapter_number.floor() - val allChapters = (1..chpNumber).toMutableSet() - - remove0ChaptersFromCount.forEach { - allChapters.remove(it.chapter_number.floor()) - } - - if (allChapters.isEmpty()) return null - return allChapters.size.toString() - } - return null - } - val dateFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss+SSS", Locale.US) .apply { timeZone = TimeZone.getTimeZone("UTC") } @@ -144,7 +87,7 @@ class MdUtil { fun createMangaEntry(json: MangaDataDto, lang: String): SManga { return SManga( url = buildMangaUrl(json.id), - title = getTitleFromManga(json.attributes, lang), + title = getTitleFromManga(json.attributes, lang, true), thumbnail_url = json.relationships .firstOrNull { relationshipDto -> relationshipDto.type == MdConstants.Types.coverArt } ?.attributes @@ -155,12 +98,30 @@ class MdUtil { ) } - fun getTitleFromManga(json: MangaAttributesDto, lang: String): String { - return getFromLangMap(json.title.asMdMap(), lang, json.originalLanguage) - ?: getAltTitle(json.altTitles, lang, json.originalLanguage) - ?: json.title.asMdMap()[json.originalLanguage] - ?: json.altTitles.firstNotNullOfOrNull { it[json.originalLanguage] } - .orEmpty() + fun getTitleFromManga(json: MangaAttributesDto, lang: String, preferExtensionLangTitle: Boolean): String { + val titleMap = json.title.asMdMap() + val altTitles = json.altTitles + val originalLang = json.originalLanguage + + titleMap[lang]?.let { return it } + + val mainTitle = titleMap.values.firstOrNull() + val langTitle = findTitleInMaps(lang, titleMap, altTitles) + val enTitle = findTitleInMaps("en", titleMap, altTitles) + val originalLangTitle = findTitleInMaps("$originalLang-ro", titleMap, altTitles) ?: findTitleInMaps( + originalLang, + titleMap, + altTitles, + ) + + val ordered = if (preferExtensionLangTitle) { + listOf(langTitle, mainTitle, enTitle, originalLangTitle) + } else { + listOf(mainTitle, langTitle, enTitle, originalLangTitle) + } + + return ordered.firstOrNull { it != null } + ?: "" } fun getFromLangMap(langMap: Map, currentLang: String, originalLanguage: String): String? { @@ -174,15 +135,12 @@ class MdUtil { } } - fun getAltTitle(langMaps: List>, currentLang: String, originalLanguage: String): String? { - return langMaps.firstNotNullOfOrNull { it[currentLang] } - ?: langMaps.firstNotNullOfOrNull { it["en"] } - ?: if (originalLanguage == "ja") { - langMaps.firstNotNullOfOrNull { it["ja-ro"] } - ?: langMaps.firstNotNullOfOrNull { it["jp-ro"] } - } else { - null - } + fun findTitleInMaps( + lang: String, + titleMap: Map, + altTitleMaps: List>, + ): String? { + return titleMap[lang] ?: altTitleMaps.firstNotNullOfOrNull { it[lang] } } fun cdnCoverUrl(dexId: String, fileName: String): String { @@ -200,7 +158,7 @@ class MdUtil { fun loadOAuth(preferences: TrackPreferences, mdList: MdList): MALOAuth? { return try { jsonParser.decodeFromString(preferences.trackToken(mdList).get()) - } catch (e: Exception) { + } catch (_: Exception) { null } } @@ -230,7 +188,10 @@ class MdUtil { return codeVerifier ?: PkceUtil.generateCodeVerifier().also { codeVerifier = it } } - fun getEnabledMangaDex(sourcePreferences: SourcePreferences = Injekt.get(), sourceManager: SourceManager = Injekt.get()): MangaDex? { + fun getEnabledMangaDex( + sourcePreferences: SourcePreferences = Injekt.get(), + sourceManager: SourceManager = Injekt.get(), + ): MangaDex? { return getEnabledMangaDexs(sourcePreferences, sourceManager).let { mangadexs -> sourcePreferences.preferredMangaDexId().get().toLongOrNull()?.nullIfZero() ?.let { preferredMangaDexId -> @@ -240,7 +201,10 @@ class MdUtil { } } - fun getEnabledMangaDexs(preferences: SourcePreferences, sourceManager: SourceManager = Injekt.get()): List { + fun getEnabledMangaDexs( + preferences: SourcePreferences, + sourceManager: SourceManager = Injekt.get(), + ): List { val languages = preferences.enabledLanguages().get() val disabledSourceIds = preferences.disabledSources().get() @@ -262,8 +226,30 @@ class MdUtil { description } else { val altTitlesDesc = altTitles - .joinToString("\n", "${Injekt.get().getString(R.string.alt_titles)}:\n") { "• $it" } - description + (if (description.isBlank()) "" else "\n\n") + Parser.unescapeEntities(altTitlesDesc, false) + .joinToString( + "\n", + "${Injekt.get().stringResource(SYMR.strings.alt_titles)}:\n", + ) { "• $it" } + description + (if (description.isBlank()) "" else "\n\n") + Parser.unescapeEntities( + altTitlesDesc, + false, + ) + } + } + + fun addFinalChapterToDesc(description: String, lastVolume: String?, lastChapter: String?): String { + val parts = listOfNotNull( + lastVolume?.takeIf { it.isNotEmpty() }?.let { "Vol.$it" }, + lastChapter?.takeIf { it.isNotEmpty() }?.let { "Ch.$it" }, + ) + + return if (parts.isEmpty()) { + description + } else { + description + (if (description.isBlank()) "" else "\n\n") + parts.joinToString( + " ", + "${Injekt.get().stringResource(SYMR.strings.final_chapter)}:\n", + ) } } } diff --git a/i18n-sy/src/commonMain/moko-resources/base/strings.xml b/i18n-sy/src/commonMain/moko-resources/base/strings.xml index a024c256e..d5a4664b9 100644 --- a/i18n-sy/src/commonMain/moko-resources/base/strings.xml +++ b/i18n-sy/src/commonMain/moko-resources/base/strings.xml @@ -708,7 +708,8 @@ Syncs any non MdList tracked entries to MangaDex as reading. Community recommendations Similar titles - Alternative Titles + Alternative titles + Final chapter Scanlator groups to show