Compare commits

...

7 Commits

Author SHA1 Message Date
lyswhut
ad95d5091c 发布 v2.12.6 2026-09-19 09:57:31 +08:00
3gf8jv4dv
a04cf3b307 fix: correct wrong product name on win legacy prompt (#2978) 2026-09-19 08:39:57 +08:00
lyswhut
f9f0e61c5b 更新版本号(2.12.6-beta.2) 2026-09-19 00:51:18 +08:00
lyswhut
d2f5485996 新增 Windows 旧版提示功能,提醒用户升级至新版系统以提高安全性和性能 2026-09-19 00:50:49 +08:00
lyswhut
e20b2844fb 优化透明窗口边框显示效果,修复全屏模式下的相关问题 2026-09-19 00:29:30 +08:00
lyswhut
e9bc6a572b 修复打开某些 kg 歌单时歌曲丢失的问题 2026-09-16 23:27:12 +08:00
lyswhut
bb8d07274f 优化 tx 推荐歌单列表 2026-09-16 21:18:54 +08:00
14 changed files with 286 additions and 85 deletions

View File

@@ -6,6 +6,24 @@ Project versioning adheres to [Semantic Versioning](http://semver.org/).
Commit convention is based on [Conventional Commits](http://conventionalcommits.org). Commit convention is based on [Conventional Commits](http://conventionalcommits.org).
Change log format is based on [Keep a Changelog](http://keepachangelog.com/). Change log format is based on [Keep a Changelog](http://keepachangelog.com/).
## [2.12.6](https://github.com/lyswhut/lx-music-desktop/compare/v2.12.5...v2.12.6) - 2026-09-19
目前新项目 Any Listen 的桌面版、Web 版已实现 LX Music 的大部分功能,并额外支持 WebDAV 歌曲播放、WebDAV 数据同步、独立播放列表等功能。
以后的开发精力将主要集中在新项目上,之前大家在 LX Music 提的功能我们也会考虑在新项目中添加。
对于日常使用 LX Music 的人可以试试迁移到 Any Listen若遇到任何问题可以发 issue 反馈。
Any Listen 的项目地址为 https://github.com/any-listen/any-listen
### 优化
- 优化 tx 推荐歌单列表
- 优化禁用透明窗口的窗口边框显示效果
### 修复
- 修复打开某些 kg 歌单时歌曲丢失的问题
- 修复 Windows 7 无法启动的问题
- 修复禁用透明窗口时按 F11 无法全屏的问题
## [2.12.5](https://github.com/lyswhut/lx-music-desktop/compare/v2.12.4...v2.12.5) - 2026-09-14 ## [2.12.5](https://github.com/lyswhut/lx-music-desktop/compare/v2.12.4...v2.12.5) - 2026-09-14
目前新项目 Any Listen 的桌面版、Web 版已实现 LX Music 的大部分功能,并额外支持 WebDAV 歌曲播放、WebDAV 数据同步、独立播放列表等功能。 目前新项目 Any Listen 的桌面版、Web 版已实现 LX Music 的大部分功能,并额外支持 WebDAV 歌曲播放、WebDAV 数据同步、独立播放列表等功能。

View File

@@ -37,6 +37,7 @@ module.exports = merge(baseConfig, {
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env': { 'process.env': {
NODE_ENV: '"production"', NODE_ENV: '"production"',
BUILD_WIN7: `'${process.env.BUILD_WIN7}'`,
}, },
}), }),
], ],

