diff --git a/lib/textinterceptor/build.gradle.kts b/lib/textinterceptor/build.gradle.kts
new file mode 100644
index 000000000..e8086827c
--- /dev/null
+++ b/lib/textinterceptor/build.gradle.kts
@@ -0,0 +1,22 @@
+plugins {
+ id("com.android.library")
+ kotlin("android")
+}
+
+android {
+ compileSdk = AndroidConfig.compileSdk
+
+ defaultConfig {
+ minSdk = AndroidConfig.minSdk
+ targetSdk = AndroidConfig.targetSdk
+ }
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ compileOnly(libs.kotlin.stdlib)
+ compileOnly(libs.okhttp)
+}
diff --git a/lib/textinterceptor/src/main/AndroidManifest.xml b/lib/textinterceptor/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..6d52001ad
--- /dev/null
+++ b/lib/textinterceptor/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/lib/textinterceptor/src/main/java/eu/kanade/tachiyomi/lib/textinterceptor/TextInterceptor.kt b/lib/textinterceptor/src/main/java/eu/kanade/tachiyomi/lib/textinterceptor/TextInterceptor.kt
new file mode 100644
index 000000000..b614c03b9
--- /dev/null
+++ b/lib/textinterceptor/src/main/java/eu/kanade/tachiyomi/lib/textinterceptor/TextInterceptor.kt
@@ -0,0 +1,121 @@
+package eu.kanade.tachiyomi.lib.textinterceptor
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Typeface
+import android.net.Uri
+import android.os.Build
+import android.text.Html
+import android.text.Layout
+import android.text.StaticLayout
+import android.text.TextPaint
+import okhttp3.Interceptor
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.Protocol
+import okhttp3.Response
+import okhttp3.ResponseBody.Companion.toResponseBody
+import java.io.ByteArrayOutputStream
+
+class TextInterceptor : Interceptor {
+ // With help from:
+ // https://github.com/tachiyomiorg/tachiyomi-extensions/pull/13304#issuecomment-1234532897
+ // https://medium.com/over-engineering/drawing-multiline-text-to-canvas-on-android-9b98f0bfa16a
+
+ companion object {
+ // Designer values:
+ private const val WIDTH: Int = 1000
+ private const val X_PADDING: Float = 50f
+ private const val Y_PADDING: Float = 25f
+ private const val HEADING_FONT_SIZE: Float = 36f
+ private const val BODY_FONT_SIZE: Float = 30f
+ private const val SPACING_MULT: Float = 1.1f
+ private const val SPACING_ADD: Float = 2f
+
+ // No need to touch this one:
+ private const val HOST = TextInterceptorHelper.HOST
+ }
+
+ override fun intercept(chain: Interceptor.Chain): Response {
+ val request = chain.request()
+ val url = request.url
+ if (url.host != HOST) return chain.proceed(request)
+
+ val creator = textFixer("Author's Notes from ${url.pathSegments[0]}")
+ val story = textFixer(url.pathSegments[1])
+
+ // Heading
+ val paintHeading = TextPaint().apply {
+ color = Color.BLACK
+ textSize = HEADING_FONT_SIZE
+ typeface = Typeface.DEFAULT_BOLD
+ isAntiAlias = true
+ }
+
+ @Suppress("DEPRECATION")
+ val heading: StaticLayout = StaticLayout(
+ creator, paintHeading, (WIDTH - 2 * X_PADDING).toInt(),
+ Layout.Alignment.ALIGN_NORMAL, SPACING_MULT, SPACING_ADD, true
+ )
+
+ // Body
+ val paintBody = TextPaint().apply {
+ color = Color.BLACK
+ textSize = BODY_FONT_SIZE
+ typeface = Typeface.DEFAULT
+ isAntiAlias = true
+ }
+
+ @Suppress("DEPRECATION")
+ val body: StaticLayout = StaticLayout(
+ story, paintBody, (WIDTH - 2 * X_PADDING).toInt(),
+ Layout.Alignment.ALIGN_NORMAL, SPACING_MULT, SPACING_ADD, true
+ )
+
+ // Image building
+ val imgHeight: Int = (heading.height + body.height + 2 * Y_PADDING).toInt()
+ val bitmap: Bitmap = Bitmap.createBitmap(WIDTH, imgHeight, Bitmap.Config.ARGB_8888)
+ val canvas: Canvas = Canvas(bitmap)
+
+ // Image drawing
+ canvas.drawColor(Color.WHITE)
+ heading.draw(canvas, X_PADDING, Y_PADDING)
+ body.draw(canvas, X_PADDING, Y_PADDING + heading.height.toFloat())
+
+ // Image converting & returning
+ val stream = ByteArrayOutputStream()
+ bitmap.compress(Bitmap.CompressFormat.PNG, 0, stream)
+ val responseBody = stream.toByteArray().toResponseBody("image/png".toMediaType())
+ return Response.Builder()
+ .request(request)
+ .protocol(Protocol.HTTP_1_1)
+ .code(200)
+ .message("OK")
+ .body(responseBody)
+ .build()
+ }
+
+ private fun textFixer(htmlString: String): String {
+ return if (Build.VERSION.SDK_INT >= 24) {
+ Html.fromHtml(htmlString , Html.FROM_HTML_MODE_LEGACY).toString()
+ } else {
+ Html.fromHtml(htmlString).toString()
+ }
+ }
+
+ private fun StaticLayout.draw(canvas: Canvas, x: Float, y: Float) {
+ canvas.save()
+ canvas.translate(x, y)
+ this.draw(canvas)
+ canvas.restore()
+ }
+}
+
+object TextInterceptorHelper {
+
+ const val HOST = "tachiyomi-lib-textinterceptor"
+
+ fun createUrl(creator: String, text: String): String {
+ return "http://$HOST/" + Uri.encode(creator) + "/" + Uri.encode(text)
+ }
+}
diff --git a/multisrc/overrides/webtoons/webtoons/additional.gradle b/multisrc/overrides/webtoons/webtoons/additional.gradle
new file mode 100644
index 000000000..8de52e9e0
--- /dev/null
+++ b/multisrc/overrides/webtoons/webtoons/additional.gradle
@@ -0,0 +1,3 @@
+dependencies {
+ implementation(project(':lib-textinterceptor'))
+}
diff --git a/multisrc/overrides/webtoons/webtoons/src/WebtoonsFactory.kt b/multisrc/overrides/webtoons/webtoons/src/WebtoonsFactory.kt
index 20f3a88d0..9a3664e75 100644
--- a/multisrc/overrides/webtoons/webtoons/src/WebtoonsFactory.kt
+++ b/multisrc/overrides/webtoons/webtoons/src/WebtoonsFactory.kt
@@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.extension.all.webtoons
-import eu.kanade.tachiyomi.multisrc.webtoons.Webtoons
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceFactory
import java.text.SimpleDateFormat
@@ -18,8 +17,8 @@ class WebtoonsFactory : SourceFactory {
WebtoonsDE(),
)
}
-class WebtoonsEN : Webtoons("Webtoons.com", "https://www.webtoons.com", "en")
-class WebtoonsID : Webtoons("Webtoons.com", "https://www.webtoons.com", "id") {
+class WebtoonsEN : WebtoonsSrc("Webtoons.com", "https://www.webtoons.com", "en")
+class WebtoonsID : WebtoonsSrc("Webtoons.com", "https://www.webtoons.com", "id") {
// Override ID as part of the name was removed to be more consiten with other enteries
override val id: Long = 8749627068478740298
@@ -36,8 +35,8 @@ class WebtoonsID : Webtoons("Webtoons.com", "https://www.webtoons.com", "id") {
return GregorianCalendar(year.toInt(), monthIndex, day.toInt()).time.time
}
}
-class WebtoonsTH : Webtoons("Webtoons.com", "https://www.webtoons.com", "th", dateFormat = SimpleDateFormat("d MMM yyyy", Locale("th")))
-class WebtoonsES : Webtoons("Webtoons.com", "https://www.webtoons.com", "es") {
+class WebtoonsTH : WebtoonsSrc("Webtoons.com", "https://www.webtoons.com", "th", dateFormat = SimpleDateFormat("d MMM yyyy", Locale("th")))
+class WebtoonsES : WebtoonsSrc("Webtoons.com", "https://www.webtoons.com", "es") {
// Android seems to be unable to parse es dates like Indonesian; we'll use a short hard-coded table instead.
private val dateMap: Array = arrayOf(
"ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sep", "oct", "nov", "dic"
@@ -50,9 +49,9 @@ class WebtoonsES : Webtoons("Webtoons.com", "https://www.webtoons.com", "es") {
return GregorianCalendar(year.toInt(), monthIndex, day.toInt()).time.time
}
}
-class WebtoonsFR : Webtoons("Webtoons.com", "https://www.webtoons.com", "fr", dateFormat = SimpleDateFormat("d MMM yyyy", Locale.FRENCH))
-class WebtoonsZH : Webtoons("Webtoons.com", "https://www.webtoons.com", "zh-Hant", "zh-hant", "zh_TW", SimpleDateFormat("yyyy/MM/dd", Locale.TRADITIONAL_CHINESE)) {
+class WebtoonsFR : WebtoonsSrc("Webtoons.com", "https://www.webtoons.com", "fr", dateFormat = SimpleDateFormat("d MMM yyyy", Locale.FRENCH))
+class WebtoonsZH : WebtoonsSrc("Webtoons.com", "https://www.webtoons.com", "zh-Hant", "zh-hant", "zh_TW", SimpleDateFormat("yyyy/MM/dd", Locale.TRADITIONAL_CHINESE)) {
// Due to lang code getting more specific
override val id: Long = 2959982438613576472
}
-class WebtoonsDE : Webtoons("Webtoons.com", "https://www.webtoons.com", "de", dateFormat = SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN))
+class WebtoonsDE : WebtoonsSrc("Webtoons.com", "https://www.webtoons.com", "de", dateFormat = SimpleDateFormat("dd.MM.yyyy", Locale.GERMAN))
diff --git a/multisrc/overrides/webtoons/webtoons/src/WebtoonsSrc.kt b/multisrc/overrides/webtoons/webtoons/src/WebtoonsSrc.kt
new file mode 100644
index 000000000..2698a9cc0
--- /dev/null
+++ b/multisrc/overrides/webtoons/webtoons/src/WebtoonsSrc.kt
@@ -0,0 +1,97 @@
+package eu.kanade.tachiyomi.extension.all.webtoons
+
+import android.app.Application
+import android.content.SharedPreferences
+import androidx.preference.PreferenceScreen
+import androidx.preference.SwitchPreferenceCompat
+import eu.kanade.tachiyomi.lib.textinterceptor.TextInterceptor
+import eu.kanade.tachiyomi.lib.textinterceptor.TextInterceptorHelper
+import eu.kanade.tachiyomi.multisrc.webtoons.Webtoons
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.source.ConfigurableSource
+import eu.kanade.tachiyomi.source.model.Page
+import kotlinx.serialization.json.jsonObject
+import kotlinx.serialization.json.jsonPrimitive
+import okhttp3.OkHttpClient
+import org.jsoup.nodes.Document
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+import java.text.SimpleDateFormat
+import java.util.Locale
+
+open class WebtoonsSrc(
+ override val name: String,
+ override val baseUrl: String,
+ override val lang: String,
+ langCode: String = lang,
+ override val localeForCookie: String = lang,
+ dateFormat: SimpleDateFormat = SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH)
+) : ConfigurableSource, Webtoons(name, baseUrl, lang, langCode, localeForCookie, dateFormat) {
+
+ override val client: OkHttpClient = super.client.newBuilder()
+ .addInterceptor(TextInterceptor())
+ .build()
+
+ private val preferences: SharedPreferences by lazy {
+ Injekt.get().getSharedPreferences("source_$id", 0x0000)
+ }
+
+ override fun setupPreferenceScreen(screen: PreferenceScreen) {
+ val authorsNotesPref = SwitchPreferenceCompat(screen.context).apply {
+ key = SHOW_AUTHORS_NOTES_KEY
+ title = "Show author's notes"
+ summary = "Enable to see the author's notes at the end of chapters (if they're there)."
+ setDefaultValue(false)
+
+ setOnPreferenceChangeListener { _, newValue ->
+ val checkValue = newValue as Boolean
+ preferences.edit().putBoolean(SHOW_AUTHORS_NOTES_KEY, checkValue).commit()
+ }
+ }
+ screen.addPreference(authorsNotesPref)
+ }
+
+ private fun showAuthorsNotesPref() = preferences.getBoolean(SHOW_AUTHORS_NOTES_KEY, false)
+
+ override fun pageListParse(document: Document): List {
+ var pages = document.select("div#_imageList > img").mapIndexed { i, element -> Page(i, "", element.attr("data-url")) }
+
+ if (showAuthorsNotesPref()) {
+ val note = document.select("div.comment_area div.info_area p").text()
+
+ if (note.isNotEmpty()) {
+
+ val creator = document.select("div.creator_note span.author a").text().trim()
+
+ pages = pages + Page(
+ pages.size, "",
+ TextInterceptorHelper.createUrl(creator, note)
+ )
+ }
+ }
+
+ if (pages.isNotEmpty()) { return pages }
+
+ val docString = document.toString()
+
+ val docUrlRegex = Regex("documentURL:.*?'(.*?)'")
+ val motiontoonPathRegex = Regex("jpg:.*?'(.*?)\\{")
+
+ val docUrl = docUrlRegex.find(docString)!!.destructured.toList()[0]
+ val motiontoonPath = motiontoonPathRegex.find(docString)!!.destructured.toList()[0]
+ val motiontoonResponse = client.newCall(GET(docUrl, headers)).execute()
+
+ val motiontoonJson = json.parseToJsonElement(motiontoonResponse.body!!.string()).jsonObject
+ val motiontoonImages = motiontoonJson["assets"]!!.jsonObject["image"]!!.jsonObject
+
+ return motiontoonImages.entries
+ .filter { it.key.contains("layer") }
+ .mapIndexed { i, entry ->
+ Page(i, "", motiontoonPath + entry.value.jsonPrimitive.content)
+ }
+ }
+
+ companion object {
+ private const val SHOW_AUTHORS_NOTES_KEY = "showAuthorsNotes"
+ }
+}
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/Webtoons.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/Webtoons.kt
index 7cbe7a84a..07adc7162 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/Webtoons.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/Webtoons.kt
@@ -1,19 +1,6 @@
package eu.kanade.tachiyomi.multisrc.webtoons
-import android.app.Application
-import android.content.SharedPreferences
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Typeface
-import android.net.Uri
-import android.text.Layout
-import android.text.StaticLayout
-import android.text.TextPaint
-import androidx.preference.PreferenceScreen
-import androidx.preference.SwitchPreferenceCompat
import eu.kanade.tachiyomi.network.GET
-import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.Filter.Header
import eu.kanade.tachiyomi.source.model.Filter.Select
import eu.kanade.tachiyomi.source.model.Filter.Separator
@@ -33,19 +20,13 @@ import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor
-import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
-import okhttp3.Protocol
import okhttp3.Request
import okhttp3.Response
-import okhttp3.ResponseBody.Companion.toResponseBody
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
-import uy.kohesive.injekt.Injekt
-import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
-import java.io.ByteArrayOutputStream
import java.net.SocketException
import java.text.ParseException
import java.text.SimpleDateFormat
@@ -59,7 +40,7 @@ open class Webtoons(
open val langCode: String = lang,
open val localeForCookie: String = lang,
private val dateFormat: SimpleDateFormat = SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH)
-) : ConfigurableSource, ParsedHttpSource() {
+) : ParsedHttpSource() {
override val supportsLatest = true
@@ -84,7 +65,6 @@ open class Webtoons(
}
)
.addInterceptor(::sslRetryInterceptor)
- .addInterceptor(TextInterceptor)
.build()
// m.webtoons.com throws an SSL error that can be solved by a simple retry
@@ -112,16 +92,12 @@ open class Webtoons(
}
}
- private val json: Json by injectLazy()
+ val json: Json by injectLazy()
override fun popularMangaSelector() = "not using"
override fun latestUpdatesSelector() = "div#dailyList > $day li > a"
- private val preferences: SharedPreferences by lazy {
- Injekt.get().getSharedPreferences("source_$id", 0x0000)
- }
-
override fun headersBuilder(): Headers.Builder = super.headersBuilder()
.add("Referer", "https://www.webtoons.com/$langCode/")
@@ -129,23 +105,6 @@ open class Webtoons(
.add("Referer", "https://m.webtoons.com")
.build()
- override fun setupPreferenceScreen(screen: PreferenceScreen) {
- val authorsNotesPref = SwitchPreferenceCompat(screen.context).apply {
- key = SHOW_AUTHORS_NOTES_KEY
- title = "Show author's notes"
- summary = "Enable to see the author's notes at the end of chapters (if they're there)."
- setDefaultValue(false)
-
- setOnPreferenceChangeListener { _, newValue ->
- val checkValue = newValue as Boolean
- preferences.edit().putBoolean(SHOW_AUTHORS_NOTES_KEY, checkValue).commit()
- }
- }
- screen.addPreference(authorsNotesPref)
- }
-
- private fun showAuthorsNotesPref() = preferences.getBoolean(SHOW_AUTHORS_NOTES_KEY, false)
-
override fun popularMangaRequest(page: Int) = GET("$baseUrl/$langCode/dailySchedule", headers)
override fun popularMangaParse(response: Response): MangasPage {
@@ -317,20 +276,6 @@ open class Webtoons(
override fun pageListParse(document: Document): List {
var pages = document.select("div#_imageList > img").mapIndexed { i, element -> Page(i, "", element.attr("data-url")) }
- if (showAuthorsNotesPref()) {
- val note = document.select("div.comment_area div.info_area p").text()
-
- if (note.isNotEmpty()) {
-
- val creator = document.select("div.creator_note span.author a").text().trim()
-
- pages = pages + Page(
- pages.size, "",
- "http://note/" + Uri.encode(creator) + "/" + Uri.encode(note)
- )
- }
- }
-
if (pages.isNotEmpty()) { return pages }
val docString = document.toString()
@@ -354,102 +299,5 @@ open class Webtoons(
companion object {
const val URL_SEARCH_PREFIX = "url:"
- private const val SHOW_AUTHORS_NOTES_KEY = "showAuthorsNotes"
- }
-
- // TODO: Split off into library file or something, because Webtoons is using the exact same TextInterceptor
- // src/en/tapastic/src/eu/kanade/tachiyomi/extension/en/tapastic/Tapastic.kt
- object TextInterceptor : Interceptor {
- // With help from:
- // https://github.com/tachiyomiorg/tachiyomi-extensions/pull/13304#issuecomment-1234532897
- // https://medium.com/over-engineering/drawing-multiline-text-to-canvas-on-android-9b98f0bfa16a
-
- // Designer values:
- private const val WIDTH: Int = 1000
- private const val X_PADDING: Float = 50f
- private const val Y_PADDING: Float = 25f
- private const val HEADING_FONT_SIZE: Float = 36f
- private const val BODY_FONT_SIZE: Float = 30f
- private const val SPACING_MULT: Float = 1.1f
- private const val SPACING_ADD: Float = 2f
-
- // No need to touch this one:
- private const val HOST = "note"
-
- override fun intercept(chain: Interceptor.Chain): Response {
- val request = chain.request()
- val url = request.url
- if (url.host != HOST) return chain.proceed(request)
-
- val creator = textFixer("Author's Notes from ${url.pathSegments[0]}")
- val story = textFixer(url.pathSegments[1])
-
- // Heading
- val paintHeading = TextPaint().apply {
- color = Color.BLACK
- textSize = HEADING_FONT_SIZE
- typeface = Typeface.DEFAULT_BOLD
- isAntiAlias = true
- }
-
- @Suppress("DEPRECATION")
- val heading: StaticLayout = StaticLayout(
- creator, paintHeading, (WIDTH - 2 * X_PADDING).toInt(),
- Layout.Alignment.ALIGN_NORMAL, SPACING_MULT, SPACING_ADD, true
- )
-
- // Body
- val paintBody = TextPaint().apply {
- color = Color.BLACK
- textSize = BODY_FONT_SIZE
- typeface = Typeface.DEFAULT
- isAntiAlias = true
- }
-
- @Suppress("DEPRECATION")
- val body: StaticLayout = StaticLayout(
- story, paintBody, (WIDTH - 2 * X_PADDING).toInt(),
- Layout.Alignment.ALIGN_NORMAL, SPACING_MULT, SPACING_ADD, true
- )
-
- // Image building
- val imgHeight: Int = (heading.height + body.height + 2 * Y_PADDING).toInt()
- val bitmap: Bitmap = Bitmap.createBitmap(WIDTH, imgHeight, Bitmap.Config.ARGB_8888)
- val canvas: Canvas = Canvas(bitmap)
-
- // Image drawing
- canvas.drawColor(Color.WHITE)
- heading.draw(canvas, X_PADDING, Y_PADDING)
- body.draw(canvas, X_PADDING, Y_PADDING + heading.height.toFloat())
-
- // Image converting & returning
- val stream: ByteArrayOutputStream = ByteArrayOutputStream()
- bitmap.compress(Bitmap.CompressFormat.PNG, 0, stream)
- val responseBody = stream.toByteArray().toResponseBody("image/png".toMediaType())
- return Response.Builder()
- .request(request)
- .protocol(Protocol.HTTP_1_1)
- .code(200)
- .message("OK")
- .body(responseBody)
- .build()
- }
-
- private fun textFixer(t: String): String {
- return t
- .replace("&", "&")
- .replace("'", "'")
- .replace(""", "\"")
- .replace("<", "<")
- .replace(">", ">")
- .replace("\\s*
\\s*".toRegex(), "\n")
- }
-
- private fun StaticLayout.draw(canvas: Canvas, x: Float, y: Float) {
- canvas.save()
- canvas.translate(x, y)
- this.draw(canvas)
- canvas.restore()
- }
}
}
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/WebtoonsTranslate.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/WebtoonsTranslate.kt
index 642e11cdb..7c5b967d1 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/WebtoonsTranslate.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/WebtoonsTranslate.kt
@@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga
-import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.contentOrNull
@@ -24,7 +23,6 @@ import okhttp3.Response
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import rx.Observable
-import uy.kohesive.injekt.injectLazy
open class WebtoonsTranslate(
override val name: String,
@@ -42,8 +40,6 @@ open class WebtoonsTranslate(
private val pageSize = 24
- private val json: Json by injectLazy()
-
override fun headersBuilder(): Headers.Builder = super.headersBuilder()
.removeAll("Referer")
.add("Referer", mobileBaseUrl.toString())
diff --git a/settings.gradle.kts b/settings.gradle.kts
index a1f75eb5b..12157fe60 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,6 +1,6 @@
include(":core")
-listOf("dataimage", "unpacker", "cryptoaes").forEach {
+listOf("dataimage", "unpacker", "cryptoaes", "textinterceptor").forEach {
include(":lib-$it")
project(":lib-$it").projectDir = File("lib/$it")
}
diff --git a/src/en/tapastic/build.gradle b/src/en/tapastic/build.gradle
index c9597ef64..f2a387f54 100644
--- a/src/en/tapastic/build.gradle
+++ b/src/en/tapastic/build.gradle
@@ -11,3 +11,7 @@ ext {
}
apply from: "$rootDir/common.gradle"
+
+dependencies {
+ implementation(project(':lib-textinterceptor'))
+}
\ No newline at end of file
diff --git a/src/en/tapastic/src/eu/kanade/tachiyomi/extension/en/tapastic/Tapastic.kt b/src/en/tapastic/src/eu/kanade/tachiyomi/extension/en/tapastic/Tapastic.kt
index e4e38dd65..0766618e7 100644
--- a/src/en/tapastic/src/eu/kanade/tachiyomi/extension/en/tapastic/Tapastic.kt
+++ b/src/en/tapastic/src/eu/kanade/tachiyomi/extension/en/tapastic/Tapastic.kt
@@ -2,18 +2,12 @@ package eu.kanade.tachiyomi.extension.en.tapastic
import android.app.Application
import android.content.SharedPreferences
-import android.graphics.Bitmap
-import android.graphics.Bitmap.CompressFormat
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Typeface
import android.net.Uri
-import android.text.Layout
-import android.text.StaticLayout
-import android.text.TextPaint
import android.webkit.CookieManager
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
+import eu.kanade.tachiyomi.lib.textinterceptor.TextInterceptor
+import eu.kanade.tachiyomi.lib.textinterceptor.TextInterceptorHelper
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.Filter
@@ -33,20 +27,15 @@ import okhttp3.CookieJar
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
-import okhttp3.Interceptor
-import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
-import okhttp3.Protocol
import okhttp3.Request
import okhttp3.Response
-import okhttp3.ResponseBody.Companion.toResponseBody
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
-import java.io.ByteArrayOutputStream
import java.text.SimpleDateFormat
import java.util.Locale
@@ -108,7 +97,7 @@ class Tapastic : ConfigurableSource, ParsedHttpSource() {
}
}
)
- .addInterceptor(TextInterceptor)
+ .addInterceptor(TextInterceptor())
.build()
private val preferences: SharedPreferences by lazy {
@@ -372,7 +361,7 @@ class Tapastic : ConfigurableSource, ParsedHttpSource() {
pages = pages + Page(
pages.size, "",
- "http://note/" + Uri.encode(creator) + "/" + Uri.encode(episodeStory)
+ TextInterceptorHelper.createUrl(creator, episodeStory)
)
}
}
@@ -528,100 +517,4 @@ class Tapastic : ConfigurableSource, ParsedHttpSource() {
private const val SHOW_AUTHORS_NOTES_KEY = "showAuthorsNotes"
private const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:105.0) Gecko/20100101 Firefox/105.0"
}
-
- // TODO: Split off into library file or something, because Webtoons is using the exact same TextInterceptor
- // multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/Webtoons.kt
- object TextInterceptor : Interceptor {
- // With help from:
- // https://github.com/tachiyomiorg/tachiyomi-extensions/pull/13304#issuecomment-1234532897
- // https://medium.com/over-engineering/drawing-multiline-text-to-canvas-on-android-9b98f0bfa16a
-
- // Designer values:
- private const val WIDTH: Int = 1000
- private const val X_PADDING: Float = 50f
- private const val Y_PADDING: Float = 25f
- private const val HEADING_FONT_SIZE: Float = 36f
- private const val BODY_FONT_SIZE: Float = 30f
- private const val SPACING_MULT: Float = 1.1f
- private const val SPACING_ADD: Float = 2f
-
- // No need to touch this one:
- private const val HOST = "note"
-
- override fun intercept(chain: Interceptor.Chain): Response {
- val request = chain.request()
- val url = request.url
- if (url.host != HOST) return chain.proceed(request)
-
- val creator = textFixer("Author's Notes from ${url.pathSegments[0]}")
- val story = textFixer(url.pathSegments[1])
-
- // Heading
- val paintHeading = TextPaint().apply {
- color = Color.BLACK
- textSize = HEADING_FONT_SIZE
- typeface = Typeface.DEFAULT_BOLD
- isAntiAlias = true
- }
-
- @Suppress("DEPRECATION")
- val heading: StaticLayout = StaticLayout(
- creator, paintHeading, (WIDTH - 2 * X_PADDING).toInt(),
- Layout.Alignment.ALIGN_NORMAL, SPACING_MULT, SPACING_ADD, true
- )
-
- // Body
- val paintBody = TextPaint().apply {
- color = Color.BLACK
- textSize = BODY_FONT_SIZE
- typeface = Typeface.DEFAULT
- isAntiAlias = true
- }
-
- @Suppress("DEPRECATION")
- val body: StaticLayout = StaticLayout(
- story, paintBody, (WIDTH - 2 * X_PADDING).toInt(),
- Layout.Alignment.ALIGN_NORMAL, SPACING_MULT, SPACING_ADD, true
- )
-
- // Image building
- val imgHeight: Int = (heading.height + body.height + 2 * Y_PADDING).toInt()
- val bitmap: Bitmap = Bitmap.createBitmap(WIDTH, imgHeight, Bitmap.Config.ARGB_8888)
- val canvas: Canvas = Canvas(bitmap)
-
- // Image drawing
- canvas.drawColor(Color.WHITE)
- heading.draw(canvas, X_PADDING, Y_PADDING)
- body.draw(canvas, X_PADDING, Y_PADDING + heading.height.toFloat())
-
- // Image converting & returning
- val stream: ByteArrayOutputStream = ByteArrayOutputStream()
- bitmap.compress(CompressFormat.PNG, 0, stream)
- val responseBody = stream.toByteArray().toResponseBody("image/png".toMediaType())
- return Response.Builder()
- .request(request)
- .protocol(Protocol.HTTP_1_1)
- .code(200)
- .message("OK")
- .body(responseBody)
- .build()
- }
-
- private fun textFixer(t: String): String {
- return t
- .replace("&", "&")
- .replace("'", "'")
- .replace(""", "\"")
- .replace("<", "<")
- .replace(">", ">")
- .replace("\\s*
\\s*".toRegex(), "\n")
- }
-
- private fun StaticLayout.draw(canvas: Canvas, x: Float, y: Float) {
- canvas.save()
- canvas.translate(x, y)
- this.draw(canvas)
- canvas.restore()
- }
- }
}