mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-10-05 21:05:39 +08:00
之前的状态面板(chat.getCurrentAccountRoot())和自动获取图片密钥(settings.imageKeyRoot) 走两个完全不同的来源,二者漂移导致状态显示 D 盘但实际扫描到 C 盘。 index.ts: - key:autoGetImageKey 优先级调整为 chat.getCurrentAccountRoot() → self.accountRoot → settings.imageKeyRoot → settings.dbRoot,让运行时识别的目录永远最优先。 - db:init:保存 dbRoot 时同步更新 imageKeyRoot,避免 settings 缓存漂移。 - db:reopenWithRoot:用户手动切换根目录时同步更新 imageKeyRoot。 settings-store.ts: - loadSettings:dbRoot 修正时同步 imageKeyRoot;若 imageKeyRoot 指向旧路径或不存在, 回退到 dbRoot。 - 新增 redirectLegacyWeChatFilesToXwechat():当 settings 里的 dbRoot/imageKeyRoot 仍指向 V3 时代的 "Documents\WeChat Files" 路径时,自动重定向到同目录下的 xwechat_files(如果存在)。 老用户升级时无需手动迁移。 wcdb4-client / windows-db-root-discovery / key-service-win: - 候选目录列表只保留 xwechat_files 形态(Documents\xwechat_files、 AppData\Roaming\Tencent\xwechat_files、macOS 的 Library/Containers/.../xwechat_files), 移除 V3 时代的 "WeChat Files" 候选。 - DB_ROOT_NAMES 只包含 xwechat_files,自动扫描也只认 V4 路径。 V3 数据(V1 头 dat / WeChat Files 目录结构)如果用户机器上还存在,仅作为残留, 不会被 WechatExplorer 当成有效数据源。
78 lines
2.3 KiB
TypeScript
78 lines
2.3 KiB
TypeScript
import fs from 'fs-extra'
|
|
import path from 'path'
|
|
|
|
// 仅支持 WeChat 4.0:只扫描 xwechat_files;旧 V3 时代的 "WeChat Files" 不再纳入候选
|
|
const DB_ROOT_NAMES = new Set(['xwechat_files'])
|
|
const SKIPPED_DIRECTORY_NAMES = new Set([
|
|
'$recycle.bin',
|
|
'system volume information',
|
|
'windows',
|
|
'program files',
|
|
'program files (x86)',
|
|
'programdata',
|
|
'recovery'
|
|
])
|
|
const MAX_VISITED_DIRECTORIES_PER_DRIVE = 20_000
|
|
let cachedDiscoveredRoots: string[] | null = null
|
|
|
|
function unique(values: string[]): string[] {
|
|
return Array.from(new Set(values.map((value) => path.normalize(value))))
|
|
}
|
|
|
|
export function getWindowsDrives(): string[] {
|
|
if (process.platform !== 'win32') return []
|
|
const drives: string[] = []
|
|
for (let code = 67; code <= 90; code += 1) {
|
|
const root = `${String.fromCharCode(code)}:\\`
|
|
if (fs.existsSync(root)) drives.push(root)
|
|
}
|
|
return drives
|
|
}
|
|
|
|
export function scanWindowsDbRoots(driveRoots: string[], maxDepth = 3): string[] {
|
|
const results: string[] = []
|
|
|
|
for (const driveRoot of driveRoots) {
|
|
const queue: Array<{ directory: string; depth: number }> = [{ directory: driveRoot, depth: 0 }]
|
|
let visited = 0
|
|
|
|
while (queue.length > 0 && visited < MAX_VISITED_DIRECTORIES_PER_DRIVE) {
|
|
const current = queue.shift()
|
|
if (!current || current.depth >= maxDepth) continue
|
|
|
|
let entries: fs.Dirent[]
|
|
try {
|
|
entries = fs.readdirSync(current.directory, { withFileTypes: true })
|
|
} catch {
|
|
continue
|
|
}
|
|
|
|
for (const entry of entries) {
|
|
if (!entry.isDirectory() || entry.isSymbolicLink()) continue
|
|
const lowered = entry.name.toLowerCase()
|
|
const fullPath = path.join(current.directory, entry.name)
|
|
const depth = current.depth + 1
|
|
visited += 1
|
|
|
|
if (DB_ROOT_NAMES.has(lowered)) {
|
|
results.push(fullPath)
|
|
continue
|
|
}
|
|
if (depth < maxDepth && !SKIPPED_DIRECTORY_NAMES.has(lowered)) {
|
|
queue.push({ directory: fullPath, depth })
|
|
}
|
|
if (visited >= MAX_VISITED_DIRECTORIES_PER_DRIVE) break
|
|
}
|
|
}
|
|
}
|
|
|
|
return unique(results)
|
|
}
|
|
|
|
export function discoverWindowsDbRoots(): string[] {
|
|
if (!cachedDiscoveredRoots) {
|
|
cachedDiscoveredRoots = scanWindowsDbRoots(getWindowsDrives(), 3)
|
|
}
|
|
return [...cachedDiscoveredRoots]
|
|
}
|