From a277cb71810ecadc0dcd6d4ea105c16d0250b674 Mon Sep 17 00:00:00 2001 From: sqj Date: Sat, 15 Nov 2025 13:26:42 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=85=B7=E7=8B=97=E6=AD=8C=E5=8D=95?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E6=92=AD=E6=94=BE=EF=BC=8C=E8=BD=AF=E4=BB=B6?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E9=80=80=E5=87=BA=EF=BC=8C=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E8=BF=9B=E5=85=A5=E8=AE=BE=E7=BD=AE=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E4=B8=8B=E4=B8=80=E6=9B=B2=20feat:=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 - playlist-converter.html | 692 +++ src/common/types/localSongs.ts | 9 + src/common/types/playList.ts | 3 +- src/main/events/index.ts | 2 + src/main/events/localMusic.ts | 334 ++ src/main/events/lyric.ts | 22 + src/main/events/pluginNotice.ts | 8 +- src/main/index.ts | 4 +- src/main/services/LocalMusicIndex.ts | 113 + src/main/services/songList/PlayListSongs.ts | 1 - src/main/windows/lyric-window.ts | 5 +- src/preload/index.d.ts | 10 + src/preload/index.ts | 25 + src/renderer/components.d.ts | 5 + src/renderer/src/App.vue | 8 + src/renderer/src/components/Play/FullPlay.vue | 178 +- .../src/components/Play/PlayMusic.vue | 215 +- .../src/components/layout/HomeLayout.vue | 7 +- src/renderer/src/router/index.ts | 5 + src/renderer/src/utils/audio/audioManager.ts | 11 +- .../src/utils/audio/globalControls.ts | 117 + .../src/utils/lyrics/desktopLyricBridge.ts | 229 + .../lx_list_part_list__name_default.json | 4202 +++++++++++++++++ .../lx_list_part_list__name_default.lxmc | Bin 0 -> 11735 bytes .../src/utils/playlist/playlistManager.ts | 7 + src/renderer/src/views/music/list.vue | 15 +- src/renderer/src/views/music/local.vue | 2384 ++-------- src/renderer/src/views/music/recent.vue | 1 + src/renderer/src/views/music/songlist.vue | 2139 +++++++++ src/web/lyric.html | 296 +- 31 files changed, 8730 insertions(+), 2318 deletions(-) create mode 100644 playlist-converter.html create mode 100644 src/common/types/localSongs.ts create mode 100644 src/main/events/localMusic.ts create mode 100644 src/main/services/LocalMusicIndex.ts create mode 100644 src/renderer/src/utils/audio/globalControls.ts create mode 100644 src/renderer/src/utils/lyrics/desktopLyricBridge.ts create mode 100644 src/renderer/src/utils/playlist/lx_list_part_list__name_default.json create mode 100644 src/renderer/src/utils/playlist/lx_list_part_list__name_default.lxmc create mode 100644 src/renderer/src/views/music/songlist.vue diff --git a/package.json b/package.json index 141c8fe..cb1ad20 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "@pixi/sprite": "^7.4.3", "@types/howler": "^2.2.12", "@types/needle": "^3.3.0", - "NeteaseCloudMusicApi": "^4.27.0", "animate.css": "^4.1.1", "axios": "^1.11.0", "color-extraction": "^1.0.8", diff --git a/playlist-converter.html b/playlist-converter.html new file mode 100644 index 0000000..9cc9347 --- /dev/null +++ b/playlist-converter.html @@ -0,0 +1,692 @@ + + + + + + 歌单格式转换器 + + + + +
+
+

🎵 澜音歌单格式转换器

+

将洛雪音乐平台的歌单转换为澜音音乐的加密格式

