From 42a3e238fe62dcbadbffd4d1f3efbc7f26178a66 Mon Sep 17 00:00:00 2001 From: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:58:39 -0300 Subject: [PATCH] Update MangaPlus to handle the subscription changes (#18367) * Update MangaPlus to handle the subscription changes. * Update the FAQ for MangaPlus. * Remove unused import. --- src/all/mangaplus/README.md | 6 +- .../assets/i18n/messages_en.properties | 16 +- .../assets/i18n/messages_pt_br.properties | 16 +- src/all/mangaplus/build.gradle | 2 +- .../extension/all/mangaplus/MangaPlus.kt | 11 +- .../extension/all/mangaplus/MangaPlusDto.kt | 150 +++++++++++++++--- 6 files changed, 168 insertions(+), 33 deletions(-) diff --git a/src/all/mangaplus/README.md b/src/all/mangaplus/README.md index 5a5683857..aaf8352d7 100644 --- a/src/all/mangaplus/README.md +++ b/src/all/mangaplus/README.md @@ -9,10 +9,10 @@ Table of Content ### Why chapters are missing in some titles? -MANGA Plus does not host all chapters due to their licensing politics. The website +MANGA Plus does not host all chapters for free due to their licensing politics. The website is designed to let the users read the weekly/monthly official simulreleases. If you want -to read old chapters of a series that does not have the complete list, you need to find -another sources. +to read old chapters of a series that does not have the complete list, you need subscribe +to one of their paid plans and read directly in their app instead. ### Why disabling split pages setting does not work? diff --git a/src/all/mangaplus/assets/i18n/messages_en.properties b/src/all/mangaplus/assets/i18n/messages_en.properties index db98e72de..75adbec29 100644 --- a/src/all/mangaplus/assets/i18n/messages_en.properties +++ b/src/all/mangaplus/assets/i18n/messages_en.properties @@ -4,7 +4,21 @@ image_quality_high=High image_quality_low=Low image_quality_medium=Medium not_available=Title not available in this language. +rating=Rating: %s +rating_all_ages=All ages +rating_mature=Mature +rating_teen=Teen +rating_teen_plus=Teen Plus +schedule=Schedule: %s +schedule_bimonthly=Bimonthly +schedule_biweekly=Biweekly +schedule_everyday=Everyday +schedule_monthly=Monthly +schedule_other=Other +schedule_trimonthly=Trimonthly +schedule_weekly=Weekly +serialization=Serialization: %s split_double_pages=Split double pages split_double_pages_summary=Only a few titles supports disabling this setting. title_removed=This title was removed from the MANGA Plus catalogue. -unknown_error=An unknown error happened. \ No newline at end of file +unknown_error=An unknown error happened. diff --git a/src/all/mangaplus/assets/i18n/messages_pt_br.properties b/src/all/mangaplus/assets/i18n/messages_pt_br.properties index 6b07b5af6..86272f535 100644 --- a/src/all/mangaplus/assets/i18n/messages_pt_br.properties +++ b/src/all/mangaplus/assets/i18n/messages_pt_br.properties @@ -4,7 +4,21 @@ image_quality_high=Alta image_quality_low=Baixa image_quality_medium=Média not_available=Título não disponível neste idioma. +rating=Classificação: %s +rating_all_ages=Todas as idades +rating_mature=+18 +rating_teen=+10 +rating_teen_plus=+16 +schedule=Periodicidade: %s +schedule_bimonthly=Bimestral +schedule_biweekly=Bisemanal +schedule_everyday=Diário +schedule_monthly=Mensal +schedule_other=Outros +schedule_trimonthly=Trimestral +schedule_weekly=Semanal +serialization=Serialização: %s split_double_pages=Dividir as páginas duplas split_double_pages_summary=Somente poucos títulos suportam a desativação desta configuração. title_removed=Este título foi removido do catálogo do MANGA Plus. -unknown_error=Um erro desconhecido ocorreu. \ No newline at end of file +unknown_error=Um erro desconhecido ocorreu. diff --git a/src/all/mangaplus/build.gradle b/src/all/mangaplus/build.gradle index 32fec15bc..f2842fcbd 100644 --- a/src/all/mangaplus/build.gradle +++ b/src/all/mangaplus/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'MANGA Plus by SHUEISHA' pkgNameSuffix = 'all.mangaplus' extClass = '.MangaPlusFactory' - extVersionCode = 44 + extVersionCode = 45 } dependencies { diff --git a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlus.kt b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlus.kt index 67134c8a1..cbac52e66 100644 --- a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlus.kt +++ b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlus.kt @@ -186,7 +186,7 @@ class MangaPlus( titleResult.success.titleDetailView!! .takeIf { it.title.language == langCode } - ?.toSManga() + ?.toSManga(intl) } return MangasPage(listOfNotNull(title), hasNextPage = false) @@ -220,7 +220,7 @@ class MangaPlus( .set("Referer", "$baseUrl/titles/$titleId") .build() - return GET("$API_URL/title_detail?title_id=$titleId&format=json", newHeaders) + return GET("$API_URL/title_detailV3?title_id=$titleId&format=json", newHeaders) } override fun mangaDetailsParse(response: Response): SManga { @@ -240,7 +240,7 @@ class MangaPlus( .takeIf { it.title.language == langCode } ?: throw Exception(intl["not_available"]) - return titleDetails.toSManga() + return titleDetails.toSManga(intl) } override fun chapterListRequest(manga: SManga): Request = mangaDetailsRequest(manga.url) @@ -260,11 +260,10 @@ class MangaPlus( val titleDetailView = result.success.titleDetailView!! - val chapters = titleDetailView.firstChapterList + titleDetailView.lastChapterList - - return chapters.reversed() + return titleDetailView.chapterList .filterNot(Chapter::isExpired) .map(Chapter::toSChapter) + .reversed() } // Remove the '#' and map to the new url format used in website. diff --git a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusDto.kt b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusDto.kt index 2aa7db7c7..c2b455f02 100644 --- a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusDto.kt +++ b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusDto.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.extension.all.mangaplus +import eu.kanade.tachiyomi.lib.i18n.Intl import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import kotlinx.serialization.SerialName @@ -57,51 +58,83 @@ data class TitleDetailView( val title: Title, val titleImageUrl: String, val overview: String? = null, - val backgroundImageUrl: String, val nextTimeStamp: Int = 0, val viewingPeriodDescription: String = "", val nonAppearanceInfo: String = "", - val firstChapterList: List = emptyList(), - val lastChapterList: List = emptyList(), + val chapterListGroup: List = emptyList(), val isSimulReleased: Boolean = false, + val rating: Rating = Rating.ALL_AGES, val chaptersDescending: Boolean = true, + val titleLabels: TitleLabels, + val label: Label? = Label(LabelCode.WEEKLY_SHOUNEN_JUMP), ) { + + val chapterList: List by lazy { + chapterListGroup.flatMap { it.firstChapterList + it.lastChapterList } + } + private val isWebtoon: Boolean - get() = firstChapterList.all(Chapter::isVerticalOnly) && - lastChapterList.all(Chapter::isVerticalOnly) + get() = chapterList.isNotEmpty() && chapterList.all(Chapter::isVerticalOnly) private val isOneShot: Boolean - get() = chapterCount == 1 && firstChapterList.firstOrNull() + get() = chapterList.size == 1 && chapterList.firstOrNull() ?.name?.equals("one-shot", true) == true - private val chapterCount: Int - get() = firstChapterList.size + lastChapterList.size - private val isReEdition: Boolean get() = viewingPeriodDescription.contains(REEDITION_REGEX) private val isCompleted: Boolean - get() = nonAppearanceInfo.contains(COMPLETED_REGEX) || isOneShot + get() = nonAppearanceInfo.contains(COMPLETED_REGEX) || isOneShot || + titleLabels.releaseSchedule == ReleaseSchedule.COMPLETED || + titleLabels.releaseSchedule == ReleaseSchedule.DISABLED + + private val isSimulpub: Boolean + get() = isSimulReleased || titleLabels.isSimulpub private val isOnHiatus: Boolean get() = nonAppearanceInfo.contains(HIATUS_REGEX) - private val genres: List - get() = listOfNotNull( - "Simulrelease".takeIf { isSimulReleased && !isReEdition && !isOneShot }, - "One-shot".takeIf { isOneShot }, - "Re-edition".takeIf { isReEdition }, - "Webtoon".takeIf { isWebtoon }, - ) + private fun createGenres(intl: Intl): List = buildList { + if (isSimulpub && !isReEdition && !isOneShot && !isCompleted) { + add("Simulrelease") + } - fun toSManga(): SManga = title.toSManga().apply { - description = (overview.orEmpty() + "\n\n" + viewingPeriodDescription).trim() + if (isOneShot) { + add("One-shot") + } + + if (isReEdition) { + add("Re-edition") + } + + if (isWebtoon) { + add("Webtoon") + } + + if (label?.magazine != null) { + add(intl.format("serialization", label.magazine)) + } + + if (!isCompleted) { + val scheduleLabel = intl["schedule_" + titleLabels.releaseSchedule.toString().lowercase()] + add(intl.format("schedule", scheduleLabel)) + } + + val ratingLabel = intl["rating_" + rating.toString().lowercase()] + add(intl.format("rating", ratingLabel)) + } + + private val viewingInformation: String? + get() = viewingPeriodDescription.takeIf { !isCompleted } + + fun toSManga(intl: Intl): SManga = title.toSManga().apply { + description = "${overview.orEmpty()}\n\n${viewingInformation.orEmpty()}".trim() status = when { isCompleted -> SManga.COMPLETED isOnHiatus -> SManga.ON_HIATUS else -> SManga.ONGOING } - genre = genres.joinToString() + genre = createGenres(intl).joinToString() } companion object { @@ -111,6 +144,82 @@ data class TitleDetailView( } } +@Serializable +data class TitleLabels( + val releaseSchedule: ReleaseSchedule = ReleaseSchedule.DISABLED, + val isSimulpub: Boolean = false, +) + +enum class ReleaseSchedule { + DISABLED, + EVERYDAY, + WEEKLY, + BIWEEKLY, + MONTHLY, + BIMONTHLY, + TRIMONTHLY, + OTHER, + COMPLETED, +} + +@Serializable +enum class Rating { + @SerialName("ALLAGE") + ALL_AGES, + TEEN, + + @SerialName("TEENPLUS") + TEEN_PLUS, + MATURE, +} + +@Serializable +data class Label(val label: LabelCode? = LabelCode.WEEKLY_SHOUNEN_JUMP) { + val magazine: String? + get() = when (label) { + LabelCode.WEEKLY_SHOUNEN_JUMP -> "Weekly Shounen Jump" + LabelCode.JUMP_SQUARE -> "Jump SQ." + LabelCode.V_JUMP -> "V Jump" + LabelCode.WEEKLY_YOUNG_JUMP -> "Weekly Young Jump" + LabelCode.TONARI_NO_YOUNG_JUMP -> "Tonari no Young Jump" + LabelCode.SHOUNEN_JUMP_PLUS -> "Shounen Jump+" + LabelCode.REVIVAL -> "Revival" + LabelCode.MANGA_PLUS_CREATORS -> "MANGA Plus Creators" + else -> null + } +} + +@Serializable +enum class LabelCode { + @SerialName("WJ") + WEEKLY_SHOUNEN_JUMP, + + @SerialName("SQ") + JUMP_SQUARE, + + @SerialName("VJ") + V_JUMP, + + @SerialName("YJ") + WEEKLY_YOUNG_JUMP, + + @SerialName("TYJ") + TONARI_NO_YOUNG_JUMP, + + @SerialName("J_PLUS") + SHOUNEN_JUMP_PLUS, + REVIVAL, + + @SerialName("CREATORS") + MANGA_PLUS_CREATORS, +} + +@Serializable +data class ChapterListGroup( + val firstChapterList: List = emptyList(), + val lastChapterList: List = emptyList(), +) + @Serializable data class MangaViewer( val pages: List = emptyList(), @@ -124,7 +233,6 @@ data class Title( val name: String, val author: String? = null, val portraitImageUrl: String, - val landscapeImageUrl: String, val viewCount: Int = 0, val language: Language? = Language.ENGLISH, ) {