From 32e060ceb26fe4281055343d66fde7b8f7dac624 Mon Sep 17 00:00:00 2001 From: 0xboobface <0xboobface@gmail.com> Date: Tue, 28 Aug 2018 14:48:00 +0200 Subject: [PATCH] Add update check Add update check, which checks the github API to retrieve the latest version and opens a tab, if new update is available. --- pom.xml | 20 +++ src/main/java/ctbrec/Version.java | 112 ++++++++++++++ src/main/java/ctbrec/ui/Launcher.java | 193 +++++++++++++++++++------ src/main/java/ctbrec/ui/UpdateTab.java | 21 +++ src/main/resources/version | 1 + src/test/java/ctbrec/VersionTest.java | 53 +++++++ 6 files changed, 357 insertions(+), 43 deletions(-) create mode 100644 src/main/java/ctbrec/Version.java create mode 100644 src/main/java/ctbrec/ui/UpdateTab.java create mode 100644 src/main/resources/version create mode 100644 src/test/java/ctbrec/VersionTest.java diff --git a/pom.xml b/pom.xml index a53f81da..641413ba 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,19 @@ + + + src/main/resources + false + + + src/main/resources + true + + version + + + maven-assembly-plugin @@ -156,5 +169,12 @@ jcodec 0.2.3 + + junit + junit + 4.12 + test + + diff --git a/src/main/java/ctbrec/Version.java b/src/main/java/ctbrec/Version.java new file mode 100644 index 00000000..a84e9e6d --- /dev/null +++ b/src/main/java/ctbrec/Version.java @@ -0,0 +1,112 @@ +package ctbrec; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Version implements Comparable { + int major = 0; + int minor = 0; + int revision = 0; + String designator = ""; + + public static Version of(String s) { + Pattern p = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)(?:-(.+))?"); + Matcher m = p.matcher(s); + if(m.matches()) { + Version v = new Version(); + v.major = Integer.parseInt(m.group(1)); + v.minor = Integer.parseInt(m.group(2)); + v.revision = Integer.parseInt(m.group(3)); + if(m.group(4) != null) { + v.designator = m.group(4); + } + return v; + } else { + throw new IllegalArgumentException("Version format has to be x.x.x"); + } + } + + public int getMajor() { + return major; + } + + public int getMinor() { + return minor; + } + + public int getRevision() { + return revision; + } + + public String getDesignator() { + return designator; + } + + @Override + public String toString() { + String version = major + "." + minor + "." + revision; + if(!designator.isEmpty()) { + version += "-" + designator; + } + return version; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((designator == null) ? 0 : designator.hashCode()); + result = prime * result + major; + result = prime * result + minor; + result = prime * result + revision; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Version other = (Version) obj; + if (designator == null) { + if (other.designator != null) + return false; + } else if (!designator.equals(other.designator)) + return false; + if (major != other.major) + return false; + if (minor != other.minor) + return false; + if (revision != other.revision) + return false; + return true; + } + + @Override + public int compareTo(Version o) { + int result = 0; + if(major == o.major) { + if(minor == o.minor) { + if(revision == o.revision) { + if(!designator.isEmpty() && o.designator.isEmpty()) { + result = -1; + } else if(designator.isEmpty() && !o.designator.isEmpty()) { + result = 1; + } else { + result = 0; + } + } else { + result = revision - o.revision; + } + } else { + result = minor - o.minor; + } + } else { + result = major - o.major; + } + return result; + } +} diff --git a/src/main/java/ctbrec/ui/Launcher.java b/src/main/java/ctbrec/ui/Launcher.java index 093f97ff..0e5f0cc0 100644 --- a/src/main/java/ctbrec/ui/Launcher.java +++ b/src/main/java/ctbrec/ui/Launcher.java @@ -1,14 +1,23 @@ package ctbrec.ui; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.util.List; import java.util.Objects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.squareup.moshi.JsonAdapter; +import com.squareup.moshi.Moshi; +import com.squareup.moshi.Types; + import ctbrec.Config; import ctbrec.HttpClient; +import ctbrec.Version; import ctbrec.recorder.LocalRecorder; import ctbrec.recorder.Recorder; import ctbrec.recorder.RemoteRecorder; @@ -24,57 +33,39 @@ import javafx.scene.control.TabPane; import javafx.scene.control.TabPane.TabClosingPolicy; import javafx.scene.image.Image; import javafx.stage.Stage; +import okhttp3.Request; +import okhttp3.Response; public class Launcher extends Application { private static final transient Logger LOG = LoggerFactory.getLogger(Launcher.class); public static final String BASE_URI = "https://chaturbate.com"; + private Config config; private Recorder recorder; private HttpClient client; private static HostServices hostServices; private SettingsTab settingsTab; + private TabPane tabPane = new TabPane(); @Override public void start(Stage primaryStage) throws Exception { - try { - Config.init(); - } catch (Exception e) { - Alert alert = new AutosizeAlert(Alert.AlertType.ERROR); - alert.setTitle("Whoopsie"); - alert.setContentText("Couldn't load settings."); - alert.showAndWait(); - System.exit(1); - } + loadConfig(); hostServices = getHostServices(); - Config config = Config.getInstance(); client = HttpClient.getInstance(); - if(config.getSettings().localRecording) { - recorder = new LocalRecorder(config); - } else { - recorder = new RemoteRecorder(config, client); - } - if(config.getSettings().username != null && !config.getSettings().username.isEmpty()) { - new Thread() { - @Override - public void run() { - if(!Objects.equals(System.getenv("CTBREC_DEV"), "1")) { - try { - client.login(); - } catch (IOException e1) { - LOG.warn("Initial login failed" , e1); - } - } - }; - }.start(); - } + createRecorder(); + doInitialLogin(); + createGui(primaryStage); + checkForUpdates(); + } + private void createGui(Stage primaryStage) { LOG.debug("Creating GUI"); primaryStage.setTitle("CTB Recorder"); InputStream icon = getClass().getResourceAsStream("/icon.png"); primaryStage.getIcons().add(new Image(icon)); - TabPane root = new TabPane(); - root.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { + tabPane = new TabPane(); + tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue ov, Tab from, Tab to) { if(from != null && from instanceof TabSelectionListener) { @@ -85,24 +76,24 @@ public class Launcher extends Application { } } }); - root.setTabClosingPolicy(TabClosingPolicy.SELECTED_TAB); - root.getTabs().add(createTab("Featured", BASE_URI + "/")); - root.getTabs().add(createTab("Female", BASE_URI + "/female-cams/")); - root.getTabs().add(createTab("Male", BASE_URI + "/male-cams/")); - root.getTabs().add(createTab("Couples", BASE_URI + "/couple-cams/")); - root.getTabs().add(createTab("Trans", BASE_URI + "/trans-cams/")); + tabPane.setTabClosingPolicy(TabClosingPolicy.SELECTED_TAB); + tabPane.getTabs().add(createTab("Featured", BASE_URI + "/")); + tabPane.getTabs().add(createTab("Female", BASE_URI + "/female-cams/")); + tabPane.getTabs().add(createTab("Male", BASE_URI + "/male-cams/")); + tabPane.getTabs().add(createTab("Couples", BASE_URI + "/couple-cams/")); + tabPane.getTabs().add(createTab("Trans", BASE_URI + "/trans-cams/")); FollowedTab tab = new FollowedTab("Followed", BASE_URI + "/followed-cams/"); tab.setRecorder(recorder); - root.getTabs().add(tab); + tabPane.getTabs().add(tab); RecordedModelsTab modelsTab = new RecordedModelsTab("Recording", recorder); - root.getTabs().add(modelsTab); + tabPane.getTabs().add(modelsTab); RecordingsTab recordingsTab = new RecordingsTab("Recordings", recorder, config); - root.getTabs().add(recordingsTab); + tabPane.getTabs().add(recordingsTab); settingsTab = new SettingsTab(); - root.getTabs().add(settingsTab); - root.getTabs().add(new DonateTabFx()); + tabPane.getTabs().add(settingsTab); + tabPane.getTabs().add(new DonateTabFx()); - primaryStage.setScene(new Scene(root, 1340, 800)); + primaryStage.setScene(new Scene(tabPane, 1340, 800)); primaryStage.show(); primaryStage.setOnCloseRequest((e) -> { e.consume(); @@ -137,6 +128,44 @@ public class Launcher extends Application { }); } + private void doInitialLogin() { + if(config.getSettings().username != null && !config.getSettings().username.isEmpty()) { + new Thread() { + @Override + public void run() { + if(!Objects.equals(System.getenv("CTBREC_DEV"), "1")) { + try { + client.login(); + } catch (IOException e1) { + LOG.warn("Initial login failed" , e1); + } + } + }; + }.start(); + } + } + + private void createRecorder() { + if(config.getSettings().localRecording) { + recorder = new LocalRecorder(config); + } else { + recorder = new RemoteRecorder(config, client); + } + } + + private void loadConfig() { + try { + Config.init(); + } catch (Exception e) { + Alert alert = new AutosizeAlert(Alert.AlertType.ERROR); + alert.setTitle("Whoopsie"); + alert.setContentText("Couldn't load settings."); + alert.showAndWait(); + System.exit(1); + } + config = Config.getInstance(); + } + Tab createTab(String title, String url) { ThumbOverviewTab tab = new ThumbOverviewTab(title, url, false); tab.setRecorder(recorder); @@ -150,4 +179,82 @@ public class Launcher extends Application { public static void main(String[] args) { launch(args); } + + private void checkForUpdates() { + Thread updateCheck = new Thread(() -> { + try { + String url = "https://api.github.com/repos/0xboobface/ctbrec/releases"; + Request request = new Request.Builder().url(url).build(); + Response response = client.execute(request); + if(response.isSuccessful()) { + Moshi moshi = new Moshi.Builder().build(); + Type type = Types.newParameterizedType(List.class, Release.class); + JsonAdapter> adapter = moshi.adapter(type); + List releases = adapter.fromJson(response.body().source()); + Release latest = releases.get(0); + Version latestVersion = latest.getVersion(); + Version ctbrecVersion = getVersion(); + if(latestVersion.compareTo(ctbrecVersion) > 0) { + LOG.debug("Update available {} < {}", ctbrecVersion, latestVersion); + Platform.runLater(() -> tabPane.getTabs().add(new UpdateTab(latest))); + } else { + LOG.debug("ctbrec is up-to-date {}", ctbrecVersion); + } + } + } catch (IOException e) { + LOG.warn("Update check failed {}", e.getMessage()); + } + + }); + updateCheck.setName("Update Check"); + updateCheck.setDaemon(true); + updateCheck.start(); + } + + private Version getVersion() throws IOException { + if(Objects.equals(System.getenv("CTBREC_DEV"), "1")) { + return Version.of("0.0.0"); + } else { + try(InputStream is = getClass().getClassLoader().getResourceAsStream("version")) { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + String versionString = reader.readLine(); + Version version = Version.of(versionString); + return version; + } + } + } + + static class Release { + private String name; + private String tag_name; + private String html_url; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getTagName() { + return tag_name; + } + + public void setTagName(String tagName) { + this.tag_name = tagName; + } + + public String getHtmlUrl() { + return html_url; + } + + public void setHtmlUrl(String htmlUrl) { + this.html_url = htmlUrl; + } + + public Version getVersion() { + return Version.of(tag_name); + } + } } diff --git a/src/main/java/ctbrec/ui/UpdateTab.java b/src/main/java/ctbrec/ui/UpdateTab.java new file mode 100644 index 00000000..2da070d2 --- /dev/null +++ b/src/main/java/ctbrec/ui/UpdateTab.java @@ -0,0 +1,21 @@ +package ctbrec.ui; + +import ctbrec.ui.Launcher.Release; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.Tab; +import javafx.scene.layout.VBox; + +public class UpdateTab extends Tab { + public UpdateTab(Release latest) { + setText("Update Available"); + VBox vbox = new VBox(10); + vbox.getChildren().add(new Label("New Version available " + latest.getVersion())); + Button button = new Button("Download"); + button.setOnAction((e) -> Launcher.open(latest.getHtmlUrl())); + vbox.getChildren().add(button); + vbox.setAlignment(Pos.CENTER); + setContent(vbox); + } +} diff --git a/src/main/resources/version b/src/main/resources/version new file mode 100644 index 00000000..f2ab45c3 --- /dev/null +++ b/src/main/resources/version @@ -0,0 +1 @@ +${project.version} \ No newline at end of file diff --git a/src/test/java/ctbrec/VersionTest.java b/src/test/java/ctbrec/VersionTest.java new file mode 100644 index 00000000..340f6978 --- /dev/null +++ b/src/test/java/ctbrec/VersionTest.java @@ -0,0 +1,53 @@ +package ctbrec; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class VersionTest { + @Test + public void testOf() { + Version v = Version.of("1.0.0"); + assertEquals(1, v.major); + assertEquals(0, v.minor); + assertEquals(0, v.revision); + + v = Version.of("12.123.1234"); + assertEquals(12, v.major); + assertEquals(123, v.minor); + assertEquals(1234, v.revision); + + v = Version.of("1.0.0-SNAPSHOT"); + assertEquals(1, v.major); + assertEquals(0, v.minor); + assertEquals(0, v.revision); + assertEquals("SNAPSHOT", v.designator); + } + + @Test + public void testCompareTo() { + Version a = Version.of("1.0.0"); + Version b = Version.of("1.0.0"); + assertEquals(0, a.compareTo(b)); + + a = Version.of("1.0.0"); + b = Version.of("1.0.1"); + assertEquals(-1, a.compareTo(b)); + + a = Version.of("1.0.0"); + b = Version.of("1.1.0"); + assertEquals(-1, a.compareTo(b)); + + a = Version.of("1.0.0"); + b = Version.of("2.0.0"); + assertEquals(-1, a.compareTo(b)); + + a = Version.of("1.0.0-SNAPSHOT"); + b = Version.of("1.0.0"); + assertEquals(-1, a.compareTo(b)); + + a = Version.of("1.0.0-beta1"); + b = Version.of("1.0.0"); + assertEquals(-1, a.compareTo(b)); + } +}