jafea7-ctbrec-v5.3.0-based/common/src/main/java/ctbrec/sites/jasmin/LiveJasminModel.java

256 lines
9.5 KiB
Java

package ctbrec.sites.jasmin;
import com.iheartradio.m3u8.ParseException;
import com.iheartradio.m3u8.PlaylistException;
import ctbrec.AbstractModel;
import ctbrec.Config;
import ctbrec.StringUtil;
import ctbrec.io.HttpException;
import ctbrec.recorder.download.RecordingProcess;
import ctbrec.recorder.download.StreamSource;
import okhttp3.Request;
import okhttp3.Response;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import static ctbrec.io.HttpConstants.*;
public class LiveJasminModel extends AbstractModel {
private static final Logger LOG = LoggerFactory.getLogger(LiveJasminModel.class);
private String id;
private boolean online = false;
private int[] resolution;
private final Random rng = new Random();
private transient LiveJasminModelInfo modelInfo;
@Override
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
if (ignoreCache) {
loadModelInfo();
}
return online;
}
protected void loadModelInfo() throws IOException {
Request req = new Request.Builder().url(LiveJasmin.baseUrl)
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.header(ACCEPT, "*/*")
.header(ACCEPT_ENCODING, "deflate")
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
.header(REFERER, getSite().getBaseUrl() + "/")
.header(ORIGIN, getSite().getBaseUrl())
.build();
try (Response response = getSite().getHttpClient().execute(req)) {
// do nothing we just want the cookies
LOG.debug("Initial request succeeded: {} - {}", response.isSuccessful(), response.code());
}
String url = LiveJasmin.baseUrl + "/en/flash/get-performer-details/" + getName();
req = new Request.Builder().url(url)
.header(USER_AGENT, "Mozilla/5.0 (iPhone; CPU OS 10_14 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.1 Mobile/14E304 Safari/605.1.15")
.header(ACCEPT, MIMETYPE_APPLICATION_JSON)
.header(ACCEPT_ENCODING, "deflate")
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
.header(REFERER, getSite().getBaseUrl() + "/")
.header(ORIGIN, getSite().getBaseUrl())
.header(X_REQUESTED_WITH, XML_HTTP_REQUEST)
.build();
try (Response response = getSite().getHttpClient().execute(req)) {
if (response.isSuccessful()) {
String body = response.body().string();
JSONObject json = new JSONObject(body);
LOG.trace(json.toString(2));
if (json.optBoolean("success")) {
JSONObject data = json.getJSONObject("data");
modelInfo = new LiveJasminModelInfo.LiveJasminModelInfoBuilder()
.sbIp(data.optString("sb_ip", null))
.sbHash(data.optString("sb_hash", null))
.sessionId("m12345678901234567890123456789012")
.jsm2session(getSite().getHttpClient().getCookiesByName("session").get(0).value())
.performerId(data.optString("performer_id", getName()))
.displayName(data.optString("display_name", getName()))
.clientInstanceId(randomClientInstanceId())
.status(data.optInt("status", -1))
.build();
if (data.has("channelsiteurl")) {
setUrl(LiveJasmin.baseUrl + data.getString("channelsiteurl"));
}
onlineState = mapStatus(modelInfo.getStatus());
online = onlineState == State.ONLINE
&& StringUtil.isNotBlank(modelInfo.getSbIp())
&& StringUtil.isNotBlank(modelInfo.getSbHash());
LOG.debug("{} - status:{} {} {} {} {}", getName(), online, onlineState, Arrays.toString(resolution), getUrl(), id);
} else {
throw new IOException("Response was not successful: " + body);
}
} else {
throw new HttpException(response.code(), response.message());
}
}
}
private String randomClientInstanceId() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 32; i++) {
sb.append(rng.nextInt(9) + 1);
}
return sb.toString();
}
public static State mapStatus(int status) {
switch (status) {
case 0 -> {
return State.OFFLINE;
}
case 1 -> {
return State.ONLINE;
}
case 2, 3 -> {
return State.PRIVATE;
}
default -> {
LOG.debug("Unkown state {}", status);
return State.UNKNOWN;
}
}
}
@Override
public void setOnlineState(State status) {
super.setOnlineState(status);
online = status == State.ONLINE;
}
@Override
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException {
loadModelInfo();
String websocketUrlTemplate = "wss://dss-relay-{ipWithDashes}.dditscdn.com/memberChat/jasmin{modelName}{sb_hash}?random={clientInstanceId}";
String websocketUrl = websocketUrlTemplate
.replace("{ipWithDashes}", modelInfo.getSbIp().replace('.', '-'))
.replace("{modelName}", getName())
.replace("{sb_hash}", modelInfo.getSbHash())
.replace("{clientInstanceId}", modelInfo.getClientInstanceId());
modelInfo.setWebsocketUrl(websocketUrl);
LiveJasminStreamRegistration liveJasminStreamRegistration = new LiveJasminStreamRegistration(site, modelInfo);
List<StreamSource> streamSources = liveJasminStreamRegistration.getStreamSources();
Collections.sort(streamSources);
return streamSources;
}
@Override
public void invalidateCacheEntries() {
// noop
}
@Override
public void receiveTip(Double tokens) throws IOException {
LiveJasminTippingWebSocket tippingSocket = new LiveJasminTippingWebSocket(site.getHttpClient());
try {
tippingSocket.sendTip(this, Config.getInstance(), tokens);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException(e);
}
}
@Override
public int[] getStreamResolution(boolean failFast) throws ExecutionException {
if (resolution == null) {
if (failFast) {
return new int[2];
}
try {
loadModelInfo();
} catch (IOException e) {
throw new ExecutionException(e);
}
}
return resolution;
}
@Override
public boolean follow() throws IOException {
return follow(true);
}
@Override
public boolean unfollow() throws IOException {
return follow(false);
}
private boolean follow(boolean follow) throws IOException {
if (id == null) {
loadModelInfo();
}
String sessionId = ((LiveJasminHttpClient) site.getHttpClient()).getSessionId();
String url;
if (follow) {
url = site.getBaseUrl() + "/en/free/favourite/add-favourite?session=" + sessionId + "&performerId=" + id;
} else {
url = site.getBaseUrl() + "/en/free/favourite/delete-favourite?session=" + sessionId + "&performerId=" + id;
}
Request request = new Request.Builder()
.url(url)
.addHeader(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.addHeader(ACCEPT, "*/*")
.addHeader(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
.addHeader(REFERER, getUrl())
.addHeader(X_REQUESTED_WITH, XML_HTTP_REQUEST)
.build();
try (Response response = site.getHttpClient().execute(request)) {
if (response.isSuccessful()) {
String body = response.body().string();
JSONObject json = new JSONObject(body);
return json.optString("status").equalsIgnoreCase("ok");
} else {
throw new HttpException(response.code(), response.message());
}
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public void readSiteSpecificData(Map<String, String> data) {
id = data.get("id");
}
@Override
public void writeSiteSpecificData(Map<String, String> data) {
if (id == null) {
try {
loadModelInfo();
} catch (IOException e) {
LOG.error("Couldn't load model ID for {}. This can cause problems with saving / loading the model", getName());
}
}
data.put("id", id);
}
public void setOnline(boolean online) {
this.online = online;
}
@Override
public RecordingProcess createDownload() {
return new LiveJasminWebrtcDownload(getSite().getHttpClient());
}
}