Make sure that only one recording per model is started and that recordings terminate before shutting down the thread pools
This commit is contained in:
parent
fb5fef8912
commit
013d28c33d
|
@ -71,6 +71,11 @@
|
|||
<artifactId>antlr4-runtime</artifactId>
|
||||
<version>${antlr.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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<Recording> 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));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Model, Recording> recordingProcesses = Collections.synchronizedMap(new HashMap<>());
|
||||
private final List<Recording> 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<RecordingProcess> 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<Recording> 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<PostProcessor> 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<PostProcessor> 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<Recording> 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<Model, Recording> getRecordingProcesses() {
|
||||
List<Recording> getRecordingProcesses() {
|
||||
return recordingProcesses;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<StreamSource> 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<StreamSource> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,9 @@ public interface RecordingProcess extends Callable<RecordingProcess> {
|
|||
|
||||
void postProcess(Recording recording);
|
||||
|
||||
int getSelectedResolution();
|
||||
default int getSelectedResolution() {
|
||||
return StreamSource.UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the recording in the filesystem as file object
|
||||
|
|
|
@ -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<StreamSource> 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;
|
||||
}
|
||||
|
|
|
@ -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<Model, Recording> 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<Model, Recording> 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<Model, Recording> recordingProcesses = new HashMap<>();
|
||||
recordingProcesses.put(theOtherOne, new Recording());
|
||||
List<Recording> 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<Model, Recording> recordingProcesses = new HashMap<>();
|
||||
List<Recording> 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<Model, Recording> recordingProcesses = new HashMap<>();
|
||||
List<Recording> 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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue