Change post-process return value to boolean

This commit is contained in:
0xb00bface 2020-09-30 20:09:52 +02:00
parent 4823c603a7
commit 99b21845ab
19 changed files with 183 additions and 29 deletions

View File

@ -1,11 +1,15 @@
package ctbrec.ui;
import java.io.File;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import java.util.Set;
import ctbrec.Config;
import ctbrec.Model;
import ctbrec.Recording;
import ctbrec.recorder.download.Download;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleStringProperty;
@ -158,6 +162,8 @@ public class JavaFxRecording extends Recording {
setProgress(updated.getProgress());
setSizeInByte(updated.getSizeInByte());
setSingleFile(updated.isSingleFile());
getAssociatedFiles().clear();
getAssociatedFiles().addAll(updated.getAssociatedFiles());
}
@Override
@ -246,5 +252,59 @@ public class JavaFxRecording extends Recording {
delegate.setId(id);
}
@Override
public void setStatusWithEvent(State status) {
delegate.setStatusWithEvent(status);
}
@Override
public File getPostProcessedFile() {
return delegate.getPostProcessedFile();
}
@Override
public void setPostProcessedFile(File postProcessedFile) {
delegate.setPostProcessedFile(postProcessedFile);
}
@Override
public Download getDownload() {
return delegate.getDownload();
}
@Override
public void setDownload(Download download) {
delegate.setDownload(download);
}
@Override
public Duration getLength() {
return delegate.getLength();
}
@Override
public void refresh() {
delegate.refresh();
}
@Override
public boolean canBePostProcessed() {
return delegate.canBePostProcessed();
}
@Override
public Set<String> getAssociatedFiles() {
return delegate.getAssociatedFiles();
}
@Override
public boolean hasContactSheet() {
return delegate.hasContactSheet();
}
@Override
public Optional<File> getContactSheet() {
return delegate.getContactSheet();
}
}

View File

@ -5,7 +5,6 @@ import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@ -23,11 +22,11 @@ import com.iheartradio.m3u8.ParseException;
import com.iheartradio.m3u8.PlaylistException;
import ctbrec.Config;
import ctbrec.Hmac;
import ctbrec.Model;
import ctbrec.OS;
import ctbrec.Recording;
import ctbrec.io.StreamRedirectThread;
import ctbrec.io.UrlUtil;
import ctbrec.recorder.download.StreamSource;
import ctbrec.ui.controls.Dialogs;
import javafx.scene.Scene;
@ -208,14 +207,7 @@ public class Player {
String hlsBase = Config.getInstance().getServerUrl() + "/hls";
String recUrl = hlsBase + '/' + rec.getId() + (rec.isSingleFile() ? "" : "/playlist.m3u8");
if (cfg.getSettings().requireAuthentication) {
URL u = new URL(recUrl);
String path = u.getPath();
if (!cfg.getContextPath().isEmpty()) {
path = path.substring(cfg.getContextPath().length());
}
byte[] key = cfg.getSettings().key;
String hmac = Hmac.calculate(path, key);
recUrl = recUrl + "?hmac=" + hmac;
recUrl = UrlUtil.addHmac(recUrl, cfg);
}
return recUrl;
}

View File

@ -15,7 +15,8 @@ public class DownloadPostprocessor extends AbstractPlaceholderAwarePostProcessor
}
@Override
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
public boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
// nothing really to do in here, we just inherit from AbstractPlaceholderAwarePostProcessor to use fillInPlaceHolders
return true;
}
}

View File

@ -33,6 +33,7 @@ import ctbrec.Recording.State;
import ctbrec.StringUtil;
import ctbrec.event.EventBusHolder;
import ctbrec.event.RecordingStateChangedEvent;
import ctbrec.io.UrlUtil;
import ctbrec.recorder.ProgressListener;
import ctbrec.recorder.Recorder;
import ctbrec.recorder.RecordingPinnedException;
@ -386,11 +387,16 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
JavaFxRecording first = recordings.get(0);
MenuItem openInPlayer = new MenuItem("Open in Player");
openInPlayer.setOnAction(e -> play(recordings.get(0)));
openInPlayer.setOnAction(e -> play(first));
if(first.getStatus() == FINISHED || Config.getInstance().getSettings().localRecording) {
contextMenu.getItems().add(openInPlayer);
}
MenuItem openContactSheet = new MenuItem("Open contact sheet");
openContactSheet.setOnAction(e -> openContactSheet(first));
openContactSheet.setDisable(!first.hasContactSheet());
contextMenu.getItems().add(openContactSheet);
// TODO find a way to reenable this
// MenuItem stopRecording = new MenuItem("Stop recording");
// stopRecording.setOnAction((e) -> {
@ -451,6 +457,32 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
return contextMenu;
}
private void openContactSheet(JavaFxRecording recording) {
if (config.getSettings().localRecording) {
recording.getContactSheet().ifPresent(f -> new Thread(() -> DesktopIntegration.open(f)).start());
} else {
recording.getContactSheet().ifPresent(f -> new Thread(() -> {
File target;
try {
target = File.createTempFile("cs_", ".jpg");
target.deleteOnExit();
FileDownload download = new FileDownload(CamrecApplication.httpClient, (p) -> {
if (p == 100) {
DesktopIntegration.open(target);
}
});
String url = config.getServerUrl() + "/hls/" + recording.getId() + "/contactsheet.jpg";
if (config.getSettings().requireAuthentication) {
url = UrlUtil.addHmac(url, config);
}
download.start(new URL(url), target);
} catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) {
Dialogs.showError(getTabPane().getScene(), "Download Error", "An error occurred while downloading the contact sheet", e);
}
}).start());
}
}
private void notes(JavaFxRecording recording) {
Node source = getTabPane();
String notes = recording.getNote();

View File

@ -18,6 +18,7 @@ import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import org.slf4j.Logger;
@ -312,4 +313,15 @@ public class Recording implements Serializable {
public Set<String> getAssociatedFiles() {
return associatedFiles;
}
public boolean hasContactSheet() {
return getAssociatedFiles().stream().anyMatch(path -> path.endsWith("contactsheet.jpg"));
}
public Optional<File> getContactSheet() {
return getAssociatedFiles().stream()
.filter(path -> path.endsWith("contactsheet.jpg"))
.findFirst()
.map(File::new);
}
}

View File

@ -0,0 +1,29 @@
package ctbrec.io;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import ctbrec.Config;
import ctbrec.Hmac;
public class UrlUtil {
private UrlUtil() {}
public static String addHmac(String url, Config config) throws MalformedURLException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException, UnsupportedEncodingException {
URL u = new URL(url);
String path = u.getPath();
if (!config.getContextPath().isEmpty()) {
path = path.substring(config.getContextPath().length());
}
byte[] key = config.getSettings().key;
String hmac = Hmac.calculate(path, key);
url = url + "?hmac=" + hmac;
return url;
}
}

View File

@ -164,7 +164,10 @@ public class NextGenLocalRecorder implements Recorder {
List<PostProcessor> postProcessors = config.getSettings().postProcessors;
for (PostProcessor postProcessor : postProcessors) {
LOG.debug("Running post-processor: {}", postProcessor.getName());
postProcessor.postprocess(recording, recordingManager, config);
boolean continuePP = postProcessor.postprocess(recording, recordingManager, config);
if (!continuePP) {
break;
}
}
setRecordingStatus(recording, State.FINISHED);
recordingManager.saveRecording(recording);
@ -632,6 +635,7 @@ public class NextGenLocalRecorder implements Recorder {
download.init(Config.getInstance(), other.getModel(), other.getStartDate());
other.setDownload(download);
other.setPostProcessedFile(null);
other.setStatus(State.WAITING);
submitPostProcessingJob(other);
return;
}

View File

@ -22,7 +22,7 @@ public class Copy extends AbstractPostProcessor {
}
@Override
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
public boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
File orig = rec.getPostProcessedFile();
String copyFilename = getFilenameForCopy(orig);
File copy = new File(orig.getParentFile(), copyFilename);
@ -34,6 +34,7 @@ public class Copy extends AbstractPostProcessor {
}
rec.setPostProcessedFile(copy);
rec.getAssociatedFiles().add(copy.getCanonicalPath());
return true;
}
private String getFilenameForCopy(File orig) {

View File

@ -35,7 +35,7 @@ public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor {
}
@Override
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
public boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
int totalWidth = Integer.parseInt(getConfig().getOrDefault(TOTAL_SIZE, "1920"));
int padding = Integer.parseInt(getConfig().getOrDefault(PADDING, "4"));
int cols = Integer.parseInt(getConfig().getOrDefault(COLS, "8"));
@ -73,13 +73,16 @@ public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor {
"0",
"-qscale:v",
"3",
"-y",
"-frames:v",
"1",
output.getCanonicalPath()
};
String[] cmdline = OS.getFFmpegCommand(args);
LOG.info("Executing {} in working directory {}", Arrays.toString(cmdline), executionDir);
Process ffmpeg = Runtime.getRuntime().exec(cmdline, new String[0], executionDir);
int exitCode = 1;
File ffmpegLog = File.createTempFile("create_contact_sheet_" + input.getName(), ".log");
File ffmpegLog = File.createTempFile("create_contact_sheet_" + rec.getId() + '_', ".log");
try (FileOutputStream mergeLogStream = new FileOutputStream(ffmpegLog)) {
Thread stdout = new Thread(new StreamRedirectThread(ffmpeg.getInputStream(), mergeLogStream));
Thread stderr = new Thread(new StreamRedirectThread(ffmpeg.getErrorStream(), mergeLogStream));
@ -91,12 +94,13 @@ public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor {
stderr.join();
mergeLogStream.flush();
}
rec.getAssociatedFiles().add(output.getCanonicalPath());
if (exitCode != 1) {
rec.getAssociatedFiles().add(output.getCanonicalPath());
if (ffmpegLog.exists()) {
Files.delete(ffmpegLog.toPath());
}
}
return true;
}
private File getInputFile(Recording rec) {

View File

@ -15,7 +15,9 @@ public class CreateTimelineThumbs extends AbstractPlaceholderAwarePostProcessor
}
@Override
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
public boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
// create 1 thumb every second with a width of 360 pixels and save it as jpeg with a 5-digit sequence number
// ffmpeg -i <file> -vf 'fps=1,scale=360:-1' thumbs/out%05d.jpg
throw new NotImplementedExcetion();
}
}

