forked from j62/ctbrec
1
0
Fork 0

Add possibility to group models through a dialog

This commit is contained in:
0xb00bface 2021-05-08 14:15:39 +02:00
parent d52b728c1c
commit 0358a35a84
14 changed files with 333 additions and 182 deletions

View File

@ -3,7 +3,6 @@ package ctbrec.ui;
import java.io.IOException; import java.io.IOException;
import java.time.Instant; import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
@ -14,7 +13,6 @@ import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter; import com.squareup.moshi.JsonWriter;
import ctbrec.Model; import ctbrec.Model;
import ctbrec.ModelGroupEntry;
import ctbrec.SubsequentAction; import ctbrec.SubsequentAction;
import ctbrec.recorder.download.Download; import ctbrec.recorder.download.Download;
import ctbrec.recorder.download.HttpHeaderFactory; import ctbrec.recorder.download.HttpHeaderFactory;
@ -331,16 +329,4 @@ public class JavaFxModel implements Model {
public void setMarkedForLaterRecording(boolean marked) { public void setMarkedForLaterRecording(boolean marked) {
delegate.setMarkedForLaterRecording(marked); delegate.setMarkedForLaterRecording(marked);
} }
@Override
public void setModelGroup(ModelGroupEntry modelGroupEntry) {
delegate.setModelGroup(modelGroupEntry);
}
@Override
public Optional<ModelGroupEntry> getModelGroup() {
return delegate.getModelGroup();
}
} }

View File

@ -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<ModelGroup> modelGroups = Config.getInstance().getSettings().modelGroups;
Optional<ModelGroup> 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<ModelGroupListItem> 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<ModelGroup> modelGroups = Config.getInstance().getSettings().modelGroups;
List<ModelGroupListItem> comboBoxItems = modelGroups.stream().map(ModelGroupListItem::new).sorted().collect(Collectors.toList());
ObservableList<ModelGroupListItem> 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<String> 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<ModelGroupListItem> {
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());
}
}
}

View File

