forked from j62/ctbrec
Fix post-processing timestamp problems
This commit is contained in:
parent
b8cdb2200e
commit
6cc8fd9cc2
|
@ -8,6 +8,10 @@ import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -142,23 +146,22 @@ public class Config {
|
||||||
return configDir;
|
return configDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getFileForRecording(Model model, String suffix) {
|
public File getFileForRecording(Model model, String suffix, Instant startTime) {
|
||||||
File dirForRecording = getDirForRecording(model);
|
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(RECORDING_DATE_FORMAT);
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat(RECORDING_DATE_FORMAT);
|
LocalDateTime startDateTime = LocalDateTime.ofInstant(startTime, ZoneId.systemDefault());
|
||||||
String startTime = sdf.format(new Date());
|
String formattedDate = dateTimeFormatter.format(startDateTime);
|
||||||
File targetFile = new File(dirForRecording, model.getSanitizedNamed() + '_' + startTime + '.' + suffix);
|
File dirForRecording = getDirForRecording(model, formattedDate);
|
||||||
|
File targetFile = new File(dirForRecording, model.getSanitizedNamed() + '_' + formattedDate + '.' + suffix);
|
||||||
return targetFile;
|
return targetFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
private File getDirForRecording(Model model) {
|
private File getDirForRecording(Model model, String formattedDate) {
|
||||||
switch(getSettings().recordingsDirStructure) {
|
switch(getSettings().recordingsDirStructure) {
|
||||||
case ONE_PER_MODEL:
|
case ONE_PER_MODEL:
|
||||||
return new File(getSettings().recordingsDir, model.getSanitizedNamed());
|
return new File(getSettings().recordingsDir, model.getSanitizedNamed());
|
||||||
case ONE_PER_RECORDING:
|
case ONE_PER_RECORDING:
|
||||||
File modelDir = new File(getSettings().recordingsDir, model.getSanitizedNamed());
|
File modelDir = new File(getSettings().recordingsDir, model.getSanitizedNamed());
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat(RECORDING_DATE_FORMAT);
|
return new File(modelDir, formattedDate);
|
||||||
String startTime = sdf.format(new Date());
|
|
||||||
return new File(modelDir, startTime);
|
|
||||||
case FLAT:
|
case FLAT:
|
||||||
default:
|
default:
|
||||||
return new File(getSettings().recordingsDir);
|
return new File(getSettings().recordingsDir);
|
||||||
|
|
|
@ -16,6 +16,7 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
@ -95,6 +96,21 @@ public class NextGenLocalRecorder implements Recorder {
|
||||||
LOG.info("Models to record: {}", models);
|
LOG.info("Models to record: {}", models);
|
||||||
LOG.info("Saving recordings in {}", config.getSettings().recordingsDir);
|
LOG.info("Saving recordings in {}", config.getSettings().recordingsDir);
|
||||||
|
|
||||||
|
startCompletionHandler();
|
||||||
|
|
||||||
|
scheduler.scheduleWithFixedDelay(() -> {
|
||||||
|
try {
|
||||||
|
if (!recordingProcesses.isEmpty() && !enoughSpaceForRecording()) {
|
||||||
|
LOG.info("No space left -> Stopping all recordings");
|
||||||
|
stopRecordingProcesses();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Couldn't check space left on device", e);
|
||||||
|
}
|
||||||
|
}, 1, 1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startCompletionHandler() {
|
||||||
Thread completionHandler = new Thread(() -> {
|
Thread completionHandler = new Thread(() -> {
|
||||||
while (!Thread.interrupted()) {
|
while (!Thread.interrupted()) {
|
||||||
try {
|
try {
|
||||||
|
@ -127,17 +143,6 @@ public class NextGenLocalRecorder implements Recorder {
|
||||||
completionHandler.setName("CompletionHandler");
|
completionHandler.setName("CompletionHandler");
|
||||||
completionHandler.setDaemon(true);
|
completionHandler.setDaemon(true);
|
||||||
completionHandler.start();
|
completionHandler.start();
|
||||||
|
|
||||||
scheduler.scheduleWithFixedDelay(() -> {
|
|
||||||
try {
|
|
||||||
if (!recordingProcesses.isEmpty() && !enoughSpaceForRecording()) {
|
|
||||||
LOG.info("No space left -> Stopping all recordings");
|
|
||||||
stopRecordingProcesses();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOG.error("Couldn't check space left on device", e);
|
|
||||||
}
|
|
||||||
}, 1, 1, TimeUnit.SECONDS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void submitPostProcessingJob(Recording recording) {
|
private void submitPostProcessingJob(Recording recording) {
|
||||||
|
@ -150,6 +155,9 @@ public class NextGenLocalRecorder implements Recorder {
|
||||||
recordingManager.saveRecording(recording);
|
recordingManager.saveRecording(recording);
|
||||||
deleteIfTooShort(recording);
|
deleteIfTooShort(recording);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
if (e instanceof InterruptedException) { // NOSONAR
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
LOG.error("Error while post-processing recording {}", recording, e);
|
LOG.error("Error while post-processing recording {}", recording, e);
|
||||||
recording.setStatus(State.FAILED);
|
recording.setStatus(State.FAILED);
|
||||||
try {
|
try {
|
||||||
|
@ -240,14 +248,16 @@ public class NextGenLocalRecorder implements Recorder {
|
||||||
|
|
||||||
LOG.debug("Starting recording for model {}", model.getName());
|
LOG.debug("Starting recording for model {}", model.getName());
|
||||||
Download download = model.createDownload();
|
Download download = model.createDownload();
|
||||||
download.init(config, model);
|
download.init(config, model, Instant.now());
|
||||||
|
Objects.requireNonNull(download.getStartTime(),
|
||||||
|
"At this point the download should have set a startTime. Make sure to set a startTime in " + download.getClass() + ".init()");
|
||||||
LOG.debug("Downloading with {}", download.getClass().getSimpleName());
|
LOG.debug("Downloading with {}", download.getClass().getSimpleName());
|
||||||
|
|
||||||
Recording rec = new Recording();
|
Recording rec = new Recording();
|
||||||
rec.setDownload(download);
|
rec.setDownload(download);
|
||||||
rec.setPath(download.getPath(model).replaceAll("\\\\", "/"));
|
rec.setPath(download.getPath(model).replaceAll("\\\\", "/"));
|
||||||
rec.setModel(model);
|
rec.setModel(model);
|
||||||
rec.setStartDate(Instant.ofEpochMilli(System.currentTimeMillis()));
|
rec.setStartDate(download.getStartTime());
|
||||||
recordingProcesses.put(model, rec);
|
recordingProcesses.put(model, rec);
|
||||||
recordingManager.add(rec);
|
recordingManager.add(rec);
|
||||||
completionService.submit(() -> {
|
completionService.submit(() -> {
|
||||||
|
@ -621,7 +631,7 @@ public class NextGenLocalRecorder implements Recorder {
|
||||||
for (Recording other : recordings) {
|
for (Recording other : recordings) {
|
||||||
if(other.equals(recording)) {
|
if(other.equals(recording)) {
|
||||||
Download download = other.getModel().createDownload();
|
Download download = other.getModel().createDownload();
|
||||||
download.init(Config.getInstance(), other.getModel());
|
download.init(Config.getInstance(), other.getModel(), other.getStartDate());
|
||||||
other.setDownload(download);
|
other.setDownload(download);
|
||||||
submitPostProcessingJob(other);
|
submitPostProcessingJob(other);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package ctbrec.recorder.download;
|
package ctbrec.recorder.download;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -15,12 +17,13 @@ public abstract class AbstractDownload implements Download {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(AbstractDownload.class);
|
private static final Logger LOG = LoggerFactory.getLogger(AbstractDownload.class);
|
||||||
|
|
||||||
protected void runPostProcessingScript(Recording recording) {
|
protected Instant startTime;
|
||||||
|
|
||||||
|
protected void runPostProcessingScript(Recording recording) throws IOException, InterruptedException {
|
||||||
String postProcessing = Config.getInstance().getSettings().postProcessing;
|
String postProcessing = Config.getInstance().getSettings().postProcessing;
|
||||||
if (postProcessing != null && !postProcessing.isEmpty()) {
|
if (postProcessing != null && !postProcessing.isEmpty()) {
|
||||||
File target = recording.getAbsoluteFile();
|
File target = recording.getAbsoluteFile();
|
||||||
Runtime rt = Runtime.getRuntime();
|
Runtime rt = Runtime.getRuntime();
|
||||||
try {
|
|
||||||
String[] args = new String[] {
|
String[] args = new String[] {
|
||||||
postProcessing,
|
postProcessing,
|
||||||
target.getParentFile().getAbsolutePath(),
|
target.getParentFile().getAbsolutePath(),
|
||||||
|
@ -45,9 +48,11 @@ public abstract class AbstractDownload implements Download {
|
||||||
|
|
||||||
process.waitFor();
|
process.waitFor();
|
||||||
LOG.debug("Process finished.");
|
LOG.debug("Process finished.");
|
||||||
} catch (Exception e) {
|
|
||||||
LOG.error("Error in process thread", e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Instant getStartTime() {
|
||||||
|
return startTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import ctbrec.Model;
|
||||||
import ctbrec.Recording;
|
import ctbrec.Recording;
|
||||||
|
|
||||||
public interface Download {
|
public interface Download {
|
||||||
public void init(Config config, Model model);
|
public void init(Config config, Model model, Instant startTime);
|
||||||
public void start() throws IOException;
|
public void start() throws IOException;
|
||||||
public void stop();
|
public void stop();
|
||||||
public Model getModel();
|
public Model getModel();
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package ctbrec.recorder.download;
|
||||||
|
|
||||||
|
public class ProcessExitedUncleanException extends RuntimeException {
|
||||||
|
public ProcessExitedUncleanException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,10 +32,11 @@ import org.slf4j.LoggerFactory;
|
||||||
import ctbrec.Config;
|
import ctbrec.Config;
|
||||||
import ctbrec.Model;
|
import ctbrec.Model;
|
||||||
import ctbrec.Recording;
|
import ctbrec.Recording;
|
||||||
import ctbrec.Recording.State;
|
|
||||||
import ctbrec.io.HttpClient;
|
import ctbrec.io.HttpClient;
|
||||||
|
import ctbrec.io.HttpException;
|
||||||
import ctbrec.recorder.download.AbstractDownload;
|
import ctbrec.recorder.download.AbstractDownload;
|
||||||
import ctbrec.recorder.download.dash.SegmentTimelineType.S;
|
import ctbrec.recorder.download.dash.SegmentTimelineType.S;
|
||||||
|
import ctbrec.recorder.download.hls.PostProcessingException;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
@ -53,7 +54,6 @@ public class DashDownload extends AbstractDownload {
|
||||||
private HttpClient httpClient;
|
private HttpClient httpClient;
|
||||||
private Config config;
|
private Config config;
|
||||||
private Model model;
|
private Model model;
|
||||||
private Instant startTime;
|
|
||||||
private Instant endTime;
|
private Instant endTime;
|
||||||
private Path downloadDir;
|
private Path downloadDir;
|
||||||
private String manifestUrl;
|
private String manifestUrl;
|
||||||
|
@ -80,7 +80,11 @@ public class DashDownload extends AbstractDownload {
|
||||||
.build(); // @formatter:on
|
.build(); // @formatter:on
|
||||||
LOG.trace("Loading manifest {}", url);
|
LOG.trace("Loading manifest {}", url);
|
||||||
try (Response response = httpClient.execute(request)) {
|
try (Response response = httpClient.execute(request)) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
return response.body().string();
|
return response.body().string();
|
||||||
|
} else {
|
||||||
|
throw new HttpException(response.code(), "Couldn't load manifest: " + response.message());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +185,7 @@ public class DashDownload extends AbstractDownload {
|
||||||
File segmentFile = new File(dir, prefix + '_' + df.format(c) + '_' + new File(absFile).getName());
|
File segmentFile = new File(dir, prefix + '_' + df.format(c) + '_' + new File(absFile).getName());
|
||||||
while (tries <= 10) {
|
while (tries <= 10) {
|
||||||
if (!segmentFile.exists() || segmentFile.length() == 0) {
|
if (!segmentFile.exists() || segmentFile.length() == 0) {
|
||||||
if (tries > 1) {
|
if (tries == 10) {
|
||||||
LOG.debug("Loading segment, try {}, {} {} {}", tries, response.code(), response.headers().values("Content-Length"), url);
|
LOG.debug("Loading segment, try {}, {} {} {}", tries, response.code(), response.headers().values("Content-Length"), url);
|
||||||
} else {
|
} else {
|
||||||
LOG.trace("Loading segment, try {}, {} {} {}", tries, response.code(), response.headers().values("Content-Length"), url);
|
LOG.trace("Loading segment, try {}, {} {} {}", tries, response.code(), response.headers().values("Content-Length"), url);
|
||||||
|
@ -204,11 +208,11 @@ public class DashDownload extends AbstractDownload {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Config config, Model model) {
|
public void init(Config config, Model model, Instant startTime) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.model = model;
|
this.model = model;
|
||||||
startTime = Instant.now();
|
this.startTime = startTime;
|
||||||
File finalFile = Config.getInstance().getFileForRecording(model, "mp4");
|
File finalFile = Config.getInstance().getFileForRecording(model, "mp4", startTime);
|
||||||
targetFile = new File(finalFile.getParentFile(), finalFile.getName() + ".part");
|
targetFile = new File(finalFile.getParentFile(), finalFile.getName() + ".part");
|
||||||
downloadDir = targetFile.toPath();
|
downloadDir = targetFile.toPath();
|
||||||
}
|
}
|
||||||
|
@ -241,10 +245,10 @@ public class DashDownload extends AbstractDownload {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean splitRecording() {
|
private boolean splitRecording() {
|
||||||
if(config.getSettings().splitRecordings > 0) {
|
if (config.getSettings().splitRecordings > 0) {
|
||||||
Duration recordingDuration = Duration.between(splitRecStartTime, ZonedDateTime.now());
|
Duration recordingDuration = Duration.between(splitRecStartTime, ZonedDateTime.now());
|
||||||
long seconds = recordingDuration.getSeconds();
|
long seconds = recordingDuration.getSeconds();
|
||||||
if(seconds >= config.getSettings().splitRecordings) {
|
if (seconds >= config.getSettings().splitRecordings) {
|
||||||
internalStop();
|
internalStop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -331,11 +335,6 @@ public class DashDownload extends AbstractDownload {
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Instant getStartTime() {
|
|
||||||
return startTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Duration getLength() {
|
public Duration getLength() {
|
||||||
return Duration.between(startTime, Optional.ofNullable(endTime).orElse(Instant.now()));
|
return Duration.between(startTime, Optional.ofNullable(endTime).orElse(Instant.now()));
|
||||||
|
@ -352,9 +351,8 @@ public class DashDownload extends AbstractDownload {
|
||||||
targetFile = file;
|
targetFile = file;
|
||||||
recording.setPath(path.substring(0, path.length() - 5));
|
recording.setPath(path.substring(0, path.length() - 5));
|
||||||
runPostProcessingScript(recording);
|
runPostProcessingScript(recording);
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
LOG.error("Error while merging dash segments", e);
|
throw new PostProcessingException(e);
|
||||||
recording.setStatus(State.FAILED);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import ctbrec.OS;
|
import ctbrec.OS;
|
||||||
import ctbrec.io.StreamRedirectThread;
|
import ctbrec.io.StreamRedirectThread;
|
||||||
|
import ctbrec.recorder.download.ProcessExitedUncleanException;
|
||||||
|
|
||||||
public class FfmpegMuxer {
|
public class FfmpegMuxer {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(FfmpegMuxer.class);
|
private static final Logger LOG = LoggerFactory.getLogger(FfmpegMuxer.class);
|
||||||
|
@ -96,10 +97,4 @@ public class FfmpegMuxer {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ProcessExitedUncleanException extends RuntimeException {
|
|
||||||
public ProcessExitedUncleanException(String msg) {
|
|
||||||
super(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -51,7 +50,6 @@ public abstract class AbstractHlsDownload extends AbstractDownload {
|
||||||
|
|
||||||
protected HttpClient client;
|
protected HttpClient client;
|
||||||
protected volatile boolean running = false;
|
protected volatile boolean running = false;
|
||||||
protected Instant startTime;
|
|
||||||
protected Model model = new UnknownModel();
|
protected Model model = new UnknownModel();
|
||||||
protected BlockingQueue<Runnable> downloadQueue = new LinkedBlockingQueue<>(50);
|
protected BlockingQueue<Runnable> downloadQueue = new LinkedBlockingQueue<>(50);
|
||||||
protected ExecutorService downloadThreadPool = new ThreadPoolExecutor(5, 5, 2, TimeUnit.MINUTES, downloadQueue, createThreadFactory());
|
protected ExecutorService downloadThreadPool = new ThreadPoolExecutor(5, 5, 2, TimeUnit.MINUTES, downloadQueue, createThreadFactory());
|
||||||
|
@ -180,11 +178,6 @@ public abstract class AbstractHlsDownload extends AbstractDownload {
|
||||||
|
|
||||||
abstract void internalStop();
|
abstract void internalStop();
|
||||||
|
|
||||||
@Override
|
|
||||||
public Instant getStartTime() {
|
|
||||||
return startTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Model getModel() {
|
public Model getModel() {
|
||||||
return model;
|
return model;
|
||||||
|
|
|
@ -64,14 +64,14 @@ public class HlsDownload extends AbstractHlsDownload {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Config config, Model model) {
|
public void init(Config config, Model model, Instant startTime) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
super.model = model;
|
super.model = model;
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Config.RECORDING_DATE_FORMAT);
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Config.RECORDING_DATE_FORMAT);
|
||||||
this.startTime = Instant.now();
|
this.startTime = startTime;
|
||||||
String startTime = formatter.format(ZonedDateTime.ofInstant(this.startTime, ZoneId.systemDefault()));
|
String formattedStartTime = formatter.format(ZonedDateTime.ofInstant(this.startTime, ZoneId.systemDefault()));
|
||||||
Path modelDir = FileSystems.getDefault().getPath(config.getSettings().recordingsDir, model.getSanitizedNamed());
|
Path modelDir = FileSystems.getDefault().getPath(config.getSettings().recordingsDir, model.getSanitizedNamed());
|
||||||
downloadDir = FileSystems.getDefault().getPath(modelDir.toString(), startTime);
|
downloadDir = FileSystems.getDefault().getPath(modelDir.toString(), formattedStartTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -179,7 +179,11 @@ public class HlsDownload extends AbstractHlsDownload {
|
||||||
recording.setStatusWithEvent(State.GENERATING_PLAYLIST);
|
recording.setStatusWithEvent(State.GENERATING_PLAYLIST);
|
||||||
generatePlaylist(recording);
|
generatePlaylist(recording);
|
||||||
recording.setStatusWithEvent(State.POST_PROCESSING);
|
recording.setStatusWithEvent(State.POST_PROCESSING);
|
||||||
|
try {
|
||||||
runPostProcessingScript(recording);
|
runPostProcessingScript(recording);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new PostProcessingException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected File generatePlaylist(Recording recording) {
|
protected File generatePlaylist(Recording recording) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import java.nio.file.Files;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.jcodec.containers.mp4.MP4Util;
|
import org.jcodec.containers.mp4.MP4Util;
|
||||||
|
@ -27,6 +28,7 @@ import ctbrec.io.HttpClient;
|
||||||
import ctbrec.io.StreamRedirectThread;
|
import ctbrec.io.StreamRedirectThread;
|
||||||
import ctbrec.recorder.ProgressListener;
|
import ctbrec.recorder.ProgressListener;
|
||||||
import ctbrec.recorder.RecordingManager;
|
import ctbrec.recorder.RecordingManager;
|
||||||
|
import ctbrec.recorder.download.ProcessExitedUncleanException;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
@ -40,9 +42,15 @@ public class MergedHlsDownload extends HlsDownload {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Config config, Model model) {
|
public void init(Config config, Model model, Instant startTime) {
|
||||||
super.init(config, model);
|
super.init(config, model, startTime);
|
||||||
finalFile = Config.getInstance().getFileForRecording(model, "mp4");
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
finalFile = Config.getInstance().getFileForRecording(model, "mp4", startTime);
|
||||||
|
downloadDir = finalFile.getParentFile().toPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,12 +68,12 @@ public class MergedHlsDownload extends HlsDownload {
|
||||||
}
|
}
|
||||||
|
|
||||||
runPostProcessingScript(recording);
|
runPostProcessingScript(recording);
|
||||||
} catch (PostProcessingException | IOException e) {
|
} catch (Exception e) {
|
||||||
LOG.error("An error occurred during post-processing", e);
|
throw new PostProcessingException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void postprocess(File playlist, File target) throws PostProcessingException {
|
private void postprocess(File playlist, File target) {
|
||||||
try {
|
try {
|
||||||
File dir = playlist.getParentFile();
|
File dir = playlist.getParentFile();
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
|
@ -90,7 +98,7 @@ public class MergedHlsDownload extends HlsDownload {
|
||||||
Files.delete(segment.toPath());
|
Files.delete(segment.toPath());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new PostProcessingException("FFmpeg exit code was " + exitCode);
|
throw new ProcessExitedUncleanException("FFmpeg exit code was " + exitCode);
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
|
@ -101,7 +109,7 @@ public class MergedHlsDownload extends HlsDownload {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void downloadFinishedRecording(String segmentPlaylistUri, File target, ProgressListener progressListener)
|
public void downloadFinishedRecording(String segmentPlaylistUri, File target, ProgressListener progressListener)
|
||||||
throws IOException, ParseException, PlaylistException, InvalidKeyException, NoSuchAlgorithmException, PostProcessingException {
|
throws IOException, ParseException, PlaylistException, InvalidKeyException, NoSuchAlgorithmException {
|
||||||
if (Config.getInstance().getSettings().requireAuthentication) {
|
if (Config.getInstance().getSettings().requireAuthentication) {
|
||||||
URL u = new URL(segmentPlaylistUri);
|
URL u = new URL(segmentPlaylistUri);
|
||||||
String path = u.getPath();
|
String path = u.getPath();
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package ctbrec.recorder.download.hls;
|
package ctbrec.recorder.download.hls;
|
||||||
|
|
||||||
public class PostProcessingException extends Exception {
|
public class PostProcessingException extends RuntimeException {
|
||||||
|
|
||||||
public PostProcessingException(String msg) {
|
public PostProcessingException(String msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PostProcessingException(Exception cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue