forked from j62/ctbrec
Add menu entry to rerun the post-processing script
In client/server mode this will trigger the playlist generation and post-processing. In standalone mode only the post-processing will be executed.
This commit is contained in:
parent
da486bbf4a
commit
b136fce0db
|
@ -1,6 +1,10 @@
|
|||
1.21.2
|
||||
2.0.0
|
||||
========================
|
||||
* Complete rewrite of the recording code
|
||||
* Added split recordings for the server
|
||||
* Added menu entry to rerun the post-processing script
|
||||
* Fix: CamSoda overview
|
||||
* Fix: BongaCams model online check
|
||||
* Fix: Downloads not working in client/server setup (regression in last version)
|
||||
* Fix: post-processing for split recordings
|
||||
* Fix: All recordings are finished properly on shutdown (with playlist
|
||||
|
|
|
@ -78,9 +78,6 @@ public class JavaFxRecording extends Recording {
|
|||
case POST_PROCESSING:
|
||||
statusProperty.set("post-processing");
|
||||
break;
|
||||
case STOPPED:
|
||||
statusProperty.set("stopped");
|
||||
break;
|
||||
case DELETED:
|
||||
statusProperty.set("deleted");
|
||||
break;
|
||||
|
|
|
@ -373,11 +373,12 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
|
|||
contextMenu.setAutoHide(true);
|
||||
contextMenu.setAutoFix(true);
|
||||
|
||||
JavaFxRecording first = recordings.get(0);
|
||||
MenuItem openInPlayer = new MenuItem("Open in Player");
|
||||
openInPlayer.setOnAction((e) -> {
|
||||
play(recordings.get(0));
|
||||
});
|
||||
if(recordings.get(0).getStatus() == State.FINISHED || Config.getInstance().getSettings().localRecording) {
|
||||
if(first.getStatus() == State.FINISHED || Config.getInstance().getSettings().localRecording) {
|
||||
contextMenu.getItems().add(openInPlayer);
|
||||
}
|
||||
|
||||
|
@ -399,14 +400,14 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
|
|||
deleteRecording.setOnAction((e) -> {
|
||||
delete(recordings);
|
||||
});
|
||||
if(recordings.get(0).getStatus() == State.FINISHED || recordings.size() > 1) {
|
||||
if(first.getStatus() == State.FINISHED || first.getStatus() == State.WAITING || first.getStatus() == State.FAILED || recordings.size() > 1) {
|
||||
contextMenu.getItems().add(deleteRecording);
|
||||
}
|
||||
|
||||
MenuItem openDir = new MenuItem("Open directory");
|
||||
openDir.setOnAction((e) -> {
|
||||
String recordingsDir = Config.getInstance().getSettings().recordingsDir;
|
||||
String path = recordings.get(0).getPath();
|
||||
String path = first.getPath();
|
||||
File tsFile = new File(recordingsDir, path);
|
||||
new Thread(() -> {
|
||||
DesktopIntegration.open(tsFile.getParent());
|
||||
|
@ -419,24 +420,44 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
|
|||
MenuItem downloadRecording = new MenuItem("Download");
|
||||
downloadRecording.setOnAction((e) -> {
|
||||
try {
|
||||
download(recordings.get(0));
|
||||
download(first);
|
||||
} catch (IOException | ParseException | PlaylistException e1) {
|
||||
showErrorDialog("Error while downloading recording", "The recording could not be downloaded", e1);
|
||||
LOG.error("Error while downloading recording", e1);
|
||||
}
|
||||
});
|
||||
if (!Config.getInstance().getSettings().localRecording && recordings.get(0).getStatus() == State.FINISHED) {
|
||||
if (!Config.getInstance().getSettings().localRecording && first.getStatus() == State.FINISHED) {
|
||||
contextMenu.getItems().add(downloadRecording);
|
||||
}
|
||||
|
||||
MenuItem rerunPostProcessing = new MenuItem("Rerun Post-Processing");
|
||||
rerunPostProcessing.setOnAction((e) -> {
|
||||
triggerPostProcessing(first);
|
||||
});
|
||||
if (first.getStatus() == State.FINISHED || first.getStatus() == State.WAITING) {
|
||||
contextMenu.getItems().add(rerunPostProcessing);
|
||||
}
|
||||
|
||||
if(recordings.size() > 1) {
|
||||
openInPlayer.setDisable(true);
|
||||
openDir.setDisable(true);
|
||||
rerunPostProcessing.setDisable(true);
|
||||
}
|
||||
|
||||
return contextMenu;
|
||||
}
|
||||
|
||||
private void triggerPostProcessing(JavaFxRecording first) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
recorder.rerunPostProcessing(first.getDelegate());
|
||||
} catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e1) {
|
||||
showErrorDialog("Error while starting post-processing", "The post-processing could not be started", e1);
|
||||
LOG.error("Error while starting post-processing", e1);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void download(Recording recording) throws IOException, ParseException, PlaylistException {
|
||||
String filename = recording.getPath().substring(1).replaceAll("/", "-") + ".ts";
|
||||
FileChooser chooser = new FileChooser();
|
||||
|
@ -455,40 +476,37 @@ public class RecordingsTab extends Tab implements TabSelectionListener {
|
|||
URL url = new URL(hlsBase + recording.getPath() + "/playlist.m3u8");
|
||||
LOG.info("Downloading {}", recording.getPath());
|
||||
|
||||
Thread t = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
MergedHlsDownload download = new MergedHlsDownload(CamrecApplication.httpClient);
|
||||
download.start(url.toString(), target, (progress) -> {
|
||||
Platform.runLater(() -> {
|
||||
if (progress == 100) {
|
||||
recording.setStatus(FINISHED);
|
||||
recording.setProgress(-1);
|
||||
LOG.debug("Download finished for recording {}", recording.getPath());
|
||||
} else {
|
||||
recording.setStatus(DOWNLOADING);
|
||||
recording.setProgress(progress);
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (FileNotFoundException e) {
|
||||
showErrorDialog("Error while downloading recording", "The target file couldn't be created", e);
|
||||
LOG.error("Error while downloading recording", e);
|
||||
} catch (IOException e) {
|
||||
showErrorDialog("Error while downloading recording", "The recording could not be downloaded", e);
|
||||
LOG.error("Error while downloading recording", e);
|
||||
} finally {
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
MergedHlsDownload download = new MergedHlsDownload(CamrecApplication.httpClient);
|
||||
download.start(url.toString(), target, (progress) -> {
|
||||
Platform.runLater(() -> {
|
||||
if (progress == 100) {
|
||||
recording.setStatus(FINISHED);
|
||||
recording.setProgress(-1);
|
||||
LOG.debug("Download finished for recording {}", recording.getPath());
|
||||
} else {
|
||||
recording.setStatus(DOWNLOADING);
|
||||
recording.setProgress(progress);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (FileNotFoundException e) {
|
||||
showErrorDialog("Error while downloading recording", "The target file couldn't be created", e);
|
||||
LOG.error("Error while downloading recording", e);
|
||||
} catch (IOException e) {
|
||||
showErrorDialog("Error while downloading recording", "The recording could not be downloaded", e);
|
||||
LOG.error("Error while downloading recording", e);
|
||||
} finally {
|
||||
Platform.runLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
recording.setStatus(FINISHED);
|
||||
recording.setProgress(-1);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
t.setDaemon(true);
|
||||
t.setName("Download Thread " + recording.getPath());
|
||||
t.start();
|
||||
|
|
|
@ -27,7 +27,6 @@ public class Recording {
|
|||
|
||||
public static enum State {
|
||||
RECORDING("recording"),
|
||||
STOPPED("stopped"),
|
||||
GENERATING_PLAYLIST("generating playlist"),
|
||||
POST_PROCESSING("post-processing"),
|
||||
FINISHED("finished"),
|
||||
|
|
|
@ -109,24 +109,7 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
}
|
||||
if (recording.getStatus() == State.WAITING) {
|
||||
LOG.debug("Download finished for {} -> Starting post-processing", recording.getModel().getName());
|
||||
ppPool.submit(() -> {
|
||||
try {
|
||||
setRecordingStatus(recording, State.POST_PROCESSING);
|
||||
recordingManager.saveRecording(recording);
|
||||
recording.postprocess();
|
||||
setRecordingStatus(recording, State.FINISHED);
|
||||
recordingManager.saveRecording(recording);
|
||||
deleteIfTooShort(recording);
|
||||
} catch (Exception e) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
submitPostProcessingJob(recording);
|
||||
|
||||
// check, if we have to restart the recording
|
||||
Model model = recording.getModel();
|
||||
|
@ -158,6 +141,27 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
}, 1, 1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void submitPostProcessingJob(Recording recording) {
|
||||
ppPool.submit(() -> {
|
||||
try {
|
||||
setRecordingStatus(recording, State.POST_PROCESSING);
|
||||
recordingManager.saveRecording(recording);
|
||||
recording.postprocess();
|
||||
setRecordingStatus(recording, State.FINISHED);
|
||||
recordingManager.saveRecording(recording);
|
||||
deleteIfTooShort(recording);
|
||||
} catch (Exception e) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setRecordingStatus(Recording recording, State status) {
|
||||
recording.setStatus(status);
|
||||
RecordingStateChangedEvent evt = new RecordingStateChangedEvent(recording.getDownload().getTarget(), status, recording.getModel(),
|
||||
|
@ -603,4 +607,19 @@ public class NextGenLocalRecorder implements Recorder {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rerunPostProcessing(Recording recording) {
|
||||
List<Recording> recordings = recordingManager.getAll();
|
||||
for (Recording other : recordings) {
|
||||
if(other.equals(recording)) {
|
||||
Download download = other.getModel().createDownload();
|
||||
download.init(Config.getInstance(), other.getModel());
|
||||
other.setDownload(download);
|
||||
submitPostProcessingJob(other);
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG.error("Recording {} not found. Can't rerun post-processing", recording);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,4 +82,15 @@ public interface Recorder {
|
|||
* @throws IOException
|
||||
*/
|
||||
public long getFreeSpaceBytes() throws IOException;
|
||||
|
||||
/**
|
||||
* Regenerate the playlist for a recording. This is helpful, if the
|
||||
* playlist is corrupt or hasn't been generated for whatever reason
|
||||
* @param recording
|
||||
* @throws IllegalStateException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws InvalidKeyException
|
||||
* @throws IOException
|
||||
*/
|
||||
public void rerunPostProcessing(Recording recording) throws IOException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ public class RemoteRecorder implements Recorder {
|
|||
private JsonAdapter<RecordingListResponse> recordingListResponseAdapter = moshi.adapter(RecordingListResponse.class);
|
||||
private JsonAdapter<ModelRequest> modelRequestAdapter = moshi.adapter(ModelRequest.class);
|
||||
private JsonAdapter<RecordingRequest> recordingRequestAdapter = moshi.adapter(RecordingRequest.class);
|
||||
private JsonAdapter<SimpleResponse> simpleResponseAdapter = moshi.adapter(SimpleResponse.class);
|
||||
|
||||
private List<Model> models = Collections.emptyList();
|
||||
private List<Model> onlineModels = Collections.emptyList();
|
||||
|
@ -321,6 +322,11 @@ public class RemoteRecorder implements Recorder {
|
|||
public List<Model> models;
|
||||
}
|
||||
|
||||
private static class SimpleResponse {
|
||||
public String status;
|
||||
public String msg;
|
||||
}
|
||||
|
||||
private static class RecordingListResponse {
|
||||
public String status;
|
||||
public String msg;
|
||||
|
@ -459,4 +465,27 @@ public class RemoteRecorder implements Recorder {
|
|||
public long getFreeSpaceBytes() {
|
||||
return spaceFree;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rerunPostProcessing(Recording recording) throws IOException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException {
|
||||
RecordingRequest recReq = new RecordingRequest("rerunPostProcessing", recording);
|
||||
String msg = recordingRequestAdapter.toJson(recReq);
|
||||
LOG.debug(msg);
|
||||
RequestBody body = RequestBody.create(JSON, msg);
|
||||
Request.Builder builder = new Request.Builder().url("http://" + config.getSettings().httpServer + ":" + config.getSettings().httpPort + "/rec")
|
||||
.post(body);
|
||||
addHmacIfNeeded(msg, builder);
|
||||
Request request = builder.build();
|
||||
try (Response response = client.execute(request)) {
|
||||
String json = response.body().string();
|
||||
SimpleResponse resp = simpleResponseAdapter.fromJson(json);
|
||||
if (response.isSuccessful()) {
|
||||
if (!resp.status.equals("success")) {
|
||||
throw new IOException("Couldn't start post-processing for recording: " + resp.msg);
|
||||
}
|
||||
} else {
|
||||
throw new HttpException(response.code(), response.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -192,12 +192,13 @@ public abstract class AbstractHlsDownload implements Download {
|
|||
|
||||
@Override
|
||||
public void postprocess(Recording recording) {
|
||||
runPostProcessingScript(recording.getAbsoluteFile());
|
||||
runPostProcessingScript(recording);
|
||||
}
|
||||
|
||||
private void runPostProcessingScript(File target) {
|
||||
private void runPostProcessingScript(Recording recording) {
|
||||
String postProcessing = Config.getInstance().getSettings().postProcessing;
|
||||
if (postProcessing != null && !postProcessing.isEmpty()) {
|
||||
File target = recording.getAbsoluteFile();
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
try {
|
||||
String[] args = new String[] {
|
||||
|
@ -206,7 +207,7 @@ public abstract class AbstractHlsDownload implements Download {
|
|||
target.getAbsolutePath(),
|
||||
getModel().getName(),
|
||||
getModel().getSite().getName(),
|
||||
Long.toString(getStartTime().getEpochSecond())
|
||||
Long.toString(recording.getStartDate().getEpochSecond())
|
||||
};
|
||||
LOG.debug("Running {}", Arrays.toString(args));
|
||||
Process process = rt.exec(args, OS.getEnvironment());
|
||||
|
|
|
@ -129,6 +129,11 @@ public class RecorderServlet extends AbstractCtbrecServlet {
|
|||
resp.getWriter().write(recAdapter.toJson(request.recording));
|
||||
resp.getWriter().write("]}");
|
||||
break;
|
||||
case "rerunPostProcessing":
|
||||
recorder.rerunPostProcessing(request.recording);
|
||||
recAdapter = moshi.adapter(Recording.class);
|
||||
resp.getWriter().write("{\"status\": \"success\", \"msg\": \"Post-Processing triggered\"}");
|
||||
break;
|
||||
case "switch":
|
||||
recorder.switchStreamSource(request.model);
|
||||
response = "{\"status\": \"success\", \"msg\": \"Resolution switched\"}";
|
||||
|
|
Loading…
Reference in New Issue