forked from j62/ctbrec
Add online/offline switch on followed tab for Stripchat
This commit is contained in:
parent
b07a478d03
commit
c376f30c56
|
@ -4,14 +4,17 @@ import ctbrec.sites.stripchat.Stripchat;
|
||||||
import ctbrec.ui.tabs.FollowedTab;
|
import ctbrec.ui.tabs.FollowedTab;
|
||||||
import ctbrec.ui.tabs.ThumbOverviewTab;
|
import ctbrec.ui.tabs.ThumbOverviewTab;
|
||||||
import javafx.concurrent.WorkerStateEvent;
|
import javafx.concurrent.WorkerStateEvent;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.RadioButton;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
|
||||||
public class StripchatFollowedTab extends ThumbOverviewTab implements FollowedTab {
|
public class StripchatFollowedTab extends ThumbOverviewTab implements FollowedTab {
|
||||||
private Label status;
|
private Label status;
|
||||||
boolean showOnline = true;
|
|
||||||
|
|
||||||
public StripchatFollowedTab(String title, Stripchat stripchat) {
|
public StripchatFollowedTab(String title, Stripchat stripchat) {
|
||||||
super(title, new StripchatFollowedUpdateService(stripchat), stripchat);
|
super(title, new StripchatFollowedUpdateService(stripchat), stripchat);
|
||||||
|
@ -19,6 +22,34 @@ public class StripchatFollowedTab extends ThumbOverviewTab implements FollowedTa
|
||||||
grid.getChildren().add(status);
|
grid.getChildren().add(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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 -> {
|
||||||
|
if (online.isSelected()) {
|
||||||
|
((StripchatFollowedUpdateService) updateService).setOnline(true);
|
||||||
|
} else {
|
||||||
|
((StripchatFollowedUpdateService) updateService).setOnline(false);
|
||||||
|
}
|
||||||
|
queue.clear();
|
||||||
|
updateService.restart();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSuccess() {
|
protected void onSuccess() {
|
||||||
grid.getChildren().remove(status);
|
grid.getChildren().remove(status);
|
||||||
|
|
|
@ -8,27 +8,30 @@ import ctbrec.sites.stripchat.StripchatHttpClient;
|
||||||
import ctbrec.sites.stripchat.StripchatModel;
|
import ctbrec.sites.stripchat.StripchatModel;
|
||||||
import ctbrec.ui.SiteUiFactory;
|
import ctbrec.ui.SiteUiFactory;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import okhttp3.HttpUrl;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.time.Instant;
|
import java.util.*;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static ctbrec.Model.State.*;
|
|
||||||
import static ctbrec.io.HttpConstants.*;
|
import static ctbrec.io.HttpConstants.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class StripchatFollowedUpdateService extends AbstractStripchatUpdateService {
|
public class StripchatFollowedUpdateService extends AbstractStripchatUpdateService {
|
||||||
private static final int PAGE_SIZE = 48;
|
|
||||||
private static final String FAVORITES = "/favorites";
|
private static final Random RNG = new Random();
|
||||||
|
private static final int MODELS_PER_PAGE = 60;
|
||||||
private final Stripchat stripchat;
|
private final Stripchat stripchat;
|
||||||
|
private final boolean loginRequired;
|
||||||
|
private String url;
|
||||||
|
|
||||||
public StripchatFollowedUpdateService(Stripchat stripchat) {
|
public StripchatFollowedUpdateService(Stripchat stripchat) {
|
||||||
this.stripchat = stripchat;
|
this.stripchat = stripchat;
|
||||||
|
this.loginRequired = true;
|
||||||
|
this.url = stripchat.getBaseUrl() + "/api/front/models/favorites?sortBy=lastAdded";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -36,97 +39,80 @@ public class StripchatFollowedUpdateService extends AbstractStripchatUpdateServi
|
||||||
return new Task<>() {
|
return new Task<>() {
|
||||||
@Override
|
@Override
|
||||||
public List<Model> call() throws IOException {
|
public List<Model> call() throws IOException {
|
||||||
return loadModels();
|
if (loginRequired && !stripchat.credentialsAvailable()) {
|
||||||
}
|
return Collections.emptyList();
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Model> loadModels() throws IOException {
|
|
||||||
int startIndex = (getPage() - 1) * PAGE_SIZE;
|
|
||||||
JSONArray favoriteModelIds = loadFavoriteModelIds();
|
|
||||||
List<Integer> modelIdsToLoad = new ArrayList<>(PAGE_SIZE);
|
|
||||||
List<Model> models;
|
|
||||||
if (startIndex < favoriteModelIds.length()) {
|
|
||||||
int modelsOnPage = Math.min(PAGE_SIZE, favoriteModelIds.length() - startIndex - 1);
|
|
||||||
for (var i = 0; i < modelsOnPage; i++) {
|
|
||||||
modelIdsToLoad.add(favoriteModelIds.getInt(startIndex + i));
|
|
||||||
}
|
|
||||||
models = loadModels(modelIdsToLoad);
|
|
||||||
} else {
|
} else {
|
||||||
models = Collections.emptyList();
|
int offset = (getPage() - 1) * MODELS_PER_PAGE;
|
||||||
}
|
int limit = MODELS_PER_PAGE;
|
||||||
return models;
|
String paginatedUrl = url + "&offset=" + offset + "&limit=" + limit + "&uniq=" + getUniq();
|
||||||
}
|
log.debug("Fetching page {}", paginatedUrl);
|
||||||
|
if (loginRequired) {
|
||||||
private List<Model> loadModels(List<Integer> modelIdsToLoad) throws IOException {
|
|
||||||
List<Model> models = new ArrayList<>();
|
|
||||||
var urlBuilder = HttpUrl.parse(stripchat.getBaseUrl() + "/api/front/models/list").newBuilder();
|
|
||||||
for (var i = 0; i < modelIdsToLoad.size(); i++) {
|
|
||||||
urlBuilder.addQueryParameter("modelIds[" + i + "]", modelIdsToLoad.get(i).toString());
|
|
||||||
}
|
|
||||||
var request = new Request.Builder()
|
|
||||||
.url(urlBuilder.build())
|
|
||||||
.header(ACCEPT, "*/*")
|
|
||||||
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
|
||||||
.header(REFERER, Stripchat.baseUri + FAVORITES)
|
|
||||||
.header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON)
|
|
||||||
.build();
|
|
||||||
try (var response = stripchat.getHttpClient().execute(request)) {
|
|
||||||
if (response.isSuccessful()) {
|
|
||||||
var json = new JSONObject(response.body().string());
|
|
||||||
if (json.has("models")) {
|
|
||||||
var users = json.getJSONArray("models");
|
|
||||||
for (var i = 0; i < users.length(); i++) {
|
|
||||||
var user = users.getJSONObject(i);
|
|
||||||
StripchatModel model = stripchat.createModel(user.optString("username"));
|
|
||||||
model.setDescription(user.optString("description"));
|
|
||||||
model.setPreview(getPreviewUrl(user));
|
|
||||||
model.setOnlineState(mapStatus(user.optString("status")));
|
|
||||||
model.setLastSeen(Instant.now());
|
|
||||||
models.add(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new HttpException(response.code(), response.message());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return models;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JSONArray loadFavoriteModelIds() throws IOException {
|
|
||||||
SiteUiFactory.getUi(stripchat).login();
|
SiteUiFactory.getUi(stripchat).login();
|
||||||
stripchat.getHttpClient().login();
|
|
||||||
long userId = ((StripchatHttpClient) stripchat.getHttpClient()).getUserId();
|
|
||||||
String url = stripchat.getBaseUrl() + "/api/front/users/" + userId + FAVORITES;
|
|
||||||
var request = new Request.Builder()
|
|
||||||
.url(url)
|
|
||||||
.header(ACCEPT, MIMETYPE_APPLICATION_JSON)
|
|
||||||
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
|
||||||
.header(ORIGIN, Stripchat.baseUri)
|
|
||||||
.header(REFERER, Stripchat.baseUri + FAVORITES)
|
|
||||||
.header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON)
|
|
||||||
.build();
|
|
||||||
try (var response = stripchat.getHttpClient().execute(request)) {
|
|
||||||
if (response.isSuccessful()) {
|
|
||||||
var json = new JSONObject(response.body().string());
|
|
||||||
if (json.has("modelIds")) {
|
|
||||||
var userIds = json.getJSONArray("modelIds");
|
|
||||||
return userIds;
|
|
||||||
} else {
|
|
||||||
return new JSONArray();
|
|
||||||
}
|
}
|
||||||
|
String jwtToken = ((StripchatHttpClient) stripchat.getHttpClient()).getJwtToken();
|
||||||
|
Request request = new Request.Builder()
|
||||||
|
.url(paginatedUrl)
|
||||||
|
.header(ACCEPT, MIMETYPE_APPLICATION_JSON)
|
||||||
|
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
|
||||||
|
.header(X_REQUESTED_WITH, XML_HTTP_REQUEST)
|
||||||
|
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||||
|
.header("Authorization", Optional.ofNullable(jwtToken).orElse(""))
|
||||||
|
.build();
|
||||||
|
try (Response response = stripchat.getHttpClient().execute(request)) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
List<Model> models = new ArrayList<>();
|
||||||
|
JSONObject json = new JSONObject(response.body().string());
|
||||||
|
if (json.has("totalCount") && offset >= json.getInt("totalCount")) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
if (json.has("models")) {
|
||||||
|
JSONArray jsonModels = json.getJSONArray("models");
|
||||||
|
models.addAll(parseModels(jsonModels));
|
||||||
|
} else {
|
||||||
|
log.debug("Response was not successful: {}", json);
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return models;
|
||||||
} else {
|
} else {
|
||||||
throw new HttpException(response.code(), response.message());
|
throw new HttpException(response.code(), response.message());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
protected ctbrec.Model.State mapStatus(String status) {
|
|
||||||
return switch (status) {
|
|
||||||
case "public" -> ONLINE;
|
|
||||||
case "idle" -> AWAY;
|
|
||||||
case "off" -> OFFLINE;
|
|
||||||
default -> UNKNOWN;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Model> parseModels(JSONArray jsonModels) {
|
||||||
|
List<Model> models = new ArrayList<>();
|
||||||
|
for (var i = 0; i < jsonModels.length(); i++) {
|
||||||
|
var jsonModel = jsonModels.getJSONObject(i);
|
||||||
|
try {
|
||||||
|
StripchatModel model = stripchat.createModel(jsonModel.getString("username"));
|
||||||
|
model.setPreview(getPreviewUrl(jsonModel));
|
||||||
|
model.setDisplayName(model.getName());
|
||||||
|
models.add(model);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Couldn't parse one of the models: {}", jsonModel, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return models;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnline(boolean online) {
|
||||||
|
if (online) {
|
||||||
|
url = stripchat.getBaseUrl() + "/api/front/models/favorites?sortBy=lastAdded";
|
||||||
|
} else {
|
||||||
|
url = stripchat.getBaseUrl() + "/api/front/models/favorites/offline?sortBy=lastAdded";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getUniq() {
|
||||||
|
String dict = "0123456789abcdefghijklmnopqarstvwxyz";
|
||||||
|
char[] text = new char[16];
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
text[i] = dict.charAt(RNG.nextInt(dict.length()));
|
||||||
|
}
|
||||||
|
return new String(text);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,37 @@
|
||||||
package ctbrec.sites.stripchat;
|
package ctbrec.sites.stripchat;
|
||||||
|
|
||||||
import static ctbrec.io.HttpConstants.*;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import ctbrec.Config;
|
import ctbrec.Config;
|
||||||
|
import ctbrec.StringUtil;
|
||||||
import ctbrec.io.HttpClient;
|
import ctbrec.io.HttpClient;
|
||||||
import ctbrec.io.HttpException;
|
import ctbrec.io.HttpException;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import okhttp3.MediaType;
|
import okhttp3.MediaType;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.RequestBody;
|
import okhttp3.RequestBody;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static ctbrec.io.HttpConstants.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class StripchatHttpClient extends HttpClient {
|
public class StripchatHttpClient extends HttpClient {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(StripchatHttpClient.class);
|
|
||||||
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
||||||
|
|
||||||
private long userId;
|
private long userId;
|
||||||
|
|
||||||
|
@Getter
|
||||||
private String csrfToken;
|
private String csrfToken;
|
||||||
|
@Getter
|
||||||
private String csrfTimestamp;
|
private String csrfTimestamp;
|
||||||
|
@Getter
|
||||||
private String csrfNotifyTimestamp;
|
private String csrfNotifyTimestamp;
|
||||||
|
@Getter
|
||||||
|
private String jwtToken;
|
||||||
|
|
||||||
public StripchatHttpClient(Config config) {
|
public StripchatHttpClient(Config config) {
|
||||||
super("stripchat", config);
|
super("stripchat", config);
|
||||||
|
@ -43,7 +49,7 @@ public class StripchatHttpClient extends HttpClient {
|
||||||
// persisted cookies might let us log in
|
// persisted cookies might let us log in
|
||||||
if (checkLoginSuccess()) {
|
if (checkLoginSuccess()) {
|
||||||
loggedIn = true;
|
loggedIn = true;
|
||||||
LOG.debug("Logged in with cookies");
|
log.debug("Logged in with cookies");
|
||||||
if (csrfToken == null) {
|
if (csrfToken == null) {
|
||||||
loadCsrfToken();
|
loadCsrfToken();
|
||||||
}
|
}
|
||||||
|
@ -83,14 +89,14 @@ public class StripchatHttpClient extends HttpClient {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.info("Auto-Login failed: {} {} {}", response.code(), response.message(), url);
|
log.info("Auto-Login failed: {} {} {}", response.code(), response.message(), url);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadCsrfToken() throws IOException {
|
private void loadCsrfToken() throws IOException {
|
||||||
String url = Stripchat.baseUri + "/api/front/v2/config/data?requestPath=%2F&timezoneOffset=0";
|
String url = Stripchat.baseUri + "/api/front/v2/config?requestPath=%2F&timezoneOffset=0";
|
||||||
Request request = new Request.Builder()
|
Request request = new Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
||||||
.header(ACCEPT, MIMETYPE_APPLICATION_JSON)
|
.header(ACCEPT, MIMETYPE_APPLICATION_JSON)
|
||||||
|
@ -106,6 +112,8 @@ public class StripchatHttpClient extends HttpClient {
|
||||||
csrfToken = data.optString("csrfToken");
|
csrfToken = data.optString("csrfToken");
|
||||||
csrfTimestamp = data.optString("csrfTimestamp");
|
csrfTimestamp = data.optString("csrfTimestamp");
|
||||||
csrfNotifyTimestamp = data.optString("csrfNotifyTimestamp");
|
csrfNotifyTimestamp = data.optString("csrfNotifyTimestamp");
|
||||||
|
JSONObject config = resp.getJSONObject("config");
|
||||||
|
jwtToken = config.optString("jwtToken");
|
||||||
} else {
|
} else {
|
||||||
throw new HttpException(response.code(), response.message());
|
throw new HttpException(response.code(), response.message());
|
||||||
}
|
}
|
||||||
|
@ -114,28 +122,18 @@ public class StripchatHttpClient extends HttpClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check, if the login worked
|
* check, if the login worked
|
||||||
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public boolean checkLoginSuccess() throws IOException {
|
public boolean checkLoginSuccess() throws IOException {
|
||||||
userId = getUserId();
|
try {
|
||||||
String url = Stripchat.baseUri + "/api/front/users/" + userId + "/favorites";
|
loadCsrfToken();
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url(url)
|
|
||||||
.header(ACCEPT, MIMETYPE_APPLICATION_JSON)
|
|
||||||
.header(USER_AGENT, config.getSettings().httpUserAgent)
|
|
||||||
.header(ORIGIN, Stripchat.baseUri)
|
|
||||||
.header(REFERER, Stripchat.baseUri + "/favorites")
|
|
||||||
.header(CONTENT_TYPE, MIMETYPE_APPLICATION_JSON)
|
|
||||||
.build();
|
|
||||||
try (Response response = execute(request)) {
|
|
||||||
if (response.isSuccessful()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.info("Login check returned unsuccessful: {}", e.getLocalizedMessage());
|
log.info("Login check returned unsuccessful: {}", e.getLocalizedMessage());
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return StringUtil.isNotBlank(jwtToken);
|
||||||
|
}
|
||||||
|
|
||||||
public long getUserId() throws JSONException, IOException {
|
public long getUserId() throws JSONException, IOException {
|
||||||
if (userId == 0) {
|
if (userId == 0) {
|
||||||
|
@ -160,16 +158,4 @@ public class StripchatHttpClient extends HttpClient {
|
||||||
}
|
}
|
||||||
return userId;
|
return userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCsrfNotifyTimestamp() {
|
|
||||||
return csrfNotifyTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCsrfTimestamp() {
|
|
||||||
return csrfTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCsrfToken() {
|
|
||||||
return csrfToken;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue