Fix "Open in browser" wrong URL and add missing Referer headers at MangaPlus. (#2667)

This commit is contained in:
Alessandro Jean 2020-04-14 23:15:35 -03:00 committed by GitHub
parent e106314621
commit 6c1e518184
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 30 deletions

View File

@ -6,7 +6,7 @@ ext {
appName = 'Tachiyomi: MANGA Plus by SHUEISHA' appName = 'Tachiyomi: MANGA Plus by SHUEISHA'
pkgNameSuffix = 'all.mangaplus' pkgNameSuffix = 'all.mangaplus'
extClass = '.MangaPlusFactory' extClass = '.MangaPlusFactory'
extVersionCode = 6 extVersionCode = 7
libVersion = '1.2' libVersion = '1.2'
} }

View File

@ -6,6 +6,9 @@ import android.os.Build
import android.support.v7.preference.CheckBoxPreference import android.support.v7.preference.CheckBoxPreference
import android.support.v7.preference.ListPreference import android.support.v7.preference.ListPreference
import android.support.v7.preference.PreferenceScreen import android.support.v7.preference.PreferenceScreen
import androidx.preference.CheckBoxPreference as AndroidXCheckBoxPreference
import androidx.preference.ListPreference as AndroidXListPreference
import androidx.preference.PreferenceScreen as AndroidXPreferenceScreen
import com.google.gson.Gson import com.google.gson.Gson
import com.squareup.duktape.Duktape import com.squareup.duktape.Duktape
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
@ -36,15 +39,15 @@ abstract class MangaPlus(
override val name = "Manga Plus by Shueisha" override val name = "Manga Plus by Shueisha"
override val baseUrl = "https://jumpg-webapi.tokyo-cdn.com/api" override val baseUrl = "https://mangaplus.shueisha.co.jp"
override val supportsLatest = true override val supportsLatest = true
override fun headersBuilder(): Headers.Builder = Headers.Builder() override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("Origin", WEB_URL) .add("Origin", baseUrl)
.add("Referer", WEB_URL) .add("Referer", baseUrl)
.add("User-Agent", USER_AGENT) .add("User-Agent", USER_AGENT)
.add("SESSION-TOKEN", UUID.randomUUID().toString()) .add("Session-Token", UUID.randomUUID().toString())
override val client: OkHttpClient = network.client.newBuilder() override val client: OkHttpClient = network.client.newBuilder()
.addInterceptor { imageIntercept(it) } .addInterceptor { imageIntercept(it) }
@ -67,7 +70,11 @@ abstract class MangaPlus(
get() = if (preferences.getBoolean("${SPLIT_PREF_KEY}_$lang", SPLIT_PREF_DEFAULT_VALUE)) "yes" else "no" get() = if (preferences.getBoolean("${SPLIT_PREF_KEY}_$lang", SPLIT_PREF_DEFAULT_VALUE)) "yes" else "no"
override fun popularMangaRequest(page: Int): Request { override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/title_list/ranking", headers) val newHeaders = headersBuilder()
.set("Referer", "$baseUrl/manga_list/hot")
.build()
return GET("$API_URL/title_list/ranking", newHeaders)
} }
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
@ -90,7 +97,11 @@ abstract class MangaPlus(
} }
override fun latestUpdatesRequest(page: Int): Request { override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/web/web_home?lang=$internalLang", headers) val newHeaders = headersBuilder()
.set("Referer", "$baseUrl/updates")
.build()
return GET("$API_URL/web/web_home?lang=$internalLang", newHeaders)
} }
override fun latestUpdatesParse(response: Response): MangasPage { override fun latestUpdatesParse(response: Response): MangasPage {
@ -121,7 +132,11 @@ abstract class MangaPlus(
} }
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
return GET("$baseUrl/title_list/all", headers) val newHeaders = headersBuilder()
.set("Referer", "$baseUrl/manga_list/all")
.build()
return GET("$API_URL/title_list/all", newHeaders)
} }
override fun searchMangaParse(response: Response): MangasPage { override fun searchMangaParse(response: Response): MangasPage {
@ -144,8 +159,13 @@ abstract class MangaPlus(
} }
private fun titleDetailsRequest(manga: SManga): Request { private fun titleDetailsRequest(manga: SManga): Request {
val mangaId = manga.url.substringAfterLast("/") val titleId = manga.url.substringAfterLast("/")
return GET("$baseUrl/title_detail?title_id=$mangaId", headers)
val newHeaders = headersBuilder()
.set("Referer", "$baseUrl/titles/$titleId")
.build()
return GET("$API_URL/title_detail?title_id=$titleId", newHeaders)
} }
// Workaround to allow "Open in browser" use the real URL. // Workaround to allow "Open in browser" use the real URL.
@ -157,8 +177,10 @@ abstract class MangaPlus(
} }
} }
// Always returns the real URL for the "Open in browser". override fun mangaDetailsRequest(manga: SManga): Request {
override fun mangaDetailsRequest(manga: SManga): Request = GET(WEB_URL + manga.url, headers) // Remove the '#' and map to the new url format used in website.
return GET(baseUrl + manga.url.substring(1), headers)
}
override fun mangaDetailsParse(response: Response): SManga { override fun mangaDetailsParse(response: Response): SManga {
val result = response.asProto() val result = response.asProto()
@ -206,7 +228,18 @@ abstract class MangaPlus(
override fun pageListRequest(chapter: SChapter): Request { override fun pageListRequest(chapter: SChapter): Request {
val chapterId = chapter.url.substringAfterLast("/") val chapterId = chapter.url.substringAfterLast("/")
return GET("$baseUrl/manga_viewer?chapter_id=$chapterId&split=$splitImages&img_quality=$imageResolution", headers)
val newHeaders = headersBuilder()
.set("Referer", "$baseUrl/viewer/$chapterId")
.build()
val url = HttpUrl.parse("$API_URL/manga_viewer")!!.newBuilder()
.addQueryParameter("chapter_id", chapterId)
.addQueryParameter("split", splitImages)
.addQueryParameter("img_quality", imageResolution)
.toString()
return GET(url, newHeaders)
} }
override fun pageListParse(response: Response): List<Page> { override fun pageListParse(response: Response): List<Page> {
@ -215,31 +248,31 @@ abstract class MangaPlus(
if (result.success == null) if (result.success == null)
throw Exception(result.error!!.langPopup.body) throw Exception(result.error!!.langPopup.body)
val referer = response.request().header("Referer")!!
return result.success.mangaViewer!!.pages return result.success.mangaViewer!!.pages
.mapNotNull { it.page } .mapNotNull { it.page }
.mapIndexed { i, page -> .mapIndexed { i, page ->
val encryptionKey = if (page.encryptionKey == null) "" else "&encryptionKey=${page.encryptionKey}" val encryptionKey = if (page.encryptionKey == null) "" else "&encryptionKey=${page.encryptionKey}"
Page(i, "", "${page.imageUrl}$encryptionKey") Page(i, referer, "${page.imageUrl}$encryptionKey")
} }
} }
override fun fetchImageUrl(page: Page): Observable<String> { override fun fetchImageUrl(page: Page): Observable<String> = Observable.just(page.imageUrl!!)
return Observable.just(page.imageUrl!!)
}
override fun imageUrlParse(response: Response): String = "" override fun imageUrlParse(response: Response): String = ""
override fun imageRequest(page: Page): Request { override fun imageRequest(page: Page): Request {
val newHeaders = Headers.Builder() val newHeaders = headersBuilder()
.add("Referer", WEB_URL) .removeAll("Origin")
.add("User-Agent", USER_AGENT) .set("Referer", page.url)
.build() .build()
return GET(page.imageUrl!!, newHeaders) return GET(page.imageUrl!!, newHeaders)
} }
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) { override fun setupPreferenceScreen(screen: AndroidXPreferenceScreen) {
val resolutionPref = androidx.preference.ListPreference(screen.context).apply { val resolutionPref = AndroidXListPreference(screen.context).apply {
key = "${RESOLUTION_PREF_KEY}_$lang" key = "${RESOLUTION_PREF_KEY}_$lang"
title = RESOLUTION_PREF_TITLE title = RESOLUTION_PREF_TITLE
entries = RESOLUTION_PREF_ENTRIES entries = RESOLUTION_PREF_ENTRIES
@ -254,7 +287,7 @@ abstract class MangaPlus(
preferences.edit().putString("${RESOLUTION_PREF_KEY}_$lang", entry).commit() preferences.edit().putString("${RESOLUTION_PREF_KEY}_$lang", entry).commit()
} }
} }
val splitPref = androidx.preference.CheckBoxPreference(screen.context).apply { val splitPref = AndroidXCheckBoxPreference(screen.context).apply {
key = "${SPLIT_PREF_KEY}_$lang" key = "${SPLIT_PREF_KEY}_$lang"
title = SPLIT_PREF_TITLE title = SPLIT_PREF_TITLE
summary = SPLIT_PREF_SUMMARY summary = SPLIT_PREF_SUMMARY
@ -305,22 +338,28 @@ abstract class MangaPlus(
private fun imageIntercept(chain: Interceptor.Chain): Response { private fun imageIntercept(chain: Interceptor.Chain): Response {
var request = chain.request() var request = chain.request()
if (!request.url().queryParameterNames().contains("encryptionKey")) { if (request.url().queryParameter("encryptionKey") == null)
return chain.proceed(request) return chain.proceed(request)
}
val encryptionKey = request.url().queryParameter("encryptionKey")!! val encryptionKey = request.url().queryParameter("encryptionKey")!!
// Change the url and remove the encryptionKey to avoid detection. // Change the url and remove the encryptionKey to avoid detection.
val newUrl = request.url().newBuilder().removeAllQueryParameters("encryptionKey").build() val newUrl = request.url().newBuilder()
request = request.newBuilder().url(newUrl).build() .removeAllQueryParameters("encryptionKey")
.build()
request = request.newBuilder()
.url(newUrl)
.build()
val response = chain.proceed(request) val response = chain.proceed(request)
val contentType = response.header("Content-Type", "image/jpeg")!! val contentType = response.header("Content-Type", "image/jpeg")!!
val image = decodeImage(encryptionKey, response.body()!!.bytes()) val image = decodeImage(encryptionKey, response.body()!!.bytes())
val body = ResponseBody.create(MediaType.parse(contentType), image) val body = ResponseBody.create(MediaType.parse(contentType), image)
return response.newBuilder().body(body).build()
return response.newBuilder()
.body(body)
.build()
} }
private fun decodeImage(encryptionKey: String, image: ByteArray): ByteArray { private fun decodeImage(encryptionKey: String, image: ByteArray): ByteArray {
@ -385,9 +424,9 @@ abstract class MangaPlus(
} }
companion object { companion object {
private const val WEB_URL = "https://mangaplus.shueisha.co.jp" private const val API_URL = "https://jumpg-webapi.tokyo-cdn.com/api"
private const val IMAGES_WESERV_URL = "https://images.weserv.nl" private const val IMAGES_WESERV_URL = "https://images.weserv.nl"
private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36" private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.92 Safari/537.36"
private val HEX_GROUP = "(.{1,2})".toRegex() private val HEX_GROUP = "(.{1,2})".toRegex()