Fix MV Live downloads and search

This commit is contained in:
0xb00bface 2020-11-29 16:36:39 +01:00
parent cc277022f0
commit 75131cd325
5 changed files with 74 additions and 113 deletions

View File

@ -1,7 +1,8 @@
3.10.5 3.10.5
======================== ========================
* MFC websocket now uses the TLS URL * Fixed MV Live downloads
* Fix: date placeholders with patterns with more than one ocurrence are * MFC web socket now uses the TLS URL
* Fix: date placeholders with patterns with more than one occurrence are
replaced with the value of the first one replaced with the value of the first one
* Some smaller UI tweaks * Some smaller UI tweaks
* adjusted component sizes for small resolutions * adjusted component sizes for small resolutions

View File

@ -1,19 +1,5 @@
package ctbrec.sites.manyvids; package ctbrec.sites.manyvids;
import static ctbrec.io.HttpConstants.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.JSONArray;
import org.json.JSONObject;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import ctbrec.Config; import ctbrec.Config;
import ctbrec.Model; import ctbrec.Model;
import ctbrec.Model.State; import ctbrec.Model.State;
@ -25,11 +11,23 @@ import okhttp3.FormBody;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
import org.json.JSONArray;
import org.json.JSONObject;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static ctbrec.io.HttpConstants.*;
public class MVLive extends AbstractSite { public class MVLive extends AbstractSite {
public static final String WS_URL = "wss://live.manyvids.com"; public static final String WS_URL = "wss://app-v2.live.manyvids.com";
//public static final String WS_URL = "http://localhost:8080";
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/";
@ -111,7 +109,8 @@ public class MVLive extends AbstractSite {
} }
@Override @Override
public void init() throws IOException { public void init() {
// nothing special to do for manyvids
} }
public List<Model> getModels() throws IOException { public List<Model> getModels() throws IOException {
@ -175,9 +174,8 @@ public class MVLive extends AbstractSite {
String getMvToken() throws IOException { String getMvToken() throws IOException {
if (mvtoken == null) { if (mvtoken == null) {
Request request = new Request.Builder() Request request = new Request.Builder()
.url(getBaseUrl()) .url("https://www.manyvids.com/")
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.header(REFERER, MVLive.BASE_URL)
.build(); .build();
try (Response response = getHttpClient().execute(request)) { try (Response response = getHttpClient().execute(request)) {
if (response.isSuccessful()) { if (response.isSuccessful()) {

View File

@ -1,56 +1,43 @@
package ctbrec.sites.manyvids; package ctbrec.sites.manyvids;
import static ctbrec.StringUtil.*; import com.google.common.base.Objects;
import static ctbrec.io.HttpConstants.*; import ctbrec.Config;
import static ctbrec.sites.manyvids.MVLive.*; import ctbrec.sites.manyvids.wsmsg.*;
import okhttp3.Response;
import java.io.IOException; import okhttp3.*;
import java.util.HashMap; import okio.ByteString;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.common.base.Objects; import java.io.IOException;
import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import ctbrec.Config; import static ctbrec.StringUtil.isNotBlank;
import ctbrec.io.HttpException; import static ctbrec.io.HttpConstants.*;
import ctbrec.sites.manyvids.wsmsg.GetBroadcastHealth; import static ctbrec.sites.manyvids.MVLive.WS_ORIGIN;
import ctbrec.sites.manyvids.wsmsg.Message; import static ctbrec.sites.manyvids.MVLive.WS_URL;
import ctbrec.sites.manyvids.wsmsg.Ping;
import ctbrec.sites.manyvids.wsmsg.RegisterMessage;
import ctbrec.sites.manyvids.wsmsg.SendMessage;
import okhttp3.Cookie;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
public class MVLiveClient { public class MVLiveClient {
private static final Logger LOG = LoggerFactory.getLogger(MVLiveClient.class); private static final Logger LOG = LoggerFactory.getLogger(MVLiveClient.class);
private final Map<String, Message> futureResponses = new HashMap<>();
private final MVLiveHttpClient httpClient;
private final Object streamUrlMonitor = new Object();
private final Random rng = new Random();
private WebSocket ws; private WebSocket ws;
private Random rng = new Random();
private volatile boolean running = false; private volatile boolean running = false;
private volatile boolean connecting = false; private volatile boolean connecting = false;
private Object streamUrlMonitor = new Object();
private String masterPlaylist = null; private String masterPlaylist = null;
private String roomNumber; private String roomNumber;
private String roomId; private String roomId;
private ScheduledExecutorService scheduler; private ScheduledExecutorService scheduler;
private Map<String, Message> futureResponses = new HashMap<>();
private MVLiveHttpClient httpClient;
public MVLiveClient(MVLiveHttpClient httpClient) { public MVLiveClient(MVLiveHttpClient httpClient) {
this.httpClient = httpClient; this.httpClient = httpClient;
@ -61,7 +48,7 @@ public class MVLiveClient {
if (ws == null && !connecting) { if (ws == null && !connecting) {
httpClient.fetchAuthenticationCookies(); httpClient.fetchAuthenticationCookies();
JSONObject response = getRoomLocation(model); JSONObject response = model.getRoomLocation();
roomNumber = response.optString("floorId"); roomNumber = response.optString("floorId");
roomId = response.optString("roomId"); roomId = response.optString("roomId");
int randomNumber = 100 + rng.nextInt(800); int randomNumber = 100 + rng.nextInt(800);
@ -73,22 +60,6 @@ public class MVLiveClient {
} }
} }
private JSONObject getRoomLocation(MVLiveModel model) throws IOException {
Request req = new Request.Builder()
.url(WS_ORIGIN + "/api/roomlocation/" + model.getDisplayName() + "?private=false")
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.header(ACCEPT, MIMETYPE_APPLICATION_JSON)
.header(COOKIE, getPhpSessionIdCookie())
.build();
try (Response response = httpClient.execute(req)) {
if (response.isSuccessful()) {
return new JSONObject(response.body().string());
} else {
throw new HttpException(response.code(), response.message());
}
}
}
private String getPhpSessionIdCookie() { private String getPhpSessionIdCookie() {
List<Cookie> cookies = httpClient.getCookiesByName("PHPSESSID"); List<Cookie> cookies = httpClient.getCookiesByName("PHPSESSID");
return cookies.stream().map(c -> c.name() + "=" + c.value()).findFirst().orElse(""); return cookies.stream().map(c -> c.name() + "=" + c.value()).findFirst().orElse("");
@ -109,7 +80,7 @@ public class MVLiveClient {
.header(ORIGIN, WS_ORIGIN) .header(ORIGIN, WS_ORIGIN)
.header(COOKIE, getPhpSessionIdCookie()) .header(COOKIE, getPhpSessionIdCookie())
.build(); .build();
WebSocket websocket = httpClient.newWebSocket(req, new WebSocketListener() { return httpClient.newWebSocket(req, new WebSocketListener() {
@Override @Override
public void onOpen(WebSocket webSocket, Response response) { public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response); super.onOpen(webSocket, response);
@ -157,7 +128,6 @@ public class MVLiveClient {
@Override @Override
public void onMessage(WebSocket webSocket, String text) { public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text); super.onMessage(webSocket, text);
//msgBuffer.append(text);
LOG.trace("Message: {}", text); LOG.trace("Message: {}", text);
text = Optional.ofNullable(text).orElse(""); text = Optional.ofNullable(text).orElse("");
if (Objects.equal("o", text)) { if (Objects.equal("o", text)) {
@ -202,7 +172,6 @@ public class MVLiveClient {
LOG.debug("Binary Message: {}", bytes.hex()); LOG.debug("Binary Message: {}", bytes.hex());
} }
}); });
return websocket;
} }
void sendMessages(Message... messages) { void sendMessages(Message... messages) {

View File

@ -1,21 +1,20 @@
package ctbrec.sites.manyvids; package ctbrec.sites.manyvids;
import ctbrec.io.HttpClient;
import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ctbrec.io.HttpClient;
import ctbrec.recorder.download.hls.MergedFfmpegHlsDownload;
public class MVLiveMergedHlsDownload extends MergedFfmpegHlsDownload { public class MVLiveMergedHlsDownload extends MergedFfmpegHlsDownload {
private static final Logger LOG = LoggerFactory.getLogger(MVLiveMergedHlsDownload.class); private static final Logger LOG = LoggerFactory.getLogger(MVLiveMergedHlsDownload.class);
private ScheduledExecutorService scheduler; private transient ScheduledExecutorService scheduler;
public MVLiveMergedHlsDownload(HttpClient client) { public MVLiveMergedHlsDownload(HttpClient client) {
super(client); super(client);
@ -31,7 +30,7 @@ public class MVLiveMergedHlsDownload extends MergedFfmpegHlsDownload {
t.setPriority(Thread.MIN_PRIORITY); t.setPriority(Thread.MIN_PRIORITY);
return t; return t;
}); });
scheduler.scheduleAtFixedRate(() -> updateCloudFlareCookies(), 2, 2, TimeUnit.MINUTES); scheduler.scheduleAtFixedRate(this::updateCloudFlareCookies, 2, 2, TimeUnit.MINUTES);
updateCloudFlareCookies(); updateCloudFlareCookies();
super.start(); super.start();
} finally { } finally {

View File

@ -1,8 +1,21 @@
package ctbrec.sites.manyvids; package ctbrec.sites.manyvids;
import static ctbrec.Model.State.*; import com.iheartradio.m3u8.*;
import static ctbrec.io.HttpConstants.*; import com.iheartradio.m3u8.data.MasterPlaylist;
import static java.nio.charset.StandardCharsets.*; import com.iheartradio.m3u8.data.Playlist;
import com.iheartradio.m3u8.data.PlaylistData;
import ctbrec.AbstractModel;
import ctbrec.Config;
import ctbrec.Model;
import ctbrec.StringUtil;
import ctbrec.io.HttpException;
import ctbrec.recorder.download.Download;
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.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
@ -13,29 +26,9 @@ 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.JSONObject; import static ctbrec.Model.State.ONLINE;
import org.slf4j.Logger; import static ctbrec.io.HttpConstants.*;
import org.slf4j.LoggerFactory; import static java.nio.charset.StandardCharsets.UTF_8;
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 ctbrec.AbstractModel;
import ctbrec.Config;
import ctbrec.Model;
import ctbrec.StringUtil;
import ctbrec.io.HttpException;
import ctbrec.recorder.download.Download;
import ctbrec.recorder.download.StreamSource;
import okhttp3.Request;
import okhttp3.Response;
public class MVLiveModel extends AbstractModel { public class MVLiveModel extends AbstractModel {
@ -116,7 +109,7 @@ public class MVLiveModel extends AbstractModel {
} }
public void updateCloudFlareCookies() throws IOException, InterruptedException { public void updateCloudFlareCookies() throws IOException, InterruptedException {
String url = MVLive.WS_ORIGIN + "/api/" + getRoomNumber() + "/player-settings/" + getDisplayName(); String url = "https://app-v2.live.manyvids.com/api/" + 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)
@ -158,8 +151,8 @@ public class MVLiveModel extends AbstractModel {
public JSONObject getRoomLocation() throws IOException { public JSONObject getRoomLocation() throws IOException {
fetchGeneralCookies(); fetchGeneralCookies();
httpClient.fetchAuthenticationCookies(); httpClient.fetchAuthenticationCookies();
String url = MVLive.WS_ORIGIN + "/api/roomlocation/" + getDisplayName() + "?private=false"; String url = "https://roompool.live.manyvids.com/roompool/" + getDisplayName() + "?private=false";
LOG.trace("Fetching room location from {}", url); LOG.debug("Fetching room location from {}", url);
Request req = new Request.Builder() Request req = new Request.Builder()
.url(url) .url(url)
.header(ACCEPT, MIMETYPE_APPLICATION_JSON) .header(ACCEPT, MIMETYPE_APPLICATION_JSON)
@ -169,8 +162,9 @@ 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()) {
JSONObject json = new JSONObject(response.body().string()); String body = response.body().string();
LOG.trace("Room location response: {}", json.toString(2)); JSONObject json = new JSONObject(body);
LOG.trace("Room location response: {}", json);
return json; return json;
} else { } else {
throw new HttpException(response.code(), response.message()); throw new HttpException(response.code(), response.message());