Last active 1 month ago

Created by ChatGPT, but not the first comment section.

airikr revised this gist 1 month ago. Go to revision

1 file changed, 1 insertion

gistfile1.txt

@@ -132,6 +132,7 @@ EXISTING_ID="$(ffprobe -v error \
132 132
133 133 if [[ "$EXISTING_ID" =~ ^${UNIQUE_PREFIX}[A-Za-z0-9]+$ ]]; then
134 134 echo "Skipping already-compressed file: $INPUT"
135 + echo
135 136 exit 0
136 137 fi
137 138

airikr revised this gist 1 month ago. Go to revision

1 file changed, 2 insertions

gistfile1.txt

@@ -7,6 +7,8 @@
7 7 # - Adding metadata to compressed MP4 file.
8 8 # - Checking if the file has already been compressed by this script.
9 9 #
10 + # However, I have done some fine tuning like adding new rows.
11 + #
10 12 ##########################################################################
11 13
12 14 set -u

airikr revised this gist 1 month ago. Go to revision

1 file changed, 55 insertions, 13 deletions

gistfile1.txt

@@ -1,15 +1,16 @@
1 1 #!/usr/bin/env bash
2 2
3 - ########################################################################
3 + ##########################################################################
4 4 #
5 - # This bash script was written by ChatGPT, but not this docblock.
5 + # This bash script was written by ChatGPT, but not this comment section.
6 6 # Everything works after testing, but not 2 functions:
7 7 # - Adding metadata to compressed MP4 file.
8 8 # - Checking if the file has already been compressed by this script.
9 9 #
10 - ########################################################################
10 + ##########################################################################
11 11
12 12 set -u
13 + export LC_NUMERIC=C
13 14
14 15 SCRIPT_NAME="$(basename "$0")"
15 16 UNIQUE_PREFIX="r-"
@@ -55,7 +56,7 @@ if [[ "${1:-}" == "batch" ]]; then
55 56 shift || true
56 57
57 58 shopt -s nullglob
58 - FILES=( *.mp4 *.mkv )
59 + FILES=( *.mp4 *.MP4 *.mkv )
59 60 shopt -u nullglob
60 61
61 62 if [[ ${#FILES[@]} -eq 0 ]]; then
@@ -87,7 +88,7 @@ fi
87 88
88 89 BASENAME="${INPUT%.*}"
89 90 OUTPUT="${BASENAME}-r.mp4"
90 - SUMMARY="${BASENAME}-summary.json"
91 + SUMMARY="${BASENAME}.json"
91 92
92 93 ########################################
93 94 # Resolution
@@ -140,9 +141,15 @@ COMPRESS_ID="${UNIQUE_PREFIX}$(tr -dc A-Za-z0-9 </dev/urandom | head -c 12)"
140 141 ########################################
141 142 # Encode
142 143 ########################################
144 + echo
143 145 echo "Compressing: $INPUT > $OUTPUT"
144 146
145 - if ! ffmpeg -y -hide_banner -loglevel error -stats \
147 + DURATION=$(ffprobe -v error -show_entries format=duration \
148 + -of default=nk=1:nw=1 "$INPUT")
149 +
150 + START_TIME=$(date +%s)
151 +
152 + ffmpeg -y -hide_banner -loglevel error \
146 153 -i "$INPUT" \
147 154 -map 0:v:0 -map 0:a? \
148 155 -vf "$SCALE" \
@@ -155,10 +162,31 @@ if ! ffmpeg -y -hide_banner -loglevel error -stats \
155 162 "${AUDIO_OPTS[@]}" \
156 163 -movflags +faststart \
157 164 -metadata compressed_by="$COMPRESS_ID" \
158 - "$OUTPUT"; then
159 - echo "FFmpeg failed for $INPUT"
160 - exit 0
161 - fi
165 + -progress pipe:1 \
166 + "$OUTPUT" 2>/dev/null | while IFS='=' read -r key value; do
167 +
168 + if [[ "$key" == "out_time_ms" ]]; then
169 + CURRENT_SEC=$(awk "BEGIN { print $value / 1000000 }")
170 +
171 + PERCENT=$(awk -v c="$CURRENT_SEC" -v d="$DURATION" \
172 + 'BEGIN { if (d>0) printf "%.1f", (c/d)*100; else print 0 }')
173 +
174 + NOW=$(date +%s)
175 + ELAPSED_SEC=$(( NOW - START_TIME ))
176 +
177 + ETA_SEC=$(awk -v e="$ELAPSED_SEC" -v p="$PERCENT" '
178 + BEGIN {
179 + if (p>0) printf "%.0f", e*(100-p)/p;
180 + else print 0
181 + }')
182 +
183 + printf "\rProgress: %5.1f%% | Elapsed: %02d:%02d | ETA: %02d:%02d" \
184 + "$PERCENT" \
185 + $((ELAPSED_SEC/60)) $((ELAPSED_SEC%60)) \
186 + $((ETA_SEC/60)) $((ETA_SEC%60))
187 + fi
188 +
189 + done
162 190
163 191 ########################################
164 192 # Size check
@@ -167,7 +195,8 @@ ORIG_SIZE=$(stat -c%s "$INPUT")
167 195 NEW_SIZE=$(stat -c%s "$OUTPUT")
168 196
169 197 if (( NEW_SIZE >= ORIG_SIZE )); then
170 - echo "Result larger than original → skipping"
198 + echo
199 + echo "Result larger than original - skipping"
171 200 rm -f "$OUTPUT"
172 201 exit 0
173 202 fi
@@ -179,6 +208,15 @@ HUMAN_ORIG=$(numfmt --to=iec --suffix=B "$ORIG_SIZE")
179 208 HUMAN_NEW=$(numfmt --to=iec --suffix=B "$NEW_SIZE")
180 209 OCCURRED=$(date +"%Y-%m-%d %H:%M:%S")
181 210
211 + ########################################
212 + # Safe reduction percent calculation
213 + ########################################
214 + if [[ -z "$ORIG_SIZE" ]] || (( ORIG_SIZE == 0 )); then
215 + REDUCTION_PERCENT=0
216 + else
217 + REDUCTION_PERCENT=$(awk "BEGIN { pct=(1-$NEW_SIZE/$ORIG_SIZE)*100; if(pct<0) pct=0; printf \"%.2f\", pct }")
218 + fi
219 +
182 220 cat > "$SUMMARY" <<EOF
183 221 {
184 222 "input": "$(basename "$INPUT")",
@@ -190,7 +228,7 @@ cat > "$SUMMARY" <<EOF
190 228 "original": "$HUMAN_ORIG",
191 229 "compressed": "$HUMAN_NEW",
192 230
193 - "reduction_percent": $(awk "BEGIN { printf \"%.2f\", (1-$NEW_SIZE/$ORIG_SIZE)*100 }"),
231 + "reduction_percent": $REDUCTION_PERCENT,
194 232 "occurred": "$OCCURRED",
195 233
196 234 "compression_id": "$COMPRESS_ID",
@@ -215,4 +253,8 @@ cat > "$SUMMARY" <<EOF
215 253 }
216 254 EOF
217 255
218 - echo "Done ✓"
256 + REDUCTION_HR=$(awk "BEGIN { printf \"%.2f\", (1 - $NEW_SIZE / $ORIG_SIZE) * 100 }")
257 +
258 + echo
259 + echo "Done. Final reduction: $REDUCTION_HR% ($HUMAN_ORIG > $HUMAN_NEW)"
260 + echo

airikr revised this gist 1 month ago. Go to revision

1 file changed, 218 insertions

gistfile1.txt(file created)

@@ -0,0 +1,218 @@
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 ✓"
Newer Older