@ -4,16 +4,26 @@ import static javafx.scene.control.ButtonType.*;
import java.io.InputStream; import java.io.InputStream;
import java.util.Optional; 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 ctbrec.ui.AutosizeAlert;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.ButtonType; import javafx.scene.control.ButtonType;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Dialog; import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea; import javafx.scene.control.TextArea;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
@ -63,7 +73,7 @@ public class Dialogs {
Dialog<String> dialog = new Dialog<>(); Dialog<String> dialog = new Dialog<>();
dialog.setTitle(title); dialog.setTitle(title);
dialog.setHeaderText(header); dialog.setHeaderText(header);
dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); dialog.getDialogPane().getButtonTypes().addAll(OK, CANCEL);
dialog.initModality(Modality.APPLICATION_MODAL); dialog.initModality(Modality.APPLICATION_MODAL);
dialog.setResizable(true); dialog.setResizable(true);
InputStream icon = Dialogs.class.getResourceAsStream("/icon.png"); InputStream icon = Dialogs.class.getResourceAsStream("/icon.png");
@ -86,7 +96,7 @@ public class Dialogs {
Platform.runLater(notes::requestFocus); Platform.runLater(notes::requestFocus);
dialog.setResultConverter(dialogButton -> { dialog.setResultConverter(dialogButton -> {
if (dialogButton == ButtonType.OK) { if (dialogButton == OK) {
return notes.getText(); return notes.getText();
} }
return null; return null;
@ -98,7 +108,7 @@ public class Dialogs {
public static Boolean showCustomInput(Scene parent, String title, Region region) { public static Boolean showCustomInput(Scene parent, String title, Region region) {
Dialog<?> dialog = new Dialog<>(); Dialog<?> dialog = new Dialog<>();
dialog.setTitle(title); dialog.setTitle(title);
dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); dialog.getDialogPane().getButtonTypes().addAll(OK, CANCEL);
dialog.initModality(Modality.APPLICATION_MODAL); dialog.initModality(Modality.APPLICATION_MODAL);
dialog.setResizable(true); dialog.setResizable(true);
InputStream icon = Dialogs.class.getResourceAsStream("/icon.png"); InputStream icon = Dialogs.class.getResourceAsStream("/icon.png");
@ -109,7 +119,7 @@ public class Dialogs {
} }
dialog.getDialogPane().setContent(region); dialog.getDialogPane().setContent(region);
dialog.showAndWait(); dialog.showAndWait();
return dialog.getResult() == ButtonType.OK; return dialog.getResult() == OK;
} }
public static boolean showConfirmDialog(String title, String message, String header, Scene parent) { public static boolean showConfirmDialog(String title, String message, String header, Scene parent) {
@ -117,7 +127,7 @@ public class Dialogs {
confirm.setTitle(title); confirm.setTitle(title);
confirm.setHeaderText(header); confirm.setHeaderText(header);
confirm.showAndWait(); confirm.showAndWait();
return confirm.getResult() == ButtonType.YES; return confirm.getResult() == YES;
} }
public static ButtonType showShutdownDialog(Scene parent) { public static ButtonType showShutdownDialog(Scene parent) {
@ -134,4 +144,36 @@ public class Dialogs {
confirm.showAndWait(); confirm.showAndWait();
return confirm.getResult(); return confirm.getResult();
} }
public static Optional<ModelGroup> showModelGroupSelectionDialog(Scene parent, Model model) {
GridPane dialogPane = new GridPane();
Set<ModelGroup> modelGroups = Config.getInstance().getSettings().modelGroups;
ObservableList<ModelGroup> comboBoxModel = FXCollections.observableArrayList(modelGroups);
ComboBox<ModelGroup> 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<ModelGroup> 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();
}
}
} }

View File

@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory;
import ctbrec.Config; import ctbrec.Config;
import ctbrec.GlobalThreadPool; import ctbrec.GlobalThreadPool;
import ctbrec.Model; import ctbrec.Model;
import ctbrec.ModelGroup;
import ctbrec.event.EventBusHolder; import ctbrec.event.EventBusHolder;
import ctbrec.recorder.Recorder; import ctbrec.recorder.Recorder;
import ctbrec.sites.Site; import ctbrec.sites.Site;
@ -39,6 +40,7 @@ import ctbrec.ui.DesktopIntegration;
import ctbrec.ui.SiteUiFactory; import ctbrec.ui.SiteUiFactory;
import ctbrec.ui.TipDialog; import ctbrec.ui.TipDialog;
import ctbrec.ui.TokenLabel; import ctbrec.ui.TokenLabel;
import ctbrec.ui.action.AddToGroupAction;
import ctbrec.ui.action.IgnoreModelsAction; import ctbrec.ui.action.IgnoreModelsAction;
import ctbrec.ui.action.OpenRecordingsDir; import ctbrec.ui.action.OpenRecordingsDir;
import ctbrec.ui.action.SetStopDateAction; import ctbrec.ui.action.SetStopDateAction;
@ -501,6 +503,11 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
MenuItem unfollow = new MenuItem("Unfollow"); MenuItem unfollow = new MenuItem("Unfollow");
unfollow.setOnAction(e -> follow(getSelectedThumbCells(cell), false)); 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"); MenuItem ignore = new MenuItem("Ignore");
ignore.setOnAction(e -> ignore(getSelectedThumbCells(cell))); ignore.setOnAction(e -> ignore(getSelectedThumbCells(cell)));
@ -535,6 +542,8 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
if (site.supportsTips()) { if (site.supportsTips()) {
contextMenu.getItems().add(sendTip); contextMenu.getItems().add(sendTip);
} }
Optional<ModelGroup> modelGroup = Config.getInstance().getModelGroup(model);
contextMenu.getItems().add(modelGroup.isEmpty() ? addToGroup : editGroup);
contextMenu.getItems().addAll(copyUrl, openInBrowser, ignore, refresh, openRecDir); contextMenu.getItems().addAll(copyUrl, openInBrowser, ignore, refresh, openRecDir);
if (model instanceof MyFreeCamsModel && Objects.equals(System.getenv("CTBREC_DEV"), "1")) { if (model instanceof MyFreeCamsModel && Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
MenuItem debug = new MenuItem("debug"); MenuItem debug = new MenuItem("debug");
@ -545,6 +554,13 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
return contextMenu; return contextMenu;
} }
private void editGroup(Model model) {
}
private void addToGroup(Model model) {
new AddToGroupAction(this.getContent(), recorder, model).execute();
}
private void recordLater(List<ThumbCell> list, boolean recordLater) { private void recordLater(List<ThumbCell> list, boolean recordLater) {
for (ThumbCell cell : list) { for (ThumbCell cell : list) {
cell.recordLater(recordLater); cell.recordLater(recordLater);

View File

@ -41,7 +41,6 @@ public abstract class AbstractModel implements Model {
private Instant lastRecorded; private Instant lastRecorded;
private Instant recordUntil; private Instant recordUntil;
private SubsequentAction recordUntilSubsequentAction; private SubsequentAction recordUntilSubsequentAction;
private Optional<ModelGroupEntry> modelGroup = Optional.empty(); // NOSONAR
@Override @Override
public boolean isOnline() throws IOException, ExecutionException, InterruptedException { public boolean isOnline() throws IOException, ExecutionException, InterruptedException {
@ -310,14 +309,4 @@ public abstract class AbstractModel implements Model {
} }
return true; return true;
} }
@Override
public Optional<ModelGroupEntry> getModelGroup() {
return modelGroup;
}
@Override
public void setModelGroup(ModelGroupEntry modelGroup) {
this.modelGroup = Optional.ofNullable(modelGroup);
}
} }

View File

@ -18,8 +18,8 @@ import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -185,11 +185,11 @@ public class Config {
} }
// 3.11.0 make Cam4 model names lower case // 3.11.0 make Cam4 model names lower case
settings.models.stream() settings.models.stream()
.filter(Cam4Model.class::isInstance) .filter(Cam4Model.class::isInstance)
.forEach(m -> m.setName(m.getName().toLowerCase())); .forEach(m -> m.setName(m.getName().toLowerCase()));
settings.modelsIgnored.stream() settings.modelsIgnored.stream()
.filter(Cam4Model.class::isInstance) .filter(Cam4Model.class::isInstance)
.forEach(m -> m.setName(m.getName().toLowerCase())); .forEach(m -> m.setName(m.getName().toLowerCase()));
// 4.1.2 reduce models ignore to store only the URL // 4.1.2 reduce models ignore to store only the URL
if (settings.modelsIgnored != null && !settings.modelsIgnored.isEmpty()) { if (settings.modelsIgnored != null && !settings.modelsIgnored.isEmpty()) {
settings.ignoredModels = settings.modelsIgnored.stream() settings.ignoredModels = settings.modelsIgnored.stream()
@ -230,9 +230,9 @@ public class Config {
} }
public synchronized void save() throws IOException { public synchronized void save() throws IOException {
if (savingDisabled) { if (savingDisabled) {
return; return;
} }
Moshi moshi = new Moshi.Builder() Moshi moshi = new Moshi.Builder()
.add(Model.class, new ModelJsonAdapter()) .add(Model.class, new ModelJsonAdapter())
.add(PostProcessor.class, new PostProcessorJsonAdapter()) .add(PostProcessor.class, new PostProcessorJsonAdapter())
@ -303,12 +303,18 @@ public class Config {
public String getModelNotes(Model m) { public String getModelNotes(Model m) {
return Config.getInstance().getSettings().modelNotes.getOrDefault(m.getUrl(), ""); return Config.getInstance().getSettings().modelNotes.getOrDefault(m.getUrl(), "");
} }
public void disableSaving() { public void disableSaving() {
savingDisabled = true; savingDisabled = true;
} }
public void enableSaving() { public void enableSaving() {
savingDisabled = false; savingDisabled = false;
}
public Optional<ModelGroup> getModelGroup(Model model) {
return getSettings().modelGroups.stream()
.filter(mg -> mg.getModelUrls().contains(model.getUrl()))
.findFirst();
} }
} }

View File

@ -4,7 +4,6 @@ import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.time.Instant; import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
@ -149,7 +148,4 @@ public interface Model extends Comparable<Model>, Serializable {
*/ */
public boolean exists() throws IOException; public boolean exists() throws IOException;
public void setModelGroup(ModelGroupEntry modelGroupEntry);
public Optional<ModelGroupEntry> getModelGroup();
} }

View File

@ -1,7 +1,6 @@
package ctbrec; package ctbrec;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -12,7 +11,7 @@ public class ModelGroup implements Serializable {
private UUID id; private UUID id;
private String name; private String name;
private transient List<Model> models = new LinkedList<>(); private List<String> modelUrls = new LinkedList<>();
public UUID getId() { public UUID getId() {
return id; return id;
@ -30,12 +29,12 @@ public class ModelGroup implements Serializable {
this.name = name; this.name = name;
} }
public List<Model> getModels() { public List<String> getModelUrls() {
return models; return modelUrls;
} }
public void setModels(List<Model> models) { public void setModelUrls(List<String> modelUrls) {
this.models = models; this.modelUrls = modelUrls;
} }
@Override @Override
@ -60,15 +59,14 @@ public class ModelGroup implements Serializable {
@Override @Override
public String toString() { 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) { public void add(Model model) {
models.add(model); modelUrls.add(model.getUrl());
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;
});
} }
} }

View File

@ -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 + "]";
}
}

View File

@ -5,7 +5,6 @@ import java.lang.reflect.InvocationTargetException;
import java.time.Instant; import java.time.Instant;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -16,7 +15,6 @@ import com.squareup.moshi.JsonReader.Token;
import com.squareup.moshi.JsonWriter; import com.squareup.moshi.JsonWriter;
import ctbrec.Model; import ctbrec.Model;
import ctbrec.ModelGroupEntry;
import ctbrec.SubsequentAction; import ctbrec.SubsequentAction;
import ctbrec.sites.Site; import ctbrec.sites.Site;
import ctbrec.sites.chaturbate.ChaturbateModel; import ctbrec.sites.chaturbate.ChaturbateModel;
@ -71,18 +69,6 @@ public class ModelJsonAdapter extends JsonAdapter<Model> {
model.setRecordUntil(Instant.ofEpochMilli(reader.nextLong())); model.setRecordUntil(Instant.ofEpochMilli(reader.nextLong()));
} else if (key.equals("recordUntilSubsequentAction")) { } else if (key.equals("recordUntilSubsequentAction")) {
model.setRecordUntilSubsequentAction(SubsequentAction.valueOf(reader.nextString())); 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")) { } else if (key.equals("siteSpecific")) {
reader.beginObject(); reader.beginObject();
try { try {
@ -128,8 +114,6 @@ public class ModelJsonAdapter extends JsonAdapter<Model> {
writer.name("lastRecorded").value(model.getLastRecorded().toEpochMilli()); writer.name("lastRecorded").value(model.getLastRecorded().toEpochMilli());
writer.name("recordUntil").value(model.getRecordUntil().toEpochMilli()); writer.name("recordUntil").value(model.getRecordUntil().toEpochMilli());
writer.name("recordUntilSubsequentAction").value(model.getRecordUntilSubsequentAction().name()); 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.name("siteSpecific");
writer.beginObject(); writer.beginObject();
model.writeSiteSpecificData(writer); model.writeSiteSpecificData(writer);

View File

@ -47,7 +47,6 @@ import com.google.common.eventbus.Subscribe;
import ctbrec.Config; import ctbrec.Config;
import ctbrec.Model; import ctbrec.Model;
import ctbrec.ModelGroup; import ctbrec.ModelGroup;
import ctbrec.ModelGroupEntry;
import ctbrec.Recording; import ctbrec.Recording;
import ctbrec.Recording.State; import ctbrec.Recording.State;
import ctbrec.event.Event; import ctbrec.event.Event;
@ -93,7 +92,6 @@ public class NextGenLocalRecorder implements Recorder {
threadPoolScaler = new ThreadPoolScaler((ThreadPoolExecutor) downloadPool, 5); threadPoolScaler = new ThreadPoolScaler((ThreadPoolExecutor) downloadPool, 5);
recordingManager = new RecordingManager(config, sites); recordingManager = new RecordingManager(config, sites);
loadModels(); loadModels();
createModelGroups();
int ppThreads = config.getSettings().postProcessingThreads; int ppThreads = config.getSettings().postProcessingThreads;
ppPool = new ThreadPoolExecutor(ppThreads, ppThreads, 5, TimeUnit.MINUTES, ppQueue, createThreadFactory("PP", MIN_PRIORITY)); 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() { private void startCompletionHandler() {
downloadCompletionPool.submit(() -> { downloadCompletionPool.submit(() -> {
while (!Thread.currentThread().isInterrupted()) { while (!Thread.currentThread().isInterrupted()) {
@ -796,38 +784,26 @@ public class NextGenLocalRecorder implements Recorder {
} }
@Override @Override
public ModelGroup getModelGroup(UUID id) { public Optional<ModelGroup> getModelGroup(UUID id) {
for (ModelGroup group : getModelGroups()) { for (ModelGroup group : getModelGroups()) {
if (Objects.equals(group.getId(), id)) { 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 @Override
public ModelGroup createModelGroup(String name) { public void saveModelGroup(ModelGroup group) throws IOException {
ModelGroup group = new ModelGroup(); Set<ModelGroup> modelGroups = config.getSettings().modelGroups;
group.setName(name); modelGroups.remove(group);
config.getSettings().modelGroups.add(group); modelGroups.add(group);
try { config.save();
config.save();
} catch (IOException e) {
LOG.error("Couldn't save new model group", e);
}
return group;
} }
@Override @Override
public void deleteModelGroup(ModelGroup group) { public void deleteModelGroup(ModelGroup group) throws IOException {
for (Model model : group.getModels()) {
model.setModelGroup(null);
}
config.getSettings().modelGroups.remove(group); config.getSettings().modelGroups.remove(group);
try { config.save();
config.save();
} catch (IOException e) {
LOG.error("Couldn't delete model group", e);
}
} }
} }

View File

@ -4,6 +4,7 @@ import java.io.IOException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -153,7 +154,16 @@ public interface Recorder {
public int getModelCount(); public int getModelCount();
public Set<ModelGroup> getModelGroups(); public Set<ModelGroup> 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<ModelGroup> getModelGroup(UUID uuid);
public void deleteModelGroup(ModelGroup group) throws IOException;
} }

View File

@ -2,7 +2,10 @@ package ctbrec.recorder;
import static ctbrec.recorder.NextGenLocalRecorder.*; import static ctbrec.recorder.NextGenLocalRecorder.*;
import java.io.IOException; import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant; import java.time.Instant;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -13,7 +16,6 @@ import org.slf4j.LoggerFactory;
import ctbrec.Config; import ctbrec.Config;
import ctbrec.Model; import ctbrec.Model;
import ctbrec.ModelGroup; import ctbrec.ModelGroup;
import ctbrec.ModelGroupEntry;
import ctbrec.Recording; import ctbrec.Recording;
import ctbrec.recorder.download.Download; import ctbrec.recorder.download.Download;
@ -28,7 +30,7 @@ public class RecordingPreconditions {
this.recorder = recorder; this.recorder = recorder;
} }
void check(Model model) throws IOException { void check(Model model) throws IOException, InvalidKeyException, NoSuchAlgorithmException {
ensureRecorderIsActive(); ensureRecorderIsActive();
ensureModelIsNotSuspended(model); ensureModelIsNotSuspended(model);
ensureModelIsNotMarkedForLaterRecording(model); ensureModelIsNotMarkedForLaterRecording(model);
@ -134,20 +136,38 @@ public class RecordingPreconditions {
return concurrentRecordings == 0 || concurrentRecordings > 0 && recorder.getRecordingProcesses().size() < concurrentRecordings; return concurrentRecordings == 0 || concurrentRecordings > 0 && recorder.getRecordingProcesses().size() < concurrentRecordings;
} }
private void ensureNoOtherFromModelGroupIsRecording(Model model) { private void ensureNoOtherFromModelGroupIsRecording(Model model) throws InvalidKeyException, NoSuchAlgorithmException, IOException {
if (model.getModelGroup().isPresent()) { Optional<ModelGroup> modelGroup = Config.getInstance().getModelGroup(model);
ModelGroupEntry modelGroupEntry = model.getModelGroup().get(); // NOSONAR if (modelGroup.isPresent()) {
ModelGroup modelGroup = recorder.getModelGroup(modelGroupEntry.getId()); for (String modelUrl : modelGroup.get().getModelUrls()) {
for (Model groupModel : modelGroup.getModels()) { if (modelUrl.equals(model.getUrl())) {
if (groupModel.equals(model)) { // no other model with higher prio is online, start recording
return; // no other model with lower group index is online, start recording // but before that stop all recordings of models with lower prio
} else if (otherModelCanBeRecorded(groupModel)) { stopModelsWithLowerPrio(modelGroup.get());
throw new PreconditionNotMetException(groupModel + " from the same group is already recorded"); return;
} else {
Optional<Model> 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<Model> getModelForUrl(String modelUrl) {
return Config.getInstance().getSettings().models.stream()
.filter(m -> Objects.equals(m.getUrl(), modelUrl))
.findFirst();
}
private boolean otherModelCanBeRecorded(Model model) { private boolean otherModelCanBeRecorded(Model model) {
try { try {
ensureRecorderIsActive(); ensureRecorderIsActive();

View File

@ -11,7 +11,6 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -616,9 +615,8 @@ public class RemoteRecorder implements Recorder {
} }
@Override @Override
public ModelGroup createModelGroup(String name) { public void saveModelGroup(ModelGroup group) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null;
} }
@Override @Override
@ -627,12 +625,12 @@ public class RemoteRecorder implements Recorder {
} }
@Override @Override
public ModelGroup getModelGroup(UUID id) { public Optional<ModelGroup> getModelGroup(UUID id) {
for (ModelGroup group : getModelGroups()) { for (ModelGroup group : getModelGroups()) {
if (Objects.equals(group.getId(), id)) { if (Objects.equals(group.getId(), id)) {
return group; return Optional.of(group);
} }
} }
throw new NoSuchElementException("ModelGroup with id " + id + " not found"); return Optional.empty();
} }
} }