Added Voyeur tab for Camsoda

This commit is contained in:
0xb00bface 2023-12-30 22:34:16 +01:00
parent 9da5eb2eb4
commit 81f641b777
4 changed files with 49 additions and 18 deletions

View File

@ -12,6 +12,8 @@
* Changes by @WinkRU * Changes by @WinkRU
* Added setting to restrict recording by bit rate * Added setting to restrict recording by bit rate
* Added setting to use the shortest side to restrict the resolution * Added setting to use the shortest side to restrict the resolution
* Cam4: Fixed stream URLs search. Slightly increased chances to find good one.
* Camsoda: Added "Voyeur" tab
* Chaturbate: Added "Gaming" tab * Chaturbate: Added "Gaming" tab
* Streamate: * Streamate:
- Fixed "Couldn't load model ID" error while adding models by URL or by - Fixed "Couldn't load model ID" error while adding models by URL or by
@ -19,7 +21,6 @@
- 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

@ -33,6 +33,7 @@ public class CamsodaTabProvider extends AbstractTabProvider {
tabs.add(createTab("Male", API_URL, m -> Objects.equals("m", m.getGender()))); tabs.add(createTab("Male", API_URL, m -> Objects.equals("m", m.getGender())));
tabs.add(createTab("Couples", API_URL, m -> Objects.equals("c", m.getGender()))); tabs.add(createTab("Couples", API_URL, m -> Objects.equals("c", m.getGender())));
tabs.add(createTab("Trans", API_URL, m -> Objects.equals("t", m.getGender()))); tabs.add(createTab("Trans", API_URL, m -> Objects.equals("t", m.getGender())));
tabs.add(createTab("Voyeur", API_URL, CamsodaModel::isVoyeur));
followedTab.setRecorder(recorder); followedTab.setRecorder(recorder);
followedTab.setScene(scene); followedTab.setScene(scene);
tabs.add(followedTab); tabs.add(followedTab);

View File

@ -8,13 +8,15 @@ import ctbrec.sites.camsoda.CamsodaModel;
import ctbrec.ui.SiteUiFactory; import ctbrec.ui.SiteUiFactory;
import ctbrec.ui.tabs.PaginatedScheduledService; import ctbrec.ui.tabs.PaginatedScheduledService;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Request; import okhttp3.Request;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -23,15 +25,16 @@ import static ctbrec.ErrorMessages.HTTP_RESPONSE_BODY_IS_NULL;
import static ctbrec.Model.State.OFFLINE; import static ctbrec.Model.State.OFFLINE;
import static ctbrec.Model.State.ONLINE; import static ctbrec.Model.State.ONLINE;
@Slf4j
public class CamsodaUpdateService extends PaginatedScheduledService { public class CamsodaUpdateService extends PaginatedScheduledService {
private static final Logger LOG = LoggerFactory.getLogger(CamsodaUpdateService.class);
protected final String url; protected final String url;
protected final boolean loginRequired; protected final boolean loginRequired;
protected final Camsoda camsoda; protected final Camsoda camsoda;
protected int modelsPerPage = 50; protected int modelsPerPage = 60;
private static List<CamsodaModel> modelsList;
private static Instant lastListInfoRequest = Instant.EPOCH;
@Setter
protected Predicate<CamsodaModel> filter; protected Predicate<CamsodaModel> filter;
public CamsodaUpdateService(String url, boolean loginRequired, Camsoda camsoda, Predicate<CamsodaModel> filter) { public CamsodaUpdateService(String url, boolean loginRequired, Camsoda camsoda, Predicate<CamsodaModel> filter) {
@ -46,7 +49,7 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
return new Task<>() { return new Task<>() {
@Override @Override
public List<Model> call() throws IOException { public List<Model> call() throws IOException {
return loadOnlineModels().stream() return getModelList().stream()
.sorted((m1, m2) -> (int) (m2.getSortOrder() - m1.getSortOrder())) .sorted((m1, m2) -> (int) (m2.getSortOrder() - m1.getSortOrder()))
.filter(filter) .filter(filter)
.skip((page - 1) * (long) modelsPerPage) .skip((page - 1) * (long) modelsPerPage)
@ -56,11 +59,20 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
}; };
} }
private List<CamsodaModel> getModelList() throws IOException {
if (Objects.nonNull(modelsList) && Duration.between(lastListInfoRequest, Instant.now()).getSeconds() < 30) {
return modelsList;
}
lastListInfoRequest = Instant.now();
modelsList = loadOnlineModels();
return Optional.ofNullable(modelsList).orElse(Collections.emptyList());
}
protected List<CamsodaModel> loadOnlineModels() throws IOException { protected List<CamsodaModel> loadOnlineModels() throws IOException {
if (loginRequired && StringUtil.isBlank(ctbrec.Config.getInstance().getSettings().camsodaUsername)) { if (loginRequired && StringUtil.isBlank(ctbrec.Config.getInstance().getSettings().camsodaUsername)) {
return Collections.emptyList(); return Collections.emptyList();
} else { } else {
LOG.debug("Fetching page {}", url); log.debug("Fetching page {}", url);
if (loginRequired) { if (loginRequired) {
SiteUiFactory.getUi(camsoda).login(); SiteUiFactory.getUi(camsoda).login();
} }
@ -87,27 +99,29 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
if (templateObject instanceof JSONObject) { if (templateObject instanceof JSONObject) {
parseModelFromObject(result, templateObject, template, models); parseModelFromObject(result, templateObject, template, models);
} else if (templateObject instanceof JSONArray) { } else if (templateObject instanceof JSONArray) {
parseModelFromArray(templateObject, template, models); parseModelFromArray(result, templateObject, template, models);
} }
} catch (Exception e) { } catch (Exception e) {
LOG.warn("Couldn't parse one of the models: {}", result, e); log.warn("Couldn't parse one of the models: {}", result, e);
} }
} }
return models; return models;
} }
private void parseModelFromArray(Object templateObject, JSONArray template, List<CamsodaModel> models) { private void parseModelFromArray(JSONObject result, Object templateObject, JSONArray template, List<CamsodaModel> models) {
var tpl = (JSONArray) templateObject; var tpl = (JSONArray) templateObject;
var name = tpl.getString(getTemplateIndex(template, "username")); var name = tpl.getString(getTemplateIndex(template, "username"));
CamsodaModel model = (CamsodaModel) camsoda.createModel(name); CamsodaModel model = (CamsodaModel) camsoda.createModel(name);
model.setSortOrder(tpl.getFloat(getTemplateIndex(template, "sort_value"))); model.setSortOrder(tpl.getFloat(getTemplateIndex(template, "sort_value")));
model.setDescription(tpl.getString(getTemplateIndex(template, "subject_html"))); model.setDescription(tpl.getString(getTemplateIndex(template, "subject_html")));
model.setNew(result.optBoolean("new"));
model.setVoyeur(result.optBoolean("voyeur"));
var preview = tpl.getString(getTemplateIndex(template, "thumb")); var preview = tpl.getString(getTemplateIndex(template, "thumb"));
if (preview.startsWith("//")) { if (preview.startsWith("//")) {
preview = "https:" + preview; preview = "https:" + preview;
} }
model.setPreview(preview); model.setPreview(preview);
LOG.trace("Preview: {}", preview); log.trace("Preview: {}", preview);
model.setOnlineStateByStatus(tpl.getString(getTemplateIndex(template, "status"))); model.setOnlineStateByStatus(tpl.getString(getTemplateIndex(template, "status")));
var displayName = tpl.getString(getTemplateIndex(template, "display_name")); var displayName = tpl.getString(getTemplateIndex(template, "display_name"));
model.setDisplayName(displayName.replaceAll("[^a-zA-Z0-9]", "")); model.setDisplayName(displayName.replaceAll("[^a-zA-Z0-9]", ""));
@ -124,6 +138,7 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
model.setDescription(tpl.getString(Integer.toString(getTemplateIndex(template, "subject_html")))); model.setDescription(tpl.getString(Integer.toString(getTemplateIndex(template, "subject_html"))));
model.setSortOrder(tpl.getFloat(Integer.toString(getTemplateIndex(template, "sort_value")))); model.setSortOrder(tpl.getFloat(Integer.toString(getTemplateIndex(template, "sort_value"))));
model.setNew(result.optBoolean("new")); model.setNew(result.optBoolean("new"));
model.setVoyeur(result.optBoolean("voyeur"));
model.setGender(tpl.getString(Integer.toString(getTemplateIndex(template, "gender")))); model.setGender(tpl.getString(Integer.toString(getTemplateIndex(template, "gender"))));
var preview = tpl.getString(Integer.toString(getTemplateIndex(template, "thumb"))); var preview = tpl.getString(Integer.toString(getTemplateIndex(template, "thumb")));
if (preview.startsWith("//")) { if (preview.startsWith("//")) {
@ -160,8 +175,4 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
} }
throw new NoSuchElementException(string + " not found in template: " + template); throw new NoSuchElementException(string + " not found in template: " + template);
} }
public void setFilter(Predicate<CamsodaModel> filter) {
this.filter = filter;
}
} }

View File

@ -7,6 +7,7 @@ import com.iheartradio.m3u8.data.PlaylistData;
import com.iheartradio.m3u8.data.StreamInfo; import com.iheartradio.m3u8.data.StreamInfo;
import ctbrec.AbstractModel; import ctbrec.AbstractModel;
import ctbrec.Config; import ctbrec.Config;
import ctbrec.StringUtil;
import ctbrec.io.HttpException; import ctbrec.io.HttpException;
import ctbrec.recorder.download.StreamSource; import ctbrec.recorder.download.StreamSource;
import lombok.Getter; import lombok.Getter;
@ -21,6 +22,7 @@ import org.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@ -40,6 +42,9 @@ public class CamsodaModel extends AbstractModel {
private transient String gender; private transient String gender;
@Getter @Getter
@Setter @Setter
private transient boolean isVoyeur;
@Getter
@Setter
private float sortOrder = 0; private float sortOrder = 0;
private final Random random = new Random(); private final Random random = new Random();
int[] resolution = new int[2]; int[] resolution = new int[2];
@ -166,6 +171,9 @@ public class CamsodaModel extends AbstractModel {
JSONObject chat = result.getJSONObject("chat"); JSONObject chat = result.getJSONObject("chat");
String status = chat.getString(STATUS); String status = chat.getString(STATUS);
setOnlineStateByStatus(status); setOnlineStateByStatus(status);
if (onlineState == OFFLINE) {
setLastSeen(chat.optString("lastOnlineAt"));
}
} catch (JSONException e) { } catch (JSONException e) {
throw new IOException("Couldn't parse body as JSON:\n" + body, e); throw new IOException("Couldn't parse body as JSON:\n" + body, e);
} }
@ -203,6 +211,16 @@ public class CamsodaModel extends AbstractModel {
return onlineState; return onlineState;
} }
private void setLastSeen(String date) {
try {
if (StringUtil.isNotBlank(date)) {
setLastSeen(Instant.parse(date.replace("+0000", ".00Z")));
}
} catch (Exception e) {
// fail silently
}
}
@Override @Override
public void invalidateCacheEntries() { public void invalidateCacheEntries() {
streamSources = null; streamSources = null;
@ -219,7 +237,7 @@ public class CamsodaModel extends AbstractModel {
if (sources.isEmpty()) { if (sources.isEmpty()) {
return new int[]{0, 0}; return new int[]{0, 0};
} else { } else {
StreamSource src = sources.get(sources.size() - 1); StreamSource src = sources.getLast();
resolution = new int[]{src.getWidth(), src.getHeight()}; resolution = new int[]{src.getWidth(), src.getHeight()};
return resolution; return resolution;
} }