Update MangaPlus to handle the subscription changes (#18367)

* Update MangaPlus to handle the subscription changes.

* Update the FAQ for MangaPlus.

* Remove unused import.
This commit is contained in:
Alessandro Jean 2023-10-04 13:58:39 -03:00 committed by GitHub
parent 477ef241b3
commit 42a3e238fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 168 additions and 33 deletions

View File

@ -9,10 +9,10 @@ Table of Content
### Why chapters are missing in some titles? ### 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 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 to read old chapters of a series that does not have the complete list, you need subscribe
another sources. to one of their paid plans and read directly in their app instead.
### Why disabling split pages setting does not work? ### Why disabling split pages setting does not work?

View File

@ -4,6 +4,20 @@ image_quality_high=High
image_quality_low=Low image_quality_low=Low
image_quality_medium=Medium image_quality_medium=Medium
not_available=Title not available in this language. 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=Split double pages
split_double_pages_summary=Only a few titles supports disabling this setting. split_double_pages_summary=Only a few titles supports disabling this setting.
title_removed=This title was removed from the MANGA Plus catalogue. title_removed=This title was removed from the MANGA Plus catalogue.

View File

@ -4,6 +4,20 @@ image_quality_high=Alta
image_quality_low=Baixa image_quality_low=Baixa
image_quality_medium=Média image_quality_medium=Média
not_available=Título não disponível neste idioma. 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=Dividir as páginas duplas
split_double_pages_summary=Somente poucos títulos suportam a desativação desta configuração. 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. title_removed=Este título foi removido do catálogo do MANGA Plus.

View File

@ -6,7 +6,7 @@ ext {
extName = 'MANGA Plus by SHUEISHA' extName = 'MANGA Plus by SHUEISHA'
pkgNameSuffix = 'all.mangaplus' pkgNameSuffix = 'all.mangaplus'
extClass = '.MangaPlusFactory' extClass = '.MangaPlusFactory'
extVersionCode = 44 extVersionCode = 45
} }
dependencies { dependencies {

View File

@ -186,7 +186,7 @@ class MangaPlus(
titleResult.success.titleDetailView!! titleResult.success.titleDetailView!!
.takeIf { it.title.language == langCode } .takeIf { it.title.language == langCode }
?.toSManga() ?.toSManga(intl)
} }
return MangasPage(listOfNotNull(title), hasNextPage = false) return MangasPage(listOfNotNull(title), hasNextPage = false)
@ -220,7 +220,7 @@ class MangaPlus(
.set("Referer", "$baseUrl/titles/$titleId") .set("Referer", "$baseUrl/titles/$titleId")
.build() .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 { override fun mangaDetailsParse(response: Response): SManga {
@ -240,7 +240,7 @@ class MangaPlus(
.takeIf { it.title.language == langCode } .takeIf { it.title.language == langCode }
?: throw Exception(intl["not_available"]) ?: throw Exception(intl["not_available"])
return titleDetails.toSManga() return titleDetails.toSManga(intl)
} }
override fun chapterListRequest(manga: SManga): Request = mangaDetailsRequest(manga.url) override fun chapterListRequest(manga: SManga): Request = mangaDetailsRequest(manga.url)
@ -260,11 +260,10 @@ class MangaPlus(
val titleDetailView = result.success.titleDetailView!! val titleDetailView = result.success.titleDetailView!!
val chapters = titleDetailView.firstChapterList + titleDetailView.lastChapterList return titleDetailView.chapterList
return chapters.reversed()
.filterNot(Chapter::isExpired) .filterNot(Chapter::isExpired)
.map(Chapter::toSChapter) .map(Chapter::toSChapter)
.reversed()
} }
// Remove the '#' and map to the new url format used in website. // Remove the '#' and map to the new url format used in website.

View File

@ -1,5 +1,6 @@
package eu.kanade.tachiyomi.extension.all.mangaplus 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.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
@ -57,51 +58,83 @@ data class TitleDetailView(
val title: Title, val title: Title,
val titleImageUrl: String, val titleImageUrl: String,
val overview: String? = null, val overview: String? = null,
val backgroundImageUrl: String,
val nextTimeStamp: Int = 0, val nextTimeStamp: Int = 0,
val viewingPeriodDescription: String = "", val viewingPeriodDescription: String = "",
val nonAppearanceInfo: String = "", val nonAppearanceInfo: String = "",
val firstChapterList: List<Chapter> = emptyList(), val chapterListGroup: List<ChapterListGroup> = emptyList(),
val lastChapterList: List<Chapter> = emptyList(),
val isSimulReleased: Boolean = false, val isSimulReleased: Boolean = false,
val rating: Rating = Rating.ALL_AGES,
val chaptersDescending: Boolean = true, val chaptersDescending: Boolean = true,
val titleLabels: TitleLabels,
val label: Label? = Label(LabelCode.WEEKLY_SHOUNEN_JUMP),
) { ) {
val chapterList: List<Chapter> by lazy {
chapterListGroup.flatMap { it.firstChapterList + it.lastChapterList }
}
private val isWebtoon: Boolean private val isWebtoon: Boolean
get() = firstChapterList.all(Chapter::isVerticalOnly) && get() = chapterList.isNotEmpty() && chapterList.all(Chapter::isVerticalOnly)
lastChapterList.all(Chapter::isVerticalOnly)
private val isOneShot: Boolean private val isOneShot: Boolean
get() = chapterCount == 1 && firstChapterList.firstOrNull() get() = chapterList.size == 1 && chapterList.firstOrNull()
?.name?.equals("one-shot", true) == true ?.name?.equals("one-shot", true) == true
private val chapterCount: Int
get() = firstChapterList.size + lastChapterList.size
private val isReEdition: Boolean private val isReEdition: Boolean
get() = viewingPeriodDescription.contains(REEDITION_REGEX) get() = viewingPeriodDescription.contains(REEDITION_REGEX)
private val isCompleted: Boolean 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 private val isOnHiatus: Boolean
get() = nonAppearanceInfo.contains(HIATUS_REGEX) get() = nonAppearanceInfo.contains(HIATUS_REGEX)
private val genres: List<String> private fun createGenres(intl: Intl): List<String> = buildList {
get() = listOfNotNull( if (isSimulpub && !isReEdition && !isOneShot && !isCompleted) {
"Simulrelease".takeIf { isSimulReleased && !isReEdition && !isOneShot }, add("Simulrelease")
"One-shot".takeIf { isOneShot }, }
"Re-edition".takeIf { isReEdition },
"Webtoon".takeIf { isWebtoon },
)
fun toSManga(): SManga = title.toSManga().apply { if (isOneShot) {
description = (overview.orEmpty() + "\n\n" + viewingPeriodDescription).trim() 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 { status = when {
isCompleted -> SManga.COMPLETED isCompleted -> SManga.COMPLETED
isOnHiatus -> SManga.ON_HIATUS isOnHiatus -> SManga.ON_HIATUS
else -> SManga.ONGOING else -> SManga.ONGOING
} }
genre = genres.joinToString() genre = createGenres(intl).joinToString()
} }
companion object { 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<Chapter> = emptyList(),
val lastChapterList: List<Chapter> = emptyList(),
)
@Serializable @Serializable
data class MangaViewer( data class MangaViewer(
val pages: List<MangaPlusPage> = emptyList(), val pages: List<MangaPlusPage> = emptyList(),
@ -124,7 +233,6 @@ data class Title(
val name: String, val name: String,
val author: String? = null, val author: String? = null,
val portraitImageUrl: String, val portraitImageUrl: String,
val landscapeImageUrl: String,
val viewCount: Int = 0, val viewCount: Int = 0,
val language: Language? = Language.ENGLISH, val language: Language? = Language.ENGLISH,
) { ) {