-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathplex-optimize
276 lines (227 loc) · 11 KB
/
plex-optimize
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#!/bin/bash
######### CONFIGURATION #########
HOSTNAME=$(hostname) ## THE HOSTNAME OF THIS COMPUTER. USED IN FILE LIST FILENAME
WORK_DIR="./convert_work" ## PATH TO DIRECTORY IN WHICH THE FILE LISTS ARE STORED
FILELIST="${WORK_DIR}/list_${HOSTNAME}.txt" ## FULL PATH TO THE FILELIST FILE
CURRENT_CONVERSION="${WORK_DIR}/current_conversion_${HOSTNAME}.txt" ## FULL PATH TO THE FILE THAT STORES THE CURRENT CONVERSION JOB TITLE
PATHS=() # BASH ARRAY OF PATHS TO RECURSIVELY CHECK FOR VIDEO FILES
FILE_EXT=("avi" "mkv" "m4v" "flv" "mov" "wmv" "webm" "gif") # FILE EXTENSIONS TO SEARCH FOR
######### END CONFIGURATION #########
######### GENERATE THE FILE LIST #########
FIND_FILE_EXT=() # FILE EXTENSIONS WILL BE UPATED WITH FIND SYNTAX AND STORED IN A SEPARATE ARRAY
# LOOP THROUGH THE FILE EXTENSIONS AND UPDATE THEM TO BE IN FIND SYNTAX
for EXT in ${FILE_EXT[@]}
do
FIND_FILE_EXT+=("-iname *.${EXT}")
done
#### JOIN ALL OF THE FIND FILE EXT ELEMENTS TOGETHER IN FIND SYNTAX ####
delim=$' -o ' # DELIMITER IS THE OR OPERATOR
# MERGE ALL OF THE EXTENSIONS USING THE -O OPERATOR AND REPLACE FIND_FILE_EXT WITH THE NEW STRING
printf -v FIND_FILE_EXT "%s$delim" "${FIND_FILE_EXT[@]}"
# REMOVE THE TRAILING -O
FIND_FILE_EXT="${FIND_FILE_EXT%$delim}"
# CREATE THE WORK DIRECTORY
echo "Creating work directory..."
mkdir -p "$WORK_DIR"
# CREATE/TRUNCATE THE FILE LIST
echo "Creating and clearing file list..."
touch "$FILELIST"
truncate -s 0 "$FILELIST"
# POPULATE THE FILE LIST
echo "Building file list..."
for i in "${PATHS[@]}"
do
## SEARCHES ALL PATHS FOR EACH OF THE EXTENSIONS AND
## STORES THE FULL PATH FOR EACH ONE FOUND, ONE PER LINE
find "$i" \( $FIND_FILE_EXT \) -exec greadlink -f {} >> "$FILELIST" \;
done
FILE_COUNT=$(wc -l "$FILELIST" | awk '{print $1}')
CURRENT_INDEX=0
####### END FILE LIST BUILD #######
####### BEGIN TRANSCODE #######
echo "Transcoding..."
while IFS= read -r line ## ITERATE THROUGH THE FILE LIST, LINE BY LINE
do
## INCREMENT THE INDEX COUNT
CURRENT_INDEX=$((CURRENT_INDEX+1))
CURRENT_INDEX_PCT=$(echo "scale=2; ${CURRENT_INDEX}/${FILE_COUNT}*100" | bc)
## GET THE ORIGINAL DIRECTORY PATH
FILE_DIR=$(dirname "${line}")
## USE SUBSTITUTION TO BUILD THE DESTINATION FILE NAME
filename=$(basename -- "$line")
extension="${filename##*.}"
filename="${filename%.*}"
NEW_FILE="${FILE_DIR}/${filename}.mp4"
## if the process is killed prematurely, delete the destination file in progress so that it is started over on next invocation
trap "rm \"${NEW_FILE}\"; exit" SIGINT
## TRANSCODE IF FILE DOESN'T ALREADY EXIST
if [ ! -f "$NEW_FILE" ]; then
## SET THE WINDOW TITLE ON OH-MY-ZSH
DISABLE_AUTO_TITLE="true"
echo -e "\033];Transcoding ${line}\007"
## tell me what we're transcoding
echo "transcoding ${line}"
echo "${line} -> ${NEW_FILE}" > $CURRENT_CONVERSION
## capture the source file's metadata and use it to calculate the destination parameters
eval $(ffprobe -v quiet -show_format -of flat=s=_ -show_entries stream=height,width,nb_frames,nb_streams,duration,codec_name,bit_rate,channels,display_aspect_ratio,codec_type,size "$line");
audio_stream_index=false
video_stream_index=false
max_video_bitrate=$((5 * 1024))
video_filters=()
video_encode_codec="h264_videotoolbox" ## use h264_videotoolbox as the video codec to leverage the GPU. libx264 will use the CPU and will provide smaller files, but also longer encode times
stereo_audio_bitrate=441
surround_audio_bitrate=640
min_height=1080
audio_channels=false
audio_processing=("-c:a:0 aac")
metadata=("-metadata:s:a:0 title=Stereo" '-metadata:s:a:0 language=eng')
input_maps=()
stream_count=${format_nb_streams}
upscale=false ##changed to true when going from 720p to 1080p
## loop through the streams and identify the audio and video channels and properties
for stream in `seq 0 $((stream_count - 1))`;
do
codec_type_var="streams_stream_${stream}_codec_type"
codec_type=${!codec_type_var}
## if this is the first audio channel, update the mapping and capture the number of channels
if [[ $codec_type == "audio" && $audio_stream_index == false ]]; then
audio_stream_index=$stream
audio_channel_var="streams_stream_${stream}_channels"
audio_channels=${!audio_channel_var}
audio_bitrate_var="streams_stream_${stream}_bit_rate"
audio_bitrate=${!audio_bitrate_var}
detected_audio_bitrate=$audio_bitrate
## if no audio bitrate was detected, use the stereo audio bitrate
if [ $audio_bitrate == "N/A" ]; then
audio_bitrate=$stereo_audio_bitrate
else
audio_bitrate=$((audio_bitrate / 1000))
detected_audio_bitrate="${audio_bitrate}k"
fi
## for consistency, always map primary audio channel to 1 in destination file
input_maps[1]="-map 0:${audio_stream_index}"
fi
if [[ $codec_type == "video" && $video_stream_index == false ]]; then
video_stream_index=$stream
height_var="streams_stream_${video_stream_index}_height"
height=${!height_var}
aspect_ratio_var="streams_stream_${video_stream_index}_display_aspect_ratio"
aspect_ratio=${!aspect_ratio_var}
video_bitrate=${format_bit_rate}
video_bitrate=$((video_bitrate / 1000))
original_file_size=${format_size}
framerate_var="streams_stream_${video_stream_index}_r_frame_rate"
framerate=${!framerate_var}
## for consistency, always map video channel to 0 in destination file
input_maps[0]="-map 0:${video_stream_index}"
fi
done
# Limit the bitrate to the max_bitrate
detected_video_bitrate=$video_bitrate
if [ "$video_bitrate" -gt "$max_video_bitrate" ]; then
video_bitrate=$max_video_bitrate
fi
# Make sure the height is 1080 when the original video is 16:9 or 4:3
if [[ "$min_height" -ne "$height" && ($aspect_ratio == "16:9" || $aspect_ratio == "4:3") ]]; then
video_filters+=("scale=-1:$min_height:flags=lanczos")
# if we're upscaling to 1080p, let's tweak audo and video bitrates
if [ "$height" -lt "$min_height" ]; then
video_bitrate=$max_video_bitrate
upscale=true
fi
fi
## if the audio bitrate is higher than the stereo bitrate, lower it
if [ "$audio_bitrate" -gt "$stereo_audio_bitrate" ]; then
audio_processing+=("-b:a:0 ${stereo_audio_bitrate}k")
## if it isn't higher than the stereo bitrate, but this is a 720p to 1080p upscale, increase it
elif [ "$upscale" == true ]; then
audio_processing+=("-b:a:0 ${stereo_audio_bitrate}k")
## otherwise, enforce the original bitrate
else
audio_processing+=("-b:a:0 ${audio_bitrate}k")
fi
## if the audio track has fewer than two channels, make it stereo
if [ "$audio_channels" -lt 2 ]; then
## make the primary audio stereo
audio_processing+=("-ac:a:0 2")
fi
## if the audio track has more than two channels, create a stereo version and store this version as a secondary stream
if [ "$audio_channels" -gt 2 ]; then
## add another map to the original audio
input_maps+=("-map 0:${audio_stream_index}")
## make the primary audio stereo and encode the surround audio as ac3
audio_processing+=("-ac:a:0 2" '-c:a:1 ac3')
## if the audio bitrate is higher than the surround bitrate, lower it
if [ "$audio_bitrate" -gt "$surround_audio_bitrate" ]; then
audio_processing+=("-b:a:1 ${surround_audio_bitrate}k")
## if it isn't higher than the surround bitrate, but this is a 720p to 1080p upscale, increase it
elif [ "$upscale" == true ]; then
audio_processing+=("-b:a:1 ${surround_audio_bitrate}k")
## otherwise, enforce the original bitrate
else
audio_processing+=("-b:a:1 ${audio_bitrate}k")
fi
## set the metadata
metadata+=("-metadata:s:a:1 title=Surround" '-metadata:s:a:1 language=eng')
fi
## convert command arrays into strings
input_maps=$( IFS=$' '; echo "${input_maps[*]}" )
audio_processing=$( IFS=$' '; echo "${audio_processing[*]}" )
metadata=$( IFS=$' '; echo "${metadata[*]}" )
if [ ${#video_filters[@]} -ne 0 ]; then
video_filters=$( IFS=$','; echo "${video_filters[*]}" )
video_filters="-vf $video_filters"
else
video_filters=""
fi
video_filters_debug="None"
if [ ! -z "$video_filters" ]; then
video_filters_debug=$video_filters
fi
#### DEBUG DETAILS ###
echo -e "\n#####"
echo "Count: ${CURRENT_INDEX} of ${FILE_COUNT} (${CURRENT_INDEX_PCT}%)"
echo "File: ${line}"
echo "Audio channels: $audio_channels"
echo "Audio Bitrate: ${audio_bitrate}k (used in conversion calculations)"
echo "Audio Stream Index: ${audio_stream_index}"
echo "Detected Audio Bitrate: ${detected_audio_bitrate} (results from ffprobe)"
echo "Video Bitrate: ${video_bitrate}k (used in conversion calculations)"
echo "Detected Video Bitrate: ${detected_video_bitrate}k (results from ffprobe)"
echo "Video height: $height"
echo "Video aspect ratio: $aspect_ratio"
echo "Video stream index: ${video_stream_index}"
echo "Calculated filters: $video_filters_debug"
echo "Calculated input map: $input_maps"
echo "Calculated audio processing: $audio_processing"
echo "Calculated metadata: $metadata"
echo -e "\n"
echo "### Command ###"
echo -e "ffmpeg-bar -i \"${line}\" \ \n ${input_maps} \ \n -c:v ${video_encode_codec} \ \n -b:v ${video_bitrate}k \ \n -pix_fmt yuv420p \ \n ${video_filters} \ \n -sn \ \n ${audio_processing} \ \n ${metadata} \ \n \"${NEW_FILE}\""
echo -e "#####\n"
## transcode
ffmpeg-bar -i "$line" `## input the discovered source file` \
$input_maps `## handles input mapping for multichannel audio` \
-c:v "$video_encode_codec" \
-b:v "$video_bitrate"k `## use the calculated video bit rate based on the source` \
-pix_fmt yuv420p `## force the pixel format to yuv420p for quicktime compatibility` \
$video_filters `## plugs in built out filters` \
-sn `## strip subtitles in favor of external files` \
$audio_processing `## handles audio mapping when source material is 5.1` \
$metadata `## adds metadata` \
"$NEW_FILE" < /dev/null ## output to new file name
trash "$line"
eval $(ffprobe -v quiet -show_format -of flat=s=_ -show_entries stream=height,width,nb_frames,nb_streams,duration,codec_name,bit_rate,channels,display_aspect_ratio,codec_type,size "$NEW_FILE");
new_file_size=${format_size}
file_size_change=$(echo "scale=2; ${new_file_size}-${original_file_size}" | bc)
file_size_pct_change=$(echo "scale=2; ${file_size_change}/${original_file_size}*100" | bc)
file_size_change=$(echo "scale=2; ${file_size_change}/1000/1000" | bc)
#### DEBUG DETAILS ###
echo -e "\n#####"
echo "Complete"
echo "File size change: ${file_size_change}MB"
echo "File size pct change: ${file_size_pct_change}%"
echo -e "#####\n"
### END DEBUG ###
fi
done < "$FILELIST"