Add BandwidthMeter, which tracks the current bandwidth usage
This commit is contained in:
parent
bd48d6bf9c
commit
cd6175a7eb
|
@ -31,6 +31,8 @@ import ctbrec.event.Event;
|
||||||
import ctbrec.event.EventBusHolder;
|
import ctbrec.event.EventBusHolder;
|
||||||
import ctbrec.event.EventHandler;
|
import ctbrec.event.EventHandler;
|
||||||
import ctbrec.event.EventHandlerConfiguration;
|
import ctbrec.event.EventHandlerConfiguration;
|
||||||
|
import ctbrec.io.BandwidthMeter;
|
||||||
|
import ctbrec.io.ByteUnitFormatter;
|
||||||
import ctbrec.io.HttpClient;
|
import ctbrec.io.HttpClient;
|
||||||
import ctbrec.io.HttpException;
|
import ctbrec.io.HttpException;
|
||||||
import ctbrec.recorder.NextGenLocalRecorder;
|
import ctbrec.recorder.NextGenLocalRecorder;
|
||||||
|
@ -63,11 +65,15 @@ import javafx.application.Application;
|
||||||
import javafx.application.HostServices;
|
import javafx.application.HostServices;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TabPane;
|
import javafx.scene.control.TabPane;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.WindowEvent;
|
import javafx.stage.WindowEvent;
|
||||||
|
@ -83,7 +89,10 @@ public class CamrecApplication extends Application {
|
||||||
private OnlineMonitor onlineMonitor;
|
private OnlineMonitor onlineMonitor;
|
||||||
static HostServices hostServices;
|
static HostServices hostServices;
|
||||||
private SettingsTab settingsTab;
|
private SettingsTab settingsTab;
|
||||||
private TabPane rootPane = new TabPane();
|
private BorderPane rootPane = new BorderPane();
|
||||||
|
private HBox statusBar = new HBox();
|
||||||
|
private Label statusLabel = new Label();
|
||||||
|
private TabPane tabPane = new TabPane();
|
||||||
private List<Site> sites = new ArrayList<>();
|
private List<Site> sites = new ArrayList<>();
|
||||||
public static HttpClient httpClient;
|
public static HttpClient httpClient;
|
||||||
public static String title;
|
public static String title;
|
||||||
|
@ -91,6 +100,9 @@ public class CamrecApplication extends Application {
|
||||||
private RecordedModelsTab modelsTab;
|
private RecordedModelsTab modelsTab;
|
||||||
private RecordingsTab recordingsTab;
|
private RecordingsTab recordingsTab;
|
||||||
|
|
||||||
|
private int activeRecordings = 0;
|
||||||
|
private double bytesPerSecond = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage primaryStage) throws Exception {
|
public void start(Stage primaryStage) throws Exception {
|
||||||
this.primaryStage = primaryStage;
|
this.primaryStage = primaryStage;
|
||||||
|
@ -109,6 +121,7 @@ public class CamrecApplication extends Application {
|
||||||
loadConfig();
|
loadConfig();
|
||||||
registerAlertSystem();
|
registerAlertSystem();
|
||||||
registerActiveRecordingsCounter();
|
registerActiveRecordingsCounter();
|
||||||
|
registerBandwidthMeterListener();
|
||||||
createHttpClient();
|
createHttpClient();
|
||||||
hostServices = getHostServices();
|
hostServices = getHostServices();
|
||||||
createRecorder();
|
createRecorder();
|
||||||
|
@ -158,26 +171,28 @@ public class CamrecApplication extends Application {
|
||||||
int windowWidth = Config.getInstance().getSettings().windowWidth;
|
int windowWidth = Config.getInstance().getSettings().windowWidth;
|
||||||
int windowHeight = Config.getInstance().getSettings().windowHeight;
|
int windowHeight = Config.getInstance().getSettings().windowHeight;
|
||||||
|
|
||||||
rootPane = new TabPane();
|
|
||||||
Scene scene = new Scene(rootPane, windowWidth, windowHeight);
|
Scene scene = new Scene(rootPane, windowWidth, windowHeight);
|
||||||
primaryStage.setScene(scene);
|
primaryStage.setScene(scene);
|
||||||
|
rootPane.setCenter(tabPane);
|
||||||
|
rootPane.setBottom(statusBar);
|
||||||
for (Iterator<Site> iterator = sites.iterator(); iterator.hasNext();) {
|
for (Iterator<Site> iterator = sites.iterator(); iterator.hasNext();) {
|
||||||
Site site = iterator.next();
|
Site site = iterator.next();
|
||||||
if(site.isEnabled()) {
|
if(site.isEnabled()) {
|
||||||
SiteTab siteTab = new SiteTab(site, scene);
|
SiteTab siteTab = new SiteTab(site, scene);
|
||||||
rootPane.getTabs().add(siteTab);
|
tabPane.getTabs().add(siteTab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
modelsTab = new RecordedModelsTab("Recording", recorder, sites);
|
modelsTab = new RecordedModelsTab("Recording", recorder, sites);
|
||||||
rootPane.getTabs().add(modelsTab);
|
tabPane.getTabs().add(modelsTab);
|
||||||
recordingsTab = new RecordingsTab("Recordings", recorder, config, sites);
|
recordingsTab = new RecordingsTab("Recordings", recorder, config, sites);
|
||||||
rootPane.getTabs().add(recordingsTab);
|
tabPane.getTabs().add(recordingsTab);
|
||||||
settingsTab = new SettingsTab(sites, recorder);
|
settingsTab = new SettingsTab(sites, recorder);
|
||||||
rootPane.getTabs().add(settingsTab);
|
tabPane.getTabs().add(settingsTab);
|
||||||
rootPane.getTabs().add(new NewsTab());
|
tabPane.getTabs().add(new NewsTab());
|
||||||
rootPane.getTabs().add(new DonateTabFx());
|
tabPane.getTabs().add(new DonateTabFx());
|
||||||
rootPane.getTabs().add(new HelpTab());
|
tabPane.getTabs().add(new HelpTab());
|
||||||
|
|
||||||
switchToStartTab();
|
switchToStartTab();
|
||||||
writeColorSchemeStyleSheet();
|
writeColorSchemeStyleSheet();
|
||||||
|
@ -205,7 +220,7 @@ public class CamrecApplication extends Application {
|
||||||
primaryStage.setOnCloseRequest(createShutdownHandler());
|
primaryStage.setOnCloseRequest(createShutdownHandler());
|
||||||
|
|
||||||
// register changelistener to activate / deactivate tabs, when the user switches between them
|
// register changelistener to activate / deactivate tabs, when the user switches between them
|
||||||
rootPane.getSelectionModel().selectedItemProperty().addListener((ChangeListener<Tab>) (ov, from, to) -> {
|
tabPane.getSelectionModel().selectedItemProperty().addListener((ChangeListener<Tab>) (ov, from, to) -> {
|
||||||
if (from instanceof TabSelectionListener) {
|
if (from instanceof TabSelectionListener) {
|
||||||
((TabSelectionListener) from).deselected();
|
((TabSelectionListener) from).deselected();
|
||||||
}
|
}
|
||||||
|
@ -213,6 +228,9 @@ public class CamrecApplication extends Application {
|
||||||
((TabSelectionListener) to).selected();
|
((TabSelectionListener) to).selected();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
statusBar.getChildren().add(statusLabel);
|
||||||
|
HBox.setMargin(statusLabel, new Insets(10, 10, 10, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
private javafx.event.EventHandler<WindowEvent> createShutdownHandler() {
|
private javafx.event.EventHandler<WindowEvent> createShutdownHandler() {
|
||||||
|
@ -303,8 +321,8 @@ public class CamrecApplication extends Application {
|
||||||
if(evt.getType() == Event.Type.MODEL_ONLINE || evt.getType() == Event.Type.MODEL_STATUS_CHANGED || evt.getType() == Event.Type.RECORDING_STATUS_CHANGED) {
|
if(evt.getType() == Event.Type.MODEL_ONLINE || evt.getType() == Event.Type.MODEL_STATUS_CHANGED || evt.getType() == Event.Type.RECORDING_STATUS_CHANGED) {
|
||||||
try {
|
try {
|
||||||
List<Model> models = recorder.getCurrentlyRecording();
|
List<Model> models = recorder.getCurrentlyRecording();
|
||||||
long count = models.size();
|
activeRecordings = models.size();
|
||||||
String windowTitle = count > 0 ? "(" + count + ") " + title : title;
|
String windowTitle = activeRecordings > 0 ? "(" + activeRecordings + ") " + title : title;
|
||||||
Platform.runLater(() -> primaryStage.setTitle(windowTitle));
|
Platform.runLater(() -> primaryStage.setTitle(windowTitle));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("Couldn't update window title", e);
|
LOG.warn("Couldn't update window title", e);
|
||||||
|
@ -314,6 +332,16 @@ public class CamrecApplication extends Application {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerBandwidthMeterListener() {
|
||||||
|
BandwidthMeter.addListener((bytes, dur) -> {
|
||||||
|
long seconds = dur.getSeconds();
|
||||||
|
bytesPerSecond = bytes / (double)seconds;
|
||||||
|
String humanreadable = ByteUnitFormatter.format(bytesPerSecond);
|
||||||
|
String status = String.format("Recording %s / %s models @ %s/s", activeRecordings, recorder.getModels().size(), humanreadable);
|
||||||
|
Platform.runLater(() -> statusLabel.setText(status));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void writeColorSchemeStyleSheet() {
|
private void writeColorSchemeStyleSheet() {
|
||||||
File colorCss = new File(Config.getInstance().getConfigDir(), "color.css");
|
File colorCss = new File(Config.getInstance().getConfigDir(), "color.css");
|
||||||
try(FileOutputStream fos = new FileOutputStream(colorCss)) {
|
try(FileOutputStream fos = new FileOutputStream(colorCss)) {
|
||||||
|
@ -340,15 +368,15 @@ public class CamrecApplication extends Application {
|
||||||
private void switchToStartTab() {
|
private void switchToStartTab() {
|
||||||
String startTab = Config.getInstance().getSettings().startTab;
|
String startTab = Config.getInstance().getSettings().startTab;
|
||||||
if(StringUtil.isNotBlank(startTab)) {
|
if(StringUtil.isNotBlank(startTab)) {
|
||||||
for (Tab tab : rootPane.getTabs()) {
|
for (Tab tab : tabPane.getTabs()) {
|
||||||
if(Objects.equals(startTab, tab.getText())) {
|
if(Objects.equals(startTab, tab.getText())) {
|
||||||
rootPane.getSelectionModel().select(tab);
|
tabPane.getSelectionModel().select(tab);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(rootPane.getSelectionModel().getSelectedItem() instanceof TabSelectionListener) {
|
if(tabPane.getSelectionModel().getSelectedItem() instanceof TabSelectionListener) {
|
||||||
((TabSelectionListener)rootPane.getSelectionModel().getSelectedItem()).selected();
|
((TabSelectionListener)tabPane.getSelectionModel().getSelectedItem()).selected();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,7 +440,7 @@ public class CamrecApplication extends Application {
|
||||||
Version ctbrecVersion = getVersion();
|
Version ctbrecVersion = getVersion();
|
||||||
if (latestVersion.compareTo(ctbrecVersion) > 0) {
|
if (latestVersion.compareTo(ctbrecVersion) > 0) {
|
||||||
LOG.debug("Update available {} < {}", ctbrecVersion, latestVersion);
|
LOG.debug("Update available {} < {}", ctbrecVersion, latestVersion);
|
||||||
Platform.runLater(() -> rootPane.getTabs().add(new UpdateTab(latest)));
|
Platform.runLater(() -> tabPane.getTabs().add(new UpdateTab(latest)));
|
||||||
} else {
|
} else {
|
||||||
LOG.debug("ctbrec is up-to-date {}", ctbrecVersion);
|
LOG.debug("ctbrec is up-to-date {}", ctbrecVersion);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package ctbrec.io;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
public class BandwidthMeter {
|
||||||
|
|
||||||
|
private static final Duration MEASURE_TIMEFRAME = Duration.ofSeconds(10);
|
||||||
|
private static List<Record> records = new ArrayList<>(100);
|
||||||
|
private static Lock lock = new ReentrantLock(true);
|
||||||
|
private static List<Listener> listeners = new ArrayList<>();
|
||||||
|
private static Instant lastUpdate = Instant.EPOCH;
|
||||||
|
|
||||||
|
private BandwidthMeter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void add(long bytes) {
|
||||||
|
Record r = new Record(bytes);
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
records.add(r);
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instant oneSecondAgo = Instant.now().minus(Duration.ofSeconds(1));
|
||||||
|
if (lastUpdate.isBefore(oneSecondAgo)) {
|
||||||
|
long throughput = getThroughput();
|
||||||
|
for (Listener listener : listeners) {
|
||||||
|
listener.bandwidthCalculated(throughput, MEASURE_TIMEFRAME);
|
||||||
|
}
|
||||||
|
lastUpdate = Instant.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the throughput over the last 10 seconds
|
||||||
|
* @return throughput in bytes
|
||||||
|
*/
|
||||||
|
public static long getThroughput() {
|
||||||
|
return getThroughput(MEASURE_TIMEFRAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the throughput over the given duration
|
||||||
|
* @return throughput in bytes
|
||||||
|
*/
|
||||||
|
public static long getThroughput(Duration d) {
|
||||||
|
Instant now = Instant.now();
|
||||||
|
Instant measureStart = now.minus(d);
|
||||||
|
long throughput = 0;
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
for (Iterator<Record> iterator = records.iterator(); iterator.hasNext();) {
|
||||||
|
Record record = iterator.next();
|
||||||
|
if (record.timestamp.isBefore(measureStart)) {
|
||||||
|
iterator.remove();
|
||||||
|
} else {
|
||||||
|
throughput += record.bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
return throughput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addListener(Listener l) {
|
||||||
|
listeners.add(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeListener(Listener l) {
|
||||||
|
listeners.remove(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Record {
|
||||||
|
public Instant timestamp;
|
||||||
|
public long bytes;
|
||||||
|
|
||||||
|
public Record(long bytes) {
|
||||||
|
timestamp = Instant.now();
|
||||||
|
this.bytes = bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public static interface Listener {
|
||||||
|
void bandwidthCalculated(long bytes, Duration timeframe);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package ctbrec.io;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
|
||||||
|
public class ByteUnitFormatter {
|
||||||
|
|
||||||
|
private ByteUnitFormatter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final long KiB = 1024;
|
||||||
|
public static final long MiB = KiB * 1024;
|
||||||
|
public static final long GiB = MiB * 1024;
|
||||||
|
public static final long TiB = GiB * 1024;
|
||||||
|
|
||||||
|
private static NumberFormat nf = new DecimalFormat(".00");
|
||||||
|
|
||||||
|
public static String format(long bytes) {
|
||||||
|
double decimal = bytes;
|
||||||
|
return format(decimal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String format(double bytes) {
|
||||||
|
double decimal = bytes;
|
||||||
|
String unit = "Bytes";
|
||||||
|
if (bytes >= TiB) {
|
||||||
|
decimal = bytes / (1024 * 1024 * 1024 * 1024);
|
||||||
|
unit = "TiB";
|
||||||
|
} else if (bytes >= GiB) {
|
||||||
|
decimal = bytes / (1024 * 1024 * 1024);
|
||||||
|
unit = "GiB";
|
||||||
|
} else if (bytes >= MiB) {
|
||||||
|
decimal = bytes / (1024 * 1024);
|
||||||
|
unit = "MiB";
|
||||||
|
} else if (bytes >= KiB) {
|
||||||
|
decimal = bytes / 1024;
|
||||||
|
unit = "KiB";
|
||||||
|
}
|
||||||
|
|
||||||
|
return nf.format(decimal) + " " + unit;
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ import org.slf4j.LoggerFactory;
|
||||||
import ctbrec.Config;
|
import ctbrec.Config;
|
||||||
import ctbrec.Model;
|
import ctbrec.Model;
|
||||||
import ctbrec.Recording;
|
import ctbrec.Recording;
|
||||||
|
import ctbrec.io.BandwidthMeter;
|
||||||
import ctbrec.io.HttpClient;
|
import ctbrec.io.HttpClient;
|
||||||
import ctbrec.io.HttpException;
|
import ctbrec.io.HttpException;
|
||||||
import ctbrec.recorder.download.AbstractDownload;
|
import ctbrec.recorder.download.AbstractDownload;
|
||||||
|
@ -217,6 +218,7 @@ public class DashDownload extends AbstractDownload {
|
||||||
int len = -1;
|
int len = -1;
|
||||||
while ((len = in.read(b)) >= 0) {
|
while ((len = in.read(b)) >= 0) {
|
||||||
out.write(b, 0, len);
|
out.write(b, 0, len);
|
||||||
|
BandwidthMeter.add(len);
|
||||||
}
|
}
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ import ctbrec.Config;
|
||||||
import ctbrec.Model;
|
import ctbrec.Model;
|
||||||
import ctbrec.Recording.State;
|
import ctbrec.Recording.State;
|
||||||
import ctbrec.UnknownModel;
|
import ctbrec.UnknownModel;
|
||||||
|
import ctbrec.io.BandwidthMeter;
|
||||||
import ctbrec.io.HttpClient;
|
import ctbrec.io.HttpClient;
|
||||||
import ctbrec.io.HttpException;
|
import ctbrec.io.HttpException;
|
||||||
import ctbrec.recorder.PlaylistGenerator.InvalidPlaylistException;
|
import ctbrec.recorder.PlaylistGenerator.InvalidPlaylistException;
|
||||||
|
@ -95,7 +96,9 @@ public abstract class AbstractHlsDownload extends AbstractDownload {
|
||||||
return new SegmentPlaylist(segmentsURL);
|
return new SegmentPlaylist(segmentsURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
InputStream inputStream = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
|
byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
|
||||||
|
BandwidthMeter.add(bytes.length);
|
||||||
|
InputStream inputStream = new ByteArrayInputStream(bytes);
|
||||||
PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8, ParsingMode.LENIENT);
|
PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8, ParsingMode.LENIENT);
|
||||||
Playlist playlist = parser.parse();
|
Playlist playlist = parser.parse();
|
||||||
if (playlist.hasMediaPlaylist()) {
|
if (playlist.hasMediaPlaylist()) {
|
||||||
|
|
|
@ -45,6 +45,7 @@ import ctbrec.Config;
|
||||||
import ctbrec.Model;
|
import ctbrec.Model;
|
||||||
import ctbrec.Recording;
|
import ctbrec.Recording;
|
||||||
import ctbrec.Recording.State;
|
import ctbrec.Recording.State;
|
||||||
|
import ctbrec.io.BandwidthMeter;
|
||||||
import ctbrec.io.HttpClient;
|
import ctbrec.io.HttpClient;
|
||||||
import ctbrec.io.HttpException;
|
import ctbrec.io.HttpException;
|
||||||
import ctbrec.recorder.PlaylistGenerator;
|
import ctbrec.recorder.PlaylistGenerator;
|
||||||
|
@ -274,7 +275,7 @@ public class HlsDownload extends AbstractHlsDownload {
|
||||||
downloadThreadPool.shutdownNow();
|
downloadThreadPool.shutdownNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SegmentDownload implements Callable<Boolean> {
|
private class SegmentDownload implements Callable<Boolean> {
|
||||||
private URL url;
|
private URL url;
|
||||||
private Path file;
|
private Path file;
|
||||||
private HttpClient client;
|
private HttpClient client;
|
||||||
|
@ -308,6 +309,7 @@ public class HlsDownload extends AbstractHlsDownload {
|
||||||
int length = -1;
|
int length = -1;
|
||||||
while( (length = in.read(b)) >= 0 && !Thread.currentThread().isInterrupted()) {
|
while( (length = in.read(b)) >= 0 && !Thread.currentThread().isInterrupted()) {
|
||||||
fos.write(b, 0, length);
|
fos.write(b, 0, length);
|
||||||
|
BandwidthMeter.add(length);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import ctbrec.Hmac;
|
||||||
import ctbrec.Model;
|
import ctbrec.Model;
|
||||||
import ctbrec.OS;
|
import ctbrec.OS;
|
||||||
import ctbrec.Recording;
|
import ctbrec.Recording;
|
||||||
|
import ctbrec.io.BandwidthMeter;
|
||||||
import ctbrec.io.HttpClient;
|
import ctbrec.io.HttpClient;
|
||||||
import ctbrec.io.HttpException;
|
import ctbrec.io.HttpException;
|
||||||
import ctbrec.io.StreamRedirectThread;
|
import ctbrec.io.StreamRedirectThread;
|
||||||
|
@ -419,6 +420,7 @@ public class MergedFfmpegHlsDownload extends AbstractHlsDownload {
|
||||||
try (Response response = client.execute(request)) {
|
try (Response response = client.execute(request)) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
byte[] segment = response.body().bytes();
|
byte[] segment = response.body().bytes();
|
||||||
|
BandwidthMeter.add(segment.length);
|
||||||
if (lsp.encrypted) {
|
if (lsp.encrypted) {
|
||||||
segment = new Crypto(lsp.encryptionKeyUrl, client).decrypt(segment);
|
segment = new Crypto(lsp.encryptionKeyUrl, client).decrypt(segment);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.iheartradio.m3u8.ParseException;
|
||||||
import com.iheartradio.m3u8.PlaylistException;
|
import com.iheartradio.m3u8.PlaylistException;
|
||||||
|
|
||||||
import ctbrec.Config;
|
import ctbrec.Config;
|
||||||
|
import ctbrec.io.BandwidthMeter;
|
||||||
import ctbrec.io.HttpClient;
|
import ctbrec.io.HttpClient;
|
||||||
import ctbrec.io.HttpException;
|
import ctbrec.io.HttpException;
|
||||||
import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload;
|
import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload;
|
||||||
|
@ -44,6 +45,7 @@ public class ShowupMergedDownload extends MergedFfmpegHlsDownload {
|
||||||
int length = -1;
|
int length = -1;
|
||||||
boolean keepGoing = true;
|
boolean keepGoing = true;
|
||||||
while ((length = in.read(buffer)) >= 0 && keepGoing) {
|
while ((length = in.read(buffer)) >= 0 && keepGoing) {
|
||||||
|
BandwidthMeter.add(length);
|
||||||
writeSegment(buffer, 0, length);
|
writeSegment(buffer, 0, length);
|
||||||
keepGoing = running && !Thread.interrupted() && model.isOnline(true);
|
keepGoing = running && !Thread.interrupted() && model.isOnline(true);
|
||||||
if (livestreamDownload && splitRecording()) {
|
if (livestreamDownload && splitRecording()) {
|
||||||
|
|
Loading…
Reference in New Issue