# 拒绝截断视频，只重取交付文件

把文件交付恢复与生成恢复分开。HTTP 200 下载也可能是损坏视频；本离线夹具只重取同一个已知交付文件身份。

完整包只使用 Node 标准库。FFmpeg 需使用自己已有的安装，本包不分发二进制；不在 PATH 时将 FFMPEG_BIN 设为其绝对路径。

在本目录运行，并选择不存在的新输出目录，脚本拒绝覆盖。受内存管理的工作站可在 Node 命令前加本地串行执行包装器。不需要提供商密钥，夹具不会向设备外发请求。

```sh
node run-recipe.mjs --out ./my-run
```

实际记录：7项检查通过，包含1项预期拒绝，Node v24.18.0。ffmpeg version 6.0 Copyright (c) 2000-2023 the FFmpeg developers

准备

- 准备 Node.js 与 FFmpeg。原创源片为两秒、320×180、二十帧；大小与预期 SHA-256 写入 artifact-manifest.json。
- 查看 truncated.mp4：它真实取自源文件的前半段字节，不是虚构错误标签；首次本机下载实际返回这份损坏文件。
- 使用全新目录。请求仅指向 127.0.0.1 临时端口的同一文件 URL，设三秒超时、2MB 响应上限，并在 finally 关闭服务；不调用生产任务。

## 01. 保持原任务与文件身份

读取 local-paid-once 与 /artifact/local-paid-once 清单。夹具提供一个刻意禁止的 /generate 路由，只用于统计误提交，成功流程从不调用。本地名字不代表实际付费任务。

## 02. HTTP 成功仍要拒绝坏媒体

首个响应虽然是 HTTP 200，实际仅17775字节，为35550字节源文件的一半。FFmpeg 使用 -xerror 与 explode 错误检测执行完整视频解码，真实失败日志保留在负向回执。

## 03. 对同一文件 URL 只重试一次

第二次请求完全相同的交付文件身份，本机服务返回完整原创字节。先与清单核对 SHA-256，再完整解码二十帧，不只看网络状态。

## 04. 验收重取与生成之间的边界

确认下载次数恰为二、生成请求为零、临时服务已经关闭。此有界夹具没有退避、签名 URL 更新或提供商错误分类；真实下载器应使用其服务文档定义的恢复规则。

验收

- 首个 HTTP 200 实际包含截断字节前缀，SHA-256 不同于预期。
- 截断文件全解码退出失败，实际错误已记录。
- 第二次使用同一文件 URL，下载总数准确为二。
- 第二份文件 SHA-256 与完整交付清单一致。
- 恢复文件完整解码为二十帧 320×180。
- 没有 /generate 请求，服务正常关闭，七项检查全部通过。

排错

HTTP 返回200，但播放器打不开。保留实际下载字节并运行完整解码，媒体检查失败后只重取同一交付文件。

第二份能播放，但校验和不同。先只核对文件与清单绑定再接受；可读不代表它来自预期任务。

文件重试又产生了一次生成请求。把重试目标改回已保存的文件 URL，再跑零生成断言；生成提交不能放在交付恢复循环内。

公开回执副本仅归一化本地 checkout 路径，publicPathNormalization 保存原始回执 SHA-256；未改动原件保留在非公开资产之外。真实回执位于 executed/。failure-receipt.json 记录预期负向案例，不表示套件断言失败。原创新夹具会重新生成到指定新目录，包内保留实跑输入输出便于复核。子进程使用参数数组、禁用 shell，设15秒超时与8MB输出上限。临时原始数据和本机 HTTP 服务在 finally 清理。

checksums.json 覆盖自身与 ZIP 以外的全部分发文件，避免循环自引用；ZIP 内包含同份清单。下载后可重算，但比较前提是预期清单可信。execution-receipt.json 记录实际脚本运行前后哈希，ZIP 也会由发布目录的资产清单绑定。

本机重放只证明这个截断 MP4 在保持文件身份下可恢复，不代表提供商重试承诺，也没有真实支付、生成或签名下载执行。

2026-10-14 实际读取的来源

- FFmpeg command-line documentation: maps, codecs and error exit：https://ffmpeg.org/ffmpeg.html
- Node.js HTTP API: server lifecycle and requests：https://nodejs.org/api/http.html
- Node.js child process API: bounded argument-array execution：https://nodejs.org/api/child_process.html
