feat(播放): 添加支持直接使用playUrl播放歌曲的功能
- 在SearchResultTable和SearchResultList组件中添加playTheSongWithPlayUrl方法 - 修改播放逻辑,优先使用playUrl进行播放 - 在Mobile.vue中添加playUrl处理逻辑 - 新增tunehub服务的getSongInfo和searchSongs接口 - 重构歌曲搜索处理逻辑,使用tunehub服务替代原有实现
This commit is contained in:
@@ -1,8 +1,51 @@
|
||||
const logger = require('consola');
|
||||
const { searchSongsWithKeyword, searchSongsWithSongMeta } = require('../service/search_songs');
|
||||
const { getPlayUrlWithOptions } = require('../service/songs_info');
|
||||
const { getMetaWithUrl } = require('../service/media_fetcher');
|
||||
const { matchUrlFromStr } = require('../utils/regex');
|
||||
const { searchSongs, getSongInfo, buildSongUrl } = require('../service/music_platform/tunehub');
|
||||
const configManager = require('../service/config_manager');
|
||||
|
||||
function buildPageUrl(source, songId) {
|
||||
if (!source || !songId) {
|
||||
return '';
|
||||
}
|
||||
if (source === 'netease') {
|
||||
return `https://music.163.com/song?id=${songId}`;
|
||||
}
|
||||
if (source === 'qq') {
|
||||
return `https://y.qq.com/n/ryqq/songDetail/${songId}`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function mapTunehubResult(item) {
|
||||
const playUrl = item.url || buildSongUrl(item.platform, item.id);
|
||||
const pageUrl = buildPageUrl(item.platform, item.id);
|
||||
return {
|
||||
songName: item.name || '',
|
||||
artist: item.artist || '',
|
||||
album: item.album || '',
|
||||
duration: 0,
|
||||
url: pageUrl || playUrl || '',
|
||||
playUrl: playUrl || '',
|
||||
pageUrl: pageUrl || '',
|
||||
coverUrl: item.pic || '',
|
||||
resourceForbidden: false,
|
||||
source: item.platform || '',
|
||||
fromMusicPlatform: true,
|
||||
score: 0,
|
||||
};
|
||||
}
|
||||
|
||||
function parseNeteaseSongId(url) {
|
||||
const match = url.match(/song\\?id=(\\d+)/);
|
||||
if (match && match[1]) {
|
||||
return match[1];
|
||||
}
|
||||
const altMatch = url.match(/\\bid=(\\d+)/);
|
||||
if (altMatch && altMatch[1]) {
|
||||
return altMatch[1];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
async function search(req, res) {
|
||||
const query = req.query;
|
||||
@@ -16,31 +59,50 @@ async function search(req, res) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
let songs = [];
|
||||
let keyword = keywordOrUrl;
|
||||
const url = matchUrlFromStr(keywordOrUrl);
|
||||
if (!url) {
|
||||
songs = await searchSongsWithKeyword(keywordOrUrl);
|
||||
} else {
|
||||
const songMeta = await getMetaWithUrl(url);
|
||||
if (!songMeta) {
|
||||
res.send({
|
||||
status: 2,
|
||||
message: "can not get song meta with this url",
|
||||
});
|
||||
return;
|
||||
if (url) {
|
||||
const neteaseId = parseNeteaseSongId(url);
|
||||
if (neteaseId) {
|
||||
const info = await getSongInfo('netease', neteaseId);
|
||||
if (info && info.name && info.artist) {
|
||||
keyword = `${info.name} ${info.artist}`;
|
||||
}
|
||||
}
|
||||
songs = await searchSongsWithSongMeta({
|
||||
songName: songMeta.songName,
|
||||
artist: songMeta.artist,
|
||||
album: songMeta.album,
|
||||
duration: songMeta.duration,
|
||||
}, {
|
||||
expectArtistAkas: [],
|
||||
allowSongsJustMatchDuration: true,
|
||||
allowSongsNotMatchMeta: true,
|
||||
});
|
||||
}
|
||||
|
||||
const limit = query.limit ? parseInt(query.limit, 10) : 20;
|
||||
const requestSource = query.source || '';
|
||||
const searchData = await searchSongs(keyword, {
|
||||
source: requestSource,
|
||||
limit: Number.isNaN(limit) ? 20 : limit,
|
||||
aggregate: !requestSource,
|
||||
});
|
||||
if (searchData === false || !searchData.results) {
|
||||
res.send({
|
||||
status: 0,
|
||||
data: {
|
||||
songs: [],
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const globalConfig = await configManager.getGlobalConfig();
|
||||
const enabledSources = globalConfig && Array.isArray(globalConfig.sources) ? globalConfig.sources : [];
|
||||
const songs = searchData.results
|
||||
.filter(item => {
|
||||
if (!item.platform) {
|
||||
return false;
|
||||
}
|
||||
if (enabledSources.length === 0) {
|
||||
return true;
|
||||
}
|
||||
return enabledSources.includes(item.platform);
|
||||
})
|
||||
.map(mapTunehubResult)
|
||||
.filter(song => song.songName.length > 0);
|
||||
|
||||
res.send({
|
||||
status: 0,
|
||||
data: {
|
||||
@@ -73,4 +135,4 @@ async function getPlayUrl(req, res) {
|
||||
module.exports = {
|
||||
search: search,
|
||||
getPlayUrl: getPlayUrl
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,40 @@ async function getPlaylistDetail(source, playlistId) {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function getSongInfo(source, songId) {
|
||||
const response = await fetchJson({
|
||||
source,
|
||||
id: songId,
|
||||
type: 'info',
|
||||
});
|
||||
if (!response || response.code !== 200 || !response.data) {
|
||||
return false;
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function searchSongs(keyword, {
|
||||
source = '',
|
||||
limit = 20,
|
||||
aggregate = true,
|
||||
} = {}) {
|
||||
const params = {
|
||||
keyword,
|
||||
limit,
|
||||
};
|
||||
if (aggregate) {
|
||||
params.type = 'aggregateSearch';
|
||||
} else {
|
||||
params.type = 'search';
|
||||
params.source = source;
|
||||
}
|
||||
const response = await fetchJson(params);
|
||||
if (!response || response.code !== 200 || !response.data) {
|
||||
return false;
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
|
||||
function buildSongUrl(source, songId, br) {
|
||||
const params = {
|
||||
source,
|
||||
@@ -48,5 +82,7 @@ function buildSongUrl(source, songId, br) {
|
||||
|
||||
module.exports = {
|
||||
getPlaylistDetail,
|
||||
getSongInfo,
|
||||
searchSongs,
|
||||
buildSongUrl,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user