Cam4: Fixed stream URLs search. Slightly increased chances to find good one

This commit is contained in:
0xb00bface 2023-12-30 22:20:41 +01:00
parent 9b45896961
commit 9da5eb2eb4
2 changed files with 35 additions and 51 deletions

View File

@ -19,6 +19,7 @@
- Online/Offline switch on all tabs. Up to 10 000 offline models in each - Online/Offline switch on all tabs. Up to 10 000 offline models in each
category. How do you like it, Elon Musk? category. How do you like it, Elon Musk?
- Added "New Girls" tab and adjusted others. All same models on less tabs. - Added "New Girls" tab and adjusted others. All same models on less tabs.
* Cam4: Fixed stream URLs search. Slightly increased chances to find good one.
5.2.3 5.2.3
======================== ========================

View File

@ -22,16 +22,14 @@ import org.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.time.Duration;
import java.time.Instant;
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.Pattern;
import static ctbrec.Model.State.*; import static ctbrec.Model.State.*;
import static ctbrec.io.HttpClient.bodyToJsonObject; import static ctbrec.io.HttpClient.bodyToJsonObject;
import static ctbrec.io.HttpConstants.*; import static ctbrec.io.HttpConstants.*;
import static java.util.regex.Pattern.DOTALL;
import static java.util.regex.Pattern.MULTILINE;
@Slf4j @Slf4j
public class Cam4Model extends AbstractModel { public class Cam4Model extends AbstractModel {
@ -40,6 +38,7 @@ public class Cam4Model extends AbstractModel {
private String playlistUrl; private String playlistUrl;
private int[] resolution = null; private int[] resolution = null;
private transient JSONObject modelInfo; private transient JSONObject modelInfo;
private transient Instant lastInfoRequest = Instant.EPOCH;
@Override @Override
public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException { public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException {
@ -57,12 +56,16 @@ public class Cam4Model extends AbstractModel {
} }
private JSONObject loadModelInfo() throws IOException { private JSONObject loadModelInfo() throws IOException {
JSONObject roomState = new Cam4WsClient(Config.getInstance(), (Cam4) getSite(), this).getRoomState(); if (Objects.nonNull(modelInfo) && Duration.between(lastInfoRequest, Instant.now()).getSeconds() < 5) {
log.trace(roomState.toString(2)); return modelInfo;
String state = roomState.optString("newShowsState"); }
lastInfoRequest = Instant.now();
modelInfo = new Cam4WsClient(Config.getInstance(), (Cam4) getSite(), this).getRoomState();
log.trace(modelInfo.toString(2));
String state = modelInfo.optString("newShowsState");
setOnlineStateByShowType(state); setOnlineStateByShowType(state);
setDescription(roomState.optString("status")); setDescription(modelInfo.optString("status"));
return roomState; return modelInfo;
} }
public void setOnlineStateByShowType(String showType) { public void setOnlineStateByShowType(String showType) {
@ -94,37 +97,36 @@ public class Cam4Model extends AbstractModel {
} }
private String getPlaylistUrl() throws IOException { private String getPlaylistUrl() throws IOException {
try { if (Objects.nonNull(modelInfo)) {
getPlaylistUrlFromStreamUrl();
if (StringUtil.isNotBlank(playlistUrl)) {
return playlistUrl;
}
} catch (IOException e) {
log.debug("Couldn't get playlist url from stream info: {}", e.getMessage());
}
if (modelInfo != null && modelInfo.has("hls")) {
String hls = modelInfo.optString("hls"); String hls = modelInfo.optString("hls");
log.debug("Stream hls: {}", hls);
if (StringUtil.isNotBlank(hls) && hls.startsWith("http")) { if (StringUtil.isNotBlank(hls) && hls.startsWith("http")) {
log.debug("Stream hls: {}", hls);
playlistUrl = hls; playlistUrl = hls;
return playlistUrl; return playlistUrl;
} }
String llhls = modelInfo.optString("llhls");
if (StringUtil.isNotBlank(llhls) && llhls.startsWith("http")) {
log.debug("Stream hls: {}", llhls);
playlistUrl = llhls;
return playlistUrl;
} }
if (modelInfo != null && modelInfo.has("streamUUID")) { }
try {
getPlaylistUrlFromStreamUrl();
} catch (IOException e) {
log.debug("Couldn't get playlist url from stream info: {}", e.getMessage());
}
if (Objects.nonNull(modelInfo) && StringUtil.isBlank(playlistUrl)) {
String uuid = modelInfo.optString("streamUUID"); String uuid = modelInfo.optString("streamUUID");
log.debug("Stream UUID: {}", uuid); if (StringUtil.isNotBlank(uuid) && uuid.toLowerCase().startsWith(getName().toLowerCase())) {
String[] parts = uuid.split("-"); String[] parts = uuid.split("-");
if (parts.length > 3) { if (parts.length > 5) {
String urlTemplate = "https://cam4-hls.xcdnpro.com/{0}/cam4-origin-live/{1}_aac/playlist.m3u8"; String urlTemplate = "https://cam4-hls.xcdnpro.com/{0}/cam4-origin-live/{1}_aac/playlist.m3u8";
playlistUrl = MessageFormat.format(urlTemplate, parts[1], uuid); playlistUrl = MessageFormat.format(urlTemplate, parts[1], uuid);
log.debug("Stream UUID: {}, hls: {}", uuid, playlistUrl);
return playlistUrl; return playlistUrl;
} }
} }
String page = loadModelPage();
Matcher m = Pattern.compile("hlsUrl\\s*:\\s*'(.*?)'", DOTALL | MULTILINE).matcher(page);
if (m.find()) {
playlistUrl = m.group(1);
return playlistUrl;
} }
if (StringUtil.isBlank(playlistUrl)) { if (StringUtil.isBlank(playlistUrl)) {
throw new IOException("Couldn't determine playlist url"); throw new IOException("Couldn't determine playlist url");
@ -161,25 +163,6 @@ public class Cam4Model extends AbstractModel {
} }
} }
private String loadModelPage() throws IOException {
Request req = new Request.Builder() // @formatter:off
.url(getUrl())
.header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent)
.header(ACCEPT, "*/*")
.header(ACCEPT_LANGUAGE, "*")
.header(CACHE_CONTROL, NO_CACHE)
.header(PRAGMA, NO_CACHE)
.header(REFERER, getUrl())
.build(); // @formatter:on
try (Response response = site.getHttpClient().execute(req)) {
if (response.isSuccessful()) {
return response.body().string();
} else {
throw new HttpException(response.code(), response.message());
}
}
}
@Override @Override
public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException { public List<StreamSource> getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException {
MasterPlaylist masterPlaylist = getMasterPlaylist(); MasterPlaylist masterPlaylist = getMasterPlaylist();