初始提交

This commit is contained in:
lyswhut
2019-08-16 17:27:23 +08:00
parent 4dc34a48f4
commit 14f7bd10a3
112 changed files with 24294 additions and 2 deletions
+93
View File
@@ -0,0 +1,93 @@
import { DownloaderHelper } from 'node-downloader-helper'
// import { pauseResumeTimer } from './util'
import { sizeFormate } from '../index'
import { debugDownload } from '../env'
// these are the default options
// const options = {
// method: 'GET', // Request Method Verb
// // Custom HTTP Header ex: Authorization, User-Agent
// headers: {},
// fileName: '', // Custom filename when saved
// override: false, // if true it will override the file, otherwise will append '(number)' to the end of file
// forceResume: false, // If the server does not return the "accept-ranges" header, can be force if it does support it
// // httpRequestOptions: {}, // Override the http request options
// // httpsRequestOptions: {}, // Override the https request options, ex: to add SSL Certs
// }
export default ({
url,
path,
fileName,
method = 'get',
headers,
override,
forceResume,
// resumeTime = 5000,
onEnd = () => {},
onError = () => {},
onStateChanged = () => {},
onDownload = () => {},
onPause = () => {},
onResume = () => {},
onProgress = () => {},
resumeInfo,
} = {}) => {
const dl = new DownloaderHelper(url, path, {
fileName,
method,
headers,
override,
forceResume,
})
dl.on('end', () => {
onEnd()
debugDownload && console.log('Download Completed')
}).on('error', err => {
onError(err)
dl.resume()
console.log('Download failed, Attempting Retry')
debugDownload && console.error('Something happend', err)
}).on('stateChanged', state => {
onStateChanged(state)
debugDownload && console.log('State: ', state)
}).on('download', () => {
onDownload()
// pauseResumeTimer(dl, resumeTime)
}).on('progress', stats => {
const progress = stats.progress.toFixed(2)
const speed = sizeFormate(stats.speed)
onProgress({
progress,
speed,
downloaded: stats.downloaded,
total: stats.total,
})
if (debugDownload) {
const downloaded = sizeFormate(stats.downloaded)
const total = sizeFormate(stats.total)
console.log(`${speed}/s - ${progress}% [${downloaded}/${total}]`)
}
}).on('pause', () => {
onPause()
debugDownload && console.log('paused')
}).on('resume', () => {
onResume()
debugDownload && console.log('resume')
})
debugDownload && console.log('Downloading: ', url)
if (resumeInfo) {
dl.__total = resumeInfo.totalFileSize // <--- Workaround
// dl.__filePath = resumeInfo.filePath // <--- Workaround
dl.__isResumable = true // <--- Workaround
dl.resume()
} else {
dl.start()
}
return dl
}
+24
View File
@@ -0,0 +1,24 @@
import { DH_STATES } from 'node-downloader-helper'
export const pauseResumeTimer = (_dl, wait) => {
setTimeout(() => {
if (_dl.state === DH_STATES.FINISHED || _dl.state === DH_STATES.FAILED) {
return
}
_dl
.pause()
.then(() => console.log(`Paused for ${wait / 1000} seconds`))
.then(() =>
setTimeout(() => {
if (!_dl.isResumable()) {
console.warn(
"This URL doesn't support resume, it will start from the beginning"
)
}
return _dl.resume()
}, wait)
)
}, wait)
}
+5
View File
@@ -0,0 +1,5 @@
const isDev = process.env.NODE_ENV === 'development'
export const debug = isDev && true
export const debugRequest = isDev && false
export const debugDownload = isDev && false
+196
View File
@@ -0,0 +1,196 @@
import fs from 'fs'
import { shell, remote } from 'electron'
import path from 'path'
import os from 'os'
/**
* 获取两个数之间的随机整数,大于等于min,小于max
* @param {*} min
* @param {*} max
*/
export const getRandom = (min, max) => Math.floor(Math.random() * (max - min)) + min
export const sizeFormate = size => {
// https://gist.github.com/thomseddon/3511330
if (!size) return '0 b'
let units = ['b', 'kB', 'MB', 'GB', 'TB']
let number = Math.floor(Math.log(size) / Math.log(1024))
return `${(size / Math.pow(1024, Math.floor(number))).toFixed(2)} ${units[number]}`
}
export const formatPlayTime = time => {
let m = parseInt(time / 60)
let s = parseInt(time % 60)
return m === 0 && s === 0 ? '--/--' : (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s)
}
export const formatPlayTime2 = time => {
let m = parseInt(time / 60)
let s = parseInt(time % 60)
return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s)
}
export const b64DecodeUnicode = str => {
// Going backwards: from bytestream, to percent-encoding, to original string.
return decodeURIComponent(window.atob(str).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
}).join(''))
}
export const decodeName = str => str.replace(/&apos;/g, '\'')
export const scrollTo = (element, to, duration = 300, fn = function() {}) => {
const start = element.scrollTop || element.scrollY
const change = to - start
const increment = 10
if (!change) {
fn()
return
}
let currentTime = 0; let val
const easeInOutQuad = (t, b, c, d) => {
t /= d / 2
if (t < 1) return (c / 2) * t * t + b
t--
return (-c / 2) * (t * (t - 2) - 1) + b
}
const animateScroll = () => {
currentTime += increment
val = parseInt(easeInOutQuad(currentTime, start, change, duration))
if (element.scrollTo) {
element.scrollTo(0, val)
} else {
element.scrollTop = val
}
if (currentTime < duration) {
setTimeout(animateScroll, increment)
} else {
fn()
}
}
animateScroll()
}
/**
* 检查路径是否存在
* @param {*} path
*/
export const checkPath = path => fs.existsSync(path)
/**
* 在资源管理器中打开目录
* @param {*} 选项
*/
export const openSelectDir = options => remote.dialog.showOpenDialog(remote.getCurrentWindow(), options)
/**
* 在资源管理器中打开目录
* @param {*} 选项
*/
export const openSaveDir = options => remote.dialog.showSaveDialog(remote.getCurrentWindow(), options)
/**
* 在资源管理器中打开目录
* @param {*} dir
*/
export const openDirInExplorer = dir => {
shell.showItemInFolder(dir)
}
export const checkVersion = (currentVer, targetVer) => {
// console.log(currentVer)
// console.log(targetVer)
currentVer = currentVer.split('.')
targetVer = targetVer.split('.')
let maxLen = Math.max(currentVer.length, targetVer.length)
if (currentVer.length < maxLen) {
for (let index = 0, len = maxLen - currentVer.length; index < len; index++) {
currentVer.push(0)
}
}
if (targetVer.length < maxLen) {
for (let index = 0, len = maxLen - targetVer.length; index < len; index++) {
targetVer.push(0)
}
}
for (let index = 0; index < currentVer.length; index++) {
if (parseInt(currentVer[index]) < parseInt(targetVer[index])) return true
if (parseInt(currentVer[index]) > parseInt(targetVer[index])) return false
}
return false
}
export const isObject = item => item && typeof item === 'object' && !Array.isArray(item)
/**
* 对象深度合并
* 注意:循环引用的对象会出现死循环
* @param {} target 要合并源对象
* @param {} source 要合并目标对象
*/
export const objectDeepMerge = (target, source) => {
let base = {}
Object.keys(source).forEach(item => {
if (Array.isArray(source[item])) {
let arr = Array.isArray(target[item]) ? target[item] : []
target[item] = arr.concat(source[item])
return
} else if (isObject(source[item])) {
if (!isObject(target[item])) target[item] = {}
objectDeepMerge(target[item], source[item])
return
}
base[item] = source[item]
})
Object.assign(target, base)
}
/**
* 判断是否父子元素
* @param {*} parent
* @param {*} children
*/
export const isChildren = (parent, children) => {
return children.parentNode ? children.parentNode === parent ? true : isChildren(parent, children.parentNode) : false
}
export const updateSetting = setting => {
const defaultVersion = '1.0.1'
const defaultSetting = {
version: defaultVersion,
player: {
togglePlayMethod: 'listLoop',
highQuality: false,
},
list: {
isShowAlbumName: true,
},
download: {
savePath: path.join(os.homedir(), 'Desktop'),
fileName: '歌名 - 歌手',
},
leaderboard: {
source: 'kw',
tabId: 'kwbiaosb',
},
themeId: 0,
sourceId: 'kw',
randomAnimate: true,
}
const overwriteSetting = {
version: defaultVersion,
sourceId: 'kw',
}
if (!setting) {
setting = defaultSetting
} else if (checkVersion(setting.version, defaultSetting.version)) {
objectDeepMerge(defaultSetting, setting)
objectDeepMerge(defaultSetting, overwriteSetting)
setting = defaultSetting
}
return setting
}
+28
View File
@@ -0,0 +1,28 @@
import kw from './kw'
import kg from './kg'
import tx from './tx'
import wy from './wy'
export default {
sources: [
{
name: '酷我音乐',
id: 'kw',
},
{
name: '酷狗音乐',
id: 'kg',
},
{
name: 'QQ音乐',
id: 'tx',
},
{
name: '网易音乐',
id: 'wy',
},
],
kw,
kg,
tx,
wy,
}
+7
View File
@@ -0,0 +1,7 @@
import leaderboard from './leaderboard'
const kg = {
leaderboard,
}
export default kg
+125
View File
@@ -0,0 +1,125 @@
import { httpGet, cancelHttp } from '../../request'
import { formatPlayTime } from '../../index'
export default {
list: [
{
id: 'kgtop500',
name: '酷狗TOP500',
bangid: '8888',
},
{
id: 'kgwlhgb',
name: '网络红歌榜',
bangid: '23784',
},
{
id: 'kgbsb',
name: '飙升榜',
bangid: '6666',
},
{
id: 'kgfxb',
name: '分享榜',
bangid: '21101',
},
{
id: 'kgcyyb',
name: '纯音乐榜',
bangid: '33164',
},
{
id: 'kggfjqb',
name: '古风金曲榜',
bangid: '33161',
},
{
id: 'kgyyjqb',
name: '粤语金曲榜',
bangid: '33165',
},
{
id: 'kgomjqb',
name: '欧美金曲榜',
bangid: '33166',
},
// {
// id: 'kgdyrgb',
// name: '电音热歌榜',
// bangid: '33160',
// },
// {
// id: 'kgjdrgb',
// name: 'DJ热歌榜',
// bangid: '24971',
// },
// {
// id: 'kghyxgb',
// name: '华语新歌榜',
// bangid: '31308',
// },
],
getUrl(p, id) {
return `http://www2.kugou.kugou.com/yueku/v9/rank/home/${p}-${id}.html`
},
regExps: {
total: /total: '(\d+)',/,
page: /page: '(\d+)',/,
limit: /pagesize: '(\d+)',/,
listData: /global\.features = (\[.+\]);/,
},
_cancelIndex: null,
_cancelPromiseCancelFn: null,
getData(url) {
if (this._cancelIndex != null) {
cancelHttp(this._cancelIndex)
this._cancelPromiseCancelFn(new Error('取消http请求'))
}
return new Promise((resolve, reject) => {
this._cancelPromiseCancelFn = reject
this._cancelIndex = httpGet(url, (err, resp, body) => {
this._cancelIndex = null
this._cancelPromiseCancelFn = null
if (err) {
console.log(err)
reject(err)
}
resolve(body)
})
})
},
filterData(rawList) {
return rawList.map(item => ({
singer: item.singername,
name: item.songname,
albumName: item.album_name,
albumId: item.album_id,
songmid: item.audio_id,
source: 'kg',
interval: formatPlayTime(item.duration / 1000),
img: null,
lrc: null,
typeUrl: {},
}))
},
getList(id, page) {
let type = this.list.find(s => s.id === id)
if (!type) return Promise.reject()
return this.getData(this.getUrl(page, type.bangid)).then(html => {
let total = html.match(this.regExps.total)
if (total) total = parseInt(RegExp.$1)
page = html.match(this.regExps.page)
if (page) page = parseInt(RegExp.$1)
let limit = html.match(this.regExps.limit)
if (limit) limit = parseInt(RegExp.$1)
let listData = html.match(this.regExps.listData)
if (listData) listData = this.filterData(JSON.parse(RegExp.$1))
return {
total,
list: listData,
limit,
page,
}
})
},
}
+121
View File
@@ -0,0 +1,121 @@
import { httpGet, cancelHttp } from '../../request'
import tempSearch from './tempSearch'
import musicSearch from './musicSearch'
import { formatSinger } from './util'
import leaderboard from './leaderboard'
import lyric from './lyric'
const kw = {
_musicInfoIndex: null,
_musicInfoPromiseCancelFn: null,
_musicPicIndex: null,
_musicPicPromiseCancelFn: null,
// context: null,
// init(context) {
// if (this.isInited) return
// this.isInited = true
// this.context = context
// // this.musicSearch.search('我又想你了').then(res => {
// // console.log(res)
// // })
// // this.getMusicUrl('62355680', '320k').then(url => {
// // console.log(url)
// // })
// },
tempSearch,
musicSearch,
leaderboard,
getLyric(songInfo) {
// let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer
return lyric.getLyric(songInfo.songmid)
},
handleMusicInfo(songInfo) {
return this.getMusicInfo(songInfo).then(info => {
// console.log(JSON.stringify(info))
songInfo.name = info.name
songInfo.singer = formatSinger(info.artist)
songInfo.img = info.pic
songInfo.albumName = info.album
return songInfo
// return Object.assign({}, songInfo, {
// name: info.name,
// singer: formatSinger(info.artist),
// img: info.pic,
// albumName: info.album,
// })
})
},
getMusicUrl(songInfo, type) {
return new Promise((resolve, reject) => {
httpGet(`https://v1.itooi.cn/kuwo/url?id=${songInfo.songmid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, (err, resp, body) => {
if (err) {
console.log(err)
return this.getMusicUrl(songInfo, type)
}
body.code === 200 ? resolve({ type, url: body.data }) : reject(new Error(body.msg))
})
})
},
getMusicInfo(songInfo) {
if (this._musicInfoIndex != null) {
cancelHttp(this._musicInfoIndex)
this._musicInfoPromiseCancelFn(new Error('取消http请求'))
}
return new Promise((resolve, reject) => {
this._musicInfoPromiseCancelFn = reject
this._musicInfoIndex = httpGet(`http://www.kuwo.cn/api/www/music/musicInfo?mid=${songInfo.songmid}`, (err, resp, body) => {
this._musicInfoIndex = null
this._musicInfoPromiseCancelFn = null
if (err) {
console.log(err)
reject(err)
}
body.code === 200 ? resolve(body.data) : reject(new Error(body.msg))
})
})
},
getMusicUrls(musicInfo, cb) {
let tasks = []
let songId = musicInfo.songmid
musicInfo.types.forEach(type => {
tasks.push(kw.getMusicUrl(songId, type.type))
})
Promise.all(tasks).then(urlInfo => {
let typeUrl = {}
urlInfo.forEach(info => {
typeUrl[info.type] = info.url
})
cb(typeUrl)
})
},
getPic(songInfo) {
if (this._musicPicIndex != null) {
cancelHttp(this._musicPicIndex)
this._musicPicPromiseCancelFn(new Error('取消http请求'))
}
return new Promise((resolve, reject) => {
this._musicPicPromiseCancelFn = reject
this._musicPicIndex = httpGet(`https://v1.itooi.cn/kuwo/pic?id=${songInfo.songmid}&isRedirect=0`, (err, resp, body) => {
this._musicPicIndex = null
this._musicPicPromiseCancelFn = null
if (err) {
console.log(err)
reject(err)
}
console.log(body)
body.code === 200 ? resolve(body.data) : reject(new Error(body.msg))
})
})
},
}
export default kw
+190
View File
@@ -0,0 +1,190 @@
import { httpGet, cancelHttp } from '../../request'
import { formatPlayTime } from '../../index'
export default {
list: [
{
id: 'kwbiaosb',
name: '飙升榜',
bangid: 93,
},
{
id: 'kwregb',
name: '热歌榜',
bangid: 16,
},
{
id: 'kwhuiyb',
name: '会员榜',
bangid: 145,
},
{
id: 'kwdouyb',
name: '抖音榜',
bangid: 158,
},
{
id: 'kwqsb',
name: '趋势榜',
bangid: 187,
},
{
id: 'kwhuaijb',
name: '怀旧榜',
bangid: 26,
},
{
id: 'kwhuayb',
name: '华语榜',
bangid: 104,
},
{
id: 'kwyueyb',
name: '粤语榜',
bangid: 182,
},
{
id: 'kwoumb',
name: '欧美榜',
bangid: 22,
},
{
id: 'kwhanyb',
name: '韩语榜',
bangid: 184,
},
{
id: 'kwriyb',
name: '日语榜',
bangid: 183,
},
],
getUrl: (p, l, id) => `http://kbangserver.kuwo.cn/ksong.s?from=pc&fmt=json&pn=${p - 1}&rn=${l}&type=bang&data=content&id=${id}&show_copyright_off=0&pcmp4=1&isbang=1`,
getUrl2: (p, l, id) => `http://www.kuwo.cn/api/www/bang/bang/musicList?bangId=${id}&pn=${p}&rn=${l}`,
regExps: {
},
limit: 30,
_cancelIndex: null,
_cancelPromiseCancelFn: null,
_cancelIndex2: null,
_cancelPromiseCancelFn2: null,
getData(url) {
if (this._cancelIndex != null) {
cancelHttp(this._cancelIndex)
this._cancelPromiseCancelFn(new Error('取消http请求'))
}
return new Promise((resolve, reject) => {
this._cancelPromiseCancelFn = reject
this._cancelIndex = httpGet(url, (err, resp, body) => {
this._cancelIndex = null
this._cancelPromiseCancelFn = null
if (err) {
console.log(err)
reject(err)
}
resolve(body)
})
})
},
getData2(url) {
if (this._cancelIndex2 != null) {
cancelHttp(this._cancelIndex2)
this._cancelPromiseCancelFn2(new Error('取消http请求'))
}
return new Promise((resolve, reject) => {
this._cancelPromiseCancelFn2 = reject
this._cancelIndex2 = httpGet(url, (err, resp, body) => {
this._cancelIndex2 = null
this._cancelPromiseCancelFn2 = null
if (err) {
console.log(err)
reject(err)
}
resolve(body)
})
})
},
filterData(rawList, rawList2) {
return rawList.map((item, inedx) => {
let formats = item.formats.split('|')
let types = []
let _types = {}
if (formats.indexOf('MP3128')) {
types.push({ type: '128k', size: null })
_types['128k'] = {
size: null,
}
}
if (formats.indexOf('MP3192')) {
types.push({ type: '192k', size: null })
_types['192k'] = {
size: null,
}
}
if (formats.indexOf('MP3H')) {
types.push({ type: '320k', size: null })
_types['320k'] = {
size: null,
}
}
if (formats.indexOf('AL')) {
types.push({ type: 'ape', size: null })
_types['ape'] = {
size: null,
}
}
if (formats.indexOf('ALFLAC')) {
types.push({ type: 'flac', size: null })
_types['flac'] = {
size: null,
}
}
types.reverse()
return {
singer: item.artist,
name: item.name,
albumName: item.album,
albumId: item.albumid,
songmid: item.id,
source: 'kw',
interval: formatPlayTime(rawList2[inedx].duration),
img: item.pic,
lrc: null,
types,
_types,
typeUrl: {},
}
})
},
loadData(p1, p2, page, bangid) {
return Promise.all([p1, p2]).then(([data1, data2]) => {
// console.log(data1, data2)
if (!data1.musiclist.length) {
return this.loadData(this.getData(this.getUrl(page, this.limit, bangid)),
data2.data.musicList.length
? Promise.resolve(data2)
: this.getData2(this.getUrl2(page, this.limit, bangid)), page, bangid)
}
if (!data2.data.musicList.length) {
return this.loadData(Promise.resolve(data1), this.getData2(this.getUrl2(page, this.limit, bangid)), page, bangid)
}
return Promise.resolve([data1, data2])
})
},
getList(id, page) {
let type = this.list.find(s => s.id === id)
if (!type) return Promise.reject()
return this.loadData(this.getData(this.getUrl(page, this.limit, type.bangid)), this.getData2(this.getUrl2(page, this.limit, type.bangid)), page, type.bangid).then(([data1, data2]) => {
// console.log(data1.musiclist, data2.data)
let total = parseInt(data1.num)
let list = this.filterData(data1.musiclist, data2.data.musicList)
return {
total,
list,
limit: this.limit,
page,
}
})
},
}
+31
View File
@@ -0,0 +1,31 @@
import { httpGet, cancelHttp } from '../../request'
export default {
_musicLrcIndex: null,
_musicLrcPromiseCancelFn: null,
formatTime(time) {
let m = parseInt(time / 60)
let s = (time % 60).toFixed(2)
return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s)
},
getLyric(songId) {
if (this._musicLrcIndex != null) {
cancelHttp(this._musicLrcIndex)
this._musicLrcPromiseCancelFn(new Error('取消http请求'))
}
return new Promise((resolve, reject) => {
this._musicLrcPromiseCancelFn = reject
this._musicLrcIndex = httpGet(`https://v1.itooi.cn/kuwo/lrc?id=${songId}`, (err, resp, body) => {
this._musicLrcIndex = null
this._musicLrcPromiseCancelFn = null
if (err) {
console.log(err)
reject(err)
}
// console.log(body.data)
// console.log(this.transformLrc(body.data))
resolve(body)
})
})
},
}
+144
View File
@@ -0,0 +1,144 @@
// import '../../polyfill/array.find'
// import jshtmlencode from 'js-htmlencode'
import { httpGet, cancelHttp } from '../../request'
import { formatPlayTime, decodeName } from '../../index'
// import { debug } from '../../utils/env'
import { formatSinger } from './util'
export default {
regExps: {
mInfo: /bitrate:(\d+),format:(\w+),size:([\w.]+)/,
},
_musicSearchIndex: null,
_musicSearchPromiseCancelFn: null,
limit: 30,
total: 0,
page: 0,
allPage: 1,
// cancelFn: null,
musicSearch(str, page) {
if (this._musicSearchIndex != null) {
cancelHttp(this._musicSearchIndex)
this._musicSearchPromiseCancelFn(new Error('取消http请求'))
}
return new Promise((resolve, reject) => {
this._musicSearchPromiseCancelFn = reject
this._musicSearchIndex = httpGet(`http://search.kuwo.cn/r.s?client=kt&all=${encodeURIComponent(str)}&pn=${page - 1}&rn=${this.limit}&uid=794762570&ver=kwplayer_ar_9.2.2.1&vipver=1&show_copyright_off=1&newver=1&ft=music&cluster=0&strategy=2012&encoding=utf8&rformat=json&vermerge=1&mobi=1&issubtitle=1`, (err, resp, body) => {
this._musicSearchIndex = null
this._musicSearchPromiseCancelFn = null
if (err) {
console.log(err)
reject(err)
}
resolve(body)
})
})
},
// getImg(songId) {
// return httpGet(`http://player.kuwo.cn/webmusic/sj/dtflagdate?flag=6&rid=MUSIC_${songId}`)
// },
// getLrc(songId) {
// return httpGet(`http://mobile.kuwo.cn/mpage/html5/songinfoandlrc?mid=${songId}&flag=0`)
// },
handleResult(rawData) {
const result = []
for (let i = 0; i < rawData.length; i++) {
const info = rawData[i]
let songId = info.MUSICRID.replace('MUSIC_', '')
// const format = (info.FORMATS || info.formats).split('|')
if (!info.MINFO) {
console.log('mInfo is undefined')
return null
}
const types = []
const _types = {}
let infoArr = info.MINFO.split(';')
infoArr.forEach(info => {
info = info.match(this.regExps.mInfo)
if (info) {
switch (info[2]) {
case 'flac':
types.push({ type: 'flac', size: info[3] })
_types.flac = {
size: info[3].toLocaleUpperCase(),
}
break
case 'ape':
types.push({ type: 'ape', size: info[3] })
_types.ape = {
size: info[3].toLocaleUpperCase(),
}
break
case 'mp3':
switch (info[1]) {
case '320':
types.push({ type: '320k', size: info[3] })
_types['320k'] = {
size: info[3].toLocaleUpperCase(),
}
break
case '192':
types.push({ type: '192k', size: info[3] })
_types['192k'] = {
size: info[3].toLocaleUpperCase(),
}
break
case '128':
types.push({ type: '128k', size: info[3] })
_types['128k'] = {
size: info[3].toLocaleUpperCase(),
}
break
}
break
}
}
})
types.reverse()
let interval = parseInt(info.DURATION)
result.push({
name: decodeName(info.SONGNAME),
singer: formatSinger(decodeName(info.ARTIST)),
source: 'kw',
// img = (info.album.name === '' || info.album.name === '空')
// ? `http://player.kuwo.cn/webmusic/sj/dtflagdate?flag=6&rid=MUSIC_160911.jpg`
// : `https://y.gtimg.cn/music/photo_new/T002R500x500M000${info.album.mid}.jpg`
songmid: songId,
albumId: decodeName(info.ALBUMID || ''),
interval: Number.isNaN(interval) ? 0 : formatPlayTime(interval),
albumName: info.ALBUM ? decodeName(info.ALBUM) : '',
lyric: null,
img: null,
types,
_types,
typeUrl: {},
})
}
return result
},
search(str, page = 1, { limit }) {
if (limit != null) this.limit = limit
// http://newlyric.kuwo.cn/newlyric.lrc?62355680
return this.musicSearch(str, page).then(result => {
if (!result || (result.TOTAL !== '0' && result.SHOW === '0')) return this.search(str, page, { limit })
let list = this.handleResult(result.abslist)
if (list == null) return this.search(str, page, { limit })
this.total = parseInt(result.TOTAL)
this.page = page
this.allPage = Math.ceil(this.total / this.limit)
return Promise.resolve({
list,
allPage: this.allPage,
total: this.total,
})
})
},
}
+43
View File
@@ -0,0 +1,43 @@
import { httpGet, cancelHttp } from '../../request'
import { decodeName } from '../../index'
export default {
regExps: {
relWord: /RELWORD=(.+)/,
},
_musicTempSearchIndex: null,
_musicTempSearchPromiseCancelFn: null,
tempSearch(str) {
if (this._musicTempSearchIndex != null) {
cancelHttp(this._musicTempSearchIndex)
this._musicTempSearchPromiseCancelFn(new Error('取消http请求'))
}
return new Promise((resolve, reject) => {
this._musicTempSearchPromiseCancelFn = reject
this._musicTempSearchIndex = httpGet(`http://www.kuwo.cn/api/www/search/searchKey?key=${encodeURIComponent(str)}`, (err, resp, body) => {
this._musicTempSearchIndex = null
this._musicTempSearchPromiseCancelFn = null
if (err) {
console.log(err)
reject(err)
}
resolve(body)
})
})
},
handleResult(rawData) {
return rawData.map(info => {
let matchResult = info.match(this.regExps.relWord)
return matchResult ? decodeName(matchResult[1]) : ''
})
},
cancelTempSearch() {
if (this._musicTempSearchIndex != null) {
cancelHttp(this._musicTempSearchIndex)
this._musicTempSearchPromiseCancelFn(new Error('取消http请求'))
}
},
search(str) {
return this.tempSearch(str).then(result => this.handleResult(result.data))
},
}
+2
View File
@@ -0,0 +1,2 @@
export const formatSinger = rawData => rawData.replace(/&/g, '、')
+7
View File
@@ -0,0 +1,7 @@
import leaderboard from './leaderboard'
const tx = {
leaderboard,
}
export default tx
+203
View File
@@ -0,0 +1,203 @@
import { httpGet, cancelHttp } from '../../request'
import { formatPlayTime, sizeFormate } from '../../index'
export default {
limit: 300,
list: [
{
id: 'txlxzsb',
name: '流行榜',
bangid: 4,
},
{
id: 'txrgb',
name: '热歌榜',
bangid: 26,
},
{
id: 'txwlhgb',
name: '网络榜',
bangid: 28,
},
{
id: 'txdyb',
name: '抖音榜',
bangid: 60,
},
{
id: 'txndb',
name: '内地榜',
bangid: 5,
},
{
id: 'txxgb',
name: '香港榜',
bangid: 59,
},
{
id: 'txtwb',
name: '台湾榜',
bangid: 61,
},
{
id: 'txoumb',
name: '欧美榜',
bangid: 3,
},
{
id: 'txhgb',
name: '韩国榜',
bangid: 16,
},
{
id: 'txrbb',
name: '日本榜',
bangid: 17,
},
// {
// id: 'txtybb',
// name: 'YouTube榜',
// bangid: 128,
// },
],
getUrl(id, period, limit) {
return `https://u.y.qq.com/cgi-bin/musicu.fcg?format=json&inCharset=utf8&outCharset=utf-8&platform=yqq.json&needNewCode=0&data=${encodeURIComponent(JSON.stringify({
comm: {
cv: 1602,
ct: 20,
},
toplist: {
module: 'musicToplist.ToplistInfoServer',
method: 'GetDetail',
param: {
topid: id,
num: limit,
period,
},
},
}))}`
},
regExps: {
periodList: /<i class="play_cover__btn c_tx_link js_icon_play" data-listkey=".+?" data-listname=".+?" data-tid=".+?" data-date=".+?" .+?<\/i>/g,
period: /data-listname="(.+?)" data-tid=".*?\/(.+?)" data-date="(.+?)" .+?<\/i>/,
},
periods: {},
periodUrl: 'https://c.y.qq.com/node/pc/wk_v15/top.html',
_cancelIndex: null,
_cancelPromiseCancelFn: null,
getData(url) {
if (this._cancelIndex != null) {
cancelHttp(this._cancelIndex)
this._cancelPromiseCancelFn(new Error('取消http请求'))
}
return new Promise((resolve, reject) => {
this._cancelPromiseCancelFn = reject
this._cancelIndex = httpGet(url, (err, resp, body) => {
this._cancelIndex = null
this._cancelPromiseCancelFn = null
if (err) {
console.log(err)
reject(err)
}
resolve(body)
})
})
},
getSinger(singers) {
let arr = []
singers.forEach(singer => {
arr.push(singer.name)
})
return arr.join('、')
},
filterData(rawList) {
// console.log(rawList)
return rawList.map(item => {
let types = []
let _types = {}
if (item.file.size_128mp3 !== 0) {
let size = sizeFormate(item.file.size_128mp3)
types.push({ type: '128k', size })
_types['128k'] = {
size,
}
}
if (item.file.size_320mp3 !== 0) {
let size = sizeFormate(item.file.size_320mp3)
types.push({ type: '320k', size })
_types['320k'] = {
size,
}
}
if (item.file.size_ape !== 0) {
let size = sizeFormate(item.file.size_ape)
types.push({ type: 'ape', size })
_types.ape = {
size,
}
}
if (item.file.size_flac !== 0) {
let size = sizeFormate(item.file.size_flac)
types.push({ type: 'flac', size })
_types.flac = {
size,
}
}
types.reverse()
return {
singer: this.getSinger(item.singer),
name: item.title,
albumName: item.album.title,
albumId: item.album.mid,
source: 'tx',
interval: formatPlayTime(item.interval),
songId: item.id,
albumMid: item.album.mid,
strMediaMid: item.file.media_mid,
songmid: item.mid,
img: (item.album.name === '' || item.album.name === '空')
? `https://y.gtimg.cn/music/photo_new/T001R500x500M000${item.singer[0].mid}.jpg`
: `https://y.gtimg.cn/music/photo_new/T002R500x500M000${item.album.mid}.jpg`,
lrc: null,
types,
_types,
typeUrl: {},
}
})
},
getPeriods(bangid) {
return this.getData(this.periodUrl).then(html => {
let result = html.match(this.regExps.periodList)
if (!result) return Promise.reject()
result.forEach(item => {
let result = item.match(this.regExps.period)
if (!result) return
this.periods[result[2]] = {
name: result[1],
bangid: result[2],
period: result[3],
}
})
const info = this.periods[bangid]
return info && info.period
})
},
getList(id, page) {
let type = this.list.find(s => s.id === id)
if (!type) return Promise.reject()
let info = this.periods[type.bangid]
let p = info ? Promise.resolve(info.period) : this.getPeriods(type.bangid)
return p.then(period => {
return this.getData(this.getUrl(type.bangid, period, this.limit)).then(data => {
// console.log(data)
if (data.code !== 0) return Promise.reject()
return {
total: data.toplist.data.songInfoList.length,
list: this.filterData(data.toplist.data.songInfoList),
limit: this.limit,
page: 1,
}
})
})
},
}
+7
View File
@@ -0,0 +1,7 @@
import leaderboard from './leaderboard'
const wy = {
leaderboard,
}
export default wy
+123
View File
@@ -0,0 +1,123 @@
import { httpGet, cancelHttp } from '../../request'
import { formatPlayTime } from '../../index'
export default {
limit: 300,
list: [
{
id: 'wybsb',
name: '飙升榜',
bangid: '19723756',
},
{
id: 'wyrgb',
name: '热歌榜',
bangid: '3778678',
},
{
id: 'wyxgb',
name: '新歌榜',
bangid: '3779629',
},
{
id: 'wyycb',
name: '原创榜',
bangid: '2884035',
},
{
id: 'wygdb',
name: '古典榜',
bangid: '71384707',
},
{
id: 'wydouyb',
name: '抖音榜',
bangid: '2250011882',
},
{
id: 'wyhyb',
name: '韩语榜',
bangid: '745956260',
},
{
id: 'wydianyb',
name: '电音榜',
bangid: '1978921795',
},
{
id: 'wydjb',
name: '电竞榜',
bangid: '2006508653',
},
{
id: 'wyktvbb',
name: 'KTV唛榜',
bangid: '21845217',
},
],
getUrl(id) {
return `https://music.163.com/discover/toplist?id=${id}`
},
regExps: {
list: /<textarea id="song-list-pre-data" style="display:none;">(.+?)<\/textarea>/,
},
_cancelIndex: null,
_cancelPromiseCancelFn: null,
getData(url) {
if (this._cancelIndex != null) {
cancelHttp(this._cancelIndex)
this._cancelPromiseCancelFn(new Error('取消http请求'))
}
return new Promise((resolve, reject) => {
this._cancelPromiseCancelFn = reject
this._cancelIndex = httpGet(url, (err, resp, body) => {
this._cancelIndex = null
this._cancelPromiseCancelFn = null
if (err) {
console.log(err)
reject(err)
}
resolve(body)
})
})
},
getSinger(singers) {
let arr = []
singers.forEach(singer => {
arr.push(singer.name)
})
return arr.join('、')
},
filterData(rawList) {
// console.log(rawList)
return rawList.map(item => {
return {
singer: this.getSinger(item.artists),
name: item.name,
albumName: item.album.name,
albumId: item.album.id,
source: 'wy',
interval: formatPlayTime(item.duration / 1000),
songmid: item.id,
img: item.album.picUrl,
lrc: null,
typeUrl: {},
}
})
},
getList(id, page) {
let type = this.list.find(s => s.id === id)
if (!type) return Promise.reject()
return this.getData(this.getUrl(type.bangid)).then(html => {
let result = html.match(this.regExps.list)
if (!result) return Promise.reject()
const list = this.filterData(JSON.parse(RegExp.$1))
return {
total: list.length,
list,
limit: this.limit,
page,
}
})
},
}
+175
View File
@@ -0,0 +1,175 @@
import request from 'request'
// import progress from 'request-progress'
import { debugRequest } from './env'
// import fs from 'fs'
const tasks = []
const fatchData = (url, method, options, callback) => {
let index = pushTask(tasks, request(url, {
method,
headers: options.headers,
Origin: options.origin,
data: options.data,
json: options.format === undefined || options.format === 'json',
}, (err, resp, body) => {
tasks[index] = null
if (err) return callback(err, null)
callback(null, resp, body)
}))
return index
}
/**
* 取消请求
* @param {*} index
*/
export const cancelHttp = index => {
if (index == null) return
console.log('cancel: ', index)
let r = tasks[index]
if (r == null) return
r.abort()
tasks[index] = null
}
const pushTask = (tasks, newTask) => {
for (const [index, task] of tasks.entries()) {
if (task == null) {
tasks[index] = newTask
return index
}
}
tasks.push(newTask)
return tasks.length - 1
}
/**
* http 请求
* @param {*} url 地址
* @param {*} options 选项
* @param {*} cb 回调
* @return {Number} index 用于取消请求
*/
export const http = (url, options, cb) => {
if (typeof options === 'function') {
cb = options
options = {}
}
// 默认选项
if (options.method == null) options.method = 'get'
debugRequest && console.log(`\n---send request------${url}------------`)
return fatchData(url, options.method, options, (err, resp, body) => {
// options.isShowProgress && window.api.hideProgress()
debugRequest && console.log(`\n---response------${url}------------`)
debugRequest && console.log(JSON.stringify(body))
if (err) {
debugRequest && console.log(JSON.stringify(err))
}
cb(err, resp, body)
})
}
/**
* http get 请求
* @param {*} url 地址
* @param {*} options 选项
* @param {*} callback 回调
* @return {Number} index 用于取消请求
*/
export const httpGet = (url, options, callback) => {
if (typeof options === 'function') {
callback = options
options = {}
}
// options.isShowProgress && window.api.showProgress({
// title: options.progressMsg || '请求中',
// modal: true,
// })
debugRequest && console.log(`\n---send request-------${url}------------`)
return fatchData(url, 'get', options, function(err, resp, body) {
// options.isShowProgress && window.api.hideProgress()
debugRequest && console.log(`\n---response------${url}------------`)
debugRequest && console.log(JSON.stringify(body))
if (err) {
debugRequest && console.log(JSON.stringify(err))
}
callback(err, resp, body)
})
}
/**
* http post 请求
* @param {*} url 请求地址
* @param {*} data 提交的数据
* @param {*} options 选项
* @param {*} callback 回调
* @return {Number} index 用于取消请求
*/
export const httpPost = (url, data, options, callback) => {
if (typeof options === 'function') {
callback = options
options = {}
}
// options.isShowProgress && window.api.showProgress({
// title: options.progressMsg || '请求中',
// modal: true,
// })
options.data = data
debugRequest && console.log(`\n---send request-------${url}------------`)
return fatchData(url, 'post', options, function(err, resp, body) {
// options.isShowProgress && window.api.hideProgress()
debugRequest && console.log(`\n---response------${url}------------`)
debugRequest && console.log(JSON.stringify(body))
if (err) {
debugRequest && console.log(JSON.stringify(err))
}
callback(err, resp, body)
})
}
/**
* http jsonp 请求
* @param {*} url 请求地址
* @param {*} options 选项
* options.jsonpCallback 回调
* @param {*} callback 回调
* @return {Number} index 用于取消请求
*/
export const http_jsonp = (url, options, callback) => {
if (typeof options === 'function') {
callback = options
options = {}
}
let jsonpCallback = 'jsonpCallback'
if (url.indexOf('?') < 0) url += '?'
url += `&${options.jsonpCallback}=${jsonpCallback}`
options.format = 'script'
// options.isShowProgress && window.api.showProgress({
// title: options.progressMsg || '请求中',
// modal: true,
// })
debugRequest && console.log(`\n---send request-------${url}------------`)
return fatchData(url, 'get', options, function(err, resp, body) {
// options.isShowProgress && window.api.hideProgress()
debugRequest && console.log(`\n---response------${url}------------`)
debugRequest && console.log(JSON.stringify(body))
if (err) {
debugRequest && console.log(JSON.stringify(err))
} else {
body = JSON.parse(body.replace(new RegExp(`^${jsonpCallback}\\(({.*})\\)$`), '$1'))
}
callback(err, resp, body)
})
}