diff --git a/publish/changeLog.md b/publish/changeLog.md index 296a704e..1f6c5353 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -7,6 +7,8 @@ - 新增软件内的包括字体在内的界面内容大小调整,现在当窗口大小切换到“较小/大/较大”时,软件内的元素将会适当减小或加大,窗口大小的“小”与“中”内的元素将保持之前的大小暂不做改变 - 新增音源别名,默认将显示别名,想要显示回原名可到设置切换(免责声明:别名仅是本软件用于描述各音源的标签,其名字归版权方所有) - 新增发现新版本更新失败弹窗的忽略提醒按钮,忽略提醒后,以后同一个版本再失败时将不会弹窗提醒,但仍可到设置-版本更新手动点开更新弹窗查看或恢复提醒 +- 新增热搜词,默认关闭,可到设置开启 +- 新增历史搜索记录,默认关闭,可到设置开启 ### 优化 diff --git a/src/renderer/App.vue b/src/renderer/App.vue index 3762c590..68ae0a02 100644 --- a/src/renderer/App.vue +++ b/src/renderer/App.vue @@ -53,6 +53,9 @@ export default { downloadList: 'list', downloadStatus: 'downloadStatus', }), + ...mapGetters('search', { + searchHistoryList: 'historyList', + }), }, created() { this.saveSetting = throttle(n => { @@ -67,6 +70,9 @@ export default { this.saveDownloadList = throttle(n => { window.electronStore_list.set('downloadList', n) }, 1000) + this.saveSearchHistoryList = throttle(n => { + window.electronStore_data.set('searchHistoryList', n) + }, 1000) }, mounted() { document.body.classList.add(this.isNt ? 'noTransparent' : 'transparent') @@ -97,6 +103,9 @@ export default { }, deep: true, }, + searchHistoryList(n) { + this.saveSearchHistoryList(n) + }, 'globalObj.apiSource'(n) { if (n != this.setting.apiSource) { this.setSetting(Object.assign({}, this.setting, { diff --git a/src/renderer/assets/styles/index.less b/src/renderer/assets/styles/index.less index d4f29c83..4adad004 100644 --- a/src/renderer/assets/styles/index.less +++ b/src/renderer/assets/styles/index.less @@ -157,6 +157,7 @@ input, textarea { } .scroll { + overflow: auto; &::-webkit-scrollbar { width: 6px; height: 6px; diff --git a/src/renderer/components/core/Icons.vue b/src/renderer/components/core/Icons.vue index 0502f375..6a8492ef 100644 --- a/src/renderer/components/core/Icons.vue +++ b/src/renderer/components/core/Icons.vue @@ -14,6 +14,7 @@ svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/19 g#icon-addTo path(fill='currentColor' d='M37.059,16H26V4.941C26,2.224,23.718,0,21,0s-5,2.224-5,4.941V16H4.941C2.224,16,0,18.282,0,21s2.224,5,4.941,5H16v11.059C16,39.776,18.282,42,21,42s5-2.224,5-4.941V26h11.059C39.776,26,42,23.718,42,21S39.776,16,37.059,16z') g#icon-delete + // 0 0 212.982 212.982 path(fill='currentColor' d='M131.804,106.491l75.936-75.936c6.99-6.99,6.99-18.323,0-25.312c-6.99-6.99-18.322-6.99-25.312,0l-75.937,75.937L30.554,5.242c-6.99-6.99-18.322-6.99-25.312,0c-6.989,6.99-6.989,18.323,0,25.312l75.937,75.936L5.242,182.427c-6.989,6.99-6.989,18.323,0,25.312c6.99,6.99,18.322,6.99,25.312,0l75.937-75.937l75.937,75.937c6.989,6.99,18.322,6.99,25.312,0c6.99-6.99,6.99-18.322,0-25.312L131.804,106.491z') g#icon-left // 451.847 451.847 @@ -72,5 +73,8 @@ svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/19 g#icon-refresh // 0 0 512 512 path(fill='currentColor' d='M440.65 12.57l4 82.77A247.16 247.16 0 0 0 255.83 8C134.73 8 33.91 94.92 12.29 209.82A12 12 0 0 0 24.09 224h49.05a12 12 0 0 0 11.67-9.26 175.91 175.91 0 0 1 317-56.94l-101.46-4.86a12 12 0 0 0-12.57 12v47.41a12 12 0 0 0 12 12H500a12 12 0 0 0 12-12V12a12 12 0 0 0-12-12h-47.37a12 12 0 0 0-11.98 12.57zM255.83 432a175.61 175.61 0 0 1-146-77.8l101.8 4.87a12 12 0 0 0 12.57-12v-47.4a12 12 0 0 0-12-12H12a12 12 0 0 0-12 12V500a12 12 0 0 0 12 12h47.35a12 12 0 0 0 12-12.6l-4.15-82.57A247.17 247.17 0 0 0 255.83 504c121.11 0 221.93-86.92 243.55-201.82a12 12 0 0 0-11.8-14.18h-49.05a12 12 0 0 0-11.67 9.26A175.86 175.86 0 0 1 255.83 432z') + g#icon-eraser + // 0 0 512 512 + path(fill='currentColor' d='M497.941 273.941c18.745-18.745 18.745-49.137 0-67.882l-160-160c-18.745-18.745-49.136-18.746-67.883 0l-256 256c-18.745 18.745-18.745 49.137 0 67.882l96 96A48.004 48.004 0 0 0 144 480h356c6.627 0 12-5.373 12-12v-40c0-6.627-5.373-12-12-12H355.883l142.058-142.059zm-302.627-62.627l137.373 137.373L265.373 416H150.628l-80-80 124.686-124.686z') diff --git a/src/renderer/lang/cns/view/search.json b/src/renderer/lang/cns/view/search.json index 9ae0a0fd..81ae5eed 100644 --- a/src/renderer/lang/cns/view/search.json +++ b/src/renderer/lang/cns/view/search.json @@ -8,5 +8,9 @@ "time": "时长", "lossless": "无损", "high_quality": "高品质", - "no_item": "搜我所想~~😉" + "no_item": "搜我所想~~😉", + "hot_search": "热门搜索", + "history_search": "历史搜索", + "history_clear": "清空搜索历史", + "history_remove": "右击移除该历史" } diff --git a/src/renderer/lang/cns/view/setting.json b/src/renderer/lang/cns/view/setting.json index ba5b0ea6..29b99e94 100644 --- a/src/renderer/lang/cns/view/setting.json +++ b/src/renderer/lang/cns/view/setting.json @@ -37,6 +37,12 @@ "play_mediaDevice_refresh_btn_title": "刷新音频设备列表", "play_mediaDevice_refresh_btn": "刷新", + "search": "搜索设置", + "search_hot_title": "是否显示热门搜索", + "search_hot": "热门搜索", + "search_history_title": "是否显示历史搜索记录", + "search_history": "搜索历史", + "list": "列表设置", "list_source_title": "是否显示歌曲源", "list_source": "是否显示歌曲源(仅对我的音乐分类有效)", diff --git a/src/renderer/lang/cnt/view/search.json b/src/renderer/lang/cnt/view/search.json index 9c2efe41..8e84801a 100644 --- a/src/renderer/lang/cnt/view/search.json +++ b/src/renderer/lang/cnt/view/search.json @@ -8,5 +8,9 @@ "time": "時長", "lossless": "無損", "high_quality": "高品質", - "no_item": "搜我所想~~😉" + "no_item": "搜我所想~~😉", + "hot_search": "熱門搜索", + "history_search": "歷史搜索", + "history_clear": "清空搜索歷史", + "history_remove": "右擊移除該歷史" } diff --git a/src/renderer/lang/cnt/view/setting.json b/src/renderer/lang/cnt/view/setting.json index 8e84c792..50011d1c 100644 --- a/src/renderer/lang/cnt/view/setting.json +++ b/src/renderer/lang/cnt/view/setting.json @@ -35,6 +35,11 @@ "play_mediaDevice": "音頻輸出", "play_mediaDevice_refresh_btn_title": "刷新音頻設備列表", "play_mediaDevice_refresh_btn": "刷新", + "search": "搜索設置", + "search_hot_title": "是否顯示熱門搜索", + "search_hot": "熱門搜索", + "search_history_title": "是否顯示歷史搜索記錄", + "search_history": "搜索歷史", "list": "列表設置", "list_source_title": "是否顯示歌曲源", "list_source": "是否顯示歌曲源(僅對我的音樂分類有效)", diff --git a/src/renderer/lang/en/view/search.json b/src/renderer/lang/en/view/search.json index df76aa77..5d33ed29 100644 --- a/src/renderer/lang/en/view/search.json +++ b/src/renderer/lang/en/view/search.json @@ -8,5 +8,9 @@ "time": "Interval", "lossless": "SQ", "high_quality": "HQ", - "no_item": "Search what i think~~😉" + "no_item": "Search what i think~~😉", + "hot_search": "Hot Search", + "history_search": "History Search", + "history_clear": "Clear Search History", + "history_remove": "Right click to remove this history" } diff --git a/src/renderer/lang/en/view/setting.json b/src/renderer/lang/en/view/setting.json index bad0c775..1ff617cd 100644 --- a/src/renderer/lang/en/view/setting.json +++ b/src/renderer/lang/en/view/setting.json @@ -37,6 +37,12 @@ "play_mediaDevice_refresh_btn_title": "Refresh audio device list", "play_mediaDevice_refresh_btn": "refresh", + "search": "Search Settings", + "search_hot_title": "Show hot searches", + "search_hot": "Hot Search", + "search_history_title": "Show search history", + "search_history": "Search History", + "list": "List settings", "list_source_title": "Whether to show song sources", "list_source": "Whether to show song sources (only valid for my music category)", diff --git a/src/renderer/store/modules/hotSearch.js b/src/renderer/store/modules/hotSearch.js new file mode 100644 index 00000000..3368276e --- /dev/null +++ b/src/renderer/store/modules/hotSearch.js @@ -0,0 +1,87 @@ +import music from '../../utils/music' +const sources = [] +const sourceList = {} +for (const source of music.sources) { + const hotSearch = music[source.id].hotSearch + if (!hotSearch) continue + sources.push(source) + sourceList[source.id] = [] +} + +sources.push({ + id: 'all', + name: '聚合搜索', +}) + +// state +const state = { + list: { + ...sourceList, + all: [], + }, + currentSource: null, +} + +// getters +const getters = { + sources(state, getters, rootState, { sourceNames }) { + return sources.map(item => ({ id: item.id, name: sourceNames[item.id] })) + }, + list: state => state.list, +} + +// actions +const actions = { + getList({ state, commit }, source) { + if (source == 'all') { + let task = [] + for (const source of sources) { + if (source.id == 'all') continue + task.push( + state.list[source.id].length + ? Promise.resolve({ source: source.id, list: state.list[source.id] }) + : music[source.id].hotSearch.getList(), + ) + } + Promise.all(task).then(results => commit('setLists', results)) + } else { + if (!music[source].hotSearch) { + commit('setList', { source, data: null }) + return Promise.resolve() + } + return music[source].hotSearch.getList() + .then(data => commit('setList', { source, data })) + } + }, +} + +// mitations +const mutations = { + setList(state, { source, data }) { + state.list[source] = data ? data.list.slice(0, 20) : [] + }, + setLists(state, lists) { + let list = [] + for (const source of lists) { + if (!state.list[source.source].length) state.list[source.source] = source.list.slice(0, 20) + const sourceList = source.list.slice(0, 10) + for (const item of sourceList) { + list.push(item.trim()) + } + } + list = Array.from(new Set(list)) + list.sort((a, b) => a.charCodeAt(0) - b.charCodeAt(0)) + state.list.all = list + }, + clearList(state, source) { + state.list[source] = [] + }, +} + +export default { + namespaced: true, + state, + getters, + actions, + mutations, +} diff --git a/src/renderer/store/modules/list.js b/src/renderer/store/modules/list.js index b1f9fed2..e22c0d63 100644 --- a/src/renderer/store/modules/list.js +++ b/src/renderer/store/modules/list.js @@ -85,7 +85,7 @@ const mutations = { listClear(state, id) { let targetList = getList(state, id) if (!targetList) return - targetList.list.length = 0 + targetList.list.length = [] }, updateMusicInfo(state, { id, index, data }) { let targetList = getList(state, id) diff --git a/src/renderer/store/modules/search.js b/src/renderer/store/modules/search.js index 5b0d8a48..18ce121b 100644 --- a/src/renderer/store/modules/search.js +++ b/src/renderer/store/modules/search.js @@ -1,4 +1,15 @@ +import Store from 'electron-store' import music from '../../utils/music' + +const electronStore_data = window.electronStore_data = new Store({ + name: 'data', +}) +let historyList = electronStore_data.get('searchHistoryList') +if (historyList == null) { + historyList = [] + electronStore_data.set('searchHistoryList', historyList) +} + const sources = [] const sourceList = {} const sourceMaxPage = {} @@ -31,6 +42,7 @@ const state = { allPage: 1, total: 0, sourceMaxPage, + historyList, } // getters @@ -40,28 +52,34 @@ const getters = { }, sourceList: state => state.sourceList || [], searchText: state => state.text, + historyList: state => state.historyList, allList: state => ({ list: state.list, allPage: state.allPage, page: state.page, total: state.total, limit: state.limit, sourceMaxPage: state.sourceMaxPage }), } // actions const actions = { search({ commit, rootState }, { text, page, limit }) { + commit('setText', text) + commit('addHistory', text) if (rootState.setting.search.searchSource == 'all') { let task = [] for (const source of sources) { if (source.id == 'all') continue task.push(music[source.id].musicSearch.search(text, page)) } - Promise.all(task).then(results => commit('setLists', { results, text, page })) + Promise.all(task).then(results => commit('setLists', { results, page })) } else { return music[rootState.setting.search.searchSource].musicSearch.search(text, page, limit) - .then(data => commit('setList', { text, page, ...data })) + .then(data => commit('setList', { page, ...data })) } }, } // mitations const mutations = { + setText(state, text) { + state.text = text + }, setList(state, datas) { let source = state.sourceList[datas.source] source.list = datas.list @@ -69,9 +87,8 @@ const mutations = { source.allPage = datas.allPage source.page = datas.page source.limit = datas.limit - state.text = datas.text }, - setLists(state, { results, text, page }) { + setLists(state, { results, page }) { let pages = [] let total = 0 let limit = 0 @@ -84,28 +101,39 @@ const mutations = { total += source.total limit += source.limit } - list.sort((a, b) => b.name.charCodeAt(0) - a.name.charCodeAt(0)) + list.sort((a, b) => a.name.charCodeAt(0) - b.name.charCodeAt(0)) state.allPage = Math.max(...pages) state.total = total state.limit = limit state.page = page - state.text = text state.list = list }, clearList(state) { for (const source of Object.keys(state.sourceList)) { - state.sourceList[source].list.length = 0 + state.sourceList[source].list = [] state.sourceList[source].page = 0 state.sourceList[source].allPage = 0 state.sourceList[source].total = 0 state.sourceMaxPage[source] = 0 } - state.list.length = 0 + state.list.length = [] state.page = 0 state.allPage = 0 state.total = 0 state.text = '' }, + addHistory(state, text) { + let index = state.historyList.indexOf(text) + if (index > -1) state.historyList.splice(index, 1) + if (state.historyList.length >= 15) state.historyList = state.historyList.slice(0, 14) + state.historyList.unshift(text) + }, + removeHistory(state, index) { + state.historyList.splice(index, 1) + }, + clearHistory(state) { + state.historyList = [] + }, } export default { diff --git a/src/renderer/utils/index.js b/src/renderer/utils/index.js index 1e7f60c2..2d4582a7 100644 --- a/src/renderer/utils/index.js +++ b/src/renderer/utils/index.js @@ -177,7 +177,7 @@ export const isChildren = (parent, children) => { * @param {*} setting */ export const updateSetting = (setting, version) => { - const defaultVersion = '1.0.18' + const defaultVersion = '1.0.19' if (!version) { if (setting) { version = setting.version @@ -226,6 +226,8 @@ export const updateSetting = (setting, version) => { search: { searchSource: 'kw', tempSearchSource: 'kw', + isShowHotSearch: false, + isShowHistorySearch: false, }, network: { proxy: { diff --git a/src/renderer/utils/music/bd/hotSearch.js b/src/renderer/utils/music/bd/hotSearch.js new file mode 100644 index 00000000..5a9cbf2c --- /dev/null +++ b/src/renderer/utils/music/bd/hotSearch.js @@ -0,0 +1,23 @@ +import { httpFetch } from '../../request' + +export default { + _requestObj: null, + async getList(retryNum = 0) { + if (this._requestObj) this._requestObj.cancelHttp() + if (retryNum > 2) return Promise.reject(new Error('try max num')) + + const _requestObj = httpFetch('http://musicapi.qianqian.com/v1/restserver/ting?from=android&version=7.0.2.0&channel=ppzs&operator=0&method=baidu.ting.search.hot', { + method: 'get', + headers: { + 'User-Agent': 'android_7.0.2.0;baiduyinyue', + }, + }) + const { body, statusCode } = await _requestObj.promise + if (statusCode != 200 || body.error_code !== 22000) throw new Error('获取热搜词失败') + // console.log(body, statusCode) + return { source: 'bd', list: this.filterList(body.result) } + }, + filterList(rawList) { + return rawList.map(item => item.word) + }, +} diff --git a/src/renderer/utils/music/bd/index.js b/src/renderer/utils/music/bd/index.js index aaab3393..2f40dca0 100644 --- a/src/renderer/utils/music/bd/index.js +++ b/src/renderer/utils/music/bd/index.js @@ -4,11 +4,13 @@ import musicInfo from './musicInfo' import songList from './songList' import { httpFetch } from '../../request' import musicSearch from './musicSearch' +import hotSearch from './hotSearch' const bd = { leaderboard, songList, musicSearch, + hotSearch, getMusicUrl(songInfo, type) { return api_source('bd').getMusicUrl(songInfo, type) }, diff --git a/src/renderer/utils/music/kg/hotSearch.js b/src/renderer/utils/music/kg/hotSearch.js new file mode 100644 index 00000000..2cafe5b1 --- /dev/null +++ b/src/renderer/utils/music/kg/hotSearch.js @@ -0,0 +1,32 @@ +import { httpFetch } from '../../request' + +export default { + _requestObj: null, + async getList(retryNum = 0) { + if (this._requestObj) this._requestObj.cancelHttp() + if (retryNum > 2) return Promise.reject(new Error('try max num')) + + const _requestObj = httpFetch('http://gateway.kugou.com/api/v3/search/hot_tab?signature=ee44edb9d7155821412d220bcaf509dd&appid=1005&clientver=10026&plat=0', { + method: 'get', + headers: { + dfid: '1ssiv93oVqMp27cirf2CvoF1', + mid: '156798703528610303473757548878786007104', + clienttime: 1584257267, + 'x-router': 'msearch.kugou.com', + 'user-agent': 'Android9-AndroidPhone-10020-130-0-searchrecommendprotocol-wifi', + 'kg-rc': 1, + }, + }) + const { body, statusCode } = await _requestObj.promise + if (statusCode != 200 || body.errcode !== 0) throw new Error('获取热搜词失败') + // console.log(body, statusCode) + return { source: 'kg', list: this.filterList(body.data.list) } + }, + filterList(rawList) { + const list = [] + rawList.forEach(item => { + item.keywords.map(k => list.push(k.keyword)) + }) + return list + }, +} diff --git a/src/renderer/utils/music/kg/index.js b/src/renderer/utils/music/kg/index.js index 751a8143..243ee632 100644 --- a/src/renderer/utils/music/kg/index.js +++ b/src/renderer/utils/music/kg/index.js @@ -4,11 +4,13 @@ import songList from './songList' import musicSearch from './musicSearch' import pic from './pic' import lyric from './lyric' +import hotSearch from './hotSearch' const kg = { leaderboard, songList, musicSearch, + hotSearch, getMusicUrl(songInfo, type) { return api_source('kg').getMusicUrl(songInfo, type) }, diff --git a/src/renderer/utils/music/kw/hotSearch.js b/src/renderer/utils/music/kw/hotSearch.js new file mode 100644 index 00000000..7f3fd7a9 --- /dev/null +++ b/src/renderer/utils/music/kw/hotSearch.js @@ -0,0 +1,22 @@ +import { httpFetch } from '../../request' + +export default { + _requestObj: null, + async getList(retryNum = 0) { + if (this._requestObj) this._requestObj.cancelHttp() + if (retryNum > 2) return Promise.reject(new Error('try max num')) + + const _requestObj = httpFetch('http://hotword.kuwo.cn/hotword.s?prod=kwplayer_ar_9.3.0.1&corp=kuwo&newver=2&vipver=9.3.0.1&source=kwplayer_ar_9.3.0.1_40.apk&p2p=1¬race=0&uid=0&plat=kwplayer_ar&rformat=json&encoding=utf8&tabid=1', { + headers: { + 'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 9;)', + }, + }) + const { body, statusCode } = await _requestObj.promise + if (statusCode != 200 || body.status !== 'ok') throw new Error('获取热搜词失败') + // console.log(body, statusCode) + return { source: 'kw', list: this.filterList(body.tagvalue) } + }, + filterList(rawList) { + return rawList.map(item => item.key) + }, +} diff --git a/src/renderer/utils/music/kw/index.js b/src/renderer/utils/music/kw/index.js index ae883f9e..4bd05179 100644 --- a/src/renderer/utils/music/kw/index.js +++ b/src/renderer/utils/music/kw/index.js @@ -7,6 +7,7 @@ import lyric from './lyric' import pic from './pic' import api_source from '../api-source' import songList from './songList' +import hotSearch from './hotSearch' const kw = { _musicInfoRequestObj: null, @@ -34,6 +35,7 @@ const kw = { musicSearch, leaderboard, songList, + hotSearch, getLyric(songInfo) { // let singer = songInfo.singer.indexOf('、') > -1 ? songInfo.singer.split('、')[0] : songInfo.singer return lyric.getLyric(songInfo.songmid) diff --git a/src/renderer/utils/music/mg/hotSearch.js b/src/renderer/utils/music/mg/hotSearch.js new file mode 100644 index 00000000..70691847 --- /dev/null +++ b/src/renderer/utils/music/mg/hotSearch.js @@ -0,0 +1,18 @@ +import { httpFetch } from '../../request' + +export default { + _requestObj: null, + async getList(retryNum = 0) { + if (this._requestObj) this._requestObj.cancelHttp() + if (retryNum > 2) return Promise.reject(new Error('try max num')) + + const _requestObj = httpFetch('http://jadeite.migu.cn:7090/music_search/v2/search/hotword') + const { body, statusCode } = await _requestObj.promise + if (statusCode != 200 || body.code !== '000000') throw new Error('获取热搜词失败') + // console.log(body, statusCode) + return { source: 'mg', list: this.filterList(body.data) } + }, + filterList(rawList) { + return rawList.map(item => item.word) + }, +} diff --git a/src/renderer/utils/music/mg/index.js b/src/renderer/utils/music/mg/index.js index dda6d214..8cdde2c9 100644 --- a/src/renderer/utils/music/mg/index.js +++ b/src/renderer/utils/music/mg/index.js @@ -4,11 +4,13 @@ import songList from './songList' import musicSearch from './musicSearch' import pic from './pic' import lyric from './lyric' +import hotSearch from './hotSearch' const mg = { songList, musicSearch, leaderboard, + hotSearch, getMusicUrl(songInfo, type) { return api_source('mg').getMusicUrl(songInfo, type) }, diff --git a/src/renderer/utils/music/tx/hotSearch.js b/src/renderer/utils/music/tx/hotSearch.js new file mode 100644 index 00000000..f1451e21 --- /dev/null +++ b/src/renderer/utils/music/tx/hotSearch.js @@ -0,0 +1,23 @@ +import { httpFetch } from '../../request' + +export default { + _requestObj: null, + async getList(retryNum = 0) { + if (this._requestObj) this._requestObj.cancelHttp() + if (retryNum > 2) return Promise.reject(new Error('try max num')) + + const _requestObj = httpFetch('https://c.y.qq.com/splcloud/fcgi-bin/gethotkey.fcg', { + method: 'get', + headers: { + Referer: 'https://y.qq.com/portal/player.html', + }, + }) + const { body, statusCode } = await _requestObj.promise + if (statusCode != 200 || body.code !== 0) throw new Error('获取热搜词失败') + // console.log(body) + return { source: 'tx', list: this.filterList(body.data.hotkey) } + }, + filterList(rawList) { + return rawList.map(item => item.k) + }, +} diff --git a/src/renderer/utils/music/tx/index.js b/src/renderer/utils/music/tx/index.js index 6fe76998..db678147 100644 --- a/src/renderer/utils/music/tx/index.js +++ b/src/renderer/utils/music/tx/index.js @@ -3,11 +3,13 @@ import lyric from './lyric' import songList from './songList' import musicSearch from './musicSearch' import api_source from '../api-source' +import hotSearch from './hotSearch' const tx = { leaderboard, songList, musicSearch, + hotSearch, getMusicUrl(songInfo, type) { return api_source('tx').getMusicUrl(songInfo, type) diff --git a/src/renderer/utils/music/wy/hotSearch.js b/src/renderer/utils/music/wy/hotSearch.js new file mode 100644 index 00000000..5ee4ffa4 --- /dev/null +++ b/src/renderer/utils/music/wy/hotSearch.js @@ -0,0 +1,26 @@ +import { httpFetch } from '../../request' +import { weapi } from './utils/crypto' + +export default { + _requestObj: null, + async getList(retryNum = 0) { + if (this._requestObj) this._requestObj.cancelHttp() + if (retryNum > 2) return Promise.reject(new Error('try max num')) + + const _requestObj = httpFetch('https://music.163.com/weapi/search/hot', { + method: 'post', + headers: { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', + origin: 'https://music.163.com', + }, + form: weapi({ type: 1111 }), + }) + const { body, statusCode } = await _requestObj.promise + if (statusCode != 200 || body.code !== 200) throw new Error('获取热搜词失败') + // console.log(body) + return { source: 'wy', list: this.filterList(body.result.hots) } + }, + filterList(rawList) { + return rawList.map(item => item.first) + }, +} diff --git a/src/renderer/utils/music/wy/index.js b/src/renderer/utils/music/wy/index.js index 65056dd1..4a2f9019 100644 --- a/src/renderer/utils/music/wy/index.js +++ b/src/renderer/utils/music/wy/index.js @@ -4,11 +4,13 @@ import getLyric from './lyric' import getMusicInfo from './musicInfo' import musicSearch from './musicSearch' import songList from './songList' +import hotSearch from './hotSearch' const wy = { leaderboard, musicSearch, songList, + hotSearch, getMusicUrl(songInfo, type) { return api_source('wy').getMusicUrl(songInfo, type) }, diff --git a/src/renderer/utils/music/wy/musicSearch.js b/src/renderer/utils/music/wy/musicSearch.js index bd081e9a..f0c66ec5 100644 --- a/src/renderer/utils/music/wy/musicSearch.js +++ b/src/renderer/utils/music/wy/musicSearch.js @@ -81,7 +81,6 @@ export default { if (++retryNum > 3) return Promise.reject(new Error('try max num')) if (limit != null) this.limit = limit return this.musicSearch(str, page).then(result => { - // console.log(JSON.stringify(result)) if (!result || result.code !== 200) return this.search(str, page, { limit }, retryNum) let list = this.handleResult(result.result.songs) diff --git a/src/renderer/views/Search.vue b/src/renderer/views/Search.vue index 47d6dd61..5dcf7dda 100644 --- a/src/renderer/views/Search.vue +++ b/src/renderer/views/Search.vue @@ -41,7 +41,19 @@ div(:class="$style.pagination") material-pagination(:count="listInfo.total" :limit="listInfo.limit" :page="page" @btn-click="handleTogglePage") div(v-else :class="$style.noitem") - p {{$t('view.search.no_item')}} + div.scroll(:class="$style.noitemListContainer" v-if="setting.search.isShowHotSearch || setting.search.isShowHistorySearch") + dl(:class="[$style.noitemList, $style.noitemHotSearchList]" v-if="setting.search.isShowHotSearch") + dt(:class="$style.noitemListTitle") {{$t('view.search.hot_search')}} + dd(:class="$style.noitemListItem" @click="handleNoitemSearch(item)" v-for="item in hotSearchList") {{item}} + dl(:class="$style.noitemList" v-if="setting.search.isShowHistorySearch && historyList.length") + dt(:class="$style.noitemListTitle") + span {{$t('view.search.history_search')}} + span(:class="$style.historyClearBtn" @click="clearHistory" :title="$t('view.search.history_clear')") + svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' height='100%' viewBox='0 0 512 512' space='preserve') + use(xlink:href='#icon-eraser') + dd(:class="$style.noitemListItem" v-for="(item, index) in historyList" @contextmenu="removeHistory(index)" :key="index + item" @click="handleNoitemSearch(item)" :title="$t('view.search.history_remove')") {{item}} + div(v-else :class="$style.noitem_list") + p {{$t('view.search.no_item')}} 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 && (searchSourceId == 'kw' || searchSourceId == 'all' || !isAPITemp)" :remove-btn="false" @btn-click="handleFlowBtnClick") @@ -100,6 +112,7 @@ export default { this.page = 1 this.handleSearch(this.text, this.page) } + this.handleGetHotSearch() }, watch: { selectdData(n) { @@ -121,6 +134,7 @@ export default { this.$nextTick(() => { this.page = 1 this.handleSearch(this.text, this.page) + this.handleGetHotSearch() }) this.setSearchSource({ searchSource: n, @@ -129,7 +143,7 @@ export default { }, computed: { ...mapGetters(['userInfo', 'setting']), - ...mapGetters('search', ['sourceList', 'allList', 'sources']), + ...mapGetters('search', ['sourceList', 'allList', 'sources', 'historyList']), ...mapGetters('list', ['defaultList']), listInfo() { return this.setting.search.searchSource == 'all' ? this.allList : this.sourceList[this.setting.search.searchSource] @@ -137,14 +151,20 @@ export default { isAPITemp() { return this.setting.apiSource == 'temp' }, + hotSearchList() { + return this.$store.getters['hotSearch/list'][this.setting.search.searchSource] || [] + }, }, methods: { ...mapMutations(['setSearchSource']), ...mapActions('search', ['search']), ...mapActions('download', ['createDownload', 'createDownloadMultiple']), - ...mapMutations('search', ['clearList', 'setPage']), + ...mapMutations('search', ['clearList', 'setPage', 'removeHistory', 'clearHistory']), ...mapMutations('list', ['listAdd', 'listAddMultiple']), ...mapMutations('player', ['setList']), + ...mapActions('hotSearch', { + getHotSearch: 'getList', + }), handleSearch(text, page) { if (text === '') return this.clearList() @@ -261,6 +281,18 @@ export default { clipboardWriteText(str) }) }, + handleGetHotSearch() { + if (this.hotSearchList.length || !this.setting.search.isShowHotSearch) return + this.getHotSearch(this.setting.search.searchSource) + }, + handleNoitemSearch(text) { + this.$router.push({ + path: 'search', + query: { + text, + }, + }) + }, }, } @@ -331,11 +363,66 @@ export default { display: flex; flex-flow: column nowrap; justify-content: center; - align-items: center; p { font-size: 24px; color: @color-theme_2-font-label; + text-align: center; + } +} +.noitem-list-container { + padding: 0 15px; + margin-top: -20px; + min-height: 300px; + max-height: 94.7%; +} +.noitem-list { + +.noitem-list { + margin-top: 15px; + } +} +.noitem-hot-search-list { + min-height: 106px; +} +.noitem-list-title { + color: @color-theme_2-font-label; + padding: 5px; + font-size: 14px; +} +.noitem-list-item { + display: inline-block; + margin: 3px 5px; + background-color: @color-btn-background; + padding: 7px 10px; + border-radius: @radius-progress-border; + transition: background-color @transition-theme; + cursor: pointer; + font-size: 13px; + color: @color-btn; + .mixin-ellipsis-1; + max-width: 150px; + &:hover { + background-color: @color-theme_2-hover; + } + &:active { + background-color: @color-theme_2-active; + } +} +.history-clear-btn { + padding: 0 5px; + margin-left: 5px; + color: @color-theme_2-font-label; + cursor: pointer; + transition: color @transition-theme; + &:hover { + color: @color-theme-hover; + } + &:active { + color: @color-theme-active; + } + svg { + vertical-align: middle; + width: 15px; } } @@ -346,6 +433,28 @@ each(@themes, { color: ~'@{color-@{value}-theme_2-font-label}'; } } + .noitem-list-title { + color: ~'@{color-@{value}-theme_2-font-label}'; + } + .noitem-list-item { + 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}'; + } + } + .history-clear-btn { + color: ~'@{color-@{value}-theme_2-font-label}'; + &:hover { + color: ~'@{color-@{value}-theme-hover}'; + } + &:active { + color: ~'@{color-@{value}-theme-active}'; + } + } } }) diff --git a/src/renderer/views/Setting.vue b/src/renderer/views/Setting.vue index 7f7c44fd..74ba8cf6 100644 --- a/src/renderer/views/Setting.vue +++ b/src/renderer/views/Setting.vue @@ -64,6 +64,16 @@ div.scroll(:class="$style.setting") use(xlink:href='#icon-refresh') span {{$t('view.setting.play_mediaDevice_refresh_btn')}} + dt {{$t('view.setting.search')}} + dd(:title="$t('view.setting.search_hot_title')") + h3 {{$t('view.setting.search_hot')}} + div + material-checkbox(id="setting_search_showHot_enable" v-model="current_setting.search.isShowHotSearch" :label="$t('view.setting.is_show')") + dd(:title="$t('view.setting.search_history_title')") + h3 {{$t('view.setting.search_history')}} + div + material-checkbox(id="setting_search_showHistory_enable" v-model="current_setting.search.isShowHistorySearch" :label="$t('view.setting.is_show')") + dt {{$t('view.setting.list')}} dd(:title="$t('view.setting.list_source_title')") h3 {{$t('view.setting.list_source')}} @@ -321,6 +331,12 @@ export default { locations: {}, }, }, + search: { + searchSource: 'kw', + tempSearchSource: 'kw', + isShowHotSearch: false, + isShowHistorySearch: false, + }, download: { savePath: '', fileName: '歌名 - 歌手',