Add setting to split up the recording after x minutes

This commit is contained in:
0xboobface 2018-09-10 13:45:05 +02:00
parent 01aa04b3da
commit 527f5f61ff
4 changed files with 132 additions and 51 deletions

View File

@ -40,4 +40,5 @@ public class Settings {
public boolean windowMaximized = false;
public int windowX;
public int windowY;
public int splitRecordings = 0;
}

View File

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

View File

@ -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) {

View File

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