├── .bashrc ├── .bin ├── build-wireguard ├── dl ├── docker-upgrade ├── docker │ └── configure ├── entware │ ├── entware-startup.sh │ └── install ├── fix-plex-transcoder ├── fix-srt ├── media │ └── fix-srt.ps1 ├── misc │ ├── fix-permissions │ ├── throw │ ├── upgrade-docker-completion.sh │ └── upgrade-git-completion.sh ├── nas ├── pwsh └── tvshows ├── .config ├── fish │ ├── config.fish │ └── functions │ │ ├── df.fish │ │ └── du.fish └── sshd_config.patch ├── .gitconfig ├── .gitignore ├── .profile ├── LICENSE ├── README.md ├── docker ├── .env.template ├── configure ├── docker-compose.yml └── sslh │ └── Dockerfile └── media ├── .env.template ├── .tvshows.template ├── configure ├── docker-compose.yml └── medusa └── Dockerfile /.bashrc: -------------------------------------------------------------------------------- 1 | # 2 | # ~/.bashrc 3 | # 4 | 5 | [[ -f /opt/etc/profile ]] && . /opt/etc/profile 6 | 7 | [[ -f /opt/bin/less ]] && export PAGER=less && export LESS=-R 8 | 9 | for file in ~/.bash_completion/*; do 10 | [[ -f "$file" ]] && . "$file" 11 | done 12 | 13 | # If not running interactively, don't do anything else 14 | [[ $- != *i* ]] && return 15 | 16 | export GIT_PS1_SHOWUPSTREAM='git verbose' 17 | GIT_PS1='\[\033[01;37m\]`status=$(git status --porcelain=v1 2> /dev/null);[[ $(echo -e "$status"|grep "^ M ") ]]&&echo "\[\033[31m\]"||([[ -n $status ]]&&echo "\[\033[33m\]"||echo "\[\033[96m\]")`$(__git_ps1 "(%s) ")' 18 | export PS1="$GIT_PS1$PS1" 19 | 20 | alias df='df -h' 21 | alias docker='sudo docker' 22 | alias docker-compose='sudo docker-compose' 23 | alias du='du -h' 24 | alias ls='ls --color=auto -h' 25 | -------------------------------------------------------------------------------- /.bin/build-wireguard: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | output="$1" 3 | 4 | pushd "$(realpath "$0" | xargs dirname)" > /dev/null 5 | . misc/throw 6 | popd > /dev/null 7 | 8 | [ -z "$output" ] && throw 'output expected' 9 | output="$(realpath "$output")" 10 | [ -d "$output" ] || throw "$output: directory not found" 11 | 12 | [ -f "/proc/syno_platform" ] || throw "/proc/syno_platform: file not found" 13 | platform="$(cat /proc/syno_platform | tr '[:upper:]' '[:lower:]')" 14 | 15 | echo "Building Wireguard module for $platform into $output" 16 | sudo docker run --rm --privileged --env "PACKAGE_ARCH=$platform" --env DSM_VER=6.2 -v "$output:/result_spk" blackvoidclub/synobuild62 17 | -------------------------------------------------------------------------------- /.bin/dl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | log() { 4 | local messages="$@" 5 | [ -n "$LOG" ] && echo "$messages" | tee -a "$LOG" || echo "$messages" 6 | } 7 | 8 | show() { 9 | local title="$1" && shift && local messages="$@" 10 | [ -n "$LOG" ] && echo -e "\e[35m$title\e[39m" "$messages" | tee -a "$LOG" || echo "$title" "$messages" 11 | } 12 | 13 | throw() { 14 | local message="$1" 15 | [ -n "$LOG" ] && echo -e "\e[31m$message\e[39m" | tee -a "$LOG" >&2 || echo "$message" >&2 16 | exit 1 17 | } 18 | 19 | MEDIAHOME="$(realpath "$0" | xargs dirname)/.." 20 | MEDIAPATH="$MEDIAHOME/media/.env" 21 | [ -f "$MEDIAPATH" ] && MEDIAUSER="$(grep '^PUID=' "$MEDIAPATH" | cut -d'=' -f2)" || MEDIAUSER= 22 | [ -z "$MEDIAUSER" ] && throw 'Run ~/media/configure first' 23 | LOG="$MEDIAHOME/.log/tvshows.log" 24 | 25 | PATTERN="$1" 26 | [ "$2" == "-d" ] && MODE=download 27 | 28 | alias mediado="sudo -u '#$MEDIAUSER'" 29 | 30 | mediado mkdir -p "$MEDIAHOME/.log" 31 | mediado touch "$LOG" 32 | echo -e "*** $(date) Start ***\n" >> "$LOG" 33 | 34 | end() { 35 | echo -e "\n*** $(date) End ***\n" >> "$LOG" 36 | } 37 | trap end EXIT 38 | 39 | # Load & check tv shows configuration 40 | [ -f "$MEDIAHOME/.tvshows" ] || mediado cp "$MEDIAHOME/media/.tvshows.template" "$MEDIAHOME/.tvshows" 41 | . "$MEDIAHOME/.tvshows" 42 | if [ -z "$SERVER" ] || [ -z "$DL_SOURCE" ] || [ -z "$DL_DESTINATION" ] || [ -z "$TVSHOWS" ]; then 43 | throw '~/.tvshows is not properly configured' 44 | fi 45 | mediado ssh -o PasswordAuthentication=no "$SERVER" 'true' 46 | [ $? -eq 0 ] || throw "$SERVER: password is required" 47 | mediado ssh "$SERVER" "[ -d \"$DL_SOURCE\" ]" || throw "$SERVER:$DL_SOURCE: source path does not exist" 48 | [ -d "$DL_DESTINATION" ] || throw "$DL_DESTINATION: destination path does not exist" 49 | 50 | if [ "$MODE" == "download" ]; then 51 | # Downloads requested files 52 | show 'Downloading files...' 53 | mediado ssh "$SERVER" "find \"$DL_SOURCE\" -type f -printf '%P\\n' | egrep \"^($PATTERN)$\" | sort" \ 54 | | mediado rsync -hrtv -e ssh --files-from=- --stats --progress "$SERVER:$DL_SOURCE/" "$DL_DESTINATION/" 2>&1 | tee -a "$LOG" 55 | 56 | # Set permissions of downloaded tv shows 57 | log 58 | show 'Setting permissions of downloaded tv shows...' 59 | sudo true || throw 'Permission denied' 60 | sudo find "$DL_DESTINATION" -exec synoacltool -enforce-inherit "{}" \; 61 | else 62 | # Listing files 63 | show 'Listing files...' 64 | mediado ssh "$SERVER" "find \"$DL_SOURCE\" -type f -printf '%P\\n' | egrep \"^($PATTERN)$\" | sort" 2>&1 | tee -a "$LOG" 65 | fi 66 | -------------------------------------------------------------------------------- /.bin/docker-upgrade: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | name="$1" service="$2" 3 | 4 | cd "$(realpath "$0" | xargs dirname)" 5 | . misc/throw 6 | 7 | [ -z "$name" ] && throw 'name expected' 8 | [ -d "../$name" ] || throw "$name: directory not found" 9 | cd "../$name" 10 | 11 | [ -f docker-compose.yml ] || throw "docker-compose.yml does not exist, run 'configure' script first" 12 | 13 | which docker-compose > /dev/null || throw 'docker-compose: command not found' 14 | 15 | sudo docker-compose pull $service 16 | echo 17 | sudo docker-compose build --pull $service 18 | echo 19 | sudo docker-compose up -d $service 20 | -------------------------------------------------------------------------------- /.bin/docker/configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | source=$1 3 | 4 | cd "$(realpath "$0" | xargs dirname)" 5 | . ../misc/throw 6 | 7 | [ -f "$source/.env" ] && command -v diff &> /dev/null && diff -y "$source/.env.template" "$source/.env" 8 | [ -f "$source/.env" ] || echo -e "cat < "$source/.env" 9 | 10 | echo 11 | read -p 'Apply configuration? [y/N] ' -e 'answer' 12 | [ "$answer" == 'y' ] || throw "Configuration aborted" 13 | -------------------------------------------------------------------------------- /.bin/entware/entware-startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | case $1 in 4 | start) 5 | mkdir -p /opt 6 | mount -o bind /volume1/@entware-ng/opt /opt 7 | /opt/etc/init.d/rc.unslung start 8 | ;; 9 | stop) 10 | ;; 11 | esac 12 | -------------------------------------------------------------------------------- /.bin/entware/install: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # https://github.com/Entware/Entware-ng/wiki/Install-on-Synology-NAS 4 | 5 | cd "$(realpath "$0" | xargs dirname)" 6 | . ../misc/throw 7 | 8 | hardware="$(uname -m)" 9 | case "$hardware" in 10 | # armv5) 11 | # url=http://pkg.entware.net/binaries/armv5/installer/entware_install.sh 12 | # ;; 13 | # armv7) 14 | # url=http://pkg.entware.net/binaries/armv7/installer/entware_install.sh 15 | # ;; 16 | # i386) 17 | # url=http://pkg.entware.net/binaries/x86-32/installer/entware_install.sh 18 | # ;; 19 | x86_64) 20 | url=http://pkg.entware.net/binaries/x86-64/installer/entware_install.sh 21 | ;; 22 | # mips) 23 | # url=http://pkg.entware.net/binaries/mipsel/installer/installer.sh 24 | # ;; 25 | *) 26 | throw "$hardware: unknown hardware" 27 | ;; 28 | esac 29 | 30 | wget -O - "$url" | /bin/sh 31 | -------------------------------------------------------------------------------- /.bin/fix-plex-transcoder: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sudo chmod 666 /dev/dri/card0 /dev/dri/renderD128 3 | -------------------------------------------------------------------------------- /.bin/fix-srt: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | path="$(realpath "$1")" && shift && args="$@" 3 | # e.g. -Debug -WhatIf 4 | 5 | cd "$(realpath "$0" | xargs dirname)" 6 | . misc/throw 7 | 8 | [ -z "$path" ] && throw 'path expected' 9 | [ -d "$path" ] || throw "$path: directory not found" 10 | 11 | mkdir -p "$PWD/../.cache" 12 | [ -f "$PWD/../.cache/fix-srt.json" ] || touch "$PWD/../.cache/fix-srt.json" 13 | 14 | sudo docker run -it --rm \ 15 | -e POWERSHELL_TELEMETRY_OPTOUT=1 \ 16 | -v "$PWD:/mnt/bin" \ 17 | -v "$PWD/../.cache:/mnt/cache" \ 18 | -v "$path:$path" \ 19 | mcr.microsoft.com/powershell \ 20 | pwsh -File /mnt/bin/media/fix-srt.ps1 -Path "$path" -Cache /mnt/cache/fix-srt.json $args 21 | -------------------------------------------------------------------------------- /.bin/media/fix-srt.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding(SupportsShouldProcess)] 2 | param( 3 | [Parameter(Position=0, ValueFromPipeline, ValueFromPipelineByPropertyName)] 4 | [ValidateNotNullOrEmpty()] 5 | [SupportsWildcards()] 6 | [string[]] 7 | $Path = $PWD, 8 | [Parameter(Position=1)] 9 | [string] 10 | $Cache 11 | ) 12 | 13 | filter Get-FileEncoding { 14 | [CmdletBinding()] 15 | [OutputType([System.Text.Encoding])] 16 | param( 17 | [Parameter(Mandatory, Position=0, ValueFromPipelineByPropertyName)] 18 | [Alias("PSPath")] 19 | [ValidateNotNullOrEmpty()] 20 | [string[]] 21 | $LiteralPath 22 | ) 23 | $LiteralPath | ForEach-Object { 24 | $bom = Get-Content -LiteralPath $_ -AsByteStream -ReadCount 4 -TotalCount 4 25 | # UTF-8 format without Byte Order Mark 26 | if ($bom[0] -eq 0xef -and $bom[1] -eq 0xbb -and $bom[2] -eq 0xbf ) 27 | { return [System.Text.Encoding]::UTF8 } 28 | # UTF-16 format using the big-endian byte order 29 | if ($bom[0] -eq 0xfe -and $bom[1] -eq 0xff) 30 | { return [System.Text.Encoding]::BigEndianUnicode } 31 | # UTF-16 format using the little-endian byte order 32 | if ($bom[0] -eq 0xff -and $bom[1] -eq 0xfe) 33 | { return [System.Text.Encoding]::Unicode } 34 | # UTF-32 format using the big-endian byte order 35 | if ($bom[0] -eq 0 -and $bom[1] -eq 0 -and $bom[2] -eq 0xfe -and $bom[3] -eq 0xff) 36 | { return [System.Text.UTF32Encoding]::new($true, $true) } 37 | # UTF-32 format using the little-endian byte order 38 | if ($bom[0] -eq 0xfe -and $bom[1] -eq 0xff -and $bom[2] -eq 0 -and $bom[3] -eq 0) 39 | { return [System.Text.Encoding]::UTF32 } 40 | # UTF-7 format 41 | if ($bom[0] -eq 0x2b -and $bom[1] -eq 0x2f -and $bom[2] -eq 0x76 -and $bom[3] -in 0x38, 0x39, 0x2b, 0x2f) 42 | { return [System.Text.Encoding]::UTF7 } 43 | # UTF-8 format without Byte Order Mark (BOM) 44 | return [System.Text.UTF8Encoding]::new($false) 45 | } 46 | } 47 | 48 | $Cached = @{ } 49 | if ($Cache -and (Test-Path $Cache)) { 50 | Get-Content $Cache | ConvertFrom-Json | ForEach-Object { 51 | $Cached.$_ = $true 52 | } 53 | } 54 | 55 | Get-ChildItem -Path $Path -Include "*.srt" -Recurse -File ` 56 | | ForEach-Object { 57 | if ($Cached[$_.FullName]) { 58 | "Skipping $_" | Write-Debug 59 | } else { 60 | $encoding = $_ | Get-FileEncoding 61 | $content = $_ | Get-Content -Raw -Encoding $encoding 62 | $content -match "(?m)(\r?\n)?(\r?\n)?\z" | Out-Null 63 | if ($Matches.2) { 64 | "$_ is OK" | Write-Debug 65 | } else { 66 | $missing = if ($Matches.1) { 1 } else { 2 } 67 | $newline = if ($content -match "(?m)[^\r]\n") { "LF" } else { "CRLF" } 68 | "Fixing $_ ($newline)..." 69 | $newline = if ($newline -eq "LF") { "`n" } else { "`r`n" } 70 | if (!$WhatIfPreference) { 71 | $_ | Add-Content -Value ($newline * $missing) -NoNewline 72 | } 73 | } 74 | $Cached[$_.FullName] = $true 75 | } 76 | } 77 | 78 | if (!$WhatIfPreference -and $Cache) { 79 | $Cached.Keys | Sort-Object | ConvertTo-Json -AsArray | Set-Content -Path $Cache 80 | } 81 | -------------------------------------------------------------------------------- /.bin/misc/fix-permissions: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | home="${1:-$HOME}" user="${2:-$USER}" 3 | 4 | # Remove @eaDir directories 5 | find "$home" -depth -name @eaDir -type d -exec rm -rf '{}' \; 6 | find /volume1/media -depth -name @eaDir -type d -exec rm -rf '{}' \; 7 | 8 | # Reset home default permissions 9 | chown -R "$user:users" "$home" 10 | synoacltool -copy "$home/.." "$home" 11 | synoacltool -del "$home" 12 | synoacltool -add "$home" 'user:root:allow:rwxpdDaARWcCo:---n' 13 | synoacltool -add "$home" "user:$user:allow:rwxpdDaARWcCo:fd--" 14 | 15 | # Enforce home permissions (skipping .docker directory) 16 | find "$home" -mindepth 1 \( -path "$home/.docker" -type d -prune -o -type d -exec synoacltool -enforce-inherit '{}' \; \) 17 | find "$home" -path "$home/.docker" -type d -prune -o -not -type d -exec synoacltool -enforce-inherit '{}' \; 18 | 19 | # Set right permissions on .ssh directory in order to allow SSH keys authentication 20 | [ -d "$home/.ssh" ] && chmod 755 "$home" 21 | [ -d "$home/.ssh" ] && chmod -R a-x,u=rwX,go=rX "$home/.ssh" 22 | [ -f "$home/.ssh/authorized_keys" ] && chmod go-r "$home/.ssh/authorized_keys" 23 | [ -f "$home/.ssh/id_rsa" ] && chmod go-r "$home/.ssh/id_rsa" 24 | [ -f "$home/.ssh/id_rsa.ppk" ] && chmod go-r "$home/.ssh/id_rsa.ppk" 25 | 26 | # Set right permissions on .docker directory 27 | [ -d "$home/.docker" ] && chmod -R a-x,u=rwX,go=rX "$home/.docker" 28 | [ -d "$home/.docker/plex/Library" ] && chown root:root "$home/.docker/plex/Library" 29 | -------------------------------------------------------------------------------- /.bin/misc/throw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | throw() { 3 | local message="${1:-Unexpected error}" status=${2:-1} 4 | echo -e "\e[31m$message\e[39m" >&2 5 | exit $status 6 | } 7 | -------------------------------------------------------------------------------- /.bin/misc/upgrade-docker-completion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | home="${1:-$HOME}" 3 | 4 | version="$(\docker-compose --version | grep -oP '\d+(\.\d+)+')" 5 | echo "docker-compose version: $version" 6 | 7 | mkdir -p "$home/.bash_completion" 8 | curl "https://raw.githubusercontent.com/docker/compose/$version/contrib/completion/bash/docker-compose" -o "$home/.bash_completion/docker-compose" 9 | . "$home/.bash_completion/docker-compose" 10 | -------------------------------------------------------------------------------- /.bin/misc/upgrade-git-completion.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | home="${1:-$HOME}" 3 | 4 | version=$(git --version | cut -d ' ' -f 3) 5 | echo "git version: $version" 6 | 7 | mkdir -p "$home/.bash_completion" 8 | curl "https://raw.githubusercontent.com/git/git/v$version/contrib/completion/git-completion.bash" -o "$home/.bash_completion/git-completion.bash" 9 | . "$home/.bash_completion/git-completion.bash" 10 | curl "https://raw.githubusercontent.com/git/git/v$version/contrib/completion/git-prompt.sh" -o "$home/.bash_completion/git-prompt.sh" 11 | . "$home/.bash_completion/git-prompt.sh" 12 | -------------------------------------------------------------------------------- /.bin/nas: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ~/.bin/misc/throw 4 | 5 | LOG=~/.log/nas.log 6 | 7 | mkdir -p ~/.log 8 | echo -e "*** $(date) Start ***\n" > "$LOG" 9 | 10 | end() { 11 | echo -e "\n*** $(date) End ***" >> "$LOG" 12 | } 13 | trap end EXIT 14 | 15 | execute() { 16 | local message="$1" command="$2" 17 | echo -n "$message... " 18 | echo "*** $message..." >> "$LOG" 19 | local errorMessage 20 | errorMessage="$(eval "sudo $command 2>&1" | tee -a "$LOG"; (exit ${PIPESTATUS[0]}))" 21 | if [ $? -ne 0 ]; then 22 | echo failed | tee -a "$LOG" 23 | throw "$errorMessage" 24 | fi 25 | echo done | tee -a "$LOG" 26 | return 0 27 | } 28 | 29 | patch() { 30 | local file="$1" patch="$HOME/.config/$2" 31 | echo -n "Patching file $file... " 32 | echo "*** Patching file $file..." >> "$LOG" 33 | local errorMessage 34 | errorMessage="$(eval "sudo patch -N --dry-run \"$file\" < \"$patch\" 2>&1" | tee -a "$LOG"; (exit ${PIPESTATUS[0]}))" 35 | if [ $? -ne 0 ]; then 36 | if [ -z "$(echo "$errorMessage" | grep 'Skipping patch.')" ]; then 37 | echo failed | tee -a "$LOG" 38 | throw "$errorMessage" 39 | fi 40 | echo skipped | tee -a "$LOG" 41 | return 1 42 | fi 43 | eval "sudo patch -N \"$file\" < \"$patch\" 2>&1" >> "$LOG" 44 | echo done | tee -a "$LOG" 45 | return 0 46 | } 47 | 48 | processEntwareNg() { 49 | # https://github.com/Entware/Entware-ng/wiki/Install-on-Synology-NAS 50 | sudo mkdir -p /volume1/@entware-ng/opt /opt 51 | grep -qs ' /opt ' /proc/mounts || sudo mount -o bind /volume1/@entware-ng/opt /opt 52 | 53 | if [ -z "$(ls -A /volume1/@entware-ng/opt)" ]; then 54 | execute 'Installing Entware-ng' "$HOME/.bin/entware/install" 55 | sudo cp ~/.bin/entware/entware-startup.sh /usr/local/etc/rc.d/ 56 | echo '*** Please reboot the nas ***' | tee -a "$LOG" 57 | exit 58 | fi 59 | 60 | . /opt/etc/profile 61 | execute 'Updating Entware-ng' 'opkg update' 62 | execute 'Upgrading Entware-ng' 'opkg upgrade' 63 | 64 | # make sure basic packages are installed 65 | sudo opkg install fish less patch >> "$LOG" 66 | } 67 | 68 | sudo true || throw 'Permission denied' 69 | 70 | processEntwareNg 71 | # execute 'Fixing permissions' "$HOME/.bin/misc/fix-permissions \"$HOME\" \"$USER\"" 72 | patch /etc/ssh/sshd_config sshd_config.patch && echo '*** Please reboot the nas ***' | tee -a "$LOG" 73 | 74 | execute 'Upgrading bash git completion' "$HOME/.bin/misc/upgrade-git-completion.sh \"$HOME\"" 75 | execute 'Upgrading bash docker completion' "$HOME/.bin/misc/upgrade-docker-completion.sh \"$HOME\"" 76 | 77 | execute 'Upgrading docker containers' 'docker-upgrade docker' 78 | execute 'Upgrading media containers' 'docker-upgrade media' 79 | execute 'Restarting plex container' 'docker restart plex' 80 | execute 'Pruning docker build cache' 'docker builder prune --all --force' 81 | execute 'Pruning docker images' 'docker image prune --all --force' 82 | execute 'Pruning docker volumes' 'docker volume prune --force' 83 | -------------------------------------------------------------------------------- /.bin/pwsh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | args="$@" 3 | # e.g. -Debug -WhatIf 4 | 5 | sudo docker run -it --rm \ 6 | -e POWERSHELL_TELEMETRY_OPTOUT=1 \ 7 | -v "/:/mnt/nas" \ 8 | mcr.microsoft.com/powershell \ 9 | pwsh $args 10 | -------------------------------------------------------------------------------- /.bin/tvshows: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | log() { 4 | local messages="$@" 5 | [ -n "$LOG" ] && echo "$messages" | tee -a "$LOG" || echo "$messages" 6 | } 7 | 8 | show() { 9 | local title="$1" && shift && local messages="$@" 10 | [ -n "$LOG" ] && echo -e "\e[35m$title\e[39m" "$messages" | tee -a "$LOG" || echo "$title" "$messages" 11 | } 12 | 13 | throw() { 14 | local message="$1" 15 | [ -n "$LOG" ] && echo -e "\e[31m$message\e[39m" | tee -a "$LOG" >&2 || echo "$message" >&2 16 | exit 1 17 | } 18 | 19 | MEDIAHOME="$(realpath "$0" | xargs dirname)/.." 20 | MEDIAPATH="$MEDIAHOME/media/.env" 21 | [ -f "$MEDIAPATH" ] && MEDIAUSER="$(grep '^PUID=' "$MEDIAPATH" | cut -d'=' -f2)" || MEDIAUSER= 22 | [ -z "$MEDIAUSER" ] && throw 'Run ~/media/configure first' 23 | LOG="$MEDIAHOME/.log/tvshows.log" 24 | CACHE="$MEDIAHOME/.cache/tvshows" 25 | EXTENSIONS='\.(mkv|avi|divx|xvid|mov|wmv|mp4|mpg|mpeg|vob|iso|m4v|ts)' 26 | DEBUGTVSHOWS=10 27 | 28 | alias mediado="sudo -u '#$MEDIAUSER'" 29 | 30 | mediado mkdir -p "$MEDIAHOME/.log" 31 | mediado touch "$LOG" 32 | echo -e "*** $(date) Start ***\n" >> "$LOG" 33 | 34 | end() { 35 | echo -e "\n*** $(date) End ***\n" >> "$LOG" 36 | } 37 | trap end EXIT 38 | 39 | # Load & check tv shows configuration 40 | [ -f "$MEDIAHOME/.tvshows" ] || mediado cp "$MEDIAHOME/media/.tvshows.template" "$MEDIAHOME/.tvshows" 41 | . "$MEDIAHOME/.tvshows" 42 | if [ -z "$SERVER" ] || [ -z "$SOURCE" ] || [ -z "$DESTINATION" ] || [ -z "$TVSHOWS" ]; then 43 | throw '~/.tvshows is not properly configured' 44 | fi 45 | mediado ssh -o PasswordAuthentication=no "$SERVER" 'true' 46 | [ $? -eq 0 ] || throw "$SERVER: password is required" 47 | mediado ssh "$SERVER" "[ -d \"$SOURCE\" ]" || throw "$SERVER:$SOURCE: source path does not exist" 48 | [ -d "$DESTINATION" ] || throw "$DESTINATION: destination path does not exist" 49 | if [ -z "$MEDUSA_HOST" ] || [ -z "$MEDUSA_PORT" ] || [ -z "$MEDUSA_TOKEN" ] || [ -z "$MEDUSA_DOWNLOADS" ]; then 50 | throw '~/.tvshows is not properly configured (medusa)' 51 | fi 52 | 53 | # Display latest available tv shows 54 | if [ $DEBUGTVSHOWS -gt 0 ]; then 55 | show 'Latest available tv shows:' 56 | mediado ssh "$SERVER" "find \"$SOURCE\" -type f -printf '%T+ %P\\n' | sort -n | egrep -i \"^[^ ]+ ($TVSHOWS)/.+$EXTENSIONS$\" | tail -$DEBUGTVSHOWS" 2>&1 | tee -a "$LOG" 57 | log 58 | fi 59 | 60 | # Load latest downloaded tv show 61 | if [ ! -f "$CACHE" ]; then 62 | LATEST="$(mediado ssh "$SERVER" "find \"$SOURCE\" -type f -printf '%T@ %P\\n' | sort -n | egrep -i \"^[^ ]+ ($TVSHOWS)/.+$EXTENSIONS$\" | tail -1 | cut -f2- -d' '")" 63 | mediado echo -n "$LATEST" > "$CACHE" 64 | log 'First run, nothing will be downloaded' 65 | show 'Latest tv show:' "$LATEST" 66 | exit 67 | fi 68 | LATEST="$(cat "$CACHE")" 69 | show 'Latest downloaded tv show:' "$LATEST" 70 | 71 | # Downloads any new tv shows 72 | log 73 | show 'Downloading new tv shows...' 74 | mediado ssh "$SERVER" "find \"$SOURCE\" -type f -newer \"$SOURCE/$LATEST\" -printf '%P\\n' | egrep \"^($TVSHOWS)/.+$EXTENSIONS$\" | sort" \ 75 | | mediado rsync -hrtv -e ssh --files-from=- --stats --progress "$SERVER:$SOURCE/" "$DESTINATION/" 2>&1 | tee -a "$LOG" 76 | 77 | # Display latest downloaded tv shows 78 | if [ $DEBUGTVSHOWS -gt 0 ]; then 79 | log 80 | show 'Latest downloaded tv shows:' 81 | find "$DESTINATION" -type f -printf '%T@ %P\n' | sort -n | tail -$DEBUGTVSHOWS | cut -f2- -d' ' | tee -a "$LOG" 82 | fi 83 | 84 | # Save new latest downloaded tv show if any 85 | LATEST="$(find "$DESTINATION" -type f -printf '%T@ %P\n' | sort -n | tail -1 | cut -f2- -d' ')" 86 | if [ -n "$LATEST" ]; then 87 | mediado echo -n "$LATEST" > "$CACHE" 88 | log 89 | show 'New latest downloaded tv show:' "$LATEST" 90 | fi 91 | 92 | # Set permissions of downloaded tv shows 93 | log 94 | show 'Setting permissions of downloaded tv shows...' 95 | sudo true || throw 'Permission denied' 96 | sudo find "$DESTINATION" -exec synoacltool -enforce-inherit "{}" \; 97 | 98 | # Trigger Medusa post-processing 99 | log 100 | show 'Triggering Medusa post-processing...' 101 | ENCODED_PATH="$(python -c "import urllib;print urllib.quote(raw_input())" <<< "$MEDUSA_DOWNLOADS")" 102 | URL="http://$MEDUSA_HOST:$MEDUSA_PORT/api/v1/$MEDUSA_TOKEN/?cmd=postprocess&type=manual&path=$ENCODED_PATH&process_method=move&force_replace=0&is_priority=0&delete_files=1&return_data=1" 103 | RESPONSE="$(curl -fSs "$URL")" 104 | [ $? -eq 0 ] || throw 'medusa post-processing failed' 105 | python -c "import json;print json.loads(raw_input())['data']" <<< "$RESPONSE" 2>&1 | tee -a "$LOG" 106 | -------------------------------------------------------------------------------- /.config/fish/config.fish: -------------------------------------------------------------------------------- 1 | set -l paths /usr/syno/sbin /usr/syno/bin /usr/local/sbin /usr/local/bin ~/.bin 2 | for path in $paths 3 | if test -d $path 4 | if not contains -- $path $PATH 5 | set PATH $PATH $path 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /.config/fish/functions/df.fish: -------------------------------------------------------------------------------- 1 | function df 2 | command df -h $argv 3 | end 4 | -------------------------------------------------------------------------------- /.config/fish/functions/du.fish: -------------------------------------------------------------------------------- 1 | function du 2 | command du -h $argv 3 | end 4 | -------------------------------------------------------------------------------- /.config/sshd_config.patch: -------------------------------------------------------------------------------- 1 | --- /etc/ssh/sshd_config 2 | +++ /etc/ssh/sshd_config 3 | @@ -43,3 +43,4 @@ 4 | #LoginGraceTime 2m 5 | -#PermitRootLogin yes 6 | +PermitRootLogin no 7 | +DenyUsers admin 8 | #StrictModes yes 9 | @@ -99,3 +100,3 @@ 10 | #AllowAgentForwarding yes 11 | -AllowTcpForwarding no 12 | +AllowTcpForwarding yes 13 | #GatewayPorts no 14 | -------------------------------------------------------------------------------- /.gitconfig: -------------------------------------------------------------------------------- 1 | [user] 2 | name = Thomas Démoulins 3 | email = tdemoulins@gmail.com 4 | [alias] 5 | br = branch 6 | ci = commit 7 | cia = commit --all 8 | cim = commit --message 9 | ciam = commit --all --message 10 | amend = commit --all --amend --no-edit 11 | co = checkout 12 | cob = !git checkout -b $1 && git push --set-upstream origin 13 | main = !git checkout main && git status --short --branch 14 | master = !git checkout master && git status --short --branch 15 | cp = cherry-pick 16 | has = branch --all --contains 17 | hist = log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short 18 | lgg = !git log --graph --decorate=short --abbrev-commit --numstat --date=relative -4 19 | st = status --short --branch 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore most root stuff 2 | /.* 3 | !/.bin/ 4 | /home/ 5 | 6 | # Ignore configuration files 7 | .env 8 | 9 | -------------------------------------------------------------------------------- /.profile: -------------------------------------------------------------------------------- 1 | # 2 | # ~/.bash_profile 3 | # 4 | 5 | [[ -d ~/.bin ]] && export PATH="$PATH:$HOME/.bin" 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Thomas Démoulins 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Synology NAS Configuration 2 | 3 | This repository contains a bunch of scripts to help configure, maintain and operate a [Synology NAS](https://www.synology.com/products) ([DS218+](https://www.synology.com/products/DS218+) in my case). 4 | 5 | ## Features 6 | 7 | - Provide `.bashrc` and `.profile` configuration 8 | - Install [Entware-ng](https://entware.net/), a software repository for network attached storages, routers and other embedded devices 9 | - Install [fish](https://fishshell.com/), a smart and user-friendly command line shell 10 | - Change some permissions 11 | - Allow to use SSH keys 12 | - Change default OpenSSH server configuration 13 | - Deny root and admin login 14 | - Allow TCP forwarding 15 | - Install [Homepage](https://github.com/gethomepage/homepage) ([docker](https://github.com/gethomepage/homepage/pkgs/container/homepage), [service](http://nassau:9080/)), a highly customizable homepage with Docker and service API integrations 16 | - Install [Portainer](https://github.com/portainer/portainer) ([docker](https://hub.docker.com/r/portainer/portainer-ce), [service](http://nassau:9000/)), a lightweight service delivery platform for containerized applications 17 | - Install [SSLH](https://github.com/yrutschle/sslh) ([docker](https://hub.docker.com/r/oorabona/sslh), [service](https://nassau:44322/)), an applicative protocol multiplexer 18 | - Install [Wireguard](https://www.wireguard.com/) ([docker](https://hub.docker.com/r/linuxserver/wireguard)), an extremely simple yet fast and modern VPN 19 | - Install [Plex](https://www.plex.tv/) ([docker](https://hub.docker.com/r/plexinc/pms-docker), [service](http://nassau:32400/), [web](https://app.plex.tv/)), brings together all the media that matters to you 20 | - Install [Plex Webhook for BetaSeries](https://github.com/Thilas/plex-betaseries-webhook) ([docker](https://hub.docker.com/r/thilas/plex-betaseries-webhook), [service](http://nassau:12000/)), a Plex webhook to mark series and movies as watched on BetaSeries 21 | - Install [Tautulli](https://github.com/Tautulli/Tautulli) ([docker](https://hub.docker.com/r/tautulli/tautulli), [service](http://nassau:8181/)), a Python based monitoring and tracking tool for Plex Media Server 22 | - Install [Medusa](https://github.com/pymedusa/Medusa) ([docker](https://hub.docker.com/r/linuxserver/medusa), [service](http://nassau:8081/)), an automatic video library manager for TV shows 23 | - Install [Radarr](https://github.com/Radarr/Radarr) ([docker](https://hub.docker.com/r/linuxserver/radarr), [service](http://nassau:7878/)), a movie organizer/manager for usenet and torrent users 24 | - Install [Sonarr](https://github.com/Sonarr/Sonarr) ([docker](https://hub.docker.com/r/linuxserver/sonarr), [service](http://nassau:8989/)), a smart PVR for newsgroup and bittorrent users 25 | - Install [Bazarr](https://github.com/morpheus65535/bazarr) ([docker](https://hub.docker.com/r/linuxserver/bazarr), [service](http://nassau:6767/)), a companion application to Sonarr and Radarr that manages and downloads subtitles 26 | - Install [Komga](https://github.com/gotson/komga) ([docker](https://hub.docker.com/r/gotson/komga), [service](http://nassau:8080/)), a media server for comics/mangas/BDs/magazines/eBooks with API, OPDS, Kobo Sync and KOReader Sync support 27 | 28 | ## Scripts 29 | 30 | ### `build-wireguard` 31 | 32 | `build-wireguard` aims at building a valid SPK package that provides an up-to-date wireguard module to the nas. Complete instructions: [Wireguard SPK for your Synology NAS](https://www.blackvoid.club/wireguard-spk-for-your-synology-nas/). 33 | 34 | ### `configure` 35 | 36 | Each docker directory (`~/docker` and `~/media`) has a `configure` script that allows to configure the related `docker-compose.yml` file. It must be run once before starting the containers. They can be started using `nas` script (see below) or `docker-upgrade [docker|media]` 37 | 38 | ### `fix-plex-transcoder` 39 | 40 | `fix-plex-transcoder` grants the required rights on `/dev/dri` for Plex [Hardware-Accelerated Streaming](https://support.plex.tv/articles/115002178853-using-hardware-accelerated-streaming/) feature to work correctly. 41 | 42 | This script must be triggered from DSM Task Scheduler on boot-up using the `root` account in order to restore the permissions after a reboot. 43 | 44 | ### `fix-srt` 45 | 46 | `fix-srt` allows to fix `.srt` subtitle files so they work correctly with Plex. Basically, it ensures that all files end with at least 2 empty lines. 47 | 48 | ### `nas` 49 | 50 | `nas` installs/upgrades/fixes/starts all features mentioned above. Logs are available in `~/.log/nas.log`. 51 | 52 | ### `tvshows` 53 | 54 | `tvshows` allows to download automatically new tv shows from a remote server, fix their permissions (using `sudo`) and send them to Medusa's post-processing. Configuration is located in `~/.tvshows`. Logs are available in `~/.log/tvshows.log`. 55 | 56 | `tvshows` automatically impersonates the user configured for media services so the script can be run from root as well as from the configured user. It allows to easily schedule `tvshows`. 57 | 58 | ## Media 59 | 60 | By default, all media services (Plex, Plex Webhook for BetaSeries, Tautulli, Medusa, Radarr, Bazarr and Komga) use the following locations: 61 | 62 | - `~/.docker`: contains the configuration of each service 63 | - `/volume1/media`: contains all media files (i.e. `books`, `comics`, `movies`, `music`, `tvshows`) 64 | 65 | This `media` directory is customizable (thanks to the `configure` script) but it must be created using DSM before configuring docker containers. All required subdirectories will then be created automatically, as well as `downloads/movies` and `downloads/tvshows` subdirectories. 66 | 67 | ## Resources 68 | 69 | - [Install on Synology NAS](https://github.com/Entware/Entware-ng/wiki/Install-on-Synology-NAS) 70 | - [SSH Key Authentication](https://help.ubuntu.com/community/SSH/OpenSSH/Keys) 71 | - [WireGuard support for Synology NAS](https://github.com/runfalk/synology-wireguard) 72 | - [How to Control Synology DSM Services via Terminal / SSH](https://tech.setepontos.com/2018/03/25/control-synology-dsm-services-via-terminal-ssh/) 73 | - [Plex on Docker on Synology: enabling Hardware Transcoding](https://medium.com/@MrNick4B/plex-on-docker-on-synology-enabling-hardware-transcoding-fa017190cad7) 74 | - [SRT subtitles on Chromecast constistently disappear a few minutes before the movie end](https://forums.plex.tv/t/srt-subtitles-on-chromecast-constistently-disappear-a-few-minutes-before-the-movie-end/510491/36) 75 | -------------------------------------------------------------------------------- /docker/.env.template: -------------------------------------------------------------------------------- 1 | # Shared configuration 2 | CONFIG=$HOME/.docker 3 | # Set the timezone inside the containers 4 | TZ=$(find /usr/share/zoneinfo -type f ! -name posixrules -exec cmp -s {} /etc/localtime \; -print | sed -e 's@.*/zoneinfo/@@' | head -n1) 5 | # User / Group identifiers 6 | PUID=$(id -u) 7 | PGID=$(id -g) 8 | 9 | # SSLH configuration 10 | # SSLH_HOST=$(hostname --fqdn) 11 | # SSLH_PORT=44322 12 | # SSLH_SSH_HOST=$(hostname --fqdn) 13 | # SSLH_SSH_PORT=22 14 | # SSLH_HTTPS_HOST=$(hostname --fqdn) 15 | # SSLH_HTTPS_PORT=443 16 | 17 | # Wireguard configuration 18 | # WIREGUARD_URL=auto 19 | # WIREGUARD_PORT=51820 20 | # WIREGUARD_PEERS=1 21 | # WIREGUARD_PEERDNS=auto 22 | # WIREGUARD_SUBNET=10.13.13.0 23 | # WIREGUARD_ALLOWEDIPS=0.0.0.0/0 24 | -------------------------------------------------------------------------------- /docker/configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd "$(realpath "$0" | xargs dirname)" 4 | ../.bin/docker/configure "$PWD" || exit 5 | 6 | . ./.env 7 | 8 | echo 'Creating missing directories...' 9 | for directory in homepage portainer wireguard; do 10 | sudo -u "#$PUID" -- mkdir -p "$CONFIG/$directory" 11 | done 12 | 13 | echo 'Containers configured successfully' 14 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2.4" 2 | services: 3 | 4 | homepage: 5 | # https://github.com/gethomepage/homepage/pkgs/container/homepage 6 | image: ghcr.io/gethomepage/homepage 7 | container_name: homepage 8 | hostname: homepage 9 | ports: 10 | - 3000:3000 11 | volumes: 12 | - /var/run/docker.sock:/var/run/docker.sock # For docker integrations 13 | - $CONFIG/homepage:/app/config # Make sure your local config directory exists 14 | restart: unless-stopped 15 | 16 | portainer: 17 | # https://hub.docker.com/r/portainer/portainer-ce 18 | image: portainer/portainer-ce 19 | container_name: portainer 20 | hostname: portainer 21 | ports: 22 | - 8000:8000 23 | - 9000:9000 24 | volumes: 25 | - /var/run/docker.sock:/var/run/docker.sock 26 | - $CONFIG/portainer:/data 27 | restart: unless-stopped 28 | 29 | # sslh: 30 | # # https://github.com/yrutschle/sslh 31 | # build: ./sslh 32 | # image: sslh 33 | # container_name: sslh 34 | # hostname: sslh 35 | # network_mode: host 36 | # command: --listen $SSLH_HOST:$SSLH_PORT --ssh $SSLH_SSH_HOST:$SSLH_SSH_PORT --tls $SSLH_HTTPS_HOST:$SSLH_HTTPS_PORT -v 37 | # restart: unless-stopped 38 | 39 | # wireguard: 40 | # # https://hub.docker.com/r/linuxserver/wireguard 41 | # image: linuxserver/wireguard 42 | # container_name: wireguard 43 | # hostname: wireguard 44 | # cap_add: 45 | # - NET_ADMIN 46 | # environment: 47 | # - PUID=$PUID 48 | # - PGID=$PGID 49 | # - TZ=$TZ 50 | # - SERVERURL=$WIREGUARD_URL 51 | # - SERVERPORT=$WIREGUARD_PORT 52 | # - PEERS=$WIREGUARD_PEERS 53 | # - PEERDNS=$WIREGUARD_PEERDNS 54 | # - INTERNAL_SUBNET=$WIREGUARD_SUBNET 55 | # - ALLOWEDIPS=$WIREGUARD_ALLOWEDIPS 56 | # - PERSISTENTKEEPALIVE_PEERS= 57 | # - LOG_CONFS=true 58 | # ports: 59 | # - 51820:51820/udp 60 | # volumes: 61 | # - $CONFIG/wireguard:/config 62 | # restart: unless-stopped 63 | -------------------------------------------------------------------------------- /docker/sslh/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian 2 | 3 | RUN \ 4 | apt-get update && \ 5 | DEBIAN_FRONTEND=noninteractive apt-get -y --no-install-recommends install sslh && \ 6 | apt-get clean && \ 7 | rm -rf /var/lib/apt/lists/* 8 | 9 | ENTRYPOINT [ "sslh", "--foreground" ] 10 | -------------------------------------------------------------------------------- /media/.env.template: -------------------------------------------------------------------------------- 1 | # Shared configuration 2 | CONFIG=$HOME/.docker 3 | DATA=/volume1/media 4 | # Set the timezone inside the containers 5 | TZ=$(find /usr/share/zoneinfo -type f ! -name posixrules -exec cmp -s {} /etc/localtime \; -print | sed -e 's@.*/zoneinfo/@@' | head -n1) 6 | # User / Group identifiers 7 | PUID=$(id -u) 8 | PGID=$(id -g) 9 | 10 | # Plex configuration 11 | PLEX_MEM_LIMIT=3g 12 | # The claim token for the server to obtain a real server token: https://www.plex.tv/claim 13 | # PLEX_CLAIM= 14 | # This variable defines the additional IPs on which the server may be be found 15 | # ADVERTISE_IP=http://:32400/ 16 | # IP/netmask entries which allow access to the server without requiring authorization 17 | # ALLOWED_NETWORKS= 18 | 19 | # Plex webhook for BetaSeries configuration 20 | WEBHOOK_SERVER_URL= 21 | BETASERIES_CLIENTID= 22 | BETASERIES_CLIENTSECRET= 23 | 24 | # Medusa configuration 25 | MEDUSA_MEM_LIMIT=512m 26 | 27 | # Komga configuration 28 | # Set the maximum memory 29 | # KOMGA_JAVA_TOOL_OPTIONS=-Xmx1g 30 | # Set an optional extra volume 31 | # KOMGA_EXTRA_VOLUME=/path/to/files:/files 32 | -------------------------------------------------------------------------------- /media/.tvshows.template: -------------------------------------------------------------------------------- 1 | # SSH server & user to download tv shows from (e.g. user@server) 2 | # SSH key required for the specified user 3 | SERVER= 4 | # Path to tv shows on remote server 5 | SOURCE=/path/to/tvshows 6 | # Path to downloaded tv shows on current server 7 | DESTINATION=/volume1/media/downloads/tvshows 8 | # Pipe-separated & regex-escaped list of TV shows to downloads (e.g. "Doctor Who \(2005\)|Game of Thrones") 9 | TVSHOWS= 10 | 11 | # Path to downloads on remote server 12 | DL_SOURCE=/path/to/tvshows 13 | # Path to downloads on current server 14 | DL_DESTINATION=/volume1/media/downloads 15 | 16 | # Medusa settings 17 | MEDUSA_HOST=localhost 18 | MEDUSA_PORT=8081 19 | MEDUSA_TOKEN= 20 | MEDUSA_DOWNLOADS=/downloads 21 | -------------------------------------------------------------------------------- /media/configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd "$(realpath "$0" | xargs dirname)" 4 | ../.bin/docker/configure "$PWD" || exit 5 | 6 | . ./.env 7 | 8 | echo 'Creating missing directories...' 9 | for directory in plex medusa radarr sonarr bazarr tautulli komga; do 10 | sudo -u "#$PUID" -- mkdir -p "$CONFIG/$directory" 11 | done 12 | for directory in books comics downloads/movies downloads/tvshows movies music tvshows; do 13 | sudo -u "#$PUID" -- mkdir -p "$DATA/$directory" 14 | done 15 | 16 | echo 'Containers configured successfully' 17 | -------------------------------------------------------------------------------- /media/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2.4" 2 | services: 3 | 4 | plex: 5 | # https://hub.docker.com/r/plexinc/pms-docker 6 | image: plexinc/pms-docker:plexpass 7 | container_name: plex 8 | hostname: plex 9 | depends_on: 10 | - plex-betaseries-webhook 11 | network_mode: host 12 | mem_limit: $PLEX_MEM_LIMIT 13 | devices: 14 | - /dev/dri:/dev/dri 15 | environment: 16 | - TZ=$TZ 17 | - PLEX_UID=$PUID 18 | - PLEX_GID=$PGID 19 | - PLEX_CLAIM=$PLEX_CLAIM 20 | - ADVERTISE_IP=$PLEX_ADVERTISE_IP 21 | - ALLOWED_NETWORKS=$PLEX_ALLOWED_NETWORKS 22 | volumes: 23 | - $CONFIG/plex:/config 24 | - $DATA/movies:/data/movies 25 | - $DATA/music:/data/music 26 | - $DATA/tvshows:/data/tvshows 27 | - /tmp:/transcode 28 | restart: unless-stopped 29 | 30 | plex-betaseries-webhook: 31 | # https://hub.docker.com/r/thilas/plex-betaseries-webhook 32 | image: thilas/plex-betaseries-webhook 33 | container_name: plex-betaseries-webhook 34 | hostname: plex-betaseries-webhook 35 | environment: 36 | - SERVER_URL=$WEBHOOK_SERVER_URL 37 | - BETASERIES_CLIENTID=$BETASERIES_CLIENTID 38 | - BETASERIES_CLIENTSECRET=$BETASERIES_CLIENTSECRET 39 | ports: 40 | - 12000:12000 41 | restart: unless-stopped 42 | 43 | tautulli: 44 | # https://hub.docker.com/r/tautulli/tautulli 45 | image: tautulli/tautulli 46 | container_name: tautulli 47 | hostname: tautulli 48 | depends_on: 49 | - plex 50 | environment: 51 | - TZ=$TZ 52 | - PUID=$PUID 53 | - PGID=$PGID 54 | ports: 55 | - 8181:8181 56 | volumes: 57 | - $CONFIG/tautulli:/config 58 | - $CONFIG/plex/Library/Application Support/Plex Media Server/Logs:/plex_logs:ro 59 | restart: unless-stopped 60 | 61 | medusa: 62 | # https://hub.docker.com/r/linuxserver/medusa 63 | # build: ./medusa 64 | # image: medusa 65 | image: linuxserver/medusa 66 | container_name: medusa 67 | hostname: medusa 68 | depends_on: 69 | - plex 70 | mem_limit: $MEDUSA_MEM_LIMIT 71 | environment: 72 | - TZ=$TZ 73 | - PUID=$PUID 74 | - PGID=$PGID 75 | ports: 76 | - 8081:8081 77 | volumes: 78 | - $CONFIG/medusa:/config 79 | - $DATA/downloads/tvshows:/downloads 80 | - $DATA/tvshows:/tv 81 | restart: unless-stopped 82 | 83 | radarr: 84 | # https://hub.docker.com/r/linuxserver/radarr 85 | image: linuxserver/radarr 86 | container_name: radarr 87 | hostname: radarr 88 | depends_on: 89 | - plex 90 | environment: 91 | - TZ=$TZ 92 | - PUID=$PUID 93 | - PGID=$PGID 94 | ports: 95 | - 7878:7878 96 | volumes: 97 | - $CONFIG/radarr:/config 98 | - $DATA/downloads/movies:/downloads 99 | - $DATA/movies:/movies 100 | restart: unless-stopped 101 | 102 | sonarr: 103 | # https://hub.docker.com/r/linuxserver/sonarr 104 | image: linuxserver/sonarr 105 | container_name: sonarr 106 | hostname: sonarr 107 | depends_on: 108 | - plex 109 | environment: 110 | - TZ=$TZ 111 | - PUID=$PUID 112 | - PGID=$PGID 113 | ports: 114 | - 8989:8989 115 | volumes: 116 | - $CONFIG/sonarr:/config 117 | - $DATA/downloads/tvshows:/downloads 118 | - $DATA/tvshows:/tv 119 | restart: unless-stopped 120 | 121 | bazarr: 122 | # https://hub.docker.com/r/linuxserver/bazarr 123 | image: linuxserver/bazarr 124 | container_name: bazarr 125 | hostname: bazarr 126 | depends_on: 127 | - radarr 128 | - sonarr 129 | environment: 130 | - TZ=$TZ 131 | - PUID=$PUID 132 | - PGID=$PGID 133 | ports: 134 | - 6767:6767 135 | volumes: 136 | - $CONFIG/bazarr:/config 137 | - $DATA/movies:/movies 138 | - $DATA/tvshows:/tv 139 | restart: unless-stopped 140 | 141 | komga: 142 | # https://hub.docker.com/r/gotson/komga 143 | image: gotson/komga 144 | container_name: komga 145 | hostname: komga 146 | user: $PUID:$PGID 147 | environment: 148 | - TZ=$TZ 149 | - JAVA_TOOL_OPTIONS=$KOMGA_JAVA_TOOL_OPTIONS 150 | ports: 151 | - 25600:25600 152 | volumes: 153 | - $CONFIG/komga:/config 154 | - $DATA/books:/books 155 | - $DATA/comics:/comics 156 | - $KOMGA_EXTRA_VOLUME 157 | restart: unless-stopped 158 | -------------------------------------------------------------------------------- /media/medusa/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ghcr.io/linuxserver/medusa 2 | 3 | ADD https://raw.githubusercontent.com/Thilas/Medusa/master/ext/subliminal/providers/addic7ed.py /app/medusa/ext/subliminal/providers/addic7ed.py 4 | --------------------------------------------------------------------------------