-
-
Notifications
You must be signed in to change notification settings - Fork 31
/
PlexPostProc.sh
executable file
·228 lines (197 loc) · 10.7 KB
/
PlexPostProc.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#!/bin/bash
#******************************************************************************
#******************************************************************************
#
# Plex DVR Post Processing Script
#
#******************************************************************************
#******************************************************************************
#
# Version: 2022.2.7 (forked by apassiou)
#
# Pre-requisites:
# ffmpeg (recommended) with libx265 or handbrakecli
#
# Usage:
# 'PlexPostProc.sh %1'
#
# Description:
# My script is currently pretty simple. Here's the general flow:
#
# 1. Creates a temporary directory in the /tmp directory for
# the show it is about to transcode.
#
# 2. Uses the selected encoder to transcode the original, very
# large MPEG2 format file to a smaller, more manageable H.264 mkv file
# (which can be streamed to various devices more easily).
#
# 3. Copies the file back to the original .grab folder for final processing
#
# Log:
# Single log is generated with timestamped transcodes.
# Note: Logs are not deleted, so some cleanup of the temp directory may be
# required, or a server reboot should clear this folder.
#
#******************************************************************************
TMPFOLDER="/tmp"
ENCODER="ffmpeg" # Encoder to use:
# "ffmpeg" for FFMPEG [DEFAULT]
# "handbrake" for HandBrake
# "nvtrans" for Plex Transcoder with NVENC support
RES="720" # Resolution to convert to:
# "480" = 480 Vertical Resolution
# "720" = 720 Vertical Resolution
# "1080" = 1080 Vertical Resolution
#**************************FFMPEG SPECIFIC SETTINGS****************************
AUDIO_CODEC="ac3" # From best to worst: libfdk_aac > libmp3lame/eac3/ac3 > aac. But libfdk_acc requires manual compilaton of ffmpeg. For OTA DVR standard acc should be enough.
AUDIO_BITRATE=96
VIDEO_CODEC="libx265" # Will need Ubuntu 18.04 LTS or later. Otherwise change to "libx264". On average libx265 should produce files half in size of libx264 without losing quality. It is more compute intensive, so transcoding will take longer.
VIDEO_QUALITY=26 #Lower values produce better quality. It is not recommended going lower than 18. 26 produces around 1Mbps video, 23 around 1.5Mbps.
VIDEO_FRAMERATE="24000/1001" #Standard US movie framerate, most US TV shows run at this framerate as well
DOWNMIX_AUDIO=2 #Number of channels to downmix to, set to 0 to turn off (leave source number of channels, but make sure to increase audio bitrate to accomodate all the needed bitrate. For 5.1 Id set no lower than 320). 1 == mono, 2 == stereo, 6 == 5.1
#******************************************************************************
# Do not edit below this line
#******************************************************************************
PPP_CHECK=0
#In order to avoid log file jumble, if another transcode process is active will wait to write to log until done
if ls "$TMPFOLDER/"*".ppplock" 1> /dev/null 2>&1; then
PPP_CHECK=1
fi
LOG_STRING_3="" #Placeholder as some portions dont use all log strings.
LOG_STRING_4=""
sleep 3
check_errs()
{
# Function. Parameter 1 is the return code
# Para. 2 is text to display on failure
if [ "${1}" -ne "0" ]; then
echo "ERROR # ${1} : ${2}" | tee -a $LOGFILE
exit ${1}
fi
}
if [ ! -z "$1" ]; then
# The if selection statement proceeds to the script if $1 is not empty.
if [ ! -f "$1" ]; then
fatal "$1 does not exist"
fi
# The above if selection statement checks if the file exists before proceeding.
FILENAME=$1 # %FILE% - Filename of original file
FILESIZE="$(ls -lh "$FILENAME" | awk '{ print $5 }')"
RANDFILENAME="$(mktemp)" # Base random name, will be used for cleanup
rm -f "$RANDFILENAME" #Cleanup mktemp artifact
TEMPFILENAME="$RANDFILENAME.mkv" # Temporary File Name for transcoding
LOCKFILE="$(mktemp)" # [WORKAROUND] Temporary File for blocking simultaneous scripts from ending early
rm -f "$LOCKFILE" #Clean up mktemp artifact
touch "$LOCKFILE.ppplock" # Create the lock file
check_errs $? "Failed to create temporary lockfile: $LOCKFILE.ppplock"
LOGFILE="$TMPFOLDER/plex_DVR_post_processing_log"
touch $LOGFILE # Create the log file
# Uncomment if you want to adjust the bandwidth for this thread
#MYPID=$$ # Process ID for current script
# Adjust niceness of CPU priority for the current process
#renice 19 $MYPID
# ********************************************************
# Starting Transcoding
# ********************************************************
LOG_STRING_1="\n$(date +"%Y%m%d-%H%M%S"): Transcoding $FILENAME to $TEMPFILENAME\n"
if [[ PPP_CHECK -eq 0 ]]; then
printf "$LOG_STRING_1" | tee -a $LOGFILE
fi
if [[ $ENCODER == "handbrake" ]]; then
LOG_STRING_2="You have selected HandBrake"
if [[ PPP_CHECK -eq 0 ]]; then
printf "$LOG_STRING_1" | tee -a $LOGFILE
fi
HandBrakeCLI -i "$FILENAME" -f mkv --aencoder copy -e qsv_h264 --x264-preset veryfast --x264-profile auto -q 16 --maxHeight $RES --decomb bob -o "$TEMPFILENAME"
check_errs $? "Failed to convert using Handbrake."
elif [[ $ENCODER == "ffmpeg" ]]; then
LOG_STRING_2="Using FFMPEG"
LOG_STRING_3=" [$FILESIZE -> "
if [[ PPP_CHECK -eq 0 ]]; then
printf "$LOG_STRING_2$LOG_STRING_3" | tee -a $LOGFILE
fi
start_time=$(date +%s)
if [[ $DOWNMIX_AUDIO -ne 0 ]]; then
ffmpeg -i "$FILENAME" -s hd$RES -c:v "$VIDEO_CODEC" -r "$VIDEO_FRAMERATE" -preset veryfast -crf "$VIDEO_QUALITY" -vf yadif -codec:a "$AUDIO_CODEC" -ac "$DOWNMIX_AUDIO" -b:a "$AUDIO_BITRATE"k -async 1 "$TEMPFILENAME"
else
ffmpeg -i "$FILENAME" -s hd$RES -c:v "$VIDEO_CODEC" -r "$VIDEO_FRAMERATE" -preset veryfast -crf "$VIDEO_QUALITY" -vf yadif -codec:a "$AUDIO_CODEC" -b:a "$AUDIO_BITRATE"k -async 1 "$TEMPFILENAME"
fi
end_time=$(date +%s)
seconds="$(( end_time - start_time ))"
minutes_taken="$(( seconds / 60 ))"
seconds_taken="$(( $seconds - (minutes_taken * 60) ))"
LOG_STRING_4="$(ls -lh $TEMPFILENAME | awk ' { print $5 }')] - [$minutes_taken min $seconds_taken sec]\n"
check_errs $? "Failed to convert using FFMPEG."
elif [[ $ENCODER == "nvtrans" ]]; then
export FFMPEG_EXTERNAL_LIBS="$(find ~/Library/Application\ Support/Plex\ Media\ Server/Codecs/ -name "libmpeg2video_decoder.so" -printf "%h\n")/"
check_errs $? "Failed to convert using smart Plex Transcoder (NVENC). libmpeg2video_decoder.so not found."
export LD_LIBRARY_PATH="/usr/lib/plexmediaserver:/usr/lib/plexmediaserver/lib/"
# Grab some dimension and framerate info so we can set bitrates
HEIGHT="$(/usr/lib/plexmediaserver/Plex\ Transcoder -i "$FILENAME" 2>&1 | grep "Stream #0:0" | perl -lane 'print $1 if /, \d{3,}x(\d{3,})/')"
FPS="$(/usr/lib/plexmediaserver/Plex\ Transcoder -i "$FILENAME" 2>&1 | grep "Stream #0:0" | perl -lane 'print $1 if /, (\d+(.\d+)*) fps/')"
if [[ -z "${HEIGHT}" ]]; then
# Failed to get dimensions of source... try a dumb transcode.
/usr/lib/plexmediaserver/Plex\ Transcoder -y -hide_banner -hwaccel nvdec -i "$FILENAME" -s hd$RES -c:v h264_nvenc -preset veryfast -c:a copy "$TEMPFILENAME"
check_errs $? "Failed to convert using simple Plex Transcoder (NVENC)."
else
# Smart transcode based on source dimensions and fps
# Bitrate vlaues (Mb). Assuming 1080i30 (ATSC max) has same needs as 720p60
# Assuming 60 fps needs 2x bitrate than 30 fps
MULTIPLIER=$(echo - | perl -lane "if (${FPS} < 59) {print 1.0} else {print 2.0}")
ABR=$(echo - | perl -lane "if (${HEIGHT} < 720) {print 1*${MULTIPLIER}} elsif (${HEIGHT} < 1080) {print 2*${MULTIPLIER}} else {print 4*${MULTIPLIER}}")
MBR=$(echo - | perl -lane "print ${ABR} * 1.5")
BUF=$(echo - | perl -lane "print ${MBR} * 2.0")
/usr/lib/plexmediaserver/Plex\ Transcoder -y -hide_banner -hwaccel nvdec -i "$FILENAME" -c:v h264_nvenc -b:v "${ABR}M" -maxrate:v "${MBR}M" -profile:v high -bf:v 3 -bufsize:v "${BUF}M" -preset:v hq -forced-idr:v 1 -c:a copy "$TEMPFILENAME"
check_errs $? "Failed to convert using smart Plex Transcoder (NVENC)."
fi
else
echo "Oops, invalid ENCODER string. Using Default [FFMpeg]." | tee -a $LOGFILE
ffmpeg -i "$FILENAME" -s hd$RES -c:v libx264 -preset veryfast -vf yadif -c:a copy "$TEMPFILENAME"
check_errs $? "Failed to convert using FFMPEG."
fi
# ********************************************************"
# Encode Done. Performing Cleanup
# ********************************************************"
LOG_STRING_5="$(date +"%Y%m%d-%H%M%S"): Finished transcode,"
if [[ PPP_CHECK -eq 0 ]]; then
printf "$LOG_STRING_4$LOG_STRING_5" | tee -a $LOGFILE
fi
rm -f "$FILENAME" # Delete original in .grab folder
check_errs $? "Failed to remove original file: $FILENAME"
mv -f "$TEMPFILENAME" "${FILENAME%.ts}.mkv" # Move completed tempfile to .grab folder/filename
check_errs $? "Failed to move converted file: $TEMPFILENAME"
rm -f "$LOCKFILE.ppplock"* # Delete the lockfile
check_errs $? "Failed to remove lockfile."
# [WORKAROUND] Wait for any other post-processing scripts to complete before exiting. So that plex doesnt start deleting grab files.
timeout_counter=120
while [ true ] ; do
if ls "$TMPFOLDER/"*".ppplock" 1> /dev/null 2>&1; then
if [[ $timeout_counter -eq 0 ]]; then
echo "Timeout reached, ending wait" | tee -a $LOGFILE
break
fi
if [[ timeout_counter -eq 120 ]]; then #Prevents log spam, after initial message simple '.' will be printed to log.
printf "\n$(date +"%Y%m%d-%H%M%S"): Another transcode running. Waiting." | tee -a $LOGFILE
else
printf "." | tee -a $LOGFILE
fi
timeout_counter=$((timeout_counter-1))
sleep 60
else
if [[ $timeout_counter -lt 119 ]]; then
echo "$(date +"%Y%m%d-%H%M%S"): It looks like all scripts are done running, exiting." | tee -a $LOGFILE
fi
break
fi
done
if [[ PPP_CHECK -eq 1 ]]; then
printf "$LOG_STRING_1$LOG_STRING_2$LOG_STRING_3$LOG_STRING_4$LOG_STRING_5" | tee -a $LOGFILE #Doing all together as to not stumble over multiple concurrent processes in log
fi
printf " exiting.\n" | tee -a $LOGFILE
else
echo "********************************************************" | tee -a $LOGFILE
echo "PlexPostProc by nebhead" | tee -a $LOGFILE
echo "Usage: $0 FileName" | tee -a $LOGFILE
echo "********************************************************" | tee -a $LOGFILE
fi
sleep 3 #Time for things to settle down, move commands to finish etc...