Compare commits

...

40 Commits

Author SHA1 Message Date
J62 ceceb4dd6e fix top rated and trending 2025-03-16 00:07:23 -07:00
J62 1c1066d399 final db8 2025-03-15 23:52:44 -07:00
J62 a4984b02f1 db8 2025-03-15 23:33:42 -07:00
J62 98d6ece1a6 db8 2025-03-15 23:31:42 -07:00
J62 b1e1f9b2c7 db8 2025-03-15 23:25:21 -07:00
J62 45434afd96 db8 2025-03-15 23:21:54 -07:00
J62 2a0a3ad4a6 d7 2025-03-15 17:07:16 -07:00
J62 e7aae593cc d7 2025-03-15 17:04:25 -07:00
J62 2622305951 d7 2025-03-15 16:54:33 -07:00
J62 41ddc25cc9 d6 2025-03-15 16:43:27 -07:00
J62 809325ebac d5 2025-03-15 16:31:29 -07:00
J62 e58f491887 d5 2025-03-15 16:30:55 -07:00
J62 ec4e5e9ba2 d4 2025-03-15 16:29:10 -07:00
J62 819c80ca30 d3 2025-03-15 16:27:25 -07:00
J62 674a84dcb0 d2 2025-03-15 16:25:45 -07:00
J62 733cdc6da4 d2 2025-03-15 16:24:48 -07:00
J62 f59bf26d54 debug 2025-03-15 16:20:59 -07:00
J62 6dbbde9d80 FIX WRONG IMPORT 2025-03-15 16:14:14 -07:00
J62 6aec3a2354 test 2025-03-15 16:11:30 -07:00
J62 4cf096ff1a test live update NA filtering 2025-03-15 16:06:56 -07:00
J62 9c1cd6b073 .needsRestart(), 2025-03-15 14:04:46 -07:00
J62 bb91a319c4 typo 2025-03-15 13:55:52 -07:00
J62 0dc8bbc870 na filtering testing 2025-03-15 13:48:02 -07:00
J62 3f1a8f5b0a na testing 2025-03-15 13:44:46 -07:00
J62 bb9e679970 na filter 2025-03-15 13:41:00 -07:00
J62 3f4ae44bae test na only filter 2025-03-15 13:16:45 -07:00
J62 daa192c42b revert 2025-03-14 22:27:45 -07:00
J62 c47d7f7077 bump ver and test build numbering 2025-03-14 22:20:59 -07:00
J62 d747e4cf7a add optoin 2025-03-14 22:17:02 -07:00
J62 4753ad2eb4 temp revert 2025-03-14 19:20:11 -07:00
J62 713b95d05b update 2025-03-14 18:59:05 -07:00
J62 1a51c1b1bb fix 2025-03-14 14:43:28 -07:00
J62 9b0acfa1a2 rename 2025-03-14 14:39:30 -07:00
J62 226a8316e2 update 2025-03-14 14:24:11 -07:00
J62 f49f2dae43 news testing 2025-03-14 14:09:42 -07:00
J62 ad386bd5df fix news to allow to open urls properly 2025-03-14 14:09:29 -07:00
J62 9a5a469ad0 update news tab testing 2025-03-14 13:50:32 -07:00
J62 7a1d0ae197 typo 2025-03-14 11:32:21 -07:00
J62 a92c8b7753 add ignore .code-workspace 2025-03-14 11:30:25 -07:00
J62 20ebde9b73 2025-03-14 11:28:05 -07:00
19 changed files with 249 additions and 85 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@
.project
*/.factorypath
**/.antlr/
*.code-workspace

View File

@ -19,7 +19,8 @@ Support me:<br>
Bitcoin: bc1q7fvtkx8wklvd4zttsec7sfgxqh9zadk0x236lt <br>
Ether: 0x2e687A5628ff16c8f9624A914C1f727000089C3A <br>
Solana: Z5YwNPkLheSHuaSJjyHhg3L8UxjpJPt5WU6vu4hFsNR
Solana: Z5YwNPkLheSHuaSJjyHhg3L8UxjpJPt5WU6vu4hFsNR <br>
Monero: 47tjD1z63wu3FEnDCvWnFaRAZbpDKc3Ys1WCbgzvB2Gg8XbqU8bARpcCC37mWzuWBAeZPu2UGY4TAcYGhb6fptoTR8X9vjc
## A free recording software for different camsites. Currently supported: BongaCams, Cam4, CamSoda, Chaturbate, FC2Live, LiveJasmin, MyFreeCams, Streamate

View File

