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
|
## Tachidesk-Server
|
||||||
### Public API
|
### Public API
|
||||||
#### Non-breaking changes
|
#### Non-breaking changes
|
||||||
- N/A
|
- (r888) add installing APK from external sources endpoint
|
||||||
|
|
||||||
#### Breaking changes
|
#### Breaking changes
|
||||||
- (r877 #188 by @Syer10) `MangaDataClass.genre` changed type to `List<String>`
|
- (r877 #188 by @Syer10) `MangaDataClass.genre` changed type to `List<String>`
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
- N/A
|
- N/A
|
||||||
|
|
||||||
### Private API
|
### Private API
|
||||||
- N/A
|
- (r887) the `run` task won't call `downloadWebUI` now
|
||||||
|
|
||||||
|
|
||||||
## Tachidesk-WebUI
|
## Tachidesk-WebUI
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
- (r774 #21 by @voltrare) `ReaderNavbar.jsx`: Swap close and retract Navbar buttons
|
- (r774 #21 by @voltrare) `ReaderNavbar.jsx`: Swap close and retract Navbar buttons
|
||||||
- (r775 #23 by @voltrare) `yarn.lock`: Fixes version inconsistency after commit 9b866811b
|
- (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
|
- (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
|
#### Bug fixes
|
||||||
- N/A
|
- N/A
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const val MainClass = "suwayomi.tachidesk.MainKt"
|
|||||||
// should be bumped with each stable release
|
// should be bumped with each stable release
|
||||||
val tachideskVersion = System.getenv("ProductVersion") ?: "v0.4.9"
|
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
|
// counts commits on the the master branch
|
||||||
val tachideskRevision = runCatching {
|
val tachideskRevision = runCatching {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ object MangaAPI {
|
|||||||
get("list", ExtensionController::list)
|
get("list", ExtensionController::list)
|
||||||
|
|
||||||
get("install/:pkgName", ExtensionController::install)
|
get("install/:pkgName", ExtensionController::install)
|
||||||
|
post("install", ExtensionController::installFile)
|
||||||
get("update/:pkgName", ExtensionController::update)
|
get("update/:pkgName", ExtensionController::update)
|
||||||
get("uninstall/:pkgName", ExtensionController::uninstall)
|
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" */
|
/** update extension identified with "pkgName" */
|
||||||
fun update(ctx: Context) {
|
fun update(ctx: Context) {
|
||||||
val pkgName = ctx.pathParam("pkgName")
|
val pkgName = ctx.pathParam("pkgName")
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import mu.KotlinLogging
|
|||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okio.buffer
|
import okio.buffer
|
||||||
import okio.sink
|
import okio.sink
|
||||||
|
import okio.source
|
||||||
import org.jetbrains.exposed.sql.deleteWhere
|
import org.jetbrains.exposed.sql.deleteWhere
|
||||||
import org.jetbrains.exposed.sql.insert
|
import org.jetbrains.exposed.sql.insert
|
||||||
import org.jetbrains.exposed.sql.select
|
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.getPackageInfo
|
||||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.getSignatureHash
|
import suwayomi.tachidesk.manga.impl.util.PackageTools.getSignatureHash
|
||||||
import suwayomi.tachidesk.manga.impl.util.PackageTools.loadExtensionSources
|
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.network.await
|
||||||
import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
|
import suwayomi.tachidesk.manga.impl.util.storage.CachedImageResponse.getCachedImageResponse
|
||||||
import suwayomi.tachidesk.manga.model.table.ExtensionTable
|
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 {
|
suspend fun installAPK(fetcher: suspend () -> String): Int {
|
||||||
val apkFilePath = fetcher()
|
val apkFilePath = fetcher()
|
||||||
val apkName = File(apkFilePath).name
|
val apkName = File(apkFilePath).name
|
||||||
@@ -103,12 +119,12 @@ object Extension {
|
|||||||
|
|
||||||
val signatureHash = getSignatureHash(packageInfo)
|
val signatureHash = getSignatureHash(packageInfo)
|
||||||
|
|
||||||
if (signatureHash == null) {
|
// if (signatureHash == null) {
|
||||||
throw Exception("Package $pkgName isn't signed")
|
// throw Exception("Package $pkgName isn't signed")
|
||||||
} else if (signatureHash !in trustedSignatures) {
|
// } else if (signatureHash !in trustedSignatures) {
|
||||||
// TODO: allow trusting keys
|
// // TODO: allow trusting keys
|
||||||
throw Exception("This apk is not a signed with the official tachiyomi signature")
|
// throw Exception("This apk is not a signed with the official tachiyomi signature")
|
||||||
}
|
// }
|
||||||
|
|
||||||
val isNsfw = packageInfo.applicationInfo.metaData.getString(METADATA_NSFW) == "1"
|
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 officialSignature = "7ce04da7773d41b489f4693a366c36bcd0a11fc39b547168553c285bd7348e23" // inorichi's key
|
||||||
private const val unofficialSignature = "64feb21075ba97ebc9cc981243645b331595c111cef1b0d084236a0403b00581" // ArMor'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
|
* Convert dex to jar, a wrapper for the dex2jar library
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package suwayomi.tachidesk.manga.impl.util.lang
|
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
|
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