mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-06-30 17:34:39 -05:00
download webUI on demand
This commit is contained in:
@@ -66,6 +66,9 @@ dependencies {
|
|||||||
// asm for fixing SimpleDateFormat (must match Dex2Jar version)
|
// asm for fixing SimpleDateFormat (must match Dex2Jar version)
|
||||||
implementation("org.ow2.asm:asm-debug-all:5.0.3")
|
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
|
// Source models and interfaces from Tachiyomi 1.x
|
||||||
// using source class from tachiyomi commit 9493577de27c40ce8b2b6122cc447d025e34c477 to not depend on tachiyomi.sourceapi
|
// using source class from tachiyomi commit 9493577de27c40ce8b2b6122cc447d025e34c477 to not depend on tachiyomi.sourceapi
|
||||||
// implementation("tachiyomi.sourceapi:source-api:1.1")
|
// implementation("tachiyomi.sourceapi:source-api:1.1")
|
||||||
@@ -99,6 +102,7 @@ sourceSets {
|
|||||||
|
|
||||||
// should be bumped with each stable release
|
// should be bumped with each stable release
|
||||||
val tachideskVersion = System.getenv("ProductVersion") ?: "v0.4.3"
|
val tachideskVersion = System.getenv("ProductVersion") ?: "v0.4.3"
|
||||||
|
val webUIRevisionTag = System.getenv("WebUIRevision") ?: "r19"
|
||||||
|
|
||||||
// counts commit count on master
|
// counts commit count on master
|
||||||
val tachideskRevision = runCatching {
|
val tachideskRevision = runCatching {
|
||||||
@@ -126,6 +130,11 @@ buildConfig {
|
|||||||
buildConfigField("String", "BUILD_TYPE", if (System.getenv("ProductBuildType") == "Stable") "Stable" else "Preview")
|
buildConfigField("String", "BUILD_TYPE", if (System.getenv("ProductBuildType") == "Stable") "Stable" else "Preview")
|
||||||
buildConfigField("long", "BUILD_TIME", Instant.now().epochSecond.toString())
|
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", "GITHUB", "https://github.com/Suwayomi/Tachidesk")
|
||||||
buildConfigField("String", "DISCORD", "https://discord.gg/DDZdqZWaHA")
|
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/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import io.javalin.Javalin
|
import io.javalin.Javalin
|
||||||
|
import io.javalin.http.staticfiles.Location
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.future.future
|
import kotlinx.coroutines.future.future
|
||||||
import mu.KotlinLogging
|
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.anime.AnimeAPI
|
||||||
import suwayomi.tachidesk.global.GlobalAPI
|
import suwayomi.tachidesk.global.GlobalAPI
|
||||||
import suwayomi.tachidesk.manga.MangaAPI
|
import suwayomi.tachidesk.manga.MangaAPI
|
||||||
import suwayomi.tachidesk.server.util.Browser
|
import suwayomi.tachidesk.server.util.Browser
|
||||||
|
import suwayomi.tachidesk.server.util.setupWebUI
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
@@ -24,6 +29,8 @@ import kotlin.concurrent.thread
|
|||||||
object JavalinSetup {
|
object JavalinSetup {
|
||||||
private val logger = KotlinLogging.logger {}
|
private val logger = KotlinLogging.logger {}
|
||||||
|
|
||||||
|
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||||
|
|
||||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
|
|
||||||
fun <T> future(block: suspend CoroutineScope.() -> T): CompletableFuture<T> {
|
fun <T> future(block: suspend CoroutineScope.() -> T): CompletableFuture<T> {
|
||||||
@@ -31,25 +38,19 @@ object JavalinSetup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun javalinSetup() {
|
fun javalinSetup() {
|
||||||
var hasWebUiBundled = false
|
|
||||||
|
|
||||||
val app = Javalin.create { config ->
|
val app = Javalin.create { config ->
|
||||||
try {
|
if (serverConfig.webUIEnabled) {
|
||||||
// if the bellow line throws an exception then webUI is not bundled
|
setupWebUI()
|
||||||
this::class.java.getResource("/webUI/index.html")
|
|
||||||
|
|
||||||
// no exception so we can tell javalin to serve webUI
|
logger.info { "Serving webUI static files" }
|
||||||
hasWebUiBundled = true
|
config.addStaticFiles(applicationDirs.webUIRoot, Location.EXTERNAL)
|
||||||
config.addStaticFiles("/webUI")
|
config.addSinglePageRoot("/", applicationDirs.webUIRoot + "/index.html", Location.EXTERNAL)
|
||||||
config.addSinglePageRoot("/", "/webUI/index.html")
|
|
||||||
} catch (e: RuntimeException) {
|
|
||||||
logger.warn("react build files are missing.")
|
|
||||||
hasWebUiBundled = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config.enableCorsForAllOrigins()
|
config.enableCorsForAllOrigins()
|
||||||
}.events { event ->
|
}.events { event ->
|
||||||
event.serverStarted {
|
event.serverStarted {
|
||||||
if (hasWebUiBundled && serverConfig.initialOpenInBrowserEnabled) {
|
if (serverConfig.webUIEnabled && serverConfig.initialOpenInBrowserEnabled) {
|
||||||
Browser.openInBrowser()
|
Browser.openInBrowser()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class ApplicationDirs(
|
|||||||
val mangaThumbnailsRoot = "$dataRoot/manga-thumbnails"
|
val mangaThumbnailsRoot = "$dataRoot/manga-thumbnails"
|
||||||
val animeThumbnailsRoot = "$dataRoot/anime-thumbnails"
|
val animeThumbnailsRoot = "$dataRoot/anime-thumbnails"
|
||||||
val mangaRoot = "$dataRoot/manga"
|
val mangaRoot = "$dataRoot/manga"
|
||||||
|
val webUIRoot = "$dataRoot/webUI"
|
||||||
}
|
}
|
||||||
|
|
||||||
val serverConfig: ServerConfig by lazy { GlobalConfigManager.module() }
|
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