#!/usr/bin/env python3
"""cd_discid.py —— 从 cdparanoia TOC 计算 freedb/CDDB disc id 与 MusicBrainz disc id

用法: python3 cd_discid.py            # 自动调用 cdparanoia -Q 读 TOC
      python3 cd_discid.py TOC.txt
输出: 轨数、各轨起始扇区(秒)、总时长、CDDB id、MusicBrainz disc id 及查询 URL
"""
import re, subprocess, sys, hashlib, base64

def get_toc_text():
    if len(sys.argv) > 1:
        return open(sys.argv[1], encoding="utf-8", errors="ignore").read()
    return subprocess.run(["cdparanoia", "-Q", "-d", "/dev/sr0"],
                          capture_output=True, text=True).stdout + \
           subprocess.run(["cdparanoia", "-Q", "-d", "/dev/sr0"],
                          capture_output=True, text=True).stderr

txt = get_toc_text()
# 解析行: "  1.    14853 [03:18.03]       32 [00:00.32]    no   no  2"
rows = []
for m in re.finditer(r"^\s*(\d+)\.\s+(\d+)\s+\[([\d:.]+)\]\s+(\d+)\s+\[([\d:.]+)\]", txt, re.M):
    rows.append(dict(trk=int(m.group(1)), length=int(m.group(2)), begin=int(m.group(4))))
tm = re.search(r"TOTAL\s+(\d+)\s+", txt)
total = int(tm.group(1)) if tm else 0

offsets = [r["begin"] for r in rows]           # 帧(1/75 秒)
n = len(rows)
leadout = total + offsets[0]

def frames2sec(f): return f / 75.0

print(f"轨数: {n}")
for r in rows:
    print(f"  轨{r['trk']:>2}: 起始 {r['begin']:>7} 帧 = {frames2sec(r['begin']):8.2f}s  长度 {frames2sec(r['length']):7.2f}s")
print(f"总长: {total} 帧 = {frames2sec(total):.1f}s ({frames2sec(total)//60:.0f}:{frames2sec(total)%60:04.1f})")
print(f"leadout(帧): {leadout}")

# ---- CDDB / freedb id: (sum of digits of each offset in seconds) % 0xff << 24 | total_seconds << 8 | n ----
def cddb_sum(x):
    s = 0
    while x > 0:
        s += x % 10; x //= 10
    return s
tsecs = [int(frames2sec(o)) for o in offsets]        # 每轨起始（秒）
csum = sum(cddb_sum(t) for t in tsecs)
total_sec = int(frames2sec(leadout))
cddb_id = ((csum % 0xFF) << 24) | (total_sec << 8) | n
print(f"\nCDDB/freedb disc id: {cddb_id:08x}  (十进制 {cddb_id})")
print(f"  查询: http://gnudb.gnudb.org/~cddb/cddb.cgi?cmd=cddb+read+{{category}}+{cddb_id:08x}&hello=localhost+cdrip+1.0&proto=6")

# ---- MusicBrainz disc id: SHA1(首轨偏移 + 各轨偏移 + leadout, 各 8 位大写十六进制) ----
parts = [f"{offsets[0]:08X}", f"{leadout:08X}"] + [f"{o:08X}" for o in offsets[1:]]
sha = hashlib.sha1("".join(parts).encode()).digest()
mbid = base64.b64encode(sha).decode().rstrip("=").replace("+", ".").replace("/", "_")
print(f"\nMusicBrainz disc id: {mbid}")
print(f"  查询: https://musicbrainz.org/ws/2/discid/{mbid}?fmt=json&inc=artists+recordings")
print(f"  网页: https://musicbrainz.org/cdtoc/{mbid}")
print(f"\nMB 查询参数(备用): toc={offsets[0]}+{'%20'.join(str(o) for o in offsets[1:])}+{leadout}")
