Skip to content

Commit 0f9e5ca

Browse files
committed
init
1 parent 7c6335f commit 0f9e5ca

File tree

14 files changed

+608
-2
lines changed

14 files changed

+608
-2
lines changed

.gitignore

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
*.egg-info/
24+
.installed.cfg
25+
*.egg
26+
MANIFEST
27+
28+
# PyInstaller
29+
# Usually these files are written by a python script from a template
30+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
31+
*.manifest
32+
*.spec
33+
34+
# Installer logs
35+
pip-log.txt
36+
pip-delete-this-directory.txt
37+
38+
# Unit test / coverage reports
39+
htmlcov/
40+
.tox/
41+
.coverage
42+
.coverage.*
43+
.cache
44+
nosetests.xml
45+
coverage.xml
46+
*.cover
47+
.hypothesis/
48+
.pytest_cache/
49+
50+
# Translations
51+
*.mo
52+
*.pot
53+
54+
# Django stuff:
55+
*.log
56+
local_settings.py
57+
db.sqlite3
58+
59+
# Flask stuff:
60+
instance/
61+
.webassets-cache
62+
63+
# Scrapy stuff:
64+
.scrapy
65+
66+
# Sphinx documentation
67+
docs/_build/
68+
69+
# PyBuilder
70+
target/
71+
72+
# Jupyter Notebook
73+
.ipynb_checkpoints
74+
75+
# pyenv
76+
.python-version
77+
78+
# celery beat schedule file
79+
celerybeat-schedule
80+
81+
# SageMath parsed files
82+
*.sage.py
83+
84+
# Environments
85+
.env
86+
.venv
87+
env/
88+
venv/
89+
ENV/
90+
env.bak/
91+
venv.bak/
92+
93+
# Spyder project settings
94+
.spyderproject
95+
.spyproject
96+
97+
# Rope project settings
98+
.ropeproject
99+
100+
# mkdocs documentation
101+
/site
102+
103+
# mypy
104+
.mypy_cache/
105+
106+
#
107+
temp

README.md

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,55 @@
1-
# ncmdump
2-
将.ncm格式音频文件转换为flac格式,提供windows客户端和WEB两种使用方式。(Convert .ncm format audio files to flac format, providing two usage options: a Windows client and a web interface.)
1+
# ncm -> flac converter
2+
3+
将.ncm格式音频文件转换为flac格式,提供windows客户端和WEB两种使用方式。
4+
5+
## 1 环境
6+
7+
基本环境:
8+
```bash
9+
pip install mutagen
10+
pip install pycryptodome
11+
```
12+
13+
GUI额外环境:
14+
```bash
15+
pip install PyQt6
16+
pip install pyinstaller
17+
```
18+
19+
WEB额外环境:
20+
```bash
21+
pip install streamlit
22+
```
23+
24+
全安装:
25+
```bash
26+
pip install -r requirements.txt
27+
```
28+
29+
## 2 使用
30+
31+
### 2.1 GUI
32+
33+
运行:
34+
```bash
35+
python gui.py
36+
```
37+
38+
编译:
39+
```bash
40+
pyinstaller --onefile --add-data="file:file" -wF -i file/favicon-32x32.png -n "NCM转换器" .\gui.py
41+
```
42+
43+
效果:
44+
![s1](./file/s1.gif)
45+
46+
47+
### 2.2 WEB
48+
49+
运行:
50+
```bash
51+
streamlit run web.py --server.port 1111
52+
```
53+
54+
效果:
55+
![s2](./file/s2.gif)

cplusplus/libncmdump.dll

350 KB
Binary file not shown.

