From 4f3fd8a677c7340ce137b05e9c3031e560f2b8db Mon Sep 17 00:00:00 2001 From: 0xboobface <0xboobface@gmail.com> Date: Wed, 19 Dec 2018 12:57:44 +0100 Subject: [PATCH] Add classes for LiveJasmin --- .../java/ctbrec/ui/CamrecApplication.java | 2 + .../main/java/ctbrec/ui/SiteUiFactory.java | 8 + .../ui/sites/jasmin/LiveJasminSiteUi.java | 35 ++++ .../sites/jasmin/LiveJasminTabProvider.java | 36 ++++ .../sites/jasmin/LiveJasminUpdateService.java | 82 +++++++++ .../java/ctbrec/sites/jasmin/LiveJasmin.java | 92 ++++++++++ .../sites/jasmin/LiveJasminHttpClient.java | 18 ++ .../ctbrec/sites/jasmin/LiveJasminModel.java | 170 ++++++++++++++++++ 8 files changed, 443 insertions(+) create mode 100644 client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminSiteUi.java create mode 100644 client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminTabProvider.java create mode 100644 client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminUpdateService.java create mode 100644 common/src/main/java/ctbrec/sites/jasmin/LiveJasmin.java create mode 100644 common/src/main/java/ctbrec/sites/jasmin/LiveJasminHttpClient.java create mode 100644 common/src/main/java/ctbrec/sites/jasmin/LiveJasminModel.java diff --git a/client/src/main/java/ctbrec/ui/CamrecApplication.java b/client/src/main/java/ctbrec/ui/CamrecApplication.java index b2913d98..29ff5a0c 100644 --- a/client/src/main/java/ctbrec/ui/CamrecApplication.java +++ b/client/src/main/java/ctbrec/ui/CamrecApplication.java @@ -37,6 +37,7 @@ import ctbrec.sites.bonga.BongaCams; import ctbrec.sites.cam4.Cam4; import ctbrec.sites.camsoda.Camsoda; import ctbrec.sites.chaturbate.Chaturbate; +import ctbrec.sites.jasmin.LiveJasmin; import ctbrec.sites.mfc.MyFreeCams; import ctbrec.sites.streamate.Streamate; import ctbrec.ui.settings.SettingsTab; @@ -76,6 +77,7 @@ public class CamrecApplication extends Application { sites.add(new Cam4()); sites.add(new Camsoda()); sites.add(new Chaturbate()); + sites.add(new LiveJasmin()); sites.add(new MyFreeCams()); sites.add(new Streamate()); loadConfig(); diff --git a/client/src/main/java/ctbrec/ui/SiteUiFactory.java b/client/src/main/java/ctbrec/ui/SiteUiFactory.java index 7475868c..48728605 100644 --- a/client/src/main/java/ctbrec/ui/SiteUiFactory.java +++ b/client/src/main/java/ctbrec/ui/SiteUiFactory.java @@ -5,12 +5,14 @@ import ctbrec.sites.bonga.BongaCams; import ctbrec.sites.cam4.Cam4; import ctbrec.sites.camsoda.Camsoda; import ctbrec.sites.chaturbate.Chaturbate; +import ctbrec.sites.jasmin.LiveJasmin; import ctbrec.sites.mfc.MyFreeCams; import ctbrec.sites.streamate.Streamate; import ctbrec.ui.sites.bonga.BongaCamsSiteUi; import ctbrec.ui.sites.cam4.Cam4SiteUi; import ctbrec.ui.sites.camsoda.CamsodaSiteUi; import ctbrec.ui.sites.chaturbate.ChaturbateSiteUi; +import ctbrec.ui.sites.jasmin.LiveJasminSiteUi; import ctbrec.ui.sites.myfreecams.MyFreeCamsSiteUi; import ctbrec.ui.sites.streamate.StreamateSiteUi; @@ -20,6 +22,7 @@ public class SiteUiFactory { private static Cam4SiteUi cam4SiteUi; private static CamsodaSiteUi camsodaSiteUi; private static ChaturbateSiteUi ctbSiteUi; + private static LiveJasminSiteUi jasminSiteUi; private static MyFreeCamsSiteUi mfcSiteUi; private static StreamateSiteUi streamateSiteUi; @@ -54,6 +57,11 @@ public class SiteUiFactory { streamateSiteUi = new StreamateSiteUi((Streamate) site); } return streamateSiteUi; + } else if (site instanceof LiveJasmin) { + if (jasminSiteUi == null) { + jasminSiteUi = new LiveJasminSiteUi((LiveJasmin) site); + } + return jasminSiteUi; } throw new RuntimeException("Unknown site " + site.getName()); } diff --git a/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminSiteUi.java b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminSiteUi.java new file mode 100644 index 00000000..cf09617d --- /dev/null +++ b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminSiteUi.java @@ -0,0 +1,35 @@ +package ctbrec.ui.sites.jasmin; + +import java.io.IOException; + +import ctbrec.sites.ConfigUI; +import ctbrec.sites.jasmin.LiveJasmin; +import ctbrec.ui.SiteUI; +import ctbrec.ui.TabProvider; + +public class LiveJasminSiteUi implements SiteUI { + + private LiveJasmin liveJasmin; + private LiveJasminTabProvider tabProvider; + + public LiveJasminSiteUi(LiveJasmin liveJasmin) { + this.liveJasmin = liveJasmin; + tabProvider = new LiveJasminTabProvider(liveJasmin); + } + + @Override + public TabProvider getTabProvider() { + return tabProvider; + } + + @Override + public ConfigUI getConfigUI() { + return null; + } + + @Override + public boolean login() throws IOException { + return liveJasmin.login(); + } + +} diff --git a/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminTabProvider.java b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminTabProvider.java new file mode 100644 index 00000000..df06011a --- /dev/null +++ b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminTabProvider.java @@ -0,0 +1,36 @@ +package ctbrec.ui.sites.jasmin; + +import java.util.ArrayList; +import java.util.List; + +import ctbrec.sites.jasmin.LiveJasmin; +import ctbrec.ui.TabProvider; +import ctbrec.ui.ThumbOverviewTab; +import javafx.scene.Scene; +import javafx.scene.control.Tab; + +public class LiveJasminTabProvider extends TabProvider { + + private LiveJasmin liveJasmin; + + public LiveJasminTabProvider(LiveJasmin liveJasmin) { + this.liveJasmin = liveJasmin; + } + + @Override + public List getTabs(Scene scene) { + List tabs = new ArrayList<>(); + + long ts = System.currentTimeMillis(); + LiveJasminUpdateService s = new LiveJasminUpdateService(liveJasmin, liveJasmin.getBaseUrl() + "/en/girls/?listPageOrderType=most_popular&_dc=" + ts); + ThumbOverviewTab tab = new ThumbOverviewTab("Girls", s, liveJasmin); + tab.setRecorder(liveJasmin.getRecorder()); + tabs.add(tab); + return tabs; + } + + @Override + public Tab getFollowedTab() { + return null; + } +} diff --git a/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminUpdateService.java b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminUpdateService.java new file mode 100644 index 00000000..b4e67a1f --- /dev/null +++ b/client/src/main/java/ctbrec/ui/sites/jasmin/LiveJasminUpdateService.java @@ -0,0 +1,82 @@ +package ctbrec.ui.sites.jasmin; + +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.Config; +import ctbrec.Model; +import ctbrec.io.HttpException; +import ctbrec.sites.jasmin.LiveJasmin; +import ctbrec.sites.jasmin.LiveJasminModel; +import ctbrec.ui.PaginatedScheduledService; +import javafx.concurrent.Task; +import okhttp3.Request; +import okhttp3.Response; + +public class LiveJasminUpdateService extends PaginatedScheduledService { + + private static final transient Logger LOG = LoggerFactory.getLogger(LiveJasminUpdateService.class); + private String url; + private LiveJasmin liveJasmin; + + public LiveJasminUpdateService(LiveJasmin liveJasmin, String url) { + this.liveJasmin = liveJasmin; + this.url = url; + } + + @Override + protected Task> createTask() { + return new Task>() { + @Override + public List call() throws IOException { + String _url = url + ((page-1) * 36); // TODO find out how to switch pages + LOG.debug("Fetching page {}", url); + Request request = new Request.Builder() + .url(url) + .addHeader("User-Agent", Config.getInstance().getSettings().httpUserAgent) + .addHeader("Accept", "application/json, text/javascript, */*") + .addHeader("Accept-Language", "en") + .addHeader("Referer", liveJasmin.getBaseUrl()) + .addHeader("X-Requested-With", "XMLHttpRequest") + .build(); + try (Response response = liveJasmin.getHttpClient().execute(request)) { + if (response.isSuccessful()) { + String body = response.body().string(); + List models = new ArrayList<>(); + JSONObject json = new JSONObject(body); + //LOG.debug(json.toString(2)); + if(json.optBoolean("success")) { + JSONObject data = json.getJSONObject("data"); + JSONObject content = data.getJSONObject("content"); + JSONArray performers = content.getJSONArray("performers"); + for (int i = 0; i < performers.length(); i++) { + JSONObject m = performers.getJSONObject(i); + String name = m.optString("pid"); + if(name.isEmpty()) { + continue; + } + LiveJasminModel model = (LiveJasminModel) liveJasmin.createModel(name); + model.setId(m.getString("id")); + model.setPreview(m.getString("profilePictureUrl")); + model.setOnline(true); + models.add(model); + } + } else { + LOG.error("Request failed:\n{}", body); + throw new IOException("Response was not successfull"); + } + return models; + } else { + throw new HttpException(response.code(), response.message()); + } + } + } + }; + } +} diff --git a/common/src/main/java/ctbrec/sites/jasmin/LiveJasmin.java b/common/src/main/java/ctbrec/sites/jasmin/LiveJasmin.java new file mode 100644 index 00000000..f9885f4f --- /dev/null +++ b/common/src/main/java/ctbrec/sites/jasmin/LiveJasmin.java @@ -0,0 +1,92 @@ +package ctbrec.sites.jasmin; + +import java.io.IOException; + +import ctbrec.Model; +import ctbrec.io.HttpClient; +import ctbrec.sites.AbstractSite; + +public class LiveJasmin extends AbstractSite { + + public static final String BASE_URL = "https://www.livejasmin.com"; + private HttpClient httpClient; + + @Override + public String getName() { + return "LiveJasmin"; + } + + @Override + public String getBaseUrl() { + return BASE_URL; + } + + @Override + public String getAffiliateLink() { + return "https://awejmp.com/?siteId=jasmin&categoryName=girl&pageName=listpage&performerName=&prm[psid]=0xb00bface&prm[pstool]=205_1&prm[psprogram]=pps&prm[campaign_id]=&subAffId={SUBAFFID}&filters="; + } + + @Override + public Model createModel(String name) { + LiveJasminModel model = new LiveJasminModel(); + model.setName(name); + model.setDescription(""); + model.setSite(this); + return model; + } + + @Override + public Integer getTokenBalance() throws IOException { + return 0; + } + + @Override + public String getBuyTokensLink() { + return getAffiliateLink(); + } + + @Override + public boolean login() throws IOException { + return false; + } + + @Override + public HttpClient getHttpClient() { + if (httpClient == null) { + httpClient = new LiveJasminHttpClient(); + } + return httpClient; + } + + @Override + public void init() throws IOException { + } + + @Override + public void shutdown() { + if (httpClient != null) { + httpClient.shutdown(); + } + } + + @Override + public boolean supportsTips() { + return false; + } + + @Override + public boolean supportsFollow() { + return false; + } + + @Override + public boolean isSiteForModel(Model m) { + return m instanceof LiveJasminModel; + } + + @Override + public boolean credentialsAvailable() { + return false; + } + +} diff --git a/common/src/main/java/ctbrec/sites/jasmin/LiveJasminHttpClient.java b/common/src/main/java/ctbrec/sites/jasmin/LiveJasminHttpClient.java new file mode 100644 index 00000000..f853fa12 --- /dev/null +++ b/common/src/main/java/ctbrec/sites/jasmin/LiveJasminHttpClient.java @@ -0,0 +1,18 @@ +package ctbrec.sites.jasmin; + +import java.io.IOException; + +import ctbrec.io.HttpClient; + +public class LiveJasminHttpClient extends HttpClient { + + protected LiveJasminHttpClient() { + super("livejasmin"); + } + + @Override + public boolean login() throws IOException { + return false; + } + +} diff --git a/common/src/main/java/ctbrec/sites/jasmin/LiveJasminModel.java b/common/src/main/java/ctbrec/sites/jasmin/LiveJasminModel.java new file mode 100644 index 00000000..184eb87a --- /dev/null +++ b/common/src/main/java/ctbrec/sites/jasmin/LiveJasminModel.java @@ -0,0 +1,170 @@ +package ctbrec.sites.jasmin; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +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.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 com.squareup.moshi.JsonReader; +import com.squareup.moshi.JsonWriter; + +import ctbrec.AbstractModel; +import ctbrec.Config; +import ctbrec.io.HttpException; +import ctbrec.recorder.download.StreamSource; +import okhttp3.Request; +import okhttp3.Response; + +public class LiveJasminModel extends AbstractModel { + + private static final transient Logger LOG = LoggerFactory.getLogger(LiveJasminModel.class); + private String id; + private boolean online = false; + + @Override + public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException { + if(ignoreCache) { + try { + getMasterPlaylistUrl(); + online = true; + } catch (Exception e) { + online = false; + } + } + return online; + } + + @Override + public List getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException { + String masterUrl = getMasterPlaylistUrl(); + List streamSources = new ArrayList<>(); + Request req = new Request.Builder().url(masterUrl).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(); + String baseUrl = masterUrl.toString(); + baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1); + streamsource.mediaPlaylistUrl = baseUrl + 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 getMasterPlaylistUrl() throws IOException { + String url = site.getBaseUrl() + "/en/stream/hls/free/" + getName(); + Request request = new Request.Builder() + .url(url) + .addHeader("User-Agent", Config.getInstance().getSettings().httpUserAgent) + .addHeader("Accept", "application/json, text/javascript, */*") + .addHeader("Accept-Language", "en") + .addHeader("Referer", site.getBaseUrl()) + .addHeader("X-Requested-With", "XMLHttpRequest") + .build(); + try (Response response = site.getHttpClient().execute(request)) { + if (response.isSuccessful()) { + String body = response.body().string(); + JSONObject json = new JSONObject(body); + LOG.debug(json.toString(2)); + if(json.optBoolean("success")) { + JSONObject data = json.getJSONObject("data"); + JSONObject hlsStream = data.getJSONObject("hls_stream"); + return hlsStream.getString("url"); + } else { + LOG.error("Request failed:\n{}", body); + throw new IOException("Response was not successfull"); + } + } else { + throw new HttpException(response.code(), response.message()); + } + } + } + + @Override + public void invalidateCacheEntries() { + } + + @Override + public void receiveTip(int tokens) throws IOException { + } + + @Override + public int[] getStreamResolution(boolean failFast) throws ExecutionException { + return new int[2]; + } + + @Override + public boolean follow() throws IOException { + return false; + } + + @Override + public boolean unfollow() throws IOException { + return false; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public void readSiteSpecificData(JsonReader reader) throws IOException { + reader.nextName(); + id = reader.nextString(); + } + + @Override + public void writeSiteSpecificData(JsonWriter writer) throws IOException { + if(id == null) { + // TODO make sure the id is set + // try { + // loadModelInfo(); + // } catch (IOException e) { + // LOG.error("Couldn't load model ID for {}. This can cause problems with saving / loading the model", getName()); + // } + } + writer.name("id").value(id); + } + + public void setOnline(boolean online) { + this.online = online; + } +}