Added Online / Offline switch on "Favorites" tab for Streamray
This commit is contained in:
parent
67ef95cc8c
commit
f45991effc
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -19,13 +19,15 @@
|
|||
* Streamate:
|
||||
- Fixed "Couldn't load model ID" error while adding models by URL or by
|
||||
nickname
|
||||
- Online/Offline switch on all tabs. Up to 10 000 offline models in each
|
||||
- Online / Offline switch on all tabs. Up to 10 000 offline models in each
|
||||
category. How do you like it, Elon Musk?
|
||||
- Added "New Girls" tab and adjusted others. All same models on less tabs.
|
||||
- Added "New Girls" tab and adjusted others. All same models on less tabs
|
||||
* Stripchat:
|
||||
- Added "Private" tab.
|
||||
- Added "Private" tab
|
||||
- CTBRec can record your Spy/Private/Ticket shows (login required)
|
||||
*
|
||||
* Streamray:
|
||||
- Added models tags
|
||||
- Added Online / Offline switch on "Favorites" tab
|
||||
|
||||
5.2.3
|
||||
========================
|
||||
|
|
|
@ -2,17 +2,95 @@ package ctbrec.ui.sites.streamray;
|
|||
|
||||
import ctbrec.sites.streamray.Streamray;
|
||||
import ctbrec.ui.tabs.PaginatedScheduledService;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.json.JSONArray;
|
||||
|
||||
abstract class AbstractStreamrayUpdateService extends PaginatedScheduledService {
|
||||
import java.net.URLEncoder;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
protected int modelsPerPage = 48;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public abstract class AbstractStreamrayUpdateService extends PaginatedScheduledService {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private static JSONArray mapping;
|
||||
protected static final int MODELS_PER_PAGE = 48;
|
||||
protected static final String API_URL = "https://beta-api.cams.com/won/compressed/";
|
||||
protected final Streamray site;
|
||||
|
||||
AbstractStreamrayUpdateService(Streamray site) {
|
||||
this.site = site;
|
||||
protected String getPreviewURL(String name) {
|
||||
String lname = name.toLowerCase();
|
||||
String url = MessageFormat.format("https://images4.streamray.com/images/streamray/won/jpg/{0}/{1}/{2}_640.jpg", lname.substring(0, 1), lname.substring(lname.length() - 1), lname);
|
||||
try {
|
||||
return MessageFormat.format("https://dynimages.securedataimages.com/unsigned/rs:fill:640::0/g:no/plain/{0}@jpg", URLEncoder.encode(url, UTF_8));
|
||||
} catch (Exception ex) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isLoggedIn() {
|
||||
return site.isLoggedIn();
|
||||
protected List<String> createTags(JSONArray m) {
|
||||
List<String> tags = new ArrayList<>();
|
||||
int idx1 = mappingIndex("gender");
|
||||
switch (m.optString(idx1)) {
|
||||
case "M" -> tags.add("male");
|
||||
case "F" -> tags.add("female");
|
||||
case "TS" -> tags.add("trans");
|
||||
default -> {
|
||||
// don't add anything
|
||||
}
|
||||
}
|
||||
int idx2 = mappingIndex("ethnicity");
|
||||
switch (m.optString(idx2)) {
|
||||
case "02" -> tags.add("asian");
|
||||
case "03" -> tags.add("ebony");
|
||||
case "04" -> tags.add("white");
|
||||
case "05" -> tags.add("indian");
|
||||
case "06" -> tags.add("latina");
|
||||
case "07" -> tags.add("middle-eastern");
|
||||
default -> {
|
||||
// don't add anything
|
||||
}
|
||||
}
|
||||
int idx3 = mappingIndex("hair_color");
|
||||
switch (m.optString(idx3)) {
|
||||
case "01" -> tags.add("black-hair");
|
||||
case "02" -> tags.add("blonde");
|
||||
case "03" -> tags.add("brunette");
|
||||
case "06" -> tags.add("redhead");
|
||||
default -> {
|
||||
// don't add anything
|
||||
}
|
||||
}
|
||||
int idx4 = mappingIndex("chat_type");
|
||||
switch (m.optString(idx4)) {
|
||||
case "0" -> tags.add("offline");
|
||||
case "1" -> tags.add("public");
|
||||
case "2" -> tags.add("nude-show");
|
||||
case "3" -> tags.add("private");
|
||||
case "4" -> tags.add("exclusive");
|
||||
case "6" -> tags.add("ticket-show");
|
||||
case "7" -> tags.add("voyeur");
|
||||
case "10" -> tags.add("party");
|
||||
case "13" -> tags.add("group");
|
||||
case "14" -> tags.add("c2c");
|
||||
default -> {
|
||||
// don't add anything
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
protected int mappingIndex(String s) {
|
||||
for (var i = 0; i < mapping.length(); i++) {
|
||||
if (Objects.equals(s, mapping.get(i))) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,36 @@
|
|||
package ctbrec.ui.sites.streamray;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.StringUtil;
|
||||
import ctbrec.io.HttpException;
|
||||
import ctbrec.sites.streamray.Streamray;
|
||||
import ctbrec.sites.streamray.StreamrayHttpClient;
|
||||
import ctbrec.sites.streamray.StreamrayModel;
|
||||
import javafx.concurrent.Task;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
|
||||
import static ctbrec.io.HttpConstants.*;
|
||||
|
||||
@Slf4j
|
||||
public class StreamrayFavoritesService extends AbstractStreamrayUpdateService {
|
||||
|
||||
private List<StreamrayModel> modelsList;
|
||||
private Instant lastListInfoRequest = Instant.EPOCH;
|
||||
@Getter
|
||||
private boolean loggedIn = false;
|
||||
private boolean showOnline = true;
|
||||
|
||||
public StreamrayFavoritesService(Streamray site) {
|
||||
super(site);
|
||||
}
|
||||
|
@ -22,20 +40,120 @@ public class StreamrayFavoritesService extends AbstractStreamrayUpdateService {
|
|||
return new Task<>() {
|
||||
@Override
|
||||
public List<Model> call() throws IOException {
|
||||
return getModelList().stream()
|
||||
.skip((page - 1) * (long) modelsPerPage)
|
||||
.limit(modelsPerPage)
|
||||
.map(Model.class::cast)
|
||||
.toList();
|
||||
if (showOnline) {
|
||||
return getModelList().stream()
|
||||
.skip((page - 1) * (long) MODELS_PER_PAGE)
|
||||
.limit(MODELS_PER_PAGE)
|
||||
.map(Model.class::cast)
|
||||
.toList();
|
||||
} else {
|
||||
return loadOfflineModelList().stream()
|
||||
.map(Model.class::cast)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private List<StreamrayModel> getModelList() throws IOException {
|
||||
List<StreamrayModel> models = site.loadModelList(true).stream().filter(StreamrayModel::isFavorite).toList();
|
||||
if (models == null) {
|
||||
models = Collections.emptyList();
|
||||
if (Duration.between(lastListInfoRequest, Instant.now()).getSeconds() < 30) {
|
||||
return Objects.nonNull(modelsList) ? modelsList : loadModelList(API_URL);
|
||||
}
|
||||
return models;
|
||||
modelsList = loadModelList(API_URL);
|
||||
return modelsList;
|
||||
}
|
||||
|
||||
private List<StreamrayModel> loadOfflineModelList() throws IOException {
|
||||
String url = "https://beta-api.cams.com/favorites/member_favorites/?gender=female";
|
||||
int offset = (getPage() - 1) * MODELS_PER_PAGE;
|
||||
String paginatedUrl = url + "&offset=" + offset + "&limit=" + MODELS_PER_PAGE;
|
||||
return loadModelList(paginatedUrl);
|
||||
}
|
||||
|
||||
private List<StreamrayModel> loadModelList(String url) throws IOException {
|
||||
log.debug("Fetching page {}", url);
|
||||
lastListInfoRequest = Instant.now();
|
||||
StreamrayHttpClient client = (StreamrayHttpClient) site.getHttpClient();
|
||||
String token;
|
||||
if (site.login()) {
|
||||
loggedIn = true;
|
||||
token = client.getUserToken();
|
||||
} else {
|
||||
loggedIn = false;
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Request req = new Request.Builder()
|
||||
.url(url)
|
||||
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||
.header(ACCEPT, MIMETYPE_APPLICATION_JSON)
|
||||
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
|
||||
.header(REFERER, site.getBaseUrl() + "/")
|
||||
.header(ORIGIN, site.getBaseUrl())
|
||||
.header(X_REQUESTED_WITH, XML_HTTP_REQUEST)
|
||||
.header(AUTHORIZATION, "Bearer " + token)
|
||||
.build();
|
||||
try (Response response = client.execute(req)) {
|
||||
if (response.isSuccessful()) {
|
||||
List<StreamrayModel> models = new ArrayList<>();
|
||||
JSONObject json = new JSONObject(response.body().string());
|
||||
if (showOnline) {
|
||||
if (json.has("models")) {
|
||||
JSONArray modelNodes = json.getJSONArray("models");
|
||||
AbstractStreamrayUpdateService.setMapping(json.getJSONArray("mapping"));
|
||||
parseModels(modelNodes, models);
|
||||
}
|
||||
} else {
|
||||
if (json.has("results")) {
|
||||
JSONArray modelNodes = json.getJSONArray("results");
|
||||
parseOfflineModels(modelNodes, models);
|
||||
}
|
||||
}
|
||||
return models;
|
||||
} else {
|
||||
throw new HttpException(response.code(), response.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseModels(JSONArray jsonModels, List<StreamrayModel> models) {
|
||||
int nameIdx = mappingIndex("stream_name");
|
||||
int favIdx = mappingIndex("is_favorite");
|
||||
for (int i = 0; i < jsonModels.length(); i++) {
|
||||
JSONArray m = jsonModels.getJSONArray(i);
|
||||
String name = m.optString(nameIdx);
|
||||
boolean favorite = m.optBoolean(favIdx);
|
||||
if (favorite) {
|
||||
StreamrayModel model = site.createModel(name);
|
||||
String preview = getPreviewURL(name);
|
||||
model.setPreview(preview);
|
||||
model.setTags(createTags(m));
|
||||
StringBuilder description = new StringBuilder();
|
||||
for (String tag : model.getTags()) {
|
||||
description.append("#").append(tag).append(" ");
|
||||
}
|
||||
model.setDescription(description.toString());
|
||||
models.add(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseOfflineModels(JSONArray jsonModels, List<StreamrayModel> models) {
|
||||
for (int i = 0; i < jsonModels.length(); i++) {
|
||||
JSONObject m = jsonModels.getJSONObject(i);
|
||||
String name = m.optString("stream_name");
|
||||
if (StringUtil.isBlank(name) || m.optBoolean("is_online")) {
|
||||
continue;
|
||||
}
|
||||
StreamrayModel model = site.createModel(name);
|
||||
String preview = getPreviewURL(name);
|
||||
model.setPreview(preview);
|
||||
model.setDisplayName(m.getString("screen_name"));
|
||||
model.setOnlineState(Model.State.OFFLINE);
|
||||
models.add(model);
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnline(boolean online) {
|
||||
showOnline = online;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,11 @@ import javafx.geometry.Pos;
|
|||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.RadioButton;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
public class StreamrayFavoritesTab extends ThumbOverviewTab implements FollowedTab {
|
||||
private final Label status;
|
||||
|
@ -29,12 +32,38 @@ public class StreamrayFavoritesTab extends ThumbOverviewTab implements FollowedT
|
|||
loginButton.setOnAction(e -> {
|
||||
try {
|
||||
new StreamrayElectronLoginDialog(site.getHttpClient().getCookieJar());
|
||||
queue.clear();
|
||||
updateService.restart();
|
||||
} catch (Exception ex) {
|
||||
// fail silently
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createGui() {
|
||||
super.createGui();
|
||||
addOnlineOfflineSelector();
|
||||
}
|
||||
|
||||
private void addOnlineOfflineSelector() {
|
||||
var group = new ToggleGroup();
|
||||
var online = new RadioButton("online");
|
||||
online.setToggleGroup(group);
|
||||
var offline = new RadioButton("offline");
|
||||
offline.setToggleGroup(group);
|
||||
pagination.getChildren().add(online);
|
||||
pagination.getChildren().add(offline);
|
||||
HBox.setMargin(online, new Insets(5, 5, 5, 40));
|
||||
HBox.setMargin(offline, new Insets(5, 5, 5, 5));
|
||||
online.setSelected(true);
|
||||
group.selectedToggleProperty().addListener(e -> {
|
||||
((StreamrayFavoritesService) updateService).setOnline(online.isSelected());
|
||||
queue.clear();
|
||||
updateService.restart();
|
||||
});
|
||||
}
|
||||
|
||||
protected void addLoginButton() {
|
||||
grid.getChildren().clear();
|
||||
grid.setAlignment(Pos.CENTER);
|
||||
|
@ -45,7 +74,7 @@ public class StreamrayFavoritesTab extends ThumbOverviewTab implements FollowedT
|
|||
protected void onSuccess() {
|
||||
grid.getChildren().removeAll(status, loginButton);
|
||||
grid.setAlignment(Pos.TOP_LEFT);
|
||||
if (!((AbstractStreamrayUpdateService) updateService).isLoggedIn()) {
|
||||
if (!streamrayFavoritesService.isLoggedIn()) {
|
||||
addLoginButton();
|
||||
} else {
|
||||
super.onSuccess();
|
||||
|
|
|
@ -1,22 +1,37 @@
|
|||
package ctbrec.ui.sites.streamray;
|
||||
|
||||
import ctbrec.Config;
|
||||
import ctbrec.Model;
|
||||
import ctbrec.io.HttpException;
|
||||
import ctbrec.sites.streamray.Streamray;
|
||||
import ctbrec.sites.streamray.StreamrayHttpClient;
|
||||
import ctbrec.sites.streamray.StreamrayModel;
|
||||
import javafx.concurrent.Task;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static ctbrec.io.HttpConstants.*;
|
||||
|
||||
@Slf4j
|
||||
public class StreamrayUpdateService extends AbstractStreamrayUpdateService {
|
||||
|
||||
private List<StreamrayModel> models;
|
||||
private List<StreamrayModel> modelsList;
|
||||
private Instant lastListInfoRequest = Instant.EPOCH;
|
||||
|
||||
@Setter
|
||||
|
@ -32,21 +47,78 @@ public class StreamrayUpdateService extends AbstractStreamrayUpdateService {
|
|||
return new Task<>() {
|
||||
@Override
|
||||
public List<Model> call() throws IOException {
|
||||
return getModels().stream()
|
||||
return getModelList().stream()
|
||||
.filter(filter)
|
||||
.skip((page - 1) * (long) modelsPerPage)
|
||||
.limit(modelsPerPage)
|
||||
.skip((page - 1) * (long) MODELS_PER_PAGE)
|
||||
.limit(MODELS_PER_PAGE)
|
||||
.map(Model.class::cast)
|
||||
.toList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private List<StreamrayModel> getModels() throws IOException {
|
||||
if (models == null || Duration.between(lastListInfoRequest, Instant.now()).getSeconds() >= 10) {
|
||||
models = site.loadModelList();
|
||||
lastListInfoRequest = Instant.now();
|
||||
private List<StreamrayModel> getModelList() throws IOException {
|
||||
if (Duration.between(lastListInfoRequest, Instant.now()).getSeconds() < 30) {
|
||||
return Objects.nonNull(modelsList) ? modelsList : loadModelList();
|
||||
}
|
||||
modelsList = loadModelList();
|
||||
return modelsList;
|
||||
}
|
||||
|
||||
private List<StreamrayModel> loadModelList() throws IOException {
|
||||
log.debug("Fetching page {}", API_URL);
|
||||
lastListInfoRequest = Instant.now();
|
||||
StreamrayHttpClient client = (StreamrayHttpClient) site.getHttpClient();
|
||||
Request req = new Request.Builder()
|
||||
.url(API_URL)
|
||||
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||
.header(ACCEPT, MIMETYPE_APPLICATION_JSON)
|
||||
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
|
||||
.header(REFERER, site.getBaseUrl() + "/")
|
||||
.header(ORIGIN, site.getBaseUrl())
|
||||
.header(X_REQUESTED_WITH, XML_HTTP_REQUEST)
|
||||
.build();
|
||||
try (Response response = client.execute(req)) {
|
||||
if (response.isSuccessful()) {
|
||||
List<StreamrayModel> models = new ArrayList<>();
|
||||
String content = response.body().string();
|
||||
JSONObject json = new JSONObject(content);
|
||||
JSONArray modelNodes = json.getJSONArray("models");
|
||||
AbstractStreamrayUpdateService.setMapping(json.getJSONArray("mapping"));
|
||||
parseModels(modelNodes, models);
|
||||
return models;
|
||||
} else {
|
||||
throw new HttpException(response.code(), response.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseModels(JSONArray jsonModels, List<StreamrayModel> models) {
|
||||
int nameIdx = mappingIndex("stream_name");
|
||||
int dateIdx = mappingIndex("create_date");
|
||||
int genIdx = mappingIndex("gender");
|
||||
for (var i = 0; i < jsonModels.length(); i++) {
|
||||
var m = jsonModels.getJSONArray(i);
|
||||
String name = m.optString(nameIdx);
|
||||
String gender = m.optString(genIdx);
|
||||
String reg = m.optString(dateIdx);
|
||||
StreamrayModel model = site.createModel(name);
|
||||
try {
|
||||
LocalDate regDate = LocalDate.parse(reg, DateTimeFormatter.BASIC_ISO_DATE);
|
||||
model.setRegDate(regDate);
|
||||
} catch (DateTimeParseException e) {
|
||||
model.setRegDate(LocalDate.EPOCH);
|
||||
}
|
||||
String preview = getPreviewURL(name);
|
||||
model.setPreview(preview);
|
||||
model.setGender(gender);
|
||||
model.setTags(createTags(m));
|
||||
StringBuilder description = new StringBuilder();
|
||||
for (String tag : model.getTags()) {
|
||||
description.append("#").append(tag).append(" ");
|
||||
}
|
||||
model.setDescription(description.toString());
|
||||
models.add(model);
|
||||
}
|
||||
return models;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
package ctbrec.ui.tabs;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ctbrec.Model;
|
||||
import javafx.concurrent.ScheduledService;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public abstract class PaginatedScheduledService extends ScheduledService<List<Model>> {
|
||||
|
||||
protected int page = 1;
|
||||
|
||||
public int getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
public void setPage(int page) {
|
||||
this.page = page;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue