開源小工具 酷狗、網(wǎng)易音樂緩存文件轉(zhuǎn)mp3工具

發(fā)布一個開源小工具,支持將酷狗和網(wǎng)易云音樂的緩存文件轉(zhuǎn)碼為MP3文件。

以前寫過kgtemp文件轉(zhuǎn)mp3工具,正好當前又有網(wǎng)易云音樂緩存文件需求,因此就在原來小工具的基礎(chǔ)上做了一點修改,增加了對網(wǎng)易云音樂的支持,并簡單調(diào)整了下代碼結(jié)構(gòu),方便后續(xù)增加其他音樂軟件的支持。

工具使用介紹

下載程序(點擊下載),然后啟動程序,

enter description here
enter description here
  • 首先,設(shè)置輸入目錄,也就是解密后的文件存放在哪里
  • 然后將酷狗或者網(wǎng)易的緩存文件 or 整個文件夾,拖入到程序即可
轉(zhuǎn)碼中
轉(zhuǎn)碼中

打開轉(zhuǎn)碼結(jié)果目錄,可以看到轉(zhuǎn)碼后的結(jié)果

轉(zhuǎn)碼結(jié)果
轉(zhuǎn)碼結(jié)果

緩存目錄如何找

網(wǎng)易云音樂的緩存目錄

打開設(shè)置 -- 下載設(shè)置 - 緩存目錄就是了

enter description here
enter description here

酷狗緩存目錄

如圖,在設(shè)置--下載設(shè)置里

enter description here
enter description here

工具代碼簡要說明

類圖

enter description here
enter description here

ICacheDecrypt

我們定義一個解碼接口ICacheDecrypt,實現(xiàn)將緩存文件字節(jié)流轉(zhuǎn)換為mp3字節(jié)流。

 /// <summary>
    /// 解密接口
    /// </summary>
    public interface ICacheDecrypt
    {

        string AcceptableExtension
        {
            get;
        }

        bool isAcceptable(string cacheFile);

        /// <summary>
        /// 解密文件
        /// </summary>
        /// <param name="cacheFile">緩存文件</param>
        /// <returns>解密后二進制數(shù)據(jù)</returns>
        byte[] Decrypt(string cacheFile);

        /// <summary>
        /// 解密文件
        /// </summary>
        /// <param name="cacheFileData">緩存文件數(shù)據(jù)</param>
        /// <returns></returns>
        byte[] Decrypt(byte[] cacheFileData);

        /// <summary>
        /// 解密文件
        /// </summary>
        /// <param name="cacheFile">cache文件</param>
        /// <param name="decodedFile">解密后文件</param>
        void Decrypt(string cacheFile,string decodedFile);

    }

BaseCacheDecrypt

然后,實現(xiàn)一個默認的抽象類BaseCacheDecrypt,實現(xiàn)一些公共的東西,具體的轉(zhuǎn)碼工作讓子類去實現(xiàn),比如網(wǎng)易和酷狗可以分別建一個子類。

public abstract class BaseCacheDecrypt : ICacheDecrypt
    {

        protected string currentCacheFile;

        public abstract string AcceptableExtension
        {
            get;
        }

        public abstract byte[] Decrypt(byte[] cacheFileData);

        public byte[] Decrypt(string cacheFile)
        {
            currentCacheFile = cacheFile;
            return Decrypt(File.ReadAllBytes(cacheFile));
        }

        public void Decrypt(string cacheFile, string decodedFile)
        {
            File.WriteAllBytes(decodedFile, Decrypt(cacheFile));
        }

        public  bool isAcceptable(string cacheFile)
        {
            return cacheFile.EndsWith(AcceptableExtension);
        }
    }

NetMusicCacheDecrypt

然后,分別實現(xiàn)酷狗和網(wǎng)易云音樂的解碼工作,酷狗的上次已經(jīng)寫了如何解碼,這里只貼網(wǎng)易的,解碼很簡單,異或0xa3就可以了。網(wǎng)易音樂在測試時發(fā)現(xiàn)好多mp3沒有ID3信息,經(jīng)過觀察發(fā)現(xiàn)緩存文件名里包含歌曲的id信息,因此可以根據(jù)這個id信息去抓取歌曲網(wǎng)頁,解析出歌手和歌曲名稱,然后寫入到ID3里,這里ID3的讀寫采用了GitHub上的一個開源庫


    /// <summary>
    /// 網(wǎng)易云緩存解密
    /// </summary>
    public class NetMusicCacheDecrypt : BaseCacheDecrypt
    {
        public override string AcceptableExtension
        {
            get
            {
                return ".uc";
            }
        }

        string cut(string str,string start,string end)
        {
            var startIndex = str.IndexOf(start);
            if (startIndex == -1)
            {
                return "";
            }
            startIndex += start.Length;
            var endIndex = str.IndexOf(end, startIndex);
            if (endIndex == -1)
            {
                return "";
            }
            return str.Substring(startIndex, endIndex - startIndex);
        }

        public override byte[] Decrypt(byte[] cacheFileData)
        {         
            for (var i = 0; i < cacheFileData.Length; i++)
            {
                // 異或0xa3
                cacheFileData[i] ^= 0xa3;
            }

            var fileName = new FileInfo(currentCacheFile).Name;
            var songId = fileName.Substring(0, fileName.IndexOf("-"));
            var html = HttpHelper.SendGet("http://music.163.com/song?id=" + songId);
            if (html.Length > 0)
            {
                var title = cut(html, "<title>", "</title>").Trim();
                var tempFile = currentCacheFile+ Guid.NewGuid().ToString();
                File.WriteAllBytes(tempFile, cacheFileData);
                Track theTrack = new Track(tempFile);
                // 父親寫的散文詩(時光版) - 許飛 - 單曲 - 網(wǎng)易云音樂
                theTrack.Artist = cut(title, "-", "-").Trim();
                theTrack.Title = title.Substring(0, title.IndexOf("-")).Trim();
                // Save modifications on the disc
                theTrack.Save();
                cacheFileData = File.ReadAllBytes(tempFile);
                File.Delete(tempFile);

            }
            
            return cacheFileData;
        }

    }

