Update VIZ endpoints (#13788)
* Update VIZ endpoints. * Reword the page fetch error. Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
This commit is contained in:
parent
008e56a237
commit
7b4cd430a1
|
@ -0,0 +1,53 @@
|
|||
# VIZ Shonen Jump
|
||||
|
||||
Table of Content
|
||||
- [FAQ](#FAQ)
|
||||
- [Why are some chapters missing?](#why-are-some-chapters-missing)
|
||||
- [Guides](#Guides)
|
||||
- [Reading subscription-locked chapters](#reading-subscription-locked-chapters)
|
||||
|
||||
Don't find the question you are looking for? Go check out our general FAQs and Guides
|
||||
over at [Extension FAQ] or [Getting Started].
|
||||
|
||||
[Extension FAQ]: https://tachiyomi.org/help/faq/#extensions
|
||||
[Getting Started]: https://tachiyomi.org/help/guides/getting-started/#installation
|
||||
|
||||
## FAQ
|
||||
|
||||
### Why are some chapters missing?
|
||||
|
||||
VIZ Shonen Jump is a paid subscription-based service and you need an active monthly subscription to
|
||||
be able to read most of the chapters available in their service. These locked chapters will be
|
||||
filtered out from the chapter list by default if you're not signed in. To sign in with
|
||||
your existing account, follow the guide available below.
|
||||
|
||||
## Guides
|
||||
|
||||
### Reading subscription-locked chapters
|
||||
|
||||
The **VIZ Shonen Jump** sources allows the reading of paid chapters if you have
|
||||
an active paid subscription on their service.
|
||||
|
||||
> **Warning**
|
||||
> Although the extension supports sign in and reading subscription-locked chapters,
|
||||
> it's more a convenience. **We are not responsible if your account get banned** or something
|
||||
> related, as using other clients that are not their official app or website goes against
|
||||
> their Terms of Service. **It's recommended to use their official app or website instead**,
|
||||
> use this feature in the extension at your own risk.
|
||||
|
||||
Follow the following steps to be able to sign in and get access to the locked chapters:
|
||||
|
||||
1. Open the popular or latest section of the source.
|
||||
2. Open the WebView by clicking the button with a globe icon.
|
||||
3. Do the login with your existing account *(read the observations section)*.
|
||||
4. Close the WebView and refresh the chapter list of the titles
|
||||
you want to read the already paid chapters.
|
||||
|
||||
#### Observations
|
||||
|
||||
- You may sometime face the *"Your session has expired"* error. To fix it,
|
||||
you just need to open the WebView, await for the website to completely load and
|
||||
sign in again if you're not logged anymore. After that, you can close the
|
||||
WebView and try again.
|
||||
- The extension **will not** bypass any payment requirement. You still do need
|
||||
to have an active paid subscription to be able to read the locked chapters.
|
|
@ -6,11 +6,11 @@ ext {
|
|||
extName = 'VIZ Shonen Jump'
|
||||
pkgNameSuffix = 'en.vizshonenjump'
|
||||
extClass = '.VizShonenJump'
|
||||
extVersionCode = 14
|
||||
extVersionCode = 15
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.drewnoakes:metadata-extractor:2.14.0'
|
||||
implementation 'com.drewnoakes:metadata-extractor:2.18.0'
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
|
|
@ -22,23 +22,23 @@ class VizImageInterceptor : Interceptor {
|
|||
if (chain.request().url.queryParameter(SIGNATURE) == null)
|
||||
return response
|
||||
|
||||
val image = decodeImage(response.body!!.byteStream())
|
||||
val image = response.body!!.byteStream().decodeImage()
|
||||
val body = image.toResponseBody(MEDIA_TYPE)
|
||||
return response.newBuilder()
|
||||
.body(body)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun decodeImage(image: InputStream): ByteArray {
|
||||
private fun InputStream.decodeImage(): ByteArray {
|
||||
// See: https://stackoverflow.com/a/5924132
|
||||
// See: https://github.com/tachiyomiorg/tachiyomi-extensions/issues/2678#issuecomment-645857603
|
||||
val byteOutputStream = ByteArrayOutputStream()
|
||||
image.copyTo(byteOutputStream)
|
||||
copyTo(byteOutputStream)
|
||||
|
||||
val byteInputStreamForImage = ByteArrayInputStream(byteOutputStream.toByteArray())
|
||||
val byteInputStreamForMetadata = ByteArrayInputStream(byteOutputStream.toByteArray())
|
||||
|
||||
val imageData = getImageData(byteInputStreamForMetadata)
|
||||
val imageData = byteInputStreamForMetadata.getImageData()
|
||||
?: return byteOutputStream.toByteArray()
|
||||
|
||||
val input = BitmapFactory.decodeStream(byteInputStreamForImage)
|
||||
|
@ -127,8 +127,8 @@ class VizImageInterceptor : Interceptor {
|
|||
drawBitmap(from, srcRect, dstRect, null)
|
||||
}
|
||||
|
||||
private fun getImageData(inputStream: InputStream): ImageData? {
|
||||
val metadata = ImageMetadataReader.readMetadata(inputStream)
|
||||
private fun ByteArrayInputStream.getImageData(): ImageData? {
|
||||
val metadata = ImageMetadataReader.readMetadata(this)
|
||||
|
||||
val sizeDir = metadata.directories.firstOrNull {
|
||||
it.containsTag(ExifSubIFDDirectory.TAG_IMAGE_WIDTH) &&
|
||||
|
|
|
@ -9,15 +9,11 @@ import eu.kanade.tachiyomi.source.model.SChapter
|
|||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import eu.kanade.tachiyomi.source.online.ParsedHttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
import kotlinx.serialization.json.int
|
||||
import kotlinx.serialization.json.intOrNull
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
|
@ -27,6 +23,7 @@ import org.jsoup.nodes.Document
|
|||
import org.jsoup.nodes.Element
|
||||
import rx.Observable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -226,17 +223,16 @@ class VizShonenJump : ParsedHttpSource() {
|
|||
.substringAfterLast("/")
|
||||
.substringBefore("?")
|
||||
|
||||
return IntRange(0, pageCount)
|
||||
.map {
|
||||
val imageUrl = "$baseUrl/manga/get_manga_url".toHttpUrlOrNull()!!.newBuilder()
|
||||
.addQueryParameter("device_id", "3")
|
||||
.addQueryParameter("manga_id", mangaId)
|
||||
.addQueryParameter("page", it.toString())
|
||||
.addEncodedQueryParameter("referer", document.location())
|
||||
.toString()
|
||||
return IntRange(0, pageCount).map {
|
||||
val imageUrl = "$baseUrl/manga/get_manga_url".toHttpUrl().newBuilder()
|
||||
.addQueryParameter("device_id", "3")
|
||||
.addQueryParameter("manga_id", mangaId)
|
||||
.addQueryParameter("pages", it.toString())
|
||||
.addEncodedQueryParameter("referer", document.location())
|
||||
.toString()
|
||||
|
||||
Page(it, imageUrl)
|
||||
}
|
||||
Page(it, imageUrl)
|
||||
}
|
||||
}
|
||||
|
||||
override fun imageUrlRequest(page: Page): Request {
|
||||
|
@ -247,6 +243,7 @@ class VizShonenJump : ParsedHttpSource() {
|
|||
.toString()
|
||||
|
||||
val newHeaders = headersBuilder()
|
||||
.add("Accept", ACCEPT_JSON)
|
||||
.add("X-Client-Login", (loggedIn ?: false).toString())
|
||||
.add("X-Requested-With", "XMLHttpRequest")
|
||||
.set("Referer", referer)
|
||||
|
@ -256,10 +253,11 @@ class VizShonenJump : ParsedHttpSource() {
|
|||
}
|
||||
|
||||
override fun imageUrlParse(response: Response): String {
|
||||
val cdnUrl = response.body!!.string()
|
||||
val referer = response.request.header("Referer")!!
|
||||
val pageUrl = response.parseAs<VizPageUrlDto>()
|
||||
.data?.values?.firstOrNull() ?: throw Exception(FAILED_TO_FETCH_PAGE_URL)
|
||||
|
||||
return cdnUrl.toHttpUrlOrNull()!!.newBuilder()
|
||||
return pageUrl.toHttpUrl().newBuilder()
|
||||
.addEncodedQueryParameter("referer", referer)
|
||||
.toString()
|
||||
}
|
||||
|
@ -274,6 +272,7 @@ class VizShonenJump : ParsedHttpSource() {
|
|||
.toString()
|
||||
|
||||
val newHeaders = headersBuilder()
|
||||
.add("Accept", "*/*")
|
||||
.set("Referer", referer)
|
||||
.build()
|
||||
|
||||
|
@ -324,15 +323,9 @@ class VizShonenJump : ParsedHttpSource() {
|
|||
.addQueryParameter("manga_id", mangaId)
|
||||
.toString()
|
||||
val authCheckRequest = GET(authCheckUrl, authCheckHeaders)
|
||||
val authCheckResponse = chain.proceed(authCheckRequest)
|
||||
val authCheckJson = json.parseToJsonElement(authCheckResponse.body!!.string()).jsonObject
|
||||
val authCheckResponse = chain.proceed(authCheckRequest).parseAs<VizMangaAuthDto>()
|
||||
|
||||
authCheckResponse.close()
|
||||
|
||||
if (
|
||||
authCheckJson["ok"]!!.jsonPrimitive.int == 1 &&
|
||||
authCheckJson["archive_info"]!!.jsonObject["ok"]!!.jsonPrimitive.int == 1
|
||||
) {
|
||||
if (authCheckResponse.ok == 1 && authCheckResponse.archiveInfo?.ok == 1) {
|
||||
val newChapterUrl = chain.request().url.newBuilder()
|
||||
.removeAllQueryParameters("locked")
|
||||
.build()
|
||||
|
@ -343,18 +336,15 @@ class VizShonenJump : ParsedHttpSource() {
|
|||
return chain.proceed(newChapterRequest)
|
||||
}
|
||||
|
||||
if (
|
||||
authCheckJson["archive_info"]!!.jsonObject["err"] is JsonObject &&
|
||||
authCheckJson["archive_info"]!!.jsonObject["err"]!!.jsonObject["code"]?.jsonPrimitive?.intOrNull == 4 &&
|
||||
loggedIn == true
|
||||
) {
|
||||
throw Exception(SESSION_EXPIRED)
|
||||
if (authCheckResponse.archiveInfo?.error?.code == 4) {
|
||||
throw IOException(SESSION_EXPIRED)
|
||||
}
|
||||
|
||||
val errorMessage = authCheckJson["archive_info"]!!.jsonObject["err"]?.jsonObject
|
||||
?.get("msg")?.jsonPrimitive?.contentOrNull ?: AUTH_CHECK_FAILED
|
||||
throw IOException(authCheckResponse.archiveInfo?.error?.message ?: AUTH_CHECK_FAILED)
|
||||
}
|
||||
|
||||
throw Exception(errorMessage)
|
||||
private inline fun <reified T> Response.parseAs(): T = use {
|
||||
json.decodeFromString(it.body?.string().orEmpty())
|
||||
}
|
||||
|
||||
private fun String.toDate(): Long {
|
||||
|
@ -365,7 +355,7 @@ class VizShonenJump : ParsedHttpSource() {
|
|||
companion object {
|
||||
private const val ACCEPT_JSON = "application/json, text/javascript, */*; q=0.01"
|
||||
private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36"
|
||||
|
||||
private val DATE_FORMATTER by lazy {
|
||||
SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH)
|
||||
|
@ -374,6 +364,7 @@ class VizShonenJump : ParsedHttpSource() {
|
|||
private const val COUNTRY_NOT_SUPPORTED = "Your country is not supported by the service."
|
||||
private const val SESSION_EXPIRED = "Your session has expired, please log in through WebView again."
|
||||
private const val AUTH_CHECK_FAILED = "Something went wrong in the auth check."
|
||||
private const val FAILED_TO_FETCH_PAGE_URL = "Something went wrong while trying to fetch page."
|
||||
|
||||
private const val REFRESH_LOGIN_LINKS_URL = "account/refresh_login_links"
|
||||
private const val MANGA_AUTH_CHECK_URL = "manga/auth"
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package eu.kanade.tachiyomi.extension.en.vizshonenjump
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class VizPageUrlDto(
|
||||
val ok: Int = 0,
|
||||
val data: Map<Int, String>? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class VizMangaAuthDto(
|
||||
val ok: Int = 0,
|
||||
@SerialName("archive_info") val archiveInfo: VizArchiveInfoDto? = null
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class VizArchiveInfoDto(
|
||||
val ok: Int = 0,
|
||||
@SerialName("err") val error: VizErrorDto? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class VizErrorDto(
|
||||
val code: Int,
|
||||
@SerialName("msg") val message: String? = null
|
||||
)
|
Loading…
Reference in New Issue