@ -8,7 +8,7 @@
<parent>
<groupId>ctbrec</groupId>
<artifactId>master</artifactId>
<version>5.3.3</version>
<version>5.3.4</version>
<relativePath>../master</relativePath>
</parent>

View File

@ -268,7 +268,8 @@ public class CamrecApplication extends Application {
tabPane.getTabs().add(new RecentlyWatchedTab(recorder, sites));
}
tabPane.getTabs().add(new SettingsTab(sites, recorder));
tabPane.getTabs().add(new NewsTab(config));
//tabPane.getTabs().add(new NewsTab(config));
tabPane.getTabs().add(new NewsTab(config, getHostServices()));
tabPane.getTabs().add(new DonateTabFx());
tabPane.getTabs().add(new HelpTab());
tabPane.getTabs().add(new LoggingTab());

View File

@ -6,17 +6,22 @@ import ctbrec.GlobalThreadPool;
import ctbrec.Version;
import ctbrec.io.HttpException;
import ctbrec.io.json.ObjectMapperFactory;
import ctbrec.ui.CamrecApplication;
import ctbrec.ui.controls.Dialogs;
import ctbrec.ui.tabs.TabSelectionListener;
import javafx.application.HostServices;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.layout.VBox;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
@ -27,16 +32,17 @@ import static ctbrec.io.HttpConstants.USER_AGENT;
@Slf4j
public class NewsTab extends Tab implements TabSelectionListener {
private static final String ACCESS_TOKEN = "a2804d73a89951a22e0f8483a6fcec8943afd88b7ba17c459c095aa9e6f94fd0";
private static final String URL = "https://mastodon.cloud/api/v1/accounts/480960/statuses?limit=20&exclude_replies=true";
private static final String URL = "https://git.ctbrec.com/api/v1/repos/j62/ctbrec/releases";
private final Config config;
private final HostServices hostServices;
private final VBox layout = new VBox();
private final ObjectMapper mapper = ObjectMapperFactory.getMapper();
private final OkHttpClient httpClient = new OkHttpClient();
public NewsTab(Config config) {
public NewsTab(Config config, HostServices hostServices) {
this.config = config;
setText("News");
this.hostServices = hostServices;
setText("Releases");
layout.setMaxWidth(800);
layout.setAlignment(Pos.CENTER);
setContent(new ScrollPane(layout));
@ -44,24 +50,21 @@ public class NewsTab extends Tab implements TabSelectionListener {
@Override
public void selected() {
GlobalThreadPool.submit(this::loadToots);
GlobalThreadPool.submit(this::loadReleases);
}
private void loadToots() {
private void loadReleases() {
try {
var request = new Request.Builder()
.url(URL)
.header("Authorization", "Bearer " + ACCESS_TOKEN)
.header(USER_AGENT, "ctbrec " + Version.getVersion())
.build();
try (var response = CamrecApplication.httpClient.execute(request)) {
try (Response response = httpClient.newCall(request).execute()) {
if (response.isSuccessful()) {
var body = Objects.requireNonNull(response.body(), HTTP_RESPONSE_BODY_IS_NULL).string();
log.debug(body);
if (body.startsWith("[")) {
onSuccess(body);
} else if (body.startsWith("{")) {
onError(body);
} else {
throw new IOException("Unexpected response: " + body);
}
@ -70,30 +73,53 @@ public class NewsTab extends Tab implements TabSelectionListener {
}
}
} catch (IOException e) {
log.info("Error while loading news", e);
Dialogs.showError(getTabPane().getScene(), "News", "Couldn't load news from mastodon", e);
}
}
private void onError(String body) throws IOException {
var json = new JSONObject(body);
if (json.has("error")) {
throw new IOException("Request not successful: " + json.getString("error"));
} else {
throw new IOException("Unexpected response: " + body);
log.info("Error while loading releases", e);
Dialogs.showError(getTabPane().getScene(), "Releases", "Couldn't load release information", e);
}
}
private void onSuccess(String body) throws IOException {
Status[] statusArray = mapper.readValue(body, Status[].class);
JSONArray releases = new JSONArray(body);
Platform.runLater(() -> {
layout.getChildren().clear();
for (Status status : statusArray) {
if (status.getInReplyToId() == null && !Objects.equals("direct", status.getVisibility())) {
var stp = new StatusPane(status, config.getDateTimeFormatter());
layout.getChildren().add(stp);
VBox.setMargin(stp, new Insets(10));
for (int i = 0; i < releases.length(); i++) {
JSONObject release = releases.getJSONObject(i);
String tagName = release.optString("tag_name", "Unknown Version");
String releaseName = release.optString("name", "No Name");
String description = release.optString("body", "No description available.");
JSONArray assets = release.optJSONArray("assets");
var releasePane = new VBox();
releasePane.setPadding(new Insets(10));
releasePane.setSpacing(5);
Label versionLabel = new Label("Version: " + tagName);
versionLabel.setStyle("-fx-font-weight: bold;");
Label nameLabel = new Label("Release: " + releaseName);
Label descLabel = new Label(description);
releasePane.getChildren().addAll(versionLabel, nameLabel, descLabel);
if (assets != null) {
Label assetsLabel = new Label("Assets:");
releasePane.getChildren().add(assetsLabel);
for (int j = 0; j < assets.length(); j++) {
JSONObject asset = assets.getJSONObject(j);
String assetName = asset.optString("name", "Unknown File");
String downloadUrl = asset.optString("browser_download_url", "#");
int size = asset.optInt("size", 0);
int downloads = asset.optInt("download_count", 0);
Hyperlink assetLink = new Hyperlink(assetName + " (" + size / 1024 / 1024 + " MB, " + downloads + " downloads)");
assetLink.setOnAction(event -> hostServices.showDocument(downloadUrl));
releasePane.getChildren().add(assetLink);
}
}
layout.getChildren().add(releasePane);
VBox.setMargin(releasePane, new Insets(10));
}
});
}

View File

@ -9,6 +9,7 @@ import ctbrec.Settings.ProxyType;
import ctbrec.docs.DocServer;
import ctbrec.recorder.Recorder;
import ctbrec.sites.Site;
import ctbrec.sites.chaturbate.Chaturbate;
import ctbrec.ui.DesktopIntegration;
import ctbrec.ui.SiteUI;
import ctbrec.ui.SiteUiFactory;
@ -16,6 +17,8 @@ import ctbrec.ui.controls.range.DiscreteRange;
import ctbrec.ui.settings.api.*;
import ctbrec.ui.sites.ConfigUI;
import ctbrec.ui.tabs.TabSelectionListener;
import ctbrec.ui.tabs.ThumbOverviewTab;
import ctbrec.ui.sites.chaturbate.ChaturbateTabProvider;
import javafx.animation.FadeTransition;
import javafx.animation.PauseTransition;
import javafx.animation.Transition;
@ -30,13 +33,16 @@ import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TextInputDialog;
import javafx.scene.control.TabPane;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.util.Duration;
import javafx.scene.Parent;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@ -70,6 +76,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
private SimpleBooleanProperty determineResolution;
private SimpleBooleanProperty chooseStreamQuality;
private SimpleBooleanProperty confirmationDialogs;
private SimpleBooleanProperty naCamsOnly;
private SimpleBooleanProperty livePreviews;
private SimpleBooleanProperty monitorClipboard;
private SimpleListProperty<String> startTab;
@ -152,7 +159,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
private void initializeProperties() {
flaresolverrApiUrl = new SimpleStringProperty(null, "flaresolverr.apiUrl", settings.flaresolverr.apiUrl);
flaresolverrTimeoutInMillis = new SimpleIntegerProperty(null, "flaresolverr.timeoutInMillis", settings.flaresolverr.timeoutInMillis);
flaresolverrUseForDomains = new SimpleJoinedStringListProperty(null, "flaresolverr.useForDomains", "\n",
flaresolverrUseForDomains = new SimpleJoinedStringListProperty(null, "flaresolverr.useForDomains", "\n",
FXCollections.observableList(settings.flaresolverr.useForDomains));
httpUserAgent = new SimpleStringProperty(null, "httpUserAgent", settings.httpUserAgent);
httpUserAgentMobile = new SimpleStringProperty(null, "httpUserAgentMobile", settings.httpUserAgentMobile);
@ -207,6 +214,14 @@ public class SettingsTab extends Tab implements TabSelectionListener {
onlineCheckSkipsPausedModels = new SimpleBooleanProperty(null, "onlineCheckSkipsPausedModels", settings.onlineCheckSkipsPausedModels);
fastScrollSpeed = new SimpleBooleanProperty(null, "fastScrollSpeed", settings.fastScrollSpeed);
confirmationDialogs = new SimpleBooleanProperty(null, "confirmationForDangerousActions", settings.confirmationForDangerousActions);
naCamsOnly = new SimpleBooleanProperty(null, "filterNAcamsOnly", settings.filterNAcamsOnly);
naCamsOnly.addListener((obs, oldValue, newValue) -> {
settings.filterNAcamsOnly = newValue;
Config.getInstance().getSettings().filterNAcamsOnly = newValue; // Ensure the config is updated
saveConfig();
refreshChaturbateTabs(); // Refresh tabs when the setting changes
});
useHlsdl = new SimpleBooleanProperty(null, "useHlsdl", settings.useHlsdl);
hlsdlExecutable = new SimpleFileProperty(null, "hlsdlExecutable", settings.hlsdlExecutable);
recentlyWatched = new SimpleBooleanProperty(null, "recentlyWatched", settings.recentlyWatched);
@ -230,6 +245,48 @@ public class SettingsTab extends Tab implements TabSelectionListener {
httpClientMaxRequestsPerHost = new SimpleIntegerProperty(null, "httpClientMaxRequestsPerHost", settings.httpClientMaxRequestsPerHost);
}
private void refreshChaturbateTabs() {
System.out.println("Refreshing Chaturbate Tabs..."); // Debugging output
// Find the Chaturbate tab
Tab chaturbateTab = null;
for (Tab tab : getTabPane().getTabs()) {
if (tab.getText().equalsIgnoreCase("Chaturbate")) {
chaturbateTab = tab;
break;
}
}
if (chaturbateTab == null) {
System.out.println("Chaturbate tab not found! Cannot refresh.");
return;
}
if (chaturbateTab instanceof ThumbOverviewTab overviewTab) {
overviewTab.getUpdateService().reset(); // Reset update service for new URLs
}
// Get the tab container inside the Chaturbate tab
if (chaturbateTab.getContent() instanceof Parent parent) {
var chaturbateTabsContainer = parent.lookup(".tab-pane");
if (chaturbateTabsContainer instanceof TabPane chaturbateTabPane) {
// Remove all existing Chaturbate sub-tabs
chaturbateTabPane.getTabs().clear();
// Get updated Chaturbate tabs and add them inside the Chaturbate section
var newTabs = SiteUiFactory.getUi(new Chaturbate()).getTabProvider().getTabs(getTabPane().getScene());
chaturbateTabPane.getTabs().addAll(newTabs);
System.out.println("Chaturbate tabs refreshed!");
return;
}
}
System.out.println("Could not find the correct TabPane inside Chaturbate tab!");
}
private void createGui() {
var postProcessingStepPanel = new PostProcessingStepPanel(config);
var variablesHelpButton = createHelpButton("Variables", "http://localhost:5689/docs/PostProcessing.md#variables");
@ -257,6 +314,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
Setting.of("Start minimized", startMinimized, "Start the app minimized to the tray, automatically activates \"Minimize to tray\""),
Setting.of("Add models from clipboard", monitorClipboard, "Monitor clipboard for model URLs and automatically add them to the recorder").needsRestart(),
Setting.of("Show confirmation dialogs", confirmationDialogs, "Show confirmation dialogs for irreversible actions"),
Setting.of("Show only North America Cams", naCamsOnly, "Show only North America Cams").needsRestart(),
Setting.of("Recording tab per site", recordedModelsPerSite, "Add a Recording tab for each site").needsRestart(),
Setting.of("Check for new versions at startup", checkForUpdates, "Search for updates every startup"),
Setting.of("Start Tab", startTab)),
@ -272,7 +330,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
Setting.of("Browser", browserOverride),
Setting.of("Start parameters", browserParams),
Setting.of("Force use (ignore default browser)", forceBrowserOverride, "Default behaviour will fallback to OS default if the above browser fails")),
Group.of("Flaresolverr",
Setting.of("API URL", flaresolverrApiUrl),
Setting.of("Request timeout", flaresolverrTimeoutInMillis),
@ -343,11 +401,11 @@ public class SettingsTab extends Tab implements TabSelectionListener {
Category.of("Advanced / Devtools",
Group.of("Networking",
Setting.of("Playlist request timeout (ms)", playlistRequestTimeout, "Timeout in ms for playlist requests"),
Setting.of("Max requests", httpClientMaxRequests,
Setting.of("Max requests", httpClientMaxRequests,
"The maximum number of requests to execute concurrently. Above this requests queue in memory,\n" + //
"waiting for the running calls to complete.\n\n" + //
"If more than [maxRequests] requests are in flight when this is invoked, those requests will remain in flight."),
Setting.of("Max requests per host", httpClientMaxRequestsPerHost,
Setting.of("Max requests per host", httpClientMaxRequestsPerHost,
"The maximum number of requests for each host to execute concurrently. This limits requests by\n" + //
"the URL's host name. Note that concurrent requests to a single IP address may still exceed this\n" + //
"limit: multiple hostnames may share an IP address or be routed through the same HTTP proxy.\n\n" + //
@ -363,7 +421,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
Setting.of("hlsdl executable", hlsdlExecutable, "Path to the hlsdl executable"),
Setting.of("Log hlsdl output", loghlsdlOutput, "Log hlsdl output to files in the system's temp directory")),
Group.of("Miscelaneous",
Setting.of("Config file saving delay (ms)", configSavingDelayMs,
Setting.of("Config file saving delay (ms)", configSavingDelayMs,
"Wait specified number of milliseconds before actually writing config to disk"))));
Region preferencesView = prefs.getView();
prefs.onRestartRequired(this::showRestartRequired);
@ -667,7 +725,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
private static final String DATE_FORMATTER_TOOLTIP = """
Leave empty for system default
Symbol Meaning Presentation Examples
------ ------- ------------ -------
G era text AD; Anno Domini; A
@ -676,7 +734,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
D day-of-year number 189
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
Y week-based-year year 1996; 96
w week-of-week-based-year number 27
@ -684,12 +742,12 @@ public class SettingsTab extends Tab implements TabSelectionListener {
E day-of-week text Tue; Tuesday; T
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
F week-of-month number 3
a am-pm-of-day text PM
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-am-pm (1-24) number 0
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
@ -697,16 +755,16 @@ public class SettingsTab extends Tab implements TabSelectionListener {
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
p pad next pad modifier 1
' escape for text delimiter
'' single quote literal '
[ optional section start

View File

@ -1,5 +1,6 @@
package ctbrec.ui.sites.chaturbate;
import ctbrec.Config;
import ctbrec.sites.chaturbate.Chaturbate;
import ctbrec.ui.sites.AbstractTabProvider;
import ctbrec.ui.tabs.PaginatedScheduledService;
@ -14,32 +15,47 @@ public class ChaturbateTabProvider extends AbstractTabProvider {
private final String apiUrl;
private final ChaturbateFollowedTab followedTab;
private final boolean regionNAEnabled; // Store the setting
public ChaturbateTabProvider(Chaturbate chaturbate) {
super(chaturbate);
apiUrl = site.getBaseUrl() + "/api/ts";
this.followedTab = new ChaturbateFollowedTab("Followed", apiUrl + "/roomlist/room-list/?enable_recommendations=false&follow=true", chaturbate);
regionNAEnabled = Config.getInstance().isFilterNAcamsOnlyEnabled();
this.followedTab = new ChaturbateFollowedTab("Followed", buildUrl("/roomlist/room-list/?enable_recommendations=false&follow=true"), chaturbate);
}
@Override
protected List<Tab> getSiteTabs(Scene scene) {
List<Tab> tabs = new ArrayList<>();
tabs.add(createTab("Featured", apiUrl + "/roomlist/room-list/?enable_recommendations=false"));
tabs.add(createTab("Female", apiUrl + "/roomlist/room-list/?enable_recommendations=false&genders=f"));
tabs.add(createTab("New Female", apiUrl + "/roomlist/room-list/?enable_recommendations=false&genders=f&new_cams=true"));
tabs.add(createTab("Male", apiUrl + "/roomlist/room-list/?enable_recommendations=false&genders=m"));
tabs.add(createTab("New Male", apiUrl + "/roomlist/room-list/?enable_recommendations=false&genders=m&new_cams=true"));
tabs.add(createTab("Couples", apiUrl + "/roomlist/room-list/?enable_recommendations=false&genders=c"));
tabs.add(createTab("Trans", apiUrl + "/roomlist/room-list/?enable_recommendations=false&genders=t"));
tabs.add(createTab("Private", apiUrl + "/roomlist/room-list/?enable_recommendations=false&private=true"));
tabs.add(createTab("Hidden", apiUrl + "/roomlist/room-list/?enable_recommendations=false&hidden=true"));
tabs.add(createTab("Gaming", apiUrl + "/roomlist/room-list/?enable_recommendations=false&gaming=true"));
tabs.add(createTab("Featured", buildUrl("/roomlist/room-list/?enable_recommendations=false")));
tabs.add(createTab("Female", buildUrl("/roomlist/room-list/?enable_recommendations=false&genders=f")));
tabs.add(createTab("New Female", buildUrl("/roomlist/room-list/?enable_recommendations=false&genders=f&new_cams=true")));
tabs.add(createTab("Milf", buildUrl("/roomlist/room-list/?enable_recommendations=false&hashtags=milf")));
tabs.add(createTab("Teen", buildUrl("/roomlist/room-list/?enable_recommendations=false&hashtags=teen")));
tabs.add(createTab("Creampie", buildUrl("/roomlist/room-list/?enable_recommendations=false&hashtags=creampie")));
tabs.add(createTab("BBW", buildUrl("/roomlist/room-list/?enable_recommendations=false&hashtags=bbw")));
tabs.add(createTab("Chubby", buildUrl("/roomlist/room-list/?enable_recommendations=false&hashtags=chubby")));
tabs.add(createTab("Pregnant", buildUrl("/roomlist/room-list/?enable_recommendations=false&hashtags=pregnant")));
tabs.add(createTab("Male", buildUrl("/roomlist/room-list/?enable_recommendations=false&genders=m")));
tabs.add(createTab("New Male", buildUrl("/roomlist/room-list/?enable_recommendations=false&genders=m&new_cams=true")));
tabs.add(createTab("Couples", buildUrl("/roomlist/room-list/?enable_recommendations=false&genders=c")));
tabs.add(createTab("Trans", buildUrl("/roomlist/room-list/?enable_recommendations=false&genders=t")));
tabs.add(createTab("N.American Cams", buildUrl("/roomlist/room-list/?enable_recommendations=false&regions=NA")));
tabs.add(createTab("6TPM private", buildUrl("/roomlist/room-list/?enable_recommendations=false&private_prices=6")));
tabs.add(createTab("Private", buildUrl("/roomlist/room-list/?enable_recommendations=false&private=true")));
tabs.add(createTab("Hidden", buildUrl("/roomlist/room-list/?enable_recommendations=false&hidden=true")));
tabs.add(createTab("Gaming", buildUrl("/roomlist/room-list/?enable_recommendations=false&gaming=true")));
followedTab.setScene(scene);
followedTab.setRecorder(recorder);
followedTab.setImageAspectRatio(9.0 / 16.0);
tabs.add(followedTab);
//tabs.add(createApiTab("Top Rated", buildUrl("/discover/carousels/top-rated/")));
//tabs.add(createApiTab("Trending", buildUrl("/discover/carousels/trending/")));
tabs.add(createApiTab("Top Rated", apiUrl + "/discover/carousels/top-rated/"));
tabs.add(createApiTab("Trending", apiUrl + "/discover/carousels/trending/"));
return tabs;
}
@ -64,4 +80,29 @@ public class ChaturbateTabProvider extends AbstractTabProvider {
var updateService = new ChaturbateApiUpdateService(apiUrl, (Chaturbate) site);
return createTab(title, updateService);
}
}
private String buildUrl(String endpoint) {
boolean filterNA = Config.getInstance().getSettings().filterNAcamsOnly; // Always check latest setting
// Do NOT modify "N.American Cams" - it should always have &regions=NA
if (endpoint.contains("&regions=NA")) {
return apiUrl + endpoint; // Keep it unchanged
}
// Ensure Top Rated & Trending use ? instead of &
if (filterNA && endpoint.contains("discover/carousels")) {
endpoint += "?regions=NA"; // Use ? instead of &
}
// For all other tabs (except N.American Cams), append &regions=NA if enabled
else if (filterNA) {
endpoint += "&regions=NA";
}
String url = apiUrl + endpoint;
System.out.println("Building URL: " + url); // Debugging output
return url;
}
}

View File

@ -24,9 +24,9 @@ public class DonateTabFx extends Tab {
var headerVbox = new VBox(10);
headerVbox.setAlignment(Pos.CENTER);
var beer = new Label("Buy me some beer?!");
var beer = new Label("Buy me a drink?!");
beer.setFont(new Font(36));
var desc = new Label("If you like this software and want to buy me some beer or pizza, here are some possibilities!");
var desc = new Label("If you like this software and want to buy me some MtDew or Energy Drinks(boost ctbrec dev productivity! :), Beer or Pizza, here are some possibilities!");
desc.setFont(new Font(24));
headerVbox.getChildren().addAll(beer, desc);
var header = new HBox();

View File

@ -773,6 +773,9 @@ public class ThumbOverviewTab extends Tab implements TabSelectionListener {
popoverTreeList.setRecorder(recorder);
}
public PaginatedScheduledService getUpdateService() {
return updateService;
}
@Override
public void selected() {
grid.getChildren().removeAll(noResultsFound, errorLabel);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -8,7 +8,7 @@
<parent>
<groupId>ctbrec</groupId>
<artifactId>master</artifactId>
<version>5.3.3</version>
<version>5.3.4</version>
<relativePath>../master</relativePath>
</parent>

View File

@ -162,6 +162,9 @@ public class Config {
String json = new String(fileContent, UTF_8).trim();
json = migrateJson(json);
settings = Objects.requireNonNull(mapper.readValue(json, Settings.class));
JSONObject jsonObject = new JSONObject(json);
settings.filterNAcamsOnly = jsonObject.optBoolean("filterNAcamsOnly", false);
settings.httpTimeout = Math.max(settings.httpTimeout, 10_000);
if (settings.recordingsDir.endsWith("/")) {
settings.recordingsDir = settings.recordingsDir.substring(0, settings.recordingsDir.length() - 1);
@ -221,11 +224,11 @@ public class Config {
}
}
}
private void migrateTo5_3_2(JSONObject json) {
if (json.has("chaturbateUseFlaresolverr") && json.has("flaresolverr")) {
var fsr = json.getJSONObject("flaresolverr");
if (!fsr.has("useForDomains") && json.getBoolean("chaturbateUseFlaresolverr")) {
fsr.put("useForDomains", new JSONArray().put("chaturbate.com"));
}
@ -257,11 +260,13 @@ public class Config {
if (savingDisabled) {
return;
}
String json = mapper.writeValueAsString(settings);
JSONObject jsonObject = new JSONObject(mapper.writeValueAsString(settings));
jsonObject.put("filterNAcamsOnly", settings.filterNAcamsOnly);
String jsonString = jsonObject.toString();
File configFile = new File(configDir, filename);
log.debug("Saving config to {}", configFile.getAbsolutePath());
Files.createDirectories(configDir.toPath());
Files.writeString(configFile.toPath(), json, CREATE, WRITE, TRUNCATE_EXISTING);
Files.writeString(configFile.toPath(), jsonString, CREATE, WRITE, TRUNCATE_EXISTING);
}
public static boolean isServerMode() {
@ -358,4 +363,8 @@ public class Config {
List<String> ignored = Config.getInstance().getSettings().ignoredModels;
return ignored.contains(model.getUrl());
}
public boolean isFilterNAcamsOnlyEnabled() {
return settings.filterNAcamsOnly;
}
}

View File

@ -127,6 +127,7 @@ public class Settings {
public String mfcPassword = "";
public String mfcUsername = "";
public boolean minimizeToTray = false;
public boolean filterNAcamsOnly = false;
@Deprecated
public int minimumLengthInSeconds = 0;
public long minimumSpaceLeftInBytes = 0;

View File

@ -42,7 +42,7 @@
<!-- Navigation -->
<nav class="navbar navbar-expand-lg bg-secondary fixed-top text-uppercase" id="mainNav">
<div class="container">
<a class="navbar-brand js-scroll-trigger" href="#page-top"><img src="https://raw.githubusercontent.com/0xboobface/ctbrec/master/client/src/main/resources/icon64.png" alt="Logo"/>CTB Recorder</a>
<a class="navbar-brand js-scroll-trigger" href="#page-top"><img src="https://git.ctbrec.com/j62/ctbrec/raw/branch/main/client/src/main/resources/icon64.png" alt="Logo"/>CTB Recorder</a>
<button class="navbar-toggler navbar-toggler-right text-uppercase bg-primary text-white rounded" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
Menu
<i class="fa fa-bars"></i>
@ -79,7 +79,7 @@
-->
<h1 class="text-uppercase mb-0">CTB Recorder</h1>
<hr class="star-light">
<h2 class="font-weight-light mb-0">A free recording software for different camsites.<br/>Currently supported: BongaCams, Cam4, CamSoda, Chaturbate, FC2Live, LiveJasmin, MyFreeCams, Streamate</h2>
<h2 class="font-weight-light mb-0">A free recording software for different camsites.<br/>Currently supported: Amateur.tv, BongaCams, Cam4, CamSoda, Chaturbate, CherryTV, Dreamcam, FC2Live, Flirt4free, LiveJasmin, MV Live, MyFreeCams, SecretFriends, Showup.tv, Streamate, Stripchat, Streamray, WinkTV, XLoveCam</h2>
</div>
</header>
@ -118,19 +118,19 @@
</script>
<div class="row text-center">
<div class="col">
<a id="download_windows" class="btn btn-xl btn-outline-dark" href="#donate" onclick="downloadFile('https://github.com/0xboobface/ctbrec/releases/download/1.18.0/ctbrec-1.18.0-win64-jre.zip');">
<a id="download_windows" class="btn btn-xl btn-outline-dark" href="#donate" onclick="downloadFile('https://git.ctbrec.com/j62/ctbrec/releases/download/5.3.3/ctbrec-5.3.3-win64-jre.zip');">
<i class="fa fa-windows mr-2"></i>
Download for Windows!
</a>
</div>
<div class="col">
<a id="download_macos" class="btn btn-xl btn-outline-dark" href="#donate" onclick="downloadFile('https://github.com/0xboobface/ctbrec/releases/download/1.18.0/ctbrec-1.18.0-macos-jre.zip');">
<a id="download_macos" class="btn btn-xl btn-outline-dark" href="#donate" onclick="downloadFile('https://git.ctbrec.com/j62/ctbrec/releases/download/5.3.3/ctbrec-5.3.3-macos-jre.zip');">
<i class="fa fa-apple mr-2"></i>
Download for macOS!
</a>
</div>
<div class="col">
<a id="download_linux" class="btn btn-xl btn-outline-dark" href="#donate" onclick="downloadFile('https://github.com/0xboobface/ctbrec/releases/download/1.18.0/ctbrec-1.18.0-linux-jre.zip');">
<a id="download_linux" class="btn btn-xl btn-outline-dark" href="#donate" onclick="downloadFile('https://git.ctbrec.com/j62/ctbrec/releases/download/5.3.3/ctbrec-5.3.3-linux-jre.zip');">
<i class="fa fa-linux mr-2"></i>
Download for Linux!
</a>
@ -147,7 +147,7 @@
<div class="col-lg-10 mx-auto text-center">
<p id="download-counter" class="lead text-center" style="display:none"></p>
<p class="lead">
CTB Recorder is free and open source. I'm a student and wrote this software in my spare time.
CTB Recorder is free and open source. (Originally created by 0xboobface). I'm a student and am helping with current development, hosting this site, git(source code repo) and more on servers I am providing for free in my spare time.
If you like the software or want to suggest a new feature, please consider buying me a coffee or two. Thanks!
</p>
</div>
@ -155,29 +155,21 @@
<div class="row text-center">
<div class="col">
<p class="lead">
<a href="https://www.buymeacoffee.com/0xboobface" target="_blank">
<a href="https://buymeacoffee.com/j62ctbrec" target="_blank">
<img src="img/buymeacoffee-round.png" alt="Buy a coffee" style="height: 160px; margin: 20px"/>
</a><br/>
<input type="button" value="Buy a coffee"
onclick="window.open('https://www.buymeacoffee.com/0xboobface','_blank')">
onclick="window.open('https://buymeacoffee.com/j62ctbrec','_blank')">
</p>
</div>
<div class="col">
<p class="lead">
<a href="https://www.paypal.me/0xb00bface" target="_blank">
<img src="img/paypal-round.png" alt="PayPal" style="height: 160px; margin: 20px"/>
</a><br/>
<input type="button" value="PayPal"
onclick="window.open('https://www.paypal.me/0xb00bface','_blank')">
</p>
</div>
<div class="col">
<p class="lead">
<a href="https://www.patreon.com/0xb00bface" target="_blank">
<a href="https://www.patreon.com/j62ctbrec" target="_blank">
<img src="img/patreon-round.png" alt="Patreon" style="height: 160px; margin: 20px"/>
</a><br/>
<input type="button" value="Patreon"
onclick="window.open('https://www.patreon.com/0xb00bface','_blank')">
onclick="window.open('https://www.patreon.com/j62ctbrec','_blank')">
</p>
</div>
</div>
@ -397,7 +389,7 @@
<div class="row">
<div class="col-lg-8 mx-auto text-center">
<p class="lead">
CTB Recorder is free and open source. The source code is available on <a href="https://github.com/0xboobface/ctbrec">Github</a>.
CTB Recorder is free and open source. The source code is available on <a href="https://git.ctbrec.com/j62/ctbrec">CTBRec Gitea</a>.
</p>
</div>
</div>

View File

@ -6,7 +6,7 @@
<groupId>ctbrec</groupId>
<artifactId>master</artifactId>
<packaging>pom</packaging>
<version>5.3.3</version>
<version>5.3.4</version>
<modules>
<module>../common</module>
@ -23,6 +23,8 @@
<jackson.version>2.15.1</jackson.version>
<org.mapstruct.version>1.5.3.Final</org.mapstruct.version>
<lombok.version>1.18.30</lombok.version>
<lombok.version>1.18.30</lombok.version>
<buildNumber>SNAPSHOT</buildNumber> <!-- Default value if none is provided -->
</properties>
<build>
@ -44,6 +46,27 @@
<redirectTestOutputToFile>true</redirectTestOutputToFile>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
<configuration>
<doCheck>true</doCheck>
<doUpdate>false</doUpdate>
<providerImplementations>
<svn>javasvn</svn>
<git>jgit</git>
</providerImplementations>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>

View File

@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}

View File

@ -8,7 +8,7 @@
<parent>
<groupId>ctbrec</groupId>
<artifactId>master</artifactId>
<version>5.3.3</version>
<version>5.3.4</version>
<relativePath>../master</relativePath>
</parent>