From 73eb13944c363c00c4d2c35e49417b44e50c5622 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 30 Jan 2021 13:03:42 +0100 Subject: [PATCH] Add new class RecordingDownload for recording downloads This class inherits from MergedFfmpegHlsDownload, but doesn't create a directory and start FFmpeg in the init method. Moved the dedicated methods for recording downloads from MergedFfmpegHlsDownload to RecordingDownload. --- .../main/java/ctbrec/RecordingDownload.java | 88 +++++++++++++++++++ .../java/ctbrec/ui/tabs/RecordingsTab.java | 4 +- .../download/hls/MergedFfmpegHlsDownload.java | 59 +------------ 3 files changed, 91 insertions(+), 60 deletions(-) create mode 100644 client/src/main/java/ctbrec/RecordingDownload.java diff --git a/client/src/main/java/ctbrec/RecordingDownload.java b/client/src/main/java/ctbrec/RecordingDownload.java new file mode 100644 index 00000000..23e2cedb --- /dev/null +++ b/client/src/main/java/ctbrec/RecordingDownload.java @@ -0,0 +1,88 @@ +package ctbrec; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; +import java.util.concurrent.ExecutorService; + +import com.iheartradio.m3u8.ParseException; +import com.iheartradio.m3u8.PlaylistException; + +import ctbrec.io.HttpClient; +import ctbrec.io.HttpException; +import ctbrec.recorder.ProgressListener; +import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload; +import ctbrec.recorder.download.hls.SegmentPlaylist; +import okhttp3.Request; +import okhttp3.Response; + +public class RecordingDownload extends MergedFfmpegHlsDownload { + + public RecordingDownload(HttpClient client) { + super(client); + } + + @Override + public void init(Config config, Model model, Instant startTime, ExecutorService executorService) throws IOException { + this.config = config; + this.model = model; + this.startTime = startTime; + this.downloadExecutor = executorService; + splittingStrategy = initSplittingStrategy(config.getSettings()); + } + + public void downloadFinishedRecording(String segmentPlaylistUri, File target, ProgressListener progressListener, long sizeInBytes) + throws InvalidKeyException, NoSuchAlgorithmException, IllegalStateException, InterruptedException, IOException, ParseException, PlaylistException { + running = true; + if (Config.getInstance().getSettings().requireAuthentication) { + URL u = new URL(segmentPlaylistUri); + String path = u.getPath(); + byte[] key = Config.getInstance().getSettings().key; + if (!Config.getInstance().getContextPath().isEmpty()) { + path = path.substring(Config.getInstance().getContextPath().length()); + } + String hmac = Hmac.calculate(path, key); + segmentPlaylistUri = segmentPlaylistUri + "?hmac=" + hmac; + } + + startFfmpegProcess(target); + for (int i = 0; i < 10 && ffmpegStdIn == null; i++) { + Thread.sleep(100); + } + + SegmentPlaylist segmentPlaylist = getNextSegments(segmentPlaylistUri); + long loadedBytes = 0; + for (String segmentUrl : segmentPlaylist.segments) { + loadedBytes += downloadFile(segmentUrl, loadedBytes, sizeInBytes, progressListener); + int progress = (int) (loadedBytes / (double) sizeInBytes * 100); + progressListener.update(progress); + } + + internalStop(); + } + + private long downloadFile(String fileUri, long loadedBytes, long totalBytes, ProgressListener progressListener) throws IOException { + long fileLoadedBytes = 0; + Request request = new Request.Builder().url(fileUri).addHeader("connection", "keep-alive").build(); + try (Response response = client.execute(request)) { + if (response.isSuccessful()) { + InputStream in = response.body().byteStream(); + byte[] b = new byte[1024 * 100]; + int length = -1; + while ((length = in.read(b)) >= 0) { + ffmpegStdIn.write(b, 0, length); + fileLoadedBytes += length; + int progress = (int) ((loadedBytes + fileLoadedBytes) / (double) totalBytes * 100); + progressListener.update(progress); + } + } else { + throw new HttpException(response.code(), response.message()); + } + } + return fileLoadedBytes; + } +} diff --git a/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java b/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java index fc74686c..f36083e0 100644 --- a/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/RecordingsTab.java @@ -32,6 +32,7 @@ import ctbrec.GlobalThreadPool; import ctbrec.Model; import ctbrec.Recording; import ctbrec.Recording.State; +import ctbrec.RecordingDownload; import ctbrec.StringUtil; import ctbrec.event.EventBusHolder; import ctbrec.event.RecordingStateChangedEvent; @@ -39,7 +40,6 @@ import ctbrec.io.UrlUtil; import ctbrec.recorder.ProgressListener; import ctbrec.recorder.Recorder; import ctbrec.recorder.RecordingPinnedException; -import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload; import ctbrec.ui.AutosizeAlert; import ctbrec.ui.CamrecApplication; import ctbrec.ui.DesktopIntegration; @@ -702,7 +702,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener, Shutdown download.start(url, target); } else { URL url = new URL(hlsBase + '/' + recording.getId() + "/playlist.m3u8"); - MergedFfmpegHlsDownload download = new MergedFfmpegHlsDownload(CamrecApplication.httpClient); + RecordingDownload download = new RecordingDownload(CamrecApplication.httpClient); download.init(config, recording.getModel(), Instant.now(), Executors.newSingleThreadExecutor()); LOG.info("Downloading {}", url); download.downloadFinishedRecording(url.toString(), target, createDownloadListener(recording), recording.getSizeInByte()); diff --git a/common/src/main/java/ctbrec/recorder/download/hls/MergedFfmpegHlsDownload.java b/common/src/main/java/ctbrec/recorder/download/hls/MergedFfmpegHlsDownload.java index 9eb892a0..725a5fe7 100644 --- a/common/src/main/java/ctbrec/recorder/download/hls/MergedFfmpegHlsDownload.java +++ b/common/src/main/java/ctbrec/recorder/download/hls/MergedFfmpegHlsDownload.java @@ -3,9 +3,7 @@ package ctbrec.recorder.download.hls; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; -import java.net.URL; import java.nio.file.Files; import java.time.Instant; import java.util.concurrent.BlockingQueue; @@ -21,17 +19,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Config; -import ctbrec.Hmac; import ctbrec.Model; import ctbrec.OS; import ctbrec.Recording; import ctbrec.io.HttpClient; -import ctbrec.io.HttpException; import ctbrec.recorder.FFmpeg; -import ctbrec.recorder.ProgressListener; import ctbrec.recorder.download.ProcessExitedUncleanException; -import okhttp3.Request; -import okhttp3.Response; public class MergedFfmpegHlsDownload extends AbstractHlsDownload { @@ -118,7 +111,7 @@ public class MergedFfmpegHlsDownload extends AbstractHlsDownload { internalStop(); } - private void startFfmpegProcess(File target) { + protected void startFfmpegProcess(File target) { try { String[] cmdline = prepareCommandLine(target); ffmpeg = new FFmpeg.Builder() @@ -204,56 +197,6 @@ public class MergedFfmpegHlsDownload extends AbstractHlsDownload { // nothing to do } - public void downloadFinishedRecording(String segmentPlaylistUri, File target, ProgressListener progressListener, long sizeInBytes) throws Exception { - running = true; - if (Config.getInstance().getSettings().requireAuthentication) { - URL u = new URL(segmentPlaylistUri); - String path = u.getPath(); - byte[] key = Config.getInstance().getSettings().key; - if (!Config.getInstance().getContextPath().isEmpty()) { - path = path.substring(Config.getInstance().getContextPath().length()); - } - String hmac = Hmac.calculate(path, key); - segmentPlaylistUri = segmentPlaylistUri + "?hmac=" + hmac; - } - - startFfmpegProcess(target); - for (int i = 0; i < 10 && ffmpegStdIn == null; i++) { - Thread.sleep(100); - } - - SegmentPlaylist segmentPlaylist = getNextSegments(segmentPlaylistUri); - long loadedBytes = 0; - for (String segmentUrl : segmentPlaylist.segments) { - loadedBytes += downloadFile(segmentUrl, loadedBytes, sizeInBytes, progressListener); - int progress = (int) (loadedBytes / (double) sizeInBytes * 100); - progressListener.update(progress); - } - - internalStop(); - } - - private long downloadFile(String fileUri, long loadedBytes, long totalBytes, ProgressListener progressListener) throws IOException { - long fileLoadedBytes = 0; - Request request = new Request.Builder().url(fileUri).addHeader("connection", "keep-alive").build(); - try (Response response = client.execute(request)) { - if (response.isSuccessful()) { - InputStream in = response.body().byteStream(); - byte[] b = new byte[1024 * 100]; - int length = -1; - while ((length = in.read(b)) >= 0) { - ffmpegStdIn.write(b, 0, length); - fileLoadedBytes += length; - int progress = (int) ((loadedBytes + fileLoadedBytes) / (double) totalBytes * 100); - progressListener.update(progress); - } - } else { - throw new HttpException(response.code(), response.message()); - } - } - return fileLoadedBytes; - } - @Override public boolean isSingleFile() { return true;