1.添加多线程支持
2.优化代码结构 3.升级依赖
This commit is contained in:
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
@ -1,5 +1,8 @@
|
||||
name: build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch: {}
|
||||
jobs:
|
||||
build:
|
||||
@ -10,7 +13,7 @@ jobs:
|
||||
build: [linux, macos, windows]
|
||||
include:
|
||||
- build: linux
|
||||
os: ubuntu-18.04
|
||||
os: ubuntu-latest
|
||||
rust: stable
|
||||
target: x86_64-unknown-linux-musl
|
||||
archive-name: ncmmiao-linux.tar.gz
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
/target
|
||||
src/main.rs.old
|
||||
src/lib.rs.bak
|
||||
/testfile
|
||||
/output
|
||||
988
Cargo.lock
generated
988
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -7,11 +7,12 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
aes = "0.8.3"
|
||||
base64 = "0.21.7"
|
||||
base64 = "0.22.*"
|
||||
clap = { version = "4.5.9", features = ["derive"] }
|
||||
colored = "2.1.0"
|
||||
env_logger = "0.11.1"
|
||||
hex = "0.4.3"
|
||||
image = "0.24.8"
|
||||
image = "0.25.*"
|
||||
log = "0.4.20"
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
serde_derive = "1.0.195"
|
||||
|
||||
10
README.md
10
README.md
@ -15,7 +15,7 @@ cargo build -r
|
||||
## 使用
|
||||
支持单一文件,多文件夹递归批量解密。
|
||||
```
|
||||
cargo build -r <文件或文件夹路径1> <文件或文件夹路径2> <文件或文件夹路径3> ...
|
||||
cargo run -r <文件或文件夹路径1> <文件或文件夹路径2> <文件或文件夹路径3> ...
|
||||
```
|
||||
> 注意!如果没有指定任何文件夹或者文件,那么程序将自动读取工作目录下的CloudMusic和input文件夹下的文件。如果都没有将自动退出。
|
||||
|
||||
@ -24,7 +24,7 @@ cargo build -r <文件或文件夹路径1> <文件或文件夹路径2> <文件
|
||||
---
|
||||
|
||||
# TODO :construction:
|
||||
- [ ] 多线程支持
|
||||
- [x] 多线程支持
|
||||
- [ ] 解密进度条
|
||||
- [ ] 命令行解析
|
||||
- [ ] 自定义输出文件夹
|
||||
@ -50,6 +50,12 @@ cargo build -r <文件或文件夹路径1> <文件或文件夹路径2> <文件
|
||||
## [1.1.3] - 2024-2-6
|
||||
### Fixed :bug:
|
||||
- 修正了部分音乐名称中含有不合法字符时创建文件失败的问题
|
||||
## [1.1.4] - 2024-7-14
|
||||
### Features :sparkles:
|
||||
- 增加多线程支持!
|
||||
> 目前固定4线程,还没写命令行参数。可以源代码修改线程数
|
||||
### Fixed
|
||||
- 优化代码结构
|
||||
|
||||
---
|
||||
|
||||
|
||||
27
src/main.rs
27
src/main.rs
@ -1,19 +1,26 @@
|
||||
use env_logger::Builder;
|
||||
use hex::decode;
|
||||
use log::{error, info, warn};
|
||||
use ncmmiao::{dump, Key, Ncmfile};
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
// use std::time::SystemTime;
|
||||
use walkdir::WalkDir;
|
||||
#[warn(unreachable_code)]
|
||||
use walkdir::WalkDir; //遍历目录
|
||||
|
||||
mod ncmdump;
|
||||
mod threadpool;
|
||||
use ncmdump::{dump, Key, Ncmfile};
|
||||
mod test;
|
||||
|
||||
fn main() {
|
||||
// 最大线程数
|
||||
let max_workers = 4;
|
||||
|
||||
let mut builder = Builder::new();
|
||||
builder.filter(None, log::LevelFilter::Info);
|
||||
builder.init(); //初始化logger
|
||||
|
||||
let keys = Key {
|
||||
let keys: Key = Key {
|
||||
core: decode("687A4852416D736F356B496E62617857").unwrap(),
|
||||
meta: decode("2331346C6A6B5F215C5D2630553C2728").unwrap(),
|
||||
};
|
||||
@ -21,6 +28,7 @@ fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let args = if args.len() == 1 {
|
||||
warn!("未指定文件夹,将使用默认文件夹。");
|
||||
|
||||
let mut args_temp = Vec::new();
|
||||
if Path::new("CloudMusic").exists() {
|
||||
warn!("CloudMusic文件夹存在,将自动使用。");
|
||||
@ -39,7 +47,6 @@ fn main() {
|
||||
} else {
|
||||
args[1..].to_vec()
|
||||
};
|
||||
// let args = &args[1..];
|
||||
|
||||
let mut undumpfile = Vec::new(); // 该列表将存入文件的路径
|
||||
|
||||
@ -78,8 +85,14 @@ fn main() {
|
||||
// let filepaths = undumpfile;
|
||||
// let count = undumpfile.len();
|
||||
// let mut time = 0usize;
|
||||
|
||||
// 初始化线程池
|
||||
let pool = threadpool::Pool::new(max_workers);
|
||||
for filepath in undumpfile {
|
||||
let mut ncmfile = Ncmfile::new(&filepath).unwrap();
|
||||
dump(&mut ncmfile, &keys, Path::new("output")).unwrap();
|
||||
let tkey = keys.clone();
|
||||
pool.execute(move || {
|
||||
let mut ncmfile = Ncmfile::new(filepath.as_str()).unwrap();
|
||||
dump(&mut ncmfile, &tkey, Path::new("output")).unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,9 +16,9 @@ use std::str::from_utf8;
|
||||
pub struct Ncmfile {
|
||||
/// 文件对象
|
||||
pub file: File,
|
||||
/// 只是名称,不带后缀
|
||||
/// 歌曲名称,不带文件后缀
|
||||
pub name: String,
|
||||
/// 带后缀名
|
||||
/// 文件名称,带后缀名
|
||||
pub filename: String,
|
||||
/// 文件大小
|
||||
pub size: u64,
|
||||
@ -182,6 +182,7 @@ struct Metadata {
|
||||
}
|
||||
|
||||
// 存储各种密钥的结构体
|
||||
#[derive(Clone)]
|
||||
pub struct Key {
|
||||
pub core: Vec<u8>,
|
||||
pub meta: Vec<u8>,
|
||||
@ -257,7 +258,7 @@ pub fn dump(ncmfile: &mut Ncmfile, keys: &Key, outputdir: &Path) -> Result<(), M
|
||||
};
|
||||
|
||||
// 跳过4个字节的校验码
|
||||
trace!("跳过4个字节的校验码");
|
||||
trace!("读取校验码");
|
||||
let crc32 = u32::from_le_bytes(ncmfile.seekread(4).unwrap().try_into().unwrap()) as u64;
|
||||
|
||||
// 跳过5个字节
|
||||
@ -278,7 +279,7 @@ pub fn dump(ncmfile: &mut Ncmfile, keys: &Key, outputdir: &Path) -> Result<(), M
|
||||
// trace!("保存图片");
|
||||
// let mut file = File::create(format!("TEST.jpg",)).unwrap();
|
||||
// file.write_all(&image_data).unwrap();
|
||||
|
||||
trace!("组成密码盒");
|
||||
let key_box = {
|
||||
let key_length = key_data.len();
|
||||
let key_data = Vec::from(key_data);
|
||||
@ -430,6 +431,7 @@ pub fn dump(ncmfile: &mut Ncmfile, keys: &Key, outputdir: &Path) -> Result<(), M
|
||||
}
|
||||
|
||||
// fn read_meta(file: &mut File, meta_length: u32) -> Result<Vec<u8>, Error> {}
|
||||
|
||||
fn convert_to_generic_arrays(input: &[u8]) -> Vec<GenericArray<u8, U16>> {
|
||||
// 确保输入的长度是16的倍数
|
||||
assert!(
|
||||
@ -470,6 +472,8 @@ fn aes128(key: &[u8], blocks: &[u8]) -> String {
|
||||
|
||||
x.to_string()
|
||||
}
|
||||
|
||||
/// ## AES128解密
|
||||
fn aes128_to_slice(key: &[u8], blocks: &[u8]) -> Vec<u8> {
|
||||
trace!("进行AES128解密");
|
||||
let key = GenericArray::from_slice(key);
|
||||
@ -481,6 +485,8 @@ fn aes128_to_slice(key: &[u8], blocks: &[u8]) -> Vec<u8> {
|
||||
|
||||
// 开始解密
|
||||
cipher.decrypt_blocks(&mut blocks);
|
||||
|
||||
//取出解密后的值
|
||||
let mut x: Vec<u8> = Vec::new();
|
||||
for block in blocks.iter() {
|
||||
for i in block {
|
||||
@ -496,8 +502,9 @@ fn aes128_to_slice(key: &[u8], blocks: &[u8]) -> Vec<u8> {
|
||||
/// - \ / * ? " : < > |
|
||||
/// - _ _ * ? " : ⟨ ⟩ _
|
||||
fn standardize_filename(old_filename: String) -> String {
|
||||
trace!("格式化文件名");
|
||||
let mut new_filename = String::from(old_filename);
|
||||
debug!("规范文件名:{}", new_filename);
|
||||
// debug!("规范文件名:{}", new_filename);
|
||||
let standard = ["\\", "/", "*", "?", "\"", ":", "<", ">", "|"];
|
||||
let resolution = ["_", "_", "*", "?", """, ":", "⟨", "⟩", "_"];
|
||||
for i in 0..standard.len() {
|
||||
14
src/test.rs
Normal file
14
src/test.rs
Normal file
@ -0,0 +1,14 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::*;
|
||||
|
||||
#[test]
|
||||
#[ignore = "测验成功"]
|
||||
fn it_works() {
|
||||
let p = threadpool::Pool::new(4);
|
||||
p.execute(|| println!("do new job1"));
|
||||
p.execute(|| println!("do new job2"));
|
||||
p.execute(|| println!("do new job3"));
|
||||
p.execute(|| println!("do new job4"));
|
||||
}
|
||||
}
|
||||
90
src/threadpool.rs
Normal file
90
src/threadpool.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
use std::thread::{self, JoinHandle};
|
||||
use log::{info,debug};
|
||||
use serde::de;
|
||||
|
||||
type Job = Box<dyn FnOnce() + 'static + Send>;
|
||||
enum Message {
|
||||
ByeBye,
|
||||
NewJob(Job),
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
_id: usize,
|
||||
t: Option<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker {
|
||||
let t = thread::spawn(move || loop {
|
||||
let message = receiver.lock().unwrap().recv().unwrap();
|
||||
match message {
|
||||
Message::NewJob(job) => {
|
||||
debug!("线程[{}]获得任务", id);
|
||||
job();
|
||||
}
|
||||
Message::ByeBye => {
|
||||
debug!("线程[{}]结束任务", id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Worker {
|
||||
_id: id,
|
||||
t: Some(t),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Pool {
|
||||
workers: Vec<Worker>,
|
||||
max_workers: usize,
|
||||
sender: mpsc::Sender<Message>,
|
||||
}
|
||||
|
||||
impl Pool {
|
||||
pub fn new(max_workers: usize) -> Pool {
|
||||
if max_workers == 0 {
|
||||
panic!("最大线程数不能小于零!")
|
||||
}else {
|
||||
debug!("将开启{}线程",max_workers);
|
||||
}
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
let mut workers = Vec::with_capacity(max_workers);
|
||||
let receiver = Arc::new(Mutex::new(rx));
|
||||
for i in 0..max_workers {
|
||||
workers.push(Worker::new(i, Arc::clone(&receiver)));
|
||||
}
|
||||
|
||||
Pool {
|
||||
workers: workers,
|
||||
max_workers: max_workers,
|
||||
sender: tx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute<F>(&self, f: F)
|
||||
where
|
||||
F: FnOnce() + 'static + Send,
|
||||
{
|
||||
let job = Message::NewJob(Box::new(f));
|
||||
self.sender.send(job).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Pool {
|
||||
fn drop(&mut self) {
|
||||
for _ in 0..self.max_workers {
|
||||
self.sender.send(Message::ByeBye).unwrap();
|
||||
}
|
||||
for w in self.workers.iter_mut() {
|
||||
if let Some(t) = w.t.take() {
|
||||
t.join().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user