服务端NCM转MP3
示例

服务器端安装
wget https://github.com/magic-akari/ncmc/releases/download/vX.Y.Z/ncmc-x86_64-unknown-linux-musl.tgz或进入此开源项目下载对应服务器版本
magic-akari/ncmc
解压
tar -xzvf ncmc-x86_64-unknown-linux-musl.tgz移动解压文件
sudo mv ncmc /usr/local/bin/
sudo chmod +x /usr/local/bin验证安装
ncmc --version调用
**本网站使用的是 Typecho,我们需要在默认模板下方新建一个PHP文件用于发布自定义页面,路径在宝塔面板此处:
/www/wwwroot/你的网页域名/usr/themes/default**
使用
需要在新建空白的PHP里面复制粘贴以下php代码
<?php
/**
* Typecho 页面模板:NCM 转 MP3(拖拽上传+美化版)
*/
$this->need('header.php');
?>
<style>
.typecho-page-content {
max-width: 600px;
margin: 40px auto;
padding: 30px;
background: #f9f9f9;
border-radius: 12px;
box-shadow: 0 6px 20px rgba(0,0,0,0.1);
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
/* 标题 */
.typecho-page-content h2 {
text-align: center;
margin-bottom: 25px;
color: #333;
}
/* 上传区域 */
#drop-area {
border: 2px dashed #4CAF50;
border-radius: 12px;
padding: 40px;
text-align: center;
cursor: pointer;
transition: background 0.3s, border-color 0.3s;
color: #666;
}
#drop-area.hover {
background: #e8f5e9;
border-color: #45a049;
}
#drop-area p {
margin: 0;
font-size: 16px;
}
/* 隐藏默认文件输入框 */
#fileElem {
display: none;
}
/* 按钮 */
button {
margin-top: 20px;
padding: 12px;
background: #4CAF50;
color: white;
border: none;
border-radius: 6px;
font-size: 16px;
cursor: pointer;
transition: 0.3s;
}
button:hover {
background: #45a049;
}
/* 状态信息 */
#status {
margin-top: 20px;
padding: 15px;
border-radius: 8px;
background: #fff3cd;
color: #856404;
border: 1px solid #ffeeba;
}
#status a {
color: #155724;
font-weight: bold;
text-decoration: none;
}
#status a:hover {
text-decoration: underline;
}
</style>
<div class="typecho-page-content">
<h2>NCM 文件转换 MP3(30 分钟有效)</h2>
<form method="post" enctype="multipart/form-data" id="uploadForm">
<div id="drop-area">
<p>拖拽 NCM 文件到此处,或点击选择文件</p>
<input type="file" name="ncmfile" id="fileElem" accept=".ncm" required>
<button type="submit">上传并转换</button>
</div>
</form>
<div id="status">
<?php
$uploadDir = __DIR__ . '/uploads/';
$downloadDir = __DIR__ . '/downloads/';
foreach([$uploadDir, $downloadDir] as $dir){
if(!is_dir($dir)) mkdir($dir, 0755, true);
}
if(!isset($_COOKIE['fingerprint'])){
$fingerprint = bin2hex(random_bytes(16));
setcookie('fingerprint', $fingerprint, time()+3600, "/");
} else {
$fingerprint = $_COOKIE['fingerprint'];
}
// 自动清理超过 30 分钟的 MP3 文件
$files = glob($downloadDir . '*.mp3');
foreach($files as $f){
if(time() - filemtime($f) > 30*60){
@unlink($f);
}
}
// 检查用户最近 30 分钟上传的 MP3 文件
$userFiles = glob($downloadDir . '*' . $fingerprint . '*.mp3');
// 按时间排序,最近上传的在前
usort($userFiles, function($a, $b){
return filemtime($b) - filemtime($a);
});
if(!empty($userFiles)){
echo "<strong>您最近 30 分钟转换的 MP3 文件:</strong><br><ul style='padding-left:20px'>";
foreach($userFiles as $f){
// 获取文件名显示给用户(去掉指纹和时间戳)
$baseName = basename($f);
// 移除 _fingerprint_timestamp 部分
$displayName = preg_replace('/_[a-f0-9]{32}_\d+\.mp3$/', '.mp3', $baseName);
$fileUrl = str_replace($_SERVER['DOCUMENT_ROOT'], '', $f);
echo "<li><a href='$fileUrl' download>$displayName</a></li>";
}
echo "</ul>";
}
// 上传并转换
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['ncmfile'])){
$file = $_FILES['ncmfile'];
$filename = basename($file['name']);
$filePath = $uploadDir . $filename;
if(strtolower(pathinfo($filename, PATHINFO_EXTENSION)) !== 'ncm'){
echo "只允许上传 .ncm 文件。";
exit;
}
if(move_uploaded_file($file['tmp_name'], $filePath)){
$timestamp = time();
$outputFile = $downloadDir . pathinfo($filename, PATHINFO_FILENAME) . '_' . $fingerprint . '_' . $timestamp . '.mp3';
$cmd = '/usr/local/bin/ncmc ' . escapeshellarg($filePath) . ' 2>&1';
exec($cmd, $output, $returnVar);
$mp3File = $uploadDir . pathinfo($filename, PATHINFO_FILENAME) . '.mp3';
if($returnVar === 0 && file_exists($mp3File)){
rename($mp3File, $outputFile);
$fileUrl = str_replace($_SERVER['DOCUMENT_ROOT'], '', $outputFile);
echo "转换成功!<a href='$fileUrl' download>点击下载 MP3</a>";
} else {
echo "转换失败,错误信息:<pre>" . implode("\n", $output) . "</pre>";
}
} else {
echo "上传失败,请检查上传目录权限。";
}
}
?>
</div>
</div>
<script>
// 拖拽上传逻辑
let dropArea = document.getElementById('drop-area');
let fileInput = document.getElementById('fileElem');
['dragenter','dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, e => {
e.preventDefault();
e.stopPropagation();
dropArea.classList.add('hover');
}, false);
});
['dragleave','drop'].forEach(eventName => {
dropArea.addEventListener(eventName, e => {
e.preventDefault();
e.stopPropagation();
dropArea.classList.remove('hover');
}, false);
});
dropArea.addEventListener('drop', e => {
let dt = e.dataTransfer;
let files = dt.files;
if(files.length){
fileInput.files = files;
}
});
dropArea.addEventListener('click', () => {
fileInput.click();
});
</script>
<?php $this->need('footer.php'); ?>来到Typecho管理页面选择:
- 创建页面
- 填写一个页面标题,比如“ncm转mp3”内容可不填写
来到页面的右边,选择“自定义模板”发布即可**
关于为什么没有前端解密
因为没有找到相关前端解密的js,反正能用就行