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
* Added setting to restrict recording by bit rate
* 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
* Streamate:
- 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
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
========================

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("Couples", API_URL, m -> Objects.equals("c", 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.setScene(scene);
tabs.add(followedTab);

View File

@ -8,13 +8,15 @@ import ctbrec.sites.camsoda.CamsodaModel;
import ctbrec.ui.SiteUiFactory;
import ctbrec.ui.tabs.PaginatedScheduledService;
import javafx.concurrent.Task;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Request;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.function.Predicate;
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.ONLINE;
@Slf4j
public class CamsodaUpdateService extends PaginatedScheduledService {
private static final Logger LOG = LoggerFactory.getLogger(CamsodaUpdateService.class);
protected final String url;
protected final boolean loginRequired;
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;
public CamsodaUpdateService(String url, boolean loginRequired, Camsoda camsoda, Predicate<CamsodaModel> filter) {
@ -46,7 +49,7 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
return new Task<>() {
@Override
public List<Model> call() throws IOException {
return loadOnlineModels().stream()
return getModelList().stream()
.sorted((m1, m2) -> (int) (m2.getSortOrder() - m1.getSortOrder()))
.filter(filter)
.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 {
if (loginRequired && StringUtil.isBlank(ctbrec.Config.getInstance().getSettings().camsodaUsername)) {
return Collections.emptyList();
} else {
LOG.debug("Fetching page {}", url);
log.debug("Fetching page {}", url);
if (loginRequired) {
SiteUiFactory.getUi(camsoda).login();
}
@ -87,27 +99,29 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
if (templateObject instanceof JSONObject) {
parseModelFromObject(result, templateObject, template, models);
} else if (templateObject instanceof JSONArray) {
parseModelFromArray(templateObject, template, models);
parseModelFromArray(result, templateObject, template, models);
}
} 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;
}
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 name = tpl.getString(getTemplateIndex(template, "username"));
CamsodaModel model = (CamsodaModel) camsoda.createModel(name);
model.setSortOrder(tpl.getFloat(getTemplateIndex(template, "sort_value")));
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"));
if (preview.startsWith("//")) {
preview = "https:" + preview;
}
model.setPreview(preview);
LOG.trace("Preview: {}", preview);
log.trace("Preview: {}", preview);
model.setOnlineStateByStatus(tpl.getString(getTemplateIndex(template, "status")));
var displayName = tpl.getString(getTemplateIndex(template, "display_name"));
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.setSortOrder(tpl.getFloat(Integer.toString(getTemplateIndex(template, "sort_value"))));
model.setNew(result.optBoolean("new"));
model.setVoyeur(result.optBoolean("voyeur"));
model.setGender(tpl.getString(Integer.toString(getTemplateIndex(template, "gender"))));
var preview = tpl.getString(Integer.toString(getTemplateIndex(template, "thumb")));
if (preview.startsWith("//")) {
@ -160,8 +175,4 @@ public class CamsodaUpdateService extends PaginatedScheduledService {
}
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 ctbrec.AbstractModel;
import ctbrec.Config;
import ctbrec.StringUtil;
import ctbrec.io.HttpException;
import ctbrec.recorder.download.StreamSource;
import lombok.Getter;
@ -21,6 +22,7 @@ import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ExecutionException;
@ -40,6 +42,9 @@ public class CamsodaModel extends AbstractModel {
private transient String gender;
@Getter
@Setter
private transient boolean isVoyeur;
@Getter
@Setter
private float sortOrder = 0;
private final Random random = new Random();
int[] resolution = new int[2];
@ -166,6 +171,9 @@ public class CamsodaModel extends AbstractModel {
JSONObject chat = result.getJSONObject("chat");
String status = chat.getString(STATUS);
setOnlineStateByStatus(status);
if (onlineState == OFFLINE) {
setLastSeen(chat.optString("lastOnlineAt"));
}
} catch (JSONException e) {
throw new IOException("Couldn't parse body as JSON:\n" + body, e);
}
@ -203,6 +211,16 @@ public class CamsodaModel extends AbstractModel {
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
public void invalidateCacheEntries() {
streamSources = null;
@ -219,7 +237,7 @@ public class CamsodaModel extends AbstractModel {
if (sources.isEmpty()) {
return new int[]{0, 0};
} else {
StreamSource src = sources.get(sources.size() - 1);
StreamSource src = sources.getLast();
resolution = new int[]{src.getWidth(), src.getHeight()};
return resolution;
}