Manga Ship extension, DataImageInterceptor (#3177)

* Manga Ship extension, DataImageInterceptor

* tweak regex
This commit is contained in:
Mike 2020-05-18 22:30:26 -04:00 committed by GitHub
parent af17930c45
commit 7e9bb52cbc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 224 additions and 0 deletions

View File

@ -0,0 +1,30 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 27
buildToolsVersion '29.0.3'
defaultConfig {
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName '1.0.0'
}
buildTypes {
release {
minifyEnabled false
}
}
}
repositories {
mavenCentral()
}
dependencies {
compileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compileOnly 'com.squareup.okhttp3:okhttp:3.10.0'
compileOnly 'org.jsoup:jsoup:1.13.1'
}

View File

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="eu.kanade.tachiyomi.lib.dataimage" />

View File

@ -0,0 +1,65 @@
package eu.kanade.tachiyomi.lib.dataimage
import android.util.Base64
import okhttp3.Interceptor
import okhttp3.MediaType
import okhttp3.Protocol
import okhttp3.Response
import okhttp3.ResponseBody
import org.jsoup.nodes.Element
/**
* If a source provides images via a data:image string instead of a URL, use these functions and interceptor
*/
/**
* Use if the attribute tag could have a data:image string or URL
* Transforms data:image in to a fake URL that OkHttp won't die on
*/
fun Element.dataImageAsUrl(attr: String): String {
return if (this.attr(attr).startsWith("data")) {
"https://127.0.0.1/?" + this.attr(attr).substringAfter(":")
} else {
this.attr("abs:$attr")
}
}
/**
* Use if the attribute tag has a data:image string but real URLs are on a different attribute
*/
fun Element.dataImageAsUrlOrNull(attr: String): String? {
return if (this.attr(attr).startsWith("data")) {
"https://127.0.0.1/?" + this.attr(attr).substringAfter(":")
} else {
null
}
}
/**
* Interceptor that detects the URLs we created with the above functions, base64 decodes the data if necessary,
* and builds a response with a valid image that Tachiyomi can display
*/
class DataImageInterceptor : Interceptor {
private val mediaTypePattern = Regex("""(^[^;,]*)[;,]""")
override fun intercept(chain: Interceptor.Chain): Response {
val url = chain.request().url().toString()
return if (url.startsWith("https://127.0.0.1/?image")) {
val dataString = url.substringAfter("?")
val byteArray = if (dataString.contains("base64")) {
Base64.decode(dataString.substringAfter("base64,"), Base64.DEFAULT)
} else {
dataString.substringAfter(",").toByteArray()
}
val mediaType = MediaType.parse(mediaTypePattern.find(dataString)!!.value)
Response.Builder().body(ResponseBody.create(mediaType, byteArray))
.request(chain.request())
.protocol(Protocol.HTTP_1_0)
.code(200)
.message("")
.build()
} else {
chain.proceed(chain.request())
}
}
}

View File

@ -4,6 +4,9 @@ project(':lib-ratelimit').projectDir = new File("lib/ratelimit")
include ':duktape-stub'
project(':duktape-stub').projectDir = new File("lib/duktape-stub")
include ':lib-dataimage'
project(':lib-dataimage').projectDir = new File("lib/dataimage")
new File(rootDir, "src").eachDir { dir ->
dir.eachDir { subdir ->
String name = ":${dir.name}-${subdir.name}"

View File

@ -0,0 +1,17 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
ext {
appName = 'Tachiyomi: Manga Ship'
pkgNameSuffix = 'tr.mangaship'
extClass = '.MangaShip'
extVersionCode = 1
libVersion = '1.2'
}
dependencies {
implementation project(':lib-dataimage')
}
apply from: "$rootDir/common.gradle"

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@ -0,0 +1,107 @@
package eu.kanade.tachiyomi.extension.tr.mangaship
import eu.kanade.tachiyomi.lib.dataimage.DataImageInterceptor
import eu.kanade.tachiyomi.lib.dataimage.dataImageAsUrl
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.model.FilterList
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.ParsedHttpSource
import okhttp3.OkHttpClient
import okhttp3.Request
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
class MangaShip : ParsedHttpSource() {
override val name = "Manga Ship"
override val baseUrl = "https://www.mangaship.com"
override val lang = "tr"
override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addInterceptor(DataImageInterceptor())
.build()
// Popular
override fun popularMangaRequest(page: Int): Request {
return GET("$baseUrl/Tr/PopulerMangalar?page=$page", headers)
}
override fun popularMangaSelector() = "div.movie-item-contents"
override fun popularMangaFromElement(element: Element): SManga {
return SManga.create().apply {
element.select("div.movie-item-title a").let {
title = it.text()
setUrlWithoutDomain(it.attr("href"))
}
thumbnail_url = element.select("img").attr("abs:src")
}
}
override fun popularMangaNextPageSelector() = "li.active + li a:not(.lastpage)"
// Latest
override fun latestUpdatesRequest(page: Int): Request {
return GET("$baseUrl/Tr/YeniMangalar?page=$page", headers)
}
override fun latestUpdatesSelector() = popularMangaSelector()
override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element)
override fun latestUpdatesNextPageSelector() = popularMangaNextPageSelector()
// Search
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request {
return GET("$baseUrl/Tr/Search?kelime=$query&tur=Manga&page=$page", headers)
}
override fun searchMangaSelector() = popularMangaSelector()
override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element)
override fun searchMangaNextPageSelector() = popularMangaNextPageSelector()
// Details
override fun mangaDetailsParse(document: Document): SManga {
return SManga.create().apply {
thumbnail_url = document.select("div.dec-review-img img").attr("abs:src")
genre = document.select("div.col-md-10 li:contains(Kategori) div a").joinToString { it.text() }
author = document.select("div.col-md-10 li:contains(Yazar) div a").text()
description = document.select("div.details-dectiontion p").joinToString("\n") { it.text() }
}
}
// Chapters
override fun chapterListSelector() = "div.item > div"
override fun chapterFromElement(element: Element): SChapter {
return SChapter.create().apply {
element.select("div.plylist-single-content > a[title]").let {
name = it.text()
setUrlWithoutDomain(it.attr("href"))
}
}
}
// Pages
override fun pageListParse(document: Document): List<Page> {
return document.select("div.reading-content img").mapIndexed { i, img ->
Page(i, "", img.dataImageAsUrl("src"))
}
}
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used")
}