Files
LoveLiveMusicPlayer-Next/src/preload/index.ts
2026-08-08 18:34:12 +08:00

169 lines
6.4 KiB
TypeScript

import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
import { IPC } from '@shared/ipc/channels'
/** 暴露给渲染进程的类型安全 API */
const api = {
store: {
get: <T>(key: string): Promise<T> => ipcRenderer.invoke(IPC.STORE_GET, key),
set: (key: string, value: unknown): Promise<void> =>
ipcRenderer.invoke(IPC.STORE_SET, key, value),
delete: (key: string): Promise<void> => ipcRenderer.invoke(IPC.STORE_DELETE, key)
},
db: {
query: <T>(sql: string, params: unknown[] = []): Promise<T[]> =>
ipcRenderer.invoke(IPC.DB_QUERY, sql, params),
exec: (
sql: string,
params: unknown[] = []
): Promise<{ changes: number; lastInsertRowid: number }> =>
ipcRenderer.invoke(IPC.DB_EXEC, sql, params)
},
http: {
start: (root: string, port: number) => ipcRenderer.invoke(IPC.HTTP_START, root, port),
stop: () => ipcRenderer.invoke(IPC.HTTP_STOP),
status: () => ipcRenderer.invoke(IPC.HTTP_STATUS)
},
net: {
lanIps: () => ipcRenderer.invoke(IPC.NET_LAN_IPS),
fetchJson: <T>(url: string): Promise<T> => ipcRenderer.invoke(IPC.NET_FETCH_JSON, url),
fetchText: (url: string): Promise<string> => ipcRenderer.invoke(IPC.NET_FETCH_TEXT, url)
},
library: {
sync: (): Promise<{ version: number; albums: number; musics: number }> =>
ipcRenderer.invoke(IPC.LIBRARY_SYNC),
dataVersion: (): Promise<string> => ipcRenderer.invoke(IPC.LIBRARY_DATA_VERSION)
},
convert: {
start: (input: string, output: string) =>
ipcRenderer.invoke(IPC.CONVERT_START, input, output),
stop: () => ipcRenderer.invoke(IPC.CONVERT_STOP),
onProgress: (cb: (data: { input: string; progress: number }) => void) => {
const listener = (_: IpcRendererEvent, data: { input: string; progress: number }): void =>
cb(data)
ipcRenderer.on(IPC.CONVERT_PROGRESS, listener)
return () => ipcRenderer.removeListener(IPC.CONVERT_PROGRESS, listener)
},
onDone: (cb: (data: { input: string; output: string }) => void) => {
const listener = (_: IpcRendererEvent, data: { input: string; output: string }): void =>
cb(data)
ipcRenderer.on(IPC.CONVERT_DONE, listener)
return () => ipcRenderer.removeListener(IPC.CONVERT_DONE, listener)
}
},
wifi: {
ensureIosWav: (items: { baseUrl: string; musicPath: string }[]) =>
ipcRenderer.invoke(IPC.WIFI_ENSURE_IOS_WAV, items),
cleanIosWav: (item: { baseUrl: string; musicPath: string }) =>
ipcRenderer.invoke(IPC.WIFI_CLEAN_IOS_WAV, item)
},
musicServer: {
start: () => ipcRenderer.invoke(IPC.MUSIC_SERVER_START),
stop: () => ipcRenderer.invoke(IPC.MUSIC_SERVER_STOP),
send: (cmd: string, body: string) => ipcRenderer.invoke(IPC.MUSIC_SERVER_SEND, cmd, body),
onEvent: (cb: (e: unknown) => void) => {
const listener = (_: IpcRendererEvent, e: unknown): void => cb(e)
ipcRenderer.on(IPC.MUSIC_SERVER_EVENT, listener)
return () => ipcRenderer.removeListener(IPC.MUSIC_SERVER_EVENT, listener)
}
},
dataServer: {
start: () => ipcRenderer.invoke(IPC.DATA_SERVER_START),
stop: () => ipcRenderer.invoke(IPC.DATA_SERVER_STOP),
send: (cmd: string, body: string) => ipcRenderer.invoke(IPC.DATA_SERVER_SEND, cmd, body),
onEvent: (cb: (e: unknown) => void) => {
const listener = (_: IpcRendererEvent, e: unknown): void => cb(e)
ipcRenderer.on(IPC.DATA_SERVER_EVENT, listener)
return () => ipcRenderer.removeListener(IPC.DATA_SERVER_EVENT, listener)
}
},
dialog: {
openDir: (): Promise<string | null> => ipcRenderer.invoke(IPC.DIALOG_OPEN_DIR),
openFile: (): Promise<string[] | null> => ipcRenderer.invoke(IPC.DIALOG_OPEN_FILE)
},
lyric: {
toggle: (show: boolean) => ipcRenderer.invoke(IPC.LYRIC_TOGGLE, show),
update: (payload: unknown) => ipcRenderer.send(IPC.LYRIC_UPDATE, payload),
setIgnoreMouse: (ignore: boolean) => ipcRenderer.send(IPC.LYRIC_SET_IGNORE_MOUSE, ignore),
onUpdate: (cb: (payload: unknown) => void) => {
const listener = (_: IpcRendererEvent, p: unknown): void => cb(p)
ipcRenderer.on(IPC.LYRIC_UPDATE, listener)
return () => ipcRenderer.removeListener(IPC.LYRIC_UPDATE, listener)
},
onState: (cb: (show: boolean) => void) => {
const listener = (_: IpcRendererEvent, show: boolean): void => cb(show)
ipcRenderer.on(IPC.LYRIC_STATE, listener)
return () => ipcRenderer.removeListener(IPC.LYRIC_STATE, listener)
}
},
win: {
minimize: () => ipcRenderer.send(IPC.WIN_MIN),
maximize: () => ipcRenderer.send(IPC.WIN_MAX),
close: () => ipcRenderer.send(IPC.WIN_CLOSE)
},
player: {
onControl: (cb: (action: 'playpause' | 'next' | 'prev') => void) => {
const listener = (_: IpcRendererEvent, action: 'playpause' | 'next' | 'prev'): void =>
cb(action)
ipcRenderer.on(IPC.PLAYER_CONTROL, listener)
return () => ipcRenderer.removeListener(IPC.PLAYER_CONTROL, listener)
}
},
exporter: {
run: (
dest: string,
platform: 'android' | 'ios',
list: unknown[]
): Promise<{ done: number; total: number; fail: number }> =>
ipcRenderer.invoke(IPC.EXPORT_SONGS, dest, platform, list),
onProgress: (
cb: (p: {
musicUId: string
status: 'converting' | 'copying' | 'done' | 'fail'
done: number
total: number
}) => void
) => {
const listener = (
_: IpcRendererEvent,
p: {
musicUId: string
status: 'converting' | 'copying' | 'done' | 'fail'
done: number
total: number
}
): void => cb(p)
ipcRenderer.on(IPC.EXPORT_PROGRESS, listener)
return () => ipcRenderer.removeListener(IPC.EXPORT_PROGRESS, listener)
}
},
update: {
check: (): Promise<{
version: string
latest: string
hasUpdate: boolean
message: string
url: string
error?: boolean
}> => ipcRenderer.invoke(IPC.UPDATE_CHECK),
openDownload: (url: string): Promise<boolean> =>
ipcRenderer.invoke(IPC.UPDATE_OPEN_DOWNLOAD, url)
}
}
export type ApiType = typeof api
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
} catch (error) {
console.error(error)
}
} else {
// @ts-ignore (define in dts)
window.electron = electronAPI
// @ts-ignore
window.api = api
}