diff --git a/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminFollowedTab.java b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminFollowedTab.java index 50c65017..330b8cc9 100644 --- a/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminFollowedTab.java +++ b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminFollowedTab.java @@ -2,9 +2,13 @@ package ctbrec.ui.sites.jasmin; import ctbrec.sites.jasmin.LiveJasmin; import ctbrec.ui.tabs.FollowedTab; +import javafx.geometry.Insets; import javafx.scene.Scene; +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 LiveJasminFollowedTab extends LiveJasminTab implements FollowedTab { @@ -20,4 +24,28 @@ public class LiveJasminFollowedTab extends LiveJasminTab implements FollowedTab } }); } + + @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 -> { + ((LiveJasminFollowedUpdateService)updateService).setShowOnline(online.isSelected()); + queue.clear(); + updateService.restart(); + }); + } } diff --git a/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminFollowedUpdateService.java b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminFollowedUpdateService.java index c18ac97c..70ccb271 100644 --- a/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminFollowedUpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminFollowedUpdateService.java @@ -1,38 +1,42 @@ package ctbrec.ui.sites.jasmin; -import static ctbrec.io.HtmlParser.*; import static ctbrec.io.HttpConstants.*; import java.io.IOException; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import java.util.Locale; +import java.util.concurrent.Future; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; +import org.json.JSONArray; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Config; +import ctbrec.GlobalThreadPool; import ctbrec.Model; -import ctbrec.NotLoggedInExcetion; import ctbrec.io.HttpException; import ctbrec.sites.jasmin.LiveJasmin; import ctbrec.sites.jasmin.LiveJasminModel; import ctbrec.ui.SiteUiFactory; import ctbrec.ui.tabs.PaginatedScheduledService; import javafx.concurrent.Task; +import okhttp3.HttpUrl; import okhttp3.Request; +import okhttp3.Response; public class LiveJasminFollowedUpdateService extends PaginatedScheduledService { private static final Logger LOG = LoggerFactory.getLogger(LiveJasminFollowedUpdateService.class); private LiveJasmin liveJasmin; private String url; + private boolean showOnline = true; public LiveJasminFollowedUpdateService(LiveJasmin liveJasmin) { this.liveJasmin = liveJasmin; - this.url = liveJasmin.getBaseUrl() + "/en/member/favorite"; + this.url = liveJasmin.getBaseUrl() + "/en/free/favourite/get-favourite-list"; } @Override @@ -40,34 +44,64 @@ public class LiveJasminFollowedUpdateService extends PaginatedScheduledService { return new Task>() { @Override public List call() throws IOException { - if (!liveJasmin.credentialsAvailable()) { - throw new NotLoggedInExcetion("Credentials missing"); + if(!liveJasmin.credentialsAvailable()) { + throw new RuntimeException("Credentials missing"); } boolean loggedIn = SiteUiFactory.getUi(liveJasmin).login(); - if (!loggedIn) { - throw new NotLoggedInExcetion("Couldn't login to livejasmin"); + if(!loggedIn) { + throw new RuntimeException("Couldn't login to livejasmin"); } - LOG.debug("Fetching page {}", url); - var request = new Request.Builder() + Request request = new Request.Builder() .url(url) .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) .header(ACCEPT, "*/*") .header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage()) - .header(REFERER, liveJasmin.getBaseUrl()) + .header(REFERER, liveJasmin.getBaseUrl() + "/en/free/favorite") + .header(X_REQUESTED_WITH, XML_HTTP_REQUEST) .build(); - try (var response = liveJasmin.getHttpClient().execute(request)) { + try (Response response = liveJasmin.getHttpClient().execute(request)) { if (response.isSuccessful()) { - var body = response.body().string(); + String body = response.body().string(); List models = new ArrayList<>(); - Elements modelCells = getTags(body, "article[class~=perf_container]"); - for (Element modelCell : modelCells) { - String cellHtml = modelCell.html(); - String name = getText(cellHtml, "span[class~=performer_name_simple]").trim(); - LiveJasminModel model = (LiveJasminModel) liveJasmin.createModel(name); - model.setPreview(getTag(cellHtml, "img[class~=performer-image]").attr("data-src")); - model.setId(getTag(cellHtml, "span[class~=remove][class~=favorite]").attr("data-model-id")); - models.add(model); + JSONObject json = new JSONObject(body); + if (json.has("success")) { + JSONObject data = json.getJSONObject("data"); + JSONArray performers = data.getJSONArray("performers"); + List> loadDetailsFutures = new LinkedList<>(); + for (int i = 0; i < performers.length(); i++) { + JSONObject m = performers.getJSONObject(i); + String name = m.optString("pid"); + if (name.isEmpty()) { + continue; + } + + LiveJasminModel model = (LiveJasminModel) liveJasmin.createModel(name); + model.setId(m.getString("id")); + model.setDisplayName(m.getString("display_name")); + Model.State onlineState = LiveJasminModel.mapStatus(m.getInt("status")); + boolean online = onlineState == Model.State.ONLINE; + model.setOnlineState(onlineState); + if (online == showOnline) { + models.add(model); + } + loadDetailsFutures.add(GlobalThreadPool.submit(() -> { + loadModelDetails(model); + })); + } + for (Future future : loadDetailsFutures) { + try { + future.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + // details couldn't be loaded, but that doesn't matter + } + } + LOG.debug("done"); + } else { + LOG.error("Request failed:\n{}", body); + throw new IOException("Response was not successful"); } return models; } else { @@ -75,6 +109,35 @@ public class LiveJasminFollowedUpdateService extends PaginatedScheduledService { } } } + + private void loadModelDetails(LiveJasminModel model) { + try { + String sessionId = liveJasmin.getHttpClient().getCookieJar().getCookie(HttpUrl.parse(liveJasmin.getBaseUrl()), "session").value(); + String detailsUrl = liveJasmin.getBaseUrl() + "/en/member/flash/get-performer-details/" + model.getName() + "?appletType=html5&noFlash=0&session=" + sessionId; + Request request = new Request.Builder() + .url(detailsUrl) + .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) + .header(ACCEPT, "*/*") + .header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage()) + .header(REFERER, liveJasmin.getBaseUrl() + "/en/member/chat-html5/" + model.getName()) + .build(); + try (Response response = liveJasmin.getHttpClient().execute(request)) { + if (response.isSuccessful()) { + JSONObject json = new JSONObject(response.body().string()); + if (json.optBoolean("success")) { + JSONObject data = json.getJSONObject("data"); + model.setPreview(data.getString("profile_picture_url")); + } + } + } + } catch(IOException e) { + // details couldn't be loaded, but that doesn't matter + } + } }; } + + public void setShowOnline(boolean showOnline) { + this.showOnline = showOnline; + } }