Última actividad 1 month ago

Created by ChatGPT, but not the first comment section.

Revisión 597fca54789cd2a0c6de25710e04d2465f132320

gistfile1.txt Sin formato
1#!/usr/bin/env bash
2
3##########################################################################
4#
5# This bash script was written by ChatGPT, but not this comment section.
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# However, I have done some fine tuning like adding new rows.
11#
12##########################################################################
13
14set -u
15export LC_NUMERIC=C
16
17SCRIPT_NAME="$(basename "$0")"
18UNIQUE_PREFIX="r-"
19
20########################################
21# Encoding presets overview:
22#
23# none : balanced quality, AAC 96k
24# vlog : spoken voice / crowd, copy audio
25# tiny : max compression, AAC 64k
26#
27# Resolutions: 720p | 1080p
28########################################
29
30show_help() {
31 cat <<EOF
32Usage:
33 $SCRIPT_NAME <file>
34 $SCRIPT_NAME [none|vlog|tiny] [720p|1080p] <file>
35 $SCRIPT_NAME batch
36
37Batch:
38 Compresses all MP4 + MKV files using:
39 mode=none, resolution=1080p
40
41Defaults:
42 mode=none
43 resolution=720p
44
45EOF
46 exit 0
47}
48
49[[ "${1:-}" == "--help" ]] && show_help
50
51MODE="none"
52RES="720p"
53
54# --- Batch mode ---
55if [[ "${1:-}" == "batch" ]]; then
56 MODE="none"
57 RES="1080p"
58 shift || true
59
60 shopt -s nullglob
61 FILES=( *.mp4 *.MP4 *.mkv )
62 shopt -u nullglob
63
64 if [[ ${#FILES[@]} -eq 0 ]]; then
65 echo "No MP4 or MKV files found."
66 exit 0
67 fi
68
69 for f in "${FILES[@]}"; do
70 "$0" "$MODE" "$RES" "$f"
71 done
72 exit 0
73fi
74
75# --- Parse arguments ---
76if [[ $# -eq 1 ]]; then
77 INPUT="$1"
78elif [[ $# -eq 2 ]]; then
79 MODE="$1"
80 INPUT="$2"
81elif [[ $# -eq 3 ]]; then
82 MODE="$1"
83 RES="$2"
84 INPUT="$3"
85else
86 show_help
87fi
88
89[[ ! -f "$INPUT" ]] && { echo "File not found: $INPUT"; exit 1; }
90
91BASENAME="${INPUT%.*}"
92OUTPUT="${BASENAME}-r.mp4"
93SUMMARY="${BASENAME}.json"
94
95########################################
96# Resolution
97########################################
98case "$RES" in
99 720p) SCALE="scale=-2:720" ;;
100 1080p) SCALE="scale=-2:1080" ;;
101 *) echo "Invalid resolution"; exit 1 ;;
102esac
103
104########################################
105# Mode settings
106########################################
107CQ=23
108AUDIO_OPTS=("-c:a" "aac" "-b:a" "96k")
109
110case "$MODE" in
111 vlog)
112 AUDIO_OPTS=("-c:a" "copy")
113 CQ=23
114 ;;
115 tiny)
116 AUDIO_OPTS=("-c:a" "aac" "-b:a" "64k")
117 CQ=28
118 ;;
119 none) ;;
120 *)
121 echo "Invalid mode"
122 exit 1
123 ;;
124esac
125
126########################################
127# Metadata detection (robust)
128########################################
129EXISTING_ID="$(ffprobe -v error \
130 -show_entries format_tags=compressed_by \
131 -of default=nk=1:nw=1 "$INPUT" 2>/dev/null || true)"
132
133if [[ "$EXISTING_ID" =~ ^${UNIQUE_PREFIX}[A-Za-z0-9]+$ ]]; then
134 echo "Skipping already-compressed file: $INPUT"
135 exit 0
136fi
137
138########################################
139# Generate unique ID
140########################################
141COMPRESS_ID="${UNIQUE_PREFIX}$(tr -dc A-Za-z0-9 </dev/urandom | head -c 12)"
142
143########################################
144# Encode
145########################################
146echo
147echo "Compressing: $INPUT > $OUTPUT"
148
149DURATION=$(ffprobe -v error -show_entries format=duration \
150 -of default=nk=1:nw=1 "$INPUT")
151
152START_TIME=$(date +%s)
153
154ffmpeg -y -hide_banner -loglevel error \
155 -i "$INPUT" \
156 -map 0:v:0 -map 0:a? \
157 -vf "$SCALE" \
158 -c:v hevc_nvenc \
159 -preset p6 -tune hq \
160 -rc vbr -multipass fullres \
161 -profile:v main -pix_fmt yuv420p \
162 -spatial_aq 1 -temporal_aq 1 -aq-strength 8 \
163 -cq "$CQ" \
164 "${AUDIO_OPTS[@]}" \
165 -movflags +faststart \
166 -metadata compressed_by="$COMPRESS_ID" \
167 -progress pipe:1 \
168 "$OUTPUT" 2>/dev/null | while IFS='=' read -r key value; do
169
170 if [[ "$key" == "out_time_ms" ]]; then
171 CURRENT_SEC=$(awk "BEGIN { print $value / 1000000 }")
172
173 PERCENT=$(awk -v c="$CURRENT_SEC" -v d="$DURATION" \
174 'BEGIN { if (d>0) printf "%.1f", (c/d)*100; else print 0 }')
175
176 NOW=$(date +%s)
177 ELAPSED_SEC=$(( NOW - START_TIME ))
178
179 ETA_SEC=$(awk -v e="$ELAPSED_SEC" -v p="$PERCENT" '
180 BEGIN {
181 if (p>0) printf "%.0f", e*(100-p)/p;
182 else print 0
183 }')
184
185 printf "\rProgress: %5.1f%% | Elapsed: %02d:%02d | ETA: %02d:%02d" \
186 "$PERCENT" \
187 $((ELAPSED_SEC/60)) $((ELAPSED_SEC%60)) \
188 $((ETA_SEC/60)) $((ETA_SEC%60))
189 fi
190
191done
192
193########################################
194# Size check
195########################################
196ORIG_SIZE=$(stat -c%s "$INPUT")
197NEW_SIZE=$(stat -c%s "$OUTPUT")
198
199if (( NEW_SIZE >= ORIG_SIZE )); then
200 echo
201 echo "Result larger than original - skipping"
202 rm -f "$OUTPUT"
203 exit 0
204fi
205
206########################################
207# JSON summary
208########################################
209HUMAN_ORIG=$(numfmt --to=iec --suffix=B "$ORIG_SIZE")
210HUMAN_NEW=$(numfmt --to=iec --suffix=B "$NEW_SIZE")
211OCCURRED=$(date +"%Y-%m-%d %H:%M:%S")
212
213########################################
214# Safe reduction percent calculation
215########################################
216if [[ -z "$ORIG_SIZE" ]] || (( ORIG_SIZE == 0 )); then
217 REDUCTION_PERCENT=0
218else
219 REDUCTION_PERCENT=$(awk "BEGIN { pct=(1-$NEW_SIZE/$ORIG_SIZE)*100; if(pct<0) pct=0; printf \"%.2f\", pct }")
220fi
221
222cat > "$SUMMARY" <<EOF
223{
224 "input": "$(basename "$INPUT")",
225 "output": "$(basename "$OUTPUT")",
226
227 "original_bytes": $ORIG_SIZE,
228 "compressed_bytes": $NEW_SIZE,
229
230 "original": "$HUMAN_ORIG",
231 "compressed": "$HUMAN_NEW",
232
233 "reduction_percent": $REDUCTION_PERCENT,
234 "occurred": "$OCCURRED",
235
236 "compression_id": "$COMPRESS_ID",
237
238 "settings": {
239 "codec": "hevc_nvenc",
240 "resolution": "$RES",
241 "mode": "$MODE",
242 "cq": $CQ,
243 "scale_filter": "$SCALE",
244 "audio": "${AUDIO_OPTS[*]}",
245 "preset": "p6",
246 "rate_control": "vbr",
247 "multipass": "fullres",
248 "pixel_format": "yuv420p",
249 "aq": {
250 "spatial": 1,
251 "temporal": 1,
252 "strength": 8
253 }
254 }
255}
256EOF
257
258REDUCTION_HR=$(awk "BEGIN { printf \"%.2f\", (1 - $NEW_SIZE / $ORIG_SIZE) * 100 }")
259
260echo
261echo "Done. Final reduction: $REDUCTION_HR% ($HUMAN_ORIG > $HUMAN_NEW)"
262echo