├── README.md └── plexEncode.sh /README.md: -------------------------------------------------------------------------------- 1 | Project is no longer being developed. I recommend taking a look at this; https://github.com/HaveAGitGat/Tdarr 2 | 3 | ![plexEncode.sh](https://image.ibb.co/jmZzxH/plex_Encode_logo.png) 4 | ### Usage: 5 | #### Plex DVR 6 | Setup: Plex \ Settings \ Live TV & DVR \ DVR Settings \ Postprocessing Script = full path to the script 7 | #### Shell 8 | ``` 9 | plexEncode.sh 10 | 11 | = ffmpeg # .mkv file output. modify $ffmpeg_options to your specs. 12 | = handbrake # .m4v file output. modify $handbrake_options to your specs. 13 | = 0 # keep original input file. 14 | = 1 # delete original input file. 15 | ``` 16 | #### Shell Examples: 17 | ``` 18 | # encode a single file using default encoder and original file handling set in script 19 | plexEncode.sh "file" 20 | 21 | # encode a single file using handbrake, remove the original file 22 | plexEncode.sh "file" handbrake 1 23 | 24 | # encode a single file using ffmpeg, keep the original file 25 | plexEncode.sh "file" ffmpeg 0 26 | 27 | # loop thru a directory containing multiple .ts files 28 | for i in *.ts; do plexEncode.sh "$i" ; done 29 | ``` 30 | #### Sonarr 31 | ![Sonarr](https://image.ibb.co/f9zrcH/plex_Encode_sonarr.png) 32 | #### Radarr 33 | ![Radarr](https://image.ibb.co/eWAKWc/plex_Encode_radarr.png) 34 | 35 | #### Required: 36 | Script to add handbrake/ffmpeg/mediainfo/etc packages to your plex/sonarr/radarr dockers; https://gist.github.com/scrathe/ba29e50d95f71bfb207ccf6f74a425a7 37 | 38 | #### Sample Logging: 39 | ![example](https://image.ibb.co/igfF97/plex_Encode_example.png) 40 | -------------------------------------------------------------------------------- /plexEncode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | shopt -s extglob 3 | # plexEncode.sh 4 | # ██████╗ ██╗ ███████╗██╗ ██╗███████╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗ 5 | # ██╔══██╗██║ ██╔════╝╚██╗██╔╝██╔════╝████╗ ██║██╔════╝██╔═══██╗██╔══██╗██╔════╝ 6 | # ██████╔╝██║ █████╗ ╚███╔╝ █████╗ ██╔██╗ ██║██║ ██║ ██║██║ ██║█████╗ 7 | # ██╔═══╝ ██║ ██╔══╝ ██╔██╗ ██╔══╝ ██║╚██╗██║██║ ██║ ██║██║ ██║██╔══╝ 8 | # ██║ ███████╗███████╗██╔╝ ██╗███████╗██║ ╚████║╚██████╗╚██████╔╝██████╔╝███████╗ 9 | # ╚═╝ ╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ 10 | # Home: https://github.com/scrathe 11 | # Usage: 12 | # Plex DVR: Plex \ Settings \ Live TV & DVR \ DVR Settings \ Postprocessing Script = full path to the script 13 | # 14 | # Shell: plexEncode.sh 15 | # 16 | # = ffmpeg # .mkv file output. modify $ffmpeg_options to your specs. 17 | # = handbrake # .m4v file output. modify $handbrake_options to your specs. 18 | # = 0 # keep original input file. 19 | # = 1 # delete original input file. 20 | # 21 | # Sonarr: plexEncode.sh sonarr 22 | # 23 | # Settings \ Connect \ plexEncode \ Path = full path to the script 24 | # On Download = yes 25 | # On Upgrade = yes 26 | # Arguments = x ffmpeg 1 sonarr 27 | # 28 | # Radarr: plexEncode.sh radarr 29 | # 30 | # Settings \ Connect \ plexEncode \ Path = full path to the script 31 | # On Download = yes 32 | # On Upgrade = yes 33 | # Arguments = x ffmpeg 1 radarr 34 | # 35 | # Required: script to add handbrake/ffmpeg/mediainfo/etc packages to your plex/sonarr/radarr dockers; https://gist.github.com/scrathe/ba29e50d95f71bfb207ccf6f74a425a7 36 | 37 | plex_logfile="/media/scripts/logs/plexEncode.log" 38 | plex_lockfile="/media/scripts/logs/plexEncode.lock" # used to limit Plex DVR simultaneous encodes 39 | sonarr_logfile="/tv/scripts/logs/plexEncode.log" 40 | radarr_logfile="/movies/scripts/logs/plexEncode.log" 41 | 42 | # Optional: script to push notifications; https://github.com/jnwatts/pushover.sh 43 | # enable/disable at very bottom of script 44 | 45 | enable_push_notification="1" # 0 = disable 46 | plex_push="/media/scripts/pushover.sh" 47 | sonarr_push="/tv/scripts/pushover.sh" 48 | radarr_push="/movies/scripts/pushover.sh" 49 | 50 | # Shell Examples: 51 | # /media/scripts/plexEncode/plexEncode.sh "file" ffmpeg 0 # ffmpeg encoder, keep original input file 52 | # for i in *.ts; do /media/scripts/plexEncode/plexEncode.sh "$i" ; done # loop thru all *.ts files 53 | 54 | echo_log() { 55 | if [[ ! -e $logfile ]]; then 56 | touch "$logfile" 57 | fi 58 | echo "`date --iso-8601=seconds` $script: $file: ${*}" 59 | echo "`date --iso-8601=seconds` $script: $file: ${*}" >> "$logfile" 60 | } 61 | 62 | # check for $1 parameter = filename 63 | file="$1" 64 | if [[ -z $1 ]]; then 65 | echo "ERROR, no filename specified" 66 | exit 1 67 | fi 68 | 69 | # check for $2 parameter = encoder type. plex does not pass $2, set default below. 70 | encoder="$2" 71 | if [[ -z $encoder ]]; then 72 | # set default encoder 73 | # handbrake 74 | # ffmpeg 75 | encoder="ffmpeg" 76 | fi 77 | 78 | # check for $3 parameter = remove original input file. plex does not pass $3, set default below. 79 | remove_original=$3 80 | if [[ -z $3 ]]; then 81 | # set default behavior 82 | # 0 = keep input file 83 | # 1 = remove input file 84 | remove_original=1 85 | fi 86 | 87 | # check for mediainfo 88 | mediainfo=$(which mediainfo) 89 | if [[ $? != 0 ]]; then 90 | echo_log "ERROR, mediainfo missing" 91 | exit 1 92 | fi 93 | 94 | check_skipped_shows(){ 95 | # skip shows you don't archive 96 | regex="^(WGN Evening News|WGN Weekend Evening News|CNN News|CNN Tonight).*$" 97 | if [[ $file =~ $regex ]]; then 98 | echo_log "Skipping" 99 | exit 1 100 | fi 101 | } 102 | 103 | push_notification(){ 104 | if [[ ! -z $push_script ]]; then 105 | $push_script "$name | $speed min | $size %" 106 | fi 107 | } 108 | 109 | encode_file(){ 110 | height="$(mediainfo --Inform='Video;%Height%' "$file")" 111 | if [[ ! -z $height ]]; then 112 | height="[${height}p]" 113 | echo_log "Input Resolution: $height" 114 | fi 115 | 116 | # name = filename without extension 117 | name=$(echo ${file%.*}) 118 | # strip trailing space or hyphen 119 | name=$(echo $name | sed -r 's/[- ]{1,}$//g') 120 | 121 | if [[ $encoder = "handbrake" ]]; then 122 | # output file extension 123 | ext="m4v" 124 | atomic_file="atomicfile_${RANDOM}.$ext" 125 | 126 | # check for handbrake 127 | handbrake_cli=$(which HandBrakeCLI) 128 | if [[ $? != 0 ]]; then 129 | echo_log "ERROR, handbrake missing" 130 | exit 1 131 | fi 132 | 133 | # handbrake options https://github.com/HandBrake/HandBrake 134 | # https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Levels 135 | handbrake_options="-e x264 -q 20 --optimize --srt-lang eng --native-language eng --native-dub -f mp4 --decomb --loose-anamorphic --modulus 2 -m --x264-preset medium --h264-profile high --h264-level 4.1" 136 | 137 | # modify handbrake options to suit input file audio 138 | channels="$(mediainfo --Inform='Audio;%Channels%' "$file" | sed 's/[^0-9].*$//')" 139 | if [[ $channels > 2 ]]; then 140 | handbrake_options="$handbrake_options --aencoder ca_aac,copy:ac3,copy:dts,copy:dtshd" 141 | elif [ "$(mediainfo --Inform='General;%Audio_Format_List%' "$file" | sed 's| /.*||')" == 'AAC' ]; then 142 | handbrake_options="$handbrake_options --aencoder copy:aac" 143 | fi 144 | 145 | # encode 146 | start=$(date +%s%N) 147 | echo "" | $handbrake_cli -i "$file" -o "$atomic_file" $handbrake_options > /dev/null 2>&1 148 | if [[ $? != 0 ]]; then 149 | echo_log "ERROR, HandBrake exit code $?" 150 | rm "$atomic_file" 151 | rm "$lockfile" 152 | exit 1 153 | fi 154 | end=$(date +%s%N) 155 | elif [[ $encoder = "ffmpeg" ]]; then 156 | # output file extension 157 | ext="mkv" 158 | atomic_file="atomicfile_${RANDOM}.$ext" 159 | 160 | # check for ffmpeg 161 | ffmpeg=$(which ffmpeg) 162 | if [[ $? != 0 ]]; then 163 | echo_log "ERROR, ffmpeg missing" 164 | rm "$lockfile" 165 | exit 1 166 | fi 167 | 168 | # ffmpeg advanced options; https://ffmpeg.org/ffmpeg.html#Advanced-options 169 | # ffmpeg audio stream selection; https://ffmpeg.org/ffmpeg.html#Stream-selection 170 | # ffmpeg x265; https://trac.ffmpeg.org/wiki/Encode/H.265 171 | # ffmpeg_options="-map 0 -c:a copy -c:s copy -c:v libx265 -crf 18 -preset faster -pix_fmt yuv420p10le" 172 | ffmpeg_options="-map 0 -c:a copy -c:s copy -c:v libx265 -crf 18 -preset faster" 173 | 174 | # encode 175 | start=$(date +%s%N) 176 | ffmpeg -i "$file" $ffmpeg_options "$atomic_file" # > /dev/null 2>&1 177 | if [[ $? != 0 ]]; then 178 | echo_log "ERROR, ffmpeg exit code $?" 179 | rm "$atomic_file" 180 | rm "$lockfile" 181 | exit 1 182 | fi 183 | end=$(date +%s%N) 184 | fi 185 | 186 | # input file size 187 | # echo_log "`ls -l "$file"`" # debug 188 | isize=$(du -b "$file" | awk '{print $1}') 189 | isizeh=$(du -h "$file" | awk '{print $1}') 190 | 191 | if [[ $remove_original = 1 ]]; then 192 | echo_log "Removing Original File" 193 | rm "$file" 194 | if [[ $? != 0 ]]; then 195 | echo_log "WARNING, original file missing: $file" 196 | fi 197 | fi 198 | 199 | mv "$atomic_file" "$name.$ext" 200 | if [[ $? != 0 ]]; then 201 | echo_log "WARNING, atomic file missing: $atomic_file" 202 | fi 203 | 204 | # output file size 205 | osize=$(du -b "$name.$ext" | awk '{print $1}') 206 | osizeh=$(du -h "$name.$ext" | awk '{print $1}') 207 | # calculations 208 | speed=$(echo "scale=2; ($end - $start) / 1000000000 / 60" | bc) 209 | size=$(echo "scale=2; ($isize - $osize)/$isize * 100" | bc) 210 | 211 | echo_log "Input Size: $isizeh" 212 | echo_log "Output Size: $osizeh" 213 | echo_log "Encoding Speed: $speed min" 214 | echo_log "Size Change: $size %" 215 | echo_log "Output Filename: `basename "$name.$ext"`" 216 | } 217 | 218 | script=$(basename $0 .sh) 219 | 220 | case $4 in 221 | sonarr) 222 | # Sonarr execution 223 | # plexEncode.sh x ffmpeg 1 sonarr 224 | logfile="$sonarr_logfile" 225 | push_script="$sonarr_push" 226 | # $sonarr_episodefile_path is passed from Sonarr; https://github.com/Sonarr/Sonarr/wiki/Custom-Post-Processing-Scripts 227 | file="$sonarr_episodefile_path" 228 | DIR=$(dirname "$file") 229 | file=$(basename "$file") 230 | echo_log "Start: Sonarr" 231 | cd "$DIR" 232 | if [[ $? -ne 0 ]]; then 233 | echo_log "ERROR, cd '$DIR'" 234 | exit 1 235 | fi 236 | encode_file 237 | if [[ $enable_push_notification = "1" ]]; then 238 | push_notification 239 | fi 240 | echo_log "End: Sonarr" 241 | ;; 242 | radarr) 243 | # Radarr execution 244 | # plexEncode.sh x ffmpeg 1 radarr 245 | logfile="$radarr_logfile" 246 | push_script="$radarr_push" 247 | # $radarr_moviefile_path is passed from Radarr; https://github.com/Radarr/Radarr/wiki/Custom-Post-Processing-Scripts 248 | file="$radarr_moviefile_path" 249 | DIR=$(dirname "$file") 250 | file=$(basename "$file") 251 | echo_log "Start: Radarr" 252 | cd "$DIR" 253 | if [[ $? -ne 0 ]]; then 254 | echo_log "ERROR, cd '$DIR'" 255 | exit 1 256 | fi 257 | encode_file 258 | if [[ $enable_push_notification = "1" ]]; then 259 | push_notification 260 | fi 261 | echo_log "End: Radarr" 262 | ;; 263 | *) 264 | # Plex DVR or Shell execution 265 | logfile="$plex_logfile" 266 | push_script="$plex_push" 267 | DIR=$(dirname "$file") 268 | file=$(basename "$file") 269 | echo_log "Start: Shell" 270 | cd "$DIR" 271 | if [[ $? -ne 0 ]]; then 272 | echo_log "ERROR, cd '$DIR'" 273 | exit 1 274 | fi 275 | check_skipped_shows 276 | # prevent simultaneous encodes 277 | lockfile="$plex_lockfile" 278 | while [ -f "$lockfile" ]; do 279 | echo_log "Waiting for lockfile to clear" 280 | sleep 600 # seconds to wait 281 | done 282 | echo_log "Creating Lockfile" 283 | touch "$lockfile" 284 | encode_file 285 | if [[ $enable_push_notification = "1" ]]; then 286 | push_notification 287 | fi 288 | echo_log "Removing Lockfile" 289 | rm "$lockfile" 290 | echo_log "End: Shell" 291 | ;; 292 | esac 293 | --------------------------------------------------------------------------------