diff --git a/.assets/danger.png b/.assets/danger.png new file mode 100644 index 0000000..f9d46cc Binary files /dev/null and b/.assets/danger.png differ diff --git a/.assets/warning.png b/.assets/warning.png new file mode 100644 index 0000000..0fe039d Binary files /dev/null and b/.assets/warning.png differ diff --git a/.dockerignore b/.dockerignore index cdb1a82..c129946 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,10 @@ +# Docker ignore file .git .gitignore .github .gitattributes +.assets +hooks READMETEMPLATE.md README.md +SECURITY.md diff --git a/.github/workflows/BuildImage.yml b/.github/workflows/BuildImage.yml index 37ddec5..108473b 100644 --- a/.github/workflows/BuildImage.yml +++ b/.github/workflows/BuildImage.yml @@ -15,7 +15,15 @@ jobs: - name: Build image run: | - docker build --no-cache -t ${{ github.sha }} . + # Build variables + VERSION=$(git describe --tags --always) + cat <**NOTE:** This mod support Linux OSes only. +>**NOTE:** This mod supports Linux OSes only. -Container info: -![Docker Image Size (latest by date)](https://img.shields.io/docker/image-size/linuxserver/mods/lidarr-flac2mp3)) +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 "Container Pulls") +Production Container info: ![Docker Image Size](https://img.shields.io/docker/image-size/linuxserver/mods/lidarr-flac2mp3 "Container Size") # Installation 1. Pull the [linuxserver/lidarr](https://hub.docker.com/r/linuxserver/lidarr "LinuxServer.io's Lidarr container") docker image from Docker Hub: @@ -11,81 +14,114 @@ Container info: 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. Add the **DOCKER_MODS** environment variable to the `docker create` command, as follows: - `-e DOCKER_MODS=linuxserver/mods:lidarr-flac2mp3` + 1. Add a **DOCKER_MODS** environment variable to the `docker run` command, as follows: + - Stable release: `-e DOCKER_MODS=linuxserver/mods:lidarr-flac2mp3` + + *Example Docker CLI Configuration* + ```shell + docker run -d \ + --name=lidarr \ + -e PUID=1000 \ + -e PGID=1000 \ + -e TZ=America/Chicago \ + -e DOCKER_MODS=linuxserver/mods:lidarr-flac2mp3 \ + -p 8686:8686 \ + -v /path/to/appdata/config:/config \ + -v /path/to/music:/music \ + -v /path/to/downloads:/downloads \ + --restart unless-stopped \ + ghcr.io/linuxserver/lidarr + ``` *Example Synology Configuration* ![flac2mp3](.assets/lidarr-synology.png "Synology container settings") 2. Start the container. -3. After all of the above configuration is complete, to use ffmpeg, configure a custom script from the Settings->Connect screen and type the following in the **Path** field: +3. After the above configuration is complete, to use ffmpeg, configure a custom script from Lidarr's *Settings* > *Connect* screen and type the following in the **Path** field: `/usr/local/bin/flac2mp3.sh` -## Usage -New file(s) with an MP3 extension will be placed in the same directory as the original FLAC file(s). Existing MP3 files with the same track name will be overwritten. + *Example* + ![lidarr-flac2mp3](.assets/lidarr-custom-script.png "Lidarr Custom Script dialog") -If you've configured the Lidarr Recycle Bin path correctly, the original audio file will be moved there. -![warning24] **NOTE:** If you have *not* configured the Recycle Bin, the original FLAC audio file(s) will be deleted and permanently lost. + This will use the defaults to create a 320Kbps MP3 file. + + *For any other setting, you **must** either use one of the [included wrapper scripts](./README.md#included-wrapper-scripts) or create a custom script with the command line options you desire. See the [Syntax](./README.md#syntax) section below.* + +## Usage +New file(s) with an MP3 extension will be placed in the same directory as the original FLAC file(s) and have the same owner and permissions. Existing MP3 files with the same track name will be overwritten. + +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. ### 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. To support options with this version and later, a wrapper script can be manually created that will call *flac2mp3.sh* with the required arguments. -The script accepts two options which may be placed in the **Arguments** field: +The script accepts three command line options: -`[-d] [-b ]` +`[-d] [-b | -v ]` -The `-b bitrate` option, if specified, sets the output quality in bits per second. If no `-b` option is specified, the script will default to 320Kbps. +The `-b bitrate` option sets the output quality in constant bits per second (CBR). +The `-v quality` option sets the output quality using a variable bit rate (VBR) where `quality` is 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. +If neither `-b` nor `-v` options are specified, the script will default to constant 320Kbps. The `-d` option enables debug logging. ### Examples ``` --b 320k # Output 320 kilobits per second MP3 (same as default behavior) --d -b 160k # Enable debugging, and output 160 kilobits per second MP3 +-b 320k # Output 320 kbit/s MP3 (non VBR; same as default behavior) +-v 0 # Output variable bitrate, VBR 220-260 kbit/s +-d -b 160k # Enable debugging, and output 160 kbit/s MP3 ``` -### Included Wrapper Script -For your convenience, a wrapper script to enable debugging is included in the `/usr/local/bin/` directory. -Use this script in place of the `flac2mp3.sh` mentioned in the [Installation](./README.md#installation) section above. +### 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. ``` flac2mp3-debug.sh # Enable debugging +flac2mp3-vbr.sh # Use variable bit rate, quality 0 ``` ### Example Wrapper Script -To configure the last entry from the [Examples](./README.md#examples) section above, create and save a file called `wrapper.sh` to `/usr/local/bin` containing the following text: -``` +To configure the last entry from the [Examples](./README.md#examples) section above, create and save a file called `striptracks-custom.sh` to `/config` containing the following text: +```shell #!/bin/bash . /usr/local/bin/flac2mp3.sh -d -b 160k ``` -Then put `/usr/local/bin/wrapper.sh` in the **Path** field in place of `/usr/local/bin/flac2mp3.sh` mentioned in the [Installation](./README.md#installation) section above. +Make it executable: +```shell +chmod +x /config/striptracks-custom.sh +``` + +Then put `/config/striptracks-custom.sh` in the **Path** field in place of `/usr/local/bin/flac2mp3.sh` mentioned in the [Installation](./README.md#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. ### Triggers The only events/notification triggers that have been tested are **On Release Import** and **On Upgrade** -![lidarr-flac2mp3](.assets/lidarr-custom-script.png "Lidarr Custom Script dialog") - ### Logs A log file is created for the script activity called: `/config/logs/flac2mp3.txt` -This log can be downloaded from the Lidarr GUI under System->Log Files +This log can be downloaded from Lidarr under *System* > *Log Files* Log rotation is performed, with 5 log files of 1MB each kept, matching Lidarr's log retention. ->![warning24] **NOTE:** If debug logging is enabled, the log file can grow very large very quickly. *Do not leave debug logging enabled permanently.* +>![danger] **NOTE:** If debug logging is enabled, the log file can grow very large very quickly. *Do not leave debug logging enabled permanently.* -___ -# Credits +## Credits This would not be possible without the following: [Lidarr](https://lidarr.audio/ "Lidarr homepage") [LinuxServer.io Lidarr](https://hub.docker.com/r/linuxserver/lidarr "Lidarr Docker container") container [LinuxServer.io Docker Mods](https://hub.docker.com/r/linuxserver/mods "Docker Mods containers") project -[ffmpeg](https://ffmpeg.org/ "FFMpeg homepage") +[ffmpeg](https://ffmpeg.org/ "FFMpeg homepage") +Icons made by [Freepik](https://www.freepik.com) from [Flaticon](https://www.flaticon.com/) -[warning]: http://files.softicons.com/download/application-icons/32x32-free-design-icons-by-aha-soft/png/32/Warning.png "Warning" -[warning24]: http://files.softicons.com/download/toolbar-icons/24x24-free-pixel-icons-by-aha-soft/png/24x24/Warning.png "Warning" +[warning]: .assets/warning.png "Warning" +[danger]: .assets/danger.png "Danger" diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..97dc1fc --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,18 @@ +# Security Policy + +## Supported Versions + +Only the latest major and minor version are supported. + +| Version | Supported | +| ------- | ------------------ | +| 1.2.x | :heavy_check_mark: | +| < 1.2 | :x: | + +## Reporting a Vulnerability + +Vulnerability reports or security concerns may be submitted as a new issue in GitHub, or emailed to: + +>thecaptain989@protonmail.com + +Please allow 48-hours for an initial response. An issue will be entered if accepted. diff --git a/root/etc/cont-init.d/98-flac2mp3 b/root/etc/cont-init.d/98-flac2mp3 index 7943d61..e8bff43 100644 --- a/root/etc/cont-init.d/98-flac2mp3 +++ b/root/etc/cont-init.d/98-flac2mp3 @@ -3,7 +3,9 @@ cat <>> Flac2MP3 Mod by TheCaptain989 <<< -Repo: https://github.com/linuxserver/docker-mods/tree/lidarr-flac2mp3 +Repos: + Dev/test: https://github.com/TheCaptain989/lidarr-flac2mp3 + Prod: https://github.com/linuxserver/docker-mods/tree/lidarr-flac2mp3 Version: $(cat /etc/version.tc989) ---------------- diff --git a/root/usr/local/bin/flac2mp3-vbr.sh b/root/usr/local/bin/flac2mp3-vbr.sh new file mode 100644 index 0000000..9fff691 --- /dev/null +++ b/root/usr/local/bin/flac2mp3-vbr.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +. /usr/local/bin/flac2mp3.sh -v 0 diff --git a/root/usr/local/bin/flac2mp3.sh b/root/usr/local/bin/flac2mp3.sh index 02b5891..b157243 100644 --- a/root/usr/local/bin/flac2mp3.sh +++ b/root/usr/local/bin/flac2mp3.sh @@ -1,20 +1,22 @@ #!/bin/bash # Script to convert FLAC files to MP3 using FFMpeg -# https://github.com/linuxserver/docker-mods/tree/lidarr-flac2mp3 -# Can also process MP3s and tag them appropriately -# Resultant MP3s are fully tagged +# Dev/test: https://github.com/TheCaptain989/lidarr-flac2mp3 +# Prod: https://github.com/linuxserver/docker-mods/tree/lidarr-flac2mp3 +# Resultant MP3s are fully tagged and retain same permissions as original file # Dependencies: # ffmpeg # awk # stat # nice +# chmod # Exit codes: # 0 - success; or test -# 1 - no tracks files specified on command line +# 1 - no tracks files found in environment # 2 - mkvmerge not found +# 3 - invalid command line arguments # 10 - awk script generated an error ### Variables @@ -34,25 +36,25 @@ RET=$?; [ "$RET" != 0 ] && >&2 echo "WARNING[$RET]: Unable to read recyclebin in function usage { usage=" $flac2mp3_script -Audio conversion script designed for use with Bazarr +Audio conversion script designed for use with Lidarr Source: https://github.com/TheCaptain989/lidarr-flac2mp3 Usage: - $0 [-d] [-b ] - -Arguments: - bitrate # output quality in bits per second (SI units) + $0 [-d] [-b | -v ] Options: - -d # enable debug logging - -b # set bitrate; default 320K + -d enable debug logging + -b set output quality in constant bits per second [default: 320k] + Ex: 160k, 240k, 300000 + -v set variable bitrate; quality between 0-9 + 0 is highest quality, 9 is lowest + See https://trac.ffmpeg.org/wiki/Encode/MP3 for more details Examples: - $flac2mp3_script -b 320k # Output 320 kilobits per second MP3 - (same as default behavior) - $flac2mp3_script -d -b 160k # Enable debugging, and output quality - 160 kilobits per second + $flac2mp3_script -b 320k # Output 320 kbit/s MP3 (non VBR; same as default behavior) + $flac2mp3_script -v 0 # Output variable bitrate, VBR 220-260 kbit/s + $flac2mp3_script -d -b 160k # Enable debugging and set output to 160 kbit/s " >&2 echo "$usage" } @@ -119,7 +121,7 @@ function check_rescan { return $RET } # Process options -while getopts ":db:" opt; do +while getopts ":db:v:" opt; do case ${opt} in d ) # For debug purposes only MSG="Debug|Enabling debug logging." @@ -128,27 +130,57 @@ while getopts ":db:" opt; do flac2mp3_debug=1 printenv | sort | sed 's/^/Debug|/' | log ;; - b ) # Set bitrate - flac2mp3_bitrate="$OPTARG" + b ) # Set constant bit rate + if [ -n "$flac2mp3_vbrquality" ]; then + MSG="Error|Both -b and -v options cannot be set at the same time." + echo "$MSG" | log + >&2 echo "$MSG" + usage + exit 3 + else + flac2mp3_bitrate="$OPTARG" + fi ;; - : ) - MSG="Error|Invalid option: -$OPTARG requires an argument" + v ) # Set variable quality + if [ -n "$flac2mp3_bitrate" ]; then + MSG="Error|Both -v and -b options cannot be set at the same time." + echo "$MSG" | log + >&2 echo "$MSG" + usage + exit 3 + else + flac2mp3_vbrquality="$OPTARG" + fi + ;; + : ) # No required argument specified + MSG="Error|Invalid option: -${OPTARG} requires an argument" echo "$MSG" | log >&2 echo "$MSG" + usage + exit 3 + ;; + * ) # Unknown option + MSG="Error|Unknown option: -${OPTARG}" + echo "$MSG" | log + >&2 echo "$MSG" + usage + exit 3 ;; esac done shift $((OPTIND -1)) -# Set default bitrate -[ -z "$flac2mp3_bitrate" ] && flac2mp3_bitrate="320k" +# Set default bit rate +[ -z "$flac2mp3_vbrquality" ] && [ -z "$flac2mp3_bitrate" ] && flac2mp3_bitrate="320k" +# Handle Lidarr Test event if [[ "$lidarr_eventtype" = "Test" ]]; then echo "Info|Lidarr event: $lidarr_eventtype" | log echo "Info|Script was test executed successfully." | log exit 0 fi +# Check if called from within Lidarr if [ -z "$flac2mp3_tracks" ]; then MSG="Error|No track file(s) specified! Not called from Lidarr?" echo "$MSG" | log @@ -157,6 +189,7 @@ if [ -z "$flac2mp3_tracks" ]; then exit 1 fi +# Check for required binaries if [ ! -f "/usr/bin/ffmpeg" ]; then MSG="Error|/usr/bin/ffmpeg is required by this script" echo "$MSG" | log @@ -164,48 +197,67 @@ if [ ! -f "/usr/bin/ffmpeg" ]; then exit 2 fi -# Legacy one-liner script +# Legacy one-liner script for posterity #find "$lidarr_artist_path" -name "*.flac" -exec bash -c 'ffmpeg -loglevel warning -i "{}" -y -acodec libmp3lame -b:a 320k "${0/.flac}.mp3" && rm "{}"' {} \; #### MAIN -echo "Info|Lidarr event: $lidarr_eventtype, Artist: $lidarr_artist_name ($lidarr_artist_id), Album: $lidarr_album_title ($lidarr_album_id), Export bitrate: $flac2mp3_bitrate, Tracks: $flac2mp3_tracks" | log +echo "Info|Lidarr event: $lidarr_eventtype, Artist: $lidarr_artist_name ($lidarr_artist_id), Album: $lidarr_album_title ($lidarr_album_id), Export bitrate: ${flac2mp3_bitrate:-$flac2mp3_vbrquality}, Tracks: $flac2mp3_tracks" | log echo "$flac2mp3_tracks" | awk -v Debug=$flac2mp3_debug \ -v Recycle="$flac2mp3_recyclebin" \ --v Bitrate=$flac2mp3_bitrate ' +-v Bitrate=$flac2mp3_bitrate \ +-v VBR=$flac2mp3_vbrquality ' BEGIN { FFMpeg="/usr/bin/ffmpeg" FS="|" RS="|" IGNORECASE=1 + if (Bitrate) { + if (Debug) print "Debug|Using constant bitrate of "Bitrate + BrCommand="-b:a "Bitrate + } else { + if (Debug) print "Debug|Using variable quality of "VBR + BrCommand="-q:a "VBR + } } /\.flac/ { + # Get each FLAC file name and create a new MP3 name Track=$1 sub(/\n/,"",Track) NewTrack=substr(Track, 1, length(Track)-5)".mp3" print "Info|Writing: "NewTrack - if (Debug) print "Debug|Executing: nice "FFMpeg" -loglevel error -i \""Track"\" "CoverCmds1"-map 0 -y -acodec libmp3lame -b:a "Bitrate" -write_id3v1 1 -id3v2_version 3 "CoverCmds2"\""NewTrack"\"" - Result=system("nice "FFMpeg" -loglevel error -i \""Track"\" "CoverCmds1"-map 0 -y -acodec libmp3lame -b:a "Bitrate" -write_id3v1 1 -id3v2_version 3 "CoverCmds2"\""NewTrack"\" 2>&1") + # Convert the track + if (Debug) print "Debug|Executing: nice "FFMpeg" -loglevel error -i \""Track"\" -c:v copy -map 0 -y -acodec libmp3lame "BrCommand" -write_id3v1 1 -id3v2_version 3 \""NewTrack"\"" + Result=system("nice "FFMpeg" -loglevel error -i \""Track"\" -c:v copy -map 0 -y -acodec libmp3lame "BrCommand" -write_id3v1 1 -id3v2_version 3 \""NewTrack"\" 2>&1") if (Result) { - print "Error|"Result" converting \""Track"\"" + print "Error|Exit code "Result" converting \""Track"\"" } else { if (Recycle=="") { - if (Debug) print "Debug|Deleting: \""Track"\"" - system("[ -s \""NewTrack"\" ] && [ -f \""Track"\" ] && rm \""Track"\"") + # No Recycle Bin, so check for non-zero size new file and delete the old one + if (Debug) print "Debug|Deleting: \""Track"\" and setting permissions on \""NewTrack"\"" + #Command="[ -s \""NewTrack"\" ] && [ -f \""Track"\" ] && chown --reference=\""Track"\" \""NewTrack"\" && chmod --reference=\""Track"\" \""NewTrack"\" && rm \""Track"\"" + Command="if [ -s \""NewTrack"\" ]; then if [ -f \""Track"\" ]; then chown --reference=\""Track"\" \""NewTrack"\"; chmod --reference=\""Track"\" \""NewTrack"\"; rm \""Track"\"; fi; fi" + if (Debug) print "Debug|Executing: "Command + system(Command) } else { + # Recycle Bin is configured, so check if it exists, append a relative path to it from the track, check for non-zero size new file, and move the old one to the Recycle Bin match(Track,/^\/?[^\/]+\//) RecPath=substr(Track,RSTART+RLENGTH) sub(/[^\/]+$/,"",RecPath) RecPath=Recycle RecPath - if (Debug) print "Debug|Moving: \""Track"\" to \""RecPath"\"" - system("[ ! -e \""RecPath"\" ] && mkdir -p \""RecPath"\"; [ -s \""NewTrack"\" ] && [ -f \""Track"\" ] && mv -t \""RecPath"\" \""Track"\"") + if (Debug) print "Debug|Moving: \""Track"\" to \""RecPath"\" and setting permissions on \""NewTrack"\"" + Command="if [ ! -e \""RecPath"\" ]; then mkdir -p \""RecPath"\"; fi; if [ -s \""NewTrack"\" ]; then if [ -f \""Track"\" ]; then chown --reference=\""Track"\" \""NewTrack"\"; chmod --reference=\""Track"\" \""NewTrack"\"; mv -t \""RecPath"\" \""Track"\"; fi; fi" + if (Debug) print "Debug|Executing: "Command + system(Command) } } } ' | log +#### END MAIN + +# Check for awk script completion RET="${PIPESTATUS[1]}" # captures awk exit status if [ $RET != "0" ]; then - # Check for script completion and non-empty file MSG="Error|Script exited abnormally. File permissions issue?" echo "$MSG" | log >&2 echo "$MSG" @@ -241,11 +293,13 @@ if [ ! -z "$lidarr_artist_id" ]; then >&2 echo "$MSG" fi else + # No config file means we can't call the API MSG="Warn|Unable to locate Lidarr config file: '$flac2mp3_config'" echo "$MSG" | log >&2 echo "$MSG" fi else + # No Artist ID means we can't call the API MSG="Warn|Missing environment variable lidarr_artist_id" echo "$MSG" | log >&2 echo "$MSG"