refactor(音乐服务): 重构网易云音乐匹配和歌曲同步逻辑

- 将网易云音乐API调用迁移至tunehub服务
- 新增歌曲信息解析和URL处理工具函数
- 改进歌曲匹配逻辑,支持更多来源的URL解析
- 优化下载流程,增加tunehub下载支持
This commit is contained in:
史悦
2026-01-07 17:20:28 +08:00
parent 6fa4f1a72e
commit 6baa2c4868
2 changed files with 113 additions and 17 deletions

View File

@@ -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) {

View File

@@ -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: "任务开始",
}); });
} }