接著介紹核心的Decryptor,實現(xiàn)轉(zhuǎn)碼的調(diào)度,這里的思路就是將所有的解碼器放到一個list里,當一個文件過來的時候,遍歷所有解碼器,如果accetbale,就處理,否則跳過。
兩個主要工作:

  • 加載所有的BaseCacheDecrypt
  • 進行解碼工作

加載所有的BaseCacheDecrypt

兩種方法,一是自己實例化,一是使用反射,這里當然用反射了:)


private Decryptor()
        {
           
        }

        public static Decryptor Instance
        {
            get
            {
                return Holder.decryptor;
            }
        }
 static class Holder
        {
            public static Decryptor decryptor = Load();


            /// <summary>
            /// 從當前Assembly加載
            /// </summary>
            /// <returns></returns>
            private static Decryptor Load()
            {
                Assembly assembly = Assembly.GetExecutingAssembly();
                List<Type> hostTypes = new List<Type>();

                foreach (var type in assembly.GetExportedTypes())
                {
                    //確定type為類并且繼承自(實現(xiàn))IMyInstance
                    if (type.IsClass && typeof(BaseCacheDecrypt).IsAssignableFrom(type) && !type.IsAbstract)
                        hostTypes.Add(type);
                }

                Decryptor decryptor = new Decryptor();
                foreach (var type in hostTypes)
                {
                    ICacheDecrypt instance = (ICacheDecrypt)Activator.CreateInstance(type);
                    decryptor.cacheDecryptors.Add(instance);
                }

                return decryptor;
            }
        }

Decryptor通過單例模式對外提供調(diào)用。

進行解碼

判斷拖入的是文件夾還是文件,文件夾的話遍歷子文件,依次處理。解碼方式就是鋼說的,遍歷decryptors,如果支持就解碼。
解碼完后,讀取ID3信息,對文件進行重命名。

 public int Process(string path)
        {
            int success = 0;

            if (Directory.Exists(path))//如果是文件夾
            {
                DirectoryInfo dinfo = new DirectoryInfo(path);//實例化一個DirectoryInfo對象
                foreach (FileInfo fs in dinfo.GetFiles()) //查找.kgtemp文件
                {
                    ProcessFile(fs.FullName);
                    success++;
                }
            }
            else
            {
                ProcessFile(path);
                success = 1;
            }

            return success;
        }

        private string GetCleanFileName(string fileName)
        {
            StringBuilder rBuilder = new StringBuilder(fileName);
            foreach (char rInvalidChar in Path.GetInvalidFileNameChars())
                rBuilder.Replace(rInvalidChar.ToString(), string.Empty);
            return rBuilder.ToString();
        }

        private string GetTargetFileName(string fileName)
        {
            var fileinfo = new FileInfo(fileName);
            var rawName = fileinfo.Name.Substring(0, fileinfo.Name.IndexOf("."));
            return TargetDirectory + Path.DirectorySeparatorChar + rawName + ".mp3";
        }


        void ProcessFile(string fileName)
        {
            _logger.Info("開始處理" + fileName);
            try
            {
                foreach (var decryptor in cacheDecryptors)
                {
                    if (decryptor.isAcceptable(fileName))
                    {
                        var targetName = TargetDirectory + Path.DirectorySeparatorChar + new FileInfo(fileName).Name + ".mp3";

                        decryptor.Decrypt(fileName, targetName);

                        // 重命名
                        if (AutoRename)
                        {
                            var mp3 = ID3Helper.ReadMp3(targetName);

                            if (mp3.Title.Length > 0)
                            {
                                string realFileName = GetTargetFileName(GetCleanFileName(mp3.Title + "-" + mp3.Artist + ".mp3"));

                                _logger.Info("重命名" + realFileName);
                                if (File.Exists(realFileName))
                                {
                                    File.Delete(realFileName);
                                }

                                File.Move(targetName, realFileName);
                            }
                        }
                    }
                }
                _logger.Info(fileName + "處理完成");
            }
            catch(Exception ex)
            {
                _logger.Error(fileName + "出現(xiàn)異常" + ex.Message);
            }
          
        }

開源地址

代碼托管到了GitHub,musicDecryptor, 感興趣的可以訪問進行

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容