mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 06:34:54 +08:00
fix: 完成下载并发与翻译交接链路
This commit is contained in:
@@ -146,6 +146,12 @@
|
||||
17. 旧 launcher 包或 `resources.assets` 下载使用官方 launcher CDN 配置,primary CDN 失败后切换 official backup CDN;资源 patch host 不猜测非官方镜像。
|
||||
18. 记录最终文件大小、本次传输字节数、官方 hash 校验数和执行状态。
|
||||
19. 非官方 URL 直接拒绝。
|
||||
20. 下载调度默认并发数为 `8`,允许范围是 `1..=256`,由
|
||||
`--download-concurrency` / `BAT_DOWNLOAD_CONCURRENCY` 配置。worker 从共享
|
||||
plan 队列逐项领取任务,单个任务完成后立即领取下一个,不等待其他 worker
|
||||
的当前任务;完成结果在协调线程即时更新 manifest、hash 事件和进度计数。
|
||||
最终 `OfficialResourcePullReport.items` 仍按 `OfficialResourcePullPlan`
|
||||
顺序排列,避免并发完成顺序泄露到发布和 API 读侧。
|
||||
|
||||
路径映射时会做分段清理,并在写入前做输出目录安全校验、相对路径归属校验和现有路径组件 symlink 检查,避免把不安全路径写进输出目录或通过 symlink 跳出输出目录。
|
||||
|
||||
@@ -171,9 +177,12 @@
|
||||
|
||||
这层的意义是把“下载到磁盘的文件”变成“可查询、可复用、可去重”的资源对象。
|
||||
`resource.index` RPC / CLI 只读查询现有 SQLite 索引;索引不存在时返回
|
||||
`available=false`,不会因为查询创建空库。`translation.tasks` RPC / CLI
|
||||
可查询离线 TextUnit 任务状态和跳过/失败 reason;G-011 剩余工作是真实
|
||||
worker 状态写回、CAS 诊断入口和面向大索引的查询优化。
|
||||
`available=false`,不会因为查询创建空库。发布后的 TextUnit 队列还会在当前
|
||||
release 根目录写入 `translation-tasks.sqlite`,由版本化 `schema_migrations`
|
||||
管理 queued/running/failed/completed/skipped 和 failure reason。`translation.tasks`
|
||||
优先查询这份状态库,`translation.task.update` 供 provider worker 回写状态;
|
||||
没有状态库的旧 release 才回退到 immutable JSON 队列。G-011 剩余工作是 CAS
|
||||
诊断入口和面向大索引的查询优化。
|
||||
|
||||
对应实现主要在:
|
||||
|
||||
@@ -226,7 +235,7 @@ worker 状态写回、CAS 诊断入口和面向大索引的查询优化。
|
||||
11. 下载、manifest、本地 BLAKE3、ZIP 和官方 `.hash` 校验完成后写入新的 snapshot,并在 staging 中写入 `official-launcher-bootstrap.json`(若本轮启用 `--auto-discover`)。
|
||||
12. 将 staging rename 为 `<output>/versions/<id>`,再原子替换 `<output>/current` symlink 指向该 versioned 目录。
|
||||
13. 发布完成后先对比上一完整 release 和当前 release 的 `official-download-manifest.json`,写出 `official-resource-changes.json` 和 `crowdin-translation-handoff.json`。同一 destination 只有 size 或 BLAKE3 变化才算 modified;新增+变更资源进入解析/翻译 handoff,删除资源只进入差异记录。当前只预留 Crowdin 本地 handoff,不发外部 API 请求。
|
||||
14. 随后刷新 active release 下的 `official-parse-cache.json` 和 `official-textunit-index.json`,并从 Added/Modified 资源、parse cache 与 TextUnit 明细索引派生 `official-textunit-tasks.json` 和 `crowdin-textunit-queue.json`;up-to-date 轮询在已有有效解析缓存、TextUnit 明细索引和 TextUnit 队列时只读取摘要,不重复解析。
|
||||
14. 随后刷新 active release 下的 `official-parse-cache.json` 和 `official-textunit-index.json`,并从 Added/Modified 资源、parse cache 与 TextUnit 明细索引派生 `official-textunit-tasks.json`、`crowdin-textunit-queue.json` 和版本化的 `translation-tasks.sqlite`;up-to-date 轮询在已有有效解析缓存、TextUnit 明细索引和 TextUnit 队列时只读取摘要,不重复解析,重新同步队列时保留已有 worker 状态。
|
||||
15. 若启用 `--import-repository`,已校验 release 会被导入 CAS + `ResourceRepository`,并可经 `resource.index` 查询。
|
||||
16. 官方同步报告默认给出 `localized_release_status=not_localized`,表示原版资源已发布、汉化资源未发布;UnityFS TextAsset patch 发布成功并通过 `localized-patch-manifest.json`、current symlink 和 release ID 校验后,`localized.status` 才返回 `localized`,表示原版和汉化两套资源都已发布。
|
||||
|
||||
|
||||
@@ -219,12 +219,20 @@ cargo run -p bat-infrastructure --bin bat -- \
|
||||
`official-resource-changes.json`、`crowdin-translation-handoff.json`、
|
||||
`official-parse-cache.json` 和 `official-textunit-index.json`,随后从
|
||||
Added/Modified 资源、parse cache 与 TextUnit 明细索引派生
|
||||
`official-textunit-tasks.json` 和 `crowdin-textunit-queue.json`。本地已有旧完整
|
||||
`official-textunit-tasks.json`、`crowdin-textunit-queue.json`、
|
||||
`translation-tasks.sqlite` 和 `translation-handoff.json`。本地已有旧完整
|
||||
版本时,新版本发布后会先按 manifest destination 对比旧/新 release,只把新增和
|
||||
内容变更的资源写入解析与 Crowdin handoff;删除资源只记录差异,不进入翻译队列。
|
||||
up-to-date 轮询发现本地文件、解析缓存、TextUnit 明细索引和 TextUnit 队列未变时不会重复解析。
|
||||
Crowdin 队列当前只落本地文件,不发网络请求。
|
||||
|
||||
官方下载服务默认使用 8 个有界 worker,`--download-concurrency` /
|
||||
`BAT_DOWNLOAD_CONCURRENCY` 只接受 `1..=256`。worker 完成一个 URL 后立即从共享
|
||||
队列领取下一个任务;finished 进度按实际完成顺序即时上报,完成计数单调递增,
|
||||
最终 report 的资源列表仍按 pull plan 顺序。需要验证顺序模式时显式使用
|
||||
`--download-concurrency 1`。本文档中的真实资源命令仅是隔离 runbook;本地轻量
|
||||
验证应使用 fake-curl/fixture,不要在开发机执行真实下载或 smoke run。
|
||||
|
||||
需要把已校验官方 release 导入 CAS + `ResourceRepository` 时,显式启用:
|
||||
|
||||
```bash
|
||||
@@ -243,6 +251,8 @@ cargo run -p bat-infrastructure --bin bat -- parse-status
|
||||
cargo run -p bat-infrastructure --bin bat -- parse-text-units --limit 50
|
||||
cargo run -p bat-infrastructure --bin bat -- parse-errors --limit 50
|
||||
cargo run -p bat-infrastructure --bin bat -- translation-tasks --task-status skipped_parse_failed --has-reason --limit 50
|
||||
cargo run -p bat-infrastructure --bin bat -- translation-tasks --worker-status failed --has-failure-reason --limit 50
|
||||
cargo run -p bat-infrastructure --bin bat -- translation-handoff
|
||||
cargo run -p bat-infrastructure --bin bat -- localized-status
|
||||
cargo run -p bat-infrastructure --bin bat -- resource-index --limit 50
|
||||
cargo run -p bat-infrastructure --bin bat -- resource-index --release-id <ID> --platform windows --archive-entry <PATH> --format json --limit 50
|
||||
@@ -251,8 +261,13 @@ cargo run -p bat-infrastructure --bin bat -- resource-index --release-id <ID> --
|
||||
`parse-status` 会额外显示 TextUnit 明细索引和队列摘要;`parse-text-units` /
|
||||
`parse-errors` 可按 destination、archive entry、path id、class id、field path
|
||||
和 format 分页查询当前官方 release 的 TextUnit 明细与解析错误;
|
||||
`translation-tasks` 可按 release、destination、archive entry、任务状态、parse status、
|
||||
TextUnit format 和 reason presence 查询离线 TextUnit 翻译任务状态与跳过/失败原因;
|
||||
`translation-tasks` 可按 release、destination、archive entry、队列任务状态、provider
|
||||
worker 状态、parse status、TextUnit format、队列 reason 和 provider failure reason
|
||||
查询离线 TextUnit 翻译任务状态与跳过/失败原因;发布后的状态保存在当前 release
|
||||
根目录的 `translation-tasks.sqlite`,旧 release 没有状态库时回退到 JSON 队列;
|
||||
`translation-handoff` / `translation.handoff` 会动态合并版本化
|
||||
`translation-handoff.json` 与 SQLite 状态,返回 job、unit、provider run 的完整交接
|
||||
视图;
|
||||
`resource-index` 返回的资源 JSON 包含 release、平台、bundle path、TextAsset 和 TextUnit metadata,
|
||||
并可按 release、平台、destination、bundle path、archive entry、parse status 和 TextUnit format 做资源级过滤;
|
||||
`localized-status` 只有在 `localized-version-state.json`、`current` symlink 和
|
||||
|
||||
@@ -322,7 +322,7 @@ cargo run -p bat-infrastructure --bin bat -- \
|
||||
|
||||
默认平台是 `Windows,Android`,无需显式传 `--platforms`;只有要覆盖默认平台时才传。`--interval` 是正常检查周期,默认 `1h`;watch/daemon 模式还会在每天北京时间(UTC+8)`03:00`、`16:00`、`18:00` 强制执行一次自动刷新,该轮会注入 `force=true`,并且会中断普通 interval 的 sleep。`--error-retry` 是下载、发现或校验失败后的重试周期,默认 `60s`,也可以用 `--error-retry-seconds 60`。CLI 默认启动时向 stderr 打印 `BlueArchiveToolkit` ASCII banner,并把阶段进度日志写到 stderr,包括自动发现、proxy、server-info、marker、catalog、audit、download、snapshot 和 publish 阶段;download 阶段会输出已完成计数和单文件开始/完成状态,下载执行保持顺序处理,已完成计数保持单调不倒退,audit 阶段会输出官方 `.hash`、本地 BLAKE3、需修复项和 ZIP 结构校验结果摘要。daemon 还会写 `bat-events.jsonl` 结构化日志并按大小轮转。命令结果默认以人类可读摘要写到 stdout。需要纯机器输出时加 `--json --no-progress`,需要显式开启进度日志则用 `--progress`;只想关闭横幅但保留日志时可加 `--no-banner`。错误时 stderr 输出 JSON error,watch 模式下错误 JSON 的 `next_retry_seconds` 使用失败重试周期;如果未关闭 progress,错误 JSON 前可能已有 banner 和进度日志。普通错误 exit `1`,资源目录锁冲突 exit `75`,`verify` 或 `doctor` 发现问题也返回非 0。
|
||||
|
||||
生产可以直接运行 `--watch`,也可以用 `--daemon` 后台运行,或者用 systemd service、容器或 Go 进程守护它。cron/systemd timer 仍可调用单次模式,但不再是 Rust 自动更新的唯一方式。项目是否热更新、热重载或重启进程,由上层业务集成决定。生产官方资源目录应使用独立输出目录,不要指向现有客户端或人工维护的资源目录;上层读取原版资源时应读取 `--output/current`,不要读取 `.staging` 或 `versions` 中未切换的目录。汉化 Patch/导出应写入 `--localized-output`,并保留官方相对目录结构,不能写回 `--output/current`。发布状态分两档:`not_localized` 只发布原版资源、不发布汉化资源;`localized` 发布原版和汉化两套资源。非 dry-run 每轮会创建 `--output/.official-sync.lock`,防止并发写同一官方资源目录;live daemon 还会阻止前台写命令直接修改它正在管理的同一目录。
|
||||
生产可以直接运行 `--watch`,也可以用 `--daemon` 后台运行,或者用 systemd service、容器或 Go 进程守护它。cron/systemd timer 仍可调用单次模式,但不再是 Rust 自动更新的唯一方式。下载默认并发 8,可用 `--download-concurrency` / `BAT_DOWNLOAD_CONCURRENCY` 配置为 `1..=256`;worker 动态领取共享 plan,finished 进度即时按完成数统计,发布 report 仍按 plan 顺序。项目是否热更新、热重载或重启进程,由上层业务集成决定。生产官方资源目录应使用独立输出目录,不要指向现有客户端或人工维护的资源目录;上层读取原版资源时应读取 `--output/current`,不要读取 `.staging` 或 `versions` 中未切换的目录。汉化 Patch/导出应写入 `--localized-output`,并保留官方相对目录结构,不能写回 `--output/current`。发布状态分两档:`not_localized` 只发布原版资源、不发布汉化资源;`localized` 发布原版和汉化两套资源。非 dry-run 每轮会创建 `--output/.official-sync.lock`,防止并发写同一官方资源目录;live daemon 还会阻止前台写命令直接修改它正在管理的同一目录。
|
||||
|
||||
需要只做探测时可以加 `--dry-run`。需要关闭本地 audit 或 repair 时可以显式使用 `--no-audit-local` 或 `--no-repair`,但生产同步默认应保持开启。
|
||||
|
||||
|
||||
@@ -151,6 +151,11 @@ SQLite `ResourceRepository`,索引不存在时返回 `ok=true` 且
|
||||
TextUnit 明细索引派生,记录 TextUnit 任务、跳过原因和解析诊断。
|
||||
- `crowdin-textunit-queue.json`:只包含已产生 TextUnit 的离线任务,预留给后续
|
||||
Crowdin worker;当前不会发出网络请求。
|
||||
- `translation-tasks.sqlite`:当前 release 的可变 worker 状态库,记录
|
||||
queued / running / failed / completed / skipped、attempt count、provider run
|
||||
ID 和 failure reason;schema 由 `schema_migrations` 版本表管理。
|
||||
- `translation-handoff.json`:当前 release 的版本化 job/unit/provider run 交接
|
||||
快照;worker 更新后的实时状态仍以 `translation-tasks.sqlite` 为准。
|
||||
|
||||
删除资源只进入 `official-resource-changes.json`,不进入 Crowdin handoff。
|
||||
|
||||
@@ -162,6 +167,8 @@ SQLite `ResourceRepository`,索引不存在时返回 `ok=true` 且
|
||||
| `parse.text_units` | 已实现 | `{ "offset": 0, "limit": 100, "destination": "*Table*", "archive_entry": "*.bytes", "path_id": 1, "class_id": 114, "field_path": "*Text*", "format": "json" }` | 当前官方 release 的 TextUnit 明细分页。 |
|
||||
| `parse.errors` | 已实现 | `{ "offset": 0, "limit": 100, "destination": "*Table*", "archive_entry": "*.bytes", "path_id": 1, "class_id": 114, "field_path": "*Text*", "format": "json" }` | 当前官方 release 的解析错误分页。 |
|
||||
| `translation.tasks` | 已实现 | `{ "offset": 0, "limit": 100, "task_id": "...", "release_id": "...", "destination": "...", "archive_entry": "...", "status": "skipped_parse_failed", "parse_status": "failed", "format": "json", "has_reason": true }` | 当前官方 release 的离线 TextUnit 翻译任务状态分页。 |
|
||||
| `translation.handoff` | 已实现 | `null` | 当前官方 release 的 job、unit、provider run 交接视图;动态合并队列和 SQLite worker 状态。 |
|
||||
| `translation.task.update` | 已实现 | `{ "task_id": "...", "status": "failed", "failure_reason": "...", "provider_run_id": "..." }` | 写入当前 release 的 provider worker 状态,返回可回查任务记录。 |
|
||||
|
||||
`parse.status` 是只读查询;没有当前 release 或没有解析缓存时返回
|
||||
`ok=true` 且 `data.available=false`。解析缓存来自官方原版资源目录,不读取
|
||||
@@ -195,15 +202,25 @@ offset 和 error。TypeTree-covered managed reference 字段会进入结构化
|
||||
完整 managed reference registry 等暂不支持结构会进入解析错误,而不是静默降级为
|
||||
低保真文本。
|
||||
|
||||
`translation.tasks` 查询 `official-textunit-tasks.json`,用于查看离线
|
||||
TextUnit 翻译任务候选、跳过状态和失败/跳过原因;没有当前 release 或没有任务队列时
|
||||
返回 `ok=true` 且 `data.available=false`。过滤参数包括 `task_id`、
|
||||
`translation.tasks` 优先查询当前 release 的 `translation-tasks.sqlite`,旧 release
|
||||
没有该文件时回退到 `official-textunit-tasks.json`;用于查看离线 TextUnit
|
||||
翻译任务候选和 provider worker 状态。没有当前 release 或没有任务队列时返回
|
||||
`ok=true` 且 `data.available=false`。过滤参数包括 `task_id`、
|
||||
`official_release_id`/`release_id`、`destination`、`path_pattern`、
|
||||
`archive_entry`、`status`/`task_status`、`parse_status`、`text_unit_format`/`format`
|
||||
和 `has_reason`。`entries[]` 会包含 `official_release_id`、`destination`、
|
||||
`archive_entry`、`parse_status`、`status`、`reason`、TextAsset/TextUnit 摘要和
|
||||
校验指纹。当前它只反映 Rust 侧离线 handoff 状态;真实 Crowdin worker 消费、
|
||||
重试和完成状态仍需后续 worker 模型写回后才能暴露。
|
||||
`archive_entry`、`status`/`task_status`、`worker_status`、`parse_status`、
|
||||
`text_unit_format`/`format`、`has_reason` 和 `has_failure_reason`。
|
||||
`entries[]` 会包含 `official_release_id`、`destination`、`archive_entry`、
|
||||
`parse_status`、队列 `status`、`task_status`、`failure_reason`、`attempt_count`、
|
||||
`provider_run_id`、TextAsset/TextUnit 摘要和校验指纹。
|
||||
|
||||
`translation.task.update` 只更新当前 release 的 SQLite 状态库,不改写 immutable
|
||||
队列文件,也不主动访问 Crowdin。`status` 支持 `queued`、`running`、`failed`、
|
||||
`completed` 和 `skipped`;进入 `running` 会增加 attempt count,`completed` 会
|
||||
记录完成时间,`failed` 可写入 `failure_reason`。因此 worker 消费 handoff 后,
|
||||
bat-api 可通过 `translation.tasks` 查询单项任务,也可通过
|
||||
`translation.handoff` 获取完整 job/unit/provider run 状态。`translation.handoff`
|
||||
不会触发下载或 provider 网络请求;没有当前 release 或任务队列时返回
|
||||
`data.available=false`。
|
||||
|
||||
### localized
|
||||
|
||||
|
||||
@@ -286,9 +286,12 @@
|
||||
- 官方 release 发布后会写出 `official-resource-changes.json` 和 `crowdin-translation-handoff.json`,新增+变更资源进入解析/翻译 handoff,删除资源只进入差异记录。
|
||||
- `Resource` metadata 已通过 SQLite `metadata_json` 兼容迁移保存 release、平台、bundle path、parse status、TextAsset 名称和 TextUnit 数量/格式;`resource.index` 会返回这些 metadata。
|
||||
- 官方 release 发布后会持久化 `official-textunit-index.json`,记录单条 TextUnit 和解析错误;`parse.text_units` / `parse.errors` RPC 和 `parse-text-units` / `parse-errors` CLI 可按 destination、archive entry、path id、class id、field path 和 format 分页过滤。
|
||||
- 官方 release 发布后会从 Added/Modified 资源、parse cache 和 TextUnit 明细索引派生 `official-textunit-tasks.json` 和 `crowdin-textunit-queue.json`;删除资源不会进入队列。
|
||||
- `translation.tasks` RPC/CLI 已能分页查询 `official-textunit-tasks.json` 中的离线 TextUnit 任务状态、parse 状态、TextUnit format 和跳过/失败 reason。
|
||||
- 真实 Crowdin worker 消费后的任务状态、重试次数、远端失败原因和完成结果查询仍需补齐。
|
||||
- 官方 release 发布后会从 Added/Modified 资源、parse cache 和 TextUnit 明细索引派生 `official-textunit-tasks.json`、`crowdin-textunit-queue.json` 和 `translation-tasks.sqlite`;删除资源不会进入队列。
|
||||
- 官方 release 发布后还会写入版本化 `translation-handoff.json`;`translation.handoff` RPC/CLI 动态合并该快照与 SQLite worker 状态,暴露 job、unit、provider run、attempt 和 failure reason。
|
||||
- `translation-tasks.sqlite` 由 `schema_migrations` 管理 durable task state、attempt count、provider run ID 和 failure reason;重复同步会保留已有 worker 状态。
|
||||
- `translation.tasks` RPC/CLI 优先查询 `translation-tasks.sqlite`,旧 release 没有状态库时回退到 `official-textunit-tasks.json`;返回队列 `status`、worker `task_status`、failure reason 和时间/尝试次数。
|
||||
- `translation.task.update` RPC 已提供 queued/running/failed/completed/skipped 状态回写契约,provider worker 可在消费 handoff 后按 task_id 更新并由同一查询接口反查。
|
||||
- 真实 Crowdin 网络 worker、翻译记忆和完整 localized repack 仍属于后续翻译系统工作,不在当前 Rust 离线状态仓储范围内。
|
||||
|
||||
验收:
|
||||
|
||||
@@ -554,7 +557,9 @@
|
||||
|
||||
1. issue #24:失败 staging 复用回归已补;核对残余场景。
|
||||
2. issue #1:RPC 主体、文件级 `patch.apply` / `unityfs.patch_text_asset` / `unityfs.patch_string_field` / `unityfs.patch_field` 已落地;剩余发布级 patch build/rollback、复杂 UnityFS 语义编辑与设计边界确认。
|
||||
3. issue #17 及子 issue:已按 wontfix 关闭多线程下载(顺序下载 + 指数退避)。
|
||||
3. issue #17 的历史顺序/重试契约仍保留;当前 issue #33/#35 已补有界 downloader
|
||||
scheduler 和默认并发 8,范围 `1..=256`,worker 完成后立即领取下一个任务,
|
||||
进度即时按完成数上报,最终 report 保持 plan 顺序。
|
||||
4. **G-008:已决策关闭**(同步 CLI = Rust `bat`;见 `GO_STATUS.md`)。
|
||||
5. **G-009 / issue #19**:资源 bootstrap/分发 MVP 已编码;优先服务器联调与索引实勘,非「从零实现」。
|
||||
6. issue #2 / G-007(P1):Addressables 可校验字段。
|
||||
|
||||
Reference in New Issue
Block a user