From 2f013bc8706b6f95f22cba3e05bbfd451f2d9962 Mon Sep 17 00:00:00 2001 From: 0xboobface <0xboobface@gmail.com> Date: Sat, 29 Feb 2020 18:30:16 +0100 Subject: [PATCH] Change BongaCams online check --- .../ctbrec/sites/bonga/BongaCamsModel.java | 72 +++++-- .../ctbrec/sites/bonga/BongaCamsModel.java | 193 ++++++++++++++++++ 2 files changed, 243 insertions(+), 22 deletions(-) create mode 100644 src/main/java/ctbrec/sites/bonga/BongaCamsModel.java diff --git a/common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java b/common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java index 1abd9ee3..53195f4f 100644 --- a/common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java +++ b/common/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java @@ -5,18 +5,20 @@ import static ctbrec.io.HttpConstants.*; import java.io.IOException; import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.concurrent.ExecutionException; +import org.json.JSONArray; import org.json.JSONObject; import org.jsoup.nodes.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Objects; import com.iheartradio.m3u8.Encoding; import com.iheartradio.m3u8.Format; import com.iheartradio.m3u8.ParseException; @@ -30,6 +32,7 @@ import com.iheartradio.m3u8.data.StreamInfo; import ctbrec.AbstractModel; import ctbrec.Config; +import ctbrec.Model; import ctbrec.io.HtmlParser; import ctbrec.io.HttpException; import ctbrec.recorder.download.StreamSource; @@ -40,29 +43,54 @@ import okhttp3.Response; public class BongaCamsModel extends AbstractModel { + private static final String ARGS = "args[]"; private static final Logger LOG = LoggerFactory.getLogger(BongaCamsModel.class); + private static final String SUCCESS = "success"; + private static final String STATUS = "status"; private int userId; private boolean online = false; - private List streamSources = new ArrayList<>(); + private transient List streamSources = new ArrayList<>(); private int[] resolution; @Override public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException { if (ignoreCache) { - String url = BongaCams.baseUrl + "/profile/" + getName(); + JSONObject roomData = getRoomData(); + JSONObject performerData = roomData.getJSONObject("performerData"); + setDisplayName(performerData.optString("displayName")); + String url = BongaCams.baseUrl + "/tools/listing_v3.php?livetab=&online_only=true&offset=0&model_search%5Bdisplay_name%5D%5Btext%5D=" + + URLEncoder.encode(getDisplayName(), StandardCharsets.UTF_8.name()) + "&_online_filter=0"; + LOG.trace("Online Check: {}", url); Request req = new Request.Builder() .url(url) .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgentMobile) + .header(ACCEPT, MIMETYPE_APPLICATION_JSON) + .header(ACCEPT_LANGUAGE, "en") + .header(X_REQUESTED_WITH, XML_HTTP_REQUEST) + .header(REFERER, getSite().getBaseUrl()) .build(); try (Response resp = site.getHttpClient().execute(req)) { String body = resp.body().string(); - Element span = HtmlParser.getTag(body, "div.online_status_block span"); - String status = span.text(); - if(status.toLowerCase().contains("online")) { - JSONObject roomData = getRoomData(); - String showType = roomData.getJSONObject("performerData").optString("showType"); - online = Objects.equal(showType, "public") && isStreamAvailable(); + LOG.trace(body); + JSONObject json = new JSONObject(body); + if (json.optString(STATUS).equals(SUCCESS)) { + JSONArray models = json.getJSONArray("models"); + for (int i = 0; i < models.length(); i++) { + JSONObject model = models.getJSONObject(i); + setDescription(model.optString("topic")); + String username = model.optString("username"); + if (username.equalsIgnoreCase(getName())) { + boolean away = model.optBoolean("is_away"); + String room = model.optString("room"); + online = !away + && model.optBoolean("online") + && room.equalsIgnoreCase("public") + && isStreamAvailable(); + onlineState = Model.State.ONLINE; + break; + } + } } else { online = false; } @@ -88,8 +116,8 @@ public class BongaCamsModel extends AbstractModel { String url = BongaCams.baseUrl + "/tools/amf.php"; RequestBody body = new FormBody.Builder() .add("method", "getRoomData") - .add("args[]", getName()) - .add("args[]", "false") + .add(ARGS, getName()) + .add(ARGS, "false") .build(); Request request = new Request.Builder() .url(url) @@ -166,7 +194,7 @@ public class BongaCamsModel extends AbstractModel { private String getStreamUrl() throws IOException { JSONObject roomData = getRoomData(); - if(roomData.optString("status").equals("success")) { + if(roomData.optString(STATUS).equals(SUCCESS)) { JSONObject localData = roomData.getJSONObject("localData"); String server = localData.getString("videoServerUrl"); return "https:" + server + "/hls/stream_" + getName() + "/playlist.m3u8"; @@ -183,12 +211,12 @@ public class BongaCamsModel extends AbstractModel { @Override public void receiveTip(Double tokens) throws IOException { String url = BongaCams.baseUrl + "/chat-ajax-amf-service?" + System.currentTimeMillis(); - int userId = ((BongaCamsHttpClient)site.getHttpClient()).getUserId(); + userId = ((BongaCamsHttpClient)site.getHttpClient()).getUserId(); RequestBody body = new FormBody.Builder() .add("method", "tipModel") - .add("args[]", getName()) - .add("args[]", Integer.toString(tokens.intValue())) - .add("args[]", Integer.toString(userId)) + .add(ARGS, getName()) + .add(ARGS, Integer.toString(tokens.intValue())) + .add(ARGS, Integer.toString(userId)) .add("args[3]", "") .build(); Request request = new Request.Builder() @@ -203,7 +231,7 @@ public class BongaCamsModel extends AbstractModel { try(Response response = site.getHttpClient().execute(request)) { if(response.isSuccessful()) { JSONObject json = new JSONObject(response.body().string()); - if(!json.optString("status").equals("success")) { + if(!json.optString(STATUS).equals(SUCCESS)) { LOG.error("Sending tip failed {}", json.toString(2)); throw new IOException("Sending tip failed"); } @@ -223,9 +251,9 @@ public class BongaCamsModel extends AbstractModel { if(!isOnline()) { return new int[2]; } - List streamSources = getStreamSources(); - Collections.sort(streamSources); - StreamSource best = streamSources.get(streamSources.size()-1); + List sources = getStreamSources(); + Collections.sort(sources); + StreamSource best = sources.get(sources.size()-1); resolution = new int[] {best.width, best.height}; } catch (InterruptedException e) { Thread.currentThread().interrupt(); @@ -264,7 +292,7 @@ public class BongaCamsModel extends AbstractModel { if(resp.isSuccessful()) { String msg = resp.body().string(); JSONObject json = new JSONObject(msg); - if(json.optBoolean("success")) { + if(json.optBoolean(SUCCESS)) { LOG.debug("Follow/Unfollow -> {}", msg); return true; } else { @@ -322,7 +350,7 @@ public class BongaCamsModel extends AbstractModel { if (resp.isSuccessful()) { String msg = resp.body().string(); JSONObject json = new JSONObject(msg); - if (json.optString("status").equals("success")) { + if (json.optString(STATUS).equals(SUCCESS)) { LOG.debug("Follow/Unfollow -> {}", msg); return true; } else { diff --git a/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java b/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java new file mode 100644 index 00000000..dba742fe --- /dev/null +++ b/src/main/java/ctbrec/sites/bonga/BongaCamsModel.java @@ -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 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 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 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; + } +}