mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-11 15:22:05 +01:00
Format
This commit is contained in:
@@ -242,13 +242,14 @@ class JavaSharedPreferences(
|
|||||||
}
|
}
|
||||||
notify(it.key)
|
notify(it.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
is Action.Remove -> {
|
is Action.Remove -> {
|
||||||
preferences.remove(it.key)
|
preferences.remove(it.key)
|
||||||
/**
|
/*
|
||||||
* Set<String> are stored like
|
Set<String> are stored like
|
||||||
* key.0 = value1
|
key.0 = value1
|
||||||
* key.1 = value2
|
key.1 = value2
|
||||||
* key.size = 2
|
key.size = 2
|
||||||
*/
|
*/
|
||||||
preferences.keys.forEach { key ->
|
preferences.keys.forEach { key ->
|
||||||
if (key.startsWith(it.key + ".")) {
|
if (key.startsWith(it.key + ".")) {
|
||||||
@@ -258,7 +259,10 @@ class JavaSharedPreferences(
|
|||||||
|
|
||||||
notify(it.key)
|
notify(it.key)
|
||||||
}
|
}
|
||||||
Action.Clear -> preferences.clear()
|
|
||||||
|
Action.Clear -> {
|
||||||
|
preferences.clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,8 +72,12 @@ internal class RateLimitInterceptor(
|
|||||||
|
|
||||||
val request = chain.request()
|
val request = chain.request()
|
||||||
when (host) {
|
when (host) {
|
||||||
null, request.url.host -> {} // need rate limit
|
// need rate limit
|
||||||
else -> return chain.proceed(request)
|
null, request.url.host -> {}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
return chain.proceed(request)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ class LocalSource(
|
|||||||
mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name })
|
mangaDirs.sortedWith(compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is OrderBy.Latest -> {
|
is OrderBy.Latest -> {
|
||||||
mangaDirs =
|
mangaDirs =
|
||||||
if (filter.state!!.ascending) {
|
if (filter.state!!.ascending) {
|
||||||
@@ -246,6 +247,7 @@ class LocalSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is Format.Rar -> {
|
is Format.Rar -> {
|
||||||
JunrarArchive(chapter).use { rar ->
|
JunrarArchive(chapter).use { rar ->
|
||||||
rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile ->
|
rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile ->
|
||||||
@@ -255,6 +257,7 @@ class LocalSource(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,6 +340,7 @@ class LocalSource(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is Format.Zip -> {
|
is Format.Zip -> {
|
||||||
val loader = ZipPageLoader(format.file)
|
val loader = ZipPageLoader(format.file)
|
||||||
val pages = loader.getPages()
|
val pages = loader.getPages()
|
||||||
@@ -344,6 +348,7 @@ class LocalSource(
|
|||||||
|
|
||||||
pages
|
pages
|
||||||
}
|
}
|
||||||
|
|
||||||
is Format.Rar -> {
|
is Format.Rar -> {
|
||||||
val loader = RarPageLoader(format.file)
|
val loader = RarPageLoader(format.file)
|
||||||
val pages = loader.getPages()
|
val pages = loader.getPages()
|
||||||
@@ -351,6 +356,7 @@ class LocalSource(
|
|||||||
|
|
||||||
pages
|
pages
|
||||||
}
|
}
|
||||||
|
|
||||||
is Format.Epub -> {
|
is Format.Epub -> {
|
||||||
val loader = EpubPageLoader(format.file)
|
val loader = EpubPageLoader(format.file)
|
||||||
val pages = loader.getPages()
|
val pages = loader.getPages()
|
||||||
@@ -390,6 +396,7 @@ class LocalSource(
|
|||||||
|
|
||||||
entry?.let { coverManager.update(manga, it.inputStream()) }
|
entry?.let { coverManager.update(manga, it.inputStream()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
is Format.Zip -> {
|
is Format.Zip -> {
|
||||||
ZipFile.builder().setFile(format.file).get().use { zip ->
|
ZipFile.builder().setFile(format.file).get().use { zip ->
|
||||||
val entry =
|
val entry =
|
||||||
@@ -401,6 +408,7 @@ class LocalSource(
|
|||||||
entry?.let { coverManager.update(manga, zip.getInputStream(it)) }
|
entry?.let { coverManager.update(manga, zip.getInputStream(it)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is Format.Rar -> {
|
is Format.Rar -> {
|
||||||
JunrarArchive(format.file).use { archive ->
|
JunrarArchive(format.file).use { archive ->
|
||||||
val entry =
|
val entry =
|
||||||
@@ -411,6 +419,7 @@ class LocalSource(
|
|||||||
entry?.let { coverManager.update(manga, archive.getInputStream(it)) }
|
entry?.let { coverManager.update(manga, archive.getInputStream(it)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is Format.Epub -> {
|
is Format.Epub -> {
|
||||||
EpubFile(format.file).use { epub ->
|
EpubFile(format.file).use { epub ->
|
||||||
val entry =
|
val entry =
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ object ChapterRecognition {
|
|||||||
numberMatch.none() -> {
|
numberMatch.none() -> {
|
||||||
return chapterNumber ?: -1.0
|
return chapterNumber ?: -1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
numberMatch.count() > 1 -> {
|
numberMatch.count() > 1 -> {
|
||||||
// Remove unwanted tags.
|
// Remove unwanted tags.
|
||||||
unwanted.replace(cleanChapterName, "").let { name ->
|
unwanted.replace(cleanChapterName, "").let { name ->
|
||||||
|
|||||||
@@ -367,37 +367,127 @@ class KcefWebView {
|
|||||||
): KeyEvent? {
|
): KeyEvent? {
|
||||||
val code =
|
val code =
|
||||||
when (char.uppercaseChar()) {
|
when (char.uppercaseChar()) {
|
||||||
in 'A'..'Z', in '0'..'9' -> char.uppercaseChar().code
|
in 'A'..'Z', in '0'..'9' -> {
|
||||||
'&' -> KeyEvent.VK_AMPERSAND
|
char.uppercaseChar().code
|
||||||
'*' -> KeyEvent.VK_ASTERISK
|
}
|
||||||
'@' -> KeyEvent.VK_AT
|
|
||||||
'\\' -> KeyEvent.VK_BACK_SLASH
|
'&' -> {
|
||||||
'{' -> KeyEvent.VK_BRACELEFT
|
KeyEvent.VK_AMPERSAND
|
||||||
'}' -> KeyEvent.VK_BRACERIGHT
|
}
|
||||||
'^' -> KeyEvent.VK_CIRCUMFLEX
|
|
||||||
']' -> KeyEvent.VK_CLOSE_BRACKET
|
'*' -> {
|
||||||
':' -> KeyEvent.VK_COLON
|
KeyEvent.VK_ASTERISK
|
||||||
',' -> KeyEvent.VK_COMMA
|
}
|
||||||
'$' -> KeyEvent.VK_DOLLAR
|
|
||||||
'=' -> KeyEvent.VK_EQUALS
|
'@' -> {
|
||||||
'€' -> KeyEvent.VK_EURO_SIGN
|
KeyEvent.VK_AT
|
||||||
'!' -> KeyEvent.VK_EXCLAMATION_MARK
|
}
|
||||||
'>' -> KeyEvent.VK_GREATER
|
|
||||||
'(' -> KeyEvent.VK_LEFT_PARENTHESIS
|
'\\' -> {
|
||||||
'<' -> KeyEvent.VK_LESS
|
KeyEvent.VK_BACK_SLASH
|
||||||
'-' -> KeyEvent.VK_MINUS
|
}
|
||||||
'#' -> KeyEvent.VK_NUMBER_SIGN
|
|
||||||
'[' -> KeyEvent.VK_OPEN_BRACKET
|
'{' -> {
|
||||||
'.' -> KeyEvent.VK_PERIOD
|
KeyEvent.VK_BRACELEFT
|
||||||
'+' -> KeyEvent.VK_PLUS
|
}
|
||||||
'\'' -> KeyEvent.VK_QUOTE
|
|
||||||
'"' -> KeyEvent.VK_QUOTEDBL
|
'}' -> {
|
||||||
')' -> KeyEvent.VK_RIGHT_PARENTHESIS
|
KeyEvent.VK_BRACERIGHT
|
||||||
';' -> KeyEvent.VK_SEMICOLON
|
}
|
||||||
'/' -> KeyEvent.VK_SLASH
|
|
||||||
' ' -> KeyEvent.VK_SPACE
|
'^' -> {
|
||||||
'_' -> KeyEvent.VK_UNDERSCORE
|
KeyEvent.VK_CIRCUMFLEX
|
||||||
else ->
|
}
|
||||||
|
|
||||||
|
']' -> {
|
||||||
|
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) {
|
when (strKey) {
|
||||||
"Alt" -> KeyEvent.VK_ALT
|
"Alt" -> KeyEvent.VK_ALT
|
||||||
"Backspace" -> KeyEvent.VK_BACK_SPACE
|
"Backspace" -> KeyEvent.VK_BACK_SPACE
|
||||||
@@ -435,6 +525,7 @@ class KcefWebView {
|
|||||||
"ArrowUp" -> KeyEvent.VK_UP
|
"ArrowUp" -> KeyEvent.VK_UP
|
||||||
else -> KeyEvent.VK_UNDEFINED
|
else -> KeyEvent.VK_UNDEFINED
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (id == KeyEvent.KEY_TYPED) {
|
if (id == KeyEvent.KEY_TYPED) {
|
||||||
if (char == KeyEvent.CHAR_UNDEFINED && code != KeyEvent.VK_ENTER) return null
|
if (char == KeyEvent.CHAR_UNDEFINED && code != KeyEvent.VK_ENTER) return null
|
||||||
|
|||||||
@@ -104,18 +104,23 @@ object WebView : Websocket<String>() {
|
|||||||
dr.resize(event.width, event.height)
|
dr.resize(event.width, event.height)
|
||||||
logger.debug { "Loading URL $url" }
|
logger.debug { "Loading URL $url" }
|
||||||
}
|
}
|
||||||
|
|
||||||
is ResizeMessage -> {
|
is ResizeMessage -> {
|
||||||
dr.resize(event.width, event.height)
|
dr.resize(event.width, event.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
is JsEventMessage -> {
|
is JsEventMessage -> {
|
||||||
dr.event(event)
|
dr.event(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
is JsPasteMessage -> {
|
is JsPasteMessage -> {
|
||||||
dr.paste(event.data)
|
dr.paste(event.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
is JsCopyMessage -> {
|
is JsCopyMessage -> {
|
||||||
dr.copy()
|
dr.copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
is JsPingMessage -> {
|
is JsPingMessage -> {
|
||||||
notifyAllClients("{\"type\":\"pong\"}")
|
notifyAllClients("{\"type\":\"pong\"}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,9 +135,11 @@ class SourceMutation {
|
|||||||
filters = updateFilterList(source, filters),
|
filters = updateFilterList(source, filters),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
FetchSourceMangaType.POPULAR -> {
|
FetchSourceMangaType.POPULAR -> {
|
||||||
source.getPopularManga(page)
|
source.getPopularManga(page)
|
||||||
}
|
}
|
||||||
|
|
||||||
FetchSourceMangaType.LATEST -> {
|
FetchSourceMangaType.LATEST -> {
|
||||||
if (!source.supportsLatest) throw Exception("Source does not support latest")
|
if (!source.supportsLatest) throw Exception("Source does not support latest")
|
||||||
source.getLatestUpdates(page)
|
source.getLatestUpdates(page)
|
||||||
|
|||||||
@@ -61,8 +61,14 @@ class TrackQuery {
|
|||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
): Boolean =
|
): Boolean =
|
||||||
when (this) {
|
when (this) {
|
||||||
ID -> tracker.id > cursor.value.toInt()
|
ID -> {
|
||||||
NAME -> tracker.name > cursor.value
|
tracker.id > cursor.value.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
NAME -> {
|
||||||
|
tracker.name > cursor.value
|
||||||
|
}
|
||||||
|
|
||||||
IS_LOGGED_IN -> {
|
IS_LOGGED_IN -> {
|
||||||
val value = cursor.value.substringAfter('-').toBooleanStrict()
|
val value = cursor.value.substringAfter('-').toBooleanStrict()
|
||||||
!value || tracker.isLoggedIn
|
!value || tracker.isLoggedIn
|
||||||
@@ -74,8 +80,14 @@ class TrackQuery {
|
|||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
): Boolean =
|
): Boolean =
|
||||||
when (this) {
|
when (this) {
|
||||||
ID -> tracker.id < cursor.value.toInt()
|
ID -> {
|
||||||
NAME -> tracker.name < cursor.value
|
tracker.id < cursor.value.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
NAME -> {
|
||||||
|
tracker.name < cursor.value
|
||||||
|
}
|
||||||
|
|
||||||
IS_LOGGED_IN -> {
|
IS_LOGGED_IN -> {
|
||||||
val value = cursor.value.substringAfter('-').toBooleanStrict()
|
val value = cursor.value.substringAfter('-').toBooleanStrict()
|
||||||
value || !tracker.isLoggedIn
|
value || !tracker.isLoggedIn
|
||||||
@@ -159,18 +171,21 @@ class TrackQuery {
|
|||||||
|
|
||||||
res =
|
res =
|
||||||
when (orderType) {
|
when (orderType) {
|
||||||
SortOrder.DESC, SortOrder.DESC_NULLS_FIRST, SortOrder.DESC_NULLS_LAST ->
|
SortOrder.DESC, SortOrder.DESC_NULLS_FIRST, SortOrder.DESC_NULLS_LAST -> {
|
||||||
when (orderBy) {
|
when (orderBy) {
|
||||||
TrackerOrderBy.ID -> res.sortedByDescending { it.id }
|
TrackerOrderBy.ID -> res.sortedByDescending { it.id }
|
||||||
TrackerOrderBy.NAME -> res.sortedByDescending { it.name }
|
TrackerOrderBy.NAME -> res.sortedByDescending { it.name }
|
||||||
TrackerOrderBy.IS_LOGGED_IN -> res.sortedByDescending { it.isLoggedIn }
|
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) {
|
when (orderBy) {
|
||||||
TrackerOrderBy.ID -> res.sortedBy { it.id }
|
TrackerOrderBy.ID -> res.sortedBy { it.id }
|
||||||
TrackerOrderBy.NAME -> res.sortedBy { it.name }
|
TrackerOrderBy.NAME -> res.sortedBy { it.name }
|
||||||
TrackerOrderBy.IS_LOGGED_IN -> res.sortedBy { it.isLoggedIn }
|
TrackerOrderBy.IS_LOGGED_IN -> res.sortedBy { it.isLoggedIn }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ class JavalinGraphQLRequestParser : GraphQLRequestParser<Context> {
|
|||||||
is GraphQLRequest -> {
|
is GraphQLRequest -> {
|
||||||
request.copy(variables = request.variables?.modifyFiles(mapItems))
|
request.copy(variables = request.variables?.modifyFiles(mapItems))
|
||||||
}
|
}
|
||||||
|
|
||||||
is GraphQLBatchRequest -> {
|
is GraphQLBatchRequest -> {
|
||||||
request.copy(
|
request.copy(
|
||||||
requests =
|
requests =
|
||||||
|
|||||||
@@ -63,10 +63,16 @@ class CustomSchemaGeneratorHooks : FlowSubscriptionSchemaGeneratorHooks() {
|
|||||||
|
|
||||||
override fun willGenerateGraphQLType(type: KType): GraphQLType? =
|
override fun willGenerateGraphQLType(type: KType): GraphQLType? =
|
||||||
when (type.classifier as? KClass<*>) {
|
when (type.classifier as? KClass<*>) {
|
||||||
Long::class -> GraphQLLongAsString // encode to string for JS
|
// encode to string for JS
|
||||||
Duration::class -> GraphQLDurationAsString // encode Duration as ISO-8601 string
|
Long::class -> GraphQLLongAsString
|
||||||
|
|
||||||
|
// encode Duration as ISO-8601 string
|
||||||
|
Duration::class -> GraphQLDurationAsString
|
||||||
|
|
||||||
Cursor::class -> GraphQLCursor
|
Cursor::class -> GraphQLCursor
|
||||||
|
|
||||||
UploadedFile::class -> GraphQLUpload
|
UploadedFile::class -> GraphQLUpload
|
||||||
|
|
||||||
else -> super.willGenerateGraphQLType(type)
|
else -> super.willGenerateGraphQLType(type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ private class GraphqlDurationAsStringCoercing : Coercing<Duration, String> {
|
|||||||
private fun toStringImpl(input: Any): String =
|
private fun toStringImpl(input: Any): String =
|
||||||
when (input) {
|
when (input) {
|
||||||
is Duration -> input.toIsoString()
|
is Duration -> input.toIsoString()
|
||||||
|
|
||||||
is String -> Duration.parse(input).toIsoString()
|
is String -> Duration.parse(input).toIsoString()
|
||||||
|
|
||||||
else -> throw CoercingSerializeException(
|
else -> throw CoercingSerializeException(
|
||||||
"Expected a Duration or String but was ${CoercingUtil.typeName(input)}",
|
"Expected a Duration or String but was ${CoercingUtil.typeName(input)}",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,46 +31,59 @@ data class BackupRestoreStatus(
|
|||||||
|
|
||||||
fun ProtoBackupImport.BackupRestoreState.toStatus(): BackupRestoreStatus =
|
fun ProtoBackupImport.BackupRestoreState.toStatus(): BackupRestoreStatus =
|
||||||
when (this) {
|
when (this) {
|
||||||
ProtoBackupImport.BackupRestoreState.Idle ->
|
ProtoBackupImport.BackupRestoreState.Idle -> {
|
||||||
BackupRestoreStatus(
|
BackupRestoreStatus(
|
||||||
state = BackupRestoreState.IDLE,
|
state = BackupRestoreState.IDLE,
|
||||||
totalManga = 0,
|
totalManga = 0,
|
||||||
mangaProgress = 0,
|
mangaProgress = 0,
|
||||||
)
|
)
|
||||||
is ProtoBackupImport.BackupRestoreState.Success ->
|
}
|
||||||
|
|
||||||
|
is ProtoBackupImport.BackupRestoreState.Success -> {
|
||||||
BackupRestoreStatus(
|
BackupRestoreStatus(
|
||||||
state = BackupRestoreState.SUCCESS,
|
state = BackupRestoreState.SUCCESS,
|
||||||
totalManga = 0,
|
totalManga = 0,
|
||||||
mangaProgress = 0,
|
mangaProgress = 0,
|
||||||
)
|
)
|
||||||
is ProtoBackupImport.BackupRestoreState.Failure ->
|
}
|
||||||
|
|
||||||
|
is ProtoBackupImport.BackupRestoreState.Failure -> {
|
||||||
BackupRestoreStatus(
|
BackupRestoreStatus(
|
||||||
state = BackupRestoreState.FAILURE,
|
state = BackupRestoreState.FAILURE,
|
||||||
totalManga = 0,
|
totalManga = 0,
|
||||||
mangaProgress = 0,
|
mangaProgress = 0,
|
||||||
)
|
)
|
||||||
is ProtoBackupImport.BackupRestoreState.RestoringCategories ->
|
}
|
||||||
|
|
||||||
|
is ProtoBackupImport.BackupRestoreState.RestoringCategories -> {
|
||||||
BackupRestoreStatus(
|
BackupRestoreStatus(
|
||||||
state = BackupRestoreState.RESTORING_CATEGORIES,
|
state = BackupRestoreState.RESTORING_CATEGORIES,
|
||||||
totalManga = totalManga,
|
totalManga = totalManga,
|
||||||
mangaProgress = current,
|
mangaProgress = current,
|
||||||
)
|
)
|
||||||
is ProtoBackupImport.BackupRestoreState.RestoringMeta ->
|
}
|
||||||
|
|
||||||
|
is ProtoBackupImport.BackupRestoreState.RestoringMeta -> {
|
||||||
BackupRestoreStatus(
|
BackupRestoreStatus(
|
||||||
state = BackupRestoreState.RESTORING_META,
|
state = BackupRestoreState.RESTORING_META,
|
||||||
totalManga = totalManga,
|
totalManga = totalManga,
|
||||||
mangaProgress = current,
|
mangaProgress = current,
|
||||||
)
|
)
|
||||||
is ProtoBackupImport.BackupRestoreState.RestoringSettings ->
|
}
|
||||||
|
|
||||||
|
is ProtoBackupImport.BackupRestoreState.RestoringSettings -> {
|
||||||
BackupRestoreStatus(
|
BackupRestoreStatus(
|
||||||
state = BackupRestoreState.RESTORING_SETTINGS,
|
state = BackupRestoreState.RESTORING_SETTINGS,
|
||||||
totalManga = totalManga,
|
totalManga = totalManga,
|
||||||
mangaProgress = current,
|
mangaProgress = current,
|
||||||
)
|
)
|
||||||
is ProtoBackupImport.BackupRestoreState.RestoringManga ->
|
}
|
||||||
|
|
||||||
|
is ProtoBackupImport.BackupRestoreState.RestoringManga -> {
|
||||||
BackupRestoreStatus(
|
BackupRestoreStatus(
|
||||||
state = BackupRestoreState.RESTORING_MANGA,
|
state = BackupRestoreState.RESTORING_MANGA,
|
||||||
totalManga = totalManga,
|
totalManga = totalManga,
|
||||||
mangaProgress = current,
|
mangaProgress = current,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,12 +204,27 @@ data class GroupFilter(
|
|||||||
|
|
||||||
fun filterOf(filter: SourceFilter<*>): Filter =
|
fun filterOf(filter: SourceFilter<*>): Filter =
|
||||||
when (filter) {
|
when (filter) {
|
||||||
is SourceFilter.Header -> HeaderFilter(filter.name)
|
is SourceFilter.Header -> {
|
||||||
is SourceFilter.Separator -> SeparatorFilter(filter.name)
|
HeaderFilter(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.Separator -> {
|
||||||
is SourceFilter.TriState ->
|
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(
|
TriStateFilter(
|
||||||
filter.name,
|
filter.name,
|
||||||
when (filter.state) {
|
when (filter.state) {
|
||||||
@@ -218,13 +233,22 @@ fun filterOf(filter: SourceFilter<*>): Filter =
|
|||||||
else -> TriState.IGNORE
|
else -> TriState.IGNORE
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
is SourceFilter.Group<*> ->
|
}
|
||||||
|
|
||||||
|
is SourceFilter.Group<*> -> {
|
||||||
GroupFilter(
|
GroupFilter(
|
||||||
filter.name,
|
filter.name,
|
||||||
filter.state.map { filterOf(it as SourceFilter<*>) },
|
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 {
|
/*sealed interface FilterChange {
|
||||||
@@ -286,25 +310,31 @@ fun updateFilterList(
|
|||||||
is SourceFilter.Header -> {
|
is SourceFilter.Header -> {
|
||||||
// NOOP
|
// NOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
is SourceFilter.Separator -> {
|
is SourceFilter.Separator -> {
|
||||||
// NOOP
|
// NOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
is SourceFilter.Select<*> -> {
|
is SourceFilter.Select<*> -> {
|
||||||
filter.state = change.selectState
|
filter.state = change.selectState
|
||||||
?: throw Exception("Expected select state change at position ${change.position}")
|
?: throw Exception("Expected select state change at position ${change.position}")
|
||||||
}
|
}
|
||||||
|
|
||||||
is SourceFilter.Text -> {
|
is SourceFilter.Text -> {
|
||||||
filter.state = change.textState
|
filter.state = change.textState
|
||||||
?: throw Exception("Expected text state change at position ${change.position}")
|
?: throw Exception("Expected text state change at position ${change.position}")
|
||||||
}
|
}
|
||||||
|
|
||||||
is SourceFilter.CheckBox -> {
|
is SourceFilter.CheckBox -> {
|
||||||
filter.state = change.checkBoxState
|
filter.state = change.checkBoxState
|
||||||
?: throw Exception("Expected checkbox state change at position ${change.position}")
|
?: throw Exception("Expected checkbox state change at position ${change.position}")
|
||||||
}
|
}
|
||||||
|
|
||||||
is SourceFilter.TriState -> {
|
is SourceFilter.TriState -> {
|
||||||
filter.state = change.triState?.ordinal
|
filter.state = change.triState?.ordinal
|
||||||
?: throw Exception("Expected tri state change at position ${change.position}")
|
?: throw Exception("Expected tri state change at position ${change.position}")
|
||||||
}
|
}
|
||||||
|
|
||||||
is SourceFilter.Group<*> -> {
|
is SourceFilter.Group<*> -> {
|
||||||
val groupChange =
|
val groupChange =
|
||||||
change.groupChange
|
change.groupChange
|
||||||
@@ -315,20 +345,24 @@ fun updateFilterList(
|
|||||||
groupFilter.state = groupChange.checkBoxState
|
groupFilter.state = groupChange.checkBoxState
|
||||||
?: throw Exception("Expected checkbox state change at position ${change.position}")
|
?: throw Exception("Expected checkbox state change at position ${change.position}")
|
||||||
}
|
}
|
||||||
|
|
||||||
is SourceFilter.TriState -> {
|
is SourceFilter.TriState -> {
|
||||||
groupFilter.state = groupChange.triState?.ordinal
|
groupFilter.state = groupChange.triState?.ordinal
|
||||||
?: throw Exception("Expected tri state change at position ${change.position}")
|
?: throw Exception("Expected tri state change at position ${change.position}")
|
||||||
}
|
}
|
||||||
|
|
||||||
is SourceFilter.Text -> {
|
is SourceFilter.Text -> {
|
||||||
groupFilter.state = groupChange.textState
|
groupFilter.state = groupChange.textState
|
||||||
?: throw Exception("Expected text state change at position ${change.position}")
|
?: throw Exception("Expected text state change at position ${change.position}")
|
||||||
}
|
}
|
||||||
|
|
||||||
is SourceFilter.Select<*> -> {
|
is SourceFilter.Select<*> -> {
|
||||||
groupFilter.state = groupChange.selectState
|
groupFilter.state = groupChange.selectState
|
||||||
?: throw Exception("Expected select state change at position ${change.position}")
|
?: throw Exception("Expected select state change at position ${change.position}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is SourceFilter.Sort -> {
|
is SourceFilter.Sort -> {
|
||||||
filter.state = change.sortState?.run {
|
filter.state = change.sortState?.run {
|
||||||
SourceFilter.Sort.Selection(index, ascending)
|
SourceFilter.Sort.Selection(index, ascending)
|
||||||
@@ -402,7 +436,7 @@ data class MultiSelectListPreference(
|
|||||||
|
|
||||||
fun preferenceOf(preference: SourcePreference): Preference =
|
fun preferenceOf(preference: SourcePreference): Preference =
|
||||||
when (preference) {
|
when (preference) {
|
||||||
is SourceSwitchPreference ->
|
is SourceSwitchPreference -> {
|
||||||
SwitchPreference(
|
SwitchPreference(
|
||||||
preference.key,
|
preference.key,
|
||||||
preference.title?.toString(),
|
preference.title?.toString(),
|
||||||
@@ -412,7 +446,9 @@ fun preferenceOf(preference: SourcePreference): Preference =
|
|||||||
preference.currentValue as Boolean,
|
preference.currentValue as Boolean,
|
||||||
preference.defaultValue as Boolean,
|
preference.defaultValue as Boolean,
|
||||||
)
|
)
|
||||||
is SourceCheckBoxPreference ->
|
}
|
||||||
|
|
||||||
|
is SourceCheckBoxPreference -> {
|
||||||
CheckBoxPreference(
|
CheckBoxPreference(
|
||||||
preference.key,
|
preference.key,
|
||||||
preference.title?.toString(),
|
preference.title?.toString(),
|
||||||
@@ -422,7 +458,9 @@ fun preferenceOf(preference: SourcePreference): Preference =
|
|||||||
preference.currentValue as Boolean,
|
preference.currentValue as Boolean,
|
||||||
preference.defaultValue as Boolean,
|
preference.defaultValue as Boolean,
|
||||||
)
|
)
|
||||||
is SourceEditTextPreference ->
|
}
|
||||||
|
|
||||||
|
is SourceEditTextPreference -> {
|
||||||
EditTextPreference(
|
EditTextPreference(
|
||||||
preference.key,
|
preference.key,
|
||||||
preference.title?.toString(),
|
preference.title?.toString(),
|
||||||
@@ -435,7 +473,9 @@ fun preferenceOf(preference: SourcePreference): Preference =
|
|||||||
preference.dialogMessage?.toString(),
|
preference.dialogMessage?.toString(),
|
||||||
preference.text,
|
preference.text,
|
||||||
)
|
)
|
||||||
is SourceListPreference ->
|
}
|
||||||
|
|
||||||
|
is SourceListPreference -> {
|
||||||
ListPreference(
|
ListPreference(
|
||||||
preference.key,
|
preference.key,
|
||||||
preference.title?.toString(),
|
preference.title?.toString(),
|
||||||
@@ -447,7 +487,9 @@ fun preferenceOf(preference: SourcePreference): Preference =
|
|||||||
preference.entries.map { it.toString() },
|
preference.entries.map { it.toString() },
|
||||||
preference.entryValues.map { it.toString() },
|
preference.entryValues.map { it.toString() },
|
||||||
)
|
)
|
||||||
is SourceMultiSelectListPreference ->
|
}
|
||||||
|
|
||||||
|
is SourceMultiSelectListPreference -> {
|
||||||
MultiSelectListPreference(
|
MultiSelectListPreference(
|
||||||
preference.key,
|
preference.key,
|
||||||
preference.title?.toString(),
|
preference.title?.toString(),
|
||||||
@@ -461,5 +503,9 @@ fun preferenceOf(preference: SourcePreference): Preference =
|
|||||||
preference.entries.map { it.toString() },
|
preference.entries.map { it.toString() },
|
||||||
preference.entryValues.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!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -513,25 +513,33 @@ object Chapter {
|
|||||||
// Make sure some filter is defined
|
// Make sure some filter is defined
|
||||||
val condition =
|
val condition =
|
||||||
when {
|
when {
|
||||||
mangaId != null ->
|
mangaId != null -> {
|
||||||
// mangaId is not null, scope query under manga
|
// mangaId is not null, scope query under manga
|
||||||
when {
|
when {
|
||||||
input.chapterIds != null ->
|
input.chapterIds != null -> {
|
||||||
Op.build { (ChapterTable.manga eq mangaId) and (ChapterTable.id inList input.chapterIds) }
|
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) }
|
Op.build { (ChapterTable.manga eq mangaId) and (ChapterTable.sourceOrder inList input.chapterIndexes) }
|
||||||
|
}
|
||||||
|
|
||||||
else -> null
|
else -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
// mangaId is null, only chapterIndexes is valid for this case
|
// mangaId is null, only chapterIndexes is valid for this case
|
||||||
when {
|
when {
|
||||||
input.chapterIds != null ->
|
input.chapterIds != null -> {
|
||||||
Op.build { (ChapterTable.id inList input.chapterIds) }
|
Op.build { (ChapterTable.id inList input.chapterIds) }
|
||||||
|
}
|
||||||
|
|
||||||
else -> null
|
else -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} ?: return
|
} ?: return
|
||||||
|
|||||||
@@ -372,10 +372,11 @@ object Manga {
|
|||||||
val sourceId = mangaEntry[MangaTable.sourceReference]
|
val sourceId = mangaEntry[MangaTable.sourceReference]
|
||||||
|
|
||||||
return when (val source = getCatalogueSourceOrStub(sourceId)) {
|
return when (val source = getCatalogueSourceOrStub(sourceId)) {
|
||||||
is HttpSource ->
|
is HttpSource -> {
|
||||||
getImageResponse(cacheSaveDir, fileName) {
|
getImageResponse(cacheSaveDir, fileName) {
|
||||||
fetchHttpSourceMangaThumbnail(source, mangaEntry)
|
fetchHttpSourceMangaThumbnail(source, mangaEntry)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
is LocalSource -> {
|
is LocalSource -> {
|
||||||
val imageFile =
|
val imageFile =
|
||||||
@@ -393,7 +394,7 @@ object Manga {
|
|||||||
imageFile.inputStream() to contentType
|
imageFile.inputStream() to contentType
|
||||||
}
|
}
|
||||||
|
|
||||||
is StubSource ->
|
is StubSource -> {
|
||||||
getImageResponse(cacheSaveDir, fileName) {
|
getImageResponse(cacheSaveDir, fileName) {
|
||||||
val thumbnailUrl =
|
val thumbnailUrl =
|
||||||
mangaEntry[MangaTable.thumbnail_url]
|
mangaEntry[MangaTable.thumbnail_url]
|
||||||
@@ -403,8 +404,11 @@ object Manga {
|
|||||||
GET(thumbnailUrl, cache = CacheControl.FORCE_NETWORK),
|
GET(thumbnailUrl, cache = CacheControl.FORCE_NETWORK),
|
||||||
).await()
|
).await()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> throw IllegalArgumentException("Unknown source")
|
else -> {
|
||||||
|
throw IllegalArgumentException("Unknown source")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,10 @@ object Search {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> it
|
|
||||||
|
else -> {
|
||||||
|
it
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -122,13 +125,27 @@ object Search {
|
|||||||
is Filter.Header -> {
|
is Filter.Header -> {
|
||||||
// NOOP
|
// NOOP
|
||||||
}
|
}
|
||||||
|
|
||||||
is Filter.Separator -> {
|
is Filter.Separator -> {
|
||||||
// NOOP
|
// NOOP
|
||||||
}
|
}
|
||||||
is Filter.Select<*> -> filter.state = change.state.toInt()
|
|
||||||
is Filter.Text -> filter.state = change.state
|
is Filter.Select<*> -> {
|
||||||
is Filter.CheckBox -> filter.state = change.state.toBooleanStrict()
|
filter.state = change.state.toInt()
|
||||||
is Filter.TriState -> 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<*> -> {
|
is Filter.Group<*> -> {
|
||||||
val groupChange = jsonMapper.fromJsonString<FilterChange>(change.state)
|
val groupChange = jsonMapper.fromJsonString<FilterChange>(change.state)
|
||||||
|
|
||||||
@@ -139,6 +156,7 @@ object Search {
|
|||||||
is Filter.Select<*> -> groupFilter.state = groupChange.state.toInt()
|
is Filter.Select<*> -> groupFilter.state = groupChange.state.toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is Filter.Sort -> {
|
is Filter.Sort -> {
|
||||||
filter.state = jsonMapper.fromJsonString(change.state, Filter.Sort.Selection::class.java)
|
filter.state = jsonMapper.fromJsonString(change.state, Filter.Sort.Selection::class.java)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,8 +120,11 @@ object DownloadManager {
|
|||||||
|
|
||||||
fun handleRequest(ctx: WsMessageContext) {
|
fun handleRequest(ctx: WsMessageContext) {
|
||||||
when (ctx.message()) {
|
when (ctx.message()) {
|
||||||
"STATUS" -> notifyClient(ctx)
|
"STATUS" -> {
|
||||||
else ->
|
notifyClient(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
ctx.send(
|
ctx.send(
|
||||||
"""
|
"""
|
||||||
|Invalid command.
|
|Invalid command.
|
||||||
@@ -131,6 +134,7 @@ object DownloadManager {
|
|||||||
|
|
|
|
||||||
""".trimMargin(),
|
""".trimMargin(),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ sealed class FileType {
|
|||||||
is RegularFile -> {
|
is RegularFile -> {
|
||||||
this.file.name
|
this.file.name
|
||||||
}
|
}
|
||||||
|
|
||||||
is ZipFile -> {
|
is ZipFile -> {
|
||||||
this.entry.name
|
this.entry.name
|
||||||
}
|
}
|
||||||
@@ -59,6 +60,7 @@ sealed class FileType {
|
|||||||
is RegularFile -> {
|
is RegularFile -> {
|
||||||
this.file.extension
|
this.file.extension
|
||||||
}
|
}
|
||||||
|
|
||||||
is ZipFile -> {
|
is ZipFile -> {
|
||||||
this.entry.name.substringAfterLast(".")
|
this.entry.name.substringAfterLast(".")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ object ExtensionsList {
|
|||||||
this[ExtensionTable.hasUpdate] = true
|
this[ExtensionTable.hasUpdate] = true
|
||||||
updateMap.putIfAbsent(foundExtension.pkgName, foundExtension)
|
updateMap.putIfAbsent(foundExtension.pkgName, foundExtension)
|
||||||
}
|
}
|
||||||
|
|
||||||
foundExtension.versionCode < extensionRecord[ExtensionTable.versionCode] -> {
|
foundExtension.versionCode < extensionRecord[ExtensionTable.versionCode] -> {
|
||||||
// somehow the user installed an invalid version
|
// somehow the user installed an invalid version
|
||||||
this[ExtensionTable.isObsolete] = true
|
this[ExtensionTable.isObsolete] = true
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ object KoreaderSyncService {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KoreaderSyncChecksumMethod.FILENAME -> {
|
KoreaderSyncChecksumMethod.FILENAME -> {
|
||||||
logger.debug { "[KOSYNC HASH] No hash for chapterId=$chapterId. Generating from filename." }
|
logger.debug { "[KOSYNC HASH] No hash for chapterId=$chapterId. Generating from filename." }
|
||||||
(ChapterTable innerJoin MangaTable)
|
(ChapterTable innerJoin MangaTable)
|
||||||
|
|||||||
@@ -69,56 +69,82 @@ class Anilist(
|
|||||||
when (trackPreferences.getScoreType(this)) {
|
when (trackPreferences.getScoreType(this)) {
|
||||||
// 10 point
|
// 10 point
|
||||||
POINT_10 -> IntRange(0, 10).map(Int::toString)
|
POINT_10 -> IntRange(0, 10).map(Int::toString)
|
||||||
|
|
||||||
// 100 point
|
// 100 point
|
||||||
POINT_100 -> IntRange(0, 100).map(Int::toString)
|
POINT_100 -> IntRange(0, 100).map(Int::toString)
|
||||||
|
|
||||||
// 5 stars
|
// 5 stars
|
||||||
POINT_5 -> IntRange(0, 5).map { "$it ★" }
|
POINT_5 -> IntRange(0, 5).map { "$it ★" }
|
||||||
|
|
||||||
// Smiley
|
// Smiley
|
||||||
POINT_3 -> listOf("-", "😦", "😐", "😊")
|
POINT_3 -> listOf("-", "😦", "😐", "😊")
|
||||||
|
|
||||||
// 10 point decimal
|
// 10 point decimal
|
||||||
POINT_10_DECIMAL -> IntRange(0, 100).map { (it / 10f).toString() }
|
POINT_10_DECIMAL -> IntRange(0, 100).map { (it / 10f).toString() }
|
||||||
|
|
||||||
else -> throw Exception("Unknown score type")
|
else -> throw Exception("Unknown score type")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun indexToScore(index: Int): Double =
|
override fun indexToScore(index: Int): Double =
|
||||||
when (trackPreferences.getScoreType(this)) {
|
when (trackPreferences.getScoreType(this)) {
|
||||||
// 10 point
|
// 10 point
|
||||||
POINT_10 -> index * 10.0
|
POINT_10 -> {
|
||||||
|
index * 10.0
|
||||||
|
}
|
||||||
|
|
||||||
// 100 point
|
// 100 point
|
||||||
POINT_100 -> index.toDouble()
|
POINT_100 -> {
|
||||||
|
index.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
// 5 stars
|
// 5 stars
|
||||||
POINT_5 ->
|
POINT_5 -> {
|
||||||
when (index) {
|
when (index) {
|
||||||
0 -> 0.0
|
0 -> 0.0
|
||||||
else -> index * 20.0 - 10.0
|
else -> index * 20.0 - 10.0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Smiley
|
// Smiley
|
||||||
POINT_3 ->
|
POINT_3 -> {
|
||||||
when (index) {
|
when (index) {
|
||||||
0 -> 0.0
|
0 -> 0.0
|
||||||
else -> index * 25.0 + 10.0
|
else -> index * 25.0 + 10.0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 10 point decimal
|
// 10 point decimal
|
||||||
POINT_10_DECIMAL -> index.toDouble()
|
POINT_10_DECIMAL -> {
|
||||||
else -> throw Exception("Unknown score type")
|
index.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
throw Exception("Unknown score type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun displayScore(track: Track): String {
|
override fun displayScore(track: Track): String {
|
||||||
val score = track.score
|
val score = track.score
|
||||||
return when (val type = trackPreferences.getScoreType(this)) {
|
return when (val type = trackPreferences.getScoreType(this)) {
|
||||||
POINT_5 ->
|
POINT_5 -> {
|
||||||
when (score) {
|
when (score) {
|
||||||
0.0 -> "0 ★"
|
0.0 -> "0 ★"
|
||||||
else -> "${((score + 10) / 20).toInt()} ★"
|
else -> "${((score + 10) / 20).toInt()} ★"
|
||||||
}
|
}
|
||||||
POINT_3 ->
|
}
|
||||||
|
|
||||||
|
POINT_3 -> {
|
||||||
when {
|
when {
|
||||||
score == 0.0 -> "0"
|
score == 0.0 -> "0"
|
||||||
score <= 35 -> "😦"
|
score <= 35 -> "😦"
|
||||||
score <= 60 -> "😐"
|
score <= 60 -> "😐"
|
||||||
else -> "😊"
|
else -> "😊"
|
||||||
}
|
}
|
||||||
else -> track.toApiScore(type)
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
track.toApiScore(type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,17 @@ fun Track.toApiStatus() =
|
|||||||
fun Track.toApiScore(scoreType: String?): String =
|
fun Track.toApiScore(scoreType: String?): String =
|
||||||
when (scoreType) {
|
when (scoreType) {
|
||||||
// 10 point
|
// 10 point
|
||||||
"POINT_10" -> (score.toInt() / 10).toString()
|
"POINT_10" -> {
|
||||||
|
(score.toInt() / 10).toString()
|
||||||
|
}
|
||||||
|
|
||||||
// 100 point
|
// 100 point
|
||||||
"POINT_100" -> score.toInt().toString()
|
"POINT_100" -> {
|
||||||
|
score.toInt().toString()
|
||||||
|
}
|
||||||
|
|
||||||
// 5 stars
|
// 5 stars
|
||||||
"POINT_5" ->
|
"POINT_5" -> {
|
||||||
when {
|
when {
|
||||||
score == 0.0 -> "0"
|
score == 0.0 -> "0"
|
||||||
score < 30 -> "1"
|
score < 30 -> "1"
|
||||||
@@ -29,15 +35,24 @@ fun Track.toApiScore(scoreType: String?): String =
|
|||||||
score < 90 -> "4"
|
score < 90 -> "4"
|
||||||
else -> "5"
|
else -> "5"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Smiley
|
// Smiley
|
||||||
"POINT_3" ->
|
"POINT_3" -> {
|
||||||
when {
|
when {
|
||||||
score == 0.0 -> "0"
|
score == 0.0 -> "0"
|
||||||
score <= 35 -> ":("
|
score <= 35 -> ":("
|
||||||
score <= 60 -> ":|"
|
score <= 60 -> ":|"
|
||||||
else -> ":)"
|
else -> ":)"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 10 point decimal
|
// 10 point decimal
|
||||||
"POINT_10_DECIMAL" -> (score / 10).toString()
|
"POINT_10_DECIMAL" -> {
|
||||||
else -> throw NotImplementedError("Unknown score type")
|
(score / 10).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
throw NotImplementedError("Unknown score type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,12 +24,19 @@ class MangaUpdates(
|
|||||||
(0..10)
|
(0..10)
|
||||||
.flatMap { decimal ->
|
.flatMap { decimal ->
|
||||||
when (decimal) {
|
when (decimal) {
|
||||||
0 -> listOf("-")
|
0 -> {
|
||||||
10 -> listOf("10.0")
|
listOf("-")
|
||||||
else ->
|
}
|
||||||
|
|
||||||
|
10 -> {
|
||||||
|
listOf("10.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
(0..9).map { fraction ->
|
(0..9).map { fraction ->
|
||||||
"$decimal.$fraction"
|
"$decimal.$fraction"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,11 @@ object UpdaterSocket : Websocket<UpdateStatus>() {
|
|||||||
|
|
||||||
override fun handleRequest(ctx: WsMessageContext) {
|
override fun handleRequest(ctx: WsMessageContext) {
|
||||||
when (ctx.message()) {
|
when (ctx.message()) {
|
||||||
"STATUS" -> notifyClient(ctx, updater.statusDeprecated.value)
|
"STATUS" -> {
|
||||||
else ->
|
notifyClient(ctx, updater.statusDeprecated.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
ctx.send(
|
ctx.send(
|
||||||
"""
|
"""
|
||||||
|Invalid command.
|
|Invalid command.
|
||||||
@@ -37,6 +40,7 @@ object UpdaterSocket : Websocket<UpdateStatus>() {
|
|||||||
|
|
|
|
||||||
""".trimMargin(),
|
""".trimMargin(),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,10 @@ private fun getChapterDir(
|
|||||||
chapterEntry[ChapterTable.scanlator] != null -> {
|
chapterEntry[ChapterTable.scanlator] != null -> {
|
||||||
"${chapterEntry[ChapterTable.scanlator]}_${chapterEntry[ChapterTable.name]}"
|
"${chapterEntry[ChapterTable.scanlator]}_${chapterEntry[ChapterTable.name]}"
|
||||||
}
|
}
|
||||||
else -> chapterEntry[ChapterTable.name]
|
|
||||||
|
else -> {
|
||||||
|
chapterEntry[ChapterTable.name]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -319,6 +319,7 @@ object OpdsEntryBuilder {
|
|||||||
}
|
}
|
||||||
titlePrefix = statusKey.localized(locale)
|
titlePrefix = statusKey.localized(locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ProgressSource.Remote -> {
|
is ProgressSource.Remote -> {
|
||||||
idSuffix = ":remote"
|
idSuffix = ":remote"
|
||||||
titlePrefix = MR.strings.opds_chapter_status_synced.localized(locale, progressSource.device)
|
titlePrefix = MR.strings.opds_chapter_status_synced.localized(locale, progressSource.device)
|
||||||
@@ -378,6 +379,7 @@ object OpdsEntryBuilder {
|
|||||||
}
|
}
|
||||||
titleRes.localized(locale)
|
titleRes.localized(locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
progressSource is ProgressSource.Local -> {
|
progressSource is ProgressSource.Local -> {
|
||||||
val titleRes =
|
val titleRes =
|
||||||
if (progressSource.lastPageRead > 0) {
|
if (progressSource.lastPageRead > 0) {
|
||||||
@@ -387,6 +389,7 @@ object OpdsEntryBuilder {
|
|||||||
}
|
}
|
||||||
titleRes.localized(locale)
|
titleRes.localized(locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
progressSource is ProgressSource.Remote -> {
|
progressSource is ProgressSource.Remote -> {
|
||||||
val titleRes =
|
val titleRes =
|
||||||
if (progressSource.lastPageRead > 0) {
|
if (progressSource.lastPageRead > 0) {
|
||||||
@@ -396,7 +399,11 @@ object OpdsEntryBuilder {
|
|||||||
}
|
}
|
||||||
titleRes.localized(locale, progressSource.device)
|
titleRes.localized(locale, progressSource.device)
|
||||||
}
|
}
|
||||||
else -> "" // Should not happen
|
|
||||||
|
else -> {
|
||||||
|
// Should not happen
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
links.add(
|
links.add(
|
||||||
|
|||||||
@@ -155,30 +155,40 @@ object OpdsFeedBuilder {
|
|||||||
|
|
||||||
val feedTitle =
|
val feedTitle =
|
||||||
when (criteria.primaryFilter) {
|
when (criteria.primaryFilter) {
|
||||||
PrimaryFilterType.SOURCE ->
|
PrimaryFilterType.SOURCE -> {
|
||||||
MR.strings.opds_feeds_library_source_specific_title.localized(
|
MR.strings.opds_feeds_library_source_specific_title.localized(
|
||||||
locale,
|
locale,
|
||||||
result.feedTitleComponent ?: criteria.sourceId.toString(),
|
result.feedTitleComponent ?: criteria.sourceId.toString(),
|
||||||
)
|
)
|
||||||
PrimaryFilterType.CATEGORY ->
|
}
|
||||||
|
|
||||||
|
PrimaryFilterType.CATEGORY -> {
|
||||||
MR.strings.opds_feeds_category_specific_title.localized(
|
MR.strings.opds_feeds_category_specific_title.localized(
|
||||||
locale,
|
locale,
|
||||||
result.feedTitleComponent ?: criteria.categoryId.toString(),
|
result.feedTitleComponent ?: criteria.categoryId.toString(),
|
||||||
)
|
)
|
||||||
PrimaryFilterType.GENRE ->
|
}
|
||||||
|
|
||||||
|
PrimaryFilterType.GENRE -> {
|
||||||
MR.strings.opds_feeds_genre_specific_title.localized(
|
MR.strings.opds_feeds_genre_specific_title.localized(
|
||||||
locale,
|
locale,
|
||||||
result.feedTitleComponent ?: "Unknown",
|
result.feedTitleComponent ?: "Unknown",
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
PrimaryFilterType.STATUS -> {
|
PrimaryFilterType.STATUS -> {
|
||||||
val statusName = NavigationRepository.getStatuses(locale).find { it.id == criteria.statusId }?.title
|
val statusName = NavigationRepository.getStatuses(locale).find { it.id == criteria.statusId }?.title
|
||||||
MR.strings.opds_feeds_status_specific_title.localized(locale, statusName ?: criteria.statusId.toString())
|
MR.strings.opds_feeds_status_specific_title.localized(locale, statusName ?: criteria.statusId.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimaryFilterType.LANGUAGE -> {
|
PrimaryFilterType.LANGUAGE -> {
|
||||||
val langName = Locale.forLanguageTag(criteria.langCode ?: "").getDisplayName(locale)
|
val langName = Locale.forLanguageTag(criteria.langCode ?: "").getDisplayName(locale)
|
||||||
MR.strings.opds_feeds_language_specific_title.localized(locale, langName)
|
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 =
|
val feedUrl =
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ object MangaRepository {
|
|||||||
// Efficiently get the name of the primary filter item
|
// Efficiently get the name of the primary filter item
|
||||||
val specificFilterName =
|
val specificFilterName =
|
||||||
when (criteria.primaryFilter) {
|
when (criteria.primaryFilter) {
|
||||||
PrimaryFilterType.SOURCE ->
|
PrimaryFilterType.SOURCE -> {
|
||||||
criteria.sourceId?.let {
|
criteria.sourceId?.let {
|
||||||
SourceTable
|
SourceTable
|
||||||
.select(SourceTable.name)
|
.select(SourceTable.name)
|
||||||
@@ -116,7 +116,9 @@ object MangaRepository {
|
|||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
?.get(SourceTable.name)
|
?.get(SourceTable.name)
|
||||||
}
|
}
|
||||||
PrimaryFilterType.CATEGORY ->
|
}
|
||||||
|
|
||||||
|
PrimaryFilterType.CATEGORY -> {
|
||||||
criteria.categoryId?.let {
|
criteria.categoryId?.let {
|
||||||
CategoryTable
|
CategoryTable
|
||||||
.select(CategoryTable.name)
|
.select(CategoryTable.name)
|
||||||
@@ -124,10 +126,25 @@ object MangaRepository {
|
|||||||
.firstOrNull()
|
.firstOrNull()
|
||||||
?.get(CategoryTable.name)
|
?.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
|
PrimaryFilterType.GENRE -> {
|
||||||
else -> null
|
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)
|
applyMangaLibrarySortAndFilter(query, sort, filter)
|
||||||
|
|||||||
@@ -44,10 +44,18 @@ object OpdsStringUtil {
|
|||||||
if (serverConfig.opdsUseBinaryFileSizes.value) {
|
if (serverConfig.opdsUseBinaryFileSizes.value) {
|
||||||
// Binary notation (base 1024)
|
// Binary notation (base 1024)
|
||||||
when {
|
when {
|
||||||
size >= 1_125_899_906_842_624 -> "%.2f TiB".format(size / 1_125_899_906_842_624.0) // 1024^4
|
// 1024^4
|
||||||
size >= 1_073_741_824 -> "%.2f GiB".format(size / 1_073_741_824.0) // 1024^3
|
size >= 1_125_899_906_842_624 -> "%.2f TiB".format(size / 1_125_899_906_842_624.0)
|
||||||
size >= 1_048_576 -> "%.2f MiB".format(size / 1_048_576.0) // 1024^2
|
|
||||||
size >= 1024 -> "%.2f KiB".format(size / 1024.0) // 1024
|
// 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 -> "$size bytes"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ object DBManager {
|
|||||||
addDataSourceProperty("cachePrepStmts", "true")
|
addDataSourceProperty("cachePrepStmts", "true")
|
||||||
addDataSourceProperty("useServerPrepStmts", "true")
|
addDataSourceProperty("useServerPrepStmts", "true")
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseType.H2 -> {
|
DatabaseType.H2 -> {
|
||||||
jdbcUrl = "jdbc:h2:${applicationDirs.dataRoot}/database"
|
jdbcUrl = "jdbc:h2:${applicationDirs.dataRoot}/database"
|
||||||
driverClassName = "org.h2.Driver"
|
driverClassName = "org.h2.Driver"
|
||||||
@@ -103,7 +104,7 @@ object DBManager {
|
|||||||
.connect(hikariDataSource!!, databaseConfig = dbConfig)
|
.connect(hikariDataSource!!, databaseConfig = dbConfig)
|
||||||
} else {
|
} else {
|
||||||
when (serverConfig.databaseType.value) {
|
when (serverConfig.databaseType.value) {
|
||||||
DatabaseType.POSTGRESQL ->
|
DatabaseType.POSTGRESQL -> {
|
||||||
Database.connect(
|
Database.connect(
|
||||||
"jdbc:${serverConfig.databaseUrl.value}",
|
"jdbc:${serverConfig.databaseUrl.value}",
|
||||||
"org.postgresql.Driver",
|
"org.postgresql.Driver",
|
||||||
@@ -111,12 +112,15 @@ object DBManager {
|
|||||||
password = serverConfig.databasePassword.value,
|
password = serverConfig.databasePassword.value,
|
||||||
databaseConfig = dbConfig,
|
databaseConfig = dbConfig,
|
||||||
)
|
)
|
||||||
DatabaseType.H2 ->
|
}
|
||||||
|
|
||||||
|
DatabaseType.H2 -> {
|
||||||
Database.connect(
|
Database.connect(
|
||||||
"jdbc:h2:${Injekt.get<ApplicationDirs>().dataRoot}/database",
|
"jdbc:h2:${Injekt.get<ApplicationDirs>().dataRoot}/database",
|
||||||
"org.h2.Driver",
|
"org.h2.Driver",
|
||||||
databaseConfig = dbConfig,
|
databaseConfig = dbConfig,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.also { db = it }
|
}.also { db = it }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import suwayomi.tachidesk.server.serverConfig
|
|||||||
val UNLIMITED_TEXT
|
val UNLIMITED_TEXT
|
||||||
get() =
|
get() =
|
||||||
when (serverConfig.databaseType.value) {
|
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"
|
DatabaseType.POSTGRESQL -> "TEXT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,14 @@ fun UserType.requireUser(): Int =
|
|||||||
|
|
||||||
fun UserType.requireUserWithBasicFallback(ctx: Context): Int =
|
fun UserType.requireUserWithBasicFallback(ctx: Context): Int =
|
||||||
when (this) {
|
when (this) {
|
||||||
is UserType.Admin -> id
|
is UserType.Admin -> {
|
||||||
UserType.Visitor if ctx.getAttribute(Attribute.TachideskBasic) -> 1
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
UserType.Visitor if ctx.getAttribute(Attribute.TachideskBasic) -> {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
UserType.Visitor -> {
|
UserType.Visitor -> {
|
||||||
ctx.header("WWW-Authenticate", "Basic")
|
ctx.header("WWW-Authenticate", "Basic")
|
||||||
throw UnauthorizedException()
|
throw UnauthorizedException()
|
||||||
@@ -53,8 +59,14 @@ fun getUserFromContext(ctx: Context): UserType {
|
|||||||
|
|
||||||
return when (serverConfig.authMode.value) {
|
return when (serverConfig.authMode.value) {
|
||||||
// NOTE: Basic Auth is expected to have been validated by JavalinSetup
|
// NOTE: Basic Auth is expected to have been validated by JavalinSetup
|
||||||
AuthMode.NONE, AuthMode.BASIC_AUTH -> UserType.Admin(1)
|
AuthMode.NONE, AuthMode.BASIC_AUTH -> {
|
||||||
AuthMode.SIMPLE_LOGIN -> if (cookieValid()) UserType.Admin(1) else UserType.Visitor
|
UserType.Admin(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthMode.SIMPLE_LOGIN -> {
|
||||||
|
if (cookieValid()) UserType.Admin(1) else UserType.Visitor
|
||||||
|
}
|
||||||
|
|
||||||
AuthMode.UI_LOGIN -> {
|
AuthMode.UI_LOGIN -> {
|
||||||
val authentication = ctx.header(Header.AUTHORIZATION) ?: ctx.cookie("suwayomi-server-token")
|
val authentication = ctx.header(Header.AUTHORIZATION) ?: ctx.cookie("suwayomi-server-token")
|
||||||
val token = authentication?.substringAfter("Bearer ") ?: ctx.queryParam("token")
|
val token = authentication?.substringAfter("Bearer ") ?: ctx.queryParam("token")
|
||||||
@@ -72,8 +84,14 @@ fun getUserFromWsContext(ctx: WsConnectContext): UserType {
|
|||||||
|
|
||||||
return when (serverConfig.authMode.value) {
|
return when (serverConfig.authMode.value) {
|
||||||
// NOTE: Basic Auth is expected to have been validated by JavalinSetup
|
// NOTE: Basic Auth is expected to have been validated by JavalinSetup
|
||||||
AuthMode.NONE, AuthMode.BASIC_AUTH -> UserType.Admin(1)
|
AuthMode.NONE, AuthMode.BASIC_AUTH -> {
|
||||||
AuthMode.SIMPLE_LOGIN -> if (cookieValid()) UserType.Admin(1) else UserType.Visitor
|
UserType.Admin(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthMode.SIMPLE_LOGIN -> {
|
||||||
|
if (cookieValid()) UserType.Admin(1) else UserType.Visitor
|
||||||
|
}
|
||||||
|
|
||||||
AuthMode.UI_LOGIN -> {
|
AuthMode.UI_LOGIN -> {
|
||||||
val authentication =
|
val authentication =
|
||||||
ctx.header(Header.AUTHORIZATION) ?: ctx.header("Sec-WebSocket-Protocol") ?: ctx.cookie("suwayomi-server-token")
|
ctx.header(Header.AUTHORIZATION) ?: ctx.header("Sec-WebSocket-Protocol") ?: ctx.cookie("suwayomi-server-token")
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ object AppMutex {
|
|||||||
AppMutexState.Clear -> {
|
AppMutexState.Clear -> {
|
||||||
logger.info { "Mutex status is clear, Resuming startup." }
|
logger.info { "Mutex status is clear, Resuming startup." }
|
||||||
}
|
}
|
||||||
|
|
||||||
AppMutexState.TachideskInstanceRunning -> {
|
AppMutexState.TachideskInstanceRunning -> {
|
||||||
logger.info { "Another instance of Suwayomi-Server is running on $appIP:${serverConfig.port.value}" }
|
logger.info { "Another instance of Suwayomi-Server is running on $appIP:${serverConfig.port.value}" }
|
||||||
|
|
||||||
@@ -82,6 +83,7 @@ object AppMutex {
|
|||||||
|
|
||||||
shutdownApp(MutexCheckFailedTachideskRunning)
|
shutdownApp(MutexCheckFailedTachideskRunning)
|
||||||
}
|
}
|
||||||
|
|
||||||
AppMutexState.OtherApplicationRunning -> {
|
AppMutexState.OtherApplicationRunning -> {
|
||||||
logger.error { "A non Suwayomi-Server application is running on $appIP:${serverConfig.port.value}, aborting startup." }
|
logger.error { "A non Suwayomi-Server application is running on $appIP:${serverConfig.port.value}, aborting startup." }
|
||||||
shutdownApp(MutexCheckFailedAnotherAppRunning)
|
shutdownApp(MutexCheckFailedAnotherAppRunning)
|
||||||
|
|||||||
@@ -42,12 +42,30 @@ fun <T> getParam(
|
|||||||
}
|
}
|
||||||
val typedItem: Any? =
|
val typedItem: Any? =
|
||||||
when (val clazz = param.clazz as Class<T>) {
|
when (val clazz = param.clazz as Class<T>) {
|
||||||
String::class.java, java.lang.String::class.java -> getSimpleParamItem(ctx, param) ?: param.defaultValue
|
String::class.java, java.lang.String::class.java -> {
|
||||||
Int::class.java, java.lang.Integer::class.java -> getSimpleParamItem(ctx, param)?.toIntOrNull() ?: param.defaultValue
|
getSimpleParamItem(ctx, param) ?: 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
|
Int::class.java, java.lang.Integer::class.java -> {
|
||||||
Double::class.java, java.lang.Double::class.java -> getSimpleParamItem(ctx, param)?.toDoubleOrNull() ?: param.defaultValue
|
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 -> {
|
else -> {
|
||||||
when (param) {
|
when (param) {
|
||||||
is Param.FormParam -> ctx.formParamAsClass(param.key, clazz)
|
is Param.FormParam -> ctx.formParamAsClass(param.key, clazz)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import android.content.Context
|
|||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||||
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import io.github.oshai.kotlinlogging.KLogger
|
import io.github.oshai.kotlinlogging.KLogger
|
||||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||||
import io.github.reactivecircus.cache4k.Cache
|
import io.github.reactivecircus.cache4k.Cache
|
||||||
@@ -193,7 +194,7 @@ object WebInterfaceManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launchIO {
|
||||||
setupWebUI()
|
setupWebUI()
|
||||||
isSetupComplete = true
|
isSetupComplete = true
|
||||||
}
|
}
|
||||||
@@ -400,7 +401,7 @@ object WebInterfaceManager {
|
|||||||
try {
|
try {
|
||||||
downloadVersion(flavor, getVersion())
|
downloadVersion(flavor, getVersion())
|
||||||
true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
false
|
false
|
||||||
} ||
|
} ||
|
||||||
isLocalWebUIValid
|
isLocalWebUIValid
|
||||||
@@ -427,7 +428,7 @@ object WebInterfaceManager {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setupBundledWebUI()
|
setupBundledWebUI()
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
throw Exception("Unable to setup a webUI")
|
throw Exception("Unable to setup a webUI")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -498,7 +499,7 @@ object WebInterfaceManager {
|
|||||||
private fun getLocalVersion(path: String = applicationDirs.webUIRoot): String =
|
private fun getLocalVersion(path: String = applicationDirs.webUIRoot): String =
|
||||||
try {
|
try {
|
||||||
File("$path/revision").readText().trim()
|
File("$path/revision").readText().trim()
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
"r-1"
|
"r-1"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,7 +586,7 @@ object WebInterfaceManager {
|
|||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
})
|
})
|
||||||
} catch (e: Exception) {
|
} catch (_: Exception) {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,7 +713,7 @@ object WebInterfaceManager {
|
|||||||
version: String,
|
version: String,
|
||||||
) {
|
) {
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launchIO {
|
||||||
downloadVersion(flavor, version)
|
downloadVersion(flavor, version)
|
||||||
serveWebUI()
|
serveWebUI()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,9 +61,11 @@ class TestExtensionCompatibility {
|
|||||||
it.obsolete -> {
|
it.obsolete -> {
|
||||||
uninstallExtension(it.pkgName)
|
uninstallExtension(it.pkgName)
|
||||||
}
|
}
|
||||||
|
|
||||||
it.hasUpdate -> {
|
it.hasUpdate -> {
|
||||||
updateExtension(it.pkgName)
|
updateExtension(it.pkgName)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
uninstallExtension(it.pkgName)
|
uninstallExtension(it.pkgName)
|
||||||
installExtension(it.pkgName)
|
installExtension(it.pkgName)
|
||||||
|
|||||||
Reference in New Issue
Block a user