開源工具軟件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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM