mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-04-28 15:09:19 +00:00
fix: support service runtime fallbacks
This commit is contained in:
@@ -6,7 +6,6 @@ import * as https from 'https'
|
|||||||
import * as http from 'http'
|
import * as http from 'http'
|
||||||
import * as fzstd from 'fzstd'
|
import * as fzstd from 'fzstd'
|
||||||
import * as crypto from 'crypto'
|
import * as crypto from 'crypto'
|
||||||
import { app, BrowserWindow, dialog } from 'electron'
|
|
||||||
import { ConfigService } from './config'
|
import { ConfigService } from './config'
|
||||||
import { wcdbService } from './wcdbService'
|
import { wcdbService } from './wcdbService'
|
||||||
import { MessageCacheService } from './messageCacheService'
|
import { MessageCacheService } from './messageCacheService'
|
||||||
@@ -18,6 +17,7 @@ import { voiceTranscribeService } from './voiceTranscribeService'
|
|||||||
import { ImageDecryptService } from './imageDecryptService'
|
import { ImageDecryptService } from './imageDecryptService'
|
||||||
import { CONTACT_REGION_LOOKUP_DATA } from './contactRegionLookupData'
|
import { CONTACT_REGION_LOOKUP_DATA } from './contactRegionLookupData'
|
||||||
import { LRUCache } from '../utils/LRUCache.js'
|
import { LRUCache } from '../utils/LRUCache.js'
|
||||||
|
import { getAppPathFallback, getElectronBrowserWindow, getElectronDialog, getPathFallback, isElectronAppPackaged } from './electronRuntime'
|
||||||
|
|
||||||
export interface ChatSession {
|
export interface ChatSession {
|
||||||
username: string
|
username: string
|
||||||
@@ -498,7 +498,7 @@ class ChatService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async maybeShowInitFailureDialog(errorMessage: string): Promise<void> {
|
private async maybeShowInitFailureDialog(errorMessage: string): Promise<void> {
|
||||||
if (!app.isPackaged) return
|
if (!isElectronAppPackaged()) return
|
||||||
if (this.initFailureDialogShown) return
|
if (this.initFailureDialogShown) return
|
||||||
|
|
||||||
const code = this.extractErrorCode(errorMessage)
|
const code = this.extractErrorCode(errorMessage)
|
||||||
@@ -519,6 +519,8 @@ class ChatService {
|
|||||||
].join('\n')
|
].join('\n')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const dialog = getElectronDialog()
|
||||||
|
if (!dialog?.showMessageBox) return
|
||||||
await dialog.showMessageBox({
|
await dialog.showMessageBox({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
title: 'WeFlow 启动失败',
|
title: 'WeFlow 启动失败',
|
||||||
@@ -600,7 +602,7 @@ class ChatService {
|
|||||||
console.error('[ChatService] 数据库监听回调失败:', error)
|
console.error('[ChatService] 数据库监听回调失败:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const windows = BrowserWindow.getAllWindows()
|
const windows = getElectronBrowserWindow()?.getAllWindows?.() || []
|
||||||
// 广播给所有渲染进程窗口
|
// 广播给所有渲染进程窗口
|
||||||
windows.forEach((win) => {
|
windows.forEach((win) => {
|
||||||
if (!win.isDestroyed()) {
|
if (!win.isDestroyed()) {
|
||||||
@@ -7180,7 +7182,7 @@ class ChatService {
|
|||||||
return join(cachePath, 'Voices')
|
return join(cachePath, 'Voices')
|
||||||
}
|
}
|
||||||
// 回退到默认目录
|
// 回退到默认目录
|
||||||
const documentsPath = app.getPath('documents')
|
const documentsPath = getPathFallback('documents')
|
||||||
return join(documentsPath, 'WeFlow', 'Voices')
|
return join(documentsPath, 'WeFlow', 'Voices')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7190,7 +7192,7 @@ class ChatService {
|
|||||||
return join(cachePath, 'Emojis')
|
return join(cachePath, 'Emojis')
|
||||||
}
|
}
|
||||||
// 回退到默认目录
|
// 回退到默认目录
|
||||||
const documentsPath = app.getPath('documents')
|
const documentsPath = getPathFallback('documents')
|
||||||
return join(documentsPath, 'WeFlow', 'Emojis')
|
return join(documentsPath, 'WeFlow', 'Emojis')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8435,13 +8437,13 @@ class ChatService {
|
|||||||
private async decodeSilkToPcm(silkData: Buffer, sampleRate: number): Promise<Buffer | null> {
|
private async decodeSilkToPcm(silkData: Buffer, sampleRate: number): Promise<Buffer | null> {
|
||||||
try {
|
try {
|
||||||
let wasmPath: string
|
let wasmPath: string
|
||||||
if (app.isPackaged) {
|
if (isElectronAppPackaged()) {
|
||||||
wasmPath = join(process.resourcesPath, 'app.asar.unpacked', 'node_modules', 'silk-wasm', 'lib', 'silk.wasm')
|
wasmPath = join(process.resourcesPath, 'app.asar.unpacked', 'node_modules', 'silk-wasm', 'lib', 'silk.wasm')
|
||||||
if (!existsSync(wasmPath)) {
|
if (!existsSync(wasmPath)) {
|
||||||
wasmPath = join(process.resourcesPath, 'node_modules', 'silk-wasm', 'lib', 'silk.wasm')
|
wasmPath = join(process.resourcesPath, 'node_modules', 'silk-wasm', 'lib', 'silk.wasm')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wasmPath = join(app.getAppPath(), 'node_modules', 'silk-wasm', 'lib', 'silk.wasm')
|
wasmPath = join(getAppPathFallback(), 'node_modules', 'silk-wasm', 'lib', 'silk.wasm')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!existsSync(wasmPath)) {
|
if (!existsSync(wasmPath)) {
|
||||||
@@ -8629,7 +8631,7 @@ class ChatService {
|
|||||||
/** 获取持久化转写缓存文件路径 */
|
/** 获取持久化转写缓存文件路径 */
|
||||||
private getTranscriptCachePath(): string {
|
private getTranscriptCachePath(): string {
|
||||||
const cachePath = this.configService.get('cachePath')
|
const cachePath = this.configService.get('cachePath')
|
||||||
const base = cachePath || join(app.getPath('documents'), 'WeFlow')
|
const base = cachePath || join(getPathFallback('documents'), 'WeFlow')
|
||||||
return join(base, 'Voices', 'transcripts.json')
|
return join(base, 'Voices', 'transcripts.json')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { join } from 'path'
|
import { dirname, join } from 'path'
|
||||||
import { app, safeStorage } from 'electron'
|
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
import Store from 'electron-store'
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
|
||||||
import { expandHomePath } from '../utils/pathUtils'
|
import { expandHomePath } from '../utils/pathUtils'
|
||||||
|
import { getElectronSafeStorage, getPathFallback, isWorkerRuntime } from './electronRuntime'
|
||||||
|
|
||||||
// 加密前缀标记
|
// 加密前缀标记
|
||||||
const SAFE_PREFIX = 'safe:' // safeStorage 加密(普通模式)
|
const SAFE_PREFIX = 'safe:' // safeStorage 加密(普通模式)
|
||||||
const isSafeStorageAvailable = (): boolean => {
|
const isSafeStorageAvailable = (): boolean => {
|
||||||
try {
|
try {
|
||||||
|
const safeStorage = getElectronSafeStorage()
|
||||||
return typeof safeStorage?.isEncryptionAvailable === 'function' && safeStorage.isEncryptionAvailable()
|
return typeof safeStorage?.isEncryptionAvailable === 'function' && safeStorage.isEncryptionAvailable()
|
||||||
} catch {
|
} catch {
|
||||||
return false
|
return false
|
||||||
@@ -112,6 +113,68 @@ interface ConfigSchema {
|
|||||||
aiInsightDebugLogEnabled: boolean
|
aiInsightDebugLogEnabled: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ConfigStoreLike<T extends Record<string, any>> {
|
||||||
|
get<K extends keyof T>(key: K): T[K]
|
||||||
|
set<K extends keyof T>(key: K, value: T[K]): void
|
||||||
|
clear(): void
|
||||||
|
store: T
|
||||||
|
}
|
||||||
|
|
||||||
|
function cloneJson<T>(value: T): T {
|
||||||
|
return JSON.parse(JSON.stringify(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
class JsonConfigStore<T extends Record<string, any>> implements ConfigStoreLike<T> {
|
||||||
|
private readonly filePath: string
|
||||||
|
private readonly defaults: T
|
||||||
|
private data: T
|
||||||
|
|
||||||
|
constructor(options: { name: string; defaults: T; cwd?: string }) {
|
||||||
|
const baseDir = options.cwd || getPathFallback('userData')
|
||||||
|
mkdirSync(baseDir, { recursive: true })
|
||||||
|
this.filePath = join(baseDir, `${options.name}.json`)
|
||||||
|
this.defaults = cloneJson(options.defaults)
|
||||||
|
this.data = cloneJson(options.defaults)
|
||||||
|
this.load()
|
||||||
|
}
|
||||||
|
|
||||||
|
get store(): T {
|
||||||
|
return this.data
|
||||||
|
}
|
||||||
|
|
||||||
|
private load(): void {
|
||||||
|
try {
|
||||||
|
if (!existsSync(this.filePath)) return
|
||||||
|
const raw = readFileSync(this.filePath, 'utf8')
|
||||||
|
const parsed = JSON.parse(raw)
|
||||||
|
if (parsed && typeof parsed === 'object') {
|
||||||
|
this.data = { ...cloneJson(this.defaults), ...parsed }
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
this.data = cloneJson(this.defaults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private persist(): void {
|
||||||
|
mkdirSync(dirname(this.filePath), { recursive: true })
|
||||||
|
writeFileSync(this.filePath, JSON.stringify(this.data), 'utf8')
|
||||||
|
}
|
||||||
|
|
||||||
|
get<K extends keyof T>(key: K): T[K] {
|
||||||
|
return this.data[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
set<K extends keyof T>(key: K, value: T[K]): void {
|
||||||
|
this.data[key] = value
|
||||||
|
this.persist()
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
this.data = cloneJson(this.defaults)
|
||||||
|
this.persist()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 需要 safeStorage 加密的字段(普通模式)
|
// 需要 safeStorage 加密的字段(普通模式)
|
||||||
const ENCRYPTED_STRING_KEYS: Set<string> = new Set([
|
const ENCRYPTED_STRING_KEYS: Set<string> = new Set([
|
||||||
'decryptKey',
|
'decryptKey',
|
||||||
@@ -131,7 +194,7 @@ const LOCKABLE_NUMBER_KEYS: Set<string> = new Set(['imageXorKey'])
|
|||||||
|
|
||||||
export class ConfigService {
|
export class ConfigService {
|
||||||
private static instance: ConfigService
|
private static instance: ConfigService
|
||||||
private store!: Store<ConfigSchema>
|
private store!: ConfigStoreLike<ConfigSchema>
|
||||||
|
|
||||||
// 锁定模式运行时状态
|
// 锁定模式运行时状态
|
||||||
private unlockedKeys: Map<string, any> = new Map()
|
private unlockedKeys: Map<string, any> = new Map()
|
||||||
@@ -225,36 +288,17 @@ export class ConfigService {
|
|||||||
aiInsightDebugLogEnabled: false
|
aiInsightDebugLogEnabled: false
|
||||||
}
|
}
|
||||||
|
|
||||||
const storeOptions: any = {
|
const cwd = String(process.env.WEFLOW_CONFIG_CWD || process.env.WEFLOW_USER_DATA_PATH || '').trim()
|
||||||
|
this.store = new JsonConfigStore<ConfigSchema>({
|
||||||
name: 'WeFlow-config',
|
name: 'WeFlow-config',
|
||||||
defaults,
|
defaults,
|
||||||
projectName: String(process.env.WEFLOW_PROJECT_NAME || 'WeFlow').trim() || 'WeFlow'
|
cwd: cwd || undefined
|
||||||
}
|
})
|
||||||
const runningInWorker = process.env.WEFLOW_WORKER === '1'
|
|
||||||
if (runningInWorker) {
|
|
||||||
const cwd = String(process.env.WEFLOW_CONFIG_CWD || process.env.WEFLOW_USER_DATA_PATH || '').trim()
|
|
||||||
if (cwd) {
|
|
||||||
storeOptions.cwd = cwd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
if (!isWorkerRuntime()) {
|
||||||
this.store = new Store<ConfigSchema>(storeOptions)
|
this.migrateAuthFields()
|
||||||
} catch (error) {
|
this.migrateAiConfig()
|
||||||
const message = String((error as Error)?.message || error || '')
|
|
||||||
if (message.includes('projectName')) {
|
|
||||||
const fallbackOptions = {
|
|
||||||
...storeOptions,
|
|
||||||
projectName: 'WeFlow',
|
|
||||||
cwd: storeOptions.cwd || process.env.WEFLOW_CONFIG_CWD || process.env.WEFLOW_USER_DATA_PATH || process.cwd()
|
|
||||||
}
|
|
||||||
this.store = new Store<ConfigSchema>(fallbackOptions)
|
|
||||||
} else {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
this.migrateAuthFields()
|
|
||||||
this.migrateAiConfig()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// === 状态查询 ===
|
// === 状态查询 ===
|
||||||
@@ -356,6 +400,8 @@ export class ConfigService {
|
|||||||
if (!plaintext) return ''
|
if (!plaintext) return ''
|
||||||
if (plaintext.startsWith(SAFE_PREFIX)) return plaintext
|
if (plaintext.startsWith(SAFE_PREFIX)) return plaintext
|
||||||
if (!isSafeStorageAvailable()) return plaintext
|
if (!isSafeStorageAvailable()) return plaintext
|
||||||
|
const safeStorage = getElectronSafeStorage()
|
||||||
|
if (!safeStorage) return plaintext
|
||||||
const encrypted = safeStorage.encryptString(plaintext)
|
const encrypted = safeStorage.encryptString(plaintext)
|
||||||
return SAFE_PREFIX + encrypted.toString('base64')
|
return SAFE_PREFIX + encrypted.toString('base64')
|
||||||
}
|
}
|
||||||
@@ -364,6 +410,8 @@ export class ConfigService {
|
|||||||
if (!stored) return ''
|
if (!stored) return ''
|
||||||
if (!stored.startsWith(SAFE_PREFIX)) return stored
|
if (!stored.startsWith(SAFE_PREFIX)) return stored
|
||||||
if (!isSafeStorageAvailable()) return ''
|
if (!isSafeStorageAvailable()) return ''
|
||||||
|
const safeStorage = getElectronSafeStorage()
|
||||||
|
if (!safeStorage) return ''
|
||||||
try {
|
try {
|
||||||
const buf = Buffer.from(stored.slice(SAFE_PREFIX.length), 'base64')
|
const buf = Buffer.from(stored.slice(SAFE_PREFIX.length), 'base64')
|
||||||
return safeStorage.decryptString(buf)
|
return safeStorage.decryptString(buf)
|
||||||
@@ -831,7 +879,7 @@ export class ConfigService {
|
|||||||
if (workerUserDataPath) {
|
if (workerUserDataPath) {
|
||||||
return workerUserDataPath
|
return workerUserDataPath
|
||||||
}
|
}
|
||||||
return app?.getPath?.('userData') || process.cwd()
|
return getPathFallback('userData')
|
||||||
}
|
}
|
||||||
|
|
||||||
getCacheBasePath(): string {
|
getCacheBasePath(): string {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { join, dirname } from 'path'
|
import { join, dirname } from 'path'
|
||||||
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs'
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs'
|
||||||
import { app } from 'electron'
|
|
||||||
import { ConfigService } from './config'
|
import { ConfigService } from './config'
|
||||||
|
|
||||||
export interface ContactCacheEntry {
|
export interface ContactCacheEntry {
|
||||||
|
|||||||
96
electron/services/electronRuntime.ts
Normal file
96
electron/services/electronRuntime.ts
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import { homedir, tmpdir } from 'os'
|
||||||
|
import { join } from 'path'
|
||||||
|
|
||||||
|
type RuntimeRequire = (id: string) => any
|
||||||
|
|
||||||
|
let cachedElectron: any | null | false = null
|
||||||
|
|
||||||
|
export function isWorkerRuntime(): boolean {
|
||||||
|
return process.env.WEFLOW_WORKER === '1'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElectronModule(): any | null {
|
||||||
|
if (isWorkerRuntime()) return null
|
||||||
|
if (cachedElectron !== null) return cachedElectron || null
|
||||||
|
try {
|
||||||
|
const runtimeRequire = (0, eval)('require') as RuntimeRequire
|
||||||
|
cachedElectron = runtimeRequire('electron')
|
||||||
|
} catch {
|
||||||
|
cachedElectron = false
|
||||||
|
}
|
||||||
|
return cachedElectron || null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElectronApp(): any | null {
|
||||||
|
return getElectronModule()?.app || null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElectronBrowserWindow(): any | null {
|
||||||
|
return getElectronModule()?.BrowserWindow || null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElectronDialog(): any | null {
|
||||||
|
return getElectronModule()?.dialog || null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElectronSafeStorage(): any | null {
|
||||||
|
return getElectronModule()?.safeStorage || null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getElectronPath(name: string): string | null {
|
||||||
|
try {
|
||||||
|
const getter = getElectronApp()?.getPath
|
||||||
|
if (typeof getter === 'function') {
|
||||||
|
return getter(name)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// fall through to caller fallback
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAppPathFallback(): string {
|
||||||
|
try {
|
||||||
|
const getter = getElectronApp()?.getAppPath
|
||||||
|
if (typeof getter === 'function') {
|
||||||
|
return getter()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
return process.cwd()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPathFallback(name: string): string {
|
||||||
|
const fromElectron = getElectronPath(name)
|
||||||
|
if (fromElectron) return fromElectron
|
||||||
|
|
||||||
|
const home = homedir()
|
||||||
|
switch (name) {
|
||||||
|
case 'userData': {
|
||||||
|
const workerUserDataPath = String(process.env.WEFLOW_USER_DATA_PATH || process.env.WEFLOW_CONFIG_CWD || '').trim()
|
||||||
|
if (workerUserDataPath) return workerUserDataPath
|
||||||
|
if (process.platform === 'win32' && process.env.APPDATA) return join(process.env.APPDATA, 'WeFlow')
|
||||||
|
if (process.platform === 'darwin') return join(home, 'Library', 'Application Support', 'WeFlow')
|
||||||
|
return join(process.env.XDG_CONFIG_HOME || join(home, '.config'), 'WeFlow')
|
||||||
|
}
|
||||||
|
case 'documents':
|
||||||
|
return join(home, 'Documents')
|
||||||
|
case 'desktop':
|
||||||
|
return join(home, 'Desktop')
|
||||||
|
case 'downloads':
|
||||||
|
return join(home, 'Downloads')
|
||||||
|
case 'temp':
|
||||||
|
return tmpdir()
|
||||||
|
case 'appData':
|
||||||
|
return process.platform === 'win32' && process.env.APPDATA ? process.env.APPDATA : join(home, '.config')
|
||||||
|
default:
|
||||||
|
return process.cwd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isElectronAppPackaged(): boolean {
|
||||||
|
const app = getElectronApp()
|
||||||
|
if (typeof app?.isPackaged === 'boolean') return app.isPackaged
|
||||||
|
return Boolean((process as any).resourcesPath && process.env.NODE_ENV !== 'development')
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { app } from 'electron'
|
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import { getPathFallback } from './electronRuntime'
|
||||||
|
|
||||||
export interface ExportRecord {
|
export interface ExportRecord {
|
||||||
exportTime: number
|
exportTime: number
|
||||||
@@ -20,7 +20,7 @@ class ExportRecordService {
|
|||||||
private resolveFilePath(): string {
|
private resolveFilePath(): string {
|
||||||
if (this.filePath) return this.filePath
|
if (this.filePath) return this.filePath
|
||||||
const workerUserDataPath = String(process.env.WEFLOW_USER_DATA_PATH || process.env.WEFLOW_CONFIG_CWD || '').trim()
|
const workerUserDataPath = String(process.env.WEFLOW_USER_DATA_PATH || process.env.WEFLOW_CONFIG_CWD || '').trim()
|
||||||
const userDataPath = workerUserDataPath || app?.getPath?.('userData') || process.cwd()
|
const userDataPath = workerUserDataPath || getPathFallback('userData')
|
||||||
fs.mkdirSync(userDataPath, { recursive: true })
|
fs.mkdirSync(userDataPath, { recursive: true })
|
||||||
this.filePath = path.join(userDataPath, 'weflow-export-records.json')
|
this.filePath = path.join(userDataPath, 'weflow-export-records.json')
|
||||||
return this.filePath
|
return this.filePath
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { app, BrowserWindow } from 'electron'
|
|
||||||
import { basename, dirname, extname, join } from 'path'
|
import { basename, dirname, extname, join } from 'path'
|
||||||
import { pathToFileURL } from 'url'
|
import { pathToFileURL } from 'url'
|
||||||
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, appendFileSync } from 'fs'
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, appendFileSync } from 'fs'
|
||||||
@@ -8,6 +7,7 @@ import crypto from 'crypto'
|
|||||||
import { ConfigService } from './config'
|
import { ConfigService } from './config'
|
||||||
import { wcdbService } from './wcdbService'
|
import { wcdbService } from './wcdbService'
|
||||||
import { decryptDatViaNative, nativeAddonLocation } from './nativeImageDecrypt'
|
import { decryptDatViaNative, nativeAddonLocation } from './nativeImageDecrypt'
|
||||||
|
import { getElectronBrowserWindow, getPathFallback, isElectronAppPackaged } from './electronRuntime'
|
||||||
|
|
||||||
// 获取 ffmpeg-static 的路径
|
// 获取 ffmpeg-static 的路径
|
||||||
function getStaticFfmpegPath(): string | null {
|
function getStaticFfmpegPath(): string | null {
|
||||||
@@ -35,7 +35,7 @@ function getStaticFfmpegPath(): string | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 方法3: 打包后的路径
|
// 方法3: 打包后的路径
|
||||||
if (app?.isPackaged) {
|
if (isElectronAppPackaged()) {
|
||||||
const resourcesPath = process.resourcesPath
|
const resourcesPath = process.resourcesPath
|
||||||
const packedPath = join(resourcesPath, 'app.asar.unpacked', 'node_modules', 'ffmpeg-static', 'ffmpeg.exe')
|
const packedPath = join(resourcesPath, 'app.asar.unpacked', 'node_modules', 'ffmpeg-static', 'ffmpeg.exe')
|
||||||
if (existsSync(packedPath)) {
|
if (existsSync(packedPath)) {
|
||||||
@@ -1475,7 +1475,7 @@ export class ImageDecryptService {
|
|||||||
|
|
||||||
private getActiveWindowsSafely(): Array<{ isDestroyed: () => boolean; webContents: { send: (channel: string, payload: unknown) => void } }> {
|
private getActiveWindowsSafely(): Array<{ isDestroyed: () => boolean; webContents: { send: (channel: string, payload: unknown) => void } }> {
|
||||||
try {
|
try {
|
||||||
const getter = (BrowserWindow as unknown as { getAllWindows?: () => any[] } | undefined)?.getAllWindows
|
const getter = (getElectronBrowserWindow() as { getAllWindows?: () => any[] } | undefined)?.getAllWindows
|
||||||
if (typeof getter !== 'function') return []
|
if (typeof getter !== 'function') return []
|
||||||
const windows = getter()
|
const windows = getter()
|
||||||
if (!Array.isArray(windows)) return []
|
if (!Array.isArray(windows)) return []
|
||||||
@@ -2191,14 +2191,7 @@ export class ImageDecryptService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getElectronPath(name: 'userData' | 'documents' | 'temp'): string | null {
|
private getElectronPath(name: 'userData' | 'documents' | 'temp'): string | null {
|
||||||
try {
|
return getPathFallback(name)
|
||||||
const getter = (app as unknown as { getPath?: (n: string) => string } | undefined)?.getPath
|
|
||||||
if (typeof getter !== 'function') return null
|
|
||||||
const value = getter(name)
|
|
||||||
return typeof value === 'string' && value.trim() ? value : null
|
|
||||||
} catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getUserDataPath(): string {
|
private getUserDataPath(): string {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { join, dirname } from 'path'
|
import { join, dirname } from 'path'
|
||||||
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs'
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs'
|
||||||
import { app } from 'electron'
|
|
||||||
import { ConfigService } from './config'
|
import { ConfigService } from './config'
|
||||||
|
|
||||||
export interface SessionMessageCacheEntry {
|
export interface SessionMessageCacheEntry {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { existsSync, readdirSync, statSync, readFileSync, appendFileSync, mkdirSync } from 'fs'
|
import { existsSync, readdirSync, statSync, readFileSync, appendFileSync, mkdirSync } from 'fs'
|
||||||
import { pathToFileURL } from 'url'
|
import { pathToFileURL } from 'url'
|
||||||
import { app } from 'electron'
|
|
||||||
import { ConfigService } from './config'
|
import { ConfigService } from './config'
|
||||||
import { wcdbService } from './wcdbService'
|
import { wcdbService } from './wcdbService'
|
||||||
|
import { getPathFallback } from './electronRuntime'
|
||||||
|
|
||||||
export interface VideoInfo {
|
export interface VideoInfo {
|
||||||
videoUrl?: string // 视频文件路径(用于 readFile)
|
videoUrl?: string // 视频文件路径(用于 readFile)
|
||||||
@@ -45,7 +45,7 @@ class VideoService {
|
|||||||
try {
|
try {
|
||||||
const timestamp = new Date().toISOString()
|
const timestamp = new Date().toISOString()
|
||||||
const metaStr = meta ? ` ${JSON.stringify(meta)}` : ''
|
const metaStr = meta ? ` ${JSON.stringify(meta)}` : ''
|
||||||
const logDir = join(app.getPath('userData'), 'logs')
|
const logDir = join(getPathFallback('userData'), 'logs')
|
||||||
if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true })
|
if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true })
|
||||||
appendFileSync(join(logDir, 'wcdb.log'), `[${timestamp}] [VideoService] ${message}${metaStr}\n`, 'utf8')
|
appendFileSync(join(logDir, 'wcdb.log'), `[${timestamp}] [VideoService] ${message}${metaStr}\n`, 'utf8')
|
||||||
} catch { }
|
} catch { }
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { app } from 'electron'
|
|
||||||
import { existsSync, mkdirSync, statSync, unlinkSync, createWriteStream, openSync, writeSync, closeSync } from 'fs'
|
import { existsSync, mkdirSync, statSync, unlinkSync, createWriteStream, openSync, writeSync, closeSync } from 'fs'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import * as https from 'https'
|
import * as https from 'https'
|
||||||
import * as http from 'http'
|
import * as http from 'http'
|
||||||
import { ConfigService } from './config'
|
import { ConfigService } from './config'
|
||||||
|
import { getPathFallback } from './electronRuntime'
|
||||||
|
|
||||||
// Sherpa-onnx 类型定义
|
// Sherpa-onnx 类型定义
|
||||||
type OfflineRecognizer = any
|
type OfflineRecognizer = any
|
||||||
@@ -91,7 +91,7 @@ export class VoiceTranscribeService {
|
|||||||
private resolveModelDir(): string {
|
private resolveModelDir(): string {
|
||||||
const configured = this.configService.get('whisperModelDir') as string | undefined
|
const configured = this.configService.get('whisperModelDir') as string | undefined
|
||||||
if (configured) return configured
|
if (configured) return configured
|
||||||
return join(app.getPath('documents'), 'WeFlow', 'models', 'sensevoice')
|
return join(getPathFallback('documents'), 'WeFlow', 'models', 'sensevoice')
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveModelPath(fileName: string): string {
|
private resolveModelPath(fileName: string): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user