add qq music search

master
Janx 2 years ago
parent 3361a5086a
commit affc7849a1

@ -21,8 +21,6 @@ export function getAlbum(id) {
return data;
});
};
fetchLatest();
return getAlbumFromCache(id).then(result => {
return result ?? fetchLatest();
});

@ -20,7 +20,9 @@ export function search(params) {
return request({
url: '/search',
method: 'get',
params,
params: {
...params,
},
}).then(data => {
if (data.result?.song !== undefined)
data.result.song.songs = mapTrackPlayableStatus(data.result.song.songs);

@ -42,15 +42,21 @@ export function dailyRecommendPlaylist(params) {
* @param {number} id
* @param {boolean=} noCache
*/
export function getPlaylistDetail(id, noCache = false) {
export function getPlaylistDetail(id, noCache = false, server) {
let params = { id };
if (noCache) params.timestamp = new Date().getTime();
return request({
url: '/playlist/detail',
method: 'get',
params,
params: {
...params,
server,
},
}).then(data => {
if (data.playlist) {
if (data.playlist.source) {
return data;
}
data.playlist.tracks = mapTrackPlayableStatus(
data.playlist.tracks,
data.privileges || []

@ -53,8 +53,6 @@ export function getTrackDetail(ids) {
return data;
});
};
fetchLatest();
let idsInArray = [String(ids)];
if (typeof ids === 'string') {
idsInArray = ids.split(',');
@ -71,25 +69,23 @@ export function getTrackDetail(ids) {
/**
* 获取歌词
* 说明 : 调用此接口 , 传入音乐 id 可获得对应音乐的歌词 ( 不需要登录 )
* @param {number} id - 音乐 id
* @param id - 音乐 id
*/
export function getLyric(id) {
export function getLyric(id, server) {
const fetchLatest = () => {
return request({
url: '/lyric',
method: 'get',
params: {
id,
server: server ? server : undefined,
},
}).then(result => {
cacheLyric(id, result);
cacheLyric(`${server ? server : ''}${id}`, result);
return result;
});
};
fetchLatest();
return getLyricFromCache(id).then(result => {
return getLyricFromCache(`${server ? server : ''}${id}`).then(result => {
return result ?? fetchLatest();
});
}

@ -16,7 +16,12 @@
><svg-icon icon-class="play" />
</button>
</div>
<img :src="imageUrl" :style="imageStyles" loading="lazy" />
<img
:src="imageUrl"
onerror="this.src = 'https://p2.music.126.net/UeTuwE7pvjBpypWLudqukA==/3132508627578625.jpg'; this.onerror=null;"
:style="imageStyles"
loading="lazy"
/>
<transition v-if="coverHover || alwaysShowShadow" name="fade">
<div
v-show="focus || alwaysShowShadow"

@ -7,7 +7,7 @@
:class="{ artist: type === 'artist' }"
>
<Cover
:id="item.id"
:id="parseInt(item.id)"
:image-url="getImageUrl(item)"
:type="type"
:play-button-size="type === 'artist' ? 26 : playButtonSize"
@ -99,7 +99,8 @@ export default {
return this.type === 'album' && item.mark === 1056768;
},
getTitleLink(item) {
return `/${this.type}/${item.id}`;
let server = this.$route.query.server;
return `/${this.type}/${item.id}?${server ? 'server=' + server : ''}`;
},
getImageUrl(item) {
if (item.img1v1Url) {

@ -41,6 +41,9 @@
@blur="inputFocus = false"
/>
</div>
<a @click="showSearchList">
<svg-icon icon-class="arrow-down" @click="showSearchList" />
</a>
</div>
</div>
<img
@ -51,7 +54,12 @@
/>
</div>
</nav>
<ContextMenu ref="showSearchList">
<div class="item" @click="toCoSearch('tencent')">
<svg-icon icon-class="settings" />
QQ音乐
</div>
</ContextMenu>
<ContextMenu ref="userProfileMenu">
<div class="item" @click="toSettings">
<svg-icon icon-class="settings" />
@ -155,6 +163,15 @@ export default {
showUserProfileMenu(e) {
this.$refs.userProfileMenu.openMenu(e);
},
showSearchList(e) {
this.$refs.showSearchList.openMenu(e);
},
toCoSearch(item) {
this.$router.push({
name: 'coSearch',
query: { server: item, keywords: this.keywords },
});
},
logout() {
if (!confirm('确定要退出登录吗?')) return;
doLogout();

@ -28,6 +28,7 @@
<img
:src="currentTrack.al && currentTrack.al.picUrl | resizeImage(224)"
loading="lazy"
onerror="this.src = 'https://p2.music.126.net/UeTuwE7pvjBpypWLudqukA==/3132508627578625.jpg'; this.onerror=null;"
@click="goToAlbum"
/>
<div class="track-info" :title="audioSource">

@ -62,11 +62,11 @@
<div :style="listStyles">
<TrackListItem
v-for="(track, index) in tracks"
v-for="(track, index) in tracks.slice(0, maxSize)"
:key="itemKey === 'id' ? track.id : `${track.id}${index}`"
:track-prop="track"
:highlight-playing-track="highlightPlayingTrack"
@dblclick.native="playThisList(track.id || track.songId)"
@dblclick.native="playThisList(track)"
@click.right.native="openMenu($event, track, index)"
/>
</div>
@ -101,8 +101,12 @@ export default {
default: 'tracklist',
}, // tracklist | album | playlist | cloudDisk
id: {
type: Number,
default: 0,
type: String,
default: '0',
},
maxSize: {
type: String,
default: '20',
},
dbclickTrackFunc: {
type: String,
@ -195,7 +199,17 @@ export default {
};
this.rightClickedTrackIndex = -1;
},
playThisListByTrack(track) {
this.showToast('正在进行其他平台播放');
let tracks = this.tracks.filter(_track => {
return _track.playable == 1;
});
this.player.replacePlaylist(tracks, track, 'artist', track);
},
playThisList(trackID) {
if (trackID.constructor === Object) this.playThisListByTrack(trackID);
trackID = trackID.id || trackID.songId;
console.log(this.dbclickTrackFunc);
if (this.dbclickTrackFunc === 'default') {
this.playThisListDefault(trackID);
} else if (this.dbclickTrackFunc === 'none') {

@ -4,6 +4,7 @@
:class="trackClass"
:style="trackStyle"
:title="showUnavailableSongInGreyStyle ? track.reason : ''"
@click="playTrack"
@mouseover="hover = true"
@mouseleave="hover = false"
>
@ -12,7 +13,8 @@
:src="imgUrl"
loading="lazy"
:class="{ hover: focus }"
@click="goToAlbum"
onerror="this.src = 'https://p2.music.126.net/UeTuwE7pvjBpypWLudqukA==/3132508627578625.jpg'; this.onerror=null;"
@click.stop="goToAlbum"
/>
<div v-if="showOrderNumber" class="no">
<button v-show="focus && playable && !isPlaying" @click="playTrack">
@ -118,6 +120,7 @@ export default {
},
imgUrl() {
let image =
this.track?.picUrl ??
this.track?.al?.picUrl ??
this.track?.album?.picUrl ??
'https://p2.music.126.net/UeTuwE7pvjBpypWLudqukA==/3132508627578625.jpg';
@ -210,10 +213,16 @@ export default {
methods: {
goToAlbum() {
if (this.track.al.id === 0) return;
if (this.track.sourceUrl) {
window.open(this.track.sourceUrl);
return;
}
this.$router.push({ path: '/album/' + this.track.al.id });
},
playTrack() {
this.$parent.playThisList(this.track.id);
if (this.track.constructor === Object)
this.$parent.playThisListByTrack(this.track);
else this.$parent.playThisList(this.track.id);
},
likeThisSong() {
this.$parent.likeATrack(this.track.id);

@ -83,6 +83,11 @@ const routes = [
name: 'searchType',
component: () => import('@/views/searchType.vue'),
},
{
path: '/coSearch',
name: 'coSearch',
component: () => import('@/views/coSearch.vue'),
},
{
path: '/new-album',
name: 'newAlbum',

@ -258,19 +258,15 @@ export default class {
let trackID = this._playNextList.shift();
return [trackID, this.current];
}
// 循环模式开启,则重新播放当前模式下的相对的下一首
if (this.repeatMode === 'on') {
if (this._reversed && this.current === 0) {
// 倒序模式,当前歌曲是第一首,则重新播放列表最后一首
return [this.list[this.list.length - 1], this.list.length - 1];
} else if (this.list.length === this.current + 1) {
// 正序模式,当前歌曲是最后一首,则重新播放第一首
return [this.list[0], 0];
}
}
// 返回 [trackID, index]
return [this.list[next], next];
}
_getPrevTrack() {
@ -488,6 +484,8 @@ export default class {
if (autoplay && this._currentTrack.name) {
this._scrobble(this.currentTrack, this._howler?.seek());
}
if (id.constructor === Object)
return this._replaceCurrentTrackByTrack(id, (autoplay = true));
return getTrackDetail(id).then(data => {
const track = data.songs[0];
this._currentTrack = track;
@ -500,6 +498,20 @@ export default class {
);
});
}
_replaceCurrentTrackByTrack(track, autoplay = true) {
if (autoplay && this._currentTrack.name) {
this._scrobble(this.currentTrack, this._howler?.seek());
}
this._currentTrack = track;
if (track.url) {
let replaced = false;
if (track.id === this.currentTrackID) {
this._playAudioSource(track.url, autoplay);
replaced = true;
}
return replaced;
}
}
/**
* @returns 是否成功加载音频并使用加载完成的音频替换了howler实例
*/
@ -553,7 +565,7 @@ export default class {
});
}
_loadSelfFromLocalStorage() {
const player = JSON.parse(localStorage.getItem('player'));
let player = JSON.parse(localStorage.getItem('player'));
if (!player) return;
for (const [key, value] of Object.entries(player)) {
this[key] = value;
@ -766,7 +778,6 @@ export default class {
if (excludeSaveKeys.includes(key)) continue;
player[key] = value;
}
localStorage.setItem('player', JSON.stringify(player));
}
@ -833,7 +844,6 @@ export default class {
}
this._howler?._sounds[0]._node.setSinkId(store.state.settings.outputDevice);
}
replacePlaylist(
trackIDs,
playlistSourceID,
@ -842,6 +852,7 @@ export default class {
) {
this._isPersonalFM = false;
if (!this._enabled) this._enabled = true;
console.log(trackIDs);
this.list = trackIDs;
this.current = 0;
this._playlistSource = {
@ -859,6 +870,7 @@ export default class {
playAlbumByID(id, trackID = 'first') {
getAlbum(id).then(data => {
let trackIDs = data.songs.map(t => t.id);
console.log('playAlbumByID');
this.replacePlaylist(trackIDs, id, 'album', trackID);
});
}
@ -868,12 +880,14 @@ export default class {
);
getPlaylistDetail(id, noCache).then(data => {
let trackIDs = data.playlist.trackIds.map(t => t.id);
console.log('getPlaylistDetail');
this.replacePlaylist(trackIDs, id, 'playlist', trackID);
});
}
playArtistByID(id, trackID = 'first') {
getArtist(id).then(data => {
let trackIDs = data.hotSongs.map(t => t.id);
console.log('playArtistByID');
this.replacePlaylist(trackIDs, id, 'artist', trackID);
});
}
@ -891,6 +905,7 @@ export default class {
const songId = data.playlist.trackIds[randomId].id;
intelligencePlaylist({ id: songId, pid: id }).then(result => {
let trackIDs = result.data.map(t => t.id);
console.log('playIntelligenceListById');
this.replacePlaylist(trackIDs, id, 'playlist', trackID);
});
});

@ -20,19 +20,23 @@ export function isTrackPlayable(track) {
if (isAccountLoggedIn() && store.state.data.user.vipType === 11) {
result.playable = true;
} else {
console.log('VIP Only');
result.playable = false;
result.reason = 'VIP Only';
}
} else if (track.fee === 4 || track.privilege?.fee === 4) {
console.log('付费专辑');
result.playable = false;
result.reason = '付费专辑';
} else if (
track.noCopyrightRcmd !== null &&
track.noCopyrightRcmd !== undefined
) {
console.log('无版权');
result.playable = false;
result.reason = '无版权';
} else if (track.privilege?.st < 0 && isAccountLoggedIn()) {
console.log('VIP Only');
result.playable = false;
result.reason = '已下架';
}

@ -133,7 +133,8 @@ export function cacheLyric(id, lyrics) {
}
export function getLyricFromCache(id) {
return db.lyric.get(Number(id)).then(result => {
return db.lyric.get(id).then(result => {
console.log(result);
if (!result) return undefined;
return result.lyrics;
});

@ -1,7 +1,7 @@
import router from '@/router';
import { doLogout, getCookie } from '@/utils/auth';
import axios from 'axios';
let baseURL = '';
let baseURL = '/api';
// Web 和 Electron 跑在不同端口避免同时启动时冲突
if (process.env.IS_ELECTRON) {
if (process.env.NODE_ENV === 'production') {
@ -13,7 +13,7 @@ if (process.env.IS_ELECTRON) {
baseURL = process.env.VUE_APP_NETEASE_API_URL;
}
const service = axios.create({
baseURL: baseURL,
baseURL,
withCredentials: true,
timeout: 15000,
});

@ -37,7 +37,11 @@
</div>
<div class="cover">
<div class="cover-container">
<img :src="imageUrl" loading="lazy" />
<img
:src="imageUrl"
loading="lazy"
onerror="this.src='https://p2.music.126.net/UeTuwE7pvjBpypWLudqukA==/3132508627578625.jpg'; this.onerror=null;"
/>
<div
class="shadow"
:style="{ backgroundImage: `url(${imageUrl})` }"
@ -362,7 +366,11 @@ export default {
},
watch: {
currentTrack() {
if (this.currentTrack.source) {
this.getLyric('tencent');
} else {
this.getLyric();
}
this.getCoverColor();
},
showLyrics(show) {
@ -440,9 +448,8 @@ export default {
this.player.playNextTrack();
}
},
getLyric() {
if (!this.currentTrack.id) return;
return getLyric(this.currentTrack.id).then(data => {
getLyric(server) {
return getLyric(this.currentTrack.id, server).then(data => {
if (!data?.lrc?.lyric) {
this.lyric = [];
this.tlyric = [];
@ -912,7 +919,6 @@ export default {
.lyrics-page.no-lyric {
.left-side {
transition: all 0.5s;
transform: translateX(27vh);
margin-right: 0;
}
}
@ -960,7 +966,6 @@ export default {
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateX(27vh);
opacity: 0;
}
</style>

@ -5,8 +5,8 @@
class="playlist-info"
>
<Cover
:id="playlist.id"
:image-url="playlist.coverImgUrl | resizeImage(1024)"
:id="parseInt(playlist.id)"
:image-url="playlist.picUrl || playlist.coverImgUrl | resizeImage(1024)"
:show-play-button="true"
:always-show-shadow="true"
:click-cover-to-play="true"
@ -44,8 +44,11 @@
{{ playlist.updateTime | formatDate }} · {{ playlist.trackCount }}
{{ $t('common.songs') }}
</div>
<div class="description" @click="toggleFullDescription">
{{ playlist.description }}
<div
class="description"
@click="toggleFullDescription"
v-html="playlist.description"
>
</div>
<div class="buttons">
<ButtonTwoTone icon-class="play" @click.native="playPlaylistByID()">
@ -164,9 +167,10 @@
</div>
<TrackList
:id="playlist.id"
:tracks="filteredTracks"
max-size="20"
type="playlist"
dbclick-track-func="none"
:extra-context-menu-item="
isUserOwnPlaylist ? ['removeTrackFromPlaylist'] : []
"
@ -447,7 +451,7 @@ export default {
},
loadData(id, next = undefined) {
this.id = id;
getPlaylistDetail(this.id, true)
getPlaylistDetail(this.id, true, this.$route.query.server || undefined)
.then(data => {
this.playlist = data.playlist;
this.tracks = data.playlist.tracks;

@ -14,7 +14,8 @@ module.exports = {
proxy: {
'^/api': {
target:
'https://service-osrz4um8-1257251314.gz.apigw.tencentcs.com/release/',
// 'https://service-osrz4um8-1257251314.gz.apigw.tencentcs.com/release/',
'http://127.0.0.1:3000/',
changeOrigin: true,
pathRewrite: {
'^/api': '/',

Loading…
Cancel
Save