From 83c94c044da69c7f9da70d3077fbe6019d60c633 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 12:37:19 -0500 Subject: [PATCH] Update dependency com.pinterest.ktlint:ktlint-cli to v1.8.0 (#1786) * Update dependency com.pinterest.ktlint:ktlint-cli to v1.8.0 * Format --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Syer10 --- .../io/sharedprefs/JavaSharedPreferences.kt | 16 +- gradle/libs.versions.toml | 2 +- .../interceptor/RateLimitInterceptor.kt | 8 +- .../tachiyomi/source/local/LocalSource.kt | 9 ++ .../util/chapter/ChapterRecognition.kt | 1 + .../tachidesk/global/impl/KcefWebView.kt | 153 ++++++++++++++---- .../suwayomi/tachidesk/global/impl/WebView.kt | 5 + .../graphql/mutations/SourceMutation.kt | 2 + .../tachidesk/graphql/queries/TrackQuery.kt | 27 +++- .../server/JavalinGraphQLRequestParser.kt | 1 + .../graphql/server/TachideskGraphQLSchema.kt | 10 +- .../server/primitives/DurationAsString.kt | 2 + .../tachidesk/graphql/types/BackupTypes.kt | 27 +++- .../tachidesk/graphql/types/SourceType.kt | 76 +++++++-- .../suwayomi/tachidesk/manga/impl/Chapter.kt | 20 ++- .../suwayomi/tachidesk/manga/impl/Manga.kt | 10 +- .../suwayomi/tachidesk/manga/impl/Search.kt | 28 +++- .../manga/impl/download/DownloadManager.kt | 8 +- .../fileProvider/ChaptersFilesProvider.kt | 2 + .../manga/impl/extension/ExtensionsList.kt | 1 + .../manga/impl/sync/KoreaderSyncService.kt | 1 + .../impl/track/tracker/anilist/Anilist.kt | 44 +++-- .../track/tracker/anilist/AnilistUtils.kt | 27 +++- .../tracker/mangaupdates/MangaUpdates.kt | 13 +- .../manga/impl/update/UpdaterSocket.kt | 8 +- .../tachidesk/manga/impl/util/DirName.kt | 5 +- .../tachidesk/opds/impl/OpdsEntryBuilder.kt | 9 +- .../tachidesk/opds/impl/OpdsFeedBuilder.kt | 18 ++- .../opds/repository/MangaRepository.kt | 29 +++- .../tachidesk/opds/util/OpdsStringUtil.kt | 16 +- .../tachidesk/server/database/DBManager.kt | 8 +- .../database/migration/helpers/TypeHelpers.kt | 4 +- .../tachidesk/server/user/UserType.kt | 30 +++- .../tachidesk/server/util/AppMutex.kt | 2 + .../tachidesk/server/util/DocumentationDsl.kt | 30 +++- .../server/util/WebInterfaceManager.kt | 13 +- .../masstest/TestExtensionCompatibility.kt | 2 + 37 files changed, 524 insertions(+), 143 deletions(-) diff --git a/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/io/sharedprefs/JavaSharedPreferences.kt b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/io/sharedprefs/JavaSharedPreferences.kt index b30c1d99..a1641c66 100644 --- a/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/io/sharedprefs/JavaSharedPreferences.kt +++ b/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/io/sharedprefs/JavaSharedPreferences.kt @@ -242,13 +242,14 @@ class JavaSharedPreferences( } notify(it.key) } + is Action.Remove -> { preferences.remove(it.key) - /** - * Set are stored like - * key.0 = value1 - * key.1 = value2 - * key.size = 2 + /* + Set are stored like + key.0 = value1 + key.1 = value2 + key.size = 2 */ preferences.keys.forEach { key -> if (key.startsWith(it.key + ".")) { @@ -258,7 +259,10 @@ class JavaSharedPreferences( notify(it.key) } - Action.Clear -> preferences.clear() + + Action.Clear -> { + preferences.clear() + } } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b647aca9..a7597943 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ settings = "1.3.0" twelvemonkeys = "3.12.0" graphqlkotlin = "8.8.1" xmlserialization = "0.91.3" -ktlint = "1.7.1" +ktlint = "1.8.0" koin = "4.1.1" moko = "0.25.1" diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt index a6a3346e..586671d9 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt @@ -72,8 +72,12 @@ internal class RateLimitInterceptor( val request = chain.request() when (host) { - null, request.url.host -> {} // need rate limit - else -> return chain.proceed(request) + // need rate limit + null, request.url.host -> {} + + else -> { + return chain.proceed(request) + } } try { diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt index 0e6b77d8..a6a6cb78 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/source/local/LocalSource.kt @@ -110,6 +110,7 @@ class LocalSource( mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name }) } } + is OrderBy.Latest -> { mangaDirs = if (filter.state!!.ascending) { @@ -246,6 +247,7 @@ class LocalSource( } } } + is Format.Rar -> { JunrarArchive(chapter).use { rar -> rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile -> @@ -255,6 +257,7 @@ class LocalSource( } } } + else -> {} } } @@ -337,6 +340,7 @@ class LocalSource( ) } } + is Format.Zip -> { val loader = ZipPageLoader(format.file) val pages = loader.getPages() @@ -344,6 +348,7 @@ class LocalSource( pages } + is Format.Rar -> { val loader = RarPageLoader(format.file) val pages = loader.getPages() @@ -351,6 +356,7 @@ class LocalSource( pages } + is Format.Epub -> { val loader = EpubPageLoader(format.file) val pages = loader.getPages() @@ -390,6 +396,7 @@ class LocalSource( entry?.let { coverManager.update(manga, it.inputStream()) } } + is Format.Zip -> { ZipFile.builder().setFile(format.file).get().use { zip -> val entry = @@ -401,6 +408,7 @@ class LocalSource( entry?.let { coverManager.update(manga, zip.getInputStream(it)) } } } + is Format.Rar -> { JunrarArchive(format.file).use { archive -> val entry = @@ -411,6 +419,7 @@ class LocalSource( entry?.let { coverManager.update(manga, archive.getInputStream(it)) } } } + is Format.Epub -> { EpubFile(format.file).use { epub -> val entry = diff --git a/server/src/main/kotlin/eu/kanade/tachiyomi/util/chapter/ChapterRecognition.kt b/server/src/main/kotlin/eu/kanade/tachiyomi/util/chapter/ChapterRecognition.kt index 07727cd5..a1d13eea 100644 --- a/server/src/main/kotlin/eu/kanade/tachiyomi/util/chapter/ChapterRecognition.kt +++ b/server/src/main/kotlin/eu/kanade/tachiyomi/util/chapter/ChapterRecognition.kt @@ -58,6 +58,7 @@ object ChapterRecognition { numberMatch.none() -> { return chapterNumber ?: -1.0 } + numberMatch.count() > 1 -> { // Remove unwanted tags. unwanted.replace(cleanChapterName, "").let { name -> diff --git a/server/src/main/kotlin/suwayomi/tachidesk/global/impl/KcefWebView.kt b/server/src/main/kotlin/suwayomi/tachidesk/global/impl/KcefWebView.kt index deeb1806..7fbfc023 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/global/impl/KcefWebView.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/global/impl/KcefWebView.kt @@ -367,37 +367,127 @@ class KcefWebView { ): KeyEvent? { val code = when (char.uppercaseChar()) { - in 'A'..'Z', in '0'..'9' -> char.uppercaseChar().code - '&' -> KeyEvent.VK_AMPERSAND - '*' -> KeyEvent.VK_ASTERISK - '@' -> KeyEvent.VK_AT - '\\' -> KeyEvent.VK_BACK_SLASH - '{' -> KeyEvent.VK_BRACELEFT - '}' -> KeyEvent.VK_BRACERIGHT - '^' -> KeyEvent.VK_CIRCUMFLEX - ']' -> KeyEvent.VK_CLOSE_BRACKET - ':' -> KeyEvent.VK_COLON - ',' -> KeyEvent.VK_COMMA - '$' -> KeyEvent.VK_DOLLAR - '=' -> KeyEvent.VK_EQUALS - '€' -> KeyEvent.VK_EURO_SIGN - '!' -> KeyEvent.VK_EXCLAMATION_MARK - '>' -> KeyEvent.VK_GREATER - '(' -> KeyEvent.VK_LEFT_PARENTHESIS - '<' -> KeyEvent.VK_LESS - '-' -> KeyEvent.VK_MINUS - '#' -> KeyEvent.VK_NUMBER_SIGN - '[' -> KeyEvent.VK_OPEN_BRACKET - '.' -> KeyEvent.VK_PERIOD - '+' -> KeyEvent.VK_PLUS - '\'' -> KeyEvent.VK_QUOTE - '"' -> KeyEvent.VK_QUOTEDBL - ')' -> KeyEvent.VK_RIGHT_PARENTHESIS - ';' -> KeyEvent.VK_SEMICOLON - '/' -> KeyEvent.VK_SLASH - ' ' -> KeyEvent.VK_SPACE - '_' -> KeyEvent.VK_UNDERSCORE - else -> + in 'A'..'Z', in '0'..'9' -> { + char.uppercaseChar().code + } + + '&' -> { + KeyEvent.VK_AMPERSAND + } + + '*' -> { + KeyEvent.VK_ASTERISK + } + + '@' -> { + KeyEvent.VK_AT + } + + '\\' -> { + KeyEvent.VK_BACK_SLASH + } + + '{' -> { + KeyEvent.VK_BRACELEFT + } + + '}' -> { + KeyEvent.VK_BRACERIGHT + } + + '^' -> { + KeyEvent.VK_CIRCUMFLEX + } + + ']' -> { + KeyEvent.VK_CLOSE_BRACKET + } + + ':' -> { + KeyEvent.VK_COLON + } + + ',' -> { + KeyEvent.VK_COMMA + } + + '$' -> { + KeyEvent.VK_DOLLAR + } + + '=' -> { + KeyEvent.VK_EQUALS + } + + '€' -> { + KeyEvent.VK_EURO_SIGN + } + + '!' -> { + KeyEvent.VK_EXCLAMATION_MARK + } + + '>' -> { + KeyEvent.VK_GREATER + } + + '(' -> { + KeyEvent.VK_LEFT_PARENTHESIS + } + + '<' -> { + KeyEvent.VK_LESS + } + + '-' -> { + KeyEvent.VK_MINUS + } + + '#' -> { + KeyEvent.VK_NUMBER_SIGN + } + + '[' -> { + KeyEvent.VK_OPEN_BRACKET + } + + '.' -> { + KeyEvent.VK_PERIOD + } + + '+' -> { + KeyEvent.VK_PLUS + } + + '\'' -> { + KeyEvent.VK_QUOTE + } + + '"' -> { + KeyEvent.VK_QUOTEDBL + } + + ')' -> { + KeyEvent.VK_RIGHT_PARENTHESIS + } + + ';' -> { + KeyEvent.VK_SEMICOLON + } + + '/' -> { + KeyEvent.VK_SLASH + } + + ' ' -> { + KeyEvent.VK_SPACE + } + + '_' -> { + KeyEvent.VK_UNDERSCORE + } + + else -> { when (strKey) { "Alt" -> KeyEvent.VK_ALT "Backspace" -> KeyEvent.VK_BACK_SPACE @@ -435,6 +525,7 @@ class KcefWebView { "ArrowUp" -> KeyEvent.VK_UP else -> KeyEvent.VK_UNDEFINED } + } } if (id == KeyEvent.KEY_TYPED) { if (char == KeyEvent.CHAR_UNDEFINED && code != KeyEvent.VK_ENTER) return null diff --git a/server/src/main/kotlin/suwayomi/tachidesk/global/impl/WebView.kt b/server/src/main/kotlin/suwayomi/tachidesk/global/impl/WebView.kt index 3e4f4a29..ccade4a0 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/global/impl/WebView.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/global/impl/WebView.kt @@ -104,18 +104,23 @@ object WebView : Websocket() { dr.resize(event.width, event.height) logger.debug { "Loading URL $url" } } + is ResizeMessage -> { dr.resize(event.width, event.height) } + is JsEventMessage -> { dr.event(event) } + is JsPasteMessage -> { dr.paste(event.data) } + is JsCopyMessage -> { dr.copy() } + is JsPingMessage -> { notifyAllClients("{\"type\":\"pong\"}") } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt index f937abea..ebadf2a9 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/mutations/SourceMutation.kt @@ -135,9 +135,11 @@ class SourceMutation { filters = updateFilterList(source, filters), ) } + FetchSourceMangaType.POPULAR -> { source.getPopularManga(page) } + FetchSourceMangaType.LATEST -> { if (!source.supportsLatest) throw Exception("Source does not support latest") source.getLatestUpdates(page) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt index fbab0173..296eb72b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/queries/TrackQuery.kt @@ -61,8 +61,14 @@ class TrackQuery { cursor: Cursor, ): Boolean = when (this) { - ID -> tracker.id > cursor.value.toInt() - NAME -> tracker.name > cursor.value + ID -> { + tracker.id > cursor.value.toInt() + } + + NAME -> { + tracker.name > cursor.value + } + IS_LOGGED_IN -> { val value = cursor.value.substringAfter('-').toBooleanStrict() !value || tracker.isLoggedIn @@ -74,8 +80,14 @@ class TrackQuery { cursor: Cursor, ): Boolean = when (this) { - ID -> tracker.id < cursor.value.toInt() - NAME -> tracker.name < cursor.value + ID -> { + tracker.id < cursor.value.toInt() + } + + NAME -> { + tracker.name < cursor.value + } + IS_LOGGED_IN -> { val value = cursor.value.substringAfter('-').toBooleanStrict() value || !tracker.isLoggedIn @@ -159,18 +171,21 @@ class TrackQuery { res = when (orderType) { - SortOrder.DESC, SortOrder.DESC_NULLS_FIRST, SortOrder.DESC_NULLS_LAST -> + SortOrder.DESC, SortOrder.DESC_NULLS_FIRST, SortOrder.DESC_NULLS_LAST -> { when (orderBy) { TrackerOrderBy.ID -> res.sortedByDescending { it.id } TrackerOrderBy.NAME -> res.sortedByDescending { it.name } TrackerOrderBy.IS_LOGGED_IN -> res.sortedByDescending { it.isLoggedIn } } - SortOrder.ASC, SortOrder.ASC_NULLS_FIRST, SortOrder.ASC_NULLS_LAST -> + } + + SortOrder.ASC, SortOrder.ASC_NULLS_FIRST, SortOrder.ASC_NULLS_LAST -> { when (orderBy) { TrackerOrderBy.ID -> res.sortedBy { it.id } TrackerOrderBy.NAME -> res.sortedBy { it.name } TrackerOrderBy.IS_LOGGED_IN -> res.sortedBy { it.isLoggedIn } } + } } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/JavalinGraphQLRequestParser.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/JavalinGraphQLRequestParser.kt index 87c46939..2c0a1ef8 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/JavalinGraphQLRequestParser.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/JavalinGraphQLRequestParser.kt @@ -61,6 +61,7 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser { is GraphQLRequest -> { request.copy(variables = request.variables?.modifyFiles(mapItems)) } + is GraphQLBatchRequest -> { request.copy( requests = diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt index c3dedfd9..dd51ee55 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt @@ -63,10 +63,16 @@ class CustomSchemaGeneratorHooks : FlowSubscriptionSchemaGeneratorHooks() { override fun willGenerateGraphQLType(type: KType): GraphQLType? = when (type.classifier as? KClass<*>) { - Long::class -> GraphQLLongAsString // encode to string for JS - Duration::class -> GraphQLDurationAsString // encode Duration as ISO-8601 string + // encode to string for JS + Long::class -> GraphQLLongAsString + + // encode Duration as ISO-8601 string + Duration::class -> GraphQLDurationAsString + Cursor::class -> GraphQLCursor + UploadedFile::class -> GraphQLUpload + else -> super.willGenerateGraphQLType(type) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/DurationAsString.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/DurationAsString.kt index 81f28dfd..3469900a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/DurationAsString.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/DurationAsString.kt @@ -25,7 +25,9 @@ private class GraphqlDurationAsStringCoercing : Coercing { private fun toStringImpl(input: Any): String = when (input) { is Duration -> input.toIsoString() + is String -> Duration.parse(input).toIsoString() + else -> throw CoercingSerializeException( "Expected a Duration or String but was ${CoercingUtil.typeName(input)}", ) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/BackupTypes.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/BackupTypes.kt index 6e38194f..cd3853a2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/BackupTypes.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/BackupTypes.kt @@ -31,46 +31,59 @@ data class BackupRestoreStatus( fun ProtoBackupImport.BackupRestoreState.toStatus(): BackupRestoreStatus = when (this) { - ProtoBackupImport.BackupRestoreState.Idle -> + ProtoBackupImport.BackupRestoreState.Idle -> { BackupRestoreStatus( state = BackupRestoreState.IDLE, totalManga = 0, mangaProgress = 0, ) - is ProtoBackupImport.BackupRestoreState.Success -> + } + + is ProtoBackupImport.BackupRestoreState.Success -> { BackupRestoreStatus( state = BackupRestoreState.SUCCESS, totalManga = 0, mangaProgress = 0, ) - is ProtoBackupImport.BackupRestoreState.Failure -> + } + + is ProtoBackupImport.BackupRestoreState.Failure -> { BackupRestoreStatus( state = BackupRestoreState.FAILURE, totalManga = 0, mangaProgress = 0, ) - is ProtoBackupImport.BackupRestoreState.RestoringCategories -> + } + + is ProtoBackupImport.BackupRestoreState.RestoringCategories -> { BackupRestoreStatus( state = BackupRestoreState.RESTORING_CATEGORIES, totalManga = totalManga, mangaProgress = current, ) - is ProtoBackupImport.BackupRestoreState.RestoringMeta -> + } + + is ProtoBackupImport.BackupRestoreState.RestoringMeta -> { BackupRestoreStatus( state = BackupRestoreState.RESTORING_META, totalManga = totalManga, mangaProgress = current, ) - is ProtoBackupImport.BackupRestoreState.RestoringSettings -> + } + + is ProtoBackupImport.BackupRestoreState.RestoringSettings -> { BackupRestoreStatus( state = BackupRestoreState.RESTORING_SETTINGS, totalManga = totalManga, mangaProgress = current, ) - is ProtoBackupImport.BackupRestoreState.RestoringManga -> + } + + is ProtoBackupImport.BackupRestoreState.RestoringManga -> { BackupRestoreStatus( state = BackupRestoreState.RESTORING_MANGA, totalManga = totalManga, mangaProgress = current, ) + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SourceType.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SourceType.kt index 7be34d56..56f85259 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SourceType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/types/SourceType.kt @@ -204,12 +204,27 @@ data class GroupFilter( fun filterOf(filter: SourceFilter<*>): Filter = when (filter) { - is SourceFilter.Header -> HeaderFilter(filter.name) - is SourceFilter.Separator -> SeparatorFilter(filter.name) - is SourceFilter.Select<*> -> SelectFilter(filter.name, filter.displayValues, filter.state) - is SourceFilter.Text -> TextFilter(filter.name, filter.state) - is SourceFilter.CheckBox -> CheckBoxFilter(filter.name, filter.state) - is SourceFilter.TriState -> + is SourceFilter.Header -> { + HeaderFilter(filter.name) + } + + is SourceFilter.Separator -> { + SeparatorFilter(filter.name) + } + + is SourceFilter.Select<*> -> { + SelectFilter(filter.name, filter.displayValues, filter.state) + } + + is SourceFilter.Text -> { + TextFilter(filter.name, filter.state) + } + + is SourceFilter.CheckBox -> { + CheckBoxFilter(filter.name, filter.state) + } + + is SourceFilter.TriState -> { TriStateFilter( filter.name, when (filter.state) { @@ -218,13 +233,22 @@ fun filterOf(filter: SourceFilter<*>): Filter = else -> TriState.IGNORE }, ) - is SourceFilter.Group<*> -> + } + + is SourceFilter.Group<*> -> { GroupFilter( filter.name, filter.state.map { filterOf(it as SourceFilter<*>) }, ) - is SourceFilter.Sort -> SortFilter(filter.name, filter.values.asList(), filter.state?.let(SortFilter::SortSelection)) - else -> throw RuntimeException("sealed class cannot have more subtypes!") + } + + is SourceFilter.Sort -> { + SortFilter(filter.name, filter.values.asList(), filter.state?.let(SortFilter::SortSelection)) + } + + else -> { + throw RuntimeException("sealed class cannot have more subtypes!") + } } /*sealed interface FilterChange { @@ -286,25 +310,31 @@ fun updateFilterList( is SourceFilter.Header -> { // NOOP } + is SourceFilter.Separator -> { // NOOP } + is SourceFilter.Select<*> -> { filter.state = change.selectState ?: throw Exception("Expected select state change at position ${change.position}") } + is SourceFilter.Text -> { filter.state = change.textState ?: throw Exception("Expected text state change at position ${change.position}") } + is SourceFilter.CheckBox -> { filter.state = change.checkBoxState ?: throw Exception("Expected checkbox state change at position ${change.position}") } + is SourceFilter.TriState -> { filter.state = change.triState?.ordinal ?: throw Exception("Expected tri state change at position ${change.position}") } + is SourceFilter.Group<*> -> { val groupChange = change.groupChange @@ -315,20 +345,24 @@ fun updateFilterList( groupFilter.state = groupChange.checkBoxState ?: throw Exception("Expected checkbox state change at position ${change.position}") } + is SourceFilter.TriState -> { groupFilter.state = groupChange.triState?.ordinal ?: throw Exception("Expected tri state change at position ${change.position}") } + is SourceFilter.Text -> { groupFilter.state = groupChange.textState ?: throw Exception("Expected text state change at position ${change.position}") } + is SourceFilter.Select<*> -> { groupFilter.state = groupChange.selectState ?: throw Exception("Expected select state change at position ${change.position}") } } } + is SourceFilter.Sort -> { filter.state = change.sortState?.run { SourceFilter.Sort.Selection(index, ascending) @@ -402,7 +436,7 @@ data class MultiSelectListPreference( fun preferenceOf(preference: SourcePreference): Preference = when (preference) { - is SourceSwitchPreference -> + is SourceSwitchPreference -> { SwitchPreference( preference.key, preference.title?.toString(), @@ -412,7 +446,9 @@ fun preferenceOf(preference: SourcePreference): Preference = preference.currentValue as Boolean, preference.defaultValue as Boolean, ) - is SourceCheckBoxPreference -> + } + + is SourceCheckBoxPreference -> { CheckBoxPreference( preference.key, preference.title?.toString(), @@ -422,7 +458,9 @@ fun preferenceOf(preference: SourcePreference): Preference = preference.currentValue as Boolean, preference.defaultValue as Boolean, ) - is SourceEditTextPreference -> + } + + is SourceEditTextPreference -> { EditTextPreference( preference.key, preference.title?.toString(), @@ -435,7 +473,9 @@ fun preferenceOf(preference: SourcePreference): Preference = preference.dialogMessage?.toString(), preference.text, ) - is SourceListPreference -> + } + + is SourceListPreference -> { ListPreference( preference.key, preference.title?.toString(), @@ -447,7 +487,9 @@ fun preferenceOf(preference: SourcePreference): Preference = preference.entries.map { it.toString() }, preference.entryValues.map { it.toString() }, ) - is SourceMultiSelectListPreference -> + } + + is SourceMultiSelectListPreference -> { MultiSelectListPreference( preference.key, preference.title?.toString(), @@ -461,5 +503,9 @@ fun preferenceOf(preference: SourcePreference): Preference = preference.entries.map { it.toString() }, preference.entryValues.map { it.toString() }, ) - else -> throw RuntimeException("sealed class cannot have more subtypes!") + } + + else -> { + throw RuntimeException("sealed class cannot have more subtypes!") + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt index d3fceb71..b5f7d415 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Chapter.kt @@ -513,25 +513,33 @@ object Chapter { // Make sure some filter is defined val condition = when { - mangaId != null -> + mangaId != null -> { // mangaId is not null, scope query under manga when { - input.chapterIds != null -> + input.chapterIds != null -> { Op.build { (ChapterTable.manga eq mangaId) and (ChapterTable.id inList input.chapterIds) } + } - input.chapterIndexes != null -> + input.chapterIndexes != null -> { Op.build { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder inList input.chapterIndexes) } + } - else -> null + else -> { + null + } } + } else -> { // mangaId is null, only chapterIndexes is valid for this case when { - input.chapterIds != null -> + input.chapterIds != null -> { Op.build { (ChapterTable.id inList input.chapterIds) } + } - else -> null + else -> { + null + } } } } ?: return diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt index 8b026208..a52c29d9 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Manga.kt @@ -372,10 +372,11 @@ object Manga { val sourceId = mangaEntry[MangaTable.sourceReference] return when (val source = getCatalogueSourceOrStub(sourceId)) { - is HttpSource -> + is HttpSource -> { getImageResponse(cacheSaveDir, fileName) { fetchHttpSourceMangaThumbnail(source, mangaEntry) } + } is LocalSource -> { val imageFile = @@ -393,7 +394,7 @@ object Manga { imageFile.inputStream() to contentType } - is StubSource -> + is StubSource -> { getImageResponse(cacheSaveDir, fileName) { val thumbnailUrl = mangaEntry[MangaTable.thumbnail_url] @@ -403,8 +404,11 @@ object Manga { GET(thumbnailUrl, cache = CacheControl.FORCE_NETWORK), ).await() } + } - else -> throw IllegalArgumentException("Unknown source") + else -> { + throw IllegalArgumentException("Unknown source") + } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Search.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Search.kt index 9ab51dbc..9c3182f2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Search.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/Search.kt @@ -86,7 +86,10 @@ object Search { }, ) } - else -> it + + else -> { + it + } }, ) } @@ -122,13 +125,27 @@ object Search { is Filter.Header -> { // NOOP } + is Filter.Separator -> { // NOOP } - is Filter.Select<*> -> filter.state = change.state.toInt() - is Filter.Text -> filter.state = change.state - is Filter.CheckBox -> filter.state = change.state.toBooleanStrict() - is Filter.TriState -> filter.state = change.state.toInt() + + is Filter.Select<*> -> { + filter.state = change.state.toInt() + } + + is Filter.Text -> { + filter.state = change.state + } + + is Filter.CheckBox -> { + filter.state = change.state.toBooleanStrict() + } + + is Filter.TriState -> { + filter.state = change.state.toInt() + } + is Filter.Group<*> -> { val groupChange = jsonMapper.fromJsonString(change.state) @@ -139,6 +156,7 @@ object Search { is Filter.Select<*> -> groupFilter.state = groupChange.state.toInt() } } + is Filter.Sort -> { filter.state = jsonMapper.fromJsonString(change.state, Filter.Sort.Selection::class.java) } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt index 11b10c3f..7d66561f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt @@ -120,8 +120,11 @@ object DownloadManager { fun handleRequest(ctx: WsMessageContext) { when (ctx.message()) { - "STATUS" -> notifyClient(ctx) - else -> + "STATUS" -> { + notifyClient(ctx) + } + + else -> { ctx.send( """ |Invalid command. @@ -131,6 +134,7 @@ object DownloadManager { | """.trimMargin(), ) + } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/ChaptersFilesProvider.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/ChaptersFilesProvider.kt index cd0b98f0..77d38f14 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/ChaptersFilesProvider.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/fileProvider/ChaptersFilesProvider.kt @@ -49,6 +49,7 @@ sealed class FileType { is RegularFile -> { this.file.name } + is ZipFile -> { this.entry.name } @@ -59,6 +60,7 @@ sealed class FileType { is RegularFile -> { this.file.extension } + is ZipFile -> { this.entry.name.substringAfterLast(".") } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/ExtensionsList.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/ExtensionsList.kt index 16742c72..5371ba08 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/ExtensionsList.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/extension/ExtensionsList.kt @@ -149,6 +149,7 @@ object ExtensionsList { this[ExtensionTable.hasUpdate] = true updateMap.putIfAbsent(foundExtension.pkgName, foundExtension) } + foundExtension.versionCode < extensionRecord[ExtensionTable.versionCode] -> { // somehow the user installed an invalid version this[ExtensionTable.isObsolete] = true diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/sync/KoreaderSyncService.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/sync/KoreaderSyncService.kt index 1194ed56..c50e2848 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/sync/KoreaderSyncService.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/sync/KoreaderSyncService.kt @@ -150,6 +150,7 @@ object KoreaderSyncService { null } } + KoreaderSyncChecksumMethod.FILENAME -> { logger.debug { "[KOSYNC HASH] No hash for chapterId=$chapterId. Generating from filename." } (ChapterTable innerJoin MangaTable) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/Anilist.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/Anilist.kt index 97d039dd..84c49f4a 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/Anilist.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/Anilist.kt @@ -69,56 +69,82 @@ class Anilist( when (trackPreferences.getScoreType(this)) { // 10 point POINT_10 -> IntRange(0, 10).map(Int::toString) + // 100 point POINT_100 -> IntRange(0, 100).map(Int::toString) + // 5 stars POINT_5 -> IntRange(0, 5).map { "$it ★" } + // Smiley POINT_3 -> listOf("-", "😦", "😐", "😊") + // 10 point decimal POINT_10_DECIMAL -> IntRange(0, 100).map { (it / 10f).toString() } + else -> throw Exception("Unknown score type") } override fun indexToScore(index: Int): Double = when (trackPreferences.getScoreType(this)) { // 10 point - POINT_10 -> index * 10.0 + POINT_10 -> { + index * 10.0 + } + // 100 point - POINT_100 -> index.toDouble() + POINT_100 -> { + index.toDouble() + } + // 5 stars - POINT_5 -> + POINT_5 -> { when (index) { 0 -> 0.0 else -> index * 20.0 - 10.0 } + } + // Smiley - POINT_3 -> + POINT_3 -> { when (index) { 0 -> 0.0 else -> index * 25.0 + 10.0 } + } + // 10 point decimal - POINT_10_DECIMAL -> index.toDouble() - else -> throw Exception("Unknown score type") + POINT_10_DECIMAL -> { + index.toDouble() + } + + else -> { + throw Exception("Unknown score type") + } } override fun displayScore(track: Track): String { val score = track.score return when (val type = trackPreferences.getScoreType(this)) { - POINT_5 -> + POINT_5 -> { when (score) { 0.0 -> "0 ★" else -> "${((score + 10) / 20).toInt()} ★" } - POINT_3 -> + } + + POINT_3 -> { when { score == 0.0 -> "0" score <= 35 -> "😦" score <= 60 -> "😐" else -> "😊" } - else -> track.toApiScore(type) + } + + else -> { + track.toApiScore(type) + } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/AnilistUtils.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/AnilistUtils.kt index 8433dcb4..5aa8aac8 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/AnilistUtils.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/anilist/AnilistUtils.kt @@ -16,11 +16,17 @@ fun Track.toApiStatus() = fun Track.toApiScore(scoreType: String?): String = when (scoreType) { // 10 point - "POINT_10" -> (score.toInt() / 10).toString() + "POINT_10" -> { + (score.toInt() / 10).toString() + } + // 100 point - "POINT_100" -> score.toInt().toString() + "POINT_100" -> { + score.toInt().toString() + } + // 5 stars - "POINT_5" -> + "POINT_5" -> { when { score == 0.0 -> "0" score < 30 -> "1" @@ -29,15 +35,24 @@ fun Track.toApiScore(scoreType: String?): String = score < 90 -> "4" else -> "5" } + } + // Smiley - "POINT_3" -> + "POINT_3" -> { when { score == 0.0 -> "0" score <= 35 -> ":(" score <= 60 -> ":|" else -> ":)" } + } + // 10 point decimal - "POINT_10_DECIMAL" -> (score / 10).toString() - else -> throw NotImplementedError("Unknown score type") + "POINT_10_DECIMAL" -> { + (score / 10).toString() + } + + else -> { + throw NotImplementedError("Unknown score type") + } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/MangaUpdates.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/MangaUpdates.kt index 417288fa..ba93dd3b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/MangaUpdates.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/track/tracker/mangaupdates/MangaUpdates.kt @@ -24,12 +24,19 @@ class MangaUpdates( (0..10) .flatMap { decimal -> when (decimal) { - 0 -> listOf("-") - 10 -> listOf("10.0") - else -> + 0 -> { + listOf("-") + } + + 10 -> { + listOf("10.0") + } + + else -> { (0..9).map { fraction -> "$decimal.$fraction" } + } } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/UpdaterSocket.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/UpdaterSocket.kt index 905c9d62..f0423ad5 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/UpdaterSocket.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/update/UpdaterSocket.kt @@ -26,8 +26,11 @@ object UpdaterSocket : Websocket() { override fun handleRequest(ctx: WsMessageContext) { when (ctx.message()) { - "STATUS" -> notifyClient(ctx, updater.statusDeprecated.value) - else -> + "STATUS" -> { + notifyClient(ctx, updater.statusDeprecated.value) + } + + else -> { ctx.send( """ |Invalid command. @@ -37,6 +40,7 @@ object UpdaterSocket : Websocket() { | """.trimMargin(), ) + } } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/DirName.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/DirName.kt index cb1c9f83..276a7f75 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/DirName.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/util/DirName.kt @@ -48,7 +48,10 @@ private fun getChapterDir( chapterEntry[ChapterTable.scanlator] != null -> { "${chapterEntry[ChapterTable.scanlator]}_${chapterEntry[ChapterTable.name]}" } - else -> chapterEntry[ChapterTable.name] + + else -> { + chapterEntry[ChapterTable.name] + } }, ) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/opds/impl/OpdsEntryBuilder.kt b/server/src/main/kotlin/suwayomi/tachidesk/opds/impl/OpdsEntryBuilder.kt index b16ba7fc..e3bed66f 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/opds/impl/OpdsEntryBuilder.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/opds/impl/OpdsEntryBuilder.kt @@ -319,6 +319,7 @@ object OpdsEntryBuilder { } titlePrefix = statusKey.localized(locale) } + is ProgressSource.Remote -> { idSuffix = ":remote" titlePrefix = MR.strings.opds_chapter_status_synced.localized(locale, progressSource.device) @@ -378,6 +379,7 @@ object OpdsEntryBuilder { } titleRes.localized(locale) } + progressSource is ProgressSource.Local -> { val titleRes = if (progressSource.lastPageRead > 0) { @@ -387,6 +389,7 @@ object OpdsEntryBuilder { } titleRes.localized(locale) } + progressSource is ProgressSource.Remote -> { val titleRes = if (progressSource.lastPageRead > 0) { @@ -396,7 +399,11 @@ object OpdsEntryBuilder { } titleRes.localized(locale, progressSource.device) } - else -> "" // Should not happen + + else -> { + // Should not happen + "" + } } links.add( diff --git a/server/src/main/kotlin/suwayomi/tachidesk/opds/impl/OpdsFeedBuilder.kt b/server/src/main/kotlin/suwayomi/tachidesk/opds/impl/OpdsFeedBuilder.kt index f08bf8a9..af759c27 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/opds/impl/OpdsFeedBuilder.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/opds/impl/OpdsFeedBuilder.kt @@ -155,30 +155,40 @@ object OpdsFeedBuilder { val feedTitle = when (criteria.primaryFilter) { - PrimaryFilterType.SOURCE -> + PrimaryFilterType.SOURCE -> { MR.strings.opds_feeds_library_source_specific_title.localized( locale, result.feedTitleComponent ?: criteria.sourceId.toString(), ) - PrimaryFilterType.CATEGORY -> + } + + PrimaryFilterType.CATEGORY -> { MR.strings.opds_feeds_category_specific_title.localized( locale, result.feedTitleComponent ?: criteria.categoryId.toString(), ) - PrimaryFilterType.GENRE -> + } + + PrimaryFilterType.GENRE -> { MR.strings.opds_feeds_genre_specific_title.localized( locale, result.feedTitleComponent ?: "Unknown", ) + } + PrimaryFilterType.STATUS -> { val statusName = NavigationRepository.getStatuses(locale).find { it.id == criteria.statusId }?.title MR.strings.opds_feeds_status_specific_title.localized(locale, statusName ?: criteria.statusId.toString()) } + PrimaryFilterType.LANGUAGE -> { val langName = Locale.forLanguageTag(criteria.langCode ?: "").getDisplayName(locale) MR.strings.opds_feeds_language_specific_title.localized(locale, langName) } - else -> MR.strings.opds_feeds_all_series_in_library_title.localized(locale) + + else -> { + MR.strings.opds_feeds_all_series_in_library_title.localized(locale) + } } val feedUrl = diff --git a/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/MangaRepository.kt b/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/MangaRepository.kt index c3f04c64..d0b57c37 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/MangaRepository.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/opds/repository/MangaRepository.kt @@ -108,7 +108,7 @@ object MangaRepository { // Efficiently get the name of the primary filter item val specificFilterName = when (criteria.primaryFilter) { - PrimaryFilterType.SOURCE -> + PrimaryFilterType.SOURCE -> { criteria.sourceId?.let { SourceTable .select(SourceTable.name) @@ -116,7 +116,9 @@ object MangaRepository { .firstOrNull() ?.get(SourceTable.name) } - PrimaryFilterType.CATEGORY -> + } + + PrimaryFilterType.CATEGORY -> { criteria.categoryId?.let { CategoryTable .select(CategoryTable.name) @@ -124,10 +126,25 @@ object MangaRepository { .firstOrNull() ?.get(CategoryTable.name) } - PrimaryFilterType.GENRE -> criteria.genre - PrimaryFilterType.STATUS -> criteria.statusId.toString() // Controller will map this to a localized string - PrimaryFilterType.LANGUAGE -> criteria.langCode // Controller will map this to a display name - else -> null + } + + PrimaryFilterType.GENRE -> { + criteria.genre + } + + // Controller will map this to a localized string + PrimaryFilterType.STATUS -> { + criteria.statusId.toString() + } + + // Controller will map this to a display name + PrimaryFilterType.LANGUAGE -> { + criteria.langCode + } + + else -> { + null + } } applyMangaLibrarySortAndFilter(query, sort, filter) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/opds/util/OpdsStringUtil.kt b/server/src/main/kotlin/suwayomi/tachidesk/opds/util/OpdsStringUtil.kt index 22b1c5d4..364e611e 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/opds/util/OpdsStringUtil.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/opds/util/OpdsStringUtil.kt @@ -44,10 +44,18 @@ object OpdsStringUtil { if (serverConfig.opdsUseBinaryFileSizes.value) { // Binary notation (base 1024) when { - size >= 1_125_899_906_842_624 -> "%.2f TiB".format(size / 1_125_899_906_842_624.0) // 1024^4 - size >= 1_073_741_824 -> "%.2f GiB".format(size / 1_073_741_824.0) // 1024^3 - size >= 1_048_576 -> "%.2f MiB".format(size / 1_048_576.0) // 1024^2 - size >= 1024 -> "%.2f KiB".format(size / 1024.0) // 1024 + // 1024^4 + size >= 1_125_899_906_842_624 -> "%.2f TiB".format(size / 1_125_899_906_842_624.0) + + // 1024^3 + size >= 1_073_741_824 -> "%.2f GiB".format(size / 1_073_741_824.0) + + // 1024^2 + size >= 1_048_576 -> "%.2f MiB".format(size / 1_048_576.0) + + // 1024 + size >= 1024 -> "%.2f KiB".format(size / 1024.0) + else -> "$size bytes" } } else { diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBManager.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBManager.kt index 7171caae..78c9fc8c 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBManager.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/DBManager.kt @@ -52,6 +52,7 @@ object DBManager { addDataSourceProperty("cachePrepStmts", "true") addDataSourceProperty("useServerPrepStmts", "true") } + DatabaseType.H2 -> { jdbcUrl = "jdbc:h2:${applicationDirs.dataRoot}/database" driverClassName = "org.h2.Driver" @@ -103,7 +104,7 @@ object DBManager { .connect(hikariDataSource!!, databaseConfig = dbConfig) } else { when (serverConfig.databaseType.value) { - DatabaseType.POSTGRESQL -> + DatabaseType.POSTGRESQL -> { Database.connect( "jdbc:${serverConfig.databaseUrl.value}", "org.postgresql.Driver", @@ -111,12 +112,15 @@ object DBManager { password = serverConfig.databasePassword.value, databaseConfig = dbConfig, ) - DatabaseType.H2 -> + } + + DatabaseType.H2 -> { Database.connect( "jdbc:h2:${Injekt.get().dataRoot}/database", "org.h2.Driver", databaseConfig = dbConfig, ) + } } }.also { db = it } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/helpers/TypeHelpers.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/helpers/TypeHelpers.kt index b8c5f562..80731ad2 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/helpers/TypeHelpers.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/database/migration/helpers/TypeHelpers.kt @@ -6,7 +6,9 @@ import suwayomi.tachidesk.server.serverConfig val UNLIMITED_TEXT get() = when (serverConfig.databaseType.value) { - DatabaseType.H2 -> "VARCHAR" // the default length is `Integer.MAX_VALUE` + // the default length is `Integer.MAX_VALUE` + DatabaseType.H2 -> "VARCHAR" + DatabaseType.POSTGRESQL -> "TEXT" } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/user/UserType.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/user/UserType.kt index 7743a066..70b5e042 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/user/UserType.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/user/UserType.kt @@ -25,8 +25,14 @@ fun UserType.requireUser(): Int = fun UserType.requireUserWithBasicFallback(ctx: Context): Int = when (this) { - is UserType.Admin -> id - UserType.Visitor if ctx.getAttribute(Attribute.TachideskBasic) -> 1 + is UserType.Admin -> { + id + } + + UserType.Visitor if ctx.getAttribute(Attribute.TachideskBasic) -> { + 1 + } + UserType.Visitor -> { ctx.header("WWW-Authenticate", "Basic") throw UnauthorizedException() @@ -53,8 +59,14 @@ fun getUserFromContext(ctx: Context): UserType { return when (serverConfig.authMode.value) { // NOTE: Basic Auth is expected to have been validated by JavalinSetup - AuthMode.NONE, AuthMode.BASIC_AUTH -> UserType.Admin(1) - AuthMode.SIMPLE_LOGIN -> if (cookieValid()) UserType.Admin(1) else UserType.Visitor + AuthMode.NONE, AuthMode.BASIC_AUTH -> { + UserType.Admin(1) + } + + AuthMode.SIMPLE_LOGIN -> { + if (cookieValid()) UserType.Admin(1) else UserType.Visitor + } + AuthMode.UI_LOGIN -> { val authentication = ctx.header(Header.AUTHORIZATION) ?: ctx.cookie("suwayomi-server-token") val token = authentication?.substringAfter("Bearer ") ?: ctx.queryParam("token") @@ -72,8 +84,14 @@ fun getUserFromWsContext(ctx: WsConnectContext): UserType { return when (serverConfig.authMode.value) { // NOTE: Basic Auth is expected to have been validated by JavalinSetup - AuthMode.NONE, AuthMode.BASIC_AUTH -> UserType.Admin(1) - AuthMode.SIMPLE_LOGIN -> if (cookieValid()) UserType.Admin(1) else UserType.Visitor + AuthMode.NONE, AuthMode.BASIC_AUTH -> { + UserType.Admin(1) + } + + AuthMode.SIMPLE_LOGIN -> { + if (cookieValid()) UserType.Admin(1) else UserType.Visitor + } + AuthMode.UI_LOGIN -> { val authentication = ctx.header(Header.AUTHORIZATION) ?: ctx.header("Sec-WebSocket-Protocol") ?: ctx.cookie("suwayomi-server-token") diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/util/AppMutex.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/util/AppMutex.kt index ead11e93..d77d6fb4 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/util/AppMutex.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/util/AppMutex.kt @@ -72,6 +72,7 @@ object AppMutex { AppMutexState.Clear -> { logger.info { "Mutex status is clear, Resuming startup." } } + AppMutexState.TachideskInstanceRunning -> { logger.info { "Another instance of Suwayomi-Server is running on $appIP:${serverConfig.port.value}" } @@ -82,6 +83,7 @@ object AppMutex { shutdownApp(MutexCheckFailedTachideskRunning) } + AppMutexState.OtherApplicationRunning -> { logger.error { "A non Suwayomi-Server application is running on $appIP:${serverConfig.port.value}, aborting startup." } shutdownApp(MutexCheckFailedAnotherAppRunning) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/util/DocumentationDsl.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/util/DocumentationDsl.kt index 9d847d0f..3cdbe9a3 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/util/DocumentationDsl.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/util/DocumentationDsl.kt @@ -42,12 +42,30 @@ fun getParam( } val typedItem: Any? = when (val clazz = param.clazz as Class) { - String::class.java, java.lang.String::class.java -> getSimpleParamItem(ctx, param) ?: param.defaultValue - Int::class.java, java.lang.Integer::class.java -> getSimpleParamItem(ctx, param)?.toIntOrNull() ?: param.defaultValue - Long::class.java, java.lang.Long::class.java -> getSimpleParamItem(ctx, param)?.toLongOrNull() ?: param.defaultValue - Boolean::class.java, java.lang.Boolean::class.java -> getSimpleParamItem(ctx, param)?.toBoolean() ?: param.defaultValue - Float::class.java, java.lang.Float::class.java -> getSimpleParamItem(ctx, param)?.toFloatOrNull() ?: param.defaultValue - Double::class.java, java.lang.Double::class.java -> getSimpleParamItem(ctx, param)?.toDoubleOrNull() ?: param.defaultValue + String::class.java, java.lang.String::class.java -> { + getSimpleParamItem(ctx, param) ?: param.defaultValue + } + + Int::class.java, java.lang.Integer::class.java -> { + getSimpleParamItem(ctx, param)?.toIntOrNull() ?: param.defaultValue + } + + Long::class.java, java.lang.Long::class.java -> { + getSimpleParamItem(ctx, param)?.toLongOrNull() ?: param.defaultValue + } + + Boolean::class.java, java.lang.Boolean::class.java -> { + getSimpleParamItem(ctx, param)?.toBoolean() ?: param.defaultValue + } + + Float::class.java, java.lang.Float::class.java -> { + getSimpleParamItem(ctx, param)?.toFloatOrNull() ?: param.defaultValue + } + + Double::class.java, java.lang.Double::class.java -> { + getSimpleParamItem(ctx, param)?.toDoubleOrNull() ?: param.defaultValue + } + else -> { when (param) { is Param.FormParam -> ctx.formParamAsClass(param.key, clazz) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt b/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt index 3358f9b4..cd20c40d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/server/util/WebInterfaceManager.kt @@ -12,6 +12,7 @@ import android.content.Context import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.awaitSuccess +import eu.kanade.tachiyomi.util.lang.launchIO import io.github.oshai.kotlinlogging.KLogger import io.github.oshai.kotlinlogging.KotlinLogging import io.github.reactivecircus.cache4k.Cache @@ -193,7 +194,7 @@ object WebInterfaceManager { } @OptIn(DelicateCoroutinesApi::class) - GlobalScope.launch(Dispatchers.IO) { + GlobalScope.launchIO { setupWebUI() isSetupComplete = true } @@ -400,7 +401,7 @@ object WebInterfaceManager { try { downloadVersion(flavor, getVersion()) true - } catch (e: Exception) { + } catch (_: Exception) { false } || isLocalWebUIValid @@ -427,7 +428,7 @@ object WebInterfaceManager { try { setupBundledWebUI() - } catch (e: Exception) { + } catch (_: Exception) { throw Exception("Unable to setup a webUI") } } @@ -498,7 +499,7 @@ object WebInterfaceManager { private fun getLocalVersion(path: String = applicationDirs.webUIRoot): String = try { File("$path/revision").readText().trim() - } catch (e: Exception) { + } catch (_: Exception) { "r-1" } @@ -585,7 +586,7 @@ object WebInterfaceManager { .string() .trim() }) - } catch (e: Exception) { + } catch (_: Exception) { "" } @@ -712,7 +713,7 @@ object WebInterfaceManager { version: String, ) { @OptIn(DelicateCoroutinesApi::class) - GlobalScope.launch(Dispatchers.IO) { + GlobalScope.launchIO { downloadVersion(flavor, version) serveWebUI() } diff --git a/server/src/test/kotlin/masstest/TestExtensionCompatibility.kt b/server/src/test/kotlin/masstest/TestExtensionCompatibility.kt index e7d6b421..f5055112 100644 --- a/server/src/test/kotlin/masstest/TestExtensionCompatibility.kt +++ b/server/src/test/kotlin/masstest/TestExtensionCompatibility.kt @@ -61,9 +61,11 @@ class TestExtensionCompatibility { it.obsolete -> { uninstallExtension(it.pkgName) } + it.hasUpdate -> { updateExtension(it.pkgName) } + else -> { uninstallExtension(it.pkgName) installExtension(it.pkgName)