diff --git a/.assets/danger.png b/.assets/danger.png deleted file mode 100644 index f9d46cc20..000000000 Binary files a/.assets/danger.png and /dev/null differ diff --git a/.assets/notes.png b/.assets/notes.png new file mode 100644 index 000000000..1773c1340 Binary files /dev/null and b/.assets/notes.png differ diff --git a/.dockerignore b/.dockerignore index c129946e4..03a45ec9d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,8 +3,8 @@ .gitignore .github .gitattributes +.vscode .assets -hooks READMETEMPLATE.md README.md SECURITY.md diff --git a/.github/workflows/BuildImage.yml b/.github/workflows/BuildImage.yml index 34940bf1e..134cb792e 100644 --- a/.github/workflows/BuildImage.yml +++ b/.github/workflows/BuildImage.yml @@ -20,7 +20,7 @@ jobs: echo "BASEIMAGE=${{ env.BASEIMAGE }}" >> $GITHUB_OUTPUT echo "MODNAME=${{ env.MODNAME }}" >> $GITHUB_OUTPUT # **** If the mod needs to be versioned, set the versioning logic below. Otherwise leave as is. **** - MOD_VERSION="2.4.0" + MOD_VERSION="2.4.3" echo "MOD_VERSION=${MOD_VERSION}" >> $GITHUB_OUTPUT outputs: GITHUB_REPO: ${{ steps.outputs.outputs.GITHUB_REPO }} diff --git a/README.md b/README.md index f565d64d3..d062ae9b3 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,23 @@ # About A [Docker Mod](https://github.com/linuxserver/docker-mods) for the LinuxServer.io Lidarr Docker container that uses ffmpeg and a script to automatically convert downloaded FLAC (or other format) files to MP3s. Default output quality is 320Kbps constant bit rate. Advanced options act as a light wrapper to ffmpeg, allowing conversion to any supported audio format, including AAC, AC3, Opus, and many others. -A [Batch Mode](./README.md#batch-mode) is also supported that allows usage outside of Lidarr. +A [Batch Mode](#batch-mode) is also supported that allows usage outside of Lidarr. ->**NOTE:** This mod supports Linux OSes only. +> [!NOTE] +> This mod supports Linux OSes only. -Production Container info: ![Docker Image Size](https://img.shields.io/docker/image-size/linuxserver/mods/lidarr-flac2mp3 "Container Size") -[![linuxserver/docker-mods/mods/lidarr-flac2mp3](https://img.shields.io/badge/dynamic/json?logo=github&url=https%3A%2F%2Fraw.githubusercontent.com%2Fthecaptain989%2Fghcr-pulls%2Fmaster%2Findex.json&query=%24%5B%3F(%40.owner%3D%3D%22linuxserver%22%20%26%26%20%40.repo%3D%3D%22docker-mods%22%20%26%26%20%40.image%3D%3D%22mods%22%20%26%26%20%40.tag%3D%3D%22lidarr-flac2mp3%22)%5D.pulls&label=ghcr%20pulls&color=1572A4)](https://github.com/linuxserver/docker-mods/pkgs/container/mods) + +Production Container info: [![Docker Image Size](https://img.shields.io/docker/image-size/linuxserver/mods/lidarr-flac2mp3)](https://hub.docker.com/r/linuxserver/mods/tags?name=lidarr-flac2mp3 "Docker image size") +[![linuxserver/docker-mods/mods/lidarr-flac2mp3](https://img.shields.io/badge/dynamic/json?logo=github&url=https%3A%2F%2Fthecaptain989.github.io%2Fghcr-pulls%2Flidarr-flac2mp3.json&query=%24.pulls&label=ghcr%20pulls&color=1572A4)](https://github.com/linuxserver/docker-mods/pkgs/container/mods "GitHub package pulls") Development Container info: -![Docker Image Size](https://img.shields.io/docker/image-size/thecaptain989/lidarr-flac2mp3 "Container Size") -![Docker Pulls](https://img.shields.io/docker/pulls/thecaptain989/lidarr-flac2mp3?logo=docker "Container Pulls") -[![GitHub Super-Linter](https://github.com/TheCaptain989/lidarr-flac2mp3/actions/workflows/linter.yml/badge.svg)](https://github.com/TheCaptain989/lidarr-flac2mp3/actions/workflows/linter.yml "Linter Job") +[![Docker Image Size](https://img.shields.io/docker/image-size/thecaptain989/lidarr-flac2mp3)](https://hub.docker.com/r/thecaptain989/lidarr-flac2mp3 "Docker image size") +[![Docker Pulls](https://img.shields.io/docker/pulls/thecaptain989/lidarr-flac2mp3?logo=docker)](https://hub.docker.com/r/thecaptain989/lidarr-flac2mp3 "Docker container pulls") +[![Build Image](https://github.com/TheCaptain989/lidarr-flac2mp3/actions/workflows/BuildImage.yml/badge.svg)](https://github.com/TheCaptain989/lidarr-flac2mp3/actions/workflows/BuildImage.yml "BuildImage Job") + # Installation -1. Pull the [linuxserver/lidarr](https://hub.docker.com/r/linuxserver/lidarr "LinuxServer.io's Lidarr container") docker image from GitHub Container Registry or Docker Hub: - `docker pull lscr.io/linuxserver/lidarr:latest` - -2. Configure the Docker container with all the port, volume, and environment settings from the *original container documentation* here: - **[linuxserver/lidarr](https://hub.docker.com/r/linuxserver/lidarr "Docker container")** +1. Configure the Docker container with all the port, volume, and environment settings from the *original container documentation* here: + **[linuxserver/lidarr](https://hub.docker.com/r/linuxserver/lidarr "Lidarr Docker container")** 1. Add a **DOCKER_MODS** environment variable to your `compose.yml` file or `docker run` command, as follows: - Stable release: `DOCKER_MODS=linuxserver/mods:lidarr-flac2mp3` - Dev/test release: `DOCKER_MODS=thecaptain989/lidarr-flac2mp3:latest` @@ -44,7 +44,8 @@ Development Container info: restart: unless-stopped ``` - *Example Docker Run Command* +
+ Example Docker Run Command ```shell docker run -d \ @@ -61,29 +62,45 @@ Development Container info: lscr.io/linuxserver/lidarr ``` +
+
+ Synology Screenshot + *Example Synology Configuration* ![flac2mp3](.assets/lidarr-synology.png "Synology container settings") +
+ 2. Start the container. -3. Configure a custom script from Lidarr's *Settings* > *Connect* screen and type the following in the **Path** field: +2. Configure a custom script from Lidarr's *Settings* > *Connect* screen and type the following in the **Path** field: `/usr/local/bin/flac2mp3.sh` - *Example* +
+ Screenshot + + *Example Custom Script* ![lidarr-flac2mp3](.assets/lidarr-custom-script.png "Lidarr Custom Script dialog") +
+ This will use the defaults to create a 320Kbps MP3 file. - *For any other setting, you **must** use one of the supported methods to pass arguments to the script. See the [Command-Line Syntax](./README.md#command-line-syntax) section below.* +> [!IMPORTANT] +> For any other setting, you **must** use one of the supported methods to pass arguments to the script. See the [Command-Line Syntax](#command-line-syntax) section below. # Usage New file(s) will be placed in the same directory as the original FLAC file(s) (unless redirected with the `--output` option below) with permissions preserved. Existing files with the same track name will be overwritten. Owner is preserved if the script is executed as root. -By default, if you've configured Lidarr's **Recycle Bin** path correctly, the original audio file will be moved there. -![danger] **NOTE:** If you have *not* configured the Recycle Bin, the original FLAC audio file(s) will be deleted and permanently lost. This behavior may be modified with the `--keep-file` option. +> [!TIP] +> By default, if you've configured Lidarr's **Recycle Bin** path correctly, the original audio file will be moved there. + +> [!CAUTION] +> If you have *not* configured the Recycle Bin, the original FLAC audio file(s) will be deleted and permanently lost. This behavior may be modified with the `--keep-file` option. ## Command-Line Syntax ->**Note:** The *Arguments* field for Custom Scripts was removed in Lidarr release [v0.7.0.1347](https://github.com/lidarr/Lidarr/commit/b9d240924f8965ebb2c5e307e36b810ae076101e "Lidarr commit notes") due to security concerns. +> [!NOTE] +> The *Arguments* field for Custom Scripts was removed in Lidarr release [v0.7.0.1347](https://github.com/lidarr/Lidarr/commit/b9d240924f8965ebb2c5e307e36b810ae076101e "Lidarr commit notes") due to security concerns. ### Options and Arguments The script may be called with optional command-line arguments. @@ -91,27 +108,33 @@ The script may be called with optional command-line arguments. The syntax for the command-line is: `flac2mp3 [{-b|--bitrate} | {-v|--quality} | {-a|--advanced} "" {-e|--extension} ] [{-f|--file} ] [{-k|--keep-file}] [{-o|--output} ] [{-r|--regex} ''] [{-t|--tags} ] [{-l|--log} ] [{-d|--debug} []]` -Where: +
+Table of Command-Line Arguments Option|Argument|Description ---|---|--- --b, --bitrate|\|Sets the output quality in constant bits per second (CBR).
Examples: 160k, 240k, 300000
**Note:** May not be specified with `-v`, `-a`, or `-e`. --v, --quality|\|Sets the output variable bit rate (VBR).
Specify a value between 0 and 9, with 0 being the highest quality.
See the [FFmpeg MP3 Encoding Guide](https://trac.ffmpeg.org/wiki/Encode/MP3) for more details.
**Note:** May not be specified with `-b`, `-a`, or `-e`. --a, --advanced|\"\\"|Advanced ffmpeg options.
The specified `options` replace all script defaults and are sent directly to ffmpeg.
The `options` value must be enclosed in quotes.
See [FFmpeg Options](https://ffmpeg.org/ffmpeg.html#Options) for details on valid options, and [Guidelines for high quality audio encoding](https://trac.ffmpeg.org/wiki/Encode/HighQualityAudio) for suggested usage.
**Note:** Requires the `-e` option to also be specified. May not be specified with `-v` or `-b`.
![warning] **WARNING:** You must specify an audio codec (by including a `-c:a ` ffmpeg option) or the resulting file will contain no audio!
![warning] **WARNING:** Invalid `options` could result in script failure! --e, --extension|\|Sets the output file extension.
The extension may be prefixed by a dot (".") or not.
Example: .ogg
**Note:** Requires the `-a` option to also be specified. May not be specified with `-v` or `-b`. --f, --file||If included, the script enters **[Batch Mode](./README.md#batch-mode)** and converts the specified audio file.
![warning] **WARNING:** Do not use this argument when called from Lidarr! --o, --output|\|Converted audio file(s) are saved to `directory` instead of being located in the same directory as the source audio file.
The path will be created if it does not exist. --k, --keep-file| |Do not delete the source file or move it to the Lidarr Recycle bin.
**Note:** This also disables importing the new files into Lidarr after conversion. --r, --regex|'\'|Sets the regular expression used to select input files.
The `regex` value should be enclosed in single quotes and escaped properly.
Defaults to `[.]flac$`. --l, --log|\|The log filename
Default of /config/log/flac2mp3.txt --t, --tags|\|Comma separated list of metadata tags to apply automated corrections to.
See [Metadata Corrections](./README.md#metadata-corrections) section. --d, --debug|\[\\]|Enables debug logging. Level is optional.
Default of 1 (low).
2 includes JSON and FFmpeg output.
3 contains even more JSON output. ---help| |Display help and exit. ---version| |Display version and exit. +`-b`, `--bitrate`|``|Sets the output quality in constant bits per second (CBR).
Examples: 160k, 240k, 300000
**Note:** May not be specified with `-v`, `-a`, or `-e`. +`-v`, `--quality`|``|Sets the output variable bit rate (VBR).
Specify a value between 0 and 9, with 0 being the highest quality.
See the [FFmpeg MP3 Encoding Guide](https://trac.ffmpeg.org/wiki/Encode/MP3) for more details.
**Note:** May not be specified with `-b`, `-a`, or `-e`. +`-a`, `--advanced`|`""`|Advanced ffmpeg options.
The specified `options` replace all script defaults and are sent directly to ffmpeg.
The `options` value must be enclosed in quotes.
See [FFmpeg Options](https://ffmpeg.org/ffmpeg.html#Options) for details on valid options, and [Guidelines for high quality audio encoding](https://trac.ffmpeg.org/wiki/Encode/HighQualityAudio) for suggested usage.
**Note:** Requires the `-e` option to also be specified. May not be specified with `-v` or `-b`.
![warning] **WARNING:** You must specify an audio codec (by including a `-c:a ` ffmpeg option) or the resulting file will contain no audio!
![warning] **WARNING:** Invalid `options` could result in script failure! +`-e`, `--extension`|``|Sets the output file extension.
The extension may be prefixed by a dot (".") or not.
Example: .ogg
**Note:** Requires the `-a` option to also be specified. May not be specified with `-v` or `-b`. +`-f`, `--file`|``|If included, the script enters **[Batch Mode](#batch-mode)** and converts the specified audio file.
![note] **Do not** use this argument when called from Lidarr! +`-o`, `--output`|``|Converted audio file(s) are saved to `directory` instead of being located in the same directory as the source audio file.
The path will be created if it does not exist. +`-k`, `--keep-file`| |Do not delete the source file or move it to the Lidarr Recycle bin.
**Note:** This also disables importing the new files into Lidarr after conversion. +`-r`, `--regex`|`''`|Sets the regular expression used to select input files.
The `regex` value should be enclosed in single quotes and escaped properly.
Defaults to `[.]flac$`. +`-l`, `--log`|``|The log filename
Default of /config/log/flac2mp3.txt +`-t`, `--tags`|``|Comma separated list of metadata tags to apply automated corrections to.
See [Metadata Corrections](#metadata-corrections) section. +`-d`, `--debug`|`[]`|Enables debug logging. Level is optional.
Default of 1 (low).
2 includes JSON and FFmpeg output.
3 contains even more JSON output. +`--help`| |Display help and exit. +`--version`| |Display version and exit. + +
If neither `-b`, `-v`, `-a`, or `-e` options are specified, the script will default to a constant 320Kbps MP3. #### Technical notes on advanced options +
+Advanced Options Details + The `-a` option effectively makes the script a generic wrapper for ffmpeg. FFmpeg is executed once per track with only the loglevel, input filename, and output filename being set. All other options are passed unparsed to the command-line. The exact format of the executed ffmpeg command is: @@ -120,17 +143,20 @@ The exact format of the executed ffmpeg command is: ffmpeg -loglevel error -nostdin -i "input.flac" ${options} "output.${extension}" ``` +
+ #### Technical notes on regular expressions +
+Regular Expression Details + -By default, the script only matches and interacts with FLAC files (specifically, files ending in ".flac"). The `-r` option allows the script to match on a user specified regular expression (i.e. "regex") pattern. +By default, the script only matches and interacts with FLAC files (specifically, files ending in ".flac"). The `--regex` option allows the script to match on a user specified regular expression (i.e. "regex") pattern. Files are passed to the script with the full Linux path intact. (Ex: `/path/to/audio/a-ha/Hunting High and Low/01 Take on Me.mp3`). Craft your regular expression with this in mind. -![warning] **NOTE:** Escaping special regular expression characters (like a dot `.`) requires a double backslash or a character class. A character class (i.e. `[.]`) is recommended because backslashes can be stripped by the bash shell and getting this right can be confusing. - For example, to convert all audio files to AAC audio files, use the following options: ```shell @@ -139,7 +165,14 @@ For example, to convert all audio files to AAC audio files, use the following op Regular expression syntax is beyond the scope of this document. See this [tutorial](https://www.regular-expressions.info/tutorial.html "Regular Expressions Tutorial") for more information. Regular expression patterns may be tested [here](http://regexstorm.net/tester "regex tester"). +
+ +> [!TIP] +> Escaping special regular expression characters (like a dot `.`) requires a double backslash or a character class. A character class (i.e. `[.]`) is recommended because backslashes can be stripped by the bash shell and getting this right can be confusing. + ## Examples +
+Command-line Examples ```shell -b 320k # Output 320 kbit/s MP3 (non-VBR; same as default behavior) @@ -159,12 +192,21 @@ Regular expression syntax is beyond the scope of this document. See this [tutor # Place the converted file(s) in the specified directory and do not delete the original audio file(s) ``` +
+ ## 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 `FLAC2MP3_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 `FLAC2MP3_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 Lidarr. ### Included Wrapper Scripts For your convenience, several wrapper scripts are included in the `/usr/local/bin/` directory. -You may use any of these scripts in place of the `flac2mp3.sh` mentioned in the [Installation](./README.md#installation) section above. +You may use any of these scripts in place of the `flac2mp3.sh` mentioned in the [Installation](#installation) section above. + +
+List of scripts ```shell flac2mp3-debug.sh # Enable debugging, level 1 @@ -174,8 +216,13 @@ flac2opus.sh # Convert to Opus format using .opus extension, 192 kbi flac2alac.sh # Convert to Apple Lossless using an .m4a extension ``` +
+ ### Example Wrapper Script -To configure an entry from the [Examples](./README.md#examples) section above, create and save a file called `flac2mp3-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 `flac2mp3-custom.sh` to `/config` containing the following text: ```shell #!/bin/bash @@ -189,68 +236,93 @@ Make it executable: chmod +x /config/flac2mp3-custom.sh ``` -Then put `/config/flac2mp3-custom.sh` in the **Path** field in place of `/usr/local/bin/flac2mp3.sh` mentioned in the [Installation](./README.md#installation) section above. +Then put `/config/flac2mp3-custom.sh` in the **Path** field in place of `/usr/local/bin/flac2mp3.sh` mentioned in the [Installation](#installation) section above. ->**Note:** 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 Lidarr. +
## Environment Variable -The `flac2mp3.sh` script also allows the use of arguments provided by the `FLAC2MP3_ARGS` environment variable. This allows advanced use cases without having to provide a custom script. +The script can also read arguments from the `FLAC2MP3_ARGS` environment variable. This allows advanced use cases without having to provide a custom wrapper script. -For example, the following value in your `docker run` command would convert any .mp3 to Opus: +Make sure to correctly use quotes and/or escape special characters when using this method. (See [regular expression notes](#technical-notes-on-regular-expressions) above.) -```shell --e FLAC2MP3_ARGS='-a "-vn -c:a libopus -b:a 192k" -e .opus -r "[.]mp3$"' -``` +> [!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. -Make sure to correctly use quotes and/or escape special characters when using this method. (See [regular expression notes](./README.md#technical-notes-on-regular-expressions) above.) -In Docker Compose, the previous command would need an extra `$` to match the end-of-line: +
+Example Docker Compose + +For example, the following lines in your `compose.yml` file would convert any .mp3 to Opus, and would need an extra `$` to escape the end-of-line regular expression match: ```yaml environment: - FLAC2MP3_ARGS=-a "-vn -c:a libopus -b:a 192k" -e .opus -r '[.]mp3$$' ``` +
+
+Example Docker Run Command + +In a `docker run` command, it would be: + +```shell +-e FLAC2MP3_ARGS='-a "-vn -c:a libopus -b:a 192k" -e .opus -r "[.]mp3$"' +``` + +
+
+Synology Screenshot + *Example Synology Configuration* ![flac2mp3](.assets/lidarr-synology-2.png "Synology container settings") ->**NOTE:** The environment variable settings are *only* used 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 Release Import** and **On Upgrade** +The only events/notification triggers that are supported are **On Release 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 Lidarr. It converts the file specified on the command-line and ignores any environment variables that are normally expected to be set by the music management program. -Using this function, you can easily process all of your audio 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 audio 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 Lidarr, expect the following behavior while in Batch Mode: -* *The filename must be specified on the command-line*
(The `-f` option places the script in Batch Mode) +* *The filename must be specified on the command-line*
The `-f` option places the script in Batch Mode. * *Lidarr APIs are not called and its database is not updated.*
This may require a manual import of converted music files or an artist rescan. -* *Original audio files are deleted.*
The Recycle Bin function is not available. (Modifiable using the `-k` option.) +* *Original audio files are deleted.* (Modifiable using the `-k` option.)
The Recycle Bin function is not available. ### Batch Example +
+Batch Mode Example + To convert all .FLAC files in the `/music` directory to Apple Lossless Audio Codec (ALAC), enter the following at the Linux command-line: ```shell find /music/ -type f -name "*.flac" | while read file; do /usr/local/bin/flac2mp3.sh -f "$file" -a "-c:a alac" -e m4a; done ``` +
+ ## Metadata Corrections **This feature is not meant for general purpose use.** It is only documented here for completeness. +
+Metadata Tag Details + List of supported tags and metadata corrections that are applied: -|Tag|Original|Correction -|---|---|--- -|title|Parenthesis for live\|remix, etc. "()"|Square brackets "\[]" -|disc|1|1/1 -|genre|/Pop/|"Pop" -| |/Indie/|"Alternative & Indie" -| |/Industrial/|"Industrial Rock" -| |/Electronic/|"Electronica & Dance" -| |/Punk\|Alternative/|"Alternative & Punk" -| |/Rock/|"Rock" +Tag|Original|Correction +---|---|--- +`title`|Parenthesis for live\|remix, etc. "()"|Square brackets "\[]" +`disc`|1|1/1 +`genre`|/Pop/|"Pop" + |/Indie/|"Alternative & Indie" + |/Industrial/|"Industrial Rock" + |/Electronic/|"Electronica & Dance" + |/Punk\|Alternative/|"Alternative & Punk" + |/Rock/|"Rock" + +
## Logs By default, a log file is created for the script activity called: @@ -260,11 +332,13 @@ By default, a log file is created for the script activity called: This log can be downloaded from Lidarr under *System* > *Log Files*. The log filename can be modified with the `--log` command-line option. Log rotation is performed, with 5 log files of 1MB each kept, matching Lidarr's log retention. ->![danger] **NOTE:** If debug logging is enabled with a level above 1, the log file can grow very large very quickly and is much more likely to be rotated. *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 and is much more likely to be rotated. *Do not leave high-level debug logging enabled permanently.* # Uninstall To completely remove the mod: -1. Delete the custom script from Lidarr's *Settings* > *Connect* screen that you created in the [Installation](./README.md#installation) section above. +1. Delete the custom script from Lidarr's *Settings* > *Connect* screen that you created in the [Installation](#installation) section above. 2. Stop and delete the Lidarr container. 3. Exclude the **DOCKER_MODS** environment variable from your `compose.yaml` file or the `docker run` command when re-creating the Lidarr container. @@ -281,4 +355,4 @@ This would not be possible without the following: Icons made by [Freepik](https://www.freepik.com) from [Flaticon](https://www.flaticon.com/) [warning]: .assets/warning.png "Warning" -[danger]: .assets/danger.png "Danger" +[note]: .assets/notes.png "Note" diff --git a/root/usr/local/bin/flac2mp3.sh b/root/usr/local/bin/flac2mp3.sh index f1ce14530..3bea74011 100755 --- a/root/usr/local/bin/flac2mp3.sh +++ b/root/usr/local/bin/flac2mp3.sh @@ -6,24 +6,27 @@ # Resultant MP3s are fully tagged and retain same permissions as original file # Dependencies: -# ffmpeg -# ffprobe -# awk -# curl -# jq -# stat -# nice -# basename -# dirname -# printenv -# chmod -# tr -# sed +# From ffmpeg package: +# ffmpeg +# ffprobe +# From jq package: +# jq +# Generally always available: +# awk +# curl +# stat +# nice +# basename +# dirname +# printenv +# chmod +# tr +# sed # Exit codes: # 0 - success; or test # 1 - no audio tracks detected -# 2 - ffmpeg not found +# 2 - ffmpeg, ffprobe, or jq not found # 3 - invalid command line arguments # 4 - log file is not writable # 5 - specified audio file not found @@ -52,8 +55,12 @@ export flac2mp3_debug=0 export flac2mp3_keep=0 export flac2mp3_type=$(printenv | sed -n 's/_eventtype *=.*$//p') -# Usage function +# Usage functions function usage { + usage="Try '$flac2mp3_script --help' for more information." + echo "$usage" >&2 +} +function long_usage { usage=" $flac2mp3_script Version: $flac2mp3_ver Audio conversion script designed for use with Lidarr @@ -103,7 +110,7 @@ Options: [default: \.flac$] -t, --tags Comma separated list of metadata tags to apply automated corrections to. - Supports: disc, genre + Supports: title, disc, genre -l, --log log filename [default: /config/log/flac2mp3.txt] -d, --debug [] Enable debug logging @@ -137,7 +144,7 @@ Examples: directory and do not delete the original audio file(s) " - echo "$usage" >&2 + echo "$usage" } # Check for environment variable arguments @@ -164,7 +171,7 @@ while (( "$#" )); do fi ;; --help ) # Display usage - usage + long_usage exit 0 ;; --version ) # Display version @@ -331,7 +338,8 @@ elif [[ "${flac2mp3_type,,}" = "lidarr" ]]; then [ -z "$flac2mp3_tracks" ] && flac2mp3_tracks="$lidarr_trackfile_path" else # Called in an unexpected way - echo -e "Error|Unknown or missing '*_eventtype' environment variable: ${flac2mp3_type}\nNot called from Lidarr.\nTry using Batch Mode option: -f " + echo -e "Error|Unknown or missing '*_eventtype' environment variable: ${flac2mp3_type}\nNot called from Lidarr. Try using Batch Mode option: -f " >&2 + usage exit 7 fi @@ -531,11 +539,12 @@ function import_tracks { } # Get track media info from ffprobe function ffprobe { - [ $flac2mp3_debug -ge 2 ] && echo "Debug|Executing: /usr/bin/ffprobe -hide_banner -loglevel $flac2mp3_ffmpeg_log -print_format json=compact=1 -show_format -show_entries \"format=tags : format_tags=title,disc,genre\" -i \"$1\"" | log + local flac2mp3_ffcommand="/usr/bin/ffprobe -hide_banner -loglevel $flac2mp3_ffmpeg_log -print_format json=compact=1 -show_format -show_entries \"format=tags : format_tags=title,disc,genre\" -i \"$1\"" + [ $flac2mp3_debug -ge 1 ] && echo "Debug|Executing: $flac2mp3_ffcommand" | log unset flac2mp3_ffprobe_json - flac2mp3_ffprobe_json=$(/usr/bin/ffprobe -hide_banner -loglevel $flac2mp3_ffmpeg_log -print_format json=compact=1 -show_format -show_entries "format=tags : format_tags=title,disc,genre" -i "$1") + flac2mp3_ffprobe_json=$(eval $flac2mp3_ffcommand 2>&1) flac2mp3_return=$?; [ $flac2mp3_return -ne 0 ] && { - flac2mp3_message="Error|[$flac2mp3_return] ffprobe error when inspecting track: \"$1\"" + flac2mp3_message=$(echo -e "[$flac2mp3_return] ffprobe error when inspecting file: \"$1\"\nffprobe returned: $flac2mp3_ffprobe_json" | awk '{print "Error|"$0}') echo "$flac2mp3_message" | log echo "$flac2mp3_message" >&2 } @@ -567,7 +576,7 @@ fi # Check that the log file exists if [ ! -f "$flac2mp3_log" ]; then echo "Info|Creating a new log file: $flac2mp3_log" - touch "$flac2mp3_log" 2>&1 + touch "$flac2mp3_log" fi # Check that the log file is writable @@ -578,24 +587,20 @@ if [ ! -w "$flac2mp3_log" ]; then fi # Check for required binaries -if [ ! -f "/usr/bin/ffmpeg" ]; then - flac2mp3_message="Error|/usr/bin/ffmpeg is required by this script" - echo "$flac2mp3_message" | log - echo "$flac2mp3_message" >&2 - end_script 2 -fi -if [ ! -f "/usr/bin/ffprobe" ]; then - flac2mp3_message="Error|/usr/bin/ffprobe is required by this script" - echo "$flac2mp3_message" | log - echo "$flac2mp3_message" >&2 - end_script 2 -fi +for flac2mp3_file in "/usr/bin/ffmpeg" "/usr/bin/ffprobe" "/usr/bin/jq"; do + if [ ! -f "$flac2mp3_file" ]; then + flac2mp3_message="Error|$flac2mp3_file is required by this script" + echo "$flac2mp3_message" | log + echo "$flac2mp3_message" >&2 + end_script 2 + fi +done # Log Debug state if [ $flac2mp3_debug -ge 1 ]; then flac2mp3_message="Debug|Enabling debug logging level ${flac2mp3_debug}. Starting ${lidarr_eventtype^} run." echo "$flac2mp3_message" | log - echo "$flac2mp3_message" + echo "$flac2mp3_message" >&2 fi # Log FLAC2MP3_ARGS usage @@ -653,7 +658,7 @@ elif [ -f "$flac2mp3_config" ]; then # Check for localhost [[ $flac2mp3_bindaddress = "*" ]] && flac2mp3_bindaddress=localhost - # Strip leading and trailing forward slashes from URL base + # Strip leading and trailing forward slashes from URL base (see issue #44) flac2mp3_urlbase="$(echo "$flac2mp3_urlbase" | sed -re 's/^\/+//; s/\/+$//')" # Build URL to Lidarr API @@ -699,7 +704,7 @@ fi # If specified, check if destination folder exists and create if necessary if [ "$flac2mp3_output" -a ! -d "$flac2mp3_output" ]; then [ $flac2mp3_debug -ge 1 ] && echo "Debug|Destination directory does not exist. Creating: $flac2mp3_output" | log - mkdir -p "$flac2mp3_output" >&2 + mkdir -p "$flac2mp3_output" flac2mp3_return=$?; [ $flac2mp3_return -ne 0 ] && { flac2mp3_message="Error|[$flac2mp3_return] mkdir returned an error. Unable to create output directory." echo "$flac2mp3_message" | log @@ -829,7 +834,7 @@ for flac2mp3_track in $flac2mp3_tracks; do esac done # shellcheck disable=SC2090 - [ $flac2mp3_debug -ge 1 ] && echo "Debug|New metadata: echo ${flac2mp3_ffmpeg_metadata//-metadata /}" | log + [ $flac2mp3_debug -ge 1 ] && echo "Debug|New metadata: ${flac2mp3_ffmpeg_metadata//-metadata /}" | log else echo "Warn|ffprobe did not return any data when querying track: \"$flac2mp3_track\"" | log flac2mp3_exitstatus=12 @@ -838,10 +843,11 @@ for flac2mp3_track in $flac2mp3_tracks; do # Convert the track echo "Info|Writing: $flac2mp3_newTrack" | log - [ $flac2mp3_debug -ge 1 ] && echo "Debug|Executing: nice /usr/bin/ffmpeg -loglevel $flac2mp3_ffmpeg_log -nostdin -i \"$flac2mp3_track\" $flac2mp3_ffmpeg_opts $flac2mp3_ffmpeg_metadata\"$flac2mp3_newTrack\"" | log - eval nice /usr/bin/ffmpeg -loglevel $flac2mp3_ffmpeg_log -nostdin -i \"$flac2mp3_track\" $flac2mp3_ffmpeg_opts $flac2mp3_ffmpeg_metadata\"$flac2mp3_newTrack\" 2>&1 | log + flac2mp3_ffcommand="nice /usr/bin/ffmpeg -loglevel $flac2mp3_ffmpeg_log -nostdin -i \"$flac2mp3_track\" $flac2mp3_ffmpeg_opts $flac2mp3_ffmpeg_metadata\"$flac2mp3_newTrack\"" + [ $flac2mp3_debug -ge 1 ] && echo "Debug|Executing: $flac2mp3_ffcommand" | log + flac2mp3_result=$(eval $flac2mp3_ffcommand 2>&1) flac2mp3_return=$?; [ $flac2mp3_return -ne 0 ] && { - flac2mp3_message="Error|[$flac2mp3_return] ffmpeg error when converting track: \"$flac2mp3_track\" to \"$flac2mp3_newTrack\"" + flac2mp3_message=$(echo -e "[$flac2mp3_return] ffmpeg error when converting track: \"$flac2mp3_track\" to \"$flac2mp3_newTrack\"\nffmpeg returned: $flac2mp3_result" | awk '{print "Error|"$0}') echo "$flac2mp3_message" | log echo "$flac2mp3_message" >&2 flac2mp3_exitstatus=13 @@ -861,9 +867,9 @@ for flac2mp3_track in $flac2mp3_tracks; do if [ "$(id -u)" -eq 0 ]; then # Set owner [ $flac2mp3_debug -ge 1 ] && echo "Debug|Changing owner of file \"$flac2mp3_newTrack\"" | log - chown --reference="$flac2mp3_track" "$flac2mp3_newTrack" >&2 + flac2mp3_result=$(chown --reference="$flac2mp3_track" "$flac2mp3_newTrack") flac2mp3_return=$?; [ $flac2mp3_return -ne 0 ] && { - flac2mp3_message="Error|[$flac2mp3_return] Error when changing owner of track: \"$flac2mp3_newTrack\"" + flac2mp3_message=$(echo -e "[$flac2mp3_return] Error when changing owner of file: \"$flac2mp3_newTrack\"\nchown returned: $flac2mp3_result" | awk '{print "Error|"$0}') echo "$flac2mp3_message" | log echo "$flac2mp3_message" >&2 flac2mp3_exitstatus=15 @@ -873,9 +879,9 @@ for flac2mp3_track in $flac2mp3_tracks; do [ $flac2mp3_debug -ge 1 ] && echo "Debug|Unable to change owner of track when running as user '$(id -un)'" | log fi # Set permissions - chmod --reference="$flac2mp3_track" "$flac2mp3_newTrack" >&2 + flac2mp3_result=$(chmod --reference="$flac2mp3_track" "$flac2mp3_newTrack") flac2mp3_return=$?; [ $flac2mp3_return -ne 0 ] && { - flac2mp3_message="Error|[$flac2mp3_return] Error when changing permissions of track: \"$flac2mp3_newTrack\"" + flac2mp3_message=$(echo -e "[$flac2mp3_return] Error when changing permissions of file: \"$flac2mp3_newTrack\"\nchmod returned: $flac2mp3_result" | awk '{print "Error|"$0}') echo "$flac2mp3_message" | log echo "$flac2mp3_message" >&2 flac2mp3_exitstatus=15 @@ -887,12 +893,12 @@ for flac2mp3_track in $flac2mp3_tracks; do continue fi - # Skip the rest of the loop if in batch mode, just delete the track + # Skip the rest of the loop if in batch mode, just delete the file if [ "$flac2mp3_type" = "batch" ]; then [ $flac2mp3_debug -ge 1 ] && echo "Debug|Deleting: \"$flac2mp3_track\"" | log - rm "$flac2mp3_track" >&2 + flac2mp3_result=$(rm "$flac2mp3_track") flac2mp3_return=$?; [ $flac2mp3_return -ne 0 ] && { - flac2mp3_message="Error|[$flac2mp3_return] Error when deleting track: \"$flac2mp3_track\"" + flac2mp3_message=$(echo -e "[$flac2mp3_return] Error when deleting file: \"$flac2mp3_track\"\nrm returned: $flac2mp3_result" | awk '{print "Error|"$0}') echo "$flac2mp3_message" | log echo "$flac2mp3_message" >&2 flac2mp3_exitstatus=16 @@ -937,14 +943,14 @@ elif [ -n "$flac2mp3_api_url" ]; then # Build JSON data for all tracks # NOTE: Tracks with empty track IDs will not appear in the resulting JSON and will therefore not be imported into Lidarr [ $flac2mp3_debug -ge 1 ] && echo "Debug|Building JSON data to import" | log - export flac2mp3_json=$(echo $flac2mp3_result | jq -jcrM " + export flac2mp3_json=$(echo $flac2mp3_result | jq -jcM " map( select(.path | inside(\"$flac2mp3_import_list\")) | {path, \"artistId\":$lidarr_artist_id, \"albumId\":$lidarr_album_id, albumReleaseId,\"trackIds\":[.tracks[].id], quality, \"disableReleaseSwitching\":false} ) ") - # Import new files into Lidarr + # Import new files into Lidarr (see issue #39) import_tracks flac2mp3_return=$?; [ $flac2mp3_return -ne 0 ] && { flac2mp3_message="Error|[$flac2mp3_return] Lidarr error when importing the new tracks!" @@ -954,7 +960,7 @@ elif [ -n "$flac2mp3_api_url" ]; then } flac2mp3_jobid="$(echo $flac2mp3_result | jq -crM .id)" - # Check status of job + # Check status of job (see issue #39) check_job flac2mp3_return=$?; [ $flac2mp3_return -ne 0 ] && { case $flac2mp3_return in