開源小工具 酷狗、網易音樂緩存文件轉mp3工具


發布一個開源小工具,支持將酷狗和網易雲音樂的緩存文件轉碼為MP3文件。

以前寫過kgtemp文件轉mp3工具,正好當前又有網易雲音樂緩存文件需求,因此就在原來小工具的基礎上做了一點修改,增加了對網易雲音樂的支持,並簡單調整了下代碼結構,方便后續增加其他音樂軟件的支持。

工具使用介紹

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

enter description here

  • 首先,設置輸入目錄,也就是解密后的文件存放在哪里
  • 然后將酷狗或者網易的緩存文件 or 整個文件夾,拖入到程序即可

轉碼中

打開轉碼結果目錄,可以看到轉碼后的結果

轉碼結果

緩存目錄如何找

網易雲音樂的緩存目錄

打開設置 -- 下載設置 - 緩存目錄就是了

enter description here

酷狗緩存目錄

如圖,在設置--下載設置里

enter description here

工具代碼簡要說明

類圖

enter description here

ICacheDecrypt

我們定義一個解碼接口ICacheDecrypt,實現將緩存文件字節流轉換為mp3字節流。

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

        string AcceptableExtension
        {
            get;
        }

        bool isAcceptable(string cacheFile);

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

        /// <summary>
        /// 解密文件
        /// </summary>
        /// <param name="cacheFileData">緩存文件數據</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

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

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

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


    /// <summary>
    /// 網易雲緩存解密
    /// </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);
                // 父親寫的散文詩(時光版) - 許飛 - 單曲 - 網易雲音樂
                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,實現轉碼的調度,這里的思路就是將所有的解碼器放到一個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為類並且繼承自(實現)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通過單例模式對外提供調用。

進行解碼

判斷拖入的是文件夾還是文件,文件夾的話遍歷子文件,依次處理。解碼方式就是鋼說的,遍歷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 + "出現異常" + ex.Message);
            }
          
        }

開源地址

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


作者:Jadepeng
出處:jqpeng的技術記事本--http://www.cnblogs.com/xiaoqi
您的支持是對博主最大的鼓勵,感謝您的認真閱讀。
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。


免責聲明!

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



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