airikr revisó este gist 1 month ago. Ir a la revisión
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 revisó este gist 1 month ago. Ir a la revisión
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 revisó este gist 1 month ago. Ir a la revisión
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 revisó este gist 1 month ago. Ir a la revisión
1 file changed, 218 insertions
gistfile1.txt(archivo creado)
| @@ -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 ✓" | |