diff --git a/electron/services/chatService.ts b/electron/services/chatService.ts index 68cb598..415ba1e 100644 --- a/electron/services/chatService.ts +++ b/electron/services/chatService.ts @@ -6,7 +6,6 @@ import * as https from 'https' import * as http from 'http' import * as fzstd from 'fzstd' import * as crypto from 'crypto' -import Database from 'better-sqlite3' import { app, BrowserWindow } from 'electron' import { ConfigService } from './config' import { wcdbService } from './wcdbService' @@ -18,12 +17,6 @@ import { exportCardDiagnosticsService } from './exportCardDiagnosticsService' import { voiceTranscribeService } from './voiceTranscribeService' import { LRUCache } from '../utils/LRUCache.js' -type HardlinkState = { - db: Database.Database - imageTable?: string - dirTable?: string -} - export interface ChatSession { username: string type: number @@ -213,7 +206,6 @@ class ChatService { private avatarCache: Map private readonly avatarCacheTtlMs = 10 * 60 * 1000 private readonly defaultV1AesKey = 'cfcd208495d565ef' - private hardlinkCache = new Map() private readonly contactCacheService: ContactCacheService private readonly messageCacheService: MessageCacheService private readonly sessionStatsCacheService: SessionStatsCacheService @@ -4852,13 +4844,6 @@ class ChatService { this.groupMyMessageCountCacheService.clearAll() } - for (const state of this.hardlinkCache.values()) { - try { - state.db?.close() - } catch { } - } - this.hardlinkCache.clear() - if (includeEmojis) { emojiCache.clear() emojiDownloading.clear() @@ -6707,10 +6692,6 @@ class ChatService { private async findDatFile(accountDir: string, baseName: string, sessionId?: string): Promise { const normalized = this.normalizeDatBase(baseName) - if (this.looksLikeMd5(normalized)) { - const hardlinkPath = this.resolveHardlinkPath(accountDir, normalized, sessionId) - if (hardlinkPath) return hardlinkPath - } const searchPaths = [ join(accountDir, 'FileStorage', 'Image'), @@ -6776,68 +6757,6 @@ class ChatService { return /[._][a-z]$/.test(baseLower) } - private resolveHardlinkPath(accountDir: string, md5: string, sessionId?: string): string | null { - try { - const hardlinkPath = join(accountDir, 'hardlink.db') - if (!existsSync(hardlinkPath)) return null - - const state = this.getHardlinkState(accountDir, hardlinkPath) - if (!state.imageTable) return null - - const row = state.db - .prepare(`SELECT dir1, dir2, file_name FROM ${state.imageTable} WHERE md5 = ? LIMIT 1`) - .get(md5) as { dir1?: string; dir2?: string; file_name?: string } | undefined - - if (!row) return null - const dir1 = row.dir1 as string | undefined - const dir2 = row.dir2 as string | undefined - const fileName = row.file_name as string | undefined - if (!dir1 || !dir2 || !fileName) return null - const lowerFileName = fileName.toLowerCase() - if (lowerFileName.endsWith('.dat')) { - const baseLower = lowerFileName.slice(0, -4) - if (!this.hasXVariant(baseLower)) return null - } - - let dirName = dir2 - if (state.dirTable && sessionId) { - try { - const dirRow = state.db - .prepare(`SELECT dir_name FROM ${state.dirTable} WHERE dir_id = ? AND username = ? LIMIT 1`) - .get(dir2, sessionId) as { dir_name?: string } | undefined - if (dirRow?.dir_name) dirName = dirRow.dir_name as string - } catch { } - } - - const fullPath = join(accountDir, dir1, dirName, fileName) - if (existsSync(fullPath)) return fullPath - - const withDat = `${fullPath}.dat` - if (existsSync(withDat)) return withDat - } catch { } - return null - } - - private getHardlinkState(accountDir: string, hardlinkPath: string): HardlinkState { - const cached = this.hardlinkCache.get(accountDir) - if (cached) return cached - - const db = new Database(hardlinkPath, { readonly: true, fileMustExist: true }) - const imageRow = db - .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'image_hardlink_info%' ORDER BY name DESC LIMIT 1") - .get() as { name?: string } | undefined - const dirRow = db - .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'dir2id%' LIMIT 1") - .get() as { name?: string } | undefined - const state: HardlinkState = { - db, - imageTable: imageRow?.name as string | undefined, - dirTable: dirRow?.name as string | undefined - } - this.hardlinkCache.set(accountDir, state) - return state - } - private getDatVersion(data: Buffer): number { if (data.length < 6) return 0 const sigV1 = Buffer.from([0x07, 0x08, 0x56, 0x31, 0x08, 0x07]) diff --git a/electron/services/videoService.ts b/electron/services/videoService.ts index 1893eab..10eb1d2 100644 --- a/electron/services/videoService.ts +++ b/electron/services/videoService.ts @@ -2,7 +2,6 @@ import { existsSync, readdirSync, statSync, readFileSync, appendFileSync, mkdirSync } from 'fs' import { app } from 'electron' import { ConfigService } from './config' -import Database from 'better-sqlite3' import { wcdbService } from './wcdbService' export interface VideoInfo { @@ -71,58 +70,21 @@ class VideoService { /** * 从 video_hardlink_info_v4 表查询视频文件名 - * 优先使用 cachePath 中解密后的 hardlink.db(使用 better-sqlite3) - * 如果失败,则尝试使用 wcdbService.execQuery 查询加密的 hardlink.db + * 使用 wcdbService.execQuery 查询加密的 hardlink.db */ private async queryVideoFileName(md5: string): Promise { - const cachePath = this.getCachePath() const dbPath = this.getDbPath() const wxid = this.getMyWxid() const cleanedWxid = this.cleanWxid(wxid) - this.log('queryVideoFileName 开始', { md5, wxid, cleanedWxid, cachePath, dbPath }) + this.log('queryVideoFileName 开始', { md5, wxid, cleanedWxid, dbPath }) if (!wxid) { this.log('queryVideoFileName: wxid 为空') return undefined } - // 方法1:优先在 cachePath 下查找解密后的 hardlink.db - if (cachePath) { - const cacheDbPaths = [ - join(cachePath, cleanedWxid, 'hardlink.db'), - join(cachePath, wxid, 'hardlink.db'), - join(cachePath, 'hardlink.db'), - join(cachePath, 'databases', cleanedWxid, 'hardlink.db'), - join(cachePath, 'databases', wxid, 'hardlink.db') - ] - - for (const p of cacheDbPaths) { - if (existsSync(p)) { - try { - this.log('尝试缓存 hardlink.db', { path: p }) - const db = new Database(p, { readonly: true }) - const row = db.prepare(` - SELECT file_name, md5 FROM video_hardlink_info_v4 - WHERE md5 = ? - LIMIT 1 - `).get(md5) as { file_name: string; md5: string } | undefined - db.close() - - if (row?.file_name) { - const realMd5 = row.file_name.replace(/\.[^.]+$/, '') - this.log('缓存 hardlink.db 命中', { file_name: row.file_name, realMd5 }) - return realMd5 - } - this.log('缓存 hardlink.db 未命中', { path: p }) - } catch (e) { - this.log('缓存 hardlink.db 查询失败', { path: p, error: String(e) }) - } - } - } - } - - // 方法2:使用 wcdbService.execQuery 查询加密的 hardlink.db + // 使用 wcdbService.execQuery 查询加密的 hardlink.db if (dbPath) { const dbPathLower = dbPath.toLowerCase() const wxidLower = wxid.toLowerCase() diff --git a/package.json b/package.json index 514d99d..7e19d6f 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "electron:build": "npm run build" }, "dependencies": { - "better-sqlite3": "^12.5.0", "echarts": "^5.5.1", "echarts-for-react": "^3.0.2", "electron-store": "^10.0.0", @@ -46,7 +45,6 @@ }, "devDependencies": { "@electron/rebuild": "^4.0.2", - "@types/better-sqlite3": "^7.6.13", "@types/react": "^19.1.0", "@types/react-dom": "^19.1.0", "@vitejs/plugin-react": "^4.3.4",