Add setting to split up the recording after x minutes
This commit is contained in:
parent
01aa04b3da
commit
527f5f61ff
|
@ -40,4 +40,5 @@ public class Settings {
|
|||
public boolean windowMaximized = false;
|
||||
public int windowX;
|
||||
public int windowY;
|
||||
public int splitRecordings = 0;
|
||||
}
|
||||
|
|
|
@ -471,7 +471,13 @@ public class LocalRecorder implements Recorder {
|
|||
recording.setStatus(GENERATING_PLAYLIST);
|
||||
recording.setProgress(playlistGenerator.getProgress());
|
||||
} else {
|
||||
if (Recording.isMergedRecording(rec)) {
|
||||
if (config.isServerMode()) {
|
||||
if (recording.hasPlaylist()) {
|
||||
recording.setStatus(FINISHED);
|
||||
} else {
|
||||
recording.setStatus(RECORDING);
|
||||
}
|
||||
} else {
|
||||
boolean dirUsedByRecordingProcess = false;
|
||||
for (Download download : recordingProcesses.values()) {
|
||||
if(rec.equals(download.getDirectory())) {
|
||||
|
@ -484,12 +490,6 @@ public class LocalRecorder implements Recorder {
|
|||
} else {
|
||||
recording.setStatus(FINISHED);
|
||||
}
|
||||
} else {
|
||||
if (recording.hasPlaylist()) {
|
||||
recording.setStatus(FINISHED);
|
||||
} else {
|
||||
recording.setStatus(RECORDING);
|
||||
}
|
||||
}
|
||||
}
|
||||
recordings.add(recording);
|
||||
|
|
|
@ -14,7 +14,10 @@ import java.nio.file.FileSystems;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Duration;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Objects;
|
||||
|
@ -48,15 +51,20 @@ public class MergedHlsDownload extends AbstractHlsDownload {
|
|||
private BlockingMultiMTSSource multiSource;
|
||||
private Thread mergeThread;
|
||||
private Streamer streamer;
|
||||
private ZonedDateTime startTime;
|
||||
private Config config;
|
||||
private File targetFile;
|
||||
private DecimalFormat df = new DecimalFormat("00000");
|
||||
private int splitCounter = 0;
|
||||
|
||||
public MergedHlsDownload(HttpClient client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
public void start(String segmentPlaylistUri, File target, ProgressListener progressListener) throws IOException {
|
||||
public void start(String segmentPlaylistUri, File targetFile, ProgressListener progressListener) throws IOException {
|
||||
try {
|
||||
running = true;
|
||||
mergeThread = createMergeThread(target, progressListener, false);
|
||||
mergeThread = createMergeThread(targetFile, progressListener, false);
|
||||
mergeThread.start();
|
||||
downloadSegments(segmentPlaylistUri, false);
|
||||
} catch(ParseException e) {
|
||||
|
@ -72,8 +80,10 @@ public class MergedHlsDownload extends AbstractHlsDownload {
|
|||
|
||||
@Override
|
||||
public void start(Model model, Config config) throws IOException {
|
||||
this.config = config;
|
||||
try {
|
||||
running = true;
|
||||
startTime = ZonedDateTime.now();
|
||||
StreamInfo streamInfo = Chaturbate.getStreamInfo(model, client);
|
||||
if(!Objects.equals(streamInfo.room_status, "public")) {
|
||||
throw new IOException(model.getName() +"'s room is not public");
|
||||
|
@ -87,8 +97,13 @@ public class MergedHlsDownload extends AbstractHlsDownload {
|
|||
Files.createDirectories(downloadDir);
|
||||
}
|
||||
|
||||
File targetFile = Recording.mergedFileFromDirectory(downloadDir.toFile());
|
||||
mergeThread = createMergeThread(targetFile, null, true);
|
||||
targetFile = Recording.mergedFileFromDirectory(downloadDir.toFile());
|
||||
File target = targetFile;
|
||||
if(config.getSettings().splitRecordings > 0) {
|
||||
LOG.debug("Splitting recordings every {} seconds", config.getSettings().splitRecordings);
|
||||
target = new File(targetFile.getAbsolutePath().replaceAll("\\.ts", "-00000.ts"));
|
||||
}
|
||||
mergeThread = createMergeThread(target, null, true);
|
||||
mergeThread.start();
|
||||
|
||||
String segments = parseMaster(streamInfo.url, model.getStreamUrlIndex());
|
||||
|
@ -159,6 +174,7 @@ public class MergedHlsDownload extends AbstractHlsDownload {
|
|||
}
|
||||
|
||||
if(livestreamDownload) {
|
||||
// download new segments
|
||||
while(!mergeQueue.isEmpty()) {
|
||||
try {
|
||||
writeSegment(mergeQueue.poll());
|
||||
|
@ -168,21 +184,32 @@ public class MergedHlsDownload extends AbstractHlsDownload {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (livestreamDownload) {
|
||||
long wait = 0;
|
||||
if (lastSegment == lsp.seq) {
|
||||
// playlist didn't change -> wait for at least half the target duration
|
||||
wait = (long) lsp.targetDuration * 1000 / 2;
|
||||
LOG.trace("Playlist didn't change... waiting for {}ms", wait);
|
||||
} else {
|
||||
// playlist did change -> wait for at least last segment duration
|
||||
wait = 1;// (long) lsp.lastSegDuration * 1000;
|
||||
LOG.trace("Playlist changed... waiting for {}ms", wait);
|
||||
// split up the recording, if configured
|
||||
if(config.getSettings().splitRecordings > 0) {
|
||||
Duration recordingDuration = Duration.between(startTime, ZonedDateTime.now());
|
||||
long seconds = recordingDuration.getSeconds();
|
||||
if(seconds >= config.getSettings().splitRecordings) {
|
||||
streamer.stop();
|
||||
File target = new File(targetFile.getAbsolutePath().replaceAll("\\.ts", "-"+df.format(++splitCounter)+".ts"));
|
||||
mergeThread = createMergeThread(target, null, true);
|
||||
mergeThread.start();
|
||||
startTime = ZonedDateTime.now();
|
||||
}
|
||||
}
|
||||
|
||||
// wait some time until requesting the segment playlist again to not hammer the server
|
||||
try {
|
||||
long wait = 0;
|
||||
if (lastSegment == lsp.seq) {
|
||||
// playlist didn't change -> wait for at least half the target duration
|
||||
wait = (long) lsp.targetDuration * 1000 / 2;
|
||||
LOG.trace("Playlist didn't change... waiting for {}ms", wait);
|
||||
} else {
|
||||
// playlist did change -> wait for at least last segment duration
|
||||
wait = 1;// (long) lsp.lastSegDuration * 1000;
|
||||
LOG.trace("Playlist changed... waiting for {}ms", wait);
|
||||
}
|
||||
Thread.sleep(wait);
|
||||
} catch (InterruptedException e) {
|
||||
if (running) {
|
||||
|
|
|
@ -2,11 +2,15 @@ package ctbrec.ui;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.sun.javafx.collections.ObservableListWrapper;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Hmac;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
|
@ -18,6 +22,7 @@ import javafx.scene.control.Alert;
|
|||
import javafx.scene.control.Alert.AlertType;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.PasswordField;
|
||||
import javafx.scene.control.RadioButton;
|
||||
|
@ -60,6 +65,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
private ToggleGroup recordLocation;
|
||||
private ProxySettingsPane proxySettingsPane;
|
||||
private TitledPane ctb;
|
||||
private ComboBox<SplitAfterOption> splitAfter;
|
||||
|
||||
public SettingsTab() {
|
||||
setText("Settings");
|
||||
|
@ -75,6 +81,49 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
setContent(mainLayout);
|
||||
|
||||
GridPane layout = createGridLayout();
|
||||
Label l = new Label("Display stream resolution in overview");
|
||||
layout.add(l, 0, 0);
|
||||
loadResolution = new CheckBox();
|
||||
loadResolution.setSelected(Config.getInstance().getSettings().determineResolution);
|
||||
loadResolution.setOnAction((e) -> {
|
||||
Config.getInstance().getSettings().determineResolution = loadResolution.isSelected();
|
||||
if(!loadResolution.isSelected()) {
|
||||
ThumbOverviewTab.queue.clear();
|
||||
}
|
||||
});
|
||||
//GridPane.setMargin(l, new Insets(CHECKBOX_MARGIN, 0, 0, 0));
|
||||
GridPane.setMargin(loadResolution, new Insets(0, 0, 0, CHECKBOX_MARGIN));
|
||||
layout.add(loadResolution, 1, 0);
|
||||
|
||||
l = new Label("Manually select stream quality");
|
||||
layout.add(l, 0, 1);
|
||||
chooseStreamQuality.setSelected(Config.getInstance().getSettings().chooseStreamQuality);
|
||||
chooseStreamQuality.setOnAction((e) -> Config.getInstance().getSettings().chooseStreamQuality = chooseStreamQuality.isSelected());
|
||||
GridPane.setMargin(l, new Insets(CHECKBOX_MARGIN, 0, 0, 0));
|
||||
GridPane.setMargin(chooseStreamQuality, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN));
|
||||
layout.add(chooseStreamQuality, 1, 1);
|
||||
|
||||
l = new Label("Split recordings after (minutes)");
|
||||
layout.add(l, 0, 2);
|
||||
List<SplitAfterOption> options = new ArrayList<>();
|
||||
options.add(new SplitAfterOption("disabled", 0));
|
||||
options.add(new SplitAfterOption("10 min", 10 * 60));
|
||||
options.add(new SplitAfterOption("15 min", 15 * 60));
|
||||
options.add(new SplitAfterOption("20 min", 20 * 60));
|
||||
options.add(new SplitAfterOption("30 min", 30 * 60));
|
||||
options.add(new SplitAfterOption("60 min", 60 * 60));
|
||||
splitAfter = new ComboBox<>(new ObservableListWrapper<>(options));
|
||||
layout.add(splitAfter, 1, 2);
|
||||
setSplitAfterValue();
|
||||
splitAfter.setOnAction((e) -> Config.getInstance().getSettings().splitRecordings = splitAfter.getSelectionModel().getSelectedItem().getValue());
|
||||
GridPane.setMargin(l, new Insets(CHECKBOX_MARGIN, 0, 0, 0));
|
||||
GridPane.setMargin(splitAfter, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN));
|
||||
|
||||
TitledPane general = new TitledPane("General", layout);
|
||||
general.setCollapsible(false);
|
||||
mainLayout.add(general, 0, 0);
|
||||
|
||||
layout = createGridLayout();
|
||||
layout.add(new Label("Recordings Directory"), 0, 0);
|
||||
recordingsDirectory = new TextField(Config.getInstance().getSettings().recordingsDir);
|
||||
recordingsDirectory.focusedProperty().addListener(createRecordingsDirectoryFocusListener());
|
||||
|
@ -95,7 +144,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
layout.add(mediaPlayer, 1, 1);
|
||||
layout.add(createMpvBrowseButton(), 3, 1);
|
||||
|
||||
Label l = new Label("Allow multiple players");
|
||||
l = new Label("Allow multiple players");
|
||||
layout.add(l, 0, 2);
|
||||
multiplePlayers.setSelected(!Config.getInstance().getSettings().singlePlayer);
|
||||
multiplePlayers.setOnAction((e) -> Config.getInstance().getSettings().singlePlayer = !multiplePlayers.isSelected());
|
||||
|
@ -107,7 +156,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
|
||||
TitledPane locations = new TitledPane("Locations", layout);
|
||||
locations.setCollapsible(false);
|
||||
mainLayout.add(locations, 0, 0);
|
||||
mainLayout.add(locations, 0, 1);
|
||||
|
||||
proxySettingsPane = new ProxySettingsPane();
|
||||
mainLayout.add(proxySettingsPane, 1, 0);
|
||||
|
@ -158,33 +207,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
|
||||
ctb = new TitledPane("Chaturbate", layout);
|
||||
ctb.setCollapsible(false);
|
||||
mainLayout.add(ctb, 0, 1);
|
||||
|
||||
layout = createGridLayout();
|
||||
l = new Label("Display stream resolution in overview");
|
||||
layout.add(l, 0, 0);
|
||||
loadResolution = new CheckBox();
|
||||
loadResolution.setSelected(Config.getInstance().getSettings().determineResolution);
|
||||
loadResolution.setOnAction((e) -> {
|
||||
Config.getInstance().getSettings().determineResolution = loadResolution.isSelected();
|
||||
if(!loadResolution.isSelected()) {
|
||||
ThumbOverviewTab.queue.clear();
|
||||
}
|
||||
});
|
||||
//GridPane.setMargin(l, new Insets(CHECKBOX_MARGIN, 0, 0, 0));
|
||||
GridPane.setMargin(loadResolution, new Insets(0, 0, 0, CHECKBOX_MARGIN));
|
||||
layout.add(loadResolution, 1, 0);
|
||||
|
||||
l = new Label("Manually select stream quality");
|
||||
layout.add(l, 0, 1);
|
||||
chooseStreamQuality.setSelected(Config.getInstance().getSettings().chooseStreamQuality);
|
||||
chooseStreamQuality.setOnAction((e) -> Config.getInstance().getSettings().chooseStreamQuality = chooseStreamQuality.isSelected());
|
||||
GridPane.setMargin(l, new Insets(CHECKBOX_MARGIN, 0, 0, 0));
|
||||
GridPane.setMargin(chooseStreamQuality, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN));
|
||||
layout.add(chooseStreamQuality, 1, 1);
|
||||
TitledPane quality = new TitledPane("Stream Quality", layout);
|
||||
quality.setCollapsible(false);
|
||||
mainLayout.add(quality, 0, 2);
|
||||
mainLayout.add(ctb, 0, 2);
|
||||
|
||||
layout = createGridLayout();
|
||||
l = new Label("Record Location");
|
||||
|
@ -272,6 +295,15 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
setRecordingMode(recordLocal.isSelected());
|
||||
}
|
||||
|
||||
private void setSplitAfterValue() {
|
||||
int value = Config.getInstance().getSettings().splitRecordings;
|
||||
for (SplitAfterOption option : splitAfter.getItems()) {
|
||||
if(option.getValue() == value) {
|
||||
splitAfter.getSelectionModel().select(option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void showRestartRequired() {
|
||||
Alert restart = new AutosizeAlert(AlertType.INFORMATION);
|
||||
restart.setTitle("Restart required");
|
||||
|
@ -295,6 +327,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
ctb.setDisable(!local);
|
||||
recordingsDirectory.setDisable(!local);
|
||||
recordingsDirectoryButton.setDisable(!local);
|
||||
splitAfter.setDisable(!local);
|
||||
}
|
||||
|
||||
private ChangeListener<? super Boolean> createRecordingsDirectoryFocusListener() {
|
||||
|
@ -422,4 +455,24 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
|||
public void saveConfig() {
|
||||
proxySettingsPane.saveConfig();
|
||||
}
|
||||
|
||||
public static class SplitAfterOption {
|
||||
private String label;
|
||||
private int value;
|
||||
|
||||
public SplitAfterOption(String label, int value) {
|
||||
super();
|
||||
this.label = label;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue