1.添加多线程支持

2.优化代码结构
3.升级依赖
This commit is contained in:
Lkhsss
2024-08-17 22:05:34 +08:00
parent 7245260604
commit 4af4ce7405
9 changed files with 996 additions and 164 deletions

View File

@ -1,5 +1,8 @@
name: build name: build
on: on:
push:
branches:
- main
workflow_dispatch: {} workflow_dispatch: {}
jobs: jobs:
build: build:
@ -10,7 +13,7 @@ jobs:
build: [linux, macos, windows] build: [linux, macos, windows]
include: include:
- build: linux - build: linux
os: ubuntu-18.04 os: ubuntu-latest
rust: stable rust: stable
target: x86_64-unknown-linux-musl target: x86_64-unknown-linux-musl
archive-name: ncmmiao-linux.tar.gz archive-name: ncmmiao-linux.tar.gz

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
/target /target
src/main.rs.old src/main.rs.old
src/lib.rs.bak src/lib.rs.bak
/testfile
/output

988
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,11 +7,12 @@ edition = "2021"
[dependencies] [dependencies]
aes = "0.8.3" aes = "0.8.3"
base64 = "0.21.7" base64 = "0.22.*"
clap = { version = "4.5.9", features = ["derive"] }
colored = "2.1.0" colored = "2.1.0"
env_logger = "0.11.1" env_logger = "0.11.1"
hex = "0.4.3" hex = "0.4.3"
image = "0.24.8" image = "0.25.*"
log = "0.4.20" log = "0.4.20"
serde = { version = "1.0.195", features = ["derive"] } serde = { version = "1.0.195", features = ["derive"] }
serde_derive = "1.0.195" serde_derive = "1.0.195"

View File

@ -15,7 +15,7 @@ cargo build -r
## 使用 ## 使用
支持单一文件,多文件夹递归批量解密。 支持单一文件,多文件夹递归批量解密。
``` ```
cargo build -r <文件或文件夹路径1> <文件或文件夹路径2> <文件或文件夹路径3> ... cargo run -r <文件或文件夹路径1> <文件或文件夹路径2> <文件或文件夹路径3> ...
``` ```
> 注意如果没有指定任何文件夹或者文件那么程序将自动读取工作目录下的CloudMusic和input文件夹下的文件。如果都没有将自动退出。 > 注意如果没有指定任何文件夹或者文件那么程序将自动读取工作目录下的CloudMusic和input文件夹下的文件。如果都没有将自动退出。
@ -24,7 +24,7 @@ cargo build -r <文件或文件夹路径1> <文件或文件夹路径2> <文件
--- ---
# TODO :construction: # TODO :construction:
- [ ] 多线程支持 - [x] 多线程支持
- [ ] 解密进度条 - [ ] 解密进度条
- [ ] 命令行解析 - [ ] 命令行解析
- [ ] 自定义输出文件夹 - [ ] 自定义输出文件夹
@ -50,6 +50,12 @@ cargo build -r <文件或文件夹路径1> <文件或文件夹路径2> <文件
## [1.1.3] - 2024-2-6 ## [1.1.3] - 2024-2-6
### Fixed :bug: ### Fixed :bug:
- 修正了部分音乐名称中含有不合法字符时创建文件失败的问题 - 修正了部分音乐名称中含有不合法字符时创建文件失败的问题
## [1.1.4] - 2024-7-14
### Features :sparkles:
- 增加多线程支持!
> 目前固定4线程还没写命令行参数。可以源代码修改线程数
### Fixed
- 优化代码结构
--- ---

View File

@ -1,19 +1,26 @@
use env_logger::Builder; use env_logger::Builder;
use hex::decode; use hex::decode;
use log::{error, info, warn}; use log::{error, info, warn};
use ncmmiao::{dump, Key, Ncmfile};
use std::env; use std::env;
use std::path::Path; use std::path::Path;
// use std::time::SystemTime; // use std::time::SystemTime;
use walkdir::WalkDir; use walkdir::WalkDir; //遍历目录
#[warn(unreachable_code)]
mod ncmdump;
mod threadpool;
use ncmdump::{dump, Key, Ncmfile};
mod test;
fn main() { fn main() {
// 最大线程数
let max_workers = 4;
let mut builder = Builder::new(); let mut builder = Builder::new();
builder.filter(None, log::LevelFilter::Info); builder.filter(None, log::LevelFilter::Info);
builder.init(); //初始化logger builder.init(); //初始化logger
let keys = Key { let keys: Key = Key {
core: decode("687A4852416D736F356B496E62617857").unwrap(), core: decode("687A4852416D736F356B496E62617857").unwrap(),
meta: decode("2331346C6A6B5F215C5D2630553C2728").unwrap(), meta: decode("2331346C6A6B5F215C5D2630553C2728").unwrap(),
}; };
@ -21,6 +28,7 @@ fn main() {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let args = if args.len() == 1 { let args = if args.len() == 1 {
warn!("未指定文件夹,将使用默认文件夹。"); warn!("未指定文件夹,将使用默认文件夹。");
let mut args_temp = Vec::new(); let mut args_temp = Vec::new();
if Path::new("CloudMusic").exists() { if Path::new("CloudMusic").exists() {
warn!("CloudMusic文件夹存在将自动使用。"); warn!("CloudMusic文件夹存在将自动使用。");
@ -39,7 +47,6 @@ fn main() {
} else { } else {
args[1..].to_vec() args[1..].to_vec()
}; };
// let args = &args[1..];
let mut undumpfile = Vec::new(); // 该列表将存入文件的路径 let mut undumpfile = Vec::new(); // 该列表将存入文件的路径
@ -78,8 +85,14 @@ fn main() {
// let filepaths = undumpfile; // let filepaths = undumpfile;
// let count = undumpfile.len(); // let count = undumpfile.len();
// let mut time = 0usize; // let mut time = 0usize;
// 初始化线程池
let pool = threadpool::Pool::new(max_workers);
for filepath in undumpfile { for filepath in undumpfile {
let mut ncmfile = Ncmfile::new(&filepath).unwrap(); let tkey = keys.clone();
dump(&mut ncmfile, &keys, Path::new("output")).unwrap(); pool.execute(move || {
let mut ncmfile = Ncmfile::new(filepath.as_str()).unwrap();
dump(&mut ncmfile, &tkey, Path::new("output")).unwrap();
});
} }
} }

View File

@ -16,9 +16,9 @@ use std::str::from_utf8;
pub struct Ncmfile { pub struct Ncmfile {
/// 文件对象 /// 文件对象
pub file: File, pub file: File,
/// 只是名称,不带后缀 /// 歌曲名称,不带文件后缀
pub name: String, pub name: String,
/// 带后缀名 /// 文件名称,带后缀名
pub filename: String, pub filename: String,
/// 文件大小 /// 文件大小
pub size: u64, pub size: u64,
@ -182,6 +182,7 @@ struct Metadata {
} }
// 存储各种密钥的结构体 // 存储各种密钥的结构体
#[derive(Clone)]
pub struct Key { pub struct Key {
pub core: Vec<u8>, pub core: Vec<u8>,
pub meta: Vec<u8>, pub meta: Vec<u8>,
@ -257,7 +258,7 @@ pub fn dump(ncmfile: &mut Ncmfile, keys: &Key, outputdir: &Path) -> Result<(), M
}; };
// 跳过4个字节的校验码 // 跳过4个字节的校验码
trace!("跳过4个字节的校验码"); trace!("读取校验码");
let crc32 = u32::from_le_bytes(ncmfile.seekread(4).unwrap().try_into().unwrap()) as u64; let crc32 = u32::from_le_bytes(ncmfile.seekread(4).unwrap().try_into().unwrap()) as u64;
// 跳过5个字节 // 跳过5个字节
@ -278,7 +279,7 @@ pub fn dump(ncmfile: &mut Ncmfile, keys: &Key, outputdir: &Path) -> Result<(), M
// trace!("保存图片"); // trace!("保存图片");
// let mut file = File::create(format!("TEST.jpg",)).unwrap(); // let mut file = File::create(format!("TEST.jpg",)).unwrap();
// file.write_all(&image_data).unwrap(); // file.write_all(&image_data).unwrap();
trace!("组成密码盒");
let key_box = { let key_box = {
let key_length = key_data.len(); let key_length = key_data.len();
let key_data = Vec::from(key_data); 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 read_meta(file: &mut File, meta_length: u32) -> Result<Vec<u8>, Error> {}
fn convert_to_generic_arrays(input: &[u8]) -> Vec<GenericArray<u8, U16>> { fn convert_to_generic_arrays(input: &[u8]) -> Vec<GenericArray<u8, U16>> {
// 确保输入的长度是16的倍数 // 确保输入的长度是16的倍数
assert!( assert!(
@ -470,6 +472,8 @@ fn aes128(key: &[u8], blocks: &[u8]) -> String {
x.to_string() x.to_string()
} }
/// ## AES128解密
fn aes128_to_slice(key: &[u8], blocks: &[u8]) -> Vec<u8> { fn aes128_to_slice(key: &[u8], blocks: &[u8]) -> Vec<u8> {
trace!("进行AES128解密"); trace!("进行AES128解密");
let key = GenericArray::from_slice(key); 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); cipher.decrypt_blocks(&mut blocks);
//取出解密后的值
let mut x: Vec<u8> = Vec::new(); let mut x: Vec<u8> = Vec::new();
for block in blocks.iter() { for block in blocks.iter() {
for i in block { 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 { fn standardize_filename(old_filename: String) -> String {
trace!("格式化文件名");
let mut new_filename = String::from(old_filename); let mut new_filename = String::from(old_filename);
debug!("规范文件名:{}", new_filename); // debug!("规范文件名:{}", new_filename);
let standard = ["\\", "/", "*", "?", "\"", ":", "<", ">", "|"]; let standard = ["\\", "/", "*", "?", "\"", ":", "<", ">", "|"];
let resolution = ["_", "_", "", "", "", "", "", "", "_"]; let resolution = ["_", "_", "", "", "", "", "", "", "_"];
for i in 0..standard.len() { for i in 0..standard.len() {

14
src/test.rs Normal file
View 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
View 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();
}
}
}
}