#!/usr/bin/env bash
# verify-cd-vcd.sh —— 《法国·朋友》CD+VCD 到货验收（DSH 机跑，纯 CPU，不占 GPU）
#
# 用法:
#   bash verify-cd-vcd.sh --cd /dev/sr0                        # 光驱直接抓轨分析
#   bash verify-cd-vcd.sh --cd ~/rip/track01.wav               # 已抓好的 WAV/FLAC
#   bash verify-cd-vcd.sh --vcd ~/VCD/AVSEQ01.DAT              # VCD 的 .DAT / .mpg / 整盘 .iso
#   bash verify-cd-vcd.sh --cd /dev/sr0 --vcd ~/VCD/AVSEQ01.DAT
#
# 产出（默认 dl-hub/54-高清修复调研/验收-<时间戳>/）:
#   cd-信息.txt  cd-频谱.csv  cd-vs-现有音轨.png   ← 音频高频是否真的回来了
#   vcd帧对比.png                                  ← VCD 是否优于现有 320x240 源
#   结论.txt                                       ← 判定 + 下一步
set -uo pipefail

OUT="${OUT:-$HOME/Downloads/dl-hub/54-高清修复调研/验收-$(date +%Y%m%d-%H%M%S)}"
SRC_MP4="${SRC_MP4:-/home/zyw/Downloads/dl-hub/53-B站视频下载/【天籁童声】法语翻唱周华健《朋友》—Amis（理查德克莱德曼伴奏） [BV1Ss411o7p3] [240P15-HEVC].mp4}"
BASE_CSV="${BASE_CSV:-/home/zyw/Downloads/dl-hub/54-高清修复调研/现有音轨-频谱.csv}"
CD=""; VCD=""
while [[ $# -gt 0 ]]; do
  case "$1" in
    --cd)  CD="$2";  shift 2;;
    --vcd) VCD="$2"; shift 2;;
    *) echo "未知参数: $1"; exit 2;;
  esac
done
mkdir -p "$OUT"
VERDICT="$OUT/结论.txt"; : > "$VERDICT"
say() { echo "$*" | tee -a "$VERDICT"; }
for t in ffmpeg ffprobe python3; do command -v "$t" >/dev/null || { echo "❌ 缺 $t"; exit 1; }; done

# ---- 把两个 python 助手写成临时文件（关键：避免 heredoc 占用 stdin，管道数据才能进 python）----
PY_SPECTRUM=$(mktemp /tmp/kf-spectrum.XXXX.py)
PY_PLOT=$(mktemp /tmp/kf-plot.XXXX.py)
trap 'rm -f "$PY_SPECTRUM" "$PY_PLOT"' EXIT

cat > "$PY_SPECTRUM" <<'PY'
import sys, struct, math, cmath
csv = sys.argv[1]
d = sys.stdin.buffer.read(); n = len(d)//2
if n < 4096:
    print(f"❌ 音频采样不足（{n} 点）", file=sys.stderr); sys.exit(1)
