Merge BongaCams implementation from feature branch to dev

This commit is contained in:
0xboobface 2018-11-06 19:32:49 +01:00
commit f26cb547b7
19 changed files with 1043 additions and 7 deletions

View File

@ -4,6 +4,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
@ -78,5 +80,16 @@ public class CookieJarImpl implements CookieJar {
return host;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Entry<String, List<Cookie>> entry : cookieStore.entrySet()) {
sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n');
}
return sb.toString();
}
protected Map<String, List<Cookie>> getCookies() {
return cookieStore;
}
}

View File

@ -0,0 +1,79 @@
package ctbrec.io;
import java.io.IOException;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
import okhttp3.Cookie;
import okhttp3.Cookie.Builder;
public class CookieJsonAdapter extends JsonAdapter<Cookie> {
@Override
public Cookie fromJson(JsonReader reader) throws IOException {
Builder builder = new Cookie.Builder();
// domain
reader.nextName();
String domain = reader.nextString();
builder.domain(domain);
// expiresAt
reader.nextName();
builder.expiresAt(reader.nextLong());
// host only
reader.nextName();
if(reader.nextBoolean()) {
builder.hostOnlyDomain(domain);
}
// http only
reader.nextName();
if(reader.nextBoolean()) {
builder.httpOnly();
}
// name
reader.nextName();
builder.name(reader.nextString());
// path
reader.nextName();
builder.path(reader.nextString());
// persistent
reader.nextName();
if(reader.nextBoolean()) {
// noop
}
// secure
reader.nextName();
if(reader.nextBoolean()) {
builder.secure();
}
// value
reader.nextName();
builder.value(reader.nextString());
return builder.build();
}
@Override
public void toJson(JsonWriter writer, Cookie cookie) throws IOException {
writer.beginObject();
writer.name("domain").value(cookie.domain());
writer.name("expiresAt").value(cookie.expiresAt());
writer.name("hostOnly").value(cookie.hostOnly());
writer.name("httpOnly").value(cookie.httpOnly());
writer.name("name").value(cookie.name());
writer.name("path").value(cookie.path());
writer.name("persistent").value(cookie.persistent());
writer.name("secure").value(cookie.secure());
writer.name("value").value(cookie.value());
writer.endObject();
}
}

View File

