refactor(音乐服务): 重构网易云音乐匹配和歌曲同步逻辑
- 将网易云音乐API调用迁移至tunehub服务 - 新增歌曲信息解析和URL处理工具函数 - 改进歌曲匹配逻辑,支持更多来源的URL解析 - 优化下载流程,增加tunehub下载支持
This commit is contained in:
@@ -1,14 +1,27 @@
|
|||||||
const { searchSong, getSongInfo } = require('../music_platform/wycloud');
|
const { getSongInfo, searchSongs } = require('../music_platform/tunehub');
|
||||||
const logger = require('consola');
|
const logger = require('consola');
|
||||||
|
|
||||||
|
function splitArtists(artist) {
|
||||||
|
if (!artist) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return artist
|
||||||
|
.split(/[、/]/)
|
||||||
|
.map(item => item.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = async function findTheBestMatchFromWyCloud(uid, {songName, artist, album, musicPlatformSongId} = {}) {
|
module.exports = async function findTheBestMatchFromWyCloud(uid, {songName, artist, album, musicPlatformSongId} = {}) {
|
||||||
if (musicPlatformSongId) {
|
if (musicPlatformSongId) {
|
||||||
const songInfo = await getSongInfo(uid, musicPlatformSongId);
|
const songInfo = await getSongInfo('netease', musicPlatformSongId);
|
||||||
|
|
||||||
if (songInfo) {
|
if (songInfo) {
|
||||||
return songInfo;
|
return {
|
||||||
|
songId: musicPlatformSongId,
|
||||||
|
songName: songInfo.name || songName,
|
||||||
|
artists: splitArtists(songInfo.artist),
|
||||||
|
album: songInfo.album || album,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (songName && artist) {
|
if (songName && artist) {
|
||||||
return {
|
return {
|
||||||
songId: musicPlatformSongId,
|
songId: musicPlatformSongId,
|
||||||
@@ -24,12 +37,24 @@ module.exports = async function findTheBestMatchFromWyCloud(uid, {songName, arti
|
|||||||
if (songName === "" || artist === "") {
|
if (songName === "" || artist === "") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const searchLists = await searchSong(uid, songName, artist);
|
const searchData = await searchSongs(`${songName} ${artist}`, {
|
||||||
logger.info('searchLists', searchLists);
|
source: 'netease',
|
||||||
if (searchLists === false) {
|
limit: 20,
|
||||||
|
aggregate: false,
|
||||||
|
});
|
||||||
|
if (searchData === false || !searchData.results) {
|
||||||
logger.warn(`search song failed, no matter, go on`);
|
logger.warn(`search song failed, no matter, go on`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const searchLists = searchData.results.map(item => {
|
||||||
|
return {
|
||||||
|
songId: item.id,
|
||||||
|
songName: item.name,
|
||||||
|
artists: splitArtists(item.artist),
|
||||||
|
album: item.album,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
logger.info('searchLists', searchLists);
|
||||||
|
|
||||||
let matchSongAndArtist = null;
|
let matchSongAndArtist = null;
|
||||||
for (const searchItem of searchLists) {
|
for (const searchItem of searchLists) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const { fetchWithUrl, getMetaWithUrl } = require('../media_fetcher');
|
const { fetchWithUrl, getMetaWithUrl, downloadViaSourceUrl } = require('../media_fetcher');
|
||||||
const logger = require('consola');
|
const logger = require('consola');
|
||||||
const sleep = require('../../utils/sleep');
|
const sleep = require('../../utils/sleep');
|
||||||
const findTheBestMatchFromWyCloud = require('../search_songs/find_the_best_match_from_wycloud');
|
const findTheBestMatchFromWyCloud = require('../search_songs/find_the_best_match_from_wycloud');
|
||||||
@@ -11,6 +11,57 @@ const libPath = require('path');
|
|||||||
const utilFs = require('../../utils/fs');
|
const utilFs = require('../../utils/fs');
|
||||||
const { downloadFromLocalTmpPath } = require('./download_to_local');
|
const { downloadFromLocalTmpPath } = require('./download_to_local');
|
||||||
const uploadWithRetryThenMatch = require('./upload_to_wycloud_disk_with_retry_then_match');
|
const uploadWithRetryThenMatch = require('./upload_to_wycloud_disk_with_retry_then_match');
|
||||||
|
const { getSongInfo, buildSongUrl } = require('../music_platform/tunehub');
|
||||||
|
|
||||||
|
function parseTunehubParams(url) {
|
||||||
|
try {
|
||||||
|
const parsed = new URL(url);
|
||||||
|
if (!parsed.hostname.includes('music-dl.sayqz.com')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const source = parsed.searchParams.get('source');
|
||||||
|
const id = parsed.searchParams.get('id');
|
||||||
|
if (!source || !id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return { source, id };
|
||||||
|
} catch (err) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePageUrlParams(url) {
|
||||||
|
if (!url) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (url.indexOf('music.163.com') >= 0) {
|
||||||
|
const match = url.match(/id=(\d+)/);
|
||||||
|
if (match && match[1]) {
|
||||||
|
return { source: 'netease', id: match[1] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (url.indexOf('y.qq.com') >= 0) {
|
||||||
|
const match = url.match(/songDetail\/([A-Za-z0-9]+)/);
|
||||||
|
if (match && match[1]) {
|
||||||
|
return { source: 'qq', id: match[1] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSongInfoFromTunehub(source, info) {
|
||||||
|
return {
|
||||||
|
songName: info.name || '',
|
||||||
|
artist: info.artist || '',
|
||||||
|
album: info.album || '',
|
||||||
|
coverUrl: info.pic || '',
|
||||||
|
duration: 0,
|
||||||
|
fromMusicPlatform: true,
|
||||||
|
resourceForbidden: false,
|
||||||
|
source,
|
||||||
|
audios: info.url ? [{ url: info.url }] : [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = async function syncSingleSongWithUrl(uid, url, {
|
module.exports = async function syncSingleSongWithUrl(uid, url, {
|
||||||
songName = "",
|
songName = "",
|
||||||
@@ -19,11 +70,25 @@ module.exports = async function syncSingleSongWithUrl(uid, url, {
|
|||||||
songFromWyCloud = null
|
songFromWyCloud = null
|
||||||
} = {}, jobId = 0, jobType = JobType.SyncSongFromUrl, playlistName = "", collectRet) {
|
} = {}, jobId = 0, jobType = JobType.SyncSongFromUrl, playlistName = "", collectRet) {
|
||||||
// step 1. fetch song info
|
// step 1. fetch song info
|
||||||
const songInfo = await getMetaWithUrl(url);
|
let songInfo = null;
|
||||||
logger.info(songInfo);
|
let downloadUrl = url;
|
||||||
if (songInfo === false || songInfo.isTrial) {
|
let usedTunehub = false;
|
||||||
logger.error(`fetch song info failed or it's a trial song. ${JSON.stringify(songInfo)}`);
|
const tunehubParams = parseTunehubParams(url) || parsePageUrlParams(url);
|
||||||
return false;
|
if (tunehubParams) {
|
||||||
|
const tunehubInfo = await getSongInfo(tunehubParams.source, tunehubParams.id);
|
||||||
|
if (tunehubInfo) {
|
||||||
|
usedTunehub = true;
|
||||||
|
songInfo = buildSongInfoFromTunehub(tunehubParams.source, tunehubInfo);
|
||||||
|
downloadUrl = tunehubInfo.url || buildSongUrl(tunehubParams.source, tunehubParams.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!songInfo) {
|
||||||
|
songInfo = await getMetaWithUrl(url);
|
||||||
|
logger.info(songInfo);
|
||||||
|
if (songInfo === false || songInfo.isTrial) {
|
||||||
|
logger.error(`fetch song info failed or it's a trial song. ${JSON.stringify(songInfo)}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateJobIfNeed(uid, jobId, songInfo, jobType);
|
await updateJobIfNeed(uid, jobId, songInfo, jobType);
|
||||||
@@ -36,7 +101,7 @@ module.exports = async function syncSingleSongWithUrl(uid, url, {
|
|||||||
findSongName = songName;
|
findSongName = songName;
|
||||||
findArtist = artist;
|
findArtist = artist;
|
||||||
findAlbum = album;
|
findAlbum = album;
|
||||||
} else if (songInfo.fromMusicPlatform) {
|
} else if (songInfo && songInfo.fromMusicPlatform) {
|
||||||
findSongName = songInfo.songName;
|
findSongName = songInfo.songName;
|
||||||
findArtist = songInfo.artist;
|
findArtist = songInfo.artist;
|
||||||
findAlbum = songInfo.album;
|
findAlbum = songInfo.album;
|
||||||
@@ -54,7 +119,13 @@ module.exports = async function syncSingleSongWithUrl(uid, url, {
|
|||||||
|
|
||||||
// step 3. download
|
// step 3. download
|
||||||
// should add meta tag if not matched song on wycloud
|
// should add meta tag if not matched song on wycloud
|
||||||
const path = await fetchWithUrl(url, {songName: songInfo.songName, addMediaTag: songFromWyCloud ? false : true});
|
let path = false;
|
||||||
|
if (usedTunehub && downloadUrl) {
|
||||||
|
path = await downloadViaSourceUrl(downloadUrl);
|
||||||
|
}
|
||||||
|
if (path === false) {
|
||||||
|
path = await fetchWithUrl(url, {songName: songInfo.songName, addMediaTag: songFromWyCloud ? false : true});
|
||||||
|
}
|
||||||
if (path === false) {
|
if (path === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -80,4 +151,4 @@ async function updateJobIfNeed(uid, jobId, songInfo, jobType) {
|
|||||||
desc: `歌曲: ${songInfo.songName}`,
|
desc: `歌曲: ${songInfo.songName}`,
|
||||||
tip: "任务开始",
|
tip: "任务开始",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user