Fix chapters download in VIZ due to expiration time (#16085)

Fix chapters download due to expiration time.
This commit is contained in:
Alessandro Jean 2023-04-17 17:24:28 -03:00 committed by GitHub
parent 64edb4a916
commit a912752ea5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 51 deletions

View File

@ -6,7 +6,7 @@ ext {
extName = 'VIZ Shonen Jump'
pkgNameSuffix = 'en.vizshonenjump'
extClass = '.VizShonenJump'
extVersionCode = 15
extVersionCode = 16
}
dependencies {

View File

@ -4,32 +4,62 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Rect
import com.drew.imaging.ImageMetadataReader
import com.drew.imaging.jpeg.JpegMetadataReader
import com.drew.metadata.exif.ExifSubIFDDirectory
import eu.kanade.tachiyomi.network.GET
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import uy.kohesive.injekt.injectLazy
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.InputStream
class VizImageInterceptor : Interceptor {
private val json: Json by injectLazy()
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
if (chain.request().url.queryParameter(SIGNATURE) == null) {
if (!chain.request().url.toString().contains(IMAGE_URL_ENDPOINT)) {
return response
}
val image = response.body.byteStream().decodeImage()
val imageUrl = imageUrlParse(response)
val imageResponse = chain.proceed(imageRequest(imageUrl))
val image = imageResponse.body.byteStream().decodeImage()
val body = image.toResponseBody(MEDIA_TYPE)
return response.newBuilder()
return imageResponse.newBuilder()
.body(body)
.header("Content-Type", MEDIA_TYPE.toString())
.build()
}
private fun imageUrlParse(response: Response): String {
return response.use { json.decodeFromString<VizPageUrlDto>(it.body.string()) }
.data?.values?.firstOrNull() ?: throw IOException(FAILED_TO_FETCH_PAGE_URL)
}
private fun imageRequest(url: String): Request {
val headers = Headers.Builder()
.add("Accept", "*/*")
.add("Origin", "https://www.viz.com")
.add("Referer", "https://www.viz.com/")
.add("User-Agent", VizShonenJump.USER_AGENT)
.build()
return GET(url, headers)
}
private fun InputStream.decodeImage(): ByteArray {
// See: https://stackoverflow.com/a/5924132
// See: https://github.com/tachiyomiorg/tachiyomi-extensions/issues/2678#issuecomment-645857603
@ -110,7 +140,7 @@ class VizImageInterceptor : Interceptor {
}
val output = ByteArrayOutputStream()
result.compress(Bitmap.CompressFormat.PNG, 100, output)
result.compress(Bitmap.CompressFormat.JPEG, 95, output)
return output.toByteArray()
}
@ -129,7 +159,7 @@ class VizImageInterceptor : Interceptor {
}
private fun ByteArrayInputStream.getImageData(): ImageData? {
val metadata = ImageMetadataReader.readMetadata(this)
val metadata = JpegMetadataReader.readMetadata(this)
val sizeDir = metadata.directories.firstOrNull {
it.containsTag(ExifSubIFDDirectory.TAG_IMAGE_WIDTH) &&
@ -155,8 +185,10 @@ class VizImageInterceptor : Interceptor {
}
companion object {
private const val SIGNATURE = "Signature"
private val MEDIA_TYPE = "image/png".toMediaTypeOrNull()
private const val IMAGE_URL_ENDPOINT = "get_manga_url"
private val MEDIA_TYPE = "image/jpeg".toMediaType()
private const val FAILED_TO_FETCH_PAGE_URL = "Something went wrong while trying to fetch page."
private const val CELL_WIDTH_COUNT = 10
private const val CELL_HEIGHT_COUNT = 15

View File

@ -228,55 +228,30 @@ class VizShonenJump : ParsedHttpSource() {
.addQueryParameter("device_id", "3")
.addQueryParameter("manga_id", mangaId)
.addQueryParameter("pages", it.toString())
.addEncodedQueryParameter("referer", document.location())
.toString()
Page(it, imageUrl)
// The image URL is actually fetched in the interceptor to avoid the short
// time expiration it have. Using the interceptor will guarantee the requests
// always follow the expected order, even when downloading:
// imageUrlRequest -> imageRequest -> decryption
// By using the url field of page, while downloading through the app it will
// do a batch call to get all imageUrl's first and then starts downloading it,
// but this takes time and the imageUrl's will be already expired. The reader
// doesn't face this issue as it follows the expected request order.
Page(it, imageUrl = imageUrl)
}
}
override fun imageUrlRequest(page: Page): Request {
val url = page.url.toHttpUrlOrNull()!!
val referer = url.queryParameter("referer")!!
val newUrl = url.newBuilder()
.removeAllEncodedQueryParameters("referer")
.toString()
val newHeaders = headersBuilder()
.add("Accept", ACCEPT_JSON)
.add("X-Client-Login", (loggedIn ?: false).toString())
.add("X-Requested-With", "XMLHttpRequest")
.set("Referer", referer)
.build()
return GET(newUrl, newHeaders)
}
override fun imageUrlParse(response: Response): String {
val referer = response.request.header("Referer")!!
val pageUrl = response.parseAs<VizPageUrlDto>()
.data?.values?.firstOrNull() ?: throw Exception(FAILED_TO_FETCH_PAGE_URL)
return pageUrl.toHttpUrl().newBuilder()
.addEncodedQueryParameter("referer", referer)
.toString()
}
override fun imageUrlParse(document: Document) = ""
override fun imageRequest(page: Page): Request {
val imageUrl = page.imageUrl!!.toHttpUrlOrNull()!!
val referer = imageUrl.queryParameter("referer")!!
val newImageUrl = imageUrl.newBuilder()
.removeAllEncodedQueryParameters("referer")
.toString()
val newHeaders = headersBuilder()
.add("Accept", "*/*")
.set("Referer", referer)
.add("Accept", ACCEPT_JSON)
.add("X-Client-Login", (loggedIn ?: false).toString())
.add("X-Requested-With", "XMLHttpRequest")
.build()
return GET(newImageUrl, newHeaders)
return GET(page.imageUrl!!, newHeaders)
}
private fun checkIfIsLoggedIn(chain: Interceptor.Chain? = null) {
@ -354,8 +329,8 @@ 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/106.0.0.0 Safari/537.36"
const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"
private val DATE_FORMATTER by lazy {
SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH)
@ -364,7 +339,6 @@ 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"