diff --git a/build-config/renderer/webpack.config.dev.js b/build-config/renderer/webpack.config.dev.js index 2bce67f3..5fd16fd7 100644 --- a/build-config/renderer/webpack.config.dev.js +++ b/build-config/renderer/webpack.config.dev.js @@ -10,7 +10,7 @@ const { mergeCSSLoaderDev } = require('../utils') module.exports = merge(baseConfig, { mode: 'development', - devtool: '#cheap-module-eval-source-map', + devtool: 'eval-source-map', module: { rules: [ { @@ -46,7 +46,7 @@ module.exports = merge(baseConfig, { NODE_ENV: '"development"', ELECTRON_DISABLE_SECURITY_WARNINGS: 'true', }, - '__static': `"${path.join(__dirname, '../../src/static').replace(/\\/g, '\\\\')}"`, + __static: `"${path.join(__dirname, '../../src/static').replace(/\\/g, '\\\\')}"`, }), ], performance: { diff --git a/build-config/web/webpack.config.dev.js b/build-config/web/webpack.config.dev.js index 3aa6420e..3f0d180d 100644 --- a/build-config/web/webpack.config.dev.js +++ b/build-config/web/webpack.config.dev.js @@ -9,7 +9,7 @@ const { mergeCSSLoaderDev } = require('../utils') module.exports = merge(baseConfig, { mode: 'development', - devtool: '#cheap-module-eval-source-map', + devtool: 'eval-source-map', output: { filename: '[name].js', path: path.join(__dirname, '../../dist/web'), diff --git a/package-lock.json b/package-lock.json index 189b0d23..dc796222 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "lx-music-desktop", - "version": "0.1.0", + "version": "0.1.4", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/publish/changeLog.md b/publish/changeLog.md index e69de29b..da95ebf8 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -0,0 +1,10 @@ +### 新增 + +- 新增搜索列表批量试听与下载功能 +- 新增排行榜列表批量试听与下载功能 +- 新增试听列表批量移除与下载功能 +- 新增下载列表批量开始、暂停与移除功能 + +### 优化 + +- 优化歌曲切换机制 diff --git a/publish/index.js b/publish/index.js index aa2e9e65..15da4437 100644 --- a/publish/index.js +++ b/publish/index.js @@ -16,7 +16,7 @@ const run = async() => { try { console.log(chalk.blue('Clearing assets...')) await clearAssets() - console.log(chalk.green('Assets clear complated...')) + console.log(chalk.green('Assets clear completed...')) // console.log(chalk.blue('Compileing assets...')) // await compileAssets() diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 66d50448..79f4566b 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -38,6 +38,7 @@ export default { ...mapGetters('list', ['defaultList']), ...mapGetters('download', { downloadList: 'list', + downloadStatus: 'downloadStatus', }), }, mounted() { @@ -119,6 +120,12 @@ export default { initDownloadList() { let downloadList = this.electronStore.get('download.list') if (downloadList) { + downloadList.forEach(item => { + if (item.status == this.downloadStatus.RUN || item.status == this.downloadStatus.WAITING) { + item.status = this.downloadStatus.PAUSE + item.statusText = '暂停下载' + } + }) this.updateDownloadList(downloadList) } }, diff --git a/src/renderer/components/core/Player.vue b/src/renderer/components/core/Player.vue index f085e9bc..57b4778c 100644 --- a/src/renderer/components/core/Player.vue +++ b/src/renderer/components/core/Player.vue @@ -252,36 +252,43 @@ export default { }, handleNext() { // if (this.list.listName === null) return - const list = this.isAPITemp ? this.list.filter(s => s.source == 'kw') : this.list - if (!list.length) return + let list + if (this.listId == 'download') { + list = this.list.filter(s => !(!checkPath(s.filePath) || !s.isComplate || /\.ape$/.test(s.filePath))) + } else if (this.isAPITemp) { + list = this.list.filter(s => s.source == 'kw') + } else { + list = this.list + } + if (!list.length) return this.setPlayIndex(-1) + let playIndex = this.list === list ? this.playIndex : list.indexOf(this.list[this.playIndex]) + let index switch (this.setting.player.togglePlayMethod) { case 'listLoop': - index = this.hanldeListLoop() + index = this.hanldeListLoop(list, playIndex) break case 'random': - index = this.hanldeListRandom() + index = this.hanldeListRandom(list, playIndex) break case 'list': - index = this.hanldeListNext() + index = this.hanldeListNext(list, playIndex) break default: return } if (index < 0) return + if (this.list !== list) index = this.list.indexOf(list[index]) this.setPlayIndex(index) }, - hanldeListLoop(index = this.playIndex) { - index = index === this.list.length - 1 ? 0 : index + 1 - return this.isAPITemp && this.list[index].source != 'kw' ? this.hanldeListLoop(index) : index + hanldeListLoop(list, index) { + return index === list.length - 1 ? 0 : index + 1 }, - hanldeListNext(index = this.playIndex) { - index = index === this.list.length - 1 ? -1 : index + 1 - return this.isAPITemp && this.list[index].source != 'kw' ? this.hanldeListNext(index) : index + hanldeListNext(list, index) { + return index === list.length - 1 ? -1 : index + 1 }, - hanldeListRandom(index = this.playIndex) { - index = getRandom(0, this.list.length) - return this.isAPITemp && this.list[index].source != 'kw' ? this.hanldeListRandom(index) : index + hanldeListRandom(list, index) { + return getRandom(0, list.length) }, startPlay() { this.isPlay = true @@ -333,6 +340,9 @@ export default { this.audio.src = this.musicInfo.url }).catch(err => { this.status = err.message + setTimeout(() => { + this.handleNext() + }, 2000) }) }, setImg(targetSong) { diff --git a/src/renderer/components/material/Checkbox.vue b/src/renderer/components/material/Checkbox.vue index e7fb58db..abba8097 100644 --- a/src/renderer/components/material/Checkbox.vue +++ b/src/renderer/components/material/Checkbox.vue @@ -10,7 +10,7 @@ div(:class="$style.checkbox") div(v-else) svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' width="100%" viewBox='0 32 448 448' space='preserve') use(xlink:href='#icon-check-true') - span(v-if="label != null") {{label}} + span(v-if="label != null" v-html="label") + + + diff --git a/src/renderer/components/material/FlowBtn.vue b/src/renderer/components/material/FlowBtn.vue new file mode 100644 index 00000000..ccf8deca --- /dev/null +++ b/src/renderer/components/material/FlowBtn.vue @@ -0,0 +1,122 @@ + + + + + + diff --git a/src/renderer/components/material/ListButtons.vue b/src/renderer/components/material/ListButtons.vue index 6a5eea0d..9126fcf6 100644 --- a/src/renderer/components/material/ListButtons.vue +++ b/src/renderer/components/material/ListButtons.vue @@ -9,6 +9,12 @@ div(:class="$style.btns") button(type="button" title="添加" v-if="userInfo" @click.stop="handleClick('add')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 42 42' space='preserve') use(xlink:href='#icon-addTo') + button(type="button" v-if="startBtn" title="开始" @click.stop="handleClick('start')") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 170 170' space='preserve') + use(xlink:href='#icon-play') + button(type="button" v-if="pauseBtn" title="暂停" @click.stop="handleClick('pause')") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 277.338 277.338' space='preserve') + use(xlink:href='#icon-pause') button(type="button" v-if="removeBtn" title="移除" @click.stop="handleClick('remove')") svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 212.982 212.982' space='preserve') use(xlink:href='#icon-delete') @@ -27,6 +33,14 @@ export default { type: Number, required: true, }, + startBtn: { + type: Boolean, + default: false, + }, + pauseBtn: { + type: Boolean, + default: false, + }, removeBtn: { type: Boolean, default: true, diff --git a/src/renderer/store/modules/download.js b/src/renderer/store/modules/download.js index c1b45efa..b20070d5 100644 --- a/src/renderer/store/modules/download.js +++ b/src/renderer/store/modules/download.js @@ -2,18 +2,30 @@ import download from '../../utils/download' import fs from 'fs' import path from 'path' import music from '../../utils/music' +import { getMusicType } from '../../utils/music/utils' // state const state = { list: [], + waitingList: [], + downloadStatus: { + RUN: 'run', + WAITING: 'waiting', + PAUSE: 'pause', + ERROR: 'error', + COMPLETED: 'completed', + }, } + const dls = {} +const tryNum = {} // getters const getters = { list: state => state.list || [], dls: () => dls || {}, + downloadStatus: state => state.downloadStatus, } const checkPath = path => { @@ -40,19 +52,37 @@ const getExt = type => { const checkList = (list, musicInfo, type) => list.some(s => s.musicInfo.songmid === musicInfo.songmid && s.type === type) +const getStartTask = (list, downloadStatus, maxDownloadNum) => { + let downloadCount = 0 + const waitList = list.filter(item => item.status == downloadStatus.WAITING ? true : (item.status === downloadStatus.RUN && ++downloadCount && false)) + console.log(downloadCount, waitList) + return downloadCount < maxDownloadNum && waitList.length > 0 && waitList.shift() +} + +const addTask = (list, type, store) => { + window.requestAnimationFrame(() => { + let item = list.shift() + store.dispatch('download/createDownload', { + musicInfo: item, + type: getMusicType(item, type), + }) + if (list.length) addTask(list, type, store) + }) +} + const refreshUrl = downloadInfo => { return music[downloadInfo.musicInfo.source].getMusicUrl(downloadInfo.musicInfo, downloadInfo.type).promise } // actions const actions = { - createDownload({ state, rootState }, { musicInfo, type }) { + createDownload({ state, rootState, commit }, { musicInfo, type }) { if (checkList(state.list, musicInfo, type)) return let ext = getExt(type) const downloadInfo = { isComplate: false, - isDownloading: false, - statusText: '任务初始化中', + status: state.downloadStatus.WAITING, + statusText: '待下载', url: null, fileName: `${rootState.setting.download.fileName .replace('歌名', musicInfo.name) @@ -68,19 +98,33 @@ const actions = { key: `${musicInfo.songmid}${ext}`, } downloadInfo.filePath = path.join(rootState.setting.download.savePath, downloadInfo.fileName) + commit('addTask', downloadInfo) if (dls[downloadInfo.key]) { dls[downloadInfo.key].stop().finally(() => { - this.dispatch('download/addTask', downloadInfo) + delete dls[downloadInfo.key] + this.dispatch('download/startTask', downloadInfo) }) } else { // console.log(downloadInfo) - this.dispatch('download/addTask', downloadInfo) + this.dispatch('download/startTask', downloadInfo) } }, - addTask({ commit, rootState }, downloadInfo) { - commit('addTask', downloadInfo) + createDownloadMultiple({ state, rootState }, { list, type }) { + addTask([...list], type, this) + }, + startTask({ commit, state, rootState }, downloadInfo) { + // 检查是否可以开始任务 + if (downloadInfo && downloadInfo != state.downloadStatus.WAITING) commit('setStatus', { downloadInfo, status: state.downloadStatus.WAITING }) + let result = getStartTask(state.list, state.downloadStatus, rootState.setting.download.maxDownloadNum) + if (!result) return downloadInfo && commit('setStatus', { downloadInfo, status: state.downloadStatus.WAITING }) + if (!downloadInfo) downloadInfo = result + + // 开始任务 + commit('setStatusText', { downloadInfo, text: '任务初始化中' }) + commit('onDownload', downloadInfo) let msg = checkPath(rootState.setting.download.savePath) if (msg) return commit('setStatusText', '检查下载目录出错: ' + msg) + const _this = this const options = { url: downloadInfo.url, path: rootState.setting.download.savePath, @@ -89,37 +133,46 @@ const actions = { override: true, onEnd() { commit('onEnd', downloadInfo) + _this.dispatch('download/startTask') console.log('on complate') }, onError(err) { - console.log(err) - if (err.message.includes('Response status was')) { - const code = err.message.replace(/Response status was (\d+)$/, '$1') - switch (code) { - case '401': - case '403': - case '410': - commit('setStatusText', { downloadInfo, text: '链接失效,正在刷新链接' }) - refreshUrl(downloadInfo).then(result => { - commit('updateUrl', { downloadInfo, url: result.url }) - commit('setStatusText', { downloadInfo, text: '链接刷新成功' }) - dls[downloadInfo.key].url = dls[downloadInfo.key].requestURL = result.url - dls[downloadInfo.key].__initProtocol(result.url) - dls[downloadInfo.key].resume() - }).catch(err => { - console.log(err) - }) - return - - default: - break - } - } + console.log(err.message) commit('onError', downloadInfo) + // console.log(tryNum[downloadInfo.key]) + if (++tryNum[downloadInfo.key] > 5) return + let code + if (err.message.includes('Response status was')) { + code = err.message.replace(/Response status was (\d+)$/, '$1') + } if (err.code === 'ETIMEDOUT') { + code = 'ETIMEDOUT' + } else { + console.log('Download failed, Attempting Retry') + dls[downloadInfo.key].resume() + commit('setStatusText', { downloadInfo, text: '正在重试' }) + return + } + switch (code) { + case '401': + case '403': + case '410': + case 'ETIMEDOUT': + commit('setStatusText', { downloadInfo, text: '链接失效,正在刷新链接' }) + refreshUrl(downloadInfo).then(result => { + commit('updateUrl', { downloadInfo, url: result.url }) + commit('setStatusText', { downloadInfo, text: '链接刷新成功' }) + dls[downloadInfo.key].url = dls[downloadInfo.key].requestURL = result.url + dls[downloadInfo.key].__initProtocol(result.url) + dls[downloadInfo.key].resume() + }).catch(err => { + console.log(err) + _this.dispatch('download/startTask') + }) + } }, - onStateChanged(state) { - console.log(state) - }, + // onStateChanged(state) { + // console.log(state) + // }, onDownload() { commit('onDownload', downloadInfo) console.log('on download') @@ -130,6 +183,7 @@ const actions = { }, onPause() { commit('pauseTask', downloadInfo) + _this.dispatch('download/startTask') }, onResume() { commit('resumeTask', downloadInfo) @@ -138,80 +192,52 @@ const actions = { let p = options.url ? Promise.resolve() : refreshUrl(downloadInfo).then(result => { commit('updateUrl', { downloadInfo, url: result.url }) options.url = result.url + }).catch(err => { + commit('onError', downloadInfo) + commit('setStatusText', { downloadInfo, text: err.message }) + return Promise.reject(err) }) p.then(() => { + tryNum[downloadInfo.key] = 0 dls[downloadInfo.key] = download(options) }) + }, + startTaskMultiple({ state, rootState }, list) { + }, removeTask({ commit, state }, index) { let info = state.list[index] - if (state.list[index].isDownloading) { - dls[info.key].stop().finally(() => { - delete dls[info.key] - }) + if (state.list[index].status == state.downloadStatus.RUN) { + if (dls[info.key]) { + dls[info.key].stop().finally(() => { + delete dls[info.key] + }) + } } commit('removeTask', index) if (dls[info.key]) delete dls[info.key] + this.dispatch('download/startTask') }, - resumeTask({ commit, rootState }, downloadInfo) { - let msg = checkPath(rootState.setting.download.savePath) - if (msg) return commit('setStatusText', '检查下载目录出错: ' + msg) - const options = { - url: downloadInfo.url, - path: rootState.setting.download.savePath, - fileName: downloadInfo.fileName, - method: 'get', - override: true, - onEnd() { - commit('onEnd', downloadInfo) - console.log('on complate') - }, - onError(err) { - commit('onError', downloadInfo) - commit('setStatusText', { downloadInfo, text: '链接失效,正在刷新链接' }) - refreshUrl(downloadInfo).then(result => { - commit('updateUrl', { downloadInfo, url: result.url }) - commit('setStatusText', { downloadInfo, text: '链接刷新成功' }) - dls[downloadInfo.key].url = dls[downloadInfo.key].requestURL = result.url - dls[downloadInfo.key].__initProtocol(result.url) - dls[downloadInfo.key].resume() - }).catch(err => { - console.log(err) - }) - console.log(err) - }, - onStateChanged(state) { - console.log(state) - }, - onDownload() { - commit('onDownload', downloadInfo) - console.log('on download') - }, - onProgress(status) { - commit('onProgress', { downloadInfo, status }) - console.log(status) - }, - onPause() { - commit('pauseTask', downloadInfo) - }, - onResume() { - commit('resumeTask', downloadInfo) - }, - } - - let p = options.url ? Promise.resolve() : refreshUrl(downloadInfo).then(result => { - commit('updateUrl', { downloadInfo, url: result.url }) - options.url = result.url - }) - - if (fs.existsSync(downloadInfo.filePath)) { - options.resumeInfo = { - totalFileSize: downloadInfo.progress.total, + removeTaskMultiple({ commit, rootState, state }, list) { + list.forEach(item => { + let index = state.list.indexOf(item) + if (index < 0) return + // this.dispatch('download/removeTask', index) + if (state.list[index].status == state.downloadStatus.RUN) { + if (dls[item.key]) { + dls[item.key].stop().finally(() => { + delete dls[item.key] + }) + } } - } - p.then(() => { - dls[downloadInfo.key] = download(options) + commit('removeTask', index) + if (dls[item.key]) delete dls[item.key] }) + let result = getStartTask(state.list, state.downloadStatus, rootState.setting.download.maxDownloadNum) + while (result) { + this.dispatch('download/startTask', result) + result = getStartTask(state.list, state.downloadStatus, rootState.setting.download.maxDownloadNum) + } }, } @@ -224,29 +250,57 @@ const mutations = { state.list.splice(index, 1) }, pauseTask(state, downloadInfo) { - downloadInfo.isDownloading = false + downloadInfo.status = state.downloadStatus.PAUSE + downloadInfo.statusText = '暂停下载' }, resumeTask(state, downloadInfo) { - downloadInfo.statusText = '恢复下载' + downloadInfo.statusText = '开始下载' }, - setStatusText(state, { downloadInfo, index, text }) { + setStatusText(state, { downloadInfo, index, text }) { // 设置状态文本 if (downloadInfo) { downloadInfo.statusText = text } else { state.list[index].statusText = text } }, + setStatus(state, { downloadInfo, index, status }) { // 设置状态及状态文本 + let text + switch (status) { + case state.downloadStatus.RUN: + text = '正在下载' + break + case state.downloadStatus.WAITING: + text = '等待下载' + break + case state.downloadStatus.PAUSE: + text = '暂停下载' + break + case state.downloadStatus.ERROR: + text = '任务出错' + break + case state.downloadStatus.COMPLETED: + text = '下载完成' + break + } + if (downloadInfo) { + downloadInfo.statusText = text + downloadInfo.status = status + } else { + state.list[index].statusText = text + state.list[index].status = status + } + }, onEnd(state, downloadInfo) { downloadInfo.isComplate = true - downloadInfo.isDownloading = false + downloadInfo.status = state.downloadStatus.COMPLETED downloadInfo.statusText = '下载完成' }, onError(state, downloadInfo) { - downloadInfo.isDownloading = false + downloadInfo.status = state.downloadStatus.ERROR downloadInfo.statusText = '任务出错' }, onDownload(state, downloadInfo) { - downloadInfo.isDownloading = true + downloadInfo.status = state.downloadStatus.RUN downloadInfo.statusText = '正在下载' }, onProgress(state, { downloadInfo, status }) { diff --git a/src/renderer/store/modules/list.js b/src/renderer/store/modules/list.js index bf261251..1ec74d02 100644 --- a/src/renderer/store/modules/list.js +++ b/src/renderer/store/modules/list.js @@ -30,9 +30,22 @@ const mutations = { if (state.defaultList.list.some(s => s.songmid === musicInfo.songmid)) return state.defaultList.list.push(musicInfo) }, + defaultListAddMultiple(state, list) { + list.forEach(musicInfo => { + if (state.defaultList.list.some(s => s.songmid === musicInfo.songmid)) return + state.defaultList.list.push(musicInfo) + }) + }, defaultListRemove(state, index) { state.defaultList.list.splice(index, 1) }, + defaultListRemoveMultiple(state, list) { + list.forEach(musicInfo => { + let index = state.defaultList.list.indexOf(musicInfo) + if (index < 0) return + state.defaultList.list.splice(index, 1) + }) + }, defaultListClear(state) { state.defaultList.list.length = 0 }, diff --git a/src/renderer/utils/download/index.js b/src/renderer/utils/download/index.js index eb77c2c1..40dd7b42 100644 --- a/src/renderer/utils/download/index.js +++ b/src/renderer/utils/download/index.js @@ -45,9 +45,8 @@ export default ({ onEnd() debugDownload && console.log('Download Completed') }).on('error', err => { + if (err.message === 'socket hang up') return onError(err) - dl.resume() - console.log('Download failed, Attempting Retry') debugDownload && console.error('Something happend', err) }).on('stateChanged', state => { onStateChanged(state) diff --git a/src/renderer/utils/index.js b/src/renderer/utils/index.js index 25699eb7..0cb34a3c 100644 --- a/src/renderer/utils/index.js +++ b/src/renderer/utils/index.js @@ -156,8 +156,12 @@ export const isChildren = (parent, children) => { return children.parentNode ? children.parentNode === parent ? true : isChildren(parent, children.parentNode) : false } +/** + * 升级设置 + * @param {*} setting + */ export const updateSetting = setting => { - const defaultVersion = '1.0.2' + const defaultVersion = '1.0.3' const defaultSetting = { version: defaultVersion, player: { @@ -170,6 +174,7 @@ export const updateSetting = setting => { download: { savePath: path.join(os.homedir(), 'Desktop'), fileName: '歌名 - 歌手', + maxDownloadNum: 3, }, leaderboard: { source: 'kw', @@ -203,3 +208,4 @@ export const updateSetting = setting => { export const openUrl = url => { shell.openExternal(url) } + diff --git a/src/renderer/utils/message.js b/src/renderer/utils/message.js new file mode 100644 index 00000000..2977f075 --- /dev/null +++ b/src/renderer/utils/message.js @@ -0,0 +1,6 @@ +export const requestMsg = { + fail: '请求异常😮,可以多试几次,若还是不行就换一首吧。。。', + unachievable: '哦No😱...接口无法访问了!已帮你切换到临时接口,重试下看能不能播放吧~', + notConnectNetwork: '无法连接网络', + cancelRequest: '取消http请求', +} diff --git a/src/renderer/utils/music/kw/api-messoer.js b/src/renderer/utils/music/kw/api-messoer.js index 9a4749e4..599c14ab 100644 --- a/src/renderer/utils/music/kw/api-messoer.js +++ b/src/renderer/utils/music/kw/api-messoer.js @@ -1,4 +1,5 @@ import { httpFatch } from '../../request' +import { requestMsg } from '../../message' const api_messoer = { getMusicUrl(songInfo, type) { @@ -7,7 +8,7 @@ const api_messoer = { timeout: 5000, }) requestObj.promise = requestObj.promise.then(({ body }) => { - return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(body.msg)) + return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail)) }) return requestObj }, @@ -17,7 +18,7 @@ const api_messoer = { timeout: 5000, }) requestObj.promise = requestObj.promise.then(({ body }) => { - return body.code === 200 ? Promise.resolve(body.data) : Promise.reject(new Error(body.msg)) + return body.code === 200 ? Promise.resolve(body.data) : Promise.reject(new Error(requestMsg.fail)) }) return requestObj }, diff --git a/src/renderer/utils/music/kw/api-temp.js b/src/renderer/utils/music/kw/api-temp.js index d7091ae0..0c8436f2 100644 --- a/src/renderer/utils/music/kw/api-temp.js +++ b/src/renderer/utils/music/kw/api-temp.js @@ -7,11 +7,6 @@ const api_temp = { }) requestObj.promise = requestObj.promise.then(({ body }) => { return body.code === 0 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(body.msg)) - }).catch(err => { - console.log(err) - if (err.message === 'socket hang up') return Promise.reject(new Error('接口挂了')) - if (err.code === 'ENOTFOUND') return Promise.reject(new Error('无法连接网络')) - return Promise.reject(err) }) return requestObj }, @@ -21,10 +16,6 @@ const api_temp = { }) requestObj.promise = requestObj.promise.then(({ body }) => { return body.code === 0 ? Promise.resolve(body.data) : Promise.reject(new Error(body.msg)) - }).catch(err => { - if (err.message === 'socket hang up') return Promise.reject(new Error('接口挂了')) - if (err.code === 'ENOTFOUND') return Promise.reject(new Error('无法连接网络')) - return Promise.reject(err) }) return requestObj }, diff --git a/src/renderer/utils/music/tx/api-messoer.js b/src/renderer/utils/music/tx/api-messoer.js index 0b00f629..49ac2f04 100644 --- a/src/renderer/utils/music/tx/api-messoer.js +++ b/src/renderer/utils/music/tx/api-messoer.js @@ -1,4 +1,5 @@ import { httpFatch } from '../../request' +import { requestMsg } from '../../message' const api_messoer = { getMusicUrl(songInfo, type) { @@ -7,7 +8,7 @@ const api_messoer = { timeout: 5000, }) requestObj.promise = requestObj.promise.then(({ body }) => { - return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(body.msg)) + return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail)) }) return requestObj }, diff --git a/src/renderer/utils/music/utils.js b/src/renderer/utils/music/utils.js new file mode 100644 index 00000000..14f0e1c6 --- /dev/null +++ b/src/renderer/utils/music/utils.js @@ -0,0 +1,12 @@ +/** + * 获取音乐音质 + * @param {*} info + * @param {*} type + */ +const types = ['flac', 'ape', '320k', '192k', '128k'] +export const getMusicType = (info, type) => { + const rangeType = types.slice(types.indexOf(type)) + for (const type of rangeType) { + if (info._types[type]) return type + } +} diff --git a/src/renderer/utils/request.js b/src/renderer/utils/request.js index 40214d00..f96b6383 100644 --- a/src/renderer/utils/request.js +++ b/src/renderer/utils/request.js @@ -1,6 +1,7 @@ import request from 'request' // import progress from 'request-progress' import { debugRequest } from './env' +import { requestMsg } from './message' // import fs from 'fs' const headers = { @@ -9,7 +10,7 @@ const headers = { const fatchData = (url, method, options, callback) => { // console.log(url, options) - // console.log('---start---', url) + console.log('---start---', url) return request(url, { method, headers: Object.assign({}, headers, options.headers || {}), @@ -60,7 +61,7 @@ const buildHttpPromose = (url, options) => { console.log('cancel') if (!requestObj) return cancelHttp(requestObj) - cancelFn(new Error('取消http请求')) + cancelFn(new Error(requestMsg.cancelRequest)) requestObj = null cancelFn = null }, @@ -84,9 +85,9 @@ export const httpFatch = (url, options = { method: 'get' }) => { } if (err.message === 'socket hang up') { window.globalObj.apiSource = 'temp' - return Promise.reject(new Error('哦No😱...接口无法访问了!已帮你切换到临时接口,重试下看能不能播放吧~')) + return Promise.reject(new Error(requestMsg.unachievable)) } - if (err.code === 'ENOTFOUND') return Promise.reject(new Error('无法连接网络')) + if (err.code === 'ENOTFOUND') return Promise.reject(new Error(requestMsg.notConnectNetwork)) return Promise.reject(err) }) return requestObj diff --git a/src/renderer/views/Download.vue b/src/renderer/views/Download.vue index f52bd6bd..0266b9cb 100644 --- a/src/renderer/views/Download.vue +++ b/src/renderer/views/Download.vue @@ -6,8 +6,11 @@ div(:class="$style.download") table thead tr - th.nobreak(style="width: 30%;") 歌曲名 - th.nobreak(style="width: 25%;") 进度 + 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: 28%;") 歌曲名 + th.nobreak(style="width: 22%;") 进度 th.nobreak(style="width: 15%;") 状态 th.nobreak(style="width: 10%;") 品质 th.nobreak(style="width: 20%;") 操作 @@ -15,12 +18,17 @@ div(:class="$style.download") table tbody tr(v-for='(item, index) in list' :key='item.key' @click="handleDoubleClick(index)" :class="isPlayList && playIndex === index ? $style.active : ''") - td.break(style="width: 30%;") {{item.musicInfo.name}} - {{item.musicInfo.singer}} - td.break(style="width: 25%;") {{item.progress.progress}}% + td.nobreak.center(style="width: 37px;" @click.stop) + material-checkbox(:id="index.toString()" v-model="selectdData" :value="item") + td.break(style="width: 28%;") {{item.musicInfo.name}} - {{item.musicInfo.singer}} + td.break(style="width: 22%;") {{item.progress.progress}}% td.break(style="width: 15%;") {{item.statusText}} td.break(style="width: 10%;") {{item.type.toUpperCase()}} td(style="width: 20%; padding-left: 0; padding-right: 0;") - material-list-buttons(:index="index" :downloadBtn="false" @btn-click="handleBtnClick") + material-list-buttons(:index="index" :download-btn="false" :start-btn="!item.isComplate && item.status != downloadStatus.WAITING && (item.status != downloadStatus.RUN)" + :pause-btn="!item.isComplate && (item.status == downloadStatus.RUN || item.status == downloadStatus.WAITING)" + :play-btn="item.status == downloadStatus.COMPLETED" @btn-click="handleListBtnClick") + material-flow-btn(:show="isShowEditBtn" :play-btn="false" :download-btn="false" :add-btn="false" :start-btn="true" :pause-btn="true" @btn-click="handleFlowBtnClick") div(:class="$style.noItem" v-else) @@ -34,26 +42,51 @@ export default { return { clickTime: window.performance.now(), clickIndex: -1, + selectdData: [], + isSelectAll: false, + isIndeterminate: false, + isShowEditBtn: false, + isShowDownloadMultiple: false, } }, computed: { - ...mapGetters('download', ['list', 'dls']), + ...mapGetters('download', ['list', 'dls', 'downloadStatus']), ...mapGetters('player', ['listId', 'playIndex']), isPlayList() { return this.listId == 'download' }, }, - methods: { - ...mapActions('download', ['removeTask', 'resumeTask']), - ...mapMutations('player', ['setList']), - pauseTask(index) { - let info = this.list[index] - this.dls[info.key].pause() + watch: { + 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 + } }, - startTask(index) { + list() { + this.resetSelect() + }, + }, + methods: { + ...mapActions('download', ['removeTask', 'removeTaskMultiple', 'startTask']), + ...mapMutations('player', ['setList']), + ...mapMutations('download', ['pauseTask']), + handlePauseTask(index) { let info = this.list[index] let dl = this.dls[info.key] - dl ? dl.resume() : this.resumeTask(info) + dl ? dl.pause() : this.pauseTask(info) + console.log('pause') + }, + handleStartTask(index) { + console.log('start') + let info = this.list[index] + let dl = this.dls[info.key] + dl ? dl.resume() : this.startTask(info) }, handleDoubleClick(index) { if ( @@ -72,26 +105,71 @@ export default { let info = this.list[index] if (info.isComplate) { this.handlePlay(index) - } else if (info.isDownloading) { - this.pauseTask(index) + } else if (info.status === this.downloadStatus.RUN) { + this.handlePauseTask(index) } else { - this.startTask(index) + this.handleStartTask(index) } }, handlePlay(index) { if (!checkPath(this.list[index].filePath)) return this.setList({ list: this.list, listId: 'download', index }) }, - handleBtnClick(info) { + handleListBtnClick(info) { switch (info.action) { case 'play': this.handlePlay(info.index) break + case 'start': + this.handleStartTask(info.index) + break + case 'pause': + this.handlePauseTask(info.index) + break case 'remove': this.removeTask(info.index) break } }, + handleSelectAllData(isSelect) { + this.selectdData = isSelect ? [...this.list] : [] + }, + resetSelect() { + this.isSelectAll = false + this.selectdData = [] + }, + handleFlowBtnClick(action) { + switch (action) { + case 'start': + this.selectdData.forEach(item => { + if (item.isComplate || item.status == this.downloadStatus.RUN) return + let index = this.list.indexOf(item) + if (index < 0) return + this.handleStartTask(index) + }) + break + case 'pause': + let runs = [] + this.selectdData.forEach(item => { + if (item.isComplate || item.status == this.downloadStatus.PAUSE) return + if (item.status == this.downloadStatus.RUN) return runs.push(item) + let index = this.list.indexOf(item) + if (index < 0) return + this.handlePauseTask(index) + }) + runs.forEach(item => { + if (item.isComplate || item.status == this.downloadStatus.PAUSE) return + let index = this.list.indexOf(item) + if (index < 0) return + this.handlePauseTask(index) + }) + break + case 'remove': + this.removeTaskMultiple(this.selectdData) + break + } + this.resetSelect() + }, }, } diff --git a/src/renderer/views/Leaderboard.vue b/src/renderer/views/Leaderboard.vue index 91300978..6d0ebcd8 100644 --- a/src/renderer/views/Leaderboard.vue +++ b/src/renderer/views/Leaderboard.vue @@ -9,30 +9,37 @@ table thead tr - th.nobreak(style="width: 27%;") 歌曲名 + 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: 25%;") 专辑 + 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.break(style="width: 27%;") + 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: 25%;") {{item.albumName}} + 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="handleBtnClick") + 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="info.total" :limit="info.limit" :page="info.page" @btn-click="handleTogglePage") - material-download-modal(:show="showDownload" :musicInfo="musicInfo" @select="handleAddDownload" @close="showDownload = false") + 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" :remove-btn="false" @btn-click="handleFlowBtnClick") diff --git a/src/renderer/views/Setting.vue b/src/renderer/views/Setting.vue index 6280c9fa..ac5fdd1b 100644 --- a/src/renderer/views/Setting.vue +++ b/src/renderer/views/Setting.vue @@ -142,7 +142,7 @@ export default { apiSources: [ { id: 'messoer', - label: '由 messoer 提供的接口(推荐,软件的所有功能都可用)', + label: '由 messoer 提供的接口(推荐,软件的所有功能都可用)
注意:本接口10秒内请求数超过100次会封10小时的IP', }, { id: 'temp',