diff --git a/backend/src/service/search_songs/find_the_best_match_from_wycloud.js b/backend/src/service/search_songs/find_the_best_match_from_wycloud.js index 638aa97..a12d0ab 100644 --- a/backend/src/service/search_songs/find_the_best_match_from_wycloud.js +++ b/backend/src/service/search_songs/find_the_best_match_from_wycloud.js @@ -1,14 +1,27 @@ -const { searchSong, getSongInfo } = require('../music_platform/wycloud'); +const { getSongInfo, searchSongs } = require('../music_platform/tunehub'); 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} = {}) { if (musicPlatformSongId) { - const songInfo = await getSongInfo(uid, musicPlatformSongId); - + const songInfo = await getSongInfo('netease', musicPlatformSongId); if (songInfo) { - return songInfo; + return { + songId: musicPlatformSongId, + songName: songInfo.name || songName, + artists: splitArtists(songInfo.artist), + album: songInfo.album || album, + }; } - if (songName && artist) { return { songId: musicPlatformSongId, @@ -24,12 +37,24 @@ module.exports = async function findTheBestMatchFromWyCloud(uid, {songName, arti if (songName === "" || artist === "") { return null; } - const searchLists = await searchSong(uid, songName, artist); - logger.info('searchLists', searchLists); - if (searchLists === false) { + const searchData = await searchSongs(`${songName} ${artist}`, { + source: 'netease', + limit: 20, + aggregate: false, + }); + if (searchData === false || !searchData.results) { logger.warn(`search song failed, no matter, go on`); 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; for (const searchItem of searchLists) { diff --git a/backend/src/service/sync_music/sync_single_song_with_url.js b/backend/src/service/sync_music/sync_single_song_with_url.js index 60d7d2f..9b3238b 100644 --- a/backend/src/service/sync_music/sync_single_song_with_url.js +++ b/backend/src/service/sync_music/sync_single_song_with_url.js @@ -1,4 +1,4 @@ -const { fetchWithUrl, getMetaWithUrl } = require('../media_fetcher'); +const { fetchWithUrl, getMetaWithUrl, downloadViaSourceUrl } = require('../media_fetcher'); const logger = require('consola'); const sleep = require('../../utils/sleep'); 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 { downloadFromLocalTmpPath } = require('./download_to_local'); 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, { songName = "", @@ -19,11 +70,25 @@ module.exports = async function syncSingleSongWithUrl(uid, url, { songFromWyCloud = null } = {}, jobId = 0, jobType = JobType.SyncSongFromUrl, playlistName = "", collectRet) { // step 1. fetch song info - const 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; + let songInfo = null; + let downloadUrl = url; + let usedTunehub = false; + const tunehubParams = parseTunehubParams(url) || parsePageUrlParams(url); + 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); @@ -36,7 +101,7 @@ module.exports = async function syncSingleSongWithUrl(uid, url, { findSongName = songName; findArtist = artist; findAlbum = album; - } else if (songInfo.fromMusicPlatform) { + } else if (songInfo && songInfo.fromMusicPlatform) { findSongName = songInfo.songName; findArtist = songInfo.artist; findAlbum = songInfo.album; @@ -54,7 +119,13 @@ module.exports = async function syncSingleSongWithUrl(uid, url, { // step 3. download // 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) { return false; } @@ -80,4 +151,4 @@ async function updateJobIfNeed(uid, jobId, songInfo, jobType) { desc: `歌曲: ${songInfo.songName}`, tip: "任务开始", }); -} \ No newline at end of file +}