diff --git a/src/main/java/ctbrec/recorder/LocalRecorder.java b/src/main/java/ctbrec/recorder/LocalRecorder.java index a35f2f56..2db79755 100644 --- a/src/main/java/ctbrec/recorder/LocalRecorder.java +++ b/src/main/java/ctbrec/recorder/LocalRecorder.java @@ -19,7 +19,9 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; +import java.util.concurrent.ExecutionException; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -239,6 +241,20 @@ public class LocalRecorder implements Recorder { } } + @Override + public List getOnlineModels() { + return getModelsRecording() + .stream() + .filter(m -> { + try { + return m.isOnline(); + } catch (IOException | ExecutionException | InterruptedException e) { + return false; + } + }) + .collect(Collectors.toList()); + } + @Override public void shutdown() { LOG.info("Shutting down"); diff --git a/src/main/java/ctbrec/recorder/Recorder.java b/src/main/java/ctbrec/recorder/Recorder.java index b22fe1a4..e1ffa76b 100644 --- a/src/main/java/ctbrec/recorder/Recorder.java +++ b/src/main/java/ctbrec/recorder/Recorder.java @@ -33,4 +33,10 @@ public interface Recorder { public void resumeRecording(Model model) throws IOException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException; public boolean isSuspended(Model model); + + /** + * Returns only the models from getModelsRecording(), which are online + * @return + */ + public List getOnlineModels(); } diff --git a/src/main/java/ctbrec/recorder/RemoteRecorder.java b/src/main/java/ctbrec/recorder/RemoteRecorder.java index 2e07d10c..957906c1 100644 --- a/src/main/java/ctbrec/recorder/RemoteRecorder.java +++ b/src/main/java/ctbrec/recorder/RemoteRecorder.java @@ -42,6 +42,7 @@ public class RemoteRecorder implements Recorder { private JsonAdapter modelRequestAdapter = moshi.adapter(ModelRequest.class); private List models = Collections.emptyList(); + private List onlineModels = Collections.emptyList(); private List sites; private Config config; @@ -145,39 +146,76 @@ public class RemoteRecorder implements Recorder { public void run() { running = true; while(running) { - try { - String msg = "{\"action\": \"list\"}"; - RequestBody body = RequestBody.create(JSON, msg); - Request.Builder builder = new Request.Builder() - .url("http://" + config.getSettings().httpServer + ":" + config.getSettings().httpPort + "/rec") - .post(body); - addHmacIfNeeded(msg, builder); - Request request = builder.build(); - Response response = client.execute(request); - String json = response.body().string(); - if(response.isSuccessful()) { - ModelListResponse resp = modelListResponseAdapter.fromJson(json); - if(resp.status.equals("success")) { - models = resp.models; - for (Model model : models) { - for (Site site : sites) { - if(site.isSiteForModel(model)) { - model.setSite(site); - } + syncModels(); + syncOnlineModels(); + sleep(); + } + } + + private void syncModels() { + try { + String msg = "{\"action\": \"list\"}"; + RequestBody body = RequestBody.create(JSON, msg); + Request.Builder builder = new Request.Builder() + .url("http://" + config.getSettings().httpServer + ":" + config.getSettings().httpPort + "/rec") + .post(body); + addHmacIfNeeded(msg, builder); + Request request = builder.build(); + Response response = client.execute(request); + String json = response.body().string(); + if(response.isSuccessful()) { + ModelListResponse resp = modelListResponseAdapter.fromJson(json); + if(resp.status.equals("success")) { + models = resp.models; + for (Model model : models) { + for (Site site : sites) { + if(site.isSiteForModel(model)) { + model.setSite(site); + } + } + } + lastSync = Instant.now(); + } else { + LOG.error("Server returned error: {} - {}", resp.status, resp.msg); + } + } else { + LOG.error("Couldn't synchronize with server. HTTP status: {} - {}", response.code(), json); + } + } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) { + LOG.error("Couldn't synchronize with server", e); + } + } + + private void syncOnlineModels() { + try { + String msg = "{\"action\": \"listOnline\"}"; + RequestBody body = RequestBody.create(JSON, msg); + Request.Builder builder = new Request.Builder() + .url("http://" + config.getSettings().httpServer + ":" + config.getSettings().httpPort + "/rec") + .post(body); + addHmacIfNeeded(msg, builder); + Request request = builder.build(); + Response response = client.execute(request); + String json = response.body().string(); + if(response.isSuccessful()) { + ModelListResponse resp = modelListResponseAdapter.fromJson(json); + if(resp.status.equals("success")) { + onlineModels = resp.models; + for (Model model : models) { + for (Site site : sites) { + if(site.isSiteForModel(model)) { + model.setSite(site); } } - lastSync = Instant.now(); - } else { - LOG.error("Server returned error: {} - {}", resp.status, resp.msg); } } else { - LOG.error("Couldn't synchronize with server. HTTP status: {} - {}", response.code(), json); + LOG.error("Server returned error: {} - {}", resp.status, resp.msg); } - } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) { - LOG.error("Couldn't synchronize with server", e); + } else { + LOG.error("Couldn't synchronize with server. HTTP status: {} - {}", response.code(), json); } - - sleep(); + } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) { + LOG.error("Couldn't synchronize with server", e); } } @@ -219,7 +257,6 @@ public class RemoteRecorder implements Recorder { Response response = client.execute(request); String json = response.body().string(); if(response.isSuccessful()) { - LOG.debug(json); RecordingListResponse resp = recordingListResponseAdapter.fromJson(json); if(resp.status.equals("success")) { List recordings = resp.recordings; @@ -311,4 +348,9 @@ public class RemoteRecorder implements Recorder { m.setSuspended(false); } } + + @Override + public List getOnlineModels() { + return onlineModels; + } } diff --git a/src/main/java/ctbrec/recorder/server/RecorderServlet.java b/src/main/java/ctbrec/recorder/server/RecorderServlet.java index 16a4210f..ad6f81dd 100644 --- a/src/main/java/ctbrec/recorder/server/RecorderServlet.java +++ b/src/main/java/ctbrec/recorder/server/RecorderServlet.java @@ -85,6 +85,19 @@ public class RecorderServlet extends AbstractCtbrecServlet { } resp.getWriter().write("]}"); break; + case "listOnline": + resp.getWriter().write("{\"status\": \"success\", \"msg\": \"List of online models\", \"models\": ["); + modelAdapter = new ModelJsonAdapter(); + models = recorder.getOnlineModels(); + for (Iterator iterator = models.iterator(); iterator.hasNext();) { + Model model = iterator.next(); + resp.getWriter().write(modelAdapter.toJson(model)); + if(iterator.hasNext()) { + resp.getWriter().write(','); + } + } + resp.getWriter().write("]}"); + break; case "recordings": resp.getWriter().write("{\"status\": \"success\", \"msg\": \"List of recordings\", \"recordings\": ["); JsonAdapter recAdapter = moshi.adapter(Recording.class); diff --git a/src/main/java/ctbrec/ui/JavaFxModel.java b/src/main/java/ctbrec/ui/JavaFxModel.java index 9cd8420a..7d94afb4 100644 --- a/src/main/java/ctbrec/ui/JavaFxModel.java +++ b/src/main/java/ctbrec/ui/JavaFxModel.java @@ -20,6 +20,7 @@ import javafx.beans.property.SimpleBooleanProperty; */ public class JavaFxModel implements Model { private transient BooleanProperty onlineProperty = new SimpleBooleanProperty(); + private transient BooleanProperty recordingProperty = new SimpleBooleanProperty(); private transient BooleanProperty pausedProperty = new SimpleBooleanProperty(); private Model delegate; @@ -86,6 +87,10 @@ public class JavaFxModel implements Model { return onlineProperty; } + public BooleanProperty getRecordingProperty() { + return recordingProperty; + } + public BooleanProperty getPausedProperty() { return pausedProperty; } diff --git a/src/main/java/ctbrec/ui/RecordedModelsTab.java b/src/main/java/ctbrec/ui/RecordedModelsTab.java index 3d5dfd37..d028c6c3 100644 --- a/src/main/java/ctbrec/ui/RecordedModelsTab.java +++ b/src/main/java/ctbrec/ui/RecordedModelsTab.java @@ -15,11 +15,13 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.Function; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Model; +import ctbrec.Recording; import ctbrec.recorder.Recorder; import ctbrec.sites.Site; import javafx.application.Platform; @@ -59,7 +61,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { static BlockingQueue queue = new LinkedBlockingQueue<>(); static ExecutorService threadPool = new ThreadPoolExecutor(2, 2, 10, TimeUnit.MINUTES, queue); - private ScheduledService> updateService; + private ScheduledService> updateService; private Recorder recorder; private List sites; @@ -103,12 +105,16 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { TableColumn online = new TableColumn<>("Online"); online.setCellValueFactory((cdf) -> cdf.getValue().getOnlineProperty()); online.setCellFactory(CheckBoxTableCell.forTableColumn(online)); - online.setPrefWidth(60); + online.setPrefWidth(100); + TableColumn recording = new TableColumn<>("Recording"); + recording.setCellValueFactory((cdf) -> cdf.getValue().getRecordingProperty()); + recording.setCellFactory(CheckBoxTableCell.forTableColumn(recording)); + recording.setPrefWidth(100); TableColumn paused = new TableColumn<>("Paused"); paused.setCellValueFactory((cdf) -> cdf.getValue().getPausedProperty()); paused.setCellFactory(CheckBoxTableCell.forTableColumn(paused)); - paused.setPrefWidth(60); - table.getColumns().addAll(name, url, online, paused); + paused.setPrefWidth(100); + table.getColumns().addAll(name, url, online, recording, paused); table.setItems(observableModels); table.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, event -> { popup = createContextMenu(); @@ -185,28 +191,24 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { updateService = createUpdateService(); updateService.setPeriod(new Duration(TimeUnit.SECONDS.toMillis(2))); updateService.setOnSucceeded((event) -> { - List models = updateService.getValue(); + List models = updateService.getValue(); if(models == null) { return; } - queue.clear(); - for (Model model : models) { - int index = observableModels.indexOf(model); - final JavaFxModel javaFxModel; + + for (JavaFxModel updatedModel : models) { + int index = observableModels.indexOf(updatedModel); if (index == -1) { - javaFxModel = new JavaFxModel(model); - observableModels.add(javaFxModel); + observableModels.add(updatedModel); } else { // make sure to update the JavaFX online property, so that the table cell is updated - javaFxModel = observableModels.get(index); + JavaFxModel oldModel = observableModels.get(index); + oldModel.setSuspended(updatedModel.isSuspended()); + oldModel.getOnlineProperty().set(updatedModel.getOnlineProperty().get()); + oldModel.getRecordingProperty().set(updatedModel.getRecordingProperty().get()); } - threadPool.submit(() -> { - try { - javaFxModel.getOnlineProperty().set(javaFxModel.isOnline()); - javaFxModel.setSuspended(model.isSuspended()); - } catch (IOException | ExecutionException | InterruptedException e) {} - }); } + for (Iterator iterator = observableModels.iterator(); iterator.hasNext();) { Model model = iterator.next(); if (!models.contains(model)) { @@ -219,15 +221,37 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { }); } - private ScheduledService> createUpdateService() { - ScheduledService> updateService = new ScheduledService>() { + private ScheduledService> createUpdateService() { + ScheduledService> updateService = new ScheduledService>() { @Override - protected Task> createTask() { - return new Task>() { + protected Task> createTask() { + return new Task>() { @Override - public List call() { + public List call() throws InvalidKeyException, NoSuchAlgorithmException, IllegalStateException, IOException { LOG.trace("Updating recorded models"); - return recorder.getModelsRecording(); + List recordings = recorder.getRecordings(); + List onlineModels = recorder.getOnlineModels(); + return recorder.getModelsRecording() + .stream() + .map(m -> new JavaFxModel(m)) + .peek(fxm -> { + for (Recording recording : recordings) { + if(recording.getStatus() == Recording.STATUS.RECORDING && + recording.getModelName().equals(fxm.getName())) + { + fxm.getRecordingProperty().set(true); + break; + } + } + + for (Model onlineModel : onlineModels) { + if(Objects.equals(onlineModel, fxm)) { + fxm.getOnlineProperty().set(true); + break; + } + } + }) + .collect(Collectors.toList()); } }; }