smbc: add extension for smbc-comics.com (#10192)
* smbc: add extension for smbc-comics.com Adds an extension for Saturday Morning Breakfast Comics * hiveworks: remove references to Saturday Morning Breakfast Comics Removes code that was made to handle reading SMBC specifically. If a user still has the comic in the Hiveworks extension, they'll get a warning to migrate to the SMBC extension.
This commit is contained in:
parent
804fd752e8
commit
b6bce67308
@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'Hiveworks Comics'
|
||||
extClass = '.Hiveworks'
|
||||
extVersionCode = 10
|
||||
extVersionCode = 11
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
||||
|
@ -23,6 +23,10 @@ import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Code that used to handle Saturday Morning Breakfast Comics has been split to its
|
||||
* own separate extension at eu.kanade.tachiyomi.extension.en.saturdaymorningbreakfastcomics
|
||||
*/
|
||||
class Hiveworks : ParsedHttpSource() {
|
||||
|
||||
// Info
|
||||
@ -39,28 +43,6 @@ class Hiveworks : ParsedHttpSource() {
|
||||
.readTimeout(1, TimeUnit.MINUTES)
|
||||
.retryOnConnectionFailure(true)
|
||||
.followRedirects(true)
|
||||
.addNetworkInterceptor { chain ->
|
||||
val request = chain.request()
|
||||
if (!request.url.toString().contains("smbc-comics")) {
|
||||
return@addNetworkInterceptor chain.proceed(request)
|
||||
}
|
||||
|
||||
val response = chain.proceed(request)
|
||||
// As of March 2025, SMBC chapter list page returns status code 500 even
|
||||
// though it still has correct data. Do not throw an error in this case.
|
||||
//
|
||||
// I reported this error to SMBC on 2025-05-28 and it was not fixed by
|
||||
// 2025-06-11, but even if it is fixed eventually, the same problem might
|
||||
// occur again in the future.
|
||||
if (response.code == 500) {
|
||||
val newResponse = response.newBuilder()
|
||||
.code(200)
|
||||
.build()
|
||||
newResponse
|
||||
} else {
|
||||
response
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
||||
// Popular
|
||||
@ -216,6 +198,7 @@ class Hiveworks : ParsedHttpSource() {
|
||||
when {
|
||||
"sssscomic" in uri.toString() -> uri.appendQueryParameter("id", "archive") // sssscomic uses query string in url
|
||||
"awkwardzombie" in uri.toString() -> uri.appendPath("awkward-zombie").appendPath("archive")
|
||||
"smbc-comics" in uri.toString() -> throw Exception("Migrate to the Saturday Morning Breakfast Comics extension to read this comic")
|
||||
else -> {
|
||||
uri.appendPath("comic")
|
||||
uri.appendPath("archive")
|
||||
@ -268,10 +251,6 @@ class Hiveworks : ParsedHttpSource() {
|
||||
|
||||
// Site specific pages can be added here
|
||||
when {
|
||||
"smbc-comics" in url -> {
|
||||
pages.add(Page(pages.size, "", document.select("div#aftercomic img").attr("src")))
|
||||
pages.add(Page(pages.size, "", smbcTextHandler(document)))
|
||||
}
|
||||
"sssscomic" in url -> {
|
||||
val urlPath = document.select("img.comicnormal").attr("src")
|
||||
val urlimg = response.request.url.resolve("../../$urlPath").toString()
|
||||
@ -497,40 +476,6 @@ class Hiveworks : ParsedHttpSource() {
|
||||
return chapters
|
||||
}
|
||||
|
||||
// Builds Image from mouse tooltip text
|
||||
private fun smbcTextHandler(document: Document): String {
|
||||
val title = document.select("title").text().trim()
|
||||
val altText = document.select("div#cc-comicbody img").attr("title")
|
||||
|
||||
val titleWords: Sequence<String> = title.splitToSequence(" ")
|
||||
val altTextWords: Sequence<String> = altText.splitToSequence(" ")
|
||||
|
||||
val builder = StringBuilder()
|
||||
var count = 0
|
||||
|
||||
for (i in titleWords) {
|
||||
if (count != 0 && count.rem(7) == 0) {
|
||||
builder.append("%0A")
|
||||
}
|
||||
builder.append(i).append("+")
|
||||
count++
|
||||
}
|
||||
builder.append("%0A%0A")
|
||||
|
||||
var charCount = 0
|
||||
|
||||
for (i in altTextWords) {
|
||||
if (charCount > 25) {
|
||||
builder.append("%0A")
|
||||
charCount = 0
|
||||
}
|
||||
builder.append(i).append("+")
|
||||
charCount += i.length + 1
|
||||
}
|
||||
|
||||
return "https://fakeimg.ryd.tools/1500x2126/ffffff/000000/?text=$builder&font_size=42&font=museo"
|
||||
}
|
||||
|
||||
// Used to throw custom error codes for http codes
|
||||
private fun Call.asObservableSuccess(): Observable<Response> {
|
||||
return asObservable().doOnNext { response ->
|
||||
|
8
src/en/saturdaymorningbreakfastcomics/build.gradle
Normal file
8
src/en/saturdaymorningbreakfastcomics/build.gradle
Normal file
@ -0,0 +1,8 @@
|
||||
ext {
|
||||
extName = 'Saturday Morning Breakfast Comics'
|
||||
extClass = '.SaturdayMorningBreakfastComics'
|
||||
extVersionCode = 1
|
||||
isNsfw = false
|
||||
}
|
||||
|
||||
apply from: "$rootDir/common.gradle"
|
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
@ -0,0 +1,134 @@
|
||||
package eu.kanade.tachiyomi.extension.en.saturdaymorningbreakfastcomics
|
||||
|
||||
import android.net.Uri.encode
|
||||
import eu.kanade.tachiyomi.network.asObservable
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
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 eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import keiyoushi.utils.tryParse
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import rx.Observable
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Split from Hiveworks extension
|
||||
*/
|
||||
class SaturdayMorningBreakfastComics : HttpSource() {
|
||||
|
||||
override val name = "Saturday Morning Breakfast Comics"
|
||||
|
||||
override val baseUrl = "https://smbc-comics.com"
|
||||
|
||||
override val lang = "en"
|
||||
|
||||
override val supportsLatest = false
|
||||
|
||||
private fun String.image() =
|
||||
"https://fakeimg.ryd.tools/1500x2126/ffffff/000000/?font=museo&font_size=42&text=" + encode(
|
||||
this,
|
||||
)
|
||||
|
||||
// Taken from XKCD
|
||||
private fun wordWrap(text: String) = buildString {
|
||||
var charCount = 0
|
||||
text.replace("\r\n", " ").split(' ').forEach { w ->
|
||||
if (charCount > 25) {
|
||||
append("\n")
|
||||
charCount = 0
|
||||
}
|
||||
append(w).append(' ')
|
||||
charCount += w.length + 1
|
||||
}
|
||||
}
|
||||
|
||||
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
|
||||
val manga = SManga.create().apply {
|
||||
title = "Saturday Morning Breakfast Comics"
|
||||
artist = "Zach Weinersmith"
|
||||
author = "Zach Weinersmith"
|
||||
status = SManga.ONGOING
|
||||
url = "/comic/archive"
|
||||
description =
|
||||
"SMBC is a daily comic strip about life, philosophy, science, mathematics, and dirty jokes."
|
||||
thumbnail_url = "https://fakeimg.ryd.tools/550x780/ffffff/6e7b91/?font=museo&text=SMBC"
|
||||
}
|
||||
|
||||
return Observable.just(MangasPage(listOf(manga), false))
|
||||
}
|
||||
|
||||
override fun fetchSearchManga(
|
||||
page: Int,
|
||||
query: String,
|
||||
filters: FilterList,
|
||||
): Observable<MangasPage> = Observable.just(MangasPage(emptyList(), false))
|
||||
|
||||
override fun fetchMangaDetails(manga: SManga): Observable<SManga> = Observable.just(manga)
|
||||
|
||||
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
|
||||
return client.newCall(chapterListRequest(manga))
|
||||
.asObservable()
|
||||
.map { response ->
|
||||
if (!response.isSuccessful && response.code != 500) {
|
||||
response.close()
|
||||
throw Exception("HTTP ${response.code}")
|
||||
}
|
||||
response.asJsoup().select("option[value*=\"comic/\"]")
|
||||
.mapIndexed { index, element ->
|
||||
val chapter = SChapter.create()
|
||||
chapter.url = "/${element.attr("value")}"
|
||||
val (date, title) = element.text().split(" - ")
|
||||
chapter.name = title
|
||||
chapter.date_upload = dateFormat.tryParse(date)
|
||||
chapter.chapter_number = (index + 1).toFloat()
|
||||
chapter
|
||||
}
|
||||
.reversed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun pageListParse(response: Response): List<Page> {
|
||||
val document = response.asJsoup()
|
||||
val pages = mutableListOf<Page>()
|
||||
val image = document.select("img#cc-comic")
|
||||
pages.add(Page(0, "", image.attr("abs:src")))
|
||||
if (image.hasAttr("title")) {
|
||||
pages.add(Page(1, "", wordWrap(image.attr("title")).image()))
|
||||
}
|
||||
pages.add(Page(2, "", document.select("#aftercomic > img").attr("abs:src")))
|
||||
return pages
|
||||
}
|
||||
|
||||
private val dateFormat by lazy {
|
||||
SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH)
|
||||
}
|
||||
|
||||
override fun chapterListParse(response: Response): List<SChapter> =
|
||||
throw UnsupportedOperationException()
|
||||
|
||||
override fun popularMangaRequest(page: Int): Request = throw UnsupportedOperationException()
|
||||
|
||||
override fun searchMangaParse(response: Response): MangasPage =
|
||||
throw UnsupportedOperationException()
|
||||
|
||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request =
|
||||
throw UnsupportedOperationException()
|
||||
|
||||
override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException()
|
||||
|
||||
override fun latestUpdatesParse(response: Response): MangasPage =
|
||||
throw UnsupportedOperationException()
|
||||
|
||||
override fun latestUpdatesRequest(page: Int): Request = throw UnsupportedOperationException()
|
||||
|
||||
override fun mangaDetailsParse(response: Response): SManga =
|
||||
throw UnsupportedOperationException()
|
||||
|
||||
override fun popularMangaParse(response: Response): MangasPage =
|
||||
throw UnsupportedOperationException()
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user