Compare commits

...

3 Commits

Author SHA1 Message Date
Jafea7 4876cad920 Update version 25.06.02 2025-06-02 17:33:43 +10:00
Jafea7 90d0479ea7 Update HelpTab.java 2025-06-02 17:29:54 +10:00
Jafea7 23b38bb6a2 Add search to Ignore list 2025-06-02 17:29:32 +10:00
8 changed files with 279 additions and 25 deletions

View File

@ -1,4 +1,115 @@
5.3.0
**DO NOT OVERWRITE YOUR CURRENT INSTALL**, (I will have a good laugh if it deletes your "educational" videos).
I've tested this on Windows 10 and LMDE and it records from each site, haven't tested anything else apart from basic post-processing but, in theory, I haven't messed with anything other than what I've noted below, so groups, etc should still work.
**NOTE:** The included ffmpeg is a static compiled version from the Jellyfin project, if it causes problems for you, replace it with the version out of the original v5.3.0 archives.
**DISCLAIMER:** There most likely are bugs, shit happens.
If this version doesn't do what you want, don't use it ... simple.
Changes from reusedname's v5.3.2 version.
25.06.02
========================
* Updated Help tab layout a little, now displays WAN IP if it can get it
connects to `icanhazip.com` so you can check if VPN/Proxy is working
* Added Search input for Settings->Filtering->Ignore list
* Increased -Xmx to 4g/1g for standalone/server respectively
25.05.13
========================
* Added more search options
* Revert 05f83f7c96b280a9b5d74b041421e46ed514364f
* Add Record Until to WebUI
25.05.11
========================
* Fix a bug with Models list display (would sometimes not show anything)
25.05.06
========================
* Move portrait directory from `config/<version>/` to `config` **MOVE YOUR EXISTING DIRECTORY**
**Will no longer be included in the auto-backup of the config**
* Recordings tab is optional
* Replace some deprecated calls
* Replace Logback calls with Slf4j calls (there was a mix)
* Open config directory from Settings->Help
* Version numbering change to vYY.MM.DD
* Update NR Tool URL
25.04.28
========================
* Make side tabs optional for most sites
* Fix WinkTv capture
* Misc deprecated/redundant changes
* Add NR Tool search context menu
* Fix CGF search for CB (new preview URL)
* Change Record Later to Bookmark in various references
25.04.27
========================
* Fix SC login (v3 API from WinkRU)
25.04.03
========================
* Removed:
* News/Logging tabs, see Settings->Help for Logging
* Check for updates
* Non-working sites (ATv, CTv, LJ, MV, & SF)
* Live previews
* Progress column in Recordings tab
* Gaming, Trending, Top-rated side tabs for CB
* Various options in Settings related to above
* Added:
* Button to open the log file under Settings->Help
* Bongacams Mobile tab
* Superchatlive alternative for SC
* Region tab selection for CB, (Restart after selecting)
* Extra Thumbnail sizes
* Extra steps under Split by time/size
* Allow server to access contact sheets
* Pre-input parameters for post-processing remux/transcode
(⚠️ Do not even try it if you have no idea how to use ffmpeg, you can end up with
no recording ... which can happen even if you do know what you're doing)
* Changed:
* Recording tab is now Models (ala WinkRU's version)
* Help and Donate moved to Settings (ala WinkRU's version)
* Config file is saved to a ./config by default (I hate hiding files all over the place)
* Fixed:
* CS audio (reusedname's patch instead of my hack)
* BC room detection (reusedname's improved patch instead of mine)
* F4F thumbnails
5.3.2 - reusedname
========================
* Generalise Flaresolverr for any domain: replace per-site setting (it was just one for CB though) with list of hosts. Previous setting is migrated to new format on first start.
* Improve group handling somewhat. Models in the group are started in priority order when changing priority, pausing/resuming, and after recording restart has failed. The solution is kind of brute force right now and may cause excessive checks in some cases. I also noticed that sometimes the highest priority model is not on the recording.
* Fix Bongacams online check: remove basic one, do the complete check straight away. Also update its logic to check new field.
* Reduce config save spam from recorder operations (400ms delay default, configurable). Helps with Suspend/Resume all actions.
* Add a couple of server relevant settings to the WebUI Settings page by @Jafea7
* Better spec compliance regarding playlist update timing. The delay was hardcoded to 1 second, now it is taken from the playlist (usually 2 seconds). This also Should reduce network pressure and timeouts.
* New settings for max concurrent http requests (total and per host). Increasing them should reduce timeouts with a lot of active recordings. Found in Advanced group.
* Fix zombie recordings in some cases.
* Fix group precondition not respecting priority.
* Add online state column (hidden by default). Fix capitalization.
* Fix Stripchat login
* Set model state offline if online check is skipped.
* Optimize model updates in the WebUI.
5.3.1 - reusedname
========================
* Flaresolverr support for CB.
* Performance fixes/improvements (handle more concurrent recordings).
* Camsoda sound fix (@Jafea7 I ended up editing the site model class, not the AbstractHlsDownload).
* Missing Stripchat thumbnails fix.
* Client: Show grouped model count in status bar.
* Web interface performance fix by crazy hacker man.
* Some hlsdl stability improvements.
* Bandwidth meter support for hlsdl downloads.
5.3.0 - b00bface
========================
* Added menu entry to force recording of models without changing the prio
* Added blacklist and whitelist settings to automatically filter out models

View File

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

View File

@ -6,9 +6,13 @@ import ctbrec.Config;
import ctbrec.io.json.ObjectMapperFactory;
import ctbrec.ui.AutosizeAlert;
import ctbrec.ui.controls.Dialogs;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.geometry.Insets;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
@ -31,7 +35,9 @@ import static javafx.scene.control.ButtonType.YES;
public class IgnoreList extends GridPane {
private ListView<String> ignoreListView;
private TextField searchField;
private ObservableList<String> masterList;
private FilteredList<String> filteredList;
private final ObjectMapper mapper = ObjectMapperFactory.getMapper();
public IgnoreList() {
@ -43,7 +49,12 @@ public class IgnoreList extends GridPane {
setHgap(10);
setVgap(10);
setPadding(new Insets(20, 10, 10, 10));
// Create search field with clear button
HBox searchBox = createSearchField();
add(searchBox, 0, 0);
GridPane.setHgrow(searchBox, Priority.ALWAYS);
// Initialize ListView
ignoreListView = new ListView<>();
ignoreListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
ignoreListView.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
@ -51,9 +62,28 @@ public class IgnoreList extends GridPane {
removeSelectedModels();
}
});
add(ignoreListView, 0, 0);
// Set custom cell factory for highlighting
ignoreListView.setCellFactory(listView -> new ListCell<>() {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setStyle("");
} else {
setText(item);
// Highlight if the item matches the search text
String searchText = searchField.getText().trim().toLowerCase();
if (searchText.isEmpty() || !item.toLowerCase().contains(searchText)) {
setStyle("");
}
}
}
});
add(ignoreListView, 0, 1);
GridPane.setHgrow(ignoreListView, Priority.ALWAYS);
// Buttons
var remove = new Button("Remove");
remove.setOnAction(evt -> removeSelectedModels());
var exportIgnoreList = new Button("Export");
@ -65,12 +95,52 @@ public class IgnoreList extends GridPane {
buttons.setStyle("-fx-background-color: -fx-background"); // workaround so that the buttons don't shrink
}
private HBox createSearchField() {
HBox searchBox = new HBox(5);
searchField = new TextField();
searchField.setPromptText("Search ignore list...");
HBox.setHgrow(searchField, Priority.ALWAYS);
// Create clear button
Button clearButton = new Button("");
clearButton.setStyle("-fx-font-size: 12px; -fx-padding: 2px 5px; -fx-background-radius: 50%;");
clearButton.setVisible(false); // Initially hidden
clearButton.setOnAction(event -> {
searchField.clear();
searchField.requestFocus(); // Return focus to the TextField
});
// Bind clear button visibility to text presence
clearButton.visibleProperty().bind(
Bindings.createBooleanBinding(
() -> !searchField.getText().trim().isEmpty(),
searchField.textProperty()
)
);
// Bind the filter to the search field text property
searchField.textProperty().addListener((obs, oldValue, newValue) -> {
filteredList.setPredicate(item -> {
if (newValue == null || newValue.trim().isEmpty()) {
return true; // Show all items if search is empty
}
return item.toLowerCase().contains(newValue.trim().toLowerCase());
});
// Force ListView to refresh to update highlighting
ignoreListView.refresh();
});
searchBox.getChildren().addAll(searchField, clearButton);
searchBox.setAlignment(javafx.geometry.Pos.CENTER_LEFT);
return searchBox;
}
private void removeSelectedModels() {
List<String> selectedModels = ignoreListView.getSelectionModel().getSelectedItems();
if (!selectedModels.isEmpty()) {
Config.getInstance().getSettings().ignoredModels.removeAll(selectedModels);
ignoreListView.getItems().removeAll(selectedModels);
log.debug(Config.getInstance().getSettings().ignoredModels.toString());
masterList.removeAll(selectedModels); // Update master list
try {
Config.getInstance().save();
} catch (IOException e) {
@ -81,9 +151,10 @@ public class IgnoreList extends GridPane {
private void loadIgnoredModels() {
List<String> ignored = Config.getInstance().getSettings().ignoredModels;
ignoreListView.getItems().clear();
ignoreListView.getItems().addAll(ignored);
Collections.sort(ignoreListView.getItems());
masterList = FXCollections.observableArrayList(ignored);
Collections.sort(masterList);
filteredList = new FilteredList<>(masterList, p -> true);
ignoreListView.setItems(filteredList);
}
public void refresh() {

View File

@ -3,12 +3,13 @@ package ctbrec.ui.tabs;
import ctbrec.Config;
import ctbrec.docs.DocServer;
import ctbrec.ui.DesktopIntegration;
import ctbrec.WANIPFetcher;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Tab;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import lombok.extern.slf4j.Slf4j;
@ -22,33 +23,65 @@ public class HelpTab extends Tab {
private boolean serverStarted = false;
private String getWanIp() {
try {
return WANIPFetcher.getWANIP();
} catch (Exception e) {
return "Unavailable";
}
}
public HelpTab() {
setClosable(false);
setText("Help");
Label wanStaticLabel = new Label("WAN IP:");
wanStaticLabel.setStyle("-fx-font-family: 'Arial'; -fx-font-size: 14px; -fx-font-weight: bold; -fx-alignment: center;");
Label wanValueLabel = new Label(getWanIp());
wanValueLabel.setStyle("-fx-font-family: 'Arial'; -fx-font-size: 14px; -fx-underline: true; -fx-alignment: center;");
VBox wanLabelBox = new VBox(2);
wanLabelBox.setAlignment(Pos.CENTER);
wanLabelBox.getChildren().addAll(wanStaticLabel, wanValueLabel);
Button openHelp = new Button("Open Help");
openHelp.setStyle("-fx-font-family: 'Arial'; -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: #ffffff; -fx-background-color: #4CAF50; -fx-padding: 15 30; -fx-border-radius: 5; -fx-background-radius: 5;");
Button openLog = new Button("Open Log File");
Button openCfg = new Button("Open config dir");
openHelp.setPadding(new Insets(20));
openLog.setPadding(new Insets(20));
openCfg.setPadding(new Insets(20));
Label logFilePathLabel = new Label("Log file: " + new File("ctbrec.log").getAbsolutePath());
openLog.setStyle("-fx-font-family: 'Arial'; -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: #ffffff; -fx-background-color: #2196F3; -fx-padding: 15 30; -fx-border-radius: 5; -fx-background-radius: 5;");
Button openCfg = new Button("Open Config Dir");
openCfg.setStyle("-fx-font-family: 'Arial'; -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: #ffffff; -fx-background-color: #FF9800; -fx-padding: 15 30; -fx-border-radius: 5; -fx-background-radius: 5;");
File cfgDir = Config.getInstance().getConfigDir();
String path = (cfgDir != null) ? cfgDir.getAbsolutePath() : "Unknown";
Label configPathLabel = new Label("Config dir: " + path);
Label configStaticLabel = new Label("Config Dir:");
configStaticLabel.setStyle("-fx-font-family: 'Arial'; -fx-font-size: 14px; -fx-font-weight: bold; -fx-alignment: center;");
Label configValueLabel = new Label(path);
configValueLabel.setStyle("-fx-font-family: 'Arial'; -fx-font-size: 14px; -fx-underline: true; -fx-alignment: center;");
VBox configLabelBox = new VBox(2);
configLabelBox.setAlignment(Pos.CENTER);
configLabelBox.getChildren().addAll(configStaticLabel, configValueLabel);
Label logStaticLabel = new Label("Log File:");
logStaticLabel.setStyle("-fx-font-family: 'Arial'; -fx-font-size: 14px; -fx-font-weight: bold; -fx-alignment: center;");
Label logValueLabel = new Label(new File("ctbrec.log").getAbsolutePath());
logValueLabel.setStyle("-fx-font-family: 'Arial'; -fx-font-size: 14px; -fx-underline: true; -fx-alignment: center;");
VBox logLabelBox = new VBox(2);
logLabelBox.setAlignment(Pos.CENTER);
logLabelBox.getChildren().addAll(logStaticLabel, logValueLabel);
// Create VBox for all components
VBox vbox = new VBox();
vbox.setAlignment(Pos.CENTER); // Center align the buttons
vbox.setSpacing(20); // Set a 20 pixel gap between the buttons
vbox.getChildren().addAll(openHelp, openCfg, configPathLabel, openLog, logFilePathLabel);
vbox.setAlignment(Pos.CENTER);
vbox.setSpacing(20);
vbox.getChildren().addAll(wanLabelBox, openHelp, configLabelBox, openCfg, logLabelBox, openLog);
BorderPane layout = new BorderPane();
// Add the VBox to the center region of the BorderPane
layout.setCenter(vbox);
setContent(layout);
// Button actions remain unchanged
openHelp.setOnAction(e -> {
if (!serverStarted) {
startDocumentationServer();

View File

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

View File

@ -0,0 +1,39 @@
package ctbrec;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
public class WANIPFetcher {
public static void main(String[] args) {
try {
String ip = getWANIP();
System.out.println("Your WAN IP address is: " + ip);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getWANIP() throws Exception {
URI uri = new URI("https://icanhazip.com");
URL url = uri.toURL(); // Convert URI to URL
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
StringBuilder content = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
connection.disconnect();
return content.toString().trim();
}
}

View File

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

View File

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