diff --git a/.assets/radarr-enable-hardlinks.png b/.assets/radarr-enable-hardlinks.png
new file mode 100644
index 0000000..8c6526c
Binary files /dev/null and b/.assets/radarr-enable-hardlinks.png differ
diff --git a/.github/workflows/BuildImage.yml b/.github/workflows/BuildImage.yml
index 7ff8728..79a5cf1 100644
--- a/.github/workflows/BuildImage.yml
+++ b/.github/workflows/BuildImage.yml
@@ -22,7 +22,7 @@ jobs:
echo "MODNAME=${{ env.MODNAME }}" >> $GITHUB_OUTPUT
echo "MULTI_ARCH=${{ env.MULTI_ARCH }}" >> $GITHUB_OUTPUT
# **** If the mod needs to be versioned, set the versioning logic below. Otherwise leave as is. ****
- MOD_VERSION="2.9.0"
+ MOD_VERSION="2.13.0"
echo "MOD_VERSION=${MOD_VERSION}" >> $GITHUB_OUTPUT
outputs:
GITHUB_REPO: ${{ steps.outputs.outputs.GITHUB_REPO }}
diff --git a/README.md b/README.md
index 549221e..4003d32 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,11 @@
# About
-A [Docker Mod](https://github.com/linuxserver/docker-mods) for the LinuxServer.io Radarr/Sonarr v3 or higher Docker containers that adds a script to automatically strip out unwanted audio and subtitle tracks, keeping only the desired languages.
+A [Docker Mod](https://github.com/linuxserver/docker-mods) for the LinuxServer.io Radarr/Sonarr v3 or higher Docker containers that adds a script to automatically strip out unwanted audio and subtitle tracks, keeping only the desired languages. A [Batch Mode](#batch-mode) is also supported that allows usage outside of Radarr/Sonarr.
**This unified script works in both Radarr and Sonarr. Use this mod in either container!**
->![notes] This mod supports Linux OSes only.
+> [!NOTE]
+> This mod supports Linux OSes only.
+
Production Container info: [](https://hub.docker.com/r/linuxserver/mods/tags?name=radarr-striptracks "Docker image size")
[](https://github.com/linuxserver/docker-mods/pkgs/container/mods "GitHub package pulls")
@@ -64,7 +66,7 @@ Development Container info:
Synology Screenshot
- *Example Synology Configuration*
+ *Example Synology Configuration*

@@ -77,15 +79,16 @@ Development Container info:
Screenshot
- *Example Custom Script*
+ *Example Custom Script*

The script will detect the language(s) defined in Radarr/Sonarr for the movie or TV show and only keep the audio and subtitles selected.
- Alternatively, a wrapper script or an environment variable may be used to more granularly define which tracks to keep. See [Wrapper Scripts](./README.md#wrapper-scripts) or [Environment Variable](./README.md#environment-variable) for more details.
+ Alternatively, a wrapper script or an environment variable may be used to more granularly define which tracks to keep. See [Wrapper Scripts](#wrapper-scripts) or [Environment Variable](#environment-variable) for more details.
- >![notes] You **must** configure language(s) in Radarr/Sonarr *or* pass command-line arguments for the script to do anything! See the next section for an example.
+> [!IMPORTANT]
+> You **must** configure language(s) in Radarr/Sonarr *or* pass command-line arguments for the script to do anything! See the next section for an example.
## Radarr Configuration Example
The following is a simplified example and steps to configure Radarr so the script will keep Original and English languages of an imported movie.
@@ -95,7 +98,7 @@ The following is a simplified example and steps to configure Radarr so the scrip
Screenshot
- *New Custom Format Example*
+ *New Custom Format Example*

@@ -118,13 +121,13 @@ The following is a simplified example and steps to configure Radarr so the scrip
Screenshot
- *Radarr Quality Profile Example*
+ *Radarr Quality Profile Example*

Now, when Radarr imports a movie with the 'Any' Quality Profile, the script will keep only Original and English languages. This is equivalent to calling the script with `--audio :org:eng --subs :org:eng` command-line arguments.
-See [Automatic Language Detection](./README.md#automatic-language-detection) for more details.
+See [Automatic Language Detection](#automatic-language-detection) for more details.
# Usage Details
The source video can be any mkvtoolnix supported video format. The output is an MKV file with the same name and the same permissions. Owner is preserved if the script is executed as root.
@@ -132,34 +135,43 @@ Chapters, if they exist, are preserved. The Title attribute in the MKV is set to
(ex: `The Sting (1973)`) or the series title plus episode information (ex: `Happy! 01x01 - What Smiles Are For`).
The language of the video file will be updated in the Radarr or Sonarr database to reflect the actual languages preserved in the remuxed video, and the video will be renamed according to the Radarr/Sonarr rules if needed (for example, if a removed track would trigger a name change.)
-If you've configured the Radarr/Sonarr **Recycle Bin** path correctly, the original video will be moved there.
->![warning] **WARNING:** If you have *not* configured the Recycle Bin, the original video file will be deleted/overwritten and permanently lost.
-
If the resulting video file would contain the same tracks as the original, and it's already an MKV, the remux step is skipped.
+> [!TIP]
+> If you've configured the Radarr/Sonarr **Recycle Bin** path correctly, the original video will be moved there.
+
+> [!CAUTION]
+> If you have ***not*** configured the Recycle Bin, the original video file will be deleted/overwritten and permanently lost.
+
## Automatic Language Detection
-Beginning with version 2.0 of this mod, the script may be called with no arguments. It will detect the language(s) configured within Radarr/Sonarr on the particular movie or TV show.
-Language selection(s) may be configured in ***Custom Formats*** (in Radarr v3 and higher and Sonarr v4 and higher), ***Quality Profiles*** (only in Radarr), or ***Language Profiles*** (Sonarr v3). Example screenshots are below.
+When the script is called with no arguments, it will attempt to detect the language(s) configured within Radarr/Sonarr on the particular movie or TV show.
+Language selection(s) may be configured in:
+- ***Custom Formats*** (in Radarr v3 and higher and Sonarr v4 and higher),
+- ***Quality Profiles*** (only in Radarr), or
+- ***Language Profiles*** (Sonarr v3)
Both audio **and** subtitle tracks that match the configured language(s) are kept.
+> [!TIP]
+> It is **highly recommended** to review the [TraSH Guides](https://trash-guides.info/Radarr/Tips/How-to-setup-language-custom-formats/) setup instructions for Language Custom Formats.
+
+### Special Language Selections
The language selection **'Original'** will use the language Radarr pulled from [The Movie Database](https://www.themoviedb.org/ "TMDB") or that Sonarr pulled from [The TVDB](https://www.thetvdb.com/ "TVDB") during its last refresh.
-Selecting this language is functionally equivalent to calling the script with `--audio :org --subs :org` command-line arguments. See [Original language code](./README.md#original-language-code) below for more details.
+Selecting this language is functionally equivalent to calling the script with `--audio :org --subs :org` command-line arguments. See [Original language code](#original-language-code) below for more details.
The language selection **'Unknown'** will match tracks with **no configured language** in the video file. Selecting this language is functionally equivalent to calling the script with `--audio :und --subs :und` command-line arguments.
-See [Unknown language code](./README.md#unknown-language-code) below for more details.
+See [Unknown language code](#unknown-language-code) below for more details.
-The language selection **'Any'** has two purposes (Radarr only):
- 1) It will trigger a search of languages in ***Custom Formats***
- 2) If none are found, it will preserve **all languages** in the video file. This is functionally equivalent to calling the script with `--audio :any --subs :any` command-line arguments.
- See [Any language code](./README.md#any-language-code) below for more details.
+The language selection **'Any'** has two purposes:
+ 1) In Radarr only, when set on a Quality Profile, it will trigger a search of languages in ***Custom Formats***
+ 2) If languages are not configured in a Custom Format, or if you're using Sonarr, it will preserve **all languages** in the video file. This is functionally equivalent to calling the script with `--audio :any --subs :any` command-line arguments.
+ See [Any language code](#any-language-code) below for more details.
->![notes] When using *Custom Format* language conditions and scoring you may not get the results you expect.
->This can be non-intuitive configuration, especially when using negative scoring, the 'Negate' option, and the 'Except Language' option.
->The script does not care what custom format is *detected* by Radarr/Sonarr on the video file, only what the *scores* are in the *Quality Profile*.
->If you choose to use Custom Formats, it is **highly recommended** to first run the script with the debug option `-d`, perform some test downloads and script runs, and then examine your results and the script logs closely to be sure things are working the way you want them to.
-
-It is **highly recommended** to review the [TraSH Guides](https://trash-guides.info/Radarr/Tips/How-to-setup-language-custom-formats/) setup instructions for Language Custom Formats.
+> [!IMPORTANT]
+> When using *Custom Formats* language conditions and scoring you may not get the results you expect.
+> This can be non-intuitive configuration, especially when using negative scoring, the 'Negate' option, and the 'Except Language' option.
+> The script does not care what custom format is *applied* by Radarr/Sonarr on the video file, only what the custom format conditions are and the *scores* are in the corresponding *Quality Profile*.
+> If you choose to use Custom Formats, it is **highly recommended** to first run the script with the debug option `-d`, perform some test downloads and script runs, and then examine your results and the script logs closely to be sure things are working the way you want them to.
### Language Detection Precedence
The following chart represents the order of precedence that the script uses to decide which language(s) to select when there are multiple settings configured. Moving left to right, it will stop when it finds a configured language.
@@ -167,25 +179,30 @@ The following chart represents the order of precedence that the script uses to d
```mermaid
graph LR
A[Command-Line]
- B["Quality
+ B["Environment
+ Variable"]
+ C["Quality
Profile"]
- C["Custom
+ D["Custom
Formats"]
- D["Language Profile
+ E["Language Profile
(Sonarr only)"]
A-->B
- B-- 'Any' -->C
- C-->D
+ B-->C
+ C-- 'Any' -->D
+ D-->E
```
Descriptively, these steps are:
-1. Command-line arguments (or environment variable) override all automatic language selection.
-2. If there are no command-line arguments, the video's *Quality Profile* is examined for a language configuration (only supported in Radarr).
-3. If there is no *Quality Profile* language **or** it is set to 'Any', then examine the *Custom Formats* and scores associated with the quality profile.
+1. Command-line arguments override all automatic language selection.
+2. Environment variable is checked for arguments.
+3. If there are no command-line or environment variable arguments, the video's *Quality Profile* is examined for a language configuration (only supported in Radarr).
+4. If there is no *Quality Profile* language **or** it is set to 'Any', then examine the *Custom Formats* and scores associated with the quality profile.
All language conditions with positive scores *and* Negated conditions with negative scores *and* non-Negated Except Language conditions with negative scores are selected.
-4. If the *Custom Format* scores are zero (0) or there are none with configured language conditions, use the *Language Profile* (only supported in Sonarr v3)
+5. If the *Custom Format* scores are zero (0) or there are none with configured language conditions, use the *Language Profile* (only supported in Sonarr v3)
->![notes] For step 3 above, using *Custom Formats* when 'Any' is in the *Quality Profile* is consistent with the behavior described in [TRaSH Guides](https://trash-guides.info/Radarr/Tips/How-to-setup-language-custom-formats/ "TraSH Guides: How to setup Language Custom Formats").
+> [!NOTE]
+> For step 4 above, using *Custom Formats* when 'Any' is in the *Quality Profile* is consistent with the behavior described in [TRaSH Guides](https://trash-guides.info/Radarr/Tips/How-to-setup-language-custom-formats/ "TraSH Guides: How to setup Language Custom Formats").
## Command-Line Syntax
@@ -193,25 +210,26 @@ All language conditions with positive scores *and* Negated conditions with negat
The script also supports command-line arguments that will override the automatic language detection. More granular control can therefore be exerted or extended using tagging and defining multiple *Connect* scripts (this is native Radarr/Sonarr functionality outside the scope of this documentation).
The syntax for the command-line is:
-`striptracks.sh [{-a|--audio} [{-s|--subs} ] [{-f|--file} ]] [{-l,--log} ] [{-c,--config} ] [{-d|--debug} []]`
+`striptracks.sh [{-a|--audio} [{-s|--subs} ] [--reorder] [{-f|--file} ]] [{-l|--log} ] [{-c|--config} ] [{-d|--debug} []]`
Table of Command-Line Arguments
Option|Argument|Description
---|---|---
--a, --audio||Audio languages to keep
ISO 639-2 code(s) prefixed with a colon (`:`)
--s, --subs||Subtitle languages to keep
ISO 639-2 code(s) prefixed with a colon (`:`)
--f, --file||If included, the script enters **[Batch Mode](./README.md#batch-mode)** and converts the specified video file.
Requires the `-a` option.
![notes] **Do not** use this argument when called from Radarr or Sonarr!
--l, --log|\|The log filename
Default is /config/log/striptracks.txt
--c, --config|\|Radarr/Sonarr XML configuration file
Default is /config/config.xml
--d, --debug|\[\\]|Enables debug logging. Level is optional.
Default is 1 (low)
2 includes JSON output
3 contains even more JSON output
---help| |Display help and exit.
---version| |Display version and exit.
+`-a`, `--audio`|``|Audio languages to keep
ISO 639-2 code(s) prefixed with a colon (`:`)
Each code may optionally be followed by a plus (`+`) and one or more [modifiers](#language-code-modifiers).
+`-s`, `--subs`|``|Subtitle languages to keep
ISO 639-2 code(s) prefixed with a colon (`:`)
Each code may optionally be followed by a plus (`+`) and one or more modifiers.
+`--reorder`| |Reorder audio and subtitles tracks to match the language code order specified in the `` and `` arguments.
This is skipped if no tracks are removed.
+`-f`, `--file`|``|If included, the script enters **[Batch Mode](#batch-mode)** and converts the specified video file.
Requires the `--audio` option.
![notes] **Do not** use this argument when called from Radarr or Sonarr!
+`-l`, `--log`|``|The log filename
Default is `/config/log/striptracks.txt`
+`-c`, `--config`|``|Radarr/Sonarr XML configuration file
Default is `/config/config.xml`
+`-d`, `--debug`|`[]`|Enables debug logging. Level is optional.
Default is `1` (low)
`2` includes JSON output
`3` contains even more JSON output
+`--help`| |Display help and exit.
+`--version`| |Display version and exit.
-The `` and `` are optional arguments that are colon (`:`) prepended language codes in [ISO 639-2](https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes "List of ISO 639-2 codes") format.
+The `` and `` arguments are colon (`:`) prepended language codes in [ISO 639-2](https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes "List of ISO 639-2 codes") format.
For example:
* `:eng`
@@ -220,9 +238,30 @@ For example:
...etc.
-Multiple codes may be concatenated, such as `:eng:spa` for both English and Spanish. Order is unimportant.
+Multiple codes may be concatenated, such as `:eng:spa` for both English and Spanish. Order is unimportant, unless the `--reorder` option is also specified.
->![warning] **WARNING:** If no subtitle language is detected via Radarr/Sonarr configuration or specified on the command-line, all subtitles are removed.
+> [!WARNING]
+> If no subtitle language is detected via Radarr/Sonarr configuration or specified on the command-line, all subtitles are removed.
+
+### Language Code Modifiers
+Each language code can optionally be followed by a plus (`+`) and one or more modifier characters. Supported modifiers are:
+
+Modifier|Function
+---|---
+`f`|Selects only tracks with the forced flag set
+`d`|Selects only tracks with the default flag set
+`[0-9]`|Specifies the maximum number of tracks to select.
Based on the order of the tracks in the original source video.
+
+These modifiers must be applied to each language code you want to modify. They may be used with either audio or subtitles codes.
+For example, the following options, `--audio :org:any+d --subs :eng+1:any+f` would keep:
+- All original language audio tracks, and all Default audio tracks regardless of language
+- One English language subtitles track, and all Forced subtitles tracks regardless of language
+
+Modifiers may be combined, such as `:any+fd` to keep all forced and all default tracks, or `:eng+1d` to keep one default English track.
+
+> [!NOTE]
+> Note the exact phrasing of the previous sentence. There is nuance here that is not obvious.
+> `:any+fd` is equivalent to `:any+f:any+d`, but `:eng+1d` is **not** the same as `:eng+1:eng+d`.
### Any language code
The `:any` language code is a special code. When used, the script will preserve all language tracks, regardless of how they are tagged in the source video.
@@ -230,14 +269,16 @@ The `:any` language code is a special code. When used, the script will preserve
### Original language code
The `:org` language code is a special code. When used, instead of retaining a specific language, the script substitutes the original movie or TV show language as specified in its [The Movie Database](https://www.themoviedb.org/ "TMDB") or [The TVDB](https://www.thetvdb.com/ "TVDB") entry.
As an example, when importing "*Amores Perros (2000)*" with options `--audio :org:eng`, the Spanish and English audio tracks are preserved.
-Several [Included Wrapper Scripts](./README.md#included-wrapper-scripts) use this special code.
+Several [Included Wrapper Scripts](#included-wrapper-scripts) use this special code.
->![notes] This feature relies on the 'originalLanguage' field in the Radarr/Sonarr database. The `:org` code is therefore invalid when used in Batch Mode.
+> [!NOTE]
+> This feature relies on the 'originalLanguage' field in the Radarr/Sonarr database. The `:org` code is therefore invalid when used in Batch Mode.
> The script will log a warning if it detects the use of `:org` in an invalid way, though it will continue to execute.
### Unknown language code
The `:und` language code is a special code. When used, the script will match on any track that has a null or blank language attribute. If not included, tracks with no language attribute will be removed.
->![warning] **WARNING:** It is common for M2TS and AVI files to have tracks with unknown languages! It is strongly recommended to include `:und` in most instances unless you know exactly what you're doing.
+> [!TIP]
+> It is common for M2TS and AVI files to have tracks with unknown languages! It is recommended to include `:und` in most instances unless you know exactly what you're doing.
## Special Handling of Audio
The script is smart enough to not remove the last audio track. There is in fact no way to force the script to remove all audio. This way you don't have to specify every possible language if you are importing a foreign film, for example.
@@ -255,25 +296,34 @@ There is no way to force the script to remove audio tracks with these codes.
-d 2 # Enable debugging level 2, audio and subtitles
# languages detected from Radarr/Sonarr
-a :eng:und -s :eng # Keep English and Unknown audio, and English subtitles
--a :org:eng -s :eng # Keep English and Original audio, and English subtitles
-:eng "" # Keep English audio and remove all subtitles
--d -a :eng:kor:jpn -s :eng:spa # Enable debugging level 1, keeping English, Korean, and Japanese audio, and
- # English and Spanish subtitles
+-a :org:eng -s :any+f:eng # Keep English and Original audio, and all forced or English subtitles
+-a :eng -s "" # Keep English audio and remove all subtitles
+-a :any -s "" # Keep all audio and remove all subtitles
+-d -a :eng:kor:jpn -s :eng:spa # Enable debugging level 1, keeping English, Korean, and Japanese audio,
+ # and English and Spanish subtitles
-f "/movies/Finding Nemo (2003).mkv" -a :eng:und -s :eng
# Batch Mode
# Keep English and Unknown audio and English subtitles, converting
# video specified
--a :any -s "" # Keep all audio and remove all subtitles
+--audio :org:any+d1 --subs :eng+1:any+f2
+ # Keep Original audio and one default audio track regardless of language
+ # (first audio track flagged as Default as it appears in the source file),
+ # one English subtitles track and two forced subtitles regardless of
+ # language (as they appear in the source file)
```
## Wrapper Scripts
-To supply arguments to the script, you must either use one of the included wrapper scripts, create a custom wrapper script, or set the `STRIPTRACKS_ARGS` [environment variable](./README.md#environment-variable).
+To supply arguments to the script, you must either use one of the included wrapper scripts, create a custom wrapper script, or set the `STRIPTRACKS_ARGS` [environment variable](#environment-variable).
+
+> [!TIP]
+> If you followed the Linuxserver.io recommendations when configuring your container, the `/config` directory will be mapped to an external storage location.
+> It is therefore recommended to place custom scripts in the `/config` directory so they will survive container updates, but they may be placed anywhere that is accessible by Radarr or Sonarr.
### Included Wrapper Scripts
For your convenience, several wrapper scripts are included in the `/usr/local/bin/` directory.
-You may use any of these in place of `striptracks.sh` mentioned in the [Installation](./README.md#installation) section above.
+You may use any of these in place of `striptracks.sh` mentioned in the [Installation](#installation) section above.
List of scripts
@@ -288,10 +338,9 @@ striptracks-eng-debug.sh # Keep English and Unknown audio, and English subtitl
striptracks-eng-fre.sh # Keep English, French, and Unknown audio, and English and French subtitles
striptracks-eng-jpn.sh # Keep English, Japanese, and Unknown audio and English subtitles
striptracks-fre.sh # Keep French and Unknown audio, and French subtitles
-striptracks-fre-debug.sh # Keep French and Unknown audio, French subtitles, and enable debug logging
striptracks-ger.sh # Keep German and Unknown audio, and German subtitles
striptracks-spa.sh # Keep Spanish and Unknown audio, and Spanish subtitles
-striptracks-org-eng.sh # Keep Original, English, and Unknown audio, and Original and English subtitles
+striptracks-org-eng.sh # Keep Original, English, Unknown, and forced audio, and Original, English, and forced subtitles
striptracks-org-ger.sh # Keep Original, German, and Unknown audio, and Original and German subtitles
striptracks-org-spa.sh # Keep Original, Spanish, and Unknown audio, and Original and Spanish subtitles
```
@@ -299,7 +348,10 @@ striptracks-org-spa.sh # Keep Original, Spanish, and Unknown audio, and Orig
### Example Wrapper Script
-To configure an entry from the [Examples](./README.md#examples) section above, create and save a file called `striptracks-custom.sh` to `/config` containing the following text:
+
+Example Script
+
+To configure an entry from the [Examples](#examples) section above, create and save a file called `striptracks-custom.sh` to `/config` containing the following text:
```shell
#!/bin/bash
@@ -313,12 +365,18 @@ Make it executable:
chmod +x /config/striptracks-custom.sh
```
-Then put `/config/striptracks-custom.sh` in the **Path** field in place of `/usr/local/bin/striptracks.sh` mentioned in the [Installation](./README.md#installation) section above.
+Then put `/config/striptracks-custom.sh` in the **Path** field in place of `/usr/local/bin/striptracks.sh` mentioned in the [Installation](#installation) section above.
->![notes] If you followed the Linuxserver.io recommendations when configuring your container, the `/config` directory will be mapped to an external storage location. It is therefore recommended to place custom scripts in the `/config` directory so they will survive container updates, but they may be placed anywhere that is accessible by Radarr or Sonarr.
+
## Environment Variable
-The `striptracks.sh` script can read command-line arguments from the `STRIPTRACKS_ARGS` environment variable. This allows advanced use cases without having to provide a custom script.
+The script can also read arguments from the `STRIPTRACKS_ARGS` environment variable. This allows advanced use cases without having to provide a custom wrapper script.
+
+> [!NOTE]
+> The environment variable is *only* used when **no** command-line arguments are present. **Any** command-line argument will disable the use of the environment variable.
+
+
+Example Docker Compose
For example, the following lines in your `compose.yml` file would keep English, Japanese, and Unknown audio and English subtitles:
@@ -327,12 +385,18 @@ environment:
- STRIPTRACKS_ARGS=--audio :eng:jpn:und --subs :eng
```
+
+
+Example Docker Run Command
+
In a `docker run` command, it would be:
```shell
-e STRIPTRACKS_ARGS='--audio :eng:jpn:und --subs :eng'
```
+
+
Synology Screenshot
@@ -341,15 +405,13 @@ In a `docker run` command, it would be:
->![notes] The environment variable is *only* read when **no** command-line arguments are present. **Any** command-line argument will disable the use of the environment variable.
-
## Triggers
The only events/notification triggers that are supported are **On Import** and **On Upgrade**. The script will log an error if executed by any other trigger.
## Batch Mode
Batch mode allows the script to be executed independently of Radarr or Sonarr. It converts the file specified on the command-line and ignores any environment variables that are normally expected to be set by the video management program.
-Using this function, you can easily process all of your video files in any subdirectory at once. See the [Batch Example](./README.md#batch-example) below.
+Using this function, you can easily process all of your video files in any subdirectory at once. See the [Batch Example](#batch-example) below.
### Script Execution Differences in Batch Mode
Because the script is not called from within Radarr or Sonarr, their database is unavailable to the script. Therefore, expect the following behavior while in Batch Mode:
@@ -361,18 +423,23 @@ Because the script is not called from within Radarr or Sonarr, their database is
* *Original video files are deleted.*
The Recycle Bin function is not available.
### Batch Example
+
+Batch Mode Example
+
To keep English and Unknown audio and English subtitles on all video files ending in .MKV, .AVI, or .MP4 in the `/movies` directory, enter the following at the Linux command-line:
```shell
find /movies/ -type f \( -name "*.mkv" -o -name "*.avi" -o -name "*.mp4" \) | while read file; do /usr/local/bin/striptracks.sh -f "$file" -a :eng:und -s :eng; done
```
-Here's another example to keep English, Danish, and Unknown languages on all video files in your `./videos` directory (requires the `file` program; testable with `file -v`):
+Here's another example to keep English, Danish, Unknown languages, and all forced subtitles on all video files in your `./videos` directory (requires the `file` program; testable with `file -v`):
```shell
-find ./videos/ -type f | while read filename; do if file -i "$filename" | grep -q video; then /usr/local/bin/striptracks.sh -f "$filename" --audio :eng:dan:und --subs :eng:dan:und; fi; done
+find ./videos/ -type f | while read filename; do if file -i "$filename" | grep -q video; then /usr/local/bin/striptracks.sh -f "$filename" --audio :eng:dan:und --subs :eng:dan:und:any+f; fi; done
```
+
+
## Logs
By default, a log file is created for the script activity called:
@@ -383,11 +450,39 @@ This log can be inspected or downloaded from Radarr/Sonarr under *System* > *Log
Script errors will show up in both the script log and the native Radarr/Sonarr log.
Log rotation is performed with 5 log files of 512KB each being kept.
->![warning] **WARNING:** If debug logging is enabled with a level above 1, the log file can grow very large very quickly. *Do not leave high-level debug logging enabled permanently.*
+> [!CAUTION]
+> If debug logging is enabled with a level above 1, the log file can grow very large very quickly. *Do not leave high-level debug logging enabled permanently.*
+
+# Limitations
+It should be noted that this script's core functionality nulifies some of the benefits of [hardlinks](https://trash-guides.info/hardlinks/).
+However, configuring hardlinks is still recommended.
+
+
+Hardlink Limitations
+
+*Radarr Hardlinks Configuration Screenshot*
+
+
+Hardlinks are essentially multiple references to the *same file*.
+The purpose of a hardlink is to:
+- Allow instant file moves from the download client to Radarr or Sonarr
+- Reduce duplicate storage space
+- Allow torrent seeding after download
+
+Because the script creates a brand-new video file that includes only the selected streams and deletes the original, a hardlink cannot be preserved.
+Instant file moves from your download client will continue to work, but the new file will consume additional space, and the original file will be deleted (or unlinked) by
+the script which could prevent torrent seeding.
+
+The script will log a warning if it detects the input video is a hardlink.
+
+Note that the script does not *always* create a new file. If there are no streams removed, the original video file is not deleted and any hardlinks are preserved.
+It is therefore still recommended to enable and use hardlinks in Radarr and Sonarr.
+
+
# Uninstall
To completely remove the mod:
-1. Delete the custom script from Radarr's or Sonarr's *Settings* > *Connect* screen that you created in the [Installation](./README.md#installation) section above.
+1. Delete the custom script from Radarr's or Sonarr's *Settings* > *Connect* screen that you created in the [Installation](#installation) section above.
2. Stop and delete the Radarr/Sonarr container.
3. Remove the **DOCKER_MODS** environment variable from your `compose.yaml` file or exclude it from the `docker run` command when re-creating the Radarr/Sonarr container.
@@ -403,11 +498,11 @@ This would not be possible without the following:
[LinuxServer.io Sonarr](https://hub.docker.com/r/linuxserver/sonarr "Sonarr Docker container") container
[LinuxServer.io Docker Mods](https://hub.docker.com/r/linuxserver/mods "Docker Mods containers") project
[MKVToolNix](https://mkvtoolnix.download/ "MKVToolNix homepage") by Moritz Bunkus
-The AWK script parsing mkvmerge output is adapted from Endoro's post on [VideoHelp](https://forum.videohelp.com/threads/343271-BULK-remove-non-English-tracks-from-MKV-container#post2292889).
+Inspired by Endoro's post on [VideoHelp](https://forum.videohelp.com/threads/343271-BULK-remove-non-English-tracks-from-MKV-container#post2292889).
Icons made by [Freepik](https://www.freepik.com) from [Flaticon](https://www.flaticon.com/)
## Legacy Change Notes
-Beginning with version 2.0 of this mod, it only supports v3 or later of Radarr/Sonarr. For legacy Radarr/Sonarr v2 please use mod release 1.3 or earlier.
+Beginning with version 2.0 of this mod, it only supports v3 or later of Radarr/Sonarr. For legacy Radarr/Sonarr v2 please use mod release 1.3 or earlier.
+Version 2.0 of this mod introduced automatic language detection.
-[warning]: .assets/warning.png "Warning"
[notes]: .assets/notes.png "Note"
diff --git a/SECURITY.md b/SECURITY.md
index d8bfb32..21ce748 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -6,8 +6,8 @@ Only the latest major and minor version are supported.
| Version | Supported |
| ------- | ------------------ |
-| 2.9.x | :heavy_check_mark: |
-| < 2.9 | :x: |
+| 2.13.x | :heavy_check_mark: |
+| < 2.13 | :x: |
## Reporting a Vulnerability
diff --git a/root/usr/local/bin/striptracks-dut.sh b/root/usr/local/bin/striptracks-dut.sh
index 57571f6..1d635ee 100755
--- a/root/usr/local/bin/striptracks-dut.sh
+++ b/root/usr/local/bin/striptracks-dut.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-. /usr/local/bin/striptracks.sh :nld:dut:und :nld:dut
+. /usr/local/bin/striptracks.sh --audio :nld:dut:und --subs :nld:dut
diff --git a/root/usr/local/bin/striptracks-eng-debug.sh b/root/usr/local/bin/striptracks-eng-debug.sh
index 87ed2bb..3759e3b 100755
--- a/root/usr/local/bin/striptracks-eng-debug.sh
+++ b/root/usr/local/bin/striptracks-eng-debug.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-. /usr/local/bin/striptracks.sh -d :eng:und :eng
+. /usr/local/bin/striptracks.sh -d --audio :eng:und:any+d --subs :eng:any+f
diff --git a/root/usr/local/bin/striptracks-eng-fre.sh b/root/usr/local/bin/striptracks-eng-fre.sh
index 47030e3..7acca83 100755
--- a/root/usr/local/bin/striptracks-eng-fre.sh
+++ b/root/usr/local/bin/striptracks-eng-fre.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-. /usr/local/bin/striptracks.sh :eng:fre:und :eng:fre
+. /usr/local/bin/striptracks.sh --audio :eng:fre:und --subs :eng:fre
diff --git a/root/usr/local/bin/striptracks-eng-jpn.sh b/root/usr/local/bin/striptracks-eng-jpn.sh
index ea37f5d..9e9268c 100755
--- a/root/usr/local/bin/striptracks-eng-jpn.sh
+++ b/root/usr/local/bin/striptracks-eng-jpn.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-. /usr/local/bin/striptracks.sh :eng:jpn:und :eng
+. /usr/local/bin/striptracks.sh --audio :eng:jpn:und --subs :eng
diff --git a/root/usr/local/bin/striptracks-eng.sh b/root/usr/local/bin/striptracks-eng.sh
index a0eb344..c499f2a 100755
--- a/root/usr/local/bin/striptracks-eng.sh
+++ b/root/usr/local/bin/striptracks-eng.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-. /usr/local/bin/striptracks.sh :eng:und :eng
+. /usr/local/bin/striptracks.sh --audio :eng:und --subs :eng
diff --git a/root/usr/local/bin/striptracks-fre-debug.sh b/root/usr/local/bin/striptracks-fre-debug.sh
deleted file mode 100755
index 8c3b765..0000000
--- a/root/usr/local/bin/striptracks-fre-debug.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-. /usr/local/bin/striptracks.sh -d :fre:fra:und :fre:fra
diff --git a/root/usr/local/bin/striptracks-fre.sh b/root/usr/local/bin/striptracks-fre.sh
index 832339b..717a3b7 100755
--- a/root/usr/local/bin/striptracks-fre.sh
+++ b/root/usr/local/bin/striptracks-fre.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-. /usr/local/bin/striptracks.sh :fre:fra:und :fre:fra
+. /usr/local/bin/striptracks.sh --audio :fre:fra:und --subs :fre:fra
diff --git a/root/usr/local/bin/striptracks-ger.sh b/root/usr/local/bin/striptracks-ger.sh
index 00b99fe..2244030 100755
--- a/root/usr/local/bin/striptracks-ger.sh
+++ b/root/usr/local/bin/striptracks-ger.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-. /usr/local/bin/striptracks.sh :ger:deu:und :ger:deu
+. /usr/local/bin/striptracks.sh --audio :ger:deu:und --subs :ger:deu
diff --git a/root/usr/local/bin/striptracks-org-eng.sh b/root/usr/local/bin/striptracks-org-eng.sh
index 364dbd3..6718f68 100755
--- a/root/usr/local/bin/striptracks-org-eng.sh
+++ b/root/usr/local/bin/striptracks-org-eng.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-. /usr/local/bin/striptracks.sh --audio :org:eng:und --subs :org:eng
+. /usr/local/bin/striptracks.sh --audio :org:eng:und:any+d --subs :org:eng:any+f
diff --git a/root/usr/local/bin/striptracks-spa.sh b/root/usr/local/bin/striptracks-spa.sh
index 2464f25..ae4e244 100755
--- a/root/usr/local/bin/striptracks-spa.sh
+++ b/root/usr/local/bin/striptracks-spa.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-. /usr/local/bin/striptracks.sh :spa:und :spa
+. /usr/local/bin/striptracks.sh --audio :spa:und --subs :spa
diff --git a/root/usr/local/bin/striptracks.sh b/root/usr/local/bin/striptracks.sh
index 82d1c75..2090317 100755
--- a/root/usr/local/bin/striptracks.sh
+++ b/root/usr/local/bin/striptracks.sh
@@ -34,16 +34,16 @@
# 1 - no video file specified on command line
# 2 - no audio language specified on command line
# 3 - no subtitles language specified on command line
-# 4 - mkvmerge or mkvpropedit not found
+# 4 - mkvmerge, mkvpropedit, or jq not found
# 5 - input video file not found
# 6 - unable to rename temp video to MKV
# 7 - unknown eventtype environment variable
# 8 - unsupported Radarr/Sonarr version (v2)
-# 9 - mkvmerge get media info failed
+# 9 - mkvmerge get media info produced an error or warning
# 10 - remuxing completed, but no output file found
-# 11 - source video had no audio or subtitle tracks
+# 11 - source video had no audio tracks
# 12 - log file is not writable
-# 13 - awk script exited abnormally
+# 13 - mkvmerge or mkvpropedit exited with an error
# 15 - could not set permissions and/or owner on new file
# 16 - could not delete the original file
# 17 - Radarr/Sonarr API error
@@ -62,10 +62,13 @@ export striptracks_debug=0
# Presence of '*_eventtype' variable sets script mode
export striptracks_type=$(printenv | sed -n 's/_eventtype *=.*$//p')
-# Usage function
+# Usage functions
function usage {
- usage="
-$striptracks_script Version: $striptracks_ver
+ usage="Try '$striptracks_script --help' for more information."
+ echo "$usage" >&2
+}
+function long_usage {
+ usage="$striptracks_script Version: $striptracks_ver
Video remuxing script that only keeps tracks with the specified languages.
Designed for use with Radarr and Sonarr, but may be used standalone in batch
mode.
@@ -73,7 +76,7 @@ mode.
Source: https://github.com/TheCaptain989/radarr-striptracks
Usage:
- $0 [{-a|--audio} [{-s|--subs} ] [{-f|--file} ]] [{-l|--log} ] [{-d|--debug} []]
+ $0 [{-a|--audio} [{-s|--subs} ] [--reorder] [{-f|--file} ]] [{-l|--log} ] [{-c|--config} ] [{-d|--debug} []]
Options can also be set via the STRIPTRACKS_ARGS environment variable.
Command-line arguments override the environment variable.
@@ -82,9 +85,19 @@ Options and Arguments:
-a, --audio Audio languages to keep
ISO639-2 code(s) prefixed with a colon \`:\`
multiple codes may be concatenated.
+ Each code may optionally be followed by a
+ plus \`+\` and one or more modifiers.
-s, --subs Subtitles languages to keep
ISO639-2 code(s) prefixed with a colon \`:\`
multiple codes may be concatenated.
+ Each code may optionally be followed by a
+ plus \`+\` and one or more modifiers.
+ --reorder Reorder audio and subtitles tracks to match
+ the language code order specified in the
+ and
+ arguments.
+ Track reorder is skipped if no tracks are
+ removed.
-f, --file If included, the script enters batch mode
and converts the specified video file.
WARNING: Do not use this argument when called
@@ -105,6 +118,9 @@ audio or subtitle languages configured in the Radarr or Sonarr profile. When
used on the command line, they override the detected codes. They are also
accepted as positional parameters for backwards compatibility.
+Language modifiers may be \`f\` or \`d\` which select Forced or Default tracks
+respectively, or a number which specifies the maximum tracks to keep.
+
Batch Mode:
In batch mode the script acts as if it were not called from within Radarr
or Sonarr. It converts the file specified on the command line and ignores
@@ -115,11 +131,11 @@ Examples:
$striptracks_script -d 2 # Enable debugging level 2, audio and
# subtitles languages detected from
# Radarr/Sonarr
- $striptracks_script -a :eng:und -s :eng # keep English and Unknown audio and
+ $striptracks_script -a :eng:und -s :eng # Keep English and Unknown audio and
# English subtitles
- $striptracks_script -a :eng:org -s :eng # keep English and Original audio and
- # English subtitles
- $striptracks_script :eng \"\" # keep English audio and no subtitles
+ $striptracks_script -a :eng:org -s :any+f:eng # Keep English and Original audio,
+ # and forced or English subtitles
+ $striptracks_script -a :eng -s \"\" # Keep English audio and no subtitles
$striptracks_script -d :eng:kor:jpn :eng:spa # Enable debugging level 1, keeping
# English, Korean, and Japanese
# audio, and English and Spanish
@@ -130,8 +146,13 @@ Examples:
# English subtitles, converting video
# specified
$striptracks_script -a :any -s \"\" # Keep all audio and no subtitles
+ $striptracks_script -a :org:any+d1 -s :eng+1:any+f2
+ # Keep all Original and one default
+ # audio in any language, and one
+ # English and two forced subtitles
+ # in any language
"
- echo "$usage" >&2
+ echo "$usage"
}
# Log command-line arguments
@@ -151,7 +172,7 @@ if [ -n "$STRIPTRACKS_ARGS" ]; then
fi
# Process arguments
-# Taken from Drew Strokes post 3/24/2015:
+# Taken from Drew Stokes post 3/24/2015:
# https://medium.com/@Drew_Stokes/bash-argument-parsing-54f3b81a6a8f
unset striptracks_pos_params
while (( "$#" )); do
@@ -175,8 +196,8 @@ while (( "$#" )); do
exit 1
fi
;;
- --help ) # Display usage
- usage
+ --help ) # Display full usage
+ long_usage
exit 0
;;
--version ) # Display version
@@ -196,24 +217,30 @@ while (( "$#" )); do
fi
;;
-a|--audio ) # Audio languages to keep
- if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
- export striptracks_audiokeep="$2"
- shift 2
- else
+ if [ -z "$2" ] || [ ${2:0:1} = "-" ]; then
echo "Error|Invalid option: $1 requires an argument." >&2
usage
exit 2
+ elif [[ "$2" != :* ]]; then
+ echo "Error|Invalid option: $1 argument requires a colon." >&2
+ usage
+ exit 2
fi
+ export striptracks_audiokeep="$2"
+ shift 2
;;
- -s|--subs ) # Subtitles languages to keep
- if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
- export striptracks_subskeep="$2"
- shift 2
- else
+ -s|--subs|--subtitles ) # Subtitles languages to keep
+ if [ -z "$2" ] || [ ${2:0:1} = "-" ]; then
echo "Error|Invalid option: $1 requires an argument." >&2
usage
exit 3
+ elif [[ "$2" != :* ]]; then
+ echo "Error|Invalid option: $1 argument requires a colon." >&2
+ usage
+ exit 3
fi
+ export striptracks_subskeep="$2"
+ shift 2
;;
-c|--config ) # *arr XML configuration file
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
@@ -226,6 +253,10 @@ while (( "$#" )); do
exit 1
fi
;;
+ --reorder ) # Reorder audio and subtitles tracks
+ export striptracks_reorder="true"
+ shift
+ ;;
-*) # Unknown option
echo "Error|Unknown option: $1" >&2
usage
@@ -306,7 +337,8 @@ elif [[ "${striptracks_type,,}" = "sonarr" ]]; then
# export striptracks_sonarr_json=" \"episodeIds\":[.episodes[].id],"
else
# Called in an unexpected way
- echo -e "Error|Unknown or missing '*_eventtype' environment variable: ${striptracks_type}\nNot called from Radarr/Sonarr.\nTry using Batch Mode option: -f " >&2
+ echo -e "Error|Unknown or missing '*_eventtype' environment variable: ${striptracks_type}\nNot calling from Radarr/Sonarr? Try using Batch Mode option: -f " >&2
+ usage
exit 7
fi
export striptracks_rescan_api="Rescan${striptracks_video_type^}"
@@ -314,7 +346,7 @@ export striptracks_eventtype="${striptracks_type,,}_eventtype"
export striptracks_newvideo="${striptracks_video%.*}.mkv"
# If this were defined directly in Radarr or Sonarr this would not be needed here
# shellcheck disable=SC2089
-striptracks_isocodemap='{"languages":[{"language":{"name":"Any","iso639-2":["any"]}},{"language":{"name":"Arabic","iso639-2":["ara"]}},{"language":{"name":"Bengali","iso639-2":["ben"]}},{"language":{"name":"Bosnian","iso639-2":["bos"]}},{"language":{"name":"Bulgarian","iso639-2":["bul"]}},{"language":{"name":"Catalan","iso639-2":["cat"]}},{"language":{"name":"Chinese","iso639-2":["zho","chi"]}},{"language":{"name":"Croatian","iso639-2":["hrv"]}},{"language":{"name":"Czech","iso639-2":["ces","cze"]}},{"language":{"name":"Danish","iso639-2":["dan"]}},{"language":{"name":"Dutch","iso639-2":["nld","dut"]}},{"language":{"name":"English","iso639-2":["eng"]}},{"language":{"name":"Estonian","iso639-2":["est"]}},{"language":{"name":"Finnish","iso639-2":["fin"]}},{"language":{"name":"Flemish","iso639-2":["nld","dut"]}},{"language":{"name":"French","iso639-2":["fra","fre"]}},{"language":{"name":"German","iso639-2":["deu","ger"]}},{"language":{"name":"Greek","iso639-2":["ell","gre"]}},{"language":{"name":"Hebrew","iso639-2":["heb"]}},{"language":{"name":"Hindi","iso639-2":["hin"]}},{"language":{"name":"Hungarian","iso639-2":["hun"]}},{"language":{"name":"Icelandic","iso639-2":["isl","ice"]}},{"language":{"name":"Indonesian","iso639-2":["ind"]}},{"language":{"name":"Italian","iso639-2":["ita"]}},{"language":{"name":"Japanese","iso639-2":["jpn"]}},{"language":{"name":"Kannada","iso639-2":["kan"]}},{"language":{"name":"Korean","iso639-2":["kor"]}},{"language":{"name":"Latvian","iso639-2":["lav"]}},{"language":{"name":"Lithuanian","iso639-2":["lit"]}},{"language":{"name":"Macedonian","iso639-2":["mac","mkd"]}},{"language":{"name":"Malayalam","iso639-2":["mal"]}},{"language":{"name":"Norwegian","iso639-2":["nno","nob","nor"]}},{"language":{"name":"Persian","iso639-2":["fas","per"]}},{"language":{"name":"Polish","iso639-2":["pol"]}},{"language":{"name":"Portuguese","iso639-2":["por"]}},{"language":{"name":"Portuguese (Brazil)","iso639-2":["por"]}},{"language":{"name":"Romanian","iso639-2":["rum","ron"]}},{"language":{"name":"Russian","iso639-2":["rus"]}},{"language":{"name":"Serbian","iso639-2":["srp"]}},{"language":{"name":"Slovak","iso639-2":["slk","slo"]}},{"language":{"name":"Slovenian","iso639-2":["slv"]}},{"language":{"name":"Spanish","iso639-2":["spa"]}},{"language":{"name":"Spanish (Latino)","iso639-2":["spa"]}},{"language":{"name":"Swedish","iso639-2":["swe"]}},{"language":{"name":"Tamil","iso639-2":["tam"]}},{"language":{"name":"Telugu","iso639-2":["tel"]}},{"language":{"name":"Thai","iso639-2":["tha"]}},{"language":{"name":"Turkish","iso639-2":["tur"]}},{"language":{"name":"Ukrainian","iso639-2":["ukr"]}},{"language":{"name":"Vietnamese","iso639-2":["vie"]}},{"language":{"name":"Unknown","iso639-2":["und"]}}]}'
+striptracks_isocodemap='{"languages":[{"language":{"name":"Any","iso639-2":["any"]}},{"language":{"name":"Afrikaans","iso639-2":["afr"]}},{"language":{"name":"Albanian","iso639-2":["sqi","alb"]}},{"language":{"name":"Arabic","iso639-2":["ara"]}},{"language":{"name":"Bengali","iso639-2":["ben"]}},{"language":{"name":"Bosnian","iso639-2":["bos"]}},{"language":{"name":"Bulgarian","iso639-2":["bul"]}},{"language":{"name":"Catalan","iso639-2":["cat"]}},{"language":{"name":"Chinese","iso639-2":["zho","chi"]}},{"language":{"name":"Croatian","iso639-2":["hrv"]}},{"language":{"name":"Czech","iso639-2":["ces","cze"]}},{"language":{"name":"Danish","iso639-2":["dan"]}},{"language":{"name":"Dutch","iso639-2":["nld","dut"]}},{"language":{"name":"English","iso639-2":["eng"]}},{"language":{"name":"Estonian","iso639-2":["est"]}},{"language":{"name":"Finnish","iso639-2":["fin"]}},{"language":{"name":"Flemish","iso639-2":["nld","dut"]}},{"language":{"name":"French","iso639-2":["fra","fre"]}},{"language":{"name":"German","iso639-2":["deu","ger"]}},{"language":{"name":"Greek","iso639-2":["ell","gre"]}},{"language":{"name":"Hebrew","iso639-2":["heb"]}},{"language":{"name":"Hindi","iso639-2":["hin"]}},{"language":{"name":"Hungarian","iso639-2":["hun"]}},{"language":{"name":"Icelandic","iso639-2":["isl","ice"]}},{"language":{"name":"Indonesian","iso639-2":["ind"]}},{"language":{"name":"Italian","iso639-2":["ita"]}},{"language":{"name":"Japanese","iso639-2":["jpn"]}},{"language":{"name":"Kannada","iso639-2":["kan"]}},{"language":{"name":"Korean","iso639-2":["kor"]}},{"language":{"name":"Latvian","iso639-2":["lav"]}},{"language":{"name":"Lithuanian","iso639-2":["lit"]}},{"language":{"name":"Macedonian","iso639-2":["mac","mkd"]}},{"language":{"name":"Malayalam","iso639-2":["mal"]}},{"language":{"name":"Marathi","iso639-2":["mar"]}},{"language":{"name":"Norwegian","iso639-2":["nno","nob","nor"]}},{"language":{"name":"Persian","iso639-2":["fas","per"]}},{"language":{"name":"Polish","iso639-2":["pol"]}},{"language":{"name":"Portuguese","iso639-2":["por"]}},{"language":{"name":"Portuguese (Brazil)","iso639-2":["por"]}},{"language":{"name":"Romanian","iso639-2":["rum","ron"]}},{"language":{"name":"Russian","iso639-2":["rus"]}},{"language":{"name":"Serbian","iso639-2":["srp"]}},{"language":{"name":"Slovak","iso639-2":["slk","slo"]}},{"language":{"name":"Slovenian","iso639-2":["slv"]}},{"language":{"name":"Spanish","iso639-2":["spa"]}},{"language":{"name":"Spanish (Latino)","iso639-2":["spa"]}},{"language":{"name":"Swedish","iso639-2":["swe"]}},{"language":{"name":"Tagalog","iso639-2":["tgl"]}},{"language":{"name":"Tamil","iso639-2":["tam"]}},{"language":{"name":"Telugu","iso639-2":["tel"]}},{"language":{"name":"Thai","iso639-2":["tha"]}},{"language":{"name":"Turkish","iso639-2":["tur"]}},{"language":{"name":"Ukrainian","iso639-2":["ukr"]}},{"language":{"name":"Vietnamese","iso639-2":["vie"]}},{"language":{"name":"Unknown","iso639-2":["und"]}}]}'
### Functions
@@ -351,7 +383,7 @@ function get_version {
-H "Accept: application/json" \
--get "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
@@ -373,7 +405,7 @@ function get_video_info {
-H "Accept: application/json" \
--get "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
@@ -393,9 +425,9 @@ function get_videofile_info {
striptracks_result=$(curl -s --fail-with-body -H "X-Api-Key: $striptracks_apikey" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
- --get "$url" )
+ --get "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
@@ -422,13 +454,13 @@ function rescan {
-d "$data" \
"$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
[ $striptracks_debug -ge 2 ] && echo "API returned: $striptracks_result" | awk '{print "Debug|"$0}' | log
# Exit loop if database is not locked, else wait 1 minute
- if [[ ! "$(echo $striptracks_result | jq -jcrM .message?)" =~ database\ is\ locked ]]; then
+ if [[ ! "$(echo $striptracks_result | jq -jcM .message?)" =~ database\ is\ locked ]]; then
break
else
echo "Warn|Database is locked; system is likely overloaded. Sleeping 1 minute." | log
@@ -461,7 +493,7 @@ function check_job {
-H "Accept: application/json" \
--get "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
local striptracks_return=10
@@ -500,7 +532,7 @@ function get_profiles {
-H "Accept: application/json" \
--get "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
@@ -527,7 +559,7 @@ function get_language_codes {
-H "Accept: application/json" \
--get "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
@@ -551,7 +583,7 @@ function get_custom_formats {
-H "Accept: application/json" \
--get "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
@@ -577,13 +609,13 @@ function delete_video {
-H "Accept: application/json" \
-X DELETE "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
[ $striptracks_debug -ge 2 ] && echo "API returned: $striptracks_result" | awk '{print "Debug|"$0}' | log
# Exit loop if database is not locked, else wait 1 minute
- if [[ ! "$(echo $striptracks_result | jq -jcrM .message?)" =~ database\ is\ locked ]]; then
+ if [[ ! "$(echo $striptracks_result | jq -jcM .message?)" =~ database\ is\ locked ]]; then
break
else
echo "Warn|Database is locked; system is likely overloaded. Sleeping 1 minute." | log
@@ -614,7 +646,7 @@ function delete_video {
# -d "filterExistingFiles=false" \
# --get "$url")
# local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- # local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url?${temp_id:+$temp_id&}folder=$striptracks_video_folder&filterExistingFiles=false\"\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ # local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url?${temp_id:+$temp_id&}folder=$striptracks_video_folder&filterExistingFiles=false\"\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
# echo "$striptracks_message" | log
# echo "$striptracks_message" >&2
# }
@@ -641,14 +673,14 @@ function set_metadata {
-d "$data" \
-X PUT "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
[ $striptracks_debug -ge 2 ] && echo "Debug|API returned ${#striptracks_result} bytes." | log
[ $striptracks_debug -ge 3 ] && echo "API returned: $striptracks_result" | awk '{print "Debug|"$0}' | log
# Exit loop if database is not locked, else wait 1 minute
- if [[ ! "$(echo $striptracks_result | jq -jcrM .message?)" =~ database\ is\ locked ]]; then
+ if [[ ! "$(echo $striptracks_result | jq -jcM .message?)" =~ database\ is\ locked ]]; then
break
else
echo "Warn|Database is locked; system is likely overloaded. Sleeping 1 minute." | log
@@ -662,22 +694,32 @@ function set_metadata {
fi
return $striptracks_return
}
-# Read in the output of mkvmerge info extraction
+# Read in the output of mkvmerge info extraction (see issue #87)
function get_mediainfo {
[ $striptracks_debug -ge 1 ] && echo "Debug|Executing: /usr/bin/mkvmerge -J \"$1\"" | log
unset striptracks_json
- striptracks_json=$(/usr/bin/mkvmerge -J "$1" 2>&1)
- local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message="Error|[$striptracks_curlret] Error executing mkvmerge. It returned: $striptracks_json"
- echo "$striptracks_message" | log
- echo "$striptracks_message" >&2
- }
+ striptracks_json=$(/usr/bin/mkvmerge -J "$1")
+ local striptracks_return=$?
[ $striptracks_debug -ge 2 ] && echo "mkvmerge returned: $striptracks_json" | awk '{print "Debug|"$0}' | log
- if [ "$(echo $striptracks_json | jq -crM '.container.supported')" = "true" ]; then
- local striptracks_return=0
- else
- local striptracks_return=1
- fi
+ case $striptracks_return in
+ 0)
+ # Check for unsupported container.
+ if [ "$(echo "$striptracks_json" | jq -crM '.container.supported')" = "false" ]; then
+ striptracks_message="Error|Video format for '$1' is unsupported. Unable to continue. mkvmerge returned container info: $(echo $striptracks_json | jq -crM .container)"
+ echo "$striptracks_message" | log
+ echo "$striptracks_message" >&2
+ end_script 9
+ fi
+ ;;
+ 1) striptracks_message=$(echo -e "[$striptracks_return] Warning when inspecting video.\nmkvmerge returned: $(echo "$striptracks_json" | jq -crM '.warnings[]')" | awk '{print "Warn|"$0}')
+ echo "$striptracks_message" | log
+ ;;
+ 2) striptracks_message=$(echo -e "[$striptracks_return] Error when inspecting video.\nmkvmerge returned: $(echo "$striptracks_json" | jq -crM '.errors[]')" | awk '{print "Error|"$0}')
+ echo "$striptracks_message" | log
+ echo "$striptracks_message" >&2
+ end_script 9
+ ;;
+ esac
return $striptracks_return
}
# # Import new video into Radarr/Sonarr
@@ -693,7 +735,7 @@ function get_mediainfo {
# -d "$data" \
# "$url")
# local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- # local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ # local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
# echo "$striptracks_message" | log
# echo "$striptracks_message" >&2
# }
@@ -717,7 +759,7 @@ function get_rename {
-d "$data" \
--get "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url&$data\"\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url&$data\"\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
@@ -742,7 +784,7 @@ function rename_video {
-d "$data" \
"$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
@@ -766,7 +808,7 @@ function set_radarr_language {
-d "$data" \
-X PUT "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
@@ -790,7 +832,7 @@ function set_sonarr_language {
-d "$data" \
-X PUT "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
@@ -811,23 +853,30 @@ function check_compat {
[ ${striptracks_arr_version/.*/} -ge 3 ] && local striptracks_return=0
;;
languageprofile)
+ # Langauge Profiles
[ "${striptracks_type,,}" = "sonarr" ] && [ ${striptracks_arr_version/.*/} -eq 3 ] && local striptracks_return=0
;;
customformat)
+ # Language option in Custom Formats
[ "${striptracks_type,,}" = "radarr" ] && [ ${striptracks_arr_version/.*/} -ge 3 ] && local striptracks_return=0
[ "${striptracks_type,,}" = "sonarr" ] && [ ${striptracks_arr_version/.*/} -ge 4 ] && local striptracks_return=0
;;
originallanguage)
+ # Original language selection
[ "${striptracks_type,,}" = "radarr" ] && [ ${striptracks_arr_version/.*/} -ge 3 ] && local striptracks_return=0
[ "${striptracks_type,,}" = "sonarr" ] && [ ${striptracks_arr_version/.*/} -ge 4 ] && local striptracks_return=0
;;
+ qualitylanguage)
+ # Language option in Quality Profile
+ [ "${striptracks_type,,}" = "radarr" ] && [ ${striptracks_arr_version/.*/} -ge 3 ] && local striptracks_return=0
+ ;;
*) # Unknown feature
local striptracks_message="Error|Unknown feature $1 in ${striptracks_type^}"
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
;;
esac
- [ $striptracks_debug -ge 1 ] && echo "Debug|Feature $1 is $([ $striptracks_return -eq 1 ] && echo "not ")compatible with ${striptracks_type^} v${striptracks_arr_version}." | log
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Feature $1 is $([ $striptracks_return -eq 1 ] && echo "not ")compatible with ${striptracks_type^} v${striptracks_arr_version}" | log
return $striptracks_return
}
# Get media management configuration
@@ -840,7 +889,7 @@ function get_media_config {
-H "Accept: application/json" \
--get "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\"\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
@@ -855,10 +904,10 @@ function get_media_config {
# Update file metadata in Radarr/Sonarr
function set_video_info {
local url="$striptracks_api_url/$striptracks_video_api/$striptracks_video_id"
- local data="$(echo $striptracks_videoinfo | jq -crM .monitored='true')"
+ local data="$(echo $striptracks_videoinfo | jq -crM .monitored="$striptracks_videomonitored")"
local i=0
for ((i=1; i <= 5; i++)); do
- [ $striptracks_debug -ge 1 ] && echo "Debug|Updating monitored to 'true'. Calling ${striptracks_type^} API using PUT and URL '$url' with data $data" | log
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Updating monitored to '$striptracks_videomonitored'. Calling ${striptracks_type^} API using PUT and URL '$url' with data $data" | log
unset striptracks_result
striptracks_result=$(curl -s --fail-with-body -H "X-Api-Key: $striptracks_apikey" \
-H "Content-Type: application/json" \
@@ -866,14 +915,14 @@ function set_video_info {
-d "$data" \
-X PUT "$url")
local striptracks_curlret=$?; [ $striptracks_curlret -ne 0 ] && {
- local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcrM .message?)" | awk '{print "Error|"$0}')
+ local striptracks_message=$(echo -e "[$striptracks_curlret] curl error when calling: \"$url\" with data $data\nWeb server returned: $(echo $striptracks_result | jq -jcM .message?)" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
}
[ $striptracks_debug -ge 2 ] && echo "Debug|API returned ${#striptracks_result} bytes." | log
[ $striptracks_debug -ge 3 ] && echo "API returned: $striptracks_result" | awk '{print "Debug|"$0}' | log
# Exit loop if database is not locked, else wait 1 minute
- if [[ ! "$(echo $striptracks_result | jq -jcrM .message?)" =~ database\ is\ locked ]]; then
+ if [[ ! "$(echo $striptracks_result | jq -jcM .message?)" =~ database\ is\ locked ]]; then
break
else
echo "Warn|Database is locked; system is likely overloaded. Sleeping 1 minute." | log
@@ -887,6 +936,30 @@ function set_video_info {
fi
return $striptracks_return
}
+# Handle :org language code
+function process_org_code {
+ local striptracks_track_type="$1" # 'audio' or 'subtitles'
+ local striptracks_keep_var="$2" # Variable name, e.g., striptracks_audiokeep or striptracks_subskeep
+
+ if [[ "${!striptracks_keep_var}" =~ :org ]]; then
+ # Check compatibility
+ if [ "${striptracks_type,,}" = "batch" ]; then
+ local striptracks_message="Warn|${striptracks_track_type^} argument contains ':org' code, but this is undefined for Batch mode! Unexpected behavior may result."
+ echo "$striptracks_message" | log
+ echo "$striptracks_message" >&2
+ elif ! check_compat originallanguage; then
+ local striptracks_message="Warn|${striptracks_track_type^} argument contains ':org' code, but this is undefined and not compatible with this mode/version! Unexpected behavior may result."
+ echo "$striptracks_message" | log
+ echo "$striptracks_message" >&2
+ fi
+
+ # Log debug message if applicable
+ [ "$striptracks_debug" -ge 1 ] && echo "Debug|${striptracks_track_type^} argument ':org' specified. Changing '${!striptracks_keep_var}' to '${!striptracks_keep_var//:org/${striptracks_originalLangCode}}'" | log
+
+ # Replace :org with the original language code
+ declare -g "$striptracks_keep_var=${!striptracks_keep_var//:org/${striptracks_originalLangCode}}"
+ fi
+}
# Exit program
function end_script {
# Cool bash feature
@@ -907,7 +980,7 @@ fi
# Check that the log file exists
if [ ! -f "$striptracks_log" ]; then
echo "Info|Creating a new log file: $striptracks_log"
- touch "$striptracks_log" 2>&1
+ touch "$striptracks_log"
fi
# Check that the log file is writable
@@ -977,7 +1050,7 @@ if [[ "${!striptracks_eventtype}" = "Test" ]]; then
end_script 0
fi
-# First normal log entry (when there are no errors)
+# First normal log entry (when there are no errors) (see issue #61)
# shellcheck disable=SC2046
striptracks_filesize=$(stat -c %s "${striptracks_video}" | numfmt --to iec --format "%.3f")
striptracks_message="Info|${striptracks_type^} event: ${!striptracks_eventtype}, Video: $striptracks_video, Size: $striptracks_filesize"
@@ -1019,10 +1092,10 @@ elif [ -f "$striptracks_arr_config" ]; then
# Check for localhost
[[ $striptracks_bindaddress = "*" ]] && striptracks_bindaddress=localhost
- # Strip leading and trailing forward slashes from URL base
+ # Strip leading and trailing forward slashes from URL base (see issue #66)
striptracks_urlbase="$(echo "$striptracks_urlbase" | sed -re 's/^\/+//; s/\/+$//')"
- # Build URL to Radarr/Sonarr API
+ # Build URL to Radarr/Sonarr API (see issue #57)
striptracks_api_url="http://$striptracks_bindaddress:$striptracks_port${striptracks_urlbase:+/$striptracks_urlbase}/api/v3"
# Check Radarr/Sonarr version
@@ -1054,7 +1127,7 @@ fi
# Check if video file variable is blank
if [ -z "$striptracks_video" ]; then
- striptracks_message="Error|No video file detected! radarr_moviefile_path or sonarr_episodefile_path environment variable missing and -f option not specified on command line."
+ striptracks_message="Error|No video file found! radarr_moviefile_path or sonarr_episodefile_path environment variable missing and -f option not specified on command line."
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
usage
@@ -1063,7 +1136,7 @@ fi
# Check if source video exists
if [ ! -f "$striptracks_video" ]; then
- striptracks_message="Error|Input file not found: \"$striptracks_video\""
+ striptracks_message="Error|Input video file not found: \"$striptracks_video\""
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
end_script 5
@@ -1088,6 +1161,7 @@ elif [ -n "$striptracks_api_url" ]; then
# Get video profile
if get_video_info; then
striptracks_videoinfo="$striptracks_result"
+ striptracks_videomonitored="$(echo "$striptracks_videoinfo" | jq -crM ".monitored")"
# This is not strictly necessary as this is normally set in the environment. However, this is needed for testing scripts and it doesn't hurt to use the data returned by the API call.
striptracks_videofile_id="$(echo $striptracks_videoinfo | jq -crM .${striptracks_json_quality_root}.id)"
@@ -1101,14 +1175,14 @@ elif [ -n "$striptracks_api_url" ]; then
# Save original metadata
striptracks_original_metadata="$(echo $striptracks_videofile_info | jq -crM '{quality, releaseGroup}')"
- [ $striptracks_debug -ge 1 ] && echo "Debug|Detected video file quality '$(echo $striptracks_original_metadata | jq -crM .quality.quality.name)' and release group '$(echo $striptracks_original_metadata | jq -crM '.releaseGroup | select(. != null)')'" | log
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Found video file quality '$(echo $striptracks_original_metadata | jq -crM .quality.quality.name)' and release group '$(echo $striptracks_original_metadata | jq -crM '.releaseGroup | select(. != null)')'" | log
# Get language name(s) from quality profile used by video
striptracks_profileId="$(echo $striptracks_videoinfo | jq -crM ${striptracks_video_rootNode}.qualityProfileId)"
striptracks_profileName="$(echo $striptracks_qualityProfiles | jq -crM ".[] | select(.id == $striptracks_profileId).name")"
striptracks_profileLanguages="$(echo $striptracks_qualityProfiles | jq -cM "[.[] | select(.id == $striptracks_profileId) | .language]")"
striptracks_languageSource="quality profile"
- [ $striptracks_debug -ge 1 ] && echo "Debug|Detected quality profile '(${striptracks_profileId}) ${striptracks_profileName}' with language '$(echo $striptracks_profileLanguages | jq -crM '[.[] | "(\(.id | tostring)) \(.name)"] | join(",")')'" | log
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Found quality profile '${striptracks_profileName} (${striptracks_profileId})'$(check_compat qualitylanguage && echo " with language '$(echo $striptracks_profileLanguages | jq -crM '[.[] | "\(.name) (\(.id | tostring))"] | join(",")')'")" | log
# Query custom formats if returned language from quality profile is null or -1 (Any)
if [ -z "$striptracks_profileLanguages" -o "$striptracks_profileLanguages" = "[null]" -o "$(echo $striptracks_profileLanguages | jq -crM '.[].id')" = "-1" ] && check_compat customformat; then
@@ -1121,35 +1195,36 @@ elif [ -n "$striptracks_api_url" ]; then
# Pick our languages by combining data from quality profile and custom format configuration.
# I'm open to suggestions if there's a better way to get this list or selected languages.
# Did I mention that JQ is crazy hard?
- striptracks_qcf_langcodes=$(echo "$striptracks_qualityProfiles $striptracks_customFormats" | jq -s -crM "
+ striptracks_qcf_langcodes=$(echo "$striptracks_qualityProfiles $striptracks_customFormats" | jq -s -crM --argjson ProfileId $striptracks_profileId '
[
# This combines the custom formats [1] with the quality profiles [0], iterating over custom formats that
# specify languages and evaluating the scoring from the selected quality profile.
(
.[1] | .[] |
- {id, specs: [.specifications[] | select(.implementation == \"LanguageSpecification\") | {langCode: .fields[] | select(.name == \"value\").value, negate, except: ((.fields[] | select(.name == \"exceptLanguage\").value) // false)}]}
- ) as \$cf |
+ {id, specs: [.specifications[] | select(.implementation == "LanguageSpecification") | {langCode: .fields[] | select(.name == "value").value, negate, except: ((.fields[] | select(.name == "exceptLanguage").value) // false)}]}
+ ) as $CustomFormat |
.[0] | .[] |
- select(.id == $striptracks_profileId) | .formatItems[] | select(.format == \$cf.id) |
- {format, name, score, specs: \$cf.specs}
+ select(.id == $ProfileId) | .formatItems[] | select(.format == $CustomFormat.id) |
+ {format, name, score, specs: $CustomFormat.specs}
] |
[
# Only count languages with positive scores plus languages with negative scores that are negated, and
# languages with negative scores that use Except
.[] |
- (select(.score > 0) | .specs[] | select(.negate == false and .except == false)), (select(.score < 0) | .specs[] | select(.negate == true and .except == false)), (select(.score < 0) | .specs[] | select(.negate == false and .except == true)) |
+ (select(.score > 0) | .specs[] | select(.negate == false and .except == false)),
+ (select(.score < 0) | .specs[] | select(.negate == true and .except == false)),
+ (select(.score < 0) | .specs[] | select(.negate == false and .except == true)) |
.langCode
] |
- unique |
- join(\",\")
- ")
+ unique | join(",")
+ ')
[ $striptracks_debug -ge 2 ] && echo "Debug|Custom format language code(s) '$striptracks_qcf_langcodes' were selected based on quality profile scores." | log
if [ -n "$striptracks_qcf_langcodes" ]; then
# Convert the language codes into language code/name pairs
striptracks_profileLanguages="$(echo $striptracks_lang_codes | jq -crM "map(select(.id | inside($striptracks_qcf_langcodes)) | {id, name})")"
striptracks_languageSource="custom format"
- [ $striptracks_debug -ge 1 ] && echo "Debug|Detected custom format language(s) '$(echo $striptracks_profileLanguages | jq -crM '[.[] | "(\(.id | tostring)) \(.name)"] | join(",")')'" | log
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Found custom format language(s) '$(echo $striptracks_profileLanguages | jq -crM '[.[] | "\(.name) (\(.id | tostring))"] | join(",")')'" | log
else
[ $striptracks_debug -ge 1 ] && echo "Debug|None of the applied custom formats have language conditions with usable scores." | log
fi
@@ -1166,7 +1241,7 @@ elif [ -n "$striptracks_api_url" ]; then
striptracks_profileName="$(echo $striptracks_languageProfiles | jq -crM ".[] | select(.id == $striptracks_profileId).name")"
striptracks_profileLanguages="$(echo $striptracks_languageProfiles | jq -cM "[.[] | select(.id == $striptracks_profileId) | .languages[] | select(.allowed).language]")"
striptracks_languageSource="language profile"
- [ $striptracks_debug -ge 1 ] && echo "Debug|Detected language profile '(${striptracks_profileId}) ${striptracks_profileName}' with language(s) '$(echo $striptracks_profileLanguages | jq -crM '[.[].name] | join(",")')'" | log
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Found language profile '(${striptracks_profileId}) ${striptracks_profileName}' with language(s) '$(echo $striptracks_profileLanguages | jq -crM '[.[].name] | join(",")')'" | log
else
# languageProfile API failed
striptracks_message="Warn|The 'languageprofile' API returned an error."
@@ -1185,7 +1260,7 @@ elif [ -n "$striptracks_api_url" ]; then
else
# Final determination of configured languages in profiles or custom formats
striptracks_profileLangNames="$(echo $striptracks_profileLanguages | jq -crM '[.[].name]')"
- [ $striptracks_debug -ge 1 ] && echo "Debug|Determined ${striptracks_type^} configured language(s) of '$(echo $striptracks_profileLanguages | jq -crM '[.[] | "(\(.id | tostring)) \(.name)"] | join(",")')' from $striptracks_languageSource" | log
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Determined ${striptracks_type^} configured language(s) of '$(echo $striptracks_profileLanguages | jq -crM '[.[] | "\(.name) (\(.id | tostring))"] | join(",")')' from $striptracks_languageSource" | log
fi
# Get originalLanguage of video
@@ -1193,8 +1268,8 @@ elif [ -n "$striptracks_api_url" ]; then
striptracks_originalLangName="$(echo $striptracks_videoinfo | jq -crM ${striptracks_video_rootNode}.originalLanguage.name)"
# shellcheck disable=SC2090
- striptracks_originalLangCode="$(echo $striptracks_isocodemap | jq -jcrM ".languages[] | select(.language.name == \"$striptracks_originalLangName\") | .language | \":\(.\"iso639-2\"[])\"")"
- [ $striptracks_debug -ge 1 ] && echo "Debug|Detected original video language of '$striptracks_originalLangName ($striptracks_originalLangCode)' from $striptracks_video_type '$striptracks_rescan_id'" | log
+ striptracks_originalLangCode="$(echo $striptracks_isocodemap | jq -jcM ".languages[] | select(.language.name == \"$striptracks_originalLangName\") | .language | \":\(.\"iso639-2\"[])\"")"
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Found original video language of '$striptracks_originalLangName ($striptracks_originalLangCode)' from $striptracks_video_type '$striptracks_rescan_id'" | log
fi
# Map language names to ISO code(s) used by mkvmerge
@@ -1205,7 +1280,7 @@ elif [ -n "$striptracks_api_url" ]; then
striptracks_templang="$striptracks_originalLangName"
fi
# shellcheck disable=SC2090
- striptracks_profileLangCodes+="$(echo $striptracks_isocodemap | jq -jcrM ".languages[] | select(.language.name == \"$striptracks_templang\") | .language | \":\(.\"iso639-2\"[])\"")"
+ striptracks_profileLangCodes+="$(echo $striptracks_isocodemap | jq -jcM ".languages[] | select(.language.name == \"$striptracks_templang\") | .language | \":\(.\"iso639-2\"[])\"")"
done
[ $striptracks_debug -ge 1 ] && echo "Debug|Mapped $striptracks_languageSource language(s) '$(echo $striptracks_profileLangNames | jq -crM "join(\",\")")' to ISO639-2 code list '$striptracks_profileLangCodes'" | log
else
@@ -1246,7 +1321,6 @@ elif [ -n "$striptracks_api_url" ]; then
striptracks_exitstatus=17
}
if [ "$(echo "$striptracks_result" | jq -crM ".autoUnmonitorPreviouslyDownloaded${striptracks_video_api^}s")" = "true" ]; then
- striptracks_conf_unmonitor=1
striptracks_message="Warn|Will compensate for ${striptracks_type^} configuration to unmonitor deleted ${striptracks_video_api}s."
echo "$striptracks_message" | log
fi
@@ -1259,24 +1333,8 @@ else
fi
# Special handling for ':org' code from command line.
-if [[ "$striptracks_audiokeep" =~ :org ]]; then
- [ $striptracks_debug -ge 1 ] && echo "Debug|Command line ':org' code specified for audio. Changing '${striptracks_audiokeep}' to '${striptracks_audiokeep//:org/${striptracks_originalLangCode}}'" | log
- striptracks_audiokeep="${striptracks_audiokeep//:org/${striptracks_originalLangCode}}"
- if ! check_compat originallanguage; then
- striptracks_message="Warn|:org code specified for audio, but this is undefined and not compatible with this mode/version! Unexpected behavior may result."
- echo "$striptracks_message" | log
- echo "$striptracks_message" >&2
- fi
-fi
-if [[ "$striptracks_subskeep" =~ :org ]]; then
- [ $striptracks_debug -ge 1 ] && echo "Debug|Command line ':org' specified for subtitles. Changing '${striptracks_subskeep}' to '${striptracks_subskeep//:org/${striptracks_originalLangCode}}'" | log
- striptracks_subskeep="${striptracks_subskeep//:org/${striptracks_originalLangCode}}"
- if [ "${striptracks_type,,}" = "batch" ]; then
- striptracks_message="Warn|:org code specified for subtitles, but this is undefined for Batch mode! Unexpected behavior may result."
- echo "$striptracks_message" | log
- echo "$striptracks_message" >&2
- fi
-fi
+process_org_code "audio" "striptracks_audiokeep"
+process_org_code "subtitles" "striptracks_subskeep"
# Final assignment of audio and subtitles selection
## Guard clause
@@ -1295,7 +1353,7 @@ else
[ $striptracks_debug -ge 1 ] && echo "Debug|Using command line audio languages '$striptracks_audiokeep'" | log
fi
-## Guard clause
+## Log configuration that removes all subtitles
if [ -z "$striptracks_subskeep" -a -z "$striptracks_profileLangCodes" ]; then
striptracks_message="Info|No subtitles languages specified or detected. Removing all subtitles found."
echo "$striptracks_message" | log
@@ -1315,184 +1373,273 @@ echo "$striptracks_message" | log
#### BEGIN MAIN
# Read in the output of mkvmerge info extraction
-if get_mediainfo "$striptracks_video"; then
- # This and the modified AWK script are a hack, and I know it. JQ is crazy hard to learn, BTW.
- # Mimic the mkvmerge --identify-verbose option that has been deprecated
- striptracks_json_processed=$(echo $striptracks_json | jq -jcrM '
- ( if (.chapters[] | .num_entries) then
- "Chapters: \(.chapters[] | .num_entries) entries\n"
- else
- empty
- end
- ),
- ( .tracks[] |
- ( "Track ID \(.id): \(.type) (\(.codec)) [",
- ( [.properties | to_entries[] | "\(.key):\(.value | tostring | gsub(" "; "\\s"))"] | join(" ")),
- "]\n"
- )
- )
- ')
- [ $striptracks_debug -ge 1 ] && echo "$striptracks_json_processed" | awk '{print "Debug|"$0}' | log
-else
- # Get media info failed
- if [ "$(echo $striptracks_json | jq -crM '.container.supported')" = "false" ]; then
- striptracks_message="Error|Container format '$(echo $striptracks_json | jq -crM .container.type)' is unsupported by mkvmerge. Unable to continue."
- else
- striptracks_message="Error|mkvmerge error. Unable to continue."
- fi
+# Populates the striptracks_json variable
+get_mediainfo "$striptracks_video"
+
+# Process JSON data from MKVmerge; track selection logic
+striptracks_json_processed=$(echo "$striptracks_json" | jq -jcM --arg AudioKeep "$striptracks_audiokeep" \
+--arg SubsKeep "$striptracks_subskeep" '
+# Parse input string into JSON language rules
+def parse_language_codes(codes):
+ # Supports f, d, and number modifiers (see issues #82 and #86)
+ # -1 default value in language key means to keep unlimited tracks
+ # NOTE: Logic can result in duplicate keys, but jq just uses the last defined key
+ codes | split(":")[1:] | map(split("+") | {lang: .[0], mods: .[1]}) |
+ {languages: map(
+ # Select tracks with no modifiers or only numeric modifiers
+ (select(.mods == null) | {(.lang): -1}),
+ (select(.mods | test("^[0-9]+$")?) | {(.lang): .mods | tonumber})
+ ) | add,
+ forced_languages: map(
+ # Select tracks with f modifier
+ select(.mods | contains("f")?) | {(.lang): ((.mods | scan("[0-9]+") | tonumber) // -1)}
+ ) | add,
+ default_languages: map(
+ # Select tracks with d modifier
+ select(.mods | contains("d")?) | {(.lang): ((.mods | scan("[0-9]+") | tonumber) // -1)}
+ ) | add
+ };
+
+# Language rules for audio and subtitles, adding required audio tracks (see issue #54)
+(parse_language_codes($AudioKeep) | .languages += {"mis":-1,"zxx":-1}) as $AudioRules |
+parse_language_codes($SubsKeep) as $SubsRules |
+
+# Log chapter information
+if (.chapters[0].num_entries) then
+ .striptracks_log = "Info|Chapters: \(.chapters[].num_entries)"
+else . end |
+
+# Process tracks
+reduce .tracks[] as $track (
+ # Create object to hold tracks and counters for each reduce iteration
+ # This is what will be output at the end of the reduce loop
+ {"tracks": [], "counters": {"audio": {"normal": {}, "forced": {}, "default": {}}, "subtitles": {"normal": {}, "forced": {}, "default": {}}}};
+
+ # Set track language to "und" if null or empty
+ # NOTE: The // operator cannot be used here because it checks for null or empty values, not blank strings
+ (if ($track.properties.language == "" or $track.properties.language == null) then "und" else $track.properties.language end) as $track_lang |
+
+ # Initialize counters for each track type and language
+ (.counters[$track.type].normal[$track_lang] //= 0) |
+ if $track.properties.forced_track then (.counters[$track.type].forced[$track_lang] //= 0) else . end |
+ if $track.properties.default_track then (.counters[$track.type].default[$track_lang] //= 0) else . end |
+ .counters[$track.type] as $track_counters |
+
+ # Add tracks one at a time to output object above
+ .tracks += [
+ $track |
+ .striptracks_debug_log = "Debug|Parsing track ID:\(.id) Type:\(.type) Name:\(.properties.track_name) Lang:\($track_lang) Codec:\(.codec) Default:\(.properties.default_track) Forced:\(.properties.forced_track)" |
+ # Use track language evaluation above
+ .properties.language = $track_lang |
+
+ # Determine keep logic based on type and rules
+ if .type == "video" then
+ .striptracks_keep = true
+ elif .type == "audio" or .type == "subtitles" then
+ .striptracks_log = "\(.id): \($track_lang) (\(.codec))\(if .properties.track_name then " \"" + .properties.track_name + "\"" else "" end)" |
+ # Same logic for both audio and subtitles
+ (if .type == "audio" then $AudioRules else $SubsRules end) as $currentRules |
+ if ($currentRules.languages["any"] == -1 or ($track_counters.normal | add) < $currentRules.languages["any"] or
+ $currentRules.languages[$track_lang] == -1 or $track_counters.normal[$track_lang] < $currentRules.languages[$track_lang]) then
+ .striptracks_keep = true
+ elif (.properties.forced_track and
+ ($currentRules.forced_languages["any"] == -1 or ($track_counters.forced | add) < $currentRules.forced_languages["any"] or
+ $currentRules.forced_languages[$track_lang] == -1 or $track_counters.forced[$track_lang] < $currentRules.forced_languages[$track_lang])) then
+ .striptracks_keep = true |
+ .striptracks_rule = "forced"
+ elif (.properties.default_track and
+ ($currentRules.default_languages["any"] == -1 or ($track_counters.default | add) < $currentRules.default_languages["any"] or
+ $currentRules.default_languages[$track_lang] == -1 or $track_counters.default[$track_lang] < $currentRules.default_languages[$track_lang])) then
+ .striptracks_keep = true |
+ .striptracks_rule = "default"
+ else . end |
+ if .striptracks_keep then
+ .striptracks_log = "Info|Keeping \(if .striptracks_rule then .striptracks_rule + " " else "" end)\(.type) track " + .striptracks_log
+ else
+ .striptracks_keep = false
+ end
+ else . end
+ ] |
+
+ # Increment counters for each track type and language
+ .counters[$track.type].normal[$track_lang] +=
+ if .tracks[-1].striptracks_keep then
+ 1
+ else 0 end |
+ .counters[$track.type].forced[$track_lang] +=
+ if ($track.properties.forced_track and .tracks[-1].striptracks_keep) then
+ 1
+ else 0 end |
+ .counters[$track.type].default[$track_lang] +=
+ if ($track.properties.default_track and .tracks[-1].striptracks_keep) then
+ 1
+ else 0 end
+) |
+
+# Ensure at least one audio track is kept
+if ((.tracks | map(select(.type == "audio")) | length == 1) and (.tracks | map(select(.type == "audio" and .striptracks_keep)) | length == 0)) then
+ # If there is only one audio track and none are kept, keep the only audio track
+ .tracks |= map(if .type == "audio" then
+ .striptracks_log = "Warn|No audio tracks matched! Keeping only audio track " + .striptracks_log |
+ .striptracks_keep = true
+ else . end)
+elif (.tracks | map(select(.type == "audio" and .striptracks_keep)) | length == 0) then
+ # If no audio tracks are kept, first try to keep the default audio track
+ .tracks |= map(if .type == "audio" and .properties.default_track then
+ .striptracks_log = "Warn|No audio tracks matched! Keeping default audio track " + .striptracks_log |
+ .striptracks_keep = true
+ else . end) |
+ # If still no audio tracks are kept, keep the first audio track
+ if (.tracks | map(select(.type == "audio" and .striptracks_keep)) | length == 0) then
+ (first(.tracks[] | select(.type == "audio"))) |= . +
+ {striptracks_log: ("Warn|No audio tracks matched! Keeping first audio track " + .striptracks_log),
+ striptracks_keep: true}
+ else . end
+else . end |
+
+# Output simplified dataset
+{ striptracks_log, tracks: .tracks | map({ id, type, language: .properties.language, forced: .properties.forced_track, default: .properties.default_track, striptracks_debug_log, striptracks_log, striptracks_keep }) }
+')
+[ $striptracks_debug -ge 1 ] && echo "Debug|Track processing returned ${#striptracks_json_processed} bytes." | log
+[ $striptracks_debug -ge 2 ] && echo "Track processing returned: $(echo "$striptracks_json_processed" | jq)" | awk '{print "Debug|"$0}' | log
+
+# Write messages to log
+echo "$striptracks_json_processed" | jq -crM --argjson Debug $striptracks_debug '
+# Join log messages into one line
+def log_removed_tracks($type):
+ if (.tracks | map(select(.type == $type and .striptracks_keep == false)) | length > 0) then
+ "Info|Removing \($type) tracks: " +
+ (.tracks | map(select(.type == $type and .striptracks_keep == false) | .striptracks_log) | join(", "))
+ else empty end;
+
+# Log the chapters, if any
+.striptracks_log // empty,
+
+# Log debug messages
+( .tracks[] | (if $Debug >= 1 then .striptracks_debug_log else empty end),
+
+ # Log messages for kept tracks
+ (select(.striptracks_keep) | .striptracks_log // empty)
+),
+
+# Log removed tracks
+log_removed_tracks("audio"),
+log_removed_tracks("subtitles"),
+
+# Summary of kept tracks
+"Info|Kept tracks: \(.tracks | map(select(.striptracks_keep)) | length) " +
+"(audio: \(.tracks | map(select(.type == "audio" and .striptracks_keep)) | length), " +
+"subtitles: \(.tracks | map(select(.type == "subtitles" and .striptracks_keep)) | length))"
+' | log
+
+# Check for no audio or subtitle tracks
+if [ "$(echo "$striptracks_json_processed" | jq -crM '.tracks|map(select(.type=="audio" and .striptracks_keep))')" = "" ]; then
+ striptracks_message="Warn|Script encountered an error when determining audio tracks to keep and must close."
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
- end_script 9
+ end_script 11
fi
-# Process video file
-echo "$striptracks_json_processed" | awk -v Debug=$striptracks_debug \
--v Video="$striptracks_video" \
--v TempVideo="$striptracks_tempvideo" \
--v Title="$striptracks_title" \
--v AudioKeep="$striptracks_audiokeep" \
--v SubsKeep="$striptracks_subskeep" '
-# Exit codes: 0 success; 1 No tracks in source file; 2 No tracks removed; 3 How did we get here?
-# Array join function, based on GNU docs
-function join(array, sep, i, ret) {
- for (i in array)
- if (ret == "")
- ret = array[i]
- else
- ret = ret sep array[i]
- return ret
-}
-BEGIN {
- MKVMerge = "/usr/bin/mkvmerge"
- FS = "[\t\n: ]"
- IGNORECASE = 1
- split("", AudioCommand)
- split("", SubsCommand)
- split("", AudRmvLog)
- split("", SubsRmvLog)
-}
-/^Track ID/ {
- FieldCount = split($0, Fields)
- if (Fields[1] == "Track") {
- NoTr++
- Track[NoTr, "id"] = Fields[3]
- Track[NoTr, "typ"] = Fields[5]
- # This is inelegant and I know it
- # Finds the codec in parenthesis
- if (Fields[6] ~ /^\(/) {
- for (i = 6; i <= FieldCount; i++) {
- Track[NoTr, "codec"] = Track[NoTr, "codec"]" "Fields[i]
- if (match(Fields[i], /\)$/))
- break
- }
- sub(/^ /, "", Track[NoTr, "codec"])
+# All tracks matched/no tracks removed (see issues #49 and #89)
+if [ "$(echo "$striptracks_json" | jq -crM '.tracks|map(select(.type=="audio" or .type=="subtitles"))|length')" = "$(echo "$striptracks_json_processed" | jq -crM '.tracks|map(select((.type=="audio" or .type=="subtitles") and .striptracks_keep))|length')" ]; then
+ [ $striptracks_debug -ge 1 ] && echo "Debug|No tracks will be removed from video \"$striptracks_video\"" | log
+ # Check if already MKV
+ if [[ $striptracks_video == *.mkv ]]; then
+ # Remuxing not performed
+ striptracks_message="Info|No tracks would be removed from video. Setting Title only and exiting."
+ echo "$striptracks_message" | log
+ striptracks_mkvcommand="/usr/bin/mkvpropedit -q --edit info --set \"title=$striptracks_title\" \"$striptracks_video\""
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Executing: $striptracks_mkvcommand" | log
+ striptracks_result=$(eval $striptracks_mkvcommand)
+ striptracks_return=$?; [ $striptracks_return -ne 0 ] && {
+ striptracks_message=$(echo -e "[$striptracks_return] Error when setting video title: \"$striptracks_tempvideo\"\nmkvpropedit returned: $striptracks_result" | awk '{print "Error|"$0}')
+ echo "$striptracks_message" | log
+ echo "$striptracks_message" >&2
+ striptracks_exitstatus=13
}
- if (Track[NoTr, "typ"] == "video") VidCnt++
- if (Track[NoTr, "typ"] == "audio") AudCnt++
- if (Track[NoTr, "typ"] == "subtitles") SubsCnt++
- for (i = 6; i <= FieldCount; i++) {
- if (Fields[i] == "language")
- Track[NoTr, "lang"] = Fields[++i]
- }
- if (Track[NoTr, "lang"] == "")
- Track[NoTr, "lang"] = "und"
- }
-}
-/^Chapters/ {
- Chapters = $3
-}
-END {
- # Source video had no tracks
- if (!NoTr) {
- exit 1
- }
- if (!AudCnt) AudCnt=0; if (!SubsCnt) SubsCnt=0
- print "Info|Original tracks: "NoTr" (audio: "AudCnt", subtitles: "SubsCnt")"
- if (Chapters) print "Info|Chapters: "Chapters
- for (i = 1; i <= NoTr; i++) {
- if (Debug >= 2) print "Debug|Parsed: Track ID:"Track[i,"id"],"Type:"Track[i,"typ"],"Lang:"Track[i, "lang"],"Codec:"Track[i, "codec"]
- if (Track[i, "typ"] == "audio") {
- # Keep track if it matches command line selection, or if it is matches pseudo code ":any"
- if (AudioKeep ~ Track[i, "lang"] || AudioKeep ~ ":any") {
- print "Info|Keeping audio track "Track[i, "id"]": "Track[i, "lang"]" "Track[i, "codec"]
- AudioCommand[i] = Track[i, "id"]
- # Special case if there is only one audio track, even if it was not selected
- } else if (AudCnt == 1) {
- print "Warn|No audio tracks matched! Keeping only audio track "Track[i, "id"]": "Track[i, "lang"]" "Track[i, "codec"]
- AudioCommand[i] = Track[i, "id"]
- # Special case if there were multiple tracks, none were selected, and this is the last one.
- } else if (length(AudioCommand) == 0 && Track[i, "id"] == AudCnt) {
- print "Warn|No audio tracks matched! Keeping last audio track "Track[i, "id"]": "Track[i, "lang"]" "Track[i, "codec"]
- AudioCommand[i] = Track[i, "id"]
- # Special case for mis and zxx
- } else if (":mis:zxx" ~ Track[i, "lang"]) {
- print "Info|Keeping special audio track "Track[i, "id"]": "Track[i, "lang"]" "Track[i, "codec"]
- AudioCommand[i] = Track[i, "id"]
- } else
- AudRmvLog[i] = Track[i, "id"]": "Track[i, "lang"]" "Track[i, "codec"]
- } else {
- if (Track[i, "typ"] == "subtitles") {
- if (SubsKeep ~ Track[i, "lang"] || SubsKeep ~ ":any") {
- print "Info|Keeping subtitles track "Track[i, "id"]": "Track[i, "lang"]" "Track[i, "codec"]
- SubsCommand[i] = Track[i, "id"]
- } else
- SubsRmvLog[i] = Track[i, "id"]": "Track[i, "lang"]" "Track[i, "codec"]
- }
- }
- }
- if (length(AudRmvLog) != 0) print "Info|Removed audio tracks: " join(AudRmvLog, ",")
- if (length(SubsRmvLog) != 0) print "Info|Removed subtitles tracks: " join(SubsRmvLog, ",")
- print "Info|Kept tracks: "length(AudioCommand)+length(SubsCommand)+VidCnt" (audio: "length(AudioCommand)", subtitles: "length(SubsCommand)")"
- # All tracks matched/no tracks removed.
- if (length(AudioCommand)+length(SubsCommand)+VidCnt == NoTr) {
- if (Debug >= 1) print "Debug|No tracks will be removed from video \""Video"\""
- # Only skip remux if already MKV.
- if (match(Video, /\.mkv$/)) {
- exit 2
- }
- if (Debug >= 1) print "Debug|Source video is not MKV. Remuxing anyway."
- }
- # This should never happen, but belt and suspenders
- if (length(AudioCommand) == 0) {
- print "Warn|Script encountered an error when determining audio tracks to keep and must close."
- exit 3
- }
- CommandLine = "-a " join(AudioCommand, ",")
- if (length(SubsCommand) == 0)
- CommandLine = CommandLine" -S"
+ end_script
else
- CommandLine = CommandLine" -s " join(SubsCommand, ",")
- if (Debug >= 1) print "Debug|Executing: nice "MKVMerge" --title \""Title"\" -q -o \""TempVideo"\" "CommandLine" \""Video"\""
- Result = system("nice "MKVMerge" --title \""Title"\" -q -o \""TempVideo"\" "CommandLine" \""Video"\"")
- if (Result > 1) print "Error|["Result"] remuxing \""Video"\"" > "/dev/stderr"
-}' | log
-#### END MAIN
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Source video is not MKV. Remuxing anyway." | log
+ fi
+fi
-# Check awk exit code
-striptracks_return="${PIPESTATUS[1]}"
-[ $striptracks_debug -ge 2 ] && echo "Debug|awk exited with code: $striptracks_return" | log
-[ $striptracks_return -ne 0 ] && {
- case "$striptracks_return" in
- 1) # Source video had no tracks
- striptracks_message="Error|The original video \"$striptracks_video\" had no audio or subtitle tracks!"
- echo "$striptracks_message" | log
- echo "$striptracks_message" >&2
- end_script 11
- ;;
- 2) # All tracks matched/no tracks removed and already MKV. Remuxing not performed.
- striptracks_message="Info|No tracks would be removed from video. Setting Title only and exiting."
- echo "$striptracks_message" | log
- [ $striptracks_debug -ge 1 ] && echo "Debug|Executing: /usr/bin/mkvpropedit -q --edit info --set \"title=$striptracks_title\" \"$striptracks_video\"" | log
- /usr/bin/mkvpropedit -q --edit info --set "title=$striptracks_title" "$striptracks_video" 2>&1 | log
- end_script 0
- ;;
- *) striptracks_message="Error|[$striptracks_return] Script exited abnormally."
- echo "$striptracks_message" | log
- echo "$striptracks_message" >&2
- end_script 13
- ;;
- esac
-}
+# Prepare to reorder tracks if option is enabled
+if [ "$striptracks_reorder" = "true" ]; then
+ striptracks_neworder=$(echo "$striptracks_json_processed" | jq -jcM --arg AudioKeep "$striptracks_audiokeep" \
+--arg SubsKeep "$striptracks_subskeep" '
+# Reorder tracks
+def order_tracks(tracks; rules; tracktype):
+ rules | split(":")[1:] | map(split("+") | {lang: .[0], mods: .[1]}) |
+ reduce .[] as $rule (
+ [];
+ . as $orderedTracks |
+ . += [tracks |
+ map(. as $track |
+ select(.type == tracktype and .striptracks_keep and
+ ($rule.lang | in({"any":0,($track.language):0})) and
+ ($rule.mods == null or
+ ($rule.mods | test("[fd]") | not) or
+ ($rule.mods | contains("f") and $track.forced) or
+ ($rule.mods | contains("d") and $track.default)
+ )
+ ) |
+ .id as $id |
+ # Remove track id from orderedTracks if it already exists
+ if ([$id] | flatten | inside($orderedTracks | flatten)) then empty else $id end
+ )]
+ ) | flatten;
+
+# Reorder audio and subtitles according to language rules
+.tracks as $tracks |
+order_tracks($tracks; $AudioKeep; "audio") as $audioOrder |
+order_tracks($tracks; $SubsKeep; "subtitles") as $subsOrder |
+
+# Output ordered track string compatible with the mkvmerge --track-order option
+# Video tracks are always first, followed by audio tracks, then subtitles
+$tracks | map(select(.type == "video") | .id) + $audioOrder + $subsOrder | map("0:" + tostring) | join(",")
+')
+ striptracks_neworder="--track-order $striptracks_neworder"
+ striptracks_message="Info|Reordering tracks using language rules."
+ echo "$striptracks_message" | log
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Using track reorder string: $striptracks_neworder" | log
+fi
+
+# Test for hardlinked file (see issue #85)
+striptracks_refcount=$(stat -c %h "$striptracks_video")
+[ $striptracks_debug -ge 1 ] && echo "Debug|Input file has a hard link count of $striptracks_refcount" | log
+if [ "$striptracks_refcount" != "1" ]; then
+ striptracks_message="Warn|Input video file is a hardlink and this will be broken by remuxing."
+ echo "$striptracks_message" | log
+ echo "$striptracks_message" >&2
+fi
+
+# Build argument with kept audio tracks for MKVmerge
+striptracks_audioarg=$(echo "$striptracks_json_processed" | jq -crM '.tracks | map(select(.type == "audio" and .striptracks_keep) | .id) | join(",")')
+striptracks_audioarg="-a $striptracks_audioarg"
+
+# Build argument with kept subtitles tracks for MKVmerge, or remove all subtitles
+striptracks_subsarg=$(echo "$striptracks_json_processed" | jq -crM '.tracks | map(select(.type == "subtitles" and .striptracks_keep) | .id) | join(",")')
+if [ ${#striptracks_subsarg} -ne 0 ]; then
+ striptracks_subsarg="-s $striptracks_subsarg"
+else
+ striptracks_subsarg="-S"
+fi
+
+# Execute MKVmerge (remux then rename, see issue #46)
+striptracks_mkvcommand="nice /usr/bin/mkvmerge --title \"$striptracks_title\" -q -o \"$striptracks_tempvideo\" $striptracks_audioarg $striptracks_subsarg $striptracks_neworder \"$striptracks_video\""
+[ $striptracks_debug -ge 1 ] && echo "Debug|Executing: $striptracks_mkvcommand" | log
+striptracks_result=$(eval $striptracks_mkvcommand)
+striptracks_return=$?
+case $striptracks_return in
+ 1) striptracks_message=$(echo -e "[$striptracks_return] Warning when remuxing video: \"$striptracks_video\"\nmkvmerge returned: $striptracks_result" | awk '{print "Warn|"$0}')
+ echo "$striptracks_message" | log
+ ;;
+ 2) striptracks_message=$(echo -e "[$striptracks_return] Error when remuxing video: \"$striptracks_video\"\nmkvmerge returned: $striptracks_result" | awk '{print "Error|"$0}')
+ echo "$striptracks_message" | log
+ echo "$striptracks_message" >&2
+ end_script 13
+ ;;
+esac
# Check for non-empty file
if [ ! -s "$striptracks_tempvideo" ]; then
@@ -1506,9 +1653,9 @@ fi
if [ "$(id -u)" -eq 0 ]; then
# Set owner
[ $striptracks_debug -ge 1 ] && echo "Debug|Changing owner of file \"$striptracks_tempvideo\"" | log
- chown --reference="$striptracks_video" "$striptracks_tempvideo" >&2
+ striptracks_result=$(chown --reference="$striptracks_video" "$striptracks_tempvideo")
striptracks_return=$?; [ $striptracks_return -ne 0 ] && {
- striptracks_message="Error|[$striptracks_return] Error when changing owner of file: \"$striptracks_tempvideo\""
+ striptracks_message=$(echo -e "[$striptracks_return] Error when changing owner of file: \"$striptracks_tempvideo\"\nchown returned: $striptracks_result" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
striptracks_exitstatus=15
@@ -1518,9 +1665,9 @@ else
[ $striptracks_debug -ge 1 ] && echo "Debug|Unable to change owner of file when running as user '$(id -un)'" | log
fi
# Set permissions
-chmod --reference="$striptracks_video" "$striptracks_tempvideo" >&2
+striptracks_result=$(chmod --reference="$striptracks_video" "$striptracks_tempvideo")
striptracks_return=$?; [ $striptracks_return -ne 0 ] && {
- striptracks_message="Error|[$striptracks_return] Error when changing permissions of file: \"$striptracks_tempvideo\""
+ striptracks_message=$(echo -e "[$striptracks_return] Error when changing permissions of file: \"$striptracks_tempvideo\"\nchmod returned: $striptracks_result" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
striptracks_exitstatus=15
@@ -1529,9 +1676,9 @@ striptracks_return=$?; [ $striptracks_return -ne 0 ] && {
# Just delete the original video if running in batch mode
if [ "$striptracks_type" = "batch" ]; then
[ $striptracks_debug -ge 1 ] && echo "Debug|Deleting: \"$striptracks_video\"" | log
- rm "$striptracks_video" 2>&1 | log
+ striptracks_result=$(rm "$striptracks_video")
striptracks_return=$?; [ $striptracks_return -ne 0 ] && {
- striptracks_message="Error|[$striptracks_return] Error when deleting video: \"$striptracks_video\""
+ striptracks_message=$(echo -e "[$striptracks_return] Error when deleting video: \"$striptracks_video\"\nrm returned: $striptracks_result" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
striptracks_exitstatus=16
@@ -1547,7 +1694,7 @@ else
}
fi
-# Another check for the temporary file, to make sure it wasn't deleted
+# Another check for the temporary file, to make sure it wasn't deleted (see issue #65)
if [ ! -f "$striptracks_tempvideo" ]; then
striptracks_message="Error|${striptracks_type^} deleted the temporary remuxed file: \"$striptracks_tempvideo\". Halting."
echo "$striptracks_message" | log
@@ -1557,18 +1704,20 @@ fi
# Rename the temporary video file to MKV
[ $striptracks_debug -ge 1 ] && echo "Debug|Renaming \"$striptracks_tempvideo\" to \"$striptracks_newvideo\"" | log
-mv -f "$striptracks_tempvideo" "$striptracks_newvideo" 2>&1 | log
+striptracks_result=$(mv -f "$striptracks_tempvideo" "$striptracks_newvideo")
striptracks_return=$?; [ $striptracks_return -ne 0 ] && {
- striptracks_message="Error|[$striptracks_return] Unable to rename temp video: \"$striptracks_tempvideo\" to: \"$striptracks_newvideo\". Halting."
+ striptracks_message=$(echo -e "[$striptracks_return] Unable to rename temp video: \"$striptracks_tempvideo\" to: \"$striptracks_newvideo\". Halting.\nmv returned: $striptracks_result" | awk '{print "Error|"$0}')
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
end_script 6
}
+# Get file size (see issue #61)
# shellcheck disable=SC2046
striptracks_filesize=$(stat -c %s "${striptracks_newvideo}" | numfmt --to iec --format "%.3f")
striptracks_message="Info|New size: $striptracks_filesize"
echo "$striptracks_message" | log
+#### END MAIN
#### Call Radarr/Sonarr API to RescanMovie/RescanSeries
# Check for URL
@@ -1583,7 +1732,7 @@ elif [ -n "$striptracks_api_url" ]; then
# if get_import_info; then
# # Build JSON data
# [ $striptracks_debug -ge 1 ] && echo "Debug|Building JSON data to import" | log
- # striptracks_json=$(echo $striptracks_result | jq -jcrM "
+ # striptracks_json=$(echo $striptracks_result | jq -jcM "
# map(
# select(.path == \"$striptracks_newvideo\") |
# {path, folderName, \"${striptracks_video_type}Id\":.${striptracks_video_type}.id,${striptracks_sonarr_json} quality, $striptracks_language_node}
@@ -1632,11 +1781,11 @@ elif [ -n "$striptracks_api_url" ]; then
striptracks_videofile_id="$(echo $striptracks_videoinfo | jq -crM .${striptracks_json_quality_root}.id)"
[ $striptracks_debug -ge 1 ] && echo "Debug|Using new video file id '$striptracks_videofile_id'" | log
- # Check if video is unmonitored after the delete/import
- if [ ${striptracks_conf_unmonitor:-0} -eq 1 -a "$(echo "$striptracks_videoinfo" | jq -crM ".monitored")" = "false" ]; then
- striptracks_message="Warn|'$striptracks_title' is unmonitored after deleting the original video. Compensating for ${striptracks_type^} configuration."
+ # Check if video monitored status changed after the delete/import (see issues #87 and #90)
+ if [ "$(echo "$striptracks_videoinfo" | jq -crM ".monitored")" != "$striptracks_videomonitored" ]; then
+ striptracks_message="Warn|Video monitor status changed after deleting the original. Setting it back to '$striptracks_videomonitored'"
echo "$striptracks_message" | log
- # Set video to monitored again
+ # Set video monitor state
set_video_info
fi
@@ -1666,76 +1815,70 @@ elif [ -n "$striptracks_api_url" ]; then
# If we stripped out other languages, remove them
# Only works in Radarr and Sonarr v4 (no per-episode edit function in Sonarr v3)
[ $striptracks_debug -ge 1 ] && echo "Debug|Getting languages in new video file \"$striptracks_newvideo\"" | log
- if get_mediainfo "$striptracks_newvideo"; then
- # Build array of full name languages
- striptracks_newvideo_langcodes="$(echo $striptracks_json | jq -crM '.tracks[] | select(.type == "audio") | .properties.language')"
- unset striptracks_newvideo_languages
- for i in $striptracks_newvideo_langcodes; do
- # shellcheck disable=SC2090
- # Exclude Any, Original, and Unknown
- striptracks_newvideo_languages+="$(echo $striptracks_isocodemap | jq -crM ".languages[] | .language | select((.\"iso639-2\"[]) == \"$i\") | select(.name != \"Any\" and .name != \"Original\" and .name != \"Unknown\").name")"
- done
- if [ -n "$striptracks_newvideo_languages" ]; then
- # Covert to standard JSON
- striptracks_json_languages="$(echo $striptracks_lang_codes | jq -crM "map(select(.name | inside(\"$striptracks_newvideo_languages\")) | {id, name})")"
-
- # Check languages for Radarr and Sonarr v4
- # Sooooo glad I did it this way
- if [ "$(echo $striptracks_videofile_info | jq -crM .languages)" != "null" ]; then
- if [ "$(echo $striptracks_videofile_info | jq -crM .languages)" != "$striptracks_json_languages" ]; then
- if set_radarr_language; then
- striptracks_exitstatus=0
- else
- striptracks_message="Error|${striptracks_type^} error when updating video language(s)."
- echo "$striptracks_message" | log
- echo "$striptracks_message" >&2
- striptracks_exitstatus=17
- fi
+ get_mediainfo "$striptracks_newvideo"
+
+ # Build array of full name languages
+ striptracks_newvideo_langcodes="$(echo $striptracks_json | jq -crM '.tracks[] | select(.type == "audio") | .properties.language')"
+ unset striptracks_newvideo_languages
+ for i in $striptracks_newvideo_langcodes; do
+ # shellcheck disable=SC2090
+ # Exclude Any, Original, and Unknown
+ striptracks_newvideo_languages+="$(echo $striptracks_isocodemap | jq -crM ".languages[] | .language | select((.\"iso639-2\"[]) == \"$i\") | select(.name != \"Any\" and .name != \"Original\" and .name != \"Unknown\").name")"
+ done
+ if [ -n "$striptracks_newvideo_languages" ]; then
+ # Covert to standard JSON
+ striptracks_json_languages="$(echo $striptracks_lang_codes | jq -crM "map(select(.name | inside(\"$striptracks_newvideo_languages\")) | {id, name})")"
+
+ # Check languages for Radarr and Sonarr v4
+ # Sooooo glad I did it this way
+ if [ "$(echo $striptracks_videofile_info | jq -crM .languages)" != "null" ]; then
+ if [ "$(echo $striptracks_videofile_info | jq -crM .languages)" != "$striptracks_json_languages" ]; then
+ if set_radarr_language; then
+ striptracks_exitstatus=0
else
- # The languages are already correct
- [ $striptracks_debug -ge 1 ] && echo "Debug|Language(s) '$(echo $striptracks_json_languages | jq -crM "[.[].name] | join(\",\")")' remained unchanged." | log
- fi
- # Check languages for Sonarr v3 and earlier
- elif [ "$(echo $striptracks_videofile_info | jq -crM .language)" != "null" ]; then
- if [ "$(echo $striptracks_videofile_info | jq -crM .language)" != "$(echo $striptracks_json_languages | jq -crM '.[0]')" ]; then
- if set_sonarr_language; then
- striptracks_exitstatus=0
- else
- striptracks_message="Error|${striptracks_type^} error when updating video language(s)."
- echo "$striptracks_message" | log
- echo "$striptracks_message" >&2
- striptracks_exitstatus=17
- fi
- else
- # The languages are already correct
- [ $striptracks_debug -ge 1 ] && echo "Debug|Language '$(echo $striptracks_json_languages | jq -crM ".[0].name")' remained unchanged." | log
+ striptracks_message="Error|${striptracks_type^} error when updating video language(s)."
+ echo "$striptracks_message" | log
+ echo "$striptracks_message" >&2
+ striptracks_exitstatus=17
fi
else
- # Some unknown JSON formatting
- striptracks_message="Warn|The '$striptracks_videofile_api' API returned unknown JSON language node."
- echo "$striptracks_message" | log
- echo "$striptracks_message" >&2
- striptracks_exitstatus=20
+ # The languages are already correct
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Language(s) '$(echo $striptracks_json_languages | jq -crM "[.[].name] | join(\",\")")' remained unchanged." | log
+ fi
+ # Check languages for Sonarr v3 and earlier
+ elif [ "$(echo $striptracks_videofile_info | jq -crM .language)" != "null" ]; then
+ if [ "$(echo $striptracks_videofile_info | jq -crM .language)" != "$(echo $striptracks_json_languages | jq -crM '.[0]')" ]; then
+ if set_sonarr_language; then
+ striptracks_exitstatus=0
+ else
+ striptracks_message="Error|${striptracks_type^} error when updating video language(s)."
+ echo "$striptracks_message" | log
+ echo "$striptracks_message" >&2
+ striptracks_exitstatus=17
+ fi
+ else
+ # The languages are already correct
+ [ $striptracks_debug -ge 1 ] && echo "Debug|Language '$(echo $striptracks_json_languages | jq -crM ".[0].name")' remained unchanged." | log
fi
- elif [ "$striptracks_newvideo_langcodes" = "und" ]; then
- # Only language detected is Unknown
- echo "Warn|The only language in the video file was Unknown (und). Not updating ${striptracks_type^} database." | log
else
- # Video language not in striptracks_isocodemap
- striptracks_message="Warn|Video language code(s) '${striptracks_newvideo_langcodes//$'\n'/,}' not found in the ISO Codemap. Cannot evaluate."
+ # Some unknown JSON formatting
+ striptracks_message="Warn|The '$striptracks_videofile_api' API returned unknown JSON language node."
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
striptracks_exitstatus=20
fi
+ elif [ "$striptracks_newvideo_langcodes" = "und" ]; then
+ # Only language detected is Unknown
+ echo "Warn|The only audio language in the video file was 'Unknown (und)'. Not updating ${striptracks_type^} database." | log
else
- # Get media info failed
- striptracks_message="Error|Could not get media info from new video file. Can't check resulting languages."
+ # Video language not in striptracks_isocodemap
+ striptracks_message="Warn|Video language code(s) '${striptracks_newvideo_langcodes//$'\n'/,}' not found in the ISO Codemap. Cannot evaluate."
echo "$striptracks_message" | log
echo "$striptracks_message" >&2
- striptracks_exitstatus=9
+ striptracks_exitstatus=20
fi
- # Get list of videos that could be renamed
+ # Get list of videos that could be renamed (see issue #50)
get_rename
striptracks_return=$?; [ $striptracks_return -ne 0 ] && {
striptracks_message="Warn|[$striptracks_return] ${striptracks_type^} error when getting list of videos to rename."