diff --git a/client/src/main/java/ctbrec/ui/CamrecApplication.java b/client/src/main/java/ctbrec/ui/CamrecApplication.java index 7218895a..70bbf4bd 100644 --- a/client/src/main/java/ctbrec/ui/CamrecApplication.java +++ b/client/src/main/java/ctbrec/ui/CamrecApplication.java @@ -1,8 +1,8 @@ package ctbrec.ui; -import static ctbrec.EventBusHolder.*; -import static ctbrec.EventBusHolder.EVENT_TYPE.*; -import static ctbrec.Model.STATUS.*; + +import static ctbrec.Model.State.*; +import static ctbrec.event.Event.Type.*; import java.io.BufferedReader; import java.io.File; @@ -14,7 +14,6 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.Map; import java.util.Objects; import org.slf4j.Logger; @@ -26,11 +25,14 @@ import com.squareup.moshi.Moshi; import com.squareup.moshi.Types; import ctbrec.Config; -import ctbrec.EventBusHolder; import ctbrec.Model; import ctbrec.OS; import ctbrec.StringUtil; import ctbrec.Version; +import ctbrec.event.Event; +import ctbrec.event.EventBusHolder; +import ctbrec.event.LogReaction; +import ctbrec.event.ModelStateChangedEvent; import ctbrec.io.HttpClient; import ctbrec.recorder.LocalRecorder; import ctbrec.recorder.OnlineMonitor; @@ -42,6 +44,7 @@ import ctbrec.sites.cam4.Cam4; import ctbrec.sites.camsoda.Camsoda; import ctbrec.sites.chaturbate.Chaturbate; import ctbrec.sites.mfc.MyFreeCams; +import ctbrec.ui.settings.SettingsTab; import javafx.application.Application; import javafx.application.HostServices; import javafx.application.Platform; @@ -143,7 +146,7 @@ public class CamrecApplication extends Application { loadStyleSheet(primaryStage, "color.css"); } loadStyleSheet(primaryStage, "style.css"); - primaryStage.getScene().getStylesheets().add("/ctbrec/ui/ColorSettingsPane.css"); + primaryStage.getScene().getStylesheets().add("/ctbrec/ui/settings/ColorSettingsPane.css"); primaryStage.getScene().getStylesheets().add("/ctbrec/ui/ThumbCell.css"); primaryStage.getScene().getStylesheets().add("/ctbrec/ui/controls/SearchBox.css"); primaryStage.getScene().getStylesheets().add("/ctbrec/ui/controls/Popover.css"); @@ -212,40 +215,43 @@ public class CamrecApplication extends Application { } private void registerAlertSystem() { - new Thread(() -> { - // try { - // // don't register before 1 minute has passed, because directly after - // // the start of ctbrec, an event for every online model would be fired, - // // which is annoying as f - // Thread.sleep(TimeUnit.MINUTES.toMillis(1)); - // } catch (InterruptedException e) { - // e.printStackTrace(); - // } - LOG.debug("Alert System registered"); - Platform.runLater(() -> { - EventBusHolder.BUS.register(new Object() { - @Subscribe - public void modelEvent(Map e) { - try { - if (Objects.equals(e.get(EVENT), MODEL_STATUS_CHANGED)) { - LOG.debug("Alert: {}", e); - Model.STATUS status = (Model.STATUS) e.get(STATUS); - Model model = (Model) e.get(MODEL); - if (status == ONLINE) { - Platform.runLater(() -> { - String header = "Model Online"; - String msg = model.getDisplayName() + " is now online"; - OS.notification(primaryStage.getTitle(), header, msg); - }); - } - } - } catch (Exception e1) { - e1.printStackTrace(); + // try { + // // don't register before 1 minute has passed, because directly after + // // the start of ctbrec, an event for every online model would be fired, + // // which is annoying as f + // Thread.sleep(TimeUnit.MINUTES.toMillis(1)); + // } catch (InterruptedException e) { + // e.printStackTrace(); + // } + EventBusHolder.BUS.register(new Object() { + @Subscribe + public void modelEvent(Event e) { + try { + if (e.getType() == MODEL_STATUS_CHANGED) { + ModelStateChangedEvent evt = (ModelStateChangedEvent) e; + Model model = evt.getModel(); + if (evt.getNewState() == ONLINE) { + String header = "Model Online"; + String msg = model.getDisplayName() + " is now online"; + OS.notification(primaryStage.getTitle(), header, msg); } } - }); - }); - }).start(); + } catch (Exception e1) { + LOG.error("Couldn't show notification", e1); + } + } + }); + + EventBusHolder.BUS.register(new Object() { + LogReaction reaction = new LogReaction(); + @Subscribe + public void modelEvent(Event e) { + reaction.reactToEvent(e); + } + }); + + + LOG.debug("Alert System registered"); } private void writeColorSchemeStyleSheet(Stage primaryStage) { diff --git a/client/src/main/java/ctbrec/ui/JavaFxModel.java b/client/src/main/java/ctbrec/ui/JavaFxModel.java index e57cc45d..63193d69 100644 --- a/client/src/main/java/ctbrec/ui/JavaFxModel.java +++ b/client/src/main/java/ctbrec/ui/JavaFxModel.java @@ -110,7 +110,7 @@ public class JavaFxModel implements Model { } @Override - public STATUS getOnlineState(boolean failFast) throws IOException, ExecutionException { + public State getOnlineState(boolean failFast) throws IOException, ExecutionException { return delegate.getOnlineState(failFast); } diff --git a/client/src/main/java/ctbrec/ui/JavaFxRecording.java b/client/src/main/java/ctbrec/ui/JavaFxRecording.java index 74926de8..e737ce8e 100644 --- a/client/src/main/java/ctbrec/ui/JavaFxRecording.java +++ b/client/src/main/java/ctbrec/ui/JavaFxRecording.java @@ -43,7 +43,7 @@ public class JavaFxRecording extends Recording { } @Override - public STATUS getStatus() { + public State getStatus() { return delegate.getStatus(); } @@ -52,7 +52,7 @@ public class JavaFxRecording extends Recording { } @Override - public void setStatus(STATUS status) { + public void setStatus(State status) { delegate.setStatus(status); switch(status) { case RECORDING: @@ -121,7 +121,7 @@ public class JavaFxRecording extends Recording { public void update(Recording updated) { if(!Config.getInstance().getSettings().localRecording) { - if(getStatus() == STATUS.DOWNLOADING && updated.getStatus() != STATUS.DOWNLOADING) { + if(getStatus() == State.DOWNLOADING && updated.getStatus() != State.DOWNLOADING) { // ignore, because the the status coming from the server is FINISHED and we are // overriding it with DOWNLOADING return; diff --git a/client/src/main/java/ctbrec/ui/RecordedModelsTab.java b/client/src/main/java/ctbrec/ui/RecordedModelsTab.java index 878c8d00..c4276e8a 100644 --- a/client/src/main/java/ctbrec/ui/RecordedModelsTab.java +++ b/client/src/main/java/ctbrec/ui/RecordedModelsTab.java @@ -369,7 +369,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { .map(m -> new JavaFxModel(m)) .peek(fxm -> { for (Recording recording : recordings) { - if(recording.getStatus() == Recording.STATUS.RECORDING && + if(recording.getStatus() == Recording.State.RECORDING && recording.getModelName().equals(fxm.getName())) { fxm.getRecordingProperty().set(true); diff --git a/client/src/main/java/ctbrec/ui/RecordingsTab.java b/client/src/main/java/ctbrec/ui/RecordingsTab.java index da238bc3..46bfb00f 100644 --- a/client/src/main/java/ctbrec/ui/RecordingsTab.java +++ b/client/src/main/java/ctbrec/ui/RecordingsTab.java @@ -1,5 +1,6 @@ package ctbrec.ui; +import static ctbrec.Recording.State.*; import static javafx.scene.control.ButtonType.*; import java.io.File; @@ -34,7 +35,7 @@ import com.iheartradio.m3u8.PlaylistException; import ctbrec.Config; import ctbrec.Recording; -import ctbrec.Recording.STATUS; +import ctbrec.Recording.State; import ctbrec.StringUtil; import ctbrec.recorder.Recorder; import ctbrec.recorder.download.MergedHlsDownload; @@ -172,7 +173,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { if(Objects.equals(System.getenv("CTBREC_DEV"), "1")) { int row = this.getTableRow().getIndex(); JavaFxRecording rec = tableViewProperty().get().getItems().get(row); - if(!rec.valueChanged() && rec.getStatus() == STATUS.RECORDING) { + if(!rec.valueChanged() && rec.getStatus() == State.RECORDING) { setStyle("-fx-alignment: CENTER-RIGHT; -fx-background-color: red"); } } @@ -212,11 +213,11 @@ public class RecordingsTab extends Tab implements TabSelectionListener { List recordings = table.getSelectionModel().getSelectedItems(); if (recordings != null && !recordings.isEmpty()) { if (event.getCode() == KeyCode.DELETE) { - if(recordings.size() > 1 || recordings.get(0).getStatus() == STATUS.FINISHED) { + if(recordings.size() > 1 || recordings.get(0).getStatus() == State.FINISHED) { delete(recordings); } } else if (event.getCode() == KeyCode.ENTER) { - if(recordings.get(0).getStatus() == STATUS.FINISHED) { + if(recordings.get(0).getStatus() == State.FINISHED) { play(recordings.get(0)); } } @@ -376,7 +377,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { openInPlayer.setOnAction((e) -> { play(recordings.get(0)); }); - if(recordings.get(0).getStatus() == STATUS.FINISHED || Config.getInstance().getSettings().localRecording) { + if(recordings.get(0).getStatus() == State.FINISHED || Config.getInstance().getSettings().localRecording) { contextMenu.getItems().add(openInPlayer); } @@ -398,7 +399,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { deleteRecording.setOnAction((e) -> { delete(recordings); }); - if(recordings.get(0).getStatus() == STATUS.FINISHED || recordings.size() > 1) { + if(recordings.get(0).getStatus() == State.FINISHED || recordings.size() > 1) { contextMenu.getItems().add(deleteRecording); } @@ -424,7 +425,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { LOG.error("Error while downloading recording", e1); } }); - if (!Config.getInstance().getSettings().localRecording && recordings.get(0).getStatus() == STATUS.FINISHED) { + if (!Config.getInstance().getSettings().localRecording && recordings.get(0).getStatus() == State.FINISHED) { contextMenu.getItems().add(downloadRecording); } @@ -463,11 +464,11 @@ public class RecordingsTab extends Tab implements TabSelectionListener { download.start(url.toString(), target, (progress) -> { Platform.runLater(() -> { if (progress == 100) { - recording.setStatus(STATUS.FINISHED); + recording.setStatus(FINISHED); recording.setProgress(-1); LOG.debug("Download finished for recording {}", recording.getPath()); } else { - recording.setStatus(STATUS.DOWNLOADING); + recording.setStatus(DOWNLOADING); recording.setProgress(progress); } }); @@ -482,7 +483,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { Platform.runLater(new Runnable() { @Override public void run() { - recording.setStatus(STATUS.FINISHED); + recording.setStatus(FINISHED); recording.setProgress(-1); } }); @@ -493,7 +494,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { t.setName("Download Thread " + recording.getPath()); t.start(); - recording.setStatus(STATUS.DOWNLOADING); + recording.setStatus(State.DOWNLOADING); recording.setProgress(0); } } @@ -563,7 +564,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { List deleted = new ArrayList<>(); for (Iterator iterator = recordings.iterator(); iterator.hasNext();) { JavaFxRecording r = iterator.next(); - if(r.getStatus() != STATUS.FINISHED) { + if(r.getStatus() != FINISHED) { continue; } try { diff --git a/client/src/main/java/ctbrec/ui/ThumbOverviewTab.java b/client/src/main/java/ctbrec/ui/ThumbOverviewTab.java index 00d3fe77..ee38177a 100644 --- a/client/src/main/java/ctbrec/ui/ThumbOverviewTab.java +++ b/client/src/main/java/ctbrec/ui/ThumbOverviewTab.java @@ -25,8 +25,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Config; -import ctbrec.EventBusHolder; import ctbrec.Model; +import ctbrec.event.EventBusHolder; import ctbrec.recorder.Recorder; import ctbrec.sites.Site; import ctbrec.sites.mfc.MyFreeCamsClient; diff --git a/client/src/main/java/ctbrec/ui/TokenLabel.java b/client/src/main/java/ctbrec/ui/TokenLabel.java index c24c40a9..2117c444 100644 --- a/client/src/main/java/ctbrec/ui/TokenLabel.java +++ b/client/src/main/java/ctbrec/ui/TokenLabel.java @@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory; import com.google.common.eventbus.Subscribe; -import ctbrec.EventBusHolder; +import ctbrec.event.EventBusHolder; import ctbrec.sites.Site; import javafx.application.Platform; import javafx.concurrent.Task; diff --git a/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java b/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java new file mode 100644 index 00000000..08e57242 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java @@ -0,0 +1,30 @@ +package ctbrec.ui.settings; + +import ctbrec.event.Event.Type; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.TitledPane; +import javafx.scene.layout.GridPane; + +public class ActionSettingsPanel extends TitledPane { + + public ActionSettingsPanel(SettingsTab settingsTab) { + setText("Actions"); + setExpanded(true); + setCollapsible(false); + createGui(); + } + + private void createGui() { + GridPane mainLayout = SettingsTab.createGridLayout(); + setContent(mainLayout); + + int row = 0; + for (Type type : Type.values()) { + Label l = new Label(type.name()); + mainLayout.add(l, 0, row); + Button b = new Button("Configure"); + mainLayout.add(b, 1, row++); + } + } +} diff --git a/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsUpdateService.java b/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsUpdateService.java index 06b1824c..d7eebd8d 100644 --- a/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsUpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/bonga/BongaCamsUpdateService.java @@ -1,6 +1,6 @@ package ctbrec.ui.sites.bonga; -import static ctbrec.Model.STATUS.*; +import static ctbrec.Model.State.*; import java.io.IOException; import java.util.ArrayList; diff --git a/client/src/main/java/ctbrec/ui/sites/camsoda/CamsodaFollowedUpdateService.java b/client/src/main/java/ctbrec/ui/sites/camsoda/CamsodaFollowedUpdateService.java index 39935366..2d225780 100644 --- a/client/src/main/java/ctbrec/ui/sites/camsoda/CamsodaFollowedUpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/camsoda/CamsodaFollowedUpdateService.java @@ -1,6 +1,6 @@ package ctbrec.ui.sites.camsoda; -import static ctbrec.Model.STATUS.*; +import static ctbrec.Model.State.*; import java.io.IOException; import java.util.ArrayList; diff --git a/common/src/main/java/ctbrec/AbstractModel.java b/common/src/main/java/ctbrec/AbstractModel.java index 2c925369..cd9803e7 100644 --- a/common/src/main/java/ctbrec/AbstractModel.java +++ b/common/src/main/java/ctbrec/AbstractModel.java @@ -21,7 +21,7 @@ public abstract class AbstractModel implements Model { private int streamUrlIndex = -1; private boolean suspended = false; protected Site site; - protected STATUS onlineState = STATUS.UNKNOWN; + protected State onlineState = State.UNKNOWN; @Override public boolean isOnline() throws IOException, ExecutionException, InterruptedException { @@ -123,11 +123,11 @@ public abstract class AbstractModel implements Model { } @Override - public STATUS getOnlineState(boolean failFast) throws IOException, ExecutionException { + public State getOnlineState(boolean failFast) throws IOException, ExecutionException { return onlineState; } - public void setOnlineState(STATUS status) { + public void setOnlineState(State status) { this.onlineState = status; } diff --git a/common/src/main/java/ctbrec/Model.java b/common/src/main/java/ctbrec/Model.java index 7df65838..77127903 100644 --- a/common/src/main/java/ctbrec/Model.java +++ b/common/src/main/java/ctbrec/Model.java @@ -14,7 +14,7 @@ import ctbrec.sites.Site; public interface Model { - public static enum STATUS { + public static enum State { ONLINE("online"), OFFLINE("offline"), AWAY("away"), @@ -23,7 +23,7 @@ public interface Model { UNKNOWN("unknown"); String display; - STATUS(String display) { + State(String display) { this.display = display; } @@ -65,7 +65,7 @@ public interface Model { public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException; - public STATUS getOnlineState(boolean failFast) throws IOException, ExecutionException; + public State getOnlineState(boolean failFast) throws IOException, ExecutionException; public List getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException; diff --git a/common/src/main/java/ctbrec/Recording.java b/common/src/main/java/ctbrec/Recording.java index cd41386d..e1417306 100644 --- a/common/src/main/java/ctbrec/Recording.java +++ b/common/src/main/java/ctbrec/Recording.java @@ -10,11 +10,11 @@ public class Recording { private Instant startDate; private String path; private boolean hasPlaylist; - private STATUS status = STATUS.UNKNOWN; + private State status = State.UNKNOWN; private int progress = -1; private long sizeInByte; - public static enum STATUS { + public static enum State { RECORDING, GENERATING_PLAYLIST, STOPPED, @@ -50,11 +50,11 @@ public class Recording { this.startDate = startDate; } - public STATUS getStatus() { + public State getStatus() { return status; } - public void setStatus(STATUS status) { + public void setStatus(State status) { this.status = status; } diff --git a/common/src/main/java/ctbrec/event/AbstractModelEvent.java b/common/src/main/java/ctbrec/event/AbstractModelEvent.java new file mode 100644 index 00000000..51da54e7 --- /dev/null +++ b/common/src/main/java/ctbrec/event/AbstractModelEvent.java @@ -0,0 +1,16 @@ +package ctbrec.event; + +import ctbrec.Model; + +public abstract class AbstractModelEvent extends Event { + + protected Model model; + + public Model getModel() { + return model; + } + + public void setModel(Model model) { + this.model = model; + } +} diff --git a/common/src/main/java/ctbrec/event/Event.java b/common/src/main/java/ctbrec/event/Event.java new file mode 100644 index 00000000..a5477e6d --- /dev/null +++ b/common/src/main/java/ctbrec/event/Event.java @@ -0,0 +1,28 @@ +package ctbrec.event; + +public abstract class Event { + + public static enum Type { + /** + * This event is fired every time the OnlineMonitor sees a model online + * It is also fired, if the model was online before. You can see it as a "still online ping". + */ + MODEL_ONLINE, + + /** + * This event is fired whenever the model's online state (Model.STATUS) changes. + */ + MODEL_STATUS_CHANGED, + + + /** + * This event is fired whenever the state of a recording changes. + */ + RECORDING_STATUS_CHANGED + } + + public abstract Type getType(); + public abstract String getName(); + public abstract String getDescription(); + public abstract String[] getExecutionParams(); +} diff --git a/common/src/main/java/ctbrec/event/EventBusHolder.java b/common/src/main/java/ctbrec/event/EventBusHolder.java index 05e42ee7..e848c892 100644 --- a/common/src/main/java/ctbrec/event/EventBusHolder.java +++ b/common/src/main/java/ctbrec/event/EventBusHolder.java @@ -1,4 +1,4 @@ -package ctbrec; +package ctbrec.event; import java.util.concurrent.Executors; @@ -12,24 +12,5 @@ public class EventBusHolder { public static final String STATUS = "status"; public static final String MODEL = "model"; - public static enum EVENT_TYPE { - /** - * This event is fired every time the OnlineMonitor sees a model online - * It is also fired, if the model was online before. You can see it as a "still online ping". - */ - MODEL_ONLINE, - - /** - * This event is fired whenever the model's online state (Model.STATUS) changes. - */ - MODEL_STATUS_CHANGED, - - - /** - * This event is fired whenever the state of a recording changes. - */ - RECORDING_STATUS_CHANGED - } - public static final EventBus BUS = new AsyncEventBus(Executors.newSingleThreadExecutor()); } diff --git a/common/src/main/java/ctbrec/event/EventReaction.java b/common/src/main/java/ctbrec/event/EventReaction.java new file mode 100644 index 00000000..8ee2df81 --- /dev/null +++ b/common/src/main/java/ctbrec/event/EventReaction.java @@ -0,0 +1,32 @@ +package ctbrec.event; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; + +public class EventReaction { + + private List> predicates = new ArrayList<>(); + private Consumer action; + + @SafeVarargs + public EventReaction(Consumer action, Predicate... predicates) { + this.action = action; + for (Predicate predicate : predicates) { + this.predicates.add(predicate); + } + } + + public void reactToEvent(Event evt) { + boolean matches = true; + for (Predicate predicate : predicates) { + if(!predicate.test(evt)) { + matches = false; + } + } + if(matches) { + action.accept(evt); + } + } +} diff --git a/common/src/main/java/ctbrec/event/LogReaction.java b/common/src/main/java/ctbrec/event/LogReaction.java new file mode 100644 index 00000000..ec5444f7 --- /dev/null +++ b/common/src/main/java/ctbrec/event/LogReaction.java @@ -0,0 +1,15 @@ +package ctbrec.event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LogReaction extends EventReaction { + + private static final transient Logger LOG = LoggerFactory.getLogger(LogReaction.class); + + public LogReaction() { + super(evt -> { + LOG.debug("LogReaction: {}", evt); + }, TypePredicate.of(Event.Type.RECORDING_STATUS_CHANGED)); + } +} diff --git a/common/src/main/java/ctbrec/event/ModelIsOnlineEvent.java b/common/src/main/java/ctbrec/event/ModelIsOnlineEvent.java new file mode 100644 index 00000000..d93e7d5d --- /dev/null +++ b/common/src/main/java/ctbrec/event/ModelIsOnlineEvent.java @@ -0,0 +1,36 @@ +package ctbrec.event; + +import ctbrec.Model; + +public class ModelIsOnlineEvent extends AbstractModelEvent { + + public ModelIsOnlineEvent(Model model) { + super.model = model; + } + + @Override + public Type getType() { + return Event.Type.MODEL_ONLINE; + } + + @Override + public String getName() { + return "Model is online"; + } + + @Override + public String getDescription() { + return "Repeatedly fired when a model is online"; + } + + @Override + public String[] getExecutionParams() { + return new String[] { + getType().toString(), + model.getDisplayName(), + model.getUrl(), + model.getSite().getName() + }; + } + +} diff --git a/common/src/main/java/ctbrec/event/ModelStateChangedEvent.java b/common/src/main/java/ctbrec/event/ModelStateChangedEvent.java new file mode 100644 index 00000000..98d681e7 --- /dev/null +++ b/common/src/main/java/ctbrec/event/ModelStateChangedEvent.java @@ -0,0 +1,59 @@ +package ctbrec.event; + +import ctbrec.Model; +import ctbrec.Model.State; + +public class ModelStateChangedEvent extends AbstractModelEvent { + + private State oldState; + private State newState; + + public ModelStateChangedEvent(Model model, Model.State oldState, Model.State newState) { + super.model = model; + this.oldState = oldState; + this.newState = newState; + } + + @Override + public Type getType() { + return Event.Type.MODEL_STATUS_CHANGED; + } + + @Override + public String getName() { + return "Model state changed"; + } + + @Override + public String getDescription() { + return "Fired when a model state changed. E.g. from OFFLINE to ONLINE"; + } + + @Override + public String[] getExecutionParams() { + return new String[] { + getType().toString(), + model.getDisplayName(), + model.getUrl(), + model.getSite().getName(), + oldState.toString(), + newState.toString() + }; + } + + public State getOldState() { + return oldState; + } + + public void setOldState(State oldState) { + this.oldState = oldState; + } + + public State getNewState() { + return newState; + } + + public void setNewState(State newState) { + this.newState = newState; + } +} diff --git a/common/src/main/java/ctbrec/event/RecordingStateChangedEvent.java b/common/src/main/java/ctbrec/event/RecordingStateChangedEvent.java new file mode 100644 index 00000000..f260552e --- /dev/null +++ b/common/src/main/java/ctbrec/event/RecordingStateChangedEvent.java @@ -0,0 +1,55 @@ +package ctbrec.event; + +import java.io.File; +import java.time.Instant; + +import ctbrec.Model; +import ctbrec.Recording.State; + +public class RecordingStateChangedEvent extends AbstractModelEvent { + + private File path; + private State newState; + private Instant startTime; + + public RecordingStateChangedEvent(File recording, State newState, Model model, Instant startTime) { + super.model = model; + this.path = recording; + this.newState = newState; + this.startTime = startTime; + } + + @Override + public Type getType() { + return Event.Type.RECORDING_STATUS_CHANGED; + } + + @Override + public String getName() { + return "Recording state changed"; + } + + @Override + public String getDescription() { + return "Fired when a recording state changed. E.g. from RECORDING to STOPPED"; + } + + @Override + public String[] getExecutionParams() { + return new String[] { + getType().toString(), + path.getAbsolutePath(), + newState.toString(), + model.getDisplayName(), + model.getSite().getName(), + model.getUrl(), + Long.toString(startTime.getEpochSecond()) + }; + } + + @Override + public String toString() { + return "RecordingStateChanged[" + newState + "," + model.getDisplayName() + "," + path + "]"; + } + +} diff --git a/common/src/main/java/ctbrec/event/TypePredicate.java b/common/src/main/java/ctbrec/event/TypePredicate.java new file mode 100644 index 00000000..e84be4df --- /dev/null +++ b/common/src/main/java/ctbrec/event/TypePredicate.java @@ -0,0 +1,23 @@ +package ctbrec.event; + +import java.util.function.Predicate; + +import ctbrec.event.Event.Type; + +public class TypePredicate implements Predicate { + + private Type type; + + private TypePredicate(Type type) { + this.type = type; + } + + @Override + public boolean test(Event evt) { + return evt.getType() == type; + } + + public static TypePredicate of(Type type) { + return new TypePredicate(type); + } +} diff --git a/common/src/main/java/ctbrec/io/StreamRedirectThread.java b/common/src/main/java/ctbrec/io/StreamRedirectThread.java index 07d88485..2ff52c9c 100644 --- a/common/src/main/java/ctbrec/io/StreamRedirectThread.java +++ b/common/src/main/java/ctbrec/io/StreamRedirectThread.java @@ -26,7 +26,7 @@ public class StreamRedirectThread implements Runnable { while(in != null && (length = in.read(buffer)) >= 0) { out.write(buffer, 0, length); } - LOG.debug("Stream redirect thread ended"); + LOG.trace("Stream redirect thread ended"); } catch(Exception e) { LOG.error("Couldn't redirect stream: {}", e.getLocalizedMessage()); } diff --git a/common/src/main/java/ctbrec/recorder/LocalRecorder.java b/common/src/main/java/ctbrec/recorder/LocalRecorder.java index 85309816..2cd50878 100644 --- a/common/src/main/java/ctbrec/recorder/LocalRecorder.java +++ b/common/src/main/java/ctbrec/recorder/LocalRecorder.java @@ -1,8 +1,7 @@ package ctbrec.recorder; -import static ctbrec.EventBusHolder.*; -import static ctbrec.EventBusHolder.EVENT_TYPE.*; -import static ctbrec.Recording.STATUS.*; +import static ctbrec.Recording.State.*; +import static ctbrec.event.Event.Type.*; import java.io.File; import java.io.FilenameFilter; @@ -24,7 +23,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; -import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; @@ -38,11 +36,14 @@ import com.iheartradio.m3u8.ParseException; import com.iheartradio.m3u8.PlaylistException; import ctbrec.Config; -import ctbrec.EventBusHolder; import ctbrec.Model; import ctbrec.OS; import ctbrec.Recording; -import ctbrec.Recording.STATUS; +import ctbrec.Recording.State; +import ctbrec.event.Event; +import ctbrec.event.EventBusHolder; +import ctbrec.event.ModelIsOnlineEvent; +import ctbrec.event.RecordingStateChangedEvent; import ctbrec.io.HttpClient; import ctbrec.io.StreamRedirectThread; import ctbrec.recorder.PlaylistGenerator.InvalidPlaylistException; @@ -97,10 +98,11 @@ public class LocalRecorder implements Recorder { private void registerEventBusListener() { EventBusHolder.BUS.register(new Object() { @Subscribe - public void modelEvent(Map e) { + public void modelEvent(Event e) { try { - if (Objects.equals(e.get(EVENT), MODEL_ONLINE)) { - Model model = (Model) e.get(MODEL); + if (e.getType() == MODEL_ONLINE) { + ModelIsOnlineEvent evt = (ModelIsOnlineEvent) e; + Model model = evt.getModel(); if(!isSuspended(model) && !recordingProcesses.containsKey(model)) { startRecordingProcess(model); } @@ -198,7 +200,6 @@ public class LocalRecorder implements Recorder { } } }.start(); - fireRecordingStateChanged(model, RECORDING); } private void stopRecordingProcess(Model model) { @@ -208,7 +209,7 @@ public class LocalRecorder implements Recorder { if(!Config.isServerMode()) { postprocess(download); } - fireRecordingStateChanged(model, FINISHED); + fireRecordingStateChanged(download.getTarget(), FINISHED, model, download.getStartTime()); } private void postprocess(Download download) { @@ -388,7 +389,7 @@ public class LocalRecorder implements Recorder { } else { postprocess(d); } - fireRecordingStateChanged(m, FINISHED); // TODO fire all the events + fireRecordingStateChanged(d.getTarget(), FINISHED, m, d.getStartTime()); // TODO fire all the events } } for (Model m : restart) { @@ -439,13 +440,9 @@ public class LocalRecorder implements Recorder { } } - private void fireRecordingStateChanged(Model model, Recording.STATUS status) { - Map evt = new HashMap<>(); - evt.put(EVENT, RECORDING_STATUS_CHANGED); - evt.put(STATUS, status); - evt.put(MODEL, model); + private void fireRecordingStateChanged(File path, Recording.State newState, Model model, Instant startTime) { + RecordingStateChangedEvent evt = new RecordingStateChangedEvent(path, newState, model, startTime); EventBusHolder.BUS.post(evt); - LOG.debug("Event fired {}", evt); } private class PostProcessingTrigger extends Thread { @@ -532,7 +529,7 @@ public class LocalRecorder implements Recorder { return recordings; } - private STATUS getStatus(Recording recording) { + private State getStatus(Recording recording) { File absolutePath = new File(Config.getInstance().getSettings().recordingsDir, recording.getPath()); PlaylistGenerator playlistGenerator = playlistGenerators.get(absolutePath); diff --git a/common/src/main/java/ctbrec/recorder/OnlineMonitor.java b/common/src/main/java/ctbrec/recorder/OnlineMonitor.java index 5ec98567..15370194 100644 --- a/common/src/main/java/ctbrec/recorder/OnlineMonitor.java +++ b/common/src/main/java/ctbrec/recorder/OnlineMonitor.java @@ -1,8 +1,6 @@ package ctbrec.recorder; -import static ctbrec.EventBusHolder.*; -import static ctbrec.EventBusHolder.EVENT_TYPE.*; -import static ctbrec.Model.STATUS.*; +import static ctbrec.Model.State.*; import java.io.InterruptedIOException; import java.net.SocketTimeoutException; @@ -18,9 +16,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Config; -import ctbrec.EventBusHolder; import ctbrec.Model; -import ctbrec.Model.STATUS; +import ctbrec.event.EventBusHolder; +import ctbrec.event.ModelIsOnlineEvent; +import ctbrec.event.ModelStateChangedEvent; import ctbrec.io.HttpException; public class OnlineMonitor extends Thread { @@ -30,7 +29,7 @@ public class OnlineMonitor extends Thread { private volatile boolean running = false; private Recorder recorder; - private Map states = new HashMap<>(); + private Map states = new HashMap<>(); public OnlineMonitor(Recorder recorder) { this.recorder = recorder; @@ -57,13 +56,13 @@ public class OnlineMonitor extends Thread { for (Model model : models) { try { if(model.isOnline(IGNORE_CACHE)) { - fireModelOnline(model); + EventBusHolder.BUS.post(new ModelIsOnlineEvent(model)); } - STATUS state = model.getOnlineState(false); - STATUS oldState = states.getOrDefault(model, UNKNOWN); + Model.State state = model.getOnlineState(false); + Model.State oldState = states.getOrDefault(model, UNKNOWN); states.put(model, state); if(state != oldState) { - fireModelOnlineStateChanged(model, oldState, state); + EventBusHolder.BUS.post(new ModelStateChangedEvent(model, oldState, state)); } } catch (HttpException e) { LOG.error("Couldn't check if model {} is online. HTTP Response: {} - {}", @@ -98,22 +97,6 @@ public class OnlineMonitor extends Thread { LOG.debug(getName() + " terminated"); } - private void fireModelOnline(Model model) { - Map evt = new HashMap<>(); - evt.put(EVENT, MODEL_ONLINE); - evt.put(MODEL, model); - EventBusHolder.BUS.post(evt); - } - - private void fireModelOnlineStateChanged(Model model, STATUS oldStatus, STATUS newStatus) { - Map evt = new HashMap<>(); - evt.put(EVENT, MODEL_STATUS_CHANGED); - evt.put(STATUS, newStatus); - evt.put(OLD, oldStatus); - evt.put(MODEL, model); - EventBusHolder.BUS.post(evt); - } - public void shutdown() { running = false; interrupt(); diff --git a/common/src/main/java/ctbrec/recorder/RemoteRecorder.java b/common/src/main/java/ctbrec/recorder/RemoteRecorder.java index 4056bdc7..dc56188e 100644 --- a/common/src/main/java/ctbrec/recorder/RemoteRecorder.java +++ b/common/src/main/java/ctbrec/recorder/RemoteRecorder.java @@ -18,10 +18,10 @@ import com.squareup.moshi.JsonAdapter; import com.squareup.moshi.Moshi; import ctbrec.Config; -import ctbrec.EventBusHolder; import ctbrec.Hmac; import ctbrec.Model; import ctbrec.Recording; +import ctbrec.event.EventBusHolder; import ctbrec.io.HttpClient; import ctbrec.io.HttpException; import ctbrec.io.InstantJsonAdapter; diff --git a/common/src/main/java/ctbrec/recorder/download/HlsDownload.java b/common/src/main/java/ctbrec/recorder/download/HlsDownload.java index c2720b16..94aa5a4d 100644 --- a/common/src/main/java/ctbrec/recorder/download/HlsDownload.java +++ b/common/src/main/java/ctbrec/recorder/download/HlsDownload.java @@ -1,5 +1,7 @@ package ctbrec.recorder.download; +import static ctbrec.Recording.State.*; + import java.io.EOFException; import java.io.File; import java.io.FileNotFoundException; @@ -24,6 +26,8 @@ import com.iheartradio.m3u8.PlaylistException; import ctbrec.Config; import ctbrec.Model; +import ctbrec.event.EventBusHolder; +import ctbrec.event.RecordingStateChangedEvent; import ctbrec.io.HttpClient; import ctbrec.io.HttpException; import okhttp3.Request; @@ -54,6 +58,10 @@ public class HlsDownload extends AbstractHlsDownload { throw new IOException(model.getName() +"'s room is not public"); } + // let the world know, that we are recording now + RecordingStateChangedEvent evt = new RecordingStateChangedEvent(getTarget(), RECORDING, model, getStartTime()); + EventBusHolder.BUS.post(evt); + String segments = getSegmentPlaylistUrl(model); if(segments != null) { if (!Files.exists(downloadDir, LinkOption.NOFOLLOW_LINKS)) { diff --git a/common/src/main/java/ctbrec/recorder/download/MergedHlsDownload.java b/common/src/main/java/ctbrec/recorder/download/MergedHlsDownload.java index e876af62..e298d48a 100644 --- a/common/src/main/java/ctbrec/recorder/download/MergedHlsDownload.java +++ b/common/src/main/java/ctbrec/recorder/download/MergedHlsDownload.java @@ -1,5 +1,6 @@ package ctbrec.recorder.download; +import static ctbrec.Recording.State.*; import static java.nio.file.StandardOpenOption.*; import java.io.ByteArrayInputStream; @@ -44,6 +45,8 @@ import com.iheartradio.m3u8.PlaylistException; import ctbrec.Config; import ctbrec.Hmac; import ctbrec.Model; +import ctbrec.event.EventBusHolder; +import ctbrec.event.RecordingStateChangedEvent; import ctbrec.io.HttpClient; import ctbrec.io.HttpException; import ctbrec.recorder.ProgressListener; @@ -126,6 +129,11 @@ public class MergedHlsDownload extends AbstractHlsDownload { splitRecStartTime = ZonedDateTime.now(); super.model = model; targetFile = Config.getInstance().getFileForRecording(model); + + // let the world know, that we are recording now + RecordingStateChangedEvent evt = new RecordingStateChangedEvent(getTarget(), RECORDING, model, getStartTime()); + EventBusHolder.BUS.post(evt); + String segments = getSegmentPlaylistUrl(model); mergeThread = createMergeThread(targetFile, null, true); mergeThread.start(); diff --git a/common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java b/common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java index 8414adda..bd891eba 100644 --- a/common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java +++ b/common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java @@ -1,6 +1,6 @@ package ctbrec.sites.bonga; -import static ctbrec.Model.STATUS.*; +import static ctbrec.Model.State.*; import java.io.IOException; import java.io.InputStream; @@ -85,7 +85,7 @@ public class BongaCamsModel extends AbstractModel { } @Override - public STATUS getOnlineState(boolean failFast) throws IOException, ExecutionException { + public State getOnlineState(boolean failFast) throws IOException, ExecutionException { if(failFast) { return onlineState; } else { @@ -97,7 +97,7 @@ public class BongaCamsModel extends AbstractModel { } @Override - public void setOnlineState(STATUS onlineState) { + public void setOnlineState(State onlineState) { this.onlineState = onlineState; } diff --git a/common/src/main/java/ctbrec/sites/cam4/Cam4Model.java b/common/src/main/java/ctbrec/sites/cam4/Cam4Model.java index 680b0d59..4b9c4842 100644 --- a/common/src/main/java/ctbrec/sites/cam4/Cam4Model.java +++ b/common/src/main/java/ctbrec/sites/cam4/Cam4Model.java @@ -1,6 +1,6 @@ package ctbrec.sites.cam4; -import static ctbrec.Model.STATUS.*; +import static ctbrec.Model.State.*; import java.io.IOException; import java.io.InputStream; @@ -109,7 +109,7 @@ public class Cam4Model extends AbstractModel { } @Override - public STATUS getOnlineState(boolean failFast) throws IOException, ExecutionException { + public State getOnlineState(boolean failFast) throws IOException, ExecutionException { if(failFast) { return onlineState; } else { diff --git a/common/src/main/java/ctbrec/sites/camsoda/CamsodaModel.java b/common/src/main/java/ctbrec/sites/camsoda/CamsodaModel.java index 12421216..36bd9019 100644 --- a/common/src/main/java/ctbrec/sites/camsoda/CamsodaModel.java +++ b/common/src/main/java/ctbrec/sites/camsoda/CamsodaModel.java @@ -1,6 +1,6 @@ package ctbrec.sites.camsoda; -import static ctbrec.Model.STATUS.*; +import static ctbrec.Model.State.*; import java.io.IOException; import java.io.InputStream; @@ -102,7 +102,7 @@ public class CamsodaModel extends AbstractModel { } @Override - public STATUS getOnlineState(boolean failFast) throws IOException, ExecutionException { + public State getOnlineState(boolean failFast) throws IOException, ExecutionException { if(failFast) { return onlineState; } else { diff --git a/common/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java b/common/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java index faad4fd8..73531926 100644 --- a/common/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java +++ b/common/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java @@ -1,6 +1,6 @@ package ctbrec.sites.chaturbate; -import static ctbrec.Model.STATUS.*; +import static ctbrec.Model.State.*; import java.io.IOException; import java.util.ArrayList; @@ -76,12 +76,12 @@ public class ChaturbateModel extends AbstractModel { getChaturbate().streamInfoCache.invalidate(getName()); } - public STATUS getOnlineState() throws IOException, ExecutionException { + public State getOnlineState() throws IOException, ExecutionException { return getOnlineState(false); } @Override - public STATUS getOnlineState(boolean failFast) throws IOException, ExecutionException { + public State getOnlineState(boolean failFast) throws IOException, ExecutionException { if(failFast) { StreamInfo info = getChaturbate().streamInfoCache.getIfPresent(getName()); setOnlineStateByRoomStatus(info.room_status); @@ -109,11 +109,11 @@ public class ChaturbateModel extends AbstractModel { onlineState = AWAY; break; case "group": - onlineState = STATUS.GROUP; + onlineState = State.GROUP; break; default: LOG.debug("Unknown show type {}", room_status); - onlineState = STATUS.UNKNOWN; + onlineState = State.UNKNOWN; } } } diff --git a/common/src/main/java/ctbrec/sites/mfc/MyFreeCamsClient.java b/common/src/main/java/ctbrec/sites/mfc/MyFreeCamsClient.java index c9317abf..c1bf7fea 100644 --- a/common/src/main/java/ctbrec/sites/mfc/MyFreeCamsClient.java +++ b/common/src/main/java/ctbrec/sites/mfc/MyFreeCamsClient.java @@ -601,7 +601,7 @@ public class MyFreeCamsClient { String name = json.getString("nm"); MyFreeCamsModel model = mfc.createModel(name); model.setUid(json.getInt("uid")); - model.setState(State.of(json.getInt("vs"))); + model.setMfcState(State.of(json.getInt("vs"))); String uid = Integer.toString(model.getUid()); String uidStart = uid.substring(0, 3); String previewUrl = "https://img.mfcimg.com/photos2/"+uidStart+'/'+uid+"/avatar.90x90.jpg"; diff --git a/common/src/main/java/ctbrec/sites/mfc/MyFreeCamsModel.java b/common/src/main/java/ctbrec/sites/mfc/MyFreeCamsModel.java index 0c30428d..4e07f0cd 100644 --- a/common/src/main/java/ctbrec/sites/mfc/MyFreeCamsModel.java +++ b/common/src/main/java/ctbrec/sites/mfc/MyFreeCamsModel.java @@ -46,7 +46,7 @@ public class MyFreeCamsModel extends AbstractModel { private String hlsUrl; private double camScore; private int viewerCount; - private State state; + private ctbrec.sites.mfc.State state; private int resolution[] = new int[2]; /** @@ -61,7 +61,7 @@ public class MyFreeCamsModel extends AbstractModel { @Override public boolean isOnline() throws IOException, ExecutionException, InterruptedException { MyFreeCamsClient.getInstance().update(this); - return state == State.ONLINE; + return state == ctbrec.sites.mfc.State.ONLINE; } @Override @@ -70,27 +70,27 @@ public class MyFreeCamsModel extends AbstractModel { } @Override - public STATUS getOnlineState(boolean failFast) throws IOException, ExecutionException { + public State getOnlineState(boolean failFast) throws IOException, ExecutionException { if(state == null) { - return STATUS.UNKNOWN; + return State.UNKNOWN; } switch(state) { case ONLINE: case RECORDING: - return ctbrec.Model.STATUS.ONLINE; + return ctbrec.Model.State.ONLINE; case AWAY: - return ctbrec.Model.STATUS.AWAY; + return ctbrec.Model.State.AWAY; case PRIVATE: - return ctbrec.Model.STATUS.PRIVATE; + return ctbrec.Model.State.PRIVATE; case GROUP_SHOW: - return ctbrec.Model.STATUS.GROUP; + return ctbrec.Model.State.GROUP; case OFFLINE: case CAMOFF: - return ctbrec.Model.STATUS.OFFLINE; + return ctbrec.Model.State.OFFLINE; default: LOG.debug("State {} is not mapped", this.state); - return ctbrec.Model.STATUS.UNKNOWN; + return ctbrec.Model.State.UNKNOWN; } } @@ -233,7 +233,7 @@ public class MyFreeCamsModel extends AbstractModel { this.camScore = camScore; } - public void setState(State state) { + public void setMfcState(ctbrec.sites.mfc.State state) { this.state = state; } @@ -249,7 +249,7 @@ public class MyFreeCamsModel extends AbstractModel { public void update(SessionState state, String streamUrl) { uid = Integer.parseInt(state.getUid().toString()); setName(state.getNm()); - setState(State.of(state.getVs())); + setMfcState(ctbrec.sites.mfc.State.of(state.getVs())); setStreamUrl(streamUrl); Optional camScore = Optional.ofNullable(state.getM()).map(m -> m.getCamscore()); setCamScore(camScore.orElse(0.0)); @@ -258,7 +258,7 @@ public class MyFreeCamsModel extends AbstractModel { String uid = state.getUid().toString(); String uidStart = uid.substring(0, 3); String previewUrl = "https://img.mfcimg.com/photos2/"+uidStart+'/'+uid+"/avatar.300x300.jpg"; - if(MyFreeCamsModel.this.state == State.ONLINE) { + if(MyFreeCamsModel.this.state == ctbrec.sites.mfc.State.ONLINE) { try { previewUrl = getLivePreviewUrl(state); } catch(Exception e) {