View File

@ -17,7 +17,7 @@ public class DeleteOriginal extends AbstractPostProcessor {
}
@Override
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
public boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
if (rec.getAbsoluteFile().isFile()) {
Files.deleteIfExists(rec.getAbsoluteFile().toPath());
deleteEmptyParents(rec.getAbsoluteFile().getParentFile());
@ -27,5 +27,6 @@ public class DeleteOriginal extends AbstractPostProcessor {
}
rec.setAbsoluteFile(rec.getPostProcessedFile());
rec.getAssociatedFiles().remove(rec.getAbsoluteFile().getCanonicalPath());
return true;
}
}

View File

@ -21,14 +21,16 @@ public class DeleteTooShort extends AbstractPostProcessor {
}
@Override
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException {
public boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException {
Duration minimumLengthInSeconds = Duration.ofSeconds(Integer.parseInt(getConfig().getOrDefault(MIN_LEN_IN_SECS, "0")));
if (minimumLengthInSeconds.getSeconds() > 0) {
Duration recordingLength = rec.getLength();
if (!(recordingLength.isNegative() || recordingLength.isZero()) && recordingLength.compareTo(minimumLengthInSeconds) < 0) {
LOG.info("Deleting too short recording {} [{} < {}]", rec, recordingLength, minimumLengthInSeconds);
recordingManager.delete(rec);
return false;
}
}
return true;
}
}

View File

