zoukankan      html  css  js  c++  java
  • 开源工具软件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
    您的支持是对博主最大的鼓励,感谢您的认真阅读。
    本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    出处:https://www.cnblogs.com/xiaoqi/p/xmusicdownloader.html

    =======================================================================================

    网易云音乐无版权音乐补全工具

    缘起#

    网易云音乐的不少歌曲因为版权下架了,或者变成收费的,导致无法收听,因此需要一个小工具,希望可以从其他来源补全歌曲。

    如图所示,不能听的显示为灰色。

    之前写的小工具XMusicDownloader(https://github.com/jadepeng/XMusicDownloader) 可以从多个来源搜索歌曲并下载,因此以这个为基础,可以很快实现需求。

    查看本文之前,建议查看开源工具软件XMusicDownloader——音乐下载神器.

    歌曲版权声明#

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

    工具原理#

    1. 获取用户歌单,找出无版权和收费歌曲
    2. 从QQ、咪咕、百度等源搜索这些歌曲,匹配成功的可以下载
    3. 下载后可以手动上传到云盘

    获取用户歌单#

    借助NeteaseCloudMusicApi,可以方便调用云音乐的api。

    分析获取到的json,可以发现,包含noCopyrightRcmd的是没有版权的,包含fee的是收费的,我们可以将这些歌曲提取出来,变为song对象。

    Copy
    private static List<Song> FetchNoCopyrightSongs(JObject json)
            {
                List<Song> noCopyrightsSongs = new List<Song>();
                foreach (JObject songObj in json["songs"])
                {
                    int id = 0;
                   
                    if (songObj["noCopyrightRcmd"].HasValues || songObj["fee"].Value<int>() == 1)
                    {
                        noCopyrightsSongs.Add(NeteaseProvider.extractSong(ref id, songObj));
                    }
                }
    
                return noCopyrightsSongs;
            }
    		
    		public static Song extractSong(ref int index, JToken songItem)
            {
                var song = new Song
                {
                    id = (string)songItem["id"],
                    name = (string)songItem["name"],
    
                    album = (string)songItem["al"]["name"],
                    //rate = 128,
                    index = index++,
                    //size = (double)songItem["FileSize"],
                    source = "网易",
                    duration = (double)songItem["dt"] / 1000
                };
    
                song.singer = "";
                foreach (var ar in songItem["ar"])
                {
                    song.singer += ar["name"] + " ";
                }
    
                if (songItem.Contains("privilege") && songItem["privilege"].HasValues)
                {
                    song.rate = ((int)songItem["privilege"]["fl"]) / 1000;
                    var fl = (int)songItem["privilege"]["fl"];
                    if (songItem["h"] != null && fl >= 320000)
                    {
                        song.size = (double)songItem["h"]["size"];
                    }
                    else if (songItem["m"] != null && fl >= 192000)
                    {
                        song.size = (double)songItem["m"]["size"];
                    }
                    else if (songItem["l"] != null)
                    {
                        song.size = (double)songItem["l"]["size"];
                    }
                }
                else
                {
                    song.rate = 128;
                    song.size = 0;
                }
    
                return song;
            }
    

    从其他来源获取歌曲#

    在之前的博文开源工具软件XMusicDownloader——音乐下载神器里,我们有一个聚合的搜索歌曲的方法:

    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();
            }
    

    类似的,匹配也是先搜索,但是要排除网易源,然后根据搜索结果去匹配。搜索的时候,可以将 “歌曲名称 + 歌手名称” 组合用来搜索。

    Copy
      public MergedSong SearchSong(string singer, string songName, string exceptProvider)
            {
    		  // search
                var songs = new List<Song>();
                Providers.AsParallel().ForAll(provider =>
                {
                    try
                    {
                        if (provider.Name != exceptProvider)
                        {
                            var currentSongs = provider.SearchSongs(singer + " " + songName, 1, 10);
                            songs.AddRange(currentSongs);
                        }
                    }
                    catch (Exception e)
                    {
    
                    }
                });
    
                // merge
    
                List<MergedSong> mergedSongs = songs.GroupBy(s => s.getMergedKey()).Select(g => new MergedSong(g.ToList())).OrderByDescending(s => s.score).ToList();
    		
    		 // match
                foreach (MergedSong song in mergedSongs)
                {
                    if (song.singer == singer && song.name == songName)
                    {
                        return song;
                    }
                }
    
                return null;
            }
    

    软件界面#

    软件界面,增加用户、密码输入

    界面

    搜索结果,设置为默认选中:

    Copy
     List<ListViewItem> listViewItems = new List<ListViewItem>();
                mergedSongs.ForEach(item =>
                {
                    ListViewItem lvi = new ListViewItem();
                    lvi.Text = item.name;
                    lvi.SubItems.Add(item.singer);
                    lvi.SubItems.Add(item.rate + "kb");
                    lvi.SubItems.Add((item.size / (1024 * 1024)).ToString("F2") + "MB");  //将文件大小装换成MB的单位
                    TimeSpan ts = new TimeSpan(0, 0, (int)item.duration); //把秒数换算成分钟数
                    lvi.SubItems.Add(ts.Minutes + ":" + ts.Seconds.ToString("00"));
                    lvi.SubItems.Add(item.source);
                    lvi.Tag = item;
                    lvi.Checked = true; // 默认选中
                    listViewItems.Add(lvi);
                });
    

    搜索出来后,下载可以完全复用之前逻辑。

    下载歌曲使用#

    下载后的歌曲,可以通过网易云音乐客户端,上传到云盘,然后批量选中,添加到我喜欢的音乐

    上传

    批量选中后收藏到歌单:

    批量收藏

    工具下载地址#


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

    出处:https://www.cnblogs.com/xiaoqi/p/music163tool.html

  • 相关阅读:
    c#冒泡排序
    C# 虚方法(virtual)覆盖(override) 隐藏(new) 重载
    Javascript 大括号
    C# const.static.readonly.
    热点链接(img map area)
    WeiBo返回错误码的二种方式
    Cookie跨域操作
    synchronized(this)与synchronized(class)
    线程安全场景备忘
    git新建一个分支setupstream
  • 原文地址:https://www.cnblogs.com/mq0036/p/14223346.html
Copyright © 2011-2022 走看看