diff --git a/common/src/main/java/ctbrec/sites/stripchat/StripchatModel.java b/common/src/main/java/ctbrec/sites/stripchat/StripchatModel.java index 93dd95d6..bb60516d 100644 --- a/common/src/main/java/ctbrec/sites/stripchat/StripchatModel.java +++ b/common/src/main/java/ctbrec/sites/stripchat/StripchatModel.java @@ -6,6 +6,8 @@ import com.iheartradio.m3u8.data.Playlist; import com.iheartradio.m3u8.data.PlaylistData; import ctbrec.AbstractModel; import ctbrec.Config; +import ctbrec.Model; +import ctbrec.ModelGroup; import ctbrec.StringUtil; import ctbrec.io.HttpException; import ctbrec.recorder.download.RecordingProcess; @@ -29,6 +31,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.ExecutionException; import static ctbrec.Model.State.*; @@ -45,35 +48,59 @@ public class StripchatModel extends AbstractModel { @Override public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException { - if (ignoreCache) { - JSONObject jsonResponse = getModelInfo(); - if (jsonResponse.has("user")) { - JSONObject user = jsonResponse.getJSONObject("user").getJSONObject("user"); - String status = user.optString("status"); - mapOnlineState(status); - if (onlineState == OFFLINE) { - setLastSeen(user.optString("statusChangedAt")); - } - if (isBanned(user)) { - log.debug("Model inactive or deleted: {}", getName()); - setMarkedForLaterRecording(true); - } - if ((onlineState == PRIVATE) && jsonResponse.has("cam")) { - JSONObject cam = jsonResponse.getJSONObject("cam"); - if (StringUtil.isNotBlank(cam.optString(KEY_MODEL_TOKEN))) { - setOnlineState(ONLINE); - return true; - } - } + JSONObject info; + if (ignoreCache && (info = getModelInfo()).has("user")) { + JSONObject cam; + JSONObject user = info.getJSONObject("user").getJSONObject("user"); + String status = user.optString("status"); + mapOnlineState(status); + if (onlineState == OFFLINE) { + setLastSeen(user.optString("statusChangedAt")); } - if (jsonResponse.optString("error").equals("Not Found")) { + if (isBanned(user)) { + log.debug("Model inactive or deleted: {}", getName()); + // Config.getInstance().setModelNotes(this, "Model inactive or deleted"); setMarkedForLaterRecording(true); - setOnlineState(OFFLINE); + } + if (onlineState == PRIVATE && info.has("cam") && StringUtil.isNotBlank((cam = info.getJSONObject("cam")).optString(KEY_MODEL_TOKEN))) { + setOnlineState(ONLINE); + return true; } } return onlineState == ONLINE; } + // @Override + // public boolean isOnline(boolean ignoreCache) throws IOException, ExecutionException, InterruptedException { + // JSONObject jsonResponse = getModelInfo(); + // if (ignoreCache) { + // if (jsonResponse.has("user")) { + // JSONObject user = jsonResponse.getJSONObject("user").getJSONObject("user"); + // String status = user.optString("status"); + // mapOnlineState(status); + // if (onlineState == OFFLINE) { + // setLastSeen(user.optString("statusChangedAt")); + // } + // if (isBanned(user)) { + // log.debug("Model inactive or deleted: {}", getName()); + // setMarkedForLaterRecording(true); + // } + // if ((onlineState == PRIVATE) && jsonResponse.has("cam")) { + // JSONObject cam = jsonResponse.getJSONObject("cam"); + // if (StringUtil.isNotBlank(cam.optString(KEY_MODEL_TOKEN))) { + // setOnlineState(ONLINE); + // return true; + // } + // } + // } + // if (jsonResponse.optString("error").equals("Not Found")) { + // setMarkedForLaterRecording(true); + // setOnlineState(OFFLINE); + // } + // } + // return onlineState == ONLINE; + // } + private boolean isBanned(JSONObject user) { boolean isDeleted = user.optBoolean("isDeleted", false); boolean isApprovedModel = user.optBoolean("isApprovedModel", true); @@ -92,17 +119,45 @@ public class StripchatModel extends AbstractModel { private void mapOnlineState(String status) { switch (status) { - case "public" -> setOnlineState(ONLINE); - case "idle" -> setOnlineState(AWAY); - case "private", "p2p", "groupShow", "virtualPrivate" -> setOnlineState(PRIVATE); - case "off" -> setOnlineState(OFFLINE); - default -> { + case "public": { + setOnlineState(Model.State.ONLINE); + break; + } + case "idle": { + setOnlineState(Model.State.AWAY); + break; + } + case "private": + case "p2p": + case "groupShow": + case "virtualPrivate": { + setOnlineState(Model.State.PRIVATE); + break; + } + case "off": { + setOnlineState(Model.State.OFFLINE); + break; + } + default: { log.debug("Unknown online state {} for model {}", status, getName()); - setOnlineState(OFFLINE); + setOnlineState(Model.State.OFFLINE); } } } + // private void mapOnlineState(String status) { + // switch (status) { + // case "public" -> setOnlineState(ONLINE); + // case "idle" -> setOnlineState(AWAY); + // case "private", "p2p", "groupShow", "virtualPrivate" -> setOnlineState(PRIVATE); + // case "off" -> setOnlineState(OFFLINE); + // default -> { + // log.debug("Unknown online state {} for model {}", status, getName()); + // setOnlineState(OFFLINE); + // } + // } + // } + private JSONObject getModelInfo() throws IOException { if (Objects.nonNull(modelInfo) && Duration.between(lastInfoRequest, Instant.now()).getSeconds() < 5) { return modelInfo; @@ -113,7 +168,7 @@ public class StripchatModel extends AbstractModel { } private JSONObject loadModelInfo() throws IOException { - String url = getSite().getBaseUrl() + "/api/front/v2/models/username/" + getName() + "/cam?timezoneOffset=0&triggerRequest=loadCam&uniq=" + getUniq(); + String url = getSite().getBaseUrl() + "/api/front/v2/models/username/" + getName() + "/cam?timezoneOffset=0&triggerRequest=loadCam&uniq=" + StripchatUtil.getUniq(); Request req = new Request.Builder() .url(url) .header(ACCEPT, MIMETYPE_APPLICATION_JSON) @@ -123,19 +178,70 @@ public class StripchatModel extends AbstractModel { .header(REFERER, getUrl()) .header(ORIGIN, getSite().getBaseUrl()) .build(); - try (Response response = site.getHttpClient().execute(req)) { + try (Response response = site.getHttpClient().execute(req);){ if (response.isSuccessful()) { - JSONObject jsonResponse = new JSONObject(response.body().string()); + JSONObject jsonResponse = new JSONObject(response.body().string()); return jsonResponse; - } else { - throw new HttpException(response.code(), response.message()); } + if (response.code() == 404) { + checkIfRenamed(response.body().string()); + } + throw new HttpException(response.code(), response.message()); + } + } + + // private JSONObject loadModelInfo() throws IOException { + // String url = getSite().getBaseUrl() + "/api/front/v2/models/username/" + getName() + "/cam?timezoneOffset=0&triggerRequest=loadCam&uniq=" + getUniq(); + // Request req = new Request.Builder() + // .url(url) + // .header(ACCEPT, MIMETYPE_APPLICATION_JSON) + // .header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage()) + // .header(X_REQUESTED_WITH, XML_HTTP_REQUEST) + // .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) + // .header(REFERER, getUrl()) + // .header(ORIGIN, getSite().getBaseUrl()) + // .build(); + // try (Response response = site.getHttpClient().execute(req)) { + // if (response.isSuccessful()) { + // JSONObject jsonResponse = new JSONObject(response.body().string()); + // return jsonResponse; + // } else { + // throw new HttpException(response.code(), response.message()); + // } + // } + // } + + private void checkIfRenamed(String responseText) { + try { + String newName; + JSONObject data; + JSONObject jsonError = new JSONObject(responseText); + if (jsonError.has("data") && (data = jsonError.getJSONObject("data")).has("newUsername") && StringUtil.isNotBlank(newName = data.optString("newUsername"))) { + String oldName = getName(); + String newUrl = site.getBaseUrl() + "/" + newName; + Optional modelGroup = site.getRecorder().getModelGroup(this); + if (modelGroup.isPresent()) { + modelGroup.get().add(newUrl); + } + // Config.getInstance().setModelNotes(this, "Old username: " + oldName); + setName(newName); + setUrl(newUrl); + log.warn("Model {} renamed to {}", (Object)oldName, (Object)newName); + } + } + catch (Exception exception) { + // empty catch block } } @Override public List getStreamSources() throws IOException, ExecutionException, ParseException, PlaylistException { + log.debug("getStreamSources: modelInfo = \n{}", modelInfo.toString(2)); // Added + String url = getMasterPlaylistUrl(); + + log.debug("getStreamSources: url = {}", url); // Added + MasterPlaylist masterPlaylist = getMasterPlaylist(url); List streamSources = extractStreamSources(masterPlaylist); try { @@ -166,10 +272,10 @@ public class StripchatModel extends AbstractModel { src.setHeight(playlist.getStreamInfo().getResolution().height); src.setWidth(playlist.getStreamInfo().getResolution().width); src.setMediaPlaylistUrl(playlist.getUri()); - if (src.getMediaPlaylistUrl().contains("?")) { - src.setMediaPlaylistUrl(src.getMediaPlaylistUrl().substring(0, src.getMediaPlaylistUrl().lastIndexOf('?'))); - } - log.trace("Media playlist {}", src.getMediaPlaylistUrl()); + // if (src.getMediaPlaylistUrl().contains("?")) { + // src.setMediaPlaylistUrl(src.getMediaPlaylistUrl().substring(0, src.getMediaPlaylistUrl().lastIndexOf('?'))); + // } + log.debug("Media playlist {}", src.getMediaPlaylistUrl()); sources.add(src); } } @@ -177,7 +283,6 @@ public class StripchatModel extends AbstractModel { } private MasterPlaylist getMasterPlaylist(String url) throws IOException, ParseException, PlaylistException { - log.trace("Loading master playlist {}", url); Request req = new Request.Builder() .url(url) .header(USER_AGENT, Config.getInstance().getSettings().httpUserAgent) @@ -185,20 +290,44 @@ public class StripchatModel extends AbstractModel { try (Response response = getSite().getHttpClient().execute(req)) { if (response.isSuccessful()) { String body = response.body().string(); - log.trace(body); + log.debug(body); InputStream inputStream = new ByteArrayInputStream(body.getBytes(UTF_8)); PlaylistParser parser = new PlaylistParser(inputStream, Format.EXT_M3U, Encoding.UTF_8, ParsingMode.LENIENT); Playlist playlist = parser.parse(); MasterPlaylist master = playlist.getMasterPlaylist(); return master; - } else { - throw new HttpException(response.code(), response.message()); - } + } // else { + throw new HttpException(response.code(), response.message()); + // } } } + // private String getMasterPlaylistUrl() throws IOException { + // JSONObject info = getModelInfo(); + // if (info.has("user")) { + // JSONObject cam; + // JSONObject user = info.getJSONObject("user").getJSONObject("user"); + // long modelId = user.optLong("id"); + + // boolean saveVR = Config.getInstance().getSettings().stripchatVR; + // boolean isVRStream = user.optBoolean("isVr", false); + // String vrSuffix = (saveVR && isVRStream) ? "_vr" : ""; + + // Object token = ""; + // if (info.has("cam") && StringUtil.isNotBlank((cam = info.getJSONObject("cam")).optString(KEY_MODEL_TOKEN))) { + // log.debug("Cam token: {}", token); // Added + // token = "&aclAuth=" + cam.getString(KEY_MODEL_TOKEN); + // log.debug("Spy start for {}", getName()); + // } + // String hlsUrlTemplate = "https://edge-hls.doppiocdn.com/hls/{0}/master/{0}_auto.m3u8?playlistType=Standart{1}"; + // return MessageFormat.format(hlsUrlTemplate, String.valueOf(modelId), token); + // } + // throw new IOException("Playlist URL not found"); + // } + private String getMasterPlaylistUrl() throws IOException { JSONObject info = getModelInfo(); + log.debug("getMasterPlaylistUrl: modelInfo = \n{}", info.toString(2)); // Added if (info.has("user")) { JSONObject user = info.getJSONObject("user").getJSONObject("user"); long id = user.optLong("id"); @@ -210,10 +339,16 @@ public class StripchatModel extends AbstractModel { String token = ""; if (info.has("cam")) { JSONObject cam = info.getJSONObject("cam"); - if (StringUtil.isNotBlank(cam.optString(KEY_MODEL_TOKEN))) { - token = "&aclAuth=" + cam.getString(KEY_MODEL_TOKEN); + token = cam.optString(KEY_MODEL_TOKEN); + log.debug("getMasterPlaylistUrl: Cam token: {}", token); // Added + if (StringUtil.isNotBlank(token)) { + token = "&aclAuth=" + token; log.debug("Spy start for {}", getName()); - } + } + // if (StringUtil.isNotBlank(cam.optString(KEY_MODEL_TOKEN))) { + // token = "&aclAuth=" + cam.getString(KEY_MODEL_TOKEN); + // log.debug("Spy start for {}", getName()); + // } } String hlsUrlTemplate = "https://edge-hls.doppiocdn.com/hls/{0}{1}/master/{0}{1}_auto.m3u8?playlistType=Standart{2}"; return MessageFormat.format(hlsUrlTemplate, String.valueOf(id), vrSuffix, token); @@ -257,13 +392,21 @@ public class StripchatModel extends AbstractModel { JSONObject info = getModelInfo(); JSONObject user = info.getJSONObject("user").getJSONObject("user"); long id = user.optLong("id"); + long userId = client.getUserId(); String url = Stripchat.getBaseUri() + "/api/front/users/" + client.getUserId() + "/favorites/" + id; + JSONObject eventData = StripchatUtil.getEventData() + .put("eventName", "fav") + .put("f.action", "add") + .put("f.userId", userId) + .put("f.modelId", id); JSONObject requestParams = new JSONObject() .put("csrfToken", client.getCsrfToken()) .put("csrfTimestamp", client.getCsrfTimestamp()) .put("csrfNotifyTimestamp", client.getCsrfNotifyTimestamp()) .put("uniq", getUniq()) + .put("an", new JSONArray() + .put(eventData)) .put("ampl", client.getAmpl()); RequestBody body = RequestBody.Companion.create(requestParams.toString(), JSON); Request request = new Request.Builder() @@ -291,15 +434,22 @@ public class StripchatModel extends AbstractModel { JSONObject info = getModelInfo(); JSONObject user = info.getJSONObject("user").getJSONObject("user"); long id = user.optLong("id"); + long userId = client.getUserId(); JSONArray favoriteIds = new JSONArray().put(id); - favoriteIds.put(id); + // favoriteIds.put(id); String url = Stripchat.getBaseUri() + "/api/front/users/" + client.getUserId() + "/favorites"; + JSONObject eventData = StripchatUtil.getEventData() + .put("eventName", "fav") + .put("f.action", "remove") + .put("f.userId", userId) + .put("f.modelId", id); JSONObject requestParams = new JSONObject() .put("favoriteIds", favoriteIds) .put("csrfToken", client.getCsrfToken()) .put("csrfTimestamp", client.getCsrfTimestamp()) .put("csrfNotifyTimestamp", client.getCsrfNotifyTimestamp()) + .put("an", new JSONArray().put(eventData)) .put("uniq", getUniq()); RequestBody body = RequestBody.Companion.create(requestParams.toString(), JSON); Request request = new Request.Builder() diff --git a/common/src/main/java/ctbrec/sites/stripchat/StripchatUtil.java b/common/src/main/java/ctbrec/sites/stripchat/StripchatUtil.java index 67d8d8aa..b748230f 100644 --- a/common/src/main/java/ctbrec/sites/stripchat/StripchatUtil.java +++ b/common/src/main/java/ctbrec/sites/stripchat/StripchatUtil.java @@ -1,6 +1,6 @@ package ctbrec.sites.stripchat; -// import ctbrec.sites.stripchat.Stripchat; +import ctbrec.sites.stripchat.Stripchat; import java.text.MessageFormat; import java.time.Instant; import java.util.Random;