250 lines
6.6 KiB
Bash
250 lines
6.6 KiB
Bash
|
#!/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 "$@"
|