mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 10:04:55 +08:00
feat(bat): 完善工作流调度与 dashboard RPC
补全资源拉取、解析、翻译、重打包和本地化发布命令,支持单次、限定次数与周期调度。移除 TUI 计划并通过 schedule.* RPC 暴露给 bat-api dashboard。 Closes #43
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
use super::*;
|
||||
|
||||
pub(super) fn run_parse_once(options: &CliOptions) -> anyhow::Result<()> {
|
||||
let (resource_root, release_id) = current_official_release(options)?;
|
||||
let parse_config =
|
||||
OfficialParseConfig::new(&resource_root, options.config.unzip_command.clone())
|
||||
.with_force(options.config.force);
|
||||
let parse_report = OfficialParseCacheService::new()
|
||||
.run(&parse_config)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
let queue_report =
|
||||
write_official_textunit_queues(&resource_root).map_err(anyhow::Error::msg)?;
|
||||
let data = serde_json::json!({
|
||||
"official_release_id": release_id,
|
||||
"resource_root": resource_root,
|
||||
"forced": options.config.force,
|
||||
"parse": parse_report,
|
||||
"translation_queue": queue_report,
|
||||
});
|
||||
print_report(
|
||||
options.output_format,
|
||||
&CommandReport {
|
||||
command: "parse",
|
||||
status: "completed",
|
||||
message: "官方资源解析已执行",
|
||||
data,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn run_translate_once(options: &CliOptions) -> anyhow::Result<()> {
|
||||
let (resource_root, release_id) = current_official_release(options)?;
|
||||
let queue = write_official_textunit_queues(&resource_root).map_err(anyhow::Error::msg)?;
|
||||
let exported = options
|
||||
.translation_file
|
||||
.as_ref()
|
||||
.map(|path| {
|
||||
export_translation_workbench(&resource_root, release_id.clone(), path).map(
|
||||
|workbench| {
|
||||
serde_json::json!({
|
||||
"path": path,
|
||||
"entry_count": workbench.entries.len(),
|
||||
})
|
||||
},
|
||||
)
|
||||
})
|
||||
.transpose()?;
|
||||
let data = serde_json::json!({
|
||||
"official_release_id": release_id,
|
||||
"resource_root": resource_root,
|
||||
"queue": queue,
|
||||
"workbench": exported,
|
||||
"provider": "offline",
|
||||
"note": "当前 translate 只生成/刷新离线队列和可编辑工作台,不调用外部翻译 provider",
|
||||
});
|
||||
print_report(
|
||||
options.output_format,
|
||||
&CommandReport {
|
||||
command: "translate",
|
||||
status: "queued",
|
||||
message: "翻译离线队列已刷新",
|
||||
data,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn run_translation_set(options: &CliOptions) -> anyhow::Result<()> {
|
||||
let path = options
|
||||
.translation_file
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("translation-set 必须指定 --translation-file"))?;
|
||||
let text = match (&options.translation_text, &options.translation_text_file) {
|
||||
(Some(_), Some(_)) => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"--translated-text 与 --translated-file 只能指定一个"
|
||||
))
|
||||
}
|
||||
(Some(text), None) => text.clone(),
|
||||
(None, Some(path)) => String::from_utf8(
|
||||
read_file_no_symlink(path, "翻译文本文件")
|
||||
.map_err(anyhow::Error::msg)?
|
||||
.ok_or_else(|| anyhow::anyhow!("翻译文本文件不存在:{}", path.display()))?,
|
||||
)?,
|
||||
(None, None) => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"translation-set 必须指定 --translated-text 或 --translated-file"
|
||||
))
|
||||
}
|
||||
};
|
||||
let entry_id = options
|
||||
.translation_id
|
||||
.as_deref()
|
||||
.ok_or_else(|| anyhow::anyhow!("translation-set 必须指定 --translation-id"))?;
|
||||
let entry = set_translation(path, entry_id, text)?;
|
||||
let data = serde_json::json!({
|
||||
"translation_file": path,
|
||||
"entry": entry,
|
||||
});
|
||||
print_report(
|
||||
options.output_format,
|
||||
&CommandReport {
|
||||
command: "translation-set",
|
||||
status: "updated",
|
||||
message: "翻译工作台条目已更新",
|
||||
data,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn run_repack(options: &CliOptions) -> anyhow::Result<()> {
|
||||
let spec = options
|
||||
.repack_spec
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("repack 必须指定 --repack-spec"))?;
|
||||
let report = repack_bundle(spec)?;
|
||||
print_report(options.output_format, &report)
|
||||
}
|
||||
|
||||
pub(super) fn run_publish_localized(options: &CliOptions) -> anyhow::Result<()> {
|
||||
let translation_file = options
|
||||
.translation_file
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("publish-localized 必须指定 --translation-file"))?;
|
||||
let (resource_root, official_release_id) = current_official_release(options)?;
|
||||
let workbench = read_translation_workbench(translation_file)?;
|
||||
if workbench.official_release_id != official_release_id {
|
||||
return Err(anyhow::anyhow!(
|
||||
"翻译工作台 release={} 与当前官方 release={} 不一致;请重新导出",
|
||||
workbench.official_release_id,
|
||||
official_release_id
|
||||
));
|
||||
}
|
||||
let expected_root = lexical_absolute(&resource_root).map_err(anyhow::Error::msg)?;
|
||||
if workbench.official_resource_root != expected_root {
|
||||
return Err(anyhow::anyhow!(
|
||||
"翻译工作台资源根目录与当前 release 不一致;请重新导出"
|
||||
));
|
||||
}
|
||||
let patches = localized_text_asset_patches(&resource_root, &workbench)?;
|
||||
let localized_release_id = options.localized_release_id.clone().or_else(|| {
|
||||
options
|
||||
.config
|
||||
.force
|
||||
.then(|| format!("{}-manual-{}", official_release_id, unix_seconds_now()))
|
||||
});
|
||||
let mut config = LocalizedPatchConfig::new(
|
||||
resource_root,
|
||||
options.config.localized_output_root.clone(),
|
||||
official_release_id,
|
||||
patches,
|
||||
)
|
||||
.with_force(options.config.force);
|
||||
if let Some(release_id) = localized_release_id {
|
||||
config = config.with_localized_release_id(release_id);
|
||||
}
|
||||
let report = LocalizedPatchService::new().publish(&config)?;
|
||||
print_report(options.output_format, &report)
|
||||
}
|
||||
|
||||
fn current_official_release(options: &CliOptions) -> anyhow::Result<(PathBuf, String)> {
|
||||
let state = read_version_state(&options.config.version_state_path())?;
|
||||
let resource_root = if let Some(resource_root) = options.resource_root.clone() {
|
||||
lexical_absolute(&resource_root).map_err(anyhow::Error::msg)?
|
||||
} else {
|
||||
state
|
||||
.as_ref()
|
||||
.and_then(|state| state.current_completed_version.as_ref())
|
||||
.map(|record| record.resource_root.clone())
|
||||
.unwrap_or(active_official_resource_root(&options.config.output_root)?)
|
||||
};
|
||||
let release_id = state
|
||||
.as_ref()
|
||||
.and_then(|state| state.current_completed_version.as_ref())
|
||||
.filter(|_| options.resource_root.is_none())
|
||||
.map(|record| record.id.clone())
|
||||
.or_else(|| {
|
||||
resource_root
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.map(str::to_string)
|
||||
})
|
||||
.ok_or_else(|| anyhow::anyhow!("无法从当前官方资源根目录确定 release id"))?;
|
||||
if read_download_manifest_at(&resource_root)
|
||||
.map_err(anyhow::Error::msg)?
|
||||
.is_none()
|
||||
{
|
||||
return Err(anyhow::anyhow!(
|
||||
"当前官方 release 缺少官方下载 manifest:{}",
|
||||
resource_root.display()
|
||||
));
|
||||
}
|
||||
Ok((resource_root, release_id))
|
||||
}
|
||||
Reference in New Issue
Block a user