@ -25,14 +25,14 @@ public class Move extends AbstractPlaceholderAwarePostProcessor {
}
@Override
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException {
public boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException {
String pathTemplate = getConfig().getOrDefault(PATH_TEMPLATE, DEFAULT);
String path = fillInPlaceHolders(pathTemplate, rec, config);
File src = rec.getPostProcessedFile();
boolean isFile = src.isFile();
File target = new File(path, src.getName());
if (Objects.equals(src, target)) {
return;
return true;
}
LOG.info("Moving {} to {}", src.getName(), target.getParentFile().getCanonicalPath());
Files.createDirectories(target.getParentFile().toPath());
@ -48,6 +48,7 @@ public class Move extends AbstractPlaceholderAwarePostProcessor {
} else {
deleteEmptyParents(src);
}
return true;
}
@Override

View File

@ -11,7 +11,16 @@ import ctbrec.recorder.RecordingManager;
public interface PostProcessor extends Serializable {
String getName();
void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException;
/**
* Runs the post-processing code on the given recording
* @param rec the recording to post-process
* @param recordingManager
* @param config
* @return false to stop futher post-processing, true to continue
* @throws IOException
* @throws InterruptedException
*/
boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException;
Map<String, String> getConfig();
void setConfig(Map<String, String> conf);

View File

@ -14,8 +14,9 @@ public class RemoveKeepFile extends AbstractPostProcessor {
}
@Override
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
public boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
recordingManager.remove(rec);
rec.setMetaDataFile(null);
return true;
}
}

View File

@ -31,7 +31,7 @@ public class Remux extends AbstractPostProcessor {
}
@Override
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
public boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
String fileExt = getConfig().get(FILE_EXT);
String[] args = getConfig().get(FFMPEG_ARGS).split(" ");
String[] argsPlusFile = new String[args.length + 3];
@ -67,6 +67,7 @@ public class Remux extends AbstractPostProcessor {
IoUtils.deleteEmptyParents(inputFile.getParentFile());
rec.getAssociatedFiles().remove(inputFile.getCanonicalPath());
rec.getAssociatedFiles().add(remuxedFile.getCanonicalPath());
return true;
}
private void setupLogging(Process ffmpeg, Recording rec) throws IOException, InterruptedException {

View File

@ -24,14 +24,14 @@ public class Rename extends AbstractPlaceholderAwarePostProcessor {
}
@Override
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException {
public boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException {
String defaultTemplate = rec.isSingleFile() ? DEFAULT : DEFAULT_DIR;
String filenameTemplate = getConfig().getOrDefault(FILE_NAME_TEMPLATE, defaultTemplate);
String filename = fillInPlaceHolders(filenameTemplate, rec, config);
File src = rec.getPostProcessedFile();
File target = new File(src.getParentFile(), filename);
if (Objects.equals(src, target)) {
return;
return true;
}
LOG.info("Renaming {} to {}", src.getName(), target.getName());
Files.move(rec.getPostProcessedFile().toPath(), target.toPath());
@ -41,6 +41,7 @@ public class Rename extends AbstractPlaceholderAwarePostProcessor {
}
rec.getAssociatedFiles().remove(src.getCanonicalPath());
rec.getAssociatedFiles().add(target.getCanonicalPath());
return true;
}
@Override

View File

@ -27,7 +27,7 @@ public class Script extends AbstractPlaceholderAwarePostProcessor {
}
@Override
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
public boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
List<String> cmdline = buildCommandLine(rec, config);
Runtime rt = Runtime.getRuntime();
String[] args = cmdline.toArray(new String[0]);
@ -41,6 +41,7 @@ public class Script extends AbstractPlaceholderAwarePostProcessor {
if (exitCode != 0) {
throw new ProcessExitedUncleanException("Script finished with exit code " + exitCode);
}
return true;
}
private List<String> buildCommandLine(Recording rec, Config config) throws IOException {

View File

@ -25,7 +25,7 @@ public class Webhook extends AbstractPlaceholderAwarePostProcessor {
}
@Override
public void postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
public boolean postprocess(Recording rec, RecordingManager recordingManager, Config config) throws IOException, InterruptedException {
throw new NotImplementedExcetion();
}