// 一次性 DB 维护:恢复被 INSERT OR IGNORE + deleted=1 阻塞的 3 条缓存, // 清理无 DB 引用的孤儿音频文件。用法:node dev_test_scripts/debug/debug_tts_db_fix.js const fs = require('fs') const path = require('path') const db = require('better-sqlite3')('data/mp_rep_clock.db') const FIX = { '还有5秒': 'rc_1788857916346_c9bb0cc4.wav', '还有3秒': 'rc_1788857916424_0d8f7e90.wav', '本次训练结束,辛苦了': 'rc_1788857916507_e06bd852.wav', } let recovered = 0 for (const [text, file] of Object.entries(FIX)) { const abs = path.join(process.cwd(), 'uploads', 'mp_rep_clock', 'tts_cache', file) if (!fs.existsSync(abs)) { console.log('[skip 文件不存在]', file); continue } const info = db.prepare('UPDATE rc_tts_cache SET deleted=0, url=? WHERE text=? AND voice=?') .run('/uploads/mp_rep_clock/tts_cache/' + file, text, 'female_01') console.log('[fix]', text, '->', file, 'affected rows:', info.changes) recovered += info.changes } // 清理有文件但没有任何 active 缓存行引用的孤儿 const activeUrls = db.prepare('SELECT url FROM rc_tts_cache WHERE deleted=0').all().map(r => r.url) const cacheDir = path.join(process.cwd(), 'uploads', 'mp_rep_clock', 'tts_cache') let orphans = 0 for (const f of fs.readdirSync(cacheDir)) { const rel = '/uploads/mp_rep_clock/tts_cache/' + f if (!activeUrls.includes(rel)) { try { fs.unlinkSync(path.join(cacheDir, f)); orphans++; console.log('[orphan-del]', f) } catch (_) {} } } console.log('[result] 恢复缓存行:', recovered, '; 删除孤儿文件:', orphans) console.log('[result] active 缓存条数:', db.prepare('SELECT COUNT(*) c FROM rc_tts_cache WHERE deleted=0').get().c) console.log('[result] active 缓存列表:') db.prepare('SELECT text, url FROM rc_tts_cache WHERE deleted=0 ORDER BY text').all().forEach(r => console.log(' ', r.text, '=>', r.url))