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

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
+2 -1
View File
@@ -6,7 +6,7 @@ import { getTheme, initHotKey, initSetting, parseEnvParams } from './utils'
import { navigationUrlWhiteList } from '@common/config'
import defaultSetting from '@common/defaultSetting'
import { closeWindow, isExistWindow as isExistMainWindow, showWindow as showMainWindow } from './modules/winMain'
import { createAppEvent, createListEvent } from '@main/event'
import { createAppEvent, createDislikeEvent, createListEvent } from '@main/event'
import { isMac, log } from '@common/utils'
import createWorkers from './worker'
import { migrateDBData } from './utils/migrate'
@@ -212,6 +212,7 @@ export const initAppSetting = async() => {
// mainWindowClosed: true,
event_app: createAppEvent(),
event_list: createListEvent(),
event_dislike: createDislikeEvent(),
appSetting: defaultSetting,
worker: createWorkers(),
hotKey: {
+56
View File
@@ -0,0 +1,56 @@
import { EventEmitter } from 'events'
export class Event extends EventEmitter {
dislike_changed() {
this.emit('dislike_changed')
}
/**
* 覆盖整个列表数据
* @param dislikeData 列表数据
* @param isRemote 是否属于远程操作
*/
async dislike_data_overwrite(dislikeData: LX.Dislike.DislikeRules, isRemote: boolean = false) {
await global.lx.worker.dbService.dislikeInfoOverwrite(dislikeData)
this.emit('dislike_data_overwrite', dislikeData, isRemote)
this.dislike_changed()
}
/**
* 批量添加歌曲到列表
* @param dislikeId 列表id
* @param musicInfos 添加的歌曲信息
* @param addMusicLocationType 添加在到列表的位置
* @param isRemote 是否属于远程操作
*/
async dislike_music_add(musicInfo: LX.Dislike.DislikeMusicInfo[], isRemote: boolean = false) {
// const changedIds =
await global.lx.worker.dbService.dislikeInfoAdd(musicInfo)
// await checkUpdateDislike(changedIds)
this.emit('dislike_music_add', musicInfo, isRemote)
this.dislike_changed()
}
/**
* 清空列表内的歌曲
* @param ids 列表Id
* @param isRemote 是否属于远程操作
*/
async dislike_music_clear(isRemote: boolean = false) {
// const changedIds =
await global.lx.worker.dbService.dislikeInfoOverwrite('')
// await checkUpdateDislike(changedIds)
this.emit('dislike_music_clear', isRemote)
this.dislike_changed()
}
}
type EventMethods = Omit<EventType, keyof EventEmitter>
declare class EventType extends Event {
on<K extends keyof EventMethods>(event: K, listener: EventMethods[K]): this
once<K extends keyof EventMethods>(event: K, listener: EventMethods[K]): this
off<K extends keyof EventMethods>(event: K, listener: EventMethods[K]): this
}
export type Type = Omit<EventType, keyof Omit<EventEmitter, 'on' | 'off' | 'once'>>
+6
View File
@@ -1,9 +1,11 @@
import { Event as App, type Type as AppType } from './AppEvent'
import { Event as List, type Type as ListType } from './ListEvent'
import { Event as Dislike, type Type as DislikeType } from './DislikeEvent'
export type {
AppType,
ListType,
DislikeType,
}
export const createAppEvent = (): AppType => {
@@ -14,3 +16,7 @@ export const createListEvent = (): ListType => {
return new List()
}
export const createDislikeEvent = (): DislikeType => {
return new Dislike()
}
@@ -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)
+2 -1
View File
@@ -1,7 +1,7 @@
/* eslint-disable no-var */
// import { Event as WinMainEvent } from '@main/modules/winMain/event'
// import { Event as WinLyricEvent } from '@main/modules/winLyric/event'
import { type AppType, type ListType } from '@main/event'
import { type DislikeType, type AppType, type ListType } from '@main/event'
import { type DBSeriveTypes } from '@main/worker/utils'
interface Lx {
@@ -25,6 +25,7 @@ interface Lx {
// mainWindowClosed: boolean
event_app: AppType
event_list: ListType
event_dislike: DislikeType
worker: {
dbService: DBSeriveTypes
}
+1
View File
@@ -13,3 +13,4 @@ import '@common/types/theme'
import '@common/types/ipc_main'
import '@common/types/sound_effect'
import '@common/types/dislike_list'
import '@common/types/dislike_list_sync'
+4
View File
@@ -15,11 +15,13 @@ declare global {
}
moduleReadys: {
list: boolean
dislike: boolean
}
onClose: (handler: (err: Error) => (void | Promise<void>)) => () => void
remote: LX.Sync.ServerSyncActions
remoteQueueList: LX.Sync.ServerSyncListActions
remoteQueueDislike: LX.Sync.ServerSyncDislikeActions
}
interface UrlInfo {
@@ -38,6 +40,7 @@ declare global {
feature: LX.Sync.EnabledFeatures
moduleReadys: {
list: boolean
dislike: boolean
}
onClose: (handler: (err: Error) => (void | Promise<void>)) => () => void
@@ -45,6 +48,7 @@ declare global {
remote: LX.Sync.ClientSyncActions
remoteQueueList: LX.Sync.ClientSyncListActions
remoteQueueDislike: LX.Sync.ClientSyncDislikeActions
}
type SocketServer = WS.Server<Socket>
}
+25
View File
@@ -1,16 +1,30 @@
type WarpSyncHandlerActions<Socket, Actions> = {
[K in keyof Actions]: (...args: [Socket, ...Parameters<Actions[K]>]) => ReturnType<Actions[K]>
}
declare namespace LX {
namespace Sync {
type ServerSyncActions = WarpPromiseRecord<{
onFeatureChanged: (feature: EnabledFeatures) => void
}>
type ServerSyncHandlerActions<Socket> = WarpSyncHandlerActions<Socket, ServerSyncActions>
type ServerSyncListActions = WarpPromiseRecord<{
onListSyncAction: (action: LX.Sync.List.ActionList) => void
}>
type ServerSyncHandlerListActions<Socket> = WarpSyncHandlerActions<Socket, ServerSyncListActions>
type ServerSyncDislikeActions = WarpPromiseRecord<{
onDislikeSyncAction: (action: LX.Sync.Dislike.ActionList) => void
}>
type ServerSyncHandlerDislikeActions<Socket> = WarpSyncHandlerActions<Socket, ServerSyncDislikeActions>
type ClientSyncActions = WarpPromiseRecord<{
getEnabledFeatures: (serverType: ServerType, supportedFeatures: SupportedFeatures) => EnabledFeatures
finished: () => void
}>
type ClientSyncHandlerActions<Socket> = WarpSyncHandlerActions<Socket, ClientSyncActions>
type ClientSyncListActions = WarpPromiseRecord<{
onListSyncAction: (action: LX.Sync.List.ActionList) => void
list_sync_get_md5: () => string
@@ -19,6 +33,17 @@ declare namespace LX {
list_sync_set_list_data: (data: LX.Sync.List.ListData) => void
list_sync_finished: () => void
}>
type ClientSyncHandlerListActions<Socket> = WarpSyncHandlerActions<Socket, ClientSyncListActions>
type ClientSyncDislikeActions = WarpPromiseRecord<{
onDislikeSyncAction: (action: LX.Sync.Dislike.ActionList) => void
dislike_sync_get_md5: () => string
dislike_sync_get_sync_mode: () => LX.Sync.Dislike.SyncMode
dislike_sync_get_list_data: () => LX.Dislike.DislikeRules
dislike_sync_set_list_data: (data: LX.Dislike.DislikeRules) => void
dislike_sync_finished: () => void
}>
type ClientSyncHandlerDislikeActions<Socket> = WarpSyncHandlerActions<Socket, ClientSyncDislikeActions>
}
}
@@ -51,7 +51,7 @@ const initDislikeList = () => {
}
}
dislikeInfo.rules = Array.from(new Set(list)).join('\n') + '\n'
dislikeInfo.rules = Array.from(new Set(list)).join('\n')
return dislikeInfo
}