Skip to content

Commit

Permalink
优化同步逻辑
Browse files Browse the repository at this point in the history
  • Loading branch information
lyswhut committed Aug 14, 2023
1 parent 4ad9c81 commit 1c159f5
Show file tree
Hide file tree
Showing 29 changed files with 567 additions and 405 deletions.
25 changes: 18 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lx-music-desktop",
"version": "2.4.0-beta.6",
"version": "2.4.0-beta.7",
"description": "一个免费的音乐查找助手",
"main": "./dist/main/main.js",
"productName": "lx-music-desktop",
Expand Down Expand Up @@ -212,7 +212,7 @@
"@tsconfig/recommended": "^1.0.2",
"@types/better-sqlite3": "^7.6.4",
"@types/needle": "^3.2.0",
"@types/node": "^20.4.9",
"@types/node": "^20.4.10",
"@types/spinnies": "^0.5.0",
"@types/tunnel": "^0.0.3",
"@types/ws": "8.5.4",
Expand Down Expand Up @@ -262,6 +262,7 @@
"image-size": "^1.0.2",
"jschardet": "^3.0.0",
"long": "^5.2.3",
"message2call": "^0.1.0",
"music-metadata": "^8.1.4",
"needle": "github:lyswhut/needle#93299ac841b7e9a9f82ca7279b88aaaeda404060",
"node-id3": "^0.2.6",
Expand Down
6 changes: 6 additions & 0 deletions publish/changeLog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
目前本项目的原始发布地址只有 **GitHub****蓝奏网盘** ,其他渠道均为第三方转载发布,可信度请自行鉴别。
本项目无微信公众号之类的官方账号,谨防被骗。

### 不兼容性变更

该版本修改了同步协议逻辑,需要PC端v2.4.0或移动端v1.7.0版本才能连接使用。

### 新增

