30 Commits
1.3.0 ... 1.4.0

Author SHA1 Message Date
67cba29a8f chore: enhance output 2024-09-13 10:58:29 +08:00
2e5fc75fee Merge pull request #27 from um-lsr/main
fix: ncm 3.x version compatibility issues
2024-09-13 10:47:32 +08:00
鲁树人
f58c6c3880 fix: cover image memory leak
Signed-off-by: 鲁树人 <lu.shuren@um-react.app>
2024-09-13 00:01:26 +01:00
鲁树人
2b52a367f2 fix #26: correctly calculate cover frame size
Signed-off-by: 鲁树人 <lu.shuren@um-react.app>
2024-09-13 00:00:35 +01:00
36e6801f29 fix: bug report template 2024-08-17 09:01:46 +08:00
2cd7d69aa5 fix: bug report 2024-08-17 09:01:09 +08:00
1aaaab10dc feat: notice of bug report 2024-08-17 09:00:15 +08:00
e04415db9b removed: feature request template 2024-08-17 08:57:24 +08:00
911beb25bc enhanced: bug report template 2024-08-17 08:56:06 +08:00
0a6ac353ff removed: expect.bin 2024-04-17 16:56:43 +08:00
f89ea7eab6 fix: cannot process in folder 2024-04-07 13:59:03 +08:00
6eb3649753 update: README.md 2024-04-06 12:29:08 +08:00
8a13d0fe07 update: parse UTF-8 to lib in C# 2024-04-06 12:10:56 +08:00
321e5f342e fix: parse UTF-8 path to lib 2024-04-06 12:09:03 +08:00
dbdb73c120 fix: close file after fixing metadata(#17) 2024-04-06 10:47:49 +08:00
7ad6f27b75 feat: star history 2024-04-06 10:36:58 +08:00
dd8b2d6fec chore: typo 2024-04-02 09:19:31 +08:00
981913d729 feat: 1.3.0 update README.md 2024-03-31 09:55:24 +08:00
5fbfc2db81 fix: Action cannot find builds 2024-03-25 12:09:35 +08:00
e5cd9bddde fix: cannot find build target 2024-03-25 12:05:50 +08:00
068b1a6512 fix: missing header 2024-03-25 11:51:55 +08:00
265fe6765d fix: target name 2024-03-25 10:55:00 +08:00
5c970f4a09 feat: add header for lib 2024-03-25 09:07:01 +08:00
53b0d99431 feat: library example 2024-03-24 18:00:07 +08:00
37acbf07a7 fix: missing include and link options 2024-03-24 17:48:04 +08:00
d47d29426e chore: only build lib on Windows 2024-03-24 17:44:51 +08:00
602c6ed43a add: unix/linux lib support; upload libs 2024-03-24 17:36:44 +08:00
d8a0944810 feat: add dynamic library build 2024-03-24 17:27:41 +08:00
aa7ae6128d refactor: project file structure 2024-03-24 09:20:25 +08:00
wshon
ae08d4475c fix: exception when artist is empty (#16)
Co-authored-by: wshon <me@wshon.com>
2024-03-24 09:01:38 +08:00
21 changed files with 275 additions and 84 deletions

View File

@@ -1,31 +1,35 @@
---
name: Bug report
about: Create a bug report to help us improve
title: "[Bug] Summarize the bug here"
name: "[请按照此模板填写] 报告 Bug"
about: "创建一个 Bug 报告,不按照模板的 Issue 会被关闭。"
title: "[Bug] 总结你的 Bug 报告"
labels: bug
assignees: taurusxin
---
**Describe the bug**
A clear and concise description of what the bug is.
**Bug 描述**
清晰地描述一下 Bug 的大致问题。
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**复现方法**
**Expected behavior**
A clear and concise description of what you expected to happen.
复现此 Bug 的方法
**Screenshots**
If applicable, add screenshots to help explain your problem.
1. 打开 '...'
2. 点击 '....'
3. 发生报错
**预期行为**
解释一下原本应该出现的结果。
**屏幕截图**
如果可以,屏幕截图可以更好地阐述你的问题。
**环境**
**Desktop (please complete the following information):**
- OS: [e.g. Windows 11]
- 软件版本: [e.g. 1.3.2]
**附加内容**
**Additional context**
Add any other context about the problem here.
添加更多其他内容以帮助开发者更好地了解这个 Bug。

View File

@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[Feature] Summarize new feature here"
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -53,12 +53,18 @@ jobs:
- name: Build
run: cmake --build ${{ env.BUILD_PATH }} -j 4 --config ${{ env.BUILD_TYPE }}
- name: Upload artifact
- name: Upload artifact executable
uses: actions/upload-artifact@v4
with:
name: Windows amd64 Build - MSVC
path: ${{ env.BUILD_PATH }}/${{ env.BUILD_TYPE }}/ncmdump.exe
- name: Upload artifact DLL
uses: actions/upload-artifact@v4
with:
name: Windows amd64 Build - MSVC DLL
path: ${{ env.BUILD_PATH }}/${{ env.BUILD_TYPE }}/libncmdump.dll
build_on_linux:
runs-on: ubuntu-latest

View File

@@ -14,34 +14,73 @@ if(MSVC)
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/execution-charset:utf-8>")
endif()
FILE(GLOB HEADERS ./*.h)
FILE(GLOB COMMON_SOURCES cJSON.cpp aes.cpp main.cpp ncmcrypt.cpp)
FILE(GLOB WIN_SOURCES win32_init.cpp)
FILE(GLOB COMMON_HEADERS src/include/*.h)
FILE(GLOB COMMON_SOURCES src/ncmcrypt.cpp src/utils/*.cpp)
FILE(GLOB EXECUTABLE_SOURCES src/main.cpp)
FILE(GLOB LIBRARY_HEADERS src/lib/libncmdump.h)
FILE(GLOB LIBRARY_SOURCES src/lib/*.cpp)
FILE(GLOB WIN_SOURCES src/platform/win32_init.cpp)
if(WIN32)
set(SOURCES ${COMMON_SOURCES} ${WIN_SOURCES})
else()
set(SOURCES ${COMMON_SOURCES})
endif()
add_executable(ncmdump
${HEADERS}
${SOURCES}
add_executable(ncmdump_exec
${COMMON_HEADERS}
${COMMON_SOURCES}
${EXECUTABLE_SOURCES}
${WIN_SOURCES}
)
add_library(ncmdump_lib SHARED
${COMMON_HEADERS}
${COMMON_SOURCES}
${LIBRARY_HEADERS}
${LIBRARY_SOURCES}
${WIN_SOURCES}
)
target_link_libraries(ncmdump tag)
target_include_directories(ncmdump PRIVATE taglib)
target_include_directories(ncmdump PRIVATE taglib/taglib)
target_include_directories(ncmdump PRIVATE taglib/taglib/toolkit)
target_include_directories(ncmdump PRIVATE taglib/taglib/mpeg/id3v2)
if(WIN32)
set_target_properties(ncmdump_lib PROPERTIES OUTPUT_NAME "libncmdump")
target_include_directories(ncmdump_lib PRIVATE src/include)
target_link_libraries(ncmdump_lib tag)
target_include_directories(ncmdump_lib PRIVATE taglib)
target_include_directories(ncmdump_lib PRIVATE taglib/taglib)
target_include_directories(ncmdump_lib PRIVATE taglib/taglib/toolkit)
target_include_directories(ncmdump_lib PRIVATE taglib/taglib/mpeg/id3v2)
if(CMAKE_COMPILER_IS_GNUCXX)
target_link_options(ncmdump PRIVATE -static)
# static link when using MinGW
target_link_options(ncmdump_exec PRIVATE -static)
target_link_options(ncmdump_lib PRIVATE -static)
endif()
else()
add_executable(ncmdump_exec
${COMMON_HEADERS}
${COMMON_SOURCES}
${EXECUTABLE_SOURCES}
)
endif()
# all executable target
set_target_properties(ncmdump_exec PROPERTIES OUTPUT_NAME "ncmdump")
target_include_directories(ncmdump_exec PRIVATE src/include)
target_link_libraries(ncmdump_exec tag)
target_include_directories(ncmdump_exec PRIVATE taglib)
target_include_directories(ncmdump_exec PRIVATE taglib/taglib)
target_include_directories(ncmdump_exec PRIVATE taglib/taglib/toolkit)
target_include_directories(ncmdump_exec PRIVATE taglib/taglib/mpeg/id3v2)
include(GNUInstallDirs)
install(TARGETS ncmdump
if(WIN32)
install(TARGETS ncmdump_exec ncmdump_lib
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
else()
install(TARGETS ncmdump_exec
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()

View File

@@ -6,8 +6,9 @@
该版本为最早的 C++ 版本,也是作者开发的市面上第一个支持 ncm 转换的程序
源码复刻自 anonymous5l/ncmdump感谢前辈的付出
做了 Windows 下的移植,修复了一些编译问题
源码复刻自 anonymous5l/ncmdump感谢前辈的付出做了 Windows 下的移植,修复了一些编译问题
1.3.0 版本更新说明因为之前有小伙伴反馈无法解密带有特殊字符的文件名例如中文、日文、韩文或者是表情符号等在1.3.0以及之后的版本彻底修复了这个问题,所有的 UTF-8 字符都可以正常解密。并且还自带了 dll 的构建可以供其他应用程序C#、Python、Java等调用。
## 传送门
@@ -15,7 +16,9 @@
## 使用
你可以使用 Homebrew 来安装 ncmdump
### 命令行工具
你可以使用 Homebrew 来安装 ncmdump 的 cli 版本
```shell
brew install ncmdump
@@ -41,6 +44,12 @@ ncmdump file1 file2...
ncmdump -d folder
```
### 动态库
或者,如果你想利用此项目进行二次开发,例如在你的 C#、Python、Java 等项目中调用,你可以使用 `libncmdump` 动态库,具体使用方法见仓库的 `example` 文件夹
请注意!如果你在 Windows 下开发,传递到库构造函数的文件名编码**必须为 UTF-8 编码**,否则会抛出运行时错误。
## 编译项目
克隆本仓库
@@ -78,3 +87,9 @@ cmake --build build -j 8 --config Release
# Windows MinGW / Linux / macOS
cmake --build build -j 8
```
你可以在 `build` 文件夹下找到编译好的二进制文件,以及一个可供其它项目使用的动态库(Windows Only),使用方法见仓库的 `example` 文件夹
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=taurusxin/ncmdump&type=Date)](https://star-history.com/#taurusxin/ncmdump&Date)

View File

@@ -0,0 +1,67 @@
using System;
using System.Runtime.InteropServices;
namespace libncmdump_demo_cli
{
/// <summary>
/// NeteaseCrypt C# Wrapper
/// </summary>
class NeteaseCrypt
{
const string DLL_PATH = "libncmdump.dll";
[DllImport(DLL_PATH, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateNeteaseCrypt(IntPtr path);
[DllImport(DLL_PATH, CallingConvention = CallingConvention.Cdecl)]
private static extern int Dump(IntPtr NeteaseCrypt);
[DllImport(DLL_PATH, CallingConvention = CallingConvention.Cdecl)]
private static extern void FixMetadata(IntPtr NeteaseCrypt);
[DllImport(DLL_PATH, CallingConvention = CallingConvention.Cdecl)]
private static extern void DestroyNeteaseCrypt(IntPtr NeteaseCrypt);
private IntPtr NeteaseCryptClass = IntPtr.Zero;
/// <summary>
/// 创建 NeteaseCrypt 类的实例。
/// </summary>
/// <param name="FileName">网易云音乐 ncm 加密文件路径</param>
public NeteaseCrypt(string FileName)
{
byte[] bytes = Encoding.UTF8.GetBytes(FileName);
IntPtr inputPtr = Marshal.AllocHGlobal(bytes.Length + 1);
Marshal.Copy(bytes, 0, inputPtr, bytes.Length);
Marshal.WriteByte(inputPtr, bytes.Length, 0);
NeteaseCryptClass = CreateNeteaseCrypt(inputPtr);
}
/// <summary>
/// 启动转换过程。
/// </summary>
/// <returns>返回一个整数指示转储过程的结果。如果成功返回0如果失败返回1。</returns>
public int Dump()
{
return Dump(NeteaseCryptClass);
}
/// <summary>
/// 修复音乐文件元数据。
/// </summary>
public void FixMetadata()
{
FixMetadata(NeteaseCryptClass);
}
/// <summary>
/// 销毁 NeteaseCrypt 类的实例。
/// </summary>
public void Destroy()
{
DestroyNeteaseCrypt(NeteaseCryptClass);
}
}
}

26
example/csharp/Program.cs Normal file
View File

@@ -0,0 +1,26 @@
using System;
using System.Threading.Tasks;
namespace libncmdump_demo_cli
{
internal class Program
{
static void Main(string[] args)
{
// 文件名
string filePath = "test.ncm";
// 创建 NeteaseCrypt 类的实例
NeteaseCrypt neteaseCrypt = new NeteaseCrypt(filePath);
// 启动转换过程
int result = neteaseCrypt.Dump();
// 修复元数据
neteaseCrypt.FixMetadata();
// [务必]销毁 NeteaseCrypt 类的实例
neteaseCrypt.Destroy();
}
}
}

31
src/lib/libncmdump.cpp Normal file
View File

@@ -0,0 +1,31 @@
#include "libncmdump.h"
#include <filesystem>
namespace fs = std::filesystem;
extern "C" {
API NeteaseCrypt* CreateNeteaseCrypt(const char* path) {
fs::path fPath = fs::u8path(path);
return new NeteaseCrypt(fPath.u8string());
}
API int Dump(NeteaseCrypt* neteaseCrypt) {
try
{
neteaseCrypt->Dump();
}
catch (const std::invalid_argument& e)
{
return 1;
}
return 0;
}
API void FixMetadata(NeteaseCrypt* neteaseCrypt) {
neteaseCrypt->FixMetadata();
}
API void DestroyNeteaseCrypt(NeteaseCrypt* neteaseCrypt) {
delete neteaseCrypt;
}
}

14
src/lib/libncmdump.h Normal file
View File

@@ -0,0 +1,14 @@
#include "ncmcrypt.h"
#ifdef _WIN32
#define API __declspec(dllexport)
#else
#define API
#endif
extern "C" {
API NeteaseCrypt* CreateNeteaseCrypt(const char* path);
API int Dump(NeteaseCrypt* neteaseCrypt);
API void FixMetadata(NeteaseCrypt* neteaseCrypt);
API void DestroyNeteaseCrypt(NeteaseCrypt* neteaseCrypt);
}

View File

@@ -35,7 +35,7 @@ void processFile(const fs::path &filePath)
crypt.Dump();
crypt.FixMetadata();
std::cout << BOLDGREEN << "Done: " << RESET << "'" << crypt.dumpFilepath().u8string() << "'" << std::endl;
std::cout << BOLDGREEN << "[Done] " << RESET << "'" << filePath.u8string() << "' -> '" << crypt.dumpFilepath().u8string() << "'" << std::endl;
}
catch (const std::invalid_argument &e)
{
@@ -77,7 +77,7 @@ int main(int argc, char **argv)
#define COMPARE_STR(s1, s2) (strcmp(s1, s2) == 0)
#define HELP_SHORT "-h"
#define HELP_LONG "--help"
#define FOLDER "-d"
#define PROCESS_FOLDER "-d"
for (int i = 1; i < argc; ++i)
{
if (COMPARE_STR(argv[i], HELP_SHORT) || COMPARE_STR(argv[i], HELP_LONG))
@@ -85,13 +85,13 @@ int main(int argc, char **argv)
displayHelp();
return 0;
}
else if (COMPARE_STR(argv[i], FOLDER))
else if (COMPARE_STR(argv[i], PROCESS_FOLDER))
{
processFolders = true;
if (i + 1 < argc && argv[i + 1][0] != '-')
{
folderProvided = true;
processFilesInFolder(argv[i + 1]);
processFilesInFolder(fs::u8path(argv[i + 1]));
// Skip the folder name
++i;
}

View File

@@ -2,6 +2,7 @@
#include "aes.h"
#include "base64.h"
#include "cJSON.h"
#include "color.h"
#define TAGLIB_STATIC
#include "taglib/toolkit/tfile.h"
@@ -94,12 +95,18 @@ NeteaseMusicMetadata::NeteaseMusicMetadata(cJSON *raw)
artistLen = cJSON_GetArraySize(swap);
i = 0;
for (i = 0; i < artistLen - 1; i++)
for (i = 0; i < artistLen; i++)
{
auto artist = cJSON_GetArrayItem(swap, i);
if (cJSON_GetArraySize(artist) > 0)
{
if (!mArtist.empty())
{
mArtist += std::string(cJSON_GetStringValue(cJSON_GetArrayItem(cJSON_GetArrayItem(swap, i), 0)));
mArtist += "/";
}
mArtist += std::string(cJSON_GetStringValue(cJSON_GetArrayItem(cJSON_GetArrayItem(swap, i), 0)));
mArtist += std::string(cJSON_GetStringValue(cJSON_GetArrayItem(artist, 0)));
}
}
}
swap = cJSON_GetObjectItem(raw, "bitrate");
@@ -247,9 +254,10 @@ void NeteaseCrypt::FixMetadata()
tag->setAlbum(TagLib::String(mMetaData->album(), TagLib::String::UTF8));
}
tag->setComment(TagLib::String("Create by netease copyright protected dump tool. author 5L", TagLib::String::UTF8));
tag->setComment(TagLib::String("Create by taurusxin/ncmdump.", TagLib::String::UTF8));
audioFile->save();
audioFile->~File();
}
void NeteaseCrypt::Dump()
@@ -351,7 +359,7 @@ NeteaseCrypt::NeteaseCrypt(std::string const &path)
if (n <= 0)
{
std::cout << "[Warn] " << path << " missing metadata infomation can't fix some infomation!" << std::endl;
std::cout << BOLDYELLOW << "[Warn] " << RESET << "'" << path << "' missing metadata infomation can't fix some infomation!" << std::endl;
mMetaData = NULL;
}
@@ -384,23 +392,24 @@ NeteaseCrypt::NeteaseCrypt(std::string const &path)
mMetaData = new NeteaseMusicMetadata(cJSON_Parse(modifyDecryptData.c_str()));
}
// skip crc32 & unuse charset
if (!mFile.seekg(9, mFile.cur))
// skip crc32 & image version
if (!mFile.seekg(5, mFile.cur))
{
throw std::invalid_argument("can't seek file");
}
uint32_t cover_frame_len{0};
read(reinterpret_cast<char *>(&cover_frame_len), 4);
read(reinterpret_cast<char *>(&n), sizeof(n));
if (n > 0)
{
char *imageData = (char *)malloc(n);
read(imageData, n);
mImageData = std::string(imageData, n);
mImageData = std::string(n, '\0');
read(&mImageData[0], n);
}
else
{
std::cout << "[Warn] " << path << " missing album can't fix album image!" << std::endl;
std::cout << BOLDYELLOW << "[Warn] " << RESET << "'" << path << "' missing album can't fix album image!" << std::endl;
}
mFile.seekg(cover_frame_len - n, mFile.cur);
}

Binary file not shown.