Manga Ship extension, DataImageInterceptor (#3177)
* Manga Ship extension, DataImageInterceptor * tweak regex
This commit is contained in:
parent
af17930c45
commit
7e9bb52cbc
|
@ -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'
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="eu.kanade.tachiyomi.lib.dataimage" />
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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}"
|
||||
|
|
|
@ -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 |
|
@ -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")
|
||||
}
|
Loading…
Reference in New Issue