diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ec4cc19..8697d278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Online/Offline switch on all tabs. Up to 10 000 offline models in each category. How do you like it, Elon Musk? - 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 ======================== diff --git a/common/src/main/java/ctbrec/sites/cam4/Cam4Model.java b/common/src/main/java/ctbrec/sites/cam4/Cam4Model.java index 24eb3539..981688a6 100644 --- a/common/src/main/java/ctbrec/sites/cam4/Cam4Model.java +++ b/common/src/main/java/ctbrec/sites/cam4/Cam4Model.java @@ -22,16 +22,14 @@ import org.json.JSONObject; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; +import java.time.Duration; +import java.time.Instant; import java.util.*; import java.util.concurrent.ExecutionException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import static ctbrec.Model.State.*; import static ctbrec.io.HttpClient.bodyToJsonObject; import static ctbrec.io.HttpConstants.*; -import static java.util.regex.Pattern.DOTALL; -import static java.util.regex.Pattern.MULTILINE; @Slf4j public class Cam4Model extends AbstractModel { @@ -40,6 +38,7 @@ public class Cam4Model extends AbstractModel { private String playlistUrl; private int[] resolution = null; private transient JSONObject modelInfo; + private transient Instant lastInfoRequest = Instant.EPOCH; @Override public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException { @@ -57,12 +56,16 @@ public class Cam4Model extends AbstractModel { } private JSONObject loadModelInfo() throws IOException { - JSONObject roomState = new Cam4WsClient(Config.getInstance(), (Cam4) getSite(), this).getRoomState(); - log.trace(roomState.toString(2)); - String state = roomState.optString("newShowsState"); + if (Objects.nonNull(modelInfo) && Duration.between(lastInfoRequest, Instant.now()).getSeconds() < 5) { + return modelInfo; + } + lastInfoRequest = Instant.now(); + modelInfo = new Cam4WsClient(Config.getInstance(), (Cam4) getSite(), this).getRoomState(); + log.trace(modelInfo.toString(2)); + String state = modelInfo.optString("newShowsState"); setOnlineStateByShowType(state); - setDescription(roomState.optString("status")); - return roomState; + setDescription(modelInfo.optString("status")); + return modelInfo; } public void setOnlineStateByShowType(String showType) { @@ -94,37 +97,36 @@ public class Cam4Model extends AbstractModel { } private String getPlaylistUrl() throws IOException { - try { - 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")) { + if (Objects.nonNull(modelInfo)) { String hls = modelInfo.optString("hls"); - log.debug("Stream hls: {}", hls); if (StringUtil.isNotBlank(hls) && hls.startsWith("http")) { + log.debug("Stream hls: {}", hls); playlistUrl = hls; return playlistUrl; } - } - if (modelInfo != null && modelInfo.has("streamUUID")) { - String uuid = modelInfo.optString("streamUUID"); - log.debug("Stream UUID: {}", uuid); - String[] parts = uuid.split("-"); - if (parts.length > 3) { - String urlTemplate = "https://cam4-hls.xcdnpro.com/{0}/cam4-origin-live/{1}_aac/playlist.m3u8"; - playlistUrl = MessageFormat.format(urlTemplate, parts[1], uuid); + String llhls = modelInfo.optString("llhls"); + if (StringUtil.isNotBlank(llhls) && llhls.startsWith("http")) { + log.debug("Stream hls: {}", llhls); + playlistUrl = llhls; 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; + 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"); + if (StringUtil.isNotBlank(uuid) && uuid.toLowerCase().startsWith(getName().toLowerCase())) { + String[] parts = uuid.split("-"); + if (parts.length > 5) { + String urlTemplate = "https://cam4-hls.xcdnpro.com/{0}/cam4-origin-live/{1}_aac/playlist.m3u8"; + playlistUrl = MessageFormat.format(urlTemplate, parts[1], uuid); + log.debug("Stream UUID: {}, hls: {}", uuid, playlistUrl); + return playlistUrl; + } + } } if (StringUtil.isBlank(playlistUrl)) { 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 public List getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException { MasterPlaylist masterPlaylist = getMasterPlaylist();