forked from j62/ctbrec
Improve Showup websocket error handling
This commit is contained in:
parent
3dddd91945
commit
a5e047124e
|
@ -1,5 +1,6 @@
|
||||||
4.2.1
|
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
|
* Fixed bug, which caused the window to stay invisible after being minimized to
|
||||||
tray on windows
|
tray on windows
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@ package ctbrec.sites.showup;
|
||||||
|
|
||||||
import static ctbrec.io.HttpConstants.*;
|
import static ctbrec.io.HttpConstants.*;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -65,7 +66,7 @@ public class ShowupWebrtcDownload extends AbstractDownload {
|
||||||
@Override
|
@Override
|
||||||
public void onOpen(WebSocket webSocket, Response response) {
|
public void onOpen(WebSocket webSocket, Response response) {
|
||||||
super.onOpen(webSocket, response);
|
super.onOpen(webSocket, response);
|
||||||
LOG.debug("onOpen {} {}", webSocket, response);
|
LOG.trace("onOpen {} {}", webSocket, response);
|
||||||
response.close();
|
response.close();
|
||||||
try {
|
try {
|
||||||
LOG.debug("Recording video stream to {}", targetFile);
|
LOG.debug("Recording video stream to {}", targetFile);
|
||||||
|
@ -85,20 +86,27 @@ public class ShowupWebrtcDownload extends AbstractDownload {
|
||||||
try {
|
try {
|
||||||
fout.write(bytes.toByteArray());
|
fout.write(bytes.toByteArray());
|
||||||
} catch (IOException e) {
|
} 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
|
@Override
|
||||||
public void onMessage(WebSocket webSocket, String text) {
|
public void onMessage(WebSocket webSocket, String text) {
|
||||||
super.onMessage(webSocket, text);
|
super.onMessage(webSocket, text);
|
||||||
LOG.debug("onMessageT {} {}", webSocket, text);
|
LOG.trace("onMessageT {} {}", webSocket, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
|
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
|
||||||
super.onFailure(webSocket, t, 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) {
|
if (response != null) {
|
||||||
response.close();
|
response.close();
|
||||||
}
|
}
|
||||||
|
@ -107,13 +115,14 @@ public class ShowupWebrtcDownload extends AbstractDownload {
|
||||||
@Override
|
@Override
|
||||||
public void onClosing(WebSocket webSocket, int code, String reason) {
|
public void onClosing(WebSocket webSocket, int code, String reason) {
|
||||||
super.onClosing(webSocket, code, reason);
|
super.onClosing(webSocket, code, reason);
|
||||||
LOG.debug("onClosing {} {} {}", webSocket, code, reason);
|
LOG.trace("onClosing {} {} {}", webSocket, code, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClosed(WebSocket webSocket, int code, String reason) {
|
public void onClosed(WebSocket webSocket, int code, String reason) {
|
||||||
super.onClosed(webSocket, code, reason);
|
super.onClosed(webSocket, code, reason);
|
||||||
LOG.debug("onClosed {} {} {}", webSocket, code, reason);
|
LOG.debug("onClosed {} {} {}", webSocket, code, reason);
|
||||||
|
stop();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -122,16 +131,25 @@ public class ShowupWebrtcDownload extends AbstractDownload {
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
running = false;
|
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
|
@Override
|
||||||
public void finalizeDownload() {
|
public void finalizeDownload() {
|
||||||
try {
|
if (fout != null) {
|
||||||
LOG.debug("Closing recording file {}", targetFile);
|
try {
|
||||||
fout.close();
|
LOG.debug("Closing recording file {}", targetFile);
|
||||||
} catch (IOException e) {
|
fout.close();
|
||||||
LOG.error("Error while closing recording file {}", targetFile, e);
|
} catch (IOException e) {
|
||||||
|
LOG.error("Error while closing recording file {}", targetFile, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +192,10 @@ public class ShowupWebrtcDownload extends AbstractDownload {
|
||||||
stop();
|
stop();
|
||||||
rescheduleTime = Instant.now();
|
rescheduleTime = Instant.now();
|
||||||
} else {
|
} else {
|
||||||
rescheduleTime = Instant.now().plusSeconds(5);
|
rescheduleTime = Instant.now().plusSeconds(Config.getInstance().getSettings().onlineCheckIntervalInSecs);
|
||||||
|
}
|
||||||
|
if (!model.isOnline(true)) {
|
||||||
|
stop();
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue