gistfile1.txt
· 4.6 KiB · Text
Bruto
#!/usr/bin/env bash
########################################################################
#
# This bash script was written by ChatGPT, but not this docblock.
# Everything works after testing, but not 2 functions:
# - Adding metadata to compressed MP4 file.
# - Checking if the file has already been compressed by this script.
#
########################################################################
set -u
SCRIPT_NAME="$(basename "$0")"
UNIQUE_PREFIX="r-"
########################################
# Encoding presets overview:
#
# none : balanced quality, AAC 96k
# vlog : spoken voice / crowd, copy audio
# tiny : max compression, AAC 64k
#
# Resolutions: 720p | 1080p
########################################
show_help() {
cat <<EOF
Usage:
$SCRIPT_NAME <file>
$SCRIPT_NAME [none|vlog|tiny] [720p|1080p] <file>
$SCRIPT_NAME batch
Batch:
Compresses all MP4 + MKV files using:
mode=none, resolution=1080p
Defaults:
mode=none
resolution=720p
EOF
exit 0
}
[[ "${1:-}" == "--help" ]] && show_help
MODE="none"
RES="720p"
# --- Batch mode ---
if [[ "${1:-}" == "batch" ]]; then
MODE="none"
RES="1080p"
shift || true
shopt -s nullglob
FILES=( *.mp4 *.mkv )
shopt -u nullglob
if [[ ${#FILES[@]} -eq 0 ]]; then
echo "No MP4 or MKV files found."
exit 0
fi
for f in "${FILES[@]}"; do
"$0" "$MODE" "$RES" "$f"
done
exit 0
fi
# --- Parse arguments ---
if [[ $# -eq 1 ]]; then
INPUT="$1"
elif [[ $# -eq 2 ]]; then
MODE="$1"
INPUT="$2"
elif [[ $# -eq 3 ]]; then
MODE="$1"
RES="$2"
INPUT="$3"
else
show_help
fi
[[ ! -f "$INPUT" ]] && { echo "File not found: $INPUT"; exit 1; }
BASENAME="${INPUT%.*}"
OUTPUT="${BASENAME}-r.mp4"
SUMMARY="${BASENAME}-summary.json"
########################################
# Resolution
########################################
case "$RES" in
720p) SCALE="scale=-2:720" ;;
1080p) SCALE="scale=-2:1080" ;;
*) echo "Invalid resolution"; exit 1 ;;
esac
########################################
# Mode settings
########################################
CQ=23
AUDIO_OPTS=("-c:a" "aac" "-b:a" "96k")
case "$MODE" in
vlog)
AUDIO_OPTS=("-c:a" "copy")
CQ=23
;;
tiny)
AUDIO_OPTS=("-c:a" "aac" "-b:a" "64k")
CQ=28
;;
none) ;;
*)
echo "Invalid mode"
exit 1
;;
esac
########################################
# Metadata detection (robust)
########################################
EXISTING_ID="$(ffprobe -v error \
-show_entries format_tags=compressed_by \
-of default=nk=1:nw=1 "$INPUT" 2>/dev/null || true)"
if [[ "$EXISTING_ID" =~ ^${UNIQUE_PREFIX}[A-Za-z0-9]+$ ]]; then
echo "Skipping already-compressed file: $INPUT"
exit 0
fi
########################################
# Generate unique ID
########################################
COMPRESS_ID="${UNIQUE_PREFIX}$(tr -dc A-Za-z0-9 </dev/urandom | head -c 12)"
########################################
# Encode
########################################
echo "Compressing: $INPUT > $OUTPUT"
if ! ffmpeg -y -hide_banner -loglevel error -stats \
-i "$INPUT" \
-map 0:v:0 -map 0:a? \
-vf "$SCALE" \
-c:v hevc_nvenc \
-preset p6 -tune hq \
-rc vbr -multipass fullres \
-profile:v main -pix_fmt yuv420p \
-spatial_aq 1 -temporal_aq 1 -aq-strength 8 \
-cq "$CQ" \
"${AUDIO_OPTS[@]}" \
-movflags +faststart \
-metadata compressed_by="$COMPRESS_ID" \
"$OUTPUT"; then
echo "FFmpeg failed for $INPUT"
exit 0
fi
########################################
# Size check
########################################
ORIG_SIZE=$(stat -c%s "$INPUT")
NEW_SIZE=$(stat -c%s "$OUTPUT")
if (( NEW_SIZE >= ORIG_SIZE )); then
echo "Result larger than original → skipping"
rm -f "$OUTPUT"
exit 0
fi
########################################
# JSON summary
########################################
HUMAN_ORIG=$(numfmt --to=iec --suffix=B "$ORIG_SIZE")
HUMAN_NEW=$(numfmt --to=iec --suffix=B "$NEW_SIZE")
OCCURRED=$(date +"%Y-%m-%d %H:%M:%S")
cat > "$SUMMARY" <<EOF
{
"input": "$(basename "$INPUT")",
"output": "$(basename "$OUTPUT")",
"original_bytes": $ORIG_SIZE,
"compressed_bytes": $NEW_SIZE,
"original": "$HUMAN_ORIG",
"compressed": "$HUMAN_NEW",
"reduction_percent": $(awk "BEGIN { printf \"%.2f\", (1-$NEW_SIZE/$ORIG_SIZE)*100 }"),
"occurred": "$OCCURRED",
"compression_id": "$COMPRESS_ID",
"settings": {
"codec": "hevc_nvenc",
"resolution": "$RES",
"mode": "$MODE",
"cq": $CQ,
"scale_filter": "$SCALE",
"audio": "${AUDIO_OPTS[*]}",
"preset": "p6",
"rate_control": "vbr",
"multipass": "fullres",
"pixel_format": "yuv420p",
"aq": {
"spatial": 1,
"temporal": 1,
"strength": 8
}
}
}
EOF
echo "Done ✓"
| 1 | #!/usr/bin/env bash |
| 2 | |
| 3 | ######################################################################## |
| 4 | # |
| 5 | # This bash script was written by ChatGPT, but not this docblock. |
| 6 | # Everything works after testing, but not 2 functions: |
| 7 | # - Adding metadata to compressed MP4 file. |
| 8 | # - Checking if the file has already been compressed by this script. |
| 9 | # |
| 10 | ######################################################################## |
| 11 | |
| 12 | set -u |
| 13 | |
| 14 | SCRIPT_NAME="$(basename "$0")" |
| 15 | UNIQUE_PREFIX="r-" |
| 16 | |
| 17 | ######################################## |
| 18 | # Encoding presets overview: |
| 19 | # |
| 20 | # none : balanced quality, AAC 96k |
| 21 | # vlog : spoken voice / crowd, copy audio |
| 22 | # tiny : max compression, AAC 64k |
| 23 | # |
| 24 | # Resolutions: 720p | 1080p |
| 25 | ######################################## |
| 26 | |
| 27 | show_help() { |
| 28 | cat <<EOF |
| 29 | Usage: |
| 30 | $SCRIPT_NAME <file> |
| 31 | $SCRIPT_NAME [none|vlog|tiny] [720p|1080p] <file> |
| 32 | $SCRIPT_NAME batch |
| 33 | |
| 34 | Batch: |
| 35 | Compresses all MP4 + MKV files using: |
| 36 | mode=none, resolution=1080p |
| 37 | |
| 38 | Defaults: |
| 39 | mode=none |
| 40 | resolution=720p |
| 41 | |
| 42 | EOF |
| 43 | exit 0 |
| 44 | } |
| 45 | |
| 46 | [[ "${1:-}" == "--help" ]] && show_help |
| 47 | |
| 48 | MODE="none" |
| 49 | RES="720p" |
| 50 | |
| 51 | # --- Batch mode --- |
| 52 | if [[ "${1:-}" == "batch" ]]; then |
| 53 | MODE="none" |
| 54 | RES="1080p" |
| 55 | shift || true |
| 56 | |
| 57 | shopt -s nullglob |
| 58 | FILES=( *.mp4 *.mkv ) |
| 59 | shopt -u nullglob |
| 60 | |
| 61 | if [[ ${#FILES[@]} -eq 0 ]]; then |
| 62 | echo "No MP4 or MKV files found." |
| 63 | exit 0 |
| 64 | fi |
| 65 | |
| 66 | for f in "${FILES[@]}"; do |
| 67 | "$0" "$MODE" "$RES" "$f" |
| 68 | done |
| 69 | exit 0 |
| 70 | fi |
| 71 | |
| 72 | # --- Parse arguments --- |
| 73 | if [[ $# -eq 1 ]]; then |
| 74 | INPUT="$1" |
| 75 | elif [[ $# -eq 2 ]]; then |
| 76 | MODE="$1" |
| 77 | INPUT="$2" |
| 78 | elif [[ $# -eq 3 ]]; then |
| 79 | MODE="$1" |
| 80 | RES="$2" |
| 81 | INPUT="$3" |
| 82 | else |
| 83 | show_help |
| 84 | fi |
| 85 | |
| 86 | [[ ! -f "$INPUT" ]] && { echo "File not found: $INPUT"; exit 1; } |
| 87 | |
| 88 | BASENAME="${INPUT%.*}" |
| 89 | OUTPUT="${BASENAME}-r.mp4" |
| 90 | SUMMARY="${BASENAME}-summary.json" |
| 91 | |
| 92 | ######################################## |
| 93 | # Resolution |
| 94 | ######################################## |
| 95 | case "$RES" in |
| 96 | 720p) SCALE="scale=-2:720" ;; |
| 97 | 1080p) SCALE="scale=-2:1080" ;; |
| 98 | *) echo "Invalid resolution"; exit 1 ;; |
| 99 | esac |
| 100 | |
| 101 | ######################################## |
| 102 | # Mode settings |
| 103 | ######################################## |
| 104 | CQ=23 |
| 105 | AUDIO_OPTS=("-c:a" "aac" "-b:a" "96k") |
| 106 | |
| 107 | case "$MODE" in |
| 108 | vlog) |
| 109 | AUDIO_OPTS=("-c:a" "copy") |
| 110 | CQ=23 |
| 111 | ;; |
| 112 | tiny) |
| 113 | AUDIO_OPTS=("-c:a" "aac" "-b:a" "64k") |
| 114 | CQ=28 |
| 115 | ;; |
| 116 | none) ;; |
| 117 | *) |
| 118 | echo "Invalid mode" |
| 119 | exit 1 |
| 120 | ;; |
| 121 | esac |
| 122 | |
| 123 | ######################################## |
| 124 | # Metadata detection (robust) |
| 125 | ######################################## |
| 126 | EXISTING_ID="$(ffprobe -v error \ |
| 127 | -show_entries format_tags=compressed_by \ |
| 128 | -of default=nk=1:nw=1 "$INPUT" 2>/dev/null || true)" |
| 129 | |
| 130 | if [[ "$EXISTING_ID" =~ ^${UNIQUE_PREFIX}[A-Za-z0-9]+$ ]]; then |
| 131 | echo "Skipping already-compressed file: $INPUT" |
| 132 | exit 0 |
| 133 | fi |
| 134 | |
| 135 | ######################################## |
| 136 | # Generate unique ID |
| 137 | ######################################## |
| 138 | COMPRESS_ID="${UNIQUE_PREFIX}$(tr -dc A-Za-z0-9 </dev/urandom | head -c 12)" |
| 139 | |
| 140 | ######################################## |
| 141 | # Encode |
| 142 | ######################################## |
| 143 | echo "Compressing: $INPUT > $OUTPUT" |
| 144 | |
| 145 | if ! ffmpeg -y -hide_banner -loglevel error -stats \ |
| 146 | -i "$INPUT" \ |
| 147 | -map 0:v:0 -map 0:a? \ |
| 148 | -vf "$SCALE" \ |
| 149 | -c:v hevc_nvenc \ |
| 150 | -preset p6 -tune hq \ |
| 151 | -rc vbr -multipass fullres \ |
| 152 | -profile:v main -pix_fmt yuv420p \ |
| 153 | -spatial_aq 1 -temporal_aq 1 -aq-strength 8 \ |
| 154 | -cq "$CQ" \ |
| 155 | "${AUDIO_OPTS[@]}" \ |
| 156 | -movflags +faststart \ |
| 157 | -metadata compressed_by="$COMPRESS_ID" \ |
| 158 | "$OUTPUT"; then |
| 159 | echo "FFmpeg failed for $INPUT" |
| 160 | exit 0 |
| 161 | fi |
| 162 | |
| 163 | ######################################## |
| 164 | # Size check |
| 165 | ######################################## |
| 166 | ORIG_SIZE=$(stat -c%s "$INPUT") |
| 167 | NEW_SIZE=$(stat -c%s "$OUTPUT") |
| 168 | |
| 169 | if (( NEW_SIZE >= ORIG_SIZE )); then |
| 170 | echo "Result larger than original → skipping" |
| 171 | rm -f "$OUTPUT" |
| 172 | exit 0 |
| 173 | fi |
| 174 | |
| 175 | ######################################## |
| 176 | # JSON summary |
| 177 | ######################################## |
| 178 | HUMAN_ORIG=$(numfmt --to=iec --suffix=B "$ORIG_SIZE") |
| 179 | HUMAN_NEW=$(numfmt --to=iec --suffix=B "$NEW_SIZE") |
| 180 | OCCURRED=$(date +"%Y-%m-%d %H:%M:%S") |
| 181 | |
| 182 | cat > "$SUMMARY" <<EOF |
| 183 | { |
| 184 | "input": "$(basename "$INPUT")", |
| 185 | "output": "$(basename "$OUTPUT")", |
| 186 | |
| 187 | "original_bytes": $ORIG_SIZE, |
| 188 | "compressed_bytes": $NEW_SIZE, |
| 189 | |
| 190 | "original": "$HUMAN_ORIG", |
| 191 | "compressed": "$HUMAN_NEW", |
| 192 | |
| 193 | "reduction_percent": $(awk "BEGIN { printf \"%.2f\", (1-$NEW_SIZE/$ORIG_SIZE)*100 }"), |
| 194 | "occurred": "$OCCURRED", |
| 195 | |
| 196 | "compression_id": "$COMPRESS_ID", |
| 197 | |
| 198 | "settings": { |
| 199 | "codec": "hevc_nvenc", |
| 200 | "resolution": "$RES", |
| 201 | "mode": "$MODE", |
| 202 | "cq": $CQ, |
| 203 | "scale_filter": "$SCALE", |
| 204 | "audio": "${AUDIO_OPTS[*]}", |
| 205 | "preset": "p6", |
| 206 | "rate_control": "vbr", |
| 207 | "multipass": "fullres", |
| 208 | "pixel_format": "yuv420p", |
| 209 | "aq": { |
| 210 | "spatial": 1, |
| 211 | "temporal": 1, |
| 212 | "strength": 8 |
| 213 | } |
| 214 | } |
| 215 | } |
| 216 | EOF |
| 217 | |
| 218 | echo "Done ✓" |