x = struct.unpack('<%dh' % n, d[:n*2])
N = 4096
def fft(a):
    n = len(a)
    if n == 1: return a
    e = fft(a[0::2]); o = fft(a[1::2])
    t = [cmath.exp(-2j*math.pi*k/n)*o[k] for k in range(n//2)]
    return [e[k]+t[k] for k in range(n//2)] + [e[k]-t[k] for k in range(n//2)]
acc = [0.0]*(N//2); cnt = 0
for i in range(0, max(0, n-N), max(N, N*4)):
    blk = x[i:i+N]
    w = [blk[k]*(0.5-0.5*math.cos(2*math.pi*k/(N-1))) for k in range(N)]
    s = fft([complex(v) for v in w])
    for k in range(N//2): acc[k] += abs(s[k])
    cnt += 1
cnt = max(cnt, 1); sr = 48000
bands = [(0,100),(100,500),(500,2000),(2000,5000),(5000,10000),(10000,14000),(14000,16000),(16000,18000),(18000,20000),(20000,24000)]
rows = []
for lo, hi in bands:
    a, b = int(lo/sr*N), min(int(hi/sr*N), N//2)
    v = sum(acc[a:b])/max(1, b-a)/cnt
    rows.append((lo, hi, 20*math.log10(v/32768+1e-12)))
pk = max(r[2] for r in rows)
with open(csv, 'w') as fp:
    fp.write("lo,hi,db,db_rel_peak\n")
    for lo, hi, db in rows: fp.write(f"{lo},{hi},{db:.1f},{db-pk:.1f}\n")
print(f"[频谱] {csv}  峰值 {pk:.1f} dB  块数 {cnt}")
PY

cat > "$PY_PLOT" <<'PY'
import sys
def rd(p):
    out=[]
    for ln in open(p):
        if ln.startswith('#') or ln.startswith('lo'): continue
        f=ln.strip().split(',')
        if len(f)<4: continue
        lo=int(f[0]); lab=f"{lo//1000}k" if lo>=1000 else f"{lo}"
        out.append((lab, float(f[3])))
    return out
a=rd(sys.argv[1]); b=rd(sys.argv[2])
W,H=1000,460; mL,mR,mB,mT=70,20,40,20
def y(dbh): return int(mT+(0-max(-80.0,min(0.0,dbh)))/80*(H-mB-mT))
buf=bytearray(b'\x20'*(W*H*3))
def px(x,y,c):
    if 0<=x<W and 0<=y<H:
        i=(y*W+x)*3; buf[i:i+3]=bytes(c)
for x in range(mL,W-mR): px(x,H-mB,(70,70,70))
for yy in range(mT,H-mB): px(mL,yy,(70,70,70))
for g in range(0,-81,-20):
    yy=y(g)
    for x in range(mL,W-mR):
        if x%4<2: px(x,yy,(45,45,45))
for series,col in ((a,(80,140,255)),(b,(255,150,60))):
    n=max(1,len(series)-1); prev=None
    for i,(lab,db) in enumerate(series):
        x=int(mL+6+(W-mL-mR-12)*i/n); yv=y(db)
        if prev:
            x0,y0=prev; steps=max(abs(x-x0),abs(yv-y0),1)
            for s in range(steps+1):
                px(int(x0+(x-x0)*s/steps), int(y0+(yv-y0)*s/steps), col)
        prev=(x,yv); px(x,yv,col); px(x,yv+1,col); px(x+1,yv,col)
x10=int(mL+6+(W-mL-mR-12)*5/n)
for yy in range(mT,H-mB):
    if yy%6<3: px(x10,yy,(210,70,70))
with open(sys.argv[3],'wb') as f:
    f.write(b'P6\n%d %d\n255\n'%(W,H)); f.write(bytes(buf))
print(f"[制图] {sys.argv[3]}  (蓝=现有音轨 橙=新音轨 红虚线=10kHz 位置; 纵轴 0..-80dB 相对峰值)")
PY

audio_pcm() { ffmpeg -v error -ss "${2:-30}" -t "${3:-20}" -i "$1" -map 0:a:0 -ac 1 -ar 48000 -f s16le - 2>/dev/null; }
spectrum_csv() { audio_pcm "$1" "${3:-30}" "${4:-20}" | python3 "$PY_SPECTRUM" "$2"; }
plot_cmp() {  # $1=基线csv $2=新csv $3=输出png（先出 PPM 再转真 PNG）
  python3 "$PY_PLOT" "$1" "$2" "${3%.png}.ppm" || return 1
  if ffmpeg -y -v error -i "${3%.png}.ppm" -frames:v 1 -update 1 "$3" 2>/dev/null; then
    rm -f "${3%.png}.ppm"; echo "[制图] $3  (蓝=现有音轨 橙=新音轨 红虚线=10kHz; 纵轴 0..-80dB 相对峰值)"
  else echo "[制图] ${3%.png}.ppm （转 PNG 失败，直接看 PPM）"; fi
}
audio_hf() {  # $1=csv → 一行结论
  python3 -c "
import sys
d={}
for ln in open(sys.argv[1]):
    if ln.startswith('#') or ln.startswith('lo'): continue
    f=ln.strip().split(','); d[int(f[0])]=float(f[3])
h10=d.get(10000,-99); h14=d.get(14000,-99); h20=d.get(20000,-99)
print(f'相对峰值: 10-14k={h10:.1f}dB  14-16k={h14:.1f}dB  20-24k={h20:.1f}dB')
print('✅ 音频验收通过：高频回来了（10-14k > -30dB）→ 音频天花板解除，直接换音轨' if h10>-30 else
      '⚠️ 高频仍低（10-14k ≤ -30dB）：先确认抓对了轨，再怀疑这张 CD 本身就是低切母带')
" "$1"
}

say "验收目录: $OUT"
if [[ ! -f "$BASE_CSV" ]]; then say "生成现有音轨基线频谱…"; spectrum_csv "$SRC_MP4" "$BASE_CSV" | tee -a "$VERDICT"; fi

# ================= CD =================
if [[ -n "$CD" ]]; then
  say ""; say "===== CD 侧 ====="
  audio="$CD"
  if [[ -b "$CD" || -c "$CD" ]]; then
    say "光驱设备: $CD"
    if command -v cdparanoia >/dev/null; then
      say "TOC（先看《朋友 / Amis》是第几轨）:"; cdparanoia -Q -d "$CD" 2>&1 | tee -a "$VERDICT" | tail -25
      audio="$OUT/cd-track01.wav"
      cdparanoia -w -d "$CD" 1 "$audio" 2>&1 | tail -5 | tee -a "$VERDICT" || true
      say "⚠️ 若《朋友》不是第 1 轨：把该轨 WAV 抓好后用 --cd <该WAV> 重跑"
    else
      say "⚠️ 未装 cdparanoia（sudo apt install cdparanoia）；ffmpeg 读 CD-DA 能力有限"
      audio="$OUT/cd-all.wav"; ffmpeg -y -v warning -i "$CD" -map 0:a:0 -c:a pcm_s16le "$audio" 2>&1 | tail -3 | tee -a "$VERDICT" || true
    fi
  fi
  if [[ -f "$audio" ]]; then
    { echo "--- 音频流信息 ---"; ffprobe -v error -i "$audio" -select_streams a:0 \
        -show_entries stream=codec_name,sample_rate,channels,bits_per_raw_sample \
        -show_entries format=duration,bit_rate -of default=noprint_wrappers=1; } | tee -a "$VERDICT"
    spectrum_csv "$audio" "$OUT/cd-频谱.csv" | tee -a "$VERDICT"
    [[ -f "$BASE_CSV" ]] && plot_cmp "$BASE_CSV" "$OUT/cd-频谱.csv" "$OUT/cd-vs-现有音轨.png" | tee -a "$VERDICT"
    audio_hf "$OUT/cd-频谱.csv" | tee -a "$VERDICT"
  else say "❌ 没拿到 CD 音频文件"; fi
fi

# ================= VCD =================
if [[ -n "$VCD" ]]; then
  say ""; say "===== VCD 侧 ====="
  say "输入: $VCD"
  say "--- 视频流信息（VCD 的 .DAT 是 MPEG-PS，用 select_streams 取才稳）---"
  ffprobe -v error -i "$VCD" -select_streams v:0 \
    -show_entries stream=codec_name,width,height,r_frame_rate,bit_rate \
    -show_entries format=duration,bit_rate -of default=noprint_wrappers=1 2>&1 | tee -a "$VERDICT"
  vw=$(ffprobe -v error -i "$VCD" -select_streams v:0 -show_entries stream=width  -of csv=p=0 2>/dev/null | head -1)
  vh=$(ffprobe -v error -i "$VCD" -select_streams v:0 -show_entries stream=height -of csv=p=0 2>/dev/null | head -1)
  say "VCD 分辨率: ${vw:-?}x${vh:-?}（现有源 320x240）"
  ffmpeg -y -v error -ss 6 -i "$VCD" -ss 6 -i "$SRC_MP4" -filter_complex \
    "[0:v:0]scale=960:720:force_original_aspect_ratio=decrease,pad=960:720:(ow-iw)/2:(oh-ih)/2:black[v];[1:v:0]scale=960:720:flags=neighbor[s];[v][s]hstack=inputs=2[o]" \
    -map "[o]" -frames:v 1 -update 1 "$OUT/vcd帧对比.png" 2>&1 | tail -2 | tee -a "$VERDICT" || true
  [[ -f "$OUT/vcd帧对比.png" ]] && say "帧对比图: $OUT/vcd帧对比.png（左=VCD 原始，右=现有 320x240 源放大，同一 6.0s）"
  say "判读：左边明显更少 8x8 块效应/更锐 → VCD 值得当超分起点；否则沿用现有源"
fi

say ""; say "===== 下一步 ====="
say "1) 音频验收通过 → 直接用 CD 轨做成品音轨（无需 AI 修）"
say "2) 视频若 VCD 更干净 → 以 VCD 为超分起点，否则沿用 320x240"
say "3) 合成：AI 超分 3x → 960x720 + 时域稳定(插帧) + CD 音轨"
say "产物: $OUT"
echo; echo "完成 → $VERDICT"
