refactor: 清理死代码与构建加固

This commit is contained in:
snowy-yang committed 2026-09-16 21:40:28 +08:00
1 parent 5dd0468860
commit d142dd6096
11 files changed
+6 -1145

No files matched your search

+1 -3
View File
@@ -4,7 +4,6 @@ import tx from './tx/index.ts'
import wy from './wy/index.ts'
import mg from './mg/index.ts'
import bd from './bd/index.ts'
import xm from './xm.ts'
import { supportQuality } from './api-source.ts'
import type { PlatformModule, SearchResult, SongInfo } from './types.ts'
import {
@@ -26,7 +25,7 @@ const SUPPORTED_SOURCES: Array<{ id: string; name: string; module: PlatformModul
{ id: 'tx', name: 'QQ音乐', module: tx },
{ id: 'wy', name: '网易音乐', module: wy },
{ id: 'mg', name: '咪咕音乐', module: mg },
{ id: 'xm', name: '虾米音乐', module: xm },
// bd 取流接口可用但暂不列入平台列表(直连路由仍可访问 /url/bd/:id)
// { id: 'bd', name: '百度音乐', module: bd },
]
@@ -87,7 +86,6 @@ export const searchMusic = async (
const tasks: Array<Promise<SearchResult | null>> = []
for (const source of SUPPORTED_SOURCES) {
if (
source.id === 'xm' ||
source.id === excludeSource ||
!source.module.musicSearch
)
-112
View File
@@ -1,112 +0,0 @@
import { decodeName, formatPlayTime, sizeFormate } from '../index.js'
import { signatureParams, createHttpFetch } from './util.js'
import { formatSingerName } from '../../utils.js'
export default {
limit: 30,
total: 0,
page: 0,
allPage: 1,
musicSearch(str, page, limit) {
const sign = signatureParams(`userid=0&area_code=1&appid=1005&dopicfull=1&page=${page}&token=0&privilegefilter=0&requestid=0&pagesize=${limit}&user_labels=&clienttime=0&sec_aggre=1&iscorrection=1&uuid=0&mid=0&keyword=${str}&dfid=-&clientver=11409&platform=AndroidFilter&tag=`, 3)
return createHttpFetch(`https://gateway.kugou.com/complexsearch/v3/search/song?userid=0&area_code=1&appid=1005&dopicfull=1&page=${page}&token=0&privilegefilter=0&requestid=0&pagesize=${limit}&user_labels=&clienttime=0&sec_aggre=1&iscorrection=1&uuid=0&mid=0&dfid=-&clientver=11409&platform=AndroidFilter&tag=&keyword=${encodeURIComponent(str)}&signature=${sign}`, {
headers: {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
referer: 'https://kugou.com',
},
}).then(body => body)
},
filterList(raw) {
let ids = new Set()
const list = []
raw.forEach(item => {
if (ids.has(item.Audioid)) return
ids.add(item.Audioid)
const types = []
const _types = {}
if (item.FileSize !== 0) {
let size = sizeFormate(item.FileSize)
types.push({ type: '128k', size, hash: item.FileHash })
_types['128k'] = {
size,
hash: item.FileHash,
}
}
if (item.HQ != undefined) {
let size = sizeFormate(item.HQ.FileSize)
types.push({ type: '320k', size, hash: item.HQ.Hash })
_types['320k'] = {
size,
hash: item.HQ.Hash,
}
}
if (item.SQ != undefined) {
let size = sizeFormate(item.SQ.FileSize)
types.push({ type: 'flac', size, hash: item.SQ.Hash })
_types.flac = {
size,
hash: item.SQ.Hash,
}
}
if (item.Res != undefined) {
let size = sizeFormate(item.Res.FileSize)
types.push({ type: 'flac24bit', size, hash: item.Res.Hash })
_types.flac24bit = {
size,
hash: item.Res.Hash,
}
}
list.push({
singer: decodeName(formatSingerName(item.Singers)),
name: decodeName(item.SongName),
albumName: decodeName(item.AlbumName),
albumId: item.AlbumID,
songmid: item.Audioid,
source: 'kg',
interval: formatPlayTime(item.Duration),
_interval: item.Duration,
img: null,
lrc: null,
otherSource: null,
hash: item.FileHash,
types,
_types,
typeUrl: {},
})
})
return list
},
handleResult(rawData) {
const rawList = []
rawData.forEach(item => {
rawList.push(item)
item.Grp.forEach(e => rawList.push(e))
})
return this.filterList(rawList)
},
search(str, page = 1, limit, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
if (limit == null) limit = this.limit
return this.musicSearch(str, page, limit).then(data => {
let list = this.handleResult(data.lists)
if (!list) return this.search(str, page, limit, retryNum)
this.total = data.total
this.page = page
this.allPage = Math.ceil(this.total / limit)
return Promise.resolve({
list,
allPage: this.allPage,
limit,
total: this.total,
source: 'kg',
})
})
},
}
-794
View File
@@ -1,794 +0,0 @@
import { httpFetch } from '../../../request.js'
import { formatSingerName } from '../../utils.js'
import { decodeName, formatPlayTime, sizeFormate, dateFormat, formatPlayCount } from '../../../index.js'
import { signatureParams, createHttpFetch } from './../util.js'
import { getMusicInfosByList } from '../musicInfo.js'
import album from '../album.js'
export default {
_requestObj_tags: null,
_requestObj_listInfo: null,
_requestObj_list: null,
_requestObj_listRecommend: null,
listDetailLimit: 10000,
currentTagInfo: {
id: undefined,
info: undefined,
},
sortList: [
{
name: '推荐',
id: '5',
},
{
name: '最热',
id: '6',
},
{
name: '最新',
id: '7',
},
{
name: '热藏',
id: '3',
},
{
name: '飙升',
id: '8',
},
],
cache: new Map(),
collectionIdListInfoCache: new Map(),
regExps: {
listData: /global\.data = (\[.+\]);/,
listInfo: /global = {[\s\S]+?name: "(.+)"[\s\S]+?pic: "(.+)"[\s\S]+?};/,
// https://www.kugou.com/yy/special/single/1067062.html
listDetailLink: /^.+\/(\d+)\.html(?:\?.*|&.*$|#.*$|$)/,
},
/**
* 获取歌曲列表内的音乐
* @param {*} id
* @param {*} page
*/
async getListDetail(id, page) {
id = id.toString()
if (id.includes('special/single/')) id = id.replace(this.regExps.listDetailLink, '$1')
// fix https://www.kugou.com/songlist/xxx/?uid=xxx&chl=qq_client&cover=http%3A%2F%2Fimge.kugou.com%xxx.jpg&iszlist=1
if (/https?:/.test(id)) {
if (id.includes('#')) id = id.replace(/#.*$/, '')
if (id.includes('global_collection_id')) return this.getUserListDetailByCollectionId(id.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'), page)
if (id.includes('chain=')) return this.getUserListDetail3(id.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page)
if (id.includes('.html')) {
if (id.includes('zlist.html')) {
id = id.replace(/^(.*)zlist\.html/, 'https://m3ws.kugou.com/zlist/list')
if (id.includes('pagesize')) {
id = id.replace('pagesize=30', 'pagesize=' + this.listDetailLimit).replace('page=1', 'page=' + page)
} else {
id += `&pagesize=${this.listDetailLimit}&page=${page}`
}
} else if (!id.includes('song.html')) return this.getUserListDetail3(id.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page)
}
return this.getUserListDetail(id.replace(/^.*?http/, 'http'), page)
}
if (/^\d+$/.test(id)) return this.getUserListDetailByCode(id, page)
if (id.startsWith('gid_')) return this.getUserListDetailByCollectionId(id.replace('gid_', ''), page)
if (id.startsWith('id_')) return this.getUserListDetailBySpecialId(id.replace('id_', ''), page)
return new Error('Failed.')
},
/**
* 获取SpecialId歌单
* @param {*} id
*/
async getUserListDetailBySpecialId(id, page, tryNum = 0) {
if (tryNum > 2) throw new Error('try max num')
const { body } = await httpFetch(this.getSongListDetailUrl(id)).promise
let listData = body.match(this.regExps.listData)
let listInfo = body.match(this.regExps.listInfo)
if (!listData) return this.getListDetailBySpecialId(id, page, ++tryNum)
let list = await getMusicInfosByList(JSON.parse(listData[1]))
let name
let pic
if (listInfo) {
name = listInfo[1]
pic = listInfo[2]
}
let desc = this.parseHtmlDesc(body)
return {
list,
page: 1,
limit: 10000,
total: list.length,
source: 'kg',
info: {
name,
img: pic,
desc,
// author: body.result.info.userinfo.username,
// play_count: formatPlayCount(body.result.listen_num),
},
}
},
parseHtmlDesc(html) {
const prefix = '<div class="pc_specail_text pc_singer_tab_content" id="specailIntroduceWrap">'
let index = html.indexOf(prefix)
if (index < 0) return null
const afterStr = html.substring(index + prefix.length)
index = afterStr.indexOf('</div>')
if (index < 0) return null
return decodeName(afterStr.substring(0, index))
},
/**
* 使用SpecialId获取CollectionId
* @param {*} specialId
*/
async getCollectionIdBySpecialId(specialId) {
return httpFetch(`http://mobilecdnbj.kugou.com/api/v5/special/info?specialid=${specialId}`, {
headers: {
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; HLK-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.102 Mobile Safari/537.36 EdgA/104.0.1293.70',
},
}).promise.then(({ body }) => {
// console.log('getCollectionIdBySpecialId', body)
if (!body.data.global_specialid) return Promise.reject(new Error('Failed to get global collection id.'))
return body.data.global_specialid
})
},
/**
* 获取歌单URL
* @param {*} sortId
* @param {*} tagId
* @param {*} page
*/
getSongListUrl(sortId, tagId, page) {
if (tagId == null) tagId = ''
return `http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_ajax=1&cdn=cdn&t=${sortId}&c=${tagId}&p=${page}`
},
getInfoUrl(tagId) {
return tagId
? `http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_smarty=1&cdn=cdn&t=5&c=${tagId}`
: 'http://www2.kugou.kugou.com/yueku/v9/special/getSpecial?is_smarty=1&'
},
getSongListDetailUrl(id) {
return `http://www2.kugou.kugou.com/yueku/v9/special/single/${id}-5-9999.html`
},
filterInfoHotTag(rawData) {
const result = []
if (rawData.status !== 1) return result
for (const key of Object.keys(rawData.data)) {
let tag = rawData.data[key]
result.push({
id: tag.special_id,
name: tag.special_name,
source: 'kg',
})
}
return result
},
filterTagInfo(rawData) {
const result = []
for (const name of Object.keys(rawData)) {
result.push({
name,
list: rawData[name].data.map(tag => ({
parent_id: tag.parent_id,
parent_name: tag.pname,
id: tag.id,
name: tag.name,
source: 'kg',
})),
})
}
return result
},
filterSongList(rawData) {
return rawData.map(item => ({
play_count: item.total_play_count || formatPlayCount(item.play_count),
id: 'id_' + item.specialid,
author: item.nickname,
name: item.specialname,
time: dateFormat(item.publish_time || item.publishtime, 'Y-M-D'),
img: item.img || item.imgurl,
total: item.songcount,
grade: item.grade,
desc: item.intro,
source: 'kg',
}))
},
getSongList(sortId, tagId, page, tryNum = 0) {
if (this._requestObj_list) this._requestObj_list.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_list = httpFetch(
this.getSongListUrl(sortId, tagId, page),
)
return this._requestObj_list.promise.then(({ body }) => {
if (!body || body.status !== 1) return this.getSongList(sortId, tagId, page, ++tryNum)
return this.filterSongList(body.special_db)
})
},
getSongListRecommend(tryNum = 0) {
if (this._requestObj_listRecommend) this._requestObj_listRecommend.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listRecommend = httpFetch(
'http://everydayrec.service.kugou.com/guess_special_recommend',
{
method: 'post',
headers: {
'User-Agent': 'KuGou2012-8275-web_browser_event_handler',
},
body: {
appid: 1001,
clienttime: 1566798337219,
clientver: 8275,
key: 'f1f93580115bb106680d2375f8032d96',
mid: '21511157a05844bd085308bc76ef3343',
platform: 'pc',
userid: '262643156',
return_min: 6,
return_max: 15,
},
},
)
return this._requestObj_listRecommend.promise.then(({ body }) => {
if (body.status !== 1) return this.getSongListRecommend(++tryNum)
return this.filterSongList(body.data.special_list)
})
},
/**
* 通过CollectionId获取歌单详情
* @param {*} id
*/
async getUserListInfoByCollectionId(id) {
if (!id || id.length > 1000) return Promise.reject(new Error('get list error'))
if (this.collectionIdListInfoCache.has(id)) return this.collectionIdListInfoCache.get(id)
const params = `appid=1058&specialid=0&global_specialid=${id}&format=jsonp&srcappid=2919&clientver=20000&clienttime=1586163242519&mid=1586163242519&uuid=1586163242519&dfid=-`
return createHttpFetch(`https://mobiles.kugou.com/api/v5/special/info_v2?${params}&signature=${signatureParams(params, 'web')}`, {
headers: {
mid: '1586163242519',
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: '1586163242519',
},
}).then(body => {
let info = {
type: body.type,
userName: body.nickname,
userAvatar: body.user_avatar,
imageUrl: body.imgurl,
desc: body.intro,
name: body.specialname,
globalSpecialid: body.global_specialid,
total: body.songcount,
playCount: body.playcount,
}
this.collectionIdListInfoCache.set(id, info)
return info
})
},
/**
* 通过SpecialId获取歌单
* @param {*} id
*/
// async getUserListDetailBySpecialId(id, page = 1, limit = 300) {
// if (!id || id.length > 1000) return Promise.reject(new Error('get list error.'))
// const listInfo = await this.getListInfoBySpecialId(id)
// const params = `specialid=${id}&need_sort=1&module=CloudMusic&clientver=11589&pagesize=${limit}&userid=0&page=${page}&type=0&area_code=1&appid=1005`
// return createHttpFetch(`http://pubsongs.kugou.com/v2/get_other_list_file?${params}&signature=${signatureParams(params, 2)}`, {
// headers: {
// 'User-Agent': 'Android10-AndroidPhone-11589-201-0-playlist-wifi',
// },
// }).then(body => {
// if (!body.info) return Promise.reject(new Error('Get list failed.'))
// const songList = this.filterListByCollectionId(body.info)
// return {
// list: songList || [],
// page,
// limit,
// total: body.count,
// source: 'kg',
// info: {
// name: listInfo.name,
// img: listInfo.image,
// desc: listInfo.desc,
// // author: listInfo.userName,
// // play_count: formatPlayCount(listInfo.playCount),
// },
// }
// })
// },
/**
* 通过CollectionId获取歌单
* @param {*} id
*/
async getUserListDetailByCollectionId(id, page = 1, limit = 300) {
if (!id || id.length > 1000) return Promise.reject(new Error('ID error.'))
const listInfo = await this.getUserListInfoByCollectionId(id)
const params = `need_sort=1&module=CloudMusic&clientver=11589&pagesize=${limit}&global_collection_id=${id}&userid=0&page=${page}&type=0&area_code=1&appid=1005`
return createHttpFetch(`http://pubsongs.kugou.com/v2/get_other_list_file?${params}&signature=${signatureParams(params, 'android')}`, {
headers: {
'User-Agent': 'Android10-AndroidPhone-11589-201-0-playlist-wifi',
},
}).then(body => {
if (!body.info) return Promise.reject(new Error('Get list failed.'))
const songList = this.filterListByCollectionId(body.info)
return {
list: songList || [],
page,
limit,
total: listInfo.total,
source: 'kg',
info: {
name: listInfo.name,
img: listInfo.imageUrl && listInfo.imageUrl.replace('{size}', 240),
desc: listInfo.desc,
author: listInfo.userName,
play_count: formatPlayCount(listInfo.playCount),
},
}
})
},
/**
* 过滤GlobalSpecialId歌单数据
* @param {*} rawData
*/
filterListByCollectionId(rawData) {
let ids = new Set()
let list = []
rawData.forEach(item => {
if (!item) return
if (ids.has(item.hash)) return
ids.add(item.hash)
const types = []
const _types = {}
item.relate_goods.forEach(data => {
let size = sizeFormate(data.size)
switch (data.level) {
case 2:
types.push({ type: '128k', size, hash: data.hash })
_types['128k'] = {
size,
hash: data.hash,
}
break
case 4:
types.push({ type: '320k', size, hash: data.hash })
_types['320k'] = {
size,
hash: data.hash,
}
break
case 5:
types.push({ type: 'flac', size, hash: data.hash })
_types.flac = {
size,
hash: data.hash,
}
break
case 6:
types.push({ type: 'flac24bit', size, hash: data.hash })
_types.flac24bit = {
size,
hash: data.hash,
}
break
}
})
list.push({
singer: formatSingerName(item.singerinfo, 'name') || decodeName(item.name).split(' - ')[0].replace(/&/g, '、'),
name: decodeName(item.name).split(' - ')[1],
albumName: decodeName(item.albuminfo.name),
albumId: item.albuminfo.id,
songmid: item.audio_id,
source: 'kg',
interval: formatPlayTime(parseInt(item.timelen) / 1000),
img: null,
lrc: null,
hash: item.hash,
otherSource: null,
types,
_types,
typeUrl: {},
})
})
return list
},
/**
* 通过酷狗码获取歌单
* @param {*} id
* @param {*} page
*/
async getUserListDetailByCode(id, page = 1) {
// type 1单曲,2歌单,3电台,4酷狗码,5别人的播放队列
const codeData = await createHttpFetch('http://t.kugou.com/command/', {
method: 'POST',
headers: {
'KG-RC': 1,
'KG-THash': 'network_super_call.cpp:3676261689:379',
'User-Agent': '',
},
body: { appid: 1001, clientver: 9020, mid: '21511157a05844bd085308bc76ef3343', clienttime: 640612895, key: '36164c4015e704673c588ee202b9ecb8', data: id },
})
if (!codeData) return Promise.reject(new Error('Get list failed.'))
const codeInfo = codeData.info
switch (codeInfo.type) {
case 2:
if (!codeInfo.global_collection_id) return this.getUserListDetailBySpecialId(codeInfo.id, page)
break
case 3:
return album.getAlbumDetail(codeInfo.id, page)
}
if (codeInfo.global_collection_id) return this.getUserListDetailByCollectionId(codeInfo.global_collection_id, page)
if (codeInfo.userid != null) {
const songList = await createHttpFetch('http://www2.kugou.kugou.com/apps/kucodeAndShare/app/', {
method: 'POST',
headers: {
'KG-RC': 1,
'KG-THash': 'network_super_call.cpp:3676261689:379',
'User-Agent': '',
},
body: { appid: 1001, clientver: 9020, mid: '21511157a05844bd085308bc76ef3343', clienttime: 640612895, key: '36164c4015e704673c588ee202b9ecb8', data: { id: codeInfo.id, type: 3, userid: codeInfo.userid, collect_type: 0, page: 1, pagesize: codeInfo.count } },
})
// console.log(songList)
let list = await getMusicInfosByList(songList || codeInfo.list)
return {
list,
page: 1,
limit: codeInfo.count,
total: list.length,
source: 'kg',
info: {
name: codeInfo.name,
img: (codeInfo.img_size && codeInfo.img_size.replace('{size}', 240)) || codeInfo.img,
// desc: body.result.info.list_desc,
author: codeInfo.username,
// play_count: formatPlayCount(info.count),
},
}
}
},
async getUserListDetail3(chain, page) {
const songInfo = await createHttpFetch(`http://m.kugou.com/schain/transfer?pagesize=${this.listDetailLimit}&chain=${chain}&su=1&page=${page}&n=0.7928855356604456`, {
headers: {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
},
})
if (!songInfo.list) {
if (songInfo.global_collection_id) return this.getUserListDetailByCollectionId(songInfo.global_collection_id, page)
else return this.getUserListDetail4(songInfo, chain, page).catch(() => this.getUserListDetail5(chain))
}
let list = await getMusicInfosByList(songInfo.list)
// console.log(info, songInfo)
return {
list,
page: 1,
limit: this.listDetailLimit,
total: list.length,
source: 'kg',
info: {
name: songInfo.info.name,
img: songInfo.info.img,
// desc: body.result.info.list_desc,
author: songInfo.info.username,
// play_count: formatPlayCount(info.count),
},
}
},
async getUserListDetailByLink({ info }, link) {
let listInfo = info['0']
let total = listInfo.count
let tasks = []
let page = 0
while (total) {
const limit = total > 90 ? 90 : total
total -= limit
page += 1
tasks.push(createHttpFetch(link.replace(/pagesize=\d+/, 'pagesize=' + limit).replace(/page=\d+/, 'page=' + page), {
headers: {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
Referer: link,
},
}).then(data => data.list.info))
}
let result = await Promise.all(tasks).then(([...datas]) => datas.flat())
result = await getMusicInfosByList(result)
// console.log(result)
return {
list: result,
page,
limit: this.listDetailLimit,
total: result.length,
source: 'kg',
info: {
name: listInfo.name,
img: listInfo.pic && listInfo.pic.replace('{size}', 240),
// desc: body.result.info.list_desc,
author: listInfo.list_create_username,
// play_count: formatPlayCount(listInfo.count),
},
}
},
createGetListDetail2Task(id, total) {
let tasks = []
let page = 0
while (total) {
const limit = total > 300 ? 300 : total
total -= limit
page += 1
const params = 'appid=1058&global_specialid=' + id + '&specialid=0&plat=0&version=8000&page=' + page + '&pagesize=' + limit + '&srcappid=2919&clientver=20000&clienttime=1586163263991&mid=1586163263991&uuid=1586163263991&dfid=-'
tasks.push(createHttpFetch(`https://mobiles.kugou.com/api/v5/special/song_v2?${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.info))
}
return Promise.all(tasks).then(([...datas]) => datas.flat())
},
async getUserListDetail2(global_collection_id) {
let id = global_collection_id
if (id.length > 1000) throw new Error('get list error')
const params = 'appid=1058&specialid=0&global_specialid=' + id + '&format=jsonp&srcappid=2919&clientver=20000&clienttime=1586163242519&mid=1586163242519&uuid=1586163242519&dfid=-'
let info = await createHttpFetch(`https://mobiles.kugou.com/api/v5/special/info_v2?${params}&signature=${signatureParams(params, 'web')}`, {
headers: {
mid: '1586163242519',
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: '1586163242519',
},
})
const songInfo = await this.createGetListDetail2Task(id, info.songcount)
let list = await getMusicInfosByList(songInfo)
// console.log(info, songInfo, list)
return {
list,
page: 1,
limit: this.listDetailLimit,
total: list.length,
source: 'kg',
info: {
name: info.specialname,
img: info.imgurl && info.imgurl.replace('{size}', 240),
desc: info.intro,
author: info.nickname,
play_count: formatPlayCount(info.playcount),
},
}
},
async getListInfoByChain(chain) {
if (this.cache.has(chain)) return this.cache.get(chain)
const { body } = await httpFetch(`https://m.kugou.com/share/?chain=${chain}&id=${chain}`, {
headers: {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
},
}).promise
// console.log(body)
let result = body.match(/var\sphpParam\s=\s({.+?});/)
if (result) result = JSON.parse(result[1])
this.cache.set(chain, result)
return result
},
async getUserListDetailByPcChain(chain) {
let key = `${chain}_pc_list`
if (this.cache.has(key)) return this.cache.get(key)
const { body } = await httpFetch(`http://www.kugou.com/share/${chain}.html`, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36',
},
}).promise
let result = body.match(/var\sdataFromSmarty\s=\s(\[.+?\])/)
if (result) result = JSON.parse(result[1])
this.cache.set(chain, result)
result = await getMusicInfosByList(result)
// console.log(info, songInfo)
return result
},
async getUserListDetail4(songInfo, chain, page) {
const limit = 100
const [listInfo, list] = await Promise.all([
this.getListInfoByChain(chain),
this.getUserListDetailBySpecialId(songInfo.id, page, limit),
])
return {
list: list || [],
page,
limit,
total: list.length ?? 0,
source: 'kg',
info: {
name: listInfo.specialname,
img: listInfo.imgurl && listInfo.imgurl.replace('{size}', 240),
// desc: body.result.info.list_desc,
author: listInfo.nickname,
// play_count: formatPlayCount(info.count),
},
}
},
async getUserListDetail5(chain) {
const [listInfo, list] = await Promise.all([
this.getListInfoByChain(chain),
this.getUserListDetailByPcChain(chain),
])
return {
list: list || [],
page: 1,
limit: this.listDetailLimit,
total: list.length ?? 0,
source: 'kg',
info: {
name: listInfo.specialname,
img: listInfo.imgurl && listInfo.imgurl.replace('{size}', 240),
// desc: body.result.info.list_desc,
author: listInfo.nickname,
// play_count: formatPlayCount(info.count),
},
}
},
async getUserListDetail(link, page, retryNum = 0) {
if (retryNum > 3) return Promise.reject(new Error('link try max num'))
const requestLink = httpFetch(link, {
headers: {
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',
Referer: link,
},
follow_max: 2,
})
const { headers: { location }, statusCode, body } = await requestLink.promise
// console.log(body, location, statusCode)
if (statusCode > 400) return this.getUserListDetail(link, page, ++retryNum)
if (typeof body == 'string') {
if (body.includes('"global_collection_id":')) return this.getUserListDetailByCollectionId(body.replace(/^[\s\S]+?"global_collection_id":"(\w+)"[\s\S]+?$/, '$1'), page)
if (body.includes('"albumid":')) return album.getAlbumDetail(body.replace(/^[\s\S]+?"albumid":(\w+)[\s\S]+?$/, '$1'), page)
if (body.includes('"album_id":') && link.includes('album/info')) return album.getAlbumDetail(body.replace(/^[\s\S]+?"album_id":(\w+)[\s\S]+?$/, '$1'), page)
if (body.includes('list_id = "') && link.includes('album/info')) return album.getAlbumDetail(body.replace(/^[\s\S]+?list_id = "(\w+)"[\s\S]+?$/, '$1'), page)
}
if (location) {
// 概念版分享链接 https://t1.kugou.com/xxx
if (location.includes('global_specialid')) return this.getUserListDetailByCollectionId(location.replace(/^.*?global_specialid=(\w+)(?:&.*$|#.*$|$)/, '$1'), page)
if (location.includes('global_collection_id')) return this.getUserListDetailByCollectionId(location.replace(/^.*?global_collection_id=(\w+)(?:&.*$|#.*$|$)/, '$1'), page)
if (location.includes('chain=')) return this.getUserListDetail3(location.replace(/^.*?chain=(\w+)(?:&.*$|#.*$|$)/, '$1'), page)
if (location.includes('.html')) {
if (location.includes('zlist.html')) {
let link = location.replace(/^(.*)zlist\.html/, 'https://m3ws.kugou.com/zlist/list')
if (link.includes('pagesize')) {
link = link.replace('pagesize=30', 'pagesize=' + this.listDetailLimit).replace('page=1', 'page=' + page)
} else {
link += `&pagesize=${this.listDetailLimit}&page=${page}`
}
return this.getUserListDetail(link, page, ++retryNum)
} else return this.getUserListDetail3(location.replace(/.+\/(\w+).html(?:\?.*|&.*$|#.*$|$)/, '$1'), page)
}
return this.getUserListDetail(location, page, ++retryNum)
}
if (body.errcode !== 0) return this.getUserListDetail(link, page, ++retryNum)
return this.getUserListDetailByLink(body, link)
},
// 获取列表信息
getListInfo(tagId, tryNum = 0) {
if (this._requestObj_listInfo) this._requestObj_listInfo.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_listInfo = httpFetch(this.getInfoUrl(tagId))
return this._requestObj_listInfo.promise.then(({ body }) => {
if (body.status !== 1) return this.getListInfo(tagId, ++tryNum)
return {
limit: body.data.params.pagesize,
page: body.data.params.p,
total: body.data.params.total,
source: 'kg',
}
})
},
// 获取列表数据
getList(sortId, tagId, page) {
let tasks = [this.getSongList(sortId, tagId, page)]
tasks.push(
this.currentTagInfo.id === tagId
? Promise.resolve(this.currentTagInfo.info)
: this.getListInfo(tagId).then(info => {
this.currentTagInfo.id = tagId
this.currentTagInfo.info = Object.assign({}, info)
return info
}),
)
if (!tagId && page === 1 && sortId === this.sortList[0].id) tasks.push(this.getSongListRecommend()) // 如果是所有类别,则顺便获取推荐列表
return Promise.all(tasks).then(([list, info, recommendList]) => {
if (recommendList) list.unshift(...recommendList)
return {
list,
...info,
}
})
},
// 获取标签
getTags(tryNum = 0) {
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
if (tryNum > 2) return Promise.reject(new Error('try max num'))
this._requestObj_tags = httpFetch(this.getInfoUrl())
return this._requestObj_tags.promise.then(({ body }) => {
if (body.status !== 1) return this.getTags(++tryNum)
return {
hotTag: this.filterInfoHotTag(body.data.hotTag),
tags: this.filterTagInfo(body.data.tagids),
source: 'kg',
}
})
},
getDetailPageUrl(id) {
if (typeof id == 'string') {
if (/^https?:\/\//.test(id)) return id
id = id.replace('id_', '')
}
return `https://www.kugou.com/yy/special/single/${id}.html`
},
search(text, page, limit = 20) {
const params = `userid=1384394652&req_custom=1&appid=1005&req_multi=1&version=11589&page=${page}&filter=0&pagesize=${limit}&order=0&clienttime=1681779443&iscorrection=1&searchsong=0&keyword=${text}&mid=288799920684148686226285199951543865551&dfid=3eSBsO1u97EY1zeIZd40hH4p&clientver=11589&platform=AndroidFilter`
const url = encodeURI(`http://complexsearchretry.kugou.com/v1/search/special?${params}&signature=${signatureParams(params, 'android')}`)
return createHttpFetch(url).then(body => {
// console.log(body)
return {
list: body.lists.map(item => {
return {
play_count: formatPlayCount(item.total_play_count),
id: item.gid ? `gid_${item.gid}` : `id_${item.specialid}`,
author: item.nickname,
name: item.specialname,
time: dateFormat(item.publish_time, 'Y-M-D'),
img: item.img,
grade: item.grade,
desc: item.intro,
total: item.song_count,
source: 'kg',
}
}),
limit,
total: body.total,
source: 'kg',
}
})
// http://msearchretry.kugou.com/api/v3/search/special?version=9209&keyword=%E5%91%A8%E6%9D%B0%E4%BC%A6&pagesize=20&filter=0&page=1&sver=2&with_res_tag=0
// http://ioscdn.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&plat=2&version=7910&correct=1&sver=5
// http://msearchretry.kugou.com/api/v3/search/special?keyword=${encodeURIComponent(text)}&page=${page}&pagesize=${limit}&showtype=10&filter=0&version=7910&sver=2
},
}
// getList
// getTags
// getListDetail
-195
View File
@@ -1,195 +0,0 @@
import { httpFetch } from '../../../request.js'
import { formatPlayTime } from '../../../index.js'
// import { sizeFormate } from '../index.js'
// const boardList = [{ id: 'mg__27553319', name: '咪咕尖叫新歌榜', bangid: '27553319' }, { id: 'mg__27186466', name: '咪咕尖叫热歌榜', bangid: '27186466' }, { id: 'mg__27553408', name: '咪咕尖叫原创榜', bangid: '27553408' }, { id: 'mg__23189800', name: '咪咕港台榜', bangid: '23189800' }, { id: 'mg__23189399', name: '咪咕内地榜', bangid: '23189399' }, { id: 'mg__19190036', name: '咪咕欧美榜', bangid: '19190036' }, { id: 'mg__23189813', name: '咪咕日韩榜', bangid: '23189813' }, { id: 'mg__23190126', name: '咪咕彩铃榜', bangid: '23190126' }, { id: 'mg__15140045', name: '咪咕KTV榜', bangid: '15140045' }, { id: 'mg__15140034', name: '咪咕网络榜', bangid: '15140034' }, { id: 'mg__23217754', name: 'MV榜', bangid: '23217754' }, { id: 'mg__23218151', name: '新专辑榜', bangid: '23218151' }, { id: 'mg__21958042', name: 'iTunes榜', bangid: '21958042' }, { id: 'mg__21975570', name: 'billboard榜', bangid: '21975570' }, { id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815' }, { id: 'mg__22272904', name: '中国TOP排行榜', bangid: '22272904' }, { id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943' }, { id: 'mg__22273437', name: '英国UK榜', bangid: '22273437' }]
const boardList = [
{ id: 'mg__27553319', name: '尖叫新歌榜', bangid: '27553319', webId: 'jianjiao_newsong' },
{ id: 'mg__27186466', name: '尖叫热歌榜', bangid: '27186466', webId: 'jianjiao_hotsong' },
{ id: 'mg__27553408', name: '尖叫原创榜', bangid: '27553408', webId: 'jianjiao_original' },
{ id: 'mg__migumusic', name: '音乐榜', bangid: 'migumusic', webId: 'migumusic' },
{ id: 'mg__movies', name: '影视榜', bangid: 'movies', webId: 'movies' },
{ id: 'mg__23189800', name: '港台榜', bangid: '23189800', webId: 'hktw' },
{ id: 'mg__23189399', name: '内地榜', bangid: '23189399', webId: 'mainland' },
{ id: 'mg__19190036', name: '欧美榜', bangid: '19190036', webId: 'eur_usa' },
{ id: 'mg__23189813', name: '日韩榜', bangid: '23189813', webId: 'jpn_kor' },
{ id: 'mg__23190126', name: '彩铃榜', bangid: '23190126', webId: 'coloring' },
{ id: 'mg__15140045', name: 'KTV榜', bangid: '15140045', webId: 'ktv' },
{ id: 'mg__15140034', name: '网络榜', bangid: '15140034', webId: 'network' },
{ id: 'mg__23217754', name: 'MV榜', bangid: '23217754', webId: 'mv' },
{ id: 'mg__23218151', name: '新专辑榜', bangid: '23218151', webId: 'newalbum' },
{ id: 'mg__21958042', name: '美国iTunes榜', bangid: '21958042', webId: 'itunes' },
{ id: 'mg__21975570', name: '美国billboard榜', bangid: '21975570', webId: 'billboard' },
{ id: 'mg__22272815', name: '台湾Hito中文榜', bangid: '22272815', webId: 'hito' },
{ id: 'mg__22272904', name: '中国TOP排行榜', bangid: '22272904' },
{ id: 'mg__22272943', name: '韩国Melon榜', bangid: '22272943', webId: 'mnet' },
{ id: 'mg__22273437', name: '英国UK榜', bangid: '22273437', webId: 'uk' },
]
// const boardList = [
// { id: 'mg__jianjiao_newsong', bangid: 'jianjiao_newsong', name: '尖叫新歌榜' },
// { id: 'mg__jianjiao_hotsong', bangid: 'jianjiao_hotsong', name: '尖叫热歌榜' },
// { id: 'mg__jianjiao_original', bangid: 'jianjiao_original', name: '尖叫原创榜' },
// { id: 'mg__migumusic', bangid: 'migumusic', name: '音乐榜' },
// { id: 'mg__movies', bangid: 'movies', name: '影视榜' },
// { id: 'mg__mainland', bangid: 'mainland', name: '内地榜' },
// { id: 'mg__hktw', bangid: 'hktw', name: '港台榜' },
// { id: 'mg__eur_usa', bangid: 'eur_usa', name: '欧美榜' },
// { id: 'mg__jpn_kor', bangid: 'jpn_kor', name: '日韩榜' },
// { id: 'mg__coloring', bangid: 'coloring', name: '彩铃榜' },
// { id: 'mg__ktv', bangid: 'ktv', name: 'KTV榜' },
// { id: 'mg__network', bangid: 'network', name: '网络榜' },
// { id: 'mg__newalbum', bangid: 'newalbum', name: '新专辑榜' },
// { id: 'mg__mv', bangid: 'mv', name: 'MV榜' },
// { id: 'mg__itunes', bangid: 'itunes', name: '美国iTunes榜' },
// { id: 'mg__billboard', bangid: 'billboard', name: '美国billboard榜' },
// { id: 'mg__hito', bangid: 'hito', name: 'Hito中文榜' },
// { id: 'mg__mnet', bangid: 'mnet', name: '韩国Melon榜' },
// { id: 'mg__uk', bangid: 'uk', name: '英国UK榜' },
// ]
export default {
limit: 10000,
getUrl(id, page) {
const targetBoard = boardList.find(board => board.bangid == id)
return `https://music.migu.cn/v3/music/top/${targetBoard.webId}`
// return `http://m.music.migu.cn/migu/remoting/cms_list_tag?nid=${id}&pageSize=${this.limit}&pageNo=${page - 1}`
},
successCode: '000000',
requestBoardsObj: null,
regExps: {
listData: /var listData = (\{.+\})<\/script>/,
},
getData(url) {
const requestObj = httpFetch(url)
return requestObj.promise
},
getSinger(singers) {
let arr = []
singers.forEach(singer => {
arr.push(singer.name)
})
return arr.join('、')
},
getIntv(interval) {
if (!interval) return 0
let intvArr = interval.split(':')
let intv = 0
let unit = 1
while (intvArr.length) {
intv += (intvArr.pop()) * unit
unit *= 60
}
return parseInt(intv)
},
formateIntv() {
},
filterData(rawData) {
// console.log(JSON.stringify(rawData))
// console.log(rawData)
let ids = new Set()
const list = []
rawData.forEach(item => {
if (ids.has(item.copyrightId)) return
ids.add(item.copyrightId)
const types = []
const _types = {}
const size = null
types.push({ type: '128k', size })
_types['128k'] = { size }
if (item.hq) {
const size = null
types.push({ type: '320k', size })
_types['320k'] = { size }
}
if (item.sq) {
const size = null
types.push({ type: 'flac', size })
_types.flac = { size }
}
list.push({
singer: this.getSinger(item.singers),
name: item.name,
albumName: item.album && item.album.albumName,
albumId: item.album && item.album.albumId,
songmid: item.id,
copyrightId: item.copyrightId,
source: 'mg',
interval: item.duration ? formatPlayTime(this.getIntv(item.duration)) : null,
img: item.mediumPic ? `https:${item.mediumPic}` : null,
lrc: null,
// lrcUrl: item.lrcUrl,
otherSource: null,
types,
_types,
typeUrl: {},
})
})
return list
},
filterBoardsData(rawList) {
// console.log(rawList)
let list = []
for (const board of rawList) {
if (board.template != 'group1') continue
for (const item of board.itemList) {
if ((item.template != 'row1' && item.template != 'grid1' && !item.actionUrl) || !item.actionUrl.includes('rank-info')) continue
let data = item.displayLogId.param
list.push({
id: 'mg__' + data.rankId,
name: data.rankName,
bangid: String(data.rankId),
})
}
}
return list
},
async getBoards(retryNum = 0) {
// if (++retryNum > 3) return Promise.reject(new Error('try max num'))
// let response
// try {
// response = await this.getBoardsData()
// } catch (error) {
// return this.getBoards(retryNum)
// }
// // console.log(response.body.data.contentItemList)
// if (response.statusCode !== 200 || response.body.code !== this.successCode) return this.getBoards(retryNum)
// const list = this.filterBoardsData(response.body.data.contentItemList)
// // console.log(list)
// // console.log(JSON.stringify(list))
// this.list = list
// return {
// list,
// source: 'mg',
// }
this.list = boardList
return {
list: boardList,
source: 'mg',
}
},
getList(bangid, page, retryNum = 0) {
if (++retryNum > 3) return Promise.reject(new Error('try max num'))
return this.getData(this.getUrl(bangid, page)).then(({ statusCode, body }) => {
if (statusCode !== 200) return this.getList(bangid, page, retryNum)
let listData = body.match(this.regExps.listData)
if (!listData) return this.getList(bangid, page, retryNum)
const datas = JSON.parse(RegExp.$1)
// console.log(datas)
listData = this.filterData(datas.songs.items)
return {
total: datas.songs.itemTotal,
list: this.filterData(datas.songs.items),
limit: this.limit,
page,
source: 'mg',
}
})
},
}
+1 -1
View File
@@ -2,7 +2,7 @@
// 所有平台模块与根模块的类型锚点。类型只描述实际返回的数据形状,不改变运行时行为。
/** 音乐平台标识 */
export type Source = 'kw' | 'kg' | 'tx' | 'wy' | 'mg' | 'bd' | 'xm'
export type Source = 'kw' | 'kg' | 'tx' | 'wy' | 'mg' | 'bd'
/** 音质标识(wav 仅部分接口声明使用) */
export type Quality = '128k' | '320k' | 'flac' | 'flac24bit' | 'wav'
-30
View File
@@ -1,30 +0,0 @@
// 虾米音乐 —— 服务已关停,全部接口返回失败(保留桩实现)
import type { PlatformModule, SongInfo, Quality } from './types.ts'
const xm: PlatformModule = {
comment: {
getComment(_songInfo: SongInfo) {
return Promise.reject(new Error('fail'))
},
getHotComment(_songInfo: SongInfo) {
return Promise.reject(new Error('fail'))
},
},
getMusicUrl(_songInfo: SongInfo, _type: Quality) {
return {
promise: Promise.reject(new Error('fail')),
cancelHttp() {},
}
},
getLyric(_songInfo: SongInfo) {
return {
promise: Promise.reject(new Error('fail')),
cancelHttp() {},
}
},
getPic(_songInfo: SongInfo) {
return Promise.reject(new Error('fail'))
},
}
export default xm