Batch 3a: library_run.py 支持 --work-dir(工作根可指定,默认常量不变)
_apply_work_dir() 在 argv 解析后重绑 WORK/STAGE/LEDGER/LOCKF/POLICY;--stage-dir 仍只覆盖暂存。 不传 --work-dir 时行为与以前逐位一致(控制面 docs/DESIGN.md §18 是规则出处)。 STATE.md 记录本轮。验收:控制面 scripts/acceptance_work_dir.py 真跑 copy 档 PASS 8/8, 控制面 124 用例 OK。回滚点 tag pre-batch3a。
This commit is contained in:
@@ -2,6 +2,24 @@
|
||||
|
||||
给下一个会话的交接。**权威运行器:`scripts/library_run.py`**(不要再恢复旧的 `hevc_lib.py`)。
|
||||
|
||||
## 2026-10-13:`--work-dir` —— 工作根可以指定了(Batch 3a)
|
||||
|
||||
**改了什么**:`WORK`(默认 `C:\AI\transcode-run`)从「唯一真相」退化成**默认值**。`main()` 解析完 argv 后,
|
||||
若给了 `--work-dir` 就调用新增的 `_apply_work_dir()` 重新绑定 `WORK / STAGE / LEDGER / LOCKF / POLICY`
|
||||
(模块全局;所有引用点都是调用时查找,所以函数体一行没改)。`--stage-dir` 仍然只覆盖暂存。
|
||||
|
||||
**为什么**:控制面(`hoelee/hevc-transcode`)过去要用正则读本文件的 `WORK` 常量、并要求 `settings.work_root`
|
||||
与它**完全一致**,否则 doctor FAIL —— 那是跨仓库的隐式契约。现在由控制面显式传,`work_root` 成为权威
|
||||
(规则见控制面仓 `docs/DESIGN.md` §18)。**根目录指错不会报错,只会读到一个空的 `gate_policy.json`**,表现是
|
||||
业主的 park/accept 决定「全都丢了」—— 所以默认值必须保持不变,只有显式传 `--work-dir` 才移动。
|
||||
|
||||
**手跑行为不变**:`python scripts/library_run.py …`(不传 `--work-dir`)写的地方和以前一模一样。
|
||||
**控制面**只在检测到本文件支持该参数时才传(`rt.config.engine_features()` 读源码判定)。
|
||||
|
||||
**验收**(在控制面仓):`scripts/acceptance_work_dir.py` 真跑一次 copy 档 → **PASS 8/8**(ledger 与 `stage/`
|
||||
落在新根、默认根里**没有本项**的记录、源档 SHA-256 未变、安装件可 probe);控制面 124 用例 OK。
|
||||
⚠ 这类验收**不要**用「默认根 ledger 哈希没变」当断言 —— 生产 lane 同时在追加那份文件。
|
||||
|
||||
## 2026-10-11:画质策略落地(闸门对齐 / 门槛 / VMAF)—— 先读这一节
|
||||
|
||||
**结论:闸门以前把帧配错了对**,靠它判死过 14 个健康编码(9.6 h 里烧掉 12.4 h lane 时间、13 项被 park)。
|
||||
|
||||
+33
-4
@@ -99,10 +99,32 @@ def run_pinned(cmd, timeout=None, **kw):
|
||||
raise subprocess.TimeoutExpired(cmd, timeout, output=out, stderr=err)
|
||||
return subprocess.CompletedProcess(cmd, p.returncode, out, err)
|
||||
ROOT = r"Z:\Class\樊登读书\樊登读书会(每周更新)"
|
||||
# The work root: ledger.jsonl + gate_policy.json + the per-tier locks + (by default) the scratch dir.
|
||||
# `--work-dir` re-points ALL of it BEFORE a run starts (see _apply_work_dir below). The constant here
|
||||
# is the compiled-in DEFAULT, kept so a hand-run engine writes exactly where it always did: an
|
||||
# existing ledger and an existing gate_policy.json - the only record of what is parked and accepted -
|
||||
# must never be silently swapped for an empty directory. The control plane passes --work-dir
|
||||
# explicitly (Batch 3a), which is what makes `settings.work_root` authoritative instead of a number
|
||||
# the two repositories had to keep in sync.
|
||||
WORK = r"C:\AI\transcode-run"
|
||||
STAGE = os.path.join(WORK, "stage")
|
||||
LEDGER = os.path.join(WORK, "ledger.jsonl")
|
||||
LOCKF = os.path.join(WORK, "run.lock")
|
||||
|
||||
|
||||
def _apply_work_dir(path):
|
||||
"""Re-point WORK and everything derived from it. Called from main() AFTER argparse.
|
||||
|
||||
Rebound as module globals so every existing call site keeps working unchanged; the ledger, the
|
||||
locks and the policy file are all read through those names at call time, never captured at
|
||||
import time.
|
||||
"""
|
||||
global WORK, STAGE, LEDGER, LOCKF, POLICY
|
||||
WORK = os.path.abspath(path)
|
||||
STAGE = os.path.join(WORK, "stage")
|
||||
LEDGER = os.path.join(WORK, "ledger.jsonl")
|
||||
LOCKF = os.path.join(WORK, "run.lock")
|
||||
POLICY = os.path.join(WORK, "gate_policy.json")
|
||||
VIDEO_EXT = {".mkv", ".mp4", ".mov", ".avi", ".webm", ".flv", ".ts", ".m4v", ".wmv",
|
||||
".mpg", ".mpeg", ".m2ts", ".3gp", ".ogv", ".vob", ".rmvb", ".rm", ".divx"}
|
||||
DECODE_CHECK = True
|
||||
@@ -1334,7 +1356,7 @@ def collect(root):
|
||||
|
||||
|
||||
def main():
|
||||
global DECODE_CHECK, STAGE
|
||||
global DECODE_CHECK, STAGE # WORK/STAGE/LEDGER/LOCKF/POLICY move via _apply_work_dir
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--cq", type=int, default=CQ_NVENC, help="nvenc tier quality (measured default 30)")
|
||||
ap.add_argument("--crf", type=int, default=CRF_X265, help="x265 tier quality (measured default 24)")
|
||||
@@ -1347,9 +1369,13 @@ def main():
|
||||
help="hex CPU mask for the x265 tier (default = the E-core mask); the mask is "
|
||||
"inherited by the ffmpeg children and must agree with --pools")
|
||||
ap.add_argument("--stage-dir", default=None,
|
||||
help="scratch directory for this run (default %s). The ledger, the locks and "
|
||||
"the policy file stay in WORK - only the disposable per-item scratch "
|
||||
"moves, so the dashboard can point it at a roomier drive." % STAGE)
|
||||
help="scratch directory for this run (default <work-dir>/stage). The ledger, the "
|
||||
"locks and the policy file stay in --work-dir - only the disposable "
|
||||
"per-item scratch moves, so the dashboard can point it at a roomier drive.")
|
||||
ap.add_argument("--work-dir", default=None,
|
||||
help="work root for this run: ledger.jsonl, gate_policy.json and run-*.lock "
|
||||
"(default %s, this file's compiled-in constant, so a hand-run engine is "
|
||||
"unchanged). An explicit value moves ALL of them." % WORK)
|
||||
ap.add_argument("--tier", choices=["copy", "nvenc", "x265"],
|
||||
help="only touch files classified into this tier (run tiers separately)")
|
||||
ap.add_argument("--force-tier", choices=["copy", "nvenc", "x265"], default=None,
|
||||
@@ -1384,6 +1410,9 @@ def main():
|
||||
"THROW THE OUTPUT AWAY - nothing is uploaded and nothing is replaced")
|
||||
args = ap.parse_args()
|
||||
DECODE_CHECK = not args.no_decode_check
|
||||
if args.work_dir:
|
||||
_apply_work_dir(args.work_dir)
|
||||
log("=== work dir: %s (ledger, locks, gate_policy.json)" % WORK)
|
||||
if args.stage_dir:
|
||||
STAGE = os.path.abspath(args.stage_dir)
|
||||
log("=== stage dir: %s (ledger + locks stay in %s)" % (STAGE, WORK))
|
||||
|
||||
Reference in New Issue
Block a user