Implement client-side search in Bakkin (#8351)

This commit is contained in:
ObserverOfTime 2021-08-02 17:59:23 +03:00 committed by GitHub
parent 604f86f4d8
commit 28234597b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 56 deletions

View File

@ -11,19 +11,17 @@ class BakkinSelfHosted : BakkinReaderX("Bakkin Self-hosted", "", "en") {
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
super.setupPreferenceScreen(screen) super.setupPreferenceScreen(screen)
screen.addPreference( EditTextPreference(screen.context).apply {
EditTextPreference(screen.context).apply { key = "baseUrl"
key = "baseUrl" title = "Custom URL"
title = "Custom URL" summary = "Connect to a self-hosted Bakkin Reader X server"
summary = "Connect to a self-hosted Bakkin Reader X server" setDefaultValue("http://127.0.0.1/")
setDefaultValue("http://127.0.0.1/")
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
// Make sure the URL ends with one slash // Make sure the URL ends with one slash
val url = (newValue as String).trimEnd('/') + '/' val url = (newValue as String).trimEnd('/') + '/'
preferences.edit().putString("baseUrl", url).commit() preferences.edit().putString("baseUrl", url).commit()
}
} }
) }.let(screen::addPreference)
} }
} }

View File

@ -8,7 +8,7 @@ class BakkinGenerator : ThemeSourceGenerator {
override val themeClass = "BakkinReaderX" override val themeClass = "BakkinReaderX"
override val baseVersionCode: Int = 1 override val baseVersionCode = 2
override val sources = listOf( override val sources = listOf(
SingleLang("Bakkin", "https://bakkin.moe/reader/", "en"), SingleLang("Bakkin", "https://bakkin.moe/reader/", "en"),
@ -16,6 +16,7 @@ class BakkinGenerator : ThemeSourceGenerator {
) )
companion object { companion object {
@JvmStatic fun main(args: Array<String>) = BakkinGenerator().createAll() @JvmStatic
fun main(args: Array<String>) = BakkinGenerator().createAll()
} }
} }

View File

@ -14,7 +14,8 @@ internal data class Series(
it.map { ch -> ch.copy(name = "$it - $ch") } it.map { ch -> ch.copy(name = "$it - $ch") }
}.iterator() }.iterator()
val cover get() = thumb ?: "static/nocover.png" val cover: String
get() = thumb ?: "static/nocover.png"
override fun toString() = name.ifEmpty { dir } override fun toString() = name.ifEmpty { dir }
} }

View File

