Updated 02152023-205251
https://davidblue.wtf/targetffmpeg
I'm gonna call this one Mastodon-related because basically any other method of getting a given video clip below the file limit (40MB, in mastodon.social's case,) drives me crazy. #shortcuts #video #automation #mac https://routinehub.co/shortcut/14270
Please note: this shortcut is macOS-only.
brew install ffmpeg
)This shortcut re-encodes single inputted video files to a specified file size target (in MB, per run) with ffmpeg, which it will then save to a new .mp4 file entitled [source filename]-[provided file size]MB.mp4
in a directory of your choosing via a Save File action.
At install, you will be asked to specify:
Photos
will ask you for a video file from iCloud Photos while Files
will use a standard Finder prompt.)Delete Immediately
option in the Delete Files action toggled on by default. This action is handling a (frankly, completely redundant) file - neither the source file nor the output from the Save File action will be deleted.#!/bin/bash
#
# Re-encode a video to a target size in MB.
# Example:
# ./this_script.sh video.mp4 15
T_SIZE="$2" # target size in MB
T_FILE="${1%.*}-$2MB.mp4" # filename out
# Original duration in seconds
O_DUR=$(\
ffprobe \
-v error \
-show_entries format=duration \
-of csv=p=0 "$1")
# Original audio rate
O_ARATE=$(\
ffprobe \
-v error \
-select_streams a:0 \
-show_entries stream=bit_rate \
-of csv=p=0 "$1")
# Original audio rate in KiB/s
O_ARATE=$(\
awk \
-v arate="$O_ARATE" \
'BEGIN { printf "%.0f", (arate / 1024) }')
# Target size is required to be less than the size of the original audio stream
T_MINSIZE=$(\
awk \
-v arate="$O_ARATE" \
-v duration="$O_DUR" \
'BEGIN { printf "%.2f", ( (arate * duration) / 8192 ) }')
# Equals 1 if target size is ok, 0 otherwise
IS_MINSIZE=$(\
awk \
-v size="$T_SIZE" \
-v minsize="$T_MINSIZE" \
'BEGIN { print (minsize < size) }')
# Give useful information if size is too small
if [[ $IS_MINSIZE -eq 0 ]]; then
printf "%s\n" "Target size ${T_SIZE}MB is too small!" >&2
printf "%s %s\n" "Try values larger than" "${T_MINSIZE}MB" >&2
exit 1
fi
# Set target audio bitrate
T_ARATE=$O_ARATE
# Calculate target video rate - MB -> KiB/s
T_VRATE=$(\
awk \
-v size="$T_SIZE" \
-v duration="$O_DUR" \
-v audio_rate="$O_ARATE" \
'BEGIN { print ( ( size * 8192.0 ) / ( 1.048576 * duration ) - audio_rate) }')
# Perform the conversion
ffmpeg \
-y \
-i "$1" \
-c:v libx264 \
-b:v "$T_VRATE"k \
-pass 1 \
-an \
-f mp4 \
/dev/null \
&& \
ffmpeg \
-i "$1" \
-c:v libx264 \
-b:v "$T_VRATE"k \
-pass 2 \
-c:a aac \
-b:a "$T_ARATE"k \
$T_FILE