From 88fc45c2d14895ba0ab7a76d4659e3c56a2b49b3 Mon Sep 17 00:00:00 2001
From: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
Date: Mon, 4 Apr 2022 08:43:15 -0300
Subject: [PATCH] Use MangaPlus JSON API instead of Protobuf. (#11326)
---
src/all/mangaplus/AndroidManifest.xml | 8 +
src/all/mangaplus/build.gradle | 2 +-
.../extension/all/mangaplus/MangaPlus.kt | 39 ++---
.../extension/all/mangaplus/MangaPlusDto.kt | 138 ++++++++----------
.../all/mangaplus/MangaPlusUrlActivity.kt | 25 ++--
5 files changed, 108 insertions(+), 104 deletions(-)
diff --git a/src/all/mangaplus/AndroidManifest.xml b/src/all/mangaplus/AndroidManifest.xml
index d4d44d41d..51dd67096 100644
--- a/src/all/mangaplus/AndroidManifest.xml
+++ b/src/all/mangaplus/AndroidManifest.xml
@@ -22,6 +22,14 @@
android:host="www.mangaplus.shueisha.co.jp"
android:pathPattern="/titles/..*"
android:scheme="https" />
+
+
diff --git a/src/all/mangaplus/build.gradle b/src/all/mangaplus/build.gradle
index 9cdcb982c..3f3b66d85 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 = 28
+ extVersionCode = 29
}
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 b089dff26..fceb92653 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
@@ -15,8 +15,8 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
-import kotlinx.serialization.decodeFromByteArray
-import kotlinx.serialization.protobuf.ProtoBuf
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
@@ -29,6 +29,7 @@ import okhttp3.ResponseBody.Companion.toResponseBody
import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
+import uy.kohesive.injekt.injectLazy
import java.util.UUID
abstract class MangaPlus(
@@ -56,6 +57,8 @@ abstract class MangaPlus(
.addInterceptor(SpecificHostRateLimitInterceptor(baseUrl.toHttpUrl(), 2))
.build()
+ private val json: Json by injectLazy()
+
private val preferences: SharedPreferences by lazy {
Injekt.get().getSharedPreferences("source_$id", 0x0000)
}
@@ -73,11 +76,11 @@ abstract class MangaPlus(
.set("Referer", "$baseUrl/manga_list/hot")
.build()
- return GET("$API_URL/title_list/ranking", newHeaders)
+ return GET("$API_URL/title_list/ranking?format=json", newHeaders)
}
override fun popularMangaParse(response: Response): MangasPage {
- val result = response.asProto()
+ val result = response.asMangaPlusResponse()
if (result.success == null)
throw Exception(result.error!!.langPopup.body)
@@ -101,17 +104,18 @@ abstract class MangaPlus(
.set("Referer", "$baseUrl/updates")
.build()
- return GET("$API_URL/web/web_homeV3?lang=$internalLang", newHeaders)
+ return GET("$API_URL/web/web_homeV3?lang=$internalLang&format=json", newHeaders)
}
override fun latestUpdatesParse(response: Response): MangasPage {
- val result = response.asProto()
+ val result = response.asMangaPlusResponse()
if (result.success == null)
throw Exception(result.error!!.langPopup.body)
// Fetch all titles to get newer thumbnail URLs in the interceptor.
- val popularResponse = client.newCall(popularMangaRequest(1)).execute().asProto()
+ val popularResponse = client.newCall(popularMangaRequest(1)).execute()
+ .asMangaPlusResponse()
if (popularResponse.success != null) {
titleList = popularResponse.success.titleRankingView!!.titles
@@ -143,7 +147,7 @@ abstract class MangaPlus(
}
val filteredResult = it.mangas.filter { manga ->
- manga.title.contains(query, true)
+ manga.title.contains(query.trim(), true)
}
MangasPage(filteredResult, it.hasNextPage)
@@ -159,11 +163,11 @@ abstract class MangaPlus(
.set("Referer", "$baseUrl/manga_list/all")
.build()
- return GET("$API_URL/title_list/allV2", newHeaders)
+ return GET("$API_URL/title_list/allV2?format=json", newHeaders)
}
override fun searchMangaParse(response: Response): MangasPage {
- val result = response.asProto()
+ val result = response.asMangaPlusResponse()
if (result.success == null)
throw Exception(result.error!!.langPopup.body)
@@ -206,7 +210,7 @@ abstract class MangaPlus(
.set("Referer", "$baseUrl/titles/$titleId")
.build()
- return GET("$API_URL/title_detail?title_id=$titleId", newHeaders)
+ return GET("$API_URL/title_detail?title_id=$titleId&format=json", newHeaders)
}
// Workaround to allow "Open in browser" use the real URL.
@@ -224,7 +228,7 @@ abstract class MangaPlus(
}
override fun mangaDetailsParse(response: Response): SManga {
- val result = response.asProto()
+ val result = response.asMangaPlusResponse()
if (result.success == null)
throw Exception(result.error!!.langPopup.body)
@@ -245,7 +249,7 @@ abstract class MangaPlus(
override fun chapterListRequest(manga: SManga): Request = titleDetailsRequest(manga.url)
override fun chapterListParse(response: Response): List {
- val result = response.asProto()
+ val result = response.asMangaPlusResponse()
if (result.success == null)
throw Exception(result.error!!.langPopup.body)
@@ -278,13 +282,14 @@ abstract class MangaPlus(
.addQueryParameter("chapter_id", chapterId)
.addQueryParameter("split", if (splitImages) "yes" else "no")
.addQueryParameter("img_quality", imageQuality)
+ .addQueryParameter("format", "json")
.toString()
return GET(url, newHeaders)
}
override fun pageListParse(response: Response): List {
- val result = response.asProto()
+ val result = response.asMangaPlusResponse()
if (result.success == null)
throw Exception(result.error!!.langPopup.body)
@@ -292,7 +297,7 @@ abstract class MangaPlus(
val referer = response.request.header("Referer")!!
return result.success.mangaViewer!!.pages
- .mapNotNull(MangaPlusPage::page)
+ .mapNotNull(MangaPlusPage::mangaPage)
.mapIndexed { i, page ->
val encryptionKey = if (page.encryptionKey == null) "" else
"&encryptionKey=${page.encryptionKey}"
@@ -418,8 +423,8 @@ abstract class MangaPlus(
else -> englishPopup
}
- private fun Response.asProto(): MangaPlusResponse = use {
- ProtoBuf.decodeFromByteArray(body!!.bytes())
+ private fun Response.asMangaPlusResponse(): MangaPlusResponse = use {
+ json.decodeFromString(body!!.string())
}
companion object {
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 f6e6a383b..c047040d7 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,66 +1,66 @@
package eu.kanade.tachiyomi.extension.all.mangaplus
+import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
-import kotlinx.serialization.protobuf.ProtoNumber
@Serializable
data class MangaPlusResponse(
- @ProtoNumber(1) val success: SuccessResult? = null,
- @ProtoNumber(2) val error: ErrorResult? = null
+ val success: SuccessResult? = null,
+ val error: ErrorResult? = null
)
@Serializable
data class ErrorResult(
- @ProtoNumber(2) val englishPopup: Popup,
- @ProtoNumber(3) val spanishPopup: Popup
+ val englishPopup: Popup,
+ val spanishPopup: Popup
)
@Serializable
data class Popup(
- @ProtoNumber(1) val subject: String,
- @ProtoNumber(2) val body: String
+ val subject: String,
+ val body: String
)
@Serializable
data class SuccessResult(
- @ProtoNumber(1) val isFeaturedUpdated: Boolean? = false,
- @ProtoNumber(6) val titleRankingView: TitleRankingView? = null,
- @ProtoNumber(8) val titleDetailView: TitleDetailView? = null,
- @ProtoNumber(10) val mangaViewer: MangaViewer? = null,
- @ProtoNumber(25) val allTitlesViewV2: AllTitlesViewV2? = null,
- @ProtoNumber(31) val webHomeViewV3: WebHomeViewV3? = null
+ val isFeaturedUpdated: Boolean? = false,
+ val titleRankingView: TitleRankingView? = null,
+ val titleDetailView: TitleDetailView? = null,
+ val mangaViewer: MangaViewer? = null,
+ val allTitlesViewV2: AllTitlesViewV2? = null,
+ val webHomeViewV3: WebHomeViewV3? = null
)
@Serializable
-data class TitleRankingView(@ProtoNumber(1) val titles: List = emptyList())
+data class TitleRankingView(val titles: List = emptyList())
@Serializable
data class AllTitlesViewV2(
- @ProtoNumber(1) val allTitlesGroup: List = emptyList()
+ @SerialName("AllTitlesGroup") val allTitlesGroup: List = emptyList()
)
@Serializable
data class AllTitlesGroup(
- @ProtoNumber(1) val theTitle: String,
- @ProtoNumber(2) val titles: List = emptyList()
+ val theTitle: String,
+ val titles: List = emptyList()
)
@Serializable
-data class WebHomeViewV3(@ProtoNumber(2) val groups: List = emptyList())
+data class WebHomeViewV3(val groups: List = emptyList())
@Serializable
data class TitleDetailView(
- @ProtoNumber(1) val title: Title,
- @ProtoNumber(2) val titleImageUrl: String,
- @ProtoNumber(3) val overview: String,
- @ProtoNumber(4) val backgroundImageUrl: String,
- @ProtoNumber(5) val nextTimeStamp: Int = 0,
- @ProtoNumber(7) val viewingPeriodDescription: String = "",
- @ProtoNumber(8) val nonAppearanceInfo: String = "",
- @ProtoNumber(9) val firstChapterList: List = emptyList(),
- @ProtoNumber(10) val lastChapterList: List = emptyList(),
- @ProtoNumber(14) val isSimulReleased: Boolean = false,
- @ProtoNumber(17) val chaptersDescending: Boolean = true
+ val title: Title,
+ val titleImageUrl: String,
+ val overview: String,
+ val backgroundImageUrl: String,
+ val nextTimeStamp: Int = 0,
+ val viewingPeriodDescription: String = "",
+ val nonAppearanceInfo: String = "",
+ val firstChapterList: List = emptyList(),
+ val lastChapterList: List = emptyList(),
+ val isSimulReleased: Boolean = false,
+ val chaptersDescending: Boolean = true
) {
private val isWebtoon: Boolean
get() = firstChapterList.all(Chapter::isVerticalOnly) &&
@@ -89,76 +89,62 @@ data class TitleDetailView(
}
@Serializable
-data class MangaViewer(@ProtoNumber(1) val pages: List = emptyList())
+data class MangaViewer(val pages: List = emptyList())
@Serializable
data class Title(
- @ProtoNumber(1) val titleId: Int,
- @ProtoNumber(2) val name: String,
- @ProtoNumber(3) val author: String,
- @ProtoNumber(4) val portraitImageUrl: String,
- @ProtoNumber(5) val landscapeImageUrl: String,
- @ProtoNumber(6) val viewCount: Int = 0,
- @ProtoNumber(7) val language: Language? = Language.ENGLISH
+ val titleId: Int,
+ val name: String,
+ val author: String,
+ val portraitImageUrl: String,
+ val landscapeImageUrl: String,
+ val viewCount: Int = 0,
+ val language: Language? = Language.ENGLISH
)
-@Serializable
-enum class Language(val id: Int) {
- @ProtoNumber(0)
- ENGLISH(0),
-
- @ProtoNumber(1)
- SPANISH(1),
-
- @ProtoNumber(2)
- FRENCH(2),
-
- @ProtoNumber(3)
- INDONESIAN(4),
-
- @ProtoNumber(4)
- PORTUGUESE_BR(4),
-
- @ProtoNumber(5)
- RUSSIAN(5),
-
- @ProtoNumber(6)
- THAI(6)
+enum class Language {
+ ENGLISH,
+ SPANISH,
+ FRENCH,
+ INDONESIAN,
+ PORTUGUESE_BR,
+ RUSSIAN,
+ THAI
}
@Serializable
data class UpdatedTitleV2Group(
- @ProtoNumber(1) val groupName: String,
- @ProtoNumber(2) val titleGroups: List = emptyList()
+ val groupName: String,
+ val titleGroups: List = emptyList()
)
@Serializable
data class OriginalTitleGroup(
- @ProtoNumber(1) val theTitle: String,
- @ProtoNumber(3) val titles: List = emptyList()
+ val theTitle: String,
+ val titles: List = emptyList()
)
@Serializable
-data class UpdatedTitle(@ProtoNumber(1) val title: Title)
+data class UpdatedTitle(val title: Title)
@Serializable
data class Chapter(
- @ProtoNumber(1) val titleId: Int,
- @ProtoNumber(2) val chapterId: Int,
- @ProtoNumber(3) val name: String,
- @ProtoNumber(4) val subTitle: String? = null,
- @ProtoNumber(6) val startTimeStamp: Int,
- @ProtoNumber(7) val endTimeStamp: Int,
- @ProtoNumber(9) val isVerticalOnly: Boolean = false
+ val titleId: Int,
+ val chapterId: Int,
+ val name: String,
+ val subTitle: String? = null,
+ val startTimeStamp: Int,
+ val endTimeStamp: Int,
+ val isVerticalOnly: Boolean = false
)
@Serializable
-data class MangaPlusPage(@ProtoNumber(1) val page: MangaPage? = null)
+data class MangaPlusPage(val mangaPage: MangaPage? = null)
@Serializable
data class MangaPage(
- @ProtoNumber(1) val imageUrl: String,
- @ProtoNumber(2) val width: Int,
- @ProtoNumber(3) val height: Int,
- @ProtoNumber(5) val encryptionKey: String? = null
+ val imageUrl: String,
+ val width: Int,
+ val height: Int,
+ val encryptionKey: String? = null
)
diff --git a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusUrlActivity.kt b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusUrlActivity.kt
index 85bac6877..2d70c4a0a 100644
--- a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusUrlActivity.kt
+++ b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusUrlActivity.kt
@@ -14,18 +14,23 @@ class MangaPlusUrlActivity : Activity() {
val pathSegments = intent?.data?.pathSegments
if (pathSegments != null && pathSegments.size > 1) {
- val titleId = pathSegments[1]
+ val titleId = if (!pathSegments[1].equals("sns_share")) pathSegments[1] else
+ intent?.data?.getQueryParameter("title_id")
- val mainIntent = Intent().apply {
- action = "eu.kanade.tachiyomi.SEARCH"
- putExtra("query", MangaPlus.PREFIX_ID_SEARCH + titleId)
- putExtra("filter", packageName)
- }
+ if (titleId != null) {
+ val mainIntent = Intent().apply {
+ action = "eu.kanade.tachiyomi.SEARCH"
+ putExtra("query", MangaPlus.PREFIX_ID_SEARCH + titleId)
+ putExtra("filter", packageName)
+ }
- try {
- startActivity(mainIntent)
- } catch (e: ActivityNotFoundException) {
- Log.e("MangaPlusUrlActivity", e.toString())
+ try {
+ startActivity(mainIntent)
+ } catch (e: ActivityNotFoundException) {
+ Log.e("MangaPlusUrlActivity", e.toString())
+ }
+ } else {
+ Log.e("MangaPlusUrlActivity", "Missing title ID from the URL")
}
} else {
Log.e("MangaPlusUrlActivity", "Could not parse URI from intent $intent")