@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.multisrc.bakkin
import android.app.Application import android.app.Application
import android.os.Build import android.os.Build
import androidx.preference.CheckBoxPreference import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
@ -18,9 +18,7 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
import okhttp3.Headers import okhttp3.Headers
import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -41,28 +39,38 @@ abstract class BakkinReaderX(
private val json by lazy { Injekt.get<Json>() } private val json by lazy { Injekt.get<Json>() }
private val mainUrl private val mainUrl: String
get() = baseUrl + "/main.php" + get() = baseUrl + "main.php" + preferences.getString("quality", "")
if (preferences.getBoolean("fullsize", false)) "?fullsize" else ""
private var seriesCache = emptyList<Series>() private var seriesCache = emptyList<Series>()
private fun <R> observableSeries(block: (List<Series>) -> R) = private fun <R> observableSeries(block: (List<Series>) -> R) =
if (seriesCache.isNotEmpty()) Observable.just(block(seriesCache)) if (seriesCache.isNotEmpty()) {
else client.newCall(GET(mainUrl, headers)).asObservableSuccess().map { rx.Observable.just(block(seriesCache))!!
seriesCache = json.parseToJsonElement(it.body!!.string()) } else {
.jsonObject.values.map(json::decodeFromJsonElement) client.newCall(GET(mainUrl, headers)).asObservableSuccess().map {
block(seriesCache) seriesCache = json.parseToJsonElement(it.body!!.string())
.jsonObject.values.map(json::decodeFromJsonElement)
block(seriesCache)
}!!
} }
override fun headersBuilder() = Headers.Builder().add("User-Agent", userAgent) private fun List<Series>.search(query: String) =
if (query.isBlank()) this else filter { it.toString().contains(query, true) }
override fun headersBuilder() =
Headers.Builder().add("User-Agent", userAgent)
// Request the actual manga URL for the webview // Request the actual manga URL for the webview
override fun mangaDetailsRequest(manga: SManga) = GET("$baseUrl#m=${manga.url}", headers) override fun mangaDetailsRequest(manga: SManga) =
GET("$baseUrl#m=${manga.url}", headers)
override fun fetchPopularManga(page: Int): Observable<MangasPage> = override fun fetchPopularManga(page: Int) =
fetchSearchManga(page, "", FilterList())
override fun fetchSearchManga(page: Int, query: String, filters: FilterList) =
observableSeries { series -> observableSeries { series ->
series.map { series.search(query).map {
SManga.create().apply { SManga.create().apply {
url = it.dir url = it.dir
title = it.toString() title = it.toString()
@ -71,7 +79,7 @@ abstract class BakkinReaderX(
}.let { MangasPage(it, false) } }.let { MangasPage(it, false) }
} }
override fun fetchMangaDetails(manga: SManga): Observable<SManga> = override fun fetchMangaDetails(manga: SManga) =
observableSeries { series -> observableSeries { series ->
series.first { it.dir == manga.url }.let { series.first { it.dir == manga.url }.let {
SManga.create().apply { SManga.create().apply {
@ -89,65 +97,65 @@ abstract class BakkinReaderX(
} }
} }
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = override fun fetchChapterList(manga: SManga) =
observableSeries { series -> observableSeries { series ->
series.first { it.dir == manga.url }.mapIndexed { idx, chapter -> series.first { it.dir == manga.url }.mapIndexed { idx, chapter ->
SChapter.create().apply { SChapter.create().apply {
url = chapter.dir url = chapter.dir
name = chapter.toString() name = chapter.toString()
chapter_number = idx.toFloat() chapter_number = idx.toFloat()
date_upload = -1L date_upload = 0L
} }
} }
} }
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> = override fun fetchPageList(chapter: SChapter) =
observableSeries { series -> observableSeries { series ->
series.flatten().first { it.dir == chapter.url } series.flatten().first { it.dir == chapter.url }
.mapIndexed { idx, page -> Page(idx, "", "$baseUrl$page") } .mapIndexed { idx, page -> Page(idx, "", baseUrl + page) }
} }
override fun setupPreferenceScreen(screen: PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
screen.addPreference( ListPreference(screen.context).apply {
CheckBoxPreference(screen.context).apply { key = "quality"
key = "fullsize" summary = "Image quality: %s"
summary = "View fullsize images" entries = arrayOf("Original", "Compressed")
setDefaultValue(false) entryValues = arrayOf("?fullsize", "")
setDefaultValue("")
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
preferences.edit().putBoolean(key, newValue as Boolean).commit() preferences.edit().putString(key, newValue as String).commit()
}
} }
) }.let(screen::addPreference)
} }
override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
throw UnsupportedOperationException("Search is not supported by this source.")
override fun popularMangaRequest(page: Int): Request =
throw UnsupportedOperationException("Not used!") throw UnsupportedOperationException("Not used!")
override fun latestUpdatesRequest(page: Int): Request = override fun popularMangaRequest(page: Int) =
throw UnsupportedOperationException("Not used!") throw UnsupportedOperationException("Not used!")
override fun searchMangaParse(response: Response): MangasPage = override fun latestUpdatesRequest(page: Int) =
throw UnsupportedOperationException("Not used!") throw UnsupportedOperationException("Not used!")
override fun popularMangaParse(response: Response): MangasPage = override fun searchMangaParse(response: Response) =
throw UnsupportedOperationException("Not used!") throw UnsupportedOperationException("Not used!")
override fun latestUpdatesParse(response: Response): MangasPage = override fun popularMangaParse(response: Response) =
throw UnsupportedOperationException("Not used!") throw UnsupportedOperationException("Not used!")
override fun mangaDetailsParse(response: Response): SManga = override fun latestUpdatesParse(response: Response) =
throw UnsupportedOperationException("Not used!") throw UnsupportedOperationException("Not used!")
override fun chapterListParse(response: Response): List<SChapter> = override fun mangaDetailsParse(response: Response) =
throw UnsupportedOperationException("Not used!") throw UnsupportedOperationException("Not used!")
override fun pageListParse(response: Response): List<Page> = override fun chapterListParse(response: Response) =
throw UnsupportedOperationException("Not used!") throw UnsupportedOperationException("Not used!")
override fun imageUrlParse(response: Response): String = override fun pageListParse(response: Response) =
throw UnsupportedOperationException("Not used!")
override fun imageUrlParse(response: Response) =
throw UnsupportedOperationException("Not used!") throw UnsupportedOperationException("Not used!")
} }