reprocess-history.sh
4.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#!/usr/bin/env bash
# 一次性:把所有旧的逐句 .opus 重新合并成「块」,清空旧识别(segments/transcripts/
# speaker_profiles),让设备上 RecorderService 用**本地模型**(pyannote+paraformer+campplus)
# 按新 block 管线整体重跑声纹+文字。
#
# 本脚本只做机械活:解码+重打包成 blocks/<ts>/{audio.pcm,manifest.json} + 清 DB。
# 真正的识别 100% 在 Pixel 端上模型跑,不用任何云/Claude。
#
# 用法: bash reprocess-history.sh
# 跑完后:打开 app → 录音 → 开始录音,RecorderService 会自动处理所有块。
set -euo pipefail
PKG=com.lemon.lifelog
DBR=/data/data/$PKG/databases/lifelog.db
SEGR=/data/data/$PKG/files/segments
BLKR=/data/data/$PKG/files/blocks
GAP_MS=$((5*60*1000)) # 静音≥5min 切块
CAP_MS=$((10*60*1000)) # 块≥10min 封顶(控内存/单次 diarize 时长)
SR=16000
WORK=$(mktemp -d /tmp/reprocess.XXXXXX)
echo "工作目录: $WORK"
echo "[1/8] 停 app"
adb shell am force-stop $PKG
echo "[2/8] 备份 + 拉 DB"
TS=$(date +%Y%m%d-%H%M%S)
adb shell "su -c 'cp $DBR /sdcard/_bk.db'"; adb pull /sdcard/_bk.db "$WORK/backup-$TS.db" >/dev/null; adb shell "su -c 'rm /sdcard/_bk.db'"
adb shell "su -c 'cp ${DBR}* /sdcard/ 2>/dev/null'"
for f in lifelog.db lifelog.db-wal lifelog.db-shm; do adb pull /sdcard/$f "$WORK/$f" >/dev/null 2>&1 || true; done
adb shell "su -c 'rm /sdcard/lifelog.db* 2>/dev/null'"
sqlite3 "$WORK/lifelog.db" "PRAGMA wal_checkpoint(TRUNCATE);" >/dev/null 2>&1 || true
echo " 备份: $WORK/backup-$TS.db"
echo "[3/8] 拉所有 .opus"
adb shell "su -c 'rm -rf /sdcard/_seg && cp -r $SEGR /sdcard/_seg'"
adb pull /sdcard/_seg "$WORK/segments" >/dev/null 2>&1
adb shell "su -c 'rm -rf /sdcard/_seg'"
echo "[4/8] 分块 + 解码重打包(ffmpeg)"
python3 - "$WORK/lifelog.db" "$WORK/segments" "$WORK/blocks" "$GAP_MS" "$CAP_MS" "$SR" <<'PY'
import sqlite3, subprocess, os, json, sys
db, segdir, outdir, GAP, CAP, SR = sys.argv[1], sys.argv[2], sys.argv[3], int(sys.argv[4]), int(sys.argv[5]), int(sys.argv[6])
con = sqlite3.connect(db)
rows = con.execute("SELECT id,startTs,durationMs,audioPath FROM segments ORDER BY startTs").fetchall()
# 按静音间隔 + 时长封顶分块
blocks, cur = [], []
for r in rows:
if cur:
prev = cur[-1]
prev_end = prev[1] + prev[2]
span = r[1] - cur[0][1]
if (r[1] - prev_end) >= GAP or span >= CAP:
blocks.append(cur); cur = []
cur.append(r)
if cur: blocks.append(cur)
os.makedirs(outdir, exist_ok=True)
made = 0
for b in blocks:
start = b[0][1]
bdir = os.path.join(outdir, str(start)); os.makedirs(bdir, exist_ok=True)
pcm = bytearray(); runs = []; off = 0
for seg in b:
rel = seg[3].split('/files/segments/')[-1]
local = os.path.join(segdir, rel)
if not os.path.exists(local):
continue
raw = subprocess.run(["ffmpeg","-v","quiet","-i",local,"-ar",str(SR),"-ac","1","-f","s16le","-"],
capture_output=True).stdout
n = len(raw)//2
if n == 0:
continue
runs.append({"ts": seg[1], "off": off, "len": n}); off += n; pcm += raw
if off == 0:
os.rmdir(bdir); continue
open(os.path.join(bdir, "audio.pcm"), "wb").write(pcm)
json.dump({"start": start, "sr": SR, "closed": True, "runs": runs}, open(os.path.join(bdir, "manifest.json"), "w"))
made += 1
print(f" block {start}: {len(b)} segs -> {off/SR:.0f}s speech")
print(f" => {made} blocks from {len(rows)} segments")
PY
echo "[5/8] 清空旧识别(segments/transcripts/speaker_profiles)"
sqlite3 "$WORK/lifelog.db" "DELETE FROM transcripts; DELETE FROM segments; DELETE FROM speaker_profiles; PRAGMA wal_checkpoint(TRUNCATE); VACUUM;"
sqlite3 "$WORK/lifelog.db" "PRAGMA integrity_check;" | head -1
echo "[6/8] 推块到设备 + 清旧 .opus + 清旧 blocks"
APP_UID=$(adb shell "su -c 'stat -c %u /data/data/$PKG'" | tr -d '\r')
echo " app uid=$APP_UID"
adb shell "su -c 'rm -rf $BLKR; mkdir -p $BLKR'"
adb shell "su -c 'rm -rf $SEGR'" # 旧逐句 .opus 全删(内容已并进块);RecorderService 会重建并写新块 .opus
adb push "$WORK/blocks" /sdcard/_blk >/dev/null
adb shell "su -c 'cp -r /sdcard/_blk/* $BLKR/ && chown -R $APP_UID:$APP_UID $BLKR && chcon -R u:object_r:app_data_file:s0 $BLKR; rm -rf /sdcard/_blk'"
echo " pushed $(adb shell "su -c 'ls $BLKR | wc -l'" | tr -d '\r') blocks"
echo "[7/8] 推回清空后的 DB"
adb push "$WORK/lifelog.db" /sdcard/_db >/dev/null
adb shell "su -c 'cp /sdcard/_db $DBR && rm -f ${DBR}-wal ${DBR}-shm && chown $APP_UID:$APP_UID $DBR && chcon u:object_r:app_data_file:s0 $DBR; rm -f /sdcard/_db'"
echo "[8/8] 完成。现在打开 app → 录音 → 开始录音,端上本地模型会自动重跑所有块。"
echo " 监控: adb logcat -s BlockProcessor:I SpeakerClusterer:I"
echo " 备份在: $WORK/backup-$TS.db"