From 54de1339fbaba5c4741ee56d2e2d0b73629a2b5b Mon Sep 17 00:00:00 2001 From: 0xboobface <0xboobface@gmail.com> Date: Wed, 17 Oct 2018 14:11:04 +0200 Subject: [PATCH] Add interfaces to abstract from camsite This step should enable the application to handle different camsites. At the moment only chaturbate is available, but others like MFC can now be added. There might be still some things to do, like the settings and HttpClient etc. But this is the first step to support more sites than only Chatubate. --- src/main/java/ctbrec/Model.java | 3 + src/main/java/ctbrec/Site.java | 13 +++ src/main/java/ctbrec/io/HttpClient.java | 8 +- .../java/ctbrec/recorder/LocalRecorder.java | 7 +- .../recorder/server/RecorderServlet.java | 5 +- .../ctbrec/sites/chaturbate/Chaturbate.java | 44 ++++++++++ .../sites/chaturbate/ChaturbateModel.java | 13 +-- ...Parser.java => ChaturbateModelParser.java} | 13 +-- .../chaturbate/ChaturbateTabProvider.java | 45 ++++++++++ .../chaturbate/ChaturbateUpdateService.java | 67 +++++++++++++++ ...pplication.java => CamrecApplication.java} | 41 ++++----- .../java/ctbrec/ui/DesktopIntergation.java | 2 +- src/main/java/ctbrec/ui/DonateTabFx.java | 2 +- src/main/java/ctbrec/ui/FollowedTab.java | 7 +- src/main/java/ctbrec/ui/JavaFxModel.java | 15 ++++ src/main/java/ctbrec/ui/Launcher.java | 4 +- .../ctbrec/ui/PaginatedScheduledService.java | 19 +++++ .../java/ctbrec/ui/RecordedModelsTab.java | 10 +-- src/main/java/ctbrec/ui/RecordingsTab.java | 11 +-- src/main/java/ctbrec/ui/SettingsTab.java | 2 +- src/main/java/ctbrec/ui/TabProvider.java | 14 ++++ src/main/java/ctbrec/ui/ThumbCell.java | 34 ++++---- src/main/java/ctbrec/ui/ThumbOverviewTab.java | 83 +++++-------------- src/main/java/ctbrec/ui/TipDialog.java | 4 +- src/main/java/ctbrec/ui/TokenLabel.java | 2 +- src/main/java/ctbrec/ui/UpdateTab.java | 2 +- 26 files changed, 326 insertions(+), 144 deletions(-) create mode 100644 src/main/java/ctbrec/Site.java create mode 100644 src/main/java/ctbrec/sites/chaturbate/Chaturbate.java rename src/main/java/ctbrec/sites/chaturbate/{ModelParser.java => ChaturbateModelParser.java} (80%) create mode 100644 src/main/java/ctbrec/sites/chaturbate/ChaturbateTabProvider.java create mode 100644 src/main/java/ctbrec/sites/chaturbate/ChaturbateUpdateService.java rename src/main/java/ctbrec/ui/{CtbrecApplication.java => CamrecApplication.java} (93%) create mode 100644 src/main/java/ctbrec/ui/PaginatedScheduledService.java create mode 100644 src/main/java/ctbrec/ui/TabProvider.java diff --git a/src/main/java/ctbrec/Model.java b/src/main/java/ctbrec/Model.java index 1034b41f..63869b78 100644 --- a/src/main/java/ctbrec/Model.java +++ b/src/main/java/ctbrec/Model.java @@ -27,5 +27,8 @@ public interface Model { public String getOnlineState(boolean failFast) throws IOException, ExecutionException; public List getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException; public String getSegmentPlaylistUrl() throws IOException, ExecutionException, ParseException, PlaylistException; + public void invalidateCacheEntries(); + public void receiveTip(int tokens) throws IOException; + public int[] getStreamResolution(boolean failFast) throws ExecutionException; } \ No newline at end of file diff --git a/src/main/java/ctbrec/Site.java b/src/main/java/ctbrec/Site.java new file mode 100644 index 00000000..5565f7ce --- /dev/null +++ b/src/main/java/ctbrec/Site.java @@ -0,0 +1,13 @@ +package ctbrec; + +import ctbrec.recorder.Recorder; +import ctbrec.ui.TabProvider; + +public interface Site { + public String getName(); + public String getBaseUrl(); + public String getAffiliateLink(); + public void setRecorder(Recorder recorder); + public TabProvider getTabProvider(); + public Model createModel(String name); +} diff --git a/src/main/java/ctbrec/io/HttpClient.java b/src/main/java/ctbrec/io/HttpClient.java index b2ac5e09..7a3dd0ca 100644 --- a/src/main/java/ctbrec/io/HttpClient.java +++ b/src/main/java/ctbrec/io/HttpClient.java @@ -10,7 +10,7 @@ import org.slf4j.LoggerFactory; import ctbrec.Config; import ctbrec.Settings.ProxyType; import ctbrec.ui.CookieJarImpl; -import ctbrec.ui.CtbrecApplication; +import ctbrec.ui.CamrecApplication; import ctbrec.ui.HtmlParser; import okhttp3.ConnectionPool; import okhttp3.Cookie; @@ -122,7 +122,7 @@ public class HttpClient { public boolean login() throws IOException { try { Request login = new Request.Builder() - .url(CtbrecApplication.BASE_URI + "/auth/login/") + .url(CamrecApplication.BASE_URI + "/auth/login/") .build(); Response response = client.newCall(login).execute(); String content = response.body().string(); @@ -136,8 +136,8 @@ public class HttpClient { .add("csrfmiddlewaretoken", token) .build(); login = new Request.Builder() - .url(CtbrecApplication.BASE_URI + "/auth/login/") - .header("Referer", CtbrecApplication.BASE_URI + "/auth/login/") + .url(CamrecApplication.BASE_URI + "/auth/login/") + .header("Referer", CamrecApplication.BASE_URI + "/auth/login/") .post(body) .build(); diff --git a/src/main/java/ctbrec/recorder/LocalRecorder.java b/src/main/java/ctbrec/recorder/LocalRecorder.java index 6cb03866..ff9bfeda 100644 --- a/src/main/java/ctbrec/recorder/LocalRecorder.java +++ b/src/main/java/ctbrec/recorder/LocalRecorder.java @@ -35,8 +35,7 @@ import ctbrec.recorder.PlaylistGenerator.InvalidPlaylistException; import ctbrec.recorder.download.Download; import ctbrec.recorder.download.HlsDownload; import ctbrec.recorder.download.MergedHlsDownload; -import ctbrec.sites.chaturbate.ChaturbateModel; -import ctbrec.sites.chaturbate.ModelParser; +import ctbrec.sites.chaturbate.ChaturbateModelParser; import okhttp3.Request; import okhttp3.Response; @@ -270,10 +269,10 @@ public class LocalRecorder implements Recorder { Request request = new Request.Builder().url(url).build(); Response response = client.execute(request, true); if (response.isSuccessful()) { - List followed = ModelParser.parseModels(response.body().string()); + List followed = ChaturbateModelParser.parseModels(response.body().string()); response.close(); followedModels.clear(); - for (ChaturbateModel model : followed) { + for (Model model : followed) { if (!followedModels.contains(model) && !models.contains(model)) { LOG.info("Model {} added", model); followedModels.add(model); diff --git a/src/main/java/ctbrec/recorder/server/RecorderServlet.java b/src/main/java/ctbrec/recorder/server/RecorderServlet.java index 58d4e9cf..bb491126 100644 --- a/src/main/java/ctbrec/recorder/server/RecorderServlet.java +++ b/src/main/java/ctbrec/recorder/server/RecorderServlet.java @@ -25,7 +25,6 @@ import ctbrec.Recording; import ctbrec.io.InstantJsonAdapter; import ctbrec.io.ModelJsonAdapter; import ctbrec.recorder.Recorder; -import ctbrec.sites.chaturbate.ChaturbateModel; public class RecorderServlet extends AbstractCtbrecServlet { @@ -74,7 +73,7 @@ public class RecorderServlet extends AbstractCtbrecServlet { break; case "list": resp.getWriter().write("{\"status\": \"success\", \"msg\": \"List of models\", \"models\": ["); - JsonAdapter modelAdapter = moshi.adapter(Model.class); + JsonAdapter modelAdapter = new ModelJsonAdapter(); List models = recorder.getModelsRecording(); for (Iterator iterator = models.iterator(); iterator.hasNext();) { Model model = iterator.next(); @@ -133,7 +132,7 @@ public class RecorderServlet extends AbstractCtbrecServlet { private static class Request { public String action; - public ChaturbateModel model; + public Model model; public String recording; } } diff --git a/src/main/java/ctbrec/sites/chaturbate/Chaturbate.java b/src/main/java/ctbrec/sites/chaturbate/Chaturbate.java new file mode 100644 index 00000000..e97887e4 --- /dev/null +++ b/src/main/java/ctbrec/sites/chaturbate/Chaturbate.java @@ -0,0 +1,44 @@ +package ctbrec.sites.chaturbate; + +import ctbrec.Model; +import ctbrec.Site; +import ctbrec.recorder.Recorder; +import ctbrec.ui.TabProvider; + +public class Chaturbate implements Site { + + private Recorder recorder; + + @Override + public String getName() { + return "Chaturbate"; + } + + @Override + public String getBaseUrl() { + return "https://chaturbate.com"; + } + + @Override + public String getAffiliateLink() { + return getBaseUrl() + "/in/?track=default&tour=LQps&campaign=55vTi&room=0xb00bface"; + } + + @Override + public TabProvider getTabProvider() { + return new ChaturbateTabProvider(this, recorder); + } + + @Override + public void setRecorder(Recorder recorder) { + this.recorder = recorder; + } + + @Override + public Model createModel(String name) { + ChaturbateModel m = new ChaturbateModel(); + m.setName(name); + m.setUrl(getBaseUrl() + '/' + name + '/'); + return m; + } +} diff --git a/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java b/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java index c4070143..311c9fbd 100644 --- a/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java +++ b/src/main/java/ctbrec/sites/chaturbate/ChaturbateModel.java @@ -55,23 +55,25 @@ public class ChaturbateModel extends AbstractModel { return Objects.equals("public", info.room_status); } + @Override public int[] getStreamResolution(boolean failFast) throws ExecutionException { int[] resolution = Chaturbate.INSTANCE.streamResolutionCache.getIfPresent(getName()); if(resolution != null) { return Chaturbate.INSTANCE.getResolution(getName()); } else { - return new int[2]; + if(failFast) { + return new int[2]; + } else { + return Chaturbate.INSTANCE.getResolution(getName()); + } } } - public int[] getStreamResolution() throws ExecutionException { - return Chaturbate.INSTANCE.getResolution(getName()); - } - /** * Invalidates the entries in StreamInfo and resolution cache for this model * and thus causes causes the LoadingCache to update them */ + @Override public void invalidateCacheEntries() { Chaturbate.INSTANCE.streamInfoCache.invalidate(getName()); Chaturbate.INSTANCE.streamResolutionCache.invalidate(getName()); @@ -94,6 +96,7 @@ public class ChaturbateModel extends AbstractModel { return Chaturbate.INSTANCE.getMasterPlaylist(getName()); } + @Override public void receiveTip(int tokens) throws IOException { Chaturbate.INSTANCE.sendTip(getName(), tokens); } diff --git a/src/main/java/ctbrec/sites/chaturbate/ModelParser.java b/src/main/java/ctbrec/sites/chaturbate/ChaturbateModelParser.java similarity index 80% rename from src/main/java/ctbrec/sites/chaturbate/ModelParser.java rename to src/main/java/ctbrec/sites/chaturbate/ChaturbateModelParser.java index f67726a0..a3fc0579 100644 --- a/src/main/java/ctbrec/sites/chaturbate/ModelParser.java +++ b/src/main/java/ctbrec/sites/chaturbate/ChaturbateModelParser.java @@ -1,6 +1,6 @@ package ctbrec.sites.chaturbate; -import static ctbrec.ui.CtbrecApplication.BASE_URI; +import static ctbrec.ui.CamrecApplication.BASE_URI; import java.util.ArrayList; import java.util.List; @@ -10,18 +10,19 @@ import org.jsoup.select.Elements; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import ctbrec.Model; import ctbrec.ui.HtmlParser; -public class ModelParser { - private static final transient Logger LOG = LoggerFactory.getLogger(ModelParser.class); +public class ChaturbateModelParser { + private static final transient Logger LOG = LoggerFactory.getLogger(ChaturbateModelParser.class); - public static List parseModels(String html) { - List models = new ArrayList<>(); + public static List parseModels(String html) { + List models = new ArrayList<>(); Elements cells = HtmlParser.getTags(html, "ul.list > li"); for (Element cell : cells) { String cellHtml = cell.html(); try { - ChaturbateModel model = new ChaturbateModel(); + Model model = new ChaturbateModel(); model.setName(HtmlParser.getText(cellHtml, "div.title > a").trim()); model.setPreview(HtmlParser.getTag(cellHtml, "a img").attr("src")); model.setUrl(BASE_URI + HtmlParser.getTag(cellHtml, "a").attr("href")); diff --git a/src/main/java/ctbrec/sites/chaturbate/ChaturbateTabProvider.java b/src/main/java/ctbrec/sites/chaturbate/ChaturbateTabProvider.java new file mode 100644 index 00000000..2aa5674e --- /dev/null +++ b/src/main/java/ctbrec/sites/chaturbate/ChaturbateTabProvider.java @@ -0,0 +1,45 @@ +package ctbrec.sites.chaturbate; + +import java.util.ArrayList; +import java.util.List; + +import ctbrec.recorder.Recorder; +import ctbrec.ui.FollowedTab; +import ctbrec.ui.TabProvider; +import ctbrec.ui.ThumbOverviewTab; +import javafx.scene.Scene; +import javafx.scene.control.Tab; + +public class ChaturbateTabProvider extends TabProvider { + + private Chaturbate chaturbate; + private Recorder recorder; + + public ChaturbateTabProvider(Chaturbate chaturbate, Recorder recorder) { + this.chaturbate = chaturbate; + this.recorder = recorder; + } + + @Override + public List getTabs(Scene scene) { + final String BASE_URI = chaturbate.getBaseUrl(); + List tabs = new ArrayList<>(); + tabs.add(createTab("Featured", BASE_URI + "/")); + tabs.add(createTab("Female", BASE_URI + "/female-cams/")); + tabs.add(createTab("Male", BASE_URI + "/male-cams/")); + tabs.add(createTab("Couples", BASE_URI + "/couple-cams/")); + tabs.add(createTab("Trans", BASE_URI + "/trans-cams/")); + FollowedTab followedTab = new FollowedTab("Followed", BASE_URI + "/followed-cams/"); + followedTab.setRecorder(recorder); + followedTab.setScene(scene); + tabs.add(followedTab); + return tabs; + } + + private Tab createTab(String title, String url) { + ChaturbateUpdateService updateService = new ChaturbateUpdateService(url, false); + ThumbOverviewTab tab = new ThumbOverviewTab(title, updateService); + tab.setRecorder(recorder); + return tab; + } +} diff --git a/src/main/java/ctbrec/sites/chaturbate/ChaturbateUpdateService.java b/src/main/java/ctbrec/sites/chaturbate/ChaturbateUpdateService.java new file mode 100644 index 00000000..b13d81f3 --- /dev/null +++ b/src/main/java/ctbrec/sites/chaturbate/ChaturbateUpdateService.java @@ -0,0 +1,67 @@ +package ctbrec.sites.chaturbate; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ctbrec.Model; +import ctbrec.io.HttpClient; +import ctbrec.ui.PaginatedScheduledService; +import javafx.concurrent.Task; +import okhttp3.Request; +import okhttp3.Response; + +public class ChaturbateUpdateService extends PaginatedScheduledService { + + private static final transient Logger LOG = LoggerFactory.getLogger(ChaturbateUpdateService.class); + private String url; + private boolean loginRequired; + + public ChaturbateUpdateService(String url, boolean loginRequired) { + this.url = url; + this.loginRequired = loginRequired; + + ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + t.setName("ThumbOverviewTab UpdateService"); + return t; + } + }); + setExecutor(executor); + } + + @Override + protected Task> createTask() { + return new Task>() { + @Override + public List call() throws IOException { + String url = ChaturbateUpdateService.this.url + "?page="+page+"&keywords=&_=" + System.currentTimeMillis(); + LOG.debug("Fetching page {}", url); + Request request = new Request.Builder().url(url).build(); + Response response = HttpClient.getInstance().execute(request, loginRequired); + if (response.isSuccessful()) { + List models = ChaturbateModelParser.parseModels(response.body().string()); + response.close(); + return models; + } else { + int code = response.code(); + response.close(); + throw new IOException("HTTP status " + code); + } + } + }; + } + + public void setUrl(String url) { + this.url = url; + } + +} diff --git a/src/main/java/ctbrec/ui/CtbrecApplication.java b/src/main/java/ctbrec/ui/CamrecApplication.java similarity index 93% rename from src/main/java/ctbrec/ui/CtbrecApplication.java rename to src/main/java/ctbrec/ui/CamrecApplication.java index a67ff856..3ffbe289 100644 --- a/src/main/java/ctbrec/ui/CtbrecApplication.java +++ b/src/main/java/ctbrec/ui/CamrecApplication.java @@ -20,11 +20,13 @@ import com.squareup.moshi.Moshi; import com.squareup.moshi.Types; import ctbrec.Config; +import ctbrec.Site; import ctbrec.Version; import ctbrec.io.HttpClient; import ctbrec.recorder.LocalRecorder; import ctbrec.recorder.Recorder; import ctbrec.recorder.RemoteRecorder; +import ctbrec.sites.chaturbate.Chaturbate; import javafx.application.Application; import javafx.application.HostServices; import javafx.application.Platform; @@ -49,9 +51,9 @@ import javafx.stage.Stage; import okhttp3.Request; import okhttp3.Response; -public class CtbrecApplication extends Application { +public class CamrecApplication extends Application { - static final transient Logger LOG = LoggerFactory.getLogger(CtbrecApplication.class); + static final transient Logger LOG = LoggerFactory.getLogger(CamrecApplication.class); public static final String BASE_URI = "https://chaturbate.com"; public static final String AFFILIATE_LINK = BASE_URI + "/in/?track=default&tour=LQps&campaign=55vTi&room=0xb00bface"; @@ -63,6 +65,7 @@ public class CtbrecApplication extends Application { private TabPane tabPane = new TabPane(); static EventBus bus; private HBox tokenPanel; + private Site site; @Override public void start(Stage primaryStage) throws Exception { @@ -71,6 +74,8 @@ public class CtbrecApplication extends Application { hostServices = getHostServices(); client = HttpClient.getInstance(); createRecorder(); + site = new Chaturbate(); + site.setRecorder(recorder); doInitialLogin(); createGui(primaryStage); checkForUpdates(); @@ -81,8 +86,12 @@ public class CtbrecApplication extends Application { primaryStage.setTitle("CTB Recorder " + getVersion()); InputStream icon = getClass().getResourceAsStream("/icon.png"); primaryStage.getIcons().add(new Image(icon)); - + int windowWidth = Config.getInstance().getSettings().windowWidth; + int windowHeight = Config.getInstance().getSettings().windowHeight; tabPane = new TabPane(); + Scene scene = new Scene(tabPane, windowWidth, windowHeight); + primaryStage.setScene(scene); + tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue ov, Tab from, Tab to) { @@ -95,26 +104,18 @@ public class CtbrecApplication extends Application { } }); tabPane.setTabClosingPolicy(TabClosingPolicy.SELECTED_TAB); - tabPane.getTabs().add(createTab("Featured", BASE_URI + "/")); - tabPane.getTabs().add(createTab("Female", BASE_URI + "/female-cams/")); - tabPane.getTabs().add(createTab("Male", BASE_URI + "/male-cams/")); - tabPane.getTabs().add(createTab("Couples", BASE_URI + "/couple-cams/")); - tabPane.getTabs().add(createTab("Trans", BASE_URI + "/trans-cams/")); - FollowedTab followedTab = new FollowedTab("Followed", BASE_URI + "/followed-cams/"); - followedTab.setRecorder(recorder); - tabPane.getTabs().add(followedTab); - RecordedModelsTab modelsTab = new RecordedModelsTab("Recording", recorder); + for (Tab tab : site.getTabProvider().getTabs(scene)) { + tabPane.getTabs().add(tab); + } + RecordedModelsTab modelsTab = new RecordedModelsTab("Recording", recorder, site); tabPane.getTabs().add(modelsTab); - RecordingsTab recordingsTab = new RecordingsTab("Recordings", recorder, config); + RecordingsTab recordingsTab = new RecordingsTab("Recordings", recorder, config, site); tabPane.getTabs().add(recordingsTab); settingsTab = new SettingsTab(); tabPane.getTabs().add(settingsTab); tabPane.getTabs().add(new DonateTabFx()); - int windowWidth = Config.getInstance().getSettings().windowWidth; - int windowHeight = Config.getInstance().getSettings().windowHeight; - primaryStage.setScene(new Scene(tabPane, windowWidth, windowHeight)); - followedTab.setScene(primaryStage.getScene()); + primaryStage.getScene().getStylesheets().add("/ctbrec/ui/ThumbCell.css"); primaryStage.getScene().widthProperty().addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowWidth = newVal.intValue()); primaryStage.getScene().heightProperty().addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowHeight = newVal.intValue()); @@ -270,12 +271,6 @@ public class CtbrecApplication extends Application { config = Config.getInstance(); } - Tab createTab(String title, String url) { - ThumbOverviewTab tab = new ThumbOverviewTab(title, url, false); - tab.setRecorder(recorder); - return tab; - } - public static void main(String[] args) { launch(args); } diff --git a/src/main/java/ctbrec/ui/DesktopIntergation.java b/src/main/java/ctbrec/ui/DesktopIntergation.java index b3c7d5f9..bb8eb565 100644 --- a/src/main/java/ctbrec/ui/DesktopIntergation.java +++ b/src/main/java/ctbrec/ui/DesktopIntergation.java @@ -19,7 +19,7 @@ public class DesktopIntergation { public static void open(String uri) { try { - CtbrecApplication.hostServices.showDocument(uri); + CamrecApplication.hostServices.showDocument(uri); return; } catch (Exception e) { LOG.debug("Couldn't open URL with host services {}", uri); diff --git a/src/main/java/ctbrec/ui/DonateTabFx.java b/src/main/java/ctbrec/ui/DonateTabFx.java index 6d8ca1e8..50125693 100644 --- a/src/main/java/ctbrec/ui/DonateTabFx.java +++ b/src/main/java/ctbrec/ui/DonateTabFx.java @@ -44,7 +44,7 @@ public class DonateTabFx extends Tab { ImageView tokenImage = new ImageView(getClass().getResource("/html/token.png").toString()); Button tokenButton = new Button("Buy tokens"); - tokenButton.setOnAction((e) -> { DesktopIntergation.open(CtbrecApplication.AFFILIATE_LINK); }); + tokenButton.setOnAction((e) -> { DesktopIntergation.open(CamrecApplication.AFFILIATE_LINK); }); VBox tokenBox = new VBox(5); tokenBox.setAlignment(Pos.TOP_CENTER); Label tokenDesc = new Label("If you buy tokens by using this button,\n" diff --git a/src/main/java/ctbrec/ui/FollowedTab.java b/src/main/java/ctbrec/ui/FollowedTab.java index f8b22214..446f6c28 100644 --- a/src/main/java/ctbrec/ui/FollowedTab.java +++ b/src/main/java/ctbrec/ui/FollowedTab.java @@ -1,5 +1,6 @@ package ctbrec.ui; +import ctbrec.sites.chaturbate.ChaturbateUpdateService; import javafx.concurrent.WorkerStateEvent; import javafx.geometry.Insets; import javafx.scene.Scene; @@ -16,7 +17,7 @@ public class FollowedTab extends ThumbOverviewTab { private String offlineUrl; public FollowedTab(String title, String url) { - super(title, url, true); + super(title, new ChaturbateUpdateService(url, true)); onlineUrl = url; offlineUrl = url + "offline/"; @@ -43,9 +44,9 @@ public class FollowedTab extends ThumbOverviewTab { online.setSelected(true); group.selectedToggleProperty().addListener((e) -> { if(online.isSelected()) { - super.url = onlineUrl; + ((ChaturbateUpdateService)updateService).setUrl(onlineUrl); } else { - super.url = offlineUrl; + ((ChaturbateUpdateService)updateService).setUrl(offlineUrl); } queue.clear(); updateService.restart(); diff --git a/src/main/java/ctbrec/ui/JavaFxModel.java b/src/main/java/ctbrec/ui/JavaFxModel.java index 7948bd1e..ef48491b 100644 --- a/src/main/java/ctbrec/ui/JavaFxModel.java +++ b/src/main/java/ctbrec/ui/JavaFxModel.java @@ -111,4 +111,19 @@ public class JavaFxModel extends AbstractModel { public List getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException { return delegate.getStreamSources(); } + + @Override + public void invalidateCacheEntries() { + delegate.invalidateCacheEntries(); + } + + @Override + public void receiveTip(int tokens) throws IOException { + delegate.receiveTip(tokens); + } + + @Override + public int[] getStreamResolution(boolean b) throws ExecutionException { + return delegate.getStreamResolution(b); + } } diff --git a/src/main/java/ctbrec/ui/Launcher.java b/src/main/java/ctbrec/ui/Launcher.java index 65643326..70e1bd34 100644 --- a/src/main/java/ctbrec/ui/Launcher.java +++ b/src/main/java/ctbrec/ui/Launcher.java @@ -13,7 +13,7 @@ public class Launcher { // check for OpenJFX try { Class.forName("javafx.application.Application"); - CtbrecApplication.main(args); + CamrecApplication.main(args); } catch (ClassNotFoundException e) { LOG.error("You are running ctbrec with OpenJDK, but OpenJFX can not be found.\n" + "Please either install OpenJFX or use the Oracle JRE, which you can download at\n" @@ -21,7 +21,7 @@ public class Launcher { System.exit(1); } } else { - CtbrecApplication.main(args); + CamrecApplication.main(args); } } } diff --git a/src/main/java/ctbrec/ui/PaginatedScheduledService.java b/src/main/java/ctbrec/ui/PaginatedScheduledService.java new file mode 100644 index 00000000..bba88ef5 --- /dev/null +++ b/src/main/java/ctbrec/ui/PaginatedScheduledService.java @@ -0,0 +1,19 @@ +package ctbrec.ui; + +import java.util.List; + +import ctbrec.Model; +import javafx.concurrent.ScheduledService; + +public abstract class PaginatedScheduledService extends ScheduledService> { + + protected int page = 1; + + public int getPage() { + return page; + } + + public void setPage(int page) { + this.page = page; + } +} diff --git a/src/main/java/ctbrec/ui/RecordedModelsTab.java b/src/main/java/ctbrec/ui/RecordedModelsTab.java index 245e9f2c..c8054536 100644 --- a/src/main/java/ctbrec/ui/RecordedModelsTab.java +++ b/src/main/java/ctbrec/ui/RecordedModelsTab.java @@ -19,9 +19,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.Model; +import ctbrec.Site; import ctbrec.io.HttpClient; import ctbrec.recorder.Recorder; -import ctbrec.sites.chaturbate.ChaturbateModel; import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -60,6 +60,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { private ScheduledService> updateService; private Recorder recorder; + private Site site; FlowPane grid = new FlowPane(); ScrollPane scrollPane = new ScrollPane(); @@ -71,9 +72,10 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { TextField model = new TextField(); Button addModelButton = new Button("Record"); - public RecordedModelsTab(String title, Recorder recorder) { + public RecordedModelsTab(String title, Recorder recorder, Site site) { super(title); this.recorder = recorder; + this.site = site; createGui(); setClosable(false); initializeUpdateService(); @@ -126,9 +128,7 @@ public class RecordedModelsTab extends Tab implements TabSelectionListener { model.setPrefWidth(300); BorderPane.setMargin(addModelBox, new Insets(5)); addModelButton.setOnAction((e) -> { - ChaturbateModel m = new ChaturbateModel(); - m.setName(model.getText()); - m.setUrl("https://chaturbate.com/" + m.getName() + "/"); + Model m = site.createModel(model.getText()); try { recorder.startRecording(m); } catch (IOException | InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e1) { diff --git a/src/main/java/ctbrec/ui/RecordingsTab.java b/src/main/java/ctbrec/ui/RecordingsTab.java index 44613752..2e490a6a 100644 --- a/src/main/java/ctbrec/ui/RecordingsTab.java +++ b/src/main/java/ctbrec/ui/RecordingsTab.java @@ -29,12 +29,13 @@ import com.iheartradio.m3u8.ParseException; import com.iheartradio.m3u8.PlaylistException; import ctbrec.Config; +import ctbrec.Model; import ctbrec.Recording; import ctbrec.Recording.STATUS; +import ctbrec.Site; import ctbrec.io.HttpClient; import ctbrec.recorder.Recorder; import ctbrec.recorder.download.MergedHlsDownload; -import ctbrec.sites.chaturbate.ChaturbateModel; import javafx.application.Platform; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; @@ -67,6 +68,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { private ScheduledService> updateService; private Config config; private Recorder recorder; + private Site site; FlowPane grid = new FlowPane(); ScrollPane scrollPane = new ScrollPane(); @@ -74,10 +76,11 @@ public class RecordingsTab extends Tab implements TabSelectionListener { ObservableList observableRecordings = FXCollections.observableArrayList(); ContextMenu popup; - public RecordingsTab(String title, Recorder recorder, Config config) { + public RecordingsTab(String title, Recorder recorder, Config config, Site site) { super(title); this.recorder = recorder; this.config = config; + this.site = site; createGui(); setClosable(false); initializeUpdateService(); @@ -246,9 +249,7 @@ public class RecordingsTab extends Tab implements TabSelectionListener { MenuItem stopRecording = new MenuItem("Stop recording"); stopRecording.setOnAction((e) -> { - ChaturbateModel m = new ChaturbateModel(); - m.setName(recording.getModelName()); - m.setUrl(CtbrecApplication.BASE_URI + '/' + recording.getModelName() + '/'); + Model m = site.createModel(recording.getModelName()); try { recorder.stopRecording(m); } catch (Exception e1) { diff --git a/src/main/java/ctbrec/ui/SettingsTab.java b/src/main/java/ctbrec/ui/SettingsTab.java index e24ce9f1..4cfb8804 100644 --- a/src/main/java/ctbrec/ui/SettingsTab.java +++ b/src/main/java/ctbrec/ui/SettingsTab.java @@ -188,7 +188,7 @@ public class SettingsTab extends Tab implements TabSelectionListener { layout.add(password, 1, 1); Button createAccount = new Button("Create new Account"); - createAccount.setOnAction((e) -> DesktopIntergation.open(CtbrecApplication.AFFILIATE_LINK)); + createAccount.setOnAction((e) -> DesktopIntergation.open(CamrecApplication.AFFILIATE_LINK)); layout.add(createAccount, 1, 2); GridPane.setColumnSpan(createAccount, 2); diff --git a/src/main/java/ctbrec/ui/TabProvider.java b/src/main/java/ctbrec/ui/TabProvider.java new file mode 100644 index 00000000..1437a0e2 --- /dev/null +++ b/src/main/java/ctbrec/ui/TabProvider.java @@ -0,0 +1,14 @@ +package ctbrec.ui; + +import java.util.Collections; +import java.util.List; + +import javafx.scene.Scene; +import javafx.scene.control.Tab; + +public class TabProvider { + + public List getTabs(Scene scene) { + return Collections.emptyList(); + } +} diff --git a/src/main/java/ctbrec/ui/ThumbCell.java b/src/main/java/ctbrec/ui/ThumbCell.java index a18dae85..c5d00020 100644 --- a/src/main/java/ctbrec/ui/ThumbCell.java +++ b/src/main/java/ctbrec/ui/ThumbCell.java @@ -1,6 +1,8 @@ package ctbrec.ui; import java.io.IOException; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.function.Function; @@ -8,12 +10,14 @@ import java.util.function.Function; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.iheartradio.m3u8.ParseException; +import com.iheartradio.m3u8.PlaylistException; + import ctbrec.Config; import ctbrec.Model; import ctbrec.io.HttpClient; import ctbrec.recorder.Recorder; -import ctbrec.sites.chaturbate.ChaturbateModel; -import ctbrec.sites.chaturbate.StreamInfo; +import ctbrec.recorder.download.StreamSource; import javafx.animation.FadeTransition; import javafx.animation.FillTransition; import javafx.animation.ParallelTransition; @@ -51,7 +55,7 @@ public class ThumbCell extends StackPane { public static int width = 180; private static final Duration ANIMATION_DURATION = new Duration(250); - private ChaturbateModel model; + private Model model; private ImageView iv; private Rectangle resolutionBackground; private final Paint resolutionOnlineColor = new Color(0.22, 0.8, 0.29, 1); @@ -77,7 +81,7 @@ public class ThumbCell extends StackPane { private boolean mouseHovering = false; private boolean recording = false; - public ThumbCell(ThumbOverviewTab parent, ChaturbateModel model, Recorder recorder, HttpClient client) { + public ThumbCell(ThumbOverviewTab parent, Model model, Recorder recorder, HttpClient client) { this.thumbCellList = parent.grid.getChildren(); this.model = model; this.recorder = recorder; @@ -198,7 +202,7 @@ public class ThumbCell extends StackPane { ThumbOverviewTab.threadPool.submit(() -> { try { ThumbOverviewTab.resolutionProcessing.add(model); - int[] resolution = model.getStreamResolution(); + int[] resolution = model.getStreamResolution(false); updateResolutionTag(resolution); // the model is online, but the resolution is 0. probably something went wrong @@ -225,14 +229,14 @@ public class ThumbCell extends StackPane { private void updateResolutionTag(int[] resolution) throws IOException, ExecutionException { String _res = "n/a"; Paint resolutionBackgroundColor = resolutionOnlineColor; - String state = model.getOnlineState(); + String state = model.getOnlineState(false); if ("public".equals(state)) { LOG.trace("Model resolution {} {}x{}", model.getName(), resolution[0], resolution[1]); LOG.trace("Resolution queue size: {}", ThumbOverviewTab.queue.size()); final int w = resolution[1]; _res = w > 0 ? Integer.toString(w) : state; } else { - _res = model.getOnlineState(); + _res = model.getOnlineState(false); resolutionBackgroundColor = resolutionOfflineColor; } final String resText = _res; @@ -281,16 +285,18 @@ public class ThumbCell extends StackPane { void startPlayer() { try { if(model.isOnline(true)) { - StreamInfo streamInfo = model.getStreamInfo(); - LOG.debug("Playing {}", streamInfo.url); - Player.play(streamInfo.url); + List sources = model.getStreamSources(); + Collections.sort(sources); + StreamSource best = sources.get(sources.size()-1); + LOG.debug("Playing {}", best.getMediaPlaylistUrl()); + Player.play(best.getMediaPlaylistUrl()); } else { Alert alert = new AutosizeAlert(Alert.AlertType.INFORMATION); alert.setTitle("Room not public"); alert.setHeaderText("Room is currently not public"); alert.showAndWait(); } - } catch (IOException | ExecutionException | InterruptedException e1) { + } catch (IOException | ExecutionException | InterruptedException | ParseException | PlaylistException e1) { LOG.error("Couldn't get stream information for model {}", model, e1); Alert alert = new AutosizeAlert(Alert.AlertType.ERROR); alert.setTitle("Error"); @@ -374,9 +380,9 @@ public class ThumbCell extends StackPane { String url = null; if(follow) { - url = CtbrecApplication.BASE_URI + "/follow/follow/" + model.getName() + "/"; + url = CamrecApplication.BASE_URI + "/follow/follow/" + model.getName() + "/"; } else { - url = CtbrecApplication.BASE_URI + "/follow/unfollow/" + model.getName() + "/"; + url = CamrecApplication.BASE_URI + "/follow/unfollow/" + model.getName() + "/"; } RequestBody body = RequestBody.create(null, new byte[0]); @@ -422,7 +428,7 @@ public class ThumbCell extends StackPane { }.start(); } - public ChaturbateModel getModel() { + public Model getModel() { return model; } diff --git a/src/main/java/ctbrec/ui/ThumbOverviewTab.java b/src/main/java/ctbrec/ui/ThumbOverviewTab.java index 404b26fb..d043eb28 100644 --- a/src/main/java/ctbrec/ui/ThumbOverviewTab.java +++ b/src/main/java/ctbrec/ui/ThumbOverviewTab.java @@ -15,9 +15,7 @@ import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; @@ -28,13 +26,10 @@ import org.slf4j.LoggerFactory; import com.sun.javafx.collections.ObservableListWrapper; import ctbrec.Config; +import ctbrec.Model; import ctbrec.io.HttpClient; import ctbrec.recorder.Recorder; -import ctbrec.sites.chaturbate.ChaturbateModel; -import ctbrec.sites.chaturbate.ModelParser; import javafx.collections.ObservableList; -import javafx.concurrent.ScheduledService; -import javafx.concurrent.Task; import javafx.concurrent.Worker.State; import javafx.concurrent.WorkerStateEvent; import javafx.event.EventHandler; @@ -59,17 +54,15 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.util.Duration; -import okhttp3.Request; -import okhttp3.Response; public class ThumbOverviewTab extends Tab implements TabSelectionListener { private static final transient Logger LOG = LoggerFactory.getLogger(ThumbOverviewTab.class); - static Set resolutionProcessing = Collections.synchronizedSet(new HashSet<>()); + static Set resolutionProcessing = Collections.synchronizedSet(new HashSet<>()); static BlockingQueue queue = new LinkedBlockingQueue<>(); static ExecutorService threadPool = new ThreadPoolExecutor(2, 2, 10, TimeUnit.MINUTES, queue); - ScheduledService> updateService; + PaginatedScheduledService updateService; Recorder recorder; List filteredThumbCells = Collections.synchronizedList(new ArrayList<>()); List selectedThumbCells = Collections.synchronizedList(new ArrayList<>()); @@ -77,12 +70,10 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { FlowPane grid = new FlowPane(); ReentrantLock gridLock = new ReentrantLock(); ScrollPane scrollPane = new ScrollPane(); - String url; boolean loginRequired; HttpClient client = HttpClient.getInstance(); HBox pagination; - int page = 1; - TextField pageInput = new TextField(Integer.toString(page)); + TextField pageInput = new TextField(Integer.toString(1)); Button pagePrev = new Button("◀"); Button pageNext = new Button("▶"); private volatile boolean updatesSuspended = false; @@ -90,10 +81,9 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { private ComboBox thumbWidth; - public ThumbOverviewTab(String title, String url, boolean loginRequired) { + public ThumbOverviewTab(String title, PaginatedScheduledService updateService) { super(title); - this.url = url; - this.loginRequired = loginRequired; + this.updateService = updateService; setClosable(false); createGui(); initializeUpdateService(); @@ -136,13 +126,17 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { pageInput.setPrefWidth(50); pageInput.setOnAction((e) -> handlePageNumberInput()); pagePrev.setOnAction((e) -> { + int page = updateService.getPage(); page = Math.max(1, --page); pageInput.setText(Integer.toString(page)); + updateService.setPage(page); restartUpdateService(); }); pageNext.setOnAction((e) -> { + int page = updateService.getPage(); page++; pageInput.setText(Integer.toString(page)); + updateService.setPage(page); restartUpdateService(); }); @@ -189,12 +183,13 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { private void handlePageNumberInput() { try { - page = Integer.parseInt(pageInput.getText()); + int page = Integer.parseInt(pageInput.getText()); page = Math.max(1, page); + updateService.setPage(page); restartUpdateService(); } catch(NumberFormatException e) { } finally { - pageInput.setText(Integer.toString(page)); + pageInput.setText(Integer.toString(updateService.getPage())); } } @@ -211,7 +206,6 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { } void initializeUpdateService() { - updateService = createUpdateService(); updateService.setPeriod(new Duration(TimeUnit.SECONDS.toMillis(10))); updateService.setOnSucceeded((event) -> onSuccess()); updateService.setOnFailed((event) -> onFail(event)); @@ -223,7 +217,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { } gridLock.lock(); try { - List models = updateService.getValue(); + List models = updateService.getValue(); ObservableList nodes = grid.getChildren(); // first remove models, which are not in the updated list @@ -238,7 +232,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { List positionChangedOrNew = new ArrayList<>(); int index = 0; - for (ChaturbateModel model : models) { + for (Model model : models) { boolean found = false; for (Iterator iterator = nodes.iterator(); iterator.hasNext();) { Node node = iterator.next(); @@ -278,7 +272,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { } - ThumbCell createThumbCell(ThumbOverviewTab thumbOverviewTab, ChaturbateModel model, Recorder recorder2, HttpClient client2) { + ThumbCell createThumbCell(ThumbOverviewTab thumbOverviewTab, Model model, Recorder recorder2, HttpClient client2) { ThumbCell newCell = new ThumbCell(this, model, recorder, client); newCell.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, event -> { suspendUpdates(true); @@ -341,7 +335,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { Map event = new HashMap<>(); event.put("event", "tokens.sent"); event.put("amount", tokens); - CtbrecApplication.bus.post(event); + CamrecApplication.bus.post(event); } catch (IOException e1) { Alert alert = new AutosizeAlert(Alert.AlertType.ERROR); alert.setTitle("Error"); @@ -476,7 +470,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { for (Iterator iterator = grid.getChildren().iterator(); iterator.hasNext();) { Node node = iterator.next(); ThumbCell cell = (ThumbCell) node; - ChaturbateModel m = cell.getModel(); + Model m = cell.getModel(); if(!matches(m, filter)) { iterator.remove(); filteredThumbCells.add(cell); @@ -487,7 +481,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { // add the ones, which might have been filtered before, but now match for (Iterator iterator = filteredThumbCells.iterator(); iterator.hasNext();) { ThumbCell thumbCell = iterator.next(); - ChaturbateModel m = thumbCell.getModel(); + Model m = thumbCell.getModel(); if(matches(m, filter)) { iterator.remove(); insert(thumbCell); @@ -520,7 +514,7 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { } } - private boolean matches(ChaturbateModel m, String filter) { + private boolean matches(Model m, String filter) { try { String[] tokens = filter.split(" "); StringBuilder searchTextBuilder = new StringBuilder(m.getName()); @@ -558,43 +552,6 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener { } } - private ScheduledService> createUpdateService() { - ScheduledService> updateService = new ScheduledService>() { - @Override - protected Task> createTask() { - return new Task>() { - @Override - public List call() throws IOException { - String url = ThumbOverviewTab.this.url + "?page="+page+"&keywords=&_=" + System.currentTimeMillis(); - LOG.debug("Fetching page {}", url); - Request request = new Request.Builder().url(url).build(); - Response response = client.execute(request, loginRequired); - if (response.isSuccessful()) { - List models = ModelParser.parseModels(response.body().string()); - response.close(); - return models; - } else { - int code = response.code(); - response.close(); - throw new IOException("HTTP status " + code); - } - } - }; - } - }; - ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setDaemon(true); - t.setName("ThumbOverviewTab UpdateService"); - return t; - } - }); - updateService.setExecutor(executor); - return updateService; - } - public void setRecorder(Recorder recorder) { this.recorder = recorder; } diff --git a/src/main/java/ctbrec/ui/TipDialog.java b/src/main/java/ctbrec/ui/TipDialog.java index f9533dce..8e01cf72 100644 --- a/src/main/java/ctbrec/ui/TipDialog.java +++ b/src/main/java/ctbrec/ui/TipDialog.java @@ -54,7 +54,7 @@ public class TipDialog extends TextInputDialog { Map event = new HashMap<>(); event.put("event", "tokens"); event.put("amount", tokens); - CtbrecApplication.bus.post(event); + CamrecApplication.bus.post(event); return tokens; } else { throw new IOException("HTTP response: " + resp.code() + " - " + resp.message()); @@ -78,7 +78,7 @@ public class TipDialog extends TextInputDialog { buyTokens.showAndWait(); TipDialog.this.close(); if(buyTokens.getResult() == ButtonType.YES) { - DesktopIntergation.open(CtbrecApplication.AFFILIATE_LINK); + DesktopIntergation.open(CamrecApplication.AFFILIATE_LINK); } } else { getEditor().setDisable(false); diff --git a/src/main/java/ctbrec/ui/TokenLabel.java b/src/main/java/ctbrec/ui/TokenLabel.java index b9d9a23b..073fb038 100644 --- a/src/main/java/ctbrec/ui/TokenLabel.java +++ b/src/main/java/ctbrec/ui/TokenLabel.java @@ -14,7 +14,7 @@ public class TokenLabel extends Label { public TokenLabel() { setText("Tokens: loading…"); - CtbrecApplication.bus.register(new Object() { + CamrecApplication.bus.register(new Object() { @Subscribe public void tokensUpdates(Map e) { if(Objects.equals("tokens", e.get("event"))) { diff --git a/src/main/java/ctbrec/ui/UpdateTab.java b/src/main/java/ctbrec/ui/UpdateTab.java index 4c82647e..7306b392 100644 --- a/src/main/java/ctbrec/ui/UpdateTab.java +++ b/src/main/java/ctbrec/ui/UpdateTab.java @@ -3,7 +3,7 @@ package ctbrec.ui; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import ctbrec.ui.CtbrecApplication.Release; +import ctbrec.ui.CamrecApplication.Release; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button;