新增同步功能对“不喜欢歌曲”列表的同步

This commit is contained in:
lyswhut
2023-09-05 23:11:09 +08:00
parent fc03cd77fc
commit b1f9e525ab
62 changed files with 1275 additions and 314 deletions
@@ -0,0 +1,3 @@
export { registerRendererEvents } from './winRendererEvent'
export { default } from './rendererEvent'
@@ -0,0 +1,18 @@
import { mainHandle } from '@common/mainIpc'
import { DISLIKE_EVENT_NAME } from '@common/ipcNames'
// 列表操作事件(公共,只注册一次)
export default () => {
mainHandle<LX.Dislike.DislikeInfo>(DISLIKE_EVENT_NAME.get_dislike_music_infos, async() => {
return global.lx.worker.dbService.getDislikeListInfo()
})
mainHandle<LX.Dislike.DislikeMusicInfo[]>(DISLIKE_EVENT_NAME.add_dislike_music_infos, async({ params: listData }) => {
await global.lx.event_dislike.dislike_music_add(listData, false)
})
mainHandle<LX.Dislike.DislikeRules>(DISLIKE_EVENT_NAME.overwrite_dislike_music_infos, async({ params: rules }) => {
await global.lx.event_dislike.dislike_data_overwrite(rules, false)
})
mainHandle(DISLIKE_EVENT_NAME.clear_dislike_music_infos, async() => {
await global.lx.event_dislike.dislike_music_clear(false)
})
}
@@ -0,0 +1,24 @@
import { DISLIKE_EVENT_NAME } from '@common/ipcNames'
// 发送列表操作事件到渲染进程的注册方法
// 哪个渲染进程需要接收则引入此方法注册
export const registerRendererEvents = (sendEvent: <T = any>(name: string, params?: T | undefined) => void) => {
const dislike_music_add = async(listData: LX.Dislike.DislikeMusicInfo[]) => {
sendEvent<LX.Dislike.DislikeMusicInfo[]>(DISLIKE_EVENT_NAME.add_dislike_music_infos, listData)
}
const dislike_data_overwrite = async(rules: LX.Dislike.DislikeRules) => {
sendEvent<LX.Dislike.DislikeRules>(DISLIKE_EVENT_NAME.overwrite_dislike_music_infos, rules)
}
const dislike_music_clear = async() => {
sendEvent(DISLIKE_EVENT_NAME.clear_dislike_music_infos)
}
global.lx.event_dislike.on('dislike_music_add', dislike_music_add)
global.lx.event_dislike.on('dislike_data_overwrite', dislike_data_overwrite)
global.lx.event_dislike.on('dislike_music_clear', dislike_music_clear)
return () => {
global.lx.event_dislike.off('dislike_music_add', dislike_music_add)
global.lx.event_dislike.off('dislike_data_overwrite', dislike_data_overwrite)
global.lx.event_dislike.off('dislike_music_clear', dislike_music_clear)
}
}
@@ -1,7 +1,9 @@
import common from './common'
import list from './list'
import dislike from './dislike'
export default () => {
common()
list()
dislike()
}
+2
View File
@@ -200,6 +200,7 @@ export const connect = (urlInfo: LX.Sync.Client.UrlInfo, keyInfo: LX.Sync.Client
client.remote = message2read.remote
client.remoteQueueList = message2read.createQueueRemote('list')
client.remoteQueueDislike = message2read.createQueueRemote('dislike')
client.addEventListener('message', ({ data }) => {
if (data == 'ping') return
@@ -235,6 +236,7 @@ export const connect = (urlInfo: LX.Sync.Client.UrlInfo, keyInfo: LX.Sync.Client
client!.isReady = false
client!.moduleReadys = {
list: false,
dislike: false,
}
disconnected = false
sendSyncStatus({
@@ -0,0 +1,68 @@
// 这个文件导出的方法将暴露给服务端调用,第一个参数固定为当前 socket 对象
import { handleRemoteDislikeAction, getLocalDislikeData, setLocalDislikeData } from '@main/modules/sync/dislikeEvent'
import { toMD5 } from '@common/utils/nodejs'
import { removeSelectModeListener, sendCloseSelectMode, sendSelectMode } from '@main/modules/winMain'
import log from '@main/modules/sync/log'
import { registerEvent, unregisterEvent } from './localEvent'
const logInfo = (eventName: string, success = false) => {
log.info(`[${eventName}]${eventName.replace('dislike:sync:dislike_sync_', '').replaceAll('_', ' ')}${success ? ' success' : ''}`)
}
// const logError = (eventName: string, err: Error) => {
// log.error(`[${eventName}]${eventName.replace('dislike:sync:dislike_sync_', '').replaceAll('_', ' ')} error: ${err.message}`)
// }
const getSyncMode = async(socket: LX.Sync.Client.Socket): Promise<LX.Sync.Dislike.SyncMode> => new Promise((resolve, reject) => {
const handleDisconnect = (err: Error) => {
sendCloseSelectMode()
removeSelectModeListener()
reject(err)
}
let removeEventClose = socket.onClose(handleDisconnect)
sendSelectMode(socket.data.keyInfo.serverName, 'dislike', (mode) => {
if (mode == null) {
reject(new Error('cancel'))
return
}
resolve(mode)
removeSelectModeListener()
removeEventClose()
})
})
const handler: LX.Sync.ClientSyncHandlerDislikeActions<LX.Sync.Client.Socket> = {
async onDislikeSyncAction(socket, action) {
if (!socket.moduleReadys?.dislike) return
await handleRemoteDislikeAction(action)
},
async dislike_sync_get_md5(socket) {
logInfo('dislike:sync:dislike_sync_get_md5')
return toMD5((await getLocalDislikeData()).trim())
},
async dislike_sync_get_sync_mode(socket) {
return getSyncMode(socket)
},
async dislike_sync_get_list_data(socket) {
logInfo('dislike:sync:dislike_sync_get_list_data')
return getLocalDislikeData()
},
async dislike_sync_set_list_data(socket, data) {
logInfo('dislike:sync:dislike_sync_set_list_data')
await setLocalDislikeData(data)
},
async dislike_sync_finished(socket) {
logInfo('dislike:sync:finished')
socket.moduleReadys.dislike = true
registerEvent(socket)
socket.onClose(() => {
unregisterEvent()
})
},
}
export default handler
@@ -0,0 +1,4 @@
export { default as handler } from './handler'
export * from './localEvent'
@@ -0,0 +1,21 @@
import { registerDislikeActionEvent } from '@main/modules/sync/dislikeEvent'
let unregisterLocalListAction: (() => void) | null
export const registerEvent = (socket: LX.Sync.Client.Socket) => {
// socket = _socket
// socket.onClose(() => {
// unregisterLocalListAction?.()
// unregisterLocalListAction = null
// })
unregisterEvent()
unregisterLocalListAction = registerDislikeActionEvent((action) => {
if (!socket.moduleReadys?.dislike) return
void socket.remoteQueueDislike.onDislikeSyncAction(action)
})
}
export const unregisterEvent = () => {
unregisterLocalListAction?.()
unregisterLocalListAction = null
}
@@ -1,14 +1,20 @@
import * as list from './list'
import * as dislike from './dislike'
// export * as theme from './theme'
export const callObj = Object.assign({}, list.handler)
export const callObj = Object.assign({},
list.handler,
dislike.handler,
)
export const modules = {
list,
dislike,
}
export const featureVersion = {
list: 1,
dislike: 1,
} as const
@@ -1,6 +1,9 @@
// 这个文件导出的方法将暴露给服务端调用,第一个参数固定为当前 socket 对象
import { handleRemoteListAction } from '@main/modules/sync/utils'
import { getLocalListData, setLocalListData } from '../../../utils'
import {
handleRemoteListAction,
getLocalListData,
setLocalListData,
} from '@main/modules/sync/listEvent'
import { toMD5 } from '@common/utils/nodejs'
import { removeSelectModeListener, sendCloseSelectMode, sendSelectMode } from '@main/modules/winMain'
import log from '@main/modules/sync/log'
@@ -12,17 +15,6 @@ const logInfo = (eventName: string, success = false) => {
// const logError = (eventName: string, err: Error) => {
// log.error(`[${eventName}]${eventName.replace('list:sync:list_sync_', '').replaceAll('_', ' ')} error: ${err.message}`)
// }
export const onListSyncAction = async(socket: LX.Sync.Client.Socket, action: LX.Sync.List.ActionList) => {
if (!socket.moduleReadys?.list) return
await handleRemoteListAction(action)
}
export const list_sync_get_md5 = async(socket: LX.Sync.Client.Socket) => {
logInfo('list:sync:list_sync_get_md5')
return toMD5(JSON.stringify(await getLocalListData()))
}
const getSyncMode = async(socket: LX.Sync.Client.Socket): Promise<LX.Sync.List.SyncMode> => new Promise((resolve, reject) => {
const handleDisconnect = (err: Error) => {
sendCloseSelectMode()
@@ -30,7 +22,7 @@ const getSyncMode = async(socket: LX.Sync.Client.Socket): Promise<LX.Sync.List.S
reject(err)
}
let removeEventClose = socket.onClose(handleDisconnect)
sendSelectMode(socket.data.keyInfo.serverName, (mode) => {
sendSelectMode(socket.data.keyInfo.serverName, 'list', (mode) => {
if (mode == null) {
reject(new Error('cancel'))
return
@@ -40,26 +32,41 @@ const getSyncMode = async(socket: LX.Sync.Client.Socket): Promise<LX.Sync.List.S
removeEventClose()
})
})
export const list_sync_get_sync_mode = async(socket: LX.Sync.Client.Socket) => {
return getSyncMode(socket)
}
export const list_sync_get_list_data = async(socket: LX.Sync.Client.Socket) => {
logInfo('list:sync:list_sync_get_list_data')
return getLocalListData()
}
const handler: LX.Sync.ClientSyncHandlerListActions<LX.Sync.Client.Socket> = {
async onListSyncAction(socket, action) {
if (!socket.moduleReadys?.list) return
await handleRemoteListAction(action)
},
export const list_sync_set_list_data = async(socket: LX.Sync.Client.Socket, data: LX.Sync.List.ListData) => {
logInfo('list:sync:list_sync_set_list_data')
await setLocalListData(data)
}
async list_sync_get_md5(socket) {
logInfo('list:sync:list_sync_get_md5')
return toMD5(JSON.stringify(await getLocalListData()))
},
export const list_sync_finished = async(socket: LX.Sync.Client.Socket) => {
logInfo('list:sync:finished')
socket.moduleReadys.list = true
registerEvent(socket)
socket.onClose(() => {
unregisterEvent()
})
async list_sync_get_sync_mode(socket) {
return getSyncMode(socket)
},
async list_sync_get_list_data(socket) {
logInfo('list:sync:list_sync_get_list_data')
return getLocalListData()
},
async list_sync_set_list_data(socket, data) {
logInfo('list:sync:list_sync_set_list_data')
await setLocalListData(data)
},
async list_sync_finished(socket) {
logInfo('list:sync:finished')
socket.moduleReadys.list = true
registerEvent(socket)
socket.onClose(() => {
unregisterEvent()
})
},
}
export default handler
@@ -1,4 +1,4 @@
export * as handler from './handler'
export { default as handler } from './handler'
export * from './localEvent'
@@ -1,4 +1,4 @@
import { registerListActionEvent } from '@main/modules/sync/utils'
import { registerListActionEvent } from '@main/modules/sync/listEvent'
let unregisterLocalListAction: (() => void) | null
@@ -12,11 +12,13 @@ export const getEnabledFeatures = async(socket: LX.Sync.Client.Socket, serverTyp
case 'server':
return {
list: featureVersion.list == supportedFeatures.list,
dislike: featureVersion.dislike == supportedFeatures.dislike,
}
case 'desktop-app':
default:
return {
list: featureVersion.list == supportedFeatures.list,
dislike: featureVersion.dislike == supportedFeatures.dislike,
}
}
}
+50
View File
@@ -0,0 +1,50 @@
export const getLocalDislikeData = async(): Promise<LX.Dislike.DislikeRules> => {
return (await global.lx.worker.dbService.getDislikeListInfo()).rules
}
export const setLocalDislikeData = async(listData: LX.Dislike.DislikeRules) => {
await global.lx.event_dislike.dislike_data_overwrite(listData, true)
}
export const registerDislikeActionEvent = (sendDislikeAction: (action: LX.Sync.Dislike.ActionList) => (void | Promise<void>)) => {
const dislike_music_add = async(listData: LX.Dislike.DislikeMusicInfo[], isRemote: boolean = false) => {
if (isRemote) return
await sendDislikeAction({ action: 'dislike_music_add', data: listData })
}
const dislike_data_overwrite = async(listInfos: LX.Dislike.DislikeRules, isRemote: boolean = false) => {
if (isRemote) return
await sendDislikeAction({ action: 'dislike_data_overwrite', data: listInfos })
}
const dislike_music_clear = async(isRemote: boolean = false) => {
if (isRemote) return
await sendDislikeAction({ action: 'dislike_music_clear' })
}
global.lx.event_dislike.on('dislike_music_add', dislike_music_add)
global.lx.event_dislike.on('dislike_data_overwrite', dislike_data_overwrite)
global.lx.event_dislike.on('dislike_music_clear', dislike_music_clear)
return () => {
global.lx.event_dislike.off('dislike_music_add', dislike_music_add)
global.lx.event_dislike.off('dislike_data_overwrite', dislike_data_overwrite)
global.lx.event_dislike.off('dislike_music_clear', dislike_music_clear)
}
}
export const handleRemoteDislikeAction = async(event: LX.Sync.Dislike.ActionList) => {
// console.log('handleRemoteDislikeAction', event)
switch (event.action) {
case 'dislike_music_add':
await global.lx.event_dislike.dislike_music_add(event.data, true)
break
case 'dislike_data_overwrite':
await global.lx.event_dislike.dislike_data_overwrite(event.data, true)
break
case 'dislike_music_clear':
await global.lx.event_dislike.dislike_music_clear(true)
break
default:
throw new Error('unknown list sync action')
}
}
+144
View File
@@ -0,0 +1,144 @@
import { LIST_IDS } from '@common/constants'
export const getLocalListData = async(): Promise<LX.Sync.List.ListData> => {
const lists: LX.Sync.List.ListData = {
defaultList: await global.lx.worker.dbService.getListMusics(LIST_IDS.DEFAULT),
loveList: await global.lx.worker.dbService.getListMusics(LIST_IDS.LOVE),
userList: [],
}
const userListInfos = await global.lx.worker.dbService.getAllUserList()
for await (const list of userListInfos) {
lists.userList.push(await global.lx.worker.dbService.getListMusics(list.id)
.then(musics => ({ ...list, list: musics })))
}
return lists
}
export const setLocalListData = async(listData: LX.Sync.List.ListData) => {
await global.lx.event_list.list_data_overwrite(listData, true)
}
export const registerListActionEvent = (sendListAction: (action: LX.Sync.List.ActionList) => (void | Promise<void>)) => {
const list_data_overwrite = async(listData: MakeOptional<LX.List.ListDataFull, 'tempList'>, isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_data_overwrite', data: listData })
}
const list_create = async(position: number, listInfos: LX.List.UserListInfo[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_create', data: { position, listInfos } })
}
const list_remove = async(ids: string[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_remove', data: ids })
}
const list_update = async(lists: LX.List.UserListInfo[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_update', data: lists })
}
const list_update_position = async(position: number, ids: string[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_update_position', data: { position, ids } })
}
const list_music_overwrite = async(listId: string, musicInfos: LX.Music.MusicInfo[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_overwrite', data: { listId, musicInfos } })
}
const list_music_add = async(id: string, musicInfos: LX.Music.MusicInfo[], addMusicLocationType: LX.AddMusicLocationType, isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_add', data: { id, musicInfos, addMusicLocationType } })
}
const list_music_move = async(fromId: string, toId: string, musicInfos: LX.Music.MusicInfo[], addMusicLocationType: LX.AddMusicLocationType, isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_move', data: { fromId, toId, musicInfos, addMusicLocationType } })
}
const list_music_remove = async(listId: string, ids: string[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_remove', data: { listId, ids } })
}
const list_music_update = async(musicInfos: LX.List.ListActionMusicUpdate, isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_update', data: musicInfos })
}
const list_music_clear = async(ids: string[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_clear', data: ids })
}
const list_music_update_position = async(listId: string, position: number, ids: string[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_update_position', data: { listId, position, ids } })
}
global.lx.event_list.on('list_data_overwrite', list_data_overwrite)
global.lx.event_list.on('list_create', list_create)
global.lx.event_list.on('list_remove', list_remove)
global.lx.event_list.on('list_update', list_update)
global.lx.event_list.on('list_update_position', list_update_position)
global.lx.event_list.on('list_music_overwrite', list_music_overwrite)
global.lx.event_list.on('list_music_add', list_music_add)
global.lx.event_list.on('list_music_move', list_music_move)
global.lx.event_list.on('list_music_remove', list_music_remove)
global.lx.event_list.on('list_music_update', list_music_update)
global.lx.event_list.on('list_music_clear', list_music_clear)
global.lx.event_list.on('list_music_update_position', list_music_update_position)
return () => {
global.lx.event_list.off('list_data_overwrite', list_data_overwrite)
global.lx.event_list.off('list_create', list_create)
global.lx.event_list.off('list_remove', list_remove)
global.lx.event_list.off('list_update', list_update)
global.lx.event_list.off('list_update_position', list_update_position)
global.lx.event_list.off('list_music_overwrite', list_music_overwrite)
global.lx.event_list.off('list_music_add', list_music_add)
global.lx.event_list.off('list_music_move', list_music_move)
global.lx.event_list.off('list_music_remove', list_music_remove)
global.lx.event_list.off('list_music_update', list_music_update)
global.lx.event_list.off('list_music_clear', list_music_clear)
global.lx.event_list.off('list_music_update_position', list_music_update_position)
}
}
export const handleRemoteListAction = async({ action, data }: LX.Sync.List.ActionList) => {
// console.log('handleRemoteListAction', action)
switch (action) {
case 'list_data_overwrite':
await global.lx.event_list.list_data_overwrite(data, true)
break
case 'list_create':
await global.lx.event_list.list_create(data.position, data.listInfos, true)
break
case 'list_remove':
await global.lx.event_list.list_remove(data, true)
break
case 'list_update':
await global.lx.event_list.list_update(data, true)
break
case 'list_update_position':
await global.lx.event_list.list_update_position(data.position, data.ids, true)
break
case 'list_music_add':
await global.lx.event_list.list_music_add(data.id, data.musicInfos, data.addMusicLocationType, true)
break
case 'list_music_move':
await global.lx.event_list.list_music_move(data.fromId, data.toId, data.musicInfos, data.addMusicLocationType, true)
break
case 'list_music_remove':
await global.lx.event_list.list_music_remove(data.listId, data.ids, true)
break
case 'list_music_update':
await global.lx.event_list.list_music_update(data, true)
break
case 'list_music_update_position':
await global.lx.event_list.list_music_update_position(data.listId, data.position, data.ids, true)
break
case 'list_music_overwrite':
await global.lx.event_list.list_music_overwrite(data.listId, data.musicInfos, true)
break
case 'list_music_clear':
await global.lx.event_list.list_music_clear(data, true)
break
default:
throw new Error('unknown list sync action')
}
}
@@ -0,0 +1,3 @@
export * as sync from './sync'
export { DislikeManage } from './manage'
@@ -0,0 +1,55 @@
import { type UserDataManage } from '../../user'
import { SnapshotDataManage } from './snapshotDataManage'
import { toMD5 } from '../../utils'
import { getLocalDislikeData } from '@main/modules/sync/dislikeEvent'
export class DislikeManage {
snapshotDataManage: SnapshotDataManage
constructor(userDataManage: UserDataManage) {
this.snapshotDataManage = new SnapshotDataManage(userDataManage)
}
createSnapshot = async() => {
const listData = await this.getDislikeRules()
const md5 = toMD5(listData.trim())
const snapshotInfo = await this.snapshotDataManage.getSnapshotInfo()
console.log(md5, snapshotInfo.latest)
if (snapshotInfo.latest == md5) return md5
if (snapshotInfo.list.includes(md5)) {
snapshotInfo.list.splice(snapshotInfo.list.indexOf(md5), 1)
} else await this.snapshotDataManage.saveSnapshot(md5, listData)
if (snapshotInfo.latest) snapshotInfo.list.unshift(snapshotInfo.latest)
snapshotInfo.latest = md5
snapshotInfo.time = Date.now()
this.snapshotDataManage.saveSnapshotInfo(snapshotInfo)
return md5
}
getCurrentListInfoKey = async() => {
const snapshotInfo = await this.snapshotDataManage.getSnapshotInfo()
if (snapshotInfo.latest) {
return snapshotInfo.latest
}
snapshotInfo.latest = toMD5((await this.getDislikeRules()).trim())
this.snapshotDataManage.saveSnapshotInfo(snapshotInfo)
return snapshotInfo.latest
}
getDeviceCurrentSnapshotKey = async(clientId: string) => {
return this.snapshotDataManage.getDeviceCurrentSnapshotKey(clientId)
}
updateDeviceSnapshotKey = async(clientId: string, key: string) => {
await this.snapshotDataManage.updateDeviceSnapshotKey(clientId, key)
}
removeDevice = async(clientId: string) => {
this.snapshotDataManage.removeSnapshotInfo(clientId)
}
getDislikeRules = async() => {
return getLocalDislikeData()
}
}
@@ -0,0 +1,143 @@
import { throttle } from '@common/utils/common'
import fs from 'node:fs'
import path from 'node:path'
import syncLog from '../../../log'
import { getUserConfig, type UserDataManage } from '../../user/data'
import { File } from '../../../../../../common/constants_sync'
import { checkAndCreateDirSync } from '../../utils'
interface SnapshotInfo {
latest: string | null
time: number
list: string[]
clients: Record<string, LX.Sync.Dislike.ListInfo>
}
export class SnapshotDataManage {
userDataManage: UserDataManage
dislikeDir: string
snapshotDir: string
snapshotInfoFilePath: string
snapshotInfo: SnapshotInfo
clientSnapshotKeys: string[]
private readonly saveSnapshotInfoThrottle: () => void
isIncluedsDevice = (key: string) => {
return this.clientSnapshotKeys.includes(key)
}
clearOldSnapshot = async() => {
if (!this.snapshotInfo) return
const snapshotList = this.snapshotInfo.list.filter(key => !this.isIncluedsDevice(key))
// console.log(snapshotList.length, lx.config.maxSnapshotNum)
const userMaxSnapshotNum = getUserConfig(this.userDataManage.userName).maxSnapshotNum
let requiredSave = snapshotList.length > userMaxSnapshotNum
while (snapshotList.length > userMaxSnapshotNum) {
const name = snapshotList.pop()
if (name) {
await this.removeSnapshot(name)
this.snapshotInfo.list.splice(this.snapshotInfo.list.indexOf(name), 1)
} else break
}
if (requiredSave) this.saveSnapshotInfo(this.snapshotInfo)
}
updateDeviceSnapshotKey = async(clientId: string, key: string) => {
// console.log('updateDeviceSnapshotKey', key)
let client = this.snapshotInfo.clients[clientId]
if (!client) client = this.snapshotInfo.clients[clientId] = { snapshotKey: '', lastSyncDate: 0 }
if (client.snapshotKey) this.clientSnapshotKeys.splice(this.clientSnapshotKeys.indexOf(client.snapshotKey), 1)
client.snapshotKey = key
client.lastSyncDate = Date.now()
this.clientSnapshotKeys.push(key)
this.saveSnapshotInfoThrottle()
}
getDeviceCurrentSnapshotKey = async(clientId: string) => {
// console.log('updateDeviceSnapshotKey', key)
const client = this.snapshotInfo.clients[clientId]
return client?.snapshotKey
}
getSnapshotInfo = async(): Promise<SnapshotInfo> => {
return this.snapshotInfo
}
saveSnapshotInfo = (info: SnapshotInfo) => {
this.snapshotInfo = info
this.saveSnapshotInfoThrottle()
}
removeSnapshotInfo = (clientId: string) => {
let client = this.snapshotInfo.clients[clientId]
if (!client) return
if (client.snapshotKey) this.clientSnapshotKeys.splice(this.clientSnapshotKeys.indexOf(client.snapshotKey), 1)
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete this.snapshotInfo.clients[clientId]
this.saveSnapshotInfoThrottle()
}
getSnapshot = async(name: string) => {
const filePath = path.join(this.snapshotDir, `snapshot_${name}`)
let listData: LX.Dislike.DislikeRules
try {
listData = (await fs.promises.readFile(filePath)).toString('utf-8')
} catch (err) {
syncLog.warn(err)
return null
}
return listData
}
saveSnapshot = async(name: string, data: string) => {
syncLog.info('saveSnapshot', this.userDataManage.userName, name)
const filePath = path.join(this.snapshotDir, `snapshot_${name}`)
try {
fs.writeFileSync(filePath, data)
} catch (err) {
syncLog.error(err)
throw err
}
}
removeSnapshot = async(name: string) => {
syncLog.info('removeSnapshot', this.userDataManage.userName, name)
const filePath = path.join(this.snapshotDir, `snapshot_${name}`)
try {
fs.unlinkSync(filePath)
} catch (err) {
syncLog.error(err)
}
}
constructor(userDataManage: UserDataManage) {
this.userDataManage = userDataManage
this.dislikeDir = path.join(userDataManage.userDir, File.dislikeDir)
checkAndCreateDirSync(this.dislikeDir)
this.snapshotDir = path.join(this.dislikeDir, File.dislikeSnapshotDir)
checkAndCreateDirSync(this.snapshotDir)
this.snapshotInfoFilePath = path.join(this.dislikeDir, File.dislikeSnapshotInfoJSON)
this.snapshotInfo = fs.existsSync(this.snapshotInfoFilePath)
? JSON.parse(fs.readFileSync(this.snapshotInfoFilePath).toString())
: { latest: null, time: 0, list: [], clients: {} }
this.saveSnapshotInfoThrottle = throttle(() => {
fs.writeFile(this.snapshotInfoFilePath, JSON.stringify(this.snapshotInfo), 'utf8', (err) => {
if (err) console.error(err)
void this.clearOldSnapshot()
})
})
this.clientSnapshotKeys = Object.values(this.snapshotInfo.clients).map(device => device.snapshotKey).filter(k => k)
}
}
// type UserDataManages = Map<string, UserDataManage>
// export const createUserDataManage = (user: LX.UserConfig) => {
// const manage = Object.create(userDataManage) as typeof userDataManage
// manage.userDir = user.dataPath
// }
@@ -0,0 +1,35 @@
// 这个文件导出的方法将暴露给客户端调用,第一个参数固定为当前 socket 对象
// import { throttle } from '@common/utils/common'
// import { sendSyncActionList } from '@main/modules/winMain'
// import { SYNC_CLOSE_CODE } from '@/constants'
// import { SYNC_CLOSE_CODE } from '@common/constants_sync'
import { SYNC_CLOSE_CODE } from '@common/constants_sync'
import { getUserSpace } from '@main/modules/sync/server/user'
import { handleRemoteDislikeAction } from '@main/modules/sync/dislikeEvent'
// import { encryptMsg } from '@/utils/tools'
const handler: LX.Sync.ServerSyncHandlerDislikeActions<LX.Sync.Server.Socket> = {
async onDislikeSyncAction(socket, action) {
if (!socket.moduleReadys.dislike) return
await handleRemoteDislikeAction(action)
const userSpace = getUserSpace(socket.userInfo.name)
const key = await userSpace.dislikeManage.createSnapshot()
userSpace.dislikeManage.updateDeviceSnapshotKey(socket.keyInfo.clientId, key)
const currentUserName = socket.userInfo.name
const currentId = socket.keyInfo.clientId
socket.broadcast((client) => {
if (client.keyInfo.clientId == currentId || !client.moduleReadys?.dislike || client.userInfo.name != currentUserName) return
void client.remoteQueueDislike.onDislikeSyncAction(action).then(async() => {
return userSpace.dislikeManage.updateDeviceSnapshotKey(client.keyInfo.clientId, key)
}).catch(err => {
// TODO send status
client.close(SYNC_CLOSE_CODE.failed)
// client.moduleReadys.dislike = false
console.log(err.message)
})
})
},
}
export default handler
@@ -0,0 +1,3 @@
export { default as handler } from './handler'
export { sync } from './sync'
export * from './localEvent'
@@ -0,0 +1,43 @@
import { SYNC_CLOSE_CODE } from '@common/constants_sync'
import { registerDislikeActionEvent } from '../../../../dislikeEvent'
import { getUserSpace } from '../../../user'
// let socket: LX.Sync.Server.Socket | null
let unregisterLocalListAction: (() => void) | null
const sendListAction = async(wss: LX.Sync.Server.SocketServer, action: LX.Sync.Dislike.ActionList) => {
// console.log('sendListAction', action.action)
const userSpace = getUserSpace()
let key = ''
for (const client of wss.clients) {
if (!client.moduleReadys?.dislike) continue
// eslint-disable-next-line require-atomic-updates
if (!key) key = await userSpace.dislikeManage.createSnapshot()
void client.remoteQueueDislike.onDislikeSyncAction(action).then(async() => {
return userSpace.dislikeManage.updateDeviceSnapshotKey(client.keyInfo.clientId, key)
}).catch(err => {
// TODO send status
client.close(SYNC_CLOSE_CODE.failed)
// client.moduleReadys.dislike = false
console.log(err.message)
})
}
}
export const registerEvent = (wss: LX.Sync.Server.SocketServer) => {
// socket = _socket
// socket.onClose(() => {
// unregisterLocalListAction?.()
// unregisterLocalListAction = null
// })
unregisterEvent()
unregisterLocalListAction = registerDislikeActionEvent((action) => {
void sendListAction(wss, action)
})
}
export const unregisterEvent = () => {
unregisterLocalListAction?.()
unregisterLocalListAction = null
}
@@ -0,0 +1,232 @@
// import { SYNC_CLOSE_CODE } from '../../../../constants'
import { removeSelectModeListener, sendCloseSelectMode, sendSelectMode } from '@main/modules/winMain'
import { getUserSpace } from '../../../user'
import { getLocalDislikeData, setLocalDislikeData } from '@main/modules/sync/dislikeEvent'
import { SYNC_CLOSE_CODE } from '@common/constants_sync'
import { filterRules } from '../utils'
// import { LIST_IDS } from '@common/constants'
// type ListInfoType = LX.Dislike.UserListInfoFull | LX.Dislike.MyDefaultListInfoFull | LX.Dislike.MyLoveListInfoFull
// let wss: LX.Sync.Server.SocketServer | null
let syncingId: string | null = null
const wait = async(time = 1000) => await new Promise((resolve, reject) => setTimeout(resolve, time))
const getRemoteListData = async(socket: LX.Sync.Server.Socket): Promise<LX.Dislike.DislikeRules> => {
console.log('getRemoteListData')
return (await socket.remoteQueueDislike.dislike_sync_get_list_data()) ?? ''
}
const getRemoteDataMD5 = async(socket: LX.Sync.Server.Socket): Promise<string> => {
return socket.remoteQueueDislike.dislike_sync_get_md5()
}
// const getLocalDislikeData async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.Dislike.ListData> => {
// return getUserSpace(socket.userInfo.name).listManage.getListData()
// }
const getSyncMode = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.Dislike.SyncMode> => new Promise((resolve, reject) => {
const handleDisconnect = (err: Error) => {
sendCloseSelectMode()
removeSelectModeListener()
reject(err)
}
let removeEventClose = socket.onClose(handleDisconnect)
sendSelectMode(socket.keyInfo.deviceName, 'dislike', (mode) => {
if (mode == null) {
reject(new Error('cancel'))
return
}
resolve(mode)
removeSelectModeListener()
removeEventClose()
})
})
// const getSyncMode = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.Dislike.SyncMode> => {
// return socket.remoteQueueDislike.list_sync_get_sync_mode()
// }
const finishedSync = async(socket: LX.Sync.Server.Socket) => {
await socket.remoteQueueDislike.dislike_sync_finished()
}
const setLocalList = async(socket: LX.Sync.Server.Socket, listData: LX.Dislike.DislikeRules) => {
await setLocalDislikeData(listData)
const userSpace = getUserSpace(socket.userInfo.name)
return userSpace.listManage.createSnapshot()
}
const overwriteRemoteListData = async(socket: LX.Sync.Server.Socket, listData: LX.Dislike.DislikeRules, key: string, excludeIds: string[] = []) => {
const action = { action: 'dislike_data_overwrite', data: listData } as const
const tasks: Array<Promise<void>> = []
const userSpace = getUserSpace(socket.userInfo.name)
socket.broadcast((client) => {
if (excludeIds.includes(client.keyInfo.clientId) || client.userInfo?.name != socket.userInfo.name || !client.moduleReadys?.dislike) return
tasks.push(client.remoteQueueDislike.onDislikeSyncAction(action).then(async() => {
return userSpace.dislikeManage.updateDeviceSnapshotKey(client.keyInfo.clientId, key)
}).catch(err => {
// TODO send status
client.close(SYNC_CLOSE_CODE.failed)
// client.moduleReadys.list = false
console.log(err.message)
}))
})
if (!tasks.length) return
await Promise.all(tasks)
}
const setRemotelList = async(socket: LX.Sync.Server.Socket, listData: LX.Dislike.DislikeRules, key: string): Promise<void> => {
await socket.remoteQueueDislike.dislike_sync_set_list_data(listData)
const userSpace = getUserSpace(socket.userInfo.name)
await userSpace.dislikeManage.updateDeviceSnapshotKey(socket.keyInfo.clientId, key)
}
const mergeList = (socket: LX.Sync.Server.Socket, sourceListData: LX.Dislike.DislikeRules, targetListData: LX.Dislike.DislikeRules): LX.Dislike.DislikeRules => {
return Array.from(filterRules(sourceListData + '\n' + targetListData)).join('\n')
}
const handleMergeListData = async(socket: LX.Sync.Server.Socket): Promise<[LX.Dislike.DislikeRules, boolean, boolean]> => {
const mode: LX.Sync.Dislike.SyncMode = await getSyncMode(socket)
if (mode == 'cancel') throw new Error('cancel')
const [remoteListData, localListData] = await Promise.all([getRemoteListData(socket), getLocalDislikeData()])
console.log('handleMergeListData', 'remoteListData, localListData')
let listData: LX.Dislike.DislikeRules
let requiredUpdateLocalListData = true
let requiredUpdateRemoteListData = true
switch (mode) {
case 'merge_local_remote':
listData = mergeList(socket, localListData, remoteListData)
break
case 'merge_remote_local':
listData = mergeList(socket, remoteListData, localListData)
break
case 'overwrite_local_remote':
listData = localListData
requiredUpdateLocalListData = false
break
case 'overwrite_remote_local':
listData = remoteListData
requiredUpdateRemoteListData = false
break
// case 'none': return null
// case 'cancel':
default: throw new Error('cancel')
}
return [listData, requiredUpdateLocalListData, requiredUpdateRemoteListData]
}
const handleSyncList = async(socket: LX.Sync.Server.Socket) => {
const [remoteListData, localListData] = await Promise.all([getRemoteListData(socket), getLocalDislikeData()])
console.log('handleSyncList', 'remoteListData, localListData')
console.log('localListData', localListData.length)
console.log('remoteListData', remoteListData.length)
const userSpace = getUserSpace(socket.userInfo.name)
const clientId = socket.keyInfo.clientId
if (localListData.length) {
if (remoteListData.length) {
const [mergedList, requiredUpdateLocalListData, requiredUpdateRemoteListData] = await handleMergeListData(socket)
console.log('handleMergeListData', 'mergedList', requiredUpdateLocalListData, requiredUpdateRemoteListData)
let key
if (requiredUpdateLocalListData) {
key = await setLocalList(socket, mergedList)
await overwriteRemoteListData(socket, mergedList, key, [clientId])
if (!requiredUpdateRemoteListData) await userSpace.dislikeManage.updateDeviceSnapshotKey(clientId, key)
}
if (requiredUpdateRemoteListData) {
if (!key) key = await userSpace.dislikeManage.getCurrentListInfoKey()
await setRemotelList(socket, mergedList, key)
}
} else {
await setRemotelList(socket, localListData, await userSpace.dislikeManage.getCurrentListInfoKey())
}
} else {
let key: string
if (remoteListData.length) {
key = await setLocalList(socket, remoteListData)
await overwriteRemoteListData(socket, remoteListData, key, [clientId])
}
key ??= await userSpace.dislikeManage.getCurrentListInfoKey()
await userSpace.dislikeManage.updateDeviceSnapshotKey(clientId, key)
}
}
const mergeDataFromSnapshot = (
sourceList: LX.Dislike.DislikeRules,
targetList: LX.Dislike.DislikeRules,
snapshotList: LX.Dislike.DislikeRules,
): LX.Dislike.DislikeRules => {
const removedRules = new Set<string>()
const sourceRules = filterRules(sourceList)
const targetRules = filterRules(targetList)
if (snapshotList) {
const snapshotRules = filterRules(snapshotList)
for (const m of snapshotRules.values()) {
if (!sourceRules.has(m) || !targetRules.has(m)) removedRules.add(m)
}
}
return Array.from(new Set(Array.from([...sourceRules, ...targetRules]).filter((rule) => {
return !removedRules.has(rule)
}))).join('\n')
}
const checkListLatest = async(socket: LX.Sync.Server.Socket) => {
const remoteListMD5 = await getRemoteDataMD5(socket)
const userSpace = getUserSpace(socket.userInfo.name)
const userCurrentListInfoKey = await userSpace.dislikeManage.getDeviceCurrentSnapshotKey(socket.keyInfo.clientId)
const currentListInfoKey = await userSpace.dislikeManage.getCurrentListInfoKey()
const latest = remoteListMD5 == currentListInfoKey
if (latest && userCurrentListInfoKey != currentListInfoKey) await userSpace.dislikeManage.updateDeviceSnapshotKey(socket.keyInfo.clientId, currentListInfoKey)
return latest
}
const handleMergeListDataFromSnapshot = async(socket: LX.Sync.Server.Socket, snapshot: LX.Dislike.DislikeRules) => {
if (await checkListLatest(socket)) return
const [remoteListData, localListData] = await Promise.all([getRemoteListData(socket), getLocalDislikeData()])
const newDislikeData = mergeDataFromSnapshot(localListData, remoteListData, snapshot)
const key = await setLocalList(socket, newDislikeData)
const err = await setRemotelList(socket, newDislikeData, key).catch(err => err)
await overwriteRemoteListData(socket, newDislikeData, key, [socket.keyInfo.clientId])
if (err) throw err
}
const syncDislike = async(socket: LX.Sync.Server.Socket) => {
// socket.data.snapshotFilePath = getSnapshotFilePath(socket.keyInfo)
// console.log(socket.keyInfo)
const user = getUserSpace(socket.userInfo.name)
const userCurrentDislikeInfoKey = await user.dislikeManage.getDeviceCurrentSnapshotKey(socket.keyInfo.clientId)
if (userCurrentDislikeInfoKey) {
const listData = await user.dislikeManage.snapshotDataManage.getSnapshot(userCurrentDislikeInfoKey)
if (listData) {
console.log('handleMergeDislikeDataFromSnapshot')
await handleMergeListDataFromSnapshot(socket, listData)
return
}
}
await handleSyncList(socket)
}
export const sync = async(socket: LX.Sync.Server.Socket) => {
let disconnected = false
socket.onClose(() => {
disconnected = true
if (syncingId == socket.keyInfo.clientId) syncingId = null
})
while (true) {
if (disconnected) throw new Error('disconnected')
if (!syncingId) break
await wait()
}
syncingId = socket.keyInfo.clientId
await syncDislike(socket).then(async() => {
await finishedSync(socket)
socket.moduleReadys.dislike = true
}).finally(() => {
syncingId = null
})
}
@@ -0,0 +1,23 @@
import { SPLIT_CHAR } from '@common/constants'
export const filterRules = (rules: string) => {
const list: string[] = []
for (const item of rules.split('\n')) {
if (!item) continue
let [name, singer] = item.split(SPLIT_CHAR.DISLIKE_NAME)
if (name) {
name = name.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim()
if (singer) {
singer = singer.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim()
list.push(`${name}${SPLIT_CHAR.DISLIKE_NAME}${singer}`)
} else {
list.push(name)
}
} else if (singer) {
singer = singer.replaceAll(SPLIT_CHAR.DISLIKE_NAME, SPLIT_CHAR.DISLIKE_NAME_ALIAS).toLocaleLowerCase().trim()
list.push(`${SPLIT_CHAR.DISLIKE_NAME}${singer}`)
}
}
return new Set(list)
}
@@ -1,16 +1,22 @@
import { sync } from './list'
import { sync as listSync } from './list'
import { sync as dislikeSync } from './dislike'
export const callObj = Object.assign({},
sync.handler,
listSync.handler,
dislikeSync.handler,
)
export const modules = {
list: sync,
list: listSync,
dislike: dislikeSync,
}
export { ListManage } from './list'
export { DislikeManage } from './dislike'
export const featureVersion = {
list: 1,
dislike: 1,
} as const
@@ -1,7 +1,7 @@
import { type UserDataManage } from '../../user'
import { SnapshotDataManage } from './snapshotDataManage'
import { toMD5 } from '../../utils'
import { getLocalListData } from '@main/modules/sync/utils'
import { getLocalListData } from '@main/modules/sync/listEvent'
export class ListManage {
snapshotDataManage: SnapshotDataManage
@@ -5,7 +5,7 @@
// import { SYNC_CLOSE_CODE } from '@common/constants_sync'
import { SYNC_CLOSE_CODE } from '@common/constants_sync'
import { getUserSpace } from '@main/modules/sync/server/user'
import { handleRemoteListAction } from '@main/modules/sync/utils'
import { handleRemoteListAction } from '@main/modules/sync/listEvent'
// import { encryptMsg } from '@/utils/tools'
// let wss: LX.SocketServer | null
@@ -146,23 +146,27 @@ import { handleRemoteListAction } from '@main/modules/sync/utils'
// // }
// }
export const onListSyncAction = async(socket: LX.Sync.Server.Socket, action: LX.Sync.List.ActionList) => {
if (!socket.moduleReadys.list) return
await handleRemoteListAction(action)
const userSpace = getUserSpace(socket.userInfo.name)
const key = await userSpace.listManage.createSnapshot()
userSpace.listManage.updateDeviceSnapshotKey(socket.keyInfo.clientId, key)
const currentUserName = socket.userInfo.name
const currentId = socket.keyInfo.clientId
socket.broadcast((client) => {
if (client.keyInfo.clientId == currentId || !client.moduleReadys?.list || client.userInfo.name != currentUserName) return
void client.remoteQueueList.onListSyncAction(action).then(async() => {
return userSpace.listManage.updateDeviceSnapshotKey(client.keyInfo.clientId, key)
}).catch(err => {
// TODO send status
client.close(SYNC_CLOSE_CODE.failed)
// client.moduleReadys.list = false
console.log(err.message)
const handler: LX.Sync.ServerSyncHandlerListActions<LX.Sync.Server.Socket> = {
async onListSyncAction(socket, action) {
if (!socket.moduleReadys.list) return
await handleRemoteListAction(action)
const userSpace = getUserSpace(socket.userInfo.name)
const key = await userSpace.listManage.createSnapshot()
userSpace.listManage.updateDeviceSnapshotKey(socket.keyInfo.clientId, key)
const currentUserName = socket.userInfo.name
const currentId = socket.keyInfo.clientId
socket.broadcast((client) => {
if (client.keyInfo.clientId == currentId || !client.moduleReadys?.list || client.userInfo.name != currentUserName) return
void client.remoteQueueList.onListSyncAction(action).then(async() => {
return userSpace.listManage.updateDeviceSnapshotKey(client.keyInfo.clientId, key)
}).catch(err => {
// TODO send status
client.close(SYNC_CLOSE_CODE.failed)
// client.moduleReadys.list = false
console.log(err.message)
})
})
})
},
}
export default handler
@@ -1,3 +1,3 @@
export * as handler from './handler'
export { default as handler } from './handler'
export { sync } from './sync'
export * from './localEvent'
@@ -1,5 +1,5 @@
import { SYNC_CLOSE_CODE } from '@common/constants_sync'
import { registerListActionEvent } from '../../../../utils'
import { registerListActionEvent } from '../../../../listEvent'
import { getUserSpace } from '../../../user'
// let socket: LX.Sync.Server.Socket | null
@@ -1,7 +1,7 @@
// import { SYNC_CLOSE_CODE } from '../../../../constants'
import { removeSelectModeListener, sendCloseSelectMode, sendSelectMode } from '@main/modules/winMain'
import { getUserSpace, getUserConfig } from '../../../user'
import { getLocalListData, setLocalListData } from '@main/modules/sync/utils'
import { getLocalListData, setLocalListData } from '@main/modules/sync/listEvent'
import { SYNC_CLOSE_CODE } from '@common/constants_sync'
// import { LIST_IDS } from '@common/constants'
@@ -38,7 +38,7 @@ const getSyncMode = async(socket: LX.Sync.Server.Socket): Promise<LX.Sync.List.S
reject(err)
}
let removeEventClose = socket.onClose(handleDisconnect)
sendSelectMode(socket.keyInfo.deviceName, (mode) => {
sendSelectMode(socket.keyInfo.deviceName, 'list', (mode) => {
if (mode == null) {
reject(new Error('cancel'))
return
@@ -156,9 +156,11 @@ const handleStartServer = async(port = 9527, ip = '0.0.0.0') => await new Promis
socket.isReady = false
socket.moduleReadys = {
list: false,
dislike: false,
}
socket.feature = {
list: false,
dislike: false,
}
socket.on('pong', () => {
socket.isAlive = true
@@ -196,6 +198,7 @@ const handleStartServer = async(port = 9527, ip = '0.0.0.0') => await new Promis
})
socket.remote = msg2call.remote
socket.remoteQueueList = msg2call.createQueueRemote('list')
socket.remoteQueueDislike = msg2call.createQueueRemote('dislike')
socket.addEventListener('message', ({ data }) => {
if (typeof data != 'string') return
void decryptMsg(socket.keyInfo, data).then((data) => {
@@ -1,11 +1,13 @@
import { UserDataManage } from './data'
import {
ListManage,
DislikeManage,
} from '../modules'
export interface UserSpace {
dataManage: UserDataManage
listManage: ListManage
dislikeManage: DislikeManage
getDecices: () => Promise<LX.Sync.ServerKeyInfo[]>
removeDevice: (clientId: string) => Promise<void>
}
@@ -34,9 +36,11 @@ export const getUserSpace = (userName = 'default') => {
console.log('new user data manage:', userName)
const dataManage = new UserDataManage(userName)
const listManage = new ListManage(dataManage)
const dislikeManage = new DislikeManage(dataManage)
users.set(userName, user = {
dataManage,
listManage,
dislikeManage,
async getDecices() {
return this.dataManage.getAllClientKeyInfo()
},
-144
View File
@@ -2,7 +2,6 @@ import { createCipheriv, createDecipheriv, publicEncrypt, privateDecrypt, consta
import os, { networkInterfaces } from 'node:os'
import zlib from 'node:zlib'
import cp from 'node:child_process'
import { LIST_IDS } from '@common/constants'
export const getAddress = (): string[] => {
@@ -89,146 +88,3 @@ export const rsaEncrypt = (buffer: Buffer, key: string): string => {
export const rsaDecrypt = (buffer: Buffer, key: string): Buffer => {
return privateDecrypt({ key, padding: constants.RSA_PKCS1_OAEP_PADDING }, buffer)
}
export const getLocalListData = async(): Promise<LX.Sync.List.ListData> => {
const lists: LX.Sync.List.ListData = {
defaultList: await global.lx.worker.dbService.getListMusics(LIST_IDS.DEFAULT),
loveList: await global.lx.worker.dbService.getListMusics(LIST_IDS.LOVE),
userList: [],
}
const userListInfos = await global.lx.worker.dbService.getAllUserList()
for await (const list of userListInfos) {
lists.userList.push(await global.lx.worker.dbService.getListMusics(list.id)
.then(musics => ({ ...list, list: musics })))
}
return lists
}
export const setLocalListData = async(listData: LX.Sync.List.ListData) => {
await global.lx.event_list.list_data_overwrite(listData, true)
}
export const registerListActionEvent = (sendListAction: (action: LX.Sync.List.ActionList) => (void | Promise<void>)) => {
const list_data_overwrite = async(listData: MakeOptional<LX.List.ListDataFull, 'tempList'>, isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_data_overwrite', data: listData })
}
const list_create = async(position: number, listInfos: LX.List.UserListInfo[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_create', data: { position, listInfos } })
}
const list_remove = async(ids: string[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_remove', data: ids })
}
const list_update = async(lists: LX.List.UserListInfo[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_update', data: lists })
}
const list_update_position = async(position: number, ids: string[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_update_position', data: { position, ids } })
}
const list_music_overwrite = async(listId: string, musicInfos: LX.Music.MusicInfo[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_overwrite', data: { listId, musicInfos } })
}
const list_music_add = async(id: string, musicInfos: LX.Music.MusicInfo[], addMusicLocationType: LX.AddMusicLocationType, isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_add', data: { id, musicInfos, addMusicLocationType } })
}
const list_music_move = async(fromId: string, toId: string, musicInfos: LX.Music.MusicInfo[], addMusicLocationType: LX.AddMusicLocationType, isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_move', data: { fromId, toId, musicInfos, addMusicLocationType } })
}
const list_music_remove = async(listId: string, ids: string[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_remove', data: { listId, ids } })
}
const list_music_update = async(musicInfos: LX.List.ListActionMusicUpdate, isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_update', data: musicInfos })
}
const list_music_clear = async(ids: string[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_clear', data: ids })
}
const list_music_update_position = async(listId: string, position: number, ids: string[], isRemote: boolean = false) => {
if (isRemote) return
await sendListAction({ action: 'list_music_update_position', data: { listId, position, ids } })
}
global.lx.event_list.on('list_data_overwrite', list_data_overwrite)
global.lx.event_list.on('list_create', list_create)
global.lx.event_list.on('list_remove', list_remove)
global.lx.event_list.on('list_update', list_update)
global.lx.event_list.on('list_update_position', list_update_position)
global.lx.event_list.on('list_music_overwrite', list_music_overwrite)
global.lx.event_list.on('list_music_add', list_music_add)
global.lx.event_list.on('list_music_move', list_music_move)
global.lx.event_list.on('list_music_remove', list_music_remove)
global.lx.event_list.on('list_music_update', list_music_update)
global.lx.event_list.on('list_music_clear', list_music_clear)
global.lx.event_list.on('list_music_update_position', list_music_update_position)
return () => {
global.lx.event_list.off('list_data_overwrite', list_data_overwrite)
global.lx.event_list.off('list_create', list_create)
global.lx.event_list.off('list_remove', list_remove)
global.lx.event_list.off('list_update', list_update)
global.lx.event_list.off('list_update_position', list_update_position)
global.lx.event_list.off('list_music_overwrite', list_music_overwrite)
global.lx.event_list.off('list_music_add', list_music_add)
global.lx.event_list.off('list_music_move', list_music_move)
global.lx.event_list.off('list_music_remove', list_music_remove)
global.lx.event_list.off('list_music_update', list_music_update)
global.lx.event_list.off('list_music_clear', list_music_clear)
global.lx.event_list.off('list_music_update_position', list_music_update_position)
}
}
export const handleRemoteListAction = async({ action, data }: LX.Sync.List.ActionList) => {
// console.log('handleRemoteListAction', action)
switch (action) {
case 'list_data_overwrite':
await global.lx.event_list.list_data_overwrite(data, true)
break
case 'list_create':
await global.lx.event_list.list_create(data.position, data.listInfos, true)
break
case 'list_remove':
await global.lx.event_list.list_remove(data, true)
break
case 'list_update':
await global.lx.event_list.list_update(data, true)
break
case 'list_update_position':
await global.lx.event_list.list_update_position(data.position, data.ids, true)
break
case 'list_music_add':
await global.lx.event_list.list_music_add(data.id, data.musicInfos, data.addMusicLocationType, true)
break
case 'list_music_move':
await global.lx.event_list.list_music_move(data.fromId, data.toId, data.musicInfos, data.addMusicLocationType, true)
break
case 'list_music_remove':
await global.lx.event_list.list_music_remove(data.listId, data.ids, true)
break
case 'list_music_update':
await global.lx.event_list.list_music_update(data, true)
break
case 'list_music_update_position':
await global.lx.event_list.list_music_update_position(data.listId, data.position, data.ids, true)
break
case 'list_music_overwrite':
await global.lx.event_list.list_music_overwrite(data.listId, data.musicInfos, true)
break
case 'list_music_clear':
await global.lx.event_list.list_music_clear(data, true)
break
default:
throw new Error('unknown list sync action')
}
}
@@ -1,5 +1,6 @@
import { registerRendererEvents as common } from '@main/modules/commonRenderers/common'
import { registerRendererEvents as list } from '@main/modules/commonRenderers/list'
import { registerRendererEvents as dislike } from '@main/modules/commonRenderers/dislike'
import app, { sendConfigChange } from './app'
import hotKey from './hotKey'
import kw_decodeLyric from './kw_decodeLyric'
@@ -25,6 +26,7 @@ export default () => {
common(sendEvent)
list(sendEvent)
dislike(sendEvent)
app()
hotKey()
kw_decodeLyric()
@@ -70,16 +70,6 @@ export default () => {
return global.lx.worker.dbService.musicInfoOtherSourceCount()
})
// =========================不喜欢的歌曲=========================
mainHandle<LX.Dislike.DislikeInfo>(WIN_MAIN_RENDERER_EVENT_NAME.get_dislike_music_infos, async() => {
return global.lx.worker.dbService.getDislikeListInfo()
})
mainHandle<LX.Dislike.DislikeMusicInfo[]>(WIN_MAIN_RENDERER_EVENT_NAME.add_dislike_music_infos, async({ params: infos }) => {
await global.lx.worker.dbService.dislikeInfoAdd(infos)
})
mainHandle<string>(WIN_MAIN_RENDERER_EVENT_NAME.overwrite_dislike_music_infos, async({ params: rules }) => {
await global.lx.worker.dbService.dislikeInfoOverwrite(rules)
})
// mainHandle<string[]>(WIN_MAIN_RENDERER_EVENT_NAME.remove_dislike_music_infos, async({ params: ids }) => {
// await global.lx.worker.dbService.dislikeInfoRemove(ids)
// })
@@ -13,7 +13,8 @@ import {
} from '@main/modules/sync'
import { sendEvent } from '../main'
let selectModeListenr: ((mode: LX.Sync.List.SyncMode | null) => void) | null = null
let selectModeListenr: ((mode: LX.Sync.ModeTypes[keyof LX.Sync.ModeTypes] | null) => void) | null = null
export default () => {
mainHandle<LX.Sync.SyncServiceActions, any>(WIN_MAIN_RENDERER_EVENT_NAME.sync_action, async({ params: data }) => {
@@ -29,7 +30,7 @@ export default () => {
case 'generate_code': return generateCode()
case 'select_mode':
if (selectModeListenr) {
selectModeListenr(data.data)
selectModeListenr(data.data.mode)
selectModeListenr = null
}
break
@@ -62,9 +63,9 @@ export const sendServerStatus = (status: LX.Sync.ServerStatus) => {
data: status,
})
}
export const sendSelectMode = (deviceName: string, listener: (mode: LX.Sync.List.SyncMode | null) => void) => {
selectModeListenr = listener
sendSyncAction({ action: 'select_mode', data: deviceName })
export const sendSelectMode = <T extends keyof LX.Sync.ModeTypes>(deviceName: string, type: T, listener: (mode: LX.Sync.ModeTypes[T] | null) => void) => {
selectModeListenr = listener as typeof selectModeListenr
sendSyncAction({ action: 'select_mode', data: { deviceName, type } })
}
export const removeSelectModeListener = () => {
if (selectModeListenr) selectModeListenr(null)