diff --git a/client/src/main/java/ctbrec/ui/RecordingsTab.java b/client/src/main/java/ctbrec/ui/RecordingsTab.java index 46bfb00f..e2d33cbe 100644 --- a/client/src/main/java/ctbrec/ui/RecordingsTab.java +++ b/client/src/main/java/ctbrec/ui/RecordingsTab.java @@ -429,10 +429,23 @@ public class RecordingsTab extends Tab implements TabSelectionListener { contextMenu.getItems().add(downloadRecording); } + MenuItem regenPlaylist = new MenuItem("Regenerate Playlist"); + regenPlaylist.setOnAction((e) -> { + try { + recorder.regeneratePlaylist(recordings.get(0)); + } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e1) { + showErrorDialog("Error while regenerating playlist", "The recording could not be regenerated", e1); + LOG.error("Error while regenerating playlist", e1); + } + }); + if (!Config.getInstance().getSettings().localRecording && recordings.get(0).getStatus() == State.FINISHED) { + contextMenu.getItems().add(regenPlaylist); + } + if(recordings.size() > 1) { openInPlayer.setDisable(true); openDir.setDisable(true); - downloadRecording.setDisable(true); + regenPlaylist.setDisable(true); } return contextMenu; diff --git a/common/src/main/java/ctbrec/recorder/LocalRecorder.java b/common/src/main/java/ctbrec/recorder/LocalRecorder.java index 914f2335..6f6b1290 100644 --- a/common/src/main/java/ctbrec/recorder/LocalRecorder.java +++ b/common/src/main/java/ctbrec/recorder/LocalRecorder.java @@ -830,4 +830,14 @@ public class LocalRecorder implements Recorder { throw new FileNotFoundException(playlist.getAbsolutePath() + " does not exist"); } } + + @Override + public void regeneratePlaylist(Recording recording) throws IOException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException { + new Thread(() -> { + LOG.debug("Regenerate playlist {}", recording.getPath()); + File recordingsDir = new File(config.getSettings().recordingsDir); + File path = new File(recordingsDir, recording.getPath()); + generatePlaylist(path); + }).start(); + } } diff --git a/common/src/main/java/ctbrec/recorder/Recorder.java b/common/src/main/java/ctbrec/recorder/Recorder.java index 1a9bf682..4fcdce1a 100644 --- a/common/src/main/java/ctbrec/recorder/Recorder.java +++ b/common/src/main/java/ctbrec/recorder/Recorder.java @@ -56,4 +56,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 regeneratePlaylist(Recording recording) throws IOException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException; } diff --git a/common/src/main/java/ctbrec/recorder/RemoteRecorder.java b/common/src/main/java/ctbrec/recorder/RemoteRecorder.java index c8629a7f..f64e68e2 100644 --- a/common/src/main/java/ctbrec/recorder/RemoteRecorder.java +++ b/common/src/main/java/ctbrec/recorder/RemoteRecorder.java @@ -437,6 +437,28 @@ public class RemoteRecorder implements Recorder { return spaceFree; } + @Override + public void regeneratePlaylist(Recording recording) throws IOException, InvalidKeyException, NoSuchAlgorithmException, IllegalStateException { + String msg = "{\"action\": \"regeneratePlaylist\", \"recording\": \""+recording.getPath()+"\"}"; + 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(); + RecordingListResponse resp = recordingListResponseAdapter.fromJson(json); + if(response.isSuccessful()) { + if(!resp.status.equals("success")) { + throw new IOException("Couldn't regenerate playlist for recording: " + resp.msg); + } + } else { + throw new IOException("Couldn't regenerate playlist recording: " + resp.msg); + } + } + } + private static class UnknownModel extends AbstractModel { @Override public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException { diff --git a/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java b/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java index c213ce2f..19113064 100644 --- a/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java +++ b/server/src/main/java/ctbrec/recorder/server/RecorderServlet.java @@ -120,6 +120,15 @@ public class RecorderServlet extends AbstractCtbrecServlet { resp.getWriter().write(recAdapter.toJson(rec)); resp.getWriter().write("]}"); break; + case "regeneratePlaylist": + path = request.recording; + rec = new Recording(path); + recorder.regeneratePlaylist(rec); + recAdapter = moshi.adapter(Recording.class); + resp.getWriter().write("{\"status\": \"success\", \"msg\": \"List of recordings\", \"recordings\": ["); + resp.getWriter().write(recAdapter.toJson(rec)); + resp.getWriter().write("]}"); + break; case "switch": recorder.switchStreamSource(request.model); response = "{\"status\": \"success\", \"msg\": \"Resolution switched\"}";