mirror of
https://github.com/Suwayomi/Tachidesk.git
synced 2025-12-23 13:02:34 +01:00
add support for installing external APK
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# Server: v0.4.9-r882 + WebUI: r776
|
||||
# Server: v0.4.9-r888 + WebUI: r777
|
||||
## Tachidesk-Server
|
||||
### Public API
|
||||
#### Non-breaking changes
|
||||
- N/A
|
||||
- (r888) add installing APK from external sources endpoint
|
||||
|
||||
#### Breaking changes
|
||||
- (r877 #188 by @Syer10) `MangaDataClass.genre` changed type to `List<String>`
|
||||
@@ -11,7 +11,7 @@
|
||||
- N/A
|
||||
|
||||
### Private API
|
||||
- N/A
|
||||
- (r887) the `run` task won't call `downloadWebUI` now
|
||||
|
||||
|
||||
## Tachidesk-WebUI
|
||||
@@ -21,6 +21,7 @@
|
||||
- (r774 #21 by @voltrare) `ReaderNavbar.jsx`: Swap close and retract Navbar buttons
|
||||
- (r775 #23 by @voltrare) `yarn.lock`: Fixes version inconsistency after commit 9b866811b
|
||||
- (r776 #22 by @voltrare) add margin between Source and Extension cards, make the Search button look nicer
|
||||
- (r777) add support for installing external APK files
|
||||
|
||||
#### Bug fixes
|
||||
- N/A
|
||||
|
||||
@@ -14,7 +14,7 @@ const val MainClass = "suwayomi.tachidesk.MainKt"
|
||||
// should be bumped with each stable release
|
||||
val tachideskVersion = System.getenv("ProductVersion") ?: "v0.4.9"
|
||||
|
||||
val webUIRevisionTag = System.getenv("WebUIRevision") ?: "r776"
|
||||
val webUIRevisionTag = System.getenv("WebUIRevision") ?: "r777"
|
||||
|
||||
// counts commits on the the master branch
|
||||
val tachideskRevision = runCatching {
|
||||
|
||||
@@ -26,6 +26,7 @@ object MangaAPI {
|
||||
get("list", ExtensionController::list)
|
||||
|
||||
get("install/:pkgName", ExtensionController::install)
|
||||
post("install", ExtensionController::installFile)
|
||||
get("update/:pkgName", ExtensionController::update)
|
||||
get("uninstall/:pkgName", ExtensionController::uninstall)
|
||||
|
||||
|
||||
@@ -33,6 +33,19 @@ object ExtensionController {
|
||||
)
|
||||
}
|
||||
|
||||
/** install the uploaded apk file */
|
||||
fun installFile(ctx: Context) {
|
||||
|
||||
val uploadedFile = ctx.uploadedFile("file")!!
|
||||
println(uploadedFile.filename)
|
||||
|
||||
ctx.json(
|
||||
future {
|
||||
Extension.installExternalExtension(uploadedFile.content)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/** update extension identified with "pkgName" */
|
||||
fun update(ctx: Context) {
|
||||
val pkgName = ctx.pathParam("pkgName")
|
||||
|
||||
@@ -17,6 +17,7 @@ import mu.KotlinLogging
|
||||
import okhttp3.Request
|
||||
import okio.buffer
|
||||
import okio.sink
|
||||
import okio.source
|
||||
import org.jetbrains.exposed.sql.deleteWhere
|
||||
import org.jetbrains.exposed.sql.insert
|
||||
import org.jetbrains.exposed.sql.select
|
||||
@@ -36,7 +37,6 @@ import suwayomi.tachidesk.manga.impl.util.PackageTools.dex2jar
|
||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.getPackageInfo
|
||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.getSignatureHash
|
||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.loadExtensionSources
|
||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.trustedSignatures
|
||||
import suwayomi.tachidesk.manga.impl.util.network.await
|
||||
import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
|
||||
import suwayomi.tachidesk.manga.model.table.ExtensionTable
|
||||
@@ -68,6 +68,22 @@ object Extension {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun installExternalExtension(inputStream: InputStream): Int {
|
||||
return installAPK {
|
||||
val apkName = "apkToSave.apk"
|
||||
val savePath = "${applicationDirs.extensionsRoot}/$apkName"
|
||||
// download apk file
|
||||
val downloadedFile = File(savePath)
|
||||
downloadedFile.sink().buffer().use { sink ->
|
||||
inputStream.source().use { source ->
|
||||
sink.writeAll(source)
|
||||
sink.flush()
|
||||
}
|
||||
}
|
||||
savePath
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun installAPK(fetcher: suspend () -> String): Int {
|
||||
val apkFilePath = fetcher()
|
||||
val apkName = File(apkFilePath).name
|
||||
@@ -103,12 +119,12 @@ object Extension {
|
||||
|
||||
val signatureHash = getSignatureHash(packageInfo)
|
||||
|
||||
if (signatureHash == null) {
|
||||
throw Exception("Package $pkgName isn't signed")
|
||||
} else if (signatureHash !in trustedSignatures) {
|
||||
// TODO: allow trusting keys
|
||||
throw Exception("This apk is not a signed with the official tachiyomi signature")
|
||||
}
|
||||
// if (signatureHash == null) {
|
||||
// throw Exception("Package $pkgName isn't signed")
|
||||
// } else if (signatureHash !in trustedSignatures) {
|
||||
// // TODO: allow trusting keys
|
||||
// throw Exception("This apk is not a signed with the official tachiyomi signature")
|
||||
// }
|
||||
|
||||
val isNsfw = packageInfo.applicationInfo.metaData.getString(METADATA_NSFW) == "1"
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ object PackageTools {
|
||||
|
||||
private const val officialSignature = "7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23" // inorichi's key
|
||||
private const val unofficialSignature = "64feb21075ba97ebc9cc981243645b331595c111cef1b0d084236a0403b00581" // ArMor's key
|
||||
var trustedSignatures = mutableSetOf<String>() + officialSignature + unofficialSignature
|
||||
val trustedSignatures = mutableSetOf<String>() + officialSignature + unofficialSignature
|
||||
|
||||
/**
|
||||
* Convert dex to jar, a wrapper for the dex2jar library
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package suwayomi.tachidesk.manga.impl.util.lang
|
||||
|
||||
fun List<String>.trimAll() = map { it.trim() }
|
||||
fun List<String>.trimAll() = map { it.trim() }
|
||||
|
||||
@@ -41,4 +41,4 @@ data class PagedMangaListDataClass(
|
||||
val hasNextPage: Boolean
|
||||
)
|
||||
|
||||
internal fun String?.toGenreList() = this?.split(",")?.trimAll().orEmpty()
|
||||
internal fun String?.toGenreList() = this?.split(",")?.trimAll().orEmpty()
|
||||
|
||||
Reference in New Issue
Block a user