diff --git a/src/all/comickfun/build.gradle b/src/all/comickfun/build.gradle index eeeadb960..fb93c9f7a 100644 --- a/src/all/comickfun/build.gradle +++ b/src/all/comickfun/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Comick' pkgNameSuffix = 'all.comickfun' extClass = '.ComickFunFactory' - extVersionCode = 38 + extVersionCode = 39 isNsfw = true } diff --git a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFun.kt b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFun.kt index d8f7db87e..79cc11123 100644 --- a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFun.kt +++ b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFun.kt @@ -82,6 +82,20 @@ abstract class ComickFun( .commit() } }.also(screen::addPreference) + + SwitchPreferenceCompat(screen.context).apply { + key = FIRST_COVER_PREF + title = "Update Covers" + summaryOff = "Prefer first cover" + summaryOn = "Keep cover updated" + setDefaultValue(FIRST_COVER_DEFAULT) + + setOnPreferenceChangeListener { _, newValue -> + preferences.edit() + .putBoolean(FIRST_COVER_PREF, newValue as Boolean) + .commit() + } + }.also(screen::addPreference) } private val SharedPreferences.ignoredGroups: Set @@ -97,6 +111,9 @@ abstract class ComickFun( private val SharedPreferences.includeMuTags: Boolean get() = getBoolean(INCLUDE_MU_TAGS_PREF, INCLUDE_MU_TAGS_DEFAULT) + private val SharedPreferences.updateCover: Boolean + get() = getBoolean(FIRST_COVER_PREF, FIRST_COVER_DEFAULT) + init { preferences.newLineIgnoredGroups() } @@ -283,8 +300,37 @@ abstract class ComickFun( return GET("$apiUrl$mangaUrl?tachiyomi=true", headers) } - override fun mangaDetailsParse(response: Response): SManga { + override fun fetchMangaDetails(manga: SManga): Observable { + return client.newCall(mangaDetailsRequest(manga)) + .asObservableSuccess() + .map { response -> + mangaDetailsParse(response, manga).apply { initialized = true } + } + } + + override fun mangaDetailsParse(response: Response): SManga = + mangaDetailsParse(response, SManga.create()) + + private fun mangaDetailsParse(response: Response, manga: SManga): SManga { val mangaData = response.parseAs() + if (!preferences.updateCover && manga.thumbnail_url != mangaData.comic.cover) { + if (manga.thumbnail_url.toString().endsWith("#")) { + return mangaData.toSManga( + includeMuTags = preferences.includeMuTags, + covers = listOf( + MDcovers( + b2key = manga.thumbnail_url?.removeSuffix("#") + ?.substringAfterLast("/"), + ), + ), + ) + } + val coversUrl = + "$apiUrl/comic/${mangaData.comic.slug ?: mangaData.comic.hid}/covers?tachiyomi=true" + val covers = client.newCall(GET(coversUrl)).execute() + .parseAs().md_covers.reversed() + return mangaData.toSManga(includeMuTags = preferences.includeMuTags, covers = covers) + } return mangaData.toSManga(includeMuTags = preferences.includeMuTags) } @@ -392,6 +438,8 @@ abstract class ComickFun( private const val INCLUDE_MU_TAGS_PREF = "IncludeMangaUpdatesTags" private const val INCLUDE_MU_TAGS_DEFAULT = false private const val MIGRATED_IGNORED_GROUPS = "MigratedIgnoredGroups" + private const val FIRST_COVER_PREF = "DefaultCover" + private const val FIRST_COVER_DEFAULT = true private const val limit = 20 val dateFormat by lazy { SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH).apply { diff --git a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunDto.kt b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunDto.kt index f915468f9..91d9ab1f1 100644 --- a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunDto.kt +++ b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunDto.kt @@ -4,6 +4,8 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import java.math.BigDecimal +import java.math.RoundingMode @Serializable data class SearchManga( @@ -29,41 +31,57 @@ data class Manga( val genres: List = emptyList(), val demographic: String? = null, ) { - fun toSManga(includeMuTags: Boolean = false) = SManga.create().apply { - // appennding # at end as part of migration from slug to hid - url = "/comic/${comic.hid}#" - title = comic.title - description = comic.desc?.beautifyDescription() - if (comic.altTitles.isNotEmpty()) { - if (description.isNullOrEmpty()) { - description = "Alternative Titles:\n" - } else { - description += "\n\nAlternative Titles:\n" - } - - description += comic.altTitles.mapNotNull { title -> - title.title?.let { "• $it" } - }.joinToString("\n") - } - status = comic.status.parseStatus(comic.translationComplete) - thumbnail_url = parseCover(comic.cover, comic.mdCovers) - artist = artists.joinToString { it.name.trim() } - author = authors.joinToString { it.name.trim() } - genre = buildList { - comic.origination?.let(::add) - demographic?.let { add(Name(it)) } - addAll(genres) - addAll(comic.mdGenres.mapNotNull { it.name }) - if (includeMuTags) { - comic.muGenres.categories.forEach { category -> - category?.category?.title?.let { add(Name(it)) } + fun toSManga(includeMuTags: Boolean = false, covers: List? = null) = + SManga.create().apply { + // appennding # at end as part of migration from slug to hid + url = "/comic/${comic.hid}#" + title = comic.title + description = buildString { + if (!comic.score.isNullOrEmpty()) { + val stars = comic.score.toBigDecimal().div(BigDecimal(2)) + .setScale(0, RoundingMode.HALF_UP).toInt() + append("★".repeat(stars)) + if (stars < 5) append("☆".repeat(5 - stars)) + append(" ${comic.score} ") + } + val desc = comic.desc?.beautifyDescription() + if (!desc.isNullOrEmpty()) { + if (this.isNotEmpty()) append("\n\n") + append(desc) + } + if (comic.altTitles.isNotEmpty()) { + if (this.isNotEmpty()) append("\n\n") + append("Alternative Titles:\n") + append( + comic.altTitles.mapNotNull { title -> + title.title?.let { "• $it" } + }.joinToString("\n"), + ) } } + + status = comic.status.parseStatus(comic.translationComplete) + thumbnail_url = parseCover( + comic.cover, + covers ?: comic.mdCovers, + ) + artist = artists.joinToString { it.name.trim() } + author = authors.joinToString { it.name.trim() } + genre = buildList { + comic.origination?.let(::add) + demographic?.let { add(Name(it)) } + addAll(genres) + addAll(comic.mdGenres.mapNotNull { it.name }) + if (includeMuTags) { + comic.muGenres.categories.forEach { category -> + category?.category?.title?.let { add(Name(it)) } + } + } + } + .distinctBy { it.name } + .filter { it.name.isNotBlank() } + .joinToString { it.name.trim() } } - .distinctBy { it.name } - .filter { it.name.isNotBlank() } - .joinToString { it.name.trim() } - } } @Serializable @@ -71,6 +89,7 @@ data class Comic( val hid: String, val title: String, val country: String? = null, + val slug: String? = null, @SerialName("md_titles") val altTitles: List = emptyList(), val desc: String? = null, val status: Int? = 0, @@ -79,6 +98,7 @@ data class Comic( @SerialName("cover_url") val cover: String? = null, @SerialName("md_comic_md_genres") val mdGenres: List<MdGenres>, @SerialName("mu_comics") val muGenres: MuComicCategories = MuComicCategories(emptyList()), + @SerialName("bayesian_rating") val score: String? = null, ) { val origination = when (country) { "jp" -> Name("Manga") @@ -103,6 +123,11 @@ data class MuCategories( @SerialName("mu_categories") val category: Title? = null, ) +@Serializable +data class Covers( + val md_covers: List<MDcovers> = emptyList(), +) + @Serializable data class MDcovers( val b2key: String?, diff --git a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunHelper.kt b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunHelper.kt index ea8c05946..29ed1eb15 100644 --- a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunHelper.kt +++ b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunHelper.kt @@ -38,7 +38,7 @@ internal fun parseCover(thumbnailUrl: String?, mdCovers: List<MDcovers>): String val b2key = mdCovers.firstOrNull()?.b2key ?: return thumbnailUrl - return thumbnailUrl?.let { "$it#$b2key" } + return thumbnailUrl?.replaceAfterLast("/", "$b2key#") } internal fun thumbnailIntercept(chain: Interceptor.Chain): Response {