v2.0.0
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
import { autoUpdater } from 'electron-updater'
|
||||
import { log, isWin } from '@common/utils'
|
||||
import { mainOn } from '@common/mainIpc'
|
||||
import { isExistWindow, sendEvent } from './index'
|
||||
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||
|
||||
autoUpdater.logger = log
|
||||
// autoUpdater.autoDownload = false
|
||||
|
||||
let isFirstCheckedUpdate = true
|
||||
|
||||
log.info('App starting...')
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Open a window that displays the version
|
||||
//
|
||||
// THIS SECTION IS NOT REQUIRED
|
||||
//
|
||||
// This isn't required for auto-updates to work, but it's easier
|
||||
// for the app to show a window than to have to click "About" to see
|
||||
// that updates are working.
|
||||
// -------------------------------------------------------------------
|
||||
// let win
|
||||
|
||||
function sendStatusToWindow(text: string) {
|
||||
log.info(text)
|
||||
// ipcMain.send('message', text)
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Auto updates
|
||||
//
|
||||
// For details about these events, see the Wiki:
|
||||
// https://github.com/electron-userland/electron-builder/wiki/Auto-Update#events
|
||||
//
|
||||
// The app doesn't need to listen to any events except `update-downloaded`
|
||||
//
|
||||
// Uncomment any of the below events to listen for them. Also,
|
||||
// look in the previous section to see them being used.
|
||||
// -------------------------------------------------------------------
|
||||
// autoUpdater.on('checking-for-update', () => {
|
||||
// })
|
||||
// autoUpdater.on('update-available', (ev, info) => {
|
||||
// })
|
||||
// autoUpdater.on('update-not-available', (ev, info) => {
|
||||
// })
|
||||
// autoUpdater.on('error', (ev, err) => {
|
||||
// })
|
||||
// autoUpdater.on('download-progress', (ev, progressObj) => {
|
||||
// })
|
||||
// autoUpdater.on('update-downloaded', (ev, info) => {
|
||||
// // Wait 5 seconds, then quit and install
|
||||
// // In your application, you don't need to wait 5 seconds.
|
||||
// // You could call autoUpdater.quitAndInstall(); immediately
|
||||
// // setTimeout(function() {
|
||||
// // autoUpdater.quitAndInstall()
|
||||
// // }, 5000)
|
||||
|
||||
// })
|
||||
|
||||
interface WaitEvent {
|
||||
type: string
|
||||
info: any
|
||||
}
|
||||
|
||||
let waitEvent: WaitEvent[] = []
|
||||
const handleSendEvent = (action: WaitEvent) => {
|
||||
if (isExistWindow()) {
|
||||
setTimeout(() => { // 延迟发送事件,过早发送可能渲染进程还没启动完成
|
||||
sendEvent(action.type, action.info)
|
||||
}, 2000)
|
||||
} else {
|
||||
waitEvent.push(action)
|
||||
}
|
||||
}
|
||||
|
||||
export default () => {
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
sendStatusToWindow('Checking for update...')
|
||||
})
|
||||
autoUpdater.on('update-available', info => {
|
||||
sendStatusToWindow('Update available.')
|
||||
handleSendEvent({ type: WIN_MAIN_RENDERER_EVENT_NAME.update_available, info })
|
||||
})
|
||||
autoUpdater.on('update-not-available', info => {
|
||||
sendStatusToWindow('Update not available.')
|
||||
handleSendEvent({ type: WIN_MAIN_RENDERER_EVENT_NAME.update_not_available, info })
|
||||
})
|
||||
autoUpdater.on('error', err => {
|
||||
sendStatusToWindow('Error in auto-updater.')
|
||||
handleSendEvent({ type: WIN_MAIN_RENDERER_EVENT_NAME.update_error, info: err.message })
|
||||
})
|
||||
autoUpdater.on('download-progress', progressObj => {
|
||||
let log_message = `Download speed: ${progressObj.bytesPerSecond}`
|
||||
log_message = `${log_message} - Downloaded ${progressObj.percent}%`
|
||||
log_message = `${log_message} (progressObj.transferred/${progressObj.total})`
|
||||
sendStatusToWindow(log_message)
|
||||
handleSendEvent({ type: WIN_MAIN_RENDERER_EVENT_NAME.update_progress, info: progressObj })
|
||||
})
|
||||
autoUpdater.on('update-downloaded', info => {
|
||||
sendStatusToWindow('Update downloaded.')
|
||||
handleSendEvent({ type: WIN_MAIN_RENDERER_EVENT_NAME.update_downloaded, info })
|
||||
})
|
||||
|
||||
mainOn(WIN_MAIN_RENDERER_EVENT_NAME.update_check, () => {
|
||||
checkUpdate()
|
||||
})
|
||||
|
||||
mainOn(WIN_MAIN_RENDERER_EVENT_NAME.quit_update, () => {
|
||||
global.lx.isQuitting = true
|
||||
|
||||
setTimeout(() => {
|
||||
autoUpdater.quitAndInstall(true, true)
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
export const checkUpdate = () => {
|
||||
if (!isFirstCheckedUpdate) {
|
||||
if (waitEvent.length) {
|
||||
waitEvent.forEach((event, index) => {
|
||||
setTimeout(() => { // 延迟发送事件,过早发送可能渲染进程还没启动完成
|
||||
sendEvent(event.type, event.info)
|
||||
}, 2000 * (index + 1))
|
||||
})
|
||||
waitEvent = []
|
||||
}
|
||||
return
|
||||
}
|
||||
isFirstCheckedUpdate = false
|
||||
|
||||
// 由于集合安装包中不包含win arm版,这将会导致arm版更新失败
|
||||
if (isWin && process.arch.includes('arm')) {
|
||||
handleSendEvent({ type: WIN_MAIN_RENDERER_EVENT_NAME.update_error, info: 'failed' })
|
||||
} else {
|
||||
void autoUpdater.checkForUpdates()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import initRendererEvent, { handleKeyDown, hotKeyConfigUpdate } from './rendererEvent'
|
||||
|
||||
import { APP_EVENT_NAMES } from '@common/constants'
|
||||
import { createWindow, minimize, toggleHide, toggleMinimize } from './main'
|
||||
import initUpdate from './autoUpdate'
|
||||
import { HOTKEY_COMMON } from '@common/hotKey'
|
||||
import { quitApp } from '@main/app'
|
||||
|
||||
export default () => {
|
||||
initRendererEvent()
|
||||
initUpdate()
|
||||
|
||||
global.lx.event_app.on('hot_key_down', ({ type, key }) => {
|
||||
let info = global.lx.hotKey.config.global.keys[key]
|
||||
if (info?.type != APP_EVENT_NAMES.winMainName) return
|
||||
switch (info.action) {
|
||||
case HOTKEY_COMMON.close.action:
|
||||
quitApp()
|
||||
break
|
||||
case HOTKEY_COMMON.hide_toggle.action:
|
||||
toggleHide()
|
||||
break
|
||||
case HOTKEY_COMMON.min.action:
|
||||
minimize()
|
||||
break
|
||||
case HOTKEY_COMMON.min_toggle.action:
|
||||
toggleMinimize()
|
||||
break
|
||||
default:
|
||||
handleKeyDown(type, key)
|
||||
break
|
||||
}
|
||||
})
|
||||
global.lx.event_app.on('hot_key_config_update', (config) => {
|
||||
hotKeyConfigUpdate(config)
|
||||
})
|
||||
|
||||
global.lx.event_app.on('app_inited', () => {
|
||||
createWindow()
|
||||
})
|
||||
}
|
||||
|
||||
export * from './main'
|
||||
export * from './rendererEvent'
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
import { BrowserWindow, dialog } from 'electron'
|
||||
import { join } from 'path'
|
||||
import { createTaskBarButtons, getWindowSizeInfo } from './utils'
|
||||
import { encodePath, isLinux, isWin } from '@common/utils'
|
||||
import { openDevTools as handleOpenDevTools } from '@main/utils'
|
||||
import { mainSend } from '@common/mainIpc'
|
||||
import { sendFocus, sendTaskbarButtonClick } from './rendererEvent'
|
||||
|
||||
let browserWindow: Electron.BrowserWindow | null = null
|
||||
|
||||
const winEvent = () => {
|
||||
if (!browserWindow) return
|
||||
|
||||
browserWindow.on('close', event => {
|
||||
if (global.lx.isQuitting || !global.lx.appSetting['tray.enable'] || (!isWin && !global.lx.isTrafficLightClose)) {
|
||||
browserWindow!.setProgressBar(-1)
|
||||
global.lx.event_app.main_window_close()
|
||||
return
|
||||
}
|
||||
|
||||
if (global.lx.isTrafficLightClose) global.lx.isTrafficLightClose = false
|
||||
event.preventDefault()
|
||||
browserWindow!.hide()
|
||||
})
|
||||
|
||||
browserWindow.on('closed', () => {
|
||||
browserWindow = null
|
||||
})
|
||||
|
||||
// browserWindow.on('restore', () => {
|
||||
// browserWindow.webContents.send('restore')
|
||||
// })
|
||||
browserWindow.on('focus', () => {
|
||||
sendFocus()
|
||||
global.lx.event_app.main_window_focus()
|
||||
})
|
||||
|
||||
browserWindow.on('blur', () => {
|
||||
global.lx.event_app.main_window_blur()
|
||||
})
|
||||
|
||||
browserWindow.once('ready-to-show', () => {
|
||||
showWindow()
|
||||
setThumbarButtons()
|
||||
global.lx.event_app.main_window_ready_to_show()
|
||||
})
|
||||
|
||||
browserWindow.on('show', () => {
|
||||
global.lx.event_app.main_window_show()
|
||||
})
|
||||
browserWindow.on('hide', () => {
|
||||
global.lx.event_app.main_window_hide()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const createWindow = () => {
|
||||
closeWindow()
|
||||
const windowSizeInfo = getWindowSizeInfo(global.lx.appSetting['common.windowSizeId'])
|
||||
|
||||
const { shouldUseDarkColors, theme } = global.lx.theme
|
||||
|
||||
/**
|
||||
* Initial window options
|
||||
*/
|
||||
const options: Electron.BrowserWindowConstructorOptions = {
|
||||
height: windowSizeInfo.height,
|
||||
useContentSize: true,
|
||||
width: windowSizeInfo.width,
|
||||
frame: false,
|
||||
transparent: !global.envParams.cmdParams.dt,
|
||||
// enableRemoteModule: false,
|
||||
// icon: join(global.__static, isWin ? 'icons/256x256.ico' : 'icons/512x512.png'),
|
||||
resizable: false,
|
||||
maximizable: false,
|
||||
fullscreenable: true,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegrationInWorker: true,
|
||||
contextIsolation: false,
|
||||
webSecurity: !global.isDev,
|
||||
nodeIntegration: true,
|
||||
spellcheck: false, // 禁用拼写检查器
|
||||
},
|
||||
}
|
||||
if (global.envParams.cmdParams.dt) options.backgroundColor = theme.colors['--color-primary-light-1000']
|
||||
if (global.lx.appSetting['common.startInFullscreen']) {
|
||||
options.fullscreen = true
|
||||
if (isLinux) options.resizable = true
|
||||
}
|
||||
browserWindow = new BrowserWindow(options)
|
||||
|
||||
const winURL = global.isDev ? 'http://localhost:9080' : `file://${join(encodePath(isWin ? __dirname.replaceAll('\\', '/') : __dirname), 'index.html')}`
|
||||
void browserWindow.loadURL(winURL + `?dt=${!!global.envParams.cmdParams.dt}&dark=${shouldUseDarkColors}&theme=${encodeURIComponent(JSON.stringify(theme))}`)
|
||||
|
||||
winEvent()
|
||||
|
||||
if (global.envParams.cmdParams.odt) handleOpenDevTools(browserWindow.webContents)
|
||||
// browserWindow.webContents.openDevTools()
|
||||
}
|
||||
|
||||
export const isExistWindow = (): boolean => !!browserWindow
|
||||
export const isShowWindow = (): boolean => {
|
||||
if (!browserWindow) return false
|
||||
return browserWindow.isVisible() && (isWin ? true : browserWindow.isFocused())
|
||||
}
|
||||
|
||||
export const closeWindow = () => {
|
||||
if (!browserWindow) return
|
||||
browserWindow.close()
|
||||
}
|
||||
|
||||
export const sendEvent = <T = any>(name: string, params?: T) => {
|
||||
if (!browserWindow) return
|
||||
mainSend(browserWindow, name, params)
|
||||
}
|
||||
|
||||
export const showSelectDialog = async(options: Electron.OpenDialogOptions) => {
|
||||
if (!browserWindow) throw new Error('main window is undefined')
|
||||
return await dialog.showOpenDialog(browserWindow, options)
|
||||
}
|
||||
export const showDialog = ({ type, message, detail }: Electron.MessageBoxSyncOptions) => {
|
||||
if (!browserWindow) return
|
||||
dialog.showMessageBoxSync(browserWindow, {
|
||||
type,
|
||||
message,
|
||||
detail,
|
||||
})
|
||||
}
|
||||
export const showSaveDialog = async(options: Electron.SaveDialogOptions) => {
|
||||
if (!browserWindow) throw new Error('main window is undefined')
|
||||
return await dialog.showSaveDialog(browserWindow, options)
|
||||
}
|
||||
export const minimize = () => {
|
||||
if (!browserWindow) return
|
||||
browserWindow.minimize()
|
||||
}
|
||||
export const maximize = () => {
|
||||
if (!browserWindow) return
|
||||
browserWindow.maximize()
|
||||
}
|
||||
export const unmaximize = () => {
|
||||
if (!browserWindow) return
|
||||
browserWindow.unmaximize()
|
||||
}
|
||||
export const toggleHide = () => {
|
||||
if (!browserWindow) return
|
||||
browserWindow.isVisible()
|
||||
? browserWindow.hide()
|
||||
: browserWindow.show()
|
||||
}
|
||||
export const toggleMinimize = () => {
|
||||
if (!browserWindow) return
|
||||
if (browserWindow.isMinimized()) {
|
||||
if (!browserWindow.isVisible()) {
|
||||
browserWindow.show()
|
||||
}
|
||||
browserWindow.restore()
|
||||
browserWindow.focus()
|
||||
} else {
|
||||
browserWindow.minimize()
|
||||
}
|
||||
}
|
||||
export const showWindow = () => {
|
||||
if (!browserWindow) return
|
||||
if (browserWindow.isMinimized()) {
|
||||
browserWindow.restore()
|
||||
}
|
||||
if (browserWindow.isVisible()) {
|
||||
browserWindow.focus()
|
||||
} else {
|
||||
browserWindow.show()
|
||||
}
|
||||
}
|
||||
export const hideWindow = () => {
|
||||
if (!browserWindow) return
|
||||
browserWindow.hide()
|
||||
}
|
||||
export const setWindowBounds = (options: Partial<Electron.Rectangle>) => {
|
||||
if (!browserWindow) return
|
||||
browserWindow.setBounds(options)
|
||||
}
|
||||
export const setProgressBar = (progress: number, options?: Electron.ProgressBarOptions) => {
|
||||
if (!browserWindow) return
|
||||
browserWindow.setProgressBar(progress, options)
|
||||
}
|
||||
export const setIgnoreMouseEvents = (ignore: boolean, options?: Electron.IgnoreMouseEventsOptions) => {
|
||||
if (!browserWindow) return
|
||||
browserWindow.setIgnoreMouseEvents(ignore, options)
|
||||
}
|
||||
export const toggleDevTools = () => {
|
||||
if (!browserWindow) return
|
||||
if (browserWindow.webContents.isDevToolsOpened()) {
|
||||
browserWindow.webContents.closeDevTools()
|
||||
} else {
|
||||
handleOpenDevTools(browserWindow.webContents)
|
||||
}
|
||||
}
|
||||
|
||||
export const setFullScreen = (isFullscreen: boolean): boolean => {
|
||||
if (!browserWindow) return false
|
||||
if (isLinux) { // linux 需要先设置为可调整窗口大小才能全屏
|
||||
if (isFullscreen) {
|
||||
browserWindow.setResizable(isFullscreen)
|
||||
browserWindow.setFullScreen(isFullscreen)
|
||||
} else {
|
||||
browserWindow.setFullScreen(isFullscreen)
|
||||
browserWindow.setResizable(isFullscreen)
|
||||
}
|
||||
} else {
|
||||
browserWindow.setFullScreen(isFullscreen)
|
||||
}
|
||||
return isFullscreen
|
||||
}
|
||||
|
||||
const taskBarButtonFlags: LX.TaskBarButtonFlags = {
|
||||
empty: true,
|
||||
collect: false,
|
||||
play: false,
|
||||
next: true,
|
||||
prev: true,
|
||||
}
|
||||
export const setThumbarButtons = ({ empty, collect, play, next, prev }: LX.TaskBarButtonFlags = taskBarButtonFlags) => {
|
||||
if (!isWin || !browserWindow) return
|
||||
taskBarButtonFlags.empty = empty
|
||||
taskBarButtonFlags.collect = collect
|
||||
taskBarButtonFlags.play = play
|
||||
taskBarButtonFlags.next = next
|
||||
taskBarButtonFlags.prev = prev
|
||||
browserWindow.setThumbarButtons(createTaskBarButtons(taskBarButtonFlags, action => {
|
||||
sendTaskbarButtonClick(action)
|
||||
}))
|
||||
}
|
||||
|
||||
export const setThumbnailClip = (region: Electron.Rectangle) => {
|
||||
if (!browserWindow) return
|
||||
return browserWindow.setThumbnailClip(region)
|
||||
}
|
||||
|
||||
|
||||
export const clearCache = async() => {
|
||||
if (!browserWindow) throw new Error('main window is undefined')
|
||||
return await browserWindow.webContents.session.clearCache()
|
||||
}
|
||||
|
||||
export const getCacheSize = async() => {
|
||||
if (!browserWindow) throw new Error('main window is undefined')
|
||||
return await browserWindow.webContents.session.getCacheSize()
|
||||
}
|
||||
|
||||
export const getWebContents = (): Electron.WebContents => {
|
||||
if (!browserWindow) throw new Error('main window is undefined')
|
||||
return browserWindow.webContents
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
// const path = require('path')
|
||||
import { app } from 'electron'
|
||||
import { mainHandle, mainOn } from '@common/mainIpc'
|
||||
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||
// import { name as defaultName } from '../../../../../package.json'
|
||||
import {
|
||||
minimize,
|
||||
maximize,
|
||||
closeWindow,
|
||||
showWindow,
|
||||
setFullScreen,
|
||||
sendEvent,
|
||||
clearCache,
|
||||
getCacheSize,
|
||||
toggleDevTools,
|
||||
setWindowBounds,
|
||||
setProgressBar,
|
||||
setIgnoreMouseEvents,
|
||||
// setThumbnailClip,
|
||||
setThumbarButtons,
|
||||
toggleMinimize,
|
||||
toggleHide,
|
||||
showSelectDialog,
|
||||
showDialog,
|
||||
showSaveDialog,
|
||||
} from '@main/modules/winMain'
|
||||
import { quitApp } from '@main/app'
|
||||
import { getAllThemes, removeTheme, saveTheme } from '@main/utils'
|
||||
|
||||
export default () => {
|
||||
// 设置应用名称
|
||||
// mainOn(WIN_MAIN_RENDERER_EVENT_NAME.set_app_name, ({ params: name }) => {
|
||||
// if (name == null) {
|
||||
// app.setName(defaultName)
|
||||
// } else {
|
||||
// app.setName(name)
|
||||
// }
|
||||
// })
|
||||
mainOn(WIN_MAIN_RENDERER_EVENT_NAME.quit, () => {
|
||||
quitApp()
|
||||
})
|
||||
mainOn(WIN_MAIN_RENDERER_EVENT_NAME.min_toggle, () => {
|
||||
toggleMinimize()
|
||||
})
|
||||
mainOn(WIN_MAIN_RENDERER_EVENT_NAME.hide_toggle, () => {
|
||||
toggleHide()
|
||||
})
|
||||
mainOn(WIN_MAIN_RENDERER_EVENT_NAME.min, () => {
|
||||
minimize()
|
||||
})
|
||||
mainOn(WIN_MAIN_RENDERER_EVENT_NAME.max, () => {
|
||||
maximize()
|
||||
})
|
||||
mainOn(WIN_MAIN_RENDERER_EVENT_NAME.focus, () => {
|
||||
showWindow()
|
||||
})
|
||||
mainOn<boolean>(WIN_MAIN_RENDERER_EVENT_NAME.close, ({ params: isForce }) => {
|
||||
if (isForce) return app.exit(0)
|
||||
global.lx.isTrafficLightClose = true
|
||||
closeWindow()
|
||||
})
|
||||
// 全屏
|
||||
mainHandle<boolean, boolean>(WIN_MAIN_RENDERER_EVENT_NAME.fullscreen, async({ params: isFullscreen }) => {
|
||||
return setFullScreen(isFullscreen)
|
||||
})
|
||||
|
||||
// 选择目录
|
||||
mainHandle<Electron.OpenDialogOptions, Electron.OpenDialogReturnValue>(WIN_MAIN_RENDERER_EVENT_NAME.show_select_dialog, async({ params: options }) => {
|
||||
return await showSelectDialog(options)
|
||||
})
|
||||
// 显示弹窗信息
|
||||
mainOn<Electron.MessageBoxSyncOptions>(WIN_MAIN_RENDERER_EVENT_NAME.show_dialog, ({ params }) => {
|
||||
showDialog(params)
|
||||
})
|
||||
// 显示保存弹窗
|
||||
mainHandle<Electron.SaveDialogOptions, Electron.SaveDialogReturnValue>(WIN_MAIN_RENDERER_EVENT_NAME.show_save_dialog, async({ params }) => {
|
||||
return await showSaveDialog(params)
|
||||
})
|
||||
|
||||
|
||||
mainHandle(WIN_MAIN_RENDERER_EVENT_NAME.clear_cache, async() => {
|
||||
return await clearCache()
|
||||
})
|
||||
|
||||
mainHandle<number>(WIN_MAIN_RENDERER_EVENT_NAME.get_cache_size, async() => {
|
||||
return await getCacheSize()
|
||||
})
|
||||
|
||||
mainOn(WIN_MAIN_RENDERER_EVENT_NAME.open_dev_tools, () => {
|
||||
toggleDevTools()
|
||||
})
|
||||
|
||||
mainOn<Partial<Electron.Rectangle>>(WIN_MAIN_RENDERER_EVENT_NAME.set_window_size, ({ params }) => {
|
||||
setWindowBounds(params)
|
||||
})
|
||||
|
||||
mainOn<LX.Player.ProgressBarOptions>(WIN_MAIN_RENDERER_EVENT_NAME.progress, ({ params }) => {
|
||||
// console.log(params)
|
||||
setProgressBar(params.progress, {
|
||||
mode: params.mode ?? 'normal',
|
||||
})
|
||||
})
|
||||
|
||||
mainOn<boolean>(WIN_MAIN_RENDERER_EVENT_NAME.set_ignore_mouse_events, ({ params: isIgnored }) => {
|
||||
isIgnored
|
||||
? setIgnoreMouseEvents(isIgnored, { forward: true })
|
||||
: setIgnoreMouseEvents(false)
|
||||
})
|
||||
|
||||
// mainHandle<Electron.Rectangle>(WIN_MAIN_RENDERER_EVENT_NAME.taskbar_set_thumbnail_clip, async({ params }) => {
|
||||
// return setThumbnailClip(params)
|
||||
// })
|
||||
|
||||
mainOn<LX.TaskBarButtonFlags>(WIN_MAIN_RENDERER_EVENT_NAME.player_action_set_buttons, ({ params }) => {
|
||||
setThumbarButtons(params)
|
||||
})
|
||||
|
||||
mainOn(WIN_MAIN_RENDERER_EVENT_NAME.inited, () => {
|
||||
global.lx.event_app.main_window_inited()
|
||||
})
|
||||
|
||||
mainHandle<{ themes: LX.Theme[], userThemes: LX.Theme[] }>(WIN_MAIN_RENDERER_EVENT_NAME.get_themes, async() => {
|
||||
return getAllThemes()
|
||||
})
|
||||
mainHandle<LX.Theme>(WIN_MAIN_RENDERER_EVENT_NAME.save_theme, async({ params: theme }) => {
|
||||
saveTheme(theme)
|
||||
})
|
||||
mainHandle<string>(WIN_MAIN_RENDERER_EVENT_NAME.remove_theme, async({ params: id }) => {
|
||||
removeTheme(id)
|
||||
})
|
||||
}
|
||||
|
||||
export const sendFocus = () => {
|
||||
sendEvent(WIN_MAIN_RENDERER_EVENT_NAME.focus)
|
||||
}
|
||||
|
||||
export const sendTaskbarButtonClick = (action: LX.Player.StatusButtonActions) => {
|
||||
sendEvent(WIN_MAIN_RENDERER_EVENT_NAME.player_action_on_button_click, action)
|
||||
}
|
||||
export const sendConfigChange = (setting: Partial<LX.AppSetting>) => {
|
||||
sendEvent(WIN_MAIN_RENDERER_EVENT_NAME.on_config_change, setting)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { STORE_NAMES } from '@common/constants'
|
||||
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||
import { mainOn, mainHandle } from '@common/mainIpc'
|
||||
import getStore from '@main/utils/store'
|
||||
|
||||
export default () => {
|
||||
mainHandle<string, any>(WIN_MAIN_RENDERER_EVENT_NAME.get_data, ({ params: path }) => {
|
||||
return getStore(STORE_NAMES.DATA).get(path) as any
|
||||
})
|
||||
|
||||
mainOn<{
|
||||
path: string
|
||||
data: any
|
||||
}>(WIN_MAIN_RENDERER_EVENT_NAME.save_data, ({ params: { path, data } }) => {
|
||||
getStore(STORE_NAMES.DATA).set(path, data)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||
import { mainHandle } from '@common/mainIpc'
|
||||
|
||||
|
||||
export default () => {
|
||||
mainHandle<LX.Download.ListItem[]>(WIN_MAIN_RENDERER_EVENT_NAME.download_list_get, async() => {
|
||||
return await global.lx.worker.dbService.getDownloadList()
|
||||
})
|
||||
mainHandle<LX.Download.saveDownloadMusicInfo>(WIN_MAIN_RENDERER_EVENT_NAME.download_list_add, async({ params: { list, addMusicLocationType } }) => {
|
||||
await global.lx.worker.dbService.downloadInfoSave(list, addMusicLocationType)
|
||||
})
|
||||
mainHandle<LX.Download.ListItem[]>(WIN_MAIN_RENDERER_EVENT_NAME.download_list_update, async({ params: list }) => {
|
||||
await global.lx.worker.dbService.downloadInfoUpdate(list)
|
||||
})
|
||||
mainHandle<string[]>(WIN_MAIN_RENDERER_EVENT_NAME.download_list_remove, async({ params: ids }) => {
|
||||
return await global.lx.worker.dbService.downloadInfoRemove(ids)
|
||||
})
|
||||
mainHandle(WIN_MAIN_RENDERER_EVENT_NAME.download_list_clear, async() => {
|
||||
await global.lx.worker.dbService.downloadInfoClear()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||
import { mainHandle } from '@common/mainIpc'
|
||||
import { sendEvent } from '../main'
|
||||
// import getStore from '@common/store'
|
||||
|
||||
|
||||
// const { registerHotkey, unRegisterHotkey } = require('../modules/hotKey/utils')
|
||||
|
||||
// mainHandle(ipcMainWindowNames.set_hot_key_config, async(event, { action, data }) => {
|
||||
// switch (action) {
|
||||
// case 'config':
|
||||
// global.lx_event.hotKey.saveConfig(data.data, MAIN_WINDOW_EVENT_NAME.source)
|
||||
// return
|
||||
// case 'register':
|
||||
// return registerHotkey(data)
|
||||
// case 'unregister':
|
||||
// return unRegisterHotkey(data)
|
||||
// }
|
||||
// })
|
||||
|
||||
export default () => {
|
||||
mainHandle<LX.HotKeyConfigAll>(WIN_MAIN_RENDERER_EVENT_NAME.get_hot_key, async() => {
|
||||
// const electronStore_hotKey = getStore('hotKey')
|
||||
return {
|
||||
local: global.lx.hotKey?.config.local,
|
||||
global: global.lx.hotKey?.config.global,
|
||||
}
|
||||
})
|
||||
|
||||
// global.lx.event_app.on(APP_EVENT_NAMES.hotKeyConfig, (config, source) => {
|
||||
// if (!global.modules.mainWindow || source === MAIN_WINDOW_EVENT_NAME.name) return
|
||||
// mainSend(global.modules.mainWindow, WIN_MAIN_RENDERER_EVENT_NAME.set_hot_key_config, { config, source })
|
||||
// })
|
||||
}
|
||||
|
||||
export const handleKeyDown = (type: string, key: string) => {
|
||||
sendEvent<LX.HotKeyEvent>(WIN_MAIN_RENDERER_EVENT_NAME.key_down, { type, key })
|
||||
}
|
||||
|
||||
export const hotKeyConfigUpdate = (config: LX.HotKeyConfigAll) => {
|
||||
sendEvent<LX.HotKeyConfigAll>(WIN_MAIN_RENDERER_EVENT_NAME.set_hot_key_config, config)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { registerRendererEvents as common } from '@main/modules/commonRenderers/common'
|
||||
import { registerRendererEvents as list } from '@main/modules/commonRenderers/list'
|
||||
import app, { sendConfigChange } from './app'
|
||||
import hotKey from './hotKey'
|
||||
import kw_decodeLyric from './kw_decodeLyric'
|
||||
import userApi from './userApi'
|
||||
import sync from './sync'
|
||||
import data from './data'
|
||||
import music from './music'
|
||||
import download from './download'
|
||||
import { sendEvent } from '../main'
|
||||
|
||||
export * from './app'
|
||||
export * from './hotKey'
|
||||
export * from './userApi'
|
||||
export * from './sync'
|
||||
export * from './process'
|
||||
|
||||
let isInitialized = false
|
||||
export default () => {
|
||||
if (isInitialized) return
|
||||
isInitialized = true
|
||||
|
||||
common(sendEvent)
|
||||
list(sendEvent)
|
||||
app()
|
||||
hotKey()
|
||||
kw_decodeLyric()
|
||||
userApi()
|
||||
sync()
|
||||
data()
|
||||
music()
|
||||
download()
|
||||
|
||||
global.lx.event_app.on('updated_config', (keys, setting) => {
|
||||
sendConfigChange(setting)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { inflate } from 'zlib'
|
||||
import iconv from 'iconv-lite'
|
||||
import { mainHandle } from '@common/mainIpc'
|
||||
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||
|
||||
const handleInflate = async(data: Buffer) => {
|
||||
return await new Promise((resolve: (result: Buffer) => void, reject) => {
|
||||
inflate(data, (err, result) => {
|
||||
if (err) return reject(err)
|
||||
resolve(result)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const buf_key = Buffer.from('yeelion')
|
||||
const buf_key_len = buf_key.length
|
||||
|
||||
const decodeLyric = async(buf: Buffer, isGetLyricx: boolean) => {
|
||||
// const info = buf.slice(0, index).toString()
|
||||
// if (!info.startsWith('tp=content')) return null
|
||||
// const isLyric = info.includes('\r\nlrcx=0\r\n')
|
||||
if (buf.toString('utf8', 0, 10) != 'tp=content') return ''
|
||||
// const index = buf.indexOf('\r\n\r\n') + 4
|
||||
const lrcData = await handleInflate(buf.slice(buf.indexOf('\r\n\r\n') + 4))
|
||||
|
||||
if (!isGetLyricx) return iconv.decode(lrcData, 'gb18030')
|
||||
|
||||
const buf_str = Buffer.from(lrcData.toString(), 'base64')
|
||||
const buf_str_len = buf_str.length
|
||||
const output = new Uint16Array(buf_str_len)
|
||||
let i = 0
|
||||
while (i < buf_str_len) {
|
||||
let j = 0
|
||||
while (j < buf_key_len && i < buf_str_len) {
|
||||
output[i] = buf_str[i] ^ buf_key[j]
|
||||
i++
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
return iconv.decode(Buffer.from(output), 'gb18030')
|
||||
}
|
||||
|
||||
|
||||
export default () => {
|
||||
mainHandle<{ lrcBase64: string, isGetLyricx: boolean }, string>(WIN_MAIN_RENDERER_EVENT_NAME.handle_kw_decode_lyric, async({ params: { lrcBase64, isGetLyricx } }) => {
|
||||
const lrc = await decodeLyric(Buffer.from(lrcBase64, 'base64'), isGetLyricx)
|
||||
return Buffer.from(lrc).toString('base64')
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||
import { mainHandle } from '@common/mainIpc'
|
||||
|
||||
|
||||
export default () => {
|
||||
// =========================歌词=========================
|
||||
mainHandle<string, LX.Player.LyricInfo>(WIN_MAIN_RENDERER_EVENT_NAME.get_palyer_lyric, async({ params: id }) => {
|
||||
// return (getStore(LRC_EDITED, true, false).get(id) as LX.Music.LyricInfo | undefined) ??
|
||||
// getStore(LRC_RAW, true, false).get(id, {}) as LX.Music.LyricInfo
|
||||
return await global.lx.worker.dbService.getPlayerLyric(id)
|
||||
})
|
||||
|
||||
// 原始歌词
|
||||
mainHandle<string, LX.Music.LyricInfo>(WIN_MAIN_RENDERER_EVENT_NAME.get_lyric_raw, async({ params: id }) => {
|
||||
return await global.lx.worker.dbService.getRawLyric(id)
|
||||
})
|
||||
mainHandle<LX.Music.LyricInfoSave>(WIN_MAIN_RENDERER_EVENT_NAME.save_lyric_raw, async({ params: { id, lyrics } }) => {
|
||||
await global.lx.worker.dbService.rawLyricAdd(id, lyrics)
|
||||
})
|
||||
mainHandle(WIN_MAIN_RENDERER_EVENT_NAME.clear_lyric_raw, async() => {
|
||||
await global.lx.worker.dbService.rawLyricClear()
|
||||
})
|
||||
mainHandle(WIN_MAIN_RENDERER_EVENT_NAME.get_lyric_raw_count, async() => {
|
||||
return await global.lx.worker.dbService.rawLyricCount()
|
||||
})
|
||||
|
||||
// 已编辑的歌词
|
||||
mainHandle<string, LX.Music.LyricInfo>(WIN_MAIN_RENDERER_EVENT_NAME.get_lyric_edited, async({ params: id }) => {
|
||||
return await global.lx.worker.dbService.getEditedLyric(id)
|
||||
})
|
||||
mainHandle<LX.Music.LyricInfoSave>(WIN_MAIN_RENDERER_EVENT_NAME.save_lyric_edited, async({ params: { id, lyrics } }) => {
|
||||
await global.lx.worker.dbService.editedLyricUpdateAddAndUpdate(id, lyrics)
|
||||
})
|
||||
mainHandle<string>(WIN_MAIN_RENDERER_EVENT_NAME.remove_lyric_edited, async({ params: id }) => {
|
||||
await global.lx.worker.dbService.editedLyricRemove([id])
|
||||
})
|
||||
mainHandle<string>(WIN_MAIN_RENDERER_EVENT_NAME.clear_lyric_edited, async() => {
|
||||
await global.lx.worker.dbService.editedLyricClear()
|
||||
})
|
||||
mainHandle(WIN_MAIN_RENDERER_EVENT_NAME.get_lyric_edited_count, async() => {
|
||||
return await global.lx.worker.dbService.editedLyricCount()
|
||||
})
|
||||
|
||||
|
||||
// =========================歌曲URL=========================
|
||||
mainHandle<string, string>(WIN_MAIN_RENDERER_EVENT_NAME.get_music_url, async({ params: id }) => {
|
||||
return await global.lx.worker.dbService.getMusicUrl(id) ?? ''
|
||||
})
|
||||
mainHandle<LX.Music.MusicUrlInfo>(WIN_MAIN_RENDERER_EVENT_NAME.save_music_url, async({ params: { id, url } }) => {
|
||||
await global.lx.worker.dbService.musicUrlSave([{ id, url }])
|
||||
})
|
||||
mainHandle(WIN_MAIN_RENDERER_EVENT_NAME.clear_music_url, async() => {
|
||||
await global.lx.worker.dbService.musicUrlClear()
|
||||
})
|
||||
mainHandle(WIN_MAIN_RENDERER_EVENT_NAME.get_music_url_count, async() => {
|
||||
return await global.lx.worker.dbService.musicUrlCount()
|
||||
})
|
||||
|
||||
// =========================换源歌曲=========================
|
||||
mainHandle<string, LX.Music.MusicInfoOnline[]>(WIN_MAIN_RENDERER_EVENT_NAME.get_other_source, async({ params: id }) => {
|
||||
return await global.lx.worker.dbService.getMusicInfoOtherSource(id)
|
||||
})
|
||||
mainHandle<LX.Music.MusicInfoOtherSourceSave>(WIN_MAIN_RENDERER_EVENT_NAME.save_other_source, async({ params: { id, list } }) => {
|
||||
await global.lx.worker.dbService.musicInfoOtherSourceAdd(id, list)
|
||||
})
|
||||
mainHandle(WIN_MAIN_RENDERER_EVENT_NAME.clear_other_source, async() => {
|
||||
await global.lx.worker.dbService.musicInfoOtherSourceClear()
|
||||
})
|
||||
mainHandle(WIN_MAIN_RENDERER_EVENT_NAME.get_other_source_count, async() => {
|
||||
return await global.lx.worker.dbService.musicInfoOtherSourceCount()
|
||||
})
|
||||
|
||||
|
||||
// =========================我的列表=========================
|
||||
// mainHandle<boolean>(WIN_MAIN_RENDERER_EVENT_NAME.get_playlist, async({ params: isIgnoredError = false }) => {
|
||||
// const electronStore_list = getStore('playList', isIgnoredError, false)
|
||||
|
||||
// return {
|
||||
// defaultList: electronStore_list.get('defaultList'),
|
||||
// loveList: electronStore_list.get('loveList'),
|
||||
// tempList: electronStore_list.get('tempList'),
|
||||
// userList: electronStore_list.get('userList'),
|
||||
// downloadList: getStore('downloadList').get('list'),
|
||||
// }
|
||||
// })
|
||||
|
||||
// const handleSaveList = ({ defaultList, loveList, userList, tempList }: Partial<LX.List.MyAllList>) => {
|
||||
// let data: Partial<LX.List.MyAllList> = {}
|
||||
// if (defaultList != null) data.defaultList = defaultList
|
||||
// if (loveList != null) data.loveList = loveList
|
||||
// if (userList != null) data.userList = userList
|
||||
// if (tempList != null) data.tempList = tempList
|
||||
// getStore('playList').set(data)
|
||||
// }
|
||||
// mainOn<LX.List.ListSaveInfo>(WIN_MAIN_RENDERER_EVENT_NAME.save_playlist, ({ params }) => {
|
||||
// switch (params.type) {
|
||||
// case 'myList':
|
||||
// handleSaveList(params.data)
|
||||
// global.lx.event_app.save_my_list(params.data)
|
||||
// break
|
||||
// case 'downloadList':
|
||||
// getStore('downloadList').set('list', params.data)
|
||||
// break
|
||||
// }
|
||||
// })
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||
import { getWebContents } from '../main'
|
||||
|
||||
|
||||
// export default () => {
|
||||
|
||||
|
||||
// }
|
||||
|
||||
/**
|
||||
* 发送桌面歌词进程创建事件
|
||||
* @param port 端口
|
||||
*/
|
||||
export const sendNewDesktopLyricClient = (port: Electron.MessagePortMain) => {
|
||||
getWebContents().postMessage(WIN_MAIN_RENDERER_EVENT_NAME.process_new_desktop_lyric_client, null, [port])
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { mainHandle } from '@common/mainIpc'
|
||||
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||
import { startServer, stopServer, getStatus, generateCode } from '@main/modules/sync'
|
||||
import { sendEvent } from '../main'
|
||||
|
||||
let selectModeListenr: ((mode: LX.Sync.Mode) => void) | null = null
|
||||
|
||||
export default () => {
|
||||
mainHandle<LX.Sync.SyncServiceActions, any>(WIN_MAIN_RENDERER_EVENT_NAME.sync_action, async({ params: data }) => {
|
||||
switch (data.action) {
|
||||
case 'enable':
|
||||
return data.data.enable ? await startServer(parseInt(data.data.port)) : await stopServer()
|
||||
case 'get_status':
|
||||
return getStatus()
|
||||
case 'generate_code':
|
||||
return await generateCode()
|
||||
case 'select_mode':
|
||||
if (selectModeListenr) selectModeListenr(data.data)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const sendSyncAction = (data: LX.Sync.SyncMainWindowActions) => {
|
||||
sendEvent(WIN_MAIN_RENDERER_EVENT_NAME.sync_action, data)
|
||||
}
|
||||
|
||||
export const sendStatus = (status: LX.Sync.Status) => {
|
||||
sendSyncAction({
|
||||
action: 'status',
|
||||
data: status,
|
||||
})
|
||||
}
|
||||
export const sendSelectMode = (keyInfo: LX.Sync.KeyInfo, listener: (mode: LX.Sync.Mode) => void) => {
|
||||
selectModeListenr = listener
|
||||
sendSyncAction({ action: 'select_mode', data: keyInfo })
|
||||
return () => {
|
||||
selectModeListenr = null
|
||||
}
|
||||
}
|
||||
export const sendCloseSelectMode = () => {
|
||||
sendSyncAction({ action: 'close_select_mode' })
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
|
||||
import { mainHandle } from '@common/mainIpc'
|
||||
import {
|
||||
getApiList,
|
||||
importApi,
|
||||
removeApi,
|
||||
setApi,
|
||||
getStatus,
|
||||
request,
|
||||
cancelRequest,
|
||||
setAllowShowUpdateAlert,
|
||||
} from '@main/modules/userApi'
|
||||
import { sendEvent } from '@main/modules/winMain/main'
|
||||
|
||||
export default () => {
|
||||
mainHandle<string, LX.UserApi.ImportUserApi>(WIN_MAIN_RENDERER_EVENT_NAME.import_user_api, async({ params: script }) => {
|
||||
return importApi(script)
|
||||
})
|
||||
|
||||
mainHandle<string[], LX.UserApi.UserApiInfo[]>(WIN_MAIN_RENDERER_EVENT_NAME.remove_user_api, async({ params: apiIds }) => {
|
||||
return await removeApi(apiIds)
|
||||
})
|
||||
|
||||
mainHandle<LX.UserApi.UserApiSetApiParams>(WIN_MAIN_RENDERER_EVENT_NAME.set_user_api, async({ params: apiId }) => {
|
||||
return await setApi(apiId)
|
||||
})
|
||||
|
||||
mainHandle<LX.UserApi.UserApiInfo[]>(WIN_MAIN_RENDERER_EVENT_NAME.get_user_api_list, async() => {
|
||||
return getApiList()
|
||||
})
|
||||
|
||||
mainHandle<LX.UserApi.UserApiStatus>(WIN_MAIN_RENDERER_EVENT_NAME.get_user_api_status, async() => {
|
||||
return getStatus()
|
||||
})
|
||||
|
||||
mainHandle<LX.UserApi.UserApiSetAllowUpdateAlertParams>(WIN_MAIN_RENDERER_EVENT_NAME.user_api_set_allow_update_alert, async({ params: { id, enable } }) => {
|
||||
return setAllowShowUpdateAlert(id, enable)
|
||||
})
|
||||
|
||||
mainHandle<LX.UserApi.UserApiRequestParams>(WIN_MAIN_RENDERER_EVENT_NAME.request_user_api, async({ params }) => {
|
||||
return await request(params)
|
||||
})
|
||||
mainHandle<LX.UserApi.UserApiRequestCancelParams>(WIN_MAIN_RENDERER_EVENT_NAME.request_user_api_cancel, async({ params: requestKey }) => {
|
||||
return cancelRequest(requestKey)
|
||||
})
|
||||
}
|
||||
|
||||
export const sendStatusChange = (status: LX.UserApi.UserApiStatus) => {
|
||||
sendEvent(WIN_MAIN_RENDERER_EVENT_NAME.user_api_status, status)
|
||||
}
|
||||
export const sendShowUpdateAlert = (info: LX.UserApi.UserApiUpdateInfo) => {
|
||||
sendEvent(WIN_MAIN_RENDERER_EVENT_NAME.user_api_show_update_alert, info)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// import fs from 'fs'
|
||||
import { join } from 'path'
|
||||
import { WindowSize, windowSizeList } from '@common/config'
|
||||
import { nativeImage } from 'electron'
|
||||
|
||||
export const getWindowSizeInfo = (windowSizeId: number | string): WindowSize => {
|
||||
return windowSizeList.find(i => i.id == windowSizeId) ?? windowSizeList[0]
|
||||
}
|
||||
|
||||
const getIconPath = (name: string): Electron.NativeImage => {
|
||||
return nativeImage.createFromPath(join(global.staticPath, 'images/taskbar', name + '.png'))
|
||||
}
|
||||
|
||||
export const createTaskBarButtons = ({
|
||||
empty = false,
|
||||
collect = false,
|
||||
play = false,
|
||||
next = true,
|
||||
prev = true,
|
||||
}: LX.TaskBarButtonFlags, onClick: (action: LX.Player.StatusButtonActions) => void): Electron.ThumbarButton[] => {
|
||||
const buttons: Electron.ThumbarButton[] = [
|
||||
collect
|
||||
? {
|
||||
icon: getIconPath('collected'),
|
||||
click() {
|
||||
onClick('unCollect')
|
||||
},
|
||||
tooltip: '取消收藏',
|
||||
flags: ['nobackground'],
|
||||
}
|
||||
: {
|
||||
icon: getIconPath('collect'),
|
||||
click() {
|
||||
onClick('collect')
|
||||
},
|
||||
tooltip: '收藏',
|
||||
flags: ['nobackground'],
|
||||
},
|
||||
{
|
||||
icon: getIconPath('prev'),
|
||||
click() {
|
||||
onClick('prev')
|
||||
},
|
||||
tooltip: '上一曲',
|
||||
flags: prev ? ['nobackground'] : ['nobackground', 'disabled'],
|
||||
},
|
||||
play
|
||||
? {
|
||||
icon: getIconPath('pause'),
|
||||
click() {
|
||||
onClick('pause')
|
||||
},
|
||||
tooltip: '暂停',
|
||||
flags: ['nobackground'],
|
||||
}
|
||||
: {
|
||||
icon: getIconPath('play'),
|
||||
click() {
|
||||
onClick('play')
|
||||
},
|
||||
tooltip: '播放',
|
||||
flags: ['nobackground'],
|
||||
},
|
||||
{
|
||||
icon: getIconPath('next'),
|
||||
click() {
|
||||
onClick('next')
|
||||
},
|
||||
tooltip: '下一曲',
|
||||
flags: next ? ['nobackground'] : ['nobackground', 'disabled'],
|
||||
},
|
||||
]
|
||||
if (empty) {
|
||||
for (const button of buttons) {
|
||||
button.flags = ['nobackground', 'disabled']
|
||||
}
|
||||
}
|
||||
return buttons
|
||||
}
|
||||
Reference in New Issue
Block a user