12
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "lx-music-desktop", "name": "lx-music-desktop",
"version": "2.12.6-beta.1", "version": "2.12.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "lx-music-desktop", "name": "lx-music-desktop",
"version": "2.12.6-beta.1", "version": "2.12.6",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@@ -50,7 +50,7 @@
"css-loader": "^7.1.5", "css-loader": "^7.1.5",
"css-minimizer-webpack-plugin": "^8.0.0", "css-minimizer-webpack-plugin": "^8.0.0",
"del": "^6.1.1", "del": "^6.1.1",
"electron": "42.11.3", "electron": "42.11.6",
"electron-builder": "^26.15.7", "electron-builder": "^26.15.7",
"electron-debug": "^3.2.0", "electron-debug": "^3.2.0",
"electron-devtools-installer": "github:lyswhut/electron-devtools-installer#64596d615c1fc891eefd8aef1dfcb2c87aaadf03", "electron-devtools-installer": "github:lyswhut/electron-devtools-installer#64596d615c1fc891eefd8aef1dfcb2c87aaadf03",
@@ -5396,9 +5396,9 @@
} }
}, },
"node_modules/electron": { "node_modules/electron": {
"version": "42.11.3", "version": "42.11.6",
"resolved": "https://registry.npmjs.org/electron/-/electron-42.11.3.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-42.11.6.tgz",
"integrity": "sha512-r/rC6BikQ2vEGQLhnHmFwFpvVPlWTc/rnHcYfJe4HDa5MEl00yhx72ffKw7zMf1WvAM2r2hcGJ3ZftuJRo0QaQ==", "integrity": "sha512-IgDsjQp+CYLnArfQbzzS2VmkRgHmSU1G9nGVOtSnFpkUYR8rkeAUSDO77qtqAPX1jgAGly1kgisUmwB9CbUHXw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "lx-music-desktop", "name": "lx-music-desktop",
"version": "2.12.6-beta.1", "version": "2.12.6",
"description": "一个免费的音乐查找助手", "description": "一个免费的音乐查找助手",
"main": "./dist/main.js", "main": "./dist/main.js",
"scripts": { "scripts": {
@@ -123,7 +123,7 @@
"css-loader": "^7.1.5", "css-loader": "^7.1.5",
"css-minimizer-webpack-plugin": "^8.0.0", "css-minimizer-webpack-plugin": "^8.0.0",
"del": "^6.1.1", "del": "^6.1.1",
"electron": "42.11.3", "electron": "42.11.6",
"electron-builder": "^26.15.7", "electron-builder": "^26.15.7",
"electron-debug": "^3.2.0", "electron-debug": "^3.2.0",
"electron-devtools-installer": "github:lyswhut/electron-devtools-installer#64596d615c1fc891eefd8aef1dfcb2c87aaadf03", "electron-devtools-installer": "github:lyswhut/electron-devtools-installer#64596d615c1fc891eefd8aef1dfcb2c87aaadf03",

View File

@@ -3,7 +3,13 @@
对于日常使用 LX Music 的人可以试试迁移到 Any Listen若遇到任何问题可以发 issue 反馈。 对于日常使用 LX Music 的人可以试试迁移到 Any Listen若遇到任何问题可以发 issue 反馈。
Any Listen 的项目地址为 https://github.com/any-listen/any-listen Any Listen 的项目地址为 https://github.com/any-listen/any-listen
### 优化
- 优化 tx 推荐歌单列表
- 优化禁用透明窗口的窗口边框显示效果
### 修复 ### 修复
- 修复自定义主题编辑器的颜色选择器默认值显示异常的问题(#2957 - 修复打开某些 kg 歌单时歌曲丢失的问题
- 修复下载的 MP3 文件内嵌歌曲数据失败的问题(#2959 - 修复 Windows 7 无法启动的问题
- 修复禁用透明窗口时按 F11 无法全屏的问题

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,5 @@
import log from 'electron-log/node' import log from 'electron-log/node'
import os from 'node:os'
export const isLinux = process.platform == 'linux' export const isLinux = process.platform == 'linux'
@@ -13,7 +14,12 @@ export const getPlatform = (platform: NodeJS.Platform = process.platform) => {
default: return 'linux' default: return 'linux'
} }
} }
export const getOSVersion = (): string => {
const release = os.release()
const versionMatch = /^(\d+)/.exec(release)
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
return versionMatch?.[1] || release
}
// https://stackoverflow.com/a/53387532 // https://stackoverflow.com/a/53387532
export function compareVer(currentVer: string, targetVer: string): -1 | 0 | 1 { export function compareVer(currentVer: string, targetVer: string): -1 | 0 | 1 {

View File

@@ -16,6 +16,7 @@ import registerModules from '@main/modules'
// 初始化应用 // 初始化应用
const init = () => { const init = () => {
console.log('init') console.log('init')
if (process.env.BUILD_WIN7 == 'true') import('./utils/winLegacy')
void initAppSetting().then(() => { void initAppSetting().then(() => {
registerModules() registerModules()
global.lx.event_app.app_inited() global.lx.event_app.app_inited()

View File

@@ -1,7 +1,7 @@
import { BrowserWindow, dialog, session } from 'electron' import { BrowserWindow, dialog, session } from 'electron'
import path from 'node:path' import path from 'node:path'
import { createTaskBarButtons, getWindowSizeInfo } from './utils' import { createTaskBarButtons, getWindowSizeInfo } from './utils'
import { getPlatform, isLinux, isWin } from '@common/utils' import { getOSVersion, getPlatform, isLinux, isMac, isWin } from '@common/utils'
import { getProxy, openDevTools as handleOpenDevTools } from '@main/utils' import { getProxy, openDevTools as handleOpenDevTools } from '@main/utils'
import { mainSend } from '@common/mainIpc' import { mainSend } from '@common/mainIpc'
import { sendFocus, sendTaskbarButtonClick } from './rendererEvent' import { sendFocus, sendTaskbarButtonClick } from './rendererEvent'
@@ -40,6 +40,19 @@ const winEvent = () => {
browserWindow.on('blur', () => { browserWindow.on('blur', () => {
global.lx.event_app.main_window_blur() global.lx.event_app.main_window_blur()
}) })
browserWindow.on('enter-full-screen', () => {
global.lx.event_app.main_window_fullscreen(true)
})
browserWindow.on('leave-full-screen', () => {
global.lx.event_app.main_window_fullscreen(false)
// macOS needs here to set resizable to false after exiting full screen
if (isMac) {
if (browserWindow?.resizable) {
browserWindow.setResizable(false)
}
}
})
browserWindow.once('ready-to-show', () => { browserWindow.once('ready-to-show', () => {
if (!global.envParams.cmdParams.hidden) { if (!global.envParams.cmdParams.hidden) {
@@ -107,7 +120,7 @@ export const createWindow = () => {
browserWindow = new BrowserWindow(options) browserWindow = new BrowserWindow(options)
const winURL = process.env.NODE_ENV !== 'production' ? 'http://localhost:9080' : `file://${path.join(encodePath(__dirname), 'index.html')}` const winURL = process.env.NODE_ENV !== 'production' ? 'http://localhost:9080' : `file://${path.join(encodePath(__dirname), 'index.html')}`
void browserWindow.loadURL(winURL + `?os=${getPlatform()}&dt=${global.envParams.cmdParams.dt}&dark=${shouldUseDarkColors}&theme=${encodeURIComponent(JSON.stringify(theme))}`) void browserWindow.loadURL(winURL + `?os=${getPlatform()}&osver=${encodeURIComponent(getOSVersion())}&dt=${global.envParams.cmdParams.dt}&dark=${shouldUseDarkColors}&theme=${encodeURIComponent(JSON.stringify(theme))}`)
winEvent() winEvent()
@@ -228,13 +241,17 @@ export const toggleDevTools = () => {
export const setFullScreen = (isFullscreen: boolean): boolean => { export const setFullScreen = (isFullscreen: boolean): boolean => {
if (!browserWindow) return false if (!browserWindow) return false
if (isLinux) { // linux 需要先设置为可调整窗口大小才能全屏 // https://github.com/any-listen/any-listen/issues/190
// in electron ^41.2.0, windows -dt mode need to set resizable to true before setting full screen
if (!!global.envParams.cmdParams.dt || isLinux) {
// linux 需要先设置为可调整窗口大小才能全屏
if (isFullscreen) { if (isFullscreen) {
browserWindow.setResizable(isFullscreen) browserWindow.setResizable(isFullscreen)
browserWindow.setFullScreen(isFullscreen) browserWindow.setFullScreen(isFullscreen)
} else { } else {
browserWindow.setFullScreen(isFullscreen) browserWindow.setFullScreen(isFullscreen)
browserWindow.setResizable(isFullscreen) // windows/linux need to set resizable to true after exiting full screen
if (!isMac) browserWindow.setResizable(isFullscreen)
} }
} else { } else {
browserWindow.setFullScreen(isFullscreen) browserWindow.setFullScreen(isFullscreen)

View File

@@ -0,0 +1,44 @@
import os from 'node:os'
import { dialog } from 'electron'
import { STORE_NAMES } from '@common/constants'
import getStore from '@main/utils/store'
import { openUrl } from '@common/utils/electron'
const showWinLegacyMessage = () => {
if (process.platform !== 'win32') return
const storeKey = 'winLegacyMessageShown'
const dataStore = getStore(STORE_NAMES.DATA)
const count = dataStore.get<number>(storeKey) || 0
if (count > 2) return
const osVersion = os.release()
const majorVersion = parseInt(osVersion.split('.')[0], 10)
const isOldWindows = majorVersion < 10
if (isOldWindows) {
const result = dialog.showMessageBoxSync({
type: 'warning',
title: 'Windows 旧版提示',
message: '您正在使用专为旧版 Windows 准备的 LX Music它仅用于实在无法升级到新版系统的用户。\n该版本缺乏安全更新可能存在安全风险并且功能未经测试可能存在兼容性问题。\n\n如果您的设备支持 Windows 10 或更高版本,强烈建议升级系统,切换到标准版本的 LX Music以获得更好的安全性和性能。',
buttons: ['知道了'],
})
if (result === 0) {
dataStore.set(storeKey, count + 1)
}
} else {
const result = dialog.showMessageBoxSync({
type: 'warning',
title: '您正在使用旧版',
message: '您正在使用专为旧版 Windows 准备的 LX Music它仅用于实在无法升级到新版系统的用户但您的系统似乎在标准版的支持范围内为了获得最佳体验请切换到 LX Music 的标准版本。\n\n注意当前版本缺乏安全更新可能存在安全风险并且功能未经测试可能存在兼容性问题。',
buttons: ['跳转新版发布页', '知道了'],
})
if (result === 0) {
void openUrl('https://github.com/lyswhut/lx-music-desktop/releases')
} else if (result === 1) {
dataStore.set('winLegacyMessageShown', count + 1)
}
}
}
showWinLegacyMessage()

View File

@@ -99,8 +99,11 @@ body {
.disableTransparent { .disableTransparent {
background-color: var(--color-content-background); background-color: var(--color-content-background);
#body { &.linux #body {
border: 1Px solid var(--color-primary-light-500); border: 1px solid var(--color-primary-light-500);
}
&.windows:not(.windows-11, .fullscreen) #body {
border-top: 1px solid #aaa;
} }
#right { #right {

View File

@@ -15,31 +15,35 @@
<div id="root" style="display: none;"> <div id="root" style="display: none;">
</div> </div>
<script> <script>
const formatLang = (lang = 'en') => { (() => {
if (lang === 'zh-cn') return 'zh-Hans' const formatLang = (lang = 'en') => {
if (lang === 'zh-tw') return 'zh-Hant' if (lang === 'zh-cn') return 'zh-Hans'
return lang.split('-')[0] if (lang === 'zh-tw') return 'zh-Hant'
} return lang.split('-')[0]
window.setLang = (lang = navigator.language.toLocaleLowerCase()) => { }
document.documentElement.setAttribute('lang', formatLang(lang)) window.setLang = (lang = navigator.language.toLocaleLowerCase()) => {
} document.documentElement.setAttribute('lang', formatLang(lang))
window.setLang() }
document.documentElement.classList.add(/os=(\w+)/.exec(window.location.search)[1]) window.setLang()
window.shouldUseDarkColors = /dark=true/.test(window.location.search) const os = /os=(\w+)/.exec(window.location.search)?.[1]
window.dt = /dt=true/.test(window.location.search) const osver = decodeURIComponent(/osver=([^&]+)/.exec(window.location.search)?.[1] || '')
document.documentElement.classList.add(window.dt ? 'disableTransparent' : 'transparent') document.documentElement.classList.add(os, `${os}-${osver}`)
window.shouldUseDarkColors = /dark=true/.test(window.location.search)
window.dt = /dt=true/.test(window.location.search)
document.documentElement.classList.add(window.dt ? 'disableTransparent' : 'transparent')
window.dom_style = document.createElement('style') window.dom_style = document.createElement('style')
window.setTheme = colors => { window.setTheme = colors => {
window.dom_style.innerText = `:root {${(Object.entries(colors)).map(([key, value]) => `${key}:${value};`).join('')}}` window.dom_style.innerText = `:root {${(Object.entries(colors)).map(([key, value]) => `${key}:${value};`).join('')}}`
} }
const applyThemeColor = (theme) => { const applyThemeColor = (theme) => {
theme = JSON.parse(decodeURIComponent(theme)) theme = JSON.parse(decodeURIComponent(theme))
window.setTheme(theme.colors) window.setTheme(theme.colors)
document.body.appendChild(window.dom_style) document.body.appendChild(window.dom_style)
} }
if (/theme=(.+)(#|$)/.test(window.location.search)) applyThemeColor(RegExp.$1) if (/theme=(.+)(#|$)/.test(window.location.search)) applyThemeColor(RegExp.$1)
})()
</script> </script>
</body> </body>
</html> </html>

View File

@@ -384,8 +384,10 @@ export default {
}, },
}) })
if (!songInfo.list) { if (!songInfo.list) {
if (songInfo.global_collection_id) return this.getUserListDetail2(songInfo.global_collection_id) if (songInfo.global_collection_id) {
else return this.getUserListDetail4(songInfo, chain, page).catch(() => this.getUserListDetail5(chain)) return this.getListDetailByGcidChain(songInfo.global_collection_id, chain)
.catch(() => this.getUserListDetail2(songInfo.global_collection_id))
} else return this.getUserListDetail4(songInfo, chain, page).catch(() => this.getUserListDetail5(chain))
} }
let list = await this.getMusicInfos(songInfo.list) let list = await this.getMusicInfos(songInfo.list)
// console.log(info, songInfo) // console.log(info, songInfo)
@@ -490,6 +492,53 @@ export default {
} }
return Promise.all(tasks).then(([...datas]) => datas.flat()) return Promise.all(tasks).then(([...datas]) => datas.flat())
}, },
async getListDetailByGcidChain(id, chain) {
const params = 'srcappid=2919&clientver=20000&clienttime=1789566751657&mid=1789566751657&uuid=1789566751657&dfid=-&listid=15&type=0&pagesize=50&global_collection_id=' + id + '&page=1&share_type=collect&appid=1058&chain=' + chain
const data = await this.createHttp(`https://m3ws.kugou.com/v2/zlist/list?${params}&signature=${signatureParams(params, 'web')}`, {
headers: {
mid: '1586163263991',
Referer: 'https://m3ws.kugou.com/share/index.php',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
dfid: '-',
clienttime: '1586163263991',
},
})
const info = data.info[0]
if (!info) throw new Error('get list error')
let tasks = [Promise.resolve(data.list.info)]
let page = 1
let totalPage = Math.ceil(info.count / 50)
while (page < totalPage) {
const limit = 50
page += 1
const params = 'srcappid=2919&clientver=20000&clienttime=1789566751657&mid=1789566751657&uuid=1789566751657&dfid=-&listid=15&type=0&pagesize=' + limit + '&global_collection_id=' + id + '&page=' + page + '&share_type=collect&appid=1058&chain=' + chain
tasks.push(this.createHttp(`https://m3ws.kugou.com/v2/zlist/list?${params}&signature=${signatureParams(params, 'web')}`, {
headers: {
mid: '1586163263991',
Referer: 'https://m3ws.kugou.com/share/index.php',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1',
dfid: '-',
clienttime: '1586163263991',
},
}).then(data => data.list.info))
}
const hashs = await Promise.all(tasks).then(([...datas]) => datas.flat())
let list = await this.getMusicInfos(hashs)
return {
list,
page: 1,
limit: this.listDetailLimit,
total: info.count,
source: 'kg',
info: {
name: info.name,
img: info.pic && info.pic.replace('{size}', 240),
desc: info.intro,
author: info.list_create_username,
// play_count: formatPlayCount(info.playcount),
},
}
},
async getUserListDetail2(global_collection_id) { async getUserListDetail2(global_collection_id) {
let id = global_collection_id let id = global_collection_id
if (id.length > 1000) throw new Error('get list error') if (id.length > 1000) throw new Error('get list error')
@@ -613,7 +662,10 @@ export default {
async getUserListDetail(link, page, retryNum = 0) { async getUserListDetail(link, page, retryNum = 0) {
if (retryNum > 3) return Promise.reject(new Error('link try max num')) if (retryNum > 3) return Promise.reject(new Error('link try max num'))
if (link.includes('#')) link = link.replace(/#.*$/, '') if (link.includes('#')) link = link.replace(/#.*$/, '')
if (link.includes('global_collection_id')) return this.getUserListDetail2(link.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1')) if (link.includes('global_collection_id') && link.includes('chain')) {
return this.getListDetailByGcidChain(link.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'), link.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'))
}
if (link.includes('chain=')) return this.getUserListDetail3(link.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page)
if (link.includes('gcid_')) { if (link.includes('gcid_')) {
let gcid = link.match(/gcid_\w+/)?.[0] let gcid = link.match(/gcid_\w+/)?.[0]
if (gcid) { if (gcid) {
@@ -621,7 +673,6 @@ export default {
if (global_collection_id) return this.getUserListDetail2(global_collection_id) if (global_collection_id) return this.getUserListDetail2(global_collection_id)
} }
} }
if (link.includes('chain=')) return this.getUserListDetail3(link.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page)
if (link.includes('.html')) { if (link.includes('.html')) {
if (link.includes('zlist.html')) { if (link.includes('zlist.html')) {
link = link.replace(/^(.*)zlist\.html/, 'https://m3ws.kugou.com/zlist/list') link = link.replace(/^(.*)zlist\.html/, 'https://m3ws.kugou.com/zlist/list')
@@ -632,6 +683,7 @@ export default {
} }
} else if (!link.includes('song.html')) return this.getUserListDetail3(link.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page) } else if (!link.includes('song.html')) return this.getUserListDetail3(link.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page)
} }
if (link.includes('global_collection_id')) return this.getUserListDetail2(link.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'))
const requestObj_listDetailLink = httpFetch(link, { const requestObj_listDetailLink = httpFetch(link, {
headers: { headers: {
@@ -643,16 +695,18 @@ export default {
// console.log(body, location) // console.log(body, location)
if (statusCode > 400) return this.getUserListDetail(link, page, ++retryNum) if (statusCode > 400) return this.getUserListDetail(link, page, ++retryNum)
if (location) { if (location) {
if (location.includes('global_collection_id') && location.includes('chain')) {
return this.getListDetailByGcidChain(location.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'), location.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'))
}
// console.log(location) // console.log(location)
if (location.includes('global_collection_id')) return this.getUserListDetail2(location.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1')) if (location.includes('chain=')) return this.getUserListDetail3(location.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page)
if (location.includes('gcid_')) { if (location.includes('gcid_')) {
let gcid = link.match(/gcid_\w+/)?.[0] let gcid = location.match(/gcid_\w+/)?.[0]
if (gcid) { if (gcid) {
const global_collection_id = await this.decodeGcid(gcid) const global_collection_id = await this.decodeGcid(gcid)
if (global_collection_id) return this.getUserListDetail2(global_collection_id) if (global_collection_id) return this.getUserListDetail2(global_collection_id)
} }
} }
if (location.includes('chain=')) return this.getUserListDetail3(location.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page)
if (location.includes('.html')) { if (location.includes('.html')) {
if (location.includes('zlist.html')) { if (location.includes('zlist.html')) {
let link = location.replace(/^(.*)zlist\.html/, 'https://m3ws.kugou.com/zlist/list') let link = location.replace(/^(.*)zlist\.html/, 'https://m3ws.kugou.com/zlist/list')
@@ -664,6 +718,7 @@ export default {
return this.getUserListDetail(link, page, ++retryNum) return this.getUserListDetail(link, page, ++retryNum)
} else return this.getUserListDetail3(location.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page) } else return this.getUserListDetail3(location.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page)
} }
if (location.includes('global_collection_id')) return this.getUserListDetail2(location.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'))
// console.log('location', location) // console.log('location', location)
return this.getUserListDetail(location, page, ++retryNum) return this.getUserListDetail(location, page, ++retryNum)
} }

View File

@@ -10,9 +10,13 @@ export default {
limit_song: 100000, limit_song: 100000,
successCode: 0, successCode: 0,
sortList: [ sortList: [
{
name: '推荐',
id: -1,
},
{ {
name: '最热', name: '最热',
id: 5, id: 3,
}, },
{ {
name: '最新', name: '最新',
@@ -30,10 +34,9 @@ export default {
}, },
tagsUrl: 'https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&hostUin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=wk_v15.json&needNewCode=0&data=%7B%22tags%22%3A%7B%22method%22%3A%22get_all_categories%22%2C%22param%22%3A%7B%22qq%22%3A%22%22%7D%2C%22module%22%3A%22playlist.PlaylistAllCategoriesServer%22%7D%7D', tagsUrl: 'https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&hostUin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=wk_v15.json&needNewCode=0&data=%7B%22tags%22%3A%7B%22method%22%3A%22get_all_categories%22%2C%22param%22%3A%7B%22qq%22%3A%22%22%7D%2C%22module%22%3A%22playlist.PlaylistAllCategoriesServer%22%7D%7D',
hotTagUrl: 'https://c.y.qq.com/node/pc/wk_v15/category_playlist.html', hotTagUrl: 'https://c.y.qq.com/node/pc/wk_v15/category_playlist.html',
getListUrl(sortId, id, page) { getListUrl(id, page) {
if (id) { id = parseInt(id)
id = parseInt(id) return `https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&hostUin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=wk_v15.json&needNewCode=0&data=${encodeURIComponent(JSON.stringify({
return `https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&hostUin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=wk_v15.json&needNewCode=0&data=${encodeURIComponent(JSON.stringify({
comm: { cv: 1602, ct: 20 }, comm: { cv: 1602, ct: 20 },
playlist: { playlist: {
method: 'get_category_content', method: 'get_category_content',
@@ -48,15 +51,14 @@ export default {
module: 'playlist.PlayListCategoryServer', module: 'playlist.PlayListCategoryServer',
}, },
}))}` }))}`
} // return `https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&hostUin=0&format=json&inCharset=utf-8&outCharset=utf-8&data=${encodeURIComponent(JSON.stringify({
return `https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&hostUin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=wk_v15.json&needNewCode=0&data=${encodeURIComponent(JSON.stringify({ // comm: { cv: 1602, ct: 20 },
comm: { cv: 1602, ct: 20 }, // playlist: {
playlist: { // method: 'get_playlist_by_tag',
method: 'get_playlist_by_tag', // param: { id: 10000000, sin: this.limit_list * (page - 1), size: this.limit_list, order: sortId, cur_page: page },
param: { id: 10000000, sin: this.limit_list * (page - 1), size: this.limit_list, order: sortId, cur_page: page }, // module: 'playlist.PlayListPlazaServer',
module: 'playlist.PlayListPlazaServer', // },
}, // }))}`
}))}`
}, },
getListDetailUrl(id) { getListDetailUrl(id) {
return `https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&new_format=1&disstid=${id}&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0` return `https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&new_format=1&disstid=${id}&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0`
@@ -112,41 +114,85 @@ export default {
})) }))
}, },
async getRecommendList(page) {
const { body } = await httpFetch(
`https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&hostUin=0&format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=wk_v15.json&needNewCode=0&data=${encodeURIComponent(
JSON.stringify({
comm: { cv: 1602, ct: 20 },
playlist: {
module: 'music.playlist.PlaylistSquare',
method: 'GetRecommendWhole',
param: {
IsReqFeed: true,
FeedReq: {
From: (page - 1) * this.limit_list,
Size: this.limit_list,
},
},
},
}),
)}`,
).promise
if (body.code !== this.successCode) throw new Error('tx getRecommendList failed')
return {
list:
body.playlist.data.FeedRsp.List?.map(({ Playlist }) => ({
play_count: formatPlayCount(Playlist.basic.play_cnt),
id: String(Playlist.basic.tid),
author: decodeName(Playlist.basic.creator.nick),
name: decodeName(Playlist.basic.title),
time: Playlist.basic.modify_time ? dateFormat(Playlist.basic.modify_time * 1000, 'Y-M-D') : '',
img: Playlist.basic.cover.medium_url || Playlist.basic.cover.default_url,
total: Playlist.basic.song_cnt,
desc: decodeName(Playlist.basic.desc).replace(/<br>/g, '\n'),
source: 'tx',
})) || [],
total: body.playlist.data.FeedRsp.FromLimit,
limit: this.limit_list,
page,
source: 'tx',
}
},
// 获取列表数据 // 获取列表数据
getList(sortId, tagId, page, tryNum = 0) { getList(sortId, tagId, page, tryNum = 0) {
if (this._requestObj_list) this._requestObj_list.cancelHttp() if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num')) if (tryNum > 2) return Promise.reject(new Error('try max num'))
if (!tagId) {
if (!tagId && sortId == -1) return this.getRecommendList(page)
tagId = sortId
}
this._requestObj_list = httpFetch( this._requestObj_list = httpFetch(
this.getListUrl(sortId, tagId, page), this.getListUrl(tagId, page),
) )
// console.log(this.getListUrl(sortId, tagId, page)) // console.log(this.getListUrl(sortId, tagId, page))
return this._requestObj_list.promise.then(({ body }) => { return this._requestObj_list.promise.then(({ body }) => {
if (body.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum) if (body.code !== this.successCode) return this.getList(sortId, tagId, page, ++tryNum)
return tagId ? this.filterList2(body.playlist.data, page) : this.filterList(body.playlist.data, page) return this.filterList(body.playlist.data, page)
}) })
}, },
filterList(data, page) { // filterList(data, page) {
return { // return {
list: data.v_playlist.map(item => ({ // list: data.v_playlist.map(item => ({
play_count: formatPlayCount(item.access_num), // play_count: formatPlayCount(item.access_num),
id: String(item.tid), // id: String(item.tid),
author: item.creator_info.nick, // author: item.creator_info.nick,
name: item.title, // name: item.title,
time: item.modify_time ? dateFormat(item.modify_time * 1000, 'Y-M-D') : '', // time: item.modify_time ? dateFormat(item.modify_time * 1000, 'Y-M-D') : '',
img: item.cover_url_medium, // img: item.cover_url_medium,
// grade: item.favorcnt / 10, // // grade: item.favorcnt / 10,
total: item.song_ids?.length, // total: item.song_ids?.length,
desc: decodeName(item.desc).replace(/<br>/g, '\n'), // desc: decodeName(item.desc).replace(/<br>/g, '\n'),
source: 'tx', // source: 'tx',
})), // })),
total: data.total, // total: data.total,
page, // page,
limit: this.limit_list, // limit: this.limit_list,
source: 'tx', // source: 'tx',
} // }
}, // },
filterList2({ content }, page) { filterList({ content }, page) {
// console.log(content.v_item) // console.log(content.v_item)
return { return {
list: content.v_item.map(({ basic }) => ({ list: content.v_item.map(({ basic }) => ({