forked from j62/ctbrec
1
0
Fork 0

Add documentation for the post-processing steps

This commit is contained in:
0xb00bface 2020-09-28 17:46:11 +02:00
parent e4eee063ba
commit a8b1af848a
2 changed files with 106 additions and 66 deletions

View File

@ -20,6 +20,7 @@ import ctbrec.Settings.DirectoryStructure;
import ctbrec.Settings.ProxyType; import ctbrec.Settings.ProxyType;
import ctbrec.recorder.Recorder; import ctbrec.recorder.Recorder;
import ctbrec.sites.Site; import ctbrec.sites.Site;
import ctbrec.ui.DesktopIntegration;
import ctbrec.ui.SiteUI; import ctbrec.ui.SiteUI;
import ctbrec.ui.SiteUiFactory; import ctbrec.ui.SiteUiFactory;
import ctbrec.ui.controls.range.DiscreteRange; import ctbrec.ui.controls.range.DiscreteRange;
@ -44,6 +45,7 @@ import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.Tab; import javafx.scene.control.Tab;
import javafx.scene.control.TextInputDialog; import javafx.scene.control.TextInputDialog;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
@ -100,6 +102,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
private SimpleIntegerProperty postProcessingThreads; private SimpleIntegerProperty postProcessingThreads;
private IgnoreList ignoreList; private IgnoreList ignoreList;
private PostProcessingStepPanel postProcessingStepPanel; private PostProcessingStepPanel postProcessingStepPanel;
private Button variablesHelpButton;
public SettingsTab(List<Site> sites, Recorder recorder) { public SettingsTab(List<Site> sites, Recorder recorder) {
this.sites = sites; this.sites = sites;
@ -152,6 +155,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
private void createGui() { private void createGui() {
postProcessingStepPanel = new PostProcessingStepPanel(config); postProcessingStepPanel = new PostProcessingStepPanel(config);
variablesHelpButton = createHelpButton("Variables", "http://localhost:5689/docs/PostProcessing.md#variables");
ignoreList = new IgnoreList(sites); ignoreList = new IgnoreList(sites);
List<Category> siteCategories = new ArrayList<>(); List<Category> siteCategories = new ArrayList<>();
for (Site site : sites) { for (Site site : sites) {
@ -199,6 +203,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
Setting.of("Port", port), Setting.of("Port", port),
Setting.of("Path", path, "Leave empty, if you didn't change the servletContext in the server config"), Setting.of("Path", path, "Leave empty, if you didn't change the servletContext in the server config"),
Setting.of("Download Filename", downloadFilename, "File name pattern for downloads"), Setting.of("Download Filename", downloadFilename, "File name pattern for downloads"),
Setting.of("", variablesHelpButton),
Setting.of("Require authentication", requireAuthentication), Setting.of("Require authentication", requireAuthentication),
Setting.of("Use Secure Communication (TLS)", transportLayerSecurity) Setting.of("Use Secure Communication (TLS)", transportLayerSecurity)
) )
@ -206,7 +211,8 @@ public class SettingsTab extends Tab implements TabSelectionListener {
Category.of("Post-Processing", Category.of("Post-Processing",
Group.of("Post-Processing", Group.of("Post-Processing",
Setting.of("Threads", postProcessingThreads), Setting.of("Threads", postProcessingThreads),
Setting.of("Steps", postProcessingStepPanel) Setting.of("Steps", postProcessingStepPanel),
Setting.of("", createHelpButton("Post-Processing Help", "http://localhost:5689/docs/PostProcessing.md"))
) )
), ),
Category.of("Events & Actions", new ActionSettingsPanel(recorder)), Category.of("Events & Actions", new ActionSettingsPanel(recorder)),
@ -246,6 +252,13 @@ public class SettingsTab extends Tab implements TabSelectionListener {
prefs.getSetting("concurrentRecordings").ifPresent(s -> bindEnabledProperty(s, recordLocal.not())); prefs.getSetting("concurrentRecordings").ifPresent(s -> bindEnabledProperty(s, recordLocal.not()));
prefs.getSetting("downloadFilename").ifPresent(s -> bindEnabledProperty(s, recordLocal)); prefs.getSetting("downloadFilename").ifPresent(s -> bindEnabledProperty(s, recordLocal));
postProcessingStepPanel.disableProperty().bind(recordLocal.not()); postProcessingStepPanel.disableProperty().bind(recordLocal.not());
variablesHelpButton.disableProperty().bind(recordLocal);
}
private Button createHelpButton(String text, String url) {
Button postProcessingHelpButton = new Button(text);
postProcessingHelpButton.setOnAction(e -> DesktopIntegration.open(url));
return postProcessingHelpButton;
} }
private void bindEnabledProperty(Setting s, BooleanExpression bindTo) { private void bindEnabledProperty(Setting s, BooleanExpression bindTo) {

View File

@ -1,79 +1,106 @@
#### Post-Processing #### Post-Processing
The post-processing gives you the possibility to run any script / program after a recording has finished. You can use that to convert 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. the files to another format, create preview images, rename / move the file etc.
There are example scripts included in the distribution zip (`pp.bat`, `pp.ps1`, `pp.sh`). See also the comments in those files.
##### Local Recording ##### Available Steps
If you are using the local recording mode, you can set the post-processing script / program on the settings tab under *Recorder* -> *Post-Processing*. - **create a copy** - Creates a copy of the original recording. All following post-processing steps are executed on the copy, not on the
ctbrec will call the selected script after the recording has finished with the following parameters: original recording. This means, that the post-processing can be rerun in case a step failed, because the original recording is still
available.
- **rename** - Renames the recording with the help of variables. See [Variables](#variables)
- **move** - Moves the recording with the help of variables. See [Variables](#variables)
- **remux / transcode** - Executes FFmpeg with the given arguments on the recording. This step can be used to convert a recording to
MP4 or re-encode it to a different format / resolution. The input and output file don't have to be defined. If the step was successful
the input file gets deleted.
- **execute script** - Executes a script or program with the given arguments. You can use the [variables](#variables) to define what
to pass over to the script.
- **delete too short** - Delete a recording, if it is shorter than the given duration. This post-processing step has replaced the
setting, which was available in the post-processing category before
- **delete original** - This is a companion step for "create a copy". If you use the copy step and all other steps are successful,
you probably want to get rid of the original file and just keep the result of the post-processing. That's what this step does.
- **remove recording, but keep the files** - Removes the recording from the recordings list, but keeps the files untouched.
1. directory (absolute path) #### Planned for future releases
2. file (absolute path) - **call a webhook** - call a URL once a recording is finished
3. model name - **create contactsheet** - create a contact sheet with preview images of the recording
4. site name - **create timeline thumbnails** - create a small thumbnail for every second or every few seconds, which can be used to very fast
5. unixtime (seconds since 1/1/1970 00:00) scan through a recording
##### Remote Recording (server) #### Variables
If you are running the server and want to run a post-processing script, you have to edit the [configuration file](/docs/ConfigurationFile.md). Just set the absolute path to an executable script / program for the setting **postProcessing**. Since the recordings look a bit different for the server, the parameters, which are passed to the script are a bit different, too:
1. parent directory (absolute path) <a id="variables" />
2. directory of the recording (absolute path) - this directory contains the segments and the playlist
3. model name
4. site name
5. unixtime (seconds since 1/1/1970 00:00)
##### Example Scripts - **${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
${modelName}
- **${modelSanitizedName}** - sanitized name of the model. The following characters are replaced by an underscore:
\\, /, ', " and space
- **${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)
- **${modelNotes}** - sanitized model notes. The following characters are replaced by an underscore:
\\, /, ', " 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
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
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.
<table class="table-striped">
<thead>
<tr><th scope="col">Symbol</th> <th scope="col">Meaning</th> <th scope="col">Presentation</th> <th scope="col">Examples</th>
</tr></thead>
<tbody>
<tr><th scope="row">G</th> <td>era</td> <td>text</td> <td>AD; Anno Domini; A</td>
</tr><tr><th scope="row">u</th> <td>year</td> <td>year</td> <td>2004; 04</td>
</tr><tr><th scope="row">y</th> <td>year-of-era</td> <td>year</td> <td>2004; 04</td>
</tr><tr><th scope="row">D</th> <td>day-of-year</td> <td>number</td> <td>189</td>
</tr><tr><th scope="row">M/L</th> <td>month-of-year</td> <td>number/text</td> <td>7; 07; Jul; July; J</td>
</tr><tr><th scope="row">d</th> <td>day-of-month</td> <td>number</td> <td>10</td>
</tr><tr><th scope="row">g</th> <td>modified-julian-day</td> <td>number</td> <td>2451334</td>
Server script by @uo0Evx99@mastodon.cloud used on a Raspberry Pi 3 B+ to convert recordigns to mp4 files: </tr><tr><th scope="row">Q/q</th> <td>quarter-of-year</td> <td>number/text</td> <td>3; 03; Q3; 3rd quarter</td>
``` </tr><tr><th scope="row">Y</th> <td>week-based-year</td> <td>year</td> <td>1996; 96</td>
#!/bin/bash </tr><tr><th scope="row">w</th> <td>week-of-week-based-year</td> <td>number</td> <td>27</td>
</tr><tr><th scope="row">W</th> <td>week-of-month</td> <td>number</td> <td>4</td>
</tr><tr><th scope="row">E</th> <td>day-of-week</td> <td>text</td> <td>Tue; Tuesday; T</td>
</tr><tr><th scope="row">e/c</th> <td>localized day-of-week</td> <td>number/text</td> <td>2; 02; Tue; Tuesday; T</td>
</tr><tr><th scope="row">F</th> <td>day-of-week-in-month</td> <td>number</td> <td>3</td>
# </tr><tr><th scope="row">a</th> <td>am-pm-of-day</td> <td>text</td> <td>PM</td>
#Explanation of the code: It first takes all the .ts files (because sadly the server </tr><tr><th scope="row">h</th> <td>clock-hour-of-am-pm (1-12)</td> <td>number</td> <td>12</td>
#doesnt save the video in 1 big file instead) and creates one big .ts file in the same </tr><tr><th scope="row">K</th> <td>hour-of-am-pm (0-11)</td> <td>number</td> <td>0</td>
#folder called "all". </tr><tr><th scope="row">k</th> <td>clock-hour-of-day (1-24)</td> <td>number</td> <td>24</td>
#
#Then it uses ffmpeg to re-encode the .ts file to .mp4 while just copying the audio
#and video codec to reduce the encoding time. It also uses the -movflags faststart so
#the video can be played back instantly when viewing online (e.g. with Plex)
#That re-encoded file is being stored in the folder /home/pi/hdd/plex/"MODEL NAME"/
#with the name "CURRENT TIME"-"MODEL NAME".mp4
#
#Then it deletes the folder in which all the .ts files were, because we dont need
#those anymore as we have our .mp4 file re-encoded already and ready to use/stream.
#
#This script has been used on a Raspberry Pi 3 B+
#
# $1 directory (absolute path) </tr><tr><th scope="row">H</th> <td>hour-of-day (0-23)</td> <td>number</td> <td>0</td>
# $2 file (absolute path) </tr><tr><th scope="row">m</th> <td>minute-of-hour</td> <td>number</td> <td>30</td>
# $3 model name </tr><tr><th scope="row">s</th> <td>second-of-minute</td> <td>number</td> <td>55</td>
# $4 site name </tr><tr><th scope="row">S</th> <td>fraction-of-second</td> <td>fraction</td> <td>978</td>
# $5 unixtime </tr><tr><th scope="row">A</th> <td>milli-of-day</td> <td>number</td> <td>1234</td>
</tr><tr><th scope="row">n</th> <td>nano-of-second</td> <td>number</td> <td>987654321</td>
</tr><tr><th scope="row">N</th> <td>nano-of-day</td> <td>number</td> <td>1234000000</td>
# format unixtime to human readable </tr><tr><th scope="row">V</th> <td>time-zone ID</td> <td>zone-id</td> <td>America/Los_Angeles; Z; -08:30</td>
TIME=$(date --date="@$5" +%H:%M_%d.%m.%Y) </tr><tr><th scope="row">v</th> <td>generic time-zone name</td> <td>zone-name</td> <td>Pacific Time; PT</td>
</tr><tr><th scope="row">z</th> <td>time-zone name</td> <td>zone-name</td> <td>Pacific Standard Time; PST</td>
</tr><tr><th scope="row">O</th> <td>localized zone-offset</td> <td>offset-O</td> <td>GMT+8; GMT+08:00; UTC-08:00</td>
</tr><tr><th scope="row">X</th> <td>zone-offset 'Z' for zero</td> <td>offset-X</td> <td>Z; -08; -0830; -08:30; -083015; -08:30:15</td>
</tr><tr><th scope="row">x</th> <td>zone-offset</td> <td>offset-x</td> <td>+0000; -08; -0830; -08:30; -083015; -08:30:15</td>
</tr><tr><th scope="row">Z</th> <td>zone-offset</td> <td>offset-Z</td> <td>+0000; -0800; -08:00</td>
cat $2/*.ts > "$2/all.ts" && ffmpeg -i "$2/all.ts" -acodec copy -vcodec copy -movflags faststart "/home/pi/hdd/plex/$3/$TIME-$3.mp4" && rm -r "$2" </tr><tr><th scope="row">p</th> <td>pad next</td> <td>pad modifier</td> <td>1</td>
```
</tr><tr><th scope="row">'</th> <td>escape for text</td> <td>delimiter</td> <td></td>
</tr><tr><th scope="row">''</th> <td>single quote</td> <td>literal</td> <td>'</td>
</tr></tbody>
</table>
Windows script by @electrotek@mastodon.cloud. For more information see: [DateTimeFormatter](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html)
Automatically converts .ts files into mkv after recording ended, factors in multiple/split files. Assumes mkvtoolnix installed in path stated
```
@echo off
set directory=%1
set file=%2
set model=%3
set site=%4
set unixtime=%5
FOR %%f in (%directory%\*.ts) do (
"C:\Program Files\MKVToolNix\mkvmerge.exe" -o "%directory%\%%~nf.mkv" --title "%model%" "%directory%\%%~nf.ts"
del "%directory%\%%~nf.ts"
)
```
##### Video Tutorials
<iframe width="560" height="315" src="https://www.youtube.com/embed/QIZhrhBFhoc" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>