68 lines
2.7 KiB
Bash
Executable File
68 lines
2.7 KiB
Bash
Executable File
#!/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 <audio-file>" >&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; } |