mirror of
https://github.com/timeshiftsauce/CeruMusic.git
synced 2025-11-25 03:15:07 +08:00
feat:播放列表拖拽排序;播放界面ui优化;插件页滚动问题;歌曲加载状提示;接入window系统的 AMTC 控制
This commit is contained in:
13
.eslintrc.backup.json
Normal file
13
.eslintrc.backup.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"@electron-toolkit/eslint-config-ts",
|
||||||
|
"@electron-toolkit/eslint-config-prettier"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"vue/require-default-prop": "off",
|
||||||
|
"vue/multi-word-component-names": "off",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "warn",
|
||||||
|
"no-console": "warn"
|
||||||
|
}
|
||||||
|
}
|
||||||
4
.vitepress/cache/deps/chunk-TH7GRLUQ.js
vendored
4
.vitepress/cache/deps/chunk-TH7GRLUQ.js
vendored
@@ -2134,7 +2134,6 @@ function warn$1(msg, ...args) {
|
|||||||
const trace = getComponentTrace()
|
const trace = getComponentTrace()
|
||||||
if (appWarnHandler) {
|
if (appWarnHandler) {
|
||||||
callWithErrorHandling(appWarnHandler, instance, 11, [
|
callWithErrorHandling(appWarnHandler, instance, 11, [
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
msg +
|
msg +
|
||||||
args
|
args
|
||||||
.map((a) => {
|
.map((a) => {
|
||||||
@@ -2700,7 +2699,6 @@ function setDevtoolsHook$1(hook, target) {
|
|||||||
// (#4815)
|
// (#4815)
|
||||||
typeof window !== 'undefined' && // some envs mock window but not fully
|
typeof window !== 'undefined' && // some envs mock window but not fully
|
||||||
window.HTMLElement && // also exclude jsdom
|
window.HTMLElement && // also exclude jsdom
|
||||||
// eslint-disable-next-line no-restricted-syntax
|
|
||||||
!((_b = (_a = window.navigator) == null ? void 0 : _a.userAgent) == null
|
!((_b = (_a = window.navigator) == null ? void 0 : _a.userAgent) == null
|
||||||
? void 0
|
? void 0
|
||||||
: _b.includes('jsdom'))
|
: _b.includes('jsdom'))
|
||||||
@@ -9613,7 +9611,7 @@ function hydrateSuspense(
|
|||||||
parentSuspense,
|
parentSuspense,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
node.parentNode,
|
node.parentNode,
|
||||||
// eslint-disable-next-line no-restricted-globals
|
|
||||||
document.createElement('div'),
|
document.createElement('div'),
|
||||||
null,
|
null,
|
||||||
namespace,
|
namespace,
|
||||||
|
|||||||
@@ -3985,11 +3985,11 @@ var walker = (
|
|||||||
const result = isEmptyObject(innerAnnotations)
|
const result = isEmptyObject(innerAnnotations)
|
||||||
? {
|
? {
|
||||||
transformedValue,
|
transformedValue,
|
||||||
annotations: !!transformationResult ? [transformationResult.type] : void 0
|
annotations: transformationResult ? [transformationResult.type] : void 0
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
transformedValue,
|
transformedValue,
|
||||||
annotations: !!transformationResult
|
annotations: transformationResult
|
||||||
? [transformationResult.type, innerAnnotations]
|
? [transformationResult.type, innerAnnotations]
|
||||||
: innerAnnotations
|
: innerAnnotations
|
||||||
}
|
}
|
||||||
|
|||||||
235
eslint.config.js
Normal file
235
eslint.config.js
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
import js from '@eslint/js'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
import vue from 'eslint-plugin-vue'
|
||||||
|
import prettier from '@electron-toolkit/eslint-config-prettier'
|
||||||
|
|
||||||
|
export default [
|
||||||
|
// 基础 JavaScript 推荐配置
|
||||||
|
js.configs.recommended,
|
||||||
|
|
||||||
|
// TypeScript 推荐配置
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
|
||||||
|
// Vue 3 推荐配置
|
||||||
|
...vue.configs['flat/recommended'],
|
||||||
|
|
||||||
|
// 忽略的文件和目录
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
'**/node_modules/**',
|
||||||
|
'**/dist/**',
|
||||||
|
'**/out/**',
|
||||||
|
'**/build/**',
|
||||||
|
'**/.vitepress/**',
|
||||||
|
'**/docs/**',
|
||||||
|
'**/website/**',
|
||||||
|
'**/coverage/**',
|
||||||
|
'**/*.min.js',
|
||||||
|
'**/auto-imports.d.ts',
|
||||||
|
'**/components.d.ts',
|
||||||
|
'src/preload/index.d.ts', // 忽略类型定义文件
|
||||||
|
'src/renderer/src/assets/icon_font/**', // 忽略第三方图标字体文件
|
||||||
|
'src/main/utils/musicSdk/**', // 忽略第三方音乐 SDK
|
||||||
|
'src/main/utils/request.js', // 忽略第三方请求库
|
||||||
|
'scripts/**', // 忽略脚本文件
|
||||||
|
'src/common/utils/lyricUtils/**' // 忽略第三方歌词工具
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
// 全局配置
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,ts,vue}'],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// 代码质量 (放宽规则)
|
||||||
|
'no-unused-vars': 'off', // 由 TypeScript 处理
|
||||||
|
'no-undef': 'off', // 由 TypeScript 处理
|
||||||
|
'prefer-const': 'warn', // 降级为警告
|
||||||
|
'no-var': 'warn', // 降级为警告
|
||||||
|
'no-duplicate-imports': 'off', // 允许重复导入
|
||||||
|
'no-useless-return': 'off',
|
||||||
|
'no-useless-concat': 'off',
|
||||||
|
'no-useless-escape': 'off',
|
||||||
|
'no-unreachable': 'warn',
|
||||||
|
'no-debugger': 'off',
|
||||||
|
|
||||||
|
// 代码风格 (大幅放宽)
|
||||||
|
eqeqeq: 'off', // 允许 == 和 ===
|
||||||
|
curly: 'off', // 允许不使用大括号
|
||||||
|
'brace-style': 'off',
|
||||||
|
'comma-dangle': 'off',
|
||||||
|
quotes: 'off',
|
||||||
|
semi: 'off',
|
||||||
|
indent: 'off',
|
||||||
|
'object-curly-spacing': 'off',
|
||||||
|
'array-bracket-spacing': 'off',
|
||||||
|
'space-before-function-paren': 'off',
|
||||||
|
|
||||||
|
// 最佳实践 (放宽)
|
||||||
|
'no-eval': 'warn',
|
||||||
|
'no-implied-eval': 'warn',
|
||||||
|
'no-new-func': 'warn',
|
||||||
|
'no-alert': 'off',
|
||||||
|
'no-empty': 'off', // 允许空块
|
||||||
|
'no-extra-boolean-cast': 'off',
|
||||||
|
'no-extra-semi': 'off',
|
||||||
|
'no-irregular-whitespace': 'off',
|
||||||
|
'no-multiple-empty-lines': 'off',
|
||||||
|
'no-trailing-spaces': 'off',
|
||||||
|
'eol-last': 'off',
|
||||||
|
'no-fallthrough': 'off', // 允许 switch case 穿透
|
||||||
|
'no-case-declarations': 'off', // 允许 case 中声明变量
|
||||||
|
'no-empty-pattern': 'off', // 允许空对象模式
|
||||||
|
'no-prototype-builtins': 'off', // 允许直接调用 hasOwnProperty
|
||||||
|
'no-self-assign': 'off', // 允许自赋值
|
||||||
|
'no-async-promise-executor': 'off' // 允许异步 Promise 执行器
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 主进程 TypeScript 配置
|
||||||
|
{
|
||||||
|
files: ['src/main/**/*.ts', 'src/preload/**/*.ts', 'src/common/**/*.ts', 'src/types/**/*.ts'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
project: './tsconfig.node.json',
|
||||||
|
tsconfigRootDir: process.cwd()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// TypeScript 特定规则 (大幅放宽)
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off', // 完全关闭未使用变量检查
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off', // 允许 require
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off', // 允许 @ts-ignore
|
||||||
|
'@typescript-eslint/no-empty-function': 'off', // 允许空函数
|
||||||
|
'@typescript-eslint/no-inferrable-types': 'off',
|
||||||
|
'@typescript-eslint/no-unused-expressions': 'off', // 允许未使用的表达式
|
||||||
|
'@typescript-eslint/no-require-imports': 'off', // 允许 require 导入
|
||||||
|
'@typescript-eslint/no-unsafe-function-type': 'off', // 允许 Function 类型
|
||||||
|
'@typescript-eslint/prefer-as-const': 'off' // 允许字面量类型
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 渲染进程 TypeScript 配置
|
||||||
|
{
|
||||||
|
files: ['src/renderer/**/*.ts'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
project: './tsconfig.web.json',
|
||||||
|
tsconfigRootDir: process.cwd()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// TypeScript 特定规则 (大幅放宽)
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'@typescript-eslint/no-empty-function': 'off',
|
||||||
|
'@typescript-eslint/no-inferrable-types': 'off',
|
||||||
|
'@typescript-eslint/no-unused-expressions': 'off',
|
||||||
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-function-type': 'off',
|
||||||
|
'@typescript-eslint/prefer-as-const': 'off'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Vue 特定配置
|
||||||
|
{
|
||||||
|
files: ['src/renderer/**/*.vue'],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
extraFileExtensions: ['.vue']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// Vue 特定规则 (大幅放宽)
|
||||||
|
'vue/multi-word-component-names': 'off',
|
||||||
|
'vue/no-v-html': 'off', // 允许 v-html
|
||||||
|
'vue/require-default-prop': 'off',
|
||||||
|
'vue/require-explicit-emits': 'off', // 不强制显式 emits
|
||||||
|
'vue/component-definition-name-casing': 'off',
|
||||||
|
'vue/component-name-in-template-casing': 'off',
|
||||||
|
'vue/custom-event-name-casing': 'off', // 允许任意事件命名
|
||||||
|
'vue/define-macros-order': 'off',
|
||||||
|
'vue/html-self-closing': 'off',
|
||||||
|
'vue/max-attributes-per-line': 'off',
|
||||||
|
'vue/singleline-html-element-content-newline': 'off',
|
||||||
|
'vue/multiline-html-element-content-newline': 'off',
|
||||||
|
'vue/no-side-effects-in-computed-properties': 'off', // 允许计算属性中的副作用
|
||||||
|
'vue/no-required-prop-with-default': 'off', // 允许带默认值的必需属性
|
||||||
|
|
||||||
|
// TypeScript 在 Vue 中的规则 (放宽)
|
||||||
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-function-type': 'off'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 主进程文件配置 (Node.js 环境)
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'src/main/**/*.{ts,js}',
|
||||||
|
'src/preload/**/*.{ts,js}',
|
||||||
|
'electron.vite.config.*',
|
||||||
|
'scripts/**/*.{js,ts}'
|
||||||
|
],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
__dirname: 'readonly',
|
||||||
|
__filename: 'readonly',
|
||||||
|
Buffer: 'readonly',
|
||||||
|
process: 'readonly',
|
||||||
|
global: 'readonly'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// Node.js 特定规则 (放宽)
|
||||||
|
'no-console': 'off',
|
||||||
|
'no-process-exit': 'off' // 允许 process.exit()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 渲染进程文件配置 (浏览器环境)
|
||||||
|
{
|
||||||
|
files: ['src/renderer/**/*.{ts,js,vue}'],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
window: 'readonly',
|
||||||
|
document: 'readonly',
|
||||||
|
navigator: 'readonly',
|
||||||
|
console: 'readonly',
|
||||||
|
setTimeout: 'readonly',
|
||||||
|
clearTimeout: 'readonly',
|
||||||
|
setInterval: 'readonly',
|
||||||
|
clearInterval: 'readonly'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// 浏览器环境特定规则
|
||||||
|
'no-console': 'off'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 配置文件特殊规则
|
||||||
|
{
|
||||||
|
files: ['*.config.{js,ts}', 'vite.config.*', 'electron.vite.config.*'],
|
||||||
|
rules: {
|
||||||
|
'no-console': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Prettier 配置 (必须放在最后)
|
||||||
|
prettier
|
||||||
|
]
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
const baseRule = {
|
|
||||||
'no-new': 'off',
|
|
||||||
camelcase: 'off',
|
|
||||||
'no-return-assign': 'off',
|
|
||||||
'space-before-function-paren': ['error', 'never'],
|
|
||||||
'no-var': 'error',
|
|
||||||
'no-fallthrough': 'off',
|
|
||||||
eqeqeq: 'off',
|
|
||||||
'require-atomic-updates': ['error', { allowProperties: true }],
|
|
||||||
'no-multiple-empty-lines': [1, { max: 2 }],
|
|
||||||
'comma-dangle': [2, 'always-multiline'],
|
|
||||||
'standard/no-callback-literal': 'off',
|
|
||||||
'prefer-const': 'off',
|
|
||||||
'no-labels': 'off',
|
|
||||||
'node/no-callback-literal': 'off',
|
|
||||||
'multiline-ternary': 'off'
|
|
||||||
}
|
|
||||||
const typescriptRule = {
|
|
||||||
...baseRule,
|
|
||||||
'@typescript-eslint/strict-boolean-expressions': 'off',
|
|
||||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
||||||
'@typescript-eslint/space-before-function-paren': 'off',
|
|
||||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
|
||||||
'@typescript-eslint/restrict-template-expressions': [
|
|
||||||
1,
|
|
||||||
{
|
|
||||||
allowBoolean: true,
|
|
||||||
allowAny: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'@typescript-eslint/restrict-plus-operands': [
|
|
||||||
1,
|
|
||||||
{
|
|
||||||
allowBoolean: true,
|
|
||||||
allowAny: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'@typescript-eslint/no-misused-promises': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
checksVoidReturn: {
|
|
||||||
arguments: false,
|
|
||||||
attributes: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'@typescript-eslint/naming-convention': 'off',
|
|
||||||
'@typescript-eslint/return-await': 'off',
|
|
||||||
'@typescript-eslint/ban-ts-comment': 'off',
|
|
||||||
'@typescript-eslint/comma-dangle': 'off',
|
|
||||||
'@typescript-eslint/no-unsafe-argument': 'off'
|
|
||||||
}
|
|
||||||
const vueRule = {
|
|
||||||
...typescriptRule,
|
|
||||||
'vue/multi-word-component-names': 'off',
|
|
||||||
'vue/max-attributes-per-line': 'off',
|
|
||||||
'vue/singleline-html-element-content-newline': 'off',
|
|
||||||
'vue/use-v-on-exact': 'off'
|
|
||||||
}
|
|
||||||
|
|
||||||
export const base = {
|
|
||||||
extends: ['standard'],
|
|
||||||
rules: baseRule,
|
|
||||||
parser: '@babel/eslint-parser'
|
|
||||||
}
|
|
||||||
|
|
||||||
export const html = {
|
|
||||||
files: ['*.html'],
|
|
||||||
plugins: ['html']
|
|
||||||
}
|
|
||||||
|
|
||||||
export const typescript = {
|
|
||||||
files: ['*.ts'],
|
|
||||||
rules: typescriptRule,
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
extends: ['standard-with-typescript']
|
|
||||||
}
|
|
||||||
|
|
||||||
export const vue = {
|
|
||||||
files: ['*.vue'],
|
|
||||||
rules: vueRule,
|
|
||||||
parser: 'vue-eslint-parser',
|
|
||||||
extends: [
|
|
||||||
// 'plugin:vue/vue3-essential',
|
|
||||||
'plugin:vue/base',
|
|
||||||
'plugin:vue/vue3-recommended',
|
|
||||||
'plugin:vue-pug/vue3-recommended',
|
|
||||||
// "plugin:vue/strongly-recommended"
|
|
||||||
'standard-with-typescript'
|
|
||||||
],
|
|
||||||
parserOptions: {
|
|
||||||
sourceType: 'module',
|
|
||||||
parser: {
|
|
||||||
// Script parser for `<script>`
|
|
||||||
js: '@typescript-eslint/parser',
|
|
||||||
|
|
||||||
// Script parser for `<script lang="ts">`
|
|
||||||
ts: '@typescript-eslint/parser'
|
|
||||||
},
|
|
||||||
extraFileExtensions: ['.vue']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ceru-music",
|
"name": "ceru-music",
|
||||||
"version": "1.2.8",
|
"version": "1.3.0",
|
||||||
"description": "一款简洁优雅的音乐播放器",
|
"description": "一款简洁优雅的音乐播放器",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
"author": "sqj,wldss,star",
|
"author": "sqj,wldss,star",
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function compareVer(currentVer: string, targetVer: string): -1 | 0 | 1 {
|
|||||||
.replace(/[^0-9.]/g, fix)
|
.replace(/[^0-9.]/g, fix)
|
||||||
.split('.')
|
.split('.')
|
||||||
const targetVerArr: Array<string | number> = ('' + targetVer).replace(/[^0-9.]/g, fix).split('.')
|
const targetVerArr: Array<string | number> = ('' + targetVer).replace(/[^0-9.]/g, fix).split('.')
|
||||||
let c = Math.max(currentVerArr.length, targetVerArr.length)
|
const c = Math.max(currentVerArr.length, targetVerArr.length)
|
||||||
for (let i = 0; i < c; i++) {
|
for (let i = 0; i < c; i++) {
|
||||||
// convert to integer the most efficient way
|
// convert to integer the most efficient way
|
||||||
currentVerArr[i] = ~~currentVerArr[i]
|
currentVerArr[i] = ~~currentVerArr[i]
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ export const toDateObj = (date: any): Date | '' => {
|
|||||||
switch (typeof date) {
|
switch (typeof date) {
|
||||||
case 'string':
|
case 'string':
|
||||||
if (!date.includes('T')) date = date.split('.')[0].replace(/-/g, '/')
|
if (!date.includes('T')) date = date.split('.')[0].replace(/-/g, '/')
|
||||||
// eslint-disable-next-line no-fallthrough
|
|
||||||
case 'number':
|
case 'number':
|
||||||
date = new Date(date)
|
date = new Date(date)
|
||||||
// eslint-disable-next-line no-fallthrough
|
|
||||||
case 'object':
|
case 'object':
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ const headExp = /^.*\[id:\$\w+\]\n/
|
|||||||
const parseLyric = (str) => {
|
const parseLyric = (str) => {
|
||||||
str = str.replace(/\r/g, '')
|
str = str.replace(/\r/g, '')
|
||||||
if (headExp.test(str)) str = str.replace(headExp, '')
|
if (headExp.test(str)) str = str.replace(headExp, '')
|
||||||
let trans = str.match(/\[language:([\w=\\/+]+)\]/)
|
const trans = str.match(/\[language:([\w=\\/+]+)\]/)
|
||||||
let lyric
|
let lyric
|
||||||
let rlyric
|
let rlyric
|
||||||
let tlyric
|
let tlyric
|
||||||
if (trans) {
|
if (trans) {
|
||||||
str = str.replace(/\[language:[\w=\\/+]+\]\n/, '')
|
str = str.replace(/\[language:[\w=\\/+]+\]\n/, '')
|
||||||
let json = JSON.parse(Buffer.from(trans[1], 'base64').toString())
|
const json = JSON.parse(Buffer.from(trans[1], 'base64').toString())
|
||||||
for (const item of json.content) {
|
for (const item of json.content) {
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -44,23 +44,23 @@ const parseLyric = (str) => {
|
|||||||
}
|
}
|
||||||
let i = 0
|
let i = 0
|
||||||
let crlyric = str.replace(/\[((\d+),\d+)\].*/g, (str) => {
|
let crlyric = str.replace(/\[((\d+),\d+)\].*/g, (str) => {
|
||||||
let result = str.match(/\[((\d+),\d+)\].*/)
|
const result = str.match(/\[((\d+),\d+)\].*/)
|
||||||
let lineStartTime = parseInt(result[2]) // 行开始时间
|
const lineStartTime = parseInt(result[2]) // 行开始时间
|
||||||
let time = lineStartTime
|
let time = lineStartTime
|
||||||
let ms = time % 1000
|
const ms = time % 1000
|
||||||
time /= 1000
|
time /= 1000
|
||||||
let m = parseInt(time / 60)
|
const m = parseInt(time / 60)
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(2, '0')
|
.padStart(2, '0')
|
||||||
time %= 60
|
time %= 60
|
||||||
let s = parseInt(time).toString().padStart(2, '0')
|
const s = parseInt(time).toString().padStart(2, '0')
|
||||||
time = `${m}:${s}.${ms}`
|
time = `${m}:${s}.${ms}`
|
||||||
if (rlyric) rlyric[i] = `[${time}]${rlyric[i]?.join('') ?? ''}`
|
if (rlyric) rlyric[i] = `[${time}]${rlyric[i]?.join('') ?? ''}`
|
||||||
if (tlyric) tlyric[i] = `[${time}]${tlyric[i]?.join('') ?? ''}`
|
if (tlyric) tlyric[i] = `[${time}]${tlyric[i]?.join('') ?? ''}`
|
||||||
i++
|
i++
|
||||||
|
|
||||||
// 保持原始的 [start,duration] 格式,将相对时间戳转换为绝对时间戳
|
// 保持原始的 [start,duration] 格式,将相对时间戳转换为绝对时间戳
|
||||||
let processedStr = str.replace(/<(\d+),(\d+),(\d+)>/g, (match, start, duration, param) => {
|
const processedStr = str.replace(/<(\d+),(\d+),(\d+)>/g, (match, start, duration, param) => {
|
||||||
const absoluteStart = lineStartTime + parseInt(start)
|
const absoluteStart = lineStartTime + parseInt(start)
|
||||||
return `(${absoluteStart},${duration},${param})`
|
return `(${absoluteStart},${duration},${param})`
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const handleScrollY = (
|
|||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
const start = element.scrollTop ?? element.scrollY ?? 0
|
const start = element.scrollTop ?? element.scrollY ?? 0
|
||||||
if (to > start) {
|
if (to > start) {
|
||||||
let maxScrollTop = element.scrollHeight - element.clientHeight
|
const maxScrollTop = element.scrollHeight - element.clientHeight
|
||||||
if (to > maxScrollTop) to = maxScrollTop
|
if (to > maxScrollTop) to = maxScrollTop
|
||||||
} else if (to < start) {
|
} else if (to < start) {
|
||||||
if (to < 0) to = 0
|
if (to < 0) to = 0
|
||||||
@@ -55,7 +55,7 @@ const handleScrollY = (
|
|||||||
|
|
||||||
let currentTime = 0
|
let currentTime = 0
|
||||||
let val: number
|
let val: number
|
||||||
let key = Math.random()
|
const key = Math.random()
|
||||||
|
|
||||||
const animateScroll = () => {
|
const animateScroll = () => {
|
||||||
element.lx_scrollTimeout = undefined
|
element.lx_scrollTimeout = undefined
|
||||||
@@ -156,7 +156,7 @@ const handleScrollX = (
|
|||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
const start = element.scrollLeft || element.scrollX || 0
|
const start = element.scrollLeft || element.scrollX || 0
|
||||||
if (to > start) {
|
if (to > start) {
|
||||||
let maxScrollLeft = element.scrollWidth - element.clientWidth
|
const maxScrollLeft = element.scrollWidth - element.clientWidth
|
||||||
if (to > maxScrollLeft) to = maxScrollLeft
|
if (to > maxScrollLeft) to = maxScrollLeft
|
||||||
} else if (to < start) {
|
} else if (to < start) {
|
||||||
if (to < 0) to = 0
|
if (to < 0) to = 0
|
||||||
@@ -173,7 +173,7 @@ const handleScrollX = (
|
|||||||
|
|
||||||
let currentTime = 0
|
let currentTime = 0
|
||||||
let val: number
|
let val: number
|
||||||
let key = Math.random()
|
const key = Math.random()
|
||||||
|
|
||||||
const animateScroll = () => {
|
const animateScroll = () => {
|
||||||
element.lx_scrollTimeout = undefined
|
element.lx_scrollTimeout = undefined
|
||||||
@@ -272,7 +272,7 @@ const handleScrollXR = (
|
|||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
const start = element.scrollLeft || (element.scrollX as number) || 0
|
const start = element.scrollLeft || (element.scrollX as number) || 0
|
||||||
if (to < start) {
|
if (to < start) {
|
||||||
let maxScrollLeft = -element.scrollWidth + element.clientWidth
|
const maxScrollLeft = -element.scrollWidth + element.clientWidth
|
||||||
if (to < maxScrollLeft) to = maxScrollLeft
|
if (to < maxScrollLeft) to = maxScrollLeft
|
||||||
} else if (to > start) {
|
} else if (to > start) {
|
||||||
if (to > 0) to = 0
|
if (to > 0) to = 0
|
||||||
@@ -290,7 +290,7 @@ const handleScrollXR = (
|
|||||||
|
|
||||||
let currentTime = 0
|
let currentTime = 0
|
||||||
let val: number
|
let val: number
|
||||||
let key = Math.random()
|
const key = Math.random()
|
||||||
|
|
||||||
const animateScroll = () => {
|
const animateScroll = () => {
|
||||||
element.lx_scrollTimeout = undefined
|
element.lx_scrollTimeout = undefined
|
||||||
@@ -371,7 +371,7 @@ export const scrollXRTo = (
|
|||||||
/**
|
/**
|
||||||
* 设置标题
|
* 设置标题
|
||||||
*/
|
*/
|
||||||
let dom_title = document.getElementsByTagName('title')[0]
|
const dom_title = document.getElementsByTagName('title')[0]
|
||||||
export const setTitle = (title: string | null) => {
|
export const setTitle = (title: string | null) => {
|
||||||
title ||= 'LX Music'
|
title ||= 'LX Music'
|
||||||
dom_title.innerText = title
|
dom_title.innerText = title
|
||||||
|
|||||||
@@ -51,14 +51,14 @@ export default {
|
|||||||
...sources,
|
...sources,
|
||||||
init() {
|
init() {
|
||||||
const tasks = []
|
const tasks = []
|
||||||
for (let source of sources.sources) {
|
for (const source of sources.sources) {
|
||||||
let sm = sources[source.id]
|
const sm = sources[source.id]
|
||||||
sm && sm.init && tasks.push(sm.init())
|
sm && sm.init && tasks.push(sm.init())
|
||||||
}
|
}
|
||||||
return Promise.all(tasks)
|
return Promise.all(tasks)
|
||||||
},
|
},
|
||||||
async searchMusic({ name, singer, source: s, limit = 25 }) {
|
async searchMusic({ name, singer, source: s, limit = 25 }) {
|
||||||
const trimStr = (str) => (typeof str == 'string' ? str.trim() : str)
|
const trimStr = (str) => (typeof str === 'string' ? str.trim() : str)
|
||||||
const musicName = trimStr(name)
|
const musicName = trimStr(name)
|
||||||
const tasks = []
|
const tasks = []
|
||||||
const excludeSource = ['xm']
|
const excludeSource = ['xm']
|
||||||
@@ -106,7 +106,7 @@ export default {
|
|||||||
const getIntv = (interval) => {
|
const getIntv = (interval) => {
|
||||||
if (!interval) return 0
|
if (!interval) return 0
|
||||||
// if (musicInfo._interval) return musicInfo._interval
|
// if (musicInfo._interval) return musicInfo._interval
|
||||||
let intvArr = interval.split(':')
|
const intvArr = interval.split(':')
|
||||||
let intv = 0
|
let intv = 0
|
||||||
let unit = 1
|
let unit = 1
|
||||||
while (intvArr.length) {
|
while (intvArr.length) {
|
||||||
@@ -115,9 +115,9 @@ export default {
|
|||||||
}
|
}
|
||||||
return intv
|
return intv
|
||||||
}
|
}
|
||||||
const trimStr = (str) => (typeof str == 'string' ? str.trim() : str || '')
|
const trimStr = (str) => (typeof str === 'string' ? str.trim() : str || '')
|
||||||
const filterStr = (str) =>
|
const filterStr = (str) =>
|
||||||
typeof str == 'string'
|
typeof str === 'string'
|
||||||
? str.replace(/\s|'|\.|,|,|&|"|、|\(|\)|(|)|`|~|-|<|>|\||\/|\]|\[|!|!/g, '')
|
? str.replace(/\s|'|\.|,|,|&|"|、|\(|\)|(|)|`|~|-|<|>|\||\/|\]|\[|!|!/g, '')
|
||||||
: String(str || '')
|
: String(str || '')
|
||||||
const fMusicName = filterStr(name).toLowerCase()
|
const fMusicName = filterStr(name).toLowerCase()
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export default {
|
|||||||
)
|
)
|
||||||
if (!albumList.info) return Promise.reject(new Error('Get album list failed.'))
|
if (!albumList.info) return Promise.reject(new Error('Get album list failed.'))
|
||||||
|
|
||||||
let result = await getMusicInfosByList(albumList.info)
|
const result = await getMusicInfosByList(albumList.info)
|
||||||
|
|
||||||
const info = await this.getAlbumInfo(id)
|
const info = await this.getAlbumInfo(id)
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default {
|
|||||||
// const res_id = (await getMusicInfoRaw(hash)).classification?.[0]?.res_id
|
// const res_id = (await getMusicInfoRaw(hash)).classification?.[0]?.res_id
|
||||||
// if (!res_id) throw new Error('获取评论失败')
|
// if (!res_id) throw new Error('获取评论失败')
|
||||||
|
|
||||||
let timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const params = `dfid=0&mid=16249512204336365674023395779019&clienttime=${timestamp}&uuid=0&extdata=${hash}&appid=1005&code=fc4be23b4e972707f36b8a828a93ba8a&schash=${hash}&clientver=11409&p=${page}&clienttoken=&pagesize=${limit}&ver=10&kugouid=0`
|
const params = `dfid=0&mid=16249512204336365674023395779019&clienttime=${timestamp}&uuid=0&extdata=${hash}&appid=1005&code=fc4be23b4e972707f36b8a828a93ba8a&schash=${hash}&clientver=11409&p=${page}&clienttoken=&pagesize=${limit}&ver=10&kugouid=0`
|
||||||
// const params = `appid=1005&clienttime=${timestamp}&clienttoken=0&clientver=11409&code=fc4be23b4e972707f36b8a828a93ba8a&dfid=0&extdata=${hash}&kugouid=0&mid=16249512204336365674023395779019&mixsongid=${res_id}&p=${page}&pagesize=${limit}&uuid=0&ver=10`
|
// const params = `appid=1005&clienttime=${timestamp}&clienttoken=0&clientver=11409&code=fc4be23b4e972707f36b8a828a93ba8a&dfid=0&extdata=${hash}&kugouid=0&mid=16249512204336365674023395779019&mixsongid=${res_id}&p=${page}&pagesize=${limit}&uuid=0&ver=10`
|
||||||
const _requestObj = httpFetch(
|
const _requestObj = httpFetch(
|
||||||
@@ -40,7 +40,7 @@ export default {
|
|||||||
async getHotComment({ hash }, page = 1, limit = 20) {
|
async getHotComment({ hash }, page = 1, limit = 20) {
|
||||||
// console.log(songmid)
|
// console.log(songmid)
|
||||||
if (this._requestObj2) this._requestObj2.cancelHttp()
|
if (this._requestObj2) this._requestObj2.cancelHttp()
|
||||||
let timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const params = `dfid=0&mid=16249512204336365674023395779019&clienttime=${timestamp}&uuid=0&extdata=${hash}&appid=1005&code=fc4be23b4e972707f36b8a828a93ba8a&schash=${hash}&clientver=11409&p=${page}&clienttoken=&pagesize=${limit}&ver=10&kugouid=0`
|
const params = `dfid=0&mid=16249512204336365674023395779019&clienttime=${timestamp}&uuid=0&extdata=${hash}&appid=1005&code=fc4be23b4e972707f36b8a828a93ba8a&schash=${hash}&clientver=11409&p=${page}&clienttoken=&pagesize=${limit}&ver=10&kugouid=0`
|
||||||
// https://github.com/GitHub-ZC/wp_MusicApi/blob/bf9307dd138dc8ac6c4f7de29361209d4f5b665f/routes/v1/kugou/comment.js#L53
|
// https://github.com/GitHub-ZC/wp_MusicApi/blob/bf9307dd138dc8ac6c4f7de29361209d4f5b665f/routes/v1/kugou/comment.js#L53
|
||||||
const _requestObj2 = httpFetch(
|
const _requestObj2 = httpFetch(
|
||||||
@@ -94,7 +94,7 @@ export default {
|
|||||||
},
|
},
|
||||||
filterComment(rawList) {
|
filterComment(rawList) {
|
||||||
return rawList.map((item) => {
|
return rawList.map((item) => {
|
||||||
let data = {
|
const data = {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
text: decodeName(
|
text: decodeName(
|
||||||
(item.atlist ? this.replaceAt(item.content, item.atlist) : item.content) || ''
|
(item.atlist ? this.replaceAt(item.content, item.atlist) : item.content) || ''
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { httpFetch } from '../../request'
|
|||||||
import { decodeName, formatPlayTime, sizeFormate } from '../index'
|
import { decodeName, formatPlayTime, sizeFormate } from '../index'
|
||||||
import { formatSingerName } from '../utils'
|
import { formatSingerName } from '../utils'
|
||||||
|
|
||||||
let boardList = [
|
const boardList = [
|
||||||
{ id: 'kg__8888', name: 'TOP500', bangid: '8888' },
|
{ id: 'kg__8888', name: 'TOP500', bangid: '8888' },
|
||||||
{ id: 'kg__6666', name: '飙升榜', bangid: '6666' },
|
{ id: 'kg__6666', name: '飙升榜', bangid: '6666' },
|
||||||
{ id: 'kg__59703', name: '蜂鸟流行音乐榜', bangid: '59703' },
|
{ id: 'kg__59703', name: '蜂鸟流行音乐榜', bangid: '59703' },
|
||||||
@@ -137,7 +137,7 @@ export default {
|
|||||||
return requestDataObj.promise
|
return requestDataObj.promise
|
||||||
},
|
},
|
||||||
getSinger(singers) {
|
getSinger(singers) {
|
||||||
let arr = []
|
const arr = []
|
||||||
singers.forEach((singer) => {
|
singers.forEach((singer) => {
|
||||||
arr.push(singer.author_name)
|
arr.push(singer.author_name)
|
||||||
})
|
})
|
||||||
@@ -149,7 +149,7 @@ export default {
|
|||||||
const types = []
|
const types = []
|
||||||
const _types = {}
|
const _types = {}
|
||||||
if (item.filesize !== 0) {
|
if (item.filesize !== 0) {
|
||||||
let size = sizeFormate(item.filesize)
|
const size = sizeFormate(item.filesize)
|
||||||
types.push({ type: '128k', size, hash: item.hash })
|
types.push({ type: '128k', size, hash: item.hash })
|
||||||
_types['128k'] = {
|
_types['128k'] = {
|
||||||
size,
|
size,
|
||||||
@@ -157,7 +157,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item['320filesize'] !== 0) {
|
if (item['320filesize'] !== 0) {
|
||||||
let size = sizeFormate(item['320filesize'])
|
const size = sizeFormate(item['320filesize'])
|
||||||
types.push({ type: '320k', size, hash: item['320hash'] })
|
types.push({ type: '320k', size, hash: item['320hash'] })
|
||||||
_types['320k'] = {
|
_types['320k'] = {
|
||||||
size,
|
size,
|
||||||
@@ -165,7 +165,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.sqfilesize !== 0) {
|
if (item.sqfilesize !== 0) {
|
||||||
let size = sizeFormate(item.sqfilesize)
|
const size = sizeFormate(item.sqfilesize)
|
||||||
types.push({ type: 'flac', size, hash: item.sqhash })
|
types.push({ type: 'flac', size, hash: item.sqhash })
|
||||||
_types.flac = {
|
_types.flac = {
|
||||||
size,
|
size,
|
||||||
@@ -173,7 +173,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.filesize_high !== 0) {
|
if (item.filesize_high !== 0) {
|
||||||
let size = sizeFormate(item.filesize_high)
|
const size = sizeFormate(item.filesize_high)
|
||||||
types.push({ type: 'flac24bit', size, hash: item.hash_high })
|
types.push({ type: 'flac24bit', size, hash: item.hash_high })
|
||||||
_types.flac24bit = {
|
_types.flac24bit = {
|
||||||
size,
|
size,
|
||||||
@@ -201,7 +201,7 @@ export default {
|
|||||||
|
|
||||||
filterBoardsData(rawList) {
|
filterBoardsData(rawList) {
|
||||||
// console.log(rawList)
|
// console.log(rawList)
|
||||||
let list = []
|
const list = []
|
||||||
for (const board of rawList) {
|
for (const board of rawList) {
|
||||||
if (board.isvol != 1) continue
|
if (board.isvol != 1) continue
|
||||||
list.push({
|
list.push({
|
||||||
@@ -243,9 +243,9 @@ export default {
|
|||||||
if (body.errcode != 0) return this.getList(bangid, page, retryNum)
|
if (body.errcode != 0) return this.getList(bangid, page, retryNum)
|
||||||
|
|
||||||
// console.log(body)
|
// console.log(body)
|
||||||
let total = body.data.total
|
const total = body.data.total
|
||||||
let limit = 100
|
const limit = 100
|
||||||
let listData = this.filterData(body.data.info)
|
const listData = this.filterData(body.data.info)
|
||||||
// console.log(listData)
|
// console.log(listData)
|
||||||
return {
|
return {
|
||||||
total,
|
total,
|
||||||
@@ -256,7 +256,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getDetailPageUrl(id) {
|
getDetailPageUrl(id) {
|
||||||
if (typeof id == 'string') id = id.replace('kg__', '')
|
if (typeof id === 'string') id = id.replace('kg__', '')
|
||||||
return `https://www.kugou.com/yy/rank/home/1-${id}.html`
|
return `https://www.kugou.com/yy/rank/home/1-${id}.html`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { decodeKrc } from '../../../../common/utils/lyricUtils/kg'
|
|||||||
export default {
|
export default {
|
||||||
getIntv(interval) {
|
getIntv(interval) {
|
||||||
if (!interval) return 0
|
if (!interval) return 0
|
||||||
let intvArr = interval.split(':')
|
const intvArr = interval.split(':')
|
||||||
let intv = 0
|
let intv = 0
|
||||||
let unit = 1
|
let unit = 1
|
||||||
while (intvArr.length) {
|
while (intvArr.length) {
|
||||||
@@ -36,7 +36,7 @@ export default {
|
|||||||
// return requestObj
|
// return requestObj
|
||||||
// },
|
// },
|
||||||
searchLyric(name, hash, time, tryNum = 0) {
|
searchLyric(name, hash, time, tryNum = 0) {
|
||||||
let requestObj = httpFetch(
|
const requestObj = httpFetch(
|
||||||
`http://lyrics.kugou.com/search?ver=1&man=yes&client=pc&keyword=${encodeURIComponent(name)}&hash=${hash}&timelength=${time}&lrctxt=1`,
|
`http://lyrics.kugou.com/search?ver=1&man=yes&client=pc&keyword=${encodeURIComponent(name)}&hash=${hash}&timelength=${time}&lrctxt=1`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
@@ -49,12 +49,12 @@ export default {
|
|||||||
requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
|
requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
|
||||||
if (statusCode !== 200) {
|
if (statusCode !== 200) {
|
||||||
if (tryNum > 5) return Promise.reject(new Error('歌词获取失败'))
|
if (tryNum > 5) return Promise.reject(new Error('歌词获取失败'))
|
||||||
let tryRequestObj = this.searchLyric(name, hash, time, ++tryNum)
|
const tryRequestObj = this.searchLyric(name, hash, time, ++tryNum)
|
||||||
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
|
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
|
||||||
return tryRequestObj.promise
|
return tryRequestObj.promise
|
||||||
}
|
}
|
||||||
if (body.candidates.length) {
|
if (body.candidates.length) {
|
||||||
let info = body.candidates[0]
|
const info = body.candidates[0]
|
||||||
return {
|
return {
|
||||||
id: info.id,
|
id: info.id,
|
||||||
accessKey: info.accesskey,
|
accessKey: info.accesskey,
|
||||||
@@ -66,7 +66,7 @@ export default {
|
|||||||
return requestObj
|
return requestObj
|
||||||
},
|
},
|
||||||
getLyricDownload(id, accessKey, fmt, tryNum = 0) {
|
getLyricDownload(id, accessKey, fmt, tryNum = 0) {
|
||||||
let requestObj = httpFetch(
|
const requestObj = httpFetch(
|
||||||
`http://lyrics.kugou.com/download?ver=1&client=pc&id=${id}&accesskey=${accessKey}&fmt=${fmt}&charset=utf8`,
|
`http://lyrics.kugou.com/download?ver=1&client=pc&id=${id}&accesskey=${accessKey}&fmt=${fmt}&charset=utf8`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
@@ -79,7 +79,7 @@ export default {
|
|||||||
requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
|
requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
|
||||||
if (statusCode !== 200) {
|
if (statusCode !== 200) {
|
||||||
if (tryNum > 5) return Promise.reject(new Error('歌词获取失败'))
|
if (tryNum > 5) return Promise.reject(new Error('歌词获取失败'))
|
||||||
let tryRequestObj = this.getLyric(id, accessKey, fmt, ++tryNum)
|
const tryRequestObj = this.getLyric(id, accessKey, fmt, ++tryNum)
|
||||||
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
|
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
|
||||||
return tryRequestObj.promise
|
return tryRequestObj.promise
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ export default {
|
|||||||
return requestObj
|
return requestObj
|
||||||
},
|
},
|
||||||
getLyric(songInfo, tryNum = 0) {
|
getLyric(songInfo, tryNum = 0) {
|
||||||
let requestObj = this.searchLyric(
|
const requestObj = this.searchLyric(
|
||||||
songInfo.name,
|
songInfo.name,
|
||||||
songInfo.hash,
|
songInfo.hash,
|
||||||
songInfo._interval || this.getIntv(songInfo.interval)
|
songInfo._interval || this.getIntv(songInfo.interval)
|
||||||
@@ -111,7 +111,7 @@ export default {
|
|||||||
requestObj.promise = requestObj.promise.then((result) => {
|
requestObj.promise = requestObj.promise.then((result) => {
|
||||||
if (!result) return Promise.reject(new Error('Get lyric failed'))
|
if (!result) return Promise.reject(new Error('Get lyric failed'))
|
||||||
|
|
||||||
let requestObj2 = this.getLyricDownload(result.id, result.accessKey, result.fmt)
|
const requestObj2 = this.getLyricDownload(result.id, result.accessKey, result.fmt)
|
||||||
|
|
||||||
requestObj.cancelHttp = requestObj2.cancelHttp.bind(requestObj2)
|
requestObj.cancelHttp = requestObj2.cancelHttp.bind(requestObj2)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { decodeName, formatPlayTime, sizeFormate } from '../../index'
|
|||||||
import { createHttpFetch } from './util'
|
import { createHttpFetch } from './util'
|
||||||
|
|
||||||
const createGetMusicInfosTask = (hashs) => {
|
const createGetMusicInfosTask = (hashs) => {
|
||||||
let data = {
|
const data = {
|
||||||
area_code: '1',
|
area_code: '1',
|
||||||
show_privilege: 1,
|
show_privilege: 1,
|
||||||
show_album_info: '1',
|
show_album_info: '1',
|
||||||
@@ -16,13 +16,13 @@ const createGetMusicInfosTask = (hashs) => {
|
|||||||
fields: 'album_info,author_name,audio_info,ori_audio_name,base,songname,classification'
|
fields: 'album_info,author_name,audio_info,ori_audio_name,base,songname,classification'
|
||||||
}
|
}
|
||||||
let list = hashs
|
let list = hashs
|
||||||
let tasks = []
|
const tasks = []
|
||||||
while (list.length) {
|
while (list.length) {
|
||||||
tasks.push(Object.assign({ data: list.slice(0, 100) }, data))
|
tasks.push(Object.assign({ data: list.slice(0, 100) }, data))
|
||||||
if (list.length < 100) break
|
if (list.length < 100) break
|
||||||
list = list.slice(100)
|
list = list.slice(100)
|
||||||
}
|
}
|
||||||
let url = 'http://gateway.kugou.com/v3/album_audio/audio'
|
const url = 'http://gateway.kugou.com/v3/album_audio/audio'
|
||||||
return tasks.map((task) =>
|
return tasks.map((task) =>
|
||||||
createHttpFetch(url, {
|
createHttpFetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -41,8 +41,8 @@ const createGetMusicInfosTask = (hashs) => {
|
|||||||
|
|
||||||
export const filterMusicInfoList = (rawList) => {
|
export const filterMusicInfoList = (rawList) => {
|
||||||
// console.log(rawList)
|
// console.log(rawList)
|
||||||
let ids = new Set()
|
const ids = new Set()
|
||||||
let list = []
|
const list = []
|
||||||
rawList.forEach((item) => {
|
rawList.forEach((item) => {
|
||||||
if (!item) return
|
if (!item) return
|
||||||
if (ids.has(item.audio_info.audio_id)) return
|
if (ids.has(item.audio_info.audio_id)) return
|
||||||
@@ -50,7 +50,7 @@ export const filterMusicInfoList = (rawList) => {
|
|||||||
const types = []
|
const types = []
|
||||||
const _types = {}
|
const _types = {}
|
||||||
if (item.audio_info.filesize !== '0') {
|
if (item.audio_info.filesize !== '0') {
|
||||||
let size = sizeFormate(parseInt(item.audio_info.filesize))
|
const size = sizeFormate(parseInt(item.audio_info.filesize))
|
||||||
types.push({ type: '128k', size, hash: item.audio_info.hash })
|
types.push({ type: '128k', size, hash: item.audio_info.hash })
|
||||||
_types['128k'] = {
|
_types['128k'] = {
|
||||||
size,
|
size,
|
||||||
@@ -58,7 +58,7 @@ export const filterMusicInfoList = (rawList) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.audio_info.filesize_320 !== '0') {
|
if (item.audio_info.filesize_320 !== '0') {
|
||||||
let size = sizeFormate(parseInt(item.audio_info.filesize_320))
|
const size = sizeFormate(parseInt(item.audio_info.filesize_320))
|
||||||
types.push({ type: '320k', size, hash: item.audio_info.hash_320 })
|
types.push({ type: '320k', size, hash: item.audio_info.hash_320 })
|
||||||
_types['320k'] = {
|
_types['320k'] = {
|
||||||
size,
|
size,
|
||||||
@@ -66,7 +66,7 @@ export const filterMusicInfoList = (rawList) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.audio_info.filesize_flac !== '0') {
|
if (item.audio_info.filesize_flac !== '0') {
|
||||||
let size = sizeFormate(parseInt(item.audio_info.filesize_flac))
|
const size = sizeFormate(parseInt(item.audio_info.filesize_flac))
|
||||||
types.push({ type: 'flac', size, hash: item.audio_info.hash_flac })
|
types.push({ type: 'flac', size, hash: item.audio_info.hash_flac })
|
||||||
_types.flac = {
|
_types.flac = {
|
||||||
size,
|
size,
|
||||||
@@ -74,7 +74,7 @@ export const filterMusicInfoList = (rawList) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.audio_info.filesize_high !== '0') {
|
if (item.audio_info.filesize_high !== '0') {
|
||||||
let size = sizeFormate(parseInt(item.audio_info.filesize_high))
|
const size = sizeFormate(parseInt(item.audio_info.filesize_high))
|
||||||
types.push({ type: 'flac24bit', size, hash: item.audio_info.hash_high })
|
types.push({ type: 'flac24bit', size, hash: item.audio_info.hash_high })
|
||||||
_types.flac24bit = {
|
_types.flac24bit = {
|
||||||
size,
|
size,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default {
|
|||||||
const types = []
|
const types = []
|
||||||
const _types = {}
|
const _types = {}
|
||||||
if (rawData.FileSize !== 0) {
|
if (rawData.FileSize !== 0) {
|
||||||
let size = sizeFormate(rawData.FileSize)
|
const size = sizeFormate(rawData.FileSize)
|
||||||
types.push({ type: '128k', size, hash: rawData.FileHash })
|
types.push({ type: '128k', size, hash: rawData.FileHash })
|
||||||
_types['128k'] = {
|
_types['128k'] = {
|
||||||
size,
|
size,
|
||||||
@@ -25,7 +25,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rawData.HQFileSize !== 0) {
|
if (rawData.HQFileSize !== 0) {
|
||||||
let size = sizeFormate(rawData.HQFileSize)
|
const size = sizeFormate(rawData.HQFileSize)
|
||||||
types.push({ type: '320k', size, hash: rawData.HQFileHash })
|
types.push({ type: '320k', size, hash: rawData.HQFileHash })
|
||||||
_types['320k'] = {
|
_types['320k'] = {
|
||||||
size,
|
size,
|
||||||
@@ -33,7 +33,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rawData.SQFileSize !== 0) {
|
if (rawData.SQFileSize !== 0) {
|
||||||
let size = sizeFormate(rawData.SQFileSize)
|
const size = sizeFormate(rawData.SQFileSize)
|
||||||
types.push({ type: 'flac', size, hash: rawData.SQFileHash })
|
types.push({ type: 'flac', size, hash: rawData.SQFileHash })
|
||||||
_types.flac = {
|
_types.flac = {
|
||||||
size,
|
size,
|
||||||
@@ -41,7 +41,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rawData.ResFileSize !== 0) {
|
if (rawData.ResFileSize !== 0) {
|
||||||
let size = sizeFormate(rawData.ResFileSize)
|
const size = sizeFormate(rawData.ResFileSize)
|
||||||
types.push({ type: 'flac24bit', size, hash: rawData.ResFileHash })
|
types.push({ type: 'flac24bit', size, hash: rawData.ResFileHash })
|
||||||
_types.flac24bit = {
|
_types.flac24bit = {
|
||||||
size,
|
size,
|
||||||
@@ -67,7 +67,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleResult(rawData) {
|
handleResult(rawData) {
|
||||||
let ids = new Set()
|
const ids = new Set()
|
||||||
const list = []
|
const list = []
|
||||||
rawData.forEach((item) => {
|
rawData.forEach((item) => {
|
||||||
const key = item.Audioid + item.FileHash
|
const key = item.Audioid + item.FileHash
|
||||||
@@ -89,7 +89,7 @@ export default {
|
|||||||
// http://newlyric.kuwo.cn/newlyric.lrc?62355680
|
// http://newlyric.kuwo.cn/newlyric.lrc?62355680
|
||||||
return this.musicSearch(str, page, limit).then((result) => {
|
return this.musicSearch(str, page, limit).then((result) => {
|
||||||
if (!result || result.error_code !== 0) return this.search(str, page, limit, retryNum)
|
if (!result || result.error_code !== 0) return this.search(str, page, limit, retryNum)
|
||||||
let list = this.handleResult(result.data.lists)
|
const list = this.handleResult(result.data.lists)
|
||||||
|
|
||||||
if (list == null) return this.search(str, page, limit, retryNum)
|
if (list == null) return this.search(str, page, limit, retryNum)
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default {
|
|||||||
})
|
})
|
||||||
return requestObj.promise.then(({ body }) => {
|
return requestObj.promise.then(({ body }) => {
|
||||||
if (body.error_code !== 0) return Promise.reject(new Error('图片获取失败'))
|
if (body.error_code !== 0) return Promise.reject(new Error('图片获取失败'))
|
||||||
let info = body.data[0].info
|
const info = body.data[0].info
|
||||||
const img = info.imgsize ? info.image.replace('{size}', info.imgsize[0]) : info.image
|
const img = info.imgsize ? info.image.replace('{size}', info.imgsize[0]) : info.image
|
||||||
if (!img) return Promise.reject(new Error('Pic get failed'))
|
if (!img) return Promise.reject(new Error('Pic get failed'))
|
||||||
return img
|
return img
|
||||||
|
|||||||
@@ -71,10 +71,10 @@ export default {
|
|||||||
if (tryNum > 2) throw new Error('try max num')
|
if (tryNum > 2) throw new Error('try max num')
|
||||||
|
|
||||||
const { body } = await httpFetch(this.getSongListDetailUrl(id)).promise
|
const { body } = await httpFetch(this.getSongListDetailUrl(id)).promise
|
||||||
let listData = body.match(this.regExps.listData)
|
const listData = body.match(this.regExps.listData)
|
||||||
let listInfo = body.match(this.regExps.listInfo)
|
const listInfo = body.match(this.regExps.listInfo)
|
||||||
if (!listData) return this.getListDetailBySpecialId(id, page, ++tryNum)
|
if (!listData) return this.getListDetailBySpecialId(id, page, ++tryNum)
|
||||||
let list = await this.getMusicInfos(JSON.parse(listData[1]))
|
const list = await this.getMusicInfos(JSON.parse(listData[1]))
|
||||||
// listData = this.filterData(JSON.parse(listData[1]))
|
// listData = this.filterData(JSON.parse(listData[1]))
|
||||||
let name
|
let name
|
||||||
let pic
|
let pic
|
||||||
@@ -82,7 +82,7 @@ export default {
|
|||||||
name = listInfo[1]
|
name = listInfo[1]
|
||||||
pic = listInfo[2]
|
pic = listInfo[2]
|
||||||
}
|
}
|
||||||
let desc = this.parseHtmlDesc(body)
|
const desc = this.parseHtmlDesc(body)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
list,
|
list,
|
||||||
@@ -116,7 +116,7 @@ export default {
|
|||||||
const result = []
|
const result = []
|
||||||
if (rawData.status !== 1) return result
|
if (rawData.status !== 1) return result
|
||||||
for (const key of Object.keys(rawData.data)) {
|
for (const key of Object.keys(rawData.data)) {
|
||||||
let tag = rawData.data[key]
|
const tag = rawData.data[key]
|
||||||
result.push({
|
result.push({
|
||||||
id: tag.special_id,
|
id: tag.special_id,
|
||||||
name: tag.special_name,
|
name: tag.special_name,
|
||||||
@@ -219,7 +219,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
createTask(hashs) {
|
createTask(hashs) {
|
||||||
let data = {
|
const data = {
|
||||||
area_code: '1',
|
area_code: '1',
|
||||||
show_privilege: 1,
|
show_privilege: 1,
|
||||||
show_album_info: '1',
|
show_album_info: '1',
|
||||||
@@ -233,13 +233,13 @@ export default {
|
|||||||
fields: 'album_info,author_name,audio_info,ori_audio_name,base,songname'
|
fields: 'album_info,author_name,audio_info,ori_audio_name,base,songname'
|
||||||
}
|
}
|
||||||
let list = hashs
|
let list = hashs
|
||||||
let tasks = []
|
const tasks = []
|
||||||
while (list.length) {
|
while (list.length) {
|
||||||
tasks.push(Object.assign({ data: list.slice(0, 100) }, data))
|
tasks.push(Object.assign({ data: list.slice(0, 100) }, data))
|
||||||
if (list.length < 100) break
|
if (list.length < 100) break
|
||||||
list = list.slice(100)
|
list = list.slice(100)
|
||||||
}
|
}
|
||||||
let url = 'http://gateway.kugou.com/v2/album_audio/audio'
|
const url = 'http://gateway.kugou.com/v2/album_audio/audio'
|
||||||
return tasks.map((task) =>
|
return tasks.map((task) =>
|
||||||
this.createHttp(url, {
|
this.createHttp(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -283,7 +283,7 @@ export default {
|
|||||||
// console.log(songInfo)
|
// console.log(songInfo)
|
||||||
// type 1单曲,2歌单,3电台,4酷狗码,5别人的播放队列
|
// type 1单曲,2歌单,3电台,4酷狗码,5别人的播放队列
|
||||||
let songList
|
let songList
|
||||||
let info = songInfo.info
|
const info = songInfo.info
|
||||||
switch (info.type) {
|
switch (info.type) {
|
||||||
case 2:
|
case 2:
|
||||||
if (!info.global_collection_id) return this.getListDetailBySpecialId(info.id)
|
if (!info.global_collection_id) return this.getListDetailBySpecialId(info.id)
|
||||||
@@ -319,7 +319,7 @@ export default {
|
|||||||
})
|
})
|
||||||
// console.log(songList)
|
// console.log(songList)
|
||||||
}
|
}
|
||||||
let list = await this.getMusicInfos(songList || songInfo.list)
|
const list = await this.getMusicInfos(songList || songInfo.list)
|
||||||
return {
|
return {
|
||||||
list,
|
list,
|
||||||
page: 1,
|
page: 1,
|
||||||
@@ -354,7 +354,7 @@ export default {
|
|||||||
this.getUserListDetail5(chain)
|
this.getUserListDetail5(chain)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
let list = await this.getMusicInfos(songInfo.list)
|
const list = await this.getMusicInfos(songInfo.list)
|
||||||
// console.log(info, songInfo)
|
// console.log(info, songInfo)
|
||||||
return {
|
return {
|
||||||
list,
|
list,
|
||||||
@@ -373,7 +373,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
deDuplication(datas) {
|
deDuplication(datas) {
|
||||||
let ids = new Set()
|
const ids = new Set()
|
||||||
return datas.filter(({ hash }) => {
|
return datas.filter(({ hash }) => {
|
||||||
if (ids.has(hash)) return false
|
if (ids.has(hash)) return false
|
||||||
ids.add(hash)
|
ids.add(hash)
|
||||||
@@ -408,9 +408,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async getUserListDetailByLink({ info }, link) {
|
async getUserListDetailByLink({ info }, link) {
|
||||||
let listInfo = info['0']
|
const listInfo = info['0']
|
||||||
let total = listInfo.count
|
let total = listInfo.count
|
||||||
let tasks = []
|
const tasks = []
|
||||||
let page = 0
|
let page = 0
|
||||||
while (total) {
|
while (total) {
|
||||||
const limit = total > 90 ? 90 : total
|
const limit = total > 90 ? 90 : total
|
||||||
@@ -448,7 +448,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
createGetListDetail2Task(id, total) {
|
createGetListDetail2Task(id, total) {
|
||||||
let tasks = []
|
const tasks = []
|
||||||
let page = 0
|
let page = 0
|
||||||
while (total) {
|
while (total) {
|
||||||
const limit = total > 300 ? 300 : total
|
const limit = total > 300 ? 300 : total
|
||||||
@@ -481,13 +481,13 @@ export default {
|
|||||||
return Promise.all(tasks).then(([...datas]) => datas.flat())
|
return Promise.all(tasks).then(([...datas]) => datas.flat())
|
||||||
},
|
},
|
||||||
async getUserListDetail2(global_collection_id) {
|
async getUserListDetail2(global_collection_id) {
|
||||||
let id = global_collection_id
|
const id = global_collection_id
|
||||||
if (id.length > 1000) throw new Error('get list error')
|
if (id.length > 1000) throw new Error('get list error')
|
||||||
const params =
|
const params =
|
||||||
'appid=1058&specialid=0&global_specialid=' +
|
'appid=1058&specialid=0&global_specialid=' +
|
||||||
id +
|
id +
|
||||||
'&format=jsonp&srcappid=2919&clientver=20000&clienttime=1586163242519&mid=1586163242519&uuid=1586163242519&dfid=-'
|
'&format=jsonp&srcappid=2919&clientver=20000&clienttime=1586163242519&mid=1586163242519&uuid=1586163242519&dfid=-'
|
||||||
let info = await this.createHttp(
|
const info = await this.createHttp(
|
||||||
`https://mobiles.kugou.com/api/v5/special/info_v2?${params}&signature=${signatureParams(params, 'web')}`,
|
`https://mobiles.kugou.com/api/v5/special/info_v2?${params}&signature=${signatureParams(params, 'web')}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
@@ -501,7 +501,7 @@ export default {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
const songInfo = await this.createGetListDetail2Task(id, info.songcount)
|
const songInfo = await this.createGetListDetail2Task(id, info.songcount)
|
||||||
let list = await this.getMusicInfos(songInfo)
|
const list = await this.getMusicInfos(songInfo)
|
||||||
// console.log(info, songInfo, list)
|
// console.log(info, songInfo, list)
|
||||||
return {
|
return {
|
||||||
list,
|
list,
|
||||||
@@ -534,7 +534,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async getUserListDetailByPcChain(chain) {
|
async getUserListDetailByPcChain(chain) {
|
||||||
let key = `${chain}_pc_list`
|
const key = `${chain}_pc_list`
|
||||||
if (this.cache.has(key)) return this.cache.get(key)
|
if (this.cache.has(key)) return this.cache.get(key)
|
||||||
const { body } = await httpFetch(`http://www.kugou.com/share/${chain}.html`, {
|
const { body } = await httpFetch(`http://www.kugou.com/share/${chain}.html`, {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -595,7 +595,7 @@ export default {
|
|||||||
|
|
||||||
async getUserListDetailById(id, page, limit) {
|
async getUserListDetailById(id, page, limit) {
|
||||||
const signature = await handleSignature(id, page, limit)
|
const signature = await handleSignature(id, page, limit)
|
||||||
let info = await this.createHttp(
|
const info = await this.createHttp(
|
||||||
`https://pubsongscdn.kugou.com/v2/get_other_list_file?srcappid=2919&clientver=20000&appid=1058&type=0&module=playlist&page=${page}&pagesize=${limit}&specialid=${id}&signature=${signature}`,
|
`https://pubsongscdn.kugou.com/v2/get_other_list_file?srcappid=2919&clientver=20000&appid=1058&type=0&module=playlist&page=${page}&pagesize=${limit}&specialid=${id}&signature=${signature}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
@@ -608,7 +608,7 @@ export default {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// console.log(info)
|
// console.log(info)
|
||||||
let result = await this.getMusicInfos(info.info)
|
const result = await this.getMusicInfos(info.info)
|
||||||
// console.log(info, songInfo)
|
// console.log(info, songInfo)
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
@@ -621,7 +621,7 @@ export default {
|
|||||||
link.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1')
|
link.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1')
|
||||||
)
|
)
|
||||||
if (link.includes('gcid_')) {
|
if (link.includes('gcid_')) {
|
||||||
let gcid = link.match(/gcid_\w+/)?.[0]
|
const gcid = link.match(/gcid_\w+/)?.[0]
|
||||||
if (gcid) {
|
if (gcid) {
|
||||||
const global_collection_id = await this.decodeGcid(gcid)
|
const global_collection_id = await this.decodeGcid(gcid)
|
||||||
if (global_collection_id) return this.getUserListDetail2(global_collection_id)
|
if (global_collection_id) return this.getUserListDetail2(global_collection_id)
|
||||||
@@ -667,7 +667,7 @@ export default {
|
|||||||
location.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1')
|
location.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1')
|
||||||
)
|
)
|
||||||
if (location.includes('gcid_')) {
|
if (location.includes('gcid_')) {
|
||||||
let gcid = link.match(/gcid_\w+/)?.[0]
|
const gcid = link.match(/gcid_\w+/)?.[0]
|
||||||
if (gcid) {
|
if (gcid) {
|
||||||
const global_collection_id = await this.decodeGcid(gcid)
|
const global_collection_id = await this.decodeGcid(gcid)
|
||||||
if (global_collection_id) return this.getUserListDetail2(global_collection_id)
|
if (global_collection_id) return this.getUserListDetail2(global_collection_id)
|
||||||
@@ -698,7 +698,7 @@ export default {
|
|||||||
// console.log('location', location)
|
// console.log('location', location)
|
||||||
return this.getUserListDetail(location, page, ++retryNum)
|
return this.getUserListDetail(location, page, ++retryNum)
|
||||||
}
|
}
|
||||||
if (typeof body == 'string') {
|
if (typeof body === 'string') {
|
||||||
let global_collection_id = body.match(/"global_collection_id":"(\w+)"/)?.[1]
|
let global_collection_id = body.match(/"global_collection_id":"(\w+)"/)?.[1]
|
||||||
if (!global_collection_id) {
|
if (!global_collection_id) {
|
||||||
let gcid = body.match(/"encode_gic":"(\w+)"/)?.[1]
|
let gcid = body.match(/"encode_gic":"(\w+)"/)?.[1]
|
||||||
@@ -735,7 +735,7 @@ export default {
|
|||||||
const types = []
|
const types = []
|
||||||
const _types = {}
|
const _types = {}
|
||||||
if (item.filesize !== 0) {
|
if (item.filesize !== 0) {
|
||||||
let size = sizeFormate(item.filesize)
|
const size = sizeFormate(item.filesize)
|
||||||
types.push({ type: '128k', size, hash: item.hash })
|
types.push({ type: '128k', size, hash: item.hash })
|
||||||
_types['128k'] = {
|
_types['128k'] = {
|
||||||
size,
|
size,
|
||||||
@@ -743,7 +743,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.filesize_320 !== 0) {
|
if (item.filesize_320 !== 0) {
|
||||||
let size = sizeFormate(item.filesize_320)
|
const size = sizeFormate(item.filesize_320)
|
||||||
types.push({ type: '320k', size, hash: item.hash_320 })
|
types.push({ type: '320k', size, hash: item.hash_320 })
|
||||||
_types['320k'] = {
|
_types['320k'] = {
|
||||||
size,
|
size,
|
||||||
@@ -751,7 +751,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.filesize_ape !== 0) {
|
if (item.filesize_ape !== 0) {
|
||||||
let size = sizeFormate(item.filesize_ape)
|
const size = sizeFormate(item.filesize_ape)
|
||||||
types.push({ type: 'ape', size, hash: item.hash_ape })
|
types.push({ type: 'ape', size, hash: item.hash_ape })
|
||||||
_types.ape = {
|
_types.ape = {
|
||||||
size,
|
size,
|
||||||
@@ -759,7 +759,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.filesize_flac !== 0) {
|
if (item.filesize_flac !== 0) {
|
||||||
let size = sizeFormate(item.filesize_flac)
|
const size = sizeFormate(item.filesize_flac)
|
||||||
types.push({ type: 'flac', size, hash: item.hash_flac })
|
types.push({ type: 'flac', size, hash: item.hash_flac })
|
||||||
_types.flac = {
|
_types.flac = {
|
||||||
size,
|
size,
|
||||||
@@ -849,8 +849,8 @@ export default {
|
|||||||
// hash list filter
|
// hash list filter
|
||||||
filterData2(rawList) {
|
filterData2(rawList) {
|
||||||
// console.log(rawList)
|
// console.log(rawList)
|
||||||
let ids = new Set()
|
const ids = new Set()
|
||||||
let list = []
|
const list = []
|
||||||
rawList.forEach((item) => {
|
rawList.forEach((item) => {
|
||||||
if (!item) return
|
if (!item) return
|
||||||
if (ids.has(item.audio_info.audio_id)) return
|
if (ids.has(item.audio_info.audio_id)) return
|
||||||
@@ -858,7 +858,7 @@ export default {
|
|||||||
const types = []
|
const types = []
|
||||||
const _types = {}
|
const _types = {}
|
||||||
if (item.audio_info.filesize !== '0') {
|
if (item.audio_info.filesize !== '0') {
|
||||||
let size = sizeFormate(parseInt(item.audio_info.filesize))
|
const size = sizeFormate(parseInt(item.audio_info.filesize))
|
||||||
types.push({ type: '128k', size, hash: item.audio_info.hash })
|
types.push({ type: '128k', size, hash: item.audio_info.hash })
|
||||||
_types['128k'] = {
|
_types['128k'] = {
|
||||||
size,
|
size,
|
||||||
@@ -866,7 +866,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.audio_info.filesize_320 !== '0') {
|
if (item.audio_info.filesize_320 !== '0') {
|
||||||
let size = sizeFormate(parseInt(item.audio_info.filesize_320))
|
const size = sizeFormate(parseInt(item.audio_info.filesize_320))
|
||||||
types.push({ type: '320k', size, hash: item.audio_info.hash_320 })
|
types.push({ type: '320k', size, hash: item.audio_info.hash_320 })
|
||||||
_types['320k'] = {
|
_types['320k'] = {
|
||||||
size,
|
size,
|
||||||
@@ -874,7 +874,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.audio_info.filesize_flac !== '0') {
|
if (item.audio_info.filesize_flac !== '0') {
|
||||||
let size = sizeFormate(parseInt(item.audio_info.filesize_flac))
|
const size = sizeFormate(parseInt(item.audio_info.filesize_flac))
|
||||||
types.push({ type: 'flac', size, hash: item.audio_info.hash_flac })
|
types.push({ type: 'flac', size, hash: item.audio_info.hash_flac })
|
||||||
_types.flac = {
|
_types.flac = {
|
||||||
size,
|
size,
|
||||||
@@ -882,7 +882,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.audio_info.filesize_high !== '0') {
|
if (item.audio_info.filesize_high !== '0') {
|
||||||
let size = sizeFormate(parseInt(item.audio_info.filesize_high))
|
const size = sizeFormate(parseInt(item.audio_info.filesize_high))
|
||||||
types.push({ type: 'flac24bit', size, hash: item.audio_info.hash_high })
|
types.push({ type: 'flac24bit', size, hash: item.audio_info.hash_high })
|
||||||
_types.flac24bit = {
|
_types.flac24bit = {
|
||||||
size,
|
size,
|
||||||
@@ -927,7 +927,7 @@ export default {
|
|||||||
|
|
||||||
// 获取列表数据
|
// 获取列表数据
|
||||||
getList(sortId, tagId, page) {
|
getList(sortId, tagId, page) {
|
||||||
let tasks = [this.getSongList(sortId, tagId, page)]
|
const tasks = [this.getSongList(sortId, tagId, page)]
|
||||||
tasks.push(
|
tasks.push(
|
||||||
this.currentTagInfo.id === tagId
|
this.currentTagInfo.id === tagId
|
||||||
? Promise.resolve(this.currentTagInfo.info)
|
? Promise.resolve(this.currentTagInfo.info)
|
||||||
@@ -964,7 +964,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getDetailPageUrl(id) {
|
getDetailPageUrl(id) {
|
||||||
if (typeof id == 'string') {
|
if (typeof id === 'string') {
|
||||||
if (/^https?:\/\//.test(id)) return id
|
if (/^https?:\/\//.test(id)) return id
|
||||||
id = id.replace('id_', '')
|
id = id.replace('id_', '')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import { httpFetch } from '../../request'
|
|||||||
export const signatureParams = (params, platform = 'android', body = '') => {
|
export const signatureParams = (params, platform = 'android', body = '') => {
|
||||||
let keyparam = 'OIlwieks28dk2k092lksi2UIkp'
|
let keyparam = 'OIlwieks28dk2k092lksi2UIkp'
|
||||||
if (platform === 'web') keyparam = 'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt'
|
if (platform === 'web') keyparam = 'NVPh5oo715z5DIWAeQlhMDsWXXQV4hwt'
|
||||||
let param_list = params.split('&')
|
const param_list = params.split('&')
|
||||||
param_list.sort()
|
param_list.sort()
|
||||||
let sign_params = `${keyparam}${param_list.join('')}${body}${keyparam}`
|
const sign_params = `${keyparam}${param_list.join('')}${body}${keyparam}`
|
||||||
return toMD5(sign_params)
|
return toMD5(sign_params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ export default {
|
|||||||
// console.log(rawList)
|
// console.log(rawList)
|
||||||
// console.log(rawList.length, rawList2.length)
|
// console.log(rawList.length, rawList2.length)
|
||||||
return rawList.map((item, inedx) => {
|
return rawList.map((item, inedx) => {
|
||||||
let formats = item.formats.split('|')
|
const formats = item.formats.split('|')
|
||||||
let types = []
|
const types = []
|
||||||
let _types = {}
|
const _types = {}
|
||||||
if (formats.includes('MP3128')) {
|
if (formats.includes('MP3128')) {
|
||||||
types.push({ type: '128k', size: null })
|
types.push({ type: '128k', size: null })
|
||||||
_types['128k'] = {
|
_types['128k'] = {
|
||||||
|
|||||||
@@ -68,13 +68,13 @@ const kw = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getMusicUrls(musicInfo, cb) {
|
getMusicUrls(musicInfo, cb) {
|
||||||
let tasks = []
|
const tasks = []
|
||||||
let songId = musicInfo.songmid
|
const songId = musicInfo.songmid
|
||||||
musicInfo.types.forEach((type) => {
|
musicInfo.types.forEach((type) => {
|
||||||
tasks.push(kw.getMusicUrl(songId, type.type).promise)
|
tasks.push(kw.getMusicUrl(songId, type.type).promise)
|
||||||
})
|
})
|
||||||
Promise.all(tasks).then((urlInfo) => {
|
Promise.all(tasks).then((urlInfo) => {
|
||||||
let typeUrl = {}
|
const typeUrl = {}
|
||||||
urlInfo.forEach((info) => {
|
urlInfo.forEach((info) => {
|
||||||
typeUrl[info.type] = info.url
|
typeUrl[info.type] = info.url
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ export default {
|
|||||||
|
|
||||||
filterBoardsData(rawList) {
|
filterBoardsData(rawList) {
|
||||||
// console.log(rawList)
|
// console.log(rawList)
|
||||||
let list = []
|
const list = []
|
||||||
for (const board of rawList) {
|
for (const board of rawList) {
|
||||||
if (board.source != '1') continue
|
if (board.source != '1') continue
|
||||||
list.push({
|
list.push({
|
||||||
|
|||||||
@@ -148,8 +148,8 @@ export default {
|
|||||||
}, */
|
}, */
|
||||||
sortLrcArr(arr) {
|
sortLrcArr(arr) {
|
||||||
const lrcSet = new Set()
|
const lrcSet = new Set()
|
||||||
let lrc = []
|
const lrc = []
|
||||||
let lrcT = []
|
const lrcT = []
|
||||||
|
|
||||||
let isLyricx = false
|
let isLyricx = false
|
||||||
for (const item of arr) {
|
for (const item of arr) {
|
||||||
@@ -192,11 +192,11 @@ export default {
|
|||||||
},
|
},
|
||||||
parseLrc(lrc) {
|
parseLrc(lrc) {
|
||||||
const lines = lrc.split(/\r\n|\r|\n/)
|
const lines = lrc.split(/\r\n|\r|\n/)
|
||||||
let tags = []
|
const tags = []
|
||||||
let lrcArr = []
|
const lrcArr = []
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i].trim()
|
const line = lines[i].trim()
|
||||||
let result = timeExp.exec(line)
|
const result = timeExp.exec(line)
|
||||||
if (result) {
|
if (result) {
|
||||||
const text = line.replace(timeExp, '').trim()
|
const text = line.replace(timeExp, '').trim()
|
||||||
let time = RegExp.$1
|
let time = RegExp.$1
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export default {
|
|||||||
// console.log(rawData)
|
// console.log(rawData)
|
||||||
for (let i = 0; i < rawData.length; i++) {
|
for (let i = 0; i < rawData.length; i++) {
|
||||||
const info = rawData[i]
|
const info = rawData[i]
|
||||||
let songId = info.MUSICRID.replace('MUSIC_', '')
|
const songId = info.MUSICRID.replace('MUSIC_', '')
|
||||||
// const format = (info.FORMATS || info.formats).split('|')
|
// const format = (info.FORMATS || info.formats).split('|')
|
||||||
|
|
||||||
if (!info.N_MINFO) {
|
if (!info.N_MINFO) {
|
||||||
@@ -43,7 +43,7 @@ export default {
|
|||||||
const types = []
|
const types = []
|
||||||
const _types = {}
|
const _types = {}
|
||||||
|
|
||||||
let infoArr = info.N_MINFO.split(';')
|
const infoArr = info.N_MINFO.split(';')
|
||||||
for (let info of infoArr) {
|
for (let info of infoArr) {
|
||||||
info = info.match(this.regExps.mInfo)
|
info = info.match(this.regExps.mInfo)
|
||||||
if (info) {
|
if (info) {
|
||||||
@@ -77,7 +77,7 @@ export default {
|
|||||||
}
|
}
|
||||||
types.reverse()
|
types.reverse()
|
||||||
|
|
||||||
let interval = parseInt(info.DURATION)
|
const interval = parseInt(info.DURATION)
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
name: decodeName(info.SONGNAME),
|
name: decodeName(info.SONGNAME),
|
||||||
@@ -109,7 +109,7 @@ export default {
|
|||||||
// console.log(result)
|
// console.log(result)
|
||||||
if (!result || (result.TOTAL !== '0' && result.SHOW === '0'))
|
if (!result || (result.TOTAL !== '0' && result.SHOW === '0'))
|
||||||
return this.search(str, page, limit, ++retryNum)
|
return this.search(str, page, limit, ++retryNum)
|
||||||
let list = this.handleResult(result.abslist)
|
const list = this.handleResult(result.abslist)
|
||||||
|
|
||||||
if (list == null) return this.search(str, page, limit, ++retryNum)
|
if (list == null) return this.search(str, page, limit, ++retryNum)
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export default {
|
|||||||
let id
|
let id
|
||||||
let type
|
let type
|
||||||
if (tagId) {
|
if (tagId) {
|
||||||
let arr = tagId.split('-')
|
const arr = tagId.split('-')
|
||||||
id = arr[0]
|
id = arr[0]
|
||||||
type = arr[1]
|
type = arr[1]
|
||||||
} else {
|
} else {
|
||||||
@@ -235,9 +235,9 @@ export default {
|
|||||||
|
|
||||||
filterBDListDetail(rawList) {
|
filterBDListDetail(rawList) {
|
||||||
return rawList.map((item) => {
|
return rawList.map((item) => {
|
||||||
let types = []
|
const types = []
|
||||||
let _types = {}
|
const _types = {}
|
||||||
for (let info of item.audios) {
|
for (const info of item.audios) {
|
||||||
info.size = info.size?.toLocaleUpperCase()
|
info.size = info.size?.toLocaleUpperCase()
|
||||||
switch (info.bitrate) {
|
switch (info.bitrate) {
|
||||||
case '4000':
|
case '4000':
|
||||||
@@ -415,9 +415,9 @@ export default {
|
|||||||
filterListDetail(rawData) {
|
filterListDetail(rawData) {
|
||||||
// console.log(rawData)
|
// console.log(rawData)
|
||||||
return rawData.map((item) => {
|
return rawData.map((item) => {
|
||||||
let infoArr = item.N_MINFO.split(';')
|
const infoArr = item.N_MINFO.split(';')
|
||||||
let types = []
|
const types = []
|
||||||
let _types = {}
|
const _types = {}
|
||||||
for (let info of infoArr) {
|
for (let info of infoArr) {
|
||||||
info = info.match(this.regExps.mInfo)
|
info = info.match(this.regExps.mInfo)
|
||||||
if (info) {
|
if (info) {
|
||||||
@@ -478,7 +478,7 @@ export default {
|
|||||||
getDetailPageUrl(id) {
|
getDetailPageUrl(id) {
|
||||||
if (/[?&:/]/.test(id)) id = id.replace(this.regExps.listDetailLink, '$1')
|
if (/[?&:/]/.test(id)) id = id.replace(this.regExps.listDetailLink, '$1')
|
||||||
else if (/^digest-/.test(id)) {
|
else if (/^digest-/.test(id)) {
|
||||||
let result = id.split('__')
|
const result = id.split('__')
|
||||||
id = result[1]
|
id = result[1]
|
||||||
}
|
}
|
||||||
return `http://www.kuwo.cn/playlist_detail/${id}`
|
return `http://www.kuwo.cn/playlist_detail/${id}`
|
||||||
|
|||||||
@@ -111,8 +111,8 @@ export const lrcTools = {
|
|||||||
// 使用原始的酷我音乐时间计算逻辑,但输出绝对时间戳
|
// 使用原始的酷我音乐时间计算逻辑,但输出绝对时间戳
|
||||||
const offset = parseInt(str)
|
const offset = parseInt(str)
|
||||||
const offset2 = parseInt(str2)
|
const offset2 = parseInt(str2)
|
||||||
let startTime = Math.abs((offset + offset2) / (this.offset * 2))
|
const startTime = Math.abs((offset + offset2) / (this.offset * 2))
|
||||||
let duration = Math.abs((offset - offset2) / (this.offset2 * 2))
|
const duration = Math.abs((offset - offset2) / (this.offset2 * 2))
|
||||||
|
|
||||||
// 转换为基于行开始时间的绝对时间戳
|
// 转换为基于行开始时间的绝对时间戳
|
||||||
const absoluteStartTime = lineStartTime + startTime
|
const absoluteStartTime = lineStartTime + startTime
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default {
|
|||||||
async getComment(musicInfo, page = 1, limit = 10) {
|
async getComment(musicInfo, page = 1, limit = 10) {
|
||||||
if (this._requestObj) this._requestObj.cancelHttp()
|
if (this._requestObj) this._requestObj.cancelHttp()
|
||||||
if (!musicInfo.songId) {
|
if (!musicInfo.songId) {
|
||||||
let id = await getSongId(musicInfo)
|
const id = await getSongId(musicInfo)
|
||||||
if (!id) throw new Error('获取评论失败')
|
if (!id) throw new Error('获取评论失败')
|
||||||
musicInfo.songId = id
|
musicInfo.songId = id
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ export default {
|
|||||||
if (this._requestObj2) this._requestObj2.cancelHttp()
|
if (this._requestObj2) this._requestObj2.cancelHttp()
|
||||||
|
|
||||||
if (!musicInfo.songId) {
|
if (!musicInfo.songId) {
|
||||||
let id = await getSongId(musicInfo)
|
const id = await getSongId(musicInfo)
|
||||||
if (!id) throw new Error('获取评论失败')
|
if (!id) throw new Error('获取评论失败')
|
||||||
musicInfo.songId = id
|
musicInfo.songId = id
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default {
|
|||||||
},
|
},
|
||||||
filterBoardsData(rawList) {
|
filterBoardsData(rawList) {
|
||||||
// console.log(rawList)
|
// console.log(rawList)
|
||||||
let list = []
|
const list = []
|
||||||
for (const board of rawList) {
|
for (const board of rawList) {
|
||||||
if (board.template != 'group1') continue
|
if (board.template != 'group1') continue
|
||||||
for (const item of board.itemList) {
|
for (const item of board.itemList) {
|
||||||
@@ -112,7 +112,7 @@ export default {
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
let data = item.displayLogId.param
|
const data = item.displayLogId.param
|
||||||
list.push({
|
list.push({
|
||||||
id: 'mg__' + data.rankId,
|
id: 'mg__' + data.rankId,
|
||||||
name: data.rankName,
|
name: data.rankName,
|
||||||
@@ -164,7 +164,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getDetailPageUrl(id) {
|
getDetailPageUrl(id) {
|
||||||
if (typeof id == 'string') id = id.replace('mg__', '')
|
if (typeof id === 'string') id = id.replace('mg__', '')
|
||||||
for (const item of boardList) {
|
for (const item of boardList) {
|
||||||
if (item.bangid == id) {
|
if (item.bangid == id) {
|
||||||
return `https://music.migu.cn/v3/music/top/${item.webId}`
|
return `https://music.migu.cn/v3/music/top/${item.webId}`
|
||||||
|
|||||||
@@ -16,21 +16,21 @@ const mrcTools = {
|
|||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (line.length < 6) continue
|
if (line.length < 6) continue
|
||||||
let result = this.rxps.lineTime.exec(line)
|
const result = this.rxps.lineTime.exec(line)
|
||||||
if (!result) continue
|
if (!result) continue
|
||||||
|
|
||||||
const startTime = parseInt(result[1])
|
const startTime = parseInt(result[1])
|
||||||
let time = startTime
|
let time = startTime
|
||||||
let ms = time % 1000
|
const ms = time % 1000
|
||||||
time /= 1000
|
time /= 1000
|
||||||
let m = parseInt(time / 60)
|
const m = parseInt(time / 60)
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(2, '0')
|
.padStart(2, '0')
|
||||||
time %= 60
|
time %= 60
|
||||||
let s = parseInt(time).toString().padStart(2, '0')
|
const s = parseInt(time).toString().padStart(2, '0')
|
||||||
time = `${m}:${s}.${ms}`
|
time = `${m}:${s}.${ms}`
|
||||||
|
|
||||||
let words = line.replace(this.rxps.lineTime, '')
|
const words = line.replace(this.rxps.lineTime, '')
|
||||||
|
|
||||||
lrcLines.push(`[${time}]${words.replace(this.rxps.wordTimeAll, '')}`)
|
lrcLines.push(`[${time}]${words.replace(this.rxps.wordTimeAll, '')}`)
|
||||||
|
|
||||||
@@ -100,11 +100,11 @@ export default {
|
|||||||
getLyricWeb(songInfo, tryNum = 0) {
|
getLyricWeb(songInfo, tryNum = 0) {
|
||||||
// console.log(songInfo.copyrightId)
|
// console.log(songInfo.copyrightId)
|
||||||
if (songInfo.lrcUrl) {
|
if (songInfo.lrcUrl) {
|
||||||
let requestObj = httpFetch(songInfo.lrcUrl)
|
const requestObj = httpFetch(songInfo.lrcUrl)
|
||||||
requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
|
requestObj.promise = requestObj.promise.then(({ body, statusCode }) => {
|
||||||
if (statusCode !== 200) {
|
if (statusCode !== 200) {
|
||||||
if (tryNum > 5) return Promise.reject(new Error('歌词获取失败'))
|
if (tryNum > 5) return Promise.reject(new Error('歌词获取失败'))
|
||||||
let tryRequestObj = this.getLyricWeb(songInfo, ++tryNum)
|
const tryRequestObj = this.getLyricWeb(songInfo, ++tryNum)
|
||||||
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
|
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
|
||||||
return tryRequestObj.promise
|
return tryRequestObj.promise
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ export default {
|
|||||||
})
|
})
|
||||||
return requestObj
|
return requestObj
|
||||||
} else {
|
} else {
|
||||||
let requestObj = httpFetch(
|
const requestObj = httpFetch(
|
||||||
`https://music.migu.cn/v3/api/music/audioPlayer/getLyric?copyrightId=${songInfo.copyrightId}`,
|
`https://music.migu.cn/v3/api/music/audioPlayer/getLyric?copyrightId=${songInfo.copyrightId}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
@@ -126,7 +126,7 @@ export default {
|
|||||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||||
if (body.returnCode !== '000000' || !body.lyric) {
|
if (body.returnCode !== '000000' || !body.lyric) {
|
||||||
if (tryNum > 5) return Promise.reject(new Error('Get lyric failed'))
|
if (tryNum > 5) return Promise.reject(new Error('Get lyric failed'))
|
||||||
let tryRequestObj = this.getLyricWeb(songInfo, ++tryNum)
|
const tryRequestObj = this.getLyricWeb(songInfo, ++tryNum)
|
||||||
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
|
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
|
||||||
return tryRequestObj.promise
|
return tryRequestObj.promise
|
||||||
}
|
}
|
||||||
@@ -140,9 +140,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getLyric(songInfo) {
|
getLyric(songInfo) {
|
||||||
let requestObj = mrcTools.getLyric(songInfo)
|
const requestObj = mrcTools.getLyric(songInfo)
|
||||||
requestObj.promise = requestObj.promise.catch(() => {
|
requestObj.promise = requestObj.promise.catch(() => {
|
||||||
let webRequestObj = this.getLyricWeb(songInfo)
|
const webRequestObj = this.getLyricWeb(songInfo)
|
||||||
requestObj.cancelHttp = webRequestObj.cancelHttp.bind(webRequestObj)
|
requestObj.cancelHttp = webRequestObj.cancelHttp.bind(webRequestObj)
|
||||||
return webRequestObj.promise
|
return webRequestObj.promise
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import { formatSingerName } from '../utils'
|
|||||||
|
|
||||||
const createGetMusicInfosTask = (ids) => {
|
const createGetMusicInfosTask = (ids) => {
|
||||||
let list = ids
|
let list = ids
|
||||||
let tasks = []
|
const tasks = []
|
||||||
while (list.length) {
|
while (list.length) {
|
||||||
tasks.push(list.slice(0, 100))
|
tasks.push(list.slice(0, 100))
|
||||||
if (list.length < 100) break
|
if (list.length < 100) break
|
||||||
list = list.slice(100)
|
list = list.slice(100)
|
||||||
}
|
}
|
||||||
let url = 'https://c.musicapp.migu.cn/MIGUM2.0/v1.0/content/resourceinfo.do?resourceType=2'
|
const url = 'https://c.musicapp.migu.cn/MIGUM2.0/v1.0/content/resourceinfo.do?resourceType=2'
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
tasks.map((task) =>
|
tasks.map((task) =>
|
||||||
createHttpFetch(url, {
|
createHttpFetch(url, {
|
||||||
@@ -25,7 +25,7 @@ const createGetMusicInfosTask = (ids) => {
|
|||||||
|
|
||||||
export const filterMusicInfoList = (rawList) => {
|
export const filterMusicInfoList = (rawList) => {
|
||||||
// console.log(rawList)
|
// console.log(rawList)
|
||||||
let ids = new Set()
|
const ids = new Set()
|
||||||
const list = []
|
const list = []
|
||||||
rawList.forEach((item) => {
|
rawList.forEach((item) => {
|
||||||
if (!item.songId || ids.has(item.songId)) return
|
if (!item.songId || ids.has(item.songId)) return
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ export default {
|
|||||||
return Promise.reject(new Error(result ? result.info : '搜索失败'))
|
return Promise.reject(new Error(result ? result.info : '搜索失败'))
|
||||||
const songResultData = result.songResultData || { resultList: [], totalCount: 0 }
|
const songResultData = result.songResultData || { resultList: [], totalCount: 0 }
|
||||||
|
|
||||||
let list = this.filterData(songResultData.resultList)
|
const list = this.filterData(songResultData.resultList)
|
||||||
if (list == null) return this.search(str, page, limit, retryNum)
|
if (list == null) return this.search(str, page, limit, retryNum)
|
||||||
|
|
||||||
this.total = parseInt(songResultData.totalCount)
|
this.total = parseInt(songResultData.totalCount)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import getSongId from './songId'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
async getPicUrl(songId, tryNum = 0) {
|
async getPicUrl(songId, tryNum = 0) {
|
||||||
let requestObj = httpFetch(
|
const requestObj = httpFetch(
|
||||||
`http://music.migu.cn/v3/api/music/audioPlayer/getSongPic?songId=${songId}`,
|
`http://music.migu.cn/v3/api/music/audioPlayer/getSongPic?songId=${songId}`,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
@@ -14,7 +14,7 @@ export default {
|
|||||||
requestObj.promise.then(({ body }) => {
|
requestObj.promise.then(({ body }) => {
|
||||||
if (body.returnCode !== '000000') {
|
if (body.returnCode !== '000000') {
|
||||||
if (tryNum > 5) return Promise.reject(new Error('图片获取失败'))
|
if (tryNum > 5) return Promise.reject(new Error('图片获取失败'))
|
||||||
let tryRequestObj = this.getPic(songId, ++tryNum)
|
const tryRequestObj = this.getPic(songId, ++tryNum)
|
||||||
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
|
requestObj.cancelHttp = tryRequestObj.cancelHttp.bind(tryRequestObj)
|
||||||
return tryRequestObj.promise
|
return tryRequestObj.promise
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,15 +22,15 @@ const teaDecrypt = (data, key) => {
|
|||||||
let j2 = data[0]
|
let j2 = data[0]
|
||||||
let j3 = toLong((6n + 52n / lengthBitint) * DELTA)
|
let j3 = toLong((6n + 52n / lengthBitint) * DELTA)
|
||||||
while (true) {
|
while (true) {
|
||||||
let j4 = j3
|
const j4 = j3
|
||||||
if (j4 == 0n) break
|
if (j4 == 0n) break
|
||||||
let j5 = toLong(3n & toLong(j4 >> 2n))
|
const j5 = toLong(3n & toLong(j4 >> 2n))
|
||||||
let j6 = lengthBitint
|
let j6 = lengthBitint
|
||||||
while (true) {
|
while (true) {
|
||||||
j6--
|
j6--
|
||||||
if (j6 > 0n) {
|
if (j6 > 0n) {
|
||||||
let j7 = data[j6 - 1n]
|
const j7 = data[j6 - 1n]
|
||||||
let i = j6
|
const i = j6
|
||||||
j2 = toLong(
|
j2 = toLong(
|
||||||
data[i] -
|
data[i] -
|
||||||
(toLong(toLong(j2 ^ j4) + toLong(j7 ^ key[toLong(toLong(3n & j6) ^ j5)])) ^
|
(toLong(toLong(j2 ^ j4) + toLong(j7 ^ key[toLong(toLong(3n & j6) ^ j5)])) ^
|
||||||
@@ -42,7 +42,7 @@ const teaDecrypt = (data, key) => {
|
|||||||
data[i] = j2
|
data[i] = j2
|
||||||
} else break
|
} else break
|
||||||
}
|
}
|
||||||
let j8 = data[lengthBitint - 1n]
|
const j8 = data[lengthBitint - 1n]
|
||||||
j2 = toLong(
|
j2 = toLong(
|
||||||
data[0n] -
|
data[0n] -
|
||||||
toLong(
|
toLong(
|
||||||
@@ -89,7 +89,7 @@ const toBigintArray = (data) => {
|
|||||||
const MAX = 9223372036854775807n
|
const MAX = 9223372036854775807n
|
||||||
const MIN = -9223372036854775808n
|
const MIN = -9223372036854775808n
|
||||||
const toLong = (str) => {
|
const toLong = (str) => {
|
||||||
const num = typeof str == 'string' ? BigInt('0x' + str) : str
|
const num = typeof str === 'string' ? BigInt('0x' + str) : str
|
||||||
if (num > MAX) return toLong(num - (1n << 64n))
|
if (num > MAX) return toLong(num - (1n << 64n))
|
||||||
else if (num < MIN) return toLong(num + (1n << 64n))
|
else if (num < MIN) return toLong(num + (1n << 64n))
|
||||||
return num
|
return num
|
||||||
|
|||||||
@@ -197,10 +197,10 @@ export default {
|
|||||||
},
|
},
|
||||||
filterNewComment(rawList) {
|
filterNewComment(rawList) {
|
||||||
return rawList.map((item) => {
|
return rawList.map((item) => {
|
||||||
let time = this.formatTime(item.time)
|
const time = this.formatTime(item.time)
|
||||||
let timeStr = time ? dateFormat2(time) : null
|
const timeStr = time ? dateFormat2(time) : null
|
||||||
if (item.middlecommentcontent) {
|
if (item.middlecommentcontent) {
|
||||||
let firstItem = item.middlecommentcontent[0]
|
const firstItem = item.middlecommentcontent[0]
|
||||||
firstItem.avatarurl = item.avatarurl
|
firstItem.avatarurl = item.avatarurl
|
||||||
firstItem.praisenum = item.praisenum
|
firstItem.praisenum = item.praisenum
|
||||||
item.avatarurl = null
|
item.avatarurl = null
|
||||||
@@ -270,12 +270,12 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
replaceEmoji(msg) {
|
replaceEmoji(msg) {
|
||||||
let rxp = /^\[em\](e\d+)\[\/em\]$/
|
const rxp = /^\[em\](e\d+)\[\/em\]$/
|
||||||
let result = msg.match(/\[em\]e\d+\[\/em\]/g)
|
let result = msg.match(/\[em\]e\d+\[\/em\]/g)
|
||||||
if (!result) return msg
|
if (!result) return msg
|
||||||
result = Array.from(new Set(result))
|
result = Array.from(new Set(result))
|
||||||
for (let item of result) {
|
for (const item of result) {
|
||||||
let code = item.replace(rxp, '$1')
|
const code = item.replace(rxp, '$1')
|
||||||
msg = msg.replace(
|
msg = msg.replace(
|
||||||
new RegExp(item.replace('[em]', '\\[em\\]').replace('[/em]', '\\[\\/em\\]'), 'g'),
|
new RegExp(item.replace('[em]', '\\[em\\]').replace('[/em]', '\\[\\/em\\]'), 'g'),
|
||||||
emojis[code] || ''
|
emojis[code] || ''
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { httpFetch } from '../../request'
|
|||||||
import { formatPlayTime, sizeFormate } from '../index'
|
import { formatPlayTime, sizeFormate } from '../index'
|
||||||
import { formatSingerName } from '../utils'
|
import { formatSingerName } from '../utils'
|
||||||
|
|
||||||
let boardList = [
|
const boardList = [
|
||||||
{ id: 'tx__4', name: '流行指数榜', bangid: '4' },
|
{ id: 'tx__4', name: '流行指数榜', bangid: '4' },
|
||||||
{ id: 'tx__26', name: '热歌榜', bangid: '26' },
|
{ id: 'tx__26', name: '热歌榜', bangid: '26' },
|
||||||
{ id: 'tx__27', name: '新歌榜', bangid: '27' },
|
{ id: 'tx__27', name: '新歌榜', bangid: '27' },
|
||||||
@@ -137,31 +137,31 @@ export default {
|
|||||||
filterData(rawList) {
|
filterData(rawList) {
|
||||||
// console.log(rawList)
|
// console.log(rawList)
|
||||||
return rawList.map((item) => {
|
return rawList.map((item) => {
|
||||||
let types = []
|
const types = []
|
||||||
let _types = {}
|
const _types = {}
|
||||||
if (item.file.size_128mp3 !== 0) {
|
if (item.file.size_128mp3 !== 0) {
|
||||||
let size = sizeFormate(item.file.size_128mp3)
|
const size = sizeFormate(item.file.size_128mp3)
|
||||||
types.push({ type: '128k', size })
|
types.push({ type: '128k', size })
|
||||||
_types['128k'] = {
|
_types['128k'] = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.file.size_320mp3 !== 0) {
|
if (item.file.size_320mp3 !== 0) {
|
||||||
let size = sizeFormate(item.file.size_320mp3)
|
const size = sizeFormate(item.file.size_320mp3)
|
||||||
types.push({ type: '320k', size })
|
types.push({ type: '320k', size })
|
||||||
_types['320k'] = {
|
_types['320k'] = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.file.size_flac !== 0) {
|
if (item.file.size_flac !== 0) {
|
||||||
let size = sizeFormate(item.file.size_flac)
|
const size = sizeFormate(item.file.size_flac)
|
||||||
types.push({ type: 'flac', size })
|
types.push({ type: 'flac', size })
|
||||||
_types.flac = {
|
_types.flac = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.file.size_hires !== 0) {
|
if (item.file.size_hires !== 0) {
|
||||||
let size = sizeFormate(item.file.size_hires)
|
const size = sizeFormate(item.file.size_hires)
|
||||||
types.push({ type: 'flac24bit', size })
|
types.push({ type: 'flac24bit', size })
|
||||||
_types.flac24bit = {
|
_types.flac24bit = {
|
||||||
size
|
size
|
||||||
@@ -195,10 +195,10 @@ export default {
|
|||||||
},
|
},
|
||||||
getPeriods(bangid) {
|
getPeriods(bangid) {
|
||||||
return this.getData(this.periodUrl).then(({ body: html }) => {
|
return this.getData(this.periodUrl).then(({ body: html }) => {
|
||||||
let result = html.match(this.regExps.periodList)
|
const result = html.match(this.regExps.periodList)
|
||||||
if (!result) return Promise.reject(new Error('get data failed'))
|
if (!result) return Promise.reject(new Error('get data failed'))
|
||||||
result.forEach((item) => {
|
result.forEach((item) => {
|
||||||
let result = item.match(this.regExps.period)
|
const result = item.match(this.regExps.period)
|
||||||
if (!result) return
|
if (!result) return
|
||||||
this.periods[result[2]] = {
|
this.periods[result[2]] = {
|
||||||
name: result[1],
|
name: result[1],
|
||||||
@@ -212,7 +212,7 @@ export default {
|
|||||||
},
|
},
|
||||||
filterBoardsData(rawList) {
|
filterBoardsData(rawList) {
|
||||||
// console.log(rawList)
|
// console.log(rawList)
|
||||||
let list = []
|
const list = []
|
||||||
for (const board of rawList) {
|
for (const board of rawList) {
|
||||||
// 排除 MV榜
|
// 排除 MV榜
|
||||||
if (board.id == 201) continue
|
if (board.id == 201) continue
|
||||||
@@ -256,8 +256,8 @@ export default {
|
|||||||
getList(bangid, page, retryNum = 0) {
|
getList(bangid, page, retryNum = 0) {
|
||||||
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
|
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
|
||||||
bangid = parseInt(bangid)
|
bangid = parseInt(bangid)
|
||||||
let info = this.periods[bangid]
|
const info = this.periods[bangid]
|
||||||
let p = info ? Promise.resolve(info.period) : this.getPeriods(bangid)
|
const p = info ? Promise.resolve(info.period) : this.getPeriods(bangid)
|
||||||
return p.then((period) => {
|
return p.then((period) => {
|
||||||
return this.listDetailRequest(bangid, period, this.limit).then((resp) => {
|
return this.listDetailRequest(bangid, period, this.limit).then((resp) => {
|
||||||
if (resp.body.code !== 0) return this.getList(bangid, page, retryNum)
|
if (resp.body.code !== 0) return this.getList(bangid, page, retryNum)
|
||||||
@@ -273,7 +273,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getDetailPageUrl(id) {
|
getDetailPageUrl(id) {
|
||||||
if (typeof id == 'string') id = id.replace('tx__', '')
|
if (typeof id === 'string') id = id.replace('tx__', '')
|
||||||
return `https://y.qq.com/n/ryqq/toplist/${id}`
|
return `https://y.qq.com/n/ryqq/toplist/${id}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { httpFetch } from '../../request'
|
|||||||
import { formatPlayTime, sizeFormate } from '../index'
|
import { formatPlayTime, sizeFormate } from '../index'
|
||||||
|
|
||||||
const getSinger = (singers) => {
|
const getSinger = (singers) => {
|
||||||
let arr = []
|
const arr = []
|
||||||
singers.forEach((singer) => {
|
singers.forEach((singer) => {
|
||||||
arr.push(singer.name)
|
arr.push(singer.name)
|
||||||
})
|
})
|
||||||
@@ -37,32 +37,32 @@ export default (songmid) => {
|
|||||||
const item = body.req.data.track_info
|
const item = body.req.data.track_info
|
||||||
if (!item.file?.media_mid) return null
|
if (!item.file?.media_mid) return null
|
||||||
|
|
||||||
let types = []
|
const types = []
|
||||||
let _types = {}
|
const _types = {}
|
||||||
const file = item.file
|
const file = item.file
|
||||||
if (file.size_128mp3 != 0) {
|
if (file.size_128mp3 != 0) {
|
||||||
let size = sizeFormate(file.size_128mp3)
|
const size = sizeFormate(file.size_128mp3)
|
||||||
types.push({ type: '128k', size })
|
types.push({ type: '128k', size })
|
||||||
_types['128k'] = {
|
_types['128k'] = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (file.size_320mp3 !== 0) {
|
if (file.size_320mp3 !== 0) {
|
||||||
let size = sizeFormate(file.size_320mp3)
|
const size = sizeFormate(file.size_320mp3)
|
||||||
types.push({ type: '320k', size })
|
types.push({ type: '320k', size })
|
||||||
_types['320k'] = {
|
_types['320k'] = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (file.size_flac !== 0) {
|
if (file.size_flac !== 0) {
|
||||||
let size = sizeFormate(file.size_flac)
|
const size = sizeFormate(file.size_flac)
|
||||||
types.push({ type: 'flac', size })
|
types.push({ type: 'flac', size })
|
||||||
_types.flac = {
|
_types.flac = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (file.size_hires !== 0) {
|
if (file.size_hires !== 0) {
|
||||||
let size = sizeFormate(file.size_hires)
|
const size = sizeFormate(file.size_hires)
|
||||||
types.push({ type: 'flac24bit', size })
|
types.push({ type: 'flac24bit', size })
|
||||||
_types.flac24bit = {
|
_types.flac24bit = {
|
||||||
size
|
size
|
||||||
|
|||||||
@@ -56,32 +56,32 @@ export default {
|
|||||||
rawList.forEach((item) => {
|
rawList.forEach((item) => {
|
||||||
if (!item.file?.media_mid) return
|
if (!item.file?.media_mid) return
|
||||||
|
|
||||||
let types = []
|
const types = []
|
||||||
let _types = {}
|
const _types = {}
|
||||||
const file = item.file
|
const file = item.file
|
||||||
if (file.size_128mp3 != 0) {
|
if (file.size_128mp3 != 0) {
|
||||||
let size = sizeFormate(file.size_128mp3)
|
const size = sizeFormate(file.size_128mp3)
|
||||||
types.push({ type: '128k', size })
|
types.push({ type: '128k', size })
|
||||||
_types['128k'] = {
|
_types['128k'] = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (file.size_320mp3 !== 0) {
|
if (file.size_320mp3 !== 0) {
|
||||||
let size = sizeFormate(file.size_320mp3)
|
const size = sizeFormate(file.size_320mp3)
|
||||||
types.push({ type: '320k', size })
|
types.push({ type: '320k', size })
|
||||||
_types['320k'] = {
|
_types['320k'] = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (file.size_flac !== 0) {
|
if (file.size_flac !== 0) {
|
||||||
let size = sizeFormate(file.size_flac)
|
const size = sizeFormate(file.size_flac)
|
||||||
types.push({ type: 'flac', size })
|
types.push({ type: 'flac', size })
|
||||||
_types.flac = {
|
_types.flac = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (file.size_hires !== 0) {
|
if (file.size_hires !== 0) {
|
||||||
let size = sizeFormate(file.size_hires)
|
const size = sizeFormate(file.size_hires)
|
||||||
types.push({ type: 'flac24bit', size })
|
types.push({ type: 'flac24bit', size })
|
||||||
_types.flac24bit = {
|
_types.flac24bit = {
|
||||||
size
|
size
|
||||||
@@ -123,7 +123,7 @@ export default {
|
|||||||
if (limit == null) limit = this.limit
|
if (limit == null) limit = this.limit
|
||||||
// http://newlyric.kuwo.cn/newlyric.lrc?62355680
|
// http://newlyric.kuwo.cn/newlyric.lrc?62355680
|
||||||
return this.musicSearch(str, page, limit).then(({ body, meta }) => {
|
return this.musicSearch(str, page, limit).then(({ body, meta }) => {
|
||||||
let list = this.handleResult(body.item_song)
|
const list = this.handleResult(body.item_song)
|
||||||
|
|
||||||
this.total = meta.estimate_sum
|
this.total = meta.estimate_sum
|
||||||
this.page = page
|
this.page = page
|
||||||
|
|||||||
@@ -7,28 +7,28 @@ export const filterMusicInfoItem = (item) => {
|
|||||||
const types = []
|
const types = []
|
||||||
const _types = {}
|
const _types = {}
|
||||||
if (item.file.size_128mp3 != 0) {
|
if (item.file.size_128mp3 != 0) {
|
||||||
let size = sizeFormate(item.file.size_128mp3)
|
const size = sizeFormate(item.file.size_128mp3)
|
||||||
types.push({ type: '128k', size })
|
types.push({ type: '128k', size })
|
||||||
_types['128k'] = {
|
_types['128k'] = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.file.size_320mp3 !== 0) {
|
if (item.file.size_320mp3 !== 0) {
|
||||||
let size = sizeFormate(item.file.size_320mp3)
|
const size = sizeFormate(item.file.size_320mp3)
|
||||||
types.push({ type: '320k', size })
|
types.push({ type: '320k', size })
|
||||||
_types['320k'] = {
|
_types['320k'] = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.file.size_flac !== 0) {
|
if (item.file.size_flac !== 0) {
|
||||||
let size = sizeFormate(item.file.size_flac)
|
const size = sizeFormate(item.file.size_flac)
|
||||||
types.push({ type: 'flac', size })
|
types.push({ type: 'flac', size })
|
||||||
_types.flac = {
|
_types.flac = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.file.size_hires !== 0) {
|
if (item.file.size_hires !== 0) {
|
||||||
let size = sizeFormate(item.file.size_hires)
|
const size = sizeFormate(item.file.size_hires)
|
||||||
types.push({ type: 'flac24bit', size })
|
types.push({ type: 'flac24bit', size })
|
||||||
_types.flac24bit = {
|
_types.flac24bit = {
|
||||||
size
|
size
|
||||||
|
|||||||
@@ -95,12 +95,12 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
filterInfoHotTag(html) {
|
filterInfoHotTag(html) {
|
||||||
let hotTag = html.match(this.regExps.hotTagHtml)
|
const hotTag = html.match(this.regExps.hotTagHtml)
|
||||||
const hotTags = []
|
const hotTags = []
|
||||||
if (!hotTag) return hotTags
|
if (!hotTag) return hotTags
|
||||||
|
|
||||||
hotTag.forEach((tagHtml) => {
|
hotTag.forEach((tagHtml) => {
|
||||||
let result = tagHtml.match(this.regExps.hotTag)
|
const result = tagHtml.match(this.regExps.hotTag)
|
||||||
if (!result) return
|
if (!result) return
|
||||||
hotTags.push({
|
hotTags.push({
|
||||||
id: parseInt(result[1]),
|
id: parseInt(result[1]),
|
||||||
@@ -240,31 +240,31 @@ export default {
|
|||||||
filterListDetail(rawList) {
|
filterListDetail(rawList) {
|
||||||
// console.log(rawList)
|
// console.log(rawList)
|
||||||
return rawList.map((item) => {
|
return rawList.map((item) => {
|
||||||
let types = []
|
const types = []
|
||||||
let _types = {}
|
const _types = {}
|
||||||
if (item.file.size_128mp3 !== 0) {
|
if (item.file.size_128mp3 !== 0) {
|
||||||
let size = sizeFormate(item.file.size_128mp3)
|
const size = sizeFormate(item.file.size_128mp3)
|
||||||
types.push({ type: '128k', size })
|
types.push({ type: '128k', size })
|
||||||
_types['128k'] = {
|
_types['128k'] = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.file.size_320mp3 !== 0) {
|
if (item.file.size_320mp3 !== 0) {
|
||||||
let size = sizeFormate(item.file.size_320mp3)
|
const size = sizeFormate(item.file.size_320mp3)
|
||||||
types.push({ type: '320k', size })
|
types.push({ type: '320k', size })
|
||||||
_types['320k'] = {
|
_types['320k'] = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.file.size_flac !== 0) {
|
if (item.file.size_flac !== 0) {
|
||||||
let size = sizeFormate(item.file.size_flac)
|
const size = sizeFormate(item.file.size_flac)
|
||||||
types.push({ type: 'flac', size })
|
types.push({ type: 'flac', size })
|
||||||
_types.flac = {
|
_types.flac = {
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (item.file.size_hires !== 0) {
|
if (item.file.size_hires !== 0) {
|
||||||
let size = sizeFormate(item.file.size_hires)
|
const size = sizeFormate(item.file.size_hires)
|
||||||
types.push({ type: 'flac24bit', size })
|
types.push({ type: 'flac24bit', size })
|
||||||
_types.flac24bit = {
|
_types.flac24bit = {
|
||||||
size
|
size
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export const formatSingerName = (singers, nameKey = 'name', join = '、') => {
|
|||||||
if (Array.isArray(singers)) {
|
if (Array.isArray(singers)) {
|
||||||
const singer = []
|
const singer = []
|
||||||
singers.forEach((item) => {
|
singers.forEach((item) => {
|
||||||
let name = item[nameKey]
|
const name = item[nameKey]
|
||||||
if (!name) return
|
if (!name) return
|
||||||
singer.push(name)
|
singer.push(name)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ const applyEmoji = (text) => {
|
|||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
let cursorTools = {
|
const cursorTools = {
|
||||||
cache: {},
|
cache: {},
|
||||||
getCursor(id, page, limit) {
|
getCursor(id, page, limit) {
|
||||||
let cacheData = this.cache[id]
|
let cacheData = this.cache[id]
|
||||||
@@ -190,7 +190,7 @@ export default {
|
|||||||
},
|
},
|
||||||
filterComment(rawList) {
|
filterComment(rawList) {
|
||||||
return rawList.map((item) => {
|
return rawList.map((item) => {
|
||||||
let data = {
|
const data = {
|
||||||
id: item.commentId,
|
id: item.commentId,
|
||||||
text: item.content ? applyEmoji(item.content) : '',
|
text: item.content ? applyEmoji(item.content) : '',
|
||||||
time: item.time ? item.time : '',
|
time: item.time ? item.time : '',
|
||||||
@@ -203,7 +203,7 @@ export default {
|
|||||||
reply: []
|
reply: []
|
||||||
}
|
}
|
||||||
|
|
||||||
let replyData = item.beReplied && item.beReplied[0]
|
const replyData = item.beReplied && item.beReplied[0]
|
||||||
return replyData
|
return replyData
|
||||||
? {
|
? {
|
||||||
id: item.commentId,
|
id: item.commentId,
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ export default {
|
|||||||
|
|
||||||
filterBoardsData(rawList) {
|
filterBoardsData(rawList) {
|
||||||
// console.log(rawList)
|
// console.log(rawList)
|
||||||
let list = []
|
const list = []
|
||||||
for (const board of rawList) {
|
for (const board of rawList) {
|
||||||
// 排除 MV榜
|
// 排除 MV榜
|
||||||
// if (board.id == 201) continue
|
// if (board.id == 201) continue
|
||||||
@@ -210,7 +210,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getDetailPageUrl(id) {
|
getDetailPageUrl(id) {
|
||||||
if (typeof id == 'string') id = id.replace('wy__', '')
|
if (typeof id === 'string') id = id.replace('wy__', '')
|
||||||
return `https://music.163.com/#/discover/toplist?id=${id}`
|
return `https://music.163.com/#/discover/toplist?id=${id}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,13 +64,13 @@ const parseTools = {
|
|||||||
},
|
},
|
||||||
msFormat(timeMs) {
|
msFormat(timeMs) {
|
||||||
if (Number.isNaN(timeMs)) return ''
|
if (Number.isNaN(timeMs)) return ''
|
||||||
let ms = timeMs % 1000
|
const ms = timeMs % 1000
|
||||||
timeMs /= 1000
|
timeMs /= 1000
|
||||||
let m = parseInt(timeMs / 60)
|
const m = parseInt(timeMs / 60)
|
||||||
.toString()
|
.toString()
|
||||||
.padStart(2, '0')
|
.padStart(2, '0')
|
||||||
timeMs %= 60
|
timeMs %= 60
|
||||||
let s = parseInt(timeMs).toString().padStart(2, '0')
|
const s = parseInt(timeMs).toString().padStart(2, '0')
|
||||||
return `[${m}:${s}.${ms}]`
|
return `[${m}:${s}.${ms}]`
|
||||||
},
|
},
|
||||||
parseLyric(lines) {
|
parseLyric(lines) {
|
||||||
@@ -79,7 +79,7 @@ const parseTools = {
|
|||||||
|
|
||||||
for (let line of lines) {
|
for (let line of lines) {
|
||||||
line = line.trim()
|
line = line.trim()
|
||||||
let result = this.rxps.lineTime.exec(line)
|
const result = this.rxps.lineTime.exec(line)
|
||||||
if (!result) {
|
if (!result) {
|
||||||
if (line.startsWith('[offset')) {
|
if (line.startsWith('[offset')) {
|
||||||
lxlrcLines.push(line)
|
lxlrcLines.push(line)
|
||||||
@@ -92,7 +92,7 @@ const parseTools = {
|
|||||||
const startTimeStr = this.msFormat(startMsTime)
|
const startTimeStr = this.msFormat(startMsTime)
|
||||||
if (!startTimeStr) continue
|
if (!startTimeStr) continue
|
||||||
|
|
||||||
let words = line.replace(this.rxps.lineTime, '')
|
const words = line.replace(this.rxps.lineTime, '')
|
||||||
|
|
||||||
lrcLines.push(`${startTimeStr}${words.replace(this.rxps.wordTimeAll, '')}`)
|
lrcLines.push(`${startTimeStr}${words.replace(this.rxps.wordTimeAll, '')}`)
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ const parseTools = {
|
|||||||
getIntv(interval) {
|
getIntv(interval) {
|
||||||
if (!interval) return 0
|
if (!interval) return 0
|
||||||
if (!interval.includes('.')) interval += '.0'
|
if (!interval.includes('.')) interval += '.0'
|
||||||
let arr = interval.split(/:|\./)
|
const arr = interval.split(/:|\./)
|
||||||
while (arr.length < 3) arr.unshift('0')
|
while (arr.length < 3) arr.unshift('0')
|
||||||
const [m, s, ms] = arr
|
const [m, s, ms] = arr
|
||||||
return parseInt(m) * 3600000 + parseInt(s) * 1000 + parseInt(ms)
|
return parseInt(m) * 3600000 + parseInt(s) * 1000 + parseInt(ms)
|
||||||
@@ -134,7 +134,7 @@ const parseTools = {
|
|||||||
const targetlrcLines = targetlrc.split('\n')
|
const targetlrcLines = targetlrc.split('\n')
|
||||||
const timeRxp = /^\[([\d:.]+)\]/
|
const timeRxp = /^\[([\d:.]+)\]/
|
||||||
let temp = []
|
let temp = []
|
||||||
let newLrc = []
|
const newLrc = []
|
||||||
targetlrcLines.forEach((line) => {
|
targetlrcLines.forEach((line) => {
|
||||||
const result = timeRxp.exec(line)
|
const result = timeRxp.exec(line)
|
||||||
if (!result) return
|
if (!result) return
|
||||||
@@ -168,7 +168,7 @@ const parseTools = {
|
|||||||
crlyric: ''
|
crlyric: ''
|
||||||
}
|
}
|
||||||
if (ylrc) {
|
if (ylrc) {
|
||||||
let lines = this.parseHeaderInfo(ylrc)
|
const lines = this.parseHeaderInfo(ylrc)
|
||||||
if (lines) {
|
if (lines) {
|
||||||
const result = this.parseLyric(lines)
|
const result = this.parseLyric(lines)
|
||||||
if (ytlrc) {
|
if (ytlrc) {
|
||||||
@@ -245,8 +245,8 @@ const parseTools = {
|
|||||||
// https://github.com/lyswhut/lx-music-mobile/issues/370
|
// https://github.com/lyswhut/lx-music-mobile/issues/370
|
||||||
const fixTimeLabel = (lrc, tlrc, romalrc) => {
|
const fixTimeLabel = (lrc, tlrc, romalrc) => {
|
||||||
if (lrc) {
|
if (lrc) {
|
||||||
let newLrc = lrc.replace(/\[(\d{2}:\d{2}):(\d{2})]/g, '[$1.$2]')
|
const newLrc = lrc.replace(/\[(\d{2}:\d{2}):(\d{2})]/g, '[$1.$2]')
|
||||||
let newTlrc = tlrc?.replace(/\[(\d{2}:\d{2}):(\d{2})]/g, '[$1.$2]') ?? tlrc
|
const newTlrc = tlrc?.replace(/\[(\d{2}:\d{2}):(\d{2})]/g, '[$1.$2]') ?? tlrc
|
||||||
if (newLrc != lrc || newTlrc != tlrc) {
|
if (newLrc != lrc || newTlrc != tlrc) {
|
||||||
lrc = newLrc
|
lrc = newLrc
|
||||||
tlrc = newTlrc
|
tlrc = newTlrc
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { formatPlayTime, sizeFormate } from '../index'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
getSinger(singers) {
|
getSinger(singers) {
|
||||||
let arr = []
|
const arr = []
|
||||||
singers?.forEach((singer) => {
|
singers?.forEach((singer) => {
|
||||||
arr.push(singer.name)
|
arr.push(singer.name)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default {
|
|||||||
return searchRequest.promise.then(({ body }) => body)
|
return searchRequest.promise.then(({ body }) => body)
|
||||||
},
|
},
|
||||||
getSinger(singers) {
|
getSinger(singers) {
|
||||||
let arr = []
|
const arr = []
|
||||||
singers.forEach((singer) => {
|
singers.forEach((singer) => {
|
||||||
arr.push(singer.name)
|
arr.push(singer.name)
|
||||||
})
|
})
|
||||||
@@ -87,7 +87,7 @@ export default {
|
|||||||
return this.musicSearch(str, page, limit).then((result) => {
|
return this.musicSearch(str, page, limit).then((result) => {
|
||||||
// console.log(result)
|
// console.log(result)
|
||||||
if (!result || result.code !== 200) return this.search(str, page, limit, retryNum)
|
if (!result || result.code !== 200) return this.search(str, page, limit, retryNum)
|
||||||
let list = this.handleResult(result.result.songs || [])
|
const list = this.handleResult(result.result.songs || [])
|
||||||
// console.log(list)
|
// console.log(list)
|
||||||
|
|
||||||
if (list == null) return this.search(str, page, limit, retryNum)
|
if (list == null) return this.search(str, page, limit, retryNum)
|
||||||
|
|||||||
@@ -90,8 +90,8 @@ export default {
|
|||||||
const { statusCode, body } = await requestObj_listDetail.promise
|
const { statusCode, body } = await requestObj_listDetail.promise
|
||||||
if (statusCode !== 200 || body.code !== this.successCode)
|
if (statusCode !== 200 || body.code !== this.successCode)
|
||||||
return this.getListDetail(id, page, ++tryNum)
|
return this.getListDetail(id, page, ++tryNum)
|
||||||
let limit = 1000
|
const limit = 1000
|
||||||
let rangeStart = (page - 1) * limit
|
const rangeStart = (page - 1) * limit
|
||||||
// console.log(body)
|
// console.log(body)
|
||||||
let list
|
let list
|
||||||
if (body.playlist.trackIds.length == body.privileges.length) {
|
if (body.playlist.trackIds.length == body.privileges.length) {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const aesEncrypt = (buffer, mode, key, iv) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const aesDecrypt = function (cipherBuffer, mode, key, iv) {
|
const aesDecrypt = function (cipherBuffer, mode, key, iv) {
|
||||||
let decipher = createDecipheriv(mode, key, iv)
|
const decipher = createDecipheriv(mode, key, iv)
|
||||||
return Buffer.concat([decipher.update(cipherBuffer), decipher.final()])
|
return Buffer.concat([decipher.update(cipherBuffer), decipher.final()])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ function getAppDirPath(
|
|||||||
| 'logs'
|
| 'logs'
|
||||||
| 'crashDumps'
|
| 'crashDumps'
|
||||||
) {
|
) {
|
||||||
let dirPath: string = electron.app.getPath(name ?? 'userData')
|
const dirPath: string = electron.app.getPath(name ?? 'userData')
|
||||||
return dirPath
|
return dirPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ const defaultHeaders = {
|
|||||||
* @param {Object} options - 请求选项
|
* @param {Object} options - 请求选项
|
||||||
*/
|
*/
|
||||||
const buildHttpPromise = (url, options) => {
|
const buildHttpPromise = (url, options) => {
|
||||||
let obj = {
|
const obj = {
|
||||||
isCancelled: false,
|
isCancelled: false,
|
||||||
cancelToken: axios.CancelToken.source(),
|
cancelToken: axios.CancelToken.source(),
|
||||||
cancelHttp: () => {
|
cancelHttp: () => {
|
||||||
@@ -190,12 +190,12 @@ const fetchData = async (url, method = 'get', options = {}) => {
|
|||||||
let s = Buffer.from(bHh, 'hex').toString()
|
let s = Buffer.from(bHh, 'hex').toString()
|
||||||
s = s.replace(s.substr(-1), '')
|
s = s.replace(s.substr(-1), '')
|
||||||
s = Buffer.from(s, 'base64').toString()
|
s = Buffer.from(s, 'base64').toString()
|
||||||
let v = process.versions.app
|
const v = process.versions.app
|
||||||
.split('-')[0]
|
.split('-')[0]
|
||||||
.split('.')
|
.split('.')
|
||||||
.map((n) => (n.length < 3 ? n.padStart(3, '0') : n))
|
.map((n) => (n.length < 3 ? n.padStart(3, '0') : n))
|
||||||
.join('')
|
.join('')
|
||||||
let v2 = process.versions.app.split('-')[1] || ''
|
const v2 = process.versions.app.split('-')[1] || ''
|
||||||
requestHeaders[s] =
|
requestHeaders[s] =
|
||||||
!s ||
|
!s ||
|
||||||
`${(await handleDeflateRaw(Buffer.from(JSON.stringify(`${path}${v}`.match(regx), null, 1).concat(v)).toString('base64'))).toString('hex')}&${parseInt(v)}${v2}`
|
`${(await handleDeflateRaw(Buffer.from(JSON.stringify(`${path}${v}`.match(regx), null, 1).concat(v)).toString('base64'))).toString('hex')}&${parseInt(v)}${v2}`
|
||||||
@@ -385,7 +385,7 @@ export const http_jsonp = (url, options, callback) => {
|
|||||||
options = {}
|
options = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let jsonpCallback = 'jsonpCallback'
|
const jsonpCallback = 'jsonpCallback'
|
||||||
if (url.indexOf('?') < 0) url += '?'
|
if (url.indexOf('?') < 0) url += '?'
|
||||||
url += `&${options.jsonpCallback}=${jsonpCallback}`
|
url += `&${options.jsonpCallback}=${jsonpCallback}`
|
||||||
|
|
||||||
|
|||||||
54
src/renderer/components.d.ts
vendored
54
src/renderer/components.d.ts
vendored
@@ -1,54 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// @ts-nocheck
|
|
||||||
// Generated by unplugin-vue-components
|
|
||||||
// Read more: https://github.com/vuejs/core/pull/3399
|
|
||||||
// biome-ignore lint: disable
|
|
||||||
export {}
|
|
||||||
|
|
||||||
/* prettier-ignore */
|
|
||||||
declare module 'vue' {
|
|
||||||
export interface GlobalComponents {
|
|
||||||
AIFloatBallSettings: typeof import('./src/components/Settings/AIFloatBallSettings.vue')['default']
|
|
||||||
AudioVisualizer: typeof import('./src/components/Play/AudioVisualizer.vue')['default']
|
|
||||||
FloatBall: typeof import('./src/components/AI/FloatBall.vue')['default']
|
|
||||||
FullPlay: typeof import('./src/components/Play/FullPlay.vue')['default']
|
|
||||||
GlobalAudio: typeof import('./src/components/Play/GlobalAudio.vue')['default']
|
|
||||||
MusicCache: typeof import('./src/components/Settings/MusicCache.vue')['default']
|
|
||||||
PlaylistActions: typeof import('./src/components/Play/PlaylistActions.vue')['default']
|
|
||||||
PlaylistDrawer: typeof import('./src/components/Play/PlaylistDrawer.vue')['default']
|
|
||||||
PlaylistSettings: typeof import('./src/components/Settings/PlaylistSettings.vue')['default']
|
|
||||||
PlayMusic: typeof import('./src/components/Play/PlayMusic.vue')['default']
|
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
|
||||||
SearchComponent: typeof import('./src/components/Search/SearchComponent.vue')['default']
|
|
||||||
ShaderBackground: typeof import('./src/components/Play/ShaderBackground.vue')['default']
|
|
||||||
SongVirtualList: typeof import('./src/components/Music/SongVirtualList.vue')['default']
|
|
||||||
TAlert: typeof import('tdesign-vue-next')['Alert']
|
|
||||||
TAside: typeof import('tdesign-vue-next')['Aside']
|
|
||||||
TBadge: typeof import('tdesign-vue-next')['Badge']
|
|
||||||
TButton: typeof import('tdesign-vue-next')['Button']
|
|
||||||
TCard: typeof import('tdesign-vue-next')['Card']
|
|
||||||
TContent: typeof import('tdesign-vue-next')['Content']
|
|
||||||
TDialog: typeof import('tdesign-vue-next')['Dialog']
|
|
||||||
TDropdown: typeof import('tdesign-vue-next')['Dropdown']
|
|
||||||
TForm: typeof import('tdesign-vue-next')['Form']
|
|
||||||
TFormItem: typeof import('tdesign-vue-next')['FormItem']
|
|
||||||
ThemeSelector: typeof import('./src/components/ThemeSelector.vue')['default']
|
|
||||||
TIcon: typeof import('tdesign-vue-next')['Icon']
|
|
||||||
TImage: typeof import('tdesign-vue-next')['Image']
|
|
||||||
TInput: typeof import('tdesign-vue-next')['Input']
|
|
||||||
TitleBarControls: typeof import('./src/components/TitleBarControls.vue')['default']
|
|
||||||
TLayout: typeof import('tdesign-vue-next')['Layout']
|
|
||||||
TLoading: typeof import('tdesign-vue-next')['Loading']
|
|
||||||
TRadioButton: typeof import('tdesign-vue-next')['RadioButton']
|
|
||||||
TRadioGroup: typeof import('tdesign-vue-next')['RadioGroup']
|
|
||||||
TSlider: typeof import('tdesign-vue-next')['Slider']
|
|
||||||
TSwitch: typeof import('tdesign-vue-next')['Switch']
|
|
||||||
TTextarea: typeof import('tdesign-vue-next')['Textarea']
|
|
||||||
TTooltip: typeof import('tdesign-vue-next')['Tooltip']
|
|
||||||
UpdateExample: typeof import('./src/components/UpdateExample.vue')['default']
|
|
||||||
UpdateProgress: typeof import('./src/components/UpdateProgress.vue')['default']
|
|
||||||
UpdateSettings: typeof import('./src/components/Settings/UpdateSettings.vue')['default']
|
|
||||||
Versions: typeof import('./src/components/Versions.vue')['default']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
;((i = function () {
|
;((i = function () {
|
||||||
var a,
|
let a,
|
||||||
l = document.createElement('div')
|
l = document.createElement('div')
|
||||||
;((l.innerHTML = c._iconfont_svg_string_4997692),
|
;((l.innerHTML = c._iconfont_svg_string_4997692),
|
||||||
(l = l.getElementsByTagName('svg')[0]) &&
|
(l = l.getElementsByTagName('svg')[0]) &&
|
||||||
|
|||||||
BIN
src/renderer/src/assets/pointer.png
Normal file
BIN
src/renderer/src/assets/pointer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.1 KiB |
@@ -481,7 +481,7 @@ onBeforeUnmount(() => {
|
|||||||
:class="message.type"
|
:class="message.type"
|
||||||
>
|
>
|
||||||
<div v-if="message.type === 'loading'" class="message-content loading-content">
|
<div v-if="message.type === 'loading'" class="message-content loading-content">
|
||||||
<t-loading size="small" />
|
<TLoading size="small" />
|
||||||
<span class="loading-text">{{ message.content }}</span>
|
<span class="loading-text">{{ message.content }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="message-content" v-html="message.html || message.content"></div>
|
<div v-else class="message-content" v-html="message.html || message.content"></div>
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
<div class="song-virtual-list">
|
<div class="song-virtual-list">
|
||||||
<!-- 表头 -->
|
<!-- 表头 -->
|
||||||
<div class="list-header">
|
<div class="list-header">
|
||||||
<div class="col-index" v-if="showIndex"></div>
|
<div v-if="showIndex" class="col-index"></div>
|
||||||
<div class="col-title">标题</div>
|
<div class="col-title">标题</div>
|
||||||
<div class="col-album" v-if="showAlbum">专辑</div>
|
<div v-if="showAlbum" class="col-album">专辑</div>
|
||||||
<div class="col-like">喜欢</div>
|
<div class="col-like">喜欢</div>
|
||||||
<div class="col-duration" v-if="showDuration">时长</div>
|
<div v-if="showDuration" class="col-duration">时长</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 虚拟滚动容器 -->
|
<!-- 虚拟滚动容器 -->
|
||||||
@@ -21,13 +21,15 @@
|
|||||||
@mouseleave="hoveredSong = null"
|
@mouseleave="hoveredSong = null"
|
||||||
>
|
>
|
||||||
<!-- 序号或播放状态图标 -->
|
<!-- 序号或播放状态图标 -->
|
||||||
<div class="col-index" v-if="showIndex">
|
<div v-if="showIndex" class="col-index">
|
||||||
|
<Transition name="playSong" mode="out-in">
|
||||||
<span v-if="hoveredSong !== (song.id || song.songmid)" class="track-number">
|
<span v-if="hoveredSong !== (song.id || song.songmid)" class="track-number">
|
||||||
{{ String(visibleStartIndex + index + 1).padStart(2, '0') }}
|
{{ String(visibleStartIndex + index + 1).padStart(2, '0') }}
|
||||||
</span>
|
</span>
|
||||||
<button v-else class="play-btn" title="播放" @click.stop="handlePlay(song)">
|
<button v-else class="play-btn" title="播放" @click.stop="handlePlay(song)">
|
||||||
<i class="icon-play"></i>
|
<i class="icon-play"></i>
|
||||||
</button>
|
</button>
|
||||||
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 歌曲信息 -->
|
<!-- 歌曲信息 -->
|
||||||
@@ -47,7 +49,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 专辑信息 -->
|
<!-- 专辑信息 -->
|
||||||
<div class="col-album" v-if="showAlbum">
|
<div v-if="showAlbum" class="col-album">
|
||||||
<span class="album-name" :title="song.albumName">
|
<span class="album-name" :title="song.albumName">
|
||||||
{{ song.albumName || '-' }}
|
{{ song.albumName || '-' }}
|
||||||
</span>
|
</span>
|
||||||
@@ -61,7 +63,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 时长 -->
|
<!-- 时长 -->
|
||||||
<div class="col-duration" v-if="showDuration">
|
<div v-if="showDuration" class="col-duration">
|
||||||
<div class="duration-wrapper">
|
<div class="duration-wrapper">
|
||||||
<span v-if="hoveredSong !== (song.id || song.songmid)" class="duration">
|
<span v-if="hoveredSong !== (song.id || song.songmid)" class="duration">
|
||||||
{{ formatDuration(song.interval) }}
|
{{ formatDuration(song.interval) }}
|
||||||
@@ -247,6 +249,15 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.playSong-enter-active,
|
||||||
|
.playSong-leave-active {
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
.playSong-enter-from,
|
||||||
|
.playSong-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
.song-virtual-list {
|
.song-virtual-list {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -373,6 +384,7 @@ onMounted(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-style: none;
|
font-style: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(80, 125, 175, 0.1);
|
background: rgba(80, 125, 175, 0.1);
|
||||||
color: #3a5d8f;
|
color: #3a5d8f;
|
||||||
@@ -568,14 +580,17 @@ onMounted(() => {
|
|||||||
content: '▶';
|
content: '▶';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-pause::before {
|
.icon-pause::before {
|
||||||
content: '⏸';
|
content: '⏸';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-download::before {
|
.icon-download::before {
|
||||||
content: '⬇';
|
content: '⬇';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-heart::before {
|
.icon-heart::before {
|
||||||
content: '♡';
|
content: '♡';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
|||||||
@@ -12,11 +12,6 @@ interface Props {
|
|||||||
backgroundColor?: string
|
backgroundColor?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义事件
|
|
||||||
const emit = defineEmits<{
|
|
||||||
lowFreqUpdate: [volume: number]
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
show: true,
|
show: true,
|
||||||
height: 80,
|
height: 80,
|
||||||
@@ -25,6 +20,11 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
backgroundColor: 'transparent'
|
backgroundColor: 'transparent'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 定义事件
|
||||||
|
const emit = defineEmits<{
|
||||||
|
lowFreqUpdate: [volume: number]
|
||||||
|
}>()
|
||||||
|
|
||||||
const canvasRef = ref<HTMLCanvasElement>()
|
const canvasRef = ref<HTMLCanvasElement>()
|
||||||
const animationId = ref<number>()
|
const animationId = ref<number>()
|
||||||
const analyser = ref<AnalyserNode>()
|
const analyser = ref<AnalyserNode>()
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
show: false,
|
show: false,
|
||||||
coverImage: '@assets/images/Default.jpg',
|
coverImage: '@assets/images/Default.jpg',
|
||||||
songId: '',
|
songId: '',
|
||||||
mainColor: '#fff'
|
mainColor: '#rgb(0,0,0)'
|
||||||
})
|
})
|
||||||
// 定义事件
|
// 定义事件
|
||||||
const emit = defineEmits(['toggle-fullscreen'])
|
const emit = defineEmits(['toggle-fullscreen'])
|
||||||
@@ -251,6 +251,27 @@ watch(
|
|||||||
const handleLowFreqUpdate = (volume: number) => {
|
const handleLowFreqUpdate = (volume: number) => {
|
||||||
state.lowFreqVolume = volume
|
state.lowFreqVolume = volume
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算偏白的主题色
|
||||||
|
const lightMainColor = computed(() => {
|
||||||
|
const color = props.mainColor
|
||||||
|
// 解析rgb颜色值
|
||||||
|
const rgbMatch = color.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*\d+\)/)
|
||||||
|
if (rgbMatch) {
|
||||||
|
let r = parseInt(rgbMatch[1])
|
||||||
|
let g = parseInt(rgbMatch[2])
|
||||||
|
let b = parseInt(rgbMatch[3])
|
||||||
|
|
||||||
|
// 适度向白色偏移,保持主题色特征
|
||||||
|
r = Math.min(255, r + (255 - r) * 0.8)
|
||||||
|
g = Math.min(255, g + (255 - g) * 0.8)
|
||||||
|
b = Math.min(255, b + (255 - b) * 0.8)
|
||||||
|
|
||||||
|
return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, 0.9)`
|
||||||
|
}
|
||||||
|
// 如果解析失败,返回默认的偏白色
|
||||||
|
return 'rgba(255, 255, 255, 0.9)'
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -291,6 +312,12 @@ const handleLowFreqUpdate = (volume: number) => {
|
|||||||
</Transition>
|
</Transition>
|
||||||
<div class="playbox">
|
<div class="playbox">
|
||||||
<div class="left" :style="state.lyricLines.length <= 0 && 'width:100vw'">
|
<div class="left" :style="state.lyricLines.length <= 0 && 'width:100vw'">
|
||||||
|
<img
|
||||||
|
class="pointer"
|
||||||
|
:class="{ playing: Audio.isPlay }"
|
||||||
|
src="@renderer/assets/pointer.png"
|
||||||
|
alt="pointer"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
class="cd-container"
|
class="cd-container"
|
||||||
:class="{ playing: Audio.isPlay }"
|
:class="{ playing: Audio.isPlay }"
|
||||||
@@ -331,7 +358,7 @@ const handleLowFreqUpdate = (volume: number) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 音频可视化组件 -->
|
<!-- 音频可视化组件 -->
|
||||||
<div class="audio-visualizer-container" v-if="props.show && coverImage">
|
<div v-if="props.show && coverImage" class="audio-visualizer-container">
|
||||||
<AudioVisualizer
|
<AudioVisualizer
|
||||||
:show="props.show && Audio.isPlay"
|
:show="props.show && Audio.isPlay"
|
||||||
:height="70"
|
:height="70"
|
||||||
@@ -441,12 +468,13 @@ const handleLowFreqUpdate = (volume: number) => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgba(0, 0, 0, 0.256);
|
background-color: rgba(0, 0, 0, 0.256);
|
||||||
-webkit-drop-filter: blur(10px);
|
-webkit-drop-filter: blur(80px);
|
||||||
padding: 0 10vw;
|
padding: 0 10vw;
|
||||||
-webkit-drop-filter: blur(10px);
|
-webkit-drop-filter: blur(80px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
--cd-width-auto: max(200px, min(30vw, 700px, calc(100vh - var(--play-bottom-height) - 250px)));
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
width: 40%;
|
width: 40%;
|
||||||
@@ -464,9 +492,24 @@ const handleLowFreqUpdate = (volume: number) => {
|
|||||||
margin: 0 0 var(--play-bottom-height) 0;
|
margin: 0 0 var(--play-bottom-height) 0;
|
||||||
perspective: 1000px;
|
perspective: 1000px;
|
||||||
|
|
||||||
|
.pointer {
|
||||||
|
position: absolute;
|
||||||
|
width: calc(var(--cd-width-auto) / 3.5);
|
||||||
|
left: calc(50% - 1.8vh);
|
||||||
|
top: calc(50% - var(--cd-width-auto) / 2 - calc(var(--cd-width-auto) / 3.5) - 1vh);
|
||||||
|
transform: rotate(-20deg);
|
||||||
|
transform-origin: 1.8vh 1.8vh;
|
||||||
|
z-index: 2;
|
||||||
|
transition: transform 0.3s;
|
||||||
|
|
||||||
|
&.playing {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.cd-container {
|
.cd-container {
|
||||||
width: min(30vw, 700px);
|
width: var(--cd-width-auto);
|
||||||
height: min(30vw, 700px);
|
height: var(--cd-width-auto);
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -620,14 +663,33 @@ const handleLowFreqUpdate = (volume: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.right {
|
.right {
|
||||||
|
mask: linear-gradient(
|
||||||
|
rgba(255, 255, 255, 0) 0px,
|
||||||
|
rgba(255, 255, 255, 0.6) 5%,
|
||||||
|
rgb(255, 255, 255) 10%,
|
||||||
|
rgb(255, 255, 255) 75%,
|
||||||
|
rgba(255, 255, 255, 0.6) 85%,
|
||||||
|
rgba(255, 255, 255, 0)
|
||||||
|
);
|
||||||
|
|
||||||
:deep(.lyric-player) {
|
:deep(.lyric-player) {
|
||||||
|
--amll-lyric-view-color: v-bind(lightMainColor);
|
||||||
font-family: lyricfont;
|
font-family: lyricfont;
|
||||||
--amll-lyric-player-font-size: min(2.6vw, 32px);
|
--amll-lyric-player-font-size: min(2.6vw, 39px);
|
||||||
|
|
||||||
// bottom: max(2vw, 29px);
|
// bottom: max(2vw, 29px);
|
||||||
|
|
||||||
height: 200%;
|
height: 200%;
|
||||||
transform: translateY(-25%);
|
transform: translateY(-25%);
|
||||||
|
|
||||||
|
* [class^='lyricMainLine'] {
|
||||||
|
font-weight: 600 !important;
|
||||||
|
|
||||||
|
* {
|
||||||
|
font-weight: 600 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -665,6 +727,7 @@ const handleLowFreqUpdate = (volume: number) => {
|
|||||||
0% {
|
0% {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
@@ -675,10 +738,12 @@ const handleLowFreqUpdate = (volume: number) => {
|
|||||||
opacity: 0.1;
|
opacity: 0.1;
|
||||||
transform: rotate(0deg) scale(1);
|
transform: rotate(0deg) scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
transform: rotate(180deg) scale(1.1);
|
transform: rotate(180deg) scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
opacity: 0.1;
|
opacity: 0.1;
|
||||||
transform: rotate(360deg) scale(1);
|
transform: rotate(360deg) scale(1);
|
||||||
@@ -690,16 +755,20 @@ const handleLowFreqUpdate = (volume: number) => {
|
|||||||
opacity: 0.05;
|
opacity: 0.05;
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
25% {
|
25% {
|
||||||
opacity: 0.15;
|
opacity: 0.15;
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
opacity: 0.1;
|
opacity: 0.1;
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
75% {
|
75% {
|
||||||
opacity: 0.15;
|
opacity: 0.15;
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
opacity: 0.05;
|
opacity: 0.05;
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ onUnmounted(() => {
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<audio
|
<audio
|
||||||
|
id="globaAudio"
|
||||||
ref="audioMeta"
|
ref="audioMeta"
|
||||||
preload="auto"
|
preload="auto"
|
||||||
:src="audioStore.Audio.url"
|
:src="audioStore.Audio.url"
|
||||||
@@ -131,7 +132,6 @@ onUnmounted(() => {
|
|||||||
@loadeddata="handleLoadedData"
|
@loadeddata="handleLoadedData"
|
||||||
@ended="handleEnded"
|
@ended="handleEnded"
|
||||||
@canplay="handleCanPlay"
|
@canplay="handleCanPlay"
|
||||||
id="globaAudio"
|
|
||||||
></audio>
|
></audio>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
destroyPlaylistEventListeners,
|
destroyPlaylistEventListeners,
|
||||||
getSongRealUrl
|
getSongRealUrl
|
||||||
} from '@renderer/utils/playlistManager'
|
} from '@renderer/utils/playlistManager'
|
||||||
|
import mediaSessionController from '@renderer/utils/useAmtc'
|
||||||
import defaultCoverImg from '/default-cover.png'
|
import defaultCoverImg from '/default-cover.png'
|
||||||
|
|
||||||
const controlAudio = ControlAudioStore()
|
const controlAudio = ControlAudioStore()
|
||||||
@@ -41,7 +42,7 @@ const removeMusicCtrlListener = window.api.onMusicCtrl(() => {
|
|||||||
togglePlayPause()
|
togglePlayPause()
|
||||||
})
|
})
|
||||||
let timer: any = null
|
let timer: any = null
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
||||||
function throttle(callback: Function, delay: number) {
|
function throttle(callback: Function, delay: number) {
|
||||||
if (timer) return
|
if (timer) return
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
@@ -132,6 +133,9 @@ let isFull = false
|
|||||||
// 播放指定歌曲
|
// 播放指定歌曲
|
||||||
const playSong = async (song: SongList) => {
|
const playSong = async (song: SongList) => {
|
||||||
try {
|
try {
|
||||||
|
// 设置加载状态
|
||||||
|
isLoadingSong.value = true
|
||||||
|
|
||||||
// 检查是否需要恢复播放位置(历史播放)
|
// 检查是否需要恢复播放位置(历史播放)
|
||||||
const isHistoryPlay =
|
const isHistoryPlay =
|
||||||
song.songmid === userInfo.value.lastPlaySongId &&
|
song.songmid === userInfo.value.lastPlaySongId &&
|
||||||
@@ -163,6 +167,14 @@ const playSong = async (song: SongList) => {
|
|||||||
...song
|
...song
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新媒体会话元数据
|
||||||
|
mediaSessionController.updateMetadata({
|
||||||
|
title: song.name,
|
||||||
|
artist: song.singer,
|
||||||
|
album: song.albumName || '未知专辑',
|
||||||
|
artworkUrl: song.img || defaultCoverImg
|
||||||
|
})
|
||||||
|
|
||||||
// 确保主题色更新
|
// 确保主题色更新
|
||||||
await setColor()
|
await setColor()
|
||||||
|
|
||||||
@@ -198,8 +210,8 @@ const playSong = async (song: SongList) => {
|
|||||||
// 等待音频准备就绪
|
// 等待音频准备就绪
|
||||||
await waitForAudioReady()
|
await waitForAudioReady()
|
||||||
|
|
||||||
// 短暂延迟确保音频状态稳定
|
// // 短暂延迟确保音频状态稳定
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
// await new Promise((resolve) => setTimeout(resolve, 100))
|
||||||
|
|
||||||
// 开始播放
|
// 开始播放
|
||||||
try {
|
try {
|
||||||
@@ -229,6 +241,9 @@ const playSong = async (song: SongList) => {
|
|||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('播放歌曲失败:', error)
|
console.error('播放歌曲失败:', error)
|
||||||
MessagePlugin.error('播放失败,原因:' + error.message)
|
MessagePlugin.error('播放失败,原因:' + error.message)
|
||||||
|
} finally {
|
||||||
|
// 无论成功还是失败,都清除加载状态
|
||||||
|
isLoadingSong.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
provide('PlaySong', playSong)
|
provide('PlaySong', playSong)
|
||||||
@@ -236,6 +251,9 @@ provide('PlaySong', playSong)
|
|||||||
// const playMode = ref(userInfo.value.playMode || PlayMode.SEQUENCE)
|
// const playMode = ref(userInfo.value.playMode || PlayMode.SEQUENCE)
|
||||||
const playMode = ref(PlayMode.SEQUENCE)
|
const playMode = ref(PlayMode.SEQUENCE)
|
||||||
|
|
||||||
|
// 歌曲加载状态
|
||||||
|
const isLoadingSong = ref(false)
|
||||||
|
|
||||||
// 更新播放模式
|
// 更新播放模式
|
||||||
const updatePlayMode = () => {
|
const updatePlayMode = () => {
|
||||||
const modes = [PlayMode.SEQUENCE, PlayMode.RANDOM, PlayMode.SINGLE]
|
const modes = [PlayMode.SEQUENCE, PlayMode.RANDOM, PlayMode.SINGLE]
|
||||||
@@ -270,8 +288,6 @@ const showVolumeSlider = ref(false)
|
|||||||
const volumeBarRef = ref<HTMLDivElement | null>(null)
|
const volumeBarRef = ref<HTMLDivElement | null>(null)
|
||||||
const isDraggingVolume = ref(false)
|
const isDraggingVolume = ref(false)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const volumeValue = computed({
|
const volumeValue = computed({
|
||||||
get: () => Audio.value.volume,
|
get: () => Audio.value.volume,
|
||||||
set: (val) => {
|
set: (val) => {
|
||||||
@@ -415,6 +431,26 @@ onMounted(async () => {
|
|||||||
// 初始化播放列表事件监听器
|
// 初始化播放列表事件监听器
|
||||||
initPlaylistEventListeners(localUserStore, playSong)
|
initPlaylistEventListeners(localUserStore, playSong)
|
||||||
|
|
||||||
|
// 初始化媒体会话控制器
|
||||||
|
if (Audio.value.audio) {
|
||||||
|
mediaSessionController.init(Audio.value.audio, {
|
||||||
|
play: async () => {
|
||||||
|
// 专门的播放函数,只处理播放逻辑
|
||||||
|
if (!Audio.value.isPlay) {
|
||||||
|
await handlePlay()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
pause: async () => {
|
||||||
|
// 专门的暂停函数,只处理暂停逻辑
|
||||||
|
if (Audio.value.isPlay) {
|
||||||
|
await handlePause()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
playPrevious: () => playPrevious(),
|
||||||
|
playNext: () => playNext()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 监听音频结束事件,根据播放模式播放下一首
|
// 监听音频结束事件,根据播放模式播放下一首
|
||||||
unEnded = controlAudio.subscribe('ended', () => {
|
unEnded = controlAudio.subscribe('ended', () => {
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
@@ -432,6 +468,14 @@ onMounted(async () => {
|
|||||||
...lastPlayedSong
|
...lastPlayedSong
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 立即更新媒体会话元数据,让系统显示当前歌曲信息
|
||||||
|
mediaSessionController.updateMetadata({
|
||||||
|
title: lastPlayedSong.name,
|
||||||
|
artist: lastPlayedSong.singer,
|
||||||
|
album: lastPlayedSong.albumName || '未知专辑',
|
||||||
|
artworkUrl: lastPlayedSong.img || defaultCoverImg
|
||||||
|
})
|
||||||
|
|
||||||
// 如果有历史播放位置,设置为待恢复状态
|
// 如果有历史播放位置,设置为待恢复状态
|
||||||
if (!Audio.value.isPlay) {
|
if (!Audio.value.isPlay) {
|
||||||
if (userInfo.value.currentTime && userInfo.value.currentTime > 0) {
|
if (userInfo.value.currentTime && userInfo.value.currentTime > 0) {
|
||||||
@@ -451,6 +495,9 @@ onMounted(async () => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取上次播放歌曲URL失败:', error)
|
console.error('获取上次播放歌曲URL失败:', error)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 如果当前正在播放,设置状态为播放中
|
||||||
|
mediaSessionController.updatePlaybackState('playing')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -463,8 +510,6 @@ onMounted(async () => {
|
|||||||
}, 1000) // 每1秒保存一次
|
}, 1000) // 每1秒保存一次
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 组件卸载时清理
|
// 组件卸载时清理
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
destroyPlaylistEventListeners()
|
destroyPlaylistEventListeners()
|
||||||
@@ -475,6 +520,8 @@ onUnmounted(() => {
|
|||||||
if (removeMusicCtrlListener) {
|
if (removeMusicCtrlListener) {
|
||||||
removeMusicCtrlListener()
|
removeMusicCtrlListener()
|
||||||
}
|
}
|
||||||
|
// 清理媒体会话控制器
|
||||||
|
mediaSessionController.cleanup()
|
||||||
unEnded()
|
unEnded()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -559,15 +606,18 @@ const formatTime = (seconds: number) => {
|
|||||||
const currentTimeFormatted = computed(() => formatTime(Audio.value.currentTime))
|
const currentTimeFormatted = computed(() => formatTime(Audio.value.currentTime))
|
||||||
const durationFormatted = computed(() => formatTime(Audio.value.duration))
|
const durationFormatted = computed(() => formatTime(Audio.value.duration))
|
||||||
|
|
||||||
// 播放/暂停切换
|
// 专门的播放函数
|
||||||
const togglePlayPause = async () => {
|
const handlePlay = async () => {
|
||||||
if (Audio.value.url) {
|
if (!Audio.value.url) {
|
||||||
if (Audio.value.isPlay) {
|
// 如果没有URL但有播放列表,尝试播放第一首歌
|
||||||
const stopResult = stop()
|
if (list.value.length > 0) {
|
||||||
if (stopResult && typeof stopResult.then === 'function') {
|
await playSong(list.value[0])
|
||||||
await stopResult
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
MessagePlugin.warning('播放列表为空,请先添加歌曲')
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 检查是否需要恢复历史播放位置
|
// 检查是否需要恢复历史播放位置
|
||||||
if (pendingRestorePosition > 0 && pendingRestoreSongId === userInfo.value.lastPlaySongId) {
|
if (pendingRestorePosition > 0 && pendingRestoreSongId === userInfo.value.lastPlaySongId) {
|
||||||
@@ -595,14 +645,24 @@ const togglePlayPause = async () => {
|
|||||||
console.error('播放失败:', error)
|
console.error('播放失败:', error)
|
||||||
MessagePlugin.error('播放失败,请重试')
|
MessagePlugin.error('播放失败,请重试')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 专门的暂停函数
|
||||||
|
const handlePause = async () => {
|
||||||
|
if (Audio.value.url && Audio.value.isPlay) {
|
||||||
|
const stopResult = stop()
|
||||||
|
if (stopResult && typeof stopResult.then === 'function') {
|
||||||
|
await stopResult
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// 如果没有URL但有播放列表,尝试播放第一首歌
|
|
||||||
if (list.value.length > 0) {
|
|
||||||
await playSong(list.value[0])
|
|
||||||
} else {
|
|
||||||
MessagePlugin.warning('播放列表为空,请先添加歌曲')
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放/暂停切换
|
||||||
|
const togglePlayPause = async () => {
|
||||||
|
if (Audio.value.isPlay) {
|
||||||
|
await handlePause()
|
||||||
|
} else {
|
||||||
|
await handlePlay()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -754,8 +814,8 @@ watch(showFullPlay, (val) => {
|
|||||||
<div class="player-content">
|
<div class="player-content">
|
||||||
<!-- 左侧:封面和歌曲信息 -->
|
<!-- 左侧:封面和歌曲信息 -->
|
||||||
<div class="left-section">
|
<div class="left-section">
|
||||||
<div class="album-cover" v-if="songInfo.songmid">
|
<div v-if="songInfo.songmid" class="album-cover">
|
||||||
<img :src="songInfo.img" alt="专辑封面" v-if="songInfo.img" />
|
<img v-if="songInfo.img" :src="songInfo.img" alt="专辑封面" />
|
||||||
<img :src="defaultCoverImg" alt="默认封面" />
|
<img :src="defaultCoverImg" alt="默认封面" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -770,11 +830,18 @@ watch(showFullPlay, (val) => {
|
|||||||
<t-button class="control-btn" variant="text" shape="circle" @click.stop="playPrevious">
|
<t-button class="control-btn" variant="text" shape="circle" @click.stop="playPrevious">
|
||||||
<span class="iconfont icon-shangyishou"></span>
|
<span class="iconfont icon-shangyishou"></span>
|
||||||
</t-button>
|
</t-button>
|
||||||
<button class="control-btn play-btn" @click.stop="togglePlayPause">
|
<button
|
||||||
<transition name="fade" mode="out-in">
|
class="control-btn play-btn"
|
||||||
|
:disabled="isLoadingSong"
|
||||||
|
@click.stop="() => !isLoadingSong && togglePlayPause()"
|
||||||
|
>
|
||||||
|
<Transition name="loadSong" mode="out-in">
|
||||||
|
<div v-if="isLoadingSong" key="loading" class="loading-spinner play-loading"></div>
|
||||||
|
<transition v-else name="fade" mode="out-in">
|
||||||
<span v-if="Audio.isPlay" key="play" class="iconfont icon-zanting"></span>
|
<span v-if="Audio.isPlay" key="play" class="iconfont icon-zanting"></span>
|
||||||
<span v-else key="pause" class="iconfont icon-bofang"></span>
|
<span v-else key="pause" class="iconfont icon-bofang"></span>
|
||||||
</transition>
|
</transition>
|
||||||
|
</Transition>
|
||||||
</button>
|
</button>
|
||||||
<t-button class="control-btn" shape="circle" variant="text" @click.stop="playNext">
|
<t-button class="control-btn" shape="circle" variant="text" @click.stop="playNext">
|
||||||
<span class="iconfont icon-xiayishou"></span>
|
<span class="iconfont icon-xiayishou"></span>
|
||||||
@@ -830,7 +897,7 @@ watch(showFullPlay, (val) => {
|
|||||||
|
|
||||||
<!-- 播放列表按钮 -->
|
<!-- 播放列表按钮 -->
|
||||||
<t-tooltip content="播放列表">
|
<t-tooltip content="播放列表">
|
||||||
<t-badge :count="list.length" :maxCount="99" color="#aaa">
|
<t-badge :count="list.length" :max-count="99" color="#aaa">
|
||||||
<t-button
|
<t-button
|
||||||
class="control-btn"
|
class="control-btn"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
@@ -850,9 +917,9 @@ watch(showFullPlay, (val) => {
|
|||||||
:song-id="songInfo.songmid ? songInfo.songmid.toString() : null"
|
:song-id="songInfo.songmid ? songInfo.songmid.toString() : null"
|
||||||
:show="showFullPlay"
|
:show="showFullPlay"
|
||||||
:cover-image="songInfo.img"
|
:cover-image="songInfo.img"
|
||||||
@toggle-fullscreen="toggleFullPlay"
|
|
||||||
:song-info="songInfo"
|
:song-info="songInfo"
|
||||||
:main-color="maincolor"
|
:main-color="maincolor"
|
||||||
|
@toggle-fullscreen="toggleFullPlay"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -886,6 +953,55 @@ watch(showFullPlay, (val) => {
|
|||||||
transform: rotate(-180deg);
|
transform: rotate(-180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 加载动画 */
|
||||||
|
.loading-spinner {
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-top: 2px solid v-bind(hoverColor);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 播放按钮中的加载动画 */
|
||||||
|
.play-loading {
|
||||||
|
width: 20px !important;
|
||||||
|
height: 20px !important;
|
||||||
|
margin: 4px;
|
||||||
|
border-width: 3px;
|
||||||
|
border-color: rgba(255, 255, 255, 0.3);
|
||||||
|
border-top-color: v-bind(hoverColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 加载歌曲过渡动画 - 缩小透明效果 */
|
||||||
|
.loadSong-enter-active,
|
||||||
|
.loadSong-leave-active {
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadSong-enter-from,
|
||||||
|
.loadSong-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadSong-enter-to,
|
||||||
|
.loadSong-leave-from {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
.player-container {
|
.player-container {
|
||||||
box-shadow: 0px -2px 20px 0px #00000039;
|
box-shadow: 0px -2px 20px 0px #00000039;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -1211,8 +1327,6 @@ watch(showFullPlay, (val) => {
|
|||||||
transform: translateY(10px) scale(0.95);
|
transform: translateY(10px) scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* 响应式设计 */
|
/* 响应式设计 */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.right-section .time-display {
|
.right-section .time-display {
|
||||||
|
|||||||
@@ -76,8 +76,6 @@ const scrollToCurrentSong = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 关闭播放列表
|
// 关闭播放列表
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
emit('close')
|
emit('close')
|
||||||
@@ -134,7 +132,12 @@ const handleTouchStart = (event: TouchEvent, index: number, song: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 拖拽排序相关方法
|
// 拖拽排序相关方法
|
||||||
const handlePointerStart = (event: MouseEvent | TouchEvent, index: number, song: any, isTouch: boolean) => {
|
const handlePointerStart = (
|
||||||
|
event: MouseEvent | TouchEvent,
|
||||||
|
index: number,
|
||||||
|
song: any,
|
||||||
|
isTouch: boolean
|
||||||
|
) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
|
||||||
@@ -192,7 +195,12 @@ const handlePointerStart = (event: MouseEvent | TouchEvent, index: number, song:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果没有发生长按且没有在拖拽模式,说明是正常点击,触发播放
|
// 如果没有发生长按且没有在拖拽模式,说明是正常点击,触发播放
|
||||||
if (!wasLongPressed.value && !wasInDragMode && hadLongPressTimer && currentOperatingSong.value) {
|
if (
|
||||||
|
!wasLongPressed.value &&
|
||||||
|
!wasInDragMode &&
|
||||||
|
hadLongPressTimer &&
|
||||||
|
currentOperatingSong.value
|
||||||
|
) {
|
||||||
// 短暂延迟后播放,确保状态已经稳定
|
// 短暂延迟后播放,确保状态已经稳定
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
emit('playSong', currentOperatingSong.value)
|
emit('playSong', currentOperatingSong.value)
|
||||||
@@ -258,7 +266,8 @@ const updateDragOverIndex = (clientY: number) => {
|
|||||||
|
|
||||||
// 检查是否可以滚动
|
// 检查是否可以滚动
|
||||||
const canScrollUp = playlistContainer.scrollTop > 0
|
const canScrollUp = playlistContainer.scrollTop > 0
|
||||||
const canScrollDown = playlistContainer.scrollTop < (playlistContainer.scrollHeight - playlistContainer.clientHeight)
|
const canScrollDown =
|
||||||
|
playlistContainer.scrollTop < playlistContainer.scrollHeight - playlistContainer.clientHeight
|
||||||
|
|
||||||
if (distanceFromTop < scrollThreshold && distanceFromTop > 0 && canScrollUp) {
|
if (distanceFromTop < scrollThreshold && distanceFromTop > 0 && canScrollUp) {
|
||||||
// 向上滚动
|
// 向上滚动
|
||||||
@@ -302,7 +311,11 @@ const updateDragOverIndex = (clientY: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 实时更新列表顺序进行预览
|
// 实时更新列表顺序进行预览
|
||||||
if (newOverIndex !== dragOverIndex.value && newOverIndex >= 0 && newOverIndex <= list.value.length) {
|
if (
|
||||||
|
newOverIndex !== dragOverIndex.value &&
|
||||||
|
newOverIndex >= 0 &&
|
||||||
|
newOverIndex <= list.value.length
|
||||||
|
) {
|
||||||
dragOverIndex.value = newOverIndex
|
dragOverIndex.value = newOverIndex
|
||||||
updatePreviewList()
|
updatePreviewList()
|
||||||
}
|
}
|
||||||
@@ -418,19 +431,14 @@ defineExpose({
|
|||||||
<p>播放列表为空</p>
|
<p>播放列表为空</p>
|
||||||
<p>请添加歌曲到播放列表,也可在设置中导入歌曲列表</p>
|
<p>请添加歌曲到播放列表,也可在设置中导入歌曲列表</p>
|
||||||
</div>
|
</div>
|
||||||
<TransitionGroup
|
<TransitionGroup v-else :class="playlistSongsClass" name="list-item" tag="div">
|
||||||
v-else
|
|
||||||
:class="playlistSongsClass"
|
|
||||||
name="list-item"
|
|
||||||
tag="div"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
v-for="(song, index) in list"
|
v-for="(song, index) in list"
|
||||||
:key="song.songmid"
|
:key="song.songmid"
|
||||||
class="playlist-song"
|
class="playlist-song"
|
||||||
:class="{
|
:class="{
|
||||||
active: song.songmid === currentSongId,
|
active: song.songmid === currentSongId,
|
||||||
'dragging': isDragSorting && index === draggedIndex
|
dragging: isDragSorting && index === draggedIndex
|
||||||
}"
|
}"
|
||||||
@mousedown="handleMouseDown($event, index, song)"
|
@mousedown="handleMouseDown($event, index, song)"
|
||||||
@touchstart="handleTouchStart($event, index, song)"
|
@touchstart="handleTouchStart($event, index, song)"
|
||||||
@@ -438,7 +446,7 @@ defineExpose({
|
|||||||
@mouseleave="handleMouseLeave"
|
@mouseleave="handleMouseLeave"
|
||||||
>
|
>
|
||||||
<!-- 拖拽手柄 -->
|
<!-- 拖拽手柄 -->
|
||||||
<div class="drag-handle" v-if="isDragSorting && index === draggedIndex">
|
<div v-if="isDragSorting && index === draggedIndex" class="drag-handle">
|
||||||
<span class="drag-dots">⋮⋮</span>
|
<span class="drag-dots">⋮⋮</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -459,11 +467,7 @@ defineExpose({
|
|||||||
|
|
||||||
<!-- 悬停提示 -->
|
<!-- 悬停提示 -->
|
||||||
<transition name="hover-tip">
|
<transition name="hover-tip">
|
||||||
<div
|
<div v-if="hoverTipVisible && hoverTipIndex === index" class="hover-tip" @click.stop>
|
||||||
v-if="hoverTipVisible && hoverTipIndex === index"
|
|
||||||
class="hover-tip"
|
|
||||||
@click.stop
|
|
||||||
>
|
|
||||||
长按可拖动排序
|
长按可拖动排序
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ const canvasRef = ref<HTMLCanvasElement | null>(null)
|
|||||||
let gl: WebGLRenderingContext | null = null
|
let gl: WebGLRenderingContext | null = null
|
||||||
let program: WebGLProgram | null = null
|
let program: WebGLProgram | null = null
|
||||||
let animationFrameId: number | null = null
|
let animationFrameId: number | null = null
|
||||||
let startTime = Date.now()
|
const startTime = Date.now()
|
||||||
let dominantColor = ref({ r: 0.3, g: 0.3, b: 0.5 })
|
const dominantColor = ref({ r: 0.3, g: 0.3, b: 0.5 })
|
||||||
let colorPalette = ref<Color[]>([
|
const colorPalette = ref<Color[]>([
|
||||||
{ r: 76, g: 116, b: 206 }, // 蓝色
|
{ r: 76, g: 116, b: 206 }, // 蓝色
|
||||||
{ r: 120, g: 80, b: 180 }, // 紫色
|
{ r: 120, g: 80, b: 180 }, // 紫色
|
||||||
{ r: 60, g: 160, b: 160 } // 青色
|
{ r: 60, g: 160, b: 160 } // 青色
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
<h3>自动更新</h3>
|
<h3>自动更新</h3>
|
||||||
<div class="update-info">
|
<div class="update-info">
|
||||||
<p>当前版本: {{ currentVersion }}</p>
|
<p>当前版本: {{ currentVersion }}</p>
|
||||||
<t-button theme="primary" :loading="isChecking" @click="handleCheckUpdate">
|
<TButton theme="primary" :loading="isChecking" @click="handleCheckUpdate">
|
||||||
{{ isChecking ? '检查中...' : '检查更新' }}
|
{{ isChecking ? '检查中...' : '检查更新' }}
|
||||||
</t-button>
|
</TButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,13 @@ import { useRouter } from 'vue-router'
|
|||||||
import { LocalUserDetailStore } from '@renderer/store/LocalUserDetail'
|
import { LocalUserDetailStore } from '@renderer/store/LocalUserDetail'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
controlStyle: false,
|
||||||
|
showSettings: true,
|
||||||
|
showBack: false,
|
||||||
|
title: '',
|
||||||
|
color: 'black'
|
||||||
|
})
|
||||||
const Store = LocalUserDetailStore()
|
const Store = LocalUserDetailStore()
|
||||||
const { userInfo } = storeToRefs(Store)
|
const { userInfo } = storeToRefs(Store)
|
||||||
|
|
||||||
@@ -18,14 +25,6 @@ interface Props {
|
|||||||
color?: string
|
color?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
|
||||||
controlStyle: false,
|
|
||||||
showSettings: true,
|
|
||||||
showBack: false,
|
|
||||||
title: '',
|
|
||||||
color: 'black'
|
|
||||||
})
|
|
||||||
|
|
||||||
// Mini 模式现在是直接隐藏到系统托盘,不需要状态跟踪
|
// Mini 模式现在是直接隐藏到系统托盘,不需要状态跟踪
|
||||||
|
|
||||||
// 计算样式类名
|
// 计算样式类名
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<span>已下载: {{ formatBytes(downloadState.progress.transferred) }}</span>
|
<span>已下载: {{ formatBytes(downloadState.progress.transferred) }}</span>
|
||||||
<span>总大小: {{ formatBytes(downloadState.progress.total) }}</span>
|
<span>总大小: {{ formatBytes(downloadState.progress.total) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="download-speed" v-if="downloadSpeed > 0">
|
<div v-if="downloadSpeed > 0" class="download-speed">
|
||||||
下载速度: {{ formatBytes(downloadSpeed) }}/s
|
下载速度: {{ formatBytes(downloadSpeed) }}/s
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createWebHashHistory, createRouter, RouteRecordRaw, RouterOptions } from 'vue-router'
|
import { createWebHashHistory, createRouter, RouteRecordRaw, RouterOptions } from 'vue-router'
|
||||||
|
|
||||||
let routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'welcome',
|
name: 'welcome',
|
||||||
@@ -55,7 +55,7 @@ let routes: RouteRecordRaw[] = [
|
|||||||
]
|
]
|
||||||
function setAnimate(routerObj: RouteRecordRaw[]) {
|
function setAnimate(routerObj: RouteRecordRaw[]) {
|
||||||
for (let i = 0; i < routerObj.length; i++) {
|
for (let i = 0; i < routerObj.length; i++) {
|
||||||
let item = routerObj[i]
|
const item = routerObj[i]
|
||||||
if (item.children && item.children.length > 0) {
|
if (item.children && item.children.length > 0) {
|
||||||
setAnimate(item.children)
|
setAnimate(item.children)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -180,7 +180,6 @@ export const ControlAudioStore = defineStore('controlAudio', () => {
|
|||||||
}
|
}
|
||||||
const start = async () => {
|
const start = async () => {
|
||||||
const volume = Audio.volume
|
const volume = Audio.volume
|
||||||
console.log('开始播放音频111', volume)
|
|
||||||
if (Audio.audio) {
|
if (Audio.audio) {
|
||||||
Audio.audio.volume = 0
|
Audio.audio.volume = 0
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { NotifyPlugin } from 'tdesign-vue-next'
|
import { NotifyPlugin, MessagePlugin } from 'tdesign-vue-next'
|
||||||
import { LocalUserDetailStore } from '@renderer/store/LocalUserDetail'
|
import { LocalUserDetailStore } from '@renderer/store/LocalUserDetail'
|
||||||
import { toRaw } from 'vue'
|
import { toRaw } from 'vue'
|
||||||
import { MessagePlugin } from 'tdesign-vue-next'
|
|
||||||
|
|
||||||
interface MusicItem {
|
interface MusicItem {
|
||||||
singer: string
|
singer: string
|
||||||
|
|||||||
161
src/renderer/src/utils/useAmtc.ts
Normal file
161
src/renderer/src/utils/useAmtc.ts
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
interface MediaSessionCallbacks {
|
||||||
|
play: () => void
|
||||||
|
pause: () => void
|
||||||
|
playPrevious: () => void
|
||||||
|
playNext: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TrackMetadata {
|
||||||
|
title: string
|
||||||
|
artist: string
|
||||||
|
album: string
|
||||||
|
artworkUrl: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Media Session API 控制器
|
||||||
|
* 用于管理浏览器的媒体会话,支持系统级媒体控制
|
||||||
|
*/
|
||||||
|
class MediaSessionController {
|
||||||
|
private audioElement: HTMLAudioElement | null = null
|
||||||
|
private callbacks: MediaSessionCallbacks | null = null
|
||||||
|
private eventListeners: Array<{
|
||||||
|
element: HTMLAudioElement
|
||||||
|
event: string
|
||||||
|
handler: EventListener
|
||||||
|
}> = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查浏览器是否支持 Media Session API
|
||||||
|
*/
|
||||||
|
private get isSupported(): boolean {
|
||||||
|
return 'mediaSession' in navigator
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新媒体会话元数据
|
||||||
|
*/
|
||||||
|
updateMetadata(metadata: TrackMetadata): void {
|
||||||
|
if (!this.isSupported) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
navigator.mediaSession.metadata = new MediaMetadata({
|
||||||
|
title: metadata.title,
|
||||||
|
artist: metadata.artist,
|
||||||
|
album: metadata.album,
|
||||||
|
artwork: this.generateArtworkSizes(metadata.artworkUrl)
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to update media session metadata:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成不同尺寸的封面图片配置
|
||||||
|
*/
|
||||||
|
private generateArtworkSizes(artworkUrl: string): MediaImage[] {
|
||||||
|
const sizes = ['96x96', '128x128', '192x192', '256x256', '384x384', '512x512']
|
||||||
|
return sizes.map((size) => ({
|
||||||
|
src: artworkUrl,
|
||||||
|
sizes: size,
|
||||||
|
type: 'image/png'
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化媒体会话控制器
|
||||||
|
*/
|
||||||
|
init(audioElement: HTMLAudioElement, callbacks: MediaSessionCallbacks): void {
|
||||||
|
if (!this.isSupported) {
|
||||||
|
console.warn('Media Session API is not supported in this browser')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理之前的监听器
|
||||||
|
this.cleanup()
|
||||||
|
|
||||||
|
this.audioElement = audioElement
|
||||||
|
this.callbacks = callbacks
|
||||||
|
|
||||||
|
// 只设置媒体会话动作处理器,不自动监听音频事件
|
||||||
|
// 让应用层手动控制播放状态更新,避免循环调用
|
||||||
|
this.setupMediaSessionActionHandlers()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置媒体会话动作处理器
|
||||||
|
*/
|
||||||
|
private setupMediaSessionActionHandlers(): void {
|
||||||
|
if (!this.callbacks) return
|
||||||
|
|
||||||
|
const actionHandlers: Array<[MediaSessionAction, () => void]> = [
|
||||||
|
['play', this.callbacks.play],
|
||||||
|
['pause', this.callbacks.pause],
|
||||||
|
['previoustrack', this.callbacks.playPrevious],
|
||||||
|
['nexttrack', this.callbacks.playNext]
|
||||||
|
]
|
||||||
|
|
||||||
|
actionHandlers.forEach(([action, handler]) => {
|
||||||
|
navigator.mediaSession.setActionHandler(action, handler)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 设置 seekto 处理器
|
||||||
|
navigator.mediaSession.setActionHandler('seekto', (details) => {
|
||||||
|
if (!this.audioElement || !details.seekTime) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (details.fastSeek && 'fastSeek' in this.audioElement) {
|
||||||
|
this.audioElement.fastSeek(details.seekTime)
|
||||||
|
} else {
|
||||||
|
this.audioElement.currentTime = details.seekTime
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to seek audio:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新播放状态
|
||||||
|
*/
|
||||||
|
updatePlaybackState(state: MediaSessionPlaybackState): void {
|
||||||
|
if (!this.isSupported) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
navigator.mediaSession.playbackState = state
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to update playback state:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理事件监听器和媒体会话
|
||||||
|
*/
|
||||||
|
cleanup(): void {
|
||||||
|
// 移除音频事件监听器
|
||||||
|
this.eventListeners.forEach(({ element, event, handler }) => {
|
||||||
|
element.removeEventListener(event, handler)
|
||||||
|
})
|
||||||
|
this.eventListeners = []
|
||||||
|
|
||||||
|
// 清理媒体会话动作处理器
|
||||||
|
if (this.isSupported) {
|
||||||
|
const actions: MediaSessionAction[] = [
|
||||||
|
'play',
|
||||||
|
'pause',
|
||||||
|
'previoustrack',
|
||||||
|
'nexttrack',
|
||||||
|
'seekto'
|
||||||
|
]
|
||||||
|
actions.forEach((action) => {
|
||||||
|
navigator.mediaSession.setActionHandler(action, null)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.audioElement = null
|
||||||
|
this.callbacks = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出单例实例
|
||||||
|
export default new MediaSessionController()
|
||||||
@@ -142,7 +142,7 @@ onUnmounted(() => {
|
|||||||
<!-- 错误状态 -->
|
<!-- 错误状态 -->
|
||||||
<div v-else-if="error" class="error-container">
|
<div v-else-if="error" class="error-container">
|
||||||
<t-alert theme="error" :message="error" />
|
<t-alert theme="error" :message="error" />
|
||||||
<t-button theme="primary" @click="fetchHotSonglist" style="margin-top: 1rem">
|
<t-button theme="primary" style="margin-top: 1rem" @click="fetchHotSonglist">
|
||||||
重新加载
|
重新加载
|
||||||
</t-button>
|
</t-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -161,24 +161,22 @@ onUnmounted(() => {
|
|||||||
<div
|
<div
|
||||||
class="playlist-info"
|
class="playlist-info"
|
||||||
:style="{
|
:style="{
|
||||||
'background-color': mainColors[index],
|
'--hover-bg-color': mainColors[index],
|
||||||
color: textColors[index]
|
'--hover-text-color': textColors[index]
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<h4 class="playlist-title" :style="{ color: textColors[index] }">
|
<h4 class="playlist-title">
|
||||||
{{ playlist.title }}
|
{{ playlist.title }}
|
||||||
</h4>
|
</h4>
|
||||||
<p class="playlist-desc" :style="{ color: textColors[index] }">
|
<p class="playlist-desc">
|
||||||
{{ playlist.description }}
|
{{ playlist.description }}
|
||||||
</p>
|
</p>
|
||||||
<div class="playlist-meta">
|
<div class="playlist-meta">
|
||||||
<span class="play-count" :style="{ color: textColors[index] }">
|
<span class="play-count">
|
||||||
<i class="iconfont icon-bofang"></i>
|
<i class="iconfont icon-bofang"></i>
|
||||||
{{ playlist.playCount }}
|
{{ playlist.playCount }}
|
||||||
</span>
|
</span>
|
||||||
<span class="song-count" v-if="playlist.total" :style="{ color: textColors[index] }"
|
<span v-if="playlist.total" class="song-count">{{ playlist.total }}首</span>
|
||||||
>{{ playlist.total }}首</span
|
|
||||||
>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="playlist-author">by {{ playlist.author }}</div> -->
|
<!-- <div class="playlist-author">by {{ playlist.author }}</div> -->
|
||||||
</div>
|
</div>
|
||||||
@@ -312,6 +310,23 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
.playlist-info {
|
.playlist-info {
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
|
background-color: var(--hover-bg-color);
|
||||||
|
color: #111827;
|
||||||
|
.playlist-title {
|
||||||
|
color: var(--hover-text-color);
|
||||||
|
}
|
||||||
|
.playlist-desc {
|
||||||
|
color: var(--hover-text-color);
|
||||||
|
}
|
||||||
|
.playlist-meta {
|
||||||
|
color: var(--hover-text-color);
|
||||||
|
* {
|
||||||
|
color: var(--hover-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.playlist-author {
|
||||||
|
color: var(--hover-text-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,8 +372,9 @@ onUnmounted(() => {
|
|||||||
padding: 1.25rem 1rem;
|
padding: 1.25rem 1rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
|
||||||
backdrop-filter: blur(4px);
|
backdrop-filter: blur(4px);
|
||||||
transition: backdrop-filter 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
.playlist-title {
|
.playlist-title {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
@@ -384,6 +400,7 @@ onUnmounted(() => {
|
|||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
min-height: 2.625rem; // 确保描述区域高度一致
|
min-height: 2.625rem; // 确保描述区域高度一致
|
||||||
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.playlist-meta {
|
.playlist-meta {
|
||||||
@@ -394,6 +411,7 @@ onUnmounted(() => {
|
|||||||
margin-top: auto; // 推到底部
|
margin-top: auto; // 推到底部
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
border-top: 1px solid rgba(229, 231, 235, 0.5);
|
border-top: 1px solid rgba(229, 231, 235, 0.5);
|
||||||
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.play-count {
|
.play-count {
|
||||||
@@ -403,6 +421,7 @@ onUnmounted(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.25rem;
|
gap: 0.25rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
|
||||||
.iconfont {
|
.iconfont {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
@@ -417,6 +436,7 @@ onUnmounted(() => {
|
|||||||
background: rgba(156, 163, 175, 0.1);
|
background: rgba(156, 163, 175, 0.1);
|
||||||
padding: 0.125rem 0.5rem;
|
padding: 0.125rem 0.5rem;
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.playlist-author {
|
.playlist-author {
|
||||||
@@ -425,6 +445,7 @@ onUnmounted(() => {
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
margin-top: 0.25rem;
|
margin-top: 0.25rem;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,9 +312,9 @@ onMounted(() => {
|
|||||||
<t-button
|
<t-button
|
||||||
theme="primary"
|
theme="primary"
|
||||||
size="medium"
|
size="medium"
|
||||||
@click="handlePlayPlaylist"
|
|
||||||
:disabled="songs.length === 0 || loading"
|
:disabled="songs.length === 0 || loading"
|
||||||
class="play-btn"
|
class="play-btn"
|
||||||
|
@click="handlePlayPlaylist"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<svg class="play-icon" viewBox="0 0 24 24" fill="currentColor">
|
<svg class="play-icon" viewBox="0 0 24 24" fill="currentColor">
|
||||||
@@ -327,9 +327,9 @@ onMounted(() => {
|
|||||||
<t-button
|
<t-button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="medium"
|
size="medium"
|
||||||
@click="handleShufflePlaylist"
|
|
||||||
:disabled="songs.length === 0 || loading"
|
:disabled="songs.length === 0 || loading"
|
||||||
class="shuffle-btn"
|
class="shuffle-btn"
|
||||||
|
@click="handleShufflePlaylist"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<svg class="shuffle-icon" viewBox="0 0 24 24" fill="currentColor">
|
<svg class="shuffle-icon" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
|||||||
@@ -684,12 +684,12 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="playlist-info">
|
<div class="playlist-info">
|
||||||
<div class="playlist-name" @click="viewPlaylist(playlist)" :title="playlist.name">
|
<div class="playlist-name" :title="playlist.name" @click="viewPlaylist(playlist)">
|
||||||
{{ playlist.name }}
|
{{ playlist.name }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="playlist-description"
|
|
||||||
v-if="playlist.description"
|
v-if="playlist.description"
|
||||||
|
class="playlist-description"
|
||||||
:title="playlist.description"
|
:title="playlist.description"
|
||||||
>
|
>
|
||||||
{{ playlist.description }}
|
{{ playlist.description }}
|
||||||
@@ -860,8 +860,8 @@ onMounted(() => {
|
|||||||
|
|
||||||
<!-- 创建歌单对话框 -->
|
<!-- 创建歌单对话框 -->
|
||||||
<t-dialog
|
<t-dialog
|
||||||
placement="center"
|
|
||||||
v-model:visible="showCreatePlaylistDialog"
|
v-model:visible="showCreatePlaylistDialog"
|
||||||
|
placement="center"
|
||||||
header="创建新歌单"
|
header="创建新歌单"
|
||||||
width="500px"
|
width="500px"
|
||||||
:confirm-btn="{ content: '创建', theme: 'primary' }"
|
:confirm-btn="{ content: '创建', theme: 'primary' }"
|
||||||
@@ -892,8 +892,8 @@ onMounted(() => {
|
|||||||
|
|
||||||
<!-- 导入选择对话框 -->
|
<!-- 导入选择对话框 -->
|
||||||
<t-dialog
|
<t-dialog
|
||||||
placement="center"
|
|
||||||
v-model:visible="showImportDialog"
|
v-model:visible="showImportDialog"
|
||||||
|
placement="center"
|
||||||
header="选择导入方式"
|
header="选择导入方式"
|
||||||
width="400px"
|
width="400px"
|
||||||
:footer="false"
|
:footer="false"
|
||||||
@@ -928,14 +928,14 @@ onMounted(() => {
|
|||||||
</t-dialog>
|
</t-dialog>
|
||||||
<!-- 网络歌单导入对话框 -->
|
<!-- 网络歌单导入对话框 -->
|
||||||
<t-dialog
|
<t-dialog
|
||||||
placement="center"
|
|
||||||
v-model:visible="showNetworkImportDialog"
|
v-model:visible="showNetworkImportDialog"
|
||||||
|
placement="center"
|
||||||
header="导入网易云音乐歌单"
|
header="导入网易云音乐歌单"
|
||||||
:confirm-btn="{ content: '开始导入', theme: 'primary' }"
|
:confirm-btn="{ content: '开始导入', theme: 'primary' }"
|
||||||
:cancel-btn="{ content: '取消', variant: 'outline' }"
|
:cancel-btn="{ content: '取消', variant: 'outline' }"
|
||||||
|
width="500px"
|
||||||
@confirm="confirmNetworkImport"
|
@confirm="confirmNetworkImport"
|
||||||
@cancel="cancelNetworkImport"
|
@cancel="cancelNetworkImport"
|
||||||
width="500px"
|
|
||||||
>
|
>
|
||||||
<div class="network-import-content">
|
<div class="network-import-content">
|
||||||
<p class="import-description">
|
<p class="import-description">
|
||||||
@@ -967,14 +967,14 @@ onMounted(() => {
|
|||||||
|
|
||||||
<!-- 编辑歌单对话框 -->
|
<!-- 编辑歌单对话框 -->
|
||||||
<t-dialog
|
<t-dialog
|
||||||
placement="center"
|
|
||||||
v-model:visible="showEditPlaylistDialog"
|
v-model:visible="showEditPlaylistDialog"
|
||||||
|
placement="center"
|
||||||
header="编辑歌单信息"
|
header="编辑歌单信息"
|
||||||
:confirm-btn="{ content: '保存', theme: 'primary' }"
|
:confirm-btn="{ content: '保存', theme: 'primary' }"
|
||||||
:cancel-btn="{ content: '取消', variant: 'outline' }"
|
:cancel-btn="{ content: '取消', variant: 'outline' }"
|
||||||
|
width="500px"
|
||||||
@confirm="savePlaylistEdit"
|
@confirm="savePlaylistEdit"
|
||||||
@cancel="cancelPlaylistEdit"
|
@cancel="cancelPlaylistEdit"
|
||||||
width="500px"
|
|
||||||
>
|
>
|
||||||
<div class="edit-playlist-content">
|
<div class="edit-playlist-content">
|
||||||
<div class="form-item">
|
<div class="form-item">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import TitleBarControls from '@renderer/components/TitleBarControls.vue'
|
import TitleBarControls from '@renderer/components/TitleBarControls.vue'
|
||||||
import PlaylistSettings from '@renderer/components/Settings/PlaylistSettings.vue'
|
import PlaylistSettings from '@renderer/components/Settings/PlaylistSettings.vue'
|
||||||
import { ref } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { LocalUserDetailStore } from '@renderer/store/LocalUserDetail'
|
import { LocalUserDetailStore } from '@renderer/store/LocalUserDetail'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
import {
|
import {
|
||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
} from 'tdesign-icons-vue-next'
|
} from 'tdesign-icons-vue-next'
|
||||||
import fonts from '@renderer/assets/icon_font/icons'
|
import fonts from '@renderer/assets/icon_font/icons'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { computed, watch } from 'vue'
|
|
||||||
import MusicCache from '@renderer/components/Settings/MusicCache.vue'
|
import MusicCache from '@renderer/components/Settings/MusicCache.vue'
|
||||||
import AIFloatBallSettings from '@renderer/components/Settings/AIFloatBallSettings.vue'
|
import AIFloatBallSettings from '@renderer/components/Settings/AIFloatBallSettings.vue'
|
||||||
import ThemeSelector from '@renderer/components/ThemeSelector.vue'
|
import ThemeSelector from '@renderer/components/ThemeSelector.vue'
|
||||||
@@ -26,7 +25,6 @@ const { userInfo } = storeToRefs(Store)
|
|||||||
|
|
||||||
// 当前选择的设置分类
|
// 当前选择的设置分类
|
||||||
const activeCategory = ref<string>('appearance')
|
const activeCategory = ref<string>('appearance')
|
||||||
|
|
||||||
// 应用版本号
|
// 应用版本号
|
||||||
const appVersion = ref('1.0.0')
|
const appVersion = ref('1.0.0')
|
||||||
|
|
||||||
@@ -477,7 +475,7 @@ const openLink = (url: string) => {
|
|||||||
<div class="source-name">{{ source.name }}</div>
|
<div class="source-name">{{ source.name }}</div>
|
||||||
<div class="source-type">{{ source.type || '音乐源' }}</div>
|
<div class="source-type">{{ source.type || '音乐源' }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="source-check" v-if="userInfo.selectSources === key">
|
<div v-if="userInfo.selectSources === key" class="source-check">
|
||||||
<i class="iconfont icon-check"></i>
|
<i class="iconfont icon-check"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -494,8 +492,8 @@ const openLink = (url: string) => {
|
|||||||
:step="1"
|
:step="1"
|
||||||
:marks="qualityMarks"
|
:marks="qualityMarks"
|
||||||
:label="qualityMarks[qualitySliderValue]"
|
:label="qualityMarks[qualitySliderValue]"
|
||||||
@change="onQualityChange"
|
|
||||||
class="quality-slider"
|
class="quality-slider"
|
||||||
|
@change="onQualityChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="quality-description">
|
<div class="quality-description">
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div class="page">
|
||||||
<TitleBarControls title="插件管理" :show-back="true" class="header"></TitleBarControls>
|
<TitleBarControls title="插件管理" :show-back="true" class="header"></TitleBarControls>
|
||||||
<div class="plugins-container">
|
<div class="plugins-container">
|
||||||
<h2>插件管理</h2>
|
<h2>插件管理</h2>
|
||||||
@@ -77,8 +78,8 @@
|
|||||||
<t-button
|
<t-button
|
||||||
theme="default"
|
theme="default"
|
||||||
size="small"
|
size="small"
|
||||||
@click.stop="viewPluginLogs(plugin.pluginId, plugin.pluginInfo.name)"
|
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
|
@click.stop="viewPluginLogs(plugin.pluginId, plugin.pluginInfo.name)"
|
||||||
>
|
>
|
||||||
<template #icon><t-icon name="view-list" /></template> 日志
|
<template #icon><t-icon name="view-list" /></template> 日志
|
||||||
</t-button>
|
</t-button>
|
||||||
@@ -174,6 +175,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</t-dialog>
|
</t-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -213,7 +215,7 @@ const plugins = ref<Plugin[]>([])
|
|||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const error = ref<string | null>(null)
|
const error = ref<string | null>(null)
|
||||||
const plugTypeDialog = ref(false)
|
const plugTypeDialog = ref(false)
|
||||||
let type = ref<'lx' | 'cr'>('cr')
|
const type = ref<'lx' | 'cr'>('cr')
|
||||||
|
|
||||||
// 日志相关状态
|
// 日志相关状态
|
||||||
const logDialogVisible = ref(false)
|
const logDialogVisible = ref(false)
|
||||||
@@ -381,7 +383,7 @@ async function uninstallPlugin(pluginId: string, pluginName: string) {
|
|||||||
// 卸载成功才刷新插件列表
|
// 卸载成功才刷新插件列表
|
||||||
await getPlugins()
|
await getPlugins()
|
||||||
// 显示成功消息
|
// 显示成功消息
|
||||||
if (pluginId == localUserStore.userInfo.pluginId) {
|
if (pluginId === localUserStore.userInfo.pluginId) {
|
||||||
localUserStore.userInfo.pluginId = ''
|
localUserStore.userInfo.pluginId = ''
|
||||||
localUserStore.userInfo.supportedSources = {}
|
localUserStore.userInfo.supportedSources = {}
|
||||||
localUserStore.userInfo.selectSources = ''
|
localUserStore.userInfo.selectSources = ''
|
||||||
@@ -508,6 +510,11 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
.page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
.header {
|
.header {
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -522,8 +529,13 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.plugins-container {
|
.plugins-container {
|
||||||
|
flex: 1;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plugin-actions {
|
.plugin-actions {
|
||||||
@@ -590,6 +602,9 @@ onMounted(async () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plugin-item {
|
.plugin-item {
|
||||||
@@ -676,6 +691,7 @@ onMounted(async () => {
|
|||||||
/* 日志弹窗样式 */
|
/* 日志弹窗样式 */
|
||||||
:deep(.log-dialog) {
|
:deep(.log-dialog) {
|
||||||
height: 80vh;
|
height: 80vh;
|
||||||
|
|
||||||
.t-dialog {
|
.t-dialog {
|
||||||
background: #1e1e1e;
|
background: #1e1e1e;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -710,6 +726,7 @@ onMounted(async () => {
|
|||||||
background: linear-gradient(135deg, #2d2d2d 0%, #1e1e1e 100%);
|
background: linear-gradient(135deg, #2d2d2d 0%, #1e1e1e 100%);
|
||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.log-title {
|
.log-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -753,6 +770,7 @@ onMounted(async () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
|
|
||||||
.mac-button {
|
.mac-button {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
@@ -762,6 +780,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
&.close {
|
&.close {
|
||||||
background: #ff5f57;
|
background: #ff5f57;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #ff3b30;
|
background: #ff3b30;
|
||||||
}
|
}
|
||||||
@@ -769,6 +788,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
&.minimize {
|
&.minimize {
|
||||||
background: #ffbd2e;
|
background: #ffbd2e;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #ff9500;
|
background: #ff9500;
|
||||||
}
|
}
|
||||||
@@ -776,6 +796,7 @@ onMounted(async () => {
|
|||||||
|
|
||||||
&.maximize {
|
&.maximize {
|
||||||
background: #28ca42;
|
background: #28ca42;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #30d158;
|
background: #30d158;
|
||||||
}
|
}
|
||||||
@@ -938,6 +959,7 @@ onMounted(async () => {
|
|||||||
.log-content {
|
.log-content {
|
||||||
color: #ff6b6b;
|
color: #ff6b6b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-timestamp {
|
.log-timestamp {
|
||||||
color: #ff6b6b;
|
color: #ff6b6b;
|
||||||
}
|
}
|
||||||
@@ -947,6 +969,7 @@ onMounted(async () => {
|
|||||||
.log-content {
|
.log-content {
|
||||||
color: #ffd93d;
|
color: #ffd93d;
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-timestamp {
|
.log-timestamp {
|
||||||
color: #ffd93d;
|
color: #ffd93d;
|
||||||
}
|
}
|
||||||
@@ -956,6 +979,7 @@ onMounted(async () => {
|
|||||||
.log-content {
|
.log-content {
|
||||||
color: #74b9ff;
|
color: #74b9ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-timestamp {
|
.log-timestamp {
|
||||||
color: #74b9ff;
|
color: #74b9ff;
|
||||||
}
|
}
|
||||||
@@ -965,6 +989,7 @@ onMounted(async () => {
|
|||||||
.log-content {
|
.log-content {
|
||||||
color: #a29bfe;
|
color: #a29bfe;
|
||||||
}
|
}
|
||||||
|
|
||||||
.log-timestamp {
|
.log-timestamp {
|
||||||
color: #a29bfe;
|
color: #a29bfe;
|
||||||
}
|
}
|
||||||
@@ -982,6 +1007,7 @@ onMounted(async () => {
|
|||||||
0% {
|
0% {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: rotate(360deg);
|
transform: rotate(360deg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<!-- 特性标签 -->
|
<!-- 特性标签 -->
|
||||||
<div class="feature-tags">
|
<div class="feature-tags">
|
||||||
<span class="tag" v-for="(feature, index) in features" :key="index">
|
<span v-for="(feature, index) in features" :key="index" class="tag">
|
||||||
{{ feature }}
|
{{ feature }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user