342 lines
14 KiB
Java
342 lines
14 KiB
Java
package ctbrec.ui;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.lang.reflect.Type;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import java.util.concurrent.ExecutionException;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import com.squareup.moshi.JsonAdapter;
|
|
import com.squareup.moshi.Moshi;
|
|
import com.squareup.moshi.Types;
|
|
|
|
import ctbrec.Config;
|
|
import ctbrec.HttpClient;
|
|
import ctbrec.Version;
|
|
import ctbrec.recorder.LocalRecorder;
|
|
import ctbrec.recorder.Recorder;
|
|
import ctbrec.recorder.RemoteRecorder;
|
|
import javafx.application.Application;
|
|
import javafx.application.HostServices;
|
|
import javafx.application.Platform;
|
|
import javafx.beans.value.ChangeListener;
|
|
import javafx.beans.value.ObservableValue;
|
|
import javafx.concurrent.Task;
|
|
import javafx.geometry.Insets;
|
|
import javafx.geometry.Pos;
|
|
import javafx.scene.Scene;
|
|
import javafx.scene.control.Alert;
|
|
import javafx.scene.control.Button;
|
|
import javafx.scene.control.Label;
|
|
import javafx.scene.control.Tab;
|
|
import javafx.scene.control.TabPane;
|
|
import javafx.scene.control.TabPane.TabClosingPolicy;
|
|
import javafx.scene.image.Image;
|
|
import javafx.scene.layout.HBox;
|
|
import javafx.scene.layout.StackPane;
|
|
import javafx.scene.text.Font;
|
|
import javafx.stage.Stage;
|
|
import okhttp3.Request;
|
|
import okhttp3.Response;
|
|
|
|
public class CtbrecApplication extends Application {
|
|
|
|
static final transient Logger LOG = LoggerFactory.getLogger(CtbrecApplication.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";
|
|
|
|
private Config config;
|
|
private Recorder recorder;
|
|
private HttpClient client;
|
|
static HostServices hostServices;
|
|
private SettingsTab settingsTab;
|
|
private TabPane tabPane = new TabPane();
|
|
|
|
@Override
|
|
public void start(Stage primaryStage) throws Exception {
|
|
loadConfig();
|
|
hostServices = getHostServices();
|
|
client = HttpClient.getInstance();
|
|
createRecorder();
|
|
doInitialLogin();
|
|
createGui(primaryStage);
|
|
checkForUpdates();
|
|
}
|
|
|
|
private void createGui(Stage primaryStage) throws IOException {
|
|
LOG.debug("Creating GUI");
|
|
primaryStage.setTitle("CTB Recorder " + getVersion());
|
|
InputStream icon = getClass().getResourceAsStream("/icon.png");
|
|
primaryStage.getIcons().add(new Image(icon));
|
|
|
|
tabPane = new TabPane();
|
|
tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>() {
|
|
@Override
|
|
public void changed(ObservableValue<? extends Tab> ov, Tab from, Tab to) {
|
|
if(from != null && from instanceof TabSelectionListener) {
|
|
((TabSelectionListener) from).deselected();
|
|
}
|
|
if(to != null && to instanceof TabSelectionListener) {
|
|
((TabSelectionListener) to).selected();
|
|
}
|
|
}
|
|
});
|
|
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);
|
|
tabPane.getTabs().add(modelsTab);
|
|
RecordingsTab recordingsTab = new RecordingsTab("Recordings", recorder, config);
|
|
tabPane.getTabs().add(recordingsTab);
|
|
settingsTab = new SettingsTab();
|
|
tabPane.getTabs().add(settingsTab);
|
|
tabPane.getTabs().add(new DonateTabFx());
|
|
|
|
StackPane stackPane = new StackPane();
|
|
stackPane.getChildren().add(tabPane);
|
|
|
|
String username = Config.getInstance().getSettings().username;
|
|
if(username != null && !username.trim().isEmpty()) {
|
|
Button buyTokens = new Button("Buy Tokens");
|
|
buyTokens.setFont(Font.font(11));
|
|
buyTokens.setOnAction((e) -> DesktopIntergation.open(AFFILIATE_LINK));
|
|
Label tokenBalance = new Label("Tokens: loading…");
|
|
tokenBalance.setFont(Font.font(11));
|
|
HBox tokenPanel = new HBox(5, tokenBalance, buyTokens);
|
|
//tokenPanel.setBackground(new Background(new BackgroundFill(Color.GREEN, CornerRadii.EMPTY, new Insets(0))));
|
|
tokenPanel.setAlignment(Pos.BASELINE_RIGHT);
|
|
tokenPanel.setMaxWidth(200);
|
|
tokenPanel.setMaxHeight(25);
|
|
HBox.setMargin(tokenBalance, new Insets(5, 5, 0, 0));
|
|
stackPane.getChildren().add(tokenPanel);
|
|
StackPane.setAlignment(tokenPanel, Pos.TOP_RIGHT);
|
|
StackPane.setMargin(tokenPanel, new Insets(5));
|
|
loadTokenBalance(tokenBalance);
|
|
}
|
|
|
|
int windowWidth = Config.getInstance().getSettings().windowWidth;
|
|
int windowHeight = Config.getInstance().getSettings().windowHeight;
|
|
primaryStage.setScene(new Scene(stackPane, 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());
|
|
primaryStage.setMaximized(Config.getInstance().getSettings().windowMaximized);
|
|
primaryStage.maximizedProperty().addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowMaximized = newVal.booleanValue());
|
|
primaryStage.setX(Config.getInstance().getSettings().windowX);
|
|
primaryStage.setY(Config.getInstance().getSettings().windowY);
|
|
primaryStage.xProperty().addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowX = newVal.intValue());
|
|
primaryStage.yProperty().addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowY = newVal.intValue());
|
|
primaryStage.show();
|
|
primaryStage.setOnCloseRequest((e) -> {
|
|
e.consume();
|
|
Alert shutdownInfo = new AutosizeAlert(Alert.AlertType.INFORMATION);
|
|
shutdownInfo.setTitle("Shutdown");
|
|
shutdownInfo.setContentText("Shutting down. Please wait a few seconds...");
|
|
shutdownInfo.show();
|
|
|
|
new Thread() {
|
|
@Override
|
|
public void run() {
|
|
settingsTab.saveConfig();
|
|
recorder.shutdown();
|
|
client.shutdown();
|
|
try {
|
|
Config.getInstance().save();
|
|
LOG.info("Shutdown complete. Goodbye!");
|
|
Platform.exit();
|
|
// This is needed, because OkHttp?! seems to block the shutdown with its writer threads. They are not daemon threads :(
|
|
System.exit(0);
|
|
} catch (IOException e1) {
|
|
Platform.runLater(() -> {
|
|
Alert alert = new AutosizeAlert(Alert.AlertType.ERROR);
|
|
alert.setTitle("Error saving settings");
|
|
alert.setContentText("Couldn't save settings: " + e1.getLocalizedMessage());
|
|
alert.showAndWait();
|
|
System.exit(1);
|
|
});
|
|
}
|
|
}
|
|
}.start();
|
|
});
|
|
}
|
|
|
|
private void loadTokenBalance(Label label) {
|
|
Task<Integer> task = new Task<Integer>() {
|
|
@Override
|
|
protected Integer call() throws Exception {
|
|
String username = Config.getInstance().getSettings().username;
|
|
if (username == null || username.trim().isEmpty()) {
|
|
throw new IOException("Not logged in");
|
|
}
|
|
|
|
String url = "https://chaturbate.com/p/" + username + "/";
|
|
HttpClient client = HttpClient.getInstance();
|
|
Request req = new Request.Builder().url(url).build();
|
|
Response resp = client.execute(req, true);
|
|
if (resp.isSuccessful()) {
|
|
String profilePage = resp.body().string();
|
|
String tokenText = HtmlParser.getText(profilePage, "span.tokencount");
|
|
int tokens = Integer.parseInt(tokenText);
|
|
return tokens;
|
|
} else {
|
|
throw new IOException("HTTP response: " + resp.code() + " - " + resp.message());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void done() {
|
|
try {
|
|
int tokens = get();
|
|
Platform.runLater(() -> {
|
|
label.setText("Tokens: " + tokens);
|
|
});
|
|
} catch (InterruptedException | ExecutionException e) {
|
|
LOG.error("Couldn't retrieve account balance", e);
|
|
label.setText("Tokens: error");
|
|
}
|
|
}
|
|
};
|
|
new Thread(task).start();
|
|
}
|
|
|
|
private void doInitialLogin() {
|
|
if(config.getSettings().username != null && !config.getSettings().username.isEmpty()) {
|
|
new Thread() {
|
|
@Override
|
|
public void run() {
|
|
if(!Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
|
|
try {
|
|
client.login();
|
|
} catch (IOException e1) {
|
|
LOG.warn("Initial login failed" , e1);
|
|
}
|
|
}
|
|
};
|
|
}.start();
|
|
}
|
|
}
|
|
|
|
private void createRecorder() {
|
|
if(config.getSettings().localRecording) {
|
|
recorder = new LocalRecorder(config);
|
|
} else {
|
|
recorder = new RemoteRecorder(config, client);
|
|
}
|
|
}
|
|
|
|
private void loadConfig() {
|
|
try {
|
|
Config.init();
|
|
} catch (Exception e) {
|
|
Alert alert = new AutosizeAlert(Alert.AlertType.ERROR);
|
|
alert.setTitle("Whoopsie");
|
|
alert.setContentText("Couldn't load settings.");
|
|
alert.showAndWait();
|
|
System.exit(1);
|
|
}
|
|
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);
|
|
}
|
|
|
|
private void checkForUpdates() {
|
|
Thread updateCheck = new Thread(() -> {
|
|
try {
|
|
String url = "https://api.github.com/repos/0xboobface/ctbrec/releases";
|
|
Request request = new Request.Builder().url(url).build();
|
|
Response response = client.execute(request);
|
|
if(response.isSuccessful()) {
|
|
Moshi moshi = new Moshi.Builder().build();
|
|
Type type = Types.newParameterizedType(List.class, Release.class);
|
|
JsonAdapter<List<Release>> adapter = moshi.adapter(type);
|
|
List<Release> releases = adapter.fromJson(response.body().source());
|
|
Release latest = releases.get(0);
|
|
Version latestVersion = latest.getVersion();
|
|
Version ctbrecVersion = getVersion();
|
|
if(latestVersion.compareTo(ctbrecVersion) > 0) {
|
|
LOG.debug("Update available {} < {}", ctbrecVersion, latestVersion);
|
|
Platform.runLater(() -> tabPane.getTabs().add(new UpdateTab(latest)));
|
|
} else {
|
|
LOG.debug("ctbrec is up-to-date {}", ctbrecVersion);
|
|
}
|
|
}
|
|
response.close();
|
|
} catch (IOException e) {
|
|
LOG.warn("Update check failed {}", e.getMessage());
|
|
}
|
|
|
|
});
|
|
updateCheck.setName("Update Check");
|
|
updateCheck.setDaemon(true);
|
|
updateCheck.start();
|
|
}
|
|
|
|
private Version getVersion() throws IOException {
|
|
if(Objects.equals(System.getenv("CTBREC_DEV"), "1")) {
|
|
return Version.of("0.0.0-DEV");
|
|
} else {
|
|
try(InputStream is = getClass().getClassLoader().getResourceAsStream("version")) {
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
|
|
String versionString = reader.readLine();
|
|
Version version = Version.of(versionString);
|
|
return version;
|
|
}
|
|
}
|
|
}
|
|
|
|
static class Release {
|
|
private String name;
|
|
private String tag_name;
|
|
private String html_url;
|
|
|
|
public String getName() {
|
|
return name;
|
|
}
|
|
|
|
public void setName(String name) {
|
|
this.name = name;
|
|
}
|
|
|
|
public String getTagName() {
|
|
return tag_name;
|
|
}
|
|
|
|
public void setTagName(String tagName) {
|
|
this.tag_name = tagName;
|
|
}
|
|
|
|
public String getHtmlUrl() {
|
|
return html_url;
|
|
}
|
|
|
|
public void setHtmlUrl(String htmlUrl) {
|
|
this.html_url = htmlUrl;
|
|
}
|
|
|
|
public Version getVersion() {
|
|
return Version.of(tag_name);
|
|
}
|
|
}
|
|
}
|