#!/usr/bin/env bash # ============================================================================== # WORKER SCRIPT: compress_audio.sh (v6.1 - libvorbis only, bug-fix) # ============================================================================== set -Eeuo pipefail trap 'echo "❌ 发生错误 (line $LINENO)" >&2' ERR # ---------- 可调环境变量 ---------- SOURCE_DIR="${SOURCE_DIR:-./asset/audio}" OUTPUT_DIR="${OUTPUT_DIR:-./compressed_audio}" BYTE_RATE_THRESHOLD="${BYTE_RATE_THRESHOLD:-60000}" # < 60 kB/s 视为已压缩 OVERWRITE="${OVERWRITE:-0}" # 0=跳过已存在 AUDIO_RATE="${AUDIO_RATE:-44100}" # Vorbis 常用 44.1 kHz DRY_RUN="${DRY_RUN:-0}" LOG_LEVEL="${LOG_LEVEL:-info}" # info / debug # ---------- 固定编解码参数 ---------- OUTPUT_EXT="ogg" ENC_OPTS="-c:a libvorbis -q:a 5" # 0–10, 5≈~160 kbps # ---------- 参数检查 ---------- [[ $# -eq 0 || -z ${1:-} ]] && { echo "用法: $0 " >&2; exit 2; } audio_file=$1 [[ ! -f $audio_file ]] && { echo "文件不存在: $audio_file" >&2; exit 2; } # ---------- 辅助 ---------- log(){ [[ $LOG_LEVEL == debug || $1 != debug ]] && echo "[$1] $2" >&2; } get_size(){ [[ $(uname) == Darwin ]] && stat -f%z "$1" || stat -c%s "$1"; } # ---------- 输出路径 ---------- relative="${audio_file#$SOURCE_DIR/}" base="${relative%.*}" output="$OUTPUT_DIR/${base}.${OUTPUT_EXT}" # ---------- 跳过已存在 ---------- if [[ $OVERWRITE -eq 0 && -f $output ]]; then log info "SKIP(已存在): $output"; exit 2; fi # ---------- 获取时长 ---------- duration=$(ffprobe -v error -show_entries format=duration \ -of default=noprint_wrappers=1:nokey=1 "$audio_file" | head -n1) if [[ -z $duration || $(awk "BEGIN{print ($duration<=0)}") -eq 1 ]]; then log info "SKIP(无效): $audio_file"; exit 2; fi # ---------- 获取比特率 ---------- bitrate=$(ffprobe -v error -select_streams a:0 -show_entries stream=bit_rate \ -of default=noprint_wrappers=1:nokey=1 "$audio_file" | head -n1) # 某些文件无 bit_rate;回退到 size/duration if [[ -z $bitrate || $bitrate == N/A || $bitrate == 0 ]]; then size=$(get_size "$audio_file") bitrate=$(awk "BEGIN{printf \"%.0f\", $size/$duration}") fi if (( bitrate < BYTE_RATE_THRESHOLD )); then log info "SKIP(已压缩, ${bitrate}B/s): $audio_file"; exit 2; fi [[ $DRY_RUN -eq 1 ]] && { log info "DRY-RUN: $audio_file → $output"; exit 0; } mkdir -p "$(dirname "$output")" log info "COMPRESS → $output" ffmpeg -hide_banner -loglevel error -y \ -i "$audio_file" -vn -sn $ENC_OPTS -ar "$AUDIO_RATE" -map_metadata -1 \ "$output" \ && { log info "SUCCESS"; exit 0; } \ || { log info "FAILED"; exit 1; }