Compare commits

...

7 Commits

Author SHA1 Message Date
Jafea7 178e6a8eb0 v25.09.27 2025-09-27 16:05:33 +10:00
Jafea7 7409bb8fa9 Fix CheckURL for SC (WinkRU) 2025-09-27 16:05:20 +10:00
Jafea7 fa609157c5 Add BC URL warning 2025-09-27 16:04:42 +10:00
Jafea7 367cbd659e Added checks for CGF search 2025-09-27 16:04:15 +10:00
Jafea7 b746f52f67 Implement PAC (Proxy Auto Config) 2025-09-27 16:03:36 +10:00
Jafea7 63440b5ea8 v25.09.22 2025-09-22 22:14:23 +10:00
Jafea7 231a6c9456 Change default contact sheet name 2025-09-22 22:09:00 +10:00
19 changed files with 149 additions and 38 deletions

View File

@ -11,6 +11,19 @@ If this version doesn't do what you want, don't use it ... simple.
Changes from reusedname's v5.3.2 version. Changes from reusedname's v5.3.2 version.
25.09.27
========================
* Added BongaCams URL entry warning
* Added model online detection for CGF search, sends name instead of image URL if offline
Note: Sending name for FC2 will fail to find matches (Japanese), DC cannot send URL to preview image (wrong mimetype).
* Implement PAC (Proxy Auto Configuration)
* Fix CheckURL for SC (WinkRU)
25.09.22
========================
* Adjust -Xmx parameter, increase Heap size
* Change default contact sheet filename
25.09.15 25.09.15
======================== ========================
* Add missing UserAgent parameter to minimal-browser call for some sites * Add missing UserAgent parameter to minimal-browser call for some sites

View File

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

View File

@ -26,6 +26,12 @@
<filtered>true</filtered> <filtered>true</filtered>
<destName>ctbrec-no-splash.sh</destName> <destName>ctbrec-no-splash.sh</destName>
</file> </file>
<file>
<source>${project.basedir}/src/assembly/pac.js</source>
<outputDirectory>ctbrec</outputDirectory>
<filtered>true</filtered>
<destName>pac.js</destName>
</file>
<file> <file>
<source>${project.build.directory}/${project.artifactId}-${project.version}.jar</source> <source>${project.build.directory}/${project.artifactId}-${project.version}.jar</source>
<outputDirectory>ctbrec</outputDirectory> <outputDirectory>ctbrec</outputDirectory>

View File

@ -26,6 +26,12 @@
<filtered>true</filtered> <filtered>true</filtered>
<destName>ctbrec-no-splash.sh</destName> <destName>ctbrec-no-splash.sh</destName>
</file> </file>
<file>
<source>${project.basedir}/src/assembly/pac.js</source>
<outputDirectory>ctbrec</outputDirectory>
<filtered>true</filtered>
<destName>pac.js</destName>
</file>
<file> <file>
<source>${project.build.directory}/${project.artifactId}-${project.version}.jar <source>${project.build.directory}/${project.artifactId}-${project.version}.jar
</source> </source>

View File

@ -0,0 +1,9 @@
function FindProxyForURL(url, host) {
if (shExpMatch(host, "*stripchat.com")) {
return "SOCKS 127.0.0.1:1080";
} else if (shExpMatch(host, "*chaturbate.com")) {
return "PROXY 127.0.0.1:8080";
} else {
return "DIRECT";
}
}

View File

@ -22,6 +22,12 @@
<source>${project.build.directory}/ctbrec-no-splash.exe</source> <source>${project.build.directory}/ctbrec-no-splash.exe</source>
<outputDirectory>ctbrec</outputDirectory> <outputDirectory>ctbrec</outputDirectory>
</file> </file>
<file>
<source>${project.basedir}/src/assembly/pac.js</source>
<outputDirectory>ctbrec</outputDirectory>
<filtered>true</filtered>
<destName>pac.js</destName>
</file>
<file> <file>
<source>${project.build.directory}/${project.artifactId}-${project.version}.jar</source> <source>${project.build.directory}/${project.artifactId}-${project.version}.jar</source>
<outputDirectory>ctbrec</outputDirectory> <outputDirectory>ctbrec</outputDirectory>

View File

