11import { ipcMain } from "electron" ;
2- import { existsSync , mkdirSync } from "fs" ;
3- import { readdir , readFile , rm , stat , writeFile } from "fs/promises" ;
4- import { join , resolve } from "path" ;
5- import { useStore } from "../store" ;
6- import type Store from "electron-store" ;
7- import type { StoreType } from "../store" ;
2+ import { CacheService , type CacheResourceType , type CacheListItem } from "../services/CacheService" ;
3+ import { MusicCacheService } from "../services/MusicCacheService" ;
84import { processLog } from "../logger" ;
95
10- /**
11- * 缓存资源类型
12- * - music: 音乐缓存
13- * - lyrics: 歌词缓存
14- * - local-data: 本地音乐数据缓存
15- * - playlist-data: 歌单数据缓存
16- */
17- type CacheResourceType = "music" | "lyrics" | "local-data" | "playlist-data" ;
18-
196/**
207 * 缓存 IPC 通用返回结果
218 * @template T 返回数据类型
@@ -29,87 +16,6 @@ type CacheIpcResult<T = any> = {
2916 message ?: string ;
3017} ;
3118
32- /**
33- * 缓存列表项信息
34- */
35- type CacheListItem = {
36- /** 缓存 key(文件名或相对路径) */
37- key : string ;
38- /** 文件大小(字节) */
39- size : number ;
40- /** 最后修改时间(毫秒时间戳) */
41- mtime : number ;
42- } ;
43-
44- /**
45- * 不同缓存类型对应的子目录映射
46- */
47- const CACHE_SUB_DIR : Record < CacheResourceType , string > = {
48- music : "music" ,
49- lyrics : "lyrics" ,
50- "local-data" : "local-data" ,
51- "playlist-data" : "playlist-data" ,
52- } ;
53-
54- /**
55- * 确保缓存根目录及各子目录存在
56- * @param basePath 缓存根路径
57- */
58- const ensureCacheDirs = ( basePath : string ) : void => {
59- if ( ! existsSync ( basePath ) ) mkdirSync ( basePath , { recursive : true } ) ;
60- Object . values ( CACHE_SUB_DIR ) . forEach ( ( sub ) => {
61- const dir = join ( basePath , sub ) ;
62- if ( ! existsSync ( dir ) ) mkdirSync ( dir , { recursive : true } ) ;
63- } ) ;
64- } ;
65-
66- /**
67- * 从 Store 中获取缓存根路径
68- * @param store Electron Store 实例
69- * @returns 缓存根路径
70- * @throws 当未配置 cachePath 时抛出异常
71- */
72- const getCacheBasePath = ( store : Store < StoreType > ) : string => {
73- const base = store . get ( "cachePath" ) as string | undefined ;
74- if ( ! base ) {
75- throw new Error ( "cachePath 未配置" ) ;
76- }
77- return base ;
78- } ;
79-
80- /**
81- * 解析并校验缓存文件路径,防止路径穿越
82- * @param basePath 缓存根路径
83- * @param type 缓存资源类型
84- * @param key 缓存 key(文件名或相对路径)
85- * @returns 目录与最终文件路径
86- */
87- const resolveSafePath = ( basePath : string , type : CacheResourceType , key : string ) => {
88- const dir = join ( basePath , CACHE_SUB_DIR [ type ] ) ;
89- const target = resolve ( dir , key ) ;
90- if ( ! target . startsWith ( resolve ( dir ) ) ) {
91- throw new Error ( "非法的缓存 key" ) ;
92- }
93- return { dir, target } ;
94- } ;
95-
96- /**
97- * 将多种类型的数据转换为 Buffer
98- * @param data 输入数据(Buffer / Uint8Array / ArrayBuffer / string / Node Buffer JSON)
99- * @returns 对应的 Buffer
100- * @throws 不支持的类型时抛出异常
101- */
102- const toBuffer = ( data : any ) : Buffer => {
103- if ( Buffer . isBuffer ( data ) ) return data ;
104- if ( data instanceof Uint8Array ) return Buffer . from ( data ) ;
105- if ( data instanceof ArrayBuffer ) return Buffer . from ( new Uint8Array ( data ) ) ;
106- if ( typeof data === "string" ) return Buffer . from ( data , "utf-8" ) ;
107- if ( data ?. type === "Buffer" && Array . isArray ( data ?. data ) ) {
108- return Buffer . from ( data . data ) ;
109- }
110- throw new Error ( "不支持的缓存写入数据类型" ) ;
111- } ;
112-
11319/**
11420 * 通用错误捕获包装器,为 IPC 返回统一结果结构
11521 * @param action 实际执行的异步逻辑
@@ -129,39 +35,18 @@ const withErrorCatch = async <T>(action: () => Promise<T>): Promise<CacheIpcResu
12935 * 初始化缓存相关 IPC 事件
13036 */
13137const initCacheIpc = ( ) : void => {
132- const store = useStore ( ) ;
133- if ( ! store ) return ;
38+ const cacheService = CacheService . getInstance ( ) ;
39+ const musicCacheService = MusicCacheService . getInstance ( ) ;
13440
135- try {
136- const basePath = getCacheBasePath ( store ) ;
137- ensureCacheDirs ( basePath ) ;
138- } catch ( error ) {
139- processLog . error ( "❌ 初始化缓存目录失败:" , error ) ;
140- }
41+ // 初始化缓存服务
42+ cacheService . init ( ) ;
14143
14244 // 列出指定类型下的缓存文件
14345 ipcMain . handle (
14446 "cache-list" ,
14547 ( _event , type : CacheResourceType ) : Promise < CacheIpcResult < CacheListItem [ ] > > => {
14648 return withErrorCatch ( async ( ) => {
147- const basePath = getCacheBasePath ( store ) ;
148- ensureCacheDirs ( basePath ) ;
149- const dir = join ( basePath , CACHE_SUB_DIR [ type ] ) ;
150- const files = await readdir ( dir , { withFileTypes : true } ) ;
151- const items : CacheListItem [ ] = [ ] ;
152-
153- for ( const file of files ) {
154- if ( ! file . isFile ( ) ) continue ;
155- const filePath = join ( dir , file . name ) ;
156- const info = await stat ( filePath ) ;
157- items . push ( {
158- key : file . name ,
159- size : info . size ,
160- mtime : info . mtimeMs ,
161- } ) ;
162- }
163-
164- return items ;
49+ return await cacheService . list ( type ) ;
16550 } ) ;
16651 } ,
16752 ) ;
@@ -171,10 +56,7 @@ const initCacheIpc = (): void => {
17156 "cache-get" ,
17257 ( _event , type : CacheResourceType , key : string ) : Promise < CacheIpcResult < Buffer > > => {
17358 return withErrorCatch ( async ( ) => {
174- const basePath = getCacheBasePath ( store ) ;
175- ensureCacheDirs ( basePath ) ;
176- const { target } = resolveSafePath ( basePath , type , key ) ;
177- return await readFile ( target ) ;
59+ return await cacheService . get ( type , key ) ;
17860 } ) ;
17961 } ,
18062 ) ;
@@ -189,12 +71,7 @@ const initCacheIpc = (): void => {
18971 data : Buffer | Uint8Array | ArrayBuffer | string ,
19072 ) : Promise < CacheIpcResult < null > > => {
19173 return withErrorCatch ( async ( ) => {
192- const basePath = getCacheBasePath ( store ) ;
193- ensureCacheDirs ( basePath ) ;
194- const { dir, target } = resolveSafePath ( basePath , type , key ) ;
195- if ( ! existsSync ( dir ) ) mkdirSync ( dir , { recursive : true } ) ;
196- const buffer = toBuffer ( data ) ;
197- await writeFile ( target , buffer ) ;
74+ await cacheService . put ( type , key , data ) ;
19875 return null ;
19976 } ) ;
20077 } ,
@@ -205,10 +82,7 @@ const initCacheIpc = (): void => {
20582 "cache-remove" ,
20683 ( _event , type : CacheResourceType , key : string ) : Promise < CacheIpcResult < null > > => {
20784 return withErrorCatch ( async ( ) => {
208- const basePath = getCacheBasePath ( store ) ;
209- ensureCacheDirs ( basePath ) ;
210- const { target } = resolveSafePath ( basePath , type , key ) ;
211- await rm ( target , { force : true } ) ;
85+ await cacheService . remove ( type , key ) ;
21286 return null ;
21387 } ) ;
21488 } ,
@@ -219,14 +93,43 @@ const initCacheIpc = (): void => {
21993 "cache-clear" ,
22094 ( _event , type : CacheResourceType ) : Promise < CacheIpcResult < null > > => {
22195 return withErrorCatch ( async ( ) => {
222- const basePath = getCacheBasePath ( store ) ;
223- const dir = join ( basePath , CACHE_SUB_DIR [ type ] ) ;
224- await rm ( dir , { recursive : true , force : true } ) ;
225- ensureCacheDirs ( basePath ) ;
96+ await cacheService . clear ( type ) ;
22697 return null ;
22798 } ) ;
22899 } ,
229100 ) ;
101+
102+ // 获取所有缓存类型的总大小
103+ ipcMain . handle ( "cache-size" , ( ) : Promise < CacheIpcResult < number > > => {
104+ return withErrorCatch ( async ( ) => {
105+ return await cacheService . getSize ( ) ;
106+ } ) ;
107+ } ) ;
108+
109+ // 检查是否存在音乐缓存
110+ ipcMain . handle ( "music-cache-check" , async ( _event , id : number | string , quality ?: string ) => {
111+ try {
112+ return await musicCacheService . hasCache ( id , quality ) ;
113+ } catch ( error ) {
114+ processLog . error ( "Check music cache failed:" , error ) ;
115+ return null ;
116+ }
117+ } ) ;
118+
119+ // 下载并缓存音乐
120+ ipcMain . handle (
121+ "music-cache-download" ,
122+ async ( _event , id : number | string , url : string , quality ?: string ) => {
123+ try {
124+ const qualitySuffix = quality || "standard" ;
125+ const path = await musicCacheService . cacheMusic ( id , url , qualitySuffix ) ;
126+ return { success : true , path } ;
127+ } catch ( error : any ) {
128+ processLog . error ( "Download music cache failed:" , error ) ;
129+ return { success : false , message : error . message } ;
130+ }
131+ } ,
132+ ) ;
230133} ;
231134
232135export default initCacheIpc ;
0 commit comments