Code cleanup

This commit is contained in:
0xb00bface 2023-11-04 21:58:32 +01:00
parent 541fcf5bc7
commit 03dd723fb6
14 changed files with 247 additions and 374 deletions

View File

@ -0,0 +1,18 @@
package ctbrec.ui.sites.streamray;
import ctbrec.sites.streamray.Streamray;
import ctbrec.ui.tabs.PaginatedScheduledService;
abstract class AbstractStreamrayUpdateService extends PaginatedScheduledService {
protected int modelsPerPage = 48;
protected final Streamray site;
AbstractStreamrayUpdateService(Streamray site) {
this.site = site;
}
boolean isLoggedIn() {
return site.isLoggedIn();
}
}

View File

@ -1,37 +1,31 @@
package ctbrec.ui.sites.streamray;
import ctbrec.Config;
import ctbrec.sites.streamray.Streamray;
import ctbrec.ui.ExternalBrowser;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Cookie;
import okhttp3.Cookie.Builder;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import org.json.JSONObject;
import java.io.IOException;
import java.util.Collections;
import java.util.function.Consumer;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ctbrec.Config;
import ctbrec.sites.streamray.Streamray;
import ctbrec.ui.ExternalBrowser;
import okhttp3.Cookie;
import okhttp3.Cookie.Builder;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
@Slf4j
public class StreamrayElectronLoginDialog {
private static final Logger LOG = LoggerFactory.getLogger(StreamrayElectronLoginDialog.class);
public static final String DOMAIN = "streamray.com";
public static final String URL = "https://streamray.com/";
private CookieJar cookieJar;
private ExternalBrowser browser;
private boolean firstCall = true;
private final static Streamray site = new Streamray();
public StreamrayElectronLoginDialog(CookieJar cookieJar) throws IOException {
this.cookieJar = cookieJar;
browser = ExternalBrowser.getInstance();
try {
try (ExternalBrowser browser = ExternalBrowser.getInstance()) {
var config = new JSONObject();
config.put("url", URL);
config.put("url", Streamray.BASE_URI);
config.put("w", 800);
config.put("h", 600);
config.put("userAgent", Config.getInstance().getSettings().httpUserAgent);
@ -41,23 +35,17 @@ public class StreamrayElectronLoginDialog {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Couldn't wait for login dialog", e);
} finally {
browser.close();
}
}
private Consumer<String> msgHandler = line -> {
private final Consumer<String> msgHandler = line -> {
if (!line.startsWith("{")) return;
JSONObject json = new JSONObject(line);
boolean loginCookie = false;
if (json.has("cookies")) {
var cookies = json.getJSONArray("cookies");
for (var i = 0; i < cookies.length(); i++) {
var cookie = cookies.getJSONObject(i);
if (cookie.getString("domain").contains(DOMAIN)) {
if (cookie.optString("name").equals("memberToken")) {
loginCookie = true;
}
Builder b = new Cookie.Builder()
.path(cookie.getString("path"))
.domain(DOMAIN)
@ -74,8 +62,8 @@ public class StreamrayElectronLoginDialog {
b.secure();
}
Cookie c = b.build();
LOG.trace("Adding cookie {}={}", c.name(), c.value());
cookieJar.saveFromResponse(HttpUrl.parse(URL), Collections.singletonList(c));
log.trace("Adding cookie {}={}", c.name(), c.value());
cookieJar.saveFromResponse(HttpUrl.parse(Streamray.BASE_URI), Collections.singletonList(c));
} // if
} // for
}

View File

@ -1,132 +1,41 @@
package ctbrec.ui.sites.streamray;
import static ctbrec.io.HttpConstants.*;
import ctbrec.Config;
import ctbrec.Model;
import ctbrec.StringUtil;
import ctbrec.io.HttpException;
import ctbrec.sites.streamray.*;
import ctbrec.ui.SiteUiFactory;
import ctbrec.ui.tabs.PaginatedScheduledService;
import ctbrec.sites.streamray.Streamray;
import ctbrec.sites.streamray.StreamrayModel;
import javafx.concurrent.Task;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javafx.concurrent.Task;
import okhttp3.*;
import org.json.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StreamrayFavoritesService extends PaginatedScheduledService {
private static final Logger LOG = LoggerFactory.getLogger(StreamrayFavoritesService.class);
private static final String API_URL = "https://beta-api.cams.com/won/compressed/";
private Streamray site;
private static List<StreamrayModel> modelsList;
private static JSONArray mapping;
protected int modelsPerPage = 48;
public boolean loggedIn = false;
@Slf4j
public class StreamrayFavoritesService extends AbstractStreamrayUpdateService {
public StreamrayFavoritesService(Streamray site) {
this.site = site;
super(site);
}
@Override
protected Task<List<Model>> createTask() {
return new Task<List<Model>>() {
return new Task<>() {
@Override
public List<Model> call() throws IOException {
return getModelList().stream()
.skip((page - 1) * (long) modelsPerPage)
.limit(modelsPerPage)
.collect(Collectors.toList()); // NOSONAR
.map(Model.class::cast)
.toList();
}
};
}
private List<StreamrayModel> getModelList() throws IOException {
modelsList = loadModelList();
if (modelsList == null) {
modelsList = Collections.emptyList();
List<StreamrayModel> models = site.loadModelList(true).stream().filter(StreamrayModel::isFavorite).toList();
if (models == null) {
models = Collections.emptyList();
}
return modelsList;
}
private List<StreamrayModel> loadModelList() throws IOException {
LOG.debug("Fetching page {}", API_URL);
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(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)
.header(AUTHORIZATION, "Bearer " + token)
.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");
mapping = 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 name_idx = mapping_index("stream_name");
int fav_idx = mapping_index("is_favorite");
for (int i = 0; i < jsonModels.length(); i++) {
JSONArray m = jsonModels.getJSONArray(i);
String name = m.optString(name_idx);
boolean favorite = m.optBoolean(fav_idx);
if (favorite) {
StreamrayModel model = (StreamrayModel) site.createModel(name);
String preview = getPreviewURL(name);
model.setPreview(preview);
models.add(model);
}
}
}
private 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;
}
}
private int mapping_index(String s) {
for (var i = 0; i < mapping.length(); i++) {
if (Objects.equals(s, mapping.get(i))) return i;
}
return -1;
return models;
}
}

View File

@ -4,37 +4,34 @@ import ctbrec.sites.streamray.Streamray;
import ctbrec.ui.tabs.FollowedTab;
import ctbrec.ui.tabs.ThumbOverviewTab;
import javafx.concurrent.WorkerStateEvent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
public class StreamrayFavoritesTab extends ThumbOverviewTab implements FollowedTab {
private static final Logger LOG = LoggerFactory.getLogger(StreamrayFavoritesTab.class);
private Label status;
private Button loginButton;
private Streamray site;
private StreamrayFavoritesService updateService;
private final Label status;
private final Button loginButton;
private final StreamrayFavoritesService streamrayFavoritesService;
public StreamrayFavoritesTab(String title, StreamrayFavoritesService updateService, Streamray site) {
super(title, updateService, site);
this.site = site;
this.updateService = updateService;
this.streamrayFavoritesService = updateService;
status = new Label("Logging in...");
grid.getChildren().addAll(status);
loginButton = new Button("Login");
loginButton.setPadding(new Insets(20));
loginButton.setOnAction(e -> {
try {
new StreamrayElectronLoginDialog(site.getHttpClient().getCookieJar());
updateService.restart();
} catch (Exception ex) {}
} catch (Exception ex) {
}
});
}
@ -48,8 +45,8 @@ public class StreamrayFavoritesTab extends ThumbOverviewTab implements FollowedT
protected void onSuccess() {
grid.getChildren().removeAll(status, loginButton);
grid.setAlignment(Pos.TOP_LEFT);
if (updateService.loggedIn == false) {
addLoginButton();
if (!((AbstractStreamrayUpdateService) updateService).isLoggedIn()) {
addLoginButton();
} else {
super.onSuccess();
}

View File

@ -37,8 +37,4 @@ public class StreamraySiteUi extends AbstractSiteUi {
public boolean login() throws IOException {
return site.login();
}
public synchronized boolean checkLogin() throws IOException {
return site.login();
}
}

View File

@ -2,7 +2,6 @@ package ctbrec.ui.sites.streamray;
import ctbrec.sites.streamray.Streamray;
import ctbrec.sites.streamray.StreamrayModel;
import ctbrec.ui.sites.AbstractTabProvider;
import ctbrec.ui.tabs.ThumbOverviewTab;
import javafx.scene.Scene;
@ -25,7 +24,7 @@ public class StreamrayTabProvider extends AbstractTabProvider {
tabs.add(createTab("Girls", m -> Objects.equals("F", m.getGender())));
tabs.add(createTab("Boys", m -> Objects.equals("M", m.getGender())));
tabs.add(createTab("Trans", m -> Objects.equals("TS", m.getGender())));
tabs.add(createTab("New", m -> m.isNew()));
tabs.add(createTab("New", StreamrayModel::isNew));
tabs.add(favoritesTab());
return tabs;
}

View File

@ -1,146 +1,52 @@
package ctbrec.ui.sites.streamray;
import static ctbrec.io.HttpConstants.*;
import ctbrec.Config;
import ctbrec.Model;
import ctbrec.StringUtil;
import ctbrec.io.HttpException;
import ctbrec.sites.streamray.*;
import ctbrec.ui.SiteUiFactory;
import ctbrec.ui.tabs.PaginatedScheduledService;
import ctbrec.sites.streamray.Streamray;
import ctbrec.sites.streamray.StreamrayModel;
import javafx.concurrent.Task;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.MessageFormat;
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.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javafx.concurrent.Task;
import okhttp3.Request;
import okhttp3.Response;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StreamrayUpdateService extends PaginatedScheduledService {
@Slf4j
public class StreamrayUpdateService extends AbstractStreamrayUpdateService {
private static final Logger LOG = LoggerFactory.getLogger(StreamrayUpdateService.class);
private static final String API_URL = "https://beta-api.cams.com/won/compressed/";
private List<StreamrayModel> models;
private Instant lastListInfoRequest = Instant.EPOCH;
private Streamray site;
private static List<StreamrayModel> modelsList;
private static Instant lastListInfoRequest = Instant.EPOCH;
private static JSONArray mapping;
protected int modelsPerPage = 48;
@Setter
protected Predicate<StreamrayModel> filter;
public StreamrayUpdateService(Streamray site, Predicate<StreamrayModel> filter) {
this.site = site;
super(site);
this.filter = filter;
}
@Override
protected Task<List<Model>> createTask() {
return new Task<List<Model>>() {
return new Task<>() {
@Override
public List<Model> call() throws IOException {
return getModelList().stream()
return getModels().stream()
.filter(filter)
.skip((page - 1) * (long) modelsPerPage)
.limit(modelsPerPage)
.collect(Collectors.toList()); // NOSONAR
.map(Model.class::cast)
.toList();
}
};
}
private List<StreamrayModel> getModelList() throws IOException {
if (Duration.between(lastListInfoRequest, Instant.now()).getSeconds() < 30) {
return Optional.ofNullable(modelsList).orElse(loadModelList());
private List<StreamrayModel> getModels() throws IOException {
if (models == null || Duration.between(lastListInfoRequest, Instant.now()).getSeconds() >= 10) {
models = site.loadModelList();
lastListInfoRequest = Instant.now();
}
modelsList = loadModelList();
return Optional.ofNullable(modelsList).orElse(Collections.emptyList());
}
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");
mapping = 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 name_idx = mapping_index("stream_name");
int date_idx = mapping_index("create_date");
int gen_idx = mapping_index("gender");
for (var i = 0; i < jsonModels.length(); i++) {
var m = jsonModels.getJSONArray(i);
String name = m.optString(name_idx);
String gender = m.optString(gen_idx);
String reg = m.optString(date_idx);
StreamrayModel model = (StreamrayModel) 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);
models.add(model);
}
}
private 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;
}
}
public void setFilter(Predicate<StreamrayModel> filter) {
this.filter = filter;
}
private int mapping_index(String s) {
for (var i = 0; i < mapping.length(); i++) {
if (Objects.equals(s, mapping.get(i))) return i;
}
return -1;
return models;
}
}

View File

@ -32,7 +32,9 @@ public interface Site {
HttpClient getHttpClient();
void init() throws IOException;
default void init() throws IOException {
// do nothing per default
}
void shutdown();

View File

@ -32,7 +32,7 @@ import static ctbrec.io.HttpConstants.*;
public class AmateurTvModel extends AbstractModel {
private static final Logger LOG = LoggerFactory.getLogger(AmateurTvModel.class);
private JSONArray qualities = new JSONArray();
private transient JSONArray qualities = new JSONArray();
private int[] resolution = new int[2];
@Override
@ -55,9 +55,7 @@ public class AmateurTvModel extends AbstractModel {
@Override
public State getOnlineState(boolean failFast) throws IOException, ExecutionException {
if (failFast && onlineState != UNKNOWN) {
return onlineState;
} else {
if (!failFast || onlineState == UNKNOWN) {
try {
onlineState = isOnline(true) ? ONLINE : OFFLINE;
} catch (InterruptedException e) {
@ -66,8 +64,8 @@ public class AmateurTvModel extends AbstractModel {
} catch (IOException | ExecutionException e) {
onlineState = OFFLINE;
}
return onlineState;
}
return onlineState;
}
@Override

View File

@ -31,7 +31,7 @@ import static ctbrec.Model.State.*;
import static ctbrec.io.HttpConstants.*;
import static java.nio.charset.StandardCharsets.UTF_8;
public class ChaturbateModel extends AbstractModel { // NOSONAR
public class ChaturbateModel extends AbstractModel {
private static final String PUBLIC = "public";
private static final Logger LOG = LoggerFactory.getLogger(ChaturbateModel.class);

View File

@ -108,11 +108,6 @@ public class CherryTvModel extends AbstractModel {
return onlineState;
}
@Override
public void setOnlineState(State onlineState) {
this.onlineState = onlineState;
}
@Override
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException {
try {

View File

@ -1,40 +1,43 @@
package ctbrec.sites.streamray;
import ctbrec.Config;
import ctbrec.Model;
import ctbrec.Model.State;
import ctbrec.StringUtil;
import ctbrec.io.HttpClient;
import ctbrec.io.HttpException;
import ctbrec.sites.AbstractSite;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Request;
import okhttp3.Response;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static ctbrec.io.HttpConstants.USER_AGENT;
import static ctbrec.Model.State.*;
import static ctbrec.io.HttpConstants.*;
import static java.nio.charset.StandardCharsets.UTF_8;
@Slf4j
public class Streamray extends AbstractSite {
private static final Logger LOG = LoggerFactory.getLogger(Streamray.class);
public static final String BASE_URI = "https://streamray.com";
public static final String API_URL = "https://beta-api.cams.com";
@Getter
private boolean loggedIn = false;
private StreamrayHttpClient httpClient;
public static String domain = "streamray.com";
public static String baseUri = "https://streamray.com";
public static String apiURL = "https://beta-api.cams.com";
@Override
public void init() throws IOException {
}
@Override
public String getName() {
@ -43,11 +46,11 @@ public class Streamray extends AbstractSite {
@Override
public String getBaseUrl() {
return baseUri;
return BASE_URI;
}
public String getApiUrl() {
return apiURL;
return API_URL;
}
@Override
@ -72,9 +75,14 @@ public class Streamray extends AbstractSite {
@Override
public synchronized boolean login() throws IOException {
boolean result = getHttpClient().login();
LOG.debug("Streamray site login call result: {}", result);
return result;
if (!loggedIn) {
boolean result = getHttpClient().login();
log.debug("Streamray site login call result: {}", result);
loggedIn = result;
return result;
} else {
return loggedIn;
}
}
@Override
@ -107,17 +115,12 @@ public class Streamray extends AbstractSite {
return true;
}
@Override
public boolean searchRequiresLogin() {
return false;
}
@Override
public List<Model> search(String q) throws IOException, InterruptedException {
if (StringUtil.isBlank(q)) {
return Collections.emptyList();
}
String url = getApiUrl() + "/models/new/?limit=30&search=" + URLEncoder.encode(q, "utf-8") + "&order=is_online";
String url = getApiUrl() + "/models/new/?limit=30&search=" + URLEncoder.encode(q, UTF_8) + "&order=is_online";
Request req = new Request.Builder()
.url(url)
.header(USER_AGENT, getConfig().getSettings().httpUserAgent)
@ -128,15 +131,12 @@ public class Streamray extends AbstractSite {
if (json.has("results")) {
List<Model> models = new ArrayList<>();
JSONArray results = json.getJSONArray("results");
if (results.length() == 0) {
return Collections.emptyList();
}
for (int i = 0; i < results.length(); i++) {
JSONObject result = results.getJSONObject(i);
StreamrayModel model = createModel(result.getString("stream_name"));
String image = result.optString("profile_image");
if (StringUtil.isBlank(image)) {
image = model.getPreviewURL();
image = getPreviewURL(model.getName());
}
model.setPreview(image);
models.add(model);
@ -163,7 +163,7 @@ public class Streamray extends AbstractSite {
@Override
public Model createModelFromUrl(String url) {
Matcher m = Pattern.compile("https://(streamray|cams).com/([_a-zA-Z0-9]+)").matcher(url);
Matcher m = Pattern.compile("https://(streamray|cams).com/(\\w+)").matcher(url);
if (m.matches()) {
String modelName = m.group(2);
return createModel(modelName);
@ -176,4 +176,98 @@ public class Streamray extends AbstractSite {
public String getAffiliateLink() {
return getBaseUrl();
}
public List<StreamrayModel> loadModelList() throws IOException {
return loadModelList(false);
}
public List<StreamrayModel> loadModelList(boolean withLogin) throws IOException {
String url = API_URL + "/won/compressed/";
log.debug("Fetching page {}", url);
StreamrayHttpClient client = (StreamrayHttpClient) getHttpClient();
String token;
Request.Builder builder = new Request.Builder().url(url);
if (withLogin) {
if (login()) {
token = client.getUserToken();
builder.header(AUTHORIZATION, "Bearer " + token);
} else {
return Collections.emptyList();
}
}
builder.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.header(ACCEPT, MIMETYPE_APPLICATION_JSON)
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
.header(REFERER, getBaseUrl() + "/")
.header(ORIGIN, getBaseUrl())
.header(X_REQUESTED_WITH, XML_HTTP_REQUEST);
Request req = builder.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);
parseModels(json, models);
return models;
} else {
throw new HttpException(response.code(), response.message());
}
}
}
private void parseModels(JSONObject json, List<StreamrayModel> models) {
JSONArray mapping = json.getJSONArray("mapping");
JSONArray jsonModels = json.getJSONArray("models");
int nameIdx = indexOfProperty(mapping, "stream_name");
int dateIdx = indexOfProperty(mapping, "create_date");
int genIdx = indexOfProperty(mapping, "gender");
int chatTypeIdx = indexOfProperty(mapping, "chat_type");
int favIdx = indexOfProperty(mapping, "is_favorite");
for (var i = 0; i < jsonModels.length(); i++) {
var m = jsonModels.getJSONArray(i);
String name = m.optString(nameIdx);
StreamrayModel model = createModel(name);
try {
LocalDate regDate = LocalDate.parse(m.optString(dateIdx), DateTimeFormatter.BASIC_ISO_DATE);
model.setRegDate(regDate);
} catch (DateTimeParseException e) {
model.setRegDate(LocalDate.EPOCH);
}
model.setOnlineState(mapOnlineState(m.optString(chatTypeIdx)));
String preview = getPreviewURL(name);
model.setPreview(preview);
model.setGender(m.optString(genIdx));
model.setFavorite(m.optBoolean(favIdx));
models.add(model);
}
}
public State mapOnlineState(String status) {
boolean goalShows = Config.getInstance().getSettings().streamrayRecordGoalShows;
return switch (status) {
case "0" -> OFFLINE;
case "1" -> ONLINE;
case "6" -> goalShows ? ONLINE : PRIVATE;
case "2", "3", "4", "7", "10", "11", "12", "13", "14" -> PRIVATE;
default -> UNKNOWN;
};
}
private int indexOfProperty(JSONArray mapping, String key) {
for (var i = 0; i < mapping.length(); i++) {
if (Objects.equals(key, mapping.get(i))) return i;
}
return -1;
}
private 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;
}
}
}

View File

@ -39,26 +39,28 @@ public class StreamrayHttpClient extends HttpClient {
private void updateToken() {
Request req = new Request.Builder()
.url(Streamray.baseUri)
.url(Streamray.BASE_URI)
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.header(ACCEPT, "*/*")
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
.build();
try (Response response = execute(req)) {
// nothing to do, we just call the base URI to get the cookie
} catch (Exception ex) {
// fail silently
}
}
private boolean checkLoginSuccess() {
String token = getUserToken();
Request req = new Request.Builder()
.url(Streamray.apiURL + "/members/me/balance/")
.url(Streamray.API_URL + "/members/me/balance/")
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.header(ACCEPT, MIMETYPE_APPLICATION_JSON)
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
.header(AUTHORIZATION, "Bearer " + token)
.header(REFERER, Streamray.baseUri + "/")
.header(ORIGIN, Streamray.baseUri)
.header(REFERER, Streamray.BASE_URI + "/")
.header(ORIGIN, Streamray.BASE_URI)
.build();
try (Response response = execute(req)) {
if (response.isSuccessful()) {
@ -75,7 +77,7 @@ public class StreamrayHttpClient extends HttpClient {
public String getUserToken() {
try {
Cookie cookie = getCookieJar().getCookie(HttpUrl.parse(Streamray.baseUri), "memberToken");
Cookie cookie = getCookieJar().getCookie(HttpUrl.parse(Streamray.BASE_URI), "memberToken");
String token = cookie.value();
return token;
} catch (Exception e) {

View File

@ -8,15 +8,15 @@ import ctbrec.io.HttpException;
import ctbrec.recorder.download.RecordingProcess;
import ctbrec.recorder.download.StreamSource;
import ctbrec.recorder.download.hls.FfmpegHlsDownload;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Request;
import okhttp3.Response;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.time.Duration;
import java.time.Instant;
@ -24,21 +24,28 @@ import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import static ctbrec.Model.State.*;
import static ctbrec.io.HttpConstants.*;
import static java.nio.charset.StandardCharsets.UTF_8;
@Slf4j
public class StreamrayModel extends AbstractModel {
private static final Logger LOG = LoggerFactory.getLogger(StreamrayModel.class);
private String status = null;
private String gender = null;
private LocalDate regDate = LocalDate.EPOCH;
private JSONObject modelInfo;
@Getter
@Setter
private String gender = null;
@Setter
private LocalDate regDate = LocalDate.EPOCH;
@Getter
@Setter
private boolean favorite = false;
private transient JSONObject modelInfo;
private transient Instant lastInfoRequest = Instant.EPOCH;
@Override
@ -48,7 +55,7 @@ public class StreamrayModel extends AbstractModel {
JSONObject json = getModelInfo();
if (json.has("online")) {
status = json.optString("online");
mapOnlineState(status);
setOnlineState(((Streamray) site).mapOnlineState(status));
}
} catch (Exception e) {
setOnlineState(UNKNOWN);
@ -57,22 +64,9 @@ public class StreamrayModel extends AbstractModel {
return onlineState == ONLINE;
}
private void mapOnlineState(String status) {
boolean goalShows = Config.getInstance().getSettings().streamrayRecordGoalShows;
switch (status) {
case "0" -> setOnlineState(OFFLINE);
case "1" -> setOnlineState(ONLINE);
case "6" -> setOnlineState(goalShows ? ONLINE : PRIVATE);
case "2", "3", "4", "7", "10", "11", "12", "13", "14" -> setOnlineState(PRIVATE);
default -> setOnlineState(OFFLINE);
}
}
@Override
public State getOnlineState(boolean failFast) throws IOException, ExecutionException {
if (failFast && onlineState != UNKNOWN) {
return onlineState;
} else {
if (!failFast || onlineState == UNKNOWN) {
try {
onlineState = isOnline(true) ? ONLINE : OFFLINE;
} catch (InterruptedException e) {
@ -81,8 +75,8 @@ public class StreamrayModel extends AbstractModel {
} catch (IOException | ExecutionException e) {
onlineState = OFFLINE;
}
return onlineState;
}
return onlineState;
}
@Override
@ -97,7 +91,7 @@ public class StreamrayModel extends AbstractModel {
src.bandwidth = 0;
sources.add(src);
} catch (IOException e) {
LOG.error("Can not get stream sources for {}", getName());
log.error("Can not get stream sources for {}", getName());
}
return sources;
}
@ -115,9 +109,7 @@ public class StreamrayModel extends AbstractModel {
}
private JSONObject getModelInfo() throws IOException {
if (Duration.between(lastInfoRequest, Instant.now()).getSeconds() < 5) {
modelInfo = Optional.ofNullable(modelInfo).orElse(loadModelInfo());
} else {
if (modelInfo == null || Duration.between(lastInfoRequest, Instant.now()).getSeconds() < 5) {
modelInfo = loadModelInfo();
}
return modelInfo;
@ -134,24 +126,13 @@ public class StreamrayModel extends AbstractModel {
.build();
try (Response response = site.getHttpClient().execute(req)) {
if (response.isSuccessful()) {
JSONObject jsonResponse = new JSONObject(response.body().string());
return jsonResponse;
return new JSONObject(response.body().string());
} else {
throw new HttpException(response.code(), response.message());
}
}
}
public String getPreviewURL() {
String lname = getName().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:320::0/g:no/plain/{0}@jpg", URLEncoder.encode(url, UTF_8));
} catch (Exception ex) {
return url;
}
}
@Override
public RecordingProcess createDownload() {
return new FfmpegHlsDownload(getSite().getHttpClient());
@ -179,18 +160,6 @@ public class StreamrayModel extends AbstractModel {
modelInfo = null;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setRegDate(LocalDate reg) {
this.regDate = reg;
}
public boolean isNew() {
return ChronoUnit.DAYS.between(this.regDate, LocalDate.now()) < 30;
}