- 当未指定输入文件时使用-a参数仍然可以打开输出目录

- 更换终端彩色库以支持Windows默认不启用ANSI转义序列的终端
This commit is contained in:
lkhsss
2025-09-07 14:59:35 +08:00
parent f35deaef80
commit 122d4e251d
9 changed files with 356 additions and 107 deletions

View File

@ -14,6 +14,7 @@ pub enum AppError {
FileDataError,
SaveError,
SystemTimeError,
CannotCreateDir,
}
impl std::error::Error for AppError {}
@ -34,8 +35,8 @@ impl std::fmt::Display for AppError {
Self::FileDataError => "处理文件数据时出错",
Self::SaveError => "保存文件出错",
Self::SystemTimeError => "获取时间戳失败",
// _ => "未知错误",
Self::CannotCreateDir => "无法创建父级目录", // _ => "未知错误",
};
write!(f, "{}", result)
}
}
}

View File

@ -1,5 +1,4 @@
use colored::Color::{Cyan, Green, Red, Yellow,Magenta};
use colored::Colorize;
use crossterm::style::{Color, Stylize}; //防止windows终端乱码
use indicatif::MultiProgress;
use log::{Log, Metadata, Record, SetLoggerError};
use std::sync::Arc;
@ -16,6 +15,15 @@ impl Log for MultiProgressLogger {
fn log(&self, record: &Record) {
if self.enabled(record.metadata()) {
#[cfg(target_os = "windows")]
let level = match record.level() {
log::Level::Error => ("Error").with(Color::Red),
log::Level::Warn => ("Warn").with(Color::Yellow),
log::Level::Info => ("Info").with(Color::Green),
log::Level::Debug => ("Debug").with(Color::Magenta),
log::Level::Trace => ("Trace").with(Color::Cyan),
};
#[cfg(not(target_os = "windows"))]
let level = match record.level() {
log::Level::Error => ("Error").color(Red),
log::Level::Warn => ("Warn").color(Yellow),
@ -23,6 +31,7 @@ impl Log for MultiProgressLogger {
log::Level::Debug => ("Debug").color(Magenta),
log::Level::Trace => ("Trace").color(Cyan),
};
let message = format!(
"[{}][{}] {}",
chrono::Local::now().format("%H:%M:%S"),
@ -42,6 +51,6 @@ pub fn init_logger() -> Result<(), SetLoggerError> {
mp: crate::MP.clone(),
};
log::set_boxed_logger(Box::new(logger))?;
Ok(())
}

View File

@ -1,6 +1,6 @@
use ::clap::Parser;
use colored::{Color, Colorize};
use crossbeam_channel::{bounded, Sender};
use crossterm::style::{Color, Stylize}; //防止windows终端乱码
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use lazy_static::lazy_static;
use log::{error, info, warn, LevelFilter};
@ -23,16 +23,7 @@ use apperror::AppError;
use ncmdump::Ncmfile;
use time::TimeCompare;
fn main() -> Result<(), AppError> {
let timer = match TimeCompare::new() {
Ok(t) => t,
Err(e) => {
error!("无法初始化时间戳系统。{}", e);
exit(1)
}
};
// 初始化日志系统
match logger::init_logger() {
Ok(_) => (),
@ -41,11 +32,22 @@ fn main() -> Result<(), AppError> {
}
};
let timer = match TimeCompare::new() {
Ok(t) => t,
Err(e) => {
error!("无法初始化时间戳系统。{}", e);
exit(1)
}
};
let cli = clap::Cli::parse();
//设置彩色输出
let if_colorful = !cli.nocolor;
colored::control::set_override(if_colorful);
// let if_colorful = !cli.nocolor;
// colored::control::set_override(if_colorful);
// crossterm::terminal::enable_raw_mode()
//FIXME 更改颜色库
//TODO控制颜色输出,更改为使用环境变量
//获取cpu核心数
let cpus = num_cpus::get();
@ -58,7 +60,7 @@ fn main() -> Result<(), AppError> {
1
}
}
None => cpus,//默认使用cpu核心数作为线程数
None => cpus, //默认使用cpu核心数作为线程数
};
//输入目录
let input = cli.input;
@ -67,7 +69,7 @@ fn main() -> Result<(), AppError> {
// 强制覆盖
let forcesave = cli.forcesave;
if forcesave {
warn!("文件{}已开启!", "强制覆盖".bright_red())
warn!("文件{}已开启!", "强制覆盖".with(Color::Red))
}
let level = match cli.debug {
0 | 3 => LevelFilter::Info,
@ -77,7 +79,7 @@ fn main() -> Result<(), AppError> {
5 => LevelFilter::Trace,
_ => LevelFilter::Off,
};
info!("日志等级:{}", level.to_string());
info!("日志等级:{}", level);
log::set_max_level(level);
let undumpfile = pathparse::pathparse(input); // 该列表将存入文件的路径
@ -88,16 +90,22 @@ fn main() -> Result<(), AppError> {
let mut failure_count = 0; //发生错误的
if taskcount == 0 {
error!("没有找到有效文件。使用-i参数输入需要解密的文件或文件夹。");
if cli.autoopen {
opendir::autoopen(cli.autoopen, outputdir);
} else {
error!("没有找到有效文件。使用-i参数输入需要解密的文件或文件夹。使用-a参数自动打开输出文件夹。");
}
exit(2);
};
// 创建完整的父目录
if std::fs::create_dir_all(&outputdir).is_err() {
return Err(AppError::CannotCreateDir);
}
// 初始化线程池
let pool = threadpool::Pool::new(max_workers);
info!(
"将启用{}线程",
max_workers.to_string().color(Color::BrightGreen)
);
info!("将启用{}线程", max_workers.to_string().with(Color::Green));
// 初始化通讯
// let (tx, rx) = mpsc::channel();
let (tx, rx) = bounded(taskcount * 6);
@ -145,7 +153,7 @@ fn main() -> Result<(), AppError> {
Signals::Err(_) => failure_count += 1,
_ => (),
}
if (success_count+ignore_count+failure_count) < taskcount {
if (success_count + ignore_count + failure_count) < taskcount {
progressbar.inc(1);
// messages.log(); //发送log
} else {
@ -164,19 +172,14 @@ fn main() -> Result<(), AppError> {
};
info!(
"成功解密{}个文件,跳过{}个文件,{}个文件解密失败,{}",
success_count.to_string().bright_green(),
ignore_count.to_string().purple(),
failure_count.to_string().bright_red(),
success_count.to_string().with(Color::Green),
ignore_count.to_string().with(Color::Magenta),
failure_count.to_string().with(Color::Red),
showtime()
);
// 自动打开输出文件夹
if cli.autoopen {
info!("自动打开文件夹:[{}]", outputdir.cyan());
opendir::opendir(outputdir.into());
} else {
info!("输出文件夹:[{}]", outputdir.cyan());
};
opendir::autoopen(cli.autoopen, outputdir);
Ok(())
}

View File

@ -1,5 +1,3 @@
use crate::{messager, AppError};
use std::fmt::Debug;
// use

View File

@ -5,13 +5,13 @@ use aes::cipher::{generic_array::GenericArray, BlockDecrypt, KeyInit};
use aes::Aes128;
use audiotags::{MimeType, Picture, Tag};
use base64::{self, Engine};
use colored::*;
use crossterm::style::{Color, Stylize}; //防止windows终端乱码
use log::{debug, info, trace};
use messager::Signals;
use serde_derive::{Deserialize, Serialize};
use serde_json::{self, Value};
use std::fmt::Debug;
use std::fs::{self, File};
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use std::str::from_utf8;
@ -132,7 +132,7 @@ impl Ncmfile {
let mut buf: Vec<u8> = vec![0; (self.size - self.position) as usize];
let _ = reader.read_exact(&mut buf);
self.position += length;
buf[..].to_vec()
buf[..].to_vec()
}
} else {
let mut reader = BufReader::new(&self.file);
@ -154,7 +154,7 @@ impl Ncmfile {
}
///按字节进行0x64异或。
fn parse_key(key: &mut [u8]) -> &[u8] {
for item in &mut *key {
for item in &mut *key {
*item ^= 0x64;
}
key
@ -186,6 +186,15 @@ impl Ncmfile {
fn unpad(data: &[u8]) -> Vec<u8> {
data[..data.len() - data[data.len() - 1] as usize].to_vec()
}
fn get_filename(&self) -> &str {
&self.filename
}
#[allow(dead_code)]
fn get_fullfilename(&self) -> &str {
&self.fullfilename
}
}
impl Ncmfile {
@ -273,23 +282,25 @@ impl Ncmfile {
//处理文件路径
trace!("拼接文件路径");
let path = {
let filename = format!(
let output_filename = &format!(
"{}.{}",
self.filename,
self.get_filename(),
meta_data
.get("format")
.ok_or(AppError::CannotReadMetaInfo)?
.as_str()
.ok_or(AppError::CannotReadMetaInfo)?
);
)[..];
// let filename = standardize_filename(filename);
debug!("文件名:{}", filename.yellow());
// let output_filename = standardize_filename(output_filename);
debug!("文件名:{}", output_filename.with(Color::Yellow));
//已在程序开头创建,无需浪费性能
//链级创建输出目录
if fs::create_dir_all(outputdir).is_err() {
return Err(AppError::FileWriteError);
}
outputdir.join(filename)
// if fs::create_dir_all(outputdir).is_err() {
// return Err(AppError::FileWriteError);
// }
outputdir.join(output_filename)
};
debug!("文件路径: {:?}", path);
@ -392,13 +403,13 @@ impl Ncmfile {
info!(
"[{}] 文件已保存到: {}",
self.filename.yellow(),
path.to_str().ok_or(AppError::SaveError)?.bright_cyan()
self.get_filename().with(Color::Yellow),
path.to_str().ok_or(AppError::SaveError)?.with(Color::Cyan)
);
info!(
"[{}]{}",
self.fullfilename.yellow(),
"解密成功".bright_green()
self.get_filename().with(Color::Yellow),
"解密成功".with(Color::Green)
);
let _ = messager.send(Signals::End);
Ok(())

View File

@ -1,11 +1,13 @@
use log::error;
use std::{path::PathBuf, process::Command};
use crossterm::style::{Color, Stylize};
use log::{error, info};
use std::{path::PathBuf, process::Command}; //防止windows终端乱码
#[cfg(target_os = "windows")]
pub fn opendir(dir: PathBuf) {
if Command::new("explorer")
.arg(&dir) // <- Specify the directory you'd like to open.
.spawn().is_err()
.spawn()
.is_err()
{
error!("无法打开输出文件夹:[{}]", dir.display())
}
@ -15,7 +17,8 @@ pub fn opendir(dir: PathBuf) {
pub fn opendir(dir: PathBuf) {
if Command::new("open")
.arg(&dir) // <- Specify the directory you'd like to open.
.spawn().is_err()
.spawn()
.is_err()
{
error!("无法打开输出文件夹:[{}]", dir.display())
}
@ -24,8 +27,20 @@ pub fn opendir(dir: PathBuf) {
pub fn opendir(dir: PathBuf) {
if Command::new("open")
.arg(&dir) // <- Specify the directory you'd like to open.
.spawn().is_err()
.spawn()
.is_err()
{
error!("无法打开输出文件夹:[{}]", dir.display())
}
}
// 自动打开输出文件夹的跨平台函数
pub fn autoopen(if_auto_open: bool, path: String) {
let styled_path = (&path[..]).with(Color::Cyan);
if if_auto_open {
info!("自动打开文件夹:[{}]", styled_path);
opendir(path.into());
} else {
info!("输出文件夹:[{}]", styled_path);
};
}