开源工具软件XMusicDownloader——音乐下载神器

XMusicDownloader,一款 支持从百度、网易、qq和酷狗、咪咕音乐等音乐网站搜索并下载歌曲的程序。

补充说明(2020.8)#

歌曲版权声明#

  • 该工具本质是歌曲聚合搜索,API来自公开网络,非破解版,只能下载各家公开的音乐,付费歌曲不能下载,例如QQ、网易等的收费歌曲不能从QQ\网易源下载,工具的原理是基于聚合实现补全
  • 工具和代码仅供技术研究使用,禁止将本工具用于商业用途,如产生法律纠纷与本人无关,如有侵权,请联系删除。

以下是原文:

缘起:

一直用网易音乐听歌,但是诸如李健、周杰伦的不少歌曲,网易都没有版权,要从QQ等音乐去下载,因此一直想写一个小程序,可以从其他音乐网站下载相关歌曲,趁放假,花了几小时做了这样一个程序。

BTW: 之前写过一个从酷狗和网易音乐提取缓存文件的程序,感兴趣的可以查看。

功能#

  • 聚合搜索多家音乐网站
  • 支持音乐批量下载
  • 搜索结果综合排序
  • 可以编写Provider程序,支持其他音乐网站

实现IMusicProvider即可,主要是搜索和获取下载链接的方法。

Copy
public interface IMusicProvider { string Name { get; } string getDownloadUrl(Song song); List<Song> SearchSongs(string keyword, int page, int pageSize); }

界面截图#

预览

下载程序#

https://github.com/jadepeng/XMusicDownloader/releases

实现方案介绍#

定义song实体#

Copy
public class Song { public string id { get; set; } public string name { get; set; } public string singer { get; set; } public string album { get; set; } public string source { get; set; } public double duration { get; set; } public double size { get; set; } public string url { get; set; } public int rate { get; set; } public int index { get; set; } public string getFileName() { return singer + "-" + name + ".mp3"; } public string getMergedKey() { return singer.Replace(" ", "") + name.Replace(" ", ""); } }

封装各个音乐网站#

抽象为MusicProvider,音乐提供方:),定义Name为名称,SearchSongs搜索歌曲,getDownloadUrl获取音乐下载地址。

Copy
public interface IMusicProvider { string Name { get; } string getDownloadUrl(Song song); List<Song> SearchSongs(string keyword, int page, int pageSize); }

然后就是依次实现百度、网易等音乐网站,以QQ为例。

Copy
public class QQProvider : IMusicProvider { static HttpConfig DEFAULT_CONFIG = new HttpConfig { Referer = "http://m.y.qq.com", }; public string Name { get; } = "QQ"; static string[] prefixes = new string[] { "M800", "M500", "C400" }; public List<Song> SearchSongs(string keyword,int page,int pageSize) { var searchResult = HttpHelper.GET(string.Format("http://c.y.qq.com/soso/fcgi-bin/search_for_qq_cp?w={0}&format=json&p={1}&n={2}", keyword, page,pageSize), DEFAULT_CONFIG); var searchResultJson = JsonParser.Deserialize(searchResult).data.song; var result = new List<Song>(); var index = 1; foreach(var songItem in searchResultJson.list) { var song = new Song { id = songItem["songmid"], name = songItem["songname"], album = songItem["albumname"], rate = 128, size = songItem["size128"], source = Name, index = index++, duration = songItem["interval"] }; song.singer = ""; foreach (var ar in songItem["singer"]) { song.singer += ar["name"] + " "; } result.Add(song); } return result; } public string getDownloadUrl(Song song) { var guid = new Random().Next(1000000000, 2000000000); var key = JsonParser.Deserialize(HttpHelper.GET(string.Format("http://base.music.qq.com/fcgi-bin/fcg_musicexpress.fcg?guid={0}&format=json&json=3",guid), DEFAULT_CONFIG)).key; foreach(var prefix in prefixes) { var musicUrl = string.Format("http://dl.stream.qqmusic.qq.com/{0}{1}.mp3?vkey={2}&guid={3}&fromtag=1", prefix, song.id, key, guid); if (HttpHelper.GetUrlContentLength(musicUrl) > 0) { return musicUrl; } } return null; } }
  • 搜索调用http://c.y.qq.com/soso/fcgi-bin/search_for_qq_cp?w={0}&format=json&p={1}&n={2}接口,获取下载地址调用http://base.music.qq.com/fcgi-bin/fcg_musicexpress.fcg?guid={0}&format=json&json=3,然后再组合。

