Merge branch 'dev'

pull/930/merge v1.21.0
lyswhut 2022-05-22 11:17:47 +08:00
commit f2f2008d5d
80 changed files with 2634 additions and 4021 deletions

View File

@ -6,6 +6,32 @@ 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/).
## [1.21.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.20.0...v1.21.0) - 2022-05-22
### 新增
- 新增设置-播放设置-显示歌词罗马音,默认关闭,注:目前只有网易源能获取到罗马音歌词(得益于 Binaryify/NeteaseCloudMusicApi/pull/1523如果你知道其他源的歌词罗马音获取方式欢迎PR或开issue交流
### 优化
- 同时删除一首歌以上时将需要二次确认删除
- 禁用透明窗口时右侧不再偏移5px距离在win7、Ubuntu等系统上测试发现不偏移也不影响滚动条的拖动了
- 删除未下载完成的任务时,只同时尝试删除已有下载进度的本地文件
- 在全屏状态下使用`Esc`键可以退出全屏(#827
### 修复
- 修复某些情况下歌曲播放出错时不会自动切歌的问题
- 修复关闭“显示切换动画”设置后,在应用启动时该设置没有被应用的问题
- 修复原始歌词存在偏移时,歌词偏移设置的重置未按预期工作的问题
- 修复长度大于一行的歌词在使用歌词调整播放进度时的时间不准问题
- 修复潜在歌单更新失败的问题
### 文档
- 将歌曲添加“稍后播放”后,它们会被放在一个优先级最高的特殊队列中,点击“下一曲”时会消耗该队列中的歌曲,并且无法通过“上一曲”功能播放该队列的上一首歌曲
- 在切歌时若不是通过“上一曲”、“下一曲”功能切歌(例如直接点击“排行榜列表”、“我的列表”中的歌曲切歌),“稍后播放”队列将会被清空
## [1.20.0](https://github.com/lyswhut/lx-music-desktop/compare/v1.19.0...v1.20.0) - 2022-04-17
特别说明Scheme URL其实是支持Linux系统的但好像需要deb之类的安装包创建出`.desktop`文件才行。

4
FAQ.md
View File

@ -14,6 +14,8 @@
4. 对于歌单详情列表除了可以使用第2条的方式播放外你可以点击详情页上面的播放按钮临时播放当前歌单或点击收藏将当前歌单收藏到“我的列表”后再去播放
5. 对于排行榜详情列表除了可以使用第2条的方式播放外你可以在右击排行榜名字后弹出的菜单中播放或收藏整个排行榜这与第四条的歌单中的播放、与收藏按钮功能一致
6. v1.18.0及之后新增了“双击列表里的歌曲时自动切换到当前列表播放”设置,默认关闭,此功能仅对歌单、排行榜有效
7. 将歌曲添加“稍后播放”后,它们会被放在一个优先级最高的特殊队列中,点击“下一曲”时会消耗该队列中的歌曲,并且无法通过“上一曲”功能播放该队列的上一首歌曲
8. 在切歌时若不是通过“上一曲”、“下一曲”功能切歌(例如直接点击“排行榜列表”、“我的列表”中的歌曲切歌),“稍后播放”队列将会被清空
## 可用的鼠标、键盘快捷操作
@ -165,7 +167,7 @@
### Windows 7 下界面异常
由于软件默认使用了透明窗口根据Electron官方文档的[说明](https://electronjs.org/docs/api/frameless-window#%E5%B1%80%E9%99%90%E6%80%A7)
由于软件默认使用了透明窗口根据Electron官方文档的[说明](https://www.electronjs.org/docs/latest/tutorial/window-customization#limitations)
> 在 windows 操作系统上, 当 DWM 被禁用时, 透明窗口将无法工作。
因此,当 win7 没有使用**Aero**主题时界面将会显示异常开启AERO的方法请自行百度`win7开启Aero效果`(开启后可看到任务栏变透明)。<br>

View File

@ -6,7 +6,7 @@ module.exports = {
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../../dist/electron'),
path: path.join(__dirname, '../../dist'),
},
resolve: {
alias: {
@ -25,9 +25,6 @@ module.exports = {
},
],
},
performance: {
maxEntrypointSize: 300000,
},
plugins: [
new ESLintPlugin(),
],

View File

@ -19,4 +19,8 @@ module.exports = merge(baseConfig, {
__userApi: `"${path.join(__dirname, '../../src/main/modules/userApi').replace(/\\/g, '\\\\')}"`,
}),
],
performance: {
maxEntrypointSize: 1024 * 1024 * 50,
maxAssetSize: 1024 * 1024 * 30,
},
})

View File

@ -26,11 +26,11 @@ module.exports = merge(baseConfig, {
patterns: [
{
from: path.join(__dirname, '../../src/main/modules/userApi/renderer'),
to: path.join(__dirname, '../../dist/electron/userApi/renderer'),
to: path.join(__dirname, '../../dist/userApi/renderer'),
},
{
from: path.join(__dirname, '../../src/main/modules/userApi/rendererEvent/name.js'),
to: path.join(__dirname, '../../dist/electron/userApi/rendererEvent/name.js'),
to: path.join(__dirname, '../../dist/userApi/rendererEvent/name.js'),
},
],
}),
@ -40,6 +40,10 @@ module.exports = merge(baseConfig, {
},
}),
],
performance: {
maxEntrypointSize: 1024 * 1024 * 10,
maxAssetSize: 1024 * 1024 * 20,
},
optimization: {
minimize: false,
},

View File

@ -14,7 +14,7 @@ const okayLog = chalk.bgGreen.white(' OKAY ') + ' '
function build() {
del.sync(['dist/electron/**', 'build/**'])
del.sync(['dist/**', 'build/**'])
const spinners = new Spinnies({ color: 'blue' })
spinners.add('main', { text: 'main building' })

View File

@ -17,7 +17,7 @@ module.exports = {
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../../dist/electron'),
path: path.join(__dirname, '../../dist'),
publicPath: 'auto',
},
resolve: {
@ -127,9 +127,6 @@ module.exports = {
},
],
},
performance: {
maxEntrypointSize: 300000,
},
plugins: [
new HTMLPlugin({
filename: 'lyric.html',

View File

@ -1,8 +1,7 @@
const path = require('path')
// const path = require('path')
const webpack = require('webpack')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.config.base')
@ -20,14 +19,6 @@ module.exports = merge(baseConfig, {
...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)),
],
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: path.join(__dirname, '../../src/static'),
to: path.join(__dirname, '../../dist/electron/static'),
},
],
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"',
@ -44,6 +35,8 @@ module.exports = merge(baseConfig, {
],
},
performance: {
maxEntrypointSize: 1024 * 1024 * 10,
maxAssetSize: 1024 * 1024 * 20,
hints: 'warning',
},
node: {

View File

@ -17,7 +17,7 @@ module.exports = {
output: {
filename: '[name].js',
libraryTarget: 'commonjs2',
path: path.join(__dirname, '../../dist/electron'),
path: path.join(__dirname, '../../dist'),
publicPath: 'auto',
},
resolve: {
@ -127,9 +127,6 @@ module.exports = {
},
],
},
performance: {
maxEntrypointSize: 300000,
},
plugins: [
new HTMLPlugin({
filename: 'index.html',

View File

@ -23,7 +23,7 @@ module.exports = merge(baseConfig, {
patterns: [
{
from: path.join(__dirname, '../../src/static'),
to: path.join(__dirname, '../../dist/electron/static'),
to: path.join(__dirname, '../../dist/static'),
},
],
}),
@ -43,6 +43,8 @@ module.exports = merge(baseConfig, {
],
},
performance: {
maxEntrypointSize: 1024 * 1024 * 10,
maxAssetSize: 1024 * 1024 * 20,
hints: 'warning',
},
node: {

View File

@ -151,7 +151,7 @@ function startElectron() {
let args = [
'--inspect=5858',
// 'NODE_ENV=development',
path.join(__dirname, '../dist/electron/main.js'),
path.join(__dirname, '../dist/main.js'),
]
// detect yarn or npm and process commandline args accordingly

5701
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
{
"name": "lx-music-desktop",
"version": "1.20.0",
"version": "1.21.0",
"description": "一个免费的音乐查找助手",
"main": "./dist/electron/main.js",
"main": "./dist/main.js",
"productName": "lx-music-desktop",
"scripts": {
"pack": "node build-config/pack.js && npm run pack:win:setup:x64",
@ -57,7 +57,7 @@
"publish:linux:rpm": "cross-env ARCH=x64 electron-builder -l=rpm --x64 -p onTagOrDraft",
"publish:linux:pacman": "cross-env ARCH=x64 electron-builder -l=pacman --x64 -p onTagOrDraft",
"dev": "node build-config/runner-dev.js",
"clean:electron": "rimraf dist/electron",
"clean:electron": "rimraf dist",
"clean": "rimraf dist && rimraf build",
"build:src": "node build-config/pack.js",
"build:main": "cross-env NODE_ENV=production webpack --config build-config/main/webpack.config.prod.js --progress",
@ -74,7 +74,7 @@
],
"engines": {
"node": ">= 16",
"npm": ">=8.3.0"
"npm": ">=8.5.2"
},
"build": {
"appId": "cn.toside.music.desktop",
@ -89,7 +89,7 @@
"output": "./build"
},
"files": [
"dist/electron/**/*"
"dist/**/*"
],
"asar": {
"smartUnpack": false
@ -178,33 +178,33 @@
},
"homepage": "https://github.com/lyswhut/lx-music-desktop#readme",
"devDependencies": {
"@babel/core": "^7.17.9",
"@babel/core": "^7.18.0",
"@babel/eslint-parser": "^7.17.0",
"@babel/plugin-proposal-class-properties": "^7.16.7",
"@babel/plugin-proposal-class-properties": "^7.17.12",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/plugin-transform-modules-umd": "^7.16.7",
"@babel/plugin-transform-runtime": "^7.17.0",
"@babel/plugin-transform-modules-umd": "^7.18.0",
"@babel/plugin-transform-runtime": "^7.18.0",
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.16.11",
"babel-loader": "^8.2.4",
"babel-preset-minify": "^0.5.1",
"browserslist": "^4.20.2",
"@babel/preset-env": "^7.18.0",
"babel-loader": "^8.2.5",
"babel-preset-minify": "^0.5.2",
"browserslist": "^4.20.3",
"chalk": "^4.1.2",
"changelog-parser": "^2.8.1",
"copy-webpack-plugin": "^10.2.4",
"core-js": "^3.22.0",
"copy-webpack-plugin": "^11.0.0",
"core-js": "^3.22.5",
"cross-env": "^7.0.3",
"css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^3.4.1",
"del": "^6.0.0",
"css-minimizer-webpack-plugin": "^4.0.0",
"del": "^6.1.0",
"electron": "^13.6.9",
"electron-builder": "^23.0.6",
"electron-builder": "^23.0.9",
"electron-debug": "^3.2.0",
"electron-devtools-installer": "^3.2.0",
"electron-to-chromium": "^1.4.111",
"electron-updater": "^5.0.2",
"eslint": "^8.13.0",
"eslint-config-standard": "^16.0.3",
"electron-to-chromium": "^1.4.137",
"electron-updater": "^5.0.4",
"eslint": "^8.16.0",
"eslint-config-standard": "^17.0.0",
"eslint-formatter-friendly": "git+https://github.com/lyswhut/eslint-friendly-formatter.git#2170d1320e2fad13615a9dcf229669f0bb473a53",
"eslint-plugin-html": "^6.2.0",
"eslint-plugin-import": "^2.26.0",
@ -215,12 +215,11 @@
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.5.0",
"less": "^4.1.2",
"less-loader": "^10.2.0",
"markdown-it": "^12.3.2",
"less-loader": "^11.0.0",
"mini-css-extract-plugin": "^2.6.0",
"node-loader": "^2.0.0",
"postcss": "^8.4.12",
"postcss-loader": "^6.2.1",
"postcss": "^8.4.14",
"postcss-loader": "^7.0.0",
"postcss-pxtorem": "^6.0.0",
"pug": "^3.0.2",
"pug-loader": "^2.4.0",
@ -235,16 +234,16 @@
"url-loader": "^4.1.1",
"vue-loader": "^17.0.0",
"vue-template-compiler": "^2.6.14",
"webpack": "^5.72.0",
"webpack": "^5.72.1",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.8.1",
"webpack-dev-server": "^4.9.0",
"webpack-hot-middleware": "git+https://github.com/lyswhut/webpack-hot-middleware.git#329c4375134b89d39da23a56a94db651247c74a1",
"webpack-merge": "^5.8.0"
},
"dependencies": {
"bufferutil": "^4.0.6",
"crypto-js": "^4.1.1",
"electron-log": "^4.4.6",
"electron-log": "^4.4.7",
"electron-store": "^8.0.1",
"font-list": "git+https://github.com/lyswhut/node-font-list.git#4edbb1933b49a9bac1eedd63a31da16b487fe57d",
"http-terminator": "^3.2.0",
@ -256,13 +255,13 @@
"needle": "^3.1.0",
"node-id3": "^0.2.3",
"request": "^2.88.2",
"socket.io": "^4.4.1",
"socket.io": "^4.5.1",
"sortablejs": "^1.15.0",
"tunnel": "^0.0.6",
"utf-8-validate": "^5.0.9",
"vue": "^3.2.33",
"vue": "^3.2.35",
"vue-i18n": "^9.2.0-beta.35",
"vue-router": "^4.0.14",
"vue-router": "^4.1.0-aabe509",
"vuex": "^4.0.2"
},
"overrides": {

View File

@ -1,36 +1,23 @@
特别说明Scheme URL其实是支持Linux系统的但好像需要deb之类的安装包创建出`.desktop`文件才行。
### 新增
- 新增播放详情页歌词右键菜单,原来设置-播放详情页设置的字体重置已迁移到此菜单内
- 新增歌词偏移设置,可以在播放详情页歌词右键菜单中使用
- 新增设置-播放设置-播放错误时自动切换歌曲设置默认开启原来的行为若你不想在遇到音频加载失败、url获取失败等错误时自动切歌可以关闭此设置
- 新增设置-桌面歌词设置-自动刷新歌词置顶(当歌词置顶后仍被某些程序遮挡时可尝试启用此设置)
- 新增列表更新管理,可以在鼠标移入“我的列表”标题时出现的按钮中进入,这可以用来设置启动软件时需要自动从原平台更新的列表
- 新增设置-播放设置-显示歌词罗马音,默认关闭,注:目前只有网易源能获取到罗马音歌词(得益于 Binaryify/NeteaseCloudMusicApi/pull/1523如果你知道其他源的歌词罗马音获取方式欢迎PR或开issue交流
### 优化
- 优化播放详情页背景显示,现在有背景图片的主题可以在播放详情页显示它的图片了
- 播放详情页在全屏状态下仍会显示退出播放详情页按钮,同时在其旁边添加退出全屏按钮
- 播放详情页在全屏状态下鼠标在空白处静止不动3秒后自动将其隐藏
- 同时删除一首歌以上时将需要二次确认删除
- 禁用透明窗口时右侧不再偏移5px距离在win7、Ubuntu等系统上测试发现不偏移也不影响滚动条的拖动了
- 删除未下载完成的任务时,只同时尝试删除已有下载进度的本地文件
- 在全屏状态下使用`Esc`键可以退出全屏(#827
### 修复
- 修复Linux无法全屏的问题
- 修复播放下载列表的歌曲时使用Windows任务栏缩略图工具栏控制按钮的收藏按钮收藏歌曲时的异常问题
- 修复启用搜索历史但不启用热门搜索时,搜索历史不显示的问题
- 修复窗口尺寸设置对应的字体大小在启动后不生效的问题
- 修复wy源搜索某些歌曲时第一页之后的歌曲无法加载的问题
- 修复使用Scheme URL搜索歌曲时不会自动关闭播放详情页若处于打开状态的问题
- 修复换源失败时的处理问题
- 修复启用代理时https请求可能被挂起或被转为http的问题
- 修复正在下载的歌曲暂停任务后,再开始会导致程序卡死的问题
- 修复某些情况下歌曲播放出错时不会自动切歌的问题
- 修复关闭“显示切换动画”设置后,在应用启动时该设置没有被应用的问题
- 修复原始歌词存在偏移时,歌词偏移设置的重置未按预期工作的问题
- 修复长度大于一行的歌词在使用歌词调整播放进度时的时间不准问题
- 修复潜在歌单更新失败的问题
### 变更
### 文档
- 播放详情页的任意地方右键双击隐藏详情页的行为,“任意区域”改为在“非歌词区域”
### 移除
- 移除设置-播放详情页设置-歌词字体重置,此设置项已迁移到播放详情页的歌词菜单中
- 移除播放详情页使用+-快捷键调整字体大小的功能,改用歌词右键菜单的字体大小调整功能
- 将歌曲添加“稍后播放”后,它们会被放在一个优先级最高的特殊队列中,点击“下一曲”时会消耗该队列中的歌曲,并且无法通过“上一曲”功能播放该队列的上一首歌曲
- 在切歌时若不是通过“上一曲”、“下一曲”功能切歌(例如直接点击“排行榜列表”、“我的列表”中的歌曲切歌),“稍后播放”队列将会被清空

View File

@ -9,12 +9,12 @@ const version_bak = JSON.stringify(version, null, 2)
const parseChangelog = require('changelog-parser')
const changelogPath = jp('../../CHANGELOG.md')
const md_renderer = markdownStr => new (require('markdown-it'))({
html: true,
linkify: true,
typographer: true,
breaks: true,
}).render(markdownStr)
// const md_renderer = markdownStr => new (require('markdown-it'))({
// html: true,
// linkify: true,
// typographer: true,
// breaks: true,
// }).render(markdownStr)
const getPrevVer = () => parseChangelog(changelogPath).then(res => {
if (!res.versions.length) throw new Error('CHANGELOG 无法解析到版本号')
@ -28,7 +28,7 @@ const updateChangeLog = async(newVerNum, newChangeLog) => {
fs.writeFileSync(changelogPath, changeLog.replace(new RegExp('(## [?0.1.1]?)'), log + '\n$1'), 'utf-8')
}
const renderChangeLog = md => md_renderer(md)
// const renderChangeLog = md => md_renderer(md)
module.exports = async newVerNum => {
@ -38,7 +38,7 @@ module.exports = async newVerNum => {
newVerNum = verArr.join('.')
}
const newMDChangeLog = fs.readFileSync(jp('../changeLog.md'), 'utf-8')
const newChangeLog = renderChangeLog(newMDChangeLog)
// const newChangeLog = renderChangeLog(newMDChangeLog)
version.history.unshift({
version: version.version,
desc: version.desc,
@ -58,7 +58,7 @@ module.exports = async newVerNum => {
return {
pkg_bak,
version_bak,
changeLog: newChangeLog,
// changeLog: newChangeLog,
}
}

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@ const path = require('path')
const os = require('os')
const defaultSetting = {
version: '1.0.56',
version: '1.0.57',
player: {
togglePlayMethod: 'listLoop',
highQuality: false,
@ -12,6 +12,7 @@ const defaultSetting = {
mediaDeviceId: 'default',
isMediaDeviceRemovedStopPlay: false,
isShowLyricTranslation: false,
isShowLyricRoma: false,
isS2t: false, // 是否将歌词从简体转换为繁体
isPlayLxlrc: true,
isSavePlayTime: false,

View File

@ -120,6 +120,7 @@
"lists__new_list_btn": "Create list",
"lists__new_list_input": "New list...",
"lists__remove": "Remove",
"lists__remove music_tip": "Do you really want to remove the selected {len} songs?",
"lists__remove_tip": "Do you really want to remove {name}?",
"lists__remove_tip_button": "Yes, that's right",
"lists__rename": "Rename",
@ -355,7 +356,8 @@
"setting__play_detail_font_size_reset": "Reset",
"setting__play_detail_font_zoom": "Zoom the currently playing lyrics",
"setting__play_detail_lyric_progress": "Allows to adjust playback progress by lyrics",
"setting__play_lyric_lxlrc": "Use Karaoke-style lyrics playback (if supported)",
"setting__play_lyric_lxlrc": "Play with karaoke-style lyrics (if available)",
"setting__play_lyric_roma": "Show lyrics roman",
"setting__play_lyric_s2t": "Convert the playing and downloading lyrics to Traditional Chinese",
"setting__play_lyric_transition": "Show lyrics translation",
"setting__play_mediaDevice": "Audio output",
@ -363,7 +365,7 @@
"setting__play_mediaDevice_title": "Select a media device for audio output",
"setting__play_media_device_error_tip": "This function conflicts with the audio visualization function. You have enabled audio visualization when you started the software this time. This setting is temporarily unavailable. Please restart the software and then modify this setting.",
"setting__play_media_device_tip": "This feature conflicts with Audio Visualization, both cannot be enabled at the same time, would you like to turn Audio Visualization off and apply the selected audio output settings?",
"setting__play_quality": "Play 320K quality songs first (if supported)",
"setting__play_quality": "Priority playback of 320K quality songs (if available)",
"setting__play_save_play_time": "Remember playback progress",
"setting__play_task_bar": "Show playing progress on the taskbar",
"setting__play_timeout": "Timed pause",
@ -432,6 +434,7 @@
"sync__title": "Choose how to synchronize the list with {name}",
"tag__high_quality": "HQ",
"tag__lossless": "SQ",
"tag__lossless_24bit": "24bit",
"theme_auto": "Auto",
"theme_auto_tip": "Right-click to open the light and dark theme settings window",
"theme_black": "Black",

View File

@ -120,6 +120,7 @@
"lists__new_list_btn": "新建列表",
"lists__new_list_input": "新列表...",
"lists__remove": "删除",
"lists__remove music_tip": "你真的要移除所选的 {len} 首歌曲吗?",
"lists__remove_tip": "你真的想要移除 {name} 吗?",
"lists__remove_tip_button": "是的 没错",
"lists__rename": "重命名",
@ -355,15 +356,16 @@
"setting__play_detail_font_size_reset": "重置",
"setting__play_detail_font_zoom": "缩放当前正在播放的歌词",
"setting__play_detail_lyric_progress": "允许通过歌词调整播放进度",
"setting__play_lyric_lxlrc": "使用卡拉OK式歌词播放如果支持",
"setting__play_lyric_lxlrc": "使用卡拉OK式歌词播放如果可用",
"setting__play_lyric_roma": "显示歌词罗马音(如果可用)",
"setting__play_lyric_s2t": "将播放与下载的歌词转换为繁体中文",
"setting__play_lyric_transition": "显示歌词翻译",
"setting__play_lyric_transition": "显示歌词翻译(如果可用)",
"setting__play_mediaDevice": "音频输出",
"setting__play_mediaDevice_remove_stop_play": "当前的声音输出设备被改变时暂停播放歌曲",
"setting__play_mediaDevice_title": "选择声音输出的媒体设备",
"setting__play_media_device_error_tip": "此功能与音频可视化功能冲突,你本次启动软件时已启用过音频可视化,此设置暂不可用,请 重启 软件后,再来修改此设置。",
"setting__play_media_device_tip": "此功能与音频可视化功能冲突,两者无法同时启用,是否将音频可视化关闭 并 应用所选音频输出设置?",
"setting__play_quality": "优先播放320K品质的歌曲如果支持",
"setting__play_quality": "优先播放320K品质的歌曲如果可用",
"setting__play_save_play_time": "记住播放进度",
"setting__play_task_bar": "在任务栏上显示当前歌曲播放进度",
"setting__play_timeout": "定时暂停",
@ -432,6 +434,7 @@
"sync__title": "选择与 {name} 的列表同步方式",
"tag__high_quality": "HQ",
"tag__lossless": "SQ",
"tag__lossless_24bit": "24bit",
"theme_auto": "道法自然",
"theme_auto_tip": "鼠标 右击 可打开亮、暗主题设置窗口",
"theme_black": "黑灯瞎火",

View File

@ -120,6 +120,7 @@
"lists__new_list_btn": "新建列表",
"lists__new_list_input": "新列表...",
"lists__remove": "刪除",
"lists__remove music_tip": "你真的要移除所選的 {len} 首歌曲嗎?",
"lists__remove_tip": "你真的想要移除 {name} 嗎?",
"lists__remove_tip_button": "是的 沒錯",
"lists__rename": "重命名",
@ -355,15 +356,16 @@
"setting__play_detail_font_size_reset": "重置",
"setting__play_detail_font_zoom": "縮放當前正在播放的歌詞",
"setting__play_detail_lyric_progress": "允許通過歌詞調整播放進度",
"setting__play_lyric_lxlrc": "使用卡拉OK式歌詞播放如果支持",
"setting__play_lyric_lxlrc": "使用卡拉OK式歌詞播放如果可用",
"setting__play_lyric_roma": "顯示歌詞羅馬音(如果可用)",
"setting__play_lyric_s2t": "將播放與下載的歌詞轉換為繁體中文",
"setting__play_lyric_transition": "顯示歌詞翻譯",
"setting__play_lyric_transition": "顯示歌詞翻譯(如果可用)",
"setting__play_mediaDevice": "音頻輸出",
"setting__play_mediaDevice_remove_stop_play": "當前的聲音輸出設備被改變時暫停播放歌曲",
"setting__play_mediaDevice_title": "選擇聲音輸出的媒體設備",
"setting__play_media_device_error_tip": "此功能與音頻可視化功能衝突,你本次啟動軟件時已啟用過音頻可視化,此設置暫不可用,請 重啟 軟件後,再來修改此設置。",
"setting__play_media_device_tip": "此功能與音頻可視化功能衝突,兩者無法同時啟用,是否將音頻可視化關閉 並 應用所選音頻輸出設置?",
"setting__play_quality": "優先播放320K品質的歌曲如果支持",
"setting__play_quality": "優先播放320K品質的歌曲如果可用",
"setting__play_save_play_time": "記住播放進度",
"setting__play_task_bar": "在任務欄上顯示當前歌曲播放進度",
"setting__play_timeout": "定時暫停",
@ -432,6 +434,7 @@
"sync__title": "選擇與 {name} 的列表同步方式",
"tag__high_quality": "HQ",
"tag__lossless": "SQ",
"tag__lossless_24bit": "24bit",
"theme_auto": "道法自然",
"theme_auto_tip": "鼠標 右擊 可打開亮、暗主題設置窗口",
"theme_black": "黑燈瞎火",

View File

@ -67,11 +67,10 @@ exports.createWindow = async userApi => {
event.preventDefault()
})
}
global.modules.userApiWindow.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => {
if (webContents === global.modules.mainWindow.webContents) return callback(true)
global.modules.userApiWindow.webContents.session.setPermissionRequestHandler((webContents, permission, resolve) => {
if (webContents === global.modules.mainWindow.webContents) return resolve(true)
// eslint-disable-next-line node/no-callback-literal
callback(false)
resolve(false)
})
global.modules.userApiWindow.webContents.setWindowOpenHandler(() => {
return { action: 'deny' }

View File

@ -165,7 +165,7 @@ contextBridge.exposeInMainWorld('lx', {
headers: resp.headers,
bytes: resp.bytes,
raw: resp.raw,
body: body,
body,
}, body)
}).request
@ -212,7 +212,7 @@ contextBridge.exposeInMainWorld('lx', {
},
rsaEncrypt(buffer, key) {
buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer])
return publicEncrypt({ key: key, padding: constants.RSA_NO_PADDING }, buffer)
return publicEncrypt({ key, padding: constants.RSA_NO_PADDING }, buffer)
},
randomBytes(size) {
return randomBytes(size)

View File

@ -41,6 +41,7 @@ const setLrcConfig = () => {
config: desktopLyric,
languageId: global.appSetting.langId,
isShowLyricTranslation: global.appSetting.player.isShowLyricTranslation,
isShowLyricRoma: global.appSetting.player.isShowLyricRoma,
isPlayLxlrc: global.appSetting.player.isPlayLxlrc,
})
if (isLock != desktopLyric.isLock) {

View File

@ -28,6 +28,7 @@ mainHandle(ipcWinLyricNames.get_lyric_config, async() => {
config: global.appSetting.desktopLyric,
languageId: global.appSetting.langId,
isShowLyricTranslation: global.appSetting.player.isShowLyricTranslation,
isShowLyricRoma: global.appSetting.player.isShowLyricRoma,
isPlayLxlrc: global.appSetting.player.isPlayLxlrc,
}
})

View File

@ -4,7 +4,7 @@
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
.control-bar(v-show="!lrcConfig.isLock")
core-control-bar(:lrcConfig="lrcConfig" :themes="themeList")
core-lyric(:lrcConfig="lrcConfig" :isPlayLxlrc="isPlayLxlrc" :isShowLyricTranslation="isShowLyricTranslation")
core-lyric(:lrcConfig="lrcConfig" :isPlayLxlrc="isPlayLxlrc" :isShowLyricTranslation="isShowLyricTranslation" :isShowLyricRoma="isShowLyricRoma")
div.resize-left(@mousedown.self="handleMouseDown('left', $event)" @touchstart.self="handleTouchDown('left', $event)")
div.resize-top(@mousedown.self="handleMouseDown('top', $event)" @touchstart.self="handleTouchDown('top', $event)")
div.resize-right(@mousedown.self="handleMouseDown('right', $event)" @touchstart.self="handleTouchDown('right', $event)")
@ -46,7 +46,8 @@ export default {
isZoomActiveLrc: true,
},
},
isShowLyricTranslation: true,
isShowLyricTranslation: false,
isShowLyricRoma: false,
isPlayLxlrc: true,
themeList: [
{
@ -111,9 +112,10 @@ export default {
document.removeEventListener('mouseup', this.handleMouseUp)
},
methods: {
handleUpdateConfig({ config, languageId, isShowLyricTranslation, isPlayLxlrc }) {
handleUpdateConfig({ config, languageId, isShowLyricTranslation, isShowLyricRoma, isPlayLxlrc }) {
this.lrcConfig = config
this.isShowLyricTranslation = isShowLyricTranslation
this.isShowLyricRoma = isShowLyricRoma
this.isPlayLxlrc = isPlayLxlrc
if (this.$i18n.locale !== languageId && languageId != null) this.$i18n.locale = languageId
},

View File

@ -40,6 +40,10 @@ export default {
type: Boolean,
default: true,
},
isShowLyricRoma: {
type: Boolean,
default: true,
},
},
data() {
return {
@ -74,6 +78,7 @@ export default {
lyrics: {
lyric: '',
tlyric: '',
rlyric: '',
lxlyric: '',
},
}
@ -143,6 +148,10 @@ export default {
this.setLyric()
rendererSend(NAMES.winLyric.get_lyric_info, 'status')
},
isShowLyricRoma() {
this.setLyric()
rendererSend(NAMES.winLyric.get_lyric_info, 'status')
},
isPlayLxlrc() {
this.setLyric()
rendererSend(NAMES.winLyric.get_lyric_info, 'status')
@ -195,6 +204,7 @@ export default {
case 'lyric':
this.lyrics.lyric = data.lrc
this.lyrics.tlyric = data.tlrc
this.lyrics.rlyric = data.rlrc
this.lyrics.lxlyric = data.lxlrc
this.setLyric()
break
@ -211,12 +221,14 @@ export default {
this.lyrics.lyric = ''
this.lyrics.tlyric = ''
this.lyrics.lxlyric = ''
this.lyrics.rlyric = ''
this.setLyric()
break
case 'info':
// console.log('info', data)
this.lyrics.lyric = data.lrc
this.lyrics.tlyric = data.tlrc
this.lyrics.rlyric = data.rlrc
this.lyrics.lxlyric = data.lxlrc
this.setLyric()
this.$nextTick(() => {
@ -343,10 +355,12 @@ export default {
rendererSend(NAMES.winLyric.close)
},
setLyric() {
const extendedLyrics = []
if (this.isShowLyricTranslation && this.lyrics.tlyric) extendedLyrics.push(this.lyrics.tlyric)
if (this.isShowLyricRoma && this.lyrics.rlyric) extendedLyrics.push(this.lyrics.rlyric)
window.lrc.setLyric(
this.isPlayLxlrc && this.lyrics.lxlyric ? this.lyrics.lxlyric : this.lyrics.lyric,
this.isShowLyricTranslation && this.lyrics.tlyric ? this.lyrics.tlyric : '',
// (this.isShowLyricTranslation && this.lyrics.tlyric ? (this.lyrics.tlyric + '\n') : '') + (this.lyrics.lyric || ''),
extendedLyrics,
)
},
},
@ -375,11 +389,11 @@ export default {
display: inline-block;
}
.font, .translation {
.font, .extended {
cursor: grab;
}
.translation {
.extended {
transition: @transition-theme !important;
transition-property: font-size, color;
font-size: 0.8em;
@ -396,7 +410,7 @@ export default {
.line {
color: @color-theme;
}
.translation {
.extended {
color: @color-theme;
}
// span {
@ -465,7 +479,7 @@ export default {
.draging {
:global {
.lrc-content {
.font, .translation {
.font, .extended {
cursor: grabbing;
}
}
@ -475,7 +489,7 @@ export default {
:global {
.lrc-content {
&.active {
.translation {
.extended {
font-size: .94em;
}
span {
@ -508,7 +522,7 @@ each(@themes, {
:global {
.lrc-content {
&.active {
.translation {
.extended {
color: ~'@{color-@{value}-theme}';
}
.line {

View File

@ -14,41 +14,13 @@
</template>
<script>
import { useRefGetter, watch, onMounted } from '@renderer/utils/vueTools'
import { onMounted } from '@renderer/utils/vueTools'
import useApp from '@renderer/core/useApp'
import { isFullscreen } from '@renderer/core/share'
import { getFontSizeWithScreen } from '@renderer/utils'
export default {
setup() {
const theme = useRefGetter('theme')
const font = useRefGetter('font')
const windowSizeActive = useRefGetter('windowSizeActive')
const dom_root = document.getElementById('root')
watch(theme, (val) => {
dom_root.className = val
})
watch(font, (val) => {
document.documentElement.style.fontFamily = val
}, {
immediate: true,
})
watch(isFullscreen, val => {
if (val) {
document.body.classList.remove(window.dt ? 'disableTransparent' : 'transparent')
document.body.classList.add('fullscreen')
document.documentElement.style.fontSize = getFontSizeWithScreen(window.screen.width) + 'px'
} else {
document.body.classList.remove('fullscreen')
document.body.classList.add(window.dt ? 'disableTransparent' : 'transparent')
document.documentElement.style.fontSize = windowSizeActive.value.fontSize
}
}, {
immediate: true,
})
useApp()
onMounted(() => {
@ -124,9 +96,9 @@ body {
border-bottom-left-radius: 0;
}
#view { // 5px
margin-right: 5Px;
}
// #view { // 5px
// margin-right: 5Px;
// }
}
.fullscreen {
background-color: #fff;

View File

@ -55,6 +55,7 @@ export default {
},
getTypeName(type) {
switch (type) {
case 'flac32bit':
case 'flac':
case 'ape':
case 'wav':

View File

@ -5,6 +5,7 @@
<base-btn :class="$style.btn" @click="handleClick('128k')">{{$t('download__normal')}} - 128K</base-btn>
<base-btn :class="$style.btn" @click="handleClick('320k')">{{$t('download__high_quality')}} - 320K</base-btn>
<base-btn :class="$style.btn" @click="handleClick('flac')">{{$t('download__lossless')}} - FLAC</base-btn>
<base-btn :class="$style.btn" @click="handleClick('flac32bit')">{{$t('download__lossless')}} - FLAC 24bit</base-btn>
</main>
</material-modal>
</template>

View File

@ -68,7 +68,7 @@ export default {
}
return {
setting: setting,
setting,
isShowAddMusicTo,
nextTogglePlayName,
toggleNextPlayMode,

View File

@ -27,8 +27,10 @@
<div :class="[$style.lyricSelectContent, 'select', 'scroll', 'lyricSelectContent']" v-if="isShowLrcSelectContent" @contextmenu="handleCopySelectText">
<div v-for="(info, index) in lyric.lines" :key="index" :class="[$style.lyricSelectline, { [$style.lrcActive]: lyric.line == index }]">
<span>{{info.text}}</span>
<br v-if="info.translation"/>
<span :class="$style.lyricSelectlineTransition">{{info.translation}}</span>
<template v-for="(lrc, index) in info.extendedLyrics" :key="index">
<br />
<span :class="$style.lyricSelectlineExtended">{{lrc}}</span>
</template>
</div>
</div>
</transition>
@ -77,13 +79,17 @@ export default {
const lyricInfo = reactive({
lyric: '',
tlyric: '',
rlyric: '',
lxlyric: '',
rawlyric: '',
musicInfo: null,
})
const updateMusicInfo = () => {
lyricInfo.lyric = playerMusicInfo.lrc
lyricInfo.tlyric = playerMusicInfo.tlrc
lyricInfo.rlyric = playerMusicInfo.rlrc
lyricInfo.lxlyric = playerMusicInfo.lxlrc
lyricInfo.rawlyric = playerMusicInfo.rawlrc
lyricInfo.musicInfo = musicInfoItem.value
}
const handleShowLyricMenu = event => {
@ -92,13 +98,14 @@ export default {
lyricMenuXY.y = event.pageY
lyricMenuVisible.value = true
}
const handleUpdateLyric = ({ lyric, tlyric, lxlyric, offset }) => {
const handleUpdateLyric = ({ lyric, tlyric, rlyric, lxlyric, offset }) => {
setMusicInfo({
lrc: lyric,
tlrc: tlyric,
rlrc: rlyric,
lxlrc: lxlyric,
})
console.log(offset)
// console.log(offset)
window.eventHub.emit(eventPlayerNames.updateLyricOffset, offset)
}
@ -181,13 +188,13 @@ export default {
:global {
.lrc-content {
line-height: 1.2;
padding: calc(var(--playDetail-lrc-font-size, 16px) / 2) 0;
padding: calc(var(--playDetail-lrc-font-size, 16px) / 2) 1px;
overflow-wrap: break-word;
color: @color-player-detail-lyric;
transition: @transition-theme;
transition-property: padding;
.translation {
.extended {
transition: @transition-theme !important;
transition-property: font-size, color;
font-size: .9em;
@ -203,7 +210,7 @@ export default {
.line {
color: @color-theme;
}
.translation {
.extended {
color: @color-theme;
}
// span {
@ -241,7 +248,7 @@ export default {
:global {
.lrc-content {
&.active {
.translation {
.extended {
font-size: .94em;
}
span {
@ -314,7 +321,7 @@ export default {
transition-property: color, font-size;
line-height: 1.3;
}
.lyricSelectlineTransition {
.lyricSelectlineExtended {
font-size: 14px;
}
.lrc-active {
@ -334,7 +341,7 @@ each(@themes, {
color: ~'@{color-@{value}-player-detail-lyric}';
&.active {
.translation {
.extended {
color: ~'@{color-@{value}-player-detail-lyric-active}';
}
.line {

View File

@ -29,7 +29,7 @@
<div :class="$style.group">
<div :class="$style.subGroup">
<div :class="$style.title">{{$t('lyric_menu__offset', { offset })}}</div>
<button :class="[$style.btn, $style.titleBtn]" :disabled="offsetDisabled || !offset" @click="offsetReset">{{$t('lyric_menu__offset_reset')}}</button>
<button :class="[$style.btn, $style.titleBtn]" :disabled="offsetDisabled || offset == originOffset" @click="offsetReset">{{$t('lyric_menu__offset_reset')}}</button>
</div>
<div :class="$style.subGroup">
<button :class="$style.btn" :disabled="offsetDisabled" @click="setOffset(10)" ignore-tip :aria-label="$t('lyric_menu__offset_add_10')">+ 10ms</button>
@ -59,6 +59,15 @@ 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: {
@ -75,6 +84,7 @@ export default {
const offset = ref(0)
const offsetDisabled = ref(true)
const originOffset = ref(0)
const visible = computed(() => props.modelValue)
const musicInfo = computed(() => props.lyricInfo.musicInfo)
@ -104,28 +114,35 @@ export default {
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) {
if (offset == originOffset.value) {
removeLyric(props.lyricInfo.musicInfo)
} else {
saveLyric(props.lyricInfo.musicInfo, {
lyric,
tlyric,
rlyric,
lxlyric,
})
} else removeLyric(props.lyricInfo.musicInfo)
}
emit('updateLyric', {
lyric,
tlyric,
rlyric,
lxlyric,
offset,
})
@ -135,25 +152,15 @@ export default {
updateLyric(offset.value)
}
const offsetReset = () => {
if (!offset.value) return
offset.value = 0
updateLyric(0)
if (offset.value == originOffset.value) return
offset.value = originOffset.value
updateLyric(originOffset.value)
}
const parseLrcOffset = () => {
let lrcOffset
if (props.lyricInfo.lyric) {
lrcOffset = offsetTagRxp.exec(props.lyricInfo.lyric)
if (lrcOffset) {
lrcOffset = parseInt(lrcOffset[1])
if (Number.isNaN(lrcOffset)) lrcOffset = 0
} else lrcOffset = 0
offsetDisabled.value = false
} else {
offsetDisabled.value = true
lrcOffset = 0
}
offset.value = lrcOffset
offset.value = getOffset(props.lyricInfo.lyric)
originOffset.value = getOffset(props.lyricInfo.rawlyric)
offsetDisabled.value = !props.lyricInfo.lyric
}
@ -177,6 +184,7 @@ export default {
menuStyles,
playDetailSetting,
offset,
originOffset,
fontSizeUp,
fontSizeDown,
fontSizeReset,

View File

@ -119,6 +119,7 @@ export default {
p {
line-height: 1.5;
word-break: break-all;
overflow-wrap: break-word;
}
}

View File

@ -20,9 +20,10 @@ div(:class="$style.songList")
div.list-item(@click="handleListItemClick($event, index)" @contextmenu="handleListItemRightClick($event, index)"
:class="[{ selected: rightClickSelectedIndex == index }, { active: selectedList.includes(item) }]")
div.list-item-cell.nobreak.center(:style="{ width: rowWidth.r1 }" style="padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}}
div.list-item-cell.auto(:style="{ width: rowWidth.r2 }" :aria-label="item.name + ((item._types.ape || item._types.flac || item._types.wav) ? ` - ${$t('tag__lossless')}` : item._types['320k'] ? ` - ${$t('tag__high_quality')}` : '')")
div.list-item-cell.auto(:style="{ width: rowWidth.r2 }" :aria-label="item.name + (item._types.flac32bit ? ` - ${$t('tag__lossless_24bit')}` : (item._types.ape || item._types.flac || item._types.wav) ? ` - ${$t('tag__lossless')}` : item._types['320k'] ? ` - ${$t('tag__high_quality')}` : '')")
span.select {{item.name}}
span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-if="item._types.ape || item._types.flac || item._types.wav") {{$t('tag__lossless')}}
span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-if="item._types.flac32bit") {{$t('tag__lossless_24bit')}}
span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types.ape || item._types.flac || item._types.wav") {{$t('tag__lossless')}}
span.badge.badge-theme-info(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types['320k']") {{$t('tag__high_quality')}}
div.list-item-cell(:style="{ width: rowWidth.r3 }" :aria-label="item.singer")
span.select {{item.singer}}

View File

@ -6,7 +6,9 @@ export const musicInfo = window.musicInfo = reactive({
img: null,
lrc: null,
tlrc: null,
rlrc: null,
lxlrc: null,
rawlrc: null,
url: null,
name: '',
singer: '',

View File

@ -1,4 +1,4 @@
import { openUrl } from '@renderer/utils'
import { openUrl, getFontSizeWithScreen } from '@renderer/utils'
import { base as eventBaseName } from '@renderer/event/names'
import { onSetConfig, onSystemThemeChange } from '@renderer/utils/tools'
import { isFullscreen, themeShouldUseDarkColors } from '@renderer/core/share'
@ -14,9 +14,18 @@ import {
const handle_key_esc_down = ({ event }) => {
if (event.repeat) return
if (event.target.tagName != 'INPUT' || event.target.classList.contains('ignore-esc')) return
if (event.target.tagName != 'INPUT' || event.target.classList.contains('ignore-esc')) {
if (isFullscreen.value) {
event.lx_handled = true
rendererInvoke(NAMES.mainWindow.fullscreen, false).then(fullscreen => {
isFullscreen.value = fullscreen
})
}
return
}
event.target.value = ''
event.target.blur()
event.lx_handled = true
}
const handleBodyClick = event => {
if (event.target.tagName != 'A') return
@ -44,10 +53,37 @@ export default ({
isProd,
isLinux,
}) => {
const setSetting = useCommit('setSetting')
const theme = useRefGetter('theme')
const font = useRefGetter('font')
const windowSizeActive = useRefGetter('windowSizeActive')
const setSetting = useCommit('setSetting')
const isShowAnimation = useRefGetter('isShowAnimation')
const dom_root = document.getElementById('root')
watch(theme, (val) => {
dom_root.className = val
})
watch(font, (val) => {
document.documentElement.style.fontFamily = val
}, {
immediate: true,
})
watch(isFullscreen, val => {
if (val) {
document.body.classList.remove(window.dt ? 'disableTransparent' : 'transparent')
document.body.classList.add('fullscreen')
document.documentElement.style.fontSize = getFontSizeWithScreen(window.screen.width) + 'px'
} else {
document.body.classList.remove('fullscreen')
document.body.classList.add(window.dt ? 'disableTransparent' : 'transparent')
document.documentElement.style.fontSize = windowSizeActive.value.fontSize
}
}, {
immediate: true,
})
watch(windowSizeActive, ({ fontSize }) => {
document.documentElement.style.fontSize = fontSize
})
@ -61,6 +97,8 @@ export default ({
document.body.classList.add('disableAnimation')
}
}
}, {
immediate: true,
})
const rSetConfig = onSetConfig((event, config) => {

View File

@ -39,7 +39,7 @@ export default ({ setting }) => {
promise: userApiRequest({
requestKey,
data: {
source: source,
source,
action: 'musicUrl',
info: {
type,

View File

@ -43,11 +43,14 @@ export default ({ setting }) => {
const setLyric = () => {
if (!musicInfo.songmid) return
const extendedLyrics = []
if (setting.value.player.isShowLyricTranslation && musicInfo.tlrc) extendedLyrics.push(musicInfo.tlrc)
if (setting.value.player.isShowLyricRoma && musicInfo.rlrc) extendedLyrics.push(musicInfo.rlrc)
lrc.setLyric(
setting.value.player.isPlayLxlrc && musicInfo.lxlrc ? musicInfo.lxlrc : musicInfo.lrc,
setting.value.player.isShowLyricTranslation && musicInfo.tlrc ? musicInfo.tlrc : '',
extendedLyrics,
)
setDesktopLyricInfo('lyric', { lrc: musicInfo.lrc, tlrc: musicInfo.tlrc, lxlrc: musicInfo.lxlrc })
setDesktopLyricInfo('lyric', { lrc: musicInfo.lrc, tlrc: musicInfo.tlrc, rlrc: musicInfo.rlrc, lxlrc: musicInfo.lxlrc })
if (isPlay.value && (musicInfo.url || playMusicInfo.listId == 'download')) {
setTimeout(() => {
@ -99,6 +102,7 @@ export default ({ setting }) => {
album: musicInfo.album,
lrc: musicInfo.lrc,
tlrc: musicInfo.tlrc,
rlrc: musicInfo.rlrc,
lxlrc: musicInfo.lxlrc,
isPlay: isPlay.value,
line: lyric.line,

View File

@ -91,8 +91,12 @@ export default ({
}
if (setting.value.player.autoSkipOnError) {
setAllStatus(t('player__error'))
addDelayNextTimeout()
if (document.hidden) {
playNext()
} else {
setAllStatus(t('player__error'))
setTimeout(addDelayNextTimeout)
}
}
}

View File

@ -135,21 +135,24 @@ export default ({ setting }) => {
}
}
const setLrc = (targetSong) => {
getLrc(targetSong).then(({ lyric, tlyric, lxlyric }) => {
getLrc(targetSong).then(({ lyric, tlyric, rlyric, lxlyric, rawInfo }) => {
if (targetSong.songmid !== musicInfo.songmid) return
return (
setting.value.player.isS2t
? Promise.all([
lyric ? langS2T(lyric) : Promise.resolve(''),
tlyric ? langS2T(tlyric) : Promise.resolve(''),
rlyric ? langS2T(rlyric) : Promise.resolve(''),
lxlyric ? langS2T(lxlyric) : Promise.resolve(''),
])
: Promise.resolve([lyric, tlyric, lxlyric])
).then(([lyric, tlyric, lxlyric]) => {
: Promise.resolve([lyric, tlyric, rlyric, lxlyric])
).then(([lyric, tlyric, rlyric, lxlyric]) => {
setMusicInfo({
lrc: lyric,
tlrc: tlyric,
rlrc: rlyric,
lxlrc: lxlyric,
rawlrc: rawInfo.lyric,
})
})
}).catch((err) => {
@ -212,7 +215,9 @@ export default ({ setting }) => {
img: null,
lrc: null,
tlrc: null,
rlrc: null,
lxlrc: null,
rawlrc: null,
url: null,
name: '',
singer: '',
@ -223,6 +228,7 @@ export default ({ setting }) => {
// 播放音乐
const playMusic = async() => {
// console.log('playMusic')
isGettingUrl = false
setStopStatus()
if (window.restorePlayInfo) {
handleRestorePlay(window.restorePlayInfo)

View File

@ -32,8 +32,9 @@ export default () => {
})
const rOnError = onError(() => {
// console.log('onError')
window.eventHub.emit(player.player_error, getErrorCode())
window.eventHub.emit(player.error)
const errorCode = getErrorCode()
window.eventHub.emit(player.player_error, errorCode)
window.eventHub.emit(player.error, errorCode)
})
const rOnLoadeddata = onLoadeddata(() => {
// console.log('onLoadeddata')

View File

@ -10,7 +10,7 @@ function route(path, view, name, meta, props) {
path,
meta,
props,
component: (resovle) => import(`../views/${view}.vue`).then(resovle),
component: require(`../views/${view}.vue`).default,
}
}

View File

@ -69,6 +69,7 @@ const getExt = type => {
case 'ape':
return 'ape'
case 'flac':
case 'flac32bit':
return 'flac'
case 'wav':
return 'wav'
@ -185,14 +186,14 @@ const getLyric = function(musicInfo, isUseOtherSource, isS2t) {
return getLyricFromStorage(musicInfo).then(lrcInfo => {
return (
existTimeExp.test(lrcInfo.lyric)
? Promise.resolve({ lyric: lrcInfo.lyric, tlyric: lrcInfo.tlyric || '' })
? Promise.resolve({ lyric: lrcInfo.lyric, tlyric: lrcInfo.tlyric || '', rlyric: lrcInfo.rlyric || '', lxlyric: lrcInfo.lxlyric || '' })
: (
isUseOtherSource
? handleGetLyric.call(this, musicInfo)
: music[musicInfo.source].getLyric(musicInfo).promise
).then(({ lyric, tlyric, lxlyric }) => {
setLyric(musicInfo, { lyric, tlyric, lxlyric })
return { lyric, tlyric, lxlyric }
).then(({ lyric, tlyric, rlyric, lxlyric }) => {
setLyric(musicInfo, { lyric, tlyric, rlyric, lxlyric })
return { lyric, tlyric, rlyric, lxlyric }
}).catch(err => {
console.log(err)
return null
@ -519,7 +520,8 @@ const actions = {
delete dls[item.key]
}
commit('removeTask', item)
if (item.status != downloadStatus.COMPLETED) {
// 没有未完成、已下载大于1k
if (item.status != downloadStatus.COMPLETED && item.progress.total && item.progress.downloaded > 1024) {
try {
await deleteFile(item.metadata.filePath)
} catch (_) {}
@ -541,7 +543,8 @@ const actions = {
delete dls[item.key]
}
}
if (item.status != downloadStatus.COMPLETED) {
// 没有未完成、已下载大于1k
if (item.status != downloadStatus.COMPLETED && item.progress.total && item.progress.downloaded > 1024) {
deleteFile(item.metadata.filePath).catch(_ => _)
}
}

View File

@ -4,6 +4,7 @@ import {
getRandom,
checkPath,
getLyric as getStoreLyric,
getLyricRaw as getStoreLyricRaw,
setLyric,
setMusicUrl,
getMusicUrl as getStoreMusicUrl,
@ -177,6 +178,14 @@ const getLyric = function(musicInfo, retryedSource = [], originMusic) {
})
}
const buildLyricInfo = async(lyricInfo, musicInfo) => {
const lyricRawInfo = await getStoreLyricRaw(musicInfo)
return {
...lyricInfo,
rawInfo: lyricRawInfo,
}
}
// getters
const getters = {
@ -214,7 +223,7 @@ const actions = {
},
async getLrc({ commit, state }, musicInfo) {
const lrcInfo = await getStoreLyric(musicInfo)
// lrcInfo = {}
// let lrcInfo = {}
// if (lrcRequest && lrcRequest.cancelHttp) lrcRequest.cancelHttp()
if (existTimeExp.test(lrcInfo.lyric) && lrcInfo.tlyric != null) {
// if (musicInfo.lrc.startsWith('\ufeff[id:$00000000]')) {
@ -231,16 +240,18 @@ const actions = {
case 'kw':
break
default:
return lrcInfo
return buildLyricInfo(lrcInfo, musicInfo)
}
} else return lrcInfo
} else if (lrcInfo.rlyric == null) {
if (musicInfo.source != 'wy') return buildLyricInfo(lrcInfo, musicInfo)
} else return buildLyricInfo(lrcInfo, musicInfo)
}
// lrcRequest = music[musicInfo.source].getLyric(musicInfo)
return getLyric.call(this, musicInfo).then(({ lyric, tlyric, lxlyric }) => {
return getLyric.call(this, musicInfo).then(({ lyric, tlyric, rlyric, lxlyric }) => {
// lrcRequest = null
commit('setLrc', { musicInfo, lyric, tlyric, lxlyric })
return { lyric, tlyric, lxlyric }
commit('setLrc', { musicInfo, lyric, tlyric, rlyric, lxlyric })
return buildLyricInfo({ lyric, tlyric, rlyric, lxlyric }, musicInfo)
}).catch(err => {
// lrcRequest = null
return Promise.reject(err)
@ -434,6 +445,7 @@ const mutations = {
setLyric(datas.musicInfo, {
lyric: datas.lyric,
tlyric: datas.tlyric,
rlyric: datas.rlyric,
lxlyric: datas.lxlyric,
})
},

View File

@ -151,6 +151,7 @@ const mutations = {
state.text = text
},
setList(state, datas) {
if (!state.text) return
let source = state.sourceList[datas.source]
datas.list = deduplicationList(datas.list)
source.list = markRawList(datas.list)
@ -160,6 +161,7 @@ const mutations = {
source.limit = datas.limit
},
setLists(state, { results, page }) {
if (!state.text) return
let pages = []
let total = 0
let limit = 0

View File

@ -65,6 +65,8 @@ const getters = {
},
}
let loadId = null
// actions
const actions = {
getTags({ state, rootState, commit }) {
@ -76,11 +78,14 @@ const actions = {
let tabId = rootState.setting.songList.tagInfo.id
let sortId = rootState.setting.songList.sortId
// console.log(sortId)
let key = `slist__${source}__${sortId}__${tabId}__${page}`
let key = loadId = `slist__${source}__${sortId}__${tabId}__${page}`
if (state.list.list.length && state.list.key == key) return
if (cache.has(key)) return Promise.resolve(cache.get(key)).then(result => commit('setList', { result, key, page }))
commit('clearList')
return music[source]?.songList.getList(sortId, tabId, page).then(result => commit('setList', { result, key, page }))
return music[source]?.songList.getList(sortId, tabId, page).then(result => {
if (loadId != key) return
commit('setList', { result, key, page })
})
},
getListDetail({ state, commit }, { id, source, page, isRefresh = false }) {
let key = `sdetail__${source}__${id}__${page}`

View File

@ -513,13 +513,14 @@ export const parseUrlParams = str => {
}
export const getLyric = musicInfo => rendererInvoke(NAMES.mainWindow.get_lyric, `${musicInfo.source}_${musicInfo.songmid}`)
export const setLyric = (musicInfo, { lyric, tlyric, lxlyric }) => rendererSend(NAMES.mainWindow.save_lyric_raw, {
export const getLyricRaw = musicInfo => rendererInvoke(NAMES.mainWindow.get_lyric_raw, `${musicInfo.source}_${musicInfo.songmid}`)
export const setLyric = (musicInfo, { lyric, tlyric, rlyric, lxlyric }) => rendererSend(NAMES.mainWindow.save_lyric_raw, {
id: `${musicInfo.source}_${musicInfo.songmid}`,
lyrics: { lyric, tlyric, lxlyric },
lyrics: { lyric, tlyric, rlyric, lxlyric },
})
export const setLyricEdited = (musicInfo, { lyric, tlyric, lxlyric }) => rendererSend(NAMES.mainWindow.save_lyric_edited, {
export const setLyricEdited = (musicInfo, { lyric, tlyric, rlyric, lxlyric }) => rendererSend(NAMES.mainWindow.save_lyric_edited, {
id: `${musicInfo.source}_${musicInfo.songmid}`,
lyrics: { lyric, tlyric, lxlyric },
lyrics: { lyric, tlyric, rlyric, lxlyric },
})
export const removeLyricEdited = musicInfo => rendererSend(NAMES.mainWindow.remove_lyric_edited, `${musicInfo.source}_${musicInfo.songmid}`)

View File

@ -20,14 +20,14 @@ const createAnimation = (dom, duration) => new window.Animation(new window.Keyfr
// https://jsfiddle.net/ceqpnbky/1/
module.exports = class FontPlayer {
constructor({ time = 0, lyric = '', translationLyric = '', lineClassName = '', fontClassName = '', translationClassName = '', lineModeClassName = '', shadowContent = false, shadowClassName = '' }) {
constructor({ time = 0, lyric = '', extendedLyrics = '', lineClassName = '', fontClassName = '', extendedLrcClassName = '', lineModeClassName = '', shadowContent = false, shadowClassName = '' }) {
this.time = time
this.lyric = lyric
this.translationLyric = translationLyric
this.extendedLyrics = extendedLyrics
this.lineClassName = lineClassName
this.fontClassName = fontClassName
this.translationClassName = translationClassName
this.extendedLrcClassName = extendedLrcClassName
this.lineModeClassName = lineModeClassName
this.shadowContent = shadowContent
this.shadowClassName = shadowClassName
@ -40,8 +40,8 @@ module.exports = class FontPlayer {
this.fontContent = null
this.timeoutTools = new TimeoutTools()
this.waitPlayTimeout = new TimeoutTools()
this.timeoutTools = new TimeoutTools(80)
this.waitPlayTimeout = new TimeoutTools(80)
this._init()
}
@ -64,20 +64,20 @@ module.exports = class FontPlayer {
this.fontContent.appendChild(this.fontShadowContent)
}
this.lineContent.appendChild(this.fontContent)
if (this.translationLyric) {
this.translationContent = document.createElement('div')
this.translationContent.style = 'position:relative;display:inline-block;'
this.translationContent.className = this.translationClassName
this.translationContent.textContent = this.translationLyric
for (const lrc of this.extendedLyrics) {
const extendedLrcContent = document.createElement('div')
extendedLrcContent.style = 'position:relative;display:inline-block;'
extendedLrcContent.className = this.extendedLrcClassName
extendedLrcContent.textContent = lrc
this.lineContent.appendChild(document.createElement('br'))
this.lineContent.appendChild(this.translationContent)
this.lineContent.appendChild(extendedLrcContent)
if (this.shadowContent) {
this.translationShadowContent = document.createElement('div')
this.translationShadowContent.style = 'position:absolute;top:0;left:0;width:100%;z-index:-1;'
this.translationShadowContent.className = this.shadowClassName
this.translationShadowContent.textContent = this.translationLyric
this.translationContent.appendChild(this.translationShadowContent)
const extendedLrcShadowContent = document.createElement('div')
extendedLrcShadowContent.style = 'position:absolute;top:0;left:0;width:100%;z-index:-1;'
extendedLrcShadowContent.className = this.shadowClassName
extendedLrcShadowContent.textContent = lrc
extendedLrcContent.appendChild(extendedLrcShadowContent)
}
}
this._parseLyric()

View File

@ -6,11 +6,11 @@ const fontTimeExp = /<(\d+),(\d+)>/g
module.exports = class Lyric {
constructor({
lyric = '',
translationLyric = '',
extendedLyrics = [],
offset = 0,
lineClassName = '',
fontClassName = 'font',
translationClassName = 'translation',
extendedLrcClassName = 'extended',
activeLineClassName = 'active',
lineModeClassName = 'line',
shadowClassName = '',
@ -19,14 +19,14 @@ module.exports = class Lyric {
onSetLyric = function() { },
}) {
this.lyric = lyric
this.translationLyric = translationLyric
this.extendedLyrics = extendedLyrics
this.offset = offset
this.onPlay = onPlay
this.onSetLyric = onSetLyric
this.lineClassName = lineClassName
this.fontClassName = fontClassName
this.translationClassName = translationClassName
this.extendedLrcClassName = extendedLrcClassName
this.activeLineClassName = activeLineClassName
this.lineModeClassName = lineModeClassName
this.shadowClassName = shadowClassName
@ -46,7 +46,7 @@ module.exports = class Lyric {
this.playingLineNum = -1
this.isLineMode = false
this.linePlayer.setLyric(this.lyric, this.translationLyric)
this.linePlayer.setLyric(this.lyric, this.extendedLyrics)
}
_handleLinePlayerOnPlay = (num, text, curTime) => {
@ -104,10 +104,10 @@ module.exports = class Lyric {
const fontPlayer = new FontPlayer({
time: line.time,
lyric: line.text,
translationLyric: line.translation,
extendedLyrics: line.extendedLyrics,
lineClassName: this.lineClassName,
fontClassName: this.fontClassName,
translationClassName: this.translationClassName,
extendedLrcClassName: this.extendedLrcClassName,
lineModeClassName: this.lineModeClassName,
shadowClassName: this.shadowClassName,
shadowContent: this.shadowContent,
@ -117,7 +117,7 @@ module.exports = class Lyric {
return {
text: line.text,
time: line.time,
translation: line.translation,
extendedLyrics: line.extendedLyrics,
dom_line: fontPlayer.lineContent,
}
})
@ -126,10 +126,10 @@ module.exports = class Lyric {
const fontPlayer = new FontPlayer({
time: line.time,
lyric: line.text,
translationLyric: line.translation,
extendedLyrics: line.extendedLyrics,
lineClassName: this.lineClassName,
fontClassName: this.fontClassName,
translationClassName: this.translationClassName,
extendedLrcClassName: this.extendedLrcClassName,
shadowClassName: this.shadowClassName,
shadowContent: this.shadowContent,
})
@ -138,7 +138,7 @@ module.exports = class Lyric {
return {
text: line.text.replace(fontTimeExp, ''),
time: line.time,
translation: line.translation,
extendedLyrics: line.extendedLyrics,
dom_line: fontPlayer.lineContent,
}
})
@ -162,9 +162,9 @@ module.exports = class Lyric {
if (this.playingLineNum > -1) this._lineFonts[this.playingLineNum].pause()
}
setLyric(lyric, translationLyric) {
setLyric(lyric, extendedLyrics) {
this.lyric = lyric
this.translationLyric = translationLyric
this.extendedLyrics = extendedLyrics
this._init()
}
}

View File

@ -11,11 +11,26 @@ const tagRegMap = {
const timeoutTools = new TimeoutTools()
const parseExtendedLyric = (lrcLinesMap, extendedLyric) => {
const extendedLines = extendedLyric.split(/\r\n|\n|\r/)
for (let i = 0; i < extendedLines.length; i++) {
const line = extendedLines[i].trim()
let result = timeExp.exec(line)
if (result) {
const text = line.replace(timeExp, '').trim()
if (text) {
const timeStr = RegExp.$1.replace(/(\.\d\d)0$/, '$1')
const targetLine = lrcLinesMap[timeStr]
if (targetLine) targetLine.extendedLyrics.push(text)
}
}
}
}
module.exports = class LinePlayer {
constructor({ offset = 0, onPlay = function() { }, onSetLyric = function() { } } = {}) {
this.tags = {}
this.lines = null
this.translationLines = null
this.onPlay = onPlay
this.onSetLyric = onSetLyric
this.isPlay = false
@ -28,7 +43,7 @@ module.exports = class LinePlayer {
_init() {
if (this.lyric == null) this.lyric = ''
if (this.translationLyric == null) this.translationLyric = ''
if (this.extendedLyrics == null) this.extendedLyrics = []
this._initTag()
this._initLines()
this.onSetLyric(this.lines, this.tags.offset + this.offset)
@ -50,17 +65,15 @@ module.exports = class LinePlayer {
_initLines() {
this.lines = []
this.translationLines = []
const lines = this.lyric.split(/\r\n|\r|\n/)
const linesMap = {}
// const translationLines = this.translationLyric.split('\n')
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim()
let result = timeExp.exec(line)
if (result) {
const text = line.replace(timeExp, '').trim()
if (text) {
const timeStr = RegExp.$1
const timeStr = RegExp.$1.replace(/(\.\d\d)0$/, '$1')
const timeArr = timeStr.split(':')
if (timeArr.length < 3) timeArr.unshift(0)
if (timeArr[2].indexOf('.') > -1) {
@ -70,24 +83,13 @@ module.exports = class LinePlayer {
linesMap[timeStr] = {
time: parseInt(timeArr[0]) * 60 * 60 * 1000 + parseInt(timeArr[1]) * 60 * 1000 + parseInt(timeArr[2]) * 1000 + parseInt(timeArr[3] || 0),
text,
extendedLyrics: [],
}
}
}
}
const translationLines = this.translationLyric.split('\n')
for (let i = 0; i < translationLines.length; i++) {
const line = translationLines[i].trim()
let result = timeExp.exec(line)
if (result) {
const text = line.replace(timeExp, '').trim()
if (text) {
const timeStr = RegExp.$1
const targetLine = linesMap[timeStr]
if (targetLine) targetLine.translation = text
}
}
}
for (const lrc of this.extendedLyrics) parseExtendedLyric(linesMap, lrc)
this.lines = Object.values(linesMap)
this.lines.sort((a, b) => {
return a.time - b.time
@ -172,11 +174,11 @@ module.exports = class LinePlayer {
}
}
setLyric(lyric, translationLyric) {
// console.log(translationLyric)
setLyric(lyric, extendedLyrics) {
// console.log(extendedLyrics)
if (this.isPlay) this.pause()
this.lyric = lyric
this.translationLyric = translationLyric
this.extendedLyrics = extendedLyrics
this._init()
}
}

View File

@ -2,12 +2,12 @@
const getNow = exports.getNow = typeof performance == 'object' && window.performance.now ? window.performance.now.bind(window.performance) : Date.now.bind(Date)
exports.TimeoutTools = class TimeoutTools {
constructor() {
constructor(thresholdTime = 200) {
this.invokeTime = 0
this.animationFrameId = null
this.timeoutId = null
this.callback = null
this.thresholdTime = 200
this.thresholdTime = thresholdTime
}
run() {

View File

@ -79,11 +79,9 @@ export default {
item: /data-song="({.+?})"/g,
info: /{total[\s:]+"(\d+)", size[\s:]+"(\d+)", page[\s:]+"(\d+)"}/,
},
requestObj: null,
getData(url) {
if (this.requestObj) this.requestObj.cancelHttp()
this.requestObj = httpFetch(url)
return this.requestObj.promise
const requestObj = httpFetch(url)
return requestObj.promise
},
filterData(rawList) {
// console.log(rawList)

View File

@ -5,15 +5,13 @@ import { formatPlayTime } from '../../index'
// import { debug } from '../../utils/env'
// import { formatSinger } from './util'
let searchRequest
export default {
limit: 30,
total: 0,
page: 0,
allPage: 1,
musicSearch(str, page, limit) {
if (searchRequest && searchRequest.cancelHttp) searchRequest.cancelHttp()
searchRequest = httpFetch(`http://tingapi.ting.baidu.com/v1/restserver/ting?from=android&version=5.6.5.6&method=baidu.ting.search.merge&format=json&query=${encodeURIComponent(str)}&page_no=${page}&page_size=${limit}&type=0&data_source=0&use_cluster=1`)
const searchRequest = httpFetch(`http://tingapi.ting.baidu.com/v1/restserver/ting?from=android&version=5.6.5.6&method=baidu.ting.search.merge&format=json&query=${encodeURIComponent(str)}&page_no=${page}&page_size=${limit}&type=0&data_source=0&use_cluster=1`)
return searchRequest.promise.then(({ body }) => body)
},
handleResult(rawData) {
@ -81,7 +79,7 @@ export default {
return Promise.resolve({
list,
allPage: this.allPage,
limit: limit,
limit,
total: this.total,
source: 'bd',
})

View File

@ -6,7 +6,6 @@ export default {
_requestObj_tags: null,
_requestObj_list: null,
_requestObj_listRecommend: null,
_requestObj_listDetail: null,
limit_list: 30,
limit_song: 10000,
successCode: 22000,
@ -66,7 +65,7 @@ export default {
},
}
let encrypted = CryptoJS.AES.encrypt(strData, key, {
iv: iv,
iv,
blockSize: 16,
mode: CryptoJS.mode.CBC,
format: JsonFormatter,
@ -74,9 +73,9 @@ export default {
let ciphertext = encrypted.toString().ct
let sign = toMD5('baidu_taihe_music' + ciphertext + timestamp)
let jsonRet = {
timestamp: timestamp,
timestamp,
param: ciphertext,
sign: sign,
sign,
}
return jsonRet
},
@ -188,13 +187,12 @@ export default {
// 获取歌曲列表内的音乐
getListDetail(id, page, tryNum = 0) {
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1')
this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id, page))
return this._requestObj_listDetail.promise.then(({ body }) => {
const requestObj_listDetail = httpFetch(this.getListDetailUrl(id, page))
return requestObj_listDetail.promise.then(({ body }) => {
if (body.error_code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
let listData = this.filterData(body.result.songlist)
return {

View File

@ -71,16 +71,14 @@ export default {
listData: /global\.features = (\[.+\]);/,
},
_requestBoardsObj: null,
_requestDataObj: null,
getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch('http://mobilecdnbj.kugou.com/api/v3/rank/list?version=9108&plat=0&showtype=2&parentid=0&apiver=6&area_code=1&withsong=1')
return this._requestBoardsObj.promise
},
getData(url) {
if (this._requestDataObj) this._requestDataObj.cancelHttp()
this._requestDataObj = httpFetch(url)
return this._requestDataObj.promise
const requestDataObj = httpFetch(url)
return requestDataObj.promise
},
filterData(rawList) {
// console.log(rawList)

View File

@ -5,15 +5,13 @@ import { decodeName, formatPlayTime, sizeFormate } from '../../index'
// import { debug } from '../../utils/env'
// import { formatSinger } from './util'
let searchRequest
export default {
limit: 30,
total: 0,
page: 0,
allPage: 1,
musicSearch(str, page, limit) {
if (searchRequest && searchRequest.cancelHttp) searchRequest.cancelHttp()
searchRequest = httpFetch(`http://ioscdn.kugou.com/api/v3/search/song?keyword=${encodeURIComponent(str)}&page=${page}&pagesize=${limit}&showtype=10&plat=2&version=7910&tag=1&correct=1&privilege=1&sver=5`)
const searchRequest = httpFetch(`http://ioscdn.kugou.com/api/v3/search/song?keyword=${encodeURIComponent(str)}&page=${page}&pagesize=${limit}&showtype=10&plat=2&version=7910&tag=1&correct=1&privilege=1&sver=5`)
return searchRequest.promise.then(({ body }) => body)
},
filterData(rawData) {

View File

@ -18,7 +18,6 @@ export default {
_requestObj_listInfo: null,
_requestObj_list: null,
_requestObj_listRecommend: null,
_requestObj_listDetail: null,
listDetailLimit: 10000,
currentTagInfo: {
id: undefined,
@ -477,15 +476,14 @@ export default {
}
} else if (!link.includes('song.html')) return this.getUserListDetail3(link.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page)
}
if (this._requestObj_listDetailLink) this._requestObj_listDetailLink.cancelHttp()
this._requestObj_listDetailLink = httpFetch(link, {
const requestObj_listDetailLink = httpFetch(link, {
headers: {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
Referer: link,
},
})
const { headers: { location }, statusCode, body } = await this._requestObj_listDetailLink.promise
const { headers: { location }, statusCode, body } = await requestObj_listDetailLink.promise
// console.log(body, location)
if (statusCode > 400) return this.getUserListDetail(link, page, ++retryNum)
if (location) {
@ -512,7 +510,6 @@ export default {
},
getListDetail(id, page, tryNum = 0) { // 获取歌曲列表内的音乐
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
id = id.toString()
@ -528,8 +525,8 @@ export default {
// if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1')
this._requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id))
return this._requestObj_listDetail.promise.then(({ body }) => {
const requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id))
return requestObj_listDetail.promise.then(({ body }) => {
let listData = body.match(this.regExps.listData)
let listInfo = body.match(this.regExps.listInfo)
if (!listData) return this.getListDetail(id, page, ++tryNum)

View File

@ -3,7 +3,6 @@ import { decodeName } from '../../index'
import { formatSinger, objStr2JSON } from './util'
// let requestObj_list
let requestObj_listDetail
export default {
limit_list: 36,
limit_song: 1000,
@ -72,11 +71,8 @@ export default {
return num
},
getAlbumListDetail(id, page, retryNum = 0) {
if (requestObj_listDetail) {
requestObj_listDetail.cancelHttp()
}
if (retryNum > 2) return Promise.reject(new Error('try max num'))
requestObj_listDetail = httpFetch(`http://search.kuwo.cn/r.s?pn=${page - 1}&rn=${this.limit_song}&stype=albuminfo&albumid=${id}&show_copyright_off=0&encoding=utf&vipver=MUSIC_9.1.0`)
const requestObj_listDetail = httpFetch(`http://search.kuwo.cn/r.s?pn=${page - 1}&rn=${this.limit_song}&stype=albuminfo&albumid=${id}&show_copyright_off=0&encoding=utf&vipver=MUSIC_9.1.0`)
return requestObj_listDetail.promise.then(({ statusCode, body }) => {
if (statusCode !== 200) return this.getAlbumListDetail(id, page, ++retryNum)
body = objStr2JSON(body)

View File

@ -69,16 +69,14 @@ export default {
limit: 100,
_requestBoardsObj: null,
_requestDataObj: null,
getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch('http://qukudata.kuwo.cn/q.k?op=query&cont=tree&node=2&pn=0&rn=1000&fmt=json&level=2')
return this._requestBoardsObj.promise
},
getData(url) {
if (this._requestDataObj) this._requestDataObj.cancelHttp()
this._requestDataObj = httpFetch(url)
return this._requestDataObj.promise
const requestDataObj = httpFetch(url)
return requestDataObj.promise
},
filterData(rawList) {
// console.log(rawList)

View File

@ -93,6 +93,58 @@ const buildParams = (id, isGetLyricx) => {
const timeExp = /^\[([\d:.]*)\]{1}/g
const existTimeExp = /\[\d{1,2}:.*\d{1,4}\]/
export default {
/* sortLrcArr(arr) {
const lrcSet = new Set()
let lrc = []
let lrcT = []
let markIndex = []
for (const item of arr) {
if (lrcSet.has(item.time)) {
if (lrc.length < 2) continue
const index = lrc.findIndex(l => l.time == item.time)
markIndex.push(index)
if (index == lrc.length - 1) {
lrcT.push({ ...lrc[index], time: item.time })
lrc.push(item)
} else {
lrcT.push({ ...lrc[index], time: lrc[index + 1].time })
if (item.text) {
// const lastIndex = lrc.length - 1
// markIndex.push(lastIndex)
// lrcT.push({ ...lrc[lastIndex], time: lrc[lastIndex - 1].time })
lrc.push(item)
}
}
} else {
lrc.push(item)
lrcSet.add(item.time)
}
}
// console.log(markIndex)
markIndex = Array.from(new Set(markIndex))
for (let index = markIndex.length - 1; index >= 0; index--) {
lrc.splice(markIndex[index], 1)
}
// if (lrcT.length) {
// if (lrc.length * 0.4 < lrcT.length) { // 翻译数量需大于歌词数量的0.4倍,否则认为没有翻译
// const tItem = lrc.pop()
// tItem.time = lrc[lrc.length - 1].time
// lrcT.push(tItem)
// } else {
// lrc = arr
// lrcT = []
// }
// }
console.log(lrc, lrcT)
return {
lrc,
lrcT,
}
}, */
sortLrcArr(arr) {
const lrcSet = new Set()
let lrc = []
@ -111,15 +163,16 @@ export default {
}
}
if (lrcT.length) {
if (lrc.length * 0.4 < lrcT.length) { // 翻译数量需大于歌词数量的0.4倍,否则认为没有翻译
const tItem = lrc.pop()
tItem.time = lrc[lrc.length - 1].time
lrcT.push(tItem)
} else {
lrc = arr
lrcT = []
}
if (lrcT.length > lrc.length * 0.3) {
throw new Error('failed')
// if (lrc.length * 0.4 < lrcT.length) { // 翻译数量需大于歌词数量的0.4倍,否则认为没有翻译
// const tItem = lrc.pop()
// tItem.time = lrc[lrc.length - 1].time
// lrcT.push(tItem)
// } else {
// lrc = arr
// lrcT = []
// }
}
return {

View File

@ -9,16 +9,14 @@ export default {
regExps: {
mInfo: /bitrate:(\d+),format:(\w+),size:([\w.]+)/,
},
_musicSearchRequestObj: null,
limit: 30,
total: 0,
page: 0,
allPage: 1,
// cancelFn: null,
musicSearch(str, page, limit) {
if (this._musicSearchRequestObj) this._musicSearchRequestObj.cancelHttp()
this._musicSearchRequestObj = httpFetch(`http://search.kuwo.cn/r.s?client=kt&all=${encodeURIComponent(str)}&pn=${page - 1}&rn=${limit}&uid=794762570&ver=kwplayer_ar_9.2.2.1&vipver=1&show_copyright_off=1&newver=1&ft=music&cluster=0&strategy=2012&encoding=utf8&rformat=json&vermerge=1&mobi=1&issubtitle=1`)
return this._musicSearchRequestObj.promise
const musicSearchRequestObj = httpFetch(`http://search.kuwo.cn/r.s?client=kt&all=${encodeURIComponent(str)}&pn=${page - 1}&rn=${limit}&uid=794762570&ver=kwplayer_ar_9.2.2.1&vipver=1&show_copyright_off=1&newver=1&ft=music&cluster=0&strategy=2012&encoding=utf8&rformat=json&vermerge=1&mobi=1&issubtitle=1`)
return musicSearchRequestObj.promise
},
// getImg(songId) {
// return httpGet(`http://player.kuwo.cn/webmusic/sj/dtflagdate?flag=6&rid=MUSIC_${songId}`)

View File

@ -7,7 +7,6 @@ export default {
_requestObj_tags: null,
_requestObj_hotTags: null,
_requestObj_list: null,
_requestObj_listDetail: null,
limit_list: 36,
limit_song: 10000,
successCode: 200,
@ -165,13 +164,10 @@ export default {
},
getListDetailDigest8(id, page, tryNum = 0) {
if (this._requestObj_listDetail) {
this._requestObj_listDetail.cancelHttp()
}
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id, page))
return this._requestObj_listDetail.promise.then(({ body }) => {
const requestObj = httpFetch(this.getListDetailUrl(id, page))
return requestObj.promise.then(({ body }) => {
if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum)
return {
list: this.filterListDetail(body.musiclist),
@ -190,24 +186,18 @@ export default {
})
},
getListDetailDigest5Info(id, tryNum = 0) {
if (this._requestObj_listDetail) {
this._requestObj_listDetail.cancelHttp()
}
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listDetail = httpFetch(`http://qukudata.kuwo.cn/q.k?op=query&cont=ninfo&node=${id}&pn=0&rn=1&fmt=json&src=mbox&level=2`)
return this._requestObj_listDetail.promise.then(({ statusCode, body }) => {
const requestObj = httpFetch(`http://qukudata.kuwo.cn/q.k?op=query&cont=ninfo&node=${id}&pn=0&rn=1&fmt=json&src=mbox&level=2`)
return requestObj.promise.then(({ statusCode, body }) => {
if (statusCode != 200 || !body.child) return this.getListDetail(id, ++tryNum)
// console.log(body)
return body.child.length ? body.child[0].sourceid : null
})
},
getListDetailDigest5Music(id, page, tryNum = 0) {
if (this._requestObj_listDetail) {
this._requestObj_listDetail.cancelHttp()
}
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listDetail = httpFetch(`http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=${id}&pn=${page - 1}}&rn=${this.limit_song}&encode=utf-8&keyset=pl2012&identity=kuwo&pcmp4=1`)
return this._requestObj_listDetail.promise.then(({ body }) => {
const requestObj = httpFetch(`http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=${id}&pn=${page - 1}}&rn=${this.limit_song}&encode=utf-8&keyset=pl2012&identity=kuwo&pcmp4=1`)
return requestObj.promise.then(({ body }) => {
// console.log(body)
if (body.result !== 'ok') return this.getListDetail(id, page, ++tryNum)
return {

View File

@ -65,7 +65,6 @@ export default {
},
successCode: '000000',
requestBoardsObj: null,
requestObj: null,
getBoardsData() {
if (this.requestBoardsObj) this._requestBoardsObj.cancelHttp()
this.requestBoardsObj = httpFetch('https://app.c.nf.migu.cn/MIGUM3.0/v1.0/template/rank-list/release', {
@ -79,9 +78,8 @@ export default {
return this.requestBoardsObj.promise
},
getData(url) {
if (this.requestObj) this.requestObj.cancelHttp()
this.requestObj = httpFetch(url)
return this.requestObj.promise
const requestObj = httpFetch(url)
return requestObj.promise
},
getSinger(singers) {
let arr = []
@ -105,28 +103,28 @@ export default {
let size
switch (type.formatType) {
case 'PQ':
size = sizeFormate(type.size)
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '128k', size })
_types['128k'] = {
size,
}
break
case 'HQ':
size = sizeFormate(type.size)
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '320k', size })
_types['320k'] = {
size,
}
break
case 'SQ':
size = sizeFormate(type.size)
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac', size })
_types.flac = {
size,
}
break
case 'ZQ':
size = sizeFormate(type.size)
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac32bit', size })
_types.flac32bit = {
size,

View File

@ -57,14 +57,12 @@ export default {
},
successCode: '000000',
requestBoardsObj: null,
requestObj: null,
regExps: {
listData: /var listData = (\{.+\})<\/script>/,
},
getData(url) {
if (this.requestObj) this.requestObj.cancelHttp()
this.requestObj = httpFetch(url)
return this.requestObj.promise
const requestObj = httpFetch(url)
return requestObj.promise
},
getSinger(singers) {
let arr = []

View File

@ -5,15 +5,13 @@ import { sizeFormate } from '../../index'
// import { debug } from '../../utils/env'
// import { formatSinger } from './util'
let searchRequest
export default {
limit: 20,
total: 0,
page: 0,
allPage: 1,
musicSearch(str, page, limit) {
if (searchRequest && searchRequest.cancelHttp) searchRequest.cancelHttp()
searchRequest = httpFetch(`http://pd.musicapp.migu.cn/MIGUM2.0/v1.0/content/search_all.do?ua=Android_migu&version=5.0.1&text=${encodeURIComponent(str)}&pageNo=${page}&pageSize=${limit}&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A0%2C%22mvSong%22%3A0%2C%22songlist%22%3A0%2C%22bestShow%22%3A1%7D`, {
const searchRequest = httpFetch(`http://pd.musicapp.migu.cn/MIGUM2.0/v1.0/content/search_all.do?ua=Android_migu&version=5.0.1&text=${encodeURIComponent(str)}&pageNo=${page}&pageSize=${limit}&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A0%2C%22mvSong%22%3A0%2C%22songlist%22%3A0%2C%22bestShow%22%3A1%7D`, {
// searchRequest = httpFetch(`http://jadeite.migu.cn:7090/music_search/v2/search/searchAll?sid=4f87090d01c84984a11976b828e2b02c18946be88a6b4c47bcdc92fbd40762db&isCorrect=1&isCopyright=1&searchSwitch=%7B%22song%22%3A1%2C%22album%22%3A0%2C%22singer%22%3A0%2C%22tagSong%22%3A1%2C%22mvSong%22%3A0%2C%22bestShow%22%3A1%2C%22songlist%22%3A0%2C%22lyricSong%22%3A0%7D&pageSize=${limit}&text=${encodeURIComponent(str)}&pageNo=${page}&sort=0`, {
headers: {
// sign: 'c3b7ae985e2206e97f1b2de8f88691e2',
@ -49,28 +47,28 @@ export default {
let size
switch (type.formatType) {
case 'PQ':
size = sizeFormate(type.size)
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '128k', size })
_types['128k'] = {
size,
}
break
case 'HQ':
size = sizeFormate(type.size)
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '320k', size })
_types['320k'] = {
size,
}
break
case 'SQ':
size = sizeFormate(type.size)
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac', size })
_types.flac = {
size,
}
break
case 'ZQ':
size = sizeFormate(type.size)
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac32bit', size })
_types.flac32bit = {
size,

View File

@ -6,9 +6,6 @@ import { sizeFormate } from '../../index'
export default {
_requestObj_tags: null,
_requestObj_list: null,
_requestObj_listDetail: null,
_requestObj_listDetailLink: null,
_requestObj_listDetailInfo: null,
limit_list: 10,
limit_song: 50,
successCode: '000000',
@ -74,7 +71,6 @@ export default {
},
getListDetailList(id, page, tryNum = 0) {
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
// https://h5.nf.migu.cn/app/v4/p/share/playlist/index.html?id=184187437&channel=0146921
@ -82,8 +78,8 @@ export default {
id = id.replace(/.*(?:\?|&)id=(\d+)(?:&.*|$)/, '$1')
} else if ((/[?&:/]/.test(id))) id = id.replace(this.regExps.listDetailLink, '$1')
this._requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id, page), { headers: this.defaultHeaders })
return this._requestObj_listDetail.promise.then(({ body }) => {
const requestObj_listDetail = httpFetch(this.getSongListDetailUrl(id, page), { headers: this.defaultHeaders })
return requestObj_listDetail.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
// console.log(JSON.stringify(body))
// console.log(body)
@ -98,14 +94,13 @@ export default {
},
getListDetailInfo(id, tryNum = 0) {
if (this._requestObj_listDetailInfo) this._requestObj_listDetailInfo.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
if (this.cachedDetailInfo[id]) return Promise.resolve(this.cachedDetailInfo[id])
this._requestObj_listDetailInfo = httpFetch(`https://c.musicapp.migu.cn/MIGUM3.0/resource/playlist/v2.0?playlistId=${id}`, {
const requestObj_listDetailInfo = httpFetch(`https://c.musicapp.migu.cn/MIGUM3.0/resource/playlist/v2.0?playlistId=${id}`, {
headers: this.defaultHeaders,
})
return this._requestObj_listDetailInfo.promise.then(({ body }) => {
return requestObj_listDetailInfo.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getListDetail(id, ++tryNum)
// console.log(JSON.stringify(body))
// console.log(body)
@ -123,15 +118,13 @@ export default {
async getDetailUrl(link, page, retryNum = 0) {
if (retryNum > 3) return Promise.reject(new Error('link try max num'))
if (this._requestObj_listDetailLink) this._requestObj_listDetailLink.cancelHttp()
this._requestObj_listDetailLink = httpFetch(link, {
const requestObj_listDetailLink = httpFetch(link, {
headers: {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
Referer: link,
},
})
const { headers: { location }, statusCode } = await this._requestObj_listDetailLink.promise
const { headers: { location }, statusCode } = await requestObj_listDetailLink.promise
// console.log(body, location)
if (statusCode > 400) return this.getDetailUrl(link, page, ++retryNum)
if (location) {
@ -174,28 +167,28 @@ export default {
let size
switch (type.formatType) {
case 'PQ':
size = sizeFormate(type.size)
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '128k', size })
_types['128k'] = {
size,
}
break
case 'HQ':
size = sizeFormate(type.size)
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: '320k', size })
_types['320k'] = {
size,
}
break
case 'SQ':
size = sizeFormate(type.size)
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac', size })
_types.flac = {
size,
}
break
case 'ZQ':
size = sizeFormate(type.size)
size = sizeFormate(type.size ?? type.androidSize)
types.push({ type: 'flac32bit', size })
_types.flac32bit = {
size,

View File

@ -86,16 +86,14 @@ export default {
periods: {},
periodUrl: 'https://c.y.qq.com/node/pc/wk_v15/top.html',
_requestBoardsObj: null,
_requestDataObj: null,
getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch('https://c.y.qq.com/v8/fcg-bin/fcg_myqq_toplist.fcg?g_tk=1928093487&inCharset=utf-8&outCharset=utf-8&notice=0&format=json&uin=0&needNewCode=1&platform=h5')
return this._requestBoardsObj.promise
},
getData(url) {
if (this._requestDataObj) this._requestDataObj.cancelHttp()
this._requestDataObj = httpFetch(url)
return this._requestDataObj.promise
const requestDataObj = httpFetch(url)
return requestDataObj.promise
},
getSinger(singers) {
let arr = []

View File

@ -5,7 +5,6 @@ import { formatPlayTime, sizeFormate } from '../../index'
// import { debug } from '../../utils/env'
// import { formatSinger } from './util'
let searchRequest
export default {
limit: 30,
total: 0,
@ -13,10 +12,9 @@ export default {
allPage: 1,
successCode: 0,
musicSearch(str, page, limit, retryNum = 0) {
if (searchRequest && searchRequest.cancelHttp) searchRequest.cancelHttp()
if (retryNum > 5) return Promise.reject(new Error('搜索失败'))
// searchRequest = httpFetch(`https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=sizer.yqq.song_next&searchid=49252838123499591&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=${page}&n=${limit}&w=${encodeURIComponent(str)}&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0`)
searchRequest = httpFetch(`https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&remoteplace=txt.yqq.top&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=${page}&n=${limit}&w=${encodeURIComponent(str)}&cv=4747474&ct=24&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0&uin=0&hostUin=0&loginUin=0`)
const searchRequest = httpFetch(`https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&remoteplace=txt.yqq.top&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=${page}&n=${limit}&w=${encodeURIComponent(str)}&cv=4747474&ct=24&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0&uin=0&hostUin=0&loginUin=0`)
// searchRequest = httpFetch(`http://ioscdn.kugou.com/api/v3/search/song?keyword=${encodeURIComponent(str)}&page=${page}&pagesize=${this.limit}&showtype=10&plat=2&version=7910&tag=1&correct=1&privilege=1&sver=5`)
return searchRequest.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.musicSearch(str, page, limit, ++retryNum)

View File

@ -5,8 +5,6 @@ export default {
_requestObj_tags: null,
_requestObj_hotTags: null,
_requestObj_list: null,
_requestObj_listDetail: null,
_requestObj_listDetailLink: null,
limit_list: 36,
limit_song: 100000,
successCode: 0,
@ -175,11 +173,10 @@ export default {
},
async handleParseId(link, retryNum = 0) {
if (this._requestObj_listDetailLink) this._requestObj_listDetailLink.cancelHttp()
if (retryNum > 2) return Promise.reject(new Error('link try max num'))
this._requestObj_listDetailLink = httpFetch(link)
const { headers: { location }, statusCode } = await this._requestObj_listDetailLink.promise
const requestObj_listDetailLink = httpFetch(link)
const { headers: { location }, statusCode } = await requestObj_listDetailLink.promise
// console.log(headers)
if (statusCode > 400) return this.handleParseId(link, ++retryNum)
return location == null ? link : location
@ -200,18 +197,17 @@ export default {
},
// 获取歌曲列表内的音乐
async getListDetail(id, tryNum = 0) {
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
id = await this.getListId(id)
this._requestObj_listDetail = httpFetch(this.getListDetailUrl(id), {
const requestObj_listDetail = httpFetch(this.getListDetailUrl(id), {
headers: {
Origin: 'https://y.qq.com',
Referer: `https://y.qq.com/n/yqq/playsquare/${id}.html`,
},
})
const { body } = await this._requestObj_listDetail.promise
const { body } = await requestObj_listDetail.promise
if (body.code !== this.successCode) return this.getListDetail(id, ++tryNum)
const cdlist = body.cdlist[0]

View File

@ -104,7 +104,6 @@ export default {
list: /<textarea id="song-list-pre-data" style="display:none;">(.+?)<\/textarea>/,
},
_requestBoardsObj: null,
_requestBoardsDetailObj: null,
getBoardsData() {
if (this._requestBoardsObj) this._requestBoardsObj.cancelHttp()
this._requestBoardsObj = httpFetch('https://music.163.com/weapi/toplist', {
@ -114,8 +113,7 @@ export default {
return this._requestBoardsObj.promise
},
getData(id) {
if (this._requestBoardsDetailObj) this._requestBoardsDetailObj.cancelHttp()
this._requestBoardsDetailObj = httpFetch('https://music.163.com/weapi/v3/playlist/detail', {
const requestBoardsDetailObj = httpFetch('https://music.163.com/weapi/v3/playlist/detail', {
method: 'post',
form: weapi({
id,
@ -123,7 +121,7 @@ export default {
p: 1,
}),
})
return this._requestBoardsDetailObj.promise
return requestBoardsDetailObj.promise
},
filterBoardsData(rawList) {

View File

@ -34,26 +34,30 @@ import { linuxapi } from './utils/crypto'
// return lxlyric.trim()
// }
// https://github.com/Binaryify/NeteaseCloudMusicApi/pull/1523/files
export default songmid => {
const requestObj = httpFetch('https://music.163.com/api/linux/forward', {
method: 'post',
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
form: linuxapi({
method: 'POST',
url: 'https://music.163.com/api/song/lyric',
url: 'https://music.163.com/api/song/lyric?_nmclfl=1',
params: {
id: songmid,
lv: -1,
kv: -1,
tv: -1,
lv: -1,
rv: -1,
kv: -1,
},
}),
})
requestObj.promise = requestObj.promise.then(({ body }) => {
if (body.code !== 200 || !body?.lrc?.lyric) return Promise.reject(new Error('Get lyric failed'))
// console.log(body)
return {
lyric: body.lrc.lyric,
tlyric: body.tlyric?.lyric ?? '',
rlyric: body.romalrc?.lyric ?? '',
// lxlyric: parseLyric(body.klyric.lyric),
}
})

View File

@ -4,7 +4,6 @@ import { formatPlayTime, sizeFormate } from '../..'
// https://github.com/Binaryify/NeteaseCloudMusicApi/blob/master/module/song_detail.js
export default {
_requestObj: null,
getSinger(singers) {
let arr = []
singers.forEach(singer => {
@ -70,10 +69,9 @@ export default {
return list
},
async getList(ids = [], retryNum = 0) {
if (this._requestObj) this._requestObj.cancelHttp()
if (retryNum > 2) return Promise.reject(new Error('try max num'))
const _requestObj = httpFetch('https://music.163.com/weapi/v3/song/detail', {
const requestObj = httpFetch('https://music.163.com/weapi/v3/song/detail', {
method: 'post',
headers: {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
@ -84,7 +82,7 @@ export default {
ids: '[' + ids.join(',') + ']',
}),
})
const { body, statusCode } = await _requestObj.promise
const { body, statusCode } = await requestObj.promise
if (statusCode != 200 || body.code !== 200) throw new Error('获取歌曲详情失败')
// console.log(body)
return { source: 'wy', list: this.filterList(body) }

View File

@ -4,15 +4,13 @@ import { sizeFormate, formatPlayTime } from '../../index'
// import musicDetailApi from './musicDetail'
import { eapiRequest } from './utils'
let searchRequest
export default {
limit: 30,
total: 0,
page: 0,
allPage: 1,
musicSearch(str, page, limit) {
if (searchRequest && searchRequest.cancelHttp) searchRequest.cancelHttp()
searchRequest = eapiRequest('/api/cloudsearch/pc', {
const searchRequest = eapiRequest('/api/cloudsearch/pc', {
s: str,
type: 1, // 1: 单曲, 10: 专辑, 100: 歌手, 1000: 歌单, 1002: 用户, 1004: MV, 1006: 歌词, 1009: 电台, 1014: 视频
limit,

View File

@ -12,8 +12,6 @@ export default {
_requestObj_tags: null,
_requestObj_hotTags: null,
_requestObj_list: null,
_requestObj_listDetail: null,
_requestObj_listDetailLink: null,
limit_list: 30,
limit_song: 100000,
successCode: 200,
@ -50,11 +48,10 @@ export default {
},
async handleParseId(link, retryNum = 0) {
if (this._requestObj_listDetailLink) this._requestObj_listDetailLink.cancelHttp()
if (retryNum > 2) throw new Error('link try max num')
this._requestObj_listDetailLink = httpFetch(link)
const { headers: { location }, statusCode } = await this._requestObj_listDetailLink.promise
const requestObj_listDetailLink = httpFetch(link)
const { headers: { location }, statusCode } = await requestObj_listDetailLink.promise
// console.log(headers)
if (statusCode > 400) return this.handleParseId(link, ++retryNum)
const url = location == null ? link : location
@ -83,13 +80,12 @@ export default {
return { id, cookie }
},
async getListDetail(rawId, page, tryNum = 0) { // 获取歌曲列表内的音乐
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
const { id, cookie } = await this.getListId(rawId)
if (cookie) this.cookie = cookie
this._requestObj_listDetail = httpFetch('https://music.163.com/api/linux/forward', {
const requestObj_listDetail = httpFetch('https://music.163.com/api/linux/forward', {
method: 'post',
headers: {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
@ -105,7 +101,7 @@ export default {
},
}),
})
const { statusCode, body } = await this._requestObj_listDetail.promise
const { statusCode, body } = await requestObj_listDetail.promise
if (statusCode !== 200 || body.code !== this.successCode) return this.getListDetail(id, page, ++tryNum)
let limit = 1000
let rangeStart = (page - 1) * limit

View File

@ -19,7 +19,7 @@ const aesDecrypt = function(cipherBuffer, mode, key, iv) {
const rsaEncrypt = (buffer, key) => {
buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer])
return publicEncrypt({ key: key, padding: constants.RSA_NO_PADDING }, buffer)
return publicEncrypt({ key, padding: constants.RSA_NO_PADDING }, buffer)
}
export const weapi = object => {

View File

@ -27,7 +27,7 @@ export const setAllowShowUserApiUpdateAlert = (id, enable) => {
export const saveMyList = data => {
rendererSend(NAMES.mainWindow.save_playlist, {
type: 'myList',
data: data,
data,
})
}
export const saveDownloadList = list => {

View File

@ -22,7 +22,8 @@ div(:class="$style.search")
td.nobreak.center(style="width: 5%; padding-left: 3px; padding-right: 3px;" :class="$style.noSelect" @click.stop) {{index + 1}}
td.break
span.select {{item.name}}
span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-if="item._types.ape || item._types.flac || item._types.wav") {{$t('tag__lossless')}}
span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-if="item._types.flac32bit") {{$t('tag__lossless_24bit')}}
span.badge.badge-theme-success(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types.ape || item._types.flac || item._types.wav") {{$t('tag__lossless')}}
span.badge.badge-theme-info(:class="[$style.labelQuality, $style.noSelect]" v-else-if="item._types['320k']") {{$t('tag__high_quality')}}
span(:class="[$style.labelSource, $style.noSelect]" v-if="searchSourceId == 'all'") {{item.source}}
td.break(style="width: 22%;")
@ -107,6 +108,7 @@ export default {
},
},
isLoading: false,
searchId: null,
}
},
beforeRouteUpdate(to, from) {
@ -259,14 +261,20 @@ export default {
this.handleSelectAllData()
},
handleSearch(text, page) {
if (text === '') return this.clearList()
const searchId = this.searchId = `${this.searchSourceId}__${page}__${text}`
if (text === '') {
this.isLoading = false
return this.clearList()
}
this.isLoading = true
this.search({ text, page, limit: this.listInfo.limit }).then(data => {
if (this.searchId != searchId) return
this.page = page
this.$nextTick(() => {
this.$refs.dom_scrollContent.scrollTo(0, 0)
})
}).finally(() => {
if (this.searchId != searchId) return
this.isLoading = false
})
},

View File

@ -1,10 +1,12 @@
import { useCommit, useRouter } from '@renderer/utils/vueTools'
import { useCommit, useRouter, useI18n } from '@renderer/utils/vueTools'
import musicSdk from '@renderer/utils/music'
import { openUrl, clipboardWriteText } from '@renderer/utils'
import { dialog } from '@renderer/plugins/Dialog'
export default ({ props, list, setting, selectedList, removeAllSelect }) => {
const router = useRouter()
const { t } = useI18n()
const listRemove = useCommit('list', 'listRemove')
const listRemoveMultiple = useCommit('list', 'listRemoveMultiple')
@ -31,8 +33,16 @@ export default ({ props, list, setting, selectedList, removeAllSelect }) => {
clipboardWriteText(setting.value.download.fileName.replace('歌名', minfo.name).replace('歌手', minfo.singer))
}
const handleRemoveMusic = (index, single) => {
const handleRemoveMusic = async(index, single) => {
if (selectedList.value.length && !single) {
const confirm = await (selectedList.value.length > 1
? dialog.confirm({
message: t('lists__remove music_tip', { len: selectedList.value.length }),
confirmButtonText: t('lists__remove_tip_button'),
})
: Promise.resolve(true)
)
if (!confirm) return
listRemoveMultiple({ listId: props.listId, ids: selectedList.value.map(m => m.songmid) })
removeAllSelect()
} else {

View File

@ -5,6 +5,8 @@ dd
base-checkbox(id="setting_player_save_play_time" v-model="currentStting.player.isSavePlayTime" :label="$t('setting__play_save_play_time')")
.gap-top
base-checkbox(id="setting_player_lyric_transition" v-model="currentStting.player.isShowLyricTranslation" :label="$t('setting__play_lyric_transition')")
.gap-top
base-checkbox(id="setting_player_lyric_roma" v-model="currentStting.player.isShowLyricRoma" :label="$t('setting__play_lyric_roma')")
.gap-top
base-checkbox(id="setting_player_auto_skip_on_error" v-model="currentStting.player.autoSkipOnError" :label="$t('setting__play_auto_skip_on_error')")
.gap-top

View File

@ -10,6 +10,7 @@ export const currentStting = ref({
mediaDeviceId: 'default',
isMediaDeviceRemovedStopPlay: false,
isShowLyricTranslation: false,
isShowLyricRoma: false,
isS2t: false, // 是否将歌词从简体转换为繁体
isPlayLxlrc: true,
isSavePlayTime: false,