Fix formatting in a bunch of files

This commit is contained in:
Syer10
2025-10-03 21:20:55 -04:00
parent a7cc5e664b
commit bdd4ef20cb
92 changed files with 4647 additions and 4709 deletions

View File

@@ -23,103 +23,102 @@ import kotlinx.coroutines.launch
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class ServerListeners
@Inject
constructor() {
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
@Inject
class ServerListeners {
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private fun <T> Flow<T>.startWith(value: T) = onStart { emit(value) }
private fun <T> Flow<T>.startWith(value: T) = onStart { emit(value) }
private val _mangaListener = MutableSharedFlow<List<Long>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val mangaListener = _mangaListener.asSharedFlow()
private val _mangaListener = MutableSharedFlow<List<Long>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val mangaListener = _mangaListener.asSharedFlow()
private val _chapterIdsListener = MutableSharedFlow<List<Long>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val chapterIdsListener = _chapterIdsListener.asSharedFlow()
private val _chapterIdsListener = MutableSharedFlow<List<Long>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val chapterIdsListener = _chapterIdsListener.asSharedFlow()
private val _mangaChapterIdsListener = MutableSharedFlow<List<Long>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val mangaChapterIdsListener = _mangaChapterIdsListener.asSharedFlow()
private val _mangaChapterIdsListener = MutableSharedFlow<List<Long>>(
extraBufferCapacity = Channel.UNLIMITED,
)
val mangaChapterIdsListener = _mangaChapterIdsListener.asSharedFlow()
private val categoryMangaListener = MutableSharedFlow<Long>(
extraBufferCapacity = Channel.UNLIMITED,
)
private val categoryMangaListener = MutableSharedFlow<Long>(
extraBufferCapacity = Channel.UNLIMITED,
)
private val extensionListener = MutableSharedFlow<List<String>>(
extraBufferCapacity = Channel.UNLIMITED,
)
private val extensionListener = MutableSharedFlow<List<String>>(
extraBufferCapacity = Channel.UNLIMITED,
)
fun <T> combineMangaUpdates(
flow: Flow<T>,
predate: (suspend (List<Long>) -> Boolean)? = null,
) = if (predate != null) {
_mangaListener
.filter(predate)
.startWith(Unit)
} else {
_mangaListener.startWith(Unit)
}
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.flatMapLatest { flow }
fun <T> combineMangaUpdates(
flow: Flow<T>,
predate: (suspend (List<Long>) -> Boolean)? = null,
) = if (predate != null) {
_mangaListener
.filter(predate)
.startWith(Unit)
} else {
_mangaListener.startWith(Unit)
}
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.flatMapLatest { flow }
fun updateManga(vararg ids: Long) {
scope.launch {
_mangaListener.emit(ids.toList())
}
}
fun <T> combineCategoryManga(
flow: Flow<T>,
predate: (suspend (Long) -> Boolean)? = null,
) = if (predate != null) {
categoryMangaListener.filter(predate).startWith(-1)
} else {
categoryMangaListener.startWith(-1)
}
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.flatMapLatest { flow }
fun updateCategoryManga(id: Long) {
scope.launch {
categoryMangaListener.emit(id)
}
}
fun <T> combineChapters(
flow: Flow<T>,
chapterIdPredate: (suspend (List<Long>) -> Boolean)? = null,
mangaIdPredate: (suspend (List<Long>) -> Boolean)? = null,
): Flow<T> {
val idsListener = _chapterIdsListener
.filter { chapterIdPredate?.invoke(it) ?: false }
.startWith(Unit)
.combine(
_mangaChapterIdsListener.filter { mangaIdPredate?.invoke(it) ?: false }
.startWith(Unit),
) { _, _ -> }
return idsListener
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.flatMapLatest { flow }
}
fun updateChapters(chapterIds: List<Long>) {
scope.launch {
_chapterIdsListener.emit(chapterIds)
}
}
fun updateChapters(vararg chapterIds: Long) {
scope.launch {
_chapterIdsListener.emit(chapterIds.toList())
}
}
companion object {
private val log = logging()
fun updateManga(vararg ids: Long) {
scope.launch {
_mangaListener.emit(ids.toList())
}
}
fun <T> combineCategoryManga(
flow: Flow<T>,
predate: (suspend (Long) -> Boolean)? = null,
) = if (predate != null) {
categoryMangaListener.filter(predate).startWith(-1)
} else {
categoryMangaListener.startWith(-1)
}
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.flatMapLatest { flow }
fun updateCategoryManga(id: Long) {
scope.launch {
categoryMangaListener.emit(id)
}
}
fun <T> combineChapters(
flow: Flow<T>,
chapterIdPredate: (suspend (List<Long>) -> Boolean)? = null,
mangaIdPredate: (suspend (List<Long>) -> Boolean)? = null,
): Flow<T> {
val idsListener = _chapterIdsListener
.filter { chapterIdPredate?.invoke(it) ?: false }
.startWith(Unit)
.combine(
_mangaChapterIdsListener.filter { mangaIdPredate?.invoke(it) ?: false }
.startWith(Unit),
) { _, _ -> }
return idsListener
.buffer(capacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.flatMapLatest { flow }
}
fun updateChapters(chapterIds: List<Long>) {
scope.launch {
_chapterIdsListener.emit(chapterIds)
}
}
fun updateChapters(vararg chapterIds: Long) {
scope.launch {
_chapterIdsListener.emit(chapterIds.toList())
}
}
companion object {
private val log = logging()
}
}

View File

@@ -13,30 +13,29 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class ExportBackupFile
@Inject
constructor(
private val backupRepository: BackupRepository,
) {
suspend fun await(
includeCategories: Boolean,
includeChapters: Boolean,
block: HttpRequestBuilder.() -> Unit = {},
onError: suspend (Throwable) -> Unit = {},
) = asFlow(includeCategories, includeChapters, block)
.catch {
onError(it)
log.warn(it) { "Failed to export backup" }
}
.singleOrNull()
fun asFlow(
includeCategories: Boolean,
includeChapters: Boolean,
block: HttpRequestBuilder.() -> Unit = {},
) = backupRepository.createBackup(includeCategories, includeChapters, block)
companion object {
private val log = logging()
@Inject
class ExportBackupFile(
private val backupRepository: BackupRepository,
) {
suspend fun await(
includeCategories: Boolean,
includeChapters: Boolean,
block: HttpRequestBuilder.() -> Unit = {},
onError: suspend (Throwable) -> Unit = {},
) = asFlow(includeCategories, includeChapters, block)
.catch {
onError(it)
log.warn(it) { "Failed to export backup" }
}
.singleOrNull()
fun asFlow(
includeCategories: Boolean,
includeChapters: Boolean,
block: HttpRequestBuilder.() -> Unit = {},
) = backupRepository.createBackup(includeCategories, includeChapters, block)
companion object {
private val log = logging()
}
}

View File

@@ -15,24 +15,23 @@ import okio.Path
import okio.SYSTEM
import org.lighthousegames.logging.logging
class ImportBackupFile
@Inject
constructor(
private val backupRepository: BackupRepository,
) {
suspend fun await(
file: Path,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(file)
.catch {
onError(it)
log.warn(it) { "Failed to import backup ${file.name}" }
}
.singleOrNull()
fun asFlow(file: Path) = backupRepository.restoreBackup(FileSystem.SYSTEM.source(file))
companion object {
private val log = logging()
@Inject
class ImportBackupFile(
private val backupRepository: BackupRepository,
) {
suspend fun await(
file: Path,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(file)
.catch {
onError(it)
log.warn(it) { "Failed to import backup ${file.name}" }
}
.singleOrNull()
fun asFlow(file: Path) = backupRepository.restoreBackup(FileSystem.SYSTEM.source(file))
companion object {
private val log = logging()
}
}

View File

@@ -15,24 +15,23 @@ import okio.Path
import okio.SYSTEM
import org.lighthousegames.logging.logging
class ValidateBackupFile
@Inject
constructor(
private val backupRepository: BackupRepository,
) {
suspend fun await(
file: Path,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(file)
.catch {
onError(it)
log.warn(it) { "Failed to validate backup ${file.name}" }
}
.singleOrNull()
fun asFlow(file: Path) = backupRepository.validateBackup(FileSystem.SYSTEM.source(file))
companion object {
private val log = logging()
@Inject
class ValidateBackupFile(
private val backupRepository: BackupRepository,
) {
suspend fun await(
file: Path,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(file)
.catch {
onError(it)
log.warn(it) { "Failed to validate backup ${file.name}" }
}
.singleOrNull()
fun asFlow(file: Path) = backupRepository.validateBackup(FileSystem.SYSTEM.source(file))
companion object {
private val log = logging()
}
}

View File

@@ -17,61 +17,60 @@ import kotlinx.coroutines.flow.map
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class AddMangaToCategory
@Inject
constructor(
private val categoryRepository: CategoryRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, categoryId)
.catch {
onError(it)
log.warn(it) { "Failed to add $mangaId to category $categoryId" }
}
.collect()
suspend fun await(
manga: Manga,
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, category)
.catch {
onError(it)
log.warn(it) { "Failed to add ${manga.title}(${manga.id}) to category ${category.name}" }
}
.collect()
fun asFlow(
mangaId: Long,
categoryId: Long,
) = if (categoryId != 0L) {
categoryRepository.addMangaToCategory(mangaId, categoryId)
.map { serverListeners.updateCategoryManga(categoryId) }
} else {
flow {
serverListeners.updateCategoryManga(categoryId)
emit(Unit)
}
@Inject
class AddMangaToCategory(
private val categoryRepository: CategoryRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, categoryId)
.catch {
onError(it)
log.warn(it) { "Failed to add $mangaId to category $categoryId" }
}
.collect()
fun asFlow(
manga: Manga,
category: Category,
) = if (category.id != 0L) {
categoryRepository.addMangaToCategory(manga.id, category.id)
.map { serverListeners.updateCategoryManga(category.id) }
} else {
flow {
serverListeners.updateCategoryManga(category.id)
emit(Unit)
}
suspend fun await(
manga: Manga,
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, category)
.catch {
onError(it)
log.warn(it) { "Failed to add ${manga.title}(${manga.id}) to category ${category.name}" }
}
.collect()
companion object {
private val log = logging()
fun asFlow(
mangaId: Long,
categoryId: Long,
) = if (categoryId != 0L) {
categoryRepository.addMangaToCategory(mangaId, categoryId)
.map { serverListeners.updateCategoryManga(categoryId) }
} else {
flow {
serverListeners.updateCategoryManga(categoryId)
emit(Unit)
}
}
fun asFlow(
manga: Manga,
category: Category,
) = if (category.id != 0L) {
categoryRepository.addMangaToCategory(manga.id, category.id)
.map { serverListeners.updateCategoryManga(category.id) }
} else {
flow {
serverListeners.updateCategoryManga(category.id)
emit(Unit)
}
}
companion object {
private val log = logging()
}
}

View File

@@ -12,24 +12,23 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class CreateCategory
@Inject
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
name: String,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(name)
.catch {
onError(it)
log.warn(it) { "Failed to create category $name" }
}
.collect()
fun asFlow(name: String) = categoryRepository.createCategory(name)
companion object {
private val log = logging()
@Inject
class CreateCategory(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
name: String,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(name)
.catch {
onError(it)
log.warn(it) { "Failed to create category $name" }
}
.collect()
fun asFlow(name: String) = categoryRepository.createCategory(name)
companion object {
private val log = logging()
}
}

View File

@@ -13,36 +13,35 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class DeleteCategory
@Inject
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(categoryId)
.catch {
onError(it)
log.warn(it) { "Failed to delete category $categoryId" }
}
.collect()
suspend fun await(
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category)
.catch {
onError(it)
log.warn(it) { "Failed to delete category ${category.name}" }
}
.collect()
fun asFlow(categoryId: Long) = categoryRepository.deleteCategory(categoryId)
fun asFlow(category: Category) = categoryRepository.deleteCategory(category.id)
companion object {
private val log = logging()
@Inject
class DeleteCategory(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(categoryId)
.catch {
onError(it)
log.warn(it) { "Failed to delete category $categoryId" }
}
.collect()
suspend fun await(
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category)
.catch {
onError(it)
log.warn(it) { "Failed to delete category ${category.name}" }
}
.collect()
fun asFlow(categoryId: Long) = categoryRepository.deleteCategory(categoryId)
fun asFlow(category: Category) = categoryRepository.deleteCategory(category.id)
companion object {
private val log = logging()
}
}

View File

@@ -13,32 +13,31 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetCategories
@Inject
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
dropDefault: Boolean = false,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(dropDefault)
.catch {
onError(it)
log.warn(it) { "Failed to get categories" }
}
.singleOrNull()
fun asFlow(dropDefault: Boolean = false) =
categoryRepository.getCategories()
.map { categories ->
if (dropDefault) {
categories.filterNot { it.name.equals("default", true) }
} else {
categories
}
}
companion object {
private val log = logging()
@Inject
class GetCategories(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
dropDefault: Boolean = false,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(dropDefault)
.catch {
onError(it)
log.warn(it) { "Failed to get categories" }
}
.singleOrNull()
fun asFlow(dropDefault: Boolean = false) =
categoryRepository.getCategories()
.map { categories ->
if (dropDefault) {
categories.filterNot { it.name.equals("default", true) }
} else {
categories
}
}
companion object {
private val log = logging()
}
}

View File

@@ -13,36 +13,35 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetMangaCategories
@Inject
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.catch {
onError(it)
log.warn(it) { "Failed to get categories for $mangaId" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.catch {
onError(it)
log.warn(it) { "Failed to get categories for ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) = categoryRepository.getMangaCategories(mangaId)
fun asFlow(manga: Manga) = categoryRepository.getMangaCategories(manga.id)
companion object {
private val log = logging()
@Inject
class GetMangaCategories(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.catch {
onError(it)
log.warn(it) { "Failed to get categories for $mangaId" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.catch {
onError(it)
log.warn(it) { "Failed to get categories for ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) = categoryRepository.getMangaCategories(mangaId)
fun asFlow(manga: Manga) = categoryRepository.getMangaCategories(manga.id)
companion object {
private val log = logging()
}
}

View File

@@ -15,45 +15,44 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetMangaListFromCategory
@Inject
constructor(
private val categoryRepository: CategoryRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(categoryId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga list from category $categoryId" }
}
.singleOrNull()
suspend fun await(
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga list from category ${category.name}" }
}
.singleOrNull()
fun asFlow(categoryId: Long) =
serverListeners.combineCategoryManga(
categoryRepository.getMangaFromCategory(categoryId),
) { categoryId == it }
fun asFlow(category: Category) =
serverListeners.combineCategoryManga(
categoryRepository.getMangaFromCategory(category.id),
) { category.id == it }
companion object {
private val log = logging()
@Inject
class GetMangaListFromCategory(
private val categoryRepository: CategoryRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(categoryId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga list from category $categoryId" }
}
.singleOrNull()
suspend fun await(
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga list from category ${category.name}" }
}
.singleOrNull()
fun asFlow(categoryId: Long) =
serverListeners.combineCategoryManga(
categoryRepository.getMangaFromCategory(categoryId),
) { categoryId == it }
fun asFlow(category: Category) =
serverListeners.combineCategoryManga(
categoryRepository.getMangaFromCategory(category.id),
) { category.id == it }
companion object {
private val log = logging()
}
}

View File

@@ -13,52 +13,51 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class ModifyCategory
@Inject
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
categoryId: Long,
name: String,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(
categoryId = categoryId,
name = name,
).catch {
onError(it)
log.warn(it) { "Failed to modify category $categoryId with options: name=$name" }
}.collect()
@Inject
class ModifyCategory(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
categoryId: Long,
name: String,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(
categoryId = categoryId,
name = name,
).catch {
onError(it)
log.warn(it) { "Failed to modify category $categoryId with options: name=$name" }
}.collect()
suspend fun await(
category: Category,
name: String? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(
category = category,
name = name,
).catch {
onError(it)
log.warn(it) { "Failed to modify category ${category.name} with options: name=$name" }
}.collect()
suspend fun await(
category: Category,
name: String? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(
category = category,
name = name,
).catch {
onError(it)
log.warn(it) { "Failed to modify category ${category.name} with options: name=$name" }
}.collect()
fun asFlow(
categoryId: Long,
name: String,
) = categoryRepository.modifyCategory(
categoryId = categoryId,
name = name,
)
fun asFlow(
categoryId: Long,
name: String,
) = categoryRepository.modifyCategory(
categoryId = categoryId,
name = name,
)
fun asFlow(
category: Category,
name: String? = null,
) = categoryRepository.modifyCategory(
categoryId = category.id,
name = name ?: category.name,
)
fun asFlow(
category: Category,
name: String? = null,
) = categoryRepository.modifyCategory(
categoryId = category.id,
name = name ?: category.name,
)
companion object {
private val log = logging()
}
companion object {
private val log = logging()
}
}

View File

@@ -17,61 +17,60 @@ import kotlinx.coroutines.flow.map
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class RemoveMangaFromCategory
@Inject
constructor(
private val categoryRepository: CategoryRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, categoryId)
.catch {
onError(it)
log.warn(it) { "Failed to remove $mangaId from category $categoryId" }
}
.collect()
suspend fun await(
manga: Manga,
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, category)
.catch {
onError(it)
log.warn(it) { "Failed to remove ${manga.title}(${manga.id}) from category ${category.name}" }
}
.collect()
fun asFlow(
mangaId: Long,
categoryId: Long,
) = if (categoryId != 0L) {
categoryRepository.removeMangaFromCategory(mangaId, categoryId)
.map { serverListeners.updateCategoryManga(categoryId) }
} else {
flow {
serverListeners.updateCategoryManga(categoryId)
emit(Unit)
}
@Inject
class RemoveMangaFromCategory(
private val categoryRepository: CategoryRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId, categoryId)
.catch {
onError(it)
log.warn(it) { "Failed to remove $mangaId from category $categoryId" }
}
.collect()
fun asFlow(
manga: Manga,
category: Category,
) = if (category.id != 0L) {
categoryRepository.removeMangaFromCategory(manga.id, category.id)
.map { serverListeners.updateCategoryManga(category.id) }
} else {
flow {
serverListeners.updateCategoryManga(category.id)
emit(Unit)
}
suspend fun await(
manga: Manga,
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, category)
.catch {
onError(it)
log.warn(it) { "Failed to remove ${manga.title}(${manga.id}) from category ${category.name}" }
}
.collect()
companion object {
private val log = logging()
fun asFlow(
mangaId: Long,
categoryId: Long,
) = if (categoryId != 0L) {
categoryRepository.removeMangaFromCategory(mangaId, categoryId)
.map { serverListeners.updateCategoryManga(categoryId) }
} else {
flow {
serverListeners.updateCategoryManga(categoryId)
emit(Unit)
}
}
fun asFlow(
manga: Manga,
category: Category,
) = if (category.id != 0L) {
categoryRepository.removeMangaFromCategory(manga.id, category.id)
.map { serverListeners.updateCategoryManga(category.id) }
} else {
flow {
serverListeners.updateCategoryManga(category.id)
emit(Unit)
}
}
companion object {
private val log = logging()
}
}

View File

@@ -12,28 +12,27 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class ReorderCategory
@Inject
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
categoryId: Long,
position: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(categoryId, position)
.catch {
onError(it)
log.warn(it) { "Failed to move category $categoryId to $position" }
}
.collect()
fun asFlow(
categoryId: Long,
position: Int,
) = categoryRepository.reorderCategory(categoryId, position)
companion object {
private val log = logging()
@Inject
class ReorderCategory(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
categoryId: Long,
position: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(categoryId, position)
.catch {
onError(it)
log.warn(it) { "Failed to move category $categoryId to $position" }
}
.collect()
fun asFlow(
categoryId: Long,
position: Int,
) = categoryRepository.reorderCategory(categoryId, position)
companion object {
private val log = logging()
}
}

View File

@@ -14,37 +14,36 @@ import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateCategoryMeta
@Inject
constructor(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
category: Category,
example: Int = category.meta.example,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category, example)
.catch {
onError(it)
log.warn(it) { "Failed to update ${category.name}(${category.id}) meta" }
}
.collect()
fun asFlow(
category: Category,
example: Int = category.meta.example,
) = flow {
if (example != category.meta.example) {
categoryRepository.updateCategoryMeta(
category.id,
"example",
example.toString(),
).collect()
}
emit(Unit)
@Inject
class UpdateCategoryMeta(
private val categoryRepository: CategoryRepository,
) {
suspend fun await(
category: Category,
example: Int = category.meta.example,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category, example)
.catch {
onError(it)
log.warn(it) { "Failed to update ${category.name}(${category.id}) meta" }
}
.collect()
companion object {
private val log = logging()
fun asFlow(
category: Category,
example: Int = category.meta.example,
) = flow {
if (example != category.meta.example) {
categoryRepository.updateCategoryMeta(
category.id,
"example",
example.toString(),
).collect()
}
emit(Unit)
}
companion object {
private val log = logging()
}
}

View File

@@ -16,73 +16,72 @@ import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
import kotlin.jvm.JvmName
class DeleteChapterDownload
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId)
.catch {
onError(it)
log.warn(it) { "Failed to delete chapter download for $chapterId" }
}
.collect()
@JvmName("awaitChapter")
suspend fun await(
chapter: Chapter,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter)
.catch {
onError(it)
log.warn(it) { "Failed to delete chapter download for ${chapter.index} of ${chapter.mangaId}" }
}
.collect()
suspend fun await(
chapterIds: List<Long>,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterIds)
.catch {
onError(it)
log.warn(it) { "Failed to delete chapter download for $chapterIds" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
chapters: List<Chapter>,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapters)
.catch {
onError(it)
log.warn(it) { "Failed to delete chapter download for ${chapters.joinToString { it.id.toString() }}" }
}
.collect()
fun asFlow(chapterId: Long) =
chapterRepository.deleteDownloadedChapter(chapterId)
.onEach { serverListeners.updateChapters(chapterId) }
@JvmName("asFlowChapter")
fun asFlow(chapter: Chapter) =
chapterRepository.deleteDownloadedChapter(chapter.id)
.onEach { serverListeners.updateChapters(chapter.id) }
fun asFlow(chapterIds: List<Long>) =
chapterRepository.deleteDownloadedChapters(chapterIds)
.onEach { serverListeners.updateChapters(chapterIds) }
@JvmName("asFlowChapters")
fun asFlow(chapter: List<Chapter>) =
chapterRepository.deleteDownloadedChapters(chapter.map { it.id })
.onEach { serverListeners.updateChapters(chapter.map { it.id }) }
companion object {
private val log = logging()
@Inject
class DeleteChapterDownload(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId)
.catch {
onError(it)
log.warn(it) { "Failed to delete chapter download for $chapterId" }
}
.collect()
@JvmName("awaitChapter")
suspend fun await(
chapter: Chapter,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter)
.catch {
onError(it)
log.warn(it) { "Failed to delete chapter download for ${chapter.index} of ${chapter.mangaId}" }
}
.collect()
suspend fun await(
chapterIds: List<Long>,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterIds)
.catch {
onError(it)
log.warn(it) { "Failed to delete chapter download for $chapterIds" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
chapters: List<Chapter>,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapters)
.catch {
onError(it)
log.warn(it) { "Failed to delete chapter download for ${chapters.joinToString { it.id.toString() }}" }
}
.collect()
fun asFlow(chapterId: Long) =
chapterRepository.deleteDownloadedChapter(chapterId)
.onEach { serverListeners.updateChapters(chapterId) }
@JvmName("asFlowChapter")
fun asFlow(chapter: Chapter) =
chapterRepository.deleteDownloadedChapter(chapter.id)
.onEach { serverListeners.updateChapters(chapter.id) }
fun asFlow(chapterIds: List<Long>) =
chapterRepository.deleteDownloadedChapters(chapterIds)
.onEach { serverListeners.updateChapters(chapterIds) }
@JvmName("asFlowChapters")
fun asFlow(chapter: List<Chapter>) =
chapterRepository.deleteDownloadedChapters(chapter.map { it.id })
.onEach { serverListeners.updateChapters(chapter.map { it.id }) }
companion object {
private val log = logging()
}
}

View File

@@ -15,47 +15,46 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetChapter
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapter $chapterId" }
}
.singleOrNull()
suspend fun await(
chapter: Chapter,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapter ${chapter.index} for ${chapter.mangaId}" }
}
.singleOrNull()
fun asFlow(chapterId: Long) =
serverListeners.combineChapters(
chapterRepository.getChapter(chapterId),
chapterIdPredate = { ids -> chapterId in ids },
)
fun asFlow(chapter: Chapter) =
serverListeners.combineChapters(
chapterRepository.getChapter(chapter.id),
chapterIdPredate = { ids -> chapter.id in ids },
)
companion object {
private val log = logging()
@Inject
class GetChapter(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapter $chapterId" }
}
.singleOrNull()
suspend fun await(
chapter: Chapter,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapter ${chapter.index} for ${chapter.mangaId}" }
}
.singleOrNull()
fun asFlow(chapterId: Long) =
serverListeners.combineChapters(
chapterRepository.getChapter(chapterId),
chapterIdPredate = { ids -> chapterId in ids },
)
fun asFlow(chapter: Chapter) =
serverListeners.combineChapters(
chapterRepository.getChapter(chapter.id),
chapterIdPredate = { ids -> chapter.id in ids },
)
companion object {
private val log = logging()
}
}

View File

@@ -13,40 +13,39 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetChapterPages
@Inject
constructor(
private val chapterRepository: ChapterRepository,
) {
suspend fun await(
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId)
.catch {
onError(it)
log.warn(it) { "Failed to get pages for $chapterId" }
}
.singleOrNull()
suspend fun await(
url: String,
onError: suspend (Throwable) -> Unit = {},
block: HttpRequestBuilder.() -> Unit,
) = asFlow(url, block)
.catch {
onError(it)
log.warn(it) { "Failed to get page $url" }
}
.singleOrNull()
fun asFlow(chapterId: Long) = chapterRepository.getPages(chapterId)
fun asFlow(
url: String,
block: HttpRequestBuilder.() -> Unit,
) = chapterRepository.getPage(url, block)
companion object {
private val log = logging()
@Inject
class GetChapterPages(
private val chapterRepository: ChapterRepository,
) {
suspend fun await(
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId)
.catch {
onError(it)
log.warn(it) { "Failed to get pages for $chapterId" }
}
.singleOrNull()
suspend fun await(
url: String,
onError: suspend (Throwable) -> Unit = {},
block: HttpRequestBuilder.() -> Unit,
) = asFlow(url, block)
.catch {
onError(it)
log.warn(it) { "Failed to get page $url" }
}
.singleOrNull()
fun asFlow(chapterId: Long) = chapterRepository.getPages(chapterId)
fun asFlow(
url: String,
block: HttpRequestBuilder.() -> Unit,
) = chapterRepository.getPage(url, block)
companion object {
private val log = logging()
}
}

View File

@@ -15,47 +15,46 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetChapters
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapters for $mangaId" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapters for ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
serverListeners.combineChapters(
chapterRepository.getChapters(mangaId),
chapterIdPredate = { ids -> false }, // todo
)
fun asFlow(manga: Manga) =
serverListeners.combineChapters(
chapterRepository.getChapters(manga.id),
chapterIdPredate = { ids -> false }, // todo
)
companion object {
private val log = logging()
@Inject
class GetChapters(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapters for $mangaId" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get chapters for ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
serverListeners.combineChapters(
chapterRepository.getChapters(mangaId),
chapterIdPredate = { ids -> false }, // todo
)
fun asFlow(manga: Manga) =
serverListeners.combineChapters(
chapterRepository.getChapters(manga.id),
chapterIdPredate = { ids -> false }, // todo
)
companion object {
private val log = logging()
}
}

View File

@@ -15,41 +15,40 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class RefreshChapters
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.catch {
onError(it)
log.warn(it) { "Failed to refresh chapters for $mangaId" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.catch {
onError(it)
log.warn(it) { "Failed to refresh chapters for ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
chapterRepository.fetchChapters(mangaId)
.onEach { serverListeners.updateChapters(mangaId) }
fun asFlow(manga: Manga) =
chapterRepository.fetchChapters(manga.id)
.onEach { serverListeners.updateChapters(manga.id) }
companion object {
private val log = logging()
@Inject
class RefreshChapters(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.catch {
onError(it)
log.warn(it) { "Failed to refresh chapters for $mangaId" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.catch {
onError(it)
log.warn(it) { "Failed to refresh chapters for ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
chapterRepository.fetchChapters(mangaId)
.onEach { serverListeners.updateChapters(mangaId) }
fun asFlow(manga: Manga) =
chapterRepository.fetchChapters(manga.id)
.onEach { serverListeners.updateChapters(manga.id) }
companion object {
private val log = logging()
}
}

View File

@@ -16,115 +16,114 @@ import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
import kotlin.jvm.JvmName
class UpdateChapter
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapterId: Long,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId, bookmarked, read, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapter $chapterId" }
}
.collect()
suspend fun await(
chapter: Chapter,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, bookmarked, read, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapter ${chapter.index} of ${chapter.mangaId}" }
}
.collect()
suspend fun await(
chapterIds: List<Long>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterIds, bookmarked, read, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapters $chapterIds" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
chapters: List<Chapter>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapters, bookmarked, read, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapters ${chapters.joinToString { it.id.toString() }}" }
}
.collect()
fun asFlow(
chapterId: Long,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
) = chapterRepository.updateChapter(
chapterId = chapterId,
bookmarked = bookmarked,
read = read,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapterId) }
fun asFlow(
chapter: Chapter,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
) = chapterRepository.updateChapter(
chapterId = chapter.id,
bookmarked = bookmarked,
read = read,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapter.id) }
fun asFlow(
chapterIds: List<Long>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
) = chapterRepository.updateChapters(
chapterIds = chapterIds,
bookmarked = bookmarked,
read = read,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapterIds) }
@JvmName("asFlowChapters")
fun asFlow(
chapters: List<Chapter>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
) = chapterRepository.updateChapters(
chapterIds = chapters.map { it.id },
bookmarked = bookmarked,
read = read,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapters.map { it.id }) }
companion object {
private val log = logging()
@Inject
class UpdateChapter(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapterId: Long,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId, bookmarked, read, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapter $chapterId" }
}
.collect()
suspend fun await(
chapter: Chapter,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, bookmarked, read, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapter ${chapter.index} of ${chapter.mangaId}" }
}
.collect()
suspend fun await(
chapterIds: List<Long>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterIds, bookmarked, read, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapters $chapterIds" }
}
.collect()
@JvmName("awaitChapters")
suspend fun await(
chapters: List<Chapter>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapters, bookmarked, read, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter bookmark for chapters ${chapters.joinToString { it.id.toString() }}" }
}
.collect()
fun asFlow(
chapterId: Long,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
) = chapterRepository.updateChapter(
chapterId = chapterId,
bookmarked = bookmarked,
read = read,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapterId) }
fun asFlow(
chapter: Chapter,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
) = chapterRepository.updateChapter(
chapterId = chapter.id,
bookmarked = bookmarked,
read = read,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapter.id) }
fun asFlow(
chapterIds: List<Long>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
) = chapterRepository.updateChapters(
chapterIds = chapterIds,
bookmarked = bookmarked,
read = read,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapterIds) }
@JvmName("asFlowChapters")
fun asFlow(
chapters: List<Chapter>,
bookmarked: Boolean? = null,
read: Boolean? = null,
lastPageRead: Int? = null,
) = chapterRepository.updateChapters(
chapterIds = chapters.map { it.id },
bookmarked = bookmarked,
read = read,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapters.map { it.id }) }
companion object {
private val log = logging()
}
}

View File

@@ -15,51 +15,50 @@ import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateChapterLastPageRead
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapterId: Long,
lastPageRead: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter last page read for chapter $chapterId" }
}
.collect()
suspend fun await(
chapter: Chapter,
lastPageRead: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter last page read for chapter ${chapter.index} of ${chapter.mangaId}" }
}
.collect()
fun asFlow(
chapterId: Long,
lastPageRead: Int,
) = chapterRepository.updateChapter(
chapterId = chapterId,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapterId) }
fun asFlow(
chapter: Chapter,
lastPageRead: Int,
) = chapterRepository.updateChapter(
chapterId = chapter.id,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapter.id) }
companion object {
private val log = logging()
@Inject
class UpdateChapterLastPageRead(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapterId: Long,
lastPageRead: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter last page read for chapter $chapterId" }
}
.collect()
suspend fun await(
chapter: Chapter,
lastPageRead: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, lastPageRead)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter last page read for chapter ${chapter.index} of ${chapter.mangaId}" }
}
.collect()
fun asFlow(
chapterId: Long,
lastPageRead: Int,
) = chapterRepository.updateChapter(
chapterId = chapterId,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapterId) }
fun asFlow(
chapter: Chapter,
lastPageRead: Int,
) = chapterRepository.updateChapter(
chapterId = chapter.id,
lastPageRead = lastPageRead,
).onEach { serverListeners.updateChapters(chapter.id) }
companion object {
private val log = logging()
}
}

View File

@@ -15,39 +15,38 @@ import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateChapterMeta
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapter: Chapter,
pageOffset: Int = chapter.meta.juiPageOffset,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, pageOffset)
.catch {
onError(it)
log.warn(it) { "Failed to update ${chapter.name}(${chapter.index}) meta" }
}
.collect()
fun asFlow(
chapter: Chapter,
pageOffset: Int = chapter.meta.juiPageOffset,
) = flow {
if (pageOffset != chapter.meta.juiPageOffset) {
chapterRepository.updateChapterMeta(
chapter.id,
"juiPageOffset",
pageOffset.toString(),
).collect()
serverListeners.updateChapters(chapter.id)
}
emit(Unit)
@Inject
class UpdateChapterMeta(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapter: Chapter,
pageOffset: Int = chapter.meta.juiPageOffset,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, pageOffset)
.catch {
onError(it)
log.warn(it) { "Failed to update ${chapter.name}(${chapter.index}) meta" }
}
.collect()
companion object {
private val log = logging()
fun asFlow(
chapter: Chapter,
pageOffset: Int = chapter.meta.juiPageOffset,
) = flow {
if (pageOffset != chapter.meta.juiPageOffset) {
chapterRepository.updateChapterMeta(
chapter.id,
"juiPageOffset",
pageOffset.toString(),
).collect()
serverListeners.updateChapters(chapter.id)
}
emit(Unit)
}
companion object {
private val log = logging()
}
}

View File

@@ -15,59 +15,58 @@ import kotlinx.coroutines.flow.onEach
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateChapterRead
@Inject
constructor(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapterId: Long,
read: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId, read)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter read status for chapter $chapterId" }
}
.collect()
suspend fun await(
chapter: Chapter,
read: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, read)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter read status for chapter ${chapter.index} of ${chapter.mangaId}" }
}
.collect()
fun asFlow(
chapterId: Long,
read: Boolean,
) = chapterRepository.updateChapter(
chapterId = chapterId,
read = read,
).onEach { serverListeners.updateChapters(chapterId) }
fun asFlow(
chapterIds: List<Long>,
read: Boolean,
) = chapterRepository.updateChapters(
chapterIds = chapterIds,
read = read,
).onEach { serverListeners.updateChapters(chapterIds) }
fun asFlow(
chapter: Chapter,
read: Boolean,
) = chapterRepository.updateChapter(
chapterId = chapter.id,
read = read,
).onEach { serverListeners.updateChapters(chapter.id) }
companion object {
private val log = logging()
@Inject
class UpdateChapterRead(
private val chapterRepository: ChapterRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
chapterId: Long,
read: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId, read)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter read status for chapter $chapterId" }
}
.collect()
suspend fun await(
chapter: Chapter,
read: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapter, read)
.catch {
onError(it)
log.warn(it) { "Failed to update chapter read status for chapter ${chapter.index} of ${chapter.mangaId}" }
}
.collect()
fun asFlow(
chapterId: Long,
read: Boolean,
) = chapterRepository.updateChapter(
chapterId = chapterId,
read = read,
).onEach { serverListeners.updateChapters(chapterId) }
fun asFlow(
chapterIds: List<Long>,
read: Boolean,
) = chapterRepository.updateChapters(
chapterIds = chapterIds,
read = read,
).onEach { serverListeners.updateChapters(chapterIds) }
fun asFlow(
chapter: Chapter,
read: Boolean,
) = chapterRepository.updateChapter(
chapterId = chapter.id,
read = read,
).onEach { serverListeners.updateChapters(chapter.id) }
companion object {
private val log = logging()
}
}

View File

@@ -12,36 +12,35 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class BatchChapterDownload
@Inject
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(
chapterIds: List<Long>,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterIds)
.catch {
onError(it)
log.warn(it) { "Failed to queue chapters $chapterIds for a download" }
}
.collect()
suspend fun await(
vararg chapterIds: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(*chapterIds)
.catch {
onError(it)
log.warn(it) { "Failed to queue chapters ${chapterIds.asList()} for a download" }
}
.collect()
fun asFlow(chapterIds: List<Long>) = downloadRepository.batchDownload(chapterIds)
fun asFlow(vararg chapterIds: Long) = downloadRepository.batchDownload(chapterIds.asList())
companion object {
private val log = logging()
@Inject
class BatchChapterDownload(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(
chapterIds: List<Long>,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterIds)
.catch {
onError(it)
log.warn(it) { "Failed to queue chapters $chapterIds for a download" }
}
.collect()
suspend fun await(
vararg chapterIds: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(*chapterIds)
.catch {
onError(it)
log.warn(it) { "Failed to queue chapters ${chapterIds.asList()} for a download" }
}
.collect()
fun asFlow(chapterIds: List<Long>) = downloadRepository.batchDownload(chapterIds)
fun asFlow(vararg chapterIds: Long) = downloadRepository.batchDownload(chapterIds.asList())
companion object {
private val log = logging()
}
}

View File

@@ -12,22 +12,21 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class ClearDownloadQueue
@Inject
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to clear download queue" }
}
.collect()
@Inject
class ClearDownloadQueue(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to clear download queue" }
}
.collect()
fun asFlow() = downloadRepository.clearDownloadQueue()
fun asFlow() = downloadRepository.clearDownloadQueue()
companion object {
private val log = logging()
}
companion object {
private val log = logging()
}
}

View File

@@ -12,24 +12,23 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class QueueChapterDownload
@Inject
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId)
.catch {
onError(it)
log.warn(it) { "Failed to queue chapter $chapterId for a download" }
}
.collect()
fun asFlow(chapterId: Long) = downloadRepository.queueChapterDownload(chapterId)
companion object {
private val log = logging()
@Inject
class QueueChapterDownload(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId)
.catch {
onError(it)
log.warn(it) { "Failed to queue chapter $chapterId for a download" }
}
.collect()
fun asFlow(chapterId: Long) = downloadRepository.queueChapterDownload(chapterId)
companion object {
private val log = logging()
}
}

View File

@@ -12,28 +12,27 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class ReorderChapterDownload
@Inject
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(
chapterId: Long,
to: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId, to)
.catch {
onError(it)
log.warn(it) { "Failed to reorder chapter download for $chapterId to $to" }
}
.collect()
fun asFlow(
chapterId: Long,
to: Int,
) = downloadRepository.reorderChapterDownload(chapterId, to)
companion object {
private val log = logging()
@Inject
class ReorderChapterDownload(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(
chapterId: Long,
to: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId, to)
.catch {
onError(it)
log.warn(it) { "Failed to reorder chapter download for $chapterId to $to" }
}
.collect()
fun asFlow(
chapterId: Long,
to: Int,
) = downloadRepository.reorderChapterDownload(chapterId, to)
companion object {
private val log = logging()
}
}

View File

@@ -12,22 +12,21 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class StartDownloading
@Inject
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to start downloader" }
}
.collect()
@Inject
class StartDownloading(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to start downloader" }
}
.collect()
fun asFlow() = downloadRepository.startDownloading()
fun asFlow() = downloadRepository.startDownloading()
companion object {
private val log = logging()
}
companion object {
private val log = logging()
}
}

View File

@@ -12,24 +12,23 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class StopChapterDownload
@Inject
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId)
.catch {
onError(it)
log.warn(it) { "Failed to stop chapter download for $chapterId" }
}
.collect()
fun asFlow(chapterId: Long) = downloadRepository.stopChapterDownload(chapterId)
companion object {
private val log = logging()
@Inject
class StopChapterDownload(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(
chapterId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(chapterId)
.catch {
onError(it)
log.warn(it) { "Failed to stop chapter download for $chapterId" }
}
.collect()
fun asFlow(chapterId: Long) = downloadRepository.stopChapterDownload(chapterId)
companion object {
private val log = logging()
}
}

View File

@@ -12,22 +12,21 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class StopDownloading
@Inject
constructor(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to stop downloader" }
}
.collect()
@Inject
class StopDownloading(
private val downloadRepository: DownloadRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to stop downloader" }
}
.collect()
fun asFlow() = downloadRepository.stopDownloading()
fun asFlow() = downloadRepository.stopDownloading()
companion object {
private val log = logging()
}
companion object {
private val log = logging()
}
}

View File

@@ -16,42 +16,40 @@ import io.ktor.websocket.Frame
import io.ktor.websocket.readText
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString
import me.tatarka.inject.annotations.Inject
class DownloadService
@Inject
constructor(
serverPreferences: ServerPreferences,
client: Http,
) : WebsocketService(serverPreferences, client) {
override val _status: MutableStateFlow<Status>
get() = status
@Inject
class DownloadService(
serverPreferences: ServerPreferences,
client: Http,
) : WebsocketService(serverPreferences, client) {
override val _status: MutableStateFlow<Status>
get() = status
override val query: String
get() = "/api/v1/downloads"
override val query: String
get() = "/api/v1/downloads"
override suspend fun onReceived(frame: Frame.Text) {
val status = json.decodeFromString<DownloadStatus>(frame.readText())
downloaderStatus.value = status.status
downloadQueue.value = status.queue
}
companion object {
val status = MutableStateFlow(Status.STARTING)
val downloadQueue = MutableStateFlow(emptyList<DownloadChapter>())
val downloaderStatus = MutableStateFlow(DownloaderStatus.Stopped)
fun registerWatch(mangaId: Long) =
downloadQueue
.map {
it.filter { it.mangaId == mangaId }
}
fun registerWatches(mangaIds: Set<Long>) =
downloadQueue
.map {
it.filter { it.mangaId in mangaIds }
}
}
override suspend fun onReceived(frame: Frame.Text) {
val status = json.decodeFromString<DownloadStatus>(frame.readText())
downloaderStatus.value = status.status
downloadQueue.value = status.queue
}
companion object {
val status = MutableStateFlow(Status.STARTING)
val downloadQueue = MutableStateFlow(emptyList<DownloadChapter>())
val downloaderStatus = MutableStateFlow(DownloaderStatus.Stopped)
fun registerWatch(mangaId: Long) =
downloadQueue
.map {
it.filter { it.mangaId == mangaId }
}
fun registerWatches(mangaIds: Set<Long>) =
downloadQueue
.map {
it.filter { it.mangaId in mangaIds }
}
}
}

View File

@@ -12,22 +12,21 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetExtensionList
@Inject
constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to get extension list" }
}
.singleOrNull()
@Inject
class GetExtensionList(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to get extension list" }
}
.singleOrNull()
fun asFlow() = extensionRepository.getExtensionList()
fun asFlow() = extensionRepository.getExtensionList()
companion object {
private val log = logging()
}
companion object {
private val log = logging()
}
}

View File

@@ -13,24 +13,23 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class InstallExtension
@Inject
constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(
extension: Extension,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(extension)
.catch {
onError(it)
log.warn(it) { "Failed to install extension ${extension.apkName}" }
}
.collect()
fun asFlow(extension: Extension) = extensionRepository.installExtension(extension.pkgName)
companion object {
private val log = logging()
@Inject
class InstallExtension(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(
extension: Extension,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(extension)
.catch {
onError(it)
log.warn(it) { "Failed to install extension ${extension.apkName}" }
}
.collect()
fun asFlow(extension: Extension) = extensionRepository.installExtension(extension.pkgName)
companion object {
private val log = logging()
}
}

View File

@@ -15,24 +15,23 @@ import okio.Path
import okio.SYSTEM
import org.lighthousegames.logging.logging
class InstallExtensionFile
@Inject
constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(
path: Path,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(path)
.catch {
onError(it)
log.warn(it) { "Failed to install extension from $path" }
}
.collect()
fun asFlow(path: Path) = extensionRepository.installExtension(FileSystem.SYSTEM.source(path))
companion object {
private val log = logging()
@Inject
class InstallExtensionFile(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(
path: Path,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(path)
.catch {
onError(it)
log.warn(it) { "Failed to install extension from $path" }
}
.collect()
fun asFlow(path: Path) = extensionRepository.installExtension(FileSystem.SYSTEM.source(path))
companion object {
private val log = logging()
}
}

View File

@@ -13,24 +13,23 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UninstallExtension
@Inject
constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(
extension: Extension,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(extension)
.catch {
onError(it)
log.warn(it) { "Failed to uninstall extension ${extension.apkName}" }
}
.collect()
fun asFlow(extension: Extension) = extensionRepository.uninstallExtension(extension.pkgName)
companion object {
private val log = logging()
@Inject
class UninstallExtension(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(
extension: Extension,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(extension)
.catch {
onError(it)
log.warn(it) { "Failed to uninstall extension ${extension.apkName}" }
}
.collect()
fun asFlow(extension: Extension) = extensionRepository.uninstallExtension(extension.pkgName)
companion object {
private val log = logging()
}
}

View File

@@ -13,24 +13,23 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateExtension
@Inject
constructor(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(
extension: Extension,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(extension)
.catch {
onError(it)
log.warn(it) { "Failed to update extension ${extension.apkName}" }
}
.collect()
fun asFlow(extension: Extension) = extensionRepository.updateExtension(extension.pkgName)
companion object {
private val log = logging()
@Inject
class UpdateExtension(
private val extensionRepository: ExtensionRepository,
) {
suspend fun await(
extension: Extension,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(extension)
.catch {
onError(it)
log.warn(it) { "Failed to update extension ${extension.apkName}" }
}
.collect()
fun asFlow(extension: Extension) = extensionRepository.updateExtension(extension.pkgName)
companion object {
private val log = logging()
}
}

View File

@@ -12,22 +12,21 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetGlobalMeta
@Inject
constructor(
private val globalRepository: GlobalRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to get global meta" }
}
.singleOrNull()
@Inject
class GetGlobalMeta(
private val globalRepository: GlobalRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to get global meta" }
}
.singleOrNull()
fun asFlow() = globalRepository.getGlobalMeta()
fun asFlow() = globalRepository.getGlobalMeta()
companion object {
private val log = logging()
}
companion object {
private val log = logging()
}
}

View File

@@ -14,36 +14,35 @@ import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateGlobalMeta
@Inject
constructor(
private val globalRepository: GlobalRepository,
) {
suspend fun await(
globalMeta: GlobalMeta,
example: Int = globalMeta.example,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(globalMeta, example)
.catch {
onError(it)
log.warn(it) { "Failed to update global meta" }
}
.collect()
fun asFlow(
globalMeta: GlobalMeta,
example: Int = globalMeta.example,
) = flow {
if (example != globalMeta.example) {
globalRepository.updateGlobalMeta(
"example",
example.toString(),
).collect()
}
emit(Unit)
@Inject
class UpdateGlobalMeta(
private val globalRepository: GlobalRepository,
) {
suspend fun await(
globalMeta: GlobalMeta,
example: Int = globalMeta.example,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(globalMeta, example)
.catch {
onError(it)
log.warn(it) { "Failed to update global meta" }
}
.collect()
companion object {
private val log = logging()
fun asFlow(
globalMeta: GlobalMeta,
example: Int = globalMeta.example,
) = flow {
if (example != globalMeta.example) {
globalRepository.updateGlobalMeta(
"example",
example.toString(),
).collect()
}
emit(Unit)
}
companion object {
private val log = logging()
}
}

View File

@@ -15,41 +15,40 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class AddMangaToLibrary
@Inject
constructor(
private val libraryRepository: LibraryRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.catch {
onError(it)
log.warn(it) { "Failed to add $mangaId to library" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.catch {
onError(it)
log.warn(it) { "Failed to add ${manga.title}(${manga.id}) to library" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
libraryRepository.addMangaToLibrary(mangaId)
.onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) =
libraryRepository.addMangaToLibrary(manga.id)
.onEach { serverListeners.updateManga(manga.id) }
companion object {
private val log = logging()
@Inject
class AddMangaToLibrary(
private val libraryRepository: LibraryRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.catch {
onError(it)
log.warn(it) { "Failed to add $mangaId to library" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.catch {
onError(it)
log.warn(it) { "Failed to add ${manga.title}(${manga.id}) to library" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
libraryRepository.addMangaToLibrary(mangaId)
.onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) =
libraryRepository.addMangaToLibrary(manga.id)
.onEach { serverListeners.updateManga(manga.id) }
companion object {
private val log = logging()
}
}

View File

@@ -15,41 +15,40 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class RemoveMangaFromLibrary
@Inject
constructor(
private val libraryRepository: LibraryRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.catch {
onError(it)
log.warn(it) { "Failed to remove $mangaId from library" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.catch {
onError(it)
log.warn(it) { "Failed to remove ${manga.title}(${manga.id}) from library" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
libraryRepository.removeMangaFromLibrary(mangaId)
.onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) =
libraryRepository.removeMangaFromLibrary(manga.id)
.onEach { serverListeners.updateManga(manga.id) }
companion object {
private val log = logging()
@Inject
class RemoveMangaFromLibrary(
private val libraryRepository: LibraryRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.catch {
onError(it)
log.warn(it) { "Failed to remove $mangaId from library" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.catch {
onError(it)
log.warn(it) { "Failed to remove ${manga.title}(${manga.id}) from library" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
libraryRepository.removeMangaFromLibrary(mangaId)
.onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) =
libraryRepository.removeMangaFromLibrary(manga.id)
.onEach { serverListeners.updateManga(manga.id) }
companion object {
private val log = logging()
}
}

View File

@@ -16,26 +16,25 @@ import kotlinx.coroutines.flow.MutableStateFlow
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class LibraryUpdateService
@Inject
constructor(
serverPreferences: ServerPreferences,
client: Http,
) : WebsocketService(serverPreferences, client) {
override val _status: MutableStateFlow<Status>
get() = status
@Inject
class LibraryUpdateService(
serverPreferences: ServerPreferences,
client: Http,
) : WebsocketService(serverPreferences, client) {
override val _status: MutableStateFlow<Status>
get() = status
override val query: String
get() = "/api/v1/update"
override val query: String
get() = "/api/v1/update"
override suspend fun onReceived(frame: Frame.Text) {
updateStatus.value = json.decodeFromString<UpdateStatus>(frame.readText())
}
companion object {
private val log = logging()
val status = MutableStateFlow(Status.STARTING)
val updateStatus = MutableStateFlow(UpdateStatus(emptyMap(), emptyMap(), false))
}
override suspend fun onReceived(frame: Frame.Text) {
updateStatus.value = json.decodeFromString<UpdateStatus>(frame.readText())
}
companion object {
private val log = logging()
val status = MutableStateFlow(Status.STARTING)
val updateStatus = MutableStateFlow(UpdateStatus(emptyMap(), emptyMap(), false))
}
}

View File

@@ -15,45 +15,44 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetManga
@Inject
constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga $mangaId" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
serverListeners.combineMangaUpdates(
mangaRepository.getManga(mangaId),
) { mangaId in it }
fun asFlow(manga: Manga) =
serverListeners.combineMangaUpdates(
mangaRepository.getManga(manga.id),
) { manga.id in it }
companion object {
private val log = logging()
@Inject
class GetManga(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga $mangaId" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to get manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) =
serverListeners.combineMangaUpdates(
mangaRepository.getManga(mangaId),
) { mangaId in it }
fun asFlow(manga: Manga) =
serverListeners.combineMangaUpdates(
mangaRepository.getManga(manga.id),
) { manga.id in it }
companion object {
private val log = logging()
}
}

View File

@@ -16,39 +16,38 @@ import kotlinx.coroutines.flow.take
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class RefreshManga
@Inject
constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to refresh manga $mangaId" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to refresh manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) = mangaRepository.refreshManga(mangaId).onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) = mangaRepository.refreshManga(manga.id).onEach { serverListeners.updateManga(manga.id) }
companion object {
private val log = logging()
@Inject
class RefreshManga(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
mangaId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(mangaId)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to refresh manga $mangaId" }
}
.singleOrNull()
suspend fun await(
manga: Manga,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga)
.take(1)
.catch {
onError(it)
log.warn(it) { "Failed to refresh manga ${manga.title}(${manga.id})" }
}
.singleOrNull()
fun asFlow(mangaId: Long) = mangaRepository.refreshManga(mangaId).onEach { serverListeners.updateManga(mangaId) }
fun asFlow(manga: Manga) = mangaRepository.refreshManga(manga.id).onEach { serverListeners.updateManga(manga.id) }
companion object {
private val log = logging()
}
}

View File

@@ -17,39 +17,38 @@ import kotlinx.coroutines.flow.flow
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateMangaMeta
@Inject
constructor(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
manga: Manga,
readerMode: String = manga.meta.juiReaderMode,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, readerMode)
.catch {
onError(it)
log.warn(it) { "Failed to update ${manga.title}(${manga.id}) meta" }
}
.collect()
fun asFlow(
manga: Manga,
readerMode: String = manga.meta.juiReaderMode.decodeURLQueryComponent(),
) = flow {
if (readerMode.encodeURLQueryComponent() != manga.meta.juiReaderMode) {
mangaRepository.updateMangaMeta(
manga.id,
"juiReaderMode",
readerMode,
).collect()
serverListeners.updateManga(manga.id)
}
emit(Unit)
@Inject
class UpdateMangaMeta(
private val mangaRepository: MangaRepository,
private val serverListeners: ServerListeners,
) {
suspend fun await(
manga: Manga,
readerMode: String = manga.meta.juiReaderMode,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manga, readerMode)
.catch {
onError(it)
log.warn(it) { "Failed to update ${manga.title}(${manga.id}) meta" }
}
.collect()
companion object {
private val log = logging()
fun asFlow(
manga: Manga,
readerMode: String = manga.meta.juiReaderMode.decodeURLQueryComponent(),
) = flow {
if (readerMode.encodeURLQueryComponent() != manga.meta.juiReaderMode) {
mangaRepository.updateMangaMeta(
manga.id,
"juiReaderMode",
readerMode,
).collect()
serverListeners.updateManga(manga.id)
}
emit(Unit)
}
companion object {
private val log = logging()
}
}

View File

@@ -11,20 +11,19 @@ import ca.gosyer.jui.domain.migration.service.MigrationPreferences
import ca.gosyer.jui.domain.reader.service.ReaderPreferences
import me.tatarka.inject.annotations.Inject
class RunMigrations
@Inject
constructor(
private val migrationPreferences: MigrationPreferences,
private val readerPreferences: ReaderPreferences,
) {
fun runMigrations() {
val code = migrationPreferences.version().get()
if (code <= 0) {
readerPreferences.modes().get().forEach {
readerPreferences.getMode(it).direction().delete()
}
migrationPreferences.version().set(BuildKonfig.MIGRATION_CODE)
return
@Inject
class RunMigrations(
private val migrationPreferences: MigrationPreferences,
private val readerPreferences: ReaderPreferences,
) {
fun runMigrations() {
val code = migrationPreferences.version().get()
if (code <= 0) {
readerPreferences.modes().get().forEach {
readerPreferences.getMode(it).direction().delete()
}
migrationPreferences.version().set(BuildKonfig.MIGRATION_CODE)
return
}
}
}

View File

@@ -12,22 +12,21 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class AboutServer
@Inject
constructor(
private val settingsRepository: SettingsRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to get server information" }
}
.singleOrNull()
@Inject
class AboutServer(
private val settingsRepository: SettingsRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to get server information" }
}
.singleOrNull()
fun asFlow() = settingsRepository.aboutServer()
fun asFlow() = settingsRepository.aboutServer()
companion object {
private val log = logging()
}
companion object {
private val log = logging()
}
}

View File

@@ -12,22 +12,21 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetSettings
@Inject
constructor(
private val settingsRepository: SettingsRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to check for server updates" }
}
.singleOrNull()
@Inject
class GetSettings(
private val settingsRepository: SettingsRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to check for server updates" }
}
.singleOrNull()
fun asFlow() = settingsRepository.getSettings()
fun asFlow() = settingsRepository.getSettings()
companion object {
private val log = logging()
}
companion object {
private val log = logging()
}
}

View File

@@ -13,24 +13,23 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class SetSettings
@Inject
constructor(
private val settingsRepository: SettingsRepository,
) {
suspend fun await(
input: SetSettingsInput,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(input)
.catch {
onError(it)
log.warn(it) { "Failed to check for server updates" }
}
.singleOrNull()
fun asFlow(input: SetSettingsInput) = settingsRepository.setSettings(input)
companion object {
private val log = logging()
@Inject
class SetSettings(
private val settingsRepository: SettingsRepository,
) {
suspend fun await(
input: SetSettingsInput,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(input)
.catch {
onError(it)
log.warn(it) { "Failed to check for server updates" }
}
.singleOrNull()
fun asFlow(input: SetSettingsInput) = settingsRepository.setSettings(input)
companion object {
private val log = logging()
}
}

View File

@@ -13,36 +13,35 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetFilterList
@Inject
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(source.id)
.catch {
onError(it)
log.warn(it) { "Failed to get filter list for ${source.displayName}" }
}
.singleOrNull()
suspend fun await(
sourceId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(sourceId)
.catch {
onError(it)
log.warn(it) { "Failed to get filter list for $sourceId" }
}
.singleOrNull()
fun asFlow(source: Source) = sourceRepository.getFilterList(source.id)
fun asFlow(sourceId: Long) = sourceRepository.getFilterList(sourceId)
companion object {
private val log = logging()
@Inject
class GetFilterList(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(source.id)
.catch {
onError(it)
log.warn(it) { "Failed to get filter list for ${source.displayName}" }
}
.singleOrNull()
suspend fun await(
sourceId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(sourceId)
.catch {
onError(it)
log.warn(it) { "Failed to get filter list for $sourceId" }
}
.singleOrNull()
fun asFlow(source: Source) = sourceRepository.getFilterList(source.id)
fun asFlow(sourceId: Long) = sourceRepository.getFilterList(sourceId)
companion object {
private val log = logging()
}
}

View File

@@ -13,44 +13,43 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetLatestManga
@Inject
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
page: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(source.id, page)
.catch {
onError(it)
log.warn(it) { "Failed to get latest manga from ${source.displayName} on page $page" }
}
.singleOrNull()
suspend fun await(
sourceId: Long,
page: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(sourceId, page)
.catch {
onError(it)
log.warn(it) { "Failed to get latest manga from $sourceId on page $page" }
}
.singleOrNull()
fun asFlow(
source: Source,
page: Int,
) = sourceRepository.getLatestManga(source.id, page)
fun asFlow(
sourceId: Long,
page: Int,
) = sourceRepository.getLatestManga(sourceId, page)
companion object {
private val log = logging()
@Inject
class GetLatestManga(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
page: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(source.id, page)
.catch {
onError(it)
log.warn(it) { "Failed to get latest manga from ${source.displayName} on page $page" }
}
.singleOrNull()
suspend fun await(
sourceId: Long,
page: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(sourceId, page)
.catch {
onError(it)
log.warn(it) { "Failed to get latest manga from $sourceId on page $page" }
}
.singleOrNull()
fun asFlow(
source: Source,
page: Int,
) = sourceRepository.getLatestManga(source.id, page)
fun asFlow(
sourceId: Long,
page: Int,
) = sourceRepository.getLatestManga(sourceId, page)
companion object {
private val log = logging()
}
}

View File

@@ -13,44 +13,43 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetPopularManga
@Inject
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
page: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(source.id, page)
.catch {
onError(it)
log.warn(it) { "Failed to get popular manga from ${source.displayName} on page $page" }
}
.singleOrNull()
suspend fun await(
sourceId: Long,
page: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(sourceId, page)
.catch {
onError(it)
log.warn(it) { "Failed to get popular manga from $sourceId on page $page" }
}
.singleOrNull()
fun asFlow(
source: Source,
page: Int,
) = sourceRepository.getPopularManga(source.id, page)
fun asFlow(
sourceId: Long,
page: Int,
) = sourceRepository.getPopularManga(sourceId, page)
companion object {
private val log = logging()
@Inject
class GetPopularManga(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
page: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(source.id, page)
.catch {
onError(it)
log.warn(it) { "Failed to get popular manga from ${source.displayName} on page $page" }
}
.singleOrNull()
suspend fun await(
sourceId: Long,
page: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(sourceId, page)
.catch {
onError(it)
log.warn(it) { "Failed to get popular manga from $sourceId on page $page" }
}
.singleOrNull()
fun asFlow(
source: Source,
page: Int,
) = sourceRepository.getPopularManga(source.id, page)
fun asFlow(
sourceId: Long,
page: Int,
) = sourceRepository.getPopularManga(sourceId, page)
companion object {
private val log = logging()
}
}

View File

@@ -14,64 +14,63 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetSearchManga
@Inject
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
page: Int,
searchTerm: String?,
filters: List<SourceFilter>?,
onError: suspend (Throwable) -> Unit = {
},
) = asFlow(source.id, page, searchTerm, filters)
.catch {
onError(it)
log.warn(it) { "Failed to get search results from ${source.displayName} on page $page with query '$searchTerm'" }
}
.singleOrNull()
suspend fun await(
sourceId: Long,
searchTerm: String?,
page: Int,
filters: List<SourceFilter>?,
onError: suspend (Throwable) -> Unit = {
},
) = asFlow(sourceId, page, searchTerm, filters)
.catch {
onError(it)
log.warn(it) { "Failed to get search results from $sourceId on page $page with query '$searchTerm'" }
}
.singleOrNull()
fun asFlow(
source: Source,
page: Int,
searchTerm: String?,
filters: List<SourceFilter>?,
) = sourceRepository.getSearchResults(
source.id,
page,
searchTerm?.ifBlank { null },
filters,
)
fun asFlow(
sourceId: Long,
page: Int,
searchTerm: String?,
filters: List<SourceFilter>?,
) = sourceRepository.getSearchResults(
sourceId,
page,
searchTerm?.ifBlank { null },
filters,
)
companion object {
private val log = logging()
@Inject
class GetSearchManga(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
page: Int,
searchTerm: String?,
filters: List<SourceFilter>?,
onError: suspend (Throwable) -> Unit = {
},
) = asFlow(source.id, page, searchTerm, filters)
.catch {
onError(it)
log.warn(it) { "Failed to get search results from ${source.displayName} on page $page with query '$searchTerm'" }
}
.singleOrNull()
suspend fun await(
sourceId: Long,
searchTerm: String?,
page: Int,
filters: List<SourceFilter>?,
onError: suspend (Throwable) -> Unit = {
},
) = asFlow(sourceId, page, searchTerm, filters)
.catch {
onError(it)
log.warn(it) { "Failed to get search results from $sourceId on page $page with query '$searchTerm'" }
}
.singleOrNull()
fun asFlow(
source: Source,
page: Int,
searchTerm: String?,
filters: List<SourceFilter>?,
) = sourceRepository.getSearchResults(
source.id,
page,
searchTerm?.ifBlank { null },
filters,
)
fun asFlow(
sourceId: Long,
page: Int,
searchTerm: String?,
filters: List<SourceFilter>?,
) = sourceRepository.getSearchResults(
sourceId,
page,
searchTerm?.ifBlank { null },
filters,
)
companion object {
private val log = logging()
}
}

View File

@@ -12,22 +12,21 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetSourceList
@Inject
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to get source list" }
}
.singleOrNull()
@Inject
class GetSourceList(
private val sourceRepository: SourceRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to get source list" }
}
.singleOrNull()
fun asFlow() = sourceRepository.getSourceList()
fun asFlow() = sourceRepository.getSourceList()
companion object {
private val log = logging()
}
companion object {
private val log = logging()
}
}

View File

@@ -13,36 +13,35 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetSourceSettings
@Inject
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(source.id)
.catch {
onError(it)
log.warn(it) { "Failed to get source settings for ${source.displayName}" }
}
.singleOrNull()
suspend fun await(
sourceId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(sourceId)
.catch {
onError(it)
log.warn(it) { "Failed to get source settings for $sourceId" }
}
.singleOrNull()
fun asFlow(source: Source) = sourceRepository.getSourceSettings(source.id)
fun asFlow(sourceId: Long) = sourceRepository.getSourceSettings(sourceId)
companion object {
private val log = logging()
@Inject
class GetSourceSettings(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
source: Source,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(source.id)
.catch {
onError(it)
log.warn(it) { "Failed to get source settings for ${source.displayName}" }
}
.singleOrNull()
suspend fun await(
sourceId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(sourceId)
.catch {
onError(it)
log.warn(it) { "Failed to get source settings for $sourceId" }
}
.singleOrNull()
fun asFlow(source: Source) = sourceRepository.getSourceSettings(source.id)
fun asFlow(sourceId: Long) = sourceRepository.getSourceSettings(sourceId)
companion object {
private val log = logging()
}
}

View File

@@ -13,32 +13,31 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class SetSourceSetting
@Inject
constructor(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
sourceId: Long,
sourcePreference: SourcePreference,
onError: suspend (Throwable) -> Unit = {
},
) = asFlow(sourceId, sourcePreference)
.catch {
onError(it)
log.warn(it) { "Failed to set setting for $sourceId with index = ${sourcePreference.position}" }
}
.collect()
fun asFlow(
sourceId: Long,
sourcePreference: SourcePreference,
) = sourceRepository.setSourceSetting(
sourceId,
sourcePreference,
)
companion object {
private val log = logging()
@Inject
class SetSourceSetting(
private val sourceRepository: SourceRepository,
) {
suspend fun await(
sourceId: Long,
sourcePreference: SourcePreference,
onError: suspend (Throwable) -> Unit = {
},
) = asFlow(sourceId, sourcePreference)
.catch {
onError(it)
log.warn(it) { "Failed to set setting for $sourceId with index = ${sourcePreference.position}" }
}
.collect()
fun asFlow(
sourceId: Long,
sourcePreference: SourcePreference,
) = sourceRepository.setSourceSetting(
sourceId,
sourcePreference,
)
companion object {
private val log = logging()
}
}

View File

@@ -33,21 +33,21 @@ fun interface GetMangaPage {
suspend fun get(page: Int): MangaPage?
}
class SourcePager
@Inject
constructor(
private val getManga: GetManga,
private val serverListeners: ServerListeners,
@Assisted private val fetcher: GetMangaPage,
) : CoroutineScope by CoroutineScope(Dispatchers.Default + SupervisorJob()) {
private val sourceMutex = Mutex()
@Inject
class SourcePager(
private val getManga: GetManga,
private val serverListeners: ServerListeners,
@Assisted private val fetcher: GetMangaPage,
) : CoroutineScope by CoroutineScope(Dispatchers.Default + SupervisorJob()) {
private val sourceMutex = Mutex()
private val _sourceManga = MutableStateFlow<List<Manga>>(emptyList())
private val _sourceManga = MutableStateFlow<List<Manga>>(emptyList())
private val mangaIds = _sourceManga.map { mangas -> mangas.map { it.id } }
.stateIn(this, SharingStarted.Eagerly, emptyList())
private val mangaIds = _sourceManga.map { mangas -> mangas.map { it.id } }
.stateIn(this, SharingStarted.Eagerly, emptyList())
private val changedManga = serverListeners.mangaListener.runningFold(emptyMap<Long, Manga>()) { manga, updatedMangaIds ->
private val changedManga =
serverListeners.mangaListener.runningFold(emptyMap<Long, Manga>()) { manga, updatedMangaIds ->
coroutineScope {
manga + updatedMangaIds.filter { it in mangaIds.value }.map {
async {
@@ -57,37 +57,37 @@ class SourcePager
}
}.stateIn(this, SharingStarted.Eagerly, emptyMap())
val mangas = combine(_sourceManga, changedManga) { sourceManga, changedManga ->
sourceManga.map { changedManga[it.id] ?: it }
}.stateIn(this, SharingStarted.Eagerly, emptyList())
val mangas = combine(_sourceManga, changedManga) { sourceManga, changedManga ->
sourceManga.map { changedManga[it.id] ?: it }
}.stateIn(this, SharingStarted.Eagerly, emptyList())
private val _pageNum = MutableStateFlow(0)
val pageNum = _pageNum.asStateFlow()
private val _pageNum = MutableStateFlow(0)
val pageNum = _pageNum.asStateFlow()
private val _hasNextPage = MutableStateFlow(true)
val hasNextPage = _hasNextPage.asStateFlow()
private val _hasNextPage = MutableStateFlow(true)
val hasNextPage = _hasNextPage.asStateFlow()
private val _loading = MutableStateFlow(true)
val loading = _loading.asStateFlow()
private val _loading = MutableStateFlow(true)
val loading = _loading.asStateFlow()
fun loadNextPage() {
launch {
if (hasNextPage.value && sourceMutex.tryLock()) {
_pageNum.value++
val page = fetcher.get(_pageNum.value)
if (page != null) {
_sourceManga.value = _sourceManga.value + page.mangaList
_hasNextPage.value = page.hasNextPage
} else {
_pageNum.value--
}
sourceMutex.unlock()
fun loadNextPage() {
launch {
if (hasNextPage.value && sourceMutex.tryLock()) {
_pageNum.value++
val page = fetcher.get(_pageNum.value)
if (page != null) {
_sourceManga.value = _sourceManga.value + page.mangaList
_hasNextPage.value = page.hasNextPage
} else {
_pageNum.value--
}
_loading.value = false
sourceMutex.unlock()
}
}
companion object {
private val log = logging()
_loading.value = false
}
}
companion object {
private val log = logging()
}
}

View File

@@ -15,7 +15,7 @@ enum class StartScreen {
Library,
Updates,
// History,
// History,
Sources,
Extensions,
}

View File

@@ -12,24 +12,23 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class GetRecentUpdates
@Inject
constructor(
private val updatesRepository: UpdatesRepository,
) {
suspend fun await(
pageNum: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(pageNum)
.catch {
onError(it)
log.warn(it) { "Failed to get updates for page $pageNum" }
}
.singleOrNull()
fun asFlow(pageNum: Int) = updatesRepository.getRecentUpdates(pageNum)
companion object {
private val log = logging()
@Inject
class GetRecentUpdates(
private val updatesRepository: UpdatesRepository,
) {
suspend fun await(
pageNum: Int,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(pageNum)
.catch {
onError(it)
log.warn(it) { "Failed to get updates for page $pageNum" }
}
.singleOrNull()
fun asFlow(pageNum: Int) = updatesRepository.getRecentUpdates(pageNum)
companion object {
private val log = logging()
}
}

View File

@@ -13,36 +13,35 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateCategory
@Inject
constructor(
private val updatesRepository: UpdatesRepository,
) {
suspend fun await(
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(categoryId)
.catch {
onError(it)
log.warn(it) { "Failed to update category $categoryId" }
}
.collect()
suspend fun await(
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category)
.catch {
onError(it)
log.warn(it) { "Failed to update category ${category.name}(${category.id})" }
}
.collect()
fun asFlow(categoryId: Long) = updatesRepository.updateCategory(categoryId)
fun asFlow(category: Category) = updatesRepository.updateCategory(category.id)
companion object {
private val log = logging()
@Inject
class UpdateCategory(
private val updatesRepository: UpdatesRepository,
) {
suspend fun await(
categoryId: Long,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(categoryId)
.catch {
onError(it)
log.warn(it) { "Failed to update category $categoryId" }
}
.collect()
suspend fun await(
category: Category,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(category)
.catch {
onError(it)
log.warn(it) { "Failed to update category ${category.name}(${category.id})" }
}
.collect()
fun asFlow(categoryId: Long) = updatesRepository.updateCategory(categoryId)
fun asFlow(category: Category) = updatesRepository.updateCategory(category.id)
companion object {
private val log = logging()
}
}

View File

@@ -21,77 +21,76 @@ import kotlinx.coroutines.flow.singleOrNull
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateChecker
@Inject
constructor(
private val updatePreferences: UpdatePreferences,
private val client: Http,
) {
suspend fun await(
manualFetch: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manualFetch)
.catch {
onError(it)
log.warn(it) { "Failed to check for updates" }
}
.singleOrNull()
fun asFlow(manualFetch: Boolean) =
flow {
if (!manualFetch && !updatePreferences.enabled().get()) return@flow
val latestRelease = client.get(
"https://api.github.com/repos/$GITHUB_REPO/releases/latest",
).body<GithubRelease>()
if (isNewVersion(latestRelease.version)) {
emit(Update.UpdateFound(latestRelease))
} else {
emit(Update.NoUpdatesFound)
}
}.flowOn(Dispatchers.IO)
sealed class Update {
data class UpdateFound(
val release: GithubRelease,
) : Update()
data object NoUpdatesFound : Update()
@Inject
class UpdateChecker(
private val updatePreferences: UpdatePreferences,
private val client: Http,
) {
suspend fun await(
manualFetch: Boolean,
onError: suspend (Throwable) -> Unit = {},
) = asFlow(manualFetch)
.catch {
onError(it)
log.warn(it) { "Failed to check for updates" }
}
.singleOrNull()
// Thanks to Tachiyomi for inspiration
private fun isNewVersion(versionTag: String): Boolean {
// Removes prefixes like "r" or "v"
val newVersion = versionTag.replace("[^\\d.]".toRegex(), "")
fun asFlow(manualFetch: Boolean) =
flow {
if (!manualFetch && !updatePreferences.enabled().get()) return@flow
val latestRelease = client.get(
"https://api.github.com/repos/$GITHUB_REPO/releases/latest",
).body<GithubRelease>()
return if (BuildKonfig.IS_PREVIEW) {
// Preview builds: based on releases in "Suwayomi/Suwayomi-JUI-preview" repo
// tagged as something like "r123"
newVersion.toInt() > BuildKonfig.PREVIEW_BUILD
if (isNewVersion(latestRelease.version)) {
emit(Update.UpdateFound(latestRelease))
} else {
// Release builds: based on releases in "Suwayomi/Suwayomi-JUI" repo
// tagged as something like "v1.1.2"
newVersion != BuildKonfig.VERSION
emit(Update.NoUpdatesFound)
}
}
}.flowOn(Dispatchers.IO)
companion object {
private val GITHUB_REPO = if (BuildKonfig.IS_PREVIEW) {
"Suwayomi/Suwayomi-JUI-preview"
} else {
"Suwayomi/Suwayomi-JUI"
}
sealed class Update {
data class UpdateFound(
val release: GithubRelease,
) : Update()
private val RELEASE_TAG: String by lazy {
if (BuildKonfig.IS_PREVIEW) {
"r${BuildKonfig.PREVIEW_BUILD}"
} else {
"v${BuildKonfig.VERSION}"
}
}
data object NoUpdatesFound : Update()
}
val RELEASE_URL = "https://github.com/$GITHUB_REPO/releases/tag/$RELEASE_TAG"
// Thanks to Tachiyomi for inspiration
private fun isNewVersion(versionTag: String): Boolean {
// Removes prefixes like "r" or "v"
val newVersion = versionTag.replace("[^\\d.]".toRegex(), "")
private val log = logging()
return if (BuildKonfig.IS_PREVIEW) {
// Preview builds: based on releases in "Suwayomi/Suwayomi-JUI-preview" repo
// tagged as something like "r123"
newVersion.toInt() > BuildKonfig.PREVIEW_BUILD
} else {
// Release builds: based on releases in "Suwayomi/Suwayomi-JUI" repo
// tagged as something like "v1.1.2"
newVersion != BuildKonfig.VERSION
}
}
companion object {
private val GITHUB_REPO = if (BuildKonfig.IS_PREVIEW) {
"Suwayomi/Suwayomi-JUI-preview"
} else {
"Suwayomi/Suwayomi-JUI"
}
private val RELEASE_TAG: String by lazy {
if (BuildKonfig.IS_PREVIEW) {
"r${BuildKonfig.PREVIEW_BUILD}"
} else {
"v${BuildKonfig.VERSION}"
}
}
val RELEASE_URL = "https://github.com/$GITHUB_REPO/releases/tag/$RELEASE_TAG"
private val log = logging()
}
}

View File

@@ -12,22 +12,21 @@ import kotlinx.coroutines.flow.collect
import me.tatarka.inject.annotations.Inject
import org.lighthousegames.logging.logging
class UpdateLibrary
@Inject
constructor(
private val updatesRepository: UpdatesRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to update library" }
}
.collect()
@Inject
class UpdateLibrary(
private val updatesRepository: UpdatesRepository,
) {
suspend fun await(onError: suspend (Throwable) -> Unit = {}) =
asFlow()
.catch {
onError(it)
log.warn(it) { "Failed to update library" }
}
.collect()
fun asFlow() = updatesRepository.updateLibrary()
fun asFlow() = updatesRepository.updateLibrary()
companion object {
private val log = logging()
}
companion object {
private val log = logging()
}
}

View File

@@ -37,54 +37,54 @@ import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import me.tatarka.inject.annotations.Inject
class UpdatesPager
@Inject
constructor(
private val getRecentUpdates: GetRecentUpdates,
private val getManga: GetManga,
private val getChapter: GetChapter,
private val serverListeners: ServerListeners,
) : CoroutineScope by CoroutineScope(Dispatchers.Default + SupervisorJob()) {
private val updatesMutex = Mutex()
@Inject
class UpdatesPager(
private val getRecentUpdates: GetRecentUpdates,
private val getManga: GetManga,
private val getChapter: GetChapter,
private val serverListeners: ServerListeners,
) : CoroutineScope by CoroutineScope(Dispatchers.Default + SupervisorJob()) {
private val updatesMutex = Mutex()
private val fetchedUpdates = MutableSharedFlow<List<MangaAndChapter>>()
private val foldedUpdates = fetchedUpdates.runningFold(emptyList<Updates>()) { updates, newUpdates ->
updates.ifEmpty {
val first = newUpdates.firstOrNull()?.chapter ?: return@runningFold updates
listOf(
Updates.Date(
Instant.fromEpochSeconds(first.fetchedAt)
.toLocalDateTime(TimeZone.currentSystemDefault())
.date,
),
)
} + newUpdates.fold(emptyList()) { list, (manga, chapter) ->
val date = (list.lastOrNull() as? Updates.Update)?.let {
val lastUpdateDate = Instant.fromEpochSeconds(it.chapter.fetchedAt)
private val fetchedUpdates = MutableSharedFlow<List<MangaAndChapter>>()
private val foldedUpdates = fetchedUpdates.runningFold(emptyList<Updates>()) { updates, newUpdates ->
updates.ifEmpty {
val first = newUpdates.firstOrNull()?.chapter ?: return@runningFold updates
listOf(
Updates.Date(
Instant.fromEpochSeconds(first.fetchedAt)
.toLocalDateTime(TimeZone.currentSystemDefault())
.date
val chapterDate = Instant.fromEpochSeconds(chapter.fetchedAt)
.toLocalDateTime(TimeZone.currentSystemDefault())
.date
chapterDate.takeUnless { it == lastUpdateDate }
}
if (date == null) {
list + Updates.Update(manga, chapter)
} else {
list + Updates.Date(date) + Updates.Update(manga, chapter)
}
.date,
),
)
} + newUpdates.fold(emptyList()) { list, (manga, chapter) ->
val date = (list.lastOrNull() as? Updates.Update)?.let {
val lastUpdateDate = Instant.fromEpochSeconds(it.chapter.fetchedAt)
.toLocalDateTime(TimeZone.currentSystemDefault())
.date
val chapterDate = Instant.fromEpochSeconds(chapter.fetchedAt)
.toLocalDateTime(TimeZone.currentSystemDefault())
.date
chapterDate.takeUnless { it == lastUpdateDate }
}
}.stateIn(this, SharingStarted.Eagerly, emptyList())
private val mangaIds = foldedUpdates.map { updates ->
updates.filterIsInstance<Updates.Update>().map { it.manga.id }
}.stateIn(this, SharingStarted.Eagerly, emptyList())
private val chapterIds = foldedUpdates.map { updates ->
updates.filterIsInstance<Updates.Update>().map { it.chapter.id }
}.stateIn(this, SharingStarted.Eagerly, emptyList())
if (date == null) {
list + Updates.Update(manga, chapter)
} else {
list + Updates.Date(date) + Updates.Update(manga, chapter)
}
}
}.stateIn(this, SharingStarted.Eagerly, emptyList())
private val changedManga = serverListeners.mangaListener.runningFold(emptyMap<Long, Manga>()) { manga, updatedMangaIds ->
private val mangaIds = foldedUpdates.map { updates ->
updates.filterIsInstance<Updates.Update>().map { it.manga.id }
}.stateIn(this, SharingStarted.Eagerly, emptyList())
private val chapterIds = foldedUpdates.map { updates ->
updates.filterIsInstance<Updates.Update>().map { it.chapter.id }
}.stateIn(this, SharingStarted.Eagerly, emptyList())
private val changedManga =
serverListeners.mangaListener.runningFold(emptyMap<Long, Manga>()) { manga, updatedMangaIds ->
coroutineScope {
manga + updatedMangaIds.filter { it in mangaIds.value }.map {
async {
@@ -94,82 +94,82 @@ class UpdatesPager
}
}.stateIn(this, SharingStarted.Eagerly, emptyMap())
private val changedChapters = MutableStateFlow(emptyMap<Long, Chapter>())
private val changedChapters = MutableStateFlow(emptyMap<Long, Chapter>())
init {
serverListeners.chapterIdsListener
.onEach { updatedChapterIds ->
val chapters = coroutineScope {
updatedChapterIds.mapNotNull { id -> chapterIds.value.find { it == id } }.map {
async {
getChapter.await(it)
}
}.awaitAll().filterNotNull().associateBy { it.id }
}
changedChapters.update { it + chapters }
}
.launchIn(this)
}
val updates = combine(
foldedUpdates,
changedManga,
changedChapters,
) { updates, changedManga, changedChapters ->
updates.map {
when (it) {
is Updates.Date -> it
is Updates.Update -> it.copy(
manga = changedManga[it.manga.id] ?: it.manga,
chapter = changedChapters[it.chapter.id] ?: it.chapter,
)
init {
serverListeners.chapterIdsListener
.onEach { updatedChapterIds ->
val chapters = coroutineScope {
updatedChapterIds.mapNotNull { id -> chapterIds.value.find { it == id } }.map {
async {
getChapter.await(it)
}
}.awaitAll().filterNotNull().associateBy { it.id }
}
changedChapters.update { it + chapters }
}
}.stateIn(this, SharingStarted.Eagerly, emptyList())
.launchIn(this)
}
private val currentPage = MutableStateFlow(0)
private val hasNextPage = MutableStateFlow(true)
val updates = combine(
foldedUpdates,
changedManga,
changedChapters,
) { updates, changedManga, changedChapters ->
updates.map {
when (it) {
is Updates.Date -> it
is Updates.Update -> it.copy(
manga = changedManga[it.manga.id] ?: it.manga,
chapter = changedChapters[it.chapter.id] ?: it.chapter,
)
}
}
}.stateIn(this, SharingStarted.Eagerly, emptyList())
private val currentPage = MutableStateFlow(0)
private val hasNextPage = MutableStateFlow(true)
@Immutable
sealed class Updates {
@Immutable
data class Update(
val manga: Manga,
val chapter: Chapter,
) : Updates()
@Immutable
sealed class Updates {
@Immutable
data class Update(
val manga: Manga,
val chapter: Chapter,
) : Updates()
@Immutable
data class Date(
val date: String,
) : Updates() {
constructor(date: LocalDate) : this(date.toString())
}
}
fun loadNextPage(
onComplete: (() -> Unit)? = null,
onError: suspend (Throwable) -> Unit,
) {
launch {
if (hasNextPage.value && updatesMutex.tryLock()) {
currentPage.value++
if (!getUpdates(currentPage.value, onError)) {
currentPage.value--
}
updatesMutex.unlock()
}
onComplete?.invoke()
}
}
private suspend fun getUpdates(
page: Int,
onError: suspend (Throwable) -> Unit,
): Boolean {
val updates = getRecentUpdates.await(page, onError) ?: return false
hasNextPage.value = updates.hasNextPage
fetchedUpdates.emit(updates.page)
return true
data class Date(
val date: String,
) : Updates() {
constructor(date: LocalDate) : this(date.toString())
}
}
fun loadNextPage(
onComplete: (() -> Unit)? = null,
onError: suspend (Throwable) -> Unit,
) {
launch {
if (hasNextPage.value && updatesMutex.tryLock()) {
currentPage.value++
if (!getUpdates(currentPage.value, onError)) {
currentPage.value--
}
updatesMutex.unlock()
}
onComplete?.invoke()
}
}
private suspend fun getUpdates(
page: Int,
onError: suspend (Throwable) -> Unit,
): Boolean {
val updates = getRecentUpdates.await(page, onError) ?: return false
hasNextPage.value = updates.hasNextPage
fetchedUpdates.emit(updates.page)
return true
}
}

View File

@@ -38,169 +38,168 @@ import kotlin.io.path.exists
import kotlin.io.path.isExecutable
@OptIn(DelicateCoroutinesApi::class)
class ServerService
@Inject
constructor(
private val serverHostPreferences: ServerHostPreferences,
) {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
@Inject
class ServerService(
private val serverHostPreferences: ServerHostPreferences,
) {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private val host = serverHostPreferences.host().stateIn(GlobalScope)
private val _initialized = MutableStateFlow(
if (host.value) {
ServerResult.STARTING
} else {
ServerResult.UNUSED
},
)
val initialized = _initialized.asStateFlow()
private var process: Process? = null
private val host = serverHostPreferences.host().stateIn(GlobalScope)
private val _initialized = MutableStateFlow(
if (host.value) {
ServerResult.STARTING
} else {
ServerResult.UNUSED
},
)
val initialized = _initialized.asStateFlow()
private var process: Process? = null
fun startAnyway() {
_initialized.value = ServerResult.UNUSED
}
fun startAnyway() {
_initialized.value = ServerResult.UNUSED
}
@Throws(IOException::class)
private suspend fun copyJar(jarFile: Path) {
javaClass.getResourceAsStream("/Tachidesk.jar")?.source()
?.copyTo(FileSystem.SYSTEM.sink(jarFile).buffer())
}
@Throws(IOException::class)
private suspend fun copyJar(jarFile: Path) {
javaClass.getResourceAsStream("/Tachidesk.jar")?.source()
?.copyTo(FileSystem.SYSTEM.sink(jarFile).buffer())
}
private fun getJavaFromPath(javaPath: Path): String? {
val javaExeFile = javaPath.resolve("java.exe").toNioPath()
val javaUnixFile = javaPath.resolve("java").toNioPath()
return when {
javaExeFile.exists() && javaExeFile.isExecutable() -> javaExeFile.absolutePathString()
javaUnixFile.exists() && javaUnixFile.isExecutable() -> javaUnixFile.absolutePathString()
else -> null
}
}
private fun getRuntimeJava(): String? = System.getProperty("java.home")?.let { getJavaFromPath(it.toPath().resolve("bin")) }
private fun getPossibleJava(): String? =
System.getProperty("java.library.path")?.split(pathSeparatorChar)
.orEmpty()
.asSequence()
.mapNotNull {
val file = it.toPath()
if (file.toString().contains("java") || file.toString().contains("jdk")) {
if (file.name.equals("bin", true)) {
file
} else {
file.resolve("bin")
}
} else {
null
}
}
.mapNotNull { getJavaFromPath(it) }
.firstOrNull()
private suspend fun runService() {
process?.destroy()
withIOContext {
process?.waitFor()
}
_initialized.value = if (host.value) {
ServerResult.STARTING
} else {
ServerResult.UNUSED
return
}
val jarFile = userDataDir / "Tachidesk.jar"
if (!FileSystem.SYSTEM.exists(jarFile)) {
log.info { "Copying server to resources" }
withIOContext { copyJar(jarFile) }
} else {
try {
val jarVersion = withIOContext {
JarInputStream(FileSystem.SYSTEM.source(jarFile).buffer().inputStream()).use { jar ->
jar.manifest?.mainAttributes?.getValue("JUI-KEY")?.toIntOrNull()
}
}
if (jarVersion != BuildKonfig.SERVER_CODE) {
log.info { "Updating server file from resources" }
withIOContext { copyJar(jarFile) }
}
} catch (e: IOException) {
log.error(e) {
"Error accessing server jar, cannot update server, ${BuildKonfig.NAME} may not work properly"
}
}
}
val javaPath = getRuntimeJava() ?: getPossibleJava() ?: "java"
log.info { "Starting server with $javaPath" }
val properties = serverHostPreferences.properties()
log.info { "Using server properties:\n" + properties.joinToString(separator = "\n") }
withIOContext {
val reader: Reader
process = ProcessBuilder(javaPath, *properties, "-jar", jarFile.toString())
.redirectErrorStream(true)
.start()
.also {
reader = it.inputStream.reader()
}
log.info { "Server started successfully" }
val log = logging("Server")
reader.forEachLine {
if (_initialized.value == ServerResult.STARTING) {
when {
it.contains("Javalin started") ->
_initialized.value = ServerResult.STARTED
it.contains("Javalin has stopped") ->
_initialized.value = ServerResult.FAILED
}
}
log.info { it }
}
if (_initialized.value == ServerResult.STARTING) {
_initialized.value = ServerResult.FAILED
}
log.info { "Server closed" }
val exitVal = process?.waitFor()
log.info { "Process exitValue: $exitVal" }
process = null
}
}
fun startServer() {
scope.coroutineContext.cancelChildren()
host
.mapLatest {
runService()
}
.catch {
log.error(it) { "Error launching Tachidesk.jar" }
if (_initialized.value == ServerResult.STARTING || _initialized.value == ServerResult.STARTED) {
_initialized.value = ServerResult.FAILED
}
}
.launchIn(scope)
}
init {
Runtime.getRuntime().addShutdownHook(
thread(start = false) {
process?.destroy()
process = null
},
)
}
enum class ServerResult {
UNUSED,
STARTING,
STARTED,
FAILED,
}
private companion object {
private val log = logging()
private fun getJavaFromPath(javaPath: Path): String? {
val javaExeFile = javaPath.resolve("java.exe").toNioPath()
val javaUnixFile = javaPath.resolve("java").toNioPath()
return when {
javaExeFile.exists() && javaExeFile.isExecutable() -> javaExeFile.absolutePathString()
javaUnixFile.exists() && javaUnixFile.isExecutable() -> javaUnixFile.absolutePathString()
else -> null
}
}
private fun getRuntimeJava(): String? = System.getProperty("java.home")?.let { getJavaFromPath(it.toPath().resolve("bin")) }
private fun getPossibleJava(): String? =
System.getProperty("java.library.path")?.split(pathSeparatorChar)
.orEmpty()
.asSequence()
.mapNotNull {
val file = it.toPath()
if (file.toString().contains("java") || file.toString().contains("jdk")) {
if (file.name.equals("bin", true)) {
file
} else {
file.resolve("bin")
}
} else {
null
}
}
.mapNotNull { getJavaFromPath(it) }
.firstOrNull()
private suspend fun runService() {
process?.destroy()
withIOContext {
process?.waitFor()
}
_initialized.value = if (host.value) {
ServerResult.STARTING
} else {
ServerResult.UNUSED
return
}
val jarFile = userDataDir / "Tachidesk.jar"
if (!FileSystem.SYSTEM.exists(jarFile)) {
log.info { "Copying server to resources" }
withIOContext { copyJar(jarFile) }
} else {
try {
val jarVersion = withIOContext {
JarInputStream(FileSystem.SYSTEM.source(jarFile).buffer().inputStream()).use { jar ->
jar.manifest?.mainAttributes?.getValue("JUI-KEY")?.toIntOrNull()
}
}
if (jarVersion != BuildKonfig.SERVER_CODE) {
log.info { "Updating server file from resources" }
withIOContext { copyJar(jarFile) }
}
} catch (e: IOException) {
log.error(e) {
"Error accessing server jar, cannot update server, ${BuildKonfig.NAME} may not work properly"
}
}
}
val javaPath = getRuntimeJava() ?: getPossibleJava() ?: "java"
log.info { "Starting server with $javaPath" }
val properties = serverHostPreferences.properties()
log.info { "Using server properties:\n" + properties.joinToString(separator = "\n") }
withIOContext {
val reader: Reader
process = ProcessBuilder(javaPath, *properties, "-jar", jarFile.toString())
.redirectErrorStream(true)
.start()
.also {
reader = it.inputStream.reader()
}
log.info { "Server started successfully" }
val log = logging("Server")
reader.forEachLine {
if (_initialized.value == ServerResult.STARTING) {
when {
it.contains("Javalin started") ->
_initialized.value = ServerResult.STARTED
it.contains("Javalin has stopped") ->
_initialized.value = ServerResult.FAILED
}
}
log.info { it }
}
if (_initialized.value == ServerResult.STARTING) {
_initialized.value = ServerResult.FAILED
}
log.info { "Server closed" }
val exitVal = process?.waitFor()
log.info { "Process exitValue: $exitVal" }
process = null
}
}
fun startServer() {
scope.coroutineContext.cancelChildren()
host
.mapLatest {
runService()
}
.catch {
log.error(it) { "Error launching Tachidesk.jar" }
if (_initialized.value == ServerResult.STARTING || _initialized.value == ServerResult.STARTED) {
_initialized.value = ServerResult.FAILED
}
}
.launchIn(scope)
}
init {
Runtime.getRuntime().addShutdownHook(
thread(start = false) {
process?.destroy()
process = null
},
)
}
enum class ServerResult {
UNUSED,
STARTING,
STARTED,
FAILED,
}
private companion object {
private val log = logging()
}
}