mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 11:34:59 +08:00
fix: 完成下载并发与翻译交接链路
This commit is contained in:
@@ -29,6 +29,9 @@ use crate::path_security::{
|
||||
read_file_no_symlink, validate_output_root, write_file_atomic, STATE_FILE_MODE,
|
||||
};
|
||||
use crate::release_flow::ReleaseFlowStatusCode;
|
||||
use crate::translation_tasks::{
|
||||
build_translation_handoff, sync_translation_task_repository_at, write_translation_handoff_at,
|
||||
};
|
||||
use crate::{
|
||||
build_official_pull_plan_for_platform_inventory, build_official_sync_plan,
|
||||
changed_endpoint_urls, default_official_platforms, DownloadError,
|
||||
@@ -42,15 +45,17 @@ use crate::{
|
||||
read_parse_cache_at, OfficialParseCacheService, OfficialParseConfig, OfficialParseSummary,
|
||||
};
|
||||
use crate::{FileSystemCasRepository, SqliteResourceRepository};
|
||||
use crate::{DEFAULT_DOWNLOAD_CONCURRENCY, MAX_DOWNLOAD_CONCURRENCY, MIN_DOWNLOAD_CONCURRENCY};
|
||||
use bat_adapters::official::game_main_config::YostarJpGameMainConfig;
|
||||
use bat_adapters::official::inventory::{
|
||||
YostarJpPlatformCatalogInventory, YostarJpPlatformDownloadInventory,
|
||||
};
|
||||
use bat_adapters::official::launcher::YostarJpLauncherManifestFile;
|
||||
use bat_adapters::official::yostar_jp::{
|
||||
server_info_url, PatchPlatform, YostarJpResourceDiscoveryPlan, YostarJpResourceEndpoint,
|
||||
PatchPlatform, YostarJpResourceDiscoveryPlan, YostarJpResourceEndpoint,
|
||||
YostarJpResourceEndpointKind, YostarJpServerInfo, YostarJpSyncSnapshot,
|
||||
};
|
||||
use bat_adapters::official::{
|
||||
OfficialResourceBackend, PlatformCatalogInput, YostarJpBackend,
|
||||
YostarJpPlatformDownloadInventory,
|
||||
};
|
||||
use bat_core::ErrorCode;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
@@ -115,6 +120,10 @@ pub struct OfficialUpdateConfig {
|
||||
pub curl_command: PathBuf,
|
||||
/// Proxy selection used by all official `curl` transfers.
|
||||
pub curl_proxy: CurlProxyConfig,
|
||||
/// Maximum number of resource downloads executed concurrently.
|
||||
///
|
||||
/// `8` is the default; values are accepted only in `1..=256`.
|
||||
pub download_concurrency: usize,
|
||||
/// Unzip command used when a metadata change requires GameMainConfig parsing.
|
||||
pub unzip_command: PathBuf,
|
||||
/// Dry run reports decisions and optional plan URLs without writing sync state.
|
||||
@@ -151,6 +160,7 @@ impl Default for OfficialUpdateConfig {
|
||||
snapshot_path: None,
|
||||
curl_command: PathBuf::from("curl"),
|
||||
curl_proxy: CurlProxyConfig::default(),
|
||||
download_concurrency: DEFAULT_DOWNLOAD_CONCURRENCY,
|
||||
unzip_command: PathBuf::from("unzip"),
|
||||
dry_run: false,
|
||||
plan: false,
|
||||
@@ -844,6 +854,8 @@ pub struct OfficialUpdateReport {
|
||||
pub crowdin_textunit_queue_path: Option<PathBuf>,
|
||||
/// Incremental TextUnit task queue summary.
|
||||
pub textunit_task_summary: Option<OfficialTextUnitTaskSummary>,
|
||||
/// Versioned translation worker handoff view.
|
||||
pub translation_handoff_path: Option<PathBuf>,
|
||||
/// Snapshot path written after success.
|
||||
pub snapshot_written: Option<PathBuf>,
|
||||
/// Launcher bootstrap artifact written for this run.
|
||||
@@ -1221,7 +1233,8 @@ impl OfficialUpdateService {
|
||||
&active_resource_root,
|
||||
&config.curl_command,
|
||||
)
|
||||
.with_proxy_config(config.curl_proxy.clone());
|
||||
.with_proxy_config(config.curl_proxy.clone())
|
||||
.with_max_concurrency(config.download_concurrency);
|
||||
let snapshot_path = snapshot_path_for(config, &active_resource_root);
|
||||
let bootstrap_cache_path = config.bootstrap_cache_path();
|
||||
|
||||
@@ -1668,6 +1681,7 @@ impl OfficialUpdateService {
|
||||
textunit_task_queue_path: None,
|
||||
crowdin_textunit_queue_path: None,
|
||||
textunit_task_summary: None,
|
||||
translation_handoff_path: None,
|
||||
snapshot_written: None,
|
||||
launcher_bootstrap_artifact_path: None,
|
||||
repository_import_enabled: config.import_repository,
|
||||
@@ -1846,7 +1860,8 @@ impl OfficialUpdateService {
|
||||
&publish_plan.staging_path,
|
||||
&config.curl_command,
|
||||
)
|
||||
.with_proxy_config(config.curl_proxy.clone());
|
||||
.with_proxy_config(config.curl_proxy.clone())
|
||||
.with_max_concurrency(config.download_concurrency);
|
||||
let pruned_stale_resource_count = staging_fetcher
|
||||
.prune_stale_manifest_entries(&pull_plan)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
@@ -2201,6 +2216,7 @@ fn run_post_sync_textunit_queue_if_needed(
|
||||
&& is_crowdin_textunit_queue_current(resource_root, &queue)
|
||||
.map_err(anyhow::Error::msg)?
|
||||
{
|
||||
sync_translation_task_repository(resource_root, &queue)?;
|
||||
progress(OfficialUpdateProgress::new(
|
||||
"textunit",
|
||||
format!(
|
||||
@@ -2212,7 +2228,8 @@ fn run_post_sync_textunit_queue_if_needed(
|
||||
));
|
||||
report.textunit_task_queue_path = Some(task_queue_path);
|
||||
report.crowdin_textunit_queue_path = Some(crowdin_queue_path);
|
||||
report.textunit_task_summary = Some(queue.summary);
|
||||
report.textunit_task_summary = Some(queue.summary.clone());
|
||||
write_translation_handoff_for_release(resource_root, &queue, report, progress)?;
|
||||
return Ok(());
|
||||
}
|
||||
progress(OfficialUpdateProgress::new(
|
||||
@@ -2227,6 +2244,19 @@ fn run_post_sync_textunit_queue_if_needed(
|
||||
run_post_sync_textunit_queue(resource_root, report, progress, should_cancel)
|
||||
}
|
||||
|
||||
fn sync_translation_task_repository(
|
||||
resource_root: &Path,
|
||||
queue: &crate::official_textunit_queue::OfficialTextUnitTaskQueue,
|
||||
) -> anyhow::Result<()> {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()?;
|
||||
runtime
|
||||
.block_on(sync_translation_task_repository_at(resource_root, queue))
|
||||
.map_err(|error| anyhow::anyhow!("{error}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_post_sync_textunit_queue(
|
||||
resource_root: &Path,
|
||||
report: &mut OfficialUpdateReport,
|
||||
@@ -2257,6 +2287,11 @@ fn run_post_sync_textunit_queue(
|
||||
),
|
||||
));
|
||||
apply_textunit_queue_report(report, queue_report);
|
||||
if let Some(queue) =
|
||||
read_textunit_task_queue_at(resource_root).map_err(anyhow::Error::msg)?
|
||||
{
|
||||
write_translation_handoff_for_release(resource_root, &queue, report, progress)?;
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
progress(OfficialUpdateProgress::new(
|
||||
@@ -2277,6 +2312,48 @@ fn apply_textunit_queue_report(
|
||||
report.textunit_task_summary = Some(queue_report.summary);
|
||||
}
|
||||
|
||||
fn write_translation_handoff_for_release(
|
||||
resource_root: &Path,
|
||||
queue: &crate::official_textunit_queue::OfficialTextUnitTaskQueue,
|
||||
report: &mut OfficialUpdateReport,
|
||||
progress: &mut dyn FnMut(OfficialUpdateProgress),
|
||||
) -> anyhow::Result<()> {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()?;
|
||||
let tasks = runtime.block_on(async {
|
||||
let repository = crate::translation_tasks::SqliteTranslationTaskRepository::new(
|
||||
crate::translation_tasks::SqliteTranslationTaskRepository::repository_path(
|
||||
resource_root,
|
||||
),
|
||||
)
|
||||
.await
|
||||
.map_err(|error| anyhow::anyhow!("{error}"))?;
|
||||
repository
|
||||
.sync_queue(queue)
|
||||
.await
|
||||
.map_err(|error| anyhow::anyhow!("{error}"))?;
|
||||
repository
|
||||
.list(&crate::official_textunit_queue::OfficialTextUnitTaskQuery::default())
|
||||
.await
|
||||
.map_err(|error| anyhow::anyhow!("{error}"))
|
||||
})?;
|
||||
let handoff = build_translation_handoff(queue, &tasks);
|
||||
write_translation_handoff_at(resource_root, &handoff).map_err(anyhow::Error::msg)?;
|
||||
let path = resource_root.join(crate::translation_tasks::TRANSLATION_HANDOFF_FILE);
|
||||
report.translation_handoff_path = Some(path.clone());
|
||||
progress(OfficialUpdateProgress::new(
|
||||
"textunit",
|
||||
format!(
|
||||
"翻译 handoff 已发布:任务={} provider_run={} {}",
|
||||
handoff.units.len(),
|
||||
handoff.provider_runs.len(),
|
||||
path.display()
|
||||
),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_post_sync_repository_import(
|
||||
config: &OfficialUpdateConfig,
|
||||
resource_root: &Path,
|
||||
@@ -2370,8 +2447,9 @@ fn build_pull_plan(
|
||||
should_cancel: &mut dyn FnMut() -> bool,
|
||||
) -> anyhow::Result<OfficialResourcePullPlan> {
|
||||
check_shutdown_requested(should_cancel)?;
|
||||
let discovery = server_info
|
||||
.discovery_plan(connection_group, app_version, platforms)
|
||||
let backend = YostarJpBackend;
|
||||
let discovery = backend
|
||||
.discovery_plan(server_info, connection_group, app_version, platforms)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
progress(OfficialUpdateProgress::new(
|
||||
"plan",
|
||||
@@ -2402,7 +2480,9 @@ fn load_server_info(
|
||||
match source {
|
||||
OfficialServerInfoSource::LocalPath(path) => Ok(fs::read(path)?),
|
||||
OfficialServerInfoSource::OfficialFile(file_name) => {
|
||||
let url = server_info_url(file_name).map_err(anyhow::Error::msg)?;
|
||||
let url = YostarJpBackend
|
||||
.server_info_url(file_name)
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
fetcher.fetch_bytes(&url).map_err(anyhow::Error::new)
|
||||
}
|
||||
OfficialServerInfoSource::OfficialUrl(url) => {
|
||||
@@ -2489,6 +2569,7 @@ fn waiting_for_official_resources_report(
|
||||
textunit_task_queue_path: None,
|
||||
crowdin_textunit_queue_path: None,
|
||||
textunit_task_summary: None,
|
||||
translation_handoff_path: None,
|
||||
snapshot_written: None,
|
||||
launcher_bootstrap_artifact_path: None,
|
||||
repository_import_enabled: config.import_repository,
|
||||
@@ -2894,6 +2975,12 @@ pub fn diff_extended_snapshot(
|
||||
}
|
||||
|
||||
fn validate_update_paths(config: &OfficialUpdateConfig) -> Result<(), String> {
|
||||
if !(MIN_DOWNLOAD_CONCURRENCY..=MAX_DOWNLOAD_CONCURRENCY).contains(&config.download_concurrency)
|
||||
{
|
||||
return Err(format!(
|
||||
"下载并发数必须在 {MIN_DOWNLOAD_CONCURRENCY}..={MAX_DOWNLOAD_CONCURRENCY} 范围内"
|
||||
));
|
||||
}
|
||||
validate_output_root(&config.output_root)?;
|
||||
validate_output_root(&config.localized_output_root)?;
|
||||
validate_separate_output_roots(&config.output_root, &config.localized_output_root)?;
|
||||
@@ -4047,17 +4134,14 @@ fn build_inventory_from_seed_catalogs(
|
||||
anyhow::anyhow!("官方发现结果缺少 {} MediaCatalog.bytes", platform.as_str())
|
||||
})?;
|
||||
|
||||
platform_catalogs.push(YostarJpPlatformCatalogInventory::from_catalog_bytes(
|
||||
*platform,
|
||||
platform_catalogs.push(PlatformCatalogInput {
|
||||
platform: *platform,
|
||||
bundle_packing_info,
|
||||
media_catalog,
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
Ok(YostarJpPlatformDownloadInventory::from_catalog_bytes(
|
||||
table_catalog,
|
||||
platform_catalogs,
|
||||
))
|
||||
Ok(YostarJpBackend.inventory(table_catalog, &platform_catalogs))
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
||||
Reference in New Issue
Block a user