diff --git a/publish/changeLog.md b/publish/changeLog.md index 01f796d3..2e5aecd7 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -2,6 +2,7 @@ - 新增“我的收藏”本地播放列表 - 新增缓存清理功能,可到**设置-其他**查看与清理软件缓存 +- 新增QQ音乐源搜索 #### 优化 @@ -11,3 +12,4 @@ #### 修复 - 修复列表延迟显示的Bug +- 修复QQ音源128k音质试听 diff --git a/src/renderer/components/core/Player.vue b/src/renderer/components/core/Player.vue index 840adf3d..435d97ad 100644 --- a/src/renderer/components/core/Player.vue +++ b/src/renderer/components/core/Player.vue @@ -367,6 +367,7 @@ export default { getPlayType(highQuality, songInfo) { switch (songInfo.source) { case 'wy': + case 'tx': // case 'kg': return '128k' } diff --git a/src/renderer/components/material/DownloadModal.vue b/src/renderer/components/material/DownloadModal.vue index 6dac4a39..d31bb125 100644 --- a/src/renderer/components/material/DownloadModal.vue +++ b/src/renderer/components/material/DownloadModal.vue @@ -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()}` }} @@ -52,7 +52,8 @@ export default { checkSource(type) { switch (this.musicInfo.source) { case 'wy': - // case 'kg': + return false + case 'tx': return type == '128k' default: diff --git a/src/renderer/components/material/SongList.vue b/src/renderer/components/material/SongList.vue index c828bfe8..2370b6b2 100644 --- a/src/renderer/components/material/SongList.vue +++ b/src/renderer/components/material/SongList.vue @@ -29,9 +29,9 @@ div(:class="$style.songList") td(style="width: 20%; padding-left: 0; padding-right: 0;") material-list-buttons(:index="index" :search-btn="true" :remove-btn="false" @btn-click="handleListBtnClick" - :listAdd-btn="item.source == 'kw' || (!isAPITemp && item.source != 'tx' && item.source != 'wy')" - :play-btn="item.source == 'kw' || (!isAPITemp && item.source != 'tx' && item.source != 'wy')" - :download-btn="item.source == 'kw' || (!isAPITemp && item.source != 'tx' && item.source != 'wy')") + :listAdd-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')" + :play-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')" + :download-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')") //- 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)') + @@ -140,7 +140,7 @@ export default { this.clickIndex = index return } - this.emitEvent((this.source == 'kw' || (!this.isAPITemp && this.list[index].source != 'tx' && this.list[index].source != 'wy')) ? 'testPlay' : 'search', index) + this.emitEvent((this.source == 'kw' || (!this.isAPITemp && this.list[index].source != 'wy')) ? 'testPlay' : 'search', index) this.clickTime = 0 this.clickIndex = -1 }, diff --git a/src/renderer/utils/music/api-source.js b/src/renderer/utils/music/api-source.js index 3b323b9a..bcf67b7d 100644 --- a/src/renderer/utils/music/api-source.js +++ b/src/renderer/utils/music/api-source.js @@ -1,6 +1,6 @@ import kw_api_temp from './kw/api-temp' import kw_api_test from './kw/api-test' -// import tx_api_messoer from './tx/api-messoer' +import tx_api_test from './tx/api-test' import kg_api_test from './kg/api-test' import wy_api_test from './wy/api-test' import bd_api_test from './bd/api-test' @@ -12,7 +12,7 @@ import bd_api_test from './bd/api-test' const apis = { kw_api_test, - // tx_api_messoer, + tx_api_test, kg_api_test, wy_api_test, bd_api_test, diff --git a/src/renderer/utils/music/tx/api-test.js b/src/renderer/utils/music/tx/api-test.js index de8fac74..9785d110 100644 --- a/src/renderer/utils/music/tx/api-test.js +++ b/src/renderer/utils/music/tx/api-test.js @@ -1,17 +1,17 @@ import { httpFetch } from '../../request' import { requestMsg } from '../../message' -import { headers, timeout } from '../messoer' +import { headers, timeout } from '../options' const api_messoer = { getMusicUrl(songInfo, type) { - const requestObj = httpFetch(`https://v1.itooi.cn/tencent/url?id=${songInfo.strMediaMid}&quality=${type.replace(/k$/, '')}`, { + const requestObj = httpFetch(`http://ts.tempmusic.tk/url/tx/${songInfo.strMediaMid}/${type}`, { method: 'get', timeout, headers, family: 4, }) requestObj.promise = requestObj.promise.then(({ body }) => { - return body.code === 200 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail)) + return body.code === 0 ? Promise.resolve({ type, url: body.data }) : Promise.reject(new Error(requestMsg.fail)) }) return requestObj }, diff --git a/src/renderer/utils/music/tx/index.js b/src/renderer/utils/music/tx/index.js index 88bce30a..6fe76998 100644 --- a/src/renderer/utils/music/tx/index.js +++ b/src/renderer/utils/music/tx/index.js @@ -1,11 +1,13 @@ import leaderboard from './leaderboard' import lyric from './lyric' import songList from './songList' +import musicSearch from './musicSearch' import api_source from '../api-source' const tx = { leaderboard, songList, + musicSearch, getMusicUrl(songInfo, type) { return api_source('tx').getMusicUrl(songInfo, type) diff --git a/src/renderer/utils/music/tx/musicSearch.js b/src/renderer/utils/music/tx/musicSearch.js new file mode 100644 index 00000000..188943f7 --- /dev/null +++ b/src/renderer/utils/music/tx/musicSearch.js @@ -0,0 +1,106 @@ +// import '../../polyfill/array.find' +// import jshtmlencode from 'js-htmlencode' +import { httpFetch } from '../../request' +import { formatPlayTime, sizeFormate } from '../../index' +// import { debug } from '../../utils/env' +// import { formatSinger } from './util' + +let searchRequest +export default { + limit: 30, + total: 0, + page: 0, + allPage: 1, + successCode: 0, + musicSearch(str, page, retryNum = 0) { + if (searchRequest && searchRequest.cancelHttp) searchRequest.cancelHttp() + if (retryNum > 5) return Promise.reject(new Error('搜索失败')) + searchRequest = httpFetch(`https://c.y.qq.com/soso/fcgi-bin/client_search_cp?ct=24&qqmusic_ver=1298&new_json=1&remoteplace=sizer.yqq.song_next&searchid=49252838123499591&t=0&aggr=1&cr=1&catZhida=1&lossless=0&flag_qc=0&p=${page}&n=${this.limit}&w=${encodeURIComponent(str)}&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0`) + // searchRequest = httpFetch(`http://ioscdn.kugou.com/api/v3/search/song?keyword=${encodeURIComponent(str)}&page=${page}&pagesize=${this.limit}&showtype=10&plat=2&version=7910&tag=1&correct=1&privilege=1&sver=5`) + return searchRequest.promise.then(({ body }) => { + if (body.code !== this.successCode) return this.musicSearch(str, page, ++retryNum) + return body.data + }) + }, + getSinger(singers) { + let arr = [] + singers.forEach(singer => { + arr.push(singer.name) + }) + return arr.join('、') + }, + handleResult(rawList) { + // console.log(rawData) + return rawList.map(item => { + let types = [] + let _types = {} + if (item.file.size_128mp3 !== 0) { + let size = sizeFormate(item.file.size_128mp3) + types.push({ type: '128k', size }) + _types['128k'] = { + size, + } + } + if (item.file.size_320mp3 !== 0) { + let size = sizeFormate(item.file.size_320mp3) + types.push({ type: '320k', size }) + _types['320k'] = { + size, + } + } + if (item.file.size_ape !== 0) { + let size = sizeFormate(item.file.size_ape) + types.push({ type: 'ape', size }) + _types.ape = { + size, + } + } + if (item.file.size_flac !== 0) { + let size = sizeFormate(item.file.size_flac) + types.push({ type: 'flac', size }) + _types.flac = { + size, + } + } + // types.reverse() + return { + singer: this.getSinger(item.singer), + name: item.title, + albumName: item.album.title, + albumId: item.album.mid, + source: 'tx', + interval: formatPlayTime(item.interval), + songId: item.id, + albumMid: item.album.mid, + strMediaMid: item.file.strMediaMid, + songmid: item.mid, + img: (item.album.name === '' || item.album.name === '空') + ? `https://y.gtimg.cn/music/photo_new/T001R500x500M000${item.singer[0].mid}.jpg` + : `https://y.gtimg.cn/music/photo_new/T002R500x500M000${item.album.mid}.jpg`, + lrc: null, + types, + _types, + typeUrl: {}, + } + }) + }, + search(str, page = 1, { limit } = {}) { + if (limit != null) this.limit = limit + // http://newlyric.kuwo.cn/newlyric.lrc?62355680 + return this.musicSearch(str, page).then(({ song }) => { + let list = this.handleResult(song.list) + + this.total = song.totalnum + this.page = page + this.allPage = Math.ceil(this.total / this.limit) + + return Promise.resolve({ + list, + allPage: this.allPage, + limit: this.limit, + total: this.total, + source: 'tx', + }) + }) + }, +} diff --git a/src/renderer/utils/music/utils.js b/src/renderer/utils/music/utils.js index 2b43f645..793e237e 100644 --- a/src/renderer/utils/music/utils.js +++ b/src/renderer/utils/music/utils.js @@ -6,9 +6,10 @@ const types = ['flac', 'ape', '320k', '192k', '128k'] export const getMusicType = (info, type) => { - switch (window.globalObj.apiSource) { + switch (info.source) { // case 'kg': case 'wy': + case 'tx': return '128k' } const rangeType = types.slice(types.indexOf(type)) diff --git a/src/renderer/views/List.vue b/src/renderer/views/List.vue index 1f7a264b..b8f7d956 100644 --- a/src/renderer/views/List.vue +++ b/src/renderer/views/List.vue @@ -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') || item.source == 'tx' || item.source == 'wy' ? $style.disabled : '']") + @click="handleDoubleClick(index)" :class="[isPlayList && playIndex === index ? $style.active : '', (isAPITemp && item.source != 'kw') || item.source == 'wy' ? $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}} @@ -213,7 +213,7 @@ export default { this.clickIndex = -1 }, testPlay(index) { - if ((this.isAPITemp && this.list[index].source != 'kw') || this.list[index].source == 'tx' || this.list[index].source == 'wy') return + if ((this.isAPITemp && this.list[index].source != 'kw') || this.list[index].source == 'wy') return this.setPlayList({ list: this.list, listId: this.listId, index }) }, handleRemove(index) { @@ -223,7 +223,7 @@ export default { switch (info.action) { case 'download': { const minfo = this.list[info.index] - if ((this.isAPITemp && minfo.source != 'kw') || minfo.source == 'tx' || minfo.source == 'wy') return + if ((this.isAPITemp && minfo.source != 'kw') || minfo.source == 'wy') return this.musicInfo = minfo this.$nextTick(() => { this.isShowDownload = true @@ -256,7 +256,7 @@ export default { this.selectdData = [] }, handleAddDownloadMultiple(type) { - const list = this.setting.apiSource == 'temp' ? this.selectdData.filter(s => s.source == 'kw') : this.selectdData.filter(s => s.source != 'tx' && s.source != 'wy') + const list = this.setting.apiSource == 'temp' ? this.selectdData.filter(s => s.source == 'kw') : this.selectdData.filter(s => s.source != 'wy') this.createDownloadMultiple({ list, type }) this.resetSelect() this.isShowDownloadMultiple = false diff --git a/src/renderer/views/Search.vue b/src/renderer/views/Search.vue index 48406f7b..d683a5a6 100644 --- a/src/renderer/views/Search.vue +++ b/src/renderer/views/Search.vue @@ -30,8 +30,8 @@ td.break(style="width: 25%;") {{item.albumName}} td(style="width: 15%; padding-left: 0; padding-right: 0;") material-list-buttons(:index="index" :remove-btn="false" :class="$style.listBtn" - :play-btn="item.source == 'kw' || (!isAPITemp && item.source != 'tx' && item.source != 'wy')" - :download-btn="item.source == 'kw' || (!isAPITemp && item.source != 'tx' && item.source != 'wy')" + :play-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')" + :download-btn="item.source == 'kw' || (!isAPITemp && item.source != 'wy')" @btn-click="handleListBtnClick") td(style="width: 10%;") {{item.interval}} div(:class="$style.pagination") @@ -189,7 +189,7 @@ export default { targetSong = this.selectdData[0] this.listAddMultiple({ id: 'default', list: this.filterList(this.selectdData) }) } else { - if ((this.isAPITemp && this.listInfo.list[index].source != 'kw') || this.listInfo.list[index].source == 'tx' || this.listInfo.list[index].source == 'wy') return + if ((this.isAPITemp && this.listInfo.list[index].source != 'kw') || this.listInfo.list[index].source == 'wy') return targetSong = this.listInfo.list[index] this.listAdd({ id: 'default', musicInfo: targetSong }) } @@ -238,7 +238,7 @@ export default { } }, filterList(list) { - return this.setting.apiSource == 'temp' ? list.filter(s => s.source == 'kw') : list.filter(s => s.source != 'tx' && s.source != 'wy') + return this.setting.apiSource == 'temp' ? list.filter(s => s.source == 'kw') : list.filter(s => s.source != 'wy') }, handleListAddModalClose(isSelect) { if (isSelect) this.resetSelect()