@ -3,11 +3,20 @@ package ctbrec.io;
import java.io.IOException;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.squareup.moshi.JsonAdapter;
import com.squareup.moshi.Moshi;
import ctbrec.Config;
import ctbrec.Settings.ProxyType;
import okhttp3.ConnectionPool;
import okhttp3.Cookie;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.OkHttpClient.Builder;
@ -16,12 +25,16 @@ import okhttp3.Response;
import okhttp3.Route;
public abstract class HttpClient {
private static final transient Logger LOG = LoggerFactory.getLogger(HttpClient.class);
protected OkHttpClient client;
protected CookieJarImpl cookieJar = new CookieJarImpl();
protected boolean loggedIn = false;
protected int loginTries = 0;
private String name;
protected HttpClient() {
protected HttpClient(String name) {
this.name = name;
reconfigure();
}
@ -112,10 +125,23 @@ public abstract class HttpClient {
}
public void shutdown() {
persistCookies();
client.connectionPool().evictAll();
client.dispatcher().executorService().shutdown();
}
private void persistCookies() {
try {
Map<String, List<Cookie>> cookies = cookieJar.getCookies();
Moshi moshi = new Moshi.Builder().add(Cookie.class, new CookieJsonAdapter()).build();
@SuppressWarnings("rawtypes")
JsonAdapter<Map> adapter = moshi.adapter(Map.class).indent(" ");
String json = adapter.toJson(cookies);
} catch (Exception e) {
LOG.error("Couldn't persist cookies for {}", name, e);
}
}
private okhttp3.Authenticator createHttpProxyAuthenticator(String username, String password) {
return new okhttp3.Authenticator() {
@Override

View File

@ -20,6 +20,7 @@ import ctbrec.Config;
import ctbrec.recorder.LocalRecorder;
import ctbrec.recorder.Recorder;
import ctbrec.sites.Site;
import ctbrec.sites.bonga.BongaCams;
import ctbrec.sites.cam4.Cam4;
import ctbrec.sites.camsoda.Camsoda;
import ctbrec.sites.chaturbate.Chaturbate;
@ -66,6 +67,7 @@ public class HttpServer {
sites.add(new MyFreeCams());
sites.add(new Camsoda());
sites.add(new Cam4());
sites.add(new BongaCams());
}
private void addShutdownHook() {

View File

@ -6,6 +6,10 @@ import ctbrec.io.HttpClient;
public class RecorderHttpClient extends HttpClient {
public RecorderHttpClient() {
super("recorder");
}
@Override
public boolean login() throws IOException {
return false;

View File

@ -0,0 +1,150 @@
package ctbrec.sites.bonga;
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.sites.ConfigUI;
import ctbrec.ui.TabProvider;
import okhttp3.FormBody;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class BongaCams extends AbstractSite {
public static final String BASE_URL = "https://bongacams.com";
private BongaCamsHttpClient httpClient;
private Recorder recorder;
@Override
public String getName() {
return "BongaCams";
}
@Override
public String getBaseUrl() {
return BASE_URL;
}
@Override
public String getAffiliateLink() {
return "http://bongacams2.com/track?c=610249";
}
@Override
public void setRecorder(Recorder recorder) {
this.recorder = recorder;
}
@Override
public TabProvider getTabProvider() {
return new BongaCamsTabProvider(recorder, this);
}
@Override
public Model createModel(String name) {
BongaCamsModel model = new BongaCamsModel();
model.setName(name);
model.setUrl(BASE_URL + '/' + name);
model.setDescription("");
model.setSite(this);
return model;
}
@Override
public Integer getTokenBalance() throws IOException {
int userId = ((BongaCamsHttpClient)getHttpClient()).getUserId();
String url = BongaCams.BASE_URL + "/tools/amf.php";
RequestBody body = new FormBody.Builder()
.add("method", "ping")
.add("args[]", Integer.toString(userId))
.build();
Request request = new Request.Builder()
.url(url)
.addHeader("User-Agent", "Mozilla/5.0 (Android 9.0; Mobile; rv:61.0) Gecko/61.0 Firefox/61.0")
.addHeader("Accept", "application/json, text/javascript, */*")
.addHeader("Accept-Language", "en")
.addHeader("Referer", BongaCams.BASE_URL)
.addHeader("X-Requested-With", "XMLHttpRequest")
.post(body)
.build();
try(Response response = getHttpClient().execute(request, true)) {
if(response.isSuccessful()) {
JSONObject json = new JSONObject(response.body().string());
if(json.optString("status").equals("online")) {
JSONObject userData = json.getJSONObject("userData");
return userData.getInt("balance");
} else {
throw new IOException("Request was not successful: " + json.toString(2));
}
} else {
throw new IOException(response.code() + " " + response.message());
}
}
}
@Override
public String getBuyTokensLink() {
return getAffiliateLink();
}
@Override
public void login() throws IOException {
getHttpClient().login();
}
@Override
public HttpClient getHttpClient() {
if(httpClient == null) {
httpClient = new BongaCamsHttpClient();
}
return httpClient;
}
@Override
public void init() throws IOException {
}
@Override
public void shutdown() {
if(httpClient != null) {
httpClient.shutdown();
}
}
@Override
public boolean supportsTips() {
return true;
}
@Override
public boolean supportsFollow() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isSiteForModel(Model m) {
return m instanceof BongaCamsModel;
}
@Override
public ConfigUI getConfigurationGui() {
return new BongaCamsConfigUI(this);
}
@Override
public boolean credentialsAvailable() {
String username = Config.getInstance().getSettings().bongaUsername;
return username != null && !username.trim().isEmpty();
}
}

View File

@ -0,0 +1,54 @@
package ctbrec.sites.bonga;
import ctbrec.Config;
import ctbrec.sites.ConfigUI;
import ctbrec.ui.DesktopIntergation;
import ctbrec.ui.SettingsTab;
import javafx.geometry.Insets;
import javafx.scene.Parent;
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;
public class BongaCamsConfigUI implements ConfigUI {
private BongaCams bongaCams;
public BongaCamsConfigUI(BongaCams bongaCams) {
this.bongaCams = bongaCams;
}
@Override
public Parent createConfigPanel() {
GridPane layout = SettingsTab.createGridLayout();
layout.add(new Label("BongaCams User"), 0, 0);
TextField username = new TextField(Config.getInstance().getSettings().bongaUsername);
username.focusedProperty().addListener((e) -> Config.getInstance().getSettings().bongaUsername = username.getText());
GridPane.setFillWidth(username, true);
GridPane.setHgrow(username, Priority.ALWAYS);
GridPane.setColumnSpan(username, 2);
layout.add(username, 1, 0);
layout.add(new Label("BongaCams Password"), 0, 1);
PasswordField password = new PasswordField();
password.setText(Config.getInstance().getSettings().bongaPassword);
password.focusedProperty().addListener((e) -> Config.getInstance().getSettings().bongaPassword = 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(bongaCams.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,208 @@
package ctbrec.sites.bonga;
import java.io.IOException;
import java.net.HttpCookie;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ctbrec.io.HttpClient;
import javafx.application.Platform;
import okhttp3.Cookie;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class BongaCamsHttpClient extends HttpClient {
private static final transient Logger LOG = LoggerFactory.getLogger(BongaCamsHttpClient.class);
private int userId = 0;
public BongaCamsHttpClient() {
super("bongacams");
}
@Override
public synchronized boolean login() throws IOException {
if(loggedIn) {
return true;
}
BlockingQueue<Boolean> queue = new LinkedBlockingQueue<>();
Runnable showDialog = () -> {
// login with javafx WebView
BongaCamsLoginDialog loginDialog = new BongaCamsLoginDialog();
// transfer cookies from WebView to OkHttp cookie jar
transferCookies(loginDialog);
try {
queue.put(true);
} catch (InterruptedException e) {
LOG.error("Error while signaling termination", e);
}
};
if(Platform.isFxApplicationThread()) {
showDialog.run();
} else {
Platform.runLater(showDialog);
try {
queue.take();
} catch (InterruptedException e) {
LOG.error("Error while waiting for login dialog to close", e);
throw new IOException(e);
}
}
loggedIn = checkLoginSuccess();
if(loggedIn) {
LOG.info("Logged in. User ID is {}", userId);
} else {
LOG.info("Login failed");
}
return loggedIn;
}
/**
* Check, if the login worked by requesting roomdata and looking
* @throws IOException
*/
private boolean checkLoginSuccess() throws IOException {
String modelName = getAnyModelName();
// we request the roomData of a random model, because it contains
// user data, if the user is logged in, which we can use to verify, that the login worked
String url = BongaCams.BASE_URL + "/tools/amf.php";
RequestBody body = new FormBody.Builder()
.add("method", "getRoomData")
.add("args[]", modelName)
.add("args[]", "false")
//.add("method", "ping") // TODO alternative request, but
//.add("args[]", <userId>) // where to get the userId
.build();
Request request = new Request.Builder()
.url(url)
.addHeader("User-Agent", "Mozilla/5.0 (Android 9.0; Mobile; rv:61.0) Gecko/61.0 Firefox/61.0")
.addHeader("Accept", "application/json, text/javascript, */*")
.addHeader("Accept-Language", "en")
.addHeader("Referer", BongaCams.BASE_URL)
.addHeader("X-Requested-With", "XMLHttpRequest")
.post(body)
.build();
try(Response response = execute(request)) {
if(response.isSuccessful()) {
JSONObject json = new JSONObject(response.body().string());
if(json.optString("status").equals("success")) {
JSONObject userData = json.getJSONObject("userData");
userId = userData.optInt("userId");
return userId > 0;
} else {
throw new IOException("Request was not successful: " + json.toString(2));
}
} else {
throw new IOException(response.code() + " " + response.message());
}
}
}
/**
* Fetches the list of online models and returns the name of the first model
*/
private String getAnyModelName() throws IOException {
Request request = new Request.Builder()
.url(BongaCams.BASE_URL + "/tools/listing_v3.php?livetab=female&online_only=true&is_mobile=true&offset=0")
.addHeader("User-Agent", "Mozilla/5.0 (Android 9.0; Mobile; rv:61.0) Gecko/61.0 Firefox/61.0")
.addHeader("Accept", "application/json, text/javascript, */*")
.addHeader("Accept-Language", "en")
.addHeader("Referer", BongaCams.BASE_URL)
.addHeader("X-Requested-With", "XMLHttpRequest")
.build();
try(Response response = execute(request)) {
if (response.isSuccessful()) {
String content = response.body().string();
JSONObject json = new JSONObject(content);
if(json.optString("status").equals("success")) {
JSONArray _models = json.getJSONArray("models");
JSONObject m = _models.getJSONObject(0);
String name = m.getString("username");
return name;
} else {
throw new IOException("Request was not successful: " + content);
}
} else {
throw new IOException(response.code() + ' ' + response.message());
}
}
}
private void transferCookies(BongaCamsLoginDialog loginDialog) {
HttpUrl redirectedUrl = HttpUrl.parse(loginDialog.getUrl());
List<Cookie> cookies = new ArrayList<>();
for (HttpCookie webViewCookie : loginDialog.getCookies()) {
Cookie cookie = Cookie.parse(redirectedUrl, webViewCookie.toString());
cookies.add(cookie);
}
cookieJar.saveFromResponse(redirectedUrl, cookies);
HttpUrl origUrl = HttpUrl.parse(BongaCamsLoginDialog.URL);
cookies = new ArrayList<>();
for (HttpCookie webViewCookie : loginDialog.getCookies()) {
Cookie cookie = Cookie.parse(origUrl, webViewCookie.toString());
cookies.add(cookie);
}
cookieJar.saveFromResponse(origUrl, cookies);
}
// @Override
// public boolean login() throws IOException {
// String url = BongaCams.BASE_URL + "/login";
// String dateTime = new SimpleDateFormat("d.MM.yyyy', 'HH:mm:ss").format(new Date());
// RequestBody body = new FormBody.Builder()
// .add("security_log_additional_info","{\"language\":\"en\",\"cookieEnabled\":true,\"javaEnabled\":false,\"flashVersion\":\"31.0.0\",\"dateTime\":\""+dateTime+"\",\"ips\":[\"192.168.0.1\"]}")
// .add("log_in[username]", Config.getInstance().getSettings().bongaUsername)
// .add("log_in[password]", Config.getInstance().getSettings().bongaPassword)
// .add("log_in[remember]", "1")
// .add("log_in[bfpt]", "")
// .add("header_form", "1")
// .build();
// Request request = new Request.Builder()
// .url(url)
// .post(body)
// .addHeader("User-Agent", "Mozilla/5.0 (Android 9.0; Mobile; rv:61.0) Gecko/61.0 Firefox/61.0")
// .addHeader("Accept","application/json")
// .addHeader("Accept-Language", "en")
// .addHeader("Referer", BongaCams.BASE_URL)
// .addHeader("X-Requested-With", "XMLHttpRequest")
// .build();
// try(Response response = execute(request)) {
// if(response.isSuccessful()) {
// JSONObject json = new JSONObject(response.body().string());
// if(json.optString("status").equals("success")) {
// return true;
// } else {
// LOG.debug("Login response: {}", json.toString(2));
// Platform.runLater(() -> new BongaCamsLoginDialog());
// throw new IOException("Login not successful");
// }
// } else {
// throw new IOException(response.code() + " " + response.message());
// }
// }
// }
public int getUserId() throws IOException {
if(userId == 0) {
login();
}
return userId;
}
}

View File

@ -0,0 +1,118 @@
package ctbrec.sites.bonga;
import java.io.File;
import java.io.InputStream;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.HttpCookie;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ctbrec.Config;
import ctbrec.OS;
import javafx.concurrent.Worker.State;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.image.Image;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class BongaCamsLoginDialog {
private static final transient Logger LOG = LoggerFactory.getLogger(BongaCamsLoginDialog.class);
public static final String URL = BongaCams.BASE_URL + "/login";
private List<HttpCookie> cookies = null;
private String url;
private Region veil;
private ProgressIndicator p;
public BongaCamsLoginDialog() {
Stage stage = new Stage();
stage.setTitle("BongaCams Login");
InputStream icon = getClass().getResourceAsStream("/icon.png");
stage.getIcons().add(new Image(icon));
CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager);
WebView webView = createWebView(stage);
veil = new Region();
veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.4)");
p = new ProgressIndicator();
p.setMaxSize(140, 140);
StackPane stackPane = new StackPane();
stackPane.getChildren().addAll(webView, veil, p);
stage.setScene(new Scene(stackPane, 640, 480));
stage.showAndWait();
cookies = cookieManager.getCookieStore().getCookies();
}
private WebView createWebView(Stage stage) {
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();
webEngine.setJavaScriptEnabled(true);
webEngine.locationProperty().addListener((obs, oldV, newV) -> {
try {
URL _url = new URL(newV);
if (Objects.equals(_url.getPath(), "/")) {
stage.close();
}
} catch (MalformedURLException e) {
LOG.error("Couldn't parse new url {}", newV, e);
}
url = newV.toString();
});
webEngine.getLoadWorker().stateProperty().addListener((observable, oldState, newState) -> {
if (newState == State.SUCCEEDED) {
veil.setVisible(false);
p.setVisible(false);
//System.out.println("############# " + webEngine.getLocation());
//System.out.println(webEngine.getDocument().getDocumentElement().getTextContent());
try {
String username = Config.getInstance().getSettings().bongaUsername;
if (username != null && !username.trim().isEmpty()) {
webEngine.executeScript("$('input[name=\"log_in[username]\"]').attr('value','" + username + "')");
}
String password = Config.getInstance().getSettings().bongaPassword;
if (password != null && !password.trim().isEmpty()) {
webEngine.executeScript("$('input[name=\"log_in[password]\"]').attr('value','" + password + "')");
}
webEngine.executeScript("$('div[class~=\"fancybox-overlay\"]').css('display','none')");
webEngine.executeScript("$('div#header').css('display','none')");
webEngine.executeScript("$('div.footer').css('display','none')");
webEngine.executeScript("$('div.footer_copy').css('display','none')");
webEngine.executeScript("$('div[class~=\"banner_top_index\"]').css('display','none')");
webEngine.executeScript("$('td.menu_container').css('display','none')");
} catch(Exception e) {
LOG.warn("Couldn't auto fill username and password for BongaCams", e);
}
} else if (newState == State.CANCELLED || newState == State.FAILED) {
veil.setVisible(false);
p.setVisible(false);
}
});
webEngine.setUserDataDirectory(new File(OS.getConfigDir(), "webengine"));
webEngine.load(URL);
return browser;
}
public List<HttpCookie> getCookies() {
// for (HttpCookie httpCookie : cookies) {
// LOG.debug("Cookie: {}", httpCookie);
// }
return cookies;
}
public String getUrl() {
return url;
}
}

View File

@ -0,0 +1,226 @@
package ctbrec.sites.bonga;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.iheartradio.m3u8.Encoding;
import com.iheartradio.m3u8.Format;
import com.iheartradio.m3u8.ParseException;
import com.iheartradio.m3u8.PlaylistException;
import com.iheartradio.m3u8.PlaylistParser;
import com.iheartradio.m3u8.data.MasterPlaylist;
import com.iheartradio.m3u8.data.Playlist;
import com.iheartradio.m3u8.data.PlaylistData;
import com.iheartradio.m3u8.data.StreamInfo;
import ctbrec.AbstractModel;
import ctbrec.recorder.download.StreamSource;
import ctbrec.sites.Site;
import okhttp3.FormBody;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class BongaCamsModel extends AbstractModel {
private static final transient Logger LOG = LoggerFactory.getLogger(BongaCamsModel.class);
private BongaCams site;
private int userId;
private String onlineState = "n/a";
private boolean online = false;
private List<StreamSource> streamSources = new ArrayList<>();
private int[] resolution;
@Override
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
return online;
}
public void setOnline(boolean online) {
this.online = online;
}
@Override
public String getOnlineState(boolean failFast) throws IOException, ExecutionException {
return onlineState;
}
public void setOnlineState(String onlineState) {
this.onlineState = onlineState;
}
@Override
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException {
String streamUrl = getStreamUrl();
if (streamUrl == null) {
return Collections.emptyList();
}
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();
for (PlaylistData playlistData : master.getPlaylists()) {
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.add(streamsource);
}
} finally {
response.close();
}
return streamSources;
}
private String getStreamUrl() throws IOException {
String url = BongaCams.BASE_URL + "/tools/amf.php";
RequestBody body = new FormBody.Builder()
.add("method", "getRoomData")
.add("args[]", getName())
.add("args[]", "false")
.build();
Request request = new Request.Builder()
.url(url)
.addHeader("User-Agent", "Mozilla/5.0 (Android 9.0; Mobile; rv:61.0) Gecko/61.0 Firefox/61.0")
.addHeader("Accept", "application/json, text/javascript, */*")
.addHeader("Accept-Language", "en")
.addHeader("Referer", BongaCams.BASE_URL)
.addHeader("X-Requested-With", "XMLHttpRequest")
.post(body)
.build();
try(Response response = site.getHttpClient().execute(request)) {
if(response.isSuccessful()) {
JSONObject json = new JSONObject(response.body().string());
if(json.optString("status").equals("success")) {
JSONObject localData = json.getJSONObject("localData");
String server = localData.getString("videoServerUrl");
return "https:" + server + "/hls/stream_" + getName() + "/playlist.m3u8";
} else {
throw new IOException("Request was not successful: " + json.toString(2));
}
} else {
throw new IOException(response.code() + " " + response.message());
}
}
}
@Override
public void invalidateCacheEntries() {
// TODO Auto-generated method stub
}
@Override
public void receiveTip(int tokens) throws IOException {
// method=tipModel&args[]=Sweetsexbia&args[]=1&args[]=66050808&args[3]=&_csrf_token=dd304a3876025127cc487e71d44a5843
String url = BongaCams.BASE_URL + "/chat-ajax-amf-service?" + System.currentTimeMillis();
int userId = ((BongaCamsHttpClient)site.getHttpClient()).getUserId();
RequestBody body = new FormBody.Builder()
.add("method", "tipModel")
.add("args[]", getName())
.add("args[]", Integer.toString(tokens))
.add("args[]", Integer.toString(userId))
.add("args[3]", "")
.build();
Request request = new Request.Builder()
.url(url)
.addHeader("User-Agent", "Mozilla/5.0 (Android 9.0; Mobile; rv:61.0) Gecko/61.0 Firefox/61.0")
.addHeader("Accept", "application/json, text/javascript, */*")
.addHeader("Accept-Language", "en")
.addHeader("Referer", BongaCams.BASE_URL + '/' + getName())
.addHeader("X-Requested-With", "XMLHttpRequest")
.post(body)
.build();
try(Response response = site.getHttpClient().execute(request, true)) {
if(response.isSuccessful()) {
// {
// "dataKey": "d40f579faf592324c1b0b97bd711039f",
// "amount": "1",
// "balance": 11,
// "actionKey": "b60f780c472e83b95167efe9bc9512bf",
// "description": "Sie haben erfolgreich Sweetsexbia 1 Token Trinkgeld gegeben!",
// "status": "success"
// }
JSONObject json = new JSONObject(response.body().string());
System.out.println(json.toString(2));
} else {
throw new IOException(response.code() + ' ' + response.message());
}
}
}
@Override
public int[] getStreamResolution(boolean failFast) throws ExecutionException {
if(resolution == null) {
if(failFast) {
return new int[2];
}
try {
List<StreamSource> streamSources = getStreamSources();
Collections.sort(streamSources);
StreamSource best = streamSources.get(streamSources.size()-1);
resolution = new int[] {best.width, best.height};
} catch (ExecutionException | IOException | ParseException | PlaylistException e) {
LOG.error("Couldn't determine stream resolution", e);
}
return resolution;
} else {
return resolution;
}
}
@Override
public boolean follow() throws IOException {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean unfollow() throws IOException {
// TODO Auto-generated method stub
return false;
}
@Override
public void setSite(Site site) {
if(site instanceof BongaCams) {
this.site = (BongaCams) site;
} else {
throw new IllegalArgumentException("Site has to be an instance of BongaCams");
}
}
@Override
public Site getSite() {
return site;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
}

View File

@ -0,0 +1,66 @@
package ctbrec.sites.bonga;
import java.util.ArrayList;
import java.util.List;
import ctbrec.recorder.Recorder;
import ctbrec.ui.PaginatedScheduledService;
import ctbrec.ui.TabProvider;
import ctbrec.ui.ThumbOverviewTab;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
public class BongaCamsTabProvider extends TabProvider {
private BongaCams bongaCams;
private Recorder recorder;
public BongaCamsTabProvider(Recorder recorder, BongaCams bongaCams) {
this.recorder = recorder;
this.bongaCams = bongaCams;
}
@Override
public List<Tab> getTabs(Scene scene) {
List<Tab> tabs = new ArrayList<>();
// female
String url = BongaCams.BASE_URL + "/tools/listing_v3.php?livetab=female&online_only=true&is_mobile=true&offset=";
BongaCamsUpdateService updateService = new BongaCamsUpdateService(bongaCams, url);
tabs.add(createTab("Female", updateService));
// male
url = BongaCams.BASE_URL + "/tools/listing_v3.php?livetab=male&online_only=true&is_mobile=true&offset=";
updateService = new BongaCamsUpdateService(bongaCams, url);
tabs.add(createTab("Male", updateService));
// couples
url = BongaCams.BASE_URL + "/tools/listing_v3.php?livetab=couples&online_only=true&is_mobile=true&offset=";
updateService = new BongaCamsUpdateService(bongaCams, url);
tabs.add(createTab("Couples", updateService));
// trans
url = BongaCams.BASE_URL + "/tools/listing_v3.php?livetab=transsexual&online_only=true&is_mobile=true&offset=";
updateService = new BongaCamsUpdateService(bongaCams, url);
tabs.add(createTab("Transsexual", updateService));
// new
url = BongaCams.BASE_URL + "/tools/listing_v3.php?livetab=new-models&online_only=true&is_mobile=true&offset=";
updateService = new BongaCamsUpdateService(bongaCams, url);
tabs.add(createTab("New", updateService));
// friends
url = BongaCams.BASE_URL + "/tools/listing_v3.php?livetab=friends&online_only=true&offset=";
updateService = new BongaCamsUpdateService(bongaCams, url);
tabs.add(createTab("Friends", updateService));
return tabs;
}
private Tab createTab(String title, PaginatedScheduledService updateService) {
ThumbOverviewTab tab = new ThumbOverviewTab(title, updateService, bongaCams);
tab.setRecorder(recorder);
return tab;
}
}

View File

@ -0,0 +1,72 @@
package ctbrec.sites.bonga;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ctbrec.Model;
import ctbrec.ui.PaginatedScheduledService;
import javafx.concurrent.Task;
import okhttp3.Request;
import okhttp3.Response;
public class BongaCamsUpdateService extends PaginatedScheduledService {
private static final transient Logger LOG = LoggerFactory.getLogger(BongaCamsUpdateService.class);
private BongaCams bongaCams;
private String url;
public BongaCamsUpdateService(BongaCams bongaCams, String url) {
this.bongaCams = bongaCams;
this.url = url;
}
@Override
protected Task<List<Model>> createTask() {
return new Task<List<Model>>() {
@Override
public List<Model> call() throws IOException {
String _url = url + ((page-1) * 50);
LOG.debug("Fetching page {}", _url);
Request request = new Request.Builder()
.url(_url)
.addHeader("User-Agent", "Mozilla/5.0 (Android 9.0; Mobile; rv:61.0) Gecko/61.0 Firefox/61.0")
.addHeader("Accept", "application/json, text/javascript, */*")
.addHeader("Accept-Language", "en")
.addHeader("Referer", bongaCams.getBaseUrl())
.addHeader("X-Requested-With", "XMLHttpRequest")
.build();
Response response = bongaCams.getHttpClient().execute(request);
if (response.isSuccessful()) {
String content = response.body().string();
List<Model> models = new ArrayList<>();
JSONObject json = new JSONObject(content);
if(json.optString("status").equals("success")) {
JSONArray _models = json.getJSONArray("models");
for (int i = 0; i < _models.length(); i++) {
JSONObject m = _models.getJSONObject(i);
String name = m.getString("username");
BongaCamsModel model = (BongaCamsModel) bongaCams.createModel(name);
model.setUserId(m.getInt("user_id"));
model.setOnlineState(m.getString("room"));
model.setOnline(m.optBoolean("online") && !m.optBoolean("is_away"));
model.setPreview("https:" + m.getString("thumb_image"));
models.add(model);
}
}
return models;
} else {
int code = response.code();
response.close();
throw new IOException("HTTP status " + code);
}
}
};
}
}

View File

@ -23,6 +23,10 @@ public class Cam4HttpClient extends HttpClient {
private static final transient Logger LOG = LoggerFactory.getLogger(Cam4HttpClient.class);
public Cam4HttpClient() {
super("cam4");
}
@Override
public synchronized boolean login() throws IOException {
if(loggedIn) {

View File

@ -29,6 +29,10 @@ public class CamsodaHttpClient extends HttpClient {
private static final transient Logger LOG = LoggerFactory.getLogger(CamsodaHttpClient.class);
private String csrfToken = null;
public CamsodaHttpClient() {
super("camsoda");
}
@Override
public boolean login() throws IOException {
if(loggedIn) {

View File

@ -195,8 +195,8 @@ public class CamsodaShowsTab extends Tab implements TabSelectionListener {
root.setCenter(grid);
loadImage(model, thumb);
record.prefWidthProperty().bind(openInBrowser.widthProperty());
follow.prefWidthProperty().bind(openInBrowser.widthProperty());
record.minWidthProperty().bind(openInBrowser.widthProperty());
follow.minWidthProperty().bind(openInBrowser.widthProperty());
}
private void follow(Model model) {

View File

@ -20,6 +20,10 @@ public class ChaturbateHttpClient extends HttpClient {
private static final transient Logger LOG = LoggerFactory.getLogger(ChaturbateHttpClient.class);
protected String token;
public ChaturbateHttpClient() {
super("chaturbate");
}
private void extractCsrfToken(Request request) {
try {
Cookie csrfToken = cookieJar.getCookie(request.url(), "csrftoken");

View File

@ -24,6 +24,10 @@ public class MyFreeCamsHttpClient extends HttpClient {
private static final transient Logger LOG = LoggerFactory.getLogger(MyFreeCamsHttpClient.class);
public MyFreeCamsHttpClient() {
super("myfreecams");
}
@Override
public boolean login() throws IOException {
if(loggedIn) {

View File

@ -27,6 +27,7 @@ import ctbrec.recorder.LocalRecorder;
import ctbrec.recorder.Recorder;
import ctbrec.recorder.RemoteRecorder;
import ctbrec.sites.Site;
import ctbrec.sites.bonga.BongaCams;
import ctbrec.sites.cam4.Cam4;
import ctbrec.sites.camsoda.Camsoda;
import ctbrec.sites.chaturbate.Chaturbate;
@ -60,10 +61,11 @@ public class CamrecApplication extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
sites.add(new BongaCams());
sites.add(new Cam4());
sites.add(new Camsoda());
sites.add(new Chaturbate());
sites.add(new MyFreeCams());
sites.add(new Camsoda());
sites.add(new Cam4());
loadConfig();
createHttpClient();
bus = new AsyncEventBus(Executors.newSingleThreadExecutor());
@ -198,7 +200,7 @@ public class CamrecApplication extends Application {
}
private void createHttpClient() {
httpClient = new HttpClient() {
httpClient = new HttpClient("camrec") {
@Override
public boolean login() throws IOException {
return false;

View File

@ -208,7 +208,7 @@ public class ThumbCell extends StackPane {
LOG.trace("Removing invalid resolution value for {}", model.getName());
model.invalidateCacheEntries();
}
Thread.sleep(500);
} catch (IOException | InterruptedException e1) {
LOG.warn("Couldn't update resolution tag for model {}", model.getName(), e1);