cplusplus/ncmdumpdll.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import ctypes
2+
import os
3+
4+
class NcmdumpDll:
5+
def __init__(self, file_name):
6+
dll_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "libncmdump.dll"))
7+
self.dll = ctypes.CDLL(dll_path)
8+
9+
# Define function prototypes
10+
self.dll.CreateNeteaseCrypt.argtypes = [ctypes.c_char_p]
11+
self.dll.CreateNeteaseCrypt.restype = ctypes.c_void_p
12+
13+
self.dll.Dump.argtypes = [ctypes.c_void_p]
14+
self.dll.Dump.restype = ctypes.c_int
15+
16+
self.dll.FixMetadata.argtypes = [ctypes.c_void_p]
17+
self.dll.FixMetadata.restype = None
18+
19+
self.dll.DestroyNeteaseCrypt.argtypes = [ctypes.c_void_p]
20+
self.dll.DestroyNeteaseCrypt.restype = None
21+
22+
# Convert file name to bytes
23+
file_bytes = file_name.encode('utf-8')
24+
25+
# Allocate memory and copy file name bytes
26+
input_ptr = ctypes.create_string_buffer(file_bytes)
27+
28+
# Create NeteaseCrypt instance
29+
self.netease_crypt = self.dll.CreateNeteaseCrypt(input_ptr)
30+
31+
def dump(self):
32+
return self.dll.Dump(self.netease_crypt)
33+
34+
def fix_metadata(self):
35+
self.dll.FixMetadata(self.netease_crypt)
36+
37+
def destroy(self):
38+
self.dll.DestroyNeteaseCrypt(self.netease_crypt)
39+
40+
def process_file(self):
41+
self.dump()
42+
self.fix_metadata()
43+
self.destroy()
44+
45+
if __name__ == "__main__":
46+
# 文件名
47+
file_path = "YOASOBI.ncm"
48+
# 创建 NeteaseCrypt 类的实例
49+
netease_crypt = NcmdumpDll(file_path)
50+
# 启动转换过程
51+
netease_crypt.process_file()

file/bk.png

153 KB
Loading

file/favicon-32x32.png

2.95 KB
Loading

file/s1.gif

289 KB
Loading

file/s2.gif

551 KB
Loading

gui.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import sys
2+
from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel
3+
from PyQt6.QtCore import Qt
4+
from PyQt6.QtGui import QDropEvent,QPixmap, QPainter, QFont, QColor,QFontMetrics,QIcon,QDragEnterEvent
5+
import os
6+
from ncmdump import dump
7+
8+
class DragDropWidget(QWidget):
9+
def __init__(self, parent=None):
10+
super().__init__(parent)
11+
self.setAcceptDrops(True)
12+
self.init_ui()
13+
14+
def init_ui(self):
15+
self.setWindowTitle("NCM转换器")
16+
self.setGeometry(100, 100, 573, 573)
17+
18+
self.setWindowIcon(QIcon(self.get_resource_path("file/favicon-32x32.png")))
19+
# 加载图片
20+
self.original_pixmap = QPixmap(self.get_resource_path("file/bk.png"))
21+
22+
self.label = QLabel(self)
23+
self.label.setPixmap(self.original_pixmap)
24+
self.label.setGeometry(0, 0, 573, 573)
25+
self.update_text('将ncm文件拖拽到此处') # 初始文本内容
26+
def update_text(self, text):
27+
pixmap = self.original_pixmap.copy() # 复制原始的 QPixmap 对象
28+
painter = QPainter(pixmap)
29+
painter.setPen(QColor('black')) # 设置文本颜色
30+
font = QFont('SimHei', 20) # 设置字体和大小
31+
painter.setFont(font)
32+
33+
font_metrics = QFontMetrics(font)
34+
text_width = font_metrics.horizontalAdvance(text)
35+
text_height = font_metrics.height()
36+
37+
x = (pixmap.width() - text_width) // 2
38+
y = (pixmap.height() - text_height) // 2 + font_metrics.ascent()
39+
40+
painter.drawText(x, y, text) # 在图片中居中绘制文本
41+
painter.end()
42+
self.label.setPixmap(pixmap) # 更新 QLabel 中的图片
43+
44+
def get_resource_path(self,relative_path):
45+
if hasattr(sys, '_MEIPASS'):
46+
return os.path.join(sys._MEIPASS, relative_path)
47+
return os.path.join(os.path.abspath("."), relative_path)
48+
49+
50+
def dragEnterEvent(self, event: QDragEnterEvent):
51+
if event.mimeData().hasUrls():
52+
event.acceptProposedAction()
53+
54+
def dropEvent(self, event: QDropEvent):
55+
for url in event.mimeData().urls():
56+
file_path = url.toLocalFile()
57+
if file_path.endswith(".ncm"):
58+
try:
59+
dump(file_path)
60+
self.update_text(f"处理完成:{file_path}")
61+
except Exception as e:
62+
self.update_text(f"处理文件时出错:{str(e)}")
63+
64+
65+
if __name__ == "__main__":
66+
app = QApplication(sys.argv)
67+
widget = DragDropWidget()
68+
widget.show()
69+
sys.exit(app.exec())

ncmdump/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .core import dump

0 commit comments

Comments
 (0)