From 013d28c33db4819d2d21a823f2ad37f0d2b4fe48 Mon Sep 17 00:00:00 2001
From: 0xb00bface <0xboobface@gmail.com>
Date: Mon, 29 May 2023 17:00:22 +0200
Subject: [PATCH] Make sure that only one recording per model is started and
that recordings terminate before shutting down the thread pools
---
common/pom.xml | 5 +
.../main/java/ctbrec/GlobalThreadPool.java | 2 +-
.../recorder/RecordingPreconditions.java | 21 ++-
.../recorder/SimplifiedLocalRecorder.java | 163 ++++++++++--------
.../recorder/download/AbstractDownload.java | 43 ++++-
.../recorder/download/RecordingProcess.java | 4 +-
.../download/hls/AbstractHlsDownload.java | 27 +--
.../recorder/RecordingPreconditionsTest.java | 44 +++--
8 files changed, 180 insertions(+), 129 deletions(-)
diff --git a/common/pom.xml b/common/pom.xml
index e480c387..45c296a8 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -71,6 +71,11 @@
antlr4-runtime
${antlr.version}
+
+ ch.qos.logback
+ logback-classic
+ test
+
diff --git a/common/src/main/java/ctbrec/GlobalThreadPool.java b/common/src/main/java/ctbrec/GlobalThreadPool.java
index aaeccf92..71e93701 100644
--- a/common/src/main/java/ctbrec/GlobalThreadPool.java
+++ b/common/src/main/java/ctbrec/GlobalThreadPool.java
@@ -8,7 +8,7 @@ import java.util.concurrent.Future;
public class GlobalThreadPool {
- private static ExecutorService threadPool = Executors.newCachedThreadPool(r -> {
+ private static final ExecutorService threadPool = Executors.newCachedThreadPool(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("GlobalWorker-" + UUID.randomUUID().toString().substring(0, 8));
diff --git a/common/src/main/java/ctbrec/recorder/RecordingPreconditions.java b/common/src/main/java/ctbrec/recorder/RecordingPreconditions.java
index 05a3f454..6096b8d6 100644
--- a/common/src/main/java/ctbrec/recorder/RecordingPreconditions.java
+++ b/common/src/main/java/ctbrec/recorder/RecordingPreconditions.java
@@ -101,7 +101,7 @@ public class RecordingPreconditions {
RecordingProcess download = lowerPrioRecordingProcess.get().getRecordingProcess();
Model lowerPrioModel = download.getModel();
LOG.info("Stopping recording for {}. Prio {} < {}", lowerPrioModel.getName(), lowerPrioModel.getPriority(), model.getPriority());
- recorder.stopRecordingProcess(lowerPrioModel);
+ recorder.stopRecordingProcess(lowerPrioRecordingProcess.get());
} else {
throw new PreconditionNotMetException("Other models have higher prio, not starting recording for " + model.getName());
}
@@ -109,14 +109,17 @@ public class RecordingPreconditions {
}
private Optional recordingProcessWithLowerPrio(int priority) {
- Model lowest = null;
- for (Model m : recorder.getRecordingProcesses().keySet()) {
- if (lowest == null || m.getPriority() < lowest.getPriority()) {
- lowest = m;
+ Recording lowest = null;
+ int lowestPrio = Integer.MAX_VALUE;
+ for (Recording rec : recorder.getRecordingProcesses()) {
+ Model m = rec.getModel();
+ if (m.getPriority() < lowestPrio) {
+ lowest = rec;
+ lowestPrio = m.getPriority();
}
}
- if (lowest != null && lowest.getPriority() < priority) {
- return Optional.of(recorder.getRecordingProcesses().get(lowest));
+ if (lowestPrio < priority) {
+ return Optional.of(lowest);
} else {
return Optional.empty();
}
@@ -129,7 +132,7 @@ public class RecordingPreconditions {
}
private void ensureNoRecordingRunningForModel(Model model) {
- if (recorder.getRecordingProcesses().containsKey(model)) {
+ if (recorder.isRecordingRunningForModel(model)) {
throw new PreconditionNotMetException("A recording for model " + model + " is already running");
}
}
@@ -189,7 +192,7 @@ public class RecordingPreconditions {
private void stopModelsWithLowerPrio(ModelGroup modelGroup) throws InvalidKeyException, NoSuchAlgorithmException, IOException {
recorder.getCurrentlyRecording().stream()
.filter(m -> modelGroup.getModelUrls().contains(m.getUrl()))
- .forEach(recorder::stopRecordingProcess);
+ .forEach(m -> recorder.getRecordingProcessForModel(m).ifPresent(recorder::stopRecordingProcess));
}
diff --git a/common/src/main/java/ctbrec/recorder/SimplifiedLocalRecorder.java b/common/src/main/java/ctbrec/recorder/SimplifiedLocalRecorder.java
index 2bd34f56..846d5aa1 100644
--- a/common/src/main/java/ctbrec/recorder/SimplifiedLocalRecorder.java
+++ b/common/src/main/java/ctbrec/recorder/SimplifiedLocalRecorder.java
@@ -41,7 +41,7 @@ public class SimplifiedLocalRecorder implements Recorder {
private final ReentrantLock recorderLock = new ReentrantLock();
private final ReentrantLock modelGroupLock = new ReentrantLock();
private final RecorderHttpClient client;
- private final Map recordingProcesses = Collections.synchronizedMap(new HashMap<>());
+ private final List recordingProcesses = Collections.synchronizedList(new ArrayList<>());
private final RecordingManager recordingManager;
private final RecordingPreconditions preconditions;
@@ -88,7 +88,7 @@ public class SimplifiedLocalRecorder implements Recorder {
}
checkFreeSpace();
threadPoolScaler.tick();
- waitABit(1);
+ waitABit(100);
}
}).start();
}
@@ -130,7 +130,6 @@ public class SimplifiedLocalRecorder implements Recorder {
Thread.currentThread().interrupt();
fail(recording);
} catch (ExecutionException e) {
- // TODO react to different exceptions, e.g. with a retry
log.error("Error while recording model {}. Stopping recording.", recording.getModel(), e);
fail(recording);
}
@@ -149,14 +148,14 @@ public class SimplifiedLocalRecorder implements Recorder {
private void removeRecordingProcess(Recording rec) {
recorderLock.lock();
try {
- recordingProcesses.remove(rec.getModel());
+ recordingProcesses.remove(rec);
} finally {
recorderLock.unlock();
}
}
private void fail(Recording recording) {
- stopRecordingProcess(recording.getModel());
+ stopRecordingProcess(recording);
recording.getRecordingProcess().finalizeDownload();
if (deleteIfEmpty(recording)) {
return;
@@ -166,6 +165,12 @@ public class SimplifiedLocalRecorder implements Recorder {
}
private void scheduleRecording(Recording recording, long delayInMillis) {
+ if (recording.getModel().isSuspended()) {
+ log.info("Recording process for suspended model found: {}. Stopping now", recording.getModel());
+ stopRecordingProcess(recording);
+ submitPostProcessingJob(recording);
+ return;
+ }
ScheduledFuture future = scheduler.schedule(recording.getRecordingProcess(), delayInMillis, TimeUnit.MILLISECONDS);
recording.setCurrentIteration(future);
recording.getSelectedResolution();
@@ -203,65 +208,71 @@ public class SimplifiedLocalRecorder implements Recorder {
recording.getRecordingProcess().stop();
recording.getRecordingProcess().awaitEnd();
}
+ waitForRecordingsToTerminate();
log.info("Recordings have been stopped");
}
- // private void fail(ScheduledFuture future, Exception e) {
- // if (downloadFutureRecordingMap.containsKey(future)) {
- // Recording rec = downloadFutureRecordingMap.remove(future);
- // deleteIfEmpty(rec);
- // removeRecordingProcess(rec);
- // rec.getRecordingProcess().finalizeDownload();
- // log.error("Error while recording stream for model {}", rec.getModel(), e);
- // } else {
- // log.error("Error while recording stream", e);
- // }
- // }
+ private void waitForRecordingsToTerminate() {
+ long secondsToWait = 30;
+ for (int i = 0; i < secondsToWait; i++) {
+ if (recordingProcesses.isEmpty()) {
+ return;
+ }
+
+ log.info("Waiting for recording processes to terminate");
+ waitABit(1000);
+ }
+ log.warn("Recording processes didn't finish in {} seconds. Continuing, but some recordings might not get post-processed", secondsToWait);
+ }
private void submitPostProcessingJob(Recording recording) {
setRecordingStatus(recording, WAITING);
- postProcessing.submit(() -> {
- try {
- setRecordingStatus(recording, State.POST_PROCESSING);
- recording.getRecordingProcess().stop();
- recording.getRecordingProcess().awaitEnd();
- recording.setDirtyFlag(true);
- recording.getRecordingProcess().finalizeDownload();
- recording.refresh();
- recordingManager.saveRecording(recording);
- recording.postprocess();
- List postProcessors = config.getSettings().postProcessors;
- PostProcessingContext ctx = createPostProcessingContext(recording);
- for (PostProcessor postProcessor : postProcessors) {
- if (postProcessor.isEnabled()) {
- log.debug("Running post-processor: {}", postProcessor.getName());
- boolean continuePP = postProcessor.postprocess(ctx);
- if (!continuePP) {
- break;
+ try {
+ postProcessing.submit(() -> {
+ try {
+ setRecordingStatus(recording, State.POST_PROCESSING);
+ recording.getRecordingProcess().stop();
+ recording.getRecordingProcess().awaitEnd();
+ recording.setDirtyFlag(true);
+ recording.getRecordingProcess().finalizeDownload();
+ recording.refresh();
+ recordingManager.saveRecording(recording);
+ recording.postprocess();
+ List postProcessors = config.getSettings().postProcessors;
+ PostProcessingContext ctx = createPostProcessingContext(recording);
+ for (PostProcessor postProcessor : postProcessors) {
+ if (postProcessor.isEnabled()) {
+ log.debug("Running post-processor: {}", postProcessor.getName());
+ boolean continuePP = postProcessor.postprocess(ctx);
+ if (!continuePP) {
+ break;
+ }
+ } else {
+ log.debug("Skipping post-processor {} because it is disabled", postProcessor.getName());
}
- } else {
- log.debug("Skipping post-processor {} because it is disabled", postProcessor.getName());
+ }
+ recording.refresh();
+ if (recording.getStatus() != State.DELETED) {
+ setRecordingStatus(recording, State.FINISHED);
+ recordingManager.saveRecording(recording);
+ }
+ log.info("Post-processing finished for {}", recording.getModel().getName());
+ } catch (Exception e) {
+ if (e instanceof InterruptedException) { // NOSONAR
+ Thread.currentThread().interrupt();
+ }
+ log.error("Error while post-processing recording {}", recording, e);
+ recording.setStatus(State.FAILED);
+ try {
+ recordingManager.saveRecording(recording);
+ } catch (IOException e1) {
+ log.error("Couldn't update recording state for recording {}", recording, e1);
}
}
- recording.refresh();
- if (recording.getStatus() != State.DELETED) {
- setRecordingStatus(recording, State.FINISHED);
- recordingManager.saveRecording(recording);
- }
- log.info("Post-processing finished for {}", recording.getModel().getName());
- } catch (Exception e) {
- if (e instanceof InterruptedException) { // NOSONAR
- Thread.currentThread().interrupt();
- }
- log.error("Error while post-processing recording {}", recording, e);
- recording.setStatus(State.FAILED);
- try {
- recordingManager.saveRecording(recording);
- } catch (IOException e1) {
- log.error("Couldn't update recording state for recording {}", recording, e1);
- }
- }
- });
+ });
+ } catch (RejectedExecutionException e) {
+ log.error("Could not start post-processing for {} {}:{}. Execution rejected by thread pool", recording, recording.getModel().getSite().getName(), recording.getModel().getDisplayName());
+ }
}
private PostProcessingContext createPostProcessingContext(Recording recording) {
@@ -362,7 +373,7 @@ public class SimplifiedLocalRecorder implements Recorder {
rec.setModel(model);
rec.setStartDate(download.getStartTime());
rec.setSingleFile(download.isSingleFile());
- recordingProcesses.put(model, rec);
+ recordingProcesses.add(rec);
recordingManager.add(rec);
return rec;
}
@@ -401,15 +412,22 @@ public class SimplifiedLocalRecorder implements Recorder {
throw new NoSuchElementException("Model " + model.getName() + " [" + model.getUrl() + "] not found in list of recorded models");
}
- if (recordingProcesses.containsKey(model)) {
- Recording rec = recordingProcesses.get(model);
- rec.getRecordingProcess().stop();
+ if (isRecordingRunningForModel(model)) {
+ getRecordingProcessForModel(model).map(Recording::getRecordingProcess).ifPresent(RecordingProcess::stop);
}
} finally {
recorderLock.unlock();
}
}
+ public boolean isRecordingRunningForModel(Model model) {
+ return recordingProcesses.stream().anyMatch(rec -> Objects.equals(rec.getModel(), model));
+ }
+
+ public Optional getRecordingProcessForModel(Model model) {
+ return recordingProcesses.stream().filter(rec -> Objects.equals(rec.getModel(), model)).findAny();
+ }
+
@Override
public void switchStreamSource(Model model) throws IOException {
if (models.contains(model)) {
@@ -419,10 +437,7 @@ public class SimplifiedLocalRecorder implements Recorder {
log.debug("Switching stream source to index {} for model {}", model.getStreamUrlIndex(), model.getName());
recorderLock.lock();
try {
- Recording rec = recordingProcesses.get(model);
- if (rec != null) {
- stopRecordingProcess(model);
- }
+ getRecordingProcessForModel(model).ifPresent(this::stopRecordingProcess);
} finally {
recorderLock.unlock();
}
@@ -432,14 +447,14 @@ public class SimplifiedLocalRecorder implements Recorder {
}
}
- void stopRecordingProcess(Model model) {
+ void stopRecordingProcess(Recording rec) {
recorderLock.lock();
try {
- log.debug("Stopping recording for {} - recording found: {}", model, recordingProcesses.get(model));
- Recording rec = recordingProcesses.get(model);
+ var model = rec.getModel();
+ log.debug("Stopping recording for {} - recording found: {}", model, rec);
log.debug("Stopping download for {}", model);
rec.getRecordingProcess().stop();
- recordingProcesses.remove(model);
+ recordingProcesses.remove(rec);
} finally {
recorderLock.unlock();
}
@@ -448,7 +463,7 @@ public class SimplifiedLocalRecorder implements Recorder {
private void stopRecordingProcesses() {
recorderLock.lock();
try {
- for (Recording rec : recordingProcesses.values()) {
+ for (Recording rec : recordingProcesses) {
rec.getRecordingProcess().stop();
}
} finally {
@@ -508,8 +523,7 @@ public class SimplifiedLocalRecorder implements Recorder {
return;
}
- Recording rec = recordingProcesses.get(model);
- Optional.ofNullable(rec).map(Recording::getRecordingProcess).ifPresent(RecordingProcess::stop);
+ getRecordingProcessForModel(model).ifPresent(this::stopRecordingProcess);
} catch (IOException e) {
log.error("Couldn't save config", e);
} finally {
@@ -564,7 +578,7 @@ public class SimplifiedLocalRecorder implements Recorder {
m.setMarkedForLaterRecording(mark);
if (mark && getCurrentlyRecording().contains(m)) {
log.debug("Stopping recording of {}", m);
- stopRecordingProcess(m);
+ getRecordingProcessForModel(m).ifPresent(this::stopRecordingProcess);
}
if (!mark) {
log.debug("Removing model: {}", m);
@@ -670,8 +684,9 @@ public class SimplifiedLocalRecorder implements Recorder {
if (e.getType() == MODEL_ONLINE) {
ModelIsOnlineEvent evt = (ModelIsOnlineEvent) e;
Model model = evt.getModel();
- log.trace("Model online event: {} - suspended:{} - already recording:{}", model, model.isSuspended(), recordingProcesses.containsKey(model));
- if (!isSuspended(model) && !recordingProcesses.containsKey(model)) {
+ log.trace("Model online event: {} - suspended:{} - already recording:{}", model, model.isSuspended(), isRecordingRunningForModel(model));
+ log.trace("Recording processes: {}", recordingProcesses.size());
+ if (!isSuspended(model) && !isRecordingRunningForModel(model)) {
startRecordingProcess(model);
}
}
@@ -796,7 +811,7 @@ public class SimplifiedLocalRecorder implements Recorder {
return running;
}
- Map getRecordingProcesses() {
+ List getRecordingProcesses() {
return recordingProcesses;
}
diff --git a/common/src/main/java/ctbrec/recorder/download/AbstractDownload.java b/common/src/main/java/ctbrec/recorder/download/AbstractDownload.java
index 6da48d38..6f248b99 100644
--- a/common/src/main/java/ctbrec/recorder/download/AbstractDownload.java
+++ b/common/src/main/java/ctbrec/recorder/download/AbstractDownload.java
@@ -4,15 +4,18 @@ import ctbrec.Config;
import ctbrec.Model;
import ctbrec.Settings;
import ctbrec.UnknownModel;
-import ctbrec.recorder.download.hls.CombinedSplittingStrategy;
-import ctbrec.recorder.download.hls.NoopSplittingStrategy;
-import ctbrec.recorder.download.hls.SizeSplittingStrategy;
-import ctbrec.recorder.download.hls.TimeSplittingStrategy;
+import ctbrec.recorder.download.hls.*;
+import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.time.Instant;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import static ctbrec.recorder.download.StreamSource.UNKNOWN;
+
+@Slf4j
public abstract class AbstractDownload implements RecordingProcess {
protected Instant startTime;
@@ -22,6 +25,7 @@ public abstract class AbstractDownload implements RecordingProcess {
protected Config config;
protected SplittingStrategy splittingStrategy;
protected ExecutorService downloadExecutor;
+ protected int selectedResolution = UNKNOWN;
@Override
public void init(Config config, Model model, Instant startTime, ExecutorService executorService) throws IOException {
@@ -72,11 +76,40 @@ public abstract class AbstractDownload implements RecordingProcess {
@Override
public int getSelectedResolution() {
- return StreamSource.UNKNOWN;
+ return selectedResolution;
}
@Override
public void awaitEnd() {
// do nothing per default
}
+
+ protected StreamSource selectStreamSource(List streamSources) throws ExecutionException {
+ if (model.getStreamUrlIndex() >= 0 && model.getStreamUrlIndex() < streamSources.size()) {
+ // TODO don't use the index, but the bandwidth. if the bandwidth does not match, take the closest one
+ log.debug("Model stream index: {}", model.getStreamUrlIndex());
+ streamSources.forEach(ss -> log.debug(ss.toString()));
+ StreamSource source = streamSources.get(model.getStreamUrlIndex());
+ log.debug("{} selected {}", model.getName(), source);
+ selectedResolution = source.height;
+ return source;
+ } else {
+ // filter out stream resolutions, which are out of range of the configured min and max
+ int minRes = Config.getInstance().getSettings().minimumResolution;
+ int maxRes = Config.getInstance().getSettings().maximumResolution;
+ List filteredStreamSources = streamSources.stream()
+ .filter(src -> src.height == 0 || src.height == UNKNOWN || minRes <= src.height)
+ .filter(src -> src.height == 0 || src.height == UNKNOWN || maxRes >= src.height)
+ .toList();
+
+ if (filteredStreamSources.isEmpty()) {
+ throw new ExecutionException(new NoStreamFoundException("No stream left in playlist"));
+ } else {
+ StreamSource source = filteredStreamSources.get(filteredStreamSources.size() - 1);
+ log.debug("{} selected {}", model.getName(), source);
+ selectedResolution = source.height;
+ return source;
+ }
+ }
+ }
}
diff --git a/common/src/main/java/ctbrec/recorder/download/RecordingProcess.java b/common/src/main/java/ctbrec/recorder/download/RecordingProcess.java
index e8eb5a40..c88352a9 100644
--- a/common/src/main/java/ctbrec/recorder/download/RecordingProcess.java
+++ b/common/src/main/java/ctbrec/recorder/download/RecordingProcess.java
@@ -27,7 +27,9 @@ public interface RecordingProcess extends Callable {
void postProcess(Recording recording);
- int getSelectedResolution();
+ default int getSelectedResolution() {
+ return StreamSource.UNKNOWN;
+ }
/**
* Returns the path to the recording in the filesystem as file object
diff --git a/common/src/main/java/ctbrec/recorder/download/hls/AbstractHlsDownload.java b/common/src/main/java/ctbrec/recorder/download/hls/AbstractHlsDownload.java
index d4670f34..a1313ec2 100644
--- a/common/src/main/java/ctbrec/recorder/download/hls/AbstractHlsDownload.java
+++ b/common/src/main/java/ctbrec/recorder/download/hls/AbstractHlsDownload.java
@@ -252,31 +252,10 @@ public abstract class AbstractHlsDownload extends AbstractDownload {
for (StreamSource streamSource : streamSources) {
LOG.debug("{}:{} src {}", model.getSite().getName(), model.getName(), streamSource);
}
- String url;
- if (model.getStreamUrlIndex() >= 0 && model.getStreamUrlIndex() < streamSources.size()) {
- // TODO don't use the index, but the bandwidth. if the bandwidth does not match, take the closest one
- StreamSource source = streamSources.get(model.getStreamUrlIndex());
- LOG.debug("{} selected {}", model.getName(), source);
- url = source.getMediaPlaylistUrl();
- selectedResolution = source.height;
- } else {
- // filter out stream resolutions, which are out of range of the configured min and max
- int minRes = Config.getInstance().getSettings().minimumResolution;
- int maxRes = Config.getInstance().getSettings().maximumResolution;
- List filteredStreamSources = streamSources.stream()
- .filter(src -> src.height == 0 || src.height == UNKNOWN || minRes <= src.height)
- .filter(src -> src.height == 0 || src.height == UNKNOWN || maxRes >= src.height)
- .toList();
+ StreamSource selectedStreamSource = selectStreamSource(streamSources);
+ String url = selectedStreamSource.getMediaPlaylistUrl();
+ selectedResolution = selectedStreamSource.height;
- if (filteredStreamSources.isEmpty()) {
- throw new ExecutionException(new NoStreamFoundException("No stream left in playlist"));
- } else {
- StreamSource source = filteredStreamSources.get(filteredStreamSources.size() - 1);
- LOG.debug("{} selected {}", model.getName(), source);
- url = source.getMediaPlaylistUrl();
- selectedResolution = source.height;
- }
- }
LOG.debug("Segment playlist url {}", url);
return url;
}
diff --git a/common/src/test/java/ctbrec/recorder/RecordingPreconditionsTest.java b/common/src/test/java/ctbrec/recorder/RecordingPreconditionsTest.java
index 3c2319af..163f7f59 100644
--- a/common/src/test/java/ctbrec/recorder/RecordingPreconditionsTest.java
+++ b/common/src/test/java/ctbrec/recorder/RecordingPreconditionsTest.java
@@ -10,7 +10,10 @@ import java.io.IOException;
import java.time.Instant;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
import java.util.concurrent.ExecutionException;
import static java.time.temporal.ChronoUnit.HOURS;
@@ -84,16 +87,19 @@ class RecordingPreconditionsTest {
}
@Test
- void testRecordingAlreadyRunning() {
+ void testRecordingAlreadyRunning() throws IOException, ExecutionException, InterruptedException {
var recorder = mock(SimplifiedLocalRecorder.class);
Model model = mock(Model.class);
+ when(model.isOnline(true)).thenReturn(true);
when(recorder.isRunning()).thenReturn(true);
- Map recordingProcesses = new HashMap<>();
- recordingProcesses.put(model, new Recording());
- when(recorder.getRecordingProcesses()).thenReturn(recordingProcesses);
+ Recording rec = new Recording();
+ rec.setModel(model);
+ when(recorder.getRecordingProcesses()).thenReturn(List.of(rec));
when(model.getRecordUntil()).thenReturn(Instant.MAX);
when(model.toString()).thenReturn("Mockita Boobilicious");
+ when(recorder.getModels()).thenReturn(List.of(model));
+ when(recorder.isRecordingRunningForModel(model)).thenReturn(true);
RecordingPreconditions preconditions = new RecordingPreconditions(recorder, config);
PreconditionNotMetException ex = assertThrows(PreconditionNotMetException.class, () -> preconditions.check(model));
@@ -171,9 +177,9 @@ class RecordingPreconditionsTest {
when(recorder.notEnoughSpaceForRecording()).thenReturn(false);
when(recorder.getModelGroup(theOtherOne)).thenReturn(Optional.of(group));
when(recorder.getModelGroup(mockita)).thenReturn(Optional.of(group));
- Map recordingProcesses = new HashMap<>();
- recordingProcesses.put(theOtherOne, new Recording());
- when(recorder.getRecordingProcesses()).thenReturn(recordingProcesses);
+ Recording recording = new Recording();
+ recording.setModel(theOtherOne);
+ when(recorder.getRecordingProcesses()).thenReturn(List.of(recording));
RecordingPreconditions preconditions = new RecordingPreconditions(recorder, config);
@@ -251,8 +257,10 @@ class RecordingPreconditionsTest {
when(recorder.isRunning()).thenReturn(true);
when(recorder.getModels()).thenReturn(modelsToRecord);
when(recorder.notEnoughSpaceForRecording()).thenReturn(false);
- Map recordingProcesses = new HashMap<>();
- recordingProcesses.put(theOtherOne, new Recording());
+ List recordingProcesses = new ArrayList<>();
+ Recording rec = new Recording();
+ rec.setModel(theOtherOne);
+ recordingProcesses.add(rec);
when(recorder.getRecordingProcesses()).thenReturn(recordingProcesses);
RecordingPreconditions preconditions = new RecordingPreconditions(recorder, config);
@@ -288,7 +296,7 @@ class RecordingPreconditionsTest {
when(recorder.isRunning()).thenReturn(true);
when(recorder.getModels()).thenReturn(modelsToRecord);
when(recorder.notEnoughSpaceForRecording()).thenReturn(false);
- Map recordingProcesses = new HashMap<>();
+ List recordingProcesses = new ArrayList<>();
when(recorder.getRecordingProcesses()).thenReturn(recordingProcesses);
Model theOtherOne = mock(Model.class);
@@ -297,7 +305,7 @@ class RecordingPreconditionsTest {
when(theOtherOne.isOnline(true)).thenReturn(true);
when(theOtherOne.getUrl()).thenReturn("http://localhost/theOtherOne");
when(theOtherOne.getPriority()).thenReturn(50);
- recordingProcesses.put(theOtherOne, mockRecordingProcess(theOtherOne));
+ recordingProcesses.add(mockRecordingProcess(theOtherOne));
Model lowestPrio = mock(Model.class);
when(lowestPrio.getRecordUntil()).thenReturn(Instant.MAX);
@@ -305,11 +313,16 @@ class RecordingPreconditionsTest {
when(lowestPrio.isOnline(true)).thenReturn(true);
when(lowestPrio.getUrl()).thenReturn("http://localhost/lowest");
when(lowestPrio.getPriority()).thenReturn(1);
- recordingProcesses.put(theOtherOne, mockRecordingProcess(lowestPrio));
+ Recording lowestPrioRecording = new Recording();
+ RecordingProcess recordingProcess = mock(RecordingProcess.class);
+ when(recordingProcess.getModel()).thenReturn(lowestPrio);
+ lowestPrioRecording.setRecordingProcess(recordingProcess);
+ lowestPrioRecording.setModel(lowestPrio);
+ recordingProcesses.add(lowestPrioRecording);
RecordingPreconditions preconditions = new RecordingPreconditions(recorder, config);
assertDoesNotThrow(() -> preconditions.check(mockita));
- verify(recorder).stopRecordingProcess(lowestPrio);
+ verify(recorder).stopRecordingProcess(lowestPrioRecording);
}
@Test
@@ -328,7 +341,7 @@ class RecordingPreconditionsTest {
when(recorder.isRunning()).thenReturn(true);
when(recorder.getModels()).thenReturn(modelsToRecord);
when(recorder.notEnoughSpaceForRecording()).thenReturn(false);
- Map recordingProcesses = new HashMap<>();
+ List recordingProcesses = new ArrayList<>();
when(recorder.getRecordingProcesses()).thenReturn(recordingProcesses);
RecordingPreconditions preconditions = new RecordingPreconditions(recorder, config);
@@ -371,6 +384,7 @@ class RecordingPreconditionsTest {
when(download.getModel()).thenReturn(model);
Recording runningRecording = mock(Recording.class);
when(runningRecording.getRecordingProcess()).thenReturn(download);
+ when(runningRecording.getModel()).thenReturn(model);
return runningRecording;
}
}