forked from j62/ctbrec
1
0
Fork 0

Add regular cloudflare cookie updates

MVLive updates those cookies every 2 minutes. This makes sure, that the streaming continues. IF the cookies are not updated, the stream ends after around 2:30 minutes
This commit is contained in:
0xb00bface 2020-08-17 12:19:24 +02:00
parent db186e65f4
commit 36885900b5
5 changed files with 117 additions and 36 deletions

View File

@ -62,11 +62,6 @@ public class MVLive extends AbstractSite {
return httpClient; return httpClient;
} }
@Override
public void init() throws IOException {
MVLiveClient.getInstance().setSite(this);
}
@Override @Override
public void shutdown() { public void shutdown() {
if (httpClient != null) { if (httpClient != null) {
@ -94,4 +89,9 @@ public class MVLive extends AbstractSite {
return false; return false;
} }
@Override
public void init() throws IOException {
}
} }

View File

@ -10,6 +10,8 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.json.JSONArray; import org.json.JSONArray;
@ -37,8 +39,6 @@ public class MVLiveClient {
private static final Logger LOG = LoggerFactory.getLogger(MVLiveClient.class); private static final Logger LOG = LoggerFactory.getLogger(MVLiveClient.class);
private static MVLiveClient instance;
private MVLive site;
private WebSocket ws; private WebSocket ws;
private Random rng = new Random(); private Random rng = new Random();
private volatile boolean running = false; private volatile boolean running = false;
@ -47,19 +47,12 @@ public class MVLiveClient {
private String masterPlaylist = null; private String masterPlaylist = null;
private String roomNumber; private String roomNumber;
private String roomId; private String roomId;
private ScheduledExecutorService scheduler;
private MVLiveClient() { private MVLiveHttpClient httpClient;
}
public static synchronized MVLiveClient getInstance() { public MVLiveClient(MVLiveHttpClient httpClient) {
if (instance == null) { this.httpClient = httpClient;
instance = new MVLiveClient();
}
return instance;
}
public void setSite(MVLive site) {
this.site = site;
} }
public void start(MVLiveModel model) throws IOException { public void start(MVLiveModel model) throws IOException {
@ -86,7 +79,7 @@ public class MVLiveClient {
.header(ACCEPT, MIMETYPE_APPLICATION_JSON) .header(ACCEPT, MIMETYPE_APPLICATION_JSON)
.header(COOKIE, getPhpSessionIdCookie()) .header(COOKIE, getPhpSessionIdCookie())
.build(); .build();
try (Response response = site.getHttpClient().execute(req)) { try (Response response = httpClient.execute(req)) {
if (response.isSuccessful()) { if (response.isSuccessful()) {
return new JSONObject(response.body().string()); return new JSONObject(response.body().string());
} else { } else {
@ -100,7 +93,7 @@ public class MVLiveClient {
.url("https://www.manyvids.com/tak-live-redirect.php") .url("https://www.manyvids.com/tak-live-redirect.php")
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.build(); .build();
try (Response response = site.getHttpClient().execute(req)) { try (Response response = httpClient.execute(req)) {
if (!response.isSuccessful()) { if (!response.isSuccessful()) {
throw new HttpException(response.code(), response.message()); throw new HttpException(response.code(), response.message());
} }
@ -108,12 +101,13 @@ public class MVLiveClient {
} }
private String getPhpSessionIdCookie() { private String getPhpSessionIdCookie() {
List<Cookie> cookies = site.getHttpClient().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("");
} }
public void stop() { public void stop() {
running = false; running = false;
scheduler.shutdown();
ws.close(1000, "Good Bye"); // terminate normally (1000) ws.close(1000, "Good Bye"); // terminate normally (1000)
ws = null; ws = null;
} }
@ -126,13 +120,15 @@ public class MVLiveClient {
.header(ORIGIN, WS_ORIGIN) .header(ORIGIN, WS_ORIGIN)
.header(COOKIE, getPhpSessionIdCookie()) .header(COOKIE, getPhpSessionIdCookie())
.build(); .build();
WebSocket websocket = site.getHttpClient().newWebSocket(req, new WebSocketListener() { WebSocket websocket = 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);
try { try {
connecting = false; connecting = false;
LOG.debug("WS open: [{}]", response.body().string()); LOG.debug("WS open: [{}]", response.body().string());
scheduler = new ScheduledThreadPoolExecutor(1);
scheduler.scheduleAtFixedRate(() -> sendMessages(new Ping()), 5, 5, TimeUnit.SECONDS);
} catch (IOException e) { } catch (IOException e) {
LOG.error("Error while processing onOpen event", e); LOG.error("Error while processing onOpen event", e);
} }
@ -186,7 +182,7 @@ public class MVLiveClient {
LOG.debug("<-- {}", rr); LOG.debug("<-- {}", rr);
masterPlaylist = rr.getJSONObject("body").optString("videoUrl"); masterPlaylist = rr.getJSONObject("body").optString("videoUrl");
LOG.debug("Got the URL: {}", masterPlaylist); LOG.debug("Got the URL: {}", masterPlaylist);
stop(); //stop();
synchronized (streamUrlMonitor) { synchronized (streamUrlMonitor) {
streamUrlMonitor.notifyAll(); streamUrlMonitor.notifyAll();
} }
@ -242,12 +238,12 @@ public class MVLiveClient {
while (running) { while (running) {
synchronized (streamUrlMonitor) { synchronized (streamUrlMonitor) {
streamUrlMonitor.wait(TimeUnit.SECONDS.toMillis(20000)); streamUrlMonitor.wait(TimeUnit.SECONDS.toMillis(20000));
running = false; break;
} }
} }
if (ws != null) { // if (ws != null) {
stop(); // stop();
} // }
return new StreamLocation(roomId, roomNumber, masterPlaylist); return new StreamLocation(roomId, roomNumber, masterPlaylist);
} }
} }

View File

@ -14,5 +14,4 @@ public class MVLiveHttpClient extends HttpClient {
public boolean login() throws IOException { public boolean login() throws IOException {
return false; return false;
} }
} }

View File

@ -0,0 +1,52 @@
package ctbrec.sites.manyvids;
import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
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 {
private static final Logger LOG = LoggerFactory.getLogger(MVLiveMergedHlsDownload.class);
private ScheduledExecutorService scheduler;
public MVLiveMergedHlsDownload(HttpClient client) {
super(client);
}
@Override
public void start() throws IOException {
try {
scheduler = new ScheduledThreadPoolExecutor(1, r -> {
Thread t = new Thread(r);
t.setDaemon(true);
t.setName("MVLive CF cookie updater");
t.setPriority(Thread.MIN_PRIORITY);
return t;
});
scheduler.scheduleAtFixedRate(() -> updateCloudFlareCookies(), 2, 2, TimeUnit.MINUTES);
super.start();
} finally {
scheduler.shutdown();
}
}
private void updateCloudFlareCookies() {
try {
((MVLiveModel)getModel()).updateCloudFlareCookies();
} catch (IOException 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

@ -27,7 +27,9 @@ import com.iheartradio.m3u8.data.PlaylistData;
import ctbrec.AbstractModel; import ctbrec.AbstractModel;
import ctbrec.Config; import ctbrec.Config;
import ctbrec.io.HttpException; import ctbrec.io.HttpException;
import ctbrec.recorder.download.Download;
import ctbrec.recorder.download.StreamSource; import ctbrec.recorder.download.StreamSource;
import ctbrec.recorder.download.hls.HlsDownload;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@ -35,19 +37,23 @@ public class MVLiveModel extends AbstractModel {
private static final transient Logger LOG = LoggerFactory.getLogger(MVLiveModel.class); private static final transient Logger LOG = LoggerFactory.getLogger(MVLiveModel.class);
private MVLiveClient client;
private String roomNumber;
@Override @Override
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException { public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
//return getOnlineState(true) == Model.State.ONLINE; // TODO return getOnlineState(true) == Model.State.ONLINE;
return true; return true;
} }
@Override @Override
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException { public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException {
LOG.debug("Loading {}", getUrl()); LOG.debug("Loading {}", getUrl());
MVLiveClient client = MVLiveClient.getInstance();
try { try {
StreamLocation streamLocation = client.getStreamLocation(this); StreamLocation streamLocation = getClient().getStreamLocation(this);
getCloudFlareCookies(streamLocation.roomNumber); LOG.debug("Got the stream location from WS {}", streamLocation.masterPlaylist);
roomNumber = streamLocation.roomNumber;
updateCloudFlareCookies();
MasterPlaylist masterPlaylist = getMasterPlaylist(streamLocation.masterPlaylist); MasterPlaylist masterPlaylist = getMasterPlaylist(streamLocation.masterPlaylist);
List<StreamSource> sources = new ArrayList<>(); List<StreamSource> sources = new ArrayList<>();
for (PlaylistData playlist : masterPlaylist.getPlaylists()) { for (PlaylistData playlist : masterPlaylist.getPlaylists()) {
@ -94,8 +100,8 @@ public class MVLiveModel extends AbstractModel {
} }
} }
private void getCloudFlareCookies(String roomNumber) throws IOException { public void updateCloudFlareCookies() throws IOException, InterruptedException {
String url = MVLive.WS_ORIGIN + "/api/" + roomNumber + "/player-settings/" + getName(); String url = MVLive.WS_ORIGIN + "/api/" + getRoomNumber() + "/player-settings/" + getName();
LOG.debug("Getting CF cookies: {}", url); LOG.debug("Getting CF cookies: {}", url);
Request req = new Request.Builder() Request req = new Request.Builder()
.url(url) .url(url)
@ -104,14 +110,33 @@ public class MVLiveModel extends AbstractModel {
try (Response response = site.getHttpClient().execute(req)) { try (Response response = site.getHttpClient().execute(req)) {
if (!response.isSuccessful()) { if (!response.isSuccessful()) {
throw new HttpException(response.code(), response.message()); throw new HttpException(response.code(), response.message());
} else {
LOG.debug("headers: {}", response.headers());
} }
// else {
// LOG.debug("headers: {}", response.headers());
// }
} }
} }
public String getRoomNumber() throws IOException, InterruptedException {
if (roomNumber == null) {
StreamLocation streamLocation = getClient().getStreamLocation(this);
roomNumber = streamLocation.roomNumber;
}
return roomNumber;
}
private synchronized MVLiveClient getClient() {
if (client == null) {
MVLive site = (MVLive) getSite();
MVLiveHttpClient httpClient = (MVLiveHttpClient) site.getHttpClient();
client = new MVLiveClient(httpClient);
}
return client;
}
@Override @Override
public void invalidateCacheEntries() { public void invalidateCacheEntries() {
roomNumber = null;
} }
@Override @Override
@ -132,4 +157,13 @@ public class MVLiveModel extends AbstractModel {
public boolean unfollow() throws IOException { public boolean unfollow() throws IOException {
return false; return false;
} }
@Override
public Download createDownload() {
if (Config.isServerMode() && !Config.getInstance().getSettings().recordSingleFile) {
return new HlsDownload(getSite().getHttpClient());
} else {
return new MVLiveMergedHlsDownload(getSite().getHttpClient());
}
}
} }