From e0426d7c86d9791a8b8120a7491eaec6c69640a9 Mon Sep 17 00:00:00 2001
From: 0xb00bface <0xboobface@gmail.com>
Date: Tue, 21 Dec 2021 15:52:14 +0100
Subject: [PATCH] Fix bug in stalled recording detection

---
 .../download/hls/AbstractHlsDownload.java     | 75 ++++++++-----------
 .../download/hls/SegmentDownload.java         | 24 +++---
 2 files changed, 42 insertions(+), 57 deletions(-)

diff --git a/common/src/main/java/ctbrec/recorder/download/hls/AbstractHlsDownload.java b/common/src/main/java/ctbrec/recorder/download/hls/AbstractHlsDownload.java
index eb5a3011..607cdca3 100644
--- a/common/src/main/java/ctbrec/recorder/download/hls/AbstractHlsDownload.java
+++ b/common/src/main/java/ctbrec/recorder/download/hls/AbstractHlsDownload.java
@@ -1,52 +1,10 @@
 package ctbrec.recorder.download.hls;
 
-import static ctbrec.io.HttpConstants.*;
-import static ctbrec.recorder.download.StreamSource.*;
-import static java.nio.charset.StandardCharsets.*;
-import static java.nio.file.StandardOpenOption.*;
-
-import java.io.BufferedWriter;
-import java.io.ByteArrayInputStream;
-import java.io.EOFException;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.net.SocketTimeoutException;
-import java.net.URL;
-import java.nio.file.Files;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.time.Duration;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorCompletionService;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.stream.Collectors;
-
-import javax.xml.bind.JAXBException;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.iheartradio.m3u8.Encoding;
-import com.iheartradio.m3u8.Format;
-import com.iheartradio.m3u8.ParseException;
-import com.iheartradio.m3u8.ParsingMode;
-import com.iheartradio.m3u8.PlaylistException;
-import com.iheartradio.m3u8.PlaylistParser;
+import com.iheartradio.m3u8.*;
 import com.iheartradio.m3u8.data.EncryptionData;
 import com.iheartradio.m3u8.data.MediaPlaylist;
 import com.iheartradio.m3u8.data.Playlist;
 import com.iheartradio.m3u8.data.TrackData;
-
 import ctbrec.Config;
 import ctbrec.Model;
 import ctbrec.Model.State;
@@ -64,6 +22,32 @@ import ctbrec.sites.Site;
 import okhttp3.Request;
 import okhttp3.Request.Builder;
 import okhttp3.Response;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.bind.JAXBException;
+import java.io.*;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.Map.Entry;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import static ctbrec.io.HttpConstants.*;
+import static ctbrec.recorder.download.StreamSource.UNKNOWN;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.nio.file.StandardOpenOption.*;
 
 public abstract class AbstractHlsDownload extends AbstractDownload {
 
@@ -85,7 +69,7 @@ public abstract class AbstractHlsDownload extends AbstractDownload {
     private Instant beforeLastPlaylistRequest= Instant.EPOCH;
     private int consecutivePlaylistTimeouts = 0;
     private int consecutivePlaylistErrors = 0;
-    private Instant lastSegmentDownload = Instant.MAX;
+    private Instant lastSegmentDownload = Instant.MIN;
     private int selectedResolution = UNKNOWN;
 
     private final List<RecordingEvent> recordingEvents = new LinkedList<>();
@@ -99,6 +83,7 @@ public abstract class AbstractHlsDownload extends AbstractDownload {
     public void init(Config config, Model model, Instant startTime, ExecutorService executorService) throws IOException {
         super.init(config, model, startTime, executorService);
         segmentDownloadService = new ExecutorCompletionService<>(downloadExecutor);
+        lastSegmentDownload = Instant.now();
     }
 
     protected abstract OutputStream getSegmentOutputStream(Segment segment) throws IOException;
@@ -245,7 +230,7 @@ public abstract class AbstractHlsDownload extends AbstractDownload {
             List<StreamSource> filteredStreamSources = streamSources.stream()
                     .filter(src -> src.height == 0 || src.height == UNKNOWN || minRes <= src.height)
                     .filter(src -> src.height == 0 || src.height == UNKNOWN || maxRes >= src.height)
-                    .collect(Collectors.toList());
+                    .toList();
 
             if (filteredStreamSources.isEmpty()) {
                 throw new ExecutionException(new NoStreamFoundException("No stream left in playlist"));
diff --git a/common/src/main/java/ctbrec/recorder/download/hls/SegmentDownload.java b/common/src/main/java/ctbrec/recorder/download/hls/SegmentDownload.java
index 578245e0..c2fc8497 100644
--- a/common/src/main/java/ctbrec/recorder/download/hls/SegmentDownload.java
+++ b/common/src/main/java/ctbrec/recorder/download/hls/SegmentDownload.java
@@ -13,6 +13,7 @@ import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.util.HashMap;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.Callable;
 
@@ -34,12 +35,12 @@ import okhttp3.Response;
 public class SegmentDownload implements Callable<SegmentDownload> {
     private static final Logger LOG = LoggerFactory.getLogger(SegmentDownload.class);
 
-    private URL url;
-    private HttpClient client;
-    private SegmentPlaylist playlist;
-    private Segment segment;
-    private Model model;
-    private OutputStream out;
+    private final URL url;
+    private final HttpClient client;
+    private final SegmentPlaylist playlist;
+    private final Segment segment;
+    private final Model model;
+    private final OutputStream out;
 
     public SegmentDownload(Model model, SegmentPlaylist playlist, Segment segment, HttpClient client, OutputStream out) throws MalformedURLException {
         this.model = model;
@@ -52,7 +53,7 @@ public class SegmentDownload implements Callable<SegmentDownload> {
 
     @Override
     public SegmentDownload call() {
-        for (int tries = 1; tries <= 3 && !Thread.currentThread().isInterrupted(); tries++) {
+        for (int tries = 1; tries <= 3 && !Thread.currentThread().isInterrupted(); tries++) { // NOSONAR
             Request request = createRequest();
             try (Response response = client.execute(request)) {
                 handleResponse(response);
@@ -73,20 +74,19 @@ public class SegmentDownload implements Callable<SegmentDownload> {
         return this;
     }
 
-    private boolean handleResponse(Response response) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IOException {
+    private void handleResponse(Response response) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IOException {
         if (response.isSuccessful()) {
-            InputStream in = response.body().byteStream();
+            InputStream in = Objects.requireNonNull(response.body(), "HTTP response body is null").byteStream();
             if (playlist.encrypted) {
                 in = new Crypto(playlist.encryptionKeyUrl, client).wrap(in);
             }
             byte[] b = new byte[1024 * 100];
-            int length = -1;
+            int length;
             while ((length = in.read(b)) >= 0 && !Thread.currentThread().isInterrupted()) {
                 out.write(b, 0, length);
                 BandwidthMeter.add(length);
             }
             out.flush();
-            return true;
         } else {
             throw new HttpException(response.code(), response.message());
         }
@@ -105,4 +105,4 @@ public class SegmentDownload implements Callable<SegmentDownload> {
     public Segment getSegment() {
         return segment;
     }
-}
\ No newline at end of file
+}