From 41cba028333d0cf65f7dc3ee466660cc20e558fc Mon Sep 17 00:00:00 2001 From: real-zony Date: Wed, 24 May 2023 21:54:13 +0800 Subject: [PATCH] feat: NetEase Cloud Music playlist download supports passing in multiple IDs. Issue: https://github.com/real-zony/ZonyLrcToolsX/issues/139 --- README.md | 6 ++ .../NetEaseMusicSongListMusicScanner.cs | 76 +++++++++++-------- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index de9f1ad..dafe687 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,12 @@ macOS 和 Linux 用户请打开终端,切换到软件目录,一样执行命 例如获取地址 [https://music.163.com/#/playlist?id=158010361](https://music.163.com/#/playlist?id=158010361) 的歌单信息,那么歌单 ID 就应该传递 158010361。 +如果你需要获取多个歌单的歌曲数据,请在传递歌单 ID 时使用分号 `;` 指定多个 ID,例如下面的命令就是下载两个歌单的歌曲数据。 + +```shell +.\ZonyLrcTools.Cli.exe download -sc netease -o "D:\TempFiles" -s "7224428149;158010361" -l +``` + 由于网易云音乐的限制,要想获取完整的歌单信息,必须扫码登录程序,还是以最上面的为例,我需要下载歌单内的歌词数据,就必须扫码之后程序才会执行。 ![image-20230328223155280](./README.assets/image-20230328223155280.png) diff --git a/src/ZonyLrcTools.Common/MusicScanner/NetEaseMusicSongListMusicScanner.cs b/src/ZonyLrcTools.Common/MusicScanner/NetEaseMusicSongListMusicScanner.cs index 802c584..0fd0c61 100644 --- a/src/ZonyLrcTools.Common/MusicScanner/NetEaseMusicSongListMusicScanner.cs +++ b/src/ZonyLrcTools.Common/MusicScanner/NetEaseMusicSongListMusicScanner.cs @@ -37,11 +37,11 @@ public class NetEaseMusicSongListMusicScanner : ISingletonDependency /// /// 从网易云歌单获取需要下载的歌曲列表,调用这个 API 需要用户登录,否则获取的歌单数据不全。 /// - /// 网易云音乐歌单的 ID。 + /// 网易云音乐歌单的 ID。 /// 歌词文件的输出路径。 /// 输出的歌词文件格式,默认是 "{Artist} - {Title}.lrc" 的形式。 /// 返回获取到的歌曲列表。 - public async Task> GetMusicInfoFromNetEaseMusicSongListAsync(string songListId, string outputDirectory, string pattern) + public async Task> GetMusicInfoFromNetEaseMusicSongListAsync(string songListIds, string outputDirectory, string pattern) { if (string.IsNullOrEmpty(Cookie)) { @@ -50,40 +50,52 @@ public class NetEaseMusicSongListMusicScanner : ISingletonDependency CsrfToken = loginResponse.csrfToken ?? string.Empty; } - var secretKey = NetEaseMusicEncryptionHelper.CreateSecretKey(16); - var encSecKey = NetEaseMusicEncryptionHelper.RsaEncode(secretKey); - var response = await _warpHttpClient.PostAsync( - $"{Host}/weapi/v6/playlist/detail?csrf_token=e5044820d8b66e14b8c31d39f9651a98", requestOption: - request => - { - request.Headers.Add("Cookie", Cookie); - request.Content = new FormUrlEncodedContent(HandleRequest(new - { - csrf_token = CsrfToken, - id = songListId, - n = 1000, - offset = 0, - total = true, - limit = 1000, - }, secretKey, encSecKey)); - request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); - }); - - if (response.Code != 200 || response.PlayList?.SongList == null) + async Task> GetMusicInfoBySongIdAsync(string songId) { - throw new ErrorCodeException(ErrorCodes.NotSupportedFileEncoding); + var secretKey = NetEaseMusicEncryptionHelper.CreateSecretKey(16); + var encSecKey = NetEaseMusicEncryptionHelper.RsaEncode(secretKey); + var response = await _warpHttpClient.PostAsync( + $"{Host}/weapi/v6/playlist/detail?csrf_token={CsrfToken}", requestOption: + request => + { + request.Headers.Add("Cookie", Cookie); + request.Content = new FormUrlEncodedContent(HandleRequest(new + { + csrf_token = CsrfToken, + id = songId, + n = 1000, + offset = 0, + total = true, + limit = 1000, + }, secretKey, encSecKey)); + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); + }); + + if (response.Code != 200 || response.PlayList?.SongList == null) + { + throw new ErrorCodeException(ErrorCodes.NotSupportedFileEncoding); + } + + return response.PlayList.SongList + .Where(song => !string.IsNullOrEmpty(song.Name)) + .Select(song => + { + var artistName = song.Artists?.FirstOrDefault()?.Name ?? string.Empty; + var fakeFilePath = Path.Combine(outputDirectory, pattern.Replace("{Name}", song.Name).Replace("{Artist}", artistName)); + + return new MusicInfo(fakeFilePath, song.Name!, artistName); + }).ToList(); } - return response.PlayList.SongList - .Where(song => !string.IsNullOrEmpty(song.Name)) - .Select(song => - { - var artistName = song.Artists?.FirstOrDefault()?.Name ?? string.Empty; - var fakeFilePath = Path.Combine(outputDirectory, pattern.Replace("{Name}", song.Name).Replace("{Artist}", artistName)); + var musicInfoList = new List(); + foreach (var songListId in songListIds.Split(';')) + { + _logger.LogInformation("正在获取歌单 {SongListId} 的歌曲列表。", songListId); + var musicInfos = await GetMusicInfoBySongIdAsync(songListId); + musicInfoList.AddRange(musicInfos); + } - var info = new MusicInfo(fakeFilePath, song.Name!, artistName); - return info; - }).ToList(); + return musicInfoList; } ///