Fix bug in stalled recording detection

This commit is contained in:
0xb00bface 2021-12-21 15:52:14 +01:00
parent 21fa71c901
commit e0426d7c86
2 changed files with 42 additions and 57 deletions

View File

@ -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"));

View File

@ -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;
}
}
}