Merge remote-tracking branch 'github/dev' into dev
This commit is contained in:
commit
93cafcbd2d
|
@ -8,7 +8,8 @@ public enum Icon {
|
|||
CLOCK_16(Icon.class.getResource("/16/clock.png").toExternalForm()),
|
||||
GROUP_16(Icon.class.getResource("/16/users.png").toExternalForm()),
|
||||
MEDIA_PLAYBACK_PAUSE_16(Icon.class.getResource("/16/media-playback-pause.png").toExternalForm()),
|
||||
MEDIA_RECORD_16(Icon.class.getResource("/16/media-record.png").toExternalForm());
|
||||
MEDIA_RECORD_16(Icon.class.getResource("/16/media-record.png").toExternalForm()),
|
||||
MEDIA_FORCE_RECORD_16(Icon.class.getResource("/16/media-force-record.png").toExternalForm());
|
||||
|
||||
private String url;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ public class JavaFxModel implements Model {
|
|||
private final transient BooleanProperty onlineProperty = new SimpleBooleanProperty();
|
||||
private final transient BooleanProperty recordingProperty = new SimpleBooleanProperty();
|
||||
private final transient BooleanProperty pausedProperty = new SimpleBooleanProperty();
|
||||
private final transient BooleanProperty forcePriorityProperty = new SimpleBooleanProperty();
|
||||
private final transient SimpleIntegerProperty priorityProperty = new SimpleIntegerProperty();
|
||||
private final transient SimpleObjectProperty<Instant> lastSeenProperty = new SimpleObjectProperty<>();
|
||||
private final transient SimpleObjectProperty<Instant> lastRecordedProperty = new SimpleObjectProperty<>();
|
||||
|
@ -119,6 +120,10 @@ public class JavaFxModel implements Model {
|
|||
public BooleanProperty getPausedProperty() {
|
||||
return pausedProperty;
|
||||
}
|
||||
|
||||
public BooleanProperty getForcePriorityProperty() {
|
||||
return forcePriorityProperty;
|
||||
}
|
||||
|
||||
public SimpleIntegerProperty getPriorityProperty() {
|
||||
return priorityProperty;
|
||||
|
@ -258,6 +263,18 @@ public class JavaFxModel implements Model {
|
|||
return delegate.getPriority();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isForcePriority() {
|
||||
return delegate.isForcePriority();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForcePriority(boolean forcePriority) {
|
||||
delegate.setForcePriority(forcePriority);
|
||||
forcePriorityProperty.set(forcePriority);
|
||||
}
|
||||
|
||||
public SimpleObjectProperty<Instant> lastSeenProperty() {
|
||||
return lastSeenProperty;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package ctbrec.ui.action;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ctbrec.Model;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.ui.tasks.ForcePriorityTask;
|
||||
import javafx.scene.Node;
|
||||
|
||||
public class ForcePriorityAction extends AbstractModelAction {
|
||||
|
||||
public ForcePriorityAction(Node source, List<Model> models, Recorder recorder) {
|
||||
super(source, models, recorder, new ForcePriorityTask(recorder));
|
||||
}
|
||||
|
||||
public CompletableFuture<List<Result>> execute() {
|
||||
return super.execute("Couldn't force ignoring priority", "Force priority of {0} failed:");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package ctbrec.ui.action;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import ctbrec.Model;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.ui.tasks.ResumePriorityTask;
|
||||
import javafx.scene.Node;
|
||||
|
||||
public class ResumePriorityAction extends AbstractModelAction {
|
||||
|
||||
public ResumePriorityAction(Node source, List<Model> models, Recorder recorder) {
|
||||
super(source, models, recorder, new ResumePriorityTask(recorder));
|
||||
}
|
||||
|
||||
public CompletableFuture<List<Result>> execute() {
|
||||
return super.execute("Couldn't resume respecting priority", "Resuming priority of {0} failed:");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package ctbrec.ui.menu;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ctbrec.Model;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.ui.action.ForcePriorityAction;
|
||||
import ctbrec.ui.action.ResumePriorityAction;
|
||||
import javafx.scene.Node;
|
||||
|
||||
public class ForcePriorityHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ForcePriorityHandler.class);
|
||||
|
||||
private Node source;
|
||||
private Recorder recorder;
|
||||
private Runnable callback;
|
||||
|
||||
public ForcePriorityHandler(Node source, Recorder recorder, Runnable callback) {
|
||||
this.source = source;
|
||||
this.recorder = recorder;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
protected void forcePriority(List<Model> selectedModels) {
|
||||
new ForcePriorityAction(source, selectedModels, recorder).execute()
|
||||
.exceptionally(ex -> {
|
||||
LOG.error("Error while forcing ignore priority", ex);
|
||||
return null;
|
||||
}).whenComplete((r, ex) -> executeCallback());
|
||||
}
|
||||
|
||||
protected void resumePriority(List<Model> selectedModels) {
|
||||
new ResumePriorityAction(source, selectedModels, recorder).execute()
|
||||
.exceptionally(ex -> {
|
||||
LOG.error("Error while resuming respecting priority", ex);
|
||||
return null;
|
||||
}).whenComplete((r, ex) -> executeCallback());
|
||||
}
|
||||
|
||||
private void executeCallback() {
|
||||
try {
|
||||
callback.run();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error while executing menu callback", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -101,6 +101,7 @@ public class ModelMenuContributor {
|
|||
addStartPaused(menu, selectedModels);
|
||||
addRecordLater(menu, selectedModels);
|
||||
addPauseResume(menu, selectedModels);
|
||||
addForceRecord(menu, selectedModels);
|
||||
addGroupMenu(menu, selectedModels);
|
||||
menu.getItems().add(new SeparatorMenuItem());
|
||||
|
||||
|
@ -272,6 +273,24 @@ public class ModelMenuContributor {
|
|||
}
|
||||
}
|
||||
|
||||
private void addForceRecord(ContextMenu menu, List<Model> selectedModels) {
|
||||
var forcePriority = new MenuItem("Enable Force Recording");
|
||||
forcePriority.setOnAction(e -> {
|
||||
for (Model model : selectedModels) {
|
||||
model.setMarkedForLaterRecording(false);
|
||||
model.setSuspended(false);
|
||||
}
|
||||
if (!recorder.isTracked(selectedModels.get(0))) {
|
||||
startStopAction(selectedModels, true);
|
||||
}
|
||||
new ForcePriorityHandler(source, recorder, callback).forcePriority(selectedModels);
|
||||
});
|
||||
var resumePriority = new MenuItem("Disable Force Recording");
|
||||
resumePriority.setOnAction(e -> new ForcePriorityHandler(source, recorder, callback).resumePriority(selectedModels));
|
||||
var forceResumePriority = recorder.isForcePriority(selectedModels.get(0)) ? resumePriority : forcePriority;
|
||||
menu.getItems().add(forceResumePriority);
|
||||
}
|
||||
|
||||
private void addRecordLater(ContextMenu menu, List<Model> selectedModels) {
|
||||
var first = selectedModels.get(0);
|
||||
var recordLater = new MenuItem("Record Later");
|
||||
|
|
|
@ -44,10 +44,6 @@ public class IgnoreList extends GridPane {
|
|||
setVgap(10);
|
||||
setPadding(new Insets(20, 10, 10, 10));
|
||||
|
||||
var headline = new Label("Ignore List");
|
||||
headline.getStyleClass().add("settings-group-label");
|
||||
add(headline, 0, 0);
|
||||
|
||||
ignoreListView = new ListView<>();
|
||||
ignoreListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||
ignoreListView.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
|
||||
|
@ -55,7 +51,7 @@ public class IgnoreList extends GridPane {
|
|||
removeSelectedModels();
|
||||
}
|
||||
});
|
||||
add(ignoreListView, 0, 1);
|
||||
add(ignoreListView, 0, 0);
|
||||
GridPane.setHgrow(ignoreListView, Priority.ALWAYS);
|
||||
|
||||
var remove = new Button("Remove");
|
||||
|
|
|
@ -129,6 +129,8 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
private final VariablePlayGroundDialogFactory variablePlayGroundDialogFactory = new VariablePlayGroundDialogFactory();
|
||||
private SimpleBooleanProperty checkForUpdates;
|
||||
private PostProcessingStepPanel postProcessingStepPanel;
|
||||
private SimpleStringProperty filterBlacklist;
|
||||
private SimpleStringProperty filterWhitelist;
|
||||
|
||||
public SettingsTab(List<Site> sites, Recorder recorder) {
|
||||
this.sites = sites;
|
||||
|
@ -207,6 +209,8 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
dateTimeFormat = new SimpleStringProperty(null, "dateTimeFormat", settings.dateTimeFormat);
|
||||
tabsSortable = new SimpleBooleanProperty(null, "tabsSortable", settings.tabsSortable);
|
||||
checkForUpdates = new SimpleBooleanProperty(null, "checkForUpdates", settings.checkForUpdates);
|
||||
filterBlacklist = new SimpleStringProperty(null, "filterBlacklist", settings.filterBlacklist);
|
||||
filterWhitelist = new SimpleStringProperty(null, "filterWhitelist", settings.filterWhitelist);
|
||||
}
|
||||
|
||||
private void createGui() {
|
||||
|
@ -297,8 +301,13 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
Setting.of("Steps", postProcessingStepPanel),
|
||||
Setting.of("", createHelpButton("Post-Processing Help", "http://localhost:5689/docs/PostProcessing.md")),
|
||||
Setting.of("", createVariablePlayGroundButton()))),
|
||||
Category.of("Events & Actions", new ActionSettingsPanel(recorder)), Category.of("Ignore List", ignoreList),
|
||||
Category.of("Sites", siteCategories.toArray(new Category[0])),
|
||||
Category.of("Events & Actions", new ActionSettingsPanel(recorder)),
|
||||
Category.of("Filtering",
|
||||
Group.of("Ignore List",
|
||||
Setting.of("", ignoreList)),
|
||||
Group.of("Text Filters",
|
||||
Setting.of("Blacklist", filterBlacklist, "Default list of blacklist filters for site views, space seperated"),
|
||||
Setting.of("Whitelist", filterWhitelist, "Default list of whitelist filters for site views, space seperated"))), Category.of("Sites", siteCategories.toArray(new Category[0])),
|
||||
Category.of("Proxy",
|
||||
Group.of("Proxy",
|
||||
Setting.of("Type", proxyType).needsRestart(),
|
||||
|
|
|
@ -73,6 +73,7 @@ public class ThumbCell extends StackPane {
|
|||
private static final Duration ANIMATION_DURATION = new Duration(250);
|
||||
|
||||
private static final Image imgRecordIndicator = new Image(MEDIA_RECORD_16.url());
|
||||
private static final Image imgForceRecordIndicator = new Image(MEDIA_FORCE_RECORD_16.url());
|
||||
private static final Image imgPauseIndicator = new Image(MEDIA_PLAYBACK_PAUSE_16.url());
|
||||
private static final Image imgBookmarkIndicator = new Image(BOOKMARK_16.url());
|
||||
private static final Image imgGroupIndicator = new Image(Icon.GROUP_16.url());
|
||||
|
@ -122,6 +123,7 @@ public class ThumbCell extends StackPane {
|
|||
this.imgAspectRatio = aspectRatio;
|
||||
recording = recorder.isTracked(model);
|
||||
model.setSuspended(recorder.isSuspended(model));
|
||||
model.setForcePriority(recorder.isForcePriority(model));
|
||||
this.setStyle("-fx-background-color: -fx-base");
|
||||
|
||||
streamPreview = new StreamPreview();
|
||||
|
@ -497,7 +499,11 @@ public class ThumbCell extends StackPane {
|
|||
recordingIndicatorTooltip.setText("Resume Recording");
|
||||
} else {
|
||||
modelRecordingState = ModelRecordingState.RECORDING;
|
||||
recordingIndicator.setImage(imgRecordIndicator);
|
||||
if (model.isForcePriority()) {
|
||||
recordingIndicator.setImage(imgForceRecordIndicator);
|
||||
} else {
|
||||
recordingIndicator.setImage(imgRecordIndicator);
|
||||
}
|
||||
recordingIndicatorTooltip.setText("Pause Recording");
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -317,13 +317,29 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
if (updatesSuspended) {
|
||||
return;
|
||||
}
|
||||
List<Model> models = filterIgnoredModels(updateService.getValue());
|
||||
List<Model> models = filterModels(updateService.getValue());
|
||||
updateGrid(models);
|
||||
}
|
||||
|
||||
private List<Model> filterIgnoredModels(List<Model> models) {
|
||||
private List<Model> filterModels(List<Model> models) {
|
||||
List<String> ignored = Config.getInstance().getSettings().ignoredModels;
|
||||
return models.stream().filter(m -> !ignored.contains(m.getUrl())).collect(Collectors.toList());
|
||||
String filterBlacklist = Config.getInstance().getSettings().filterBlacklist;
|
||||
String filterWhitelist = Config.getInstance().getSettings().filterWhitelist;
|
||||
if (filterBlacklist.isBlank() && filterWhitelist.isBlank()) {
|
||||
return models.stream()
|
||||
.filter(m -> !ignored.contains(m.getUrl()))
|
||||
.collect(Collectors.toList());
|
||||
} else if (filterBlacklist.isBlank()) {
|
||||
return models.stream()
|
||||
.filter(m -> !ignored.contains(m.getUrl()))
|
||||
.filter(m -> matches(m, filterWhitelist, true))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return models.stream()
|
||||
.filter(m -> !ignored.contains(m.getUrl()))
|
||||
.filter(m -> !matches(m, filterBlacklist, true))
|
||||
.filter(m -> matches(m, filterWhitelist, true))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected void updateGrid(List<? extends Model> models) {
|
||||
|
@ -633,7 +649,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
var node = iterator.next();
|
||||
if (node instanceof ThumbCell cell) {
|
||||
var m = cell.getModel();
|
||||
if (!matches(m, filter)) {
|
||||
if (!matches(m, filter, false)) {
|
||||
iterator.remove();
|
||||
filteredThumbCells.add(cell);
|
||||
cell.setSelected(false);
|
||||
|
@ -645,7 +661,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
for (Iterator<ThumbCell> iterator = filteredThumbCells.iterator(); iterator.hasNext(); ) {
|
||||
var thumbCell = iterator.next();
|
||||
var m = thumbCell.getModel();
|
||||
if (matches(m, filter)) {
|
||||
if (matches(m, filter, false)) {
|
||||
iterator.remove();
|
||||
insert(thumbCell);
|
||||
}
|
||||
|
@ -687,12 +703,14 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean matches(Model m, String filter) {
|
||||
private boolean matches(Model m, String filter, Boolean anyMatch) {
|
||||
try {
|
||||
String[] tokens = filter.split(" ");
|
||||
var tokensMissing = false;
|
||||
for (String token : tokens) {
|
||||
if (!modelPropertiesMatchToken(token, m)) {
|
||||
if (anyMatch && modelPropertiesMatchToken(token, m)) {
|
||||
return true;
|
||||
} else if (!modelPropertiesMatchToken(token, m)) {
|
||||
tokensMissing = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,9 @@ public class ModelNameTableCell extends IconTableCell<ModelName> {
|
|||
tooltip = group.getModelUrls().size() + " models:\n";
|
||||
tooltip += group.getModelUrls().stream().collect(Collectors.joining("\n"));
|
||||
});
|
||||
if (m.isForcePriority()) {
|
||||
this.setStyle(getStyle() + "-fx-text-fill: darkred;" + "-fx-font-weight: bold;");
|
||||
}
|
||||
}
|
||||
super.updateItem(modelName, empty);
|
||||
}
|
||||
|
|
|
@ -257,10 +257,12 @@ public class RecordedModelsTab extends AbstractRecordedModelsTab implements TabS
|
|||
if (index == -1) {
|
||||
observableModels.add(updatedModel);
|
||||
updatedModel.getPausedProperty().addListener(createPauseListener(updatedModel));
|
||||
updatedModel.getForcePriorityProperty().addListener(createForcePriorityListener(updatedModel));
|
||||
} else {
|
||||
// make sure to update the JavaFX online property, so that the table cell is updated
|
||||
JavaFxModel oldModel = observableModels.get(index);
|
||||
oldModel.setSuspended(updatedModel.isSuspended());
|
||||
oldModel.setForcePriority(updatedModel.isForcePriority());
|
||||
oldModel.getOnlineProperty().set(updatedModel.getOnlineProperty().get());
|
||||
oldModel.getRecordingProperty().set(updatedModel.getRecordingProperty().get());
|
||||
oldModel.lastRecordedProperty().set(updatedModel.lastRecordedProperty().get());
|
||||
|
@ -285,6 +287,20 @@ public class RecordedModelsTab extends AbstractRecordedModelsTab implements TabS
|
|||
};
|
||||
}
|
||||
|
||||
private ChangeListener<Boolean> createForcePriorityListener(JavaFxModel updatedModel) {
|
||||
return (obs, oldV, newV) -> {
|
||||
if (Boolean.TRUE.equals(newV)) {
|
||||
if (!recorder.isForcePriority(updatedModel)) {
|
||||
forcePriority(Collections.singletonList(updatedModel));
|
||||
}
|
||||
} else {
|
||||
if (recorder.isForcePriority(updatedModel)) {
|
||||
resumePriority(Collections.singletonList(updatedModel));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private ScheduledService<List<JavaFxModel>> createUpdateService() {
|
||||
ScheduledService<List<JavaFxModel>> modelUpdateService = new ScheduledService<>() {
|
||||
@Override
|
||||
|
@ -370,6 +386,16 @@ public class RecordedModelsTab extends AbstractRecordedModelsTab implements TabS
|
|||
new ResumeAction(getTabPane(), models, recorder).execute();
|
||||
}
|
||||
|
||||
private void forcePriority(List<JavaFxModel> selectedModels) {
|
||||
List<Model> models = selectedModels.stream().map(JavaFxModel::getDelegate).toList();
|
||||
new ForcePriorityAction(getTabPane(), models, recorder).execute();
|
||||
}
|
||||
|
||||
private void resumePriority(List<JavaFxModel> selectedModels) {
|
||||
List<Model> models = selectedModels.stream().map(JavaFxModel::getDelegate).toList();
|
||||
new ResumePriorityAction(getTabPane(), models, recorder).execute();
|
||||
}
|
||||
|
||||
private class PriorityCellFactory implements Callback<TableColumn<JavaFxModel, Number>, TableCell<JavaFxModel, Number>> {
|
||||
@Override
|
||||
public TableCell<JavaFxModel, Number> call(TableColumn<JavaFxModel, Number> param) {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package ctbrec.ui.tasks;
|
||||
|
||||
import ctbrec.recorder.Recorder;
|
||||
|
||||
public class ForcePriorityTask extends AbstractModelTask {
|
||||
|
||||
public ForcePriorityTask(Recorder recorder) {
|
||||
super(recorder, model -> {
|
||||
try {
|
||||
model.setForcePriority(true);
|
||||
recorder.forcePriorityRecording(model);
|
||||
} catch (Exception e) {
|
||||
throw new TaskExecutionException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package ctbrec.ui.tasks;
|
||||
|
||||
import ctbrec.recorder.Recorder;
|
||||
|
||||
public class ResumePriorityTask extends AbstractModelTask {
|
||||
|
||||
public ResumePriorityTask(Recorder recorder) {
|
||||
super(recorder, model -> {
|
||||
try {
|
||||
model.setForcePriority(false);
|
||||
recorder.resumePriorityRecording(model);
|
||||
} catch (Exception e) {
|
||||
throw new TaskExecutionException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
|
@ -30,6 +30,7 @@ public abstract class AbstractModel implements Model {
|
|||
private int streamUrlIndex = -1;
|
||||
private int priority = new Settings().defaultPriority;
|
||||
private boolean suspended = false;
|
||||
private boolean forcePriority = false;
|
||||
private boolean markedForLaterRecording = false;
|
||||
protected transient Site site;
|
||||
protected State onlineState = State.UNKNOWN;
|
||||
|
@ -145,6 +146,12 @@ public abstract class AbstractModel implements Model {
|
|||
this.suspended = suspended;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForcePriority() { return forcePriority; }
|
||||
|
||||
@Override
|
||||
public void setForcePriority(boolean forcePriority) { this.forcePriority = forcePriority; }
|
||||
|
||||
@Override
|
||||
public void delay() {
|
||||
this.delayUntil = Instant.now().plusSeconds(120);
|
||||
|
|
|
@ -123,6 +123,10 @@ public interface Model extends Comparable<Model>, Serializable {
|
|||
|
||||
void setSuspended(boolean suspended);
|
||||
|
||||
boolean isForcePriority();
|
||||
|
||||
void setForcePriority(boolean forcePriority);
|
||||
|
||||
void delay();
|
||||
|
||||
boolean isDelayed();
|
||||
|
|
|
@ -216,4 +216,6 @@ public class Settings {
|
|||
public boolean checkForUpdates = true;
|
||||
public int thumbCacheSize = 16;
|
||||
public boolean dreamcamVR = false;
|
||||
public String filterBlacklist = "";
|
||||
public String filterWhitelist = "";
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ public class ModelDto {
|
|||
private int streamUrlIndex = -1;
|
||||
private boolean suspended = false;
|
||||
private boolean bookmarked = false;
|
||||
private boolean forcePriority = false;
|
||||
@JsonSerialize(converter = InstantToMillisConverter.class)
|
||||
@JsonDeserialize(converter = MillisToInstantConverter.class)
|
||||
private Instant lastSeen;
|
||||
|
|
|
@ -67,6 +67,14 @@ public interface Recorder {
|
|||
|
||||
boolean isSuspended(Model model);
|
||||
|
||||
/**
|
||||
* Returns true, if a model is in the list of models to ignore priorities and immediately record.
|
||||
*/
|
||||
public boolean isForcePriority(Model model);
|
||||
|
||||
public void forcePriorityRecording(Model model) throws IOException, InvalidKeyException, NoSuchAlgorithmException;
|
||||
public void resumePriorityRecording(Model model) throws IOException, InvalidKeyException, NoSuchAlgorithmException;
|
||||
|
||||
boolean isMarkedForLaterRecording(Model model);
|
||||
|
||||
void markForLaterRecording(Model model, boolean mark) throws InvalidKeyException, NoSuchAlgorithmException, IOException;
|
||||
|
|
|
@ -98,7 +98,7 @@ public class RecordingPreconditions {
|
|||
lastPreconditionMessage = now;
|
||||
}
|
||||
// check, if we can stop a recording for a model with lower priority
|
||||
Optional<Recording> lowerPrioRecordingProcess = recordingProcessWithLowerPrio(model.getPriority());
|
||||
Optional<Recording> lowerPrioRecordingProcess = recordingProcessWithLowerPrio(model.getPriority(), model.isForcePriority());
|
||||
if (lowerPrioRecordingProcess.isPresent()) {
|
||||
RecordingProcess download = lowerPrioRecordingProcess.get().getRecordingProcess();
|
||||
Model lowerPrioModel = download.getModel();
|
||||
|
@ -110,17 +110,17 @@ public class RecordingPreconditions {
|
|||
}
|
||||
}
|
||||
|
||||
private Optional<Recording> recordingProcessWithLowerPrio(int priority) {
|
||||
private Optional<Recording> recordingProcessWithLowerPrio(int priority, boolean isForced) {
|
||||
Recording lowest = null;
|
||||
int lowestPrio = Integer.MAX_VALUE;
|
||||
for (Recording rec : recorder.getRecordingProcesses()) {
|
||||
Model m = rec.getModel();
|
||||
if (m.getPriority() < lowestPrio) {
|
||||
if (m.getPriority() < lowestPrio && !m.isForcePriority()) {
|
||||
lowest = rec;
|
||||
lowestPrio = m.getPriority();
|
||||
}
|
||||
}
|
||||
if (lowestPrio < priority) {
|
||||
if (isForced || (lowestPrio < priority)) {
|
||||
return Optional.of(lowest);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
|
|
|
@ -86,6 +86,7 @@ public class RemoteRecorder implements Recorder {
|
|||
sendRequest("start", model);
|
||||
findModel(model).ifPresent(cachedModel -> {
|
||||
cachedModel.setSuspended(model.isSuspended());
|
||||
cachedModel.setForcePriority(model.isForcePriority());
|
||||
cachedModel.setMarkedForLaterRecording(model.isMarkedForLaterRecording());
|
||||
cachedModel.setRecordUntil(model.getRecordUntil());
|
||||
cachedModel.setRecordUntilSubsequentAction(model.getRecordUntilSubsequentAction());
|
||||
|
@ -219,6 +220,11 @@ public class RemoteRecorder implements Recorder {
|
|||
return findModel(model).map(Model::isSuspended).orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForcePriority(Model model) {
|
||||
return findModel(model).map(Model::isForcePriority).orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMarkedForLaterRecording(Model model) {
|
||||
return findModel(model).map(Model::isMarkedForLaterRecording).orElse(false);
|
||||
|
@ -565,6 +571,30 @@ public class RemoteRecorder implements Recorder {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forcePriorityRecording(Model model) throws InvalidKeyException, NoSuchAlgorithmException, IOException {
|
||||
sendRequest("forcePriority", model);
|
||||
model.setForcePriority(true);
|
||||
// update cached model
|
||||
int index = models.indexOf(model);
|
||||
if (index >= 0) {
|
||||
Model m = models.get(index);
|
||||
m.setForcePriority(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumePriorityRecording(Model model) throws IOException, InvalidKeyException, NoSuchAlgorithmException {
|
||||
sendRequest("resumePriority", model);
|
||||
model.setForcePriority(false);
|
||||
// update cached model
|
||||
int index = models.indexOf(model);
|
||||
if (index >= 0) {
|
||||
Model m = models.get(index);
|
||||
m.setForcePriority(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Model> getOnlineModels() {
|
||||
return onlineModels;
|
||||
|
|
|
@ -345,6 +345,7 @@ public class SimplifiedLocalRecorder implements Recorder {
|
|||
|
||||
private void copyModelProperties(Model src, Model existing) {
|
||||
existing.setSuspended(src.isSuspended());
|
||||
existing.setForcePriority(src.isForcePriority());
|
||||
existing.setMarkedForLaterRecording(src.isMarkedForLaterRecording());
|
||||
existing.setPriority(src.getPriority());
|
||||
existing.setRecordUntil(src.getRecordUntil());
|
||||
|
@ -578,6 +579,49 @@ public class SimplifiedLocalRecorder implements Recorder {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forcePriorityRecording(Model model) throws IOException {
|
||||
recorderLock.lock();
|
||||
try {
|
||||
if (models.contains(model)) {
|
||||
int index = models.indexOf(model);
|
||||
Model m = models.get(index);
|
||||
m.setForcePriority(true);
|
||||
m.setMarkedForLaterRecording(false);
|
||||
model.setForcePriority(true);
|
||||
model.setMarkedForLaterRecording(false);
|
||||
saveConfig();
|
||||
startRecordingProcess(m);
|
||||
} else {
|
||||
log.warn("Couldn't force ignore priority for model {}. Not found in list", model.getName());
|
||||
}
|
||||
} finally {
|
||||
recorderLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumePriorityRecording(Model model) {
|
||||
recorderLock.lock();
|
||||
try {
|
||||
if (models.contains(model)) {
|
||||
int index = models.indexOf(model);
|
||||
models.get(index).setForcePriority(false);
|
||||
model.setForcePriority(false);
|
||||
saveConfig();
|
||||
} else {
|
||||
log.warn("Couldn't resume respecting priority for model {}. Not found in list", model.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
getRecordingProcessForModel(model).ifPresent(this::stopRecordingProcess);
|
||||
} catch (IOException e) {
|
||||
errorSavingConfig(e);
|
||||
} finally {
|
||||
recorderLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTracked(Model model) {
|
||||
Optional<Model> m = findModel(model);
|
||||
|
@ -863,6 +907,11 @@ public class SimplifiedLocalRecorder implements Recorder {
|
|||
log.info("Resuming recorder");
|
||||
running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isForcePriority(Model model) {
|
||||
return findModel(model).map(Model::isForcePriority).orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelCount() {
|
||||
|
|
|
@ -25,6 +25,7 @@ class ModelMapperTest {
|
|||
model.setLastSeen(Instant.now().minusSeconds(60));
|
||||
model.setPriority(51);
|
||||
model.setSuspended(true);
|
||||
model.setForcePriority(false);
|
||||
model.setMarkedForLaterRecording(true);
|
||||
model.setRecordUntilSubsequentAction(SubsequentAction.REMOVE);
|
||||
model.setDisplayName("whatever");
|
||||
|
@ -45,6 +46,7 @@ class ModelMapperTest {
|
|||
assertEquals(model.getPreview(), mapped.getPreview().toString());
|
||||
assertEquals(model.isMarkedForLaterRecording(), mapped.isBookmarked());
|
||||
assertEquals(model.isSuspended(), mapped.isSuspended());
|
||||
assertEquals(model.isForcePriority(), mapped.isForcePriority());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -61,6 +63,7 @@ class ModelMapperTest {
|
|||
dto.setLastSeen(Instant.now().minusSeconds(60));
|
||||
dto.setPriority(51);
|
||||
dto.setSuspended(true);
|
||||
dto.setForcePriority(false);
|
||||
dto.setBookmarked(true);
|
||||
dto.setRecordUntilSubsequentAction(SubsequentAction.REMOVE);
|
||||
dto.setDisplayName("whatever");
|
||||
|
@ -81,5 +84,6 @@ class ModelMapperTest {
|
|||
assertEquals(dto.getPreview().toString(), mapped.getPreview());
|
||||
assertEquals(dto.isBookmarked(), mapped.isMarkedForLaterRecording());
|
||||
assertEquals(dto.isSuspended(), mapped.isSuspended());
|
||||
assertEquals(dto.isForcePriority(), mapped.isForcePriority());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ public class RecorderServlet extends AbstractCtbrecServlet {
|
|||
log.debug("Starting recording for model {} - {}", model.getName(), model.getUrl());
|
||||
log.trace("Model marked: {}", model.isMarkedForLaterRecording());
|
||||
log.trace("Model paused: {}", model.isSuspended());
|
||||
log.trace("Model forced: {}", model.isForcePriority());
|
||||
log.trace("Model until: {}", model.getRecordUntil().equals(Instant.ofEpochMilli(Model.RECORD_INDEFINITELY)) ? "no limit" : model.getRecordUntil());
|
||||
log.trace("Model after: {}", model.getRecordUntilSubsequentAction());
|
||||
recorder.addModel(model);
|
||||
|
@ -227,6 +228,24 @@ public class RecorderServlet extends AbstractCtbrecServlet {
|
|||
response = "{\"status\": \"success\"}";
|
||||
responseWriter.write(response);
|
||||
break;
|
||||
case "forcePriority":
|
||||
log.debug("Force ignore priority for model {} - {}", model.getName(), model.getUrl());
|
||||
recorder.forcePriorityRecording(model);
|
||||
response = "{\"status\": \"success\", \"msg\": \"Forcing ignore priority\"}";
|
||||
responseWriter.write(response);
|
||||
break;
|
||||
case "resumePriority":
|
||||
log.debug("Resume respecting priority for model {} - {}", model.getName(), model.getUrl());
|
||||
GlobalThreadPool.submit(() -> {
|
||||
try {
|
||||
recorder.resumePriorityRecording(model);
|
||||
} catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException | IOException e) {
|
||||
log.error("Couldn't resume respecting priority for model {}", model, e);
|
||||
}
|
||||
});
|
||||
response = "{\"status\": \"success\", \"msg\": \"Resuming respecting priority\"}";
|
||||
responseWriter.write(response);
|
||||
break;
|
||||
case "saveModelGroup":
|
||||
recorder.saveModelGroup(request.getModelGroup());
|
||||
sendModelGroups(resp, recorder.getModelGroups());
|
||||
|
|
Loading…
Reference in New Issue