Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f1ee83bf79 | |||
| a6f37a961c | |||
| 9d4c7d57b8 | |||
| ae6c580d25 |
14
CHANGELOG.md
14
CHANGELOG.md
@ -51,8 +51,20 @@
|
|||||||
- :hammer: 重构代码!使用mpsc进行线程通讯。
|
- :hammer: 重构代码!使用mpsc进行线程通讯。
|
||||||
|
|
||||||
|
|
||||||
## [2.5.10] - 2025.3.15
|
## [2.5.11] - 2025.3.15
|
||||||
|
- 修正依赖版本号
|
||||||
### Features :sparkles:
|
### Features :sparkles:
|
||||||
- 重新进度条支持!美观了不少啊
|
- 重新进度条支持!美观了不少啊
|
||||||
### Refactoring
|
### Refactoring
|
||||||
- :hammer: 重构大量代码!
|
- :hammer: 重构大量代码!
|
||||||
|
|
||||||
|
## [2.6.11] - 2025.3.15
|
||||||
|
### Features :sparkles:
|
||||||
|
- 更新自动打开文件夹选项,当解密结束后自动调用文件管理器打开输出目录
|
||||||
|
|
||||||
|
## [2.7.11] - 2025.3.23
|
||||||
|
### Fixed
|
||||||
|
- :arrow_up: 升级依赖
|
||||||
|
- 修正了解密完成后不会自动退出的bug
|
||||||
|
### Refactoring
|
||||||
|
- :hammer: 重构部分代码!
|
||||||
23
Cargo.lock
generated
23
Cargo.lock
generated
@ -230,9 +230,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.16"
|
version = "1.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
@ -331,11 +331,10 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colored"
|
name = "colored"
|
||||||
version = "2.2.0"
|
version = "3.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
|
checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -812,7 +811,7 @@ checksum = "07dcca13d1740c0a665f77104803360da0bdb3323ecce2e93fa2c959a6d52806"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ncmmiao"
|
name = "ncmmiao"
|
||||||
version = "2.5.10"
|
version = "2.7.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
"audiotags",
|
"audiotags",
|
||||||
@ -1467,9 +1466,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
|
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
@ -1555,18 +1554,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.23"
|
version = "0.8.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6"
|
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.8.23"
|
version = "0.8.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154"
|
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ncmmiao"
|
name = "ncmmiao"
|
||||||
version = "2.5.10"
|
version = "2.7.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Lkhsss <lkhsss1019@gmail.com>"]
|
authors = ["Lkhsss <lkhsss1019@gmail.com>"]
|
||||||
description = "A magic tool convert ncm to flac"
|
description = "A magic tool convert ncm to flac"
|
||||||
@ -17,8 +17,8 @@ audiotags = {version = "0.5.0",default-features = false}
|
|||||||
base64 = {version = "0.22.*"}
|
base64 = {version = "0.22.*"}
|
||||||
chrono = {version = "0.4.*",features = ["clock"],default-features = false}
|
chrono = {version = "0.4.*",features = ["clock"],default-features = false}
|
||||||
clap = { version = "4.5.*", features = ["derive","std"]}
|
clap = { version = "4.5.*", features = ["derive","std"]}
|
||||||
colored = {version = "*",default-features = false}
|
colored = {version = "3.0.0",default-features = false}
|
||||||
env_logger = {version = "*",default-features = false}
|
env_logger = {version = "0.11.7",default-features = false}
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
image = "0.25.*"
|
image = "0.25.*"
|
||||||
indicatif = "0.17.9"
|
indicatif = "0.17.9"
|
||||||
|
|||||||
14
README.md
14
README.md
@ -1,14 +1,16 @@
|
|||||||
# NcmMiao :tada:
|
# NcmMiao :tada:
|
||||||
[](https://github.com/Lkhsss/NcmMiao/actions/workflows/build.yml)
|
[](https://github.com/Lkhsss/NcmMiao/actions/workflows/build.yml)
|
||||||
|
|
||||||
一个使用Rust语言编写的ncm文件解密工具(第一!😆)。
|
一个使用Rust语言编写的ncm文件解密工具😆。
|
||||||
|
|
||||||
### 功能及特点
|
### 功能及特点
|
||||||
- 支持单一文件,多文件夹递归批量解密。
|
- 支持单一文件,多文件夹递归批量解密。
|
||||||
- 完善的日志功能
|
- 完善的日志功能
|
||||||
- Colorful
|
- Colorful
|
||||||
- 编译文件小,解密快
|
- 编译文件小,解密快
|
||||||
- [New!]支持自动添加封面!
|
- 支持自动添加封面!
|
||||||
|
- 自动打开输出文件夹
|
||||||
|
- 简约美观
|
||||||
|
|
||||||
## 编译
|
## 编译
|
||||||
```
|
```
|
||||||
@ -20,9 +22,11 @@ cargo build -r
|
|||||||
```
|
```
|
||||||
ncmmiao [OPTIONS]
|
ncmmiao [OPTIONS]
|
||||||
Options:
|
Options:
|
||||||
-w, --workers <WORKERS> 最大线程数 约束逻辑在主函数
|
-w, --workers <WORKERS> 并发的最大线程数,默认为8线程(性能还行的话建议直接32-64,嘎嘎快)
|
||||||
-i, --input <输入文件/文件夹> 需要解密的文件夹或文件
|
-i, --input <输入文件/目录> 需要解密的文件夹或文件
|
||||||
-o, --output <输出文件夹> [默认: NcmmiaoOutput]
|
-o, --output <输出目录> 输出目录 [default: NcmmiaoOutput]
|
||||||
|
-f, --forcesave 强制覆盖保存开关
|
||||||
|
-a, --autoopen 自动打开输出目录
|
||||||
```
|
```
|
||||||
|
|
||||||
~~输出文件夹在output。等我想写了再写命令行解析(bushi。~~ 写了写了
|
~~输出文件夹在output。等我想写了再写命令行解析(bushi。~~ 写了写了
|
||||||
|
|||||||
@ -19,4 +19,8 @@ pub struct Cli {
|
|||||||
/// 强制覆盖保存开关
|
/// 强制覆盖保存开关
|
||||||
#[arg(short, long, name = "强制覆盖开关")]
|
#[arg(short, long, name = "强制覆盖开关")]
|
||||||
pub forcesave: bool,
|
pub forcesave: bool,
|
||||||
|
|
||||||
|
/// 自动打开输出目录
|
||||||
|
#[arg(short, long, name = "自动打开输出目录")]
|
||||||
|
pub autoopen: bool,
|
||||||
}
|
}
|
||||||
|
|||||||
54
src/main.rs
54
src/main.rs
@ -3,6 +3,7 @@ use colored::{Color, Colorize};
|
|||||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
|
use messager::Message;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{
|
use std::{
|
||||||
path::Path,
|
path::Path,
|
||||||
@ -16,12 +17,13 @@ mod clap;
|
|||||||
mod logger;
|
mod logger;
|
||||||
mod messager;
|
mod messager;
|
||||||
mod ncmdump;
|
mod ncmdump;
|
||||||
|
mod opendir;
|
||||||
mod pathparse;
|
mod pathparse;
|
||||||
mod test;
|
mod test;
|
||||||
mod threadpool;
|
mod threadpool;
|
||||||
use ncmdump::Ncmfile;
|
use ncmdump::Ncmfile;
|
||||||
|
|
||||||
const DEFAULT_MAXWORKER:usize = 8;
|
const DEFAULT_MAXWORKER: usize = 8;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let timer = ncmdump::TimeCompare::new();
|
let timer = ncmdump::TimeCompare::new();
|
||||||
@ -53,7 +55,9 @@ fn main() {
|
|||||||
let undumpfile = pathparse::pathparse(input); // 该列表将存入文件的路径
|
let undumpfile = pathparse::pathparse(input); // 该列表将存入文件的路径
|
||||||
|
|
||||||
let taskcount = undumpfile.len();
|
let taskcount = undumpfile.len();
|
||||||
let successful = Arc::new(Mutex::new(0));
|
let mut success_count = 0; //成功任务数
|
||||||
|
|
||||||
|
|
||||||
if taskcount == 0 {
|
if taskcount == 0 {
|
||||||
error!("没有找到有效文件。使用-i参数输入需要解密的文件或文件夹。")
|
error!("没有找到有效文件。使用-i参数输入需要解密的文件或文件夹。")
|
||||||
} else {
|
} else {
|
||||||
@ -69,17 +73,20 @@ fn main() {
|
|||||||
// 循环开始
|
// 循环开始
|
||||||
for filepath in undumpfile {
|
for filepath in undumpfile {
|
||||||
let output = outputdir.clone();
|
let output = outputdir.clone();
|
||||||
let successful = Arc::clone(&successful);
|
let senderin: Sender<messager::Message> = tx.clone();
|
||||||
let sender: Sender<messager::Message> = tx.clone();
|
let senderon: Sender<messager::Message> = tx.clone();
|
||||||
pool.execute(move || match Ncmfile::new(filepath.as_str()) {
|
pool.execute(move || match Ncmfile::new(filepath.as_str()) {
|
||||||
Ok(mut n) => match n.dump(Path::new(&output), sender, forcesave) {
|
Ok(mut n) => match n.dump(Path::new(&output), senderin, forcesave) {
|
||||||
Ok(_) => {
|
Ok(_) => {}
|
||||||
let mut num = successful.lock().unwrap();
|
Err(e) => {
|
||||||
*num += 1;
|
let messager = messager::Messager::new(n.fullfilename, senderon);
|
||||||
|
messager.send(messager::Signals::Err(e));
|
||||||
}
|
}
|
||||||
Err(e) => error!("[{}] 解密失败: {}", filepath.yellow(), e),
|
|
||||||
},
|
},
|
||||||
Err(e) => error!("[{}] 解密失败: {}", filepath.yellow(), e),
|
Err(e) => {
|
||||||
|
let messager = messager::Messager::new(filepath, senderon);
|
||||||
|
messager.send(messager::Signals::Err(e));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//循环到此结束
|
//循环到此结束
|
||||||
@ -95,13 +102,21 @@ fn main() {
|
|||||||
)
|
)
|
||||||
.with_message("解密中");
|
.with_message("解密中");
|
||||||
let progressbar = MP.add(pb);
|
let progressbar = MP.add(pb);
|
||||||
//接受消息
|
|
||||||
|
|
||||||
|
//定义计数器
|
||||||
|
// 接受消息!!!!!!!!!!
|
||||||
for messages in rx {
|
for messages in rx {
|
||||||
|
match messages.signal{
|
||||||
|
messager::Signals::End|messager::Signals::Err(_)=>{success_count+=1},
|
||||||
|
_=>()
|
||||||
|
}
|
||||||
|
if success_count < taskcount {
|
||||||
progressbar.inc(1);
|
progressbar.inc(1);
|
||||||
messages.log(); //发送log
|
messages.log(); //发送log
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
progressbar.finish_and_clear();
|
progressbar.finish_and_clear();
|
||||||
}
|
}
|
||||||
let timecount = timer.compare();
|
let timecount = timer.compare();
|
||||||
@ -112,13 +127,20 @@ fn main() {
|
|||||||
format!("共计用时{}毫秒", timecount)
|
format!("共计用时{}毫秒", timecount)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let successful = *successful.lock().unwrap();
|
|
||||||
info!(
|
info!(
|
||||||
"成功解密{}个文件,{}个文件解密失败,{}",
|
"成功解密{}个文件,{}个文件解密失败,{}",
|
||||||
successful.to_string().bright_green(),
|
success_count.to_string().bright_green(),
|
||||||
(taskcount - successful).to_string().bright_red(),
|
(taskcount - success_count).to_string().bright_red(),
|
||||||
showtime()
|
showtime()
|
||||||
)
|
);
|
||||||
|
|
||||||
|
// 自动打开输出文件夹
|
||||||
|
if cli.autoopen {
|
||||||
|
info!("自动打开文件夹:[{}]", outputdir.cyan());
|
||||||
|
opendir::opendir(outputdir.into());
|
||||||
|
} else {
|
||||||
|
info!("输出文件夹:[{}]", outputdir.cyan());
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use log::info;
|
use log::{error, info, warn};
|
||||||
|
|
||||||
use crate::messager;
|
use crate::{messager, ncmdump};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
pub struct Messager {
|
pub struct Messager {
|
||||||
@ -23,10 +23,19 @@ impl Message {
|
|||||||
Signals::Decrypt => "解密歌曲信息",
|
Signals::Decrypt => "解密歌曲信息",
|
||||||
Signals::Save => "保存文件",
|
Signals::Save => "保存文件",
|
||||||
Signals::End => "成功!",
|
Signals::End => "成功!",
|
||||||
|
Signals::Err(e)=>&e.to_string(),
|
||||||
};
|
};
|
||||||
info!("[{}] {}", self.name.cyan(), loginfo)
|
match &self.signal{
|
||||||
|
Signals::Err(e)=>{match e{
|
||||||
|
ncmdump::NcmError::ProtectFile=>warn!("[{}] {}", self.name.cyan(), loginfo),
|
||||||
|
_=>error!("[{}] {}", self.name.cyan(), loginfo),
|
||||||
|
}},
|
||||||
|
_=>info!("[{}] {}", self.name.cyan(), loginfo)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(PartialEq)]
|
||||||
pub enum Signals {
|
pub enum Signals {
|
||||||
Start,
|
Start,
|
||||||
GetMetaInfo,
|
GetMetaInfo,
|
||||||
@ -34,6 +43,7 @@ pub enum Signals {
|
|||||||
Decrypt,
|
Decrypt,
|
||||||
Save,
|
Save,
|
||||||
End,
|
End,
|
||||||
|
Err(ncmdump::NcmError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Messager {
|
impl Messager {
|
||||||
@ -56,6 +66,8 @@ impl Debug for Message {
|
|||||||
Signals::End => "破解完成",
|
Signals::End => "破解完成",
|
||||||
Signals::GetMetaInfo => "获取元数据",
|
Signals::GetMetaInfo => "获取元数据",
|
||||||
Signals::GetCover => "获取封面",
|
Signals::GetCover => "获取封面",
|
||||||
|
Signals::Err(e)=>&e.to_string(),
|
||||||
|
|
||||||
};
|
};
|
||||||
write!(f, "[{}] {}", self.name, message)
|
write!(f, "[{}] {}", self.name, message)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -559,7 +559,7 @@ fn unpad(data: &[u8]) -> Vec<u8> {
|
|||||||
data[..data.len() - data[data.len() - 1] as usize].to_vec()
|
data[..data.len() - data[data.len() - 1] as usize].to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug,PartialEq)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum NcmError {
|
pub enum NcmError {
|
||||||
NotNcmFile,
|
NotNcmFile,
|
||||||
|
|||||||
34
src/opendir.rs
Normal file
34
src/opendir.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use log::error;
|
||||||
|
use std::{path::PathBuf, process::Command};
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub fn opendir(dir: PathBuf) {
|
||||||
|
match Command::new("explorer")
|
||||||
|
.arg(&dir) // <- Specify the directory you'd like to open.
|
||||||
|
.spawn()
|
||||||
|
{
|
||||||
|
Err(_) => error!("无法打开输出文件夹:[{}]", dir.display()),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn opendir(dir: PathBuf) {
|
||||||
|
match Command::new("open")
|
||||||
|
.arg(&dir) // <- Specify the directory you'd like to open.
|
||||||
|
.spawn()
|
||||||
|
{
|
||||||
|
Err(_) => error!("无法打开输出文件夹:[{}]", dir.display()),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub fn opendir(dir: PathBuf) {
|
||||||
|
match Command::new("open")
|
||||||
|
.arg(&dir) // <- Specify the directory you'd like to open.
|
||||||
|
.spawn()
|
||||||
|
{
|
||||||
|
Err(_) => error!("无法打开输出文件夹:[{}]", dir.display()),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user