diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41422077..773b637e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,12 +1,15 @@
1.16.0
========================
-* Thumbnails can show a live preview. Can be switched off in the settings.
+* Thumbnails can show a live preview. Can be switched on in the settings.
+* Live preview is experimental for now, because I noticed some funky behavior
+ of the the internal media player. You can use it on your own risk.
* Added Streamate (metcams, xhamstercams, pornhublive)
* Maximum resolution can be an arbitrary value now
* Added setting for minimal recording length. Recordings, which are shorter
than this value, get deleted automatically.
* Double-click in Recording tab starts the player
* Fix: BongaCams friends tab not working
+* Fix: BongaCams search fails with JSON exception
* Fix: In some cases MFC models got confused
1.15.0
diff --git a/client/pom.xml b/client/pom.xml
index 9ea0e01e..435967b8 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -8,7 +8,7 @@
ctbrec
master
- 1.15.0
+ 1.16.0
../master
diff --git a/client/src/main/java/ctbrec/ui/JavaFxModel.java b/client/src/main/java/ctbrec/ui/JavaFxModel.java
index 0f9019d4..f60ce99d 100644
--- a/client/src/main/java/ctbrec/ui/JavaFxModel.java
+++ b/client/src/main/java/ctbrec/ui/JavaFxModel.java
@@ -10,6 +10,7 @@ import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import ctbrec.Model;
+import ctbrec.recorder.download.Download;
import ctbrec.recorder.download.StreamSource;
import ctbrec.sites.Site;
import javafx.beans.property.BooleanProperty;
@@ -212,4 +213,9 @@ public class JavaFxModel implements Model {
public int compareTo(Model o) {
return delegate.compareTo(o);
}
+
+ @Override
+ public Download createDownload() {
+ return delegate.createDownload();
+ }
}
diff --git a/client/src/main/java/ctbrec/ui/RecordedModelsTab.java b/client/src/main/java/ctbrec/ui/RecordedModelsTab.java
index ab693d3f..020512c5 100644
--- a/client/src/main/java/ctbrec/ui/RecordedModelsTab.java
+++ b/client/src/main/java/ctbrec/ui/RecordedModelsTab.java
@@ -118,6 +118,9 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
preview.setCellValueFactory(cdf -> new SimpleStringProperty(" ▶ "));
preview.setEditable(false);
preview.setId("preview");
+ if(!Config.getInstance().getSettings().livePreviews) {
+ preview.setVisible(false);
+ }
TableColumn name = new TableColumn<>("Model");
name.setPrefWidth(200);
name.setCellValueFactory(new PropertyValueFactory("displayName"));
diff --git a/client/src/main/java/ctbrec/ui/ThumbCell.java b/client/src/main/java/ctbrec/ui/ThumbCell.java
index fa3d4746..4f1cecc8 100644
--- a/client/src/main/java/ctbrec/ui/ThumbCell.java
+++ b/client/src/main/java/ctbrec/ui/ThumbCell.java
@@ -174,7 +174,7 @@ public class ThumbCell extends StackPane {
StackPane.setAlignment(pausedIndicator, Pos.TOP_LEFT);
getChildren().add(pausedIndicator);
- if(Config.getInstance().getSettings().previewInThumbnails) {
+ if(Config.getInstance().getSettings().livePreviews) {
getChildren().add(createPreviewTrigger());
}
diff --git a/client/src/main/java/ctbrec/ui/controls/StreamPreview.java b/client/src/main/java/ctbrec/ui/controls/StreamPreview.java
index 2dd21c52..a7b5911f 100644
--- a/client/src/main/java/ctbrec/ui/controls/StreamPreview.java
+++ b/client/src/main/java/ctbrec/ui/controls/StreamPreview.java
@@ -3,6 +3,7 @@ package ctbrec.ui.controls;
import java.io.InterruptedIOException;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -161,8 +162,9 @@ public class StreamPreview extends StackPane {
private void onError(MediaPlayer videoPlayer) {
LOG.error("Error while starting preview stream", videoPlayer.getError());
- if(videoPlayer.getError().getCause() != null) {
- LOG.error("Error while starting preview stream root cause:", videoPlayer.getError().getCause());
+ Optional cause = Optional.ofNullable(videoPlayer).map(v -> v.getError()).map(e -> e.getCause());
+ if(cause.isPresent()) {
+ LOG.error("Error while starting preview stream root cause:", cause.get());
}
showTestImage();
}
diff --git a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java
index 4585978f..68c797b3 100644
--- a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java
+++ b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java
@@ -64,7 +64,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
private CheckBox chooseStreamQuality = new CheckBox();
private CheckBox multiplePlayers = new CheckBox();
private CheckBox updateThumbnails = new CheckBox();
- private CheckBox previewInThumbnails = new CheckBox();
+ private CheckBox livePreviews = new CheckBox();
private CheckBox showPlayerStarting = new CheckBox();
private RadioButton recordLocal;
private RadioButton recordRemote;
@@ -462,16 +462,17 @@ public class SettingsTab extends Tab implements TabSelectionListener {
GridPane.setMargin(updateThumbnails, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN));
layout.add(updateThumbnails, 1, row++);
- l = new Label("Preview in thumbnails");
+ l = new Label("Enable live previews (experimental)");
layout.add(l, 0, row);
- previewInThumbnails.setSelected(Config.getInstance().getSettings().previewInThumbnails);
- previewInThumbnails.setOnAction((e) -> {
- Config.getInstance().getSettings().previewInThumbnails = previewInThumbnails.isSelected();
+ livePreviews.setSelected(Config.getInstance().getSettings().livePreviews);
+ livePreviews.setOnAction((e) -> {
+ Config.getInstance().getSettings().livePreviews = livePreviews.isSelected();
saveConfig();
+ showRestartRequired();
});
GridPane.setMargin(l, new Insets(3, 0, 0, 0));
- GridPane.setMargin(previewInThumbnails, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN));
- layout.add(previewInThumbnails, 1, row++);
+ GridPane.setMargin(livePreviews, new Insets(CHECKBOX_MARGIN, 0, 0, CHECKBOX_MARGIN));
+ layout.add(livePreviews, 1, row++);
l = new Label("Start Tab");
layout.add(l, 0, row);
@@ -528,6 +529,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
onlineCheckIntervalInSecs.setDisable(!local);
leaveSpaceOnDevice.setDisable(!local);
postProcessing.setDisable(!local);
+ minimumLengthInSecs.setDisable(!local);
}
@Override
diff --git a/common/pom.xml b/common/pom.xml
index 4f20e4fa..4308d633 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -8,7 +8,7 @@
ctbrec
master
- 1.15.0
+ 1.16.0
../master
diff --git a/common/src/main/java/ctbrec/AbstractModel.java b/common/src/main/java/ctbrec/AbstractModel.java
index 53198b05..6f34272c 100644
--- a/common/src/main/java/ctbrec/AbstractModel.java
+++ b/common/src/main/java/ctbrec/AbstractModel.java
@@ -9,6 +9,9 @@ import java.util.concurrent.ExecutionException;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
+import ctbrec.recorder.download.Download;
+import ctbrec.recorder.download.HlsDownload;
+import ctbrec.recorder.download.MergedHlsDownload;
import ctbrec.sites.Site;
public abstract class AbstractModel implements Model {
@@ -184,4 +187,13 @@ public abstract class AbstractModel implements Model {
public Site getSite() {
return site;
}
+
+ @Override
+ public Download createDownload() {
+ if(Config.isServerMode()) {
+ return new HlsDownload(getSite().getHttpClient());
+ } else {
+ return new MergedHlsDownload(getSite().getHttpClient());
+ }
+ }
}
diff --git a/common/src/main/java/ctbrec/Model.java b/common/src/main/java/ctbrec/Model.java
index feb73817..70ddee51 100644
--- a/common/src/main/java/ctbrec/Model.java
+++ b/common/src/main/java/ctbrec/Model.java
@@ -9,6 +9,7 @@ import com.iheartradio.m3u8.PlaylistException;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
+import ctbrec.recorder.download.Download;
import ctbrec.recorder.download.StreamSource;
import ctbrec.sites.Site;
@@ -101,6 +102,6 @@ public interface Model extends Comparable {
public void setSuspended(boolean suspended);
-
+ public Download createDownload();
}
\ No newline at end of file
diff --git a/common/src/main/java/ctbrec/Settings.java b/common/src/main/java/ctbrec/Settings.java
index eb480855..c96cf985 100644
--- a/common/src/main/java/ctbrec/Settings.java
+++ b/common/src/main/java/ctbrec/Settings.java
@@ -69,7 +69,7 @@ public class Settings {
public List models = new ArrayList<>();
public List eventHandlers = new ArrayList<>();
public boolean determineResolution = false;
- public boolean previewInThumbnails = true;
+ public boolean livePreviews = false;
public boolean requireAuthentication = false;
public boolean chooseStreamQuality = false;
public int maximumResolution = 0;
diff --git a/common/src/main/java/ctbrec/io/ModelJsonAdapter.java b/common/src/main/java/ctbrec/io/ModelJsonAdapter.java
index 5296f3e6..54794087 100644
--- a/common/src/main/java/ctbrec/io/ModelJsonAdapter.java
+++ b/common/src/main/java/ctbrec/io/ModelJsonAdapter.java
@@ -5,6 +5,9 @@ import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Optional;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonReader.Token;
@@ -16,6 +19,8 @@ import ctbrec.sites.chaturbate.ChaturbateModel;
public class ModelJsonAdapter extends JsonAdapter {
+ private static final transient Logger LOG = LoggerFactory.getLogger(ModelJsonAdapter.class);
+
private List sites;
public ModelJsonAdapter() {
@@ -62,7 +67,12 @@ public class ModelJsonAdapter extends JsonAdapter {
model.setSuspended(suspended);
} else if(key.equals("siteSpecific")) {
reader.beginObject();
- model.readSiteSpecificData(reader);
+ try {
+ model.readSiteSpecificData(reader);
+ } catch(Exception e) {
+ LOG.error("Couldn't read site specific data for model {}", model.getName());
+ throw e;
+ }
reader.endObject();
}
} else {
diff --git a/common/src/main/java/ctbrec/recorder/LocalRecorder.java b/common/src/main/java/ctbrec/recorder/LocalRecorder.java
index 5dafee65..97982b94 100644
--- a/common/src/main/java/ctbrec/recorder/LocalRecorder.java
+++ b/common/src/main/java/ctbrec/recorder/LocalRecorder.java
@@ -61,8 +61,6 @@ import ctbrec.io.HttpClient;
import ctbrec.io.StreamRedirectThread;
import ctbrec.recorder.PlaylistGenerator.InvalidPlaylistException;
import ctbrec.recorder.download.Download;
-import ctbrec.recorder.download.HlsDownload;
-import ctbrec.recorder.download.MergedHlsDownload;
public class LocalRecorder implements Recorder {
@@ -194,13 +192,7 @@ public class LocalRecorder implements Recorder {
}
LOG.debug("Starting recording for model {}", model.getName());
- Download download;
- if (Config.isServerMode()) {
- download = new HlsDownload(client);
- } else {
- download = new MergedHlsDownload(client);
- }
-
+ Download download = model.createDownload();
recordingProcesses.put(model, download);
new Thread() {
@Override
@@ -554,8 +546,10 @@ public class LocalRecorder implements Recorder {
if (rec.listFiles().length == 0) {
continue;
}
-
- // TODO don't list recordings, which currently get deleted
+ // don't list recordings, which currently get deleted
+ if (deleteInProgress.contains(rec)) {
+ continue;
+ }
Date startDate = sdf.parse(rec.getName());
Recording recording = new Recording();
diff --git a/common/src/main/java/ctbrec/recorder/download/AbstractHlsDownload.java b/common/src/main/java/ctbrec/recorder/download/AbstractHlsDownload.java
index 1fb6333d..b4ab0507 100644
--- a/common/src/main/java/ctbrec/recorder/download/AbstractHlsDownload.java
+++ b/common/src/main/java/ctbrec/recorder/download/AbstractHlsDownload.java
@@ -52,6 +52,9 @@ public abstract class AbstractHlsDownload implements Download {
Request request = new Request.Builder().url(segmentsUrl).addHeader("connection", "keep-alive").build();
try(Response response = client.execute(request)) {
if(response.isSuccessful()) {
+ // String body = response.body().string();
+ // InputStream inputStream = new ByteArrayInputStream(body.getBytes("utf-8"));
+ // LOG.debug("Segments {}", body);
InputStream inputStream = response.body().byteStream();
PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8, ParsingMode.LENIENT);
Playlist playlist = parser.parse();
diff --git a/common/src/main/java/ctbrec/recorder/download/HlsDownload.java b/common/src/main/java/ctbrec/recorder/download/HlsDownload.java
index f682acda..9eef5d7c 100644
--- a/common/src/main/java/ctbrec/recorder/download/HlsDownload.java
+++ b/common/src/main/java/ctbrec/recorder/download/HlsDownload.java
@@ -76,13 +76,13 @@ public class HlsDownload extends AbstractHlsDownload {
}
int lastSegment = 0;
int nextSegment = 0;
- boolean sleep = true; // this enables sleeping between playlist requests. once we miss a segment, this is set to false, so that no sleeping happens anymore
+ int waitFactor = 1;
while(running) {
SegmentPlaylist lsp = getNextSegments(segments);
if(nextSegment > 0 && lsp.seq > nextSegment) {
// TODO switch to a lower bitrate/resolution ?!?
- LOG.warn("Missed segments {} < {} in download for {}", nextSegment, lsp.seq, model);
- sleep = false;
+ waitFactor *= 2;
+ LOG.warn("Missed segments {} < {} in download for {} - setting wait factor to 1/{}", nextSegment, lsp.seq, model, waitFactor);
}
int skip = nextSegment - lsp.seq;
for (String segment : lsp.segments) {
@@ -97,9 +97,9 @@ public class HlsDownload extends AbstractHlsDownload {
}
long wait = 0;
- if(sleep && lastSegment == lsp.seq) {
+ if(lastSegment == lsp.seq) {
// playlist didn't change -> wait for at least half the target duration
- wait = (long) lsp.targetDuration * 1000 / 2;
+ wait = (long) lsp.targetDuration * 1000 / waitFactor;
LOG.trace("Playlist didn't change... waiting for {}ms", wait);
} else {
// playlist did change -> wait for at least last segment duration
@@ -115,8 +115,12 @@ public class HlsDownload extends AbstractHlsDownload {
}
}
+ // this if check makes sure, that we don't decrease nextSegment. for some reason
+ // streamate playlists sometimes jump back. e.g. max sequence = 79 -> 80 -> 79
lastSegment = lsp.seq;
- nextSegment = lastSegment + lsp.segments.size();
+ if(lastSegment + lsp.segments.size() > nextSegment) {
+ nextSegment = lastSegment + lsp.segments.size();
+ }
}
} else {
throw new IOException("Couldn't determine segments uri");
diff --git a/common/src/main/java/ctbrec/sites/bonga/BongaCams.java b/common/src/main/java/ctbrec/sites/bonga/BongaCams.java
index 573c4d67..52b2d084 100644
--- a/common/src/main/java/ctbrec/sites/bonga/BongaCams.java
+++ b/common/src/main/java/ctbrec/sites/bonga/BongaCams.java
@@ -157,15 +157,17 @@ public class BongaCams extends AbstractSite {
JSONArray results = json.getJSONArray("models");
for (int i = 0; i < results.length(); i++) {
JSONObject result = results.getJSONObject(i);
- Model model = createModel(result.getString("username"));
- String thumb = result.getString("thumb_image");
- if(thumb != null) {
- model.setPreview("https:" + thumb);
+ if(result.has("username")) {
+ Model model = createModel(result.getString("username"));
+ String thumb = result.getString("thumb_image");
+ if(thumb != null) {
+ model.setPreview("https:" + thumb);
+ }
+ if(result.has("display_name")) {
+ model.setDisplayName(result.getString("display_name"));
+ }
+ models.add(model);
}
- if(result.has("display_name")) {
- model.setDisplayName(result.getString("display_name"));
- }
- models.add(model);
}
return models;
} else {
diff --git a/common/src/main/java/ctbrec/sites/streamate/StreamateModel.java b/common/src/main/java/ctbrec/sites/streamate/StreamateModel.java
index 55497e8a..519bce2c 100644
--- a/common/src/main/java/ctbrec/sites/streamate/StreamateModel.java
+++ b/common/src/main/java/ctbrec/sites/streamate/StreamateModel.java
@@ -37,6 +37,7 @@ public class StreamateModel extends AbstractModel {
private List streamSources = new ArrayList<>();
private int[] resolution;
private Long id;
+ private String streamId;
@Override
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
@@ -218,6 +219,11 @@ public class StreamateModel extends AbstractModel {
}
private String getStreamId() throws IOException {
+ loadModelInfo();
+ return streamId;
+ }
+
+ void loadModelInfo() throws IOException {
String url = "https://hybridclient.naiadsystems.com/api/v1/config/?name=" + getName()
+ "&sabasic=&sakey=&sk=www.streamate.com&userid=0&version=6.3.17&ajax=1";
Request request = new Request.Builder()
@@ -232,7 +238,9 @@ public class StreamateModel extends AbstractModel {
if(response.isSuccessful()) {
JSONObject json = new JSONObject(response.body().string());
JSONObject stream = json.getJSONObject("stream");
- return stream.getString("streamId");
+ streamId = stream.getString("streamId");
+ JSONObject performer = json.getJSONObject("performer");
+ id = performer.getLong("id");
} else {
throw new HttpException(response.code(), response.message());
}
@@ -322,6 +330,13 @@ public class StreamateModel extends AbstractModel {
@Override
public void writeSiteSpecificData(JsonWriter writer) throws IOException {
+ if(id == null) {
+ try {
+ loadModelInfo();
+ } catch (IOException e) {
+ LOG.error("Couldn't load model ID for {}. This can cause problems with saving / loading the model", getName());
+ }
+ }
writer.name("id").value(id);
}
}
\ No newline at end of file
diff --git a/master/pom.xml b/master/pom.xml
index 78ea5606..bc994b4a 100644
--- a/master/pom.xml
+++ b/master/pom.xml
@@ -6,7 +6,7 @@
ctbrec
master
pom
- 1.15.0
+ 1.16.0
../common
diff --git a/server/pom.xml b/server/pom.xml
index 24075158..4b71406f 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -8,7 +8,7 @@
ctbrec
master
- 1.15.0
+ 1.16.0
../master