xiandie/reuse_uid_to_ogg.sh

250 lines
6.6 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# ============================================================================
# SCRIPT : reuse_uid_to_ogg.sh (v4.0 - 增强版)
# TASK : 将 *.wav.import / *.mp3.import 的 UID 迁移到对应 *.ogg.import
# 并删除源文件中的 UID 行
# USAGE : DEBUG=1 ./reuse_uid_to_ogg.sh # 调试模式
# DRY_RUN=1 ./reuse_uid_to_ogg.sh # 演练
# ./reuse_uid_to_ogg.sh # 实际写入
# ============================================================================
set -Euo pipefail
[[ ${DEBUG:-0} == 1 ]] && set -x
ROOT_DIR="${ROOT_DIR:-asset/audio}"
DRY_RUN="${DRY_RUN:-0}"
VERBOSE="${VERBOSE:-0}"
MODIFIED=0
SKIPPED=0
ERRORS=0
# ---------- 错误处理 ----------
error_handler() {
local line_no=$1
local error_code=$2
printf "❌ 错误发生在第 %s 行,错误代码: %s\n" "$line_no" "$error_code" >&2
printf " 最后处理的文件: %s\n" "${CURRENT_FILE:-未知}" >&2
exit "$error_code"
}
trap 'error_handler ${LINENO} $?' ERR
# ---------- 工具函数 ----------
log() {
[[ $VERBOSE == 1 ]] && echo "[LOG] $*" >&2
}
check_writable() {
local f=$1
if [[ ! -f "$f" ]]; then
printf "❌ 文件不存在:%s\n" "$f" >&2
return 1
fi
if [[ ! -w "$f" ]]; then
printf "❌ 无写权限:%s\n" "$f" >&2
return 1
fi
return 0
}
get_uid() {
local file=$1
local uid=""
if [[ ! -f "$file" ]]; then
log "文件不存在: $file"
return 1
fi
# 提取 UID处理可能的空格和回车
uid=$(awk -F'"' '/^[[:space:]]*uid="uid:\/\// {print $2; exit}' "$file" 2>/dev/null | tr -d '\r\n' || true)
if [[ -n "$uid" ]]; then
echo "$uid"
return 0
else
return 1
fi
}
# ---------- 把 UID 写进 .ogg.import ----------
put_uid() {
local file=$1
local uid=$2
if ! check_writable "$file"; then
((ERRORS++))
return 1
fi
# 统一转义处理
local esc=${uid//\\/\\\\} # 反斜线
esc=${esc//&/\\&} # &
esc=${esc//@/\\@} # @
esc=${esc//\//\\/} # /
if [[ $DRY_RUN == 1 ]]; then
printf " ↪ [演练] 更新 %-30s → %s\n" "$(basename "$file")" "$uid"
return 0
fi
local tmp
if ! tmp=$(mktemp); then
printf "❌ 无法创建临时文件\n" >&2
((ERRORS++))
return 1
fi
# 确保临时文件被清理
trap "rm -f '$tmp'" RETURN
local success=0
if grep -qE '^[[:space:]]*uid="uid://[^"]+"' "$file" 2>/dev/null; then
# 已有 UID → 替换
if sed -E "s@^[[:space:]]*uid=\"uid://[^\"]+\"@uid=\"${esc}\"@" "$file" >"$tmp" 2>/dev/null; then
success=1
fi
else
# 无 UID → 插入
if grep -q '^\[remap\]' "$file" 2>/dev/null; then
if sed -E "/^\[remap\]/a\\
uid=\"${esc}\"
" "$file" >"$tmp" 2>/dev/null; then
success=1
fi
else
if cat "$file" >"$tmp" 2>/dev/null && printf 'uid="%s"\n' "$uid" >>"$tmp" 2>/dev/null; then
success=1
fi
fi
fi
if [[ $success == 1 ]] && mv "$tmp" "$file" 2>/dev/null; then
((MODIFIED++))
log "成功更新 $file"
return 0
else
printf "❌ 更新失败:%s\n" "$file" >&2
((ERRORS++))
return 1
fi
}
# ---------- 删除源文件的 UID 行 ----------
delete_uid_line() {
local file=$1
if ! check_writable "$file"; then
((ERRORS++))
return 1
fi
if [[ $DRY_RUN == 1 ]]; then
printf " ↪ [演练] 删除 %-30s 中的 UID 行\n" "$(basename "$file")"
return 0
fi
local tmp
if ! tmp=$(mktemp); then
printf "❌ 无法创建临时文件\n" >&2
((ERRORS++))
return 1
fi
# 确保临时文件被清理
trap "rm -f '$tmp'" RETURN
if sed -E '/^[[:space:]]*uid="uid:\/\/[^"]+"/d' "$file" >"$tmp" 2>/dev/null && \
mv "$tmp" "$file" 2>/dev/null; then
((MODIFIED++))
log "成功删除 $file 中的 UID"
return 0
else
printf "❌ 删除 UID 失败:%s\n" "$file" >&2
((ERRORS++))
return 1
fi
}
# ---------- 主程序 ----------
main() {
printf "=== 开始 UID 迁移 (DRY_RUN=%s, ROOT_DIR=%s) ===\n" "$DRY_RUN" "$ROOT_DIR"
# 检查根目录
if [[ ! -d "$ROOT_DIR" ]]; then
printf "❌ 目录不存在:%s\n" "$ROOT_DIR" >&2
exit 1
fi
# 使用数组存储文件避免管道子shell问题
local files=()
while IFS= read -r -d '' file; do
files+=("$file")
done < <(find "$ROOT_DIR" -type f \( -name '*.wav.import' -o -name '*.mp3.import' \) -print0 2>/dev/null || true)
if [[ ${#files[@]} -eq 0 ]]; then
printf "未找到任何 .wav.import 或 .mp3.import 文件\n"
exit 0
fi
printf "找到 %d 个源文件\n\n" "${#files[@]}"
# 处理每个文件
for src in "${files[@]}"; do
CURRENT_FILE="$src"
# 获取 UID
local src_uid
if ! src_uid=$(get_uid "$src"); then
printf "🟡 跳过 (无 UID) : %s\n" "$(basename "$src")"
((SKIPPED++))
continue
fi
# 构建对应的 .ogg.import 文件路径
local base="${src%.*.*}"
local ogg="$base.ogg.import"
# 检查 .ogg.import 是否存在
if [[ ! -f "$ogg" ]]; then
printf "🟡 跳过 (无 OGG) : %s\n" "$(basename "$src")"
((SKIPPED++))
continue
fi
# 检查 UID 是否已同步
local ogg_uid
ogg_uid=$(get_uid "$ogg" 2>/dev/null || echo "")
if [[ "$src_uid" == "$ogg_uid" ]]; then
printf "🟢 跳过 (UID 已同步) : %s\n" "$(basename "$src")"
((SKIPPED++))
continue
fi
# 处理文件
printf "🔄 处理 : %s → %s\n" "$(basename "$src")" "$(basename "$ogg")"
if put_uid "$ogg" "$src_uid"; then
delete_uid_line "$src" || true # 即使删除失败也继续
fi
done
# 输出统计
printf "\n=== 处理完毕 ===\n"
if [[ $DRY_RUN == 1 ]]; then
printf "演练模式,未实际修改任何文件。\n"
else
printf "✅ 成功处理: %d 个文件\n" "$((MODIFIED/2))"
printf "🟡 跳过: %d 个文件\n" "$SKIPPED"
if [[ $ERRORS -gt 0 ]]; then
printf "❌ 错误: %d 个\n" "$ERRORS"
fi
fi
return 0
}
# 运行主程序
main "$@"