mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 10:04:55 +08:00
daemon 子进程现会透传下载并发配置,下载进度按已完成数量单调上报。MediaCatalog 改为使用官方相对路径生成全局媒体 URL,覆盖 GameData、Prologue 等目录并补齐 jpg 资源,避免叶子文件名误拼媒体根目录导致 403。同步更新测试、smoke 断言和运行文档。
This commit is contained in:
@@ -118,7 +118,12 @@ impl OfficialResourcePullProgressKind {
|
||||
pub struct OfficialResourcePullProgress {
|
||||
/// Progress event kind.
|
||||
pub kind: OfficialResourcePullProgressKind,
|
||||
/// One-based URL index in the pull plan.
|
||||
/// Monotonic completed item count for the whole pull.
|
||||
///
|
||||
/// `Started` events report the currently completed count before the URL
|
||||
/// finishes; `Finished` events report the count after completion. This is
|
||||
/// intentionally not the URL's plan position, because concurrent downloads
|
||||
/// finish out of plan order and status percentages must not move backward.
|
||||
pub index: usize,
|
||||
/// Total URL count in the pull plan.
|
||||
pub total: usize,
|
||||
@@ -633,6 +638,7 @@ impl OfficialResourcePullService {
|
||||
.collect();
|
||||
let mut download_results: Vec<Option<Result<PullOneResult, PullOneError>>> =
|
||||
(0..planned.len()).map(|_| None).collect();
|
||||
let mut completed_count = 0usize;
|
||||
|
||||
if !download_indices.is_empty() {
|
||||
let concurrency = self.download_concurrency.clamp(1, MAX_DOWNLOAD_CONCURRENCY);
|
||||
@@ -680,7 +686,7 @@ impl OfficialResourcePullService {
|
||||
WorkerMessage::Started { plan_index } => {
|
||||
let item = &planned[plan_index];
|
||||
progress(OfficialResourcePullProgress::started(
|
||||
plan_index + 1,
|
||||
completed_count,
|
||||
total,
|
||||
item.url.clone(),
|
||||
));
|
||||
@@ -711,8 +717,9 @@ impl OfficialResourcePullService {
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
completed_count += 1;
|
||||
progress(OfficialResourcePullProgress::finished(
|
||||
plan_index + 1,
|
||||
completed_count,
|
||||
total,
|
||||
item.url.clone(),
|
||||
pull_result.status,
|
||||
@@ -736,7 +743,7 @@ impl OfficialResourcePullService {
|
||||
let item = &planned[plan_index];
|
||||
self.record_quarantine_entry(&item.url, &item.destination, error)?;
|
||||
progress(OfficialResourcePullProgress::failed(
|
||||
plan_index + 1,
|
||||
completed_count,
|
||||
total,
|
||||
item.url.clone(),
|
||||
error,
|
||||
@@ -768,12 +775,13 @@ impl OfficialResourcePullService {
|
||||
let result = if let Some(existing) = &item.existing {
|
||||
self.clear_quarantine_entry(&item.url)?;
|
||||
progress(OfficialResourcePullProgress::started(
|
||||
plan_index + 1,
|
||||
completed_count,
|
||||
total,
|
||||
item.url.clone(),
|
||||
));
|
||||
completed_count += 1;
|
||||
progress(OfficialResourcePullProgress::finished(
|
||||
plan_index + 1,
|
||||
completed_count,
|
||||
total,
|
||||
item.url.clone(),
|
||||
existing.status,
|
||||
@@ -2083,7 +2091,7 @@ exit 22
|
||||
YostarJpDownloadInventory::from_catalog_bytes(
|
||||
b"FullPatch_000.zip",
|
||||
b"ExcelDB.db ExcelDB.db",
|
||||
b"JP_Airi.zip",
|
||||
b"GameData\\Audio\\VOC_JP\\JP_Airi.zip",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2143,10 +2151,10 @@ done
|
||||
printf '%s' '{android_media_hash}' > "$out"
|
||||
;;
|
||||
*/MediaResources-Windows/Catalog/MediaCatalog.bytes)
|
||||
printf '%s' 'JP_Airi_Win.zip' > "$out"
|
||||
printf '%s' 'GameData\Audio\VOC_JP\JP_Airi_Win.zip' > "$out"
|
||||
;;
|
||||
*/MediaResources/Catalog/MediaCatalog.bytes)
|
||||
printf '%s' 'JP_Airi_Android.zip' > "$out"
|
||||
printf '%s' 'GameData\Audio\VOC_JP\JP_Airi_Android.zip' > "$out"
|
||||
;;
|
||||
*.zip)
|
||||
printf '\120\113\003\004\024\000\000\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\002\000\000\000\010\000\000\000file.txtok\120\113\001\002\024\000\024\000\000\000\000\000\000\000\000\000\000\000\000\000\002\000\000\000\002\000\000\000\010\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000file.txt\120\113\005\006\000\000\000\000\001\000\001\000\066\000\000\000\050\000\000\000\000\000' > "$out"
|
||||
@@ -2162,8 +2170,8 @@ fi
|
||||
table_catalog_hash = xxhash32(b"ExcelDB.db"),
|
||||
windows_bundle_hash = xxhash32(b"FullPatch_000.zip"),
|
||||
android_bundle_hash = xxhash32(b"FullPatch_001.zip"),
|
||||
windows_media_hash = xxhash32(b"JP_Airi_Win.zip"),
|
||||
android_media_hash = xxhash32(b"JP_Airi_Android.zip"),
|
||||
windows_media_hash = xxhash32(b"GameData\\Audio\\VOC_JP\\JP_Airi_Win.zip"),
|
||||
android_media_hash = xxhash32(b"GameData\\Audio\\VOC_JP\\JP_Airi_Android.zip"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2868,7 +2876,10 @@ exit 22
|
||||
assert_eq!(xxhash32(b""), 46947589);
|
||||
assert_eq!(xxhash32(b"ExcelDB.db"), 2044170421);
|
||||
assert_eq!(xxhash32(b"FullPatch_000.zip"), 4038880697);
|
||||
assert_eq!(xxhash32(b"JP_Airi_Win.zip"), 1416778215);
|
||||
assert_eq!(
|
||||
xxhash32(b"GameData\\Audio\\VOC_JP\\JP_Airi_Win.zip"),
|
||||
1865771294
|
||||
);
|
||||
assert_eq!(xxhash32(b"ExcelDB.db ExcelDB.db"), 2147704569);
|
||||
}
|
||||
|
||||
@@ -3053,11 +3064,12 @@ exit 22
|
||||
assert_eq!(started.len(), expected_urls);
|
||||
assert_eq!(finished.len(), expected_urls);
|
||||
|
||||
// 每个 started 的 total 一致;index 覆盖 1..=N 且不重复。
|
||||
let mut indices: Vec<usize> = started.iter().map(|event| event.index).collect();
|
||||
indices.sort_unstable();
|
||||
assert_eq!(indices, (1..=expected_urls).collect::<Vec<_>>());
|
||||
// 每个 started 的 total 一致;index 表示已完成数量,不能超过总数。
|
||||
assert!(started.iter().all(|event| event.index <= expected_urls));
|
||||
assert!(started.iter().all(|event| event.total == expected_urls));
|
||||
assert!(events.windows(2).all(|pair| pair[0].index <= pair[1].index));
|
||||
let finished_indices: Vec<usize> = finished.iter().map(|event| event.index).collect();
|
||||
assert_eq!(finished_indices, (1..=expected_urls).collect::<Vec<_>>());
|
||||
|
||||
// started 与 finished 覆盖同一组 URL;finished 均为已下载。
|
||||
let started_urls: HashSet<_> = started.iter().map(|event| event.url.clone()).collect();
|
||||
|
||||
Reference in New Issue
Block a user