+
+ +
+
+
📁
+
点击或拖拽洛雪歌单文件到此处
+
支持 *.json 和 *.lxmc 格式的歌单文件
+ +
+ +
+
+
歌单预览
+
0 首歌曲
+
+
+ +
+
+ + + +
+
+
正在转换中...
+
+ + +
+
+ + + + diff --git a/src/common/types/localSongs.ts b/src/common/types/localSongs.ts new file mode 100644 index 0000000..cdd4739 --- /dev/null +++ b/src/common/types/localSongs.ts @@ -0,0 +1,9 @@ +export default interface localList { + singer: string + name: string + albumName: string + interval: string + duration: string + img: string + lrc: null | string +} diff --git a/src/common/types/playList.ts b/src/common/types/playList.ts index 409238b..59e613e 100644 --- a/src/common/types/playList.ts +++ b/src/common/types/playList.ts @@ -11,5 +11,6 @@ export default interface PlayList { lrc: null | string types: string[] _types: Record - typeUrl: Record + typeUrl?: Record + url?: string } diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 9603708..e4342da 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -1,11 +1,13 @@ import InitPluginService from './plugins' import '../services/musicSdk/index' import aiEvents from '../events/ai' +// import initLocalMusicEvents from './localMusic' import { app, powerSaveBlocker } from 'electron' import { type BrowserWindow, ipcMain } from 'electron' export default function InitEventServices(mainWindow: BrowserWindow) { InitPluginService() aiEvents(mainWindow) + // initLocalMusicEvents() basisEvent(mainWindow) } diff --git a/src/main/events/localMusic.ts b/src/main/events/localMusic.ts new file mode 100644 index 0000000..8f487b2 --- /dev/null +++ b/src/main/events/localMusic.ts @@ -0,0 +1,334 @@ +import { ipcMain, dialog, app } from 'electron' +import fs from 'fs' +import fsp from 'fs/promises' +import path from 'node:path' +import crypto from 'crypto' +import { localMusicIndexService } from '../services/LocalMusicIndex' +// remove static import to avoid runtime failures if native module is missing + +const AUDIO_EXTS = new Set(['.mp3', '.flac', '.wav', '.aac', '.m4a', '.ogg', '.wma']) + +function genId(input: string) { + return crypto.createHash('md5').update(input).digest('hex') +} + +async function walkDir(dir: string, results: string[]) { + try { + const items = await fsp.readdir(dir, { withFileTypes: true }) + for (const item of items) { + const full = path.join(dir, item.name) + if (item.isDirectory()) { + await walkDir(full, results) + } else { + const ext = path.extname(item.name).toLowerCase() + if (AUDIO_EXTS.has(ext)) results.push(full) + } + } + } catch { } +} + +function readTags(filePath: string) { + try { + const taglib = require('node-taglib-sharp') + const f = taglib.File.createFromPath(filePath) + const tag = f.tag + const title = tag.title || '' + const album = tag.album || '' + const performers = Array.isArray(tag.performers) ? tag.performers : [] + let img = '' + if (Array.isArray(tag.pictures) && tag.pictures.length > 0) { + try { + const buf = tag.pictures[0].data + const mime = tag.pictures[0].mimeType || 'image/jpeg' + img = `data:${mime};base64,${Buffer.from(buf).toString('base64')}` + } catch { } + } + let lrc: string | null = null + try { + const raw = tag.lyrics || '' + if (raw && typeof raw === 'string') { + lrc = normalizeLyricsToLrc(raw) + } + } catch { } + f.dispose() + return { title, album, performers, img, lrc } + } catch { + return { title: '', album: '', performers: [], img: '', lrc: null } + } +} + +// 将两种逐字/行内时间歌词统一转换为标准LRC(仅保留行时间标签) +function normalizeLyricsToLrc(input: string): string { + const lines = String(input).replace(/\r/g, '').split('\n') + const msFormat = (timeMs: number) => { + if (!Number.isFinite(timeMs)) return '' + const m = Math.floor(timeMs / 60000) + const s = Math.floor((timeMs % 60000) / 1000) + const ms = Math.floor(timeMs % 1000) + return `[${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}.${String(ms).padStart(3, '0')}]` + } + const out: string[] = [] + for (let line of lines) { + if (!line.trim()) { out.push(line); continue } + const off = /^\[offset:[+-]?\d+\]$/i.exec(line.trim()) + if (off) { out.push(line.trim()); continue } + let mNew = /^\[(\d+),(\d+)\](.*)$/.exec(line) + if (mNew) { + const startMs = parseInt(mNew[1]) + let text = mNew[3] || '' + text = text.replace(/\(\d+,\d+(?:,\d+)?\)/g, '') + text = text.replace(/<\d{2}:\d{2}\.\d{3}>/g, '') + if (/(<\d{2}:\d{2}\.\d{3}>)|(\(\d+,\d+(?:,\d+)?\))/.test(mNew[3] || '')) { + text = text.replace(/[()]/g, '') + } + text = text.replace(/\s+/g, ' ').trim() + const tag = msFormat(startMs) + out.push(`${tag}${text}`) + continue + } + let mOld = /^\[(\d{2}:\d{2}\.\d{3})\](.*)$/.exec(line) + if (mOld) { + let text = mOld[2] || '' + text = text.replace(/\(\d+,\d+(?:,\d+)?\)/g, '') + text = text.replace(/<\d{2}:\d{2}\.\d{3}>/g, '') + if (/(<\d{2}:\d{2}\.\d{3}>)|(\(\d+,\d+(?:,\d+)?\))/.test(mOld[2] || '')) { + text = text.replace(/[()]/g, '') + } + text = text.replace(/\s+/g, ' ').trim() + const tag = `[${mOld[1]}]` + out.push(`${tag}${text}`) + continue + } + out.push(line) + } + return out.join('\n') +} + +function timeToMs(s: string): number { + const m = /(\d{2}):(\d{2})\.(\d{3})/.exec(s) + if (!m) return NaN + return parseInt(m[1]) * 60000 + parseInt(m[2]) * 1000 + parseInt(m[3]) +} + +function normalizeLyricsToCrLyric(input: string): string { + const raw = String(input).replace(/\r/g, '') + const lines = raw.split('\n') + let offset = 0 + const res: string[] = [] + for (let i = 0; i < lines.length; i++) { + const line = lines[i] + if (!line.trim()) { res.push(line); continue } + const off = /^\[offset:([+-]?\d+)\]$/i.exec(line.trim()) + if (off) { offset = parseInt(off[1]) || 0; res.push(line); continue } + const yrcLike = /\[\d+,\d+\]/.test(line) && /\(\d+,\d+,\d+\)/.test(line) + if (yrcLike) { res.push(line); continue } + const mLine = /^\[(\d{2}:\d{2}\.\d{3})\](.*)$/.exec(line) + if (!mLine) { res.push(line); continue } + const lineStart = timeToMs(mLine[1]) + offset + let rest = mLine[2] + rest = rest.replace(/\(\d+,\d+(?:,\d+)?\)/g, '') + const segs: { start: number, text: string }[] = [] + const re = /<(\d{2}:\d{2}\.\d{3})>([^<]*)/g + let m: RegExpExecArray | null + while ((m = re.exec(rest))) { + const start = timeToMs(m[1]) + offset + const text = m[2] || '' + if (text) segs.push({ start, text }) + } + if (segs.length === 0) { res.push(line); continue } + let nextLineStart: number | null = null + for (let j = i + 1; j < lines.length; j++) { + const ml = /^\[(\d{2}:\d{2}\.\d{3})\]/.exec(lines[j]) + if (ml) { nextLineStart = timeToMs(ml[1]) + offset; break } + const skip = lines[j].trim() + if (!skip || /^\[offset:/.test(skip)) continue + break + } + const tokens: string[] = [] + for (let k = 0; k < segs.length; k++) { + const cur = segs[k] + const nextStart = k < segs.length - 1 ? segs[k + 1].start : (nextLineStart ?? (cur.start + 1000)) + const span = Math.max(1, nextStart - cur.start) + const chars = Array.from(cur.text).filter((ch) => !/\s/.test(ch)) + if (chars.length <= 1) { + if (chars.length === 1) tokens.push(`(${cur.start},${span},0)` + chars[0]) + } else { + const per = Math.max(1, Math.floor(span / chars.length)) + for (let c = 0; c < chars.length; c++) { + const cs = cur.start + c * per + const cd = c === chars.length - 1 ? Math.max(1, nextStart - cs) : per + tokens.push(`(${cs},${cd},0)` + chars[c]) + } + } + } + const lineEnd = nextLineStart ?? (segs[segs.length - 1].start + Math.max(1, (nextLineStart ?? (segs[segs.length - 1].start + 1000)) - segs[segs.length - 1].start)) + const ld = Math.max(0, lineEnd - lineStart) + res.push(`[${lineStart},${ld}]` + tokens.join(' ')) + } + return res.join('\n') +} + +ipcMain.handle('local-music:select-dirs', async () => { + const res = await dialog.showOpenDialog({ properties: ['openDirectory', 'multiSelections'] }) + if (res.canceled) return [] + return res.filePaths +}) + +ipcMain.handle('local-music:scan', async (_e, dirs: string[]) => { + if (!Array.isArray(dirs) || dirs.length === 0) { + return [] + } + const existsDirs = dirs.filter((d) => { + try { + return fs.existsSync(d) + } catch { + return false + } + }) + const files: string[] = [] + try { + for (const d of existsDirs) await walkDir(d, files) + const list = files.map((p) => { + let tags = { title: '', album: '', performers: [] as string[], img: '', lrc: null as null | string } + try { + tags = readTags(p) + } catch { } + const base = path.basename(p) + const noExt = base.replace(path.extname(base), '') + let name = tags.title || '' + let singer = '' + if (!name) { + const segs = noExt.split(/[-_]|\s{2,}/).map((s) => s.trim()).filter(Boolean) + if (segs.length >= 2) { + singer = segs[0] + name = segs.slice(1).join(' ') + } else { + name = noExt + } + } else { + singer = Array.isArray(tags.performers) && tags.performers.length > 0 ? tags.performers[0] : '' + } + const songmid = genId(p) + const item = { + songmid, + singer: singer || '未知艺术家', + name: name || '未知曲目', + albumName: tags.album || '未知专辑', + albumId: 0, + source: 'local', + interval: '', + img: tags.img || '', + lrc: tags.lrc || null, + types: [], + _types: {}, + typeUrl: {}, + url: 'file://' + p, + path: p + } + return item + }) + await localMusicIndexService.setDirs(existsDirs) + await localMusicIndexService.upsertSongs(list) + try { + return JSON.stringify(list) + } catch { + return '[]' + } + } catch (e) { + return '[]' + } +}) + +ipcMain.handle('local-music:write-tags', async (_e, payload: any) => { + const { filePath, songInfo, tagWriteOptions } = payload || {} + if (!filePath || !fs.existsSync(filePath)) return { success: false, message: '文件不存在' } + try { + const taglib = require('node-taglib-sharp') + const songFile = taglib.File.createFromPath(filePath) + taglib.Id3v2Settings.forceDefaultVersion = true + taglib.Id3v2Settings.defaultVersion = 3 + songFile.tag.title = songInfo?.name || '未知曲目' + songFile.tag.album = songInfo?.albumName || '未知专辑' + const artists = songInfo?.singer ? [songInfo.singer] : ['未知艺术家'] + songFile.tag.performers = artists + songFile.tag.albumArtists = artists + if (tagWriteOptions?.lyrics && songInfo?.lrc) { + const normalized = normalizeLyricsToLrc(songInfo.lrc) + songFile.tag.lyrics = normalized + } + if (tagWriteOptions?.cover && songInfo?.img) { + try { + if (songInfo.img.startsWith('data:')) { + const m = songInfo.img.match(/^data:(.*?);base64,(.*)$/) + if (m) { + const mime = m[1] + const buf = Buffer.from(m[2], 'base64') + const tmp = path.join(path.dirname(filePath), genId(filePath) + '.cover') + await fsp.writeFile(tmp, buf) + const pic = taglib.Picture.fromPath(tmp) + songFile.tag.pictures = [pic] + try { await fsp.unlink(tmp) } catch { } + } + } + } catch { } + } + songFile.save() + songFile.dispose() + const songmid = genId(filePath) + await localMusicIndexService.upsertSong({ + songmid, + singer: songInfo?.singer || '未知艺术家', + name: songInfo?.name || '未知曲目', + albumName: songInfo?.albumName || '未知专辑', + albumId: 0, + source: 'local', + interval: '', + img: songInfo?.img || '', + lrc: songInfo?.lrc || null, + types: [], + _types: {}, + typeUrl: {}, + url: 'file://' + filePath, + path: filePath + }) + return { success: true } + } catch (e: any) { + return { success: false, message: e?.message || '写入失败' } + } +}) + +ipcMain.handle('local-music:get-dirs', async () => { + return localMusicIndexService.getDirs() +}) + +ipcMain.handle('local-music:set-dirs', async (_e, dirs: string[]) => { + await localMusicIndexService.setDirs(Array.isArray(dirs) ? dirs : []) + return { success: true } +}) + +ipcMain.handle('local-music:get-list', async () => { + return localMusicIndexService.getAllSongs() +}) + +ipcMain.handle('local-music:get-url', async (_e, id: string | number) => { + const u = localMusicIndexService.getUrlById(id) + if (!u) return { error: '未找到本地文件' } + return u +}) + +ipcMain.handle('local-music:clear-index', async () => { + try { + const fn = (localMusicIndexService as any).clearSongs + if (typeof fn === 'function') { + await fn.call(localMusicIndexService) + return { success: true } + } + const dirs = localMusicIndexService.getDirs() + const file = require('node:path').join(app.getPath('userData'), 'local-music-index.json') + const data = { songs: {}, dirs: Array.isArray(dirs) ? dirs : [], updatedAt: Date.now() } + await require('fs/promises').writeFile(file, JSON.stringify(data, null, 2)) + return { success: true } + } catch (e: any) { + return { success: false, message: e?.message || '清空失败' } + } +}) diff --git a/src/main/events/lyric.ts b/src/main/events/lyric.ts index 766ff3d..7ad1e13 100644 --- a/src/main/events/lyric.ts +++ b/src/main/events/lyric.ts @@ -28,6 +28,8 @@ const initLyricIpc = (mainWin?: BrowserWindow | null): void => { // 切换桌面歌词 ipcMain.on('change-desktop-lyric', (_event, val: boolean) => { + if (!lyricWin || lyricWin?.isDestroyed() || lyricWin?.webContents?.isDestroyed()) return + if (val) { lyricWin?.show() lyricWin?.setAlwaysOnTop(true, 'screen-saver') @@ -39,17 +41,37 @@ const initLyricIpc = (mainWin?: BrowserWindow | null): void => { // 音乐名称更改 ipcMain.on('play-song-change', (_, title) => { if (!title) return + if (!lyricWin || lyricWin?.isDestroyed() || lyricWin?.webContents?.isDestroyed()) return + lyricWin?.webContents.send('play-song-change', title) }) // 音乐歌词更改 ipcMain.on('play-lyric-change', (_, lyricData) => { if (!lyricData) return + if (!lyricWin || lyricWin?.isDestroyed() || lyricWin?.webContents?.isDestroyed()) return + lyricWin?.webContents.send('play-lyric-change', lyricData) }) + // 当前行索引变化(用于立即高亮切换) + ipcMain.on('play-lyric-index', (_, index: number) => { + if (index === undefined || index === null) return + if (!lyricWin || lyricWin?.isDestroyed() || lyricWin?.webContents?.isDestroyed()) return + + lyricWin?.webContents.send('play-lyric-index', index) + }) + + // 当前行进度(用于控制 30% 时机的延迟替换) + ipcMain.on('play-lyric-progress', (_, payload: { index: number; progress: number }) => { + if (!payload || !lyricWin || lyricWin?.isDestroyed() || lyricWin?.webContents?.isDestroyed()) + return + lyricWin?.webContents.send('play-lyric-progress', payload) + }) + // 播放状态更改(播放/暂停) ipcMain.on('play-status-change', (_, status: boolean) => { + if (!lyricWin || lyricWin?.isDestroyed() || lyricWin?.webContents?.isDestroyed()) return lyricWin?.webContents.send('play-status-change', status) }) diff --git a/src/main/events/pluginNotice.ts b/src/main/events/pluginNotice.ts index 783abcf..6786bdd 100644 --- a/src/main/events/pluginNotice.ts +++ b/src/main/events/pluginNotice.ts @@ -11,6 +11,12 @@ import { BrowserWindow } from 'electron' +let mainWindow: BrowserWindow | null = null + +export function initPluginNotice(mainWindowInstance: BrowserWindow): void { + mainWindow = mainWindowInstance +} + export interface PluginNoticeData { type: 'error' | 'info' | 'success' | 'warn' | 'update' data: { @@ -100,7 +106,6 @@ export function sendPluginNotice(noticeData: PluginNoticeData, pluginName?: stri // console.log(`[CeruMusic] 插件通知: ${noticeData.type}`, noticeData.data) // 获取主窗口实例 - const mainWindow = BrowserWindow.getAllWindows().find((win) => !win.isDestroyed()) if (!mainWindow) { console.warn('[CeruMusic] 未找到主窗口,无法发送通知') return @@ -152,7 +157,6 @@ export function sendPluginNotice(noticeData: PluginNoticeData, pluginName?: stri getDefaultMessage(noticeData.type, noticeData.data, baseNoticeData.pluginName), actions: [{ text: '我知道了', type: 'confirm', primary: true }] } - mainWindow.webContents.send('plugin-notice', infoNotice) } } catch (error: any) { diff --git a/src/main/index.ts b/src/main/index.ts index 531f653..636599c 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -23,6 +23,8 @@ import './events/songList' import './events/directorySettings' import './events/pluginNotice' import initLyricIpc from './events/lyric' +import { initPluginNotice } from './events/pluginNotice' +import './events/localMusic' // 获取单实例锁 const gotTheLock = app.requestSingleInstanceLock() @@ -262,7 +264,7 @@ function createWindow(): void { return { action: 'deny' } }) InitEventServices(mainWindow) - + initPluginNotice(mainWindow) // HMR for renderer base on electron-vite cli. // Load the remote URL for development or the local html file for production. if (is.dev && process.env['ELECTRON_RENDERER_URL']) { diff --git a/src/main/services/LocalMusicIndex.ts b/src/main/services/LocalMusicIndex.ts new file mode 100644 index 0000000..aae44ea --- /dev/null +++ b/src/main/services/LocalMusicIndex.ts @@ -0,0 +1,113 @@ +import path from 'node:path' +import fs from 'fs' +import fsp from 'fs/promises' +import crypto from 'crypto' +import { configManager } from './ConfigManager' + +export interface MusicItem { + hash?: string + singer: string + name: string + albumName: string + albumId: number + source: string + interval: string + songmid: number | string + img: string + lrc: null | string + types: string[] + _types: Record + typeUrl: Record + url?: string + path?: string +} + +type IndexSchema = { + songs: Record + dirs: string[] + updatedAt: number +} + +function md5(input: string) { + return crypto.createHash('md5').update(input).digest('hex') +} + +export class LocalMusicIndexService { + private indexFile: string + private data: IndexSchema = { songs: {}, dirs: [], updatedAt: Date.now() } + + constructor() { + const userData = require('electron').app.getPath('userData') + this.indexFile = path.join(userData, 'local-music-index.json') + this.load() + } + + private load() { + try { + if (fs.existsSync(this.indexFile)) { + const raw = fs.readFileSync(this.indexFile, 'utf-8') + const obj = JSON.parse(raw) + if (obj && typeof obj === 'object') this.data = obj as IndexSchema + } + } catch { + this.data = { songs: {}, dirs: [], updatedAt: Date.now() } + } + } + + private async save() { + try { + const dir = path.dirname(this.indexFile) + await fsp.mkdir(dir, { recursive: true }) + await fsp.writeFile(this.indexFile, JSON.stringify(this.data, null, 2)) + } catch {} + } + + getDirs() { + return [...(this.data.dirs || [])] + } + + async setDirs(dirs: string[]) { + this.data.dirs = Array.from(new Set(dirs.filter(Boolean))) + this.data.updatedAt = Date.now() + await this.save() + } + + getAllSongs(): MusicItem[] { + return Object.values(this.data.songs) + } + + getSongById(id: string | number): MusicItem | null { + const key = String(id) + return this.data.songs[key] || null + } + + getUrlById(id: string | number): string | null { + const s = this.getSongById(id) + if (!s) return null + if (s.url && typeof s.url === 'string') return s.url + if (s.path && typeof s.path === 'string') return 'file://' + s.path + return null + } + + async upsertSong(item: MusicItem) { + const key = String(item.songmid ?? md5(String(item.path || ''))) + item.songmid = key + item.hash = md5(`${item.name}-${item.singer}-${item.source}`) + this.data.songs[key] = item + this.data.updatedAt = Date.now() + await this.save() + } + + async upsertSongs(items: MusicItem[]) { + for (const it of items) { + const key = String(it.songmid ?? md5(String(it.path || ''))) + it.songmid = key + it.hash = md5(`${it.name}-${it.singer}-${it.source}`) + this.data.songs[key] = it + } + this.data.updatedAt = Date.now() + await this.save() + } +} + +export const localMusicIndexService = new LocalMusicIndexService() diff --git a/src/main/services/songList/PlayListSongs.ts b/src/main/services/songList/PlayListSongs.ts index b28a6a5..0d51ad1 100644 --- a/src/main/services/songList/PlayListSongs.ts +++ b/src/main/services/songList/PlayListSongs.ts @@ -175,7 +175,6 @@ export default class PlayListSongs { if (!Array.isArray(songs) || songs.length === 0) { return } - // 验证和过滤有效歌曲 const validSongs = songs.filter(PlayListUtils.isValidSong) if (validSongs.length === 0) { diff --git a/src/main/windows/lyric-window.ts b/src/main/windows/lyric-window.ts index 597bea9..0287980 100644 --- a/src/main/windows/lyric-window.ts +++ b/src/main/windows/lyric-window.ts @@ -32,7 +32,7 @@ class LyricWindow { const bounds = this.win?.getBounds() if (bounds) { const { width, height } = bounds - console.log('歌词窗口缩放:', width, height); + console.log('歌词窗口缩放:', width, height) lyricStore.set({ ...lyricStore.get(), @@ -41,6 +41,9 @@ class LyricWindow { }) } }) + this.win?.on('closed', () => { + this.win = null + }) } /** * 创建主窗口 diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index 0e69bb1..6f9f437 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -126,6 +126,16 @@ interface CustomAPI { pluginNotice: { onPluginNotice: (listener: (...args: any[]) => void) => () => void } + + localMusic: { + selectDirs: () => Promise + scan: (dirs: string[]) => Promise + writeTags: ( + filePath: string, + songInfo: any, + tagWriteOptions: any + ) => Promise<{ success: boolean; message?: string }> + } } declare global { diff --git a/src/preload/index.ts b/src/preload/index.ts index 96cbc83..8cde254 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -190,6 +190,31 @@ const api = { ipcRenderer.invoke('directory-settings:get-directory-size', dirPath) }, + // 本地音乐管理 + localMusic: { + selectDirs: () => ipcRenderer.invoke('local-music:select-dirs'), + scan: async (dirs: string[]) => { + const res = await ipcRenderer.invoke('local-music:scan', dirs) + if (typeof res === 'string') { + try { + return JSON.parse(res) + } catch { + return [] + } + } + return Array.isArray(res) ? res : [] + }, + writeTags: (filePath: string, songInfo: any, tagWriteOptions: any) => + ipcRenderer.invoke('local-music:write-tags', { filePath, songInfo, tagWriteOptions }) + , + getDirs: () => ipcRenderer.invoke('local-music:get-dirs'), + setDirs: (dirs: string[]) => ipcRenderer.invoke('local-music:set-dirs', dirs), + getList: () => ipcRenderer.invoke('local-music:get-list'), + getUrlById: (id: string | number) => ipcRenderer.invoke('local-music:get-url', id) + , + clearIndex: () => ipcRenderer.invoke('local-music:clear-index') + }, + // 插件通知相关 pluginNotice: { onPluginNotice(callback: (data: string) => any) { diff --git a/src/renderer/components.d.ts b/src/renderer/components.d.ts index c0680aa..bc9f34a 100644 --- a/src/renderer/components.d.ts +++ b/src/renderer/components.d.ts @@ -20,9 +20,14 @@ declare module 'vue' { HomeLayout: typeof import('./src/components/layout/HomeLayout.vue')['default'] MusicCache: typeof import('./src/components/Settings/MusicCache.vue')['default'] NBadge: typeof import('naive-ui')['NBadge'] + NButton: typeof import('naive-ui')['NButton'] NCard: typeof import('naive-ui')['NCard'] NIcon: typeof import('naive-ui')['NIcon'] + NInput: typeof import('naive-ui')['NInput'] + NModal: typeof import('naive-ui')['NModal'] NScrollbar: typeof import('naive-ui')['NScrollbar'] + NSelect: typeof import('naive-ui')['NSelect'] + NTag: typeof import('naive-ui')['NTag'] NText: typeof import('naive-ui')['NText'] PlaylistActions: typeof import('./src/components/Play/PlaylistActions.vue')['default'] PlaylistDrawer: typeof import('./src/components/Play/PlaylistDrawer.vue')['default'] diff --git a/src/renderer/src/App.vue b/src/renderer/src/App.vue index cec6d45..535e409 100644 --- a/src/renderer/src/App.vue +++ b/src/renderer/src/App.vue @@ -31,6 +31,14 @@ onMounted(() => { syncNaiveTheme() window.addEventListener('theme-changed', () => syncNaiveTheme()) + // 全局键盘/托盘播放控制安装(解耦出组件) + import('@renderer/utils/audio/globalControls') + .then((m) => m.installGlobalMusicControls()) + .catch(() => {}) + import('@renderer/utils/lyrics/desktopLyricBridge') + .then((m) => m.installDesktopLyricBridge()) + .catch(() => {}) + // 全局监听来自主进程的播放控制事件,确保路由切换也可响应 const forward = (name: string, val?: any) => { window.dispatchEvent(new CustomEvent('global-music-control', { detail: { name, val } })) diff --git a/src/renderer/src/components/Play/FullPlay.vue b/src/renderer/src/components/Play/FullPlay.vue index b5bd3a2..5efee62 100644 --- a/src/renderer/src/components/Play/FullPlay.vue +++ b/src/renderer/src/components/Play/FullPlay.vue @@ -30,7 +30,7 @@ interface Props { show?: boolean coverImage?: string songId?: string | null - songInfo: SongList | { songmid: number | null | string } + songInfo: SongList | { songmid: number | null | string; lrc: string | null } mainColor: string } @@ -101,7 +101,6 @@ watch( active = false abort.abort() }) - // 工具函数:清洗响应式对象,避免序列化问题 const getCleanSongInfo = () => JSON.parse(JSON.stringify(toRaw(props.songInfo))) @@ -173,35 +172,100 @@ watch( let parsedLyrics: LyricLine[] = [] if (source === 'wy' || source === 'tx') { - // 网易云 / QQ 音乐:优先尝试 TTML + // 网易云 / QQ 音乐:优先尝试 TTML,同时准备备用方案 + // 1. 立即启动 SDK (回退) 请求,但不 await + // 将其 Promise 存储在 sdkPromise 变量中 + const sdkPromise = (async () => { + try { + const lyricData = await window.api.music.requestSdk('getLyric', { + source, + songInfo: _.cloneDeep(toRaw(props.songInfo)) as any + // 注意:这里的 abort.signal 是用于 TTML 的 + // 如果 requestSdk 也支持 signal,你可以考虑也传入 + }) + console.log('sdkPromise', lyricData) + // 依赖外部的 active 检查 + if (!active) return null + + let lyrics: null | LyricLine[] = null + if (lyricData?.crlyric) { + lyrics = parseCrLyricBySource(source, lyricData.crlyric) + } else if (lyricData?.lyric) { + lyrics = parseLrc(lyricData.lyric) + } + lyrics = mergeTranslation(lyrics as any, lyricData?.tlyric) + + // 如果 SDK 也拿不到歌词,返回 null + if (!lyrics || lyrics.length === 0) { + return null + } + return lyrics + } catch (err: any) { + // 如果 SDK 请求失败,抛出错误 + // 这样当 TTML 也失败时,可以捕获到这个 SDK 错误 + throw new Error(`SDK request failed: ${err.message}`) + } + })() + + // 2. 尝试 TTML (主要) 请求 try { const res = await ( await fetch( `https://amll-ttml-db.stevexmh.net/${source === 'wy' ? 'ncm' : 'qq'}/${newId}`, { - signal: abort.signal - }) + signal: abort.signal // TTML 请求使用 abort signal + } + ) ).text() - if (!active) return - if (!res || res.length < 100) throw new Error('ttml 无歌词') - parsedLyrics = parseTTML(res).lines - } catch { - // 回退到统一歌词 API - const lyricData = await window.api.music.requestSdk('getLyric', { - source, - songInfo: _.cloneDeep(toRaw(props.songInfo)) as any - }) + if (!active) return - if (lyricData?.crlyric) { - parsedLyrics = parseCrLyricBySource(source, lyricData.crlyric) - } else if (lyricData?.lyric) { - parsedLyrics = parseLrc(lyricData.lyric) + if (!res || res.length < 100) { + throw new Error('ttml 无歌词') // 抛出错误以触发 catch } - parsedLyrics = mergeTranslation(parsedLyrics, lyricData?.tlyric) + const ttmlLyrics = parseTTML(res).lines + + if (!ttmlLyrics || ttmlLyrics.length === 0) { + throw new Error('TTML 解析为空') // 抛出错误以触发 catch + } + + // --- TTML 成功 --- + parsedLyrics = ttmlLyrics + + // 此时我们不再关心 SDK 的结果 + // 为防止 sdkPromise 失败时出现 "unhandled rejection", + // 我们给它加一个空的 catch 来“静音”它的潜在错误。 + sdkPromise.catch(() => { + /* TTML 优先,忽略 SDK 的错误 */ + }) + } catch (ttmlError: any) { + // --- TTML 失败,回退到 SDK --- + // 检查是否是因为中止操作 + if (!active || (ttmlError && ttmlError.name === 'AbortError')) { + return + } + + // console.log('TTML failed, falling back to SDK:', ttmlError.message); + + try { + // 现在等待已经启动的 SDK 请求 + const sdkLyrics = await sdkPromise + + if (sdkLyrics) { + parsedLyrics = sdkLyrics + } else { + // SDK 也失败了或没有返回歌词 + // console.log('SDK fallback also provided no lyrics.'); + parsedLyrics = [] // 或者保持原样 + } + } catch (sdkError) { + // TTML 和 SDK 都失败了 + // console.error('Both TTML and SDK failed:', { ttmlError, sdkError }); + parsedLyrics = [] // 最终回退 + } } - } else { + } else if (source !== 'local') { // 其他来源:直接统一歌词 API const lyricData = await window.api.music.requestSdk('getLyric', { source, @@ -216,6 +280,13 @@ watch( } parsedLyrics = mergeTranslation(parsedLyrics, lyricData?.tlyric) + } else { + const text = (props.songInfo as any).lrc as string | null + if (text && (/^\[(\d+),\d+\]/.test(text) || /\(\d+,\d+,\d+\)/.test(text))) { + parsedLyrics = text ? (parseYrc(text) as any) : [] + } else { + parsedLyrics = text ? (parseLrc(text) as any) : [] + } } if (!active) return state.lyricLines = parsedLyrics.length > 0 ? parsedLyrics : [] @@ -229,71 +300,6 @@ watch( { immediate: true } ) -// 桌面歌词联动:构建歌词负载、计算当前行并通过 IPC 推送 -const buildLyricPayload = (lines: LyricLine[]) => - (lines || []).map((l) => ({ - content: (l.words || []).map((w) => w.word).join(''), - tran: l.translatedLyric || '' - })) - -const lastLyricIndex = ref(-1) -const computeLyricIndex = (timeMs: number, lines: LyricLine[]) => { - if (!lines || lines.length === 0) return -1 - const t = timeMs - const i = lines.findIndex((l) => t >= l.startTime && t < l.endTime) - if (i !== -1) return i - for (let j = lines.length - 1; j >= 0; j--) { - if (t >= lines[j].startTime) return j - } - return -1 -} - -// 歌词集合变化时,先推一次集合,index 为 -1(由窗口自行处理占位) -watch( - () => state.lyricLines, - (lines) => { - const payload = { index: -1, lyric: buildLyricPayload(lines) } - ;(window as any)?.electron?.ipcRenderer?.send?.('play-lyric-change', payload) - }, - { deep: true, immediate: true } -) - -// 当前时间变化时,计算当前行并推送 -watch( - () => state.currentTime, - (ms) => { - const idx = computeLyricIndex(ms, state.lyricLines) - if (idx !== lastLyricIndex.value) { - lastLyricIndex.value = idx - const payload = { index: idx, lyric: buildLyricPayload(state.lyricLines) } - ;(window as any)?.electron?.ipcRenderer?.send?.('play-lyric-change', payload) - } - } -) - -// 播放状态推送(用于窗口播放/暂停按钮联动) -watch( - () => Audio.value.isPlay, - (playing) => { - ;(window as any)?.electron?.ipcRenderer?.send?.('play-status-change', playing) - }, - { immediate: true } -) - -// 歌曲标题推送 -watch( - () => props.songInfo, - (info) => { - try { - const name = (info as any)?.name || '' - const artist = (info as any)?.singer || '' - const title = [name, artist].filter(Boolean).join(' - ') - if (title) (window as any)?.electron?.ipcRenderer?.send?.('play-song-change', title) - } catch {} - }, - { immediate: true, deep: true } -) - const bgRef = ref(undefined) const lyricPlayerRef = ref(undefined) @@ -489,7 +495,7 @@ const lyricTranslateY = computed(() => {
{ console.error('切换桌面歌词失败:', e) } } - -// 监听来自主进程的锁定状态广播 -window.electron?.ipcRenderer?.on?.('toogleDesktopLyricLock', (_, lock) => { - desktopLyricLocked.value = !!lock -}) -// 监听主进程通知关闭桌面歌词 -window.electron?.ipcRenderer?.on?.('closeDesktopLyric', () => { - desktopLyricOpen.value = false - desktopLyricLocked.value = false -}) - -window.addEventListener('global-music-control', (e: any) => { - const name = e?.detail?.name - console.log(name); - switch (name) { - case 'play': - handlePlay() - break - case 'pause': - handlePause() - break - case 'playPrev': - playPrevious() - break - case 'playNext': - playNext() - break - } -}) - -document.addEventListener('keydown', KeyEvent) -// 处理最小化右键的事件 -const removeMusicCtrlListener = window.api.onMusicCtrl(() => { - togglePlayPause() -}) -let timer: any = null - -function throttle(callback: Function, delay: number) { - if (timer) return - timer = setTimeout(() => { - callback() - timer = null - }, delay) -} - -function KeyEvent(e: KeyboardEvent) { - throttle(() => { - if (e.code == 'Space' && showFullPlay.value) { - e.preventDefault() - togglePlayPause() - } else if (e.code == 'ArrowUp') { - e.preventDefault() - console.log('up') - controlAudio.setVolume(Audio.value.volume + 5) - } else if (e.code == 'ArrowDown') { - e.preventDefault() - console.log('down') - controlAudio.setVolume(Audio.value.volume - 5) - } else if (e.code == 'ArrowLeft' && Audio.value.audio && Audio.value.audio.currentTime >= 0) { - Audio.value.audio.currentTime -= 5 - } else if ( - e.code == 'ArrowRight' && - Audio.value.audio && - Audio.value.audio.currentTime <= Audio.value.audio.duration - ) { - console.log('right') - Audio.value.audio.currentTime += 5 - } - }, 100) -} - // 等待音频准备就绪 const waitForAudioReady = (): Promise => { return new Promise((resolve, reject) => { @@ -222,7 +151,7 @@ let pendingRestorePosition = 0 let pendingRestoreSongId: number | string | null = null // 记录组件被停用前的播放状态 -let wasPlaying = false +// let wasPlaying = false // let playbackPosition = 0 let isFull = false @@ -567,41 +496,33 @@ const playNext = async () => { // 定期保存当前播放位置 let savePositionInterval: number | null = null -let unEnded: () => any = () => {} +const PlayerEvent = (e: any) => { + const name = e?.detail?.name + console.log(name) + switch (name) { + case 'play': + handlePlay() + break + case 'pause': + handlePause() + break + case 'toggle': + togglePlayPause() + break + case 'playPrev': + playPrevious() + break + case 'playNext': + playNext() + break + } +} // 初始化播放器 onMounted(async () => { console.log('加载') // 初始化播放列表事件监听器 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', () => { - window.requestAnimationFrame(() => { - console.log('播放结束') - playNext() - }) - }) - // 检查是否有上次播放的歌曲 // 检查是否有上次播放的歌曲 if (userInfo.value.lastPlaySongId && list.value.length > 0) { @@ -655,21 +576,34 @@ onMounted(async () => { userInfo.value.currentTime = Audio.value.currentTime } }, 1000) // 每1秒保存一次 + + // 监听播放器事件 + + // TODO: 这边监听没有取消 + + // 监听来自主进程的锁定状态广播 + window.electron?.ipcRenderer?.on?.('toogleDesktopLyricLock', (_, lock) => { + desktopLyricLocked.value = !!lock + }) + // 监听主进程通知关闭桌面歌词 + window.electron?.ipcRenderer?.on?.('closeDesktopLyric', () => { + desktopLyricOpen.value = false + desktopLyricLocked.value = false + }) + + window.addEventListener('global-music-control', PlayerEvent) }) // 组件卸载时清理 onUnmounted(() => { destroyPlaylistEventListeners() - document.removeEventListener('keydown', KeyEvent) + // document.removeEventListener('keydown', KeyEvent) + window.removeEventListener('global-music-control', PlayerEvent) + window.electron?.ipcRenderer?.removeAllListeners?.('toogleDesktopLyricLock') + window.electron?.ipcRenderer?.removeAllListeners?.('closeDesktopLyric') if (savePositionInterval !== null) { clearInterval(savePositionInterval) } - if (removeMusicCtrlListener) { - removeMusicCtrlListener() - } - // 清理媒体会话控制器 - mediaSessionController.cleanup() - unEnded() }) // 组件被激活时(从缓存中恢复) @@ -702,15 +636,9 @@ onActivated(async () => { // 组件被停用时(缓存但不销毁) onDeactivated(() => { console.log('PlayMusic组件被停用') - // 保存当前播放状态 - wasPlaying = Audio.value.isPlay - // playbackPosition = Audio.value.currentTime + // 仅记录状态,不主动暂停,避免页面切换导致音乐暂停 + // wasPlaying = Audio.value.isPlay isFull = showFullPlay.value - // 如果正在播放,暂停播放但不改变状态标志 - if (wasPlaying && Audio.value.audio) { - Audio.value.audio.pause() - console.log('暂时暂停播放,状态已保存') - } }) // 监听用户信息变化,更新音量 @@ -878,19 +806,24 @@ const handlePlay = async () => { // 专门的暂停函数 const handlePause = async () => { - if (Audio.value.url && Audio.value.isPlay) { + const a = Audio.value.audio + if (Audio.value.url && a && !a.paused) { const stopResult = stop() if (stopResult && typeof stopResult.then === 'function') { await stopResult } - // 暂停后,同步 SMTC 状态 + mediaSessionController.updatePlaybackState('paused') + } else if (Audio.value.url) { + // 已处于暂停或未知状态,也同步一次 SMTC,确保外部显示一致 mediaSessionController.updatePlaybackState('paused') } } // 播放/暂停切换 const togglePlayPause = async () => { - if (Audio.value.isPlay) { + const a = Audio.value.audio + const isActuallyPlaying = a ? !a.paused : Audio.value.isPlay + if (isActuallyPlaying) { await handlePause() } else { await handlePlay() @@ -1304,38 +1237,37 @@ watch(showFullPlay, (val) => { /* 进度条样式 */ .progress-bar-container { width: 100%; - height: 4px; + --touch-range-height: 20px; + --play-line-height: 4px; + height: calc(var(--touch-range-height) + var(--play-line-height)); // 放大可点击区域,但保持视觉细 position: absolute; - // padding-top: 2px; + top: calc(var(--touch-range-height) / 2 * -1); cursor: pointer; transition: all 0.2s ease-in-out; - &:has(.progress-handle.dragging, *:hover) { - // margin-bottom: 0; - height: 6px; - } - .progress-bar { width: 100%; height: 100%; position: relative; - .progress-background { + // 视觉上的细轨道,垂直居中 + .progress-background, + .progress-filled { position: absolute; - top: 0; left: 0; right: 0; - height: 100%; + height: var(--play-line-height); + top: 50%; + transform: translateY(-50%); + border-radius: 999px; + } + + .progress-background { background: transparent; } .progress-filled { - position: absolute; - top: 0; - left: 0; - height: 100%; background: linear-gradient(to right, v-bind(startmaincolor), v-bind(maincolor) 80%); - border-radius: 999px; } .progress-handle { @@ -1347,7 +1279,6 @@ watch(showFullPlay, (val) => { border-radius: 50%; transform: translate(-50%, -50%); opacity: 0; - // transition: opacity 0.2s ease; &:hover, &:active, @@ -1356,6 +1287,20 @@ watch(showFullPlay, (val) => { } } + // 悬停或拖拽时,轻微加粗提升可见性 + &:hover { + .progress-background, + .progress-filled { + height: 6px; + } + } + &:has(.progress-handle.dragging) { + .progress-background, + .progress-filled { + height: 6px; + } + } + &:hover .progress-handle { opacity: 1; } diff --git a/src/renderer/src/components/layout/HomeLayout.vue b/src/renderer/src/components/layout/HomeLayout.vue index 5a67e58..131baba 100644 --- a/src/renderer/src/components/layout/HomeLayout.vue +++ b/src/renderer/src/components/layout/HomeLayout.vue @@ -33,9 +33,14 @@ const menuList: MenuItem[] = [ icon: 'icon-faxian', path: '/home/find' }, + { + name: '歌单', + icon: 'icon-yanchu', + path: '/home/songlist' + }, { name: '本地', - icon: 'icon-music', + icon: 'icon-shouye', path: '/home/local' }, { diff --git a/src/renderer/src/router/index.ts b/src/renderer/src/router/index.ts index 0bb1822..cdddc0b 100644 --- a/src/renderer/src/router/index.ts +++ b/src/renderer/src/router/index.ts @@ -16,6 +16,11 @@ const routes: RouteRecordRaw[] = [ name: 'find', component: () => import('@renderer/views/music/find.vue') }, + { + path: 'songlist', + name: 'songlist', + component: () => import('@renderer/views/music/songlist.vue') + }, { path: 'local', name: 'local', diff --git a/src/renderer/src/utils/audio/audioManager.ts b/src/renderer/src/utils/audio/audioManager.ts index 0b21946..9792463 100644 --- a/src/renderer/src/utils/audio/audioManager.ts +++ b/src/renderer/src/utils/audio/audioManager.ts @@ -28,8 +28,15 @@ class AudioManager { context = new (window.AudioContext || (window as any).webkitAudioContext)() source = context.createMediaElementSource(audioElement) - // 连接到输出,确保音频能正常播放 - source.connect(context.destination) + // 确保仅通过分流器连接,避免重复直连导致音量叠加 + let splitter = this.splitters.get(audioElement) + if (!splitter) { + splitter = context.createGain() + splitter.gain.value = 1.0 + source.connect(splitter) + splitter.connect(context.destination) + this.splitters.set(audioElement, splitter) + } // 存储引用 this.audioSources.set(audioElement, source) diff --git a/src/renderer/src/utils/audio/globalControls.ts b/src/renderer/src/utils/audio/globalControls.ts new file mode 100644 index 0000000..f4a17b5 --- /dev/null +++ b/src/renderer/src/utils/audio/globalControls.ts @@ -0,0 +1,117 @@ +import { ControlAudioStore } from '@renderer/store/ControlAudio' +import mediaSessionController from '@renderer/utils/audio/useSmtc' + +let installed = false + +function dispatch(name: string, val?: any) { + window.dispatchEvent(new CustomEvent('global-music-control', { detail: { name, val } })) +} + +export function installGlobalMusicControls() { + if (installed) return + installed = true + + const controlAudio = ControlAudioStore() + + // 初始化 SMTC,并在音频元素可用时同步一次播放状态与动作处理器 + const tryInitSmtc = () => { + const el = controlAudio.Audio.audio + if (!el) return + mediaSessionController.init(el, { + play: () => dispatch('play'), + pause: () => dispatch('pause'), + playPrevious: () => dispatch('playPrev'), + playNext: () => dispatch('playNext') + }) + // 初始同步状态 + mediaSessionController.updatePlaybackState(el.paused ? 'paused' : 'playing') + } + // 尝试立即初始化一次 + tryInitSmtc() + // 若 URL 变化或 audio 初始化稍后完成,由组件/Store 负责赋值;这里轮询几次兜底初始化 + let smtcTries = 0 + const smtcTimer = setInterval(() => { + if (smtcTries > 20) { + clearInterval(smtcTimer) + return + } + smtcTries++ + if (controlAudio.Audio.audio) { + tryInitSmtc() + clearInterval(smtcTimer) + } + }, 150) + + let keyThrottle = false + const throttle = (cb: () => void, delay: number) => { + if (keyThrottle) return + keyThrottle = true + setTimeout(() => { + try { + cb() + } finally { + keyThrottle = false + } + }, delay) + } + + const onKeyDown = (e: KeyboardEvent) => { + const target = e.target as HTMLElement | null + const tag = (target?.tagName || '').toLowerCase() + const isEditable = + target?.hasAttribute('contenteditable') || + tag === 'input' || + tag === 'textarea' || + (target as HTMLInputElement)?.type === 'text' || + (target as HTMLInputElement)?.type === 'search' || + (target as HTMLInputElement)?.type === 'password' || + (target as HTMLInputElement)?.type === 'email' || + (target as HTMLInputElement)?.type === 'url' || + (target as HTMLInputElement)?.type === 'number' + + throttle(() => { + if (e.code === 'Space') { + if (isEditable) return + e.preventDefault() + dispatch('toggle') + } else if (e.code === 'ArrowUp') { + e.preventDefault() + controlAudio.setVolume(controlAudio.Audio.volume + 5) + } else if (e.code === 'ArrowDown') { + e.preventDefault() + controlAudio.setVolume(controlAudio.Audio.volume - 5) + } else if ( + e.code === 'ArrowLeft' && + controlAudio.Audio.audio && + controlAudio.Audio.audio.currentTime >= 0 + ) { + controlAudio.Audio.audio.currentTime -= 5 + } else if ( + e.code === 'ArrowRight' && + controlAudio.Audio.audio && + controlAudio.Audio.audio.currentTime <= controlAudio.Audio.audio.duration + ) { + controlAudio.Audio.audio.currentTime += 5 + } + }, 100) + } + + document.addEventListener('keydown', onKeyDown) + + // 监听音频结束事件,根据播放模式播放下一首 + controlAudio.subscribe('ended', () => { + window.requestAnimationFrame(() => { + console.log('播放结束') + dispatch('playNext') + }) + }) + // 托盘或系统快捷键回调(若存在) + try { + const removeMusicCtrlListener = (window as any).api?.onMusicCtrl?.(() => { + dispatch('toggle') + }) + void removeMusicCtrlListener + } catch { + // ignore + } +} diff --git a/src/renderer/src/utils/lyrics/desktopLyricBridge.ts b/src/renderer/src/utils/lyrics/desktopLyricBridge.ts new file mode 100644 index 0000000..36c69e2 --- /dev/null +++ b/src/renderer/src/utils/lyrics/desktopLyricBridge.ts @@ -0,0 +1,229 @@ +import { LocalUserDetailStore } from '@renderer/store/LocalUserDetail' +import { ControlAudioStore } from '@renderer/store/ControlAudio' +import _ from 'lodash' +import { toRaw } from 'vue' +import { + parseYrc, + parseLrc, + parseTTML, + parseQrc +} from '@applemusic-like-lyrics/lyric/pkg/amll_lyric.js' + +interface LyricWord { + word: string +} +interface LyricLine { + startTime: number + endTime: number + words: LyricWord[] + translatedLyric?: string +} + +let installed = false + +function buildLyricPayload(lines: LyricLine[]) { + return (lines || []).map((l) => ({ + content: (l.words || []).map((w) => w.word).join(''), + tran: l.translatedLyric || '' + })) +} + +function computeLyricIndex(timeMs: number, lines: LyricLine[]) { + if (!lines || lines.length === 0) return -1 + const t = timeMs + const i = lines.findIndex((l) => t >= l.startTime && t < l.endTime) + if (i !== -1) return i + for (let j = lines.length - 1; j >= 0; j--) { + if (t >= lines[j].startTime) return j + } + return -1 +} + +function mergeTranslation(base: LyricLine[], tlyric?: string): LyricLine[] { + if (!tlyric || base.length === 0) return base + const translated = parseLrc(tlyric) as LyricLine[] + if (!translated || translated.length === 0) return base + const joinWords = (line: LyricLine) => (line.words || []).map((w) => w.word).join('') + + const translatedSorted = translated.slice().sort((a, b) => a.startTime - b.startTime) + const baseTolerance = 300 + const ratioTolerance = 0.4 + if (base.length > 0) { + const firstBase = base[0] + const firstDuration = Math.max(1, firstBase.endTime - firstBase.startTime) + const firstTol = Math.min(baseTolerance, firstDuration * ratioTolerance) + let anchorIndex: number | null = null + let bestDiff = Number.POSITIVE_INFINITY + for (let i = 0; i < translatedSorted.length; i++) { + const diff = Math.abs(translatedSorted[i].startTime - firstBase.startTime) + if (diff <= firstTol && diff < bestDiff) { + bestDiff = diff + anchorIndex = i + } + } + if (anchorIndex !== null) { + let j = anchorIndex + for (let i = 0; i < base.length && j < translatedSorted.length; i++, j++) { + const bl = base[i] + const tl = translatedSorted[j] + if (!tl?.words?.[0]?.word || tl.words[0].word === '//' || !bl?.words?.[0]?.word) continue + const text = joinWords(tl) + if (text) bl.translatedLyric = text + } + return base + } + } + return base +} + +export function installDesktopLyricBridge() { + if (installed) return + installed = true + + const controlAudio = ControlAudioStore() + const localUser = LocalUserDetailStore() + + let currentLines: LyricLine[] = [] + let lastIndex = -1 + let abortCtrl: AbortController | null = null + + async function fetchLyricsForCurrentSong() { + const songId = localUser.userInfo.lastPlaySongId + if (!songId) { + currentLines = [] + lastIndex = -1 + ;(window as any)?.electron?.ipcRenderer?.send?.('play-lyric-change', { index: -1, lyric: [] }) + return + } + const song = localUser.list.find((s: any) => s.songmid === songId) + if (!song) { + currentLines = [] + lastIndex = -1 + ;(window as any)?.electron?.ipcRenderer?.send?.('play-lyric-change', { index: -1, lyric: [] }) + return + } + + // 取消上一轮请求 + if (abortCtrl) abortCtrl.abort() + abortCtrl = new AbortController() + + const source = (song as any).source || 'kg' + let parsed: LyricLine[] = [] + try { + if (source === 'wy' || source === 'tx') { + try { + const res = await ( + await fetch( + `https://amll-ttml-db.stevexmh.net/${source === 'wy' ? 'ncm' : 'qq'}/${songId}`, + { signal: abortCtrl.signal } + ) + ).text() + if (!res || res.length < 100) throw new Error('no ttml') + parsed = (parseTTML(res) as any).lines as LyricLine[] + } catch { + const lyricData = await (window as any)?.api?.music?.requestSdk?.('getLyric', { + source, + songInfo: _.cloneDeep(toRaw(song)) + }) + if (lyricData?.crlyric) { + parsed = ( + source === 'tx' ? parseQrc(lyricData.crlyric) : parseYrc(lyricData.crlyric) + ) as LyricLine[] + } else if (lyricData?.lyric) { + parsed = parseLrc(lyricData.lyric) as LyricLine[] + } + parsed = mergeTranslation(parsed, lyricData?.tlyric) + } + } else if (source !== 'local') { + const lyricData = await (window as any)?.api?.music?.requestSdk?.('getLyric', { + source, + songInfo: _.cloneDeep(toRaw(song)) + }) + if (lyricData?.crlyric) { + parsed = ( + source === 'tx' ? parseQrc(lyricData.crlyric) : parseYrc(lyricData.crlyric) + ) as LyricLine[] + } else if (lyricData?.lyric) { + parsed = parseLrc(lyricData.lyric) as LyricLine[] + } + parsed = mergeTranslation(parsed, lyricData?.tlyric) + } else { + const text = (song as any).lrc as string | null + if (text && (/^\[(\d+),\d+\]/.test(text) || /\(\d+,\d+,\d+\)/.test(text))) { + parsed = text ? (parseQrc(text) as any) : [] + } else { + parsed = text ? (parseLrc(text) as any) : [] + } + } + } catch (e) { + console.error('获取歌词失败:', e) + parsed = [] + } + + currentLines = parsed || [] + lastIndex = -1 + ;(window as any)?.electron?.ipcRenderer?.send?.('play-lyric-change', { + index: -1, + lyric: buildLyricPayload(currentLines) + }) + // 提示前端进入准备态:先渲染 1、2 句左右铺开 + ;(window as any)?.electron?.ipcRenderer?.send?.('play-lyric-index', -1) + + // 同步歌名 + try { + const name = (song as any)?.name || '' + const artist = (song as any)?.singer || '' + const title = [name, artist].filter(Boolean).join(' - ') + if (title) (window as any)?.electron?.ipcRenderer?.send?.('play-song-change', title) + } catch {} + } + + // 监听歌曲切换 + let lastSongId: any = undefined + setInterval(() => { + if (localUser.userInfo.lastPlaySongId !== lastSongId) { + lastSongId = localUser.userInfo.lastPlaySongId + fetchLyricsForCurrentSong() + } + }, 300) + + // 播放状态推送 + let lastPlayState: any = undefined + setInterval(() => { + if (controlAudio.Audio.isPlay !== lastPlayState) { + lastPlayState = controlAudio.Audio.isPlay + ;(window as any)?.electron?.ipcRenderer?.send?.('play-status-change', lastPlayState) + } + }, 300) + + // 时间推进与当前行/进度推送 + setInterval(() => { + const a = controlAudio.Audio + const ms = Math.round((a?.currentTime || 0) * 1000) + const idx = computeLyricIndex(ms, currentLines) + + // 计算当前行进度(0~1) + let progress = 0 + if (idx >= 0 && currentLines[idx]) { + const line = currentLines[idx] + const dur = Math.max(1, (line.endTime ?? line.startTime + 1) - line.startTime) + progress = Math.min(1, Math.max(0, (ms - line.startTime) / dur)) + } + + // 首先推送进度,便于前端做 30% 判定(避免 setTimeout 带来的抖动) + ;(window as any)?.electron?.ipcRenderer?.send?.('play-lyric-progress', { + index: idx, + progress + }) + + // 当行变化时,推送 index(立即切换高亮),并附带完整歌词集合(仅在变化时下发,减少开销) + if (idx !== lastIndex) { + lastIndex = idx + ;(window as any)?.electron?.ipcRenderer?.send?.('play-lyric-index', idx) + ;(window as any)?.electron?.ipcRenderer?.send?.('play-lyric-change', { + index: idx, + lyric: buildLyricPayload(currentLines) + }) + } + }, 100) +} diff --git a/src/renderer/src/utils/playlist/lx_list_part_list__name_default.json b/src/renderer/src/utils/playlist/lx_list_part_list__name_default.json new file mode 100644 index 0000000..87d3b57 --- /dev/null +++ b/src/renderer/src/utils/playlist/lx_list_part_list__name_default.json @@ -0,0 +1,4202 @@ +{ + "type": "playListPart_v2", + "data": { + "id": "default", + "name": "list__name_default", + "list": [ + { + "id": "tx_004PgwF51nVObL", + "name": "See You Again", + "singer": "Wiz Khalifa、Charlie Puth", + "source": "tx", + "interval": "03:49", + "meta": { + "songId": "004PgwF51nVObL", + "albumName": "Nine Track Mind", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004Ws4fG3pT6eA.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.50 MB" + }, + { + "type": "320k", + "size": "8.76 MB" + }, + { + "type": "flac", + "size": "21.84 MB" + }, + { + "type": "atmos", + "size": "22.51 MB" + }, + { + "type": "atmos_plus", + "size": "58.02 MB" + }, + { + "type": "master", + "size": "137.95 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.50 MB" + }, + "320k": { + "size": "8.76 MB" + }, + "flac": { + "size": "21.84 MB" + }, + "atmos": { + "size": "22.51 MB" + }, + "atmos_plus": { + "size": "58.02 MB" + }, + "master": { + "size": "137.95 MB" + } + }, + "albumId": "004Ws4fG3pT6eA", + "strMediaMid": "002jB3070e7jWc", + "id": 105539549, + "albumMid": "004Ws4fG3pT6eA" + } + }, + { + "id": "tx_003cSLOO35W3yP", + "name": "特别的人", + "singer": "方大同", + "source": "tx", + "interval": "04:19", + "meta": { + "songId": "003cSLOO35W3yP", + "albumName": "危险世界", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000000GvdTR0qcKh8.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.95 MB" + }, + { + "type": "320k", + "size": "9.89 MB" + }, + { + "type": "flac", + "size": "25.84 MB" + }, + { + "type": "atmos", + "size": "26.41 MB" + }, + { + "type": "atmos_plus", + "size": "66.96 MB" + }, + { + "type": "master", + "size": "157.74 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.95 MB" + }, + "320k": { + "size": "9.89 MB" + }, + "flac": { + "size": "25.84 MB" + }, + "atmos": { + "size": "26.41 MB" + }, + "atmos_plus": { + "size": "66.96 MB" + }, + "master": { + "size": "157.74 MB" + } + }, + "albumId": "000GvdTR0qcKh8", + "strMediaMid": "00159wc43VgTEW", + "id": 5293866, + "albumMid": "000GvdTR0qcKh8" + } + }, + { + "id": "tx_001OeHvA0NASwT", + "name": "关键词", + "singer": "林俊杰", + "source": "tx", + "interval": "03:30", + "meta": { + "songId": "001OeHvA0NASwT", + "albumName": "和自己对话 From M.E. To Myself", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004Tvu0F207cps.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.21 MB" + }, + { + "type": "320k", + "size": "8.02 MB" + }, + { + "type": "flac", + "size": "21.93 MB" + }, + { + "type": "atmos", + "size": "21.40 MB" + }, + { + "type": "atmos_plus", + "size": "55.74 MB" + }, + { + "type": "master", + "size": "134.68 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.21 MB" + }, + "320k": { + "size": "8.02 MB" + }, + "flac": { + "size": "21.93 MB" + }, + "atmos": { + "size": "21.40 MB" + }, + "atmos_plus": { + "size": "55.74 MB" + }, + "master": { + "size": "134.68 MB" + } + }, + "albumId": "004Tvu0F207cps", + "strMediaMid": "0020sxUb2vNhoQ", + "id": 105393420, + "albumMid": "004Tvu0F207cps" + } + }, + { + "id": "tx_000HIb481OqGlQ", + "name": "清明上河图", + "singer": "李玉刚", + "source": "tx", + "interval": "04:34", + "meta": { + "songId": "000HIb481OqGlQ", + "albumName": "新贵妃醉酒", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000003WscFz0L8T6F.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.18 MB" + }, + { + "type": "320k", + "size": "10.46 MB" + }, + { + "type": "flac", + "size": "31.33 MB" + }, + { + "type": "atmos", + "size": "30.63 MB" + }, + { + "type": "atmos_plus", + "size": "78.01 MB" + }, + { + "type": "master", + "size": "179.52 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.18 MB" + }, + "320k": { + "size": "10.46 MB" + }, + "flac": { + "size": "31.33 MB" + }, + "atmos": { + "size": "30.63 MB" + }, + "atmos_plus": { + "size": "78.01 MB" + }, + "master": { + "size": "179.52 MB" + } + }, + "albumId": "003WscFz0L8T6F", + "strMediaMid": "001UfK5T1Qofad", + "id": 106696579, + "albumMid": "003WscFz0L8T6F" + } + }, + { + "id": "wy_1330472048", + "name": "超度我", + "singer": "福禄寿FloruitShow", + "source": "wy", + "interval": "03:51", + "meta": { + "songId": 1330472048, + "albumName": "超度我", + "picUrl": "http://p2.music.126.net/6WreqEsnzMTN3ZrEdBIoAw==/109951163700824152.jpg", + "qualitys": [ + { + "type": "128k", + "size": "5.29 MB" + }, + { + "type": "320k", + "size": "8.82 MB" + }, + { + "type": "flac", + "size": "25.59 MB" + }, + { + "type": "hires", + "size": "46.72 MB" + }, + { + "type": "master", + "size": "143.02 MB" + } + ], + "_qualitys": { + "master": { + "size": "143.02 MB" + }, + "hires": { + "size": "46.72 MB" + }, + "flac": { + "size": "25.59 MB" + }, + "320k": { + "size": "8.82 MB" + }, + "128k": { + "size": "5.29 MB" + } + }, + "albumId": 74692140 + } + }, + { + "id": "tx_0003y8uR1ZZwOI", + "name": "曹操", + "singer": "林俊杰", + "source": "tx", + "interval": "04:02", + "meta": { + "songId": "0003y8uR1ZZwOI", + "albumName": "曹操", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000002aaUOS24kcwh.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.69 MB" + }, + { + "type": "320k", + "size": "9.23 MB" + }, + { + "type": "flac", + "size": "27.97 MB" + }, + { + "type": "atmos", + "size": "27.59 MB" + }, + { + "type": "atmos_plus", + "size": "71.23 MB" + }, + { + "type": "master", + "size": "166.53 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.69 MB" + }, + "320k": { + "size": "9.23 MB" + }, + "flac": { + "size": "27.97 MB" + }, + "atmos": { + "size": "27.59 MB" + }, + "atmos_plus": { + "size": "71.23 MB" + }, + "master": { + "size": "166.53 MB" + } + }, + "albumId": "002aaUOS24kcwh", + "strMediaMid": "004PaYp94CiV3Z", + "id": 101014, + "albumMid": "002aaUOS24kcwh" + } + }, + { + "id": "tx_001tQpN629J2TT", + "name": "鸳鸯戏 (哎呦小情郎你莫愁) (哎呦小情郎你莫愁) (0.9x)", + "singer": "略略略", + "source": "tx", + "interval": "02:28", + "meta": { + "songId": "001tQpN629J2TT", + "albumName": "鸳鸯戏 (吉他版)", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004RMSpO2U0RXm.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.26 MB" + }, + { + "type": "320k", + "size": "5.66 MB" + }, + { + "type": "flac", + "size": "18.50 MB" + }, + { + "type": "atmos", + "size": "18.61 MB" + }, + { + "type": "atmos_plus", + "size": "46.27 MB" + }, + { + "type": "master", + "size": "103.51 MB" + } + ], + "_qualitys": { + "128k": { + "size": "2.26 MB" + }, + "320k": { + "size": "5.66 MB" + }, + "flac": { + "size": "18.50 MB" + }, + "atmos": { + "size": "18.61 MB" + }, + "atmos_plus": { + "size": "46.27 MB" + }, + "master": { + "size": "103.51 MB" + } + }, + "albumId": "004RMSpO2U0RXm", + "strMediaMid": "001tQpN629J2TT", + "id": 433054474, + "albumMid": "004RMSpO2U0RXm" + } + }, + { + "id": "tx_003KdkFG3Zuqsy", + "name": "盗墓笔记·十年人间", + "singer": "李常超(Lao乾妈)", + "source": "tx", + "interval": "04:37", + "meta": { + "songId": "003KdkFG3Zuqsy", + "albumName": "盗墓笔记·十年人间", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M0000009NU2K0oNnlt.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.23 MB" + }, + { + "type": "320k", + "size": "10.58 MB" + }, + { + "type": "flac", + "size": "32.80 MB" + }, + { + "type": "atmos", + "size": "31.50 MB" + }, + { + "type": "atmos_plus", + "size": "80.60 MB" + }, + { + "type": "master", + "size": "186.18 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.23 MB" + }, + "320k": { + "size": "10.58 MB" + }, + "flac": { + "size": "32.80 MB" + }, + "atmos": { + "size": "31.50 MB" + }, + "atmos_plus": { + "size": "80.60 MB" + }, + "master": { + "size": "186.18 MB" + } + }, + "albumId": "0009NU2K0oNnlt", + "strMediaMid": "004d6lyY1dfDh1", + "id": 215400896, + "albumMid": "0009NU2K0oNnlt" + } + }, + { + "id": "tx_002Edc2D27z8BE", + "name": "K歌之王 AIR (Day Version)", + "singer": "陈奕迅", + "source": "tx", + "interval": "03:41", + "meta": { + "songId": "002Edc2D27z8BE", + "albumName": "K歌之王 AIR", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004gPi2x3ZDDOF.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.38 MB" + }, + { + "type": "320k", + "size": "8.45 MB" + }, + { + "type": "flac", + "size": "45.84 MB" + }, + { + "type": "atmos", + "size": "23.42 MB" + }, + { + "type": "atmos_plus", + "size": "60.50 MB" + }, + { + "type": "master", + "size": "142.55 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.38 MB" + }, + "320k": { + "size": "8.45 MB" + }, + "flac": { + "size": "45.84 MB" + }, + "atmos": { + "size": "23.42 MB" + }, + "atmos_plus": { + "size": "60.50 MB" + }, + "master": { + "size": "142.55 MB" + } + }, + "albumId": "004gPi2x3ZDDOF", + "strMediaMid": "000B0b8n1ielZB", + "id": 610941787, + "albumMid": "004gPi2x3ZDDOF" + } + }, + { + "id": "tx_003aAPj81VWrbL", + "name": "富士山下", + "singer": "陈奕迅", + "source": "tx", + "interval": "04:19", + "meta": { + "songId": "003aAPj81VWrbL", + "albumName": "What's Going On...?", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004Z85XP1c25b7.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.96 MB" + }, + { + "type": "320k", + "size": "9.89 MB" + }, + { + "type": "flac", + "size": "26.62 MB" + }, + { + "type": "atmos", + "size": "26.42 MB" + }, + { + "type": "atmos_plus", + "size": "68.32 MB" + }, + { + "type": "master", + "size": "160.49 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.96 MB" + }, + "320k": { + "size": "9.89 MB" + }, + "flac": { + "size": "26.62 MB" + }, + "atmos": { + "size": "26.42 MB" + }, + "atmos_plus": { + "size": "68.32 MB" + }, + "master": { + "size": "160.49 MB" + } + }, + "albumId": "004Z85XP1c25b7", + "strMediaMid": "001dXZ352YGvqU", + "id": 260678, + "albumMid": "004Z85XP1c25b7" + } + }, + { + "id": "tx_003uKKf71WF3v1", + "name": "找自己", + "singer": "陶喆", + "source": "tx", + "interval": "05:04", + "meta": { + "songId": "003uKKf71WF3v1", + "albumName": "I'm OK", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000002PjDkE14OUkV.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.65 MB" + }, + { + "type": "320k", + "size": "11.63 MB" + }, + { + "type": "flac", + "size": "34.94 MB" + }, + { + "type": "atmos", + "size": "34.87 MB" + }, + { + "type": "atmos_plus", + "size": "88.65 MB" + }, + { + "type": "master", + "size": "208.73 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.65 MB" + }, + "320k": { + "size": "11.63 MB" + }, + "flac": { + "size": "34.94 MB" + }, + "atmos": { + "size": "34.87 MB" + }, + "atmos_plus": { + "size": "88.65 MB" + }, + "master": { + "size": "208.73 MB" + } + }, + "albumId": "002PjDkE14OUkV", + "strMediaMid": "001iZaB20I2YeA", + "id": 4966543, + "albumMid": "002PjDkE14OUkV" + } + }, + { + "id": "744517_452BAB089722C9401A0BF20A2CDF4DB5", + "name": "雨花石", + "singer": "李玉刚、石头", + "source": "kg", + "interval": "05:32", + "meta": { + "songId": 744517, + "albumName": "雨花石", + "picUrl": "http://imge.kugou.com/stdmusic/480/20160908/20160908181040293309.jpg", + "qualitys": [ + { + "type": "128k", + "size": "5.08 MB", + "hash": "452BAB089722C9401A0BF20A2CDF4DB5" + }, + { + "type": "320k", + "size": "12.70 MB", + "hash": "5CADD1058F35CF0DE1E56D425689725E" + }, + { + "type": "flac", + "size": "39.89 MB", + "hash": "E0D1E7640CA1A3677E9B64C4F1F927E0" + }, + { + "type": "hires", + "size": "41.78 MB", + "hash": "20671456A302739A873FD62EB2824D2F" + }, + { + "type": "atmos", + "size": "78.05 MB", + "hash": "466D2D60120BBB4E92D997BBFBA7F1FF" + }, + { + "type": "master", + "size": "142.04 MB", + "hash": "FAB0116D95A5BA5065B16EFA7FB3B994" + } + ], + "_qualitys": { + "128k": { + "size": "5.08 MB", + "hash": "452BAB089722C9401A0BF20A2CDF4DB5" + }, + "320k": { + "size": "12.70 MB", + "hash": "5CADD1058F35CF0DE1E56D425689725E" + }, + "flac": { + "size": "39.89 MB", + "hash": "E0D1E7640CA1A3677E9B64C4F1F927E0" + }, + "hires": { + "size": "41.78 MB", + "hash": "20671456A302739A873FD62EB2824D2F" + }, + "atmos": { + "size": "78.05 MB", + "hash": "466D2D60120BBB4E92D997BBFBA7F1FF" + }, + "master": { + "size": "142.04 MB", + "hash": "FAB0116D95A5BA5065B16EFA7FB3B994" + } + }, + "albumId": "509071", + "hash": "452BAB089722C9401A0BF20A2CDF4DB5" + } + }, + { + "id": "712487_69F342D52AFB4EA64301A22D119D3AC0", + "name": "新贵妃醉酒", + "singer": "李玉刚", + "source": "kg", + "interval": "04:39", + "meta": { + "songId": 712487, + "albumName": "新贵妃醉酒", + "picUrl": "http://imge.kugou.com/stdmusic/480/20150718/20150718100757235963.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.26 MB", + "hash": "69F342D52AFB4EA64301A22D119D3AC0" + }, + { + "type": "320k", + "size": "10.65 MB", + "hash": "0A7470FF2659238FFE831D7FB01AF168" + }, + { + "type": "flac", + "size": "32.07 MB", + "hash": "1232FF2575E7C13D54A803E2F3CEB9F2" + }, + { + "type": "atmos", + "size": "69.83 MB", + "hash": "B3A65E89042B7E05A48FAB54A4F1CBA2" + }, + { + "type": "master", + "size": "124.58 MB", + "hash": "497C08031E381A164B1CD0F25099F42C" + } + ], + "_qualitys": { + "128k": { + "size": "4.26 MB", + "hash": "69F342D52AFB4EA64301A22D119D3AC0" + }, + "320k": { + "size": "10.65 MB", + "hash": "0A7470FF2659238FFE831D7FB01AF168" + }, + "flac": { + "size": "32.07 MB", + "hash": "1232FF2575E7C13D54A803E2F3CEB9F2" + }, + "atmos": { + "size": "69.83 MB", + "hash": "B3A65E89042B7E05A48FAB54A4F1CBA2" + }, + "master": { + "size": "124.58 MB", + "hash": "497C08031E381A164B1CD0F25099F42C" + } + }, + "albumId": "969290", + "hash": "69F342D52AFB4EA64301A22D119D3AC0" + } + }, + { + "id": "tx_000E8jPw26JFub", + "name": "千千阕歌", + "singer": "陈慧娴", + "source": "tx", + "interval": "05:01", + "meta": { + "songId": "000E8jPw26JFub", + "albumName": "永远是你的朋友", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000003pUBFL10QPM0.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.59 MB" + }, + { + "type": "320k", + "size": "11.48 MB" + }, + { + "type": "flac", + "size": "31.57 MB" + }, + { + "type": "atmos", + "size": "29.93 MB" + }, + { + "type": "atmos_plus", + "size": "77.03 MB" + }, + { + "type": "master", + "size": "187.16 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.59 MB" + }, + "320k": { + "size": "11.48 MB" + }, + "flac": { + "size": "31.57 MB" + }, + "atmos": { + "size": "29.93 MB" + }, + "atmos_plus": { + "size": "77.03 MB" + }, + "master": { + "size": "187.16 MB" + } + }, + "albumId": "003pUBFL10QPM0", + "strMediaMid": "0037TCpa0vc6vu", + "id": 130363, + "albumMid": "003pUBFL10QPM0" + } + }, + { + "id": "tx_001OyHbk2MSIi4", + "name": "十年", + "singer": "陈奕迅", + "source": "tx", + "interval": "03:25", + "meta": { + "songId": "001OyHbk2MSIi4", + "albumName": "黑白灰", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000000GDz8k03UOaI.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.14 MB" + }, + { + "type": "320k", + "size": "7.84 MB" + }, + { + "type": "flac", + "size": "20.72 MB" + }, + { + "type": "atmos", + "size": "19.73 MB" + }, + { + "type": "atmos_plus", + "size": "50.36 MB" + }, + { + "type": "master", + "size": "125.96 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.14 MB" + }, + "320k": { + "size": "7.84 MB" + }, + "flac": { + "size": "20.72 MB" + }, + "atmos": { + "size": "19.73 MB" + }, + "atmos_plus": { + "size": "50.36 MB" + }, + "master": { + "size": "125.96 MB" + } + }, + "albumId": "000GDz8k03UOaI", + "strMediaMid": "002AIxAT3HZwiA", + "id": 4830342, + "albumMid": "000GDz8k03UOaI" + } + }, + { + "id": "tx_000c3ohn2KPrcl", + "name": "朋友", + "singer": "周华健", + "source": "tx", + "interval": "05:12", + "meta": { + "songId": "000c3ohn2KPrcl", + "albumName": "朋友", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000002mpuXH1P7UuJ.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.77 MB" + }, + { + "type": "320k", + "size": "11.92 MB" + }, + { + "type": "flac", + "size": "30.70 MB" + }, + { + "type": "atmos", + "size": "30.42 MB" + }, + { + "type": "atmos_plus", + "size": "75.94 MB" + }, + { + "type": "master", + "size": "183.48 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.77 MB" + }, + "320k": { + "size": "11.92 MB" + }, + "flac": { + "size": "30.70 MB" + }, + "atmos": { + "size": "30.42 MB" + }, + "atmos_plus": { + "size": "75.94 MB" + }, + "master": { + "size": "183.48 MB" + } + }, + "albumId": "002mpuXH1P7UuJ", + "strMediaMid": "001FBOuD3gHNm3", + "id": 4602, + "albumMid": "002mpuXH1P7UuJ" + } + }, + { + "id": "tx_002bK2ZS3S2ySt", + "name": "国家", + "singer": "成龙、刘媛媛", + "source": "tx", + "interval": "04:21", + "meta": { + "songId": "002bK2ZS3S2ySt", + "albumName": "国家", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000002uT6bS2ALHcI.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.99 MB" + }, + { + "type": "320k", + "size": "9.98 MB" + }, + { + "type": "flac", + "size": "27.31 MB" + }, + { + "type": "atmos", + "size": "25.72 MB" + }, + { + "type": "atmos_plus", + "size": "66.58 MB" + }, + { + "type": "master", + "size": "170.15 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.99 MB" + }, + "320k": { + "size": "9.98 MB" + }, + "flac": { + "size": "27.31 MB" + }, + "atmos": { + "size": "25.72 MB" + }, + "atmos_plus": { + "size": "66.58 MB" + }, + "master": { + "size": "170.15 MB" + } + }, + "albumId": "002uT6bS2ALHcI", + "strMediaMid": "002N5yL00spUUs", + "id": 1419127, + "albumMid": "002uT6bS2ALHcI" + } + }, + { + "id": "tx_001en1BP23y8xl", + "name": "好人一生平安", + "singer": "李娜、柏菲音乐", + "source": "tx", + "interval": "04:37", + "meta": { + "songId": "001en1BP23y8xl", + "albumName": "深深思念", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M0000039TnP72TRGfK.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.23 MB" + }, + { + "type": "320k", + "size": "10.57 MB" + }, + { + "type": "flac", + "size": "25.43 MB" + }, + { + "type": "atmos", + "size": "24.38 MB" + }, + { + "type": "atmos_plus", + "size": "63.51 MB" + }, + { + "type": "master", + "size": "159.34 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.23 MB" + }, + "320k": { + "size": "10.57 MB" + }, + "flac": { + "size": "25.43 MB" + }, + "atmos": { + "size": "24.38 MB" + }, + "atmos_plus": { + "size": "63.51 MB" + }, + "master": { + "size": "159.34 MB" + } + }, + "albumId": "0039TnP72TRGfK", + "strMediaMid": "003cmAUp2Dj5qk", + "id": 1208587, + "albumMid": "0039TnP72TRGfK" + } + }, + { + "id": "tx_002qU5aY3Qu24y", + "name": "青花瓷", + "singer": "周杰伦", + "source": "tx", + "interval": "03:59", + "meta": { + "songId": "002qU5aY3Qu24y", + "albumName": "我很忙", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000002eFUFm2XYZ7z.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.65 MB" + }, + { + "type": "320k", + "size": "9.13 MB" + }, + { + "type": "flac", + "size": "24.96 MB" + }, + { + "type": "atmos", + "size": "24.79 MB" + }, + { + "type": "atmos_plus", + "size": "63.29 MB" + }, + { + "type": "master", + "size": "149.21 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.65 MB" + }, + "320k": { + "size": "9.13 MB" + }, + "flac": { + "size": "24.96 MB" + }, + "atmos": { + "size": "24.79 MB" + }, + "atmos_plus": { + "size": "63.29 MB" + }, + "master": { + "size": "149.21 MB" + } + }, + "albumId": "002eFUFm2XYZ7z", + "strMediaMid": "0015zR8B3gjJLl", + "id": 410316, + "albumMid": "002eFUFm2XYZ7z" + } + }, + { + "id": "tx_0044Qzv02BfUt3", + "name": "落了白", + "singer": "蒋雪儿Snow.J", + "source": "tx", + "interval": "03:00", + "meta": { + "songId": "0044Qzv02BfUt3", + "albumName": "落了白", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000001vZDq92ImKof.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.76 MB" + }, + { + "type": "320k", + "size": "6.90 MB" + }, + { + "type": "flac", + "size": "18.79 MB" + }, + { + "type": "atmos", + "size": "17.91 MB" + }, + { + "type": "atmos_plus", + "size": "46.17 MB" + }, + { + "type": "master", + "size": "113.86 MB" + } + ], + "_qualitys": { + "128k": { + "size": "2.76 MB" + }, + "320k": { + "size": "6.90 MB" + }, + "flac": { + "size": "18.79 MB" + }, + "atmos": { + "size": "17.91 MB" + }, + "atmos_plus": { + "size": "46.17 MB" + }, + "master": { + "size": "113.86 MB" + } + }, + "albumId": "001vZDq92ImKof", + "strMediaMid": "0044Qzv02BfUt3", + "id": 285994795, + "albumMid": "001vZDq92ImKof" + } + }, + { + "id": "tx_000480tN0yShmP", + "name": "风夜行", + "singer": "蒋雪儿Snow.J", + "source": "tx", + "interval": "03:21", + "meta": { + "songId": "000480tN0yShmP", + "albumName": "风夜行", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M0000041r9FU2XUfZ2.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.08 MB" + }, + { + "type": "320k", + "size": "7.70 MB" + }, + { + "type": "flac", + "size": "22.74 MB" + }, + { + "type": "atmos", + "size": "22.06 MB" + }, + { + "type": "atmos_plus", + "size": "58.09 MB" + }, + { + "type": "master", + "size": "138.18 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.08 MB" + }, + "320k": { + "size": "7.70 MB" + }, + "flac": { + "size": "22.74 MB" + }, + "atmos": { + "size": "22.06 MB" + }, + "atmos_plus": { + "size": "58.09 MB" + }, + "master": { + "size": "138.18 MB" + } + }, + "albumId": "0041r9FU2XUfZ2", + "strMediaMid": "002MslzY3q4N3G", + "id": 321403451, + "albumMid": "0041r9FU2XUfZ2" + } + }, + { + "id": "tx_004OARai2fyN1X", + "name": "爱河", + "singer": "蒋雪儿Snow.J", + "source": "tx", + "interval": "03:38", + "meta": { + "songId": "004OARai2fyN1X", + "albumName": "和我一起唱", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000002LJ5yN2RV4vh.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.33 MB" + }, + { + "type": "320k", + "size": "8.33 MB" + }, + { + "type": "flac", + "size": "24.00 MB" + }, + { + "type": "atmos", + "size": "22.84 MB" + }, + { + "type": "atmos_plus", + "size": "58.81 MB" + }, + { + "type": "master", + "size": "139.94 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.33 MB" + }, + "320k": { + "size": "8.33 MB" + }, + "flac": { + "size": "24.00 MB" + }, + "atmos": { + "size": "22.84 MB" + }, + "atmos_plus": { + "size": "58.81 MB" + }, + "master": { + "size": "139.94 MB" + } + }, + "albumId": "002LJ5yN2RV4vh", + "strMediaMid": "003eBWwz1Y7erO", + "id": 202180802, + "albumMid": "002LJ5yN2RV4vh" + } + }, + { + "id": "tx_001vWmFz0T8O3F", + "name": "燕无歇", + "singer": "蒋雪儿Snow.J", + "source": "tx", + "interval": "03:21", + "meta": { + "songId": "001vWmFz0T8O3F", + "albumName": "燕无歇", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000000NoqvB0UzQlc.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.07 MB" + }, + { + "type": "320k", + "size": "7.69 MB" + }, + { + "type": "flac", + "size": "23.50 MB" + }, + { + "type": "atmos", + "size": "22.42 MB" + }, + { + "type": "atmos_plus", + "size": "57.89 MB" + }, + { + "type": "master", + "size": "135.52 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.07 MB" + }, + "320k": { + "size": "7.69 MB" + }, + "flac": { + "size": "23.50 MB" + }, + "atmos": { + "size": "22.42 MB" + }, + "atmos_plus": { + "size": "57.89 MB" + }, + "master": { + "size": "135.52 MB" + } + }, + "albumId": "000NoqvB0UzQlc", + "strMediaMid": "003KQTzX1nwNJt", + "id": 274060154, + "albumMid": "000NoqvB0UzQlc" + } + }, + { + "id": "tx_003gUhuW1tYujy", + "name": "莫问归期", + "singer": "蒋雪儿Snow.J", + "source": "tx", + "interval": "03:39", + "meta": { + "songId": "003gUhuW1tYujy", + "albumName": "莫问归期", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M0000043BQGC4Fz1bp.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.35 MB" + }, + { + "type": "320k", + "size": "8.37 MB" + }, + { + "type": "flac", + "size": "21.85 MB" + }, + { + "type": "atmos", + "size": "21.97 MB" + }, + { + "type": "atmos_plus", + "size": "54.70 MB" + }, + { + "type": "master", + "size": "137.50 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.35 MB" + }, + "320k": { + "size": "8.37 MB" + }, + "flac": { + "size": "21.85 MB" + }, + "atmos": { + "size": "21.97 MB" + }, + "atmos_plus": { + "size": "54.70 MB" + }, + "master": { + "size": "137.50 MB" + } + }, + "albumId": "0043BQGC4Fz1bp", + "strMediaMid": "004VdCpc2So89N", + "id": 236416044, + "albumMid": "0043BQGC4Fz1bp" + } + }, + { + "id": "tx_00128N3r2SYKMF", + "name": "兰亭序", + "singer": "周杰伦", + "source": "tx", + "interval": "04:13", + "meta": { + "songId": "00128N3r2SYKMF", + "albumName": "魔杰座", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000002Neh8l0uciQZ.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.88 MB" + }, + { + "type": "320k", + "size": "9.69 MB" + }, + { + "type": "flac", + "size": "27.00 MB" + }, + { + "type": "atmos", + "size": "26.76 MB" + }, + { + "type": "atmos_plus", + "size": "69.72 MB" + }, + { + "type": "master", + "size": "161.72 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.88 MB" + }, + "320k": { + "size": "9.69 MB" + }, + "flac": { + "size": "27.00 MB" + }, + "atmos": { + "size": "26.76 MB" + }, + "atmos_plus": { + "size": "69.72 MB" + }, + "master": { + "size": "161.72 MB" + } + }, + "albumId": "002Neh8l0uciQZ", + "strMediaMid": "003VLsik0ztbIb", + "id": 449201, + "albumMid": "002Neh8l0uciQZ" + } + }, + { + "id": "git_gitcode_z716e3", + "name": "抗战二十年", + "singer": "BEYOND", + "source": "git", + "interval": "05:42", + "meta": { + "songId": "gitcode_z716e3", + "albumName": "Together", + "picUrl": "https://cdn-img.gitcode.com/cc/bd/c646c3b08932d0d8df1abda54c6594063a23d2f29e63f37e7f4bf45545ed36d3.jpg?response-content-type=image/jpeg", + "qualitys": [ + { + "type": "flac", + "size": "92.29MB" + } + ], + "_qualitys": { + "flac": { + "size": "92.29MB" + } + }, + "albumId": "album_j2ra1y" + } + }, + { + "id": "git_gitcode_rdg3h9", + "name": "长城", + "singer": "Beyond", + "source": "git", + "interval": "04:53", + "meta": { + "songId": "gitcode_rdg3h9", + "albumName": "信念", + "picUrl": "https://cdn-img.gitcode.com/bf/ca/b90f256765272c14acbd52e45129e9fd1c54006e4da519036a689e5de75d9338.jpg?response-content-type=image/jpeg", + "qualitys": [ + { + "type": "flac", + "size": "33.24MB" + } + ], + "_qualitys": { + "flac": { + "size": "33.24MB" + } + }, + "albumId": "album_e438" + } + }, + { + "id": "tx_002iDg5414VX1S", + "name": "走进新时代", + "singer": "张也", + "source": "tx", + "interval": "04:07", + "meta": { + "songId": "002iDg5414VX1S", + "albumName": "同唱一首歌 (女生篇)", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M0000035s8lI1SaEr4.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.79 MB" + }, + { + "type": "320k", + "size": "9.46 MB" + }, + { + "type": "flac", + "size": "24.66 MB" + }, + { + "type": "atmos", + "size": "23.17 MB" + }, + { + "type": "atmos_plus", + "size": "60.57 MB" + }, + { + "type": "master", + "size": "149.94 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.79 MB" + }, + "320k": { + "size": "9.46 MB" + }, + "flac": { + "size": "24.66 MB" + }, + "atmos": { + "size": "23.17 MB" + }, + "atmos_plus": { + "size": "60.57 MB" + }, + "master": { + "size": "149.94 MB" + } + }, + "albumId": "0035s8lI1SaEr4", + "strMediaMid": "002iDg5414VX1S", + "id": 362150850, + "albumMid": "0035s8lI1SaEr4" + } + }, + { + "id": "tx_003AepR40yJdm8", + "name": "Flower Dance", + "singer": "DJ OKAWARI", + "source": "tx", + "interval": "04:23", + "meta": { + "songId": "003AepR40yJdm8", + "albumName": "A Cup Of Coffee", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000002jiwiW3WA6te.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.03 MB" + }, + { + "type": "320k", + "size": "10.07 MB" + }, + { + "type": "flac", + "size": "26.78 MB" + }, + { + "type": "atmos", + "size": "26.68 MB" + }, + { + "type": "atmos_plus", + "size": "68.77 MB" + }, + { + "type": "master", + "size": "168.51 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.03 MB" + }, + "320k": { + "size": "10.07 MB" + }, + "flac": { + "size": "26.78 MB" + }, + "atmos": { + "size": "26.68 MB" + }, + "atmos_plus": { + "size": "68.77 MB" + }, + "master": { + "size": "168.51 MB" + } + }, + "albumId": "002jiwiW3WA6te", + "strMediaMid": "001g6BVn0V24Lp", + "id": 739120, + "albumMid": "002jiwiW3WA6te" + } + }, + { + "id": "git_gitcode_ufjzga", + "name": "岁月无声", + "singer": "Beyond", + "source": "git", + "interval": "04:44", + "meta": { + "songId": "gitcode_ufjzga", + "albumName": "真的见证", + "picUrl": "https://cdn-img.gitcode.com/af/ac/af8330a48cc24a11b5f0eb36c03ee8e6d66ea69b86f125573a59647787d5a157.jpg?response-content-type=image/jpeg", + "qualitys": [ + { + "type": "flac", + "size": "32.76MB" + } + ], + "_qualitys": { + "flac": { + "size": "32.76MB" + } + }, + "albumId": "album_fiwxlx" + } + }, + { + "id": "tx_002TLlcH39XZus", + "name": "保卫黄河", + "singer": "中国人民解放军合唱团", + "source": "tx", + "interval": "01:57", + "meta": { + "songId": "002TLlcH39XZus", + "albumName": "纪念中国人民抗日战争暨世界反法西斯战争胜利70周年阅兵式合唱", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000000z4kWN1VVkK5.jpg", + "qualitys": [ + { + "type": "128k", + "size": "1.79 MB" + }, + { + "type": "320k", + "size": "4.47 MB" + }, + { + "type": "atmos", + "size": "10.22 MB" + }, + { + "type": "atmos_plus", + "size": "26.81 MB" + } + ], + "_qualitys": { + "128k": { + "size": "1.79 MB" + }, + "320k": { + "size": "4.47 MB" + }, + "atmos": { + "size": "10.22 MB" + }, + "atmos_plus": { + "size": "26.81 MB" + } + }, + "albumId": "000z4kWN1VVkK5", + "strMediaMid": "002gIHN50ZzXBN", + "id": 201412882, + "albumMid": "000z4kWN1VVkK5" + } + }, + { + "id": "tx_0018Owgb2ESTsl", + "name": "春天的故事", + "singer": "董文华", + "source": "tx", + "interval": "05:20", + "meta": { + "songId": "0018Owgb2ESTsl", + "albumName": "千古情", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000001EvznR1hTh3B.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.89 MB" + }, + { + "type": "320k", + "size": "12.24 MB" + }, + { + "type": "flac", + "size": "35.11 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.89 MB" + }, + "320k": { + "size": "12.24 MB" + }, + "flac": { + "size": "35.11 MB" + } + }, + "albumId": "001EvznR1hTh3B", + "strMediaMid": "001VvGu91xj8o5", + "id": 837018, + "albumMid": "001EvznR1hTh3B" + } + }, + { + "id": "tx_000FJG9f2KILil", + "name": "打靶归来", + "singer": "霍勇", + "source": "tx", + "interval": "02:09", + "meta": { + "songId": "000FJG9f2KILil", + "albumName": "生死不离", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000000Oj4Jx16KpM0.jpg", + "qualitys": [ + { + "type": "128k", + "size": "1.98 MB" + }, + { + "type": "320k", + "size": "4.94 MB" + }, + { + "type": "flac", + "size": "12.26 MB" + }, + { + "type": "atmos", + "size": "12.69 MB" + }, + { + "type": "atmos_plus", + "size": "33.10 MB" + }, + { + "type": "master", + "size": "84.18 MB" + } + ], + "_qualitys": { + "128k": { + "size": "1.98 MB" + }, + "320k": { + "size": "4.94 MB" + }, + "flac": { + "size": "12.26 MB" + }, + "atmos": { + "size": "12.69 MB" + }, + "atmos_plus": { + "size": "33.10 MB" + }, + "master": { + "size": "84.18 MB" + } + }, + "albumId": "000Oj4Jx16KpM0", + "strMediaMid": "002z7VUG1O7JLU", + "id": 6892338, + "albumMid": "000Oj4Jx16KpM0" + } + }, + { + "id": "tx_002sEnjF1oJvOd", + "name": "大刀进行曲", + "singer": "中央乐团", + "source": "tx", + "interval": "00:46", + "meta": { + "songId": "002sEnjF1oJvOd", + "albumName": "革命历史歌曲", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000000tzdwj0yak3O.jpg", + "qualitys": [ + { + "type": "128k", + "size": "733.67 KB" + }, + { + "type": "320k", + "size": "1.79 MB" + }, + { + "type": "atmos", + "size": "4.09 MB" + }, + { + "type": "atmos_plus", + "size": "10.75 MB" + }, + { + "type": "master", + "size": "26.98 MB" + } + ], + "_qualitys": { + "128k": { + "size": "733.67 KB" + }, + "320k": { + "size": "1.79 MB" + }, + "atmos": { + "size": "4.09 MB" + }, + "atmos_plus": { + "size": "10.75 MB" + }, + "master": { + "size": "26.98 MB" + } + }, + "albumId": "000tzdwj0yak3O", + "strMediaMid": "002sEnjF1oJvOd", + "id": 393765020, + "albumMid": "000tzdwj0yak3O" + } + }, + { + "id": "tx_000lhkgl3qGgo6", + "name": "大海航行靠舵手", + "singer": "中央广播文工团", + "source": "tx", + "interval": "01:24", + "meta": { + "songId": "000lhkgl3qGgo6", + "albumName": "伟大的领袖毛泽东", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004QQUya1hjJgh.jpg", + "qualitys": [ + { + "type": "128k", + "size": "1.30 MB" + }, + { + "type": "320k", + "size": "3.24 MB" + }, + { + "type": "atmos", + "size": "5.96 MB" + }, + { + "type": "atmos_plus", + "size": "16.28 MB" + }, + { + "type": "master", + "size": "40.22 MB" + } + ], + "_qualitys": { + "128k": { + "size": "1.30 MB" + }, + "320k": { + "size": "3.24 MB" + }, + "atmos": { + "size": "5.96 MB" + }, + "atmos_plus": { + "size": "16.28 MB" + }, + "master": { + "size": "40.22 MB" + } + }, + "albumId": "004QQUya1hjJgh", + "strMediaMid": "000vbXc71Qb2tD", + "id": 368286753, + "albumMid": "004QQUya1hjJgh" + } + }, + { + "id": "tx_004FJ7QF3vbNhM", + "name": "当那一天来临", + "singer": "华语群星", + "source": "tx", + "interval": "02:03", + "meta": { + "songId": "004FJ7QF3vbNhM", + "albumName": "战斗精神队列歌曲", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M0000031mlBe2zQ5nI.jpg", + "qualitys": [ + { + "type": "128k", + "size": "1.89 MB" + }, + { + "type": "atmos", + "size": "10.72 MB" + }, + { + "type": "atmos_plus", + "size": "30.41 MB" + } + ], + "_qualitys": { + "128k": { + "size": "1.89 MB" + }, + "atmos": { + "size": "10.72 MB" + }, + "atmos_plus": { + "size": "30.41 MB" + } + }, + "albumId": "0031mlBe2zQ5nI", + "strMediaMid": "0019xFsy2oJL9f", + "id": 107785754, + "albumMid": "0031mlBe2zQ5nI" + } + }, + { + "id": "tx_001DJH5c46JGi2", + "name": "东方红", + "singer": "华语群星", + "source": "tx", + "interval": "03:09", + "meta": { + "songId": "001DJH5c46JGi2", + "albumName": "红歌100首 献给党诞辰90周年", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004ZdCyW3XRnSw.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.89 MB" + }, + { + "type": "320k", + "size": "7.22 MB" + }, + { + "type": "atmos", + "size": "19.26 MB" + }, + { + "type": "atmos_plus", + "size": "49.84 MB" + }, + { + "type": "master", + "size": "117.29 MB" + } + ], + "_qualitys": { + "128k": { + "size": "2.89 MB" + }, + "320k": { + "size": "7.22 MB" + }, + "atmos": { + "size": "19.26 MB" + }, + "atmos_plus": { + "size": "49.84 MB" + }, + "master": { + "size": "117.29 MB" + } + }, + "albumId": "004ZdCyW3XRnSw", + "strMediaMid": "001wcq982pHgGS", + "id": 107158608, + "albumMid": "004ZdCyW3XRnSw" + } + }, + { + "id": "tx_001CbPaa0CD8Tu", + "name": "钢铁洪流进行曲", + "singer": "李旭昊", + "source": "tx", + "interval": "03:04", + "meta": { + "songId": "001CbPaa0CD8Tu", + "albumName": "钢铁洪流进行曲", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000001RMLtS23C3cf.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.82 MB" + }, + { + "type": "320k", + "size": "7.05 MB" + }, + { + "type": "flac", + "size": "17.50 MB" + }, + { + "type": "atmos", + "size": "17.09 MB" + }, + { + "type": "atmos_plus", + "size": "43.68 MB" + } + ], + "_qualitys": { + "128k": { + "size": "2.82 MB" + }, + "320k": { + "size": "7.05 MB" + }, + "flac": { + "size": "17.50 MB" + }, + "atmos": { + "size": "17.09 MB" + }, + "atmos_plus": { + "size": "43.68 MB" + } + }, + "albumId": "001RMLtS23C3cf", + "strMediaMid": "000Y6c0q3NifgO", + "id": 600888532, + "albumMid": "001RMLtS23C3cf" + } + }, + { + "id": "tx_000SeIOR3LJwDe", + "name": "歌唱祖国 (合唱)", + "singer": "中国人民解放军军乐团", + "source": "tx", + "interval": "03:07", + "meta": { + "songId": "000SeIOR3LJwDe", + "albumName": "", + "picUrl": "https://y.gtimg.cn/music/photo_new/T001R500x500M000000JhyXl2pIZJe.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.85 MB" + }, + { + "type": "320k", + "size": "7.14 MB" + }, + { + "type": "atmos", + "size": "16.81 MB" + }, + { + "type": "atmos_plus", + "size": "43.31 MB" + }, + { + "type": "master", + "size": "111.54 MB" + } + ], + "_qualitys": { + "128k": { + "size": "2.85 MB" + }, + "320k": { + "size": "7.14 MB" + }, + "atmos": { + "size": "16.81 MB" + }, + "atmos_plus": { + "size": "43.31 MB" + }, + "master": { + "size": "111.54 MB" + } + }, + "albumId": "", + "strMediaMid": "000LnhBX0eZLzF", + "id": 127232884, + "albumMid": "" + } + }, + { + "id": "tx_000X8SPS2CNqAO", + "name": "国际歌 (合唱版)", + "singer": "中国人民解放军军乐团", + "source": "tx", + "interval": "04:37", + "meta": { + "songId": "000X8SPS2CNqAO", + "albumName": "标准礼仪曲集", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000003oi1QZ2pJyaO.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.24 MB" + }, + { + "type": "320k", + "size": "10.60 MB" + }, + { + "type": "atmos", + "size": "22.64 MB" + }, + { + "type": "atmos_plus", + "size": "59.68 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.24 MB" + }, + "320k": { + "size": "10.60 MB" + }, + "atmos": { + "size": "22.64 MB" + }, + "atmos_plus": { + "size": "59.68 MB" + } + }, + "albumId": "003oi1QZ2pJyaO", + "strMediaMid": "002dJn9N3wz9dh", + "id": 1811242, + "albumMid": "003oi1QZ2pJyaO" + } + }, + { + "id": "tx_001aQzJq4GL1kn", + "name": "黄河船夫曲", + "singer": "中央乐团合唱团", + "source": "tx", + "interval": "03:32", + "meta": { + "songId": "001aQzJq4GL1kn", + "albumName": "黄河大合唱", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000000mu8ar0LxKhc.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.24 MB" + }, + { + "type": "320k", + "size": "8.09 MB" + }, + { + "type": "flac", + "size": "15.97 MB" + }, + { + "type": "atmos", + "size": "14.64 MB" + }, + { + "type": "atmos_plus", + "size": "38.40 MB" + }, + { + "type": "master", + "size": "113.83 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.24 MB" + }, + "320k": { + "size": "8.09 MB" + }, + "flac": { + "size": "15.97 MB" + }, + "atmos": { + "size": "14.64 MB" + }, + "atmos_plus": { + "size": "38.40 MB" + }, + "master": { + "size": "113.83 MB" + } + }, + "albumId": "000mu8ar0LxKhc", + "strMediaMid": "001s1NuY4C0u4h", + "id": 209096636, + "albumMid": "000mu8ar0LxKhc" + } + }, + { + "id": "tx_004034BV4UDXJr", + "name": "没有共产党就没有新中国", + "singer": "华语群星", + "source": "tx", + "interval": "01:39", + "meta": { + "songId": "004034BV4UDXJr", + "albumName": "中华人民共和国国歌 珍藏版", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M0000001je4T2drGAG.jpg", + "qualitys": [ + { + "type": "128k", + "size": "1.51 MB" + }, + { + "type": "320k", + "size": "3.78 MB" + }, + { + "type": "atmos", + "size": "9.01 MB" + }, + { + "type": "atmos_plus", + "size": "24.46 MB" + }, + { + "type": "master", + "size": "60.08 MB" + } + ], + "_qualitys": { + "128k": { + "size": "1.51 MB" + }, + "320k": { + "size": "3.78 MB" + }, + "atmos": { + "size": "9.01 MB" + }, + "atmos_plus": { + "size": "24.46 MB" + }, + "master": { + "size": "60.08 MB" + } + }, + "albumId": "0001je4T2drGAG", + "strMediaMid": "001K0ARC13GOwg", + "id": 109494415, + "albumMid": "0001je4T2drGAG" + } + }, + { + "id": "tx_000lpOlz46ayrw", + "name": "旗正飘飘", + "singer": "林译", + "source": "tx", + "interval": "03:21", + "meta": { + "songId": "000lpOlz46ayrw", + "albumName": "我的团长我的团", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M0000023vGBt1SVLcm.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.08 MB" + }, + { + "type": "320k", + "size": "7.70 MB" + }, + { + "type": "flac", + "size": "19.38 MB" + }, + { + "type": "atmos", + "size": "17.84 MB" + }, + { + "type": "atmos_plus", + "size": "46.94 MB" + }, + { + "type": "master", + "size": "117.25 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.08 MB" + }, + "320k": { + "size": "7.70 MB" + }, + "flac": { + "size": "19.38 MB" + }, + "atmos": { + "size": "17.84 MB" + }, + "atmos_plus": { + "size": "46.94 MB" + }, + "master": { + "size": "117.25 MB" + } + }, + "albumId": "0023vGBt1SVLcm", + "strMediaMid": "000HtQeR4AzTdh", + "id": 1490918, + "albumMid": "0023vGBt1SVLcm" + } + }, + { + "id": "tx_000s7N1d16GHui", + "name": "强军战歌", + "singer": "阎维文", + "source": "tx", + "interval": "02:46", + "meta": { + "songId": "000s7N1d16GHui", + "albumName": "歌声流淌四十年", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000002SBEoL0jwwhX.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.55 MB" + }, + { + "type": "320k", + "size": "6.37 MB" + }, + { + "type": "atmos", + "size": "17.00 MB" + }, + { + "type": "atmos_plus", + "size": "44.32 MB" + }, + { + "type": "master", + "size": "105.89 MB" + } + ], + "_qualitys": { + "128k": { + "size": "2.55 MB" + }, + "320k": { + "size": "6.37 MB" + }, + "atmos": { + "size": "17.00 MB" + }, + "atmos_plus": { + "size": "44.32 MB" + }, + "master": { + "size": "105.89 MB" + } + }, + "albumId": "002SBEoL0jwwhX", + "strMediaMid": "000s7N1d16GHui", + "id": 259428926, + "albumMid": "002SBEoL0jwwhX" + } + }, + { + "id": "tx_0010p2ec0EPa1L", + "name": "三大纪律八项注意", + "singer": "中国武警男声合唱团、柏菲音乐", + "source": "tx", + "interval": "02:58", + "meta": { + "songId": "0010p2ec0EPa1L", + "albumName": "辉煌之声 天路", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004Nk3oL2S3dOo.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.73 MB" + }, + { + "type": "320k", + "size": "6.83 MB" + }, + { + "type": "flac", + "size": "18.46 MB" + }, + { + "type": "atmos", + "size": "17.15 MB" + }, + { + "type": "atmos_plus", + "size": "43.85 MB" + }, + { + "type": "master", + "size": "109.69 MB" + } + ], + "_qualitys": { + "128k": { + "size": "2.73 MB" + }, + "320k": { + "size": "6.83 MB" + }, + "flac": { + "size": "18.46 MB" + }, + "atmos": { + "size": "17.15 MB" + }, + "atmos_plus": { + "size": "43.85 MB" + }, + "master": { + "size": "109.69 MB" + } + }, + "albumId": "004Nk3oL2S3dOo", + "strMediaMid": "001hipva2uPng0", + "id": 347443030, + "albumMid": "004Nk3oL2S3dOo" + } + }, + { + "id": "wy_5281548", + "name": "社会主义好", + "singer": "中央乐团合唱队、中国人民解放军总政文工团合唱队", + "source": "wy", + "interval": "01:19", + "meta": { + "songId": 5281548, + "albumName": "中华人民共和国国歌 珍藏版", + "picUrl": "http://p2.music.126.net/_lAvi41IfmPhw73Mxq2b8g==/19061133579368888.jpg", + "qualitys": [ + { + "type": "128k", + "size": "1.83 MB" + }, + { + "type": "320k", + "size": "3.05 MB" + }, + { + "type": "flac", + "size": "7.48 MB" + }, + { + "type": "master", + "size": "46.25 MB" + } + ], + "_qualitys": { + "master": { + "size": "46.25 MB" + }, + "flac": { + "size": "7.48 MB" + }, + "320k": { + "size": "3.05 MB" + }, + "128k": { + "size": "1.83 MB" + } + }, + "albumId": 513693 + } + }, + { + "id": "tx_004X2hZk3i8IgH", + "name": "十送红军", + "singer": "中央乐团合唱团", + "source": "tx", + "interval": "05:10", + "meta": { + "songId": "004X2hZk3i8IgH", + "albumName": "辉煌中央乐团革命红色经典(军民鱼水情)", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000002xfFqp1MtWZq.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.74 MB" + }, + { + "type": "320k", + "size": "11.86 MB" + }, + { + "type": "flac", + "size": "25.92 MB" + }, + { + "type": "atmos", + "size": "24.67 MB" + }, + { + "type": "atmos_plus", + "size": "62.36 MB" + }, + { + "type": "master", + "size": "168.25 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.74 MB" + }, + "320k": { + "size": "11.86 MB" + }, + "flac": { + "size": "25.92 MB" + }, + "atmos": { + "size": "24.67 MB" + }, + "atmos_plus": { + "size": "62.36 MB" + }, + "master": { + "size": "168.25 MB" + } + }, + "albumId": "002xfFqp1MtWZq", + "strMediaMid": "00386mQi2m28kT", + "id": 213373208, + "albumMid": "002xfFqp1MtWZq" + } + }, + { + "id": "wy_1298442499", + "name": "松花江上", + "singer": "程志、殷秀梅", + "source": "wy", + "interval": "03:59", + "meta": { + "songId": 1298442499, + "albumName": "永远的记忆 纪念抗日战争胜利65周年(双碟装)", + "picUrl": "http://p1.music.126.net/hTZIoejL5JAhZrvqdWgL0w==/109951168120398944.jpg", + "qualitys": [ + { + "type": "128k", + "size": "5.49 MB" + }, + { + "type": "320k", + "size": "9.15 MB" + }, + { + "type": "flac", + "size": "19.27 MB" + }, + { + "type": "master", + "size": "111.97 MB" + } + ], + "_qualitys": { + "master": { + "size": "111.97 MB" + }, + "flac": { + "size": "19.27 MB" + }, + "320k": { + "size": "9.15 MB" + }, + "128k": { + "size": "5.49 MB" + } + }, + "albumId": 72109762 + } + }, + { + "id": "tx_001sZefK32TirC", + "name": "送你一枚小弹壳", + "singer": "华语群星", + "source": "tx", + "interval": "02:37", + "meta": { + "songId": "001sZefK32TirC", + "albumName": "战斗精神队列歌曲", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M0000031mlBe2zQ5nI.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.41 MB" + }, + { + "type": "320k", + "size": "6.02 MB" + }, + { + "type": "atmos", + "size": "14.79 MB" + }, + { + "type": "atmos_plus", + "size": "39.05 MB" + } + ], + "_qualitys": { + "128k": { + "size": "2.41 MB" + }, + "320k": { + "size": "6.02 MB" + }, + "atmos": { + "size": "14.79 MB" + }, + "atmos_plus": { + "size": "39.05 MB" + } + }, + "albumId": "0031mlBe2zQ5nI", + "strMediaMid": "0037tl5X3SGHZK", + "id": 107785759, + "albumMid": "0031mlBe2zQ5nI" + } + }, + { + "id": "tx_003JL9I02Ro9jX", + "name": "团结就是力量", + "singer": "中国人民解放军军乐团", + "source": "tx", + "interval": "01:14", + "meta": { + "songId": "003JL9I02Ro9jX", + "albumName": "《中华人民共和国国歌》《国际歌》", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000001sbZPi4eka82.jpg", + "qualitys": [ + { + "type": "128k", + "size": "1.13 MB" + }, + { + "type": "320k", + "size": "2.83 MB" + }, + { + "type": "flac", + "size": "6.91 MB" + }, + { + "type": "atmos", + "size": "5.93 MB" + }, + { + "type": "atmos_plus", + "size": "15.05 MB" + }, + { + "type": "master", + "size": "41.67 MB" + } + ], + "_qualitys": { + "128k": { + "size": "1.13 MB" + }, + "320k": { + "size": "2.83 MB" + }, + "flac": { + "size": "6.91 MB" + }, + "atmos": { + "size": "5.93 MB" + }, + "atmos_plus": { + "size": "15.05 MB" + }, + "master": { + "size": "41.67 MB" + } + }, + "albumId": "001sbZPi4eka82", + "strMediaMid": "0031lT2W2WP2tW", + "id": 404873325, + "albumMid": "001sbZPi4eka82" + } + }, + { + "id": "tx_004djojf17nXqH", + "name": "我们走在大路上", + "singer": "中央乐团合唱团", + "source": "tx", + "interval": "02:32", + "meta": { + "songId": "004djojf17nXqH", + "albumName": "辉煌中央乐团革命红色经典(胸怀祖国篇)", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M0000007zPol4VF9PP.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.33 MB" + }, + { + "type": "320k", + "size": "5.82 MB" + }, + { + "type": "flac", + "size": "14.01 MB" + }, + { + "type": "atmos", + "size": "13.45 MB" + }, + { + "type": "atmos_plus", + "size": "34.54 MB" + }, + { + "type": "master", + "size": "88.35 MB" + } + ], + "_qualitys": { + "128k": { + "size": "2.33 MB" + }, + "320k": { + "size": "5.82 MB" + }, + "flac": { + "size": "14.01 MB" + }, + "atmos": { + "size": "13.45 MB" + }, + "atmos_plus": { + "size": "34.54 MB" + }, + "master": { + "size": "88.35 MB" + } + }, + "albumId": "0007zPol4VF9PP", + "strMediaMid": "0000zQJG3vRc8A", + "id": 213373275, + "albumMid": "0007zPol4VF9PP" + } + }, + { + "id": "tx_001zzfrY40iNkd", + "name": "我是一个兵", + "singer": "霍勇", + "source": "tx", + "interval": "01:52", + "meta": { + "songId": "001zzfrY40iNkd", + "albumName": "生死不离", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000000Oj4Jx16KpM0.jpg", + "qualitys": [ + { + "type": "128k", + "size": "1.71 MB" + }, + { + "type": "320k", + "size": "4.28 MB" + }, + { + "type": "atmos", + "size": "10.39 MB" + }, + { + "type": "atmos_plus", + "size": "27.77 MB" + }, + { + "type": "master", + "size": "73.71 MB" + } + ], + "_qualitys": { + "128k": { + "size": "1.71 MB" + }, + "320k": { + "size": "4.28 MB" + }, + "atmos": { + "size": "10.39 MB" + }, + "atmos_plus": { + "size": "27.77 MB" + }, + "master": { + "size": "73.71 MB" + } + }, + "albumId": "000Oj4Jx16KpM0", + "strMediaMid": "002HPFwS3Pe6kO", + "id": 6892395, + "albumMid": "000Oj4Jx16KpM0" + } + }, + { + "id": "tx_00038j1M20dkrN", + "name": "无人智胜进行曲", + "singer": "李旭昊", + "source": "tx", + "interval": "02:14", + "meta": { + "songId": "00038j1M20dkrN", + "albumName": "无人智胜进行曲", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004X5yCc20JT3E.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.05 MB" + }, + { + "type": "320k", + "size": "5.12 MB" + }, + { + "type": "flac", + "size": "28.16 MB" + } + ], + "_qualitys": { + "128k": { + "size": "2.05 MB" + }, + "320k": { + "size": "5.12 MB" + }, + "flac": { + "size": "28.16 MB" + } + }, + "albumId": "004X5yCc20JT3E", + "strMediaMid": "002iH3Dh21u1za", + "id": 600804002, + "albumMid": "004X5yCc20JT3E" + } + }, + { + "id": "tx_001hnyA40rulUO", + "name": "小白杨", + "singer": "阎维文", + "source": "tx", + "interval": "02:38", + "meta": { + "songId": "001hnyA40rulUO", + "albumName": "军歌名曲 战士心声2 咱当兵的人", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000000WTX3s4WL8ND.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.42 MB" + }, + { + "type": "320k", + "size": "6.06 MB" + }, + { + "type": "atmos", + "size": "15.61 MB" + }, + { + "type": "atmos_plus", + "size": "40.00 MB" + }, + { + "type": "master", + "size": "97.67 MB" + } + ], + "_qualitys": { + "128k": { + "size": "2.42 MB" + }, + "320k": { + "size": "6.06 MB" + }, + "atmos": { + "size": "15.61 MB" + }, + "atmos_plus": { + "size": "40.00 MB" + }, + "master": { + "size": "97.67 MB" + } + }, + "albumId": "000WTX3s4WL8ND", + "strMediaMid": "001cnDiX0Rg45i", + "id": 212041355, + "albumMid": "000WTX3s4WL8ND" + } + }, + { + "id": "wy_29023821", + "name": "一二三四歌", + "singer": "阎维文", + "source": "wy", + "interval": "02:43", + "meta": { + "songId": 29023821, + "albumName": "战士情歌", + "picUrl": "http://p1.music.126.net/iik6L0t1IFUXdsVHtVVSOQ==/109951169792927671.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.74 MB" + }, + { + "type": "320k", + "size": "6.24 MB" + }, + { + "type": "flac", + "size": "17.54 MB" + }, + { + "type": "master", + "size": "98.81 MB" + } + ], + "_qualitys": { + "master": { + "size": "98.81 MB" + }, + "flac": { + "size": "17.54 MB" + }, + "320k": { + "size": "6.24 MB" + }, + "128k": { + "size": "3.74 MB" + } + }, + "albumId": 2982092 + } + }, + { + "id": "tx_000pFOVT1k1XeC", + "name": "在太行山上", + "singer": "华语群星", + "source": "tx", + "interval": "04:30", + "meta": { + "songId": "000pFOVT1k1XeC", + "albumName": "红歌100首 献给党诞辰90周年", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004ZdCyW3XRnSw.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.14 MB" + }, + { + "type": "320k", + "size": "10.34 MB" + }, + { + "type": "atmos", + "size": "25.11 MB" + }, + { + "type": "atmos_plus", + "size": "64.20 MB" + }, + { + "type": "master", + "size": "159.99 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.14 MB" + }, + "320k": { + "size": "10.34 MB" + }, + "atmos": { + "size": "25.11 MB" + }, + "atmos_plus": { + "size": "64.20 MB" + }, + "master": { + "size": "159.99 MB" + } + }, + "albumId": "004ZdCyW3XRnSw", + "strMediaMid": "002w2PMX3snQdA", + "id": 107158703, + "albumMid": "004ZdCyW3XRnSw" + } + }, + { + "id": "tx_001XFkWq1RBeNe", + "name": "咱当兵的人", + "singer": "阎维文", + "source": "tx", + "interval": "03:54", + "meta": { + "songId": "001XFkWq1RBeNe", + "albumName": "", + "picUrl": "https://y.gtimg.cn/music/photo_new/T001R500x500M0000040znUO08D9fS.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.57 MB" + }, + { + "type": "atmos", + "size": "22.29 MB" + }, + { + "type": "atmos_plus", + "size": "58.57 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.57 MB" + }, + "atmos": { + "size": "22.29 MB" + }, + "atmos_plus": { + "size": "58.57 MB" + } + }, + "albumId": "", + "strMediaMid": "001XFkWq1RBeNe", + "id": 229482579, + "albumMid": "" + } + }, + { + "id": "wy_5266866", + "name": "咱们工人有力量", + "singer": "中国广播艺术团合唱团", + "source": "wy", + "interval": "01:51", + "meta": { + "songId": 5266866, + "albumName": "百年经典2:长城谣", + "picUrl": "http://p2.music.126.net/_f-jltTuEQxr07Ug99rFyA==/109951163380247212.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.55 MB" + }, + { + "type": "320k", + "size": "4.25 MB" + }, + { + "type": "flac", + "size": "9.06 MB" + }, + { + "type": "master", + "size": "58.30 MB" + } + ], + "_qualitys": { + "master": { + "size": "58.30 MB" + }, + "flac": { + "size": "9.06 MB" + }, + "320k": { + "size": "4.25 MB" + }, + "128k": { + "size": "2.55 MB" + } + }, + "albumId": 512757 + } + }, + { + "id": "2751490_DF4ACF49C088642DE608EE2906255E85", + "name": "中国军魂", + "singer": "北京室内男声合唱团", + "source": "kg", + "interval": "03:07", + "meta": { + "songId": 2751490, + "albumName": "一听天下 中国电视剧歌曲集", + "picUrl": "http://imge.kugou.com/stdmusic/480/20200620/20200620073043464672.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.86 MB", + "hash": "DF4ACF49C088642DE608EE2906255E85" + }, + { + "type": "320k", + "size": "7.15 MB", + "hash": "DD6918D728444813F0C5CA88C555F1C2" + }, + { + "type": "flac", + "size": "20.88 MB", + "hash": "69F8C924F2E13162F6BF83363E86DF4B" + }, + { + "type": "hires", + "size": "20.90 MB", + "hash": "E19BD7158A59CBB639C9F2EB97CDBFEA" + }, + { + "type": "atmos", + "size": "41.72 MB", + "hash": "FAC6CC11F30A50D61D109D6D2DC8CE91" + }, + { + "type": "master", + "size": "77.93 MB", + "hash": "0A061A6F3B7164C32EBC0F2BAC132DA7" + } + ], + "_qualitys": { + "128k": { + "size": "2.86 MB", + "hash": "DF4ACF49C088642DE608EE2906255E85" + }, + "320k": { + "size": "7.15 MB", + "hash": "DD6918D728444813F0C5CA88C555F1C2" + }, + "flac": { + "size": "20.88 MB", + "hash": "69F8C924F2E13162F6BF83363E86DF4B" + }, + "hires": { + "size": "20.90 MB", + "hash": "E19BD7158A59CBB639C9F2EB97CDBFEA" + }, + "atmos": { + "size": "41.72 MB", + "hash": "FAC6CC11F30A50D61D109D6D2DC8CE91" + }, + "master": { + "size": "77.93 MB", + "hash": "0A061A6F3B7164C32EBC0F2BAC132DA7" + } + }, + "albumId": "938582", + "hash": "DF4ACF49C088642DE608EE2906255E85" + } + }, + { + "id": "tx_001cFUdy0chRcO", + "name": "中国人民解放军军歌", + "singer": "霍勇", + "source": "tx", + "interval": "01:31", + "meta": { + "songId": "001cFUdy0chRcO", + "albumName": "我是一个兵", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004fHuBT1RNAXS.jpg", + "qualitys": [ + { + "type": "128k", + "size": "1.39 MB" + }, + { + "type": "320k", + "size": "3.48 MB" + }, + { + "type": "atmos", + "size": "9.37 MB" + }, + { + "type": "atmos_plus", + "size": "24.51 MB" + } + ], + "_qualitys": { + "128k": { + "size": "1.39 MB" + }, + "320k": { + "size": "3.48 MB" + }, + "atmos": { + "size": "9.37 MB" + }, + "atmos_plus": { + "size": "24.51 MB" + } + }, + "albumId": "004fHuBT1RNAXS", + "strMediaMid": "0021Fo753XZURU", + "id": 102944719, + "albumMid": "004fHuBT1RNAXS" + } + }, + { + "id": "tx_000JIBOV04FKOL", + "name": "中国人民志愿军战歌", + "singer": "霍勇", + "source": "tx", + "interval": "01:52", + "meta": { + "songId": "000JIBOV04FKOL", + "albumName": "生死不离", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000000Oj4Jx16KpM0.jpg", + "qualitys": [ + { + "type": "128k", + "size": "1.72 MB" + }, + { + "type": "320k", + "size": "4.30 MB" + }, + { + "type": "flac", + "size": "11.15 MB" + }, + { + "type": "master", + "size": "74.73 MB" + } + ], + "_qualitys": { + "128k": { + "size": "1.72 MB" + }, + "320k": { + "size": "4.30 MB" + }, + "flac": { + "size": "11.15 MB" + }, + "master": { + "size": "74.73 MB" + } + }, + "albumId": "000Oj4Jx16KpM0", + "strMediaMid": "001q44Sc41MDNG", + "id": 6892403, + "albumMid": "000Oj4Jx16KpM0" + } + }, + { + "id": "tx_002sjwXN47AMwz", + "name": "走向复兴", + "singer": "殷秀梅、戴玉强", + "source": "tx", + "interval": "02:58", + "meta": { + "songId": "002sjwXN47AMwz", + "albumName": "美丽中国梦", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000001o2zmJ3gjAwe.jpg", + "qualitys": [ + { + "type": "128k", + "size": "2.73 MB" + }, + { + "type": "320k", + "size": "6.82 MB" + }, + { + "type": "atmos", + "size": "17.37 MB" + }, + { + "type": "atmos_plus", + "size": "45.84 MB" + } + ], + "_qualitys": { + "128k": { + "size": "2.73 MB" + }, + "320k": { + "size": "6.82 MB" + }, + "atmos": { + "size": "17.37 MB" + }, + "atmos_plus": { + "size": "45.84 MB" + } + }, + "albumId": "001o2zmJ3gjAwe", + "strMediaMid": "000U3LL00cVtMO", + "id": 213086938, + "albumMid": "001o2zmJ3gjAwe" + } + }, + { + "id": "wy_1298446814", + "name": "祖国不会忘记", + "singer": "殷秀梅", + "source": "wy", + "interval": "02:36", + "meta": { + "songId": 1298446814, + "albumName": "祖国不会忘记", + "picUrl": "http://p1.music.126.net/a19I_HKI_bdbJ9k0g0CfLQ==/109951168160174069.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.59 MB" + }, + { + "type": "320k", + "size": "5.98 MB" + }, + { + "type": "flac", + "size": "14.38 MB" + }, + { + "type": "master", + "size": "86.38 MB" + } + ], + "_qualitys": { + "master": { + "size": "86.38 MB" + }, + "flac": { + "size": "14.38 MB" + }, + "320k": { + "size": "5.98 MB" + }, + "128k": { + "size": "3.59 MB" + } + }, + "albumId": 72109878 + } + }, + { + "id": "tx_004BJRhU4g74fg", + "name": "明日坐标", + "singer": "林俊杰、王者荣耀", + "source": "tx", + "interval": "04:44", + "meta": { + "songId": "004BJRhU4g74fg", + "albumName": "明日坐标", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000000AImqc0jgZOu.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.34 MB" + }, + { + "type": "320k", + "size": "10.86 MB" + }, + { + "type": "flac", + "size": "57.32 MB" + }, + { + "type": "master", + "size": "183.45 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.34 MB" + }, + "320k": { + "size": "10.86 MB" + }, + "flac": { + "size": "57.32 MB" + }, + "master": { + "size": "183.45 MB" + } + }, + "albumId": "000AImqc0jgZOu", + "strMediaMid": "004BJRhU4g74fg", + "albumMid": "000AImqc0jgZOu", + "id": 608839549 + } + }, + { + "id": "tx_0017K7gL4WYnw2", + "name": "反方向的钟", + "singer": "周杰伦", + "source": "tx", + "interval": "04:18", + "meta": { + "songId": "0017K7gL4WYnw2", + "albumName": "Jay", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000000f01724fd7TH.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.94 MB" + }, + { + "type": "320k", + "size": "9.85 MB" + }, + { + "type": "flac", + "size": "27.04 MB" + }, + { + "type": "atmos", + "size": "26.67 MB" + }, + { + "type": "atmos_plus", + "size": "69.54 MB" + }, + { + "type": "master", + "size": "160.69 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.94 MB" + }, + "320k": { + "size": "9.85 MB" + }, + "flac": { + "size": "27.04 MB" + }, + "atmos": { + "size": "26.67 MB" + }, + "atmos_plus": { + "size": "69.54 MB" + }, + "master": { + "size": "160.69 MB" + } + }, + "albumId": "000f01724fd7TH", + "strMediaMid": "00400jk23JDWwJ", + "id": 97759, + "albumMid": "000f01724fd7TH" + } + }, + { + "id": "tx_002eaFHp1H0ApQ", + "name": "农民", + "singer": "BEYOND", + "source": "tx", + "interval": "05:15", + "meta": { + "songId": "002eaFHp1H0ApQ", + "albumName": "继续革命", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004ex7iR3S9VcR.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.82 MB" + }, + { + "type": "320k", + "size": "12.05 MB" + }, + { + "type": "flac", + "size": "32.91 MB" + }, + { + "type": "atmos", + "size": "31.88 MB" + }, + { + "type": "atmos_plus", + "size": "80.27 MB" + }, + { + "type": "master", + "size": "196.11 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.82 MB" + }, + "320k": { + "size": "12.05 MB" + }, + "flac": { + "size": "32.91 MB" + }, + "atmos": { + "size": "31.88 MB" + }, + "atmos_plus": { + "size": "80.27 MB" + }, + "master": { + "size": "196.11 MB" + } + }, + "albumId": "004ex7iR3S9VcR", + "strMediaMid": "001YwK6a46ir8D", + "id": 102421559, + "albumMid": "004ex7iR3S9VcR" + } + }, + { + "id": "tx_001yS0N33yPm1B", + "name": "海阔天空", + "singer": "BEYOND", + "source": "tx", + "interval": "05:24", + "meta": { + "songId": "001yS0N33yPm1B", + "albumName": "乐与怒", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000002qcJuX3lO3EZ.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.96 MB" + }, + { + "type": "320k", + "size": "12.39 MB" + }, + { + "type": "flac", + "size": "31.36 MB" + }, + { + "type": "atmos", + "size": "30.52 MB" + }, + { + "type": "atmos_plus", + "size": "78.59 MB" + }, + { + "type": "master", + "size": "189.31 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.96 MB" + }, + "320k": { + "size": "12.39 MB" + }, + "flac": { + "size": "31.36 MB" + }, + "atmos": { + "size": "30.52 MB" + }, + "atmos_plus": { + "size": "78.59 MB" + }, + "master": { + "size": "189.31 MB" + } + }, + "albumId": "002qcJuX3lO3EZ", + "strMediaMid": "002MX8Ea4e5RDS", + "id": 4835784, + "albumMid": "002qcJuX3lO3EZ" + } + }, + { + "id": "tx_0019uVBT0LJCDw", + "name": "Amani", + "singer": "BEYOND", + "source": "tx", + "interval": "04:50", + "meta": { + "songId": "0019uVBT0LJCDw", + "albumName": "Deliberate 犹豫 (Remastered 2020)", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000001Gikfw1MiLRm.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.43 MB" + }, + { + "type": "320k", + "size": "11.08 MB" + }, + { + "type": "flac", + "size": "30.85 MB" + }, + { + "type": "atmos", + "size": "29.34 MB" + }, + { + "type": "atmos_plus", + "size": "75.26 MB" + }, + { + "type": "master", + "size": "181.87 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.43 MB" + }, + "320k": { + "size": "11.08 MB" + }, + "flac": { + "size": "30.85 MB" + }, + "atmos": { + "size": "29.34 MB" + }, + "atmos_plus": { + "size": "75.26 MB" + }, + "master": { + "size": "181.87 MB" + } + }, + "albumId": "001Gikfw1MiLRm", + "strMediaMid": "001Daffc0GU59P", + "id": 481084, + "albumMid": "001Gikfw1MiLRm" + } + }, + { + "id": "tx_002T8B4P4EUHO3", + "name": "弱水三千 (0.8x|DJ苏熠鸣版)", + "singer": "苏熠鸣", + "source": "tx", + "interval": "03:31", + "meta": { + "songId": "002T8B4P4EUHO3", + "albumName": "弱水三千 (DJ苏熠鸣版)", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000001cLhCu4IOoWc.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.23 MB" + }, + { + "type": "320k", + "size": "8.08 MB" + }, + { + "type": "flac", + "size": "23.50 MB" + }, + { + "type": "atmos", + "size": "23.01 MB" + }, + { + "type": "atmos_plus", + "size": "59.07 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.23 MB" + }, + "320k": { + "size": "8.08 MB" + }, + "flac": { + "size": "23.50 MB" + }, + "atmos": { + "size": "23.01 MB" + }, + "atmos_plus": { + "size": "59.07 MB" + } + }, + "albumId": "001cLhCu4IOoWc", + "strMediaMid": "002T8B4P4EUHO3", + "id": 506553222, + "albumMid": "001cLhCu4IOoWc" + } + }, + { + "id": "tx_0041w8IG2fENEX", + "name": "中国人", + "singer": "刘德华", + "source": "tx", + "interval": "03:52", + "meta": { + "songId": "0041w8IG2fENEX", + "albumName": "爱如此神奇", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000002poFpm3AdEuF.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.55 MB" + }, + { + "type": "320k", + "size": "8.88 MB" + }, + { + "type": "flac", + "size": "24.49 MB" + }, + { + "type": "atmos", + "size": "23.67 MB" + }, + { + "type": "atmos_plus", + "size": "61.30 MB" + }, + { + "type": "master", + "size": "145.09 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.55 MB" + }, + "320k": { + "size": "8.88 MB" + }, + "flac": { + "size": "24.49 MB" + }, + "atmos": { + "size": "23.67 MB" + }, + "atmos_plus": { + "size": "61.30 MB" + }, + "master": { + "size": "145.09 MB" + } + }, + "albumId": "002poFpm3AdEuF", + "strMediaMid": "002YQRbE3o9TOm", + "id": 102386024, + "albumMid": "002poFpm3AdEuF" + } + }, + { + "id": "tx_001ufyHx10iWpg", + "name": "如愿", + "singer": "王菲", + "source": "tx", + "interval": "04:25", + "meta": { + "songId": "001ufyHx10iWpg", + "albumName": "如愿", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000004Kbn3m2HV9Y3.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.05 MB" + }, + { + "type": "320k", + "size": "10.13 MB" + }, + { + "type": "flac", + "size": "48.31 MB" + }, + { + "type": "atmos", + "size": "22.91 MB" + }, + { + "type": "atmos_plus", + "size": "58.69 MB" + }, + { + "type": "master", + "size": "150.65 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.05 MB" + }, + "320k": { + "size": "10.13 MB" + }, + "flac": { + "size": "48.31 MB" + }, + "atmos": { + "size": "22.91 MB" + }, + "atmos_plus": { + "size": "58.69 MB" + }, + "master": { + "size": "150.65 MB" + } + }, + "albumId": "004Kbn3m2HV9Y3", + "strMediaMid": "001u49qo3TVkxH", + "albumMid": "004Kbn3m2HV9Y3", + "id": 326584182 + } + }, + { + "id": "wy_2668124242", + "name": "世界赠予我的", + "singer": "王菲", + "source": "wy", + "interval": "04:04", + "meta": { + "songId": 2668124242, + "albumName": "世界赠予我的", + "picUrl": "http://p2.music.126.net/jKutIHhQ2lNADGNm4bePTg==/109951170413164832.jpg", + "qualitys": [ + { + "type": "128k", + "size": "5.59 MB" + }, + { + "type": "320k", + "size": "9.32 MB" + }, + { + "type": "flac", + "size": "20.21 MB" + }, + { + "type": "hires", + "size": "69.31 MB" + }, + { + "type": "master", + "size": "118.38 MB" + } + ], + "_qualitys": { + "master": { + "size": "118.38 MB" + }, + "hires": { + "size": "69.31 MB" + }, + "flac": { + "size": "20.21 MB" + }, + "320k": { + "size": "9.32 MB" + }, + "128k": { + "size": "5.59 MB" + } + }, + "albumId": 260392229 + } + }, + { + "id": "tx_001hJxk11lYyCE", + "name": "灯火里的中国 (舒楠监制 官方正式版)", + "singer": "张也、周深", + "source": "tx", + "interval": "04:00", + "meta": { + "songId": "001hJxk11lYyCE", + "albumName": "灯火里的中国 (舒楠监制 官方正式版)", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M000001tzYvW2HYVgF.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.68 MB" + }, + { + "type": "320k", + "size": "9.20 MB" + }, + { + "type": "flac", + "size": "26.21 MB" + }, + { + "type": "atmos", + "size": "25.51 MB" + }, + { + "type": "atmos_plus", + "size": "66.57 MB" + }, + { + "type": "master", + "size": "155.20 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.68 MB" + }, + "320k": { + "size": "9.20 MB" + }, + "flac": { + "size": "26.21 MB" + }, + "atmos": { + "size": "25.51 MB" + }, + "atmos_plus": { + "size": "66.57 MB" + }, + "master": { + "size": "155.20 MB" + } + }, + "albumId": "001tzYvW2HYVgF", + "strMediaMid": "001pHZUr4BYDY8", + "id": 305452802, + "albumMid": "001tzYvW2HYVgF" + } + }, + { + "id": "tx_001xry3E1oVKTU", + "name": "有我", + "singer": "周深", + "source": "tx", + "interval": "04:03", + "meta": { + "songId": "001xry3E1oVKTU", + "albumName": "有我", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M00000406gmG2tvcaa.jpg", + "qualitys": [ + { + "type": "128k", + "size": "3.72 MB" + }, + { + "type": "320k", + "size": "9.30 MB" + }, + { + "type": "flac", + "size": "48.62 MB" + }, + { + "type": "atmos", + "size": "24.66 MB" + }, + { + "type": "atmos_plus", + "size": "63.77 MB" + }, + { + "type": "master", + "size": "155.92 MB" + } + ], + "_qualitys": { + "128k": { + "size": "3.72 MB" + }, + "320k": { + "size": "9.30 MB" + }, + "flac": { + "size": "48.62 MB" + }, + "atmos": { + "size": "24.66 MB" + }, + "atmos_plus": { + "size": "63.77 MB" + }, + "master": { + "size": "155.92 MB" + } + }, + "albumId": "00406gmG2tvcaa", + "strMediaMid": "004OFxpI0doQ29", + "id": 353729673, + "albumMid": "00406gmG2tvcaa" + } + }, + { + "id": "tx_001nWLsE4VJqC9", + "name": "光亮", + "singer": "周深", + "source": "tx", + "interval": "04:48", + "meta": { + "songId": "001nWLsE4VJqC9", + "albumName": "光亮", + "picUrl": "https://y.gtimg.cn/music/photo_new/T002R500x500M0000010lMkS0O2pq5.jpg", + "qualitys": [ + { + "type": "128k", + "size": "4.41 MB" + }, + { + "type": "320k", + "size": "11.02 MB" + }, + { + "type": "flac", + "size": "55.73 MB" + }, + { + "type": "atmos", + "size": "28.17 MB" + }, + { + "type": "atmos_plus", + "size": "71.57 MB" + }, + { + "type": "master", + "size": "176.47 MB" + } + ], + "_qualitys": { + "128k": { + "size": "4.41 MB" + }, + "320k": { + "size": "11.02 MB" + }, + "flac": { + "size": "55.73 MB" + }, + "atmos": { + "size": "28.17 MB" + }, + "atmos_plus": { + "size": "71.57 MB" + }, + "master": { + "size": "176.47 MB" + } + }, + "albumId": "0010lMkS0O2pq5", + "strMediaMid": "002aohWJ3XFXx7", + "id": 331715300, + "albumMid": "0010lMkS0O2pq5" + } + } + ] + } +} \ No newline at end of file diff --git a/src/renderer/src/utils/playlist/lx_list_part_list__name_default.lxmc b/src/renderer/src/utils/playlist/lx_list_part_list__name_default.lxmc new file mode 100644 index 0000000000000000000000000000000000000000..67194934a76c42301a87a90e2a4dbf44c9b9c6d4 GIT binary patch literal 11735 zcmV;|Ehy3-iwFP!000003e|mCZyQOn?q3n~a(aLsI@hX70fPgs+V|DEI4~$K617na zC0Ux=fIPO`^1ig&c3XDa-Wxp-sK-y1W@#-_$(Dji>% z&NY79I2)fSe?OgB%pE&c{+DCc-j{zjo&sdW@g7z3yn>U!ozA&9hHS{dyCdyxBmlhJK z{gE5LZOqK(QVYxR>Bgo;fSU+w{I+o>bs+L$c6PkAEP^zDe0pqYrt3h6uFPzzVPGMi zm}=dBLI-_ z-AsHjms+sCfmjbPy8qGteA{@;TH)UhR#yJ`#iboA$@&{>O$TeS{)Su3;Uc)dvsS@N zZ+*atUlH5-OHS_Z4slR{2)A$Lb>4pRE$aQRm+|0x8NVnzu& z9H{cCZKUtYKcaOIaH7AXyBpJCyxL6%*MmsyzpZ0r(g3s*NZ=%uN(8Ac{w@I2j&y)6Gjb~Kimi>%(H;O+%ZqRF|P%P zAIxi7fQbi8!aUUjI`N13%F;y4TWgXqPhBu?twN1?PJ9LCEx85E^ZTa2Jfv(TLGaM{ zKy>&J^As^`Xqtd{+c*N^VRx!!IS9If{VM|&;?0d$d)Jyb2bPyW3;{hc zw^+j|Kq5Xx%`YiqN%J%8aSC97i1&+9^V14Y+%!K4XokO2P+6L+`KuaQ*8G*l$fv+s zg@fj|#8+wls@$UH=k`sd`N87x!7;SlHIePLG(TpTARuafek9=>fR@%VV!-bC=ILJR z929mo3O6t2cg_}GzB0Ey*-pcqi^Yp)%*|UK${!Q;X%HslPebAQcIovS^YO{O(`WWJ zuGWowJiM5Qt$_|>K#TcCJ_$g>75NYZh`Z5)VE}tTAA^9V{6v7hZUlgM5GCmAEI^ST z=v!+Oq0cQ!#6Gtg3H#hK)Y#|bS76@~T);lRbE+F)Fx^fEU~e`ZPgb$7X-uQKX!Y1e zvar9hdJJL=h>id;EZmp=*f4h=7dEf(h%Y|ARD68OeEvB$on2VUBx~e6xhdk03t0go5~&0h8I%v(Z$)d&VepGx)4o5Gzy0k9 z1dLG#HLL?*AOb1!4S5dVuL3mV++iO{qSiJedSK zO$$)$&vMWlkYlY$f}6D%*>bQ};h;S&@f8Qal3UQ8{Jtr)C+UeFons`N8N#Dg?Fj)S zg6gylBbv{--nlLfu{Jcona_JWul9DH7d9_7d}ID_(Y*55+`d#exv_WhV*ca3(#78k zr%wFE^{+sH9si3>_x||k;Qy|N1T`VU!S=Sx$sZD4*vn_~@2?lnZ2qNwYrC(rf36!1 zg1(U%-)9B|kZ4iZ&kPM{?j{E`WLJ%FW}p$!{Jc4d7C_?hlg_mfWJX&F!1Y;;`eW{HFwCKnc;sV`Cdh^s}@lr((@`bZLHZ zmGiS~-7xR}Q2f`m(zEUVeQRDgVZM8v-`(B2@mesv&7Gan9~=Mvu-OsM=HGoXA8-Eq z!x=9-Tz7E7*hS$_hO0bvx@1__AZiEMuG#5a4JNsy#3hrQiblI&lB0m(aXv6~I6}ht zFhD>Pzmv{~p;b(6Pm{c=;boJ&vM9+Uw^k#Yb`q5f-PyeET>HwbG4)1#T{>-jYs|PL_GIjiw(`$%XMgV(0Wi=2W%%@l0!9sHJ%3F(3dga}rZ;7vZRaE5`q0jA`%0fzx zjADw8HZRW)RxSVyXu2Wdo*zcU{Ze~-T8G0iybQUg^O;WvPI5cw|1htgc11jG0uDY% zVICgwt^YOC(A{4D)469dG8KiSdvI!~20KS{4?RIBdkqCUhXk01g2yCaD4*A&ony%2 zh$u(E2gedo>-uk6<_C@jT zE2R_u&-D|h_WM*bO6_{EY&n^WL*_k7Yx#Yn? zAO<*s0Mq~jM)e<{0RaJsVGLLeo=C8727KE%5nr6xzvDfAC+D+*0y?mbMZ>{J1OjTr zFb&5*BnqQcix5P$^6)h3a;hqtxH?u8L||0c2nYvZ5No;~Wg(4(Nespq(xbr55efsk zeUS)gIwVvJVt{naf`*P`5e-E{$Ur25VlIcd>|dw$Urf^?D53#~KqwR7h_8 z6x3rd7W1;#0I>}h3x)uMT7=Oc4FxICXb5W2SWu6JaELMDdGNnvOC=TYC3`6;lP}ps z*|Yp5yC*5HFWNS>om%sL+4LO+4Ct_y&9O8YM8wdKX)J~bicl1cg-A505e#4up$LR5 zf`ehe-Q!Yt&poIpsWH-oRVM6#AT@;&y_FdSIy9>P00Gdcjxc2!uElMk+4jcsr*d;} zXm-4SpiXoUiy@6NgpF7%YG4@AV<7;8F{l}?t`MNx#)1f=SPW5}M)fen5lVsvz)=*# z;b@4(kc*B&In-u%cqCKsT@aVqUmto^TKI(kAG{g|N?KQ}16#pQD+%rI8lcqYo~% z)u7LHQ5Q@4oVhb=FgSerxN^ySxh-`n`-WtFjw{O8Q=eDXrqJiifqcWQM#Uv__gixE ztMqwQaM7W|?VQS?6AN`OMeuk_*9@-UTmvG=ZG(t0tFd-8+K>CuYCo5QVs3pjpZ#Hb znF_3o91s@=5Jdb4twMjrm|0HBNSI=trP7_1KLn@Dy`o{xApIgfhe$Nc=JehZ!r%=YML zpszXI{&lGe;+-IIDTx)@tNaU@gPp6j<`B z@<*zIixxb$b1DlyF%uk|Ly<{3KUFye2pCif(b~olmHzx7jUUCmONekxp1nI)%ii-J z-U`M|;m&sc!($ICo;t*5g{f?S-@Iae+B84^RKLbg#Rg+DXyoXqzE%s@(<@gBdT>37 z9N3k?uKVG7YFMT*%MNZ<$^P)>dOGGxK_{-KwHOuFbyuz@C%($jTa{bX_}sp!tQ)%4 zXN2(hWLw8{#kzq2Ly?nX8%BUSA-!wM0ENbbqG-^^2= z`)9K&fi@4o1BZkuVL}^Mo}0Bkz{BOy$UH->Gws=Q4Ma^T-_;IyEx<%;T_S3#(af7A z0p^pa2|bCL5C;rj&sJ+`BH*nxDTtbOP}_`@>M9(Gn$Vf3X~``Bp5HeW56zC9eHwy6 z86!HQ0_JTai4Rv|fLs???Vp(85@PorTr?lvF8%ZTmt)?^rWYo(xoWss>!jYm1r{4b zBZKKt|Jfn`nn zH_UV5t5N|C2ks@mZz{9Cb8&j@D4r)>xcLzCxSW`R356p2h961P^WDL|cm}0cyWj{% zPTqV`c=_tf0goM+`NC{MR%~JON`7a(^yaO3?M3~zO-CDD?LvJ+WVx0+7Ac2MBwI=! zNVW~|aNQu}z{iwfZ+IH=m{t5E-egM$L*81G!nOfUo*Qlzj<7eJ?RZOW(YE3CO=ZWY zLc=R-@Ti_z=&q=GfS>^k*?q&0B)(^thi77IV8H0cF%I_M|5&(jukiHyudnKb32n@K zZq_=z@?F{azz*2qUzoKmAahVC174rGDOC_FLx!?WlE=vdFnIu)moaY>NO{p zC3en3;a1^*c}sj1=BY#GE5C0l%(wRrtc}3gm9Dm21@k%q8h}*HL$Qq{5-rCECzgg` z?a_j)Z|{C)4nGxOus!tJ|tD10ZHv3*XPRd1iTP9q10dYi)}wg$)MYSH%YFIgji zJ=``hV2DG=^$ygDjUw`iY*DEy-5i3`TiZLlWUV!+D&0^gX8 z!|j{Oupdf>=MtztYp|{g^07vs28fh_#g8Ovdt`Ls1=N4Ey)(ww{n*a$J~ekQ)$X?; zO%OZidHYm0lm6+o!kuk%_euS>O;>8dm~Lo&fS3w>0+OoV*>-#XpN>tUg*aSw*rbKzIG$jfr{TxX=G}|7 zy-BTR_v^s9>z5Butc3|FjfI#wla~YjMqBDVj{_G|4bFzTVDY96-d!&Q!ls>`aPasXi3SM8+wB zWM~BTa~9H@?b7G1!u9RKjX(15AJ}e-5BKu#?s{Amz#*n3%w~HGUp{YMdy(H+-+O$$ z@brAcH|C>P#cOwq&%dufSsl|wW4aag$D<3RhRZ{8V2YQAM|~Vb8qh$GIoM%M*p$n}r+q%m>@G-^)aqi73rw^99|$TfB9u^yEb8`3b+bb3A<{o;VUu z8yJH)F%k(x;t-C}G)RqMEdg*UWu&yErlsN<8#A;tM3m|{PMJn@-O!UXUM|b{^={{K zRLRfnT=`CMJEt=%$ES}=_wzuDU zQ{Q?IdfoRVKs*@d5Co7Pmmkt9eqs-O1syMHOBgvKX-T2hN!IkDQ;z%HRQK(8YfBde zqiZ9fs%rv30+C@zWLbVB(KTUouZ)kOX#c<>PnNy;$b9%~IWghKjr^{SHCwv!pm6Y?=DF`(5rUe4gU?o&%BJG3-7P$QpWnGqeEhzC#ne4X+Kxl5 zeXfRV87R|9Ma3lXodq(6qVSfex{&M&Q>iYD1JKK7YY?AtX=`P&dSecVxjIb-bM z6LWLD^m(iF&+~ zpIb|=OoG+;6z=woeI4U~rZ=>^sYp+v5s|qRDriFx&^-%5XcfqMK${m_(zniRB1=>? zfH@fz#U84{i2&w)dKG}}IM{zjGpuVANDm6zNTR-;o|qb+#`DeNSKihUq3Kl2V6=@Sa)n}T zdT$Ibk9AFSa+v=3!`?q02pJi=Q4AZo2zPQ4?_R5ed`U zsn9nI*KZVGekwltdGF?3bMwYm($5&qOovivt(VUF&xL`q0V}_Wd?m7!)1rL7i3~?B zrkGwR-T&1|L{B5Pc$GQJj>i^PQMRpvrK_c6b={!4L`SfVB))tiZ7noGw6^99;vVxm zw+q+b6?gA{5s0v(Q7g=5f4AQ+JUtHq*n51vp?LmT@%>Nc#;wxxpG%*%+2N!7D-=LW zhF6F2NZ)M#N)60oj~9{d@Wkd2#~iF(kyp%QQku*w)+=;%PhPRp^GX+<<2+!QV9ZM7 zVM|68d01D~>u$9F^eQ;7B<7ic=32&^`z<&_YG^<%W5$gn!Z|$F6OV&%#28rOLV8#4 z@BMJ1@cOsHn-faQwQ%QR;l|U#&9h#(cDUq)X>De!!`yW~?fW`Aa{UO0aiSIqy;MtH z1Tt_hVhMHcTF1IS)0SYLO+zfHWfTL4`jniUDt5fPL2F4SIvBVgU3I@7)e>MHcV*J! z6;h-IfMFOEi@qq^NTSW!pK9&y!yRoakrd}+cv_A{79U+Vw?3AuW|y6ke-Uj~+0S}< z%g1vJI|fGuS$JI64q+%fzS<^MN2bwS>u8(*NDz{lULu60>INb}HJQ-s08l9-R@TKZ zKz+qGEx~0JLnkwp6V(x_*qPmH316cr{>sST0*IcFS)RAHFxGYb5NuFFQW|oZj0_Zs2K2ZzT+yjM*Odr7xpq_m~An^xNY{;F+l&N$+`-C`Q znM?l>jRor5Cxlyt!}HA&-yxMwS?ZMEH*R?@yurZhXhWh$N)+%GzXh&a3e_h`=UF{i%`Pk z24c$4E0I}S;t9||C11|d6+s|tlhh$W~)bk>qk2Dv4gh;i;`S7E#z1pC4eH+Qd$ zS6_ULFhZc1E@~S|a`Db}Pp=UzzPhl&Vf@C8!qW$P4{q)~xM_Rn{d}YJ{ECNOcT5zu zPbE|XDBe0{Zr$Je@pJW`>nBU$<>pWh_78O={CBjeKWP*)hZ+~QSDvrzIimR}d{p)s z_^bs}d(tSk7}ZnIJ$sH5-@(_Rt_8#Go66d5$@QlCNN{byvTz6kObSKY#u0J9sCU66 z)S6qCGMwf8Vb?spRd&bW<79WXmU0;Glvu4|dAgLtkjxJGFhFHuo$M-++%ocE zsAd6qm&0&i#9C`ol*1s76bx$>j$RbQF@nv>Eqb)MeN)*t6PdZ?I9lqN9S4cztvOjWazwfMsRV3mBH5m=dcWmxr2Ocl5NiyJ55y5P zF*=1aM(cQsts3s$`ibK1ee?8IeLfP}1f2>E*=JVhSZ=Zd>Q8a^e(B81;`>YH#?HS# zY?eX1eP!>(hr;&j!pV(;c6@aj9mmtL`8n8`8y=mnrP8Q}PY+h=>A2LLlWLL5rMNU- zSvkl@L0!%8X>eO>Q&bu?oYHcv)u?K5Y3^w`$gQHTCAes_a66~a#n{khdNXJS8B>)W zk_cj~Vyttd+Im;iF;xFgS&o|2E$g{V_C+mg# z8?K7gp#q&XqpEItIYr^ww)y#V!vW`==eZw{*)@6)H!q)-OXrLC@0K2@&PiG8LP6_J z42-sBQa9Hv(6*_$b2+Nh1D#1?@rc` z%cz$hj>w^Ig(aulaz)X>OGgkeT|=S;yf~Ulw_`MrSqO7J>h%-(kN5IB>xG|hncJ7l z5AVzewuFirxIB(^Jnb|8=W=;ag&X5+m{smmPxF*Gc-SmqRDNXl8idyL zwb6jEo|~p4xWBn&v|XS9F)?1^LIc8W9jq0gzAT#@;p2Gsi|>CZJBV*SH_vYEeXn?j zeNhk(HbJNUaCSj>X#Tl=*2Sp&bN$?(>u0Nx$3NHWFFXzx$3}ZHBsCQ`{MT%Qa@|ck zWRcgyOmnC~XyaM>DXtHxY{p7fDy&?{o{nYc@G3)&uQE-jLuhAUx>nZbWLMbBRiQHt>)xpb}|k1*^zn9YA)AJd;hP}n{D&< zFXqE1rMJ%oo1k{{899a+1rff$d@h~bDXgy_5K4;AzyBKMGtk$1veRTJ#(H{cQ1Kqc z-l)u45{v@k5NF{&!E$?E}Uek9S@SzAjl93>#rHI=lTnR54^o%Q_A zZ|24uo5$Kac?vdBrwF`#w*R!`Dc#+uAu>H)W-wzvMh()vQ`L1`k;gmp)4}ku1Xl5% zv8&<3i712ClC8%4r2OVfiqEpGc#@u!c4JHLEM zWX`FWt1y)qHVSfC<+;~&Gbt=ERTfZV4 z9vH!kWVplViq!JDbLSwZ>*Ca&g-%n9VMo~Q_!?$Z?J>QBlqK*uPAC8DKeR=gYymtt~&1 zX361&nusdTs4|n}lQ19rUO2gNFqNn8DwCPgIzSG##s)`{i$g8Bp`rfnUe0&LbcPtx zH67Mcddc0(t9fO`mpSQPwk0#1sxZii;0pUH*zXQ*TT4ns0VbI%jF^D{6CGDzF4jFX z0H@$cigR4;KYPk4mF9~V!pq0E%ZfB%hj@W7=l>s>L&U4qUfDYGD$iamci`dpCjsQi zqol0TP07>o@6<_;6Is<;3A*YyPDpiE8TKW)Ludu{bdKP~+1_NZkneW6qrmv|xHlr>)6xQRMt4cRCBs@W9;tj!K~10%xH{WT~#&jzMQ?)2hNHJ$pp zOR-CrQEM*8+U8VoDXOkP`!BtJ4I)MiM0LBDRE)lBnxSbnFy!C=%Y6H&Y=+-HRF`Lus3V=~7GRK0%QU`Ykr)YvV}yl)VQ2(Jq8c!wQN)0TD2*DFH>38^a(Zj; z=|61u<@p==-G7Fwuyn*McR(-ZtFx2X)H^vo{ZqL^KACh>i?Ghyig7 zglRZv7-33j42F@*a}^wxH|E=J8)1e>3`HS^8j5M5n1Qi|qlOlV5gU{zG7G6i+h7Nc zeX4^+Aqz#ycUF+Ha44i<7G^ON4KY0&3B{s8w^|~VLdVs?V!^N$4nr8jAV@((gAoW= zM2nzE*a$}%bae;mhn{(^;X2S@P>bP^4mA?SC>jcb7zzc$5Ti&?_mlfp^M)$2K8+1Qb;(KcZqbU&^Os;~&L|>v?RBUo+ zP<$);@$q7(ULN+D1vb9mR<6_dPq!?E24G)TaKyhc1!WhlNHUW+WKsf&nyxG-koc=L zYKbJ1$%u1u2zF)cShI^(JNK)ptacd7>J*QR4)zUJt#-r+(V-Z*wv8k{M%!9L-9v!H z+Pgc{05m_}D4hDNrp4D2WDsWlj|(!m`__nD?nm%fK$(prT^6Lm)%A`YIJnG+qoKwL zpiq3(nQMy-zFdS4O&s9_(CMvsEZJFJCbqUNARo#doR{ zureKN^L6p!8S}%ghqu>>nl8+2!`!Ef`JIo4t{b?(b`NK9-j=Z)RLd?O-uJ8 z?CAcq9-)=woqG7>j)WcEL+x49iyQ>*cT>ev2XRLS0EwYoXSWqk1%}3?;(WG|6jVgb z*pDGqnBn`O{QS;^{D)iS=bNQx+e#36xMi^_0mzz8fShVdPxovbvesiQ?XAbgl4EUb z3XFqrx}(=dEz|%kw^m}ccrM&8Tk6#S9V!|-OQCPYJd4O$?H8outS4YPJ^(Hy)XHE*I|EI;$0azLEcYws2?r&-D|I^m zO6%($j#_nDZE13wS!S1@&Ra3qIy0XDljEb^OEoxTC3majkoDkdQ{6}6mr7Qr4@;be0mPFC~C^2cDn?d}e1^OOsjAhC>nyw-{x+0{4sn=E7>?o@%_^S za$o&V+|+SB(}(-nP@=DvTH)@G45*-qNNozd*sd7LJ=0V;H-L}i0H*n`P44hw;TEN6 zrr^kU<(8oaIw!vZ=$7E(Gn?Bv)r05gO1l;(T4uqBSkG*NAeF#w8%b2_tNox00bF?_4abU#&mYYd+DoG=itQanyhFJ|fq* zmGCY(eFVHiIX_FpJE$}$b;rAI$VRVhiLgqAFi*TQhp-5@C<*V@YGk}y%TVK;lV4@V zR0S7Py||rIxs^IcjA)#sXkVmKNRk+s>V{Yq)i#cZcD6JW8UP(_;Rsh$BsdeF&D02U z(nOtFK-lNtVIE0MXU0+s@m#8*c=lcC#qSN@^ra4dqExb>99*ft0U2!0Or=*~XQrdi ze{B&GARdjFpj>@KH~_%ASu1m3MTiH0%2ie60{{btuSm4DHbt%@aCpmctC1}mCNL$W z17OLoIsjF{MF#`7b1H=&iKo*E&^$<4PxSymAmQIOjMU=C2$3EV9c<~wd~uN%h3(g6 z*4KrT4c~x(as2;A+Dhjx6;I#W+j$_?_^bYf2Rp*azc0vOBR1Luk!7g!WF$H!!b_yJ zJ3CxcO*OBc^&SVqPycT0uc8!XmCj+_<|YG7TBJpn>Wo*Z@oJFV8`fyFqO@4-+W;{{zu{I z!{Vc#%}4dSLeN|`HaCNV$>>t77LS=%+il6}mU>A_`Mfe<{XJ0?%?IROlC)C7&zn=^ z*mm1+LbbIR6{D89w5M+kuIB#GXfTuq5fG_+0F1_?HW{|@(RhY!4tSd9s`9N(So5eGzWr!WcwKR@& z2RQ_Cx~3iF#E{DX%cMB<8&)R8Nezu;iY5qXK3#`SX%XC_M3{4{QSt4VyYjZ=SFty% zf{PDSZs%0?$`WDoSv)W_b-YFNI|~2lehnR@QG-AO*(vH;xgY^S#MZ>1+~}|L=3ahx z^Pnc5JKUvc2vOI2x8@QVRf_pM*?5!fOS#sTiC#3_6^t}@&5*HF&%n494bjVFeW(!w z`)5#5uV@I9eb^G)35k#vI7}MzQy< Wmj)l}R_;WC~Ufa>z@wmMPb1P=r&E1~5Yi zNiOY)w&PO}P9I$jN4a?0iRZ-=zwdp2zDx|>w;&q6DQ#XYJi1rhx?*nr(O^EiS-AeL z@brQC;gV<^AGECgbNxiw`~CKXht>s7Pe{99HXGv$q^r|x&aE9?9!4!khsJBMLZp_v zB2pz*yVTfMLaI!mmuk5xtq{#eM{7ze5_wu7nnRS&T9cx$FLG4R)+!vV5Y4TlwI#P` zd2{=wQqOZOqk{`1bTo3*sHk8-2}OpK5oa4oJVVD9R&f+&huQ}QIsf79Glfl?v)tc# zaOhDaOk#^YR%WWx3IW>qOf$+YC*tv1a);dAYocL;xGj(e&Zr@bVpzhf5xmJ+O_99jRImJ3+nl5FK@FR&J zpB?U4jFO?Y`7l=_%G@}U-+fjy$ca{sK)UM1_rzQu}om2gw8_mu8?^R5^O7)bz zXzoS_r80X+M7a@wejb!M^r6ait { try { + if ((song as any).source === 'local') { + const id = (song as any).songmid + const url = await (window as any).api.localMusic.getUrlById(id) + if (typeof url === 'object' && url?.error) throw new Error(url.error) + if (typeof url === 'string') return url + throw new Error('本地歌曲URL获取失败') + } // 获取当前用户的信息 const LocalUserDetail = LocalUserDetailStore() // 通过统一的request方法获取真实的播放URL diff --git a/src/renderer/src/views/music/list.vue b/src/renderer/src/views/music/list.vue index 1970d44..b5c0601 100644 --- a/src/renderer/src/views/music/list.vue +++ b/src/renderer/src/views/music/list.vue @@ -81,20 +81,7 @@ const fetchLocalPlaylistSongs = async () => { const result = await window.api.songList.getSongs(playlistInfo.value.id) if (result.success && result.data) { - songs.value = result.data.map((song: any) => ({ - singer: song.singer || '未知歌手', - name: song.name || '未知歌曲', - albumName: song.albumName || '未知专辑', - albumId: song.albumId || 0, - source: song.source || 'local', - interval: song.interval || '0:00', - songmid: song.songmid, - img: song.img || '', - lrc: song.lrc || null, - types: song.types || [], - _types: song._types || {}, - typeUrl: song.typeUrl || {} - })) + songs.value = result.data // 更新歌单信息中的歌曲总数 playlistInfo.value.total = songs.value.length diff --git a/src/renderer/src/views/music/local.vue b/src/renderer/src/views/music/local.vue index 54ede0c..01d625b 100644 --- a/src/renderer/src/views/music/local.vue +++ b/src/renderer/src/views/music/local.vue @@ -1,2139 +1,417 @@ - diff --git a/src/renderer/src/views/music/recent.vue b/src/renderer/src/views/music/recent.vue index 998ec8c..0a76a02 100644 --- a/src/renderer/src/views/music/recent.vue +++ b/src/renderer/src/views/music/recent.vue @@ -112,6 +112,7 @@ const formatPlayTime = (timeStr: string): string => {