Monochrome: new multisrc (#10015)

This commit is contained in:
ObserverOfTime 2021-12-08 12:58:53 +02:00 committed by GitHub
parent ae9f7024d7
commit 5c5e997c04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 258 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -0,0 +1,54 @@
package eu.kanade.tachiyomi.extension.en.monochromecustom
import android.app.Application
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.multisrc.monochrome.MonochromeCMS
import eu.kanade.tachiyomi.source.ConfigurableSource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MonochromeCustom : ConfigurableSource,
MonochromeCMS("Monochrome Custom", "", "en") {
override val baseUrl by lazy {
preferences.getString("baseUrl", DEMO_BASE_URL)!!
}
override val apiUrl by lazy {
preferences.getString("apiUrl", DEMO_API_URL)!!
}
private val preferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)!!
}
override fun setupPreferenceScreen(screen: PreferenceScreen) {
EditTextPreference(screen.context).apply {
key = "baseUrl"
title = "Frontend URL"
summary = "The base URL of your Monochrome installation"
setDefaultValue(DEMO_BASE_URL)
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putString("baseUrl", newValue as String).commit()
}
}.let(screen::addPreference)
EditTextPreference(screen.context).apply {
key = "apiUrl"
title = "API URL"
summary = "The API URL of your Monochrome installation"
setDefaultValue(DEMO_API_URL)
setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putString("apiUrl", newValue as String).commit()
}
}.let(screen::addPreference)
}
companion object {
private const val DEMO_BASE_URL = "https://monochromecms.netlify.app"
private const val DEMO_API_URL = "https://api-3qnqyl7llq-lz.a.run.app"
}
}

View File

@ -0,0 +1,62 @@
package eu.kanade.tachiyomi.multisrc.monochrome
import kotlinx.serialization.Serializable
import java.text.DecimalFormat
import java.text.SimpleDateFormat
import java.util.Locale
private const val ISO_DATE = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS"
private val dateFormat = SimpleDateFormat(ISO_DATE, Locale.ROOT)
private val decimalFormat = DecimalFormat("#.##")
@Serializable
data class Results(
private val offset: Int,
private val limit: Int,
private val results: List<Manga>,
private val total: Int
) : Iterable<Manga> by results {
val hasNext: Boolean
get() = total > results.size + offset * limit
}
@Serializable
data class Manga(
val title: String,
val description: String,
val author: String,
val artist: String,
val status: String,
val id: String,
private val version: Int
) {
val cover: String
get() = "/media/$id/cover.jpg?version=$version"
}
@Serializable
data class Chapter(
private val name: String,
private val volume: Int?,
val number: Float,
val scanGroup: String,
private val id: String,
private val version: Int,
private val length: Int,
private val uploadTime: String
) {
val title: String
get() = buildString {
if (volume != null) append("Vol ").append(volume).append(" ")
append("Chapter ").append(decimalFormat.format(number))
if (name.isNotEmpty()) append(" - ").append(name)
}
val timestamp: Long
get() = dateFormat.parse(uploadTime)?.time ?: 0L
val parts: String
get() = "/$id|$version|$length"
}

View File

@ -0,0 +1,120 @@
package eu.kanade.tachiyomi.multisrc.monochrome
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess
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 kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import okhttp3.Headers
import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.injectLazy
open class MonochromeCMS(
override val name: String,
override val baseUrl: String,
override val lang: String
) : HttpSource() {
override val supportsLatest = false
protected open val apiUrl by lazy {
baseUrl.replaceFirst("://", "://api.")
}
private val json by injectLazy<Json>()
override fun headersBuilder() =
Headers.Builder().set("Referer", "$baseUrl/")
override fun fetchPopularManga(page: Int) =
fetchSearchManga(page, "", FilterList())
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
GET("$apiUrl/manga?limit=10&offset=${10 * (page - 1)}&title=$query", headers)
override fun searchMangaParse(response: Response) =
response.decode<Results>().let { results ->
val mp = results.map {
SManga.create().apply {
url = it.id
title = it.title
author = it.author
artist = it.artist
description = it.description
thumbnail_url = apiUrl + it.cover
status = when (it.status) {
"ongoing", "hiatus" -> SManga.ONGOING
"completed", "cancelled" -> SManga.COMPLETED
else -> SManga.UNKNOWN
}
}
}
MangasPage(mp, results.hasNext)
}
// Request the actual manga URL for the webview
override fun mangaDetailsRequest(manga: SManga) =
GET("$baseUrl/manga/${manga.url}", headers)
override fun fetchMangaDetails(manga: SManga) =
Observable.just(manga.apply { initialized = true })!!
override fun chapterListRequest(manga: SManga) =
GET("$apiUrl/manga/${manga.url}/chapters", headers)
override fun fetchChapterList(manga: SManga) =
client.newCall(chapterListRequest(manga)).asObservableSuccess().map {
it.decode<List<Chapter>>().map { ch ->
SChapter.create().apply {
name = ch.title
url = manga.url + ch.parts
chapter_number = ch.number
date_upload = ch.timestamp
scanlator = ch.scanGroup
}
}
}!!
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
val (uuid, version, length) = chapter.url.split('|')
val pages = IntRange(1, length.toInt()).map {
Page(it, "", "$apiUrl/media/$uuid/$it.jpg?version=$version")
}
return Observable.just(pages)
}
private inline fun <reified T> Response.decode() =
json.decodeFromString<T>(body!!.string())
override fun popularMangaRequest(page: Int) =
throw UnsupportedOperationException("Not used!")
override fun latestUpdatesRequest(page: Int) =
throw UnsupportedOperationException("Not used!")
override fun popularMangaParse(response: Response) =
throw UnsupportedOperationException("Not used!")
override fun latestUpdatesParse(response: Response) =
throw UnsupportedOperationException("Not used!")
override fun mangaDetailsParse(response: Response) =
throw UnsupportedOperationException("Not used!")
override fun chapterListParse(response: Response) =
throw UnsupportedOperationException("Not used!")
override fun pageListRequest(chapter: SChapter) =
throw UnsupportedOperationException("Not used!")
override fun pageListParse(response: Response) =
throw UnsupportedOperationException("Not used!")
override fun imageUrlParse(response: Response) =
throw UnsupportedOperationException("Not used!")
}

View File

@ -0,0 +1,22 @@
package eu.kanade.tachiyomi.multisrc.monochrome
import generator.ThemeSourceData.SingleLang
import generator.ThemeSourceGenerator
class MonochromeGenerator : ThemeSourceGenerator {
override val themePkg = "monochrome"
override val themeClass = "MonochromeCMS"
override val baseVersionCode = 1
override val sources = listOf(
SingleLang("Monochrome Scans", "https://manga.d34d.one", "en"),
SingleLang("Monochrome Custom", "", "en")
)
companion object {
@JvmStatic
fun main(args: Array<String>) = MonochromeGenerator().createAll()
}
}