From a5e047124e7e254ea96df6ac68af1a70d7e468d3 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sun, 9 May 2021 11:11:10 +0200 Subject: [PATCH] Improve Showup websocket error handling --- CHANGELOG.md | 1 + .../ctbrec/sites/showup/ShowupDownload.java | 32 ---- .../sites/showup/ShowupMergedDownload.java | 139 ------------------ .../sites/showup/ShowupWebrtcDownload.java | 45 ++++-- 4 files changed, 34 insertions(+), 183 deletions(-) delete mode 100644 common/src/main/java/ctbrec/sites/showup/ShowupDownload.java delete mode 100644 common/src/main/java/ctbrec/sites/showup/ShowupMergedDownload.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 10015c0e..cf993061 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ 4.2.1 ======================== +* Fixed Showup.tv downloads using the websocket stream instead of HLS * Fixed bug, which caused the window to stay invisible after being minimized to tray on windows diff --git a/common/src/main/java/ctbrec/sites/showup/ShowupDownload.java b/common/src/main/java/ctbrec/sites/showup/ShowupDownload.java deleted file mode 100644 index a396d51f..00000000 --- a/common/src/main/java/ctbrec/sites/showup/ShowupDownload.java +++ /dev/null @@ -1,32 +0,0 @@ -package ctbrec.sites.showup; - -import java.io.File; -import java.io.IOException; - -import com.iheartradio.m3u8.ParseException; -import com.iheartradio.m3u8.PlaylistException; - -import ctbrec.Recording; -import ctbrec.io.HttpClient; -import ctbrec.recorder.PlaylistGenerator; -import ctbrec.recorder.download.hls.HlsDownload; - -public class ShowupDownload extends HlsDownload { - - public ShowupDownload(HttpClient client) { - super(client); - } - - @Override - protected File generatePlaylist(Recording recording) throws IOException, ParseException, PlaylistException { - File recDir = recording.getAbsoluteFile(); - if (!config.getSettings().generatePlaylist) { - return null; - } - PlaylistGenerator playlistGenerator = PlaylistGenerator.newInstance(config.getSettings().fastPlaylistGenerator); - playlistGenerator.addProgressListener(recording::setProgress); - File playlist = playlistGenerator.generate(recDir, "mp4"); - recording.setProgress(-1); - return playlist; - } -} diff --git a/common/src/main/java/ctbrec/sites/showup/ShowupMergedDownload.java b/common/src/main/java/ctbrec/sites/showup/ShowupMergedDownload.java deleted file mode 100644 index b726d081..00000000 --- a/common/src/main/java/ctbrec/sites/showup/ShowupMergedDownload.java +++ /dev/null @@ -1,139 +0,0 @@ -package ctbrec.sites.showup; - -import static ctbrec.io.HttpConstants.*; - -import java.io.IOException; -import java.io.InputStream; -import java.time.Instant; -import java.util.concurrent.ExecutorService; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.iheartradio.m3u8.ParseException; -import com.iheartradio.m3u8.PlaylistException; - -import ctbrec.Config; -import ctbrec.Model; -import ctbrec.io.BandwidthMeter; -import ctbrec.io.HttpClient; -import ctbrec.io.HttpException; -import ctbrec.recorder.download.ProcessExitedUncleanException; -import ctbrec.recorder.download.hls.AbstractHlsDownload; -import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload; -import ctbrec.recorder.download.hls.SegmentPlaylist; -import okhttp3.Request; -import okhttp3.Response; - -public class ShowupMergedDownload extends MergedFfmpegHlsDownload { - - private static final transient Logger LOG = LoggerFactory.getLogger(ShowupMergedDownload.class); - - private transient Response response; - private transient InputStream in; - private transient byte[] buffer = new byte[10240]; - - public ShowupMergedDownload(HttpClient client) { - super(client); - } - - @Override - public void init(Config config, Model model, Instant startTime, ExecutorService executorService) throws IOException { - super.init(config, model, startTime, executorService); - try { - if (segmentPlaylistUrl == null) { - segmentPlaylistUrl = getSegmentPlaylistUrl(model); - } - startDownload(segmentPlaylistUrl); - } catch (Exception e) { - LOG.error("Error starting the stream", e); - stop(); - } - } - - protected void startDownload(String segmentPlaylistUri) throws IOException, ParseException, PlaylistException { - try { - SegmentPlaylist lsp = getNextSegments(segmentPlaylistUri); - emptyPlaylistCheck(lsp); - - String segment = lsp.segments.get(0); - Request request = new Request.Builder().url(segment) - .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) - .header(CONNECTION, KEEP_ALIVE) - .build(); - response = client.execute(request); - if (response.isSuccessful()) { - in = response.body().byteStream(); - } else { - throw new HttpException(response.code(), response.message()); - } - } catch (HttpException e) { - handleHttpException(e); - } catch (Exception e) { - LOG.info("Unexpected error while downloading {}", model, e); - stop(); - } - } - - @Override - public AbstractHlsDownload call() throws Exception { - if (!running) { - return this; - } - - try { - if (!ffmpegProcess.isAlive()) { - running = false; - int exitValue = ffmpegProcess.exitValue(); - ffmpeg.shutdown(exitValue); - } - } catch (ProcessExitedUncleanException e) { - LOG.error("FFmpeg exited unclean", e); - } - - streamSegmentDataToFfmpeg(); - rescheduleTime = Instant.now().plusSeconds(1); - return this; - } - - @Override - protected void streamSegmentDataToFfmpeg() { - downloadExecutor.submit(() -> { - if (!ffmpegStreamLock.tryLock()) { - return; - } - try { - int length = -1; - boolean keepGoing = true; - while ((length = in.read(buffer)) > 0 && keepGoing) { - BandwidthMeter.add(length); - ffmpegStdIn.write(buffer, 0, length); - keepGoing = running && !Thread.interrupted() && model.isOnline(true); - splittingStrategy.splitNecessary(this); - } - if (length == -1) { - LOG.info("End of stream reached for {}", model); - stop(); - } - LOG.debug("loop finished"); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (Exception e) { - LOG.error("Error while streaming data to FFmpeg", e); - } finally { - ffmpegStreamLock.unlock(); - } - }); - } - - @Override - public void finalizeDownload() { - super.finalizeDownload(); - try { - in.close(); - response.close(); - } catch (IOException e) { - LOG.error("Error while finalizing download", e); - } - } -} diff --git a/common/src/main/java/ctbrec/sites/showup/ShowupWebrtcDownload.java b/common/src/main/java/ctbrec/sites/showup/ShowupWebrtcDownload.java index b4837082..904327e6 100644 --- a/common/src/main/java/ctbrec/sites/showup/ShowupWebrtcDownload.java +++ b/common/src/main/java/ctbrec/sites/showup/ShowupWebrtcDownload.java @@ -2,6 +2,7 @@ package ctbrec.sites.showup; import static ctbrec.io.HttpConstants.*; +import java.io.EOFException; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -65,7 +66,7 @@ public class ShowupWebrtcDownload extends AbstractDownload { @Override public void onOpen(WebSocket webSocket, Response response) { super.onOpen(webSocket, response); - LOG.debug("onOpen {} {}", webSocket, response); + LOG.trace("onOpen {} {}", webSocket, response); response.close(); try { LOG.debug("Recording video stream to {}", targetFile); @@ -85,20 +86,27 @@ public class ShowupWebrtcDownload extends AbstractDownload { try { fout.write(bytes.toByteArray()); } catch (IOException e) { - LOG.error("Couldn't write video stream to file", e); + if (running) { + LOG.error("Couldn't write video stream to file", e); + } } } @Override public void onMessage(WebSocket webSocket, String text) { super.onMessage(webSocket, text); - LOG.debug("onMessageT {} {}", webSocket, text); + LOG.trace("onMessageT {} {}", webSocket, text); } @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { super.onFailure(webSocket, t, response); - LOG.error("onFailure {} {}", webSocket, response, t); + stop(); + if (t instanceof EOFException) { + LOG.info("End of stream detected for model {}", model); + } else { + LOG.error("onFailure {} {}", webSocket, response, t); + } if (response != null) { response.close(); } @@ -107,13 +115,14 @@ public class ShowupWebrtcDownload extends AbstractDownload { @Override public void onClosing(WebSocket webSocket, int code, String reason) { super.onClosing(webSocket, code, reason); - LOG.debug("onClosing {} {} {}", webSocket, code, reason); + LOG.trace("onClosing {} {} {}", webSocket, code, reason); } @Override public void onClosed(WebSocket webSocket, int code, String reason) { super.onClosed(webSocket, code, reason); LOG.debug("onClosed {} {} {}", webSocket, code, reason); + stop(); } }); @@ -122,16 +131,25 @@ public class ShowupWebrtcDownload extends AbstractDownload { @Override public void stop() { running = false; - ws.close(1000, ""); + if (ws != null) { + boolean closed = ws.close(1000, ""); + if (closed) { + ws = null; + } else { + LOG.error("websocket.close() returned false"); + } + } } @Override public void finalizeDownload() { - try { - LOG.debug("Closing recording file {}", targetFile); - fout.close(); - } catch (IOException e) { - LOG.error("Error while closing recording file {}", targetFile, e); + if (fout != null) { + try { + LOG.debug("Closing recording file {}", targetFile); + fout.close(); + } catch (IOException e) { + LOG.error("Error while closing recording file {}", targetFile, e); + } } } @@ -174,7 +192,10 @@ public class ShowupWebrtcDownload extends AbstractDownload { stop(); rescheduleTime = Instant.now(); } else { - rescheduleTime = Instant.now().plusSeconds(5); + rescheduleTime = Instant.now().plusSeconds(Config.getInstance().getSettings().onlineCheckIntervalInSecs); + } + if (!model.isOnline(true)) { + stop(); } return this; }