Compare commits

..
2 Commits
Author SHA1 Message Date
Bluemangoo 8d4b6e79f3 fix export path 2026-05-04 15:26:22 +08:00
Bluemangoo cc4e6f0615 fix temp create&remove 2026-04-29 21:15:44 +08:00
6 changed files with 44 additions and 70 deletions
+33 -59
View File
@@ -59,6 +59,17 @@ pub fn get_hex_index(input: &str) -> String {
format!("{:032x}", hash_val) format!("{:032x}", hash_val)
} }
pub fn empty_dir(base: PathBuf, name: String) -> PathBuf {
let mut dir = base.join(&name);
let mut cnt = 1;
while dir.exists() {
dir = base.join(format!("{}_{}", &name, cnt));
cnt += 1;
}
std::fs::create_dir_all(&dir).unwrap();
dir
}
pub async fn extract_unity_asset_bundle( pub async fn extract_unity_asset_bundle(
app_config: &AppConfig, app_config: &AppConfig,
sync_context: &SyncContext, sync_context: &SyncContext,
@@ -69,11 +80,13 @@ pub async fn extract_unity_asset_bundle(
category: &str, category: &str,
) -> Result<(PathBuf, bool), ExportPipelineError> { ) -> Result<(PathBuf, bool), ExportPipelineError> {
let hash = get_hex_index(export_path); let hash = get_hex_index(export_path);
let output_dir = std::env::temp_dir() let output_dir = empty_dir(
std::env::temp_dir()
.join("sekai-updater") .join("sekai-updater")
.join("extract") .join("extract")
.join(&sync_context.region) .join(&sync_context.region),
.join(hash); hash,
);
let Some(asset_studio_cli_path) = app_config.tools.asset_studio_cli_path.as_deref() else { let Some(asset_studio_cli_path) = app_config.tools.asset_studio_cli_path.as_deref() else {
return Ok((asset_bundle_file.parent().unwrap().to_path_buf(), true)); return Ok((asset_bundle_file.parent().unwrap().to_path_buf(), true));
}; };
@@ -166,7 +179,6 @@ pub async fn post_process_exported_files(
handle_usm_files( handle_usm_files(
export_path, export_path,
sync_context, sync_context,
region,
&app_config.tools.ffmpeg_path, &app_config.tools.ffmpeg_path,
&app_config.execution.retry, &app_config.execution.retry,
) )
@@ -189,7 +201,6 @@ pub async fn post_process_exported_files(
async fn handle_usm_files( async fn handle_usm_files(
export_path: &Path, export_path: &Path,
sync_context: &SyncContext, sync_context: &SyncContext,
region: &RegionConfig,
ffmpeg_path: &str, ffmpeg_path: &str,
retry: &crate::core::config::RetryConfig, retry: &crate::core::config::RetryConfig,
) -> Result<Vec<PathBuf>, ExportPipelineError> { ) -> Result<Vec<PathBuf>, ExportPipelineError> {
@@ -198,28 +209,17 @@ async fn handle_usm_files(
return Ok(Vec::new()); return Ok(Vec::new());
} }
let usm_input = if usm_files.len() == 1 { let mut out: Vec<PathBuf> = vec![];
usm_files[0].clone()
} else {
merge_usm_files(export_path, &usm_files)?
};
process_usm_file( for f in usm_files {
&usm_input, out.append(&mut process_usm_file(&f, sync_context, ffmpeg_path, retry).await?);
export_path, }
sync_context, Ok(out)
region,
ffmpeg_path,
retry,
)
.await
} }
async fn process_usm_file( async fn process_usm_file(
usm_file: &Path, usm_file: &Path,
export_path: &Path,
sync_context: &SyncContext, sync_context: &SyncContext,
_: &RegionConfig,
ffmpeg_path: &str, ffmpeg_path: &str,
retry: &crate::core::config::RetryConfig, retry: &crate::core::config::RetryConfig,
) -> Result<Vec<PathBuf>, ExportPipelineError> { ) -> Result<Vec<PathBuf>, ExportPipelineError> {
@@ -235,7 +235,10 @@ async fn process_usm_file(
if sync_context.export.video.convert_to_mp4 if sync_context.export.video.convert_to_mp4
&& sync_context.export.video.direct_usm_to_mp4_with_ffmpeg && sync_context.export.video.direct_usm_to_mp4_with_ffmpeg
{ {
let mp4 = export_path.join(format!("{output_name}.mp4")); let mp4 = usm_file
.parent()
.unwrap()
.join(format!("{output_name}.mp4"));
convert_usm_to_mp4(usm_file, &mp4, ffmpeg_path, retry).await?; convert_usm_to_mp4(usm_file, &mp4, ffmpeg_path, retry).await?;
remove_file_if_exists(usm_file)?; remove_file_if_exists(usm_file)?;
return Ok(vec![mp4]); return Ok(vec![mp4]);
@@ -247,7 +250,7 @@ async fn process_usm_file(
.and_then(|metadata| metadata.video_frame_rate()) .and_then(|metadata| metadata.video_frame_rate())
.filter(|(_, denominator)| *denominator > 0) .filter(|(_, denominator)| *denominator > 0)
.map(FrameRate::from_tuple); .map(FrameRate::from_tuple);
let extracted = codec::export_usm(usm_file, export_path)?; let extracted = codec::export_usm(usm_file, usm_file.parent().unwrap())?;
let mut generated = extracted.clone(); let mut generated = extracted.clone();
if sync_context.export.video.convert_to_mp4 { if sync_context.export.video.convert_to_mp4 {
@@ -258,7 +261,10 @@ async fn process_usm_file(
.map(|ext| ext.eq_ignore_ascii_case("m2v")) .map(|ext| ext.eq_ignore_ascii_case("m2v"))
.unwrap_or(false) .unwrap_or(false)
{ {
let mp4 = export_path.join(format!("{output_name}.mp4")); let mp4 = usm_file
.parent()
.unwrap()
.join(format!("{output_name}.mp4"));
convert_m2v_to_mp4( convert_m2v_to_mp4(
&extracted_file, &extracted_file,
&mp4, &mp4,
@@ -314,7 +320,7 @@ async fn handle_acb_files(
fn process_acb_file( fn process_acb_file(
acb_file: &Path, acb_file: &Path,
output_dir: &Path, _output_dir: &Path,
sync_context: &SyncContext, sync_context: &SyncContext,
region: &RegionConfig, region: &RegionConfig,
ffmpeg_path: &str, ffmpeg_path: &str,
@@ -368,7 +374,7 @@ fn process_acb_file(
&retry, &retry,
) )
})?; })?;
let final_outputs = move_result_files(output_dir, &generated)?; let final_outputs = move_result_files(acb_file.parent().unwrap(), &generated)?;
remove_file_if_exists(acb_file)?; remove_file_if_exists(acb_file)?;
Ok(final_outputs) Ok(final_outputs)
@@ -416,7 +422,7 @@ fn process_hca_file(
generated.clear(); generated.clear();
} }
let final_outputs = move_result_files(output_dir, &generated)?; let final_outputs = move_result_files(hca_file.parent().unwrap(), &generated)?;
Ok(final_outputs) Ok(final_outputs)
} }
@@ -697,38 +703,6 @@ fn panic_message(panic: Box<dyn std::any::Any + Send>) -> String {
} }
} }
fn merge_usm_files(dir: &Path, usm_files: &[PathBuf]) -> Result<PathBuf, ExportPipelineError> {
let dir_name = dir
.file_name()
.and_then(|name| name.to_str())
.unwrap_or("merged");
let merged_file = dir.join(format!("{dir_name}.usm"));
let mut target =
std::fs::File::create(&merged_file).map_err(|source| ExportPipelineError::Io {
path: merged_file.clone(),
source,
})?;
for source_path in usm_files {
if *source_path == merged_file {
continue;
}
let mut source =
std::fs::File::open(source_path).map_err(|source| ExportPipelineError::Io {
path: source_path.clone(),
source,
})?;
std::io::copy(&mut source, &mut target).map_err(|source| ExportPipelineError::Io {
path: source_path.clone(),
source,
})?;
drop(source);
remove_file_if_exists(source_path)?;
}
Ok(merged_file)
}
pub fn find_files(dir: &Path) -> Result<Vec<PathBuf>, ExportPipelineError> { pub fn find_files(dir: &Path) -> Result<Vec<PathBuf>, ExportPipelineError> {
let mut files = Vec::new(); let mut files = Vec::new();
walk(dir, &mut |path| { walk(dir, &mut |path| {
+3 -3
View File
@@ -1,7 +1,7 @@
use crate::config::{ClientConfig, Profile}; use crate::config::{ClientConfig, Profile};
use crate::queue::SharedQueue; use crate::queue::SharedQueue;
use crate::signal::Signal; use crate::signal::Signal;
use crate::task::{AtomicCounters, AutoSaveManifest, post_run, run_main, run_side}; use crate::task::{AtomicCounters, AutoSaveManifest, pre_run, run_main, run_side};
use common::strings::REGION_NOT_FOUND; use common::strings::REGION_NOT_FOUND;
use common::updater::DownloadTask; use common::updater::DownloadTask;
use communicator::{ClientManager, Identity, TunnelEndpoint, TunnelListener, connect_tunnel}; use communicator::{ClientManager, Identity, TunnelEndpoint, TunnelListener, connect_tunnel};
@@ -233,7 +233,7 @@ async fn main() -> anyhow::Result<()> {
if cancel_token.is_cancelled() { if cancel_token.is_cancelled() {
return; return;
} }
let sync_id = post_run(client.clone(), profile.clone(), tasks.clone(), cnt.clone()).await; let sync_id = pre_run(client.clone(), profile.clone(), tasks.clone(), cnt.clone()).await;
let sig = signal.pick().await; let sig = signal.pick().await;
let result = match sync_id { let result = match sync_id {
Ok(Some(id)) => { Ok(Some(id)) => {
@@ -328,7 +328,7 @@ async fn main() -> anyhow::Result<()> {
if cancel_token.is_cancelled() { if cancel_token.is_cancelled() {
return; return;
} }
let sync_id = post_run(client.clone(), profile.clone(), tasks.clone(), cnt.clone()).await; let sync_id = pre_run(client.clone(), profile.clone(), tasks.clone(), cnt.clone()).await;
let sig = signal.pick().await; let sig = signal.pick().await;
let result = match sync_id { let result = match sync_id {
Ok(Some(id)) => { Ok(Some(id)) => {
+1 -1
View File
@@ -15,7 +15,7 @@ use tokio::sync::{RwLock, Semaphore};
use tokio::task::JoinSet; use tokio::task::JoinSet;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
pub async fn post_run( pub async fn pre_run(
client: Arc<ClientManager>, client: Arc<ClientManager>,
profile: Arc<(String, Arc<RwLock<Profile>>)>, profile: Arc<(String, Arc<RwLock<Profile>>)>,
queue: SharedQueue<DownloadTask>, queue: SharedQueue<DownloadTask>,
+3 -3
View File
@@ -82,7 +82,7 @@ profiles:
# 并发下载数(单个资源包的同时下载线程数) # 并发下载数(单个资源包的同时下载线程数)
concurrent: 50 concurrent: 50
# 精确匹配单文件包 # 展开单文件包
exact_single_file_bundle: true exact_single_file_bundle: true
# 导出基础路径 # 导出基础路径
@@ -93,7 +93,7 @@ profiles:
# 启动应用时必须下载的资源类型 # 启动应用时必须下载的资源类型
start_app: start_app:
- "thumbnail" # 缩略图 - "thumbnail" # 缩略图
- "stamp" # 邮票/表情 - "stamp" # 表情
# - "area" # 地图 # - "area" # 地图
# - "home" # 主页背景 # - "home" # 主页背景
@@ -158,7 +158,7 @@ profiles:
# 并发下载数 # 并发下载数
concurrent: 50 concurrent: 50
# 精确匹配单文件包 # 展开单文件包
exact_single_file_bundle: true exact_single_file_bundle: true
# 导出路径 # 导出路径
+1 -1
View File
@@ -108,7 +108,7 @@ concurrency:
# ACB 音频包解析的并发数 # ACB 音频包解析的并发数
acb: 8 acb: 8
# USM 视频解析的并发数 # USM 视频解析的并发数(好像没用到)
usm: 4 usm: 4
# HCA 音频解码的并发数(通常可设置较大值) # HCA 音频解码的并发数(通常可设置较大值)
+1 -1
View File
@@ -86,7 +86,7 @@ pub async fn download(
let _ = server_send_files(send_stream, &files).await; let _ = server_send_files(send_stream, &files).await;
} }
let _ = std::fs::remove_file(dir); let _ = std::fs::remove_dir_all(dir);
Ok(()) Ok(())
} }