Webtoons: Optional author's notes at end of chapters (&Tapas bugfix) (#10398)

* Tapas: Bugfix in author's notes

Sometimes a word would get cut off

* Tapas: Bump version for buxfix

* Webtoons: Bump version for Author's Notes PR

* Webtoons: Optional author's notes @end of chapters

Previous implementation of this feature into Tapas: https://github.com/tachiyomiorg/tachiyomi-extensions/pull/10366
This commit is contained in:
altaccosc 2022-01-09 20:36:33 +01:00 committed by GitHub
parent 605f137756
commit f5583a4ed7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 82 additions and 8 deletions

View File

@ -1,6 +1,12 @@
package eu.kanade.tachiyomi.multisrc.webtoons package eu.kanade.tachiyomi.multisrc.webtoons
import android.app.Application
import android.content.SharedPreferences
import android.net.Uri
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import eu.kanade.tachiyomi.network.GET 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.Header
import eu.kanade.tachiyomi.source.model.Filter.Select import eu.kanade.tachiyomi.source.model.Filter.Select
import eu.kanade.tachiyomi.source.model.Filter.Separator import eu.kanade.tachiyomi.source.model.Filter.Separator
@ -25,11 +31,14 @@ import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Locale import java.util.Locale
import kotlin.math.ceil
open class Webtoons( open class Webtoons(
override val name: String, override val name: String,
@ -38,7 +47,7 @@ open class Webtoons(
open val langCode: String = lang, open val langCode: String = lang,
open val localeForCookie: String = lang, open val localeForCookie: String = lang,
private val dateFormat: SimpleDateFormat = SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH) private val dateFormat: SimpleDateFormat = SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH)
) : ParsedHttpSource() { ) : ConfigurableSource, ParsedHttpSource() {
override val supportsLatest = true override val supportsLatest = true
@ -86,6 +95,10 @@ open class Webtoons(
override fun latestUpdatesSelector() = "div#dailyList > $day li > a" override fun latestUpdatesSelector() = "div#dailyList > $day li > a"
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
override fun headersBuilder(): Headers.Builder = super.headersBuilder() override fun headersBuilder(): Headers.Builder = super.headersBuilder()
.add("Referer", "https://www.webtoons.com/$langCode/") .add("Referer", "https://www.webtoons.com/$langCode/")
@ -93,6 +106,23 @@ open class Webtoons(
.add("Referer", "https://m.webtoons.com") .add("Referer", "https://m.webtoons.com")
.build() .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 popularMangaRequest(page: Int) = GET("$baseUrl/$langCode/dailySchedule", headers)
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
@ -261,8 +291,48 @@ open class Webtoons(
override fun chapterListRequest(manga: SManga) = GET("https://m.webtoons.com" + manga.url, mobileHeaders) override fun chapterListRequest(manga: SManga) = GET("https://m.webtoons.com" + manga.url, mobileHeaders)
private fun wordwrap(t: String, lineWidth: Int) = buildString {
// TODO: Split off into library file or something, because Tapastic is using the exact same wordwrap and toImage functions
// src/en/tapastic/src/eu/kanade/tachiyomi/extension/en/tapastic/Tapastic.kt
val text = t.replace("\n", "\n ")
var charCount = 0
text.split(" ").forEach { w ->
if (w.contains("\n")) {
charCount = 0
}
if (charCount > lineWidth) {
append("\n")
charCount = 0
}
append("$w ")
charCount += w.length + 1
}
}
private fun toImage(t: String, fontSize: Int, bold: Boolean = false): String {
val text = wordwrap(t.replace("&amp;", "&").replace("\\s*<br>\\s*".toRegex(), "\n"), 65)
val imgHeight = (text.lines().size + 2) * fontSize * 1.3
return "https://placehold.jp/" + fontSize + "/ffffff/000000/1500x" + ceil(imgHeight).toInt() + ".png?" +
"css=%7B%22text-align%22%3A%22%20left%22%2C%22padding-left%22%3A%22%203%25%22" + (if (bold) "%2C%22font-weight%22%3A%22%20600%22" else "") + "%7D&" +
"text=" + Uri.encode(text)
}
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
val pages = document.select("div#_imageList > img").mapIndexed { i, element -> Page(i, "", element.attr("data-url")) } var pages = document.select("div#_imageList > img").mapIndexed { i, element -> Page(i, "", element.attr("data-url")) }
if (showAuthorsNotesPref()) {
val note = document.select("div.creator_note > p").text()
if (note.isNotEmpty()) {
val noteImage = toImage(note, 42)
val creator = document.select("div.creator_note > h2").html().replace("<span>Creator</span>", "").trim()
val creatorImage = toImage("Author's Notes from $creator", 43, true)
pages = pages + Page(pages.size, "", creatorImage)
pages = pages + Page(pages.size, "", noteImage)
}
}
if (pages.isNotEmpty()) { return pages } if (pages.isNotEmpty()) { return pages }
@ -287,5 +357,6 @@ open class Webtoons(
companion object { companion object {
const val URL_SEARCH_PREFIX = "url:" const val URL_SEARCH_PREFIX = "url:"
private const val SHOW_AUTHORS_NOTES_KEY = "showAuthorsNotes"
} }
} }

View File

@ -13,7 +13,7 @@ class WebtoonsGenerator : ThemeSourceGenerator {
override val baseVersionCode: Int = 2 override val baseVersionCode: Int = 2
override val sources = listOf( override val sources = listOf(
MultiLang("Webtoons.com", "https://www.webtoons.com", listOf("en", "fr", "es", "id", "th", "zh"), className = "WebtoonsFactory", pkgName = "webtoons", overrideVersionCode = 30), MultiLang("Webtoons.com", "https://www.webtoons.com", listOf("en", "fr", "es", "id", "th", "zh"), className = "WebtoonsFactory", pkgName = "webtoons", overrideVersionCode = 31),
SingleLang("Dongman Manhua", "https://www.dongmanmanhua.cn", "zh") SingleLang("Dongman Manhua", "https://www.dongmanmanhua.cn", "zh")
) )

View File

@ -6,7 +6,7 @@ ext {
extName = 'Tapas' extName = 'Tapas'
pkgNameSuffix = 'en.tapastic' pkgNameSuffix = 'en.tapastic'
extClass = '.Tapastic' extClass = '.Tapastic'
extVersionCode = 14 extVersionCode = 15
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -295,6 +295,8 @@ class Tapastic : ConfigurableSource, ParsedHttpSource() {
// Pages // Pages
// TODO: Split off into library file or something, because Webtoons is using the exact same wordwrap and toImage functions
// multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/Webtoons.kt
private fun wordwrap(t: String, lineWidth: Int) = buildString { private fun wordwrap(t: String, lineWidth: Int) = buildString {
val text = t.replace("\n", "\n ") val text = t.replace("\n", "\n ")
var charCount = 0 var charCount = 0
@ -305,9 +307,8 @@ class Tapastic : ConfigurableSource, ParsedHttpSource() {
if (charCount > lineWidth) { if (charCount > lineWidth) {
append("\n") append("\n")
charCount = 0 charCount = 0
} else {
append("$w ")
} }
append("$w ")
charCount += w.length + 1 charCount += w.length + 1
} }
} }
@ -327,12 +328,14 @@ class Tapastic : ConfigurableSource, ParsedHttpSource() {
if (showAuthorsNotesPref()) { if (showAuthorsNotesPref()) {
val episodeStory = document.select("p.js-episode-story").html() val episodeStory = document.select("p.js-episode-story").html()
if (episodeStory.isNotEmpty()) { if (episodeStory.isNotEmpty()) {
val storyImage = toImage(episodeStory, 42)
val creator = document.select("a.name.js-fb-tracking")[0].text() val creator = document.select("a.name.js-fb-tracking")[0].text()
val creatorImage = toImage("Author's Notes from $creator", 43, true) val creatorImage = toImage("Author's Notes from $creator", 43, true)
pages = pages + Page(pages.size, "", creatorImage)
val storyImage = toImage(episodeStory, 42) pages = pages + Page(pages.size, "", creatorImage)
pages = pages + Page(pages.size, "", storyImage) pages = pages + Page(pages.size, "", storyImage)
} }
} }