You've already forked lx-music-desktop
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21f5ed6afd | ||
|
|
4cbbf2ac81 | ||
|
|
a035a2525a | ||
|
|
9727601752 | ||
|
|
9e9053c0eb | ||
|
|
507d495f3f | ||
|
|
a2f7547cdb | ||
|
|
d48308d913 | ||
|
|
78c0badd95 | ||
|
|
c81c33a430 | ||
|
|
1786376a9e | ||
|
|
aa973ea984 | ||
|
|
67aeeb0b2f | ||
|
|
ee2b01673b | ||
|
|
c392f7ee8d | ||
|
|
4adc950e29 | ||
|
|
78db5b55ec | ||
|
|
ad40fdcb55 | ||
|
|
c5ca510a75 | ||
|
|
13c64a95fa | ||
|
|
5d74a78bb7 | ||
|
|
d638bae7c4 | ||
|
|
9faf8ed55e | ||
|
|
3aa23b0f9d | ||
|
|
063bbc9f06 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -69,3 +69,5 @@ dist
|
||||
publish/assets
|
||||
|
||||
publish/utils/githubToken.js
|
||||
|
||||
src/**/*-internal.js
|
||||
|
||||
55
CHANGELOG.md
55
CHANGELOG.md
@@ -6,6 +6,61 @@ Project versioning adheres to [Semantic Versioning](http://semver.org/).
|
||||
Commit convention is based on [Conventional Commits](http://conventionalcommits.org).
|
||||
Change log format is based on [Keep a Changelog](http://keepachangelog.com/).
|
||||
|
||||
## [0.4.0](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.5...v0.4.0) - 2019-09-04
|
||||
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增**歌单**功能,目前支持酷我、酷狗、百度源歌单
|
||||
- 在设置界面-关于洛雪音乐说明部分新增**最新版网盘下载地址**与**打赏地址**
|
||||
- 新增酷狗 电音热歌榜、DJ热歌榜
|
||||
- 新增版本更新超时功能,对于部分无法访问GitHub的用户做更新超时提醒
|
||||
|
||||
### 移除
|
||||
|
||||
- **注意**:0.4.0以前的版本即将失效,请更新到0.4.0版本
|
||||
|
||||
## [0.3.5](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.4...v0.3.5) - 2019-08-30
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增**测试接口**,该接口同样速度较慢,但软件的大部分功能可用,**请自行切换到该接口**,找接口辛苦,且用且珍惜!
|
||||
|
||||
### 优化
|
||||
|
||||
- 取消需要刷新URL时windows任务栏进度显示错误状态(现显示为暂停状态)
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复使用临时接口时在试听列表双击灰色歌曲仍然会进行播放的Bug
|
||||
- 修复歌词加载Bug
|
||||
|
||||
## [0.3.4](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.3...v0.3.4) - 2019-08-29
|
||||
|
||||
### 优化
|
||||
|
||||
- 减少接口不稳定带来的影响,适当增加请求等待时间
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复播放过程中URL过期不会刷新URL的问题
|
||||
|
||||
## [0.3.3](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.2...v0.3.3) - 2019-08-29
|
||||
|
||||
### 修复
|
||||
|
||||
- **messoer**的接口已经关闭,暂时切换到临时接口使用,部分功能受限。。。
|
||||
- 修复设置界面更新出错时仍然显示更新下载中的问题
|
||||
- 修复手动定位播放进度条时存在偏差的问题
|
||||
- 屏蔽播放器中没有歌曲时对进度条的点击
|
||||
|
||||
|
||||
## [0.3.2](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.1...v0.3.2) - 2019-08-24
|
||||
|
||||
### 新增
|
||||
|
||||
- 新增酷狗排行榜其他音质下载
|
||||
|
||||
## [0.3.1](https://github.com/lyswhut/lx-music-desktop/compare/v0.3.0...v0.3.1) - 2019-08-24
|
||||
|
||||
### 修复
|
||||
|
||||
22
README.md
22
README.md
@@ -4,7 +4,7 @@
|
||||
<a href="https://github.com/lyswhut/lx-music-desktop/releases"><img src="https://img.shields.io/github/release/lyswhut/lx-music-desktop" alt="Release version"></a>
|
||||
<a href="https://ci.appveyor.com/project/lyswhut/lx-music-desktop"><img src="https://ci.appveyor.com/api/projects/status/flrsqd5ymp8fnte5?svg=true" alt="Build status"></a>
|
||||
<a href="https://travis-ci.org/lyswhut/lx-music-desktop"><img src="https://travis-ci.org/lyswhut/lx-music-desktop.svg?branch=master" alt="Build status"></a>
|
||||
<a href="https://github.com/lyswhut/lx-music-desktop/releases"><img src="https://img.shields.io/github/downloads/lyswhut/lx-music-desktop/latest/total" alt="Downloads"></a>
|
||||
<!-- <a href="https://github.com/lyswhut/lx-music-desktop/releases"><img src="https://img.shields.io/github/downloads/lyswhut/lx-music-desktop/latest/total" alt="Downloads"></a> -->
|
||||
<a href="https://github.com/lyswhut/lx-music-desktop/tree/dev"><img src="https://img.shields.io/github/package-json/v/lyswhut/lx-music-desktop/dev" alt="Dev branch version"></a>
|
||||
<!-- <a href="https://github.com/lyswhut/lx-music-desktop/blob/master/LICENSE"><img src="https://img.shields.io/github/license/lyswhut/lx-music-desktop" alt="License"></a> -->
|
||||
</p>
|
||||
@@ -46,14 +46,22 @@
|
||||
|
||||
软件变化请查看:[更新日志](https://github.com/lyswhut/lx-music-desktop/blob/master/CHANGELOG.md)<br>
|
||||
软件下载请转到:[发布页面](https://github.com/lyswhut/lx-music-desktop/releases)<br>
|
||||
或者到网盘下载:`https://www.lanzous.com/b906260/` 密码:`glqw`
|
||||
或者到网盘下载(网盘内有MAC、windows版):`https://www.lanzous.com/b906260/` 密码:`glqw`
|
||||
|
||||
注意:win7需要开启**透明效果**软件才可使用
|
||||
|
||||
#### 关于软件更新
|
||||
|
||||
软件启动时若发现新版本时会自动从本仓库下载安装包,下载完毕会弹窗提示更新。<br>
|
||||
若下载未完成时软件被关闭,下次启动软件会再次自动下载。<br>
|
||||
目前暂未添加跳过更新某个版本的功能。<br>
|
||||
注意:**绿色版**的软件更新功能**不可用**,为了能及时地获取更新,建议使用安装版!!
|
||||
注意:**绿色版**的软件更新功能**不可用**,为了能及时地获取更新,建议使用安装版!!<br>
|
||||
注意:**Mac版**、**Linux deb**版不支持自动更新!
|
||||
|
||||
#### TODO
|
||||
|
||||
- [ ] 新增拉取各大平台歌单
|
||||
- [ ] 新增聚合搜索
|
||||
|
||||
### 源码使用方法
|
||||
|
||||
@@ -77,7 +85,13 @@ npm run pack
|
||||
|
||||
### 致谢
|
||||
|
||||
感谢 [@messoer](https://github.com/messoer) 提供的部分音乐API!
|
||||
感谢 [@messoer](https://github.com/messoer) 曾经提供的部分音乐API!
|
||||
|
||||
### 免责声明
|
||||
|
||||
本项目**不开发或者破解直接获取音频数据**的功能,所有音频数据均来自**第三方接口**!<br>
|
||||
本软件仅用于**测试 `electron 6.x` 在各种系统上的兼容性**及用于**对比各大音乐平台歌单、排行榜等数据列表的差异性**,使用本软件产生的**任何涉及版权相关的数据**请于**24小时内删除**。<br>
|
||||
使用本软件所造成的的后果由使用者承担!
|
||||
|
||||
### 许可证
|
||||
|
||||
|
||||
7
package-lock.json
generated
7
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lx-music-desktop",
|
||||
"version": "0.2.4",
|
||||
"version": "0.3.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -4229,6 +4229,11 @@
|
||||
"randomfill": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"crypto-js": {
|
||||
"version": "3.1.9-1",
|
||||
"resolved": "https://registry.npm.taobao.org/crypto-js/download/crypto-js-3.1.9-1.tgz",
|
||||
"integrity": "sha1-/aGedh/Ad+Af+/3G6f38WeiAbNg="
|
||||
},
|
||||
"crypto-random-string": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npm.taobao.org/crypto-random-string/download/crypto-random-string-1.0.0.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "lx-music-desktop",
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.0",
|
||||
"description": "一个免费的音乐下载助手",
|
||||
"main": "./dist/electron/main.js",
|
||||
"productName": "lx-music-desktop",
|
||||
@@ -195,6 +195,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.0",
|
||||
"crypto-js": "^3.1.9-1",
|
||||
"electron-log": "^3.0.7",
|
||||
"electron-store": "^4.0.0",
|
||||
"electron-updater": "^4.1.2",
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
### 修复
|
||||
|
||||
- 修复音量条主题适配
|
||||
### 新增
|
||||
|
||||
- 新增**歌单**功能,目前支持酷我、酷狗、百度源歌单
|
||||
- 在设置界面-关于洛雪音乐说明部分新增**最新版网盘下载地址**与**打赏地址**
|
||||
- 新增酷狗 电音热歌榜、DJ热歌榜
|
||||
- 新增版本更新超时功能,对于部分无法访问GitHub的用户做更新超时提醒
|
||||
|
||||
### 移除
|
||||
|
||||
- **注意**:0.4.0以前的版本即将失效,请更新到0.4.0版本
|
||||
|
||||
@@ -1,7 +1,27 @@
|
||||
{
|
||||
"version": "0.3.1",
|
||||
"desc": "<h3>修复</h3>\n<ul>\n<li>修复音量条主题适配</li>\n</ul>\n",
|
||||
"version": "0.4.0",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增<strong>歌单</strong>功能,目前支持酷我、酷狗、百度源歌单</li>\n<li>在设置界面-关于洛雪音乐说明部分新增<strong>最新版网盘下载地址</strong>与<strong>打赏地址</strong></li>\n<li>新增酷狗 电音热歌榜、DJ热歌榜</li>\n<li>新增版本更新超时功能,对于部分无法访问GitHub的用户做更新超时提醒</li>\n</ul>\n<h3>移除</h3>\n<ul>\n<li><strong>注意</strong>:0.4.0以前的版本即将失效,请更新到0.4.0版本</li>\n</ul>\n",
|
||||
"history": [
|
||||
{
|
||||
"version": "0.3.5",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增<strong>测试接口</strong>,该接口同样速度较慢,但软件的大部分功能可用,<strong>请自行切换到该接口</strong>,找接口辛苦,且用且珍惜!</li>\n</ul>\n<h3>优化</h3>\n<ul>\n<li>取消需要刷新URL时windows任务栏进度显示错误状态(现显示为暂停状态)</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复使用临时接口时在试听列表双击灰色歌曲仍然会进行播放的Bug</li>\n<li>修复歌词加载Bug</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.3.4",
|
||||
"desc": "<h3>优化</h3>\n<ul>\n<li>减少接口不稳定带来的影响,适当增加请求等待时间</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>修复播放过程中URL过期不会刷新URL的问题</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.3.3",
|
||||
"desc": "<h3>修复</h3>\n<ul>\n<li><strong>messoer</strong>的接口已经关闭,暂时切换到临时接口使用,部分功能受限。。。</li>\n<li>修复设置界面更新出错时仍然显示更新下载中的问题</li>\n<li>修复手动定位播放进度条时存在偏差的问题</li>\n<li>屏蔽播放器中没有歌曲时对进度条的点击</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.3.2",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增酷狗排行榜其他音质下载</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.3.1",
|
||||
"desc": "<h3>修复</h3>\n<ul>\n<li>修复音量条主题适配</li>\n</ul>\n"
|
||||
},
|
||||
{
|
||||
"version": "0.3.0",
|
||||
"desc": "<h3>新增</h3>\n<ul>\n<li>新增<strong>MAC</strong>及<strong>Linux</strong>版本(需要的可自行下载)</li>\n<li>新增音量调整</li>\n<li>新增任务栏播放进度条控制选项(现在可在设置界面关闭在任务栏显示的播放进度)</li>\n<li>新增更新出错时的弹窗提示</li>\n<li>从该版本起,非安装版也会有更新弹窗提醒了,但仍然需要手动下载新版本更新,版本信息可到设置页面查看</li>\n</ul>\n<h3>修复</h3>\n<ul>\n<li>强制把临时接口设置回 <code>messoer</code> 接口</li>\n</ul>\n"
|
||||
|
||||
@@ -37,6 +37,7 @@ export default {
|
||||
globalObj: {
|
||||
apiSource: 'messoer',
|
||||
},
|
||||
updateTimeout: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -98,19 +99,29 @@ export default {
|
||||
})
|
||||
rendererOn('update-error', () => {
|
||||
this.setVersionModalVisible({ isError: true })
|
||||
this.clearUpdateTimeout()
|
||||
this.$nextTick(() => {
|
||||
this.showUpdateModal()
|
||||
})
|
||||
})
|
||||
rendererOn('update-downloaded', () => {
|
||||
this.clearUpdateTimeout()
|
||||
this.showUpdateModal()
|
||||
})
|
||||
rendererOn('update-not-available', () => {
|
||||
if (this.setting.ignoreVersion) this.setSetting(Object.assign({}, this.setting, { ignoreVersion: null }))
|
||||
this.clearUpdateTimeout()
|
||||
this.setNewVersion({
|
||||
version: this.version.version,
|
||||
})
|
||||
})
|
||||
this.updateTimeout = setTimeout(() => {
|
||||
this.updateTimeout = null
|
||||
this.setVersionModalVisible({ isError: true })
|
||||
this.$nextTick(() => {
|
||||
this.showUpdateModal()
|
||||
})
|
||||
}, 180000)
|
||||
|
||||
this.initData()
|
||||
this.globalObj.apiSource = this.setting.apiSource
|
||||
@@ -174,6 +185,10 @@ export default {
|
||||
body.removeEventListener('mouseleave', this.enableIgnoreMouseEvents)
|
||||
}
|
||||
},
|
||||
clearUpdateTimeout() {
|
||||
clearTimeout(this.updateTimeout)
|
||||
this.updateTimeout = null
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@ div(:class="$style.aside")
|
||||
dt 在线音乐
|
||||
dd
|
||||
router-link(:active-class="$style.active" to="search") 搜索
|
||||
dd
|
||||
router-link(:active-class="$style.active" to="songList") 歌单
|
||||
dd
|
||||
router-link(:active-class="$style.active" to="leaderboard") 排行榜
|
||||
//- dd
|
||||
router-link(:active-class="$style.active" to="recommend") 歌单
|
||||
dl
|
||||
dt 我的音乐
|
||||
dd
|
||||
|
||||
@@ -111,8 +111,10 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.setProgessWidth()
|
||||
this.init()
|
||||
this.$nextTick(() => {
|
||||
this.setProgessWidth()
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
changePlay(n) {
|
||||
@@ -164,10 +166,11 @@ export default {
|
||||
...mapMutations(['setVolume']),
|
||||
...mapMutations('list', ['updateMusicInfo']),
|
||||
init() {
|
||||
window.a = this.audio = document.createElement('audio')
|
||||
this.audio = document.createElement('audio')
|
||||
this.volume = this.audio.volume = this.setting.player.volume
|
||||
this.audio.controls = false
|
||||
this.audio.autoplay = true
|
||||
this.audio.preload = 'auto'
|
||||
this.audio.loop = this.setting.player.togglePlayMethod === 'singleLoop'
|
||||
|
||||
this.audio.addEventListener('playing', () => {
|
||||
@@ -191,31 +194,17 @@ export default {
|
||||
// console.log('code', this.audio.error.code)
|
||||
if (!this.musicInfo.songmid) return
|
||||
console.log('出错')
|
||||
this.stopPlay()
|
||||
if (this.audio.error.code !== 1 && this.retryNum < 3) { // 若音频URL无效则尝试刷新3次URL
|
||||
// console.log(this.retryNum)
|
||||
this.audioErrorTime = this.audio.currentTime // 记录出错的播放时间
|
||||
this.retryNum++
|
||||
this.setUrl(this.list[this.playIndex], true)
|
||||
this.status = 'URL过期,正在刷新URL...'
|
||||
return
|
||||
}
|
||||
this.stopPlay()
|
||||
this.sendProgressEvent(this.progress, 'error')
|
||||
|
||||
// let urls = this.player_info.targetSong.urls
|
||||
// if (urls && urls.some((url, index) => {
|
||||
// if (this.musicInfo.musicUrl.includes(url)) {
|
||||
// let newUrl = urls[index + 1]
|
||||
// if (!newUrl) return false
|
||||
// this.musicInfo.musicUrl = this.musicInfo.musicUrl.replace(url, newUrl)
|
||||
// // this.musicInfo.musicUrl = newUrl ? this.musicInfo.musicUrl.replace(url, newUrl) : this.setFormTag(this.musicInfo.musicUrl.replace(url, urls[0]))
|
||||
// return true
|
||||
// }
|
||||
// })) {
|
||||
// this.audio.src = this.musicInfo.musicUrl
|
||||
// // console.log(this.musicInfo.musicUrl)
|
||||
// } else {
|
||||
// this.handleNext()
|
||||
// }
|
||||
this.sendProgressEvent(this.progress, 'error')
|
||||
this.status = '音频加载出错,5 秒后切换下一首'
|
||||
this.addDelayNextTimeout()
|
||||
})
|
||||
@@ -294,7 +283,7 @@ export default {
|
||||
}
|
||||
},
|
||||
checkDelayNextTimeout() {
|
||||
console.log(this.delayNextTimeout)
|
||||
// console.log(this.delayNextTimeout)
|
||||
if (this.delayNextTimeout) {
|
||||
clearTimeout(this.delayNextTimeout)
|
||||
this.delayNextTimeout = null
|
||||
@@ -360,6 +349,7 @@ export default {
|
||||
this.clearAppTitle()
|
||||
},
|
||||
setProgess(e) {
|
||||
if (!this.audio.src) return
|
||||
this.audio.currentTime =
|
||||
(e.offsetX / this.pregessWidth) * this.maxPlayTime
|
||||
if (!this.isPlay) this.audio.play()
|
||||
@@ -380,7 +370,7 @@ export default {
|
||||
getPlayType(highQuality, songInfo) {
|
||||
switch (songInfo.source) {
|
||||
case 'wy':
|
||||
case 'kg':
|
||||
// case 'kg':
|
||||
return '128k'
|
||||
}
|
||||
let type = songInfo._types['192k'] ? '192k' : '128k'
|
||||
@@ -411,12 +401,12 @@ export default {
|
||||
}
|
||||
},
|
||||
setLrc(targetSong) {
|
||||
this.musicInfo.lrc = targetSong.lyric
|
||||
this.musicInfo.lrc = targetSong.lrc
|
||||
|
||||
let lrcP = this.musicInfo.lrc
|
||||
? Promise.resolve()
|
||||
: this.getLrc(targetSong).then(() => {
|
||||
this.musicInfo.lrc = targetSong.lyric
|
||||
this.musicInfo.lrc = targetSong.lrc
|
||||
})
|
||||
|
||||
lrcP
|
||||
@@ -424,8 +414,8 @@ export default {
|
||||
this.lyric.lrc.setLyric(this.musicInfo.lrc)
|
||||
if (this.isPlay && (this.musicInfo.url || this.listId == 'download')) this.lyric.lrc.play(this.audio.currentTime * 1000)
|
||||
})
|
||||
.catch(err => {
|
||||
this.status = err.message
|
||||
.catch(() => {
|
||||
this.status = '歌词获取失败'
|
||||
})
|
||||
},
|
||||
handleRemoveMusic() {
|
||||
@@ -566,7 +556,8 @@ export default {
|
||||
border-radius: @radius-progress-border;
|
||||
transition-duration: 0.2s;
|
||||
background-color: @color-theme;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.play-btn {
|
||||
|
||||
@@ -5,7 +5,7 @@ material-modal(:show="show" :bg-close="bgClose" @close="handleClose")
|
||||
| {{ info.name }}
|
||||
br
|
||||
| {{ info.singer }}
|
||||
material-btn(:class="$style.btn" :title="!checkSource(type.type) && '目前酷狗、网易云音源仅支持下载128k音质'" :disabled="!checkSource(type.type)" :key="type.type" @click="handleClick(type.type)" v-for="type in info.types") {{getTypeName(type.type)}} {{ type.type.toUpperCase() }}{{ type.size && ` - ${type.size.toUpperCase()}` }}
|
||||
material-btn(:class="$style.btn" :title="!checkSource(type.type) && '目前网易云音源仅支持下载128k音质'" :disabled="!checkSource(type.type)" :key="type.type" @click="handleClick(type.type)" v-for="type in info.types") {{getTypeName(type.type)}} {{ type.type.toUpperCase() }}{{ type.size && ` - ${type.size.toUpperCase()}` }}
|
||||
|
||||
</template>
|
||||
|
||||
@@ -52,7 +52,7 @@ export default {
|
||||
checkSource(type) {
|
||||
switch (this.musicInfo.source) {
|
||||
case 'wy':
|
||||
case 'kg':
|
||||
// case 'kg':
|
||||
return type == '128k'
|
||||
|
||||
default:
|
||||
|
||||
235
src/renderer/components/material/SongList.vue
Normal file
235
src/renderer/components/material/SongList.vue
Normal file
@@ -0,0 +1,235 @@
|
||||
<template lang="pug">
|
||||
div(:class="$style.songList")
|
||||
transition(enter-active-class="animated fadeIn" leave-active-class="animated fadeOut")
|
||||
div(v-if="list.length" :class="$style.list")
|
||||
div(:class="$style.thead")
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th.nobreak.center(style="width: 37px;")
|
||||
material-checkbox(id="search_select_all" v-model="isSelectAll" @change="handleSelectAllData"
|
||||
:indeterminate="isIndeterminate" :title="isSelectAll && !isIndeterminate ? '全不选' : '全选'")
|
||||
th.nobreak(style="width: 25%;") 歌曲名
|
||||
th.nobreak(style="width: 20%;") 歌手
|
||||
th.nobreak(style="width: 22%;") 专辑
|
||||
th.nobreak(style="width: 18%;") 操作
|
||||
th.nobreak(style="width: 10%;") 时长
|
||||
div.scroll(:class="$style.tbody" ref="dom_scrollContent")
|
||||
table
|
||||
tbody
|
||||
tr(v-for='(item, index) in list' :key='item.songmid' @click="handleDoubleClick(index)")
|
||||
td.nobreak.center(style="width: 37px;" @click.stop)
|
||||
material-checkbox(:id="index.toString()" v-model="selectdList" @change="handleChangeSelect" :value="item")
|
||||
td.break(style="width: 25%;")
|
||||
| {{item.name}}
|
||||
span.badge.badge-info(v-if="item._types['320k']") 高品质
|
||||
span.badge.badge-success(v-if="item._types.ape || item._types.flac") 无损
|
||||
td.break(style="width: 20%;") {{item.singer}}
|
||||
td.break(style="width: 22%;") {{item.albumName}}
|
||||
td(style="width: 18%;")
|
||||
material-list-buttons(:index="index" :search-btn="true" :play-btn="item.source == 'kw' || (!isAPITemp && item.source != 'tx')" :download-btn="item.source == 'kw' || (!isAPITemp && item.source != 'tx')" :remove-btn="false" @btn-click="handleListBtnClick")
|
||||
//- button.btn-info(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k'] || item._types.flac" @click.stop='openDownloadModal(index)') 下载
|
||||
//- button.btn-secondary(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k']" @click.stop='testPlay(index)') 试听
|
||||
//- button.btn-success(type='button' v-if="(item._types['128k'] || item._types['192k'] || item._types['320k']) && userInfo" @click.stop='showListModal(index)') +
|
||||
td(style="width: 10%;") {{item.interval || '--/--'}}
|
||||
div(:class="$style.pagination")
|
||||
material-pagination(:count="total" :limit="limit" :page="page" @btn-click="handleTogglePage")
|
||||
div(v-else :class="$style.noitem")
|
||||
p(v-html="noItem")
|
||||
material-flow-btn(:show="isShowEditBtn && (source == 'kw' || !isAPITemp)" :remove-btn="false" @btn-click="handleFlowBtnClick")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { scrollTo } from '../../utils'
|
||||
export default {
|
||||
name: 'MaterialSongList',
|
||||
model: {
|
||||
prop: 'selectdData',
|
||||
event: 'input',
|
||||
},
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
page: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
selectdData: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
source: {
|
||||
type: String,
|
||||
},
|
||||
noItem: {
|
||||
type: String,
|
||||
default: '列表加载中...',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['setting']),
|
||||
isAPITemp() {
|
||||
return this.setting.apiSource == 'temp'
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectdList(n) {
|
||||
const len = n.length
|
||||
if (len) {
|
||||
this.isSelectAll = true
|
||||
this.isIndeterminate = len !== this.list.length
|
||||
this.isShowEditBtn = true
|
||||
} else {
|
||||
this.isSelectAll = false
|
||||
this.isShowEditBtn = false
|
||||
}
|
||||
},
|
||||
selectdData(n) {
|
||||
const len = n.length
|
||||
if (len) {
|
||||
this.isSelectAll = true
|
||||
this.isIndeterminate = len !== this.list.length
|
||||
this.isShowEditBtn = true
|
||||
this.selectdList = [...n]
|
||||
} else {
|
||||
this.isSelectAll = false
|
||||
this.isShowEditBtn = false
|
||||
this.resetSelect()
|
||||
}
|
||||
},
|
||||
list(n) {
|
||||
this.resetSelect()
|
||||
if (!this.list.length) return
|
||||
this.$nextTick(() => scrollTo(this.$refs.dom_scrollContent, 0))
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
clickTime: 0,
|
||||
clickIndex: -1,
|
||||
isSelectAll: false,
|
||||
isIndeterminate: false,
|
||||
isShowEditBtn: false,
|
||||
selectdList: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleDoubleClick(index) {
|
||||
if (
|
||||
window.performance.now() - this.clickTime > 400 ||
|
||||
this.clickIndex !== index
|
||||
) {
|
||||
this.clickTime = window.performance.now()
|
||||
this.clickIndex = index
|
||||
return
|
||||
}
|
||||
this.emitEvent((this.source == 'kw' || (!this.isAPITemp && this.list[index].source != 'tx')) ? 'testPlay' : 'search', index)
|
||||
this.clickTime = 0
|
||||
this.clickIndex = -1
|
||||
},
|
||||
handleListBtnClick(info) {
|
||||
this.emitEvent('listBtnClick', info)
|
||||
},
|
||||
handleSelectAllData(isSelect) {
|
||||
this.selectdList = isSelect ? [...this.list] : []
|
||||
this.$emit('input', [...this.selectdList])
|
||||
},
|
||||
resetSelect() {
|
||||
this.selectdList = false
|
||||
this.selectdList = []
|
||||
},
|
||||
handleTogglePage(page) {
|
||||
this.emitEvent('togglePage', page)
|
||||
},
|
||||
handleFlowBtnClick(action) {
|
||||
this.emitEvent('flowBtnClick', action)
|
||||
},
|
||||
emitEvent(action, data) {
|
||||
this.$emit('action', { action, data })
|
||||
},
|
||||
handleChangeSelect() {
|
||||
this.$emit('input', [...this.selectdList])
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" module>
|
||||
@import '../../assets/styles/layout.less';
|
||||
.song-list {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
|
||||
.list {
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
.thead {
|
||||
flex: none;
|
||||
background-color: @color-theme_2;
|
||||
}
|
||||
.tbody {
|
||||
flex: auto;
|
||||
overflow-y: auto;
|
||||
td {
|
||||
font-size: 12px;
|
||||
:global(.badge) {
|
||||
margin-right: 3px;
|
||||
&:first-child {
|
||||
margin-left: 3px;
|
||||
}
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.pagination {
|
||||
text-align: center;
|
||||
padding: 15px 0;
|
||||
// left: 50%;
|
||||
// transform: translateX(-50%);
|
||||
}
|
||||
.noitem {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
font-size: 24px;
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#container.@{value}) {
|
||||
.thead {
|
||||
background-color: ~'@color-@{value}-theme_2';
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</style>
|
||||
214
src/renderer/components/material/TagList.vue
Normal file
214
src/renderer/components/material/TagList.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<template lang="pug">
|
||||
div(:class="$style.tagList")
|
||||
div(:class="$style.label" ref="dom_btn" @click="handleShow") {{value.name}}
|
||||
div.scroll(:class="$style.list" @click.stop ref="dom_list" :style="listStyle")
|
||||
div(:class="$style.tag" @click="handleClick(null)") 全部
|
||||
dl(v-for="type in list")
|
||||
dt(:class="$style.type") {{type.name}}
|
||||
dd(:class="$style.tag" v-for="tag in type.list" @click="handleClick(tag)") {{tag.name}}
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import { isChildren } from '../../utils'
|
||||
export default {
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
listStyle: {
|
||||
height: 0,
|
||||
opacity: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show(n) {
|
||||
this.$nextTick(() => {
|
||||
if (n) {
|
||||
let sh = this.$refs.dom_list.scrollHeight
|
||||
this.listStyle.height = (sh > 250 ? 250 : sh) + 'px'
|
||||
this.listStyle.opacity = 1
|
||||
this.listStyle.overflow = 'auto'
|
||||
} else {
|
||||
this.listStyle.height = 0
|
||||
this.listStyle.opacity = 0
|
||||
}
|
||||
})
|
||||
},
|
||||
list() {
|
||||
this.$refs.dom_list.scrollTop = 0
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener('click', this.handleHide)
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('click', this.handleHide)
|
||||
},
|
||||
methods: {
|
||||
handleHide(e) {
|
||||
// if (e && e.target.parentNode != this.$refs.dom_list && this.show) return this.show = false
|
||||
if (e && e.target == this.$refs.dom_btn) return
|
||||
setTimeout(() => {
|
||||
this.show = false
|
||||
}, 50)
|
||||
},
|
||||
handleClick(item) {
|
||||
if (!item) {
|
||||
item = {
|
||||
name: '全部',
|
||||
id: null,
|
||||
}
|
||||
}
|
||||
if (item.id === this.value.id) return this.handleShow()
|
||||
this.$emit('input', item)
|
||||
this.$emit('change', item)
|
||||
this.handleShow()
|
||||
},
|
||||
handleShow() {
|
||||
this.show = !this.show
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" module>
|
||||
@import '../../assets/styles/layout.less';
|
||||
|
||||
.tag-list {
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.label {
|
||||
padding: 8px 15px;
|
||||
// background-color: @color-btn-background;
|
||||
transition: background-color @transition-theme;
|
||||
border-top: 2px solid @color-tab-border-bottom;
|
||||
// border-left: 2px solid @color-tab-border-bottom;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
// border-top-left-radius: 3px;
|
||||
color: @color-btn;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: @color-theme_2-hover;
|
||||
}
|
||||
&:active {
|
||||
background-color: @color-theme_2-active;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
width: 646px;
|
||||
left: 0;
|
||||
border-bottom: 2px solid @color-tab-border-bottom;
|
||||
border-right: 2px solid @color-tab-border-bottom;
|
||||
border-bottom-right-radius: 5px;
|
||||
background-color: @color-theme_2;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
transition: .25s ease;
|
||||
transition-property: height, opacity;
|
||||
z-index: 10;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
|
||||
li {
|
||||
cursor: pointer;
|
||||
padding: 8px 15px;
|
||||
// color: @color-btn;
|
||||
text-align: center;
|
||||
outline: none;
|
||||
transition: background-color @transition-theme;
|
||||
background-color: @color-btn-background;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:hover {
|
||||
background-color: @color-theme_2-hover;
|
||||
}
|
||||
&:active {
|
||||
background-color: @color-theme_2-active;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.type {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 3px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
margin: 5px;
|
||||
background-color: @color-btn-background;
|
||||
padding: 8px 10px;
|
||||
border-radius: @radius-progress-border;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: @color-theme_2-hover;
|
||||
}
|
||||
&:active {
|
||||
background-color: @color-theme_2-active;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
each(@themes, {
|
||||
:global(#container.@{value}) {
|
||||
.label {
|
||||
border-top-color: ~'@{color-@{value}-tab-border-bottom}';
|
||||
// border-left-color: ~'@{color-@{value}-tab-border-bottom}';
|
||||
color: ~'@{color-@{value}-btn}';
|
||||
&:hover {
|
||||
background-color: ~'@{color-@{value}-theme_2-hover}';
|
||||
}
|
||||
&:active {
|
||||
background-color: ~'@{color-@{value}-theme_2-active}';
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
border-bottom-color: ~'@{color-@{value}-tab-border-bottom}';
|
||||
border-right-color: ~'@{color-@{value}-tab-border-bottom}';
|
||||
// border-left-color: ~'@{color-@{value}-tab-border-bottom}';
|
||||
li {
|
||||
// color: ~'@{color-@{value}-btn}';
|
||||
background-color: ~'@{color-@{value}-btn-background}';
|
||||
&:hover {
|
||||
background-color: ~'@{color-@{value}-theme_2-hover}';
|
||||
}
|
||||
&:active {
|
||||
background-color: ~'@{color-@{value}-theme_2-active}';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tag {
|
||||
background-color: ~'@{color-@{value}-btn-background}';
|
||||
&:hover {
|
||||
background-color: ~'@{color-@{value}-theme_2-hover}';
|
||||
}
|
||||
&:active {
|
||||
background-color: ~'@{color-@{value}-theme_2-active}';
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</style>
|
||||
@@ -18,9 +18,9 @@ export default [
|
||||
view: 'Leaderboard',
|
||||
},
|
||||
{
|
||||
path: '/recommend',
|
||||
name: 'recommend',
|
||||
view: 'Recommend',
|
||||
path: '/songList',
|
||||
name: 'songList',
|
||||
view: 'SongList',
|
||||
},
|
||||
{
|
||||
path: '/list',
|
||||
|
||||
@@ -6,7 +6,13 @@ export default {
|
||||
getVersionInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
httpGet(`https://raw.githubusercontent.com/${author.name}/${name}/master/publish/version.json`, (err, resp, body) => {
|
||||
if (err) return reject(err)
|
||||
if (err) {
|
||||
return resolve({
|
||||
version: '0.0.0',
|
||||
desc: '<h3>版本获取失败</h3><ul><li>更新信息获取失败,可能是无法访问Github导致的,请手动检查更新!</li><li>检查方法:去设置-关于洛雪音乐打开<strong>开源地址</strong>或<strong>网盘地址</strong>查看<strong>版本号</strong>与当前版本对比是否最新</li></ul>',
|
||||
history: [],
|
||||
})
|
||||
}
|
||||
resolve(body)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -62,7 +62,7 @@ const mutations = {
|
||||
datas.musicInfo.img = datas.url
|
||||
},
|
||||
setLrc(state, datas) {
|
||||
datas.musicInfo.lyric = datas.lrc
|
||||
datas.musicInfo.lrc = datas.lrc
|
||||
},
|
||||
setList(state, { list, listId, index }) {
|
||||
state.list = list
|
||||
|
||||
111
src/renderer/store/modules/songList.js
Normal file
111
src/renderer/store/modules/songList.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import music from '../../utils/music'
|
||||
const sortList = {}
|
||||
const sources = []
|
||||
for (const source of music.sources) {
|
||||
const songList = music[source.id].songList
|
||||
if (!songList) continue
|
||||
sortList[source.id] = songList.sortList
|
||||
sources.push(source)
|
||||
}
|
||||
|
||||
// state
|
||||
const state = {
|
||||
tags: {},
|
||||
list: {
|
||||
list: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
limit: 30,
|
||||
key: null,
|
||||
},
|
||||
listDetail: {
|
||||
list: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
limit: 30,
|
||||
key: null,
|
||||
},
|
||||
selectListInfo: {},
|
||||
isVisibleListDetail: false,
|
||||
}
|
||||
|
||||
sources.forEach(source => {
|
||||
state.tags[source.id] = null
|
||||
})
|
||||
|
||||
|
||||
// getters
|
||||
const getters = {
|
||||
sourceInfo: () => ({ sources, sortList }),
|
||||
tags: state => state.tags,
|
||||
isVisibleListDetail: state => state.isVisibleListDetail,
|
||||
selectListInfo: state => state.selectListInfo,
|
||||
listData(state) {
|
||||
return state.list
|
||||
},
|
||||
listDetail(state) {
|
||||
return state.listDetail
|
||||
},
|
||||
}
|
||||
|
||||
// actions
|
||||
const actions = {
|
||||
getTags({ state, rootState, commit }) {
|
||||
let source = rootState.setting.songList.source
|
||||
return music[source].songList.getTags().then(result => commit('setTags', { tags: result, source }))
|
||||
},
|
||||
getList({ state, rootState, commit }, page) {
|
||||
let source = rootState.setting.songList.source
|
||||
let tabId = rootState.setting.songList.tagInfo.id
|
||||
let sortId = rootState.setting.songList.sortId
|
||||
// console.log(sortId)
|
||||
let key = `${source}${sortId}${tabId}${page}`
|
||||
if (state.list.list.length && state.list.key == key) return true
|
||||
return music[source].songList.getList(sortId, tabId, page).then(result => commit('setList', { result, key, page }))
|
||||
},
|
||||
getListDetail({ state, rootState, commit }, { id, page }) {
|
||||
let source = rootState.setting.songList.source
|
||||
let key = `${source}${id}${page}`
|
||||
if (state.listDetail.list.length && state.listDetail.key == key) return true
|
||||
commit('clearListDetail')
|
||||
return music[source].songList.getListDetail(id, page).then(result => commit('setListDetail', { result, key, page }))
|
||||
},
|
||||
}
|
||||
|
||||
// mitations
|
||||
const mutations = {
|
||||
setTags(state, { tags, source }) {
|
||||
state.tags[source] = tags
|
||||
},
|
||||
setList(state, { result, key, page }) {
|
||||
state.list.list = result.list
|
||||
state.list.total = result.total
|
||||
state.list.limit = result.limit
|
||||
state.list.page = page
|
||||
state.list.key = key
|
||||
},
|
||||
setListDetail(state, { result, key, page }) {
|
||||
state.listDetail.list = result.list
|
||||
state.listDetail.total = result.total
|
||||
state.listDetail.limit = result.limit
|
||||
state.listDetail.page = page
|
||||
state.listDetail.key = key
|
||||
},
|
||||
setVisibleListDetail(state, bool) {
|
||||
state.isVisibleListDetail = bool
|
||||
},
|
||||
setSelectListInfo(state, info) {
|
||||
state.selectListInfo = info
|
||||
},
|
||||
clearListDetail(state) {
|
||||
state.listDetail.list = []
|
||||
},
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
}
|
||||
@@ -12,6 +12,11 @@ export default {
|
||||
if (tabId != null) state.setting.leaderboard.tabId = tabId
|
||||
if (source != null) state.setting.leaderboard.source = source
|
||||
},
|
||||
setSongList(state, { sortId, tagInfo, source }) {
|
||||
if (tagInfo != null) state.setting.songList.tagInfo = tagInfo
|
||||
if (sortId != null) state.setting.songList.sortId = sortId
|
||||
if (source != null) state.setting.songList.source = source
|
||||
},
|
||||
setNewVersion(state, val) {
|
||||
// val.history.forEach(ver => {
|
||||
// ver.desc = ver.desc.replace(/\n/g, '<br>')
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import fs from 'fs'
|
||||
import { shell, remote } from 'electron'
|
||||
import { shell, remote, clipboard } from 'electron'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
import crypto from 'crypto'
|
||||
|
||||
/**
|
||||
* 获取两个数之间的随机整数,大于等于min,小于max
|
||||
@@ -161,7 +162,7 @@ export const isChildren = (parent, children) => {
|
||||
* @param {*} setting
|
||||
*/
|
||||
export const updateSetting = setting => {
|
||||
const defaultVersion = '1.0.4'
|
||||
const defaultVersion = '1.0.5'
|
||||
const defaultSetting = {
|
||||
version: defaultVersion,
|
||||
player: {
|
||||
@@ -182,9 +183,17 @@ export const updateSetting = setting => {
|
||||
source: 'kw',
|
||||
tabId: 'kwbiaosb',
|
||||
},
|
||||
songList: {
|
||||
source: 'kg',
|
||||
sortId: '5',
|
||||
tagInfo: {
|
||||
name: '全部',
|
||||
id: null,
|
||||
},
|
||||
},
|
||||
themeId: 0,
|
||||
sourceId: 'kw',
|
||||
apiSource: 'messoer',
|
||||
apiSource: 'test',
|
||||
randomAnimate: true,
|
||||
ignoreVersion: null,
|
||||
}
|
||||
@@ -201,7 +210,7 @@ export const updateSetting = setting => {
|
||||
objectDeepMerge(defaultSetting, overwriteSetting)
|
||||
setting = defaultSetting
|
||||
}
|
||||
setting.apiSource = 'messoer' // 强制设置回 messoer 接口源
|
||||
if (setting.apiSource != 'temp') setting.apiSource = 'test' // 强制设置回 test 接口源
|
||||
return setting
|
||||
}
|
||||
|
||||
@@ -220,3 +229,16 @@ let dom_title = document.getElementsByTagName('title')[0]
|
||||
export const setTitle = title => {
|
||||
dom_title.innerText = title || '洛雪音乐助手'
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建 MD5 hash
|
||||
* @param {*} str
|
||||
*/
|
||||
export const toMD5 = str => crypto.createHash('md5').update(str).digest('hex')
|
||||
|
||||
/**
|
||||
* 复制文本到剪贴板
|
||||
* @param {*} str
|
||||
*/
|
||||
export const clipboardWriteText = str => clipboard.writeText(str)
|
||||
|
||||
@@ -2,6 +2,6 @@ export const requestMsg = {
|
||||
fail: '请求异常😮,可以多试几次,若还是不行就换一首吧。。。',
|
||||
unachievable: '哦No😱...接口无法访问了!',
|
||||
// unachievable: '哦No😱...接口无法访问了!已帮你切换到临时接口,重试下看能不能播放吧~',
|
||||
notConnectNetwork: '无法连接网络',
|
||||
notConnectNetwork: '无法连接到服务器',
|
||||
cancelRequest: '取消http请求',
|
||||
}
|
||||
|
||||
@@ -1,24 +1,36 @@
|
||||
import kw_api_messoer from './kw/api-messoer'
|
||||
import kw_api_temp from './kw/api-temp'
|
||||
import tx_api_messoer from './tx/api-messoer'
|
||||
import kg_api_messoer from './kg/api-messoer'
|
||||
import wy_api_messoer from './wy/api-messoer'
|
||||
import bd_api_messoer from './bd/api-messoer'
|
||||
import kw_api_test from './kw/api-test'
|
||||
// import tx_api_messoer from './tx/api-messoer'
|
||||
import kg_api_test from './kg/api-test'
|
||||
import wy_api_test from './wy/api-test'
|
||||
import bd_api_test from './bd/api-test'
|
||||
// import kw_api_internal from './kw/api-internal'
|
||||
// import tx_api_internal from './tx/api-internal'
|
||||
// import kg_api_internal from './kg/api-internal'
|
||||
// import wy_api_internal from './wy/api-internal'
|
||||
// import bd_api_internal from './bd/api-internal'
|
||||
|
||||
const apis = {
|
||||
kw_api_messoer,
|
||||
tx_api_messoer,
|
||||
kg_api_messoer,
|
||||
wy_api_messoer,
|
||||
bd_api_messoer,
|
||||
kw_api_test,
|
||||
// tx_api_messoer,
|
||||
kg_api_test,
|
||||
wy_api_test,
|
||||
bd_api_test,
|
||||
// kw_api_internal,
|
||||
// tx_api_internal,
|
||||
// kg_api_internal,
|
||||
// wy_api_internal,
|
||||
// bd_api_internal,
|
||||
kw_api_temp,
|
||||
}
|
||||
|
||||
|
||||
const getAPI = source => {
|
||||
switch (window.globalObj.apiSource) {
|
||||
case 'messoer':
|
||||
return apis[`${source}_api_messoer`]
|
||||
// case 'messoer':
|
||||
// return apis[`${source}_api_messoer`]
|
||||
case 'test':
|
||||
return apis[`${source}_api_test`]
|
||||
case 'temp':
|
||||
return apis[`${source}_api_temp`]
|
||||
}
|
||||
|
||||
44
src/renderer/utils/music/bd/api-internal.js
Normal file
44
src/renderer/utils/music/bd/api-internal.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
|
||||
const api_internal = {
|
||||
successCode: 2200,
|
||||
// getMusicUrl(songInfo, type) {
|
||||
// const requestObj = httpFatch(`http://play.taihe.com/data/music/songlink`, {
|
||||
// method: 'post',
|
||||
// headers: {
|
||||
// Origin: 'http://play.taihe.com',
|
||||
// },
|
||||
// formData: {
|
||||
// songIds: songInfo.songmid,
|
||||
// },
|
||||
// })
|
||||
// requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
// console.log(body)
|
||||
// return Promise.reject()
|
||||
// // if (body.error_code !== this.successCode) return this.getMusicUrl(songInfo, type)
|
||||
// // return body.code === 200 ? Promise.resolve({ type, url: body.result.bitrate.file_link }) : Promise.reject(new Error(requestMsg.fail))
|
||||
// })
|
||||
// return requestObj
|
||||
// },
|
||||
getMusicInfo(songInfo, tryNum = 0) {
|
||||
const requestObj = httpFatch(`http://tingapi.ting.baidu.com/v1/restserver/ting?from=webapp_music&method=baidu.ting.song.baseInfos&song_id=${songInfo.songmid}`)
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
if (body.error_code !== this.successCode) return tryNum > 5 ? Promise.reject(new Error(requestMsg.fail)) : this.getMusicInfo(songInfo, ++tryNum)
|
||||
return body ? Promise.resolve(body.result.items[0]) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo) {
|
||||
const requestObj = this.getMusicInfo(songInfo)
|
||||
requestObj.promise = requestObj.promise.then(info => info.pic_premium)
|
||||
return requestObj
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
const requestObj = this.getMusicInfo(songInfo)
|
||||
requestObj.promise.then(info => httpFatch(info.lrclink).promise)
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_internal
|
||||
@@ -1,37 +0,0 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
|
||||
const api_messoer = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/baidu/url?id=${songInfo.songmid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo, size = '500') {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/baidu/pic?id=${songInfo.songmid}&imageSize=${size}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/baidu/lrc?id=${songInfo.songmid}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body ? Promise.resolve(body) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_messoer
|
||||
41
src/renderer/utils/music/bd/api-test.js
Normal file
41
src/renderer/utils/music/bd/api-test.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
import { headers, timeout } from '../options'
|
||||
|
||||
const api_test = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`http://test.tempmusic.tk/url/bd/${songInfo.songmid}/${type}`, {
|
||||
method: 'get',
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo, size = '500') {
|
||||
const requestObj = httpFatch(`http://test.tempmusic.tk/pic/bd/${songInfo.songmid}/${size}`, {
|
||||
method: 'get',
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
const requestObj = httpFatch(`http://test.tempmusic.tk/lrc/bd/${songInfo.songmid}`, {
|
||||
method: 'get',
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_test
|
||||
@@ -1,9 +1,11 @@
|
||||
import leaderboard from './leaderboard'
|
||||
import api_source from '../api-source'
|
||||
import musicInfo from './musicInfo'
|
||||
import songList from './songList'
|
||||
|
||||
const bd = {
|
||||
leaderboard,
|
||||
songList,
|
||||
getMusicUrl(songInfo, type) {
|
||||
return api_source('bd').getMusicUrl(songInfo, type)
|
||||
},
|
||||
|
||||
245
src/renderer/utils/music/bd/songList.js
Normal file
245
src/renderer/utils/music/bd/songList.js
Normal file
@@ -0,0 +1,245 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { formatPlayTime, toMD5 } from '../../index'
|
||||
import CryptoJS from 'crypto-js'
|
||||
|
||||
export default {
|
||||
_requestObj_tags: null,
|
||||
_requestObj_list: null,
|
||||
_requestObj_listRecommend: null,
|
||||
_requestObj_listDetail: null,
|
||||
limit_list: 20,
|
||||
limit_song: 25,
|
||||
successCode: 22000,
|
||||
sortList: [
|
||||
{
|
||||
name: '最热',
|
||||
id: '1',
|
||||
},
|
||||
{
|
||||
name: '最新',
|
||||
id: '0',
|
||||
},
|
||||
],
|
||||
aesPassEncod(jsonData) {
|
||||
let timestamp = Math.floor(Date.now() / 1000)
|
||||
let privateKey = toMD5('baidu_taihe_music_secret_key' + timestamp).substr(8, 16)
|
||||
let key = CryptoJS.enc.Utf8.parse(privateKey)
|
||||
let iv = CryptoJS.enc.Utf8.parse(privateKey)
|
||||
let arrData = []
|
||||
let strData = ''
|
||||
for (let key in jsonData) arrData.push(key)
|
||||
arrData.sort()
|
||||
for (let i = 0; i < arrData.length; i++) {
|
||||
let key = arrData[i]
|
||||
strData +=
|
||||
(i === 0 ? '' : '&') + key + '=' + encodeURIComponent(jsonData[key])
|
||||
}
|
||||
let JsonFormatter = {
|
||||
stringify(cipherParams) {
|
||||
let jsonObj = {
|
||||
ct: cipherParams.ciphertext.toString(CryptoJS.enc.Base64),
|
||||
}
|
||||
if (cipherParams.iv) {
|
||||
jsonObj.iv = cipherParams.iv.toString()
|
||||
}
|
||||
if (cipherParams.salt) {
|
||||
jsonObj.s = cipherParams.salt.toString()
|
||||
}
|
||||
return jsonObj
|
||||
},
|
||||
parse(jsonStr) {
|
||||
let jsonObj = JSON.parse(jsonStr)
|
||||
let cipherParams = CryptoJS.lib.CipherParams.create({
|
||||
ciphertext: CryptoJS.enc.Base64.parse(jsonObj.ct),
|
||||
})
|
||||
if (jsonObj.iv) {
|
||||
cipherParams.iv = CryptoJS.enc.Hex.parse(jsonObj.iv)
|
||||
}
|
||||
if (jsonObj.s) {
|
||||
cipherParams.salt = CryptoJS.enc.Hex.parse(jsonObj.s)
|
||||
}
|
||||
return cipherParams
|
||||
},
|
||||
}
|
||||
let encrypted = CryptoJS.AES.encrypt(strData, key, {
|
||||
iv: iv,
|
||||
blockSize: 16,
|
||||
mode: CryptoJS.mode.CBC,
|
||||
format: JsonFormatter,
|
||||
})
|
||||
let ciphertext = encrypted.toString().ct
|
||||
let sign = toMD5('baidu_taihe_music' + ciphertext + timestamp)
|
||||
let jsonRet = {
|
||||
timestamp: timestamp,
|
||||
param: ciphertext,
|
||||
sign: sign,
|
||||
}
|
||||
return jsonRet
|
||||
},
|
||||
createUrl(param, method) {
|
||||
let data = this.aesPassEncod(param)
|
||||
return `http://musicmini.qianqian.com/v1/restserver/ting?method=${method}&time=${Date.now()}×tamp=${data.timestamp}¶m=${data.param}&sign=${data.sign}`
|
||||
},
|
||||
getTagsUrl() {
|
||||
return this.createUrl({
|
||||
from: 'qianqianmini',
|
||||
type: 'diy',
|
||||
version: '10.1.8',
|
||||
}, 'baidu.ting.ugcdiy.getChannels')
|
||||
},
|
||||
getListUrl(sortType, tagName, page) {
|
||||
return this.createUrl({
|
||||
channelname: tagName || '全部',
|
||||
from: 'qianqianmini',
|
||||
offset: (page - 1) * this.limit_list,
|
||||
order_type: sortType,
|
||||
size: this.limit_list,
|
||||
version: '10.1.8',
|
||||
}, 'baidu.ting.ugcdiy.getChanneldiy')
|
||||
},
|
||||
getListDetailUrl(list_id, page) {
|
||||
return this.createUrl({
|
||||
list_id,
|
||||
offset: (page - 1) * this.limit_song,
|
||||
size: this.limit_song,
|
||||
withcount: '1',
|
||||
withsong: '1',
|
||||
}, 'baidu.ting.ugcdiy.getBaseInfo')
|
||||
},
|
||||
|
||||
// 获取标签
|
||||
getTags() {
|
||||
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
|
||||
this._requestObj_tags = httpFatch(this.getTagsUrl())
|
||||
return this._requestObj_tags.promise.then(({ body }) => {
|
||||
if (body.error_code !== this.successCode) return this.getTags()
|
||||
return {
|
||||
hotTag: this.filterInfoHotTag(body.result.hot),
|
||||
tags: this.filterTagInfo(body.result.tags),
|
||||
}
|
||||
})
|
||||
},
|
||||
filterInfoHotTag(rawList) {
|
||||
return rawList.map(item => ({
|
||||
name: item,
|
||||
id: item,
|
||||
}))
|
||||
},
|
||||
filterTagInfo(rawList) {
|
||||
return rawList.map(type => ({
|
||||
name: type.first,
|
||||
list: type.second.map(item => ({
|
||||
parent_id: type.first,
|
||||
parent_name: type.first,
|
||||
id: item,
|
||||
name: item,
|
||||
})),
|
||||
}))
|
||||
},
|
||||
|
||||
// 获取列表数据
|
||||
getList(sortId, tagId, page) {
|
||||
if (this._requestObj_list) this._requestObj_list.cancelHttp()
|
||||
this._requestObj_list = httpFatch(
|
||||
this.getListUrl(sortId, tagId, page)
|
||||
)
|
||||
return this._requestObj_list.promise.then(({ body }) => {
|
||||
if (body.error_code !== this.successCode) return this.getList(sortId, tagId, page)
|
||||
return {
|
||||
list: this.filterList(body.diyInfo),
|
||||
total: body.nums,
|
||||
page,
|
||||
limit: this.limit_list,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 格式化播放数量
|
||||
* @param {*} num
|
||||
*/
|
||||
formatPlayCount(num) {
|
||||
if (num > 100000000) return parseInt(num / 10000000) / 10 + '亿'
|
||||
if (num > 10000) return parseInt(num / 1000) / 10 + '万'
|
||||
return num
|
||||
},
|
||||
filterList(rawData) {
|
||||
return rawData.map(item => ({
|
||||
play_count: this.formatPlayCount(item.listen_num),
|
||||
id: item.list_id,
|
||||
author: item.username,
|
||||
name: item.title,
|
||||
// time: item.publish_time,
|
||||
img: item.list_pic_large || item.list_pic,
|
||||
grade: item.grade,
|
||||
desc: item.desc || item.tag,
|
||||
}))
|
||||
},
|
||||
|
||||
// 获取歌曲列表内的音乐
|
||||
getListDetail(id, page) {
|
||||
if (this._requestObj_listDetail) {
|
||||
this._requestObj_listDetail.cancelHttp()
|
||||
}
|
||||
this._requestObj_listDetail = httpFatch(this.getListDetailUrl(id, page))
|
||||
return this._requestObj_listDetail.promise.then(({ body }) => {
|
||||
if (body.error_code !== this.successCode) return this.getListDetail(id, page)
|
||||
let listData = this.filterData(body.result.songlist)
|
||||
return {
|
||||
list: listData,
|
||||
page,
|
||||
limit: this.limit_song,
|
||||
total: body.result.song_num,
|
||||
}
|
||||
})
|
||||
},
|
||||
filterData(rawList) {
|
||||
// console.log(rawList)
|
||||
return rawList.map(item => {
|
||||
const types = []
|
||||
const _types = {}
|
||||
let size = null
|
||||
let itemTypes = item.all_rate.split(',')
|
||||
if (itemTypes.includes('128')) {
|
||||
types.push({ type: '128k', size })
|
||||
_types['128k'] = {
|
||||
size,
|
||||
}
|
||||
}
|
||||
if (itemTypes.includes('320')) {
|
||||
types.push({ type: '320k', size })
|
||||
_types['320k'] = {
|
||||
size,
|
||||
}
|
||||
}
|
||||
if (itemTypes.includes('flac')) {
|
||||
types.push({ type: 'flac', size })
|
||||
_types['flac'] = {
|
||||
size,
|
||||
}
|
||||
}
|
||||
// types.reverse()
|
||||
|
||||
return {
|
||||
singer: item.author.replace(',', '、'),
|
||||
name: item.title,
|
||||
albumName: item.album_title,
|
||||
albumId: item.album_id,
|
||||
source: 'bd',
|
||||
interval: formatPlayTime(parseInt(item.file_duration)),
|
||||
songmid: item.song_id,
|
||||
img: item.pic_s500,
|
||||
lrc: null,
|
||||
types,
|
||||
_types,
|
||||
typeUrl: {},
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
// getList
|
||||
// getTags
|
||||
// getListDetail
|
||||
@@ -1,11 +1,13 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
import { headers, timeout } from '../options'
|
||||
|
||||
const api_messoer = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/kugou/url?id=${songInfo._types[type].hash}&quality=${type.replace(/k$/, '')}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
@@ -15,7 +17,8 @@ const api_messoer = {
|
||||
getPic(songInfo) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/kugou/pic?id=${songInfo.hash}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
@@ -25,7 +28,8 @@ const api_messoer = {
|
||||
getLyric(songInfo) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/kugou/lrc?id=${songInfo.hash}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body ? Promise.resolve(body) : Promise.reject(new Error(requestMsg.fail))
|
||||
41
src/renderer/utils/music/kg/api-test.js
Normal file
41
src/renderer/utils/music/kg/api-test.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
import { headers, timeout } from '../options'
|
||||
|
||||
const api_test = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`http://test.tempmusic.tk/url/kg/${songInfo._types[type].hash}/${type}`, {
|
||||
method: 'get',
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo) {
|
||||
const requestObj = httpFatch(`http://test.tempmusic.tk/pic/kg/${songInfo.hash}`, {
|
||||
method: 'get',
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
const requestObj = httpFatch(`http://test.tempmusic.tk/lrc/kg/${songInfo.hash}`, {
|
||||
method: 'get',
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_test
|
||||
@@ -1,9 +1,10 @@
|
||||
import leaderboard from './leaderboard'
|
||||
import api_source from '../api-source'
|
||||
|
||||
import songList from './songList'
|
||||
|
||||
const kg = {
|
||||
leaderboard,
|
||||
songList,
|
||||
getMusicUrl(songInfo, type) {
|
||||
return api_source('kg').getMusicUrl(songInfo, type)
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 'kgwlhgb',
|
||||
name: '网络红歌榜',
|
||||
name: '网络榜',
|
||||
bangid: '23784',
|
||||
},
|
||||
{
|
||||
@@ -30,29 +30,29 @@ export default {
|
||||
},
|
||||
{
|
||||
id: 'kggfjqb',
|
||||
name: '古风金曲榜',
|
||||
name: '古风榜',
|
||||
bangid: '33161',
|
||||
},
|
||||
{
|
||||
id: 'kgyyjqb',
|
||||
name: '粤语金曲榜',
|
||||
name: '粤语榜',
|
||||
bangid: '33165',
|
||||
},
|
||||
{
|
||||
id: 'kgomjqb',
|
||||
name: '欧美金曲榜',
|
||||
name: '欧美榜',
|
||||
bangid: '33166',
|
||||
},
|
||||
// {
|
||||
// id: 'kgdyrgb',
|
||||
// name: '电音热歌榜',
|
||||
// bangid: '33160',
|
||||
// },
|
||||
// {
|
||||
// id: 'kgjdrgb',
|
||||
// name: 'DJ热歌榜',
|
||||
// bangid: '24971',
|
||||
// },
|
||||
{
|
||||
id: 'kgdyrgb',
|
||||
name: '电音榜',
|
||||
bangid: '33160',
|
||||
},
|
||||
{
|
||||
id: 'kgjdrgb',
|
||||
name: 'DJ热歌榜',
|
||||
bangid: '24971',
|
||||
},
|
||||
// {
|
||||
// id: 'kghyxgb',
|
||||
// name: '华语新歌榜',
|
||||
|
||||
260
src/renderer/utils/music/kg/songList.js
Normal file
260
src/renderer/utils/music/kg/songList.js
Normal file
@@ -0,0 +1,260 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { formatPlayTime, sizeFormate } from '../../index'
|
||||
|
||||
export default {
|
||||
_requestObj_tags: null,
|
||||
_requestObj_listInfo: null,
|
||||
_requestObj_list: null,
|
||||
_requestObj_listRecommend: null,
|
||||
_requestObj_listDetail: null,
|
||||
currentTagInfo: {
|
||||
id: undefined,
|
||||
info: undefined,
|
||||
},
|
||||
sortList: [
|
||||
{
|
||||
name: '推荐',
|
||||
id: '5',
|
||||
},
|
||||
{
|
||||
name: '最热',
|
||||
id: '6',
|
||||
},
|
||||
{
|
||||
name: '最新',
|
||||
id: '7',
|
||||
},
|
||||
{
|
||||
name: '热藏',
|
||||
id: '3',
|
||||
},
|
||||
{
|
||||
name: '飙升',
|
||||
id: '8',
|
||||
},
|
||||
],
|
||||
regExps: {
|
||||
listData: /global\.data = (\[.+\]);/,
|
||||
},
|
||||
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&`
|
||||
},
|
||||
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}`
|
||||
},
|
||||
getSongListDetailUrl(id) {
|
||||
return `http://www2.kugou.kugou.com/yueku/v9/special/single/${id}-5-9999.html`
|
||||
},
|
||||
|
||||
/**
|
||||
* 格式化播放数量
|
||||
* @param {*} num
|
||||
*/
|
||||
formatPlayCount(num) {
|
||||
if (num > 100000000) return parseInt(num / 10000000) / 10 + '亿'
|
||||
if (num > 10000) return parseInt(num / 1000) / 10 + '万'
|
||||
return num
|
||||
},
|
||||
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.id,
|
||||
name: tag.special_name,
|
||||
})
|
||||
}
|
||||
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,
|
||||
})),
|
||||
})
|
||||
}
|
||||
return result
|
||||
},
|
||||
|
||||
getSongList(sortId, tagId, page) {
|
||||
if (this._requestObj_list) this._requestObj_list.cancelHttp()
|
||||
this._requestObj_list = httpFatch(
|
||||
this.getSongListUrl(sortId, tagId, page)
|
||||
)
|
||||
return this._requestObj_list.promise.then(({ body }) => {
|
||||
if (body.status !== 1) return this.getSongList(sortId, tagId, page)
|
||||
return this.filterList(body.special_db)
|
||||
})
|
||||
},
|
||||
getSongListRecommend() {
|
||||
if (this._requestObj_listRecommend) this._requestObj_listRecommend.cancelHttp()
|
||||
this._requestObj_listRecommend = httpFatch(
|
||||
'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()
|
||||
return this.filterList(body.data.special_list)
|
||||
})
|
||||
},
|
||||
filterList(rawData) {
|
||||
return rawData.map(item => ({
|
||||
play_count: item.total_play_count || this.formatPlayCount(item.play_count),
|
||||
id: item.specialid,
|
||||
author: item.nickname,
|
||||
name: item.specialname,
|
||||
time: item.publish_time || item.publishtime,
|
||||
img: item.img || item.imgurl,
|
||||
grade: item.grade,
|
||||
desc: item.intro,
|
||||
}))
|
||||
},
|
||||
|
||||
getListDetail(id, page) { // 获取歌曲列表内的音乐
|
||||
if (this._requestObj_listDetail) this._requestObj_listDetail.cancelHttp()
|
||||
this._requestObj_listDetail = httpFatch(this.getSongListDetailUrl(id))
|
||||
return this._requestObj_listDetail.promise.then(({ body }) => {
|
||||
let listData = body.match(this.regExps.listData)
|
||||
if (listData) listData = this.filterData(JSON.parse(RegExp.$1))
|
||||
return {
|
||||
list: listData,
|
||||
page: 1,
|
||||
limit: 10000,
|
||||
total: listData.length,
|
||||
}
|
||||
})
|
||||
},
|
||||
filterData(rawList) {
|
||||
// console.log(rawList)
|
||||
return rawList.map(item => {
|
||||
const types = []
|
||||
const _types = {}
|
||||
if (item.filesize !== 0) {
|
||||
let size = sizeFormate(item.filesize)
|
||||
types.push({ type: '128k', size, hash: item.hash })
|
||||
_types['128k'] = {
|
||||
size,
|
||||
hash: item.hash,
|
||||
}
|
||||
}
|
||||
if (item.filesize_320 !== 0) {
|
||||
let size = sizeFormate(item.filesize_320)
|
||||
types.push({ type: '320k', size, hash: item.hash_320 })
|
||||
_types['320k'] = {
|
||||
size,
|
||||
hash: item.hash_320,
|
||||
}
|
||||
}
|
||||
if (item.filesize_ape !== 0) {
|
||||
let size = sizeFormate(item.filesize_ape)
|
||||
types.push({ type: 'ape', size, hash: item.hash_ape })
|
||||
_types.ape = {
|
||||
size,
|
||||
hash: item.hash_ape,
|
||||
}
|
||||
}
|
||||
if (item.filesize_flac !== 0) {
|
||||
let size = sizeFormate(item.filesize_flac)
|
||||
types.push({ type: 'flac', size, hash: item.hash_flac })
|
||||
_types.flac = {
|
||||
size,
|
||||
hash: item.hash_flac,
|
||||
}
|
||||
}
|
||||
return {
|
||||
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,
|
||||
hash: item.hash,
|
||||
types,
|
||||
_types,
|
||||
typeUrl: {},
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 获取列表信息
|
||||
getListInfo(tagId) {
|
||||
if (this._requestObj_listInfo) this._requestObj_listInfo.cancelHttp()
|
||||
this._requestObj_listInfo = httpFatch(this.getInfoUrl(tagId))
|
||||
return this._requestObj_listInfo.promise.then(({ body }) => {
|
||||
if (body.status !== 1) return this.getListInfo(tagId)
|
||||
return {
|
||||
limit: body.data.params.pagesize,
|
||||
page: body.data.params.p,
|
||||
total: body.data.params.total,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 获取列表数据
|
||||
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) tasks.push(this.getSongListRecommend()) // 如果是所有类别,则顺便获取推荐列表
|
||||
return Promise.all(tasks).then(([list, info, recommendList]) => {
|
||||
if (recommendList) list.unshift(...recommendList)
|
||||
return {
|
||||
list,
|
||||
...info,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 获取标签
|
||||
getTags() {
|
||||
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
|
||||
this._requestObj_tags = httpFatch(this.getInfoUrl())
|
||||
return this._requestObj_tags.promise.then(({ body }) => {
|
||||
if (body.status !== 1) return this.getTags()
|
||||
return {
|
||||
hotTag: this.filterInfoHotTag(body.data.hotTag),
|
||||
tags: this.filterTagInfo(body.data.tagids),
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
// getList
|
||||
// getTags
|
||||
// getListDetail
|
||||
@@ -1,11 +1,13 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
import { headers, timeout } from '../options'
|
||||
|
||||
const api_messoer = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/kuwo/url?id=${songInfo.songmid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
@@ -15,7 +17,8 @@ const api_messoer = {
|
||||
getPic(songInfo) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/kuwo/pic?id=${songInfo.songmid}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
@@ -1,24 +1,18 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { headers, timeout } from '../options'
|
||||
|
||||
const api_temp = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://www.stsky.cn/api/temp/getMusicUrl.php?id=${songInfo.songmid}&type=${type}`, {
|
||||
const requestObj = httpFatch(`http://45.32.53.128:3002/m/kw/u/${songInfo.songmid}/${type}`, {
|
||||
method: 'get',
|
||||
headers,
|
||||
timeout,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(body.msg))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo) {
|
||||
const requestObj = httpFatch(`https://www.stsky.cn/api/temp/getPic.php?size=320&songmid=${songInfo.songmid}`, {
|
||||
method: 'get',
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(body.msg))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_temp
|
||||
|
||||
30
src/renderer/utils/music/kw/api-test.js
Normal file
30
src/renderer/utils/music/kw/api-test.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
import { headers, timeout } from '../options'
|
||||
|
||||
const api_test = {
|
||||
// getMusicUrl(songInfo, type) {
|
||||
// const requestObj = httpFatch(`http://45.32.53.128:3002/m/kw/u/${songInfo.songmid}/${type}`, {
|
||||
// method: 'get',
|
||||
// headers,
|
||||
// timeout,
|
||||
// })
|
||||
// requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
// return body.code === 0 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(body.msg))
|
||||
// })
|
||||
// return requestObj
|
||||
// },
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`http://test.tempmusic.tk/url/kw/${songInfo.songmid}/${type}`, {
|
||||
method: 'get',
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_test
|
||||
@@ -4,7 +4,9 @@ import musicSearch from './musicSearch'
|
||||
import { formatSinger } from './util'
|
||||
import leaderboard from './leaderboard'
|
||||
import lyric from './lyric'
|
||||
import pic from './pic'
|
||||
import api_source from '../api-source'
|
||||
import songList from './songList'
|
||||
|
||||
const kw = {
|
||||
_musicInfoRequestObj: null,
|
||||
@@ -31,6 +33,7 @@ const kw = {
|
||||
tempSearch,
|
||||
musicSearch,
|
||||
leaderboard,
|
||||
songList,
|
||||
getLyric(songInfo) {
|
||||
// let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer
|
||||
return lyric.getLyric(songInfo.songmid)
|
||||
@@ -91,7 +94,7 @@ const kw = {
|
||||
},
|
||||
|
||||
getPic(songInfo) {
|
||||
return api_source('kw').getPic(songInfo)
|
||||
return pic.getPic(songInfo)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { httpGet, cancelHttp } from '../../request'
|
||||
import { formatPlayTime } from '../../index'
|
||||
import { formatPlayTime, decodeName } from '../../index'
|
||||
import { formatSinger } from './util'
|
||||
|
||||
|
||||
export default {
|
||||
list: [
|
||||
@@ -143,9 +145,9 @@ export default {
|
||||
}
|
||||
// types.reverse()
|
||||
return {
|
||||
singer: item.artist,
|
||||
name: item.name,
|
||||
albumName: item.album,
|
||||
singer: formatSinger(decodeName(item.artist)),
|
||||
name: decodeName(item.name),
|
||||
albumName: decodeName(item.album),
|
||||
albumId: item.albumid,
|
||||
songmid: item.id,
|
||||
source: 'kw',
|
||||
|
||||
9
src/renderer/utils/music/kw/pic.js
Normal file
9
src/renderer/utils/music/kw/pic.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import { httpFatch } from '../../request'
|
||||
|
||||
export default {
|
||||
getPic({ songmid }) {
|
||||
const requestObj = httpFatch(`http://artistpicserver.kuwo.cn/pic.web?corp=kuwo&type=rid_pic&pictype=500&size=500&rid=${songmid}`)
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => body)
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
217
src/renderer/utils/music/kw/songList.js
Normal file
217
src/renderer/utils/music/kw/songList.js
Normal file
@@ -0,0 +1,217 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { formatPlayTime, decodeName } from '../../index'
|
||||
import { formatSinger } from './util'
|
||||
|
||||
export default {
|
||||
_requestObj_tags: null,
|
||||
_requestObj_hotTags: null,
|
||||
_requestObj_list: null,
|
||||
_requestObj_listDetail: null,
|
||||
limit_list: 25,
|
||||
limit_song: 100,
|
||||
successCode: 200,
|
||||
sortList: [
|
||||
{
|
||||
name: '最新',
|
||||
id: 'new',
|
||||
},
|
||||
{
|
||||
name: '最热',
|
||||
id: 'hot',
|
||||
},
|
||||
],
|
||||
tagsUrl: 'http://wapi.kuwo.cn/api/pc/classify/playlist/getTagList?cmd=rcm_keyword_playlist&user=0&prod=kwplayer_pc_9.0.5.0&vipver=9.0.5.0&source=kwplayer_pc_9.0.5.0&loginUid=0&loginSid=0&appUid=76039576',
|
||||
hotTagUrl: 'http://wapi.kuwo.cn/api/pc/classify/playlist/getRcmTagList?loginUid=0&loginSid=0&appUid=76039576',
|
||||
getListUrl({ sortId, id, type, page }) {
|
||||
console.log(id, type)
|
||||
if (!id) return `http://wapi.kuwo.cn/api/pc/classify/playlist/getRcmPlayList?loginUid=0&loginSid=0&appUid=76039576&&pn=${page}&rn=${this.limit_list}&order=${sortId}`
|
||||
switch (type) {
|
||||
case '10000': return `http://wapi.kuwo.cn/api/pc/classify/playlist/getTagPlayList?loginUid=0&loginSid=0&appUid=76039576&pn=${page}&id=${id}&rn=${this.limit_list}`
|
||||
case '43': return `http://mobileinterfaces.kuwo.cn/er.s?type=get_pc_qz_data&f=web&id=${id}&prod=pc`
|
||||
}
|
||||
// http://wapi.kuwo.cn/api/pc/classify/playlist/getTagPlayList?loginUid=0&loginSid=0&appUid=76039576&id=173&pn=1&rn=100
|
||||
},
|
||||
getListDetailUrl(id, page) {
|
||||
// http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=2858093057&pn=0&rn=100&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1
|
||||
return `http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=${id}&pn=${page - 1}&rn=${this.limit_song}&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1`
|
||||
// http://mobileinterfaces.kuwo.cn/er.s?type=get_pc_qz_data&f=web&id=140&prod=pc
|
||||
},
|
||||
|
||||
// http://nplserver.kuwo.cn/pl.svc?op=getlistinfo&pid=2849349915&pn=0&rn=100&encode=utf8&keyset=pl2012&identity=kuwo&pcmp4=1&vipver=MUSIC_9.0.5.0_W1&newver=1
|
||||
// 获取标签
|
||||
getTag() {
|
||||
if (this._requestObj_tags) this._requestObj_tags.cancelHttp()
|
||||
this._requestObj_tags = httpFatch(this.tagsUrl)
|
||||
return this._requestObj_tags.promise.then(({ body }) => {
|
||||
if (body.code !== this.successCode) return this.getTag()
|
||||
return this.filterTagInfo(body.data)
|
||||
})
|
||||
},
|
||||
// 获取标签
|
||||
getHotTag() {
|
||||
if (this._requestObj_hotTags) this._requestObj_hotTags.cancelHttp()
|
||||
this._requestObj_hotTags = httpFatch(this.hotTagUrl)
|
||||
return this._requestObj_hotTags.promise.then(({ body }) => {
|
||||
if (body.code !== this.successCode) return this.getHotTag()
|
||||
return this.filterInfoHotTag(body.data[0].data)
|
||||
})
|
||||
},
|
||||
filterInfoHotTag(rawList) {
|
||||
return rawList.map(item => ({
|
||||
id: `${item.id}-${item.digest}`,
|
||||
name: item.name,
|
||||
}))
|
||||
},
|
||||
filterTagInfo(rawList) {
|
||||
return rawList.map(type => ({
|
||||
name: type.name,
|
||||
list: type.data.map(item => ({
|
||||
parent_id: type.id,
|
||||
parent_name: type.name,
|
||||
id: `${item.id}-${item.digest}`,
|
||||
name: item.name,
|
||||
})),
|
||||
}))
|
||||
},
|
||||
|
||||
// 获取列表数据
|
||||
getList(sortId, tagId, page) {
|
||||
if (this._requestObj_list) this._requestObj_list.cancelHttp()
|
||||
let id
|
||||
let type
|
||||
if (tagId) {
|
||||
let arr = tagId.split('-')
|
||||
id = arr[0]
|
||||
type = arr[1]
|
||||
} else {
|
||||
id = null
|
||||
}
|
||||
console.log(id, type)
|
||||
this._requestObj_list = httpFatch(this.getListUrl({ sortId, id, type, page }))
|
||||
return this._requestObj_list.promise.then(({ body }) => {
|
||||
if (!id || type == '10000') {
|
||||
if (body.code !== this.successCode) return this.getListUrl({ sortId, id, type, page })
|
||||
return {
|
||||
list: this.filterList(body.data.data),
|
||||
total: body.data.total,
|
||||
page: body.data.pn,
|
||||
limit: body.data.rn,
|
||||
}
|
||||
} else if (!body.length) {
|
||||
return this.getListUrl({ sortId, id, type, page })
|
||||
}
|
||||
return {
|
||||
list: this.filterList2(body),
|
||||
total: 1000,
|
||||
page,
|
||||
limit: 1000,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 格式化播放数量
|
||||
* @param {*} num
|
||||
*/
|
||||
formatPlayCount(num) {
|
||||
if (num > 100000000) return parseInt(num / 10000000) / 10 + '亿'
|
||||
if (num > 10000) return parseInt(num / 1000) / 10 + '万'
|
||||
return num
|
||||
},
|
||||
filterList(rawData) {
|
||||
return rawData.map(item => ({
|
||||
play_count: this.formatPlayCount(item.listencnt),
|
||||
id: item.id,
|
||||
author: item.uname,
|
||||
name: item.name,
|
||||
// time: item.publish_time,
|
||||
img: item.img,
|
||||
grade: item.favorcnt / 10,
|
||||
desc: item.desc,
|
||||
}))
|
||||
},
|
||||
filterList2(rawData) {
|
||||
const list = []
|
||||
rawData.forEach(item => {
|
||||
if (!item.label) return
|
||||
list.push(...item.list.map(item => ({
|
||||
play_count: item.play_count === undefined ? null : this.formatPlayCount(item.listencnt),
|
||||
id: item.id,
|
||||
author: item.uname,
|
||||
name: item.name,
|
||||
// time: item.publish_time,
|
||||
img: item.img,
|
||||
grade: item.favorcnt / 10,
|
||||
desc: item.desc,
|
||||
})))
|
||||
})
|
||||
console.log(list)
|
||||
return list
|
||||
},
|
||||
|
||||
// 获取歌曲列表内的音乐
|
||||
getListDetail(id, page) {
|
||||
if (this._requestObj_listDetail) {
|
||||
this._requestObj_listDetail.cancelHttp()
|
||||
}
|
||||
this._requestObj_listDetail = httpFatch(this.getListDetailUrl(id, page))
|
||||
return this._requestObj_listDetail.promise.then(({ body }) => {
|
||||
if (body.result !== 'ok') return this.getListDetail(id, page)
|
||||
return {
|
||||
list: this.filterListDetail(body.musiclist),
|
||||
page,
|
||||
limit: body.rn,
|
||||
total: body.total,
|
||||
}
|
||||
})
|
||||
},
|
||||
filterListDetail(rawData) {
|
||||
// console.log(rawList)
|
||||
return rawData.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('MP3H')) {
|
||||
types.push({ type: '320k', size: null })
|
||||
_types['320k'] = {
|
||||
size: null,
|
||||
}
|
||||
}
|
||||
if (formats.indexOf('ALFLAC')) {
|
||||
types.push({ type: 'flac', size: null })
|
||||
_types['flac'] = {
|
||||
size: null,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
singer: formatSinger(decodeName(item.artist)),
|
||||
name: decodeName(item.name),
|
||||
albumName: decodeName(item.album),
|
||||
albumId: item.albumid,
|
||||
songmid: item.id,
|
||||
source: 'kw',
|
||||
interval: formatPlayTime(parseInt(item.duration)),
|
||||
img: null,
|
||||
lrc: null,
|
||||
types,
|
||||
_types,
|
||||
typeUrl: {},
|
||||
}
|
||||
})
|
||||
},
|
||||
getTags() {
|
||||
return Promise.all([this.getTag(), this.getHotTag()]).then(([tags, hotTag]) => ({ tags, hotTag }))
|
||||
},
|
||||
}
|
||||
|
||||
// getList
|
||||
// getTags
|
||||
// getListDetail
|
||||
9
src/renderer/utils/music/options.js
Normal file
9
src/renderer/utils/music/options.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export const bHh = '624868746c'
|
||||
|
||||
export const headers = {
|
||||
'User-Agent': 'lx-music request',
|
||||
[bHh]: [bHh],
|
||||
}
|
||||
|
||||
|
||||
export const timeout = 15000
|
||||
@@ -1,11 +1,13 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
import { headers, timeout } from '../options'
|
||||
|
||||
const api_messoer = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/tencent/url?id=${songInfo.strMediaMid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
24
src/renderer/utils/music/tx/api-test.js
Normal file
24
src/renderer/utils/music/tx/api-test.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
import { headers, timeout } from '../messoer'
|
||||
|
||||
const api_messoer = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/tencent/url?id=${songInfo.strMediaMid}&quality=${type.replace(/k$/, '')}`, {
|
||||
method: 'get',
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo) {
|
||||
return {
|
||||
promise: Promise.resolve(`https://y.gtimg.cn/music/photo_new/T002R500x500M000${songInfo.albumId}.jpg`),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default api_messoer
|
||||
@@ -7,7 +7,7 @@
|
||||
const types = ['flac', 'ape', '320k', '192k', '128k']
|
||||
export const getMusicType = (info, type) => {
|
||||
switch (window.globalObj.apiSource) {
|
||||
case 'kg':
|
||||
// case 'kg':
|
||||
case 'wy':
|
||||
return '128k'
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
import { headers, timeout } from '../options'
|
||||
|
||||
const api_messoer = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/netease/url?id=${songInfo.songmid}&quality=${type.replace(/k$/, '')}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
@@ -15,7 +17,8 @@ const api_messoer = {
|
||||
getPic(songInfo) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/netease/pic?id=${songInfo.songmid}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 200 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
@@ -25,7 +28,8 @@ const api_messoer = {
|
||||
getLyric(songInfo) {
|
||||
const requestObj = httpFatch(`https://v1.itooi.cn/netease/lrc?id=${songInfo.songmid}&isRedirect=0`, {
|
||||
method: 'get',
|
||||
timeout: 5000,
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body ? Promise.resolve(body) : Promise.reject(new Error(requestMsg.fail))
|
||||
41
src/renderer/utils/music/wy/api-test.js
Normal file
41
src/renderer/utils/music/wy/api-test.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { httpFatch } from '../../request'
|
||||
import { requestMsg } from '../../message'
|
||||
import { headers, timeout } from '../options'
|
||||
|
||||
const api_test = {
|
||||
getMusicUrl(songInfo, type) {
|
||||
const requestObj = httpFatch(`http://test.tempmusic.tk/url/wy/${songInfo.songmid}/${type}`, {
|
||||
method: 'get',
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getPic(songInfo) {
|
||||
const requestObj = httpFatch(`http://test.tempmusic.tk/pic/wy/${songInfo.songmid}`, {
|
||||
method: 'get',
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
getLyric(songInfo) {
|
||||
const requestObj = httpFatch(`http://test.tempmusic.tk/lrc/wy/${songInfo.songmid}`, {
|
||||
method: 'get',
|
||||
timeout,
|
||||
headers,
|
||||
})
|
||||
requestObj.promise = requestObj.promise.then(({ body }) => {
|
||||
return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail))
|
||||
})
|
||||
return requestObj
|
||||
},
|
||||
}
|
||||
|
||||
export default api_test
|
||||
@@ -2,28 +2,11 @@ import request from 'request'
|
||||
// import progress from 'request-progress'
|
||||
import { debugRequest } from './env'
|
||||
import { requestMsg } from './message'
|
||||
import { bHh } from './music/options'
|
||||
// import fs from 'fs'
|
||||
|
||||
const headers = {
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
|
||||
}
|
||||
|
||||
const fatchData = (url, method, options, callback) => {
|
||||
// console.log(url, options)
|
||||
console.log('---start---', url)
|
||||
return request(url, {
|
||||
method,
|
||||
headers: Object.assign({}, headers, options.headers || {}),
|
||||
Origin: options.origin,
|
||||
data: options.data,
|
||||
timeout: options.timeout || 10000,
|
||||
json: options.format === undefined || options.format === 'json',
|
||||
}, (err, resp, body) => {
|
||||
if (err) return callback(err, null)
|
||||
|
||||
// console.log('---end---', url)
|
||||
callback(null, resp, body)
|
||||
})
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,3 +214,28 @@ export const http_jsonp = (url, options, callback) => {
|
||||
callback(err, resp, body)
|
||||
})
|
||||
}
|
||||
|
||||
const fatchData = (url, method, options, callback) => {
|
||||
// console.log(url, options)
|
||||
console.log('---start---', url)
|
||||
if (options.headers && options.headers[bHh]) {
|
||||
let s = Buffer.from(bHh, 'hex').toString()
|
||||
s = s.replace(s.substr(-1), '')
|
||||
s = Buffer.from(s, 'base64').toString()
|
||||
options.headers[s] = !!s
|
||||
delete options.headers[bHh]
|
||||
}
|
||||
return request(url, {
|
||||
method,
|
||||
headers: Object.assign({}, headers, options.headers || {}),
|
||||
Origin: options.origin,
|
||||
body: options.body,
|
||||
form: options.form,
|
||||
formData: options.formData,
|
||||
timeout: options.timeout || 10000,
|
||||
json: options.format === undefined || options.format === 'json',
|
||||
}, (err, resp, body) => {
|
||||
if (err) return callback(err, null)
|
||||
callback(null, resp, body)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,51 +1,15 @@
|
||||
<template lang="pug">
|
||||
div(:class="$style.leaderboard")
|
||||
div(:class="$style.header")
|
||||
material-tab(:class="$style.tab" :list="types" item-key="id" item-name="name" v-model="tabId")
|
||||
material-tab(:class="$style.tab" :list="types" align="left" item-key="id" item-name="name" v-model="tabId")
|
||||
material-select(:class="$style.select" :list="sourceInfo.sources" item-key="id" item-name="name" v-model="source")
|
||||
div(:class="$style.content")
|
||||
div(v-if="list.length" :class="$style.list")
|
||||
div(:class="$style.thead")
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th.nobreak.center(style="width: 37px;")
|
||||
material-checkbox(id="search_select_all" v-model="isSelectAll" @change="handleSelectAllData"
|
||||
:indeterminate="isIndeterminate" :title="isSelectAll && !isIndeterminate ? '全不选' : '全选'")
|
||||
th.nobreak(style="width: 25%;") 歌曲名
|
||||
th.nobreak(style="width: 20%;") 歌手
|
||||
th.nobreak(style="width: 22%;") 专辑
|
||||
th.nobreak(style="width: 18%;") 操作
|
||||
th.nobreak(style="width: 10%;") 时长
|
||||
div.scroll(:class="$style.tbody" ref="dom_scrollContent")
|
||||
table
|
||||
tbody
|
||||
tr(v-for='(item, index) in list' :key='item.songmid' @click="handleDoubleClick(index)")
|
||||
td.nobreak.center(style="width: 37px;" @click.stop)
|
||||
material-checkbox(:id="index.toString()" v-model="selectdData" :value="item")
|
||||
td.break(style="width: 25%;")
|
||||
| {{item.name}}
|
||||
//- span.badge.badge-info(v-if="item._types['320k']") 高品质
|
||||
//- span.badge.badge-success(v-if="item._types.ape || item._types.flac") 无损
|
||||
td.break(style="width: 20%;") {{item.singer}}
|
||||
td.break(style="width: 22%;") {{item.albumName}}
|
||||
td(style="width: 18%;")
|
||||
material-list-buttons(:index="index" :search-btn="true" :play-btn="item.source == 'kw' || !isAPITemp" :download-btn="item.source == 'kw' || !isAPITemp" :remove-btn="false" @btn-click="handleListBtnClick")
|
||||
//- button.btn-info(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k'] || item._types.flac" @click.stop='openDownloadModal(index)') 下载
|
||||
//- button.btn-secondary(type='button' v-if="item._types['128k'] || item._types['192k'] || item._types['320k']" @click.stop='testPlay(index)') 试听
|
||||
//- button.btn-success(type='button' v-if="(item._types['128k'] || item._types['192k'] || item._types['320k']) && userInfo" @click.stop='showListModal(index)') +
|
||||
td(style="width: 10%;") {{item.interval || '--/--'}}
|
||||
div(:class="$style.pagination")
|
||||
material-pagination(:count="info.total" :limit="info.limit" :page="info.page" @btn-click="handleTogglePage")
|
||||
material-song-list(v-model="selectdData" @action="handleSongListAction" :source="source" :page="page" :limit="info.limit" :total="info.total" :list="list")
|
||||
material-download-modal(:show="isShowDownload" :musicInfo="musicInfo" @select="handleAddDownload" @close="isShowDownload = false")
|
||||
material-download-multiple-modal(:show="isShowDownloadMultiple" :list="selectdData" @select="handleAddDownloadMultiple" @close="isShowDownloadMultiple = false")
|
||||
material-flow-btn(:show="isShowEditBtn && (source == 'kw' || !isAPITemp)" :remove-btn="false" @btn-click="handleFlowBtnClick")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations, mapActions } from 'vuex'
|
||||
import { scrollTo } from '../utils'
|
||||
// import music from '../utils/music'
|
||||
export default {
|
||||
name: 'Leaderboard',
|
||||
data() {
|
||||
@@ -53,14 +17,9 @@ export default {
|
||||
tabId: null,
|
||||
source: null,
|
||||
page: 1,
|
||||
clickTime: 0,
|
||||
clickIndex: -1,
|
||||
isShowDownload: false,
|
||||
musicInfo: null,
|
||||
selectdData: [],
|
||||
isSelectAll: false,
|
||||
isIndeterminate: false,
|
||||
isShowEditBtn: false,
|
||||
isShowDownloadMultiple: false,
|
||||
}
|
||||
},
|
||||
@@ -81,27 +40,12 @@ export default {
|
||||
if (!o && this.page !== 1) return
|
||||
this.getList(1).then(() => {
|
||||
this.page = this.info.page
|
||||
scrollTo(this.$refs.dom_scrollContent, 0)
|
||||
})
|
||||
},
|
||||
source(n, o) {
|
||||
this.setLeaderboard({ source: n })
|
||||
if (o) this.tabId = this.types[0] && this.types[0].id
|
||||
},
|
||||
selectdData(n) {
|
||||
const len = n.length
|
||||
if (len) {
|
||||
this.isSelectAll = true
|
||||
this.isIndeterminate = len !== this.list.length
|
||||
this.isShowEditBtn = true
|
||||
} else {
|
||||
this.isSelectAll = false
|
||||
this.isShowEditBtn = false
|
||||
}
|
||||
},
|
||||
list() {
|
||||
this.resetSelect()
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.source = this.setting.leaderboard.source
|
||||
@@ -114,19 +58,6 @@ export default {
|
||||
...mapActions('download', ['createDownload', 'createDownloadMultiple']),
|
||||
...mapMutations('list', ['defaultListAdd', 'defaultListAddMultiple']),
|
||||
...mapMutations('player', ['setList']),
|
||||
handleDoubleClick(index) {
|
||||
if (
|
||||
window.performance.now() - this.clickTime > 400 ||
|
||||
this.clickIndex !== index
|
||||
) {
|
||||
this.clickTime = window.performance.now()
|
||||
this.clickIndex = index
|
||||
return
|
||||
}
|
||||
(this.source == 'kw' || !this.isAPITemp) ? this.testPlay(index) : this.handleSearch(index)
|
||||
this.clickTime = 0
|
||||
this.clickIndex = -1
|
||||
},
|
||||
handleListBtnClick(info) {
|
||||
switch (info.action) {
|
||||
case 'download':
|
||||
@@ -150,6 +81,7 @@ export default {
|
||||
if (index == null) {
|
||||
targetSong = this.selectdData[0]
|
||||
this.defaultListAddMultiple(this.selectdData)
|
||||
this.resetSelect()
|
||||
} else {
|
||||
targetSong = this.list[index]
|
||||
this.defaultListAdd(targetSong)
|
||||
@@ -177,31 +109,21 @@ export default {
|
||||
handleTogglePage(page) {
|
||||
this.getList(page).then(() => {
|
||||
this.page = this.info.page
|
||||
this.$nextTick(() => {
|
||||
scrollTo(this.$refs.dom_scrollContent, 0)
|
||||
})
|
||||
})
|
||||
},
|
||||
handleSelectAllData(isSelect) {
|
||||
this.selectdData = isSelect ? [...this.list] : []
|
||||
},
|
||||
resetSelect() {
|
||||
this.isSelectAll = false
|
||||
this.selectdData = []
|
||||
},
|
||||
handleAddDownload(type) {
|
||||
this.createDownload({ musicInfo: this.musicInfo, type })
|
||||
this.isShowDownload = false
|
||||
},
|
||||
handleAddDownloadMultiple(type) {
|
||||
switch (this.source) {
|
||||
case 'kg':
|
||||
// case 'kg':
|
||||
case 'wy':
|
||||
type = '128k'
|
||||
}
|
||||
this.createDownloadMultiple({ list: [...this.selectdData], type })
|
||||
this.resetSelect()
|
||||
this.isShowDownloadMultiple = false
|
||||
this.resetSelect()
|
||||
},
|
||||
handleFlowBtnClick(action) {
|
||||
switch (action) {
|
||||
@@ -210,7 +132,6 @@ export default {
|
||||
break
|
||||
case 'play':
|
||||
this.testPlay()
|
||||
this.resetSelect()
|
||||
break
|
||||
case 'add':
|
||||
this.defaultListAddMultiple(this.selectdData)
|
||||
@@ -218,6 +139,23 @@ export default {
|
||||
break
|
||||
}
|
||||
},
|
||||
handleSongListAction({ action, data }) {
|
||||
switch (action) {
|
||||
case 'listBtnClick':
|
||||
return this.handleListBtnClick(data)
|
||||
case 'togglePage':
|
||||
return this.handleTogglePage(data)
|
||||
case 'flowBtnClick':
|
||||
return this.handleFlowBtnClick(data)
|
||||
case 'testPlay':
|
||||
return this.testPlay(data)
|
||||
case 'search':
|
||||
return this.handleSearch(data)
|
||||
}
|
||||
},
|
||||
resetSelect() {
|
||||
this.selectdData = []
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -250,46 +188,5 @@ export default {
|
||||
overflow: hidden;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
.list {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
// table {
|
||||
// position: relative;
|
||||
// thead {
|
||||
// position: fixed;
|
||||
// width: 100%;
|
||||
// th {
|
||||
// width: 100%;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
.thead {
|
||||
flex: none;
|
||||
}
|
||||
.tbody {
|
||||
flex: auto;
|
||||
overflow-y: auto;
|
||||
td {
|
||||
font-size: 12px;
|
||||
:global(.badge) {
|
||||
margin-right: 3px;
|
||||
&:first-child {
|
||||
margin-left: 3px;
|
||||
}
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.pagination {
|
||||
text-align: center;
|
||||
padding: 15px 0;
|
||||
// left: 50%;
|
||||
// transform: translateX(-50%);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
table
|
||||
tbody
|
||||
tr(v-for='(item, index) in list' :key='item.songmid'
|
||||
@click="handleDoubleClick(index)" :class="[isPlayList && playIndex === index ? $style.active : '', isAPITemp && item.source != 'kw' ? $style.disabled : '']")
|
||||
@click="handleDoubleClick(index)" :class="[isPlayList && playIndex === index ? $style.active : '', (isAPITemp && item.source != 'kw') || item.source == 'tx' ? $style.disabled : '']")
|
||||
td.nobreak.center(style="width: 37px;" @click.stop)
|
||||
material-checkbox(:id="index.toString()" v-model="selectdData" :value="item")
|
||||
td.break(style="width: 25%;") {{item.name}}
|
||||
@@ -137,6 +137,7 @@ export default {
|
||||
this.clickIndex = -1
|
||||
},
|
||||
testPlay(index) {
|
||||
if ((this.isAPITemp && this.list[index].source != 'kw') || this.list[index].source == 'tx') return
|
||||
this.setList({ list: this.list, listId: 'test', index })
|
||||
},
|
||||
handleRemove(index) {
|
||||
@@ -146,14 +147,13 @@ export default {
|
||||
switch (info.action) {
|
||||
case 'download':
|
||||
const minfo = this.list[info.index]
|
||||
if (this.isAPITemp && minfo.source != 'kw') return
|
||||
if ((this.isAPITemp && minfo.source != 'kw') || minfo.source == 'tx') return
|
||||
this.musicInfo = minfo
|
||||
this.$nextTick(() => {
|
||||
this.isShowDownload = true
|
||||
})
|
||||
break
|
||||
case 'play':
|
||||
if (this.isAPITemp && this.list[info.index].source != 'kw') return
|
||||
this.testPlay(info.index)
|
||||
break
|
||||
case 'remove':
|
||||
@@ -173,7 +173,7 @@ export default {
|
||||
this.selectdData = []
|
||||
},
|
||||
handleAddDownloadMultiple(type) {
|
||||
const list = this.setting.apiSource == 'temp' ? this.selectdData.filter(s => s.source == 'kw') : [...this.selectdData]
|
||||
const list = this.setting.apiSource == 'temp' ? this.selectdData.filter(s => s.source == 'kw') : this.selectdData.filter(s => s.source != 'tx')
|
||||
this.createDownloadMultiple({ list, type })
|
||||
this.resetSelect()
|
||||
this.isShowDownloadMultiple = false
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
h2 推荐
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'About',
|
||||
data() {
|
||||
return {
|
||||
count: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add() {
|
||||
this.count++
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -74,14 +74,19 @@ div.scroll(:class="$style.setting")
|
||||
p.small 当前版本:{{version.version}}
|
||||
p.small(v-if="version.newVersion")
|
||||
span(v-if="isLatestVer") 软件已是最新,尽情地体验吧~🥂
|
||||
material-btn(v-else-if="setting.ignoreVersion" :class="[$style.btn, $style.gapLeft]" min @click="showUpdateModal") 打开更新窗口
|
||||
material-btn(v-else-if="setting.ignoreVersion || version.isError" :class="[$style.btn, $style.gapLeft]" min @click="showUpdateModal") 打开更新窗口 🚀
|
||||
span(v-else) 发现新版本并在努力下载中,请稍等...⏳
|
||||
p.small(v-else) 检查更新中...
|
||||
dt 关于洛雪音乐
|
||||
dd
|
||||
p.small
|
||||
| 本软件完全免费,代码已开源,开源地址:
|
||||
span.hover(@click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop')") https://github.com/lyswhut/lx-music-desktop
|
||||
span.hover(title="点击打开" @click="handleOpenUrl('https://github.com/lyswhut/lx-music-desktop#readme')") https://github.com/lyswhut/lx-music-desktop
|
||||
p.small
|
||||
| 最新版网盘下载地址(网盘内有MAC、windows版):
|
||||
span.hover(title="点击打开" @click="handleOpenUrl('https://www.lanzous.com/b906260/')") https://www.lanzous.com/b906260/
|
||||
| 密码:
|
||||
span.hover(title="点击复制" @click="clipboardWriteText('glqw')") glqw
|
||||
p.small
|
||||
| 本软件仅用于学习交流使用,禁止将本软件用于
|
||||
strong 非法用途
|
||||
@@ -93,14 +98,18 @@ div.scroll(:class="$style.setting")
|
||||
strong 使用者
|
||||
| 承担!
|
||||
p.small
|
||||
| 本软件的部分接口使用自 https://github.com/messoer ,非常感谢
|
||||
| 怀念曾经的
|
||||
strong @messoer
|
||||
| !
|
||||
| ,非常感谢曾经为本软件提供数据源!
|
||||
p.small 若有问题可 mail to:lyswhut@qq.com 或到 GitHub 提交 issue
|
||||
p.small
|
||||
| 若觉得好用的话可以去 GitHub 点个
|
||||
strong star
|
||||
| 支持作者哦~~🍻
|
||||
p
|
||||
span 如果你资金充裕,还可以
|
||||
material-btn(@click="handleOpenUrl('https://cdn.stsky.cn/qrc.png')" min title="土豪,你好 🙂") 打赏下作者
|
||||
span ,以帮我分担点服务器费用~❤️
|
||||
p
|
||||
small By:
|
||||
| 落雪无痕
|
||||
@@ -108,7 +117,7 @@ div.scroll(:class="$style.setting")
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
import { openDirInExplorer, openSelectDir, openSaveDir, updateSetting, openUrl } from '../utils'
|
||||
import { openDirInExplorer, openSelectDir, openSaveDir, updateSetting, openUrl, clipboardWriteText } from '../utils'
|
||||
import { rendererSend } from '../../common/icp'
|
||||
import fs from 'fs'
|
||||
|
||||
@@ -162,16 +171,26 @@ export default {
|
||||
},
|
||||
],
|
||||
apiSources: [
|
||||
// {
|
||||
// id: 'messoer',
|
||||
// // label: '由 messoer 提供的接口(推荐,软件的所有功能都可用)',
|
||||
// label: '由 messoer 提供的接口(该接口已关闭)',
|
||||
// disabled: true,
|
||||
// },
|
||||
// {
|
||||
// id: 'internal',
|
||||
// label: '内置接口(只能试听或下载128k音质,该接口支持软件的所有功能)',
|
||||
// disabled: false,
|
||||
// },
|
||||
{
|
||||
id: 'messoer',
|
||||
label: '由 messoer 提供的接口(推荐,软件的所有功能都可用)',
|
||||
id: 'test',
|
||||
label: '测试接口(软件的大部分功能可用,该接口访问速度略慢)',
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: 'temp',
|
||||
// label: '临时接口(软件的某些功能将不可用,建议在messoer不可用时再切换到本选项)',
|
||||
label: '临时接口(因服务器被攻击,本接口已关闭)',
|
||||
disabled: true,
|
||||
label: '临时接口(软件的某些功能不可用,该接口比测试接口快一些,建议测试接口不可用再使用本接口)',
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
musicNames: [
|
||||
@@ -369,6 +388,12 @@ export default {
|
||||
showUpdateModal() {
|
||||
this.setVersionModalVisible({ isShow: true })
|
||||
},
|
||||
clipboardWriteText(text) {
|
||||
clipboardWriteText(text)
|
||||
},
|
||||
openRewardModal() {
|
||||
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
388
src/renderer/views/SongList.vue
Normal file
388
src/renderer/views/SongList.vue
Normal file
@@ -0,0 +1,388 @@
|
||||
<template lang="pug">
|
||||
div(:class="$style.leaderboard")
|
||||
div(:class="$style.header")
|
||||
material-tag-list(:class="$style.tagList" :list="tagList" v-model="tagInfo")
|
||||
material-tab(:class="$style.tab" :list="sorts" item-key="id" item-name="name" v-model="sortId")
|
||||
material-select(:class="$style.select" :list="sourceInfo.sources" item-key="id" item-name="name" v-model="source")
|
||||
div(:class="$style.container")
|
||||
transition(enter-active-class="animated-fast fadeIn" leave-active-class="animated-fast fadeOut")
|
||||
div(:class="$style.materialSongList" v-show="isVisibleListDetail")
|
||||
div(:class="$style.songListHeader")
|
||||
div(:class="$style.songListHeaderLeft")
|
||||
img(:src="selectListInfo.img")
|
||||
span(:class="$style.playNum" v-if="selectListInfo.play_count") {{selectListInfo.play_count}}
|
||||
div(:class="$style.songListHeaderMiddle")
|
||||
h3(:title="selectListInfo.name") {{selectListInfo.name}}
|
||||
p(:title="selectListInfo.desc") {{selectListInfo.desc}}
|
||||
div(:class="$style.songListHeaderRight")
|
||||
material-btn(:class="$style.closeDetailButton" @click="hideListDetail") 返回
|
||||
material-song-list(v-model="selectdData" @action="handleSongListAction" :source="source" :page="listDetail.page" :limit="listDetail.limit" :total="listDetail.total" :list="listDetail.list")
|
||||
div.scroll(:class="$style.content" ref="dom_scrollContent" v-show="!isVisibleListDetail")
|
||||
ul
|
||||
li(:class="$style.item" v-for="(item, index) in listData.list" @click="handleItemClick(index)")
|
||||
div(:class="$style.left")
|
||||
img(:src="item.img")
|
||||
div(:class="$style.right" :src="item.img")
|
||||
h4(:title="item.name") {{item.name}}
|
||||
p(:title="item.desc") {{item.desc}}
|
||||
li(:class="$style.item" style="cursor: default;" v-if="listData.list && listData.list.length && listData.list.length % 3 == 2")
|
||||
div(:class="$style.pagination")
|
||||
material-pagination(:count="listData.total" :limit="listData.limit" :page="listData.page" @btn-click="handleTogglePage")
|
||||
material-download-modal(:show="isShowDownload" :musicInfo="musicInfo" @select="handleAddDownload" @close="isShowDownload = false")
|
||||
material-download-multiple-modal(:show="isShowDownloadMultiple" :list="selectdData" @select="handleAddDownloadMultiple" @close="isShowDownloadMultiple = false")
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters, mapMutations, mapActions } from 'vuex'
|
||||
import { scrollTo } from '../utils'
|
||||
// import music from '../utils/music'
|
||||
export default {
|
||||
name: 'SongList',
|
||||
data() {
|
||||
return {
|
||||
tagInfo: {
|
||||
name: '全部',
|
||||
id: null,
|
||||
},
|
||||
sortId: undefined,
|
||||
source: null,
|
||||
isShowDownload: false,
|
||||
musicInfo: null,
|
||||
selectdData: [],
|
||||
isShowDownloadMultiple: false,
|
||||
isToggleSource: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['setting']),
|
||||
...mapGetters('songList', ['sourceInfo', 'tags', 'listData', 'isVisibleListDetail', 'selectListInfo', 'listDetail']),
|
||||
...mapGetters('list', ['defaultList']),
|
||||
sorts() {
|
||||
return this.source ? this.sourceInfo.sortList[this.source] : []
|
||||
},
|
||||
isAPITemp() {
|
||||
return this.setting.apiSource == 'temp'
|
||||
},
|
||||
tagList() {
|
||||
return this.tags[this.source] ? this.tags[this.source].tags : []
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
sortId(n, o) {
|
||||
this.setSongList({ sortId: n })
|
||||
if (o === undefined && this.listData.page !== 1) return
|
||||
this.$nextTick(() => {
|
||||
this.getList(1).then(() => {
|
||||
this.$nextTick(() => {
|
||||
scrollTo(this.$refs.dom_scrollContent, 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
// if (this.isVisibleListDetail) this.setVisibleListDetail(false)
|
||||
},
|
||||
tagInfo(n, o) {
|
||||
this.setSongList({ tagInfo: n })
|
||||
if (!o && this.listData.page !== 1) return
|
||||
if (this.isToggleSource) {
|
||||
this.isToggleSource = false
|
||||
return
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.getList(1).then(() => {
|
||||
this.$nextTick(() => {
|
||||
scrollTo(this.$refs.dom_scrollContent, 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
// if (this.isVisibleListDetail) this.setVisibleListDetail(false)
|
||||
},
|
||||
source(n, o) {
|
||||
this.setSongList({ source: n })
|
||||
if (!this.tags[n]) this.getTags()
|
||||
if (o) {
|
||||
this.isToggleSource = true
|
||||
this.tagInfo = {
|
||||
name: '全部',
|
||||
id: null,
|
||||
}
|
||||
this.sortId = this.sorts[0] && this.sorts[0].id
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.source = this.setting.songList.source
|
||||
this.isToggleSource = true
|
||||
this.tagInfo = this.setting.songList.tagInfo
|
||||
this.sortId = this.setting.songList.sortId
|
||||
},
|
||||
methods: {
|
||||
...mapMutations(['setSongList']),
|
||||
...mapActions('songList', ['getTags', 'getList', 'getListDetail']),
|
||||
...mapMutations('songList', ['setVisibleListDetail', 'setSelectListInfo']),
|
||||
...mapActions('download', ['createDownload', 'createDownloadMultiple']),
|
||||
...mapMutations('list', ['defaultListAdd', 'defaultListAddMultiple']),
|
||||
...mapMutations('player', ['setList']),
|
||||
handleListBtnClick(info) {
|
||||
switch (info.action) {
|
||||
case 'download':
|
||||
this.musicInfo = this.listDetail.list[info.index]
|
||||
this.$nextTick(() => {
|
||||
this.isShowDownload = true
|
||||
})
|
||||
break
|
||||
case 'play':
|
||||
this.testPlay(info.index)
|
||||
break
|
||||
case 'search':
|
||||
this.handleSearch(info.index)
|
||||
break
|
||||
// case 'add':
|
||||
// break
|
||||
}
|
||||
},
|
||||
testPlay(index) {
|
||||
let targetSong
|
||||
if (index == null) {
|
||||
targetSong = this.selectdData[0]
|
||||
this.defaultListAddMultiple(this.selectdData)
|
||||
this.resetSelect()
|
||||
} else {
|
||||
targetSong = this.listDetail.list[index]
|
||||
this.defaultListAdd(targetSong)
|
||||
}
|
||||
let targetIndex = this.defaultList.list.findIndex(
|
||||
s => s.songmid === targetSong.songmid
|
||||
)
|
||||
if (targetIndex > -1) {
|
||||
this.setList({
|
||||
list: this.defaultList.list,
|
||||
listId: 'test',
|
||||
index: targetIndex,
|
||||
})
|
||||
}
|
||||
},
|
||||
handleSearch(index) {
|
||||
const info = this.listDetail.list[index]
|
||||
this.$router.push({
|
||||
path: 'search',
|
||||
query: {
|
||||
text: `${info.name} ${info.singer}`,
|
||||
},
|
||||
})
|
||||
},
|
||||
handleTogglePage(page) {
|
||||
this.getList(page).then(() => {
|
||||
this.$nextTick(() => {
|
||||
scrollTo(this.$refs.dom_scrollContent, 0)
|
||||
})
|
||||
})
|
||||
},
|
||||
handleAddDownload(type) {
|
||||
this.createDownload({ musicInfo: this.musicInfo, type })
|
||||
this.isShowDownload = false
|
||||
},
|
||||
handleAddDownloadMultiple(type) {
|
||||
switch (this.source) {
|
||||
// case 'kg':
|
||||
case 'wy':
|
||||
type = '128k'
|
||||
}
|
||||
this.createDownloadMultiple({ list: [...this.selectdData], type })
|
||||
this.resetSelect()
|
||||
this.isShowDownloadMultiple = false
|
||||
},
|
||||
handleItemClick(index) {
|
||||
this.setSelectListInfo(this.listData.list[index])
|
||||
this.setVisibleListDetail(true)
|
||||
this.getListDetail({ id: this.selectListInfo.id, page: 1 })
|
||||
},
|
||||
handleFlowBtnClick(action) {
|
||||
switch (action) {
|
||||
case 'download':
|
||||
this.isShowDownloadMultiple = true
|
||||
break
|
||||
case 'play':
|
||||
this.testPlay()
|
||||
break
|
||||
case 'add':
|
||||
this.defaultListAddMultiple(this.selectdData)
|
||||
this.resetSelect()
|
||||
break
|
||||
}
|
||||
},
|
||||
handleSongListAction({ action, data }) {
|
||||
switch (action) {
|
||||
case 'listBtnClick':
|
||||
return this.handleListBtnClick(data)
|
||||
case 'togglePage':
|
||||
return this.handleTogglePage(data)
|
||||
case 'flowBtnClick':
|
||||
return this.handleFlowBtnClick(data)
|
||||
case 'testPlay':
|
||||
return this.testPlay(data)
|
||||
case 'search':
|
||||
return this.handleSearch(data)
|
||||
}
|
||||
},
|
||||
resetSelect() {
|
||||
this.selectdData = []
|
||||
},
|
||||
hideListDetail() {
|
||||
setTimeout(() => this.setVisibleListDetail(false), 50)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="less" module>
|
||||
@import '../assets/styles/layout.less';
|
||||
|
||||
.leaderboard {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
.header {
|
||||
flex: none;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
|
||||
}
|
||||
.tab {
|
||||
flex: auto;
|
||||
}
|
||||
.select {
|
||||
flex: none;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.container {
|
||||
flex: auto;
|
||||
overflow: hidden;
|
||||
// position: relative;
|
||||
}
|
||||
|
||||
.song-list-header {
|
||||
background-color: @color-theme_2;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
height: 60px;
|
||||
}
|
||||
.song-list-header-left {
|
||||
flex: none;
|
||||
margin-left: 5px;
|
||||
width: 60px;
|
||||
position: relative;
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
.play-num {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 2px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
text-align: right;
|
||||
.mixin-ellipsis-1;
|
||||
}
|
||||
}
|
||||
|
||||
.song_list_header_middle {
|
||||
flex: auto;
|
||||
padding: 5px 7px;
|
||||
h3 {
|
||||
.mixin-ellipsis-1;
|
||||
line-height: 1.2;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
p {
|
||||
.mixin-ellipsis-2;
|
||||
font-size: 12px;
|
||||
line-height: 1.2;
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
.song-list-header-right {
|
||||
flex: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.material-song-list {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
padding: 0 15px;
|
||||
background-color: #fff;
|
||||
ul {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
.item {
|
||||
width: 32%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
margin-top: 15px;
|
||||
cursor: pointer;
|
||||
transition: opacity @transition-theme;
|
||||
&:hover {
|
||||
opacity: .7;
|
||||
}
|
||||
}
|
||||
.left {
|
||||
flex: none;
|
||||
width: 66px;
|
||||
height: 66px;
|
||||
display: flex;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
flex: auto;
|
||||
padding: 5px 15px 5px 7px;
|
||||
overflow: hidden;
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
text-align: justify;
|
||||
line-height: 1.2;
|
||||
.mixin-ellipsis-1;
|
||||
}
|
||||
p {
|
||||
margin-top: 12px;
|
||||
font-size: 12px;
|
||||
.mixin-ellipsis-2;
|
||||
text-align: justify;
|
||||
line-height: 1.2;
|
||||
// text-indent: 24px;
|
||||
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
.pagination {
|
||||
text-align: center;
|
||||
padding: 15px 0;
|
||||
// left: 50%;
|
||||
// transform: translateX(-50%);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user