Skip to content
This repository has been archived by the owner on Dec 12, 2020. It is now read-only.

feat: use Github Release API for updater and changelog #122

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ dependencies {
implementation("org.apache.httpcomponents", "httpmime", "4.5.+")
implementation("com.google.apis", "google-api-services-sheets", "v4-rev20190508-1.30.1")

implementation("com.beust", "klaxon", "5.2")

val junitVersion = "5.5.0"
testImplementation("org.junit.jupiter", "junit-jupiter-api", junitVersion)
testRuntimeOnly("org.junit.jupiter", "junit-jupiter-engine", junitVersion)
Expand Down
200 changes: 37 additions & 163 deletions src/main/xerus/monstercat/MonsterUtilities.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package xerus.monstercat

import com.beust.klaxon.JsonArray
import com.beust.klaxon.JsonObject
import com.beust.klaxon.Parser
import com.google.api.client.json.Json
import com.google.api.client.json.JsonFactory
import com.google.api.client.json.jackson2.JacksonFactory
import javafx.application.Platform
import javafx.concurrent.Task
import javafx.event.EventHandler
Expand Down Expand Up @@ -38,7 +44,10 @@ import xerus.monstercat.api.DiscordRPC
import xerus.monstercat.api.Player
import xerus.monstercat.downloader.TabDownloader
import xerus.monstercat.tabs.*
import java.awt.Desktop
import java.io.File
import java.io.InputStream
import java.net.URI
import java.net.URL
import java.net.UnknownHostException
import java.util.*
Expand Down Expand Up @@ -114,21 +123,6 @@ class MonsterUtilities(checkForUpdate: Boolean): JFXMessageDisplay {
} else {
GlobalScope.launch {
logger.info("New version! Now running $currentVersion, previously " + Settings.LASTVERSION())
val f = Settings.DELETE()
if(f.exists()) {
logger.info("Deleting older version $f...")
val time = currentSeconds()
var res: Boolean
do {
res = f.delete()
} while(!res && time + 10 > currentSeconds())
if(res) {
Settings.DELETE.clear()
logger.info("Deleted $f!")
} else {
logger.warn("Couldn't delete older version residing in $f")
}
}
Settings.LASTVERSION.put(currentVersion)
}
showChangelog()
Expand All @@ -146,31 +140,36 @@ class MonsterUtilities(checkForUpdate: Boolean): JFXMessageDisplay {

private fun String.devVersion() = takeIf { it.startsWith("dev") }?.split('v', '-')?.getOrNull(1)?.toIntOrNull()

suspend fun fetchJson(url: String) = Parser.default().parse(URL(url).openConnection().getInputStream())

fun checkForUpdate(userControlled: Boolean = false, unstable: Boolean = isUnstable) {
GlobalScope.launch {
try {
val latestVersion = URL("http://monsterutilities.bplaced.net/downloads/" + if(unstable) "unstable" else "latest").openConnection().getInputStream().reader().readLines().firstOrNull()
logger.info("Latest version: $latestVersion")
val jsonObject = fetchJson("https://api.github.com/repos/Xerus2000/monsterutilities/releases/latest") as? JsonObject
val latestVersion = jsonObject?.string("tag_name")
val versionName = jsonObject?.string("name")
val githubReleaseUrl = jsonObject?.string("html_url") ?: "https://github.com/xerus2000/monsterutilities/releases"
if(latestVersion == null || latestVersion.length > 50 || latestVersion == currentVersion || (!userControlled && latestVersion == Settings.IGNOREVERSION()) || latestVersion.devVersion()?.let { currentVersion.devVersion()!! > it } == true) {
if(userControlled)
showMessage("No update found!", "Updater", Alert.AlertType.INFORMATION)
return@launch
}
if(unstable)
update(latestVersion, true)
else
onFx {
val dialog = showAlert(Alert.AlertType.CONFIRMATION, "Updater", null, "New version $latestVersion available! Update now?", ButtonType.YES, ButtonType("Not now", ButtonBar.ButtonData.NO), ButtonType("Ignore this update", ButtonBar.ButtonData.CANCEL_CLOSE))
dialog.stage.icons.setAll(Image("img/updater.png"))
dialog.resultProperty().listen { type ->
when(type.buttonData) {
ButtonBar.ButtonData.YES -> update(latestVersion)
ButtonBar.ButtonData.CANCEL_CLOSE -> Settings.IGNOREVERSION.set(latestVersion)
else -> {
}
logger.info("Latest release: $latestVersion / $versionName")
onFx {
val dialog = showAlert(Alert.AlertType.CONFIRMATION, "Updater", "New version $latestVersion available", "\n$versionName\nUpdate now?", ButtonType.YES, ButtonType("Not now", ButtonBar.ButtonData.NO), ButtonType("Ignore this update", ButtonBar.ButtonData.CANCEL_CLOSE))
dialog.stage.icons.setAll(Image("img/updater.png"))
dialog.resultProperty().listen { type ->
when(type.buttonData) {
ButtonBar.ButtonData.YES -> {
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE))
Desktop.getDesktop().browse(URI(githubReleaseUrl))
else showAlert(Alert.AlertType.INFORMATION, "Updater", "Could not open Browser", "Please browse to the release manually:\n$githubReleaseUrl", ButtonType.OK)
}
ButtonBar.ButtonData.CANCEL_CLOSE -> Settings.IGNOREVERSION.set(latestVersion)
else -> {}
}
}
}
} catch(e: UnknownHostException) {
if(userControlled)
showMessage("No connection possible!", "Updater", Alert.AlertType.INFORMATION)
Expand All @@ -180,65 +179,6 @@ class MonsterUtilities(checkForUpdate: Boolean): JFXMessageDisplay {
}
}

private fun update(version: String, unstable: Boolean = false) {
val newFile = File(Settings.FILENAMEPATTERN().replace("%version%", version, true)).absoluteFile
logger.info("Update initiated to $newFile")
val worker = object: Task<Unit>() {
init {
updateTitle("Downloading Update")
updateMessage("Downloading ${newFile.name} to ${newFile.absoluteFile.parent}")
}

override fun call() {
val connection = URL("http://monsterutilities.bplaced.net/downloads?download&version=" + if(unstable) "unstable" else version).openConnection()
val contentLength = connection.contentLengthLong
logger.debug("Update to $version started, size ${contentLength.byteCountString()}")
connection.getInputStream().copyTo(newFile.outputStream(), true, true) {
updateProgress(it, contentLength)
isCancelled
}
if(isCancelled)
logger.info("Update cancelled, deleting $newFile: ${newFile.delete().to("Success", "FAILED")}")
}

override fun succeeded() {
if(isUnstable == unstable && codeSource.toString().endsWith(".jar")) {
val jar = File(codeSource.toURI())
logger.info("Scheduling '$jar' for delete")
Settings.DELETE.set(jar)
}
logger.warn("Exiting for update to $version!")
Settings.flush()

newFile.setExecutable(true)
val cmd = arrayOf("${System.getProperty("java.home")}/bin/java", "-jar", newFile.toString())
logger.info("Executing '${cmd.joinToString(" ")}'")
val p = Runtime.getRuntime().exec(cmd)
val exited = p.waitFor(3, TimeUnit.SECONDS)

if(!exited) {
Platform.exit()
logger.warn("Exiting $currentVersion!")
} else {
showAlert(Alert.AlertType.WARNING, "Error while updating", content = "The downloaded jar was not started successfully!")
}
}

override fun failed() {
showError(exception, "Error in the update process")
}
}
worker.launch()
checkFx {
worker.progressDialog().run {
title = "Updater"
stage.icons.setAll(Image("img/updater.png"))
show()
graphic = ImageView(Image("img/updater.png"))
}
}
}

fun showIntro() {
onFx {
val text = Label("""
Expand All @@ -257,82 +197,16 @@ class MonsterUtilities(checkForUpdate: Boolean): JFXMessageDisplay {
}

fun showChangelog() {
val c = Changelog().apply {
version("dev139", "Improved fetching, caching & processing",
"The Release fetching now works with the new pagination of the Monstercat API",
"Added a little cover art in the Player",
"Fixed naming patterns in Downloader",
"Improved cache structure",
"Squashed many small bugs")

version("dev116", "Bugfixes & Downloader aftercare",
"Updated & Expanded connect.sid instructions",
"Fixed a bug where the Player always played \"Halo Nova - The Force\"")
.change("Downloader", "Added cover icons for Releases in Downloader", "Fixed Track naming issues")

version("dev107", "Downloader Rework",
"Fixed many, many minor issues")
.change("Downloader Rework",
"Downloader now focuses on Releases first and incorporates Tracks subtly",
"\"Smart Select\" & \"Exclude already downloaded songs\" now work properly",
"Adjusted track naming patterns to enable more customization, fix parsing for more obscure titles",
"Non-downloadable songs are now properly highlighted")
.change("Improved cache", "Now saved as json", "Versioning ensures integrity")

version("dev59", "Improved Downloading and Logging")
.change("Improved Downloader, fixed Windows part files not being renamed")
.change("Reworked logging to be more transparent")

version("dev43", "Safer downloading")
.change("Downloader now creates part-files while downloading so your files are safe from crashes")
.change("Backend has been updated to cope with changes in the Monstercat API")

version("dev30", "Rework",
"Brand new shiny icons - big thanks to NocFA!", "Added intro dialog", "Automatic self-update",
"Send feedback directly from the application!", "Every Slider is now scrollable with the mouse wheel")
.change("New Downloader!",
"Can download any combinations of Releases and Tracks", "Easy filtering",
"Validates connect.sid while typing", "Two distinct filename patterns for Singles and Album tracks",
"Greatly improved pattern syntax with higher flexibility")
.change("Settings reworked",
"Multiple skins available, changeable on-the-fly", "Startup Tab can now also be the previously opened one")
.change("Catalog and Genre Tab show Genre colors")
.change("Catalog improved",
"More filtering options", "Smart column size")
.change("Player now has a slick Seekbar inspired by the website",
"It can also be controlled via scrolling (suggested by AddiVF)")
.change("Added an Audio Equalizer")
.change("Added Discord Rich Presence")

version(0, 3, "UI Rework started", "Genres are now presented as a tree",
"Music playing is better integrated", "Fixed some mistakes in the Downloader")
.change("Catalog rework", "New, extremely flexible SearchVíew added", "Visible catalog columns can now be changed on-the-fly")
.patch("Music player can now also play EPs", "Added more functionality to the Music player",
"Improved the SearchView used in the Catalog", "Added a silver skin")
.patch("Fixed a small bug in the Downloader", "Added the possibility to log to a file",
"Prevented silent crashes")
.patch("Fixed an issue that prevented the Player from playing older Remixes with featured Artists",
"Fixed an issue with the cache preventing immediate downloading")

version(0, 2, "Interactive Catalog",
"Added direct song streaming by double-clicking a Song in the Catalog")

version(0, 1, "Downloader overhaul", "Added more downloading options & prettified them", "Tweaked many Settings",
"Catalog tab is now more flexible", "Implemented dismissable infobar (Only used in the Catalog yet)")
.patch("Added Downloader continuator", "Improved the readability of the current Downloader-status and added an \"Estimated time left\"",
"Cleared up some categorising Errors, but some will stay due to mislabelings on Monstercats side", "Endless bugfixes")
.patch("Multiple Settings changed again, so another soft reset happened", "Fixed some formatting issues",
"Added capability to split off Album Mixes when downloading", "Fixed various bugs and added some logging")
.patch("Added Release caching for faster Release fetching",
"Improved Downloader view & cancellation (now with 90% less spasticity!)", "Fixed various bugs")

version(0, 0, "Offline caching", "Added Changelog", "Fixed offline Catalog caching",
"Fixed Genre Tab", "Fixed some small downloading Errors", "Improved Error handling")
.patch("Added more downloading options & prettified them", "Tweaked many Settings",
"Catalog tab is now more flexible", "Implemented dismissable infobar (Only used in the Catalog yet)")

GlobalScope.launch {
val releasesArray = fetchJson("https://api.github.com/repos/xerus2000/monsterutilities/releases") as JsonArray<*>
val c = Changelog()
releasesArray.forEach {
if (it is JsonObject) {
c.version(it.string("tag_name") ?: "", it.string("name") ?: "", it.string("body") ?: "")
}
}
onFx { c.show(App.stage) }
}
onFx { c.show(App.stage) }
}

override fun showError(error: Throwable, title: String) {
Expand Down
1 change: 0 additions & 1 deletion src/main/xerus/monstercat/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ object Settings: SettingsNode("xerus/monsterutilities") {
// Update mechanism
val LASTVERSION = create("versionLast")
val IGNOREVERSION = create("versionIgnore")
val DELETE = create("versionDelete", File(""))
val AUTOUPDATE = create("updateAutomatic", true)
val UNSTABLE = create("updateUnstable", false)

Expand Down