Added resoltion tag to thumb cell
Added the possibility to display the video resolution for each model in the top right corner of the thumb. Can be toggled in the settings.
This commit is contained in:
parent
ea3715134e
commit
4c4f885844
|
@ -15,4 +15,5 @@ public class Settings {
|
|||
public String password = "";
|
||||
public String lastDownloadDir = "";
|
||||
public List<Model> models = new ArrayList<Model>();
|
||||
public boolean determineResolution = false;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
package ctbrec.recorder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -13,6 +23,7 @@ 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);
|
||||
|
@ -27,11 +38,44 @@ public class Chaturbate {
|
|||
.post(body)
|
||||
.addHeader("X-Requested-With", "XMLHttpRequest")
|
||||
.build();
|
||||
String content = client.execute(req).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);
|
||||
return streamInfo;
|
||||
Response response = client.execute(req);
|
||||
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);
|
||||
return streamInfo;
|
||||
} else {
|
||||
int code = response.code();
|
||||
String message = response.message();
|
||||
response.close();
|
||||
throw new IOException("Server responded with " + code + " - " + message + " headers: [" + response.headers() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
URL masterUrl = new URL(streamInfo.url);
|
||||
InputStream inputStream = masterUrl.openStream();
|
||||
PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8);
|
||||
Playlist playlist = parser.parse();
|
||||
MasterPlaylist master = playlist.getMasterPlaylist();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import javafx.scene.Node;
|
|||
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.Label;
|
||||
import javafx.scene.control.PasswordField;
|
||||
import javafx.scene.control.RadioButton;
|
||||
|
@ -41,6 +42,7 @@ public class SettingsTab extends Tab {
|
|||
private TextField username;
|
||||
private TextField server;
|
||||
private TextField port;
|
||||
private CheckBox loadResolution;
|
||||
private PasswordField password;
|
||||
private RadioButton recordLocal;
|
||||
private RadioButton recordRemote;
|
||||
|
@ -101,6 +103,12 @@ public class SettingsTab extends Tab {
|
|||
GridPane.setColumnSpan(password, 2);
|
||||
layout.add(password, 1, row);
|
||||
|
||||
layout.add(new Label("Display stream resolution in overview"), 0, ++row);
|
||||
loadResolution = new CheckBox();
|
||||
loadResolution.setSelected(Config.getInstance().getSettings().determineResolution);
|
||||
loadResolution.setOnAction((e) -> Config.getInstance().getSettings().determineResolution = loadResolution.isSelected());
|
||||
layout.add(loadResolution, 1, row);
|
||||
|
||||
layout.add(new Label(), 0, ++row);
|
||||
|
||||
layout.add(new Label("Record Location"), 0, ++row);
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
package ctbrec.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
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;
|
||||
|
@ -54,12 +60,17 @@ public class ThumbCell extends StackPane {
|
|||
private static final int HEIGHT = 135;
|
||||
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;
|
||||
private Rectangle nameBackground;
|
||||
private Rectangle topicBackground;
|
||||
private Text name;
|
||||
private Text topic;
|
||||
private Text resolutionTag;
|
||||
private Recorder recorder;
|
||||
private Circle recordingIndicator;
|
||||
private FadeTransition recordingAnimation;
|
||||
|
@ -102,6 +113,15 @@ public class ThumbCell extends StackPane {
|
|||
StackPane.setAlignment(topicBackground, Pos.TOP_LEFT);
|
||||
getChildren().add(topicBackground);
|
||||
|
||||
resolutionBackground = new Rectangle(34, 16);
|
||||
resolutionBackground.setFill(new Color(0.22, 0.8, 0.29, 1));
|
||||
resolutionBackground.setVisible(false);
|
||||
resolutionBackground.setArcHeight(5);
|
||||
resolutionBackground.setArcWidth(resolutionBackground.getArcHeight());
|
||||
StackPane.setAlignment(resolutionBackground, Pos.TOP_RIGHT);
|
||||
StackPane.setMargin(resolutionBackground, new Insets(2));
|
||||
getChildren().add(resolutionBackground);
|
||||
|
||||
name = new Text(model.getName());
|
||||
name.setFill(Color.WHITE);
|
||||
name.setFont(new Font("Sansserif", 16));
|
||||
|
@ -125,10 +145,17 @@ public class ThumbCell extends StackPane {
|
|||
StackPane.setAlignment(topic, Pos.TOP_CENTER);
|
||||
getChildren().add(topic);
|
||||
|
||||
resolutionTag = new Text();
|
||||
resolutionTag.setFill(Color.WHITE);
|
||||
resolutionTag.setVisible(false);
|
||||
StackPane.setAlignment(resolutionTag, Pos.TOP_RIGHT);
|
||||
StackPane.setMargin(resolutionTag, new Insets(2, 4, 2, 2));
|
||||
getChildren().add(resolutionTag);
|
||||
|
||||
recordingIndicator = new Circle(8);
|
||||
recordingIndicator.setFill(colorRecording);
|
||||
StackPane.setMargin(recordingIndicator, new Insets(3));
|
||||
StackPane.setAlignment(recordingIndicator, Pos.TOP_RIGHT);
|
||||
StackPane.setAlignment(recordingIndicator, Pos.TOP_LEFT);
|
||||
getChildren().add(recordingIndicator);
|
||||
recordingAnimation = new FadeTransition(Duration.millis(1000), recordingIndicator);
|
||||
recordingAnimation.setInterpolator(Interpolator.EASE_BOTH);
|
||||
|
@ -163,6 +190,50 @@ public class ThumbCell extends StackPane {
|
|||
setPrefSize(WIDTH, HEIGHT);
|
||||
|
||||
setRecording(recording);
|
||||
if(Config.getInstance().getSettings().determineResolution) {
|
||||
determineResolution();
|
||||
}
|
||||
}
|
||||
|
||||
private void determineResolution() {
|
||||
if(ThumbOverviewTab.resolutionProcessing.contains(model)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
if (resolution[1] > 0) {
|
||||
LOG.trace("Model resolution {} {}x{}", model.getName(), resolution[0], resolution[1]);
|
||||
LOG.trace("Resolution queue size: {}", ThumbOverviewTab.queue.size());
|
||||
final int w = resolution[1];
|
||||
Platform.runLater(() -> {
|
||||
resolutionTag.setText(Integer.toString(w));
|
||||
resolutionTag.setVisible(true);
|
||||
resolutionBackground.setVisible(true);
|
||||
resolutionBackground.setWidth(resolutionTag.getBoundsInLocal().getWidth() + 4);
|
||||
});
|
||||
}
|
||||
} 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);
|
||||
Platform.runLater(() -> {
|
||||
resolutionTag.setText(Integer.toString(res[1]));
|
||||
resolutionTag.setVisible(true);
|
||||
resolutionBackground.setVisible(true);
|
||||
resolutionBackground.setWidth(resolutionTag.getBoundsInLocal().getWidth() + 4);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setImage(String url) {
|
||||
|
@ -369,6 +440,9 @@ public class ThumbCell extends StackPane {
|
|||
topic.setText(model.getDescription());
|
||||
setRecording(recorder.isRecording(model));
|
||||
requestLayout();
|
||||
if(Config.getInstance().getSettings().determineResolution) {
|
||||
determineResolution();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,11 +5,16 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
|
@ -42,6 +47,10 @@ import okhttp3.Response;
|
|||
public class ThumbOverviewTab extends Tab implements TabSelectionListener {
|
||||
private static final transient Logger LOG = LoggerFactory.getLogger(ThumbOverviewTab.class);
|
||||
|
||||
static Set<Model> resolutionProcessing = Collections.synchronizedSet(new HashSet<>());
|
||||
static BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
|
||||
static ExecutorService threadPool = new ThreadPoolExecutor(2, 2, 10, TimeUnit.MINUTES, queue);
|
||||
|
||||
ScheduledService<List<Model>> updateService;
|
||||
Recorder recorder;
|
||||
List<ThumbCell> filteredThumbCells = Collections.synchronizedList(new ArrayList<>());
|
||||
|
|
Loading…
Reference in New Issue