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.
This commit is contained in:
0xboobface 2018-10-17 14:11:04 +02:00
parent c7d55c6fe0
commit 54de1339fb
26 changed files with 326 additions and 144 deletions

View File

@ -27,5 +27,8 @@ public interface Model {
public String getOnlineState(boolean failFast) throws IOException, ExecutionException;
public List<StreamSource> 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;
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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<ChaturbateModel> followed = ModelParser.parseModels(response.body().string());
List<Model> 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);

View File

@ -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<Model> modelAdapter = moshi.adapter(Model.class);
JsonAdapter<Model> modelAdapter = new ModelJsonAdapter();
List<Model> models = recorder.getModelsRecording();
for (Iterator<Model> 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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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<ChaturbateModel> parseModels(String html) {
List<ChaturbateModel> models = new ArrayList<>();
public static List<Model> parseModels(String html) {
List<Model> 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"));

View File

@ -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<Tab> getTabs(Scene scene) {
final String BASE_URI = chaturbate.getBaseUrl();
List<Tab> 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;
}
}

View File

@ -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<List<Model>> createTask() {
return new Task<List<Model>>() {
@Override
public List<Model> 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<Model> 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;
}
}

View File

@ -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<Tab>() {
@Override
public void changed(ObservableValue<? extends Tab> 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);
}

View File

@ -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);

View File

@ -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"

View File

@ -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();

View File

@ -111,4 +111,19 @@ public class JavaFxModel extends AbstractModel {
public List<StreamSource> 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);
}
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,19 @@
package ctbrec.ui;
import java.util.List;
import ctbrec.Model;
import javafx.concurrent.ScheduledService;
public abstract class PaginatedScheduledService extends ScheduledService<List<Model>> {
protected int page = 1;
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
}

View File

@ -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<List<Model>> 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) {

View File

@ -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<List<JavaFxRecording>> 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<JavaFxRecording> 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) {

View File

@ -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);

View File

@ -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<Tab> getTabs(Scene scene) {
return Collections.emptyList();
}
}

View File

@ -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<StreamSource> 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;
}

View File

@ -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<ChaturbateModel> resolutionProcessing = Collections.synchronizedSet(new HashSet<>());
static Set<Model> resolutionProcessing = Collections.synchronizedSet(new HashSet<>());
static BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
static ExecutorService threadPool = new ThreadPoolExecutor(2, 2, 10, TimeUnit.MINUTES, queue);
ScheduledService<List<ChaturbateModel>> updateService;
PaginatedScheduledService updateService;
Recorder recorder;
List<ThumbCell> filteredThumbCells = Collections.synchronizedList(new ArrayList<>());
List<ThumbCell> 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<Integer> 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<ChaturbateModel> models = updateService.getValue();
List<Model> models = updateService.getValue();
ObservableList<Node> 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<ThumbCell> positionChangedOrNew = new ArrayList<>();
int index = 0;
for (ChaturbateModel model : models) {
for (Model model : models) {
boolean found = false;
for (Iterator<Node> 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<String, Object> 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<Node> 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<ThumbCell> 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<List<ChaturbateModel>> createUpdateService() {
ScheduledService<List<ChaturbateModel>> updateService = new ScheduledService<List<ChaturbateModel>>() {
@Override
protected Task<List<ChaturbateModel>> createTask() {
return new Task<List<ChaturbateModel>>() {
@Override
public List<ChaturbateModel> 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<ChaturbateModel> 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;
}

View File

@ -54,7 +54,7 @@ public class TipDialog extends TextInputDialog {
Map<String, Object> 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);

View File

@ -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<String, Object> e) {
if(Objects.equals("tokens", e.get("event"))) {

View File

@ -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;