Move Chaturbate class to Model
Chaturbate only contains Model related methods. So it makes sense to encapsulate that functionality in Model. Use guava LoadingCache in Chaturbate class to cache resolution and StreamInfo requests. The entries expire after 5 minutes. Add possibility to filter for public rooms only.
This commit is contained in:
parent
58dcc2e32d
commit
3df1dbf911
|
@ -1,7 +1,35 @@
|
|||
package ctbrec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.iheartradio.m3u8.Encoding;
|
||||
import com.iheartradio.m3u8.Format;
|
||||
import com.iheartradio.m3u8.ParseException;
|
||||
import com.iheartradio.m3u8.PlaylistException;
|
||||
import com.iheartradio.m3u8.PlaylistParser;
|
||||
import com.iheartradio.m3u8.data.MasterPlaylist;
|
||||
import com.iheartradio.m3u8.data.Playlist;
|
||||
import com.iheartradio.m3u8.data.PlaylistData;
|
||||
import com.squareup.moshi.JsonAdapter;
|
||||
import com.squareup.moshi.Moshi;
|
||||
|
||||
import ctbrec.recorder.StreamInfo;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class Model {
|
||||
private String url;
|
||||
|
@ -9,10 +37,7 @@ public class Model {
|
|||
private String preview;
|
||||
private String description;
|
||||
private List<String> tags = new ArrayList<>();
|
||||
private boolean online = false;
|
||||
private int streamUrlIndex = -1;
|
||||
private int streamResolution = -1;
|
||||
private transient String onlineState;
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
|
@ -46,12 +71,18 @@ public class Model {
|
|||
this.tags = tags;
|
||||
}
|
||||
|
||||
public boolean isOnline() {
|
||||
return online;
|
||||
public boolean isOnline() throws IOException, ExecutionException, InterruptedException {
|
||||
return isOnline(false);
|
||||
}
|
||||
|
||||
public void setOnline(boolean online) {
|
||||
this.online = online;
|
||||
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
|
||||
StreamInfo info;
|
||||
if(ignoreCache) {
|
||||
info = Chaturbate.INSTANCE.loadStreamInfo(name);
|
||||
} else {
|
||||
info = Chaturbate.INSTANCE.getStreamInfo(name);
|
||||
}
|
||||
return Objects.equals("public", info.room_status);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
|
@ -70,20 +101,33 @@ public class Model {
|
|||
this.streamUrlIndex = streamUrlIndex;
|
||||
}
|
||||
|
||||
public int getStreamResolution() {
|
||||
return streamResolution;
|
||||
public int[] getStreamResolution(boolean failFast) throws ExecutionException {
|
||||
int[] resolution = Chaturbate.INSTANCE.streamResolutionCache.getIfPresent(name);
|
||||
if(resolution != null) {
|
||||
return Chaturbate.INSTANCE.getResolution(name);
|
||||
} else {
|
||||
return new int[2];
|
||||
}
|
||||
}
|
||||
|
||||
public void setStreamResolution(int streamResolution) {
|
||||
this.streamResolution = streamResolution;
|
||||
public int[] getStreamResolution() throws ExecutionException {
|
||||
return Chaturbate.INSTANCE.getResolution(name);
|
||||
}
|
||||
|
||||
public String getOnlineState() {
|
||||
return onlineState;
|
||||
public String getOnlineState() throws IOException, ExecutionException {
|
||||
return getOnlineState(false);
|
||||
}
|
||||
|
||||
public void setOnlineState(String onlineState) {
|
||||
this.onlineState = onlineState;
|
||||
public String getOnlineState(boolean failFast) throws IOException, ExecutionException {
|
||||
StreamInfo info = Chaturbate.INSTANCE.streamInfoCache.getIfPresent(name);
|
||||
return info != null ? info.room_status : "n/a";
|
||||
}
|
||||
|
||||
public StreamInfo getStreamInfo() throws IOException, ExecutionException {
|
||||
return Chaturbate.INSTANCE.getStreamInfo(name);
|
||||
}
|
||||
public MasterPlaylist getMasterPlaylist() throws IOException, ParseException, PlaylistException, ExecutionException {
|
||||
return Chaturbate.INSTANCE.getMasterPlaylist(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -122,9 +166,126 @@ public class Model {
|
|||
return name;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Model model = new Model();
|
||||
model.name = "A";
|
||||
model.url = "url";
|
||||
private static class Chaturbate {
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(Chaturbate.class);
|
||||
|
||||
public static final Chaturbate INSTANCE = new Chaturbate(HttpClient.getInstance());
|
||||
|
||||
private HttpClient client;
|
||||
|
||||
private static long lastRequest = System.currentTimeMillis();
|
||||
|
||||
private LoadingCache<String, StreamInfo> streamInfoCache = CacheBuilder.newBuilder()
|
||||
.initialCapacity(10_000)
|
||||
.maximumSize(10_000)
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.build(new CacheLoader<String, StreamInfo> () {
|
||||
@Override
|
||||
public StreamInfo load(String model) throws Exception {
|
||||
return loadStreamInfo(model);
|
||||
}
|
||||
});
|
||||
|
||||
private LoadingCache<String, int[]> streamResolutionCache = CacheBuilder.newBuilder()
|
||||
.initialCapacity(10_000)
|
||||
.maximumSize(10_000)
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.build(new CacheLoader<String, int[]> () {
|
||||
@Override
|
||||
public int[] load(String model) throws Exception {
|
||||
return loadResolution(model);
|
||||
}
|
||||
});
|
||||
|
||||
public Chaturbate(HttpClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
private StreamInfo getStreamInfo(String modelName) throws IOException, ExecutionException {
|
||||
return streamInfoCache.get(modelName);
|
||||
}
|
||||
|
||||
private StreamInfo loadStreamInfo(String modelName) throws IOException, InterruptedException {
|
||||
throttleRequests();
|
||||
RequestBody body = new FormBody.Builder()
|
||||
.add("room_slug", modelName)
|
||||
.add("bandwidth", "high")
|
||||
.build();
|
||||
Request req = new Request.Builder()
|
||||
.url("https://chaturbate.com/get_edge_hls_url_ajax/")
|
||||
.post(body)
|
||||
.addHeader("X-Requested-With", "XMLHttpRequest")
|
||||
.build();
|
||||
Response response = client.execute(req);
|
||||
try {
|
||||
if(response.isSuccessful()) {
|
||||
String content = response.body().string();
|
||||
LOG.trace("Raw stream info: {}", content);
|
||||
Moshi moshi = new Moshi.Builder().build();
|
||||
JsonAdapter<StreamInfo> adapter = moshi.adapter(StreamInfo.class);
|
||||
StreamInfo streamInfo = adapter.fromJson(content);
|
||||
return streamInfo;
|
||||
} else {
|
||||
int code = response.code();
|
||||
String message = response.message();
|
||||
throw new IOException("Server responded with " + code + " - " + message + " headers: [" + response.headers() + "]");
|
||||
}
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
|
||||
public int[] getResolution(String modelName) throws ExecutionException {
|
||||
return streamResolutionCache.get(modelName);
|
||||
}
|
||||
|
||||
private int[] loadResolution(String modelName) throws IOException, ParseException, PlaylistException, ExecutionException, InterruptedException {
|
||||
int[] res = new int[2];
|
||||
StreamInfo streamInfo = getStreamInfo(modelName);
|
||||
if(!streamInfo.url.startsWith("http")) {
|
||||
return res;
|
||||
}
|
||||
|
||||
MasterPlaylist master = getMasterPlaylist(modelName);
|
||||
for (PlaylistData playlistData : master.getPlaylists()) {
|
||||
if(playlistData.hasStreamInfo() && playlistData.getStreamInfo().hasResolution()) {
|
||||
int h = playlistData.getStreamInfo().getResolution().height;
|
||||
int w = playlistData.getStreamInfo().getResolution().width;
|
||||
if(w > res[1]) {
|
||||
res[0] = w;
|
||||
res[1] = h;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private void throttleRequests() throws InterruptedException {
|
||||
long now = System.currentTimeMillis();
|
||||
long diff = now-lastRequest;
|
||||
if(diff < 500) {
|
||||
Thread.sleep(diff);
|
||||
}
|
||||
}
|
||||
|
||||
public MasterPlaylist getMasterPlaylist(String modelName) throws IOException, ParseException, PlaylistException, ExecutionException {
|
||||
StreamInfo streamInfo = getStreamInfo(modelName);
|
||||
return getMasterPlaylist(streamInfo);
|
||||
}
|
||||
|
||||
public MasterPlaylist getMasterPlaylist(StreamInfo streamInfo) throws IOException, ParseException, PlaylistException {
|
||||
LOG.trace("Loading master playlist {}", streamInfo.url);
|
||||
Request req = new Request.Builder().url(streamInfo.url).build();
|
||||
Response response = client.execute(req);
|
||||
try {
|
||||
InputStream inputStream = response.body().byteStream();
|
||||
PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8);
|
||||
Playlist playlist = parser.parse();
|
||||
MasterPlaylist master = playlist.getMasterPlaylist();
|
||||
return master;
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import static ctbrec.ui.CtbrecApplication.BASE_URI;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
@ -27,7 +26,6 @@ public class ModelParser {
|
|||
model.setPreview(HtmlParser.getTag(cellHtml, "a img").attr("src"));
|
||||
model.setUrl(BASE_URI + HtmlParser.getTag(cellHtml, "a").attr("href"));
|
||||
model.setDescription(HtmlParser.getText(cellHtml, "div.details ul.subject"));
|
||||
model.setOnline(!Objects.equals("offline", HtmlParser.getText(cellHtml, "div.details li.cams")));
|
||||
Elements tags = HtmlParser.getTags(cellHtml, "div.details ul.subject li a");
|
||||
if(tags != null) {
|
||||
for (Element tag : tags) {
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
package ctbrec.recorder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.iheartradio.m3u8.Encoding;
|
||||
import com.iheartradio.m3u8.Format;
|
||||
import com.iheartradio.m3u8.ParseException;
|
||||
import com.iheartradio.m3u8.PlaylistException;
|
||||
import com.iheartradio.m3u8.PlaylistParser;
|
||||
import com.iheartradio.m3u8.data.MasterPlaylist;
|
||||
import com.iheartradio.m3u8.data.Playlist;
|
||||
import com.iheartradio.m3u8.data.PlaylistData;
|
||||
import com.squareup.moshi.JsonAdapter;
|
||||
import com.squareup.moshi.Moshi;
|
||||
|
||||
import ctbrec.HttpClient;
|
||||
import ctbrec.Model;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class Chaturbate {
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(Chaturbate.class);
|
||||
|
||||
public static StreamInfo getStreamInfo(Model model, HttpClient client) throws IOException {
|
||||
RequestBody body = new FormBody.Builder()
|
||||
.add("room_slug", model.getName())
|
||||
.add("bandwidth", "high")
|
||||
.build();
|
||||
Request req = new Request.Builder()
|
||||
.url("https://chaturbate.com/get_edge_hls_url_ajax/")
|
||||
.post(body)
|
||||
.addHeader("X-Requested-With", "XMLHttpRequest")
|
||||
.build();
|
||||
Response response = client.execute(req);
|
||||
try {
|
||||
if(response.isSuccessful()) {
|
||||
String content = response.body().string();
|
||||
LOG.debug("Raw stream info: {}", content);
|
||||
Moshi moshi = new Moshi.Builder().build();
|
||||
JsonAdapter<StreamInfo> adapter = moshi.adapter(StreamInfo.class);
|
||||
StreamInfo streamInfo = adapter.fromJson(content);
|
||||
model.setOnline(Objects.equals(streamInfo.room_status, "public"));
|
||||
return streamInfo;
|
||||
} else {
|
||||
int code = response.code();
|
||||
String message = response.message();
|
||||
throw new IOException("Server responded with " + code + " - " + message + " headers: [" + response.headers() + "]");
|
||||
}
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] getResolution(Model model, HttpClient client) throws IOException, ParseException, PlaylistException {
|
||||
int[] res = new int[2];
|
||||
StreamInfo streamInfo = getStreamInfo(model, client);
|
||||
if(!streamInfo.url.startsWith("http")) {
|
||||
return res;
|
||||
}
|
||||
|
||||
MasterPlaylist master = getMasterPlaylist(model, client);
|
||||
for (PlaylistData playlistData : master.getPlaylists()) {
|
||||
if(playlistData.hasStreamInfo() && playlistData.getStreamInfo().hasResolution()) {
|
||||
int h = playlistData.getStreamInfo().getResolution().height;
|
||||
int w = playlistData.getStreamInfo().getResolution().width;
|
||||
if(w > res[1]) {
|
||||
res[0] = w;
|
||||
res[1] = h;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static MasterPlaylist getMasterPlaylist(Model model, HttpClient client) throws IOException, ParseException, PlaylistException {
|
||||
StreamInfo streamInfo = getStreamInfo(model, client);
|
||||
return getMasterPlaylist(streamInfo, client);
|
||||
}
|
||||
|
||||
public static MasterPlaylist getMasterPlaylist(StreamInfo streamInfo, HttpClient client) throws IOException, ParseException, PlaylistException {
|
||||
LOG.trace("Loading master playlist {}", streamInfo.url);
|
||||
Request req = new Request.Builder().url(streamInfo.url).build();
|
||||
Response response = client.execute(req);
|
||||
try {
|
||||
InputStream inputStream = response.body().byteStream();
|
||||
PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8);
|
||||
Playlist playlist = parser.parse();
|
||||
MasterPlaylist master = playlist.getMasterPlaylist();
|
||||
return master;
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -60,7 +59,6 @@ public class LocalRecorder implements Recorder {
|
|||
public LocalRecorder(Config config) {
|
||||
this.config = config;
|
||||
config.getSettings().models.stream().forEach((m) -> {
|
||||
m.setOnline(false);
|
||||
models.add(m);
|
||||
});
|
||||
|
||||
|
@ -193,13 +191,6 @@ public class LocalRecorder implements Recorder {
|
|||
}
|
||||
}
|
||||
|
||||
private boolean checkIfOnline(Model model) throws IOException {
|
||||
StreamInfo streamInfo = Chaturbate.getStreamInfo(model, client);
|
||||
boolean online = Objects.equals(streamInfo.room_status, "public");
|
||||
model.setOnline(online);
|
||||
return online;
|
||||
}
|
||||
|
||||
private void tryRestartRecording(Model model) {
|
||||
if (!recording) {
|
||||
// recorder is not in recording state
|
||||
|
@ -208,7 +199,7 @@ public class LocalRecorder implements Recorder {
|
|||
|
||||
try {
|
||||
boolean modelInRecordingList = isRecording(model);
|
||||
boolean online = checkIfOnline(model);
|
||||
boolean online = model.isOnline();
|
||||
if (modelInRecordingList && online) {
|
||||
LOG.info("Restarting recording for model {}", model);
|
||||
recordingProcesses.remove(model);
|
||||
|
@ -354,7 +345,8 @@ public class LocalRecorder implements Recorder {
|
|||
for (Model model : getModelsRecording()) {
|
||||
try {
|
||||
if (!recordingProcesses.containsKey(model)) {
|
||||
boolean isOnline = checkIfOnline(model);
|
||||
boolean ignoreCache = true;
|
||||
boolean isOnline = model.isOnline(ignoreCache);
|
||||
LOG.trace("Checking online state for {}: {}", model, (isOnline ? "online" : "offline"));
|
||||
if (isOnline) {
|
||||
LOG.info("Model {}'s room back to public. Starting recording", model);
|
||||
|
@ -363,7 +355,6 @@ public class LocalRecorder implements Recorder {
|
|||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Couldn't check if model {} is online", model.getName(), e);
|
||||
model.setOnline(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ import com.iheartradio.m3u8.PlaylistException;
|
|||
import ctbrec.Config;
|
||||
import ctbrec.HttpClient;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.recorder.Chaturbate;
|
||||
import ctbrec.recorder.StreamInfo;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
@ -42,7 +41,7 @@ public class HlsDownload extends AbstractHlsDownload {
|
|||
public void start(Model model, Config config) throws IOException {
|
||||
try {
|
||||
running = true;
|
||||
StreamInfo streamInfo = Chaturbate.getStreamInfo(model, client);
|
||||
StreamInfo streamInfo = model.getStreamInfo();
|
||||
if(!Objects.equals(streamInfo.room_status, "public")) {
|
||||
throw new IOException(model.getName() +"'s room is not public");
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ import ctbrec.Config;
|
|||
import ctbrec.HttpClient;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.Recording;
|
||||
import ctbrec.recorder.Chaturbate;
|
||||
import ctbrec.recorder.ProgressListener;
|
||||
import ctbrec.recorder.StreamInfo;
|
||||
import okhttp3.Request;
|
||||
|
@ -84,7 +83,7 @@ public class MergedHlsDownload extends AbstractHlsDownload {
|
|||
try {
|
||||
running = true;
|
||||
startTime = ZonedDateTime.now();
|
||||
StreamInfo streamInfo = Chaturbate.getStreamInfo(model, client);
|
||||
StreamInfo streamInfo = model.getStreamInfo();
|
||||
if(!Objects.equals(streamInfo.room_status, "public")) {
|
||||
throw new IOException(model.getName() +"'s room is not public");
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ public class JavaFxModel extends Model {
|
|||
|
||||
public JavaFxModel(Model delegate) {
|
||||
this.delegate = delegate;
|
||||
setOnline(delegate.isOnline());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -59,17 +58,6 @@ public class JavaFxModel extends Model {
|
|||
delegate.setTags(tags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return delegate.isOnline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnline(boolean online) {
|
||||
delegate.setOnline(online);
|
||||
this.onlineProperty.set(online);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return delegate.hashCode();
|
||||
|
|
|
@ -5,6 +5,7 @@ import java.security.InvalidKeyException;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
@ -150,9 +151,6 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
|
|||
for (Model model : models) {
|
||||
if (!observableModels.contains(model)) {
|
||||
observableModels.add(new JavaFxModel(model));
|
||||
} else {
|
||||
int index = observableModels.indexOf(model);
|
||||
observableModels.get(index).setOnline(model.isOnline());
|
||||
}
|
||||
}
|
||||
for (Iterator<JavaFxModel> iterator = observableModels.iterator(); iterator.hasNext();) {
|
||||
|
@ -233,6 +231,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
|
||||
private void switchStreamSource(JavaFxModel fxModel) {
|
||||
try {
|
||||
if(!fxModel.isOnline()) {
|
||||
Alert alert = new AutosizeAlert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle("Switch resolution");
|
||||
|
@ -241,6 +240,14 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener {
|
|||
alert.showAndWait();
|
||||
return;
|
||||
}
|
||||
} catch (IOException | ExecutionException | InterruptedException e1) {
|
||||
Alert alert = new AutosizeAlert(Alert.AlertType.INFORMATION);
|
||||
alert.setTitle("Switch resolution");
|
||||
alert.setHeaderText("Couldn't switch stream resolution");
|
||||
alert.setContentText("An error occured while checking, if the model is online");
|
||||
alert.showAndWait();
|
||||
return;
|
||||
}
|
||||
|
||||
HttpClient client = HttpClient.getInstance();
|
||||
Function<Model, Void> onSuccess = (m) -> {
|
||||
|
|
|
@ -14,7 +14,6 @@ import com.iheartradio.m3u8.data.PlaylistData;
|
|||
|
||||
import ctbrec.HttpClient;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.recorder.Chaturbate;
|
||||
import ctbrec.recorder.StreamInfo;
|
||||
import ctbrec.recorder.download.StreamSource;
|
||||
import javafx.concurrent.Task;
|
||||
|
@ -27,8 +26,8 @@ public class StreamSourceSelectionDialog {
|
|||
Task<List<StreamSource>> selectStreamSource = new Task<List<StreamSource>>() {
|
||||
@Override
|
||||
protected List<StreamSource> call() throws Exception {
|
||||
StreamInfo streamInfo = Chaturbate.getStreamInfo(model, client);
|
||||
MasterPlaylist masterPlaylist = Chaturbate.getMasterPlaylist(streamInfo, client);
|
||||
StreamInfo streamInfo = model.getStreamInfo();
|
||||
MasterPlaylist masterPlaylist = model.getMasterPlaylist();
|
||||
List<StreamSource> sources = new ArrayList<>();
|
||||
for (PlaylistData playlist : masterPlaylist.getPlaylists()) {
|
||||
if (playlist.hasStreamInfo()) {
|
||||
|
|
|
@ -1,21 +1,16 @@
|
|||
package ctbrec.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.iheartradio.m3u8.ParseException;
|
||||
import com.iheartradio.m3u8.PlaylistException;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.HttpClient;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.recorder.Chaturbate;
|
||||
import ctbrec.recorder.Recorder;
|
||||
import ctbrec.recorder.StreamInfo;
|
||||
import javafx.animation.FadeTransition;
|
||||
|
@ -56,9 +51,6 @@ public class ThumbCell extends StackPane {
|
|||
public static int width = 180;
|
||||
private static final Duration ANIMATION_DURATION = new Duration(250);
|
||||
|
||||
// this acts like a cache, once the stream resolution for a model has been determined, we don't do it again (until ctbrec is restarted)
|
||||
private static Map<String, int[]> resolutions = new HashMap<>();
|
||||
|
||||
private Model model;
|
||||
private ImageView iv;
|
||||
private Rectangle resolutionBackground;
|
||||
|
@ -199,53 +191,33 @@ public class ThumbCell extends StackPane {
|
|||
return;
|
||||
}
|
||||
|
||||
ThumbOverviewTab.threadPool.submit(() -> {
|
||||
try {
|
||||
ThumbOverviewTab.resolutionProcessing.add(model);
|
||||
int[] res = resolutions.get(model.getName());
|
||||
if(res == null) {
|
||||
ThumbOverviewTab.threadPool.submit(() -> {
|
||||
try {
|
||||
Thread.sleep(500); // throttle down, so that we don't do too many requests
|
||||
int[] resolution = Chaturbate.getResolution(model, client);
|
||||
resolutions.put(model.getName(), resolution);
|
||||
int[] resolution = model.getStreamResolution();
|
||||
updateResolutionTag(resolution);
|
||||
} catch (IOException | ParseException | PlaylistException | InterruptedException e) {
|
||||
LOG.error("Coulnd't get resolution for model {}", model, e);
|
||||
} finally {
|
||||
ThumbOverviewTab.resolutionProcessing.remove(model);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ThumbOverviewTab.resolutionProcessing.remove(model);
|
||||
ThumbOverviewTab.threadPool.submit(() -> {
|
||||
try {
|
||||
updateResolutionTag(res);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Coulnd't get resolution for model {}", model, e);
|
||||
} finally {
|
||||
ThumbOverviewTab.resolutionProcessing.remove(model);
|
||||
}
|
||||
});
|
||||
|
||||
// the model is online, but the resolution is 0. probably something went wrong
|
||||
// when we first requested the stream info, so we remove this invalid value from the "cache"
|
||||
// so that it is requested again
|
||||
if(model.isOnline() && res[1] == 0) {
|
||||
ThumbOverviewTab.threadPool.submit(() -> {
|
||||
try {
|
||||
Chaturbate.getStreamInfo(model, client);
|
||||
if(model.isOnline()) {
|
||||
if (model.isOnline() && resolution[1] == 0) {
|
||||
LOG.debug("Removing invalid resolution value for {}", model.getName());
|
||||
resolutions.remove(model.getName());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | ExecutionException | InterruptedException e) {
|
||||
LOG.error("Coulnd't get resolution for model {}", model, e);
|
||||
}
|
||||
} catch (ExecutionException e1) {
|
||||
LOG.warn("Couldn't update resolution tag for model {} - {}", model.getName(), e1.getCause().getMessage());
|
||||
} catch (IOException e1) {
|
||||
LOG.warn("Couldn't update resolution tag for model {} - {}", model.getName(), e1.getMessage());
|
||||
} finally {
|
||||
ThumbOverviewTab.resolutionProcessing.remove(model);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateResolutionTag(int[] resolution) throws IOException {
|
||||
private void updateResolutionTag(int[] resolution) throws IOException, ExecutionException {
|
||||
String _res = "n/a";
|
||||
Paint resolutionBackgroundColor = resolutionOnlineColor;
|
||||
if (resolution[1] > 0) {
|
||||
|
@ -253,12 +225,18 @@ public class ThumbCell extends StackPane {
|
|||
LOG.trace("Resolution queue size: {}", ThumbOverviewTab.queue.size());
|
||||
final int w = resolution[1];
|
||||
_res = Integer.toString(w);
|
||||
model.setStreamResolution(w);
|
||||
model.setOnlineState("online");
|
||||
} else {
|
||||
_res = Chaturbate.getStreamInfo(model, client).room_status;
|
||||
if(model.getOnlineState() != null) {
|
||||
String state = model.getOnlineState();
|
||||
Platform.runLater(() -> {
|
||||
resolutionTag.setText(state);
|
||||
resolutionTag.setVisible(true);
|
||||
resolutionBackground.setVisible(true);
|
||||
resolutionBackground.setWidth(resolutionTag.getBoundsInLocal().getWidth() + 4);
|
||||
});
|
||||
}
|
||||
_res = model.getOnlineState();
|
||||
resolutionBackgroundColor = resolutionOfflineColor;
|
||||
model.setOnlineState(_res);
|
||||
}
|
||||
final String resText = _res;
|
||||
final Paint c = resolutionBackgroundColor;
|
||||
|
@ -306,7 +284,7 @@ public class ThumbCell extends StackPane {
|
|||
// or maybe not, because the player should automatically switch between resolutions depending on the
|
||||
// network bandwidth
|
||||
try {
|
||||
StreamInfo streamInfo = Chaturbate.getStreamInfo(model, client);
|
||||
StreamInfo streamInfo = model.getStreamInfo();
|
||||
if(streamInfo.room_status.equals("public")) {
|
||||
LOG.debug("Playing {}", streamInfo.url);
|
||||
Player.play(streamInfo.url);
|
||||
|
@ -316,7 +294,7 @@ public class ThumbCell extends StackPane {
|
|||
alert.setHeaderText("Room is currently not public");
|
||||
alert.showAndWait();
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
} catch (IOException | ExecutionException e1) {
|
||||
LOG.error("Couldn't get stream information for model {}", model, e1);
|
||||
Alert alert = new AutosizeAlert(Alert.AlertType.ERROR);
|
||||
alert.setTitle("Error");
|
||||
|
@ -456,9 +434,7 @@ public class ThumbCell extends StackPane {
|
|||
//this.model = model;
|
||||
this.model.setName(model.getName());
|
||||
this.model.setDescription(model.getDescription());
|
||||
this.model.setOnline(model.isOnline());
|
||||
this.model.setPreview(model.getPreview());
|
||||
this.model.setStreamResolution(model.getStreamResolution());
|
||||
this.model.setTags(model.getTags());
|
||||
this.model.setUrl(model.getUrl());
|
||||
|
||||
|
@ -477,8 +453,18 @@ public class ThumbCell extends StackPane {
|
|||
setRecording(recorder.isRecording(model));
|
||||
setImage(model.getPreview());
|
||||
topic.setText(model.getDescription());
|
||||
//Tooltip t = new Tooltip(model.getDescription());
|
||||
//Tooltip.install(this, t);
|
||||
|
||||
// ThumbOverviewTab.threadPool.submit(() -> {
|
||||
// StreamInfo streamInfo;
|
||||
// try {
|
||||
// streamInfo = Chaturbate.INSTANCE.getStreamInfo(model);
|
||||
// model.setOnline(streamInfo.room_status.equals("public"));
|
||||
// model.setOnlineState(streamInfo.room_status);
|
||||
// } catch (IOException | ExecutionException e) {
|
||||
// LOG.error("Couldn't retrieve stream information for model {}", model.getName());
|
||||
// }
|
||||
// });
|
||||
|
||||
if(Config.getInstance().getSettings().determineResolution) {
|
||||
determineResolution();
|
||||
} else {
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
@ -111,8 +112,11 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
gridLock.unlock();
|
||||
}
|
||||
});
|
||||
search.setTooltip(new Tooltip("Filter the models by their name, stream description or #hashtags.\n\n"+""
|
||||
+ "If the display of stream resolution is enabled, you can even filter by resolution. Try \"1080\" or \">720\""));
|
||||
Tooltip searchTooltip = new Tooltip("Filter the models by their name, stream description or #hashtags.\n\n"
|
||||
+ "If the display of stream resolution is enabled, you can even filter for public rooms or by resolution.\n\n"
|
||||
+ "Try \"1080\" or \">720\" or \"public\"");
|
||||
search.setTooltip(searchTooltip);
|
||||
|
||||
BorderPane.setMargin(search, new Insets(5));
|
||||
|
||||
scrollPane.setContent(grid);
|
||||
|
@ -479,25 +483,30 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
|
||||
private boolean matches(Model m, String filter) {
|
||||
try {
|
||||
String[] tokens = filter.split(" ");
|
||||
StringBuilder searchTextBuilder = new StringBuilder(m.getName());
|
||||
searchTextBuilder.append(' ');
|
||||
for (String tag : m.getTags()) {
|
||||
searchTextBuilder.append(tag).append(' ');
|
||||
}
|
||||
searchTextBuilder.append(m.getStreamResolution());
|
||||
int[] resolution = m.getStreamResolution(true);
|
||||
searchTextBuilder.append(resolution[1]);
|
||||
String searchText = searchTextBuilder.toString().trim();
|
||||
//LOG.debug("{} -> {}", m.getName(), searchText);
|
||||
boolean tokensMissing = false;
|
||||
for (String token : tokens) {
|
||||
if(token.matches(">\\d+")) {
|
||||
int res = Integer.parseInt(token.substring(1));
|
||||
if(m.getStreamResolution() < res) {
|
||||
if(resolution[1] < res) {
|
||||
tokensMissing = true;
|
||||
}
|
||||
} else if(token.matches("<\\d+")) {
|
||||
int res = Integer.parseInt(token.substring(1));
|
||||
if(m.getStreamResolution() > res) {
|
||||
if(resolution[1] > res) {
|
||||
tokensMissing = true;
|
||||
}
|
||||
} else if(token.equals("public")) {
|
||||
if(!m.getOnlineState(true).equals(token)) {
|
||||
tokensMissing = true;
|
||||
}
|
||||
} else if(!searchText.contains(token)) {
|
||||
|
@ -505,6 +514,10 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
|||
}
|
||||
}
|
||||
return !tokensMissing;
|
||||
} catch (NumberFormatException | ExecutionException | IOException e) {
|
||||
LOG.error("Error while filtering model list", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private ScheduledService<List<Model>> createUpdateService() {
|
||||
|
|
Loading…
Reference in New Issue