Retrieve API URL from Manyvids insteaed of using a constant

This commit is contained in:
0xb00bface 2021-04-11 13:23:52 +02:00
parent 4c1b311559
commit ace7eb1220
6 changed files with 54 additions and 44 deletions

View File

@ -2,6 +2,8 @@
======================== ========================
* Fixed bug, which caused some recordings to get stuck * Fixed bug, which caused some recordings to get stuck
* Fixed follow/unfollow for CamSoda * Fixed follow/unfollow for CamSoda
* Fixed MVLive downloads
* Fixed bug in cookie handling, which also prevent MVLive downloads from working
* Ignore list is now saved as URLs only. The old format is not compatible * Ignore list is now saved as URLs only. The old format is not compatible
anymore, so make sure, that you export them again, if you created a backup anymore, so make sure, that you export them again, if you created a backup
before. before.

View File

@ -34,8 +34,6 @@ public class MVLive extends AbstractSite {
private static final Logger LOG = LoggerFactory.getLogger(MVLive.class); private static final Logger LOG = LoggerFactory.getLogger(MVLive.class);
public static final String APP_HOST = "app-v2.live.manyvids.com";
public static final String WS_URL = "wss://" + APP_HOST;
public static final String WS_ORIGIN = "https://live.manyvids.com"; public static final String WS_ORIGIN = "https://live.manyvids.com";
public static final String BASE_URL = "https://www.manyvids.com/MVLive/"; public static final String BASE_URL = "https://www.manyvids.com/MVLive/";

View File

@ -66,10 +66,11 @@ public class MVLiveClient {
roomId = response.optString("roomId"); roomId = response.optString("roomId");
int randomNumber = 100 + rng.nextInt(800); int randomNumber = 100 + rng.nextInt(800);
String randomString = UUID.randomUUID().toString().replace("-", "").substring(0, 8); String randomString = UUID.randomUUID().toString().replace("-", "").substring(0, 8);
String wsUrl = String.format("%s/api/%s/eventbus/%s/%s/websocket", WS_URL, roomNumber, randomNumber, randomString); String wsUrl = model.getApiUrl().replace("https", "wss");
String url = String.format("%s/%s/eventbus/%s/%s/websocket", wsUrl, roomNumber, randomNumber, randomString);
LOG.info("Websocket is null. Starting a new connection to {}", wsUrl); LOG.info("Websocket is null. Starting a new connection to {}", url);
ws = createWebSocket(wsUrl, roomId, model.getDisplayName()); ws = createWebSocket(url, roomId, model.getDisplayName());
} }
} }
@ -99,7 +100,7 @@ public class MVLiveClient {
super.onOpen(webSocket, response); super.onOpen(webSocket, response);
try { try {
connecting = false; connecting = false;
LOG.debug("WS open: [{}]", response.body().string()); LOG.trace("WS open: [{}]", response.body().string());
scheduler = new ScheduledThreadPoolExecutor(1); scheduler = new ScheduledThreadPoolExecutor(1);
scheduler.scheduleAtFixedRate(() -> sendMessages(new Ping()), 5, 5, TimeUnit.SECONDS); scheduler.scheduleAtFixedRate(() -> sendMessages(new Ping()), 5, 5, TimeUnit.SECONDS);
} catch (IOException e) { } catch (IOException e) {
@ -111,7 +112,7 @@ public class MVLiveClient {
public void onClosed(WebSocket webSocket, int code, String reason) { public void onClosed(WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason); super.onClosed(webSocket, code, reason);
connecting = false; connecting = false;
LOG.info("MVLive websocket closed: {} {}", code, reason); LOG.trace("MVLive websocket closed: {} {}", code, reason);
MVLiveClient.this.ws = null; MVLiveClient.this.ws = null;
running = false; running = false;
synchronized (streamUrlMonitor) { synchronized (streamUrlMonitor) {
@ -146,14 +147,14 @@ public class MVLiveClient {
if (Objects.equal("o", text)) { if (Objects.equal("o", text)) {
sendMessages(new Ping()); sendMessages(new Ping());
sendMessages(new GetBroadcastHealth(roomId, modelName, (m, r) -> { sendMessages(new GetBroadcastHealth(roomId, modelName, (m, r) -> {
LOG.debug("--> {}", m); LOG.trace("--> {}", m);
LOG.debug("<-- {}", r); LOG.trace("<-- {}", r);
String addr = r.getJSONObject("body").optString("subscribeAddress"); String addr = r.getJSONObject("body").optString("subscribeAddress");
sendMessages(new RegisterMessage(addr, (mr, rr) -> { sendMessages(new RegisterMessage(addr, (mr, rr) -> {
LOG.debug("--> {}", mr); LOG.trace("--> {}", mr);
LOG.debug("<-- {}", rr); LOG.trace("<-- {}", rr);
masterPlaylist = rr.getJSONObject("body").optString("videoUrl"); masterPlaylist = rr.getJSONObject("body").optString("videoUrl");
LOG.debug("Got the URL: {}", masterPlaylist); LOG.trace("Got the URL: {}", masterPlaylist);
stop(); stop();
synchronized (streamUrlMonitor) { synchronized (streamUrlMonitor) {
streamUrlMonitor.notifyAll(); streamUrlMonitor.notifyAll();
@ -182,7 +183,7 @@ public class MVLiveClient {
@Override @Override
public void onMessage(WebSocket webSocket, ByteString bytes) { public void onMessage(WebSocket webSocket, ByteString bytes) {
super.onMessage(webSocket, bytes); super.onMessage(webSocket, bytes);
LOG.debug("Binary Message: {}", bytes.hex()); LOG.trace("Binary Message: {}", bytes.hex());
} }
}); });
} }

View File

@ -44,9 +44,6 @@ public class MVLiveHlsDownload extends HlsDownload {
((MVLiveModel)getModel()).updateCloudFlareCookies(); ((MVLiveModel)getModel()).updateCloudFlareCookies();
} catch (IOException e) { } catch (IOException e) {
LOG.error("Couldn't update cloudflare cookies for model {}", getModel(), e); LOG.error("Couldn't update cloudflare cookies for model {}", getModel(), e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Couldn't update cloudflare cookies for model {}", getModel(), e);
} }
} }
} }

View File

@ -45,9 +45,6 @@ public class MVLiveMergedHlsDownload extends MergedFfmpegHlsDownload {
((MVLiveModel)getModel()).updateCloudFlareCookies(); ((MVLiveModel)getModel()).updateCloudFlareCookies();
} catch (IOException e) { } catch (IOException e) {
LOG.error("Couldn't update cloudflare cookies for model {}", getModel(), e); LOG.error("Couldn't update cloudflare cookies for model {}", getModel(), e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
LOG.error("Couldn't update cloudflare cookies for model {}", getModel(), e);
} }
} }
} }

View File

@ -2,18 +2,20 @@ package ctbrec.sites.manyvids;
import static ctbrec.Model.State.*; import static ctbrec.Model.State.*;
import static ctbrec.io.HttpConstants.*; import static ctbrec.io.HttpConstants.*;
import static ctbrec.sites.manyvids.MVLive.*;
import static java.nio.charset.StandardCharsets.*; import static java.nio.charset.StandardCharsets.*;
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.time.Duration;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -46,8 +48,11 @@ public class MVLiveModel extends AbstractModel {
private transient MVLiveHttpClient httpClient; private transient MVLiveHttpClient httpClient;
private transient MVLiveClient client; private transient MVLiveClient client;
private transient JSONObject roomLocation;
private transient Instant lastRoomLocationUpdate = Instant.EPOCH;
private String roomNumber; private String roomNumber;
@Override @Override
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException { public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
if (ignoreCache) { if (ignoreCache) {
@ -89,7 +94,7 @@ public class MVLiveModel 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.debug("Media playlist {}", src.mediaPlaylistUrl); LOG.debug("Media playlist {}", src.mediaPlaylistUrl);
@ -118,13 +123,14 @@ public class MVLiveModel extends AbstractModel {
MasterPlaylist master = playlist.getMasterPlaylist(); MasterPlaylist master = playlist.getMasterPlaylist();
return master; return master;
} else { } else {
LOG.debug("{} URL: {}\n\tResponse: {}", response.code(), url, response.body().string());
throw new HttpException(response.code(), response.message()); throw new HttpException(response.code(), response.message());
} }
} }
} }
public void updateCloudFlareCookies() throws IOException, InterruptedException { public void updateCloudFlareCookies() throws IOException {
String url = "https://" + APP_HOST + "/api/" + getRoomNumber() + "/player-settings/" + getDisplayName(); String url = getApiUrl() + '/' + getRoomNumber() + "/player-settings/" + getDisplayName();
LOG.trace("Getting CF cookies: {}", url); LOG.trace("Getting CF cookies: {}", url);
Request req = new Request.Builder() Request req = new Request.Builder()
.url(url) .url(url)
@ -132,13 +138,17 @@ public class MVLiveModel extends AbstractModel {
.build(); .build();
try (Response response = getHttpClient().execute(req)) { try (Response response = getHttpClient().execute(req)) {
if (!response.isSuccessful()) { if (!response.isSuccessful()) {
LOG.debug(response.body().string()); LOG.debug("Loading CF cookies not successful: {}", response.body().string());
throw new HttpException(response.code(), response.message()); throw new HttpException(response.code(), response.message());
} }
} }
} }
public String getRoomNumber() throws IOException, InterruptedException { String getApiUrl() throws JSONException, IOException {
return getRoomLocation().getString("publicAPIURL");
}
public String getRoomNumber() throws IOException {
if (StringUtil.isBlank(roomNumber)) { if (StringUtil.isBlank(roomNumber)) {
JSONObject json = getRoomLocation(); JSONObject json = getRoomLocation();
if (json.optBoolean("success")) { if (json.optBoolean("success")) {
@ -164,26 +174,31 @@ public class MVLiveModel extends AbstractModel {
} }
public JSONObject getRoomLocation() throws IOException { public JSONObject getRoomLocation() throws IOException {
fetchGeneralCookies(); if (Duration.between(lastRoomLocationUpdate, Instant.now()).getSeconds() > 60) {
httpClient.fetchAuthenticationCookies(); fetchGeneralCookies();
String url = "https://roompool.live.manyvids.com/roompool/" + getDisplayName() + "?private=false"; httpClient.fetchAuthenticationCookies();
LOG.debug("Fetching room location from {}", url); String url = "https://roompool.live.manyvids.com/roompool/" + getDisplayName() + "?private=false";
Request req = new Request.Builder() LOG.debug("Fetching room location from {}", url);
.url(url) Request req = new Request.Builder()
.header(ACCEPT, MIMETYPE_APPLICATION_JSON) .url(url)
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage()) .header(ACCEPT, MIMETYPE_APPLICATION_JSON)
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) .header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage())
.header(REFERER, MVLive.WS_ORIGIN + "/stream/" + getName()) .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.build(); .header(REFERER, MVLive.WS_ORIGIN + "/stream/" + getName())
try (Response response = getHttpClient().execute(req)) { .build();
if (response.isSuccessful()) { try (Response response = getHttpClient().execute(req)) {
String body = response.body().string(); if (response.isSuccessful()) {
JSONObject json = new JSONObject(body); String body = response.body().string();
LOG.trace("Room location response: {}", json); roomLocation = new JSONObject(body);
return json; LOG.trace("Room location response: {}", roomLocation);
} else { lastRoomLocationUpdate = Instant.now();
throw new HttpException(response.code(), response.message()); return roomLocation;
} else {
throw new HttpException(response.code(), response.message());
}
} }
} else {
return roomLocation;
} }
} }