diff --git a/.assets/hotio-striptracks-synology.png b/.assets/hotio-striptracks-synology.png new file mode 100644 index 0000000..2439429 Binary files /dev/null and b/.assets/hotio-striptracks-synology.png differ diff --git a/.dockerignore b/.dockerignore index 58df2a2..81a371c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,7 +3,9 @@ .gitignore .github .gitattributes +.vscode .assets +hotio READMETEMPLATE.md README.md SECURITY.md diff --git a/.github/workflows/BuildImage.yml b/.github/workflows/BuildImage.yml index 5dc31a5..4de4566 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=${GITHUB_SHA:0:7} + MOD_VERSION="2.7.1" echo "MOD_VERSION=${MOD_VERSION}" >> $GITHUB_OUTPUT outputs: GITHUB_REPO: ${{ steps.outputs.outputs.GITHUB_REPO }} diff --git a/Dockerfile b/Dockerfile index e6b09e6..b05466f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,6 @@ ## Buildstage ## FROM ghcr.io/linuxserver/baseimage-alpine:3.20 as buildstage - ARG MOD_VERSION # copy local files @@ -17,11 +16,14 @@ RUN \ ## Single layer deployed image ## FROM scratch +ARG MOD_VERSION -LABEL org.opencontainers.image.source=https://github.com/TheCaptain989/radarr-striptracks +LABEL org.opencontainers.image.title=radarr-striptracks LABEL org.opencontainers.image.description="A Docker Mod to Radarr/Sonarr to automatically strip out unwanted audio and subtitle streams" -LABEL org.opencontainers.image.licenses=GPL-3.0-only +LABEL org.opencontainers.image.version="${MOD_VERSION}" +LABEL org.opencontainers.image.source="https://github.com/TheCaptain989/radarr-striptracks" LABEL org.opencontainers.image.authors="TheCaptain989 " +LABEL org.opencontainers.image.licenses=GPL-3.0-only # Add files from buildstage COPY --from=buildstage /root-layer/ / diff --git a/README.md b/README.md index a30d014..6c0d004 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,18 @@ # 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. -**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** +**NEW!** There is a now an installer for **hotio** containers! See the [HOTIO.md](./hotio/HOTIO.md) file for more details. **This unified script works in both Radarr and Sonarr. Use this mod in either container!** >**NOTE:** This mod supports Linux OSes only. - + Production Container info: ![Docker Image Size](https://img.shields.io/docker/image-size/linuxserver/mods/radarr-striptracks "Container Size") [![linuxserver/docker-mods/mods/radarr-striptracks](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%22radarr-striptracks%22)%5D.pulls&label=ghcr%20pulls&color=1572A4)](https://github.com/linuxserver/docker-mods/pkgs/container/mods) Development Container info: ![Docker Image Size](https://img.shields.io/docker/image-size/thecaptain989/radarr-striptracks "Container Size") ![Docker Pulls](https://img.shields.io/docker/pulls/thecaptain989/radarr-striptracks?logo=docker "Container Pulls") [![GitHub Super-Linter](https://github.com/TheCaptain989/radarr-striptracks/actions/workflows/linter.yml/badge.svg)](https://github.com/TheCaptain989/radarr-striptracks/actions/workflows/linter.yml "Linter Job") + # Installation 1. Pull your selected container ([linuxserver/radarr](https://hub.docker.com/r/linuxserver/radarr "LinuxServer.io's Radarr container") or [linuxserver/sonarr](https://hub.docker.com/r/linuxserver/sonarr "LinuxServer.io's Sonarr container")) from GitHub Container Registry or Docker Hub: @@ -337,5 +338,8 @@ This would not be possible without the following: 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). 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. + [warning]: .assets/warning.png "Warning" [danger]: .assets/danger.png "Danger" diff --git a/SECURITY.md b/SECURITY.md index 46cfa04..1c28230 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,8 +6,8 @@ Only the latest major and minor version are supported. | Version | Supported | | ------- | ------------------ | -| 2.6.x | :heavy_check_mark: | -| < 2.6 | :x: | +| 2.7.x | :heavy_check_mark: | +| < 2.7 | :x: | ## Reporting a Vulnerability diff --git a/hotio/99-striptracks.sh b/hotio/99-striptracks.sh new file mode 100644 index 0000000..a8c33f3 --- /dev/null +++ b/hotio/99-striptracks.sh @@ -0,0 +1,87 @@ +#!/command/with-contenv bash +# shellcheck shell=bash + +# Custom script to install Striptracks Mod meant for Radarr or Sonarr Docker containers +# WARNING: Minimal error handling! + +# Pre-set LSIO Docker Mod variables +DOCKER_MODS=linuxserver/mods:radarr-striptracks +#DOCKER_MODS_DEBUG=true +export DOCKER_MODS +export DOCKER_MODS_DEBUG +[ "$DOCKER_MODS_DEBUG" = "true" ] && echo "[mod-install] DOCKER_MODS: $DOCKER_MODS" && echo "[mod-install] DOCKER_MODS_DEBUG: $DOCKER_MODS_DEBUG" +echo "[mod-install] installing $DOCKER_MODS mod" + +# Steal the current docker-mods version from the source +MODS_VERSION=$(curl -s --fail-with-body "https://raw.githubusercontent.com/linuxserver/docker-baseimage-alpine/master/Dockerfile" | sed -nr 's/^ARG MODS_VERSION="?([^"]+)"?/\1/p') +[ "$DOCKER_MODS_DEBUG" = "true" ] && echo "[mod-install] MODS_VERSION: $MODS_VERSION" + +# Download and execute the main docker-mods script to install the mod +# Very well thought out code, this. Why reinvent? +curl -s --fail-with-body -o /docker-mods "https://raw.githubusercontent.com/linuxserver/docker-mods/mod-scripts/docker-mods.${MODS_VERSION}" +ret=$? +[ $ret -ne 0 ] && echo "[mod-install] unable to download docker-mods: Exit code: $ret. Exiting." && exit 1 + +chmod +x /docker-mods + +. /docker-mods +[ $ret -ne 0 ] && echo "[mod-install] docker-mods installation error: $ret. Exiting." && exit 1 + +# Get script version from installed mod +VERSION=$(sed -nr 's/^export striptracks_ver="?([^"]+)"?/\1/p' /usr/local/bin/striptracks.sh) +[ "$DOCKER_MODS_DEBUG" = "true" ] && echo "[mod-install] striptracks.sh version: $VERSION" + +# Remaining setup that is normally done with s6-overlay init scripts, but that rely on a lot of Docker Mods dependencies +cat <>> Striptracks Mod by TheCaptain989 <<< +Repos: + Dev/test: https://github.com/TheCaptain989/radarr-striptracks + Prod: https://github.com/linuxserver/docker-mods/tree/radarr-striptracks + +Version: ${VERSION} +---------------- +EOF + +# Determine if setup is needed +if [ ! -f /usr/bin/mkvmerge ]; then + echo "[mod-install] Running first time setup." + + if [ -f /usr/bin/apt ]; then + # Ubuntu + echo "[mod-install] Installing MKVToolNix using apt-get" + apt-get update && \ + apt-get -y install mkvtoolnix && \ + rm -rf /var/lib/apt/lists/* + elif [ -f /sbin/apk ]; then + # Alpine + echo "[mod-install] Installing MKVToolNix using apk" + apk upgrade --no-cache && \ + apk add --no-cache mkvtoolnix && \ + rm -rf /var/lib/apt/lists/* + else + # Unknown + echo "[mod-install] Unknown package manager. Attempting to install MKVToolNix using apt-get" + apt-get update && \ + apt-get -y install mkvtoolnix && \ + rm -rf /var/lib/apt/lists/* + fi +fi + +# Check ownership and attributes on each script file +[ -z "$PUID" ] && owner_user="root" || owner_user="$PUID" +[ -z "$PGID" ] && owner_group="root" || owner_group="$PGID" +for file in /usr/local/bin/striptracks*.sh +do + # Change ownership + if [ "$(stat -c '%G' "$file")" != "$owner_group" ]; then + echo "[mod-install] Changing ownership on $file script to $owner_user:$owner_group." + chown "$owner_user":"$owner_group" "$file" + fi + + # Make executable + if [ ! -x "$file" ]; then + echo "[mod-install] Making $file script executable." + chmod +x "$file" + fi +done diff --git a/hotio/HOTIO.md b/hotio/HOTIO.md new file mode 100644 index 0000000..2e23cea --- /dev/null +++ b/hotio/HOTIO.md @@ -0,0 +1,86 @@ +# About +This mod can now be used with [hotio](https://hotio.dev/) containers by using the method described in the hotio [FAQ](https://hotio.dev/faq/#:~:text=I%20would%20like%20to%20execute%20my%20own%20scripts%20on%20startup%2C%20how%20would%20I%20do%20this%3F) to install it. +(This method relies on s6-overlay v2 behavior still working, though v3 is the current version.) + +>This is a quick and dirty script with minimal testing or error checking. Note that it only runs *inside* the hotio container. + +# Installation +1. Download the **[99-striptracks.sh](./99-striptracks.sh)** install script and save it somewhere that can be mounted by your container. + + *Example location:* `/volume1/docker/99-striptracks.sh` + + *Example curl line to download the installation script:* + + ```shell + curl -s https://raw.githubusercontent.com/TheCaptain989/radarr-striptracks/master/hotio/99-striptracks.sh >/volume1/docker/99-striptracks.sh + ``` + +2. Make it executable: + + ```shell + chmod +x /volume1/docker/99-striptracks.sh + ``` + +3. Pull your selected container ([hotio/radarr](https://github.com/orgs/hotio/packages/container/package/radarr "hotio's Radarr container") or [hotio/sonarr](https://github.com/orgs/hotio/packages/container/package/sonarr "hotio.io's Sonarr container")) from GitHub Container Registry or Docker Hub: + `docker pull ghcr.io/hotio/radarr:latest` OR + `docker pull ghcr.io/hotio/sonarr:latest` + +4. Configure the Docker container with all the port, volume, and environment settings from the *original container documentation* here: + **[hotio/radarr](https://hotio.dev/containers/radarr/ "Radarr Docker container")** + **[hotio/sonarr](https://hotio.dev/containers/sonarr/ "Sonarr Docker container")** + + >**Note:** Notice that no environment variables are used in this setup. That is a specific feature of LSIO containers and is not applicable to hotio containers. + + 1. Add the **99-striptracks.sh** file path as a mount point in your `compose.yml` file or `docker run` command. + >**Note:** The `/etc/cont-init.d/99-striptracks` path below is important; don't change it! + + *Example Docker Compose YAML Configuration* + + ```yaml + services: + sonarr: + container_name: sonarr + image: ghcr.io/hotio/sonarr + ports: + - "8989:8989" + environment: + - PUID=1000 + - PGID=1000 + - UMASK=002 + - TZ=Etc/UTC + volumes: + - /:/config + - /:/data + - /volume1/docker/99-striptracks.sh:/etc/cont-init.d/99-striptracks + ``` + + *Example Docker Run Command* + + ```shell + docker run --rm \ + --name sonarr \ + -p 8989:8989 \ + -e PUID=1000 \ + -e PGID=1000 \ + -e UMASK=002 \ + -e TZ="Etc/UTC" \ + -v /:/config \ + -v /:/data \ + -v /volume1/docker/99-striptracks.sh:/etc/cont-init.d/99-striptracks \ + ghcr.io/hotio/sonarr + ``` + + *Example Synology Configuration* + ![striptracks hotio](../.assets/hotio-striptracks-synology.png "Synology container settings") + + >**Note:** Please be sure that your mount points for `/config` and `/data` above do not overlap with the newly added mount point! + + 2. Start the container. + +5. After the container has fully started, continue with Installation step 3 in the previous [README](../README.md#installation). + +## Requirements +You must have the **bash** shell available in your host path. You *might* attempt editing the [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) in the 99-striptracks.sh script to get around this, but that is beyond the scope of this document. + +## Known Problems +On at least *some* Synology hosts that use the ash shell, this script will cause the container to abort and not start. If this happens, ***check your container logs*** for hints as to what may be wrong. diff --git a/root/usr/local/bin/striptracks.sh b/root/usr/local/bin/striptracks.sh index f257c12..08314b4 100755 --- a/root/usr/local/bin/striptracks.sh +++ b/root/usr/local/bin/striptracks.sh @@ -809,6 +809,63 @@ function check_compat { [ $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 +function get_media_config { + local url="$striptracks_api_url/config/mediamanagement" + [ $striptracks_debug -ge 1 ] && echo "Debug|Getting ${striptracks_type^} configuration. Calling ${striptracks_type^} API using GET and URL '$url'" | log + unset striptracks_result + striptracks_result=$(curl -s --fail-with-body -H "X-Api-Key: $striptracks_apikey" \ + -H "Content-Type: application/json" \ + -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}') + echo "$striptracks_message" | log + echo "$striptracks_message" >&2 + } + [ $striptracks_debug -ge 2 ] && echo "API returned: $striptracks_result" | awk '{print "Debug|"$0}' | log + if [ "$(echo $striptracks_result | jq -crM '.id?')" != "null" ] && [ "$(echo $striptracks_result | jq -crM '.id?')" != "" ]; then + local striptracks_return=0 + else + local striptracks_return=1 + fi + return $striptracks_return +} +# 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 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 + unset striptracks_result + striptracks_result=$(curl -s --fail-with-body -H "X-Api-Key: $striptracks_apikey" \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -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}') + 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 + break + else + echo "Warn|Database is locked; system is likely overloaded. Sleeping 1 minute." | log + sleep 60 + fi + done + if [ $striptracks_curlret -eq 0 -a "${#striptracks_result}" != 0 ]; then + local striptracks_return=0 + else + local striptracks_return=1 + fi + return $striptracks_return +} # Exit program function end_script { # Cool bash feature @@ -1140,6 +1197,20 @@ elif [ -n "$striptracks_api_url" ]; then echo "$striptracks_message" >&2 striptracks_exitstatus=17 fi + # Check if Radarr/Sonarr are configured to unmonitor deleted videos + get_media_config + striptracks_return=$?; [ $striptracks_return -ne 0 ] && { + # No '.id' in returned JSON + striptracks_message="Warn|The Media Management Config API returned no id." + echo "$striptracks_message" | log + echo "$striptracks_message" >&2 + 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 else # No URL means we can't call the API striptracks_message="Warn|Unable to determine ${striptracks_type^} API URL." @@ -1518,8 +1589,18 @@ elif [ -n "$striptracks_api_url" ]; then # Get new video file id if get_video_info; then - striptracks_videofile_id="$(echo $striptracks_result | jq -crM .${striptracks_json_quality_root}.id)" + striptracks_videoinfo="$striptracks_result" + 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 -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." + echo "$striptracks_message" | log + # Set video to monitored again + set_video_info + fi + # Get new video file info if get_videofile_info; then striptracks_videofile_info="$striptracks_result"