mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 06:42:05 +01:00
Update Ktor to v2.0.0
This commit is contained in:
@@ -57,7 +57,8 @@ dependencies {
|
||||
// Http client
|
||||
implementation(libs.ktor.core)
|
||||
implementation(libs.ktor.okHttp)
|
||||
implementation(libs.ktor.serialization)
|
||||
implementation(libs.ktor.contentNegotiation)
|
||||
implementation(libs.ktor.serialization.json)
|
||||
implementation(libs.ktor.logging)
|
||||
implementation(libs.ktor.websockets)
|
||||
implementation(libs.ktor.auth)
|
||||
|
||||
@@ -30,9 +30,9 @@ import ca.gosyer.jui.data.server.requests.downloadsQuery
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import dev.icerock.moko.resources.desc.desc
|
||||
import dev.icerock.moko.resources.format
|
||||
import io.ktor.client.features.websocket.ws
|
||||
import io.ktor.http.cio.websocket.Frame
|
||||
import io.ktor.http.cio.websocket.readText
|
||||
import io.ktor.client.plugins.websocket.ws
|
||||
import io.ktor.websocket.Frame
|
||||
import io.ktor.websocket.readText
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
@@ -39,7 +39,8 @@ kotlin {
|
||||
api(libs.serialization.json)
|
||||
api(libs.kotlinInject.runtime)
|
||||
api(libs.ktor.core)
|
||||
api(libs.ktor.serialization)
|
||||
api(libs.ktor.contentNegotiation)
|
||||
api(libs.ktor.serialization.json)
|
||||
api(libs.okio)
|
||||
api(libs.logging.kmlogging)
|
||||
api(libs.multiplatformSettings.core)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ca.gosyer.jui.core.io
|
||||
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.client.statement.discardRemaining
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.http.isSuccess
|
||||
|
||||
suspend fun HttpResponse.asSuccess() : HttpResponse = apply {
|
||||
if (!status.isSuccess()) {
|
||||
discardRemaining()
|
||||
throw HttpException(status)
|
||||
}
|
||||
}
|
||||
|
||||
class HttpException(val status: HttpStatusCode) : IllegalStateException("HTTP error $status")
|
||||
@@ -38,7 +38,8 @@ kotlin {
|
||||
api(libs.serialization.json)
|
||||
api(libs.kotlinInject.runtime)
|
||||
api(libs.ktor.core)
|
||||
api(libs.ktor.serialization)
|
||||
api(libs.ktor.contentNegotiation)
|
||||
api(libs.ktor.serialization.json)
|
||||
api(libs.ktor.auth)
|
||||
api(libs.ktor.logging)
|
||||
api(libs.ktor.websockets)
|
||||
|
||||
@@ -10,8 +10,8 @@ import ca.gosyer.jui.core.lang.throwIfCancellation
|
||||
import ca.gosyer.jui.data.build.BuildKonfig
|
||||
import ca.gosyer.jui.data.server.Http
|
||||
import ca.gosyer.jui.data.server.ServerPreferences
|
||||
import io.ktor.client.features.websocket.ws
|
||||
import io.ktor.http.cio.websocket.Frame
|
||||
import io.ktor.client.plugins.websocket.ws
|
||||
import io.ktor.websocket.Frame
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
|
||||
@@ -13,15 +13,13 @@ import ca.gosyer.jui.data.download.model.DownloaderStatus
|
||||
import ca.gosyer.jui.data.server.Http
|
||||
import ca.gosyer.jui.data.server.ServerPreferences
|
||||
import ca.gosyer.jui.data.server.requests.downloadsQuery
|
||||
import io.ktor.http.cio.websocket.Frame
|
||||
import io.ktor.http.cio.websocket.readText
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
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
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
class DownloadService @Inject constructor(
|
||||
serverPreferences: ServerPreferences,
|
||||
client: Http
|
||||
|
||||
@@ -11,15 +11,13 @@ import ca.gosyer.jui.data.library.model.UpdateStatus
|
||||
import ca.gosyer.jui.data.server.Http
|
||||
import ca.gosyer.jui.data.server.ServerPreferences
|
||||
import ca.gosyer.jui.data.server.requests.updatesQuery
|
||||
import io.ktor.http.cio.websocket.Frame
|
||||
import io.ktor.http.cio.websocket.readText
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import io.ktor.websocket.Frame
|
||||
import io.ktor.websocket.readText
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
import org.lighthousegames.logging.logging
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
class LibraryUpdateService @Inject constructor(
|
||||
serverPreferences: ServerPreferences,
|
||||
client: Http
|
||||
|
||||
@@ -11,19 +11,21 @@ import ca.gosyer.jui.data.server.model.Auth
|
||||
import ca.gosyer.jui.data.server.model.Proxy
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.ProxyBuilder
|
||||
import io.ktor.client.features.auth.providers.BasicAuthCredentials
|
||||
import io.ktor.client.features.auth.providers.DigestAuthCredentials
|
||||
import io.ktor.client.features.auth.providers.basic
|
||||
import io.ktor.client.features.auth.providers.digest
|
||||
import io.ktor.client.features.json.JsonFeature
|
||||
import io.ktor.client.features.json.serializer.KotlinxSerializer
|
||||
import io.ktor.client.features.logging.LogLevel
|
||||
import io.ktor.client.features.logging.Logging
|
||||
import io.ktor.client.features.websocket.WebSockets
|
||||
import io.ktor.client.plugins.auth.providers.BasicAuthCredentials
|
||||
import io.ktor.client.plugins.auth.providers.DigestAuthCredentials
|
||||
import io.ktor.client.plugins.auth.providers.basic
|
||||
import io.ktor.client.plugins.auth.providers.digest
|
||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.client.plugins.logging.LogLevel
|
||||
import io.ktor.client.plugins.logging.Logger
|
||||
import io.ktor.client.plugins.logging.Logging
|
||||
import io.ktor.client.plugins.websocket.WebSockets
|
||||
import io.ktor.http.URLBuilder
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import kotlinx.serialization.json.Json
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
import io.ktor.client.features.auth.Auth as AuthFeature
|
||||
import org.lighthousegames.logging.logging
|
||||
import io.ktor.client.plugins.auth.Auth as AuthPlugin
|
||||
|
||||
typealias Http = HttpClient
|
||||
|
||||
@@ -47,7 +49,7 @@ class HttpProvider @Inject constructor() {
|
||||
}
|
||||
when (serverPreferences.auth().get()) {
|
||||
Auth.NONE -> Unit
|
||||
Auth.BASIC -> install(AuthFeature) {
|
||||
Auth.BASIC -> AuthPlugin {
|
||||
basic {
|
||||
credentials {
|
||||
BasicAuthCredentials(
|
||||
@@ -57,7 +59,7 @@ class HttpProvider @Inject constructor() {
|
||||
}
|
||||
}
|
||||
}
|
||||
Auth.DIGEST -> install(AuthFeature) {
|
||||
Auth.DIGEST -> AuthPlugin {
|
||||
digest {
|
||||
credentials {
|
||||
DigestAuthCredentials(
|
||||
@@ -68,8 +70,8 @@ class HttpProvider @Inject constructor() {
|
||||
}
|
||||
}
|
||||
}
|
||||
install(JsonFeature) {
|
||||
serializer = KotlinxSerializer(
|
||||
install(ContentNegotiation) {
|
||||
json(
|
||||
Json {
|
||||
isLenient = false
|
||||
ignoreUnknownKeys = true
|
||||
@@ -85,6 +87,12 @@ class HttpProvider @Inject constructor() {
|
||||
} else {
|
||||
LogLevel.INFO
|
||||
}
|
||||
logger = object : Logger {
|
||||
val log = logging("HttpClient")
|
||||
override fun log(message: String) {
|
||||
log.info { message }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import ca.gosyer.jui.core.prefs.Preference
|
||||
import ca.gosyer.jui.core.prefs.getAsFlow
|
||||
import io.ktor.http.URLBuilder
|
||||
import io.ktor.http.Url
|
||||
import io.ktor.http.path
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
package ca.gosyer.jui.data.server.interactions
|
||||
|
||||
import ca.gosyer.jui.core.io.SYSTEM
|
||||
import ca.gosyer.jui.core.io.asSuccess
|
||||
import ca.gosyer.jui.core.lang.IO
|
||||
import ca.gosyer.jui.data.models.BackupValidationResult
|
||||
import ca.gosyer.jui.data.server.Http
|
||||
@@ -14,11 +15,11 @@ import ca.gosyer.jui.data.server.ServerPreferences
|
||||
import ca.gosyer.jui.data.server.requests.backupFileExportRequest
|
||||
import ca.gosyer.jui.data.server.requests.backupFileImportRequest
|
||||
import ca.gosyer.jui.data.server.requests.validateBackupFileRequest
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.HttpRequestBuilder
|
||||
import io.ktor.client.request.forms.formData
|
||||
import io.ktor.client.request.forms.submitFormWithBinaryData
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.Headers
|
||||
import io.ktor.http.HttpHeaders
|
||||
@@ -36,7 +37,7 @@ class BackupInteractionHandler @Inject constructor(
|
||||
) : BaseInteractionHandler(client, serverPreferences) {
|
||||
|
||||
fun importBackupFile(file: Path, block: HttpRequestBuilder.() -> Unit = {}) = flow {
|
||||
val response = client.submitFormWithBinaryData<HttpResponse>(
|
||||
val response = client.submitFormWithBinaryData(
|
||||
serverUrl + backupFileImportRequest(),
|
||||
formData = formData {
|
||||
append(
|
||||
@@ -48,12 +49,12 @@ class BackupInteractionHandler @Inject constructor(
|
||||
)
|
||||
},
|
||||
block = block
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun validateBackupFile(file: Path, block: HttpRequestBuilder.() -> Unit = {}) = flow {
|
||||
val response = client.submitFormWithBinaryData<BackupValidationResult>(
|
||||
val response = client.submitFormWithBinaryData(
|
||||
serverUrl + validateBackupFileRequest(),
|
||||
formData = formData {
|
||||
append(
|
||||
@@ -65,15 +66,15 @@ class BackupInteractionHandler @Inject constructor(
|
||||
)
|
||||
},
|
||||
block = block
|
||||
)
|
||||
).asSuccess().body<BackupValidationResult>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun exportBackupFile(block: HttpRequestBuilder.() -> Unit = {}) = flow {
|
||||
val response = client.get<HttpResponse>(
|
||||
val response = client.get(
|
||||
serverUrl + backupFileExportRequest(),
|
||||
block
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
package ca.gosyer.jui.data.server.interactions
|
||||
|
||||
import ca.gosyer.jui.core.io.asSuccess
|
||||
import ca.gosyer.jui.core.lang.IO
|
||||
import ca.gosyer.jui.data.models.Category
|
||||
import ca.gosyer.jui.data.models.Manga
|
||||
@@ -20,10 +21,10 @@ import ca.gosyer.jui.data.server.requests.getCategoriesQuery
|
||||
import ca.gosyer.jui.data.server.requests.getMangaCategoriesQuery
|
||||
import ca.gosyer.jui.data.server.requests.getMangaInCategoryQuery
|
||||
import ca.gosyer.jui.data.server.requests.removeMangaFromCategoryRequest
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.delete
|
||||
import io.ktor.client.request.forms.submitForm
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.Parameters
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -37,18 +38,18 @@ class CategoryInteractionHandler @Inject constructor(
|
||||
) : BaseInteractionHandler(client, serverPreferences) {
|
||||
|
||||
fun getMangaCategories(mangaId: Long) = flow {
|
||||
val response = client.get<List<Category>>(
|
||||
val response = client.get(
|
||||
serverUrl + getMangaCategoriesQuery(mangaId)
|
||||
)
|
||||
).asSuccess().body<List<Category>>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun getMangaCategories(manga: Manga) = getMangaCategories(manga.id)
|
||||
|
||||
fun addMangaToCategory(mangaId: Long, categoryId: Long) = flow {
|
||||
val response = client.get<HttpResponse>(
|
||||
val response = client.get(
|
||||
serverUrl + addMangaToCategoryQuery(mangaId, categoryId)
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
fun addMangaToCategory(manga: Manga, category: Category) = addMangaToCategory(manga.id, category.id)
|
||||
@@ -56,9 +57,9 @@ class CategoryInteractionHandler @Inject constructor(
|
||||
fun addMangaToCategory(mangaId: Long, category: Category) = addMangaToCategory(mangaId, category.id)
|
||||
|
||||
fun removeMangaFromCategory(mangaId: Long, categoryId: Long) = flow {
|
||||
val response = client.delete<HttpResponse>(
|
||||
val response = client.delete(
|
||||
serverUrl + removeMangaFromCategoryRequest(mangaId, categoryId)
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
fun removeMangaFromCategory(manga: Manga, category: Category) = removeMangaFromCategory(manga.id, category.id)
|
||||
@@ -66,9 +67,9 @@ class CategoryInteractionHandler @Inject constructor(
|
||||
fun removeMangaFromCategory(mangaId: Long, category: Category) = removeMangaFromCategory(mangaId, category.id)
|
||||
|
||||
fun getCategories(dropDefault: Boolean = false) = flow {
|
||||
val response = client.get<List<Category>>(
|
||||
val response = client.get(
|
||||
serverUrl + getCategoriesQuery()
|
||||
).let { categories ->
|
||||
).asSuccess().body<List<Category>>().let { categories ->
|
||||
if (dropDefault) {
|
||||
categories.filterNot { it.name.equals("default", true) }
|
||||
} else categories
|
||||
@@ -77,17 +78,17 @@ class CategoryInteractionHandler @Inject constructor(
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun createCategory(name: String) = flow {
|
||||
val response = client.submitForm<HttpResponse>(
|
||||
val response = client.submitForm(
|
||||
serverUrl + createCategoryRequest(),
|
||||
formParameters = Parameters.build {
|
||||
append("name", name)
|
||||
}
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun modifyCategory(categoryId: Long, name: String? = null, isLanding: Boolean? = null) = flow {
|
||||
val response = client.submitForm<HttpResponse>(
|
||||
val response = client.submitForm(
|
||||
serverUrl + categoryModifyRequest(categoryId),
|
||||
formParameters = Parameters.build {
|
||||
if (name != null) {
|
||||
@@ -99,13 +100,13 @@ class CategoryInteractionHandler @Inject constructor(
|
||||
}
|
||||
) {
|
||||
method = HttpMethod.Patch
|
||||
}
|
||||
}.asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
fun modifyCategory(category: Category, name: String? = null, isLanding: Boolean? = null) = modifyCategory(category.id, name, isLanding)
|
||||
|
||||
fun reorderCategory(to: Int, from: Int) = flow {
|
||||
val response = client.submitForm<HttpResponse>(
|
||||
val response = client.submitForm(
|
||||
serverUrl + categoryReorderRequest(),
|
||||
formParameters = Parameters.build {
|
||||
append("to", to.toString())
|
||||
@@ -113,22 +114,22 @@ class CategoryInteractionHandler @Inject constructor(
|
||||
}
|
||||
) {
|
||||
method = HttpMethod.Patch
|
||||
}
|
||||
}.asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun deleteCategory(categoryId: Long) = flow {
|
||||
val response = client.delete<HttpResponse>(
|
||||
val response = client.delete(
|
||||
serverUrl + categoryDeleteRequest(categoryId)
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
fun deleteCategory(category: Category) = deleteCategory(category.id)
|
||||
|
||||
fun getMangaFromCategory(categoryId: Long) = flow {
|
||||
val response = client.get<List<Manga>>(
|
||||
val response = client.get(
|
||||
serverUrl + getMangaInCategoryQuery(categoryId)
|
||||
)
|
||||
).asSuccess().body<List<Manga>>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
fun getMangaFromCategory(category: Category) = getMangaFromCategory(category.id)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
package ca.gosyer.jui.data.server.interactions
|
||||
|
||||
import ca.gosyer.jui.core.io.asSuccess
|
||||
import ca.gosyer.jui.core.lang.IO
|
||||
import ca.gosyer.jui.data.models.Chapter
|
||||
import ca.gosyer.jui.data.models.Manga
|
||||
@@ -19,15 +20,15 @@ import ca.gosyer.jui.data.server.requests.queueDownloadChapterRequest
|
||||
import ca.gosyer.jui.data.server.requests.stopDownloadingChapterRequest
|
||||
import ca.gosyer.jui.data.server.requests.updateChapterMetaRequest
|
||||
import ca.gosyer.jui.data.server.requests.updateChapterRequest
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.HttpRequestBuilder
|
||||
import io.ktor.client.request.delete
|
||||
import io.ktor.client.request.forms.submitForm
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.parameter
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.client.statement.bodyAsChannel
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.Parameters
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
@@ -39,7 +40,7 @@ class ChapterInteractionHandler @Inject constructor(
|
||||
) : BaseInteractionHandler(client, serverPreferences) {
|
||||
|
||||
fun getChapters(mangaId: Long, refresh: Boolean = false) = flow {
|
||||
val response = client.get<List<Chapter>>(
|
||||
val response = client.get(
|
||||
serverUrl + getMangaChaptersQuery(mangaId)
|
||||
) {
|
||||
url {
|
||||
@@ -47,16 +48,16 @@ class ChapterInteractionHandler @Inject constructor(
|
||||
parameter("onlineFetch", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.asSuccess().body<List<Chapter>>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun getChapters(manga: Manga, refresh: Boolean = false) = getChapters(manga.id, refresh)
|
||||
|
||||
fun getChapter(mangaId: Long, chapterIndex: Int) = flow {
|
||||
val response = client.get<Chapter>(
|
||||
val response = client.get(
|
||||
serverUrl + getChapterQuery(mangaId, chapterIndex)
|
||||
)
|
||||
).asSuccess().body<Chapter>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
@@ -74,7 +75,7 @@ class ChapterInteractionHandler @Inject constructor(
|
||||
lastPageRead: Int? = null,
|
||||
markPreviousRead: Boolean? = null
|
||||
) = flow {
|
||||
val response = client.submitForm<HttpResponse>(
|
||||
val response = client.submitForm(
|
||||
serverUrl + updateChapterRequest(mangaId, chapterIndex),
|
||||
formParameters = Parameters.build {
|
||||
if (read != null) {
|
||||
@@ -92,7 +93,7 @@ class ChapterInteractionHandler @Inject constructor(
|
||||
}
|
||||
) {
|
||||
method = HttpMethod.Patch
|
||||
}
|
||||
}.asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
@@ -129,10 +130,10 @@ class ChapterInteractionHandler @Inject constructor(
|
||||
)
|
||||
|
||||
fun getPage(mangaId: Long, chapterIndex: Int, pageNum: Int, block: HttpRequestBuilder.() -> Unit) = flow {
|
||||
val response = client.get<ByteReadChannel>(
|
||||
val response = client.get(
|
||||
serverUrl + getPageQuery(mangaId, chapterIndex, pageNum),
|
||||
block
|
||||
)
|
||||
).asSuccess().bodyAsChannel()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
@@ -143,9 +144,9 @@ class ChapterInteractionHandler @Inject constructor(
|
||||
fun getPage(manga: Manga, chapter: Chapter, pageNum: Int, block: HttpRequestBuilder.() -> Unit) = getPage(manga.id, chapter.index, pageNum, block)
|
||||
|
||||
fun deleteChapterDownload(mangaId: Long, chapterIndex: Int) = flow {
|
||||
val response = client.delete<HttpResponse>(
|
||||
val response = client.delete(
|
||||
serverUrl + deleteDownloadedChapterRequest(mangaId, chapterIndex)
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
@@ -156,9 +157,9 @@ class ChapterInteractionHandler @Inject constructor(
|
||||
fun deleteChapterDownload(manga: Manga, chapter: Chapter) = deleteChapterDownload(manga.id, chapter.index)
|
||||
|
||||
fun queueChapterDownload(mangaId: Long, chapterIndex: Int) = flow {
|
||||
val response = client.get<HttpResponse>(
|
||||
val response = client.get(
|
||||
serverUrl + queueDownloadChapterRequest(mangaId, chapterIndex)
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
@@ -169,9 +170,9 @@ class ChapterInteractionHandler @Inject constructor(
|
||||
fun queueChapterDownload(manga: Manga, chapter: Chapter) = queueChapterDownload(manga.id, chapter.index)
|
||||
|
||||
fun stopChapterDownload(mangaId: Long, chapterIndex: Int) = flow {
|
||||
val response = client.delete<HttpResponse>(
|
||||
val response = client.delete(
|
||||
serverUrl + stopDownloadingChapterRequest(mangaId, chapterIndex)
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
@@ -182,7 +183,7 @@ class ChapterInteractionHandler @Inject constructor(
|
||||
fun stopChapterDownload(manga: Manga, chapter: Chapter) = stopChapterDownload(manga.id, chapter.index)
|
||||
|
||||
fun updateChapterMeta(mangaId: Long, chapterIndex: Int, key: String, value: String) = flow {
|
||||
val response = client.submitForm<HttpResponse>(
|
||||
val response = client.submitForm(
|
||||
serverUrl + updateChapterMetaRequest(mangaId, chapterIndex),
|
||||
formParameters = Parameters.build {
|
||||
append("key", key)
|
||||
@@ -190,7 +191,7 @@ class ChapterInteractionHandler @Inject constructor(
|
||||
}
|
||||
) {
|
||||
method = HttpMethod.Patch
|
||||
}
|
||||
}.asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
package ca.gosyer.jui.data.server.interactions
|
||||
|
||||
import ca.gosyer.jui.core.io.asSuccess
|
||||
import ca.gosyer.jui.core.lang.IO
|
||||
import ca.gosyer.jui.data.server.Http
|
||||
import ca.gosyer.jui.data.server.ServerPreferences
|
||||
@@ -13,7 +14,6 @@ import ca.gosyer.jui.data.server.requests.downloadsClearRequest
|
||||
import ca.gosyer.jui.data.server.requests.downloadsStartRequest
|
||||
import ca.gosyer.jui.data.server.requests.downloadsStopRequest
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
@@ -25,23 +25,23 @@ class DownloadInteractionHandler @Inject constructor(
|
||||
) : BaseInteractionHandler(client, serverPreferences) {
|
||||
|
||||
fun startDownloading() = flow {
|
||||
val response = client.get<HttpResponse>(
|
||||
val response = client.get(
|
||||
serverUrl + downloadsStartRequest()
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun stopDownloading() = flow {
|
||||
val response = client.get<HttpResponse>(
|
||||
val response = client.get(
|
||||
serverUrl + downloadsStopRequest()
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun clearDownloadQueue() = flow {
|
||||
val response = client.get<HttpResponse>(
|
||||
val response = client.get(
|
||||
serverUrl + downloadsClearRequest()
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
package ca.gosyer.jui.data.server.interactions
|
||||
|
||||
import ca.gosyer.jui.core.io.asSuccess
|
||||
import ca.gosyer.jui.core.lang.IO
|
||||
import ca.gosyer.jui.data.models.Extension
|
||||
import ca.gosyer.jui.data.server.Http
|
||||
@@ -15,10 +16,10 @@ import ca.gosyer.jui.data.server.requests.apkInstallQuery
|
||||
import ca.gosyer.jui.data.server.requests.apkUninstallQuery
|
||||
import ca.gosyer.jui.data.server.requests.apkUpdateQuery
|
||||
import ca.gosyer.jui.data.server.requests.extensionListQuery
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.HttpRequestBuilder
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.client.statement.bodyAsChannel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
@@ -30,38 +31,38 @@ class ExtensionInteractionHandler @Inject constructor(
|
||||
) : BaseInteractionHandler(client, serverPreferences) {
|
||||
|
||||
fun getExtensionList() = flow {
|
||||
val response = client.get<List<Extension>>(
|
||||
val response = client.get(
|
||||
serverUrl + extensionListQuery()
|
||||
)
|
||||
).asSuccess().body<List<Extension>>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun installExtension(extension: Extension) = flow {
|
||||
val response = client.get<HttpResponse>(
|
||||
val response = client.get(
|
||||
serverUrl + apkInstallQuery(extension.pkgName)
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun updateExtension(extension: Extension) = flow {
|
||||
val response = client.get<HttpResponse>(
|
||||
val response = client.get(
|
||||
serverUrl + apkUpdateQuery(extension.pkgName)
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun uninstallExtension(extension: Extension) = flow {
|
||||
val response = client.get<HttpResponse>(
|
||||
val response = client.get(
|
||||
serverUrl + apkUninstallQuery(extension.pkgName)
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun getApkIcon(extension: Extension, block: HttpRequestBuilder.() -> Unit) = flow {
|
||||
val response = client.get<ByteReadChannel>(
|
||||
val response = client.get(
|
||||
serverUrl + apkIconQuery(extension.apkName),
|
||||
block
|
||||
)
|
||||
).asSuccess().bodyAsChannel()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
package ca.gosyer.jui.data.server.interactions
|
||||
|
||||
import ca.gosyer.jui.core.io.asSuccess
|
||||
import ca.gosyer.jui.core.lang.IO
|
||||
import ca.gosyer.jui.data.models.Manga
|
||||
import ca.gosyer.jui.data.server.Http
|
||||
@@ -14,7 +15,6 @@ import ca.gosyer.jui.data.server.requests.addMangaToLibraryQuery
|
||||
import ca.gosyer.jui.data.server.requests.removeMangaFromLibraryRequest
|
||||
import io.ktor.client.request.delete
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
@@ -26,18 +26,18 @@ class LibraryInteractionHandler @Inject constructor(
|
||||
) : BaseInteractionHandler(client, serverPreferences) {
|
||||
|
||||
fun addMangaToLibrary(mangaId: Long) = flow {
|
||||
val response = client.get<HttpResponse>(
|
||||
val response = client.get(
|
||||
serverUrl + addMangaToLibraryQuery(mangaId)
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun addMangaToLibrary(manga: Manga) = addMangaToLibrary(manga.id)
|
||||
|
||||
fun removeMangaFromLibrary(mangaId: Long) = flow {
|
||||
val response = client.delete<HttpResponse>(
|
||||
val response = client.delete(
|
||||
serverUrl + removeMangaFromLibraryRequest(mangaId)
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
package ca.gosyer.jui.data.server.interactions
|
||||
|
||||
import ca.gosyer.jui.core.io.asSuccess
|
||||
import ca.gosyer.jui.core.lang.IO
|
||||
import ca.gosyer.jui.data.models.Manga
|
||||
import ca.gosyer.jui.data.server.Http
|
||||
@@ -13,14 +14,14 @@ import ca.gosyer.jui.data.server.ServerPreferences
|
||||
import ca.gosyer.jui.data.server.requests.mangaQuery
|
||||
import ca.gosyer.jui.data.server.requests.mangaThumbnailQuery
|
||||
import ca.gosyer.jui.data.server.requests.updateMangaMetaRequest
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.HttpRequestBuilder
|
||||
import io.ktor.client.request.forms.submitForm
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.parameter
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.client.statement.bodyAsChannel
|
||||
import io.ktor.http.HttpMethod
|
||||
import io.ktor.http.Parameters
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
@@ -32,7 +33,7 @@ class MangaInteractionHandler @Inject constructor(
|
||||
) : BaseInteractionHandler(client, serverPreferences) {
|
||||
|
||||
fun getManga(mangaId: Long, refresh: Boolean = false) = flow {
|
||||
val response = client.get<Manga>(
|
||||
val response = client.get(
|
||||
serverUrl + mangaQuery(mangaId)
|
||||
) {
|
||||
url {
|
||||
@@ -40,22 +41,22 @@ class MangaInteractionHandler @Inject constructor(
|
||||
parameter("onlineFetch", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.asSuccess().body<Manga>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun getManga(manga: Manga, refresh: Boolean = false) = getManga(manga.id, refresh)
|
||||
|
||||
fun getMangaThumbnail(mangaId: Long, block: HttpRequestBuilder.() -> Unit) = flow {
|
||||
val response = client.get<ByteReadChannel>(
|
||||
val response = client.get(
|
||||
serverUrl + mangaThumbnailQuery(mangaId),
|
||||
block
|
||||
)
|
||||
).asSuccess().bodyAsChannel()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun updateMangaMeta(mangaId: Long, key: String, value: String) = flow {
|
||||
val response = client.submitForm<HttpResponse>(
|
||||
val response = client.submitForm(
|
||||
serverUrl + updateMangaMetaRequest(mangaId),
|
||||
formParameters = Parameters.build {
|
||||
append("key", key)
|
||||
@@ -63,7 +64,7 @@ class MangaInteractionHandler @Inject constructor(
|
||||
}
|
||||
) {
|
||||
method = HttpMethod.Patch
|
||||
}
|
||||
}.asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
|
||||
@@ -6,15 +6,16 @@
|
||||
|
||||
package ca.gosyer.jui.data.server.interactions
|
||||
|
||||
import ca.gosyer.jui.core.io.asSuccess
|
||||
import ca.gosyer.jui.core.lang.IO
|
||||
import ca.gosyer.jui.data.models.About
|
||||
import ca.gosyer.jui.data.server.Http
|
||||
import ca.gosyer.jui.data.server.ServerPreferences
|
||||
import ca.gosyer.jui.data.server.requests.aboutQuery
|
||||
import ca.gosyer.jui.data.server.requests.checkUpdateQuery
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
@@ -26,16 +27,16 @@ class SettingsInteractionHandler @Inject constructor(
|
||||
) : BaseInteractionHandler(client, serverPreferences) {
|
||||
|
||||
fun aboutServer() = flow {
|
||||
val response = client.get<About>(
|
||||
val response = client.get(
|
||||
serverUrl + aboutQuery()
|
||||
)
|
||||
).asSuccess().body<About>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun checkUpdate() = flow {
|
||||
val response = client.post<HttpResponse>(
|
||||
val response = client.post(
|
||||
serverUrl + checkUpdateQuery()
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
package ca.gosyer.jui.data.server.interactions
|
||||
|
||||
import ca.gosyer.jui.core.io.asSuccess
|
||||
import ca.gosyer.jui.core.lang.IO
|
||||
import ca.gosyer.jui.data.models.MangaPage
|
||||
import ca.gosyer.jui.data.models.Source
|
||||
@@ -25,10 +26,11 @@ import ca.gosyer.jui.data.server.requests.sourceListQuery
|
||||
import ca.gosyer.jui.data.server.requests.sourcePopularQuery
|
||||
import ca.gosyer.jui.data.server.requests.sourceSearchQuery
|
||||
import ca.gosyer.jui.data.server.requests.updateSourceSettingQuery
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.parameter
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.client.request.setBody
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.contentType
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -44,25 +46,25 @@ class SourceInteractionHandler @Inject constructor(
|
||||
) : BaseInteractionHandler(client, serverPreferences) {
|
||||
|
||||
fun getSourceList() = flow {
|
||||
val response = client.get<List<Source>>(
|
||||
val response = client.get(
|
||||
serverUrl + sourceListQuery()
|
||||
)
|
||||
).asSuccess().body<List<Source>>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun getSourceInfo(sourceId: Long) = flow {
|
||||
val response = client.get<Source>(
|
||||
val response = client.get(
|
||||
serverUrl + sourceInfoQuery(sourceId)
|
||||
)
|
||||
).asSuccess().body<Source>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun getSourceInfo(source: Source) = getSourceInfo(source.id)
|
||||
|
||||
fun getPopularManga(sourceId: Long, pageNum: Int) = flow {
|
||||
val response = client.get<MangaPage>(
|
||||
val response = client.get(
|
||||
serverUrl + sourcePopularQuery(sourceId, pageNum)
|
||||
)
|
||||
).asSuccess().body<MangaPage>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
@@ -72,9 +74,9 @@ class SourceInteractionHandler @Inject constructor(
|
||||
)
|
||||
|
||||
fun getLatestManga(sourceId: Long, pageNum: Int) = flow {
|
||||
val response = client.get<MangaPage>(
|
||||
val response = client.get(
|
||||
serverUrl + sourceLatestQuery(sourceId, pageNum)
|
||||
)
|
||||
).asSuccess().body<MangaPage>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
@@ -85,7 +87,7 @@ class SourceInteractionHandler @Inject constructor(
|
||||
|
||||
// TODO: 2021-03-14
|
||||
fun getGlobalSearchResults(searchTerm: String) = flow {
|
||||
val response = client.get<HttpResponse>(
|
||||
val response = client.get(
|
||||
serverUrl + globalSearchQuery()
|
||||
) {
|
||||
url {
|
||||
@@ -93,12 +95,12 @@ class SourceInteractionHandler @Inject constructor(
|
||||
parameter("searchTerm", searchTerm)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun getSearchResults(sourceId: Long, searchTerm: String, pageNum: Int) = flow {
|
||||
val response = client.get<MangaPage>(
|
||||
val response = client.get(
|
||||
serverUrl + sourceSearchQuery(sourceId)
|
||||
) {
|
||||
url {
|
||||
@@ -107,7 +109,7 @@ class SourceInteractionHandler @Inject constructor(
|
||||
parameter("searchTerm", searchTerm)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.asSuccess().body<MangaPage>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
@@ -118,7 +120,7 @@ class SourceInteractionHandler @Inject constructor(
|
||||
)
|
||||
|
||||
fun getFilterList(sourceId: Long, reset: Boolean = false) = flow {
|
||||
val response = client.get<List<SourceFilter>>(
|
||||
val response = client.get(
|
||||
serverUrl + getFilterListQuery(sourceId)
|
||||
) {
|
||||
url {
|
||||
@@ -126,19 +128,19 @@ class SourceInteractionHandler @Inject constructor(
|
||||
parameter("reset", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.asSuccess().body<List<SourceFilter>>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun getFilterList(source: Source, reset: Boolean = false) = getFilterList(source.id, reset)
|
||||
|
||||
fun setFilter(sourceId: Long, sourceFilter: SourceFilterChange) = flow {
|
||||
val response = client.post<HttpResponse>(
|
||||
val response = client.post(
|
||||
serverUrl + setFilterRequest(sourceId)
|
||||
) {
|
||||
contentType(ContentType.Application.Json)
|
||||
body = sourceFilter
|
||||
}
|
||||
setBody(sourceFilter)
|
||||
}.asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
@@ -156,21 +158,21 @@ class SourceInteractionHandler @Inject constructor(
|
||||
)
|
||||
|
||||
fun getSourceSettings(sourceId: Long) = flow {
|
||||
val response = client.get<List<SourcePreference>>(
|
||||
val response = client.get(
|
||||
serverUrl + getSourceSettingsQuery(sourceId)
|
||||
)
|
||||
).asSuccess().body<List<SourcePreference>>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun getSourceSettings(source: Source) = getSourceSettings(source.id)
|
||||
|
||||
fun setSourceSetting(sourceId: Long, sourcePreference: SourcePreferenceChange) = flow {
|
||||
val response = client.post<HttpResponse>(
|
||||
val response = client.post(
|
||||
serverUrl + updateSourceSettingQuery(sourceId)
|
||||
) {
|
||||
contentType(ContentType.Application.Json)
|
||||
body = sourcePreference
|
||||
}
|
||||
setBody(sourcePreference)
|
||||
}.asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
package ca.gosyer.jui.data.server.interactions
|
||||
|
||||
import ca.gosyer.jui.core.io.asSuccess
|
||||
import ca.gosyer.jui.core.lang.IO
|
||||
import ca.gosyer.jui.data.models.Category
|
||||
import ca.gosyer.jui.data.models.Updates
|
||||
@@ -13,10 +14,10 @@ import ca.gosyer.jui.data.server.Http
|
||||
import ca.gosyer.jui.data.server.ServerPreferences
|
||||
import ca.gosyer.jui.data.server.requests.fetchUpdatesRequest
|
||||
import ca.gosyer.jui.data.server.requests.recentUpdatesQuery
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.forms.submitForm
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.post
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.http.Parameters
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flow
|
||||
@@ -29,26 +30,26 @@ class UpdatesInteractionHandler @Inject constructor(
|
||||
) : BaseInteractionHandler(client, serverPreferences) {
|
||||
|
||||
fun getRecentUpdates(pageNum: Int) = flow {
|
||||
val response = client.get<Updates>(
|
||||
val response = client.get(
|
||||
serverUrl + recentUpdatesQuery(pageNum)
|
||||
)
|
||||
).asSuccess().body<Updates>()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun updateLibrary() = flow {
|
||||
val response = client.post<HttpResponse>(
|
||||
val response = client.post(
|
||||
serverUrl + fetchUpdatesRequest()
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
fun updateCategory(categoryId: Long) = flow {
|
||||
val response = client.submitForm<HttpResponse>(
|
||||
val response = client.submitForm(
|
||||
serverUrl + fetchUpdatesRequest(),
|
||||
formParameters = Parameters.build {
|
||||
append("category", categoryId.toString())
|
||||
}
|
||||
)
|
||||
).asSuccess()
|
||||
emit(response)
|
||||
}.flowOn(Dispatchers.IO)
|
||||
|
||||
|
||||
@@ -8,20 +8,20 @@ package ca.gosyer.jui.data.server.requests
|
||||
|
||||
@Post
|
||||
fun backupImportRequest() =
|
||||
"/api/v1/backup/import"
|
||||
"api/v1/backup/import"
|
||||
|
||||
@Post
|
||||
fun backupFileImportRequest() =
|
||||
"/api/v1/backup/import/file"
|
||||
"api/v1/backup/import/file"
|
||||
|
||||
@Post
|
||||
fun backupExportRequest() =
|
||||
"/api/v1/backup/export"
|
||||
"api/v1/backup/export"
|
||||
|
||||
@Post
|
||||
fun backupFileExportRequest() =
|
||||
"/api/v1/backup/export/file"
|
||||
"api/v1/backup/export/file"
|
||||
|
||||
@Post
|
||||
fun validateBackupFileRequest() =
|
||||
"/api/v1/backup/validate/file"
|
||||
"api/v1/backup/validate/file"
|
||||
|
||||
@@ -8,39 +8,39 @@ package ca.gosyer.jui.data.server.requests
|
||||
|
||||
@Get
|
||||
fun getMangaCategoriesQuery(mangaId: Long) =
|
||||
"/api/v1/manga/$mangaId/category/"
|
||||
"api/v1/manga/$mangaId/category/"
|
||||
|
||||
@Get
|
||||
fun addMangaToCategoryQuery(mangaId: Long, categoryId: Long) =
|
||||
"/api/v1/manga/$mangaId/category/$categoryId"
|
||||
"api/v1/manga/$mangaId/category/$categoryId"
|
||||
|
||||
@Delete
|
||||
fun removeMangaFromCategoryRequest(mangaId: Long, categoryId: Long) =
|
||||
"/api/v1/manga/$mangaId/category/$categoryId"
|
||||
"api/v1/manga/$mangaId/category/$categoryId"
|
||||
|
||||
@Get
|
||||
fun getCategoriesQuery() =
|
||||
"/api/v1/category/"
|
||||
"api/v1/category/"
|
||||
|
||||
/**
|
||||
* Post a formbody with the param {name} for creation of a category
|
||||
*/
|
||||
@Post
|
||||
fun createCategoryRequest() =
|
||||
"/api/v1/category/"
|
||||
"api/v1/category/"
|
||||
|
||||
@Patch
|
||||
fun categoryModifyRequest(categoryId: Long) =
|
||||
"/api/v1/category/$categoryId"
|
||||
"api/v1/category/$categoryId"
|
||||
|
||||
@Patch
|
||||
fun categoryReorderRequest() =
|
||||
"/api/v1/category/reorder"
|
||||
"api/v1/category/reorder"
|
||||
|
||||
@Delete
|
||||
fun categoryDeleteRequest(categoryId: Long) =
|
||||
"/api/v1/category/$categoryId"
|
||||
"api/v1/category/$categoryId"
|
||||
|
||||
@Get
|
||||
fun getMangaInCategoryQuery(categoryId: Long) =
|
||||
"/api/v1/category/$categoryId"
|
||||
"api/v1/category/$categoryId"
|
||||
|
||||
@@ -8,32 +8,32 @@ package ca.gosyer.jui.data.server.requests
|
||||
|
||||
@Get
|
||||
fun getMangaChaptersQuery(mangaId: Long) =
|
||||
"/api/v1/manga/$mangaId/chapters"
|
||||
"api/v1/manga/$mangaId/chapters"
|
||||
|
||||
@Get
|
||||
fun getChapterQuery(mangaId: Long, chapterIndex: Int) =
|
||||
"/api/v1/manga/$mangaId/chapter/$chapterIndex"
|
||||
"api/v1/manga/$mangaId/chapter/$chapterIndex"
|
||||
|
||||
@Patch
|
||||
fun updateChapterRequest(mangaId: Long, chapterIndex: Int) =
|
||||
"/api/v1/manga/$mangaId/chapter/$chapterIndex"
|
||||
"api/v1/manga/$mangaId/chapter/$chapterIndex"
|
||||
|
||||
@Get
|
||||
fun getPageQuery(mangaId: Long, chapterIndex: Int, index: Int) =
|
||||
"/api/v1/manga/$mangaId/chapter/$chapterIndex/page/$index"
|
||||
"api/v1/manga/$mangaId/chapter/$chapterIndex/page/$index"
|
||||
|
||||
@Delete
|
||||
fun deleteDownloadedChapterRequest(mangaId: Long, chapterIndex: Int) =
|
||||
"/api/v1/manga/$mangaId/chapter/$chapterIndex"
|
||||
"api/v1/manga/$mangaId/chapter/$chapterIndex"
|
||||
|
||||
@Get
|
||||
fun queueDownloadChapterRequest(mangaId: Long, chapterIndex: Int) =
|
||||
"/api/v1/download/$mangaId/chapter/$chapterIndex"
|
||||
"api/v1/download/$mangaId/chapter/$chapterIndex"
|
||||
|
||||
@Delete
|
||||
fun stopDownloadingChapterRequest(mangaId: Long, chapterIndex: Int) =
|
||||
"/api/v1/download/$mangaId/chapter/$chapterIndex"
|
||||
"api/v1/download/$mangaId/chapter/$chapterIndex"
|
||||
|
||||
@Patch
|
||||
fun updateChapterMetaRequest(mangaId: Long, chapterIndex: Int) =
|
||||
"/api/v1/manga/$mangaId/chapter/$chapterIndex/meta"
|
||||
"api/v1/manga/$mangaId/chapter/$chapterIndex/meta"
|
||||
|
||||
@@ -8,16 +8,16 @@ package ca.gosyer.jui.data.server.requests
|
||||
|
||||
@WS
|
||||
fun downloadsQuery() =
|
||||
"/api/v1/downloads"
|
||||
"api/v1/downloads"
|
||||
|
||||
@Get
|
||||
fun downloadsStartRequest() =
|
||||
"/api/v1/downloads/start"
|
||||
"api/v1/downloads/start"
|
||||
|
||||
@Get
|
||||
fun downloadsStopRequest() =
|
||||
"/api/v1/downloads/stop"
|
||||
"api/v1/downloads/stop"
|
||||
|
||||
@Get
|
||||
fun downloadsClearRequest() =
|
||||
"/api/v1/downloads/clear"
|
||||
"api/v1/downloads/clear"
|
||||
|
||||
@@ -8,20 +8,20 @@ package ca.gosyer.jui.data.server.requests
|
||||
|
||||
@Get
|
||||
fun extensionListQuery() =
|
||||
"/api/v1/extension/list"
|
||||
"api/v1/extension/list"
|
||||
|
||||
@Get
|
||||
fun apkInstallQuery(pkgName: String) =
|
||||
"/api/v1/extension/install/$pkgName"
|
||||
"api/v1/extension/install/$pkgName"
|
||||
|
||||
@Get
|
||||
fun apkUpdateQuery(pkgName: String) =
|
||||
"/api/v1/extension/update/$pkgName"
|
||||
"api/v1/extension/update/$pkgName"
|
||||
|
||||
@Get
|
||||
fun apkUninstallQuery(pkgName: String) =
|
||||
"/api/v1/extension/uninstall/$pkgName"
|
||||
"api/v1/extension/uninstall/$pkgName"
|
||||
|
||||
@Get
|
||||
fun apkIconQuery(apkName: String) =
|
||||
"/api/v1/extension/icon/$apkName"
|
||||
"api/v1/extension/icon/$apkName"
|
||||
|
||||
@@ -8,8 +8,8 @@ package ca.gosyer.jui.data.server.requests
|
||||
|
||||
@Get
|
||||
fun addMangaToLibraryQuery(mangaId: Long) =
|
||||
"/api/v1/manga/$mangaId/library"
|
||||
"api/v1/manga/$mangaId/library"
|
||||
|
||||
@Delete
|
||||
fun removeMangaFromLibraryRequest(mangaId: Long) =
|
||||
"/api/v1/manga/$mangaId/library"
|
||||
"api/v1/manga/$mangaId/library"
|
||||
|
||||
@@ -8,12 +8,12 @@ package ca.gosyer.jui.data.server.requests
|
||||
|
||||
@Get
|
||||
fun mangaQuery(mangaId: Long) =
|
||||
"/api/v1/manga/$mangaId/"
|
||||
"api/v1/manga/$mangaId/"
|
||||
|
||||
@Get
|
||||
fun mangaThumbnailQuery(mangaId: Long) =
|
||||
"/api/v1/manga/$mangaId/thumbnail"
|
||||
"api/v1/manga/$mangaId/thumbnail"
|
||||
|
||||
@Post
|
||||
fun updateMangaMetaRequest(mangaId: Long) =
|
||||
"/api/v1/manga/$mangaId/meta"
|
||||
"api/v1/manga/$mangaId/meta"
|
||||
|
||||
@@ -8,8 +8,8 @@ package ca.gosyer.jui.data.server.requests
|
||||
|
||||
@Get
|
||||
fun aboutQuery() =
|
||||
"/api/v1/settings/about"
|
||||
"api/v1/settings/about"
|
||||
|
||||
@Get
|
||||
fun checkUpdateQuery() =
|
||||
"/api/v1/settings/check-update"
|
||||
"api/v1/settings/check-update"
|
||||
|
||||
@@ -8,40 +8,40 @@ package ca.gosyer.jui.data.server.requests
|
||||
|
||||
@Get
|
||||
fun sourceListQuery() =
|
||||
"/api/v1/source/list"
|
||||
"api/v1/source/list"
|
||||
|
||||
@Get
|
||||
fun sourceInfoQuery(sourceId: Long) =
|
||||
"/api/v1/source/$sourceId"
|
||||
"api/v1/source/$sourceId"
|
||||
|
||||
@Get
|
||||
fun sourcePopularQuery(sourceId: Long, pageNum: Int) =
|
||||
"/api/v1/source/$sourceId/popular/$pageNum"
|
||||
"api/v1/source/$sourceId/popular/$pageNum"
|
||||
|
||||
@Get
|
||||
fun sourceLatestQuery(sourceId: Long, pageNum: Int) =
|
||||
"/api/v1/source/$sourceId/latest/$pageNum"
|
||||
"api/v1/source/$sourceId/latest/$pageNum"
|
||||
|
||||
@Get
|
||||
fun globalSearchQuery() =
|
||||
"/api/v1/source/all/search"
|
||||
"api/v1/source/all/search"
|
||||
|
||||
@Get
|
||||
fun sourceSearchQuery(sourceId: Long) =
|
||||
"/api/v1/source/$sourceId/search"
|
||||
"api/v1/source/$sourceId/search"
|
||||
|
||||
@Get
|
||||
fun getFilterListQuery(sourceId: Long) =
|
||||
"/api/v1/source/$sourceId/filters"
|
||||
"api/v1/source/$sourceId/filters"
|
||||
|
||||
@Post
|
||||
fun setFilterRequest(sourceId: Long) =
|
||||
"/api/v1/source/$sourceId/filters"
|
||||
"api/v1/source/$sourceId/filters"
|
||||
|
||||
@Get
|
||||
fun getSourceSettingsQuery(sourceId: Long) =
|
||||
"/api/v1/source/$sourceId/preferences"
|
||||
"api/v1/source/$sourceId/preferences"
|
||||
|
||||
@Post
|
||||
fun updateSourceSettingQuery(sourceId: Long) =
|
||||
"/api/v1/source/$sourceId/preferences"
|
||||
"api/v1/source/$sourceId/preferences"
|
||||
|
||||
@@ -8,16 +8,16 @@ package ca.gosyer.jui.data.server.requests
|
||||
|
||||
@Get
|
||||
fun recentUpdatesQuery(pageNum: Int) =
|
||||
"/api/v1/update/recentChapters/$pageNum"
|
||||
"api/v1/update/recentChapters/$pageNum"
|
||||
|
||||
@Post
|
||||
fun fetchUpdatesRequest() =
|
||||
"/api/v1/update/fetch"
|
||||
"api/v1/update/fetch"
|
||||
|
||||
@Get
|
||||
fun updatesSummaryQuery() =
|
||||
"/api/v1/update/summary"
|
||||
"api/v1/update/summary"
|
||||
|
||||
@WS
|
||||
fun updatesQuery() =
|
||||
"/api/v1/update"
|
||||
"api/v1/update"
|
||||
|
||||
@@ -10,6 +10,7 @@ import ca.gosyer.jui.core.lang.IO
|
||||
import ca.gosyer.jui.data.build.BuildKonfig
|
||||
import ca.gosyer.jui.data.server.Http
|
||||
import ca.gosyer.jui.data.update.model.GithubRelease
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.get
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flow
|
||||
@@ -22,9 +23,9 @@ class UpdateChecker @Inject constructor(
|
||||
) {
|
||||
fun checkForUpdates() = flow {
|
||||
// if (!updatePreferences.enabled().get()) return
|
||||
val latestRelease = client.get<GithubRelease>(
|
||||
val latestRelease = client.get(
|
||||
"https://api.github.com/repos/$GITHUB_REPO/releases/latest"
|
||||
)
|
||||
).body<GithubRelease>()
|
||||
|
||||
if (isNewVersion(latestRelease.version)) {
|
||||
emit(Update.UpdateFound(latestRelease))
|
||||
|
||||
@@ -59,7 +59,8 @@ dependencies {
|
||||
// Http client
|
||||
implementation(libs.ktor.core)
|
||||
implementation(libs.ktor.okHttp)
|
||||
implementation(libs.ktor.serialization)
|
||||
implementation(libs.ktor.contentNegotiation)
|
||||
implementation(libs.ktor.serialization.json)
|
||||
implementation(libs.ktor.logging)
|
||||
implementation(libs.ktor.websockets)
|
||||
implementation(libs.ktor.auth)
|
||||
|
||||
@@ -32,7 +32,7 @@ ksp = "1.6.10-1.0.4"
|
||||
kotlinInject = "0.4.1"
|
||||
|
||||
# Network
|
||||
ktor = "1.6.8"
|
||||
ktor = "2.0.0"
|
||||
|
||||
# Logging
|
||||
slf4j = "1.7.36"
|
||||
@@ -113,7 +113,8 @@ kotlinInject-compiler = { module = "me.tatarka.inject:kotlin-inject-compiler-ksp
|
||||
# Network
|
||||
ktor-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
|
||||
ktor-okHttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
|
||||
ktor-serialization = { module = "io.ktor:ktor-client-serialization", version.ref = "ktor" }
|
||||
ktor-contentNegotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
|
||||
ktor-serialization-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
|
||||
ktor-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
|
||||
ktor-websockets = { module = "io.ktor:ktor-client-websockets", version.ref = "ktor" }
|
||||
ktor-auth = { module = "io.ktor:ktor-client-auth", version.ref = "ktor" }
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
package ca.gosyer.jui.ui.base.image
|
||||
|
||||
import ca.gosyer.jui.core.io.HttpException
|
||||
import ca.gosyer.jui.data.models.Extension
|
||||
import ca.gosyer.jui.data.models.Manga
|
||||
import ca.gosyer.jui.data.models.Source
|
||||
@@ -25,16 +26,16 @@ import io.kamel.core.fetcher.Fetcher
|
||||
import io.kamel.core.mapper.Mapper
|
||||
import io.kamel.image.config.imageBitmapDecoder
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.call.receive
|
||||
import io.ktor.client.features.onDownload
|
||||
import io.ktor.client.plugins.onDownload
|
||||
import io.ktor.client.request.request
|
||||
import io.ktor.client.request.takeFrom
|
||||
import io.ktor.client.request.url
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import io.ktor.client.statement.bodyAsChannel
|
||||
import io.ktor.client.statement.discardRemaining
|
||||
import io.ktor.http.Url
|
||||
import io.ktor.http.isSuccess
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -70,19 +71,19 @@ class KamelConfigProvider @Inject constructor(
|
||||
|
||||
class MangaCoverMapper(private val serverUrlStateFlow: StateFlow<Url>) : Mapper<Manga, Url> {
|
||||
override fun map(input: Manga): Url {
|
||||
return Url(serverUrlStateFlow.value.toString() + input.thumbnailUrl)
|
||||
return Url(serverUrlStateFlow.value.toString() + input.thumbnailUrl?.trimStart('/'))
|
||||
}
|
||||
}
|
||||
|
||||
class ExtensionIconMapper(private val serverUrlStateFlow: StateFlow<Url>) : Mapper<Extension, Url> {
|
||||
override fun map(input: Extension): Url {
|
||||
return Url(serverUrlStateFlow.value.toString() + input.iconUrl)
|
||||
return Url(serverUrlStateFlow.value.toString() + input.iconUrl.trimStart('/'))
|
||||
}
|
||||
}
|
||||
|
||||
class SourceIconMapper(private val serverUrlStateFlow: StateFlow<Url>) : Mapper<Source, Url> {
|
||||
override fun map(input: Source): Url {
|
||||
return Url(serverUrlStateFlow.value.toString() + input.iconUrl)
|
||||
return Url(serverUrlStateFlow.value.toString() + input.iconUrl.trimStart('/'))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,12 +94,11 @@ class KamelConfigProvider @Inject constructor(
|
||||
override val Url.isSupported: Boolean
|
||||
get() = protocol.name == "https" || protocol.name == "http"
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun fetch(
|
||||
data: Url,
|
||||
resourceConfig: ResourceConfig
|
||||
): Flow<Resource<ByteReadChannel>> = channelFlow {
|
||||
val response = client.request<HttpResponse> {
|
||||
val response = client.request {
|
||||
onDownload { bytesSentTotal, contentLength ->
|
||||
val progress = (bytesSentTotal.toFloat() / contentLength).coerceAtMost(1.0F)
|
||||
send(Resource.Loading(progress))
|
||||
@@ -106,8 +106,13 @@ class KamelConfigProvider @Inject constructor(
|
||||
takeFrom(resourceConfig.requestData)
|
||||
url(data)
|
||||
}
|
||||
val bytes = response.receive<ByteReadChannel>()
|
||||
send(Resource.Success(bytes))
|
||||
if (response.status.isSuccess()) {
|
||||
val bytes = response.bodyAsChannel()
|
||||
send(Resource.Success(bytes))
|
||||
} else {
|
||||
response.discardRemaining()
|
||||
send(Resource.Failure(HttpException(response.status)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import ca.gosyer.jui.ui.reader.model.ReaderPage
|
||||
import ca.gosyer.jui.ui.util.compose.toImageBitmap
|
||||
import ca.gosyer.jui.ui.util.lang.priorityChannel
|
||||
import cafe.adriel.voyager.core.concurrent.AtomicInt32
|
||||
import io.ktor.client.features.onDownload
|
||||
import io.ktor.client.plugins.onDownload
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
|
||||
@@ -59,9 +59,9 @@ import com.vanpra.composematerialdialogs.MaterialDialog
|
||||
import com.vanpra.composematerialdialogs.MaterialDialogState
|
||||
import com.vanpra.composematerialdialogs.rememberMaterialDialogState
|
||||
import com.vanpra.composematerialdialogs.title
|
||||
import io.ktor.client.features.onDownload
|
||||
import io.ktor.client.features.onUpload
|
||||
import io.ktor.http.isSuccess
|
||||
import io.ktor.client.plugins.onDownload
|
||||
import io.ktor.client.plugins.onUpload
|
||||
import io.ktor.client.statement.bodyAsChannel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -202,27 +202,25 @@ class SettingsBackupViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
.onEach { backup ->
|
||||
if (backup.status.isSuccess()) {
|
||||
val filename =
|
||||
backup.headers["content-disposition"]?.substringAfter("filename=")
|
||||
?.trim('"') ?: "backup"
|
||||
tempFile.value = FileSystem.SYSTEM_TEMPORARY_DIRECTORY.resolve(filename).also {
|
||||
mutex.tryLock()
|
||||
scope.launch {
|
||||
try {
|
||||
backup.content.toSource().saveTo(it)
|
||||
} catch (e: Exception) {
|
||||
e.throwIfCancellation()
|
||||
log.warn(e) { "Error creating backup" }
|
||||
_creatingStatus.value = Status.Error
|
||||
_creating.value = false
|
||||
} finally {
|
||||
mutex.unlock()
|
||||
}
|
||||
val filename =
|
||||
backup.headers["content-disposition"]?.substringAfter("filename=")
|
||||
?.trim('"') ?: "backup"
|
||||
tempFile.value = FileSystem.SYSTEM_TEMPORARY_DIRECTORY.resolve(filename).also {
|
||||
mutex.tryLock()
|
||||
scope.launch {
|
||||
try {
|
||||
backup.bodyAsChannel().toSource().saveTo(it)
|
||||
} catch (e: Exception) {
|
||||
e.throwIfCancellation()
|
||||
log.warn(e) { "Error creating backup" }
|
||||
_creatingStatus.value = Status.Error
|
||||
_creating.value = false
|
||||
} finally {
|
||||
mutex.unlock()
|
||||
}
|
||||
}
|
||||
_createFlow.emit(filename)
|
||||
}
|
||||
_createFlow.emit(filename)
|
||||
}
|
||||
.catch {
|
||||
log.warn(it) { "Error exporting backup" }
|
||||
|
||||
@@ -8,9 +8,11 @@ package ca.gosyer.jui.ui.util.compose
|
||||
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.toComposeImageBitmap
|
||||
import ca.gosyer.jui.core.io.asSuccess
|
||||
import ca.gosyer.jui.data.server.Http
|
||||
import io.ktor.client.request.HttpRequestBuilder
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.statement.bodyAsChannel
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import io.ktor.utils.io.jvm.javaio.copyTo
|
||||
import okio.FileSystem
|
||||
@@ -25,7 +27,7 @@ fun imageFromFile(file: Path): ImageBitmap {
|
||||
}
|
||||
|
||||
suspend fun imageFromUrl(client: Http, url: String, block: HttpRequestBuilder.() -> Unit): ImageBitmap {
|
||||
return client.get<ByteReadChannel>(url, block).toImageBitmap()
|
||||
return client.get(url, block).asSuccess().bodyAsChannel().toImageBitmap()
|
||||
}
|
||||
|
||||
actual suspend fun ByteReadChannel.toImageBitmap(): ImageBitmap {
|
||||
|
||||
Reference in New Issue
Block a user