diff --git a/client/src/main/java/ctbrec/ui/sites/streamate/StreamateTabProvider.java b/client/src/main/java/ctbrec/ui/sites/streamate/StreamateTabProvider.java index 5c74b825..137854a5 100644 --- a/client/src/main/java/ctbrec/ui/sites/streamate/StreamateTabProvider.java +++ b/client/src/main/java/ctbrec/ui/sites/streamate/StreamateTabProvider.java @@ -1,8 +1,6 @@ package ctbrec.ui.sites.streamate; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -30,7 +28,15 @@ public class StreamateTabProvider extends TabProvider { public List getTabs(Scene scene) { List tabs = new ArrayList<>(); try { - tabs.add(createTab("Girls", "/ctbrec/ui/sites/streamate/girls.sml")); + tabs.add(createTab("Girls", Streamate.BASE_URL + "/api/search/list?domain=streamate.com&index=availperf&filters=gender:f")); + tabs.add(createTab("Guys", Streamate.BASE_URL + "/api/search/list?domain=streamate.com&index=availperf&filters=gender:m")); + tabs.add(createTab("Couples", Streamate.BASE_URL + "/api/search/list?domain=streamate.com&index=availperf&filters=gender:mf")); + tabs.add(createTab("Lesbian", Streamate.BASE_URL + "/api/search/list?domain=streamate.com&index=availperf&filters=gender:ff")); + tabs.add(createTab("Gay", Streamate.BASE_URL + "/api/search/list?domain=streamate.com&index=availperf&filters=gender:mm")); + tabs.add(createTab("Groups", Streamate.BASE_URL + "/api/search/list?domain=streamate.com&index=availperf&filters=gender:g")); + tabs.add(createTab("Trans female", Streamate.BASE_URL + "/api/search/list?domain=streamate.com&index=availperf&filters=gender:tm2f")); + tabs.add(createTab("Trans male", Streamate.BASE_URL + "/api/search/list?domain=streamate.com&index=availperf&filters=gender:tf2m")); + tabs.add(createTab("New", Streamate.BASE_URL + "/api/search/list?domain=streamate.com&index=availperf&filters=new:true")); } catch (IOException e) { LOG.error("Couldn't create streamate tab", e); } @@ -42,21 +48,10 @@ public class StreamateTabProvider extends TabProvider { return null; } - private Tab createTab(String title, String queryFile) throws IOException { - StreamateUpdateService updateService = new StreamateUpdateService(loadQuery(queryFile), streamate); + private Tab createTab(String title, String url) throws IOException { + StreamateUpdateService updateService = new StreamateUpdateService(streamate, url); ThumbOverviewTab tab = new ThumbOverviewTab(title, updateService, streamate); tab.setRecorder(recorder); return tab; } - - private String loadQuery(String file) throws IOException { - InputStream is = getClass().getResourceAsStream(file); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - byte[] b = new byte[1024]; - int len = -1; - while( (len = is.read(b)) >= 0) { - bos.write(b, 0, len); - } - return new String(bos.toByteArray(), "utf-8"); - } } diff --git a/client/src/main/java/ctbrec/ui/sites/streamate/StreamateUpdateService.java b/client/src/main/java/ctbrec/ui/sites/streamate/StreamateUpdateService.java index 7f9816c6..0703e662 100644 --- a/client/src/main/java/ctbrec/ui/sites/streamate/StreamateUpdateService.java +++ b/client/src/main/java/ctbrec/ui/sites/streamate/StreamateUpdateService.java @@ -1,44 +1,39 @@ package ctbrec.ui.sites.streamate; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPathExpressionException; +import org.json.JSONArray; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import ctbrec.Config; import ctbrec.Model; -import ctbrec.io.XmlParserUtils; +import ctbrec.io.HttpException; import ctbrec.sites.streamate.Streamate; import ctbrec.sites.streamate.StreamateModel; import ctbrec.ui.PaginatedScheduledService; import javafx.concurrent.Task; -import okhttp3.MediaType; import okhttp3.Request; -import okhttp3.RequestBody; import okhttp3.Response; public class StreamateUpdateService extends PaginatedScheduledService { private static final transient Logger LOG = LoggerFactory.getLogger(StreamateUpdateService.class); - private static final String URL = "http://affiliate.streamate.com/SMLive/SMLResult.xml"; + private static final int MODELS_PER_PAGE = 48; private Streamate streamate; - private String query; + private String url; - public StreamateUpdateService(String query, Streamate streamate) { - this.query = query; + public StreamateUpdateService(Streamate streamate, String url) { this.streamate = streamate; + this.url = url; } @Override @@ -46,47 +41,35 @@ public class StreamateUpdateService extends PaginatedScheduledService { return new Task>() { @Override public List call() throws IOException, SAXException, ParserConfigurationException, XPathExpressionException { - LOG.debug("Fetching page {}", URL); - String q = query - .replace("{maxresults}", "50") - .replace("{pagenum}", Integer.toString(page)); - //LOG.debug("Query:\n{}", q); - RequestBody body = RequestBody.create(MediaType.parse("text/xml"), q); + int from = (page - 1) * MODELS_PER_PAGE; + String _url = url + "&from=" + from + "&size=" + MODELS_PER_PAGE; + LOG.debug("Fetching page {}", _url); Request request = new Request.Builder() - .url(URL) + .url(_url) .addHeader("User-Agent", Config.getInstance().getSettings().httpUserAgent) - .addHeader("Accept", "text/xml, */*") + .addHeader("Accept", "application/json, */*") .addHeader("Accept-Language", "en") .addHeader("Referer", streamate.getBaseUrl()) - .post(body) .build(); - Response response = streamate.getHttpClient().execute(request); - if (response.isSuccessful()) { - List models = new ArrayList<>(); - String content = response.body().string(); - ByteArrayInputStream in = new ByteArrayInputStream(content.getBytes("utf-8")); - Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(in); - NodeList performers = doc.getElementsByTagName("Performer"); - for (int i = 0; i < performers.getLength(); i++) { - Node performer = performers.item(i); - String name = performer.getAttributes().getNamedItem("Name").getNodeValue(); - String id = performer.getAttributes().getNamedItem("Id").getNodeValue(); - String GoldShow = performer.getAttributes().getNamedItem("GoldShow").getNodeValue(); - String PreGoldShow = performer.getAttributes().getNamedItem("PreGoldShow").getNodeValue(); - String PartyChat = performer.getAttributes().getNamedItem("PartyChat").getNodeValue(); - StreamateModel model = (StreamateModel) streamate.createModel(name); - model.setId(id); - models.add(model); - Node pic = XmlParserUtils.getNodeWithXpath(performer, "Media/Pic/Full"); - String previewUrl = "https:" + pic.getAttributes().getNamedItem("Src").getNodeValue(); - model.setPreview(previewUrl); - //LOG.debug("Name {} - {}{}{}", name, PartyChat, PreGoldShow, GoldShow); + try(Response response = streamate.getHttpClient().execute(request)) { + if (response.isSuccessful()) { + List models = new ArrayList<>(); + String content = response.body().string(); + JSONObject json = new JSONObject(content); + JSONArray performers = json.getJSONArray("performers"); + for (int i = 0; i < performers.length(); i++) { + JSONObject p = performers.getJSONObject(i); + String nickname = p.getString("nickname"); + StreamateModel model = (StreamateModel) streamate.createModel(nickname); + model.setId(Long.toString(p.getLong("id"))); + model.setPreview(p.getString("thumbnail")); + model.setOnline(p.optBoolean("online")); + models.add(model); + } + return models; + } else { + throw new HttpException(response.code(), response.message()); } - return models; - } else { - int code = response.code(); - response.close(); - throw new IOException("HTTP status " + code); } } }; diff --git a/client/src/main/resources/ctbrec/ui/sites/streamate/girls.sml b/client/src/main/resources/ctbrec/ui/sites/streamate/girls.sml deleted file mode 100644 index f7841ae4..00000000 --- a/client/src/main/resources/ctbrec/ui/sites/streamate/girls.sml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - biopic, staticbiopic - - - - live,recorded - - - - \ No newline at end of file diff --git a/common/src/main/java/ctbrec/sites/streamate/StreamateModel.java b/common/src/main/java/ctbrec/sites/streamate/StreamateModel.java index 425009d7..66e58376 100644 --- a/common/src/main/java/ctbrec/sites/streamate/StreamateModel.java +++ b/common/src/main/java/ctbrec/sites/streamate/StreamateModel.java @@ -3,8 +3,6 @@ package ctbrec.sites.streamate; import static ctbrec.Model.State.*; import java.io.IOException; -import java.io.InputStream; -import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -15,16 +13,8 @@ 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.ParsingMode; 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.Config; @@ -45,10 +35,17 @@ public class StreamateModel extends AbstractModel { @Override public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException { if(ignoreCache) { - JSONObject roomInfo = getRoomInfo(); - JSONObject stream = roomInfo.getJSONObject("stream"); - String serverId = stream.optString("serverId"); - online = !serverId.equals("0"); + String url = "https://sea1c-ls.naiadsystems.com/sea1c-edge-ls/80/live/s:" + getName() + ".json"; + Request req = new Request.Builder().url(url) + .addHeader("User-Agent", Config.getInstance().getSettings().httpUserAgent) + .addHeader("Accept", "*/*") + .addHeader("Accept-Language", "en") + .addHeader("Referer", Streamate.BASE_URL + '/' + getName()) + .addHeader("X-Requested-With", "XMLHttpRequest") + .build(); + try(Response response = site.getHttpClient().execute(req)) { + online = response.isSuccessful(); + } } return online; } @@ -76,137 +73,48 @@ public class StreamateModel extends AbstractModel { @Override public List getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException { - String streamUrl = getStreamUrl(); - if (streamUrl == null) { - return Collections.emptyList(); - } - LOG.debug(streamUrl); - Request req = new Request.Builder().url(streamUrl).build(); - try(Response response = site.getHttpClient().execute(req)) { - if(response.isSuccessful()) { - InputStream inputStream = response.body().byteStream(); - PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8, ParsingMode.LENIENT); - Playlist playlist = parser.parse(); - MasterPlaylist master = playlist.getMasterPlaylist(); - streamSources.clear(); - for (PlaylistData playlistData : master.getPlaylists()) { - StreamSource streamsource = new StreamSource(); - streamsource.mediaPlaylistUrl = 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); - } - } else { - throw new HttpException(response.code(), response.message()); - } - } - return streamSources; - } - - private String getStreamUrl() throws IOException { - JSONObject json = getRoomInfo(); - JSONObject performer = json.getJSONObject("performer"); - id = Long.toString(performer.getLong("id")); - JSONObject stream = json.getJSONObject("stream"); - String sserver = stream.getString("serverId"); - String streamId = stream.getString("streamId"); - String wsHost = stream.getString("nodeHost"); - JSONObject liveservices = json.getJSONObject("liveservices"); - String streamHost = liveservices.getString("host").replace("wss", "https"); - - String roomId; - try { - roomId = getRoomId(wsHost, sserver, streamId); - LOG.debug("room id: {}", roomId); - } catch (InterruptedException e) { - throw new IOException("Couldn't get room id", e); - } - - String streamFormatUrl = getStreamFormatUrl(streamHost, roomId); - return getMasterPlaylistUrl(streamFormatUrl); - } - - private String getMasterPlaylistUrl(String url) throws IOException { - LOG.debug(url); - Request req = new Request.Builder() + String url = "https://sea1c-ls.naiadsystems.com/sea1c-edge-ls/80/live/s:" + getName() + ".json"; + Request req = new Request.Builder().url(url) .addHeader("User-Agent", Config.getInstance().getSettings().httpUserAgent) .addHeader("Accept", "*/*") .addHeader("Accept-Language", "en") .addHeader("Referer", Streamate.BASE_URL + '/' + getName()) .addHeader("X-Requested-With", "XMLHttpRequest") - .url(url) .build(); try(Response response = site.getHttpClient().execute(req)) { if(response.isSuccessful()) { JSONObject json = new JSONObject(response.body().string()); JSONObject formats = json.getJSONObject("formats"); + JSONObject ws = formats.getJSONObject("mp4-ws"); JSONObject hls = formats.getJSONObject("mp4-hls"); - return hls.getString("manifest"); - } else { - throw new HttpException(response.code(), response.message()); - } - } - } - - private String getStreamFormatUrl(String streamHost, String roomId) throws IOException { - String url = streamHost + "/videourl?payload=" - + URLEncoder.encode("{\"puserid\":" + id + ",\"roomid\":\"" + roomId + "\",\"showtype\":1,\"nginx\":1}", "utf-8"); - LOG.debug(url); - Request req = new Request.Builder() - .addHeader("User-Agent", Config.getInstance().getSettings().httpUserAgent) - .addHeader("Accept", "*/*") - .addHeader("Accept-Language", "en") - .addHeader("Referer", Streamate.BASE_URL + '/' + getName()) - .addHeader("X-Requested-With", "XMLHttpRequest") - .url(url) - .build(); - try(Response response = site.getHttpClient().execute(req)) { - if(response.isSuccessful()) { - JSONArray streamConfig = new JSONArray(response.body().string()); - JSONObject obj = streamConfig.getJSONObject(0); - return obj.getString("url"); - } else { - throw new HttpException(response.code(), response.message()); - } - } - } - - private String getRoomId(String wsHost, String sserver, String streamId) throws InterruptedException { - String wsUrl = wsHost + "/socket.io/?" - + "performerid=" + id - + "&sserver=" + sserver - + "&streamid=" + streamId - + "&sakey=&sessiontype=preview&perfdiscountid=0&minduration=0&goldshowid=0&version=7&referrer=hybrid.client.6.3.16/avchat.swf&usertype=false&lang=en&EIO=3&transport=websocket"; - - StreamateWebsocketClient wsClient = new StreamateWebsocketClient(wsUrl, site.getHttpClient()); - return wsClient.getRoomId(); - } - - private JSONObject getRoomInfo() throws IOException { - String url = "https://hybridclient.naiadsystems.com/api/v1/config/?sabasic=&sakey=&sk=www.streamate.com&userid=0&version=6.3.16&ajax=1&name=" + getName(); - Request req = new Request.Builder() - .addHeader("User-Agent", Config.getInstance().getSettings().httpUserAgent) - .addHeader("Accept", "application/json, text/javascript, */*") - .addHeader("Accept-Language", "en") - .addHeader("Referer", Streamate.BASE_URL + '/' + getName()) - .addHeader("X-Requested-With", "XMLHttpRequest") - .url(url) - .build(); - try(Response response = site.getHttpClient().execute(req)) { - if(response.isSuccessful()) { - return new JSONObject(response.body().string()); + + // add encodings + JSONArray encodings = hls.getJSONArray("encodings"); + streamSources.clear(); + for (int i = 0; i < encodings.length(); i++) { + JSONObject encoding = encodings.getJSONObject(i); + StreamSource src = new StreamSource(); + src.mediaPlaylistUrl = encoding.getString("location"); + src.width = encoding.optInt("videoWidth"); + src.height = encoding.optInt("videoHeight"); + src.bandwidth = (encoding.optInt("videoKbps") + encoding.optInt("audioKbps")) * 1024; + streamSources.add(src); + } + + // add raw source stream + JSONObject origin = hls.getJSONObject("origin"); + StreamSource src = new StreamSource(); + src.mediaPlaylistUrl = origin.getString("location"); + origin = ws.getJSONObject("origin"); // switch to web socket origin, because it has width, height and bitrates + src.width = origin.optInt("videoWidth"); + src.height = origin.optInt("videoHeight"); + src.bandwidth = (origin.optInt("videoKbps") + origin.optInt("audioKbps")) * 1024; + streamSources.add(src); } else { throw new HttpException(response.code(), response.message()); } } + return streamSources; } @Override