From 0358a35a845609ea7f4ec6f83d010d213f08e555 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 8 May 2021 14:15:39 +0200 Subject: [PATCH] Add possibility to group models through a dialog --- .../src/main/java/ctbrec/ui/JavaFxModel.java | 14 -- .../ctbrec/ui/action/AddToGroupAction.java | 183 ++++++++++++++++++ .../main/java/ctbrec/ui/controls/Dialogs.java | 52 ++++- .../java/ctbrec/ui/tabs/ThumbOverviewTab.java | 16 ++ .../src/main/java/ctbrec/AbstractModel.java | 11 -- common/src/main/java/ctbrec/Config.java | 30 +-- common/src/main/java/ctbrec/Model.java | 4 - common/src/main/java/ctbrec/ModelGroup.java | 24 ++- .../src/main/java/ctbrec/ModelGroupEntry.java | 53 ----- .../main/java/ctbrec/io/ModelJsonAdapter.java | 16 -- .../ctbrec/recorder/NextGenLocalRecorder.java | 44 +---- .../main/java/ctbrec/recorder/Recorder.java | 16 +- .../recorder/RecordingPreconditions.java | 42 ++-- .../java/ctbrec/recorder/RemoteRecorder.java | 10 +- 14 files changed, 333 insertions(+), 182 deletions(-) create mode 100644 client/src/main/java/ctbrec/ui/action/AddToGroupAction.java delete mode 100644 common/src/main/java/ctbrec/ModelGroupEntry.java diff --git a/client/src/main/java/ctbrec/ui/JavaFxModel.java b/client/src/main/java/ctbrec/ui/JavaFxModel.java index 48e31716..05a4f32b 100644 --- a/client/src/main/java/ctbrec/ui/JavaFxModel.java +++ b/client/src/main/java/ctbrec/ui/JavaFxModel.java @@ -3,7 +3,6 @@ package ctbrec.ui; import java.io.IOException; import java.time.Instant; import java.util.List; -import java.util.Optional; import java.util.concurrent.ExecutionException; import javax.xml.bind.JAXBException; @@ -14,7 +13,6 @@ import com.squareup.moshi.JsonReader; import com.squareup.moshi.JsonWriter; import ctbrec.Model; -import ctbrec.ModelGroupEntry; import ctbrec.SubsequentAction; import ctbrec.recorder.download.Download; import ctbrec.recorder.download.HttpHeaderFactory; @@ -331,16 +329,4 @@ public class JavaFxModel implements Model { public void setMarkedForLaterRecording(boolean marked) { delegate.setMarkedForLaterRecording(marked); } - - @Override - public void setModelGroup(ModelGroupEntry modelGroupEntry) { - delegate.setModelGroup(modelGroupEntry); - } - - @Override - public Optional getModelGroup() { - return delegate.getModelGroup(); - } - - } diff --git a/client/src/main/java/ctbrec/ui/action/AddToGroupAction.java b/client/src/main/java/ctbrec/ui/action/AddToGroupAction.java new file mode 100644 index 00000000..db2a5c86 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/action/AddToGroupAction.java @@ -0,0 +1,183 @@ +package ctbrec.ui.action; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import ctbrec.Config; +import ctbrec.Model; +import ctbrec.ModelGroup; +import ctbrec.StringUtil; +import ctbrec.recorder.Recorder; +import ctbrec.ui.controls.Dialogs; +import ctbrec.ui.controls.autocomplete.ObservableListSuggester; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Region; + +public class AddToGroupAction { + + private Node source; + private Model model; + private Recorder recorder; + + public AddToGroupAction(Node source, Recorder recorder, Model model) { + this.source = source; + this.recorder = recorder; + this.model = model; + } + + public void execute() { + source.setCursor(Cursor.WAIT); + try { + var dialog = new ModelGroupDialog(); + boolean ok = Dialogs.showCustomInput(source.getScene(), "Add model to group", dialog.getMainPane()); + dialog.requestFocus(); + if (ok) { + String text = dialog.getText(); + if (StringUtil.isBlank(text)) { + return; + } + Set modelGroups = Config.getInstance().getSettings().modelGroups; + Optional existingGroup = modelGroups.stream().filter(mg -> mg.getName().equalsIgnoreCase(text)).findFirst(); + if (existingGroup.isPresent()) { + existingGroup.get().add(model); + recorder.saveModelGroup(existingGroup.get()); + } else { + var group = new ModelGroup(); + group.setId(UUID.randomUUID()); + group.setName(text); + group.add(model); + modelGroups.add(group); + recorder.saveModelGroup(group); + } + } + } catch (IOException e) { + Dialogs.showError(source.getScene(), "Add model to group", "Saving model group failed", e); + } finally { + source.setCursor(Cursor.DEFAULT); + } + } + + private static class ModelGroupDialog { + private ComboBox comboBox; + private TextField editor; + private ObservableListSuggester suggester; + + public String getText() { + return comboBox.getEditor().getText(); + } + + public void requestFocus() { + comboBox.requestFocus(); + editor.requestFocus(); + } + + Region getMainPane() { + var dialogPane = new GridPane(); + Set modelGroups = Config.getInstance().getSettings().modelGroups; + List comboBoxItems = modelGroups.stream().map(ModelGroupListItem::new).sorted().collect(Collectors.toList()); + ObservableList comboBoxModel = FXCollections.observableArrayList(comboBoxItems); + suggester = new ObservableListSuggester(comboBoxModel); + comboBox = new ComboBox<>(comboBoxModel); + comboBox.setEditable(true); + editor = comboBox.getEditor(); + comboBox.getEditor().addEventHandler(KeyEvent.KEY_RELEASED, evt -> { + if (evt.getCode().isLetterKey() || evt.getCode().isDigitKey()) { + autocomplete(false); + } else if (evt.getCode() == KeyCode.ENTER) { + if (editor.getSelection().getLength() > 0) { + editor.selectRange(0, 0); + editor.insertText(editor.lengthProperty().get(), ":"); + editor.positionCaret(editor.lengthProperty().get()); + evt.consume(); + } + } else if (evt.getCode() == KeyCode.SPACE && evt.isControlDown()) { + autocomplete(true); + } + }); + comboBox.setPlaceholder(new Label(" type in a name to a add a new group ")); + dialogPane.add(new Label("Model group "), 0, 0); + dialogPane.add(comboBox, 1, 0); + return dialogPane; + } + + private void autocomplete(boolean fulltextSearch) { + String oldtext = getOldText(); + if(oldtext.isEmpty()) { + return; + } + + Optional match; + if (fulltextSearch) { + match = suggester.fulltext(oldtext); + } else { + match = suggester.startsWith(oldtext); + } + + if (match.isPresent()) { + editor.setText(match.get()); + int pos = oldtext.length(); + editor.positionCaret(pos); + editor.selectRange(pos, match.get().length()); + } + } + + private String getOldText() { + if(editor.getSelection().getLength() > 0) { + return editor.getText().substring(0, editor.getSelection().getStart()); + } else { + return editor.getText(); + } + } + } + + private static class ModelGroupListItem implements Comparable { + private ModelGroup modelGroup; + + public ModelGroupListItem(ModelGroup modelGroup) { + this.modelGroup = modelGroup; + } + + @Override + public String toString() { + return this.modelGroup.getName(); + } + + @Override + public int hashCode() { + return java.util.Objects.hash(modelGroup); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ModelGroupListItem other = (ModelGroupListItem) obj; + return java.util.Objects.equals(modelGroup, other.modelGroup); + } + + @Override + public int compareTo(ModelGroupListItem o) { + return this.modelGroup.getName().compareTo(o.modelGroup.getName()); + } + } +} diff --git a/client/src/main/java/ctbrec/ui/controls/Dialogs.java b/client/src/main/java/ctbrec/ui/controls/Dialogs.java index f9e3d78f..3de44bb5 100644 --- a/client/src/main/java/ctbrec/ui/controls/Dialogs.java +++ b/client/src/main/java/ctbrec/ui/controls/Dialogs.java @@ -4,16 +4,26 @@ import static javafx.scene.control.ButtonType.*; import java.io.InputStream; import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import ctbrec.Config; +import ctbrec.Model; +import ctbrec.ModelGroup; +import ctbrec.StringUtil; import ctbrec.ui.AutosizeAlert; import javafx.application.Platform; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Button; import javafx.scene.control.ButtonType; +import javafx.scene.control.ComboBox; import javafx.scene.control.Dialog; +import javafx.scene.control.Label; import javafx.scene.control.TextArea; import javafx.scene.image.Image; import javafx.scene.layout.GridPane; @@ -63,7 +73,7 @@ public class Dialogs { Dialog dialog = new Dialog<>(); dialog.setTitle(title); dialog.setHeaderText(header); - dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); + dialog.getDialogPane().getButtonTypes().addAll(OK, CANCEL); dialog.initModality(Modality.APPLICATION_MODAL); dialog.setResizable(true); InputStream icon = Dialogs.class.getResourceAsStream("/icon.png"); @@ -86,7 +96,7 @@ public class Dialogs { Platform.runLater(notes::requestFocus); dialog.setResultConverter(dialogButton -> { - if (dialogButton == ButtonType.OK) { + if (dialogButton == OK) { return notes.getText(); } return null; @@ -98,7 +108,7 @@ public class Dialogs { public static Boolean showCustomInput(Scene parent, String title, Region region) { Dialog dialog = new Dialog<>(); dialog.setTitle(title); - dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); + dialog.getDialogPane().getButtonTypes().addAll(OK, CANCEL); dialog.initModality(Modality.APPLICATION_MODAL); dialog.setResizable(true); InputStream icon = Dialogs.class.getResourceAsStream("/icon.png"); @@ -109,7 +119,7 @@ public class Dialogs { } dialog.getDialogPane().setContent(region); dialog.showAndWait(); - return dialog.getResult() == ButtonType.OK; + return dialog.getResult() == OK; } public static boolean showConfirmDialog(String title, String message, String header, Scene parent) { @@ -117,7 +127,7 @@ public class Dialogs { confirm.setTitle(title); confirm.setHeaderText(header); confirm.showAndWait(); - return confirm.getResult() == ButtonType.YES; + return confirm.getResult() == YES; } public static ButtonType showShutdownDialog(Scene parent) { @@ -134,4 +144,36 @@ public class Dialogs { confirm.showAndWait(); return confirm.getResult(); } + + public static Optional showModelGroupSelectionDialog(Scene parent, Model model) { + GridPane dialogPane = new GridPane(); + Set modelGroups = Config.getInstance().getSettings().modelGroups; + ObservableList comboBoxModel = FXCollections.observableArrayList(modelGroups); + ComboBox comboBox = new ComboBox<>(comboBoxModel); + comboBox.setEditable(true); + comboBox.setPlaceholder(new Label(" type in a name to a add a new group ")); + dialogPane.add(new Label("Model group"), 0, 0); + dialogPane.add(comboBox, 1, 0); + boolean ok = showCustomInput(parent, "Add model to group", dialogPane); + if (ok) { + String text = comboBox.getEditor().getText(); + if (StringUtil.isBlank(text)) { + return Optional.empty(); + } + Optional existingGroup = modelGroups.stream().filter(mg -> mg.getName().equalsIgnoreCase(text)).findFirst(); + if (existingGroup.isPresent()) { + existingGroup.get().add(model); + return existingGroup; + } else { + ModelGroup group = new ModelGroup(); + group.setId(UUID.randomUUID()); + group.setName(text); + group.add(model); + modelGroups.add(group); + return Optional.of(group); + } + } else { + return Optional.empty(); + } + } } diff --git a/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java b/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java index 440f1f19..d7748246 100644 --- a/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/ThumbOverviewTab.java @@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory; import ctbrec.Config; import ctbrec.GlobalThreadPool; import ctbrec.Model; +import ctbrec.ModelGroup; import ctbrec.event.EventBusHolder; import ctbrec.recorder.Recorder; import ctbrec.sites.Site; @@ -39,6 +40,7 @@ import ctbrec.ui.DesktopIntegration; import ctbrec.ui.SiteUiFactory; import ctbrec.ui.TipDialog; import ctbrec.ui.TokenLabel; +import ctbrec.ui.action.AddToGroupAction; import ctbrec.ui.action.IgnoreModelsAction; import ctbrec.ui.action.OpenRecordingsDir; import ctbrec.ui.action.SetStopDateAction; @@ -501,6 +503,11 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { MenuItem unfollow = new MenuItem("Unfollow"); unfollow.setOnAction(e -> follow(getSelectedThumbCells(cell), false)); + MenuItem addToGroup = new MenuItem("Add to group"); + addToGroup.setOnAction(e -> addToGroup(model)); + MenuItem editGroup = new MenuItem("Edit group"); + editGroup.setOnAction(e -> editGroup(model)); + MenuItem ignore = new MenuItem("Ignore"); ignore.setOnAction(e -> ignore(getSelectedThumbCells(cell))); @@ -535,6 +542,8 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { if (site.supportsTips()) { contextMenu.getItems().add(sendTip); } + Optional modelGroup = Config.getInstance().getModelGroup(model); + contextMenu.getItems().add(modelGroup.isEmpty() ? addToGroup : editGroup); contextMenu.getItems().addAll(copyUrl, openInBrowser, ignore, refresh, openRecDir); if (model instanceof MyFreeCamsModel && Objects.equals(System.getenv("CTBREC_DEV"), "1")) { MenuItem debug = new MenuItem("debug"); @@ -545,6 +554,13 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { return contextMenu; } + private void editGroup(Model model) { + } + + private void addToGroup(Model model) { + new AddToGroupAction(this.getContent(), recorder, model).execute(); + } + private void recordLater(List list, boolean recordLater) { for (ThumbCell cell : list) { cell.recordLater(recordLater); diff --git a/common/src/main/java/ctbrec/AbstractModel.java b/common/src/main/java/ctbrec/AbstractModel.java index 577236fe..216f4c72 100644 --- a/common/src/main/java/ctbrec/AbstractModel.java +++ b/common/src/main/java/ctbrec/AbstractModel.java @@ -41,7 +41,6 @@ public abstract class AbstractModel implements Model { private Instant lastRecorded; private Instant recordUntil; private SubsequentAction recordUntilSubsequentAction; - private Optional modelGroup = Optional.empty(); // NOSONAR @Override public boolean isOnline() throws IOException, ExecutionException, InterruptedException { @@ -310,14 +309,4 @@ public abstract class AbstractModel implements Model { } return true; } - - @Override - public Optional getModelGroup() { - return modelGroup; - } - - @Override - public void setModelGroup(ModelGroupEntry modelGroup) { - this.modelGroup = Optional.ofNullable(modelGroup); - } } diff --git a/common/src/main/java/ctbrec/Config.java b/common/src/main/java/ctbrec/Config.java index f16c6ec9..11b73172 100644 --- a/common/src/main/java/ctbrec/Config.java +++ b/common/src/main/java/ctbrec/Config.java @@ -18,8 +18,8 @@ import java.util.List; import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; import java.util.UUID; +import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.slf4j.Logger; @@ -185,11 +185,11 @@ public class Config { } // 3.11.0 make Cam4 model names lower case settings.models.stream() - .filter(Cam4Model.class::isInstance) - .forEach(m -> m.setName(m.getName().toLowerCase())); + .filter(Cam4Model.class::isInstance) + .forEach(m -> m.setName(m.getName().toLowerCase())); settings.modelsIgnored.stream() - .filter(Cam4Model.class::isInstance) - .forEach(m -> m.setName(m.getName().toLowerCase())); + .filter(Cam4Model.class::isInstance) + .forEach(m -> m.setName(m.getName().toLowerCase())); // 4.1.2 reduce models ignore to store only the URL if (settings.modelsIgnored != null && !settings.modelsIgnored.isEmpty()) { settings.ignoredModels = settings.modelsIgnored.stream() @@ -230,9 +230,9 @@ public class Config { } public synchronized void save() throws IOException { - if (savingDisabled) { - return; - } + if (savingDisabled) { + return; + } Moshi moshi = new Moshi.Builder() .add(Model.class, new ModelJsonAdapter()) .add(PostProcessor.class, new PostProcessorJsonAdapter()) @@ -303,12 +303,18 @@ public class Config { public String getModelNotes(Model m) { return Config.getInstance().getSettings().modelNotes.getOrDefault(m.getUrl(), ""); } - + public void disableSaving() { - savingDisabled = true; + savingDisabled = true; } - + public void enableSaving() { - savingDisabled = false; + savingDisabled = false; + } + + public Optional getModelGroup(Model model) { + return getSettings().modelGroups.stream() + .filter(mg -> mg.getModelUrls().contains(model.getUrl())) + .findFirst(); } } diff --git a/common/src/main/java/ctbrec/Model.java b/common/src/main/java/ctbrec/Model.java index bf30ab32..551ac5c4 100644 --- a/common/src/main/java/ctbrec/Model.java +++ b/common/src/main/java/ctbrec/Model.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.io.Serializable; import java.time.Instant; import java.util.List; -import java.util.Optional; import java.util.concurrent.ExecutionException; import javax.xml.bind.JAXBException; @@ -149,7 +148,4 @@ public interface Model extends Comparable, Serializable { */ public boolean exists() throws IOException; - public void setModelGroup(ModelGroupEntry modelGroupEntry); - public Optional getModelGroup(); - } \ No newline at end of file diff --git a/common/src/main/java/ctbrec/ModelGroup.java b/common/src/main/java/ctbrec/ModelGroup.java index 4092b219..1f9d53ca 100644 --- a/common/src/main/java/ctbrec/ModelGroup.java +++ b/common/src/main/java/ctbrec/ModelGroup.java @@ -1,7 +1,6 @@ package ctbrec; import java.io.Serializable; -import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -12,7 +11,7 @@ public class ModelGroup implements Serializable { private UUID id; private String name; - private transient List models = new LinkedList<>(); + private List modelUrls = new LinkedList<>(); public UUID getId() { return id; @@ -30,12 +29,12 @@ public class ModelGroup implements Serializable { this.name = name; } - public List getModels() { - return models; + public List getModelUrls() { + return modelUrls; } - public void setModels(List models) { - this.models = models; + public void setModelUrls(List modelUrls) { + this.modelUrls = modelUrls; } @Override @@ -60,15 +59,14 @@ public class ModelGroup implements Serializable { @Override public String toString() { - return "ModelGroup [id=" + id + ", name=" + name + ", models=" + models + "]"; + return "ModelGroup [id=" + id + ", name=" + name + ", models=" + modelUrls + "]"; + } + + public void add(String modelUrl) { + modelUrls.add(modelUrl); } public void add(Model model) { - models.add(model); - Collections.sort(models, (m1, m2) -> { - int index1 = m1.getModelGroup().map(ModelGroupEntry::getIndex).orElse(0); - int index2 = m2.getModelGroup().map(ModelGroupEntry::getIndex).orElse(0); - return index1 - index2; - }); + modelUrls.add(model.getUrl()); } } diff --git a/common/src/main/java/ctbrec/ModelGroupEntry.java b/common/src/main/java/ctbrec/ModelGroupEntry.java deleted file mode 100644 index df04eb6f..00000000 --- a/common/src/main/java/ctbrec/ModelGroupEntry.java +++ /dev/null @@ -1,53 +0,0 @@ -package ctbrec; - -import java.io.Serializable; -import java.util.Objects; -import java.util.UUID; - -public class ModelGroupEntry implements Serializable { - private static final long serialVersionUID = 0L; - - private UUID id; - private int index; - - public UUID getId() { - return id; - } - - public void setId(UUID id) { - this.id = id; - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - @Override - public int hashCode() { - return Objects.hash(id, index); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ModelGroupEntry other = (ModelGroupEntry) obj; - return Objects.equals(id, other.id) && index == other.index; - } - - @Override - public String toString() { - return "ModelGroupEntry [id=" + id + ", index=" + index + "]"; - } -} diff --git a/common/src/main/java/ctbrec/io/ModelJsonAdapter.java b/common/src/main/java/ctbrec/io/ModelJsonAdapter.java index ed44bd61..7f585968 100644 --- a/common/src/main/java/ctbrec/io/ModelJsonAdapter.java +++ b/common/src/main/java/ctbrec/io/ModelJsonAdapter.java @@ -5,7 +5,6 @@ import java.lang.reflect.InvocationTargetException; import java.time.Instant; import java.util.List; import java.util.Optional; -import java.util.UUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,7 +15,6 @@ import com.squareup.moshi.JsonReader.Token; import com.squareup.moshi.JsonWriter; import ctbrec.Model; -import ctbrec.ModelGroupEntry; import ctbrec.SubsequentAction; import ctbrec.sites.Site; import ctbrec.sites.chaturbate.ChaturbateModel; @@ -71,18 +69,6 @@ public class ModelJsonAdapter extends JsonAdapter { model.setRecordUntil(Instant.ofEpochMilli(reader.nextLong())); } else if (key.equals("recordUntilSubsequentAction")) { model.setRecordUntilSubsequentAction(SubsequentAction.valueOf(reader.nextString())); - } else if (key.equals("groupId")) { - ModelGroupEntry entry = new ModelGroupEntry(); - entry.setId(UUID.fromString(reader.nextString())); - model.setModelGroup(entry); - } else if (key.equals("groupIndex")) { - model.getModelGroup().ifPresent(mg -> { - try { - mg.setIndex(reader.nextInt()); - } catch (IOException e) { - LOG.error("Error while reading model group index", e); - } - }); } else if (key.equals("siteSpecific")) { reader.beginObject(); try { @@ -128,8 +114,6 @@ public class ModelJsonAdapter extends JsonAdapter { writer.name("lastRecorded").value(model.getLastRecorded().toEpochMilli()); writer.name("recordUntil").value(model.getRecordUntil().toEpochMilli()); writer.name("recordUntilSubsequentAction").value(model.getRecordUntilSubsequentAction().name()); - writer.name("groupId").value(model.getModelGroup().map(ModelGroupEntry::getId).map(Object::toString).orElse(null)); - writer.name("groupIndex").value(model.getModelGroup().map(ModelGroupEntry::getIndex).orElse(0)); writer.name("siteSpecific"); writer.beginObject(); model.writeSiteSpecificData(writer); diff --git a/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java b/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java index 3b120c39..90a17fd0 100644 --- a/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java +++ b/common/src/main/java/ctbrec/recorder/NextGenLocalRecorder.java @@ -47,7 +47,6 @@ import com.google.common.eventbus.Subscribe; import ctbrec.Config; import ctbrec.Model; import ctbrec.ModelGroup; -import ctbrec.ModelGroupEntry; import ctbrec.Recording; import ctbrec.Recording.State; import ctbrec.event.Event; @@ -93,7 +92,6 @@ public class NextGenLocalRecorder implements Recorder { threadPoolScaler = new ThreadPoolScaler((ThreadPoolExecutor) downloadPool, 5); recordingManager = new RecordingManager(config, sites); loadModels(); - createModelGroups(); int ppThreads = config.getSettings().postProcessingThreads; ppPool = new ThreadPoolExecutor(ppThreads, ppThreads, 5, TimeUnit.MINUTES, ppQueue, createThreadFactory("PP", MIN_PRIORITY)); @@ -135,16 +133,6 @@ public class NextGenLocalRecorder implements Recorder { }); } - private void createModelGroups() { - for (Model model : models) { - if(model.getModelGroup().isPresent()) { - ModelGroupEntry groupEntry = model.getModelGroup().get(); // NOSONAR - ModelGroup group = getModelGroup(groupEntry.getId()); - group.add(model); - } - } - } - private void startCompletionHandler() { downloadCompletionPool.submit(() -> { while (!Thread.currentThread().isInterrupted()) { @@ -796,38 +784,26 @@ public class NextGenLocalRecorder implements Recorder { } @Override - public ModelGroup getModelGroup(UUID id) { + public Optional getModelGroup(UUID id) { for (ModelGroup group : getModelGroups()) { if (Objects.equals(group.getId(), id)) { - return group; + return Optional.of(group); } } - throw new NoSuchElementException("ModelGroup with id " + id + " not found"); + return Optional.empty(); } @Override - public ModelGroup createModelGroup(String name) { - ModelGroup group = new ModelGroup(); - group.setName(name); - config.getSettings().modelGroups.add(group); - try { - config.save(); - } catch (IOException e) { - LOG.error("Couldn't save new model group", e); - } - return group; + public void saveModelGroup(ModelGroup group) throws IOException { + Set modelGroups = config.getSettings().modelGroups; + modelGroups.remove(group); + modelGroups.add(group); + config.save(); } @Override - public void deleteModelGroup(ModelGroup group) { - for (Model model : group.getModels()) { - model.setModelGroup(null); - } + public void deleteModelGroup(ModelGroup group) throws IOException { config.getSettings().modelGroups.remove(group); - try { - config.save(); - } catch (IOException e) { - LOG.error("Couldn't delete model group", e); - } + config.save(); } } diff --git a/common/src/main/java/ctbrec/recorder/Recorder.java b/common/src/main/java/ctbrec/recorder/Recorder.java index f61105d3..4ff830a2 100644 --- a/common/src/main/java/ctbrec/recorder/Recorder.java +++ b/common/src/main/java/ctbrec/recorder/Recorder.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -153,7 +154,16 @@ public interface Recorder { public int getModelCount(); public Set getModelGroups(); - public ModelGroup createModelGroup(String name); - public ModelGroup getModelGroup(UUID uuid); - public void deleteModelGroup(ModelGroup group); + + /** + * Saves a model group. If the group already exists, it will be overwritten. Otherwise it will + * be saved as a new group. + * @param group + * @throws IOException + */ + public void saveModelGroup(ModelGroup group) throws IOException; + + public Optional getModelGroup(UUID uuid); + + public void deleteModelGroup(ModelGroup group) throws IOException; } diff --git a/common/src/main/java/ctbrec/recorder/RecordingPreconditions.java b/common/src/main/java/ctbrec/recorder/RecordingPreconditions.java index 9222ba0f..67b5ab27 100644 --- a/common/src/main/java/ctbrec/recorder/RecordingPreconditions.java +++ b/common/src/main/java/ctbrec/recorder/RecordingPreconditions.java @@ -2,7 +2,10 @@ package ctbrec.recorder; import static ctbrec.recorder.NextGenLocalRecorder.*; import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.time.Instant; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -13,7 +16,6 @@ import org.slf4j.LoggerFactory; import ctbrec.Config; import ctbrec.Model; import ctbrec.ModelGroup; -import ctbrec.ModelGroupEntry; import ctbrec.Recording; import ctbrec.recorder.download.Download; @@ -28,7 +30,7 @@ public class RecordingPreconditions { this.recorder = recorder; } - void check(Model model) throws IOException { + void check(Model model) throws IOException, InvalidKeyException, NoSuchAlgorithmException { ensureRecorderIsActive(); ensureModelIsNotSuspended(model); ensureModelIsNotMarkedForLaterRecording(model); @@ -134,20 +136,38 @@ public class RecordingPreconditions { return concurrentRecordings == 0 || concurrentRecordings > 0 && recorder.getRecordingProcesses().size() < concurrentRecordings; } - private void ensureNoOtherFromModelGroupIsRecording(Model model) { - if (model.getModelGroup().isPresent()) { - ModelGroupEntry modelGroupEntry = model.getModelGroup().get(); // NOSONAR - ModelGroup modelGroup = recorder.getModelGroup(modelGroupEntry.getId()); - for (Model groupModel : modelGroup.getModels()) { - if (groupModel.equals(model)) { - return; // no other model with lower group index is online, start recording - } else if (otherModelCanBeRecorded(groupModel)) { - throw new PreconditionNotMetException(groupModel + " from the same group is already recorded"); + private void ensureNoOtherFromModelGroupIsRecording(Model model) throws InvalidKeyException, NoSuchAlgorithmException, IOException { + Optional modelGroup = Config.getInstance().getModelGroup(model); + if (modelGroup.isPresent()) { + for (String modelUrl : modelGroup.get().getModelUrls()) { + if (modelUrl.equals(model.getUrl())) { + // no other model with higher prio is online, start recording + // but before that stop all recordings of models with lower prio + stopModelsWithLowerPrio(modelGroup.get()); + return; + } else { + Optional otherModel = getModelForUrl(modelUrl); + if (otherModel.isPresent() && otherModelCanBeRecorded(otherModel.get())) { + throw new PreconditionNotMetException(otherModel.get() + " from the same group is already recorded"); + } } } } } + private void stopModelsWithLowerPrio(ModelGroup modelGroup) throws InvalidKeyException, NoSuchAlgorithmException, IOException { + recorder.getCurrentlyRecording().stream() + .filter(m -> modelGroup.getModelUrls().contains(m.getUrl())) + .forEach(recorder::stopRecordingProcess); + + } + + private Optional getModelForUrl(String modelUrl) { + return Config.getInstance().getSettings().models.stream() + .filter(m -> Objects.equals(m.getUrl(), modelUrl)) + .findFirst(); + } + private boolean otherModelCanBeRecorded(Model model) { try { ensureRecorderIsActive(); diff --git a/common/src/main/java/ctbrec/recorder/RemoteRecorder.java b/common/src/main/java/ctbrec/recorder/RemoteRecorder.java index 4e711057..e4193ecc 100644 --- a/common/src/main/java/ctbrec/recorder/RemoteRecorder.java +++ b/common/src/main/java/ctbrec/recorder/RemoteRecorder.java @@ -11,7 +11,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -616,9 +615,8 @@ public class RemoteRecorder implements Recorder { } @Override - public ModelGroup createModelGroup(String name) { + public void saveModelGroup(ModelGroup group) { // TODO Auto-generated method stub - return null; } @Override @@ -627,12 +625,12 @@ public class RemoteRecorder implements Recorder { } @Override - public ModelGroup getModelGroup(UUID id) { + public Optional getModelGroup(UUID id) { for (ModelGroup group : getModelGroups()) { if (Objects.equals(group.getId(), id)) { - return group; + return Optional.of(group); } } - throw new NoSuchElementException("ModelGroup with id " + id + " not found"); + return Optional.empty(); } }