Implement follow / unfollow
This commit is contained in:
parent
0d47952d3d
commit
18e4a43699
|
@ -102,7 +102,6 @@ public class CherryTvUpdateService extends PaginatedScheduledService {
|
||||||
for (int i = 0; i < broadcasts.length(); i++) {
|
for (int i = 0; i < broadcasts.length(); i++) {
|
||||||
JSONObject broadcast = broadcasts.getJSONObject(i);
|
JSONObject broadcast = broadcasts.getJSONObject(i);
|
||||||
CherryTvModel model = site.createModel(broadcast.optString("username"));
|
CherryTvModel model = site.createModel(broadcast.optString("username"));
|
||||||
model.setId(broadcast.getString("id"));
|
|
||||||
model.setDisplayName(broadcast.optString("title"));
|
model.setDisplayName(broadcast.optString("title"));
|
||||||
model.setDescription(broadcast.optString("description"));
|
model.setDescription(broadcast.optString("description"));
|
||||||
model.setPreview(broadcast.optString("thumbnailUrl"));
|
model.setPreview(broadcast.optString("thumbnailUrl"));
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
package ctbrec.io;
|
package ctbrec.io;
|
||||||
|
|
||||||
import static ctbrec.io.HttpConstants.*;
|
import com.squareup.moshi.JsonAdapter;
|
||||||
import static java.nio.charset.StandardCharsets.*;
|
import com.squareup.moshi.Moshi;
|
||||||
|
import ctbrec.Config;
|
||||||
|
import ctbrec.LoggingInterceptor;
|
||||||
|
import ctbrec.Settings.ProxyType;
|
||||||
|
import okhttp3.*;
|
||||||
|
import okhttp3.OkHttpClient.Builder;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.net.ssl.*;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
@ -14,44 +22,16 @@ import java.nio.file.Files;
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
import javax.net.ssl.KeyManager;
|
import static ctbrec.io.HttpConstants.ACCEPT_ENCODING_GZIP;
|
||||||
import javax.net.ssl.SSLContext;
|
import static ctbrec.io.HttpConstants.CONTENT_ENCODING;
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import javax.net.ssl.TrustManager;
|
|
||||||
import javax.net.ssl.X509TrustManager;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonAdapter;
|
|
||||||
import com.squareup.moshi.Moshi;
|
|
||||||
|
|
||||||
import ctbrec.Config;
|
|
||||||
import ctbrec.Settings.ProxyType;
|
|
||||||
import okhttp3.ConnectionPool;
|
|
||||||
import okhttp3.Cookie;
|
|
||||||
import okhttp3.Credentials;
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.OkHttpClient.Builder;
|
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.Response;
|
|
||||||
import okhttp3.Route;
|
|
||||||
import okhttp3.WebSocket;
|
|
||||||
import okhttp3.WebSocketListener;
|
|
||||||
|
|
||||||
public abstract class HttpClient {
|
public abstract class HttpClient {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class);
|
private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class);
|
||||||
|
@ -59,11 +39,11 @@ public abstract class HttpClient {
|
||||||
private static final ConnectionPool GLOBAL_HTTP_CONN_POOL = new ConnectionPool(10, 2, TimeUnit.MINUTES);
|
private static final ConnectionPool GLOBAL_HTTP_CONN_POOL = new ConnectionPool(10, 2, TimeUnit.MINUTES);
|
||||||
|
|
||||||
protected OkHttpClient client;
|
protected OkHttpClient client;
|
||||||
protected CookieJarImpl cookieJar = new CookieJarImpl();
|
protected CookieJarImpl cookieJar;
|
||||||
protected Config config;
|
protected Config config;
|
||||||
protected boolean loggedIn = false;
|
protected boolean loggedIn = false;
|
||||||
protected int loginTries = 0;
|
protected int loginTries = 0;
|
||||||
private String name;
|
private final String name;
|
||||||
|
|
||||||
protected HttpClient(String name, Config config) {
|
protected HttpClient(String name, Config config) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
@ -145,8 +125,7 @@ public abstract class HttpClient {
|
||||||
.connectionPool(GLOBAL_HTTP_CONN_POOL)
|
.connectionPool(GLOBAL_HTTP_CONN_POOL)
|
||||||
.connectTimeout(config.getSettings().httpTimeout, TimeUnit.MILLISECONDS)
|
.connectTimeout(config.getSettings().httpTimeout, TimeUnit.MILLISECONDS)
|
||||||
.readTimeout(config.getSettings().httpTimeout, TimeUnit.MILLISECONDS)
|
.readTimeout(config.getSettings().httpTimeout, TimeUnit.MILLISECONDS)
|
||||||
//.addNetworkInterceptor(new LoggingInterceptor())
|
.addNetworkInterceptor(new LoggingInterceptor());
|
||||||
;
|
|
||||||
|
|
||||||
ProxyType proxyType = config.getSettings().proxyType;
|
ProxyType proxyType = config.getSettings().proxyType;
|
||||||
if (proxyType == ProxyType.HTTP) {
|
if (proxyType == ProxyType.HTTP) {
|
||||||
|
@ -157,7 +136,7 @@ public abstract class HttpClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if transport layer security (TLS) is switched on, accept the self signed cert from the server
|
// if transport layer security (TLS) is switched on, accept the self-signed cert from the server
|
||||||
if (config.getSettings().transportLayerSecurity) {
|
if (config.getSettings().transportLayerSecurity) {
|
||||||
acceptAllTlsCerts(builder);
|
acceptAllTlsCerts(builder);
|
||||||
}
|
}
|
||||||
|
@ -177,8 +156,8 @@ public abstract class HttpClient {
|
||||||
X509Certificate[] x509Certificates = new X509Certificate[0];
|
X509Certificate[] x509Certificates = new X509Certificate[0];
|
||||||
return x509Certificates;
|
return x509Certificates;
|
||||||
}
|
}
|
||||||
@Override public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {}
|
@Override public void checkServerTrusted(final X509Certificate[] chain, final String authType) { /* noop*/ }
|
||||||
@Override public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {}
|
@Override public void checkClientTrusted(final X509Certificate[] chain, final String authType) { /* noop*/ }
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -190,7 +169,7 @@ public abstract class HttpClient {
|
||||||
sslContext.init(keyManagers, trustManagers, secureRandom);
|
sslContext.init(keyManagers, trustManagers, secureRandom);
|
||||||
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||||
builder.sslSocketFactory(sslSocketFactory, x509TrustManager);
|
builder.sslSocketFactory(sslSocketFactory, x509TrustManager);
|
||||||
builder.hostnameVerifier((name, sslSession) -> true);
|
builder.hostnameVerifier((hostname, sslSession) -> true);
|
||||||
} catch (KeyManagementException | NoSuchAlgorithmException e) {
|
} catch (KeyManagementException | NoSuchAlgorithmException e) {
|
||||||
LOG.error("Couldn't install trust managers for TLS connections");
|
LOG.error("Couldn't install trust managers for TLS connections");
|
||||||
}
|
}
|
||||||
|
@ -254,17 +233,14 @@ public abstract class HttpClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
private okhttp3.Authenticator createHttpProxyAuthenticator(String username, String password) {
|
private okhttp3.Authenticator createHttpProxyAuthenticator(String username, String password) {
|
||||||
return new okhttp3.Authenticator() {
|
return (route, response) -> {
|
||||||
@Override
|
|
||||||
public Request authenticate(Route route, Response response) throws IOException {
|
|
||||||
String credential = Credentials.basic(username, password);
|
String credential = Credentials.basic(username, password);
|
||||||
return response.request().newBuilder().header("Proxy-Authorization", credential).build();
|
return response.request().newBuilder().header("Proxy-Authorization", credential).build();
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SocksProxyAuth extends Authenticator {
|
public static class SocksProxyAuth extends Authenticator {
|
||||||
private PasswordAuthentication auth;
|
private final PasswordAuthentication auth;
|
||||||
|
|
||||||
private SocksProxyAuth(String user, String password) {
|
private SocksProxyAuth(String user, String password) {
|
||||||
auth = new PasswordAuthentication(user, password == null ? new char[]{} : password.toCharArray());
|
auth = new PasswordAuthentication(user, password == null ? new char[]{} : password.toCharArray());
|
||||||
|
@ -327,16 +303,16 @@ public abstract class HttpClient {
|
||||||
|
|
||||||
public static String gunzipBody(Response response) throws IOException {
|
public static String gunzipBody(Response response) throws IOException {
|
||||||
if (Objects.equals(response.header(CONTENT_ENCODING), ACCEPT_ENCODING_GZIP)) {
|
if (Objects.equals(response.header(CONTENT_ENCODING), ACCEPT_ENCODING_GZIP)) {
|
||||||
GZIPInputStream gzipIn = new GZIPInputStream(response.body().byteStream());
|
GZIPInputStream gzipIn = new GZIPInputStream(Objects.requireNonNull(response.body()).byteStream());
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
byte[] b = new byte[1024];
|
byte[] b = new byte[1024];
|
||||||
int len = -1;
|
int len;
|
||||||
while ((len = gzipIn.read(b)) >= 0) {
|
while ((len = gzipIn.read(b)) >= 0) {
|
||||||
bos.write(b, 0, len);
|
bos.write(b, 0, len);
|
||||||
}
|
}
|
||||||
return bos.toString(StandardCharsets.UTF_8.toString());
|
return bos.toString(StandardCharsets.UTF_8.toString());
|
||||||
} else {
|
} else {
|
||||||
return response.body().string();
|
return Objects.requireNonNull(response.body()).string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class CherryTv extends AbstractSite {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized boolean login() throws IOException {
|
public synchronized boolean login() throws IOException {
|
||||||
return false;
|
return getHttpClient().login();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -99,7 +99,7 @@ public class CherryTv extends AbstractSite {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsFollow() {
|
public boolean supportsFollow() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -129,7 +129,7 @@ public class CherryTv extends AbstractSite {
|
||||||
try (Response response = getHttpClient().execute(req)) {
|
try (Response response = getHttpClient().execute(req)) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
JSONObject json = new JSONObject(Objects.requireNonNull(response.body()).string());
|
JSONObject json = new JSONObject(Objects.requireNonNull(response.body()).string());
|
||||||
LOG.debug(json.toString(2));
|
LOG.trace(json.toString(2));
|
||||||
JSONObject data = json.getJSONObject("data");
|
JSONObject data = json.getJSONObject("data");
|
||||||
JSONObject searchResult = data.getJSONObject("searchResult");
|
JSONObject searchResult = data.getJSONObject("searchResult");
|
||||||
JSONArray streamers = searchResult.getJSONArray("streamers");
|
JSONArray streamers = searchResult.getJSONArray("streamers");
|
||||||
|
@ -159,7 +159,8 @@ public class CherryTv extends AbstractSite {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean credentialsAvailable() {
|
public boolean credentialsAvailable() {
|
||||||
return false;
|
String username = getConfig().getSettings().cherryTvUsername;
|
||||||
|
return username != null && !username.trim().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class CherryTvHttpClient extends HttpClient {
|
||||||
try (Response response = execute(request)) {
|
try (Response response = execute(request)) {
|
||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
JSONObject resp = new JSONObject(Objects.requireNonNull(response.body()).string());
|
JSONObject resp = new JSONObject(Objects.requireNonNull(response.body()).string());
|
||||||
LOG.trace(resp.toString(2));
|
if (resp.has("data")) {
|
||||||
JSONObject data = resp.getJSONObject("data");
|
JSONObject data = resp.getJSONObject("data");
|
||||||
JSONObject login = data.getJSONObject("login");
|
JSONObject login = data.getJSONObject("login");
|
||||||
loggedIn = login.optBoolean("success");
|
loggedIn = login.optBoolean("success");
|
||||||
|
@ -72,6 +72,10 @@ public class CherryTvHttpClient extends HttpClient {
|
||||||
saveAsSessionCookie(jwt);
|
saveAsSessionCookie(jwt);
|
||||||
LOG.debug("Login successful");
|
LOG.debug("Login successful");
|
||||||
return loggedIn;
|
return loggedIn;
|
||||||
|
} else {
|
||||||
|
LOG.error(resp.toString(2));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new HttpException(response.code(), response.message());
|
throw new HttpException(response.code(), response.message());
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,12 @@ import com.squareup.moshi.JsonWriter;
|
||||||
import ctbrec.AbstractModel;
|
import ctbrec.AbstractModel;
|
||||||
import ctbrec.Config;
|
import ctbrec.Config;
|
||||||
import ctbrec.NotImplementedExcetion;
|
import ctbrec.NotImplementedExcetion;
|
||||||
|
import ctbrec.StringUtil;
|
||||||
import ctbrec.io.HttpException;
|
import ctbrec.io.HttpException;
|
||||||
import ctbrec.recorder.download.StreamSource;
|
import ctbrec.recorder.download.StreamSource;
|
||||||
|
import okhttp3.MediaType;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
@ -21,9 +24,6 @@ import org.slf4j.LoggerFactory;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
@ -55,26 +55,10 @@ public class CherryTvModel extends AbstractModel {
|
||||||
.build();
|
.build();
|
||||||
try (Response resp = site.getHttpClient().execute(req)) {
|
try (Response resp = site.getHttpClient().execute(req)) {
|
||||||
String body = Objects.requireNonNull(resp.body()).string();
|
String body = Objects.requireNonNull(resp.body()).string();
|
||||||
Files.write(Paths.get("/tmp/mdl.html"), body.getBytes(StandardCharsets.UTF_8));
|
|
||||||
Matcher m = NEXT_DATA.matcher(body);
|
Matcher m = NEXT_DATA.matcher(body);
|
||||||
if (m.find()) {
|
if (m.find()) {
|
||||||
JSONObject json = new JSONObject(m.group(1));
|
JSONObject json = new JSONObject(m.group(1));
|
||||||
JSONObject apolloState = json.getJSONObject("props").getJSONObject("pageProps").getJSONObject("apolloState");
|
updateModelProperties(json);
|
||||||
online = false;
|
|
||||||
onlineState = OFFLINE;
|
|
||||||
for (Iterator<String> iter = apolloState.keys(); iter.hasNext();) {
|
|
||||||
String key = iter.next();
|
|
||||||
if (key.startsWith("Broadcast:")) {
|
|
||||||
JSONObject broadcast = apolloState.getJSONObject(key);
|
|
||||||
setDisplayName(broadcast.optString("title"));
|
|
||||||
id = broadcast.getString("id");
|
|
||||||
online = broadcast.optString("showStatus").equalsIgnoreCase("Public")
|
|
||||||
&& broadcast.optString("broadcastStatus").equalsIgnoreCase("Live");
|
|
||||||
onlineState = online ? ONLINE : OFFLINE;
|
|
||||||
masterPlaylistUrl = broadcast.optString("pullUrl", null);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
LOG.error("NEXT_DATA not found in model page {}", getUrl());
|
LOG.error("NEXT_DATA not found in model page {}", getUrl());
|
||||||
return false;
|
return false;
|
||||||
|
@ -86,6 +70,27 @@ public class CherryTvModel extends AbstractModel {
|
||||||
return online;
|
return online;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateModelProperties(JSONObject json) {
|
||||||
|
LOG.trace(json.toString(2));
|
||||||
|
JSONObject apolloState = json.getJSONObject("props").getJSONObject("pageProps").getJSONObject("apolloState");
|
||||||
|
online = false;
|
||||||
|
onlineState = OFFLINE;
|
||||||
|
for (Iterator<String> iter = apolloState.keys(); iter.hasNext(); ) {
|
||||||
|
String key = iter.next();
|
||||||
|
if (key.startsWith("Broadcast:")) {
|
||||||
|
JSONObject broadcast = apolloState.getJSONObject(key);
|
||||||
|
setDisplayName(broadcast.optString("title"));
|
||||||
|
online = broadcast.optString("showStatus").equalsIgnoreCase("Public")
|
||||||
|
&& broadcast.optString("broadcastStatus").equalsIgnoreCase("Live");
|
||||||
|
onlineState = online ? ONLINE : OFFLINE;
|
||||||
|
masterPlaylistUrl = broadcast.optString("pullUrl", null);
|
||||||
|
} else if (key.startsWith("Streamer:")) {
|
||||||
|
JSONObject streamer = apolloState.getJSONObject(key);
|
||||||
|
id = streamer.getString("id");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setOnline(boolean online) {
|
public void setOnline(boolean online) {
|
||||||
this.online = online;
|
this.online = online;
|
||||||
}
|
}
|
||||||
|
@ -125,7 +130,7 @@ public class CherryTvModel extends AbstractModel {
|
||||||
String baseUrl = masterUrl.substring(0, masterUrl.lastIndexOf('/') + 1);
|
String baseUrl = masterUrl.substring(0, masterUrl.lastIndexOf('/') + 1);
|
||||||
String segmentUri = baseUrl + playlist.getUri();
|
String segmentUri = baseUrl + playlist.getUri();
|
||||||
src.mediaPlaylistUrl = segmentUri;
|
src.mediaPlaylistUrl = segmentUri;
|
||||||
if(src.mediaPlaylistUrl.contains("?")) {
|
if (src.mediaPlaylistUrl.contains("?")) {
|
||||||
src.mediaPlaylistUrl = src.mediaPlaylistUrl.substring(0, src.mediaPlaylistUrl.lastIndexOf('?'));
|
src.mediaPlaylistUrl = src.mediaPlaylistUrl.substring(0, src.mediaPlaylistUrl.lastIndexOf('?'));
|
||||||
}
|
}
|
||||||
LOG.trace("Media playlist {}", src.mediaPlaylistUrl);
|
LOG.trace("Media playlist {}", src.mediaPlaylistUrl);
|
||||||
|
@ -172,18 +177,18 @@ public class CherryTvModel extends AbstractModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] getStreamResolution(boolean failFast) throws ExecutionException {
|
public int[] getStreamResolution(boolean failFast) throws ExecutionException {
|
||||||
if(resolution == null) {
|
if (resolution == null) {
|
||||||
if(failFast) {
|
if (failFast) {
|
||||||
return new int[2];
|
return new int[2];
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if(!isOnline()) {
|
if (!isOnline()) {
|
||||||
return new int[2];
|
return new int[2];
|
||||||
}
|
}
|
||||||
List<StreamSource> sources = getStreamSources();
|
List<StreamSource> sources = getStreamSources();
|
||||||
Collections.sort(sources);
|
Collections.sort(sources);
|
||||||
StreamSource best = sources.get(sources.size()-1);
|
StreamSource best = sources.get(sources.size() - 1);
|
||||||
resolution = new int[] {best.width, best.height};
|
resolution = new int[]{best.width, best.height};
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
LOG.warn("Couldn't determine stream resolution for {} - {}", getName(), e.getMessage());
|
LOG.warn("Couldn't determine stream resolution for {} - {}", getName(), e.getMessage());
|
||||||
|
@ -192,22 +197,81 @@ public class CherryTvModel extends AbstractModel {
|
||||||
LOG.warn("Couldn't determine stream resolution for {} - {}", getName(), e.getMessage());
|
LOG.warn("Couldn't determine stream resolution for {} - {}", getName(), e.getMessage());
|
||||||
resolution = new int[2];
|
resolution = new int[2];
|
||||||
}
|
}
|
||||||
return resolution;
|
|
||||||
} else {
|
|
||||||
return resolution;
|
|
||||||
}
|
}
|
||||||
|
return resolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean follow() throws IOException {
|
public boolean follow() throws IOException {
|
||||||
// POST https://cherry.tv/graphql
|
return followUnfollow("follow", "a7a8241014074f5c02ac83863a47f7579e5f03167a7ec424ff65ad045c7fcf6f");
|
||||||
// {"operationName":"follow","variables":{"userId":"1391"},"extensions":{"persistedQuery":{"version":1,"sha256Hash":"a7a8241014074f5c02ac83863a47f7579e5f03167a7ec424ff65ad045c7fcf6f"}}}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean unfollow() throws IOException {
|
public boolean unfollow() throws IOException {
|
||||||
|
return followUnfollow("unfollow", "e91f8f5a60d33efb2dfb3348b977b78358862d3a5cd5ef0011a6aa6bb65d0bd4");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean followUnfollow(String action, String persistedQueryHash) throws IOException {
|
||||||
|
Request request = createFollowUnfollowRequest(action, persistedQueryHash);
|
||||||
|
LOG.debug("Sending follow request for model {} with ID {}", getName(), getId());
|
||||||
|
try (Response response = getSite().getHttpClient().execute(request)) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
String responseBody = Objects.requireNonNull(response.body(), "HTTP response body is null").string();
|
||||||
|
LOG.debug(responseBody);
|
||||||
|
JSONObject resp = new JSONObject(responseBody);
|
||||||
|
if (resp.has("data") && !resp.isNull("data")) {
|
||||||
|
JSONObject data = resp.getJSONObject("data");
|
||||||
|
if (data.has(action + "User")) {
|
||||||
|
return data.getJSONObject(action + "User").optBoolean("success");
|
||||||
|
}
|
||||||
|
} else if (resp.has("errors")) {
|
||||||
|
JSONObject first = resp.getJSONArray("errors").getJSONObject(0);
|
||||||
|
if (first.optString("message").matches("You have .*? the user")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG.debug(resp.toString(2));
|
||||||
return false;
|
return false;
|
||||||
|
} else {
|
||||||
|
throw new HttpException(response.code(), response.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Request createFollowUnfollowRequest(String action, String persistedQueryHash) throws IOException {
|
||||||
|
if (StringUtil.isBlank(id)) {
|
||||||
|
try {
|
||||||
|
// if the id is not set yet, we call isOnline(true), where it gets set
|
||||||
|
isOnline(true);
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject body = new JSONObject()
|
||||||
|
.put("operationName", action)
|
||||||
|
.put("variables", new JSONObject()
|
||||||
|
.put("userId", Objects.requireNonNull(id, "Model ID is null"))
|
||||||
|
)
|
||||||
|
.put("query", "mutation " + action + "($userId: ID!) {\n " + action + "User(userId: $userId) {\n success\n __typename\n }\n}\n")
|
||||||
|
.put("extensions", new JSONObject()
|
||||||
|
.put("persistedQuery", new JSONObject()
|
||||||
|
.put("version", 1)
|
||||||
|
.put("sha256Hash", persistedQueryHash)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
RequestBody requestBody = RequestBody.create(body.toString(), MediaType.parse("application/json"));
|
||||||
|
return new Request.Builder()
|
||||||
|
.url(CherryTv.BASE_URL + "/graphql")
|
||||||
|
.header(REFERER, CherryTv.BASE_URL)
|
||||||
|
.header(ORIGIN, CherryTv.BASE_URL)
|
||||||
|
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
|
||||||
|
.post(requestBody)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mapOnlineState(String roomState) {
|
public void mapOnlineState(String roomState) {
|
||||||
|
@ -237,9 +301,11 @@ public class CherryTvModel extends AbstractModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readSiteSpecificData(JsonReader reader) throws IOException {
|
public void readSiteSpecificData(JsonReader reader) throws IOException {
|
||||||
|
if (reader.hasNext()) {
|
||||||
reader.nextName();
|
reader.nextName();
|
||||||
id = reader.nextString();
|
id = reader.nextString();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeSiteSpecificData(JsonWriter writer) throws IOException {
|
public void writeSiteSpecificData(JsonWriter writer) throws IOException {
|
||||||
|
|
Loading…
Reference in New Issue