Compare commits

..

32 Commits

Author SHA1 Message Date
lyswhut
900bdd3afe 发布v2.0.4 2023-01-15 15:15:00 +08:00
lyswhut
8dff457422 优化虚拟列表 2023-01-14 16:48:20 +08:00
lyswhut
850a429243 优化代码逻辑,修复潜在播放问题 2023-01-14 16:03:56 +08:00
lyswhut
d3c8f16aed 更新依赖 2023-01-14 00:50:43 +08:00
lyswhut
a43e127dd0 修复备份与恢复的列表导入列表信息设置逻辑问题与潜在问题 2023-01-14 00:50:29 +08:00
lyswhut
84e52f5525 修复Linux armv7l系统(如树莓派)下无法启动的问题 2023-01-14 00:06:04 +08:00
lyswhut
1dce2c1f7a 更新 2023-01-12 14:08:28 +08:00
lyswhut
1c802ad5f5 fix type 2023-01-11 16:47:57 +08:00
lyswhut
db38db9256 修复桌面歌词启用歌词缩放后的阴影显示问题;就放桌面歌词在启用卡拉OK歌词后字体边缘可能被截断的问题 2023-01-11 13:08:28 +08:00
lyswhut
cb9fa62dac 修复从搜索界面进入歌单详情后,若启用强迫症设置的清空功能会导致意外清空搜索框、搜索列表的问题 2023-01-11 10:53:17 +08:00
lyswhut
9cc36e2a6a 优化临时播放逻辑 2023-01-11 09:28:31 +08:00
lyswhut
bf310adc12 fix type 2023-01-11 08:33:13 +08:00
lyswhut
7273b6ad67 修复备份文件导入指引无法识别v2配置的问题 2023-01-11 08:32:49 +08:00
lyswhut
a02f9e1151 发布v2.0.3 2023-01-08 13:25:39 +08:00
lyswhut
6ef8015179 修复歌词阴影问题 2023-01-07 18:59:44 +08:00
lyswhut
db38b27e52 修复桌面歌词开启不允许换行后出现字体截断的问题(#1106) 2023-01-07 15:59:30 +08:00
lyswhut
cccb5b191d 修复修改播放设置-音频输出设置后,所做的更改没有被保存的问题(#1116) 2023-01-07 15:17:07 +08:00
lyswhut
3c589743b1 更新版本号 2023-01-06 17:47:21 +08:00
lyswhut
65270e02da 优化定时停止 2023-01-06 14:49:09 +08:00
lyswhut
27c1bf8eb6 eslint 2023-01-06 14:39:44 +08:00
lyswhut
1db54195f5 修复禁用切歌时歌曲播放完毕后的歌曲信息显示问题 2023-01-06 14:39:08 +08:00
lyswhut
399208429d 修复暂停按钮图标显示 2023-01-06 14:35:52 +08:00
lyswhut
b40e57f191 修复某些情况下歌词的滚动问题 2023-01-06 14:22:17 +08:00
lyswhut
be350e49f0 更新版本号 2023-01-05 19:04:50 +08:00
lyswhut
bae732016c 修复桌面歌词使用斜体出现截断的问题(#1106) 2023-01-05 18:49:50 +08:00
lyswhut
e50a67f0e4 点击打开歌单弹窗背景可以关闭弹窗(#1096) 2023-01-05 18:49:08 +08:00
lyswhut
26e8e0a1b1 修复Linux arm64系统下无法启动的问题(#1102) 2023-01-05 18:47:27 +08:00
lyswhut
c8210e0516 修复初始设置的桌面歌词窗口没有完全居右下角的问题 2023-01-05 11:57:44 +08:00
lyswhut
95590dd07f 修复列表备份文件存在异常歌曲信息时无法导入的问题 2023-01-02 18:35:42 +08:00
lyswhut
7e94e96bcc 优化迁移 2023-01-02 17:57:01 +08:00
lyswhut
5009494e89 发布v2.0.2 2023-01-02 17:52:40 +08:00
lyswhut
e58c2f86aa 修复无效的歌曲信息导致我的列表数据迁移失败的问题(#1095) 2023-01-02 17:52:19 +08:00
97 changed files with 1285 additions and 964 deletions

View File

@@ -6,6 +6,51 @@ Project versioning adheres to [Semantic Versioning](http://semver.org/).
Commit convention is based on [Conventional Commits](http://conventionalcommits.org).
Change log format is based on [Keep a Changelog](http://keepachangelog.com/).
## [2.0.4](https://github.com/lyswhut/lx-music-desktop/compare/v2.0.3...v2.0.4) - 2023-01-15
### 修复
- 修复备份文件导入指引无法识别v2配置的问题
- 修复从搜索界面进入歌单详情后,若启用强迫症设置的清空功能会导致意外清空搜索框、搜索列表的问题
- 修复桌面歌词在启用卡拉OK歌词后字体边缘可能被截断的问题特别是纵向歌词某些字的边角被截断导致后面的阴影露出来或阴影不均匀的问题
- 修复桌面歌词启用歌词缩放后的阴影显示问题
- 修复Linux armv7l系统如树莓派下无法启动的问题与修复Linux arm64的方法一样采用内置预编译模块的方式修复
- 修复备份与恢复的列表导入列表信息设置逻辑问题与潜在导入问题
## [2.0.3](https://github.com/lyswhut/lx-music-desktop/compare/v2.0.2...v2.0.3) - 2023-01-08
### 修复
- 修复初始设置的桌面歌词窗口没有完全居右下角的问题
- 修复Linux arm64系统下无法启动的问题#1102
- 修复桌面歌词使用斜体出现截断的问题(#1106
- 修复某些情况下歌词的滚动问题
- 修复禁用切歌时歌曲播放完毕后的歌曲信息显示问题
- 修复修改播放设置-音频输出设置后,所做的更改没有被保存的问题
### 优化
- 点击打开歌单弹窗背景可以关闭弹窗(#1096
## [2.0.2](https://github.com/lyswhut/lx-music-desktop/compare/v2.0.1...v2.0.2) - 2023-01-02
若你更新v2.0.0后,出现之前收藏的歌曲全部丢失或者歌曲无法添加到列表播放的问题,可以按以下方式解决:
1. 根据你的平台类型,进入软件数据目录
- Windows`%APPDATA%/lx-music-desktop`
- Linux`$XDG_CONFIG_HOME/lx-music-desktop``~/.config/lx-music-desktop`
- macOS`~/Library/Application Support/lx-music-desktop`
2. 进入`LxDatas`目录退出LX删除`lx.data.db`文件,再启动软件即可
若以上操作仍然不行可以加交流群或者在GitHub开issue反馈
### 修复
- 修复无效的歌曲信息导致我的列表数据迁移失败的问题
## [2.0.1](https://github.com/lyswhut/lx-music-desktop/compare/v2.0.0...v2.0.1) - 2023-01-02
若你更新v2.0.0后,出现之前收藏的歌曲全部丢失或者歌曲无法添加到列表播放的问题,可以按以下方式解决:

View File

@@ -0,0 +1,43 @@
const fs = require('fs')
const fsPromises = require('fs').promises
const path = require('path')
const { Arch } = require('electron-builder')
const fileNameMap = {
[Arch.arm64]: 'arm64',
[Arch.armv7l]: 'armv7l',
}
const replaceSqliteLib = async(arch) => {
// console.log(await fs.readdir(path.join(context.appOutDir, './resources/')))
// if (context.electronPlatformName != 'linux' || context.arch != Arch.arm64) return
console.log('replace sqlite lib...')
const filePath = path.join(__dirname, `./lib/better_sqlite3.linux.${fileNameMap[arch]}.node`)
const targetPath = path.join(__dirname, '../node_modules/better-sqlite3/build/Release/better_sqlite3.node')
await fsPromises.unlink(targetPath).catch(_ => _)
await fsPromises.copyFile(filePath, targetPath)
}
module.exports = async(context) => {
const { electronPlatformName, arch } = context
if (electronPlatformName !== 'linux') return
const bindingFilePath = path.join(__dirname, '../node_modules/better-sqlite3/binding.gyp')
const bindingBakFilePath = path.join(__dirname, '../node_modules/better-sqlite3/binding.gyp.bak')
switch (arch) {
case Arch.arm64:
case Arch.armv7l:
if (fs.existsSync(bindingFilePath)) {
console.log('rename binding file...')
await fsPromises.rename(bindingFilePath, bindingBakFilePath)
}
await replaceSqliteLib(arch)
break
default:
if (fs.existsSync(bindingFilePath)) return
console.log('restore binding file...')
await fsPromises.rename(bindingBakFilePath, bindingFilePath)
break
}
}

Binary file not shown.

Binary file not shown.

1021
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "lx-music-desktop",
"version": "2.0.1",
"version": "2.0.4",
"description": "一个免费的音乐查找助手",
"main": "./dist/main.js",
"productName": "lx-music-desktop",
@@ -64,7 +64,7 @@
"build:renderer-lyric": "cross-env NODE_ENV=production webpack --config build-config/renderer-lyric/webpack.config.prod.js --progress",
"build:renderer-scripts": "cross-env NODE_ENV=production webpack --config build-config/renderer-scripts/webpack.config.prod.js --progress",
"build": "npm run clean:electron && npm run build:main && npm run build:renderer && npm run build:renderer-lyric && npm run build:renderer-scripts",
"lint": "eslint --ext .js,.vue -f node_modules/eslint-formatter-friendly src",
"lint": "eslint --ext .ts,.js,.vue -f node_modules/eslint-formatter-friendly src",
"postinstall": "electron-builder install-app-deps",
"lint:fix": "eslint --ext .js,.vue -f node_modules/eslint-formatter-friendly --fix src",
"dp": "cross-env ELECTRON_GET_USE_PROXY=true GLOBAL_AGENT_HTTPS_PROXY=http://127.0.0.1:1081 npm run pack",
@@ -79,6 +79,7 @@
},
"build": {
"appId": "cn.toside.music.desktop",
"beforePack": "./build-config/build-before-pack.js",
"afterPack": "./build-config/build-after-pack.js",
"protocols": {
"name": "lx-music-protocol",
@@ -193,17 +194,14 @@
"electron-app",
"vuejs3"
],
"author": {
"name": "lyswhut",
"email": "lyswhut@qq.com"
},
"author": "lyswhut <lyswhut@qq.com>",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/lyswhut/lx-music-desktop/issues"
},
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
"devDependencies": {
"@babel/core": "^7.20.7",
"@babel/core": "^7.20.12",
"@babel/eslint-parser": "^7.19.1",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
@@ -214,10 +212,10 @@
"@types/better-sqlite3": "^7.6.3",
"@types/needle": "^3.2.0",
"@types/tunnel": "^0.0.3",
"@typescript-eslint/eslint-plugin": "^5.47.1",
"@typescript-eslint/parser": "^5.47.1",
"@volar/vue-language-plugin-pug": "^1.0.19",
"babel-loader": "^9.1.0",
"@typescript-eslint/eslint-plugin": "^5.48.1",
"@typescript-eslint/parser": "^5.48.1",
"@volar/vue-language-plugin-pug": "^1.0.24",
"babel-loader": "^9.1.2",
"browserslist": "^4.21.4",
"chalk": "^4.1.2",
"changelog-parser": "^3.0.1",
@@ -228,32 +226,32 @@
"css-minimizer-webpack-plugin": "^4.2.2",
"del": "^6.1.1",
"electron": "^19.1.9",
"electron-builder": "^24.0.0-alpha.8",
"electron-builder": "^24.0.0-alpha.10",
"electron-debug": "^3.2.0",
"electron-devtools-installer": "^3.2.0",
"electron-to-chromium": "^1.4.284",
"electron-updater": "^6.0.0-alpha.6",
"eslint": "^8.31.0",
"electron-updater": "^6.0.0-alpha.7",
"eslint": "^8.32.0",
"eslint-config-standard": "^17.0.0",
"eslint-config-standard-with-typescript": "^24.0.0",
"eslint-config-standard-with-typescript": "^27.0.1",
"eslint-formatter-friendly": "github:lyswhut/eslint-friendly-formatter#2170d1320e2fad13615a9dcf229669f0bb473a53",
"eslint-plugin-html": "^7.1.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-n": "^15.6.0",
"eslint-plugin-import": "^2.27.4",
"eslint-plugin-n": "^15.6.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.8.0",
"eslint-plugin-vue": "^9.9.0",
"eslint-webpack-plugin": "^3.2.0",
"html-webpack-plugin": "^5.5.0",
"less": "^4.1.3",
"less-loader": "^11.1.0",
"mini-css-extract-plugin": "^2.7.2",
"node-loader": "^2.0.0",
"postcss": "^8.4.20",
"postcss": "^8.4.21",
"postcss-loader": "^7.0.2",
"postcss-pxtorem": "^6.0.0",
"pug": "^3.0.2",
"pug-plain-loader": "^1.1.0",
"rimraf": "^3.0.2",
"rimraf": "^4.0.5",
"spinnies": "github:lyswhut/spinnies#233305c58694aa3b053e3ab9af9049993f918b9d",
"svg-sprite-loader": "^6.0.11",
"svg-transform-loader": "^2.0.13",
@@ -286,13 +284,13 @@
"jschardet": "^3.0.0",
"koa": "^2.14.1",
"long": "^5.2.1",
"music-metadata": "^8.1.0",
"music-metadata": "^8.1.3",
"needle": "github:lyswhut/needle#93299ac841b7e9a9f82ca7279b88aaaeda404060",
"node-id3": "^0.2.5",
"socket.io": "^4.5.4",
"sortablejs": "^1.15.0",
"tunnel": "^0.0.6",
"utf-8-validate": "^5.0.10",
"utf-8-validate": "^6.0.0",
"vue": "^3.2.45",
"vue-router": "^4.1.6"
},

View File

@@ -1,14 +1,9 @@
若你更新v2.0.0后,出现之前收藏的歌曲全部丢失或者歌曲无法添加到列表播放的问题,可以按以下方式解决:
1. 根据你的平台类型,进入软件数据目录
- Windows`%APPDATA%/lx-music-desktop`
- Linux`$XDG_CONFIG_HOME/lx-music-desktop``~/.config/lx-music-desktop`
- macOS`~/Library/Application Support/lx-music-desktop`
### 修复
2. 进入`LxDatas`目录退出LX删除`lx.data.db`文件,再启动软件即可
若以上操作仍然不行可以加交流群或者在GitHub开issue反馈
### 优化
- 单次执行所有sql语句尝试解决某些情况下某些表没有成功创建的问题
- 修复备份文件导入指引无法识别v2配置的问题
- 修复从搜索界面进入歌单详情后,若启用强迫症设置的清空功能会导致意外清空搜索框、搜索列表的问题
- 修复桌面歌词在启用卡拉OK歌词后字体边缘可能被截断的问题特别是纵向歌词某些字的边角被截断导致后面的阴影露出来或阴影不均匀的问题
- 修复桌面歌词启用歌词缩放后的阴影显示问题
- 修复Linux armv7l系统如树莓派下无法启动的问题与修复Linux arm64的方法一样采用内置预编译模块的方式修复
- 修复备份与恢复的列表导入列表信息设置逻辑问题与潜在导入问题

File diff suppressed because one or more lines are too long

View File

@@ -23,6 +23,7 @@ export const LIST_IDS = {
LOVE: 'love',
TEMP: 'temp',
DOWNLOAD: 'download',
PLAY_LATER: null,
} as const
export const DATA_KEYS = {

View File

@@ -58,7 +58,7 @@ const defaultSetting: LX.AppSetting = {
'desktopLyric.style.align': 'center',
'desktopLyric.style.font': '',
'desktopLyric.style.fontSize': 20,
'desktopLyric.style.lineGap': 15,
'desktopLyric.style.lineGap': 14,
'desktopLyric.style.lyricUnplayColor': 'rgba(255, 255, 255, 1)',
'desktopLyric.style.lyricPlayedColor': 'rgba(7, 197, 86, 1)',
'desktopLyric.style.lyricShadowColor': 'rgba(0, 0, 0, 0.15)',

View File

@@ -36,7 +36,7 @@ exports.createThemeColors = (rgbaColor, fontRgbaColor, isDark) => {
const createFontColors = (rgbaColor, isDark) => {
// rgb(238, 238, 238)
// let prec = 'rgb(255, 255, 255)'
if (rgbaColor == null) rgbaColor = isDark ? 'rgb(229, 229, 229)' : 'rgb(33, 33, 33)'
rgbaColor ??= isDark ? 'rgb(229, 229, 229)' : 'rgb(33, 33, 33)'
if (isDark) return createFontDarkColors(rgbaColor)
let colors = {

View File

@@ -65,9 +65,7 @@ declare namespace LX {
interface HotKeyConfig {
enable: boolean
keys: {
[key: string]: HotKey
}
keys: Record<string, HotKey>
}
interface HotKeyConfigAll {
local: HotKeyConfig

View File

@@ -4,7 +4,7 @@ declare namespace LX {
id: string
name: string
// list: LX.Music.MusicInfo[]
source?: LX.Source
source?: LX.OnlineSource
sourceListId?: string
// position?: number
locationUpdateTime: number | null

View File

@@ -176,7 +176,7 @@ export const similar = (a: string, b: string) => {
* @param arr
* @param data
*/
export const sortInsert = (arr: Array<{ num: number, data: any }>, data: { num: number, data: any }) => {
export const sortInsert = <T>(arr: Array<{ num: number, data: T }>, data: { num: number, data: T }) => {
let key = data.num
let left = 0
let right = arr.length - 1

View File

@@ -135,12 +135,13 @@ class Task extends EventEmitter {
try {
this.__initDownload(response)
} catch (error: any) {
return this.__handleError(error)
this.__handleError(error)
return
}
this.status = STATUS.running
response
.on('data', this.__handleWriteData.bind(this))
.on('error', err => this.__handleError(err))
.on('error', err => { this.__handleError(err) })
.on('end', () => {
if (response.complete) {
this.__handleComplete()
@@ -150,7 +151,7 @@ class Task extends EventEmitter {
}
})
})
.on('error', err => this.__handleError(err))
.on('error', err => { this.__handleError(err) })
.on('close', () => {
void this.__closeWriteStream()
})
@@ -159,7 +160,10 @@ class Task extends EventEmitter {
__initDownload(response: http.IncomingMessage) {
this.progress.total = response.headers['content-length'] ? parseInt(response.headers['content-length']) : 0
if (!this.progress.total) return this.__handleError(new Error('Content length is 0'))
if (!this.progress.total) {
this.__handleError(new Error('Content length is 0'))
return
}
let options: any = {}
let isResumable = this.options.forceResume ||
response.headers['accept-ranges'] !== 'none' ||
@@ -170,11 +174,17 @@ class Task extends EventEmitter {
options.flags = 'a'
if (this.progress.downloaded) this.progress.total -= 10
} else {
if (this.chunkInfo.startByte != '0') return this.__handleError(new Error('The resource cannot be resumed download.'))
if (this.chunkInfo.startByte != '0') {
this.__handleError(new Error('The resource cannot be resumed download.'))
return
}
}
this.progress.total += this.progress.downloaded
this.statsEstimate.prevBytes = this.progress.downloaded
if (!this.chunkInfo.path) return this.__handleError(new Error('Chunk save Path is not set.'))
if (!this.chunkInfo.path) {
this.__handleError(new Error('Chunk save Path is not set.'))
return
}
this.ws = fs.createWriteStream(this.chunkInfo.path, options)
this.ws.on('finish', () => {
@@ -216,7 +226,10 @@ class Task extends EventEmitter {
async __closeWriteStream() {
return new Promise<void>((resolve, reject) => {
if (!this.ws) return resolve()
if (!this.ws) {
resolve()
return
}
// console.log('close write stream')
this.ws.close(err => {
if (err) {
@@ -251,7 +264,10 @@ class Task extends EventEmitter {
// this.__handleError(err)
this.chunkInfo.startByte = '0'
this.resumeLastChunk = null
if (unlinkErr && unlinkErr.code !== 'ENOENT') return this.__handleError(unlinkErr)
if (unlinkErr && unlinkErr.code !== 'ENOENT') {
this.__handleError(unlinkErr)
return
}
void this.start()
})
})
@@ -259,7 +275,10 @@ class Task extends EventEmitter {
}
}
// console.log('data', chunk)
if (this.status == STATUS.stopped || this.ws == null) return console.log('cancel write')
if (this.status == STATUS.stopped || this.ws == null) {
console.log('cancel write')
return
}
this.__calculateProgress(chunk.length)
this.ws.write(chunk, err => {
if (!err) return

View File

@@ -141,7 +141,7 @@ module.exports = class FontPlayer {
// lineText += text
if (this.shadowContent) {
if (!lrcShadowContent) lrcShadowContent = document.createElement('div')
lrcShadowContent ??= document.createElement('div')
const shadowDom = document.createElement('span')
shadowDom.textContent = text
lrcShadowContent.appendChild(shadowDom)
@@ -159,7 +159,7 @@ module.exports = class FontPlayer {
}
if (this.shadowContent && lrcShadowContent) {
lrcShadowContent.style = 'position:absolute;top:0;left:0;width:100%;z-index:-1;'
lrcShadowContent.style = 'position:absolute;top:0;left:0;right:0;z-index:-1;'
lrcShadowContent.className = this.shadowClassName
this.line.appendChild(lrcShadowContent)
}

View File

@@ -25,7 +25,7 @@ export default (setting: any): Partial<LX.AppSetting> => {
// 迁移列表滚动位置设置 ~0.18.3
if (setting.list?.scroll) {
let scroll = setting.list.scroll
if (setting.list.isSaveScrollLocation) setting.list.isSaveScrollLocation = scroll.enable
setting.list.isSaveScrollLocation &&= scroll.enable
delete setting.list.scroll
}

View File

@@ -16,9 +16,15 @@ export const dirname = (p: string): string => path.dirname(p)
*/
export const checkPath = async(path: string): Promise<boolean> => {
return await new Promise(resolve => {
if (!path) return resolve(false)
if (!path) {
resolve(false)
return
}
fs.access(path, fs.constants.F_OK, err => {
if (err) return resolve(false)
if (err) {
resolve(false)
return
}
resolve(true)
})
})
@@ -26,9 +32,15 @@ export const checkPath = async(path: string): Promise<boolean> => {
export const getFileStats = async(path: string): Promise<fs.Stats | null> => {
return await new Promise(resolve => {
if (!path) return resolve(null)
if (!path) {
resolve(null)
return
}
fs.stat(path, (err, stats) => {
if (err) return resolve(null)
if (err) {
resolve(null)
return
}
resolve(stats)
})
})
@@ -39,29 +51,37 @@ export const getFileStats = async(path: string): Promise<fs.Stats | null> => {
* @param path
* @returns
*/
export const createDir = async(path: string): Promise<void> => {
return await new Promise((resolve, reject) => {
fs.access(path, fs.constants.F_OK | fs.constants.W_OK, err => {
if (err) {
if (err.code === 'ENOENT') {
fs.mkdir(path, { recursive: true }, err => {
if (err) return reject(err)
resolve()
})
return
}
return reject(err)
export const createDir = async(path: string) => new Promise<void>((resolve, reject) => {
fs.access(path, fs.constants.F_OK | fs.constants.W_OK, err => {
if (err) {
if (err.code === 'ENOENT') {
fs.mkdir(path, { recursive: true }, err => {
if (err) {
reject(err)
return
}
resolve()
})
return
}
resolve()
})
reject(err)
return
}
resolve()
})
}
})
export const removeFile = async(path: string) => new Promise<void>((resolve, reject) => {
fs.access(path, fs.constants.F_OK, err => {
if (err) return err.code == 'ENOENT' ? resolve() : reject(err)
if (err) {
err.code == 'ENOENT' ? resolve() : reject(err)
return
}
fs.unlink(path, err => {
if (err) return reject(err)
if (err) {
reject(err)
return
}
resolve()
})
})
@@ -79,7 +99,10 @@ export const toMD5 = (str: string) => crypto.createHash('md5').update(str).diges
export const gzipData = async(str: string): Promise<Buffer> => {
return await new Promise((resolve, reject) => {
gzip(str, (err, result) => {
if (err) return reject(err)
if (err) {
reject(err)
return
}
resolve(result)
})
})
@@ -88,7 +111,10 @@ export const gzipData = async(str: string): Promise<Buffer> => {
export const gunzipData = async(buf: Buffer): Promise<string> => {
return await new Promise((resolve, reject) => {
gunzip(buf, (err, result) => {
if (err) return reject(err)
if (err) {
reject(err)
return
}
resolve(result.toString())
})
})

View File

@@ -45,7 +45,10 @@ const handleScrollY = (element: HTMLElement, to: number, duration = 300, fn = no
element.scrollTop = val
}
if (currentTime < duration) {
if (cancel) return fn()
if (cancel) {
fn()
return
}
window.setTimeout(animateScroll, increment)
} else {
fn()
@@ -117,7 +120,10 @@ const handleScrollX = (element: HTMLElement, to: number, duration = 300, fn = ()
element.scrollLeft = val
}
if (currentTime < duration) {
if (cancel) return fn()
if (cancel) {
fn()
return
}
window.setTimeout(animateScroll, increment)
} else {
fn()
@@ -192,7 +198,10 @@ const handleScrollXR = (element: HTMLElement, to: number, duration = 300, fn = (
element.scrollLeft = val
}
if (currentTime < duration) {
if (cancel) return fn()
if (cancel) {
fn()
return
}
window.setTimeout(animateScroll, increment)
} else {
fn()
@@ -235,7 +244,7 @@ export const scrollXRTo = (element: HTMLElement, to: number, duration = 300, fn
*/
let dom_title = document.getElementsByTagName('title')[0]
export const setTitle = (title: string | null) => {
if (!title) title = '洛雪音乐助手'
title ||= '洛雪音乐助手'
dom_title.innerText = title
}

View File

@@ -118,3 +118,13 @@ export const fixNewMusicInfoQuality = (musicInfo: LX.Music.MusicInfo) => {
return musicInfo
}
export const filterMusicList = <T extends LX.Music.MusicInfo>(list: T[]): T[] => {
const ids: Set<string> = new Set()
return list.filter(s => {
if (!s.id || ids.has(s.id) || !s.name) return false
if (s.singer == null) s.singer = ''
ids.add(s.id)
return true
})
}

View File

@@ -116,8 +116,14 @@ export const registerDeeplink = (startApp: () => void) => {
export const listenerAppEvent = (startApp: () => void) => {
app.on('web-contents-created', (event, contents) => {
contents.on('will-navigate', (event, navigationUrl) => {
if (global.isDev) return console.log('navigation to url:', navigationUrl)
if (!navigationUrlWhiteList.some(url => url.test(navigationUrl))) return event.preventDefault()
if (global.isDev) {
console.log('navigation to url:', navigationUrl)
return
}
if (!navigationUrlWhiteList.some(url => url.test(navigationUrl))) {
event.preventDefault()
return
}
console.log('navigation to url:', navigationUrl)
})
contents.setWindowOpenHandler(({ url }) => {
@@ -233,7 +239,7 @@ export const initAppSetting = async() => {
if (!isInitialized) {
const dbFileExists = await global.lx.worker.dbService.init(global.lxDataPath)
global.lx.appSetting = (await initSetting()).setting
if (!dbFileExists) await migrateDBData().catch(err => log.error(err))
if (!dbFileExists) await migrateDBData().catch(err => { log.error(err) })
initTheme()
}
// global.lx.theme = getTheme()

View File

@@ -17,8 +17,12 @@ electronDebug({
// Install `vue-devtools`
app.on('ready', () => {
installExtension(VUEJS_DEVTOOLS)
.then((name: string) => console.log(`Added Extension: ${name}`))
.catch((err: Error) => console.log('An error occurred: ', err))
.then((name: string) => {
console.log(`Added Extension: ${name}`)
})
.catch((err: Error) => {
console.log('An error occurred: ', err)
})
})
// Require `main` process to boot app

View File

@@ -30,4 +30,6 @@ listenerAppEvent(init)
// https://github.com/electron/electron/issues/16809
void app.whenReady().then(() => isLinux ? setTimeout(init, 300) : init())
void app.whenReady().then(() => {
isLinux ? setTimeout(init, 300) : init()
})

View File

@@ -146,7 +146,7 @@ const broadcast = async(action: listAction, data: any, excludeIds: string[] = []
export const sendListAction = async(action: LX.Sync.ActionList) => {
console.log('sendListAction', action.action)
// io.sockets
return await broadcast('list:action', JSON.stringify(action))
await broadcast('list:action', JSON.stringify(action))
}
export const registerListHandler = (_io: Server, socket: LX.Sync.Socket) => {

View File

@@ -18,49 +18,54 @@ export const authCode = async(req: http.IncomingMessage, res: http.ServerRespons
let ip = req.socket.remoteAddress
// console.log(req.headers)
if (typeof req.headers.m == 'string' && ip && (requestIps.get(ip) ?? 0) < 10) {
if (req.headers.m) {
label:
if (req.headers.i) { // key验证
if (typeof req.headers.i != 'string') break label
const keyInfo = getClientKeyInfo(req.headers.i)
if (!keyInfo) break label
let text
try {
text = aesDecrypt(req.headers.m, keyInfo.key)
} catch (err) {
break label
}
// console.log(text)
if (text.startsWith(SYNC_CODE.authMsg)) {
code = 200
const deviceName = text.replace(SYNC_CODE.authMsg, '') || 'Unknown'
if (deviceName != keyInfo.deviceName) {
keyInfo.deviceName = deviceName
setClientKeyInfo(keyInfo)
if (typeof req.headers.m == 'string') {
if (ip && (requestIps.get(ip) ?? 0) < 10) {
if (req.headers.m) {
label:
if (req.headers.i) { // key验证
if (typeof req.headers.i != 'string') break label
const keyInfo = getClientKeyInfo(req.headers.i)
if (!keyInfo) break label
let text
try {
text = aesDecrypt(req.headers.m, keyInfo.key)
} catch (err) {
break label
}
// console.log(text)
if (text.startsWith(SYNC_CODE.authMsg)) {
code = 200
const deviceName = text.replace(SYNC_CODE.authMsg, '') || 'Unknown'
if (deviceName != keyInfo.deviceName) {
keyInfo.deviceName = deviceName
setClientKeyInfo(keyInfo)
}
msg = aesEncrypt(SYNC_CODE.helloMsg, keyInfo.key)
}
} else { // 连接码验证
let key = ''.padStart(16, Buffer.from(authCode).toString('hex'))
// const iv = Buffer.from(key.split('').reverse().join('')).toString('base64')
key = Buffer.from(key).toString('base64')
// console.log(req.headers.m, authCode, key)
let text
try {
text = aesDecrypt(req.headers.m, key)
} catch (err) {
break label
}
// console.log(text)
if (text.startsWith(SYNC_CODE.authMsg)) {
code = 200
const data = text.split('\n')
const publicKey = `-----BEGIN PUBLIC KEY-----\n${data[1]}\n-----END PUBLIC KEY-----`
const deviceName = data[2] || 'Unknown'
msg = rsaEncrypt(Buffer.from(JSON.stringify(createClientKeyInfo(deviceName))), publicKey)
}
msg = aesEncrypt(SYNC_CODE.helloMsg, keyInfo.key)
}
} else { // 连接码验证
let key = ''.padStart(16, Buffer.from(authCode).toString('hex'))
// const iv = Buffer.from(key.split('').reverse().join('')).toString('base64')
key = Buffer.from(key).toString('base64')
// console.log(req.headers.m, authCode, key)
let text
try {
text = aesDecrypt(req.headers.m, key)
} catch (err) {
break label
}
// console.log(text)
if (text.startsWith(SYNC_CODE.authMsg)) {
code = 200
const data = text.split('\n')
const publicKey = `-----BEGIN PUBLIC KEY-----\n${data[1]}\n-----END PUBLIC KEY-----`
const deviceName = data[2] || 'Unknown'
msg = rsaEncrypt(Buffer.from(JSON.stringify(createClientKeyInfo(deviceName))), publicKey)
}
}
} else {
code = 403
msg = SYNC_CODE.msgBlockedIp
}
}
res.writeHead(code)

View File

@@ -3,5 +3,6 @@ export const SYNC_CODE = {
idPrefix: 'OjppZDo6',
authMsg: 'lx-music auth::',
msgAuthFailed: 'Auth failed',
msgBlockedIp: 'Blocked IP',
msgConnect: 'lx-music connect',
} as const

View File

@@ -142,7 +142,10 @@ const handleStartServer = async(port = 9527) => await new Promise((resolve, reje
httpServer.on('listening', () => {
const addr = httpServer.address()
if (!addr) return reject(new Error('address is null'))
if (!addr) {
reject(new Error('address is null'))
return
}
const bind = typeof addr == 'string' ? `pipe ${addr}` : `port ${addr.port}`
console.info(`Listening on ${bind}`)
resolve(null)
@@ -171,7 +174,7 @@ export const stopServer = async() => {
return
}
console.log('stoping sync server...')
return await handleStopServer().then(() => {
await handleStopServer().then(() => {
console.log('sync server stoped')
status.status = false
status.message = ''
@@ -189,7 +192,7 @@ export const startServer = async(port: number) => {
if (status.status) await handleStopServer()
console.log('starting sync server...')
return await handleStartServer(port).then(() => {
await handleStartServer(port).then(() => {
console.log('sync server started')
status.status = true
status.message = ''

View File

@@ -31,7 +31,10 @@ const getRemoteListData = async(socket: LX.Sync.Socket): Promise<LX.Sync.ListDat
socket.removeListener('list:sync', handleSuccess)
console.log('getRemoteListData', 'handleSuccess')
const data: LX.Sync.Data | null = JSON.parse(decryptMsg(socket.data.keyInfo, enData))
if (!data) return reject(new Error('Get remote list data failed'))
if (!data) {
reject(new Error('Get remote list data failed'))
return
}
if (data.action != 'getData') return
resolve(patchListData(data.data))
}
@@ -101,10 +104,10 @@ const updateSnapshot = async(path: string, data: string) => {
let writeFilePromise = writeFilePromises.get(path) ?? Promise.resolve()
writeFilePromise = writeFilePromise.then(async() => {
if (writeFilePromise !== writeFilePromises.get(path)) return
return await fsPromises.writeFile(path, data)
await fsPromises.writeFile(path, data)
})
writeFilePromises.set(path, writeFilePromise)
return await writeFilePromise.finally(() => {
await writeFilePromise.finally(() => {
if (writeFilePromise !== writeFilePromises.get(path)) return
writeFilePromises.delete(path)
})
@@ -453,7 +456,7 @@ const checkSyncQueue = async(): Promise<void> => {
if (!syncingId) return
console.log('sync queue...')
await wait()
return await checkSyncQueue()
await checkSyncQueue()
}
// export {
@@ -484,7 +487,7 @@ const _syncList = async(_io: Server, socket: LX.Sync.Socket) => {
const removeSnapshot = async(keyInfo: LX.Sync.KeyInfo) => {
const filePath = getSnapshotFilePath(keyInfo)
return await fsPromises.unlink(filePath)
await fsPromises.unlink(filePath)
}
export {

View File

@@ -44,7 +44,7 @@ export const createClientKeyInfo = (deviceName: string): LX.Sync.KeyInfo => {
deviceName,
}
const store = getStore(STORE_NAME)
if (!keyInfos) keyInfos = store.get('keys') as KeyInfos || {}
keyInfos ??= store.get('keys') as KeyInfos || {}
if (Object.keys(keyInfos).length > 101) throw new Error('max keys')
keyInfos[keyInfo.clientId] = keyInfo

View File

@@ -27,7 +27,7 @@ const winEvent = () => {
export const createWindow = async(userApi: LX.UserApi.UserApiInfo) => {
await closeWindow()
if (!dir) dir = global.isDev ? webpackUserApiPath : join(encodePath(__dirname), 'userApi')
dir ??= global.isDev ? webpackUserApiPath : join(encodePath(__dirname), 'userApi')
if (!html) {
html = await fs.promises.readFile(join(dir, 'renderer/user-api.html'), 'utf8')
@@ -72,7 +72,10 @@ export const createWindow = async(userApi: LX.UserApi.UserApiInfo) => {
})
}
browserWindow.webContents.session.setPermissionRequestHandler((webContents, permission, resolve) => {
if (webContents === browserWindow?.webContents) return resolve(false)
if (webContents === browserWindow?.webContents) {
resolve(false)
return
}
resolve(true)
})
browserWindow.webContents.setWindowOpenHandler(() => {

View File

@@ -118,7 +118,9 @@ export const cancelRequest = (requestKey: string) => {
}
export const request = async({ requestKey, data }: LX.UserApi.UserApiRequestParams): Promise<any> => await new Promise((resolve, reject) => {
if (!userApi) return reject(new Error('user api is not load'))
if (!userApi) {
reject(new Error('user api is not load'))
}
// const requestKey = `request__${Math.random().toString().substring(2)}`
const timeout = timeouts.get(requestKey)

View File

@@ -33,7 +33,7 @@ export const importApi = (script: string): LX.UserApi.UserApiInfo => {
script,
allowShowUpdateAlert: true,
}
if (!userApis) userApis = []
userApis ??= []
userApis.push(apiInfo)
getStore(STORE_NAMES.USER_API).set('userApis', userApis)
return apiInfo

View File

@@ -1,7 +1,7 @@
import { join } from 'path'
import { BrowserWindow } from 'electron'
import { debounce, isLinux } from '@common/utils'
import { getLyricWindowBounds } from './utils'
import { getLyricWindowBounds, offset } from './utils'
import { mainSend } from '@common/mainIpc'
import { encodePath } from '@common/utils/electron'
@@ -85,8 +85,8 @@ export const createWindow = () => {
let isShowTaskbar = global.lx.appSetting['desktopLyric.isShowTaskbar']
let { width: screenWidth, height: screenHeight } = global.envParams.workAreaSize
if (x == null || y == null) {
x = screenWidth - width
y = screenHeight - height
x = screenWidth - width + offset
y = screenHeight - height + offset
}
if (isLockScreen) {
let bounds = getLyricWindowBounds({ x, y, width, height }, { x: null, y: 0, w: width, h: height })
@@ -202,7 +202,10 @@ export const alwaysOnTopTools: AlwaysOnTopTools = {
startLoop() {
this.clearLoop()
this.timeout = setInterval(() => {
if (!isExistWindow()) return this.clearLoop()
if (!isExistWindow()) {
this.clearLoop()
return
}
setAlwaysOnTop(true, 'screen-saver')
}, 1000)
},

View File

@@ -3,7 +3,7 @@ let winX
let winY
let wasW
let wasH
let offset = 8
export const offset = 8
let minWidth = 80
let minHeight = 50

View File

@@ -20,7 +20,7 @@ const winEvent = () => {
return
}
if (global.lx.isTrafficLightClose) global.lx.isTrafficLightClose = false
global.lx.isTrafficLightClose &&= false
event.preventDefault()
browserWindow!.hide()
})
@@ -242,13 +242,13 @@ export const setThumbarButtons = ({ empty, collect, play, next, prev }: LX.TaskB
export const setThumbnailClip = (region: Electron.Rectangle) => {
if (!browserWindow) return
return browserWindow.setThumbnailClip(region)
browserWindow.setThumbnailClip(region)
}
export const clearCache = async() => {
if (!browserWindow) throw new Error('main window is undefined')
return await browserWindow.webContents.session.clearCache()
await browserWindow.webContents.session.clearCache()
}
export const getCacheSize = async() => {

View File

@@ -55,7 +55,10 @@ export default () => {
showWindow()
})
mainOn<boolean>(WIN_MAIN_RENDERER_EVENT_NAME.close, ({ params: isForce }) => {
if (isForce) return app.exit(0)
if (isForce) {
app.exit(0)
return
}
global.lx.isTrafficLightClose = true
closeWindow()
})
@@ -79,7 +82,7 @@ export default () => {
mainHandle(WIN_MAIN_RENDERER_EVENT_NAME.clear_cache, async() => {
return await clearCache()
await clearCache()
})
mainHandle<number>(WIN_MAIN_RENDERER_EVENT_NAME.get_cache_size, async() => {

View File

@@ -13,7 +13,7 @@ export default () => {
await global.lx.worker.dbService.downloadInfoUpdate(list)
})
mainHandle<string[]>(WIN_MAIN_RENDERER_EVENT_NAME.download_list_remove, async({ params: ids }) => {
return await global.lx.worker.dbService.downloadInfoRemove(ids)
await global.lx.worker.dbService.downloadInfoRemove(ids)
})
mainHandle(WIN_MAIN_RENDERER_EVENT_NAME.download_list_clear, async() => {
await global.lx.worker.dbService.downloadInfoClear()

View File

@@ -6,7 +6,10 @@ import { WIN_MAIN_RENDERER_EVENT_NAME } from '@common/ipcNames'
const handleInflate = async(data: Buffer) => {
return await new Promise((resolve: (result: Buffer) => void, reject) => {
inflate(data, (err, result) => {
if (err) return reject(err)
if (err) {
reject(err)
return
}
resolve(result)
})
})

View File

@@ -9,7 +9,8 @@ export default () => {
mainHandle<LX.Sync.SyncServiceActions, any>(WIN_MAIN_RENDERER_EVENT_NAME.sync_action, async({ params: data }) => {
switch (data.action) {
case 'enable':
return data.data.enable ? await startServer(parseInt(data.data.port)) : await stopServer()
data.data.enable ? await startServer(parseInt(data.data.port)) : await stopServer()
return
case 'get_status':
return getStatus()
case 'generate_code':

View File

@@ -22,7 +22,7 @@ export default () => {
})
mainHandle<LX.UserApi.UserApiSetApiParams>(WIN_MAIN_RENDERER_EVENT_NAME.set_user_api, async({ params: apiId }) => {
return await setApi(apiId)
await setApi(apiId)
})
mainHandle<LX.UserApi.UserApiInfo[]>(WIN_MAIN_RENDERER_EVENT_NAME.get_user_api_list, async() => {
@@ -34,14 +34,14 @@ export default () => {
})
mainHandle<LX.UserApi.UserApiSetAllowUpdateAlertParams>(WIN_MAIN_RENDERER_EVENT_NAME.user_api_set_allow_update_alert, async({ params: { id, enable } }) => {
return setAllowShowUpdateAlert(id, enable)
setAllowShowUpdateAlert(id, enable)
})
mainHandle<LX.UserApi.UserApiRequestParams>(WIN_MAIN_RENDERER_EVENT_NAME.request_user_api, async({ params }) => {
return await request(params)
})
mainHandle<LX.UserApi.UserApiRequestCancelParams>(WIN_MAIN_RENDERER_EVENT_NAME.request_user_api_cancel, async({ params: requestKey }) => {
return cancelRequest(requestKey)
cancelRequest(requestKey)
})
}

View File

@@ -35,7 +35,7 @@ declare namespace LX {
interface UserListInfo {
id: string
name: string
source?: LX.Source
source?: LX.OnlineSource
sourceListId?: string
position: number
locationUpdateTime: number | null

View File

@@ -113,7 +113,7 @@ export const updateSetting = (setting?: Partial<LX.AppSetting>, isInit: boolean
let originSetting: LX.AppSetting
if (isInit) {
if (setting) setting = migrateSetting(setting)
setting &&= migrateSetting(setting)
originSetting = { ...defaultSetting }
} else originSetting = global.lx.appSetting
@@ -196,7 +196,7 @@ export const openDevTools = (webContents: Electron.WebContents) => {
let userThemes: LX.Theme[]
export const getAllThemes = () => {
if (!userThemes) userThemes = getStore(STORE_NAMES.THEME).get('themes') as LX.Theme[] | null ?? []
userThemes ??= getStore(STORE_NAMES.THEME).get('themes') as (LX.Theme[] | null) ?? []
return {
themes,
userThemes,

View File

@@ -1,7 +1,7 @@
import fs from 'node:fs'
import { checkPath, joinPath } from '@common/utils/nodejs'
import { log } from '@common/utils'
import { toNewMusicInfo } from '@common/utils/tools'
import { filterMusicList, toNewMusicInfo } from '@common/utils/tools'
import { APP_EVENT_NAMES, STORE_NAMES } from '@common/constants'
/**
@@ -28,14 +28,7 @@ interface OldUserListInfo {
locationUpdateTime?: number
list: any[]
}
const filterMusicList = <T extends LX.Music.MusicInfo>(list: T[]): T[] => {
const ids: Set<string> = new Set()
return list.filter(s => {
if (!s.id || ids.has(s.id)) return false
ids.add(s.id)
return true
})
}
/**
* 迁移 v2.0.0 之前的 list data
* @returns
@@ -89,7 +82,9 @@ const migrateFile = async(name: string, targetName: string) => {
if (!await checkPath(path) && await checkPath(oldPath)) {
await fs.promises.copyFile(oldPath, path).catch(err => {
log.error(err)
}).catch(err => log.error(err))
}).catch(err => {
log.error(err)
})
}
}
@@ -115,7 +110,9 @@ export const migrateDataJson = async() => {
if (oldDataFile.listPosition) newData.listScrollPosition = oldDataFile.listPosition
if (oldDataFile.listUpdateInfo) newData.listUpdateInfo = oldDataFile.listUpdateInfo
await fs.promises.writeFile(path, JSON.stringify(newData)).catch(err => log.error(err))
await fs.promises.writeFile(path, JSON.stringify(newData)).catch(err => {
log.error(err)
})
}

View File

@@ -4,9 +4,7 @@ import { join } from 'path'
import fs from 'fs'
import log from 'electron-log'
interface Stores {
[key: string]: Store
}
type Stores = Record<string, Store>
const stores: Stores = {}

View File

@@ -91,7 +91,7 @@ export const insertMusicInfoList = (list: LX.DBService.MusicInfo[]) => {
const musicInfoInsertStatement = createMusicInfoInsertStatement()
const musicInfoOrderInsertStatement = createMusicInfoOrderInsertStatement()
const db = getDB()
return db.transaction((musics: LX.DBService.MusicInfo[]) => {
db.transaction((musics: LX.DBService.MusicInfo[]) => {
for (const music of musics) {
musicInfoInsertStatement.run(music)
musicInfoOrderInsertStatement.run({
@@ -238,7 +238,7 @@ export const removeMusicInfos = (listId: string, ids: string[]) => {
const musicInfoDeleteStatement = createMusicInfoDeleteStatement()
const musicInfoOrderDeleteStatement = createMusicInfoOrderDeleteStatement()
const db = getDB()
return db.transaction((listId: string, ids: string[]) => {
db.transaction((listId: string, ids: string[]) => {
for (const id of ids) {
musicInfoDeleteStatement.run({ listId, id })
musicInfoOrderDeleteStatement.run({ listId, id })

View File

@@ -39,7 +39,7 @@ const toDBMusicInfo = (musicInfos: LX.Music.MusicInfo[], listId: string, offset:
* @returns
*/
export const getAllUserList = (): LX.List.UserListInfo[] => {
if (userLists == null) userLists = queryAllUserList()
userLists ??= queryAllUserList()
return userLists.map(list => {
const { position, ...newList } = list
@@ -53,7 +53,7 @@ export const getAllUserList = (): LX.List.UserListInfo[] => {
* @param lists 列表信息
*/
export const createUserLists = (position: number, lists: LX.List.UserListInfo[]) => {
if (userLists == null) userLists = queryAllUserList()
userLists ??= queryAllUserList()
if (position < 0 || position >= userLists.length) {
const newLists: LX.DBService.UserListInfo[] = lists.map((list, index) => {
return {
@@ -96,7 +96,7 @@ export const setUserLists = (lists: LX.List.UserListInfo[]) => {
*/
export const removeUserLists = (ids: string[]) => {
deleteUserLists(ids)
if (userLists) userLists = queryAllUserList()
userLists &&= queryAllUserList()
}
/**
@@ -117,7 +117,7 @@ export const updateUserLists = (lists: LX.List.UserListInfo[]) => {
}
}).filter(Boolean) as LX.DBService.UserListInfo[]
updateUserListsFromDB(dbList)
if (userLists) userLists = queryAllUserList()
userLists &&= queryAllUserList()
}
/**
@@ -126,7 +126,7 @@ export const updateUserLists = (lists: LX.List.UserListInfo[]) => {
* @param ids 列表ids
*/
export const updateUserListsPosition = (position: number, ids: string[]) => {
if (userLists == null) userLists = queryAllUserList()
userLists ??= queryAllUserList()
const newUserLists = [...userLists]

View File

@@ -5,7 +5,7 @@
:style="lrcStyles" @wheel="handleWheel" @mousedown="handleLyricMouseDown" @touchstart="handleLyricTouchStart"
>
<div :class="$style.lyricSpace" />
<div ref="dom_lyric_text" :class="[$style.lyricText]" />
<div ref="dom_lyric_text" />
<div :class="$style.lyricSpace" />
</div>
</template>
@@ -68,11 +68,15 @@ export default {
// font-weight: bold;
:global {
.font-lrc, .shadow {
padding: 0.08em 0.14em;
}
.font-lrc {
color: var(--color-lyric-unplay);
}
.shadow {
color: transparent;
// margin-left: -0.14em;
}
.line-content {
line-height: 1.2;
@@ -115,8 +119,16 @@ export default {
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
background-size: 0 100%;
padding-left: 1px;
padding-right: 1px;
padding-bottom: 1px;
}
}
.line .shadow span {
padding-left: 1px;
padding-right: 1px;
padding-bottom: 1px;
}
// &.line-mode {
// .shadow {
// text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.40);
@@ -134,7 +146,7 @@ export default {
// .stroke(2px, rgba(0, 0, 0, 0.025));
transition: font-size @transition-slow;
}
.font-mode .line .shadow {
.font-mode .line .shadow span {
.stroke(1px, var(--color-lyric-shadow-font-mode));
transition: font-size @transition-slow;
// text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3), 1px 1px 1px rgba(0, 0, 0, 0.3);
@@ -171,6 +183,9 @@ export default {
.lyric-space {
height: 80%;
}
// .lyric-text {
// }
// .lrc-active {
// .lrc-line {

View File

@@ -91,7 +91,7 @@ export default () => {
const handleMove = (x, y) => {
if (isMsDown.value) {
if (!isStopScroll) isStopScroll = true
isStopScroll ||= true
if (cancelScrollFn) {
cancelScrollFn()
cancelScrollFn = null
@@ -164,6 +164,7 @@ export default () => {
const scrollLine = (line, oldLine) => {
if (line < 0 || !lyric.lines.length) return
if (line == 0 && isSetedLines) return isSetedLines = false
isSetedLines &&= false
if (oldLine == null || line - oldLine != 1) return handleScrollLrc()
if (setting['desktopLyric.isDelayScroll']) {

View File

@@ -5,7 +5,7 @@
:style="lrcStyles" @wheel="handleWheel" @mousedown="handleLyricMouseDown" @touchstart="handleLyricTouchStart"
>
<div :class="$style.lyricSpace" />
<div ref="dom_lyric_text" :class="[$style.lyricText]" />
<div ref="dom_lyric_text" />
<div :class="$style.lyricSpace" />
</div>
</template>
@@ -72,17 +72,20 @@ export default {
}
:global {
.font-lrc, .shadow {
padding: 0.14em 0.07em;
}
.font-lrc {
color: var(--color-lyric-unplay);
}
.shadow {
color: transparent;
margin-left: -0.14em;
}
.line-content {
line-height: 1.2;
margin: 0 var(--line-gap);
overflow-wrap: break-word;
letter-spacing: 5px;
.font-lrc {
cursor: grab;
@@ -93,6 +96,7 @@ export default {
margin-right: var(--line-extended-gap);
}
&.line-mode {
letter-spacing: 5px;
.font-lrc {
transition: @transition-slow;
transition-property: font-size, color;
@@ -120,8 +124,13 @@ export default {
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
background-size: 0 100%;
padding: 2px;
}
}
&.font-mode .line .shadow span {
padding: 2px;
}
}
// .shadow {
// .stroke2(rgba(0, 0, 0, 0.05));
@@ -139,7 +148,7 @@ export default {
// .stroke(2px, rgba(0, 0, 0, 0.025));
transition: font-size @transition-slow;
}
.font-mode .line .shadow {
.font-mode .line .shadow span {
.stroke(1px, var(--color-lyric-shadow-font-mode));
// .stroke(1px, rgba(0, 0, 0, 0.07));
transition: font-size @transition-slow;
@@ -178,6 +187,9 @@ export default {
width: 80%;
height: 100%;
}
// .lyric-text {
// }
// .lrc-active {
// .lrc-line {

View File

@@ -91,7 +91,7 @@ export default () => {
const handleMove = (x, y) => {
if (isMsDown.value) {
if (!isStopScroll) isStopScroll = true
isStopScroll ||= true
if (cancelScrollFn) {
cancelScrollFn()
cancelScrollFn = null
@@ -164,6 +164,7 @@ export default () => {
const scrollLine = (line, oldLine) => {
if (line < 0) return
if (line == 0 && isSetedLines) return isSetedLines = false
isSetedLines &&= false
if (oldLine == null || line - oldLine != 1) return handleScrollLrc()
if (setting['desktopLyric.isDelayScroll']) {

View File

@@ -22,16 +22,26 @@ let mouseCheckTools: {
let yDiff = Math.abs(this.y - this.preY)
if (xDiff > 8) {
if (this.x > this.preX) {
if (this.x + xDiff * 1.25 > window.innerWidth - 16) return setShow()
if (this.x + xDiff * 1.25 > window.innerWidth - 16) {
setShow()
return
}
} else {
if (this.x - xDiff * 1.25 < 8) return setShow()
if (this.x - xDiff * 1.25 < 8) {
setShow()
return
}
}
}
if (yDiff > 8) {
if (this.y > this.preY) {
if (this.y + yDiff * 1.25 > window.innerHeight - 16) return setShow()
if (this.y + yDiff * 1.25 > window.innerHeight - 16) {
setShow()
}
} else {
if (this.y - yDiff * 1.25 < 8) return setShow()
if (this.y - yDiff * 1.25 < 8) {
setShow()
}
}
}

View File

@@ -1,5 +1,11 @@
<template>
<component :is="containerEl" ref="dom_scrollContainer" :class="containerClass" tabindex="0" style="outline: none; height: 100%; overflow-y: auto; position: relative; display: block; contain: strict;">
<component
:is="containerEl"
ref="dom_scrollContainer"
:class="containerClass"
tabindex="0"
style="outline: none; height: 100%; overflow-y: auto; position: relative; display: block; contain: strict; box-sizing: border-box; will-change: transform;"
>
<component :is="contentEl" :class="contentClass" :style="contentStyle">
<div v-for="item in views" :key="item.key" :style="item.style">
<slot name="default" v-bind="{ item: item.item, index: item.index }" />
@@ -19,6 +25,24 @@ import {
onBeforeUnmount,
} from 'vue'
/**
* 生成防抖函数
* @param {*} fn
* @param {*} delay
*/
export const debounce = (fn, delay = 100) => {
let timer = null
let _args = null
return function(...args) {
_args = args
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
timer = null
fn.apply(this, _args)
}, delay)
}
}
const easeInOutQuad = (t, b, c, d) => {
t /= d / 2
if (t < 1) return (c / 2) * t * t + b
@@ -105,12 +129,14 @@ export default {
setup(props, { emit }) {
const views = ref([])
const dom_scrollContainer = ref(null)
let isListScrolling = false
const isListScrollingRef = ref(false)
let startIndex = -1
let endIndex = -1
let scrollTop = -1
let cachedList = []
let cancelScroll = null
let isScrolling = false
let isAutoScrolling = false
let scrollToValue = 0
const createList = (startIndex, endIndex) => {
@@ -171,7 +197,14 @@ export default {
scrollTop = currentScrollTop
}
const setStopScrollStatus = debounce(() => {
isListScrolling = false
isListScrollingRef.value = false
}, 200)
const onScroll = event => {
if (!isListScrolling) isListScrolling = isListScrollingRef.value = true
setStopScrollStatus()
const currentScrollTop = dom_scrollContainer.value.scrollTop
if (Math.abs(currentScrollTop - scrollTop) > props.itemHeight * 0.6) {
updateView(currentScrollTop)
@@ -189,15 +222,15 @@ export default {
}
}).then(() => {
if (animate) {
isScrolling = true
isAutoScrolling = true
scrollToValue = scrollTop
cancelScroll = handleScroll(dom_scrollContainer.value, scrollTop, 300, () => {
cancelScroll = null
isScrolling = false
isAutoScrolling = false
onScrollEnd(true)
}, () => {
cancelScroll = null
isScrolling = false
isAutoScrolling = false
onScrollEnd('canceled')
})
} else {
@@ -217,17 +250,21 @@ export default {
}
const getScrollTop = () => {
return isScrolling ? scrollToValue : dom_scrollContainer.value.scrollTop
return isAutoScrolling ? scrollToValue : dom_scrollContainer.value.scrollTop
}
const handleResize = () => {
window.setTimeout(updateView)
}
const contentStyle = computed(() => ({
display: 'block',
height: props.list.length * props.itemHeight + 'px',
}))
const contentStyle = computed(() => {
const style = {
display: 'block',
height: props.list.length * props.itemHeight + 'px',
}
if (isListScrollingRef.value) style['pointer-events'] = 'none'
return style
})
const handleReset = list => {
cachedList = Array(list.length)

View File

@@ -56,7 +56,7 @@ export default {
}
const handleMsMove = event => {
if (!msEvent.isMsDown) return
if (!dragging.value) dragging.value = true
dragging.value ||= true
let progress = msEvent.msDownProgress + (event.clientX - msEvent.msDownX) / dom_progress.value.clientWidth
if (progress > 1) progress = 1

View File

@@ -65,6 +65,7 @@ import {
} from '@renderer/store/player/action'
import { appSetting } from '@renderer/store/setting'
import { togglePlay, playNext, playPrev } from '@renderer/core/player'
import { LIST_IDS } from '@common/constants'
export default {
name: 'CorePlayBar',
@@ -98,7 +99,7 @@ export default {
const handleToMusicLocation = () => {
const listId = playMusicInfo.listId
if (!listId || listId == '__temp__' || listId == 'download' || !playMusicInfo.musicInfo) return
if (!listId || listId == LIST_IDS.DOWNLOAD || !playMusicInfo.musicInfo) return
if (playInfo.playIndex == -1) return
router.push({
path: '/list',

View File

@@ -65,6 +65,7 @@ import {
} from '@renderer/store/player/action'
import { appSetting } from '@renderer/store/setting'
import { togglePlay, playNext, playPrev } from '@renderer/core/player'
import { LIST_IDS } from '@common/constants'
export default {
name: 'CorePlayBar',
@@ -98,7 +99,7 @@ export default {
const handleToMusicLocation = () => {
const listId = playMusicInfo.listId
if (!listId || listId == '__temp__' || listId == 'download' || !playMusicInfo.musicInfo) return
if (!listId || listId == LIST_IDS.DOWNLOAD || !playMusicInfo.musicInfo) return
if (playInfo.playIndex == -1) return
router.push({
path: '/list',

View File

@@ -67,6 +67,7 @@ import {
} from '@renderer/store/player/action'
import { appSetting } from '@renderer/store/setting'
import { togglePlay, playNext, playPrev } from '@renderer/core/player'
import { LIST_IDS } from '@common/constants'
export default {
name: 'CorePlayBar',
@@ -100,7 +101,7 @@ export default {
const handleToMusicLocation = () => {
const listId = playMusicInfo.listId
if (!listId || listId == '__temp__' || listId == 'download' || !playMusicInfo.musicInfo) return
if (!listId || listId == LIST_IDS.DOWNLOAD || !playMusicInfo.musicInfo) return
if (playInfo.playIndex == -1) return
router.push({
path: '/list',

View File

@@ -142,9 +142,11 @@ export default {
})
onMounted(() => {
window.app_event.on('musicToggled', updateMusicInfo)
window.app_event.on('lyricUpdated', updateMusicInfo)
})
onBeforeUnmount(() => {
window.app_event.off('musicToggled', updateMusicInfo)
window.app_event.off('lyricUpdated', updateMusicInfo)
})

View File

@@ -125,14 +125,14 @@ export default {
let lxlyric = props.lyricInfo.lxlyric
if (offsetTagRxp.test(lyric)) {
lyric = lyric.replace(offsetTagAllRxp, `$1[offset:${offset}]`)
if (tlyric) tlyric = tlyric.replace(offsetTagAllRxp, `$1[offset:${offset}]`)
if (lxlyric) lxlyric = lxlyric.replace(offsetTagAllRxp, `$1[offset:${offset}]`)
if (rlyric) rlyric = rlyric.replace(offsetTagAllRxp, `$1[offset:${offset}]`)
tlyric &&= tlyric.replace(offsetTagAllRxp, `$1[offset:${offset}]`)
lxlyric &&= lxlyric.replace(offsetTagAllRxp, `$1[offset:${offset}]`)
rlyric &&= rlyric.replace(offsetTagAllRxp, `$1[offset:${offset}]`)
} else {
lyric = `[offset:${offset}]\n` + lyric
if (tlyric) tlyric = `[offset:${offset}]\n` + tlyric
if (lxlyric) lxlyric = `[offset:${offset}]\n` + lxlyric
if (rlyric) rlyric = `[offset:${offset}]\n` + rlyric
lyric &&= `[offset:${offset}]\n` + lyric
tlyric &&= `[offset:${offset}]\n` + tlyric
lxlyric &&= `[offset:${offset}]\n` + lxlyric
rlyric &&= `[offset:${offset}]\n` + rlyric
}
const musicInfo = 'progress' in props.lyricInfo.musicInfo ? props.lyricInfo.musicInfo.meta.musicInfo : props.lyricInfo.musicInfo

View File

@@ -28,7 +28,7 @@ export default {
const router = useRouter()
watch(() => route.name, (newValue, oldValue) => {
if (oldValue == 'Search') {
if (oldValue == 'Search' && newValue != 'SongListDetail') {
setTimeout(() => {
if (appSetting['odc.isAutoClearSearchInput'] && searchText.value) searchText.value = ''
if (appSetting['odc.isAutoClearSearchList']) setSearchText('')
@@ -64,7 +64,7 @@ export default {
}
const handleSearch = () => {
if (visibleList.value) visibleList.value = false
visibleList.value &&= false
if (!searchText.value && route.path != '/search') {
setSearchText('')
return
@@ -83,13 +83,13 @@ export default {
switch (action) {
case 'focus':
isFocused = true
if (!visibleList.value) visibleList.value = true
visibleList.value ||= true
if (searchText.value) handleTipSearch()
break
case 'blur':
isFocused = false
setTimeout(() => {
if (visibleList.value) visibleList.value = false
visibleList.value &&= false
}, 50)
break
case 'submit':

View File

@@ -21,7 +21,7 @@
</svg>
</button>
<button v-if="pauseBtn" type="button" :aria-label="$t('list__pause')" @contextmenu.capture.stop @click.stop="handleClick('pause')">
<svg v-once version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="100%" viewBox="0 0 277.338 277.338" space="preserve">
<svg v-once version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="100%" viewBox="0 0 1024 1024" space="preserve">
<use xlink:href="#icon-pause" />
</svg>
</button>

View File

@@ -12,16 +12,16 @@ const useKeyEvent = ({ handleSelectAllData }: {
}
const handle_key_shift_down = () => {
if (!keyEvent.isShiftDown) keyEvent.isShiftDown = true
keyEvent.isShiftDown ||= true
}
const handle_key_shift_up = () => {
if (keyEvent.isShiftDown) keyEvent.isShiftDown = false
keyEvent.isShiftDown &&= false
}
const handle_key_mod_down = () => {
if (!keyEvent.isModDown) keyEvent.isModDown = true
keyEvent.isModDown ||= true
}
const handle_key_mod_up = () => {
if (keyEvent.isModDown) keyEvent.isModDown = false
keyEvent.isModDown &&= false
}
const handle_key_mod_a_down = ({ event }: LX.KeyDownEevent) => {
if (!event || (event.target as HTMLElement).tagName == 'INPUT') return

View File

@@ -5,6 +5,7 @@ import { addTempPlayList } from '@renderer/store/player/action'
import { appSetting } from '@renderer/store/setting'
import { Ref } from '@common/utils/vueTools'
import { playList } from '@renderer/core/player'
import { LIST_IDS } from '@common/constants'
export default ({ selectedList, props, removeAllSelect, emit }: {
selectedList: Ref<LX.Music.MusicInfoOnline[]>
@@ -34,10 +35,10 @@ export default ({ selectedList, props, removeAllSelect, emit }: {
const handlePlayMusicLater = (index: number, single: boolean) => {
if (selectedList.value.length && !single) {
addTempPlayList(selectedList.value.map(s => ({ listId: '__temp__', musicInfo: s })))
addTempPlayList(selectedList.value.map(s => ({ listId: LIST_IDS.PLAY_LATER, musicInfo: s })))
removeAllSelect()
} else {
addTempPlayList([{ listId: '__temp__', musicInfo: props.list[index] }])
addTempPlayList([{ listId: LIST_IDS.PLAY_LATER, musicInfo: props.list[index] }])
}
}

View File

@@ -19,7 +19,7 @@ import { requestMsg } from '@renderer/utils/message'
import { getRandom } from '@renderer/utils/index'
// import { checkMusicFileAvailable } from '@renderer/utils/music'
let isGettingUrl = false
let gettingUrlId = ''
const createDelayNextTimeout = (delay: number) => {
let timeout: NodeJS.Timeout | null
const clearDelayNextTimeout = () => {
@@ -86,7 +86,7 @@ const getMusicPlayUrl = async(musicInfo: LX.Music.MusicInfo | LX.Download.ListIt
export const setMusicUrl = (musicInfo: LX.Music.MusicInfo | LX.Download.ListItem, isRefresh?: boolean) => {
if (appSetting['player.autoSkipOnError']) addLoadTimeout()
isGettingUrl = true
gettingUrlId = musicInfo.id
void getMusicPlayUrl(musicInfo, isRefresh).then((url) => {
if (!url) return
setResource(url)
@@ -96,8 +96,10 @@ export const setMusicUrl = (musicInfo: LX.Music.MusicInfo | LX.Download.ListItem
window.app_event.error()
if (appSetting['player.autoSkipOnError']) addDelayNextTimeout()
}).finally(() => {
clearLoadTimeout()
if (musicInfo === playMusicInfo.musicInfo) isGettingUrl = false
if (musicInfo === playMusicInfo.musicInfo) {
gettingUrlId = ''
clearLoadTimeout()
}
})
}
@@ -111,7 +113,7 @@ const handleRestorePlay = async(restorePlayInfo: LX.Player.SavedPlayInfo) => {
})
void getPicPath({ musicInfo, listId: playMusicInfo.isTempPlay ? null : playMusicInfo.listId }).then((url: string) => {
void getPicPath({ musicInfo, listId: playMusicInfo.listId }).then((url: string) => {
if (musicInfo.id != playMusicInfo.musicInfo?.id) return
setMusicInfo({ pic: url })
window.app_event.picUpdated()
@@ -126,12 +128,11 @@ const handleRestorePlay = async(restorePlayInfo: LX.Player.SavedPlayInfo) => {
rlrc: lyricInfo.rlyric,
rawlrc: lyricInfo.rawlrcInfo.lyric,
})
window.app_event.lyricUpdated()
}).catch((err) => {
console.log(err)
if (musicInfo.id != playMusicInfo.musicInfo?.id) return
setAllStatus(window.i18n.t('lyric__load_error'))
}).finally(() => {
window.app_event.lyricUpdated()
})
if (appSetting['player.togglePlayMethod'] == 'random') addPlayedList({ ...playMusicInfo as LX.Player.PlayMusicInfo })
@@ -140,14 +141,17 @@ const handleRestorePlay = async(restorePlayInfo: LX.Player.SavedPlayInfo) => {
// 处理音乐播放
const handlePlay = () => {
if (window.lx.isPlayedStop) window.lx.isPlayedStop = false
if (isGettingUrl) isGettingUrl = false
window.lx.isPlayedStop &&= false
if (window.lx.restorePlayInfo) {
void handleRestorePlay(window.lx.restorePlayInfo)
window.lx.restorePlayInfo = null
return
}
const musicInfo = playMusicInfo.musicInfo
if (!musicInfo || gettingUrlId == musicInfo.id) return
gettingUrlId &&= ''
setStop()
window.app_event.pause()
@@ -155,15 +159,12 @@ const handlePlay = () => {
clearDelayNextTimeout()
clearLoadTimeout()
const musicInfo = playMusicInfo.musicInfo
if (!musicInfo) return
if (appSetting['player.togglePlayMethod'] == 'random' && playMusicInfo.listId) addPlayedList({ ...(playMusicInfo as LX.Player.PlayMusicInfo) })
if (appSetting['player.togglePlayMethod'] == 'random' && !playMusicInfo.isTempPlay) addPlayedList({ ...(playMusicInfo as LX.Player.PlayMusicInfo) })
setMusicUrl(musicInfo)
void setMusicUrl(musicInfo)
void getPicPath({ musicInfo, listId: playMusicInfo.isTempPlay ? null : playMusicInfo.listId }).then((url: string) => {
void getPicPath({ musicInfo, listId: playMusicInfo.listId }).then((url: string) => {
if (musicInfo.id != playMusicInfo.musicInfo?.id) return
setMusicInfo({ pic: url })
window.app_event.picUpdated()
@@ -178,12 +179,11 @@ const handlePlay = () => {
rlrc: lyricInfo.rlyric,
rawlrc: lyricInfo.rawlrcInfo.lyric,
})
window.app_event.lyricUpdated()
}).catch((err) => {
console.log(err)
if (musicInfo.id != playMusicInfo.musicInfo?.id) return
setAllStatus(window.i18n.t('lyric__load_error'))
}).finally(() => {
window.app_event.lyricUpdated()
})
}
@@ -221,11 +221,17 @@ export const playNext = async(isAutoToggle = false): Promise<void> => {
return
}
if (playMusicInfo.musicInfo == null) return handleToggleStop()
if (playMusicInfo.musicInfo == null) {
handleToggleStop()
return
}
// console.log(playInfo.playerListId)
const currentListId = playInfo.playerListId
if (!currentListId) return handleToggleStop()
if (!currentListId) {
handleToggleStop()
return
}
const currentList = getList(currentListId)
if (playedList.length) { // 移除已播放列表内不存在原列表的歌曲
@@ -263,7 +269,10 @@ export const playNext = async(isAutoToggle = false): Promise<void> => {
playerMusicInfo: currentList[playInfo.playerPlayIndex],
})
if (!filteredList.length) return handleToggleStop()
if (!filteredList.length) {
handleToggleStop()
return
}
// let currentIndex: number = filteredList.indexOf(currentList[playInfo.playerPlayIndex])
if (playerIndex == -1 && filteredList.length) playerIndex = 0
let nextIndex = playerIndex
@@ -309,10 +318,16 @@ export const playNext = async(isAutoToggle = false): Promise<void> => {
* 上一曲
*/
export const playPrev = async(isAutoToggle = false): Promise<void> => {
if (playMusicInfo.musicInfo == null) return handleToggleStop()
if (playMusicInfo.musicInfo == null) {
handleToggleStop()
return
}
const currentListId = playInfo.playerListId
if (!currentListId) return handleToggleStop()
if (!currentListId) {
handleToggleStop()
return
}
const currentList = getList(currentListId)
if (playedList.length) {
@@ -350,7 +365,10 @@ export const playPrev = async(isAutoToggle = false): Promise<void> => {
playedList,
playerMusicInfo: currentList[playInfo.playerPlayIndex],
})
if (!filteredList.length) return handleToggleStop()
if (!filteredList.length) {
handleToggleStop()
return
}
// let currentIndex = filteredList.indexOf(currentList[playInfo.playerPlayIndex])
if (playerIndex == -1 && filteredList.length) playerIndex = 0
@@ -398,7 +416,7 @@ export const playPrev = async(isAutoToggle = false): Promise<void> => {
export const play = () => {
if (playMusicInfo.musicInfo == null) return
if (isEmpty()) {
void setMusicUrl(playMusicInfo.musicInfo)
if (playMusicInfo.musicInfo.id != gettingUrlId) setMusicUrl(playMusicInfo.musicInfo)
return
}
setPlay()
@@ -425,7 +443,7 @@ export const stop = () => {
* 播放、暂停播放切换
*/
export const togglePlay = () => {
if (window.lx.isPlayedStop) window.lx.isPlayedStop = false
window.lx.isPlayedStop &&= false
if (isPlay.value) {
pause()
} else {

View File

@@ -2,7 +2,7 @@ import { ref, computed, ComputedRef } from '@common/utils/vueTools'
import { isPlay } from '@renderer/store/player/state'
import { appSetting } from '@renderer/store/setting'
// import { interval, intervalCancel } from '@renderer/utils/ipc'
import { stop } from './action'
import { pause } from './action'
const time = ref(-1)
@@ -25,7 +25,7 @@ const timeoutTools: {
exit() {
window.lx.isPlayedStop = true
if (!appSetting['player.waitPlayEndStop'] && isPlay.value) {
stop()
pause()
}
},
clearTimeout() {
@@ -67,12 +67,12 @@ const timeoutTools: {
}
export const startTimeoutStop = (time: number) => {
if (window.lx.isPlayedStop) window.lx.isPlayedStop = false
window.lx.isPlayedStop &&= false
timeoutTools.start(time)
}
export const stopTimeoutStop = () => {
console.warn('stopTimeoutStop')
if (window.lx.isPlayedStop) window.lx.isPlayedStop = false
window.lx.isPlayedStop &&= false
timeoutTools.clearTimeout()
}

View File

@@ -33,12 +33,16 @@ export default () => {
return async() => {
await Promise.all([
initUserApi(), // 自定义API
]).catch(err => log.error(err))
]).catch(err => {
log.error(err)
})
void music.init() // 初始化音乐sdk
unregister = registerAction((ids) => {
window.app_event.myListUpdate(ids)
})
window.lxData.userLists = await getUserLists() // 获取用户列表
await initPrevPlayInfo().catch(err => log.error(err)) // 初始化上次的歌曲播放信息
await initPrevPlayInfo().catch(err => {
log.error(err)
}) // 初始化上次的歌曲播放信息
}
}

View File

@@ -9,6 +9,7 @@ import { dataVerify, qualityFilter, sources } from './utils'
import { focusWindow } from '@renderer/utils/ipc'
import { playNext } from '@renderer/core/player/action'
import { toNewMusicInfo } from '@common/utils/tools'
import { LIST_IDS } from '@common/constants'
const useSearchMusic = () => {
const router = useRouter()
@@ -153,7 +154,7 @@ const usePlayMusic = () => {
musicInfo = toNewMusicInfo(musicInfo)
markRaw(musicInfo)
const isPlaying = !!playMusicInfo.musicInfo
addTempPlayList([{ listId: '__temp__', musicInfo, isTop: true }])
addTempPlayList([{ listId: LIST_IDS.PLAY_LATER, musicInfo, isTop: true }])
if (isPlaying) playNext()
}
}

View File

@@ -14,6 +14,7 @@ const getDevices = async() => {
export default () => {
let prevDeviceLabel: string | null = null
let prevDeviceId = ''
const setMediaDevice = async(mediaDeviceId: string) => {
let label = prevDeviceLabel
@@ -30,9 +31,15 @@ export default () => {
prevDeviceLabel = label
// console.log(device)
setMediaDeviceId(mediaDeviceId).catch((err: any) => {
setMediaDeviceId(mediaDeviceId).then(() => {
prevDeviceId = mediaDeviceId
saveMediaDeviceId(mediaDeviceId)
}).catch((err: any) => {
console.log(err)
saveMediaDeviceId('default')
setMediaDeviceId('default').finally(() => {
prevDeviceId = 'default'
saveMediaDeviceId('default')
})
})
}
@@ -53,20 +60,20 @@ export default () => {
let mediaDeviceId = appSetting['player.mediaDeviceId']
const devices = await getDevices()
let device = devices.find(device => device.deviceId === mediaDeviceId)
if (!device) device = devices.find(device => device.deviceId === 'default')
device ??= devices.find(device => device.deviceId === 'default')
// @ts-expect-error
if (!device) device = { label: '', deviceId: '' }
device ??= { label: '', deviceId: '' }
// @ts-expect-error
handleDeviceChangeStopPlay(device, mediaDeviceId)
setMediaDeviceId(device!.deviceId).catch((err: any) => {
console.log(err)
saveMediaDeviceId('default')
})
void setMediaDevice(device!.deviceId)
}
watch(() => appSetting['player.mediaDeviceId'], setMediaDevice)
watch(() => appSetting['player.mediaDeviceId'], (id) => {
if (prevDeviceId == id) return
void setMediaDevice(id)
})
void setMediaDevice(appSetting['player.mediaDeviceId'])

View File

@@ -49,7 +49,10 @@ export default () => {
const addDelayNextTimeout = () => {
clearDelayNextTimeout()
delayNextTimeout = setTimeout(() => {
if (window.lx.isPlayedStop) return setAllStatus('')
if (window.lx.isPlayedStop) {
setAllStatus('')
return
}
void playNext(true)
}, 5000)
}

View File

@@ -33,7 +33,7 @@ export default () => {
if (window.lx.isPlayedStop) return
const currentTime = getCurrentTime()
if (!mediaBuffer.playTime) mediaBuffer.playTime = currentTime
mediaBuffer.playTime ||= currentTime
let skipTime = currentTime + getRandom(3, 6)
if (skipTime > playProgress.maxPlayTime) skipTime = (playProgress.maxPlayTime - currentTime) / 2
if (skipTime - mediaBuffer.playTime < 1 || playProgress.maxPlayTime - skipTime < 1) {
@@ -61,7 +61,7 @@ export default () => {
const setProgress = (time: number, maxTime?: number) => {
if (!musicInfo.id) return
console.log('setProgress', time, maxTime)
restorePlayTime = time
if (time > 0) restorePlayTime = time
if (mediaBuffer.playTime) {
clearBufferTimeout()
mediaBuffer.playTime = time
@@ -97,7 +97,7 @@ export default () => {
}
const handleError = () => {
if (!restorePlayTime) restorePlayTime = getCurrentTime() // 记录出错的播放时间
restorePlayTime ||= getCurrentTime() // 记录出错的播放时间
console.log('handleError')
prevProgressStatus = 'error'
handleSetTaskBarState(playProgress.progress, prevProgressStatus)
@@ -106,14 +106,8 @@ export default () => {
const handleLoadeddata = () => {
setMaxplayTime(getDuration())
console.log('handleLoadeddata', restorePlayTime)
if (restorePlayTime) {
setCurrentTime(restorePlayTime)
restorePlayTime = 0
}
if (playMusicInfo.musicInfo && 'source' in playMusicInfo.musicInfo && !playMusicInfo.musicInfo.interval) {
console.log(formatPlayTime2(playProgress.maxPlayTime))
// console.log(formatPlayTime2(playProgress.maxPlayTime))
if (playMusicInfo.listId) {
void updateListMusics([{
@@ -128,12 +122,15 @@ export default () => {
}
const handleCanplay = () => {
console.log('handleCanplay', mediaBuffer.playTime)
console.log('handleCanplay', mediaBuffer.playTime, restorePlayTime)
clearBufferTimeout()
if (mediaBuffer.playTime) {
let playTime = mediaBuffer.playTime
mediaBuffer.playTime = 0
setCurrentTime(playTime)
} else if (restorePlayTime) {
setCurrentTime(restorePlayTime)
restorePlayTime = 0
}
}
const handleWating = () => {

View File

@@ -32,18 +32,18 @@ export default () => {
}
const handlePlay = () => {
if (buttons.empty) buttons.empty = false
buttons.empty &&= false
buttons.play = true
setButtons()
}
const handlePause = () => {
if (buttons.empty) buttons.empty = false
buttons.empty &&= false
buttons.play = false
setButtons()
}
const handleStop = () => {
if (playMusicInfo.musicInfo != null) return
if (buttons.collect) buttons.collect = false
buttons.collect &&= false
buttons.empty = true
setButtons()
}

View File

@@ -20,7 +20,7 @@ import {
setAllStatus,
addPlayedList,
clearPlayedList,
resetPlayerMusicInfo,
// resetPlayerMusicInfo,
} from '@renderer/store/player/action'
import { appSetting } from '@renderer/store/setting'
@@ -68,9 +68,12 @@ export default () => {
}
const handleEnded = () => {
// setTimeout(() => {
if (window.lx.isPlayedStop) return setAllStatus(t('player__end'))
resetPlayerMusicInfo()
window.app_event.stop()
if (window.lx.isPlayedStop) {
setAllStatus(t('player__end'))
return
}
// resetPlayerMusicInfo()
// window.app_event.stop()
setAllStatus(t('player__end'))
void playNext(true)
// })

View File

@@ -9,7 +9,7 @@ const changedListIds = new Set<string | null>()
export default () => {
const throttleListChange = throttle(() => {
const isSkip = !changedListIds.has(playInfo.playerListId) && !changedListIds.has(playMusicInfo.listId)
const isSkip = playMusicInfo.listId && !changedListIds.has(playInfo.playerListId) && !changedListIds.has(playMusicInfo.listId)
changedListIds.clear()
if (isSkip) return

View File

@@ -49,4 +49,6 @@ export const unregisterKeyEvent = () => {
keyBind.unbindKey()
}
export const clearDownKeys = () => keyBind.clearDownKeys()
export const clearDownKeys = () => {
keyBind.clearDownKeys()
}

View File

@@ -53,7 +53,7 @@ void getSetting().then(setting => {
break
}
}
if (langId == null) langId = 'en-us'
langId ??= 'en-us'
}
setting['common.langId'] = langId
void updateSetting({ 'common.langId': langId })

View File

@@ -56,7 +56,7 @@ export const createUserList = async({ name, id = `userlist_${Date.now()}`, list
name?: string
id?: string
list?: LX.Music.MusicInfo[]
source?: LX.Source
source?: LX.OnlineSource
sourceListId?: string
position?: number
}) => {

View File

@@ -147,7 +147,7 @@ export const listDataOverwrite = ({ defaultList, loveList, userList, tempList }:
export const userListCreate = ({ name, id, source, sourceListId, position, locationUpdateTime }: {
name: string
id: string
source?: LX.Source
source?: LX.OnlineSource
sourceListId?: string
position: number
locationUpdateTime: number | null

View File

@@ -191,12 +191,12 @@ export const registerListAction = (appSetting: LX.AppSetting, onListChanged: (li
userListsUpdatePosition(position, ids)
}
const list_music_add = ({ params: { id, musicInfos, addMusicLocationType } }: LX.IpcRendererEventParams<LX.List.ListActionMusicAdd>) => {
if (!addMusicLocationType) addMusicLocationType = appSetting['list.addMusicLocationType']
addMusicLocationType ??= appSetting['list.addMusicLocationType']
const updatedListIds = listMusicAdd(id, musicInfos, addMusicLocationType)
if (updatedListIds.length) onListChanged(updatedListIds)
}
const list_music_move = ({ params: { fromId, toId, musicInfos, addMusicLocationType } }: LX.IpcRendererEventParams<LX.List.ListActionMusicMove>) => {
if (!addMusicLocationType) addMusicLocationType = appSetting['list.addMusicLocationType']
addMusicLocationType ??= appSetting['list.addMusicLocationType']
const updatedListIds = listMusicMove(fromId, toId, musicInfos, addMusicLocationType)
if (updatedListIds.length) onListChanged(updatedListIds)
}

View File

@@ -22,7 +22,7 @@ const fetchList = async(id: string, source: LX.OnlineSource, sourceListId: strin
export default async(targetListInfo: LX.List.UserListInfo) => {
// console.log(targetListInfo)
if (!targetListInfo.source || !targetListInfo.sourceListId) return
const list = await fetchList(targetListInfo.id, targetListInfo.source as LX.OnlineSource, targetListInfo.sourceListId)
const list = await fetchList(targetListInfo.id, targetListInfo.source, targetListInfo.sourceListId)
// console.log(list)
void overwriteListMusics({ listId: targetListInfo.id, musicInfos: list })
const now = Date.now()

View File

@@ -224,7 +224,7 @@ export const clearPlayedList = () => {
* @param list 歌曲列表
*/
export const addTempPlayList = (list: LX.Player.TempPlayListItem[]) => {
const topList: Array<{ listId: string, musicInfo: LX.Music.MusicInfo | LX.Download.ListItem }> = []
const topList: Array<Omit<LX.Player.TempPlayListItem, 'top'>> = []
const bottomList = list.filter(({ isTop, ...musicInfo }) => {
if (isTop) {
topList.push(musicInfo)

View File

@@ -22,7 +22,10 @@ export const buildBgUrl = (originUrl: string, dataPath: string): string => {
}
export const getThemes = (callback: (themeInfo: LX.ThemeInfo) => void) => {
if (themeInfo.themes.length) return callback(themeInfo)
if (themeInfo.themes.length) {
callback(themeInfo)
return
}
void getTheme().then(info => {
themeInfo.themes = markRaw(info.themes)
themeInfo.userThemes = shallowReactive(info.userThemes)

View File

@@ -9,7 +9,7 @@ declare namespace LX {
/**
* 当前播放歌曲的列表 id
*/
listId: string
listId: string | null
/**
* 是否属于 “稍后播放”
*/
@@ -35,7 +35,7 @@ declare namespace LX {
/**
* 播放列表id
*/
listId: '__temp__'
listId: string | null
/**
* 歌曲信息
*/

View File

@@ -10,12 +10,15 @@ export default () => {
switch (type) {
case 'defautlList':
case 'playList':
case 'playList_v2':
message = t('list_import_tip__playlist')
break
case 'setting':
case 'setting_v2':
message = t('list_import_tip__setting')
break
case 'allData':
case 'allData_v2':
message = t('list_import_tip__alldata')
break
case 'playListPart':

View File

@@ -21,7 +21,7 @@ export default (name: string) => {
}
const handle_key_up = () => {
if (keyDown.value) keyDown.value = false
keyDown.value &&= false
}
onMounted(() => {

View File

@@ -128,7 +128,7 @@ export default ({ isPlay, lyric, playProgress, isShowLyricProgressSetting, offse
}
const handleMove = (y) => {
if (isMsDown.value) {
if (!isStopScroll.value) isStopScroll.value = true
isStopScroll.value ||= true
if (cancelScrollFn) {
cancelScrollFn()
cancelScrollFn = null
@@ -150,7 +150,7 @@ export default ({ isPlay, lyric, playProgress, isShowLyricProgressSetting, offse
const handleWheel = (event) => {
console.log(event.deltaY)
if (!isStopScroll.value) isStopScroll.value = true
isStopScroll.value ||= true
if (cancelScrollFn) {
cancelScrollFn()
cancelScrollFn = null
@@ -197,6 +197,7 @@ export default ({ isPlay, lyric, playProgress, isShowLyricProgressSetting, offse
const scrollLine = (line, oldLine) => {
if (line < 0) return
if (line == 0 && isSetedLines) return isSetedLines = false
isSetedLines &&= false
if (oldLine == null || line - oldLine != 1) return handleScrollLrc()
delayScrollTimeout = setTimeout(() => {

View File

@@ -44,7 +44,7 @@ const saveViewPrevStateThrottle = throttle((state) => {
}, 1000)
const initPosition = async() => {
if (listPosition == null) listPosition = await getListPositionInfoFromData() ?? {}
listPosition ??= await getListPositionInfoFromData() ?? {}
}
export const getListPosition = async(id: string): Promise<number> => {
await initPosition()
@@ -74,7 +74,7 @@ const saveListPrevSelectIdThrottle = throttle(() => {
saveListPrevSelectIdFromData(listPrevSelectId)
}, 200)
export const getListPrevSelectId = async() => {
if (listPrevSelectId == null) listPrevSelectId = await getListPrevSelectIdFromData() ?? LIST_IDS.DEFAULT
listPrevSelectId ??= await getListPrevSelectIdFromData() ?? LIST_IDS.DEFAULT
return listPrevSelectId ?? LIST_IDS.DEFAULT
}
export const saveListPrevSelectId = (id: string) => {
@@ -138,7 +138,7 @@ export const overwriteListUpdateInfo = async(ids: string[]) => {
export const getSearchSetting = async() => {
if (!searchSetting) searchSetting = await getSearchSettingFromData()
searchSetting ??= await getSearchSettingFromData()
return { ...searchSetting }
}
export const setSearchSetting = async(setting: Partial<typeof DEFAULT_SETTING['search']>) => {
@@ -154,7 +154,7 @@ export const setSearchSetting = async(setting: Partial<typeof DEFAULT_SETTING['s
}
export const getSongListSetting = async() => {
if (!songListSetting) songListSetting = await getSongListSettingFromData()
songListSetting ??= await getSongListSettingFromData()
return { ...songListSetting }
}
export const setSongListSetting = async(setting: Partial<typeof DEFAULT_SETTING['songList']>) => {
@@ -164,7 +164,7 @@ export const setSongListSetting = async(setting: Partial<typeof DEFAULT_SETTING[
}
export const getLeaderboardSetting = async() => {
if (!leaderboardSetting) leaderboardSetting = await getLeaderboardSettingFromData()
leaderboardSetting ??= await getLeaderboardSettingFromData()
return { ...leaderboardSetting }
}
export const setLeaderboardSetting = async(setting: Partial<typeof DEFAULT_SETTING['leaderboard']>) => {

View File

@@ -27,7 +27,7 @@ export const dateFormat2 = (time: number): string => {
*/
let dom_title = document.getElementsByTagName('title')[0]
export const setTitle = (title: string | null) => {
if (!title) title = '洛雪音乐助手'
title ||= '洛雪音乐助手'
dom_title.innerText = title
}
@@ -60,14 +60,6 @@ export const deduplicationList = <T extends LX.Music.MusicInfo>(list: T[]): T[]
return true
})
}
export const filterMusicList = <T extends LX.Music.MusicInfo>(list: T[]): T[] => {
const ids: Set<string> = new Set()
return list.filter(s => {
if (!s.id || ids.has(s.id)) return false
ids.add(s.id)
return true
})
}
export const langS2T = async(str: string) => {
return await window.lx.worker.main.langS2t(Buffer.from(str).toString('base64')).then(b64 => Buffer.from(b64, 'base64').toString())

View File

@@ -21,7 +21,7 @@ export const onSettingChanged = (listener: LX.IpcRendererEventListenerParams<Par
}
export const sendInited = () => {
return rendererSend(WIN_MAIN_RENDERER_EVENT_NAME.inited)
rendererSend(WIN_MAIN_RENDERER_EVENT_NAME.inited)
}
export const getOtherSource = async(id: string): Promise<LX.Music.MusicInfoOnline[]> => {

View File

@@ -10,16 +10,16 @@ const useKeyEvent = ({ handleSelectAllData }) => {
}
const handle_key_shift_down = () => {
if (!keyEvent.isShiftDown) keyEvent.isShiftDown = true
keyEvent.isShiftDown ||= true
}
const handle_key_shift_up = () => {
if (keyEvent.isShiftDown) keyEvent.isShiftDown = false
keyEvent.isShiftDown &&= false
}
const handle_key_mod_down = () => {
if (!keyEvent.isModDown) keyEvent.isModDown = true
keyEvent.isModDown ||= true
}
const handle_key_mod_up = () => {
if (keyEvent.isModDown) keyEvent.isModDown = false
keyEvent.isModDown &&= false
}
const handle_key_mod_a_down = ({ event }) => {
if (event.target.tagName == 'INPUT') return

View File

@@ -137,10 +137,10 @@ export default {
},
handle_key_mod_down() {
console.log('handle_key_mod_down')
if (!this.isModDown) this.isModDown = true
this.isModDown ||= true
},
handle_key_mod_up() {
if (this.isModDown) this.isModDown = false
this.isModDown &&= false
},
handle_key_mod_f_down() {
if (this.visible) this.$refs.dom_input.focus()

View File

@@ -10,16 +10,16 @@ const useKeyEvent = ({ handleSelectAllData }) => {
}
const handle_key_shift_down = () => {
if (!keyEvent.isShiftDown) keyEvent.isShiftDown = true
keyEvent.isShiftDown ||= true
}
const handle_key_shift_up = () => {
if (keyEvent.isShiftDown) keyEvent.isShiftDown = false
keyEvent.isShiftDown &&= false
}
const handle_key_mod_down = () => {
if (!keyEvent.isModDown) keyEvent.isModDown = true
keyEvent.isModDown ||= true
}
const handle_key_mod_up = () => {
if (keyEvent.isModDown) keyEvent.isModDown = false
keyEvent.isModDown &&= false
}
const handle_key_mod_a_down = ({ event }) => {
if (event.target.tagName == 'INPUT') return

View File

@@ -1,259 +0,0 @@
<!-- <template>
<teleport to="#root">
<div ref="dom_menu" :class="$style.container" :style="menuStyles" :aria-hidden="!modelValue">
<div :class="$style.group">
<div :class="$style.subGroup">
<button :class="$style.btn" :disabled="!itemMenuControl.rename" ignore-tip :aria-label="$t('lists__rename')" @click="menuClick('rename')">{{ $t('lists__rename') }}</button>
<button :class="$style.btn" :disabled="offsetDisabled" ignore-tip :aria-label="$t('lists__sync')" @click="menuClick('sync')">{{ $t('lists__sync') }}</button>
</div>
<div :class="$style.subGroup">
<button :class="$style.btn" :disabled="offsetDisabled" ignore-tip :aria-label="$t('lists__sort_list')" @click="menuClick('sort')">{{ $t('lists__sort_list') }}</button>
<button :class="$style.btn" :disabled="offsetDisabled" ignore-tip :aria-label="$t('lyric_menu__offset_dec_100')" @click="menuClick(-100)">- 100ms</button>
</div>
</div>
</div>
</teleport>
</template>
<script>
import { computed, useRefGetter, ref, useCommit, watch } from '@common/utils/vueTools'
import useMenuLocation from '@renderer/utils/compositions/useMenuLocation'
import { setLyricEdited, removeLyricEdited, debounce } from '@renderer/utils'
const offsetTagRxp = /(?:^|\n)\s*\[offset:\s*(\S+(?:\d+)*)\s*\]/
const offsetTagAllRxp = /(?:^|\n)\s*\[offset:\s*(\S+(?:\d+)*)\s*\]/g
const saveLyric = debounce((musicInfo, lyricInfo) => {
setLyricEdited(musicInfo, lyricInfo)
})
const removeLyric = debounce(musicInfo => {
removeLyricEdited(musicInfo)
})
const getOffset = lrc => {
let offset = offsetTagRxp.exec(lrc)
if (offset) {
offset = parseInt(offset[1])
if (Number.isNaN(offset)) offset = 0
} else offset = 0
return offset
}
export default {
name: 'LyricMenu',
props: {
modelValue: Boolean,
xy: Object,
lyricInfo: Object,
},
emits: ['updateLyric', 'update:modelValue'],
setup(props, { emit }) {
const setting = useRefGetter('setting')
const playDetailSetting = useRefGetter('playDetailSetting')
const setPlayDetailLyricAlign = useCommit('setPlayDetailLyricAlign')
const setPlayDetailLyricFont = useCommit('setPlayDetailLyricFont')
const offset = ref(0)
const offsetDisabled = ref(true)
const originOffset = ref(0)
const visible = computed(() => props.modelValue)
const musicInfo = computed(() => props.lyricInfo.musicInfo)
const location = computed(() => props.xy)
const onHide = () => {
emit('update:modelValue', false)
}
const setFontAlign = val => {
if (playDetailSetting.value.style.align == val) return
setPlayDetailLyricAlign(val)
}
const fontSizeUp = step => {
if (setting.value.playDetail.style.fontSize >= 200) return
setPlayDetailLyricFont(Math.min(setting.value.playDetail.style.fontSize + step, 200))
}
const fontSizeDown = step => {
if (setting.value.playDetail.style.fontSize <= 70) return
setPlayDetailLyricFont(Math.max(setting.value.playDetail.style.fontSize - step, 70))
}
const fontSizeReset = () => {
setPlayDetailLyricFont(100)
}
const updateLyric = offset => {
let lyric = props.lyricInfo.lyric
let tlyric = props.lyricInfo.tlyric
let rlyric = props.lyricInfo.rlyric
let lxlyric = props.lyricInfo.lxlyric
if (offsetTagRxp.test(lyric)) {
lyric = lyric.replace(offsetTagAllRxp, `[offset:${offset}]`)
if (tlyric) tlyric = tlyric.replace(offsetTagAllRxp, `[offset:${offset}]`)
if (lxlyric) lxlyric = lxlyric.replace(offsetTagAllRxp, `[offset:${offset}]`)
if (rlyric) rlyric = rlyric.replace(offsetTagAllRxp, `[offset:${offset}]`)
} else {
lyric = `[offset:${offset}]\n` + lyric
if (tlyric) tlyric = `[offset:${offset}]\n` + tlyric
if (lxlyric) lxlyric = `[offset:${offset}]\n` + lxlyric
if (rlyric) rlyric = `[offset:${offset}]\n` + rlyric
}
if (offset == originOffset.value) {
removeLyric(props.lyricInfo.musicInfo)
} else {
saveLyric(props.lyricInfo.musicInfo, {
lyric,
tlyric,
rlyric,
lxlyric,
})
}
emit('updateLyric', {
lyric,
tlyric,
rlyric,
lxlyric,
offset,
})
}
const setOffset = step => {
offset.value += step
updateLyric(offset.value)
}
const offsetReset = () => {
if (offset.value == originOffset.value) return
offset.value = originOffset.value
updateLyric(originOffset.value)
}
const parseLrcOffset = () => {
offset.value = getOffset(props.lyricInfo.lyric)
originOffset.value = getOffset(props.lyricInfo.rawlyric)
offsetDisabled.value = !props.lyricInfo.lyric
}
const { dom_menu, menuStyles } = useMenuLocation({
visible,
location,
onHide,
})
watch(musicInfo, () => {
if (!props.modelValue) return
parseLrcOffset()
})
watch(visible, val => {
if (!val) return
parseLrcOffset()
})
return {
dom_menu,
menuStyles,
playDetailSetting,
offset,
originOffset,
fontSizeUp,
fontSizeDown,
fontSizeReset,
setOffset,
offsetReset,
setFontAlign,
offsetDisabled,
}
},
}
</script>
<style lang="less" module>
@import '@renderer/assets/styles/layout.less';
.container {
font-size: 12px;
position: absolute;
opacity: 0;
transform: scale(0);
transform-origin: 0 0 0;
transition: .25s ease;
transition-property: transform, opacity;
border-radius: @radius-border;
background-color: var(--color-main-background);
box-shadow: 0 1px 8px 0 rgba(0,0,0,.2);
z-index: 10;
overflow: hidden;
}
.group {
display: flex;
flex-direction: column;
}
.title {
flex: auto;
padding: 10px 0 10px 10px;
color: var(--color-font-label);
white-space: nowrap;
min-width: 120px;
}
.subGroup {
display: flex;
flex-flow: row nowrap;
}
.btn {
flex: auto;
white-space: nowrap;
cursor: pointer;
min-width: 60px;
height: 34px;
display: flex;
align-items: center;
justify-content: center;
// color: var(--color-button-font);
padding: 0 10px;
outline: none;
transition: @transition-normal;
transition-property: background-color, opacity;
box-sizing: border-box;
.mixin-ellipsis-1;
background-color: var(--color-main-background);
border: none;
&:hover {
opacity: .7;
background-color: @color-theme_2-hover;
}
&:active {
background-color: @color-theme_2-active;
}
&.active {
background-color: var(--color-main-background);
color: var(--color-button-background-active);
cursor: default;
opacity: 1;
}
&[disabled] {
cursor: default;
opacity: .4;
&:hover {
background: none !important;
}
}
}
.titleBtn {
flex: none;
padding: 0 10;
min-width: 40px;
opacity: .7;
&[disabled] {
opacity: .3;
}
}
</style>
-->

View File

@@ -51,8 +51,11 @@ export default () => {
break
case 'playListPart_v2':
listData = configData.data
listData.list = filterMusicList(listData.list).map(m => fixNewMusicInfoQuality(m))
break
default: return showImportTip(configData.type)
default:
showImportTip(configData.type)
return
}
const targetList = [defaultList, loveList, ...userLists].find(l => l.id == listData.id)

View File

@@ -35,8 +35,8 @@ const verifyQueryParams = async(to, from, next) => {
if (_source == null || _type == null) {
const setting = await getSearchSetting()
if (!_source) _source = setting.source
if (!_type) _type = setting.type
_source ??= setting.source
_type ??= setting.type
next({
path: to.path,

View File

@@ -79,9 +79,9 @@ export default {
name: list.name,
id: list.id,
list: filterMusicList(list.list.map(m => toNewMusicInfo(m))),
position: list.position,
source: list.source,
sourceListId: list.sourceListId,
locationUpdateTime: list.locationUpdateTime ?? null,
})
}
} catch (err) {
@@ -98,15 +98,15 @@ export default {
try {
const targetList = allLists.find(l => l.id == list.id)
if (targetList) {
targetList.list = list.list.map(m => fixNewMusicInfoQuality(m))
targetList.list = filterMusicList(list.list).map(m => fixNewMusicInfoQuality(m))
} else {
allLists.push({
name: list.name,
id: list.id,
list: list.list.map(m => fixNewMusicInfoQuality(m)),
position: list.position,
list: filterMusicList(list.list).map(m => fixNewMusicInfoQuality(m)),
source: list.source,
sourceListId: list.sourceListId,
locationUpdateTime: list.locationUpdateTime ?? null,
})
}
} catch (err) {

View File

@@ -127,9 +127,9 @@ export default {
const autoTheme = reactive({})
const updateAutoTheme = (info) => {
let light = findTheme(info, appSetting['theme.lightId'])
if (!light) light = info.themes.find(theme => theme.id == 'green')
light ??= info.themes.find(theme => theme.id == 'green')
let dark = findTheme(info, appSetting['theme.darkId'])
if (!dark) dark = info.themes.find(theme => theme.id == 'black')
dark ??= info.themes.find(theme => theme.id == 'black')
autoTheme['--color-primary-theme-light'] = light.config.themeColors['--color-theme']
autoTheme['--background-image-theme-light'] = light.isCustom
? light.config.extInfo['--background-image'] == 'none'

View File

@@ -1,5 +1,5 @@
<template>
<material-modal :show="props.modelValue" teleport="#view" width="60%" @close="emit('update:modelValue', $event)" @after-enter="$refs.input.focus()">
<material-modal :show="props.modelValue" teleport="#view" width="60%" bg-close @close="emit('update:modelValue', $event)" @after-enter="$refs.input.focus()">
<main :class="$style.main">
<h2>{{ $t('songlist__import_input_title') }}</h2>
<div :class="$style.inputContent">

View File

@@ -172,7 +172,7 @@ export const filterDuplicateMusic = async(list: LX.Music.MusicInfo[], isFilterVa
if (isFilterVariant) {
list.forEach((musicInfo, index) => {
let musicInfoName = musicInfo.name.toLowerCase().replace(variantRxp, '').replace(variantRxp2, '')
if (!musicInfoName) musicInfoName = musicInfo.name.toLowerCase().replace(/\s+/g, '')
musicInfoName ||= musicInfo.name.toLowerCase().replace(/\s+/g, '')
handleFilter(musicInfoName, index, musicInfo)
})
} else {
@@ -195,7 +195,7 @@ export const searchListMusic = (list: LX.Music.MusicInfo[], text: string) => {
if (rxp.test(str)) result.push(mInfo)
}
const sortedList: any[] = []
const sortedList: Array<{ num: number, data: LX.Music.MusicInfo }> = []
for (const mInfo of result) {
sortInsert(sortedList, {