@ -2,6 +2,7 @@ package ctbrec.ui.menu;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import ctbrec.AbstractModel;
import ctbrec.Config; import ctbrec.Config;
import ctbrec.Model; import ctbrec.Model;
import ctbrec.ModelGroup; import ctbrec.ModelGroup;
@ -21,7 +22,9 @@ import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.control.TabPane; import javafx.scene.control.TabPane;
import javafx.scene.input.Clipboard; import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent; import javafx.scene.input.ClipboardContent;
import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.concurrent.ExecutionException;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -162,13 +165,22 @@ public class ModelMenuContributor {
var openOnCamGirlFinder = new MenuItem("CamGirlFinder"); var openOnCamGirlFinder = new MenuItem("CamGirlFinder");
openOnCamGirlFinder.setOnAction(e -> { openOnCamGirlFinder.setOnAction(e -> {
for (Model model : selectedModels) { for (Model model : selectedModels) {
try {
String query;
String preview = model.getPreview(); String preview = model.getPreview();
if (preview != null && !preview.isEmpty()) { if (preview != null && !preview.isEmpty() && model.isOnline(true) && !preview.toLowerCase().contains("dreamcam")) {
String query = URLEncoder.encode(preview, UTF_8); query = URLEncoder.encode(preview, UTF_8);
DesktopIntegration.open("https://camgirlfinder.net/search?url=" + query); DesktopIntegration.open("https://camgirlfinder.net/search?url=" + query);
} else { continue;
String query = URLEncoder.encode(model.getName(), UTF_8); }
DesktopIntegration.open("https://camgirlfinder.net/models?m=" + query + "&p=a&g=a"); query = URLEncoder.encode(model.getName(), UTF_8);
DesktopIntegration.open("https://camgirlfinder.net/models?model=" + query + "&platform=&gender=");
} catch (IOException | ExecutionException | InterruptedException ex) {
// Handle the exception (e.g., log it or show an error dialog)
Dialogs.showError(menu.getParentPopup().getScene(),
"Error encoding URL",
"Failed to encode the model name or preview URL: " + ex.getMessage(),
null);
} }
} }
}); });

View File

@ -85,6 +85,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
private SimpleStringProperty proxyPort; private SimpleStringProperty proxyPort;
private SimpleStringProperty proxyUser; private SimpleStringProperty proxyUser;
private SimpleStringProperty proxyPassword; private SimpleStringProperty proxyPassword;
private SimpleStringProperty pacUrl;
private SimpleDirectoryProperty recordingsDir; private SimpleDirectoryProperty recordingsDir;
private SimpleListProperty<DirectoryStructure> directoryStructure; private SimpleListProperty<DirectoryStructure> directoryStructure;
private SimpleListProperty<SplitAfterOption> splitAfter; private SimpleListProperty<SplitAfterOption> splitAfter;
@ -169,11 +170,12 @@ public class SettingsTab extends Tab implements TabSelectionListener {
maximumResolutionPlayer = new SimpleIntegerProperty(null, "maximumResolutionPlayer", settings.maximumResolutionPlayer); maximumResolutionPlayer = new SimpleIntegerProperty(null, "maximumResolutionPlayer", settings.maximumResolutionPlayer);
showPlayerStarting = new SimpleBooleanProperty(null, "showPlayerStarting", settings.showPlayerStarting); showPlayerStarting = new SimpleBooleanProperty(null, "showPlayerStarting", settings.showPlayerStarting);
singlePlayer = new SimpleBooleanProperty(null, "singlePlayer", settings.singlePlayer); singlePlayer = new SimpleBooleanProperty(null, "singlePlayer", settings.singlePlayer);
proxyType = new SimpleListProperty<>(null, "proxyType", FXCollections.observableList(List.of(DIRECT, HTTP, SOCKS4, SOCKS5))); proxyType = new SimpleListProperty<>(null, "proxyType", FXCollections.observableList(List.of(DIRECT, HTTP, SOCKS4, SOCKS5, PAC)));
proxyHost = new SimpleStringProperty(null, "proxyHost", settings.proxyHost); proxyHost = new SimpleStringProperty(null, "proxyHost", settings.proxyHost);
proxyPort = new SimpleStringProperty(null, "proxyPort", settings.proxyPort); proxyPort = new SimpleStringProperty(null, "proxyPort", settings.proxyPort);
proxyUser = new SimpleStringProperty(null, "proxyUser", settings.proxyUser); proxyUser = new SimpleStringProperty(null, "proxyUser", settings.proxyUser);
proxyPassword = new SimpleStringProperty(null, "proxyPassword", settings.proxyPassword); proxyPassword = new SimpleStringProperty(null, "proxyPassword", settings.proxyPassword);
pacUrl = new SimpleStringProperty(null, "pacUrl", settings.pacUrl);
recordingsDir = new SimpleDirectoryProperty(null, "recordingsDir", settings.recordingsDir); recordingsDir = new SimpleDirectoryProperty(null, "recordingsDir", settings.recordingsDir);
directoryStructure = new SimpleListProperty<>(null, "recordingsDirStructure", directoryStructure = new SimpleListProperty<>(null, "recordingsDirStructure",
FXCollections.observableList(List.of(FLAT, ONE_PER_MODEL, ONE_PER_GROUP, ONE_PER_RECORDING))); FXCollections.observableList(List.of(FLAT, ONE_PER_MODEL, ONE_PER_GROUP, ONE_PER_RECORDING)));
@ -337,7 +339,8 @@ public class SettingsTab extends Tab implements TabSelectionListener {
Setting.of("Host", proxyHost).needsRestart(), Setting.of("Host", proxyHost).needsRestart(),
Setting.of("Port", proxyPort).needsRestart(), Setting.of("Port", proxyPort).needsRestart(),
Setting.of("Username", proxyUser).needsRestart(), Setting.of("Username", proxyUser).needsRestart(),
Setting.of("Password", proxyPassword).needsRestart())), Setting.of("Password", proxyPassword).needsRestart(),
Setting.of("PAC URL", pacUrl, "URL to your Proxy Auto-Config (PAC) file (e.g. http://example.com/pac.js or file:///G:/path/to/pac.js)").needsRestart())),
Category.of("Advanced / Devtools", Category.of("Advanced / Devtools",
Group.of("Networking", Group.of("Networking",
Setting.of("Playlist request timeout (ms)", playlistRequestTimeout, "Timeout in ms for playlist requests"), Setting.of("Playlist request timeout (ms)", playlistRequestTimeout, "Timeout in ms for playlist requests"),

View File

@ -406,11 +406,20 @@ public abstract class AbstractRecordedModelsTab extends Tab implements TabSelect
} }
protected void addModelByUrl(String url) { protected void addModelByUrl(String url) {
if (url.toLowerCase().contains("bonga")) {
Dialogs.showError(getTabPane().getScene(),
"Do not use URLs for BongaCams",
"Use 'BongaCams:<model>' where <model> is obtained from the models page:\nCtrl+u, Ctrl+f, \"username\"",
null);
return;
}
for (Site site : sites) { for (Site site : sites) {
var newModel = site.createModelFromUrl(url); var newModel = site.createModelFromUrl(url);
if (newModel != null) { if (newModel != null) {
if (getMarkModelsForLaterRecording()) { if (getMarkModelsForLaterRecording()) {
new MarkForLaterRecordingAction(modelInputField, List.of(newModel), true, recorder).execute(m -> Platform.runLater(this::reload)); new MarkForLaterRecordingAction(modelInputField, List.of(newModel), true, recorder)
.execute(m -> Platform.runLater(this::reload));
} else { } else {
new StartRecordingAction(modelInputField, List.of(newModel), recorder) new StartRecordingAction(modelInputField, List.of(newModel), recorder)
.execute() .execute()

View File

@ -8,7 +8,7 @@
<parent> <parent>
<groupId>ctbrec</groupId> <groupId>ctbrec</groupId>
<artifactId>master</artifactId> <artifactId>master</artifactId>
<version>25.9.15</version> <version>25.9.27</version>
<relativePath>../master</relativePath> <relativePath>../master</relativePath>
</parent> </parent>
@ -41,6 +41,11 @@
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId> <artifactId>jackson-datatype-jsr310</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.bidib.com.github.markusbernhardt</groupId>
<artifactId>proxy-vole</artifactId>
<version>1.1.6</version>
</dependency>
<dependency> <dependency>
<groupId>org.mapstruct</groupId> <groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <artifactId>mapstruct</artifactId>

View File

@ -32,7 +32,8 @@ public class Settings {
DIRECT, DIRECT,
HTTP, HTTP,
SOCKS4, SOCKS4,
SOCKS5 SOCKS5,
PAC
} }
public enum SplitStrategy { public enum SplitStrategy {
@ -151,6 +152,7 @@ public class Settings {
public String proxyPort; public String proxyPort;
public ProxyType proxyType = ProxyType.DIRECT; public ProxyType proxyType = ProxyType.DIRECT;
public String proxyUser; public String proxyUser;
public String pacUrl;
public boolean recentlyWatched = true; public boolean recentlyWatched = true;
public List<String> recordLaterTableColumnOrder = new ArrayList<>(); public List<String> recordLaterTableColumnOrder = new ArrayList<>();
public Map<String, Boolean> recordLaterTableColumnVisibility = new HashMap<>(); public Map<String, Boolean> recordLaterTableColumnVisibility = new HashMap<>();

View File

@ -1,5 +1,7 @@
package ctbrec.io; package ctbrec.io;
import com.github.markusbernhardt.proxy.selector.pac.PacProxySelector;
import com.github.markusbernhardt.proxy.selector.pac.UrlPacScriptSource;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import ctbrec.Config; import ctbrec.Config;
import ctbrec.LoggingInterceptor; import ctbrec.LoggingInterceptor;
@ -20,6 +22,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.Authenticator; import java.net.Authenticator;
import java.net.PasswordAuthentication; import java.net.PasswordAuthentication;
import java.net.ProxySelector;
import java.nio.file.Files; import java.nio.file.Files;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -116,6 +119,18 @@ public abstract class HttpClient {
Authenticator.setDefault(new SocksProxyAuth(username, password)); Authenticator.setDefault(new SocksProxyAuth(username, password));
} }
break; break;
case PAC:
String pacUrl = config.getSettings().pacUrl;
if (pacUrl != null && !pacUrl.isEmpty()) {
try {
UrlPacScriptSource pacSource = new UrlPacScriptSource(pacUrl);
ProxySelector pacSelector = new PacProxySelector(pacSource);
ProxySelector.setDefault(pacSelector);
} catch (Exception e) {
log.warn("Failed to load PAC file: {}", e.getMessage());
}
}
break;
case DIRECT: case DIRECT:
default: default:
System.clearProperty(ProxyConstants.HTTP_PROXY_HOST); System.clearProperty(ProxyConstants.HTTP_PROXY_HOST);
@ -248,6 +263,10 @@ public abstract class HttpClient {
} }
} }
if (config.getSettings().proxyType == ProxyType.PAC) {
builder.proxySelector(ProxySelector.getDefault());
}
ProxyType proxyType = config.getSettings().proxyType; ProxyType proxyType = config.getSettings().proxyType;
if (proxyType == ProxyType.HTTP) { if (proxyType == ProxyType.HTTP) {
String username = config.getSettings().proxyUser; String username = config.getSettings().proxyUser;

View File

@ -45,7 +45,7 @@ public class CreateContactSheet extends AbstractPlaceholderAwarePostProcessor {
int cols = Integer.parseInt(getConfig().getOrDefault(COLS, "8")); int cols = Integer.parseInt(getConfig().getOrDefault(COLS, "8"));
int rows = Integer.parseInt(getConfig().getOrDefault(ROWS, "7")); int rows = Integer.parseInt(getConfig().getOrDefault(ROWS, "7"));
String color = getConfig().getOrDefault(BACKGROUND, "0x333333"); String color = getConfig().getOrDefault(BACKGROUND, "0x333333");
String filename = getConfig().getOrDefault(FILENAME, "contactsheet.jpg"); String filename = getConfig().getOrDefault(FILENAME, "$sanitize(${modelName})_$sanitize(${siteName})_$format(${localDateTime},yyyyMMdd-HHmmss).jpg");
int thumbWidth = (int) ((totalWidth - (cols + 1) * padding) / (double)cols); int thumbWidth = (int) ((totalWidth - (cols + 1) * padding) / (double)cols);
Path tempDir = createThumbnails(rec, config); Path tempDir = createThumbnails(rec, config);

View File

@ -6,7 +6,7 @@ import com.iheartradio.m3u8.data.MasterPlaylist;
import com.iheartradio.m3u8.data.Playlist; import com.iheartradio.m3u8.data.Playlist;
import com.iheartradio.m3u8.data.PlaylistData; import com.iheartradio.m3u8.data.PlaylistData;
import ctbrec.AbstractModel; import ctbrec.AbstractModel;
// import ctbrec.Config; import ctbrec.Config;
import ctbrec.StringUtil; import ctbrec.StringUtil;
import ctbrec.io.HttpException; import ctbrec.io.HttpException;
import ctbrec.io.json.ObjectMapperFactory; import ctbrec.io.json.ObjectMapperFactory;
@ -383,21 +383,23 @@ public class ChaturbateModel extends AbstractModel {
@Override @Override
public boolean exists() throws IOException { public boolean exists() throws IOException {
Request req = new Request.Builder() // @formatter:off Request req = new Request.Builder()
.url(getUrl()) .url(getUrl())
.header(USER_AGENT, site.getHttpClient().getEffectiveUserAgent()) .header("Accept", "*/*")
.header(ACCEPT_LANGUAGE, Locale.ENGLISH.getLanguage()) .header("Accept-Language", Locale.ENGLISH.getLanguage())
.build(); // @formatter:on .header("User-Agent", Config.getInstance().getSettings().httpUserAgent)
.header("Referer", getUrl())
.header("Origin", getSite().getBaseUrl())
.build();
try (Response response = getSite().getHttpClient().execute(req)) { try (Response response = getSite().getHttpClient().execute(req)) {
if (!response.isSuccessful() && response.code() == 404) { if (response.code() == 404) {
return false; return false;
} else { }
String body = response.body().string(); String body = response.body().string();
boolean banned = body.contains("This room has been banned"); boolean banned = body.contains("This room has been banned");
boolean deleted = body.contains("This account has been deleted"); boolean deleted = body.contains("This account has been deleted");
boolean redirectedToRoot = Objects.equals("/", response.request().url().encodedPath()); boolean redirectedToRoot = Objects.equals("/", response.request().url().encodedPath());
return !(banned || deleted || redirectedToRoot); return !banned && !deleted && !redirectedToRoot;
}
} }
} }

View File

@ -426,18 +426,22 @@ public class StripchatModel extends AbstractModel {
@Override @Override
public boolean exists() throws IOException { public boolean exists() throws IOException {
try {
JSONObject jsonResponse = getModelInfo(); JSONObject jsonResponse = getModelInfo();
if (jsonResponse.optString("error").equals("Not Found")) {
log.info("Model not found: {}", getName());
return false;
}
if (jsonResponse.has("user")) { if (jsonResponse.has("user")) {
JSONObject user = jsonResponse.getJSONObject("user"); JSONObject user = jsonResponse.getJSONObject("user").getJSONObject("user");
if (isBanned(user)) { if (isBanned(user)) {
log.info("Model inactive or deleted: {}", getName()); log.info("Model inactive or deleted: {}", getName());
return false; return false;
} }
} }
} catch (HttpException e) {
if (e.getResponseCode() == 404) {
log.info("Model not found: {}", getName());
return false;
}
throw e;
}
return true; return true;
} }

View File

@ -11,7 +11,7 @@
<groupId>ctbrec</groupId> <groupId>ctbrec</groupId>
<artifactId>master</artifactId> <artifactId>master</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>25.9.15</version> <version>25.9.27</version>
<modules> <modules>
<module>../common</module> <module>../common</module>

View File

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

View File

@ -0,0 +1,9 @@
function FindProxyForURL(url, host) {
if (shExpMatch(host, "*stripchat.com")) {
return "SOCKS 127.0.0.1:1080";
} else if (shExpMatch(host, "*chaturbate.com")) {
return "PROXY 127.0.0.1:8080";
} else {
return "DIRECT";
}
}

View File

@ -30,6 +30,12 @@
<source>${project.basedir}/LICENSE.txt</source> <source>${project.basedir}/LICENSE.txt</source>
<outputDirectory>ctbrec</outputDirectory> <outputDirectory>ctbrec</outputDirectory>
</file> </file>
<file>
<source>${project.basedir}/src/assembly/pac.js</source>
<outputDirectory>ctbrec</outputDirectory>
<filtered>true</filtered>
<destName>pac.js</destName>
</file>
<file> <file>
<source>${project.basedir}/../CHANGELOG.md</source> <source>${project.basedir}/../CHANGELOG.md</source>
<outputDirectory>ctbrec</outputDirectory> <outputDirectory>ctbrec</outputDirectory>