Implemented followed tab for CamSoda

This commit is contained in:
0xboobface 2018-10-28 21:23:58 +01:00
parent 7442ddd3e4
commit 8fd09fd521
10 changed files with 340 additions and 62 deletions

View File

@ -24,6 +24,8 @@ public class Settings {
public String password = ""; // chaturbate password TODO maybe rename this onetime
public String mfcUsername = "";
public String mfcPassword = "";
public String camsodaUsername = "";
public String camsodaPassword = "";
public String lastDownloadDir = "";
public List<Model> models = new ArrayList<Model>();

View File

@ -2,12 +2,26 @@ package ctbrec.sites.camsoda;
import java.io.IOException;
import org.json.JSONObject;
import ctbrec.Config;
import ctbrec.Model;
import ctbrec.io.HttpClient;
import ctbrec.recorder.Recorder;
import ctbrec.sites.AbstractSite;
import ctbrec.ui.DesktopIntergation;
import ctbrec.ui.SettingsTab;
import ctbrec.ui.TabProvider;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import okhttp3.Request;
import okhttp3.Response;
public class Camsoda extends AbstractSite {
@ -27,7 +41,7 @@ public class Camsoda extends AbstractSite {
@Override
public String getAffiliateLink() {
return "";
return BASE_URI;
}
@Override
@ -51,7 +65,26 @@ public class Camsoda extends AbstractSite {
@Override
public Integer getTokenBalance() throws IOException {
return 0;
String username = Config.getInstance().getSettings().camsodaUsername;
if (username == null || username.trim().isEmpty()) {
throw new IOException("Not logged in");
}
String url = BASE_URI + "/api/v1/user/" + username;
Request request = new Request.Builder().url(url).build();
Response response = getHttpClient().execute(request, true);
if(response.isSuccessful()) {
JSONObject json = new JSONObject(response.body().string());
if(json.has("user")) {
JSONObject user = json.getJSONObject("user");
if(user.has("tokens")) {
return user.getInt("tokens");
}
}
} else {
throw new IOException(response.code() + " " + response.message());
}
throw new RuntimeException("Tokens not found in response");
}
@Override
@ -61,23 +94,19 @@ public class Camsoda extends AbstractSite {
@Override
public void login() throws IOException {
httpClient.login();
getHttpClient().login();
}
@Override
public HttpClient getHttpClient() {
if(httpClient == null) {
httpClient = new CamsodaHttpClient();
}
return httpClient;
}
@Override
public void init() throws IOException {
httpClient = new HttpClient() {
@Override
public boolean login() throws IOException {
return false;
}
};
}
@Override
@ -87,12 +116,12 @@ public class Camsoda extends AbstractSite {
@Override
public boolean supportsTips() {
return false;
return true;
}
@Override
public boolean supportsFollow() {
return false;
return true;
}
@Override
@ -101,12 +130,38 @@ public class Camsoda extends AbstractSite {
}
@Override
public Node getConfigurationGui() {
return null;
public boolean credentialsAvailable() {
String username = Config.getInstance().getSettings().camsodaUsername;
return username != null && !username.trim().isEmpty();
}
@Override
public boolean credentialsAvailable() {
return false;
public Node getConfigurationGui() {
GridPane layout = SettingsTab.createGridLayout();
layout.add(new Label("CamSoda User"), 0, 0);
TextField username = new TextField(Config.getInstance().getSettings().camsodaUsername);
username.focusedProperty().addListener((e) -> Config.getInstance().getSettings().camsodaUsername = username.getText());
GridPane.setFillWidth(username, true);
GridPane.setHgrow(username, Priority.ALWAYS);
GridPane.setColumnSpan(username, 2);
layout.add(username, 1, 0);
layout.add(new Label("CamSoda Password"), 0, 1);
PasswordField password = new PasswordField();
password.setText(Config.getInstance().getSettings().camsodaPassword);
password.focusedProperty().addListener((e) -> Config.getInstance().getSettings().camsodaPassword = password.getText());
GridPane.setFillWidth(password, true);
GridPane.setHgrow(password, Priority.ALWAYS);
GridPane.setColumnSpan(password, 2);
layout.add(password, 1, 1);
Button createAccount = new Button("Create new Account");
createAccount.setOnAction((e) -> DesktopIntergation.open(getAffiliateLink()));
layout.add(createAccount, 1, 2);
GridPane.setColumnSpan(createAccount, 2);
GridPane.setMargin(username, new Insets(0, 0, 0, SettingsTab.CHECKBOX_MARGIN));
GridPane.setMargin(password, new Insets(0, 0, 0, SettingsTab.CHECKBOX_MARGIN));
GridPane.setMargin(createAccount, new Insets(0, 0, 0, SettingsTab.CHECKBOX_MARGIN));
return layout;
}
}

View File

@ -0,0 +1,76 @@
package ctbrec.sites.camsoda;
import ctbrec.ui.FollowedTab;
import ctbrec.ui.ThumbOverviewTab;
import javafx.concurrent.WorkerStateEvent;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
public class CamsodaFollowedTab extends ThumbOverviewTab implements FollowedTab {
private Label status;
boolean showOnline = true;
public CamsodaFollowedTab(String title, Camsoda camsoda) {
super(title, new CamsodaFollowedUpdateService(camsoda), camsoda);
status = new Label("Logging in...");
grid.getChildren().add(status);
}
@Override
protected void createGui() {
super.createGui();
addOnlineOfflineSelector();
}
private void addOnlineOfflineSelector() {
ToggleGroup group = new ToggleGroup();
RadioButton online = new RadioButton("online");
online.setToggleGroup(group);
RadioButton offline = new RadioButton("offline");
offline.setToggleGroup(group);
pagination.getChildren().add(online);
pagination.getChildren().add(offline);
HBox.setMargin(online, new Insets(5, 5, 5, 40));
HBox.setMargin(offline, new Insets(5, 5, 5, 5));
online.setSelected(true);
group.selectedToggleProperty().addListener((e) -> {
queue.clear();
((CamsodaFollowedUpdateService)updateService).showOnline(online.isSelected());
updateService.restart();
});
}
@Override
protected void onSuccess() {
grid.getChildren().remove(status);
super.onSuccess();
}
@Override
protected void onFail(WorkerStateEvent event) {
status.setText("Login failed");
super.onFail(event);
}
@Override
public void selected() {
status.setText("Logging in...");
super.selected();
}
public void setScene(Scene scene) {
scene.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (this.isSelected()) {
if (event.getCode() == KeyCode.DELETE) {
follow(selectedThumbCells, false);
}
}
});
}
}

View File

@ -0,0 +1,73 @@
package ctbrec.sites.camsoda;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import org.json.JSONArray;
import org.json.JSONObject;
import ctbrec.Model;
import ctbrec.ui.PaginatedScheduledService;
import javafx.concurrent.Task;
import okhttp3.Request;
import okhttp3.Response;
public class CamsodaFollowedUpdateService extends PaginatedScheduledService {
private Camsoda camsoda;
private boolean showOnline = true;
public CamsodaFollowedUpdateService(Camsoda camsoda) {
this.camsoda = camsoda;
}
@Override
protected Task<List<Model>> createTask() {
return new Task<List<Model>>() {
@Override
public List<Model> call() throws IOException {
List<Model> models = new ArrayList<>();
String url = camsoda.getBaseUrl() + "/api/v1/user/current";
Request request = new Request.Builder().url(url).build();
Response response = camsoda.getHttpClient().execute(request, true);
if (response.isSuccessful()) {
JSONObject json = new JSONObject(response.body().string());
if(json.has("status") && json.getBoolean("status")) {
JSONObject user = json.getJSONObject("user");
JSONArray following = user.getJSONArray("following");
for (int i = 0; i < following.length(); i++) {
JSONObject m = following.getJSONObject(i);
CamsodaModel model = (CamsodaModel) camsoda.createModel(m.getString("followname"));
boolean online = m.getInt("online") == 1;
model.setOnlineState(online ? "offline" : "online");
model.setPreview("https://md.camsoda.com/thumbs/" + model.getName() + ".jpg");
models.add(model);
}
return models.stream()
.filter((m) -> {
try {
return m.isOnline() == showOnline;
} catch (IOException | ExecutionException | InterruptedException e) {
return false;
}
}).collect(Collectors.toList());
} else {
response.close();
return Collections.emptyList();
}
} else {
int code = response.code();
response.close();
throw new IOException("HTTP status " + code);
}
}
};
}
void showOnline(boolean online) {
this.showOnline = online;
}
}

View File

@ -0,0 +1,38 @@
package ctbrec.sites.camsoda;
import java.io.IOException;
import org.json.JSONObject;
import ctbrec.Config;
import ctbrec.io.HttpClient;
import okhttp3.FormBody;
import okhttp3.Request;
import okhttp3.Response;
public class CamsodaHttpClient extends HttpClient {
@Override
public boolean login() throws IOException {
String url = Camsoda.BASE_URI + "/api/v1/auth/login";
FormBody body = new FormBody.Builder()
.add("username", Config.getInstance().getSettings().camsodaUsername)
.add("password", Config.getInstance().getSettings().camsodaPassword)
.build();
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
Response response = execute(request);
if(response.isSuccessful()) {
JSONObject resp = new JSONObject(response.body().string());
if(resp.has("error")) {
throw new IOException(resp.getString("error"));
} else {
return true;
}
} else {
throw new IOException(response.code() + " " + response.message());
}
}
}

View File

@ -25,6 +25,7 @@ import ctbrec.AbstractModel;
import ctbrec.recorder.download.StreamSource;
import ctbrec.sites.Site;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class CamsodaModel extends AbstractModel {
@ -94,32 +95,30 @@ public class CamsodaModel extends AbstractModel {
@Override
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException {
LOG.trace("Loading master playlist {}", streamUrl);
if(streamSources == null) {
Request req = new Request.Builder().url(streamUrl).build();
Response response = site.getHttpClient().execute(req);
try {
InputStream inputStream = response.body().byteStream();
PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8);
Playlist playlist = parser.parse();
MasterPlaylist master = playlist.getMasterPlaylist();
PlaylistData playlistData = master.getPlaylists().get(0);
StreamSource streamsource = new StreamSource();
streamsource.mediaPlaylistUrl = streamUrl.replace("playlist.m3u8", playlistData.getUri());
if(playlistData.hasStreamInfo()) {
StreamInfo info = playlistData.getStreamInfo();
streamsource.bandwidth = info.getBandwidth();
streamsource.width = info.hasResolution() ? info.getResolution().width : 0;
streamsource.height = info.hasResolution() ? info.getResolution().height : 0;
} else {
streamsource.bandwidth = 0;
streamsource.width = 0;
streamsource.height = 0;
}
streamSources = Collections.singletonList(streamsource);
} finally {
response.close();
LOG.debug("Loading master playlist {}", streamUrl);
Request req = new Request.Builder().url(streamUrl).build();
Response response = site.getHttpClient().execute(req);
try {
InputStream inputStream = response.body().byteStream();
PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8);
Playlist playlist = parser.parse();
MasterPlaylist master = playlist.getMasterPlaylist();
PlaylistData playlistData = master.getPlaylists().get(0);
StreamSource streamsource = new StreamSource();
streamsource.mediaPlaylistUrl = streamUrl.replace("playlist.m3u8", playlistData.getUri());
if(playlistData.hasStreamInfo()) {
StreamInfo info = playlistData.getStreamInfo();
streamsource.bandwidth = info.getBandwidth();
streamsource.width = info.hasResolution() ? info.getResolution().width : 0;
streamsource.height = info.hasResolution() ? info.getResolution().height : 0;
} else {
streamsource.bandwidth = 0;
streamsource.width = 0;
streamsource.height = 0;
}
streamSources = Collections.singletonList(streamsource);
} finally {
response.close();
}
return streamSources;
}
@ -153,18 +152,47 @@ public class CamsodaModel extends AbstractModel {
@Override
public void receiveTip(int tokens) throws IOException {
// TODO Auto-generated method stub
/*
sendTip: function(i, a, r, o, c, d) {
if (!s.isAuthenticated()) return s.showRegister(), t.when(!1);
var u = t.defer();
return e.post("/api/v1/tip/" + i, {
amount: a,
comment: o,
type: r,
app_data: c,
source_id: d
}).then(function(e) {
1 == e.data.status ? (s.currentUser.tokens = e.data.total, void 0 != e.data.tipped_performer_last_24hrs && e.data.tipped_performer_last_24hrs >= 25 && (n.$emit("local.allowed_to_rate"), 0 == n.allowedToRate && (n.allowedToRate = !0, l.pop("info", "Voting Unlocked", "You tipped " + i + " 25 tokens in the past 24 hours, you may now vote!"))), u.resolve(e.data)) : (l.pop("error", e.data.error, e.data.message), u.reject(e.data))
}), u.promise
},
*/
}
@Override
public boolean follow() throws IOException {
// TODO Auto-generated method stub
return false;
String url = Camsoda.BASE_URI + "/api/v1/follow/" + getName();
//RequestBody body = new FormBody.Builder().build();
LOG.debug("Sending follow request {}", url);
Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(null, ""))
.addHeader("Content-Lentgh", "0")
.addHeader("Referer", Camsoda.BASE_URI + '/' + getName())
.build();
Response resp = site.getHttpClient().execute(request, true);
if (resp.isSuccessful()) {
System.out.println(resp.body().string());
return true;
} else {
resp.close();
throw new IOException("HTTP status " + resp.code() + " " + resp.message());
}
}
@Override
public boolean unfollow() throws IOException {
// TODO Auto-generated method stub
// TODO /api/v1/unfollow/" + n.slug
return false;
}

View File

@ -33,13 +33,13 @@ import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TitledPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import okhttp3.Request;
@ -51,7 +51,7 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener {
private Camsoda camsoda;
private Recorder recorder;
private VBox showList;
private GridPane showList;
private ProgressIndicator progressIndicator;
public CamsodaShowsTab(Camsoda camsoda, Recorder recorder) {
@ -61,7 +61,10 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener {
}
private void createGui() {
showList = new VBox();
showList = new GridPane();
showList.setPadding(new Insets(5));
showList.setHgap(5);
showList.setVgap(5);
progressIndicator = new ProgressIndicator();
progressIndicator.setPrefSize(100, 100);
setContent(progressIndicator);
@ -118,14 +121,15 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener {
try {
List<ShowBox> boxes = get();
showList.getChildren().clear();
int index = 0;
for (ShowBox showBox : boxes) {
showList.getChildren().add(showBox);
VBox.setMargin(showBox, new Insets(20, 20, 0, 20));
showList.add(showBox, index%2, index++/2);
GridPane.setMargin(showBox, new Insets(20, 20, 0, 20));
}
} catch (Exception e) {
LOG.error("Couldn't load upcoming camsoda shows", e);
}
setContent(showList);
setContent(new ScrollPane(showList));
});
}
};

View File

@ -25,11 +25,11 @@ public class CamsodaTabProvider extends TabProvider {
public List<Tab> getTabs(Scene scene) {
List<Tab> tabs = new ArrayList<>();
tabs.add(createTab("Online", BASE_URI + "/api/v1/browse/online"));
CamsodaFollowedTab followedTab = new CamsodaFollowedTab("Followed", camsoda);
followedTab.setRecorder(recorder);
followedTab.setScene(scene);
tabs.add(followedTab);
tabs.add(new CamsodaShowsTab(camsoda, recorder));
// ChaturbateFollowedTab followedTab = new ChaturbateFollowedTab("Followed", BASE_URI + "/followed-cams/", chaturbate);
// followedTab.setRecorder(recorder);
// followedTab.setScene(scene);
// tabs.add(followedTab);
return tabs;
}

View File

@ -2,7 +2,6 @@ package ctbrec.ui;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import com.iheartradio.m3u8.ParseException;
@ -20,14 +19,13 @@ import javafx.beans.property.SimpleBooleanProperty;
*/
public class JavaFxModel extends AbstractModel {
private transient BooleanProperty onlineProperty = new SimpleBooleanProperty();
private Model delegate;
public JavaFxModel(Model delegate) {
this.delegate = delegate;
try {
onlineProperty.set(Objects.equals("public", delegate.getOnlineState(true)));
} catch (IOException | ExecutionException e) {}
onlineProperty.set(delegate.isOnline());
} catch (IOException | ExecutionException | InterruptedException e) {}
}
@Override

View File

@ -13,6 +13,7 @@ import ctbrec.sites.Site;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
public class TokenLabel extends Label {
@ -26,10 +27,10 @@ public class TokenLabel extends Label {
CamrecApplication.bus.register(new Object() {
@Subscribe
public void tokensUpdates(Map<String, Object> e) {
if(Objects.equals("tokens", e.get("event"))) {
if (Objects.equals("tokens", e.get("event"))) {
tokens = (int) e.get("amount");
updateText();
} else if(Objects.equals("tokens.sent", e.get("event"))) {
} else if (Objects.equals("tokens.sent", e.get("event"))) {
int _tokens = (int) e.get("amount");
tokens -= _tokens;
updateText();
@ -70,7 +71,10 @@ public class TokenLabel extends Label {
update(tokens);
} catch (InterruptedException | ExecutionException e) {
LOG.error("Couldn't retrieve account balance", e);
Platform.runLater(() -> setText("Tokens: error"));
Platform.runLater(() -> {
setText("Tokens: error");
setTooltip(new Tooltip(e.getMessage()));
});
}
}
};