{"id":14986283,"url":"https://github.com/jadepeng/musicdecryptor","last_synced_at":"2025-08-30T10:39:43.638Z","repository":{"id":95313887,"uuid":"127254200","full_name":"jadepeng/musicDecryptor","owner":"jadepeng","description":"酷狗、网易等音乐缓存文件转mp3","archived":false,"fork":false,"pushed_at":"2024-07-26T02:46:25.000Z","size":2773,"stargazers_count":122,"open_issues_count":4,"forks_count":41,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-02T09:11:15.118Z","etag":null,"topics":["csharp","winforms"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jadepeng.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-29T07:30:54.000Z","updated_at":"2025-03-24T16:45:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"77794fb6-8e8e-43f1-9679-8d7542414c10","html_url":"https://github.com/jadepeng/musicDecryptor","commit_stats":{"total_commits":5,"total_committers":1,"mean_commits":5.0,"dds":0.0,"last_synced_commit":"845273c5bf552727538eee4dae2d8f868ece98a1"},"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/jadepeng/musicDecryptor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jadepeng%2FmusicDecryptor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jadepeng%2FmusicDecryptor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jadepeng%2FmusicDecryptor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jadepeng%2FmusicDecryptor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jadepeng","download_url":"https://codeload.github.com/jadepeng/musicDecryptor/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jadepeng%2FmusicDecryptor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272839658,"owners_count":25001861,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-30T02:00:09.474Z","response_time":77,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","winforms"],"created_at":"2024-09-24T14:12:38.585Z","updated_at":"2025-08-30T10:39:43.602Z","avatar_url":"https://github.com/jadepeng.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# musicDecryptor\n酷狗、网易等音乐缓存文件转mp3\n\n以前写过[kgtemp文件转mp3工具](http://www.cnblogs.com/xiaoqi/p/8085563.html),正好当前又有网易云音乐缓存文件需求，因此就在原来小工具的基础上做了一点修改，增加了对网易云音乐的支持，并简单调整了下代码结构，方便后续增加其他音乐软件的支持。\nPS:写惯了java，再写c#好别扭。。\n\n\n## 工具使用介绍\n\n启动程序，\n\n![enter description here][1]\n\n- 首先，设置输入目录，也就是解密后的文件存放在哪里\n- 然后将酷狗或者网易的缓存文件 or 整个文件夹，拖入到程序即可\n\n![enter description here][2]\n打开转码结果目录，可以看到转码后的结果\n\n![enter description here][3]\n\n## FAQ\n\n### 网易云音乐的缓存目录\n\n打开设置 -- 下载设置 - 缓存目录就是了\n\n![enter description here][4]\n\n\n### 酷狗缓存目录\n\n如图，在设置--下载设置里\n\n![enter description here][5]\n\n\n## 工具代码简要说明\n\n### 类图\n\n![enter description here][6]\n\n\n### ICacheDecrypt\n我们定义一个解码接口ICacheDecrypt，实现将缓存文件字节流转换为mp3字节流。\n\n``` cs\n /// \u003csummary\u003e\n    /// 解密接口\n    /// \u003c/summary\u003e\n    public interface ICacheDecrypt\n    {\n\n        string AcceptableExtension\n        {\n            get;\n        }\n\n        bool isAcceptable(string cacheFile);\n\n        /// \u003csummary\u003e\n        /// 解密文件\n        /// \u003c/summary\u003e\n        /// \u003cparam name=\"cacheFile\"\u003e缓存文件\u003c/param\u003e\n        /// \u003creturns\u003e解密后二进制数据\u003c/returns\u003e\n        byte[] Decrypt(string cacheFile);\n\n        /// \u003csummary\u003e\n        /// 解密文件\n        /// \u003c/summary\u003e\n        /// \u003cparam name=\"cacheFileData\"\u003e缓存文件数据\u003c/param\u003e\n        /// \u003creturns\u003e\u003c/returns\u003e\n        byte[] Decrypt(byte[] cacheFileData);\n\n        /// \u003csummary\u003e\n        /// 解密文件\n        /// \u003c/summary\u003e\n        /// \u003cparam name=\"cacheFile\"\u003ecache文件\u003c/param\u003e\n        /// \u003cparam name=\"decodedFile\"\u003e解密后文件\u003c/param\u003e\n        void Decrypt(string cacheFile,string decodedFile);\n\n    }\n```\n\n### BaseCacheDecrypt\n\n然后，实现一个默认的抽象类BaseCacheDecrypt，实现一些公共的东西，具体的转码工作让子类去实现：\n\n``` cs\npublic abstract class BaseCacheDecrypt : ICacheDecrypt\n    {\n\n        protected string currentCacheFile;\n\n        public abstract string AcceptableExtension\n        {\n            get;\n        }\n\n        public abstract byte[] Decrypt(byte[] cacheFileData);\n\n        public byte[] Decrypt(string cacheFile)\n        {\n            currentCacheFile = cacheFile;\n            return Decrypt(File.ReadAllBytes(cacheFile));\n        }\n\n        public void Decrypt(string cacheFile, string decodedFile)\n        {\n            File.WriteAllBytes(decodedFile, Decrypt(cacheFile));\n        }\n\n        public  bool isAcceptable(string cacheFile)\n        {\n            return cacheFile.EndsWith(AcceptableExtension);\n        }\n    }\n```\n\n### NetMusicCacheDecrypt\n然后，分别实现酷狗和网易云音乐的解码工作，酷狗的上次已经写了如何解码，这里只贴网易的，解码很简单，异或0xa3就可以了。网易音乐在测试时发现好多mp3没有ID3信息，经过观察发现缓存文件名里包含歌曲的id信息，因此可以根据这个id信息去抓取歌曲网页，解析出歌手和歌曲名称，然后写入到ID3里，这里ID3的读写采用了GitHub上的一个开源库\n\n\n``` cs\n\n    /// \u003csummary\u003e\n    /// 网易云缓存解密\n    /// \u003c/summary\u003e\n    public class NetMusicCacheDecrypt : BaseCacheDecrypt\n    {\n        public override string AcceptableExtension\n        {\n            get\n            {\n                return \".uc\";\n            }\n        }\n\n        string cut(string str,string start,string end)\n        {\n            var startIndex = str.IndexOf(start);\n            if (startIndex == -1)\n            {\n                return \"\";\n            }\n            startIndex += start.Length;\n            var endIndex = str.IndexOf(end, startIndex);\n            if (endIndex == -1)\n            {\n                return \"\";\n            }\n            return str.Substring(startIndex, endIndex - startIndex);\n        }\n\n        public override byte[] Decrypt(byte[] cacheFileData)\n        {         \n            for (var i = 0; i \u003c cacheFileData.Length; i++)\n            {\n                // 异或0xa3\n                cacheFileData[i] ^= 0xa3;\n            }\n\n            var fileName = new FileInfo(currentCacheFile).Name;\n            var songId = fileName.Substring(0, fileName.IndexOf(\"-\"));\n            var html = HttpHelper.SendGet(\"http://music.163.com/song?id=\" + songId);\n            if (html.Length \u003e 0)\n            {\n                var title = cut(html, \"\u003ctitle\u003e\", \"\u003c/title\u003e\").Trim();\n                var tempFile = currentCacheFile+ Guid.NewGuid().ToString();\n                File.WriteAllBytes(tempFile, cacheFileData);\n                Track theTrack = new Track(tempFile);\n                // 父亲写的散文诗(时光版) - 许飞 - 单曲 - 网易云音乐\n                theTrack.Artist = cut(title, \"-\", \"-\").Trim();\n                theTrack.Title = title.Substring(0, title.IndexOf(\"-\")).Trim();\n                // Save modifications on the disc\n                theTrack.Save();\n                cacheFileData = File.ReadAllBytes(tempFile);\n                File.Delete(tempFile);\n\n            }\n            \n            return cacheFileData;\n        }\n\n    }\n```\n\n接着介绍核心的Decryptor，实现转码的调度，这里的思路就是将所有的解码器放到一个list里，当一个文件过来的时候，遍历所有解码器，如果accetbale，就处理，否则跳过。\n两个主要工作：\n-   加载所有的BaseCacheDecrypt\n-   进行解码工作\n\n### 加载所有的BaseCacheDecrypt\n\n两种方法，一是自己实例化，一是使用反射，这里当然用反射了：）\n\n``` cs\n\nprivate Decryptor()\n        {\n           \n        }\n\n        public static Decryptor Instance\n        {\n            get\n            {\n                return Holder.decryptor;\n            }\n        }\n static class Holder\n        {\n            public static Decryptor decryptor = Load();\n\n\n            /// \u003csummary\u003e\n            /// 从当前Assembly加载\n            /// \u003c/summary\u003e\n            /// \u003creturns\u003e\u003c/returns\u003e\n            private static Decryptor Load()\n            {\n                Assembly assembly = Assembly.GetExecutingAssembly();\n                List\u003cType\u003e hostTypes = new List\u003cType\u003e();\n\n                foreach (var type in assembly.GetExportedTypes())\n                {\n                    //确定type为类并且继承自(实现)IMyInstance\n                    if (type.IsClass \u0026\u0026 typeof(BaseCacheDecrypt).IsAssignableFrom(type) \u0026\u0026 !type.IsAbstract)\n                        hostTypes.Add(type);\n                }\n\n                Decryptor decryptor = new Decryptor();\n                foreach (var type in hostTypes)\n                {\n                    ICacheDecrypt instance = (ICacheDecrypt)Activator.CreateInstance(type);\n                    decryptor.cacheDecryptors.Add(instance);\n                }\n\n                return decryptor;\n            }\n        }\n```\n\nDecryptor通过单例模式对外提供调用。\n\n### 进行解码\n\n判断拖入的是文件夹还是文件，文件夹的话遍历子文件，依次处理。解码方式就是钢说的，遍历decryptors，如果支持就解码。\n解码完后，读取ID3信息，对文件进行重命名。\n\n``` cs\n public int Process(string path)\n        {\n            int success = 0;\n\n            if (Directory.Exists(path))//如果是文件夹\n            {\n                DirectoryInfo dinfo = new DirectoryInfo(path);//实例化一个DirectoryInfo对象\n                foreach (FileInfo fs in dinfo.GetFiles()) //查找.kgtemp文件\n                {\n                    ProcessFile(fs.FullName);\n                    success++;\n                }\n            }\n            else\n            {\n                ProcessFile(path);\n                success = 1;\n            }\n\n            return success;\n        }\n\n        private string GetCleanFileName(string fileName)\n        {\n            StringBuilder rBuilder = new StringBuilder(fileName);\n            foreach (char rInvalidChar in Path.GetInvalidFileNameChars())\n                rBuilder.Replace(rInvalidChar.ToString(), string.Empty);\n            return rBuilder.ToString();\n        }\n\n        private string GetTargetFileName(string fileName)\n        {\n            var fileinfo = new FileInfo(fileName);\n            var rawName = fileinfo.Name.Substring(0, fileinfo.Name.IndexOf(\".\"));\n            return TargetDirectory + Path.DirectorySeparatorChar + rawName + \".mp3\";\n        }\n\n\n        void ProcessFile(string fileName)\n        {\n            _logger.Info(\"开始处理\" + fileName);\n            try\n            {\n                foreach (var decryptor in cacheDecryptors)\n                {\n                    if (decryptor.isAcceptable(fileName))\n                    {\n                        var targetName = TargetDirectory + Path.DirectorySeparatorChar + new FileInfo(fileName).Name + \".mp3\";\n\n                        decryptor.Decrypt(fileName, targetName);\n\n                        // 重命名\n                        if (AutoRename)\n                        {\n                            var mp3 = ID3Helper.ReadMp3(targetName);\n\n                            if (mp3.Title.Length \u003e 0)\n                            {\n                                string realFileName = GetTargetFileName(GetCleanFileName(mp3.Title + \"-\" + mp3.Artist + \".mp3\"));\n\n                                _logger.Info(\"重命名\" + realFileName);\n                                if (File.Exists(realFileName))\n                                {\n                                    File.Delete(realFileName);\n                                }\n\n                                File.Move(targetName, realFileName);\n                            }\n                        }\n                    }\n                }\n                _logger.Info(fileName + \"处理完成\");\n            }\n            catch(Exception ex)\n            {\n                _logger.Error(fileName + \"出现异常\" + ex.Message);\n            }\n          \n        }\n```\n\n\n  [1]: http://oyqmmpkcm.bkt.clouddn.com/1522310112266.jpg \"1522310112266\"\n  [2]: http://oyqmmpkcm.bkt.clouddn.com/1522310257994.jpg \"1522310257994\"\n  [3]: http://oyqmmpkcm.bkt.clouddn.com/1522310276416.jpg \"1522310276416\"\n  [4]: http://oyqmmpkcm.bkt.clouddn.com/1522310348535.jpg \"1522310348535\"\n  [5]: http://oyqmmpkcm.bkt.clouddn.com/1522310427084.jpg \"1522310427084\"\n  [6]: http://oyqmmpkcm.bkt.clouddn.com/1522310518946.jpg \"1522310518946\"\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjadepeng%2Fmusicdecryptor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjadepeng%2Fmusicdecryptor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjadepeng%2Fmusicdecryptor/lists"}