From 07864dc10a4bf127d274ce26780b9104baec35bb Mon Sep 17 00:00:00 2001 From: 0xboobface <0xboobface@gmail.com> Date: Sat, 27 Oct 2018 18:39:57 +0200 Subject: [PATCH] Add Camsoda tab with upcoming shows --- .../ctbrec/sites/camsoda/CamsodaShowsTab.java | 240 ++++++++++++++++++ .../sites/camsoda/CamsodaTabProvider.java | 1 + 2 files changed, 241 insertions(+) create mode 100644 src/main/java/ctbrec/sites/camsoda/CamsodaShowsTab.java diff --git a/src/main/java/ctbrec/sites/camsoda/CamsodaShowsTab.java b/src/main/java/ctbrec/sites/camsoda/CamsodaShowsTab.java new file mode 100644 index 00000000..c85033bf --- /dev/null +++ b/src/main/java/ctbrec/sites/camsoda/CamsodaShowsTab.java @@ -0,0 +1,240 @@ +package ctbrec.sites.camsoda; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.time.temporal.TemporalAccessor; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ctbrec.Model; +import ctbrec.recorder.Recorder; +import ctbrec.ui.AutosizeAlert; +import ctbrec.ui.TabSelectionListener; +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.concurrent.Task; +import javafx.geometry.Insets; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ProgressIndicator; +import javafx.scene.control.Tab; +import javafx.scene.control.TitledPane; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.VBox; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import okhttp3.Request; +import okhttp3.Response; + +public class CamsodaShowsTab extends Tab implements TabSelectionListener { + + private static final transient Logger LOG = LoggerFactory.getLogger(CamsodaShowsTab.class); + + private Camsoda camsoda; + private Recorder recorder; + private VBox showList; + private ProgressIndicator progressIndicator; + + public CamsodaShowsTab(Camsoda camsoda, Recorder recorder) { + this.camsoda = camsoda; + this.recorder = recorder; + createGui(); + } + + private void createGui() { + showList = new VBox(10); + progressIndicator = new ProgressIndicator(); + progressIndicator.setPrefSize(100, 100); + setContent(progressIndicator); + setClosable(false); + setText("Shows"); + } + + @Override + public void selected() { + Task> task = new Task>() { + @Override + protected List call() throws Exception { + String url = camsoda.getBaseUrl() + "/api/v1/user/model_shows"; + Request req = new Request.Builder().url(url).build(); + Response response = camsoda.getHttpClient().execute(req); + if (response.isSuccessful()) { + JSONObject json = new JSONObject(response.body().string()); + if (json.optInt("success") == 1) { + List boxes = new ArrayList<>(); + JSONArray results = json.getJSONArray("results"); + for (int i = 0; i < results.length(); i++) { + JSONObject result = results.getJSONObject(i); + String modelUrl = camsoda.getBaseUrl() + result.getString("url"); + String name = modelUrl.substring(modelUrl.lastIndexOf('/') + 1); + Model model = camsoda.createModel(name); + ZonedDateTime startTime = parseUtcTime(result.getString("start")); + ZonedDateTime endTime = parseUtcTime(result.getString("end")); + boxes.add(new ShowBox(model, startTime, endTime)); + } + return boxes; + } else { + LOG.error("Couldn't load upcoming camsoda shows. Unexpected response: {}", json.toString()); + showErrorDialog("Oh no!", "Couldn't load upcoming CamSoda shows", "Got an unexpected response from server"); + } + } else { + response.close(); + showErrorDialog("Oh no!", "Couldn't load upcoming CamSoda shows", "Got an unexpected response from server"); + LOG.error("Couldn't load upcoming camsoda shows: {} {}", response.code(), response.message()); + } + return Collections.emptyList(); + } + + private ZonedDateTime parseUtcTime(String string) { + DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; + TemporalAccessor ta = formatter.parse(string.replace(" UTC", "")); + Instant instant = Instant.from(ta); + return ZonedDateTime.ofInstant(instant, ZoneId.systemDefault()); + } + + @Override + protected void done() { + super.done(); + Platform.runLater(() -> { + try { + List boxes = get(); + showList.getChildren().clear(); + for (ShowBox showBox : boxes) { + showList.getChildren().add(showBox); + VBox.setMargin(showBox, new Insets(20)); + } + } catch (Exception e) { + LOG.error("Couldn't load upcoming camsoda shows", e); + } + setContent(showList); + }); + } + }; + new Thread(task).start(); + } + + @Override + public void deselected() { + } + + private void showErrorDialog(String title, String head, String msg) { + Platform.runLater(() -> { + Alert alert = new AutosizeAlert(Alert.AlertType.ERROR); + alert.setTitle(title); + alert.setHeaderText(head); + alert.setContentText(msg); + alert.showAndWait(); + }); + } + + private class ShowBox extends TitledPane { + BorderPane root = new BorderPane(); + int thumbSize = 200; + public ShowBox(Model model, ZonedDateTime startTime, ZonedDateTime endTime) { + setText(model.getName()); + setPrefHeight(268); + setContent(root); + + ImageView thumb = new ImageView(); + thumb.setPreserveRatio(true); + thumb.setFitHeight(thumbSize); + loadImage(model, thumb); + root.setLeft(new ProgressIndicator()); + BorderPane.setMargin(thumb, new Insets(10, 30, 10, 10)); + + DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.MEDIUM); + GridPane grid = new GridPane(); + + grid.add(createLabel("Start", true), 0, 0); + grid.add(createLabel(formatter.format(startTime), false), 1, 0); + grid.add(createLabel("End", true), 0, 1); + grid.add(createLabel(formatter.format(endTime), false), 1, 1); + Button record = new Button("Record Model"); + grid.add(record, 1, 2); + GridPane.setMargin(record, new Insets(10)); + root.setCenter(grid); + + record.setOnAction((evt) -> { + setCursor(Cursor.WAIT); + new Thread(() -> { + try { + recorder.startRecording(model); + } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) { + showErrorDialog("Oh no!", "Couldn't add model to the recorder", "Recorder error: " + e.getMessage()); + } finally { + Platform.runLater(() ->{ + setCursor(Cursor.DEFAULT); + }); + } + }).start(); + }); + + loadImage(model, thumb); + } + + private void loadImage(Model model, ImageView thumb) { + new Thread(() -> { + try { + String url = camsoda.getBaseUrl() + "/api/v1/user/" + model.getName(); + Request detailRequest = new Request.Builder().url(url).build(); + Response resp = camsoda.getHttpClient().execute(detailRequest); + if (resp.isSuccessful()) { + JSONObject json = new JSONObject(resp.body().string()); + if(json.optBoolean("status") && json.has("user")) { + JSONObject user = json.getJSONObject("user"); + if(user.has("settings")) { + JSONObject settings = user.getJSONObject("settings"); + if(settings.has("offline_picture")) { + Platform.runLater(() -> { + String imageUrl = settings.getString("offline_picture"); + Image img = new Image(imageUrl, 1000, thumbSize, true, true, true); + img.progressProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Number oldValue, Number newValue) { + if(newValue.doubleValue() == 1.0) { + thumb.setImage(img); + root.setLeft(thumb); + } + } + }); + + }); + } + } + } + } + resp.close(); + } catch(Exception e) { + LOG.error("Couldn't load model details", e); + } + }).start(); + } + + private Node createLabel(String string, boolean bold) { + Label label = new Label(string); + label.setPadding(new Insets(10)); + Font def = Font.getDefault(); + label.setFont(Font.font(def.getFamily(), bold ? FontWeight.BOLD : FontWeight.NORMAL, 16)); + return label; + } + } +} diff --git a/src/main/java/ctbrec/sites/camsoda/CamsodaTabProvider.java b/src/main/java/ctbrec/sites/camsoda/CamsodaTabProvider.java index 190daa89..bb431001 100644 --- a/src/main/java/ctbrec/sites/camsoda/CamsodaTabProvider.java +++ b/src/main/java/ctbrec/sites/camsoda/CamsodaTabProvider.java @@ -25,6 +25,7 @@ public class CamsodaTabProvider extends TabProvider { public List getTabs(Scene scene) { List tabs = new ArrayList<>(); tabs.add(createTab("Online", BASE_URI + "/api/v1/browse/online")); + tabs.add(new CamsodaShowsTab(camsoda, recorder)); // ChaturbateFollowedTab followedTab = new ChaturbateFollowedTab("Followed", BASE_URI + "/followed-cams/", chaturbate); // followedTab.setRecorder(recorder); // followedTab.setScene(scene);