Implement Non-Final 1.5 Extensions API (#699)

* Implement non-final 1.5 extensions API

* Bump lib version max

* Add visibility to preferences

* Add preference visibility
This commit is contained in:
Mitchell Syer
2023-10-04 22:01:45 -04:00
committed by GitHub
parent 354968fba7
commit c8865ad185
28 changed files with 491 additions and 261 deletions

View File

@@ -14,7 +14,6 @@ import suwayomi.tachidesk.graphql.types.preferenceOf
import suwayomi.tachidesk.graphql.types.updateFilterList
import suwayomi.tachidesk.manga.impl.MangaList.insertOrGet
import suwayomi.tachidesk.manga.impl.Source
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource
import suwayomi.tachidesk.manga.model.table.MangaTable
import suwayomi.tachidesk.server.JavalinSetup.future
@@ -50,18 +49,18 @@ class SourceMutation {
val source = GetCatalogueSource.getCatalogueSourceOrNull(sourceId)!!
val mangasPage = when (type) {
FetchSourceMangaType.SEARCH -> {
source.fetchSearchManga(
source.getSearchManga(
page = page,
query = query.orEmpty(),
filters = updateFilterList(source, filters)
).awaitSingle()
)
}
FetchSourceMangaType.POPULAR -> {
source.fetchPopularManga(page).awaitSingle()
source.getPopularManga(page)
}
FetchSourceMangaType.LATEST -> {
if (!source.supportsLatest) throw Exception("Source does not support latest")
source.fetchLatestUpdates(page).awaitSingle()
source.getLatestUpdates(page)
}
}

View File

@@ -299,6 +299,7 @@ data class SwitchPreference(
val key: String,
val title: String,
val summary: String?,
val visible: Boolean,
val currentValue: Boolean?,
val default: Boolean
) : Preference
@@ -307,6 +308,7 @@ data class CheckBoxPreference(
val key: String,
val title: String,
val summary: String?,
val visible: Boolean,
val currentValue: Boolean?,
val default: Boolean
) : Preference
@@ -315,6 +317,7 @@ data class EditTextPreference(
val key: String,
val title: String?,
val summary: String?,
val visible: Boolean,
val currentValue: String?,
val default: String?,
val dialogTitle: String?,
@@ -326,6 +329,7 @@ data class ListPreference(
val key: String,
val title: String?,
val summary: String?,
val visible: Boolean,
val currentValue: String?,
val default: String?,
val entries: List<String>,
@@ -336,6 +340,7 @@ data class MultiSelectListPreference(
val key: String,
val title: String?,
val summary: String?,
val visible: Boolean,
val currentValue: List<String>?,
val default: List<String>?,
val dialogTitle: String?,
@@ -350,13 +355,15 @@ fun preferenceOf(preference: SourcePreference): Preference {
preference.key,
preference.title.toString(),
preference.summary?.toString(),
preference.visible,
preference.currentValue as Boolean,
preference.defaultValue as Boolean
preference.defaultValue as Boolean,
)
is SourceCheckBoxPreference -> CheckBoxPreference(
preference.key,
preference.title.toString(),
preference.summary?.toString(),
preference.visible,
preference.currentValue as Boolean,
preference.defaultValue as Boolean
)
@@ -364,6 +371,7 @@ fun preferenceOf(preference: SourcePreference): Preference {
preference.key,
preference.title?.toString(),
preference.summary?.toString(),
preference.visible,
(preference.currentValue as CharSequence?)?.toString(),
(preference.defaultValue as CharSequence?)?.toString(),
preference.dialogTitle?.toString(),
@@ -374,6 +382,7 @@ fun preferenceOf(preference: SourcePreference): Preference {
preference.key,
preference.title?.toString(),
preference.summary?.toString(),
preference.visible,
(preference.currentValue as CharSequence?)?.toString(),
(preference.defaultValue as CharSequence?)?.toString(),
preference.entries.map { it.toString() },
@@ -383,6 +392,7 @@ fun preferenceOf(preference: SourcePreference): Preference {
preference.key,
preference.title?.toString(),
preference.summary?.toString(),
preference.visible,
(preference.currentValue as Collection<*>?)?.map { it.toString() },
(preference.defaultValue as Collection<*>?)?.map { it.toString() },
preference.dialogTitle?.toString(),

View File

@@ -14,7 +14,6 @@ import org.jetbrains.exposed.sql.insertAndGetId
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.manga.impl.Manga.getMangaMetaMap
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
import suwayomi.tachidesk.manga.model.dataclass.PagedMangaListDataClass
@@ -33,10 +32,10 @@ object MangaList {
}
val source = getCatalogueSourceOrStub(sourceId)
val mangasPage = if (popular) {
source.fetchPopularManga(pageNum).awaitSingle()
source.getPopularManga(pageNum)
} else {
if (source.supportsLatest) {
source.fetchLatestUpdates(pageNum).awaitSingle()
source.getLatestUpdates(pageNum)
} else {
throw Exception("Source $source doesn't support latest")
}

View File

@@ -17,7 +17,6 @@ import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.update
import suwayomi.tachidesk.manga.impl.util.getChapterCachePath
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
import suwayomi.tachidesk.manga.impl.util.storage.ImageResponse.getImageResponse
import suwayomi.tachidesk.manga.impl.util.storage.ImageUtil
@@ -34,7 +33,7 @@ object Page {
*/
suspend fun getTrueImageUrl(page: Page, source: HttpSource): String {
if (page.imageUrl == null) {
page.imageUrl = source.fetchImageUrl(page).awaitSingle()
page.imageUrl = source.getImageUrl(page)
}
return page.imageUrl!!
}
@@ -100,7 +99,7 @@ object Page {
// Note: don't care about invalidating cache because OS cache is not permanent
return getImageResponse(cacheSaveDir, fileName) {
source.fetchImage(tachiyomiPage).awaitSingle()
source.getImage(tachiyomiPage)
}
}

View File

@@ -16,21 +16,20 @@ import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import suwayomi.tachidesk.manga.impl.MangaList.processEntries
import suwayomi.tachidesk.manga.impl.util.lang.awaitSingle
import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.getCatalogueSourceOrStub
import suwayomi.tachidesk.manga.model.dataclass.PagedMangaListDataClass
object Search {
suspend fun sourceSearch(sourceId: Long, searchTerm: String, pageNum: Int): PagedMangaListDataClass {
val source = getCatalogueSourceOrStub(sourceId)
val searchManga = source.fetchSearchManga(pageNum, searchTerm, getFilterListOf(source)).awaitSingle()
val searchManga = source.getSearchManga(pageNum, searchTerm, getFilterListOf(source))
return searchManga.processEntries(sourceId)
}
suspend fun sourceFilter(sourceId: Long, pageNum: Int, filter: FilterData): PagedMangaListDataClass {
val source = getCatalogueSourceOrStub(sourceId)
val filterList = if (filter.filter != null) buildFilterList(sourceId, filter.filter) else source.getFilterList()
val searchManga = source.fetchSearchManga(pageNum, filter.searchTerm ?: "", filterList).awaitSingle()
val searchManga = source.getSearchManga(pageNum, filter.searchTerm ?: "", filterList)
return searchManga.processEntries(sourceId)
}

View File

@@ -7,12 +7,10 @@ package suwayomi.tachidesk.manga.impl
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import android.app.Application
import android.content.Context
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.getPreferenceKey
import eu.kanade.tachiyomi.source.sourcePreferences
import io.javalin.plugin.json.JsonMapper
import mu.KotlinLogging
import org.jetbrains.exposed.sql.select
@@ -28,7 +26,6 @@ import suwayomi.tachidesk.manga.impl.util.source.GetCatalogueSource.unregisterCa
import suwayomi.tachidesk.manga.model.dataclass.SourceDataClass
import suwayomi.tachidesk.manga.model.table.ExtensionTable
import suwayomi.tachidesk.manga.model.table.SourceTable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import xyz.nulldev.androidcompat.androidimpl.CustomContext
@@ -106,8 +103,7 @@ object Source {
val source = getCatalogueSourceOrStub(sourceId)
if (source is ConfigurableSource) {
val sourceShardPreferences =
Injekt.get<Application>().getSharedPreferences(source.getPreferenceKey(), Context.MODE_PRIVATE)
val sourceShardPreferences = source.sourcePreferences()
val screen = PreferenceScreen(context)
screen.sharedPreferences = sourceShardPreferences

View File

@@ -48,7 +48,7 @@ object ProtoBackupImport : ProtoBackupBase() {
private val backupMutex = Mutex()
sealed class BackupRestoreState {
object Idle : BackupRestoreState()
data object Idle : BackupRestoreState()
data class RestoringCategories(val totalManga: Int) : BackupRestoreState()
data class RestoringManga(val current: Int, val totalManga: Int, val title: String) : BackupRestoreState()
}

View File

@@ -9,9 +9,10 @@ package suwayomi.tachidesk.manga.impl.extension.github
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.network.parseAs
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import mu.KotlinLogging
import suwayomi.tachidesk.manga.impl.util.PackageTools.LIB_VERSION_MAX
import suwayomi.tachidesk.manga.impl.util.PackageTools.LIB_VERSION_MIN
@@ -22,6 +23,7 @@ object ExtensionGithubApi {
private const val REPO_URL_PREFIX = "https://raw.githubusercontent.com/tachiyomiorg/tachiyomi-extensions/repo/"
private const val FALLBACK_REPO_URL_PREFIX = "https://gcore.jsdelivr.net/gh/tachiyomiorg/tachiyomi-extensions@repo/"
private val logger = KotlinLogging.logger {}
private val json: Json by injectLazy()
@Serializable
private data class ExtensionJsonObject(
@@ -52,7 +54,7 @@ object ExtensionGithubApi {
null
} else {
try {
client.newCall(GET("${REPO_URL_PREFIX}index.min.json")).await()
client.newCall(GET("${REPO_URL_PREFIX}index.min.json")).awaitSuccess()
} catch (e: Throwable) {
logger.error(e) { "Failed to get extensions from GitHub" }
requiresFallbackSource = true
@@ -61,12 +63,14 @@ object ExtensionGithubApi {
}
val response = githubResponse ?: run {
client.newCall(GET("${FALLBACK_REPO_URL_PREFIX}index.min.json")).await()
client.newCall(GET("${FALLBACK_REPO_URL_PREFIX}index.min.json")).awaitSuccess()
}
return response
.parseAs<List<ExtensionJsonObject>>()
.toExtensions()
return with(json) {
response
.parseAs<List<ExtensionJsonObject>>()
.toExtensions()
}
}
fun getApkUrl(extension: ExtensionDataClass): String {

View File

@@ -41,7 +41,7 @@ object PackageTools {
const val METADATA_SOURCE_FACTORY = "tachiyomi.extension.factory"
const val METADATA_NSFW = "tachiyomi.extension.nsfw"
const val LIB_VERSION_MIN = 1.3
const val LIB_VERSION_MAX = 1.4
const val LIB_VERSION_MAX = 1.5
private const val officialSignature = "7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23" // inorichi's key
private const val unofficialSignature = "64feb21075ba97ebc9cc981243645b331595c111cef1b0d084236a0403b00581" // ArMor's key

View File

@@ -21,14 +21,17 @@ open class StubSource(override val id: Long) : CatalogueSource {
override val name: String
get() = id.toString()
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularManga"))
override fun fetchPopularManga(page: Int): Observable<MangasPage> {
return Observable.error(getSourceNotInstalledException())
}
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getSearchManga"))
override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> {
return Observable.error(getSourceNotInstalledException())
}
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates"))
override fun fetchLatestUpdates(page: Int): Observable<MangasPage> {
return Observable.error(getSourceNotInstalledException())
}
@@ -37,14 +40,17 @@ open class StubSource(override val id: Long) : CatalogueSource {
return FilterList()
}
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getMangaDetails"))
override fun fetchMangaDetails(manga: SManga): Observable<SManga> {
return Observable.error(getSourceNotInstalledException())
}
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getChapterList"))
override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> {
return Observable.error(getSourceNotInstalledException())
}
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPageList"))
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> {
return Observable.error(getSourceNotInstalledException())
}

View File

@@ -9,7 +9,7 @@ package suwayomi.tachidesk.server.util
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.awaitSuccess
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@@ -408,7 +408,7 @@ object WebInterfaceManager {
private suspend fun fetchMD5SumFor(version: String): String {
return try {
executeWithRetry(KotlinLogging.logger("${logger.name} fetchMD5SumFor($version)"), {
network.client.newCall(GET("${getDownloadUrlFor(version)}/md5sum")).await().body.string().trim()
network.client.newCall(GET("${getDownloadUrlFor(version)}/md5sum")).awaitSuccess().body.string().trim()
})
} catch (e: Exception) {
""
@@ -422,7 +422,7 @@ object WebInterfaceManager {
private suspend fun fetchPreviewVersion(): String {
return executeWithRetry(KotlinLogging.logger("${logger.name} fetchPreviewVersion"), {
val releaseInfoJson = network.client.newCall(GET(WebUIFlavor.WEBUI.latestReleaseInfoUrl)).await().body.string()
val releaseInfoJson = network.client.newCall(GET(WebUIFlavor.WEBUI.latestReleaseInfoUrl)).awaitSuccess().body.string()
Json.decodeFromString<JsonObject>(releaseInfoJson)["tag_name"]?.jsonPrimitive?.content
?: throw Exception("Failed to get the preview version tag")
})
@@ -433,7 +433,7 @@ object WebInterfaceManager {
KotlinLogging.logger("$logger fetchServerMappingFile"),
{
json.parseToJsonElement(
network.client.newCall(GET(WebUIFlavor.WEBUI.versionMappingUrl)).await().body.string()
network.client.newCall(GET(WebUIFlavor.WEBUI.versionMappingUrl)).awaitSuccess().body.string()
).jsonArray
}
)