diff --git a/src/main/config/smartOverride.ts b/src/main/config/smartOverride.ts index 4a629c01..10daa373 100644 --- a/src/main/config/smartOverride.ts +++ b/src/main/config/smartOverride.ts @@ -1,6 +1,6 @@ import { overrideLogger } from '../utils/logger' import { getAppConfig } from './app' -import { addOverrideItem, removeOverrideItem, getOverrideItem } from './override' +import { addOverrideItem, removeOverrideItem, getOverrideItem, getOverride } from './override' const SMART_OVERRIDE_ID = 'smart-core-override' @@ -377,6 +377,13 @@ export async function createSmartOverride(): Promise { smartCollectorSize ) + // 热重载每次生效前都会重新生成覆写,内容未变时跳过写盘,避免无谓的时间戳刷新 + const existing = await getOverrideItem(SMART_OVERRIDE_ID) + if (existing) { + const current = await getOverride(SMART_OVERRIDE_ID, 'js') + if (current === template) return + } + await addOverrideItem({ id: SMART_OVERRIDE_ID, name: 'Smart Core Override', diff --git a/src/main/core/manager.ts b/src/main/core/manager.ts index 6c8a04c9..2be78f6d 100644 --- a/src/main/core/manager.ts +++ b/src/main/core/manager.ts @@ -46,6 +46,7 @@ import { getAxios } from './mihomoApi' import { generateProfile } from './factory' +import { syncSmartModelToTestDir } from './smartModel' import { checkAdminRestartForTun as checkAdminRestartForTunWithRestart, getSessionAdminStatus, @@ -967,6 +968,7 @@ export async function checkProfileConfig( ageSecretKey?: string ): Promise { const corePath = mihomoCorePath(core) + await syncSmartModelToTestDir() try { await execFilePromise(corePath, ['-t', '-f', configPath, '-d', mihomoTestDir()], { diff --git a/src/main/core/mihomoApi.ts b/src/main/core/mihomoApi.ts index 86f59a22..e382608f 100644 --- a/src/main/core/mihomoApi.ts +++ b/src/main/core/mihomoApi.ts @@ -2,7 +2,7 @@ import { createConnection } from 'net' import axios, { AxiosInstance } from 'axios' import WebSocket from 'ws' import { app } from 'electron' -import { getAppConfig, getControledMihomoConfig } from '../config' +import { getAppConfig, getControledMihomoConfig, manageSmartOverride } from '../config' import { mainWindow } from '../window' import { tray } from '../resolve/tray' import { calcTraffic } from '../utils/calc' @@ -434,6 +434,9 @@ export const mihomoHotReloadConfig = async (): Promise => { await restartCore() return } + // Smart 覆写脚本由应用配置生成,必须先同步再生成配置, + // 否则界面上改动的 Smart 选项会沿用旧脚本,要等到下次重启内核才生效 + await manageSmartOverride() const current = await generateProfile() const { diffWorkDir = false } = await getAppConfig() const configPath = diffWorkDir ? mihomoWorkConfigPath(current) : mihomoWorkConfigPath('work') diff --git a/src/main/core/smartModel.ts b/src/main/core/smartModel.ts new file mode 100644 index 00000000..8b3ec2c6 --- /dev/null +++ b/src/main/core/smartModel.ts @@ -0,0 +1,34 @@ +import { copyFile, stat } from 'fs/promises' +import { existsSync } from 'fs' +import path from 'path' +import { mihomoWorkDir, mihomoTestDir } from '../utils/dirs' +import { managerLogger } from '../utils/logger' + +const MODEL_FILE = 'Model.bin' + +async function isSourceNewer(sourcePath: string, targetPath: string): Promise { + try { + const [sourceStats, targetStats] = await Promise.all([stat(sourcePath), stat(targetPath)]) + return sourceStats.mtime > targetStats.mtime + } catch { + return true + } +} + +// Smart 内核在工作目录找不到 Model.bin 时会联网下载模型。而配置检查(mihomo -t)跑在独立的 +// test 目录里,该目录从不包含模型;这次检查又发生在旧内核已经停止、系统代理已经撤下之后, +// 于是下载必然超时失败,每次重启白白多等约 20 秒,且失败不留文件,下次重启重演一遍。 +// 正式工作目录已有模型时先同步过去,跳过这次注定失败的下载。 +export async function syncSmartModelToTestDir(): Promise { + const source = path.join(mihomoWorkDir(), MODEL_FILE) + if (!existsSync(source)) return + + const target = path.join(mihomoTestDir(), MODEL_FILE) + if (existsSync(target) && !(await isSourceNewer(source, target))) return + + try { + await copyFile(source, target) + } catch (error) { + managerLogger.warn('Failed to sync Model.bin into test dir', error) + } +}