Add BongaCams

This is the first working version. Follow / unfollow and tipping are not
implemented.
This commit is contained in:
0xboobface 2018-11-05 00:41:22 +01:00
parent 29ed8648e4
commit 1fec124bbc
7 changed files with 445 additions and 0 deletions

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

@ -0,0 +1,123 @@
package ctbrec.sites.bonga;
import java.io.IOException;
import ctbrec.Model;
import ctbrec.io.HttpClient;
import ctbrec.recorder.Recorder;
import ctbrec.sites.AbstractSite;
import ctbrec.ui.TabProvider;
import javafx.scene.Node;
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 BASE_URL;
}
@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 {
// TODO Auto-generated method stub
return 0;
}
@Override
public String getBuyTokensLink() {
// TODO Auto-generated method stub
return getBaseUrl();
}
@Override
public void login() throws IOException {
// TODO Auto-generated method stub
}
@Override
public HttpClient getHttpClient() {
if(httpClient == null) {
httpClient = new BongaCamsHttpClient();
}
return httpClient;
}
@Override
public void init() throws IOException {
// TODO Auto-generated method stub
}
@Override
public void shutdown() {
// TODO Auto-generated method stub
}
@Override
public boolean supportsTips() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean supportsFollow() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean isSiteForModel(Model m) {
// TODO Auto-generated method stub
return false;
}
@Override
public Node getConfigurationGui() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean credentialsAvailable() {
// TODO Auto-generated method stub
return false;
}
}

View File

@ -0,0 +1,15 @@
package ctbrec.sites.bonga;
import java.io.IOException;
import ctbrec.io.HttpClient;
public class BongaCamsHttpClient extends HttpClient {
@Override
public boolean login() throws IOException {
// TODO Auto-generated method stub
return false;
}
}

View File

@ -0,0 +1,193 @@
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 {
// TODO Auto-generated method stub
}
@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,39 @@
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<>();
BongaCamsUpdateService updateService = new BongaCamsUpdateService(bongaCams);
tabs.add(createTab("Online", 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,71 @@
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;
public BongaCamsUpdateService(BongaCams bongaCams) {
this.bongaCams = bongaCams;
}
@Override
protected Task<List<Model>> createTask() {
return new Task<List<Model>>() {
@Override
public List<Model> call() throws IOException {
String url = BongaCams.BASE_URL + "/tools/listing_v3.php?livetab=female&online_only=true&is_mobile=true&offset=" + ((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();
response.close();
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

@ -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;
@ -64,6 +65,7 @@ public class CamrecApplication extends Application {
sites.add(new MyFreeCams());
sites.add(new Camsoda());
sites.add(new Cam4());
sites.add(new BongaCams());
loadConfig();
createHttpClient();
bus = new AsyncEventBus(Executors.newSingleThreadExecutor());