聚合搜索#

设计一个MusicProviders,加载所有的IMusicProvider,提供一个SearchSongs方法,并发调用各个网站的搜索,然后merge到一起。

Copy
public List<MergedSong> SearchSongs(string keyword, int page, int pageSize) { var songs = new List<Song>(); Providers.AsParallel().ForAll(provider => { var currentSongs = provider.SearchSongs(keyword, page, pageSize); songs.AddRange(currentSongs); }); // merge return songs.GroupBy(s => s.getMergedKey()).Select(g => new MergedSong(g.ToList())).OrderByDescending(s => s.score).ToList(); }

关于merge,核心就是将相同的歌曲合并到一起,我们暂且认为歌手+歌曲名相同的为同一首歌曲:

Copy
public string getMergedKey() { return singer.Replace(" ", "") + name.Replace(" ", ""); }

因此按megekey分组,就能实现聚合。我们设计一个MergedSong来包裹。

Copy
public class MergedSong { public List<Song> items { get; set; } public MergedSong(List<Song> items) { this.items = items; } public string name { get { return this.items[0].name; } } public string singer { get { return this.items[0].singer; } } public string album { get { return this.items[0].album; } } public string source { get { return string.Join(",", this.items.Select(i => i.source).ToArray()); } } public double duration { get { return this.items[0].duration; } } public double size { get { return this.items[0].size; } } public double rate { get { return this.items[0].rate; } } public double score { get { // 投票+排序加权 (各50%) return this.items.Count / (MusicProviders.Instance.Providers.Count - 1) + (20 - this.items.Average(i => i.index)) / 20; } } }

MergedSong的核心是定义了一个score,我们通过投票+搜索结果排序,用来决定合并结果的排序。

下载#

下载主要是通过provider获取真实url,然后下载即可。

Copy
public class SongItemDownloader { MusicProviders musicProviders; string target; MergedSong song; public event DownloadFinishEvent DownloadFinish; public SongItemDownloader(MusicProviders musicProviders, string target, MergedSong song) { this.musicProviders = musicProviders; this.target = target; this.song = song; } public long totalBytes; public long bytesReceived; public double ReceiveProgress; public double receiveSpeed; DateTime lastTime = DateTime.Now; public void Download() { WebClient client = new WebClient(); client.DownloadProgressChanged += Client_DownloadProgressChanged; new Thread(() => { // 多来源,防止单个来源出错 foreach (var item in song.items) { try { client.DownloadFile(musicProviders.getDownloadUrl(item), target + "\\" + item.getFileName()); DownloadFinish?.Invoke(this, this); break; } catch { } } }).Start(); } private void Client_DownloadProgressChanged(object sender, DownloadEventArgs e) { this.bytesReceived = e.bytesReceived; this.totalBytes = e.totalBytes; this.receiveSpeed = e.receiveSpeed; this.ReceiveProgress = e.ReceiveProgress; } }

参考#


作者:Jadepeng
出处:jqpeng的技术记事本--http://www.cnblogs.com/xiaoqi
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

关注作者

欢迎关注作者微信公众号, 一起交流软件开发:欢迎关注作者微信公众号

posted @   JadePeng  阅读(66889)  评论(8编辑  收藏  举报
编辑推荐:
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示
CONTENTS