- 新增我的列表名右键菜单-排序歌曲-随机乱序功能,使用它可以对选中列表内歌曲进行随机重排(#1440
Expand All @@ -10,6 +14,7 @@
- 优化音效设置-环境音效启用、禁用时的操作效果显示,修复禁用环境音效时仍然可以调整增益、新增预设的问题
- 过滤翻译歌词或罗马音歌词中只有“//”的行(#1499
- 点击打开歌单弹窗背景将不再自动关闭弹窗,防止选择输入框里的内容时意外关闭弹窗
- 优化数据传输逻辑,列表同步指令使用队列机制,保证列表同步操作的顺序

### 修复

Expand All @@ -21,6 +26,7 @@
- 修复某些tx源歌词因数据异常解析失败的问题
- 修复windows平台下隐藏窗口后再显示时任务栏按钮丢失的问题
- 修复首句歌词被提前播放的问题
- 修复潜在导致列表数据不同步的问题

### 其他

Expand Down
2 changes: 1 addition & 1 deletion src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const DOWNLOAD_STATUS = {
export const QUALITYS = ['flac24bit', 'flac', 'wav', 'ape', '320k', '192k', '128k'] as const

export const SYNC_CODE = {
helloMsg: 'Hello~::^-^::~v3~',
helloMsg: 'Hello~::^-^::~v4~',
idPrefix: 'OjppZDo6',
authMsg: 'lx-music auth::',
authFailed: 'Auth failed',
Expand Down
30 changes: 30 additions & 0 deletions src/common/types/sync_common.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
declare namespace LX {
namespace Sync {
type ActionList = SyncAction<'list_data_overwrite', LX.List.ListActionDataOverwrite>
| SyncAction<'list_create', LX.List.ListActionAdd>
| SyncAction<'list_remove', LX.List.ListActionRemove>
| SyncAction<'list_update', LX.List.ListActionUpdate>
| SyncAction<'list_update_position', LX.List.ListActionUpdatePosition>
| SyncAction<'list_music_add', LX.List.ListActionMusicAdd>
| SyncAction<'list_music_move', LX.List.ListActionMusicMove>
| SyncAction<'list_music_remove', LX.List.ListActionMusicRemove>
| SyncAction<'list_music_update', LX.List.ListActionMusicUpdate>
| SyncAction<'list_music_update_position', LX.List.ListActionMusicUpdatePosition>
| SyncAction<'list_music_overwrite', LX.List.ListActionMusicOverwrite>
| SyncAction<'list_music_clear', LX.List.ListActionMusicClear>

type ServerActions = WarpPromiseRecord<{
onListSyncAction: (action: LX.Sync.ActionList) => void
}>

type ClientActions = WarpPromiseRecord<{
onListSyncAction: (action: LX.Sync.ActionList) => void
list_sync_get_md5: () => string
list_sync_get_sync_mode: () => Mode
list_sync_get_list_data: () => ListData
list_sync_set_list_data: (data: ListData) => void
list_sync_finished: () => void
}>
}
}

10 changes: 10 additions & 0 deletions src/common/types/utils.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,13 @@ type Modify<T, R> = Omit<T, keyof R> & R
type Actions<T extends { action: string, data?: any }> = {
[U in T as U['action']]: 'data' extends keyof U ? U['data'] : undefined
}

type WarpPromiseValue<T> = T extends ((...args: infer P) => Promise<infer R>)
? ((...args: P) => Promise<R>)
: T extends ((...args: infer P2) => infer R2)
? ((...args: P2) => Promise<R2>)
: Promise<T>

type WarpPromiseRecord<T extends Record<string, any>> = {
[K in keyof T]: WarpPromiseValue<T[K]>
}
3 changes: 2 additions & 1 deletion src/main/modules/sync/client/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getSyncAuthKey, setSyncAuthKey } from '../data'
import { SYNC_CODE } from '@common/constants'
import log from '../log'
import { aesDecrypt, aesEncrypt, getComputerName, rsaDecrypt } from '../utils'
import { toMD5 } from '@common/utils/nodejs'


const hello = async(urlInfo: LX.Sync.Client.UrlInfo) => request(`${urlInfo.httpProtocol}//${urlInfo.hostPath}/hello`)
Expand All @@ -25,7 +26,7 @@ const getServerId = async(urlInfo: LX.Sync.Client.UrlInfo) => request(`${urlInfo
})

const codeAuth = async(urlInfo: LX.Sync.Client.UrlInfo, serverId: string, authCode: string) => {
let key = ''.padStart(16, Buffer.from(authCode).toString('hex'))
let key = toMD5(authCode).substring(0, 16)
// const iv = Buffer.from(key.split('').reverse().join('')).toString('base64')
key = Buffer.from(key).toString('base64')
let { publicKey, privateKey } = await generateRsaKey()
Expand Down
138 changes: 79 additions & 59 deletions src/main/modules/sync/client/client.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import WebSocket from 'ws'
import { encryptMsg, decryptMsg } from './utils'
import * as modules from './modules'
import { modules, callObj } from './modules'
// import { action as commonAction } from '@root/store/modules/common'
// import { getStore } from '@root/store'
import registerSyncListHandler from './syncList'
// import registerSyncListHandler from './syncList'
import log from '../log'
import { SYNC_CLOSE_CODE, SYNC_CODE } from '@common/constants'
import { dateFormat } from '@common/utils/common'
import { aesEncrypt, getAddress } from '../utils'
import { sendClientStatus } from '@main/modules/winMain'
import { createMsg2call } from 'message2call'

let status: LX.Sync.ClientStatus = {
status: false,
Expand All @@ -31,8 +32,14 @@ export const sendSyncMessage = (message: string) => {
}

const handleConnection = (socket: LX.Sync.Client.Socket) => {
for (const moduleInit of Object.values(modules)) {
moduleInit(socket)
for (const { registerEvent } of Object.values(modules)) {
registerEvent(socket)
}
}

const handleDisconnection = () => {
for (const { unregisterEvent } of Object.values(modules)) {
unregisterEvent()
}
}

Expand Down Expand Up @@ -123,101 +130,114 @@ const heartbeatTools = {
let client: LX.Sync.Client.Socket | null
// let listSyncPromise: Promise<void>
export const connect = (urlInfo: LX.Sync.Client.UrlInfo, keyInfo: LX.Sync.ClientKeyInfo) => {
client = new WebSocket(`${urlInfo.wsProtocol}//${urlInfo.hostPath}?i=${encodeURIComponent(keyInfo.clientId)}&t=${encodeURIComponent(aesEncrypt(SYNC_CODE.msgConnect, keyInfo.key))}`, {
client = new WebSocket(`${urlInfo.wsProtocol}//${urlInfo.hostPath}/socket?i=${encodeURIComponent(keyInfo.clientId)}&t=${encodeURIComponent(aesEncrypt(SYNC_CODE.msgConnect, keyInfo.key))}`, {
}) as LX.Sync.Client.Socket
client.data = {
keyInfo,
urlInfo,
}
heartbeatTools.connect(client)

// listSyncPromise = registerSyncListHandler(socket)
let events: Partial<{ [K in keyof LX.Sync.ActionSyncSendType]: Array<(data: LX.Sync.ActionSyncSendType[K]) => (void | Promise<void>)> }> = {}
let closeEvents: Array<(err: Error) => (void | Promise<void>)> = []

const message2read = createMsg2call({
funcsObj: {
...callObj,
list_sync_finished() {
log.info('sync list success')
client!.isReady = true
handleConnection(client as LX.Sync.Client.Socket)
sendSyncStatus({
status: true,
message: '',
})
heartbeatTools.failedNum = 0
},
},
timeout: 120 * 1000,
sendMessage(data) {
void encryptMsg(keyInfo, JSON.stringify(data)).then((data) => {
client?.send(data)
}).catch((err) => {
log.error('encrypt msg error: ', err)
client?.close(SYNC_CLOSE_CODE.failed)
})
},
onCallBeforeParams(rawArgs) {
return [client, ...rawArgs]
},
onError(error, path, groupName) {
const name = groupName ?? ''
log.error(`sync call ${name} ${path.join('.')} error:`, error)
if (groupName == null) return
client?.close(SYNC_CLOSE_CODE.failed)
sendSyncStatus({
status: false,
message: error.message,
})
},
})

client.remoteSyncList = message2read.createSyncRemote('list')

client.addEventListener('message', ({ data }) => {
if (data == 'ping') return
if (typeof data === 'string') {
let syncData: LX.Sync.ActionSync
try {
syncData = JSON.parse(decryptMsg(keyInfo, data))
} catch {
return
}
const handlers = events[syncData.action]
if (handlers) {
// @ts-expect-error
for (const handler of handlers) void handler(syncData.data)
}
void decryptMsg(keyInfo, data).then((data) => {
let syncData: LX.Sync.ServerActions
try {
syncData = JSON.parse(data)
} catch (err) {
log.error('parse msg error: ', err)
client?.close(SYNC_CLOSE_CODE.failed)
return
}
message2read.onMessage(syncData)
}).catch((error) => {
log.error('decrypt msg error: ', error)
client?.close(SYNC_CLOSE_CODE.failed)
})
}
})
client.onRemoteEvent = function(eventName, handler) {
let eventArr = events[eventName]
if (!eventArr) events[eventName] = eventArr = []
// let eventArr = events.get(eventName)
// if (!eventArr) events.set(eventName, eventArr = [])
eventArr.push(handler)

return () => {
eventArr!.splice(eventArr!.indexOf(handler), 1)
}
}
client.sendData = function(eventName, data, callback) {
client?.send(encryptMsg(keyInfo, JSON.stringify({ action: eventName, data })), callback)
}
client.onClose = function(handler: typeof closeEvents[number]) {
closeEvents.push(handler)
return () => {
closeEvents.splice(closeEvents.indexOf(handler), 1)
}
}

const initMessage = 'Wait syncing...'
client.addEventListener('open', () => {
log.info('connect')
// const store = getStore()
// global.lx.syncKeyInfo = keyInfo
client!.isReady = false
sendSyncStatus({
status: false,
message: 'Wait syncing...',
})
void registerSyncListHandler(client as LX.Sync.Client.Socket).then(() => {
log.info('sync list success')
handleConnection(client as LX.Sync.Client.Socket)
log.info('register list sync service success')
client!.isReady = true
heartbeatTools.failedNum = 0
sendSyncStatus({
status: true,
message: '',
})
}).catch(err => {
if (err.message == 'closed') {
sendSyncStatus({
status: false,
message: '',
})
} else {
console.log(err)
log.r_error(err.stack)
sendSyncStatus({
status: false,
message: err.message,
})
}
message: initMessage,
})
})
client.addEventListener('close', ({ code }) => {
const err = new Error('closed')
for (const handler of closeEvents) void handler(err)
handleDisconnection()
closeEvents = []
events = {}
switch (code) {
case SYNC_CLOSE_CODE.normal:
// case SYNC_CLOSE_CODE.failed:
sendSyncStatus({
status: false,
message: '',
})
break
case SYNC_CLOSE_CODE.failed:
if (!status.message || status.message == initMessage) {
sendSyncStatus({
status: false,
message: 'failed',
})
}
break
}
})
client.addEventListener('error', ({ message }) => {
Expand Down
Loading

0 comments on commit 1c159f5

Please sign in to comment.