From 03dd723fb6ac9e57aa5d4c89bd3a0d3caba4fcea Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 4 Nov 2023 21:58:32 +0100 Subject: [PATCH] Code cleanup --- .../AbstractStreamrayUpdateService.java | 18 ++ .../StreamrayElectronLoginDialog.java | 46 ++---- .../streamray/StreamrayFavoritesService.java | 121 ++------------ .../streamray/StreamrayFavoritesTab.java | 33 ++-- .../ui/sites/streamray/StreamraySiteUi.java | 4 - .../sites/streamray/StreamrayTabProvider.java | 3 +- .../streamray/StreamrayUpdateService.java | 136 +++------------- common/src/main/java/ctbrec/sites/Site.java | 4 +- .../sites/amateurtv/AmateurTvModel.java | 8 +- .../sites/chaturbate/ChaturbateModel.java | 2 +- .../ctbrec/sites/cherrytv/CherryTvModel.java | 5 - .../ctbrec/sites/streamray/Streamray.java | 154 ++++++++++++++---- .../sites/streamray/StreamrayHttpClient.java | 12 +- .../sites/streamray/StreamrayModel.java | 75 +++------ 14 files changed, 247 insertions(+), 374 deletions(-) create mode 100644 client/src/main/java/ctbrec/ui/sites/streamray/AbstractStreamrayUpdateService.java diff --git a/client/src/main/java/ctbrec/ui/sites/streamray/AbstractStreamrayUpdateService.java b/client/src/main/java/ctbrec/ui/sites/streamray/AbstractStreamrayUpdateService.java new file mode 100644 index 00000000..907f82c9 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/sites/streamray/AbstractStreamrayUpdateService.java @@ -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(); + } +} diff --git a/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayElectronLoginDialog.java b/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayElectronLoginDialog.java index 2d727ee0..3d1a01c1 100644 --- a/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayElectronLoginDialog.java +++ b/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayElectronLoginDialog.java @@ -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 msgHandler = line -> { + private final Consumer 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 } diff --git a/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayFavoritesService.java b/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayFavoritesService.java index 64ab32aa..93b2087b 100644 --- a/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayFavoritesService.java +++ b/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayFavoritesService.java @@ -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 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> createTask() { - return new Task>() { + return new Task<>() { @Override public List call() throws IOException { return getModelList().stream() .skip((page - 1) * (long) modelsPerPage) .limit(modelsPerPage) - .collect(Collectors.toList()); // NOSONAR + .map(Model.class::cast) + .toList(); } }; } private List getModelList() throws IOException { - modelsList = loadModelList(); - if (modelsList == null) { - modelsList = Collections.emptyList(); + List models = site.loadModelList(true).stream().filter(StreamrayModel::isFavorite).toList(); + if (models == null) { + models = Collections.emptyList(); } - return modelsList; - } - - private List 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 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 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; } } diff --git a/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayFavoritesTab.java b/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayFavoritesTab.java index abb1f59f..dbe1449c 100644 --- a/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayFavoritesTab.java +++ b/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayFavoritesTab.java @@ -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(); } diff --git a/client/src/main/java/ctbrec/ui/sites/streamray/StreamraySiteUi.java b/client/src/main/java/ctbrec/ui/sites/streamray/StreamraySiteUi.java index ff3e25ee..549bbdf3 100644 --- a/client/src/main/java/ctbrec/ui/sites/streamray/StreamraySiteUi.java +++ b/client/src/main/java/ctbrec/ui/sites/streamray/StreamraySiteUi.java @@ -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(); - } } diff --git a/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayTabProvider.java b/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayTabProvider.java index 8f2d1b86..72529419 100644 --- a/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayTabProvider.java +++ b/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayTabProvider.java @@ -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; } diff --git a/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayUpdateService.java b/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayUpdateService.java index 347913b0..623dc213 100644 --- a/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayUpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/streamray/StreamrayUpdateService.java @@ -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 models; + private Instant lastListInfoRequest = Instant.EPOCH; - private Streamray site; - private static List modelsList; - private static Instant lastListInfoRequest = Instant.EPOCH; - private static JSONArray mapping; - protected int modelsPerPage = 48; + @Setter protected Predicate filter; public StreamrayUpdateService(Streamray site, Predicate filter) { - this.site = site; + super(site); this.filter = filter; } @Override protected Task> createTask() { - return new Task>() { + return new Task<>() { @Override public List 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 getModelList() throws IOException { - if (Duration.between(lastListInfoRequest, Instant.now()).getSeconds() < 30) { - return Optional.ofNullable(modelsList).orElse(loadModelList()); + private List 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 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 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 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 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; } } diff --git a/common/src/main/java/ctbrec/sites/Site.java b/common/src/main/java/ctbrec/sites/Site.java index 9b665824..1ff27c49 100644 --- a/common/src/main/java/ctbrec/sites/Site.java +++ b/common/src/main/java/ctbrec/sites/Site.java @@ -32,7 +32,9 @@ public interface Site { HttpClient getHttpClient(); - void init() throws IOException; + default void init() throws IOException { + // do nothing per default + } void shutdown(); diff --git a/common/src/main/java/ctbrec/sites/amateurtv/AmateurTvModel.java b/common/src/main/java/ctbrec/sites/amateurtv/AmateurTvModel.java index d8e80177..4cd452f0 100644 --- a/common/src/main/java/ctbrec/sites/amateurtv/AmateurTvModel.java +++ b/common/src/main/java/ctbrec/sites/amateurtv/AmateurTvModel.java @@ -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 diff --git a/common/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java b/common/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java index f13bcebb..8c8a605f 100644 --- a/common/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java +++ b/common/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java @@ -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); diff --git a/common/src/main/java/ctbrec/sites/cherrytv/CherryTvModel.java b/common/src/main/java/ctbrec/sites/cherrytv/CherryTvModel.java index 0b461ca0..63a0bf4d 100644 --- a/common/src/main/java/ctbrec/sites/cherrytv/CherryTvModel.java +++ b/common/src/main/java/ctbrec/sites/cherrytv/CherryTvModel.java @@ -108,11 +108,6 @@ public class CherryTvModel extends AbstractModel { return onlineState; } - @Override - public void setOnlineState(State onlineState) { - this.onlineState = onlineState; - } - @Override public List getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException { try { diff --git a/common/src/main/java/ctbrec/sites/streamray/Streamray.java b/common/src/main/java/ctbrec/sites/streamray/Streamray.java index 6919aed1..beec51cc 100644 --- a/common/src/main/java/ctbrec/sites/streamray/Streamray.java +++ b/common/src/main/java/ctbrec/sites/streamray/Streamray.java @@ -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 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 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 loadModelList() throws IOException { + return loadModelList(false); + } + + public List 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 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 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; + } + } } diff --git a/common/src/main/java/ctbrec/sites/streamray/StreamrayHttpClient.java b/common/src/main/java/ctbrec/sites/streamray/StreamrayHttpClient.java index 0073c76f..988a9918 100644 --- a/common/src/main/java/ctbrec/sites/streamray/StreamrayHttpClient.java +++ b/common/src/main/java/ctbrec/sites/streamray/StreamrayHttpClient.java @@ -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) { diff --git a/common/src/main/java/ctbrec/sites/streamray/StreamrayModel.java b/common/src/main/java/ctbrec/sites/streamray/StreamrayModel.java index 3379f879..6535c80e 100644 --- a/common/src/main/java/ctbrec/sites/streamray/StreamrayModel.java +++ b/common/src/main/java/ctbrec/sites/streamray/StreamrayModel.java @@ -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; }