From f2df8deb0c21a809eed783b643d472c704d012a7 Mon Sep 17 00:00:00 2001 From: 0xb00bface <0xboobface@gmail.com> Date: Sat, 4 Mar 2023 15:14:59 +0100 Subject: [PATCH] Add playground dialog for post-processing variables/functions and update the documentation --- .../java/ctbrec/ui/settings/SettingsTab.java | 9 ++- .../VariablePlayGroundDialogFactory.java | 76 +++++++++++++++++++ .../resources/html/docs/PostProcessing.md | 49 +++++++----- common/src/main/java/ctbrec/Settings.java | 2 +- ...AbstractPlaceholderAwarePostProcessor.java | 19 ++--- .../ctbrec/recorder/postprocessing/Move.java | 2 +- .../recorder/postprocessing/Rename.java | 4 +- .../ModelVariableExpander.java | 17 +---- .../functions/Capitalize.java | 5 +- .../variableexpansion/functions/Format.java | 33 ++++---- .../variableexpansion/functions/Lower.java | 5 +- .../variableexpansion/functions/OrElse.java | 10 ++- .../variableexpansion/functions/Sanitize.java | 5 +- .../variableexpansion/functions/Trim.java | 5 +- .../variableexpansion/functions/Upper.java | 5 +- ...ractPlaceholderAwarePostProcessorTest.java | 6 +- .../postprocessing/RenameDirectoryTest.java | 17 ++--- .../functions/CapitalizeTest.java | 31 ++++++++ .../functions/FormatTest.java | 55 ++++++++++++++ .../functions/LowerTest.java | 31 ++++++++ .../functions/OrElseTest.java | 29 +++++++ .../functions/SanitizeTest.java | 35 +++++++++ .../variableexpansion/functions/TrimTest.java | 33 ++++++++ .../functions/UpperTest.java | 31 ++++++++ 24 files changed, 432 insertions(+), 82 deletions(-) create mode 100644 client/src/main/java/ctbrec/ui/settings/VariablePlayGroundDialogFactory.java create mode 100644 common/src/test/java/ctbrec/variableexpansion/functions/CapitalizeTest.java create mode 100644 common/src/test/java/ctbrec/variableexpansion/functions/FormatTest.java create mode 100644 common/src/test/java/ctbrec/variableexpansion/functions/LowerTest.java create mode 100644 common/src/test/java/ctbrec/variableexpansion/functions/OrElseTest.java create mode 100644 common/src/test/java/ctbrec/variableexpansion/functions/SanitizeTest.java create mode 100644 common/src/test/java/ctbrec/variableexpansion/functions/TrimTest.java create mode 100644 common/src/test/java/ctbrec/variableexpansion/functions/UpperTest.java diff --git a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java index 1ac57835..39ca8e2d 100644 --- a/client/src/main/java/ctbrec/ui/settings/SettingsTab.java +++ b/client/src/main/java/ctbrec/ui/settings/SettingsTab.java @@ -278,7 +278,8 @@ public class SettingsTab extends Tab implements TabSelectionListener { Group.of("Post-Processing", Setting.of("Threads", postProcessingThreads), Setting.of("Steps", postProcessingStepPanel), - Setting.of("", createHelpButton("Post-Processing Help", "http://localhost:5689/docs/PostProcessing.md")))), + Setting.of("", createHelpButton("Post-Processing Help", "http://localhost:5689/docs/PostProcessing.md")), + Setting.of("", createVariablePlayGroundButton()))), Category.of("Events & Actions", new ActionSettingsPanel(recorder)), Category.of("Ignore List", ignoreList), Category.of("Sites", siteCategories.toArray(new Category[0])), Category.of("Proxy", @@ -377,6 +378,12 @@ public class SettingsTab extends Tab implements TabSelectionListener { return postProcessingHelpButton; } + private Button createVariablePlayGroundButton() { + var postProcessingHelpButton = new Button("Variable Playground"); + postProcessingHelpButton.setOnAction(e -> new VariablePlayGroundDialogFactory().openDialog(this.getTabPane().getScene(), config, recorder)); + return postProcessingHelpButton; + } + private void bindEnabledProperty(Setting s, BooleanExpression bindTo) { try { s.getGui().disableProperty().bind(bindTo); diff --git a/client/src/main/java/ctbrec/ui/settings/VariablePlayGroundDialogFactory.java b/client/src/main/java/ctbrec/ui/settings/VariablePlayGroundDialogFactory.java new file mode 100644 index 00000000..25a00b49 --- /dev/null +++ b/client/src/main/java/ctbrec/ui/settings/VariablePlayGroundDialogFactory.java @@ -0,0 +1,76 @@ +package ctbrec.ui.settings; + +import ctbrec.Config; +import ctbrec.Recording; +import ctbrec.StringUtil; +import ctbrec.UnknownModel; +import ctbrec.recorder.Recorder; +import ctbrec.recorder.postprocessing.PostProcessingContext; +import ctbrec.sites.chaturbate.Chaturbate; +import ctbrec.ui.controls.Dialogs; +import ctbrec.ui.tabs.DownloadPostprocessor; +import ctbrec.variableexpansion.functions.AntlrSyntacErrorAdapter; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; + +import java.nio.file.Paths; +import java.time.Instant; + +public class VariablePlayGroundDialogFactory { + public void openDialog(Scene parent, Config config, Recorder recorder) { + Chaturbate chaturbate = new Chaturbate(); + UnknownModel unknownModel = new UnknownModel(); + unknownModel.setName("Pussy_Galore"); + unknownModel.setDisplayName("Pussy Galore"); + unknownModel.setSite(chaturbate); + Recording recording = new Recording(); + recording.setAbsoluteFile(Paths.get("ctbrec", "recs", "pussy_galore", "2023-02-26_14-05-56").toFile()); + recording.setStartDate(Instant.now()); + recording.setStatus(Recording.State.POST_PROCESSING); + recording.setNote("notes about the recording"); + recording.setModel(unknownModel); + PostProcessingContext ctx = new PostProcessingContext(); + ctx.setConfig(config); + ctx.setRecorder(recorder); + ctx.setRecording(recording); + DownloadPostprocessor postprocessor = new DownloadPostprocessor(); + + GridPane pane = new GridPane(); + Label result = new Label(); + Label error = new Label(); + + pane.add(new Label("Expression"), 0, 0); + TextField input = new TextField(); + input.setMinWidth(600); + pane.add(input, 1, 0); + GridPane.setHgrow(input, Priority.ALWAYS); + input.setOnKeyTyped(evt -> { + String r = postprocessor.fillInPlaceHolders(input.getText(), ctx, new AntlrSyntacErrorAdapter() { + @Override + public void syntaxError(Recognizer recognizer, Object o, int line, int pos, String s, RecognitionException e) { + error.setText(String.format("Syntax error at %d:%d %s", line, pos, s)); + } + }); + result.setText(r); + if (StringUtil.isNotBlank(r)) { + error.setText(""); + } + }); + + pane.add(error, 0, 1); + GridPane.setColumnSpan(error, 2); + + pane.add(result, 0, 2); + GridPane.setColumnSpan(result, 2); + + pane.setHgap(5); + pane.vgapProperty().bind(pane.hgapProperty()); + Dialogs.showCustomInput(parent, "Playground", pane, (obs, oldV, newV) -> { + }); + } +} diff --git a/client/src/main/resources/html/docs/PostProcessing.md b/client/src/main/resources/html/docs/PostProcessing.md index 9cc13eeb..c84f8069 100644 --- a/client/src/main/resources/html/docs/PostProcessing.md +++ b/client/src/main/resources/html/docs/PostProcessing.md @@ -1,8 +1,10 @@ #### Post-Processing + The post-processing gives you the possibility to execute different actions after a recording has finished. You can use that to convert the files to another format, create preview images, rename / move the file etc. ##### Available Steps + - **create a copy** - Creates a copy of the original recording. All following post-processing steps are executed on the copy, not on the original recording. This means, that the post-processing can be rerun in case a step failed, because the original recording is still available. @@ -21,17 +23,20 @@ the files to another format, create preview images, rename / move the file etc. - **create contactsheet** - create a contact sheet with preview images of the recording #### Planned for future releases + - **call a webhook** - call a URL once a recording is finished - **create timeline thumbnails** - create a small thumbnail for every second or every few seconds, which can be used to very fast scan through a recording #### How to configure the server to do post-processing + There is currently no user interface to configure the post-processing for the server. It has to be added manually to the server config. I suggest to start the app and configure the post-processing steps in the settings. Afterwards you close the app and copy the post-processing section from the settings.json to your server.json file. To find out, where these files are on your system, read [Configuration File](ConfigurationFile.md). The part you have to copy is + ``` postProcessors: [ ... @@ -43,16 +48,13 @@ The part you have to copy is ###### Available variables: + - **${modelName}** - the name of the recorded model -- **${modelDisplayName}** - the name of the recorded model, which is shown on the webpage. Might be the same as +- **${modelDisplayName}** - the name of the recorded model, which is shown on the webpage. Might be the same as ${modelName} -- **${modelSanitizedName}** - sanitized name of the model. The following characters are replaced by an underscore: - \\, /, ', " and space - **${modelGroupName}** - name of the model group, if the model is part of a group - **${modelGroupId}** - the unique ID of the model group, if the model is part of a group - **${siteName}** - the name of the cam site, the model streams on -- **${siteSanitizedName}** - sanitized name of the site. The following characters are replaced by an underscore: - \\, /, ', " and space - **${fileSuffix}** - the file extension of the recording. E.g. ts or mp4. In case of a standard server recording, this will be empty - **${epochSecond}** - timestamp of the recording in seconds since 1970-01-01 (unixtime) @@ -60,16 +62,31 @@ The part you have to copy is \\, /, ', " and space - **${recordingNotes}** - sanitized recording notes. The following characters are replaced by an underscore: \\, /, ', " and space. Useful for the download of recordings from the server. -- **${recordingsDir}** - the base directory of all recordings. Same as Recordings Directory in the Recorder settings +- **${recordingsDir}** - the base directory of all recordings. Same as Recordings Directory in the Recorder settings section. - **${absolutePath}** - the absolute path in the filesystem to the recording file (or the recording directory in case of a server recording) -- **${absoluteParentPath}** - the absolute path to the parent directory of the recording in the filesystem (or the +- **${absoluteParentPath}** - the absolute path to the parent directory of the recording in the filesystem (or the recording dir in case of a server recording) -- **${utcDateTime}** and **${localDateTime}** - the timestamp of the recording in the UTC or your local timezone. If no - pattern is given, the default ```yyyy-MM-dd_HH-mm-ss``` is used. You can also define your own pattern using the following - symbols. For example ```${localDateTime(yyyyMMdd-HHmmss)}``` would lead to 20200928-173605. +- **${utcDateTime}** and **${localDateTime}** - the timestamp of the recording in the UTC or your local timezone + +#### Functions + +- **$trim** - removes all leading and trailing space - `$trim( hello world )` becomes `hello world` +- **$upper** - converts all of the characters to upper case - `$upper(hello world)` becomes `HELLO WORLD` +- **$lower** - converts all of the characters to lower case - `$lower(hElLo WORLD)` becomes `hello world` +- **$capitalize** - capitalizes words changing the first character to upper case - `$capitalize(hElLo WorLD)` becomes `HElLo WorLD` +- **$sanitize** - removes problematic characters - `$sanitize(hEl'Lo / WO"RLD)` becomes `hEl_Lo___WO_RLD`. The following characters are replaced by an + underscore: + \\, /, ', " and space +- **$orElse** - provide an alternative in case a variable is not set - `$orElse(${variable},someValue)` - becomes `${variable}`, if it is set or `someValue`, if + `${variable}` is not set +- **$format** - formats a date, can be used with a pattern or without. + Examples: + - `$format(${localDateTime})` - becomes something like `2023-02-26_12-23-15` + - `$format(${localDateTime},yyyyMMdd-HHmmss)` would lead to `20200928-173605` + @@ -118,16 +135,10 @@ The part you have to copy is
Symbol Meaning Presentation Examples
- - For more information see: [DateTimeFormatter](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html) -###### Fallback values: -You can define a fallback value for each variable in case there is no value available for the variable. The syntax is - ${placeholder?foobar} -Let's for example say you have created some model groups. For models, which are part of a group, you want to use the group name. But for models, which -are not part of a group you want to use the sanitized name. You can achieve that by using the following expression: +For more information see: [DateTimeFormatter](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html) - ${modelGroupName?${modelSanitizedName}} +#### Full Example -It can be read like "use the modelGroupName, but if that is not available use modelSanitizedName". \ No newline at end of file +`$orElse(${modelGroupName},$sanitize(${modelName}))_$format(${localDateTime})_${recordingNotes}` diff --git a/common/src/main/java/ctbrec/Settings.java b/common/src/main/java/ctbrec/Settings.java index 6db0dbb0..28083645 100644 --- a/common/src/main/java/ctbrec/Settings.java +++ b/common/src/main/java/ctbrec/Settings.java @@ -66,7 +66,7 @@ public class Settings { public int defaultPriority = 50; public boolean determineResolution = false; public List disabledSites = new ArrayList<>(); - public String downloadFilename = "${modelSanitizedName}-$format(${localDateTime})"; + public String downloadFilename = "$sanitize(${modelName})_$format(${localDateTime})"; public List eventHandlers = new ArrayList<>(); public boolean eventsSuspended = false; public boolean fastScrollSpeed = true; diff --git a/common/src/main/java/ctbrec/recorder/postprocessing/AbstractPlaceholderAwarePostProcessor.java b/common/src/main/java/ctbrec/recorder/postprocessing/AbstractPlaceholderAwarePostProcessor.java index 8ab1d590..b96425ba 100644 --- a/common/src/main/java/ctbrec/recorder/postprocessing/AbstractPlaceholderAwarePostProcessor.java +++ b/common/src/main/java/ctbrec/recorder/postprocessing/AbstractPlaceholderAwarePostProcessor.java @@ -10,7 +10,9 @@ import ctbrec.variableexpansion.antlr.PostProcessingLexer; import ctbrec.variableexpansion.antlr.PostProcessingParser; import ctbrec.variableexpansion.functions.AntlrSyntacErrorAdapter; import lombok.extern.slf4j.Slf4j; -import org.antlr.v4.runtime.*; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTree; import java.io.IOException; @@ -28,6 +30,10 @@ import static java.util.Optional.ofNullable; public abstract class AbstractPlaceholderAwarePostProcessor extends AbstractPostProcessor { public String fillInPlaceHolders(String input, PostProcessingContext ctx) { + return this.fillInPlaceHolders(input, ctx, null); + } + + public String fillInPlaceHolders(String input, PostProcessingContext ctx, AntlrSyntacErrorAdapter errorListener) { Recording rec = ctx.getRecording(); Config config = ctx.getConfig(); @@ -42,21 +48,16 @@ public abstract class AbstractPlaceholderAwarePostProcessor extends AbstractPost variables.put("utcDateTime", getUtcDateTime(rec)); variables.put("localDateTime", getLocalDateTime(rec)); - return fillInPlaceHolders(input, variables); + return fillInPlaceHolders(input, variables, errorListener); } - private String fillInPlaceHolders(String input, Map> variables) { + private String fillInPlaceHolders(String input, Map> variables, AntlrSyntacErrorAdapter errorListener) { try (StringReader reader = new StringReader(input)) { CharStream s = CharStreams.fromReader(reader); PostProcessingLexer lexer = new PostProcessingLexer(s); CommonTokenStream tokens = new CommonTokenStream(lexer); PostProcessingParser parser = new PostProcessingParser(tokens); - parser.addErrorListener(new AntlrSyntacErrorAdapter() { - @Override - public void syntaxError(Recognizer recognizer, Object o, int line, int pos, String s, RecognitionException e) { - log.warn("Syntax error at {}:{} {}", line, pos, s); - } - }); + Optional.ofNullable(errorListener).ifPresent(parser::addErrorListener); ParseTree parseTree = parser.line(); ParserVisitor visitor = new ParserVisitor(variables); return visitor.visit(parseTree); diff --git a/common/src/main/java/ctbrec/recorder/postprocessing/Move.java b/common/src/main/java/ctbrec/recorder/postprocessing/Move.java index c8f6b6c4..78470449 100644 --- a/common/src/main/java/ctbrec/recorder/postprocessing/Move.java +++ b/common/src/main/java/ctbrec/recorder/postprocessing/Move.java @@ -16,7 +16,7 @@ public class Move extends AbstractPlaceholderAwarePostProcessor { private static final Logger LOG = LoggerFactory.getLogger(Move.class); public static final String PATH_TEMPLATE = "path.template"; - public static final String DEFAULT = "${modelSanitizedName}" + File.separatorChar + "$format(${localDateTime})"; + public static final String DEFAULT = "$sanitize(${modelName})" + File.separatorChar + "$format(${localDateTime})"; @Override public String getName() { diff --git a/common/src/main/java/ctbrec/recorder/postprocessing/Rename.java b/common/src/main/java/ctbrec/recorder/postprocessing/Rename.java index a9a2bd82..879977ee 100644 --- a/common/src/main/java/ctbrec/recorder/postprocessing/Rename.java +++ b/common/src/main/java/ctbrec/recorder/postprocessing/Rename.java @@ -13,8 +13,8 @@ public class Rename extends AbstractPlaceholderAwarePostProcessor { private static final Logger LOG = LoggerFactory.getLogger(Rename.class); public static final String FILE_NAME_TEMPLATE = "filename.template"; - public static final String DEFAULT = "${modelSanitizedName}_$format(${localDateTime}).${fileSuffix}"; - public static final String DEFAULT_DIR = "${modelSanitizedName}_$format(${localDateTime})"; + public static final String DEFAULT = "$sanitize(${modelName})_$format(${localDateTime}).${fileSuffix}"; + public static final String DEFAULT_DIR = "$sanitize(${modelName})_$format(${localDateTime})"; @Override public String getName() { diff --git a/common/src/main/java/ctbrec/variableexpansion/ModelVariableExpander.java b/common/src/main/java/ctbrec/variableexpansion/ModelVariableExpander.java index 1db6e774..323d059b 100644 --- a/common/src/main/java/ctbrec/variableexpansion/ModelVariableExpander.java +++ b/common/src/main/java/ctbrec/variableexpansion/ModelVariableExpander.java @@ -18,10 +18,8 @@ public class ModelVariableExpander extends AbstractVariableExpander { Optional modelGroup = Optional.ofNullable(recorder).flatMap(r -> r.getModelGroup(model)); placeholderValueSuppliers.put("modelName", ofNullable(model.getName())); placeholderValueSuppliers.put("modelDisplayName", ofNullable(model.getDisplayName())); - placeholderValueSuppliers.put("modelSanitizedName",getSanitizedName(model)); - placeholderValueSuppliers.put("modelNotes",getSanitizedModelNotes(config, model)); + placeholderValueSuppliers.put("modelNotes", getSanitizedModelNotes(config, model)); placeholderValueSuppliers.put("siteName", ofNullable(model).map(Model::getSite).map(Site::getName)); - placeholderValueSuppliers.put("siteSanitizedName",getSanitizedSiteName(model)); placeholderValueSuppliers.put("modelGroupName", modelGroup.map(ModelGroup::getName)); placeholderValueSuppliers.put("modelGroupId", modelGroup.map(ModelGroup::getId).map(UUID::toString)); } @@ -30,19 +28,6 @@ public class ModelVariableExpander extends AbstractVariableExpander { return fillInPlaceHolders(input, placeholderValueSuppliers); } - private Optional getSanitizedName(Model model) { - String name = model.getSanitizedNamed(); - if (StringUtil.isBlank(name)) { - return Optional.empty(); - } else { - return Optional.of(name); - } - } - - private Optional getSanitizedSiteName(Model model) { - return ofNullable(model).map(Model::getSite).map(Site::getName).map(StringUtil::sanitize); - } - private Optional getSanitizedModelNotes(Config config, Model m) { return ofNullable(config.getModelNotes(m)).map(StringUtil::sanitize); } diff --git a/common/src/main/java/ctbrec/variableexpansion/functions/Capitalize.java b/common/src/main/java/ctbrec/variableexpansion/functions/Capitalize.java index 3b28f36b..6a807a1f 100644 --- a/common/src/main/java/ctbrec/variableexpansion/functions/Capitalize.java +++ b/common/src/main/java/ctbrec/variableexpansion/functions/Capitalize.java @@ -7,7 +7,10 @@ import java.util.Optional; public class Capitalize implements VarArgsFunction { @Override - public String apply(Object[] params) { + public String apply(Object... params) { + if (params == null || params.length == 0) { + return ""; + } return Optional.ofNullable(params).map(p -> p[0]).map(String.class::cast).map(StringUtil::capitalize).orElse(""); } } diff --git a/common/src/main/java/ctbrec/variableexpansion/functions/Format.java b/common/src/main/java/ctbrec/variableexpansion/functions/Format.java index 20839d23..810dbc3f 100644 --- a/common/src/main/java/ctbrec/variableexpansion/functions/Format.java +++ b/common/src/main/java/ctbrec/variableexpansion/functions/Format.java @@ -9,23 +9,30 @@ import java.util.Optional; public class Format implements VarArgsFunction { @Override - public String apply(Object[] params) { + public String apply(Object... params) { return Optional.ofNullable(params).map(p -> { - String format = "yyyy-MM-dd_HH-mm-ss"; - if (p.length > 1) { - format = (String) p[1]; - } - ZonedDateTime zdt; - if (p[0] instanceof String date) { - try { - zdt = ZonedDateTime.parse(date); - } catch (DateTimeParseException e) { + try { + if (p.length == 0) { return ""; } - } else { - zdt = (ZonedDateTime) p[0]; + String format = "yyyy-MM-dd_HH-mm-ss"; + if (p.length > 1) { + format = (String) p[1]; + } + ZonedDateTime zdt; + if (p != null && p.length > 0 && p[0] instanceof String date) { + try { + zdt = ZonedDateTime.parse(date); + } catch (DateTimeParseException e) { + return ""; + } + } else { + zdt = (ZonedDateTime) p[0]; + } + return DateTimeFormatter.ofPattern(format).format(zdt); + } catch (Exception e) { + return ""; } - return DateTimeFormatter.ofPattern(format).format(zdt); }) .orElse(""); } diff --git a/common/src/main/java/ctbrec/variableexpansion/functions/Lower.java b/common/src/main/java/ctbrec/variableexpansion/functions/Lower.java index 86b2d6dc..adc2d247 100644 --- a/common/src/main/java/ctbrec/variableexpansion/functions/Lower.java +++ b/common/src/main/java/ctbrec/variableexpansion/functions/Lower.java @@ -6,7 +6,10 @@ import java.util.Optional; public class Lower implements VarArgsFunction { @Override - public String apply(Object[] params) { + public String apply(Object... params) { + if (params == null || params.length == 0) { + return ""; + } return Optional.ofNullable(params).map(sa -> sa[0]).map(String.class::cast).map(String::toLowerCase).orElse(""); } } diff --git a/common/src/main/java/ctbrec/variableexpansion/functions/OrElse.java b/common/src/main/java/ctbrec/variableexpansion/functions/OrElse.java index 5fa0e4b5..eb76cd20 100644 --- a/common/src/main/java/ctbrec/variableexpansion/functions/OrElse.java +++ b/common/src/main/java/ctbrec/variableexpansion/functions/OrElse.java @@ -8,15 +8,19 @@ import java.util.Optional; public class OrElse implements VarArgsFunction { @Override public String apply(Object... params) { - if (params.length < 2) { - throw new IllegalArgumentException("OrElse needs two parameters"); + if (params == null || params.length < 1) { + return ""; + } + String fallback = ""; + if (params.length >= 2) { + fallback = String.valueOf(params[1]); } Optional result = Optional.ofNullable(params[0]).map(String::valueOf); if (result.isPresent() && StringUtil.isNotBlank(result.get())) { return result.get(); } else { - return String.valueOf(params[1]); + return fallback; } } } diff --git a/common/src/main/java/ctbrec/variableexpansion/functions/Sanitize.java b/common/src/main/java/ctbrec/variableexpansion/functions/Sanitize.java index 8a700e79..b4348c24 100644 --- a/common/src/main/java/ctbrec/variableexpansion/functions/Sanitize.java +++ b/common/src/main/java/ctbrec/variableexpansion/functions/Sanitize.java @@ -7,7 +7,10 @@ import java.util.Optional; public class Sanitize implements VarArgsFunction { @Override - public String apply(Object[] params) { + public String apply(Object... params) { + if (params == null || params.length == 0) { + return ""; + } return Optional.ofNullable(params).map(p -> p[0]).map(String.class::cast).map(StringUtil::sanitize).orElse(""); } } diff --git a/common/src/main/java/ctbrec/variableexpansion/functions/Trim.java b/common/src/main/java/ctbrec/variableexpansion/functions/Trim.java index cd67d8d3..26d4bcd9 100644 --- a/common/src/main/java/ctbrec/variableexpansion/functions/Trim.java +++ b/common/src/main/java/ctbrec/variableexpansion/functions/Trim.java @@ -6,7 +6,10 @@ import java.util.Optional; public class Trim implements VarArgsFunction { @Override - public String apply(Object[] params) { + public String apply(Object... params) { + if (params == null || params.length == 0) { + return ""; + } return Optional.ofNullable(params).map(sa -> sa[0]).map(String.class::cast).map(String::trim).orElse(""); } } diff --git a/common/src/main/java/ctbrec/variableexpansion/functions/Upper.java b/common/src/main/java/ctbrec/variableexpansion/functions/Upper.java index 2d19a817..4c492e5c 100644 --- a/common/src/main/java/ctbrec/variableexpansion/functions/Upper.java +++ b/common/src/main/java/ctbrec/variableexpansion/functions/Upper.java @@ -6,7 +6,10 @@ import java.util.Optional; public class Upper implements VarArgsFunction { @Override - public String apply(Object[] params) { + public String apply(Object... params) { + if (params == null || params.length == 0) { + return ""; + } return Optional.ofNullable(params).map(sa -> sa[0]).map(String.class::cast).map(String::toUpperCase).orElse(""); } } diff --git a/common/src/test/java/ctbrec/recorder/postprocessing/AbstractPlaceholderAwarePostProcessorTest.java b/common/src/test/java/ctbrec/recorder/postprocessing/AbstractPlaceholderAwarePostProcessorTest.java index 46c9a153..251f82e9 100644 --- a/common/src/test/java/ctbrec/recorder/postprocessing/AbstractPlaceholderAwarePostProcessorTest.java +++ b/common/src/test/java/ctbrec/recorder/postprocessing/AbstractPlaceholderAwarePostProcessorTest.java @@ -42,7 +42,7 @@ class AbstractPlaceholderAwarePostProcessorTest extends AbstractPpTest { assertEquals("asdf_Mockita Boobilicious_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config))); input = "asdf_${modelDisplayName}_asdf"; assertEquals("asdf_Mockita Boobilicious_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config))); - input = "asdf_${modelSanitizedName}_asdf"; + input = "asdf_$sanitize(${modelName})_asdf"; assertEquals("asdf_Mockita_Boobilicious_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config))); } @@ -50,7 +50,7 @@ class AbstractPlaceholderAwarePostProcessorTest extends AbstractPpTest { void testSiteNameReplacement() { String input = "asdf_${siteName}_asdf"; assertEquals("asdf_Chaturbate_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config))); - input = "asdf_${siteSanitizedName}_asdf"; + input = "asdf_$sanitize(${siteName})_asdf"; assertEquals("asdf_Chaturbate_asdf", placeHolderAwarePp.fillInPlaceHolders(input, createPostProcessingContext(rec, null, config))); } @@ -137,7 +137,7 @@ class AbstractPlaceholderAwarePostProcessorTest extends AbstractPpTest { @Test void testPlaceholderDefaultValues() { - String input = "asdf_$orElse(${modelGroupName},$orElse(${modelSanitizedName},anonymous))_asdf"; + String input = "asdf_$orElse(${modelGroupName},$orElse($sanitize(${modelName}),anonymous))_asdf"; PostProcessingContext ctx = createPostProcessingContext(rec, null, config); ctx.getRecording().getModel().setName(null); assertEquals("asdf_anonymous_asdf", placeHolderAwarePp.fillInPlaceHolders(input, ctx)); diff --git a/common/src/test/java/ctbrec/recorder/postprocessing/RenameDirectoryTest.java b/common/src/test/java/ctbrec/recorder/postprocessing/RenameDirectoryTest.java index 18f2e760..2aa69eb5 100644 --- a/common/src/test/java/ctbrec/recorder/postprocessing/RenameDirectoryTest.java +++ b/common/src/test/java/ctbrec/recorder/postprocessing/RenameDirectoryTest.java @@ -1,19 +1,18 @@ package ctbrec.recorder.postprocessing; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; +import ctbrec.Config; +import ctbrec.Model; +import ctbrec.Recording; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.file.Files; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.junit.jupiter.api.Test; - -import ctbrec.Config; -import ctbrec.Model; -import ctbrec.Recording; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; class RenameDirectoryTest extends AbstractPpTest { @@ -29,7 +28,7 @@ class RenameDirectoryTest extends AbstractPpTest { pp.postprocess(createPostProcessingContext(rec, recordingManager, config)); Matcher m = Pattern.compile("Mockita_Boobilicious_\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}-\\d{2}").matcher(rec.getAbsoluteFile().getName()); - assertTrue(m.matches()); + assertTrue(m.matches(), () -> rec.getAbsoluteFile().getName() + " does not match"); assertEquals(rec.getAbsoluteFile(), rec.getPostProcessedFile()); assertNotEquals(rec.getAbsoluteFile(), original); } diff --git a/common/src/test/java/ctbrec/variableexpansion/functions/CapitalizeTest.java b/common/src/test/java/ctbrec/variableexpansion/functions/CapitalizeTest.java new file mode 100644 index 00000000..1902d805 --- /dev/null +++ b/common/src/test/java/ctbrec/variableexpansion/functions/CapitalizeTest.java @@ -0,0 +1,31 @@ +package ctbrec.variableexpansion.functions; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class CapitalizeTest { + Capitalize capitalize = new Capitalize(); + + @Test + void testNullParams() { + assertEquals("", capitalize.apply((Object[]) null)); + } + + @Test + void testEmptyParams() { + assertEquals("", capitalize.apply()); + } + + @Test + void testNormalStrings() { + assertEquals("Hello World", capitalize.apply("hello world")); + assertEquals("HElLo WoRlD", capitalize.apply("hElLo woRlD")); + assertEquals("HELLO WORLD", capitalize.apply("HELLO WORLD")); + } + + @Test + void testSpecialCharacters() { + assertEquals("He{LLo Wo\n$rlD", capitalize.apply("he{LLo wo\n$rlD")); + } +} diff --git a/common/src/test/java/ctbrec/variableexpansion/functions/FormatTest.java b/common/src/test/java/ctbrec/variableexpansion/functions/FormatTest.java new file mode 100644 index 00000000..656bc9d4 --- /dev/null +++ b/common/src/test/java/ctbrec/variableexpansion/functions/FormatTest.java @@ -0,0 +1,55 @@ +package ctbrec.variableexpansion.functions; + +import org.junit.jupiter.api.Test; + +import java.time.ZonedDateTime; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class FormatTest { + + Format format = new Format(); + + @Test + void testNullParams() { + assertEquals("", format.apply((Object[]) null)); + } + + @Test + void testEmptyParams() { + assertEquals("", format.apply()); + } + + @Test + void testZonedDateTimeParameter() { + String expected = "2023-02-26_17-11-23"; + String zdt = "2023-02-26T17:11:23.363270467+01:00[Europe/Berlin]"; + assertEquals(expected, format.apply(ZonedDateTime.parse(zdt))); + } + + @Test + void testStringParameter() { + String expected = "2023-02-26_17-11-23"; + String zdt = "2023-02-26T17:11:23.363270467+01:00[Europe/Berlin]"; + assertEquals(expected, format.apply(zdt)); + } + + @Test + void testInvalidStringParameter() { + assertEquals("", format.apply("bullshit")); + } + + @Test + void testIStringParameterWithPattern() { + String expected = "20230226-171123"; + String zdt = "2023-02-26T17:11:23.363270467+01:00[Europe/Berlin]"; + assertEquals(expected, format.apply(zdt, "yyyyMMdd-HHmmss")); + } + + @Test + void testIStringParameterWithInvalidPattern() { + String expected = ""; + String zdt = "2023-02-26T17:11:23.363270467+01:00[Europe/Berlin]"; + assertEquals(expected, format.apply(zdt, "asdft")); + } +} diff --git a/common/src/test/java/ctbrec/variableexpansion/functions/LowerTest.java b/common/src/test/java/ctbrec/variableexpansion/functions/LowerTest.java new file mode 100644 index 00000000..85170ba4 --- /dev/null +++ b/common/src/test/java/ctbrec/variableexpansion/functions/LowerTest.java @@ -0,0 +1,31 @@ +package ctbrec.variableexpansion.functions; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class LowerTest { + Lower lower = new Lower(); + + @Test + void testNullParams() { + assertEquals("", lower.apply((Object[]) null)); + } + + @Test + void testEmptyParams() { + assertEquals("", lower.apply()); + } + + @Test + void testNormalStrings() { + assertEquals("hello world", lower.apply("hello world")); + assertEquals("hello world", lower.apply("hElLo woRlD")); + assertEquals("hello world", lower.apply("HELLO WORLD")); + } + + @Test + void testSpecialCharacters() { + assertEquals("he{llo\nwo$rld", lower.apply("he{LLo\nwo$rlD")); + } +} diff --git a/common/src/test/java/ctbrec/variableexpansion/functions/OrElseTest.java b/common/src/test/java/ctbrec/variableexpansion/functions/OrElseTest.java new file mode 100644 index 00000000..b6852133 --- /dev/null +++ b/common/src/test/java/ctbrec/variableexpansion/functions/OrElseTest.java @@ -0,0 +1,29 @@ +package ctbrec.variableexpansion.functions; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class OrElseTest { + OrElse orElse = new OrElse(); + + @Test + void testNullParams() { + assertEquals("", orElse.apply((Object[]) null)); + } + + @Test + void testEmptyParams() { + assertEquals("", orElse.apply()); + } + + @Test + void testHappyPath() { + assertEquals("world", orElse.apply("world", "hello")); + } + + @Test + void testFallback() { + assertEquals("hello", orElse.apply("", "hello")); + } +} diff --git a/common/src/test/java/ctbrec/variableexpansion/functions/SanitizeTest.java b/common/src/test/java/ctbrec/variableexpansion/functions/SanitizeTest.java new file mode 100644 index 00000000..072dac4c --- /dev/null +++ b/common/src/test/java/ctbrec/variableexpansion/functions/SanitizeTest.java @@ -0,0 +1,35 @@ +package ctbrec.variableexpansion.functions; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class SanitizeTest { + Sanitize sanitize = new Sanitize(); + + @Test + void testNullParams() { + assertEquals("", sanitize.apply((Object[]) null)); + } + + @Test + void testEmptyParams() { + assertEquals("", sanitize.apply()); + } + + @Test + void testNormalStrings() { + assertEquals("hel!lo", sanitize.apply("hel!lo")); + assertEquals("hE$lLo", sanitize.apply("hE$lLo")); + assertEquals("HELL%O", sanitize.apply("HELL%O")); + } + + @Test + void testSpecialCharacters() { + assertEquals("asdf_asdf", sanitize.apply("asdf asdf")); + assertEquals("asdf_asdf", sanitize.apply("asdf\"asdf")); + assertEquals("asdf_asdf", sanitize.apply("asdf'asdf")); + assertEquals("asdf_asdf", sanitize.apply("asdf\\asdf")); + assertEquals("asdf_asdf", sanitize.apply("asdf/asdf")); + } +} diff --git a/common/src/test/java/ctbrec/variableexpansion/functions/TrimTest.java b/common/src/test/java/ctbrec/variableexpansion/functions/TrimTest.java new file mode 100644 index 00000000..eff00c8c --- /dev/null +++ b/common/src/test/java/ctbrec/variableexpansion/functions/TrimTest.java @@ -0,0 +1,33 @@ +package ctbrec.variableexpansion.functions; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class TrimTest { + Trim trim = new Trim(); + + @Test + void testNullParams() { + assertEquals("", trim.apply((Object[]) null)); + } + + @Test + void testEmptyParams() { + assertEquals("", trim.apply()); + } + + @Test + void testNormalStrings() { + assertEquals("hello", trim.apply("hello")); + assertEquals("hello", trim.apply(" hello ")); + assertEquals("hello", trim.apply(" hello ")); + assertEquals("hello", trim.apply("\thello\t")); + assertEquals("hello", trim.apply("\nhello\n")); + } + + @Test + void testSpecialCharacters() { + assertEquals("he{LLo\nwo $rlD", trim.apply("he{LLo\nwo $rlD")); + } +} diff --git a/common/src/test/java/ctbrec/variableexpansion/functions/UpperTest.java b/common/src/test/java/ctbrec/variableexpansion/functions/UpperTest.java new file mode 100644 index 00000000..16a546ee --- /dev/null +++ b/common/src/test/java/ctbrec/variableexpansion/functions/UpperTest.java @@ -0,0 +1,31 @@ +package ctbrec.variableexpansion.functions; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class UpperTest { + Upper upper = new Upper(); + + @Test + void testNullParams() { + assertEquals("", upper.apply((Object[]) null)); + } + + @Test + void testEmptyParams() { + assertEquals("", upper.apply()); + } + + @Test + void testNormalStrings() { + assertEquals("HELLO WORLD", upper.apply("hello world")); + assertEquals("HELLO WORLD", upper.apply("hElLo woRlD")); + assertEquals("HELLO WORLD", upper.apply("HELLO WORLD")); + } + + @Test + void testSpecialCharacters() { + assertEquals("HE{LLO\nWO$RLD", upper.apply("he{LLo\nwo$rlD")); + } +}