forked from j62/ctbrec
1
0
Fork 0

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:
0xboobface 2018-10-01 16:44:01 +02:00
parent 58dcc2e32d
commit 3df1dbf911
11 changed files with 285 additions and 245 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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