download webUI on demand
This commit is contained in:
@@ -66,6 +66,9 @@ dependencies {
|
||||
// asm for fixing SimpleDateFormat (must match Dex2Jar version)
|
||||
implementation("org.ow2.asm:asm-debug-all:5.0.3")
|
||||
|
||||
// extracting zip files
|
||||
implementation("net.lingala.zip4j:zip4j:2.9.0")
|
||||
|
||||
// Source models and interfaces from Tachiyomi 1.x
|
||||
// using source class from tachiyomi commit 9493577de27c40ce8b2b6122cc447d025e34c477 to not depend on tachiyomi.sourceapi
|
||||
// implementation("tachiyomi.sourceapi:source-api:1.1")
|
||||
@@ -99,6 +102,7 @@ sourceSets {
|
||||
|
||||
// should be bumped with each stable release
|
||||
val tachideskVersion = System.getenv("ProductVersion") ?: "v0.4.3"
|
||||
val webUIRevisionTag = System.getenv("WebUIRevision") ?: "r19"
|
||||
|
||||
// counts commit count on master
|
||||
val tachideskRevision = runCatching {
|
||||
@@ -126,6 +130,11 @@ buildConfig {
|
||||
buildConfigField("String", "BUILD_TYPE", if (System.getenv("ProductBuildType") == "Stable") "Stable" else "Preview")
|
||||
buildConfigField("long", "BUILD_TIME", Instant.now().epochSecond.toString())
|
||||
|
||||
|
||||
buildConfigField("String", "WEBUI_REPO", "https://github.com/Suwayomi/Tachidesk-WebUI-preview")
|
||||
buildConfigField("String", "WEBUI_TAG", webUIRevisionTag)
|
||||
|
||||
|
||||
buildConfigField("String", "GITHUB", "https://github.com/Suwayomi/Tachidesk")
|
||||
buildConfigField("String", "DISCORD", "https://discord.gg/DDZdqZWaHA")
|
||||
}
|
||||
|
||||
@@ -8,15 +8,20 @@ package suwayomi.tachidesk.server
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import io.javalin.Javalin
|
||||
import io.javalin.http.staticfiles.Location
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.future.future
|
||||
import mu.KotlinLogging
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import suwayomi.tachidesk.anime.AnimeAPI
|
||||
import suwayomi.tachidesk.global.GlobalAPI
|
||||
import suwayomi.tachidesk.manga.MangaAPI
|
||||
import suwayomi.tachidesk.server.util.Browser
|
||||
import suwayomi.tachidesk.server.util.setupWebUI
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.concurrent.thread
|
||||
@@ -24,6 +29,8 @@ import kotlin.concurrent.thread
|
||||
object JavalinSetup {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
|
||||
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
fun <T> future(block: suspend CoroutineScope.() -> T): CompletableFuture<T> {
|
||||
@@ -31,25 +38,19 @@ object JavalinSetup {
|
||||
}
|
||||
|
||||
fun javalinSetup() {
|
||||
var hasWebUiBundled = false
|
||||
|
||||
val app = Javalin.create { config ->
|
||||
try {
|
||||
// if the bellow line throws an exception then webUI is not bundled
|
||||
this::class.java.getResource("/webUI/index.html")
|
||||
if (serverConfig.webUIEnabled) {
|
||||
setupWebUI()
|
||||
|
||||
// no exception so we can tell javalin to serve webUI
|
||||
hasWebUiBundled = true
|
||||
config.addStaticFiles("/webUI")
|
||||
config.addSinglePageRoot("/", "/webUI/index.html")
|
||||
} catch (e: RuntimeException) {
|
||||
logger.warn("react build files are missing.")
|
||||
hasWebUiBundled = false
|
||||
logger.info { "Serving webUI static files" }
|
||||
config.addStaticFiles(applicationDirs.webUIRoot, Location.EXTERNAL)
|
||||
config.addSinglePageRoot("/", applicationDirs.webUIRoot + "/index.html", Location.EXTERNAL)
|
||||
}
|
||||
|
||||
config.enableCorsForAllOrigins()
|
||||
}.events { event ->
|
||||
event.serverStarted {
|
||||
if (hasWebUiBundled && serverConfig.initialOpenInBrowserEnabled) {
|
||||
if (serverConfig.webUIEnabled && serverConfig.initialOpenInBrowserEnabled) {
|
||||
Browser.openInBrowser()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ class ApplicationDirs(
|
||||
val mangaThumbnailsRoot = "$dataRoot/manga-thumbnails"
|
||||
val animeThumbnailsRoot = "$dataRoot/anime-thumbnails"
|
||||
val mangaRoot = "$dataRoot/manga"
|
||||
val webUIRoot = "$dataRoot/webUI"
|
||||
}
|
||||
|
||||
val serverConfig: ServerConfig by lazy { GlobalConfigManager.module() }
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package suwayomi.tachidesk.server.util
|
||||
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* 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/. */
|
||||
|
||||
import mu.KotlinLogging
|
||||
import net.lingala.zip4j.ZipFile
|
||||
import org.kodein.di.DI
|
||||
import org.kodein.di.conf.global
|
||||
import org.kodein.di.instance
|
||||
import suwayomi.tachidesk.server.ApplicationDirs
|
||||
import suwayomi.tachidesk.server.BuildConfig
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.security.MessageDigest
|
||||
|
||||
private val logger = KotlinLogging.logger {}
|
||||
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||
private val tmpDir = System.getProperty("java.io.tmpdir")
|
||||
|
||||
private fun ByteArray.toHex(): String = joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
|
||||
|
||||
private fun directoryMD5(fileDir: String): String {
|
||||
var sum = ""
|
||||
File(fileDir).walk().toList().sortedBy { it.path }.forEach { file ->
|
||||
if (file.isFile) {
|
||||
val md5 = MessageDigest.getInstance("MD5")
|
||||
md5.update(file.readBytes())
|
||||
val digest = md5.digest()
|
||||
sum += digest.toHex()
|
||||
}
|
||||
}
|
||||
|
||||
val md5 = MessageDigest.getInstance("MD5")
|
||||
md5.update(sum.toByteArray(StandardCharsets.UTF_8))
|
||||
val digest = md5.digest()
|
||||
return digest.toHex()
|
||||
}
|
||||
|
||||
fun setupWebUI() {
|
||||
// check if we have webUI installed and is correct version
|
||||
val webUIRevisionFile = File(applicationDirs.webUIRoot + "/revision")
|
||||
if (webUIRevisionFile.exists() && webUIRevisionFile.readText().trim() == BuildConfig.WEBUI_TAG) {
|
||||
logger.info { "WebUI Static files exists and is the correct revision" }
|
||||
logger.info { "Verifying WebUI Static files..." }
|
||||
logger.info { "md5: " + directoryMD5(applicationDirs.webUIRoot) }
|
||||
} else {
|
||||
File(applicationDirs.webUIRoot).deleteRecursively()
|
||||
|
||||
// download webUI zip
|
||||
val webUIZip = "Tachidesk-WebUI-${BuildConfig.WEBUI_TAG}.zip"
|
||||
val webUIZipPath = "$tmpDir/$webUIZip"
|
||||
val webUIZipURL = "${BuildConfig.WEBUI_REPO}/releases/download/${BuildConfig.WEBUI_TAG}/$webUIZip"
|
||||
val webUIZipFile = File(webUIZipPath)
|
||||
webUIZipFile.delete()
|
||||
|
||||
logger.info { "Downloading WebUI zip from the Internet..." }
|
||||
val data = ByteArray(1024)
|
||||
|
||||
webUIZipFile.outputStream().use { webUIZipFileOut ->
|
||||
BufferedInputStream(URL(webUIZipURL).openStream()).use { inp ->
|
||||
var totalCount = 0
|
||||
var tresh = 0
|
||||
while (true) {
|
||||
val count = inp.read(data, 0, 1024)
|
||||
totalCount += count
|
||||
if (totalCount > tresh + 10 * 1024) {
|
||||
tresh = totalCount
|
||||
print(" *")
|
||||
}
|
||||
if (count == -1)
|
||||
break
|
||||
webUIZipFileOut.write(data, 0, count)
|
||||
}
|
||||
println()
|
||||
logger.info { "Downloading WebUI Done." }
|
||||
}
|
||||
}
|
||||
|
||||
// extract webUI zip
|
||||
logger.info { "Extracting downloaded WebUI zip..." }
|
||||
File(applicationDirs.webUIRoot).mkdirs()
|
||||
ZipFile(webUIZipPath).extractAll(applicationDirs.webUIRoot)
|
||||
logger.info { "Extracting downloaded WebUI zip Done." }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user